Linux内核中实例化i2c设备的几种方法----./Linux-4.9.88/Documentation/i2c/instantiating-devices文件翻译

目录

如何实例化 I2C 设备

方法 1a:通过总线号声明 I2C 设备

方法 1b:通过设备树声明 I2C 设备

方法 1c:通过 ACPI 声明 I2C 设备

方法 2:显式实例化设备

方法 3:探测某些设备的 I2C 总线

方法 4:从用户空间实例化


以下内容来自 ./Linux-4.9.88/Documentation/i2c/instantiating-devices,只是翻译成了中文。

如何实例化 I2C 设备

与 PCI 或 USB 设备不同,I2C 设备不会在硬件级别进行枚举。相反,软件必须知道每个 I2C 总线段上连接了哪些设备以及这些设备使用的地址。因此,内核代码必须显式地实例化 I2C 设备。根据上下文和要求,有几种方法可以实现这一点。

方法 1a:通过总线号声明 I2C 设备

当 I2C 总线是系统总线时(许多嵌入式系统都是这种情况),此方法是适当的。在这样的系统上,每个 I2C 总线都有一个预先知道的编号。因此,可以预先声明此总线上的 I2C 设备。这是通过调用 i2c_register_board_info() 注册一个 i2c_board_info 结构体数组来完成的。

示例(来自 omap2 h4):

static struct i2c_board_info h4_i2c_board_info[] __initdata = {
	{
		I2C_BOARD_INFO("isp1301_omap", 0x2d),
		.irq		= OMAP_GPIO_IRQ(125),
	},
	{	/* EEPROM on mainboard */
		I2C_BOARD_INFO("24c01", 0x52),
		.platform_data	= &m24c01,
	},
	{	/* EEPROM on cpu card */
		I2C_BOARD_INFO("24c01", 0x57),
		.platform_data	= &m24c01,
	},
};

static void __init omap_h4_init(void)
{
	(...)
	i2c_register_board_info(1, h4_i2c_board_info,
			ARRAY_SIZE(h4_i2c_board_info));
	(...)
}

上述代码在 I2C 总线 1 上声明了 3 个设备,包括它们各自的地址和驱动程序所需的自定义数据。当注册所述 I2C 总线时,I2C 设备将由 i2c-core 自动实例化。

当它们所在的 I2C 总线消失时(如果曾经消失过),设备将自动解除绑定并销毁。

方法 1b:通过设备树声明 I2C 设备

此方法与方法 1a 具有相同的含义。I2C 设备的声明是通过设备树作为主控制器的子节点完成的。

示例:

i2c1: i2c@400a0000 {
	/* ... 跳过主属性 ... */
	clock-frequency = <100000>;

	flash@50 {
		compatible = "atmel,24c256";
		reg = <0x50>;
	};

	pca9532: gpio@60 {
		compatible = "nxp,pca9532";
		gpio-controller;
		#gpio-cells = <2>;
		reg = <0x60>;
	};
};

这里,两个设备使用速度为 100kHz 的总线连接。有关可能需要设置设备的其他属性,请参阅其设备树文档,位于 Documentation/devicetree/bindings/。

方法 1c:通过 ACPI 声明 I2C 设备

ACPI 也可以描述 I2C 设备。有关此内容的特殊文档目前位于 Documentation/acpi/enumeration.txt。

方法 2:显式实例化设备

当较大的设备使用 I2C 总线进行内部通信时,此方法是适当的。典型情况是电视适配器。它们可以具有调谐器、视频解码器、音频解码器等,通常通过 I2C 总线连接到主芯片。您事先不会知道 I2C 总线的编号,因此无法使用上述方法 1。相反,您可以显式地实例化您的 I2C 设备。这是通过填充 i2c_board_info 结构体并调用 i2c_new_device() 来完成的。

示例(来自 sfe4001 网络驱动程序):

static struct i2c_board_info sfe4001_hwmon_info = {
	I2C_BOARD_INFO("max6647", 0x4e),
};

int sfe4001_init(struct efx_nic *efx)
{
	(...)
	efx->board_info.hwmon_client =
		i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info);

	(...)
}

上述代码在所述网络适配器上的 I2C 总线上实例化了 1 个 I2C 设备。

当您不确定 I2C 设备是否存在(例如对于便宜型号的板子上不存在的可选功能,但您无法区分它们),或者它可能从一个板子到另一个板子具有不同的地址(制造商更改其设计而不通知)。在这种情况下,您可以调用 i2c_new_probed_device() 而不是 i2c_new_device()

示例(来自 nxp OHCI 驱动程序):

static const unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };

