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 } |
该函数用于设置输出的数据,可以设置输出高电平或者低电平,它与设置数据流动方向的函数类似,只是操作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~~