上个月在qq2440的光盘里发现了一段简单的LED驱动程序,大致看了一下明白了大意;但是由于里面的宏定义很是麻烦,看起来绕来绕去,于是我也懒得看明白了,干脆在CU论坛里发了个帖一问了事。
然而,等了几天后,寥寥几个回复。更不爽的是,唯一那个被加了分的回复一看就感觉不对劲,可我自己也没弄清楚缘由,因此也说不清。
过完了十一,觉得又该学习了,突然想起来这个事。想想还是完全看懂比较好,毕竟自己对kernel的源码包还不算很熟悉,就当是学习kernel吧。
首先是代码中的几个宏定义:
/linux/include/asm/hardware/s3c2410/regs-gpio.h 中:
....
driver中有这样的代码:
其中
开始一直不明白的是那个32怎么得来,于是找到了s3c2410_gpio_cfgpin和s3c2410_gpio_setpin,在我的linux/arch/arm/mach-s3c2410/gpio.c中:
发现S3C24XX_VA_GPIO这个虚拟地址不知道怎么得来,于是又找到linux/include/asm-arm/arch-s3c2410/map.h里这样一段:
然而,等了几天后,寥寥几个回复。更不爽的是,唯一那个被加了分的回复一看就感觉不对劲,可我自己也没弄清楚缘由,因此也说不清。
过完了十一,觉得又该学习了,突然想起来这个事。想想还是完全看懂比较好,毕竟自己对kernel的源码包还不算很熟悉,就当是学习kernel吧。
首先是代码中的几个宏定义:
/linux/include/asm/hardware/s3c2410/regs-gpio.h 中:
01 |
#define
S3C2410_GPIONO(bank,offset) ((bank) + (offset)) |
02 |
03 |
#define
S3C2410_GPIO_BANKA (32*0) |
04 |
#define
S3C2410_GPIO_BANKB (32*1) |
05 |
#define
S3C2410_GPIO_BANKC (32*2) |
06 |
.... |
07 |
08 |
#define
S3C2410_GPIO_BASE(pin) ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO) |
09 |
#define
S3C2410_GPIO_OFFSET(pin) ((pin) & 31) |
10 |
11 |
#define
S3C2410_GPBCON S3C2410_GPIOREG(0x10) |
12 |
#define
S3C2410_GPBDAT S3C2410_GPIOREG(0x14) |
13 |
#define
S3C2410_GPBUP S3C2410_GPIOREG(0x18) |
14 |
15 |
/*
no i/o pin in port b can have value 3! */ |
16 |
17 |
#define
S3C2410_GPB0 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 0) |
18 |
#define
S3C2410_GPB0_INP (0x00 << 0) |
19 |
#define
S3C2410_GPB0_OUTP (0x01 << 0) |
20 |
#define
S3C2410_GPB0_TOUT0 (0x02 << 0) |
21 |
22 |
#define
S3C2410_GPB1 S3C2410_GPIONO(S3C2410_GPIO_BANKB, 1) |
23 |
#define
S3C2410_GPB1_INP (0x00 << 2) |
24 |
#define
S3C2410_GPB1_OUTP (0x01 << 2) |
25 |
#define
S3C2410_GPB1_TOUT1 (0x02 << 2) |
....
driver中有这样的代码:
1 |
s3c2410_gpio_cfgpin(led_table[i],
led_cfg_table[i]); |
2 |
s3c2410_gpio_setpin(led_table[i],
1); |
其中
01 |
static unsigned long led_table
[] = { |
02 |
S3C2410_GPB5, |
03 |
S3C2410_GPB6, |
04 |
S3C2410_GPB7, |
05 |
S3C2410_GPB8, |
06 |
}; |
07 |
08 |
static unsigned int led_cfg_table
[] = { |
09 |
S3C2410_GPB5_OUTP, |
10 |
S3C2410_GPB6_OUTP, |
11 |
S3C2410_GPB7_OUTP, |
12 |
S3C2410_GPB8_OUTP, |
13 |
}; |
开始一直不明白的是那个32怎么得来,于是找到了s3c2410_gpio_cfgpin和s3c2410_gpio_setpin,在我的linux/arch/arm/mach-s3c2410/gpio.c中:
01 |
void s3c2410_gpio_cfgpin(unsigned int pin,
unsigned int function) |
02 |
{ |
03 |
void __iomem
*base = S3C2410_GPIO_BASE(pin); |
04 |
unsigned long mask; |
05 |
unsigned long con; |
06 |
unsigned long flags; |
07 |
08 |
if (pin
< S3C2410_GPIO_BANKB) { |
09 |
mask
= 1 << S3C2410_GPIO_OFFSET(pin); |
10 |
} else { |
11 |
mask
= 3 << S3C2410_GPIO_OFFSET(pin)*2; |
12 |
} |
13 |
14 |
local_irq_save(flags); |
15 |
16 |
con
= __raw_readl(base + 0x00); |
17 |
con
&= ~mask; |
18 |
con
|= function; |
19 |
20 |
__raw_writel(con,
base + 0x00); //config
the GPXCON |
21 |
22 |
local_irq_restore(flags); |
23 |
} |
24 |
25 |
EXPORT_SYMBOL(s3c2410_gpio_cfgpin); |
26 |
27 |
28 |
void s3c2410_gpio_setpin(unsigned int pin,
unsigned int to) |
29 |
{ |
30 |
void __iomem
*base = S3C2410_GPIO_BASE(pin); |
31 |
unsigned long offs
= S3C2410_GPIO_OFFSET(pin); |
32 |
unsigned long flags; |
33 |
unsigned long dat; |
34 |
35 |
local_irq_save(flags); |
36 |
37 |
dat
= __raw_readl(base + 0x04); |
38 |
dat
&= ~(1 << offs); |
39 |
dat
|= to << offs; |
40 |
__raw_writel(dat,
base + 0x04); //config
the GPXDAT |
41 |
42 |
local_irq_restore(flags); |
43 |
} |
44 |
45 |
EXPORT_SYMBOL(s3c2410_gpio_setpin); |
发现S3C24XX_VA_GPIO这个虚拟地址不知道怎么得来,于是又找到linux/include/asm-arm/arch-s3c2410/map.h里这样一段:
01 |
#ifndef
__ASSEMBLY__ |
02 |
#define
S3C2410_ADDR(x) ((void __iomem *)0xF0000000 + (x)) |
03 |
#else |
04 |
#define
S3C2410_ADDR(x) (0xF0000000 + (x)) |
05 |
#endif |
06 |
07 |
#define
S3C2400_ADDR(x) S3C2410_ADDR(x) |
08 |
09 |
/*
GPIO ports */ |
10 |
#define
S3C24XX_VA_GPIO S3C2410_ADDR(0x00E00000) |
11 |
#define
S3C2400_PA_GPIO (0x15600000) |
12 |
#define
S3C2410_PA_GPIO (0x56000000) |
13 |
#define
S3C24XX_SZ_GPIO SZ_1M |
这才终于把整个线索理清:
2410中的寄存器在I/O内存中根据0xF0000000这个base加上一个偏移值就简单的得到了VA(想不到2410里的MMU映射也太简单了点)。而且不同的寄存器偏移值都相差0x100000,比如
1 |
#define
S3C24XX_VA_RTC S3C2410_ADDR(0x00F00000) |
2 |
#define
S3C24XX_VA_GPIO S3C2410_ADDR(0x00E00000) |
3 |
#define
S3C24XX_VA_IIS S3C2410_ADDR(0x00D00000) |
等等..
有个GPIO的虚拟地址base,接下来分析下面的:
1 |
#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包的各个文件内,因此不太容易寻找。线索齐了后,就一目了然了。