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

来源:互联网 发布:手机网速测试软件 编辑:程序博客网 时间:2024/05/16 16:24
导读:
  Marvell-linux研究—gpio.c源代码分析
  
  转载时请注明出处和作者联系方式:http://blog.csdn.net/absurd
  作者联系方式:李先静
  更新时间: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 }
  
  该函数用于设置输出的数据,可以设置输出高电平或者低电平,它与设置数据流动方向的函数类似,只是操作GPCR和GPSR两个寄存器,这里不再多说。让人费解是,不就是输出的数据嘛,为什么要叫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 }
  
  该函数用于设置是否启用上升沿触发中断,它与设置数据流动方向的函数类似,只是操作GCRER和GSRER两个寄存器。如果启用,则输入数据从低电平进入高电平时触发中断。
  
  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 }
  
  该函数用于设置是否启用下降沿触发中断,它与设置数据流动方向的函数类似,只是操作GCFER和GSFER两个寄存器。如果启用,则输入数据从高电平进入低电平时触发中断。由于该函数与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的设置,主要是在电源管理中,用于suspend和resume。
  
  在PXA3xxx中,GPIO实际上已经是一个逻辑上的概念,它不但可以作为通用的IO,可以作为专用的IO Pin,这可以通过程序设置,在Multi-Function Pin中,我们将继续研究。
  
  ~~end~~
  Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1679007

本文转自
http://blog.csdn.net/absurd/archive/2007/07/04/1679007.aspx
原创粉丝点击