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