phy device的注册

下面这段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函数了

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`phy_connect_direct`是一个函数,用于将网络设备连接到PHY设备。其定义如下: ```c struct phy_device *phy_connect_direct(struct net_device *dev, struct phy_device *phydev, void (*handle) (struct net_device *), phy_interface_t interface) ``` 该函数有四个参数: - `dev`:要连接的网络设备。 - `phydev`:要连接的PHY设备。如果为NULL,则会通过PHY ID自动匹配合适的PHY设备。 - `handle`:回调函数,用于在网络设备接收到数据时进行处理。 - `interface`:PHY接口类型,如PHY_INTERFACE_MODE_MII、PHY_INTERFACE_MODE_GMII等。 在函数内部,首先会调用`phy_device_create()`函数创建一个`phy_device`结构体,表示要连接的PHY设备。如果`phydev`为NULL,则会通过PHY ID自动匹配合适的PHY设备。匹配过程会调用`phy_find_first()`函数,该函数会遍历系统中所有已经注册PHY驱动程序,并与当前PHY设备的ID信息进行匹配。如果找到了匹配的PHY驱动程序,则返回该驱动程序对应的`phy_driver`结构体。如果没有找到匹配的PHY驱动程序,则返回NULL。如果找到了匹配的PHY设备,则会将PHY设备的ID信息保存到`phy_device`结构体中。 接下来,会调用`phy_device_connect()`函数,建立网络设备和PHY设备之间的连接。在这个过程中,会配置PHY设备的寄存器,设置PHY接口类型、速率、双工模式等参数。如果连接成功,则返回一个指向`phy_device`结构体的指针,否则返回NULL。 如果连接成功,则会设置网络设备的`phydev`指针,表示该网络设备已经连接到了PHY设备。同时,会设置网络设备的`netdev_ops`和`ethtool_ops`字段,用于处理网络设备的数据包和配置信息。 总之,`phy_connect_direct`函数的作用是将网络设备连接到PHY设备,并设置网络设备的`phydev`指针和相关回调函数。在实现中,会自动匹配合适的PHY设备,并根据PHY接口类型等参数进行初始化配置。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值