背景

  • Platform: RK3399
  • OS: Android7.1.2
  • Kernel: v4.4.103
  • Switch Phy: ksz8463

问题

单独用POE供电,网络不通

分析

双网口设计:一个为”进”(机器默认绑定的active Phy),一个为”出”。
硬件设计:设计在”出”的网口上。
POE要设计在”进”的网口上,机器的网络才能正常,所以需要硬件交换POE的设计,或者软件交换机器的active Phy
追踪代码:
drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c文件中的stmmac_mdio_register函数:
默认bind到MAC的phy(plat->phy_addr)是最先找到的phy,伪代码:

for(addr = 0; addr < PHY_MAX_ADDR; addr++) {  
    struct phy_device *phydev = new_bus->phy_map[addr];
    if(phydev) {
        //....
        /*
        * If we're going to bind the MAC to this PHY bus,
        * and no PHY number was provided to the MAC,
        * use the one probed here.
        */
        if (priv->plat->phy_addr == -1)
        priv->plat->phy_addr = addr;

        act = (priv->plat->phy_addr == addr);

        //....
        pr_info("%s: PHY ID %08x at %d IRQ %s (%s)%s\n",
        ndev->name, phydev->phy_id, addr,
        irq_str, dev_name(&phydev->dev),
        act ? " active" : "");
        found = 1;
    }
}  

plat->phy_addr是从dts中的snps,phy-addr节点获取:
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c文件中stmmac_probe_config_dt函数:

/* "snps,phy-addr" is not a standard property. Mark it as deprecated
 * and warn of its use. Remove this when phy node support is added.
 */
if (of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr) == 0)
    dev_warn(&pdev->dev, "snps,phy-addr property is deprecated\n");  

解决

以下方法二选一

修改snps,phy-addr实现

dts中指定MAC绑定的phy addr:

&gmac {
    phy-supply = <&vcc_phy>;
    phy-mode = "rmii";
    clock_in_out = "output";
    snps,reset-gpio = <&gpio3 15 GPIO_ACTIVE_LOW>;
    snps,reset-active-low;
    snps,reset-delays-us = <0 10000 50000>;
    snps,phy-addr = <2>; /*指定 active phy addr*/
    assigned-clocks = <&cru SCLK_RMII_SRC>;
    assigned-clock-parents = <&cru SCLK_MAC>;
    pinctrl-names = "default";
    pinctrl-0 = <&rmii_pins>;
    tx_delay = <0x28>;
    rx_delay = <0x1B>;
    status = "okay";
};  

修改for循环轮询实现

stmmac_mdio_register函数中,

for (addr = 0; addr < PHY_MAX_ADDR; addr++)
改为
for (addr = PHY_MAX_ADDR-1; addr >= 0; addr--)

效果

原始效果:

[    0.489796] eth%d: PHY ID 00221430 at 1 IRQ POLL (stmmac-0:01) active
[    0.489815] eth%d: PHY ID 00221430 at 2 IRQ POLL (stmmac-0:02)  

修改for循环效果:

[    0.489796] eth%d: PHY ID 00221430 at 2 IRQ POLL (stmmac-0:02) active
[    0.489815] eth%d: PHY ID 00221430 at 1 IRQ POLL (stmmac-0:01)  

修改snps,phy-addr效果:

[    0.496949] eth%d: PHY ID 00221430 at 1 IRQ POLL (stmmac-0:01)
[    0.496963] eth%d: PHY ID 00221430 at 2 IRQ POLL (stmmac-0:02) active  

扩展

主要是mdiobus_register(): 不是总线的注册,而是调用mdiobus_scan扫描PHY设备, phy_device_create创建phy设备,

 ‐‐> mdiobus_register
     ‐‐> device_register
     ‐‐> mdiobus_scan
         ‐‐> get_phy_device
             ‐‐> get_phy_id         // 读寄存器
                 ‐‐> phy_device_create  // 创建phy设备
                 ‐‐> INIT_DELAYED_WORK(&dev‐>state_queue, phy_state_machine); //初始化状态机  

参考: Linux phy system