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