summaryrefslogtreecommitdiff
path: root/drivers/irqchip
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/irqchip')
-rw-r--r--drivers/irqchip/Kconfig7
-rw-r--r--drivers/irqchip/Makefile2
-rw-r--r--drivers/irqchip/irq-digicolor.c4
-rw-r--r--drivers/irqchip/irq-gic.c2
-rw-r--r--drivers/irqchip/irq-mips-gic.c21
-rw-r--r--drivers/irqchip/irq-renesas-irqc.c4
-rw-r--r--drivers/irqchip/irq-st.c206
-rw-r--r--drivers/irqchip/irq-vf610-mscm-ir.c212
8 files changed, 453 insertions, 5 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index cc79d2a5a8c2..c8d260e33a90 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -110,6 +110,13 @@ config RENESAS_IRQC
bool
select IRQ_DOMAIN
+config ST_IRQCHIP
+ bool
+ select REGMAP
+ select MFD_SYSCON
+ help
+ Enables SysCfg Controlled IRQs on STi based platforms.
+
config TB10X_IRQC
bool
select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 42965d2476bb..f117092ae014 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -33,10 +33,12 @@ obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o
obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
obj-$(CONFIG_ARCH_NSPIRE) += irq-zevio.o
obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o
+obj-$(CONFIG_ST_IRQCHIP) += irq-st.o
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_SOC_VF610) += irq-vf610-mscm-ir.o
obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o
obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o
obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o
diff --git a/drivers/irqchip/irq-digicolor.c b/drivers/irqchip/irq-digicolor.c
index 930a2a2fac7f..3cbc658afe27 100644
--- a/drivers/irqchip/irq-digicolor.c
+++ b/drivers/irqchip/irq-digicolor.c
@@ -55,8 +55,8 @@ static void __exception_irq_entry digicolor_handle_irq(struct pt_regs *regs)
} while (1);
}
-static void digicolor_set_gc(void __iomem *reg_base, unsigned irq_base,
- unsigned en_reg, unsigned ack_reg)
+static void __init digicolor_set_gc(void __iomem *reg_base, unsigned irq_base,
+ unsigned en_reg, unsigned ack_reg)
{
struct irq_chip_generic *gc;
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 4634cf7d0ec3..50e70a8c7ec4 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -349,7 +349,7 @@ static u8 gic_get_cpumask(struct gic_chip_data *gic)
break;
}
- if (!mask)
+ if (!mask && num_possible_cpus() > 1)
pr_crit("GIC CPU mask not found - kernel will fail to boot.\n");
return mask;
diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c
index 9acdc080e7ec..f2d269bca789 100644
--- a/drivers/irqchip/irq-mips-gic.c
+++ b/drivers/irqchip/irq-mips-gic.c
@@ -166,6 +166,27 @@ cycle_t gic_read_compare(void)
return (((cycle_t) hi) << 32) + lo;
}
+
+void gic_start_count(void)
+{
+ u32 gicconfig;
+
+ /* Start the counter */
+ gicconfig = gic_read(GIC_REG(SHARED, GIC_SH_CONFIG));
+ gicconfig &= ~(1 << GIC_SH_CONFIG_COUNTSTOP_SHF);
+ gic_write(GIC_REG(SHARED, GIC_SH_CONFIG), gicconfig);
+}
+
+void gic_stop_count(void)
+{
+ u32 gicconfig;
+
+ /* Stop the counter */
+ gicconfig = gic_read(GIC_REG(SHARED, GIC_SH_CONFIG));
+ gicconfig |= 1 << GIC_SH_CONFIG_COUNTSTOP_SHF;
+ gic_write(GIC_REG(SHARED, GIC_SH_CONFIG), gicconfig);
+}
+
#endif
static bool gic_local_irq_is_routable(int intr)
diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c
index 384e6ed61d7c..2ea3412fdf8c 100644
--- a/drivers/irqchip/irq-renesas-irqc.c
+++ b/drivers/irqchip/irq-renesas-irqc.c
@@ -94,7 +94,7 @@ static int irqc_irq_set_type(struct irq_data *d, unsigned int type)
struct irqc_priv *p = irq_data_get_irq_chip_data(d);
int hw_irq = irqd_to_hwirq(d);
unsigned char value = irqc_sense[type & IRQ_TYPE_SENSE_MASK];
- unsigned long tmp;
+ u32 tmp;
irqc_dbg(&p->irq[hw_irq], "sense");
@@ -112,7 +112,7 @@ static irqreturn_t irqc_irq_handler(int irq, void *dev_id)
{
struct irqc_irq *i = dev_id;
struct irqc_priv *p = i->p;
- unsigned long bit = BIT(i->hw_irq);
+ u32 bit = BIT(i->hw_irq);
irqc_dbg(i, "demux1");
diff --git a/drivers/irqchip/irq-st.c b/drivers/irqchip/irq-st.c
new file mode 100644
index 000000000000..9af48a85c16f
--- /dev/null
+++ b/drivers/irqchip/irq-st.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2014 STMicroelectronics – All Rights Reserved
+ *
+ * Author: Lee Jones <lee.jones@linaro.org>
+ *
+ * This is a re-write of Christophe Kerello's PMU driver.
+ *
+ * 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.
+ */
+
+#include <dt-bindings/interrupt-controller/irq-st.h>
+#include <linux/err.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define STIH415_SYSCFG_642 0x0a8
+#define STIH416_SYSCFG_7543 0x87c
+#define STIH407_SYSCFG_5102 0x198
+#define STID127_SYSCFG_734 0x088
+
+#define ST_A9_IRQ_MASK 0x001FFFFF
+#define ST_A9_IRQ_MAX_CHANS 2
+
+#define ST_A9_IRQ_EN_CTI_0 BIT(0)
+#define ST_A9_IRQ_EN_CTI_1 BIT(1)
+#define ST_A9_IRQ_EN_PMU_0 BIT(2)
+#define ST_A9_IRQ_EN_PMU_1 BIT(3)
+#define ST_A9_IRQ_EN_PL310_L2 BIT(4)
+#define ST_A9_IRQ_EN_EXT_0 BIT(5)
+#define ST_A9_IRQ_EN_EXT_1 BIT(6)
+#define ST_A9_IRQ_EN_EXT_2 BIT(7)
+
+#define ST_A9_FIQ_N_SEL(dev, chan) (dev << (8 + (chan * 3)))
+#define ST_A9_IRQ_N_SEL(dev, chan) (dev << (14 + (chan * 3)))
+#define ST_A9_EXTIRQ_INV_SEL(dev) (dev << 20)
+
+struct st_irq_syscfg {
+ struct regmap *regmap;
+ unsigned int syscfg;
+ unsigned int config;
+ bool ext_inverted;
+};
+
+static const struct of_device_id st_irq_syscfg_match[] = {
+ {
+ .compatible = "st,stih415-irq-syscfg",
+ .data = (void *)STIH415_SYSCFG_642,
+ },
+ {
+ .compatible = "st,stih416-irq-syscfg",
+ .data = (void *)STIH416_SYSCFG_7543,
+ },
+ {
+ .compatible = "st,stih407-irq-syscfg",
+ .data = (void *)STIH407_SYSCFG_5102,
+ },
+ {
+ .compatible = "st,stid127-irq-syscfg",
+ .data = (void *)STID127_SYSCFG_734,
+ },
+ {}
+};
+
+static int st_irq_xlate(struct platform_device *pdev,
+ int device, int channel, bool irq)
+{
+ struct st_irq_syscfg *ddata = dev_get_drvdata(&pdev->dev);
+
+ /* Set the device enable bit. */
+ switch (device) {
+ case ST_IRQ_SYSCFG_EXT_0:
+ ddata->config |= ST_A9_IRQ_EN_EXT_0;
+ break;
+ case ST_IRQ_SYSCFG_EXT_1:
+ ddata->config |= ST_A9_IRQ_EN_EXT_1;
+ break;
+ case ST_IRQ_SYSCFG_EXT_2:
+ ddata->config |= ST_A9_IRQ_EN_EXT_2;
+ break;
+ case ST_IRQ_SYSCFG_CTI_0:
+ ddata->config |= ST_A9_IRQ_EN_CTI_0;
+ break;
+ case ST_IRQ_SYSCFG_CTI_1:
+ ddata->config |= ST_A9_IRQ_EN_CTI_1;
+ break;
+ case ST_IRQ_SYSCFG_PMU_0:
+ ddata->config |= ST_A9_IRQ_EN_PMU_0;
+ break;
+ case ST_IRQ_SYSCFG_PMU_1:
+ ddata->config |= ST_A9_IRQ_EN_PMU_1;
+ break;
+ case ST_IRQ_SYSCFG_pl310_L2:
+ ddata->config |= ST_A9_IRQ_EN_PL310_L2;
+ break;
+ case ST_IRQ_SYSCFG_DISABLED:
+ return 0;
+ default:
+ dev_err(&pdev->dev, "Unrecognised device %d\n", device);
+ return -EINVAL;
+ }
+
+ /* Select IRQ/FIQ channel for device. */
+ ddata->config |= irq ?
+ ST_A9_IRQ_N_SEL(device, channel) :
+ ST_A9_FIQ_N_SEL(device, channel);
+
+ return 0;
+}
+
+static int st_irq_syscfg_enable(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct st_irq_syscfg *ddata = dev_get_drvdata(&pdev->dev);
+ int channels, ret, i;
+ u32 device, invert;
+
+ channels = of_property_count_u32_elems(np, "st,irq-device");
+ if (channels != ST_A9_IRQ_MAX_CHANS) {
+ dev_err(&pdev->dev, "st,enable-irq-device must have 2 elems\n");
+ return -EINVAL;
+ }
+
+ channels = of_property_count_u32_elems(np, "st,fiq-device");
+ if (channels != ST_A9_IRQ_MAX_CHANS) {
+ dev_err(&pdev->dev, "st,enable-fiq-device must have 2 elems\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ST_A9_IRQ_MAX_CHANS; i++) {
+ of_property_read_u32_index(np, "st,irq-device", i, &device);
+
+ ret = st_irq_xlate(pdev, device, i, true);
+ if (ret)
+ return ret;
+
+ of_property_read_u32_index(np, "st,fiq-device", i, &device);
+
+ ret = st_irq_xlate(pdev, device, i, false);
+ if (ret)
+ return ret;
+ }
+
+ /* External IRQs may be inverted. */
+ of_property_read_u32(np, "st,invert-ext", &invert);
+ ddata->config |= ST_A9_EXTIRQ_INV_SEL(invert);
+
+ return regmap_update_bits(ddata->regmap, ddata->syscfg,
+ ST_A9_IRQ_MASK, ddata->config);
+}
+
+static int st_irq_syscfg_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *match;
+ struct st_irq_syscfg *ddata;
+
+ ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
+ if (!ddata)
+ return -ENOMEM;
+
+ match = of_match_device(st_irq_syscfg_match, &pdev->dev);
+ if (!match)
+ return -ENODEV;
+
+ ddata->syscfg = (unsigned int)match->data;
+
+ ddata->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+ if (IS_ERR(ddata->regmap)) {
+ dev_err(&pdev->dev, "syscfg phandle missing\n");
+ return PTR_ERR(ddata->regmap);
+ }
+
+ dev_set_drvdata(&pdev->dev, ddata);
+
+ return st_irq_syscfg_enable(pdev);
+}
+
+static int st_irq_syscfg_resume(struct device *dev)
+{
+ struct st_irq_syscfg *ddata = dev_get_drvdata(dev);
+
+ return regmap_update_bits(ddata->regmap, ddata->syscfg,
+ ST_A9_IRQ_MASK, ddata->config);
+}
+
+static SIMPLE_DEV_PM_OPS(st_irq_syscfg_pm_ops, NULL, st_irq_syscfg_resume);
+
+static struct platform_driver st_irq_syscfg_driver = {
+ .driver = {
+ .name = "st_irq_syscfg",
+ .pm = &st_irq_syscfg_pm_ops,
+ .of_match_table = st_irq_syscfg_match,
+ },
+ .probe = st_irq_syscfg_probe,
+};
+
+static int __init st_irq_syscfg_init(void)
+{
+ return platform_driver_register(&st_irq_syscfg_driver);
+}
+core_initcall(st_irq_syscfg_init);
diff --git a/drivers/irqchip/irq-vf610-mscm-ir.c b/drivers/irqchip/irq-vf610-mscm-ir.c
new file mode 100644
index 000000000000..9521057d4744
--- /dev/null
+++ b/drivers/irqchip/irq-vf610-mscm-ir.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2014-2015 Toradex AG
+ * Author: Stefan Agner <stefan@agner.ch>
+ *
+ * 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.
+ *
+ *
+ * IRQ chip driver for MSCM interrupt router available on Vybrid SoC's.
+ * The interrupt router is between the CPU's interrupt controller and the
+ * peripheral. The router allows to route the peripheral interrupts to
+ * one of the two available CPU's on Vybrid VF6xx SoC's (Cortex-A5 or
+ * Cortex-M4). The router will be configured transparently on a IRQ
+ * request.
+ *
+ * o All peripheral interrupts of the Vybrid SoC can be routed to
+ * CPU 0, CPU 1 or both. The routing is useful for dual-core
+ * variants of Vybrid SoC such as VF6xx. This driver routes the
+ * requested interrupt to the CPU currently running on.
+ *
+ * o It is required to setup the interrupt router even on single-core
+ * variants of Vybrid.
+ */
+
+#include <linux/cpu_pm.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/mfd/syscon.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+
+#include "irqchip.h"
+
+#define MSCM_CPxNUM 0x4
+
+#define MSCM_IRSPRC(n) (0x80 + 2 * (n))
+#define MSCM_IRSPRC_CPEN_MASK 0x3
+
+#define MSCM_IRSPRC_NUM 112
+
+struct vf610_mscm_ir_chip_data {
+ void __iomem *mscm_ir_base;
+ u16 cpu_mask;
+ u16 saved_irsprc[MSCM_IRSPRC_NUM];
+};
+
+static struct vf610_mscm_ir_chip_data *mscm_ir_data;
+
+static inline void vf610_mscm_ir_save(struct vf610_mscm_ir_chip_data *data)
+{
+ int i;
+
+ for (i = 0; i < MSCM_IRSPRC_NUM; i++)
+ data->saved_irsprc[i] = readw_relaxed(data->mscm_ir_base + MSCM_IRSPRC(i));
+}
+
+static inline void vf610_mscm_ir_restore(struct vf610_mscm_ir_chip_data *data)
+{
+ int i;
+
+ for (i = 0; i < MSCM_IRSPRC_NUM; i++)
+ writew_relaxed(data->saved_irsprc[i], data->mscm_ir_base + MSCM_IRSPRC(i));
+}
+
+static int vf610_mscm_ir_notifier(struct notifier_block *self,
+ unsigned long cmd, void *v)
+{
+ switch (cmd) {
+ case CPU_CLUSTER_PM_ENTER:
+ vf610_mscm_ir_save(mscm_ir_data);
+ break;
+ case CPU_CLUSTER_PM_ENTER_FAILED:
+ case CPU_CLUSTER_PM_EXIT:
+ vf610_mscm_ir_restore(mscm_ir_data);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block mscm_ir_notifier_block = {
+ .notifier_call = vf610_mscm_ir_notifier,
+};
+
+static void vf610_mscm_ir_enable(struct irq_data *data)
+{
+ irq_hw_number_t hwirq = data->hwirq;
+ struct vf610_mscm_ir_chip_data *chip_data = data->chip_data;
+ u16 irsprc;
+
+ irsprc = readw_relaxed(chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
+ irsprc &= MSCM_IRSPRC_CPEN_MASK;
+
+ WARN_ON(irsprc & ~chip_data->cpu_mask);
+
+ writew_relaxed(chip_data->cpu_mask,
+ chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
+
+ irq_chip_unmask_parent(data);
+}
+
+static void vf610_mscm_ir_disable(struct irq_data *data)
+{
+ irq_hw_number_t hwirq = data->hwirq;
+ struct vf610_mscm_ir_chip_data *chip_data = data->chip_data;
+
+ writew_relaxed(0x0, chip_data->mscm_ir_base + MSCM_IRSPRC(hwirq));
+
+ irq_chip_mask_parent(data);
+}
+
+static struct irq_chip vf610_mscm_ir_irq_chip = {
+ .name = "mscm-ir",
+ .irq_mask = irq_chip_mask_parent,
+ .irq_unmask = irq_chip_unmask_parent,
+ .irq_eoi = irq_chip_eoi_parent,
+ .irq_enable = vf610_mscm_ir_enable,
+ .irq_disable = vf610_mscm_ir_disable,
+ .irq_retrigger = irq_chip_retrigger_hierarchy,
+ .irq_set_affinity = irq_chip_set_affinity_parent,
+};
+
+static int vf610_mscm_ir_domain_alloc(struct irq_domain *domain, unsigned int virq,
+ unsigned int nr_irqs, void *arg)
+{
+ int i;
+ irq_hw_number_t hwirq;
+ struct of_phandle_args *irq_data = arg;
+ struct of_phandle_args gic_data;
+
+ if (irq_data->args_count != 2)
+ return -EINVAL;
+
+ hwirq = irq_data->args[0];
+ for (i = 0; i < nr_irqs; i++)
+ irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
+ &vf610_mscm_ir_irq_chip,
+ domain->host_data);
+
+ gic_data.np = domain->parent->of_node;
+ gic_data.args_count = 3;
+ gic_data.args[0] = GIC_SPI;
+ gic_data.args[1] = irq_data->args[0];
+ gic_data.args[2] = irq_data->args[1];
+ return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_data);
+}
+
+static const struct irq_domain_ops mscm_irq_domain_ops = {
+ .xlate = irq_domain_xlate_twocell,
+ .alloc = vf610_mscm_ir_domain_alloc,
+ .free = irq_domain_free_irqs_common,
+};
+
+static int __init vf610_mscm_ir_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ struct irq_domain *domain, *domain_parent;
+ struct regmap *mscm_cp_regmap;
+ int ret, cpuid;
+
+ domain_parent = irq_find_host(parent);
+ if (!domain_parent) {
+ pr_err("vf610_mscm_ir: interrupt-parent not found\n");
+ return -EINVAL;
+ }
+
+ mscm_ir_data = kzalloc(sizeof(*mscm_ir_data), GFP_KERNEL);
+ if (!mscm_ir_data)
+ return -ENOMEM;
+
+ mscm_ir_data->mscm_ir_base = of_io_request_and_map(node, 0, "mscm-ir");
+
+ if (!mscm_ir_data->mscm_ir_base) {
+ pr_err("vf610_mscm_ir: unable to map mscm register\n");
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ mscm_cp_regmap = syscon_regmap_lookup_by_phandle(node, "fsl,cpucfg");
+ if (IS_ERR(mscm_cp_regmap)) {
+ ret = PTR_ERR(mscm_cp_regmap);
+ pr_err("vf610_mscm_ir: regmap lookup for cpucfg failed\n");
+ goto out_unmap;
+ }
+
+ regmap_read(mscm_cp_regmap, MSCM_CPxNUM, &cpuid);
+ mscm_ir_data->cpu_mask = 0x1 << cpuid;
+
+ domain = irq_domain_add_hierarchy(domain_parent, 0,
+ MSCM_IRSPRC_NUM, node,
+ &mscm_irq_domain_ops, mscm_ir_data);
+ if (!domain) {
+ ret = -ENOMEM;
+ goto out_unmap;
+ }
+
+ cpu_pm_register_notifier(&mscm_ir_notifier_block);
+
+ return 0;
+
+out_unmap:
+ iounmap(mscm_ir_data->mscm_ir_base);
+out_free:
+ kfree(mscm_ir_data);
+ return ret;
+}
+IRQCHIP_DECLARE(vf610_mscm_ir, "fsl,vf610-mscm-ir", vf610_mscm_ir_of_init);