Add qtests for RP2040 SIO, DMA, XIP/SSI, clocks, resets, watchdog, ROSC, SYSINFO/SYSCFG, VREG, IO_QSPI, TBMAN and BUSCTRL behaviour.
The tests cover the functional contracts used by the Pico machine and the Pico SDK examples rather than cycle-level timing. Signed-off-by: Gilles Grimaud <[email protected]> --- tests/qtest/meson.build | 12 + tests/qtest/rp2040-busctrl-test.c | 92 ++++++ tests/qtest/rp2040-clocks-test.c | 107 +++++++ tests/qtest/rp2040-dma-test.c | 374 +++++++++++++++++++++++ tests/qtest/rp2040-ioqspi-test.c | 81 +++++ tests/qtest/rp2040-resets-test.c | 84 +++++ tests/qtest/rp2040-rosc-test.c | 146 +++++++++ tests/qtest/rp2040-sio-test.c | 234 ++++++++++++++ tests/qtest/rp2040-sysinfo-syscfg-test.c | 177 +++++++++++ tests/qtest/rp2040-tbman-test.c | 32 ++ tests/qtest/rp2040-vreg-test.c | 79 +++++ tests/qtest/rp2040-watchdog-test.c | 148 +++++++++ tests/qtest/rp2040-xip-test.c | 212 +++++++++++++ 13 files changed, 1778 insertions(+) create mode 100644 tests/qtest/rp2040-busctrl-test.c create mode 100644 tests/qtest/rp2040-clocks-test.c create mode 100644 tests/qtest/rp2040-dma-test.c create mode 100644 tests/qtest/rp2040-ioqspi-test.c create mode 100644 tests/qtest/rp2040-resets-test.c create mode 100644 tests/qtest/rp2040-rosc-test.c create mode 100644 tests/qtest/rp2040-sio-test.c create mode 100644 tests/qtest/rp2040-sysinfo-syscfg-test.c create mode 100644 tests/qtest/rp2040-tbman-test.c create mode 100644 tests/qtest/rp2040-vreg-test.c create mode 100644 tests/qtest/rp2040-watchdog-test.c create mode 100644 tests/qtest/rp2040-xip-test.c diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 5e18b947c7..12cc8a500d 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -250,6 +250,18 @@ qtests_arm = \ (config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test', 'tpm-tis-i2c-swtpm-test'] : []) + \ (config_all_devices.has_key('CONFIG_VEXPRESS') ? ['test-arm-mptimer'] : []) + \ (config_all_devices.has_key('CONFIG_MICROBIT') ? ['microbit-test'] : []) + \ + (config_all_devices.has_key('CONFIG_RASPI_PICO') ? ['rp2040-sysinfo-syscfg-test', + 'rp2040-busctrl-test', + 'rp2040-clocks-test', + 'rp2040-dma-test', + 'rp2040-ioqspi-test', + 'rp2040-resets-test', + 'rp2040-rosc-test', + 'rp2040-sio-test', + 'rp2040-tbman-test', + 'rp2040-vreg-test', + 'rp2040-watchdog-test', + 'rp2040-xip-test'] : []) + \ (config_all_devices.has_key('CONFIG_STM32L4X5_SOC') ? qtests_stm32l4x5 : []) + \ (config_all_devices.has_key('CONFIG_FSI_APB2OPB_ASPEED') ? ['aspeed_fsi-test'] : []) + \ (config_all_devices.has_key('CONFIG_CAN_FLEXCAN') ? ['flexcan-test'] : []) + \ diff --git a/tests/qtest/rp2040-busctrl-test.c b/tests/qtest/rp2040-busctrl-test.c new file mode 100644 index 0000000000..a86fd8a20e --- /dev/null +++ b/tests/qtest/rp2040-busctrl-test.c @@ -0,0 +1,92 @@ +/* + * QTest testcase for the RP2040 bus fabric control block. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" + +#define BUSCTRL_BASE 0x40030000 +#define BUSCTRL_PRIORITY 0x00 +#define BUSCTRL_PRIORITY_ACK 0x04 +#define BUSCTRL_PERFCTR0 0x08 +#define BUSCTRL_PERFSEL0 0x0c +#define BUSCTRL_PERFCTR1 0x10 +#define BUSCTRL_PERFSEL1 0x14 +#define BUSCTRL_PRIORITY_MASK 0x00001111 +#define BUSCTRL_PERFCTR_MASK 0x00ffffff +#define BUSCTRL_PERFSEL_RESET 0x1f +#define BUSCTRL_PERFSEL_SRAM5 0x05 + +static QTestState *rp2040_start(void) +{ + return qtest_init("-machine raspi-pico"); +} + +static void test_busctrl_reset_values(void) +{ + QTestState *qts = rp2040_start(); + + g_assert_cmphex(qtest_readl(qts, BUSCTRL_BASE + BUSCTRL_PRIORITY), ==, 0); + g_assert_cmphex(qtest_readl(qts, BUSCTRL_BASE + BUSCTRL_PRIORITY_ACK), + ==, 1); + g_assert_cmphex(qtest_readl(qts, BUSCTRL_BASE + BUSCTRL_PERFSEL0), + ==, BUSCTRL_PERFSEL_RESET); + g_assert_cmphex(qtest_readl(qts, BUSCTRL_BASE + BUSCTRL_PERFSEL1), + ==, BUSCTRL_PERFSEL_RESET); + + qtest_quit(qts); +} + +static void test_busctrl_priority(void) +{ + QTestState *qts = rp2040_start(); + + qtest_writel(qts, BUSCTRL_BASE + BUSCTRL_PRIORITY, 0xffffffff); + g_assert_cmphex(qtest_readl(qts, BUSCTRL_BASE + BUSCTRL_PRIORITY), + ==, BUSCTRL_PRIORITY_MASK); + g_assert_cmphex(qtest_readl(qts, BUSCTRL_BASE + BUSCTRL_PRIORITY_ACK), + ==, 1); + + qtest_quit(qts); +} + +static void test_busctrl_perf_counter(void) +{ + QTestState *qts = rp2040_start(); + uint32_t first; + uint32_t second; + + g_assert_cmphex(qtest_readl(qts, BUSCTRL_BASE + BUSCTRL_PERFCTR0), + ==, 0); + qtest_writel(qts, BUSCTRL_BASE + BUSCTRL_PERFSEL0, + BUSCTRL_PERFSEL_SRAM5); + g_assert_cmphex(qtest_readl(qts, BUSCTRL_BASE + BUSCTRL_PERFSEL0), + ==, BUSCTRL_PERFSEL_SRAM5); + + first = qtest_readl(qts, BUSCTRL_BASE + BUSCTRL_PERFCTR0); + second = qtest_readl(qts, BUSCTRL_BASE + BUSCTRL_PERFCTR0); + g_assert_cmphex(first, ==, 1); + g_assert_cmphex(second, ==, 2); + + qtest_writel(qts, BUSCTRL_BASE + BUSCTRL_PERFCTR0, + BUSCTRL_PERFCTR_MASK); + g_assert_cmphex(qtest_readl(qts, BUSCTRL_BASE + BUSCTRL_PERFCTR0), + ==, BUSCTRL_PERFCTR_MASK); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/rp2040-busctrl/reset-values", + test_busctrl_reset_values); + qtest_add_func("/rp2040-busctrl/priority", test_busctrl_priority); + qtest_add_func("/rp2040-busctrl/perf-counter", + test_busctrl_perf_counter); + + return g_test_run(); +} diff --git a/tests/qtest/rp2040-clocks-test.c b/tests/qtest/rp2040-clocks-test.c new file mode 100644 index 0000000000..39e49eb57f --- /dev/null +++ b/tests/qtest/rp2040-clocks-test.c @@ -0,0 +1,107 @@ +/* + * QTest testcase for the RP2040 clocks block. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bitops.h" + +#define CLOCKS_BASE 0x40008000 +#define CLK_SYS_CTRL 0x3c +#define CLK_SYS_DIV 0x40 +#define CLK_PERI_CTRL 0x48 +#define CLK_PERI_DIV 0x4c +#define CLK_USB_CTRL 0x54 +#define CLK_USB_DIV 0x58 +#define CLK_ADC_CTRL 0x60 +#define CLK_ADC_DIV 0x64 +#define CLK_RTC_CTRL 0x6c +#define CLK_RTC_DIV 0x70 +#define FC0_SRC 0x94 +#define FC0_STATUS 0x98 +#define FC0_RESULT 0x9c + +#define PLL_SYS_BASE 0x40028000 +#define PLL_USB_BASE 0x4002c000 +#define PLL_CS 0x00 +#define PLL_PWR 0x04 +#define PLL_FBDIV_INT 0x08 +#define PLL_PRIM 0x0c + +#define PLL_PWR_BITS (BIT(5) | BIT(3) | BIT(2) | BIT(0)) +#define CLK_CTRL_ENABLE BIT(11) +#define FC0_STATUS_DONE BIT(4) + +static QTestState *rp2040_start(void) +{ + return qtest_init("-machine raspi-pico"); +} + +static void pll_configure(QTestState *qts, uint32_t base, + uint32_t fbdiv, uint32_t postdiv1, + uint32_t postdiv2) +{ + qtest_writel(qts, base + PLL_CS, 1); + qtest_writel(qts, base + PLL_FBDIV_INT, fbdiv); + qtest_writel(qts, base + PLL_PWR, 0); + qtest_writel(qts, base + PLL_PRIM, (postdiv1 << 16) | (postdiv2 << 12)); +} + +static uint32_t frequency_count_khz(QTestState *qts, uint32_t src) +{ + qtest_writel(qts, CLOCKS_BASE + FC0_SRC, src); + g_assert_cmphex(qtest_readl(qts, CLOCKS_BASE + FC0_STATUS) & + FC0_STATUS_DONE, ==, FC0_STATUS_DONE); + return qtest_readl(qts, CLOCKS_BASE + FC0_RESULT) >> 5; +} + +static void test_clock_mux_and_frequency_counter(void) +{ + QTestState *qts = rp2040_start(); + + pll_configure(qts, PLL_SYS_BASE, 125, 6, 2); + pll_configure(qts, PLL_USB_BASE, 100, 5, 5); + + qtest_writel(qts, CLOCKS_BASE + CLK_SYS_DIV, 0x100); + qtest_writel(qts, CLOCKS_BASE + CLK_PERI_DIV, 0x100); + qtest_writel(qts, CLOCKS_BASE + CLK_USB_DIV, 0x100); + qtest_writel(qts, CLOCKS_BASE + CLK_ADC_DIV, 0x100); + qtest_writel(qts, CLOCKS_BASE + CLK_RTC_DIV, 0x400); + + qtest_writel(qts, CLOCKS_BASE + CLK_SYS_CTRL, BIT(0)); + qtest_writel(qts, CLOCKS_BASE + CLK_PERI_CTRL, CLK_CTRL_ENABLE); + qtest_writel(qts, CLOCKS_BASE + CLK_USB_CTRL, CLK_CTRL_ENABLE); + qtest_writel(qts, CLOCKS_BASE + CLK_ADC_CTRL, CLK_CTRL_ENABLE); + qtest_writel(qts, CLOCKS_BASE + CLK_RTC_CTRL, + CLK_CTRL_ENABLE | (0x3 << 5)); + + g_assert_cmpuint(frequency_count_khz(qts, 0x01), ==, 125000); + g_assert_cmpuint(frequency_count_khz(qts, 0x02), ==, 48000); + g_assert_cmpuint(frequency_count_khz(qts, 0x03), ==, 6000); + g_assert_cmpuint(frequency_count_khz(qts, 0x09), ==, 125000); + g_assert_cmpuint(frequency_count_khz(qts, 0x0a), ==, 125000); + g_assert_cmpuint(frequency_count_khz(qts, 0x0b), ==, 48000); + g_assert_cmpuint(frequency_count_khz(qts, 0x0c), ==, 48000); + g_assert_cmpuint(frequency_count_khz(qts, 0x0d), ==, 3000); + + qtest_writel(qts, CLOCKS_BASE + CLK_SYS_CTRL, BIT(0) | (0x1 << 5)); + qtest_writel(qts, PLL_SYS_BASE + PLL_PWR, PLL_PWR_BITS); + + g_assert_cmpuint(frequency_count_khz(qts, 0x01), ==, 0); + g_assert_cmpuint(frequency_count_khz(qts, 0x09), ==, 48000); + g_assert_cmpuint(frequency_count_khz(qts, 0x0a), ==, 48000); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/rp2040-clocks/mux-and-frequency-counter", + test_clock_mux_and_frequency_counter); + + return g_test_run(); +} diff --git a/tests/qtest/rp2040-dma-test.c b/tests/qtest/rp2040-dma-test.c new file mode 100644 index 0000000000..61ad2fac40 --- /dev/null +++ b/tests/qtest/rp2040-dma-test.c @@ -0,0 +1,374 @@ +/* + * QTest testcase for the RP2040 DMA block. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bitops.h" + +#define DMA_BASE 0x50000000 +#define DMA_CH_READ_ADDR 0x00 +#define DMA_CH_WRITE_ADDR 0x04 +#define DMA_CH_TRANS_COUNT 0x08 +#define DMA_CH_CTRL_TRIG 0x0c +#define DMA_CH_AL1_CTRL 0x10 +#define DMA_CH_AL3_TRANS_COUNT 0x38 +#define DMA_CH_AL3_READ_ADDR 0x3c +#define DMA_CH_SIZE 0x40 +#define DMA_INTR 0x400 +#define DMA_INTE0 0x404 +#define DMA_INTS0 0x40c +#define DMA_TIMER0 0x420 +#define DMA_SNIFF_CTRL 0x434 +#define DMA_SNIFF_DATA 0x438 +#define DMA_CHAN_ABORT 0x444 + +#define DMA_CTRL_AHB_ERROR BIT(31) +#define DMA_CTRL_READ_ERROR BIT(30) +#define DMA_CTRL_WRITE_ERROR BIT(29) +#define DMA_CTRL_BUSY BIT(24) +#define DMA_CTRL_SNIFF_EN BIT(23) +#define DMA_CTRL_IRQ_QUIET BIT(21) +#define DMA_CTRL_TREQ_SEL_SHIFT 15 +#define DMA_CTRL_CHAIN_TO_SHIFT 11 +#define DMA_CTRL_RING_SEL BIT(10) +#define DMA_CTRL_RING_SIZE_SHIFT 6 +#define DMA_CTRL_INCR_WRITE BIT(5) +#define DMA_CTRL_INCR_READ BIT(4) +#define DMA_CTRL_DATA_SIZE_8 (0 << 2) +#define DMA_CTRL_DATA_SIZE_32 (2 << 2) +#define DMA_CTRL_EN BIT(0) + +#define DMA_SNIFF_CTRL_OUT_INV BIT(11) +#define DMA_SNIFF_CTRL_OUT_REV BIT(10) +#define DMA_SNIFF_CTRL_CALC_SUM (0xf << 5) +#define DMA_SNIFF_CTRL_EN BIT(0) + +#define DREQ_DMA_TIMER0 59 +#define DREQ_XIP_SSIRX 39 +#define DREQ_FORCE 63 + +#define XIP_SSI_BASE 0x18000000 +#define XIP_SSI_DR0 0x60 + +#define SRAM_BASE 0x20000000 +#define BAD_DMA_ADDR 0x60000000 + +static QTestState *rp2040_start(void) +{ + return qtest_init("-machine raspi-pico"); +} + +static void test_dma_xip_ssi_rx_dreq(void) +{ + QTestState *qts = rp2040_start(); + uint32_t ctrl = DMA_CTRL_EN | DMA_CTRL_INCR_WRITE | DMA_CTRL_DATA_SIZE_8 | + (DREQ_XIP_SSIRX << DMA_CTRL_TREQ_SEL_SHIFT); + + qtest_writel(qts, SRAM_BASE, 0xffffffff); + qtest_writel(qts, DMA_BASE + DMA_CH_READ_ADDR, XIP_SSI_BASE + XIP_SSI_DR0); + qtest_writel(qts, DMA_BASE + DMA_CH_WRITE_ADDR, SRAM_BASE); + qtest_writel(qts, DMA_BASE + DMA_CH_TRANS_COUNT, 4); + qtest_writel(qts, DMA_BASE + DMA_CH_CTRL_TRIG, ctrl); + + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_CTRL_TRIG) & + DMA_CTRL_BUSY, ==, DMA_CTRL_BUSY); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_TRANS_COUNT), ==, 4); + + qtest_writel(qts, XIP_SSI_BASE + XIP_SSI_DR0, 0x03); + qtest_writel(qts, XIP_SSI_BASE + XIP_SSI_DR0, 0x00); + qtest_writel(qts, XIP_SSI_BASE + XIP_SSI_DR0, 0x00); + qtest_writel(qts, XIP_SSI_BASE + XIP_SSI_DR0, 0x00); + + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_TRANS_COUNT), ==, 0); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_CTRL_TRIG) & + DMA_CTRL_BUSY, ==, 0); + g_assert_cmphex(qtest_readl(qts, SRAM_BASE), ==, 0); + + qtest_quit(qts); +} + +static void test_dma_write_ring_wrap(void) +{ + QTestState *qts = rp2040_start(); + const uint8_t source[] = { 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 }; + const uint8_t initial[] = { 0xaa, 0xaa, 0xaa, 0xaa }; + uint8_t dest[sizeof(initial)]; + uint32_t read_addr = SRAM_BASE + 0x80; + uint32_t write_addr = SRAM_BASE + 0x100; + uint32_t ctrl = DMA_CTRL_EN | DMA_CTRL_INCR_READ | DMA_CTRL_INCR_WRITE | + DMA_CTRL_RING_SEL | DMA_CTRL_DATA_SIZE_8 | + (2 << DMA_CTRL_RING_SIZE_SHIFT) | + (DREQ_FORCE << DMA_CTRL_TREQ_SEL_SHIFT); + + qtest_memwrite(qts, read_addr, source, sizeof(source)); + qtest_memwrite(qts, write_addr, initial, sizeof(initial)); + qtest_writel(qts, DMA_BASE + DMA_CH_READ_ADDR, read_addr); + qtest_writel(qts, DMA_BASE + DMA_CH_WRITE_ADDR, write_addr); + qtest_writel(qts, DMA_BASE + DMA_CH_TRANS_COUNT, sizeof(source)); + qtest_writel(qts, DMA_BASE + DMA_CH_CTRL_TRIG, ctrl); + + qtest_memread(qts, write_addr, dest, sizeof(dest)); + g_assert_cmphex(dest[0], ==, 0x14); + g_assert_cmphex(dest[1], ==, 0x15); + g_assert_cmphex(dest[2], ==, 0x12); + g_assert_cmphex(dest[3], ==, 0x13); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_TRANS_COUNT), ==, 0); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_READ_ADDR), ==, + read_addr + sizeof(source)); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_WRITE_ADDR), ==, + write_addr + 2); + + qtest_quit(qts); +} + +static void test_dma_channel_abort(void) +{ + QTestState *qts = rp2040_start(); + uint32_t ctrl = DMA_CTRL_EN | DMA_CTRL_INCR_WRITE | DMA_CTRL_DATA_SIZE_8 | + (DREQ_XIP_SSIRX << DMA_CTRL_TREQ_SEL_SHIFT); + + qtest_writel(qts, SRAM_BASE, 0xffffffff); + qtest_writel(qts, DMA_BASE + DMA_CH_READ_ADDR, XIP_SSI_BASE + XIP_SSI_DR0); + qtest_writel(qts, DMA_BASE + DMA_CH_WRITE_ADDR, SRAM_BASE); + qtest_writel(qts, DMA_BASE + DMA_CH_TRANS_COUNT, 4); + qtest_writel(qts, DMA_BASE + DMA_CH_CTRL_TRIG, ctrl); + + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_CTRL_TRIG) & + DMA_CTRL_BUSY, ==, DMA_CTRL_BUSY); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_TRANS_COUNT), ==, 4); + + qtest_writel(qts, DMA_BASE + DMA_CHAN_ABORT, BIT(0)); + + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CHAN_ABORT), ==, 0); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_CTRL_TRIG) & + DMA_CTRL_BUSY, ==, 0); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_TRANS_COUNT), ==, 0); + + qtest_writel(qts, XIP_SSI_BASE + XIP_SSI_DR0, 0x03); + qtest_writel(qts, XIP_SSI_BASE + XIP_SSI_DR0, 0x00); + qtest_writel(qts, XIP_SSI_BASE + XIP_SSI_DR0, 0x00); + qtest_writel(qts, XIP_SSI_BASE + XIP_SSI_DR0, 0x00); + + g_assert_cmphex(qtest_readl(qts, SRAM_BASE), ==, 0xffffffff); + + qtest_quit(qts); +} + +static void test_dma_sniff_sum(void) +{ + QTestState *qts = rp2040_start(); + const uint8_t source[] = { 1, 2, 3, 4 }; + uint32_t read_addr = SRAM_BASE + 0x180; + uint32_t write_addr = SRAM_BASE + 0x190; + uint32_t ctrl = DMA_CTRL_EN | DMA_CTRL_INCR_READ | DMA_CTRL_INCR_WRITE | + DMA_CTRL_SNIFF_EN | DMA_CTRL_DATA_SIZE_8 | + (DREQ_FORCE << DMA_CTRL_TREQ_SEL_SHIFT); + + qtest_memwrite(qts, read_addr, source, sizeof(source)); + qtest_writel(qts, DMA_BASE + DMA_SNIFF_DATA, 0x100); + qtest_writel(qts, DMA_BASE + DMA_SNIFF_CTRL, + DMA_SNIFF_CTRL_EN | DMA_SNIFF_CTRL_CALC_SUM); + qtest_writel(qts, DMA_BASE + DMA_CH_READ_ADDR, read_addr); + qtest_writel(qts, DMA_BASE + DMA_CH_WRITE_ADDR, write_addr); + qtest_writel(qts, DMA_BASE + DMA_CH_TRANS_COUNT, sizeof(source)); + qtest_writel(qts, DMA_BASE + DMA_CH_CTRL_TRIG, ctrl); + + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_SNIFF_DATA), ==, 0x10a); + + qtest_writel(qts, DMA_BASE + DMA_SNIFF_CTRL, + DMA_SNIFF_CTRL_EN | DMA_SNIFF_CTRL_CALC_SUM | + DMA_SNIFF_CTRL_OUT_REV); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_SNIFF_DATA), ==, + 0x50800000); + + qtest_writel(qts, DMA_BASE + DMA_SNIFF_CTRL, + DMA_SNIFF_CTRL_EN | DMA_SNIFF_CTRL_CALC_SUM | + DMA_SNIFF_CTRL_OUT_INV); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_SNIFF_DATA), ==, + 0xfffffef5); + + qtest_quit(qts); +} + +static void test_dma_error_status(void) +{ + QTestState *qts = rp2040_start(); + uint32_t ctrl = DMA_CTRL_EN | DMA_CTRL_DATA_SIZE_32 | + (DREQ_FORCE << DMA_CTRL_TREQ_SEL_SHIFT); + + qtest_writel(qts, SRAM_BASE, 0x12345678); + qtest_writel(qts, DMA_BASE + DMA_INTR, BIT(0)); + qtest_writel(qts, DMA_BASE + DMA_INTE0, BIT(0)); + qtest_writel(qts, DMA_BASE + DMA_CH_READ_ADDR, BAD_DMA_ADDR); + qtest_writel(qts, DMA_BASE + DMA_CH_WRITE_ADDR, SRAM_BASE + 4); + qtest_writel(qts, DMA_BASE + DMA_CH_TRANS_COUNT, 1); + qtest_writel(qts, DMA_BASE + DMA_CH_CTRL_TRIG, ctrl); + + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_CTRL_TRIG) & + (DMA_CTRL_AHB_ERROR | DMA_CTRL_READ_ERROR | + DMA_CTRL_WRITE_ERROR | DMA_CTRL_BUSY), ==, + DMA_CTRL_AHB_ERROR | DMA_CTRL_READ_ERROR); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_TRANS_COUNT), ==, 1); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_INTR), ==, BIT(0)); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_INTS0), ==, BIT(0)); + + qtest_writel(qts, DMA_BASE + DMA_INTS0, BIT(0)); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_INTR), ==, 0); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_INTS0), ==, 0); + + qtest_writel(qts, DMA_BASE + DMA_CH_CTRL_TRIG, + DMA_CTRL_READ_ERROR | DMA_CTRL_WRITE_ERROR); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_CTRL_TRIG) & + (DMA_CTRL_AHB_ERROR | DMA_CTRL_READ_ERROR | + DMA_CTRL_WRITE_ERROR), ==, 0); + + qtest_writel(qts, DMA_BASE + DMA_CH_READ_ADDR, SRAM_BASE); + qtest_writel(qts, DMA_BASE + DMA_CH_WRITE_ADDR, BAD_DMA_ADDR); + qtest_writel(qts, DMA_BASE + DMA_CH_TRANS_COUNT, 1); + qtest_writel(qts, DMA_BASE + DMA_CH_CTRL_TRIG, ctrl); + + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_CTRL_TRIG) & + (DMA_CTRL_AHB_ERROR | DMA_CTRL_READ_ERROR | + DMA_CTRL_WRITE_ERROR | DMA_CTRL_BUSY), ==, + DMA_CTRL_AHB_ERROR | DMA_CTRL_WRITE_ERROR); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_TRANS_COUNT), ==, 1); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_INTR), ==, BIT(0)); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_INTS0), ==, BIT(0)); + + qtest_quit(qts); +} + +static void test_dma_timer_dreq(void) +{ + QTestState *qts = rp2040_start(); + const uint8_t source[] = { 0xa1, 0xb2, 0xc3 }; + uint8_t dest[sizeof(source)]; + uint32_t read_addr = SRAM_BASE + 0x280; + uint32_t write_addr = SRAM_BASE + 0x290; + uint32_t ctrl = DMA_CTRL_EN | DMA_CTRL_INCR_READ | DMA_CTRL_INCR_WRITE | + DMA_CTRL_DATA_SIZE_8 | + (DREQ_DMA_TIMER0 << DMA_CTRL_TREQ_SEL_SHIFT); + + qtest_memwrite(qts, read_addr, source, sizeof(source)); + memset(dest, 0, sizeof(dest)); + qtest_memwrite(qts, write_addr, dest, sizeof(dest)); + qtest_writel(qts, DMA_BASE + DMA_CH_READ_ADDR, read_addr); + qtest_writel(qts, DMA_BASE + DMA_CH_WRITE_ADDR, write_addr); + qtest_writel(qts, DMA_BASE + DMA_CH_TRANS_COUNT, sizeof(source)); + qtest_writel(qts, DMA_BASE + DMA_CH_CTRL_TRIG, ctrl); + + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_CTRL_TRIG) & + DMA_CTRL_BUSY, ==, DMA_CTRL_BUSY); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_TRANS_COUNT), ==, + sizeof(source)); + + /* + * X/Y = 1/1000 on a nominal 125 MHz sys_clk gives one DREQ every + * 8000 ns of QEMU virtual time. + */ + qtest_writel(qts, DMA_BASE + DMA_TIMER0, (1u << 16) | 1000u); + qtest_clock_step(qts, 7999); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_TRANS_COUNT), ==, + sizeof(source)); + + qtest_clock_step(qts, 1); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_TRANS_COUNT), ==, 2); + g_assert_cmphex(qtest_readb(qts, write_addr), ==, source[0]); + + qtest_clock_step(qts, 8000); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_TRANS_COUNT), ==, 1); + g_assert_cmphex(qtest_readb(qts, write_addr + 1), ==, source[1]); + + qtest_clock_step(qts, 8000); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_TRANS_COUNT), ==, 0); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_CTRL_TRIG) & + DMA_CTRL_BUSY, ==, 0); + qtest_memread(qts, write_addr, dest, sizeof(dest)); + g_assert_cmpmem(dest, sizeof(dest), source, sizeof(source)); + + qtest_quit(qts); +} + +static void test_dma_control_blocks(void) +{ + QTestState *qts = rp2040_start(); + const uint8_t source[] = { 0x44, 0x4d, 0x41 }; + uint8_t dest[sizeof(source)]; + uint32_t blocks[] = { + sizeof(source), SRAM_BASE + 0x380, + 0, 0, + }; + uint32_t block_addr = SRAM_BASE + 0x300; + uint32_t read_addr = SRAM_BASE + 0x380; + uint32_t write_addr = SRAM_BASE + 0x390; + uint32_t chan1 = DMA_BASE + DMA_CH_SIZE; + uint32_t ctrl_chan_ctrl; + uint32_t data_chan_ctrl; + + qtest_memwrite(qts, block_addr, blocks, sizeof(blocks)); + qtest_memwrite(qts, read_addr, source, sizeof(source)); + + /* + * Channel 0 writes two 32-bit words into channel 1's alias 3 + * TRANS_COUNT/READ_ADDR registers. The second write triggers channel 1. + */ + ctrl_chan_ctrl = DMA_CTRL_EN | DMA_CTRL_INCR_READ | + DMA_CTRL_INCR_WRITE | DMA_CTRL_RING_SEL | + DMA_CTRL_DATA_SIZE_32 | + (3 << DMA_CTRL_RING_SIZE_SHIFT) | + (DREQ_FORCE << DMA_CTRL_TREQ_SEL_SHIFT); + + /* + * Channel 1 copies the configured block and chains back to channel 0. + * The final zero-length control block sets channel 1's interrupt via + * IRQ_QUIET, matching the SDK control_blocks example's termination. + */ + data_chan_ctrl = DMA_CTRL_EN | DMA_CTRL_INCR_READ | + DMA_CTRL_INCR_WRITE | DMA_CTRL_IRQ_QUIET | + (DREQ_FORCE << DMA_CTRL_TREQ_SEL_SHIFT) | + (0 << DMA_CTRL_CHAIN_TO_SHIFT); + + qtest_writel(qts, chan1 + DMA_CH_WRITE_ADDR, write_addr); + qtest_writel(qts, chan1 + DMA_CH_AL1_CTRL, data_chan_ctrl); + + qtest_writel(qts, DMA_BASE + DMA_CH_READ_ADDR, block_addr); + qtest_writel(qts, DMA_BASE + DMA_CH_WRITE_ADDR, + chan1 + DMA_CH_AL3_TRANS_COUNT); + qtest_writel(qts, DMA_BASE + DMA_CH_TRANS_COUNT, 2); + qtest_writel(qts, DMA_BASE + DMA_CH_CTRL_TRIG, ctrl_chan_ctrl); + + qtest_memread(qts, write_addr, dest, sizeof(dest)); + g_assert_cmpmem(dest, sizeof(dest), source, sizeof(source)); + g_assert_cmphex(qtest_readl(qts, chan1 + DMA_CH_TRANS_COUNT), ==, 0); + g_assert_cmphex(qtest_readl(qts, chan1 + DMA_CH_CTRL_TRIG) & + DMA_CTRL_BUSY, ==, 0); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_INTR) & BIT(1), ==, + BIT(1)); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/rp2040-dma/xip-ssi-rx-dreq", + test_dma_xip_ssi_rx_dreq); + qtest_add_func("/rp2040-dma/write-ring-wrap", + test_dma_write_ring_wrap); + qtest_add_func("/rp2040-dma/channel-abort", + test_dma_channel_abort); + qtest_add_func("/rp2040-dma/sniff-sum", + test_dma_sniff_sum); + qtest_add_func("/rp2040-dma/error-status", + test_dma_error_status); + qtest_add_func("/rp2040-dma/timer-dreq", + test_dma_timer_dreq); + qtest_add_func("/rp2040-dma/control-blocks", + test_dma_control_blocks); + + return g_test_run(); +} diff --git a/tests/qtest/rp2040-ioqspi-test.c b/tests/qtest/rp2040-ioqspi-test.c new file mode 100644 index 0000000000..4f94b5f3a2 --- /dev/null +++ b/tests/qtest/rp2040-ioqspi-test.c @@ -0,0 +1,81 @@ +/* + * QTest testcase for the RP2040 QSPI IO bank block. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bitops.h" + +#define IOQSPI_BASE 0x40018000 +#define IOQSPI_SCLK_STATUS 0x00 +#define IOQSPI_SCLK_CTRL 0x04 +#define IOQSPI_SD1_CTRL 0x1c +#define IOQSPI_PROC0_INTE 0x34 +#define IOQSPI_PROC0_INTF 0x38 +#define IOQSPI_PROC0_INTS 0x3c + +#define IOQSPI_CTRL_RESET 0x1f +#define IOQSPI_SD1_CTRL_SET 0x201c +#define IOQSPI_SD1_CTRL_CLR 0x301c + +static QTestState *rp2040_start(void) +{ + return qtest_init("-machine raspi-pico"); +} + +static void test_ioqspi_reset_values(void) +{ + QTestState *qts = rp2040_start(); + + g_assert_cmphex(qtest_readl(qts, IOQSPI_BASE + IOQSPI_SCLK_STATUS), ==, + 0); + g_assert_cmphex(qtest_readl(qts, IOQSPI_BASE + IOQSPI_SCLK_CTRL), ==, + IOQSPI_CTRL_RESET); + g_assert_cmphex(qtest_readl(qts, IOQSPI_BASE + IOQSPI_SD1_CTRL), ==, + IOQSPI_CTRL_RESET); + + qtest_quit(qts); +} + +static void test_ioqspi_atomic_ctrl_aliases(void) +{ + QTestState *qts = rp2040_start(); + + qtest_writel(qts, IOQSPI_BASE + IOQSPI_SD1_CTRL, 0); + qtest_writel(qts, IOQSPI_BASE + IOQSPI_SD1_CTRL_SET, BIT(17)); + qtest_writel(qts, IOQSPI_BASE + IOQSPI_SD1_CTRL_CLR, BIT(17)); + qtest_writel(qts, IOQSPI_BASE + IOQSPI_SD1_CTRL_SET, BIT(9)); + + g_assert_cmphex(qtest_readl(qts, IOQSPI_BASE + IOQSPI_SD1_CTRL), ==, + BIT(9)); + + qtest_quit(qts); +} + +static void test_ioqspi_interrupt_force_status(void) +{ + QTestState *qts = rp2040_start(); + + qtest_writel(qts, IOQSPI_BASE + IOQSPI_PROC0_INTE, BIT(1)); + qtest_writel(qts, IOQSPI_BASE + IOQSPI_PROC0_INTF, BIT(2)); + + g_assert_cmphex(qtest_readl(qts, IOQSPI_BASE + IOQSPI_PROC0_INTS), ==, + BIT(2)); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/rp2040-ioqspi/reset-values", test_ioqspi_reset_values); + qtest_add_func("/rp2040-ioqspi/atomic-ctrl-aliases", + test_ioqspi_atomic_ctrl_aliases); + qtest_add_func("/rp2040-ioqspi/interrupt-force-status", + test_ioqspi_interrupt_force_status); + + return g_test_run(); +} diff --git a/tests/qtest/rp2040-resets-test.c b/tests/qtest/rp2040-resets-test.c new file mode 100644 index 0000000000..f874a25571 --- /dev/null +++ b/tests/qtest/rp2040-resets-test.c @@ -0,0 +1,84 @@ +/* + * QTest testcase for the RP2040 reset controller block. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" + +#define RESETS_BASE 0x4000c000 +#define RESETS_RESET 0x00 +#define RESETS_WDSEL 0x04 +#define RESETS_RESET_DONE 0x08 + +#define RESETS_VALID_MASK 0x01ffffff +#define RESET_ADC (1u << 0) +#define RESET_PWM (1u << 14) + +#define ATOMIC_SET 0x2000 +#define ATOMIC_CLR 0x3000 + +static QTestState *rp2040_start(void) +{ + return qtest_init("-machine raspi-pico"); +} + +static void test_resets_reset_values(void) +{ + QTestState *qts = rp2040_start(); + + g_assert_cmphex(qtest_readl(qts, RESETS_BASE + RESETS_RESET), ==, + RESETS_VALID_MASK); + g_assert_cmphex(qtest_readl(qts, RESETS_BASE + RESETS_RESET_DONE), ==, 0); + g_assert_cmphex(qtest_readl(qts, RESETS_BASE + RESETS_WDSEL), ==, 0); + + qtest_quit(qts); +} + +static void test_resets_set_clear_done(void) +{ + QTestState *qts = rp2040_start(); + uint32_t mask = RESET_PWM | RESET_ADC; + + qtest_writel(qts, RESETS_BASE + RESETS_RESET + ATOMIC_CLR, mask); + g_assert_cmphex(qtest_readl(qts, RESETS_BASE + RESETS_RESET) & mask, + ==, 0); + g_assert_cmphex(qtest_readl(qts, RESETS_BASE + RESETS_RESET_DONE) & mask, + ==, mask); + + qtest_writel(qts, RESETS_BASE + RESETS_RESET + ATOMIC_SET, RESET_PWM); + g_assert_cmphex(qtest_readl(qts, RESETS_BASE + RESETS_RESET) & RESET_PWM, + ==, RESET_PWM); + g_assert_cmphex(qtest_readl(qts, RESETS_BASE + RESETS_RESET_DONE) & + RESET_PWM, ==, 0); + + qtest_writel(qts, RESETS_BASE + RESETS_RESET + ATOMIC_CLR, RESET_PWM); + g_assert_cmphex(qtest_readl(qts, RESETS_BASE + RESETS_RESET_DONE) & mask, + ==, mask); + + qtest_quit(qts); +} + +static void test_resets_wdsel(void) +{ + QTestState *qts = rp2040_start(); + + qtest_writel(qts, RESETS_BASE + RESETS_WDSEL, 0xffffffff); + g_assert_cmphex(qtest_readl(qts, RESETS_BASE + RESETS_WDSEL), ==, + RESETS_VALID_MASK); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/rp2040-resets/reset-values", test_resets_reset_values); + qtest_add_func("/rp2040-resets/set-clear-done", + test_resets_set_clear_done); + qtest_add_func("/rp2040-resets/wdsel", test_resets_wdsel); + + return g_test_run(); +} diff --git a/tests/qtest/rp2040-rosc-test.c b/tests/qtest/rp2040-rosc-test.c new file mode 100644 index 0000000000..2f1fb96022 --- /dev/null +++ b/tests/qtest/rp2040-rosc-test.c @@ -0,0 +1,146 @@ +/* + * QTest testcase for the RP2040 ring oscillator block. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bitops.h" + +#define ROSC_BASE 0x40060000 +#define ROSC_CTRL 0x00 +#define ROSC_FREQA 0x04 +#define ROSC_DORMANT 0x0c +#define ROSC_DIV 0x10 +#define ROSC_PHASE 0x14 +#define ROSC_STATUS 0x18 +#define ROSC_RANDOMBIT 0x1c +#define ROSC_COUNT 0x20 + +#define ROSC_ENABLE_ENABLE 0xfab +#define ROSC_ENABLE_DISABLE 0xd1e +#define ROSC_FREQ_RANGE_LOW 0xfa4 +#define ROSC_WAKE_VALUE 0x77616b65 +#define ROSC_STATUS_STABLE BIT(31) +#define ROSC_STATUS_BADWRITE BIT(24) +#define ROSC_STATUS_DIV_RUNNING BIT(16) +#define ROSC_STATUS_ENABLED BIT(12) +#define ROSC_STATUS_RUNNING \ + (ROSC_STATUS_STABLE | ROSC_STATUS_DIV_RUNNING | ROSC_STATUS_ENABLED) + +static QTestState *rp2040_start(void) +{ + return qtest_init("-machine raspi-pico"); +} + +static uint32_t read_random_bits(QTestState *qts, unsigned count) +{ + uint32_t value = 0; + unsigned i; + + for (i = 0; i < count; i++) { + value |= (qtest_readl(qts, ROSC_BASE + ROSC_RANDOMBIT) & BIT(0)) << i; + } + + return value; +} + +static void test_rosc_reset_values(void) +{ + QTestState *qts = rp2040_start(); + + g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_CTRL), ==, + (ROSC_ENABLE_ENABLE << 12) | ROSC_FREQ_RANGE_LOW); + g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_DORMANT), ==, + ROSC_WAKE_VALUE); + g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_DIV), ==, 0x00000ab0); + g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_STATUS), ==, + ROSC_STATUS_RUNNING); + + qtest_quit(qts); +} + +static void test_rosc_badwrite_and_clear(void) +{ + QTestState *qts = rp2040_start(); + + qtest_writel(qts, ROSC_BASE + ROSC_FREQA, 0x00007777); + g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_FREQA), ==, 0); + g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_STATUS) & + ROSC_STATUS_BADWRITE, ==, ROSC_STATUS_BADWRITE); + + qtest_writel(qts, ROSC_BASE + ROSC_STATUS, ROSC_STATUS_BADWRITE); + g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_STATUS) & + ROSC_STATUS_BADWRITE, ==, 0); + + qtest_quit(qts); +} + +static void test_rosc_count_reaches_zero(void) +{ + QTestState *qts = rp2040_start(); + int i; + + qtest_writel(qts, ROSC_BASE + ROSC_COUNT, 1); + for (i = 0; i < 16 && qtest_readl(qts, ROSC_BASE + ROSC_COUNT); i++) { + qtest_clock_step(qts, 1000); + } + + g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_COUNT), ==, 0); + + qtest_quit(qts); +} + +static void test_rosc_misc_values(void) +{ + QTestState *qts = rp2040_start(); + + g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_RANDOMBIT) & ~BIT(0), + ==, 0); + qtest_writel(qts, ROSC_BASE + ROSC_DIV, 0xaa1); + g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_DIV), ==, 0xaa1); + qtest_writel(qts, ROSC_BASE + ROSC_PHASE, 0xaa8); + g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_PHASE), ==, 0xaa8); + + qtest_writel(qts, ROSC_BASE + ROSC_CTRL, + (ROSC_ENABLE_DISABLE << 12) | ROSC_FREQ_RANGE_LOW); + g_assert_cmphex(qtest_readl(qts, ROSC_BASE + ROSC_STATUS) & + ROSC_STATUS_ENABLED, ==, 0); + + qtest_quit(qts); +} + +static void test_rosc_seeded_randombit(void) +{ + QTestState *qts_a; + QTestState *qts_b; + uint32_t bits_a; + uint32_t bits_b; + + qts_a = qtest_init("-machine raspi-pico,rosc-random-seed=0x1234"); + bits_a = read_random_bits(qts_a, 32); + qtest_quit(qts_a); + + qts_b = qtest_init("-machine raspi-pico,rosc-random-seed=0x1234"); + bits_b = read_random_bits(qts_b, 32); + qtest_quit(qts_b); + + g_assert_cmphex(bits_a, ==, bits_b); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/rp2040-rosc/reset-values", test_rosc_reset_values); + qtest_add_func("/rp2040-rosc/badwrite-and-clear", + test_rosc_badwrite_and_clear); + qtest_add_func("/rp2040-rosc/count-reaches-zero", + test_rosc_count_reaches_zero); + qtest_add_func("/rp2040-rosc/misc-values", test_rosc_misc_values); + qtest_add_func("/rp2040-rosc/seeded-randombit", + test_rosc_seeded_randombit); + + return g_test_run(); +} diff --git a/tests/qtest/rp2040-sio-test.c b/tests/qtest/rp2040-sio-test.c new file mode 100644 index 0000000000..8212497e28 --- /dev/null +++ b/tests/qtest/rp2040-sio-test.c @@ -0,0 +1,234 @@ +/* + * QTest testcase for the RP2040 SIO block. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bitops.h" + +#define SIO_BASE 0xd0000000 +#define SIO_CPUID 0x000 +#define SIO_GPIO_IN 0x004 +#define SIO_GPIO_HI_IN 0x008 +#define SIO_GPIO_OUT 0x010 +#define SIO_GPIO_OUT_SET 0x014 +#define SIO_GPIO_OUT_CLR 0x018 +#define SIO_GPIO_OUT_XOR 0x01c +#define SIO_GPIO_HI_OUT 0x030 +#define SIO_GPIO_HI_OUT_SET 0x034 +#define SIO_GPIO_HI_OUT_CLR 0x038 +#define SIO_GPIO_HI_OUT_XOR 0x03c +#define SIO_FIFO_ST 0x050 +#define SIO_FIFO_RD 0x058 +#define SIO_SPINLOCK_ST 0x05c +#define SIO_SPINLOCK0 0x100 +#define SIO_INTERP0_ACCUM0 0x080 +#define SIO_INTERP0_ACCUM1 0x084 +#define SIO_INTERP0_BASE0 0x088 +#define SIO_INTERP0_BASE1 0x08c +#define SIO_INTERP0_POP_LANE1 0x098 +#define SIO_INTERP0_PEEK_LANE0 0x0a0 +#define SIO_INTERP0_PEEK_LANE1 0x0a4 +#define SIO_INTERP0_CTRL_LANE0 0x0ac +#define SIO_INTERP0_CTRL_LANE1 0x0b0 +#define SIO_INTERP0_ACCUM0_ADD 0x0b4 +#define SIO_INTERP0_BASE_1AND0 0x0bc +#define SIO_INTERP1_ACCUM0 0x0c0 +#define SIO_INTERP1_BASE0 0x0c8 +#define SIO_INTERP1_BASE1 0x0cc +#define SIO_INTERP1_PEEK_LANE0 0x0e0 +#define SIO_INTERP1_CTRL_LANE0 0x0ec + +#define INTERP_CTRL_SHIFT(n) (n) +#define INTERP_CTRL_MASK(lsb, msb) (((lsb) << 5) | ((msb) << 10)) +#define INTERP_CTRL_SIGNED BIT(15) +#define INTERP_CTRL_CROSS_RESULT BIT(17) +#define INTERP0_CTRL_BLEND BIT(21) +#define INTERP1_CTRL_CLAMP BIT(22) + +static QTestState *rp2040_start(void) +{ + return qtest_init("-machine raspi-pico"); +} + +static void test_sio_reset_values(void) +{ + QTestState *qts = rp2040_start(); + + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_CPUID), ==, 0); + g_assert_cmphex(qtest_readb(qts, SIO_BASE + SIO_CPUID), ==, 0); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_GPIO_IN), ==, 0); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_GPIO_HI_IN), ==, BIT(1)); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_GPIO_OUT), ==, 0); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_GPIO_HI_OUT), ==, 0); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_FIFO_ST), ==, BIT(1)); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_SPINLOCK_ST), ==, 0); + + qtest_quit(qts); +} + +static void test_sio_gpio_alias_registers(void) +{ + QTestState *qts = rp2040_start(); + + qtest_writel(qts, SIO_BASE + SIO_GPIO_OUT, BIT(3)); + qtest_writel(qts, SIO_BASE + SIO_GPIO_OUT_SET, BIT(5)); + qtest_writel(qts, SIO_BASE + SIO_GPIO_OUT_CLR, BIT(3)); + qtest_writel(qts, SIO_BASE + SIO_GPIO_OUT_XOR, BIT(7)); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_GPIO_OUT), ==, + BIT(5) | BIT(7)); + + qtest_writel(qts, SIO_BASE + SIO_GPIO_HI_OUT, BIT(0)); + qtest_writel(qts, SIO_BASE + SIO_GPIO_HI_OUT_SET, BIT(2)); + qtest_writel(qts, SIO_BASE + SIO_GPIO_HI_OUT_CLR, BIT(0)); + qtest_writel(qts, SIO_BASE + SIO_GPIO_HI_OUT_XOR, BIT(5)); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_GPIO_HI_OUT), ==, + BIT(2) | BIT(5)); + + qtest_quit(qts); +} + +static void test_sio_fifo_empty_read_sets_roe(void) +{ + QTestState *qts = rp2040_start(); + + qtest_readl(qts, SIO_BASE + SIO_FIFO_RD); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_FIFO_ST), ==, + BIT(3) | BIT(1)); + + qtest_writel(qts, SIO_BASE + SIO_FIFO_ST, BIT(3)); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_FIFO_ST), ==, BIT(1)); + + qtest_quit(qts); +} + +static void test_sio_spinlock_claim_release(void) +{ + QTestState *qts = rp2040_start(); + + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_SPINLOCK0), ==, BIT(0)); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_SPINLOCK0), ==, 0); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_SPINLOCK_ST), ==, BIT(0)); + + qtest_writel(qts, SIO_BASE + SIO_SPINLOCK0, 0); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_SPINLOCK_ST), ==, 0); + + qtest_quit(qts); +} + +static void test_sio_interp_mask_and_sign(void) +{ + QTestState *qts = rp2040_start(); + + qtest_writel(qts, SIO_BASE + SIO_INTERP0_ACCUM0, 0x1234abcd); + qtest_writel(qts, SIO_BASE + SIO_INTERP0_CTRL_LANE0, + INTERP_CTRL_MASK(4, 7)); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_INTERP0_ACCUM0_ADD), + ==, 0x000000c0); + + qtest_writel(qts, SIO_BASE + SIO_INTERP0_CTRL_LANE0, + INTERP_CTRL_MASK(4, 7) | INTERP_CTRL_SIGNED); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_INTERP0_ACCUM0_ADD), + ==, 0xffffffc0); + + qtest_quit(qts); +} + +static void test_sio_interp_pop_cross_result(void) +{ + QTestState *qts = rp2040_start(); + uint32_t full_mask = INTERP_CTRL_MASK(0, 31); + + qtest_writel(qts, SIO_BASE + SIO_INTERP0_ACCUM0, 123); + qtest_writel(qts, SIO_BASE + SIO_INTERP0_ACCUM1, 456); + qtest_writel(qts, SIO_BASE + SIO_INTERP0_BASE0, 1); + qtest_writel(qts, SIO_BASE + SIO_INTERP0_BASE1, 0); + qtest_writel(qts, SIO_BASE + SIO_INTERP0_CTRL_LANE0, + full_mask | INTERP_CTRL_CROSS_RESULT); + qtest_writel(qts, SIO_BASE + SIO_INTERP0_CTRL_LANE1, + full_mask | INTERP_CTRL_CROSS_RESULT); + + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_INTERP0_PEEK_LANE0), + ==, 124); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_INTERP0_POP_LANE1), + ==, 456); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_INTERP0_PEEK_LANE0), + ==, 457); + + qtest_quit(qts); +} + +static void test_sio_interp_blend(void) +{ + QTestState *qts = rp2040_start(); + uint32_t full_mask = INTERP_CTRL_MASK(0, 31); + + qtest_writel(qts, SIO_BASE + SIO_INTERP0_CTRL_LANE0, + full_mask | INTERP0_CTRL_BLEND); + qtest_writel(qts, SIO_BASE + SIO_INTERP0_CTRL_LANE1, full_mask); + qtest_writel(qts, SIO_BASE + SIO_INTERP0_ACCUM1, 128); + + qtest_writel(qts, SIO_BASE + SIO_INTERP0_BASE_1AND0, 0x30005000); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_INTERP0_PEEK_LANE1), + ==, 0x00004000); + + qtest_writel(qts, SIO_BASE + SIO_INTERP0_BASE_1AND0, 0xe000f000); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_INTERP0_PEEK_LANE1), + ==, 0x0000e800); + + qtest_writel(qts, SIO_BASE + SIO_INTERP0_CTRL_LANE1, + full_mask | INTERP_CTRL_SIGNED); + qtest_writel(qts, SIO_BASE + SIO_INTERP0_BASE_1AND0, 0xe000f000); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_INTERP0_PEEK_LANE1), + ==, 0xffffe800); + + qtest_quit(qts); +} + +static void test_sio_interp_clamp(void) +{ + QTestState *qts = rp2040_start(); + + qtest_writel(qts, SIO_BASE + SIO_INTERP1_CTRL_LANE0, + INTERP1_CTRL_CLAMP | INTERP_CTRL_SIGNED | + INTERP_CTRL_SHIFT(2) | INTERP_CTRL_MASK(0, 29)); + qtest_writel(qts, SIO_BASE + SIO_INTERP1_BASE0, 0); + qtest_writel(qts, SIO_BASE + SIO_INTERP1_BASE1, 255); + + qtest_writel(qts, SIO_BASE + SIO_INTERP1_ACCUM0, -1024); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_INTERP1_PEEK_LANE0), + ==, 0); + + qtest_writel(qts, SIO_BASE + SIO_INTERP1_ACCUM0, 512); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_INTERP1_PEEK_LANE0), + ==, 128); + + qtest_writel(qts, SIO_BASE + SIO_INTERP1_ACCUM0, 1024); + g_assert_cmphex(qtest_readl(qts, SIO_BASE + SIO_INTERP1_PEEK_LANE0), + ==, 255); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/rp2040-sio/reset-values", test_sio_reset_values); + qtest_add_func("/rp2040-sio/gpio-alias-registers", + test_sio_gpio_alias_registers); + qtest_add_func("/rp2040-sio/fifo-empty-read-sets-roe", + test_sio_fifo_empty_read_sets_roe); + qtest_add_func("/rp2040-sio/spinlock-claim-release", + test_sio_spinlock_claim_release); + qtest_add_func("/rp2040-sio/interp-mask-and-sign", + test_sio_interp_mask_and_sign); + qtest_add_func("/rp2040-sio/interp-pop-cross-result", + test_sio_interp_pop_cross_result); + qtest_add_func("/rp2040-sio/interp-blend", test_sio_interp_blend); + qtest_add_func("/rp2040-sio/interp-clamp", test_sio_interp_clamp); + + return g_test_run(); +} diff --git a/tests/qtest/rp2040-sysinfo-syscfg-test.c b/tests/qtest/rp2040-sysinfo-syscfg-test.c new file mode 100644 index 0000000000..7440c3570b --- /dev/null +++ b/tests/qtest/rp2040-sysinfo-syscfg-test.c @@ -0,0 +1,177 @@ +/* + * QTest testcase for the RP2040 sysinfo and syscfg blocks. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bitops.h" + +#define SYSINFO_BASE 0x40000000 +#define SYSINFO_CHIP_ID 0x00 +#define SYSINFO_PLATFORM 0x04 +#define SYSINFO_GITREF_RP2040 0x40 + +#define SYSINFO_PLATFORM_ASIC BIT(1) + +#define SYSCFG_BASE 0x40004000 +#define SYSCFG_PROC0_NMI_MASK 0x00 +#define SYSCFG_PROC1_NMI_MASK 0x04 +#define SYSCFG_PROC_CONFIG 0x08 +#define SYSCFG_PROC_IN_SYNC_BYPASS 0x0c +#define SYSCFG_PROC_IN_SYNC_BYPASS_HI 0x10 +#define SYSCFG_DBGFORCE 0x14 +#define SYSCFG_MEMPOWERDOWN 0x18 + +#define SRAM0_BASE 0x20000000 +#define UART0_BASE 0x40034000 +#define UARTDR 0x00 +#define UARTCR 0x30 +#define UARTIMSC 0x38 +#define UARTICR 0x44 +#define UART_INT_TX BIT(5) +#define UART0_IRQ 20 +#define NVIC_ISPR 0xe000e200 +#define NVIC_ICPR 0xe000e280 +#define NVIC_ICSR 0xe000ed04 +#define NVIC_ICSR_NMIPENDSET BIT(31) + +#define PROC_CONFIG_RESET 0x10000000 +#define DBGFORCE_RESET 0x00000066 + +#define ATOMIC_SET_ALIAS 0x2000 +#define ATOMIC_CLR_ALIAS 0x3000 + +static QTestState *rp2040_start(void) +{ + return qtest_init("-machine raspi-pico"); +} + +static void test_sysinfo_read_values(void) +{ + QTestState *qts = rp2040_start(); + + g_assert_cmphex(qtest_readl(qts, SYSINFO_BASE + SYSINFO_CHIP_ID), ==, 0); + g_assert_cmphex(qtest_readl(qts, SYSINFO_BASE + SYSINFO_PLATFORM), ==, + SYSINFO_PLATFORM_ASIC); + g_assert_cmphex(qtest_readl(qts, SYSINFO_BASE + SYSINFO_GITREF_RP2040), + ==, 0); + + qtest_quit(qts); +} + +static void test_syscfg_reset_values(void) +{ + QTestState *qts = rp2040_start(); + + g_assert_cmphex(qtest_readl(qts, SYSCFG_BASE + SYSCFG_PROC0_NMI_MASK), + ==, 0); + g_assert_cmphex(qtest_readl(qts, SYSCFG_BASE + SYSCFG_PROC1_NMI_MASK), + ==, 0); + g_assert_cmphex(qtest_readl(qts, SYSCFG_BASE + SYSCFG_PROC_CONFIG), ==, + PROC_CONFIG_RESET); + g_assert_cmphex(qtest_readl(qts, SYSCFG_BASE + SYSCFG_DBGFORCE), ==, + DBGFORCE_RESET); + + qtest_quit(qts); +} + +static void test_syscfg_rw_masks(void) +{ + QTestState *qts = rp2040_start(); + + qtest_writel(qts, SYSCFG_BASE + SYSCFG_PROC0_NMI_MASK, 0xa5a5a5a5); + qtest_writel(qts, SYSCFG_BASE + SYSCFG_PROC1_NMI_MASK, 0x5a5a5a5a); + g_assert_cmphex(qtest_readl(qts, SYSCFG_BASE + SYSCFG_PROC0_NMI_MASK), + ==, 0xa5a5a5a5); + g_assert_cmphex(qtest_readl(qts, SYSCFG_BASE + SYSCFG_PROC1_NMI_MASK), + ==, 0x5a5a5a5a); + + qtest_writel(qts, SYSCFG_BASE + SYSCFG_PROC_IN_SYNC_BYPASS, 0xffffffff); + qtest_writel(qts, SYSCFG_BASE + SYSCFG_PROC_IN_SYNC_BYPASS_HI, + 0xffffffff); + qtest_writel(qts, SYSCFG_BASE + SYSCFG_MEMPOWERDOWN, 0xffffffff); + g_assert_cmphex(qtest_readl(qts, + SYSCFG_BASE + SYSCFG_PROC_IN_SYNC_BYPASS), + ==, 0x3fffffff); + g_assert_cmphex(qtest_readl(qts, + SYSCFG_BASE + SYSCFG_PROC_IN_SYNC_BYPASS_HI), + ==, 0x0000003f); + g_assert_cmphex(qtest_readl(qts, SYSCFG_BASE + SYSCFG_MEMPOWERDOWN), ==, + 0x000000ff); + + qtest_quit(qts); +} + +static void test_syscfg_atomic_aliases(void) +{ + QTestState *qts = rp2040_start(); + + qtest_writel(qts, SYSCFG_BASE + SYSCFG_PROC0_NMI_MASK, 0); + qtest_writel(qts, SYSCFG_BASE + ATOMIC_SET_ALIAS + SYSCFG_PROC0_NMI_MASK, + 0x0000000f); + qtest_writel(qts, SYSCFG_BASE + ATOMIC_CLR_ALIAS + SYSCFG_PROC0_NMI_MASK, + 0x00000003); + g_assert_cmphex(qtest_readl(qts, SYSCFG_BASE + SYSCFG_PROC0_NMI_MASK), + ==, 0x0000000c); + + qtest_quit(qts); +} + +static void test_syscfg_mempowerdown_blocks_sram(void) +{ + QTestState *qts = rp2040_start(); + + qtest_writel(qts, SRAM0_BASE, 0x11223344); + g_assert_cmphex(qtest_readl(qts, SRAM0_BASE), ==, 0x11223344); + + qtest_writel(qts, SYSCFG_BASE + SYSCFG_MEMPOWERDOWN, BIT(0)); + qtest_writel(qts, SRAM0_BASE, 0xaabbccdd); + qtest_writel(qts, SYSCFG_BASE + SYSCFG_MEMPOWERDOWN, 0); + + g_assert_cmphex(qtest_readl(qts, SRAM0_BASE), ==, 0x11223344); + + qtest_quit(qts); +} + +static void test_syscfg_nmi_mask_routes_uart0_irq(void) +{ + QTestState *qts = rp2040_start(); + + qtest_writel(qts, UART0_BASE + UARTCR, BIT(0) | BIT(8)); + qtest_writel(qts, UART0_BASE + UARTIMSC, UART_INT_TX); + + qtest_writel(qts, UART0_BASE + UARTICR, UART_INT_TX); + qtest_writel(qts, NVIC_ICPR, BIT(UART0_IRQ)); + qtest_writel(qts, UART0_BASE + UARTDR, 'a'); + g_assert_cmphex(qtest_readl(qts, NVIC_ISPR) & BIT(UART0_IRQ), ==, + BIT(UART0_IRQ)); + + qtest_writel(qts, UART0_BASE + UARTICR, UART_INT_TX); + qtest_writel(qts, NVIC_ICPR, BIT(UART0_IRQ)); + qtest_writel(qts, SYSCFG_BASE + SYSCFG_PROC0_NMI_MASK, BIT(UART0_IRQ)); + qtest_writel(qts, UART0_BASE + UARTDR, 'b'); + g_assert_cmphex(qtest_readl(qts, NVIC_ISPR) & BIT(UART0_IRQ), ==, 0); + g_assert_cmphex(qtest_readl(qts, NVIC_ICSR) & NVIC_ICSR_NMIPENDSET, ==, + NVIC_ICSR_NMIPENDSET); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/rp2040-sysinfo/read-values", test_sysinfo_read_values); + qtest_add_func("/rp2040-syscfg/reset-values", test_syscfg_reset_values); + qtest_add_func("/rp2040-syscfg/rw-masks", test_syscfg_rw_masks); + qtest_add_func("/rp2040-syscfg/atomic-aliases", + test_syscfg_atomic_aliases); + qtest_add_func("/rp2040-syscfg/mempowerdown-blocks-sram", + test_syscfg_mempowerdown_blocks_sram); + qtest_add_func("/rp2040-syscfg/nmi-mask-routes-uart0-irq", + test_syscfg_nmi_mask_routes_uart0_irq); + + return g_test_run(); +} diff --git a/tests/qtest/rp2040-tbman-test.c b/tests/qtest/rp2040-tbman-test.c new file mode 100644 index 0000000000..a05eb3c3e2 --- /dev/null +++ b/tests/qtest/rp2040-tbman-test.c @@ -0,0 +1,32 @@ +/* + * QTest testcase for the RP2040 testbench manager block. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bitops.h" + +#define TBMAN_BASE 0x4006c000 +#define TBMAN_PLATFORM 0x00 +#define TBMAN_PLATFORM_ASIC BIT(0) + +static void test_tbman_platform(void) +{ + QTestState *qts = qtest_init("-machine raspi-pico"); + + g_assert_cmphex(qtest_readl(qts, TBMAN_BASE + TBMAN_PLATFORM), ==, + TBMAN_PLATFORM_ASIC); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/rp2040-tbman/platform", test_tbman_platform); + + return g_test_run(); +} diff --git a/tests/qtest/rp2040-vreg-test.c b/tests/qtest/rp2040-vreg-test.c new file mode 100644 index 0000000000..b08bb16765 --- /dev/null +++ b/tests/qtest/rp2040-vreg-test.c @@ -0,0 +1,79 @@ +/* + * QTest testcase for the RP2040 vreg_and_chip_reset block. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bitops.h" + +#define VREG_BASE 0x40064000 +#define VREG_VREG 0x00 +#define VREG_BOD 0x04 +#define VREG_CHIP_RESET 0x08 + +#define VREG_ROK BIT(12) +#define VREG_RESET 0x000000b1 +#define BOD_RESET 0x00000091 +#define CHIP_RESET_RESCUE BIT(24) + +#define ATOMIC_SET_ALIAS 0x2000 +#define ATOMIC_CLR_ALIAS 0x3000 + +static QTestState *rp2040_start(void) +{ + return qtest_init("-machine raspi-pico"); +} + +static void test_vreg_reset_values(void) +{ + QTestState *qts = rp2040_start(); + + g_assert_cmphex(qtest_readl(qts, VREG_BASE + VREG_VREG), ==, + VREG_RESET | VREG_ROK); + g_assert_cmphex(qtest_readl(qts, VREG_BASE + VREG_BOD), ==, BOD_RESET); + g_assert_cmphex(qtest_readl(qts, VREG_BASE + VREG_CHIP_RESET), ==, 0); + + qtest_quit(qts); +} + +static void test_vreg_rw_masks(void) +{ + QTestState *qts = rp2040_start(); + + qtest_writel(qts, VREG_BASE + VREG_VREG, 0xffffffff); + qtest_writel(qts, VREG_BASE + VREG_BOD, 0xffffffff); + qtest_writel(qts, VREG_BASE + VREG_CHIP_RESET, 0xffffffff); + + g_assert_cmphex(qtest_readl(qts, VREG_BASE + VREG_VREG), ==, 0x000000f3); + g_assert_cmphex(qtest_readl(qts, VREG_BASE + VREG_BOD), ==, 0x000000f1); + g_assert_cmphex(qtest_readl(qts, VREG_BASE + VREG_CHIP_RESET), ==, + CHIP_RESET_RESCUE); + + qtest_quit(qts); +} + +static void test_vreg_atomic_aliases(void) +{ + QTestState *qts = rp2040_start(); + + qtest_writel(qts, VREG_BASE + VREG_BOD, 0); + qtest_writel(qts, VREG_BASE + ATOMIC_SET_ALIAS + VREG_BOD, 0x00000011); + qtest_writel(qts, VREG_BASE + ATOMIC_CLR_ALIAS + VREG_BOD, 0x00000010); + + g_assert_cmphex(qtest_readl(qts, VREG_BASE + VREG_BOD), ==, 0x00000001); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/rp2040-vreg/reset-values", test_vreg_reset_values); + qtest_add_func("/rp2040-vreg/rw-masks", test_vreg_rw_masks); + qtest_add_func("/rp2040-vreg/atomic-aliases", test_vreg_atomic_aliases); + + return g_test_run(); +} diff --git a/tests/qtest/rp2040-watchdog-test.c b/tests/qtest/rp2040-watchdog-test.c new file mode 100644 index 0000000000..14b6f12f1a --- /dev/null +++ b/tests/qtest/rp2040-watchdog-test.c @@ -0,0 +1,148 @@ +/* + * QTest testcase for the RP2040 watchdog. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bitops.h" +#include "qemu/timer.h" +#include "qobject/qdict.h" + +#define WATCHDOG_BASE 0x40058000 +#define WATCHDOG_CTRL 0x00 +#define WATCHDOG_LOAD 0x04 +#define WATCHDOG_REASON 0x08 +#define WATCHDOG_SCRATCH0 0x0c +#define WATCHDOG_TICK 0x2c + +#define CTRL_TRIGGER BIT(31) +#define CTRL_ENABLE BIT(30) +#define CTRL_PAUSE_DBG1 BIT(26) +#define CTRL_PAUSE_DBG0 BIT(25) +#define CTRL_PAUSE_JTAG BIT(24) +#define CTRL_TIME_MASK 0x00ffffff + +#define REASON_FORCE BIT(1) +#define REASON_TIMER BIT(0) + +#define TICK_RUNNING BIT(10) +#define TICK_ENABLE BIT(9) + +#define NS_PER_US 1000 + +static QTestState *rp2040_watchdog_start(void) +{ + return qtest_init("-machine raspi-pico -watchdog-action none"); +} + +static void check_watchdog_event(QTestState *qts) +{ + QDict *event = qtest_qmp_eventwait_ref(qts, "WATCHDOG"); + QDict *data = qdict_get_qdict(event, "data"); + + g_assert_cmpstr(qdict_get_str(data, "action"), ==, "none"); + qobject_unref(event); +} + +static void test_reset_values(void) +{ + QTestState *qts = rp2040_watchdog_start(); + + g_assert_cmphex(qtest_readl(qts, WATCHDOG_BASE + WATCHDOG_CTRL), ==, + CTRL_PAUSE_DBG1 | CTRL_PAUSE_DBG0 | CTRL_PAUSE_JTAG); + g_assert_cmphex(qtest_readl(qts, WATCHDOG_BASE + WATCHDOG_LOAD), ==, 0); + g_assert_cmphex(qtest_readl(qts, WATCHDOG_BASE + WATCHDOG_REASON), ==, 0); + g_assert_cmphex(qtest_readl(qts, WATCHDOG_BASE + WATCHDOG_TICK), ==, + TICK_ENABLE); + + qtest_quit(qts); +} + +static void test_scratch_registers(void) +{ + QTestState *qts = rp2040_watchdog_start(); + + qtest_writel(qts, WATCHDOG_BASE + WATCHDOG_SCRATCH0, 0x12345678); + g_assert_cmphex(qtest_readl(qts, WATCHDOG_BASE + WATCHDOG_SCRATCH0), ==, + 0x12345678); + + g_assert_cmphex(qtest_readb(qts, WATCHDOG_BASE + WATCHDOG_SCRATCH0), ==, + 0x78); + g_assert_cmphex(qtest_readb(qts, WATCHDOG_BASE + WATCHDOG_SCRATCH0 + 1), + ==, 0x56); + g_assert_cmphex(qtest_readb(qts, WATCHDOG_BASE + WATCHDOG_SCRATCH0 + 2), + ==, 0x34); + g_assert_cmphex(qtest_readb(qts, WATCHDOG_BASE + WATCHDOG_SCRATCH0 + 3), + ==, 0x12); + + qtest_writeb(qts, WATCHDOG_BASE + WATCHDOG_SCRATCH0, 0xa5); + g_assert_cmphex(qtest_readl(qts, WATCHDOG_BASE + WATCHDOG_SCRATCH0), ==, + 0xa5a5a5a5); + + qtest_writeb(qts, WATCHDOG_BASE + WATCHDOG_SCRATCH0 + 1, 0x3c); + g_assert_cmphex(qtest_readl(qts, WATCHDOG_BASE + WATCHDOG_SCRATCH0), ==, + 0x3c3c3c3c); + + qtest_writew(qts, WATCHDOG_BASE + WATCHDOG_SCRATCH0, 0xf00d); + g_assert_cmphex(qtest_readl(qts, WATCHDOG_BASE + WATCHDOG_SCRATCH0), ==, + 0xf00df00d); + + qtest_quit(qts); +} + +static void test_timer_expiry(void) +{ + QTestState *qts = rp2040_watchdog_start(); + uint32_t ctrl; + + /* + * The default clk_ref is 6 MHz. CYCLES=6 generates a 1 MHz watchdog + * tick; RP2040-E1 makes the counter decrement twice per tick. + */ + qtest_writel(qts, WATCHDOG_BASE + WATCHDOG_TICK, TICK_ENABLE | 6); + g_assert_cmphex(qtest_readl(qts, WATCHDOG_BASE + WATCHDOG_TICK) & + (TICK_RUNNING | TICK_ENABLE), ==, + TICK_RUNNING | TICK_ENABLE); + + qtest_writel(qts, WATCHDOG_BASE + WATCHDOG_LOAD, 20); + qtest_writel(qts, WATCHDOG_BASE + WATCHDOG_CTRL, CTRL_ENABLE); + + qtest_clock_step(qts, 5 * NS_PER_US); + ctrl = qtest_readl(qts, WATCHDOG_BASE + WATCHDOG_CTRL); + g_assert_cmpuint(ctrl & CTRL_TIME_MASK, <, 20); + g_assert_cmpuint(ctrl & CTRL_TIME_MASK, >, 0); + + qtest_clock_step(qts, 10 * NS_PER_US); + check_watchdog_event(qts); + g_assert_cmphex(qtest_readl(qts, WATCHDOG_BASE + WATCHDOG_REASON), ==, + REASON_TIMER); + + qtest_quit(qts); +} + +static void test_force_trigger(void) +{ + QTestState *qts = rp2040_watchdog_start(); + + qtest_writel(qts, WATCHDOG_BASE + WATCHDOG_CTRL, CTRL_TRIGGER); + check_watchdog_event(qts); + g_assert_cmphex(qtest_readl(qts, WATCHDOG_BASE + WATCHDOG_REASON), ==, + REASON_FORCE); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/rp2040-watchdog/reset-values", test_reset_values); + qtest_add_func("/rp2040-watchdog/scratch-registers", + test_scratch_registers); + qtest_add_func("/rp2040-watchdog/timer-expiry", test_timer_expiry); + qtest_add_func("/rp2040-watchdog/force-trigger", test_force_trigger); + + return g_test_run(); +} diff --git a/tests/qtest/rp2040-xip-test.c b/tests/qtest/rp2040-xip-test.c new file mode 100644 index 0000000000..9de4d645b8 --- /dev/null +++ b/tests/qtest/rp2040-xip-test.c @@ -0,0 +1,212 @@ +/* + * QTest testcase for the RP2040 XIP/SSI block. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bitops.h" + +#define XIP_BASE 0x10000000 +#define XIP_NOCACHE_NOALLOC_BASE 0x13000000 +#define XIP_CTRL_BASE 0x14000000 +#define XIP_AUX_BASE 0x50400000 +#define XIP_STAT 0x08 +#define XIP_STREAM_ADDR 0x14 +#define XIP_STREAM_CTR 0x18 +#define XIP_STREAM_FIFO 0x1c +#define XIP_SSI_BASE 0x18000000 +#define SSI_CTRLR1 0x04 +#define SSI_SSIENR 0x08 +#define SSI_DR0 0x60 +#define SSI_SER 0x10 +#define SSI_DMACR 0x4c + +#define XIP_STAT_FIFO_FULL BIT(2) +#define XIP_STAT_FIFO_EMPTY BIT(1) +#define XIP_STAT_FLUSH_READY BIT(0) + +#define DMA_BASE 0x50000000 +#define DMA_CH_READ_ADDR 0x00 +#define DMA_CH_WRITE_ADDR 0x04 +#define DMA_CH_TRANS_COUNT 0x08 +#define DMA_CH_CTRL_TRIG 0x0c +#define DMA_CTRL_BUSY BIT(24) +#define DMA_CTRL_BSWAP BIT(22) +#define DMA_CTRL_TREQ_SEL_SHIFT 15 +#define DMA_CTRL_INCR_WRITE BIT(5) +#define DMA_CTRL_DATA_SIZE_32 (2 << 2) +#define DMA_CTRL_EN BIT(0) +#define DREQ_XIP_STREAM 37 +#define DREQ_XIP_SSIRX 39 +#define SRAM_BASE 0x20000000 + +static QTestState *rp2040_start(const char *machine_args) +{ + if (machine_args) { + return qtest_initf("-machine raspi-pico,%s", machine_args); + } + return qtest_init("-machine raspi-pico"); +} + +static void read_flash_uid(QTestState *qts, uint8_t *uid) +{ + int i; + + qtest_writel(qts, XIP_SSI_BASE + SSI_DR0, 0x4b); + qtest_readl(qts, XIP_SSI_BASE + SSI_DR0); + for (i = 0; i < 4; i++) { + qtest_writel(qts, XIP_SSI_BASE + SSI_DR0, 0); + qtest_readl(qts, XIP_SSI_BASE + SSI_DR0); + } + for (i = 0; i < 8; i++) { + qtest_writel(qts, XIP_SSI_BASE + SSI_DR0, 0); + uid[i] = qtest_readl(qts, XIP_SSI_BASE + SSI_DR0); + } +} + +static void program_flash_bytes(QTestState *qts, uint32_t off, + const uint8_t *buf, size_t len) +{ + size_t i; + + qtest_writel(qts, XIP_SSI_BASE + SSI_DR0, 0x06); + + qtest_writel(qts, XIP_SSI_BASE + SSI_SER, 1); + qtest_writel(qts, XIP_SSI_BASE + SSI_DR0, 0x02); + qtest_writel(qts, XIP_SSI_BASE + SSI_DR0, extract32(off, 16, 8)); + qtest_writel(qts, XIP_SSI_BASE + SSI_DR0, extract32(off, 8, 8)); + qtest_writel(qts, XIP_SSI_BASE + SSI_DR0, extract32(off, 0, 8)); + for (i = 0; i < len; i++) { + qtest_writel(qts, XIP_SSI_BASE + SSI_DR0, buf[i]); + } + qtest_writel(qts, XIP_SSI_BASE + SSI_SER, 0); + + qtest_writel(qts, XIP_SSI_BASE + SSI_DR0, 0x05); + qtest_readl(qts, XIP_SSI_BASE + SSI_DR0); +} + +static void test_flash_uid_default(void) +{ + static const uint8_t expected[] = { + 0x3e, 0xb8, 0xa7, 0x49, 0x3f, 0xcc, 0x06, 0x08, + }; + QTestState *qts = rp2040_start(NULL); + uint8_t uid[8]; + + read_flash_uid(qts, uid); + g_assert_cmpmem(uid, sizeof(uid), expected, sizeof(expected)); + + qtest_quit(qts); +} + +static void test_flash_uid_machine_option(void) +{ + static const uint8_t expected[] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + }; + QTestState *qts = rp2040_start("flash-uid=0011223344556677"); + uint8_t uid[8]; + + read_flash_uid(qts, uid); + g_assert_cmpmem(uid, sizeof(uid), expected, sizeof(expected)); + + qtest_quit(qts); +} + +static void test_stream_fifo_and_dma(void) +{ + static const uint32_t expected[] = { + 0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c, + 0x13121110, 0x17161514, + }; + QTestState *qts = rp2040_start(NULL); + uint32_t ctrl; + uint32_t buf[ARRAY_SIZE(expected)]; + + program_flash_bytes(qts, 0, (const uint8_t *)expected, sizeof(expected)); + + qtest_writel(qts, XIP_CTRL_BASE + XIP_STREAM_ADDR, XIP_BASE); + qtest_writel(qts, XIP_CTRL_BASE + XIP_STREAM_CTR, ARRAY_SIZE(expected)); + + g_assert_cmphex(qtest_readl(qts, XIP_CTRL_BASE + XIP_STAT) & + (XIP_STAT_FLUSH_READY | XIP_STAT_FIFO_FULL), ==, + XIP_STAT_FLUSH_READY | XIP_STAT_FIFO_FULL); + g_assert_cmphex(qtest_readl(qts, XIP_CTRL_BASE + XIP_STREAM_FIFO), ==, + expected[0]); + g_assert_cmphex(qtest_readl(qts, XIP_CTRL_BASE + XIP_STREAM_FIFO), ==, + expected[1]); + + ctrl = DMA_CTRL_EN | DMA_CTRL_INCR_WRITE | DMA_CTRL_DATA_SIZE_32 | + (DREQ_XIP_STREAM << DMA_CTRL_TREQ_SEL_SHIFT); + qtest_writel(qts, DMA_BASE + DMA_CH_READ_ADDR, XIP_AUX_BASE); + qtest_writel(qts, DMA_BASE + DMA_CH_WRITE_ADDR, SRAM_BASE); + qtest_writel(qts, DMA_BASE + DMA_CH_TRANS_COUNT, ARRAY_SIZE(expected) - 2); + qtest_writel(qts, DMA_BASE + DMA_CH_CTRL_TRIG, ctrl); + + qtest_memread(qts, SRAM_BASE, buf, + sizeof(buf[0]) * (ARRAY_SIZE(expected) - 2)); + g_assert_cmpmem(buf, sizeof(buf[0]) * (ARRAY_SIZE(expected) - 2), + expected + 2, + sizeof(expected[0]) * (ARRAY_SIZE(expected) - 2)); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_CTRL_TRIG) & + DMA_CTRL_BUSY, ==, 0); + g_assert_cmphex(qtest_readl(qts, XIP_CTRL_BASE + XIP_STREAM_CTR), ==, 0); + g_assert_cmphex(qtest_readl(qts, XIP_CTRL_BASE + XIP_STAT) & + XIP_STAT_FIFO_EMPTY, ==, XIP_STAT_FIFO_EMPTY); + + qtest_quit(qts); +} + +static void test_ssi_rx_dma_bulk_read(void) +{ + static const uint32_t expected[] = { + 0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c, + 0x13121110, 0x17161514, + }; + QTestState *qts = rp2040_start(NULL); + uint32_t ctrl; + uint32_t buf[ARRAY_SIZE(expected)]; + + program_flash_bytes(qts, 0, (const uint8_t *)expected, sizeof(expected)); + g_assert_cmphex(qtest_readl(qts, XIP_NOCACHE_NOALLOC_BASE), ==, + expected[0]); + + qtest_writel(qts, XIP_SSI_BASE + SSI_SSIENR, 0); + qtest_writel(qts, XIP_SSI_BASE + SSI_CTRLR1, ARRAY_SIZE(expected) - 1); + qtest_writel(qts, XIP_SSI_BASE + SSI_DMACR, 3); + qtest_writel(qts, XIP_SSI_BASE + SSI_SSIENR, 1); + + ctrl = DMA_CTRL_EN | DMA_CTRL_INCR_WRITE | DMA_CTRL_DATA_SIZE_32 | + DMA_CTRL_BSWAP | (DREQ_XIP_SSIRX << DMA_CTRL_TREQ_SEL_SHIFT); + qtest_writel(qts, DMA_BASE + DMA_CH_READ_ADDR, XIP_SSI_BASE + SSI_DR0); + qtest_writel(qts, DMA_BASE + DMA_CH_WRITE_ADDR, SRAM_BASE); + qtest_writel(qts, DMA_BASE + DMA_CH_TRANS_COUNT, ARRAY_SIZE(expected)); + qtest_writel(qts, DMA_BASE + DMA_CH_CTRL_TRIG, ctrl); + + qtest_writel(qts, XIP_SSI_BASE + SSI_DR0, 0xa0); + + qtest_memread(qts, SRAM_BASE, buf, sizeof(buf)); + g_assert_cmpmem(buf, sizeof(buf), expected, sizeof(expected)); + g_assert_cmphex(qtest_readl(qts, DMA_BASE + DMA_CH_CTRL_TRIG) & + DMA_CTRL_BUSY, ==, 0); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/rp2040-xip/flash-uid-default", + test_flash_uid_default); + qtest_add_func("/rp2040-xip/flash-uid-machine-option", + test_flash_uid_machine_option); + qtest_add_func("/rp2040-xip/stream-fifo-and-dma", + test_stream_fifo_and_dma); + qtest_add_func("/rp2040-xip/ssi-rx-dma-bulk-read", + test_ssi_rx_dma_bulk_read); + + return g_test_run(); +} -- 2.53.0
