aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/irqchip/irq-armada-370-xp.c
diff options
context:
space:
mode:
authorEzequiel Garcia <ezequiel.garcia@free-electrons.com>2014-02-10 15:00:02 -0500
committerJason Cooper <jason@lakedaemon.net>2014-02-22 01:12:29 -0500
commitbc69b8adfe221def02ea10f7b9ab32e80195334c (patch)
tree55ac7cb070259dee0c49555062fab25334fc91e9 /drivers/irqchip/irq-armada-370-xp.c
parent9b8cf779f93bc48c0f12ef71e5bc90fd92322656 (diff)
irqchip: armada-370-xp: Setup a chained handler for the MPIC
The new Armada 375 and Armada 38x Marvell SoCs are based on Cortex-A9 CPU cores and use the ARM GIC as their main interrupt controller. However, for various purposes (wake-up from suspend, MSI interrupts), they have kept a separate MPIC interrupt controller, acting as a slave to the GIC. This MPIC was already used as the primary controller on previous Marvell SoCs, so this commit extends the existing driver to allow the MPIC to be used as a GIC slave. Reviewed-by: Gregory CLEMENT <gregory.clement@free-electrons.com> Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com> Signed-off-by: Jason Cooper <jason@lakedaemon.net>
Diffstat (limited to 'drivers/irqchip/irq-armada-370-xp.c')
-rw-r--r--drivers/irqchip/irq-armada-370-xp.c50
1 files changed, 43 insertions, 7 deletions
diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
index 2ba5761a638e..cd79503abea9 100644
--- a/drivers/irqchip/irq-armada-370-xp.c
+++ b/drivers/irqchip/irq-armada-370-xp.c
@@ -18,6 +18,7 @@
18#include <linux/init.h> 18#include <linux/init.h>
19#include <linux/irq.h> 19#include <linux/irq.h>
20#include <linux/interrupt.h> 20#include <linux/interrupt.h>
21#include <linux/irqchip/chained_irq.h>
21#include <linux/io.h> 22#include <linux/io.h>
22#include <linux/of_address.h> 23#include <linux/of_address.h>
23#include <linux/of_irq.h> 24#include <linux/of_irq.h>
@@ -42,6 +43,7 @@
42#define ARMADA_370_XP_INT_SOURCE_CTL(irq) (0x100 + irq*4) 43#define ARMADA_370_XP_INT_SOURCE_CTL(irq) (0x100 + irq*4)
43 44
44#define ARMADA_370_XP_CPU_INTACK_OFFS (0x44) 45#define ARMADA_370_XP_CPU_INTACK_OFFS (0x44)
46#define ARMADA_375_PPI_CAUSE (0x10)
45 47
46#define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x4) 48#define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x4)
47#define ARMADA_370_XP_IN_DRBEL_MSK_OFFS (0xc) 49#define ARMADA_370_XP_IN_DRBEL_MSK_OFFS (0xc)
@@ -353,7 +355,7 @@ static struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
353}; 355};
354 356
355#ifdef CONFIG_PCI_MSI 357#ifdef CONFIG_PCI_MSI
356static void armada_370_xp_handle_msi_irq(struct pt_regs *regs) 358static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained)
357{ 359{
358 u32 msimask, msinr; 360 u32 msimask, msinr;
359 361
@@ -373,13 +375,41 @@ static void armada_370_xp_handle_msi_irq(struct pt_regs *regs)
373 375
374 irq = irq_find_mapping(armada_370_xp_msi_domain, 376 irq = irq_find_mapping(armada_370_xp_msi_domain,
375 msinr - 16); 377 msinr - 16);
376 handle_IRQ(irq, regs); 378
379 if (is_chained)
380 generic_handle_irq(irq);
381 else
382 handle_IRQ(irq, regs);
377 } 383 }
378} 384}
379#else 385#else
380static void armada_370_xp_handle_msi_irq(struct pt_regs *r) {} 386static void armada_370_xp_handle_msi_irq(struct pt_regs *r, bool b) {}
381#endif 387#endif
382 388
389static void armada_370_xp_mpic_handle_cascade_irq(unsigned int irq,
390 struct irq_desc *desc)
391{
392 struct irq_chip *chip = irq_get_chip(irq);
393 unsigned long irqmap, irqn;
394 unsigned int cascade_irq;
395
396 chained_irq_enter(chip, desc);
397
398 irqmap = readl_relaxed(per_cpu_int_base + ARMADA_375_PPI_CAUSE);
399
400 if (irqmap & BIT(0)) {
401 armada_370_xp_handle_msi_irq(NULL, true);
402 irqmap &= ~BIT(0);
403 }
404
405 for_each_set_bit(irqn, &irqmap, BITS_PER_LONG) {
406 cascade_irq = irq_find_mapping(armada_370_xp_mpic_domain, irqn);
407 generic_handle_irq(cascade_irq);
408 }
409
410 chained_irq_exit(chip, desc);
411}
412
383static asmlinkage void __exception_irq_entry 413static asmlinkage void __exception_irq_entry
384armada_370_xp_handle_irq(struct pt_regs *regs) 414armada_370_xp_handle_irq(struct pt_regs *regs)
385{ 415{
@@ -402,7 +432,7 @@ armada_370_xp_handle_irq(struct pt_regs *regs)
402 432
403 /* MSI handling */ 433 /* MSI handling */
404 if (irqnr == 1) 434 if (irqnr == 1)
405 armada_370_xp_handle_msi_irq(regs); 435 armada_370_xp_handle_msi_irq(regs, false);
406 436
407#ifdef CONFIG_SMP 437#ifdef CONFIG_SMP
408 /* IPI Handling */ 438 /* IPI Handling */
@@ -433,6 +463,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
433 struct device_node *parent) 463 struct device_node *parent)
434{ 464{
435 struct resource main_int_res, per_cpu_int_res; 465 struct resource main_int_res, per_cpu_int_res;
466 int parent_irq;
436 u32 control; 467 u32 control;
437 468
438 BUG_ON(of_address_to_resource(node, 0, &main_int_res)); 469 BUG_ON(of_address_to_resource(node, 0, &main_int_res));
@@ -461,8 +492,6 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
461 492
462 BUG_ON(!armada_370_xp_mpic_domain); 493 BUG_ON(!armada_370_xp_mpic_domain);
463 494
464 irq_set_default_host(armada_370_xp_mpic_domain);
465
466#ifdef CONFIG_SMP 495#ifdef CONFIG_SMP
467 armada_xp_mpic_smp_cpu_init(); 496 armada_xp_mpic_smp_cpu_init();
468 497
@@ -478,7 +507,14 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
478 507
479 armada_370_xp_msi_init(node, main_int_res.start); 508 armada_370_xp_msi_init(node, main_int_res.start);
480 509
481 set_handle_irq(armada_370_xp_handle_irq); 510 parent_irq = irq_of_parse_and_map(node, 0);
511 if (parent_irq <= 0) {
512 irq_set_default_host(armada_370_xp_mpic_domain);
513 set_handle_irq(armada_370_xp_handle_irq);
514 } else {
515 irq_set_chained_handler(parent_irq,
516 armada_370_xp_mpic_handle_cascade_irq);
517 }
482 518
483 return 0; 519 return 0;
484} 520}