From 254056f3b12563c11e6dbcfad2fbfce20a4f3302 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 10 Feb 2011 12:54:10 -0800 Subject: ARM: gic: Use cpu pm notifiers to save gic state When the cpu is powered down in a low power mode, the gic cpu interface may be reset, and when the cpu cluster is powered down, the gic distributor may also be reset. This patch uses CPU_PM_ENTER and CPU_PM_EXIT notifiers to save and restore the gic cpu interface registers, and the CPU_CLUSTER_PM_ENTER and CPU_CLUSTER_PM_EXIT notifiers to save and restore the gic distributor registers. Original-author: Gary King Signed-off-by: Colin Cross Signed-off-by: Santosh Shilimkar Tested-and-Acked-by: Shawn Guo Tested-by: Vishwanath BS --- arch/arm/common/gic.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) (limited to 'arch/arm/common') diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index 3227ca952a12..66c7c4820108 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -276,6 +277,8 @@ static void __init gic_dist_init(struct gic_chip_data *gic, if (gic_irqs > 1020) gic_irqs = 1020; + gic->gic_irqs = gic_irqs; + /* * Set all global interrupts to be level triggered, active low. */ @@ -343,6 +346,189 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) writel_relaxed(1, base + GIC_CPU_CTRL); } +#ifdef CONFIG_CPU_PM +/* + * Saves the GIC distributor registers during suspend or idle. Must be called + * with interrupts disabled but before powering down the GIC. After calling + * this function, no interrupts will be delivered by the GIC, and another + * platform-specific wakeup source must be enabled. + */ +static void gic_dist_save(unsigned int gic_nr) +{ + unsigned int gic_irqs; + void __iomem *dist_base; + int i; + + if (gic_nr >= MAX_GIC_NR) + BUG(); + + gic_irqs = gic_data[gic_nr].gic_irqs; + dist_base = gic_data[gic_nr].dist_base; + + if (!dist_base) + return; + + for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++) + gic_data[gic_nr].saved_spi_conf[i] = + readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4); + + for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) + gic_data[gic_nr].saved_spi_target[i] = + readl_relaxed(dist_base + GIC_DIST_TARGET + i * 4); + + for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) + gic_data[gic_nr].saved_spi_enable[i] = + readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4); +} + +/* + * Restores the GIC distributor registers during resume or when coming out of + * idle. Must be called before enabling interrupts. If a level interrupt + * that occured while the GIC was suspended is still present, it will be + * handled normally, but any edge interrupts that occured will not be seen by + * the GIC and need to be handled by the platform-specific wakeup source. + */ +static void gic_dist_restore(unsigned int gic_nr) +{ + unsigned int gic_irqs; + unsigned int i; + void __iomem *dist_base; + + if (gic_nr >= MAX_GIC_NR) + BUG(); + + gic_irqs = gic_data[gic_nr].gic_irqs; + dist_base = gic_data[gic_nr].dist_base; + + if (!dist_base) + return; + + writel_relaxed(0, dist_base + GIC_DIST_CTRL); + + for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++) + writel_relaxed(gic_data[gic_nr].saved_spi_conf[i], + dist_base + GIC_DIST_CONFIG + i * 4); + + for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) + writel_relaxed(0xa0a0a0a0, + dist_base + GIC_DIST_PRI + i * 4); + + for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) + writel_relaxed(gic_data[gic_nr].saved_spi_target[i], + dist_base + GIC_DIST_TARGET + i * 4); + + for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) + writel_relaxed(gic_data[gic_nr].saved_spi_enable[i], + dist_base + GIC_DIST_ENABLE_SET + i * 4); + + writel_relaxed(1, dist_base + GIC_DIST_CTRL); +} + +static void gic_cpu_save(unsigned int gic_nr) +{ + int i; + u32 *ptr; + void __iomem *dist_base; + void __iomem *cpu_base; + + if (gic_nr >= MAX_GIC_NR) + BUG(); + + dist_base = gic_data[gic_nr].dist_base; + cpu_base = gic_data[gic_nr].cpu_base; + + if (!dist_base || !cpu_base) + return; + + ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable); + for (i = 0; i < DIV_ROUND_UP(32, 32); i++) + ptr[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4); + + ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf); + for (i = 0; i < DIV_ROUND_UP(32, 16); i++) + ptr[i] = readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4); + +} + +static void gic_cpu_restore(unsigned int gic_nr) +{ + int i; + u32 *ptr; + void __iomem *dist_base; + void __iomem *cpu_base; + + if (gic_nr >= MAX_GIC_NR) + BUG(); + + dist_base = gic_data[gic_nr].dist_base; + cpu_base = gic_data[gic_nr].cpu_base; + + if (!dist_base || !cpu_base) + return; + + ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable); + for (i = 0; i < DIV_ROUND_UP(32, 32); i++) + writel_relaxed(ptr[i], dist_base + GIC_DIST_ENABLE_SET + i * 4); + + ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf); + for (i = 0; i < DIV_ROUND_UP(32, 16); i++) + writel_relaxed(ptr[i], dist_base + GIC_DIST_CONFIG + i * 4); + + for (i = 0; i < DIV_ROUND_UP(32, 4); i++) + writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4); + + writel_relaxed(0xf0, cpu_base + GIC_CPU_PRIMASK); + writel_relaxed(1, cpu_base + GIC_CPU_CTRL); +} + +static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v) +{ + int i; + + for (i = 0; i < MAX_GIC_NR; i++) { + switch (cmd) { + case CPU_PM_ENTER: + gic_cpu_save(i); + break; + case CPU_PM_ENTER_FAILED: + case CPU_PM_EXIT: + gic_cpu_restore(i); + break; + case CPU_CLUSTER_PM_ENTER: + gic_dist_save(i); + break; + case CPU_CLUSTER_PM_ENTER_FAILED: + case CPU_CLUSTER_PM_EXIT: + gic_dist_restore(i); + break; + } + } + + return NOTIFY_OK; +} + +static struct notifier_block gic_notifier_block = { + .notifier_call = gic_notifier, +}; + +static void __init gic_pm_init(struct gic_chip_data *gic) +{ + gic->saved_ppi_enable = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4, + sizeof(u32)); + BUG_ON(!gic->saved_ppi_enable); + + gic->saved_ppi_conf = __alloc_percpu(DIV_ROUND_UP(32, 16) * 4, + sizeof(u32)); + BUG_ON(!gic->saved_ppi_conf); + + cpu_pm_register_notifier(&gic_notifier_block); +} +#else +static void __init gic_pm_init(struct gic_chip_data *gic) +{ +} +#endif + void __init gic_init(unsigned int gic_nr, unsigned int irq_start, void __iomem *dist_base, void __iomem *cpu_base) { @@ -360,6 +546,7 @@ void __init gic_init(unsigned int gic_nr, unsigned int irq_start, gic_dist_init(gic, irq_start); gic_cpu_init(gic); + gic_pm_init(gic); } void __cpuinit gic_secondary_init(unsigned int gic_nr) -- cgit v1.2.1 From 9c12845ee49716209cb2b087c0b47c3e37096bde Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 13 Jun 2011 00:45:59 +0000 Subject: ARM: gic: Allow gic arch extensions to provide irqchip flags Tegra can benefit from the IRQCHIP_MASK_ON_SUSPEND flag, allow it to be passed to the gic irq chip. Signed-off-by: Colin Cross Signed-off-by: Santosh Shilimkar Reviewed-by: Kevin Hilman Tested-and-Acked-by: Shawn Guo Tested-by: Vishwanath BS --- arch/arm/common/gic.c | 1 + 1 file changed, 1 insertion(+) (limited to 'arch/arm/common') diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index 66c7c4820108..734db99eaee7 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -544,6 +544,7 @@ void __init gic_init(unsigned int gic_nr, unsigned int irq_start, if (gic_nr == 0) gic_cpu_base_addr = cpu_base; + gic_chip.flags |= gic_arch_extn.flags; gic_dist_init(gic, irq_start); gic_cpu_init(gic); gic_pm_init(gic); -- cgit v1.2.1 From 267840f3397fd9f6a2bdde14de38b9d29d525d7b Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 23 Aug 2011 22:20:03 +0100 Subject: ARM: 7061/1: gic: convert logical CPU numbers into physical numbers The GIC driver must convert logical CPU numbers passed in from Linux into physical CPU numbers that are understood by the hardware. This patch uses the new cpu_logical_map macro for performing the conversion inside the GIC driver. Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/common/gic.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'arch/arm/common') diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index 3227ca952a12..666b278e56d7 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -180,7 +180,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, return -EINVAL; mask = 0xff << shift; - bit = 1 << (cpu + shift); + bit = 1 << (cpu_logical_map(cpu) + shift); spin_lock(&irq_controller_lock); val = readl_relaxed(reg) & ~mask; @@ -259,9 +259,15 @@ static void __init gic_dist_init(struct gic_chip_data *gic, unsigned int irq_start) { unsigned int gic_irqs, irq_limit, i; + u32 cpumask; void __iomem *base = gic->dist_base; - u32 cpumask = 1 << smp_processor_id(); + u32 cpu = 0; +#ifdef CONFIG_SMP + cpu = cpu_logical_map(smp_processor_id()); +#endif + + cpumask = 1 << cpu; cpumask |= cpumask << 8; cpumask |= cpumask << 16; @@ -382,7 +388,12 @@ void __cpuinit gic_enable_ppi(unsigned int irq) #ifdef CONFIG_SMP void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) { - unsigned long map = *cpus_addr(*mask); + int cpu; + unsigned long map = 0; + + /* Convert our logical CPU mask into a physical one. */ + for_each_cpu(cpu, mask) + map |= 1 << cpu_logical_map(cpu); /* * Ensure that stores to Normal memory are visible to the -- cgit v1.2.1 From 292b293ceef2eda1f96f0c90b96e954d7bdabd1c Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 20 Jul 2011 16:24:14 +0100 Subject: ARM: gic: consolidate PPI handling PPI handling is a bit of an odd beast. It uses its own low level handling code and is hardwired to the local timers (hence lacking a registration interface). Instead, switch the low handling to the normal SPI handling code. PPIs are handled by the handle_percpu_devid_irq flow. This also allows the removal of some duplicated code. Cc: Kukjin Kim Cc: David Brown Cc: Bryan Huntsman Cc: Tony Lindgren Cc: Paul Mundt Cc: Magnus Damm Cc: Thomas Gleixner Acked-by: David Brown Tested-by: David Brown Tested-by: Shawn Guo Signed-off-by: Marc Zyngier --- arch/arm/common/gic.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) (limited to 'arch/arm/common') diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index 666b278e56d7..bbea0168779b 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -28,10 +28,14 @@ #include #include #include +#include +#include +#include #include #include #include +#include static DEFINE_SPINLOCK(irq_controller_lock); @@ -255,6 +259,32 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) irq_set_chained_handler(irq, gic_handle_cascade_irq); } +#ifdef CONFIG_LOCAL_TIMERS +#define gic_ppi_handler percpu_timer_handler +#else +static irqreturn_t gic_ppi_handler(int irq, void *dev_id) +{ + return IRQ_NONE; +} +#endif + +#define PPI_IRQACT(nr) \ + { \ + .handler = gic_ppi_handler, \ + .flags = IRQF_PERCPU | IRQF_TIMER, \ + .irq = nr, \ + .name = "PPI-" # nr, \ + } + +static struct irqaction ppi_irqaction_template[16] __initdata = { + PPI_IRQACT(0), PPI_IRQACT(1), PPI_IRQACT(2), PPI_IRQACT(3), + PPI_IRQACT(4), PPI_IRQACT(5), PPI_IRQACT(6), PPI_IRQACT(7), + PPI_IRQACT(8), PPI_IRQACT(9), PPI_IRQACT(10), PPI_IRQACT(11), + PPI_IRQACT(12), PPI_IRQACT(13), PPI_IRQACT(14), PPI_IRQACT(15), +}; + +static struct irqaction *ppi_irqaction; + static void __init gic_dist_init(struct gic_chip_data *gic, unsigned int irq_start) { @@ -262,6 +292,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic, u32 cpumask; 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()); @@ -282,6 +313,33 @@ static void __init gic_dist_init(struct gic_chip_data *gic, if (gic_irqs > 1020) gic_irqs = 1020; + /* + * 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; + + ppi_irqaction = kmemdup(&ppi_irqaction_template[16 - nrppis], + sizeof(*ppi_irqaction) * nrppis, + GFP_KERNEL); + + if (nrppis && !ppi_irqaction) { + pr_err("GIC: Can't allocate PPI memory"); + nrppis = 0; + ppi_base = 0; + } + } + + 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. */ @@ -317,7 +375,22 @@ static void __init gic_dist_init(struct gic_chip_data *gic, /* * Setup the Linux IRQ subsystem. */ - for (i = irq_start; i < irq_limit; i++) { + for (i = 0; i < nrppis; i++) { + int ppi = i + ppi_base; + int err; + + 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); + + err = setup_percpu_irq(ppi, &ppi_irqaction[i]); + if (err) + pr_err("GIC: can't setup PPI%d (%d)\n", ppi, err); + } + + 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); -- cgit v1.2.1 From 28af690a284dfcb627bd69d0963db1c0f412cb8c Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 22 Jul 2011 12:52:37 +0100 Subject: ARM: gic, local timers: use the request_percpu_irq() interface This patch remove the hardcoded link between local timers and PPIs, and convert the PPI users (TWD, MCT and MSM timers) to the new *_percpu_irq interface. Also some collateral cleanup (local_timer_ack() is gone, and the interrupt handler is strictly private to each driver). PPIs are now useable for more than just the local timers. Additional testing by David Brown (msm8250 and msm8660) and Shawn Guo (imx6q). Cc: David Brown Cc: Thomas Gleixner Acked-by: David Brown Tested-by: David Brown Tested-by: Shawn Guo Signed-off-by: Marc Zyngier --- arch/arm/common/gic.c | 52 --------------------------------------------------- 1 file changed, 52 deletions(-) (limited to 'arch/arm/common') diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index bbea0168779b..a2b320503931 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -35,7 +35,6 @@ #include #include #include -#include static DEFINE_SPINLOCK(irq_controller_lock); @@ -259,32 +258,6 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) irq_set_chained_handler(irq, gic_handle_cascade_irq); } -#ifdef CONFIG_LOCAL_TIMERS -#define gic_ppi_handler percpu_timer_handler -#else -static irqreturn_t gic_ppi_handler(int irq, void *dev_id) -{ - return IRQ_NONE; -} -#endif - -#define PPI_IRQACT(nr) \ - { \ - .handler = gic_ppi_handler, \ - .flags = IRQF_PERCPU | IRQF_TIMER, \ - .irq = nr, \ - .name = "PPI-" # nr, \ - } - -static struct irqaction ppi_irqaction_template[16] __initdata = { - PPI_IRQACT(0), PPI_IRQACT(1), PPI_IRQACT(2), PPI_IRQACT(3), - PPI_IRQACT(4), PPI_IRQACT(5), PPI_IRQACT(6), PPI_IRQACT(7), - PPI_IRQACT(8), PPI_IRQACT(9), PPI_IRQACT(10), PPI_IRQACT(11), - PPI_IRQACT(12), PPI_IRQACT(13), PPI_IRQACT(14), PPI_IRQACT(15), -}; - -static struct irqaction *ppi_irqaction; - static void __init gic_dist_init(struct gic_chip_data *gic, unsigned int irq_start) { @@ -325,16 +298,6 @@ static void __init gic_dist_init(struct gic_chip_data *gic, BUG(); ppi_base = gic->irq_offset + 32 - nrppis; - - ppi_irqaction = kmemdup(&ppi_irqaction_template[16 - nrppis], - sizeof(*ppi_irqaction) * nrppis, - GFP_KERNEL); - - if (nrppis && !ppi_irqaction) { - pr_err("GIC: Can't allocate PPI memory"); - nrppis = 0; - ppi_base = 0; - } } pr_info("Configuring GIC with %d sources (%d PPIs)\n", @@ -377,17 +340,12 @@ static void __init gic_dist_init(struct gic_chip_data *gic, */ for (i = 0; i < nrppis; i++) { int ppi = i + ppi_base; - int err; 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); - - err = setup_percpu_irq(ppi, &ppi_irqaction[i]); - if (err) - pr_err("GIC: can't setup PPI%d (%d)\n", ppi, err); } for (i = irq_start + nrppis; i < irq_limit; i++) { @@ -448,16 +406,6 @@ void __cpuinit gic_secondary_init(unsigned int gic_nr) gic_cpu_init(&gic_data[gic_nr]); } -void __cpuinit gic_enable_ppi(unsigned int irq) -{ - unsigned long flags; - - local_irq_save(flags); - irq_set_status_flags(irq, IRQ_NOPROBE); - gic_unmask_irq(irq_get_irq_data(irq)); - local_irq_restore(flags); -} - #ifdef CONFIG_SMP void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) { -- cgit v1.2.1 From 4294f8baaf174c9aa57886e7ed27caf4b02578f6 Mon Sep 17 00:00:00 2001 From: Rob Herring 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 Cc: Marc Zyngier Reviewed-by: Jamie Iles Tested-by: Thomas Abraham Acked-by: Grant Likely --- arch/arm/common/Kconfig | 1 + arch/arm/common/gic.c | 118 +++++++++++++++++++++++------------------------- 2 files changed, 57 insertions(+), 62 deletions(-) (limited to 'arch/arm/common') diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig index 4b71766fb21d..74df9ca2be31 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 016c1aeb847c..ccaa1ab18de7 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -24,11 +24,13 @@ */ #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -75,8 +77,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; } /* @@ -84,7 +85,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); @@ -95,7 +96,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) @@ -176,7 +177,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; @@ -227,7 +228,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 @@ -259,14 +260,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()); @@ -278,34 +279,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. */ @@ -331,30 +304,21 @@ static void __init gic_dist_init(struct gic_chip_data *gic, for (i = 32; i < gic_irqs; i += 32) 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); @@ -566,23 +530,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); } -- cgit v1.2.1 From b3f7ed0324091e2cb23fe1b3c10570700f614014 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 28 Sep 2011 21:27:52 -0500 Subject: ARM: gic: add OF based initialization This adds ARM gic interrupt controller initialization using device tree data. The initialization function is intended to be called by of_irq_init function like this: const static struct of_device_id irq_match[] = { { .compatible = "arm,cortex-a9-gic", .data = gic_of_init, }, {} }; static void __init init_irqs(void) { of_irq_init(irq_match); } Signed-off-by: Rob Herring Reviewed-by: Jamie Iles Tested-by: Thomas Abraham Acked-by: Grant Likely --- arch/arm/common/gic.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) (limited to 'arch/arm/common') diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index ccaa1ab18de7..1333e68b1f96 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -30,6 +30,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -530,7 +533,33 @@ static void __init gic_pm_init(struct gic_chip_data *gic) } #endif +#ifdef CONFIG_OF +static int gic_irq_domain_dt_translate(struct irq_domain *d, + struct device_node *controller, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, unsigned int *out_type) +{ + if (d->of_node != controller) + return -EINVAL; + if (intsize < 3) + return -EINVAL; + + /* Get the interrupt number and add 16 to skip over SGIs */ + *out_hwirq = intspec[1] + 16; + + /* For SPIs, we need to add 16 more to get the GIC irq ID number */ + if (!intspec[0]) + *out_hwirq += 16; + + *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; + return 0; +} +#endif + const struct irq_domain_ops gic_irq_domain_ops = { +#ifdef CONFIG_OF + .dt_translate = gic_irq_domain_dt_translate, +#endif }; void __init gic_init(unsigned int gic_nr, unsigned int irq_start, @@ -608,3 +637,35 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) writel_relaxed(map << 16 | irq, gic_data[0].dist_base + GIC_DIST_SOFTINT); } #endif + +#ifdef CONFIG_OF +static int gic_cnt __initdata = 0; + +int __init gic_of_init(struct device_node *node, struct device_node *parent) +{ + void __iomem *cpu_base; + void __iomem *dist_base; + int irq; + struct irq_domain *domain = &gic_data[gic_cnt].domain; + + if (WARN_ON(!node)) + return -ENODEV; + + dist_base = of_iomap(node, 0); + WARN(!dist_base, "unable to map gic dist registers\n"); + + cpu_base = of_iomap(node, 1); + WARN(!cpu_base, "unable to map gic cpu registers\n"); + + domain->of_node = of_node_get(node); + + gic_init(gic_cnt, 16, dist_base, cpu_base); + + if (parent) { + irq = irq_of_parse_and_map(node, 0); + gic_cascade_irq(gic_cnt, irq); + } + gic_cnt++; + return 0; +} +#endif -- cgit v1.2.1 From f37a53cc5d8a8fb199e41386d125d8c2ed9e54ef Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 21 Oct 2011 17:14:27 -0500 Subject: ARM: gic: fix irq_alloc_descs handling for sparse irq Commit "ARM: gic: add irq_domain support" (b49b6ff) breaks SPARSE_IRQ on platforms with GIC. When SPARSE_IRQ is enabled, all NR_IRQS or mach_desc->nr_irqs will be allocated by arch_probe_nr_irqs(). This caused irq_alloc_descs to allocate irq_descs after the pre-allocated space. Make irq_alloc_descs search for an exact irq range and assume it has been pre-allocated on failure. For DT probing dynamic allocation is used. DT enabled platforms should set their nr_irqs to NR_IRQ_LEGACY and have all irq_chips allocate their irq_descs with irq_alloc_descs if SPARSE_IRQ is enabled. gic_init irq_start param is changed to be signed with negative meaning do dynamic Linux irq assigment. Signed-off-by: Rob Herring --- arch/arm/common/gic.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'arch/arm/common') diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index 1333e68b1f96..9d77777076f0 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -24,6 +24,7 @@ */ #include #include +#include #include #include #include @@ -562,7 +563,7 @@ const struct irq_domain_ops gic_irq_domain_ops = { #endif }; -void __init gic_init(unsigned int gic_nr, unsigned int irq_start, +void __init gic_init(unsigned int gic_nr, int irq_start, void __iomem *dist_base, void __iomem *cpu_base) { struct gic_chip_data *gic; @@ -583,7 +584,8 @@ void __init gic_init(unsigned int gic_nr, unsigned int irq_start, if (gic_nr == 0) { gic_cpu_base_addr = cpu_base; domain->hwirq_base = 16; - irq_start = (irq_start & ~31) + 16; + if (irq_start > 0) + irq_start = (irq_start & ~31) + 16; } else domain->hwirq_base = 32; @@ -598,8 +600,13 @@ void __init gic_init(unsigned int gic_nr, unsigned int irq_start, 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, + domain->irq_base = irq_alloc_descs(irq_start, 16, domain->nr_irq, numa_node_id()); + if (IS_ERR_VALUE(domain->irq_base)) { + WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n", + irq_start); + domain->irq_base = irq_start; + } domain->priv = gic; domain->ops = &gic_irq_domain_ops; irq_domain_add(domain); @@ -659,7 +666,7 @@ int __init gic_of_init(struct device_node *node, struct device_node *parent) domain->of_node = of_node_get(node); - gic_init(gic_cnt, 16, dist_base, cpu_base); + gic_init(gic_cnt, -1, dist_base, cpu_base); if (parent) { irq = irq_of_parse_and_map(node, 0); -- cgit v1.2.1