1. 程式人生 > >/drivers/net/phy/phy.c的狀態機phy_state_machine分析

/drivers/net/phy/phy.c的狀態機phy_state_machine分析

本文分析基於PHY不採用中斷方式,而且是固定速率模式,不採用自適應。

      在mii匯流排初始化過程中,mdio匯流排會通過mdiobus_scan()掃描掛載在該總線上的所有phy裝置,並且通過phy_device_create()函式建立phy_device結構體。同時繫結marvell.c檔案中對應的驅動。

    新建立的phy_device結構體成員初始化為:

        dev->speed = 0;
	dev->duplex = -1;
	dev->pause = dev->asym_pause = 0;
	dev->link = 1;
	dev->interface = PHY_INTERFACE_MODE_GMII;

	dev->autoneg = AUTONEG_ENABLE;

	dev->state = PHY_DOWN;

    在struct net_device_ops的ndo_open()函式會在網口被開啟時呼叫。ndo_open()函式繫結PHY對應的驅動,並修改PHY的狀態

        phydev->drv->config_init  //呼叫config_init驅動函式
..........................
         phydev->state = PHY_READY;
         phy_prepare_link(phydev, handler);
         phy_start_machine(phydev);


    phy_prepare_link()繫結phy_device結構體的adjust_link回撥函式。然後啟動PHY狀態機。
	phydev->supported &= (PHY_GBIT_FEATURES | SUPPORTED_Pause |
							SUPPORTED_Asym_Pause);
	phydev->advertising = phydev->supported;

	lp->link    = 0;
	lp->speed   = 0;
	lp->duplex  = -1;
	lp->phy_dev = phydev;

	phy_start(lp->phy_dev);
void phy_start(struct phy_device *phydev)
{
	mutex_lock(&phydev->lock);

	switch (phydev->state) {
		case PHY_STARTING:
			phydev->state = PHY_PENDING;
			break;
		case PHY_READY:
			phydev->state = PHY_UP;
			break;
		case PHY_HALTED:
			phydev->state = PHY_RESUMING;
		default:
			break;
	}
	mutex_unlock(&phydev->lock);
}
    在PHY狀態機中
	case PHY_UP:
		needs_aneg = true;

		phydev->link_timeout = PHY_AN_TIMEOUT;

		break;
。。。。
    if (needs_aneg)
        err = phy_start_aneg(phydev);

   標記1:phy_start_anegphy_start_aneg會修改
	phydev->speed = settings[idx].speed;
	phydev->duplex = settings[idx].duplex;
然後呼叫驅動函式config_aneg()

    如果PHY不支援自適應,phy_start_aneg首先將PHY狀態置為PHY_FORCING,否則置為PHY_AN

/**
 * phy_start_aneg - start auto-negotiation for this PHY device
 * @phydev: the phy_device struct
 *
 * Description: Sanitizes the settings (if we're not autonegotiating
 *   them), and then calls the driver's config_aneg function.
 *   If the PHYCONTROL Layer is operating, we change the state to
 *   reflect the beginning of Auto-negotiation or forcing.
 */
int phy_start_aneg(struct phy_device *phydev)
{
	int err;

	mutex_lock(&phydev->lock);

	if (AUTONEG_DISABLE == phydev->autoneg)
		phy_sanitize_settings(phydev);

	err = phydev->drv->config_aneg(phydev);
	if (err < 0)
		goto out_unlock;

	if (phydev->state != PHY_HALTED) {
		if (AUTONEG_ENABLE == phydev->autoneg) {
			phydev->state = PHY_AN;
			phydev->link_timeout = PHY_AN_TIMEOUT;
		} else {
			phydev->state = PHY_FORCING;
			phydev->link_timeout = PHY_FORCE_TIMEOUT;
		}
	}

out_unlock:
	mutex_unlock(&phydev->lock);
	return err;
}


    PHY狀態機對PHY_FORCING狀態的處理如下:

case PHY_FORCING:
		err = genphy_update_link(phydev);
		if (err)
			break;

		if (phydev->link) {
			phydev->state = PHY_RUNNING;
			netif_carrier_on(phydev->attached_dev);
		} else {
			if (0 == phydev->link_timeout--)
				needs_aneg = true;
		}

		phydev->adjust_link(phydev->attached_dev);
		break;
1、genphy_update_link讀取PHy的狀態暫存器,更新phy_device->link;

2、如果link的話,phydev->state = PHY_RUNNING;呼叫adjust_link回撥函式

	case PHY_FORCING:
		err = genphy_update_link(phydev);
		if (err)
			break;

		if (phydev->link) {
			phydev->state = PHY_RUNNING;
			netif_carrier_on(phydev->attached_dev);
		} else {
			if (0 == phydev->link_timeout--)
				needs_aneg = true;
		}

		phydev->adjust_link(phydev->attached_dev);
		break;

3、沒有link的話,繼續呼叫phy_start_aneg,重複標記1開始的流程

PHY狀態機對PHY_RUNNING狀態的處理如下

	case PHY_RUNNING:
		/* Only register a CHANGE if we are
		 * polling or ignoring interrupts
		 */
		if (!phy_interrupt_is_valid(phydev))
			phydev->state = PHY_CHANGELINK;
		break;

	case PHY_CHANGELINK:
		err = phy_read_status(phydev);
		if (err)
			break;

		if (phydev->link) {
			phydev->state = PHY_RUNNING;
			netif_carrier_on(phydev->attached_dev);
		} else {
			phydev->state = PHY_NOLINK;
			netif_carrier_off(phydev->attached_dev);
		}

		phydev->adjust_link(phydev->attached_dev);

		if (phy_interrupt_is_valid(phydev))
			err = phy_config_interrupt(phydev,
						   PHY_INTERRUPT_ENABLED);
		break;
	case PHY_HALTED:
		if (phydev->link) {
			phydev->link = 0;
			netif_carrier_off(phydev->attached_dev);
			phydev->adjust_link(phydev->attached_dev);
			do_suspend = true;
		}
		break;

PHY_CHANGELINK狀態處理分支:

讀取PHY狀態,如果link,置為PHY_RUNNING,呼叫adjust_link

否則置為PHY_NOLINK,呼叫adjust_link