一、地址映射

MMU 全称叫做 Memory Manage Unit,也就是内存管理单元。在老版本的 Linux 中要求处理器必须有 MMU,但是现在Linux 内核已经支持无 MMU 的处理器了。MMU主要功能:

①、完成虚拟空间到物理空间的映射。 
②、内存保护,设置存储器的访问权限,设置虚拟存储空间的缓冲特性。 

对于 32 位的处理器,虚拟地址范围是 2^32=4GB,我们的阿尔法开发板上有 512MB 的 DDR3,这 512MB 的内存就是物理内存(RAM),经过 MMU 可以将其映射到整个 4GB 的虚拟空间。虚拟地址范围比物理地址范围大的问题处理器自会处理,这里我们不要去深究。

不支持虚拟化计数器_驱动开发


二、物理内存与虚拟内存的转换

Linux 内核启动的时候会初始化 MMU,设置好内存映射,设置好以后 CPU 访问的都是虚拟地址。

1、ioremap 函数 

ioremap 函数用于获取指定物理地址空间对应的虚拟地址空间。

假如我们要获取 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); // 虚拟地址,对于 I.MX6ULL 来说一个寄存器是 4 字节(32 位)的,因此映射的内存长度为 4。

2、iounmap 函数 

卸载驱动的时候需要使用 iounmap 函数释放掉 ioremap 函数所做的映射。

iounmap 只有一个参数 addr,此参数就是要取消映射的虚拟地址空间首地址,即SW_MUX_GPIO1_IO03。

iounmap(SW_MUX_GPIO1_IO03);

三、I/O 内存访问函数

这里涉及到两个概念:I/O 端口和 I/O 内存。

  • I/O 端口: 当外部寄存器或内存映射到 IO 空间时,例如我们学习单片机的时候讲的 GPIO 引脚。
  • I/O 内存: 当外部寄存器或内存映射到内存空间时。

但是对于 ARM 来说没有 I/O 空间这个概念(ARM 处理器通过内存映射 I/O 来控制 I/O 设备,而不是使用独立的I/O地址空间),因此 ARM 体系下只有 I/O 内存(可以直接理解为内存)。

使用 ioremap 函数将寄存器的物理地址映射到虚拟地址以后,我们就可以直接通过指针访问这些地址,但是 Linux 内核不建议这么做,如果直接操作虚拟内存可能出现一些意想不到的问题,因此这里推荐使用提供的两个函数进行操作。

1、读操作函数

参数 addr 就是要读取写内存地址,返回值就是读取到的数据。

// 分别是1字节、2字节、4字节( 8bit、16bit 和 32bit )读操作
1 u8  readb(const volatile void __iomem *addr) 
2 u16 readw(const volatile void __iomem *addr) 
3 u32 readl(const volatile void __iomem *addr)

2、写操作函数 

参数 value 是要写入的数值,addr 是要写入的地址。

//  8bit、16bit 和 32bit 写操作
1 void writeb(u8 value,  volatile void __iomem *addr) 
2 void writew(u16 value, volatile void __iomem *addr) 
3 void writel(u32 value, volatile void __iomem *addr)