xref: /openbmc/qemu/hw/usb/hcd-uhci-sysbus.c (revision 7d87775f)
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