午饭过后,毛毛摸着小肚子又和大家见面了,我们大家一起来把上一篇文章没有学习完的知识学习完。如果毛毛讲的好,今天就奖励陶毛毛看一集《大耳朵图图》。
好的,我们现在开始学习吧!今天早上在我们的上一篇文章“Linux下arch/arm/mach-s3c2410/include/mach/gpio-nrs.h的理解”里面介绍了一系列的宏,但是我们不知道,为什么Linux内核要这样做?今天下午毛毛就带领大家一起来看看linux/arch/arm/plat-s3c24xx/gpio.c里面的应用。
毛毛通过查询内核代码,发现linux/arch/arm/plat-s3c24xx/gpio.c,主要是定义了一些Linux下对I/O口的标准配置和读写。包括以下函数:
void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function);
unsigned int s3c2410_gpio_getcfg(unsigned int pin);
void s3c2410_gpio_pullup(unsigned int pin, unsigned int to);
int s3c2410_gpio_getpull(unsigned int pin);
void s3c2410_gpio_setpin(unsigned int pin, unsigned int to);
unsigned int s3c2410_gpio_getpin(unsigned int pin);
unsigned int s3c2410_modify_misccr(unsigned int clear, unsigned int change);
int s3c2410_gpio_getirq(unsigned int pin);
上面函数的作用毛毛就不用再一一解释了,各位同学打开gpio.c文件就会理解,因为这些函数就是对I/O口的输入输出方向,数据读写和配置的函数。
接下来毛毛仅仅从void s3c2410_gpio_setpin(unsigned int pin, unsigned int to);这个函数来讨论上一篇文章中宏定义的作用。
首先,我们还是先将这个函数的源程序列出来给大家
void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
{
void __iomem *base = S3C24XX_GPIO_BASE(pin); //宏计算,得到pin的基地址
unsigned long offs = S3C2410_GPIO_OFFSET(pin); //宏计算,得到pin的相对基地址的偏移地址
unsigned long flags;
unsigned long dat;
local_irq_save(flags); //类似于进入临界区
dat = __raw_readl(base + 0x04); //读I/O口数据
dat &= ~(1 << offs);
dat |= to << offs;
__raw_writel(dat, base + 0x04); //写I/O口数据
local_irq_restore(flags); //类似于退出临界区
}
从以上程序大家可能还不能看出什么来,但是毛毛就是那么“打破砂锅问到底”,毛毛进入S3C24XX_GPIO_BASE(pin),S3C2410_GPIO_OFFSET(pin)这两个宏。
这两个宏定义在arch/arm/mach-s3c2410/include/mach/regs-gpio.h这个文件里面:
#define S3C2410_GPIO_BASE(pin) ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)
#define S3C2410_GPIO_OFFSET(pin) ((pin) & 31)
(S3C24XX_VA_GPIO,是一个宏,代表I/O口的虚拟基地址;这个地方不再多讲,1:我自己对虚拟地址和实际物理地址还不是太熟悉,不能误人子弟。2:如果要讲需要说的东西太多,不容易一下讲清楚,还请各位同学自己琢磨)
好的,到现在为止我们已经将基本上所有的东西列出来来了,接下来毛毛会用一个例子来将上面讲到的东西联系起来。
首先我们看下面这句C程序:s3c2410_gpio_setpin(S3C2410_GPA(0), 1);
这句程序的意思就是将S3C2410端口A的0口置1,相信大家和毛毛一样都懂的。
S3C2410_GPA(0)是Linux下arch/arm/mach-s3c2410/include/mach/gpio-nrs.h里面的宏定义,进入函数void s3c2410_gpio_setpin(unsigned int pin, unsigned int to);毛毛发现S3C2410_GPA(0)这个参数是作为宏S3C2410_GPIO_BASE(pin)和S3C2410_GPIO_OFFSET(pin)的参数。
至此我们应该比较明了了吧,今天上午我们对arch/arm/mach-s3c2410/include/mach/gpio-nrs.h文件里面所做的工作就是用来供这两个宏使用的。
但是为什么要这样定义呢?
细心的毛毛在arch/arm/mach-s3c2410/include/mach/regs-gpio.h(也就是定义基地址和偏移地址的宏所在的文件)文件里发现一下程序:
#define S3C2410_GPA0_ADDR0 (1<<0)
#define S3C2410_GPA1_ADDR16 (1<<1)
#define S3C2410_GPA2_ADDR17 (1<<2)
#define S3C2410_GPA3_ADDR18 (1<<3)
#define S3C2410_GPA4_ADDR19 (1<<4)
#define S3C2410_GPA5_ADDR20 (1<<5)
#define S3C2410_GPA6_ADDR21 (1<<6)
#define S3C2410_GPA7_ADDR22 (1<<7)
#define S3C2410_GPA8_ADDR23 (1<<8)
#define S3C2410_GPA9_ADDR24 (1<<9)
#define S3C2410_GPA10_ADDR25 (1<<10)
#define S3C2400_GPA10_SCKE (1<<10)
#define S3C2410_GPA11_ADDR26 (1<<11)
#define S3C2400_GPA11_nCAS0 (1<<11)
#define S3C2410_GPA12_nGCS1 (1<<12)
#define S3C2400_GPA12_nCAS1 (1<<12)
#define S3C2410_GPA13_nGCS2 (1<<13)
#define S3C2400_GPA13_nGCS1 (1<<13)
#define S3C2410_GPA14_nGCS3 (1<<14)
#define S3C2400_GPA14_nGCS2 (1<<14)
#define S3C2410_GPA15_nGCS4 (1<<15)
#define S3C2400_GPA15_nGCS3 (1<<15)
#define S3C2410_GPA16_nGCS5 (1<<16)
#define S3C2400_GPA16_nGCS4 (1<<16)
#define S3C2410_GPA17_CLE (1<<17)
#define S3C2400_GPA17_nGCS5 (1<<17)
#define S3C2410_GPA18_ALE (1<<18)
#define S3C2410_GPA19_nFWE (1<<19)
#define S3C2410_GPA20_nFRE (1<<20)
#define S3C2410_GPA21_nRSTOUT (1<<21)
#define S3C2410_GPA22_nFCE (1<<22)
大家看出什么来了吗?如果没有看出来也没有关系,毛毛把上面程序再列写一下你就知道了
void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
{
void __iomem *base = S3C24XX_GPIO_BASE(pin); //宏计算,得到pin的基地址
unsigned long offs = S3C2410_GPIO_OFFSET(pin); //宏计算,得到pin的相对基地址的偏移地址
unsigned long flags;
unsigned long dat;
local_irq_save(flags); //类似于进入临界区
dat = __raw_readl(base + 0x04); //读I/O口数据
dat &= ~(1 << offs);
dat |= to << offs;
__raw_writel(dat, base + 0x04); //写I/O口数据
local_irq_restore(flags); //类似于退出临界区
}
知道了吧,其实就是位的偏移地址,也就是指定读写的位。
到这里也许大家还是不太明白那到底和几天上午说的arch/arm/mach-s3c2410/include/mach/gpio-nrs.h有什么关系。
接下来我们慢慢解释:其实我们的每个I/O端口的I/O口数目是0到n,由物理地址上面的四个字节控制(包括读写和配置),四个字节共32位,I/O 0到32对应于,四个自己的每一个位。所以arch/arm/mach-s3c2410/include/mach/gpio-nrs.h文件里面所实现的就是从I/O端口A的0开始到I/O端口H排列成一个从低到高的数字。而这个数字用来为下面的这两个宏来使用,从而求得相对于I/O控制寄存器的起始地址的相对地址和在所在I/O端口寄存器内的偏移位地址。
#define S3C2410_GPIO_BASE(pin) ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)
#define S3C2410_GPIO_OFFSET(pin) ((pin) & 31)
(((pin) & ~31) >> 1)得到相对于起始地址0x56000000的偏移基地址(PORTA求出来为0x00,PORTB求出来为0x10,PORTC求出来为0x20,从而和物理地址相吻合)
((pin) & 31)得到在所在控制寄存器的相对位地址
好了,到此基本上就说完了,希望大家能懂。如果有不懂的地方请给我留言。