drivers: ethernet: phy: phy_mii: start autoneg in cfg_link
already (re-)start autonegotiation in phy_mii_cfg_link. Signed-off-by: Fin Maaß <f.maass@vogl-electronic.com>
This commit is contained in:
parent
97c9f0edad
commit
66329deb9c
3 changed files with 81 additions and 78 deletions
|
@ -41,7 +41,6 @@ struct phy_mii_dev_data {
|
||||||
#if ANY_DYNAMIC_LINK
|
#if ANY_DYNAMIC_LINK
|
||||||
struct k_work_delayable monitor_work;
|
struct k_work_delayable monitor_work;
|
||||||
bool gigabit_supported;
|
bool gigabit_supported;
|
||||||
bool restart_autoneg;
|
|
||||||
bool autoneg_in_progress;
|
bool autoneg_in_progress;
|
||||||
k_timepoint_t autoneg_timeout;
|
k_timepoint_t autoneg_timeout;
|
||||||
#endif
|
#endif
|
||||||
|
@ -179,32 +178,27 @@ static int update_link_state(const struct device *dev)
|
||||||
|
|
||||||
link_up = (bmsr_reg & MII_BMSR_LINK_STATUS) != 0U;
|
link_up = (bmsr_reg & MII_BMSR_LINK_STATUS) != 0U;
|
||||||
|
|
||||||
/* If there is no change in link state don't proceed. */
|
/* If link is down, we can stop here. */
|
||||||
if ((link_up == data->state.is_up) && !data->restart_autoneg) {
|
if (!link_up) {
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
data->state.is_up = link_up;
|
|
||||||
|
|
||||||
if (!data->state.is_up) {
|
|
||||||
data->state.speed = 0;
|
data->state.speed = 0;
|
||||||
LOG_INF("PHY (%d) is down", cfg->phy_addr);
|
if (link_up != data->state.is_up) {
|
||||||
/* If not restarting auto-negotiation, just return */
|
data->state.is_up = false;
|
||||||
if (!data->restart_autoneg) {
|
LOG_INF("PHY (%d) is down", cfg->phy_addr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (phy_mii_reg_read(dev, MII_BMCR, &bmcr_reg) < 0) {
|
if (phy_mii_reg_read(dev, MII_BMCR, &bmcr_reg) < 0) {
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(bmcr_reg & MII_BMCR_AUTONEG_ENABLE)) {
|
/* If auto-negotiation is not enabled, we only need to check the link speed */
|
||||||
|
if ((bmcr_reg & MII_BMCR_AUTONEG_ENABLE) == 0U) {
|
||||||
enum phy_link_speed new_speed = phy_mii_get_link_speed_bmcr_reg(dev, bmcr_reg);
|
enum phy_link_speed new_speed = phy_mii_get_link_speed_bmcr_reg(dev, bmcr_reg);
|
||||||
|
|
||||||
data->restart_autoneg = false;
|
if ((data->state.speed != new_speed) || !data->state.is_up) {
|
||||||
|
data->state.is_up = true;
|
||||||
if ((data->state.speed != new_speed) && data->state.is_up) {
|
|
||||||
data->state.speed = new_speed;
|
data->state.speed = new_speed;
|
||||||
|
|
||||||
LOG_INF("PHY (%d) Link speed %s Mb, %s duplex",
|
LOG_INF("PHY (%d) Link speed %s Mb, %s duplex",
|
||||||
|
@ -218,22 +212,18 @@ static int update_link_state(const struct device *dev)
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/* If auto-negotiation is enabled and the link was already up last time we checked,
|
||||||
* Perform auto-negotiation sequence.
|
* we can return immediately, as the link state has not changed.
|
||||||
|
* If the link was down, we will start the auto-negotiation sequence.
|
||||||
*/
|
*/
|
||||||
LOG_DBG("PHY (%d) Starting MII PHY auto-negotiate sequence", cfg->phy_addr);
|
if (data->state.is_up) {
|
||||||
|
return -EAGAIN;
|
||||||
bmcr_reg |= MII_BMCR_AUTONEG_RESTART;
|
|
||||||
bmcr_reg &= ~MII_BMCR_ISOLATE; /* Don't isolate the PHY */
|
|
||||||
|
|
||||||
/* Configure and start auto-negotiation process */
|
|
||||||
if (phy_mii_reg_write(dev, MII_BMCR, bmcr_reg) < 0) {
|
|
||||||
return -EIO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data->restart_autoneg = false;
|
data->state.is_up = true;
|
||||||
|
|
||||||
|
LOG_DBG("PHY (%d) Starting MII PHY auto-negotiate sequence", cfg->phy_addr);
|
||||||
|
|
||||||
/* We have to wait for the auto-negotiation process to complete */
|
|
||||||
data->autoneg_timeout = sys_timepoint_calc(K_MSEC(CONFIG_PHY_AUTONEG_TIMEOUT_MS));
|
data->autoneg_timeout = sys_timepoint_calc(K_MSEC(CONFIG_PHY_AUTONEG_TIMEOUT_MS));
|
||||||
return -EINPROGRESS;
|
return -EINPROGRESS;
|
||||||
}
|
}
|
||||||
|
@ -249,10 +239,6 @@ static int check_autonegotiation_completion(const struct device *dev)
|
||||||
uint16_t c1kt_reg = 0;
|
uint16_t c1kt_reg = 0;
|
||||||
uint16_t s1kt_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
|
/* On some PHY chips, the BMSR bits are latched, so the first read may
|
||||||
* show incorrect status. A second read ensures correct values.
|
* show incorrect status. A second read ensures correct values.
|
||||||
*/
|
*/
|
||||||
|
@ -267,7 +253,7 @@ static int check_autonegotiation_completion(const struct device *dev)
|
||||||
|
|
||||||
if ((bmsr_reg & MII_BMSR_AUTONEG_COMPLETE) == 0U) {
|
if ((bmsr_reg & MII_BMSR_AUTONEG_COMPLETE) == 0U) {
|
||||||
if (sys_timepoint_expired(data->autoneg_timeout)) {
|
if (sys_timepoint_expired(data->autoneg_timeout)) {
|
||||||
LOG_DBG("PHY (%d) auto-negotiate timedout", cfg->phy_addr);
|
LOG_DBG("PHY (%d) auto-negotiate timeout", cfg->phy_addr);
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
return -EINPROGRESS;
|
return -EINPROGRESS;
|
||||||
|
@ -371,7 +357,6 @@ static int phy_mii_cfg_link(const struct device *dev, enum phy_link_speed adv_sp
|
||||||
{
|
{
|
||||||
struct phy_mii_dev_data *const data = dev->data;
|
struct phy_mii_dev_data *const data = dev->data;
|
||||||
const struct phy_mii_dev_config *const cfg = dev->config;
|
const struct phy_mii_dev_config *const cfg = dev->config;
|
||||||
uint16_t bmcr_reg;
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
/* if there is no mdio (fixed-link) it is not supported to configure link */
|
/* if there is no mdio (fixed-link) it is not supported to configure link */
|
||||||
|
@ -397,52 +382,24 @@ static int phy_mii_cfg_link(const struct device *dev, enum phy_link_speed adv_sp
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = phy_mii_set_bmcr_reg_autoneg_disabled(dev, adv_speeds);
|
ret = phy_mii_set_bmcr_reg_autoneg_disabled(dev, adv_speeds);
|
||||||
if (ret == -EALREADY) {
|
if (ret >= 0) {
|
||||||
/* If the BMCR register is already set, we don't need to do anything */
|
data->autoneg_in_progress = false;
|
||||||
ret = 0;
|
k_work_reschedule(&data->monitor_work, K_NO_WAIT);
|
||||||
} else if (ret < 0) {
|
|
||||||
goto cfg_link_end;
|
|
||||||
} else {
|
|
||||||
data->restart_autoneg = true;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ret = phy_mii_set_anar_reg(dev, adv_speeds);
|
ret = phy_mii_cfg_link_autoneg(dev, adv_speeds, data->gigabit_supported);
|
||||||
if (ret == -EALREADY) {
|
if (ret >= 0) {
|
||||||
/* If the ANAR register is already set, we don't need to do anything */
|
LOG_DBG("PHY (%d) Starting MII PHY auto-negotiate sequence", cfg->phy_addr);
|
||||||
ret = 0;
|
data->autoneg_in_progress = true;
|
||||||
} else if (ret < 0) {
|
data->autoneg_timeout =
|
||||||
goto cfg_link_end;
|
sys_timepoint_calc(K_MSEC(CONFIG_PHY_AUTONEG_TIMEOUT_MS));
|
||||||
} else {
|
k_work_reschedule(&data->monitor_work,
|
||||||
data->restart_autoneg = true;
|
K_MSEC(MII_AUTONEG_POLL_INTERVAL_MS));
|
||||||
}
|
|
||||||
|
|
||||||
if (data->gigabit_supported) {
|
|
||||||
ret = phy_mii_set_c1kt_reg(dev, adv_speeds);
|
|
||||||
if (ret == -EALREADY) {
|
|
||||||
/* If the C1KT register is already set, we don't need to do anything */
|
|
||||||
ret = 0;
|
|
||||||
} else if (ret < 0) {
|
|
||||||
goto cfg_link_end;
|
|
||||||
} else {
|
|
||||||
data->restart_autoneg = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (phy_mii_reg_read(dev, MII_BMCR, &bmcr_reg) < 0) {
|
|
||||||
ret = -EIO;
|
|
||||||
goto cfg_link_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data->restart_autoneg && data->state.is_up) {
|
if (ret == -EALREADY) {
|
||||||
k_work_reschedule(&data->monitor_work, K_NO_WAIT);
|
LOG_DBG("PHY (%d) Link already configured", cfg->phy_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg_link_end:
|
cfg_link_end:
|
||||||
|
@ -461,11 +418,11 @@ static int phy_mii_get_link_state(const struct device *dev,
|
||||||
|
|
||||||
memcpy(state, &data->state, sizeof(struct phy_link_state));
|
memcpy(state, &data->state, sizeof(struct phy_link_state));
|
||||||
|
|
||||||
if (data->state.speed == 0) {
|
if (state->speed == 0) {
|
||||||
/* If speed is 0, then link is also down, happens when autonegotiation is in
|
/* If speed is 0, then link is also down, happens when autonegotiation is in
|
||||||
* progress
|
* progress
|
||||||
*/
|
*/
|
||||||
data->state.is_up = false;
|
state->is_up = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
k_sem_give(&data->sem);
|
k_sem_give(&data->sem);
|
||||||
|
@ -573,7 +530,8 @@ static int phy_mii_initialize_dynamic_link(const struct device *dev)
|
||||||
/* Advertise default speeds */
|
/* Advertise default speeds */
|
||||||
phy_mii_cfg_link(dev, cfg->default_speeds, 0);
|
phy_mii_cfg_link(dev, cfg->default_speeds, 0);
|
||||||
|
|
||||||
monitor_work_handler(&data->monitor_work.work);
|
/* This will schedule the monitor work, if not already scheduled by phy_mii_cfg_link(). */
|
||||||
|
k_work_schedule(&data->monitor_work, K_NO_WAIT);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,50 @@ static inline int phy_mii_set_c1kt_reg(const struct device *dev, enum phy_link_s
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int phy_mii_cfg_link_autoneg(const struct device *dev, enum phy_link_speed adv_speeds,
|
||||||
|
bool gigabit_supported)
|
||||||
|
{
|
||||||
|
uint32_t bmcr_reg = 0U;
|
||||||
|
uint32_t bmcr_reg_old;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (phy_read(dev, MII_BMCR, &bmcr_reg) < 0) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
bmcr_reg_old = bmcr_reg;
|
||||||
|
|
||||||
|
/* Disable isolation */
|
||||||
|
bmcr_reg &= ~MII_BMCR_ISOLATE;
|
||||||
|
/* Enable auto-negotiation */
|
||||||
|
bmcr_reg |= MII_BMCR_AUTONEG_ENABLE;
|
||||||
|
|
||||||
|
ret = phy_mii_set_anar_reg(dev, adv_speeds);
|
||||||
|
if (ret >= 0) {
|
||||||
|
bmcr_reg |= MII_BMCR_AUTONEG_RESTART;
|
||||||
|
} else if (ret != -EALREADY) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gigabit_supported) {
|
||||||
|
ret = phy_mii_set_c1kt_reg(dev, adv_speeds);
|
||||||
|
if (ret >= 0) {
|
||||||
|
bmcr_reg |= MII_BMCR_AUTONEG_RESTART;
|
||||||
|
} else if (ret != -EALREADY) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bmcr_reg != bmcr_reg_old) {
|
||||||
|
if (phy_write(dev, MII_BMCR, bmcr_reg) < 0) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EALREADY;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int phy_mii_set_bmcr_reg_autoneg_disabled(const struct device *dev,
|
static inline int phy_mii_set_bmcr_reg_autoneg_disabled(const struct device *dev,
|
||||||
enum phy_link_speed adv_speeds)
|
enum phy_link_speed adv_speeds)
|
||||||
{
|
{
|
||||||
|
|
|
@ -228,6 +228,7 @@ __subsystem struct ethphy_driver_api {
|
||||||
* @retval 0 If successful.
|
* @retval 0 If successful.
|
||||||
* @retval -EIO If communication with PHY failed.
|
* @retval -EIO If communication with PHY failed.
|
||||||
* @retval -ENOTSUP If not supported.
|
* @retval -ENOTSUP If not supported.
|
||||||
|
* @retval -EALREADY If already configured with the same speeds and flags.
|
||||||
*/
|
*/
|
||||||
static inline int phy_configure_link(const struct device *dev, enum phy_link_speed speeds,
|
static inline int phy_configure_link(const struct device *dev, enum phy_link_speed speeds,
|
||||||
enum phy_cfg_link_flag flags)
|
enum phy_cfg_link_flag flags)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue