diff options
author | Grant Likely <grant.likely@secretlab.ca> | 2009-02-04 15:33:20 -0500 |
---|---|---|
committer | Grant Likely <grant.likely@secretlab.ca> | 2009-02-04 15:33:20 -0500 |
commit | 8f2558ded599c10d96a56fbf12849a27f6ab7997 (patch) | |
tree | 00e0893df8d0d5a4e9f726c65cce724a61c038ce /arch | |
parent | bc4346fe2733dcca723d6b8f188bc44b54eac847 (diff) |
powerpc/5200: Refactor mpc5200 interrupt controller driver
Rework the mpc5200-pic driver to simplify it and fix up the setting
of desc->status when set_type is called for internal IRQs (so they
are reported as level, not edge). The simplification is due to
splitting off the handling of external IRQs into a separate block
so they don't need to be handled as exceptions in the normal
CRIT, MAIN and PERP paths.
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/platforms/52xx/mpc52xx_pic.c | 147 |
1 files changed, 59 insertions, 88 deletions
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pic.c b/arch/powerpc/platforms/52xx/mpc52xx_pic.c index c0a955920508..480f806fd0a9 100644 --- a/arch/powerpc/platforms/52xx/mpc52xx_pic.c +++ b/arch/powerpc/platforms/52xx/mpc52xx_pic.c | |||
@@ -190,10 +190,10 @@ static void mpc52xx_extirq_ack(unsigned int virq) | |||
190 | 190 | ||
191 | static int mpc52xx_extirq_set_type(unsigned int virq, unsigned int flow_type) | 191 | static int mpc52xx_extirq_set_type(unsigned int virq, unsigned int flow_type) |
192 | { | 192 | { |
193 | struct irq_desc *desc = get_irq_desc(virq); | ||
194 | u32 ctrl_reg, type; | 193 | u32 ctrl_reg, type; |
195 | int irq; | 194 | int irq; |
196 | int l2irq; | 195 | int l2irq; |
196 | void *handler = handle_level_irq; | ||
197 | 197 | ||
198 | irq = irq_map[virq].hwirq; | 198 | irq = irq_map[virq].hwirq; |
199 | l2irq = irq & MPC52xx_IRQ_L2_MASK; | 199 | l2irq = irq & MPC52xx_IRQ_L2_MASK; |
@@ -201,32 +201,21 @@ static int mpc52xx_extirq_set_type(unsigned int virq, unsigned int flow_type) | |||
201 | pr_debug("%s: irq=%x. l2=%d flow_type=%d\n", __func__, irq, l2irq, flow_type); | 201 | pr_debug("%s: irq=%x. l2=%d flow_type=%d\n", __func__, irq, l2irq, flow_type); |
202 | 202 | ||
203 | switch (flow_type) { | 203 | switch (flow_type) { |
204 | case IRQF_TRIGGER_HIGH: | 204 | case IRQF_TRIGGER_HIGH: type = 0; break; |
205 | type = 0; | 205 | case IRQF_TRIGGER_RISING: type = 1; handler = handle_edge_irq; break; |
206 | break; | 206 | case IRQF_TRIGGER_FALLING: type = 2; handler = handle_edge_irq; break; |
207 | case IRQF_TRIGGER_RISING: | 207 | case IRQF_TRIGGER_LOW: type = 3; break; |
208 | type = 1; | ||
209 | break; | ||
210 | case IRQF_TRIGGER_FALLING: | ||
211 | type = 2; | ||
212 | break; | ||
213 | case IRQF_TRIGGER_LOW: | ||
214 | type = 3; | ||
215 | break; | ||
216 | default: | 208 | default: |
217 | type = 0; | 209 | type = 0; |
218 | } | 210 | } |
219 | 211 | ||
220 | desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL); | ||
221 | desc->status |= flow_type & IRQ_TYPE_SENSE_MASK; | ||
222 | if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) | ||
223 | desc->status |= IRQ_LEVEL; | ||
224 | |||
225 | ctrl_reg = in_be32(&intr->ctrl); | 212 | ctrl_reg = in_be32(&intr->ctrl); |
226 | ctrl_reg &= ~(0x3 << (22 - (l2irq * 2))); | 213 | ctrl_reg &= ~(0x3 << (22 - (l2irq * 2))); |
227 | ctrl_reg |= (type << (22 - (l2irq * 2))); | 214 | ctrl_reg |= (type << (22 - (l2irq * 2))); |
228 | out_be32(&intr->ctrl, ctrl_reg); | 215 | out_be32(&intr->ctrl, ctrl_reg); |
229 | 216 | ||
217 | __set_irq_handler_unlocked(virq, handler); | ||
218 | |||
230 | return 0; | 219 | return 0; |
231 | } | 220 | } |
232 | 221 | ||
@@ -241,6 +230,11 @@ static struct irq_chip mpc52xx_extirq_irqchip = { | |||
241 | /* | 230 | /* |
242 | * Main interrupt irq_chip | 231 | * Main interrupt irq_chip |
243 | */ | 232 | */ |
233 | static int mpc52xx_null_set_type(unsigned int virq, unsigned int flow_type) | ||
234 | { | ||
235 | return 0; /* Do nothing so that the sense mask will get updated */ | ||
236 | } | ||
237 | |||
244 | static void mpc52xx_main_mask(unsigned int virq) | 238 | static void mpc52xx_main_mask(unsigned int virq) |
245 | { | 239 | { |
246 | int irq; | 240 | int irq; |
@@ -268,6 +262,7 @@ static struct irq_chip mpc52xx_main_irqchip = { | |||
268 | .mask = mpc52xx_main_mask, | 262 | .mask = mpc52xx_main_mask, |
269 | .mask_ack = mpc52xx_main_mask, | 263 | .mask_ack = mpc52xx_main_mask, |
270 | .unmask = mpc52xx_main_unmask, | 264 | .unmask = mpc52xx_main_unmask, |
265 | .set_type = mpc52xx_null_set_type, | ||
271 | }; | 266 | }; |
272 | 267 | ||
273 | /* | 268 | /* |
@@ -300,6 +295,7 @@ static struct irq_chip mpc52xx_periph_irqchip = { | |||
300 | .mask = mpc52xx_periph_mask, | 295 | .mask = mpc52xx_periph_mask, |
301 | .mask_ack = mpc52xx_periph_mask, | 296 | .mask_ack = mpc52xx_periph_mask, |
302 | .unmask = mpc52xx_periph_unmask, | 297 | .unmask = mpc52xx_periph_unmask, |
298 | .set_type = mpc52xx_null_set_type, | ||
303 | }; | 299 | }; |
304 | 300 | ||
305 | /* | 301 | /* |
@@ -343,9 +339,19 @@ static struct irq_chip mpc52xx_sdma_irqchip = { | |||
343 | .mask = mpc52xx_sdma_mask, | 339 | .mask = mpc52xx_sdma_mask, |
344 | .unmask = mpc52xx_sdma_unmask, | 340 | .unmask = mpc52xx_sdma_unmask, |
345 | .ack = mpc52xx_sdma_ack, | 341 | .ack = mpc52xx_sdma_ack, |
342 | .set_type = mpc52xx_null_set_type, | ||
346 | }; | 343 | }; |
347 | 344 | ||
348 | /** | 345 | /** |
346 | * mpc52xx_is_extirq - Returns true if hwirq number is for an external IRQ | ||
347 | */ | ||
348 | static int mpc52xx_is_extirq(int l1, int l2) | ||
349 | { | ||
350 | return ((l1 == 0) && (l2 == 0)) || | ||
351 | ((l1 == 1) && (l2 >= 1) && (l2 <= 3)); | ||
352 | } | ||
353 | |||
354 | /** | ||
349 | * mpc52xx_irqhost_xlate - translate virq# from device tree interrupts property | 355 | * mpc52xx_irqhost_xlate - translate virq# from device tree interrupts property |
350 | */ | 356 | */ |
351 | static int mpc52xx_irqhost_xlate(struct irq_host *h, struct device_node *ct, | 357 | static int mpc52xx_irqhost_xlate(struct irq_host *h, struct device_node *ct, |
@@ -363,38 +369,23 @@ static int mpc52xx_irqhost_xlate(struct irq_host *h, struct device_node *ct, | |||
363 | 369 | ||
364 | intrvect_l1 = (int)intspec[0]; | 370 | intrvect_l1 = (int)intspec[0]; |
365 | intrvect_l2 = (int)intspec[1]; | 371 | intrvect_l2 = (int)intspec[1]; |
366 | intrvect_type = (int)intspec[2]; | 372 | intrvect_type = (int)intspec[2] & 0x3; |
367 | 373 | ||
368 | intrvect_linux = (intrvect_l1 << MPC52xx_IRQ_L1_OFFSET) & | 374 | intrvect_linux = (intrvect_l1 << MPC52xx_IRQ_L1_OFFSET) & |
369 | MPC52xx_IRQ_L1_MASK; | 375 | MPC52xx_IRQ_L1_MASK; |
370 | intrvect_linux |= intrvect_l2 & MPC52xx_IRQ_L2_MASK; | 376 | intrvect_linux |= intrvect_l2 & MPC52xx_IRQ_L2_MASK; |
371 | 377 | ||
372 | pr_debug("return %x, l1=%d, l2=%d\n", intrvect_linux, intrvect_l1, | ||
373 | intrvect_l2); | ||
374 | |||
375 | *out_hwirq = intrvect_linux; | 378 | *out_hwirq = intrvect_linux; |
376 | *out_flags = mpc52xx_map_senses[intrvect_type]; | 379 | *out_flags = IRQ_TYPE_LEVEL_LOW; |
380 | if (mpc52xx_is_extirq(intrvect_l1, intrvect_l2)) | ||
381 | *out_flags = mpc52xx_map_senses[intrvect_type]; | ||
377 | 382 | ||
383 | pr_debug("return %x, l1=%d, l2=%d\n", intrvect_linux, intrvect_l1, | ||
384 | intrvect_l2); | ||
378 | return 0; | 385 | return 0; |
379 | } | 386 | } |
380 | 387 | ||
381 | /** | 388 | /** |
382 | * mpc52xx_irqx_gettype - determine the IRQ sense type (level/edge) | ||
383 | * | ||
384 | * Only external IRQs need this. | ||
385 | */ | ||
386 | static int mpc52xx_irqx_gettype(int irq) | ||
387 | { | ||
388 | int type; | ||
389 | u32 ctrl_reg; | ||
390 | |||
391 | ctrl_reg = in_be32(&intr->ctrl); | ||
392 | type = (ctrl_reg >> (22 - irq * 2)) & 0x3; | ||
393 | |||
394 | return mpc52xx_map_senses[type]; | ||
395 | } | ||
396 | |||
397 | /** | ||
398 | * mpc52xx_irqhost_map - Hook to map from virq to an irq_chip structure | 389 | * mpc52xx_irqhost_map - Hook to map from virq to an irq_chip structure |
399 | */ | 390 | */ |
400 | static int mpc52xx_irqhost_map(struct irq_host *h, unsigned int virq, | 391 | static int mpc52xx_irqhost_map(struct irq_host *h, unsigned int virq, |
@@ -402,68 +393,46 @@ static int mpc52xx_irqhost_map(struct irq_host *h, unsigned int virq, | |||
402 | { | 393 | { |
403 | int l1irq; | 394 | int l1irq; |
404 | int l2irq; | 395 | int l2irq; |
405 | struct irq_chip *good_irqchip; | 396 | struct irq_chip *irqchip; |
406 | void *good_handle; | 397 | void *hndlr; |
407 | int type; | 398 | int type; |
399 | u32 reg; | ||
408 | 400 | ||
409 | l1irq = (irq & MPC52xx_IRQ_L1_MASK) >> MPC52xx_IRQ_L1_OFFSET; | 401 | l1irq = (irq & MPC52xx_IRQ_L1_MASK) >> MPC52xx_IRQ_L1_OFFSET; |
410 | l2irq = irq & MPC52xx_IRQ_L2_MASK; | 402 | l2irq = irq & MPC52xx_IRQ_L2_MASK; |
411 | 403 | ||
412 | /* | 404 | /* |
413 | * Most of ours IRQs will be level low | 405 | * External IRQs are handled differently by the hardware so they are |
414 | * Only external IRQs on some platform may be others | 406 | * handled by a dedicated irq_chip structure. |
415 | */ | 407 | */ |
416 | type = IRQ_TYPE_LEVEL_LOW; | 408 | if (mpc52xx_is_extirq(l1irq, l2irq)) { |
409 | reg = in_be32(&intr->ctrl); | ||
410 | type = mpc52xx_map_senses[(reg >> (22 - l2irq * 2)) & 0x3]; | ||
411 | if ((type == IRQ_TYPE_EDGE_FALLING) || | ||
412 | (type == IRQ_TYPE_EDGE_RISING)) | ||
413 | hndlr = handle_edge_irq; | ||
414 | else | ||
415 | hndlr = handle_level_irq; | ||
416 | |||
417 | set_irq_chip_and_handler(virq, &mpc52xx_extirq_irqchip, hndlr); | ||
418 | pr_debug("%s: External IRQ%i virq=%x, hw=%x. type=%x\n", | ||
419 | __func__, l2irq, virq, (int)irq, type); | ||
420 | return 0; | ||
421 | } | ||
417 | 422 | ||
423 | /* It is an internal SOC irq. Choose the correct irq_chip */ | ||
418 | switch (l1irq) { | 424 | switch (l1irq) { |
419 | case MPC52xx_IRQ_L1_CRIT: | 425 | case MPC52xx_IRQ_L1_MAIN: irqchip = &mpc52xx_main_irqchip; break; |
420 | pr_debug("%s: Critical. l2=%x\n", __func__, l2irq); | 426 | case MPC52xx_IRQ_L1_PERP: irqchip = &mpc52xx_periph_irqchip; break; |
421 | 427 | case MPC52xx_IRQ_L1_SDMA: irqchip = &mpc52xx_sdma_irqchip; break; | |
422 | BUG_ON(l2irq != 0); | ||
423 | |||
424 | type = mpc52xx_irqx_gettype(l2irq); | ||
425 | good_irqchip = &mpc52xx_extirq_irqchip; | ||
426 | break; | ||
427 | |||
428 | case MPC52xx_IRQ_L1_MAIN: | ||
429 | pr_debug("%s: Main IRQ[1-3] l2=%x\n", __func__, l2irq); | ||
430 | |||
431 | if ((l2irq >= 1) && (l2irq <= 3)) { | ||
432 | type = mpc52xx_irqx_gettype(l2irq); | ||
433 | good_irqchip = &mpc52xx_extirq_irqchip; | ||
434 | } else { | ||
435 | good_irqchip = &mpc52xx_main_irqchip; | ||
436 | } | ||
437 | break; | ||
438 | |||
439 | case MPC52xx_IRQ_L1_PERP: | ||
440 | pr_debug("%s: Peripherals. l2=%x\n", __func__, l2irq); | ||
441 | good_irqchip = &mpc52xx_periph_irqchip; | ||
442 | break; | ||
443 | |||
444 | case MPC52xx_IRQ_L1_SDMA: | ||
445 | pr_debug("%s: SDMA. l2=%x\n", __func__, l2irq); | ||
446 | good_irqchip = &mpc52xx_sdma_irqchip; | ||
447 | break; | ||
448 | |||
449 | default: | 428 | default: |
450 | pr_err("%s: invalid virq requested (0x%x)\n", __func__, virq); | 429 | pr_err("%s: invalid irq: virq=%i, l1=%i, l2=%i\n", |
430 | __func__, virq, l1irq, l2irq); | ||
451 | return -EINVAL; | 431 | return -EINVAL; |
452 | } | 432 | } |
453 | 433 | ||
454 | switch (type) { | 434 | set_irq_chip_and_handler(virq, irqchip, handle_level_irq); |
455 | case IRQ_TYPE_EDGE_FALLING: | 435 | pr_debug("%s: virq=%x, l1=%i, l2=%i\n", __func__, virq, l1irq, l2irq); |
456 | case IRQ_TYPE_EDGE_RISING: | ||
457 | good_handle = handle_edge_irq; | ||
458 | break; | ||
459 | default: | ||
460 | good_handle = handle_level_irq; | ||
461 | } | ||
462 | |||
463 | set_irq_chip_and_handler(virq, good_irqchip, good_handle); | ||
464 | |||
465 | pr_debug("%s: virq=%x, hw=%x. type=%x\n", __func__, virq, | ||
466 | (int)irq, type); | ||
467 | 436 | ||
468 | return 0; | 437 | return 0; |
469 | } | 438 | } |
@@ -502,6 +471,8 @@ void __init mpc52xx_init_irq(void) | |||
502 | panic(__FILE__ ": find_and_map failed on 'mpc5200-bestcomm'. " | 471 | panic(__FILE__ ": find_and_map failed on 'mpc5200-bestcomm'. " |
503 | "Check node !"); | 472 | "Check node !"); |
504 | 473 | ||
474 | pr_debug("MPC5200 IRQ controller mapped to 0x%p\n", intr); | ||
475 | |||
505 | /* Disable all interrupt sources. */ | 476 | /* Disable all interrupt sources. */ |
506 | out_be32(&sdma->IntPend, 0xffffffff); /* 1 means clear pending */ | 477 | out_be32(&sdma->IntPend, 0xffffffff); /* 1 means clear pending */ |
507 | out_be32(&sdma->IntMask, 0xffffffff); /* 1 means disabled */ | 478 | out_be32(&sdma->IntMask, 0xffffffff); /* 1 means disabled */ |