Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Cristian Birsan <cristian.birsan@microchip.com>
4 * Joshua Henderson <joshua.henderson@microchip.com>
5 * Copyright (C) 2016 Microchip Technology Inc. All rights reserved.
6 */
7#include <linux/kernel.h>
8#include <linux/module.h>
9#include <linux/interrupt.h>
10#include <linux/irqdomain.h>
11#include <linux/of_address.h>
12#include <linux/slab.h>
13#include <linux/io.h>
14#include <linux/irqchip.h>
15#include <linux/irq.h>
16#include <linux/platform_data/pic32.h>
17
18#ifdef CONFIG_MIPS
19#include <asm/irq.h>
20#include <asm/traps.h>
21#endif
22
23#define REG_INTCON 0x0000
24#define REG_INTSTAT 0x0020
25#define REG_IFS_OFFSET 0x0040
26#define REG_IEC_OFFSET 0x00C0
27#define REG_IPC_OFFSET 0x0140
28#define REG_OFF_OFFSET 0x0540
29
30#define MAJPRI_MASK 0x07
31#define SUBPRI_MASK 0x03
32#define PRIORITY_MASK 0x1F
33
34#define PIC32_INT_PRI(pri, subpri) \
35 ((((pri) & MAJPRI_MASK) << 2) | ((subpri) & SUBPRI_MASK))
36
37struct evic_chip_data {
38 u32 irq_types[NR_IRQS];
39 u32 ext_irqs[8];
40};
41
42static struct irq_domain *evic_irq_domain;
43static void __iomem *evic_base;
44
45#ifdef CONFIG_MIPS
46asmlinkage void __weak plat_irq_dispatch(void)
47{
48 unsigned int hwirq;
49
50 hwirq = readl(evic_base + REG_INTSTAT) & 0xFF;
51 do_domain_IRQ(evic_irq_domain, hwirq);
52}
53#else
54static __maybe_unused void (*board_bind_eic_interrupt)(int irq, int regset);
55#endif
56
57static struct evic_chip_data *irqd_to_priv(struct irq_data *data)
58{
59 return (struct evic_chip_data *)data->domain->host_data;
60}
61
62static int pic32_set_ext_polarity(int bit, u32 type)
63{
64 /*
65 * External interrupts can be either edge rising or edge falling,
66 * but not both.
67 */
68 switch (type) {
69 case IRQ_TYPE_EDGE_RISING:
70 writel(BIT(bit), evic_base + PIC32_SET(REG_INTCON));
71 break;
72 case IRQ_TYPE_EDGE_FALLING:
73 writel(BIT(bit), evic_base + PIC32_CLR(REG_INTCON));
74 break;
75 default:
76 return -EINVAL;
77 }
78
79 return 0;
80}
81
82static int pic32_set_type_edge(struct irq_data *data,
83 unsigned int flow_type)
84{
85 struct evic_chip_data *priv = irqd_to_priv(data);
86 int ret;
87 int i;
88
89 if (!(flow_type & IRQ_TYPE_EDGE_BOTH))
90 return -EBADR;
91
92 /* set polarity for external interrupts only */
93 for (i = 0; i < ARRAY_SIZE(priv->ext_irqs); i++) {
94 if (priv->ext_irqs[i] == data->hwirq) {
95 ret = pic32_set_ext_polarity(i, flow_type);
96 if (ret)
97 return ret;
98 }
99 }
100
101 irqd_set_trigger_type(data, flow_type);
102
103 return IRQ_SET_MASK_OK;
104}
105
106static void pic32_bind_evic_interrupt(int irq, int set)
107{
108 writel(set, evic_base + REG_OFF_OFFSET + irq * 4);
109}
110
111static void pic32_set_irq_priority(int irq, int priority)
112{
113 u32 reg, shift;
114
115 reg = irq / 4;
116 shift = (irq % 4) * 8;
117
118 writel(PRIORITY_MASK << shift,
119 evic_base + PIC32_CLR(REG_IPC_OFFSET + reg * 0x10));
120 writel(priority << shift,
121 evic_base + PIC32_SET(REG_IPC_OFFSET + reg * 0x10));
122}
123
124#define IRQ_REG_MASK(_hwirq, _reg, _mask) \
125 do { \
126 _reg = _hwirq / 32; \
127 _mask = 1 << (_hwirq % 32); \
128 } while (0)
129
130static int pic32_irq_domain_map(struct irq_domain *d, unsigned int virq,
131 irq_hw_number_t hw)
132{
133 struct evic_chip_data *priv = d->host_data;
134 struct irq_data *data;
135 int ret;
136 u32 iecclr, ifsclr;
137 u32 reg, mask;
138
139 ret = irq_map_generic_chip(d, virq, hw);
140 if (ret)
141 return ret;
142
143 /*
144 * Piggyback on xlate function to move to an alternate chip as necessary
145 * at time of mapping instead of allowing the flow handler/chip to be
146 * changed later. This requires all interrupts to be configured through
147 * DT.
148 */
149 if (priv->irq_types[hw] & IRQ_TYPE_SENSE_MASK) {
150 data = irq_domain_get_irq_data(d, virq);
151 irqd_set_trigger_type(data, priv->irq_types[hw]);
152 irq_setup_alt_chip(data, priv->irq_types[hw]);
153 }
154
155 IRQ_REG_MASK(hw, reg, mask);
156
157 iecclr = PIC32_CLR(REG_IEC_OFFSET + reg * 0x10);
158 ifsclr = PIC32_CLR(REG_IFS_OFFSET + reg * 0x10);
159
160 /* mask and clear flag */
161 writel(mask, evic_base + iecclr);
162 writel(mask, evic_base + ifsclr);
163
164 /* default priority is required */
165 pic32_set_irq_priority(hw, PIC32_INT_PRI(2, 0));
166
167 return ret;
168}
169
170static int pic32_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr,
171 const u32 *intspec, unsigned int intsize,
172 irq_hw_number_t *out_hwirq, unsigned int *out_type)
173{
174 struct evic_chip_data *priv = d->host_data;
175
176 if (WARN_ON(intsize < 2))
177 return -EINVAL;
178
179 if (WARN_ON(intspec[0] >= NR_IRQS))
180 return -EINVAL;
181
182 *out_hwirq = intspec[0];
183 *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
184
185 priv->irq_types[intspec[0]] = intspec[1] & IRQ_TYPE_SENSE_MASK;
186
187 return 0;
188}
189
190static const struct irq_domain_ops pic32_irq_domain_ops = {
191 .map = pic32_irq_domain_map,
192 .xlate = pic32_irq_domain_xlate,
193};
194
195static void __init pic32_ext_irq_of_init(struct irq_domain *domain)
196{
197 struct device_node *node = irq_domain_get_of_node(domain);
198 struct evic_chip_data *priv = domain->host_data;
199 u32 hwirq;
200 int i = 0;
201 const char *pname = "microchip,external-irqs";
202
203 of_property_for_each_u32(node, pname, hwirq) {
204 if (i >= ARRAY_SIZE(priv->ext_irqs)) {
205 pr_warn("More than %zu external irq, skip rest\n",
206 ARRAY_SIZE(priv->ext_irqs));
207 break;
208 }
209
210 priv->ext_irqs[i] = hwirq;
211 i++;
212 }
213}
214
215static int __init pic32_of_init(struct device_node *node,
216 struct device_node *parent)
217{
218 struct irq_chip_generic *gc;
219 struct evic_chip_data *priv;
220 unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
221 int nchips, ret;
222 int i;
223
224 nchips = DIV_ROUND_UP(NR_IRQS, 32);
225
226 evic_base = of_iomap(node, 0);
227 if (!evic_base)
228 return -ENOMEM;
229
230 priv = kzalloc_objs(*priv, nchips);
231 if (!priv) {
232 ret = -ENOMEM;
233 goto err_iounmap;
234 }
235
236 evic_irq_domain = irq_domain_create_linear(of_fwnode_handle(node), nchips * 32,
237 &pic32_irq_domain_ops,
238 priv);
239 if (!evic_irq_domain) {
240 ret = -ENOMEM;
241 goto err_free_priv;
242 }
243
244 /*
245 * The PIC32 EVIC has a linear list of irqs and the type of each
246 * irq is determined by the hardware peripheral the EVIC is arbitrating.
247 * These irq types are defined in the datasheet as "persistent" and
248 * "non-persistent" which are mapped here to level and edge
249 * respectively. To manage the different flow handler requirements of
250 * each irq type, different chip_types are used.
251 */
252 ret = irq_alloc_domain_generic_chips(evic_irq_domain, 32, 2,
253 "evic-level", handle_level_irq,
254 clr, 0, 0);
255 if (ret)
256 goto err_domain_remove;
257
258 board_bind_eic_interrupt = &pic32_bind_evic_interrupt;
259
260 for (i = 0; i < nchips; i++) {
261 u32 ifsclr = PIC32_CLR(REG_IFS_OFFSET + (i * 0x10));
262 u32 iec = REG_IEC_OFFSET + (i * 0x10);
263
264 gc = irq_get_domain_generic_chip(evic_irq_domain, i * 32);
265
266 gc->reg_base = evic_base;
267 gc->unused = 0;
268
269 /*
270 * Level/persistent interrupts have a special requirement that
271 * the condition generating the interrupt be cleared before the
272 * interrupt flag (ifs) can be cleared. chip.irq_eoi is used to
273 * complete the interrupt with an ack.
274 */
275 gc->chip_types[0].type = IRQ_TYPE_LEVEL_MASK;
276 gc->chip_types[0].handler = handle_fasteoi_irq;
277 gc->chip_types[0].regs.ack = ifsclr;
278 gc->chip_types[0].regs.mask = iec;
279 gc->chip_types[0].chip.name = "evic-level";
280 gc->chip_types[0].chip.irq_eoi = irq_gc_ack_set_bit;
281 gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
282 gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
283 gc->chip_types[0].chip.flags = IRQCHIP_SKIP_SET_WAKE;
284
285 /* Edge interrupts */
286 gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH;
287 gc->chip_types[1].handler = handle_edge_irq;
288 gc->chip_types[1].regs.ack = ifsclr;
289 gc->chip_types[1].regs.mask = iec;
290 gc->chip_types[1].chip.name = "evic-edge";
291 gc->chip_types[1].chip.irq_ack = irq_gc_ack_set_bit;
292 gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit;
293 gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit;
294 gc->chip_types[1].chip.irq_set_type = pic32_set_type_edge;
295 gc->chip_types[1].chip.flags = IRQCHIP_SKIP_SET_WAKE;
296
297 gc->private = &priv[i];
298 }
299
300 irq_set_default_domain(evic_irq_domain);
301
302 /*
303 * External interrupts have software configurable edge polarity. These
304 * interrupts are defined in DT allowing polarity to be configured only
305 * for these interrupts when requested.
306 */
307 pic32_ext_irq_of_init(evic_irq_domain);
308
309 return 0;
310
311err_domain_remove:
312 irq_domain_remove(evic_irq_domain);
313
314err_free_priv:
315 kfree(priv);
316
317err_iounmap:
318 iounmap(evic_base);
319
320 return ret;
321}
322
323IRQCHIP_DECLARE(pic32_evic, "microchip,pic32mzda-evic", pic32_of_init);