Linux下linux/arch/arm/plat-s3c24xx/gpio.c的理解

上一篇 / 下一篇  2013-03-04 18:56:05 / 个人分类:Linux驱动

 午饭过后,毛毛摸着小肚子又和大家见面了,我们大家一起来把上一篇文章没有学习完的知识学习完。如果毛毛讲的好,今天就奖励陶毛毛看一集《大耳朵图图》。

  好的,我们现在开始学习吧!今天早上在我们的上一篇文章“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 ffs = 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 ffs = 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)得到在所在控制寄存器的相对位地址

好了,到此基本上就说完了,希望大家能懂。如果有不懂的地方请给我留言。


TAG:

 

评分:0

我来说两句

Open Toolbar