phy device的注册

来源:互联网 发布:c语言 char 中文 编辑:程序博客网 时间:2024/05/16 12:17
下面这段code 可以看清struct phy_device *phy 注册的流程    
    phy = get_phy_device(mdio, addr, is_c45);
    if (!phy || IS_ERR(phy))
        return -EIO;

    phy->irq = mdio->irq[addr];

    /* All data is now stored in the phy struct;
     * register it
     */
    rc = phy_device_register(phy);
    if (rc) {
        phy_device_free(phy);
        return -ENODEV;
    }
这样注册之后就可以在具体的调用module_phy_driver 来注册对应的phy driver.
这里面涉及两个重要的函数,一个是get_phy_device,一个是phy_device_register。
我们先看第一个get_phy_device
struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45)
{
    struct phy_c45_device_ids c45_ids = {0};
    u32 phy_id = 0;
    int r;
//调用get_phy_id 得到phy id,将结果保存在phy_id 中,可见是一个u32 类型的值
    r = get_phy_id(bus, addr, &phy_id, is_c45, &c45_ids);
//r不为null的话,说明没有正确得到了phy id,直接返回
    if (r)
        return ERR_PTR(r);
//如果phyid 为0x1fffffff,说明没有phy device,直接返回
    /* If the phy_id is mostly Fs, there is no device there */
    if ((phy_id & 0x1fffffff) == 0x1fffffff)
        return ERR_PTR(-ENODEV);
//如果得到phy id的话,则create 这个device
    return phy_device_create(bus, addr, phy_id, is_c45, &c45_ids);
}

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;
}
从get_phy_id 中可以看到phy_id 是由两个16为的MII_PHYSID1和MII_PHYSID2 组合起来的。都是通过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);

    return retval;
}
mdiobus_read中就直接调用bus->read
而这里的mii_bus *bus的read一般是mdio driver中实现
例如:
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;
}
回到get_phy_device 中通过mdio driver得到phy id后,就通过phy_device_create 来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;
//申请phy_device *dev
    /* We allocate the device, and initialize the default values */
    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    if (!dev)
        return ERR_PTR(-ENOMEM);
//dev->mdio的赋值
    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;
    mdiodev->addr = addr;
    mdiodev->flags = MDIO_DEVICE_FLAG_PHY;
    mdiodev->device_free = phy_mdio_device_free;
    mdiodev->device_remove = phy_mdio_device_remove;

    // 要求上层insmod 这个driver对应的ko
    request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, MDIO_ID_ARGS(phy_id));
//初始化这个mdiodev->dev
    device_initialize(&mdiodev->dev);

    return dev;
}
最后回到最前面,当成功创建phy对应的device后调用phy_device_register 注册device
int phy_device_register(struct phy_device *phydev)
{
    int err;

    err = mdiobus_register_device(&phydev->mdio);
    if (err)
        return err;

    /* Run all of the fixups for this PHY */
    err = phy_scan_fixups(phydev);
    if (err) {
        pr_err("PHY %d failed to initialize\n", phydev->mdio.addr);
        goto out;
    }

    phydev->mdio.dev.groups = phy_dev_groups;

    err = device_add(&phydev->mdio.dev);
    if (err) {
        pr_err("PHY %d failed to add\n", phydev->mdio.addr);
        goto out;
    }

    return 0;

 out:
    mdiobus_unregister_device(&phydev->mdio);
    return err;
}
phy_device_register 主要是把phy device注册到MDIO bus没,这样就会调用phy driver的probe函数了