Merge tag 'pull-hex-20221003' of https://github.com/quic/qemu into staging
[qemu.git] / hw / usb / chipidea.c
1 /*
2 * Copyright (c) 2018, Impinj, Inc.
3 *
4 * Chipidea USB block emulation code
5 *
6 * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
7 *
8 * This work is licensed under the terms of the GNU GPL, version 2 or later.
9 * See the COPYING file in the top-level directory.
10 */
11
12 #include "qemu/osdep.h"
13 #include "hw/usb/hcd-ehci.h"
14 #include "hw/usb/chipidea.h"
15 #include "qemu/module.h"
16
17 enum {
18 CHIPIDEA_USBx_DCIVERSION = 0x000,
19 CHIPIDEA_USBx_DCCPARAMS = 0x004,
20 CHIPIDEA_USBx_DCCPARAMS_HC = BIT(8),
21 };
22
23 static uint64_t chipidea_read(void *opaque, hwaddr offset,
24 unsigned size)
25 {
26 return 0;
27 }
28
29 static void chipidea_write(void *opaque, hwaddr offset,
30 uint64_t value, unsigned size)
31 {
32 }
33
34 static const struct MemoryRegionOps chipidea_ops = {
35 .read = chipidea_read,
36 .write = chipidea_write,
37 .endianness = DEVICE_NATIVE_ENDIAN,
38 .impl = {
39 /*
40 * Our device would not work correctly if the guest was doing
41 * unaligned access. This might not be a limitation on the
42 * real device but in practice there is no reason for a guest
43 * to access this device unaligned.
44 */
45 .min_access_size = 4,
46 .max_access_size = 4,
47 .unaligned = false,
48 },
49 };
50
51 static uint64_t chipidea_dc_read(void *opaque, hwaddr offset,
52 unsigned size)
53 {
54 switch (offset) {
55 case CHIPIDEA_USBx_DCIVERSION:
56 return 0x1;
57 case CHIPIDEA_USBx_DCCPARAMS:
58 /*
59 * Real hardware (at least i.MX7) will also report the
60 * controller as "Device Capable" (and 8 supported endpoints),
61 * but there doesn't seem to be much point in doing so, since
62 * we don't emulate that part.
63 */
64 return CHIPIDEA_USBx_DCCPARAMS_HC;
65 }
66
67 return 0;
68 }
69
70 static void chipidea_dc_write(void *opaque, hwaddr offset,
71 uint64_t value, unsigned size)
72 {
73 }
74
75 static const struct MemoryRegionOps chipidea_dc_ops = {
76 .read = chipidea_dc_read,
77 .write = chipidea_dc_write,
78 .endianness = DEVICE_NATIVE_ENDIAN,
79 .impl = {
80 /*
81 * Our device would not work correctly if the guest was doing
82 * unaligned access. This might not be a limitation on the real
83 * device but in practice there is no reason for a guest to access
84 * this device unaligned.
85 */
86 .min_access_size = 4,
87 .max_access_size = 4,
88 .unaligned = false,
89 },
90 };
91
92 static void chipidea_init(Object *obj)
93 {
94 EHCIState *ehci = &SYS_BUS_EHCI(obj)->ehci;
95 ChipideaState *ci = CHIPIDEA(obj);
96 int i;
97
98 for (i = 0; i < ARRAY_SIZE(ci->iomem); i++) {
99 const struct {
100 const char *name;
101 hwaddr offset;
102 uint64_t size;
103 const struct MemoryRegionOps *ops;
104 } regions[ARRAY_SIZE(ci->iomem)] = {
105 /*
106 * Registers located between offsets 0x000 and 0xFC
107 */
108 {
109 .name = TYPE_CHIPIDEA ".misc",
110 .offset = 0x000,
111 .size = 0x100,
112 .ops = &chipidea_ops,
113 },
114 /*
115 * Registers located between offsets 0x1A4 and 0x1DC
116 */
117 {
118 .name = TYPE_CHIPIDEA ".endpoints",
119 .offset = 0x1A4,
120 .size = 0x1DC - 0x1A4 + 4,
121 .ops = &chipidea_ops,
122 },
123 /*
124 * USB_x_DCIVERSION and USB_x_DCCPARAMS
125 */
126 {
127 .name = TYPE_CHIPIDEA ".dc",
128 .offset = 0x120,
129 .size = 8,
130 .ops = &chipidea_dc_ops,
131 },
132 };
133
134 memory_region_init_io(&ci->iomem[i],
135 obj,
136 regions[i].ops,
137 ci,
138 regions[i].name,
139 regions[i].size);
140
141 memory_region_add_subregion(&ehci->mem,
142 regions[i].offset,
143 &ci->iomem[i]);
144 }
145 }
146
147 static void chipidea_class_init(ObjectClass *klass, void *data)
148 {
149 DeviceClass *dc = DEVICE_CLASS(klass);
150 SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(klass);
151
152 /*
153 * Offsets used were taken from i.MX7Dual Applications Processor
154 * Reference Manual, Rev 0.1, p. 3177, Table 11-59
155 */
156 sec->capsbase = 0x100;
157 sec->opregbase = 0x140;
158 sec->portnr = 1;
159
160 set_bit(DEVICE_CATEGORY_USB, dc->categories);
161 dc->desc = "Chipidea USB Module";
162 }
163
164 static const TypeInfo chipidea_info = {
165 .name = TYPE_CHIPIDEA,
166 .parent = TYPE_SYS_BUS_EHCI,
167 .instance_size = sizeof(ChipideaState),
168 .instance_init = chipidea_init,
169 .class_init = chipidea_class_init,
170 };
171
172 static void chipidea_register_type(void)
173 {
174 type_register_static(&chipidea_info);
175 }
176 type_init(chipidea_register_type)