如何实例化 I2C 设备¶
与 PCI 或 USB 设备不同,I2C 设备不会在硬件级别进行枚举。 相反,软件必须知道哪些设备连接到每个 I2C 总线段,以及这些设备正在使用的地址。 因此,内核代码必须显式地实例化 I2C 设备。 有几种方法可以实现这一点,具体取决于上下文和要求。
方法 1:静态声明 I2C 设备¶
当 I2C 总线是系统总线时,此方法适用,许多嵌入式系统就是这种情况。 在这种系统上,每个 I2C 总线都有一个预先知道的编号。 因此,可以预先声明位于此总线上的 I2C 设备。
在不同的体系结构上,此信息以不同的方式提供给内核:设备树、ACPI 或板文件。
注册有问题的 I2C 总线时,i2c-core 会自动实例化 I2C 设备。 当 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 设备是通过 struct i2c_board_info
数组完成的,该数组通过调用 i2c_register_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 驱动程序类可以探测它们(通过类位字段),而当时默认探测所有 I2C 总线。 默认值是一个空类,这意味着不发生探测。 类位字段的目的是限制上述不良的副作用。
再次,应尽可能避免方法 3。 显式设备实例化(方法 1 和 2)是更安全、更快速的首选方法。
方法 4:从用户空间实例化¶
通常,内核应该知道哪些 I2C 设备已连接以及它们所在的地址。 但是,在某些情况下,它不知道,因此添加了一个 sysfs 接口,以使用户提供信息。 此接口由 2 个属性文件组成,这些文件在每个 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 中而不是在每个设备驱动程序中单独实现,因此效率更高,并且还具有不必重新加载驱动程序即可更改设置的优点。 您还可以在加载驱动程序之前甚至没有驱动程序的情况下实例化该设备,并且您无需知道该设备需要什么驱动程序。