1*28ac3b10SNicholas Piggin // SPDX-License-Identifier: GPL-2.0-only
2*28ac3b10SNicholas Piggin /*
3*28ac3b10SNicholas Piggin * Test Timebase
4*28ac3b10SNicholas Piggin *
5*28ac3b10SNicholas Piggin * Copyright 2024 Nicholas Piggin, IBM Corp.
6*28ac3b10SNicholas Piggin *
7*28ac3b10SNicholas Piggin * This contains tests of timebase facility, TB, DEC, etc.
8*28ac3b10SNicholas Piggin */
9*28ac3b10SNicholas Piggin #include <libcflat.h>
10*28ac3b10SNicholas Piggin #include <util.h>
11*28ac3b10SNicholas Piggin #include <migrate.h>
12*28ac3b10SNicholas Piggin #include <alloc.h>
13*28ac3b10SNicholas Piggin #include <asm/handlers.h>
14*28ac3b10SNicholas Piggin #include <devicetree.h>
15*28ac3b10SNicholas Piggin #include <asm/hcall.h>
16*28ac3b10SNicholas Piggin #include <asm/processor.h>
17*28ac3b10SNicholas Piggin #include <asm/time.h>
18*28ac3b10SNicholas Piggin #include <asm/barrier.h>
19*28ac3b10SNicholas Piggin
20*28ac3b10SNicholas Piggin static int dec_bits = 0;
21*28ac3b10SNicholas Piggin
cpu_dec_bits(int fdtnode,u64 regval __unused,void * arg __unused)22*28ac3b10SNicholas Piggin static void cpu_dec_bits(int fdtnode, u64 regval __unused, void *arg __unused)
23*28ac3b10SNicholas Piggin {
24*28ac3b10SNicholas Piggin const struct fdt_property *prop;
25*28ac3b10SNicholas Piggin int plen;
26*28ac3b10SNicholas Piggin
27*28ac3b10SNicholas Piggin prop = fdt_get_property(dt_fdt(), fdtnode, "ibm,dec-bits", &plen);
28*28ac3b10SNicholas Piggin if (!prop) {
29*28ac3b10SNicholas Piggin dec_bits = 32;
30*28ac3b10SNicholas Piggin return;
31*28ac3b10SNicholas Piggin }
32*28ac3b10SNicholas Piggin
33*28ac3b10SNicholas Piggin /* Sanity check for the property layout (first two bytes are header) */
34*28ac3b10SNicholas Piggin assert(plen == 4);
35*28ac3b10SNicholas Piggin
36*28ac3b10SNicholas Piggin /* Check all CPU nodes have the same value of dec-bits */
37*28ac3b10SNicholas Piggin if (dec_bits)
38*28ac3b10SNicholas Piggin assert(dec_bits == fdt32_to_cpu(*(uint32_t *)prop->data));
39*28ac3b10SNicholas Piggin else
40*28ac3b10SNicholas Piggin dec_bits = fdt32_to_cpu(*(uint32_t *)prop->data);
41*28ac3b10SNicholas Piggin }
42*28ac3b10SNicholas Piggin
43*28ac3b10SNicholas Piggin /* Check amount of CPUs nodes that have the TM flag */
find_dec_bits(void)44*28ac3b10SNicholas Piggin static int find_dec_bits(void)
45*28ac3b10SNicholas Piggin {
46*28ac3b10SNicholas Piggin int ret;
47*28ac3b10SNicholas Piggin
48*28ac3b10SNicholas Piggin ret = dt_for_each_cpu_node(cpu_dec_bits, NULL);
49*28ac3b10SNicholas Piggin if (ret < 0)
50*28ac3b10SNicholas Piggin return ret;
51*28ac3b10SNicholas Piggin
52*28ac3b10SNicholas Piggin return dec_bits;
53*28ac3b10SNicholas Piggin }
54*28ac3b10SNicholas Piggin
55*28ac3b10SNicholas Piggin
56*28ac3b10SNicholas Piggin static bool do_migrate = false;
57*28ac3b10SNicholas Piggin static volatile bool got_interrupt;
58*28ac3b10SNicholas Piggin static volatile struct pt_regs recorded_regs;
59*28ac3b10SNicholas Piggin
60*28ac3b10SNicholas Piggin static uint64_t dec_max;
61*28ac3b10SNicholas Piggin static uint64_t dec_min;
62*28ac3b10SNicholas Piggin
test_tb(int argc,char ** argv)63*28ac3b10SNicholas Piggin static void test_tb(int argc, char **argv)
64*28ac3b10SNicholas Piggin {
65*28ac3b10SNicholas Piggin uint64_t tb;
66*28ac3b10SNicholas Piggin int i;
67*28ac3b10SNicholas Piggin
68*28ac3b10SNicholas Piggin tb = get_tb();
69*28ac3b10SNicholas Piggin report(get_tb() >= tb, "timebase is not going backwards");
70*28ac3b10SNicholas Piggin if (do_migrate) {
71*28ac3b10SNicholas Piggin tb = get_tb();
72*28ac3b10SNicholas Piggin migrate();
73*28ac3b10SNicholas Piggin report(get_tb() >= tb,
74*28ac3b10SNicholas Piggin "timebase is not going backwards over migration");
75*28ac3b10SNicholas Piggin }
76*28ac3b10SNicholas Piggin
77*28ac3b10SNicholas Piggin for (i = 0; i < 100; i++) {
78*28ac3b10SNicholas Piggin if (get_tb() > tb)
79*28ac3b10SNicholas Piggin break;
80*28ac3b10SNicholas Piggin }
81*28ac3b10SNicholas Piggin report(get_tb() > tb, "timebase is incrementing");
82*28ac3b10SNicholas Piggin }
83*28ac3b10SNicholas Piggin
dec_stop_handler(struct pt_regs * regs,void * data)84*28ac3b10SNicholas Piggin static void dec_stop_handler(struct pt_regs *regs, void *data)
85*28ac3b10SNicholas Piggin {
86*28ac3b10SNicholas Piggin mtspr(SPR_DEC, dec_max);
87*28ac3b10SNicholas Piggin }
88*28ac3b10SNicholas Piggin
dec_handler(struct pt_regs * regs,void * data)89*28ac3b10SNicholas Piggin static void dec_handler(struct pt_regs *regs, void *data)
90*28ac3b10SNicholas Piggin {
91*28ac3b10SNicholas Piggin got_interrupt = true;
92*28ac3b10SNicholas Piggin memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
93*28ac3b10SNicholas Piggin regs->msr &= ~MSR_EE;
94*28ac3b10SNicholas Piggin }
95*28ac3b10SNicholas Piggin
test_dec(int argc,char ** argv)96*28ac3b10SNicholas Piggin static void test_dec(int argc, char **argv)
97*28ac3b10SNicholas Piggin {
98*28ac3b10SNicholas Piggin uint64_t tb1, tb2, dec;
99*28ac3b10SNicholas Piggin int i;
100*28ac3b10SNicholas Piggin
101*28ac3b10SNicholas Piggin handle_exception(0x900, &dec_handler, NULL);
102*28ac3b10SNicholas Piggin
103*28ac3b10SNicholas Piggin for (i = 0; i < 100; i++) {
104*28ac3b10SNicholas Piggin tb1 = get_tb();
105*28ac3b10SNicholas Piggin mtspr(SPR_DEC, dec_max);
106*28ac3b10SNicholas Piggin dec = mfspr(SPR_DEC);
107*28ac3b10SNicholas Piggin tb2 = get_tb();
108*28ac3b10SNicholas Piggin if (tb2 - tb1 < dec_max - dec)
109*28ac3b10SNicholas Piggin break;
110*28ac3b10SNicholas Piggin }
111*28ac3b10SNicholas Piggin /* POWER CPUs can have a slight (few ticks) variation here */
112*28ac3b10SNicholas Piggin report_kfail(!host_is_tcg, tb2 - tb1 >= dec_max - dec,
113*28ac3b10SNicholas Piggin "decrementer remains within TB after mtDEC");
114*28ac3b10SNicholas Piggin
115*28ac3b10SNicholas Piggin tb1 = get_tb();
116*28ac3b10SNicholas Piggin mtspr(SPR_DEC, dec_max);
117*28ac3b10SNicholas Piggin mdelay(1000);
118*28ac3b10SNicholas Piggin dec = mfspr(SPR_DEC);
119*28ac3b10SNicholas Piggin tb2 = get_tb();
120*28ac3b10SNicholas Piggin report(tb2 - tb1 >= dec_max - dec,
121*28ac3b10SNicholas Piggin "decrementer remains within TB after 1s");
122*28ac3b10SNicholas Piggin
123*28ac3b10SNicholas Piggin mtspr(SPR_DEC, dec_max);
124*28ac3b10SNicholas Piggin local_irq_enable();
125*28ac3b10SNicholas Piggin local_irq_disable();
126*28ac3b10SNicholas Piggin if (mfspr(SPR_DEC) <= dec_max) {
127*28ac3b10SNicholas Piggin report(!got_interrupt,
128*28ac3b10SNicholas Piggin "no interrupt on decrementer positive");
129*28ac3b10SNicholas Piggin }
130*28ac3b10SNicholas Piggin got_interrupt = false;
131*28ac3b10SNicholas Piggin
132*28ac3b10SNicholas Piggin mtspr(SPR_DEC, 1);
133*28ac3b10SNicholas Piggin mdelay(100); /* Give the timer a chance to run */
134*28ac3b10SNicholas Piggin if (do_migrate)
135*28ac3b10SNicholas Piggin migrate();
136*28ac3b10SNicholas Piggin local_irq_enable();
137*28ac3b10SNicholas Piggin local_irq_disable();
138*28ac3b10SNicholas Piggin report(got_interrupt, "interrupt on decrementer underflow");
139*28ac3b10SNicholas Piggin got_interrupt = false;
140*28ac3b10SNicholas Piggin
141*28ac3b10SNicholas Piggin if (do_migrate)
142*28ac3b10SNicholas Piggin migrate();
143*28ac3b10SNicholas Piggin local_irq_enable();
144*28ac3b10SNicholas Piggin local_irq_disable();
145*28ac3b10SNicholas Piggin report(got_interrupt, "interrupt on decrementer still underflown");
146*28ac3b10SNicholas Piggin got_interrupt = false;
147*28ac3b10SNicholas Piggin
148*28ac3b10SNicholas Piggin mtspr(SPR_DEC, 0);
149*28ac3b10SNicholas Piggin mdelay(100); /* Give the timer a chance to run */
150*28ac3b10SNicholas Piggin if (do_migrate)
151*28ac3b10SNicholas Piggin migrate();
152*28ac3b10SNicholas Piggin local_irq_enable();
153*28ac3b10SNicholas Piggin local_irq_disable();
154*28ac3b10SNicholas Piggin report(got_interrupt, "DEC deal with set to 0");
155*28ac3b10SNicholas Piggin got_interrupt = false;
156*28ac3b10SNicholas Piggin
157*28ac3b10SNicholas Piggin /* Test for level-triggered decrementer */
158*28ac3b10SNicholas Piggin mtspr(SPR_DEC, -1ULL);
159*28ac3b10SNicholas Piggin if (do_migrate)
160*28ac3b10SNicholas Piggin migrate();
161*28ac3b10SNicholas Piggin local_irq_enable();
162*28ac3b10SNicholas Piggin local_irq_disable();
163*28ac3b10SNicholas Piggin report(got_interrupt, "interrupt on decrementer write MSB");
164*28ac3b10SNicholas Piggin got_interrupt = false;
165*28ac3b10SNicholas Piggin
166*28ac3b10SNicholas Piggin mtspr(SPR_DEC, dec_max);
167*28ac3b10SNicholas Piggin local_irq_enable();
168*28ac3b10SNicholas Piggin if (do_migrate)
169*28ac3b10SNicholas Piggin migrate();
170*28ac3b10SNicholas Piggin mtspr(SPR_DEC, -1);
171*28ac3b10SNicholas Piggin local_irq_disable();
172*28ac3b10SNicholas Piggin report(got_interrupt, "interrupt on decrementer write MSB with irqs on");
173*28ac3b10SNicholas Piggin got_interrupt = false;
174*28ac3b10SNicholas Piggin
175*28ac3b10SNicholas Piggin mtspr(SPR_DEC, dec_min + 1);
176*28ac3b10SNicholas Piggin mdelay(100);
177*28ac3b10SNicholas Piggin local_irq_enable();
178*28ac3b10SNicholas Piggin local_irq_disable();
179*28ac3b10SNicholas Piggin /* TCG does not model this correctly */
180*28ac3b10SNicholas Piggin report_kfail(host_is_tcg, !got_interrupt,
181*28ac3b10SNicholas Piggin "no interrupt after wrap to positive");
182*28ac3b10SNicholas Piggin got_interrupt = false;
183*28ac3b10SNicholas Piggin
184*28ac3b10SNicholas Piggin handle_exception(0x900, NULL, NULL);
185*28ac3b10SNicholas Piggin }
186*28ac3b10SNicholas Piggin
test_hdec(int argc,char ** argv)187*28ac3b10SNicholas Piggin static void test_hdec(int argc, char **argv)
188*28ac3b10SNicholas Piggin {
189*28ac3b10SNicholas Piggin uint64_t tb1, tb2, hdec;
190*28ac3b10SNicholas Piggin
191*28ac3b10SNicholas Piggin if (!machine_is_powernv()) {
192*28ac3b10SNicholas Piggin report_skip("test reqiures powernv machine");
193*28ac3b10SNicholas Piggin return;
194*28ac3b10SNicholas Piggin }
195*28ac3b10SNicholas Piggin
196*28ac3b10SNicholas Piggin handle_exception(0x900, &dec_stop_handler, NULL);
197*28ac3b10SNicholas Piggin handle_exception(0x980, &dec_handler, NULL);
198*28ac3b10SNicholas Piggin
199*28ac3b10SNicholas Piggin mtspr(SPR_HDEC, dec_max);
200*28ac3b10SNicholas Piggin mtspr(SPR_LPCR, mfspr(SPR_LPCR) | LPCR_HDICE);
201*28ac3b10SNicholas Piggin
202*28ac3b10SNicholas Piggin tb1 = get_tb();
203*28ac3b10SNicholas Piggin mtspr(SPR_HDEC, dec_max);
204*28ac3b10SNicholas Piggin hdec = mfspr(SPR_HDEC);
205*28ac3b10SNicholas Piggin tb2 = get_tb();
206*28ac3b10SNicholas Piggin report(tb2 - tb1 >= dec_max - hdec, "hdecrementer remains within TB");
207*28ac3b10SNicholas Piggin
208*28ac3b10SNicholas Piggin tb1 = get_tb();
209*28ac3b10SNicholas Piggin mtspr(SPR_HDEC, dec_max);
210*28ac3b10SNicholas Piggin mdelay(1000);
211*28ac3b10SNicholas Piggin hdec = mfspr(SPR_HDEC);
212*28ac3b10SNicholas Piggin tb2 = get_tb();
213*28ac3b10SNicholas Piggin report(tb2 - tb1 >= dec_max - hdec, "hdecrementer remains within TB after 1s");
214*28ac3b10SNicholas Piggin
215*28ac3b10SNicholas Piggin mtspr(SPR_HDEC, dec_max);
216*28ac3b10SNicholas Piggin local_irq_enable();
217*28ac3b10SNicholas Piggin local_irq_disable();
218*28ac3b10SNicholas Piggin if (mfspr(SPR_HDEC) <= dec_max) {
219*28ac3b10SNicholas Piggin report(!got_interrupt, "no interrupt on decrementer positive");
220*28ac3b10SNicholas Piggin }
221*28ac3b10SNicholas Piggin got_interrupt = false;
222*28ac3b10SNicholas Piggin
223*28ac3b10SNicholas Piggin mtspr(SPR_HDEC, 1);
224*28ac3b10SNicholas Piggin mdelay(100); /* Give the timer a chance to run */
225*28ac3b10SNicholas Piggin if (do_migrate)
226*28ac3b10SNicholas Piggin migrate();
227*28ac3b10SNicholas Piggin /* HDEC is edge triggered so ensure it still fires */
228*28ac3b10SNicholas Piggin mtspr(SPR_HDEC, dec_max);
229*28ac3b10SNicholas Piggin local_irq_enable();
230*28ac3b10SNicholas Piggin local_irq_disable();
231*28ac3b10SNicholas Piggin report(got_interrupt, "interrupt on hdecrementer underflow");
232*28ac3b10SNicholas Piggin got_interrupt = false;
233*28ac3b10SNicholas Piggin
234*28ac3b10SNicholas Piggin if (do_migrate)
235*28ac3b10SNicholas Piggin migrate();
236*28ac3b10SNicholas Piggin local_irq_enable();
237*28ac3b10SNicholas Piggin local_irq_disable();
238*28ac3b10SNicholas Piggin report(!got_interrupt, "no interrupt on hdecrementer still underflown");
239*28ac3b10SNicholas Piggin got_interrupt = false;
240*28ac3b10SNicholas Piggin
241*28ac3b10SNicholas Piggin mtspr(SPR_HDEC, -1ULL);
242*28ac3b10SNicholas Piggin if (do_migrate)
243*28ac3b10SNicholas Piggin migrate();
244*28ac3b10SNicholas Piggin local_irq_enable();
245*28ac3b10SNicholas Piggin local_irq_disable();
246*28ac3b10SNicholas Piggin report(got_interrupt, "no interrupt on hdecrementer underflown write MSB");
247*28ac3b10SNicholas Piggin got_interrupt = false;
248*28ac3b10SNicholas Piggin
249*28ac3b10SNicholas Piggin mtspr(SPR_HDEC, 0);
250*28ac3b10SNicholas Piggin mdelay(100); /* Give the timer a chance to run */
251*28ac3b10SNicholas Piggin if (do_migrate)
252*28ac3b10SNicholas Piggin migrate();
253*28ac3b10SNicholas Piggin /* HDEC is edge triggered so ensure it still fires */
254*28ac3b10SNicholas Piggin mtspr(SPR_HDEC, dec_max);
255*28ac3b10SNicholas Piggin local_irq_enable();
256*28ac3b10SNicholas Piggin local_irq_disable();
257*28ac3b10SNicholas Piggin report(got_interrupt, "HDEC deal with set to 0");
258*28ac3b10SNicholas Piggin got_interrupt = false;
259*28ac3b10SNicholas Piggin
260*28ac3b10SNicholas Piggin mtspr(SPR_HDEC, dec_max);
261*28ac3b10SNicholas Piggin local_irq_enable();
262*28ac3b10SNicholas Piggin if (do_migrate)
263*28ac3b10SNicholas Piggin migrate();
264*28ac3b10SNicholas Piggin mtspr(SPR_HDEC, -1ULL);
265*28ac3b10SNicholas Piggin local_irq_disable();
266*28ac3b10SNicholas Piggin report(got_interrupt, "interrupt on hdecrementer write MSB with irqs on");
267*28ac3b10SNicholas Piggin got_interrupt = false;
268*28ac3b10SNicholas Piggin
269*28ac3b10SNicholas Piggin mtspr(SPR_HDEC, dec_max);
270*28ac3b10SNicholas Piggin got_interrupt = false;
271*28ac3b10SNicholas Piggin mtspr(SPR_HDEC, dec_min + 1);
272*28ac3b10SNicholas Piggin if (do_migrate)
273*28ac3b10SNicholas Piggin migrate();
274*28ac3b10SNicholas Piggin mdelay(100);
275*28ac3b10SNicholas Piggin local_irq_enable();
276*28ac3b10SNicholas Piggin local_irq_disable();
277*28ac3b10SNicholas Piggin report(got_interrupt, "got interrupt after wrap to positive");
278*28ac3b10SNicholas Piggin got_interrupt = false;
279*28ac3b10SNicholas Piggin
280*28ac3b10SNicholas Piggin mtspr(SPR_HDEC, -1ULL);
281*28ac3b10SNicholas Piggin local_irq_enable();
282*28ac3b10SNicholas Piggin local_irq_disable();
283*28ac3b10SNicholas Piggin got_interrupt = false;
284*28ac3b10SNicholas Piggin mtspr(SPR_HDEC, dec_min + 1000000);
285*28ac3b10SNicholas Piggin if (do_migrate)
286*28ac3b10SNicholas Piggin migrate();
287*28ac3b10SNicholas Piggin mdelay(100);
288*28ac3b10SNicholas Piggin mtspr(SPR_HDEC, -1ULL);
289*28ac3b10SNicholas Piggin local_irq_enable();
290*28ac3b10SNicholas Piggin local_irq_disable();
291*28ac3b10SNicholas Piggin report(got_interrupt, "edge re-armed after wrap to positive");
292*28ac3b10SNicholas Piggin got_interrupt = false;
293*28ac3b10SNicholas Piggin
294*28ac3b10SNicholas Piggin mtspr(SPR_LPCR, mfspr(SPR_LPCR) & ~LPCR_HDICE);
295*28ac3b10SNicholas Piggin
296*28ac3b10SNicholas Piggin handle_exception(0x900, NULL, NULL);
297*28ac3b10SNicholas Piggin handle_exception(0x980, NULL, NULL);
298*28ac3b10SNicholas Piggin }
299*28ac3b10SNicholas Piggin
300*28ac3b10SNicholas Piggin struct {
301*28ac3b10SNicholas Piggin const char *name;
302*28ac3b10SNicholas Piggin void (*func)(int argc, char **argv);
303*28ac3b10SNicholas Piggin } hctests[] = {
304*28ac3b10SNicholas Piggin { "tb", test_tb },
305*28ac3b10SNicholas Piggin { "dec", test_dec },
306*28ac3b10SNicholas Piggin { "hdec", test_hdec },
307*28ac3b10SNicholas Piggin { NULL, NULL }
308*28ac3b10SNicholas Piggin };
309*28ac3b10SNicholas Piggin
main(int argc,char ** argv)310*28ac3b10SNicholas Piggin int main(int argc, char **argv)
311*28ac3b10SNicholas Piggin {
312*28ac3b10SNicholas Piggin bool all;
313*28ac3b10SNicholas Piggin int i;
314*28ac3b10SNicholas Piggin
315*28ac3b10SNicholas Piggin all = argc == 1 || !strcmp(argv[1], "all");
316*28ac3b10SNicholas Piggin
317*28ac3b10SNicholas Piggin for (i = 1; i < argc; i++) {
318*28ac3b10SNicholas Piggin if (!strcmp(argv[i], "-w")) {
319*28ac3b10SNicholas Piggin do_migrate = true;
320*28ac3b10SNicholas Piggin if (!all && argc == 2)
321*28ac3b10SNicholas Piggin all = true;
322*28ac3b10SNicholas Piggin }
323*28ac3b10SNicholas Piggin }
324*28ac3b10SNicholas Piggin
325*28ac3b10SNicholas Piggin find_dec_bits();
326*28ac3b10SNicholas Piggin dec_max = (1ULL << (dec_bits - 1)) - 1;
327*28ac3b10SNicholas Piggin dec_min = (1ULL << (dec_bits - 1));
328*28ac3b10SNicholas Piggin
329*28ac3b10SNicholas Piggin if (machine_is_powernv() && dec_bits > 32) {
330*28ac3b10SNicholas Piggin mtspr(SPR_LPCR, mfspr(SPR_LPCR) | LPCR_LD);
331*28ac3b10SNicholas Piggin }
332*28ac3b10SNicholas Piggin
333*28ac3b10SNicholas Piggin report_prefix_push("timebase");
334*28ac3b10SNicholas Piggin
335*28ac3b10SNicholas Piggin for (i = 0; hctests[i].name != NULL; i++) {
336*28ac3b10SNicholas Piggin if (all || strcmp(argv[1], hctests[i].name) == 0) {
337*28ac3b10SNicholas Piggin report_prefix_push(hctests[i].name);
338*28ac3b10SNicholas Piggin hctests[i].func(argc, argv);
339*28ac3b10SNicholas Piggin report_prefix_pop();
340*28ac3b10SNicholas Piggin }
341*28ac3b10SNicholas Piggin }
342*28ac3b10SNicholas Piggin
343*28ac3b10SNicholas Piggin report_prefix_pop();
344*28ac3b10SNicholas Piggin
345*28ac3b10SNicholas Piggin if (machine_is_powernv() && dec_bits > 32) {
346*28ac3b10SNicholas Piggin mtspr(SPR_LPCR, mfspr(SPR_LPCR) & ~LPCR_LD);
347*28ac3b10SNicholas Piggin }
348*28ac3b10SNicholas Piggin
349*28ac3b10SNicholas Piggin return report_summary();
350*28ac3b10SNicholas Piggin }
351