1*7d378ed6SHao Wu /* 2*7d378ed6SHao Wu * QTests for Nuvoton NPCM7xx Timer Watchdog Modules. 3*7d378ed6SHao Wu * 4*7d378ed6SHao Wu * Copyright 2020 Google LLC 5*7d378ed6SHao Wu * 6*7d378ed6SHao Wu * This program is free software; you can redistribute it and/or modify it 7*7d378ed6SHao Wu * under the terms of the GNU General Public License as published by the 8*7d378ed6SHao Wu * Free Software Foundation; either version 2 of the License, or 9*7d378ed6SHao Wu * (at your option) any later version. 10*7d378ed6SHao Wu * 11*7d378ed6SHao Wu * This program is distributed in the hope that it will be useful, but WITHOUT 12*7d378ed6SHao Wu * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13*7d378ed6SHao Wu * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14*7d378ed6SHao Wu * for more details. 15*7d378ed6SHao Wu */ 16*7d378ed6SHao Wu 17*7d378ed6SHao Wu #include "qemu/osdep.h" 18*7d378ed6SHao Wu #include "qemu/timer.h" 19*7d378ed6SHao Wu 20*7d378ed6SHao Wu #include "libqos/libqtest.h" 21*7d378ed6SHao Wu #include "qapi/qmp/qdict.h" 22*7d378ed6SHao Wu 23*7d378ed6SHao Wu #define WTCR_OFFSET 0x1c 24*7d378ed6SHao Wu #define REF_HZ (25000000) 25*7d378ed6SHao Wu 26*7d378ed6SHao Wu /* WTCR bit fields */ 27*7d378ed6SHao Wu #define WTCLK(rv) ((rv) << 10) 28*7d378ed6SHao Wu #define WTE BIT(7) 29*7d378ed6SHao Wu #define WTIE BIT(6) 30*7d378ed6SHao Wu #define WTIS(rv) ((rv) << 4) 31*7d378ed6SHao Wu #define WTIF BIT(3) 32*7d378ed6SHao Wu #define WTRF BIT(2) 33*7d378ed6SHao Wu #define WTRE BIT(1) 34*7d378ed6SHao Wu #define WTR BIT(0) 35*7d378ed6SHao Wu 36*7d378ed6SHao Wu typedef struct Watchdog { 37*7d378ed6SHao Wu int irq; 38*7d378ed6SHao Wu uint64_t base_addr; 39*7d378ed6SHao Wu } Watchdog; 40*7d378ed6SHao Wu 41*7d378ed6SHao Wu static const Watchdog watchdog_list[] = { 42*7d378ed6SHao Wu { 43*7d378ed6SHao Wu .irq = 47, 44*7d378ed6SHao Wu .base_addr = 0xf0008000 45*7d378ed6SHao Wu }, 46*7d378ed6SHao Wu { 47*7d378ed6SHao Wu .irq = 48, 48*7d378ed6SHao Wu .base_addr = 0xf0009000 49*7d378ed6SHao Wu }, 50*7d378ed6SHao Wu { 51*7d378ed6SHao Wu .irq = 49, 52*7d378ed6SHao Wu .base_addr = 0xf000a000 53*7d378ed6SHao Wu } 54*7d378ed6SHao Wu }; 55*7d378ed6SHao Wu 56*7d378ed6SHao Wu static int watchdog_index(const Watchdog *wd) 57*7d378ed6SHao Wu { 58*7d378ed6SHao Wu ptrdiff_t diff = wd - watchdog_list; 59*7d378ed6SHao Wu 60*7d378ed6SHao Wu g_assert(diff >= 0 && diff < ARRAY_SIZE(watchdog_list)); 61*7d378ed6SHao Wu 62*7d378ed6SHao Wu return diff; 63*7d378ed6SHao Wu } 64*7d378ed6SHao Wu 65*7d378ed6SHao Wu static uint32_t watchdog_read_wtcr(QTestState *qts, const Watchdog *wd) 66*7d378ed6SHao Wu { 67*7d378ed6SHao Wu return qtest_readl(qts, wd->base_addr + WTCR_OFFSET); 68*7d378ed6SHao Wu } 69*7d378ed6SHao Wu 70*7d378ed6SHao Wu static void watchdog_write_wtcr(QTestState *qts, const Watchdog *wd, 71*7d378ed6SHao Wu uint32_t value) 72*7d378ed6SHao Wu { 73*7d378ed6SHao Wu qtest_writel(qts, wd->base_addr + WTCR_OFFSET, value); 74*7d378ed6SHao Wu } 75*7d378ed6SHao Wu 76*7d378ed6SHao Wu static uint32_t watchdog_prescaler(QTestState *qts, const Watchdog *wd) 77*7d378ed6SHao Wu { 78*7d378ed6SHao Wu switch (extract32(watchdog_read_wtcr(qts, wd), 10, 2)) { 79*7d378ed6SHao Wu case 0: 80*7d378ed6SHao Wu return 1; 81*7d378ed6SHao Wu case 1: 82*7d378ed6SHao Wu return 256; 83*7d378ed6SHao Wu case 2: 84*7d378ed6SHao Wu return 2048; 85*7d378ed6SHao Wu case 3: 86*7d378ed6SHao Wu return 65536; 87*7d378ed6SHao Wu default: 88*7d378ed6SHao Wu g_assert_not_reached(); 89*7d378ed6SHao Wu } 90*7d378ed6SHao Wu } 91*7d378ed6SHao Wu 92*7d378ed6SHao Wu static QDict *get_watchdog_action(QTestState *qts) 93*7d378ed6SHao Wu { 94*7d378ed6SHao Wu QDict *ev = qtest_qmp_eventwait_ref(qts, "WATCHDOG"); 95*7d378ed6SHao Wu QDict *data; 96*7d378ed6SHao Wu 97*7d378ed6SHao Wu data = qdict_get_qdict(ev, "data"); 98*7d378ed6SHao Wu qobject_ref(data); 99*7d378ed6SHao Wu qobject_unref(ev); 100*7d378ed6SHao Wu return data; 101*7d378ed6SHao Wu } 102*7d378ed6SHao Wu 103*7d378ed6SHao Wu #define RESET_CYCLES 1024 104*7d378ed6SHao Wu static uint32_t watchdog_interrupt_cycles(QTestState *qts, const Watchdog *wd) 105*7d378ed6SHao Wu { 106*7d378ed6SHao Wu uint32_t wtis = extract32(watchdog_read_wtcr(qts, wd), 4, 2); 107*7d378ed6SHao Wu return 1 << (14 + 2 * wtis); 108*7d378ed6SHao Wu } 109*7d378ed6SHao Wu 110*7d378ed6SHao Wu static int64_t watchdog_calculate_steps(uint32_t count, uint32_t prescale) 111*7d378ed6SHao Wu { 112*7d378ed6SHao Wu return (NANOSECONDS_PER_SECOND / REF_HZ) * count * prescale; 113*7d378ed6SHao Wu } 114*7d378ed6SHao Wu 115*7d378ed6SHao Wu static int64_t watchdog_interrupt_steps(QTestState *qts, const Watchdog *wd) 116*7d378ed6SHao Wu { 117*7d378ed6SHao Wu return watchdog_calculate_steps(watchdog_interrupt_cycles(qts, wd), 118*7d378ed6SHao Wu watchdog_prescaler(qts, wd)); 119*7d378ed6SHao Wu } 120*7d378ed6SHao Wu 121*7d378ed6SHao Wu /* Check wtcr can be reset to default value */ 122*7d378ed6SHao Wu static void test_init(gconstpointer watchdog) 123*7d378ed6SHao Wu { 124*7d378ed6SHao Wu const Watchdog *wd = watchdog; 125*7d378ed6SHao Wu QTestState *qts = qtest_init("-machine quanta-gsj"); 126*7d378ed6SHao Wu 127*7d378ed6SHao Wu qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); 128*7d378ed6SHao Wu 129*7d378ed6SHao Wu watchdog_write_wtcr(qts, wd, WTCLK(1) | WTRF | WTIF | WTR); 130*7d378ed6SHao Wu g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(1)); 131*7d378ed6SHao Wu 132*7d378ed6SHao Wu qtest_quit(qts); 133*7d378ed6SHao Wu } 134*7d378ed6SHao Wu 135*7d378ed6SHao Wu /* Check a watchdog can generate interrupt and reset actions */ 136*7d378ed6SHao Wu static void test_reset_action(gconstpointer watchdog) 137*7d378ed6SHao Wu { 138*7d378ed6SHao Wu const Watchdog *wd = watchdog; 139*7d378ed6SHao Wu QTestState *qts = qtest_init("-machine quanta-gsj"); 140*7d378ed6SHao Wu QDict *ad; 141*7d378ed6SHao Wu 142*7d378ed6SHao Wu qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); 143*7d378ed6SHao Wu 144*7d378ed6SHao Wu watchdog_write_wtcr(qts, wd, 145*7d378ed6SHao Wu WTCLK(0) | WTE | WTRF | WTRE | WTIF | WTIE | WTR); 146*7d378ed6SHao Wu g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, 147*7d378ed6SHao Wu WTCLK(0) | WTE | WTRE | WTIE); 148*7d378ed6SHao Wu 149*7d378ed6SHao Wu /* Check a watchdog can generate an interrupt */ 150*7d378ed6SHao Wu qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd)); 151*7d378ed6SHao Wu g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, 152*7d378ed6SHao Wu WTCLK(0) | WTE | WTIF | WTIE | WTRE); 153*7d378ed6SHao Wu g_assert_true(qtest_get_irq(qts, wd->irq)); 154*7d378ed6SHao Wu 155*7d378ed6SHao Wu /* Check a watchdog can generate a reset signal */ 156*7d378ed6SHao Wu qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES, 157*7d378ed6SHao Wu watchdog_prescaler(qts, wd))); 158*7d378ed6SHao Wu ad = get_watchdog_action(qts); 159*7d378ed6SHao Wu /* The signal is a reset signal */ 160*7d378ed6SHao Wu g_assert_false(strcmp(qdict_get_str(ad, "action"), "reset")); 161*7d378ed6SHao Wu qobject_unref(ad); 162*7d378ed6SHao Wu qtest_qmp_eventwait(qts, "RESET"); 163*7d378ed6SHao Wu /* 164*7d378ed6SHao Wu * Make sure WTCR is reset to default except for WTRF bit which shouldn't 165*7d378ed6SHao Wu * be reset. 166*7d378ed6SHao Wu */ 167*7d378ed6SHao Wu g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(1) | WTRF); 168*7d378ed6SHao Wu qtest_quit(qts); 169*7d378ed6SHao Wu } 170*7d378ed6SHao Wu 171*7d378ed6SHao Wu /* Check a watchdog works with all possible WTCLK prescalers and WTIS cycles */ 172*7d378ed6SHao Wu static void test_prescaler(gconstpointer watchdog) 173*7d378ed6SHao Wu { 174*7d378ed6SHao Wu const Watchdog *wd = watchdog; 175*7d378ed6SHao Wu 176*7d378ed6SHao Wu for (int wtclk = 0; wtclk < 4; ++wtclk) { 177*7d378ed6SHao Wu for (int wtis = 0; wtis < 4; ++wtis) { 178*7d378ed6SHao Wu QTestState *qts = qtest_init("-machine quanta-gsj"); 179*7d378ed6SHao Wu 180*7d378ed6SHao Wu qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); 181*7d378ed6SHao Wu watchdog_write_wtcr(qts, wd, 182*7d378ed6SHao Wu WTCLK(wtclk) | WTE | WTIF | WTIS(wtis) | WTIE | WTR); 183*7d378ed6SHao Wu /* 184*7d378ed6SHao Wu * The interrupt doesn't fire until watchdog_interrupt_steps() 185*7d378ed6SHao Wu * cycles passed 186*7d378ed6SHao Wu */ 187*7d378ed6SHao Wu qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd) - 1); 188*7d378ed6SHao Wu g_assert_false(watchdog_read_wtcr(qts, wd) & WTIF); 189*7d378ed6SHao Wu g_assert_false(qtest_get_irq(qts, wd->irq)); 190*7d378ed6SHao Wu qtest_clock_step(qts, 1); 191*7d378ed6SHao Wu g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); 192*7d378ed6SHao Wu g_assert_true(qtest_get_irq(qts, wd->irq)); 193*7d378ed6SHao Wu 194*7d378ed6SHao Wu qtest_quit(qts); 195*7d378ed6SHao Wu } 196*7d378ed6SHao Wu } 197*7d378ed6SHao Wu } 198*7d378ed6SHao Wu 199*7d378ed6SHao Wu /* 200*7d378ed6SHao Wu * Check a watchdog doesn't fire if corresponding flags (WTIE and WTRE) are not 201*7d378ed6SHao Wu * set. 202*7d378ed6SHao Wu */ 203*7d378ed6SHao Wu static void test_enabling_flags(gconstpointer watchdog) 204*7d378ed6SHao Wu { 205*7d378ed6SHao Wu const Watchdog *wd = watchdog; 206*7d378ed6SHao Wu QTestState *qts; 207*7d378ed6SHao Wu 208*7d378ed6SHao Wu /* Neither WTIE or WTRE is set, no interrupt or reset should happen */ 209*7d378ed6SHao Wu qts = qtest_init("-machine quanta-gsj"); 210*7d378ed6SHao Wu qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); 211*7d378ed6SHao Wu watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTRF | WTR); 212*7d378ed6SHao Wu qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd)); 213*7d378ed6SHao Wu g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); 214*7d378ed6SHao Wu g_assert_false(qtest_get_irq(qts, wd->irq)); 215*7d378ed6SHao Wu qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES, 216*7d378ed6SHao Wu watchdog_prescaler(qts, wd))); 217*7d378ed6SHao Wu g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); 218*7d378ed6SHao Wu g_assert_false(watchdog_read_wtcr(qts, wd) & WTRF); 219*7d378ed6SHao Wu qtest_quit(qts); 220*7d378ed6SHao Wu 221*7d378ed6SHao Wu /* Only WTIE is set, interrupt is triggered but reset should not happen */ 222*7d378ed6SHao Wu qts = qtest_init("-machine quanta-gsj"); 223*7d378ed6SHao Wu qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); 224*7d378ed6SHao Wu watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTIE | WTRF | WTR); 225*7d378ed6SHao Wu qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd)); 226*7d378ed6SHao Wu g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); 227*7d378ed6SHao Wu g_assert_true(qtest_get_irq(qts, wd->irq)); 228*7d378ed6SHao Wu qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES, 229*7d378ed6SHao Wu watchdog_prescaler(qts, wd))); 230*7d378ed6SHao Wu g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); 231*7d378ed6SHao Wu g_assert_false(watchdog_read_wtcr(qts, wd) & WTRF); 232*7d378ed6SHao Wu qtest_quit(qts); 233*7d378ed6SHao Wu 234*7d378ed6SHao Wu /* Only WTRE is set, interrupt is triggered but reset should not happen */ 235*7d378ed6SHao Wu qts = qtest_init("-machine quanta-gsj"); 236*7d378ed6SHao Wu qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); 237*7d378ed6SHao Wu watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTRE | WTRF | WTR); 238*7d378ed6SHao Wu qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd)); 239*7d378ed6SHao Wu g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); 240*7d378ed6SHao Wu g_assert_false(qtest_get_irq(qts, wd->irq)); 241*7d378ed6SHao Wu qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES, 242*7d378ed6SHao Wu watchdog_prescaler(qts, wd))); 243*7d378ed6SHao Wu g_assert_false(strcmp(qdict_get_str(get_watchdog_action(qts), "action"), 244*7d378ed6SHao Wu "reset")); 245*7d378ed6SHao Wu qtest_qmp_eventwait(qts, "RESET"); 246*7d378ed6SHao Wu qtest_quit(qts); 247*7d378ed6SHao Wu 248*7d378ed6SHao Wu /* 249*7d378ed6SHao Wu * The case when both flags are set is already tested in 250*7d378ed6SHao Wu * test_reset_action(). 251*7d378ed6SHao Wu */ 252*7d378ed6SHao Wu } 253*7d378ed6SHao Wu 254*7d378ed6SHao Wu /* Check a watchdog can pause and resume by setting WTE bits */ 255*7d378ed6SHao Wu static void test_pause(gconstpointer watchdog) 256*7d378ed6SHao Wu { 257*7d378ed6SHao Wu const Watchdog *wd = watchdog; 258*7d378ed6SHao Wu QTestState *qts; 259*7d378ed6SHao Wu int64_t remaining_steps, steps; 260*7d378ed6SHao Wu 261*7d378ed6SHao Wu qts = qtest_init("-machine quanta-gsj"); 262*7d378ed6SHao Wu qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); 263*7d378ed6SHao Wu watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTIE | WTRF | WTR); 264*7d378ed6SHao Wu remaining_steps = watchdog_interrupt_steps(qts, wd); 265*7d378ed6SHao Wu g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTE | WTIE); 266*7d378ed6SHao Wu 267*7d378ed6SHao Wu /* Run for half of the execution period. */ 268*7d378ed6SHao Wu steps = remaining_steps / 2; 269*7d378ed6SHao Wu remaining_steps -= steps; 270*7d378ed6SHao Wu qtest_clock_step(qts, steps); 271*7d378ed6SHao Wu 272*7d378ed6SHao Wu /* Pause the watchdog */ 273*7d378ed6SHao Wu watchdog_write_wtcr(qts, wd, WTCLK(0) | WTIE); 274*7d378ed6SHao Wu g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTIE); 275*7d378ed6SHao Wu 276*7d378ed6SHao Wu /* Run for a long period of time, the watchdog shouldn't fire */ 277*7d378ed6SHao Wu qtest_clock_step(qts, steps << 4); 278*7d378ed6SHao Wu g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTIE); 279*7d378ed6SHao Wu g_assert_false(qtest_get_irq(qts, wd->irq)); 280*7d378ed6SHao Wu 281*7d378ed6SHao Wu /* Resume the watchdog */ 282*7d378ed6SHao Wu watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIE); 283*7d378ed6SHao Wu g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTE | WTIE); 284*7d378ed6SHao Wu 285*7d378ed6SHao Wu /* Run for the reset of the execution period, the watchdog should fire */ 286*7d378ed6SHao Wu qtest_clock_step(qts, remaining_steps); 287*7d378ed6SHao Wu g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, 288*7d378ed6SHao Wu WTCLK(0) | WTE | WTIF | WTIE); 289*7d378ed6SHao Wu g_assert_true(qtest_get_irq(qts, wd->irq)); 290*7d378ed6SHao Wu 291*7d378ed6SHao Wu qtest_quit(qts); 292*7d378ed6SHao Wu } 293*7d378ed6SHao Wu 294*7d378ed6SHao Wu static void watchdog_add_test(const char *name, const Watchdog* wd, 295*7d378ed6SHao Wu GTestDataFunc fn) 296*7d378ed6SHao Wu { 297*7d378ed6SHao Wu g_autofree char *full_name = g_strdup_printf( 298*7d378ed6SHao Wu "npcm7xx_watchdog_timer[%d]/%s", watchdog_index(wd), name); 299*7d378ed6SHao Wu qtest_add_data_func(full_name, wd, fn); 300*7d378ed6SHao Wu } 301*7d378ed6SHao Wu #define add_test(name, td) watchdog_add_test(#name, td, test_##name) 302*7d378ed6SHao Wu 303*7d378ed6SHao Wu int main(int argc, char **argv) 304*7d378ed6SHao Wu { 305*7d378ed6SHao Wu g_test_init(&argc, &argv, NULL); 306*7d378ed6SHao Wu g_test_set_nonfatal_assertions(); 307*7d378ed6SHao Wu 308*7d378ed6SHao Wu for (int i = 0; i < ARRAY_SIZE(watchdog_list); ++i) { 309*7d378ed6SHao Wu const Watchdog *wd = &watchdog_list[i]; 310*7d378ed6SHao Wu 311*7d378ed6SHao Wu add_test(init, wd); 312*7d378ed6SHao Wu add_test(reset_action, wd); 313*7d378ed6SHao Wu add_test(prescaler, wd); 314*7d378ed6SHao Wu add_test(enabling_flags, wd); 315*7d378ed6SHao Wu add_test(pause, wd); 316*7d378ed6SHao Wu } 317*7d378ed6SHao Wu 318*7d378ed6SHao Wu return g_test_run(); 319*7d378ed6SHao Wu } 320