xref: /kvm-unit-tests/arm/pl031.c (revision 2fffb37e8d9d1cff24ef57800fdef2670fce13e3)
1 /*
2  * Verify PL031 functionality
3  *
4  * This test verifies whether the emulated PL031 behaves correctly.
5  *
6  * Copyright 2019 Amazon.com, Inc. or its affiliates.
7  * Author: Alexander Graf <graf@amazon.com>
8  *
9  * This work is licensed under the terms of the GNU LGPL, version 2.
10  */
11 #include <libcflat.h>
12 #include <devicetree.h>
13 #include <asm/processor.h>
14 #include <asm/io.h>
15 #include <asm/gic.h>
16 
17 struct pl031_regs {
18 	uint32_t dr;	/* Data Register */
19 	uint32_t mr;	/* Match Register */
20 	uint32_t lr;	/* Load Register */
21 	union {
22 		uint8_t cr;	/* Control Register */
23 		uint32_t cr32;
24 	};
25 	union {
26 		uint8_t imsc;	/* Interrupt Mask Set or Clear register */
27 		uint32_t imsc32;
28 	};
29 	union {
30 		uint8_t ris;	/* Raw Interrupt Status */
31 		uint32_t ris32;
32 	};
33 	union {
34 		uint8_t mis;	/* Masked Interrupt Status */
35 		uint32_t mis32;
36 	};
37 	union {
38 		uint8_t icr;	/* Interrupt Clear Register */
39 		uint32_t icr32;
40 	};
41 	uint32_t reserved[1008];
42 	uint32_t periph_id[4];
43 	uint32_t pcell_id[4];
44 };
45 
46 static u32 cntfrq;
47 static struct pl031_regs *pl031;
48 static int pl031_irq;
49 static void *gic_ispendr;
50 static volatile bool irq_triggered;
51 
check_id(void)52 static int check_id(void)
53 {
54 	uint32_t id[] = { 0x31, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
55 	int i;
56 
57 	for (i = 0; i < ARRAY_SIZE(id); i++)
58 		if (id[i] != readl(&pl031->periph_id[i]))
59 			return 1;
60 
61 	return 0;
62 }
63 
check_ro(void)64 static int check_ro(void)
65 {
66 	uint32_t offs[] = { offsetof(struct pl031_regs, ris),
67 			    offsetof(struct pl031_regs, mis),
68 			    offsetof(struct pl031_regs, periph_id[0]),
69 			    offsetof(struct pl031_regs, periph_id[1]),
70 			    offsetof(struct pl031_regs, periph_id[2]),
71 			    offsetof(struct pl031_regs, periph_id[3]),
72 			    offsetof(struct pl031_regs, pcell_id[0]),
73 			    offsetof(struct pl031_regs, pcell_id[1]),
74 			    offsetof(struct pl031_regs, pcell_id[2]),
75 			    offsetof(struct pl031_regs, pcell_id[3]) };
76 	int i;
77 
78 	for (i = 0; i < ARRAY_SIZE(offs); i++) {
79 		uint32_t before32;
80 		uint16_t before16;
81 		uint8_t before8;
82 		void *addr = (void*)pl031 + offs[i];
83 		uint32_t poison = 0xdeadbeefULL;
84 
85 		before8 = readb(addr);
86 		before16 = readw(addr);
87 		before32 = readl(addr);
88 
89 		writeb(poison, addr);
90 		writew(poison, addr);
91 		writel(poison, addr);
92 
93 		if (before8 != readb(addr))
94 			return 1;
95 		if (before16 != readw(addr))
96 			return 1;
97 		if (before32 != readl(addr))
98 			return 1;
99 	}
100 
101 	return 0;
102 }
103 
check_rtc_freq(void)104 static int check_rtc_freq(void)
105 {
106 	uint32_t seconds_to_wait = 2;
107 	uint32_t before = readl(&pl031->dr);
108 	uint64_t before_tick = get_cntvct();
109 	uint64_t target_tick = before_tick + (cntfrq * seconds_to_wait);
110 
111 	/* Wait for 2 seconds */
112 	while (get_cntvct() < target_tick) ;
113 
114 	if (readl(&pl031->dr) != before + seconds_to_wait)
115 		return 1;
116 
117 	return 0;
118 }
119 
gic_irq_pending(void)120 static bool gic_irq_pending(void)
121 {
122 	uint32_t offset = (pl031_irq / 32) * 4;
123 
124 	return readl(gic_ispendr + offset) & (1 << (pl031_irq & 31));
125 }
126 
irq_handler(struct pt_regs * regs)127 static void irq_handler(struct pt_regs *regs)
128 {
129 	u32 irqstat = gic_read_iar();
130 	u32 irqnr = gic_iar_irqnr(irqstat);
131 
132 	gic_write_eoir(irqstat);
133 
134 	if (irqnr == pl031_irq) {
135 		report(readl(&pl031->ris) == 1, "  RTC RIS == 1");
136 		report(readl(&pl031->mis) == 1, "  RTC MIS == 1");
137 
138 		/* Writing one to bit zero should clear IRQ status */
139 		writel(1, &pl031->icr);
140 
141 		report(readl(&pl031->ris) == 0, "  RTC RIS == 0");
142 		report(readl(&pl031->mis) == 0, "  RTC MIS == 0");
143 		irq_triggered = true;
144 	} else {
145 		report_info("Unexpected interrupt: %"PRIu32"\n", irqnr);
146 		return;
147 	}
148 }
149 
check_rtc_irq(void)150 static int check_rtc_irq(void)
151 {
152 	uint32_t seconds_to_wait = 1;
153 	uint32_t before = readl(&pl031->dr);
154 	uint64_t before_tick = get_cntvct();
155 	uint64_t target_tick = before_tick + (cntfrq * (seconds_to_wait + 1));
156 
157 	report_info("Checking IRQ trigger (MR)");
158 
159 	irq_triggered = false;
160 
161 	/* Fire IRQ in 1 second */
162 	writel(before + seconds_to_wait, &pl031->mr);
163 
164 #ifdef __aarch64__
165 	install_irq_handler(EL1H_IRQ, irq_handler);
166 #else
167 	install_exception_handler(EXCPTN_IRQ, irq_handler);
168 #endif
169 
170 	/* Wait until 2 seconds are over */
171 	while (get_cntvct() < target_tick) ;
172 
173 	report(!gic_irq_pending(), "  RTC IRQ not delivered without mask");
174 
175 	/* Mask the IRQ so that it gets delivered */
176 	writel(1, &pl031->imsc);
177 	report(gic_irq_pending(), "  RTC IRQ pending now");
178 
179 	/* Enable retrieval of IRQ */
180 	gic_enable_irq(pl031_irq);
181 	local_irq_enable();
182 
183 	report(irq_triggered, "  IRQ triggered");
184 	report(!gic_irq_pending(), "  RTC IRQ not pending anymore");
185 	if (!irq_triggered) {
186 		report_info("  RTC RIS: %"PRIx32, readl(&pl031->ris));
187 		report_info("  RTC MIS: %"PRIx32, readl(&pl031->mis));
188 		report_info("  RTC IMSC: %"PRIx32, readl(&pl031->imsc));
189 		report_info("  GIC IRQs pending: %08"PRIx32" %08"PRIx32, readl(gic_ispendr), readl(gic_ispendr + 4));
190 	}
191 
192 	local_irq_disable();
193 	return 0;
194 }
195 
rtc_irq_init(void)196 static void rtc_irq_init(void)
197 {
198 	gic_enable_defaults();
199 
200 	switch (gic_version()) {
201 	case 2:
202 		gic_ispendr = gicv2_dist_base() + GICD_ISPENDR;
203 		break;
204 	case 3:
205 		gic_ispendr = gicv3_dist_base() + GICD_ISPENDR;
206 		break;
207 	}
208 }
209 
rtc_fdt_init(void)210 static int rtc_fdt_init(void)
211 {
212 	const struct fdt_property *prop;
213 	const void *fdt = dt_fdt();
214 	struct dt_pbus_reg base;
215 	int node, len;
216 	u32 *data;
217 	int ret;
218 
219 	node = fdt_node_offset_by_compatible(fdt, -1, "arm,pl031");
220 	if (node < 0)
221 		return -1;
222 
223 	prop = fdt_get_property(fdt, node, "interrupts", &len);
224 	assert(prop && len == (3 * sizeof(u32)));
225 	data = (u32 *)prop->data;
226 	assert(data[0] == 0); /* SPI */
227 	pl031_irq = SPI(fdt32_to_cpu(data[1]));
228 
229 	ret = dt_pbus_translate_node(node, 0, &base);
230 	assert(!ret);
231 	pl031 = ioremap(base.addr, base.size);
232 
233 	return 0;
234 }
235 
main(int argc,char ** argv)236 int main(int argc, char **argv)
237 {
238 	cntfrq = get_cntfrq();
239 	rtc_irq_init();
240 	if (rtc_fdt_init()) {
241 		report_skip("Skipping PL031 tests. No device present.");
242 		return 0;
243 	}
244 
245 	report_prefix_push("pl031");
246 	report(!check_id(), "Periph/PCell IDs match");
247 	report(!check_ro(), "R/O fields are R/O");
248 	report(!check_rtc_freq(), "RTC ticks at 1HZ");
249 	report(!gic_irq_pending(), "RTC IRQ not pending yet");
250 	check_rtc_irq();
251 
252 	return report_summary();
253 }
254