慢慢欣赏linux phy驱动初始化

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

设备树配置

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值