Since commit a2ce7dbd917 ("meson: convert tests/qtest to meson"),
libqtest.h is under libqos/ directory, while libqtest.c is still in
qtest/. Move back to its original location to avoid mixing with libqos/.
Suggested-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Thomas Huth <thuth@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
		
	
			
		
			
				
	
	
		
			322 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			322 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * QTests for Nuvoton NPCM7xx Timer Watchdog Modules.
 | 
						|
 *
 | 
						|
 * Copyright 2020 Google LLC
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or modify it
 | 
						|
 * under the terms of the GNU General Public License as published by the
 | 
						|
 * Free Software Foundation; either version 2 of the License, or
 | 
						|
 * (at your option) any later version.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that it will be useful, but WITHOUT
 | 
						|
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | 
						|
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 | 
						|
 * for more details.
 | 
						|
 */
 | 
						|
 | 
						|
#include "qemu/osdep.h"
 | 
						|
#include "qemu/timer.h"
 | 
						|
 | 
						|
#include "libqtest.h"
 | 
						|
#include "qapi/qmp/qdict.h"
 | 
						|
 | 
						|
#define WTCR_OFFSET     0x1c
 | 
						|
#define REF_HZ          (25000000)
 | 
						|
 | 
						|
/* WTCR bit fields */
 | 
						|
#define WTCLK(rv)       ((rv) << 10)
 | 
						|
#define WTE             BIT(7)
 | 
						|
#define WTIE            BIT(6)
 | 
						|
#define WTIS(rv)        ((rv) << 4)
 | 
						|
#define WTIF            BIT(3)
 | 
						|
#define WTRF            BIT(2)
 | 
						|
#define WTRE            BIT(1)
 | 
						|
#define WTR             BIT(0)
 | 
						|
 | 
						|
typedef struct Watchdog {
 | 
						|
    int irq;
 | 
						|
    uint64_t base_addr;
 | 
						|
} Watchdog;
 | 
						|
 | 
						|
static const Watchdog watchdog_list[] = {
 | 
						|
    {
 | 
						|
        .irq        = 47,
 | 
						|
        .base_addr  = 0xf0008000
 | 
						|
    },
 | 
						|
    {
 | 
						|
        .irq        = 48,
 | 
						|
        .base_addr  = 0xf0009000
 | 
						|
    },
 | 
						|
    {
 | 
						|
        .irq        = 49,
 | 
						|
        .base_addr  = 0xf000a000
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
static int watchdog_index(const Watchdog *wd)
 | 
						|
{
 | 
						|
    ptrdiff_t diff = wd - watchdog_list;
 | 
						|
 | 
						|
    g_assert(diff >= 0 && diff < ARRAY_SIZE(watchdog_list));
 | 
						|
 | 
						|
    return diff;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t watchdog_read_wtcr(QTestState *qts, const Watchdog *wd)
 | 
						|
{
 | 
						|
    return qtest_readl(qts, wd->base_addr + WTCR_OFFSET);
 | 
						|
}
 | 
						|
 | 
						|
static void watchdog_write_wtcr(QTestState *qts, const Watchdog *wd,
 | 
						|
        uint32_t value)
 | 
						|
{
 | 
						|
    qtest_writel(qts, wd->base_addr + WTCR_OFFSET, value);
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t watchdog_prescaler(QTestState *qts, const Watchdog *wd)
 | 
						|
{
 | 
						|
    switch (extract32(watchdog_read_wtcr(qts, wd), 10, 2)) {
 | 
						|
    case 0:
 | 
						|
        return 1;
 | 
						|
    case 1:
 | 
						|
        return 256;
 | 
						|
    case 2:
 | 
						|
        return 2048;
 | 
						|
    case 3:
 | 
						|
        return 65536;
 | 
						|
    default:
 | 
						|
        g_assert_not_reached();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static QDict *get_watchdog_action(QTestState *qts)
 | 
						|
{
 | 
						|
    QDict *ev = qtest_qmp_eventwait_ref(qts, "WATCHDOG");
 | 
						|
    QDict *data;
 | 
						|
 | 
						|
    data = qdict_get_qdict(ev, "data");
 | 
						|
    qobject_ref(data);
 | 
						|
    qobject_unref(ev);
 | 
						|
    return data;
 | 
						|
}
 | 
						|
 | 
						|
#define RESET_CYCLES 1024
 | 
						|
static uint32_t watchdog_interrupt_cycles(QTestState *qts, const Watchdog *wd)
 | 
						|
{
 | 
						|
    uint32_t wtis = extract32(watchdog_read_wtcr(qts, wd), 4, 2);
 | 
						|
    return 1 << (14 + 2 * wtis);
 | 
						|
}
 | 
						|
 | 
						|
static int64_t watchdog_calculate_steps(uint32_t count, uint32_t prescale)
 | 
						|
{
 | 
						|
    return (NANOSECONDS_PER_SECOND / REF_HZ) * count * prescale;
 | 
						|
}
 | 
						|
 | 
						|
static int64_t watchdog_interrupt_steps(QTestState *qts, const Watchdog *wd)
 | 
						|
{
 | 
						|
    return watchdog_calculate_steps(watchdog_interrupt_cycles(qts, wd),
 | 
						|
            watchdog_prescaler(qts, wd));
 | 
						|
}
 | 
						|
 | 
						|
/* Check wtcr can be reset to default value */
 | 
						|
static void test_init(gconstpointer watchdog)
 | 
						|
{
 | 
						|
    const Watchdog *wd = watchdog;
 | 
						|
    QTestState *qts = qtest_init("-machine quanta-gsj");
 | 
						|
 | 
						|
    qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
 | 
						|
 | 
						|
    watchdog_write_wtcr(qts, wd, WTCLK(1) | WTRF | WTIF | WTR);
 | 
						|
    g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(1));
 | 
						|
 | 
						|
    qtest_quit(qts);
 | 
						|
}
 | 
						|
 | 
						|
/* Check a watchdog can generate interrupt and reset actions */
 | 
						|
static void test_reset_action(gconstpointer watchdog)
 | 
						|
{
 | 
						|
    const Watchdog *wd = watchdog;
 | 
						|
    QTestState *qts = qtest_init("-machine quanta-gsj");
 | 
						|
    QDict *ad;
 | 
						|
 | 
						|
    qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
 | 
						|
 | 
						|
    watchdog_write_wtcr(qts, wd,
 | 
						|
            WTCLK(0) | WTE | WTRF | WTRE | WTIF | WTIE | WTR);
 | 
						|
    g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==,
 | 
						|
            WTCLK(0) | WTE | WTRE | WTIE);
 | 
						|
 | 
						|
    /* Check a watchdog can generate an interrupt */
 | 
						|
    qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd));
 | 
						|
    g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==,
 | 
						|
            WTCLK(0) | WTE | WTIF | WTIE | WTRE);
 | 
						|
    g_assert_true(qtest_get_irq(qts, wd->irq));
 | 
						|
 | 
						|
    /* Check a watchdog can generate a reset signal */
 | 
						|
    qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES,
 | 
						|
                watchdog_prescaler(qts, wd)));
 | 
						|
    ad = get_watchdog_action(qts);
 | 
						|
    /* The signal is a reset signal */
 | 
						|
    g_assert_false(strcmp(qdict_get_str(ad, "action"), "reset"));
 | 
						|
    qobject_unref(ad);
 | 
						|
    qtest_qmp_eventwait(qts, "RESET");
 | 
						|
    /*
 | 
						|
     * Make sure WTCR is reset to default except for WTRF bit which shouldn't
 | 
						|
     * be reset.
 | 
						|
     */
 | 
						|
    g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(1) | WTRF);
 | 
						|
    qtest_quit(qts);
 | 
						|
}
 | 
						|
 | 
						|
