PHY LINK
Linux 内核中的 PHY 状态定义
enum phy_state {
PHY_DOWN = 0, // PHY芯片和驱动没准备好,一般情况下少发生
PHY_STARTING, // PHY芯片OK了,但驱动还没有准备好
PHY_READY, // 准备好了,在probe中赋值,接下来会切到PHY_UP
PHY_PENDING,
PHY_UP, // phy启动了,可以工作了,接下来会到PHY_AN
PHY_AN, // 自动协商
PHY_RUNNING, // 正在运行中,在网络连接(插上网线)时会到这个状态
PHY_NOLINK, // 断网了
PHY_FORCING, // 强制,当自动协商不使能时,就会进行此状态(实际上会读PHY寄存器进行设置速率、双工,等)
PHY_CHANGELINK, // 变化,这个状态很重要,当连接时,会换到PHY_RUNNING,当断网时,会切到PHY_NOLINK
PHY_HALTED,
PHY_RESUMING
};
PHY 状态机
/**
* phy_state_machine - Handle the state machine
* @work: work_struct that describes the work to be done
*/
void phy_state_machine(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
struct phy_device *phydev =
container_of(dwork, struct phy_device, state_queue);
bool needs_aneg = false, do_suspend = false, do_resume = false;
int err = 0;
mutex_lock(&phydev->lock);
if (phydev->drv->link_change_notify)
phydev->drv->link_change_notify(phydev);
switch (phydev->state) {
case PHY_DOWN:
case PHY_STARTING:
case PHY_READY:
case PHY_PENDING:
break;
case PHY_UP:
needs_aneg = true;
phydev->link_timeout = PHY_AN_TIMEOUT; // 超时,自动协商不成功时,则会在超时后强制设置速率等参数
break;
case PHY_AN:
err = phy_read_status(phydev); // 读phy状态,包括link,速率、双工,等等
if (err < 0)
break;
/* If the link is down, give up on negotiation for now */
if (!phydev->link) {
phydev->state = PHY_NOLINK; // 没有连接,则状态变成PHY_NOLINK
netif_carrier_off(phydev->attached_dev); // 通知内核其它网络模块(phy是最底一层)断网了。
phydev->adjust_link(phydev->attached_dev); // 调整参数(速率、双工)
break;
}
/* Check if negotiation is done. Break if there's an error */
err = phy_aneg_done(phydev); // 检测是否完成自动协商
if (err < 0)
break;
/* If AN is done, we're running */
if (err > 0) {
phydev->state = PHY_RUNNING; // 完成后,变成PHY_RUNNING状态
netif_carrier_on(phydev->attached_dev); // 发通知,连接OK
phydev->adjust_link(phydev->attached_dev); // 打印、调用参数
} else if (0 == phydev->link_timeout--)
needs_aneg = true;
break;
case PHY_NOLINK:
err = phy_read_status(phydev); // 读phy状态,包括link,速率、双工,等等
if (err)
break;
if (phydev->link) { // 在断开网络再连接(即拨掉再插上网线),就进入此语句
if (AUTONEG_ENABLE == phydev->autoneg) {
err = phy_aneg_done(phydev); // 如果是自动协商使能,就进行自动协商
if (err < 0)
break;
if (!err) {
phydev->state = PHY_AN;
phydev->link_timeout = PHY_AN_TIMEOUT;
break;
}
}
phydev->state = PHY_RUNNING; // 运行时。。。。。
netif_carrier_on(phydev->attached_dev);
phydev->adjust_link(phydev->attached_dev);
}
break;
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;
case PHY_RUNNING:
/* Only register a CHANGE if we are
* polling or ignoring interrupts
*/
if (!phy_interrupt_is_valid(phydev))
phydev->state = PHY_CHANGELINK; // 如果是RUNNING,则改变为CHANGELINK。
break;
case PHY_CHANGELINK:
err = phy_read_status(phydev); // 读phy状态,包括link,速率、双工,等等
if (err)
break;
if (phydev->link) {
phydev->state = PHY_RUNNING; // 连接网络时,则变成RUNNING
netif_carrier_on(phydev->attached_dev);
} else {
phydev->state = PHY_NOLINK; // 不连网时,变成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;
case PHY_RESUMING:
err = phy_clear_interrupt(phydev);
if (err)
break;
err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED);
if (err)
break;
if (AUTONEG_ENABLE == phydev->autoneg) {
err = phy_aneg_done(phydev);
if (err < 0)
break;
/* err > 0 if AN is done.
* Otherwise, it's 0, and we're still waiting for AN
*/
if (err > 0) {
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;
}
phydev->adjust_link(phydev->attached_dev);
} else {
phydev->state = PHY_AN;
phydev->link_timeout = PHY_AN_TIMEOUT;
}
} else {
err = phy_read_status(phydev); // 读phy状态,包括link,速率、双工,等等
if (err)
break;
if (phydev->link) {
phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev);
} else {
phydev->state = PHY_NOLINK;
}
phydev->adjust_link(phydev->attached_dev);
}
do_resume = true;
break;
}
mutex_unlock(&phydev->lock);
if (needs_aneg)
err = phy_start_aneg(phydev);
else if (do_suspend)
phy_suspend(phydev);
else if (do_resume)
phy_resume(phydev);
if (err < 0)
phy_error(phydev);
queue_delayed_work(system_power_efficient_wq, &phydev->state_queue,
PHY_STATE_TIME * HZ);
}
PHY 状态
上电时状态变化:
PHY_READY -> PHY_UP -> PHY_AN -> PHY_RUNNING
拨出网线时状态变化:
PHY_RUNNING ->PHY_NOLINK
插上网线时状态变化:
PHY_NOLINK -> PHY_RUNNING
自动协商过程:
cpsw_ndo_open->cpsw_slave_open -> PHY_UP -> phy_start_aneg -> genphy_config_aneg -> genphy_config_advert -> genphy_restart_aneg -> PHY_AN -> PHY_NOLINK(串口打印Down) -> phy_aneg_done -> PHY_RUNNING(串口打印Up)
注:在 AN 后出现 NOLINK 状态,我猜是因为自动协商需要时间,此时间大于 1 秒,然后执行到状态机判断成 NOLINK,然后判断是否完成自动协商,然后再到 RUNNING 状态。
参考
基于STM32+W5500 的Ethernet和Internet移植
W5500 = 28J60 + LwIP