f0f6a48a1576c727ef68608fdd2f8ef22c95103f
[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
27 #define OMAP3_HSUSB_DEBUG
28
29 #ifdef OMAP3_HSUSB_DEBUG
30 #define TRACE(fmt,...) fprintf(stderr, "%s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__)
31 #else
32 #define TRACE(...)
33 #endif
34
35 extern CPUReadMemoryFunc *musb_read[];
36 extern CPUWriteMemoryFunc *musb_write[];
37
38 struct omap3_hsusb_otg_s {
39     qemu_irq mc_irq;
40     qemu_irq dma_irq;
41     struct musb_s *musb;
42     
43     uint8_t rev;
44     uint16_t sysconfig;
45     uint8_t interfsel;
46     uint8_t simenable;
47     uint8_t forcestdby;
48 };
49
50 static void omap3_hsusb_otg_reset(struct omap3_hsusb_otg_s *s)
51 {
52     s->rev = 0;
53     s->sysconfig = 0;
54     s->interfsel = 0x1;
55     s->simenable = 0;
56     s->forcestdby = 1;
57 }
58
59 static uint32_t omap3_hsusb_otg_readb(void *opaque, target_phys_addr_t addr)
60 {
61     struct omap3_hsusb_otg_s *s = (struct omap3_hsusb_otg_s *)opaque;
62     if (addr < 0x200)
63         return musb_read[0](s->musb, addr);
64     if (addr < 0x400)
65         return musb_read[0](s->musb, 0x20 + ((addr >> 3 ) & 0x3c));
66     OMAP_BAD_REG(addr);
67     return 0;
68 }
69
70 static uint32_t omap3_hsusb_otg_readh(void *opaque, target_phys_addr_t addr)
71 {
72     struct omap3_hsusb_otg_s *s = (struct omap3_hsusb_otg_s *)opaque;
73     if (addr < 0x200)
74         return musb_read[1](s->musb, addr);
75     if (addr < 0x400)
76         return musb_read[1](s->musb, 0x20 + ((addr >> 3 ) & 0x3c));
77     OMAP_BAD_REG(addr);
78     return 0;
79 }
80
81 static uint32_t omap3_hsusb_otg_read(void *opaque, target_phys_addr_t addr)
82 {
83     struct omap3_hsusb_otg_s *s = (struct omap3_hsusb_otg_s *)opaque;
84     
85     if (addr < 0x200)
86         return musb_read[2](s->musb, addr);
87     if (addr < 0x400)
88         return musb_read[2](s->musb, 0x20 + ((addr >> 3 ) & 0x3c));
89     
90     switch (addr) {
91         case 0x400: /* OTG_REVISION */
92             TRACE("OTG_REVISION: 0x%08x", s->rev);
93             return s->rev;
94         case 0x404: /* OTG_SYSCONFIG */
95             TRACE("OTG_SYSCONFIG: 0x%08x", s->sysconfig);
96             return s->sysconfig;
97         case 0x408: /* OTG_SYSSTATUS */
98             TRACE("OTG_SYSSTATUS: 0x00000001");
99             return 1; /* reset finished */
100         case 0x40c: /* OTG_INTERFSEL */
101             TRACE("OTG_INTERFSEL: 0x%08x", s->interfsel);
102             return s->interfsel;
103         case 0x410: /* OTG_SIMENABLE */
104             TRACE("OTG_SIMENABLE: 0x%08x", s->simenable);
105             return s->simenable;
106         case 0x414: /* OTG_FORCESTDBY */
107             TRACE("OTG_FORCESTDBY: 0x%08x", s->forcestdby);
108             return s->forcestdby;
109         default:
110             break;
111     }
112     OMAP_BAD_REG(addr);
113     return 0;
114 }
115
116 static void omap3_hsusb_otg_writeb(void *opaque, target_phys_addr_t addr,
117                                    uint32_t value)
118 {
119     struct omap3_hsusb_otg_s *s = (struct omap3_hsusb_otg_s *)opaque;
120     
121     if (addr < 0x200)
122         musb_write[0](s->musb, addr, value);
123     else if (addr < 0x400)
124         musb_write[0](s->musb, 0x20 + ((addr >> 3) & 0x3c), value);
125     else
126         OMAP_BAD_REG(addr);
127 }
128
129 static void omap3_hsusb_otg_writeh(void *opaque, target_phys_addr_t addr,
130                                    uint32_t value)
131 {
132     struct omap3_hsusb_otg_s *s = (struct omap3_hsusb_otg_s *)opaque;
133     
134     if (addr < 0x200)
135         musb_write[1](s->musb, addr, value);
136     else if (addr < 0x400)
137         musb_write[1](s->musb, 0x20 + ((addr >> 3) & 0x3c), value);
138     else
139         OMAP_BAD_REG(addr);
140 }
141
142 static void omap3_hsusb_otg_write(void *opaque, target_phys_addr_t addr,
143                                   uint32_t value)
144 {
145     struct omap3_hsusb_otg_s *s = (struct omap3_hsusb_otg_s *)opaque;
146     
147     if (addr < 0x200)
148         musb_write[1](s->musb, addr, value);
149     else if (addr < 0x400)
150         musb_write[1](s->musb, 0x20 + ((addr >> 3) & 0x3c), value);
151     else switch (addr) {
152         case 0x400: /* OTG_REVISION */
153         case 0x408: /* OTG_SYSSTATUS */
154             OMAP_RO_REGV(addr, value);
155             break;
156         case 0x404: /* OTG_SYSCONFIG */
157             TRACE("OTG_SYSCONFIG = 0x%08x", value);
158             if (value & 2) /* SOFTRESET */
159                 omap3_hsusb_otg_reset(s);
160             s->sysconfig = value & 0x301f;
161             break;
162         case 0x40c: /* OTG_INTERFSEL */
163             TRACE("OTG_INTERFSEL = 0x%08x", value);
164             s->interfsel = value & 0x3;
165             break;
166         case 0x410: /* OTG_SIMENABLE */
167             TRACE("OTG_SIMENABLE = 0x%08x", value);
168             s->simenable = value & 1;
169             break;
170         case 0x414: /* OTG_FORCESTDBY */
171             TRACE("OTG_FORCESTDBY = 0x%08x", value);
172             s->forcestdby = value & 1;
173             break;
174         default:
175             OMAP_BAD_REGV(addr, value);
176             break;
177     }
178 }
179
180 static CPUReadMemoryFunc *omap3_hsusb_otg_readfn[] = {
181     omap3_hsusb_otg_readb,
182     omap3_hsusb_otg_readh,
183     omap3_hsusb_otg_read,
184 };
185
186 static CPUWriteMemoryFunc *omap3_hsusb_otg_writefn[] = {
187     omap3_hsusb_otg_writeb,
188     omap3_hsusb_otg_writeh,
189     omap3_hsusb_otg_write,
190 };
191
192 static void omap3_hsusb_musb_core_intr(void *opaque, int source, int level)
193 {
194     struct omap3_hsusb_otg_s *s = (struct omap3_hsusb_otg_s *)opaque;
195     
196     qemu_set_irq(s->mc_irq, musb_core_intr_get(s->musb));
197 }
198
199 static void omap3_hsusb_otg_init(struct omap_target_agent_s *otg_ta,
200                                  qemu_irq mc_irq,
201                                  qemu_irq dma_irq,
202                                  struct omap3_hsusb_otg_s *s)
203 {
204     s->mc_irq = mc_irq;
205     s->dma_irq = dma_irq;
206     
207     omap_l4_attach(otg_ta, 0, l4_register_io_memory(0, omap3_hsusb_otg_readfn,
208                                                     omap3_hsusb_otg_writefn, s));
209     
210     s->musb = musb_init(qemu_allocate_irqs(omap3_hsusb_musb_core_intr, s, __musb_irq_max));
211     omap3_hsusb_otg_reset(s);
212 }
213
214 struct omap3_hsusb_host_s {
215     struct {
216         qemu_irq ohci_irq;
217         qemu_irq ehci_irq;
218     } hc;
219     struct {
220         qemu_irq irq;
221     } tll;
222 };
223
224 static uint32_t omap3_hsusb_host_read(void *opaque, target_phys_addr_t addr)
225 {
226     TRACE("0x%04x", addr);
227     return 0;
228 }
229
230 static void omap3_hsusb_host_write(void *opaque, target_phys_addr_t addr,
231                                    uint32_t value)
232 {
233     TRACE("0x%04x = 0x%08x", addr, value);
234 }
235
236 static CPUReadMemoryFunc *omap3_hsusb_host_readfn[] = {
237     omap_badwidth_read32,
238     omap_badwidth_read32,
239     omap3_hsusb_host_read,
240 };
241
242 static CPUWriteMemoryFunc *omap3_hsusb_host_writefn[] = {
243     omap_badwidth_write32,
244     omap_badwidth_write32,
245     omap3_hsusb_host_write,
246 };
247
248 static uint32_t omap3_hsusb_tll_read(void *opaque, target_phys_addr_t addr)
249 {
250     TRACE("0x%04x", addr);
251     return 0;
252 }
253
254 static void omap3_hsusb_tll_write(void *opaque, target_phys_addr_t addr,
255                                   uint32_t value)
256 {
257     TRACE("0x%04x = 0x%08x", addr, value);
258 }
259
260 static CPUReadMemoryFunc *omap3_hsusb_tll_readfn[] = {
261     omap_badwidth_read32,
262     omap_badwidth_read32,
263     omap3_hsusb_tll_read,
264 };
265
266 static CPUWriteMemoryFunc *omap3_hsusb_tll_writefn[] = {
267     omap_badwidth_write32,
268     omap_badwidth_write32,
269     omap3_hsusb_tll_write,
270 };
271
272 static void omap3_hsusb_host_init(struct omap_target_agent_s *host_ta,
273                                   struct omap_target_agent_s *tll_ta,
274                                   qemu_irq ohci_irq,
275                                   qemu_irq ehci_irq,
276                                   qemu_irq tll_irq,
277                                   struct omap3_hsusb_host_s *s)
278 {
279     s->hc.ohci_irq = ohci_irq;
280     s->hc.ehci_irq = ehci_irq;
281     s->tll.irq     = tll_irq;
282     
283     omap_l4_attach(host_ta, 0, l4_register_io_memory(0, omap3_hsusb_host_readfn,
284                                                      omap3_hsusb_host_writefn, s));
285     omap_l4_attach(tll_ta, 0, l4_register_io_memory(0, omap3_hsusb_tll_readfn,
286                                                     omap3_hsusb_tll_writefn, s));
287 }
288
289 struct omap3_hsusb_s {
290     struct omap3_hsusb_otg_s otg;
291     struct omap3_hsusb_host_s host;
292 };
293
294 struct omap3_hsusb_s *omap3_hsusb_init(struct omap_target_agent_s *otg_ta,
295                                        struct omap_target_agent_s *host_ta,
296                                        struct omap_target_agent_s *tll_ta,
297                                        qemu_irq mc_irq,
298                                        qemu_irq dma_irq,
299                                        qemu_irq ohci_irq,
300                                        qemu_irq ehci_irq,
301                                        qemu_irq tll_irq)
302 {
303     struct omap3_hsusb_s *s = qemu_mallocz(sizeof(struct omap3_hsusb_s));
304     omap3_hsusb_otg_init(otg_ta, mc_irq, dma_irq, &s->otg);
305     omap3_hsusb_host_init(host_ta, tll_ta, ohci_irq, ehci_irq, tll_irq, &s->host);
306     return s;
307 }