Chinaunix首页 | 论坛 | 博客
  • 博客访问: 108878
  • 博文数量: 4
  • 博客积分: 130
  • 博客等级: 入伍新兵
  • 技术积分: 91
  • 用 户 组: 普通用户
  • 注册时间: 2008-09-16 21:02
文章分类

全部博文(4)

文章存档

2011年(1)

2008年(3)

我的朋友

分类: LINUX

2008-10-06 23:31:23

    上个月在qq2440的光盘里发现了一段简单的LED驱动程序,大致看了一下明白了大意;但是由于里面的宏定义很是麻烦,看起来绕来绕去,于是我也懒得看明白了,干脆在CU论坛里发了个帖一问了事。
    然而,等了几天后,寥寥几个回复。更不爽的是,唯一那个被加了分的回复一看就感觉不对劲,可我自己也没弄清楚缘由,因此也说不清。
    过完了十一,觉得又该学习了,突然想起来这个事。想想还是完全看懂比较好,毕竟自己对kernel的源码包还不算很熟悉,就当是学习kernel吧。
    首先是代码中的几个宏定义:

/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包的各个文件内,因此不太容易寻找。线索齐了后,就一目了然了。
阅读(8551) | 评论(6) | 转发(1) |
给主人留下些什么吧!~~

chinaunix网友2009-06-18 10:49:43

“GPB 地址应该是0x56000010 而这样算出来S3C24xx_VA_GPIO 是0xF0E00000 这样算出来,不对哦,请问各位是什么原因。 ” 请参考“重映射”相关知识。

chinaunix网友2009-05-21 10:03:06

GPB 地址应该是0x56000010 而这样算出来S3C24xx_VA_GPIO 是0xF0E00000 这样算出来,不对哦,请问各位是什么原因。

chinaunix网友2009-05-20 09:37:05

GPA是否应该是0x00+S3C24XX_VA_GPIO? 而GPB=0x01+S3C24XX_VA_GPIO?

chinaunix网友2008-12-02 11:09:33

简单地说,就是GPA是32位的寄存器,可能对应32个引脚,需要用5个位来对应,而源代码是使用S3C2410_GPIO_BASE(S3C2410_GPIO_OFFSET)来获得基地址的,这样就确保引用GPA每个引脚都是对应GPA基地址

chinaunix网友2008-12-02 10:41:34

对于问题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了