aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGrygorii Strashko <grygorii.strashko@ti.com>2014-07-23 10:40:30 -0400
committerJason Cooper <jason@lakedaemon.net>2014-08-17 15:13:23 -0400
commit89323f8c504a8653c66fe4a314723b36b07e29e1 (patch)
tree57c0e382912754cfba1e95c715a54332014c3f5e
parent7d1311b93e58ed55f3a31cc8f94c4b8fe988a2b9 (diff)
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 <grygorii.strashko@ti.com> Link: https://lkml.kernel.org/r/1406126430-9978-1-git-send-email-grygorii.strashko@ti.com Signed-off-by: Jason Cooper <jason@lakedaemon.net>
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/ti,keystone-irq.txt36
-rw-r--r--drivers/irqchip/Kconfig7
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/irq-keystone.c232
4 files changed, 276 insertions, 0 deletions
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 000000000000..d9bb106bdd16
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/ti,keystone-irq.txt
@@ -0,0 +1,36 @@
1Keystone 2 IRQ controller IP
2
3On Keystone SOCs, DSP cores can send interrupts to ARM
4host using the IRQ controller IP. It provides 28 IRQ signals to ARM.
5The IRQ handler running on HOST OS can identify DSP signal source by
6analyzing SRCCx bits in IPCARx registers. This is one of the component
7used by the IPC mechanism used on Keystone SOCs.
8
9Required Properties:
10- compatible: should be "ti,keystone-irq"
11- ti,syscon-dev : phandle and offset pair. The phandle to syscon used to
12 access device control registers and the offset inside
13 device control registers range.
14- interrupt-controller : Identifies the node as an interrupt controller
15- #interrupt-cells : Specifies the number of cells needed to encode interrupt
16 source should be 1.
17- interrupts: interrupt reference to primary interrupt controller
18
19Please refer to interrupts.txt in this directory for details of the common
20Interrupt Controllers bindings used by client devices.
21
22Example:
23 kirq0: keystone_irq0@026202a0 {
24 compatible = "ti,keystone-irq";
25 ti,syscon-dev = <&devctrl 0x2a0>;
26 interrupts = <GIC_SPI 4 IRQ_TYPE_EDGE_RISING>;
27 interrupt-controller;
28 #interrupt-cells = <1>;
29 };
30
31 dsp0: dsp0 {
32 compatible = "linux,rproc-user";
33 ...
34 interrupt-parent = <&kirq0>;
35 interrupts = <10 2>;
36 };
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
113 The primary irqchip invokes the crossbar's callback which inturn allocates 113 The primary irqchip invokes the crossbar's callback which inturn allocates
114 a free irq and configures the IP. Thus the peripheral interrupts are 114 a free irq and configures the IP. Thus the peripheral interrupts are
115 routed to one of the free irqchip interrupt lines. 115 routed to one of the free irqchip interrupt lines.
116
117config KEYSTONE_IRQ
118 tristate "Keystone 2 IRQ controller IP"
119 depends on ARCH_KEYSTONE
120 help
121 Support for Texas Instruments Keystone 2 IRQ controller IP which
122 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
34obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o 34obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o
35obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o 35obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o
36obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o 36obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o
37obj-$(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 @@
1/*
2 * Texas Instruments Keystone IRQ controller IP driver
3 *
4 * Copyright (C) 2014 Texas Instruments, Inc.
5 * Author: Sajesh Kumar Saran <sajesh@ti.com>
6 * Grygorii Strashko <grygorii.strashko@ti.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation version 2.
11 *
12 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
13 * kind, whether express or implied; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 */
17
18#include <linux/irq.h>
19#include <linux/bitops.h>
20#include <linux/module.h>
21#include <linux/moduleparam.h>
22#include <linux/irqdomain.h>
23#include <linux/irqchip/chained_irq.h>
24#include <linux/of.h>
25#include <linux/of_platform.h>
26#include <linux/mfd/syscon.h>
27#include <linux/regmap.h>
28#include "irqchip.h"
29
30
31/* The source ID bits start from 4 to 31 (total 28 bits)*/
32#define BIT_OFS 4
33#define KEYSTONE_N_IRQ (32 - BIT_OFS)
34
35struct keystone_irq_device {
36 struct device *dev;
37 struct irq_chip chip;
38 u32 mask;
39 u32 irq;
40 struct irq_domain *irqd;
41 struct regmap *devctrl_regs;
42 u32 devctrl_offset;
43};
44
45static inline u32 keystone_irq_readl(struct keystone_irq_device *kirq)
46{
47 int ret;
48 u32 val = 0;
49
50 ret = regmap_read(kirq->devctrl_regs, kirq->devctrl_offset, &val);
51 if (ret < 0)
52 dev_dbg(kirq->dev, "irq read failed ret(%d)\n", ret);
53 return val;
54}
55
56static inline void
57keystone_irq_writel(struct keystone_irq_device *kirq, u32 value)
58{
59 int ret;
60
61 ret = regmap_write(kirq->devctrl_regs, kirq->devctrl_offset, value);
62 if (ret < 0)
63 dev_dbg(kirq->dev, "irq write failed ret(%d)\n", ret);
64}
65
66static void keystone_irq_setmask(struct irq_data *d)
67{
68 struct keystone_irq_device *kirq = irq_data_get_irq_chip_data(d);
69
70 kirq->mask |= BIT(d->hwirq);
71 dev_dbg(kirq->dev, "mask %lu [%x]\n", d->hwirq, kirq->mask);
72}
73
74static void keystone_irq_unmask(struct irq_data *d)
75{
76 struct keystone_irq_device *kirq = irq_data_get_irq_chip_data(d);
77
78 kirq->mask &= ~BIT(d->hwirq);
79 dev_dbg(kirq->dev, "unmask %lu [%x]\n", d->hwirq, kirq->mask);
80}
81
82static void keystone_irq_ack(struct irq_data *d)
83{
84 /* nothing to do here */
85}
86
87static void keystone_irq_handler(unsigned irq, struct irq_desc *desc)
88{
89 struct keystone_irq_device *kirq = irq_desc_get_handler_data(desc);
90 unsigned long pending;
91 int src, virq;
92
93 dev_dbg(kirq->dev, "start irq %d\n", irq);
94
95 chained_irq_enter(irq_desc_get_chip(desc), desc);
96
97 pending = keystone_irq_readl(kirq);
98 keystone_irq_writel(kirq, pending);
99
100 dev_dbg(kirq->dev, "pending 0x%lx, mask 0x%x\n", pending, kirq->mask);
101
102 pending = (pending >> BIT_OFS) & ~kirq->mask;
103
104 dev_dbg(kirq->dev, "pending after mask 0x%lx\n", pending);
105
106 for (src = 0; src < KEYSTONE_N_IRQ; src++) {
107 if (BIT(src) & pending) {
108 virq = irq_find_mapping(kirq->irqd, src);
109 dev_dbg(kirq->dev, "dispatch bit %d, virq %d\n",
110 src, virq);
111 if (!virq)
112 dev_warn(kirq->dev, "sporious irq detected hwirq %d, virq %d\n",
113 src, virq);
114 generic_handle_irq(virq);
115 }
116 }
117
118 chained_irq_exit(irq_desc_get_chip(desc), desc);
119
120 dev_dbg(kirq->dev, "end irq %d\n", irq);
121}
122
123static int keystone_irq_map(struct irq_domain *h, unsigned int virq,
124 irq_hw_number_t hw)
125{
126 struct keystone_irq_device *kirq = h->host_data;
127
128 irq_set_chip_data(virq, kirq);
129 irq_set_chip_and_handler(virq, &kirq->chip, handle_level_irq);
130 set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
131 return 0;
132}
133
134static struct irq_domain_ops keystone_irq_ops = {
135 .map = keystone_irq_map,
136 .xlate = irq_domain_xlate_onecell,
137};
138
139static int keystone_irq_probe(struct platform_device *pdev)
140{
141 struct device *dev = &pdev->dev;
142 struct device_node *np = dev->of_node;
143 struct keystone_irq_device *kirq;
144 int ret;
145
146 if (np == NULL)
147 return -EINVAL;
148
149 kirq = devm_kzalloc(dev, sizeof(*kirq), GFP_KERNEL);
150 if (!kirq)
151 return -ENOMEM;
152
153 kirq->devctrl_regs =
154 syscon_regmap_lookup_by_phandle(np, "ti,syscon-dev");
155 if (IS_ERR(kirq->devctrl_regs))
156 return PTR_ERR(kirq->devctrl_regs);
157
158 ret = of_property_read_u32_index(np, "ti,syscon-dev", 1,
159 &kirq->devctrl_offset);
160 if (ret) {
161 dev_err(dev, "couldn't read the devctrl_offset offset!\n");
162 return ret;
163 }
164
165 kirq->irq = platform_get_irq(pdev, 0);
166 if (kirq->irq < 0) {
167 dev_err(dev, "no irq resource %d\n", kirq->irq);
168 return kirq->irq;
169 }
170
171 kirq->dev = dev;
172 kirq->mask = ~0x0;
173 kirq->chip.name = "keystone-irq";
174 kirq->chip.irq_ack = keystone_irq_ack;
175 kirq->chip.irq_mask = keystone_irq_setmask;
176 kirq->chip.irq_unmask = keystone_irq_unmask;
177
178 kirq->irqd = irq_domain_add_linear(np, KEYSTONE_N_IRQ,
179 &keystone_irq_ops, kirq);
180 if (!kirq->irqd) {
181 dev_err(dev, "IRQ domain registration failed\n");
182 return -ENODEV;
183 }
184
185 platform_set_drvdata(pdev, kirq);
186
187 irq_set_chained_handler(kirq->irq, keystone_irq_handler);
188 irq_set_handler_data(kirq->irq, kirq);
189
190 /* clear all source bits */
191 keystone_irq_writel(kirq, ~0x0);
192
193 dev_info(dev, "irqchip registered, nr_irqs %u\n", KEYSTONE_N_IRQ);
194
195 return 0;
196}
197
198static int keystone_irq_remove(struct platform_device *pdev)
199{
200 struct keystone_irq_device *kirq = platform_get_drvdata(pdev);
201 int hwirq;
202
203 for (hwirq = 0; hwirq < KEYSTONE_N_IRQ; hwirq++)
204 irq_dispose_mapping(irq_find_mapping(kirq->irqd, hwirq));
205
206 irq_domain_remove(kirq->irqd);
207 return 0;
208}
209
210static const struct of_device_id keystone_irq_dt_ids[] = {
211 { .compatible = "ti,keystone-irq", },
212 {},
213};
214MODULE_DEVICE_TABLE(of, keystone_irq_dt_ids);
215
216static struct platform_driver keystone_irq_device_driver = {
217 .probe = keystone_irq_probe,
218 .remove = keystone_irq_remove,
219 .driver = {
220 .name = "keystone_irq",
221 .owner = THIS_MODULE,
222 .of_match_table = of_match_ptr(keystone_irq_dt_ids),
223 }
224};
225
226module_platform_driver(keystone_irq_device_driver);
227
228MODULE_AUTHOR("Texas Instruments");
229MODULE_AUTHOR("Sajesh Kumar Saran");
230MODULE_AUTHOR("Grygorii Strashko");
231MODULE_DESCRIPTION("Keystone IRQ chip");
232MODULE_LICENSE("GPL v2");