MDIO总线

文章详细阐述了Linux内核中mdio总线的注册过程,包括bus_register接口的使用,以及mido总线设备的创建和注册。通过对mii_bus的扫描,发现并创建phy_device,然后进行设备注册。同时,介绍了phy_driver的注册和probe过程,强调了phy驱动如何通过mii_bus与硬件交互。文章末尾提及mdio设备的操作,但未展开讨论。

基于linux-3.14.16

首先要搞清楚总线的位置,即硬件上的位置
在这里插入图片描述
如上图,mdio总线是mac和phy之间的连接方式,主要用于配置配置phy的寄存器,所以phy应该是其的一类物理设备,mdio总线驱动和总线设备都是围绕phy工作的。

一、总线注册

在这里插入图片描述
常规操作,定义一个bus_type调用bus_register接口注册一个总线。

二、mido总线设备

1、platform总线

设备树有节点fec
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、平台总线probe

在这里插入图片描述
fec_enet_mii_init即将会添加一个mdio总线设备
分配一个mii_bus,这个mii_bus内部有一个device,实际是一个mido总线设备(????)。

1)注册mii_bus

在这里插入图片描述
初始化mii_bus
在这里插入图片描述
在这里插入图片描述
最后注册mii_bus,我们看看是怎么注册的
在这里插入图片描述
扫描phy,每次添加新的mii_bus都会去扫描mido总线,如果有读到phy id,就会创建一个phy_device并注册。

这里是根据phy_mask的标记去决定要不要遍历,应该是有添加了就不会去扫描这个地址了。
在这里插入图片描述
看看是怎么扫描的,mdiobus_scan

2)创建并注册phy_device

看看mdiobus_scan的工作
总的来说,是扫描硬件,发现mido总线上有phy就创建一个phy_device,然后作为mido总线设备注册。
在这里插入图片描述

看看get_phy_device干了什么

get_phy_id会在mdio总线上发命令去读phy id,是硬件操作
有phy硬件就会创建phy_device
在这里插入图片描述
看看是怎么创建phy_device的,phy_device_create
在这里插入图片描述

在这里插入图片描述
这里看到,原来phy_device才是作为mdio的总线设备。

phy_device_register注册
硬件初始化
在这里插入图片描述
device_add就是注册设备的底层操作,Linux设备驱动模型(二)有说明
在这里插入图片描述

至此就是mdio总线设备的添加过程。

三、mido总线驱动

就是phy_driver
一般是具体phy芯片生产商提供
下面作简要分析
在这里插入图片描述
这是个数组,这里只截图了一个
在这里插入图片描述
匹配后会执行,应该会执行驱动的probe,但这里没有给probe,发现是注册驱动的时候给了默认的,不用驱动自己实现

1、phy_drivers_register

在这里插入图片描述
在这里插入图片描述
这里看出是mdio的总线驱动了,并紫萼初始化了驱动的probe

2、phy_probe

好像没干什么,跳过
在这里插入图片描述

至此mido总线的注册,mdio总线设备,mdio总线驱动的添加都分析完了。
关于这个mii_bus,还是没有分析清楚了,现在可以知道的是,phy驱动在需要操作寄存器的时候,会使用mii_bus的一些回调函数,
真正操作硬件的是mii_bus。

还有一点,mdio_bus_type的probe哪去了???
没有的话,也会直接执行驱动的probe了

四、mdio设备怎么被操作

待续…

