Device Tree.
Возможности пользователя: загрузка системы с деревом устройств.
- Базовый синтаксис дерева устройств и его компиляция.
- Простой пример фрагмента дерева устройств.
- Полная организация дерева устройств.
- Пример использования дерева устройств.
- Общие соображения о дереве устройств в Linux.
Возможности пользователя: до дерева устройств.
- Ядро включает полное описание аппаратной платформы.
- Загрузчик загружает одиночный бинарный файл, образ ядра, и выполняет это файл – uImage или zImage.
- Загрузчик подготавливает некоторую дополнительную информацию, вызывает ATAGS, адрес ядра передается в регистр r2. Дополнительную информация – мета информация такая как, размер памяти и расположение, командная строка ядра, и др.
- Загрузчик говорит ядру для этой платы, это загрузиться через целей машинный тип данных (integer), передав в регистр r1.
- U-Boot команда: bootm < адрес образа ядра >
- Barebox переменная: bootm.image
Возможности пользователя: загрузка с деревом устройств.
- Ядро не длиннее содержания описания аппаратной части, дерево устройств расположено в отдельном бинарном файле: device tree blob.
- Загрузчик загружает два бинарных файла: образ ядра и DTB. Название образа ядра остается uImage или zImage. DTB расположено в arch/arm/boot/dts, один файл для платы “название платы”.dtb.
- Загрузчик передает DTB адрес через r2. Это предложено чтобы приспособить DTB с информацией о памяти, командной строкой ядра и потенциально другой информацией.
- Нет больше машинного типа.
- bootm < адрес образа ядра > – < dtb адрес >.
- Barebox переменная: bootm.image, bootm.oftree.
Возможности пользователя: режим совместимости для DT загрузки.
- Некоторые загрузчики имеют не специфичную поддержку для дерева устройств, или версия использования для контрактного устройства уже устарела, но должна поддерживаться.
- Упрощен переход, был добавлен совместимы механизм: CONFIG_ARM_APPENDED_DTB. Сообщаем ядру посмотреть на DTB после образа ядра. Нет встроенного правила для Makefile чтобы собрать такое ядра, так что, процедуру нужно делать вручную. cat arch/arm/boot/zImage arch/arm/boot/dts/myboard.dtb > my-zImage
mkimage … -d my-zImage my-uImage - Кроме того, дополнительная опция CONFIG_ARM_ATAG_DTB_COMPAT сообщает ядру прочитать ATAGS информацию из загрузчика и обновить DT, используя данную информацию.
Что такое дерево устройств?
- Цитируемым из Power.org стандарт требований для встраивания мощных архитектурных платформ (Standard for Embedded Power Architecture Platform Requirements – ePAPR)
- ePAPR спецификация – это концепция вызова дерева устройств в описании системного железа (платформы, аппаратной части). Загрузчик загружает дерево устройств в память клиентской программы и отдает указатель на дерево устройств клиенту.
- ePAPR совместимое дерева устройств описывает информацию об устройстве в системе, что не может быть динамически определено с помощью клиентской программы.
Синтаксис базового дерева устройств.
Путь от исходного файла к бинарному.
- На всех ARM, все исходные файлы дерева устройств (Device Tree Source – DTS) имеют следующее расположение arch/arm/boot/dts. .dts
файлы уровня описания платформы. .dtsi файлы включают файлы, как правило содержащие уровень SoC описания. - Инструмент, компилятор дерева устройств (Device Tree Compiler) компилирует из исходных файлов, бинарный файл. Исходных код расположен в scripts/dtc.
- DTB создает компилятор, и бинарный файл загружается через загрузчик, в свою очередь его анализирует ядро во время загрузки.
- arch/arm/boot/dts/Makefile содержит список DTB которые должны быть собраны.
dtb-$(CONFIG_ARCH_MVEBU) += armada-370-db.dtb \ armada-370-mirabox.dtb \ ...
Простой пример дерева устройств.
auart0: serial@8006a000 { Описание "программируемая модель" для устройства. Позволяет операционной системе идентифицировать соответствующий драйвер устройства. compatible = "fsl,imx28-aurt", "fsl,imx23-aurt"; Адрес и длина области регистров reg = <0x8006a000 0x2000>; Номер прерывания. interrupts = <112>; Механизм DMA и каналы, с именами. dmas = <&dma_apbx 8>, <&dma_apbx 9>; dma-names = "rx", "tx"; Описание тактирования. clocks = <&clks 45>; Устройство не используется. status = "disabled"; }; Пример взят из arch/arm/boot/dts/imx28.dtsi
Сопоставимая строка используемая для связывания устройства с драйвером.
static struct of_device_id mxs_auart_dt_ids[] = { { .compatible = "fsl,imx28-auart", .data = &mxs_auart_devtype[IMX28_AUART] }, { .compatible = "fsl,imx23-auart", .data = &mxs_auart_devtype[IMX23_AUART] }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mxs_auart_dt_ids); [...] static struct platform_driver mxs_auart_driver = { .probe = mxs_auart_probe, .remove = mxs_auart_remove, .driver = { .name = "mxs-auart", .of_match_table = mxs_auart_dt_ids, }, };
Пример взят из drivers/tty/serial/mxs-auart.c
- of_match_device позволяет получить сопоставимый вход для таблицы mxs_auart_dt_ids.
- Полезно получать драйвер-спецификацию поля data, типичное использование изменение поведение драйвера зависящего от различных вариантов подключенных устройств.
static int mxs_auart_probe(struct platform_device *pdev) { const struct of_device_id *of_id = of_match_device(mxs_auart_dt_ids, &pdev->dev); if (of_id) { /* Use of_id->data here */ [...] } [...] }
- Получение описания тактирования. Описание свойств тактирования. s->clk = clk_get(&pdev->dev, NULL);
- Получение ресурсов Вх./Вых. регистров. Описание свойств регистров. r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- Получение прерывания. Описание свойств прерывания. s->irq = platform_get_irq(pdev, 0);
- Получение DMA канала. Описание свойств DMA. s->rx_dma_chan = dma_request_slave_channel(s->dev, “rx”); s->tx_dma_chan = dma_request_slave_channel(s->dev, “tx”);
- Проверка некоторых пользовательских свойств. struct device_node *np = pdev->dev.of_node; if (of_get_property(np, “fsl,uart-has-rtscts”, NULL))
Присоединение дерева устройств.
- Файлы дерева устройств не монолитны, они могут быть разделены на несколько частей в нескольких файлах, включая каждый из них.
- .dtsi файлы включают файлы платформы, пока .dts файлы не определят конечный вид дерева устройств.
- Типичная связь, .dtsi файлы будут включать информацию для SoC уровня(или иногда общее определение к некоторым почти идентичным платам).
- .dts файл включает информацию уровня отладочной платы (платы разработки).
- Присоединение работает следующим образом, происходит наложение файлов описывающих платформу (включенных файлов в проект) и формирование единого описания для платформы.
- Присоединение использует DT(Device Tree) оператор /include/, или с некоторых выпусков ядра, DTS через процессорную обработку #include (рекомендуется).
Пример присоединение дерева устройств.
Концепция присоединение дерева устройств.
- Цитируем ePAPR: данный раздел содержит требования, известные как присоединение (привязки) для специфики типов и классов устройств, представленных в дереве устройств.
- Совместимое свойство узла устройства описывает специфическое связывание, к которому соответствует узел.
- При создании нового представления дерева устройств для аппаратной платформы, должна быть создана привязка, которая полностью описывает требуемые свойства и значения устройства. Этот набор свойств должен быть достаточно описательным, чтобы обеспечить драйвер устройства с необходимыми атрибутами устройств.
Документация по связям дерева устройств.
- Все дерево устройств имеет связи в ядре и имеет описание в документации Documentation/devicetree/bindings.
- Каждая связь в документации описывает какие свойства могут быть разрешены, с какими значениями, какие свойства обязательные, а какие свойства используются опционально.
- Все новые связи дерева устройств должны быть описаны в поддержке дерева устройств, и после отправлено на devicetree@vger.kernel.org. Должна гарантироваться корректность и последовательность через связи.
OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS M: Rob Herring <rob.herring@calxeda.com> M: Pawel Moll <pawel.moll@arm.com> M: Mark Rutland <mark.rutland@arm.com> M: Stephen Warren <swarren@wwwdotorg.org> M: Ian Campbell <ijc+devicetree@hellion.org.uk> L: devicetree@vger.kernel.org
Пример по связям дерева устройств.
* Freescale MXS Application UART (AUART) Required properties: - compatible : Should be "fsl,<soc>-auart". The supported SoCs include imx23 and imx28. - reg : Address and length of the register set for the device - interrupts : Should contain the auart interrupt numbers - dmas: DMA specifier, consisting of a phandle to DMA controller node and AUART DMA channel ID. Refer to dma.txt and fsl-mxs-dma.txt for details. - dma-names: "rx" for RX channel, "tx" for TX channel. Example: auart0: serial@8006a000 { compatible = "fsl,imx28-auart", "fsl,imx23-auart"; reg = <0x8006a000 0x2000>; interrupts = <112>; dmas = <&dma_apbx 8>, <&dma_apbx 9>; dma-names = "rx", "tx"; }; Note: Each auart port should have an alias correctly numbered in "aliases" node. Example: [...]
Организация дерева устройств на наивысшем уровне описания узла.
Под корнем дерева устройств, используется следующая организация уровня узла дерева устройств:
- Узел cpus(центрального процессора), который имеет под-узел описания кажущего CPU в системе.
- Узел memory(узел памяти), который определяет расположение и размер ОЗУ(RAM).
- Узел chosen(выбора), который определяет параметры выбора или определения времени загрузки системной прошивки. В практике, используется для передачи управления командной строки ядра.
- Узел aliases(прозвища), по сути дела определение горячих клавиш для основного узла.
- Один или много узлов определения шин в SoC (система на кристалле).
- Один или много узлов определения устройств платы.
Организация дерева устройств на imx28.dtsi.
arch/arm/boot/dts/imx28.dtsi / { aliases { ... }; cpus { ... }; apb@80000000 { apbh@80000000 { /* Some devices */ }; apbx@80040000 { /* Some devices */ }; }; ahb@80080000 { /* Some devices */ }; };
Организация дерева шин в i.MX28.
Организация дерева устройств на imx28.dts.
arch/arm/boot/dts/imx28-evk.dts / { model = "Freescale i.MX28 Evaluation Kit"; compatible = "fsl,imx28-evk", "fsl,imx28"; memory { reg = <0x40000000 0x08000000>; }; apb@80000000 { apbh@80000000 { ... }; apbx@80040000 { ... }; }; ahb@80080000 { ... }; sound { ... }; leds { ... }; backlight { ... }; };
Верхний уровень совместимости свойств.
- Верхний уровень совместимости свойств в основном описывается совместимой строкой для платы, и для SoC.
- Значение всегда дается в первом случае в наиболее корректной форме, в последнем случае наименее корректной форме (не специфичной).
- Использование соответствия с dt_compat поля DT_MACHINE структуры.
static const char *mxs_dt_compat[] __initdata = { "fsl,imx28", "fsl,imx23", NULL, }; DT_MACHINE_START(MXS, "Freescale MXS (Device Tree)") .dt_compat = mxs_dt_compat, [...]
- Можно всегда использовать с кодом для тестирования платы.
if (of_machine_is_compatible("fsl,imx28-evk")) imx28_evk_init();
Шины, адреса ячеек и размер ячеек.
Для внутренней шины необходимо определить следующие свойства:
- Совместимые свойства, которые идентифицируют контроллер шины (для случаев I2C, SPI, PCI и других). Особое значение compatible = “simple-bus” способствует простой адресации памяти шины для не специфического обработчика или драйвера. Узел-ребенок может быть зарегистрирован для платформы.
- Свойство #address-cells показывает, сколько ячеек памяти (32 битных значений) необходимо для части базовой адресации для свойств reg.
- #size-cells одинаковый, для части размера свойства reg.
- В свойство ranges(диапазон) может описываться адрес трансляции между шиной-ребенок и шиной-родитель. Когда просто объявляется диапазон, простое объявление имеет ввиду что транслятор идентифицирует трансляцию.
simple-bus, адреса ячеек и размер ячеек.
apbh@80000000 { compatible = "simple-bus"; #address-cells = <1>; #size-cells = <1>; reg = <0x80000000 0x3c900>; ranges; [...] hsadc: hsadc@80002000 { reg = <0x80002000 0x2000>; interrupts = <13>; dmas = <&dma_apbh 12>; dma-names = "rx"; status = "disabled"; }; [...] };
Шина I2C, адреса ячеек и размер ячеек.
i2c0: i2c@80058000 { #address-cells = <1>; #size-cells = <0>; compatible = "fsl,imx28-i2c"; reg = <0x80058000 0x2000>; interrupts = <111>; [...] sgtl5000: codec@0a { compatible = "fsl,sgtl5000"; reg = <0x0a>; VDDA-supply = <®_3p3v>; VDDIO-supply = <®_3p3v>; clocks = <&saif0>; }; at24@51 { compatible = "at24,24c32"; pagesize = <32>; reg = <0x51>; }; };
Обработка прерываний.
- interrupt-controller (контроллер прерывания) бинарное свойство, показывает что текущий узел контроллер прерывания.
- #interrupt-cells (ячейка прерывания) показывает номер ячеек в свойстве прерываний, для менеджера прерываний, с помощью выбора контроллера прерывания.
- interrupt-parent (родитель-прерывания) phandle указатель на контроллер прерывания для текущего узла. Основной наивысший уровень родитель-прерывания описывает поведение для главного контроллера прерывания.
Пример прерывания imx28.dtsi.
/ { interrupt - parent = <&icoll>; apb @80000000 { apbh @80000000 { icoll: interrupt - controller @80000000 { compatible = "fsl,imx28-icoll", "fsl,icoll"; interrupt - controller; #interrupt - cells = < 1 > ; reg = <0x80000000 0x2000>; }; ssp0: ssp @80010000 { [...] interrupts = <96>; }; }; }; };
Пример совместимости на Tegra 20.
Пример прерывания tegra20.dtsi.
/ { interrupt - parent = <&intc>; intc: interrupt - controller { compatible = "arm,cortex-a9-gic"; reg = <0x50041000 0x1000 0x50040100 0x0100>; interrupt - controller; #interrupt - cells = < 3 > ; }; i2c @7000c000 { compatible = "nvidia,tegra20-i2c"; reg = <0x7000c000 0x100>; interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>; #address - cells = < 1 > ; #size - cells = < 0 > ; [...] }; gpio: gpio { compatible = "nvidia,tegra20-gpio"; reg = <0x6000d000 0x1000>; interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>, [...], <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>; #gpio - cells = < 2 > ; gpio - controller; #interrupt - cells = < 2 > ; interrupt - controller; }; };
Пример прерывания tegra20-harmony.dts.
i2c @7000c000 { status = "okay"; clock - frequency = <400000>; wm8903:wm8903 @1a { compatible = "wlf,wm8903"; reg = <0x1a>; interrupt - parent = <&gpio>; interrupts = <TEGRA_GPIO(X, 3) IRQ_TYPE_LEVEL_HIGH>; gpio - controller; #gpio - cells = < 2 > ; micdet - cfg = <0>; micdet - delay = <100>; gpio - cfg = <0xffffffff 0xffffffff 0 0xffffffff 0xffffffff>; }; };
Пример дерева тактирования, Marvell Armada XP.
Создание экземпляра объекта тактирования.
soc { coreclk: mvebu - sar @18230 { compatible = "marvell,armada-xp-core-clock"; reg = <0x18230 0x08>; #clock - cells = < 1 > ; }; cpuclk: clock - complex @18700 { #clock - cells = < 1 > ; compatible = "marvell,armada-xp-cpu-clock"; reg = <0x18700 0xA0>; clocks = <&coreclk 1>; }; gateclk: clock - gating - control @18220 { compatible = "marvell,armada-xp-gating-clock"; reg = <0x18220 0x4>; clocks = <&coreclk 0>; #clock - cells = < 1 > ; }; } clocks { /* 25 MHz reference crystal */ refclk:oscillator { compatible = "fixed-clock"; #clock - cells = < 0 > ; clock - frequency = <25000000>; }; };
Пример тактирования: распределение тактирования.
CPU, использует cpuclk
cpu@0 { device_type = "cpu"; compatible = "marvell,sheeva-v7"; reg = <0>; clocks = <&cpuclk 0>; };
Таймер, использует один из 2-х сигналов тактирования coreclk или refclk.
timer@20300 { compatible = "marvell,armada-xp-timer"; clocks = <&coreclk 2>, <&refclk>; clock-names = "nbclk", "fixed"; };
USB, использует gateclk
usb@52000 { compatible = "marvell,orion-ehci"; reg = <0x52000 0x500>; interrupts = <47>; clocks = <&gateclk 20>; status = "disabled"; };
Связи pinctrl: клиентская сторона.
- Подсистема pinctrl позволяет управлять мультиплексированием ног микропроцессора.
- В дереве устройств, ноги микропроцессора должны быть мультиплексированы и для них должна быть объявлена конфигурация pinctrl.
- pinctrl- – свойства позволяющие дать список pinctrl, конфигурация нужная для основного состояния устройства.
- pinctrl-names свойство позволяющее дать имя каждому состоянию.
- Когда устройство проверено, начальное pinctrl состояние использует определение по умолчанию, после, состояние определяется с помощью запроса.
ssp0: ssp@80010000 { pinctrl-names = "default"; pinctrl-0 = <&mmc0_8bit_pins_a &mmc0_cd_cfg &mmc0_sck_cfg>; [...] };
Конфигурация pinctrl.
- Конфигурация pinctrl предоставляет список ножек микроконтроллера и их конфигурацию.
- Подобная конфигурация определяет под-узел pinctrl устройства, для одного из уровня, уровня SoC или уровня платы.
- Связи для такой конфигурации очень зависят от спецификации pinctrl используемого устройства.
i.MX28
mmc0_8bit_pins_a: mmc0-8bit@0 { fsl,pinmux-ids = < 0x2000 /* MX28_PAD_SSP0_DATA0__SSP0_D0 */ 0x2010 /* MX28_PAD_SSP0_DATA1__SSP0_D1 */ [...] 0x2090 /* MX28_PAD_SSP0_DETECT__SSP0_... */ 0x20a0 /* MX28_PAD_SSP0_SCK__SSP0_SCK */ >; fsl,drive-strength = <1>; fsl,voltage = <1>; fsl,pull-up = <1>; };
Marvell Kirkwood
pmx_nand: pmx-nand { marvell,pins = "mpp0", "mpp1", "mpp2", "mpp3", "mpp4", "mpp5", "mpp18", "mpp19"; marvell,function = "nand"; };
DT – описание железа, не конфигурация.
- Дерево устройств – настоящий язык описания железа.
- Необходимо описать уровень(слой) железа и понимать как это работает.
- Но нет конкретного описания, конфигурации, железа нацеленного именно под ваши интересы.
- Для примера. Вы можете описать DT который может использовать DMA или Вы наоборот можете не использовать DMA. Также Вы можете не описывать DT для DMA, если в DMA нет необходимости.
DT связи для ABI(application binary interface – бинарный интерфейс приложений).
- С тех пор как появилось дерево устройств, ядро операционной системы стало более независимое.
- Данная оригинальная идея позволяет записать DTB на на различные устройства производителей, так же данный факт позволяет записать пользователю любой дистрибутив операционной системы.
- Дерево устройств связано в DTB и после этого не может быть изменено.
- Данная вещь обычно обозначает, что связь дерева устройств становиться частью ядра ABI, необходимо с осторожностью обращаться с данным функционалом.
- Однако, разработчики ядра не понимают, что этого очень тяжело добиться и замедляют интеграцию драйверов.
- На саммите ARM ядра ведутся дебаты по поводу упрощения правил. Добавляются дискуссионные вопросы для саммита ядра, и после публикуются отчеты.
Базовое руководство для проектирования связей.
- Точная совместимость строки лучше, чем расплывчатая одна. У Вас есть драйвер который может быть наложен поверх вариантов T320 и T330 для Вашего железа. Вы можете временно использовать foo, t3xx для совместной строки описания. Плохая идея, что если T340 немного отличается, в широком смысле? Лучше будет использовать foo, t320 для обеих T320 и T330.
- Не создавайте слишком большого описания деталей железа в дереве устройств. Когда две платформы отличаются незначительно, разработчики могут добавить все изменения в дерево устройств, это может быть включаемые регистры смещения, битовые маски или др.
- Плохая идея: создавать связи для большого комплекса устройств, это может привести к проблемам в будущем. Для этого лучше использовать 2 строки совмещения и обработчик для различных устройств.
Направление развития на будущее.
- Построения более разнообразных вариантов подсистем связей дерева устройств.
- Инструмент который проверит систему дерева устройств.
- Уменьшить количество вычислений за пределами ядра.
Оригинал статьи на английском языке по ссылке.