]>
Commit | Line | Data |
---|---|---|
7d378ed6 HW |
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 | ||
907b5105 | 20 | #include "libqtest.h" |
7d378ed6 HW |
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; | |
d9e2da0c | 175 | int inc = g_test_quick() ? 3 : 1; |
7d378ed6 | 176 | |
d9e2da0c TH |
177 | for (int wtclk = 0; wtclk < 4; wtclk += inc) { |
178 | for (int wtis = 0; wtis < 4; wtis += inc) { | |
7d378ed6 HW |
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; | |
7aed584c | 208 | QDict *rsp; |
7d378ed6 HW |
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))); | |
7aed584c CQ |
245 | rsp = get_watchdog_action(qts); |
246 | g_assert_false(strcmp(qdict_get_str(rsp, "action"), "reset")); | |
247 | qobject_unref(rsp); | |
7d378ed6 HW |
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 | } |