S3C2410_GPF(4)
#define S3C2410_GPF(_nr) (S3C2410_GPIO_F_START + (_nr)) ==》变成:S3C2410_GPIO_F_START+4
S3C2410_GPIO_F_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_E) ==》变成:S3C2410_GPIO_NEXT(S3C2410_GPIO_E) + 4
#define S3C2410_GPIO_NEXT(__gpio) \
((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 0)
分析:从后面开始分析
#define S3C2410_GPIO_NEXT(__gpio) \
((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 0)
其中##在这里表示连接作用,字符串的连接而已;根据上面的可以得到:S3C2410_GPIO_E_START + S3C2410_GPIO_E_NR + CONFIG_S3C_GPIO_SPACE + 0 +4
S3C2410_GPIO_E_START == S3C2410_GPIO_NEXT(S3C2410_GPIO_D) == S3C2410_GPIO_D_START+S3C2410_GPIO_D_NR+ CONFIG_S3C_GPIO_SPACE + 0
写到这里应该明白了;
S3C2410_GPIO_E_NR+S3C2410_GPIO_D_NR+S3C2410_GPIO_C_NR+S3C2410_GPIO_B_NR+S3C2410_GPIO_A_NR + 5*CONFIG_S3C_GPIO_SPACE+0+4
其中:#define CONFIG_S3C_GPIO_SPACE 0
看的都乱了,其实就是从A类管脚个数开始计算到当前类引脚个数。S3C2410_GPB(0) 就是从A类引脚开始计算,所以为:32;依次类推;
=======================================================================================================
//设置pin引进上的值为to,也就是设置输出值GPBDAT
void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
{
void __iomem *base = S3C24XX_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);
local_irq_restore(flags);
}
#define S3C24XX_GPIO_BASE(x) S3C2410_GPIO_BASE(x) 这里没做处理,还是简单替换下
#define S3C2410_GPIO_BASE(pin) ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO) 这里就处理了下,根据上面的:S3C2410_GPF(4) 例子可以知道每一个类型组都有32个引脚数(实际不一定有那么多);比如GPB0:32;GPC0:64等等;(((pin) & ~31) >> 1) 就可以变成:0x10(GPB类),0x20(GPC类)。记住宏里面是十进制数,而我们要得到的是十六进制数,每4个二进制数对应一个十六进制数,这样就容易理解也容易分析了。至于为什么是0x10、0x20,这可以看下GPBCON/GPCCON的地址尾数;接下来就看看 S3C24XX_VA_GPIO了。
#define S3C24XX_VA_GPIO ((S3C24XX_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART)
直接找到对应的数据就运算:
=((0x56000000 - 0x50000000) + (0xF4000000 + 0x01000000))
= (0x06000000 + 0xF5000000)
= (0xFB000000)
得到的这是虚拟地址,而物理地址GPIO=0x56000000 UART=0x50000000
可以参考这篇blog:http://www.phpfans.net/article/htmls/201004/MjgzODU5.html 这是虚拟地址的运算,我不是很懂,后面理解了再补上;
2、理解了上面的地址就好办了,下面是真正运算的
dat = __raw_readl(base + 0x04);// 从地址中读取数据,这是加了 0x04 则表示的是:GPBDAT位
dat &= ~(1 << offs); // 对pin指定位进行清零
dat |= to << offs; // 根据to设置pin相对应的位
__raw_writel(dat, base + 0x04);// 把数据再次写入到地址中
========================================================================================================
void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function)
{
void __iomem *base = S3C24XX_GPIO_BASE(pin);// 得到虚拟地址
unsigned long mask;
unsigned long con;
unsigned long flags;
if (pin < S3C2410_GPIO_BANKB) { // GPA类端口是有一位数来控制的
mask = 1 << S3C2410_GPIO_OFFSET(pin);
} else { // 其他类的端口是有2个位数控制的。3表示 二进制 11;
mask = 3 << S3C2410_GPIO_OFFSET(pin)*2;
}
switch (function) {
case S3C2410_GPIO_LEAVE:
mask = 0;
function = 0;
break;
case S3C2410_GPIO_INPUT:
case S3C2410_GPIO_OUTPUT:
case S3C2410_GPIO_SFN2:
case S3C2410_GPIO_SFN3:
if (pin < S3C2410_GPIO_BANKB) {
function -= 1;
function &= 1;
function <<= S3C2410_GPIO_OFFSET(pin);
} else {
function &= 3;
function <<= S3C2410_GPIO_OFFSET(pin)*2;//function移动到相应位
}
}
/* modify the specified register wwith IRQs off */
local_irq_save(flags);
con = __raw_readl(base + 0x00);//读取数据
con &= ~mask;//相应位清零
con |= function;//或上参数设置的位
__raw_writel(con, base + 0x00);写入数据
local_irq_restore(flags);
}
使用方式:
如pin=S3C2410_GPB5 function=00 则:设置S3C2410_GPB5为输入口
如pin=S3C2410_GPB5 function=01 则:设置S3C2410_GPB5为输出口
如pin=S3C2410_GPB5 function=10 则:设置S3C2410_GPB5为多功能口
可以参考blog:http://blog.163.com/appreciate_y/blog/static/189250050201152911148847/
转载请注明作者和原文出处,原文地址:http://blog.csdn.net/yuzhihui_no1/article/details/45096159