【转】__raw_readl和__raw_writel 2012-01-22 16:55 micro2440采用S3C2440处理器(和S3C2410区别不大),在其Linux源码中,和这个平台相关的代码主要在arch/arm/mach-s3c2410和include/asm-arm/arch-s3c2410中,相关驱动在drivers目录中。 (1)DM9000 网卡驱动 1.S3C2410_GPB5是端口编号,定义在regs-gpio.h中, #define S3C2410_GPIO_BANKB (32*1) S3C2410 共有130个GPIO,分为9组(GPA~GPJ),每组最多可以有32个,每个GPIO有2~4个可选功能,每组的控制寄存器空间有4个,例如对于 GPB,有GPBCON、GPBDAT、GPBUP和Reserved,分别是功能配置、数据缓存、上拉使能和保留。 上面的S3C2410_GPB5就是GPIO的编号,也就是在号码空间(0~32*9-1)中的位置,bank是分组的基号码,offset是组内偏移量。 2.S3C2410_GPB5_OUTP是端口功能,定义在regs-gpio.h中, #define S3C2410_GPB5_INP (0x00 << 10) GPBCON的第10、11两位用于配置GPB5的功能,00 = Input ,01 = Output 3.S3C2410 GPIO的操作函数 在hardware.h文件中有: s3c2410_gpio_getirq //给定端口,转换出IRQ号 s3c2410_gpio_setpin //写数据到端口 这些函数的实现在gpio.h中 void s3c2410_gpio_setpin(unsigned int pin, unsigned int to) local_irq_save(flags); dat = __raw_readl(base + 0x04); local_irq_restore(flags); (自 己的理解:dat = __raw_readl(base + 0x04); dat &= ~(1 << offs); dat |= to << offs;譬如操作的是端口5,这几句能保证其他的端口保持原值,而仅仅是端口5值改变) 4.S3C2410_GPIO_BASE和S3C2410_GPIO_OFFSET也是在regs-gpio.h文件中定义, #define S3C2410_GPIO_BASE(pin) ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO) 而在map.h中有:
S3C2410_GPIO_BASE作 用是:根据端口编号pin,算出端口所在组的虚拟基址。((pin) & ~31)是去掉pin当中小于等于31的零头(清0低5位),>>1的原因是每组GPIO中最多可以有32个端口,控制这些端口需要4个寄存 器空间,4个寄存器空间就需要4*4=16个字节进行编址,32/16=2,左移一位刚好满足。也就是说,上一组端口和下一组端口的编号相差32,而控制 寄存器的地址相差16。(自己的理解:因为每个GPIO口对应4个寄存器,每个寄存器32位,S3C2410_GPIO_BASE仅仅是算出虚拟基址,而 不管是哪个具体端口,2的5次方正好是32,((pin) & ~31)可以屏蔽掉低五位,右移一位是因为端口数不超过32,每个GPIO口对应4个寄存器,每个寄存器32位,4*32/8=16=0x10,例如GPIOA=0X10+S3C24XX_VA_GPIO ,GPIOB=0X20+S3C24XX_VA_GPIO,0x10所表示的数的个数超过32) S3C2410_GPIO_OFFSET作用是:根据端口编号pin,算出端口所在组的偏移量。((pin) & 31)即去掉比31大的数(清0第6位以上的位)。 5. __raw_readl和__raw_writel Linux对I/O的操作都定义在asm/io.h中,相应的在arm平台下,就在asm-arm/io.h中。 #define __raw_readl(a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a)) 在include\linux\compiler.h中: #ifdef __CHECKER__ __raw_readl(a) 展开是:((void)0, *(volatile unsigned int _force *)(a))。在定义了__CHECKER__的时候先调用__chk_io_ptr检查该地址,否则__chk_io_ptr什么也不做,* (volatile unsigned int _force *)(a)就是返回地址为a处的值。(void)xx的做法有时候是有用的,例如编译器打开了检查未使用的参数的时候需要将没有用到的参数这么弄一下才能 编译通过。 CPU对I/O的物理地址的编程方式有两种:一种是I/O映射,一种是内存映射。__raw_readl和 __raw_writel等是原始的操作I/O的方法,由此派生出来的操作方法有:inb、outb、_memcpy_fromio、readb、 writeb、ioread8、iowrite8等。 6.local_irq_save和local_irq_restore 关中断和开中断,在asm-arm/system.h中定义。 #define local_irq_save(x) \ #define local_irq_save(x) \ |
|