Signed-off-by: gilles grimaud <[email protected]>
---
hw/arm/rp2040.c | 96 ++++++++++++++++++++++++++++++++++-
include/hw/arm/rp2040.h | 7 +++
tests/qtest/meson.build | 1 +
tests/qtest/rp2040-usb-test.c | 80 +++++++++++++++++++++++++++++
4 files changed, 182 insertions(+), 2 deletions(-)
create mode 100644 tests/qtest/rp2040-usb-test.c
diff --git a/hw/arm/rp2040.c b/hw/arm/rp2040.c
index 37a9a6d4df..1b632be0b6 100644
--- a/hw/arm/rp2040.c
+++ b/hw/arm/rp2040.c
@@ -14,8 +14,10 @@
#include "hw/core/qdev-clock.h"
#include "hw/core/qdev-properties.h"
#include "hw/core/irq.h"
+#include "hw/misc/rp2040.h"
#include "hw/misc/unimp.h"
#include "qemu/datadir.h"
+#include "qemu/log.h"
#include "system/address-spaces.h"
#include "target/arm/cpu.h"
#include "target/arm/cpu-qom.h"
@@ -29,6 +31,17 @@
#define RP2040_IO_IRQ_BANK0 13
#define RP2040_SIO_IRQ_PROC0 15
#define RP2040_SIO_IRQ_PROC1 16
+
+#define USBCTRL_SIE_STATUS 0x50
+#define USBCTRL_BUFF_CPU_HANDLE 0x5c
+#define USBCTRL_EP_ABORT_DONE 0x64
+#define USBCTRL_INTR 0x8c
+#define USBCTRL_INTE 0x90
+#define USBCTRL_INTF 0x94
+#define USBCTRL_INTS 0x98
+
+#define USBCTRL_SIE_STATUS_VBUS_DETECTED BIT(11)
+
#define RP2040_PROC1 1
/*
@@ -149,12 +162,77 @@ static const struct {
{ "rp2040.adc", 0x4004c000, 0x4000 },
{ "rp2040.pwm", 0x40050000, 0x4000 },
{ "rp2040.rtc", 0x4005c000, 0x4000 },
- { "rp2040.usbctrl_dpram", 0x50100000, 0x10000 },
- { "rp2040.usbctrl_regs", 0x50110000, 0x10000 },
{ "rp2040.pio0", 0x50200000, 0x10000 },
{ "rp2040.pio1", 0x50300000, 0x10000 },
};
+static uint64_t rp2040_usbctrl_regs_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ RP2040State *s = opaque;
+ hwaddr offset = addr & 0xfff;
+ uint64_t value;
+
+ switch (offset) {
+ case USBCTRL_SIE_STATUS:
+ value = s->usbctrl_reg[offset / sizeof(uint32_t)] |
+ USBCTRL_SIE_STATUS_VBUS_DETECTED;
+ break;
+ case USBCTRL_BUFF_CPU_HANDLE:
+ case USBCTRL_EP_ABORT_DONE:
+ case USBCTRL_INTR:
+ value = s->usbctrl_reg[offset / sizeof(uint32_t)];
+ break;
+ case USBCTRL_INTS:
+ value = (s->usbctrl_reg[USBCTRL_INTR / sizeof(uint32_t)] |
+ s->usbctrl_reg[USBCTRL_INTF / sizeof(uint32_t)]) &
+ s->usbctrl_reg[USBCTRL_INTE / sizeof(uint32_t)];
+ break;
+ default:
+ if (offset < sizeof(s->usbctrl_reg)) {
+ value = s->usbctrl_reg[offset / sizeof(uint32_t)];
+ } else {
+ value = 0;
+ qemu_log_mask(LOG_UNIMP,
+ "%s: unimplemented read at offset 0x%"
+ HWADDR_PRIx "\n", __func__, addr & 0xfff);
+ }
+ break;
+ }
+
+ return value;
+}
+
+static void rp2040_usbctrl_regs_write(void *opaque, hwaddr addr,
+ uint64_t value64, unsigned size)
+{
+ RP2040State *s = opaque;
+ hwaddr alias = addr & RP2040_ATOMIC_ALIAS_MASK;
+ hwaddr offset = addr & 0xfff;
+ uint32_t value = value64;
+ uint32_t old;
+
+ if (offset < sizeof(s->usbctrl_reg)) {
+ old = s->usbctrl_reg[offset / sizeof(uint32_t)];
+ s->usbctrl_reg[offset / sizeof(uint32_t)] =
+ rp2040_atomic_update(old, value, alias);
+ } else {
+ qemu_log_mask(LOG_UNIMP,
+ "%s: unimplemented write at offset 0x%"
+ HWADDR_PRIx "\n", __func__, addr & 0xfff);
+ }
+}
+
+static const MemoryRegionOps rp2040_usbctrl_regs_ops = {
+ .read = rp2040_usbctrl_regs_read,
+ .write = rp2040_usbctrl_regs_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
static void rp2040_update_nmi(RP2040State *s)
{
unsigned core;
@@ -405,6 +483,20 @@ static bool rp2040_init_memory(RP2040State *s, Error
**errp)
}
memory_region_add_subregion(s->board_memory, RP2040_SRAM5_BASE,
&s->sram[5]);
+ if (!memory_region_init_ram(&s->usbctrl_dpram, OBJECT(s),
+ "rp2040.usbctrl_dpram",
+ RP2040_USBCTRL_DPRAM_SIZE, errp)) {
+ return false;
+ }
+ memory_region_add_subregion(s->board_memory, RP2040_USBCTRL_DPRAM_BASE,
+ &s->usbctrl_dpram);
+
+ memory_region_init_io(&s->usbctrl_regs, OBJECT(s),
+ &rp2040_usbctrl_regs_ops, s,
+ "rp2040.usbctrl_regs",
+ RP2040_USBCTRL_REGS_SIZE);
+ memory_region_add_subregion(s->board_memory, RP2040_USBCTRL_REGS_BASE,
+ &s->usbctrl_regs);
return true;
}
diff --git a/include/hw/arm/rp2040.h b/include/hw/arm/rp2040.h
index 974f3220b5..0daa7d6ba1 100644
--- a/include/hw/arm/rp2040.h
+++ b/include/hw/arm/rp2040.h
@@ -48,6 +48,10 @@ OBJECT_DECLARE_SIMPLE_TYPE(RP2040State, RP2040)
#define RP2040_SRAM4_BASE 0x20040000
#define RP2040_SRAM5_BASE 0x20041000
#define RP2040_SRAM_HI_SIZE (4 * KiB)
+#define RP2040_USBCTRL_DPRAM_BASE 0x50100000
+#define RP2040_USBCTRL_DPRAM_SIZE (4 * KiB)
+#define RP2040_USBCTRL_REGS_BASE 0x50110000
+#define RP2040_USBCTRL_REGS_SIZE 0x4000
#define RP2040_NUM_CORES 2
#define RP2040_NUM_IRQS 32
@@ -84,6 +88,9 @@ struct RP2040State {
MemoryRegion cpu_board_alias[RP2040_NUM_CORES];
MemoryRegion rom;
MemoryRegion sram[6];
+ MemoryRegion usbctrl_dpram;
+ MemoryRegion usbctrl_regs;
+ uint32_t usbctrl_reg[0x100 / sizeof(uint32_t)];
char *bootrom_file;
qemu_irq *irq;
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 50ef6f0c7d..4736b7da32 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -277,6 +277,7 @@ qtests_arm = \
'rp2040-sio-test',
'rp2040-tbman-test',
'rp2040-timer-test',
+ 'rp2040-usb-test',
'rp2040-uart-test',
'rp2040-vreg-test',
'rp2040-watchdog-test',
diff --git a/tests/qtest/rp2040-usb-test.c b/tests/qtest/rp2040-usb-test.c
new file mode 100644
index 0000000000..e56b6f9066
--- /dev/null
+++ b/tests/qtest/rp2040-usb-test.c
@@ -0,0 +1,80 @@
+/*
+ * QTest testcase for the shallow RP2040 USB controller model.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+
+#define USBCTRL_DPRAM_BASE 0x50100000
+#define USBCTRL_REGS_BASE 0x50110000
+#define USBCTRL_ALIAS_XOR 0x1000
+#define USBCTRL_ALIAS_SET 0x2000
+#define USBCTRL_ALIAS_CLR 0x3000
+
+#define USBCTRL_SIE_STATUS 0x50
+#define USBCTRL_USB_MUXING 0x74
+#define USBCTRL_INTR 0x8c
+#define USBCTRL_INTE 0x90
+#define USBCTRL_INTF 0x94
+#define USBCTRL_INTS 0x98
+
+#define USBCTRL_VBUS_DETECTED (1U << 11)
+
+static QTestState *rp2040_start(void)
+{
+ return qtest_init("-machine raspi-pico");
+}
+
+static void test_usb_dpram(void)
+{
+ QTestState *qts = rp2040_start();
+
+ qtest_writel(qts, USBCTRL_DPRAM_BASE + 0x20, 0x12345678);
+ g_assert_cmphex(qtest_readl(qts, USBCTRL_DPRAM_BASE + 0x20),
+ ==, 0x12345678);
+
+ qtest_quit(qts);
+}
+
+static void test_usb_registers(void)
+{
+ QTestState *qts = rp2040_start();
+
+ g_assert_cmphex(qtest_readl(qts, USBCTRL_REGS_BASE +
+ USBCTRL_SIE_STATUS) &
+ USBCTRL_VBUS_DETECTED, ==, USBCTRL_VBUS_DETECTED);
+
+ qtest_writel(qts, USBCTRL_REGS_BASE + USBCTRL_USB_MUXING, 0x5);
+ qtest_writel(qts, USBCTRL_REGS_BASE + USBCTRL_ALIAS_SET +
+ USBCTRL_USB_MUXING, 0x8);
+ g_assert_cmphex(qtest_readl(qts, USBCTRL_REGS_BASE +
+ USBCTRL_USB_MUXING), ==, 0xd);
+ qtest_writel(qts, USBCTRL_REGS_BASE + USBCTRL_ALIAS_XOR +
+ USBCTRL_USB_MUXING, 0x9);
+ g_assert_cmphex(qtest_readl(qts, USBCTRL_REGS_BASE +
+ USBCTRL_USB_MUXING), ==, 0x4);
+ qtest_writel(qts, USBCTRL_REGS_BASE + USBCTRL_ALIAS_CLR +
+ USBCTRL_USB_MUXING, 0x4);
+ g_assert_cmphex(qtest_readl(qts, USBCTRL_REGS_BASE +
+ USBCTRL_USB_MUXING), ==, 0);
+
+ qtest_writel(qts, USBCTRL_REGS_BASE + USBCTRL_INTR, 0x3);
+ qtest_writel(qts, USBCTRL_REGS_BASE + USBCTRL_INTF, 0x4);
+ qtest_writel(qts, USBCTRL_REGS_BASE + USBCTRL_INTE, 0x6);
+ g_assert_cmphex(qtest_readl(qts, USBCTRL_REGS_BASE + USBCTRL_INTS),
+ ==, 0x6);
+
+ qtest_quit(qts);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("/rp2040-usb/dpram", test_usb_dpram);
+ qtest_add_func("/rp2040-usb/registers", test_usb_registers);
+
+ return g_test_run();
+}
--
2.55.0