S3C24XX体系的Linux GPIO控制相关API实现

来源:互联网 发布:linux lamp一键 编辑:程序博客网 时间:2024/05/09 15:40

GPIOGeneral Purpose Input/Output的缩写)就是芯片的引脚,引脚是可编程的可对引脚的工作模式进行设置:输入模式(检测输入信号),输出模式(输出01),高阻状态(常用于AD转换),还有禁止或允许上内部下拉电阻(上拉:管脚通过电阻接高电平,下拉:管脚通过电阻接地,也可以外部接上拉或下拉电阻),还有管脚复用等功能,即通过对内部寄存器的设置使引脚既可以工作在一般模式,作为普通的GPIO口使用,也可以工作在特殊模式,比如作为外部中断信号输入引脚等等。如果不设置GPIO引脚,CPU工作时有一个初始化模式,可以从datasheet(芯片手册)上了解。对GPIO的控制是编写驱动程序最常见和重要的一项工作内容。

Linux2.6.25内核代码中,已经提供了针对三星S3C2410/S3C2440等芯片GPIO的控制,下面来分别介绍一般如何配置、管理GPIO

       下面的函数是用来根据新的功能需求配置一个GPIO引脚(定义在arch/arm/plat-s3c24xx/gpio.c):

     1    void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function)

     2    {

     3           void __iomem *base = S3C24XX_GPIO_BASE(pin);

     4           unsigned long mask;

     5           unsigned long con;

     6           unsigned long flags;

     7   

     8           if (pin < S3C2410_GPIO_BANKB) {

     9                  mask = 1 << S3C2410_GPIO_OFFSET(pin);

    10           } else {

    11                  mask = 3 << S3C2410_GPIO_OFFSET(pin)*2;

    12           }

    13   

    14           switch (function) {

    15           case S3C2410_GPIO_LEAVE:

    16                  mask = 0;

    17                  function = 0;

    18                  break;

    19   

    20           case S3C2410_GPIO_INPUT:

    21           case S3C2410_GPIO_OUTPUT:

    22           case S3C2410_GPIO_SFN2:

    23           case S3C2410_GPIO_SFN3:

    24                  if (pin < S3C2410_GPIO_BANKB) {

    25                         function -= 1;

    26                         function &= 1;

    27                         function <<= S3C2410_GPIO_OFFSET(pin);

    28                  } else {

    29                         function &= 3;

    30                         function <<= S3C2410_GPIO_OFFSET(pin)*2;

    31                  }

    32           }

    33   

    34           /* modify the specified register wwith IRQs off */

    35   

    36           local_irq_save(flags);

    37   

    38           con  = __raw_readl(base + 0x00);

    39           con &= ~mask;

    40           con |= function;

    41   

    42           __raw_writel(con, base + 0x00);

    43   

    44           local_irq_restore(flags);

    45    }

       其中参数pin是要配置的GPIO引脚,参数function是要配置的功能。第3,其中S3C2410_GPIO_BASE是内核代码中定义的宏,定义在include/asm-arm/arch-s3c2410/regs-gpio.h文件,定义如下:

#define S3C2410_GPIO_BASE(pin)   ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)

其中S3C24XX_VA_GPIO= ((S3C2410_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART)= 0x56000000-0x50000000+0xF5000000= 0xF9000000,即通过S3C2410_GPIO_BASE宏可获得物理地址到虚拟地址的转化。第8~12行,计算要设置寄存器的掩码位,当引脚为GPIOA端口时,它的掩码位是1位,通过芯片手册(如下)即可获得此信息。

 

如果引脚为A端口之外GPIO端口时,它是用两位来配置具体的引脚,故掩码为2位,通过芯片手册即可获得此信息。

其中S3C2410_GPIO_OFFSET宏被定义为:S3C2410_GPIO_OFFSET(pin) ((pin) & 31) 获得引脚的偏移位。第14~30行,根据参数function的值来修改设置特定function的值。第38~42行,是对寄存器操作最常见的做法:首先通过__raw_readl来读取寄存器原有的内容,第3940行是根据新的需求修改原有的寄存器特定位的值(确保其他位保持不变),第42行,调用__raw_writel将修改后的寄存器值重新写入相应的寄存器。

对于s3c2410_gpio_cfgpin()的应用具体,s3c2410_gpio_cfgpin(S3C2410_GPB5, S3C2410_GPB5_OUTP); 其中S3C2410_GPB5对应的引脚为芯片手册上的GPB5,如下所示:

根据芯片手册,GPB5是由位[11:10]来控制,当该两位值为01时,则配置为输出。S3C2410_GPB5_OUTP的定义在include/asm-arm/arch-s3c2410/regs-gpio.h文件,定义如下:

#define S3C2410_GPB5_OUTP    (0x01 << 10)

[11:10]01。如要要配置该GPIO为输入时,那么该两位的值就必须设置为00

       下面s3c2410_gpio_getcfg()函数用来读取一个GPIO引脚的配置,该函数实现代码如下:

unsigned int s3c2410_gpio_getcfg(unsigned int pin)

{

       void __iomem *base = S3C24XX_GPIO_BASE(pin);

       unsigned long val = __raw_readl(base);

 

       if (pin < S3C2410_GPIO_BANKB) {

              val >>= S3C2410_GPIO_OFFSET(pin);

              val &= 1;

              val += 1;

       } else {

              val >>= S3C2410_GPIO_OFFSET(pin)*2;

              val &= 3;

       }

 

       return val | S3C2410_GPIO_INPUT;

}

       该函数根据引脚获得指定引脚配置的功能值。对于s3c2410_gpio_getcfg()函数的应用事例如:s3c2410_gpio_getcfgS3C2410_GPF0);获得GPF0的功能值。

       下面的s3c2410_gpio_pullup()函数用来配置一个上拉电阻,其实现代码如下:

void s3c2410_gpio_pullup(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 up;

 

       if (pin < S3C2410_GPIO_BANKB)

              return;

       local_irq_save(flags);

       up = __raw_readl(base + 0x08);  //偏移的0x08是由于配置上拉寄存器的物理地址偏移

       up &= ~(1L << offs);

       up |= to << offs;

       __raw_writel(up, base + 0x08);

       local_irq_restore(flags);

}

       除此之外,还有其他的GPIO相关函数,如s3c2410_gpio_getpulls3c2410_gpio_setpins3c2410_gpio_getpin等,其实现方式与上述代码类似,有兴趣的读者可仔细阅读其实现代码。

原创粉丝点击