|
34 | 34 | #include <asm/smp_plat.h>
|
35 | 35 | #include <asm/mach/irq.h>
|
36 | 36 |
|
37 |
| -/* Interrupt Controller Registers Map */ |
38 |
| -#define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48) |
39 |
| -#define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C) |
40 |
| -#define ARMADA_370_XP_INT_FABRIC_MASK_OFFS (0x54) |
41 |
| -#define ARMADA_370_XP_INT_CAUSE_PERF(cpu) (1 << cpu) |
| 37 | +/* |
| 38 | + * Overall diagram of the Armada XP interrupt controller: |
| 39 | + * |
| 40 | + * To CPU 0 To CPU 1 |
| 41 | + * |
| 42 | + * /\ /\ |
| 43 | + * || || |
| 44 | + * +---------------+ +---------------+ |
| 45 | + * | | | | |
| 46 | + * | per-CPU | | per-CPU | |
| 47 | + * | mask/unmask | | mask/unmask | |
| 48 | + * | CPU0 | | CPU1 | |
| 49 | + * | | | | |
| 50 | + * +---------------+ +---------------+ |
| 51 | + * /\ /\ |
| 52 | + * || || |
| 53 | + * \\_______________________// |
| 54 | + * || |
| 55 | + * +-------------------+ |
| 56 | + * | | |
| 57 | + * | Global interrupt | |
| 58 | + * | mask/unmask | |
| 59 | + * | | |
| 60 | + * +-------------------+ |
| 61 | + * /\ |
| 62 | + * || |
| 63 | + * interrupt from |
| 64 | + * device |
| 65 | + * |
| 66 | + * The "global interrupt mask/unmask" is modified using the |
| 67 | + * ARMADA_370_XP_INT_SET_ENABLE_OFFS and |
| 68 | + * ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS registers, which are relative |
| 69 | + * to "main_int_base". |
| 70 | + * |
| 71 | + * The "per-CPU mask/unmask" is modified using the |
| 72 | + * ARMADA_370_XP_INT_SET_MASK_OFFS and |
| 73 | + * ARMADA_370_XP_INT_CLEAR_MASK_OFFS registers, which are relative to |
| 74 | + * "per_cpu_int_base". This base address points to a special address, |
| 75 | + * which automatically accesses the registers of the current CPU. |
| 76 | + * |
| 77 | + * The per-CPU mask/unmask can also be adjusted using the global |
| 78 | + * per-interrupt ARMADA_370_XP_INT_SOURCE_CTL register, which we use |
| 79 | + * to configure interrupt affinity. |
| 80 | + * |
| 81 | + * Due to this model, all interrupts need to be mask/unmasked at two |
| 82 | + * different levels: at the global level and at the per-CPU level. |
| 83 | + * |
| 84 | + * This driver takes the following approach to deal with this: |
| 85 | + * |
| 86 | + * - For global interrupts: |
| 87 | + * |
| 88 | + * At ->map() time, a global interrupt is unmasked at the per-CPU |
| 89 | + * mask/unmask level. It is therefore unmasked at this level for |
| 90 | + * the current CPU, running the ->map() code. This allows to have |
| 91 | + * the interrupt unmasked at this level in non-SMP |
| 92 | + * configurations. In SMP configurations, the ->set_affinity() |
| 93 | + * callback is called, which using the |
| 94 | + * ARMADA_370_XP_INT_SOURCE_CTL() readjusts the per-CPU mask/unmask |
| 95 | + * for the interrupt. |
| 96 | + * |
| 97 | + * The ->mask() and ->unmask() operations only mask/unmask the |
| 98 | + * interrupt at the "global" level. |
| 99 | + * |
| 100 | + * So, a global interrupt is enabled at the per-CPU level as soon |
| 101 | + * as it is mapped. At run time, the masking/unmasking takes place |
| 102 | + * at the global level. |
| 103 | + * |
| 104 | + * - For per-CPU interrupts |
| 105 | + * |
| 106 | + * At ->map() time, a per-CPU interrupt is unmasked at the global |
| 107 | + * mask/unmask level. |
| 108 | + * |
| 109 | + * The ->mask() and ->unmask() operations mask/unmask the interrupt |
| 110 | + * at the per-CPU level. |
| 111 | + * |
| 112 | + * So, a per-CPU interrupt is enabled at the global level as soon |
| 113 | + * as it is mapped. At run time, the masking/unmasking takes place |
| 114 | + * at the per-CPU level. |
| 115 | + */ |
42 | 116 |
|
| 117 | +/* Registers relative to main_int_base */ |
43 | 118 | #define ARMADA_370_XP_INT_CONTROL (0x00)
|
| 119 | +#define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x04) |
44 | 120 | #define ARMADA_370_XP_INT_SET_ENABLE_OFFS (0x30)
|
45 | 121 | #define ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS (0x34)
|
46 | 122 | #define ARMADA_370_XP_INT_SOURCE_CTL(irq) (0x100 + irq*4)
|
47 | 123 | #define ARMADA_370_XP_INT_SOURCE_CPU_MASK 0xF
|
48 | 124 | #define ARMADA_370_XP_INT_IRQ_FIQ_MASK(cpuid) ((BIT(0) | BIT(8)) << cpuid)
|
49 | 125 |
|
50 |
| -#define ARMADA_370_XP_CPU_INTACK_OFFS (0x44) |
| 126 | +/* Registers relative to per_cpu_int_base */ |
| 127 | +#define ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS (0x08) |
| 128 | +#define ARMADA_370_XP_IN_DRBEL_MSK_OFFS (0x0c) |
51 | 129 | #define ARMADA_375_PPI_CAUSE (0x10)
|
52 |
| - |
53 |
| -#define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x4) |
54 |
| -#define ARMADA_370_XP_IN_DRBEL_MSK_OFFS (0xc) |
55 |
| -#define ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS (0x8) |
| 130 | +#define ARMADA_370_XP_CPU_INTACK_OFFS (0x44) |
| 131 | +#define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48) |
| 132 | +#define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C) |
| 133 | +#define ARMADA_370_XP_INT_FABRIC_MASK_OFFS (0x54) |
| 134 | +#define ARMADA_370_XP_INT_CAUSE_PERF(cpu) (1 << cpu) |
56 | 135 |
|
57 | 136 | #define ARMADA_370_XP_MAX_PER_CPU_IRQS (28)
|
58 | 137 |
|
@@ -281,13 +360,11 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
|
281 | 360 | irq_set_percpu_devid(virq);
|
282 | 361 | irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
|
283 | 362 | handle_percpu_devid_irq);
|
284 |
| - |
285 | 363 | } else {
|
286 | 364 | irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
|
287 | 365 | handle_level_irq);
|
288 | 366 | }
|
289 | 367 | irq_set_probe(virq);
|
290 |
| - irq_clear_status_flags(virq, IRQ_NOAUTOEN); |
291 | 368 |
|
292 | 369 | return 0;
|
293 | 370 | }
|
@@ -345,16 +422,40 @@ static void armada_mpic_send_doorbell(const struct cpumask *mask,
|
345 | 422 | ARMADA_370_XP_SW_TRIG_INT_OFFS);
|
346 | 423 | }
|
347 | 424 |
|
| 425 | +static void armada_xp_mpic_reenable_percpu(void) |
| 426 | +{ |
| 427 | + unsigned int irq; |
| 428 | + |
| 429 | + /* Re-enable per-CPU interrupts that were enabled before suspend */ |
| 430 | + for (irq = 0; irq < ARMADA_370_XP_MAX_PER_CPU_IRQS; irq++) { |
| 431 | + struct irq_data *data; |
| 432 | + int virq; |
| 433 | + |
| 434 | + virq = irq_linear_revmap(armada_370_xp_mpic_domain, irq); |
| 435 | + if (virq == 0) |
| 436 | + continue; |
| 437 | + |
| 438 | + data = irq_get_irq_data(virq); |
| 439 | + |
| 440 | + if (!irq_percpu_is_enabled(virq)) |
| 441 | + continue; |
| 442 | + |
| 443 | + armada_370_xp_irq_unmask(data); |
| 444 | + } |
| 445 | +} |
| 446 | + |
348 | 447 | static int armada_xp_mpic_starting_cpu(unsigned int cpu)
|
349 | 448 | {
|
350 | 449 | armada_xp_mpic_perf_init();
|
351 | 450 | armada_xp_mpic_smp_cpu_init();
|
| 451 | + armada_xp_mpic_reenable_percpu(); |
352 | 452 | return 0;
|
353 | 453 | }
|
354 | 454 |
|
355 | 455 | static int mpic_cascaded_starting_cpu(unsigned int cpu)
|
356 | 456 | {
|
357 | 457 | armada_xp_mpic_perf_init();
|
| 458 | + armada_xp_mpic_reenable_percpu(); |
358 | 459 | enable_percpu_irq(parent_irq, IRQ_TYPE_NONE);
|
359 | 460 | return 0;
|
360 | 461 | }
|
@@ -502,16 +603,27 @@ static void armada_370_xp_mpic_resume(void)
|
502 | 603 | if (virq == 0)
|
503 | 604 | continue;
|
504 | 605 |
|
505 |
| - if (!is_percpu_irq(irq)) |
| 606 | + data = irq_get_irq_data(virq); |
| 607 | + |
| 608 | + if (!is_percpu_irq(irq)) { |
| 609 | + /* Non per-CPU interrupts */ |
506 | 610 | writel(irq, per_cpu_int_base +
|
507 | 611 | ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
|
508 |
| - else |
| 612 | + if (!irqd_irq_disabled(data)) |
| 613 | + armada_370_xp_irq_unmask(data); |
| 614 | + } else { |
| 615 | + /* Per-CPU interrupts */ |
509 | 616 | writel(irq, main_int_base +
|
510 | 617 | ARMADA_370_XP_INT_SET_ENABLE_OFFS);
|
511 | 618 |
|
512 |
| - data = irq_get_irq_data(virq); |
513 |
| - if (!irqd_irq_disabled(data)) |
514 |
| - armada_370_xp_irq_unmask(data); |
| 619 | + /* |
| 620 | + * Re-enable on the current CPU, |
| 621 | + * armada_xp_mpic_reenable_percpu() will take |
| 622 | + * care of secondary CPUs when they come up. |
| 623 | + */ |
| 624 | + if (irq_percpu_is_enabled(virq)) |
| 625 | + armada_370_xp_irq_unmask(data); |
| 626 | + } |
515 | 627 | }
|
516 | 628 |
|
517 | 629 | /* Reconfigure doorbells for IPIs and MSIs */
|
|
0 commit comments