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:
Peter Maydell 2020-10-29 11:40:04 +00:00
commit 802427bcda
64 changed files with 5461 additions and 279 deletions

View file

@ -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 : []) + \

View 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;
}

View 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;
}

View 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();
}