flyriz的笔记 https://passport2.21ic.com/?706617 [收藏] [复制] [RSS]

日志

穷根究底linux内核函数之s3c2410_gpio_cfgpin()

已有 998 次阅读2012-7-11 02:21 |个人分类:嵌入式linux|系统分类:嵌入式系统| 嵌入式, LINUX

穷根究底linux内核函数之s3c2410_gpio_cfgpin()
flyriz 2012-07-06
在ARM驱动程序的学习过程中,经常碰到对IO操作的函数:s3c2410_gpio_cfgpin(),结合linux内核源代码(版本linux-2.6.29.4),做一个详细的分析,以如下代码为例:
s3c2410_gpio_cfgpin(S3C2410_GPB5,S3C2410_GPB5_OUTP);
从函数名上来看,其作用是把S3C2410的GPB5引脚设置为输出,接下来就像做数学题一样进行化简吧,为方便描述,过程中用等号表示。
先把参数展开,参数S3C2410_GPB5 宏展开:
S3C2410_GPB5 = S3C2410_GPIONO(S3C2410_GPIO_BANKB, 5)
S3C2410_GPIO_BANKB = (32*1)
S3C2410_GPIONO(bank,offset) = ((bank) + (offset))       
S3C2410_GPB5 = (32*1)+5 

参数S3C2410_GPB5_OUTP 宏展开:
S3C2410_GPB5_OUTP = (0x01 << 10)

参数简化后的函数:
s3c2410_gpio_cfgpin(S3C2410_GPB5,S3C2410_GPB5_OUTP)=
s3c2410_gpio_cfgpin( (32*1)+5,(0x01 << 10) )

接下为再对函数本身化简,进入函数:s3c2410_gpio_cfgpin(unsigned int pin, unsigned int ) 
void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int )
{
        void __iomem *base = S3C24XX_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;
        }

        switch () {
        case S3C2410_GPIO_LEAVE:
                mask = 0;
                = 0;
                break;

        case S3C2410_GPIO_INPUT:
        case S3C2410_GPIO_OUTPUT:
        case S3C2410_GPIO_SFN2:
        case S3C2410_GPIO_SFN3:
                if (pin < S3C2410_GPIO_BANKB) {
                        -= 1;
                        &= 1;
                        <<= S3C2410_GPIO_OFFSET(pin);
                } else {
                        &= 3;
                        <<= S3C2410_GPIO_OFFSET(pin)*2;
                }
        }

        /* modify the specified register wwith IRQs off */

        local_irq_save(flags);

        con  = __raw_readl(base + 0x00);
        con &= ~mask;
        con |= ;

        __raw_writel(con, base + 0x00);

        local_irq_restore(flags);
}


现在看第一个子函数:
S3C24XX_GPIO_BASE(pin),这也是最复杂的一个子函数,宏展开后:
S3C24XX_GPIO_BASE(pin) = S3C2410_GPIO_BASE(pin)
                       = ( ( ( (pin) & ~31 ) >> 1 ) + S3C24XX_VA_GPIO )
S3C24XX_VA_GPIO = ( (S3C24XX_PA_GPIO-S3C24XX_PA_UART) + S3C24XX_VA_UART )
S3C24XX_PA_GPIO = S3C2410_PA_GPIO = (0x56000000)
S3C24XX_PA_UART = S3C2410_PA_UART = (0x50000000)
S3C24XX_VA_UART = S3C_VA_UART = S3C_ADDR(0x01000000)
S3C_ADDR(x)=(S3C_ADDR_BASE + (x))  
S3C_ADDR_BASE=(0xF4000000)
S3C24XX_VA_UART=(0xF4000000)+(0x01000000)=(0xF5000000)
S3C24XX_VA_GPIO =(0x56000000)-(0x50000000)+(0xF5000000)
                   =0xFB000000
最后:S3C24XX_GPIO_BASE(pin)=( ( ( (pin) & ~31 ) >> 1 ) + 0xFB000000
说明:
( ( ( (pin) & ~31 ) >> 1 ),这个东东是为了得到指定的pin引脚的寄存器相对于GPIO基地址的偏移量,至于为什么要这样写我接下来会说明。
S3C24XX_VA_GPIO,GPIO基地址的虚拟地址。顺便说明一下VA,PA,VA:虚拟地址,PA:物理地址,都是英语单词的首字母。现在很好理解了,先根据GPIO,UART的物理地址算出两者的偏移量:(S3C24XX_PA_GPIO-
S3C24XX_PA_UART),然后UART虚拟地址加上这个偏移量就得到GPIO的虚拟地址了:( (S3C24XX_PA_GPIO-
S3C24XX_PA_UART) + S3C24XX_VA_UART )。至于S3C24XX_VA_UART为什么最后就变成了(0xF5000000),我也暂时不知道,内核代码就是这么写的,应该是这样一个转换规则吧。

分析完这个子函数,接下来的代码就很好理解了。有一个地方要注意一下:if (pin < S3C2410_GPIO_BANKB),两次出现这个表达式,因为这个芯片的PORT A与其他的端口的不一样,它能实现的功能比较少,GPACON中只用一个数据位来控制这个引脚的功能,其他的比如PORT B的GPBCON寄存器,是用两个数据位来控制的。所以对PORT A的引脚要单独处理了。

有一个问题:
S3C_VA_UART = S3C_ADDR(0x01000000),这里的UART虚拟地址转制为什么是这样子的?
再看一下源代码里面的其他虚拟地址的转换:
#define S3C_VA_IRQ        S3C_ADDR(0x00000000)        /* irq controller(s) */
#define S3C_VA_SYS        S3C_ADDR(0x00100000)        /* system control */
#define S3C_VA_MEM        S3C_ADDR(0x00200000)        /* system control */
#define S3C_VA_TIMER        S3C_ADDR(0x00300000)        /* timer block */
#define S3C_VA_WATCHDOG        S3C_ADDR(0x00400000)        /* watchdog */
#define S3C_VA_UART        S3C_ADDR(0x01000000)        /* UART */
这个S3C_ADDR里面的内容是如何确定的?
S3C_ADDR_BASE=(0xF4000000),这个基地址又是怎么确定的?

期待高手的解答,谢谢!

路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)