设备树配置
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.youkuaiyun.com/subfate/article/details/44900651
以太网驱动的流程浅析(五)-mii_bus初始化以及phy id的获取
https://cloud.tencent.com/developer/article/1545855
PHY Linux 驱动
https://blog.youkuaiyun.com/doitsjz/article/details/70331804
以太网PHY自动协商和其在Linux下的初始化
https://blog.youkuaiyun.com/kl1125290220/article/details/79859742
RMII模式下PHY的软件初始化流程
https://blog.youkuaiyun.com/fengying765/article/details/6762937
Linux网络子系统之---- PHY 配置
https://blog.youkuaiyun.com/jk198310/article/details/12909341
linux以太网驱动总结
https://blog.youkuaiyun.com/helloyizhou/article/details/72675533
Linux系统网卡驱动phy工作原理解析
https://blog.youkuaiyun.com/xwb1040885790/article/details/103880341
linux ethernet PHY 驱动
https://blog.youkuaiyun.com/jackjones_008/article/details/42004831
linux网络设备—mdio总线
https://blog.youkuaiyun.com/weixin_34138056/article/details/86449521
基于对zynq以太网驱动的分析理解linux phy子系统 好文
https://blog.youkuaiyun.com/xiangweiky/article/details/104345892
Linux网络子系统之---- PHY 配置
https://blog.youkuaiyun.com/a746742897/article/details/72876314

本文详细解析了PHY设备在Linux环境下的初始化流程,从设备树配置到总线初始化,再到PHY设备的注册与初始化过程。重点介绍了MDIO总线的作用、PHY驱动的注册与匹配机制,以及phy_device的创建和注册。深入分析了marvell PHY驱动的实例,展示了如何通过设备树匹配特定的PHY设备。
183

被折叠的 条评论
为什么被折叠?



