xref: /kvm-unit-tests/arm/gic.c (revision ff31a1c46a66b14a0bbe54f8ffce50830dff81bd)
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 
63ac4a67b6SAndrew Jones static void check_acked(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) {
91ca1b7a7bSAndrew Jones 			report("Completed in %d ms", !bad, ++i * 100);
92ac4a67b6SAndrew Jones 			return;
93ac4a67b6SAndrew Jones 		}
94ac4a67b6SAndrew Jones 	}
95ac4a67b6SAndrew Jones 
96ac4a67b6SAndrew Jones 	for_each_present_cpu(cpu) {
97ac4a67b6SAndrew Jones 		if (cpumask_test_cpu(cpu, mask)) {
98ac4a67b6SAndrew Jones 			if (!acked[cpu])
99ac4a67b6SAndrew Jones 				++missing;
100ac4a67b6SAndrew Jones 			else if (acked[cpu] > 1)
101ac4a67b6SAndrew Jones 				++extra;
102ac4a67b6SAndrew Jones 		} else {
103ac4a67b6SAndrew Jones 			if (acked[cpu])
104ac4a67b6SAndrew Jones 				++unexpected;
105ac4a67b6SAndrew Jones 		}
106ac4a67b6SAndrew Jones 	}
107ac4a67b6SAndrew Jones 
108ac4a67b6SAndrew Jones 	report("Timed-out (5s). ACKS: missing=%d extra=%d unexpected=%d",
109ac4a67b6SAndrew Jones 	       false, missing, extra, unexpected);
110ac4a67b6SAndrew Jones }
111ac4a67b6SAndrew Jones 
112ac4a67b6SAndrew Jones static void check_spurious(void)
113ac4a67b6SAndrew Jones {
114ac4a67b6SAndrew Jones 	int cpu;
115ac4a67b6SAndrew Jones 
116ac4a67b6SAndrew Jones 	smp_rmb();
117ac4a67b6SAndrew Jones 	for_each_present_cpu(cpu) {
118ac4a67b6SAndrew Jones 		if (spurious[cpu])
119ac4a67b6SAndrew Jones 			report_info("WARN: cpu%d got %d spurious interrupts",
120ac4a67b6SAndrew Jones 				cpu, spurious[cpu]);
121ac4a67b6SAndrew Jones 	}
122ac4a67b6SAndrew Jones }
123ac4a67b6SAndrew Jones 
124ca1b7a7bSAndrew Jones static void check_ipi_sender(u32 irqstat)
125ca1b7a7bSAndrew Jones {
126ca1b7a7bSAndrew Jones 	if (gic_version() == 2) {
127ca1b7a7bSAndrew Jones 		int src = (irqstat >> 10) & 7;
128ca1b7a7bSAndrew Jones 
129ca1b7a7bSAndrew Jones 		if (src != IPI_SENDER)
130ca1b7a7bSAndrew Jones 			bad_sender[smp_processor_id()] = src;
131ca1b7a7bSAndrew Jones 	}
132ca1b7a7bSAndrew Jones }
133ca1b7a7bSAndrew Jones 
134ca1b7a7bSAndrew Jones static void check_irqnr(u32 irqnr)
135ca1b7a7bSAndrew Jones {
136ca1b7a7bSAndrew Jones 	if (irqnr != IPI_IRQ)
137ca1b7a7bSAndrew Jones 		bad_irq[smp_processor_id()] = irqnr;
138ca1b7a7bSAndrew Jones }
139ca1b7a7bSAndrew Jones 
140ac4a67b6SAndrew Jones static void ipi_handler(struct pt_regs *regs __unused)
141ac4a67b6SAndrew Jones {
1422e2d471dSAndrew Jones 	u32 irqstat = gic_read_iar();
1432e2d471dSAndrew Jones 	u32 irqnr = gic_iar_irqnr(irqstat);
144ac4a67b6SAndrew Jones 
145ac4a67b6SAndrew Jones 	if (irqnr != GICC_INT_SPURIOUS) {
1462e2d471dSAndrew Jones 		gic_write_eoir(irqstat);
147ca1b7a7bSAndrew Jones 		smp_rmb(); /* pairs with wmb in stats_reset */
148ac4a67b6SAndrew Jones 		++acked[smp_processor_id()];
149ca1b7a7bSAndrew Jones 		check_ipi_sender(irqstat);
150ca1b7a7bSAndrew Jones 		check_irqnr(irqnr);
151ac4a67b6SAndrew Jones 		smp_wmb(); /* pairs with rmb in check_acked */
152ac4a67b6SAndrew Jones 	} else {
153ac4a67b6SAndrew Jones 		++spurious[smp_processor_id()];
154ac4a67b6SAndrew Jones 		smp_wmb();
155ac4a67b6SAndrew Jones 	}
156ac4a67b6SAndrew Jones }
157ac4a67b6SAndrew Jones 
1582e2d471dSAndrew Jones static void gicv2_ipi_send_self(void)
1592e2d471dSAndrew Jones {
160ca1b7a7bSAndrew Jones 	writel(2 << 24 | IPI_IRQ, gicv2_dist_base() + GICD_SGIR);
1612e2d471dSAndrew Jones }
1622e2d471dSAndrew Jones 
1632e2d471dSAndrew Jones static void gicv2_ipi_send_broadcast(void)
1642e2d471dSAndrew Jones {
165ca1b7a7bSAndrew Jones 	writel(1 << 24 | IPI_IRQ, gicv2_dist_base() + GICD_SGIR);
1662e2d471dSAndrew Jones }
1672e2d471dSAndrew Jones 
1682e2d471dSAndrew Jones static void gicv3_ipi_send_self(void)
1692e2d471dSAndrew Jones {
170ca1b7a7bSAndrew Jones 	gic_ipi_send_single(IPI_IRQ, smp_processor_id());
1712e2d471dSAndrew Jones }
1722e2d471dSAndrew Jones 
1732e2d471dSAndrew Jones static void gicv3_ipi_send_broadcast(void)
1742e2d471dSAndrew Jones {
175ca1b7a7bSAndrew Jones 	gicv3_write_sgi1r(1ULL << 40 | IPI_IRQ << 24);
1762e2d471dSAndrew Jones 	isb();
1772e2d471dSAndrew Jones }
1782e2d471dSAndrew Jones 
179ac4a67b6SAndrew Jones static void ipi_test_self(void)
180ac4a67b6SAndrew Jones {
181ac4a67b6SAndrew Jones 	cpumask_t mask;
182ac4a67b6SAndrew Jones 
183ac4a67b6SAndrew Jones 	report_prefix_push("self");
184ca1b7a7bSAndrew Jones 	stats_reset();
185ac4a67b6SAndrew Jones 	cpumask_clear(&mask);
186ca1b7a7bSAndrew Jones 	cpumask_set_cpu(smp_processor_id(), &mask);
1872e2d471dSAndrew Jones 	gic->ipi.send_self();
188ac4a67b6SAndrew Jones 	check_acked(&mask);
189ac4a67b6SAndrew Jones 	report_prefix_pop();
190ac4a67b6SAndrew Jones }
191ac4a67b6SAndrew Jones 
192ac4a67b6SAndrew Jones static void ipi_test_smp(void)
193ac4a67b6SAndrew Jones {
194ac4a67b6SAndrew Jones 	cpumask_t mask;
1952e2d471dSAndrew Jones 	int i;
196ac4a67b6SAndrew Jones 
197ac4a67b6SAndrew Jones 	report_prefix_push("target-list");
198ca1b7a7bSAndrew Jones 	stats_reset();
1992e2d471dSAndrew Jones 	cpumask_copy(&mask, &cpu_present_mask);
200ca1b7a7bSAndrew Jones 	for (i = smp_processor_id() & 1; i < nr_cpus; i += 2)
2012e2d471dSAndrew Jones 		cpumask_clear_cpu(i, &mask);
202ca1b7a7bSAndrew Jones 	gic_ipi_send_mask(IPI_IRQ, &mask);
203ac4a67b6SAndrew Jones 	check_acked(&mask);
204ac4a67b6SAndrew Jones 	report_prefix_pop();
205ac4a67b6SAndrew Jones 
206ac4a67b6SAndrew Jones 	report_prefix_push("broadcast");
207ca1b7a7bSAndrew Jones 	stats_reset();
208ac4a67b6SAndrew Jones 	cpumask_copy(&mask, &cpu_present_mask);
209ca1b7a7bSAndrew Jones 	cpumask_clear_cpu(smp_processor_id(), &mask);
2102e2d471dSAndrew Jones 	gic->ipi.send_broadcast();
211ac4a67b6SAndrew Jones 	check_acked(&mask);
212ac4a67b6SAndrew Jones 	report_prefix_pop();
213ac4a67b6SAndrew Jones }
214ac4a67b6SAndrew Jones 
215ac4a67b6SAndrew Jones static void ipi_enable(void)
216ac4a67b6SAndrew Jones {
2172e2d471dSAndrew Jones 	gic_enable_defaults();
218ac4a67b6SAndrew Jones #ifdef __arm__
219ac4a67b6SAndrew Jones 	install_exception_handler(EXCPTN_IRQ, ipi_handler);
220ac4a67b6SAndrew Jones #else
221ac4a67b6SAndrew Jones 	install_irq_handler(EL1H_IRQ, ipi_handler);
222ac4a67b6SAndrew Jones #endif
223ac4a67b6SAndrew Jones 	local_irq_enable();
224ac4a67b6SAndrew Jones }
225ac4a67b6SAndrew Jones 
226ca1b7a7bSAndrew Jones static void ipi_send(void)
227ca1b7a7bSAndrew Jones {
228ca1b7a7bSAndrew Jones 	ipi_enable();
229ca1b7a7bSAndrew Jones 	wait_on_ready();
230ca1b7a7bSAndrew Jones 	ipi_test_self();
231ca1b7a7bSAndrew Jones 	ipi_test_smp();
232ca1b7a7bSAndrew Jones 	check_spurious();
233ca1b7a7bSAndrew Jones 	exit(report_summary());
234ca1b7a7bSAndrew Jones }
235ca1b7a7bSAndrew Jones 
236ac4a67b6SAndrew Jones static void ipi_recv(void)
237ac4a67b6SAndrew Jones {
238ac4a67b6SAndrew Jones 	ipi_enable();
239ac4a67b6SAndrew Jones 	cpumask_set_cpu(smp_processor_id(), &ready);
240ac4a67b6SAndrew Jones 	while (1)
241ac4a67b6SAndrew Jones 		wfi();
242ac4a67b6SAndrew Jones }
243ac4a67b6SAndrew Jones 
24400b34f56SAndrew Jones static void ipi_test(void *data __unused)
245bfd500b4SAndrew Jones {
246bfd500b4SAndrew Jones 	if (smp_processor_id() == IPI_SENDER)
247bfd500b4SAndrew Jones 		ipi_send();
248bfd500b4SAndrew Jones 	else
249bfd500b4SAndrew Jones 		ipi_recv();
250bfd500b4SAndrew Jones }
251bfd500b4SAndrew Jones 
2522e2d471dSAndrew Jones static struct gic gicv2 = {
2532e2d471dSAndrew Jones 	.ipi = {
2542e2d471dSAndrew Jones 		.send_self = gicv2_ipi_send_self,
2552e2d471dSAndrew Jones 		.send_broadcast = gicv2_ipi_send_broadcast,
2562e2d471dSAndrew Jones 	},
2572e2d471dSAndrew Jones };
2582e2d471dSAndrew Jones 
2592e2d471dSAndrew Jones static struct gic gicv3 = {
2602e2d471dSAndrew Jones 	.ipi = {
2612e2d471dSAndrew Jones 		.send_self = gicv3_ipi_send_self,
2622e2d471dSAndrew Jones 		.send_broadcast = gicv3_ipi_send_broadcast,
2632e2d471dSAndrew Jones 	},
2642e2d471dSAndrew Jones };
2652e2d471dSAndrew Jones 
266c152d8bcSChristoffer Dall static void ipi_clear_active_handler(struct pt_regs *regs __unused)
267c152d8bcSChristoffer Dall {
268c152d8bcSChristoffer Dall 	u32 irqstat = gic_read_iar();
269c152d8bcSChristoffer Dall 	u32 irqnr = gic_iar_irqnr(irqstat);
270c152d8bcSChristoffer Dall 
271c152d8bcSChristoffer Dall 	if (irqnr != GICC_INT_SPURIOUS) {
272c152d8bcSChristoffer Dall 		void *base;
273c152d8bcSChristoffer Dall 		u32 val = 1 << IPI_IRQ;
274c152d8bcSChristoffer Dall 
275c152d8bcSChristoffer Dall 		if (gic_version() == 2)
276c152d8bcSChristoffer Dall 			base = gicv2_dist_base();
277c152d8bcSChristoffer Dall 		else
2786d4d7c4bSAndrew Jones 			base = gicv3_sgi_base();
279c152d8bcSChristoffer Dall 
280c152d8bcSChristoffer Dall 		writel(val, base + GICD_ICACTIVER);
281c152d8bcSChristoffer Dall 
282c152d8bcSChristoffer Dall 		smp_rmb(); /* pairs with wmb in stats_reset */
283c152d8bcSChristoffer Dall 		++acked[smp_processor_id()];
284c152d8bcSChristoffer Dall 		check_irqnr(irqnr);
285c152d8bcSChristoffer Dall 		smp_wmb(); /* pairs with rmb in check_acked */
286c152d8bcSChristoffer Dall 	} else {
287c152d8bcSChristoffer Dall 		++spurious[smp_processor_id()];
288c152d8bcSChristoffer Dall 		smp_wmb();
289c152d8bcSChristoffer Dall 	}
290c152d8bcSChristoffer Dall }
291c152d8bcSChristoffer Dall 
292c152d8bcSChristoffer Dall static void run_active_clear_test(void)
293c152d8bcSChristoffer Dall {
294c152d8bcSChristoffer Dall 	report_prefix_push("active");
295c152d8bcSChristoffer Dall 	gic_enable_defaults();
296c152d8bcSChristoffer Dall #ifdef __arm__
297c152d8bcSChristoffer Dall 	install_exception_handler(EXCPTN_IRQ, ipi_clear_active_handler);
298c152d8bcSChristoffer Dall #else
299c152d8bcSChristoffer Dall 	install_irq_handler(EL1H_IRQ, ipi_clear_active_handler);
300c152d8bcSChristoffer Dall #endif
301c152d8bcSChristoffer Dall 	local_irq_enable();
302c152d8bcSChristoffer Dall 
303c152d8bcSChristoffer Dall 	ipi_test_self();
304c152d8bcSChristoffer Dall 	report_prefix_pop();
305c152d8bcSChristoffer Dall }
306c152d8bcSChristoffer Dall 
30778ad7e95SAndre Przywara static bool test_ro_pattern_32(void *address, u32 pattern, u32 orig)
30878ad7e95SAndre Przywara {
30978ad7e95SAndre Przywara 	u32 reg;
31078ad7e95SAndre Przywara 
31178ad7e95SAndre Przywara 	writel(pattern, address);
31278ad7e95SAndre Przywara 	reg = readl(address);
31378ad7e95SAndre Przywara 
31478ad7e95SAndre Przywara 	if (reg != orig)
31578ad7e95SAndre Przywara 		writel(orig, address);
31678ad7e95SAndre Przywara 
31778ad7e95SAndre Przywara 	return reg == orig;
31878ad7e95SAndre Przywara }
31978ad7e95SAndre Przywara 
32078ad7e95SAndre Przywara static bool test_readonly_32(void *address, bool razwi)
32178ad7e95SAndre Przywara {
32278ad7e95SAndre Przywara 	u32 orig, pattern;
32378ad7e95SAndre Przywara 
32478ad7e95SAndre Przywara 	orig = readl(address);
32578ad7e95SAndre Przywara 	if (razwi && orig)
32678ad7e95SAndre Przywara 		return false;
32778ad7e95SAndre Przywara 
32878ad7e95SAndre Przywara 	pattern = 0xffffffff;
32978ad7e95SAndre Przywara 	if (orig != pattern) {
33078ad7e95SAndre Przywara 		if (!test_ro_pattern_32(address, pattern, orig))
33178ad7e95SAndre Przywara 			return false;
33278ad7e95SAndre Przywara 	}
33378ad7e95SAndre Przywara 
33478ad7e95SAndre Przywara 	pattern = 0xa5a55a5a;
33578ad7e95SAndre Przywara 	if (orig != pattern) {
33678ad7e95SAndre Przywara 		if (!test_ro_pattern_32(address, pattern, orig))
33778ad7e95SAndre Przywara 			return false;
33878ad7e95SAndre Przywara 	}
33978ad7e95SAndre Przywara 
34078ad7e95SAndre Przywara 	pattern = 0;
34178ad7e95SAndre Przywara 	if (orig != pattern) {
34278ad7e95SAndre Przywara 		if (!test_ro_pattern_32(address, pattern, orig))
34378ad7e95SAndre Przywara 			return false;
34478ad7e95SAndre Przywara 	}
34578ad7e95SAndre Przywara 
34678ad7e95SAndre Przywara 	return true;
34778ad7e95SAndre Przywara }
34878ad7e95SAndre Przywara 
34978ad7e95SAndre Przywara static void test_typer_v2(uint32_t reg)
35078ad7e95SAndre Przywara {
35178ad7e95SAndre Przywara 	int nr_gic_cpus = ((reg >> 5) & 0x7) + 1;
35278ad7e95SAndre Przywara 
35378ad7e95SAndre Przywara 	report("all %d CPUs have interrupts", nr_cpus == nr_gic_cpus,
35478ad7e95SAndre Przywara 	       nr_gic_cpus);
35578ad7e95SAndre Przywara }
35678ad7e95SAndre Przywara 
357*ff31a1c4SAndre Przywara #define BYTE(reg32, byte) (((reg32) >> ((byte) * 8)) & 0xff)
358*ff31a1c4SAndre Przywara #define REPLACE_BYTE(reg32, byte, new) (((reg32) & ~(0xff << ((byte) * 8))) |\
359*ff31a1c4SAndre Przywara 					((new) << ((byte) * 8)))
360*ff31a1c4SAndre Przywara 
361*ff31a1c4SAndre Przywara /*
362*ff31a1c4SAndre Przywara  * Some registers are byte accessible, do a byte-wide read and write of known
363*ff31a1c4SAndre Przywara  * content to check for this.
364*ff31a1c4SAndre Przywara  * Apply a @mask to cater for special register properties.
365*ff31a1c4SAndre Przywara  * @pattern contains the value already in the register.
366*ff31a1c4SAndre Przywara  */
367*ff31a1c4SAndre Przywara static void test_byte_access(void *base_addr, u32 pattern, u32 mask)
368*ff31a1c4SAndre Przywara {
369*ff31a1c4SAndre Przywara 	u32 reg = readb(base_addr + 1);
370*ff31a1c4SAndre Przywara 
371*ff31a1c4SAndre Przywara 	report("byte reads successful (0x%08x => 0x%02x)",
372*ff31a1c4SAndre Przywara 	       reg == (BYTE(pattern, 1) & (mask >> 8)),
373*ff31a1c4SAndre Przywara 	       pattern & mask, reg);
374*ff31a1c4SAndre Przywara 
375*ff31a1c4SAndre Przywara 	pattern = REPLACE_BYTE(pattern, 2, 0x1f);
376*ff31a1c4SAndre Przywara 	writeb(BYTE(pattern, 2), base_addr + 2);
377*ff31a1c4SAndre Przywara 	reg = readl(base_addr);
378*ff31a1c4SAndre Przywara 	report("byte writes successful (0x%02x => 0x%08x)",
379*ff31a1c4SAndre Przywara 	       reg == (pattern & mask), BYTE(pattern, 2), reg);
380*ff31a1c4SAndre Przywara }
381*ff31a1c4SAndre Przywara 
382*ff31a1c4SAndre Przywara static void test_priorities(int nr_irqs, void *priptr)
383*ff31a1c4SAndre Przywara {
384*ff31a1c4SAndre Przywara 	u32 orig_prio, reg, pri_bits;
385*ff31a1c4SAndre Przywara 	u32 pri_mask, pattern;
386*ff31a1c4SAndre Przywara 	void *first_spi = priptr + GIC_FIRST_SPI;
387*ff31a1c4SAndre Przywara 
388*ff31a1c4SAndre Przywara 	orig_prio = readl(first_spi);
389*ff31a1c4SAndre Przywara 	report_prefix_push("IPRIORITYR");
390*ff31a1c4SAndre Przywara 
391*ff31a1c4SAndre Przywara 	/*
392*ff31a1c4SAndre Przywara 	 * Determine implemented number of priority bits by writing all 1's
393*ff31a1c4SAndre Przywara 	 * and checking the number of cleared bits in the value read back.
394*ff31a1c4SAndre Przywara 	 */
395*ff31a1c4SAndre Przywara 	writel(0xffffffff, first_spi);
396*ff31a1c4SAndre Przywara 	pri_mask = readl(first_spi);
397*ff31a1c4SAndre Przywara 
398*ff31a1c4SAndre Przywara 	reg = ~pri_mask;
399*ff31a1c4SAndre Przywara 	report("consistent priority masking (0x%08x)",
400*ff31a1c4SAndre Przywara 	       (((reg >> 16) == (reg & 0xffff)) &&
401*ff31a1c4SAndre Przywara 	        ((reg & 0xff) == ((reg >> 8) & 0xff))), pri_mask);
402*ff31a1c4SAndre Przywara 
403*ff31a1c4SAndre Przywara 	reg = reg & 0xff;
404*ff31a1c4SAndre Przywara 	for (pri_bits = 8; reg & 1; reg >>= 1, pri_bits--)
405*ff31a1c4SAndre Przywara 		;
406*ff31a1c4SAndre Przywara 	report("implements at least 4 priority bits (%d)",
407*ff31a1c4SAndre Przywara 	       pri_bits >= 4, pri_bits);
408*ff31a1c4SAndre Przywara 
409*ff31a1c4SAndre Przywara 	pattern = 0;
410*ff31a1c4SAndre Przywara 	writel(pattern, first_spi);
411*ff31a1c4SAndre Przywara 	report("clearing priorities", readl(first_spi) == pattern);
412*ff31a1c4SAndre Przywara 
413*ff31a1c4SAndre Przywara 	/* setting all priorities to their max valus was tested above */
414*ff31a1c4SAndre Przywara 
415*ff31a1c4SAndre Przywara 	report("accesses beyond limit RAZ/WI",
416*ff31a1c4SAndre Przywara 	       test_readonly_32(priptr + nr_irqs, true));
417*ff31a1c4SAndre Przywara 
418*ff31a1c4SAndre Przywara 	writel(pattern, priptr + nr_irqs - 4);
419*ff31a1c4SAndre Przywara 	report("accessing last SPIs",
420*ff31a1c4SAndre Przywara 	       readl(priptr + nr_irqs - 4) == (pattern & pri_mask));
421*ff31a1c4SAndre Przywara 
422*ff31a1c4SAndre Przywara 	pattern = 0xff7fbf3f;
423*ff31a1c4SAndre Przywara 	writel(pattern, first_spi);
424*ff31a1c4SAndre Przywara 	report("priorities are preserved",
425*ff31a1c4SAndre Przywara 	       readl(first_spi) == (pattern & pri_mask));
426*ff31a1c4SAndre Przywara 
427*ff31a1c4SAndre Przywara 	/* The PRIORITY registers are byte accessible. */
428*ff31a1c4SAndre Przywara 	test_byte_access(first_spi, pattern, pri_mask);
429*ff31a1c4SAndre Przywara 
430*ff31a1c4SAndre Przywara 	report_prefix_pop();
431*ff31a1c4SAndre Przywara 	writel(orig_prio, first_spi);
432*ff31a1c4SAndre Przywara }
433*ff31a1c4SAndre Przywara 
43478ad7e95SAndre Przywara static void gic_test_mmio(void)
43578ad7e95SAndre Przywara {
43678ad7e95SAndre Przywara 	u32 reg;
43778ad7e95SAndre Przywara 	int nr_irqs;
43878ad7e95SAndre Przywara 	void *gic_dist_base, *idreg;
43978ad7e95SAndre Przywara 
44078ad7e95SAndre Przywara 	switch(gic_version()) {
44178ad7e95SAndre Przywara 	case 0x2:
44278ad7e95SAndre Przywara 		gic_dist_base = gicv2_dist_base();
44378ad7e95SAndre Przywara 		idreg = gic_dist_base + GICD_ICPIDR2;
44478ad7e95SAndre Przywara 		break;
44578ad7e95SAndre Przywara 	case 0x3:
44678ad7e95SAndre Przywara 		report_abort("GICv3 MMIO tests NYI");
44778ad7e95SAndre Przywara 	default:
44878ad7e95SAndre Przywara 		report_abort("GIC version %d not supported", gic_version());
44978ad7e95SAndre Przywara 	}
45078ad7e95SAndre Przywara 
45178ad7e95SAndre Przywara 	reg = readl(gic_dist_base + GICD_TYPER);
45278ad7e95SAndre Przywara 	nr_irqs = GICD_TYPER_IRQS(reg);
45378ad7e95SAndre Przywara 	report_info("number of implemented SPIs: %d", nr_irqs - GIC_FIRST_SPI);
45478ad7e95SAndre Przywara 
45578ad7e95SAndre Przywara 	test_typer_v2(reg);
45678ad7e95SAndre Przywara 
45778ad7e95SAndre Przywara 	report_info("IIDR: 0x%08x", readl(gic_dist_base + GICD_IIDR));
45878ad7e95SAndre Przywara 
45978ad7e95SAndre Przywara 	report("GICD_TYPER is read-only",
46078ad7e95SAndre Przywara 	       test_readonly_32(gic_dist_base + GICD_TYPER, false));
46178ad7e95SAndre Przywara 	report("GICD_IIDR is read-only",
46278ad7e95SAndre Przywara 	       test_readonly_32(gic_dist_base + GICD_IIDR, false));
46378ad7e95SAndre Przywara 
46478ad7e95SAndre Przywara 	reg = readl(idreg);
46578ad7e95SAndre Przywara 	report("ICPIDR2 is read-only (0x%08x)",
46678ad7e95SAndre Przywara 	       test_readonly_32(idreg, false),
46778ad7e95SAndre Przywara 	       reg);
468*ff31a1c4SAndre Przywara 
469*ff31a1c4SAndre Przywara 	test_priorities(nr_irqs, gic_dist_base + GICD_IPRIORITYR);
47078ad7e95SAndre Przywara }
47178ad7e95SAndre Przywara 
472ac4a67b6SAndrew Jones int main(int argc, char **argv)
473ac4a67b6SAndrew Jones {
4742e2d471dSAndrew Jones 	if (!gic_init()) {
475ac4a67b6SAndrew Jones 		printf("No supported gic present, skipping tests...\n");
476ac4a67b6SAndrew Jones 		return report_summary();
477ac4a67b6SAndrew Jones 	}
478ac4a67b6SAndrew Jones 
4792b19b829SAndrew Jones 	report_prefix_pushf("gicv%d", gic_version());
480ac4a67b6SAndrew Jones 
4812e2d471dSAndrew Jones 	switch (gic_version()) {
4822e2d471dSAndrew Jones 	case 2:
4832e2d471dSAndrew Jones 		gic = &gicv2;
4842e2d471dSAndrew Jones 		break;
4852e2d471dSAndrew Jones 	case 3:
4862e2d471dSAndrew Jones 		gic = &gicv3;
4872e2d471dSAndrew Jones 		break;
4882e2d471dSAndrew Jones 	}
4892e2d471dSAndrew Jones 
490ac4a67b6SAndrew Jones 	if (argc < 2)
491ac4a67b6SAndrew Jones 		report_abort("no test specified");
492ac4a67b6SAndrew Jones 
493ac4a67b6SAndrew Jones 	if (strcmp(argv[1], "ipi") == 0) {
494ac4a67b6SAndrew Jones 		report_prefix_push(argv[1]);
495ac4a67b6SAndrew Jones 		nr_cpu_check(2);
49600b34f56SAndrew Jones 		on_cpus(ipi_test, NULL);
497c152d8bcSChristoffer Dall 	} else if (strcmp(argv[1], "active") == 0) {
498c152d8bcSChristoffer Dall 		run_active_clear_test();
49978ad7e95SAndre Przywara 	} else if (strcmp(argv[1], "mmio") == 0) {
50078ad7e95SAndre Przywara 		report_prefix_push(argv[1]);
50178ad7e95SAndre Przywara 		gic_test_mmio();
50278ad7e95SAndre Przywara 		report_prefix_pop();
503ac4a67b6SAndrew Jones 	} else {
504ac4a67b6SAndrew Jones 		report_abort("Unknown subtest '%s'", argv[1]);
505ac4a67b6SAndrew Jones 	}
506ac4a67b6SAndrew Jones 
507ac4a67b6SAndrew Jones 	return report_summary();
508ac4a67b6SAndrew Jones }
509