From 89323f8c504a8653c66fe4a314723b36b07e29e1 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Wed, 23 Jul 2014 17:40:30 +0300 Subject: irqchip: keystone: Add irq controller ip driver On Keystone SOCs, DSP cores can send interrupts to ARM host using the IRQ controller IP. It provides 28 IRQ signals to ARM. The IRQ handler running on HOST OS can identify DSP signal source by analyzing SRCCx bits in IPCARx registers. This is one of the component used by the IPC mechanism used on Keystone SOCs. Signed-off-by: Grygorii Strashko Link: https://lkml.kernel.org/r/1406126430-9978-1-git-send-email-grygorii.strashko@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/Kconfig | 7 ++ drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-keystone.c | 232 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 240 insertions(+) create mode 100644 drivers/irqchip/irq-keystone.c (limited to 'drivers') diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index b8632bf9a7f3..c88896478e70 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -113,3 +113,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 73052ba9ca62..69fc7fc8d1bb 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -34,3 +34,4 @@ 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_KEYSTONE_IRQ) += irq-keystone.o diff --git a/drivers/irqchip/irq-keystone.c b/drivers/irqchip/irq-keystone.c new file mode 100644 index 000000000000..fea26bcfd4ea --- /dev/null +++ b/drivers/irqchip/irq-keystone.c @@ -0,0 +1,232 @@ +/* + * Texas Instruments Keystone IRQ controller IP driver + * + * Copyright (C) 2014 Texas Instruments, Inc. + * Author: Sajesh Kumar Saran + * Grygorii Strashko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "irqchip.h" + + +/* The source ID bits start from 4 to 31 (total 28 bits)*/ +#define BIT_OFS 4 +#define KEYSTONE_N_IRQ (32 - BIT_OFS) + +struct keystone_irq_device { + struct device *dev; + struct irq_chip chip; + u32 mask; + u32 irq; + struct irq_domain *irqd; + struct regmap *devctrl_regs; + u32 devctrl_offset; +}; + +static inline u32 keystone_irq_readl(struct keystone_irq_device *kirq) +{ + int ret; + u32 val = 0; + + ret = regmap_read(kirq->devctrl_regs, kirq->devctrl_offset, &val); + if (ret < 0) + dev_dbg(kirq->dev, "irq read failed ret(%d)\n", ret); + return val; +} + +static inline void +keystone_irq_writel(struct keystone_irq_device *kirq, u32 value) +{ + int ret; + + ret = regmap_write(kirq->devctrl_regs, kirq->devctrl_offset, value); + if (ret < 0) + dev_dbg(kirq->dev, "irq write failed ret(%d)\n", ret); +} + +static void keystone_irq_setmask(struct irq_data *d) +{ + struct keystone_irq_device *kirq = irq_data_get_irq_chip_data(d); + + kirq->mask |= BIT(d->hwirq); + dev_dbg(kirq->dev, "mask %lu [%x]\n", d->hwirq, kirq->mask); +} + +static void keystone_irq_unmask(struct irq_data *d) +{ + struct keystone_irq_device *kirq = irq_data_get_irq_chip_data(d); + + kirq->mask &= ~BIT(d->hwirq); + dev_dbg(kirq->dev, "unmask %lu [%x]\n", d->hwirq, kirq->mask); +} + +static void keystone_irq_ack(struct irq_data *d) +{ + /* nothing to do here */ +} + +static void keystone_irq_handler(unsigned irq, struct irq_desc *desc) +{ + struct keystone_irq_device *kirq = irq_desc_get_handler_data(desc); + unsigned long pending; + int src, virq; + + dev_dbg(kirq->dev, "start irq %d\n", irq); + + chained_irq_enter(irq_desc_get_chip(desc), desc); + + pending = keystone_irq_readl(kirq); + keystone_irq_writel(kirq, pending); + + dev_dbg(kirq->dev, "pending 0x%lx, mask 0x%x\n", pending, kirq->mask); + + pending = (pending >> BIT_OFS) & ~kirq->mask; + + dev_dbg(kirq->dev, "pending after mask 0x%lx\n", pending); + + for (src = 0; src < KEYSTONE_N_IRQ; src++) { + if (BIT(src) & pending) { + virq = irq_find_mapping(kirq->irqd, src); + dev_dbg(kirq->dev, "dispatch bit %d, virq %d\n", + src, virq); + if (!virq) + dev_warn(kirq->dev, "sporious irq detected hwirq %d, virq %d\n", + src, virq); + generic_handle_irq(virq); + } + } + + chained_irq_exit(irq_desc_get_chip(desc), desc); + + dev_dbg(kirq->dev, "end irq %d\n", irq); +} + +static int keystone_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct keystone_irq_device *kirq = h->host_data; + + irq_set_chip_data(virq, kirq); + irq_set_chip_and_handler(virq, &kirq->chip, handle_level_irq); + set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); + return 0; +} + +static struct irq_domain_ops keystone_irq_ops = { + .map = keystone_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int keystone_irq_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct keystone_irq_device *kirq; + int ret; + + if (np == NULL) + return -EINVAL; + + kirq = devm_kzalloc(dev, sizeof(*kirq), GFP_KERNEL); + if (!kirq) + return -ENOMEM; + + kirq->devctrl_regs = + syscon_regmap_lookup_by_phandle(np, "ti,syscon-dev"); + if (IS_ERR(kirq->devctrl_regs)) + return PTR_ERR(kirq->devctrl_regs); + + ret = of_property_read_u32_index(np, "ti,syscon-dev", 1, + &kirq->devctrl_offset); + if (ret) { + dev_err(dev, "couldn't read the devctrl_offset offset!\n"); + return ret; + } + + kirq->irq = platform_get_irq(pdev, 0); + if (kirq->irq < 0) { + dev_err(dev, "no irq resource %d\n", kirq->irq); + return kirq->irq; + } + + kirq->dev = dev; + kirq->mask = ~0x0; + kirq->chip.name = "keystone-irq"; + kirq->chip.irq_ack = keystone_irq_ack; + kirq->chip.irq_mask = keystone_irq_setmask; + kirq->chip.irq_unmask = keystone_irq_unmask; + + kirq->irqd = irq_domain_add_linear(np, KEYSTONE_N_IRQ, + &keystone_irq_ops, kirq); + if (!kirq->irqd) { + dev_err(dev, "IRQ domain registration failed\n"); + return -ENODEV; + } + + platform_set_drvdata(pdev, kirq); + + irq_set_chained_handler(kirq->irq, keystone_irq_handler); + irq_set_handler_data(kirq->irq, kirq); + + /* clear all source bits */ + keystone_irq_writel(kirq, ~0x0); + + dev_info(dev, "irqchip registered, nr_irqs %u\n", KEYSTONE_N_IRQ); + + return 0; +} + +static int keystone_irq_remove(struct platform_device *pdev) +{ + struct keystone_irq_device *kirq = platform_get_drvdata(pdev); + int hwirq; + + for (hwirq = 0; hwirq < KEYSTONE_N_IRQ; hwirq++) + irq_dispose_mapping(irq_find_mapping(kirq->irqd, hwirq)); + + irq_domain_remove(kirq->irqd); + return 0; +} + +static const struct of_device_id keystone_irq_dt_ids[] = { + { .compatible = "ti,keystone-irq", }, + {}, +}; +MODULE_DEVICE_TABLE(of, keystone_irq_dt_ids); + +static struct platform_driver keystone_irq_device_driver = { + .probe = keystone_irq_probe, + .remove = keystone_irq_remove, + .driver = { + .name = "keystone_irq", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(keystone_irq_dt_ids), + } +}; + +module_platform_driver(keystone_irq_device_driver); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_AUTHOR("Sajesh Kumar Saran"); +MODULE_AUTHOR("Grygorii Strashko"); +MODULE_DESCRIPTION("Keystone IRQ chip"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From e5f81539f657af7e9f54ea37986fde8f92acef22 Mon Sep 17 00:00:00 2001 From: Feng Kan Date: Wed, 30 Jul 2014 14:56:58 -0700 Subject: irqchip: gic: Replace hex numbers with defines. This is to cleanup some hex numbers used in the code and replace them with defines to make the code cleaner. Signed-off-by: Feng Kan Reviewed-by: Anup Patel Link: https://lkml.kernel.org/r/1406757419-18729-2-git-send-email-fkan@apm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-gic-common.c | 15 +++++++++------ drivers/irqchip/irq-gic.c | 25 +++++++++++++------------ include/linux/irqchip/arm-gic.h | 15 +++++++++++++++ 3 files changed, 37 insertions(+), 18 deletions(-) (limited to 'drivers') 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.c b/drivers/irqchip/irq-gic.c index 4b959e606fe8..35847453cecb 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -298,8 +298,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); @@ -360,7 +360,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 +373,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,8 +400,8 @@ 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); + writel_relaxed(GICC_ENABLE, base + GIC_CPU_CTRL); } void gic_cpu_if_down(void) @@ -467,14 +467,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 +485,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 +539,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); + writel_relaxed(GICC_ENABLE, cpu_base + GIC_CPU_CTRL); } static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v) diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h index 45e2d8c15bd2..5cb9d41af5be 100644 --- a/include/linux/irqchip/arm-gic.h +++ b/include/linux/irqchip/arm-gic.h @@ -21,7 +21,10 @@ #define GIC_CPU_ACTIVEPRIO 0xd0 #define GIC_CPU_IDENT 0xfc +#define GICC_ENABLE 0x1 +#define GICC_INT_PRI_THRESHOLD 0xf0 #define GICC_IAR_INT_ID_MASK 0x3ff +#define GICC_INT_SPURIOUS 1023 #define GIC_DIST_CTRL 0x000 #define GIC_DIST_CTR 0x004 @@ -39,6 +42,18 @@ #define GIC_DIST_SGI_PENDING_CLEAR 0xf10 #define GIC_DIST_SGI_PENDING_SET 0xf20 +#define GICD_ENABLE 0x1 +#define GICD_DISABLE 0x0 +#define GICD_INT_ACTLOW_LVLTRIG 0x0 +#define GICD_INT_EN_CLR_X32 0xffffffff +#define GICD_INT_EN_SET_SGI 0x0000ffff +#define GICD_INT_EN_CLR_PPI 0xffff0000 +#define GICD_INT_DEF_PRI 0xa0 +#define GICD_INT_DEF_PRI_X4 ((GICD_INT_DEF_PRI << 24) |\ + (GICD_INT_DEF_PRI << 16) |\ + (GICD_INT_DEF_PRI << 8) |\ + GICD_INT_DEF_PRI) + #define GICH_HCR 0x0 #define GICH_VTR 0x4 #define GICH_VMCR 0x8 -- cgit v1.2.3 From 3228950621d92f0f212378f95a6998ef3a1be0bb Mon Sep 17 00:00:00 2001 From: Feng Kan Date: Wed, 30 Jul 2014 14:56:59 -0700 Subject: irqchip: gic: Preserve gic V2 bypass bits in cpu ctrl register This change is made to preserve the GIC v2 bypass bits in the GIC_CPU_CTRL register (also known as the GICC_CTLR register in spec). This code will preserve all bits configured by the bootloader regarding v2 bypass group bits. In the X-Gene platform, the bypass functionality is not used and bypass bits should not be changed by the kernel gic code as it could lead to incorrect behavior. Signed-off-by: Feng Kan Reviewed-by: Vinayak Kale Reviewed-by: Anup Patel Link: https://lkml.kernel.org/r/1406757419-18729-3-git-send-email-fkan@apm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-gic.c | 25 ++++++++++++++++++++++--- include/linux/irqchip/arm-gic.h | 1 + 2 files changed, 23 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 35847453cecb..2500f6ba29e1 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -353,6 +353,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; @@ -401,13 +416,17 @@ static void gic_cpu_init(struct gic_chip_data *gic) gic_cpu_config(dist_base, NULL); writel_relaxed(GICC_INT_PRI_THRESHOLD, base + GIC_CPU_PRIMASK); - writel_relaxed(GICC_ENABLE, base + GIC_CPU_CTRL); + 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 @@ -543,7 +562,7 @@ static void gic_cpu_restore(unsigned int gic_nr) dist_base + GIC_DIST_PRI + i * 4); writel_relaxed(GICC_INT_PRI_THRESHOLD, cpu_base + GIC_CPU_PRIMASK); - writel_relaxed(GICC_ENABLE, cpu_base + GIC_CPU_CTRL); + gic_cpu_if_up(); } static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v) diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h index 5cb9d41af5be..13eed92c7d24 100644 --- a/include/linux/irqchip/arm-gic.h +++ b/include/linux/irqchip/arm-gic.h @@ -25,6 +25,7 @@ #define GICC_INT_PRI_THRESHOLD 0xf0 #define GICC_IAR_INT_ID_MASK 0x3ff #define GICC_INT_SPURIOUS 1023 +#define GICC_DIS_BYPASS_MASK 0x1e0 #define GIC_DIST_CTRL 0x000 #define GIC_DIST_CTR 0x004 -- cgit v1.2.3 From 8e4bebe0952af357e099147023af756baa466ede Mon Sep 17 00:00:00 2001 From: Haojian Zhuang Date: Thu, 7 Aug 2014 18:51:34 +0800 Subject: irqchip: hip04: Enable Hisilicon HiP04 interrupt controller HiP04 GIC is the variate of ARM GICv2. ARM GICv2 supports 8 cores. HiP04 GIC extends to support 16 cores. It results that bit fields in GIC_DIST_TARGET & GIC_DIST_SOFTINT are different from ARM GICv2. And the maximium IRQ is downgrade from 1020 to 510. Since different register offset & bitfields definitation breaks compartible with ARM GICv2, create a new hip04 irq driver. And this driver is derived from irq-gic.c to support the Hisilicon HiP04 interrupt controller, which is similar to the GIC, but deviates at some points. Support for power management, non-banked registers, cascaded GICs (and multiple controllers in general) and bigLittle support has been removed from the GIC driver. Affinity related functions have been adjusted to match the Hisilicon hardware implementation. Signed-off-by: Haojian Zhuang Link: https://lkml.kernel.org/r/1407408695-19626-9-git-send-email-haojian.zhuang@linaro.org Signed-off-by: Jason Cooper --- drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-hip04.c | 424 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 425 insertions(+) create mode 100644 drivers/irqchip/irq-hip04.c (limited to 'drivers') diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 73052ba9ca62..1dca5bc2cc43 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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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; + + if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING) + return -EINVAL; + + raw_spin_lock(&irq_controller_lock); + + gic_configure_irq(irq, type, base, NULL); + + raw_spin_unlock(&irq_controller_lock); + + return 0; +} + +#ifdef CONFIG_SMP +static int hip04_irq_set_affinity(struct irq_data *d, + const struct cpumask *mask_val, + bool force) +{ + void __iomem *reg; + unsigned int cpu, shift = (hip04_irq(d) % 2) * 16; + u32 val, mask, bit; + + if (!force) + cpu = cpumask_any_and(mask_val, cpu_online_mask); + else + cpu = cpumask_first(mask_val); + + if (cpu >= NR_HIP04_CPU_IF || cpu >= nr_cpu_ids) + return -EINVAL; + + raw_spin_lock(&irq_controller_lock); + reg = hip04_dist_base(d) + GIC_DIST_TARGET + ((hip04_irq(d) * 2) & ~3); + mask = 0xffff << shift; + bit = hip04_cpu_map[cpu] << shift; + val = readl_relaxed(reg) & ~mask; + writel_relaxed(val | bit, reg); + raw_spin_unlock(&irq_controller_lock); + + return IRQ_SET_MASK_OK; +} +#endif + +static void __exception_irq_entry hip04_handle_irq(struct pt_regs *regs) +{ + u32 irqstat, irqnr; + void __iomem *cpu_base = hip04_data.cpu_base; + + do { + irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK); + irqnr = irqstat & GICC_IAR_INT_ID_MASK; + + if (likely(irqnr > 15 && irqnr <= HIP04_MAX_IRQS)) { + irqnr = irq_find_mapping(hip04_data.domain, irqnr); + handle_IRQ(irqnr, regs); + continue; + } + if (irqnr < 16) { + writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI); +#ifdef CONFIG_SMP + handle_IPI(irqnr, regs); +#endif + continue; + } + break; + } while (1); +} + +static struct irq_chip hip04_irq_chip = { + .name = "HIP04 INTC", + .irq_mask = hip04_mask_irq, + .irq_unmask = hip04_unmask_irq, + .irq_eoi = hip04_eoi_irq, + .irq_set_type = hip04_irq_set_type, +#ifdef CONFIG_SMP + .irq_set_affinity = hip04_irq_set_affinity, +#endif +}; + +static u16 hip04_get_cpumask(struct hip04_irq_data *intc) +{ + void __iomem *base = intc->dist_base; + u32 mask, i; + + for (i = mask = 0; i < 32; i += 2) { + mask = readl_relaxed(base + GIC_DIST_TARGET + i * 2); + mask |= mask >> 16; + if (mask) + break; + } + + if (!mask) + pr_crit("GIC CPU mask not found - kernel will fail to boot.\n"); + + return mask; +} + +static void __init hip04_irq_dist_init(struct hip04_irq_data *intc) +{ + unsigned int i; + u32 cpumask; + unsigned int nr_irqs = intc->nr_irqs; + void __iomem *base = intc->dist_base; + + writel_relaxed(0, base + GIC_DIST_CTRL); + + /* + * Set all global interrupts to this CPU only. + */ + cpumask = hip04_get_cpumask(intc); + cpumask |= cpumask << 16; + for (i = 32; i < nr_irqs; i += 2) + writel_relaxed(cpumask, base + GIC_DIST_TARGET + ((i * 2) & ~3)); + + gic_dist_config(base, nr_irqs, NULL); + + writel_relaxed(1, base + GIC_DIST_CTRL); +} + +static void hip04_irq_cpu_init(struct hip04_irq_data *intc) +{ + void __iomem *dist_base = intc->dist_base; + void __iomem *base = intc->cpu_base; + unsigned int cpu_mask, cpu = smp_processor_id(); + int i; + + /* + * Get what the GIC says our CPU mask is. + */ + BUG_ON(cpu >= NR_HIP04_CPU_IF); + cpu_mask = hip04_get_cpumask(intc); + hip04_cpu_map[cpu] = cpu_mask; + + /* + * Clear our mask from the other map entries in case they're + * still undefined. + */ + for (i = 0; i < NR_HIP04_CPU_IF; i++) + if (i != cpu) + hip04_cpu_map[i] &= ~cpu_mask; + + gic_cpu_config(dist_base, NULL); + + writel_relaxed(0xf0, base + GIC_CPU_PRIMASK); + writel_relaxed(1, base + GIC_CPU_CTRL); +} + +#ifdef CONFIG_SMP +static void hip04_raise_softirq(const struct cpumask *mask, unsigned int irq) +{ + int cpu; + unsigned long flags, map = 0; + + raw_spin_lock_irqsave(&irq_controller_lock, flags); + + /* Convert our logical CPU mask into a physical one. */ + for_each_cpu(cpu, mask) + map |= hip04_cpu_map[cpu]; + + /* + * Ensure that stores to Normal memory are visible to the + * other CPUs before they observe us issuing the IPI. + */ + dmb(ishst); + + /* this always happens on GIC0 */ + writel_relaxed(map << 8 | irq, hip04_data.dist_base + GIC_DIST_SOFTINT); + + raw_spin_unlock_irqrestore(&irq_controller_lock, flags); +} +#endif + +static int hip04_irq_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hw) +{ + if (hw < 32) { + irq_set_percpu_devid(irq); + irq_set_chip_and_handler(irq, &hip04_irq_chip, + handle_percpu_devid_irq); + set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN); + } else { + irq_set_chip_and_handler(irq, &hip04_irq_chip, + handle_fasteoi_irq); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + } + irq_set_chip_data(irq, d->host_data); + return 0; +} + +static int hip04_irq_domain_xlate(struct irq_domain *d, + struct device_node *controller, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, + unsigned int *out_type) +{ + unsigned long ret = 0; + + 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 irq ID number */ + if (!intspec[0]) + *out_hwirq += 16; + + *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; + + return ret; +} + +#ifdef CONFIG_SMP +static int hip04_irq_secondary_init(struct notifier_block *nfb, + unsigned long action, + void *hcpu) +{ + if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) + hip04_irq_cpu_init(&hip04_data); + return NOTIFY_OK; +} + +/* + * Notifier for enabling the INTC CPU interface. Set an arbitrarily high + * priority because the GIC needs to be up before the ARM generic timers. + */ +static struct notifier_block hip04_irq_cpu_notifier = { + .notifier_call = hip04_irq_secondary_init, + .priority = 100, +}; +#endif + +static const struct irq_domain_ops hip04_irq_domain_ops = { + .map = hip04_irq_domain_map, + .xlate = hip04_irq_domain_xlate, +}; + +static int __init +hip04_of_init(struct device_node *node, struct device_node *parent) +{ + irq_hw_number_t hwirq_base = 16; + int nr_irqs, irq_base, i; + + if (WARN_ON(!node)) + return -ENODEV; + + hip04_data.dist_base = of_iomap(node, 0); + WARN(!hip04_data.dist_base, "fail to map hip04 intc dist registers\n"); + + hip04_data.cpu_base = of_iomap(node, 1); + WARN(!hip04_data.cpu_base, "unable to map hip04 intc cpu registers\n"); + + /* + * Initialize the CPU interface map to all CPUs. + * It will be refined as each CPU probes its ID. + */ + for (i = 0; i < NR_HIP04_CPU_IF; i++) + hip04_cpu_map[i] = 0xff; + + /* + * Find out how many interrupts are supported. + * The HIP04 INTC only supports up to 510 interrupt sources. + */ + nr_irqs = readl_relaxed(hip04_data.dist_base + GIC_DIST_CTR) & 0x1f; + nr_irqs = (nr_irqs + 1) * 32; + if (nr_irqs > HIP04_MAX_IRQS) + nr_irqs = HIP04_MAX_IRQS; + hip04_data.nr_irqs = nr_irqs; + + nr_irqs -= hwirq_base; /* calculate # of irqs to allocate */ + + irq_base = irq_alloc_descs(-1, hwirq_base, nr_irqs, numa_node_id()); + if (IS_ERR_VALUE(irq_base)) { + pr_err("failed to allocate IRQ numbers\n"); + return -EINVAL; + } + + hip04_data.domain = irq_domain_add_legacy(node, nr_irqs, irq_base, + hwirq_base, + &hip04_irq_domain_ops, + &hip04_data); + + if (WARN_ON(!hip04_data.domain)) + return -EINVAL; + +#ifdef CONFIG_SMP + set_smp_cross_call(hip04_raise_softirq); + register_cpu_notifier(&hip04_irq_cpu_notifier); +#endif + set_handle_irq(hip04_handle_irq); + + hip04_irq_dist_init(&hip04_data); + hip04_irq_cpu_init(&hip04_data); + + return 0; +} +IRQCHIP_DECLARE(hip04_intc, "hisilicon,hip04-intc", hip04_of_init); -- cgit v1.2.3 From 1c36d42c4ffee9e38e122ed822dd0a545a3a86da Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 20 Aug 2014 16:49:32 +0200 Subject: irqchip: renesas-intc-irqpin: Add suspend-to-RAM wake up support Set the ->irq_enable() and ->irq_disable() methods to NULL to enable lazy disable of interrupts, and set IRQCHIP_MASK_ON_SUSPEND to tell the core that only IRQs marked as wake-ups need to stay enabled during suspend-to-RAM. This makes wake-up by gpio-keys from suspend-to-RAM work on r8a7740/Armadillo. Signed-off-by: Geert Uytterhoeven Link: https://lkml.kernel.org/r/1408546172-22484-1-git-send-email-geert+renesas@glider.be Signed-off-by: Jason Cooper --- drivers/irqchip/irq-renesas-intc-irqpin.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c index 3ee78f02e5d7..a9efceb0c4a0 100644 --- a/drivers/irqchip/irq-renesas-intc-irqpin.c +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c @@ -454,10 +454,8 @@ static int intc_irqpin_probe(struct platform_device *pdev) irq_chip->name = name; irq_chip->irq_mask = disable_fn; irq_chip->irq_unmask = enable_fn; - irq_chip->irq_enable = enable_fn; - irq_chip->irq_disable = disable_fn; irq_chip->irq_set_type = intc_irqpin_irq_set_type; - irq_chip->flags = IRQCHIP_SKIP_SET_WAKE; + irq_chip->flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND; p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, p->number_of_irqs, -- cgit v1.2.3 From 8703ec19c706eb4d9f2848d50e9fc41d73eb4a40 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Thu, 28 Aug 2014 20:16:29 +0300 Subject: irqchip: keystone: Remove warning unsigned 'kirq->irq' is never less than zero Remove static checker warning: drivers/irqchip/irq-keystone.c:166 keystone_irq_probe() warn: unsigned 'kirq->irq' is never less than zero. Reported-by: Dan Carpenter Signed-off-by: Grygorii Strashko Link: https://lkml.kernel.org/r/1409246189-29727-1-git-send-email-grygorii.strashko@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-keystone.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-keystone.c b/drivers/irqchip/irq-keystone.c index fea26bcfd4ea..608abf9c9283 100644 --- a/drivers/irqchip/irq-keystone.c +++ b/drivers/irqchip/irq-keystone.c @@ -36,7 +36,7 @@ struct keystone_irq_device { struct device *dev; struct irq_chip chip; u32 mask; - u32 irq; + int irq; struct irq_domain *irqd; struct regmap *devctrl_regs; u32 devctrl_offset; -- cgit v1.2.3 From 60031b4ef40b62f6563cc6635f670a144f182b83 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 26 Aug 2014 11:03:20 +0100 Subject: irqchip: gic: Convert to handle_domain_irq Use the new handle_domain_irq method to handle interrupts. Signed-off-by: Marc Zyngier Link: https://lkml.kernel.org/r/1409047421-27649-6-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-gic.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 4b959e606fe8..480bae864a87 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) { -- cgit v1.2.3 From e89c6a06b876239161653da84dbb2a0e243768b8 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 26 Aug 2014 11:03:21 +0100 Subject: irqchip: armada-370-xp: Convert to handle_domain_irq Use the new handle_domain_irq method to handle interrupts. Signed-off-by: Marc Zyngier Link: https://lkml.kernel.org/r/1409047421-27649-7-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-armada-370-xp.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'drivers') 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; } -- cgit v1.2.3 From a8e10cb769eb94277658d3f3bce12e10b5652b7f Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 26 Aug 2014 11:03:22 +0100 Subject: irqchip: clps711x: Convert to handle_domain_irq Use the new handle_domain_irq method to handle interrupts. Signed-off-by: Marc Zyngier Link: https://lkml.kernel.org/r/1409047421-27649-8-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-clps711x.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'drivers') 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); } -- cgit v1.2.3 From b918402c8092e9f55ad1e848c4264cc0c9ea7513 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 26 Aug 2014 11:03:23 +0100 Subject: irqchip: mmp: Convert to handle_domain_irq Use the new handle_domain_irq method to handle interrupts. Signed-off-by: Marc Zyngier Link: https://lkml.kernel.org/r/1409047421-27649-9-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-mmp.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-mmp.c b/drivers/irqchip/irq-mmp.c index 1c3e2c9b46ba..c0da57bdb89d 100644 --- a/drivers/irqchip/irq-mmp.c +++ b/drivers/irqchip/irq-mmp.c @@ -196,26 +196,24 @@ static struct mmp_intc_conf mmp2_conf = { static void __exception_irq_entry mmp_handle_irq(struct pt_regs *regs) { - int irq, hwirq; + int hwirq; hwirq = readl_relaxed(mmp_icu_base + PJ1_INT_SEL); if (!(hwirq & SEL_INT_PENDING)) return; hwirq &= SEL_INT_NUM_MASK; - irq = irq_find_mapping(icu_data[0].domain, hwirq); - handle_IRQ(irq, regs); + handle_domain_irq(icu_data[0].domain, hwirq, regs); } static void __exception_irq_entry mmp2_handle_irq(struct pt_regs *regs) { - int irq, hwirq; + int hwirq; hwirq = readl_relaxed(mmp_icu_base + PJ4_INT_SEL); if (!(hwirq & SEL_INT_PENDING)) return; hwirq &= SEL_INT_NUM_MASK; - irq = irq_find_mapping(icu_data[0].domain, hwirq); - handle_IRQ(irq, regs); + handle_domain_irq(icu_data[0].domain, hwirq, regs); } /* MMP (ARMv5) */ -- cgit v1.2.3 From b3410e5f4b6a9611fcdff8927d7ce04757708d96 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 26 Aug 2014 11:03:24 +0100 Subject: irqchip: mxs: Convert to handle_domain_irq Use the new handle_domain_irq method to handle interrupts. Signed-off-by: Marc Zyngier Acked-by: Shawn Guo Link: https://lkml.kernel.org/r/1409047421-27649-10-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-mxs.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-mxs.c b/drivers/irqchip/irq-mxs.c index 4044ff287663..e4acf1e3f8e3 100644 --- a/drivers/irqchip/irq-mxs.c +++ b/drivers/irqchip/irq-mxs.c @@ -78,8 +78,7 @@ asmlinkage void __exception_irq_entry icoll_handle_irq(struct pt_regs *regs) irqnr = __raw_readl(icoll_base + HW_ICOLL_STAT_OFFSET); __raw_writel(irqnr, icoll_base + HW_ICOLL_VECTOR); - irqnr = irq_find_mapping(icoll_domain, irqnr); - handle_IRQ(irqnr, regs); + handle_domain_irq(icoll_domain, irqnr, regs); } static int icoll_irq_domain_map(struct irq_domain *d, unsigned int virq, -- cgit v1.2.3 From f4bc9288b4a29ead48425dd7d1bf4b825a8aff58 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 26 Aug 2014 11:03:25 +0100 Subject: irqchip: orion: Convert to handle_domain_irq Use the new handle_domain_irq method to handle interrupts. Signed-off-by: Marc Zyngier Link: https://lkml.kernel.org/r/1409047421-27649-11-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-orion.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-orion.c b/drivers/irqchip/irq-orion.c index 34d18b48bb78..ad0c0f6f1d65 100644 --- a/drivers/irqchip/irq-orion.c +++ b/drivers/irqchip/irq-orion.c @@ -43,9 +43,8 @@ __exception_irq_entry orion_handle_irq(struct pt_regs *regs) gc->mask_cache; while (stat) { u32 hwirq = __fls(stat); - u32 irq = irq_find_mapping(orion_irq_domain, - gc->irq_base + hwirq); - handle_IRQ(irq, regs); + handle_domain_irq(orion_irq_domain, + gc->irq_base + hwirq, regs); stat &= ~(1 << hwirq); } } -- cgit v1.2.3 From cf86bfdd68997b584f4a8f1da9ffd2fae852a5a9 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 26 Aug 2014 11:03:26 +0100 Subject: irqchip: s3c24xx: Convert to handle_domain_irq Use the new handle_domain_irq method to handle interrupts. Signed-off-by: Marc Zyngier Link: https://lkml.kernel.org/r/1409047421-27649-12-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-s3c24xx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c index 78a6accd205f..c8d373fcd823 100644 --- a/drivers/irqchip/irq-s3c24xx.c +++ b/drivers/irqchip/irq-s3c24xx.c @@ -339,7 +339,6 @@ static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc, { int pnd; int offset; - int irq; pnd = __raw_readl(intc->reg_intpnd); if (!pnd) @@ -365,8 +364,7 @@ static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc, if (!(pnd & (1 << offset))) offset = __ffs(pnd); - irq = irq_find_mapping(intc->domain, intc_offset + offset); - handle_IRQ(irq, regs); + handle_domain_irq(intc->domain, intc_offset + offset, regs); return true; } -- cgit v1.2.3 From c15018e9192bbaa1a0f68ebee0284f701491b800 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 26 Aug 2014 11:03:27 +0100 Subject: irqchip: sirfsoc: Convert to handle_domain_irq Use the new handle_domain_irq method to handle interrupts. Signed-off-by: Marc Zyngier Link: https://lkml.kernel.org/r/1409047421-27649-13-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-sirfsoc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-sirfsoc.c b/drivers/irqchip/irq-sirfsoc.c index 5e54f6d71e77..a469355df352 100644 --- a/drivers/irqchip/irq-sirfsoc.c +++ b/drivers/irqchip/irq-sirfsoc.c @@ -50,12 +50,10 @@ sirfsoc_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num) static void __exception_irq_entry sirfsoc_handle_irq(struct pt_regs *regs) { void __iomem *base = sirfsoc_irqdomain->host_data; - u32 irqstat, irqnr; + u32 irqstat; irqstat = readl_relaxed(base + SIRFSOC_INIT_IRQ_ID); - irqnr = irq_find_mapping(sirfsoc_irqdomain, irqstat & 0xff); - - handle_IRQ(irqnr, regs); + handle_domain_irq(sirfsoc_irqdomain, irqstat & 0xff, regs); } static int __init sirfsoc_irq_init(struct device_node *np, -- cgit v1.2.3 From 21d06d91c23ddb24895bbe82ba96633864891f6e Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 26 Aug 2014 11:03:28 +0100 Subject: irqchip: sun4i: Convert to handle_domain_irq Use the new handle_domain_irq method to handle interrupts. Signed-off-by: Marc Zyngier Link: https://lkml.kernel.org/r/1409047421-27649-14-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-sun4i.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c index 6fcef4a95a18..64155b686081 100644 --- a/drivers/irqchip/irq-sun4i.c +++ b/drivers/irqchip/irq-sun4i.c @@ -136,7 +136,7 @@ IRQCHIP_DECLARE(allwinner_sun4i_ic, "allwinner,sun4i-a10-ic", sun4i_of_init); static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs) { - u32 irq, hwirq; + u32 hwirq; /* * hwirq == 0 can mean one of 3 things: @@ -154,8 +154,7 @@ static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs) return; do { - irq = irq_find_mapping(sun4i_irq_domain, hwirq); - handle_IRQ(irq, regs); + handle_domain_irq(sun4i_irq_domain, hwirq, regs); hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2; } while (hwirq != 0); } -- cgit v1.2.3 From 84bc7399099344e41672d72864e3c34297a877d2 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 26 Aug 2014 11:03:29 +0100 Subject: irqchip: versatile-fpga: Convert to handle_domain_irq Use the new handle_domain_irq method to handle interrupts. Signed-off-by: Marc Zyngier Link: https://lkml.kernel.org/r/1409047421-27649-15-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-versatile-fpga.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-versatile-fpga.c b/drivers/irqchip/irq-versatile-fpga.c index ccf58548b161..1ab451729a5c 100644 --- a/drivers/irqchip/irq-versatile-fpga.c +++ b/drivers/irqchip/irq-versatile-fpga.c @@ -96,7 +96,7 @@ static int handle_one_fpga(struct fpga_irq_data *f, struct pt_regs *regs) while ((status = readl(f->base + IRQ_STATUS))) { irq = ffs(status) - 1; - handle_IRQ(irq_find_mapping(f->domain, irq), regs); + handle_domain_irq(f->domain, irq, regs); handled = 1; } -- cgit v1.2.3 From 0af83b3b00cc302388beea8b6bd48c5fcbc715a8 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 26 Aug 2014 11:03:30 +0100 Subject: irqchip: vic: Convert to handle_domain_irq Use the new handle_domain_irq method to handle interrupts. Signed-off-by: Marc Zyngier Link: https://lkml.kernel.org/r/1409047421-27649-16-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-vic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-vic.c b/drivers/irqchip/irq-vic.c index 7d35287f9e90..54089debf2dc 100644 --- a/drivers/irqchip/irq-vic.c +++ b/drivers/irqchip/irq-vic.c @@ -219,7 +219,7 @@ static int handle_one_vic(struct vic_device *vic, struct pt_regs *regs) while ((stat = readl_relaxed(vic->base + VIC_IRQ_STATUS))) { irq = ffs(stat) - 1; - handle_IRQ(irq_find_mapping(vic->domain, irq), regs); + handle_domain_irq(vic->domain, irq, regs); handled = 1; } -- cgit v1.2.3 From 0beb65041e86ea313eaceaa6a04c87f9cc01f6b1 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 26 Aug 2014 11:03:31 +0100 Subject: irqchip: vt8500: Convert to handle_domain_irq Use the new handle_domain_irq method to handle interrupts. Signed-off-by: Marc Zyngier Link: https://lkml.kernel.org/r/1409047421-27649-17-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-vt8500.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c index eb6e91efdec8..b7af816f2769 100644 --- a/drivers/irqchip/irq-vt8500.c +++ b/drivers/irqchip/irq-vt8500.c @@ -181,7 +181,7 @@ static struct irq_domain_ops vt8500_irq_domain_ops = { static void __exception_irq_entry vt8500_handle_irq(struct pt_regs *regs) { u32 stat, i; - int irqnr, virq; + int irqnr; void __iomem *base; /* Loop through each active controller */ @@ -198,8 +198,7 @@ static void __exception_irq_entry vt8500_handle_irq(struct pt_regs *regs) continue; } - virq = irq_find_mapping(intc[i].domain, irqnr); - handle_IRQ(virq, regs); + handle_domain_irq(intc[i].domain, irqnr, regs); } } -- cgit v1.2.3 From d8c0ffa56798a9b6dc6ee44d230a50f1fe0a10d4 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 26 Aug 2014 11:03:32 +0100 Subject: irqchip: zevio: Convert to handle_domain_irq Use the new handle_domain_irq method to handle interrupts. Signed-off-by: Marc Zyngier Link: https://lkml.kernel.org/r/1409047421-27649-18-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-zevio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-zevio.c b/drivers/irqchip/irq-zevio.c index ceb3a4318f73..e4ef74ed454a 100644 --- a/drivers/irqchip/irq-zevio.c +++ b/drivers/irqchip/irq-zevio.c @@ -56,8 +56,7 @@ static void __exception_irq_entry zevio_handle_irq(struct pt_regs *regs) while (readl(zevio_irq_io + IO_STATUS)) { irqnr = readl(zevio_irq_io + IO_CURRENT); - irqnr = irq_find_mapping(zevio_irq_domain, irqnr); - handle_IRQ(irqnr, regs); + handle_domain_irq(zevio_irq_domain, irqnr, regs); }; } -- cgit v1.2.3 From ebc6de0056e277a0283845536fd219a96806fc80 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 26 Aug 2014 11:03:33 +0100 Subject: irqchip: gic-v3: Convert to handle_domain_irq Use the new handle_domain_irq method to handle interrupts. Signed-off-by: Marc Zyngier Link: https://lkml.kernel.org/r/1409047421-27649-19-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-gic-v3.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 57eaa5a0b1e3..9e3144975696 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -274,14 +274,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); -- cgit v1.2.3 From 841f2aa46299b894ce146cd7d2a8fd9a1f6dbdcb Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 26 Aug 2014 11:03:34 +0100 Subject: irqchip: atmel-aic: Convert to handle_domain_irq Use the new handle_domain_irq method to handle interrupts. Signed-off-by: Marc Zyngier Acked-by: Nicolas Ferre Acked-by: Boris Brezillon Link: https://lkml.kernel.org/r/1409047421-27649-20-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-atmel-aic.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') 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) -- cgit v1.2.3 From 31b7b6a86e9b86b8d97b3bc47b22585074ad115b Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 26 Aug 2014 11:03:35 +0100 Subject: irqchip: atmel-aic5: Convert to handle_domain_irq Use the new handle_domain_irq method to handle interrupts. Signed-off-by: Marc Zyngier Acked-by: Nicolas Ferre Acked-by: Boris Brezillon Link: https://lkml.kernel.org/r/1409047421-27649-21-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-atmel-aic5.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c index edb227081524..04fe2c1b5178 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) -- cgit v1.2.3 From b0fee1dc7883c3c4b2319d384decdd1563cf30bb Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Tue, 26 Aug 2014 11:03:36 +0100 Subject: irqchip: or1k-pic: Convert to handle_domain_irq Use the new handle_domain_irq method to handle interrupts. Signed-off-by: Marc Zyngier Acked-by: Stefan Kristiansson Link: https://lkml.kernel.org/r/1409047421-27649-22-git-send-email-marc.zyngier@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-or1k-pic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-or1k-pic.c b/drivers/irqchip/irq-or1k-pic.c index 17ff033d9925..e93d079fe069 100644 --- a/drivers/irqchip/irq-or1k-pic.c +++ b/drivers/irqchip/irq-or1k-pic.c @@ -113,7 +113,7 @@ static inline int pic_get_irq(int first) else hwirq = hwirq + first - 1; - return irq_find_mapping(root_domain, hwirq); + return hwirq; } static void or1k_pic_handle_irq(struct pt_regs *regs) @@ -121,7 +121,7 @@ static void or1k_pic_handle_irq(struct pt_regs *regs) int irq = -1; while ((irq = pic_get_irq(irq + 1)) != NO_IRQ) - handle_IRQ(irq, regs); + handle_domain_irq(root_domain, irq, regs); } static int or1k_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) -- cgit v1.2.3 From a5042de2688d3e903799caf1196539c57555e3ed Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 9 Sep 2014 17:44:21 -0700 Subject: irqchip: bcm7120-l2: Add Broadcom BCM7120-style Level 2 interrupt controller This patch adds support for the Level-2 interrupt controller hardware found in Broadcom Set Top Box System-on-a-Chip devices. This interrupt controller is implemented using a single enable register. This interrupt controller is always present on the platforms supported by the irq-brcmstb-l2 driver, hence the reason why both are compiled using the same Kconfig symbol. [jac] removed the following warning: drivers/irqchip/irq-bcm7120-l2.c: In function 'bcm7120_l2_intc_irq_handle': drivers/irqchip/irq-bcm7120-l2.c:49:27: warning: unused variable 'gc' [-Wunused-variable] Signed-off-by: Florian Fainelli Link: https://lkml.kernel.org/r/1410309862-27784-2-git-send-email-f.fainelli@gmail.com Signed-off-by: Jason Cooper --- drivers/irqchip/Makefile | 3 +- drivers/irqchip/irq-bcm7120-l2.c | 219 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 drivers/irqchip/irq-bcm7120-l2.c (limited to 'drivers') diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 73052ba9ca62..824f5eb780b6 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -33,4 +33,5 @@ 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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "irqchip.h" + +#include + +/* 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); -- cgit v1.2.3 From 0cae165ffc0c878b02c56efd62988f13d513c8a7 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 11 Sep 2014 16:41:51 +0200 Subject: irqchip: atmel-aic5: The sama5d3 has 48 IRQs The FUSE and RAM controllers don't have any connected IRQs, reducing the number of IRQs to 48. Signed-off-by: Alexandre Belloni Acked-by: Boris Brezillon Acked-by: Nicolas Ferre Link: https://lkml.kernel.org/r/1410446511-29477-1-git-send-email-alexandre.belloni@free-electrons.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-atmel-aic5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c index edb227081524..22c922812cd6 100644 --- a/drivers/irqchip/irq-atmel-aic5.c +++ b/drivers/irqchip/irq-atmel-aic5.c @@ -343,7 +343,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) -- cgit v1.2.3 From 20afdeb812da4097447eb324f34be11a10b11542 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 12 Sep 2014 17:43:00 +0200 Subject: irqchip: atmel-aic5: Add sama5d4 support Add sama5d4 support to irq-atmel-aic5. Signed-off-by: Alexandre Belloni Link: https://lkml.kernel.org/r/1410536587-24607-2-git-send-email-alexandre.belloni@free-electrons.com Signed-off-by: Jason Cooper --- .../devicetree/bindings/interrupt-controller/atmel,aic.txt | 2 +- drivers/irqchip/irq-atmel-aic5.c | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/interrupt-controller/atmel,aic.txt b/Documentation/devicetree/bindings/interrupt-controller/atmel,aic.txt index 2742e9cfd6b1..f292917fa00d 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/atmel,aic.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/atmel,aic.txt @@ -2,7 +2,7 @@ Required properties: - compatible: Should be "atmel,-aic" - can be "at91rm9200" or "sama5d3" + can be "at91rm9200", "sama5d3" or "sama5d4" - interrupt-controller: Identifies the node as an interrupt controller. - interrupt-parent: For single AIC system, it is an empty property. - #interrupt-cells: The number of cells to define the interrupts. It should be 3. diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c index 22c922812cd6..92b504a56293 100644 --- a/drivers/irqchip/irq-atmel-aic5.c +++ b/drivers/irqchip/irq-atmel-aic5.c @@ -297,6 +297,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 */ }, }; @@ -351,3 +352,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); -- cgit v1.2.3 From 36845f1b54edc3237626d48270bbf3bc26d45f4f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 12 Sep 2014 15:15:17 +0200 Subject: irqchip: renesas-intc-irqpin: Add helper variable dev = &pdev->dev Signed-off-by: Geert Uytterhoeven Link: https://lkml.kernel.org/r/1410527720-18061-2-git-send-email-geert+renesas@glider.be Signed-off-by: Jason Cooper --- drivers/irqchip/irq-renesas-intc-irqpin.c | 45 +++++++++++++++---------------- 1 file changed, 22 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c index a9efceb0c4a0..a6b205b72c9b 100644 --- a/drivers/irqchip/irq-renesas-intc-irqpin.c +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c @@ -329,7 +329,8 @@ static struct irq_domain_ops intc_irqpin_irq_domain_ops = { static int intc_irqpin_probe(struct platform_device *pdev) { - struct renesas_intc_irqpin_config *pdata = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + struct renesas_intc_irqpin_config *pdata = dev->platform_data; struct intc_irqpin_priv *p; struct intc_irqpin_iomem *i; struct resource *io[INTC_IRQPIN_REG_NR]; @@ -337,14 +338,14 @@ static int intc_irqpin_probe(struct platform_device *pdev) struct irq_chip *irq_chip; void (*enable_fn)(struct irq_data *d); void (*disable_fn)(struct irq_data *d); - const char *name = dev_name(&pdev->dev); + const char *name = dev_name(dev); int ref_irq; int ret; int k; - p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); + p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); if (!p) { - dev_err(&pdev->dev, "failed to allocate driver data\n"); + dev_err(dev, "failed to allocate driver data\n"); ret = -ENOMEM; goto err0; } @@ -353,9 +354,9 @@ static int intc_irqpin_probe(struct platform_device *pdev) if (pdata) { memcpy(&p->config, pdata, sizeof(*pdata)); } else { - of_property_read_u32(pdev->dev.of_node, "sense-bitfield-width", + of_property_read_u32(dev->of_node, "sense-bitfield-width", &p->config.sense_bitfield_width); - p->config.control_parent = of_property_read_bool(pdev->dev.of_node, + p->config.control_parent = of_property_read_bool(dev->of_node, "control-parent"); } if (!p->config.sense_bitfield_width) @@ -368,7 +369,7 @@ static int intc_irqpin_probe(struct platform_device *pdev) for (k = 0; k < INTC_IRQPIN_REG_NR; k++) { io[k] = platform_get_resource(pdev, IORESOURCE_MEM, k); if (!io[k]) { - dev_err(&pdev->dev, "not enough IOMEM resources\n"); + dev_err(dev, "not enough IOMEM resources\n"); ret = -EINVAL; goto err0; } @@ -386,7 +387,7 @@ static int intc_irqpin_probe(struct platform_device *pdev) p->number_of_irqs = k; if (p->number_of_irqs < 1) { - dev_err(&pdev->dev, "not enough IRQ resources\n"); + dev_err(dev, "not enough IRQ resources\n"); ret = -EINVAL; goto err0; } @@ -407,15 +408,15 @@ static int intc_irqpin_probe(struct platform_device *pdev) i->write = intc_irqpin_write32; break; default: - dev_err(&pdev->dev, "IOMEM size mismatch\n"); + dev_err(dev, "IOMEM size mismatch\n"); ret = -EINVAL; goto err0; } - i->iomem = devm_ioremap_nocache(&pdev->dev, io[k]->start, + i->iomem = devm_ioremap_nocache(dev, io[k]->start, resource_size(io[k])); if (!i->iomem) { - dev_err(&pdev->dev, "failed to remap IOMEM\n"); + dev_err(dev, "failed to remap IOMEM\n"); ret = -ENXIO; goto err0; } @@ -457,34 +458,32 @@ static int intc_irqpin_probe(struct platform_device *pdev) irq_chip->irq_set_type = intc_irqpin_irq_set_type; irq_chip->flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND; - p->irq_domain = irq_domain_add_simple(pdev->dev.of_node, + p->irq_domain = irq_domain_add_simple(dev->of_node, p->number_of_irqs, p->config.irq_base, &intc_irqpin_irq_domain_ops, p); if (!p->irq_domain) { ret = -ENXIO; - dev_err(&pdev->dev, "cannot initialize irq domain\n"); + dev_err(dev, "cannot initialize irq domain\n"); goto err0; } if (p->shared_irqs) { /* request one shared interrupt */ - if (devm_request_irq(&pdev->dev, p->irq[0].requested_irq, + if (devm_request_irq(dev, p->irq[0].requested_irq, intc_irqpin_shared_irq_handler, IRQF_SHARED, name, p)) { - dev_err(&pdev->dev, "failed to request low IRQ\n"); + dev_err(dev, "failed to request low IRQ\n"); ret = -ENOENT; goto err1; } } else { /* request interrupts one by one */ for (k = 0; k < p->number_of_irqs; k++) { - if (devm_request_irq(&pdev->dev, - p->irq[k].requested_irq, - intc_irqpin_irq_handler, - 0, name, &p->irq[k])) { - dev_err(&pdev->dev, - "failed to request low IRQ\n"); + if (devm_request_irq(dev, p->irq[k].requested_irq, + intc_irqpin_irq_handler, 0, name, + &p->irq[k])) { + dev_err(dev, "failed to request low IRQ\n"); ret = -ENOENT; goto err1; } @@ -495,12 +494,12 @@ static int intc_irqpin_probe(struct platform_device *pdev) for (k = 0; k < p->number_of_irqs; k++) intc_irqpin_mask_unmask_prio(p, k, 0); - dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs); + dev_info(dev, "driving %d irqs\n", p->number_of_irqs); /* warn in case of mismatch if irq base is specified */ if (p->config.irq_base) { if (p->config.irq_base != p->irq[0].domain_irq) - dev_warn(&pdev->dev, "irq base mismatch (%d/%d)\n", + dev_warn(dev, "irq base mismatch (%d/%d)\n", p->config.irq_base, p->irq[0].domain_irq); } -- cgit v1.2.3 From 705bc96c2c15313c0677607f6e81800f4d2b4534 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 12 Sep 2014 15:15:18 +0200 Subject: irqchip: renesas-intc-irqpin: Add minimal runtime PM support This is just enough to let pm_clk_*() enable the functional clock, and manage it for suspend/resume, if present. Before, it was assumed enabled by the bootloader or reset state. To prevent the clock from being disabled while the module is needed for wake-up, implement irq_chip.irq_set_wake(), which increments/decrements the clock's enable_count when needed. Signed-off-by: Geert Uytterhoeven Link: https://lkml.kernel.org/r/1410527720-18061-3-git-send-email-geert+renesas@glider.be Signed-off-by: Jason Cooper --- drivers/irqchip/irq-renesas-intc-irqpin.c | 38 +++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c index a6b205b72c9b..542e850f4946 100644 --- a/drivers/irqchip/irq-renesas-intc-irqpin.c +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c @@ -17,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include #include #include @@ -30,6 +31,7 @@ #include #include #include +#include #define INTC_IRQPIN_MAX 8 /* maximum 8 interrupts per driver instance */ @@ -75,6 +77,7 @@ struct intc_irqpin_priv { struct platform_device *pdev; struct irq_chip irq_chip; struct irq_domain *irq_domain; + struct clk *clk; bool shared_irqs; u8 shared_irq_mask; }; @@ -270,6 +273,21 @@ static int intc_irqpin_irq_set_type(struct irq_data *d, unsigned int type) value ^ INTC_IRQ_SENSE_VALID); } +static int intc_irqpin_irq_set_wake(struct irq_data *d, unsigned int on) +{ + struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); + + if (!p->clk) + return 0; + + if (on) + clk_enable(p->clk); + else + clk_disable(p->clk); + + return 0; +} + static irqreturn_t intc_irqpin_irq_handler(int irq, void *dev_id) { struct intc_irqpin_irq *i = dev_id; @@ -346,8 +364,7 @@ static int intc_irqpin_probe(struct platform_device *pdev) p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); if (!p) { dev_err(dev, "failed to allocate driver data\n"); - ret = -ENOMEM; - goto err0; + return -ENOMEM; } /* deal with driver instance configuration */ @@ -365,6 +382,15 @@ static int intc_irqpin_probe(struct platform_device *pdev) p->pdev = pdev; platform_set_drvdata(pdev, p); + p->clk = devm_clk_get(dev, NULL); + if (IS_ERR(p->clk)) { + dev_warn(dev, "unable to get clock\n"); + p->clk = NULL; + } + + pm_runtime_enable(dev); + pm_runtime_get_sync(dev); + /* get hold of manadatory IOMEM */ for (k = 0; k < INTC_IRQPIN_REG_NR; k++) { io[k] = platform_get_resource(pdev, IORESOURCE_MEM, k); @@ -456,7 +482,8 @@ static int intc_irqpin_probe(struct platform_device *pdev) irq_chip->irq_mask = disable_fn; irq_chip->irq_unmask = enable_fn; irq_chip->irq_set_type = intc_irqpin_irq_set_type; - irq_chip->flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND; + irq_chip->irq_set_wake = intc_irqpin_irq_set_wake; + irq_chip->flags = IRQCHIP_MASK_ON_SUSPEND; p->irq_domain = irq_domain_add_simple(dev->of_node, p->number_of_irqs, @@ -508,6 +535,8 @@ static int intc_irqpin_probe(struct platform_device *pdev) err1: irq_domain_remove(p->irq_domain); err0: + pm_runtime_put(dev); + pm_runtime_disable(dev); return ret; } @@ -516,7 +545,8 @@ static int intc_irqpin_remove(struct platform_device *pdev) struct intc_irqpin_priv *p = platform_get_drvdata(pdev); irq_domain_remove(p->irq_domain); - + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); return 0; } -- cgit v1.2.3 From a2c225101234bcef8f40497bd50ccb5e9c1fb527 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 26 Aug 2014 16:03:34 +0100 Subject: irqchip: gic-v3: Refactor gic_enable_redist to support both enabling and disabling Currently gic_enable_redist configures the redistributors to never assert WakeRequest signal. However when powering down the processors with wake-up enabled(i.e suspend), we need to configure it to assert that signal. This patch extends gic_enable_redist so that the redistributor can be configure to assert WakeRequest and hold interrupts as pending. This is useful in suspending the processors. This patch also adds check to make sure GICR_WAKER is accessible when configuring it. Cc: Lorenzo Pieralisi Signed-off-by: Sudeep Holla [maz: removed reference to GICD_CTLR.DS and added read-back of GICR_WAKER to check that it is not RAZ/WI] Signed-off-by: Marc Zyngier Acked-by: Marc Zyngier Link: https://lkml.kernel.org/r/1409065415-20176-2-git-send-email-sudeep.holla@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-gic-v3.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 57eaa5a0b1e3..37062ba6704b 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -155,7 +155,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 +163,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"); } /* @@ -381,7 +391,7 @@ static void gic_cpu_init(void) if (gic_populate_rdist()) return; - gic_enable_redist(); + gic_enable_redist(true); rbase = gic_data_rdist_sgi_base(); -- cgit v1.2.3 From 3708d52fc6bb34ae16399fe998d515dd7d188ab0 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 26 Aug 2014 16:03:35 +0100 Subject: irqchip: gic-v3: Implement CPU PM notifier When a CPU enters a low power state, the contents of the GICv3/4 system registers are lost. They need to be saved and restored if required. For now, since most of the GICv3 register are set some initial values and not modified at runtime, it is better to re-initialise rather than saving and restoring them. It may need to be saved and restored in future if required. This patch adds a notifier to disable the redistributor(if allowed) and Group1 interrupts when powering down the processor and to re-initialise the system registers on wakeup. Cc: Lorenzo Pieralisi Signed-off-by: Sudeep Holla Signed-off-by: Marc Zyngier Acked-by: Marc Zyngier Link: https://lkml.kernel.org/r/1409065415-20176-3-git-send-email-sudeep.holla@arm.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-gic-v3.c | 57 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 37062ba6704b..4afbbc835939 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include @@ -383,6 +384,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; @@ -397,17 +413,8 @@ static void gic_cpu_init(void) 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 @@ -543,6 +550,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, @@ -682,6 +716,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; -- cgit v1.2.3