1.S3C2410_GPB5是端口編號,定義在regs-gpio.h中,
#define S3C2410_GPIO_BANKB (32*1)
#define S3C2410_GPIONO(bank,offset) ((bank) + (offset))
#define S3C2410_GPB5 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 5)
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是組內偏移量。(也就是說把所有的IO口從0開始進行統一的編號如:S3C2410_GPA0=0,S3C2410_GPA1=1,S3C2410_GPB0=32,S3C2410_GPC0=48等)
2.S3C2410_GPB5_OUTP是端口功能,定義在regs-gpio.h中,
#define S3C2410_GPB5_INP (0x00 << 10)
#define S3C2410_GPB5_OUTP (0x01 << 10)
GPBCON的第10、11兩位用於配置GPB5的功能,00 = Input ,01 = Output
3.S3C2410 GPIO的操作函數
在hardware.h文件中有:
s3c2410_gpio_cfgpin //配置端口的GPIO的功能
s3c2410_gpio_getcfg //讀取功能配置
s3c2410_gpio_pullup //配置上拉電阻
s3c2410_modify_misccr //雜項配置
s3c2410_gpio_getirq //給定端口,轉換出IRQ號
s3c2410_gpio_irqfilter //配置IRQ過濾使能與否
s3c2410_gpio_setpin //寫數據到端口
s3c2410_gpio_getpin //從端口讀數據
這些函數的實現在gpio.h中
void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
{
void __iomem *base = S3C2410_GPIO_BASE(pin);//算出端口所在組虛擬基址如://GPA=0xF0E00000
//GPB=0XF0E00010
unsigned long offs = S3C2410_GPIO_OFFSET(pin); //算出端口所在組的偏移量(0~31)
unsigned long flags;
unsigned long dat;
local_irq_save(flags);
dat = __raw_readl(base + 0x04); //虛擬基址加0x04為 GP*DAT寄存器,加0x00為GP*ON等
//讀出當前GP*DAT寄存器的值
dat &= ~(1 << offs); //根據offs偏移量對該寄存器中選中的 位 清零,其他位保持不變
dat |= to << offs; //根據形參對要求的位進行位操作,來實現對具體某個IO口的配置
__raw_writel(dat, base + 0x04); //將配置寫入到寄存器(這里是虛擬地址)
local_irq_restore(flags);
}
4.S3C2410_GPIO_BASE和S3C2410_GPIO_OFFSET也是在regs-gpio.h文件中定義,
#define S3C2410_GPIO_BASE(pin) ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)
#define S3C2410_GPIO_OFFSET(pin) ((pin) & 31)
而在map.h中有:
/* GPIO ports */
#define S3C24XX_VA_GPIO S3C2410_ADDR(0x00E00000) //虛擬地址S3C24XX_VA_GPIO= 0xF0E00000
#define S3C2400_PA_GPIO (0x15600000)
#define S3C2410_PA_GPIO (0x56000000) //GPACON 物理地址
#define S3C24XX_SZ_GPIO SZ_1M //0x100000 = 1024 *1024
S3C2410_GPIO_BASE作用是:根 據端口編號pin,算出端口所在組的虛擬基址。((pin) & ~31)是去掉pin當中小於等於31的零頭(清0低5位),>>1的原因是每組GPIO中最多可以有32個端口,控制這些端口需要4個寄存 器空間,4個寄存器空間就需要4*4=16個字節進行編址,32/16=2,左移一位剛好滿足。也就是說,上一組端口和下一組端口的編號相差32,而控制 寄存器的地址相差16。
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))
#define __raw_writel(v,a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a) = (v))
在include\linux\compiler.h中:
#ifdef __CHECKER__
……
extern void __chk_io_ptr(void __iomem *);
#else
……
# define __chk_io_ptr(x) (void)0
……
#endif
__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) \
({ \
__asm__ __volatile__( \
"mrs %0, cpsr @ local_irq_save\n" \
"cpsid i" \
: "=r" (x) : : "memory", "cc"); \
})
#define local_irq_save(x) \
({ \
unsigned long temp; \
(void) (&temp == &x); \
__asm__ __volatile__( \
"mrs %0, cpsr @ local_irq_save\n" \
" orr %1, %0, #128\n" \
" msr cpsr_c, %1" \
: "=r" (x), "=r" (temp) \
: \
: "memory", "cc"); \
})