Hi Jamin,
On 3/7/26 09:43, Jamin Lin wrote:
The AST2600 has a USB 2.0 Device Controller (UDC) at 0x1e6a2000 with one
control endpoint and four programmable endpoints, driven by the Linux
"aspeed_udc" gadget driver.
Add the controller as a sysbus (system) device: the MMIO register map
described with the registerfields macros, the interrupt line and the
soft reset. This is only the register/system side.
Note: this "device controller" is the system-bus device (TYPE_ASPEED_UDC).
It is not the gadget USB device (TYPE_ASPEED_UDC_DEV) that a host
controller enumerates, which is added in the next patch.
Signed-off-by: Jamin Lin <[email protected]>
---
hw/arm/Kconfig | 1 +
hw/usb/Kconfig | 4 +
hw/usb/aspeed-udc.c | 264 ++++++++++++++++++++++++++++++++++++
hw/usb/meson.build | 1 +
hw/usb/trace-events | 7 +
include/hw/usb/aspeed-udc.h | 54 ++++++++
6 files changed, 331 insertions(+)
create mode 100644 hw/usb/aspeed-udc.c
create mode 100644 include/hw/usb/aspeed-udc.h
+static void aspeed_udc_realize(DeviceState *dev, Error **errp)
+{
+ AspeedUDCState *s = ASPEED_UDC(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ int i;
+
+ s->regs = g_new0(uint32_t, ASPEED_UDC_NR_REGS);
+
+ memory_region_init(&s->iomem, OBJECT(s), TYPE_ASPEED_UDC,
+ ASPEED_UDC_REG_SIZE);
+
+ /* Root/global registers occupy the low part of the window */
+ memory_region_init_io(&s->reg_mr, OBJECT(s), &aspeed_udc_ops, s,
+ TYPE_ASPEED_UDC ".regs", ASPEED_UDC_NR_REGS << 2);
+ memory_region_add_subregion(&s->iomem, 0, &s->reg_mr);
+
+ /* Each programmable endpoint has its own register bank */
+ for (i = 0; i < ASPEED_UDC_NUM_EP; i++) {
+ g_autofree char *name = g_strdup_printf(TYPE_ASPEED_UDC ".ep%d", i);
+
+ s->ep[i].index = i;
+ s->ep[i].regs = g_new0(uint32_t, ASPEED_UDC_EP_NR_REGS);
Why allocate a fixed size? Otherwise LGTM.
+ memory_region_init_io(&s->ep[i].mr, OBJECT(s), &aspeed_udc_ep_ops,
+ &s->ep[i], name, ASPEED_UDC_EP_NR_REGS << 2);
+ memory_region_add_subregion(&s->iomem, ASPEED_UDC_EP_REG_BASE +
+ i * ASPEED_UDC_EP_REG_SIZE, &s->ep[i].mr);
+ }
+
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq);
+}
+static void aspeed_udc_unrealize(DeviceState *dev)
+{
+ AspeedUDCState *s = ASPEED_UDC(dev);
+ int i;
+
+ for (i = 0; i < ASPEED_UDC_NUM_EP; i++) {
+ g_free(s->ep[i].regs);
+ }
+ g_free(s->regs);
+}
+/*
+ * EP0 (control) is served through the root registers (UDC_EP0_*), so only
+ * the 4 programmable endpoints get their own register bank / ep[] entry.
+ */
+#define ASPEED_UDC_NUM_EP 4
+/* 32-bit registers per programmable endpoint */
+#define ASPEED_UDC_EP_NR_REGS 4
+
+/*
+ * The root/global register block spans 0x000...0x087: the SETUP data buffer
+ * ends at 0x84. Size the backing array to cover the whole block.
+ */
+#define ASPEED_UDC_NR_REGS (0x88 >> 2)
+
+/* MMIO window: root registers below EP_REG_BASE, then the per-EP banks */
+#define ASPEED_UDC_REG_SIZE 0x300
+#define ASPEED_UDC_EP_REG_BASE 0x200
+#define ASPEED_UDC_EP_REG_SIZE 0x10
+
+typedef struct AspeedUDCEP {
+ MemoryRegion mr;
+ int index;
+ uint32_t *regs;
+} AspeedUDCEP;
+
+struct AspeedUDCState {
+ SysBusDevice parent_obj;
+
+ /* container: root registers + per-endpoint banks */
+ MemoryRegion iomem;
+ MemoryRegion reg_mr;
+ qemu_irq irq;
+ uint32_t *regs;
+ AspeedUDCEP ep[ASPEED_UDC_NUM_EP];
+};
+
+#endif /* HW_USB_ASPEED_UDC_H */