Using ioremap and readl/writel
When writing device drivers in C for Linux, you often need to access hardware registers directly. Many modern devices use memory-mapped I/O, where device registers are mapped into the system's physical address space. However, you cannot access these physical addresses directly from kernel code. Instead, you must map them into the kernel's virtual address space using the ioremap function. This mapping provides your driver with a pointer you can use to safely access device registers.
map_device_register.c
1234567891011121314151617181920212223242526272829303132333435#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/io.h> #define DEVICE_PHYS_ADDR 0xFE200000 #define DEVICE_REG_SIZE 0x1000 static void __iomem *device_base = NULL; static int __init map_device_init(void) { device_base = ioremap(DEVICE_PHYS_ADDR, DEVICE_REG_SIZE); if (!device_base) { pr_err("Failed to map device memory\n"); return -ENOMEM; } pr_info("Device memory mapped at %p\n", device_base); return 0; } static void __exit map_device_exit(void) { if (device_base) { iounmap(device_base); pr_info("Device memory unmapped\n"); } } module_init(map_device_init); module_exit(map_device_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("Example: Mapping device register with ioremap");
After mapping device memory, you must access the registers safely. Direct pointer dereferencing is not allowed for memory-mapped I/O regions in the kernel, as it can lead to undefined behavior. Instead, you use the readl and writel functions, which are designed to safely read and write 32-bit values to and from the mapped addresses. These functions ensure the correct ordering and semantics required by the hardware and the kernel, and they reference the pointer returned by ioremap, as shown in the previous code.
access_registers.c
1234567891011121314151617181920212223242526272829303132333435363738394041424344#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/io.h> #define DEVICE_PHYS_ADDR 0xFE200000 #define DEVICE_REG_SIZE 0x1000 #define DEVICE_REG_OFFSET 0x10 static void __iomem *device_base = NULL; static int __init access_registers_init(void) { u32 value; device_base = ioremap(DEVICE_PHYS_ADDR, DEVICE_REG_SIZE); if (!device_base) { pr_err("Failed to map device memory\n"); return -ENOMEM; } value = readl(device_base + DEVICE_REG_OFFSET); pr_info("Register value: 0x%08x\n", value); writel(0xA5A5A5A5, device_base + DEVICE_REG_OFFSET); pr_info("Wrote 0xA5A5A5A5 to register\n"); return 0; } static void __exit access_registers_exit(void) { if (device_base) { iounmap(device_base); pr_info("Device memory unmapped\n"); } } module_init(access_registers_init); module_exit(access_registers_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("Example: Using readl/writel to access device registers");
Thanks for your feedback!
Ask AI
Ask AI
Ask anything or try one of the suggested questions to begin our chat
Can you explain how to use `ioremap` in a device driver?
What are the differences between `readl`/`writel` and regular pointer access?
Are there any precautions to take when unmapping device memory?
Awesome!
Completion rate improved to 3.85
Using ioremap and readl/writel
Swipe to show menu
When writing device drivers in C for Linux, you often need to access hardware registers directly. Many modern devices use memory-mapped I/O, where device registers are mapped into the system's physical address space. However, you cannot access these physical addresses directly from kernel code. Instead, you must map them into the kernel's virtual address space using the ioremap function. This mapping provides your driver with a pointer you can use to safely access device registers.
map_device_register.c
1234567891011121314151617181920212223242526272829303132333435#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/io.h> #define DEVICE_PHYS_ADDR 0xFE200000 #define DEVICE_REG_SIZE 0x1000 static void __iomem *device_base = NULL; static int __init map_device_init(void) { device_base = ioremap(DEVICE_PHYS_ADDR, DEVICE_REG_SIZE); if (!device_base) { pr_err("Failed to map device memory\n"); return -ENOMEM; } pr_info("Device memory mapped at %p\n", device_base); return 0; } static void __exit map_device_exit(void) { if (device_base) { iounmap(device_base); pr_info("Device memory unmapped\n"); } } module_init(map_device_init); module_exit(map_device_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("Example: Mapping device register with ioremap");
After mapping device memory, you must access the registers safely. Direct pointer dereferencing is not allowed for memory-mapped I/O regions in the kernel, as it can lead to undefined behavior. Instead, you use the readl and writel functions, which are designed to safely read and write 32-bit values to and from the mapped addresses. These functions ensure the correct ordering and semantics required by the hardware and the kernel, and they reference the pointer returned by ioremap, as shown in the previous code.
access_registers.c
1234567891011121314151617181920212223242526272829303132333435363738394041424344#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/io.h> #define DEVICE_PHYS_ADDR 0xFE200000 #define DEVICE_REG_SIZE 0x1000 #define DEVICE_REG_OFFSET 0x10 static void __iomem *device_base = NULL; static int __init access_registers_init(void) { u32 value; device_base = ioremap(DEVICE_PHYS_ADDR, DEVICE_REG_SIZE); if (!device_base) { pr_err("Failed to map device memory\n"); return -ENOMEM; } value = readl(device_base + DEVICE_REG_OFFSET); pr_info("Register value: 0x%08x\n", value); writel(0xA5A5A5A5, device_base + DEVICE_REG_OFFSET); pr_info("Wrote 0xA5A5A5A5 to register\n"); return 0; } static void __exit access_registers_exit(void) { if (device_base) { iounmap(device_base); pr_info("Device memory unmapped\n"); } } module_init(access_registers_init); module_exit(access_registers_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("Example: Using readl/writel to access device registers");
Thanks for your feedback!