1 | /* |
2 | * QEMU USB EHCI Emulation |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2.1 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public License |
15 | * along with this program; if not, see <http://www.gnu.org/licenses/>. |
16 | */ |
17 | |
18 | #include "qemu/osdep.h" |
19 | #include "hw/qdev-properties.h" |
20 | #include "hw/usb/hcd-ehci.h" |
21 | #include "migration/vmstate.h" |
22 | #include "qemu/module.h" |
23 | |
24 | static const VMStateDescription vmstate_ehci_sysbus = { |
25 | .name = "ehci-sysbus" , |
26 | .version_id = 2, |
27 | .minimum_version_id = 1, |
28 | .fields = (VMStateField[]) { |
29 | VMSTATE_STRUCT(ehci, EHCISysBusState, 2, vmstate_ehci, EHCIState), |
30 | VMSTATE_END_OF_LIST() |
31 | } |
32 | }; |
33 | |
34 | static Property ehci_sysbus_properties[] = { |
35 | DEFINE_PROP_UINT32("maxframes" , EHCISysBusState, ehci.maxframes, 128), |
36 | DEFINE_PROP_END_OF_LIST(), |
37 | }; |
38 | |
39 | static void usb_ehci_sysbus_realize(DeviceState *dev, Error **errp) |
40 | { |
41 | SysBusDevice *d = SYS_BUS_DEVICE(dev); |
42 | EHCISysBusState *i = SYS_BUS_EHCI(dev); |
43 | EHCIState *s = &i->ehci; |
44 | |
45 | usb_ehci_realize(s, dev, errp); |
46 | sysbus_init_irq(d, &s->irq); |
47 | } |
48 | |
49 | static void usb_ehci_sysbus_reset(DeviceState *dev) |
50 | { |
51 | SysBusDevice *d = SYS_BUS_DEVICE(dev); |
52 | EHCISysBusState *i = SYS_BUS_EHCI(d); |
53 | EHCIState *s = &i->ehci; |
54 | |
55 | ehci_reset(s); |
56 | } |
57 | |
58 | static void ehci_sysbus_init(Object *obj) |
59 | { |
60 | SysBusDevice *d = SYS_BUS_DEVICE(obj); |
61 | EHCISysBusState *i = SYS_BUS_EHCI(obj); |
62 | SysBusEHCIClass *sec = SYS_BUS_EHCI_GET_CLASS(obj); |
63 | EHCIState *s = &i->ehci; |
64 | |
65 | s->capsbase = sec->capsbase; |
66 | s->opregbase = sec->opregbase; |
67 | s->portscbase = sec->portscbase; |
68 | s->portnr = sec->portnr; |
69 | s->as = &address_space_memory; |
70 | |
71 | usb_ehci_init(s, DEVICE(obj)); |
72 | sysbus_init_mmio(d, &s->mem); |
73 | } |
74 | |
75 | static void ehci_sysbus_class_init(ObjectClass *klass, void *data) |
76 | { |
77 | DeviceClass *dc = DEVICE_CLASS(klass); |
78 | SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(klass); |
79 | |
80 | sec->portscbase = 0x44; |
81 | sec->portnr = NB_PORTS; |
82 | |
83 | dc->realize = usb_ehci_sysbus_realize; |
84 | dc->vmsd = &vmstate_ehci_sysbus; |
85 | dc->props = ehci_sysbus_properties; |
86 | dc->reset = usb_ehci_sysbus_reset; |
87 | set_bit(DEVICE_CATEGORY_USB, dc->categories); |
88 | } |
89 | |
90 | static const TypeInfo ehci_type_info = { |
91 | .name = TYPE_SYS_BUS_EHCI, |
92 | .parent = TYPE_SYS_BUS_DEVICE, |
93 | .instance_size = sizeof(EHCISysBusState), |
94 | .instance_init = ehci_sysbus_init, |
95 | .abstract = true, |
96 | .class_init = ehci_sysbus_class_init, |
97 | .class_size = sizeof(SysBusEHCIClass), |
98 | }; |
99 | |
100 | static void ehci_platform_class_init(ObjectClass *oc, void *data) |
101 | { |
102 | SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); |
103 | DeviceClass *dc = DEVICE_CLASS(oc); |
104 | |
105 | sec->capsbase = 0x0; |
106 | sec->opregbase = 0x20; |
107 | set_bit(DEVICE_CATEGORY_USB, dc->categories); |
108 | } |
109 | |
110 | static const TypeInfo ehci_platform_type_info = { |
111 | .name = TYPE_PLATFORM_EHCI, |
112 | .parent = TYPE_SYS_BUS_EHCI, |
113 | .class_init = ehci_platform_class_init, |
114 | }; |
115 | |
116 | static void ehci_xlnx_class_init(ObjectClass *oc, void *data) |
117 | { |
118 | SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); |
119 | DeviceClass *dc = DEVICE_CLASS(oc); |
120 | |
121 | set_bit(DEVICE_CATEGORY_USB, dc->categories); |
122 | sec->capsbase = 0x100; |
123 | sec->opregbase = 0x140; |
124 | } |
125 | |
126 | static const TypeInfo ehci_xlnx_type_info = { |
127 | .name = "xlnx,ps7-usb" , |
128 | .parent = TYPE_SYS_BUS_EHCI, |
129 | .class_init = ehci_xlnx_class_init, |
130 | }; |
131 | |
132 | static void ehci_exynos4210_class_init(ObjectClass *oc, void *data) |
133 | { |
134 | SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); |
135 | DeviceClass *dc = DEVICE_CLASS(oc); |
136 | |
137 | sec->capsbase = 0x0; |
138 | sec->opregbase = 0x10; |
139 | set_bit(DEVICE_CATEGORY_USB, dc->categories); |
140 | } |
141 | |
142 | static const TypeInfo ehci_exynos4210_type_info = { |
143 | .name = TYPE_EXYNOS4210_EHCI, |
144 | .parent = TYPE_SYS_BUS_EHCI, |
145 | .class_init = ehci_exynos4210_class_init, |
146 | }; |
147 | |
148 | static void ehci_tegra2_class_init(ObjectClass *oc, void *data) |
149 | { |
150 | SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); |
151 | DeviceClass *dc = DEVICE_CLASS(oc); |
152 | |
153 | sec->capsbase = 0x100; |
154 | sec->opregbase = 0x140; |
155 | set_bit(DEVICE_CATEGORY_USB, dc->categories); |
156 | } |
157 | |
158 | static const TypeInfo ehci_tegra2_type_info = { |
159 | .name = TYPE_TEGRA2_EHCI, |
160 | .parent = TYPE_SYS_BUS_EHCI, |
161 | .class_init = ehci_tegra2_class_init, |
162 | }; |
163 | |
164 | static void ehci_ppc4xx_init(Object *o) |
165 | { |
166 | EHCISysBusState *s = SYS_BUS_EHCI(o); |
167 | |
168 | s->ehci.companion_enable = true; |
169 | } |
170 | |
171 | static void ehci_ppc4xx_class_init(ObjectClass *oc, void *data) |
172 | { |
173 | SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); |
174 | DeviceClass *dc = DEVICE_CLASS(oc); |
175 | |
176 | sec->capsbase = 0x0; |
177 | sec->opregbase = 0x10; |
178 | set_bit(DEVICE_CATEGORY_USB, dc->categories); |
179 | } |
180 | |
181 | static const TypeInfo ehci_ppc4xx_type_info = { |
182 | .name = TYPE_PPC4xx_EHCI, |
183 | .parent = TYPE_SYS_BUS_EHCI, |
184 | .class_init = ehci_ppc4xx_class_init, |
185 | .instance_init = ehci_ppc4xx_init, |
186 | }; |
187 | |
188 | /* |
189 | * Faraday FUSBH200 USB 2.0 EHCI |
190 | */ |
191 | |
192 | /** |
193 | * FUSBH200EHCIRegs: |
194 | * @FUSBH200_REG_EOF_ASTR: EOF/Async. Sleep Timer Register |
195 | * @FUSBH200_REG_BMCSR: Bus Monitor Control/Status Register |
196 | */ |
197 | enum FUSBH200EHCIRegs { |
198 | FUSBH200_REG_EOF_ASTR = 0x34, |
199 | FUSBH200_REG_BMCSR = 0x40, |
200 | }; |
201 | |
202 | static uint64_t fusbh200_ehci_read(void *opaque, hwaddr addr, unsigned size) |
203 | { |
204 | EHCIState *s = opaque; |
205 | hwaddr off = s->opregbase + s->portscbase + 4 * s->portnr + addr; |
206 | |
207 | switch (off) { |
208 | case FUSBH200_REG_EOF_ASTR: |
209 | return 0x00000041; |
210 | case FUSBH200_REG_BMCSR: |
211 | /* High-Speed, VBUS valid, interrupt level-high active */ |
212 | return (2 << 9) | (1 << 8) | (1 << 3); |
213 | } |
214 | |
215 | return 0; |
216 | } |
217 | |
218 | static void fusbh200_ehci_write(void *opaque, hwaddr addr, uint64_t val, |
219 | unsigned size) |
220 | { |
221 | } |
222 | |
223 | static const MemoryRegionOps fusbh200_ehci_mmio_ops = { |
224 | .read = fusbh200_ehci_read, |
225 | .write = fusbh200_ehci_write, |
226 | .valid.min_access_size = 4, |
227 | .valid.max_access_size = 4, |
228 | .endianness = DEVICE_LITTLE_ENDIAN, |
229 | }; |
230 | |
231 | static void fusbh200_ehci_init(Object *obj) |
232 | { |
233 | EHCISysBusState *i = SYS_BUS_EHCI(obj); |
234 | FUSBH200EHCIState *f = FUSBH200_EHCI(obj); |
235 | EHCIState *s = &i->ehci; |
236 | |
237 | memory_region_init_io(&f->mem_vendor, OBJECT(f), &fusbh200_ehci_mmio_ops, s, |
238 | "fusbh200" , 0x4c); |
239 | memory_region_add_subregion(&s->mem, |
240 | s->opregbase + s->portscbase + 4 * s->portnr, |
241 | &f->mem_vendor); |
242 | } |
243 | |
244 | static void fusbh200_ehci_class_init(ObjectClass *oc, void *data) |
245 | { |
246 | SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); |
247 | DeviceClass *dc = DEVICE_CLASS(oc); |
248 | |
249 | sec->capsbase = 0x0; |
250 | sec->opregbase = 0x10; |
251 | sec->portscbase = 0x20; |
252 | sec->portnr = 1; |
253 | set_bit(DEVICE_CATEGORY_USB, dc->categories); |
254 | } |
255 | |
256 | static const TypeInfo ehci_fusbh200_type_info = { |
257 | .name = TYPE_FUSBH200_EHCI, |
258 | .parent = TYPE_SYS_BUS_EHCI, |
259 | .instance_size = sizeof(FUSBH200EHCIState), |
260 | .instance_init = fusbh200_ehci_init, |
261 | .class_init = fusbh200_ehci_class_init, |
262 | }; |
263 | |
264 | static void ehci_sysbus_register_types(void) |
265 | { |
266 | type_register_static(&ehci_type_info); |
267 | type_register_static(&ehci_platform_type_info); |
268 | type_register_static(&ehci_xlnx_type_info); |
269 | type_register_static(&ehci_exynos4210_type_info); |
270 | type_register_static(&ehci_tegra2_type_info); |
271 | type_register_static(&ehci_ppc4xx_type_info); |
272 | type_register_static(&ehci_fusbh200_type_info); |
273 | } |
274 | |
275 | type_init(ehci_sysbus_register_types) |
276 | |