如何实例化 I2C 设备¶
与 PCI 或 USB 设备不同,I2C 设备在硬件级别不会被枚举。相反,软件必须知道每个 I2C 总线段上连接了哪些设备,以及这些设备正在使用什么地址。因此,内核代码必须显式地实例化 I2C 设备。根据上下文和要求,有几种方法可以实现这一点。
方法 1:静态声明 I2C 设备¶
当 I2C 总线是系统总线时,此方法是合适的,许多嵌入式系统就是这种情况。在这样的系统上,每个 I2C 总线都有一个预先知道的编号。因此,可以预先声明位于该总线上的 I2C 设备。
此信息以不同的方式提供给不同架构上的内核:设备树、ACPI 或板级文件。
当相关的 I2C 总线被注册时,I2C 设备将由 i2c-core 自动实例化。当它们所在的 I2C 总线消失时(如果有的话),这些设备将被自动解除绑定并销毁。
通过设备树声明 I2C 设备¶
在使用设备树的平台上,I2C 设备的声明在主控制器的子节点中完成。
示例
i2c1: i2c@400a0000 {
/* ... master properties skipped ... */
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/ 中的设备树文档。
通过 ACPI 声明 I2C 设备¶
ACPI 也可以描述 I2C 设备。目前在基于 ACPI 的设备枚举有关于此的特殊文档。
在板级文件中声明 I2C 设备¶
在许多嵌入式架构中,设备树已经取代了基于板级文件的旧硬件描述,但后者仍然在旧代码中使用。通过板级文件实例化 I2C 设备是通过调用i2c_register_board_info()
注册一个struct 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 个设备,包括它们各自的地址和驱动程序所需的自定义数据。
方法 2:显式实例化设备¶
当较大的设备使用 I2C 总线进行内部通信时,此方法是合适的。典型的例子是电视适配器。这些适配器通常通过 I2C 总线将调谐器、视频解码器、音频解码器等连接到主芯片。您不会预先知道 I2C 总线的编号,因此无法使用上面描述的方法 1。相反,您可以显式实例化 I2C 设备。这是通过填写struct i2c_board_info
并调用 i2c_new_client_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_client_device(&efx->i2c_adap, &sfe4001_hwmon_info);
(...)
}
上面的代码在相关的网络适配器上的 I2C 总线上实例化了 1 个 I2C 设备。
当您不确定 I2C 设备是否存在时(例如,对于主板的廉价版本上不存在的可选功能,但您无法区分它们),或者它在不同的主板上可能具有不同的地址(制造商在不通知的情况下更改其设计)时,会出现这种情况的变体。在这种情况下,您可以调用 i2c_new_scanned_device() 而不是 i2c_new_client_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));
strscpy(i2c_info.type, "isp1301_nxp", sizeof(i2c_info.type));
isp1301_i2c_client = i2c_new_scanned_device(i2c_adap, &i2c_info,
normal_i2c, NULL);
i2c_put_adapter(i2c_adap);
(...)
}
上面的代码在相关的 OHCI 适配器上的 I2C 总线上实例化了最多 1 个 I2C 设备。它首先尝试地址 0x2c,如果在那里找不到任何东西,它会尝试地址 0x2d,如果仍然找不到任何东西,它就会放弃。
实例化 I2C 设备的驱动程序负责在清理时销毁它。这是通过对 i2c_new_client_device()
或 i2c_new_scanned_device() 先前返回的指针调用 i2c_unregister_device()
来完成的。
方法 3:探测 I2C 总线上是否存在某些设备¶
有时您没有关于 I2C 设备的足够信息,甚至无法调用 i2c_new_scanned_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 驱动程序类可以探测它们(通过 class 位字段),而当时默认情况下会探测所有 I2C 总线。默认值是一个空类,这意味着不会发生探测。class 位字段的目的是限制上述不良副作用。
再次说明,应尽可能避免使用方法 3。显式设备实例化(方法 1 和 2)是更好的选择,因为它更安全、更快速。
方法 4:从用户空间实例化¶
一般来说,内核应该知道连接了哪些 I2C 设备以及它们位于哪些地址。但是,在某些情况下,它不知道,因此添加了一个 sysfs 接口来让用户提供信息。此接口由在每个 I2C 总线目录中创建的 2 个属性文件组成: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 中实现,而不是在每个设备驱动程序中单独实现,因此效率更高,而且它还具有无需重新加载驱动程序即可更改设置的优点。您还可以在加载驱动程序甚至可用之前实例化设备,而且您无需知道设备需要什么驱动程序。