Aspeed uses non-standard UHCI register addresses. On top of that,
registers are 32 bit wide instead of 16 bit.

Map Aspeed UHCI addresses to standard UHCI addresses and where needed
combine/split 32 bit accesses to solve the problem.

Signed-off-by: Guenter Roeck <li...@roeck-us.net>
---
 hw/usb/hcd-uhci-sysbus.c | 101 +++++++++++++++++++++++++++++++++++++++
 hw/usb/hcd-uhci-sysbus.h |  11 +++++
 2 files changed, 112 insertions(+)

diff --git a/hw/usb/hcd-uhci-sysbus.c b/hw/usb/hcd-uhci-sysbus.c
index 6f2428cc15..ba5398beeb 100644
--- a/hw/usb/hcd-uhci-sysbus.c
+++ b/hw/usb/hcd-uhci-sysbus.c
@@ -20,7 +20,9 @@
 
 #include "qemu/osdep.h"
 #include "hw/irq.h"
+#include "hw/usb/uhci-regs.h"
 #include "qapi/error.h"
+#include "qemu/log.h"
 #include "qemu/module.h"
 #include "qemu/timer.h"
 #include "hw/usb.h"
@@ -88,6 +90,99 @@ static void uhci_sysbus_class_init(ObjectClass *klass, void 
*data)
     dc->reset = uhci_sysbus_reset_sysbus;
 }
 
+static hwaddr aspeed_uhci_chip_to_uhci(hwaddr addr)
+{
+    switch (addr) {
+    case 0x00:
+        return UHCI_USBCMD;
+    case 0x04:
+        return UHCI_USBSTS;
+    case 0x08:
+        return UHCI_USBINTR;
+    case 0x0c:
+        return UHCI_USBFLBASEADD;
+    case 0x80:
+        return UHCI_USBFRNUM;
+    case 0x84:
+        return UHCI_USBSOF;
+    case 0x88:
+        return UHCI_USBPORTSC1;
+    case 0x8c:
+        return UHCI_USBPORTSC2;
+    case 0x90:
+        return UHCI_USBPORTSC3;
+    case 0x94:
+        return UHCI_USBPORTSC4;
+    default:        /* unimplemented */
+        qemu_log_mask(LOG_UNIMP, "Unimplemented Aspeed UHCI register 0x%lx\n",
+                      addr);
+        return 0x20;
+    }
+}
+
+/*
+ * Aspeed UHCI registers are 32 bit wide.
+ * Convert to 16 bit to access standard UHCI code.
+ */
+static uint64_t aspeed_uhci_port_read(void *opaque, hwaddr addr, unsigned size)
+{
+    UHCIState *uhci = opaque;
+    MemoryRegion *mr = &uhci->mem;
+    hwaddr uaddr = aspeed_uhci_chip_to_uhci(addr);
+
+    if (uaddr == UHCI_USBFLBASEADD) {
+        return mr->ops->read(opaque, uaddr, 2) |
+               mr->ops->read(opaque, uaddr + 2, 2) << 16;
+    }
+    return mr->ops->read(opaque, uaddr, 2);
+}
+
+static void aspeed_uhci_port_write(void *opaque, hwaddr addr, uint64_t val,
+                                   unsigned size)
+{
+    UHCIState *uhci = opaque;
+    MemoryRegion *mr = &uhci->mem;
+    hwaddr uaddr = aspeed_uhci_chip_to_uhci(addr);
+
+    if (uaddr == UHCI_USBFLBASEADD) {
+        mr->ops->write(opaque, uaddr, val & 0xffff, 2);
+        mr->ops->write(opaque, uaddr + 2, val >> 16, 2);
+    } else {
+        mr->ops->write(opaque, uaddr, val, 2);
+    }
+}
+
+static const MemoryRegionOps aspeed_uhci_mmio_ops = {
+    .read = aspeed_uhci_port_read,
+    .write = aspeed_uhci_port_write,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void uhci_sysbus_aspeed_realize(DeviceState *dev, Error **errp)
+{
+    UHCISysBusState *s = SYSBUS_UHCI(dev);
+    ASPEEDUHCIState *f = ASPEED_UHCI(dev);
+    UHCIState *uhci = &s->uhci;
+
+    uhci_sysbus_realize(dev, errp);
+
+    memory_region_init_io(&f->mem_aspeed, OBJECT(f), &aspeed_uhci_mmio_ops,
+                          uhci, "aspeed", 0x100);
+    memory_region_add_subregion(&uhci->mem, 0, &f->mem_aspeed);
+}
+
+static void uhci_sysbus_aspeed_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = uhci_sysbus_aspeed_realize;
+    set_bit(DEVICE_CATEGORY_USB, dc->categories);
+    dc->desc = "ASPEED UHCI USB Controller";
+    dc->reset = uhci_sysbus_reset_sysbus;
+}
+
 static const TypeInfo uhci_sysbus_types[] = {
     {
         .name          = TYPE_SYSBUS_UHCI,
@@ -95,6 +190,12 @@ static const TypeInfo uhci_sysbus_types[] = {
         .instance_size = sizeof(UHCISysBusState),
         .class_init    = uhci_sysbus_class_init,
     },
+    {
+        .name          = TYPE_ASPEED_UHCI,
+        .parent        = TYPE_SYSBUS_UHCI,
+        .instance_size = sizeof(ASPEEDUHCIState),
+        .class_init    = uhci_sysbus_aspeed_class_init,
+    },
 };
 
 DEFINE_TYPES(uhci_sysbus_types);
diff --git a/hw/usb/hcd-uhci-sysbus.h b/hw/usb/hcd-uhci-sysbus.h
index c491b9fc92..75c4716c40 100644
--- a/hw/usb/hcd-uhci-sysbus.h
+++ b/hw/usb/hcd-uhci-sysbus.h
@@ -4,6 +4,7 @@
 #include "hcd-uhci.h"
 
 #define TYPE_SYSBUS_UHCI "sysbus-uhci"
+#define TYPE_ASPEED_UHCI "aspeed-uhci"
 
 OBJECT_DECLARE_SIMPLE_TYPE(UHCISysBusState, SYSBUS_UHCI)
 
@@ -20,4 +21,14 @@ struct UHCISysBusState {
     uint32_t num_ports;
 };
 
+OBJECT_DECLARE_SIMPLE_TYPE(ASPEEDUHCIState, ASPEED_UHCI)
+
+struct ASPEEDUHCIState {
+    /*< private >*/
+    UHCISysBusState parent_obj;
+    /*< public >*/
+
+    MemoryRegion mem_aspeed;
+};
+
 #endif /* HW_USB_HCD_UHCI_SYSBUS_H */
-- 
2.45.2


Reply via email to