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> 19*ba74b106SEric Auger #include <asm/gic-v3-its.h> 20ac4a67b6SAndrew Jones #include <asm/smp.h> 21ac4a67b6SAndrew Jones #include <asm/barrier.h> 22ac4a67b6SAndrew Jones #include <asm/io.h> 23ac4a67b6SAndrew Jones 24ca1b7a7bSAndrew Jones #define IPI_SENDER 1 25ca1b7a7bSAndrew Jones #define IPI_IRQ 1 26ca1b7a7bSAndrew Jones 272e2d471dSAndrew Jones struct gic { 282e2d471dSAndrew Jones struct { 292e2d471dSAndrew Jones void (*send_self)(void); 302e2d471dSAndrew Jones void (*send_broadcast)(void); 312e2d471dSAndrew Jones } ipi; 322e2d471dSAndrew Jones }; 332e2d471dSAndrew Jones 342e2d471dSAndrew Jones static struct gic *gic; 35ac4a67b6SAndrew Jones static int acked[NR_CPUS], spurious[NR_CPUS]; 36ca1b7a7bSAndrew Jones static int bad_sender[NR_CPUS], bad_irq[NR_CPUS]; 37ac4a67b6SAndrew Jones static cpumask_t ready; 38ac4a67b6SAndrew Jones 39ac4a67b6SAndrew Jones static void nr_cpu_check(int nr) 40ac4a67b6SAndrew Jones { 41ac4a67b6SAndrew Jones if (nr_cpus < nr) 42ac4a67b6SAndrew Jones report_abort("At least %d cpus required", nr); 43ac4a67b6SAndrew Jones } 44ac4a67b6SAndrew Jones 45ac4a67b6SAndrew Jones static void wait_on_ready(void) 46ac4a67b6SAndrew Jones { 47ac4a67b6SAndrew Jones cpumask_set_cpu(smp_processor_id(), &ready); 48ac4a67b6SAndrew Jones while (!cpumask_full(&ready)) 49ac4a67b6SAndrew Jones cpu_relax(); 50ac4a67b6SAndrew Jones } 51ac4a67b6SAndrew Jones 52ca1b7a7bSAndrew Jones static void stats_reset(void) 53ca1b7a7bSAndrew Jones { 54ca1b7a7bSAndrew Jones int i; 55ca1b7a7bSAndrew Jones 56ca1b7a7bSAndrew Jones for (i = 0; i < nr_cpus; ++i) { 57ca1b7a7bSAndrew Jones acked[i] = 0; 58ca1b7a7bSAndrew Jones bad_sender[i] = -1; 59ca1b7a7bSAndrew Jones bad_irq[i] = -1; 60ca1b7a7bSAndrew Jones } 61ca1b7a7bSAndrew Jones smp_wmb(); 62ca1b7a7bSAndrew Jones } 63ca1b7a7bSAndrew Jones 6496edb026SAndre Przywara static void check_acked(const char *testname, cpumask_t *mask) 65ac4a67b6SAndrew Jones { 66ac4a67b6SAndrew Jones int missing = 0, extra = 0, unexpected = 0; 67ac4a67b6SAndrew Jones int nr_pass, cpu, i; 68ca1b7a7bSAndrew Jones bool bad = false; 69ac4a67b6SAndrew Jones 70ac4a67b6SAndrew Jones /* Wait up to 5s for all interrupts to be delivered */ 71ac4a67b6SAndrew Jones for (i = 0; i < 50; ++i) { 72ac4a67b6SAndrew Jones mdelay(100); 73ac4a67b6SAndrew Jones nr_pass = 0; 74ac4a67b6SAndrew Jones for_each_present_cpu(cpu) { 75ac4a67b6SAndrew Jones smp_rmb(); 76ac4a67b6SAndrew Jones nr_pass += cpumask_test_cpu(cpu, mask) ? 77ac4a67b6SAndrew Jones acked[cpu] == 1 : acked[cpu] == 0; 78ca1b7a7bSAndrew Jones 79ca1b7a7bSAndrew Jones if (bad_sender[cpu] != -1) { 80ca1b7a7bSAndrew Jones printf("cpu%d received IPI from wrong sender %d\n", 81ca1b7a7bSAndrew Jones cpu, bad_sender[cpu]); 82ca1b7a7bSAndrew Jones bad = true; 83ca1b7a7bSAndrew Jones } 84ca1b7a7bSAndrew Jones 85ca1b7a7bSAndrew Jones if (bad_irq[cpu] != -1) { 86ca1b7a7bSAndrew Jones printf("cpu%d received wrong irq %d\n", 87ca1b7a7bSAndrew Jones cpu, bad_irq[cpu]); 88ca1b7a7bSAndrew Jones bad = true; 89ca1b7a7bSAndrew Jones } 90ac4a67b6SAndrew Jones } 91ac4a67b6SAndrew Jones if (nr_pass == nr_cpus) { 92a299895bSThomas Huth report(!bad, "%s", testname); 9396edb026SAndre Przywara if (i) 9496edb026SAndre Przywara report_info("took more than %d ms", i * 100); 95ac4a67b6SAndrew Jones return; 96ac4a67b6SAndrew Jones } 97ac4a67b6SAndrew Jones } 98ac4a67b6SAndrew Jones 99ac4a67b6SAndrew Jones for_each_present_cpu(cpu) { 100ac4a67b6SAndrew Jones if (cpumask_test_cpu(cpu, mask)) { 101ac4a67b6SAndrew Jones if (!acked[cpu]) 102ac4a67b6SAndrew Jones ++missing; 103ac4a67b6SAndrew Jones else if (acked[cpu] > 1) 104ac4a67b6SAndrew Jones ++extra; 105ac4a67b6SAndrew Jones } else { 106ac4a67b6SAndrew Jones if (acked[cpu]) 107ac4a67b6SAndrew Jones ++unexpected; 108ac4a67b6SAndrew Jones } 109ac4a67b6SAndrew Jones } 110ac4a67b6SAndrew Jones 111a299895bSThomas Huth report(false, "%s", testname); 11296edb026SAndre Przywara report_info("Timed-out (5s). ACKS: missing=%d extra=%d unexpected=%d", 11396edb026SAndre Przywara missing, extra, unexpected); 114ac4a67b6SAndrew Jones } 115ac4a67b6SAndrew Jones 116ac4a67b6SAndrew Jones static void check_spurious(void) 117ac4a67b6SAndrew Jones { 118ac4a67b6SAndrew Jones int cpu; 119ac4a67b6SAndrew Jones 120ac4a67b6SAndrew Jones smp_rmb(); 121ac4a67b6SAndrew Jones for_each_present_cpu(cpu) { 122ac4a67b6SAndrew Jones if (spurious[cpu]) 123ac4a67b6SAndrew Jones report_info("WARN: cpu%d got %d spurious interrupts", 124ac4a67b6SAndrew Jones cpu, spurious[cpu]); 125ac4a67b6SAndrew Jones } 126ac4a67b6SAndrew Jones } 127ac4a67b6SAndrew Jones 128ca1b7a7bSAndrew Jones static void check_ipi_sender(u32 irqstat) 129ca1b7a7bSAndrew Jones { 130ca1b7a7bSAndrew Jones if (gic_version() == 2) { 131ca1b7a7bSAndrew Jones int src = (irqstat >> 10) & 7; 132ca1b7a7bSAndrew Jones 133ca1b7a7bSAndrew Jones if (src != IPI_SENDER) 134ca1b7a7bSAndrew Jones bad_sender[smp_processor_id()] = src; 135ca1b7a7bSAndrew Jones } 136ca1b7a7bSAndrew Jones } 137ca1b7a7bSAndrew Jones 138ca1b7a7bSAndrew Jones static void check_irqnr(u32 irqnr) 139ca1b7a7bSAndrew Jones { 140ca1b7a7bSAndrew Jones if (irqnr != IPI_IRQ) 141ca1b7a7bSAndrew Jones bad_irq[smp_processor_id()] = irqnr; 142ca1b7a7bSAndrew Jones } 143ca1b7a7bSAndrew Jones 144ac4a67b6SAndrew Jones static void ipi_handler(struct pt_regs *regs __unused) 145ac4a67b6SAndrew Jones { 1462e2d471dSAndrew Jones u32 irqstat = gic_read_iar(); 1472e2d471dSAndrew Jones u32 irqnr = gic_iar_irqnr(irqstat); 148ac4a67b6SAndrew Jones 149ac4a67b6SAndrew Jones if (irqnr != GICC_INT_SPURIOUS) { 1502e2d471dSAndrew Jones gic_write_eoir(irqstat); 151ca1b7a7bSAndrew Jones smp_rmb(); /* pairs with wmb in stats_reset */ 152ac4a67b6SAndrew Jones ++acked[smp_processor_id()]; 153ca1b7a7bSAndrew Jones check_ipi_sender(irqstat); 154ca1b7a7bSAndrew Jones check_irqnr(irqnr); 155ac4a67b6SAndrew Jones smp_wmb(); /* pairs with rmb in check_acked */ 156ac4a67b6SAndrew Jones } else { 157ac4a67b6SAndrew Jones ++spurious[smp_processor_id()]; 158ac4a67b6SAndrew Jones smp_wmb(); 159ac4a67b6SAndrew Jones } 160ac4a67b6SAndrew Jones } 161ac4a67b6SAndrew Jones 1622e2d471dSAndrew Jones static void gicv2_ipi_send_self(void) 1632e2d471dSAndrew Jones { 164ca1b7a7bSAndrew Jones writel(2 << 24 | IPI_IRQ, gicv2_dist_base() + GICD_SGIR); 1652e2d471dSAndrew Jones } 1662e2d471dSAndrew Jones 1672e2d471dSAndrew Jones static void gicv2_ipi_send_broadcast(void) 1682e2d471dSAndrew Jones { 169ca1b7a7bSAndrew Jones writel(1 << 24 | IPI_IRQ, gicv2_dist_base() + GICD_SGIR); 1702e2d471dSAndrew Jones } 1712e2d471dSAndrew Jones 1722e2d471dSAndrew Jones static void gicv3_ipi_send_self(void) 1732e2d471dSAndrew Jones { 174ca1b7a7bSAndrew Jones gic_ipi_send_single(IPI_IRQ, smp_processor_id()); 1752e2d471dSAndrew Jones } 1762e2d471dSAndrew Jones 1772e2d471dSAndrew Jones static void gicv3_ipi_send_broadcast(void) 1782e2d471dSAndrew Jones { 179ca1b7a7bSAndrew Jones gicv3_write_sgi1r(1ULL << 40 | IPI_IRQ << 24); 1802e2d471dSAndrew Jones isb(); 1812e2d471dSAndrew Jones } 1822e2d471dSAndrew Jones 183ac4a67b6SAndrew Jones static void ipi_test_self(void) 184ac4a67b6SAndrew Jones { 185ac4a67b6SAndrew Jones cpumask_t mask; 186ac4a67b6SAndrew Jones 187ac4a67b6SAndrew Jones report_prefix_push("self"); 188ca1b7a7bSAndrew Jones stats_reset(); 189ac4a67b6SAndrew Jones cpumask_clear(&mask); 190ca1b7a7bSAndrew Jones cpumask_set_cpu(smp_processor_id(), &mask); 1912e2d471dSAndrew Jones gic->ipi.send_self(); 19296edb026SAndre Przywara check_acked("IPI: self", &mask); 193ac4a67b6SAndrew Jones report_prefix_pop(); 194ac4a67b6SAndrew Jones } 195ac4a67b6SAndrew Jones 196ac4a67b6SAndrew Jones static void ipi_test_smp(void) 197ac4a67b6SAndrew Jones { 198ac4a67b6SAndrew Jones cpumask_t mask; 1992e2d471dSAndrew Jones int i; 200ac4a67b6SAndrew Jones 201ac4a67b6SAndrew Jones report_prefix_push("target-list"); 202ca1b7a7bSAndrew Jones stats_reset(); 2032e2d471dSAndrew Jones cpumask_copy(&mask, &cpu_present_mask); 204ca1b7a7bSAndrew Jones for (i = smp_processor_id() & 1; i < nr_cpus; i += 2) 2052e2d471dSAndrew Jones cpumask_clear_cpu(i, &mask); 206ca1b7a7bSAndrew Jones gic_ipi_send_mask(IPI_IRQ, &mask); 20796edb026SAndre Przywara check_acked("IPI: directed", &mask); 208ac4a67b6SAndrew Jones report_prefix_pop(); 209ac4a67b6SAndrew Jones 210ac4a67b6SAndrew Jones report_prefix_push("broadcast"); 211ca1b7a7bSAndrew Jones stats_reset(); 212ac4a67b6SAndrew Jones cpumask_copy(&mask, &cpu_present_mask); 213ca1b7a7bSAndrew Jones cpumask_clear_cpu(smp_processor_id(), &mask); 2142e2d471dSAndrew Jones gic->ipi.send_broadcast(); 21596edb026SAndre Przywara check_acked("IPI: broadcast", &mask); 216ac4a67b6SAndrew Jones report_prefix_pop(); 217ac4a67b6SAndrew Jones } 218ac4a67b6SAndrew Jones 21925f66327SEric Auger static void setup_irq(irq_handler_fn handler) 220ac4a67b6SAndrew Jones { 2212e2d471dSAndrew Jones gic_enable_defaults(); 222ac4a67b6SAndrew Jones #ifdef __arm__ 22325f66327SEric Auger install_exception_handler(EXCPTN_IRQ, handler); 224ac4a67b6SAndrew Jones #else 22525f66327SEric Auger install_irq_handler(EL1H_IRQ, handler); 226ac4a67b6SAndrew Jones #endif 227ac4a67b6SAndrew Jones local_irq_enable(); 228ac4a67b6SAndrew Jones } 229ac4a67b6SAndrew Jones 230ca1b7a7bSAndrew Jones static void ipi_send(void) 231ca1b7a7bSAndrew Jones { 23225f66327SEric Auger setup_irq(ipi_handler); 233ca1b7a7bSAndrew Jones wait_on_ready(); 234ca1b7a7bSAndrew Jones ipi_test_self(); 235ca1b7a7bSAndrew Jones ipi_test_smp(); 236ca1b7a7bSAndrew Jones check_spurious(); 237ca1b7a7bSAndrew Jones exit(report_summary()); 238ca1b7a7bSAndrew Jones } 239ca1b7a7bSAndrew Jones 240ac4a67b6SAndrew Jones static void ipi_recv(void) 241ac4a67b6SAndrew Jones { 24225f66327SEric Auger setup_irq(ipi_handler); 243ac4a67b6SAndrew Jones cpumask_set_cpu(smp_processor_id(), &ready); 244ac4a67b6SAndrew Jones while (1) 245ac4a67b6SAndrew Jones wfi(); 246ac4a67b6SAndrew Jones } 247ac4a67b6SAndrew Jones 24800b34f56SAndrew Jones static void ipi_test(void *data __unused) 249bfd500b4SAndrew Jones { 250bfd500b4SAndrew Jones if (smp_processor_id() == IPI_SENDER) 251bfd500b4SAndrew Jones ipi_send(); 252bfd500b4SAndrew Jones else 253bfd500b4SAndrew Jones ipi_recv(); 254bfd500b4SAndrew Jones } 255bfd500b4SAndrew Jones 2562e2d471dSAndrew Jones static struct gic gicv2 = { 2572e2d471dSAndrew Jones .ipi = { 2582e2d471dSAndrew Jones .send_self = gicv2_ipi_send_self, 2592e2d471dSAndrew Jones .send_broadcast = gicv2_ipi_send_broadcast, 2602e2d471dSAndrew Jones }, 2612e2d471dSAndrew Jones }; 2622e2d471dSAndrew Jones 2632e2d471dSAndrew Jones static struct gic gicv3 = { 2642e2d471dSAndrew Jones .ipi = { 2652e2d471dSAndrew Jones .send_self = gicv3_ipi_send_self, 2662e2d471dSAndrew Jones .send_broadcast = gicv3_ipi_send_broadcast, 2672e2d471dSAndrew Jones }, 2682e2d471dSAndrew Jones }; 2692e2d471dSAndrew Jones 270c152d8bcSChristoffer Dall static void ipi_clear_active_handler(struct pt_regs *regs __unused) 271c152d8bcSChristoffer Dall { 272c152d8bcSChristoffer Dall u32 irqstat = gic_read_iar(); 273c152d8bcSChristoffer Dall u32 irqnr = gic_iar_irqnr(irqstat); 274c152d8bcSChristoffer Dall 275c152d8bcSChristoffer Dall if (irqnr != GICC_INT_SPURIOUS) { 276c152d8bcSChristoffer Dall void *base; 277c152d8bcSChristoffer Dall u32 val = 1 << IPI_IRQ; 278c152d8bcSChristoffer Dall 279c152d8bcSChristoffer Dall if (gic_version() == 2) 280c152d8bcSChristoffer Dall base = gicv2_dist_base(); 281c152d8bcSChristoffer Dall else 2826d4d7c4bSAndrew Jones base = gicv3_sgi_base(); 283c152d8bcSChristoffer Dall 284c152d8bcSChristoffer Dall writel(val, base + GICD_ICACTIVER); 285c152d8bcSChristoffer Dall 286c152d8bcSChristoffer Dall smp_rmb(); /* pairs with wmb in stats_reset */ 287c152d8bcSChristoffer Dall ++acked[smp_processor_id()]; 288c152d8bcSChristoffer Dall check_irqnr(irqnr); 289c152d8bcSChristoffer Dall smp_wmb(); /* pairs with rmb in check_acked */ 290c152d8bcSChristoffer Dall } else { 291c152d8bcSChristoffer Dall ++spurious[smp_processor_id()]; 292c152d8bcSChristoffer Dall smp_wmb(); 293c152d8bcSChristoffer Dall } 294c152d8bcSChristoffer Dall } 295c152d8bcSChristoffer Dall 296c152d8bcSChristoffer Dall static void run_active_clear_test(void) 297c152d8bcSChristoffer Dall { 298c152d8bcSChristoffer Dall report_prefix_push("active"); 29925f66327SEric Auger setup_irq(ipi_clear_active_handler); 300c152d8bcSChristoffer Dall ipi_test_self(); 301c152d8bcSChristoffer Dall report_prefix_pop(); 302c152d8bcSChristoffer Dall } 303c152d8bcSChristoffer Dall 30478ad7e95SAndre Przywara static bool test_ro_pattern_32(void *address, u32 pattern, u32 orig) 30578ad7e95SAndre Przywara { 30678ad7e95SAndre Przywara u32 reg; 30778ad7e95SAndre Przywara 30878ad7e95SAndre Przywara writel(pattern, address); 30978ad7e95SAndre Przywara reg = readl(address); 31078ad7e95SAndre Przywara 31178ad7e95SAndre Przywara if (reg != orig) 31278ad7e95SAndre Przywara writel(orig, address); 31378ad7e95SAndre Przywara 31478ad7e95SAndre Przywara return reg == orig; 31578ad7e95SAndre Przywara } 31678ad7e95SAndre Przywara 31778ad7e95SAndre Przywara static bool test_readonly_32(void *address, bool razwi) 31878ad7e95SAndre Przywara { 31978ad7e95SAndre Przywara u32 orig, pattern; 32078ad7e95SAndre Przywara 32178ad7e95SAndre Przywara orig = readl(address); 32278ad7e95SAndre Przywara if (razwi && orig) 32378ad7e95SAndre Przywara return false; 32478ad7e95SAndre Przywara 32578ad7e95SAndre Przywara pattern = 0xffffffff; 32678ad7e95SAndre Przywara if (orig != pattern) { 32778ad7e95SAndre Przywara if (!test_ro_pattern_32(address, pattern, orig)) 32878ad7e95SAndre Przywara return false; 32978ad7e95SAndre Przywara } 33078ad7e95SAndre Przywara 33178ad7e95SAndre Przywara pattern = 0xa5a55a5a; 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 = 0; 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 return true; 34478ad7e95SAndre Przywara } 34578ad7e95SAndre Przywara 34678ad7e95SAndre Przywara static void test_typer_v2(uint32_t reg) 34778ad7e95SAndre Przywara { 34878ad7e95SAndre Przywara int nr_gic_cpus = ((reg >> 5) & 0x7) + 1; 34978ad7e95SAndre Przywara 3508e0a4f41SAndre Przywara report_info("nr_cpus=%d", nr_cpus); 351a299895bSThomas Huth report(nr_cpus == nr_gic_cpus, "all CPUs have interrupts"); 35278ad7e95SAndre Przywara } 35378ad7e95SAndre Przywara 354ff31a1c4SAndre Przywara #define BYTE(reg32, byte) (((reg32) >> ((byte) * 8)) & 0xff) 355ff31a1c4SAndre Przywara #define REPLACE_BYTE(reg32, byte, new) (((reg32) & ~(0xff << ((byte) * 8))) |\ 356ff31a1c4SAndre Przywara ((new) << ((byte) * 8))) 357ff31a1c4SAndre Przywara 358ff31a1c4SAndre Przywara /* 359ff31a1c4SAndre Przywara * Some registers are byte accessible, do a byte-wide read and write of known 360ff31a1c4SAndre Przywara * content to check for this. 361ff31a1c4SAndre Przywara * Apply a @mask to cater for special register properties. 362ff31a1c4SAndre Przywara * @pattern contains the value already in the register. 363ff31a1c4SAndre Przywara */ 364ff31a1c4SAndre Przywara static void test_byte_access(void *base_addr, u32 pattern, u32 mask) 365ff31a1c4SAndre Przywara { 366ff31a1c4SAndre Przywara u32 reg = readb(base_addr + 1); 3678e0a4f41SAndre Przywara bool res; 368ff31a1c4SAndre Przywara 3698e0a4f41SAndre Przywara res = (reg == (BYTE(pattern, 1) & (mask >> 8))); 370a299895bSThomas Huth report(res, "byte reads successful"); 3718e0a4f41SAndre Przywara if (!res) 3728e0a4f41SAndre Przywara report_info("byte 1 of 0x%08x => 0x%02x", pattern & mask, reg); 373ff31a1c4SAndre Przywara 374ff31a1c4SAndre Przywara pattern = REPLACE_BYTE(pattern, 2, 0x1f); 375ff31a1c4SAndre Przywara writeb(BYTE(pattern, 2), base_addr + 2); 376ff31a1c4SAndre Przywara reg = readl(base_addr); 3778e0a4f41SAndre Przywara res = (reg == (pattern & mask)); 378a299895bSThomas Huth report(res, "byte writes successful"); 3798e0a4f41SAndre Przywara if (!res) 3808e0a4f41SAndre Przywara report_info("writing 0x%02x into bytes 2 => 0x%08x", 3818e0a4f41SAndre Przywara BYTE(pattern, 2), reg); 382ff31a1c4SAndre Przywara } 383ff31a1c4SAndre Przywara 384ff31a1c4SAndre Przywara static void test_priorities(int nr_irqs, void *priptr) 385ff31a1c4SAndre Przywara { 386ff31a1c4SAndre Przywara u32 orig_prio, reg, pri_bits; 387ff31a1c4SAndre Przywara u32 pri_mask, pattern; 388ff31a1c4SAndre Przywara void *first_spi = priptr + GIC_FIRST_SPI; 389ff31a1c4SAndre Przywara 390ff31a1c4SAndre Przywara orig_prio = readl(first_spi); 391ff31a1c4SAndre Przywara report_prefix_push("IPRIORITYR"); 392ff31a1c4SAndre Przywara 393ff31a1c4SAndre Przywara /* 394ff31a1c4SAndre Przywara * Determine implemented number of priority bits by writing all 1's 395ff31a1c4SAndre Przywara * and checking the number of cleared bits in the value read back. 396ff31a1c4SAndre Przywara */ 397ff31a1c4SAndre Przywara writel(0xffffffff, first_spi); 398ff31a1c4SAndre Przywara pri_mask = readl(first_spi); 399ff31a1c4SAndre Przywara 400ff31a1c4SAndre Przywara reg = ~pri_mask; 401a299895bSThomas Huth report((((reg >> 16) == (reg & 0xffff)) && 402a299895bSThomas Huth ((reg & 0xff) == ((reg >> 8) & 0xff))), 403a299895bSThomas Huth "consistent priority masking"); 4048e0a4f41SAndre Przywara report_info("priority mask is 0x%08x", pri_mask); 405ff31a1c4SAndre Przywara 406ff31a1c4SAndre Przywara reg = reg & 0xff; 407ff31a1c4SAndre Przywara for (pri_bits = 8; reg & 1; reg >>= 1, pri_bits--) 408ff31a1c4SAndre Przywara ; 409a299895bSThomas Huth report(pri_bits >= 4, "implements at least 4 priority bits"); 4108e0a4f41SAndre Przywara report_info("%d priority bits implemented", pri_bits); 411ff31a1c4SAndre Przywara 412ff31a1c4SAndre Przywara pattern = 0; 413ff31a1c4SAndre Przywara writel(pattern, first_spi); 414a299895bSThomas Huth report(readl(first_spi) == pattern, "clearing priorities"); 415ff31a1c4SAndre Przywara 416ff31a1c4SAndre Przywara /* setting all priorities to their max valus was tested above */ 417ff31a1c4SAndre Przywara 418a299895bSThomas Huth report(test_readonly_32(priptr + nr_irqs, true), 419a299895bSThomas Huth "accesses beyond limit RAZ/WI"); 420ff31a1c4SAndre Przywara 421ff31a1c4SAndre Przywara writel(pattern, priptr + nr_irqs - 4); 422a299895bSThomas Huth report(readl(priptr + nr_irqs - 4) == (pattern & pri_mask), 423a299895bSThomas Huth "accessing last SPIs"); 424ff31a1c4SAndre Przywara 425ff31a1c4SAndre Przywara pattern = 0xff7fbf3f; 426ff31a1c4SAndre Przywara writel(pattern, first_spi); 427a299895bSThomas Huth report(readl(first_spi) == (pattern & pri_mask), 428a299895bSThomas Huth "priorities are preserved"); 429ff31a1c4SAndre Przywara 430ff31a1c4SAndre Przywara /* The PRIORITY registers are byte accessible. */ 431ff31a1c4SAndre Przywara test_byte_access(first_spi, pattern, pri_mask); 432ff31a1c4SAndre Przywara 433ff31a1c4SAndre Przywara report_prefix_pop(); 434ff31a1c4SAndre Przywara writel(orig_prio, first_spi); 435ff31a1c4SAndre Przywara } 436ff31a1c4SAndre Przywara 437fe572a5eSAndre Przywara /* GICD_ITARGETSR is only used by GICv2. */ 438fe572a5eSAndre Przywara static void test_targets(int nr_irqs) 439fe572a5eSAndre Przywara { 440fe572a5eSAndre Przywara void *targetsptr = gicv2_dist_base() + GICD_ITARGETSR; 441fe572a5eSAndre Przywara u32 orig_targets; 442fe572a5eSAndre Przywara u32 cpu_mask; 443fe572a5eSAndre Przywara u32 pattern, reg; 444fe572a5eSAndre Przywara 445fe572a5eSAndre Przywara orig_targets = readl(targetsptr + GIC_FIRST_SPI); 446fe572a5eSAndre Przywara report_prefix_push("ITARGETSR"); 447fe572a5eSAndre Przywara 448fe572a5eSAndre Przywara cpu_mask = (1 << nr_cpus) - 1; 449fe572a5eSAndre Przywara cpu_mask |= cpu_mask << 8; 450fe572a5eSAndre Przywara cpu_mask |= cpu_mask << 16; 451fe572a5eSAndre Przywara 452fe572a5eSAndre Przywara /* Check that bits for non implemented CPUs are RAZ/WI. */ 453fe572a5eSAndre Przywara if (nr_cpus < 8) { 454fe572a5eSAndre Przywara writel(0xffffffff, targetsptr + GIC_FIRST_SPI); 455a299895bSThomas Huth report(!(readl(targetsptr + GIC_FIRST_SPI) & ~cpu_mask), 456a299895bSThomas Huth "bits for non-existent CPUs masked"); 4578e0a4f41SAndre Przywara report_info("%d non-existent CPUs", 8 - nr_cpus); 458fe572a5eSAndre Przywara } else { 459fe572a5eSAndre Przywara report_skip("CPU masking (all CPUs implemented)"); 460fe572a5eSAndre Przywara } 461fe572a5eSAndre Przywara 462a299895bSThomas Huth report(test_readonly_32(targetsptr + nr_irqs, true), 463a299895bSThomas Huth "accesses beyond limit RAZ/WI"); 464fe572a5eSAndre Przywara 465fe572a5eSAndre Przywara pattern = 0x0103020f; 466fe572a5eSAndre Przywara writel(pattern, targetsptr + GIC_FIRST_SPI); 467fe572a5eSAndre Przywara reg = readl(targetsptr + GIC_FIRST_SPI); 468a299895bSThomas Huth report(reg == (pattern & cpu_mask), "register content preserved"); 4698e0a4f41SAndre Przywara if (reg != (pattern & cpu_mask)) 4708e0a4f41SAndre Przywara report_info("writing %08x reads back as %08x", 4718e0a4f41SAndre Przywara pattern & cpu_mask, reg); 472fe572a5eSAndre Przywara 473fe572a5eSAndre Przywara /* The TARGETS registers are byte accessible. */ 474fe572a5eSAndre Przywara test_byte_access(targetsptr + GIC_FIRST_SPI, pattern, cpu_mask); 475fe572a5eSAndre Przywara 476fe572a5eSAndre Przywara writel(orig_targets, targetsptr + GIC_FIRST_SPI); 477da5b8576SAndre Przywara 478da5b8576SAndre Przywara report_prefix_pop(); 479fe572a5eSAndre Przywara } 480fe572a5eSAndre Przywara 48178ad7e95SAndre Przywara static void gic_test_mmio(void) 48278ad7e95SAndre Przywara { 48378ad7e95SAndre Przywara u32 reg; 48478ad7e95SAndre Przywara int nr_irqs; 48578ad7e95SAndre Przywara void *gic_dist_base, *idreg; 48678ad7e95SAndre Przywara 48778ad7e95SAndre Przywara switch(gic_version()) { 48878ad7e95SAndre Przywara case 0x2: 48978ad7e95SAndre Przywara gic_dist_base = gicv2_dist_base(); 49078ad7e95SAndre Przywara idreg = gic_dist_base + GICD_ICPIDR2; 49178ad7e95SAndre Przywara break; 49278ad7e95SAndre Przywara case 0x3: 49378ad7e95SAndre Przywara report_abort("GICv3 MMIO tests NYI"); 49478ad7e95SAndre Przywara default: 49578ad7e95SAndre Przywara report_abort("GIC version %d not supported", gic_version()); 49678ad7e95SAndre Przywara } 49778ad7e95SAndre Przywara 49878ad7e95SAndre Przywara reg = readl(gic_dist_base + GICD_TYPER); 49978ad7e95SAndre Przywara nr_irqs = GICD_TYPER_IRQS(reg); 50078ad7e95SAndre Przywara report_info("number of implemented SPIs: %d", nr_irqs - GIC_FIRST_SPI); 50178ad7e95SAndre Przywara 50278ad7e95SAndre Przywara test_typer_v2(reg); 50378ad7e95SAndre Przywara 50478ad7e95SAndre Przywara report_info("IIDR: 0x%08x", readl(gic_dist_base + GICD_IIDR)); 50578ad7e95SAndre Przywara 506a299895bSThomas Huth report(test_readonly_32(gic_dist_base + GICD_TYPER, false), 507a299895bSThomas Huth "GICD_TYPER is read-only"); 508a299895bSThomas Huth report(test_readonly_32(gic_dist_base + GICD_IIDR, false), 509a299895bSThomas Huth "GICD_IIDR is read-only"); 51078ad7e95SAndre Przywara 51178ad7e95SAndre Przywara reg = readl(idreg); 512a299895bSThomas Huth report(test_readonly_32(idreg, false), "ICPIDR2 is read-only"); 5138e0a4f41SAndre Przywara report_info("value of ICPIDR2: 0x%08x", reg); 514ff31a1c4SAndre Przywara 515ff31a1c4SAndre Przywara test_priorities(nr_irqs, gic_dist_base + GICD_IPRIORITYR); 516fe572a5eSAndre Przywara 517fe572a5eSAndre Przywara if (gic_version() == 2) 518fe572a5eSAndre Przywara test_targets(nr_irqs); 51978ad7e95SAndre Przywara } 52078ad7e95SAndre Przywara 521*ba74b106SEric Auger #if defined(__arm__) 522*ba74b106SEric Auger 523*ba74b106SEric Auger static void test_its_introspection(void) {} 524*ba74b106SEric Auger 525*ba74b106SEric Auger #else /* __aarch64__ */ 526*ba74b106SEric Auger 527*ba74b106SEric Auger static void test_its_introspection(void) 528*ba74b106SEric Auger { 529*ba74b106SEric Auger struct its_baser *dev_baser = &its_data.device_baser; 530*ba74b106SEric Auger struct its_baser *coll_baser = &its_data.coll_baser; 531*ba74b106SEric Auger struct its_typer *typer = &its_data.typer; 532*ba74b106SEric Auger 533*ba74b106SEric Auger if (!gicv3_its_base()) { 534*ba74b106SEric Auger report_skip("No ITS, skip ..."); 535*ba74b106SEric Auger return; 536*ba74b106SEric Auger } 537*ba74b106SEric Auger 538*ba74b106SEric Auger /* IIDR */ 539*ba74b106SEric Auger report(test_readonly_32(gicv3_its_base() + GITS_IIDR, false), 540*ba74b106SEric Auger "GITS_IIDR is read-only"), 541*ba74b106SEric Auger 542*ba74b106SEric Auger /* TYPER */ 543*ba74b106SEric Auger report(test_readonly_32(gicv3_its_base() + GITS_TYPER, false), 544*ba74b106SEric Auger "GITS_TYPER is read-only"); 545*ba74b106SEric Auger 546*ba74b106SEric Auger report(typer->phys_lpi, "ITS supports physical LPIs"); 547*ba74b106SEric Auger report_info("vLPI support: %s", typer->virt_lpi ? "yes" : "no"); 548*ba74b106SEric Auger report_info("ITT entry size = 0x%x", typer->ite_size); 549*ba74b106SEric Auger report_info("Bit Count: EventID=%d DeviceId=%d CollId=%d", 550*ba74b106SEric Auger typer->eventid_bits, typer->deviceid_bits, 551*ba74b106SEric Auger typer->collid_bits); 552*ba74b106SEric Auger report(typer->eventid_bits && typer->deviceid_bits && 553*ba74b106SEric Auger typer->collid_bits, "ID spaces"); 554*ba74b106SEric Auger report_info("Target address format %s", 555*ba74b106SEric Auger typer->pta ? "Redist base address" : "PE #"); 556*ba74b106SEric Auger 557*ba74b106SEric Auger report(dev_baser && coll_baser, "detect device and collection BASER"); 558*ba74b106SEric Auger report_info("device table entry_size = 0x%x", dev_baser->esz); 559*ba74b106SEric Auger report_info("collection table entry_size = 0x%x", coll_baser->esz); 560*ba74b106SEric Auger } 561*ba74b106SEric Auger 562*ba74b106SEric Auger #endif 563*ba74b106SEric Auger 564ac4a67b6SAndrew Jones int main(int argc, char **argv) 565ac4a67b6SAndrew Jones { 5662e2d471dSAndrew Jones if (!gic_init()) { 567ac4a67b6SAndrew Jones printf("No supported gic present, skipping tests...\n"); 568ac4a67b6SAndrew Jones return report_summary(); 569ac4a67b6SAndrew Jones } 570ac4a67b6SAndrew Jones 5712b19b829SAndrew Jones report_prefix_pushf("gicv%d", gic_version()); 572ac4a67b6SAndrew Jones 5732e2d471dSAndrew Jones switch (gic_version()) { 5742e2d471dSAndrew Jones case 2: 5752e2d471dSAndrew Jones gic = &gicv2; 5762e2d471dSAndrew Jones break; 5772e2d471dSAndrew Jones case 3: 5782e2d471dSAndrew Jones gic = &gicv3; 5792e2d471dSAndrew Jones break; 5802e2d471dSAndrew Jones } 5812e2d471dSAndrew Jones 582ac4a67b6SAndrew Jones if (argc < 2) 583ac4a67b6SAndrew Jones report_abort("no test specified"); 584ac4a67b6SAndrew Jones 585ac4a67b6SAndrew Jones if (strcmp(argv[1], "ipi") == 0) { 586ac4a67b6SAndrew Jones report_prefix_push(argv[1]); 587ac4a67b6SAndrew Jones nr_cpu_check(2); 58800b34f56SAndrew Jones on_cpus(ipi_test, NULL); 589c152d8bcSChristoffer Dall } else if (strcmp(argv[1], "active") == 0) { 590c152d8bcSChristoffer Dall run_active_clear_test(); 59178ad7e95SAndre Przywara } else if (strcmp(argv[1], "mmio") == 0) { 59278ad7e95SAndre Przywara report_prefix_push(argv[1]); 59378ad7e95SAndre Przywara gic_test_mmio(); 59478ad7e95SAndre Przywara report_prefix_pop(); 595*ba74b106SEric Auger } else if (strcmp(argv[1], "its-introspection") == 0) { 596*ba74b106SEric Auger report_prefix_push(argv[1]); 597*ba74b106SEric Auger test_its_introspection(); 598*ba74b106SEric Auger report_prefix_pop(); 599ac4a67b6SAndrew Jones } else { 600ac4a67b6SAndrew Jones report_abort("Unknown subtest '%s'", argv[1]); 601ac4a67b6SAndrew Jones } 602ac4a67b6SAndrew Jones 603ac4a67b6SAndrew Jones return report_summary(); 604ac4a67b6SAndrew Jones } 605