xref: /qemu/tests/qtest/npcm7xx_watchdog_timer-test.c (revision 7d378ed6e3b4a26f4da887fcccc4c6f1db3dcd42)
1*7d378ed6SHao Wu /*
2*7d378ed6SHao Wu  * QTests for Nuvoton NPCM7xx Timer Watchdog Modules.
3*7d378ed6SHao Wu  *
4*7d378ed6SHao Wu  * Copyright 2020 Google LLC
5*7d378ed6SHao Wu  *
6*7d378ed6SHao Wu  * This program is free software; you can redistribute it and/or modify it
7*7d378ed6SHao Wu  * under the terms of the GNU General Public License as published by the
8*7d378ed6SHao Wu  * Free Software Foundation; either version 2 of the License, or
9*7d378ed6SHao Wu  * (at your option) any later version.
10*7d378ed6SHao Wu  *
11*7d378ed6SHao Wu  * This program is distributed in the hope that it will be useful, but WITHOUT
12*7d378ed6SHao Wu  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13*7d378ed6SHao Wu  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14*7d378ed6SHao Wu  * for more details.
15*7d378ed6SHao Wu  */
16*7d378ed6SHao Wu 
17*7d378ed6SHao Wu #include "qemu/osdep.h"
18*7d378ed6SHao Wu #include "qemu/timer.h"
19*7d378ed6SHao Wu 
20*7d378ed6SHao Wu #include "libqos/libqtest.h"
21*7d378ed6SHao Wu #include "qapi/qmp/qdict.h"
22*7d378ed6SHao Wu 
23*7d378ed6SHao Wu #define WTCR_OFFSET     0x1c
24*7d378ed6SHao Wu #define REF_HZ          (25000000)
25*7d378ed6SHao Wu 
26*7d378ed6SHao Wu /* WTCR bit fields */
27*7d378ed6SHao Wu #define WTCLK(rv)       ((rv) << 10)
28*7d378ed6SHao Wu #define WTE             BIT(7)
29*7d378ed6SHao Wu #define WTIE            BIT(6)
30*7d378ed6SHao Wu #define WTIS(rv)        ((rv) << 4)
31*7d378ed6SHao Wu #define WTIF            BIT(3)
32*7d378ed6SHao Wu #define WTRF            BIT(2)
33*7d378ed6SHao Wu #define WTRE            BIT(1)
34*7d378ed6SHao Wu #define WTR             BIT(0)
35*7d378ed6SHao Wu 
36*7d378ed6SHao Wu typedef struct Watchdog {
37*7d378ed6SHao Wu     int irq;
38*7d378ed6SHao Wu     uint64_t base_addr;
39*7d378ed6SHao Wu } Watchdog;
40*7d378ed6SHao Wu 
41*7d378ed6SHao Wu static const Watchdog watchdog_list[] = {
42*7d378ed6SHao Wu     {
43*7d378ed6SHao Wu         .irq        = 47,
44*7d378ed6SHao Wu         .base_addr  = 0xf0008000
45*7d378ed6SHao Wu     },
46*7d378ed6SHao Wu     {
47*7d378ed6SHao Wu         .irq        = 48,
48*7d378ed6SHao Wu         .base_addr  = 0xf0009000
49*7d378ed6SHao Wu     },
50*7d378ed6SHao Wu     {
51*7d378ed6SHao Wu         .irq        = 49,
52*7d378ed6SHao Wu         .base_addr  = 0xf000a000
53*7d378ed6SHao Wu     }
54*7d378ed6SHao Wu };
55*7d378ed6SHao Wu 
56*7d378ed6SHao Wu static int watchdog_index(const Watchdog *wd)
57*7d378ed6SHao Wu {
58*7d378ed6SHao Wu     ptrdiff_t diff = wd - watchdog_list;
59*7d378ed6SHao Wu 
60*7d378ed6SHao Wu     g_assert(diff >= 0 && diff < ARRAY_SIZE(watchdog_list));
61*7d378ed6SHao Wu 
62*7d378ed6SHao Wu     return diff;
63*7d378ed6SHao Wu }
64*7d378ed6SHao Wu 
65*7d378ed6SHao Wu static uint32_t watchdog_read_wtcr(QTestState *qts, const Watchdog *wd)
66*7d378ed6SHao Wu {
67*7d378ed6SHao Wu     return qtest_readl(qts, wd->base_addr + WTCR_OFFSET);
68*7d378ed6SHao Wu }
69*7d378ed6SHao Wu 
70*7d378ed6SHao Wu static void watchdog_write_wtcr(QTestState *qts, const Watchdog *wd,
71*7d378ed6SHao Wu         uint32_t value)
72*7d378ed6SHao Wu {
73*7d378ed6SHao Wu     qtest_writel(qts, wd->base_addr + WTCR_OFFSET, value);
74*7d378ed6SHao Wu }
75*7d378ed6SHao Wu 
76*7d378ed6SHao Wu static uint32_t watchdog_prescaler(QTestState *qts, const Watchdog *wd)
77*7d378ed6SHao Wu {
78*7d378ed6SHao Wu     switch (extract32(watchdog_read_wtcr(qts, wd), 10, 2)) {
79*7d378ed6SHao Wu     case 0:
80*7d378ed6SHao Wu         return 1;
81*7d378ed6SHao Wu     case 1:
82*7d378ed6SHao Wu         return 256;
83*7d378ed6SHao Wu     case 2:
84*7d378ed6SHao Wu         return 2048;
85*7d378ed6SHao Wu     case 3:
86*7d378ed6SHao Wu         return 65536;
87*7d378ed6SHao Wu     default:
88*7d378ed6SHao Wu         g_assert_not_reached();
89*7d378ed6SHao Wu     }
90*7d378ed6SHao Wu }
91*7d378ed6SHao Wu 
92*7d378ed6SHao Wu static QDict *get_watchdog_action(QTestState *qts)
93*7d378ed6SHao Wu {
94*7d378ed6SHao Wu     QDict *ev = qtest_qmp_eventwait_ref(qts, "WATCHDOG");
95*7d378ed6SHao Wu     QDict *data;
96*7d378ed6SHao Wu 
97*7d378ed6SHao Wu     data = qdict_get_qdict(ev, "data");
98*7d378ed6SHao Wu     qobject_ref(data);
99*7d378ed6SHao Wu     qobject_unref(ev);
100*7d378ed6SHao Wu     return data;
101*7d378ed6SHao Wu }
102*7d378ed6SHao Wu 
103*7d378ed6SHao Wu #define RESET_CYCLES 1024
104*7d378ed6SHao Wu static uint32_t watchdog_interrupt_cycles(QTestState *qts, const Watchdog *wd)
105*7d378ed6SHao Wu {
106*7d378ed6SHao Wu     uint32_t wtis = extract32(watchdog_read_wtcr(qts, wd), 4, 2);
107*7d378ed6SHao Wu     return 1 << (14 + 2 * wtis);
108*7d378ed6SHao Wu }
109*7d378ed6SHao Wu 
110*7d378ed6SHao Wu static int64_t watchdog_calculate_steps(uint32_t count, uint32_t prescale)
111*7d378ed6SHao Wu {
112*7d378ed6SHao Wu     return (NANOSECONDS_PER_SECOND / REF_HZ) * count * prescale;
113*7d378ed6SHao Wu }
114*7d378ed6SHao Wu 
115*7d378ed6SHao Wu static int64_t watchdog_interrupt_steps(QTestState *qts, const Watchdog *wd)
116*7d378ed6SHao Wu {
117*7d378ed6SHao Wu     return watchdog_calculate_steps(watchdog_interrupt_cycles(qts, wd),
118*7d378ed6SHao Wu             watchdog_prescaler(qts, wd));
119*7d378ed6SHao Wu }
120*7d378ed6SHao Wu 
121*7d378ed6SHao Wu /* Check wtcr can be reset to default value */
122*7d378ed6SHao Wu static void test_init(gconstpointer watchdog)
123*7d378ed6SHao Wu {
124*7d378ed6SHao Wu     const Watchdog *wd = watchdog;
125*7d378ed6SHao Wu     QTestState *qts = qtest_init("-machine quanta-gsj");
126*7d378ed6SHao Wu 
127*7d378ed6SHao Wu     qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
128*7d378ed6SHao Wu 
129*7d378ed6SHao Wu     watchdog_write_wtcr(qts, wd, WTCLK(1) | WTRF | WTIF | WTR);
130*7d378ed6SHao Wu     g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(1));
131*7d378ed6SHao Wu 
132*7d378ed6SHao Wu     qtest_quit(qts);
133*7d378ed6SHao Wu }
134*7d378ed6SHao Wu 
135*7d378ed6SHao Wu /* Check a watchdog can generate interrupt and reset actions */
136*7d378ed6SHao Wu static void test_reset_action(gconstpointer watchdog)
137*7d378ed6SHao Wu {
138*7d378ed6SHao Wu     const Watchdog *wd = watchdog;
139*7d378ed6SHao Wu     QTestState *qts = qtest_init("-machine quanta-gsj");
140*7d378ed6SHao Wu     QDict *ad;
141*7d378ed6SHao Wu 
142*7d378ed6SHao Wu     qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
143*7d378ed6SHao Wu 
144*7d378ed6SHao Wu     watchdog_write_wtcr(qts, wd,
145*7d378ed6SHao Wu             WTCLK(0) | WTE | WTRF | WTRE | WTIF | WTIE | WTR);
146*7d378ed6SHao Wu     g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==,
147*7d378ed6SHao Wu             WTCLK(0) | WTE | WTRE | WTIE);
148*7d378ed6SHao Wu 
149*7d378ed6SHao Wu     /* Check a watchdog can generate an interrupt */
150*7d378ed6SHao Wu     qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd));
151*7d378ed6SHao Wu     g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==,
152*7d378ed6SHao Wu             WTCLK(0) | WTE | WTIF | WTIE | WTRE);
153*7d378ed6SHao Wu     g_assert_true(qtest_get_irq(qts, wd->irq));
154*7d378ed6SHao Wu 
155*7d378ed6SHao Wu     /* Check a watchdog can generate a reset signal */
156*7d378ed6SHao Wu     qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES,
157*7d378ed6SHao Wu                 watchdog_prescaler(qts, wd)));
158*7d378ed6SHao Wu     ad = get_watchdog_action(qts);
159*7d378ed6SHao Wu     /* The signal is a reset signal */
160*7d378ed6SHao Wu     g_assert_false(strcmp(qdict_get_str(ad, "action"), "reset"));
161*7d378ed6SHao Wu     qobject_unref(ad);
162*7d378ed6SHao Wu     qtest_qmp_eventwait(qts, "RESET");
163*7d378ed6SHao Wu     /*
164*7d378ed6SHao Wu      * Make sure WTCR is reset to default except for WTRF bit which shouldn't
165*7d378ed6SHao Wu      * be reset.
166*7d378ed6SHao Wu      */
167*7d378ed6SHao Wu     g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(1) | WTRF);
168*7d378ed6SHao Wu     qtest_quit(qts);
169*7d378ed6SHao Wu }
170*7d378ed6SHao Wu 
171*7d378ed6SHao Wu /* Check a watchdog works with all possible WTCLK prescalers and WTIS cycles */
172*7d378ed6SHao Wu static void test_prescaler(gconstpointer watchdog)
173*7d378ed6SHao Wu {
174*7d378ed6SHao Wu     const Watchdog *wd = watchdog;
175*7d378ed6SHao Wu 
176*7d378ed6SHao Wu     for (int wtclk = 0; wtclk < 4; ++wtclk) {
177*7d378ed6SHao Wu         for (int wtis = 0; wtis < 4; ++wtis) {
178*7d378ed6SHao Wu             QTestState *qts = qtest_init("-machine quanta-gsj");
179*7d378ed6SHao Wu 
180*7d378ed6SHao Wu             qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
181*7d378ed6SHao Wu             watchdog_write_wtcr(qts, wd,
182*7d378ed6SHao Wu                     WTCLK(wtclk) | WTE | WTIF | WTIS(wtis) | WTIE | WTR);
183*7d378ed6SHao Wu             /*
184*7d378ed6SHao Wu              * The interrupt doesn't fire until watchdog_interrupt_steps()
185*7d378ed6SHao Wu              * cycles passed
186*7d378ed6SHao Wu              */
187*7d378ed6SHao Wu             qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd) - 1);
188*7d378ed6SHao Wu             g_assert_false(watchdog_read_wtcr(qts, wd) & WTIF);
189*7d378ed6SHao Wu             g_assert_false(qtest_get_irq(qts, wd->irq));
190*7d378ed6SHao Wu             qtest_clock_step(qts, 1);
191*7d378ed6SHao Wu             g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
192*7d378ed6SHao Wu             g_assert_true(qtest_get_irq(qts, wd->irq));
193*7d378ed6SHao Wu 
194*7d378ed6SHao Wu             qtest_quit(qts);
195*7d378ed6SHao Wu         }
196*7d378ed6SHao Wu     }
197*7d378ed6SHao Wu }
198*7d378ed6SHao Wu 
199*7d378ed6SHao Wu /*
200*7d378ed6SHao Wu  * Check a watchdog doesn't fire if corresponding flags (WTIE and WTRE) are not
201*7d378ed6SHao Wu  * set.
202*7d378ed6SHao Wu  */
203*7d378ed6SHao Wu static void test_enabling_flags(gconstpointer watchdog)
204*7d378ed6SHao Wu {
205*7d378ed6SHao Wu     const Watchdog *wd = watchdog;
206*7d378ed6SHao Wu     QTestState *qts;
207*7d378ed6SHao Wu 
208*7d378ed6SHao Wu     /* Neither WTIE or WTRE is set, no interrupt or reset should happen */
209*7d378ed6SHao Wu     qts = qtest_init("-machine quanta-gsj");
210*7d378ed6SHao Wu     qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
211*7d378ed6SHao Wu     watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTRF | WTR);
212*7d378ed6SHao Wu     qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd));
213*7d378ed6SHao Wu     g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
214*7d378ed6SHao Wu     g_assert_false(qtest_get_irq(qts, wd->irq));
215*7d378ed6SHao Wu     qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES,
216*7d378ed6SHao Wu                 watchdog_prescaler(qts, wd)));
217*7d378ed6SHao Wu     g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
218*7d378ed6SHao Wu     g_assert_false(watchdog_read_wtcr(qts, wd) & WTRF);
219*7d378ed6SHao Wu     qtest_quit(qts);
220*7d378ed6SHao Wu 
221*7d378ed6SHao Wu     /* Only WTIE is set, interrupt is triggered but reset should not happen */
222*7d378ed6SHao Wu     qts = qtest_init("-machine quanta-gsj");
223*7d378ed6SHao Wu     qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
224*7d378ed6SHao Wu     watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTIE | WTRF | WTR);
225*7d378ed6SHao Wu     qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd));
226*7d378ed6SHao Wu     g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
227*7d378ed6SHao Wu     g_assert_true(qtest_get_irq(qts, wd->irq));
228*7d378ed6SHao Wu     qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES,
229*7d378ed6SHao Wu                 watchdog_prescaler(qts, wd)));
230*7d378ed6SHao Wu     g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
231*7d378ed6SHao Wu     g_assert_false(watchdog_read_wtcr(qts, wd) & WTRF);
232*7d378ed6SHao Wu     qtest_quit(qts);
233*7d378ed6SHao Wu 
234*7d378ed6SHao Wu     /* Only WTRE is set, interrupt is triggered but reset should not happen */
235*7d378ed6SHao Wu     qts = qtest_init("-machine quanta-gsj");
236*7d378ed6SHao Wu     qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
237*7d378ed6SHao Wu     watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTRE | WTRF | WTR);
238*7d378ed6SHao Wu     qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd));
239*7d378ed6SHao Wu     g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
240*7d378ed6SHao Wu     g_assert_false(qtest_get_irq(qts, wd->irq));
241*7d378ed6SHao Wu     qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES,
242*7d378ed6SHao Wu                 watchdog_prescaler(qts, wd)));
243*7d378ed6SHao Wu     g_assert_false(strcmp(qdict_get_str(get_watchdog_action(qts), "action"),
244*7d378ed6SHao Wu                 "reset"));
245*7d378ed6SHao Wu     qtest_qmp_eventwait(qts, "RESET");
246*7d378ed6SHao Wu     qtest_quit(qts);
247*7d378ed6SHao Wu 
248*7d378ed6SHao Wu     /*
249*7d378ed6SHao Wu      * The case when both flags are set is already tested in
250*7d378ed6SHao Wu      * test_reset_action().
251*7d378ed6SHao Wu      */
252*7d378ed6SHao Wu }
253*7d378ed6SHao Wu 
254*7d378ed6SHao Wu /* Check a watchdog can pause and resume by setting WTE bits */
255*7d378ed6SHao Wu static void test_pause(gconstpointer watchdog)
256*7d378ed6SHao Wu {
257*7d378ed6SHao Wu     const Watchdog *wd = watchdog;
258*7d378ed6SHao Wu     QTestState *qts;
259*7d378ed6SHao Wu     int64_t remaining_steps, steps;
260*7d378ed6SHao Wu 
261*7d378ed6SHao Wu     qts = qtest_init("-machine quanta-gsj");
262*7d378ed6SHao Wu     qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
263*7d378ed6SHao Wu     watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTIE | WTRF | WTR);
264*7d378ed6SHao Wu     remaining_steps = watchdog_interrupt_steps(qts, wd);
265*7d378ed6SHao Wu     g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTE | WTIE);
266*7d378ed6SHao Wu 
267*7d378ed6SHao Wu     /* Run for half of the execution period. */
268*7d378ed6SHao Wu     steps = remaining_steps / 2;
269*7d378ed6SHao Wu     remaining_steps -= steps;
270*7d378ed6SHao Wu     qtest_clock_step(qts, steps);
271*7d378ed6SHao Wu 
272*7d378ed6SHao Wu     /* Pause the watchdog */
273*7d378ed6SHao Wu     watchdog_write_wtcr(qts, wd, WTCLK(0) | WTIE);
274*7d378ed6SHao Wu     g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTIE);
275*7d378ed6SHao Wu 
276*7d378ed6SHao Wu     /* Run for a long period of time, the watchdog shouldn't fire */
277*7d378ed6SHao Wu     qtest_clock_step(qts, steps << 4);
278*7d378ed6SHao Wu     g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTIE);
279*7d378ed6SHao Wu     g_assert_false(qtest_get_irq(qts, wd->irq));
280*7d378ed6SHao Wu 
281*7d378ed6SHao Wu     /* Resume the watchdog */
282*7d378ed6SHao Wu     watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIE);
283*7d378ed6SHao Wu     g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTE | WTIE);
284*7d378ed6SHao Wu 
285*7d378ed6SHao Wu     /* Run for the reset of the execution period, the watchdog should fire */
286*7d378ed6SHao Wu     qtest_clock_step(qts, remaining_steps);
287*7d378ed6SHao Wu     g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==,
288*7d378ed6SHao Wu             WTCLK(0) | WTE | WTIF | WTIE);
289*7d378ed6SHao Wu     g_assert_true(qtest_get_irq(qts, wd->irq));
290*7d378ed6SHao Wu 
291*7d378ed6SHao Wu     qtest_quit(qts);
292*7d378ed6SHao Wu }
293*7d378ed6SHao Wu 
294*7d378ed6SHao Wu static void watchdog_add_test(const char *name, const Watchdog* wd,
295*7d378ed6SHao Wu         GTestDataFunc fn)
296*7d378ed6SHao Wu {
297*7d378ed6SHao Wu     g_autofree char *full_name = g_strdup_printf(
298*7d378ed6SHao Wu             "npcm7xx_watchdog_timer[%d]/%s", watchdog_index(wd), name);
299*7d378ed6SHao Wu     qtest_add_data_func(full_name, wd, fn);
300*7d378ed6SHao Wu }
301*7d378ed6SHao Wu #define add_test(name, td) watchdog_add_test(#name, td, test_##name)
302*7d378ed6SHao Wu 
303*7d378ed6SHao Wu int main(int argc, char **argv)
304*7d378ed6SHao Wu {
305*7d378ed6SHao Wu     g_test_init(&argc, &argv, NULL);
306*7d378ed6SHao Wu     g_test_set_nonfatal_assertions();
307*7d378ed6SHao Wu 
308*7d378ed6SHao Wu     for (int i = 0; i < ARRAY_SIZE(watchdog_list); ++i) {
309*7d378ed6SHao Wu         const Watchdog *wd = &watchdog_list[i];
310*7d378ed6SHao Wu 
311*7d378ed6SHao Wu         add_test(init, wd);
312*7d378ed6SHao Wu         add_test(reset_action, wd);
313*7d378ed6SHao Wu         add_test(prescaler, wd);
314*7d378ed6SHao Wu         add_test(enabling_flags, wd);
315*7d378ed6SHao Wu         add_test(pause, wd);
316*7d378ed6SHao Wu     }
317*7d378ed6SHao Wu 
318*7d378ed6SHao Wu     return g_test_run();
319*7d378ed6SHao Wu }
320