diff options
author | Haojian Zhuang <haojian.zhuang@gmail.com> | 2013-04-21 04:53:02 -0400 |
---|---|---|
committer | Haojian Zhuang <haojian.zhuang@gmail.com> | 2013-08-24 05:42:09 -0400 |
commit | 0f374561b50df8f29e3427717b35dd57c7ac4ca4 (patch) | |
tree | d5b5275b3c77e80d6c9c01aacc8fb47e06e33489 /drivers | |
parent | c052d13c08b793a13cf0158feca324417bf9ca4b (diff) |
irqchip: mmp: support irqchip
Support IRQCHIP & CONFIG_MULTI_IRQ_HANDLER in irq-mmp driver.
Signed-off-by: Haojian Zhuang <haojian.zhuang@gmail.com>
Reviewed-by: Daniel Drake <dsd@laptop.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/irqchip/irq-mmp.c | 280 |
1 files changed, 165 insertions, 115 deletions
diff --git a/drivers/irqchip/irq-mmp.c b/drivers/irqchip/irq-mmp.c index dab6def93190..84d51ff836ee 100644 --- a/drivers/irqchip/irq-mmp.c +++ b/drivers/irqchip/irq-mmp.c | |||
@@ -21,6 +21,9 @@ | |||
21 | #include <linux/of_address.h> | 21 | #include <linux/of_address.h> |
22 | #include <linux/of_irq.h> | 22 | #include <linux/of_irq.h> |
23 | 23 | ||
24 | #include <asm/exception.h> | ||
25 | #include <asm/mach/irq.h> | ||
26 | |||
24 | #include <mach/irqs.h> | 27 | #include <mach/irqs.h> |
25 | 28 | ||
26 | #ifdef CONFIG_CPU_MMP2 | 29 | #ifdef CONFIG_CPU_MMP2 |
@@ -30,8 +33,17 @@ | |||
30 | #include <mach/pm-pxa910.h> | 33 | #include <mach/pm-pxa910.h> |
31 | #endif | 34 | #endif |
32 | 35 | ||
36 | #include "irqchip.h" | ||
37 | |||
33 | #define MAX_ICU_NR 16 | 38 | #define MAX_ICU_NR 16 |
34 | 39 | ||
40 | #define PJ1_INT_SEL 0x10c | ||
41 | #define PJ4_INT_SEL 0x104 | ||
42 | |||
43 | /* bit fields in PJ1_INT_SEL and PJ4_INT_SEL */ | ||
44 | #define SEL_INT_PENDING (1 << 6) | ||
45 | #define SEL_INT_NUM_MASK 0x3f | ||
46 | |||
35 | struct icu_chip_data { | 47 | struct icu_chip_data { |
36 | int nr_irqs; | 48 | int nr_irqs; |
37 | unsigned int virq_base; | 49 | unsigned int virq_base; |
@@ -52,7 +64,7 @@ struct mmp_intc_conf { | |||
52 | unsigned int conf_mask; | 64 | unsigned int conf_mask; |
53 | }; | 65 | }; |
54 | 66 | ||
55 | void __iomem *mmp_icu_base; | 67 | static void __iomem *mmp_icu_base; |
56 | static struct icu_chip_data icu_data[MAX_ICU_NR]; | 68 | static struct icu_chip_data icu_data[MAX_ICU_NR]; |
57 | static int max_icu_nr; | 69 | static int max_icu_nr; |
58 | 70 | ||
@@ -191,6 +203,32 @@ static struct mmp_intc_conf mmp2_conf = { | |||
191 | .conf_mask = 0x7f, | 203 | .conf_mask = 0x7f, |
192 | }; | 204 | }; |
193 | 205 | ||
206 | static asmlinkage void __exception_irq_entry | ||
207 | mmp_handle_irq(struct pt_regs *regs) | ||
208 | { | ||
209 | int irq, hwirq; | ||
210 | |||
211 | hwirq = readl_relaxed(mmp_icu_base + PJ1_INT_SEL); | ||
212 | if (!(hwirq & SEL_INT_PENDING)) | ||
213 | return; | ||
214 | hwirq &= SEL_INT_NUM_MASK; | ||
215 | irq = irq_find_mapping(icu_data[0].domain, hwirq); | ||
216 | handle_IRQ(irq, regs); | ||
217 | } | ||
218 | |||
219 | static asmlinkage void __exception_irq_entry | ||
220 | mmp2_handle_irq(struct pt_regs *regs) | ||
221 | { | ||
222 | int irq, hwirq; | ||
223 | |||
224 | hwirq = readl_relaxed(mmp_icu_base + PJ4_INT_SEL); | ||
225 | if (!(hwirq & SEL_INT_PENDING)) | ||
226 | return; | ||
227 | hwirq &= SEL_INT_NUM_MASK; | ||
228 | irq = irq_find_mapping(icu_data[0].domain, hwirq); | ||
229 | handle_IRQ(irq, regs); | ||
230 | } | ||
231 | |||
194 | /* MMP (ARMv5) */ | 232 | /* MMP (ARMv5) */ |
195 | void __init icu_init_irq(void) | 233 | void __init icu_init_irq(void) |
196 | { | 234 | { |
@@ -212,6 +250,7 @@ void __init icu_init_irq(void) | |||
212 | set_irq_flags(irq, IRQF_VALID); | 250 | set_irq_flags(irq, IRQF_VALID); |
213 | } | 251 | } |
214 | irq_set_default_host(icu_data[0].domain); | 252 | irq_set_default_host(icu_data[0].domain); |
253 | set_handle_irq(mmp_handle_irq); | ||
215 | #ifdef CONFIG_CPU_PXA910 | 254 | #ifdef CONFIG_CPU_PXA910 |
216 | icu_irq_chip.irq_set_wake = pxa910_set_wake; | 255 | icu_irq_chip.irq_set_wake = pxa910_set_wake; |
217 | #endif | 256 | #endif |
@@ -318,144 +357,155 @@ void __init mmp2_init_icu(void) | |||
318 | set_irq_flags(irq, IRQF_VALID); | 357 | set_irq_flags(irq, IRQF_VALID); |
319 | } | 358 | } |
320 | irq_set_default_host(icu_data[0].domain); | 359 | irq_set_default_host(icu_data[0].domain); |
360 | set_handle_irq(mmp2_handle_irq); | ||
321 | #ifdef CONFIG_CPU_MMP2 | 361 | #ifdef CONFIG_CPU_MMP2 |
322 | icu_irq_chip.irq_set_wake = mmp2_set_wake; | 362 | icu_irq_chip.irq_set_wake = mmp2_set_wake; |
323 | #endif | 363 | #endif |
324 | } | 364 | } |
325 | 365 | ||
326 | #ifdef CONFIG_OF | 366 | #ifdef CONFIG_OF |
327 | static const struct of_device_id intc_ids[] __initconst = { | 367 | static int __init mmp_init_bases(struct device_node *node) |
328 | { .compatible = "mrvl,mmp-intc", .data = &mmp_conf }, | ||
329 | { .compatible = "mrvl,mmp2-intc", .data = &mmp2_conf }, | ||
330 | {} | ||
331 | }; | ||
332 | |||
333 | static const struct of_device_id mmp_mux_irq_match[] __initconst = { | ||
334 | { .compatible = "mrvl,mmp2-mux-intc" }, | ||
335 | {} | ||
336 | }; | ||
337 | |||
338 | int __init mmp2_mux_init(struct device_node *parent) | ||
339 | { | 368 | { |
340 | struct device_node *node; | 369 | int ret, nr_irqs, irq, i = 0; |
341 | const struct of_device_id *of_id; | ||
342 | struct resource res; | ||
343 | int i, irq_base, ret, irq; | ||
344 | u32 nr_irqs, mfp_irq; | ||
345 | 370 | ||
346 | node = parent; | 371 | ret = of_property_read_u32(node, "mrvl,intc-nr-irqs", &nr_irqs); |
347 | max_icu_nr = 1; | 372 | if (ret) { |
348 | for (i = 1; i < MAX_ICU_NR; i++) { | 373 | pr_err("Not found mrvl,intc-nr-irqs property\n"); |
349 | node = of_find_matching_node(node, mmp_mux_irq_match); | 374 | return ret; |
350 | if (!node) | 375 | } |
351 | break; | ||
352 | of_id = of_match_node(&mmp_mux_irq_match[0], node); | ||
353 | ret = of_property_read_u32(node, "mrvl,intc-nr-irqs", | ||
354 | &nr_irqs); | ||
355 | if (ret) { | ||
356 | pr_err("Not found mrvl,intc-nr-irqs property\n"); | ||
357 | ret = -EINVAL; | ||
358 | goto err; | ||
359 | } | ||
360 | ret = of_address_to_resource(node, 0, &res); | ||
361 | if (ret < 0) { | ||
362 | pr_err("Not found reg property\n"); | ||
363 | ret = -EINVAL; | ||
364 | goto err; | ||
365 | } | ||
366 | icu_data[i].reg_status = mmp_icu_base + res.start; | ||
367 | ret = of_address_to_resource(node, 1, &res); | ||
368 | if (ret < 0) { | ||
369 | pr_err("Not found reg property\n"); | ||
370 | ret = -EINVAL; | ||
371 | goto err; | ||
372 | } | ||
373 | icu_data[i].reg_mask = mmp_icu_base + res.start; | ||
374 | icu_data[i].cascade_irq = irq_of_parse_and_map(node, 0); | ||
375 | if (!icu_data[i].cascade_irq) { | ||
376 | ret = -EINVAL; | ||
377 | goto err; | ||
378 | } | ||
379 | 376 | ||
380 | irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0); | 377 | mmp_icu_base = of_iomap(node, 0); |
381 | if (irq_base < 0) { | 378 | if (!mmp_icu_base) { |
382 | pr_err("Failed to allocate IRQ numbers for mux intc\n"); | 379 | pr_err("Failed to get interrupt controller register\n"); |
383 | ret = irq_base; | 380 | return -ENOMEM; |
381 | } | ||
382 | |||
383 | icu_data[0].virq_base = 0; | ||
384 | icu_data[0].domain = irq_domain_add_linear(node, nr_irqs, | ||
385 | &mmp_irq_domain_ops, | ||
386 | &icu_data[0]); | ||
387 | for (irq = 0; irq < nr_irqs; irq++) { | ||
388 | ret = irq_create_mapping(icu_data[0].domain, irq); | ||
389 | if (!ret) { | ||
390 | pr_err("Failed to mapping hwirq\n"); | ||
384 | goto err; | 391 | goto err; |
385 | } | 392 | } |
386 | if (!of_property_read_u32(node, "mrvl,clr-mfp-irq", | 393 | if (!irq) |
387 | &mfp_irq)) { | 394 | icu_data[0].virq_base = ret; |
388 | icu_data[i].clr_mfp_irq_base = irq_base; | ||
389 | icu_data[i].clr_mfp_hwirq = mfp_irq; | ||
390 | } | ||
391 | irq_set_chained_handler(icu_data[i].cascade_irq, | ||
392 | icu_mux_irq_demux); | ||
393 | icu_data[i].nr_irqs = nr_irqs; | ||
394 | icu_data[i].virq_base = irq_base; | ||
395 | icu_data[i].domain = irq_domain_add_legacy(node, nr_irqs, | ||
396 | irq_base, 0, | ||
397 | &mmp_irq_domain_ops, | ||
398 | &icu_data[i]); | ||
399 | for (irq = irq_base; irq < irq_base + nr_irqs; irq++) | ||
400 | icu_mask_irq(irq_get_irq_data(irq)); | ||
401 | } | 395 | } |
402 | max_icu_nr = i; | 396 | icu_data[0].nr_irqs = nr_irqs; |
403 | return 0; | 397 | return 0; |
404 | err: | 398 | err: |
405 | of_node_put(node); | 399 | if (icu_data[0].virq_base) { |
406 | max_icu_nr = i; | 400 | for (i = 0; i < irq; i++) |
407 | return ret; | 401 | irq_dispose_mapping(icu_data[0].virq_base + i); |
402 | } | ||
403 | irq_domain_remove(icu_data[0].domain); | ||
404 | iounmap(mmp_icu_base); | ||
405 | return -EINVAL; | ||
408 | } | 406 | } |
409 | 407 | ||
410 | void __init mmp_dt_irq_init(void) | 408 | static int __init mmp_of_init(struct device_node *node, |
409 | struct device_node *parent) | ||
411 | { | 410 | { |
412 | struct device_node *node; | 411 | int ret; |
413 | const struct of_device_id *of_id; | ||
414 | struct mmp_intc_conf *conf; | ||
415 | int nr_irqs, irq_base, ret, irq; | ||
416 | |||
417 | node = of_find_matching_node(NULL, intc_ids); | ||
418 | if (!node) { | ||
419 | pr_err("Failed to find interrupt controller in arch-mmp\n"); | ||
420 | return; | ||
421 | } | ||
422 | of_id = of_match_node(intc_ids, node); | ||
423 | conf = of_id->data; | ||
424 | 412 | ||
425 | ret = of_property_read_u32(node, "mrvl,intc-nr-irqs", &nr_irqs); | 413 | ret = mmp_init_bases(node); |
414 | if (ret < 0) | ||
415 | return ret; | ||
416 | |||
417 | icu_data[0].conf_enable = mmp_conf.conf_enable; | ||
418 | icu_data[0].conf_disable = mmp_conf.conf_disable; | ||
419 | icu_data[0].conf_mask = mmp_conf.conf_mask; | ||
420 | irq_set_default_host(icu_data[0].domain); | ||
421 | set_handle_irq(mmp_handle_irq); | ||
422 | max_icu_nr = 1; | ||
423 | return 0; | ||
424 | } | ||
425 | IRQCHIP_DECLARE(mmp_intc, "mrvl,mmp-intc", mmp_of_init); | ||
426 | |||
427 | static int __init mmp2_of_init(struct device_node *node, | ||
428 | struct device_node *parent) | ||
429 | { | ||
430 | int ret; | ||
431 | |||
432 | ret = mmp_init_bases(node); | ||
433 | if (ret < 0) | ||
434 | return ret; | ||
435 | |||
436 | icu_data[0].conf_enable = mmp2_conf.conf_enable; | ||
437 | icu_data[0].conf_disable = mmp2_conf.conf_disable; | ||
438 | icu_data[0].conf_mask = mmp2_conf.conf_mask; | ||
439 | irq_set_default_host(icu_data[0].domain); | ||
440 | set_handle_irq(mmp2_handle_irq); | ||
441 | max_icu_nr = 1; | ||
442 | return 0; | ||
443 | } | ||
444 | IRQCHIP_DECLARE(mmp2_intc, "mrvl,mmp2-intc", mmp2_of_init); | ||
445 | |||
446 | static int __init mmp2_mux_of_init(struct device_node *node, | ||
447 | struct device_node *parent) | ||
448 | { | ||
449 | struct resource res; | ||
450 | int i, ret, irq, j = 0; | ||
451 | u32 nr_irqs, mfp_irq; | ||
452 | |||
453 | if (!parent) | ||
454 | return -ENODEV; | ||
455 | |||
456 | i = max_icu_nr; | ||
457 | ret = of_property_read_u32(node, "mrvl,intc-nr-irqs", | ||
458 | &nr_irqs); | ||
426 | if (ret) { | 459 | if (ret) { |
427 | pr_err("Not found mrvl,intc-nr-irqs property\n"); | 460 | pr_err("Not found mrvl,intc-nr-irqs property\n"); |
428 | return; | 461 | return -EINVAL; |
429 | } | 462 | } |
430 | 463 | ret = of_address_to_resource(node, 0, &res); | |
431 | mmp_icu_base = of_iomap(node, 0); | 464 | if (ret < 0) { |
432 | if (!mmp_icu_base) { | 465 | pr_err("Not found reg property\n"); |
433 | pr_err("Failed to get interrupt controller register\n"); | 466 | return -EINVAL; |
434 | return; | ||
435 | } | 467 | } |
436 | 468 | icu_data[i].reg_status = mmp_icu_base + res.start; | |
437 | irq_base = irq_alloc_descs(-1, 0, nr_irqs - NR_IRQS_LEGACY, 0); | 469 | ret = of_address_to_resource(node, 1, &res); |
438 | if (irq_base < 0) { | 470 | if (ret < 0) { |
439 | pr_err("Failed to allocate IRQ numbers\n"); | 471 | pr_err("Not found reg property\n"); |
440 | goto err; | 472 | return -EINVAL; |
441 | } else if (irq_base != NR_IRQS_LEGACY) { | ||
442 | pr_err("ICU's irqbase should be started from 0\n"); | ||
443 | goto err; | ||
444 | } | 473 | } |
445 | icu_data[0].conf_enable = conf->conf_enable; | 474 | icu_data[i].reg_mask = mmp_icu_base + res.start; |
446 | icu_data[0].conf_disable = conf->conf_disable; | 475 | icu_data[i].cascade_irq = irq_of_parse_and_map(node, 0); |
447 | icu_data[0].conf_mask = conf->conf_mask; | 476 | if (!icu_data[i].cascade_irq) |
448 | icu_data[0].nr_irqs = nr_irqs; | 477 | return -EINVAL; |
449 | icu_data[0].virq_base = 0; | 478 | |
450 | icu_data[0].domain = irq_domain_add_legacy(node, nr_irqs, 0, 0, | 479 | icu_data[i].virq_base = 0; |
480 | icu_data[i].domain = irq_domain_add_linear(node, nr_irqs, | ||
451 | &mmp_irq_domain_ops, | 481 | &mmp_irq_domain_ops, |
452 | &icu_data[0]); | 482 | &icu_data[i]); |
453 | irq_set_default_host(icu_data[0].domain); | 483 | for (irq = 0; irq < nr_irqs; irq++) { |
454 | for (irq = 0; irq < nr_irqs; irq++) | 484 | ret = irq_create_mapping(icu_data[i].domain, irq); |
455 | icu_mask_irq(irq_get_irq_data(irq)); | 485 | if (!ret) { |
456 | mmp2_mux_init(node); | 486 | pr_err("Failed to mapping hwirq\n"); |
457 | return; | 487 | goto err; |
488 | } | ||
489 | if (!irq) | ||
490 | icu_data[i].virq_base = ret; | ||
491 | } | ||
492 | icu_data[i].nr_irqs = nr_irqs; | ||
493 | if (!of_property_read_u32(node, "mrvl,clr-mfp-irq", | ||
494 | &mfp_irq)) { | ||
495 | icu_data[i].clr_mfp_irq_base = icu_data[i].virq_base; | ||
496 | icu_data[i].clr_mfp_hwirq = mfp_irq; | ||
497 | } | ||
498 | irq_set_chained_handler(icu_data[i].cascade_irq, | ||
499 | icu_mux_irq_demux); | ||
500 | max_icu_nr++; | ||
501 | return 0; | ||
458 | err: | 502 | err: |
459 | iounmap(mmp_icu_base); | 503 | if (icu_data[i].virq_base) { |
504 | for (j = 0; j < irq; j++) | ||
505 | irq_dispose_mapping(icu_data[i].virq_base + j); | ||
506 | } | ||
507 | irq_domain_remove(icu_data[i].domain); | ||
508 | return -EINVAL; | ||
460 | } | 509 | } |
510 | IRQCHIP_DECLARE(mmp2_mux_intc, "mrvl,mmp2-mux-intc", mmp2_mux_of_init); | ||
461 | #endif | 511 | #endif |