что такое amd gpio controller
990x.top
Простой компьютерный блог для души)
AMD GPIO Driver — что это за драйвер? (AMD GPIO Controller)
Драйвер интерфейса ввода/вывода (чипсет).
Простыми словами — нужен для корректной работы чипсета (набор логики) на материнской плате.
Без драйвера скорее всего тоже все будет работать, но функции будут только базовые, дополнительные — работать не будут. Всякие технологии, заложенные в чипсет, в том числе которые теоретически могут ускорить работу Windows — будут работать только после установки этого драйвера. Например регулировка частоты в Windows (в настройках Электропитания), автоматическое ее снижение в простое — не будет работать без этого драйвера. Также могут не работать порты USB 3.0/3.1 (точнее работать будут, но как версия 2.0) или некоторые технологии ускорения передачи данных по USB.
Также вы можете встретить драйвер с названием AMD GPIO Driver Promontory — это просто версия для чипсетов серии 300, 400 и X570.
Сам драйвер входит в состав пакета AMD Chipset Drivers. Поэтому можно сделать вывод, что он точно нужен для чипсета. Также в пакет входят и другие — AMD PCI driver, PSP driver, AMD SMbus, Promonotory GPIO Driver. И еще — после установки пакета можно перейти в эту папку (в нее распаковываются дрова):
И установить оттуда схему электропитания Ryzen balanced PowerPlan, которая тоже оптимизирует работу ПК.
После корректной установки драйверов у вас в диспетчере появится устройство AMD GPIO Controller:
Кстати, выше еще видим AMD PSP Device — это специальное устройство, которое участвует в защите софта AMD, а также используется в работе антивирусов, защищает некоторые компоненты Windows от вирусов. Аналог Intel Management Engine. PS: быстрый способ открыть диспетчер устройств: зажмите Win + R > введите команду devmgmt.msc > нажмите ОК.
Надеюсь информация оказалась полезной. Удачи и добра, до новых встреч друзья!
Что такое GPIO (General Purpose Input/Output).
Что такое GPIO (General Purpose Input/Output).
Порт GPIO (универсальный ввод/вывод) размещают внутри кристаллов прцессоров, чипсетов, вспомогательных чипов управления. Порт GPIO обрабатывает как входящие, так и исходящие цифровые сигналы. В качестве входного порта его можно использовать, например, для связи ЦП или PCH с сигналами полученными от переключателей, или цифровыми показаниями, полученными от датчиков. В качестве выходного порта его можно использовать для формирования сигналов управления внешними операциями на основе программ исполняемых процессором, например, для управления включением/ выключением светодиодной подсветки дисплея, или вывода сигналов управления двигателем и т. п..
GPIO называют «порт общего назначения», поскольку каждый его разряд может быть свободно настроен для работы по приему как входных сигналов, так и для формирования выходных сигналов (программным путем). В ранних вариантах каждый порт был либо исключительно входным, либо исключительно выходным. Однако сейчас GPIO является «гибким» по использованию своих контактов. Вы можете установить их назначение наилучшим образом в соответствии с вашими потребностями ( на вход, на выход или вход/выход в любой количественной комбинации.
Во время чтения, записи и работы с цифровыми значениями (0 и 1) внешние устройства часто используют уровни сигнала: «низкое» напряжение и «высокое» напряжение. GPIO выполняет необходимые преобразования в обоих направлениях.
Регистр входных данных порта (PIDR). Показывает состояние входных контактов. Для каждого вывода вход LOW-сигнала устанавливает соответствующее значение разряда регистра в 0; вход сигнала HIGH устанавливает соответствующее значение разряда регистра в 1. CPU программно считывает этот регистр, чтобы узнать самые последние уровни сигнала его разрядов. Значения сохраняются до перезаписывания; каждый раз, когда CPU считывает регистр, он будет отображать текущие состояния входных сигналов.
Рис. 1. Базовая структура GPIO (концептуальная).
Регистр выходных данных порта (PODR). Чтобы выводить данные через выходные выводы, ЦП программно записывает выходные значения в этот регистр. Значение 0 преобразуется в LOW-выход; 1 преобразуется в HIGH выход. Как и в обычной памяти, значения, записанные здесь, сохраняются до перезаписывания. Это означает, что уровень выходного контакта будет поддерживаться до тех пор, пока значение не будет изменено.
Модуль GPIO является гибким для различных приложений. Каждый вывод GPIO может быть настроен в качестве альтернативного режима ввода или альтернативного выхода. Альтернативная функция может быть выбрана регистром установки.
На рис. 3 видим, например, что разряд GPIO77 – используется для приема входного сигнала DGPU_PWRGD; разряд GPIO7 8 – используется для формирования выходного сигнала PXS_PWREN.
Рис. 3. Фрагмент схемы (однокристальный вариант Intel Haswell ULT/Broadwell U)
Рис. 4. Использование порта GPIO в ЕС контроллере (EC-KB9012).
GPIO для чайников (часть 1)
Что делать, когда нечего делать? Попробовать что-нибудь новое!
Но всё ж таки Raspberry Pi может делать одну вещь гораздо более эффективнее, чем любой домашний компьютер- он может управлять внешними устройствами. Устройства могут быть абсолютно любыми, от обычной лампочки, до беспилотного летательного аппарата. В данном случае, область применения Raspberry ограничена лишь вашей фантазией и знаниями. И если вы никогда и ничего подобного не делали, но это вас заинтересовало, то эта статья для вас. И так, начнём.
Во-первых, нужно найти светодиод:
Его можно достать из старой сломанной игрушки, из зажигалки с фонариком, попросить у знакомого радиоэлектронщика, в конце концов, просто купить.
Во-вторых, понадобятся проводочки любые и парочка коннекторов BLS:
Такие коннекторы можно вытащить из старого системного блока вместе с проводами, или попросить у знакомого компьютерщика, или тоже купить. Они прекрасно подходят для подключения к разъёму на Raspberry.
Вот собственно как эти порты расположены на плате:
Чтобы светодиод зажёгся, нам нужно его подключить к источнику питания. Выбираем для питания светодиода Р1-01, верхний по рисунку штырёк, на котором присутствуетнапряжение 3,3в. Для управления светодиодом нам понадобится один порт GPIO. Можно выбрать любой. Но если у вас есть разъём BLS, то удобнее в данном случае использовать порт, который выведен на штырёк P1-03 и называется GPIO 0. В таком случае мы, воспользовавшись одним разъёмом, сможем подключить наш светодиод. И так, мы будем подключать светодиод между ножками разъёма P1-01 и Р1-03. С вывода Р1-01 мы берём +3,3в для питания светодиода, а вывод Р1-03 будет тем самым управляющим выводом порта GPIO. Все эти порты физически находятся внутри центрального процессора Raspberry Pi, который называется BCM2835. Этот процессор может подключать любой порт к источнику напряжения 3,3в, а может подключить порт к 0 питания (а может вообще никуда не подключать, но об этом позже). Эти переключения он делает в соответствии с поданной командой. Значит, когда порт будет подключён к напряжению +3,3в, наш светодиод гореть не будет, т.к. току некуда идти. А когда процессор подключит порт к 0, то наш светодиод загорится, т.к. ток побежит от +3,3в к 0 через светодиод. Значит наша программа должна будет отдавать соответствующие команды процессору в соответствии с нашим желанием.
Маленькое, но важное. На самом деле, мы не должны подключать светодиод напрямую между источником питания +3,3в и выводом порта. Это нельзя делать по двум причинам. Причина первая: любой светодиод нормально работает при определённом токе. Если через светодиод потечёт большой ток (а выход +3,3в способен отдать до 50мА), то светодиод сгорит. Если маленький ток, то светодиод будет гореть слишком слабо, либо вообще не будет светиться. Для большинства обычных светодиодов рабочий ток находится в пределах 10-20мА. Отсюда вытекает и вторая причина (хотя в данном случае она несущественна). Если мы пропустим большой ток через порт GPIO, то этим самым мы уничтожим процессор и Raspberry- умрёт. Поэтому, мы должны следить, чтобы через порт не протекал ток больше допустимого. Примем для себя ограничение в 16мА, так мы точно не сожжем процессор. Как этого добиться? Очень просто! Нам нужно последовательно со светодиодомвключить токоограничивающий резистор. И сейчас мы его рассчитаем.
Примем для светодиода рабочий ток в 10мА. Убеждаемся в том, что выбранный нами ток не превышает предельно допустимый ток для порта в 16мА. Теперь зная напряжение питания 3,3в и рабочий ток 10мА, мы можем по закону Ома рассчитать необходимое нам сопротивление. R=U/I=3,3/0,01=330Ом. Значит нам нужно найти резистор с сопротивлением 330Ом. А точнее- сопротивлением не менее 330Ом. Больше- можно. Светодиод будет заметно светиться и при сопротивлении 1000 Ом, или 1кОм. В общем наша задача- найти резистор с сопротивлением от 330 Ом до 1кОм. Если вы его нашли, то можно собрать вот такую схему:
Схему лучше собрать на макетной плате. Лично мне, для экспериментов, мой сын дал на прокат свой конструктор «Знаток».
Так выглядит схема в сборе:
Так мы подключаемся к Raspberry:
А вот общий план всей конструкции:
Почему на именно на Си? Просто по тому, что я других языков не знаю, а раз вы читаете эту статью, то скорее всего вы тоже немного знаете о программировании и радиоэлектронике, а значит, вам всё равно с какого языка начинать.
Обычно изучение языков программирования начинают с написания программы «Hello World!», но мы же круче «тех» чайников, поэтому мы начнём сразу с низкоуровневой работы с периферией. Тем более, что это не намного сложнее ХеллоуВорлда. 😉 Что для этого нужно? Нужен любой текстовый редактор, в котором мы будем набирать программу. В Raspbian есть отлично подходящий для этого редактор “nano”. Ещё нужен компилятор, это программа, которая осуществляет перевод написанной нами программы с человечески понятного языка на язык, понятный компьютеру. Т.е. делает из нашей программы исполняемый файл, который мы впоследствии и запустим на Raspberry. Эта штука тоже у нас есть, называется gcc. Этот компилятор поставляется в комплекте со всеми Линуксами и уже готов к работе.
Как видите,всё необходимое у нас уже есть. Хотя нет. Одной вещи все-таки у нас не хватает. Её мы возьмем из интернета. Речь идёт о библиотеке функций управления портами GPIO на Raspberry, специально написанно добрым человеком для того, чтобы наша программа по своей простоте могла бы соперничать с «Хеллоуворлдом» и нам самим бы не пришлось ломать голову, изучая техническую документацию на процессор и протоколы работы с его внутренностями. Сама библиотека состоит из заголовочного файла, в котором обозначены все имена функций со структурами переменных и файла библиотеки самих функций. Эту библиотеку нужно скачать и установить, чтобы компилятор мог с ней работать. Библиотека называется bcm2835-1.17. Последние цифры в названии библиотеки, обозначают её версию. А так, как библиотека постоянно обновляется автором, то версии будут меняться. на сегодняшний день доступна версия 1.17. Узнать о номере последней версии можно по адресу: http://www.open.com.au/mikem/bcm2835/index.html По этой же ссылке вы можете ознакомиться со всеми функциями, которые присутствуют в этой библиотеке.
Мы же пока установим версию 1.17. Запускаем окно терминала и вводим туда команду: wget http://www.open.com.au/mikem/bcm2835/bcm2835-1.17.tar.gz
Библиотека быстренько скачивается. Чтобы её установить, нужно сначала её разархивировать. Это делается следующей командой: tar zxvf bcm2835-1.17.tar.gz
Теперь перейдём в директорию, куда эта библиотека развернулась: cd bcm2835-1.17
Ну и инсталлируем её:
Тут можно создать папочку для наших экспериментов с любым именем, например myprog: mkdir myprog
Перейдём в эту папку: cd myprog
И начинаем писать нашу программу: nanoGPIO-test.c
Эта команда запускает текстовый редактор nano, который создаёт текстовый файл GPIO-test.c.Теперь можете набрать в нём следующую программу (можно просто скопировать и вставить):
Обратите внимание на строки #define. Их в программе 2 и одна из них закомментирована. Одна строка для ревизии RPi v1, вторая для RPi v2. Если у вас v1, то всё оставьте как есть. Если у вас RPi v2, то первую строку с #define удалите, а со второй уберите символ комментария //.В будущем, во всех остальных программах, просто добавляйте _V2_ между RPI и GPIO в определении портов, если ваша плата RPi v2.
Теперь о том, что делает каждая строка в нашей программе.
Все надписи после двойного слеша // являются коментариями и никак не влияют на выполнение программы.
int main() это начало нашей программы, обозначение главной функции в Си.
return 1; то аварийно завершает программу и передаёт на выходе код 1.
Т.е. алгоритм работы с портом GPIO в режиме записи, т.е. вывода, выглядит следующим образом:
1. Инициализируем GPIO;
2. Устанавливаем режим для выбранного порта на Вывод;
3. Теперь можем управлять этим портом, устанавливая его в высокое, или низкое состояние. Соответственно на этом порте будет пристутствовать либо +3,3В, либо 0В. Что соответствует логической 1 и логическому 0 соответственно.
На этом на сегодня закончим. В следующей части научим наш светодиод загораться более полезным образом, а так же научимся портами GPIO не только отдавать команды другим устройством, но и слушать их.А пока можете начинать изучать язык Си. А так же попробуйте изменить эту программу так, чтобы светдиод управлялся бы другим портом и испытайте её.
Драйвер виртуальных GPIO с контроллером прерываний на базе QEMU ivshmem для Linux
Трудно недооценить роль GPIO, особенно в мире встраиваемых систем ARM. Помимо того, что это крайне популярный материал для всех руководств для начинающих, GPIO обеспечивают способ для управления многими периферийными устройствами, выступают в качестве источника ценных прерываний, или даже могут быть единственным доступным способом общения с миром для SOC.
Основываясь на собственном скромном опыте, могу сказать, что прерывания далеко не самая освященная тема в сообществе Linux. Из-за своих особенностей, а так же сильной привязки к аппаратной части, все обучающие материалы посвященные прерываниям лишены реального и легко воспроизводимого примера. Данный факт мешает пониманию того, что очень часто прерывания и GPIO неразделимы, особенно в области встраиваемого Linux. Многие начинают верить, что GPIO это очень простая и скучная вещь (которая кстати и стала таковой благодаря подсистеме sysfs).
Даже в примере приведенном в LDD3 (драйвер snull) прерывания эмитируются явным вызовом функции парного устройства. Так же имеются примеры в курсах USFCA (http://cs.usfca.edu/
cruse/cs686s08/), но они используют чужое прерывание, тесно связаны с архитектурой x86 и сильно устарели.
Предлагаемое решение способно решить данные проблемы. С точки зрения пространства пользователя и, во многом, во внутренней реализации драйвер неотличим от большинства «реальных», предоставляющих прерывания портов входов/выходов общего назначения. На данный момент драйвер поддерживает прерывания по переднему или заднему фронту и может быть использован как источник прерываний для других устройств.
ivshmem — разделяемая память Inter-VM
Разработано для совместного использования разделяемой памяти (выделенной на хост-платформе через механизм POSIX shared memory API) множественными процессами QEMU с различными гостевыми платформами. Для того чтобы все гостевые платформы имели доступ к области разделяемой памяти, ivshmem моделирует PCI устройство предоставляя доступ к памяти как PCI BAR.
и проанализировал быстродействие в целом.
В настоящей момент, официально, сопровождение ivshmem никто не осуществляет, тем не менее большой вклад в развитие ivshmem вносят сотрудники Red Hat.
ivshmem может послужить основой для симуляции и отладки многих классов устройств.
В данной статье мы рассматриваем виртуальную pci плату ввода/вывода общего назначения (general-purpose input/output, GPIO), которая так же является источником прерываний, и соответствующий драйвер с предоставлением доступа и управления посредством механизма sysfs.
Для разработки и тестирования использовалась виртуальная плата qemu versatilepb (system ARM).
g>> — команды или вывод выполняемые на гостевой системе.
h>> — на основной.
Пример и оригинальный код
Для начала продемонстрируем оригинальный код, основанный на оригинальном коде ( https://github.com/henning-schild/ivshmem-guest-code ), и модифицированном, в последствии, Siro Mugabi.
В принципе этого вполне достаточно для эмуляции GPIO уже в таком виде. И во многих случаях так и поступали, когда достаточно простого состояния входа или записи в выход, использование sysfs и прерываний предполагают небольшую надстройку на I/O mem.
Реализация
Заметим, что /dev/ivshmem0 и ne_ivshmem_shm_guest_usr.c нам более не нужны, вся работа с устройством со стороны гостевой машины из пространства пользователя (user-space) будет осуществляться средствами интерфейса sysfs.
Прежде чем разметить наше устройство в памяти, хотелось бы отметить, что мы просто дублируем схему применяемую в большинстве gpio драйверов.
Во-первых все входа/выхода gpio разделены на порты, как правило по 8, 16, 32 входа. Каждый порт имеет, как минимум, регистр состояния входов (GPIO_DATA), регистр направления, если переключение in/out поддерживается (GPIO_OUTPUT). Далее (если есть поддержка в самом устройстве), регистр состояния прерываний, регистры прерывания по переднему фронту (rising) и заднему фронту (falling) и по уровню (high и low). Аппаратное прерывание, поставляемое главным контроллером прерываний, как правило, одно на весь порт и делится между всеми входами порта.
Примеры существующих реализаций с комментариями
Sitara am335x
более известна в составе платы beaglebone
Разработчик: Texas Instruments
Документация: AM335x Sitara Processors Technical Reference Manual (page 4865)
Соответствующий ему драйвер gpio: linux/drivers/gpio/gpio-omap.c
Соответствующий заголовок: linux/include/linux/platform_data/gpio-omap.h
Количество входов/выходов: 128 (4 gpio порта — по 32 контакта каждый)
Имя регистра | Смещение | Имя в драйвере | Комментарий |
---|---|---|---|
GPIO_IRQSTATUS_0 | 0x02С | OMAP4_GPIO_IRQSTATUS_0 | Состояние прерывания для заданного входа |
GPIO_IRQSTATUS_1 | 0x030 | OMAP4_GPIO_IRQSTATUS_1 | Состояние прерывания для заданного входа |
GPIO_IRQSTATUS_SET_0 | 0x034 | OMAP4_GPIO_IRQSTATUS_SET_0 | Включает прерывания по заданному входу |
GPIO_IRQSTATUS_SET_1 | 0x038 | OMAP4_GPIO_IRQSTATUS_SET_1 | Включает прерывания по заданному входу |
GPIO_IRQSTATUS_CLR_0 | 0x03С | OMAP4_GPIO_IRQSTATUS_CLR_0 | Выключает прерывания по заданному входу |
GPIO_IRQSTATUS_CLR_1 | 0x040 | OMAP4_GPIO_IRQSTATUS_CLR_1 | Выключает прерывания по заданному входу |
GPIO_OE | 0x134 | OMAP4_GPIO_OE | Контролирует состояние вход/выход (in/out) |
GPIO_DATAIN | 0x138 | OMAP4_GPIO_DATAIN | Состояние входа/выхода |
GPIO_DATAOUT | 0x13C | OMAP4_GPIO_DATAOUT | Задание состояния для выходов (low/high) |
GPIO_LEVELDETECT0 | 0x140 | OMAP4_GPIO_LEVELDETECT0 | Включение/выключения прерывания для входа по низкому уровню сигнала |
GPIO_LEVELDETECT1 | 0x144 | OMAP4_GPIO_LEVELDETECT1 | Включение/выключения прерывания для входа по высокому уровню сигнала |
GPIO_RISINGDETECT | 0x148 | OMAP4_GPIO_RISINGDETECT | Включение/выключения прерывания для входа по переднему фронту |
GPIO_FALLINGDETECT | 0x14С | OMAP4_GPIO_FALLINGDETECT | Включение/выключения прерывания для входа по заднему фронту |
GPIO_CLEARDATAOUT | 0x190 | OMAP4_GPIO_CLEARDATAOUT | Переключает соответствующий вход в состояние low |
GPIO_SETDATAOUT | 0x194 | OMAP4_GPIO_SETDATAOUT | Переключает соответствующий вход в состояние high |
Примечание: GPIO_IRQSTATUS_N также используется для IRQ ACK. Управление дребезгом, а так же питанием выходит за рамки данной статьи.
ep9301
Разработчик: Cirrus Logic
Документация: EP9301 User’s Guide (page 523)
Соответствующий ему драйвер gpio: linux/drivers/gpio/gpio-ep93xx.c
Соответствующий заголовок: linux/arch/arm/mach-ep93xx/include/mach/gpio-ep93xx.h
Количество входов/выходов: 56 (7 портов gpio — по 8 контактов каждый)
Имя регистра | Смещение | Имя в драйвере | Описание |
---|---|---|---|
PADR | 0x00 | EP93XX_GPIO_REG(0x0) | Регистр состояние входов/выходов доступен для чтения записи |
PADDR | 0x10 | EP93XX_GPIO_REG(0x10) | Контролирует состояние вход/выход (in/out) |
GPIOAIntEn | 0x9C | int_en_register_offset[0] | Включает прерывания по заданному входу |
GPIOAIntType1 | 0x90 | int_type1_register_offset[0] | Задает тип прерывания level/edge |
GPIOAIntType2 | 0x94 | int_type2_register_offset[0] | Задает high/rising или low/fallingв зависимости от выбранного типа прерываний |
GPIOAEOI | 0x98 | eoi_register_offset[0] | Регистр для оповещения об обработанном прерывании |
IntStsA | 0xA0 | EP93XX_GPIO_A_INT_STATUS | Регистр состояние прерывания |
Примечание:
Из них для доступны 7 портов по 8, 8, 1, 2, 3, 2, 4 входов/выходов причем регистрами прерываний обладают только первый, второй и пятый порты.
В таблице рассмотрен только порт A.
Одной из особенностей ep9301, является то что тип прерываний both на аппаратном уровне не поддерживается, в драйвере происходит переключение в момент срабатывания прерывания. Другая интересная особенность — на порту F каждый контакт имеет свое собственное прерывание.
Bt848
Последний пример: pci плата Bt848, с gpio.
Разработчик: Intel
Документация: Bt848/848A/849A (page 68)
Соответствующий драйвер gpio: linux/drivers/gpio/gpio-bt8xx.c
Соответствующий заголовок: linux/drivers/media/pci/bt8xx/bt848.h
Количество входов/выходов: 24
Bt848 является платой видеозахвата.
Имя регистра | Смещение | Имя в драйвере | Описание |
---|---|---|---|
BT848_GPIO_OUT_EN | 0x118 | BT848_GPIO_OUT_EN | Регистр состояние входов/выходов доступен для чтения и записи |
BT848_GPIO_DATA | 0x200 | BT848_GPIO_DATA | Контролирует состояние вход/выход (in/out) |
Поддержки прерываний нет. Всего два регистра — состояние и настройка in/out.
Размечаем в памяти наше устройство
Для начала выделим место под данные и управление состоянием.
Пусть устройство обладает 8 входами/выходами общего назначения, тогда:
Имя регистра | Смещение | Имя в драйвере | Описание |
---|---|---|---|
DATA | 0x00 | VIRTUAL_GPIO_DATA | Регистр состояние входов/выходов доступен для чтения и записи |
OUTPUTEN | 0x01 | VIRTUAL_GPIO_OUT_EN | Контролирует состояние вход/выход (in/out) |
Краткая справка по интерфейсу gpio
Состояние выхода при переключении
Необходимо отметить параметр int value в функции direction_output, которая обслуживает файл /sys/class/gpio/gpioN/direction, принимающий значение не только “in”/”out”, но так же и “high”/“low”, значения которых передаются как параметр value (этот простой факт, по какой-то причине, редко упоминается в руководствах для начинающих).
Динамическое присвоение int base и наследие ARCH_NR_GPIOS
Исторически, количество GPIO в ядре было ограничено параметром ARCH_NR_GPIOS, по умолчанию равном 256 и, впоследствии увеличенном до 512 (версия 3.18).
Его смысл достаточно прост, в ядре не может быть больше GPIO чем значение параметра, если планируемое количество было больше чем значение по умолчанию, он переопределялся в соответствующем заголовочном файле платформы.
Причиной такого поведения было определение таблицы описаний GPIO как статической и максимальная величина смещения для каждого порта была ограничена:
Порты GPIO и их смещения были жестко определены в файлах описывающих аппаратную часть конкретного SOC, например:
Начиная с версии 3.19 статический массив был заменен на динамические для каждого порта GPIO, выделяемого в фукнции gpiochip_add().
Тем не менее ARCH_NR_GPIOS все еще здесь (на момент версии 4.7) и используется для поиска смещения при динамическом присваивании base.
Определим следующие функции нашего драйвера
Задать соответствующий контакт как вход:
Чтение текущего состояния контакта:
Задать соответствующий контакт как выход:
Задать состояние выхода:
Функция регистрации нашего драйвера как устройства gpio_chip:
vgread и vgwrite это просто обертки для функций iowrite8 и ioread8:
Передача значения gpiobase в качестве параметра при динамической загрузки модуля
Загрузка и тестирования модуля
DATA выставлен, OUTPUTEN выставлен.
Добавляем прерывания
Разметка регистров прерываний и базовая обработка прерывания
Примечание: В виртуальном драйвере рассматриваются только EDGEDETECT_RISE и EDGEDETECT_FALL.
Добавляем следующие регистры:
Имя регистра | Смещение | Имя в драйвере | Описание |
---|---|---|---|
INTERRUPT_EN | 0x01 | VIRTUAL_GPIO_INT_EN | Включает прерывания по заданному входу |
INTERRUPT_ST | 0x02 | VIRTUAL_GPIO_INT_ST | Регистр состояния прерывания |
INTERRUPT_EOI | 0x03 | VIRTUAL_GPIO_INT_EOI | Регистр для оповещения об обработанном прерывании |
EDGEDETECT_RISE | 0x04 | VIRTUAL_GPIO_RISING | Включение/выключения прерывания для входа по переднему фронту |
EDGEDETECT_FALL | 0x05 | VIRTUAL_GPIO_FALLING | Включение/выключения прерывания для входа по заднему фронту |
LEVELDETECT_HIGH | NC | NOT CONNECTED | |
LEVELDETECT_LOW | NC | NOT CONNECTED |
За обработку прерывания от pci шины отвечает следующая функция, на данный момент её роль заключается всего лишь в уведомлении об обработанном прерывании:
irq_chip и концепция chained_interrupt
На данный момент для нас является главным тот факт, что порты GPIO предоставляющие прерывания каскадируемые от родительского контроллера прерываний обычная практика в дни современного линукса.
Вот почему часть драйвера GPIO отвечающего за прерывания использует irq_chip. Другими словами такой драйвер использует две подсистемы одновременно: gpio_chip и irq_chip.
Беглый взгляд на подсистему irq дает нам следующую картину:
High-Level Interrupt Service Routines (ISRs) — Выполняет всю необходимую работу по обслуживанию прерывания на драйвере устройства. Например, если прерывание используется для индикации доступных для чтения новых данных, работа ISR будет заключаться в копировании данных в соответствующее место.
Interrupt Flow Handling — Данная подсистема отвечает за особенности в реализации обработок прерываний, таких как срабатывание по уровню сигнала (level) или по фронту (edge).
Срабатывание по фронту (Edge-triggering) происходит при определении, что на линии произошло изменение потенциала. Срабатывание по уровню (Level-triggering), определяется как определенное значение потенциала, при этом изменение потенциала не играет роли.
С точки зрения ядра, срабатывание по уровню более сложный случай, так как, после в начале каждого прерывания его необходимо маскировать.
Chip-Level Hardware Encapsulation — Используется для инкапсуляции особенностей реализации работы с аппаратной частью. Данную подсистему можно рассматривать как разновидность “драйвера устройства” для контроллеров прерываний.
Как мы видим ядро берет на себя управление обработкой цепочки прерывания и разницу в реализации типов (по фронту и по уровню), если предоставить соответствующую инфраструктуру.
IRQ Domains
Подсистема IRQ Domain появившееся в патче irq: add irq_domain translation infrastructure позволила отделить локальные для контроллера номера прерываний от номеров прерываний в ядре, предоставив общий массив номеров прерываний. Цитируя официальную документацию: «Сегодня номер IRQ, это просто номер».
До данного обновления аппаратные номера отображались на номерами ядра как 1:1, а каскадирование не поддерживалось. Под аппаратными номерами, понимается локальные для контроллера номера прерывания, которые в нашем случае совпадают с локальными номерами GPIO.
Поскольку наш вектор прерываний достаточно мал, и у нас точно нет интереса в «No map» отображении, наше отображение линейно, фактически номера сопоставляются 1:1 со смещением, разница со старым подходом состоит в том что за присвоение номеров irq и за вычисление смещения отвечает ядро, при этом гарантируется непрерывность выделяемого диапазона.
В каждую функцию интерфейса irq_chip передается указатель на структуру struct irq_data, где irq_data->irq это номер прерывания в ядре linux, a irq_data->hwirq это наш локальный номер прерывания в рамках драйвера. Так же в struct irq_data передается указатель на нашу структуру struct virtual_gpio, что неудивительно.
Связывание irq_chip и gpio_chip
Если бы мы ориентировались на более младшие версии ядра, нам пришлось бы воспользоваться функцией irq_domain_add_simple для отображения наших номер, но с версии 3.15 в патче gpio: add IRQ chip helpers in gpiolib patch нет необходимости напрямую использовать интерфейс IRQ Domain.
Поэтому вместо прямого использования интерфейса IRQ Domain и предоставления инфраструктуры для отображения локальных номеров на глобальные (.map() ops), мы воспользуемся функциями gpiochip_irqchip_add и gpiochip_set_chained_irqchip (зависят от параметра GPIOLIB_IRQCHIP Kconfig).
Прекрасным примером использования и простоты в применении, является драйвер gpio-pl061.
Привязываем наш irq_chip к уже существующему gpio_chip:
handle_edge_irq — это один из встроенных обработчиков потока, который берет на себя управление цепочкой прерывания по фронтам.
Примечание: прерывания по фронтам является наиболее распространенным. Главное отличие от прерываний по уровню заключается как раз в управлении цепочкой, прерывание по уровню маскируется в ядре сразу после получения.
Вызовом функции gpiochip_set_chained_irqchip мы сообщаем ядру, что наш irq_chip использует прерывание от PCI шины и наши прерывания каскадируются от pdev->irq.
Доработаем наш обработчик, чтобы он генерировал прерывания в зависимости от состояния VIRTUAL_GPIO_INT_ST:
irq_find_mapping — вспомогательная функция для трансляции локального номера входа в глобальный номер прерывания.
Собираем все вместе
Прежде всего, отметим, что интерфейс irq_chip нашего драйвера, выглядит следующим образом:
Функция ack() всегда тесна связана с аппаратной спецификой контроллера. Некоторым устройствам, например требуется подтверждение обработки запроса прерывания, прежде чем могут быть обслужены последующие запросы.
В нашем случае в программе vg_get_set – используется достаточно грубая эмуляция регистра eoi. После выставления флага статуса прерывания, в цикле постоянно опрашивается eoi регистр. Когда бит входа уведомления о прерывании выставляется драйвером, происходит обнуление регистра eoi и снятие бита статуса прерывания на входе.
Маскирование и демаскирование производится записью соответствующего значения в регистр INTERRUPT_EN.
irq_type позволяет задать тип триггера — на текущий момент в ядре определены следующие типы:
IRQ_TYPE_NONE — тип не задан
IRQ_TYPE_EDGE_RISING — по переднему фронту
IRQ_TYPE_EDGE_FALLING — по заднему фронту
IRQ_TYPE_EDGE_BOTH — по переднему и заднему фронту
IRQ_TYPE_LEVEL_HIGH — по высокому уровню
IRQ_TYPE_LEVEL_LOW — по низкому уровню
Тестирование и результаты
Для тестирования передачи информации о прерываниях в user space, воспользуемся специально написанной утилитой vg_guest_client. Согласно документации по gpio_sysfs, “Если вы используете select для отслеживания событий, задайте файловый дескриптор (входа) в exceptfds”.
Подготавливаем входы к работе при помощи sysfs:
Примечание: gpio на подавляющем большинстве устройств по умолчанию инициализируются как входы.
Цепочка вызовов от нашего обработчика прерывания к уведомлению pselect:
Заключение
Данная статья подразумевалась мной, как базовая для материала, который сложно, или даже невозможно, представить без какого-либо общего вступления. Qemu в паре с ivshmem послужили отличным и понятным базисом для этой цели. Причиной выбора этой конкретной связки является наличие вменяемой документации и прозрачности использования.
Сама работа с gpio sysfs ничем не отличается для любых устройств с реализованной поддержкой sysfs, любая инструкция по использованию GPIO может быть успешно применена к другому подобному устройству, как и задумывалось при разработке данного интерфейса. Все различия заканчиваются на уровне конкретного драйвера устройства.
Сам драйвер, несмотря на безусловную образовательную ценность, далек от идеала в контексте современного ядра. Для подобного простого драйвера стоит использовать generic-gpio драйвер, созданный, чтобы избежать похожего, повторяющегося кода для mmio gpio драйверов, использование которого, правда, не так очевидно. Обработку прерываний можно было бы сделать более элегантной, а значения смещений регистров лучше хранить в структуре драйвера.
Так же нельзя упускать из виду последние изменения в gpiolib — sysfs gpio теперь является устаревшей. Новый основанный на ioctl интерфейс для gpiolib на пути становления как новый стандарт для общения с GPIO. Но младшие версии еще долго будут использоваться, к тому же никто не собирается на данный момент убирать из ядра старый интерфейс. У меня например до сих пор есть устройства успешно работающие на версии ядра 2.6.34.