一. 简介
在前面的裸机开发实验 LED灯实验中 ,其实就是操作 IMX6ULL芯片的寄存器。
Linux 驱动开发也可以操作寄存器,但是,Linux不能直接对寄存器物理地址进行读写操作,例如,寄存器 A的物理地址为 0X01010101。 裸机开发时可以直接对 0X01010101这个物理地址进行操作,但是,Linux 开发则不行。因为 Linux会使能 MMU。
MMU 全称叫做 Memory Manage Unit,也就是内存管理单元。在老版本的 Linux 中要求处理器必须有 MMU,但是现在 Linux 内核已经支持无 MMU 的处理器了。
二. Linux系统中的地址映射
1. MMU(即内存管理单元)
MMU也就是内存管理单元。主要完成的功能如下:
① 完成虚拟空间到物理空间的映射。
② 内存保护,设置存储器的访问权限,设置虚拟存储空间的缓冲特性。
地址映射
我们重点来看一下第 ① 点,也就是虚拟空间到物理空间的映射,也叫做地址映射。
首先了
解两个地址概念:虚拟地址 (VA,Virtual Address) 、物理地址 (PA , PhyscicalAddress) 。对于 32 位
的处理器来说,虚拟地址范围是 2^32=4GB ,我所使用的开发板DDR3的容量是 256MB ,这 256MB 的 内存就是物理内存,经过 MMU 可以将其映射到整个 4GB 的虚拟空间。
注意:不单单是 DDR,外设的寄存器的地址也是物理地址!!
如下图 所示:
物理内存只有 512MB ,虚拟内存有 4GB ,那么肯定存在多个虚拟地址映射到同一个物理地址上去,虚拟地址范围比物理地址范围大的问题处理器自会处理,这里我们不要去深究,因为 MMU 是很复杂的一个东西。
2. 内存映射涉及函数
Linux 内核启动的时候会初始化 MMU ,设置好内存映射,设置好以后 CPU 访问的都是虚
拟 地 址 。
例如, I.MX6ULL 的 GPIO1_IO03 引 脚 的 复 用 寄 存 器 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 的地址为 0X020E0068 。如果没有开启 MMU 的话, 直接向 0X020E0068 这个寄存器地址写入数据就可以配置 GPIO1_IO03 的复用功能。
现在开启 了 MMU ,并且设置了内存映射,因此,就不能直接向 0X020E0068 这个地址写入数据了。我们必 须得到 0X020E0068 这个物理地址在 Linux 系统里面对应的虚拟地址。
这里就涉及到了物理内 存和虚拟内存之间的转换,需要用到两个函数: ioremap()函数 和 iounmap 函数。
ioremap 函数
ioremap 函 数 用 于 获 取 指 定 物 理 地 址 空 间 对 应 的 虚 拟 地 址 空 间 , 定 义 在
arch/arm/include/asm/io.h 文件中,定义如下:
#define ioremap(cookie,size) __arm_ioremap((cookie), (size),
MT_DEVICE)void __iomem * __arm_ioremap(phys_addr_t phys_addr, size_t size,
unsigned int mtype)
{return arch_ioremap_caller(phys_addr, size, mtype,__builtin_return_address(0));
}
ioremap 是个宏,有两个参数: cookie 和 size ,真正起作用的是函数 __arm_ioremap ,此函
数有三个参数和一个返回值,这些参数和返回值的含义如下:
phys_addr :要映射给的物理起始地址。
size :要映射的内存空间大小。
mtype : ioremap 的类型,可以选择 MT_DEVICE 、 MT_DEVICE_NONSHARED 、
MT_DEVICE_CACHED 和 MT_DEVICE_WC , ioremap 函数选择 MT_DEVICE 。
返回值: __iomem 类型的指针,指向映射后的虚拟空间首地址。
iounmap 函数
卸载驱动时,需要使用 iounmap 函数释放掉 ioremap 函数所做的映射, iounmap 函数原
型如下:
void iounmap (volatile void __iomem *addr)
iounmap 只有一个参数 addr ,此参数就是要取消映射的虚拟地址空间首地址。
三. 举例说明
假如,我们要获取 I.MX6ULL 的 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 寄存器对应
的虚拟地址,使用如下代码即可:
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
static void __iomem* SW_MUX_GPIO1_IO03;
SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
宏 SW_MUX_GPIO1_IO03_BASE 是寄存器物理地址, SW_MUX_GPIO1_IO03 是映射后
的虚拟地址。对于 I.MX6ULL 来说一个寄存器是 4 字节 (32 位 ) 的,因此映射的内存长度为 4 。
映射完成以后直接对 SW_MUX_GPIO1_IO03 进行读写操作即可。
假如,我们现在要取消掉 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 寄存器的地址映射,使用如下代码 即可:
iounmap(SW_MUX_GPIO1_IO03);
这里了解 Linux地址映射,主要为 LED灯驱动开发准备。