在 x86_64 上添加 i2c 客户端设备
adding i2c client devices on x86_64
在我的 x86_64 开发板上,有一个来自 MFD 设备的 i2c 总线。此 i2c 总线上有设备。我能够使用 i2cdetect 程序检测到这些设备。
# i2cdetect -y 0
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- 4c -- -- --
50: -- -- -- -- -- -- -- 57 -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
我需要内核自动检测这些设备,所以,我尝试按照下面给出的代码编写 i2c_board_info,但内核仍然无法自动检测这些设备。
#include <linux/init.h>
#include <linux/i2c.h>
#define BUS_NUMBER 0
static struct __init i2c_board_info tst_i2c0_board_info[] = {
{
I2C_BOARD_INFO("ltc2990", 0x4c),
},
{
I2C_BOARD_INFO("24c128", 0x57),
},
};
static int tst_i2c_board_setup(void)
{
int ret=-1;
ret = i2c_register_board_info(BUS_NUMBER, tst_i2c0_board_info, ARRAY_SIZE(tst_i2c0_board_info));
return ret;
}
device_initcall(tst_i2c_board_setup);
关于如何解决这个问题有什么建议吗?
通过 Documentation/i2c/instantiating-devices 后,我知道有几种方法可以做到这一点(例如 0andriy 建议使用 acpi table 等),我使用 "i2c_new_probed_device" 方法。下面是使用的代码:
#include <linux/init.h>
#include <linux/i2c.h>
#define BUS_NUMBER 0
#define NUM_DEVICE 2
static const unsigned short normal_i2c[][2] = {
{0x4c, I2C_CLIENT_END},
{0x57, I2C_CLIENT_END},
};
static struct i2c_board_info tst_i2c0_board_info[2] = {
{I2C_BOARD_INFO("ltc2990", 0x4c), },
{I2C_BOARD_INFO("24c128", 0x57), },
};
static int tst_i2c_board_setup(void)
{
int i = 0;
struct i2c_adapter *i2c_adap;
i2c_adap = i2c_get_adapter(BUS_NUMBER);
for(i = 0; i < NUM_DEVICE; i++)
i2c_new_probed_device(i2c_adap, &tst_i2c0_board_info[i],
normal_i2c[i], NULL);
i2c_put_adapter(i2c_adap);
return 0;
}
late_initcall(tst_i2c_board_setup);
由于您拥有支持 ACPI 的平台,因此最好的方法是为给定设备提供 ASL 摘录。
由于 Intel Galileo 物联网平台,Atmel 24 系列 EEPROM 有自己的 ACPI ID,摘录很简单:
DefinitionBlock ("at24.aml", "SSDT", 5, "", "AT24", 1)
{
External (_SB_.PCI0.I2C2, DeviceObj)
Scope (\_SB.PCI0.I2C2)
{
Device (EEP0) {
Name (_HID, "INT3499")
Name (_DDN, "Atmel AT24 compatible EEPROM")
Name (_CRS, ResourceTemplate () {
I2cSerialBusV2 (
0x0057, // I2C Slave Address
ControllerInitiated,
400000, // Bus speed
AddressingMode7Bit,
"\_SB.PCI0.I2C2", // Link to ACPI I2C host controller
0
)
})
Name (_DSD, Package () {
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package () {
Package () {"size", 1024},
Package () {"pagesize", 32},
}
})
}
}
}
请注意,大小 属性 正在添加到 pending patch series (patches add eeprom "size" property and add support to fetch eeprom device property "size")。
注意,地址宽度目前硬编码为 8 位。如果您需要 16 位,则需要创建与上述类似的补丁。
对于 LTC2990 电源监视器,您需要以下摘录:
DefinitionBlock ("ltc2990.aml", "SSDT", 5, "", "PMON", 1)
{
External (\_SB_.PCI0.I2C2, DeviceObj)
Scope (\_SB.PCI0.I2C2)
{
Device (PMON)
{
Name (_HID, "PRP0001")
Name (_DDN, "Linear Technology LTC2990 power monitor")
Name (_CRS, ResourceTemplate () {
I2cSerialBus (
0x4c, // Bus address
ControllerInitiated, // Don't care
400000, // Fast mode (400 kHz)
AddressingMode7Bit, // 7-bit addressing
"\_SB.PCI0.I2C2", // I2C host controller
0 // Must be 0
)
})
Name (_DSD, Package () {
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package () {
Package () {"compatible", "lltc,ltc2990"},
}
})
}
}
}
请注意,很遗憾,驱动程序中没有兼容的字符串,因此,需要像 it's done here 添加它。
在上面的示例中,\_SB.PCI0.I2C2
是 I2C 主机控制器的绝对路径。
如何应用这些文件:
- 首先,创建一个文件夹
mkdir -p kernel/firmware/acpi
- 在该文件夹中以
DefinitionBlock()
宏中提到的名称保存文件
- 创建未压缩的 cpio 存档并将原始 initrd 连接在顶部:
find kernel | cpio -H newc --create > /boot/instrumented_initrd
cat /boot/initrd >> /boot/instrumented_initrd
SSDT Overlays 中提供了更多详细信息。
背后的其他例子和想法的描述可以在meta-acpi GitHub page上找到,这里复制了一些材料。
在我的 x86_64 开发板上,有一个来自 MFD 设备的 i2c 总线。此 i2c 总线上有设备。我能够使用 i2cdetect 程序检测到这些设备。
# i2cdetect -y 0
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- 4c -- -- --
50: -- -- -- -- -- -- -- 57 -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
我需要内核自动检测这些设备,所以,我尝试按照下面给出的代码编写 i2c_board_info,但内核仍然无法自动检测这些设备。
#include <linux/init.h>
#include <linux/i2c.h>
#define BUS_NUMBER 0
static struct __init i2c_board_info tst_i2c0_board_info[] = {
{
I2C_BOARD_INFO("ltc2990", 0x4c),
},
{
I2C_BOARD_INFO("24c128", 0x57),
},
};
static int tst_i2c_board_setup(void)
{
int ret=-1;
ret = i2c_register_board_info(BUS_NUMBER, tst_i2c0_board_info, ARRAY_SIZE(tst_i2c0_board_info));
return ret;
}
device_initcall(tst_i2c_board_setup);
关于如何解决这个问题有什么建议吗?
通过 Documentation/i2c/instantiating-devices 后,我知道有几种方法可以做到这一点(例如 0andriy 建议使用 acpi table 等),我使用 "i2c_new_probed_device" 方法。下面是使用的代码:
#include <linux/init.h>
#include <linux/i2c.h>
#define BUS_NUMBER 0
#define NUM_DEVICE 2
static const unsigned short normal_i2c[][2] = {
{0x4c, I2C_CLIENT_END},
{0x57, I2C_CLIENT_END},
};
static struct i2c_board_info tst_i2c0_board_info[2] = {
{I2C_BOARD_INFO("ltc2990", 0x4c), },
{I2C_BOARD_INFO("24c128", 0x57), },
};
static int tst_i2c_board_setup(void)
{
int i = 0;
struct i2c_adapter *i2c_adap;
i2c_adap = i2c_get_adapter(BUS_NUMBER);
for(i = 0; i < NUM_DEVICE; i++)
i2c_new_probed_device(i2c_adap, &tst_i2c0_board_info[i],
normal_i2c[i], NULL);
i2c_put_adapter(i2c_adap);
return 0;
}
late_initcall(tst_i2c_board_setup);
由于您拥有支持 ACPI 的平台,因此最好的方法是为给定设备提供 ASL 摘录。
由于 Intel Galileo 物联网平台,Atmel 24 系列 EEPROM 有自己的 ACPI ID,摘录很简单:
DefinitionBlock ("at24.aml", "SSDT", 5, "", "AT24", 1)
{
External (_SB_.PCI0.I2C2, DeviceObj)
Scope (\_SB.PCI0.I2C2)
{
Device (EEP0) {
Name (_HID, "INT3499")
Name (_DDN, "Atmel AT24 compatible EEPROM")
Name (_CRS, ResourceTemplate () {
I2cSerialBusV2 (
0x0057, // I2C Slave Address
ControllerInitiated,
400000, // Bus speed
AddressingMode7Bit,
"\_SB.PCI0.I2C2", // Link to ACPI I2C host controller
0
)
})
Name (_DSD, Package () {
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package () {
Package () {"size", 1024},
Package () {"pagesize", 32},
}
})
}
}
}
请注意,大小 属性 正在添加到 pending patch series (patches add eeprom "size" property and add support to fetch eeprom device property "size")。
注意,地址宽度目前硬编码为 8 位。如果您需要 16 位,则需要创建与上述类似的补丁。
对于 LTC2990 电源监视器,您需要以下摘录:
DefinitionBlock ("ltc2990.aml", "SSDT", 5, "", "PMON", 1)
{
External (\_SB_.PCI0.I2C2, DeviceObj)
Scope (\_SB.PCI0.I2C2)
{
Device (PMON)
{
Name (_HID, "PRP0001")
Name (_DDN, "Linear Technology LTC2990 power monitor")
Name (_CRS, ResourceTemplate () {
I2cSerialBus (
0x4c, // Bus address
ControllerInitiated, // Don't care
400000, // Fast mode (400 kHz)
AddressingMode7Bit, // 7-bit addressing
"\_SB.PCI0.I2C2", // I2C host controller
0 // Must be 0
)
})
Name (_DSD, Package () {
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package () {
Package () {"compatible", "lltc,ltc2990"},
}
})
}
}
}
请注意,很遗憾,驱动程序中没有兼容的字符串,因此,需要像 it's done here 添加它。
在上面的示例中,\_SB.PCI0.I2C2
是 I2C 主机控制器的绝对路径。
如何应用这些文件:
- 首先,创建一个文件夹
mkdir -p kernel/firmware/acpi
- 在该文件夹中以
DefinitionBlock()
宏中提到的名称保存文件 - 创建未压缩的 cpio 存档并将原始 initrd 连接在顶部:
find kernel | cpio -H newc --create > /boot/instrumented_initrd
cat /boot/initrd >> /boot/instrumented_initrd
SSDT Overlays 中提供了更多详细信息。
背后的其他例子和想法的描述可以在meta-acpi GitHub page上找到,这里复制了一些材料。