Marvell-linux研究—irq.c源代码分析
转载时请注明出处和作者联系方式:http://blog.csdn.net/absurd
作者联系方式:李先静 <xianjimli at hotmail dot com>
更新时间:2007-7-13
轮询、中断和DMA是外设与CPU通信的三种基本方式。在PXA3xx中,中断分为基本中断源(Primary Sources of interrupts)和二级中断源(Secondary Sources of interrupts)。多个二级中断源共享一个基本中断源(Primary Sources of interrupts),比如32路DMA通道共享一个基本中断源,发生中断时,光知道是DMA中断还不够,需要查询DMA相关寄存器,以确定具体的中断源。
通过编程可以控制基本中断源(Primary Sources of interrupts)的优先级和类型(IRQ/FIQ),二级中断源主要是软件上的概念,CPU不需要关心这些,甚至不知道所谓二级中断源的存在,所以不能设置二级中断源的优先级和类型(IRQ/FIQ)。
下面我们来看mach-pxa/irq.c中的代码:
35 static void pxa_mask_low_irq(unsigned int irq) 36 { 37 #ifdef CONFIG_CPU_MONAHANS 38 unsigned int temp = ~(1 << (irq + PXA_IRQ_SKIP)); 39 __asm__ __volatile__ ("/n/ 40 mrc p6, 0, r1, c1, c0, 0 @ Read out ICMR/n/ 41 and r1, r1, %0 /n/ 42 mcr p6, 0, r1, c1, c0, 0 @ Write back" 43 : 44 :"r"(temp) 45 :"r1"); 46 #else 47 ICMR &= ~(1 << (irq + PXA_IRQ_SKIP)); 48 #endif 49 } 50 51 static void pxa_unmask_low_irq(unsigned int irq) 52 { 53 #ifdef CONFIG_CPU_MONAHANS 54 unsigned int temp = (1 << (irq + PXA_IRQ_SKIP)); 55 __asm__ __volatile__ ("/n/ 56 mrc p6, 0, r1, c1, c0, 0 @ Read out ICMR/n/ 57 orr r1, r1, %0 /n/ 58 mcr p6, 0, r1, c1, c0, 0 @ Write back" 59 : 60 :"r"(temp) 61 :"r1"); 62 #else 63 ICMR |= (1 << (irq + PXA_IRQ_SKIP)); 64 #endif 65 } 66 67 static struct irqchip pxa_internal_chip_low = { 68 .ack = pxa_mask_low_irq, 69 .mask = pxa_mask_low_irq, 70 .unmask = pxa_unmask_low_irq, 71 };
79 static void pxa_mask_high_irq(unsigned int irq) 80 { 81 #ifdef CONFIG_CPU_MONAHANS 82 unsigned int temp = ~(1 << (irq - 32 + PXA_IRQ_SKIP)); 83 __asm__ __volatile__ ("/n/ 84 mrc p6, 0, r1, c7, c0, 0 @ Read out ICMR2/n/ 85 and r1, r1, %0 /n/ 86 mcr p6, 0, r1, c7, c0, 0 @ Write back" 87 : 88 :"r"(temp) 89 :"r1"); 90 #else 91 ICMR2 &= ~(1 << (irq - 32 + PXA_IRQ_SKIP)); 92 #endif 93 } 94 95 static void pxa_unmask_high_irq(unsigned int irq) 96 { 97 #ifdef CONFIG_CPU_MONAHANS 98 unsigned int temp = (1 << (irq - 32 + PXA_IRQ_SKIP)); 99 __asm__ __volatile__ ("/n/ 100 mrc p6, 0, r1, c7, c0, 0 @ Read out ICMR2/n/ 101 orr r1, r1, %0 /n/ 102 mcr p6, 0, r1, c7, c0, 0 @ Write back" 103 : 104 :"r"(temp) 105 :"r1"); 106 107 #else 108 ICMR2 |= (1 << (irq - 32 + PXA_IRQ_SKIP)); 109 #endif 110 } 111 112 static struct irqchip pxa_internal_chip_high = { 113 .ack = pxa_mask_high_irq, 114 .mask = pxa_mask_high_irq, 115 .unmask = pxa_unmask_high_irq, 116 }; 117 118 #endif
|
ICMR是中断屏蔽寄存器,它的位域决定哪些中断被禁止,哪些中断被启用。在PXA3xx中,中断屏蔽寄存器由两个32位的寄存器组成,最多允许64个基本中断源(实际上没有那么多,有几个位保留给将来用)。这里low是指低32位的中断,high是指高32位的中断,它们与中断优先级无关。ICMR控制低位中断,而ICMR2控制高位中断。
操作寄存器的方式有两种,一种是通过协处理器指令读写,读取用mrc指令,写入用mcr指令。操作中断相关寄存器使用6号协处理,中断寄存器与协处理寄存器对应关系可以参考表12-1。上面的汇编代码就是通过协处理器操作中断寄存器的。
另外一种方式是通过内存映射的I/O寄存器,它的形式和读取普通内存相同,使用方法简单直观,但与协处理器方式相比,它的速度稍慢。上面的else宏中的语句就属于这种方式。
130 static int pxa_gpio_irq_type(unsigned int irq, unsigned int type) 131 { 132 int gpio, idx; 133 134 gpio = IRQ_TO_GPIO(irq); 135 idx = gpio >> 5; 136 137 if (type == IRQT_PROBE) { 138 /* Don't mess with enabled GPIOs using preconfigured edges or 139 GPIOs set to alternate function during probe */ 140 if ((GPIO_IRQ_rising_edge[idx] | GPIO_IRQ_falling_edge[idx]) & 141 GPIO_bit(gpio)) 142 return 0; 143 #ifndef CONFIG_CPU_MONAHANS 144 if (GAFR(gpio) & (0x3 << (((gpio) & 0xf)*2))) 145 return 0; 146 #endif 147 type = __IRQT_RISEDGE | __IRQT_FALEDGE; 148 } 149 150 /* printk(KERN_DEBUG "IRQ%d (GPIO%d): ", irq, gpio); */ 151 152 #ifndef CONFIG_CPU_MONAHANS 153 pxa_gpio_mode(gpio | GPIO_IN); 154 #else 155 G CDR(gpio) &= (1 << (gpio & 0x1f)); /* Set Direction Input */ 156 #endif 157 158 if (type & __IRQT_RISEDGE) { 159 __set_bit (gpio, GPIO_IRQ_rising_edge); 160 } 161 else { 162 __clear_bit (gpio, GPIO_IRQ_rising_edge); 163 } 164 165 if (type & __IRQT_FALEDGE) { 166 __set_bit (gpio, GPIO_IRQ_falling_edge); 167 } 168 else { 169 __clear_bit (gpio, GPIO_IRQ_falling_edge); 170 } 171 172 GRER(gpio) = GPIO_IRQ_rising_edge[idx] & GPIO_IRQ_mask[idx]; 173 GFER(gpio) = GPIO_IRQ_falling_edge[idx] & GPIO_IRQ_mask[idx]; 174 return 0; 175 }
|
这个函数用来设置GPIO中断的类型,也就是触发中断的条件,上升沿触发、下降沿触发、两者同时触发、或者禁止触发,这些设置通过写GRER和GFER两个寄存器生效,同时会保存到GPIO_IRQ_rising_edge和GPIO_IRQ_falling_edge两个变量里。
181 static void pxa_ack_low_gpio(unsigned int irq) 182 { 183 GEDR0 = (1 << (irq - IRQ_GPIO0)); 184 } 185 186 static struct irqchip pxa_low_gpio_chip = { 187 .ack = pxa_ack_low_gpio, 188 .mask = pxa_mask_low_irq, 189 .unmask = pxa_unmask_low_irq, 190 .set_type = pxa_gpio_irq_type, 191 };
|
GPIO不都一样吗?为什么还来个low_gpio呢?原来GPIO0和GPIO1是独自占用基本中断源(Primary Sources of interrupts)的,而其它GPIO则共享10号中断。所以把GPIO0和GPIO1称为low_gpio,而把其它GPIO称为muxed_gpio。
Irqchip中的ack函数,用来确认中断被处理了,通常用来清除二级中断的状态,这里清除GEDR中相应的位。
197 static void pxa_gpio_demux_handler(unsigned int irq, struct irqdesc *desc, 198 struct pt_regs *regs) 199 { 200 unsigned int mask; 201 int loop; 202 203 do { 204 loop = 0; 205 206 mask = GEDR0 & ~3; 207 if (mask) { 208 GEDR0 = mask; 209 irq = IRQ_GPIO(2); 210 desc = irq_desc + irq; 211 mask >>= 2; 212 do { 213 if (mask & 1) 214 desc_handle_irq(irq, desc, regs); 215 irq++; 216 desc++; 217 mask >>= 1; 218 } while (mask); 219 loop = 1; 220 } 221 222 mask = GEDR1; 223 if (mask) { 224 GEDR1 = mask; 225 irq = IRQ_GPIO(32); 226 desc = irq_desc + irq; 227 do { 228 if (mask & 1) 229 desc_handle_irq(irq, desc, regs); 230 irq++; 231 desc++; 232 mask >>= 1; 233 } while (mask); 234 loop = 1; 235 } 236 237 mask = GEDR2; 238 if (mask) { 239 GEDR2 = mask; 240 irq = IRQ_GPIO(64); 241 desc = irq_desc + irq; 242 do { 243 if (mask & 1) 244 desc_handle_irq(irq, desc, regs); 245 irq++; 246 desc++; 247 mask >>= 1; 248 } while (mask); 249 loop = 1; 250 } 251 252 #if PXA_LAST_GPIO >= 96 253 mask = GEDR3; 254 if (mask) { 255 GEDR3 = mask; 256 irq = IRQ_GPIO(96); 257 desc = irq_desc + irq; 258 do { 259 if (mask & 1) 260 desc_handle_irq(irq, desc, regs); 261 irq++; 262 desc++; 263 mask >>= 1; 264 } while (mask); 265 loop = 1; 266 } 267 #endif 268 } while (loop); 269 }
|
这函数用来调用所有GPIO(>=2)的中断处理函数,总共有4个GEDR寄存器,它循环检查所有状态,直到处理全部GPIO为止。外层循环看似有点奇怪,其实它的目的是保证,把最近发生的中断也处理掉,即使这个中断发生在当前函数执行过程之中。
279 static void pxa_mask_muxed_gpio(unsigned int irq) 280 { 281 int gpio = irq - IRQ_GPIO(2) + 2; 282 283 u32 tem=0; 284 if ( GRER(gpio) & GPIO_bit(gpio)) { 285 __set_bit (gpio, GPIO_IRQ_rising_edge); 286 } else{ 287 __clear_bit (gpio, GPIO_IRQ_rising_edge); 288 } 289 290 if (GFER(gpio) & GPIO_bit(gpio)) { 291 __set_bit (gpio, GPIO_IRQ_falling_edge); 292 } else{ 293 __clear_bit (gpio, GPIO_IRQ_falling_edge); 294 } 295 296 __clear_bit(gpio, GPIO_IRQ_mask); 297 GRER(gpio) &= ~GPIO_bit(gpio); 298 GFER(gpio) &= ~GPIO_bit(gpio); 299 300 tem = GRER(gpio); 301 tem = GFER(gpio); 302 } 303 304 static void pxa_unmask_muxed_gpio(unsigned int irq) 305 { 306 int gpio = irq - IRQ_GPIO(2) + 2; 307 int idx = gpio >> 5; 308 u32 tem=0; 309 __set_bit(gpio, GPIO_IRQ_mask); 310 GRER(gpio) = GPIO_IRQ_rising_edge[idx] & GPIO_IRQ_mask[idx]; 311 GFER(gpio) = GPIO_IRQ_falling_edge[idx] & GPIO_IRQ_mask[idx]; 312 tem = GRER(gpio); 313 } 314 315 static struct irqchip pxa_muxed_gpio_chip = { 316 .ack = pxa_ack_muxed_gpio, 317 .mask = pxa_mask_muxed_gpio, 318 .unmask = pxa_unmask_muxed_gpio, 319 .set_type = pxa_gpio_irq_type, 320 };
|
pxa_mask_muxed_gpio保存GPIO当前中断设置,然后屏蔽相应的中断。pxa_unmask_muxed_gpio允许GPIO中断,至于中断方式,还要由GPIO_IRQ_rising_edge和GPIO_IRQ_falling_edge中的值决定。
323 void __init pxa_init_irq(void) 324 { 325 int irq; 326 327 #ifdef CONFIG_CPU_MONAHANS 328 __asm__ __volatile__ ("/n/ 329 mov r0, #0 /n/ 330 mcr p6, 0, r0, c1, c0, 0 @ ICMR=0 /n/ 331 mcr p6, 0, r0, c2, c0, 0 @ ICLR=0" 332 : 333 : 334 : "r0"); 335 #else 336 /* disable all IRQs */ 337 ICMR = 0; 338 339 /* all IRQs are IRQ, not FIQ */ 340 ICLR = 0; 341 #endif 342 /* clear all GPIO edge detects */ 343 GFER0 = 0; 344 GFER1 = 0; 345 GFER2 = 0; 346 GRER0 = 0; 347 GRER1 = 0; 348 GRER2 = 0; 349 GEDR0 = GEDR0; 350 GEDR1 = GEDR1; 351 GEDR2 = GEDR2; 352 353 #if defined(CONFIG_PXA27x) || defined(CONFIG_CPU_MONAHANS) 354 #ifdef CONFIG_CPU_MONAHANS 355 __asm__ __volatile__ ("/n/ 356 mov r0, #0 /n/ 357 mcr p6, 0, r0, c7, c0, 0 @ ICMR2=0 /n/ 358 mcr p6, 0, r0, c8, c0, 0 @ ICLR2=0" 359 : 360 : 361 : "r0"); 362 #else 363 /* And similarly for the extra regs on the PXA27x */ 364 ICMR2 = 0; 365 ICLR2 = 0; 366 #endif 367 368 GFER3 = 0; 369 GRER3 = 0; 370 GEDR3 = GEDR3; 371 #endif /*#if defined(CONFIG_PXA27x) || defined(CONFIG_CPU_MONAHANS) */ 372 373 /* only unmasked interrupts kick us out of idle */ 374 ICCR = 1; 375 376 /* GPIO 0 and 1 must have their mask bit always set */ 377 GPIO_IRQ_mask[0] = 3; 378 379 for (irq = PXA_IRQ(PXA_IRQ_SKIP); irq <= PXA_IRQ(31); irq++) { 380 set_irq_chip(irq, &pxa_internal_chip_low); 381 set_irq_handler(irq, do_level_IRQ); 382 set_irq_flags(irq, IRQF_VALID); 383 } 384 385 #if PXA_INTERNAL_IRQS > 32 386 for (irq = PXA_IRQ(32); irq < PXA_IRQ(PXA_INTERNAL_IRQS); irq++) { 387 set_irq_chip(irq, &pxa_internal_chip_high); 388 set_irq_handler(irq, do_level_IRQ); 389 set_irq_flags(irq, IRQF_VALID); 390 } 391 #endif 392 393 for (irq = IRQ_GPIO0; irq <= IRQ_GPIO1; irq++) { 394 set_irq_chip(irq, &pxa_low_gpio_chip); 395 set_irq_handler(irq, do_edge_IRQ); 396 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); 397 } 398 399 for (irq = IRQ_GPIO(2); irq <= IRQ_GPIO(PXA_LAST_GPIO); irq++) { 400 set_irq_chip(irq, &pxa_muxed_gpio_chip); 401 set_irq_handler(irq, do_edge_IRQ); 402 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); 403 } 404 405 /* Install handler for GPIO>=2 edge detect interrupts */ 406 set_irq_chip(IRQ_GPIO_2_x, &pxa_internal_chip_low); 407 set_irq_chained_handler(IRQ_GPIO_2_x, pxa_gpio_demux_handler); 408 }
|
323-341 它把ICMR清零,即屏蔽所有低位中断。把ICLR清零,即把所有低位中断设置为IRQ。353-366以同样的方式去设置高位中断,不知道为什么不和低位设置放在一起,这里好像与时序也没有什么关系啊。
343-351 清除前96个GPIO的中断触发条件。
368-370 清除后32个GPIO的中断触发条件。
对GEDR的操作,这里的代码可能有些让人不解。其实很简单,原因是对GEDR的写操作有点特别,写1是把它清零,写0对它没有影响,把GEDR的值读出来,再写回去,这样原先为1的被清零,为0的保持不变。
374 把ICCR中的DIM置1,在低功耗模式下,它只允许未屏蔽的中断唤醒CPU。
377 前面一行的注释说了等于没说,为什么要把GPIO0和GPIO1屏蔽掉呢?原因很简单,它们使用基本中断源,如果不屏蔽,基本中断源和二级中断源同时发生,会造成不必要的混乱。
379-408 设置中断处理函数。
~~end~~