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 176 for (int wtclk = 0; wtclk < 4; ++wtclk) { 177 for (int wtis = 0; wtis < 4; ++wtis) { 178 QTestState *qts = qtest_init("-machine quanta-gsj"); 179 180 qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); 181 watchdog_write_wtcr(qts, wd, 182 WTCLK(wtclk) | WTE | WTIF | WTIS(wtis) | WTIE | WTR); 183 /* 184 * The interrupt doesn't fire until watchdog_interrupt_steps() 185 * cycles passed 186 */ 187 qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd) - 1); 188 g_assert_false(watchdog_read_wtcr(qts, wd) & WTIF); 189 g_assert_false(qtest_get_irq(qts, wd->irq)); 190 qtest_clock_step(qts, 1); 191 g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); 192 g_assert_true(qtest_get_irq(qts, wd->irq)); 193 194 qtest_quit(qts); 195 } 196 } 197 } 198 199 /* 200 * Check a watchdog doesn't fire if corresponding flags (WTIE and WTRE) are not 201 * set. 202 */ 203 static void test_enabling_flags(gconstpointer watchdog) 204 { 205 const Watchdog *wd = watchdog; 206 QTestState *qts; 207 QDict *rsp; 208 209 /* Neither WTIE or WTRE is set, no interrupt or reset should happen */ 210 qts = qtest_init("-machine quanta-gsj"); 211 qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); 212 watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTRF | WTR); 213 qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd)); 214 g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); 215 g_assert_false(qtest_get_irq(qts, wd->irq)); 216 qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES, 217 watchdog_prescaler(qts, wd))); 218 g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); 219 g_assert_false(watchdog_read_wtcr(qts, wd) & WTRF); 220 qtest_quit(qts); 221 222 /* Only WTIE is set, interrupt is triggered but reset should not happen */ 223 qts = qtest_init("-machine quanta-gsj"); 224 qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); 225 watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTIE | WTRF | WTR); 226 qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd)); 227 g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); 228 g_assert_true(qtest_get_irq(qts, wd->irq)); 229 qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES, 230 watchdog_prescaler(qts, wd))); 231 g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); 232 g_assert_false(watchdog_read_wtcr(qts, wd) & WTRF); 233 qtest_quit(qts); 234 235 /* Only WTRE is set, interrupt is triggered but reset should not happen */ 236 qts = qtest_init("-machine quanta-gsj"); 237 qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); 238 watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTRE | WTRF | WTR); 239 qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd)); 240 g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); 241 g_assert_false(qtest_get_irq(qts, wd->irq)); 242 qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES, 243 watchdog_prescaler(qts, wd))); 244 rsp = get_watchdog_action(qts); 245 g_assert_false(strcmp(qdict_get_str(rsp, "action"), "reset")); 246 qobject_unref(rsp); 247 qtest_qmp_eventwait(qts, "RESET"); 248 qtest_quit(qts); 249 250 /* 251 * The case when both flags are set is already tested in 252 * test_reset_action(). 253 */ 254 } 255 256 /* Check a watchdog can pause and resume by setting WTE bits */ 257 static void test_pause(gconstpointer watchdog) 258 { 259 const Watchdog *wd = watchdog; 260 QTestState *qts; 261 int64_t remaining_steps, steps; 262 263 qts = qtest_init("-machine quanta-gsj"); 264 qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); 265 watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTIE | WTRF | WTR); 266 remaining_steps = watchdog_interrupt_steps(qts, wd); 267 g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTE | WTIE); 268 269 /* Run for half of the execution period. */ 270 steps = remaining_steps / 2; 271 remaining_steps -= steps; 272 qtest_clock_step(qts, steps); 273 274 /* Pause the watchdog */ 275 watchdog_write_wtcr(qts, wd, WTCLK(0) | WTIE); 276 g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTIE); 277 278 /* Run for a long period of time, the watchdog shouldn't fire */ 279 qtest_clock_step(qts, steps << 4); 280 g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTIE); 281 g_assert_false(qtest_get_irq(qts, wd->irq)); 282 283 /* Resume the watchdog */ 284 watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIE); 285 g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTE | WTIE); 286 287 /* Run for the reset of the execution period, the watchdog should fire */ 288 qtest_clock_step(qts, remaining_steps); 289 g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, 290 WTCLK(0) | WTE | WTIF | WTIE); 291 g_assert_true(qtest_get_irq(qts, wd->irq)); 292 293 qtest_quit(qts); 294 } 295 296 static void watchdog_add_test(const char *name, const Watchdog* wd, 297 GTestDataFunc fn) 298 { 299 g_autofree char *full_name = g_strdup_printf( 300 "npcm7xx_watchdog_timer[%d]/%s", watchdog_index(wd), name); 301 qtest_add_data_func(full_name, wd, fn); 302 } 303 #define add_test(name, td) watchdog_add_test(#name, td, test_##name) 304 305 int main(int argc, char **argv) 306 { 307 g_test_init(&argc, &argv, NULL); 308 g_test_set_nonfatal_assertions(); 309 310 for (int i = 0; i < ARRAY_SIZE(watchdog_list); ++i) { 311 const Watchdog *wd = &watchdog_list[i]; 312 313 add_test(init, wd); 314 add_test(reset_action, wd); 315 add_test(prescaler, wd); 316 add_test(enabling_flags, wd); 317 add_test(pause, wd); 318 } 319 320 return g_test_run(); 321 } 322