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
uhci_sysbus_reset(UHCIState * uhci)37 static void uhci_sysbus_reset(UHCIState *uhci)
38 {
39 uhci_state_reset(uhci);
40 }
41
uhci_sysbus_realize(DeviceState * dev,Error ** errp)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
uhci_sysbus_reset_sysbus(DeviceState * dev)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
uhci_sysbus_class_init(ObjectClass * klass,void * data)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
aspeed_uhci_chip_to_uhci(hwaddr addr)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 */
aspeed_uhci_port_read(void * opaque,hwaddr addr,unsigned size)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
aspeed_uhci_port_write(void * opaque,hwaddr addr,uint64_t val,unsigned size)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
uhci_sysbus_aspeed_realize(DeviceState * dev,Error ** errp)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
uhci_sysbus_aspeed_class_init(ObjectClass * klass,void * data)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