Skip to content

MMIO Access APIs

Overview

After a device's physical registers are mapped into the kernel virtual address space using ioremap(), Linux drivers access the registers through dedicated MMIO access APIs.

These APIs provide a portable and architecture-independent interface for accessing memory-mapped I/O (MMIO) registers.

The most commonly used APIs are:

  • readb()
  • readw()
  • readl()
  • writeb()
  • writew()
  • writel()

Why Not Dereference Pointers Directly?

Although MMIO registers appear as memory addresses after ioremap(), they are not normal RAM.

Instead of writing:

*(uint32_t *)addr = value;

Linux drivers use:

writel(value, addr);

Likewise, instead of:

value = *(uint32_t *)addr;

drivers use:

value = readl(addr);

Using the MMIO access APIs allows the kernel to provide the correct implementation for different CPU architectures and I/O memory models.


MMIO Access Workflow

A typical Linux driver accesses device registers as follows:

Physical Register Address
      ioremap()
Virtual MMIO Address
readb()/readw()/readl()
writeb()/writew()/writel()
Hardware Registers

The MMIO access APIs are used after the register region has been mapped by ioremap().


Register Access Using Offsets

Device registers are usually defined as offsets from a base address.

#define REG_CTRL      0x00
#define REG_STATUS    0x04
#define REG_DATA      0x08

Example:

void __iomem *base;

base = ioremap(PHYS_BASE, SIZE);

writel(CTRL_ENABLE, base + REG_CTRL);

status = readl(base + REG_STATUS);

data = readl(base + REG_DATA);

This is the programming model used by most Linux platform drivers.


Register Width

Different devices expose registers with different widths.

API Register Width
readb() / writeb() 8-bit
readw() / writew() 16-bit
readl() / writel() 32-bit

The register width must match the hardware specification.


Endianness

On Little Endian systems such as Raspberry Pi, ARM64, and x86, a 32-bit register value is stored as:

Value

0x12345678

Memory

+0  78
+1  56
+2  34
+3  12

The MMIO access APIs handle register accesses according to the architecture's I/O memory model.


Relationship with ioremap()

ioremap() and the MMIO access APIs serve different purposes.

API Purpose
ioremap() Map a physical register region into the kernel virtual address space
iounmap() Remove the mapping
readb() / readw() / readl() Read MMIO registers
writeb() / writew() / writel() Write MMIO registers

Both are required for accessing memory-mapped hardware devices.


Summary

Linux drivers access memory-mapped device registers through dedicated MMIO access APIs rather than directly dereferencing pointers.

The complete workflow is:

Physical Address
ioremap()
Virtual MMIO Address
readb()/readw()/readl()
writeb()/writew()/writel()
Hardware Registers

Understanding this workflow is fundamental to writing Linux platform drivers and other MMIO-based kernel drivers.