1 /* 2 * QEMU USB UHCI Emulation 3 * Copyright (c) 2006 Openedhand Ltd. 4 * Copyright (c) 2010 CodeSourcery 5 * Copyright (c) 2024 Red Hat, Inc. 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 #include "qemu/osdep.h" 22 #include "hw/irq.h" 23 #include "hw/usb/uhci-regs.h" 24 #include "qapi/error.h" 25 #include "qemu/log.h" 26 #include "qemu/module.h" 27 #include "qemu/timer.h" 28 #include "hw/usb.h" 29 #include "migration/vmstate.h" 30 #include "hw/sysbus.h" 31 #include "hw/qdev-dma.h" 32 #include "hw/qdev-properties.h" 33 #include "trace.h" 34 #include "hcd-uhci.h" 35 #include "hcd-uhci-sysbus.h" 36 37 static void uhci_sysbus_reset(UHCIState *uhci) 38 { 39 uhci_state_reset(uhci); 40 } 41 42 static void uhci_sysbus_realize(DeviceState *dev, Error **errp) 43 { 44 UHCISysBusState *s = SYSBUS_UHCI(dev); 45 UHCIState *uhci = &s->uhci; 46 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 47 Error *err = NULL; 48 49 uhci->masterbus = s->masterbus; 50 uhci->firstport = s->firstport; 51 uhci->maxframes = s->maxframes; 52 uhci->frame_bandwidth = s->frame_bandwidth; 53 uhci->as = &address_space_memory; 54 uhci->uhci_reset = uhci_sysbus_reset; 55 56 usb_uhci_init(uhci, dev, &err); 57 58 if (err) { 59 error_propagate(errp, err); 60 return; 61 } 62 sysbus_init_irq(sbd, &uhci->irq); 63 sysbus_init_mmio(sbd, &uhci->mem); 64 } 65 66 static void uhci_sysbus_reset_sysbus(DeviceState *dev) 67 { 68 UHCISysBusState *s = SYSBUS_UHCI(dev); 69 UHCIState *uhci = &s->uhci; 70 71 uhci_sysbus_reset(uhci); 72 } 73 74 static Property uhci_sysbus_properties[] = { 75 DEFINE_PROP_STRING("masterbus", UHCISysBusState, masterbus), 76 DEFINE_PROP_UINT32("firstport", UHCISysBusState, firstport, 0), 77 DEFINE_PROP_UINT32("bandwidth", UHCISysBusState, frame_bandwidth, 1280), 78 DEFINE_PROP_UINT32("maxframes", UHCISysBusState, maxframes, 128), 79 DEFINE_PROP_END_OF_LIST(), 80 }; 81 82 static void uhci_sysbus_class_init(ObjectClass *klass, void *data) 83 { 84 DeviceClass *dc = DEVICE_CLASS(klass); 85 86 dc->realize = uhci_sysbus_realize; 87 set_bit(DEVICE_CATEGORY_USB, dc->categories); 88 dc->desc = "UHCI USB Controller"; 89 device_class_set_legacy_reset(dc, uhci_sysbus_reset_sysbus); 90 } 91 92 static hwaddr aspeed_uhci_chip_to_uhci(hwaddr addr) 93 { 94 switch (addr) { 95 case 0x00: 96 return UHCI_USBCMD; 97 case 0x04: 98 return UHCI_USBSTS; 99 case 0x08: 100 return UHCI_USBINTR; 101 case 0x0c: 102 return UHCI_USBFLBASEADD; 103 case 0x80: 104 return UHCI_USBFRNUM; 105 case 0x84: 106 return UHCI_USBSOF; 107 case 0x88: 108 return UHCI_USBPORTSC1; 109 case 0x8c: 110 return UHCI_USBPORTSC2; 111 case 0x90: 112 return UHCI_USBPORTSC3; 113 case 0x94: 114 return UHCI_USBPORTSC4; 115 default: /* unimplemented */ 116 qemu_log_mask(LOG_UNIMP, "Unimplemented Aspeed UHCI register 0x%" 117 HWADDR_PRIx "\n", addr); 118 return 0x20; 119 } 120 } 121 122 /* 123 * Aspeed UHCI registers are 32 bit wide. 124 * Convert to 16 bit to access standard UHCI code. 125 */ 126 static uint64_t aspeed_uhci_port_read(void *opaque, hwaddr addr, unsigned size) 127 { 128 UHCIState *uhci = opaque; 129 MemoryRegion *mr = &uhci->mem; 130 hwaddr uaddr = aspeed_uhci_chip_to_uhci(addr); 131 132 if (uaddr == UHCI_USBFLBASEADD) { 133 return mr->ops->read(opaque, uaddr, 2) | 134 mr->ops->read(opaque, uaddr + 2, 2) << 16; 135 } 136 return mr->ops->read(opaque, uaddr, 2); 137 } 138 139 static void aspeed_uhci_port_write(void *opaque, hwaddr addr, uint64_t val, 140 unsigned size) 141 { 142 UHCIState *uhci = opaque; 143 MemoryRegion *mr = &uhci->mem; 144 hwaddr uaddr = aspeed_uhci_chip_to_uhci(addr); 145 146 if (uaddr == UHCI_USBFLBASEADD) { 147 mr->ops->write(opaque, uaddr, val & 0xffff, 2); 148 mr->ops->write(opaque, uaddr + 2, val >> 16, 2); 149 } else { 150 mr->ops->write(opaque, uaddr, val, 2); 151 } 152 } 153 154 static const MemoryRegionOps aspeed_uhci_mmio_ops = { 155 .read = aspeed_uhci_port_read, 156 .write = aspeed_uhci_port_write, 157 .valid.min_access_size = 4, 158 .valid.max_access_size = 4, 159 .endianness = DEVICE_LITTLE_ENDIAN, 160 }; 161 162 static void uhci_sysbus_aspeed_realize(DeviceState *dev, Error **errp) 163 { 164 UHCISysBusState *s = SYSBUS_UHCI(dev); 165 ASPEEDUHCIState *f = ASPEED_UHCI(dev); 166 UHCIState *uhci = &s->uhci; 167 168 uhci_sysbus_realize(dev, errp); 169 170 memory_region_init_io(&f->mem_aspeed, OBJECT(f), &aspeed_uhci_mmio_ops, 171 uhci, "aspeed", 0x100); 172 memory_region_add_subregion(&uhci->mem, 0, &f->mem_aspeed); 173 } 174 175 static void uhci_sysbus_aspeed_class_init(ObjectClass *klass, void *data) 176 { 177 DeviceClass *dc = DEVICE_CLASS(klass); 178 179 dc->realize = uhci_sysbus_aspeed_realize; 180 set_bit(DEVICE_CATEGORY_USB, dc->categories); 181 dc->desc = "ASPEED UHCI USB Controller"; 182 device_class_set_legacy_reset(dc, uhci_sysbus_reset_sysbus); 183 device_class_set_props(dc, uhci_sysbus_properties); 184 dc->user_creatable = false; 185 } 186 187 static const TypeInfo uhci_sysbus_types[] = { 188 { 189 .name = TYPE_SYSBUS_UHCI, 190 .parent = TYPE_SYS_BUS_DEVICE, 191 .instance_size = sizeof(UHCISysBusState), 192 .class_init = uhci_sysbus_class_init, 193 }, 194 { 195 .name = TYPE_ASPEED_UHCI, 196 .parent = TYPE_SYSBUS_UHCI, 197 .instance_size = sizeof(ASPEEDUHCIState), 198 .class_init = uhci_sysbus_aspeed_class_init, 199 }, 200 }; 201 202 DEFINE_TYPES(uhci_sysbus_types); 203