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