qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

npcm7xx_watchdog_timer-test.c (10381B)


      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 }