mdio总线

mdio总线用于链接mac和phy.其主要的code在drivers/net/phy/mdio_bus.c 中
static int __init phy_init(void)
{
    int rc;

    rc = mdio_bus_init();
    if (rc)
        return rc;

    rc = phy_drivers_register(genphy_driver,
                  ARRAY_SIZE(genphy_driver), THIS_MODULE);
    if (rc)
        mdio_bus_exit();

    return rc;
}

subsys_initcall(phy_init);
一般在phy_init中调用mdio_bus_init
int __init mdio_bus_init(void)
{
    int ret;

    ret = class_register(&mdio_bus_class);
    if (!ret) {
        ret = bus_register(&mdio_bus_type);
        if (ret)
            class_unregister(&mdio_bus_class);
    }

    return ret;
}
从mdio_bus_init中可以看到其主要是调用class_register(&mdio_bus_class);注册了一个设备类/sys/class/mdio_bus
static struct class mdio_bus_class = {
    .name        = "mdio_bus",
    .dev_release    = mdiobus_release,
};
如果设备类注册成功,则调用bus_register(&mdio_bus_type);注册一个总线类型/sys/bus/mdio
struct bus_type mdio_bus_type = {
    .name        = "mdio_bus",
    .match        = mdio_bus_match,
    .pm        = MDIO_BUS_PM_OPS,
};
在使用mdio bus前需要调用mdiobus_alloc申请一个mii_bus结构。那什么时候MII呢?
MII即媒体独立接口,它是IEEE-802.3定义的以太网行业标准。”媒体独立”表明在不对MAC硬件重新设计或替换的情况下,任何类型的PHY设备都可以正常工作。它包括一个数据接口,以及一个MAC和PHY之间的管理接口。
static inline struct mii_bus *mdiobus_alloc(void)
{
    return mdiobus_alloc_size(0);
}
注意这里调用mdiobus_alloc_size的时候size是0
struct mii_bus *mdiobus_alloc_size(size_t size)
{
    struct mii_bus *bus;
    size_t aligned_size = ALIGN(sizeof(*bus), NETDEV_ALIGN);
    size_t alloc_size;
    int i;
//由于size是0,因此走else的case。也就是申请的size等于mii_bus的size.
    /* If we alloc extra space, it should be aligned */
    if (size)
        alloc_size = aligned_size + size;
    else
        alloc_size = sizeof(*bus);

    bus = kzalloc(alloc_size, GFP_KERNEL);
    if (!bus)
        return NULL;

    bus->state = MDIOBUS_ALLOCATED;
//size为0,因此bus->priv 为null
    if (size)
        bus->priv = (void *)bus + aligned_size;

//一个mii_bus最多支持32个中断,这里的PHY_MAX_ADDR等于32
    /* Initialise the interrupts to polling */
    for (i = 0; i < PHY_MAX_ADDR; i++)
        bus->irq[i] = PHY_POLL;

    return bus;
}
申请mii_bus 后要调用mdiobus_register 来注册mii_bus,主要用于给mii_bus个个成员变量赋值.
#define mdiobus_register(bus) __mdiobus_register(bus, THIS_MODULE)

int __mdiobus_register(struct mii_bus *bus, struct module *owner)
{
    struct mdio_device *mdiodev;
    int i, err;

    if (NULL == bus || NULL == bus->name ||
        NULL == bus->read || NULL == bus->write)
        return -EINVAL;

    BUG_ON(bus->state != MDIOBUS_ALLOCATED &&
           bus->state != MDIOBUS_UNREGISTERED);

    bus->owner = owner;
    bus->dev.parent = bus->parent;
    bus->dev.class = &mdio_bus_class;
    bus->dev.groups = NULL;
    dev_set_name(&bus->dev, "%s", bus->id);
//注册这个bus对应的device
    err = device_register(&bus->dev);
    if (err) {
        pr_err("mii_bus %s failed to register\n", bus->id);
        put_device(&bus->dev);
        return -EINVAL;
    }

    mutex_init(&bus->mdio_lock);
//调用bus->reset 函数
    if (bus->reset)
        bus->reset(bus);

    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;
            }
        }
    }

    bus->state = MDIOBUS_REGISTERED;
    pr_info("%s: probed\n", bus->name);
    return 0;
}
在__mdiobus_register 中除了注册bus->dev,最重要的就是针对每个phy调用mdiobus_scan得到对应的phy_device *phydev
struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr)
{
    struct phy_device *phydev;
    int err;
//通过get_phy_device得到addr对应的phy_device,这里的addr是从0~32
    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);
//注册这个phy_device *phydev
    err = phy_device_register(phydev);
    if (err) {
        phy_device_free(phydev);
        return ERR_PTR(-ENODEV);
    }

    return phydev;
}

int phy_device_register(struct phy_device *phydev)
{
    int err;
//在mido_map这个数组中建立addr和struct mdio_device *mdiodev的对应关系 mdiodev->bus->mdio_map[mdiodev->addr] = mdiodev;
    err = mdiobus_register_device(&phydev->mdio);
    if (err)
        return err;
//针对需要fixed的phy调用fixup->run。并设置phydev->has_fixups = true;
    /* 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;
//添加phydev->mdio.dev 这个device
    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;
}
--------------------- 本文来自 tiantao2012 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/tiantao2012/article/details/73087798?utm_source=copy

猜你喜欢

转载自blog.csdn.net/zjy900507/article/details/82979935