aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/irqchip/irq-keystone.c
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 /drivers/irqchip/irq-keystone.c
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>
Diffstat (limited to 'drivers/irqchip/irq-keystone.c')
-rw-r--r--drivers/irqchip/irq-keystone.c232
1 files changed, 232 insertions, 0 deletions
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");