<think>我们正在讨论的是Linux内核中mdio总线的获取方法。 根据引用[1],我们知道mdio总线是mac和phy之间的连接方式,主要用于配置phy的寄存器。在Linux内核中,mdio总线由`struct mii_bus`表示。 那么如何获取mdio总线设备呢?常见方法如下: 1. **通过平台设备(platform device)获取**:在内核中,mdio总线通常作为平台设备(platform device)注册。因此,我们可以通过平台设备机制来获取mdio总线设备。例如,使用`platform_get_resource()`等函数获取资源,然后创建并注册`mii_bus`结构体。 2. **通过设备树(Device Tree)获取**:在设备树中,mdio总线节点通常位于mac节点下或者作为独立的节点。我们可以通过解析设备树节点来获取mdio总线。例如,使用`of_mdio_find_bus()`函数通过设备树节点获取对应的`mii_bus`结构体。 3. **通过已知的网络设备获取**:如果已经有一个网络设备(如eth0)关联了phy设备,那么我们可以通过该网络设备找到phy设备,再通过phy设备找到mdio总线。具体路径为:`net_device` -> `phy_device` -> `mii_bus`。 4. **通过全局链表获取**:内核中所有注册的mdio总线都会被加入一个全局链表。我们可以遍历这个链表来查找特定的mdio总线。但是这种方法一般不建议,因为内核没有提供直接的接口,而且可能涉及同步问题。 下面分别详细介绍这几种方法: ### 1. 通过平台设备获取mdio总线 在平台设备驱动中,我们通常在probe函数中创建并注册mdio总线: ```c static int my_mdio_driver_probe(struct platform_device *pdev) { struct mii_bus *bus; // 分配mii_bus结构体 bus = mdiobus_alloc(); if (!bus) return -ENOMEM; // 设置总线相关参数,比如读写函数等 bus->name = "my_mdio_bus"; bus->read = my_mdio_read; bus->write = my_mdio_write; bus->parent = &pdev->dev; // 注册mdio总线 if (mdiobus_register(bus)) { mdiobus_free(bus); return -ENXIO; } // 将总线指针保存到平台设备私有数据中,以便其他地方获取 platform_set_drvdata(pdev, bus); return 0; } ``` 这样,如果其他地方有这个平台设备,就可以通过`platform_get_drvdata()`获取到mdio总线。 ### 2. 通过设备树获取mdio总线 如果设备树中有一个mdio节点,比如: ``` mdio { compatible = "my,mdio"; #address-cells = <1>; #size-cells = <0>; ethphy0: ethernet-phy@0 { reg = <0>; }; }; ``` 我们可以通过以下方式获取mdio总线: ```c struct device_node *mdio_node; struct mii_bus *mdio_bus; // 通过节点路径或别名找到设备树节点 mdio_node = of_find_node_by_path("/mdio"); if (!mdio_node) { // 错误处理 return -ENODEV; } // 通过设备树节点获取mdio总线 mdio_bus = of_mdio_find_bus(mdio_node); if (!mdio_bus) { // 错误处理 return -ENODEV; } ``` ### 3. 通过已知网络设备获取mdio总线 假设我们有一个网络设备`net_device *dev`,那么: ```c struct phy_device *phydev = dev->phydev; if (!phydev) { // 该网络设备没有关联的phy设备 return -ENODEV; } struct mii_bus *bus = phydev->mdio.bus; ``` ### 4. 通过内核全局链表获取(不推荐) 内核中所有注册的mdio总线都链接在全局链表`mdio_buses`上(注意:不同内核版本可能不同,且该链表可能没有导出符号)。我们可以通过以下方式遍历: ```c struct mii_bus *mii_bus; list_for_each_entry(mii_bus, &mdio_buses, list) { // 根据条件判断是否是我们需要的总线 if (strcmp(mii_bus->id, "my_mdio_bus") == 0) { // 找到了 break; } } ``` 但是这种方法在内核模块中可能无法使用,因为`mdio_buses`可能没有被导出(EXPORT_SYMBOL)。而且遍历全局链表需要加锁(使用`mutex_lock(&mdio_bus_list_lock)`),所以不推荐。 总结:推荐使用前三种方法,尤其是通过设备树获取的方法,因为设备树是现代Linux内核设备驱动的主流配置方式。 下面是一个具体示例,演示如何通过设备树节点获取mdio总线: ```c #include <linux/of.h> #include <linux/of_mdio.h> struct mii_bus *get_mdio_bus_by_node(const char *node_path) { struct device_node *node; struct mii_bus *mbus; node = of_find_node_by_path(node_path); if (!node) return NULL; mbus = of_mdio_find_bus(node); of_node_put(node); // 减少节点引用计数 return mbus; } ``` 调用方式: ```c struct mii_bus *bus = get_mdio_bus_by_node("/soc/mdio"); ``` 注意:节点路径需要根据实际设备树确定。 --- §§相关问题§§ 1. 如何注册一个mdio总线? 2. 如何通过设备树配置mdio总线? 3. 如何在mdio总线上注册一个phy设备? 4. 如何通过sysfs访问mdio总线设备? 参考文献: [^1]: MDIO总线。首先要搞清楚总线的位置,即硬件上的位置... [^2]: Linux ARM平台开发系列讲解(网络篇)1.4 MDIO总线设备phy device设备ID定义和获取... [^3]: klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices); // 把 platform_device 放入 platform_bus_type的device链表中... [^4]: Linux ARM平台开发系列讲解(网络篇)1.3 MDIO总线设备device和设备driver匹配... [^5]: Linux ARM平台开发系列讲解(网络篇)1.5 MDIO总线设备phy driver中phy ID的定义... </think>### 获取 MDIO 总线设备的方法 在 Linux 内核中,MDIO 总线是管理 PHY 设备的核心基础设施。以下是获取 MDIO 总线设备的常用方法: #### 1. **通过平台设备获取(推荐)** MDIO 总线通常作为平台设备注册到内核。可通过平台设备接口获取: ```c struct platform_device *mdio_pdev; struct mii_bus *mdio_bus; // 根据设备名查找平台设备 mdio_pdev = platform_device_alloc("mdio_bus", -1); if (!mdio_pdev) { // 错误处理 return -ENODEV; } // 获取关联的 mii_bus 结构 mdio_bus = dev_get_drvdata(&mdio_pdev->dev); ``` 此方法通过平台设备系统直接定位 MDIO 总线设备[^3]。 #### 2. **通过 PHY 设备反向追溯** 若已获得 PHY 设备,可通过其成员访问总线: ```c struct phy_device *phydev; struct mii_bus *mdio_bus; // 假设已获取 phydev (如前文方法) phydev = netdev->phydev; // 反向获取 MDIO 总线 mdio_bus = phydev->mdio.bus; ``` 该方法利用了 PHY 设备与 MDIO 总线的从属关系[^4]。 #### 3. **遍历全局总线列表** 内核维护所有注册的 MDIO 总线列表: ```c struct mii_bus *bus; int found = 0; mutex_lock(&mdio_bus_list_lock); list_for_each_entry(bus, &mdio_buses, list) { if (strcmp(bus->id, "target_mdio") == 0) { found = 1; break; } } mutex_unlock(&mdio_bus_list_lock); ``` 注意:需持有 `mdio_bus_list_lock` 锁保证线程安全[^1]。 #### 4. **通过设备树直接获取** 设备树中定义的总线可直接解析: ```c struct device_node *mdio_node; struct mii_bus *mdio_bus; // 定位设备树节点 mdio_node = of_find_node_by_path("/soc/mdio"); // 转换节点为 MDIO 总线 mdio_bus = of_mdio_find_bus(mdio_node); ``` #### 关键数据结构关系 ```mermaid graph TD A[platform_device] -->|dev_get_drvdata| B(mii_bus) C[phy_device] -->|mdio.bus| B D[设备树节点] -->|of_mdio_find_bus| B B -->|phy_map| E[PHY设备0] B -->|phy_map| F[PHY设备1] ``` > **重要提示**: > 1. MDIO 总线创建时会自动扫描连接的 PHY 设备并填充 `phy_map` 数组[^2] > 2. 总线读写操作通过 `mii_bus->read()` 和 `mii_bus->write()` 实现[^1] > 3. 总线名称 `bus->id` 通常在设备树中定义(如 `mdio0`) ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值