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