183698261SGuenter Roeck /*
283698261SGuenter Roeck * QEMU USB UHCI Emulation
383698261SGuenter Roeck * Copyright (c) 2006 Openedhand Ltd.
483698261SGuenter Roeck * Copyright (c) 2010 CodeSourcery
583698261SGuenter Roeck * Copyright (c) 2024 Red Hat, Inc.
683698261SGuenter Roeck *
783698261SGuenter Roeck * This library is free software; you can redistribute it and/or
883698261SGuenter Roeck * modify it under the terms of the GNU Lesser General Public
983698261SGuenter Roeck * License as published by the Free Software Foundation; either
1083698261SGuenter Roeck * version 2.1 of the License, or (at your option) any later version.
1183698261SGuenter Roeck *
1283698261SGuenter Roeck * This library is distributed in the hope that it will be useful,
1383698261SGuenter Roeck * but WITHOUT ANY WARRANTY; without even the implied warranty of
1483698261SGuenter Roeck * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1583698261SGuenter Roeck * Lesser General Public License for more details.
1683698261SGuenter Roeck *
1783698261SGuenter Roeck * You should have received a copy of the GNU Lesser General Public
1883698261SGuenter Roeck * License along with this library; if not, see <http://www.gnu.org/licenses/>.
1983698261SGuenter Roeck */
2083698261SGuenter Roeck
2183698261SGuenter Roeck #include "qemu/osdep.h"
2283698261SGuenter Roeck #include "hw/irq.h"
23*a829d749SGuenter Roeck #include "hw/usb/uhci-regs.h"
2483698261SGuenter Roeck #include "qapi/error.h"
25*a829d749SGuenter Roeck #include "qemu/log.h"
2683698261SGuenter Roeck #include "qemu/module.h"
2783698261SGuenter Roeck #include "qemu/timer.h"
2883698261SGuenter Roeck #include "hw/usb.h"
2983698261SGuenter Roeck #include "migration/vmstate.h"
3083698261SGuenter Roeck #include "hw/sysbus.h"
3183698261SGuenter Roeck #include "hw/qdev-dma.h"
3283698261SGuenter Roeck #include "hw/qdev-properties.h"
3383698261SGuenter Roeck #include "trace.h"
3483698261SGuenter Roeck #include "hcd-uhci.h"
3583698261SGuenter Roeck #include "hcd-uhci-sysbus.h"
3683698261SGuenter Roeck
uhci_sysbus_reset(UHCIState * uhci)3783698261SGuenter Roeck static void uhci_sysbus_reset(UHCIState *uhci)
3883698261SGuenter Roeck {
3983698261SGuenter Roeck uhci_state_reset(uhci);
4083698261SGuenter Roeck }
4183698261SGuenter Roeck
uhci_sysbus_realize(DeviceState * dev,Error ** errp)4283698261SGuenter Roeck static void uhci_sysbus_realize(DeviceState *dev, Error **errp)
4383698261SGuenter Roeck {
4483698261SGuenter Roeck UHCISysBusState *s = SYSBUS_UHCI(dev);
4583698261SGuenter Roeck UHCIState *uhci = &s->uhci;
4683698261SGuenter Roeck SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
4783698261SGuenter Roeck Error *err = NULL;
4883698261SGuenter Roeck
4983698261SGuenter Roeck uhci->masterbus = s->masterbus;
5083698261SGuenter Roeck uhci->firstport = s->firstport;
5183698261SGuenter Roeck uhci->maxframes = s->maxframes;
5283698261SGuenter Roeck uhci->frame_bandwidth = s->frame_bandwidth;
5383698261SGuenter Roeck uhci->as = &address_space_memory;
5483698261SGuenter Roeck uhci->uhci_reset = uhci_sysbus_reset;
5583698261SGuenter Roeck
5683698261SGuenter Roeck usb_uhci_init(uhci, dev, &err);
5783698261SGuenter Roeck
5883698261SGuenter Roeck if (err) {
5983698261SGuenter Roeck error_propagate(errp, err);
6083698261SGuenter Roeck return;
6183698261SGuenter Roeck }
6283698261SGuenter Roeck sysbus_init_irq(sbd, &uhci->irq);
6383698261SGuenter Roeck sysbus_init_mmio(sbd, &uhci->mem);
6483698261SGuenter Roeck }
6583698261SGuenter Roeck
uhci_sysbus_reset_sysbus(DeviceState * dev)6683698261SGuenter Roeck static void uhci_sysbus_reset_sysbus(DeviceState *dev)
6783698261SGuenter Roeck {
6883698261SGuenter Roeck UHCISysBusState *s = SYSBUS_UHCI(dev);
6983698261SGuenter Roeck UHCIState *uhci = &s->uhci;
7083698261SGuenter Roeck
7183698261SGuenter Roeck uhci_sysbus_reset(uhci);
7283698261SGuenter Roeck }
7383698261SGuenter Roeck
7483698261SGuenter Roeck static Property uhci_sysbus_properties[] = {
7583698261SGuenter Roeck DEFINE_PROP_STRING("masterbus", UHCISysBusState, masterbus),
7683698261SGuenter Roeck DEFINE_PROP_UINT32("firstport", UHCISysBusState, firstport, 0),
7783698261SGuenter Roeck DEFINE_PROP_UINT32("bandwidth", UHCISysBusState, frame_bandwidth, 1280),
7883698261SGuenter Roeck DEFINE_PROP_UINT32("maxframes", UHCISysBusState, maxframes, 128),
7983698261SGuenter Roeck DEFINE_PROP_END_OF_LIST(),
8083698261SGuenter Roeck };
8183698261SGuenter Roeck
uhci_sysbus_class_init(ObjectClass * klass,void * data)8283698261SGuenter Roeck static void uhci_sysbus_class_init(ObjectClass *klass, void *data)
8383698261SGuenter Roeck {
8483698261SGuenter Roeck DeviceClass *dc = DEVICE_CLASS(klass);
8583698261SGuenter Roeck
8683698261SGuenter Roeck dc->realize = uhci_sysbus_realize;
8783698261SGuenter Roeck set_bit(DEVICE_CATEGORY_USB, dc->categories);
8883698261SGuenter Roeck dc->desc = "UHCI USB Controller";
8983698261SGuenter Roeck device_class_set_legacy_reset(dc, uhci_sysbus_reset_sysbus);
9083698261SGuenter Roeck }
9183698261SGuenter Roeck
aspeed_uhci_chip_to_uhci(hwaddr addr)92*a829d749SGuenter Roeck static hwaddr aspeed_uhci_chip_to_uhci(hwaddr addr)
93*a829d749SGuenter Roeck {
94*a829d749SGuenter Roeck switch (addr) {
95*a829d749SGuenter Roeck case 0x00:
96*a829d749SGuenter Roeck return UHCI_USBCMD;
97*a829d749SGuenter Roeck case 0x04:
98*a829d749SGuenter Roeck return UHCI_USBSTS;
99*a829d749SGuenter Roeck case 0x08:
100*a829d749SGuenter Roeck return UHCI_USBINTR;
101*a829d749SGuenter Roeck case 0x0c:
102*a829d749SGuenter Roeck return UHCI_USBFLBASEADD;
103*a829d749SGuenter Roeck case 0x80:
104*a829d749SGuenter Roeck return UHCI_USBFRNUM;
105*a829d749SGuenter Roeck case 0x84:
106*a829d749SGuenter Roeck return UHCI_USBSOF;
107*a829d749SGuenter Roeck case 0x88:
108*a829d749SGuenter Roeck return UHCI_USBPORTSC1;
109*a829d749SGuenter Roeck case 0x8c:
110*a829d749SGuenter Roeck return UHCI_USBPORTSC2;
111*a829d749SGuenter Roeck case 0x90:
112*a829d749SGuenter Roeck return UHCI_USBPORTSC3;
113*a829d749SGuenter Roeck case 0x94:
114*a829d749SGuenter Roeck return UHCI_USBPORTSC4;
115*a829d749SGuenter Roeck default: /* unimplemented */
116*a829d749SGuenter Roeck qemu_log_mask(LOG_UNIMP, "Unimplemented Aspeed UHCI register 0x%"
117*a829d749SGuenter Roeck HWADDR_PRIx "\n", addr);
118*a829d749SGuenter Roeck return 0x20;
119*a829d749SGuenter Roeck }
120*a829d749SGuenter Roeck }
121*a829d749SGuenter Roeck
122*a829d749SGuenter Roeck /*
123*a829d749SGuenter Roeck * Aspeed UHCI registers are 32 bit wide.
124*a829d749SGuenter Roeck * Convert to 16 bit to access standard UHCI code.
125*a829d749SGuenter Roeck */
aspeed_uhci_port_read(void * opaque,hwaddr addr,unsigned size)126*a829d749SGuenter Roeck static uint64_t aspeed_uhci_port_read(void *opaque, hwaddr addr, unsigned size)
127*a829d749SGuenter Roeck {
128*a829d749SGuenter Roeck UHCIState *uhci = opaque;
129*a829d749SGuenter Roeck MemoryRegion *mr = &uhci->mem;
130*a829d749SGuenter Roeck hwaddr uaddr = aspeed_uhci_chip_to_uhci(addr);
131*a829d749SGuenter Roeck
132*a829d749SGuenter Roeck if (uaddr == UHCI_USBFLBASEADD) {
133*a829d749SGuenter Roeck return mr->ops->read(opaque, uaddr, 2) |
134*a829d749SGuenter Roeck mr->ops->read(opaque, uaddr + 2, 2) << 16;
135*a829d749SGuenter Roeck }
136*a829d749SGuenter Roeck return mr->ops->read(opaque, uaddr, 2);
137*a829d749SGuenter Roeck }
138*a829d749SGuenter Roeck
aspeed_uhci_port_write(void * opaque,hwaddr addr,uint64_t val,unsigned size)139*a829d749SGuenter Roeck static void aspeed_uhci_port_write(void *opaque, hwaddr addr, uint64_t val,
140*a829d749SGuenter Roeck unsigned size)
141*a829d749SGuenter Roeck {
142*a829d749SGuenter Roeck UHCIState *uhci = opaque;
143*a829d749SGuenter Roeck MemoryRegion *mr = &uhci->mem;
144*a829d749SGuenter Roeck hwaddr uaddr = aspeed_uhci_chip_to_uhci(addr);
145*a829d749SGuenter Roeck
146*a829d749SGuenter Roeck if (uaddr == UHCI_USBFLBASEADD) {
147*a829d749SGuenter Roeck mr->ops->write(opaque, uaddr, val & 0xffff, 2);
148*a829d749SGuenter Roeck mr->ops->write(opaque, uaddr + 2, val >> 16, 2);
149*a829d749SGuenter Roeck } else {
150*a829d749SGuenter Roeck mr->ops->write(opaque, uaddr, val, 2);
151*a829d749SGuenter Roeck }
152*a829d749SGuenter Roeck }
153*a829d749SGuenter Roeck
154*a829d749SGuenter Roeck static const MemoryRegionOps aspeed_uhci_mmio_ops = {
155*a829d749SGuenter Roeck .read = aspeed_uhci_port_read,
156*a829d749SGuenter Roeck .write = aspeed_uhci_port_write,
157*a829d749SGuenter Roeck .valid.min_access_size = 4,
158*a829d749SGuenter Roeck .valid.max_access_size = 4,
159*a829d749SGuenter Roeck .endianness = DEVICE_LITTLE_ENDIAN,
160*a829d749SGuenter Roeck };
161*a829d749SGuenter Roeck
uhci_sysbus_aspeed_realize(DeviceState * dev,Error ** errp)162*a829d749SGuenter Roeck static void uhci_sysbus_aspeed_realize(DeviceState *dev, Error **errp)
163*a829d749SGuenter Roeck {
164*a829d749SGuenter Roeck UHCISysBusState *s = SYSBUS_UHCI(dev);
165*a829d749SGuenter Roeck ASPEEDUHCIState *f = ASPEED_UHCI(dev);
166*a829d749SGuenter Roeck UHCIState *uhci = &s->uhci;
167*a829d749SGuenter Roeck
168*a829d749SGuenter Roeck uhci_sysbus_realize(dev, errp);
169*a829d749SGuenter Roeck
170*a829d749SGuenter Roeck memory_region_init_io(&f->mem_aspeed, OBJECT(f), &aspeed_uhci_mmio_ops,
171*a829d749SGuenter Roeck uhci, "aspeed", 0x100);
172*a829d749SGuenter Roeck memory_region_add_subregion(&uhci->mem, 0, &f->mem_aspeed);
173*a829d749SGuenter Roeck }
174*a829d749SGuenter Roeck
uhci_sysbus_aspeed_class_init(ObjectClass * klass,void * data)175*a829d749SGuenter Roeck static void uhci_sysbus_aspeed_class_init(ObjectClass *klass, void *data)
176*a829d749SGuenter Roeck {
177*a829d749SGuenter Roeck DeviceClass *dc = DEVICE_CLASS(klass);
178*a829d749SGuenter Roeck
179*a829d749SGuenter Roeck dc->realize = uhci_sysbus_aspeed_realize;
180*a829d749SGuenter Roeck set_bit(DEVICE_CATEGORY_USB, dc->categories);
181*a829d749SGuenter Roeck dc->desc = "ASPEED UHCI USB Controller";
182*a829d749SGuenter Roeck device_class_set_legacy_reset(dc, uhci_sysbus_reset_sysbus);
183*a829d749SGuenter Roeck device_class_set_props(dc, uhci_sysbus_properties);
184*a829d749SGuenter Roeck dc->user_creatable = false;
185*a829d749SGuenter Roeck }
186*a829d749SGuenter Roeck
18783698261SGuenter Roeck static const TypeInfo uhci_sysbus_types[] = {
18883698261SGuenter Roeck {
18983698261SGuenter Roeck .name = TYPE_SYSBUS_UHCI,
19083698261SGuenter Roeck .parent = TYPE_SYS_BUS_DEVICE,
19183698261SGuenter Roeck .instance_size = sizeof(UHCISysBusState),
19283698261SGuenter Roeck .class_init = uhci_sysbus_class_init,
19383698261SGuenter Roeck },
194*a829d749SGuenter Roeck {
195*a829d749SGuenter Roeck .name = TYPE_ASPEED_UHCI,
196*a829d749SGuenter Roeck .parent = TYPE_SYSBUS_UHCI,
197*a829d749SGuenter Roeck .instance_size = sizeof(ASPEEDUHCIState),
198*a829d749SGuenter Roeck .class_init = uhci_sysbus_aspeed_class_init,
199*a829d749SGuenter Roeck },
20083698261SGuenter Roeck };
20183698261SGuenter Roeck
20283698261SGuenter Roeck DEFINE_TYPES(uhci_sysbus_types);
203