Skip to content

Day74 - MMIO Register Access

Objective

Understand how Linux platform drivers access hardware registers through Memory-Mapped I/O (MMIO).

This lab follows the complete path from Device Tree resources to register access patterns commonly used in Linux drivers.


Lab Environment

Platform:

Raspberry Pi 5
Kernel 6.12.x
Debian 13

Driver:

demo_mmio_resource

Lab1 - MMIO Resource Inspection

Device Tree

demo_mmio: demo-mmio@10000000 {
    compatible = "demo,mmio-resource";
    reg = <0x0 0x10000000 0x1000>;
};

Resource Retrieval

res = platform_get_resource(
        pdev,
        IORESOURCE_MEM,
        0);

Result

start=0x10000000
end=0x10000fff
size=0x1000
flags=0x200

Verification

Device Tree reg
struct resource

Lab2 - devm_ioremap_resource()

Test

base = devm_ioremap_resource(
            dev,
            res);

Result

failed to ioremap MMIO resource: -EBUSY

Observation

devm_ioremap_resource()

=
request_mem_region()
+
ioremap()
+
devm cleanup

The helper performs ownership checks before mapping.


Lab3 - Real MMIO Resource Inspection

Device Tree Nodes

gpio@7d508500
gpio@7d517c00

pinctrl@7d504100
pinctrl@7d510700

Device Tree Reg

gpio@7d508500
base = 0x7d508500
size = 0x40

/proc/iomem

107d508500-107d50853f

Observation

Device Tree address
!=
CPU physical address

Address translation occurs through Device Tree bus mappings.


Lab4 - Device Tree ranges Translation

Inspection

hexdump -Cv /proc/device-tree/soc@107c000000/ranges

Result

child address = 0x00000000

parent physical address
= 0x0000001000000000

size
= 0x80000000

Translation

CPU physical address

=
0x1000000000
+
bus address

Example:

7d508500
107d508500

Verification

reg
ranges
resource
/proc/iomem

Lab5 - __iomem

Test

sizeof(void *)
sizeof(void __iomem *)

Result

8
8

Observation

__iomem

is an address-space annotation used by kernel tooling.

It does not change pointer size.


Lab6 - Register Offset Model

Register Layout

#define DEMO_REG_CTRL        0x00
#define DEMO_REG_STATUS      0x04
#define DEMO_REG_DATA        0x08
#define DEMO_REG_IRQ_STATUS  0x0c

Observation

Base Address
+
Offset
=
Register

Lab7 - Simulated Register Access

Register Block

struct demo_regs {
    u32 ctrl;
    u32 status;
    u32 data;
    u32 irq_status;
};

Helper Functions

static u32 demo_readl(void *addr)
{
    return *(u32 *)addr;
}

static void demo_writel(u32 val, void *addr)
{
    *(u32 *)addr = val;
}

Result

STATUS=0x22
CTRL=0x12345678

Verification

Register Access

=
Base
+
Offset

Lab8 - Bit Operations

Definitions

#define CTRL_ENABLE    BIT(0)
#define CTRL_IRQ_EN    BIT(1)
#define CTRL_RESET     BIT(2)
#define CTRL_DMA_EN    BIT(3)

Initial Value

CTRL = 0x0A

Binary:

1010

Enable Bit

ctrl |= CTRL_ENABLE;

Result:

0x0B

Disable Bit

ctrl &= ~CTRL_DMA_EN;

Result:

0x03

Verification

Read
Modify
Write

pattern.


Lab9 - Register Field Operations

Field Definitions

#define CTRL_MODE_MASK    GENMASK(1, 0)
#define CTRL_DIV_MASK     GENMASK(4, 2)
#define CTRL_DRV_MASK     GENMASK(7, 5)

Write Fields

reg |= FIELD_PREP(CTRL_MODE_MASK, 2);
reg |= FIELD_PREP(CTRL_DIV_MASK, 5);
reg |= FIELD_PREP(CTRL_DRV_MASK, 3);

Read Fields

FIELD_GET(CTRL_MODE_MASK, reg);
FIELD_GET(CTRL_DIV_MASK, reg);
FIELD_GET(CTRL_DRV_MASK, reg);

Result

MODE=2
DIV=5
DRV=3

Verification

GENMASK()
FIELD_PREP()
FIELD_GET()

Key Learnings

Device Tree reg
platform_get_resource()
struct resource
MMIO Resource
__iomem
Register Offset
readl()/writel()
BIT()
Read-Modify-Write
GENMASK()/FIELD_PREP()/FIELD_GET()

This workflow forms the foundation of most Linux platform drivers.