/* Check a watchdog works with all possible WTCLK prescalers and WTIS cycles */
 | 
						|
static void test_prescaler(gconstpointer watchdog)
 | 
						|
{
 | 
						|
    const Watchdog *wd = watchdog;
 | 
						|
 | 
						|
    for (int wtclk = 0; wtclk < 4; ++wtclk) {
 | 
						|
        for (int wtis = 0; wtis < 4; ++wtis) {
 | 
						|
            QTestState *qts = qtest_init("-machine quanta-gsj");
 | 
						|
 | 
						|
            qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
 | 
						|
            watchdog_write_wtcr(qts, wd,
 | 
						|
                    WTCLK(wtclk) | WTE | WTIF | WTIS(wtis) | WTIE | WTR);
 | 
						|
            /*
 | 
						|
             * The interrupt doesn't fire until watchdog_interrupt_steps()
 | 
						|
             * cycles passed
 | 
						|
             */
 | 
						|
            qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd) - 1);
 | 
						|
            g_assert_false(watchdog_read_wtcr(qts, wd) & WTIF);
 | 
						|
            g_assert_false(qtest_get_irq(qts, wd->irq));
 | 
						|
            qtest_clock_step(qts, 1);
 | 
						|
            g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
 | 
						|
            g_assert_true(qtest_get_irq(qts, wd->irq));
 | 
						|
 | 
						|
            qtest_quit(qts);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Check a watchdog doesn't fire if corresponding flags (WTIE and WTRE) are not
 | 
						|
 * set.
 | 
						|
 */
 | 
						|
static void test_enabling_flags(gconstpointer watchdog)
 | 
						|
{
 | 
						|
    const Watchdog *wd = watchdog;
 | 
						|
    QTestState *qts;
 | 
						|
    QDict *rsp;
 | 
						|
 | 
						|
    /* Neither WTIE or WTRE is set, no interrupt or reset should happen */
 | 
						|
    qts = qtest_init("-machine quanta-gsj");
 | 
						|
    qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
 | 
						|
    watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTRF | WTR);
 | 
						|
    qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd));
 | 
						|
    g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
 | 
						|
    g_assert_false(qtest_get_irq(qts, wd->irq));
 | 
						|
    qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES,
 | 
						|
                watchdog_prescaler(qts, wd)));
 | 
						|
    g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
 | 
						|
    g_assert_false(watchdog_read_wtcr(qts, wd) & WTRF);
 | 
						|
    qtest_quit(qts);
 | 
						|
 | 
						|
    /* Only WTIE is set, interrupt is triggered but reset should not happen */
 | 
						|
    qts = qtest_init("-machine quanta-gsj");
 | 
						|
    qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
 | 
						|
    watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTIE | WTRF | WTR);
 | 
						|
    qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd));
 | 
						|
    g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
 | 
						|
    g_assert_true(qtest_get_irq(qts, wd->irq));
 | 
						|
    qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES,
 | 
						|
                watchdog_prescaler(qts, wd)));
 | 
						|
    g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
 | 
						|
    g_assert_false(watchdog_read_wtcr(qts, wd) & WTRF);
 | 
						|
    qtest_quit(qts);
 | 
						|
 | 
						|
    /* Only WTRE is set, interrupt is triggered but reset should not happen */
 | 
						|
    qts = qtest_init("-machine quanta-gsj");
 | 
						|
    qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
 | 
						|
    watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTRE | WTRF | WTR);
 | 
						|
    qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd));
 | 
						|
    g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
 | 
						|
    g_assert_false(qtest_get_irq(qts, wd->irq));
 | 
						|
    qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES,
 | 
						|
                watchdog_prescaler(qts, wd)));
 | 
						|
    rsp = get_watchdog_action(qts);
 | 
						|
    g_assert_false(strcmp(qdict_get_str(rsp, "action"), "reset"));
 | 
						|
    qobject_unref(rsp);
 | 
						|
    qtest_qmp_eventwait(qts, "RESET");
 | 
						|
    qtest_quit(qts);
 | 
						|
 | 
						|
    /*
 | 
						|
     * The case when both flags are set is already tested in
 | 
						|
     * test_reset_action().
 | 
						|
     */
 | 
						|
}
 | 
						|
 | 
						|
