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