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
watchdog_index(const Watchdog * wd)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
watchdog_read_wtcr(QTestState * qts,const Watchdog * wd)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
watchdog_write_wtcr(QTestState * qts,const Watchdog * wd,uint32_t value)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
watchdog_prescaler(QTestState * qts,const Watchdog * wd)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
get_watchdog_action(QTestState * qts)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
watchdog_interrupt_cycles(QTestState * qts,const Watchdog * wd)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
watchdog_calculate_steps(uint32_t count,uint32_t prescale)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
watchdog_interrupt_steps(QTestState * qts,const Watchdog * wd)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 */
test_init(gconstpointer watchdog)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 */
test_reset_action(gconstpointer watchdog)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 */
test_prescaler(gconstpointer watchdog)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 */
test_enabling_flags(gconstpointer watchdog)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 */
test_pause(gconstpointer watchdog)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
watchdog_add_test(const char * name,const Watchdog * wd,GTestDataFunc fn)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
main(int argc,char ** argv)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