1ac4a67b6SAndrew Jones /* 2ac4a67b6SAndrew Jones * GIC tests 3ac4a67b6SAndrew Jones * 4ac4a67b6SAndrew Jones * GICv2 5ac4a67b6SAndrew Jones * + test sending/receiving IPIs 678ad7e95SAndre Przywara * + MMIO access tests 72e2d471dSAndrew Jones * GICv3 82e2d471dSAndrew Jones * + test sending/receiving IPIs 9ac4a67b6SAndrew Jones * 10ac4a67b6SAndrew Jones * Copyright (C) 2016, Red Hat Inc, Andrew Jones <drjones@redhat.com> 11ac4a67b6SAndrew Jones * 12ac4a67b6SAndrew Jones * This work is licensed under the terms of the GNU LGPL, version 2. 13ac4a67b6SAndrew Jones */ 14ac4a67b6SAndrew Jones #include <libcflat.h> 15ac4a67b6SAndrew Jones #include <asm/setup.h> 16ac4a67b6SAndrew Jones #include <asm/processor.h> 17ac4a67b6SAndrew Jones #include <asm/delay.h> 18ac4a67b6SAndrew Jones #include <asm/gic.h> 19ac4a67b6SAndrew Jones #include <asm/smp.h> 20ac4a67b6SAndrew Jones #include <asm/barrier.h> 21ac4a67b6SAndrew Jones #include <asm/io.h> 22ac4a67b6SAndrew Jones 23ca1b7a7bSAndrew Jones #define IPI_SENDER 1 24ca1b7a7bSAndrew Jones #define IPI_IRQ 1 25ca1b7a7bSAndrew Jones 262e2d471dSAndrew Jones struct gic { 272e2d471dSAndrew Jones struct { 282e2d471dSAndrew Jones void (*send_self)(void); 292e2d471dSAndrew Jones void (*send_broadcast)(void); 302e2d471dSAndrew Jones } ipi; 312e2d471dSAndrew Jones }; 322e2d471dSAndrew Jones 332e2d471dSAndrew Jones static struct gic *gic; 34ac4a67b6SAndrew Jones static int acked[NR_CPUS], spurious[NR_CPUS]; 35ca1b7a7bSAndrew Jones static int bad_sender[NR_CPUS], bad_irq[NR_CPUS]; 36ac4a67b6SAndrew Jones static cpumask_t ready; 37ac4a67b6SAndrew Jones 38ac4a67b6SAndrew Jones static void nr_cpu_check(int nr) 39ac4a67b6SAndrew Jones { 40ac4a67b6SAndrew Jones if (nr_cpus < nr) 41ac4a67b6SAndrew Jones report_abort("At least %d cpus required", nr); 42ac4a67b6SAndrew Jones } 43ac4a67b6SAndrew Jones 44ac4a67b6SAndrew Jones static void wait_on_ready(void) 45ac4a67b6SAndrew Jones { 46ac4a67b6SAndrew Jones cpumask_set_cpu(smp_processor_id(), &ready); 47ac4a67b6SAndrew Jones while (!cpumask_full(&ready)) 48ac4a67b6SAndrew Jones cpu_relax(); 49ac4a67b6SAndrew Jones } 50ac4a67b6SAndrew Jones 51ca1b7a7bSAndrew Jones static void stats_reset(void) 52ca1b7a7bSAndrew Jones { 53ca1b7a7bSAndrew Jones int i; 54ca1b7a7bSAndrew Jones 55ca1b7a7bSAndrew Jones for (i = 0; i < nr_cpus; ++i) { 56ca1b7a7bSAndrew Jones acked[i] = 0; 57ca1b7a7bSAndrew Jones bad_sender[i] = -1; 58ca1b7a7bSAndrew Jones bad_irq[i] = -1; 59ca1b7a7bSAndrew Jones } 60ca1b7a7bSAndrew Jones smp_wmb(); 61ca1b7a7bSAndrew Jones } 62ca1b7a7bSAndrew Jones 6396edb026SAndre Przywara static void check_acked(const char *testname, cpumask_t *mask) 64ac4a67b6SAndrew Jones { 65ac4a67b6SAndrew Jones int missing = 0, extra = 0, unexpected = 0; 66ac4a67b6SAndrew Jones int nr_pass, cpu, i; 67ca1b7a7bSAndrew Jones bool bad = false; 68ac4a67b6SAndrew Jones 69ac4a67b6SAndrew Jones /* Wait up to 5s for all interrupts to be delivered */ 70ac4a67b6SAndrew Jones for (i = 0; i < 50; ++i) { 71ac4a67b6SAndrew Jones mdelay(100); 72ac4a67b6SAndrew Jones nr_pass = 0; 73ac4a67b6SAndrew Jones for_each_present_cpu(cpu) { 74ac4a67b6SAndrew Jones smp_rmb(); 75ac4a67b6SAndrew Jones nr_pass += cpumask_test_cpu(cpu, mask) ? 76ac4a67b6SAndrew Jones acked[cpu] == 1 : acked[cpu] == 0; 77ca1b7a7bSAndrew Jones 78ca1b7a7bSAndrew Jones if (bad_sender[cpu] != -1) { 79ca1b7a7bSAndrew Jones printf("cpu%d received IPI from wrong sender %d\n", 80ca1b7a7bSAndrew Jones cpu, bad_sender[cpu]); 81ca1b7a7bSAndrew Jones bad = true; 82ca1b7a7bSAndrew Jones } 83ca1b7a7bSAndrew Jones 84ca1b7a7bSAndrew Jones if (bad_irq[cpu] != -1) { 85ca1b7a7bSAndrew Jones printf("cpu%d received wrong irq %d\n", 86ca1b7a7bSAndrew Jones cpu, bad_irq[cpu]); 87ca1b7a7bSAndrew Jones bad = true; 88ca1b7a7bSAndrew Jones } 89ac4a67b6SAndrew Jones } 90ac4a67b6SAndrew Jones if (nr_pass == nr_cpus) { 91a299895bSThomas Huth report(!bad, "%s", testname); 9296edb026SAndre Przywara if (i) 9396edb026SAndre Przywara report_info("took more than %d ms", i * 100); 94ac4a67b6SAndrew Jones return; 95ac4a67b6SAndrew Jones } 96ac4a67b6SAndrew Jones } 97ac4a67b6SAndrew Jones 98ac4a67b6SAndrew Jones for_each_present_cpu(cpu) { 99ac4a67b6SAndrew Jones if (cpumask_test_cpu(cpu, mask)) { 100ac4a67b6SAndrew Jones if (!acked[cpu]) 101ac4a67b6SAndrew Jones ++missing; 102ac4a67b6SAndrew Jones else if (acked[cpu] > 1) 103ac4a67b6SAndrew Jones ++extra; 104ac4a67b6SAndrew Jones } else { 105ac4a67b6SAndrew Jones if (acked[cpu]) 106ac4a67b6SAndrew Jones ++unexpected; 107ac4a67b6SAndrew Jones } 108ac4a67b6SAndrew Jones } 109ac4a67b6SAndrew Jones 110a299895bSThomas Huth report(false, "%s", testname); 11196edb026SAndre Przywara report_info("Timed-out (5s). ACKS: missing=%d extra=%d unexpected=%d", 11296edb026SAndre Przywara missing, extra, unexpected); 113ac4a67b6SAndrew Jones } 114ac4a67b6SAndrew Jones 115ac4a67b6SAndrew Jones static void check_spurious(void) 116ac4a67b6SAndrew Jones { 117ac4a67b6SAndrew Jones int cpu; 118ac4a67b6SAndrew Jones 119ac4a67b6SAndrew Jones smp_rmb(); 120ac4a67b6SAndrew Jones for_each_present_cpu(cpu) { 121ac4a67b6SAndrew Jones if (spurious[cpu]) 122ac4a67b6SAndrew Jones report_info("WARN: cpu%d got %d spurious interrupts", 123ac4a67b6SAndrew Jones cpu, spurious[cpu]); 124ac4a67b6SAndrew Jones } 125ac4a67b6SAndrew Jones } 126ac4a67b6SAndrew Jones 127ca1b7a7bSAndrew Jones static void check_ipi_sender(u32 irqstat) 128ca1b7a7bSAndrew Jones { 129ca1b7a7bSAndrew Jones if (gic_version() == 2) { 130ca1b7a7bSAndrew Jones int src = (irqstat >> 10) & 7; 131ca1b7a7bSAndrew Jones 132ca1b7a7bSAndrew Jones if (src != IPI_SENDER) 133ca1b7a7bSAndrew Jones bad_sender[smp_processor_id()] = src; 134ca1b7a7bSAndrew Jones } 135ca1b7a7bSAndrew Jones } 136ca1b7a7bSAndrew Jones 137ca1b7a7bSAndrew Jones static void check_irqnr(u32 irqnr) 138ca1b7a7bSAndrew Jones { 139ca1b7a7bSAndrew Jones if (irqnr != IPI_IRQ) 140ca1b7a7bSAndrew Jones bad_irq[smp_processor_id()] = irqnr; 141ca1b7a7bSAndrew Jones } 142ca1b7a7bSAndrew Jones 143ac4a67b6SAndrew Jones static void ipi_handler(struct pt_regs *regs __unused) 144ac4a67b6SAndrew Jones { 1452e2d471dSAndrew Jones u32 irqstat = gic_read_iar(); 1462e2d471dSAndrew Jones u32 irqnr = gic_iar_irqnr(irqstat); 147ac4a67b6SAndrew Jones 148ac4a67b6SAndrew Jones if (irqnr != GICC_INT_SPURIOUS) { 1492e2d471dSAndrew Jones gic_write_eoir(irqstat); 150ca1b7a7bSAndrew Jones smp_rmb(); /* pairs with wmb in stats_reset */ 151ac4a67b6SAndrew Jones ++acked[smp_processor_id()]; 152ca1b7a7bSAndrew Jones check_ipi_sender(irqstat); 153ca1b7a7bSAndrew Jones check_irqnr(irqnr); 154ac4a67b6SAndrew Jones smp_wmb(); /* pairs with rmb in check_acked */ 155ac4a67b6SAndrew Jones } else { 156ac4a67b6SAndrew Jones ++spurious[smp_processor_id()]; 157ac4a67b6SAndrew Jones smp_wmb(); 158ac4a67b6SAndrew Jones } 159ac4a67b6SAndrew Jones } 160ac4a67b6SAndrew Jones 1612e2d471dSAndrew Jones static void gicv2_ipi_send_self(void) 1622e2d471dSAndrew Jones { 163ca1b7a7bSAndrew Jones writel(2 << 24 | IPI_IRQ, gicv2_dist_base() + GICD_SGIR); 1642e2d471dSAndrew Jones } 1652e2d471dSAndrew Jones 1662e2d471dSAndrew Jones static void gicv2_ipi_send_broadcast(void) 1672e2d471dSAndrew Jones { 168ca1b7a7bSAndrew Jones writel(1 << 24 | IPI_IRQ, gicv2_dist_base() + GICD_SGIR); 1692e2d471dSAndrew Jones } 1702e2d471dSAndrew Jones 1712e2d471dSAndrew Jones static void gicv3_ipi_send_self(void) 1722e2d471dSAndrew Jones { 173ca1b7a7bSAndrew Jones gic_ipi_send_single(IPI_IRQ, smp_processor_id()); 1742e2d471dSAndrew Jones } 1752e2d471dSAndrew Jones 1762e2d471dSAndrew Jones static void gicv3_ipi_send_broadcast(void) 1772e2d471dSAndrew Jones { 178ca1b7a7bSAndrew Jones gicv3_write_sgi1r(1ULL << 40 | IPI_IRQ << 24); 1792e2d471dSAndrew Jones isb(); 1802e2d471dSAndrew Jones } 1812e2d471dSAndrew Jones 182ac4a67b6SAndrew Jones static void ipi_test_self(void) 183ac4a67b6SAndrew Jones { 184ac4a67b6SAndrew Jones cpumask_t mask; 185ac4a67b6SAndrew Jones 186ac4a67b6SAndrew Jones report_prefix_push("self"); 187ca1b7a7bSAndrew Jones stats_reset(); 188ac4a67b6SAndrew Jones cpumask_clear(&mask); 189ca1b7a7bSAndrew Jones cpumask_set_cpu(smp_processor_id(), &mask); 1902e2d471dSAndrew Jones gic->ipi.send_self(); 19196edb026SAndre Przywara check_acked("IPI: self", &mask); 192ac4a67b6SAndrew Jones report_prefix_pop(); 193ac4a67b6SAndrew Jones } 194ac4a67b6SAndrew Jones 195ac4a67b6SAndrew Jones static void ipi_test_smp(void) 196ac4a67b6SAndrew Jones { 197ac4a67b6SAndrew Jones cpumask_t mask; 1982e2d471dSAndrew Jones int i; 199ac4a67b6SAndrew Jones 200ac4a67b6SAndrew Jones report_prefix_push("target-list"); 201ca1b7a7bSAndrew Jones stats_reset(); 2022e2d471dSAndrew Jones cpumask_copy(&mask, &cpu_present_mask); 203ca1b7a7bSAndrew Jones for (i = smp_processor_id() & 1; i < nr_cpus; i += 2) 2042e2d471dSAndrew Jones cpumask_clear_cpu(i, &mask); 205ca1b7a7bSAndrew Jones gic_ipi_send_mask(IPI_IRQ, &mask); 20696edb026SAndre Przywara check_acked("IPI: directed", &mask); 207ac4a67b6SAndrew Jones report_prefix_pop(); 208ac4a67b6SAndrew Jones 209ac4a67b6SAndrew Jones report_prefix_push("broadcast"); 210ca1b7a7bSAndrew Jones stats_reset(); 211ac4a67b6SAndrew Jones cpumask_copy(&mask, &cpu_present_mask); 212ca1b7a7bSAndrew Jones cpumask_clear_cpu(smp_processor_id(), &mask); 2132e2d471dSAndrew Jones gic->ipi.send_broadcast(); 21496edb026SAndre Przywara check_acked("IPI: broadcast", &mask); 215ac4a67b6SAndrew Jones report_prefix_pop(); 216ac4a67b6SAndrew Jones } 217ac4a67b6SAndrew Jones 218*25f66327SEric Auger static void setup_irq(irq_handler_fn handler) 219ac4a67b6SAndrew Jones { 2202e2d471dSAndrew Jones gic_enable_defaults(); 221ac4a67b6SAndrew Jones #ifdef __arm__ 222*25f66327SEric Auger install_exception_handler(EXCPTN_IRQ, handler); 223ac4a67b6SAndrew Jones #else 224*25f66327SEric Auger install_irq_handler(EL1H_IRQ, handler); 225ac4a67b6SAndrew Jones #endif 226ac4a67b6SAndrew Jones local_irq_enable(); 227ac4a67b6SAndrew Jones } 228ac4a67b6SAndrew Jones 229ca1b7a7bSAndrew Jones static void ipi_send(void) 230ca1b7a7bSAndrew Jones { 231*25f66327SEric Auger setup_irq(ipi_handler); 232ca1b7a7bSAndrew Jones wait_on_ready(); 233ca1b7a7bSAndrew Jones ipi_test_self(); 234ca1b7a7bSAndrew Jones ipi_test_smp(); 235ca1b7a7bSAndrew Jones check_spurious(); 236ca1b7a7bSAndrew Jones exit(report_summary()); 237ca1b7a7bSAndrew Jones } 238ca1b7a7bSAndrew Jones 239ac4a67b6SAndrew Jones static void ipi_recv(void) 240ac4a67b6SAndrew Jones { 241*25f66327SEric Auger setup_irq(ipi_handler); 242ac4a67b6SAndrew Jones cpumask_set_cpu(smp_processor_id(), &ready); 243ac4a67b6SAndrew Jones while (1) 244ac4a67b6SAndrew Jones wfi(); 245ac4a67b6SAndrew Jones } 246ac4a67b6SAndrew Jones 24700b34f56SAndrew Jones static void ipi_test(void *data __unused) 248bfd500b4SAndrew Jones { 249bfd500b4SAndrew Jones if (smp_processor_id() == IPI_SENDER) 250bfd500b4SAndrew Jones ipi_send(); 251bfd500b4SAndrew Jones else 252bfd500b4SAndrew Jones ipi_recv(); 253bfd500b4SAndrew Jones } 254bfd500b4SAndrew Jones 2552e2d471dSAndrew Jones static struct gic gicv2 = { 2562e2d471dSAndrew Jones .ipi = { 2572e2d471dSAndrew Jones .send_self = gicv2_ipi_send_self, 2582e2d471dSAndrew Jones .send_broadcast = gicv2_ipi_send_broadcast, 2592e2d471dSAndrew Jones }, 2602e2d471dSAndrew Jones }; 2612e2d471dSAndrew Jones 2622e2d471dSAndrew Jones static struct gic gicv3 = { 2632e2d471dSAndrew Jones .ipi = { 2642e2d471dSAndrew Jones .send_self = gicv3_ipi_send_self, 2652e2d471dSAndrew Jones .send_broadcast = gicv3_ipi_send_broadcast, 2662e2d471dSAndrew Jones }, 2672e2d471dSAndrew Jones }; 2682e2d471dSAndrew Jones 269c152d8bcSChristoffer Dall static void ipi_clear_active_handler(struct pt_regs *regs __unused) 270c152d8bcSChristoffer Dall { 271c152d8bcSChristoffer Dall u32 irqstat = gic_read_iar(); 272c152d8bcSChristoffer Dall u32 irqnr = gic_iar_irqnr(irqstat); 273c152d8bcSChristoffer Dall 274c152d8bcSChristoffer Dall if (irqnr != GICC_INT_SPURIOUS) { 275c152d8bcSChristoffer Dall void *base; 276c152d8bcSChristoffer Dall u32 val = 1 << IPI_IRQ; 277c152d8bcSChristoffer Dall 278c152d8bcSChristoffer Dall if (gic_version() == 2) 279c152d8bcSChristoffer Dall base = gicv2_dist_base(); 280c152d8bcSChristoffer Dall else 2816d4d7c4bSAndrew Jones base = gicv3_sgi_base(); 282c152d8bcSChristoffer Dall 283c152d8bcSChristoffer Dall writel(val, base + GICD_ICACTIVER); 284c152d8bcSChristoffer Dall 285c152d8bcSChristoffer Dall smp_rmb(); /* pairs with wmb in stats_reset */ 286c152d8bcSChristoffer Dall ++acked[smp_processor_id()]; 287c152d8bcSChristoffer Dall check_irqnr(irqnr); 288c152d8bcSChristoffer Dall smp_wmb(); /* pairs with rmb in check_acked */ 289c152d8bcSChristoffer Dall } else { 290c152d8bcSChristoffer Dall ++spurious[smp_processor_id()]; 291c152d8bcSChristoffer Dall smp_wmb(); 292c152d8bcSChristoffer Dall } 293c152d8bcSChristoffer Dall } 294c152d8bcSChristoffer Dall 295c152d8bcSChristoffer Dall static void run_active_clear_test(void) 296c152d8bcSChristoffer Dall { 297c152d8bcSChristoffer Dall report_prefix_push("active"); 298*25f66327SEric Auger setup_irq(ipi_clear_active_handler); 299c152d8bcSChristoffer Dall ipi_test_self(); 300c152d8bcSChristoffer Dall report_prefix_pop(); 301c152d8bcSChristoffer Dall } 302c152d8bcSChristoffer Dall 30378ad7e95SAndre Przywara static bool test_ro_pattern_32(void *address, u32 pattern, u32 orig) 30478ad7e95SAndre Przywara { 30578ad7e95SAndre Przywara u32 reg; 30678ad7e95SAndre Przywara 30778ad7e95SAndre Przywara writel(pattern, address); 30878ad7e95SAndre Przywara reg = readl(address); 30978ad7e95SAndre Przywara 31078ad7e95SAndre Przywara if (reg != orig) 31178ad7e95SAndre Przywara writel(orig, address); 31278ad7e95SAndre Przywara 31378ad7e95SAndre Przywara return reg == orig; 31478ad7e95SAndre Przywara } 31578ad7e95SAndre Przywara 31678ad7e95SAndre Przywara static bool test_readonly_32(void *address, bool razwi) 31778ad7e95SAndre Przywara { 31878ad7e95SAndre Przywara u32 orig, pattern; 31978ad7e95SAndre Przywara 32078ad7e95SAndre Przywara orig = readl(address); 32178ad7e95SAndre Przywara if (razwi && orig) 32278ad7e95SAndre Przywara return false; 32378ad7e95SAndre Przywara 32478ad7e95SAndre Przywara pattern = 0xffffffff; 32578ad7e95SAndre Przywara if (orig != pattern) { 32678ad7e95SAndre Przywara if (!test_ro_pattern_32(address, pattern, orig)) 32778ad7e95SAndre Przywara return false; 32878ad7e95SAndre Przywara } 32978ad7e95SAndre Przywara 33078ad7e95SAndre Przywara pattern = 0xa5a55a5a; 33178ad7e95SAndre Przywara if (orig != pattern) { 33278ad7e95SAndre Przywara if (!test_ro_pattern_32(address, pattern, orig)) 33378ad7e95SAndre Przywara return false; 33478ad7e95SAndre Przywara } 33578ad7e95SAndre Przywara 33678ad7e95SAndre Przywara pattern = 0; 33778ad7e95SAndre Przywara if (orig != pattern) { 33878ad7e95SAndre Przywara if (!test_ro_pattern_32(address, pattern, orig)) 33978ad7e95SAndre Przywara return false; 34078ad7e95SAndre Przywara } 34178ad7e95SAndre Przywara 34278ad7e95SAndre Przywara return true; 34378ad7e95SAndre Przywara } 34478ad7e95SAndre Przywara 34578ad7e95SAndre Przywara static void test_typer_v2(uint32_t reg) 34678ad7e95SAndre Przywara { 34778ad7e95SAndre Przywara int nr_gic_cpus = ((reg >> 5) & 0x7) + 1; 34878ad7e95SAndre Przywara 3498e0a4f41SAndre Przywara report_info("nr_cpus=%d", nr_cpus); 350a299895bSThomas Huth report(nr_cpus == nr_gic_cpus, "all CPUs have interrupts"); 35178ad7e95SAndre Przywara } 35278ad7e95SAndre Przywara 353ff31a1c4SAndre Przywara #define BYTE(reg32, byte) (((reg32) >> ((byte) * 8)) & 0xff) 354ff31a1c4SAndre Przywara #define REPLACE_BYTE(reg32, byte, new) (((reg32) & ~(0xff << ((byte) * 8))) |\ 355ff31a1c4SAndre Przywara ((new) << ((byte) * 8))) 356ff31a1c4SAndre Przywara 357ff31a1c4SAndre Przywara /* 358ff31a1c4SAndre Przywara * Some registers are byte accessible, do a byte-wide read and write of known 359ff31a1c4SAndre Przywara * content to check for this. 360ff31a1c4SAndre Przywara * Apply a @mask to cater for special register properties. 361ff31a1c4SAndre Przywara * @pattern contains the value already in the register. 362ff31a1c4SAndre Przywara */ 363ff31a1c4SAndre Przywara static void test_byte_access(void *base_addr, u32 pattern, u32 mask) 364ff31a1c4SAndre Przywara { 365ff31a1c4SAndre Przywara u32 reg = readb(base_addr + 1); 3668e0a4f41SAndre Przywara bool res; 367ff31a1c4SAndre Przywara 3688e0a4f41SAndre Przywara res = (reg == (BYTE(pattern, 1) & (mask >> 8))); 369a299895bSThomas Huth report(res, "byte reads successful"); 3708e0a4f41SAndre Przywara if (!res) 3718e0a4f41SAndre Przywara report_info("byte 1 of 0x%08x => 0x%02x", pattern & mask, reg); 372ff31a1c4SAndre Przywara 373ff31a1c4SAndre Przywara pattern = REPLACE_BYTE(pattern, 2, 0x1f); 374ff31a1c4SAndre Przywara writeb(BYTE(pattern, 2), base_addr + 2); 375ff31a1c4SAndre Przywara reg = readl(base_addr); 3768e0a4f41SAndre Przywara res = (reg == (pattern & mask)); 377a299895bSThomas Huth report(res, "byte writes successful"); 3788e0a4f41SAndre Przywara if (!res) 3798e0a4f41SAndre Przywara report_info("writing 0x%02x into bytes 2 => 0x%08x", 3808e0a4f41SAndre Przywara BYTE(pattern, 2), reg); 381ff31a1c4SAndre Przywara } 382ff31a1c4SAndre Przywara 383ff31a1c4SAndre Przywara static void test_priorities(int nr_irqs, void *priptr) 384ff31a1c4SAndre Przywara { 385ff31a1c4SAndre Przywara u32 orig_prio, reg, pri_bits; 386ff31a1c4SAndre Przywara u32 pri_mask, pattern; 387ff31a1c4SAndre Przywara void *first_spi = priptr + GIC_FIRST_SPI; 388ff31a1c4SAndre Przywara 389ff31a1c4SAndre Przywara orig_prio = readl(first_spi); 390ff31a1c4SAndre Przywara report_prefix_push("IPRIORITYR"); 391ff31a1c4SAndre Przywara 392ff31a1c4SAndre Przywara /* 393ff31a1c4SAndre Przywara * Determine implemented number of priority bits by writing all 1's 394ff31a1c4SAndre Przywara * and checking the number of cleared bits in the value read back. 395ff31a1c4SAndre Przywara */ 396ff31a1c4SAndre Przywara writel(0xffffffff, first_spi); 397ff31a1c4SAndre Przywara pri_mask = readl(first_spi); 398ff31a1c4SAndre Przywara 399ff31a1c4SAndre Przywara reg = ~pri_mask; 400a299895bSThomas Huth report((((reg >> 16) == (reg & 0xffff)) && 401a299895bSThomas Huth ((reg & 0xff) == ((reg >> 8) & 0xff))), 402a299895bSThomas Huth "consistent priority masking"); 4038e0a4f41SAndre Przywara report_info("priority mask is 0x%08x", pri_mask); 404ff31a1c4SAndre Przywara 405ff31a1c4SAndre Przywara reg = reg & 0xff; 406ff31a1c4SAndre Przywara for (pri_bits = 8; reg & 1; reg >>= 1, pri_bits--) 407ff31a1c4SAndre Przywara ; 408a299895bSThomas Huth report(pri_bits >= 4, "implements at least 4 priority bits"); 4098e0a4f41SAndre Przywara report_info("%d priority bits implemented", pri_bits); 410ff31a1c4SAndre Przywara 411ff31a1c4SAndre Przywara pattern = 0; 412ff31a1c4SAndre Przywara writel(pattern, first_spi); 413a299895bSThomas Huth report(readl(first_spi) == pattern, "clearing priorities"); 414ff31a1c4SAndre Przywara 415ff31a1c4SAndre Przywara /* setting all priorities to their max valus was tested above */ 416ff31a1c4SAndre Przywara 417a299895bSThomas Huth report(test_readonly_32(priptr + nr_irqs, true), 418a299895bSThomas Huth "accesses beyond limit RAZ/WI"); 419ff31a1c4SAndre Przywara 420ff31a1c4SAndre Przywara writel(pattern, priptr + nr_irqs - 4); 421a299895bSThomas Huth report(readl(priptr + nr_irqs - 4) == (pattern & pri_mask), 422a299895bSThomas Huth "accessing last SPIs"); 423ff31a1c4SAndre Przywara 424ff31a1c4SAndre Przywara pattern = 0xff7fbf3f; 425ff31a1c4SAndre Przywara writel(pattern, first_spi); 426a299895bSThomas Huth report(readl(first_spi) == (pattern & pri_mask), 427a299895bSThomas Huth "priorities are preserved"); 428ff31a1c4SAndre Przywara 429ff31a1c4SAndre Przywara /* The PRIORITY registers are byte accessible. */ 430ff31a1c4SAndre Przywara test_byte_access(first_spi, pattern, pri_mask); 431ff31a1c4SAndre Przywara 432ff31a1c4SAndre Przywara report_prefix_pop(); 433ff31a1c4SAndre Przywara writel(orig_prio, first_spi); 434ff31a1c4SAndre Przywara } 435ff31a1c4SAndre Przywara 436fe572a5eSAndre Przywara /* GICD_ITARGETSR is only used by GICv2. */ 437fe572a5eSAndre Przywara static void test_targets(int nr_irqs) 438fe572a5eSAndre Przywara { 439fe572a5eSAndre Przywara void *targetsptr = gicv2_dist_base() + GICD_ITARGETSR; 440fe572a5eSAndre Przywara u32 orig_targets; 441fe572a5eSAndre Przywara u32 cpu_mask; 442fe572a5eSAndre Przywara u32 pattern, reg; 443fe572a5eSAndre Przywara 444fe572a5eSAndre Przywara orig_targets = readl(targetsptr + GIC_FIRST_SPI); 445fe572a5eSAndre Przywara report_prefix_push("ITARGETSR"); 446fe572a5eSAndre Przywara 447fe572a5eSAndre Przywara cpu_mask = (1 << nr_cpus) - 1; 448fe572a5eSAndre Przywara cpu_mask |= cpu_mask << 8; 449fe572a5eSAndre Przywara cpu_mask |= cpu_mask << 16; 450fe572a5eSAndre Przywara 451fe572a5eSAndre Przywara /* Check that bits for non implemented CPUs are RAZ/WI. */ 452fe572a5eSAndre Przywara if (nr_cpus < 8) { 453fe572a5eSAndre Przywara writel(0xffffffff, targetsptr + GIC_FIRST_SPI); 454a299895bSThomas Huth report(!(readl(targetsptr + GIC_FIRST_SPI) & ~cpu_mask), 455a299895bSThomas Huth "bits for non-existent CPUs masked"); 4568e0a4f41SAndre Przywara report_info("%d non-existent CPUs", 8 - nr_cpus); 457fe572a5eSAndre Przywara } else { 458fe572a5eSAndre Przywara report_skip("CPU masking (all CPUs implemented)"); 459fe572a5eSAndre Przywara } 460fe572a5eSAndre Przywara 461a299895bSThomas Huth report(test_readonly_32(targetsptr + nr_irqs, true), 462a299895bSThomas Huth "accesses beyond limit RAZ/WI"); 463fe572a5eSAndre Przywara 464fe572a5eSAndre Przywara pattern = 0x0103020f; 465fe572a5eSAndre Przywara writel(pattern, targetsptr + GIC_FIRST_SPI); 466fe572a5eSAndre Przywara reg = readl(targetsptr + GIC_FIRST_SPI); 467a299895bSThomas Huth report(reg == (pattern & cpu_mask), "register content preserved"); 4688e0a4f41SAndre Przywara if (reg != (pattern & cpu_mask)) 4698e0a4f41SAndre Przywara report_info("writing %08x reads back as %08x", 4708e0a4f41SAndre Przywara pattern & cpu_mask, reg); 471fe572a5eSAndre Przywara 472fe572a5eSAndre Przywara /* The TARGETS registers are byte accessible. */ 473fe572a5eSAndre Przywara test_byte_access(targetsptr + GIC_FIRST_SPI, pattern, cpu_mask); 474fe572a5eSAndre Przywara 475fe572a5eSAndre Przywara writel(orig_targets, targetsptr + GIC_FIRST_SPI); 476da5b8576SAndre Przywara 477da5b8576SAndre Przywara report_prefix_pop(); 478fe572a5eSAndre Przywara } 479fe572a5eSAndre Przywara 48078ad7e95SAndre Przywara static void gic_test_mmio(void) 48178ad7e95SAndre Przywara { 48278ad7e95SAndre Przywara u32 reg; 48378ad7e95SAndre Przywara int nr_irqs; 48478ad7e95SAndre Przywara void *gic_dist_base, *idreg; 48578ad7e95SAndre Przywara 48678ad7e95SAndre Przywara switch(gic_version()) { 48778ad7e95SAndre Przywara case 0x2: 48878ad7e95SAndre Przywara gic_dist_base = gicv2_dist_base(); 48978ad7e95SAndre Przywara idreg = gic_dist_base + GICD_ICPIDR2; 49078ad7e95SAndre Przywara break; 49178ad7e95SAndre Przywara case 0x3: 49278ad7e95SAndre Przywara report_abort("GICv3 MMIO tests NYI"); 49378ad7e95SAndre Przywara default: 49478ad7e95SAndre Przywara report_abort("GIC version %d not supported", gic_version()); 49578ad7e95SAndre Przywara } 49678ad7e95SAndre Przywara 49778ad7e95SAndre Przywara reg = readl(gic_dist_base + GICD_TYPER); 49878ad7e95SAndre Przywara nr_irqs = GICD_TYPER_IRQS(reg); 49978ad7e95SAndre Przywara report_info("number of implemented SPIs: %d", nr_irqs - GIC_FIRST_SPI); 50078ad7e95SAndre Przywara 50178ad7e95SAndre Przywara test_typer_v2(reg); 50278ad7e95SAndre Przywara 50378ad7e95SAndre Przywara report_info("IIDR: 0x%08x", readl(gic_dist_base + GICD_IIDR)); 50478ad7e95SAndre Przywara 505a299895bSThomas Huth report(test_readonly_32(gic_dist_base + GICD_TYPER, false), 506a299895bSThomas Huth "GICD_TYPER is read-only"); 507a299895bSThomas Huth report(test_readonly_32(gic_dist_base + GICD_IIDR, false), 508a299895bSThomas Huth "GICD_IIDR is read-only"); 50978ad7e95SAndre Przywara 51078ad7e95SAndre Przywara reg = readl(idreg); 511a299895bSThomas Huth report(test_readonly_32(idreg, false), "ICPIDR2 is read-only"); 5128e0a4f41SAndre Przywara report_info("value of ICPIDR2: 0x%08x", reg); 513ff31a1c4SAndre Przywara 514ff31a1c4SAndre Przywara test_priorities(nr_irqs, gic_dist_base + GICD_IPRIORITYR); 515fe572a5eSAndre Przywara 516fe572a5eSAndre Przywara if (gic_version() == 2) 517fe572a5eSAndre Przywara test_targets(nr_irqs); 51878ad7e95SAndre Przywara } 51978ad7e95SAndre Przywara 520ac4a67b6SAndrew Jones int main(int argc, char **argv) 521ac4a67b6SAndrew Jones { 5222e2d471dSAndrew Jones if (!gic_init()) { 523ac4a67b6SAndrew Jones printf("No supported gic present, skipping tests...\n"); 524ac4a67b6SAndrew Jones return report_summary(); 525ac4a67b6SAndrew Jones } 526ac4a67b6SAndrew Jones 5272b19b829SAndrew Jones report_prefix_pushf("gicv%d", gic_version()); 528ac4a67b6SAndrew Jones 5292e2d471dSAndrew Jones switch (gic_version()) { 5302e2d471dSAndrew Jones case 2: 5312e2d471dSAndrew Jones gic = &gicv2; 5322e2d471dSAndrew Jones break; 5332e2d471dSAndrew Jones case 3: 5342e2d471dSAndrew Jones gic = &gicv3; 5352e2d471dSAndrew Jones break; 5362e2d471dSAndrew Jones } 5372e2d471dSAndrew Jones 538ac4a67b6SAndrew Jones if (argc < 2) 539ac4a67b6SAndrew Jones report_abort("no test specified"); 540ac4a67b6SAndrew Jones 541ac4a67b6SAndrew Jones if (strcmp(argv[1], "ipi") == 0) { 542ac4a67b6SAndrew Jones report_prefix_push(argv[1]); 543ac4a67b6SAndrew Jones nr_cpu_check(2); 54400b34f56SAndrew Jones on_cpus(ipi_test, NULL); 545c152d8bcSChristoffer Dall } else if (strcmp(argv[1], "active") == 0) { 546c152d8bcSChristoffer Dall run_active_clear_test(); 54778ad7e95SAndre Przywara } else if (strcmp(argv[1], "mmio") == 0) { 54878ad7e95SAndre Przywara report_prefix_push(argv[1]); 54978ad7e95SAndre Przywara gic_test_mmio(); 55078ad7e95SAndre Przywara report_prefix_pop(); 551ac4a67b6SAndrew Jones } else { 552ac4a67b6SAndrew Jones report_abort("Unknown subtest '%s'", argv[1]); 553ac4a67b6SAndrew Jones } 554ac4a67b6SAndrew Jones 555ac4a67b6SAndrew Jones return report_summary(); 556ac4a67b6SAndrew Jones } 557