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


Reply via email to