PHY デバイス ドライバー

1。概要

        MAC コントローラーのドライバーはプラットフォーム バスの接続方法を使用し、PHY デバイス ドライバーはデバイス、ドライバー、およびバスの接続方法に基づいています。

        そのドライバーには、次の重要な部分が含まれます。

总線 - sturct mii_bus (mii はメディアに依存しないインターフェイスを表します)

デバイス - 構造体 phy_device

ドライバー - 構造体 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/クラス/mdio_bus

        (2) bus_register(&mdio_bus_type) を実行すると、次のバス タイプになります。

/sys/バス/mdio_bus

2.3 バスにおける一致機能の解析

/**
 * 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. デバイスドライバとコントローラドライバの関係図

おすすめ

転載: blog.csdn.net/qq_41076734/article/details/129201815
おすすめ