1ac4a67b6SAndrew Jones /* 2ac4a67b6SAndrew Jones * GIC tests 3ac4a67b6SAndrew Jones * 4ac4a67b6SAndrew Jones * GICv2 5ac4a67b6SAndrew Jones * + test sending/receiving IPIs 62e2d471dSAndrew Jones * GICv3 72e2d471dSAndrew Jones * + test sending/receiving IPIs 8ac4a67b6SAndrew Jones * 9ac4a67b6SAndrew Jones * Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com> 10ac4a67b6SAndrew Jones * 11ac4a67b6SAndrew Jones * This work is licensed under the terms of the GNU LGPL, version 2. 12ac4a67b6SAndrew Jones */ 13ac4a67b6SAndrew Jones #include <libcflat.h> 14ac4a67b6SAndrew Jones #include <asm/setup.h> 15ac4a67b6SAndrew Jones #include <asm/processor.h> 16ac4a67b6SAndrew Jones #include <asm/delay.h> 17ac4a67b6SAndrew Jones #include <asm/gic.h> 18ac4a67b6SAndrew Jones #include <asm/smp.h> 19ac4a67b6SAndrew Jones #include <asm/barrier.h> 20ac4a67b6SAndrew Jones #include <asm/io.h> 21ac4a67b6SAndrew Jones 22*ca1b7a7bSAndrew Jones #define IPI_SENDER 1 23*ca1b7a7bSAndrew Jones #define IPI_IRQ 1 24*ca1b7a7bSAndrew Jones 252e2d471dSAndrew Jones struct gic { 262e2d471dSAndrew Jones struct { 272e2d471dSAndrew Jones void (*send_self)(void); 282e2d471dSAndrew Jones void (*send_broadcast)(void); 292e2d471dSAndrew Jones } ipi; 302e2d471dSAndrew Jones }; 312e2d471dSAndrew Jones 322e2d471dSAndrew Jones static struct gic *gic; 33ac4a67b6SAndrew Jones static int acked[NR_CPUS], spurious[NR_CPUS]; 34*ca1b7a7bSAndrew Jones static int bad_sender[NR_CPUS], bad_irq[NR_CPUS]; 35ac4a67b6SAndrew Jones static cpumask_t ready; 36ac4a67b6SAndrew Jones 37ac4a67b6SAndrew Jones static void nr_cpu_check(int nr) 38ac4a67b6SAndrew Jones { 39ac4a67b6SAndrew Jones if (nr_cpus < nr) 40ac4a67b6SAndrew Jones report_abort("At least %d cpus required", nr); 41ac4a67b6SAndrew Jones } 42ac4a67b6SAndrew Jones 43ac4a67b6SAndrew Jones static void wait_on_ready(void) 44ac4a67b6SAndrew Jones { 45ac4a67b6SAndrew Jones cpumask_set_cpu(smp_processor_id(), &ready); 46ac4a67b6SAndrew Jones while (!cpumask_full(&ready)) 47ac4a67b6SAndrew Jones cpu_relax(); 48ac4a67b6SAndrew Jones } 49ac4a67b6SAndrew Jones 50*ca1b7a7bSAndrew Jones static void stats_reset(void) 51*ca1b7a7bSAndrew Jones { 52*ca1b7a7bSAndrew Jones int i; 53*ca1b7a7bSAndrew Jones 54*ca1b7a7bSAndrew Jones for (i = 0; i < nr_cpus; ++i) { 55*ca1b7a7bSAndrew Jones acked[i] = 0; 56*ca1b7a7bSAndrew Jones bad_sender[i] = -1; 57*ca1b7a7bSAndrew Jones bad_irq[i] = -1; 58*ca1b7a7bSAndrew Jones } 59*ca1b7a7bSAndrew Jones smp_wmb(); 60*ca1b7a7bSAndrew Jones } 61*ca1b7a7bSAndrew Jones 62ac4a67b6SAndrew Jones static void check_acked(cpumask_t *mask) 63ac4a67b6SAndrew Jones { 64ac4a67b6SAndrew Jones int missing = 0, extra = 0, unexpected = 0; 65ac4a67b6SAndrew Jones int nr_pass, cpu, i; 66*ca1b7a7bSAndrew Jones bool bad = false; 67ac4a67b6SAndrew Jones 68ac4a67b6SAndrew Jones /* Wait up to 5s for all interrupts to be delivered */ 69ac4a67b6SAndrew Jones for (i = 0; i < 50; ++i) { 70ac4a67b6SAndrew Jones mdelay(100); 71ac4a67b6SAndrew Jones nr_pass = 0; 72ac4a67b6SAndrew Jones for_each_present_cpu(cpu) { 73ac4a67b6SAndrew Jones smp_rmb(); 74ac4a67b6SAndrew Jones nr_pass += cpumask_test_cpu(cpu, mask) ? 75ac4a67b6SAndrew Jones acked[cpu] == 1 : acked[cpu] == 0; 76*ca1b7a7bSAndrew Jones 77*ca1b7a7bSAndrew Jones if (bad_sender[cpu] != -1) { 78*ca1b7a7bSAndrew Jones printf("cpu%d received IPI from wrong sender %d\n", 79*ca1b7a7bSAndrew Jones cpu, bad_sender[cpu]); 80*ca1b7a7bSAndrew Jones bad = true; 81*ca1b7a7bSAndrew Jones } 82*ca1b7a7bSAndrew Jones 83*ca1b7a7bSAndrew Jones if (bad_irq[cpu] != -1) { 84*ca1b7a7bSAndrew Jones printf("cpu%d received wrong irq %d\n", 85*ca1b7a7bSAndrew Jones cpu, bad_irq[cpu]); 86*ca1b7a7bSAndrew Jones bad = true; 87*ca1b7a7bSAndrew Jones } 88ac4a67b6SAndrew Jones } 89ac4a67b6SAndrew Jones if (nr_pass == nr_cpus) { 90*ca1b7a7bSAndrew Jones report("Completed in %d ms", !bad, ++i * 100); 91ac4a67b6SAndrew Jones return; 92ac4a67b6SAndrew Jones } 93ac4a67b6SAndrew Jones } 94ac4a67b6SAndrew Jones 95ac4a67b6SAndrew Jones for_each_present_cpu(cpu) { 96ac4a67b6SAndrew Jones if (cpumask_test_cpu(cpu, mask)) { 97ac4a67b6SAndrew Jones if (!acked[cpu]) 98ac4a67b6SAndrew Jones ++missing; 99ac4a67b6SAndrew Jones else if (acked[cpu] > 1) 100ac4a67b6SAndrew Jones ++extra; 101ac4a67b6SAndrew Jones } else { 102ac4a67b6SAndrew Jones if (acked[cpu]) 103ac4a67b6SAndrew Jones ++unexpected; 104ac4a67b6SAndrew Jones } 105ac4a67b6SAndrew Jones } 106ac4a67b6SAndrew Jones 107ac4a67b6SAndrew Jones report("Timed-out (5s). ACKS: missing=%d extra=%d unexpected=%d", 108ac4a67b6SAndrew Jones false, missing, extra, unexpected); 109ac4a67b6SAndrew Jones } 110ac4a67b6SAndrew Jones 111ac4a67b6SAndrew Jones static void check_spurious(void) 112ac4a67b6SAndrew Jones { 113ac4a67b6SAndrew Jones int cpu; 114ac4a67b6SAndrew Jones 115ac4a67b6SAndrew Jones smp_rmb(); 116ac4a67b6SAndrew Jones for_each_present_cpu(cpu) { 117ac4a67b6SAndrew Jones if (spurious[cpu]) 118ac4a67b6SAndrew Jones report_info("WARN: cpu%d got %d spurious interrupts", 119ac4a67b6SAndrew Jones cpu, spurious[cpu]); 120ac4a67b6SAndrew Jones } 121ac4a67b6SAndrew Jones } 122ac4a67b6SAndrew Jones 123*ca1b7a7bSAndrew Jones static void check_ipi_sender(u32 irqstat) 124*ca1b7a7bSAndrew Jones { 125*ca1b7a7bSAndrew Jones if (gic_version() == 2) { 126*ca1b7a7bSAndrew Jones int src = (irqstat >> 10) & 7; 127*ca1b7a7bSAndrew Jones 128*ca1b7a7bSAndrew Jones if (src != IPI_SENDER) 129*ca1b7a7bSAndrew Jones bad_sender[smp_processor_id()] = src; 130*ca1b7a7bSAndrew Jones } 131*ca1b7a7bSAndrew Jones } 132*ca1b7a7bSAndrew Jones 133*ca1b7a7bSAndrew Jones static void check_irqnr(u32 irqnr) 134*ca1b7a7bSAndrew Jones { 135*ca1b7a7bSAndrew Jones if (irqnr != IPI_IRQ) 136*ca1b7a7bSAndrew Jones bad_irq[smp_processor_id()] = irqnr; 137*ca1b7a7bSAndrew Jones } 138*ca1b7a7bSAndrew Jones 139ac4a67b6SAndrew Jones static void ipi_handler(struct pt_regs *regs __unused) 140ac4a67b6SAndrew Jones { 1412e2d471dSAndrew Jones u32 irqstat = gic_read_iar(); 1422e2d471dSAndrew Jones u32 irqnr = gic_iar_irqnr(irqstat); 143ac4a67b6SAndrew Jones 144ac4a67b6SAndrew Jones if (irqnr != GICC_INT_SPURIOUS) { 1452e2d471dSAndrew Jones gic_write_eoir(irqstat); 146*ca1b7a7bSAndrew Jones smp_rmb(); /* pairs with wmb in stats_reset */ 147ac4a67b6SAndrew Jones ++acked[smp_processor_id()]; 148*ca1b7a7bSAndrew Jones check_ipi_sender(irqstat); 149*ca1b7a7bSAndrew Jones check_irqnr(irqnr); 150ac4a67b6SAndrew Jones smp_wmb(); /* pairs with rmb in check_acked */ 151ac4a67b6SAndrew Jones } else { 152ac4a67b6SAndrew Jones ++spurious[smp_processor_id()]; 153ac4a67b6SAndrew Jones smp_wmb(); 154ac4a67b6SAndrew Jones } 155ac4a67b6SAndrew Jones } 156ac4a67b6SAndrew Jones 1572e2d471dSAndrew Jones static void gicv2_ipi_send_self(void) 1582e2d471dSAndrew Jones { 159*ca1b7a7bSAndrew Jones writel(2 << 24 | IPI_IRQ, gicv2_dist_base() + GICD_SGIR); 1602e2d471dSAndrew Jones } 1612e2d471dSAndrew Jones 1622e2d471dSAndrew Jones static void gicv2_ipi_send_broadcast(void) 1632e2d471dSAndrew Jones { 164*ca1b7a7bSAndrew Jones writel(1 << 24 | IPI_IRQ, gicv2_dist_base() + GICD_SGIR); 1652e2d471dSAndrew Jones } 1662e2d471dSAndrew Jones 1672e2d471dSAndrew Jones static void gicv3_ipi_send_self(void) 1682e2d471dSAndrew Jones { 169*ca1b7a7bSAndrew Jones gic_ipi_send_single(IPI_IRQ, smp_processor_id()); 1702e2d471dSAndrew Jones } 1712e2d471dSAndrew Jones 1722e2d471dSAndrew Jones static void gicv3_ipi_send_broadcast(void) 1732e2d471dSAndrew Jones { 174*ca1b7a7bSAndrew Jones gicv3_write_sgi1r(1ULL << 40 | IPI_IRQ << 24); 1752e2d471dSAndrew Jones isb(); 1762e2d471dSAndrew Jones } 1772e2d471dSAndrew Jones 178ac4a67b6SAndrew Jones static void ipi_test_self(void) 179ac4a67b6SAndrew Jones { 180ac4a67b6SAndrew Jones cpumask_t mask; 181ac4a67b6SAndrew Jones 182ac4a67b6SAndrew Jones report_prefix_push("self"); 183*ca1b7a7bSAndrew Jones stats_reset(); 184ac4a67b6SAndrew Jones cpumask_clear(&mask); 185*ca1b7a7bSAndrew Jones cpumask_set_cpu(smp_processor_id(), &mask); 1862e2d471dSAndrew Jones gic->ipi.send_self(); 187ac4a67b6SAndrew Jones check_acked(&mask); 188ac4a67b6SAndrew Jones report_prefix_pop(); 189ac4a67b6SAndrew Jones } 190ac4a67b6SAndrew Jones 191ac4a67b6SAndrew Jones static void ipi_test_smp(void) 192ac4a67b6SAndrew Jones { 193ac4a67b6SAndrew Jones cpumask_t mask; 1942e2d471dSAndrew Jones int i; 195ac4a67b6SAndrew Jones 196ac4a67b6SAndrew Jones report_prefix_push("target-list"); 197*ca1b7a7bSAndrew Jones stats_reset(); 1982e2d471dSAndrew Jones cpumask_copy(&mask, &cpu_present_mask); 199*ca1b7a7bSAndrew Jones for (i = smp_processor_id() & 1; i < nr_cpus; i += 2) 2002e2d471dSAndrew Jones cpumask_clear_cpu(i, &mask); 201*ca1b7a7bSAndrew Jones gic_ipi_send_mask(IPI_IRQ, &mask); 202ac4a67b6SAndrew Jones check_acked(&mask); 203ac4a67b6SAndrew Jones report_prefix_pop(); 204ac4a67b6SAndrew Jones 205ac4a67b6SAndrew Jones report_prefix_push("broadcast"); 206*ca1b7a7bSAndrew Jones stats_reset(); 207ac4a67b6SAndrew Jones cpumask_copy(&mask, &cpu_present_mask); 208*ca1b7a7bSAndrew Jones cpumask_clear_cpu(smp_processor_id(), &mask); 2092e2d471dSAndrew Jones gic->ipi.send_broadcast(); 210ac4a67b6SAndrew Jones check_acked(&mask); 211ac4a67b6SAndrew Jones report_prefix_pop(); 212ac4a67b6SAndrew Jones } 213ac4a67b6SAndrew Jones 214ac4a67b6SAndrew Jones static void ipi_enable(void) 215ac4a67b6SAndrew Jones { 2162e2d471dSAndrew Jones gic_enable_defaults(); 217ac4a67b6SAndrew Jones #ifdef __arm__ 218ac4a67b6SAndrew Jones install_exception_handler(EXCPTN_IRQ, ipi_handler); 219ac4a67b6SAndrew Jones #else 220ac4a67b6SAndrew Jones install_irq_handler(EL1H_IRQ, ipi_handler); 221ac4a67b6SAndrew Jones #endif 222ac4a67b6SAndrew Jones local_irq_enable(); 223ac4a67b6SAndrew Jones } 224ac4a67b6SAndrew Jones 225*ca1b7a7bSAndrew Jones static void ipi_send(void) 226*ca1b7a7bSAndrew Jones { 227*ca1b7a7bSAndrew Jones ipi_enable(); 228*ca1b7a7bSAndrew Jones wait_on_ready(); 229*ca1b7a7bSAndrew Jones ipi_test_self(); 230*ca1b7a7bSAndrew Jones ipi_test_smp(); 231*ca1b7a7bSAndrew Jones check_spurious(); 232*ca1b7a7bSAndrew Jones exit(report_summary()); 233*ca1b7a7bSAndrew Jones } 234*ca1b7a7bSAndrew Jones 235ac4a67b6SAndrew Jones static void ipi_recv(void) 236ac4a67b6SAndrew Jones { 237ac4a67b6SAndrew Jones ipi_enable(); 238ac4a67b6SAndrew Jones cpumask_set_cpu(smp_processor_id(), &ready); 239ac4a67b6SAndrew Jones while (1) 240ac4a67b6SAndrew Jones wfi(); 241ac4a67b6SAndrew Jones } 242ac4a67b6SAndrew Jones 2432e2d471dSAndrew Jones static struct gic gicv2 = { 2442e2d471dSAndrew Jones .ipi = { 2452e2d471dSAndrew Jones .send_self = gicv2_ipi_send_self, 2462e2d471dSAndrew Jones .send_broadcast = gicv2_ipi_send_broadcast, 2472e2d471dSAndrew Jones }, 2482e2d471dSAndrew Jones }; 2492e2d471dSAndrew Jones 2502e2d471dSAndrew Jones static struct gic gicv3 = { 2512e2d471dSAndrew Jones .ipi = { 2522e2d471dSAndrew Jones .send_self = gicv3_ipi_send_self, 2532e2d471dSAndrew Jones .send_broadcast = gicv3_ipi_send_broadcast, 2542e2d471dSAndrew Jones }, 2552e2d471dSAndrew Jones }; 2562e2d471dSAndrew Jones 257ac4a67b6SAndrew Jones int main(int argc, char **argv) 258ac4a67b6SAndrew Jones { 259ac4a67b6SAndrew Jones char pfx[8]; 260ac4a67b6SAndrew Jones int cpu; 261ac4a67b6SAndrew Jones 2622e2d471dSAndrew Jones if (!gic_init()) { 263ac4a67b6SAndrew Jones printf("No supported gic present, skipping tests...\n"); 264ac4a67b6SAndrew Jones return report_summary(); 265ac4a67b6SAndrew Jones } 266ac4a67b6SAndrew Jones 2672e2d471dSAndrew Jones snprintf(pfx, sizeof(pfx), "gicv%d", gic_version()); 268ac4a67b6SAndrew Jones report_prefix_push(pfx); 269ac4a67b6SAndrew Jones 2702e2d471dSAndrew Jones switch (gic_version()) { 2712e2d471dSAndrew Jones case 2: 2722e2d471dSAndrew Jones gic = &gicv2; 2732e2d471dSAndrew Jones break; 2742e2d471dSAndrew Jones case 3: 2752e2d471dSAndrew Jones gic = &gicv3; 2762e2d471dSAndrew Jones break; 2772e2d471dSAndrew Jones } 2782e2d471dSAndrew Jones 279ac4a67b6SAndrew Jones if (argc < 2) 280ac4a67b6SAndrew Jones report_abort("no test specified"); 281ac4a67b6SAndrew Jones 282ac4a67b6SAndrew Jones if (strcmp(argv[1], "ipi") == 0) { 283ac4a67b6SAndrew Jones report_prefix_push(argv[1]); 284ac4a67b6SAndrew Jones nr_cpu_check(2); 285ac4a67b6SAndrew Jones 286ac4a67b6SAndrew Jones for_each_present_cpu(cpu) { 287ac4a67b6SAndrew Jones if (cpu == 0) 288ac4a67b6SAndrew Jones continue; 289*ca1b7a7bSAndrew Jones smp_boot_secondary(cpu, 290*ca1b7a7bSAndrew Jones cpu == IPI_SENDER ? ipi_send : ipi_recv); 291ac4a67b6SAndrew Jones } 292*ca1b7a7bSAndrew Jones ipi_recv(); 293ac4a67b6SAndrew Jones } else { 294ac4a67b6SAndrew Jones report_abort("Unknown subtest '%s'", argv[1]); 295ac4a67b6SAndrew Jones } 296ac4a67b6SAndrew Jones 297ac4a67b6SAndrew Jones return report_summary(); 298ac4a67b6SAndrew Jones } 299