17d378ed6SHao Wu /* 27d378ed6SHao Wu * QTests for Nuvoton NPCM7xx Timer Watchdog Modules. 37d378ed6SHao Wu * 47d378ed6SHao Wu * Copyright 2020 Google LLC 57d378ed6SHao Wu * 67d378ed6SHao Wu * This program is free software; you can redistribute it and/or modify it 77d378ed6SHao Wu * under the terms of the GNU General Public License as published by the 87d378ed6SHao Wu * Free Software Foundation; either version 2 of the License, or 97d378ed6SHao Wu * (at your option) any later version. 107d378ed6SHao Wu * 117d378ed6SHao Wu * This program is distributed in the hope that it will be useful, but WITHOUT 127d378ed6SHao Wu * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 137d378ed6SHao Wu * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 147d378ed6SHao Wu * for more details. 157d378ed6SHao Wu */ 167d378ed6SHao Wu 177d378ed6SHao Wu #include "qemu/osdep.h" 187d378ed6SHao Wu #include "qemu/timer.h" 197d378ed6SHao Wu 20*907b5105SMarc-André Lureau #include "libqtest.h" 217d378ed6SHao Wu #include "qapi/qmp/qdict.h" 227d378ed6SHao Wu 237d378ed6SHao Wu #define WTCR_OFFSET 0x1c 247d378ed6SHao Wu #define REF_HZ (25000000) 257d378ed6SHao Wu 267d378ed6SHao Wu /* WTCR bit fields */ 277d378ed6SHao Wu #define WTCLK(rv) ((rv) << 10) 287d378ed6SHao Wu #define WTE BIT(7) 297d378ed6SHao Wu #define WTIE BIT(6) 307d378ed6SHao Wu #define WTIS(rv) ((rv) << 4) 317d378ed6SHao Wu #define WTIF BIT(3) 327d378ed6SHao Wu #define WTRF BIT(2) 337d378ed6SHao Wu #define WTRE BIT(1) 347d378ed6SHao Wu #define WTR BIT(0) 357d378ed6SHao Wu 367d378ed6SHao Wu typedef struct Watchdog { 377d378ed6SHao Wu int irq; 387d378ed6SHao Wu uint64_t base_addr; 397d378ed6SHao Wu } Watchdog; 407d378ed6SHao Wu 417d378ed6SHao Wu static const Watchdog watchdog_list[] = { 427d378ed6SHao Wu { 437d378ed6SHao Wu .irq = 47, 447d378ed6SHao Wu .base_addr = 0xf0008000 457d378ed6SHao Wu }, 467d378ed6SHao Wu { 477d378ed6SHao Wu .irq = 48, 487d378ed6SHao Wu .base_addr = 0xf0009000 497d378ed6SHao Wu }, 507d378ed6SHao Wu { 517d378ed6SHao Wu .irq = 49, 527d378ed6SHao Wu .base_addr = 0xf000a000 537d378ed6SHao Wu } 547d378ed6SHao Wu }; 557d378ed6SHao Wu 567d378ed6SHao Wu static int watchdog_index(const Watchdog *wd) 577d378ed6SHao Wu { 587d378ed6SHao Wu ptrdiff_t diff = wd - watchdog_list; 597d378ed6SHao Wu 607d378ed6SHao Wu g_assert(diff >= 0 && diff < ARRAY_SIZE(watchdog_list)); 617d378ed6SHao Wu 627d378ed6SHao Wu return diff; 637d378ed6SHao Wu } 647d378ed6SHao Wu 657d378ed6SHao Wu static uint32_t watchdog_read_wtcr(QTestState *qts, const Watchdog *wd) 667d378ed6SHao Wu { 677d378ed6SHao Wu return qtest_readl(qts, wd->base_addr + WTCR_OFFSET); 687d378ed6SHao Wu } 697d378ed6SHao Wu 707d378ed6SHao Wu static void watchdog_write_wtcr(QTestState *qts, const Watchdog *wd, 717d378ed6SHao Wu uint32_t value) 727d378ed6SHao Wu { 737d378ed6SHao Wu qtest_writel(qts, wd->base_addr + WTCR_OFFSET, value); 747d378ed6SHao Wu } 757d378ed6SHao Wu 767d378ed6SHao Wu static uint32_t watchdog_prescaler(QTestState *qts, const Watchdog *wd) 777d378ed6SHao Wu { 787d378ed6SHao Wu switch (extract32(watchdog_read_wtcr(qts, wd), 10, 2)) { 797d378ed6SHao Wu case 0: 807d378ed6SHao Wu return 1; 817d378ed6SHao Wu case 1: 827d378ed6SHao Wu return 256; 837d378ed6SHao Wu case 2: 847d378ed6SHao Wu return 2048; 857d378ed6SHao Wu case 3: 867d378ed6SHao Wu return 65536; 877d378ed6SHao Wu default: 887d378ed6SHao Wu g_assert_not_reached(); 897d378ed6SHao Wu } 907d378ed6SHao Wu } 917d378ed6SHao Wu 927d378ed6SHao Wu static QDict *get_watchdog_action(QTestState *qts) 937d378ed6SHao Wu { 947d378ed6SHao Wu QDict *ev = qtest_qmp_eventwait_ref(qts, "WATCHDOG"); 957d378ed6SHao Wu QDict *data; 967d378ed6SHao Wu 977d378ed6SHao Wu data = qdict_get_qdict(ev, "data"); 987d378ed6SHao Wu qobject_ref(data); 997d378ed6SHao Wu qobject_unref(ev); 1007d378ed6SHao Wu return data; 1017d378ed6SHao Wu } 1027d378ed6SHao Wu 1037d378ed6SHao Wu #define RESET_CYCLES 1024 1047d378ed6SHao Wu static uint32_t watchdog_interrupt_cycles(QTestState *qts, const Watchdog *wd) 1057d378ed6SHao Wu { 1067d378ed6SHao Wu uint32_t wtis = extract32(watchdog_read_wtcr(qts, wd), 4, 2); 1077d378ed6SHao Wu return 1 << (14 + 2 * wtis); 1087d378ed6SHao Wu } 1097d378ed6SHao Wu 1107d378ed6SHao Wu static int64_t watchdog_calculate_steps(uint32_t count, uint32_t prescale) 1117d378ed6SHao Wu { 1127d378ed6SHao Wu return (NANOSECONDS_PER_SECOND / REF_HZ) * count * prescale; 1137d378ed6SHao Wu } 1147d378ed6SHao Wu 1157d378ed6SHao Wu static int64_t watchdog_interrupt_steps(QTestState *qts, const Watchdog *wd) 1167d378ed6SHao Wu { 1177d378ed6SHao Wu return watchdog_calculate_steps(watchdog_interrupt_cycles(qts, wd), 1187d378ed6SHao Wu watchdog_prescaler(qts, wd)); 1197d378ed6SHao Wu } 1207d378ed6SHao Wu 1217d378ed6SHao Wu /* Check wtcr can be reset to default value */ 1227d378ed6SHao Wu static void test_init(gconstpointer watchdog) 1237d378ed6SHao Wu { 1247d378ed6SHao Wu const Watchdog *wd = watchdog; 1257d378ed6SHao Wu QTestState *qts = qtest_init("-machine quanta-gsj"); 1267d378ed6SHao Wu 1277d378ed6SHao Wu qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); 1287d378ed6SHao Wu 1297d378ed6SHao Wu watchdog_write_wtcr(qts, wd, WTCLK(1) | WTRF | WTIF | WTR); 1307d378ed6SHao Wu g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(1)); 1317d378ed6SHao Wu 1327d378ed6SHao Wu qtest_quit(qts); 1337d378ed6SHao Wu } 1347d378ed6SHao Wu 1357d378ed6SHao Wu /* Check a watchdog can generate interrupt and reset actions */ 1367d378ed6SHao Wu static void test_reset_action(gconstpointer watchdog) 1377d378ed6SHao Wu { 1387d378ed6SHao Wu const Watchdog *wd = watchdog; 1397d378ed6SHao Wu QTestState *qts = qtest_init("-machine quanta-gsj"); 1407d378ed6SHao Wu QDict *ad; 1417d378ed6SHao Wu 1427d378ed6SHao Wu qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); 1437d378ed6SHao Wu 1447d378ed6SHao Wu watchdog_write_wtcr(qts, wd, 1457d378ed6SHao Wu WTCLK(0) | WTE | WTRF | WTRE | WTIF | WTIE | WTR); 1467d378ed6SHao Wu g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, 1477d378ed6SHao Wu WTCLK(0) | WTE | WTRE | WTIE); 1487d378ed6SHao Wu 1497d378ed6SHao Wu /* Check a watchdog can generate an interrupt */ 1507d378ed6SHao Wu qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd)); 1517d378ed6SHao Wu g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, 1527d378ed6SHao Wu WTCLK(0) | WTE | WTIF | WTIE | WTRE); 1537d378ed6SHao Wu g_assert_true(qtest_get_irq(qts, wd->irq)); 1547d378ed6SHao Wu 1557d378ed6SHao Wu /* Check a watchdog can generate a reset signal */ 1567d378ed6SHao Wu qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES, 1577d378ed6SHao Wu watchdog_prescaler(qts, wd))); 1587d378ed6SHao Wu ad = get_watchdog_action(qts); 1597d378ed6SHao Wu /* The signal is a reset signal */ 1607d378ed6SHao Wu g_assert_false(strcmp(qdict_get_str(ad, "action"), "reset")); 1617d378ed6SHao Wu qobject_unref(ad); 1627d378ed6SHao Wu qtest_qmp_eventwait(qts, "RESET"); 1637d378ed6SHao Wu /* 1647d378ed6SHao Wu * Make sure WTCR is reset to default except for WTRF bit which shouldn't 1657d378ed6SHao Wu * be reset. 1667d378ed6SHao Wu */ 1677d378ed6SHao Wu g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(1) | WTRF); 1687d378ed6SHao Wu qtest_quit(qts); 1697d378ed6SHao Wu } 1707d378ed6SHao Wu 1717d378ed6SHao Wu /* Check a watchdog works with all possible WTCLK prescalers and WTIS cycles */ 1727d378ed6SHao Wu static void test_prescaler(gconstpointer watchdog) 1737d378ed6SHao Wu { 1747d378ed6SHao Wu const Watchdog *wd = watchdog; 1757d378ed6SHao Wu 1767d378ed6SHao Wu for (int wtclk = 0; wtclk < 4; ++wtclk) { 1777d378ed6SHao Wu for (int wtis = 0; wtis < 4; ++wtis) { 1787d378ed6SHao Wu QTestState *qts = qtest_init("-machine quanta-gsj"); 1797d378ed6SHao Wu 1807d378ed6SHao Wu qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); 1817d378ed6SHao Wu watchdog_write_wtcr(qts, wd, 1827d378ed6SHao Wu WTCLK(wtclk) | WTE | WTIF | WTIS(wtis) | WTIE | WTR); 1837d378ed6SHao Wu /* 1847d378ed6SHao Wu * The interrupt doesn't fire until watchdog_interrupt_steps() 1857d378ed6SHao Wu * cycles passed 1867d378ed6SHao Wu */ 1877d378ed6SHao Wu qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd) - 1); 1887d378ed6SHao Wu g_assert_false(watchdog_read_wtcr(qts, wd) & WTIF); 1897d378ed6SHao Wu g_assert_false(qtest_get_irq(qts, wd->irq)); 1907d378ed6SHao Wu qtest_clock_step(qts, 1); 1917d378ed6SHao Wu g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); 1927d378ed6SHao Wu g_assert_true(qtest_get_irq(qts, wd->irq)); 1937d378ed6SHao Wu 1947d378ed6SHao Wu qtest_quit(qts); 1957d378ed6SHao Wu } 1967d378ed6SHao Wu } 1977d378ed6SHao Wu } 1987d378ed6SHao Wu 1997d378ed6SHao Wu /* 2007d378ed6SHao Wu * Check a watchdog doesn't fire if corresponding flags (WTIE and WTRE) are not 2017d378ed6SHao Wu * set. 2027d378ed6SHao Wu */ 2037d378ed6SHao Wu static void test_enabling_flags(gconstpointer watchdog) 2047d378ed6SHao Wu { 2057d378ed6SHao Wu const Watchdog *wd = watchdog; 2067d378ed6SHao Wu QTestState *qts; 2077aed584cSChen Qun QDict *rsp; 2087d378ed6SHao Wu 2097d378ed6SHao Wu /* Neither WTIE or WTRE is set, no interrupt or reset should happen */ 2107d378ed6SHao Wu qts = qtest_init("-machine quanta-gsj"); 2117d378ed6SHao Wu qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); 2127d378ed6SHao Wu watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTRF | WTR); 2137d378ed6SHao Wu qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd)); 2147d378ed6SHao Wu g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); 2157d378ed6SHao Wu g_assert_false(qtest_get_irq(qts, wd->irq)); 2167d378ed6SHao Wu qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES, 2177d378ed6SHao Wu watchdog_prescaler(qts, wd))); 2187d378ed6SHao Wu g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); 2197d378ed6SHao Wu g_assert_false(watchdog_read_wtcr(qts, wd) & WTRF); 2207d378ed6SHao Wu qtest_quit(qts); 2217d378ed6SHao Wu 2227d378ed6SHao Wu /* Only WTIE is set, interrupt is triggered but reset should not happen */ 2237d378ed6SHao Wu qts = qtest_init("-machine quanta-gsj"); 2247d378ed6SHao Wu qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); 2257d378ed6SHao Wu watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTIE | WTRF | WTR); 2267d378ed6SHao Wu qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd)); 2277d378ed6SHao Wu g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); 2287d378ed6SHao Wu g_assert_true(qtest_get_irq(qts, wd->irq)); 2297d378ed6SHao Wu qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES, 2307d378ed6SHao Wu watchdog_prescaler(qts, wd))); 2317d378ed6SHao Wu g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); 2327d378ed6SHao Wu g_assert_false(watchdog_read_wtcr(qts, wd) & WTRF); 2337d378ed6SHao Wu qtest_quit(qts); 2347d378ed6SHao Wu 2357d378ed6SHao Wu /* Only WTRE is set, interrupt is triggered but reset should not happen */ 2367d378ed6SHao Wu qts = qtest_init("-machine quanta-gsj"); 2377d378ed6SHao Wu qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); 2387d378ed6SHao Wu watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTRE | WTRF | WTR); 2397d378ed6SHao Wu qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd)); 2407d378ed6SHao Wu g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF); 2417d378ed6SHao Wu g_assert_false(qtest_get_irq(qts, wd->irq)); 2427d378ed6SHao Wu qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES, 2437d378ed6SHao Wu watchdog_prescaler(qts, wd))); 2447aed584cSChen Qun rsp = get_watchdog_action(qts); 2457aed584cSChen Qun g_assert_false(strcmp(qdict_get_str(rsp, "action"), "reset")); 2467aed584cSChen Qun qobject_unref(rsp); 2477d378ed6SHao Wu qtest_qmp_eventwait(qts, "RESET"); 2487d378ed6SHao Wu qtest_quit(qts); 2497d378ed6SHao Wu 2507d378ed6SHao Wu /* 2517d378ed6SHao Wu * The case when both flags are set is already tested in 2527d378ed6SHao Wu * test_reset_action(). 2537d378ed6SHao Wu */ 2547d378ed6SHao Wu } 2557d378ed6SHao Wu 2567d378ed6SHao Wu /* Check a watchdog can pause and resume by setting WTE bits */ 2577d378ed6SHao Wu static void test_pause(gconstpointer watchdog) 2587d378ed6SHao Wu { 2597d378ed6SHao Wu const Watchdog *wd = watchdog; 2607d378ed6SHao Wu QTestState *qts; 2617d378ed6SHao Wu int64_t remaining_steps, steps; 2627d378ed6SHao Wu 2637d378ed6SHao Wu qts = qtest_init("-machine quanta-gsj"); 2647d378ed6SHao Wu qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); 2657d378ed6SHao Wu watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTIE | WTRF | WTR); 2667d378ed6SHao Wu remaining_steps = watchdog_interrupt_steps(qts, wd); 2677d378ed6SHao Wu g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTE | WTIE); 2687d378ed6SHao Wu 2697d378ed6SHao Wu /* Run for half of the execution period. */ 2707d378ed6SHao Wu steps = remaining_steps / 2; 2717d378ed6SHao Wu remaining_steps -= steps; 2727d378ed6SHao Wu qtest_clock_step(qts, steps); 2737d378ed6SHao Wu 2747d378ed6SHao Wu /* Pause the watchdog */ 2757d378ed6SHao Wu watchdog_write_wtcr(qts, wd, WTCLK(0) | WTIE); 2767d378ed6SHao Wu g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTIE); 2777d378ed6SHao Wu 2787d378ed6SHao Wu /* Run for a long period of time, the watchdog shouldn't fire */ 2797d378ed6SHao Wu qtest_clock_step(qts, steps << 4); 2807d378ed6SHao Wu g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTIE); 2817d378ed6SHao Wu g_assert_false(qtest_get_irq(qts, wd->irq)); 2827d378ed6SHao Wu 2837d378ed6SHao Wu /* Resume the watchdog */ 2847d378ed6SHao Wu watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIE); 2857d378ed6SHao Wu g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTE | WTIE); 2867d378ed6SHao Wu 2877d378ed6SHao Wu /* Run for the reset of the execution period, the watchdog should fire */ 2887d378ed6SHao Wu qtest_clock_step(qts, remaining_steps); 2897d378ed6SHao Wu g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, 2907d378ed6SHao Wu WTCLK(0) | WTE | WTIF | WTIE); 2917d378ed6SHao Wu g_assert_true(qtest_get_irq(qts, wd->irq)); 2927d378ed6SHao Wu 2937d378ed6SHao Wu qtest_quit(qts); 2947d378ed6SHao Wu } 2957d378ed6SHao Wu 2967d378ed6SHao Wu static void watchdog_add_test(const char *name, const Watchdog* wd, 2977d378ed6SHao Wu GTestDataFunc fn) 2987d378ed6SHao Wu { 2997d378ed6SHao Wu g_autofree char *full_name = g_strdup_printf( 3007d378ed6SHao Wu "npcm7xx_watchdog_timer[%d]/%s", watchdog_index(wd), name); 3017d378ed6SHao Wu qtest_add_data_func(full_name, wd, fn); 3027d378ed6SHao Wu } 3037d378ed6SHao Wu #define add_test(name, td) watchdog_add_test(#name, td, test_##name) 3047d378ed6SHao Wu 3057d378ed6SHao Wu int main(int argc, char **argv) 3067d378ed6SHao Wu { 3077d378ed6SHao Wu g_test_init(&argc, &argv, NULL); 3087d378ed6SHao Wu g_test_set_nonfatal_assertions(); 3097d378ed6SHao Wu 3107d378ed6SHao Wu for (int i = 0; i < ARRAY_SIZE(watchdog_list); ++i) { 3117d378ed6SHao Wu const Watchdog *wd = &watchdog_list[i]; 3127d378ed6SHao Wu 3137d378ed6SHao Wu add_test(init, wd); 3147d378ed6SHao Wu add_test(reset_action, wd); 3157d378ed6SHao Wu add_test(prescaler, wd); 3167d378ed6SHao Wu add_test(enabling_flags, wd); 3177d378ed6SHao Wu add_test(pause, wd); 3187d378ed6SHao Wu } 3197d378ed6SHao Wu 3207d378ed6SHao Wu return g_test_run(); 3217d378ed6SHao Wu } 322