Как выясняется, далеко немногие знают о существовании режима оверлеев в ACPICA и их поддержки в ОС Linux. Я хочу восполнить этот пробел на примере добавления ведомых устройств I2C в систему без перекомпиляции.
Начальные условия
Допустим, при запуске
i2cdetect -y -r 0
у нас выдаётся такая картина:
0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- 53 -- -- -- 57 -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- --
где по адресу 0x53 обнаруживается акселерометер ADXL345, а по адресу 0x57 — микросхема EEPROM памяти 24c128. Описания этих устройств отсутствуют в ACPI, а именно в таблице DSDT.
Добавляем акселерометер ADXL345
Всё, что нам необходимо знать здесь — это адрес, по которому отзывается устройство, его ID, поддерживаемые драйвером, частота шины, на которой он должен работать. Обратите внимание, что частота I2C шины со стороны драйвера часто устанавливается в ту минимальную, которая поддерживается всеми ведомыми устройствами на данной шине!
Ах, да, было время, когда подсистемы IIO не существовало, а драйвер ADXL345 уже был. Так вот, мы используем новый, который доступен через подсистему IIO.
Итого,
- Адрес: 0x53
- Частота шины: 400кГц
- Ссылка на ведущее (контроллер) устройство: _SB.PCI0.I2C1
- Идентификатор: adi,adxl345
Следует обратить внимание, что мы используем здесь специальный идентификатор, который предназначен для систем с OF. В качестве прослойки в ACPI был добавлен специальный идентификатор PRP0001, который и обеспечивает совместимость с драйверами, написанными ранее для OF.
Переводим полученную информацию на язык ASL:
DefinitionBlock ("adxl345.aml", "SSDT", 5, "", "ADXL345", 1)
{
External (_SB_.PCI0.I2C1, DeviceObj)
Scope (_SB.PCI0.I2C1)
{
Device (ACL0) {
Name (_HID, "PRP0001")
Name (_DDN, "Analog Devices ADXL345 3-axis accelerometer")
Name (_CRS, ResourceTemplate () {
I2cSerialBusV2 (
0x0053, // I2C Slave Address
ControllerInitiated,
400000, // Bus speed
AddressingMode7Bit,
"\_SB.PCI0.I2C1", // Link to ACPI I2C host controller
0
)
})
Name (_DSD, Package () {
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package () {
Package () { "compatible", "adi,adxl345" },
}
})
}
}
}
Добавляем EEPROM 24c128
Так же, как и в предыдущем случае получаем необходимую информацию про устройство и его драйвер:
- Адрес: 0x57
- Частота шины: 400кГц
- Ссылка на ведущее (контроллер) устройство: _SB.PCI0.I2C1
- Идентификатор: INT3499
- Объём: 1024
- Размер страницы: 32
DefinitionBlock ("at24.aml", "SSDT", 5, "", "AT24", 1)
{
External (_SB_.PCI0.I2C1, DeviceObj)
Scope (_SB.PCI0.I2C1)
{
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.I2C1", // Link to ACPI I2C host controller
0
)
})
Name (_DSD, Package () {
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package () {
Package () { "size", 1024 },
Package () { "pagesize", 32 },
}
})
}
}
}
Заметьте разницу с предыдущим вариантом. Здесь используется напрямую ACPI ID, который выделен в пространстве, контроллируемом Intel, спасибо платформе Intel Galileo. Второе отличие, мы передаём дополнительные параметры устройства в виде строк ключ-значение.
Возможные варианты инициализации
Что теперь с этим всем делать? Алгоритм прост. Во-первых, необходимо откомпилировать полученные файлы в ASL байт-код. Достигается с помощью вызова команды
iasl adxl345.asl
и по аналогии для EEPROM. Во-вторых, выбрать способ инициализации новоиспечённой таблицы. Их собственно три: 1) присоединение к initramfs, 2) загрузка на рабочей системе через ConfigFS, 3) загрузка таблицы из переменной EFI. Рассмотрим первые два из них ниже.
Присоединение к initramfs
Ничего с самим архивом initramfs мы делать не будем, однако рекомендуется сохранить оригинал где-нибудь в сторонке.
# Добавляем ACPI таблицы в некомпрессированный cpio архив.
# Они обязаны находится в подкаталоге /kernel/firmware/acpi внутри архива.
# Некомпрессированные архивы должны идти первыми в цепочке.
mkdir -p kernel/firmware/acpi
cp adxl345.aml kernel/firmware/acpi
cp at24.aml kernel/firmware/acpi
# Создаём архив и цепочкой к нему присоединяем оригинальный initramfs:
find kernel | cpio -H newc --create > /boot/instrumented_initramfs-vX.Y
cat /boot/initramfs-vX.Y >> /boot/instrumented_initramfs-vX.Y
После этой процедуры старый архив можно заменить новым и перезагрузить компьютер.
Должно появиться в выводе dmesg что-то типа:
[ 0.000000] ACPI: Table Upgrade: install [SSDT- - ADXL345]
[ 0.000000] ACPI: SSDT 0x000000003F4FF5C4 0000A6 (v05 ADXL345 00000001 INTL 20170303)
Учтите, что ядро поддерживает только цепочку до 64 таких архивов.
Загрузка через ConfigFS
Такая возможность доступна, когда ядро собрано с опцией CONFIG_ACPI_CONFIGFS и ConfigFS примонтирована. Если предположить, что она примонтирована в подкаталог /sys/kernel/config, то нижеследующий пример показывает как загрузить таблицу.
cd /sys/kernel/config/acpi/table
mkdir adxl345
cat ~/adxl354.aml > adxl345/aml
Заключение
Хотя язык ASL требует больше скобочек, чем аналоги, тем не менее он предоставляет не меньшие возможности для описания устройств. Так, существует некоторое количество примеров в проекте meta-acpi, где в частности можно найти описания светодиодов и кнопок, подключенных к GPIO линиям, микросхем памяти, и даже описание модуля Adafruit 2.8" — TFT дисплея с сенсорным экраном!
Автор: Andy Shevchenko