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/Makefile1
-rw-r--r--drivers/irqchip/irq-keystone.c232
-rw-r--r--drivers/irqchip/irq-renesas-intc-irqpin.c85
4 files changed, 296 insertions, 29 deletions
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 1dca5bc2cc43..fc2e4f77d3db 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -35,3 +35,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..608abf9c9283
--- /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 3ee78f02e5d7..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 <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 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;
@@ -329,7 +347,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,25 +356,24 @@ 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");
- 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 @@ 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);
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 @@ 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 +434,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;
}
@@ -454,39 +481,36 @@ 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->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 @@ 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);
}
@@ -511,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;
}
@@ -519,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;
}