mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-09 02:24:58 -06:00
target-arm queue:
* raspi: add model of cprman clock manager * sbsa-ref: add an SBSA generic watchdog device * arm/trace: Fix hex printing * raspi: Add models of Pi 3 model A+, Pi Zero and Pi A+ * hw/arm/smmuv3: Set the restoration priority of the vSMMUv3 explicitly * Nuvoton NPCM7xx: Add USB, RNG, GPIO and watchdog support * hw/arm: fix min_cpus for xlnx-versal-virt platform * hw/arm/highbank: Silence warnings about missing fallthrough statements * linux-user: Support Aarch64 BTI * Armv7M systick: fix corner case bugs by rewriting to use ptimer -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAl+YBA4ZHHBldGVyLm1h eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3rWRD/9hqjzL4d7xKcFQdQdRXsxv 7zX82arHdxg9pNvusie/tuhX0PLswQ8TPEHEBVQvngxF7y/HqLBFuZAQvFf4ou6R 9+myTXE2RuWHOYKlrr/M6p4csABXNMm7PiA3VMeKcTEh4DoamLyBz6j1X4obPiA+ tLaRw4azzYAZnHoCaF6BX+4uf4bQZoqAtAS4IodJAAbDXJStl0VUFoS34MPhgW6/ dwGF8DbQJVYRqa7xEXck4Yx7dkx13I66+iYUf9kCyoCkdyz1sIq58fbKhXQP4lqN I3e5XGBVJfeku7w/TGOpsw8OCyTng0z636iglfLVOrsj5N03fT8j72ehY7jJsN9f CgHvQ1JAX1DvA/v23oxs3WccwAOfJJsOERtf9QxyMbTR1czCeIY1LYMnkOFtyL87 6IQpwM0WF1z4lja0dmrvhKJWjqn+kVI2cDtxrprsulCHi+pcIdJMq8vJDfxjpqqe SnDXVSAn8KjBrClaJRqHfbi+5ggsTwsLpBtEToQ4AOR342XVRfEY8IfTLb1D2+6q z99BFiyJtZ6iiJq5jgGMhppN6tEuHFK7Vr6IwhGDgFTchWb6by+K3i8/VzrbWVk9 O+KEeO92dg6jVd+6FyXOPnJ3DcUXEp6EVUVrKBBUC+LTU8Lf1MCgeprjSi87UHIX xQg635uOQU3gxkqxCaE0XA== =OFlu -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20201027-1' into staging target-arm queue: * raspi: add model of cprman clock manager * sbsa-ref: add an SBSA generic watchdog device * arm/trace: Fix hex printing * raspi: Add models of Pi 3 model A+, Pi Zero and Pi A+ * hw/arm/smmuv3: Set the restoration priority of the vSMMUv3 explicitly * Nuvoton NPCM7xx: Add USB, RNG, GPIO and watchdog support * hw/arm: fix min_cpus for xlnx-versal-virt platform * hw/arm/highbank: Silence warnings about missing fallthrough statements * linux-user: Support Aarch64 BTI * Armv7M systick: fix corner case bugs by rewriting to use ptimer # gpg: Signature made Tue 27 Oct 2020 11:27:10 GMT # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20201027-1: (48 commits) hw/timer/armv7m_systick: Rewrite to use ptimers hw/core/ptimer: Support ptimer being disabled by timer callback hw/arm/sbsa-ref: add SBSA watchdog device hw/watchdog: Implement SBSA watchdog device hw/arm/bcm2835_peripherals: connect the UART clock hw/char/pl011: add a clock input hw/misc/bcm2835_cprman: add sane reset values to the registers hw/misc/bcm2835_cprman: add the DSI0HSCK multiplexer hw/misc/bcm2835_cprman: implement clock mux behaviour hw/misc/bcm2835_cprman: add a clock mux skeleton implementation hw/misc/bcm2835_cprman: implement PLL channels behaviour hw/misc/bcm2835_cprman: add a PLL channel skeleton implementation hw/misc/bcm2835_cprman: implement PLLs behaviour hw/misc/bcm2835_cprman: add a PLL skeleton implementation hw/arm/raspi: add a skeleton implementation of the CPRMAN hw/arm/raspi: fix CPRMAN base address hw/core/clock: trace clock values in Hz instead of ns hw/core/clock: provide the VMSTATE_ARRAY_CLOCK macro arm/trace: Fix hex printing hw/arm/raspi: Add the Raspberry Pi 3 model A+ ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
802427bcda
64 changed files with 5461 additions and 279 deletions
|
@ -133,7 +133,11 @@ qtests_sparc64 = \
|
|||
(config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) + \
|
||||
['prom-env-test', 'boot-serial-test']
|
||||
|
||||
qtests_npcm7xx = ['npcm7xx_timer-test']
|
||||
qtests_npcm7xx = \
|
||||
['npcm7xx_gpio-test',
|
||||
'npcm7xx_rng-test',
|
||||
'npcm7xx_timer-test',
|
||||
'npcm7xx_watchdog_timer-test']
|
||||
qtests_arm = \
|
||||
(config_all_devices.has_key('CONFIG_PFLASH_CFI02') ? ['pflash-cfi02-test'] : []) + \
|
||||
(config_all_devices.has_key('CONFIG_NPCM7XX') ? qtests_npcm7xx : []) + \
|
||||
|
|
385
tests/qtest/npcm7xx_gpio-test.c
Normal file
385
tests/qtest/npcm7xx_gpio-test.c
Normal file
|
@ -0,0 +1,385 @@
|
|||
/*
|
||||
* QTest testcase for the Nuvoton NPCM7xx GPIO 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 "libqtest-single.h"
|
||||
|
||||
#define NR_GPIO_DEVICES (8)
|
||||
#define GPIO(x) (0xf0010000 + (x) * 0x1000)
|
||||
#define GPIO_IRQ(x) (116 + (x))
|
||||
|
||||
/* GPIO registers */
|
||||
#define GP_N_TLOCK1 0x00
|
||||
#define GP_N_DIN 0x04 /* Data IN */
|
||||
#define GP_N_POL 0x08 /* Polarity */
|
||||
#define GP_N_DOUT 0x0c /* Data OUT */
|
||||
#define GP_N_OE 0x10 /* Output Enable */
|
||||
#define GP_N_OTYP 0x14
|
||||
#define GP_N_MP 0x18
|
||||
#define GP_N_PU 0x1c /* Pull-up */
|
||||
#define GP_N_PD 0x20 /* Pull-down */
|
||||
#define GP_N_DBNC 0x24 /* Debounce */
|
||||
#define GP_N_EVTYP 0x28 /* Event Type */
|
||||
#define GP_N_EVBE 0x2c /* Event Both Edge */
|
||||
#define GP_N_OBL0 0x30
|
||||
#define GP_N_OBL1 0x34
|
||||
#define GP_N_OBL2 0x38
|
||||
#define GP_N_OBL3 0x3c
|
||||
#define GP_N_EVEN 0x40 /* Event Enable */
|
||||
#define GP_N_EVENS 0x44 /* Event Set (enable) */
|
||||
#define GP_N_EVENC 0x48 /* Event Clear (disable) */
|
||||
#define GP_N_EVST 0x4c /* Event Status */
|
||||
#define GP_N_SPLCK 0x50
|
||||
#define GP_N_MPLCK 0x54
|
||||
#define GP_N_IEM 0x58 /* Input Enable */
|
||||
#define GP_N_OSRC 0x5c
|
||||
#define GP_N_ODSC 0x60
|
||||
#define GP_N_DOS 0x68 /* Data OUT Set */
|
||||
#define GP_N_DOC 0x6c /* Data OUT Clear */
|
||||
#define GP_N_OES 0x70 /* Output Enable Set */
|
||||
#define GP_N_OEC 0x74 /* Output Enable Clear */
|
||||
#define GP_N_TLOCK2 0x7c
|
||||
|
||||
static void gpio_unlock(int n)
|
||||
{
|
||||
if (readl(GPIO(n) + GP_N_TLOCK1) != 0) {
|
||||
writel(GPIO(n) + GP_N_TLOCK2, 0xc0de1248);
|
||||
writel(GPIO(n) + GP_N_TLOCK1, 0xc0defa73);
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore the GPIO controller to a sensible default state. */
|
||||
static void gpio_reset(int n)
|
||||
{
|
||||
gpio_unlock(0);
|
||||
|
||||
writel(GPIO(n) + GP_N_EVEN, 0x00000000);
|
||||
writel(GPIO(n) + GP_N_EVST, 0xffffffff);
|
||||
writel(GPIO(n) + GP_N_POL, 0x00000000);
|
||||
writel(GPIO(n) + GP_N_DOUT, 0x00000000);
|
||||
writel(GPIO(n) + GP_N_OE, 0x00000000);
|
||||
writel(GPIO(n) + GP_N_OTYP, 0x00000000);
|
||||
writel(GPIO(n) + GP_N_PU, 0xffffffff);
|
||||
writel(GPIO(n) + GP_N_PD, 0x00000000);
|
||||
writel(GPIO(n) + GP_N_IEM, 0xffffffff);
|
||||
}
|
||||
|
||||
static void test_dout_to_din(void)
|
||||
{
|
||||
gpio_reset(0);
|
||||
|
||||
/* When output is enabled, DOUT should be reflected on DIN. */
|
||||
writel(GPIO(0) + GP_N_OE, 0xffffffff);
|
||||
/* PU and PD shouldn't have any impact on DIN. */
|
||||
writel(GPIO(0) + GP_N_PU, 0xffff0000);
|
||||
writel(GPIO(0) + GP_N_PD, 0x0000ffff);
|
||||
writel(GPIO(0) + GP_N_DOUT, 0x12345678);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_DOUT), ==, 0x12345678);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0x12345678);
|
||||
}
|
||||
|
||||
static void test_pullup_pulldown(void)
|
||||
{
|
||||
gpio_reset(0);
|
||||
|
||||
/*
|
||||
* When output is disabled, and PD is the inverse of PU, PU should be
|
||||
* reflected on DIN. If PD is not the inverse of PU, the state of DIN is
|
||||
* undefined, so we don't test that.
|
||||
*/
|
||||
writel(GPIO(0) + GP_N_OE, 0x00000000);
|
||||
/* DOUT shouldn't have any impact on DIN. */
|
||||
writel(GPIO(0) + GP_N_DOUT, 0xffff0000);
|
||||
writel(GPIO(0) + GP_N_PU, 0x23456789);
|
||||
writel(GPIO(0) + GP_N_PD, ~0x23456789U);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_PU), ==, 0x23456789);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_PD), ==, ~0x23456789U);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0x23456789);
|
||||
}
|
||||
|
||||
static void test_output_enable(void)
|
||||
{
|
||||
gpio_reset(0);
|
||||
|
||||
/*
|
||||
* With all pins weakly pulled down, and DOUT all-ones, OE should be
|
||||
* reflected on DIN.
|
||||
*/
|
||||
writel(GPIO(0) + GP_N_DOUT, 0xffffffff);
|
||||
writel(GPIO(0) + GP_N_PU, 0x00000000);
|
||||
writel(GPIO(0) + GP_N_PD, 0xffffffff);
|
||||
writel(GPIO(0) + GP_N_OE, 0x3456789a);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_OE), ==, 0x3456789a);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0x3456789a);
|
||||
|
||||
writel(GPIO(0) + GP_N_OEC, 0x00030002);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_OE), ==, 0x34547898);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0x34547898);
|
||||
|
||||
writel(GPIO(0) + GP_N_OES, 0x0000f001);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_OE), ==, 0x3454f899);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0x3454f899);
|
||||
}
|
||||
|
||||
static void test_open_drain(void)
|
||||
{
|
||||
gpio_reset(0);
|
||||
|
||||
/*
|
||||
* Upper half of DOUT drives a 1 only if the corresponding bit in OTYP is
|
||||
* not set. If OTYP is set, DIN is determined by PU/PD. Lower half of
|
||||
* DOUT always drives a 0 regardless of OTYP; PU/PD have no effect. When
|
||||
* OE is 0, output is determined by PU/PD; OTYP has no effect.
|
||||
*/
|
||||
writel(GPIO(0) + GP_N_OTYP, 0x456789ab);
|
||||
writel(GPIO(0) + GP_N_OE, 0xf0f0f0f0);
|
||||
writel(GPIO(0) + GP_N_DOUT, 0xffff0000);
|
||||
writel(GPIO(0) + GP_N_PU, 0xff00ff00);
|
||||
writel(GPIO(0) + GP_N_PD, 0x00ff00ff);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_OTYP), ==, 0x456789ab);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0xff900f00);
|
||||
}
|
||||
|
||||
static void test_polarity(void)
|
||||
{
|
||||
gpio_reset(0);
|
||||
|
||||
/*
|
||||
* In push-pull mode, DIN should reflect DOUT because the signal is
|
||||
* inverted in both directions.
|
||||
*/
|
||||
writel(GPIO(0) + GP_N_OTYP, 0x00000000);
|
||||
writel(GPIO(0) + GP_N_OE, 0xffffffff);
|
||||
writel(GPIO(0) + GP_N_DOUT, 0x56789abc);
|
||||
writel(GPIO(0) + GP_N_POL, 0x6789abcd);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_POL), ==, 0x6789abcd);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0x56789abc);
|
||||
|
||||
/*
|
||||
* When turning off the drivers, DIN should reflect the inverse of the
|
||||
* pulled-up lines.
|
||||
*/
|
||||
writel(GPIO(0) + GP_N_OE, 0x00000000);
|
||||
writel(GPIO(0) + GP_N_POL, 0xffffffff);
|
||||
writel(GPIO(0) + GP_N_PU, 0x789abcde);
|
||||
writel(GPIO(0) + GP_N_PD, ~0x789abcdeU);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, ~0x789abcdeU);
|
||||
|
||||
/*
|
||||
* In open-drain mode, DOUT=1 will appear to drive the pin high (since DIN
|
||||
* is inverted), while DOUT=0 will leave the pin floating.
|
||||
*/
|
||||
writel(GPIO(0) + GP_N_OTYP, 0xffffffff);
|
||||
writel(GPIO(0) + GP_N_OE, 0xffffffff);
|
||||
writel(GPIO(0) + GP_N_PU, 0xffff0000);
|
||||
writel(GPIO(0) + GP_N_PD, 0x0000ffff);
|
||||
writel(GPIO(0) + GP_N_DOUT, 0xff00ff00);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0xff00ffff);
|
||||
}
|
||||
|
||||
static void test_input_mask(void)
|
||||
{
|
||||
gpio_reset(0);
|
||||
|
||||
/* IEM=0 forces the input to zero before polarity inversion. */
|
||||
writel(GPIO(0) + GP_N_OE, 0xffffffff);
|
||||
writel(GPIO(0) + GP_N_DOUT, 0xff00ff00);
|
||||
writel(GPIO(0) + GP_N_POL, 0xffff0000);
|
||||
writel(GPIO(0) + GP_N_IEM, 0x87654321);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_DIN), ==, 0xff9a4300);
|
||||
}
|
||||
|
||||
static void test_temp_lock(void)
|
||||
{
|
||||
gpio_reset(0);
|
||||
|
||||
writel(GPIO(0) + GP_N_DOUT, 0x98765432);
|
||||
|
||||
/* Make sure we're unlocked initially. */
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_TLOCK1), ==, 0);
|
||||
/* Writing any value to TLOCK1 will lock. */
|
||||
writel(GPIO(0) + GP_N_TLOCK1, 0);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_TLOCK1), ==, 1);
|
||||
writel(GPIO(0) + GP_N_DOUT, 0xa9876543);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_DOUT), ==, 0x98765432);
|
||||
/* Now, try to unlock. */
|
||||
gpio_unlock(0);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_TLOCK1), ==, 0);
|
||||
writel(GPIO(0) + GP_N_DOUT, 0xa9876543);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_DOUT), ==, 0xa9876543);
|
||||
|
||||
/* Try it again, but write TLOCK2 to lock. */
|
||||
writel(GPIO(0) + GP_N_TLOCK2, 0);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_TLOCK1), ==, 1);
|
||||
writel(GPIO(0) + GP_N_DOUT, 0x98765432);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_DOUT), ==, 0xa9876543);
|
||||
/* Now, try to unlock. */
|
||||
gpio_unlock(0);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_TLOCK1), ==, 0);
|
||||
writel(GPIO(0) + GP_N_DOUT, 0x98765432);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_DOUT), ==, 0x98765432);
|
||||
}
|
||||
|
||||
static void test_events_level(void)
|
||||
{
|
||||
gpio_reset(0);
|
||||
|
||||
writel(GPIO(0) + GP_N_EVTYP, 0x00000000);
|
||||
writel(GPIO(0) + GP_N_DOUT, 0xba987654);
|
||||
writel(GPIO(0) + GP_N_OE, 0xffffffff);
|
||||
writel(GPIO(0) + GP_N_EVST, 0xffffffff);
|
||||
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0xba987654);
|
||||
g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0)));
|
||||
writel(GPIO(0) + GP_N_DOUT, 0x00000000);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0xba987654);
|
||||
g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0)));
|
||||
writel(GPIO(0) + GP_N_EVST, 0x00007654);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0xba980000);
|
||||
g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0)));
|
||||
writel(GPIO(0) + GP_N_EVST, 0xba980000);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00000000);
|
||||
g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0)));
|
||||
}
|
||||
|
||||
static void test_events_rising_edge(void)
|
||||
{
|
||||
gpio_reset(0);
|
||||
|
||||
writel(GPIO(0) + GP_N_EVTYP, 0xffffffff);
|
||||
writel(GPIO(0) + GP_N_EVBE, 0x00000000);
|
||||
writel(GPIO(0) + GP_N_DOUT, 0xffff0000);
|
||||
writel(GPIO(0) + GP_N_OE, 0xffffffff);
|
||||
writel(GPIO(0) + GP_N_EVST, 0xffffffff);
|
||||
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00000000);
|
||||
g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0)));
|
||||
writel(GPIO(0) + GP_N_DOUT, 0xff00ff00);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x0000ff00);
|
||||
g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0)));
|
||||
writel(GPIO(0) + GP_N_DOUT, 0x00ff0000);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00ffff00);
|
||||
g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0)));
|
||||
writel(GPIO(0) + GP_N_EVST, 0x0000f000);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00ff0f00);
|
||||
g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0)));
|
||||
writel(GPIO(0) + GP_N_EVST, 0x00ff0f00);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00000000);
|
||||
g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0)));
|
||||
}
|
||||
|
||||
static void test_events_both_edges(void)
|
||||
{
|
||||
gpio_reset(0);
|
||||
|
||||
writel(GPIO(0) + GP_N_EVTYP, 0xffffffff);
|
||||
writel(GPIO(0) + GP_N_EVBE, 0xffffffff);
|
||||
writel(GPIO(0) + GP_N_DOUT, 0xffff0000);
|
||||
writel(GPIO(0) + GP_N_OE, 0xffffffff);
|
||||
writel(GPIO(0) + GP_N_EVST, 0xffffffff);
|
||||
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00000000);
|
||||
g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0)));
|
||||
writel(GPIO(0) + GP_N_DOUT, 0xff00ff00);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00ffff00);
|
||||
g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0)));
|
||||
writel(GPIO(0) + GP_N_DOUT, 0xef00ff08);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x10ffff08);
|
||||
g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0)));
|
||||
writel(GPIO(0) + GP_N_EVST, 0x0000f000);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x10ff0f08);
|
||||
g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0)));
|
||||
writel(GPIO(0) + GP_N_EVST, 0x10ff0f08);
|
||||
g_assert_cmphex(readl(GPIO(0) + GP_N_EVST), ==, 0x00000000);
|
||||
g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(0)));
|
||||
}
|
||||
|
||||
static void test_gpion_irq(gconstpointer test_data)
|
||||
{
|
||||
intptr_t n = (intptr_t)test_data;
|
||||
|
||||
gpio_reset(n);
|
||||
|
||||
writel(GPIO(n) + GP_N_EVTYP, 0x00000000);
|
||||
writel(GPIO(n) + GP_N_DOUT, 0x00000000);
|
||||
writel(GPIO(n) + GP_N_OE, 0xffffffff);
|
||||
writel(GPIO(n) + GP_N_EVST, 0xffffffff);
|
||||
writel(GPIO(n) + GP_N_EVEN, 0x00000000);
|
||||
|
||||
/* Trigger an event; interrupts are masked. */
|
||||
g_assert_cmphex(readl(GPIO(n) + GP_N_EVST), ==, 0x00000000);
|
||||
g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(n)));
|
||||
writel(GPIO(n) + GP_N_DOS, 0x00008000);
|
||||
g_assert_cmphex(readl(GPIO(n) + GP_N_EVST), ==, 0x00008000);
|
||||
g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(n)));
|
||||
|
||||
/* Unmask all event interrupts; verify that the interrupt fired. */
|
||||
writel(GPIO(n) + GP_N_EVEN, 0xffffffff);
|
||||
g_assert_true(qtest_get_irq(global_qtest, GPIO_IRQ(n)));
|
||||
|
||||
/* Clear the current bit, set a new bit, irq stays asserted. */
|
||||
writel(GPIO(n) + GP_N_DOC, 0x00008000);
|
||||
g_assert_true(qtest_get_irq(global_qtest, GPIO_IRQ(n)));
|
||||
writel(GPIO(n) + GP_N_DOS, 0x00000200);
|
||||
g_assert_true(qtest_get_irq(global_qtest, GPIO_IRQ(n)));
|
||||
writel(GPIO(n) + GP_N_EVST, 0x00008000);
|
||||
g_assert_true(qtest_get_irq(global_qtest, GPIO_IRQ(n)));
|
||||
|
||||
/* Mask/unmask the event that's currently active. */
|
||||
writel(GPIO(n) + GP_N_EVENC, 0x00000200);
|
||||
g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(n)));
|
||||
writel(GPIO(n) + GP_N_EVENS, 0x00000200);
|
||||
g_assert_true(qtest_get_irq(global_qtest, GPIO_IRQ(n)));
|
||||
|
||||
/* Clear the input and the status bit, irq is deasserted. */
|
||||
writel(GPIO(n) + GP_N_DOC, 0x00000200);
|
||||
g_assert_true(qtest_get_irq(global_qtest, GPIO_IRQ(n)));
|
||||
writel(GPIO(n) + GP_N_EVST, 0x00000200);
|
||||
g_assert_false(qtest_get_irq(global_qtest, GPIO_IRQ(n)));
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_set_nonfatal_assertions();
|
||||
|
||||
qtest_add_func("/npcm7xx_gpio/dout_to_din", test_dout_to_din);
|
||||
qtest_add_func("/npcm7xx_gpio/pullup_pulldown", test_pullup_pulldown);
|
||||
qtest_add_func("/npcm7xx_gpio/output_enable", test_output_enable);
|
||||
qtest_add_func("/npcm7xx_gpio/open_drain", test_open_drain);
|
||||
qtest_add_func("/npcm7xx_gpio/polarity", test_polarity);
|
||||
qtest_add_func("/npcm7xx_gpio/input_mask", test_input_mask);
|
||||
qtest_add_func("/npcm7xx_gpio/temp_lock", test_temp_lock);
|
||||
qtest_add_func("/npcm7xx_gpio/events/level", test_events_level);
|
||||
qtest_add_func("/npcm7xx_gpio/events/rising_edge", test_events_rising_edge);
|
||||
qtest_add_func("/npcm7xx_gpio/events/both_edges", test_events_both_edges);
|
||||
|
||||
for (i = 0; i < NR_GPIO_DEVICES; i++) {
|
||||
g_autofree char *test_name =
|
||||
g_strdup_printf("/npcm7xx_gpio/gpio[%d]/irq", i);
|
||||
qtest_add_data_func(test_name, (void *)(intptr_t)i, test_gpion_irq);
|
||||
}
|
||||
|
||||
qtest_start("-machine npcm750-evb");
|
||||
qtest_irq_intercept_in(global_qtest, "/machine/soc/a9mpcore/gic");
|
||||
ret = g_test_run();
|
||||
qtest_end();
|
||||
|
||||
return ret;
|
||||
}
|
278
tests/qtest/npcm7xx_rng-test.c
Normal file
278
tests/qtest/npcm7xx_rng-test.c
Normal file
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* QTest testcase for the Nuvoton NPCM7xx Random Number Generator
|
||||
*
|
||||
* 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 <math.h>
|
||||
|
||||
#include "libqtest-single.h"
|
||||
#include "qemu/bitops.h"
|
||||
|
||||
#define RNG_BASE_ADDR 0xf000b000
|
||||
|
||||
/* Control and Status Register */
|
||||
#define RNGCS 0x00
|
||||
# define DVALID BIT(1) /* Data Valid */
|
||||
# define RNGE BIT(0) /* RNG Enable */
|
||||
/* Data Register */
|
||||
#define RNGD 0x04
|
||||
/* Mode Register */
|
||||
#define RNGMODE 0x08
|
||||
# define ROSEL_NORMAL (2) /* RNG only works in this mode */
|
||||
|
||||
/* Number of bits to collect for randomness tests. */
|
||||
#define TEST_INPUT_BITS (128)
|
||||
|
||||
static void rng_writeb(unsigned int offset, uint8_t value)
|
||||
{
|
||||
writeb(RNG_BASE_ADDR + offset, value);
|
||||
}
|
||||
|
||||
static uint8_t rng_readb(unsigned int offset)
|
||||
{
|
||||
return readb(RNG_BASE_ADDR + offset);
|
||||
}
|
||||
|
||||
/* Disable RNG and set normal ring oscillator mode. */
|
||||
static void rng_reset(void)
|
||||
{
|
||||
rng_writeb(RNGCS, 0);
|
||||
rng_writeb(RNGMODE, ROSEL_NORMAL);
|
||||
}
|
||||
|
||||
/* Reset RNG and then enable it. */
|
||||
static void rng_reset_enable(void)
|
||||
{
|
||||
rng_reset();
|
||||
rng_writeb(RNGCS, RNGE);
|
||||
}
|
||||
|
||||
/* Wait until Data Valid bit is set. */
|
||||
static bool rng_wait_ready(void)
|
||||
{
|
||||
/* qemu_guest_getrandom may fail. Assume it won't fail 10 times in a row. */
|
||||
int retries = 10;
|
||||
|
||||
while (retries-- > 0) {
|
||||
if (rng_readb(RNGCS) & DVALID) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform a frequency (monobit) test, as defined by NIST SP 800-22, on the
|
||||
* sequence in buf and return the P-value. This represents the probability of a
|
||||
* truly random sequence having the same proportion of zeros and ones as the
|
||||
* sequence in buf.
|
||||
*
|
||||
* An RNG which always returns 0x00 or 0xff, or has some bits stuck at 0 or 1,
|
||||
* will fail this test. However, an RNG which always returns 0x55, 0xf0 or some
|
||||
* other value with an equal number of zeroes and ones will pass.
|
||||
*/
|
||||
static double calc_monobit_p(const uint8_t *buf, unsigned int len)
|
||||
{
|
||||
unsigned int i;
|
||||
double s_obs;
|
||||
int sn = 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
/*
|
||||
* Each 1 counts as 1, each 0 counts as -1.
|
||||
* s = cp - (8 - cp) = 2 * cp - 8
|
||||
*/
|
||||
sn += 2 * ctpop8(buf[i]) - 8;
|
||||
}
|
||||
|
||||
s_obs = abs(sn) / sqrt(len * BITS_PER_BYTE);
|
||||
|
||||
return erfc(s_obs / sqrt(2));
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform a runs test, as defined by NIST SP 800-22, and return the P-value.
|
||||
* This represents the probability of a truly random sequence having the same
|
||||
* number of runs (i.e. uninterrupted sequences of identical bits) as the
|
||||
* sequence in buf.
|
||||
*/
|
||||
static double calc_runs_p(const unsigned long *buf, unsigned int nr_bits)
|
||||
{
|
||||
unsigned int j;
|
||||
unsigned int k;
|
||||
int nr_ones = 0;
|
||||
int vn_obs = 0;
|
||||
double pi;
|
||||
|
||||
g_assert(nr_bits % BITS_PER_LONG == 0);
|
||||
|
||||
for (j = 0; j < nr_bits / BITS_PER_LONG; j++) {
|
||||
nr_ones += __builtin_popcountl(buf[j]);
|
||||
}
|
||||
pi = (double)nr_ones / nr_bits;
|
||||
|
||||
for (k = 0; k < nr_bits - 1; k++) {
|
||||
vn_obs += !(test_bit(k, buf) ^ test_bit(k + 1, buf));
|
||||
}
|
||||
vn_obs += 1;
|
||||
|
||||
return erfc(fabs(vn_obs - 2 * nr_bits * pi * (1.0 - pi))
|
||||
/ (2 * sqrt(2 * nr_bits) * pi * (1.0 - pi)));
|
||||
}
|
||||
|
||||
/*
|
||||
* Verifies that DVALID is clear, and RNGD reads zero, when RNGE is cleared,
|
||||
* and DVALID eventually becomes set when RNGE is set.
|
||||
*/
|
||||
static void test_enable_disable(void)
|
||||
{
|
||||
/* Disable: DVALID should not be set, and RNGD should read zero */
|
||||
rng_reset();
|
||||
g_assert_cmphex(rng_readb(RNGCS), ==, 0);
|
||||
g_assert_cmphex(rng_readb(RNGD), ==, 0);
|
||||
|
||||
/* Enable: DVALID should be set, but we can't make assumptions about RNGD */
|
||||
rng_writeb(RNGCS, RNGE);
|
||||
g_assert_true(rng_wait_ready());
|
||||
g_assert_cmphex(rng_readb(RNGCS), ==, DVALID | RNGE);
|
||||
|
||||
/* Disable: DVALID should not be set, and RNGD should read zero */
|
||||
rng_writeb(RNGCS, 0);
|
||||
g_assert_cmphex(rng_readb(RNGCS), ==, 0);
|
||||
g_assert_cmphex(rng_readb(RNGD), ==, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Verifies that the RNG only produces data when RNGMODE is set to 'normal'
|
||||
* ring oscillator mode.
|
||||
*/
|
||||
static void test_rosel(void)
|
||||
{
|
||||
rng_reset_enable();
|
||||
g_assert_true(rng_wait_ready());
|
||||
rng_writeb(RNGMODE, 0);
|
||||
g_assert_false(rng_wait_ready());
|
||||
rng_writeb(RNGMODE, ROSEL_NORMAL);
|
||||
g_assert_true(rng_wait_ready());
|
||||
rng_writeb(RNGMODE, 0);
|
||||
g_assert_false(rng_wait_ready());
|
||||
}
|
||||
|
||||
/*
|
||||
* Verifies that a continuous sequence of bits collected after enabling the RNG
|
||||
* satisfies a monobit test.
|
||||
*/
|
||||
static void test_continuous_monobit(void)
|
||||
{
|
||||
uint8_t buf[TEST_INPUT_BITS / BITS_PER_BYTE];
|
||||
unsigned int i;
|
||||
|
||||
rng_reset_enable();
|
||||
for (i = 0; i < sizeof(buf); i++) {
|
||||
g_assert_true(rng_wait_ready());
|
||||
buf[i] = rng_readb(RNGD);
|
||||
}
|
||||
|
||||
g_assert_cmpfloat(calc_monobit_p(buf, sizeof(buf)), >, 0.01);
|
||||
}
|
||||
|
||||
/*
|
||||
* Verifies that a continuous sequence of bits collected after enabling the RNG
|
||||
* satisfies a runs test.
|
||||
*/
|
||||
static void test_continuous_runs(void)
|
||||
{
|
||||
union {
|
||||
unsigned long l[TEST_INPUT_BITS / BITS_PER_LONG];
|
||||
uint8_t c[TEST_INPUT_BITS / BITS_PER_BYTE];
|
||||
} buf;
|
||||
unsigned int i;
|
||||
|
||||
rng_reset_enable();
|
||||
for (i = 0; i < sizeof(buf); i++) {
|
||||
g_assert_true(rng_wait_ready());
|
||||
buf.c[i] = rng_readb(RNGD);
|
||||
}
|
||||
|
||||
g_assert_cmpfloat(calc_runs_p(buf.l, sizeof(buf) * BITS_PER_BYTE), >, 0.01);
|
||||
}
|
||||
|
||||
/*
|
||||
* Verifies that the first data byte collected after enabling the RNG satisfies
|
||||
* a monobit test.
|
||||
*/
|
||||
static void test_first_byte_monobit(void)
|
||||
{
|
||||
/* Enable, collect one byte, disable. Repeat until we have 100 bits. */
|
||||
uint8_t buf[TEST_INPUT_BITS / BITS_PER_BYTE];
|
||||
unsigned int i;
|
||||
|
||||
rng_reset();
|
||||
for (i = 0; i < sizeof(buf); i++) {
|
||||
rng_writeb(RNGCS, RNGE);
|
||||
g_assert_true(rng_wait_ready());
|
||||
buf[i] = rng_readb(RNGD);
|
||||
rng_writeb(RNGCS, 0);
|
||||
}
|
||||
|
||||
g_assert_cmpfloat(calc_monobit_p(buf, sizeof(buf)), >, 0.01);
|
||||
}
|
||||
|
||||
/*
|
||||
* Verifies that the first data byte collected after enabling the RNG satisfies
|
||||
* a runs test.
|
||||
*/
|
||||
static void test_first_byte_runs(void)
|
||||
{
|
||||
/* Enable, collect one byte, disable. Repeat until we have 100 bits. */
|
||||
union {
|
||||
unsigned long l[TEST_INPUT_BITS / BITS_PER_LONG];
|
||||
uint8_t c[TEST_INPUT_BITS / BITS_PER_BYTE];
|
||||
} buf;
|
||||
unsigned int i;
|
||||
|
||||
rng_reset();
|
||||
for (i = 0; i < sizeof(buf); i++) {
|
||||
rng_writeb(RNGCS, RNGE);
|
||||
g_assert_true(rng_wait_ready());
|
||||
buf.c[i] = rng_readb(RNGD);
|
||||
rng_writeb(RNGCS, 0);
|
||||
}
|
||||
|
||||
g_assert_cmpfloat(calc_runs_p(buf.l, sizeof(buf) * BITS_PER_BYTE), >, 0.01);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_set_nonfatal_assertions();
|
||||
|
||||
qtest_add_func("npcm7xx_rng/enable_disable", test_enable_disable);
|
||||
qtest_add_func("npcm7xx_rng/rosel", test_rosel);
|
||||
qtest_add_func("npcm7xx_rng/continuous/monobit", test_continuous_monobit);
|
||||
qtest_add_func("npcm7xx_rng/continuous/runs", test_continuous_runs);
|
||||
qtest_add_func("npcm7xx_rng/first_byte/monobit", test_first_byte_monobit);
|
||||
qtest_add_func("npcm7xx_rng/first_byte/runs", test_first_byte_runs);
|
||||
|
||||
qtest_start("-machine npcm750-evb");
|
||||
ret = g_test_run();
|
||||
qtest_end();
|
||||
|
||||
return ret;
|
||||
}
|
319
tests/qtest/npcm7xx_watchdog_timer-test.c
Normal file
319
tests/qtest/npcm7xx_watchdog_timer-test.c
Normal file
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
* 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 "libqos/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;
|
||||
|
||||
/* 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)));
|
||||
g_assert_false(strcmp(qdict_get_str(get_watchdog_action(qts), "action"),
|
||||
"reset"));
|
||||
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();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue