首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

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

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

穷根究底linux内核函数之s3c2410_gpio_cfgpin()
原创文章,转载注明出处:http://blog.chinaunix.net/uid-25669276-id-3265416.html
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),这个基地址又是怎么确定的?
返回列表