PHY device driver

1 Overview

        The driver of the MAC controller uses the connection method of the platform bus, and the PHY device driver is based on the connection method of device, driver, and bus.

        Its driver involves the following important parts:

总线 - sturct mii_bus (mii stand for media independent interface)

device - struct phy_device

Driver - struct phy_driver

        The phy device does not have a board_info function to add the device like i2c/spi, but directly reads the registers in the phy <According to IEEE regulations, the contents of the first 16 registers of the PHY chip must be fixed>.

2. mdio_bus bus

2.1 Entry function for bus registration

# 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); 

        The subsys_initcall(phy_init) line is very important. This line determines that the kernel will call this function when it starts up. After the registration is completed, a general PHY driver is registered immediately.

2.2 Bus registration function — mdio_bus_init analysis

# 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;
}

        in

        (1) After class_register(&mdio_bus_class) is executed, there will be the following device classes:

/sys/class/mdio_bus

        (2) After bus_register(&mdio_bus_type) is executed, there will be the following bus types:

/sys/bus/mdio_bus

2.3 Analysis of the match function in the bus

/**
 * 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. Registration of device driver

        In the phy_init function, not only the mdio_bus bus is registered, but also a general PHY driver is registered as the default kernel PHY driver, but if the internal registers of the PHY chip are not the same as those defined by 802.3 or special function configuration is required to achieve stronger function, which requires a proprietary driver.

        For the mainstream PHY brands in the market, there are generally corresponding drivers in the kernel source code drivers\net\phy directory. This section mainly takes realtek RTL8211F as an example to describe the PHY driver. The code is as follows:

# 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 Registration of phy driver

        There are many different models of PHY devices of the same brand. In order to support drivers that can register multiple models of PHYs at a time, the kernel provides the macro module_phy_driver for registering PHY drivers in include\linux\phy.h. The macro is defined as follows:

# 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 is defined as follows (note that there are some changes with the old version of the kernel)

/**
 * 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) Analysis

The macro definition is expanded as follows:

#define MODULE_DEVICE_TABLE(mdio, realtek_tbl)					\
extern const struct mdio_device_id __mod_mdio__realtek_tbl_device_table		\
  __attribute__ ((unused, "realtek_tbl")))

4. Relationship diagram between device driver and controller driver

Guess you like

Origin blog.csdn.net/qq_41076734/article/details/129201815