/drivers/net/phy/phy.c的狀態機phy_state_machine分析
阿新 • • 發佈:2019-02-19
本文分析基於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);
在PHY狀態機中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); }
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