blob: 98db94fd85646df0f54636b3b4c73a5c6ea7b376 [file] [log] [blame]
Riku Voipiod19c3642011-01-17 16:50:37 +00001/*
2 * TI OMAP3 High-Speed USB Host and OTG Controller emulation.
3 *
4 * Copyright (C) 2009 Nokia Corporation
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 or
9 * (at your option) any later version of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20#include "qemu-common.h"
21#include "qemu-timer.h"
22#include "usb.h"
23#include "omap.h"
24#include "irq.h"
25#include "devices.h"
26#include "hw.h"
Riku Voipio9a9123b2011-01-17 16:50:40 +000027#include "sysbus.h"
Riku Voipiod19c3642011-01-17 16:50:37 +000028
29//#define OMAP3_HSUSB_DEBUG
30
31#ifdef OMAP3_HSUSB_DEBUG
32#define TRACE(fmt,...) fprintf(stderr, "%s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__)
33#else
34#define TRACE(...)
35#endif
36
Riku Voipiod19c3642011-01-17 16:50:37 +000037/* usb-musb.c */
38extern CPUReadMemoryFunc *musb_read[];
39extern CPUWriteMemoryFunc *musb_write[];
40
Riku Voipio9a9123b2011-01-17 16:50:40 +000041typedef struct omap3_hsusb_otg_s {
42 SysBusDevice busdev;
Riku Voipiod19c3642011-01-17 16:50:37 +000043 qemu_irq mc_irq;
44 qemu_irq dma_irq;
Riku Voipio9a9123b2011-01-17 16:50:40 +000045 qemu_irq stdby_irq;
Riku Voipiod19c3642011-01-17 16:50:37 +000046 MUSBState *musb;
Riku Voipiod19c3642011-01-17 16:50:37 +000047
48 uint8_t rev;
49 uint16_t sysconfig;
50 uint8_t interfsel;
51 uint8_t simenable;
52 uint8_t forcestdby;
Riku Voipio9a9123b2011-01-17 16:50:40 +000053} OMAP3HSUSBOTGState;
54
55static const VMStateDescription vmstate_omap3_hsusb_otg = {
56 .name = "omap3_hsusb_otg",
57 .version_id = 1,
58 .minimum_version_id = 1,
59 .minimum_version_id_old = 1,
60 .fields = (VMStateField[]) {
61 VMSTATE_UINT16(sysconfig, OMAP3HSUSBOTGState),
62 VMSTATE_UINT8(interfsel, OMAP3HSUSBOTGState),
63 VMSTATE_UINT8(simenable, OMAP3HSUSBOTGState),
64 VMSTATE_UINT8(forcestdby, OMAP3HSUSBOTGState),
65 VMSTATE_END_OF_LIST()
66 }
Riku Voipiod19c3642011-01-17 16:50:37 +000067};
68
Riku Voipio9a9123b2011-01-17 16:50:40 +000069static void omap3_hsusb_otg_stdby_update(OMAP3HSUSBOTGState *s)
Riku Voipiod19c3642011-01-17 16:50:37 +000070{
Riku Voipio9a9123b2011-01-17 16:50:40 +000071 if (s->stdby_irq) {
72 qemu_set_irq(s->stdby_irq, s->forcestdby);
Riku Voipiod19c3642011-01-17 16:50:37 +000073 }
74}
75
Riku Voipio9a9123b2011-01-17 16:50:40 +000076static void omap3_hsusb_otg_reset(DeviceState *dev)
Riku Voipiod19c3642011-01-17 16:50:37 +000077{
Riku Voipio9a9123b2011-01-17 16:50:40 +000078 OMAP3HSUSBOTGState *s = FROM_SYSBUS(OMAP3HSUSBOTGState,
79 sysbus_from_qdev(dev));
Riku Voipiod19c3642011-01-17 16:50:37 +000080 s->rev = 0x33;
81 s->sysconfig = 0;
82 s->interfsel = 0x1;
83 s->simenable = 0;
84 s->forcestdby = 1;
85 musb_reset(s->musb);
86 omap3_hsusb_otg_stdby_update(s);
87}
88
89static uint32_t omap3_hsusb_otg_read(int access,
90 void *opaque,
91 target_phys_addr_t addr)
92{
Riku Voipio9a9123b2011-01-17 16:50:40 +000093 OMAP3HSUSBOTGState *s = opaque;
Riku Voipiod19c3642011-01-17 16:50:37 +000094
95 if (addr < 0x200)
96 return musb_read[access](s->musb, addr);
97 if (addr < 0x400)
98 return musb_read[access](s->musb, 0x20 + ((addr >> 3) & 0x3c));
99 switch (addr) {
100 case 0x400: /* OTG_REVISION */
101 TRACE("OTG_REVISION: 0x%08x", s->rev);
102 return s->rev;
103 case 0x404: /* OTG_SYSCONFIG */
104 TRACE("OTG_SYSCONFIG: 0x%08x", s->sysconfig);
105 return s->sysconfig;
106 case 0x408: /* OTG_SYSSTATUS */
107 TRACE("OTG_SYSSTATUS: 0x00000001");
108 return 1; /* reset finished */
109 case 0x40c: /* OTG_INTERFSEL */
110 TRACE("OTG_INTERFSEL: 0x%08x", s->interfsel);
111 return s->interfsel;
112 case 0x410: /* OTG_SIMENABLE */
113 TRACE("OTG_SIMENABLE: 0x%08x", s->simenable);
114 return s->simenable;
115 case 0x414: /* OTG_FORCESTDBY */
116 TRACE("OTG_FORCESTDBY: 0x%08x", s->forcestdby);
117 return s->forcestdby;
118 default:
119 break;
120 }
121 OMAP_BAD_REG(addr);
122 return 0;
123}
124
125static void omap3_hsusb_otg_write(int access,
126 void *opaque,
127 target_phys_addr_t addr,
128 uint32_t value)
129{
Riku Voipio9a9123b2011-01-17 16:50:40 +0000130 OMAP3HSUSBOTGState *s = opaque;
Riku Voipiod19c3642011-01-17 16:50:37 +0000131
132 if (addr < 0x200)
133 musb_write[access](s->musb, addr, value);
134 else if (addr < 0x400)
135 musb_write[access](s->musb, 0x20 + ((addr >> 3) & 0x3c), value);
136 else switch (addr) {
137 case 0x400: /* OTG_REVISION */
138 case 0x408: /* OTG_SYSSTATUS */
139 OMAP_RO_REGV(addr, value);
140 break;
141 case 0x404: /* OTG_SYSCONFIG */
142 TRACE("OTG_SYSCONFIG = 0x%08x", value);
143 if (value & 2) /* SOFTRESET */
Riku Voipio9a9123b2011-01-17 16:50:40 +0000144 omap3_hsusb_otg_reset(&s->busdev.qdev);
Riku Voipiod19c3642011-01-17 16:50:37 +0000145 s->sysconfig = value & 0x301f;
146 break;
147 case 0x40c: /* OTG_INTERFSEL */
148 TRACE("OTG_INTERFSEL = 0x%08x", value);
149 s->interfsel = value & 0x3;
150 break;
151 case 0x410: /* OTG_SIMENABLE */
152 TRACE("OTG_SIMENABLE = 0x%08x", value);
153 cpu_abort(cpu_single_env,
154 "%s: USB simulation mode not supported\n",
155 __FUNCTION__);
156 break;
157 case 0x414: /* OTG_FORCESTDBY */
158 TRACE("OTG_FORCESTDBY = 0x%08x", value);
159 s->forcestdby = value & 1;
160 omap3_hsusb_otg_stdby_update(s);
161 break;
162 default:
163 OMAP_BAD_REGV(addr, value);
164 break;
165 }
166}
167
168static uint32_t omap3_hsusb_otg_readb(void *opaque, target_phys_addr_t addr)
169{
170 return omap3_hsusb_otg_read(0, opaque, addr);
171}
172
173static uint32_t omap3_hsusb_otg_readh(void *opaque, target_phys_addr_t addr)
174{
175 return omap3_hsusb_otg_read(1, opaque, addr);
176}
177
178static uint32_t omap3_hsusb_otg_readw(void *opaque, target_phys_addr_t addr)
179{
180 return omap3_hsusb_otg_read(2, opaque, addr);
181}
182
183static void omap3_hsusb_otg_writeb(void *opaque, target_phys_addr_t addr,
184 uint32_t value)
185{
186 omap3_hsusb_otg_write(0, opaque, addr, value);
187}
188
189static void omap3_hsusb_otg_writeh(void *opaque, target_phys_addr_t addr,
190 uint32_t value)
191{
192 omap3_hsusb_otg_write(1, opaque, addr, value);
193}
194
195static void omap3_hsusb_otg_writew(void *opaque, target_phys_addr_t addr,
196 uint32_t value)
197{
198 omap3_hsusb_otg_write(2, opaque, addr, value);
199}
200
201static CPUReadMemoryFunc *omap3_hsusb_otg_readfn[] = {
202 omap3_hsusb_otg_readb,
203 omap3_hsusb_otg_readh,
204 omap3_hsusb_otg_readw,
205};
206
207static CPUWriteMemoryFunc *omap3_hsusb_otg_writefn[] = {
208 omap3_hsusb_otg_writeb,
209 omap3_hsusb_otg_writeh,
210 omap3_hsusb_otg_writew,
211};
212
213static void omap3_hsusb_musb_core_intr(void *opaque, int source, int level)
214{
Riku Voipio9a9123b2011-01-17 16:50:40 +0000215 OMAP3HSUSBOTGState *s = opaque;
Riku Voipiod19c3642011-01-17 16:50:37 +0000216 /*TRACE("intr 0x%08x, 0x%08x, 0x%08x", source, level, musb_core_intr_get(s->musb));*/
217 qemu_set_irq(s->mc_irq, level);
218}
219
Riku Voipio9a9123b2011-01-17 16:50:40 +0000220static int omap3_hsusb_otg_init(SysBusDevice *dev)
Riku Voipiod19c3642011-01-17 16:50:37 +0000221{
Riku Voipio9a9123b2011-01-17 16:50:40 +0000222 OMAP3HSUSBOTGState *s = FROM_SYSBUS(OMAP3HSUSBOTGState, dev);
223 sysbus_init_irq(dev, &s->mc_irq);
224 sysbus_init_irq(dev, &s->dma_irq);
225 sysbus_init_irq(dev, &s->stdby_irq);
226 sysbus_init_mmio(dev, 0x1000,
227 cpu_register_io_memory(omap3_hsusb_otg_readfn,
228 omap3_hsusb_otg_writefn, s,
229 DEVICE_NATIVE_ENDIAN));
Peter Maydella79ae552011-08-30 14:58:50 +0000230 qdev_init_gpio_in(&dev->qdev, omap3_hsusb_musb_core_intr, musb_irq_max);
Riku Voipio9a9123b2011-01-17 16:50:40 +0000231 s->musb = musb_init(&dev->qdev, 0);
232 vmstate_register(&dev->qdev, -1, &vmstate_omap3_hsusb_otg, s);
233 return 0;
Riku Voipiod19c3642011-01-17 16:50:37 +0000234}
Riku Voipiod19c3642011-01-17 16:50:37 +0000235
Riku Voipio9a9123b2011-01-17 16:50:40 +0000236static SysBusDeviceInfo omap3_hsusb_otg_info = {
237 .init = omap3_hsusb_otg_init,
238 .qdev.name = "omap3_hsusb_otg",
239 .qdev.size = sizeof(OMAP3HSUSBOTGState),
240 .qdev.reset = omap3_hsusb_otg_reset,
241};
242
243typedef struct omap3_hsusb_host_s {
244 SysBusDevice busdev;
Riku Voipiod19c3642011-01-17 16:50:37 +0000245 qemu_irq ehci_irq;
246 qemu_irq tll_irq;
247
248 uint32_t uhh_sysconfig;
249 uint32_t uhh_hostconfig;
250 uint32_t uhh_debug_csr;
251 uint32_t tll_sysconfig;
252 uint32_t insnreg05_ulpi;
Riku Voipio9a9123b2011-01-17 16:50:40 +0000253} OMAP3HSUSBHostState;
254
255static const VMStateDescription vmstate_omap3_hsusb_host = {
256 .name = "omap3_hsusb_host",
257 .version_id = 1,
258 .minimum_version_id = 1,
259 .minimum_version_id_old = 1,
260 .fields = (VMStateField[]) {
261 VMSTATE_UINT32(uhh_sysconfig, OMAP3HSUSBHostState),
262 VMSTATE_UINT32(uhh_hostconfig, OMAP3HSUSBHostState),
263 VMSTATE_UINT32(uhh_debug_csr, OMAP3HSUSBHostState),
264 VMSTATE_UINT32(tll_sysconfig, OMAP3HSUSBHostState),
265 VMSTATE_UINT32(insnreg05_ulpi, OMAP3HSUSBHostState),
266 VMSTATE_END_OF_LIST()
267 }
Riku Voipiod19c3642011-01-17 16:50:37 +0000268};
269
Riku Voipio9a9123b2011-01-17 16:50:40 +0000270static void omap3_hsusb_host_reset(DeviceState *dev)
Riku Voipiod19c3642011-01-17 16:50:37 +0000271{
Riku Voipio9a9123b2011-01-17 16:50:40 +0000272 OMAP3HSUSBHostState *s = FROM_SYSBUS(OMAP3HSUSBHostState,
273 sysbus_from_qdev(dev));
Riku Voipiod19c3642011-01-17 16:50:37 +0000274 s->uhh_sysconfig = 1;
275 s->uhh_hostconfig = 0x700;
276 s->uhh_debug_csr = 0x20;
277 /* TODO: perform OHCI & EHCI reset */
278 s->tll_sysconfig = 1;
279}
280
281static uint32_t omap3_hsusb_host_read(void *opaque, target_phys_addr_t addr)
282{
Riku Voipio9a9123b2011-01-17 16:50:40 +0000283 OMAP3HSUSBHostState *s = opaque;
Riku Voipiod19c3642011-01-17 16:50:37 +0000284 TRACE(OMAP_FMT_plx, addr);
285
286 switch (addr) {
287 case 0x00: /* UHH_REVISION */
288 return 0x10;
289 case 0x10: /* UHH_SYSCONFIG */
290 return s->uhh_sysconfig;
291 case 0x14: /* UHH_SYSSTATUS */
292 return 0x7; /* EHCI_RESETDONE | OHCI_RESETDONE | RESETDONE */
293 case 0x40: /* UHH_HOSTCONFIG */
294 return s->uhh_hostconfig;
295 case 0x44: /* UHH_DEBUG_CSR */
296 return s->uhh_debug_csr;
297 default:
298 break;
299 }
300 OMAP_BAD_REG(addr);
301 return 0;
302}
303
304static void omap3_hsusb_host_write(void *opaque, target_phys_addr_t addr,
305 uint32_t value)
306{
Riku Voipio9a9123b2011-01-17 16:50:40 +0000307 OMAP3HSUSBHostState *s = opaque;
Riku Voipiod19c3642011-01-17 16:50:37 +0000308 TRACE(OMAP_FMT_plx " = 0x%08x", addr, value);
309
310 switch (addr) {
311 case 0x00: /* UHH_REVISION */
312 case 0x14: /* UHH_SYSSTATUS */
313 OMAP_RO_REGV(addr, value);
314 break;
315 case 0x10: /* UHH_SYSCONFIG */
316 s->uhh_sysconfig = value & 0x311d;
317 if (value & 2) { /* SOFTRESET */
Riku Voipio9a9123b2011-01-17 16:50:40 +0000318 omap3_hsusb_host_reset(&s->busdev.qdev);
Riku Voipiod19c3642011-01-17 16:50:37 +0000319 }
320 break;
321 case 0x40: /* UHH_HOSTCONFIG */
322 s->uhh_hostconfig = value & 0x1f3d;
323 break;
324 case 0x44: /* UHH_DEBUG_CSR */
325 s->uhh_debug_csr = value & 0xf00ff;
326 break;
327 default:
328 OMAP_BAD_REGV(addr, value);
329 break;
330 }
331}
332
333static CPUReadMemoryFunc *omap3_hsusb_host_readfn[] = {
334 omap_badwidth_read32,
335 omap_badwidth_read32,
336 omap3_hsusb_host_read,
337};
338
339static CPUWriteMemoryFunc *omap3_hsusb_host_writefn[] = {
340 omap_badwidth_write32,
341 omap_badwidth_write32,
342 omap3_hsusb_host_write,
343};
344
345static uint32_t omap3_hsusb_ehci_read(void *opaque, target_phys_addr_t addr)
346{
Riku Voipio9a9123b2011-01-17 16:50:40 +0000347 OMAP3HSUSBHostState *s = opaque;
Riku Voipiod19c3642011-01-17 16:50:37 +0000348 TRACE(OMAP_FMT_plx, addr);
349 switch (addr) {
350 case 0xa4: /* INSNREG05_ULPI */
351 return s->insnreg05_ulpi;
352 default:
353 break;
354 }
355 return 0;
356}
357
358static void omap3_hsusb_ehci_write(void *opaque, target_phys_addr_t addr,
359 uint32_t value)
360{
Riku Voipio9a9123b2011-01-17 16:50:40 +0000361 OMAP3HSUSBHostState *s = opaque;
Riku Voipiod19c3642011-01-17 16:50:37 +0000362 TRACE(OMAP_FMT_plx " = 0x%08x", addr, value);
363
364 switch (addr) {
365 case 0xa4: /* INSNREG05_ULPI */
366 s->insnreg05_ulpi = value & 0xF0000000;
367 default:
368 break;
369 }
370}
371
372static CPUReadMemoryFunc *omap3_hsusb_ehci_readfn[] = {
373 omap_badwidth_read32,
374 omap_badwidth_read32,
375 omap3_hsusb_ehci_read,
376};
377
378static CPUWriteMemoryFunc *omap3_hsusb_ehci_writefn[] = {
379 omap_badwidth_write32,
380 omap_badwidth_write32,
381 omap3_hsusb_ehci_write,
382};
383
384static uint32_t omap3_hsusb_tll_read(void *opaque, target_phys_addr_t addr)
385{
Riku Voipio9a9123b2011-01-17 16:50:40 +0000386 OMAP3HSUSBHostState *s = opaque;
Riku Voipiod19c3642011-01-17 16:50:37 +0000387 TRACE(OMAP_FMT_plx, addr);
388
389 switch (addr) {
390 case 0x00: /* USBTLL_REVISION */
391 return 0x1;
392 case 0x10: /* USBTLL_SYSCONFIG */
393 return s->tll_sysconfig;
394 case 0x14: /* USBTLL_SYSSTATUS */
395 return 0x1; /* RESETDONE */
396 case 0x18: /* USBTLL_IRQSTATUS */
397 return 0;
398 case 0x1C: /* USBTLL_IRQENABLE */
399 return 0;
400 default:
401 break;
402 }
403 return 0;
404}
405
406static void omap3_hsusb_tll_write(void *opaque, target_phys_addr_t addr,
407 uint32_t value)
408{
Riku Voipio9a9123b2011-01-17 16:50:40 +0000409 OMAP3HSUSBHostState *s = opaque;
Riku Voipiod19c3642011-01-17 16:50:37 +0000410 TRACE(OMAP_FMT_plx " = 0x%08x", addr, value);
411
412 switch (addr) {
413 case 0x00: /* USBTLL_REVISION */
414 case 0x14: /* USBTLL_SYSSTATUS */
415 OMAP_RO_REGV(addr, value);
Matt Waddel8877abd2011-01-17 16:50:37 +0000416 break;
Riku Voipiod19c3642011-01-17 16:50:37 +0000417 case 0x10: /* USBTLL_SYSCONFIG */
418 s->tll_sysconfig = value & 0xFFFFFEE0;;
Matt Waddel8877abd2011-01-17 16:50:37 +0000419 break;
Riku Voipiod19c3642011-01-17 16:50:37 +0000420 default:
421 OMAP_BAD_REGV(addr, value);
422 break;
423 }
424}
425
426static CPUReadMemoryFunc *omap3_hsusb_tll_readfn[] = {
427 omap_badwidth_read32,
428 omap_badwidth_read32,
429 omap3_hsusb_tll_read,
430};
431
432static CPUWriteMemoryFunc *omap3_hsusb_tll_writefn[] = {
433 omap_badwidth_write32,
434 omap_badwidth_write32,
435 omap3_hsusb_tll_write,
436};
437
Riku Voipio9a9123b2011-01-17 16:50:40 +0000438static int omap3_hsusb_host_init(SysBusDevice *dev)
Riku Voipiod19c3642011-01-17 16:50:37 +0000439{
Riku Voipio9a9123b2011-01-17 16:50:40 +0000440 OMAP3HSUSBHostState *s = FROM_SYSBUS(OMAP3HSUSBHostState, dev);
441 sysbus_init_irq(dev, &s->ehci_irq);
442 sysbus_init_irq(dev, &s->tll_irq);
443 sysbus_init_mmio(dev, 0x1000,
444 cpu_register_io_memory(omap3_hsusb_tll_readfn,
445 omap3_hsusb_tll_writefn, s,
446 DEVICE_NATIVE_ENDIAN));
447 sysbus_init_mmio(dev, 0x400,
448 cpu_register_io_memory(omap3_hsusb_host_readfn,
449 omap3_hsusb_host_writefn, s,
450 DEVICE_NATIVE_ENDIAN));
451 sysbus_init_mmio(dev, 0x400,
452 cpu_register_io_memory(omap3_hsusb_ehci_readfn,
453 omap3_hsusb_ehci_writefn, s,
454 DEVICE_NATIVE_ENDIAN));
Peter Maydell30450852011-06-10 12:09:29 +0000455 /* OHCI is instantiated by omap3.c */
Riku Voipio9a9123b2011-01-17 16:50:40 +0000456 vmstate_register(&dev->qdev, -1, &vmstate_omap3_hsusb_host, s);
457 return 0;
Riku Voipiod19c3642011-01-17 16:50:37 +0000458}
Riku Voipiod19c3642011-01-17 16:50:37 +0000459
Riku Voipio9a9123b2011-01-17 16:50:40 +0000460static SysBusDeviceInfo omap3_hsusb_host_info = {
461 .init = omap3_hsusb_host_init,
462 .qdev.name = "omap3_hsusb_host",
463 .qdev.size = sizeof(OMAP3HSUSBHostState),
464 .qdev.reset = omap3_hsusb_host_reset,
Riku Voipiod19c3642011-01-17 16:50:37 +0000465};
466
Riku Voipio9a9123b2011-01-17 16:50:40 +0000467static void omap3_hsusb_register_devices(void)
Riku Voipiod19c3642011-01-17 16:50:37 +0000468{
Riku Voipio9a9123b2011-01-17 16:50:40 +0000469 sysbus_register_withprop(&omap3_hsusb_otg_info);
470 sysbus_register_withprop(&omap3_hsusb_host_info);
Riku Voipiod19c3642011-01-17 16:50:37 +0000471}
472
Riku Voipio9a9123b2011-01-17 16:50:40 +0000473device_init(omap3_hsusb_register_devices)