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 
watchdog_index(const Watchdog * wd)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 
watchdog_read_wtcr(QTestState * qts,const Watchdog * wd)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 
watchdog_write_wtcr(QTestState * qts,const Watchdog * wd,uint32_t value)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 
watchdog_prescaler(QTestState * qts,const Watchdog * wd)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 
get_watchdog_action(QTestState * qts)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
watchdog_interrupt_cycles(QTestState * qts,const Watchdog * wd)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 
watchdog_calculate_steps(uint32_t count,uint32_t prescale)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 
watchdog_interrupt_steps(QTestState * qts,const Watchdog * wd)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 */
test_init(gconstpointer watchdog)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 */
test_reset_action(gconstpointer watchdog)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 */
test_prescaler(gconstpointer watchdog)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  */
test_enabling_flags(gconstpointer watchdog)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 */
test_pause(gconstpointer watchdog)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 
watchdog_add_test(const char * name,const Watchdog * wd,GTestDataFunc fn)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 
main(int argc,char ** argv)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