PHY是IEEE 802.3规定的一个标准模块,MAC控制器可以通过MDIO对PHY进行配置或者读取phy相关状态,PHY内部寄存器必须满足
1 PHY芯片的寄存器地址空间是5位,一般由外部硬件连接决定。
2 地址空间031共32个寄存器,IEEE定义了0 - 15这16个寄存器的功能,16-31这16个寄存器由厂商自行实现。也就是说不管哪个厂商的PHY芯片,其中0~15这16个寄存器是一模一样的。 仅靠这 16个寄存器完全可以驱动起PHY芯片,至少能保证基本的网络数据通信。因此 Linux 内核有通用 PHY 驱动。所以 一般情况下,按道理来讲,不管你使用的哪个厂家的 PHY 芯片,都可以使用 Linux 的这个通用 PHY 驱动来验证网络工作是否正常。
如 寄存器0是PHY控制寄存器,通过Control Register可以对PHY的主要工作状态进行设置。 寄存器1是PHY状态寄存器,主要包含PHY的状态信息。等等 具体可以去找具体的寄存器信息,这里不做详细叙述。
如:
MAC控制器通过MDIO总线来管理phy设备,mdio总线与i2c总线类似,可以一个主机对应多个从设备,每个从设备都有地址。mdio最多接32个phy设备。
对应的目录是/sys/mdio,在/sys/mdio/devices目录中会有挂载在mdio的phy设备,在/sys/mdio/drivers中会有phy设备的驱动。
如:
/sys/bus/mdio_bus/devices/stmmac-0:00
其中 stmmac-0:00 表示 PHY 地址是 0。
该命令会读取 0~31 的所有寄存器,所以可以查看对应的寄存器值
cat /sys/bus/mdio_bus/devices/stmmac-0:00/phy_registers
root@OpenSDT:/sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00# cat phy_registers
0: 0x1140
1: 0x7989
2: 0x1c
3: 0xc982
4: 0x1e1
5: 0x0
6: 0x64
7: 0x2001
8: 0x0
9: 0xe00
10: 0x0
11: 0x0
12: 0x0
13: 0x0
14: 0x0
15: 0x2000
16: 0x23
17: 0x0
18: 0xffff
19: 0x0
20: 0x0
21: 0x0
22: 0xf00
23: 0xf00
24: 0x19c
25: 0x40
26: 0x5000
27: 0x802a
28: 0x0
29: 0x220
30: 0x0
31: 0x0
root@OpenSDT:/sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00#
drivers\net\phy\phy_device.c
static struct phy_driver genphy_driver = {
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
.name = "Generic PHY",
.soft_reset = genphy_no_soft_reset,
.config_init = genphy_config_init,
.features = PHY_GBIT_FEATURES | SUPPORTED_MII |
SUPPORTED_AUI | SUPPORTED_FIBRE |
SUPPORTED_BNC,
.aneg_done = genphy_aneg_done,
.suspend = genphy_suspend,
.resume = genphy_resume,
.set_loopback = genphy_loopback,
};
/*
不仅注册了mdio_bus总线,还注册了一个通用的PHY驱动作为缺省的内核PHY驱动,
但是如果PHY芯片的内部寄存器和802.3定义的并不一样或者需要特殊的功能配置
以实现更强的功能,这就需要专有的驱动。
*/
static int __init phy_init(void)
{
int rc;
//mdio 总线初始化注册:
/*
注册:
/sys/class/mdio_bus
/sys/bus/mdio_bus
*/
rc = mdio_bus_init();
if (rc)
return rc;
//注册 名为 "Generic 10G PHY" phy驱动 到 mdio 总线
rc = phy_driver_register(&genphy_10g_driver, THIS_MODULE);
if (rc)
goto err_10g;
//注册 名为 "Generic PHY" phy驱动 到 mdio 总线
rc = phy_driver_register(&genphy_driver, THIS_MODULE);
if (rc) {
phy_driver_unregister(&genphy_10g_driver);
err_10g:
mdio_bus_exit();
}
return rc;
}
static void __exit phy_exit(void)
{
phy_driver_unregister(&genphy_10g_driver);
phy_driver_unregister(&genphy_driver);
mdio_bus_exit();
}
//这行的作用非常重要,这一行就决定了内核在启动的时候会调用该函数,注册完了之后紧接着又注册一个通用的PHY驱动
subsys_initcall(phy_init);
module_exit(phy_exit);
\drivers\net\phy\mdio_bus.c
// sys/class/mdio_bus
static struct class mdio_bus_class = {
.name = "mdio_bus",
.dev_release = mdiobus_release,
};
// /sys/bus/mdio_bus
struct bus_type mdio_bus_type = {
.name = "mdio_bus",
.match = mdio_bus_match,
.uevent = mdio_uevent,
};
EXPORT_SYMBOL(mdio_bus_type);
int __init mdio_bus_init(void)
{
int ret;
// 注册类
// 即 /sys/class/mdio_bus
ret = class_register(&mdio_bus_class);
if (!ret) {
//注册名为 "mdio_bus" 的总线。即注册 mdio 总线
/*
struct bus_type mdio_bus_type = {
.name = "mdio_bus",
.match = mdio_bus_match,
.uevent = mdio_uevent,
};
*/
//即 /sys/bus/mdio_bus
ret = bus_register(&mdio_bus_type);
if (ret)
class_unregister(&mdio_bus_class);
}
return ret;
}
EXPORT_SYMBOL_GPL(mdio_bus_init);
int phy_driver_register(struct phy_driver *new_driver, struct module *owner)
{
int retval;
/*
//phy 驱动
struct phy_driver
.name = "Generic PHY",
//MDIO 驱动通用部分
struct mdio_driver_common mdiodrv;-----+
|
|
+---struct mdio_driver_common mdiodrv;
int flags |= MDIO_DEVICE_IS_PHY
struct device_driver driver;
.name = "Generic PHY",
//总线 //mdio 总线
struct bus_type *bus;-------------------------------struct bus_type mdio_bus_type = {
int (*probe) (struct device *dev); = phy_probe .name = "mdio_bus",
... .match = mdio_bus_match,
.uevent = mdio_uevent,
struct subsys_private *p;
//相关的驱动程序列表
struct kset *drivers_kset;
*/
new_driver->mdiodrv.flags |= MDIO_DEVICE_IS_PHY;
new_driver->mdiodrv.driver.name = new_driver->name;
new_driver->mdiodrv.driver.bus = &mdio_bus_type;//驱动所挂载的总线类型 /sys/bus/mdio_bus
new_driver->mdiodrv.driver.probe = phy_probe;//当匹配成功回调用的函数
new_driver->mdiodrv.driver.remove = phy_remove;
new_driver->mdiodrv.driver.owner = owner;
new_driver->mdiodrv.driver.probe_type = PROBE_FORCE_SYNCHRONOUS;
retval = driver_register(&new_driver->mdiodrv.driver);
if (retval) {
pr_err("%s: Error %d in registering driver\n",
new_driver->name, retval);
return retval;
}
pr_debug("%s: Registered new driver\n", new_driver->name);
return 0;
}
EXPORT_SYMBOL(phy_driver_register);
所以 phy 驱动注册部分主要做了如下工作:
1 创建 初始化 struct phy_driver
2 设置struct phy_driver ,
如所在的名为“mdio_bus” 的mdio_bus_type 总线
如 匹配成功后的 probe()函数
…
3 注册 phy_driver
至此
phy_driver注册成功了,那就差phy_device的注册。看代码就知道 phy_device的注册不依靠设备树,而是在GMAC控制器注册时候 在其中的mdiobus_register中会注册phy_device 。