1e3bcb602eb914579d2c1a503e28cc2e126ccbc1
[qemu] / hw / omap3_usb.c
1 /*
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) version 3 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"
27
28 #define OMAP3_HSUSB_DEBUG
29
30 #ifdef OMAP3_HSUSB_DEBUG
31 #define TRACE(fmt,...) fprintf(stderr, "%s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__)
32 #else
33 #define TRACE(...)
34 #endif
35
36 /* usb-musb.c */
37 extern CPUReadMemoryFunc *musb_read[];
38 extern CPUWriteMemoryFunc *musb_write[];
39
40 struct omap3_hsusb_otg_s {
41     qemu_irq mc_irq;
42     qemu_irq dma_irq;
43     struct musb_s *musb;
44     
45     uint8_t rev;
46     uint16_t sysconfig;
47     uint8_t interfsel;
48     uint8_t simenable;
49     uint8_t forcestdby;
50 };
51
52 static void omap3_hsusb_otg_save_state(QEMUFile *f, void *opaque)
53 {
54     struct omap3_hsusb_otg_s *s = (struct omap3_hsusb_otg_s *)opaque;
55     
56     qemu_put_be16(f, s->sysconfig);
57     qemu_put_byte(f, s->interfsel);
58     qemu_put_byte(f, s->simenable);
59     qemu_put_byte(f, s->forcestdby);
60 }
61
62 static int omap3_hsusb_otg_load_state(QEMUFile *f, void *opaque, int version_id)
63 {
64     struct omap3_hsusb_otg_s *s = (struct omap3_hsusb_otg_s *)opaque;
65     
66     if (version_id)
67         return -EINVAL;
68     
69     s->sysconfig = qemu_get_be16(f);
70     s->interfsel = qemu_get_byte(f);
71     s->simenable = qemu_get_byte(f);
72     s->forcestdby = qemu_get_byte(f);
73     
74     return 0;
75 }
76
77 static void omap3_hsusb_otg_reset(struct omap3_hsusb_otg_s *s)
78 {
79     s->rev = 0x33;
80     s->sysconfig = 0;
81     s->interfsel = 0x1;
82     s->simenable = 0;
83     s->forcestdby = 1;
84 }
85
86 static uint32_t omap3_hsusb_otg_readb(void *opaque, target_phys_addr_t addr)
87 {
88     struct omap3_hsusb_otg_s *s = (struct omap3_hsusb_otg_s *)opaque;
89     if (addr < 0x200)
90         return musb_read[0](s->musb, addr);
91     if (addr < 0x400)
92         return musb_read[0](s->musb, 0x20 + ((addr >> 3 ) & 0x3c));
93     OMAP_BAD_REG(addr);
94     return 0;
95 }
96
97 static uint32_t omap3_hsusb_otg_readh(void *opaque, target_phys_addr_t addr)
98 {
99     struct omap3_hsusb_otg_s *s = (struct omap3_hsusb_otg_s *)opaque;
100     if (addr < 0x200)
101         return musb_read[1](s->musb, addr);
102     if (addr < 0x400)
103         return musb_read[1](s->musb, 0x20 + ((addr >> 3 ) & 0x3c));
104     OMAP_BAD_REG(addr);
105     return 0;
106 }
107
108 static uint32_t omap3_hsusb_otg_read(void *opaque, target_phys_addr_t addr)
109 {
110     struct omap3_hsusb_otg_s *s = (struct omap3_hsusb_otg_s *)opaque;
111     
112     if (addr < 0x200)
113         return musb_read[2](s->musb, addr);
114     if (addr < 0x400)
115         return musb_read[2](s->musb, 0x20 + ((addr >> 3 ) & 0x3c));
116     
117     switch (addr) {
118         case 0x400: /* OTG_REVISION */
119             TRACE("OTG_REVISION: 0x%08x", s->rev);
120             return s->rev;
121         case 0x404: /* OTG_SYSCONFIG */
122             TRACE("OTG_SYSCONFIG: 0x%08x", s->sysconfig);
123             return s->sysconfig;
124         case 0x408: /* OTG_SYSSTATUS */
125             TRACE("OTG_SYSSTATUS: 0x00000001");
126             return 1; /* reset finished */
127         case 0x40c: /* OTG_INTERFSEL */
128             TRACE("OTG_INTERFSEL: 0x%08x", s->interfsel);
129             return s->interfsel;
130         case 0x410: /* OTG_SIMENABLE */
131             TRACE("OTG_SIMENABLE: 0x%08x", s->simenable);
132             return s->simenable;
133         case 0x414: /* OTG_FORCESTDBY */
134             TRACE("OTG_FORCESTDBY: 0x%08x", s->forcestdby);
135             return s->forcestdby;
136         default:
137             break;
138     }
139     OMAP_BAD_REG(addr);
140     return 0;
141 }
142
143 static void omap3_hsusb_otg_writeb(void *opaque, target_phys_addr_t addr,
144                                    uint32_t value)
145 {
146     struct omap3_hsusb_otg_s *s = (struct omap3_hsusb_otg_s *)opaque;
147     
148     if (addr < 0x200)
149         musb_write[0](s->musb, addr, value);
150     else if (addr < 0x400)
151         musb_write[0](s->musb, 0x20 + ((addr >> 3) & 0x3c), value);
152     else
153         OMAP_BAD_REG(addr);
154 }
155
156 static void omap3_hsusb_otg_writeh(void *opaque, target_phys_addr_t addr,
157                                    uint32_t value)
158 {
159     struct omap3_hsusb_otg_s *s = (struct omap3_hsusb_otg_s *)opaque;
160     
161     if (addr < 0x200)
162         musb_write[1](s->musb, addr, value);
163     else if (addr < 0x400)
164         musb_write[1](s->musb, 0x20 + ((addr >> 3) & 0x3c), value);
165     else
166         OMAP_BAD_REG(addr);
167 }
168
169 static void omap3_hsusb_otg_write(void *opaque, target_phys_addr_t addr,
170                                   uint32_t value)
171 {
172     struct omap3_hsusb_otg_s *s = (struct omap3_hsusb_otg_s *)opaque;
173     
174     if (addr < 0x200)
175         musb_write[2](s->musb, addr, value);
176     else if (addr < 0x400)
177         musb_write[2](s->musb, 0x20 + ((addr >> 3) & 0x3c), value);
178     else switch (addr) {
179         case 0x400: /* OTG_REVISION */
180         case 0x408: /* OTG_SYSSTATUS */
181             OMAP_RO_REGV(addr, value);
182             break;
183         case 0x404: /* OTG_SYSCONFIG */
184             TRACE("OTG_SYSCONFIG = 0x%08x", value);
185             if (value & 2) /* SOFTRESET */
186                 omap3_hsusb_otg_reset(s);
187             s->sysconfig = value & 0x301f;
188             break;
189         case 0x40c: /* OTG_INTERFSEL */
190             TRACE("OTG_INTERFSEL = 0x%08x", value);
191             s->interfsel = value & 0x3;
192             break;
193         case 0x410: /* OTG_SIMENABLE */
194             TRACE("OTG_SIMENABLE = 0x%08x", value);
195             cpu_abort(cpu_single_env, "%s: USB simulation mode not supported\n",
196                       __FUNCTION__);
197             break;
198         case 0x414: /* OTG_FORCESTDBY */
199             TRACE("OTG_FORCESTDBY = 0x%08x", value);
200             s->forcestdby = value & 1;
201             break;
202         default:
203             OMAP_BAD_REGV(addr, value);
204             break;
205     }
206 }
207
208 static CPUReadMemoryFunc *omap3_hsusb_otg_readfn[] = {
209     omap3_hsusb_otg_readb,
210     omap3_hsusb_otg_readh,
211     omap3_hsusb_otg_read,
212 };
213
214 static CPUWriteMemoryFunc *omap3_hsusb_otg_writefn[] = {
215     omap3_hsusb_otg_writeb,
216     omap3_hsusb_otg_writeh,
217     omap3_hsusb_otg_write,
218 };
219
220 static void omap3_hsusb_musb_core_intr(void *opaque, int source, int level)
221 {
222     struct omap3_hsusb_otg_s *s = (struct omap3_hsusb_otg_s *)opaque;
223     uint32_t value = musb_core_intr_get(s->musb);
224     TRACE("intr 0x%08x, 0x%08x, 0x%08x", source, level, value);
225     switch (source) {
226     case musb_set_vbus:
227        TRACE("ignoring VBUS");
228        break;
229     case musb_set_session:
230        TRACE("ignoring SESSION");
231        break;
232     case musb_irq_tx:
233     case musb_irq_rx:
234        TRACE("rxtx");
235        break;
236        /* Fall through */
237     default:
238        TRACE("other");
239     }
240     qemu_set_irq(s->mc_irq, level);
241 }
242
243 static void omap3_hsusb_otg_init(struct omap_target_agent_s *otg_ta,
244                                  qemu_irq mc_irq,
245                                  qemu_irq dma_irq,
246                                  struct omap3_hsusb_otg_s *s)
247 {
248     s->mc_irq = mc_irq;
249     s->dma_irq = dma_irq;
250     
251     omap_l4_attach(otg_ta, 0, l4_register_io_memory(0, omap3_hsusb_otg_readfn,
252                                                     omap3_hsusb_otg_writefn, s));
253     
254     s->musb = musb_init(qemu_allocate_irqs(omap3_hsusb_musb_core_intr, s, __musb_irq_max));
255     omap3_hsusb_otg_reset(s);
256     
257     register_savevm("omap3_hsusb_otg", -1, 0,
258                     omap3_hsusb_otg_save_state,
259                     omap3_hsusb_otg_load_state,
260                     s);
261 }
262
263 struct omap3_hsusb_host_s {
264     qemu_irq ehci_irq;
265     qemu_irq tll_irq;
266     
267     uint32_t uhh_sysconfig;
268     uint32_t uhh_hostconfig;
269     uint32_t uhh_debug_csr;
270 };
271
272 static void omap3_hsusb_host_reset(struct omap3_hsusb_host_s *s)
273 {
274     s->uhh_sysconfig = 1;
275     s->uhh_hostconfig = 0x700;
276     s->uhh_debug_csr = 0x20;
277     /* TODO: perform OHCI & EHCI reset */
278 }
279
280 static uint32_t omap3_hsusb_host_read(void *opaque, target_phys_addr_t addr)
281 {
282     struct omap3_hsusb_host_s *s = (struct omap3_hsusb_host_s *)opaque;
283     
284     switch (addr) {
285         case 0x00: /* UHH_REVISION */
286             return 0x10;
287         case 0x10: /* UHH_SYSCONFIG */
288             return s->uhh_sysconfig;
289         case 0x14: /* UHH_SYSSTATUS */
290             return 0x7; /* EHCI_RESETDONE | OHCI_RESETDONE | RESETDONE */
291         case 0x40: /* UHH_HOSTCONFIG */
292             return s->uhh_hostconfig;
293         case 0x44: /* UHH_DEBUG_CSR */
294             return s->uhh_debug_csr;
295         default:
296             break;
297     }
298     OMAP_BAD_REG(addr);
299     return 0;
300 }
301
302 static void omap3_hsusb_host_write(void *opaque, target_phys_addr_t addr,
303                                    uint32_t value)
304 {
305     struct omap3_hsusb_host_s *s = (struct omap3_hsusb_host_s *)opaque;
306     
307     switch (addr) {
308         case 0x00: /* UHH_REVISION */
309         case 0x14: /* UHH_SYSSTATUS */
310             OMAP_RO_REGV(addr, value);
311             break;
312         case 0x10: /* UHH_SYSCONFIG */
313             s->uhh_sysconfig = value & 0x311d;
314             if (value & 2) { /* SOFTRESET */
315                 omap3_hsusb_host_reset(s);
316             }
317             break;
318         case 0x40: /* UHH_HOSTCONFIG */
319             s->uhh_hostconfig = value & 0x1f3d;
320             break;
321         case 0x44: /* UHH_DEBUG_CSR */
322             s->uhh_debug_csr = value & 0xf00ff;
323             break;
324         default:
325             OMAP_BAD_REGV(addr, value);
326             break;
327     }
328 }
329
330 static CPUReadMemoryFunc *omap3_hsusb_host_readfn[] = {
331     omap_badwidth_read32,
332     omap_badwidth_read32,
333     omap3_hsusb_host_read,
334 };
335
336 static CPUWriteMemoryFunc *omap3_hsusb_host_writefn[] = {
337     omap_badwidth_write32,
338     omap_badwidth_write32,
339     omap3_hsusb_host_write,
340 };
341
342 static uint32_t omap3_hsusb_ehci_read(void *opaque, target_phys_addr_t addr)
343 {
344     TRACE(OMAP_FMT_plx, addr);
345     return 0;
346 }
347
348 static void omap3_hsusb_ehci_write(void *opaque, target_phys_addr_t addr,
349                                    uint32_t value)
350 {
351     TRACE(OMAP_FMT_plx " = 0x%08x", addr, value);
352 }
353
354 static CPUReadMemoryFunc *omap3_hsusb_ehci_readfn[] = {
355     omap_badwidth_read32,
356     omap_badwidth_read32,
357     omap3_hsusb_ehci_read,
358 };
359
360 static CPUWriteMemoryFunc *omap3_hsusb_ehci_writefn[] = {
361     omap_badwidth_write32,
362     omap_badwidth_write32,
363     omap3_hsusb_ehci_write,
364 };
365
366 static uint32_t omap3_hsusb_tll_read(void *opaque, target_phys_addr_t addr)
367 {
368     TRACE(OMAP_FMT_plx, addr);
369     return 0;
370 }
371
372 static void omap3_hsusb_tll_write(void *opaque, target_phys_addr_t addr,
373                                   uint32_t value)
374 {
375     TRACE(OMAP_FMT_plx " = 0x%08x", addr, value);
376 }
377
378 static CPUReadMemoryFunc *omap3_hsusb_tll_readfn[] = {
379     omap_badwidth_read32,
380     omap_badwidth_read32,
381     omap3_hsusb_tll_read,
382 };
383
384 static CPUWriteMemoryFunc *omap3_hsusb_tll_writefn[] = {
385     omap_badwidth_write32,
386     omap_badwidth_write32,
387     omap3_hsusb_tll_write,
388 };
389
390 static void omap3_hsusb_host_init(struct omap_target_agent_s *host_ta,
391                                   struct omap_target_agent_s *tll_ta,
392                                   qemu_irq ohci_irq,
393                                   qemu_irq ehci_irq,
394                                   qemu_irq tll_irq,
395                                   struct omap3_hsusb_host_s *s)
396 {
397     s->ehci_irq = ehci_irq;
398     s->tll_irq  = tll_irq;
399     
400     omap_l4_attach(tll_ta, 0, l4_register_io_memory(0,
401                                                     omap3_hsusb_tll_readfn,
402                                                     omap3_hsusb_tll_writefn,
403                                                     s));
404     omap_l4_attach(host_ta, 0, l4_register_io_memory(0,
405                                                      omap3_hsusb_host_readfn,
406                                                      omap3_hsusb_host_writefn,
407                                                      s));
408     omap_l4_attach(host_ta, 1, usb_ohci_init_omap(omap_l4_base(host_ta, 1),
409                                                   omap_l4_size(host_ta, 1),
410                                                   3, ohci_irq));
411     omap_l4_attach(host_ta, 2, l4_register_io_memory(0,
412                                                      omap3_hsusb_ehci_readfn,
413                                                      omap3_hsusb_ehci_writefn,
414                                                      s));
415     
416     omap3_hsusb_host_reset(s);
417 }
418
419 struct omap3_hsusb_s {
420     struct omap3_hsusb_otg_s otg;
421     struct omap3_hsusb_host_s host;
422 };
423
424 struct omap3_hsusb_s *omap3_hsusb_init(struct omap_target_agent_s *otg_ta,
425                                        struct omap_target_agent_s *host_ta,
426                                        struct omap_target_agent_s *tll_ta,
427                                        qemu_irq mc_irq,
428                                        qemu_irq dma_irq,
429                                        qemu_irq ohci_irq,
430                                        qemu_irq ehci_irq,
431                                        qemu_irq tll_irq)
432 {
433     struct omap3_hsusb_s *s = qemu_mallocz(sizeof(struct omap3_hsusb_s));
434     omap3_hsusb_otg_init(otg_ta, mc_irq, dma_irq, &s->otg);
435     omap3_hsusb_host_init(host_ta, tll_ta, ohci_irq, ehci_irq, tll_irq, &s->host);
436     return s;
437 }
438