一. 简介
在前面的裸机开发实验 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灯驱动开发准备。