现在在一些arm高级一点的处理器上配置寄存器的方式其实就是直接访问寄存器地址,可以简单的写成*(uint32_t *)(addr) = val。也可以通过readl()函数和writel()函数对一些地址进行读写。writel和readl,这两个函数实现在操作系统层,有内存保护的情况下,往一个寄存器或者内存地址写一个数据或者读数据。
1. writel()函数
原型
#include <linux/io.h>
void writel(unsigned char data , unsigned int addr )
功能
writel() 函数往内存映射的 I/O 空间上写入32位(4字节)数据。
参数
data:要写入的一个字节的数据.
addr:32位I/O 地址。
在内核中的实现
在kernel/io.c有
void writel(u32 b, volatile void __iomem *addr)
{
__raw_writel(b, addr);
mb();
}
这样一个writel函数的作用是向一个地址上写一个值,往下继续跟踪代码:__raw_writel(b, addr);(发现在同目录下)
void __raw_writel(u32 b, volatile void __iomem *addr)
{
IO_CONCAT(__IO_PREFIX,writel)(b, addr);
}
再往下跟踪 IO_CONCAT,在对应的io.h中的定义如下:
#define IO_CONCAT(a,b) _IO_CONCAT(a,b)
#define _IO_CONCAT(a,b) a ## _ ## b
跟踪__IO_PREFIX 定义如下
#undef __IO_PREFIX
#define __IO_PREFIX apecs
对于apsec,看看以下代码段(linux-2.6.28-rc4)
/include/asm/core_apecs.h
#undef __IO_PREFIX
#define __IO_PREFIX apecs
#define apecs_trivial_io_bw 0
#define apecs_trivial_io_lq 0
#define apecs_trivial_rw_bw 2
#define apecs_trivial_rw_lq 1
#define apecs_trivial_iounmap 1
#include <asm/io_trivial.h>
前往include/asm/io_trivial.h
__EXTERN_INLINE void
IO_CONCAT(__IO_PREFIX,writel)(u32 b, volatile void __iomem *a)
{
*(volatile u32 __force *)a = b;
}
就是最终通过*(volatile u32 __force *)a = b;来写入数据的。
如果在没有os,没有mmu的情况下,当开发板裸跑的时候,我们只需要一句话就一切ok:
*(unsigned long *)addr = value)
首先强制类型转换为 unsigned char * 类型指针,再取这个地址的值, 最后赋值。
2. readl()函数
原型
#include <linux/io.h>
unsigned char readl(unsigned int addr )
功能
readl() 函数从内存映射的 I/O 空间读取32位(4字节)数据。
参数
addr :I/O 地址。
返回值 : 从 I/O 空间读取的数值。
其他读写函数:
unsigned char readb (unsigned int addr ) //读1字节
unsigned short readw (unsigned int addr ) //读2字节
unsigned int readl (unsigned int addr ) //读四字节
void writeb (unsigned char data, unsigned short addr) //写1字节
void writew (unsigned short data, unsigned short addr) //写2字节
void writel (unsigned int data, unsigned short addr ) //写4字节