Как использовать дерево устройств


1. Базовый формат данных

2. Основные понятия

3. Как работает адресация

4. Как работают прерывания

5. Специфические данные устройства

6. Специальные узлы

7.   Машина с мостом Host/PCI  



7. Машина с мостом Host/PCI

7.1. Усовершенствованный образец машины

Теперь, когда мы определились с основами, давайте добавим некоторое оборудование к образцу машины, чтобы обсудить некоторые из более сложных вариантов использования.

Усовершенствованный образец машины добавляет мост хоста PCI с регистрами управления, отображаемый в память по адресу 0x10180000, и BAR, запрограммированный на запуск выше адреса 0x80000000.

Учитывая то, что мы уже знаем о дереве устройств, мы можем начать с добавления следующего узла для описания моста хоста PCI.


7.2. Мост Host/PCI

В этом разделе описывается узел моста Host/PCI. Обратите внимание, что в этом разделе предполагается наличие некоторых базовых знаний о PCI. Это не руководство по PCI, если вам нужна более подробная информация, прочтите PCI System Architecture by Tom Shanley/Don Anderson , а также PCI Bus Binding

7.2.1. Нумерация шины PCI

Каждый сегмент шины PCI имеет уникальный номер, и нумерация шины отображается в узле pci с помощью свойства bus-range, которое содержит две ячейки. Первая ячейка дает номер шины, присвоенный этому узлу, а вторая ячейка дает максимальный номер шины любой из подчиненных шин PCI.

У тестовой машины одна шина pci, поэтому обе ячейки равны 0.

7.2.2. Трансляция адресов PCI

Подобно локальной шине, описанной ранее, адресное пространство PCI полностью отделено от адресного пространства ЦП, поэтому для перехода от адреса PCI к адресу ЦП требуется преобразование адресов. Как всегда, это делается с помощью свойств range, #address-cells и #size-cells.

