本文共 7831 字,大约阅读时间需要 26 分钟。
设备树配置
enet0: ethernet@24000 { phy-handle = <&phy0>; tbi-handle = <&tbi0>; phy-connect-type = "sgmii"; mdio@520 { #address-cells = <1>; #size-cells = <0>; compatible = "fsl,gianfar-mdio"; reg = <0x520 0x20>; phy0: ethernet-phy@10 { interrupt-parent = <&mpic>; interrupts = <10 1>; reg = <0x10>; }; tbi0: tbi-phy@11 { reg = <0x11>; device_type = "tbi-phy"; }; };}
代码流程
总入口,总线初始化
int __init phy_init(void) { int rc = mdio_bus_init(); =>int __init mdio_bus_init(void) { int ret = class_register(&mdio_bus_class); ret = bus_register(&mdio_bus_type); //mdio_bus总线注册 ==>struct bus_type mdio_bus_type = { .name = "mdio_bus", .match = mdio_bus_match, =>int mdio_bus_match(struct device *dev, struct device_driver *drv) { //phy驱动匹配则走probe,否则在ifconfig ethx阶段通过genphy_driver走probe struct phy_device *phydev = to_phy_device(dev); struct phy_driver *phydrv = to_phy_driver(drv); return ((phydrv->phy_id & phydrv->phy_id_mask) == (phydev->phy_id & phydrv->phy_id_mask)); } .pm = MDIO_BUS_PM_OPS, }; } rc = phy_driver_register(&genphy_driver); //通用phy驱动注册 ==>struct phy_driver genphy_driver = { .phy_id = 0xffffffff, .phy_id_mask = 0xffffffff, .name = "Generic PHY", .config_init = genphy_config_init, .features = 0, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, .suspend = genphy_suspend, .resume = genphy_resume, .driver = {.owner= THIS_MODULE, }, }; =>int phy_driver_register(struct phy_driver *new_driver) { new_driver->driver.name = new_driver->name; new_driver->driver.bus = &mdio_bus_type; new_driver->driver.probe = phy_probe; new_driver->driver.remove = phy_remove; retval = driver_register(&new_driver->driver);//mdio_bus总线上的设备注册 }}
phy设备注册
int fsl_pq_mdio_init(void){ return of_register_platform_driver(&fsl_pq_mdio_driver); ==>struct of_platform_driver fsl_pq_mdio_driver = { .name = "fsl-pq_mdio", .probe = fsl_pq_mdio_probe, .remove = fsl_pq_mdio_remove, .match_table = fsl_pq_mdio_match, ==>struct of_device_id fsl_pq_mdio_match[] = { { .type = "mdio", .compatible = "ucc_geth_phy", }, { .type = "mdio", .compatible = "gianfar", }, { .compatible = "fsl,ucc-mdio", }, { .compatible = "fsl,gianfar-tbi", }, { .compatible = "fsl,gianfar-mdio", //与设备树匹配, 走fsl_pq_mdio_probe函数 }, { .compatible = "fsl,etsec2-tbi", }, { .compatible = "fsl,etsec2-mdio", }, {}, }; };}int fsl_pq_mdio_probe(struct of_device *ofdev, const struct of_device_id *match) //解析设备树, 将phy的基本信息抽象出来作为phy设备注册{ struct mii_bus *new_bus = mdiobus_alloc(); new_bus->name = "Freescale PowerQUICC MII Bus", new_bus->read = &fsl_pq_mdio_read, new_bus->write = &fsl_pq_mdio_write, new_bus->reset = &fsl_pq_mdio_reset, new_bus->priv = priv; fsl_pq_mdio_bus_name(new_bus->id, np); addrp = of_get_address(np, 0, &size, NULL); /* Set the PHY base address */ addr = of_translate_address(np, addrp); map = ioremap(addr, size); priv->map = map; map -= offsetof(struct fsl_pq_mdio, miimcfg); regs = map; priv->regs = regs; new_bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL); new_bus->parent = &ofdev->dev; dev_set_drvdata(&ofdev->dev, new_bus); err = of_mdiobus_register(new_bus, np); =>int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) { /* Register the MDIO bus */ rc = mdiobus_register(mdio); =>int mdiobus_register(struct mii_bus *bus) { bus->dev.parent = bus->parent; bus->dev.class = &mdio_bus_class; bus->dev.groups = NULL; dev_set_name(&bus->dev, "%s", bus->id); err = device_register(&bus->dev); for (i = 0; i < PHY_MAX_ADDR; i++) { if ((bus->phy_mask & (1 << i)) == 0) { //这个流程走不到 struct phy_device *phydev; phydev = mdiobus_scan(bus, i); =>struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) { struct phy_device *phydev = get_phy_device(bus, addr); err = phy_device_register(phydev); } } } } for_each_child_of_node(np, child) { /* A PHY must have a reg property in the range [0-31] */ addr = of_get_property(child, "reg", &len); phy = get_phy_device(mdio, be32_to_cpup(addr)); rc = phy_device_register(phy); =>struct phy_device * get_phy_device(struct mii_bus *bus, int addr) { struct phy_device *dev = NULL; u32 phy_id; int r = get_phy_id(bus, addr, &phy_id); //通过mii_bus读取phyid dev = phy_device_create(bus, addr, phy_id); =>struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id) { struct phy_device *dev = kzalloc(sizeof(*dev), GFP_KERNEL); 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->addr = addr; dev->phy_id = phy_id; dev->bus = bus; dev->dev.parent = bus->parent; dev->dev.bus = &mdio_bus_type; dev->irq = bus->irq != NULL ? bus->irq[addr] : PHY_POLL; dev_set_name(&dev->dev, PHY_ID_FMT, bus->id, addr); dev->state = PHY_DOWN; } } =>int phy_device_register(struct phy_device *phydev) { err = device_register(&phydev->dev); } } }}
phy设备初始化,假设phy设备为marvell,如果内核配置编译了phy的驱动
int marvell_init(void){ ret = phy_driver_register(&marvell_drivers[i]); ==>struct phy_driver marvell_drivers[] = { .phy_id = 0x01410cc0, //phydev通过get_phy_id读取的phyid,然后通过mdio_bus_match匹配 .phy_id_mask = 0xfffffff0, .name = "Marvell 88E1111", .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, .config_init = &m88e1111_config_init, .config_aneg = &marvell_config_aneg, .read_status = &marvell_read_status, .ack_interrupt = &marvell_ack_interrupt, .config_intr = &marvell_config_intr, .driver = { .owner = THIS_MODULE }, } driver_register(&new_driver->driver); =>int driver_register(struct device_driver *drv) { ret = bus_add_driver(drv); =>int bus_add_driver(struct device_driver *drv) { error = driver_attach(drv); =>int driver_attach(struct device_driver *drv) { return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); =>int __driver_attach(struct device *dev, void *data) { driver_probe_device(drv, dev); =>int driver_probe_device(struct device_driver *drv, struct device *dev) { ret = really_probe(dev, drv); =>int really_probe(struct device *dev, struct device_driver *drv) { ret = drv->probe(dev); =>int phy_probe(struct device *dev) { struct phy_device phydev = to_phy_device(dev); struct device_driver *drv = get_driver(phydev->dev.driver); struct phy_driver *phydrv = to_phy_driver(drv); phydev->drv = phydrv; phydev->supported = phydrv->features; phydev->advertising = phydrv->features; /* Set the state to READY by default */ phydev->state = PHY_READY; if (phydev->drv->probe) //一般为空 err = phydev->drv->probe(phydev); } } } } } } }}
linux PHY驱动
https://blog.csdn.net/subfate/article/details/44900651以太网驱动的流程浅析(五)-mii_bus初始化以及phy id的获取
https://cloud.tencent.com/developer/article/1545855PHY Linux 驱动
https://blog.csdn.net/doitsjz/article/details/70331804以太网PHY自动协商和其在Linux下的初始化
https://blog.csdn.net/kl1125290220/article/details/79859742RMII模式下PHY的软件初始化流程
https://blog.csdn.net/fengying765/article/details/6762937Linux网络子系统之---- PHY 配置
https://blog.csdn.net/jk198310/article/details/12909341linux以太网驱动总结
https://blog.csdn.net/helloyizhou/article/details/72675533Linux系统网卡驱动phy工作原理解析
https://blog.csdn.net/xwb1040885790/article/details/103880341linux ethernet PHY 驱动
https://blog.csdn.net/jackjones_008/article/details/42004831linux网络设备—mdio总线
https://blog.csdn.net/weixin_34138056/article/details/86449521基于对zynq以太网驱动的分析理解linux phy子系统 好文
https://blog.csdn.net/xiangweiky/article/details/104345892Linux网络子系统之---- PHY 配置
https://blog.csdn.net/a746742897/article/details/72876314