@@ -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"
@@ -84,10 +86,104 @@ static void uhci_sysbus_class_init(ObjectClass *klass, void *data)
dc->realize = uhci_sysbus_realize;
set_bit(DEVICE_CATEGORY_USB, dc->categories);
dc->desc = "UHCI USB Controller";
- device_class_set_props(dc, uhci_sysbus_properties);
device_class_set_legacy_reset(dc, 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";
+ device_class_set_legacy_reset(dc, uhci_sysbus_reset_sysbus);
+ device_class_set_props(dc, uhci_sysbus_properties);
+ dc->user_creatable = false;
+}
+
static const TypeInfo uhci_sysbus_types[] = {
{
.name = TYPE_SYSBUS_UHCI,
@@ -95,6 +191,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);
@@ -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 */
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. In addition to that, Aspeed SoCs starting with AST2600 support and use EHCI companion mode on the second EHCI interface. Support this by moving the property initialization to the Aspeed class initialization code. Since the USB ports are part of the SoC and always present, set user_creatable to false for the Aspeed UHCI controller. Signed-off-by: Guenter Roeck <linux@roeck-us.net> --- Changes since RFC: - Rebased to v9.1.0-1673-g134b443512 - Added support for EHCI companion mode hw/usb/hcd-uhci-sysbus.c | 104 ++++++++++++++++++++++++++++++++++++++- hw/usb/hcd-uhci-sysbus.h | 11 +++++ 2 files changed, 114 insertions(+), 1 deletion(-)