1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * PRU-ICSS INTC IRQChip driver for various TI SoCs
4 *
5 * Copyright (C) 2016-2019 Texas Instruments Incorporated - http://www.ti.com/
6 * Andrew F. Davis <afd@ti.com>
7 * Suman Anna <s-anna@ti.com>
8 */
10 #include <linux/irq.h>
11 #include <linux/irqchip/chained_irq.h>
12 #include <linux/irqdomain.h>
13 #include <linux/module.h>
14 #include <linux/of_device.h>
15 #include <linux/platform_device.h>
16 #include <linux/pruss_driver.h>
18 /*
19 * Number of host interrupts reaching the main MPU sub-system. Note that this
20 * is not the same as the total number of host interrupts supported by the PRUSS
21 * INTC instance
22 */
23 #define MAX_HOST_NUM_IRQS 8
25 /* PRU_ICSS_INTC registers */
26 #define PRU_INTC_REVID 0x0000
27 #define PRU_INTC_CR 0x0004
28 #define PRU_INTC_GER 0x0010
29 #define PRU_INTC_GNLR 0x001C
30 #define PRU_INTC_SISR 0x0020
31 #define PRU_INTC_SICR 0x0024
32 #define PRU_INTC_EISR 0x0028
33 #define PRU_INTC_EICR 0x002C
34 #define PRU_INTC_HIEISR 0x0034
35 #define PRU_INTC_HIDISR 0x0038
36 #define PRU_INTC_GPIR 0x0080
37 #define PRU_INTC_SRSR0 0x0200
38 #define PRU_INTC_SRSR1 0x0204
39 #define PRU_INTC_SECR0 0x0280
40 #define PRU_INTC_SECR1 0x0284
41 #define PRU_INTC_ESR0 0x0300
42 #define PRU_INTC_ESR1 0x0304
43 #define PRU_INTC_ECR0 0x0380
44 #define PRU_INTC_ECR1 0x0384
45 #define PRU_INTC_CMR(x) (0x0400 + (x) * 4)
46 #define PRU_INTC_HMR(x) (0x0800 + (x) * 4)
47 #define PRU_INTC_HIPIR(x) (0x0900 + (x) * 4)
48 #define PRU_INTC_SIPR0 0x0D00
49 #define PRU_INTC_SIPR1 0x0D04
50 #define PRU_INTC_SITR0 0x0D80
51 #define PRU_INTC_SITR1 0x0D84
52 #define PRU_INTC_HINLR(x) (0x1100 + (x) * 4)
53 #define PRU_INTC_HIER 0x1500
55 /* HIPIR register bit-fields */
56 #define INTC_HIPIR_NONE_HINT 0x80000000
58 static const char * const irq_names[] = {
59 "host2", "host3", "host4", "host5", "host6", "host7", "host8", "host9",
60 };
62 /**
63 * struct pruss_intc_match_data - match data to handle SoC variations
64 * @no_host7_intr: flag denoting the absence of host7 interrupt into MPU
65 */
66 struct pruss_intc_match_data {
67 bool no_host7_intr;
68 };
70 /**
71 * struct pruss_intc - PRUSS interrupt controller structure
72 * @pruss: back-reference to parent PRUSS structure
73 * @irqs: kernel irq numbers corresponding to PRUSS host interrupts
74 * @base: base virtual address of INTC register space
75 * @irqchip: irq chip for this interrupt controller
76 * @domain: irq domain for this interrupt controller
77 * @config_map: stored INTC configuration mapping data
78 * @lock: mutex to serialize access to INTC
79 * @host_mask: indicate which HOST IRQs are enabled
80 */
81 struct pruss_intc {
82 struct pruss *pruss;
83 unsigned int irqs[MAX_HOST_NUM_IRQS];
84 void __iomem *base;
85 struct irq_chip *irqchip;
86 struct irq_domain *domain;
87 struct pruss_intc_config config_map;
88 struct mutex lock; /* PRUSS INTC lock */
89 u32 host_mask;
90 };
92 static inline u32 pruss_intc_read_reg(struct pruss_intc *intc, unsigned int reg)
93 {
94 return readl_relaxed(intc->base + reg);
95 }
97 static inline void pruss_intc_write_reg(struct pruss_intc *intc,
98 unsigned int reg, u32 val)
99 {
100 writel_relaxed(val, intc->base + reg);
101 }
103 static int pruss_intc_check_write(struct pruss_intc *intc, unsigned int reg,
104 unsigned int sysevent)
105 {
106 if (!intc)
107 return -EINVAL;
109 if (sysevent >= MAX_PRU_SYS_EVENTS)
110 return -EINVAL;
112 pruss_intc_write_reg(intc, reg, sysevent);
114 return 0;
115 }
117 static struct pruss_intc *to_pruss_intc(struct pruss *pruss)
118 {
119 struct device_node *parent = pruss->dev->of_node;
120 struct device_node *np;
121 struct platform_device *pdev;
122 struct pruss_intc *intc = NULL;
124 np = of_get_child_by_name(parent, "interrupt-controller");
125 if (!np) {
126 dev_err(pruss->dev, "pruss does not have an interrupt-controller node\n");
127 return NULL;
128 }
130 pdev = of_find_device_by_node(np);
131 if (!pdev) {
132 dev_err(pruss->dev, "no associated platform device\n");
133 goto out;
134 }
136 intc = platform_get_drvdata(pdev);
137 out:
138 of_node_put(np);
139 return intc;
140 }
142 /**
143 * pruss_intc_configure() - configure the PRUSS INTC
144 * @pruss: the pruss instance
145 * @intc_config: PRU core-specific INTC configuration
146 *
147 * Configures the PRUSS INTC with the provided configuration from
148 * a PRU core. Any existing event to channel mappings or channel to
149 * host interrupt mappings are checked to make sure there are no
150 * conflicting configuration between both the PRU cores. The function
151 * is intended to be used only by the PRU remoteproc driver.
152 *
153 * Returns 0 on success, or a suitable error code otherwise
154 */
155 int pruss_intc_configure(struct pruss *pruss,
156 struct pruss_intc_config *intc_config)
157 {
158 struct device *dev = pruss->dev;
159 struct pruss_intc *intc = to_pruss_intc(pruss);
160 int i, idx, ret;
161 s8 ch, host;
162 u64 sysevt_mask = 0;
163 u32 ch_mask = 0;
164 u32 host_mask = 0;
165 u32 val;
167 if (!intc)
168 return -EINVAL;
170 mutex_lock(&intc->lock);
172 /*
173 * configure channel map registers - each register holds map info
174 * for 4 events, with each event occupying the lower nibble in
175 * a register byte address in little-endian fashion
176 */
177 for (i = 0; i < ARRAY_SIZE(intc_config->sysev_to_ch); i++) {
178 ch = intc_config->sysev_to_ch[i];
179 if (ch < 0)
180 continue;
182 /* check if sysevent already assigned */
183 if (intc->config_map.sysev_to_ch[i] != -1) {
184 dev_err(dev, "event %d (req. channel %d) already assigned to channel %d\n",
185 i, ch, intc->config_map.sysev_to_ch[i]);
186 ret = -EEXIST;
187 goto unlock;
188 }
190 intc->config_map.sysev_to_ch[i] = ch;
192 idx = i / 4;
193 val = pruss_intc_read_reg(intc, PRU_INTC_CMR(idx));
194 val |= ch << ((i & 3) * 8);
195 pruss_intc_write_reg(intc, PRU_INTC_CMR(idx), val);
196 sysevt_mask |= BIT_ULL(i);
197 ch_mask |= BIT(ch);
199 dev_dbg(dev, "SYSEV%d -> CH%d (CMR%d 0x%08x)\n", i, ch, idx,
200 pruss_intc_read_reg(intc, PRU_INTC_CMR(idx)));
201 }
203 /*
204 * set host map registers - each register holds map info for
205 * 4 channels, with each channel occupying the lower nibble in
206 * a register byte address in little-endian fashion
207 */
208 for (i = 0; i < ARRAY_SIZE(intc_config->ch_to_host); i++) {
209 host = intc_config->ch_to_host[i];
210 if (host < 0)
211 continue;
213 /* check if channel already assigned */
214 if (intc->config_map.ch_to_host[i] != -1) {
215 dev_err(dev, "channel %d (req. intr_no %d) already assigned to intr_no %d\n",
216 i, host, intc->config_map.ch_to_host[i]);
217 ret = -EEXIST;
218 goto unlock;
219 }
221 /* check if host intr is already in use by other PRU */
222 if (intc->host_mask & (1U << host)) {
223 dev_err(dev, "%s: host intr %d already in use\n",
224 __func__, host);
225 ret = -EEXIST;
226 goto unlock;
227 }
229 intc->config_map.ch_to_host[i] = host;
231 idx = i / 4;
233 val = pruss_intc_read_reg(intc, PRU_INTC_HMR(idx));
234 val |= host << ((i & 3) * 8);
235 pruss_intc_write_reg(intc, PRU_INTC_HMR(idx), val);
237 ch_mask |= BIT(i);
238 host_mask |= BIT(host);
240 dev_dbg(dev, "CH%d -> HOST%d (HMR%d 0x%08x)\n", i, host, idx,
241 pruss_intc_read_reg(intc, PRU_INTC_HMR(idx)));
242 }
244 dev_info(dev, "configured system_events = 0x%016llx intr_channels = 0x%08x host_intr = 0x%08x\n",
245 sysevt_mask, ch_mask, host_mask);
247 /* enable system events, writing 0 has no-effect */
248 pruss_intc_write_reg(intc, PRU_INTC_ESR0, lower_32_bits(sysevt_mask));
249 pruss_intc_write_reg(intc, PRU_INTC_SECR0, lower_32_bits(sysevt_mask));
250 pruss_intc_write_reg(intc, PRU_INTC_ESR1, upper_32_bits(sysevt_mask));
251 pruss_intc_write_reg(intc, PRU_INTC_SECR1, upper_32_bits(sysevt_mask));
253 /* enable host interrupts */
254 for (i = 0; i < MAX_PRU_HOST_INT; i++) {
255 if (host_mask & BIT(i))
256 pruss_intc_write_reg(intc, PRU_INTC_HIEISR, i);
257 }
259 /* global interrupt enable */
260 pruss_intc_write_reg(intc, PRU_INTC_GER, 1);
262 intc->host_mask |= host_mask;
264 mutex_unlock(&intc->lock);
265 return 0;
267 unlock:
268 mutex_unlock(&intc->lock);
269 return ret;
270 }
271 EXPORT_SYMBOL_GPL(pruss_intc_configure);
273 /**
274 * pruss_intc_unconfigure() - unconfigure the PRUSS INTC
275 * @pruss: the pruss instance
276 * @intc_config: PRU core specific INTC configuration
277 *
278 * Undo whatever was done in pruss_intc_configure() for a PRU core.
279 * It should be sufficient to just mark the resources free in the
280 * global map and disable the host interrupts and sysevents.
281 */
282 int pruss_intc_unconfigure(struct pruss *pruss,
283 struct pruss_intc_config *intc_config)
284 {
285 struct device *dev = pruss->dev;
286 struct pruss_intc *intc = to_pruss_intc(pruss);
287 int i;
288 s8 ch, host;
289 u64 sysevt_mask = 0;
290 u32 host_mask = 0;
292 if (!intc)
293 return -EINVAL;
295 mutex_lock(&intc->lock);
297 for (i = 0; i < ARRAY_SIZE(intc_config->sysev_to_ch); i++) {
298 ch = intc_config->sysev_to_ch[i];
299 if (ch < 0)
300 continue;
302 /* mark sysevent free in global map */
303 intc->config_map.sysev_to_ch[i] = -1;
304 sysevt_mask |= BIT_ULL(i);
305 }
307 for (i = 0; i < ARRAY_SIZE(intc_config->ch_to_host); i++) {
308 host = intc_config->ch_to_host[i];
309 if (host < 0)
310 continue;
312 /* mark channel free in global map */
313 intc->config_map.ch_to_host[i] = -1;
314 host_mask |= BIT(host);
315 }
317 dev_info(dev, "unconfigured system_events = 0x%016llx host_intr = 0x%08x\n",
318 sysevt_mask, host_mask);
320 /* disable system events, writing 0 has no-effect */
321 pruss_intc_write_reg(intc, PRU_INTC_ECR0, lower_32_bits(sysevt_mask));
322 pruss_intc_write_reg(intc, PRU_INTC_ECR1, upper_32_bits(sysevt_mask));
323 /* clear any pending status */
324 pruss_intc_write_reg(intc, PRU_INTC_SECR0, lower_32_bits(sysevt_mask));
325 pruss_intc_write_reg(intc, PRU_INTC_SECR1, upper_32_bits(sysevt_mask));
327 /* disable host interrupts */
328 for (i = 0; i < MAX_PRU_HOST_INT; i++) {
329 if (host_mask & BIT(i))
330 pruss_intc_write_reg(intc, PRU_INTC_HIDISR, i);
331 }
333 intc->host_mask &= ~host_mask;
334 mutex_unlock(&intc->lock);
336 return 0;
337 }
338 EXPORT_SYMBOL_GPL(pruss_intc_unconfigure);
340 static void pruss_intc_init(struct pruss_intc *intc)
341 {
342 int i;
344 /* configure polarity to active high for all system interrupts */
345 pruss_intc_write_reg(intc, PRU_INTC_SIPR0, 0xffffffff);
346 pruss_intc_write_reg(intc, PRU_INTC_SIPR1, 0xffffffff);
348 /* configure type to pulse interrupt for all system interrupts */
349 pruss_intc_write_reg(intc, PRU_INTC_SITR0, 0);
350 pruss_intc_write_reg(intc, PRU_INTC_SITR1, 0);
352 /* clear all 16 interrupt channel map registers */
353 for (i = 0; i < 16; i++)
354 pruss_intc_write_reg(intc, PRU_INTC_CMR(i), 0);
356 /* clear all 3 host interrupt map registers */
357 for (i = 0; i < 3; i++)
358 pruss_intc_write_reg(intc, PRU_INTC_HMR(i), 0);
359 }
361 static void pruss_intc_irq_ack(struct irq_data *data)
362 {
363 struct pruss_intc *intc = irq_data_get_irq_chip_data(data);
364 unsigned int hwirq = data->hwirq;
366 pruss_intc_check_write(intc, PRU_INTC_SICR, hwirq);
367 }
369 static void pruss_intc_irq_mask(struct irq_data *data)
370 {
371 struct pruss_intc *intc = irq_data_get_irq_chip_data(data);
372 unsigned int hwirq = data->hwirq;
374 pruss_intc_check_write(intc, PRU_INTC_EICR, hwirq);
375 }
377 static void pruss_intc_irq_unmask(struct irq_data *data)
378 {
379 struct pruss_intc *intc = irq_data_get_irq_chip_data(data);
380 unsigned int hwirq = data->hwirq;
382 pruss_intc_check_write(intc, PRU_INTC_EISR, hwirq);
383 }
385 static int pruss_intc_irq_retrigger(struct irq_data *data)
386 {
387 struct pruss_intc *intc = irq_data_get_irq_chip_data(data);
388 unsigned int hwirq = data->hwirq;
390 return pruss_intc_check_write(intc, PRU_INTC_SISR, hwirq);
391 }
393 static int pruss_intc_irq_reqres(struct irq_data *data)
394 {
395 if (!try_module_get(THIS_MODULE))
396 return -ENODEV;
398 return 0;
399 }
401 static void pruss_intc_irq_relres(struct irq_data *data)
402 {
403 module_put(THIS_MODULE);
404 }
406 /**
407 * pruss_intc_trigger() - trigger a PRU system event
408 * @irq: linux IRQ number associated with a PRU system event
409 *
410 * Trigger an interrupt by signalling a specific PRU system event.
411 * This can be used by PRUSS client users to raise/send an event to
412 * a PRU or any other core that is listening on the host interrupt
413 * mapped to that specific PRU system event. The @irq variable is the
414 * Linux IRQ number associated with a specific PRU system event that
415 * a client user/application uses. The interrupt mappings for this is
416 * provided by the PRUSS INTC irqchip instance.
417 *
418 * Returns 0 on success, or an error value upon failure.
419 */
420 int pruss_intc_trigger(unsigned int irq)
421 {
422 struct irq_desc *desc;
424 if (irq <= 0)
425 return -EINVAL;
427 desc = irq_to_desc(irq);
428 if (!desc)
429 return -EINVAL;
431 pruss_intc_irq_retrigger(&desc->irq_data);
433 return 0;
434 }
435 EXPORT_SYMBOL_GPL(pruss_intc_trigger);
437 static int pruss_intc_irq_domain_map(struct irq_domain *d, unsigned int virq,
438 irq_hw_number_t hw)
439 {
440 struct pruss_intc *intc = d->host_data;
442 irq_set_chip_data(virq, intc);
443 irq_set_chip_and_handler(virq, intc->irqchip, handle_level_irq);
445 return 0;
446 }
448 static void pruss_intc_irq_domain_unmap(struct irq_domain *d, unsigned int virq)
449 {
450 irq_set_chip_and_handler(virq, NULL, NULL);
451 irq_set_chip_data(virq, NULL);
452 }
454 static const struct irq_domain_ops pruss_intc_irq_domain_ops = {
455 .xlate = irq_domain_xlate_onecell,
456 .map = pruss_intc_irq_domain_map,
457 .unmap = pruss_intc_irq_domain_unmap,
458 };
460 static void pruss_intc_irq_handler(struct irq_desc *desc)
461 {
462 unsigned int irq = irq_desc_get_irq(desc);
463 struct irq_chip *chip = irq_desc_get_chip(desc);
464 struct pruss_intc *intc = irq_get_handler_data(irq);
465 u32 hipir;
466 unsigned int virq;
467 int i, hwirq;
469 chained_irq_enter(chip, desc);
471 /* find our host irq number */
472 for (i = 0; i < MAX_HOST_NUM_IRQS; i++)
473 if (intc->irqs[i] == irq)
474 break;
475 if (i == MAX_HOST_NUM_IRQS)
476 goto err;
478 i += MIN_PRU_HOST_INT;
480 /* get highest priority pending PRUSS system event */
481 hipir = pruss_intc_read_reg(intc, PRU_INTC_HIPIR(i));
482 while (!(hipir & BIT(31))) {
483 hwirq = hipir & GENMASK(9, 0);
484 virq = irq_linear_revmap(intc->domain, hwirq);
486 /*
487 * XXX: manually ACK any system events that do not have a
488 * handler mapped yet
489 */
490 if (unlikely(!virq))
491 pruss_intc_check_write(intc, PRU_INTC_SICR, hwirq);
492 else
493 generic_handle_irq(virq);
495 /* get next system event */
496 hipir = pruss_intc_read_reg(intc, PRU_INTC_HIPIR(i));
497 }
498 err:
499 chained_irq_exit(chip, desc);
500 }
502 static int pruss_intc_probe(struct platform_device *pdev)
503 {
504 struct device *dev = &pdev->dev;
505 struct platform_device *ppdev = to_platform_device(dev->parent);
506 struct pruss_intc *intc;
507 struct resource *res;
508 struct irq_chip *irqchip;
509 int i, irq;
510 const struct pruss_intc_match_data *data;
511 bool skip_host7;
513 data = of_device_get_match_data(dev);
514 skip_host7 = data ? data->no_host7_intr : false;
516 intc = devm_kzalloc(dev, sizeof(*intc), GFP_KERNEL);
517 if (!intc)
518 return -ENOMEM;
519 platform_set_drvdata(pdev, intc);
521 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
522 intc->base = devm_ioremap_resource(dev, res);
523 if (IS_ERR(intc->base)) {
524 dev_err(dev, "failed to parse and map intc memory resource\n");
525 return PTR_ERR(intc->base);
526 }
528 dev_dbg(dev, "intc memory: pa %pa size 0x%zx va %pK\n", &res->start,
529 (size_t)resource_size(res), intc->base);
531 mutex_init(&intc->lock);
533 for (i = 0; i < ARRAY_SIZE(intc->config_map.sysev_to_ch); i++)
534 intc->config_map.sysev_to_ch[i] = -1;
536 for (i = 0; i < ARRAY_SIZE(intc->config_map.ch_to_host); i++)
537 intc->config_map.ch_to_host[i] = -1;
539 intc->pruss = platform_get_drvdata(ppdev);
540 pruss_intc_init(intc);
542 irqchip = devm_kzalloc(dev, sizeof(*irqchip), GFP_KERNEL);
543 if (!irqchip)
544 return -ENOMEM;
546 irqchip->irq_ack = pruss_intc_irq_ack;
547 irqchip->irq_mask = pruss_intc_irq_mask;
548 irqchip->irq_unmask = pruss_intc_irq_unmask;
549 irqchip->irq_retrigger = pruss_intc_irq_retrigger;
550 irqchip->irq_request_resources = pruss_intc_irq_reqres;
551 irqchip->irq_release_resources = pruss_intc_irq_relres;
552 irqchip->name = dev_name(dev);
553 intc->irqchip = irqchip;
555 /* always 64 events */
556 intc->domain = irq_domain_add_linear(dev->of_node, MAX_PRU_SYS_EVENTS,
557 &pruss_intc_irq_domain_ops, intc);
558 if (!intc->domain)
559 return -ENOMEM;
561 for (i = 0; i < MAX_HOST_NUM_IRQS; i++) {
562 irq = platform_get_irq_byname(ppdev, irq_names[i]);
563 if (irq < 0) {
564 if (!strcmp(irq_names[i], "host7") && !!skip_host7)
565 continue;
567 dev_err(dev->parent, "platform_get_irq_byname failed for %s : %d\n",
568 irq_names[i], irq);
569 goto fail_irq;
570 }
572 intc->irqs[i] = irq;
573 irq_set_handler_data(irq, intc);
574 irq_set_chained_handler(irq, pruss_intc_irq_handler);
575 }
577 return 0;
579 fail_irq:
580 while (--i >= 0) {
581 if (intc->irqs[i])
582 irq_set_chained_handler_and_data(intc->irqs[i], NULL,
583 NULL);
584 }
585 irq_domain_remove(intc->domain);
586 return irq;
587 }
589 static int pruss_intc_remove(struct platform_device *pdev)
590 {
591 struct pruss_intc *intc = platform_get_drvdata(pdev);
592 unsigned int hwirq;
593 int i;
595 for (i = 0; i < MAX_HOST_NUM_IRQS; i++) {
596 if (intc->irqs[i])
597 irq_set_chained_handler_and_data(intc->irqs[i], NULL,
598 NULL);
599 }
601 if (intc->domain) {
602 for (hwirq = 0; hwirq < MAX_PRU_SYS_EVENTS; hwirq++)
603 irq_dispose_mapping(irq_find_mapping(intc->domain,
604 hwirq));
605 irq_domain_remove(intc->domain);
606 }
608 return 0;
609 }
611 static const struct pruss_intc_match_data am437x_pruss_intc_data = {
612 .no_host7_intr = true,
613 };
615 static const struct pruss_intc_match_data k2g_pruss_intc_data = {
616 .no_host7_intr = true,
617 };
619 static const struct of_device_id pruss_intc_of_match[] = {
620 {
621 .compatible = "ti,am3356-pruss-intc",
622 .data = NULL,
623 },
624 {
625 .compatible = "ti,am4376-pruss-intc",
626 .data = &am437x_pruss_intc_data,
627 },
628 {
629 .compatible = "ti,am5728-pruss-intc",
630 .data = NULL,
631 },
632 {
633 .compatible = "ti,k2g-pruss-intc",
634 .data = &k2g_pruss_intc_data,
635 },
636 { /* sentinel */ },
637 };
638 MODULE_DEVICE_TABLE(of, pruss_intc_of_match);
640 static struct platform_driver pruss_intc_driver = {
641 .driver = {
642 .name = "pruss-intc",
643 .of_match_table = pruss_intc_of_match,
644 },
645 .probe = pruss_intc_probe,
646 .remove = pruss_intc_remove,
647 };
648 module_platform_driver(pruss_intc_driver);
650 MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
651 MODULE_AUTHOR("Suman Anna <s-anna@ti.com>");
652 MODULE_DESCRIPTION("PRU-ICSS INTC Driver");
653 MODULE_LICENSE("GPL v2");