| From 6e52acb78212f9cfc7fb843fb01cb197a06d1b3e Mon Sep 17 00:00:00 2001 |
| From: Rob Herring <rob.herring@calxeda.com> |
| Date: Wed, 28 Sep 2011 21:25:31 -0500 |
| Subject: ARM: gic: add irq_domain support |
| |
| Convert the gic interrupt controller to use irq domains in preparation |
| for device-tree binding and MULTI_IRQ. This allows for translation between |
| GIC interrupt IDs and Linux irq numbers. |
| |
| The meaning of irq_offset has changed. It now is just the number of skipped |
| GIC interrupt IDs for the controller. It will be 16 for primary GIC and 32 |
| for secondary GICs. |
| |
| Signed-off-by: Rob Herring <rob.herring@calxeda.com> |
| Cc: Marc Zyngier <marc.zyngier@arm.com> |
| Reviewed-by: Jamie Iles <jamie@jamieiles.com> |
| Tested-by: Thomas Abraham <thomas.abraham@linaro.org> |
| Acked-by: Grant Likely <grant.likely@secretlab.ca> |
| (cherry picked from commit 4294f8baaf174c9aa57886e7ed27caf4b02578f6) |
| |
| Signed-off-by: Simon Horman <horms@verge.net.au> |
| --- |
| arch/arm/common/Kconfig | 1 + |
| arch/arm/common/gic.c | 118 +++++++++++++++++------------------- |
| arch/arm/include/asm/hardware/gic.h | 7 ++- |
| 3 files changed, 63 insertions(+), 63 deletions(-) |
| |
| diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig |
| index 4b71766..74df9ca 100644 |
| --- a/arch/arm/common/Kconfig |
| +++ b/arch/arm/common/Kconfig |
| @@ -1,4 +1,5 @@ |
| config ARM_GIC |
| + select IRQ_DOMAIN |
| bool |
| |
| config ARM_VIC |
| diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c |
| index 5f7b4e2..7286e89 100644 |
| --- a/arch/arm/common/gic.c |
| +++ b/arch/arm/common/gic.c |
| @@ -24,10 +24,12 @@ |
| */ |
| #include <linux/init.h> |
| #include <linux/kernel.h> |
| +#include <linux/export.h> |
| #include <linux/list.h> |
| #include <linux/smp.h> |
| #include <linux/cpumask.h> |
| #include <linux/io.h> |
| +#include <linux/irqdomain.h> |
| #include <linux/interrupt.h> |
| #include <linux/percpu.h> |
| #include <linux/slab.h> |
| @@ -74,8 +76,7 @@ static inline void __iomem *gic_cpu_base(struct irq_data *d) |
| |
| static inline unsigned int gic_irq(struct irq_data *d) |
| { |
| - struct gic_chip_data *gic_data = irq_data_get_irq_chip_data(d); |
| - return d->irq - gic_data->irq_offset; |
| + return d->hwirq; |
| } |
| |
| /* |
| @@ -83,7 +84,7 @@ static inline unsigned int gic_irq(struct irq_data *d) |
| */ |
| static void gic_mask_irq(struct irq_data *d) |
| { |
| - u32 mask = 1 << (d->irq % 32); |
| + u32 mask = 1 << (gic_irq(d) % 32); |
| |
| spin_lock(&irq_controller_lock); |
| writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_ENABLE_CLEAR + (gic_irq(d) / 32) * 4); |
| @@ -94,7 +95,7 @@ static void gic_mask_irq(struct irq_data *d) |
| |
| static void gic_unmask_irq(struct irq_data *d) |
| { |
| - u32 mask = 1 << (d->irq % 32); |
| + u32 mask = 1 << (gic_irq(d) % 32); |
| |
| spin_lock(&irq_controller_lock); |
| if (gic_arch_extn.irq_unmask) |
| @@ -175,7 +176,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, |
| bool force) |
| { |
| void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3); |
| - unsigned int shift = (d->irq % 4) * 8; |
| + unsigned int shift = (gic_irq(d) % 4) * 8; |
| unsigned int cpu = cpumask_any_and(mask_val, cpu_online_mask); |
| u32 val, mask, bit; |
| |
| @@ -252,7 +253,7 @@ static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) |
| if (gic_irq == 1023) |
| goto out; |
| |
| - cascade_irq = gic_irq + chip_data->irq_offset; |
| + cascade_irq = irq_domain_to_irq(&chip_data->domain, gic_irq); |
| if (unlikely(gic_irq < 32 || gic_irq > 1020 || cascade_irq >= NR_IRQS)) |
| do_bad_IRQ(cascade_irq, desc); |
| else |
| @@ -284,14 +285,14 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) |
| irq_set_chained_handler(irq, gic_handle_cascade_irq); |
| } |
| |
| -static void __init gic_dist_init(struct gic_chip_data *gic, |
| - unsigned int irq_start) |
| +static void __init gic_dist_init(struct gic_chip_data *gic) |
| { |
| - unsigned int gic_irqs, irq_limit, i; |
| + unsigned int i, irq; |
| u32 cpumask; |
| + unsigned int gic_irqs = gic->gic_irqs; |
| + struct irq_domain *domain = &gic->domain; |
| void __iomem *base = gic->dist_base; |
| u32 cpu = 0; |
| - u32 nrppis = 0, ppi_base = 0; |
| |
| #ifdef CONFIG_SMP |
| cpu = cpu_logical_map(smp_processor_id()); |
| @@ -304,34 +305,6 @@ static void __init gic_dist_init(struct gic_chip_data *gic, |
| writel_relaxed(0, base + GIC_DIST_CTRL); |
| |
| /* |
| - * Find out how many interrupts are supported. |
| - * The GIC only supports up to 1020 interrupt sources. |
| - */ |
| - gic_irqs = readl_relaxed(base + GIC_DIST_CTR) & 0x1f; |
| - gic_irqs = (gic_irqs + 1) * 32; |
| - if (gic_irqs > 1020) |
| - gic_irqs = 1020; |
| - |
| - gic->gic_irqs = gic_irqs; |
| - |
| - /* |
| - * Nobody would be insane enough to use PPIs on a secondary |
| - * GIC, right? |
| - */ |
| - if (gic == &gic_data[0]) { |
| - nrppis = (32 - irq_start) & 31; |
| - |
| - /* The GIC only supports up to 16 PPIs. */ |
| - if (nrppis > 16) |
| - BUG(); |
| - |
| - ppi_base = gic->irq_offset + 32 - nrppis; |
| - } |
| - |
| - pr_info("Configuring GIC with %d sources (%d PPIs)\n", |
| - gic_irqs, (gic == &gic_data[0]) ? nrppis : 0); |
| - |
| - /* |
| * Set all global interrupts to be level triggered, active low. |
| */ |
| for (i = 32; i < gic_irqs; i += 16) |
| @@ -357,29 +330,20 @@ static void __init gic_dist_init(struct gic_chip_data *gic, |
| writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32); |
| |
| /* |
| - * Limit number of interrupts registered to the platform maximum |
| - */ |
| - irq_limit = gic->irq_offset + gic_irqs; |
| - if (WARN_ON(irq_limit > NR_IRQS)) |
| - irq_limit = NR_IRQS; |
| - |
| - /* |
| * Setup the Linux IRQ subsystem. |
| */ |
| - for (i = 0; i < nrppis; i++) { |
| - int ppi = i + ppi_base; |
| - |
| - irq_set_percpu_devid(ppi); |
| - irq_set_chip_and_handler(ppi, &gic_chip, |
| - handle_percpu_devid_irq); |
| - irq_set_chip_data(ppi, gic); |
| - set_irq_flags(ppi, IRQF_VALID | IRQF_NOAUTOEN); |
| - } |
| - |
| - for (i = irq_start + nrppis; i < irq_limit; i++) { |
| - irq_set_chip_and_handler(i, &gic_chip, handle_fasteoi_irq); |
| - irq_set_chip_data(i, gic); |
| - set_irq_flags(i, IRQF_VALID | IRQF_PROBE); |
| + irq_domain_for_each_irq(domain, i, irq) { |
| + if (i < 32) { |
| + irq_set_percpu_devid(irq); |
| + irq_set_chip_and_handler(irq, &gic_chip, |
| + handle_percpu_devid_irq); |
| + set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN); |
| + } else { |
| + irq_set_chip_and_handler(irq, &gic_chip, |
| + handle_fasteoi_irq); |
| + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); |
| + } |
| + irq_set_chip_data(irq, gic); |
| } |
| |
| writel_relaxed(1, base + GIC_DIST_CTRL); |
| @@ -591,23 +555,53 @@ static void __init gic_pm_init(struct gic_chip_data *gic) |
| } |
| #endif |
| |
| +const struct irq_domain_ops gic_irq_domain_ops = { |
| +}; |
| + |
| void __init gic_init(unsigned int gic_nr, unsigned int irq_start, |
| void __iomem *dist_base, void __iomem *cpu_base) |
| { |
| struct gic_chip_data *gic; |
| + struct irq_domain *domain; |
| + int gic_irqs; |
| |
| BUG_ON(gic_nr >= MAX_GIC_NR); |
| |
| gic = &gic_data[gic_nr]; |
| + domain = &gic->domain; |
| gic->dist_base = dist_base; |
| gic->cpu_base = cpu_base; |
| - gic->irq_offset = (irq_start - 1) & ~31; |
| |
| - if (gic_nr == 0) |
| + /* |
| + * For primary GICs, skip over SGIs. |
| + * For secondary GICs, skip over PPIs, too. |
| + */ |
| + if (gic_nr == 0) { |
| gic_cpu_base_addr = cpu_base; |
| + domain->hwirq_base = 16; |
| + irq_start = (irq_start & ~31) + 16; |
| + } else |
| + domain->hwirq_base = 32; |
| + |
| + /* |
| + * Find out how many interrupts are supported. |
| + * The GIC only supports up to 1020 interrupt sources. |
| + */ |
| + gic_irqs = readl_relaxed(dist_base + GIC_DIST_CTR) & 0x1f; |
| + gic_irqs = (gic_irqs + 1) * 32; |
| + if (gic_irqs > 1020) |
| + gic_irqs = 1020; |
| + gic->gic_irqs = gic_irqs; |
| + |
| + domain->nr_irq = gic_irqs - domain->hwirq_base; |
| + domain->irq_base = irq_alloc_descs(-1, irq_start, domain->nr_irq, |
| + numa_node_id()); |
| + domain->priv = gic; |
| + domain->ops = &gic_irq_domain_ops; |
| + irq_domain_add(domain); |
| |
| gic_chip.flags |= gic_arch_extn.flags; |
| - gic_dist_init(gic, irq_start); |
| + gic_dist_init(gic); |
| gic_cpu_init(gic); |
| gic_pm_init(gic); |
| } |
| diff --git a/arch/arm/include/asm/hardware/gic.h b/arch/arm/include/asm/hardware/gic.h |
| index a61f11e..06a540b 100644 |
| --- a/arch/arm/include/asm/hardware/gic.h |
| +++ b/arch/arm/include/asm/hardware/gic.h |
| @@ -33,6 +33,9 @@ |
| #define GIC_DIST_SOFTINT 0xf00 |
| |
| #ifndef __ASSEMBLY__ |
| +#include <linux/irqdomain.h> |
| +struct device_node; |
| + |
| extern void __iomem *gic_cpu_base_addr; |
| extern struct irq_chip gic_arch_extn; |
| |
| @@ -43,7 +46,6 @@ void gic_cascade_irq(unsigned int gic_nr, unsigned int irq); |
| void gic_raise_softirq(const struct cpumask *mask, unsigned int irq); |
| |
| struct gic_chip_data { |
| - unsigned int irq_offset; |
| void __iomem *dist_base; |
| void __iomem *cpu_base; |
| #ifdef CONFIG_CPU_PM |
| @@ -53,6 +55,9 @@ struct gic_chip_data { |
| u32 __percpu *saved_ppi_enable; |
| u32 __percpu *saved_ppi_conf; |
| #endif |
| +#ifdef CONFIG_IRQ_DOMAIN |
| + struct irq_domain domain; |
| +#endif |
| unsigned int gic_irqs; |
| }; |
| #endif |
| -- |
| 1.7.10.2.565.gbd578b5 |
| |