Merge tag 'drivers-3.15' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[pandora-kernel.git] / drivers / irqchip / irq-vic.c
index 473f09a..37dab0b 100644 (file)
@@ -57,6 +57,7 @@
 
 /**
  * struct vic_device - VIC PM device
+ * @parent_irq: The parent IRQ number of the VIC if cascaded, or 0.
  * @irq: The IRQ number for the base of the VIC.
  * @base: The register base for the VIC.
  * @valid_sources: A bitmask of valid interrupts
@@ -224,6 +225,17 @@ static int handle_one_vic(struct vic_device *vic, struct pt_regs *regs)
        return handled;
 }
 
+static void vic_handle_irq_cascaded(unsigned int irq, struct irq_desc *desc)
+{
+       u32 stat, hwirq;
+       struct vic_device *vic = irq_desc_get_handler_data(desc);
+
+       while ((stat = readl_relaxed(vic->base + VIC_IRQ_STATUS))) {
+               hwirq = ffs(stat) - 1;
+               generic_handle_irq(irq_find_mapping(vic->domain, hwirq));
+       }
+}
+
 /*
  * Keep iterating over all registered VIC's until there are no pending
  * interrupts.
@@ -246,6 +258,7 @@ static struct irq_domain_ops vic_irqdomain_ops = {
 /**
  * vic_register() - Register a VIC.
  * @base: The base address of the VIC.
+ * @parent_irq: The parent IRQ if cascaded, else 0.
  * @irq: The base IRQ for the VIC.
  * @valid_sources: bitmask of valid interrupts
  * @resume_sources: bitmask of interrupts allowed for resume sources.
@@ -257,7 +270,8 @@ static struct irq_domain_ops vic_irqdomain_ops = {
  *
  * This also configures the IRQ domain for the VIC.
  */
-static void __init vic_register(void __iomem *base, unsigned int irq,
+static void __init vic_register(void __iomem *base, unsigned int parent_irq,
+                               unsigned int irq,
                                u32 valid_sources, u32 resume_sources,
                                struct device_node *node)
 {
@@ -273,15 +287,25 @@ static void __init vic_register(void __iomem *base, unsigned int irq,
        v->base = base;
        v->valid_sources = valid_sources;
        v->resume_sources = resume_sources;
-       v->irq = irq;
        set_handle_irq(vic_handle_irq);
        vic_id++;
+
+       if (parent_irq) {
+               irq_set_handler_data(parent_irq, v);
+               irq_set_chained_handler(parent_irq, vic_handle_irq_cascaded);
+       }
+
        v->domain = irq_domain_add_simple(node, fls(valid_sources), irq,
                                          &vic_irqdomain_ops, v);
        /* create an IRQ mapping for each valid IRQ */
        for (i = 0; i < fls(valid_sources); i++)
                if (valid_sources & (1 << i))
                        irq_create_mapping(v->domain, i);
+       /* If no base IRQ was passed, figure out our allocated base */
+       if (irq)
+               v->irq = irq;
+       else
+               v->irq = irq_find_mapping(v->domain, 0);
 }
 
 static void vic_ack_irq(struct irq_data *d)
@@ -409,10 +433,10 @@ static void __init vic_init_st(void __iomem *base, unsigned int irq_start,
                writel(32, base + VIC_PL190_DEF_VECT_ADDR);
        }
 
-       vic_register(base, irq_start, vic_sources, 0, node);
+       vic_register(base, 0, irq_start, vic_sources, 0, node);
 }
 
-void __init __vic_init(void __iomem *base, int irq_start,
+void __init __vic_init(void __iomem *base, int parent_irq, int irq_start,
                              u32 vic_sources, u32 resume_sources,
                              struct device_node *node)
 {
@@ -449,7 +473,7 @@ void __init __vic_init(void __iomem *base, int irq_start,
 
        vic_init2(base);
 
-       vic_register(base, irq_start, vic_sources, resume_sources, node);
+       vic_register(base, parent_irq, irq_start, vic_sources, resume_sources, node);
 }
 
 /**
@@ -462,8 +486,30 @@ void __init __vic_init(void __iomem *base, int irq_start,
 void __init vic_init(void __iomem *base, unsigned int irq_start,
                     u32 vic_sources, u32 resume_sources)
 {
-       __vic_init(base, irq_start, vic_sources, resume_sources, NULL);
+       __vic_init(base, 0, irq_start, vic_sources, resume_sources, NULL);
+}
+
+/**
+ * vic_init_cascaded() - initialise a cascaded vectored interrupt controller
+ * @base: iomem base address
+ * @parent_irq: the parent IRQ we're cascaded off
+ * @irq_start: starting interrupt number, must be muliple of 32
+ * @vic_sources: bitmask of interrupt sources to allow
+ * @resume_sources: bitmask of interrupt sources to allow for resume
+ *
+ * This returns the base for the new interrupts or negative on error.
+ */
+int __init vic_init_cascaded(void __iomem *base, unsigned int parent_irq,
+                             u32 vic_sources, u32 resume_sources)
+{
+       struct vic_device *v;
+
+       v = &vic_devices[vic_id];
+       __vic_init(base, parent_irq, 0, vic_sources, resume_sources, NULL);
+       /* Return out acquired base */
+       return v->irq;
 }
+EXPORT_SYMBOL_GPL(vic_init_cascaded);
 
 #ifdef CONFIG_OF
 int __init vic_of_init(struct device_node *node, struct device_node *parent)
@@ -485,7 +531,7 @@ int __init vic_of_init(struct device_node *node, struct device_node *parent)
        /*
         * Passing 0 as first IRQ makes the simple domain allocate descriptors
         */
-       __vic_init(regs, 0, interrupt_mask, wakeup_mask, node);
+       __vic_init(regs, 0, 0, interrupt_mask, wakeup_mask, node);
 
        return 0;
 }