9-Linux phy system

Linux phy system

PHY芯片为OSI的最底层-物理层(Physical Layer),通过MII与数据链路层的MAC芯片相连,对于MAC与PHY之间的一些知识可以查看Mac与Phy组成原理的简单分析,这篇文章进行熟悉。

PHY与MAC整体的连接框架:

连接框架

PHY的硬件系统算是比较复杂的,PHY与MAC相连,MAC与CPU相通,PHY与MAC通过MII和MDIO/MDC相连,MII是走网络数据的,MDIO/MDC是用来与PHY的寄存器通讯的,对PHY进行配置。类似的SWITCH芯片一般也有两种接口,MII用来走网络数据,SPI用来设置SWITCH的寄存器。

跟以前分析I2C/SPI的驱动一样,分为控制器驱动和设备器驱动。

1、控制器驱动

控制器的驱动使用的一样是platform总线的连接方式,在arch或dts下面进行对phy的platform进行add,platform_driver的register一般也放在/driver/net/phy/下面,device和driver的name匹配后就会执行platform_driver结构体所对应的probe接口,都大同小异,如下:

static struct platform_driver pfe_platform_driver = {
    .probe = pfe_platform_probe,
    .remove = pfe_platform_remove,
    .driver = {
        .name = "pfe",
#ifdef CONFIG_PM
        .pm = &pfe_platform_pm_ops,
#endif
    },
};


static int __init pfe_module_init(void)
{
    return platform_driver_register(&pfe_platform_driver);
}


static void __exit pfe_module_exit(void)
{
    platform_driver_unregister(&pfe_platform_driver);
}

MODULE_LICENSE("GPL");
module_init(pfe_module_init);
module_exit(pfe_module_exit);

因为phy与cpu的通讯配置使用的是MII、MDIO/MDC来进行传输控制的,所以probe函数里面如要对MII总线进行配置,最后调用mdiobus_register()of_mdiobus_register()对mdio_bus进行注册。

of_mdiobus_register()函数位于drivers/of/of_mdio.c中,该函数最后还是会调用mdiobus_register()函数,mdiobus_register()函数位于drivers\net\phy\mdio_bus.c中,通过一层一层的往下拨,会有如下结构:

 ‐‐> 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); //初始化状态机

这边就是控制器和设备器的交接了,创建phy设备初始化PHY状态机,接下去就看设备器的驱动。

2、设备器驱动

设备器的驱动也是三个方面device、driver、bus。PHY的device接口为phy_device_registerphy_device_release,driver接口为phy_driver_registerphy_driver_unregister,bus接口为mdiobus_registermdiobus_unregister

这样一分析感觉就跟清晰了,有关PHY驱动的内容都位于/drivers/net/phy中。

phy的设备驱动不像i2c/spi有一个board_info函数进行添加设备,而是直接读取phy中的寄存器,根据IEEE的规定PHY芯片的前16个寄存器的内容必须是固定的,如下:
register

其中寄存器0x02和0x03即设备ID的高低位,每一种型号的PHY有其一串ID号,这我们查看对于的手册即可知道。

在设备的driver中使用MODULE_DEVICE_TABLE将对应的设备ID添加进入,他的效果可以理解为board_info函数所实现的内容,如broadcom的table:

static struct mdio_device_id __maybe_unused broadcom_tbl[] = {
    { PHY_ID_BCM5411, 0xfffffff0 },
    { PHY_ID_BCM5421, 0xfffffff0 },
    { PHY_ID_BCM54210S, 0xfffffff0 },
    { PHY_ID_BCM5461, 0xfffffff0 },
    { PHY_ID_BCM5464, 0xfffffff0 },
    { PHY_ID_BCM5482, 0xfffffff0 },
    { PHY_ID_BCM5482, 0xfffffff0 },
    { PHY_ID_BCM50610, 0xfffffff0 },
    { PHY_ID_BCM50610M, 0xfffffff0 },
    { PHY_ID_BCM57780, 0xfffffff0 },
    { PHY_ID_BCMAC131, 0xfffffff0 },
    { PHY_ID_BCM5241, 0xfffffff0 },
    { }
};

MODULE_DEVICE_TABLE(mdio, broadcom_tbl);

phy_driver的register大概为如下一个过程,进行简单分析:

drivers/net/phy/phy_device.c
phy_init
    ‐‐> mdio_bus_init 注册mdio总线
       ‐‐> class_register(&mdio_bus_class);
       ‐‐> bus_register(&mdio_bus_type);
 ‐‐> phy_driver_register(&genphy_driver);

在mdio总线注册的时候会调用mdio_bus_match,如果match函数找到设备则会进行设备驱动的注册,match函数位于/drivers/net/phy/mdio_bus.c中,如下,进行phy_id的打印。

static int mdio_bus_match(struct device *dev, struct device_driver *drv)
{
    struct phy_device *phydev = to_phy_device(dev);
    struct phy_driver *phydrv = to_phy_driver(drv);
    
    printk("phydev->phy_id:%x ",phydev->phy_id);
    printk("phydrv->phy_id:%x \n",phydrv->phy_id);
    
    return ((phydrv->phy_id & phydrv->phy_id_mask) ==
        (phydev->phy_id & phydrv->phy_id_mask));
}

由log可以看出,设备dev的ID为600d8595,然后就去查找对应的驱动drv的ID,知道找到600d8595则进入probe函数。

[   23.306611] phydev->phy_id:600d8595 phydrv->phy_id:ffffffff
[   23.312150] phydev->phy_id:600d8595 phydrv->phy_id:ffffffff
[   23.317731] phydev->phy_id:600d8595 phydrv->phy_id:4dd072
[   23.323084] phydev->phy_id:600d8595 phydrv->phy_id:4dd033
[   23.328461] phydev->phy_id:600d8595 phydrv->phy_id:206070
[   23.333812] phydev->phy_id:600d8595 phydrv->phy_id:2060e0
[   23.339182] phydev->phy_id:600d8595 phydrv->phy_id:600d8595

这样找到ID后就会进行设备驱动的注册,对应的phy_driver_register()函数才能返回成功,才会执行phy_driver结构体下面的内容,如下:

static struct phy_driver bcm54210s_driver = {
    .phy_id     = PHY_ID_BCM54210S,
    .phy_id_mask    = 0xfffffff0,
    .name       = "Broadcom BCM54210S",
    .features   = PHY_GBIT_FEATURES |
              SUPPORTED_Pause | SUPPORTED_Asym_Pause,
    .flags      = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
    .config_init    = bcm54210s_config_init,
    .config_aneg    = bcm54210s_config_aneg,
    .read_status    = bcm54210s_read_status,
    .ack_interrupt  = bcm54xx_ack_interrupt,
    .config_intr    = bcm54xx_config_intr,
    .driver     = { .owner = THIS_MODULE },
};

里面对phy的寄存器等进行初始化配置,这边对PHY的驱动进行简单的介绍,关于PHY的内容还有好多,比如:PHY状态机ethtool工具这些都是在后面应用的时候需要用到的,等我自己深入研究后再进行学习总结。

Linux phy system的分析就到这边,有感悟时会持续会更新。

注:以上内容都是本人在学习过程积累的一些心得,难免会有参考到其他文章的一些知识,如有侵权,请及时通知我,我将及时删除或标注内容出处,如有错误之处也请指出,进行探讨学习。文章只是起一个引导作用,详细的数据解析内容还请查看Linux相关教程,感谢您的查阅。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值