先看一个在无操作系统的情况下,用C语言访问片上寄存器的范例,这是访问S3C2440UART1的FIFO控制寄存器的示例,先定义FIFO控制寄存器为UFCON1:
#define UFCON1 (*(volatile unsigned *)Ox50004008) //*UART 1 FIFO控制寄存器
给UFCON1赋值:
UFCON1 = Ox00; //禁止FIFO功能
这个示例的使用条件是禁止CPU的MMU。在禁止MMU的情况下,可以直接访问CPU的物理地址。
Linux内核运行后,开启了MMU,所以不能直接访问CPU的物理地址,也就是说,不能直接使用物理地址访问系统的IO内存。必须将物理地址转换为虚拟地址,内核通过虚拟地址来访问系统的IO内存。
在内核中,物理地址到虚拟地址的转换,可以采用静态IO映射,还可以采用动态IO映射。通常情况下,CPU片上寄存器和内部总线都采用静态IO映射,外部总线扩展IO则通常采用动态IO映射,也可以添加到系统中,采用静态IO映射的方式。
下面分别来看这两种方式的实现和使用方法。
1、静态VO映射 、
静态I/O映射在内核中很常见,最常见的是处理器的片内寄存器的操作,如GPIO、串口、定时器等等这些片上外设的寄存器,在内核中都通过静态IO映射后被访问。一般的操作方式是这样的:
_raw_writel(camdivn, S3C2440_CAMDIVN);
submsk =_raw_readl(S3C2410_INTSUBMSK);
(1)io_p2v
要实现静态IO映射,首先需要定义物理地址到虚拟地址的转换规则,在内核中用宏定义io_p2v(x)实现,将物理地址映射到3G~4G的内核地址空间。不同处理器的具体实现是不同的,但是前提是必须能将处理器的全部有效IO空间映射到内核空间。对于一个32位的处理器,最大可访问地址空间为232,即4G,但是实际上绝大部分地址空间都是保留的,可访问的有效地址仅仅局限于有实际物理外设地址空间。
2、动态IO映射
动态IO映射无需将物理IO内存空间写入映射表,调用ioremap即可映射到虚拟地址空间。这种方式使用起来比较灵活,不过在外扩总线设备寄存器较多的情况下使用起来就不太方便了,一般建议在寄存器较少的情况下使用。操作完毕后,用iounmap取消IO映射。
ioremap 和 iounmap相关定义在<asm/io.h>文件中:
3、IO内存访问函数
__raw_readb/readb 从 I/O 端口读取 8 位数
__raw_readw/readw 从 I/O 端口读取 16 位数
__raw_readl/readl 从 I/O 端口读取 32 位数
__raw_writeb/writeb 往 I/O 端口写入 8 位数
__raw_writew/writew 往 I/O 端口写入 16 位数
__raw_writel/writel 往 I/O 端口写入 32 位数