xref: /kvm-unit-tests/arm/pl031.c (revision d74708246bd9a593e03ecca476a5f1ed36e47288)
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 void *gic_isenabler;
51 static volatile bool irq_triggered;
52 
53 static int check_id(void)
54 {
55 	uint32_t id[] = { 0x31, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
56 	int i;
57 
58 	for (i = 0; i < ARRAY_SIZE(id); i++)
59 		if (id[i] != readl(&pl031->periph_id[i]))
60 			return 1;
61 
62 	return 0;
63 }
64 
65 static int check_ro(void)
66 {
67 	uint32_t offs[] = { offsetof(struct pl031_regs, ris),
68 			    offsetof(struct pl031_regs, mis),
69 			    offsetof(struct pl031_regs, periph_id[0]),
70 			    offsetof(struct pl031_regs, periph_id[1]),
71 			    offsetof(struct pl031_regs, periph_id[2]),
72 			    offsetof(struct pl031_regs, periph_id[3]),
73 			    offsetof(struct pl031_regs, pcell_id[0]),
74 			    offsetof(struct pl031_regs, pcell_id[1]),
75 			    offsetof(struct pl031_regs, pcell_id[2]),
76 			    offsetof(struct pl031_regs, pcell_id[3]) };
77 	int i;
78 
79 	for (i = 0; i < ARRAY_SIZE(offs); i++) {
80 		uint32_t before32;
81 		uint16_t before16;
82 		uint8_t before8;
83 		void *addr = (void*)pl031 + offs[i];
84 		uint32_t poison = 0xdeadbeefULL;
85 
86 		before8 = readb(addr);
87 		before16 = readw(addr);
88 		before32 = readl(addr);
89 
90 		writeb(poison, addr);
91 		writew(poison, addr);
92 		writel(poison, addr);
93 
94 		if (before8 != readb(addr))
95 			return 1;
96 		if (before16 != readw(addr))
97 			return 1;
98 		if (before32 != readl(addr))
99 			return 1;
100 	}
101 
102 	return 0;
103 }
104 
105 static int check_rtc_freq(void)
106 {
107 	uint32_t seconds_to_wait = 2;
108 	uint32_t before = readl(&pl031->dr);
109 	uint64_t before_tick = get_cntvct();
110 	uint64_t target_tick = before_tick + (cntfrq * seconds_to_wait);
111 
112 	/* Wait for 2 seconds */
113 	while (get_cntvct() < target_tick) ;
114 
115 	if (readl(&pl031->dr) != before + seconds_to_wait)
116 		return 1;
117 
118 	return 0;
119 }
120 
121 static bool gic_irq_pending(void)
122 {
123 	uint32_t offset = (pl031_irq / 32) * 4;
124 
125 	return readl(gic_ispendr + offset) & (1 << (pl031_irq & 31));
126 }
127 
128 static void gic_irq_unmask(void)
129 {
130 	uint32_t offset = (pl031_irq / 32) * 4;
131 
132 	writel(1 << (pl031_irq & 31), gic_isenabler + offset);
133 }
134 
135 static void irq_handler(struct pt_regs *regs)
136 {
137 	u32 irqstat = gic_read_iar();
138 	u32 irqnr = gic_iar_irqnr(irqstat);
139 
140 	gic_write_eoir(irqstat);
141 
142 	if (irqnr == pl031_irq) {
143 		report(readl(&pl031->ris) == 1, "  RTC RIS == 1");
144 		report(readl(&pl031->mis) == 1, "  RTC MIS == 1");
145 
146 		/* Writing one to bit zero should clear IRQ status */
147 		writel(1, &pl031->icr);
148 
149 		report(readl(&pl031->ris) == 0, "  RTC RIS == 0");
150 		report(readl(&pl031->mis) == 0, "  RTC MIS == 0");
151 		irq_triggered = true;
152 	} else {
153 		report_info("Unexpected interrupt: %"PRIu32"\n", irqnr);
154 		return;
155 	}
156 }
157 
158 static int check_rtc_irq(void)
159 {
160 	uint32_t seconds_to_wait = 1;
161 	uint32_t before = readl(&pl031->dr);
162 	uint64_t before_tick = get_cntvct();
163 	uint64_t target_tick = before_tick + (cntfrq * (seconds_to_wait + 1));
164 
165 	report_info("Checking IRQ trigger (MR)");
166 
167 	irq_triggered = false;
168 
169 	/* Fire IRQ in 1 second */
170 	writel(before + seconds_to_wait, &pl031->mr);
171 
172 #ifdef __aarch64__
173 	install_irq_handler(EL1H_IRQ, irq_handler);
174 #else
175 	install_exception_handler(EXCPTN_IRQ, irq_handler);
176 #endif
177 
178 	/* Wait until 2 seconds are over */
179 	while (get_cntvct() < target_tick) ;
180 
181 	report(!gic_irq_pending(), "  RTC IRQ not delivered without mask");
182 
183 	/* Mask the IRQ so that it gets delivered */
184 	writel(1, &pl031->imsc);
185 	report(gic_irq_pending(), "  RTC IRQ pending now");
186 
187 	/* Enable retrieval of IRQ */
188 	gic_irq_unmask();
189 	local_irq_enable();
190 
191 	report(irq_triggered, "  IRQ triggered");
192 	report(!gic_irq_pending(), "  RTC IRQ not pending anymore");
193 	if (!irq_triggered) {
194 		report_info("  RTC RIS: %"PRIx32, readl(&pl031->ris));
195 		report_info("  RTC MIS: %"PRIx32, readl(&pl031->mis));
196 		report_info("  RTC IMSC: %"PRIx32, readl(&pl031->imsc));
197 		report_info("  GIC IRQs pending: %08"PRIx32" %08"PRIx32, readl(gic_ispendr), readl(gic_ispendr + 4));
198 	}
199 
200 	local_irq_disable();
201 	return 0;
202 }
203 
204 static void rtc_irq_init(void)
205 {
206 	gic_enable_defaults();
207 
208 	switch (gic_version()) {
209 	case 2:
210 		gic_ispendr = gicv2_dist_base() + GICD_ISPENDR;
211 		gic_isenabler = gicv2_dist_base() + GICD_ISENABLER;
212 		break;
213 	case 3:
214 		gic_ispendr = gicv3_dist_base() + GICD_ISPENDR;
215 		gic_isenabler = gicv3_dist_base() + GICD_ISENABLER;
216 		break;
217 	}
218 }
219 
220 static int rtc_fdt_init(void)
221 {
222 	const struct fdt_property *prop;
223 	const void *fdt = dt_fdt();
224 	struct dt_pbus_reg base;
225 	int node, len;
226 	u32 *data;
227 	int ret;
228 
229 	node = fdt_node_offset_by_compatible(fdt, -1, "arm,pl031");
230 	if (node < 0)
231 		return -1;
232 
233 	prop = fdt_get_property(fdt, node, "interrupts", &len);
234 	assert(prop && len == (3 * sizeof(u32)));
235 	data = (u32 *)prop->data;
236 	assert(data[0] == 0); /* SPI */
237 	pl031_irq = SPI(fdt32_to_cpu(data[1]));
238 
239 	ret = dt_pbus_translate_node(node, 0, &base);
240 	assert(!ret);
241 	pl031 = ioremap(base.addr, base.size);
242 
243 	return 0;
244 }
245 
246 int main(int argc, char **argv)
247 {
248 	cntfrq = get_cntfrq();
249 	rtc_irq_init();
250 	if (rtc_fdt_init()) {
251 		report_skip("Skipping PL031 tests. No device present.");
252 		return 0;
253 	}
254 
255 	report_prefix_push("pl031");
256 	report(!check_id(), "Periph/PCell IDs match");
257 	report(!check_ro(), "R/O fields are R/O");
258 	report(!check_rtc_freq(), "RTC ticks at 1HZ");
259 	report(!gic_irq_pending(), "RTC IRQ not pending yet");
260 	check_rtc_irq();
261 
262 	return report_summary();
263 }
264