1 IO端口和IO内存区别
像x86系列处理器有两种地址空间:一种是内存地址空间,一种是端口地址空间,访问端口有单独的指令,例如in/out指令,而像ARM这类处理器则使用单一的地址空间,没有端口地址空间的概念。在硬件层,IO端口和IO内存并无本质区别,都是通过向地址总线和控制总线发送地址和控制信号,再通过数据总线读写数据。
2 使用IO端口
2.1 IO端口分配
IO端口分配使用request_region函数,该函数用于取得该IO端口的独占访问,头文件在linux/ioport.h中,函数原型如下:
struct resourse *request_region(unsigned long first, unsigned long n, const char *name);
first表示起始端口号,n表示端口数量。
如果分配成功,返回非NULL值,如果request_region返回NULL,表示不能使用这些IO端口(有可能被其它程序占用了)。
2.2 IO端口释放
如果不在使用该IO端口,则调用release_region函数释放该IO端口的控制,函数原型如下:
void release_region(unsigned long start, unsigned long n);
2.3 检查IO端口是否可用
int check_region(unsigned long first, unsigned long n);
如果该端口不可用,则返回错误码。Linux中并不赞成使用这个函数,因为该函数只是对IO端口做检查,并不对IO端口进行锁定,这样有可能其它程序也能使用这组IO端口,这是一个比较老的函数。
2.4 操作IO端口
这组函数与具体的体系架构有关,所以头文件在asm/io.h中
unsigned inb(unsigned port);
void outb(unsigned char byte, unsigned port);
8位(字节)读写函数。
unsigned inw(unsigned port);
void outw(unsigned short word, unsigned port);
16位读写函数。
unsigned inl(unsigned port);
void out(unsinged long word, unsinged port);
32位读写函数。
3 使用IO内存
3.1 IO内存分配
在使用之前,必须首先分配IO内存,需要包含头文件linux/ioport.h,函数原型如下:
struct resourse *request_mem_region(unsigned long start, unsigned long len, char *name);
表示从start开始分配len字节的内存区域,如果成功,返回非NULL指针,否则返回NULL,所有的IO内存分配情况可以从/proc/iomem中看到。
3.2 IO内存释放
不再使用这段内存时,需要调用release_mem_region释放,函数原型如下:
void release_mem_region(unsigned long start, unsigned long len);
同样也提供了一个比较老的函数,用来检查IO内存是否可用
int check_mem_region(unsigned long start, unsigned long len);
同check_region一样,它也是不安全的,应该避免使用。
3.3 IO内存映射和释放
通常,是不能直接访问IO内存的,需要对IO内存进行映射,映射是将物理地址映射成虚拟地址,函数原型如下:
void *ioremap(unsigned long phys_addr, unsinged long size);
void iounmap(void *addr);
要使用这些函数需要包含头文件asm/io.h
在ioremap映射之后,不应该直接使用,而使用内核提供的accessor函数。
3.4 访问IO内存
IO内存读
unsigned int ioread8(void *addr);
unsigned int ioread16(void *addr);
unsinged int ioread32(void *addr);
addr应该是从ioremap获得的地址,可能包含一个整数偏移量,返回值是从IO内存读取到的值。
IO内存写
void iowrite8(u8 value, void *addr);
void iowrite16(u16 value, void *addr);
void iowerite32(u32 value, void *addr);
连续读
如果需要连续读写一段IO内存,则使用如下函数:
void ioread8_rep(void *addr, void *buf, unsinged long count);
void ioread16_rep(void *addr, void *buf, unsinged long count);
void ioread32_rep(void *addr, void *buf, unsinged long count);
void iowrite8_rep(void *addr, const void *buf, unsinged long count);
void iowrite16_rep(void *addr, const void *buf, unsinged long count);
void iowrite32_rep(void *addr, const void *buf, unsinged long count);
上述函数从addr读取或写入count个值,注意,count以被写入的数据大小为单位表示,例如,ioread32从addr读取count个32位值到buf中。
块操作
void memset_io(void *addr, u8 value, unsigned int count);
void memcpy_fromio(void *dest, void *source, unsigned int count);
void memcpy_toio(void *dest, void *source, unsigned int count);
比较老的函数
unsigned readb(address);
unsigned readw(address);
unsigned readl(address);
void writeb(unsigned value, address);
void writew(unsigned value, address);
void writel(unsigned value, address);
4 实例
例如,s3c2410_wdt.c中部分代码如下:
352 wdt_mem = request_mem_region(res->start, size, pdev->name);
353 if (wdt_mem == NULL) {
354 dev_err(dev, "failed to get memory region\n");
355 ret = -ENOENT;
356 goto err_req;
357 }
358
359 wdt_base = ioremap(res->start, size);
360 if (wdt_base == NULL) {
361 dev_err(dev, "failed to ioremap() region\n");
362 ret = -EINVAL;
363 goto err_req;
364 }
首先是申请IO内存,然后是映射。
wdt_base定义如下:
80 static void __iomem *wdt_base;