1ac4a67b6SAndrew Jones /* 2ac4a67b6SAndrew Jones * GIC tests 3ac4a67b6SAndrew Jones * 4ac4a67b6SAndrew Jones * GICv2 5ac4a67b6SAndrew Jones * + test sending/receiving IPIs 6*2e2d471dSAndrew Jones * GICv3 7*2e2d471dSAndrew 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*2e2d471dSAndrew Jones struct gic { 23*2e2d471dSAndrew Jones struct { 24*2e2d471dSAndrew Jones void (*send_self)(void); 25*2e2d471dSAndrew Jones void (*send_broadcast)(void); 26*2e2d471dSAndrew Jones } ipi; 27*2e2d471dSAndrew Jones }; 28*2e2d471dSAndrew Jones 29*2e2d471dSAndrew Jones static struct gic *gic; 30ac4a67b6SAndrew Jones static int acked[NR_CPUS], spurious[NR_CPUS]; 31ac4a67b6SAndrew Jones static cpumask_t ready; 32ac4a67b6SAndrew Jones 33ac4a67b6SAndrew Jones static void nr_cpu_check(int nr) 34ac4a67b6SAndrew Jones { 35ac4a67b6SAndrew Jones if (nr_cpus < nr) 36ac4a67b6SAndrew Jones report_abort("At least %d cpus required", nr); 37ac4a67b6SAndrew Jones } 38ac4a67b6SAndrew Jones 39ac4a67b6SAndrew Jones static void wait_on_ready(void) 40ac4a67b6SAndrew Jones { 41ac4a67b6SAndrew Jones cpumask_set_cpu(smp_processor_id(), &ready); 42ac4a67b6SAndrew Jones while (!cpumask_full(&ready)) 43ac4a67b6SAndrew Jones cpu_relax(); 44ac4a67b6SAndrew Jones } 45ac4a67b6SAndrew Jones 46ac4a67b6SAndrew Jones static void check_acked(cpumask_t *mask) 47ac4a67b6SAndrew Jones { 48ac4a67b6SAndrew Jones int missing = 0, extra = 0, unexpected = 0; 49ac4a67b6SAndrew Jones int nr_pass, cpu, i; 50ac4a67b6SAndrew Jones 51ac4a67b6SAndrew Jones /* Wait up to 5s for all interrupts to be delivered */ 52ac4a67b6SAndrew Jones for (i = 0; i < 50; ++i) { 53ac4a67b6SAndrew Jones mdelay(100); 54ac4a67b6SAndrew Jones nr_pass = 0; 55ac4a67b6SAndrew Jones for_each_present_cpu(cpu) { 56ac4a67b6SAndrew Jones smp_rmb(); 57ac4a67b6SAndrew Jones nr_pass += cpumask_test_cpu(cpu, mask) ? 58ac4a67b6SAndrew Jones acked[cpu] == 1 : acked[cpu] == 0; 59ac4a67b6SAndrew Jones } 60ac4a67b6SAndrew Jones if (nr_pass == nr_cpus) { 61ac4a67b6SAndrew Jones report("Completed in %d ms", true, ++i * 100); 62ac4a67b6SAndrew Jones return; 63ac4a67b6SAndrew Jones } 64ac4a67b6SAndrew Jones } 65ac4a67b6SAndrew Jones 66ac4a67b6SAndrew Jones for_each_present_cpu(cpu) { 67ac4a67b6SAndrew Jones if (cpumask_test_cpu(cpu, mask)) { 68ac4a67b6SAndrew Jones if (!acked[cpu]) 69ac4a67b6SAndrew Jones ++missing; 70ac4a67b6SAndrew Jones else if (acked[cpu] > 1) 71ac4a67b6SAndrew Jones ++extra; 72ac4a67b6SAndrew Jones } else { 73ac4a67b6SAndrew Jones if (acked[cpu]) 74ac4a67b6SAndrew Jones ++unexpected; 75ac4a67b6SAndrew Jones } 76ac4a67b6SAndrew Jones } 77ac4a67b6SAndrew Jones 78ac4a67b6SAndrew Jones report("Timed-out (5s). ACKS: missing=%d extra=%d unexpected=%d", 79ac4a67b6SAndrew Jones false, missing, extra, unexpected); 80ac4a67b6SAndrew Jones } 81ac4a67b6SAndrew Jones 82ac4a67b6SAndrew Jones static void check_spurious(void) 83ac4a67b6SAndrew Jones { 84ac4a67b6SAndrew Jones int cpu; 85ac4a67b6SAndrew Jones 86ac4a67b6SAndrew Jones smp_rmb(); 87ac4a67b6SAndrew Jones for_each_present_cpu(cpu) { 88ac4a67b6SAndrew Jones if (spurious[cpu]) 89ac4a67b6SAndrew Jones report_info("WARN: cpu%d got %d spurious interrupts", 90ac4a67b6SAndrew Jones cpu, spurious[cpu]); 91ac4a67b6SAndrew Jones } 92ac4a67b6SAndrew Jones } 93ac4a67b6SAndrew Jones 94ac4a67b6SAndrew Jones static void ipi_handler(struct pt_regs *regs __unused) 95ac4a67b6SAndrew Jones { 96*2e2d471dSAndrew Jones u32 irqstat = gic_read_iar(); 97*2e2d471dSAndrew Jones u32 irqnr = gic_iar_irqnr(irqstat); 98ac4a67b6SAndrew Jones 99ac4a67b6SAndrew Jones if (irqnr != GICC_INT_SPURIOUS) { 100*2e2d471dSAndrew Jones gic_write_eoir(irqstat); 101ac4a67b6SAndrew Jones smp_rmb(); /* pairs with wmb in ipi_test functions */ 102ac4a67b6SAndrew Jones ++acked[smp_processor_id()]; 103ac4a67b6SAndrew Jones smp_wmb(); /* pairs with rmb in check_acked */ 104ac4a67b6SAndrew Jones } else { 105ac4a67b6SAndrew Jones ++spurious[smp_processor_id()]; 106ac4a67b6SAndrew Jones smp_wmb(); 107ac4a67b6SAndrew Jones } 108ac4a67b6SAndrew Jones } 109ac4a67b6SAndrew Jones 110*2e2d471dSAndrew Jones static void gicv2_ipi_send_self(void) 111*2e2d471dSAndrew Jones { 112*2e2d471dSAndrew Jones writel(2 << 24, gicv2_dist_base() + GICD_SGIR); 113*2e2d471dSAndrew Jones } 114*2e2d471dSAndrew Jones 115*2e2d471dSAndrew Jones static void gicv2_ipi_send_broadcast(void) 116*2e2d471dSAndrew Jones { 117*2e2d471dSAndrew Jones writel(1 << 24, gicv2_dist_base() + GICD_SGIR); 118*2e2d471dSAndrew Jones } 119*2e2d471dSAndrew Jones 120*2e2d471dSAndrew Jones static void gicv3_ipi_send_self(void) 121*2e2d471dSAndrew Jones { 122*2e2d471dSAndrew Jones gic_ipi_send_single(0, smp_processor_id()); 123*2e2d471dSAndrew Jones } 124*2e2d471dSAndrew Jones 125*2e2d471dSAndrew Jones static void gicv3_ipi_send_broadcast(void) 126*2e2d471dSAndrew Jones { 127*2e2d471dSAndrew Jones gicv3_write_sgi1r(1ULL << 40); 128*2e2d471dSAndrew Jones isb(); 129*2e2d471dSAndrew Jones } 130*2e2d471dSAndrew Jones 131ac4a67b6SAndrew Jones static void ipi_test_self(void) 132ac4a67b6SAndrew Jones { 133ac4a67b6SAndrew Jones cpumask_t mask; 134ac4a67b6SAndrew Jones 135ac4a67b6SAndrew Jones report_prefix_push("self"); 136ac4a67b6SAndrew Jones memset(acked, 0, sizeof(acked)); 137ac4a67b6SAndrew Jones smp_wmb(); 138ac4a67b6SAndrew Jones cpumask_clear(&mask); 139ac4a67b6SAndrew Jones cpumask_set_cpu(0, &mask); 140*2e2d471dSAndrew Jones gic->ipi.send_self(); 141ac4a67b6SAndrew Jones check_acked(&mask); 142ac4a67b6SAndrew Jones report_prefix_pop(); 143ac4a67b6SAndrew Jones } 144ac4a67b6SAndrew Jones 145ac4a67b6SAndrew Jones static void ipi_test_smp(void) 146ac4a67b6SAndrew Jones { 147ac4a67b6SAndrew Jones cpumask_t mask; 148*2e2d471dSAndrew Jones int i; 149ac4a67b6SAndrew Jones 150ac4a67b6SAndrew Jones report_prefix_push("target-list"); 151ac4a67b6SAndrew Jones memset(acked, 0, sizeof(acked)); 152ac4a67b6SAndrew Jones smp_wmb(); 153*2e2d471dSAndrew Jones cpumask_copy(&mask, &cpu_present_mask); 154*2e2d471dSAndrew Jones for (i = 0; i < nr_cpus; i += 2) 155*2e2d471dSAndrew Jones cpumask_clear_cpu(i, &mask); 156*2e2d471dSAndrew Jones gic_ipi_send_mask(0, &mask); 157ac4a67b6SAndrew Jones check_acked(&mask); 158ac4a67b6SAndrew Jones report_prefix_pop(); 159ac4a67b6SAndrew Jones 160ac4a67b6SAndrew Jones report_prefix_push("broadcast"); 161ac4a67b6SAndrew Jones memset(acked, 0, sizeof(acked)); 162ac4a67b6SAndrew Jones smp_wmb(); 163ac4a67b6SAndrew Jones cpumask_copy(&mask, &cpu_present_mask); 164ac4a67b6SAndrew Jones cpumask_clear_cpu(0, &mask); 165*2e2d471dSAndrew Jones gic->ipi.send_broadcast(); 166ac4a67b6SAndrew Jones check_acked(&mask); 167ac4a67b6SAndrew Jones report_prefix_pop(); 168ac4a67b6SAndrew Jones } 169ac4a67b6SAndrew Jones 170ac4a67b6SAndrew Jones static void ipi_enable(void) 171ac4a67b6SAndrew Jones { 172*2e2d471dSAndrew Jones gic_enable_defaults(); 173ac4a67b6SAndrew Jones #ifdef __arm__ 174ac4a67b6SAndrew Jones install_exception_handler(EXCPTN_IRQ, ipi_handler); 175ac4a67b6SAndrew Jones #else 176ac4a67b6SAndrew Jones install_irq_handler(EL1H_IRQ, ipi_handler); 177ac4a67b6SAndrew Jones #endif 178ac4a67b6SAndrew Jones local_irq_enable(); 179ac4a67b6SAndrew Jones } 180ac4a67b6SAndrew Jones 181ac4a67b6SAndrew Jones static void ipi_recv(void) 182ac4a67b6SAndrew Jones { 183ac4a67b6SAndrew Jones ipi_enable(); 184ac4a67b6SAndrew Jones cpumask_set_cpu(smp_processor_id(), &ready); 185ac4a67b6SAndrew Jones while (1) 186ac4a67b6SAndrew Jones wfi(); 187ac4a67b6SAndrew Jones } 188ac4a67b6SAndrew Jones 189*2e2d471dSAndrew Jones static struct gic gicv2 = { 190*2e2d471dSAndrew Jones .ipi = { 191*2e2d471dSAndrew Jones .send_self = gicv2_ipi_send_self, 192*2e2d471dSAndrew Jones .send_broadcast = gicv2_ipi_send_broadcast, 193*2e2d471dSAndrew Jones }, 194*2e2d471dSAndrew Jones }; 195*2e2d471dSAndrew Jones 196*2e2d471dSAndrew Jones static struct gic gicv3 = { 197*2e2d471dSAndrew Jones .ipi = { 198*2e2d471dSAndrew Jones .send_self = gicv3_ipi_send_self, 199*2e2d471dSAndrew Jones .send_broadcast = gicv3_ipi_send_broadcast, 200*2e2d471dSAndrew Jones }, 201*2e2d471dSAndrew Jones }; 202*2e2d471dSAndrew Jones 203ac4a67b6SAndrew Jones int main(int argc, char **argv) 204ac4a67b6SAndrew Jones { 205ac4a67b6SAndrew Jones char pfx[8]; 206ac4a67b6SAndrew Jones int cpu; 207ac4a67b6SAndrew Jones 208*2e2d471dSAndrew Jones if (!gic_init()) { 209ac4a67b6SAndrew Jones printf("No supported gic present, skipping tests...\n"); 210ac4a67b6SAndrew Jones return report_summary(); 211ac4a67b6SAndrew Jones } 212ac4a67b6SAndrew Jones 213*2e2d471dSAndrew Jones snprintf(pfx, sizeof(pfx), "gicv%d", gic_version()); 214ac4a67b6SAndrew Jones report_prefix_push(pfx); 215ac4a67b6SAndrew Jones 216*2e2d471dSAndrew Jones switch (gic_version()) { 217*2e2d471dSAndrew Jones case 2: 218*2e2d471dSAndrew Jones gic = &gicv2; 219*2e2d471dSAndrew Jones break; 220*2e2d471dSAndrew Jones case 3: 221*2e2d471dSAndrew Jones gic = &gicv3; 222*2e2d471dSAndrew Jones break; 223*2e2d471dSAndrew Jones } 224*2e2d471dSAndrew Jones 225ac4a67b6SAndrew Jones if (argc < 2) 226ac4a67b6SAndrew Jones report_abort("no test specified"); 227ac4a67b6SAndrew Jones 228ac4a67b6SAndrew Jones if (strcmp(argv[1], "ipi") == 0) { 229ac4a67b6SAndrew Jones report_prefix_push(argv[1]); 230ac4a67b6SAndrew Jones nr_cpu_check(2); 231ac4a67b6SAndrew Jones 232ac4a67b6SAndrew Jones for_each_present_cpu(cpu) { 233ac4a67b6SAndrew Jones if (cpu == 0) 234ac4a67b6SAndrew Jones continue; 235ac4a67b6SAndrew Jones smp_boot_secondary(cpu, ipi_recv); 236ac4a67b6SAndrew Jones } 237ac4a67b6SAndrew Jones ipi_enable(); 238ac4a67b6SAndrew Jones wait_on_ready(); 239ac4a67b6SAndrew Jones ipi_test_self(); 240ac4a67b6SAndrew Jones ipi_test_smp(); 241ac4a67b6SAndrew Jones check_spurious(); 242ac4a67b6SAndrew Jones report_prefix_pop(); 243ac4a67b6SAndrew Jones } else { 244ac4a67b6SAndrew Jones report_abort("Unknown subtest '%s'", argv[1]); 245ac4a67b6SAndrew Jones } 246ac4a67b6SAndrew Jones 247ac4a67b6SAndrew Jones return report_summary(); 248ac4a67b6SAndrew Jones } 249