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) { 91*a299895bSThomas 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 110*a299895bSThomas 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 218ac4a67b6SAndrew Jones static void ipi_enable(void) 219ac4a67b6SAndrew Jones { 2202e2d471dSAndrew Jones gic_enable_defaults(); 221ac4a67b6SAndrew Jones #ifdef __arm__ 222ac4a67b6SAndrew Jones install_exception_handler(EXCPTN_IRQ, ipi_handler); 223ac4a67b6SAndrew Jones #else 224ac4a67b6SAndrew Jones install_irq_handler(EL1H_IRQ, ipi_handler); 225ac4a67b6SAndrew Jones #endif 226ac4a67b6SAndrew Jones local_irq_enable(); 227ac4a67b6SAndrew Jones } 228ac4a67b6SAndrew Jones 229ca1b7a7bSAndrew Jones static void ipi_send(void) 230ca1b7a7bSAndrew Jones { 231ca1b7a7bSAndrew Jones ipi_enable(); 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 { 241ac4a67b6SAndrew Jones ipi_enable(); 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"); 298c152d8bcSChristoffer Dall gic_enable_defaults(); 299c152d8bcSChristoffer Dall #ifdef __arm__ 300c152d8bcSChristoffer Dall install_exception_handler(EXCPTN_IRQ, ipi_clear_active_handler); 301c152d8bcSChristoffer Dall #else 302c152d8bcSChristoffer Dall install_irq_handler(EL1H_IRQ, ipi_clear_active_handler); 303c152d8bcSChristoffer Dall #endif 304c152d8bcSChristoffer Dall local_irq_enable(); 305c152d8bcSChristoffer Dall 306c152d8bcSChristoffer Dall ipi_test_self(); 307c152d8bcSChristoffer Dall report_prefix_pop(); 308c152d8bcSChristoffer Dall } 309c152d8bcSChristoffer Dall 31078ad7e95SAndre Przywara static bool test_ro_pattern_32(void *address, u32 pattern, u32 orig) 31178ad7e95SAndre Przywara { 31278ad7e95SAndre Przywara u32 reg; 31378ad7e95SAndre Przywara 31478ad7e95SAndre Przywara writel(pattern, address); 31578ad7e95SAndre Przywara reg = readl(address); 31678ad7e95SAndre Przywara 31778ad7e95SAndre Przywara if (reg != orig) 31878ad7e95SAndre Przywara writel(orig, address); 31978ad7e95SAndre Przywara 32078ad7e95SAndre Przywara return reg == orig; 32178ad7e95SAndre Przywara } 32278ad7e95SAndre Przywara 32378ad7e95SAndre Przywara static bool test_readonly_32(void *address, bool razwi) 32478ad7e95SAndre Przywara { 32578ad7e95SAndre Przywara u32 orig, pattern; 32678ad7e95SAndre Przywara 32778ad7e95SAndre Przywara orig = readl(address); 32878ad7e95SAndre Przywara if (razwi && orig) 32978ad7e95SAndre Przywara return false; 33078ad7e95SAndre Przywara 33178ad7e95SAndre Przywara pattern = 0xffffffff; 33278ad7e95SAndre Przywara if (orig != pattern) { 33378ad7e95SAndre Przywara if (!test_ro_pattern_32(address, pattern, orig)) 33478ad7e95SAndre Przywara return false; 33578ad7e95SAndre Przywara } 33678ad7e95SAndre Przywara 33778ad7e95SAndre Przywara pattern = 0xa5a55a5a; 33878ad7e95SAndre Przywara if (orig != pattern) { 33978ad7e95SAndre Przywara if (!test_ro_pattern_32(address, pattern, orig)) 34078ad7e95SAndre Przywara return false; 34178ad7e95SAndre Przywara } 34278ad7e95SAndre Przywara 34378ad7e95SAndre Przywara pattern = 0; 34478ad7e95SAndre Przywara if (orig != pattern) { 34578ad7e95SAndre Przywara if (!test_ro_pattern_32(address, pattern, orig)) 34678ad7e95SAndre Przywara return false; 34778ad7e95SAndre Przywara } 34878ad7e95SAndre Przywara 34978ad7e95SAndre Przywara return true; 35078ad7e95SAndre Przywara } 35178ad7e95SAndre Przywara 35278ad7e95SAndre Przywara static void test_typer_v2(uint32_t reg) 35378ad7e95SAndre Przywara { 35478ad7e95SAndre Przywara int nr_gic_cpus = ((reg >> 5) & 0x7) + 1; 35578ad7e95SAndre Przywara 3568e0a4f41SAndre Przywara report_info("nr_cpus=%d", nr_cpus); 357*a299895bSThomas Huth report(nr_cpus == nr_gic_cpus, "all CPUs have interrupts"); 35878ad7e95SAndre Przywara } 35978ad7e95SAndre Przywara 360ff31a1c4SAndre Przywara #define BYTE(reg32, byte) (((reg32) >> ((byte) * 8)) & 0xff) 361ff31a1c4SAndre Przywara #define REPLACE_BYTE(reg32, byte, new) (((reg32) & ~(0xff << ((byte) * 8))) |\ 362ff31a1c4SAndre Przywara ((new) << ((byte) * 8))) 363ff31a1c4SAndre Przywara 364ff31a1c4SAndre Przywara /* 365ff31a1c4SAndre Przywara * Some registers are byte accessible, do a byte-wide read and write of known 366ff31a1c4SAndre Przywara * content to check for this. 367ff31a1c4SAndre Przywara * Apply a @mask to cater for special register properties. 368ff31a1c4SAndre Przywara * @pattern contains the value already in the register. 369ff31a1c4SAndre Przywara */ 370ff31a1c4SAndre Przywara static void test_byte_access(void *base_addr, u32 pattern, u32 mask) 371ff31a1c4SAndre Przywara { 372ff31a1c4SAndre Przywara u32 reg = readb(base_addr + 1); 3738e0a4f41SAndre Przywara bool res; 374ff31a1c4SAndre Przywara 3758e0a4f41SAndre Przywara res = (reg == (BYTE(pattern, 1) & (mask >> 8))); 376*a299895bSThomas Huth report(res, "byte reads successful"); 3778e0a4f41SAndre Przywara if (!res) 3788e0a4f41SAndre Przywara report_info("byte 1 of 0x%08x => 0x%02x", pattern & mask, reg); 379ff31a1c4SAndre Przywara 380ff31a1c4SAndre Przywara pattern = REPLACE_BYTE(pattern, 2, 0x1f); 381ff31a1c4SAndre Przywara writeb(BYTE(pattern, 2), base_addr + 2); 382ff31a1c4SAndre Przywara reg = readl(base_addr); 3838e0a4f41SAndre Przywara res = (reg == (pattern & mask)); 384*a299895bSThomas Huth report(res, "byte writes successful"); 3858e0a4f41SAndre Przywara if (!res) 3868e0a4f41SAndre Przywara report_info("writing 0x%02x into bytes 2 => 0x%08x", 3878e0a4f41SAndre Przywara BYTE(pattern, 2), reg); 388ff31a1c4SAndre Przywara } 389ff31a1c4SAndre Przywara 390ff31a1c4SAndre Przywara static void test_priorities(int nr_irqs, void *priptr) 391ff31a1c4SAndre Przywara { 392ff31a1c4SAndre Przywara u32 orig_prio, reg, pri_bits; 393ff31a1c4SAndre Przywara u32 pri_mask, pattern; 394ff31a1c4SAndre Przywara void *first_spi = priptr + GIC_FIRST_SPI; 395ff31a1c4SAndre Przywara 396ff31a1c4SAndre Przywara orig_prio = readl(first_spi); 397ff31a1c4SAndre Przywara report_prefix_push("IPRIORITYR"); 398ff31a1c4SAndre Przywara 399ff31a1c4SAndre Przywara /* 400ff31a1c4SAndre Przywara * Determine implemented number of priority bits by writing all 1's 401ff31a1c4SAndre Przywara * and checking the number of cleared bits in the value read back. 402ff31a1c4SAndre Przywara */ 403ff31a1c4SAndre Przywara writel(0xffffffff, first_spi); 404ff31a1c4SAndre Przywara pri_mask = readl(first_spi); 405ff31a1c4SAndre Przywara 406ff31a1c4SAndre Przywara reg = ~pri_mask; 407*a299895bSThomas Huth report((((reg >> 16) == (reg & 0xffff)) && 408*a299895bSThomas Huth ((reg & 0xff) == ((reg >> 8) & 0xff))), 409*a299895bSThomas Huth "consistent priority masking"); 4108e0a4f41SAndre Przywara report_info("priority mask is 0x%08x", pri_mask); 411ff31a1c4SAndre Przywara 412ff31a1c4SAndre Przywara reg = reg & 0xff; 413ff31a1c4SAndre Przywara for (pri_bits = 8; reg & 1; reg >>= 1, pri_bits--) 414ff31a1c4SAndre Przywara ; 415*a299895bSThomas Huth report(pri_bits >= 4, "implements at least 4 priority bits"); 4168e0a4f41SAndre Przywara report_info("%d priority bits implemented", pri_bits); 417ff31a1c4SAndre Przywara 418ff31a1c4SAndre Przywara pattern = 0; 419ff31a1c4SAndre Przywara writel(pattern, first_spi); 420*a299895bSThomas Huth report(readl(first_spi) == pattern, "clearing priorities"); 421ff31a1c4SAndre Przywara 422ff31a1c4SAndre Przywara /* setting all priorities to their max valus was tested above */ 423ff31a1c4SAndre Przywara 424*a299895bSThomas Huth report(test_readonly_32(priptr + nr_irqs, true), 425*a299895bSThomas Huth "accesses beyond limit RAZ/WI"); 426ff31a1c4SAndre Przywara 427ff31a1c4SAndre Przywara writel(pattern, priptr + nr_irqs - 4); 428*a299895bSThomas Huth report(readl(priptr + nr_irqs - 4) == (pattern & pri_mask), 429*a299895bSThomas Huth "accessing last SPIs"); 430ff31a1c4SAndre Przywara 431ff31a1c4SAndre Przywara pattern = 0xff7fbf3f; 432ff31a1c4SAndre Przywara writel(pattern, first_spi); 433*a299895bSThomas Huth report(readl(first_spi) == (pattern & pri_mask), 434*a299895bSThomas Huth "priorities are preserved"); 435ff31a1c4SAndre Przywara 436ff31a1c4SAndre Przywara /* The PRIORITY registers are byte accessible. */ 437ff31a1c4SAndre Przywara test_byte_access(first_spi, pattern, pri_mask); 438ff31a1c4SAndre Przywara 439ff31a1c4SAndre Przywara report_prefix_pop(); 440ff31a1c4SAndre Przywara writel(orig_prio, first_spi); 441ff31a1c4SAndre Przywara } 442ff31a1c4SAndre Przywara 443fe572a5eSAndre Przywara /* GICD_ITARGETSR is only used by GICv2. */ 444fe572a5eSAndre Przywara static void test_targets(int nr_irqs) 445fe572a5eSAndre Przywara { 446fe572a5eSAndre Przywara void *targetsptr = gicv2_dist_base() + GICD_ITARGETSR; 447fe572a5eSAndre Przywara u32 orig_targets; 448fe572a5eSAndre Przywara u32 cpu_mask; 449fe572a5eSAndre Przywara u32 pattern, reg; 450fe572a5eSAndre Przywara 451fe572a5eSAndre Przywara orig_targets = readl(targetsptr + GIC_FIRST_SPI); 452fe572a5eSAndre Przywara report_prefix_push("ITARGETSR"); 453fe572a5eSAndre Przywara 454fe572a5eSAndre Przywara cpu_mask = (1 << nr_cpus) - 1; 455fe572a5eSAndre Przywara cpu_mask |= cpu_mask << 8; 456fe572a5eSAndre Przywara cpu_mask |= cpu_mask << 16; 457fe572a5eSAndre Przywara 458fe572a5eSAndre Przywara /* Check that bits for non implemented CPUs are RAZ/WI. */ 459fe572a5eSAndre Przywara if (nr_cpus < 8) { 460fe572a5eSAndre Przywara writel(0xffffffff, targetsptr + GIC_FIRST_SPI); 461*a299895bSThomas Huth report(!(readl(targetsptr + GIC_FIRST_SPI) & ~cpu_mask), 462*a299895bSThomas Huth "bits for non-existent CPUs masked"); 4638e0a4f41SAndre Przywara report_info("%d non-existent CPUs", 8 - nr_cpus); 464fe572a5eSAndre Przywara } else { 465fe572a5eSAndre Przywara report_skip("CPU masking (all CPUs implemented)"); 466fe572a5eSAndre Przywara } 467fe572a5eSAndre Przywara 468*a299895bSThomas Huth report(test_readonly_32(targetsptr + nr_irqs, true), 469*a299895bSThomas Huth "accesses beyond limit RAZ/WI"); 470fe572a5eSAndre Przywara 471fe572a5eSAndre Przywara pattern = 0x0103020f; 472fe572a5eSAndre Przywara writel(pattern, targetsptr + GIC_FIRST_SPI); 473fe572a5eSAndre Przywara reg = readl(targetsptr + GIC_FIRST_SPI); 474*a299895bSThomas Huth report(reg == (pattern & cpu_mask), "register content preserved"); 4758e0a4f41SAndre Przywara if (reg != (pattern & cpu_mask)) 4768e0a4f41SAndre Przywara report_info("writing %08x reads back as %08x", 4778e0a4f41SAndre Przywara pattern & cpu_mask, reg); 478fe572a5eSAndre Przywara 479fe572a5eSAndre Przywara /* The TARGETS registers are byte accessible. */ 480fe572a5eSAndre Przywara test_byte_access(targetsptr + GIC_FIRST_SPI, pattern, cpu_mask); 481fe572a5eSAndre Przywara 482fe572a5eSAndre Przywara writel(orig_targets, targetsptr + GIC_FIRST_SPI); 483da5b8576SAndre Przywara 484da5b8576SAndre Przywara report_prefix_pop(); 485fe572a5eSAndre Przywara } 486fe572a5eSAndre Przywara 48778ad7e95SAndre Przywara static void gic_test_mmio(void) 48878ad7e95SAndre Przywara { 48978ad7e95SAndre Przywara u32 reg; 49078ad7e95SAndre Przywara int nr_irqs; 49178ad7e95SAndre Przywara void *gic_dist_base, *idreg; 49278ad7e95SAndre Przywara 49378ad7e95SAndre Przywara switch(gic_version()) { 49478ad7e95SAndre Przywara case 0x2: 49578ad7e95SAndre Przywara gic_dist_base = gicv2_dist_base(); 49678ad7e95SAndre Przywara idreg = gic_dist_base + GICD_ICPIDR2; 49778ad7e95SAndre Przywara break; 49878ad7e95SAndre Przywara case 0x3: 49978ad7e95SAndre Przywara report_abort("GICv3 MMIO tests NYI"); 50078ad7e95SAndre Przywara default: 50178ad7e95SAndre Przywara report_abort("GIC version %d not supported", gic_version()); 50278ad7e95SAndre Przywara } 50378ad7e95SAndre Przywara 50478ad7e95SAndre Przywara reg = readl(gic_dist_base + GICD_TYPER); 50578ad7e95SAndre Przywara nr_irqs = GICD_TYPER_IRQS(reg); 50678ad7e95SAndre Przywara report_info("number of implemented SPIs: %d", nr_irqs - GIC_FIRST_SPI); 50778ad7e95SAndre Przywara 50878ad7e95SAndre Przywara test_typer_v2(reg); 50978ad7e95SAndre Przywara 51078ad7e95SAndre Przywara report_info("IIDR: 0x%08x", readl(gic_dist_base + GICD_IIDR)); 51178ad7e95SAndre Przywara 512*a299895bSThomas Huth report(test_readonly_32(gic_dist_base + GICD_TYPER, false), 513*a299895bSThomas Huth "GICD_TYPER is read-only"); 514*a299895bSThomas Huth report(test_readonly_32(gic_dist_base + GICD_IIDR, false), 515*a299895bSThomas Huth "GICD_IIDR is read-only"); 51678ad7e95SAndre Przywara 51778ad7e95SAndre Przywara reg = readl(idreg); 518*a299895bSThomas Huth report(test_readonly_32(idreg, false), "ICPIDR2 is read-only"); 5198e0a4f41SAndre Przywara report_info("value of ICPIDR2: 0x%08x", reg); 520ff31a1c4SAndre Przywara 521ff31a1c4SAndre Przywara test_priorities(nr_irqs, gic_dist_base + GICD_IPRIORITYR); 522fe572a5eSAndre Przywara 523fe572a5eSAndre Przywara if (gic_version() == 2) 524fe572a5eSAndre Przywara test_targets(nr_irqs); 52578ad7e95SAndre Przywara } 52678ad7e95SAndre Przywara 527ac4a67b6SAndrew Jones int main(int argc, char **argv) 528ac4a67b6SAndrew Jones { 5292e2d471dSAndrew Jones if (!gic_init()) { 530ac4a67b6SAndrew Jones printf("No supported gic present, skipping tests...\n"); 531ac4a67b6SAndrew Jones return report_summary(); 532ac4a67b6SAndrew Jones } 533ac4a67b6SAndrew Jones 5342b19b829SAndrew Jones report_prefix_pushf("gicv%d", gic_version()); 535ac4a67b6SAndrew Jones 5362e2d471dSAndrew Jones switch (gic_version()) { 5372e2d471dSAndrew Jones case 2: 5382e2d471dSAndrew Jones gic = &gicv2; 5392e2d471dSAndrew Jones break; 5402e2d471dSAndrew Jones case 3: 5412e2d471dSAndrew Jones gic = &gicv3; 5422e2d471dSAndrew Jones break; 5432e2d471dSAndrew Jones } 5442e2d471dSAndrew Jones 545ac4a67b6SAndrew Jones if (argc < 2) 546ac4a67b6SAndrew Jones report_abort("no test specified"); 547ac4a67b6SAndrew Jones 548ac4a67b6SAndrew Jones if (strcmp(argv[1], "ipi") == 0) { 549ac4a67b6SAndrew Jones report_prefix_push(argv[1]); 550ac4a67b6SAndrew Jones nr_cpu_check(2); 55100b34f56SAndrew Jones on_cpus(ipi_test, NULL); 552c152d8bcSChristoffer Dall } else if (strcmp(argv[1], "active") == 0) { 553c152d8bcSChristoffer Dall run_active_clear_test(); 55478ad7e95SAndre Przywara } else if (strcmp(argv[1], "mmio") == 0) { 55578ad7e95SAndre Przywara report_prefix_push(argv[1]); 55678ad7e95SAndre Przywara gic_test_mmio(); 55778ad7e95SAndre Przywara report_prefix_pop(); 558ac4a67b6SAndrew Jones } else { 559ac4a67b6SAndrew Jones report_abort("Unknown subtest '%s'", argv[1]); 560ac4a67b6SAndrew Jones } 561ac4a67b6SAndrew Jones 562ac4a67b6SAndrew Jones return report_summary(); 563ac4a67b6SAndrew Jones } 564