PHY设备驱动
Posted 四季帆
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PHY设备驱动相关的知识,希望对你有一定的参考价值。
1. 概述
MAC控制器的驱动使用的是platform总线的连接方式,PHY设备驱动是基于device、driver、bus的连接方式。
其驱动涉及如下几个重要部分:
总线 - sturct mii_bus (mii stand for media independent interface)
设备 - struct phy_device
驱动 - struct phy_driver
phy设备不像i2c/spi有一个board_info函数进行设备的添加,而是直接读取phy中的寄存器<根据IEEE的规定,PHY芯片的前16个寄存器的内容必须是固定的>。
2. mdio_bus总线
2.1 总线注册的入口函数
# linux-4.9.225\\drivers\\net\\phy\\phy_device.c
static int __init phy_init(void)
int rc;
rc = mdio_bus_init(); //mdio_bus总线的注册
if (rc)
return rc;
rc = phy_drivers_register(genphy_driver,ARRAY_SIZE(genphy_driver), THIS_MODULE); //通用PHY驱动
if (rc)
mdio_bus_exit();
return rc;
subsys_initcall(phy_init);
subsys_initcall(phy_init) 这行的作用非常重要,这一行就决定了内核在启动的时候会调用该函数,注册完了之后紧接着又注册一个通用的PHY驱动。
2.2 总线注册函数— mdio_bus_init解析
# linux-4.9.225\\drivers\\net\\phy\\mdio_bus.c
static struct class mdio_bus_class =
.name = "mdio_bus",
.dev_release = mdiobus_release,
;
static int mdio_bus_match(struct device *dev, struct device_driver *drv)
struct mdio_device *mdio = to_mdio_device(dev);
if (of_driver_match_device(dev, drv))
return 1;
if (mdio->bus_match)
return mdio->bus_match(dev, drv);
return 0;
struct bus_type mdio_bus_type =
.name = "mdio_bus", //总线名称
.match = mdio_bus_match, //用来匹配总线上设备和驱动的函数
.pm = MDIO_BUS_PM_OPS,
;
EXPORT_SYMBOL(mdio_bus_type);
int __init mdio_bus_init(void)
int ret;
ret = class_register(&mdio_bus_class); //注册设备类 (在linux设备模型中,我再仔细讲这个类的概念)
if (!ret)
ret = bus_register(&mdio_bus_type);//总线注册
if (ret)
class_unregister(&mdio_bus_class);
return ret;
其中
(1) class_register(&mdio_bus_class)执行后会有以下设备类:
/sys/class/mdio_bus
(2)bus_register(&mdio_bus_type)执行后会有以下总线类型:
/sys/bus/mdio_bus
2.3 总线中的match函数解析
/**
* mdio_bus_match - determine if given MDIO driver supports the given
* MDIO device
* @dev: target MDIO device
* @drv: given MDIO driver
*
* Description: Given a MDIO device, and a MDIO driver, return 1 if
* the driver supports the device. Otherwise, return 0. This may
* require calling the devices own match function, since different classes
* of MDIO devices have different match criteria.
*/
static int mdio_bus_match(struct device *dev, struct device_driver *drv)
struct mdio_device *mdio = to_mdio_device(dev);
if (of_driver_match_device(dev, drv))
return 1;
if (mdio->bus_match) //实现匹配的函数
return mdio->bus_match(dev, drv);
return 0;
3. 设备驱动的注册
在phy_init函数中不仅注册了mdio_bus总线,还注册了一个通用的PHY驱动作为缺省的内核PHY驱动,但是如果PHY芯片的内部寄存器和802.3定义的并不一样或者需要特殊的功能配置以实现更强的功能,这就需要专有的驱动。
对于市场上存在的主流PHY品牌,一般在内核源码 drivers\\net\\phy目录下都有对应的驱动。本节主要以realtek RTL8211F为例,讲述PHY的驱动,代码如下:
# linux-4.9.225\\drivers\\net\\phy\\realtek.c
static struct phy_driver realtek_drvs[] =
......
,
.phy_id = 0x001cc916,
.name = "RTL8211F Gigabit Ethernet",
.phy_id_mask = 0x001fffff,
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = &genphy_config_aneg,
.config_init = &rtl8211f_config_init,
.read_status = &genphy_read_status,
.ack_interrupt = &rtl8211f_ack_interrupt,
.config_intr = &rtl8211f_config_intr,
.suspend = genphy_suspend,
.resume = genphy_resume,
,
;
module_phy_driver(realtek_drvs); //注册PHY驱动
static struct mdio_device_id __maybe_unused realtek_tbl[] =
0x001cc912, 0x001fffff ,
0x001cc914, 0x001fffff ,
0x001cc915, 0x001fffff ,
0x001cc916, 0x001fffff ,
;
MODULE_DEVICE_TABLE(mdio, realtek_tbl);
3.1 phy驱动的注册
同一品牌的PHY设备有多种不同的型号,内核为了支持一次可以注册多个型号的PHY的驱动,在include\\linux\\phy.h中提供了用于注册PHY驱动的宏module_phy_driver。该宏的定义如下:
# linux-4.9.225\\include\\linux\\phy.h
#define phy_module_driver(__phy_drivers, __count) \\
static int __init phy_module_init(void) \\
\\
return phy_drivers_register(__phy_drivers, __count, THIS_MODULE); \\
#define module_phy_driver(__phy_drivers) \\
phy_module_driver(__phy_drivers, ARRAY_SIZE(__phy_drivers))
phy_driver_register定义如下(注意这里与老版本内核有一定的改动)
/**
* phy_driver_register - register a phy_driver with the PHY layer
* @new_driver: new phy_driver to register
* @owner: module owning this PHY
*/
int phy_driver_register(struct phy_driver *new_driver, struct module *owner)
int retval;
new_driver->mdiodrv.flags |= MDIO_DEVICE_IS_PHY;
new_driver->mdiodrv.driver.name = new_driver->name;//驱动名称
new_driver->mdiodrv.driver.bus = &mdio_bus_type; //驱动挂载的总线
new_driver->mdiodrv.driver.probe = phy_probe; //PHY设备和驱动匹配后调用的probe函数
new_driver->mdiodrv.driver.remove = phy_remove;
new_driver->mdiodrv.driver.owner = owner;
retval = driver_register(&new_driver->mdiodrv.driver); //向linux设备模型框架中注册device_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;
int phy_drivers_register(struct phy_driver *new_driver, int n,
struct module *owner)
int i, ret = 0;
for (i = 0; i < n; i++)
ret = phy_driver_register(new_driver + i, owner);//注册数组中所有的phy驱动
if (ret)
while (i-- > 0)
phy_driver_unregister(new_driver + i);
break;
return ret;
3.2 MODULE_DEVICE_TABLE(mdio, realtek_tbl)解析
宏定义展开后如下:
#define MODULE_DEVICE_TABLE(mdio, realtek_tbl) \\
extern const struct mdio_device_id __mod_mdio__realtek_tbl_device_table \\
__attribute__ ((unused, "realtek_tbl")))
4. 设备驱动与控制器驱动之间的关系图
以上是关于PHY设备驱动的主要内容,如果未能解决你的问题,请参考以下文章
RK3399驱动开发 | 10 - RK3399以太网gmac调试
RK3399驱动开发 | 10 - RK3399以太网gmac调试