drivers: ethernet: phy_mii: restart autoneg after phy_configure_link
make sure that autonegotiation is restarted, after changing the speeds. Also make sure to only write the changed registers, as mdio is pretty slow. Signed-off-by: Fin Maaß <f.maass@vogl-electronic.com>
This commit is contained in:
parent
b1483a69d6
commit
f569bb523d
1 changed files with 67 additions and 18 deletions
|
@ -39,6 +39,7 @@ struct phy_mii_dev_data {
|
|||
struct k_work_delayable monitor_work;
|
||||
struct k_work_delayable autoneg_work;
|
||||
bool gigabit_supported;
|
||||
bool restart_autoneg;
|
||||
uint32_t autoneg_timeout;
|
||||
#endif
|
||||
};
|
||||
|
@ -175,16 +176,19 @@ static int update_link_state(const struct device *dev)
|
|||
link_up = bmsr_reg & MII_BMSR_LINK_STATUS;
|
||||
|
||||
/* If there is no change in link state don't proceed. */
|
||||
if (link_up == data->state.is_up) {
|
||||
if ((link_up == data->state.is_up) && !data->restart_autoneg) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
data->state.is_up = link_up;
|
||||
|
||||
/* If link is down, there is nothing more to be done */
|
||||
if (data->state.is_up == false) {
|
||||
if (!data->state.is_up) {
|
||||
data->state.speed = 0;
|
||||
LOG_INF("PHY (%d) is down", cfg->phy_addr);
|
||||
return 0;
|
||||
/* If not restarting auto-negotiation, just return */
|
||||
if (!data->restart_autoneg) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -205,6 +209,8 @@ static int update_link_state(const struct device *dev)
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
data->restart_autoneg = false;
|
||||
|
||||
/* We have to wait for the auto-negotiation process to complete */
|
||||
data->autoneg_timeout = CONFIG_PHY_AUTONEG_TIMEOUT_MS / MII_AUTONEG_POLL_INTERVAL_MS;
|
||||
return -EINPROGRESS;
|
||||
|
@ -221,6 +227,10 @@ static int check_autonegotiation_completion(const struct device *dev)
|
|||
uint16_t c1kt_reg = 0;
|
||||
uint16_t s1kt_reg = 0;
|
||||
|
||||
if (data->restart_autoneg) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/* On some PHY chips, the BMSR bits are latched, so the first read may
|
||||
* show incorrect status. A second read ensures correct values.
|
||||
*/
|
||||
|
@ -280,6 +290,8 @@ static int check_autonegotiation_completion(const struct device *dev)
|
|||
data->state.speed = LINK_HALF_10BASE;
|
||||
}
|
||||
|
||||
data->state.is_up = (bmsr_reg & MII_BMSR_LINK_STATUS) != 0U;
|
||||
|
||||
LOG_INF("PHY (%d) Link speed %s Mb, %s duplex",
|
||||
cfg->phy_addr,
|
||||
PHY_LINK_IS_SPEED_1000M(data->state.speed) ? "1000" :
|
||||
|
@ -376,8 +388,11 @@ static int phy_mii_cfg_link(const struct device *dev,
|
|||
struct phy_mii_dev_data *const data = dev->data;
|
||||
const struct phy_mii_dev_config *const cfg = dev->config;
|
||||
uint16_t anar_reg;
|
||||
uint16_t anar_reg_old;
|
||||
uint16_t bmcr_reg;
|
||||
uint16_t c1kt_reg;
|
||||
uint16_t c1kt_reg_old;
|
||||
int ret = 0;
|
||||
|
||||
/* if there is no mdio (fixed-link) it is not supported to configure link */
|
||||
if (cfg->fixed) {
|
||||
|
@ -388,17 +403,23 @@ static int phy_mii_cfg_link(const struct device *dev,
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
k_sem_take(&data->sem, K_FOREVER);
|
||||
|
||||
if (phy_mii_reg_read(dev, MII_ANAR, &anar_reg) < 0) {
|
||||
return -EIO;
|
||||
ret = -EIO;
|
||||
goto cfg_link_end;
|
||||
}
|
||||
anar_reg_old = anar_reg;
|
||||
|
||||
if (phy_mii_reg_read(dev, MII_BMCR, &bmcr_reg) < 0) {
|
||||
return -EIO;
|
||||
ret = -EIO;
|
||||
goto cfg_link_end;
|
||||
}
|
||||
|
||||
if (data->gigabit_supported) {
|
||||
if (phy_mii_reg_read(dev, MII_1KTCR, &c1kt_reg) < 0) {
|
||||
return -EIO;
|
||||
ret = -EIO;
|
||||
goto cfg_link_end;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -427,6 +448,8 @@ static int phy_mii_cfg_link(const struct device *dev,
|
|||
}
|
||||
|
||||
if (data->gigabit_supported) {
|
||||
c1kt_reg_old = c1kt_reg;
|
||||
|
||||
if (adv_speeds & LINK_FULL_1000BASE) {
|
||||
c1kt_reg |= MII_ADVERTISE_1000_FULL;
|
||||
} else {
|
||||
|
@ -439,22 +462,41 @@ static int phy_mii_cfg_link(const struct device *dev,
|
|||
c1kt_reg &= ~MII_ADVERTISE_1000_HALF;
|
||||
}
|
||||
|
||||
if (phy_mii_reg_write(dev, MII_1KTCR, c1kt_reg) < 0) {
|
||||
return -EIO;
|
||||
if (c1kt_reg != c1kt_reg_old) {
|
||||
if (phy_mii_reg_write(dev, MII_1KTCR, c1kt_reg) < 0) {
|
||||
ret = -EIO;
|
||||
goto cfg_link_end;
|
||||
}
|
||||
|
||||
data->restart_autoneg = true;
|
||||
}
|
||||
}
|
||||
|
||||
bmcr_reg |= MII_BMCR_AUTONEG_ENABLE;
|
||||
if (anar_reg != anar_reg_old) {
|
||||
if (phy_mii_reg_write(dev, MII_ANAR, anar_reg) < 0) {
|
||||
ret = -EIO;
|
||||
goto cfg_link_end;
|
||||
}
|
||||
|
||||
if (phy_mii_reg_write(dev, MII_ANAR, anar_reg) < 0) {
|
||||
return -EIO;
|
||||
data->restart_autoneg = true;
|
||||
}
|
||||
|
||||
if (phy_mii_reg_write(dev, MII_BMCR, bmcr_reg) < 0) {
|
||||
return -EIO;
|
||||
if ((bmcr_reg & MII_BMCR_AUTONEG_ENABLE) == 0U) {
|
||||
if (phy_mii_reg_write(dev, MII_BMCR, bmcr_reg | MII_BMCR_AUTONEG_ENABLE) < 0) {
|
||||
ret = -EIO;
|
||||
goto cfg_link_end;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (data->restart_autoneg && data->state.is_up) {
|
||||
k_work_cancel_delayable(&data->autoneg_work);
|
||||
k_work_reschedule(&data->monitor_work, K_NO_WAIT);
|
||||
}
|
||||
|
||||
cfg_link_end:
|
||||
k_sem_give(&data->sem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* ANY_DYNAMIC_LINK */
|
||||
|
||||
|
@ -467,6 +509,13 @@ static int phy_mii_get_link_state(const struct device *dev,
|
|||
|
||||
memcpy(state, &data->state, sizeof(struct phy_link_state));
|
||||
|
||||
if (data->state.speed == 0) {
|
||||
/* If speed is 0, then link is also down, happens when autonegotiation is in
|
||||
* progress
|
||||
*/
|
||||
data->state.is_up = false;
|
||||
}
|
||||
|
||||
k_sem_give(&data->sem);
|
||||
|
||||
return 0;
|
||||
|
@ -558,6 +607,9 @@ static int phy_mii_initialize_dynamic_link(const struct device *dev)
|
|||
|
||||
data->gigabit_supported = is_gigabit_supported(dev);
|
||||
|
||||
k_work_init_delayable(&data->monitor_work, monitor_work_handler);
|
||||
k_work_init_delayable(&data->autoneg_work, autoneg_work_handler);
|
||||
|
||||
/* Advertise all speeds */
|
||||
phy_mii_cfg_link(dev, LINK_HALF_10BASE |
|
||||
LINK_FULL_10BASE |
|
||||
|
@ -566,9 +618,6 @@ static int phy_mii_initialize_dynamic_link(const struct device *dev)
|
|||
LINK_HALF_1000BASE |
|
||||
LINK_FULL_1000BASE);
|
||||
|
||||
k_work_init_delayable(&data->monitor_work, monitor_work_handler);
|
||||
k_work_init_delayable(&data->autoneg_work, autoneg_work_handler);
|
||||
|
||||
monitor_work_handler(&data->monitor_work.work);
|
||||
|
||||
return 0;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue