首先是代码中的几个宏定义:
/linux/include/asm/hardware/s3c2410/regs-gpio.h 中:
#define S3C2410_GPIONO(bank,offset) ((bank) + (offset))
#define S3C2410_GPIO_BANKA (32*0)
#define S3C2410_GPIO_BANKB (32*1)
#define S3C2410_GPIO_BANKC (32*2)
....
#define S3C2410_GPIO_BASE(pin) ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)
#define S3C2410_GPIO_OFFSET(pin) ((pin) & 31)
#define S3C2410_GPBCON S3C2410_GPIOREG(0x10)
#define S3C2410_GPBDAT S3C2410_GPIOREG(0x14)
#define S3C2410_GPBUP S3C2410_GPIOREG(0x18)
/* no i/o pin in port b can have value 3! */
#define S3C2410_GPB0 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 0)
#define S3C2410_GPB0_INP (0x00 << 0)
#define S3C2410_GPB0_OUTP (0x01 << 0)
#define S3C2410_GPB0_TOUT0 (0x02 << 0)
#define S3C2410_GPB1 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 1)
#define S3C2410_GPB1_INP (0x00 << 2)
#define S3C2410_GPB1_OUTP (0x01 << 2)
#define S3C2410_GPB1_TOUT1 (0x02 << 2)
....
driver中有这样的代码:
s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
s3c2410_gpio_setpin(led_table[i], 1);
其中
static unsigned long led_table [] = {
S3C2410_GPB5,
S3C2410_GPB6,
S3C2410_GPB7,
S3C2410_GPB8,
};
static unsigned int led_cfg_table [] = {
S3C2410_GPB5_OUTP,
S3C2410_GPB6_OUTP,
S3C2410_GPB7_OUTP,
S3C2410_GPB8_OUTP,
};
开始一直不明白的是那个32怎么得来,于是找到了s3c2410_gpio_cfgpin和s3c2410_gpio_setpin,在我的linux/arch/arm/mach-s3c2410/gpio.c中:
void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function)
{
void __iomem *base = S3C2410_GPIO_BASE(pin);
unsigned long mask;
unsigned long con;
unsigned long flags;
if (pin < S3C2410_GPIO_BANKB) {
mask = 1 << S3C2410_GPIO_OFFSET(pin);
} else {
mask = 3 << S3C2410_GPIO_OFFSET(pin)*2;
}
local_irq_save(flags);
con = __raw_readl(base + 0x00);
con &= ~mask;
con |= function;
__raw_writel(con, base + 0x00); //config the GPXCON
local_irq_restore(flags);
}
EXPORT_SYMBOL(s3c2410_gpio_cfgpin);
void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
{
void __iomem *base = S3C2410_GPIO_BASE(pin);
unsigned long offs = S3C2410_GPIO_OFFSET(pin);
unsigned long flags;
unsigned long dat;
local_irq_save(flags);
dat = __raw_readl(base + 0x04);
dat &= ~(1 << offs);
dat |= to << offs;
__raw_writel(dat, base + 0x04); //config the GPXDAT
local_irq_restore(flags);
}
EXPORT_SYMBOL(s3c2410_gpio_setpin);
发现S3C24XX_VA_GPIO这个虚拟地址不知道怎么得来,于是又找到linux/include/asm-arm/arch-s3c2410/map.h里这样一段:
#ifndef __ASSEMBLY__
#define S3C2410_ADDR(x) ((void __iomem *)0xF0000000 + (x))
#else
#define S3C2410_ADDR(x) (0xF0000000 + (x))
#endif
#define S3C2400_ADDR(x) S3C2410_ADDR(x)
/* GPIO ports */
#define S3C24XX_VA_GPIO S3C2410_ADDR(0x00E00000)
#define S3C2400_PA_GPIO (0x15600000)
#define S3C2410_PA_GPIO (0x56000000)
#define S3C24XX_SZ_GPIO SZ_1M
这才终于把整个线索理清:
2410中的寄存器在I/O内存中根据0xF0000000这个base加上一个偏移值就简单的得到了VA(想不到2410里的MMU映射也太简单了点)。而且不同的寄存器偏移值都相差0x100000,比如
#define S3C24XX_VA_RTC S3C2410_ADDR(0x00F00000)
#define S3C24XX_VA_GPIO S3C2410_ADDR(0x00E00000)
#define S3C24XX_VA_IIS S3C2410_ADDR(0x00D00000) 等等..
有个GPIO的虚拟地址base,接下来分析下面的:
#define S3C2410_GPIO_BASE(pin) ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)
原来这句话的意思是:根据GPIO的VA,由得到的pin数,可以得到一组I/O的base。比如GPA得到的是
0x10+S3C24XX_VA_GPIO,GPB是0x20+S3C24XX_VA_GPIO,GPC是0x30+S3C24XX_VA_GPIO...
由此可知,之前的32是为了方便统计每组I/O的pin数,因为A组含23个(最多),尽管GPACON,GPBCON,GPCCON...之间的地址只相差0x10,但是用16无法满足,所以只得用32了。如果每组最多也不超多16个寄存器,我认为16是可以实现的,只是S3C2410_GPIO_BASE(pin)里不需要那个右移操作了。总之,32更多的是因为GPIO管脚设置的实现而采取的一个数字,与32位总线倒关系不大。如果GPA含33个,这里就一定得是64了。
其实想想这代码也不难理解,只是宏定义和函数的实现分散在kernel包的各个文件内,因此不太容易寻找。线索齐了后,就一目了然了。
网友评论 |
|
内容: |
分析得很彻底!不过在最后一段,我有点小问题:
GPACON,GPBCON,GPCCON...之间的地址只相差0x10,
GPA得到的应该是0x00+VA,……
问题1:为什么GPA与GPB地址相差0x10呢?是因为用于GPA的寄存器有4个(搂主说的23个寄存器其实是说GPA对应的23个引脚,只是23个位而已),GPACON,GPADATA,GPAUP,RESERVED,每个寄存器占有32(4字节)位的地址,所以它们之间相差16个字节(0x10)。
问题2:为什么要右移1位呢?
我认为开始的32*0和32*1……之所以采用32,可能还是因为32位总线的问题,如果用16的话可以不用右移1位而直接得到相差0x10的地址差, |
本站网友 评论于:2008-12-02 10:19:23 (59.41.253.★) |
|
|
内容: |
对于问题2:
先看这两个定义:
(1)#define S3C2410_GPIO_BASE(pin) ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)
pin&1111 1111 1110 0000
#define S3C2410_GPIO_OFFSET(pin) ((pin) & 31)
pin&0001 1111
很显然低5位是用作GPn的偏移地址,高位用作基地址,当然得用32了 |
kk 评论于:2008-12-02 10:41:34 (59.41.253.★) |
|
|
内容: |
简单地说,就是GPA是32位的寄存器,可能对应32个引脚,需要用5个位来对应,而源代码是使用S3C2410_GPIO_BASE(S3C2410_GPIO_OFFSET)来获得基地址的,这样就确保引用GPA每个引脚都是对应GPA基地址 |
kk 评论于:2008-12-02 11:09:33 (59.41.253.★) | |
阅读(1418) | 评论(0) | 转发(0) |