summaryrefslogtreecommitdiff
path: root/drivers/irqchip/irq-vic.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/irqchip/irq-vic.c')
-rw-r--r--drivers/irqchip/irq-vic.c60
1 files changed, 53 insertions, 7 deletions
diff --git a/drivers/irqchip/irq-vic.c b/drivers/irqchip/irq-vic.c
index 473f09a74d4d..37dab0b472cd 100644
--- a/drivers/irqchip/irq-vic.c
+++ b/drivers/irqchip/irq-vic.c
@@ -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;
}