| 15 |
theseven |
1 |
//
|
|
|
2 |
//
|
|
|
3 |
// Copyright 2010 TheSeven
|
|
|
4 |
//
|
|
|
5 |
//
|
|
|
6 |
// This file is part of emBIOS.
|
|
|
7 |
//
|
|
|
8 |
// emBIOS is free software: you can redistribute it and/or
|
|
|
9 |
// modify it under the terms of the GNU General Public License as
|
|
|
10 |
// published by the Free Software Foundation, either version 2 of the
|
|
|
11 |
// License, or (at your option) any later version.
|
|
|
12 |
//
|
|
|
13 |
// emBIOS is distributed in the hope that it will be useful,
|
|
|
14 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
15 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
16 |
// See the GNU General Public License for more details.
|
|
|
17 |
//
|
|
|
18 |
// You should have received a copy of the GNU General Public License along
|
|
|
19 |
// with emBIOS. If not, see <http://www.gnu.org/licenses/>.
|
|
|
20 |
//
|
|
|
21 |
//
|
|
|
22 |
|
|
|
23 |
|
|
|
24 |
#include "global.h"
|
|
|
25 |
#include "mmu.h"
|
|
|
26 |
#include "panic.h"
|
|
|
27 |
#include "usbdrv.h"
|
|
|
28 |
#include "thread.h"
|
|
|
29 |
#include "timer.h"
|
|
|
30 |
#include "usb.h"
|
|
|
31 |
#include "usb_ch9.h"
|
|
|
32 |
#include "synopsysotg.h"
|
| 58 |
theseven |
33 |
#include "util.h"
|
| 85 |
theseven |
34 |
#include "interrupt.h"
|
| 87 |
theseven |
35 |
#include "clockgates.h"
|
| 221 |
theseven |
36 |
#include "power.h"
|
| 15 |
theseven |
37 |
|
|
|
38 |
|
|
|
39 |
struct ep_type
|
|
|
40 |
{
|
|
|
41 |
bool active;
|
|
|
42 |
bool busy;
|
|
|
43 |
bool done;
|
|
|
44 |
int rc;
|
|
|
45 |
int size;
|
|
|
46 |
struct wakeup complete;
|
|
|
47 |
} ;
|
|
|
48 |
|
|
|
49 |
static struct ep_type endpoints[5];
|
|
|
50 |
static struct usb_ctrlrequest ctrlreq CACHEALIGN_ATTR;
|
| 221 |
theseven |
51 |
static uint32_t synopsysotg_stack[0x40] STACK_ATTR;
|
| 15 |
theseven |
52 |
|
|
|
53 |
int usb_drv_port_speed(void)
|
|
|
54 |
{
|
|
|
55 |
return (DSTS & 2) == 0 ? 1 : 0;
|
|
|
56 |
}
|
|
|
57 |
|
| 66 |
theseven |
58 |
static void reset_endpoints(int reinit)
|
| 15 |
theseven |
59 |
{
|
|
|
60 |
unsigned int i;
|
|
|
61 |
for (i = 0; i < sizeof(endpoints)/sizeof(struct ep_type); i++)
|
|
|
62 |
{
|
|
|
63 |
if (reinit) endpoints[i].active = false;
|
|
|
64 |
endpoints[i].busy = false;
|
|
|
65 |
endpoints[i].rc = -1;
|
|
|
66 |
endpoints[i].done = true;
|
|
|
67 |
wakeup_signal(&endpoints[i].complete);
|
|
|
68 |
}
|
|
|
69 |
DIEPCTL0 = 0x8800; /* EP0 IN ACTIVE NEXT=1 */
|
|
|
70 |
DOEPCTL0 = 0x8000; /* EP0 OUT ACTIVE */
|
|
|
71 |
DOEPTSIZ0 = 0x20080040; /* EP0 OUT Transfer Size:
|
|
|
72 |
64 Bytes, 1 Packet, 1 Setup Packet */
|
|
|
73 |
DOEPDMA0 = (uint32_t)&ctrlreq;
|
|
|
74 |
DOEPCTL0 |= 0x84000000; /* EP0 OUT ENABLE CLEARNAK */
|
|
|
75 |
if (reinit)
|
|
|
76 |
{
|
|
|
77 |
/* The size is getting set to zero, because we don't know
|
|
|
78 |
whether we are Full Speed or High Speed at this stage */
|
|
|
79 |
/* EP1 IN INACTIVE DATA0 SIZE=0 NEXT=3 */
|
|
|
80 |
DIEPCTL1 = 0x10001800;
|
|
|
81 |
/* EP2 OUT INACTIVE DATA0 SIZE=0 */
|
|
|
82 |
DOEPCTL2 = 0x10000000;
|
|
|
83 |
/* EP3 IN INACTIVE DATA0 SIZE=0 NEXT=0 */
|
|
|
84 |
DIEPCTL3 = 0x10000000;
|
|
|
85 |
/* EP4 OUT INACTIVE DATA0 SIZE=0 */
|
|
|
86 |
DOEPCTL4 = 0x10000000;
|
|
|
87 |
}
|
|
|
88 |
else
|
|
|
89 |
{
|
|
|
90 |
/* INACTIVE DATA0 */
|
|
|
91 |
DIEPCTL1 = (DIEPCTL1 & ~0x00008000) | 0x10000000;
|
|
|
92 |
DOEPCTL2 = (DOEPCTL2 & ~0x00008000) | 0x10000000;
|
|
|
93 |
DIEPCTL3 = (DIEPCTL3 & ~0x00008000) | 0x10000000;
|
|
|
94 |
DOEPCTL4 = (DOEPCTL4 & ~0x00008000) | 0x10000000;
|
|
|
95 |
}
|
|
|
96 |
DAINTMSK = 0xFFFFFFFF; /* Enable interrupts on all EPs */
|
|
|
97 |
}
|
|
|
98 |
|
|
|
99 |
int usb_drv_request_endpoint(int type, int dir)
|
|
|
100 |
{
|
|
|
101 |
size_t ep;
|
|
|
102 |
int ret = -1;
|
|
|
103 |
|
|
|
104 |
if (dir == USB_DIR_IN) ep = 1;
|
|
|
105 |
else ep = 2;
|
|
|
106 |
|
|
|
107 |
while (ep < 5)
|
|
|
108 |
{
|
|
|
109 |
if (!endpoints[ep].active)
|
|
|
110 |
{
|
|
|
111 |
endpoints[ep].active = true;
|
|
|
112 |
ret = ep | dir;
|
|
|
113 |
uint32_t newbits = (type << 18) | 0x10000000;
|
|
|
114 |
if (dir) DIEPCTL(ep) = (DIEPCTL(ep) & ~0x000C0000) | newbits;
|
|
|
115 |
else DOEPCTL(ep) = (DOEPCTL(ep) & ~0x000C0000) | newbits;
|
|
|
116 |
break;
|
|
|
117 |
}
|
|
|
118 |
ep += 2;
|
|
|
119 |
}
|
|
|
120 |
|
|
|
121 |
return ret;
|
|
|
122 |
}
|
|
|
123 |
|
|
|
124 |
void usb_drv_release_endpoint(int ep)
|
|
|
125 |
{
|
|
|
126 |
ep = ep & 0x7f;
|
|
|
127 |
|
|
|
128 |
if (ep < 1 || ep > USB_NUM_ENDPOINTS) return;
|
|
|
129 |
|
|
|
130 |
endpoints[ep].active = false;
|
|
|
131 |
}
|
|
|
132 |
|
|
|
133 |
static void usb_reset(void)
|
|
|
134 |
{
|
|
|
135 |
volatile int i;
|
|
|
136 |
|
|
|
137 |
DCTL = 0x802; /* Soft Disconnect */
|
|
|
138 |
|
|
|
139 |
OPHYPWR = 0; /* PHY: Power up */
|
| 85 |
theseven |
140 |
OPHYUNK1 = 1;
|
| 15 |
theseven |
141 |
OPHYUNK2 = 0xE3F;
|
| 85 |
theseven |
142 |
OPHYCLK = SYNOPSYSOTG_CLOCK;
|
| 15 |
theseven |
143 |
ORSTCON = 1; /* PHY: Assert Software Reset */
|
|
|
144 |
udelay(10);
|
|
|
145 |
ORSTCON = 0; /* PHY: Deassert Software Reset */
|
|
|
146 |
|
|
|
147 |
GRSTCTL = 1; /* OTG: Assert Software Reset */
|
|
|
148 |
while (GRSTCTL & 1); /* Wait for OTG to ack reset */
|
|
|
149 |
while (!(GRSTCTL & 0x80000000)); /* Wait for OTG AHB master idle */
|
|
|
150 |
|
|
|
151 |
GRXFSIZ = 0x00000200; /* RX FIFO: 512 bytes */
|
|
|
152 |
GNPTXFSIZ = 0x02000200; /* Non-periodic TX FIFO: 512 bytes */
|
| 85 |
theseven |
153 |
GAHBCFG = SYNOPSYSOTG_AHBCFG;
|
| 15 |
theseven |
154 |
GUSBCFG = 0x1408; /* OTG: 16bit PHY and some reserved bits */
|
|
|
155 |
|
|
|
156 |
DCFG = 4; /* Address 0 */
|
|
|
157 |
DCTL = 0x800; /* Soft Reconnect */
|
|
|
158 |
DIEPMSK = 0x0D; /* IN EP interrupt mask */
|
|
|
159 |
DOEPMSK = 0x0D; /* IN EP interrupt mask */
|
|
|
160 |
DAINTMSK = 0xFFFFFFFF; /* Enable interrupts on all endpoints */
|
|
|
161 |
GINTMSK = 0xC3000; /* Interrupt mask: IN event, OUT event, bus reset */
|
|
|
162 |
|
|
|
163 |
reset_endpoints(1);
|
|
|
164 |
}
|
|
|
165 |
|
|
|
166 |
/* IRQ handler */
|
|
|
167 |
void INT_USB_FUNC(void)
|
|
|
168 |
{
|
|
|
169 |
int i;
|
|
|
170 |
uint32_t ints = GINTSTS;
|
|
|
171 |
uint32_t epints;
|
|
|
172 |
if (ints & 0x1000) /* bus reset */
|
|
|
173 |
{
|
|
|
174 |
DCFG = 4; /* Address 0 */
|
|
|
175 |
reset_endpoints(1);
|
|
|
176 |
usb_handle_bus_reset();
|
|
|
177 |
}
|
|
|
178 |
|
|
|
179 |
if (ints & 0x2000) /* enumeration done, we now know the speed */
|
|
|
180 |
{
|
|
|
181 |
/* Set up the maximum packet sizes accordingly */
|
|
|
182 |
uint32_t maxpacket = usb_drv_port_speed() ? 512 : 64;
|
|
|
183 |
DIEPCTL1 = (DIEPCTL1 & ~0x000003FF) | maxpacket;
|
|
|
184 |
DOEPCTL2 = (DOEPCTL2 & ~0x000003FF) | maxpacket;
|
|
|
185 |
DIEPCTL3 = (DIEPCTL3 & ~0x000003FF) | maxpacket;
|
|
|
186 |
DOEPCTL4 = (DOEPCTL4 & ~0x000003FF) | maxpacket;
|
|
|
187 |
}
|
|
|
188 |
|
|
|
189 |
if (ints & 0x40000) /* IN EP event */
|
|
|
190 |
for (i = 0; i < 4; i += i + 1) // 0, 1, 3
|
|
|
191 |
if (epints = DIEPINT(i))
|
|
|
192 |
{
|
|
|
193 |
if (epints & 1) /* Transfer completed */
|
|
|
194 |
{
|
|
|
195 |
invalidate_dcache();
|
|
|
196 |
int bytes = endpoints[i].size - (DIEPTSIZ(i) & 0x3FFFF);
|
|
|
197 |
if (endpoints[i].busy)
|
|
|
198 |
{
|
|
|
199 |
endpoints[i].busy = false;
|
|
|
200 |
endpoints[i].rc = 0;
|
|
|
201 |
endpoints[i].done = true;
|
|
|
202 |
usb_handle_transfer_complete(i, USB_DIR_IN, 0, bytes);
|
|
|
203 |
wakeup_signal(&endpoints[i].complete);
|
|
|
204 |
}
|
|
|
205 |
}
|
|
|
206 |
if (epints & 4) /* AHB error */
|
|
|
207 |
panicf(PANIC_FATAL, "USB: AHB error on IN EP%d", i);
|
|
|
208 |
if (epints & 8) /* Timeout */
|
|
|
209 |
{
|
|
|
210 |
if (endpoints[i].busy)
|
|
|
211 |
{
|
|
|
212 |
endpoints[i].busy = false;
|
|
|
213 |
endpoints[i].rc = 1;
|
|
|
214 |
endpoints[i].done = true;
|
|
|
215 |
wakeup_signal(&endpoints[i].complete);
|
|
|
216 |
}
|
|
|
217 |
}
|
|
|
218 |
DIEPINT(i) = epints;
|
|
|
219 |
}
|
|
|
220 |
|
|
|
221 |
if (ints & 0x80000) /* OUT EP event */
|
|
|
222 |
for (i = 0; i < 5; i += 2) // 0, 2, 4
|
|
|
223 |
if (epints = DOEPINT(i))
|
|
|
224 |
{
|
|
|
225 |
if (epints & 1) /* Transfer completed */
|
|
|
226 |
{
|
|
|
227 |
invalidate_dcache();
|
|
|
228 |
int bytes = endpoints[i].size - (DOEPTSIZ(i) & 0x3FFFF);
|
|
|
229 |
if (endpoints[i].busy)
|
|
|
230 |
{
|
|
|
231 |
endpoints[i].busy = false;
|
|
|
232 |
endpoints[i].rc = 0;
|
|
|
233 |
endpoints[i].done = true;
|
|
|
234 |
usb_handle_transfer_complete(i, USB_DIR_OUT, 0, bytes);
|
|
|
235 |
wakeup_signal(&endpoints[i].complete);
|
|
|
236 |
}
|
|
|
237 |
}
|
|
|
238 |
if (epints & 4) /* AHB error */
|
|
|
239 |
panicf(PANIC_FATAL, "USB: AHB error on OUT EP%d", i);
|
|
|
240 |
if (epints & 8) /* SETUP phase done */
|
|
|
241 |
{
|
|
|
242 |
invalidate_dcache();
|
|
|
243 |
if (i == 0) usb_handle_control_request(&ctrlreq);
|
|
|
244 |
else panicf(PANIC_FATAL, "USB: SETUP done on OUT EP%d!?", i);
|
|
|
245 |
}
|
|
|
246 |
/* Make sure EP0 OUT is set up to accept the next request */
|
|
|
247 |
if (!i)
|
|
|
248 |
{
|
|
|
249 |
DOEPTSIZ0 = 0x20080040;
|
|
|
250 |
DOEPDMA0 = (uint32_t)&ctrlreq;
|
|
|
251 |
DOEPCTL0 |= 0x84000000;
|
|
|
252 |
}
|
|
|
253 |
DOEPINT(i) = epints;
|
|
|
254 |
}
|
|
|
255 |
|
|
|
256 |
GINTSTS = ints;
|
|
|
257 |
}
|
|
|
258 |
|
|
|
259 |
void usb_drv_set_address(int address)
|
|
|
260 |
{
|
|
|
261 |
DCFG = (DCFG & ~0x7F0) | (address << 4);
|
|
|
262 |
}
|
|
|
263 |
|
| 66 |
theseven |
264 |
static void ep_send(int ep, const void *ptr, int length)
|
| 15 |
theseven |
265 |
{
|
|
|
266 |
endpoints[ep].busy = true;
|
|
|
267 |
endpoints[ep].size = length;
|
|
|
268 |
DIEPCTL(ep) |= 0x8000; /* EPx OUT ACTIVE */
|
|
|
269 |
int blocksize = usb_drv_port_speed() ? 512 : 64;
|
|
|
270 |
int packets = (length + blocksize - 1) / blocksize;
|
|
|
271 |
if (!length)
|
|
|
272 |
{
|
|
|
273 |
DIEPTSIZ(ep) = 1 << 19; /* one empty packet */
|
|
|
274 |
DIEPDMA(ep) = 0x10000000; /* dummy address */
|
|
|
275 |
}
|
|
|
276 |
else
|
|
|
277 |
{
|
|
|
278 |
DIEPTSIZ(ep) = length | (packets << 19);
|
|
|
279 |
DIEPDMA(ep) = (uint32_t)ptr;
|
|
|
280 |
}
|
|
|
281 |
clean_dcache();
|
|
|
282 |
DIEPCTL(ep) |= 0x84000000; /* EPx OUT ENABLE CLEARNAK */
|
|
|
283 |
}
|
|
|
284 |
|
| 66 |
theseven |
285 |
static void ep_recv(int ep, void *ptr, int length)
|
| 15 |
theseven |
286 |
{
|
|
|
287 |
endpoints[ep].busy = true;
|
|
|
288 |
endpoints[ep].size = length;
|
|
|
289 |
DOEPCTL(ep) &= ~0x20000; /* EPx UNSTALL */
|
|
|
290 |
DOEPCTL(ep) |= 0x8000; /* EPx OUT ACTIVE */
|
|
|
291 |
int blocksize = usb_drv_port_speed() ? 512 : 64;
|
|
|
292 |
int packets = (length + blocksize - 1) / blocksize;
|
|
|
293 |
if (!length)
|
|
|
294 |
{
|
|
|
295 |
DOEPTSIZ(ep) = 1 << 19; /* one empty packet */
|
|
|
296 |
DOEPDMA(ep) = 0x10000000; /* dummy address */
|
|
|
297 |
}
|
|
|
298 |
else
|
|
|
299 |
{
|
|
|
300 |
DOEPTSIZ(ep) = length | (packets << 19);
|
|
|
301 |
DOEPDMA(ep) = (uint32_t)ptr;
|
|
|
302 |
}
|
|
|
303 |
clean_dcache();
|
|
|
304 |
DOEPCTL(ep) |= 0x84000000; /* EPx OUT ENABLE CLEARNAK */
|
|
|
305 |
}
|
|
|
306 |
|
|
|
307 |
int usb_drv_send(int endpoint, const void *ptr, int length)
|
|
|
308 |
{
|
|
|
309 |
endpoint &= 0x7f;
|
|
|
310 |
endpoints[endpoint].done = false;
|
|
|
311 |
ep_send(endpoint, ptr, length);
|
|
|
312 |
while (!endpoints[endpoint].done && endpoints[endpoint].busy)
|
|
|
313 |
wakeup_wait(&endpoints[endpoint].complete, TIMEOUT_BLOCK);
|
|
|
314 |
return endpoints[endpoint].rc;
|
|
|
315 |
}
|
|
|
316 |
|
|
|
317 |
int usb_drv_send_nonblocking(int endpoint, const void *ptr, int length)
|
|
|
318 |
{
|
|
|
319 |
ep_send(endpoint & 0x7f, ptr, length);
|
|
|
320 |
return 0;
|
|
|
321 |
}
|
|
|
322 |
|
|
|
323 |
int usb_drv_recv(int endpoint, void* ptr, int length)
|
|
|
324 |
{
|
|
|
325 |
ep_recv(endpoint & 0x7f, ptr, length);
|
|
|
326 |
return 0;
|
|
|
327 |
}
|
|
|
328 |
|
|
|
329 |
void usb_drv_cancel_all_transfers(void)
|
|
|
330 |
{
|
|
|
331 |
uint32_t mode = enter_critical_section();
|
|
|
332 |
reset_endpoints(0);
|
|
|
333 |
leave_critical_section(mode);
|
|
|
334 |
}
|
|
|
335 |
|
|
|
336 |
bool usb_drv_stalled(int endpoint, bool in)
|
|
|
337 |
{
|
|
|
338 |
if (in) return DIEPCTL(endpoint) & 0x00200000 ? true : false;
|
|
|
339 |
else return DOEPCTL(endpoint) & 0x00200000 ? true : false;
|
|
|
340 |
}
|
|
|
341 |
|
|
|
342 |
void usb_drv_stall(int endpoint, bool stall, bool in)
|
|
|
343 |
{
|
|
|
344 |
if (in)
|
|
|
345 |
{
|
|
|
346 |
if (stall) DIEPCTL(endpoint) |= 0x00200000;
|
|
|
347 |
else DIEPCTL(endpoint) &= ~0x00200000;
|
|
|
348 |
}
|
|
|
349 |
else
|
|
|
350 |
{
|
|
|
351 |
if (stall) DOEPCTL(endpoint) |= 0x00200000;
|
|
|
352 |
else DOEPCTL(endpoint) &= ~0x00200000;
|
|
|
353 |
}
|
|
|
354 |
}
|
|
|
355 |
|
| 225 |
theseven |
356 |
void usb_drv_power_up(void)
|
|
|
357 |
{
|
|
|
358 |
/* Enable USB clock */
|
| 221 |
theseven |
359 |
clockgate_enable(CLOCKGATE_USB_1, true);
|
|
|
360 |
clockgate_enable(CLOCKGATE_USB_2, true);
|
| 225 |
theseven |
361 |
PCGCCTL = 0;
|
|
|
362 |
|
|
|
363 |
/* reset the beast */
|
|
|
364 |
usb_reset();
|
|
|
365 |
}
|
|
|
366 |
|
|
|
367 |
void usb_drv_power_down(void)
|
|
|
368 |
{
|
|
|
369 |
DCTL = 0x802; /* Soft Disconnect */
|
|
|
370 |
|
|
|
371 |
ORSTCON = 1; /* Put the PHY into reset (needed to get current down) */
|
|
|
372 |
PCGCCTL = 1; /* Shut down PHY clock */
|
|
|
373 |
OPHYPWR = 0xF; /* PHY: Power down */
|
|
|
374 |
|
| 221 |
theseven |
375 |
clockgate_enable(CLOCKGATE_USB_1, false);
|
|
|
376 |
clockgate_enable(CLOCKGATE_USB_2, false);
|
| 225 |
theseven |
377 |
}
|
|
|
378 |
|
| 221 |
theseven |
379 |
void usb_check_vbus()
|
|
|
380 |
{
|
|
|
381 |
bool oldstate = false;
|
|
|
382 |
while (true)
|
|
|
383 |
{
|
|
|
384 |
sleep(200000);
|
|
|
385 |
bool newstate = vbus_state();
|
|
|
386 |
if (oldstate != newstate)
|
|
|
387 |
{
|
|
|
388 |
if (newstate) usb_drv_power_up();
|
|
|
389 |
else usb_drv_power_down();
|
|
|
390 |
oldstate = newstate;
|
|
|
391 |
}
|
|
|
392 |
}
|
|
|
393 |
}
|
|
|
394 |
|
| 15 |
theseven |
395 |
void usb_drv_init(void)
|
|
|
396 |
{
|
|
|
397 |
unsigned int i;
|
|
|
398 |
for (i = 0; i < sizeof(endpoints)/sizeof(struct ep_type); i++)
|
|
|
399 |
wakeup_init(&endpoints[i].complete);
|
|
|
400 |
|
|
|
401 |
/* Enable USB clock */
|
| 87 |
theseven |
402 |
clockgate_enable(CLOCKGATE_USB_1, true);
|
|
|
403 |
clockgate_enable(CLOCKGATE_USB_2, true);
|
| 15 |
theseven |
404 |
PCGCCTL = 0;
|
|
|
405 |
|
|
|
406 |
/* unmask irq */
|
| 85 |
theseven |
407 |
interrupt_enable(IRQ_USB_FUNC, true);
|
| 15 |
theseven |
408 |
|
| 221 |
theseven |
409 |
thread_create("synopsysotg", usb_check_vbus, synopsysotg_stack,
|
|
|
410 |
sizeof(synopsysotg_stack), OS_THREAD, 63, true);
|
|
|
411 |
|
|
|
412 |
usb_drv_power_down();
|
| 15 |
theseven |
413 |
}
|
| 28 |
theseven |
414 |
|
|
|
415 |
int usb_drv_get_max_out_size()
|
|
|
416 |
{
|
|
|
417 |
return usb_drv_port_speed() ? 262144 : 32768;
|
|
|
418 |
}
|
|
|
419 |
|
|
|
420 |
int usb_drv_get_max_in_size()
|
|
|
421 |
{
|
|
|
422 |
return usb_drv_port_speed() ? 262144 : 32768;
|
|
|
423 |
}
|