ioremap()
Posted Li-Yongjun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ioremap()相关的知识,希望对你有一定的参考价值。
活动地址:CSDN21天学习挑战赛
前言
之前,我们使用 devmem
命令的方式,直接操作寄存器,控制 LED 的亮灭。
今天,我们在内核中使用 ioremap() 来实现该功能。
物理地址 vs 虚拟地址
在嵌入式 Linux 中,通常会将外设的寄存器地址,映射到物理内存地址上,方便统一管理。
但是,不管是内核空间代码,还是用户空间代码,访问的都是虚拟地址
*(int *)0x87800000 = 100;
上述代码,将地址为 0x87800000 的内存赋值 100。但是,你需要知道的是,这个 0x87800000 地址,是个虚拟内存地址,它和实际物理内存地址 0x87800000 没有半毛钱关系。虚拟地址 0x87800000 会经过 mmc 映射到物理地址上,这个物理地址是多少,我们不用关心,也无法左右,可能是 0x00008888,也可能是 0x77776666,都不影响程序运行。
如果我们想要人为地和某个物理地址有联系该怎么办呢?比如说想要读写某一寄存器。这时候就要用到 ioremap() 了。
ioremap()
Linux 在 asm/io.h 中声明了 ioremap 函数,用来将 I/O 内存资源的物理地址映射到核心虚拟地址空间(内核空间:3GB~4GB)中,原型如下
#define ioremap(offset, size) __ioremap((offset), (size), 0)
void __iomem *__ioremap(unsigned long phys_addr, size_t size, unsigned long flags);
参数:
phys_addr:要映射的起始的 I/O 地址
size:要映射的空间的大小
flags:要映射的 I/O 空间和权限有关的标志
返回值:
返回映射后的内核虚拟地址(3G-4G)
接着便可以通过读写该返回的内核虚拟地址去访问这段 I/O 内存资源。
示例
led_drv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <asm/io.h>
#define GPIO_CLR_REG ((__u32)0x3f200028)
#define GPIO_SET_REG ((__u32)0x3f20001C)
static volatile __u32 *gpio_led_reg;
static int led_init(void)
printk("led on\\n");
gpio_led_reg = (__u32 *)ioremap(GPIO_CLR_REG, 4);
writel(0x7F, gpio_led_reg);
return 0;
static void led_exit(void)
printk("led off\\n");
gpio_led_reg = (__u32 *)ioremap(GPIO_SET_REG, 4);
writel(0x7F, gpio_led_reg);
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("Dual BSD/GPL");
Makefile
obj-m = led_drv.o
KDIR=/home/liyongjun/project/board/buildroot/RPi3/build/linux-custom
CROSS_COMPILE=/home/liyongjun/project/board/buildroot/RPi3/host/bin/arm-buildroot-linux-uclibcgnueabihf-
all:
make -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) modules
clean:
make -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) clean
运行
编译后将 led_drv.ko 上传到树莓派,在树莓派上安装、卸载。
# insmod led_drv.ko
[ 3168.850425] led on
# rmmod led_drv.ko
[ 3242.458328] led off
以上是关于ioremap()的主要内容,如果未能解决你的问题,请参考以下文章