phy的驱动分为platform_driver_register和platform_device_register 两部分。这两部分都是挂在mdio总线上。
这里以hns驱动为例。说先定义mido对应的struct mii_bus *new_bus;
static int hns_mdio_probe(struct platform_device *pdev)
{
struct hns_mdio_device *mdio_dev;
struct mii_bus *new_bus;
struct resource *res;
int ret = -ENODEV;
if (!pdev) {
dev_err(NULL, "pdev is NULL!\r\n");
return -ENODEV;
}
mdio_dev = devm_kzalloc(&pdev->dev, sizeof(*mdio_dev), GFP_KERNEL);
if (!mdio_dev)
return -ENOMEM;
new_bus = devm_mdiobus_alloc(&pdev->dev);
if (!new_bus) {
dev_err(&pdev->dev, "mdiobus_alloc fail!\n");
return -ENOMEM;
}
new_bus->name = MDIO_BUS_NAME;
new_bus->read = hns_mdio_read;
new_bus->write = hns_mdio_write;
new_bus->reset = hns_mdio_reset;
new_bus->priv = mdio_dev;
new_bus->parent = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mdio_dev->vbase = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(mdio_dev->vbase)) {
ret = PTR_ERR(mdio_dev->vbase);
return ret;
}
platform_set_drvdata(pdev, new_bus);
snprintf(new_bus->id, MII_BUS_ID_SIZE, "%s-%s", "Mii",
dev_name(&pdev->dev));
if (dev_of_node(&pdev->dev)) {
struct of_phandle_args reg_args;
ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
"subctrl-vbase",
4,
0,
®_args);
if (!ret) {
mdio_dev->subctrl_vbase =
syscon_node_to_regmap(reg_args.np);
if (IS_ERR(mdio_dev->subctrl_vbase)) {
dev_warn(&pdev->dev, "syscon_node_to_regmap error\n");
mdio_dev->subctrl_vbase = NULL;
} else {
if (reg_args.args_count == 4) {
mdio_dev->sc_reg.mdio_clk_en =
(u16)reg_args.args[0];
mdio_dev->sc_reg.mdio_clk_dis =
(u16)reg_args.args[0] + 4;
mdio_dev->sc_reg.mdio_reset_req =
(u16)reg_args.args[1];
mdio_dev->sc_reg.mdio_reset_dreq =
(u16)reg_args.args[1] + 4;
mdio_dev->sc_reg.mdio_clk_st =
(u16)reg_args.args[2];
mdio_dev->sc_reg.mdio_reset_st =
(u16)reg_args.args[3];
} else {
/* for compatible */
mdio_dev->sc_reg.mdio_clk_en =
MDIO_SC_CLK_EN;
mdio_dev->sc_reg.mdio_clk_dis =
MDIO_SC_CLK_DIS;
mdio_dev->sc_reg.mdio_reset_req =
MDIO_SC_RESET_REQ;
mdio_dev->sc_reg.mdio_reset_dreq =
MDIO_SC_RESET_DREQ;
mdio_dev->sc_reg.mdio_clk_st =
MDIO_SC_CLK_ST;
mdio_dev->sc_reg.mdio_reset_st =
MDIO_SC_RESET_ST;
}
}
} else {
dev_warn(&pdev->dev, "find syscon ret = %#x\n", ret);
mdio_dev->subctrl_vbase = NULL;
}
ret = of_mdiobus_register(new_bus, pdev->dev.of_node);
} else if (is_acpi_node(pdev->dev.fwnode)) {
/* Clear all the IRQ properties */
memset(new_bus->irq, PHY_POLL, 4 * PHY_MAX_ADDR);
/* Mask out all PHYs from auto probing. */
new_bus->phy_mask = ~0;
/* Register the MDIO bus */
核心是调用mdiobus_register,然后scan这个bus上对应的device
ret = mdiobus_register(new_bus);
} else {
dev_err(&pdev->dev, "Can not get cfg data from DT or ACPI\n");
ret = -ENXIO;
}
if (ret) {
dev_err(&pdev->dev, "Cannot register as MDIO bus!\n");
platform_set_drvdata(pdev, NULL);
return ret;
}
return 0;
}
mdiobus_register->__mdiobus_register
for (i = 0; i < PHY_MAX_ADDR; i++) {
if ((bus->phy_mask & (1 << i)) == 0) {
struct phy_device *phydev;
phydev = mdiobus_scan(bus, i);
if (IS_ERR(phydev) && (PTR_ERR(phydev) != -ENODEV)) {
err = PTR_ERR(phydev);
goto error;
}
}
}
根据phy id去通过上面注册的mdio的read函数实际去读芯片的寄存器,看这个phy id 对应的device是否存在
struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr)
{
struct phy_device *phydev;
int err;
phydev = get_phy_device(bus, addr, false);
if (IS_ERR(phydev))
return phydev;
/*
* For DT, see if the auto-probed phy has a correspoding child
* in the bus node, and set the of_node pointer in this case.
*/
of_mdiobus_link_mdiodev(bus, &phydev->mdio);
err = phy_device_register(phydev);
if (err) {
phy_device_free(phydev);
return ERR_PTR(-ENODEV);
}
return phydev;
}
如果get_phy_device 返回成功的话,说明这个phy id对应的硬件是存在的,就调用phy_device_register 创建phy device
static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id,
bool is_c45, struct phy_c45_device_ids *c45_ids)
{
int phy_reg;
if (is_c45)
return get_phy_c45_ids(bus, addr, phy_id, c45_ids);
/* Grab the bits from PHYIR1, and put them in the upper half */
phy_reg = mdiobus_read(bus, addr, MII_PHYSID1);
if (phy_reg < 0)
return -EIO;
*phy_id = (phy_reg & 0xffff) << 16;
/* Grab the bits from PHYIR2, and put them in the lower half */
phy_reg = mdiobus_read(bus, addr, MII_PHYSID2);
if (phy_reg < 0)
return -EIO;
*phy_id |= (phy_reg & 0xffff);
return 0;
}
可见最终通过mdiobus_read 去读寄存器
int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum)
{
int retval;
BUG_ON(in_interrupt());
mutex_lock(&bus->mdio_lock);
retval = bus->read(bus, addr, regnum);
mutex_unlock(&bus->mdio_lock);
trace_mdio_access(bus, 1, addr, regnum, retval, retval);
return retval;
}
最终调用mido bus的read函数去读的。
到这里已经准备好phy device。下来就要调用module_phy_driver(marvell_drivers); 来注册phy driver
那phy的device和driver是如何配对的呢?
get_phy_device->phy_device_create
struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
bool is_c45,
struct phy_c45_device_ids *c45_ids)
{
struct phy_device *dev;
struct mdio_device *mdiodev;
/* We allocate the device, and initialize the default values */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return ERR_PTR(-ENOMEM);
mdiodev = &dev->mdio;
mdiodev->dev.release = phy_device_release;
mdiodev->dev.parent = &bus->dev;
mdiodev->dev.bus = &mdio_bus_type;
mdiodev->bus = bus;
mdiodev->pm_ops = MDIO_BUS_PHY_PM_OPS;
mdiodev->bus_match = phy_bus_match;
}
可以看到bus_match 函数是phy_bus_match
static int phy_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);
const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids);
int i;
if (!(phydrv->mdiodrv.flags & MDIO_DEVICE_IS_PHY))
return 0;
if (phydrv->match_phy_device)
return phydrv->match_phy_device(phydev);
if (phydev->is_c45) {
for (i = 1; i < num_ids; i++) {
if (!(phydev->c45_ids.devices_in_package & (1 << i)))
continue;
if ((phydrv->phy_id & phydrv->phy_id_mask) ==
(phydev->c45_ids.device_ids[i] &
phydrv->phy_id_mask))
return 1;
}
return 0;
} else {
return (phydrv->phy_id & phydrv->phy_id_mask) ==
(phydev->phy_id & phydrv->phy_id_mask);
}
}
从phy_bus_match 中可以知道是根据phy id 来配对的.
phy device 和 driver
最新推荐文章于 2024-06-08 17:55:29 发布