如何实例化I2C设备
与PCI或USB设备不同,I2C设备不在硬件级别枚举。相反,软件必须知道每个I2C总线段上连接了哪些设备,以及这些设备使用的地址。由于这个原因,内核代码必须显式地实例化I2C设备。根据上下文和需求,有几种方法可以实现这一点。
方法一:静态声明I2C设备
当I2C总线是系统总线时(如许多嵌入式系统的情况),这种方法是合适的。在这样的系统中,每个I2C总线都有一个预先知道的号码。因此,可以预先声明位于此总线上的I2C设备。
这些信息在不同的架构上以不同的方式提供给内核:设备树、ACPI或板级文件。
当I2C总线被注册时,I2C设备将由I2C -core自动实例化。当它们所在的I2C总线消失时(如果发生的话),这些设备将自动解除绑定并被销毁。
通过设备树声明I2C设备
在使用设备树的平台上,I2C设备的声明是在主控制器的子节点中完成的。
示例:
i2c1: i2c@400a0000 { /* ... master properties skipped ... */ clock-frequency = <100000>;
status = "ok"; 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 Based Device Enumeration。
I2C总线控制器后面的从设备只需要添加ACPI id。一旦注册了适配器,I2C核心就会自动枚举控制器设备后面的任何从设备。
下面是如何将ACPI支持添加到现有的mpu3050输入驱动程序的示例:
#ifdef CONFIG_ACPI static const struct acpi_device_id mpu3050_acpi_match[] = { { "MPU3050", 0 }, { }, }; MODULE_DEVICE_TABLE(acpi, mpu3050_acpi_match); #endif static struct i2c_driver mpu3050_i2c_driver = { .driver = { .name = "mpu3050", .owner = THIS_MODULE, .pm = &mpu3050_pm, .of_match_table = mpu3050_of_match, .acpi_match_table = ACPI_PTR(mpu3050_acpi_match), }, .probe = mpu3050_probe, .remove = mpu3050_remove, .id_table = mpu3050_ids, };
在单板文件中声明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设备。这是通过填充结构体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接口来让用户提供信息。这个接口由在每个I2C总线目录中创建的2个属性文件组成:new_device 和 delete_device。这两个文件都是只写的,以便正确地实例化(分别删除)I2C设备。
文件new_device接受两个参数: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中实现而不是在每个设备驱动程序中单独实现,它更高效,而且还具有不必重新加载驱动程序来更改设置的优点。你也可以在驱动加载或可用之前实例化设备,你不需要知道设备需要什么驱动。
使用这种方法实例化的I2C设备,在设备驱动程序中可以通过别名匹配或者OF匹配表匹配。例如:
/* 如果使用OF匹配表匹配,i2c_driver.id_table必须存在,即使为空 */
static const struct i2c_device_id i2c_id_table[] = { {"eeprom", 0}, /* 若使用OF匹配表匹配,可注释掉此行 */
{} }; MODULE_DEVICE_TABLE(i2c, i2c_id_table);
static const struct of_device_id eeprom_dt_ids[] = { {.compatible = "xxx(制造商),eeprom",}, {} }; MODULE_DEVICE_TABLE(of, eeprom_dt_ids); static struct i2c_driver eeprom_drv = {
.driver = {
.name = "***", .owner = THIS_MODULE,
.of_match_table = of_match_ptr(eeprom_dt_ids), /* 若使用别名匹配,可注释掉此行 */ }, .probe = eeprom_probe, .remove = eeprom_remove, .id_table = i2c_id_table, };
本文来自博客园,作者:闹闹爸爸,转载请注明原文链接:https://www.cnblogs.com/wanglouxiaozi/p/15148548.html