您的位置:首页 > 运维架构 > Linux

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

2007-07-13 19:55 816 查看
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~~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: