phy device 和 driver

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,
                               &reg_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 来配对的.



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值