qemu/tests/qtest/npcm7xx_watchdog_timer-test.c
<<
>>
Prefs
   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
  36typedef struct Watchdog {
  37    int irq;
  38    uint64_t base_addr;
  39} Watchdog;
  40
  41static 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
  56static 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
  65static uint32_t watchdog_read_wtcr(QTestState *qts, const Watchdog *wd)
  66{
  67    return qtest_readl(qts, wd->base_addr + WTCR_OFFSET);
  68}
  69
  70static 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
  76static 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
  92static 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
 104static 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
 110static int64_t watchdog_calculate_steps(uint32_t count, uint32_t prescale)
 111{
 112    return (NANOSECONDS_PER_SECOND / REF_HZ) * count * prescale;
 113}
 114
 115static 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 */
 122static 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 */
 136static 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 */
 172static 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 */
 203static 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 */
 257static 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
 296static 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
 305int 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}
 322