static int usb_hcd_nxp_probe(struct platform_device *pdev)
{
	(...)
	struct i2c_adapter *i2c_adap;
	struct i2c_board_info i2c_info;

	(...)
	i2c_adap = i2c_get_adapter(2);
	memset(&i2c_info, 0, sizeof(struct i2c_board_info));
	strlcpy(i2c_info.type, "isp1301_nxp", I2C_NAME_SIZE);
	isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info,
						   normal_i2c, NULL);
	i2c_put_adapter(i2c_adap);
	(...)
}

上述代码在所述 OHCI 适配器上的 I2C 总线上实例化多达 1 个 I2C 设备。它首先尝试地址 0x2c,如果那里没有找到任何东西,则尝试地址 0x2d,如果仍然没有找到任何东西,则简单地放弃。

实例化 I2C 设备的驱动程序负责在清理时销毁它。这是通过在之前由 i2c_new_device() 或 i2c_new_probed_device() 返回的指针上调用 i2c_unregister_device() 来完成的。

方法 3:探测某些设备的 I2C 总线

有时您对 I2C 设备的了解不够,甚至无法调用 i2c_new_probed_device()。典型情况是 PC 主板上的硬件监控芯片。有几十种型号,可以在 25 个不同的地址上使用。鉴于那里存在大量的主板,建立正在使用的硬件监控芯片的详尽列表几乎是不可能的。幸运的是,这些芯片中的大多数都具有制造商和设备 ID 寄存器,因此可以通过探测来识别。

在这种情况下,I2C 设备既不声明也不显式实例化。相反,一旦驱动程序加载,i2c-core 将探测这些设备,如果发现任何设备,则将自动实例化 I2C 设备。为了防止此机制的任何不当行为,适用以下限制:

  • I2C 设备驱动程序必须实现 detect() 方法,该方法通过从任意寄存器读取来识别支持的设备。
  • 只有可能具有支持设备并同意被探测的总线才会被探测。例如,这避免了在电视适配器上探测硬件监控芯片。

示例: 请参见 drivers/hwmon/lm90.c 中的 lm90_driver 和 lm90_detect()

由于成功探测而实例化的 I2C 设备将在检测到它们的驱动程序被删除时或底层 I2C 总线本身被销毁时(以先发生者为准)自动销毁。

熟悉 2.4 内核和早期 2.6 内核中 i2c 子系统的人会发现,这种方法 3 本质上类似于当时所做的事情。两个显著差异是:

  • 探测现在只是实例化 I2C 设备的一种方式,而当时它是唯一的方式。在可能的情况下,应优先选择方法 1 和 2。仅当没有其他方法时才应使用方法 3,因为它可能会产生不良副作用。
  • I2C 总线现在必须显式地指定哪些 I2C 驱动程序类别可以探测它们(通过类别位字段),而当时所有 I2C 总线默认都会被探测。默认为空类别,这意味着没有探测发生。类别位字段的目的是限制上述不良副作用。

再次强调,应尽可能避免使用方法 3。显式设备实例化(方法 1 和 2)更受欢迎,因为它更安全、更快。

方法 4:从用户空间实例化

通常,内核应该知道哪些 I2C 设备已连接以及它们所在的地址。然而,在某些情况下,它并不知道,因此添加了一个 sysfs 接口来让用户提供信息。该接口由两个属性文件组成,它们都创建在每个 I2C 总线目录中:new_device 和 delete_device。这两个文件都是只写的,您必须向它们写入正确的参数才能正确实例化或删除 I2C 设备。

文件 new_device 需要 2 个参数:I2C 设备的名称(字符串)和 I2C 设备的地址(数字,通常以 0x 开头的十六进制表示,但也可以用十进制表示)。

文件 delete_device 需要一个参数:I2C 设备的地址。由于在给定的 I2C 段上没有两个设备可以位于同一地址,因此地址足以唯一标识要删除的设备。

示例:

echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device

虽然此接口仅在内核设备声明无法完成时使用,但在多种情况下它可能会有所帮助:

  • I2C 驱动程序通常检测设备(上述方法 3),但您的设备所在的总线段没有设置正确的类别位,因此检测不会触发。
  • I2C 驱动程序通常检测设备,但您的设备位于意外地址。
  • I2C 驱动程序通常检测设备,但您的设备未被检测到,原因可能是检测例程过于严格,或者您的设备尚未正式支持,但您知道它是兼容的。
  • 您正在测试板上开发驱动程序,在那里您自己焊接了 I2C 设备。

此接口是一些 I2C 驱动程序实现的 force_* 模块参数的替代品。由于它是在 i2c-core 中而不是在每个设备驱动程序中单独实现,因此效率更高,并且具有更改设置时无需重新加载驱动程序的优点。您还可以在驱动程序加载或甚至可用之前实例化设备,并且不需要知道设备需要什么驱动程序。

猜你喜欢

转载自blog.csdn.net/u013171226/article/details/132016973
今日推荐