linux设备寄存器映射,Davinci平台下linux中寄存器定义和映射的探究

近段时间在TI的DM6467平台上搞起了linux系统下的ARM开发,前几日弄了个SPI的驱动程序,里面对SPI寄存器操作时直接定义了一个寄存器组的结构体:

//定义SPI寄存器结构体

struct davinci_spi_reg {

volatile __u32 __bitwise

SPIGCR0;

volatile __u32 __bitwise

SPIGCR1;

volatile __u32 __bitwise

SPIINT;

volatile __u32 __bitwise

SPILVL;

volatile __u32 __bitwise

SPIFLG;

volatile __u32 __bitwise

SPIPC0;

volatile __u32 __bitwise

SPIPC1;

volatile __u32 __bitwise

SPIPC2;

volatile __u32 __bitwise

SPIPC3;

volatile __u32 __bitwise

SPIPC4;

volatile __u32 __bitwise

SPIPC5;

volatile __u32 __bitwise

SPIPC6;

volatile __u32 __bitwise

SPIPC7;

volatile __u32 __bitwise

SPIPC8;

volatile __u32 __bitwise

SPIDAT0;

volatile __u32 __bitwise

SPIDAT1;

volatile __u32 __bitwise

SPIBUF;

volatile __u32 __bitwise

SPIEMU;

volatile __u32 __bitwise

SPIDELAY;

volatile __u32 __bitwise

SPIDEF;

volatile __u32 __bitwise

SPIFMT[4];

volatile __u32 __bitwise

TGINTVEC[2];

volatile __u8 __bitwise

RSVD0[8];

volatile __u32 __bitwise

MIBSPIE;

};

之后对寄存器进行操作时就直接调用结构体成员进行赋值:

spi_reg_davinci->SPIDELAY =

0

| (

8 << 24

) // C2TDELAY

| (

8 << 16 ); //

T2CDELAY

起初对这种寄存器的操作方式没有想太多,感觉这些寄存器名应该有一个宏定义对其地址预先进行了定义。由于我之前是从C8051F120单片机转入的ARM开发,所以感觉应该也有一个像c8051f120.h这样的头文件来定义寄存器,另一方面,在网上查找了一些资料,在一些说到三星的S3C2410的文章里面也发现了类似单片机的寄存器定义的内容,于是更加坚定了我的想法。然而在网上搜索了一大圈也没发现davinci平台中的寄存器定义的头文件,很是郁闷。

终于在查找get_clk_rate()函数时偶然的找到了突破点。为了寻找platform_device结构体的内容,又把驱动的初始化和注册部分看了下,突然发现一条语句:

其中已经在之前定义了#define

DAVINCI_SPI_BASE (0x01C66800),也就是SPI相关寄存器的基地址。ioremap()函数是将物理地址映射为内核可以操作的虚拟地址,查了一下该函数的定义:

static inline void __iomem * ioremap(unsigned long

offset, unsigned long size)

{

return __ioremap(offset, size, 0);

}

可以看出ioremap()函数的返回值是一个地址,上面这条语句就是将SPI寄存器物理地址的首地址通过ioremap()函数映射后获得的虚拟地址作为SPI寄存器组结构体的起始地址,那么该结构体中对应的各成员的地址也就确定了,就可以对各成员进行操作了,对成员的操作也就实现了对寄存器的操作。

注意到(struct davinci_spi_reg *)ioremap(DAVINCI_SPI_BASE,200)其中的(struct davinci_spi_reg *),这是对ioremap()函数的返回值进行类型强制转换。因为ioremap()函数的返回值是(void __iomem *)类型,而spi_reg_davinci结构体是(struct davinci_spi_reg *)类型的,虽然两者对应的都是地址指针,但是编译时可能会有警告,而严谨的程序员是不容许出现警告的,这样的类型转换正体现了程序的严谨。

那么这里为什么要这样来操作寄存器,而不是像

#define

I2C_BASE 0x1c21000

#define

I2C_OAR *( volatile Uint32* )( I2C_BASE + 0x00 )

这样来定义寄存器呢?在网上查找到了以下内容:

--------为了使软件访问I/O内存,必须为设备分配虚拟地址.这就是ioremap的工作.这个函数专门用来为I/O内存区域分配虚拟地址(空间).对于直接映射的I/O地址ioremap不做任何事情(uClinux中是这么实现的??)有了ioremap(和iounmap),设备就可以访问任何I/O内存空间,不论它是否直接映射到虚拟地址空间.但是,这些地址永远不能直接使用(指物理地址),而要用readb这种函数.根据计算机平台和所使用总线的不同,I/O 内存可能是,也可能不是通过页表访问的,通过页表访问的是统一编址(PowerPC),否则是独立编址(Intel)。如果访问是经由页表进行的,内核必须首先安排物理地址使其对设备驱动

程序可见(这通常意味着在进行任何 I/O 之前必须先调用ioremap)。如果访问无需页表,那么

I/O 内存区域就很象 I/O 端口,可以使 用适当形式的函数读写它们。不管访问

I/O 内存时是否需要调用ioremap,都不鼓励直接使用指向

I/O 内存的指针。尽管(在“I/O 端口和 I/O 内存” 介绍过)I/O 内存在硬件一级是象普通 RAM 一样寻址的,但在“I/O 寄存器和常规内存”中描述过的那些需要额外小心的情况中已经建议不要使用普

通指针。相反,使用“包装的”函数访问

I/O 内存,一方面在所有平台上都是安全的,另一方面,在可以直接对指针指向的内存区域执行操作的时候,该函数是经过优化的。

看了上面的文字后,我认为使用ioremap()是为了满足程序的通用性和更好的安全性,同时也找到了关于__iomem的叙述:

__iomem是2.6.9中加入的特性。是用来个表示指会指向一个I/O的内存空间。主要是为了driver的通用性考虑。由于不同的CPU体系结构对I/O空间的表示可能不同。当使用__iomem时,compiler会忽略对变量的检查(因为用的是void __iomem)。但sparse会对它进行检查,当__iomem的指针和正常的指针混用时,就会发出一些warnings。

到此为止,基本上是理解了linux系统中这种对寄存器的定义和操作的方法及原因,虽然走了一些弯路,但最终还是小有所得,困惑我几天的问题终于得以解决了。

若文中有错误或者不足之处还请指出和讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值