zoukankan      html  css  js  c++  java
  • Marvelllinux研究—gpio.c源代码分析

    Marvell-linux研究—gpio.c源代码分析

     

    转载时请注明出处和作者联系方式:http://blog.csdn.net/absurd

    作者联系方式:李先静 <xianjimli at hotmail dot com>

    更新时间:2007-7-4

     

    GPIO General Programmable Input Output Pin的首字母缩写,G(General)表示通用,就是可以用于多种用途,P(Programmable)表示可编程,就是可以用程序去控制它,I(Input)表示输入,就是可以用于从外设读取数据,而O(Output)表示输出,也就是可以用于向外设输出数据。总之,它是CPU与外设进行交互的一种方式。

     

    正如其名字所示的,它可以用于多种用途,至于具体做什么,要根据实际情况进行配置。最简单的用途可能是用它来连接一个LED,用程序来控制LED点亮或者关闭,这种用法在实验板上很常见。

     

    GPIO编程简单而又功能强大,所以我们选择它作为研究的入口。今天我们分析一下arch/arm/mach-pxa/gpio.c的源代码,该文件提供了对GPIO编程提供了最基本的抽象:

    56 int mhn_gpio_set_direction(int gpio_id, int dir)
     57 {
     58         unsigned long flags;
     59         int gpio = MFP2GPIO(gpio_id);
     60
     61         GPIO_ID_VERIFY(gpio);
     62
     63         spin_lock_irqsave(&gpio_spin_lock, flags);
     64 #if defined(CONFIG_MONAHANS_GPIOEX)
     65         if (gpio >= GPIO_EXP_START) {
     66                 spin_unlock_irqrestore(&gpio_spin_lock, flags);
     67                 return gpio_exp_set_direction(gpio, dir);
     68         }
     69 #endif
     70         if (dir == GPIO_DIR_IN)
     71                 G CDR(gpio) = 1u << (gpio & 0x1f);
     72         else
     73                 GSDR(gpio) = 1u << (gpio & 0x1f);
     74
     75         spin_unlock_irqrestore(&gpio_spin_lock, flags);
     76
     77         return 0;
     78 }

     

    该函数用于设置某个GPIO数据的流动方向,所谓方向就是指input还是output,如果dir等于GPIO_DIR_IN则为input,否则为output。只能二选一,不同时即作为input又作为output。

     

    MFP2GPIO(gpio_id)只是确保gpio_id的高16位为0,而GPIO_ID_VERIFY(gpio)确保gpio_id没有超出范围。

     

    64-69行:如果支持GPIOEX,而且gpio >= GPIO_EXP_START,则调用另外一个函数gpio_exp_set_direction去设置。后面的函数若有类似的处理,我们就不多说了。

     

    注意:

    PXA系列芯片有128个GPIO,对于一般的设置,每个GPIO都占用寄存器的一位,所以每类寄存器都需要4个(共128位)。G CDR(gpio)之类宏就是为了把gpio映射到对应的寄存器上,而1u << (gpio & 0x1f)之类的代码就是为了设置对应的位。

     

    PXA系列芯片对于一般的GPIO设置,都有三个寄存器,一个用于读取设置,一个用于把设置置为1,一个用于把设置清为零。这样做的目的可能是为了提高效率,在设置时不必把寄存器的值先读出来,设置适当的位,然后写回去。按这种方式设计,在设置时,不会影响其它GPIO的值,所以不必读取原来的值。

     

     80 int mhn_gpio_get_direction(int gpio_id)
     81 {
     82         int gpio = MFP2GPIO(gpio_id);
     83
     84         GPIO_ID_VERIFY(gpio);
     85 #if defined(CONFIG_MONAHANS_GPIOEX)
     86         if (gpio >= GPIO_EXP_START)
     87                 return gpio_exp_get_direction(gpio);
     88 #endif
     89
     90         if (GPDR(gpio) & (1u << (gpio & 0x1f)))
     91                 return GPIO_DIR_OUT;
     92         else
     93                 return GPIO_DIR_IN;
     94 }

    该函数获取某个GPIO数据的流动方向,正如前面所说,获取方向时用的另外一个寄存器--GPDR

     

     96 int mhn_gpio_set_level(int gpio_id, int level)
     97 {
     98         unsigned long flags;
     99         int gpio = MFP2GPIO(gpio_id);
    100
    101         GPIO_ID_VERIFY(gpio);
    102
    103         spin_lock_irqsave(&gpio_spin_lock, flags);
    104 #if defined(CONFIG_MONAHANS_GPIOEX)
    105         if (gpio >= GPIO_EXP_START) {
    106                 spin_unlock_irqrestore(&gpio_spin_lock, flags);
    107                 return gpio_exp_set_level(gpio, level);
    108         }
    109 #endif
    110
    111         if (level == GPIO_LEVEL_LOW)
    112                 GPCR(gpio) = 1u << (gpio & 0x1f);
    113         else
    114                 GPSR(gpio) = 1u << (gpio & 0x1f);
    115
    116         spin_unlock_irqrestore(&gpio_spin_lock, flags);
    117
    118         return 0;
    119 }

     

    该函数用于设置输出的数据,可以设置输出高电平或者低电平,它与设置数据流动方向的函数类似,只是操作GPCRGPSR两个寄存器,这里不再多说。让人费解是,不就是输出的数据嘛,为什么要叫level而不叫data呢。我想可能与《PXA300 and PXA310 Developers Manual 1》的4.11.3.2中所说的Second Level Generic Wakeups有关呢。

     

    121 int mhn_gpio_get_level(int gpio_id)
    122 {
    123         int gpio = MFP2GPIO(gpio_id);
    124
    125         GPIO_ID_VERIFY(gpio);
    126 #if defined(CONFIG_MONAHANS_GPIOEX)
    127         if (gpio >= GPIO_EXP_START)
    128                 return gpio_exp_get_level(gpio);
    129 #endif
    130
    131         if (GPLR(gpio) & (1u << (gpio & 0x1f)))
    132                 return GPIO_LEVEL_HIGH;
    133         else
    134                 return GPIO_LEVEL_LOW;
    135 }

     

    该函数用于获取输入数据,其与获取数据流动方向类似,只是操作GPLR寄存器。

     

    140 int mhn_gpio_set_rising_edge_detect(int gpio_id, int enable)
    141 {
    142         unsigned long flags;
    143         int gpio = MFP2GPIO(gpio_id);
    144
    145         GPIO_ID_VERIFY(gpio);
    146 #if defined(CONFIG_MONAHANS_GPIOEX)
    147         if (gpio >= GPIO_EXP_START)
    148                 return 0;
    149 #endif
    150
    151         spin_lock_irqsave(&gpio_spin_lock, flags);
    152
    153         if (enable == 0)
    154                 GCRER(gpio) = 1u << (gpio & 0x1f);
    155         else
    156                 GSRER(gpio) = 1u << (gpio & 0x1f);
    157
    158         spin_unlock_irqrestore(&gpio_spin_lock, flags);
    159
    160         return 0;
    161 }

     

    该函数用于设置是否启用上升沿触发中断,它与设置数据流动方向的函数类似,只是操作GCRERGSRER两个寄存器。如果启用,则输入数据从低电平进入高电平时触发中断。

     

    163 int mhn_gpio_get_rising_edge_detect(int gpio_id)
    164 {
    165         int gpio = MFP2GPIO(gpio_id);
    166
    167         GPIO_ID_VERIFY(gpio);
    168 #if defined(CONFIG_MONAHANS_GPIOEX)
    169         if (gpio >= GPIO_EXP_START)
    170                 return 0;
    171 #endif
    172
    173         if (GRER(gpio) & (1u << (gpio & 0x1f)))
    174                 return 1;
    175
    176         return 0;
    177 }

     

    该函数用于判断是否启用了上升沿触发中断。

     

    179 int mhn_gpio_set_falling_edge_detect(int gpio_id, int enable)
    180 {
    181         unsigned long flags;
    182         int gpio = MFP2GPIO(gpio_id);
    183
    184         GPIO_ID_VERIFY(gpio);
    185
    186 #if defined(CONFIG_MONAHANS_GPIOEX)
    187         if (gpio >= GPIO_EXP_START)
    188                 return 0;
    189 #endif
    190
    191         spin_lock_irqsave(&gpio_spin_lock, flags);
    192
    193         if (enable == 0)
    194                 GCRER(gpio) = 1u << (gpio & 0x1f);
    195         else
    196                 GSRER(gpio) = 1u << (gpio & 0x1f);
    197
    198         spin_unlock_irqrestore(&gpio_spin_lock, flags);
    199
    200         return 0;
    201 }

     

    该函数用于设置是否启用下降沿触发中断,它与设置数据流动方向的函数类似,只是操作GCFERGSFER两个寄存器。如果启用,则输入数据从高电平进入低电平时触发中断。由于该函数与mhn_gpio_set_rising_edge_detect极为类似,作者copy-paste时忘了修改寄器,所以代码中的寄存器是错的。

     

    203 int mhn_gpio_get_falling_edge_detect(int gpio_id)
    204 {
    205         int gpio = MFP2GPIO(gpio_id);
    206
    207         GPIO_ID_VERIFY(gpio);
    208
    209 #if defined(CONFIG_MONAHANS_GPIOEX)
    210         if (gpio >= GPIO_EXP_START)
    211                 return 0;
    212 #endif
    213
    214         if (GFER(gpio) & (1u << (gpio & 0x1f)))
    215                 return 1;
    216
    217         return 0;
    218 }

     

    该函数用于判断是否启用了下降沿触发中断。

     

    220 int mhn_gpio_get_edge_detect_status(int gpio_id)
    221 {
    222         int gpio = MFP2GPIO(gpio_id);
    223
    224         GPIO_ID_VERIFY(gpio);
    225
    226 #if defined(CONFIG_MONAHANS_GPIOEX)
    227         if (gpio >= GPIO_EXP_START)
    228                 return 0;
    229 #endif
    230
    231         if (GEDR(gpio) & (1u << (gpio & 0x1f)))
    232                 return 1;
    233
    234         return 0;
    235 }

     

    该函数用于检测是否发生了电平变化,即是否有上升沿触发中断,或者下降沿触发中断发生。

     

    237 int mhn_gpio_clear_edge_detect_status(int gpio_id)
    238 {
    239         unsigned long flags;
    240         int gpio = MFP2GPIO(gpio_id);
    241
    242         GPIO_ID_VERIFY(gpio);
    243
    244 #if defined(CONFIG_MONAHANS_GPIOEX)
    245         if (gpio >= GPIO_EXP_START)
    246                 return 0;
    247 #endif
    248
    249         spin_lock_irqsave(&gpio_spin_lock, flags);
    250
    251         GEDR(gpio) = 1u << (gpio & 0x1f);
    252
    253         spin_unlock_irqrestore(&gpio_spin_lock, flags);
    254
    255         return 0;
    256 }

     

    清除发生电平变化的标志,如果发生电平变化,GEDR会自动设置,但不会自动清除,而需要程序主动清除。这里获取和清除是同一个寄存器,原因是不需要程序去设置,所以不需要独立的寄存器。

     

    262 void mhn_gpio_save(void)
    263 {
    264         gpio_saved_reg.gpdr0 = GPDR0;
    265         gpio_saved_reg.gpdr1 = GPDR1;
    266         gpio_saved_reg.gpdr2 = GPDR2;
    267         gpio_saved_reg.gpdr3 = GPDR3;
    268
    269         gpio_saved_reg.gplr0 = GPLR0;
    270         gpio_saved_reg.gplr1 = GPLR1;
    271         gpio_saved_reg.gplr2 = GPLR2;
    272         gpio_saved_reg.gplr3 = GPLR3;
    273
    274         gpio_saved_reg.grer0 = GRER0;
    275         gpio_saved_reg.grer1 = GRER1;
    276         gpio_saved_reg.grer2 = GRER2;
    277         gpio_saved_reg.grer3 = GRER3;
    278
    279         gpio_saved_reg.gfer0 = GFER0;
    280         gpio_saved_reg.gfer1 = GFER1;
    281         gpio_saved_reg.gfer2 = GFER2;
    282         gpio_saved_reg.gfer3 = GFER3;
    283 }
    284
    285 void mhn_gpio_restore(void)
    286 {
    287         GPDR0 = gpio_saved_reg.gpdr0;
    288         GPDR1 = gpio_saved_reg.gpdr1;
    289         GPDR2 = gpio_saved_reg.gpdr2;
    290         GPDR3 = gpio_saved_reg.gpdr3;
    291
    292         GPSR0 = gpio_saved_reg.gplr0;
    293         GPSR1 = gpio_saved_reg.gplr1;
    294         GPSR2 = gpio_saved_reg.gplr2;
    295         GPSR3 = gpio_saved_reg.gplr3;
    296         GPCR0 = ~(gpio_saved_reg.gplr0);
    297         GPCR1 = ~(gpio_saved_reg.gplr1);
    298         GPCR2 = ~(gpio_saved_reg.gplr2);
    299         GPCR3 = ~(gpio_saved_reg.gplr3);
    300
    301         GRER0 = gpio_saved_reg.grer0;
    302         GRER1 = gpio_saved_reg.grer1;
    303         GRER2 = gpio_saved_reg.grer2;
    304         GRER3 = gpio_saved_reg.grer3;
    305
    306         GFER0 = gpio_saved_reg.gfer0;
    307         GFER1 = gpio_saved_reg.gfer1;
    308         GFER2 = gpio_saved_reg.gfer2;
    309         GFER3 = gpio_saved_reg.gfer3;
    310 }

     

    这两个函数用于保存或恢复GPIO的设置,主要是在电源管理中,用于suspendresume

     

    PXA3xxx中,GPIO实际上已经是一个逻辑上的概念,它不但可以作为通用的IO,可以作为专用的IO Pin,这可以通过程序设置,在Multi-Function Pin中,我们将继续研究。

     

    ~~end~~

  • 相关阅读:
    【网摘】sql 语句修改字段名称以及字段类型
    转:关于bugfree的一些不得不说的事
    excel打乱各行的顺序,实现无序随机排列
    易语言报错:无法定位链接器!请检查 toolslink.ini 中的配置是否正确。 静态连接失败
    关于a标签的onclick和href谁先执行的问题
    关于a标签的onclick和href谁先执行的问题
    关于a标签的onclick和href谁先执行的问题
    易语言-子程序的参数属性
    SQL 随机取出一条数据
    为梦想,每天坚持30分钟
  • 原文地址:https://www.cnblogs.com/zhangyunlin/p/6167709.html
Copyright © 2011-2022 走看看