Merge branch 'irqchip/gic' into irqchip/core
diff --git a/Documentation/devicetree/bindings/interrupt-controller/renesas,intc-irqpin.txt b/Documentation/devicetree/bindings/interrupt-controller/renesas,intc-irqpin.txt
index 1f8b0c5..c73acd0 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/renesas,intc-irqpin.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/renesas,intc-irqpin.txt
@@ -2,7 +2,13 @@
Required properties:
-- compatible: has to be "renesas,intc-irqpin"
+- compatible: has to be "renesas,intc-irqpin-<soctype>", "renesas,intc-irqpin"
+ as fallback.
+ Examples with soctypes are:
+ - "renesas,intc-irqpin-r8a7740" (R-Mobile A1)
+ - "renesas,intc-irqpin-r8a7778" (R-Car M1A)
+ - "renesas,intc-irqpin-r8a7779" (R-Car H1)
+ - "renesas,intc-irqpin-sh73a0" (SH-Mobile AG5)
- #interrupt-cells: has to be <2>: an interrupt index and flags, as defined in
interrupts.txt in this directory
diff --git a/Documentation/devicetree/bindings/interrupt-controller/renesas,irqc.txt b/Documentation/devicetree/bindings/interrupt-controller/renesas,irqc.txt
new file mode 100644
index 0000000..1a88e62
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/renesas,irqc.txt
@@ -0,0 +1,32 @@
+DT bindings for the R-Mobile/R-Car interrupt controller
+
+Required properties:
+
+- compatible: has to be "renesas,irqc-<soctype>", "renesas,irqc" as fallback.
+ Examples with soctypes are:
+ - "renesas,irqc-r8a73a4" (R-Mobile AP6)
+ - "renesas,irqc-r8a7790" (R-Car H2)
+ - "renesas,irqc-r8a7791" (R-Car M2-W)
+ - "renesas,irqc-r8a7792" (R-Car V2H)
+ - "renesas,irqc-r8a7793" (R-Car M2-N)
+ - "renesas,irqc-r8a7794" (R-Car E2)
+- #interrupt-cells: has to be <2>: an interrupt index and flags, as defined in
+ interrupts.txt in this directory
+
+Optional properties:
+
+- any properties, listed in interrupts.txt, and any standard resource allocation
+ properties
+
+Example:
+
+ irqc0: interrupt-controller@e61c0000 {
+ compatible = "renesas,irqc-r8a7790", "renesas,irqc";
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ reg = <0 0xe61c0000 0 0x200>;
+ interrupts = <0 0 IRQ_TYPE_LEVEL_HIGH>,
+ <0 1 IRQ_TYPE_LEVEL_HIGH>,
+ <0 2 IRQ_TYPE_LEVEL_HIGH>,
+ <0 3 IRQ_TYPE_LEVEL_HIGH>;
+ };
diff --git a/Documentation/devicetree/bindings/interrupt-controller/ti,keystone-irq.txt b/Documentation/devicetree/bindings/interrupt-controller/ti,keystone-irq.txt
new file mode 100644
index 0000000..d9bb106
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/ti,keystone-irq.txt
@@ -0,0 +1,36 @@
+Keystone 2 IRQ controller IP
+
+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.
+
+Required Properties:
+- compatible: should be "ti,keystone-irq"
+- ti,syscon-dev : phandle and offset pair. The phandle to syscon used to
+ access device control registers and the offset inside
+ device control registers range.
+- interrupt-controller : Identifies the node as an interrupt controller
+- #interrupt-cells : Specifies the number of cells needed to encode interrupt
+ source should be 1.
+- interrupts: interrupt reference to primary interrupt controller
+
+Please refer to interrupts.txt in this directory for details of the common
+Interrupt Controllers bindings used by client devices.
+
+Example:
+ kirq0: keystone_irq0@026202a0 {
+ compatible = "ti,keystone-irq";
+ ti,syscon-dev = <&devctrl 0x2a0>;
+ interrupts = <GIC_SPI 4 IRQ_TYPE_EDGE_RISING>;
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ };
+
+ dsp0: dsp0 {
+ compatible = "linux,rproc-user";
+ ...
+ interrupt-parent = <&kirq0>;
+ interrupts = <10 2>;
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index aefa948..71e86af 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4948,6 +4948,7 @@
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/core
T: git git://git.infradead.org/users/jcooper/linux.git irqchip/core
+F: Documentation/devicetree/bindings/interrupt-controller/
F: drivers/irqchip/
IRQ DOMAINS (IRQ NUMBER MAPPING LIBRARY)
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index b8632bf..c888964 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -113,3 +113,10 @@
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 73052ba..fc2e4f7 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -2,6 +2,7 @@
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,3 +35,4 @@
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-hip04.c b/drivers/irqchip/irq-hip04.c
new file mode 100644
index 0000000..9c8f8335
--- /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;
+
+ 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);
diff --git a/drivers/irqchip/irq-keystone.c b/drivers/irqchip/irq-keystone.c
new file mode 100644
index 0000000..608abf9
--- /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 <sajesh@ti.com>
+ * Grygorii Strashko <grygorii.strashko@ti.com>
+ *
+ * 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 <linux/irq.h>
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#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;
+ int 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");
diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c
index 3ee78f0..542e850 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 <linux/clk.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -30,6 +31,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_data/irq-renesas-intc-irqpin.h>
+#include <linux/pm_runtime.h>
#define INTC_IRQPIN_MAX 8 /* maximum 8 interrupts per driver instance */
@@ -75,6 +77,7 @@
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 @@
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;
@@ -329,7 +347,8 @@
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,25 +356,24 @@
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");
- ret = -ENOMEM;
- goto err0;
+ dev_err(dev, "failed to allocate driver data\n");
+ return -ENOMEM;
}
/* deal with driver instance configuration */
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)
@@ -364,11 +382,20 @@
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);
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 +413,7 @@
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 +434,15 @@
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;
}
@@ -454,39 +481,36 @@
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->irq_set_wake = intc_irqpin_irq_set_wake;
+ irq_chip->flags = 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;
}
@@ -497,12 +521,12 @@
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);
}
@@ -511,6 +535,8 @@
err1:
irq_domain_remove(p->irq_domain);
err0:
+ pm_runtime_put(dev);
+ pm_runtime_disable(dev);
return ret;
}
@@ -519,7 +545,8 @@
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;
}