Как видите, дочерние адреса (адреса PCI) используют 3 ячейки, а диапазоны PCI кодируются в 2 ячейки. Возникает вопрос, зачем нам нужны три 32-битные ячейки для указания адреса PCI. Эти три ячейки помечены как Phys.hi, Phys.mid и Phys.low

  • phys.hi cell: npt000ss bbbbbbbb dddddfff rrrrrrrr
  • phys.mid cell: hhhhhhhh hhhhhhhh hhhhhhhh hhhhhhhh
  • phys.low cell: llllllll llllllll llllllll llllllll

    Адреса PCI имеют ширину 64 бита и закодированы в Phys.mid и Phys.low. При этом Phys.high является битовым полем:

    • n: флаг перемещаемого региона (здесь не играет роли)
    • p: флаг региона с возможностью предварительной выборки (кешируемый)
    • t: флаг адреса с псевдонимом (здесь не играет роли)
    • ss: код пространства, в котором:
      • 00: конфигурационное пространство
      • 01: пространство ввода/вывода
      • 10: 32-битное пространство памяти
      • 11: 64-битное пространство памяти
    • bbbbbbbb: Номер шины PCI. PCI может иметь иерархическую структуру, поэтому могут быть мосты PCI/PCI, которые будут определять вспомогательные шины.
    • ddddd: Номер устройства, обычно связанный с сигнальными соединениями IDSEL.
    • fff: Номер функции. Используется для многофункциональных устройств PCI.
    • rrrrrrrr: Регистрационный номер. Используется для циклов настройки.

    Для преобразования адресов PCI важными полями являются биты p и ss. Значения p и ss в Phys.hi определяют, к какому адресному пространству PCI осуществляется доступ.

    В представленном здесь свойстве range есть три региона:

  • 32-битная предварительно выбираемая область памяти, начинающаяся с адреса PCI 0x80000000 размером 512 Мб, которая будет отображена на адрес 0x80000000 на центральном процессоре.
  • 32-битная область памяти без предварительной выборки, начинающаяся с адреса PCI 0xa0000000 размером 256 Мб, которая будет отображена на адрес 0xa0000000 на центральном процессоре.
  • область ввода-вывода, начинающаяся с адреса PCI 0x00000000 размером 16 Мб, которая будет отображена на адрес 0xb0000000 на центральном процессоре.

    Наличие битового поля phys.hi означает что операционная система должна знать, что узел представляет собой мост PCI, чтобы она могла игнорировать нерелевантные поля для целей трансляции. ОС будет искать строку pci в узлах шины PCI, чтобы определить, нужно ли маскировать дополнительные поля.

    7.2.3. Трансляция адресов PCI DMA

    Вышеуказанные диапазоны определяют, как ЦП видит память PCI, и помогают ЦП настраивать правильные окна памяти и записывать правильные параметры в различные регистры устройства PCI. Иногда это называют исходящей памятью (outbound memory).

    Особый случай трансляции адресов касается того, как оборудование хоста PCI видит основную память системы. Это происходит, когда хост-контроллер PCI будет действовать как главный и независимо обращаться к основной памяти системы.

    Поскольку это часто другое представление, чем у ЦП (из-за того, как были подключены линии памяти), это может потребоваться запрограммировать в хост-контроллере PCI при инициализации. Это рассматривается как своего рода DMA, поскольку шина PCI независимо выполняет прямой доступ к памяти, и по этой причине сопоставления называются dma-range.

    Этот тип отображения памяти иногда называют входящей памятью (inbound memory), который не является частью спецификации дерева устройств PCI.

    В некоторых случаях ПЗУ (BIOS) или аналогичный будет настраивать эти регистры при загрузке, но в других случаях контроллер PCI полностью не инициализирован, и эти преобразования необходимо настраивать из дерева устройств.

    Затем драйвер хоста PCI обычно анализирует свойство dma-range и соответствующим образом настраивает некоторые регистры в хост-контроллере.

    Расширяя пример выше:

    Эта запись dma-range указывает, что с точки зрения хост-контроллера PCI 512 Мб по адресу PCI 0x00000000 появятся в основной памяти ядра по адресу 0x80000000.

    Как видите, мы просто установили тип адреса ss равным 0x02, указывая, что это некоторая 32-битная память.


    7.3. Расширенное отображение прерываний

    Теперь мы подошли к самому интересному - отображению прерываний PCI.

    Устройство PCI может инициировать прерывания, используя провода #INTA, #INTB, #INTC и #INTD. Знак решетки # перед именами прерываний означает, что активен низкий уровень, это общепринятое соглашение, и линии прерывания PCI всегда активны на низком уровне.

    Однофункциональное устройство обязано использовать #INTA для прерываний. Многофункциональное устройство должно использовать #INTA, если оно использует один вывод прерывания, #INTA и #INTB, если оно использует два вывода прерывания и т.д.

    Согласно этим правилам, #INTA обычно используется большим количеством функций, чем #INTB, #INTC. и #INTD. Чтобы распределить нагрузку по четырем линиям IRQ, поддерживающим #INTA - #INTD, каждый слот PCI или устройство обычно подключаются к разным входам на контроллере прерываний поочередно, чтобы избежать подключения всех клиентов #INTA к одной и той же входящей линии прерывания.

    Эта процедура называется переключением прерываний. Таким образом, дереву устройств требуется способ отображения каждого сигнала прерывания PCI на входы контроллера прерывания.

    Свойства #interrupt-cells, interrupt-map и interrupt-map-mask используются для описания отображения прерываний.

    Фактически, описанное здесь отображение прерываний не ограничивается шинами PCI, любой узел может указывать сложные карты прерываний, но случай PCI является наиболее распространенным.

    Обратите внимание, что номера прерываний PCI используют только одну ячейку, в отличие от контроллера прерываний системы, который использует 2 ячейки; один для номера IRQ и один для флагов. PCI требуется только одна ячейка для прерываний, потому что прерывания PCI всегда должны быть чувствительными к низкому уровню.

    В нашем примере платы у нас есть 2 слота PCI с 4 линиями прерывания, соответственно, поэтому мы должны сопоставить 8 линий прерывания с контроллером прерываний.

    Это делается с помощью свойства interrupt-map. Поскольку номера прерывания (#INTA и т.д.) недостаточно для различения нескольких устройств PCI на одной шине PCI, мы также должны указать, какое устройство PCI инициировало линию прерывания.

    У каждого устройства PCI есть уникальный номер устройства, который мы можем использовать. Чтобы различать прерывания нескольких устройств PCI, нам нужен кортеж, состоящий из номера устройства PCI и номера прерывания PCI.

    Мы создаем спецификатор единичного прерывания, который имеет четыре ячейки:

  • три ячейки #address-cells, состоящие из phys.hi, phys.mid, phys.low
  • одна ячейка #interrupt-cell (#INTA, #INTB, #INTC, #INTD).

    Поскольку нам нужна только часть номера устройства в адресе PCI, используется свойство interrupt-map-mask, которое также состоит из 4-ного кортежа, как спецификатор единичного прерывания.

    Биты 1 в маске обозначают, какую часть спецификатора единичного прерывания следует учитывать. В нашем примере мы видим, что требуется только номер устройства, часть phys.hi, и нам нужны 3 бита, чтобы различать четыре линии прерывания (счетчик линий прерывания PCI начинается с 1, а не с 0!).

    Теперь мы можем построить свойство прерывания. Это свойство представляет собой таблицу, и каждая запись в этой таблице состоит из спецификатора прерывания дочернего модуля (шина PCI), родительского дескриптора (контроллера прерывания, который отвечает за обслуживание прерываний) и спецификатора прерывания родительского модуля. Итак, в первой строке мы можем прочитать, что прерывание PCI #INTA отображается на IRQ9, низкий уровень чувствительности нашего контроллера прерываний.

    Единственная отсутствующая часть на данный момент - это странные числа в спецификаторе прерывания модуля шины PCI. Важной частью спецификатора единичного прерывания является номер устройства из битового поля phys.hi. Номер устройства зависит от платы и зависит от того, как каждый хост-контроллер PCI активирует вывод IDSEL на каждом устройстве.

    В этом примере слоту PCI 1 назначен идентификатор устройства 24 (0x18), а слоту 2 PCI назначен идентификатор устройства 25 (0x19).

    Значение phys.hi для каждого слота определяется путем сдвига номера устройства на 11 бит в раздел ddddd битового поля следующим образом: phys.hi для слота 1 - 0xC000 phys.hi для слота 2 - 0xC800

    В окончательном виде свойство interrupt-map показывает:

  • #INTA слота 1 - IRQ9, низкий уровень чувствительности (level low sensitive) на первичном контроллере прерываний.
  • #INTB слота 1 - IRQ10    -- " --
  • #INTC слота 1 - IRQ11    -- " --
  • #INTD слота 1 - IRQ12    -- " --
  • #INTA слота 2 - IRQ10, низкий уровень чувствительности на первичном контроллере прерываний.
  • #INTB слота 2 - IRQ11    -- " --
  • #INTC слота 2 - IRQ12    -- " --
  • #INTD слота 2 - IRQ9    -- " --

    Свойство interrupts = <8 0> ; описывает прерывания, которые может запускать сам контроллер хост/PCI-моста. Не путайте эти прерывания с прерываниями, которые могут вызывать устройства PCI (с использованием INTA, INTB, ...).

    Нужно отметить, что как и в случае со свойством interrupt-parent, наличие свойства interrupt-map на узле изменит контроллер прерывания по умолчанию для всех дочерних и внучатых узлов. В приведенном примере PCI это означает, что мост Host/PCI становится контроллером прерываний по умолчанию. Если устройство, подключенное через шину PCI, имеет прямое соединение с другим контроллером прерываний, ему также необходимо указать собственное свойство родительского прерывания.



  •   1    2    3    4    5    6    7