/* Check a watchdog can pause and resume by setting WTE bits */
 | 
						|
static void test_pause(gconstpointer watchdog)
 | 
						|
{
 | 
						|
    const Watchdog *wd = watchdog;
 | 
						|
    QTestState *qts;
 | 
						|
    int64_t remaining_steps, steps;
 | 
						|
 | 
						|
    qts = qtest_init("-machine quanta-gsj");
 | 
						|
    qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
 | 
						|
    watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTIE | WTRF | WTR);
 | 
						|
    remaining_steps = watchdog_interrupt_steps(qts, wd);
 | 
						|
    g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTE | WTIE);
 | 
						|
 | 
						|
    /* Run for half of the execution period. */
 | 
						|
    steps = remaining_steps / 2;
 | 
						|
    remaining_steps -= steps;
 | 
						|
    qtest_clock_step(qts, steps);
 | 
						|
 | 
						|
    /* Pause the watchdog */
 | 
						|
    watchdog_write_wtcr(qts, wd, WTCLK(0) | WTIE);
 | 
						|
    g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTIE);
 | 
						|
 | 
						|
    /* Run for a long period of time, the watchdog shouldn't fire */
 | 
						|
    qtest_clock_step(qts, steps << 4);
 | 
						|
    g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTIE);
 | 
						|
    g_assert_false(qtest_get_irq(qts, wd->irq));
 | 
						|
 | 
						|
    /* Resume the watchdog */
 | 
						|
    watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIE);
 | 
						|
    g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTE | WTIE);
 | 
						|
 | 
						|
    /* Run for the reset of the execution period, the watchdog should fire */
 | 
						|
    qtest_clock_step(qts, remaining_steps);
 | 
						|
    g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==,
 | 
						|
            WTCLK(0) | WTE | WTIF | WTIE);
 | 
						|
    g_assert_true(qtest_get_irq(qts, wd->irq));
 | 
						|
 | 
						|
    qtest_quit(qts);
 | 
						|
}
 | 
						|
 | 
						|
static void watchdog_add_test(const char *name, const Watchdog* wd,
 | 
						|
        GTestDataFunc fn)
 | 
						|
{
 | 
						|
    g_autofree char *full_name = g_strdup_printf(
 | 
						|
            "npcm7xx_watchdog_timer[%d]/%s", watchdog_index(wd), name);
 | 
						|
    qtest_add_data_func(full_name, wd, fn);
 | 
						|
}
 | 
						|
#define add_test(name, td) watchdog_add_test(#name, td, test_##name)
 | 
						|
 | 
						|
int main(int argc, char **argv)
 | 
						|
{
 | 
						|
    g_test_init(&argc, &argv, NULL);
 | 
						|
    g_test_set_nonfatal_assertions();
 | 
						|
 | 
						|
    for (int i = 0; i < ARRAY_SIZE(watchdog_list); ++i) {
 | 
						|
        const Watchdog *wd = &watchdog_list[i];
 | 
						|
 | 
						|
        add_test(init, wd);
 | 
						|
        add_test(reset_action, wd);
 | 
						|
        add_test(prescaler, wd);
 | 
						|
        add_test(enabling_flags, wd);
 | 
						|
        add_test(pause, wd);
 | 
						|
    }
 | 
						|
 | 
						|
    return g_test_run();
 | 
						|
}
 |