diff options
author | Ludovic Barre <ludovic.barre@st.com> | 2018-04-26 12:18:30 -0400 |
---|---|---|
committer | Marc Zyngier <marc.zyngier@arm.com> | 2018-05-24 07:38:22 -0400 |
commit | 927abfc4461e7fd76d5bba593dabdec381571021 (patch) | |
tree | 315a582263eb9c674023f868dce8d4b285d02bcf | |
parent | 5a2490e029c29065c6bc2f63c10a985f85522998 (diff) |
irqchip/stm32: Add stm32mp1 support with hierarchy domain
Exti controller has been differently integrated on stm32mp1 SoC.
A parent irq has only one external interrupt. A hierachy domain could
be used. Handlers are call by parent, each parent interrupt could be
masked and unmasked according to the needs.
Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
-rw-r--r-- | Documentation/devicetree/bindings/interrupt-controller/st,stm32-exti.txt | 3 | ||||
-rw-r--r-- | drivers/irqchip/irq-stm32-exti.c | 322 |
2 files changed, 325 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/interrupt-controller/st,stm32-exti.txt b/Documentation/devicetree/bindings/interrupt-controller/st,stm32-exti.txt index edf03f09244b..136bd612bd83 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/st,stm32-exti.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/st,stm32-exti.txt | |||
@@ -5,11 +5,14 @@ Required properties: | |||
5 | - compatible: Should be: | 5 | - compatible: Should be: |
6 | "st,stm32-exti" | 6 | "st,stm32-exti" |
7 | "st,stm32h7-exti" | 7 | "st,stm32h7-exti" |
8 | "st,stm32mp1-exti" | ||
8 | - reg: Specifies base physical address and size of the registers | 9 | - reg: Specifies base physical address and size of the registers |
9 | - interrupt-controller: Indentifies the node as an interrupt controller | 10 | - interrupt-controller: Indentifies the node as an interrupt controller |
10 | - #interrupt-cells: Specifies the number of cells to encode an interrupt | 11 | - #interrupt-cells: Specifies the number of cells to encode an interrupt |
11 | specifier, shall be 2 | 12 | specifier, shall be 2 |
12 | - interrupts: interrupts references to primary interrupt controller | 13 | - interrupts: interrupts references to primary interrupt controller |
14 | (only needed for exti controller with multiple exti under | ||
15 | same parent interrupt: st,stm32-exti and st,stm32h7-exti") | ||
13 | 16 | ||
14 | Example: | 17 | Example: |
15 | 18 | ||
diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c index b38c6555d66f..ebf71460c668 100644 --- a/drivers/irqchip/irq-stm32-exti.c +++ b/drivers/irqchip/irq-stm32-exti.c | |||
@@ -15,6 +15,8 @@ | |||
15 | #include <linux/of_address.h> | 15 | #include <linux/of_address.h> |
16 | #include <linux/of_irq.h> | 16 | #include <linux/of_irq.h> |
17 | 17 | ||
18 | #include <dt-bindings/interrupt-controller/arm-gic.h> | ||
19 | |||
18 | #define IRQS_PER_BANK 32 | 20 | #define IRQS_PER_BANK 32 |
19 | 21 | ||
20 | struct stm32_exti_bank { | 22 | struct stm32_exti_bank { |
@@ -29,14 +31,24 @@ struct stm32_exti_bank { | |||
29 | 31 | ||
30 | #define UNDEF_REG ~0 | 32 | #define UNDEF_REG ~0 |
31 | 33 | ||
34 | struct stm32_desc_irq { | ||
35 | u32 exti; | ||
36 | u32 irq_parent; | ||
37 | }; | ||
38 | |||
32 | struct stm32_exti_drv_data { | 39 | struct stm32_exti_drv_data { |
33 | const struct stm32_exti_bank **exti_banks; | 40 | const struct stm32_exti_bank **exti_banks; |
41 | const struct stm32_desc_irq *desc_irqs; | ||
34 | u32 bank_nr; | 42 | u32 bank_nr; |
43 | u32 irq_nr; | ||
35 | }; | 44 | }; |
36 | 45 | ||
37 | struct stm32_exti_chip_data { | 46 | struct stm32_exti_chip_data { |
38 | struct stm32_exti_host_data *host_data; | 47 | struct stm32_exti_host_data *host_data; |
39 | const struct stm32_exti_bank *reg_bank; | 48 | const struct stm32_exti_bank *reg_bank; |
49 | struct raw_spinlock rlock; | ||
50 | u32 wake_active; | ||
51 | u32 mask_cache; | ||
40 | u32 rtsr_cache; | 52 | u32 rtsr_cache; |
41 | u32 ftsr_cache; | 53 | u32 ftsr_cache; |
42 | }; | 54 | }; |
@@ -107,6 +119,89 @@ static const struct stm32_exti_drv_data stm32h7xx_drv_data = { | |||
107 | .bank_nr = ARRAY_SIZE(stm32h7xx_exti_banks), | 119 | .bank_nr = ARRAY_SIZE(stm32h7xx_exti_banks), |
108 | }; | 120 | }; |
109 | 121 | ||
122 | static const struct stm32_exti_bank stm32mp1_exti_b1 = { | ||
123 | .imr_ofst = 0x80, | ||
124 | .emr_ofst = 0x84, | ||
125 | .rtsr_ofst = 0x00, | ||
126 | .ftsr_ofst = 0x04, | ||
127 | .swier_ofst = 0x08, | ||
128 | .rpr_ofst = 0x0C, | ||
129 | .fpr_ofst = 0x10, | ||
130 | }; | ||
131 | |||
132 | static const struct stm32_exti_bank stm32mp1_exti_b2 = { | ||
133 | .imr_ofst = 0x90, | ||
134 | .emr_ofst = 0x94, | ||
135 | .rtsr_ofst = 0x20, | ||
136 | .ftsr_ofst = 0x24, | ||
137 | .swier_ofst = 0x28, | ||
138 | .rpr_ofst = 0x2C, | ||
139 | .fpr_ofst = 0x30, | ||
140 | }; | ||
141 | |||
142 | static const struct stm32_exti_bank stm32mp1_exti_b3 = { | ||
143 | .imr_ofst = 0xA0, | ||
144 | .emr_ofst = 0xA4, | ||
145 | .rtsr_ofst = 0x40, | ||
146 | .ftsr_ofst = 0x44, | ||
147 | .swier_ofst = 0x48, | ||
148 | .rpr_ofst = 0x4C, | ||
149 | .fpr_ofst = 0x50, | ||
150 | }; | ||
151 | |||
152 | static const struct stm32_exti_bank *stm32mp1_exti_banks[] = { | ||
153 | &stm32mp1_exti_b1, | ||
154 | &stm32mp1_exti_b2, | ||
155 | &stm32mp1_exti_b3, | ||
156 | }; | ||
157 | |||
158 | static const struct stm32_desc_irq stm32mp1_desc_irq[] = { | ||
159 | { .exti = 1, .irq_parent = 7 }, | ||
160 | { .exti = 2, .irq_parent = 8 }, | ||
161 | { .exti = 3, .irq_parent = 9 }, | ||
162 | { .exti = 4, .irq_parent = 10 }, | ||
163 | { .exti = 5, .irq_parent = 23 }, | ||
164 | { .exti = 6, .irq_parent = 64 }, | ||
165 | { .exti = 7, .irq_parent = 65 }, | ||
166 | { .exti = 8, .irq_parent = 66 }, | ||
167 | { .exti = 9, .irq_parent = 67 }, | ||
168 | { .exti = 10, .irq_parent = 40 }, | ||
169 | { .exti = 11, .irq_parent = 42 }, | ||
170 | { .exti = 12, .irq_parent = 76 }, | ||
171 | { .exti = 13, .irq_parent = 77 }, | ||
172 | { .exti = 14, .irq_parent = 121 }, | ||
173 | { .exti = 15, .irq_parent = 127 }, | ||
174 | { .exti = 16, .irq_parent = 1 }, | ||
175 | { .exti = 65, .irq_parent = 144 }, | ||
176 | { .exti = 68, .irq_parent = 143 }, | ||
177 | { .exti = 73, .irq_parent = 129 }, | ||
178 | }; | ||
179 | |||
180 | static const struct stm32_exti_drv_data stm32mp1_drv_data = { | ||
181 | .exti_banks = stm32mp1_exti_banks, | ||
182 | .bank_nr = ARRAY_SIZE(stm32mp1_exti_banks), | ||
183 | .desc_irqs = stm32mp1_desc_irq, | ||
184 | .irq_nr = ARRAY_SIZE(stm32mp1_desc_irq), | ||
185 | }; | ||
186 | |||
187 | static int stm32_exti_to_irq(const struct stm32_exti_drv_data *drv_data, | ||
188 | irq_hw_number_t hwirq) | ||
189 | { | ||
190 | const struct stm32_desc_irq *desc_irq; | ||
191 | int i; | ||
192 | |||
193 | if (!drv_data->desc_irqs) | ||
194 | return -EINVAL; | ||
195 | |||
196 | for (i = 0; i < drv_data->irq_nr; i++) { | ||
197 | desc_irq = &drv_data->desc_irqs[i]; | ||
198 | if (desc_irq->exti == hwirq) | ||
199 | return desc_irq->irq_parent; | ||
200 | } | ||
201 | |||
202 | return -EINVAL; | ||
203 | } | ||
204 | |||
110 | static unsigned long stm32_exti_pending(struct irq_chip_generic *gc) | 205 | static unsigned long stm32_exti_pending(struct irq_chip_generic *gc) |
111 | { | 206 | { |
112 | struct stm32_exti_chip_data *chip_data = gc->private; | 207 | struct stm32_exti_chip_data *chip_data = gc->private; |
@@ -282,6 +377,173 @@ static void stm32_irq_ack(struct irq_data *d) | |||
282 | 377 | ||
283 | irq_gc_unlock(gc); | 378 | irq_gc_unlock(gc); |
284 | } | 379 | } |
380 | |||
381 | static inline u32 stm32_exti_set_bit(struct irq_data *d, u32 reg) | ||
382 | { | ||
383 | struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); | ||
384 | void __iomem *base = chip_data->host_data->base; | ||
385 | u32 val; | ||
386 | |||
387 | val = readl_relaxed(base + reg); | ||
388 | val |= BIT(d->hwirq % IRQS_PER_BANK); | ||
389 | writel_relaxed(val, base + reg); | ||
390 | |||
391 | return val; | ||
392 | } | ||
393 | |||
394 | static inline u32 stm32_exti_clr_bit(struct irq_data *d, u32 reg) | ||
395 | { | ||
396 | struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); | ||
397 | void __iomem *base = chip_data->host_data->base; | ||
398 | u32 val; | ||
399 | |||
400 | val = readl_relaxed(base + reg); | ||
401 | val &= ~BIT(d->hwirq % IRQS_PER_BANK); | ||
402 | writel_relaxed(val, base + reg); | ||
403 | |||
404 | return val; | ||
405 | } | ||
406 | |||
407 | static void stm32_exti_h_eoi(struct irq_data *d) | ||
408 | { | ||
409 | struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); | ||
410 | const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; | ||
411 | |||
412 | raw_spin_lock(&chip_data->rlock); | ||
413 | |||
414 | stm32_exti_set_bit(d, stm32_bank->rpr_ofst); | ||
415 | if (stm32_bank->fpr_ofst != UNDEF_REG) | ||
416 | stm32_exti_set_bit(d, stm32_bank->fpr_ofst); | ||
417 | |||
418 | raw_spin_unlock(&chip_data->rlock); | ||
419 | |||
420 | if (d->parent_data->chip) | ||
421 | irq_chip_eoi_parent(d); | ||
422 | } | ||
423 | |||
424 | static void stm32_exti_h_mask(struct irq_data *d) | ||
425 | { | ||
426 | struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); | ||
427 | const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; | ||
428 | |||
429 | raw_spin_lock(&chip_data->rlock); | ||
430 | chip_data->mask_cache = stm32_exti_clr_bit(d, stm32_bank->imr_ofst); | ||
431 | raw_spin_unlock(&chip_data->rlock); | ||
432 | |||
433 | if (d->parent_data->chip) | ||
434 | irq_chip_mask_parent(d); | ||
435 | } | ||
436 | |||
437 | static void stm32_exti_h_unmask(struct irq_data *d) | ||
438 | { | ||
439 | struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); | ||
440 | const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; | ||
441 | |||
442 | raw_spin_lock(&chip_data->rlock); | ||
443 | chip_data->mask_cache = stm32_exti_set_bit(d, stm32_bank->imr_ofst); | ||
444 | raw_spin_unlock(&chip_data->rlock); | ||
445 | |||
446 | if (d->parent_data->chip) | ||
447 | irq_chip_unmask_parent(d); | ||
448 | } | ||
449 | |||
450 | static int stm32_exti_h_set_type(struct irq_data *d, unsigned int type) | ||
451 | { | ||
452 | struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); | ||
453 | const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; | ||
454 | void __iomem *base = chip_data->host_data->base; | ||
455 | u32 rtsr, ftsr; | ||
456 | int err; | ||
457 | |||
458 | raw_spin_lock(&chip_data->rlock); | ||
459 | rtsr = readl_relaxed(base + stm32_bank->rtsr_ofst); | ||
460 | ftsr = readl_relaxed(base + stm32_bank->ftsr_ofst); | ||
461 | |||
462 | err = stm32_exti_set_type(d, type, &rtsr, &ftsr); | ||
463 | if (err) { | ||
464 | raw_spin_unlock(&chip_data->rlock); | ||
465 | return err; | ||
466 | } | ||
467 | |||
468 | writel_relaxed(rtsr, base + stm32_bank->rtsr_ofst); | ||
469 | writel_relaxed(ftsr, base + stm32_bank->ftsr_ofst); | ||
470 | raw_spin_unlock(&chip_data->rlock); | ||
471 | |||
472 | return 0; | ||
473 | } | ||
474 | |||
475 | static int stm32_exti_h_set_wake(struct irq_data *d, unsigned int on) | ||
476 | { | ||
477 | struct stm32_exti_chip_data *chip_data = irq_data_get_irq_chip_data(d); | ||
478 | u32 mask = BIT(d->hwirq % IRQS_PER_BANK); | ||
479 | |||
480 | raw_spin_lock(&chip_data->rlock); | ||
481 | |||
482 | if (on) | ||
483 | chip_data->wake_active |= mask; | ||
484 | else | ||
485 | chip_data->wake_active &= ~mask; | ||
486 | |||
487 | raw_spin_unlock(&chip_data->rlock); | ||
488 | |||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | static int stm32_exti_h_set_affinity(struct irq_data *d, | ||
493 | const struct cpumask *dest, bool force) | ||
494 | { | ||
495 | if (d->parent_data->chip) | ||
496 | return irq_chip_set_affinity_parent(d, dest, force); | ||
497 | |||
498 | return -EINVAL; | ||
499 | } | ||
500 | |||
501 | static struct irq_chip stm32_exti_h_chip = { | ||
502 | .name = "stm32-exti-h", | ||
503 | .irq_eoi = stm32_exti_h_eoi, | ||
504 | .irq_mask = stm32_exti_h_mask, | ||
505 | .irq_unmask = stm32_exti_h_unmask, | ||
506 | .irq_retrigger = irq_chip_retrigger_hierarchy, | ||
507 | .irq_set_type = stm32_exti_h_set_type, | ||
508 | .irq_set_wake = stm32_exti_h_set_wake, | ||
509 | .flags = IRQCHIP_MASK_ON_SUSPEND, | ||
510 | #ifdef CONFIG_SMP | ||
511 | .irq_set_affinity = stm32_exti_h_set_affinity, | ||
512 | #endif | ||
513 | }; | ||
514 | |||
515 | static int stm32_exti_h_domain_alloc(struct irq_domain *dm, | ||
516 | unsigned int virq, | ||
517 | unsigned int nr_irqs, void *data) | ||
518 | { | ||
519 | struct stm32_exti_host_data *host_data = dm->host_data; | ||
520 | struct stm32_exti_chip_data *chip_data; | ||
521 | struct irq_fwspec *fwspec = data; | ||
522 | struct irq_fwspec p_fwspec; | ||
523 | irq_hw_number_t hwirq; | ||
524 | int p_irq, bank; | ||
525 | |||
526 | hwirq = fwspec->param[0]; | ||
527 | bank = hwirq / IRQS_PER_BANK; | ||
528 | chip_data = &host_data->chips_data[bank]; | ||
529 | |||
530 | irq_domain_set_hwirq_and_chip(dm, virq, hwirq, | ||
531 | &stm32_exti_h_chip, chip_data); | ||
532 | |||
533 | p_irq = stm32_exti_to_irq(host_data->drv_data, hwirq); | ||
534 | if (p_irq >= 0) { | ||
535 | p_fwspec.fwnode = dm->parent->fwnode; | ||
536 | p_fwspec.param_count = 3; | ||
537 | p_fwspec.param[0] = GIC_SPI; | ||
538 | p_fwspec.param[1] = p_irq; | ||
539 | p_fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH; | ||
540 | |||
541 | return irq_domain_alloc_irqs_parent(dm, virq, 1, &p_fwspec); | ||
542 | } | ||
543 | |||
544 | return 0; | ||
545 | } | ||
546 | |||
285 | static struct | 547 | static struct |
286 | stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd, | 548 | stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd, |
287 | struct device_node *node) | 549 | struct device_node *node) |
@@ -323,6 +585,8 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, | |||
323 | chip_data->host_data = h_data; | 585 | chip_data->host_data = h_data; |
324 | chip_data->reg_bank = stm32_bank; | 586 | chip_data->reg_bank = stm32_bank; |
325 | 587 | ||
588 | raw_spin_lock_init(&chip_data->rlock); | ||
589 | |||
326 | /* Determine number of irqs supported */ | 590 | /* Determine number of irqs supported */ |
327 | writel_relaxed(~0UL, base + stm32_bank->rtsr_ofst); | 591 | writel_relaxed(~0UL, base + stm32_bank->rtsr_ofst); |
328 | irqs_mask = readl_relaxed(base + stm32_bank->rtsr_ofst); | 592 | irqs_mask = readl_relaxed(base + stm32_bank->rtsr_ofst); |
@@ -421,6 +685,56 @@ out_free_mem: | |||
421 | return ret; | 685 | return ret; |
422 | } | 686 | } |
423 | 687 | ||
688 | static const struct irq_domain_ops stm32_exti_h_domain_ops = { | ||
689 | .alloc = stm32_exti_h_domain_alloc, | ||
690 | .free = irq_domain_free_irqs_common, | ||
691 | }; | ||
692 | |||
693 | static int | ||
694 | __init stm32_exti_hierarchy_init(const struct stm32_exti_drv_data *drv_data, | ||
695 | struct device_node *node, | ||
696 | struct device_node *parent) | ||
697 | { | ||
698 | struct irq_domain *parent_domain, *domain; | ||
699 | struct stm32_exti_host_data *host_data; | ||
700 | int ret, i; | ||
701 | |||
702 | parent_domain = irq_find_host(parent); | ||
703 | if (!parent_domain) { | ||
704 | pr_err("interrupt-parent not found\n"); | ||
705 | return -EINVAL; | ||
706 | } | ||
707 | |||
708 | host_data = stm32_exti_host_init(drv_data, node); | ||
709 | if (!host_data) { | ||
710 | ret = -ENOMEM; | ||
711 | goto out_free_mem; | ||
712 | } | ||
713 | |||
714 | for (i = 0; i < drv_data->bank_nr; i++) | ||
715 | stm32_exti_chip_init(host_data, i, node); | ||
716 | |||
717 | domain = irq_domain_add_hierarchy(parent_domain, 0, | ||
718 | drv_data->bank_nr * IRQS_PER_BANK, | ||
719 | node, &stm32_exti_h_domain_ops, | ||
720 | host_data); | ||
721 | |||
722 | if (!domain) { | ||
723 | pr_err("%s: Could not register exti domain.\n", node->name); | ||
724 | ret = -ENOMEM; | ||
725 | goto out_unmap; | ||
726 | } | ||
727 | |||
728 | return 0; | ||
729 | |||
730 | out_unmap: | ||
731 | iounmap(host_data->base); | ||
732 | out_free_mem: | ||
733 | kfree(host_data->chips_data); | ||
734 | kfree(host_data); | ||
735 | return ret; | ||
736 | } | ||
737 | |||
424 | static int __init stm32f4_exti_of_init(struct device_node *np, | 738 | static int __init stm32f4_exti_of_init(struct device_node *np, |
425 | struct device_node *parent) | 739 | struct device_node *parent) |
426 | { | 740 | { |
@@ -436,3 +750,11 @@ static int __init stm32h7_exti_of_init(struct device_node *np, | |||
436 | } | 750 | } |
437 | 751 | ||
438 | IRQCHIP_DECLARE(stm32h7_exti, "st,stm32h7-exti", stm32h7_exti_of_init); | 752 | IRQCHIP_DECLARE(stm32h7_exti, "st,stm32h7-exti", stm32h7_exti_of_init); |
753 | |||
754 | static int __init stm32mp1_exti_of_init(struct device_node *np, | ||
755 | struct device_node *parent) | ||
756 | { | ||
757 | return stm32_exti_hierarchy_init(&stm32mp1_drv_data, np, parent); | ||
758 | } | ||
759 | |||
760 | IRQCHIP_DECLARE(stm32mp1_exti, "st,stm32mp1-exti", stm32mp1_exti_of_init); | ||