Loading...
Note: File does not exist in v4.17.
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Renesas RZ/V2H(P) ICU Driver
4 *
5 * Based on irq-renesas-rzg2l.c
6 *
7 * Copyright (C) 2024 Renesas Electronics Corporation.
8 *
9 * Author: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
10 */
11
12#include <linux/bitfield.h>
13#include <linux/cleanup.h>
14#include <linux/clk.h>
15#include <linux/err.h>
16#include <linux/io.h>
17#include <linux/irqchip.h>
18#include <linux/irqdomain.h>
19#include <linux/of_address.h>
20#include <linux/of_platform.h>
21#include <linux/pm_runtime.h>
22#include <linux/reset.h>
23#include <linux/spinlock.h>
24#include <linux/syscore_ops.h>
25
26/* DT "interrupts" indexes */
27#define ICU_IRQ_START 1
28#define ICU_IRQ_COUNT 16
29#define ICU_TINT_START (ICU_IRQ_START + ICU_IRQ_COUNT)
30#define ICU_TINT_COUNT 32
31#define ICU_NUM_IRQ (ICU_TINT_START + ICU_TINT_COUNT)
32
33/* Registers */
34#define ICU_NSCNT 0x00
35#define ICU_NSCLR 0x04
36#define ICU_NITSR 0x08
37#define ICU_ISCTR 0x10
38#define ICU_ISCLR 0x14
39#define ICU_IITSR 0x18
40#define ICU_TSCTR 0x20
41#define ICU_TSCLR 0x24
42#define ICU_TITSR(k) (0x28 + (k) * 4)
43#define ICU_TSSR(k) (0x30 + (k) * 4)
44
45/* NMI */
46#define ICU_NMI_EDGE_FALLING 0
47#define ICU_NMI_EDGE_RISING 1
48
49#define ICU_NSCLR_NCLR BIT(0)
50
51/* IRQ */
52#define ICU_IRQ_LEVEL_LOW 0
53#define ICU_IRQ_EDGE_FALLING 1
54#define ICU_IRQ_EDGE_RISING 2
55#define ICU_IRQ_EDGE_BOTH 3
56
57#define ICU_IITSR_IITSEL_PREP(iitsel, n) ((iitsel) << ((n) * 2))
58#define ICU_IITSR_IITSEL_GET(iitsr, n) (((iitsr) >> ((n) * 2)) & 0x03)
59#define ICU_IITSR_IITSEL_MASK(n) ICU_IITSR_IITSEL_PREP(0x03, n)
60
61/* TINT */
62#define ICU_TINT_EDGE_RISING 0
63#define ICU_TINT_EDGE_FALLING 1
64#define ICU_TINT_LEVEL_HIGH 2
65#define ICU_TINT_LEVEL_LOW 3
66
67#define ICU_TSSR_K(tint_nr) ((tint_nr) / 4)
68#define ICU_TSSR_TSSEL_N(tint_nr) ((tint_nr) % 4)
69#define ICU_TSSR_TSSEL_PREP(tssel, n) ((tssel) << ((n) * 8))
70#define ICU_TSSR_TSSEL_MASK(n) ICU_TSSR_TSSEL_PREP(0x7F, n)
71#define ICU_TSSR_TIEN(n) (BIT(7) << ((n) * 8))
72
73#define ICU_TITSR_K(tint_nr) ((tint_nr) / 16)
74#define ICU_TITSR_TITSEL_N(tint_nr) ((tint_nr) % 16)
75#define ICU_TITSR_TITSEL_PREP(titsel, n) ICU_IITSR_IITSEL_PREP(titsel, n)
76#define ICU_TITSR_TITSEL_MASK(n) ICU_IITSR_IITSEL_MASK(n)
77#define ICU_TITSR_TITSEL_GET(titsr, n) ICU_IITSR_IITSEL_GET(titsr, n)
78
79#define ICU_TINT_EXTRACT_HWIRQ(x) FIELD_GET(GENMASK(15, 0), (x))
80#define ICU_TINT_EXTRACT_GPIOINT(x) FIELD_GET(GENMASK(31, 16), (x))
81#define ICU_PB5_TINT 0x55
82
83/**
84 * struct rzv2h_icu_priv - Interrupt Control Unit controller private data structure.
85 * @base: Controller's base address
86 * @irqchip: Pointer to struct irq_chip
87 * @fwspec: IRQ firmware specific data
88 * @lock: Lock to serialize access to hardware registers
89 */
90struct rzv2h_icu_priv {
91 void __iomem *base;
92 const struct irq_chip *irqchip;
93 struct irq_fwspec fwspec[ICU_NUM_IRQ];
94 raw_spinlock_t lock;
95};
96
97static inline struct rzv2h_icu_priv *irq_data_to_priv(struct irq_data *data)
98{
99 return data->domain->host_data;
100}
101
102static void rzv2h_icu_eoi(struct irq_data *d)
103{
104 struct rzv2h_icu_priv *priv = irq_data_to_priv(d);
105 unsigned int hw_irq = irqd_to_hwirq(d);
106 unsigned int tintirq_nr;
107 u32 bit;
108
109 scoped_guard(raw_spinlock, &priv->lock) {
110 if (hw_irq >= ICU_TINT_START) {
111 tintirq_nr = hw_irq - ICU_TINT_START;
112 bit = BIT(tintirq_nr);
113 if (!irqd_is_level_type(d))
114 writel_relaxed(bit, priv->base + ICU_TSCLR);
115 } else if (hw_irq >= ICU_IRQ_START) {
116 tintirq_nr = hw_irq - ICU_IRQ_START;
117 bit = BIT(tintirq_nr);
118 if (!irqd_is_level_type(d))
119 writel_relaxed(bit, priv->base + ICU_ISCLR);
120 } else {
121 writel_relaxed(ICU_NSCLR_NCLR, priv->base + ICU_NSCLR);
122 }
123 }
124
125 irq_chip_eoi_parent(d);
126}
127
128static void rzv2h_tint_irq_endisable(struct irq_data *d, bool enable)
129{
130 struct rzv2h_icu_priv *priv = irq_data_to_priv(d);
131 unsigned int hw_irq = irqd_to_hwirq(d);
132 u32 tint_nr, tssel_n, k, tssr;
133
134 if (hw_irq < ICU_TINT_START)
135 return;
136
137 tint_nr = hw_irq - ICU_TINT_START;
138 k = ICU_TSSR_K(tint_nr);
139 tssel_n = ICU_TSSR_TSSEL_N(tint_nr);
140
141 guard(raw_spinlock)(&priv->lock);
142 tssr = readl_relaxed(priv->base + ICU_TSSR(k));
143 if (enable)
144 tssr |= ICU_TSSR_TIEN(tssel_n);
145 else
146 tssr &= ~ICU_TSSR_TIEN(tssel_n);
147 writel_relaxed(tssr, priv->base + ICU_TSSR(k));
148}
149
150static void rzv2h_icu_irq_disable(struct irq_data *d)
151{
152 irq_chip_disable_parent(d);
153 rzv2h_tint_irq_endisable(d, false);
154}
155
156static void rzv2h_icu_irq_enable(struct irq_data *d)
157{
158 rzv2h_tint_irq_endisable(d, true);
159 irq_chip_enable_parent(d);
160}
161
162static int rzv2h_nmi_set_type(struct irq_data *d, unsigned int type)
163{
164 struct rzv2h_icu_priv *priv = irq_data_to_priv(d);
165 u32 sense;
166
167 switch (type & IRQ_TYPE_SENSE_MASK) {
168 case IRQ_TYPE_EDGE_FALLING:
169 sense = ICU_NMI_EDGE_FALLING;
170 break;
171
172 case IRQ_TYPE_EDGE_RISING:
173 sense = ICU_NMI_EDGE_RISING;
174 break;
175
176 default:
177 return -EINVAL;
178 }
179
180 writel_relaxed(sense, priv->base + ICU_NITSR);
181
182 return 0;
183}
184
185static void rzv2h_clear_irq_int(struct rzv2h_icu_priv *priv, unsigned int hwirq)
186{
187 unsigned int irq_nr = hwirq - ICU_IRQ_START;
188 u32 isctr, iitsr, iitsel;
189 u32 bit = BIT(irq_nr);
190
191 isctr = readl_relaxed(priv->base + ICU_ISCTR);
192 iitsr = readl_relaxed(priv->base + ICU_IITSR);
193 iitsel = ICU_IITSR_IITSEL_GET(iitsr, irq_nr);
194
195 /*
196 * When level sensing is used, the interrupt flag gets automatically cleared when the
197 * interrupt signal is de-asserted by the source of the interrupt request, therefore clear
198 * the interrupt only for edge triggered interrupts.
199 */
200 if ((isctr & bit) && (iitsel != ICU_IRQ_LEVEL_LOW))
201 writel_relaxed(bit, priv->base + ICU_ISCLR);
202}
203
204static int rzv2h_irq_set_type(struct irq_data *d, unsigned int type)
205{
206 struct rzv2h_icu_priv *priv = irq_data_to_priv(d);
207 unsigned int hwirq = irqd_to_hwirq(d);
208 u32 irq_nr = hwirq - ICU_IRQ_START;
209 u32 iitsr, sense;
210
211 switch (type & IRQ_TYPE_SENSE_MASK) {
212 case IRQ_TYPE_LEVEL_LOW:
213 sense = ICU_IRQ_LEVEL_LOW;
214 break;
215
216 case IRQ_TYPE_EDGE_FALLING:
217 sense = ICU_IRQ_EDGE_FALLING;
218 break;
219
220 case IRQ_TYPE_EDGE_RISING:
221 sense = ICU_IRQ_EDGE_RISING;
222 break;
223
224 case IRQ_TYPE_EDGE_BOTH:
225 sense = ICU_IRQ_EDGE_BOTH;
226 break;
227
228 default:
229 return -EINVAL;
230 }
231
232 guard(raw_spinlock)(&priv->lock);
233 iitsr = readl_relaxed(priv->base + ICU_IITSR);
234 iitsr &= ~ICU_IITSR_IITSEL_MASK(irq_nr);
235 iitsr |= ICU_IITSR_IITSEL_PREP(sense, irq_nr);
236 rzv2h_clear_irq_int(priv, hwirq);
237 writel_relaxed(iitsr, priv->base + ICU_IITSR);
238
239 return 0;
240}
241
242static void rzv2h_clear_tint_int(struct rzv2h_icu_priv *priv, unsigned int hwirq)
243{
244 unsigned int tint_nr = hwirq - ICU_TINT_START;
245 int titsel_n = ICU_TITSR_TITSEL_N(tint_nr);
246 u32 tsctr, titsr, titsel;
247 u32 bit = BIT(tint_nr);
248 int k = tint_nr / 16;
249
250 tsctr = readl_relaxed(priv->base + ICU_TSCTR);
251 titsr = readl_relaxed(priv->base + ICU_TITSR(k));
252 titsel = ICU_TITSR_TITSEL_GET(titsr, titsel_n);
253
254 /*
255 * Writing 1 to the corresponding flag from register ICU_TSCTR only has effect if
256 * TSTATn = 1b and if it's a rising edge or a falling edge interrupt.
257 */
258 if ((tsctr & bit) && ((titsel == ICU_TINT_EDGE_RISING) ||
259 (titsel == ICU_TINT_EDGE_FALLING)))
260 writel_relaxed(bit, priv->base + ICU_TSCLR);
261}
262
263static int rzv2h_tint_set_type(struct irq_data *d, unsigned int type)
264{
265 u32 titsr, titsr_k, titsel_n, tien;
266 struct rzv2h_icu_priv *priv;
267 u32 tssr, tssr_k, tssel_n;
268 unsigned int hwirq;
269 u32 tint, sense;
270 int tint_nr;
271
272 switch (type & IRQ_TYPE_SENSE_MASK) {
273 case IRQ_TYPE_LEVEL_LOW:
274 sense = ICU_TINT_LEVEL_LOW;
275 break;
276
277 case IRQ_TYPE_LEVEL_HIGH:
278 sense = ICU_TINT_LEVEL_HIGH;
279 break;
280
281 case IRQ_TYPE_EDGE_RISING:
282 sense = ICU_TINT_EDGE_RISING;
283 break;
284
285 case IRQ_TYPE_EDGE_FALLING:
286 sense = ICU_TINT_EDGE_FALLING;
287 break;
288
289 default:
290 return -EINVAL;
291 }
292
293 tint = (u32)(uintptr_t)irq_data_get_irq_chip_data(d);
294 if (tint > ICU_PB5_TINT)
295 return -EINVAL;
296
297 priv = irq_data_to_priv(d);
298 hwirq = irqd_to_hwirq(d);
299
300 tint_nr = hwirq - ICU_TINT_START;
301
302 tssr_k = ICU_TSSR_K(tint_nr);
303 tssel_n = ICU_TSSR_TSSEL_N(tint_nr);
304
305 titsr_k = ICU_TITSR_K(tint_nr);
306 titsel_n = ICU_TITSR_TITSEL_N(tint_nr);
307 tien = ICU_TSSR_TIEN(titsel_n);
308
309 guard(raw_spinlock)(&priv->lock);
310
311 tssr = readl_relaxed(priv->base + ICU_TSSR(tssr_k));
312 tssr &= ~(ICU_TSSR_TSSEL_MASK(tssel_n) | tien);
313 tssr |= ICU_TSSR_TSSEL_PREP(tint, tssel_n);
314
315 writel_relaxed(tssr, priv->base + ICU_TSSR(tssr_k));
316
317 titsr = readl_relaxed(priv->base + ICU_TITSR(titsr_k));
318 titsr &= ~ICU_TITSR_TITSEL_MASK(titsel_n);
319 titsr |= ICU_TITSR_TITSEL_PREP(sense, titsel_n);
320
321 writel_relaxed(titsr, priv->base + ICU_TITSR(titsr_k));
322
323 rzv2h_clear_tint_int(priv, hwirq);
324
325 writel_relaxed(tssr | tien, priv->base + ICU_TSSR(tssr_k));
326
327 return 0;
328}
329
330static int rzv2h_icu_set_type(struct irq_data *d, unsigned int type)
331{
332 unsigned int hw_irq = irqd_to_hwirq(d);
333 int ret;
334
335 if (hw_irq >= ICU_TINT_START)
336 ret = rzv2h_tint_set_type(d, type);
337 else if (hw_irq >= ICU_IRQ_START)
338 ret = rzv2h_irq_set_type(d, type);
339 else
340 ret = rzv2h_nmi_set_type(d, type);
341
342 if (ret)
343 return ret;
344
345 return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH);
346}
347
348static const struct irq_chip rzv2h_icu_chip = {
349 .name = "rzv2h-icu",
350 .irq_eoi = rzv2h_icu_eoi,
351 .irq_mask = irq_chip_mask_parent,
352 .irq_unmask = irq_chip_unmask_parent,
353 .irq_disable = rzv2h_icu_irq_disable,
354 .irq_enable = rzv2h_icu_irq_enable,
355 .irq_get_irqchip_state = irq_chip_get_parent_state,
356 .irq_set_irqchip_state = irq_chip_set_parent_state,
357 .irq_retrigger = irq_chip_retrigger_hierarchy,
358 .irq_set_type = rzv2h_icu_set_type,
359 .irq_set_affinity = irq_chip_set_affinity_parent,
360 .flags = IRQCHIP_SET_TYPE_MASKED,
361};
362
363static int rzv2h_icu_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs,
364 void *arg)
365{
366 struct rzv2h_icu_priv *priv = domain->host_data;
367 unsigned long tint = 0;
368 irq_hw_number_t hwirq;
369 unsigned int type;
370 int ret;
371
372 ret = irq_domain_translate_twocell(domain, arg, &hwirq, &type);
373 if (ret)
374 return ret;
375
376 /*
377 * For TINT interrupts the hwirq and TINT are encoded in
378 * fwspec->param[0].
379 * hwirq is embedded in bits 0-15.
380 * TINT is embedded in bits 16-31.
381 */
382 if (hwirq >= ICU_TINT_START) {
383 tint = ICU_TINT_EXTRACT_GPIOINT(hwirq);
384 hwirq = ICU_TINT_EXTRACT_HWIRQ(hwirq);
385
386 if (hwirq < ICU_TINT_START)
387 return -EINVAL;
388 }
389
390 if (hwirq > (ICU_NUM_IRQ - 1))
391 return -EINVAL;
392
393 ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, priv->irqchip,
394 (void *)(uintptr_t)tint);
395 if (ret)
396 return ret;
397
398 return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &priv->fwspec[hwirq]);
399}
400
401static const struct irq_domain_ops rzv2h_icu_domain_ops = {
402 .alloc = rzv2h_icu_alloc,
403 .free = irq_domain_free_irqs_common,
404 .translate = irq_domain_translate_twocell,
405};
406
407static int rzv2h_icu_parse_interrupts(struct rzv2h_icu_priv *priv, struct device_node *np)
408{
409 struct of_phandle_args map;
410 unsigned int i;
411 int ret;
412
413 for (i = 0; i < ICU_NUM_IRQ; i++) {
414 ret = of_irq_parse_one(np, i, &map);
415 if (ret)
416 return ret;
417
418 of_phandle_args_to_fwspec(np, map.args, map.args_count, &priv->fwspec[i]);
419 }
420
421 return 0;
422}
423
424static int rzv2h_icu_init(struct device_node *node, struct device_node *parent)
425{
426 struct irq_domain *irq_domain, *parent_domain;
427 struct rzv2h_icu_priv *rzv2h_icu_data;
428 struct platform_device *pdev;
429 struct reset_control *resetn;
430 int ret;
431
432 pdev = of_find_device_by_node(node);
433 if (!pdev)
434 return -ENODEV;
435
436 parent_domain = irq_find_host(parent);
437 if (!parent_domain) {
438 dev_err(&pdev->dev, "cannot find parent domain\n");
439 ret = -ENODEV;
440 goto put_dev;
441 }
442
443 rzv2h_icu_data = devm_kzalloc(&pdev->dev, sizeof(*rzv2h_icu_data), GFP_KERNEL);
444 if (!rzv2h_icu_data) {
445 ret = -ENOMEM;
446 goto put_dev;
447 }
448
449 rzv2h_icu_data->irqchip = &rzv2h_icu_chip;
450
451 rzv2h_icu_data->base = devm_of_iomap(&pdev->dev, pdev->dev.of_node, 0, NULL);
452 if (IS_ERR(rzv2h_icu_data->base)) {
453 ret = PTR_ERR(rzv2h_icu_data->base);
454 goto put_dev;
455 }
456
457 ret = rzv2h_icu_parse_interrupts(rzv2h_icu_data, node);
458 if (ret) {
459 dev_err(&pdev->dev, "cannot parse interrupts: %d\n", ret);
460 goto put_dev;
461 }
462
463 resetn = devm_reset_control_get_exclusive(&pdev->dev, NULL);
464 if (IS_ERR(resetn)) {
465 ret = PTR_ERR(resetn);
466 goto put_dev;
467 }
468
469 ret = reset_control_deassert(resetn);
470 if (ret) {
471 dev_err(&pdev->dev, "failed to deassert resetn pin, %d\n", ret);
472 goto put_dev;
473 }
474
475 pm_runtime_enable(&pdev->dev);
476 ret = pm_runtime_resume_and_get(&pdev->dev);
477 if (ret < 0) {
478 dev_err(&pdev->dev, "pm_runtime_resume_and_get failed: %d\n", ret);
479 goto pm_disable;
480 }
481
482 raw_spin_lock_init(&rzv2h_icu_data->lock);
483
484 irq_domain = irq_domain_add_hierarchy(parent_domain, 0, ICU_NUM_IRQ, node,
485 &rzv2h_icu_domain_ops, rzv2h_icu_data);
486 if (!irq_domain) {
487 dev_err(&pdev->dev, "failed to add irq domain\n");
488 ret = -ENOMEM;
489 goto pm_put;
490 }
491
492 /*
493 * coccicheck complains about a missing put_device call before returning, but it's a false
494 * positive. We still need &pdev->dev after successfully returning from this function.
495 */
496 return 0;
497
498pm_put:
499 pm_runtime_put(&pdev->dev);
500pm_disable:
501 pm_runtime_disable(&pdev->dev);
502 reset_control_assert(resetn);
503put_dev:
504 put_device(&pdev->dev);
505
506 return ret;
507}
508
509IRQCHIP_PLATFORM_DRIVER_BEGIN(rzv2h_icu)
510IRQCHIP_MATCH("renesas,r9a09g057-icu", rzv2h_icu_init)
511IRQCHIP_PLATFORM_DRIVER_END(rzv2h_icu)
512MODULE_AUTHOR("Fabrizio Castro <fabrizio.castro.jz@renesas.com>");
513MODULE_DESCRIPTION("Renesas RZ/V2H(P) ICU Driver");