15. 5.14. MMIO (Memory-Mapped I/O) Device Access

15.1. Введение

MMIO (Memory-Mapped I/O) — это механизм доступа к аппаратным устройствам через адресное пространство памяти. Он позволяет обращаться к регистрам периферийных устройств (GPIO, UART, SPI и др.) точно так же, как к обычным переменным, если известен базовый физический адрес устройства и структура его регистров.

На Raspberry Pi это особенно актуально при работе с низкоуровневыми устройствами, не обёрнутыми в готовые драйверы.

—

15.2. Как это работает

  1. Аппаратные регистры устройств отображаются в память через физические адреса (например, 0x3F200000 для GPIO).

  2. Драйвер резервирует и отображает (remap) эти адреса в виртуальное пространство ядра с помощью ioremap().

  3. Доступ к значениям регистров производится через функции:

    • readl() / writel() — 32-битный доступ

    • readb() / writeb() — байтовый доступ

    • readw() / writew() — 16-битный доступ

  4. По завершении работы память освобождается функцией iounmap().

—

15.3. Пример использования MMIO

Простой пример драйвера, который включает/выключает GPIO21 напрямую через регистры:

#define GPIO_BASE_PHYS 0x3F200000
#define GPIO_LEN       0xB4

static void __iomem *gpio_base;

static int __init mmio_example_init(void)
{
    gpio_base = ioremap(GPIO_BASE_PHYS, GPIO_LEN);
    if (!gpio_base) {
        pr_err("Failed to map GPIO MMIO region\n");
        return -ENOMEM;
    }

    // Настройка GPIO21 как выход (GPFSEL2)
    u32 val = readl(gpio_base + 0x08);
    val &= ~(7 << 3);     // очистить биты FSEL21
    val |=  (1 << 3);     // установить как output (001)
    writel(val, gpio_base + 0x08);

    // Включаем GPIO21 (GPSET0)
    writel(1 << 21, gpio_base + 0x1C);

    return 0;
}

static void __exit mmio_example_exit(void)
{
    // Выключаем GPIO21 (GPCLR0)
    writel(1 << 21, gpio_base + 0x28);

    iounmap(gpio_base);
}

module_init(mmio_example_init);
module_exit(mmio_example_exit);

—

15.4. Важно

  • ioremap() возвращает виртуальный адрес, через который производится доступ

  • Нельзя просто использовать (volatile uint32_t *)0x3F200000 — это нарушает принципы безопасности ядра

  • Всегда вызывайте iounmap() при выгрузке модуля

  • Не забывайте про MODULE_LICENSE() и права доступа

—

15.5. Когда использовать MMIO

MMIO нужен, когда:

  • Нет стандартного драйвера для устройства

  • Требуется максимальная производительность

  • Вы реализуете прототип низкоуровневой периферии (например, на FPGA или нестандартной плате)

  • Нужно изучить поведение контроллера «вживую»

—

15.6. Вывод

MMIO — это мощный и универсальный способ взаимодействия с железом на Linux. Но он требует точного знания адресов, регистров и правильной работы с памятью ядра.

Мы будем использовать MMIO при разработке собственного драйвера для RGB LED и других устройств, где важно получить прямой доступ к битам конфигурации GPIO.