文章目录
- 一、mdio与rmii/sgmii
- 二、主控mac控制器配置
- 三、phy driver与device的匹配规则
一、mdio与rmii/sgmii
接上一篇文章《Linux—phy外设调试(一)》,在上一篇中我们说到我们还遗留了几个问题没有解释,其中提到的有mdio总线
和rmii/sgmii接口
。我们需要先了解下这些接口硬件的概念。
首先什么是mdio总线?
它是一种简单的双线串行接口,将管理器件(如MAC控制器、微处理器)与具备管理功能的收发器(如多端口吉比特以太网收发器或 10GbE XAUI收发器)相连接,从而控制收发器并从收发器收集状态信息。一般包含2个管脚:MDC和MDIO。MDC是管理数据的时钟输入,MDIO是管理数据的输入输出双向接口,数据是与MDC时钟同步的。
接着什么是rmii/sgmii接口?
英文展开是Reduced Media Independent Interface/Serial Gigabit Media Independent Interface的缩写,翻译过来简化媒体独立接口/串行吉比特媒体独立接口,它是以太网媒体访问控制器(MAC)和物理层器件(PHY)的连接总线。如下图两个红圈所示:
二、主控mac控制器配置
我们知道外设phy芯片是与主控mac控制器进行通信交互的,所以调试phy我们需要打开主控mac控制器相关配置,以内核原生高通平台代码为例,在目录\drivers\net\ethernet\qualcomm\emac
,我们打开对应目录Makefile文件见:
#
# Makefile for the Qualcomm Technologies, Inc. EMAC Gigabit Ethernet driver
#
obj-$(CONFIG_QCOM_EMAC) += qcom-emac.o
qcom-emac-objs := emac.o emac-mac.o emac-phy.o emac-sgmii.o
同时我们看下emac.c
有个emac_platform_driver
结构体需要重点关注,当设备树中有对应匹配项,主控的mac控制器就会正常被驱动。这部分与phy芯片通过sgmii接口相互通信交互。
static struct platform_driver emac_platform_driver = {
.probe = emac_probe,
.remove = emac_remove,
.driver = {
.name = "qcom-emac",
.of_match_table = emac_dt_match,
.acpi_match_table = ACPI_PTR(emac_acpi_match),
},
};
三、phy driver与device的匹配规则
再回到我们关注的phy目录上/drivers/net/phy
通过对应Makefile文件,我们知道配置CONFIG_PHYLIB
宏,mdio_bus.c也会编译进内核。
libphy-y := phy.o phy_device.o mdio_bus.o mdio_device.o
obj-$(CONFIG_PHYLIB) += libphy.o
在mdio_bus.c中有mdiobus_scan函数
,在有phy driver或phy devices注册时会触发总线scan扫描:
int __mdiobus_register(struct mii_bus *bus, struct module *owner)
{
... ...
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;
}
}
}
... ...
}
struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr)
{
struct phy_device *phydev;
int err;
phydev = get_phy_device(bus, addr, false);
... ...
}
在phy_device.c中我们可以看到,扫描完成后会拿到设备的phy_id
struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45)
{
... ...
r = get_phy_id(bus, addr, &phy_id, is_c45, &c45_ids);
if (r)
return ERR_PTR(r);
/* If the phy_id is mostly Fs, there is no device there */
if ((phy_id & 0x1fffffff) == 0x1fffffff)
return ERR_PTR(-ENODEV);
return phy_device_create(bus, addr, phy_id, is_c45, &c45_ids);
}
拿到phy_id有什么用?
,我们且看mdio_bus.c中,driver和devices的匹配规则,第一种是设备树匹配
,第二种是driver中存在match_phy_device
的方法,第三种就是设备的phy_id与driver支持的phy_id相匹配
,就算匹配成功。(不同内核版本此处有差异)
static int mdio_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);
if (of_driver_match_device(dev, drv))
return 1;
if (phydrv->match_phy_device)
return phydrv->match_phy_device(phydev);
return (phydrv->phy_id & phydrv->phy_id_mask) ==
(phydev->phy_id & phydrv->phy_id_mask);
}
正常在调试过程中我们可以查看内核文件系统/sys/bus/mdio_bus/
节点下,是否有对应的driver以及识别到的phy设备。