Commit 66329deb authored by Fin Maaß's avatar Fin Maaß Committed by Henrik Brix Andersen
Browse files

drivers: ethernet: phy: phy_mii: start autoneg in cfg_link



already (re-)start autonegotiation in phy_mii_cfg_link.

Signed-off-by: default avatarFin Maaß <f.maass@vogl-electronic.com>
parent 97c9f0ed
Loading
Loading
Loading
Loading
+36 −78
Original line number Diff line number Diff line
@@ -41,7 +41,6 @@ struct phy_mii_dev_data {
#if ANY_DYNAMIC_LINK
	struct k_work_delayable monitor_work;
	bool gigabit_supported;
	bool restart_autoneg;
	bool autoneg_in_progress;
	k_timepoint_t autoneg_timeout;
#endif
@@ -179,32 +178,27 @@ static int update_link_state(const struct device *dev)

	link_up = (bmsr_reg & MII_BMSR_LINK_STATUS) != 0U;

	/* If there is no change in link state don't proceed. */
	if ((link_up == data->state.is_up) && !data->restart_autoneg) {
		return -EAGAIN;
	}

	data->state.is_up = link_up;

	if (!data->state.is_up) {
	/* If link is down, we can stop here. */
	if (!link_up) {
		data->state.speed = 0;
		if (link_up != data->state.is_up) {
			data->state.is_up = false;
			LOG_INF("PHY (%d) is down", cfg->phy_addr);
		/* If not restarting auto-negotiation, just return */
		if (!data->restart_autoneg) {
			return 0;
		}
		return -EAGAIN;
	}

	if (phy_mii_reg_read(dev, MII_BMCR, &bmcr_reg) < 0) {
		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);

		data->restart_autoneg = false;

		if ((data->state.speed != new_speed) && data->state.is_up) {
		if ((data->state.speed != new_speed) || !data->state.is_up) {
			data->state.is_up = true;
			data->state.speed = new_speed;

			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;
	}

	/**
	 * Perform auto-negotiation sequence.
	/* If auto-negotiation is enabled and the link was already up last time we checked,
	 * 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);

	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;
	if (data->state.is_up) {
		return -EAGAIN;
	}

	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));
	return -EINPROGRESS;
}
@@ -249,10 +239,6 @@ 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.
	 */
@@ -267,7 +253,7 @@ static int check_autonegotiation_completion(const struct device *dev)

	if ((bmsr_reg & MII_BMSR_AUTONEG_COMPLETE) == 0U) {
		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 -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;
	const struct phy_mii_dev_config *const cfg = dev->config;
	uint16_t bmcr_reg;
	int ret = 0;

	/* 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);
		if (ret == -EALREADY) {
			/* If the BMCR 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;
		}
	} else {
		ret = phy_mii_set_anar_reg(dev, adv_speeds);
		if (ret == -EALREADY) {
			/* If the ANAR 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 (ret >= 0) {
			data->autoneg_in_progress = false;
			k_work_reschedule(&data->monitor_work, K_NO_WAIT);
		}

		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;
			}
		ret = phy_mii_cfg_link_autoneg(dev, adv_speeds, data->gigabit_supported);
		if (ret >= 0) {
			LOG_DBG("PHY (%d) Starting MII PHY auto-negotiate sequence", cfg->phy_addr);
			data->autoneg_in_progress = true;
			data->autoneg_timeout =
				sys_timepoint_calc(K_MSEC(CONFIG_PHY_AUTONEG_TIMEOUT_MS));
			k_work_reschedule(&data->monitor_work,
					  K_MSEC(MII_AUTONEG_POLL_INTERVAL_MS));
		}
	}

	if (data->restart_autoneg && data->state.is_up) {
		k_work_reschedule(&data->monitor_work, K_NO_WAIT);
	if (ret == -EALREADY) {
		LOG_DBG("PHY (%d) Link already configured", cfg->phy_addr);
	}

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));

	if (data->state.speed == 0) {
	if (state->speed == 0) {
		/* If speed is 0, then link is also down, happens when autonegotiation is in
		 * progress
		 */
		data->state.is_up = false;
		state->is_up = false;
	}

	k_sem_give(&data->sem);
@@ -573,7 +530,8 @@ static int phy_mii_initialize_dynamic_link(const struct device *dev)
	/* Advertise default speeds */
	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;
}
+44 −0
Original line number Diff line number Diff line
@@ -61,6 +61,50 @@ static inline int phy_mii_set_c1kt_reg(const struct device *dev, enum phy_link_s
	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,
							enum phy_link_speed adv_speeds)
{
+1 −0
Original line number Diff line number Diff line
@@ -228,6 +228,7 @@ __subsystem struct ethphy_driver_api {
 * @retval 0 If successful.
 * @retval -EIO If communication with PHY failed.
 * @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,
				     enum phy_cfg_link_flag flags)