diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-09 06:42:04 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-09 06:42:04 -0400 |
commit | 782d59c5dfc5ac39ac8cfb4c6dd40597938dde9c (patch) | |
tree | 2d831c436a1962bfe5dfdb3afeaf87c7a3e82132 /drivers | |
parent | 47137c6ba1bcde30215795f9594cea770946456b (diff) | |
parent | 2828c9cdb8bd30f49c48210c014ccdd4cb994931 (diff) |
Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull irq updates from Thomas Gleixner:
"The irq departement delivers:
- a cleanup series to get rid of mindlessly copied code.
- another bunch of new pointlessly different interrupt chip drivers.
Adding homebrewn irq chips (and timers) to SoCs must provide a
value add which is beyond the imagination of mere mortals.
- the usual SoC irq controller updates, IOW my second cat herding
project"
* 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (44 commits)
irqchip: gic-v3: Implement CPU PM notifier
irqchip: gic-v3: Refactor gic_enable_redist to support both enabling and disabling
irqchip: renesas-intc-irqpin: Add minimal runtime PM support
irqchip: renesas-intc-irqpin: Add helper variable dev = &pdev->dev
irqchip: atmel-aic5: Add sama5d4 support
irqchip: atmel-aic5: The sama5d3 has 48 IRQs
Documentation: bcm7120-l2: Add Broadcom BCM7120-style L2 binding
irqchip: bcm7120-l2: Add Broadcom BCM7120-style Level 2 interrupt controller
irqchip: renesas-irqc: Add binding docs for new R-Car Gen2 SoCs
irqchip: renesas-irqc: Add DT binding documentation
irqchip: renesas-intc-irqpin: Document SoC-specific bindings
openrisc: Get rid of handle_IRQ
arm64: Get rid of handle_IRQ
ARM: omap2: irq: Convert to handle_domain_irq
ARM: imx: tzic: Convert to handle_domain_irq
ARM: imx: avic: Convert to handle_domain_irq
irqchip: or1k-pic: Convert to handle_domain_irq
irqchip: atmel-aic5: Convert to handle_domain_irq
irqchip: atmel-aic: Convert to handle_domain_irq
irqchip: gic-v3: Convert to handle_domain_irq
...
Diffstat (limited to 'drivers')
25 files changed, 1107 insertions, 138 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 78d4ff551590..b21f12f1766d 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -118,3 +118,10 @@ config IRQ_CROSSBAR The primary irqchip invokes the crossbar's callback which inturn allocates a free irq and configures the IP. Thus the peripheral interrupts are routed to one of the free irqchip interrupt lines. + +config KEYSTONE_IRQ + tristate "Keystone 2 IRQ controller IP" + depends on ARCH_KEYSTONE + help + Support for Texas Instruments Keystone 2 IRQ controller IP which + is part of the Keystone 2 IPC mechanism diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index d0a2613c73bc..173bb5fa2cc9 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_IRQCHIP) += irqchip.o obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o +obj-$(CONFIG_ARCH_HIP04) += irq-hip04.o obj-$(CONFIG_ARCH_MMP) += irq-mmp.o obj-$(CONFIG_ARCH_MVEBU) += irq-armada-370-xp.o obj-$(CONFIG_ARCH_MXS) += irq-mxs.o @@ -34,4 +35,6 @@ obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o -obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o +obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o \ + irq-bcm7120-l2.o +obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index 574aba0eba4e..fa75a29a0408 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -393,13 +393,15 @@ static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained) if (!(msimask & BIT(msinr))) continue; - irq = irq_find_mapping(armada_370_xp_msi_domain, - msinr - 16); - - if (is_chained) + if (is_chained) { + irq = irq_find_mapping(armada_370_xp_msi_domain, + msinr - 16); generic_handle_irq(irq); - else - handle_IRQ(irq, regs); + } else { + irq = msinr - 16; + handle_domain_irq(armada_370_xp_msi_domain, + irq, regs); + } } } #else @@ -444,9 +446,8 @@ armada_370_xp_handle_irq(struct pt_regs *regs) break; if (irqnr > 1) { - irqnr = irq_find_mapping(armada_370_xp_mpic_domain, - irqnr); - handle_IRQ(irqnr, regs); + handle_domain_irq(armada_370_xp_mpic_domain, + irqnr, regs); continue; } diff --git a/drivers/irqchip/irq-atmel-aic.c b/drivers/irqchip/irq-atmel-aic.c index a82869e9fb26..9a2cf3c1a3a5 100644 --- a/drivers/irqchip/irq-atmel-aic.c +++ b/drivers/irqchip/irq-atmel-aic.c @@ -68,12 +68,10 @@ aic_handle(struct pt_regs *regs) irqnr = irq_reg_readl(gc->reg_base + AT91_AIC_IVR); irqstat = irq_reg_readl(gc->reg_base + AT91_AIC_ISR); - irqnr = irq_find_mapping(aic_domain, irqnr); - if (!irqstat) irq_reg_writel(0, gc->reg_base + AT91_AIC_EOICR); else - handle_IRQ(irqnr, regs); + handle_domain_irq(aic_domain, irqnr, regs); } static int aic_retrigger(struct irq_data *d) diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c index edb227081524..a11aae8fb006 100644 --- a/drivers/irqchip/irq-atmel-aic5.c +++ b/drivers/irqchip/irq-atmel-aic5.c @@ -78,12 +78,10 @@ aic5_handle(struct pt_regs *regs) irqnr = irq_reg_readl(gc->reg_base + AT91_AIC5_IVR); irqstat = irq_reg_readl(gc->reg_base + AT91_AIC5_ISR); - irqnr = irq_find_mapping(aic5_domain, irqnr); - if (!irqstat) irq_reg_writel(0, gc->reg_base + AT91_AIC5_EOICR); else - handle_IRQ(irqnr, regs); + handle_domain_irq(aic5_domain, irqnr, regs); } static void aic5_mask(struct irq_data *d) @@ -297,6 +295,7 @@ static void __init sama5d3_aic_irq_fixup(struct device_node *root) static const struct of_device_id __initdata aic5_irq_fixups[] = { { .compatible = "atmel,sama5d3", .data = sama5d3_aic_irq_fixup }, + { .compatible = "atmel,sama5d4", .data = sama5d3_aic_irq_fixup }, { /* sentinel */ }, }; @@ -343,7 +342,7 @@ static int __init aic5_of_init(struct device_node *node, return 0; } -#define NR_SAMA5D3_IRQS 50 +#define NR_SAMA5D3_IRQS 48 static int __init sama5d3_aic5_of_init(struct device_node *node, struct device_node *parent) @@ -351,3 +350,12 @@ static int __init sama5d3_aic5_of_init(struct device_node *node, return aic5_of_init(node, parent, NR_SAMA5D3_IRQS); } IRQCHIP_DECLARE(sama5d3_aic5, "atmel,sama5d3-aic", sama5d3_aic5_of_init); + +#define NR_SAMA5D4_IRQS 68 + +static int __init sama5d4_aic5_of_init(struct device_node *node, + struct device_node *parent) +{ + return aic5_of_init(node, parent, NR_SAMA5D4_IRQS); +} +IRQCHIP_DECLARE(sama5d4_aic5, "atmel,sama5d4-aic", sama5d4_aic5_of_init); diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c new file mode 100644 index 000000000000..b9f4fb808e49 --- /dev/null +++ b/drivers/irqchip/irq-bcm7120-l2.c @@ -0,0 +1,219 @@ +/* + * Broadcom BCM7120 style Level 2 interrupt controller driver + * + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/irqdomain.h> +#include <linux/reboot.h> +#include <linux/irqchip/chained_irq.h> + +#include "irqchip.h" + +#include <asm/mach/irq.h> + +/* Register offset in the L2 interrupt controller */ +#define IRQEN 0x00 +#define IRQSTAT 0x04 + +struct bcm7120_l2_intc_data { + void __iomem *base; + struct irq_domain *domain; + bool can_wake; + u32 irq_fwd_mask; + u32 irq_map_mask; + u32 saved_mask; +}; + +static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc) +{ + struct bcm7120_l2_intc_data *b = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + u32 status; + + chained_irq_enter(chip, desc); + + status = __raw_readl(b->base + IRQSTAT); + + if (status == 0) { + do_bad_IRQ(irq, desc); + goto out; + } + + do { + irq = ffs(status) - 1; + status &= ~(1 << irq); + generic_handle_irq(irq_find_mapping(b->domain, irq)); + } while (status); + +out: + chained_irq_exit(chip, desc); +} + +static void bcm7120_l2_intc_suspend(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct bcm7120_l2_intc_data *b = gc->private; + u32 reg; + + irq_gc_lock(gc); + /* Save the current mask and the interrupt forward mask */ + b->saved_mask = __raw_readl(b->base) | b->irq_fwd_mask; + if (b->can_wake) { + reg = b->saved_mask | gc->wake_active; + __raw_writel(reg, b->base); + } + irq_gc_unlock(gc); +} + +static void bcm7120_l2_intc_resume(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct bcm7120_l2_intc_data *b = gc->private; + + /* Restore the saved mask */ + irq_gc_lock(gc); + __raw_writel(b->saved_mask, b->base); + irq_gc_unlock(gc); +} + +static int bcm7120_l2_intc_init_one(struct device_node *dn, + struct bcm7120_l2_intc_data *data, + int irq, const __be32 *map_mask) +{ + int parent_irq; + + parent_irq = irq_of_parse_and_map(dn, irq); + if (parent_irq < 0) { + pr_err("failed to map interrupt %d\n", irq); + return parent_irq; + } + + data->irq_map_mask |= be32_to_cpup(map_mask + irq); + + irq_set_handler_data(parent_irq, data); + irq_set_chained_handler(parent_irq, bcm7120_l2_intc_irq_handle); + + return 0; +} + +int __init bcm7120_l2_intc_of_init(struct device_node *dn, + struct device_node *parent) +{ + unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; + struct bcm7120_l2_intc_data *data; + struct irq_chip_generic *gc; + struct irq_chip_type *ct; + const __be32 *map_mask; + int num_parent_irqs; + int ret = 0, len, irq; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->base = of_iomap(dn, 0); + if (!data->base) { + pr_err("failed to remap intc L2 registers\n"); + ret = -ENOMEM; + goto out_free; + } + + if (of_property_read_u32(dn, "brcm,int-fwd-mask", &data->irq_fwd_mask)) + data->irq_fwd_mask = 0; + + /* Enable all interrupt specified in the interrupt forward mask and have + * the other disabled + */ + __raw_writel(data->irq_fwd_mask, data->base + IRQEN); + + num_parent_irqs = of_irq_count(dn); + if (num_parent_irqs <= 0) { + pr_err("invalid number of parent interrupts\n"); + ret = -ENOMEM; + goto out_unmap; + } + + map_mask = of_get_property(dn, "brcm,int-map-mask", &len); + if (!map_mask || (len != (sizeof(*map_mask) * num_parent_irqs))) { + pr_err("invalid brcm,int-map-mask property\n"); + ret = -EINVAL; + goto out_unmap; + } + + for (irq = 0; irq < num_parent_irqs; irq++) { + ret = bcm7120_l2_intc_init_one(dn, data, irq, map_mask); + if (ret) + goto out_unmap; + } + + data->domain = irq_domain_add_linear(dn, 32, + &irq_generic_chip_ops, NULL); + if (!data->domain) { + ret = -ENOMEM; + goto out_unmap; + } + + ret = irq_alloc_domain_generic_chips(data->domain, 32, 1, + dn->full_name, handle_level_irq, clr, 0, + IRQ_GC_INIT_MASK_CACHE); + if (ret) { + pr_err("failed to allocate generic irq chip\n"); + goto out_free_domain; + } + + gc = irq_get_domain_generic_chip(data->domain, 0); + gc->unused = 0xfffffff & ~data->irq_map_mask; + gc->reg_base = data->base; + gc->private = data; + ct = gc->chip_types; + + ct->regs.mask = IRQEN; + ct->chip.irq_mask = irq_gc_mask_clr_bit; + ct->chip.irq_unmask = irq_gc_mask_set_bit; + ct->chip.irq_ack = irq_gc_noop; + ct->chip.irq_suspend = bcm7120_l2_intc_suspend; + ct->chip.irq_resume = bcm7120_l2_intc_resume; + + if (of_property_read_bool(dn, "brcm,irq-can-wake")) { + data->can_wake = true; + /* This IRQ chip can wake the system, set all relevant child + * interupts in wake_enabled mask + */ + gc->wake_enabled = 0xffffffff; + gc->wake_enabled &= ~gc->unused; + ct->chip.irq_set_wake = irq_gc_set_wake; + } + + pr_info("registered BCM7120 L2 intc (mem: 0x%p, parent IRQ(s): %d)\n", + data->base, num_parent_irqs); + + return 0; + +out_free_domain: + irq_domain_remove(data->domain); +out_unmap: + iounmap(data->base); +out_free: + kfree(data); + return ret; +} +IRQCHIP_DECLARE(brcmstb_l2_intc, "brcm,bcm7120-l2-intc", + bcm7120_l2_intc_of_init); diff --git a/drivers/irqchip/irq-clps711x.c b/drivers/irqchip/irq-clps711x.c index 33340dc97d1d..33127f131d78 100644 --- a/drivers/irqchip/irq-clps711x.c +++ b/drivers/irqchip/irq-clps711x.c @@ -76,24 +76,20 @@ static struct { static asmlinkage void __exception_irq_entry clps711x_irqh(struct pt_regs *regs) { - u32 irqnr, irqstat; + u32 irqstat; do { irqstat = readw_relaxed(clps711x_intc->intmr[0]) & readw_relaxed(clps711x_intc->intsr[0]); - if (irqstat) { - irqnr = irq_find_mapping(clps711x_intc->domain, - fls(irqstat) - 1); - handle_IRQ(irqnr, regs); - } + if (irqstat) + handle_domain_irq(clps711x_intc->domain, + fls(irqstat) - 1, regs); irqstat = readw_relaxed(clps711x_intc->intmr[1]) & readw_relaxed(clps711x_intc->intsr[1]); - if (irqstat) { - irqnr = irq_find_mapping(clps711x_intc->domain, - fls(irqstat) - 1 + 16); - handle_IRQ(irqnr, regs); - } + if (irqstat) + handle_domain_irq(clps711x_intc->domain, + fls(irqstat) - 1 + 16, regs); } while (irqstat); } diff --git a/drivers/irqchip/irq-gic-common.c b/drivers/irqchip/irq-gic-common.c index 60ac704d2090..61541ff24397 100644 --- a/drivers/irqchip/irq-gic-common.c +++ b/drivers/irqchip/irq-gic-common.c @@ -74,20 +74,22 @@ void __init gic_dist_config(void __iomem *base, int gic_irqs, * Set all global interrupts to be level triggered, active low. */ for (i = 32; i < gic_irqs; i += 16) - writel_relaxed(0, base + GIC_DIST_CONFIG + i / 4); + writel_relaxed(GICD_INT_ACTLOW_LVLTRIG, + base + GIC_DIST_CONFIG + i / 4); /* * Set priority on all global interrupts. */ for (i = 32; i < gic_irqs; i += 4) - writel_relaxed(0xa0a0a0a0, base + GIC_DIST_PRI + i); + writel_relaxed(GICD_INT_DEF_PRI_X4, base + GIC_DIST_PRI + i); /* * Disable all interrupts. Leave the PPI and SGIs alone * as they are enabled by redistributor registers. */ for (i = 32; i < gic_irqs; i += 32) - writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i / 8); + writel_relaxed(GICD_INT_EN_CLR_X32, + base + GIC_DIST_ENABLE_CLEAR + i / 8); if (sync_access) sync_access(); @@ -101,14 +103,15 @@ void gic_cpu_config(void __iomem *base, void (*sync_access)(void)) * Deal with the banked PPI and SGI interrupts - disable all * PPI interrupts, ensure all SGI interrupts are enabled. */ - writel_relaxed(0xffff0000, base + GIC_DIST_ENABLE_CLEAR); - writel_relaxed(0x0000ffff, base + GIC_DIST_ENABLE_SET); + writel_relaxed(GICD_INT_EN_CLR_PPI, base + GIC_DIST_ENABLE_CLEAR); + writel_relaxed(GICD_INT_EN_SET_SGI, base + GIC_DIST_ENABLE_SET); /* * Set priority on PPI and SGI interrupts */ for (i = 0; i < 32; i += 4) - writel_relaxed(0xa0a0a0a0, base + GIC_DIST_PRI + i * 4 / 4); + writel_relaxed(GICD_INT_DEF_PRI_X4, + base + GIC_DIST_PRI + i * 4 / 4); if (sync_access) sync_access(); diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index a0698b4f0303..aa17ae805a70 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -16,6 +16,7 @@ */ #include <linux/cpu.h> +#include <linux/cpu_pm.h> #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/of.h> @@ -155,7 +156,7 @@ static void gic_enable_sre(void) pr_err("GIC: unable to set SRE (disabled at EL2), panic ahead\n"); } -static void gic_enable_redist(void) +static void gic_enable_redist(bool enable) { void __iomem *rbase; u32 count = 1000000; /* 1s! */ @@ -163,20 +164,30 @@ static void gic_enable_redist(void) rbase = gic_data_rdist_rd_base(); - /* Wake up this CPU redistributor */ val = readl_relaxed(rbase + GICR_WAKER); - val &= ~GICR_WAKER_ProcessorSleep; + if (enable) + /* Wake up this CPU redistributor */ + val &= ~GICR_WAKER_ProcessorSleep; + else + val |= GICR_WAKER_ProcessorSleep; writel_relaxed(val, rbase + GICR_WAKER); - while (readl_relaxed(rbase + GICR_WAKER) & GICR_WAKER_ChildrenAsleep) { - count--; - if (!count) { - pr_err_ratelimited("redist didn't wake up...\n"); - return; - } + if (!enable) { /* Check that GICR_WAKER is writeable */ + val = readl_relaxed(rbase + GICR_WAKER); + if (!(val & GICR_WAKER_ProcessorSleep)) + return; /* No PM support in this redistributor */ + } + + while (count--) { + val = readl_relaxed(rbase + GICR_WAKER); + if (enable ^ (val & GICR_WAKER_ChildrenAsleep)) + break; cpu_relax(); udelay(1); }; + if (!count) + pr_err_ratelimited("redistributor failed to %s...\n", + enable ? "wakeup" : "sleep"); } /* @@ -261,14 +272,13 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs irqnr = gic_read_iar(); if (likely(irqnr > 15 && irqnr < 1020)) { - u64 irq = irq_find_mapping(gic_data.domain, irqnr); - if (likely(irq)) { - handle_IRQ(irq, regs); - continue; + int err; + err = handle_domain_irq(gic_data.domain, irqnr, regs); + if (err) { + WARN_ONCE(true, "Unexpected SPI received!\n"); + gic_write_eoir(irqnr); } - - WARN_ONCE(true, "Unexpected SPI received!\n"); - gic_write_eoir(irqnr); + continue; } if (irqnr < 16) { gic_write_eoir(irqnr); @@ -360,6 +370,21 @@ static int gic_populate_rdist(void) return -ENODEV; } +static void gic_cpu_sys_reg_init(void) +{ + /* Enable system registers */ + gic_enable_sre(); + + /* Set priority mask register */ + gic_write_pmr(DEFAULT_PMR_VALUE); + + /* EOI deactivates interrupt too (mode 0) */ + gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop_dir); + + /* ... and let's hit the road... */ + gic_write_grpen1(1); +} + static void gic_cpu_init(void) { void __iomem *rbase; @@ -368,23 +393,14 @@ static void gic_cpu_init(void) if (gic_populate_rdist()) return; - gic_enable_redist(); + gic_enable_redist(true); rbase = gic_data_rdist_sgi_base(); gic_cpu_config(rbase, gic_redist_wait_for_rwp); - /* Enable system registers */ - gic_enable_sre(); - - /* Set priority mask register */ - gic_write_pmr(DEFAULT_PMR_VALUE); - - /* EOI deactivates interrupt too (mode 0) */ - gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop_dir); - - /* ... and let's hit the road... */ - gic_write_grpen1(1); + /* initialise system registers */ + gic_cpu_sys_reg_init(); } #ifdef CONFIG_SMP @@ -533,6 +549,33 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, #define gic_smp_init() do { } while(0) #endif +#ifdef CONFIG_CPU_PM +static int gic_cpu_pm_notifier(struct notifier_block *self, + unsigned long cmd, void *v) +{ + if (cmd == CPU_PM_EXIT) { + gic_enable_redist(true); + gic_cpu_sys_reg_init(); + } else if (cmd == CPU_PM_ENTER) { + gic_write_grpen1(0); + gic_enable_redist(false); + } + return NOTIFY_OK; +} + +static struct notifier_block gic_cpu_pm_notifier_block = { + .notifier_call = gic_cpu_pm_notifier, +}; + +static void gic_cpu_pm_init(void) +{ + cpu_pm_register_notifier(&gic_cpu_pm_notifier_block); +} + +#else +static inline void gic_cpu_pm_init(void) { } +#endif /* CONFIG_CPU_PM */ + static struct irq_chip gic_chip = { .name = "GICv3", .irq_mask = gic_mask_irq, @@ -672,6 +715,7 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare gic_smp_init(); gic_dist_init(); gic_cpu_init(); + gic_cpu_pm_init(); return 0; diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index dda6dbc23565..f0a4800a15b0 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -270,8 +270,7 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) irqnr = irqstat & GICC_IAR_INT_ID_MASK; if (likely(irqnr > 15 && irqnr < 1021)) { - irqnr = irq_find_mapping(gic->domain, irqnr); - handle_IRQ(irqnr, regs); + handle_domain_irq(gic->domain, irqnr, regs); continue; } if (irqnr < 16) { @@ -298,8 +297,8 @@ static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) status = readl_relaxed(gic_data_cpu_base(chip_data) + GIC_CPU_INTACK); raw_spin_unlock(&irq_controller_lock); - gic_irq = (status & 0x3ff); - if (gic_irq == 1023) + gic_irq = (status & GICC_IAR_INT_ID_MASK); + if (gic_irq == GICC_INT_SPURIOUS) goto out; cascade_irq = irq_find_mapping(chip_data->domain, gic_irq); @@ -353,6 +352,21 @@ static u8 gic_get_cpumask(struct gic_chip_data *gic) return mask; } +static void gic_cpu_if_up(void) +{ + void __iomem *cpu_base = gic_data_cpu_base(&gic_data[0]); + u32 bypass = 0; + + /* + * Preserve bypass disable bits to be written back later + */ + bypass = readl(cpu_base + GIC_CPU_CTRL); + bypass &= GICC_DIS_BYPASS_MASK; + + writel_relaxed(bypass | GICC_ENABLE, cpu_base + GIC_CPU_CTRL); +} + + static void __init gic_dist_init(struct gic_chip_data *gic) { unsigned int i; @@ -360,7 +374,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic) unsigned int gic_irqs = gic->gic_irqs; void __iomem *base = gic_data_dist_base(gic); - writel_relaxed(0, base + GIC_DIST_CTRL); + writel_relaxed(GICD_DISABLE, base + GIC_DIST_CTRL); /* * Set all global interrupts to this CPU only. @@ -373,7 +387,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic) gic_dist_config(base, gic_irqs, NULL); - writel_relaxed(1, base + GIC_DIST_CTRL); + writel_relaxed(GICD_ENABLE, base + GIC_DIST_CTRL); } static void gic_cpu_init(struct gic_chip_data *gic) @@ -400,14 +414,18 @@ static void gic_cpu_init(struct gic_chip_data *gic) gic_cpu_config(dist_base, NULL); - writel_relaxed(0xf0, base + GIC_CPU_PRIMASK); - writel_relaxed(1, base + GIC_CPU_CTRL); + writel_relaxed(GICC_INT_PRI_THRESHOLD, base + GIC_CPU_PRIMASK); + gic_cpu_if_up(); } void gic_cpu_if_down(void) { void __iomem *cpu_base = gic_data_cpu_base(&gic_data[0]); - writel_relaxed(0, cpu_base + GIC_CPU_CTRL); + u32 val = 0; + + val = readl(cpu_base + GIC_CPU_CTRL); + val &= ~GICC_ENABLE; + writel_relaxed(val, cpu_base + GIC_CPU_CTRL); } #ifdef CONFIG_CPU_PM @@ -467,14 +485,14 @@ static void gic_dist_restore(unsigned int gic_nr) if (!dist_base) return; - writel_relaxed(0, dist_base + GIC_DIST_CTRL); + writel_relaxed(GICD_DISABLE, 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, + writel_relaxed(GICD_INT_DEF_PRI_X4, dist_base + GIC_DIST_PRI + i * 4); for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++) @@ -485,7 +503,7 @@ static void gic_dist_restore(unsigned int gic_nr) 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); + writel_relaxed(GICD_ENABLE, dist_base + GIC_DIST_CTRL); } static void gic_cpu_save(unsigned int gic_nr) @@ -539,10 +557,11 @@ static void gic_cpu_restore(unsigned int gic_nr) 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(GICD_INT_DEF_PRI_X4, + dist_base + GIC_DIST_PRI + i * 4); - writel_relaxed(0xf0, cpu_base + GIC_CPU_PRIMASK); - writel_relaxed(1, cpu_base + GIC_CPU_CTRL); + writel_relaxed(GICC_INT_PRI_THRESHOLD, cpu_base + GIC_CPU_PRIMASK); + gic_cpu_if_up(); } static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v) diff --git a/drivers/irqchip/irq-hip04.c b/drivers/irqchip/irq-hip04.c new file mode 100644 index 000000000000..9c8f833522e6 --- /dev/null +++ b/drivers/irqchip/irq-hip04.c @@ -0,0 +1,424 @@ +/* + * Hisilicon HiP04 INTC + * + * Copyright (C) 2002-2014 ARM Limited. + * Copyright (c) 2013-2014 Hisilicon Ltd. + * Copyright (c) 2013-2014 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Interrupt architecture for the HIP04 INTC: + * + * o There is one Interrupt Distributor, which receives interrupts + * from system devices and sends them to the Interrupt Controllers. + * + * o There is one CPU Interface per CPU, which sends interrupts sent + * by the Distributor, and interrupts generated locally, to the + * associated CPU. The base address of the CPU interface is usually + * aliased so that the same address points to different chips depending + * on the CPU it is accessed from. + * + * Note that IRQs 0-31 are special - they are local to each CPU. + * As such, the enable set/clear, pending set/clear and active bit + * registers are banked per-cpu for these sources. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/list.h> +#include <linux/smp.h> +#include <linux/cpu.h> +#include <linux/cpu_pm.h> +#include <linux/cpumask.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/irqdomain.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/irqchip/arm-gic.h> + +#include <asm/irq.h> +#include <asm/exception.h> +#include <asm/smp_plat.h> + +#include "irq-gic-common.h" +#include "irqchip.h" + +#define HIP04_MAX_IRQS 510 + +struct hip04_irq_data { + void __iomem *dist_base; + void __iomem *cpu_base; + struct irq_domain *domain; + unsigned int nr_irqs; +}; + +static DEFINE_RAW_SPINLOCK(irq_controller_lock); + +/* + * The GIC mapping of CPU interfaces does not necessarily match + * the logical CPU numbering. Let's use a mapping as returned + * by the GIC itself. + */ +#define NR_HIP04_CPU_IF 16 +static u16 hip04_cpu_map[NR_HIP04_CPU_IF] __read_mostly; + +static struct hip04_irq_data hip04_data __read_mostly; + +static inline void __iomem *hip04_dist_base(struct irq_data *d) +{ + struct hip04_irq_data *hip04_data = irq_data_get_irq_chip_data(d); + return hip04_data->dist_base; +} + +static inline void __iomem *hip04_cpu_base(struct irq_data *d) +{ + struct hip04_irq_data *hip04_data = irq_data_get_irq_chip_data(d); + return hip04_data->cpu_base; +} + +static inline unsigned int hip04_irq(struct irq_data *d) +{ + return d->hwirq; +} + +/* + * Routines to acknowledge, disable and enable interrupts + */ +static void hip04_mask_irq(struct irq_data *d) +{ + u32 mask = 1 << (hip04_irq(d) % 32); + + raw_spin_lock(&irq_controller_lock); + writel_relaxed(mask, hip04_dist_base(d) + GIC_DIST_ENABLE_CLEAR + + (hip04_irq(d) / 32) * 4); + raw_spin_unlock(&irq_controller_lock); +} + +static void hip04_unmask_irq(struct irq_data *d) +{ + u32 mask = 1 << (hip04_irq(d) % 32); + + raw_spin_lock(&irq_controller_lock); + writel_relaxed(mask, hip04_dist_base(d) + GIC_DIST_ENABLE_SET + + (hip04_irq(d) / 32) * 4); + raw_spin_unlock(&irq_controller_lock); +} + +static void hip04_eoi_irq(struct irq_data *d) +{ + writel_relaxed(hip04_irq(d), hip04_cpu_base(d) + GIC_CPU_EOI); +} + +static int hip04_irq_set_type(struct irq_data *d, unsigned int type) +{ + void __iomem *base = hip04_dist_base(d); + unsigned int irq = hip04_irq(d); + + /* Interrupt configuration for SGIs can't be changed */ + if (irq < 16) + return -EINVAL; + |