#linux-device-driver #i2c #acpi #iio
#linux-ядро #x86 #linux-драйвер устройства #встроенный-linux #acpi
Вопрос:
На моей плате x86_64 есть шина i2c, выходящая из устройства MFD. На этой шине 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);
Есть предложения о том, как я могу это решить?
Комментарии:
1. вызывается ли tst_i2c_board_setup() после регистрации шины I2C 0
2. @Ash Я провел тестирование для обоих сценариев, т. е. вызывал до и после регистрации шины 0; изменив device_initcall на arch_initcall. Это не помогает
3. Для этого вам нужно использовать ACPI. Я бы ответил на это примерами после моего отпуска, а пока просто поискал в Google проект meta-acpi на Github, чтобы получить оригинальную идею.
Ответ №1:
Поскольку у вас платформа с поддержкой ACPI, лучшим подходом является предоставление фрагментов ASL для заданных устройств.
Благодаря платформе Intel Galileo для интернета вещей EEPROM серии Atmel 24 получила свой собственный идентификатор ACPI, и отрывок будет простым:
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},
}
})
}
}
}
Обратите внимание, свойство size добавляется в серии ожидающих исправлений (исправления добавляют свойство eeprom «size» и добавляют поддержку для извлечения свойства устройства eeprom «size»).
Обратите внимание, что ширина адреса на данный момент жестко запрограммирована и составляет 8 бит. В случае, если вам нужно иметь 16-разрядную версию, вам нужно создать аналогичные исправления, упомянутые выше.
Для LTC2990 power monitor вам понадобится следующий отрывок:
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"},
}
})
}
}
}
Обратите внимание, к сожалению, в драйвере нет совместимой строки, поэтому ее нужно добавить, как это сделано здесь.
В приведенных выше примерах \_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.
Другие примеры и описание лежащей в их основе идеи можно найти на странице meta-acpi GitHub, некоторые материалы из которой скопированы здесь.
Комментарии:
1. Что, если я не хочу использовать initrd. Я загружаю ядро непосредственно из GRUB. Как я могу применить эти файлы в таком сценарии? Не могли бы вы, пожалуйста, помочь.
2. @InsaneCoder, другой способ — загрузить их через configfs. Смотрите эту ссылку: github.com/torvalds/linux/blob/master/Documentation/acpi / … (вот патч, который принес это patchwork.kernel.org/patch/9221305 )
Ответ №2:
Просмотрев документацию /i2c/instantiating-devices, я понял, что есть несколько способов сделать то же самое (например, как 0andriy предложил использовать таблицу acpi и т.д.), Я использовал метод «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, amp;tst_i2c0_board_info[i],
normal_i2c[i], NULL);
i2c_put_adapter(i2c_adap);
return 0;
}
late_initcall(tst_i2c_board_setup);
Комментарии:
1. Пока это работает, это злоупотребление платформами с поддержкой ACPI.
2. Я согласен, это исправлено в спешке. Я буду использовать метод, основанный на ACPI.