blob: 8f238023f55290fdda0b3caf93cee282cc0f37b8 [file] [log] [blame]
Brian Swetland3e7e21a2009-01-19 19:41:24 -08001/*
2 * Copyright (c) 2008, Google Inc.
3 * All rights reserved.
4 *
Subbaraman Narayanamurthyd8b7afc2011-06-30 15:42:41 -07005 * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
Shashank Mittal23b8f422010-04-16 19:27:21 -07006 *
Brian Swetland3e7e21a2009-01-19 19:41:24 -08007 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
Amol Jadi4421e652011-06-16 15:00:48 -070014 * the documentation and/or other materials provided with the
Brian Swetland3e7e21a2009-01-19 19:41:24 -080015 * distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
Amol Jadi4421e652011-06-16 15:00:48 -070024 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
Brian Swetland3e7e21a2009-01-19 19:41:24 -080025 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
27 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include <string.h>
32#include <stdlib.h>
33#include <debug.h>
34#include <platform/iomap.h>
35#include <platform/irqs.h>
36#include <platform/interrupts.h>
37#include <kernel/thread.h>
38#include <reg.h>
39
40#include <dev/udc.h>
41
42#include "hsusb.h"
43
44/* common code - factor out into a shared file */
45
46struct udc_descriptor {
47 struct udc_descriptor *next;
48 unsigned short tag; /* ((TYPE << 8) | NUM) */
49 unsigned short len; /* total length */
50 unsigned char data[0];
51};
52
53struct udc_descriptor *udc_descriptor_alloc(unsigned type, unsigned num, unsigned len)
54{
55 struct udc_descriptor *desc;
56 if ((len > 255) || (len < 2) || (num > 255) || (type > 255))
57 return 0;
58
59 if(!(desc = malloc(sizeof(struct udc_descriptor) + len)))
60 return 0;
61
62 desc->next = 0;
63 desc->tag = (type << 8) | num;
64 desc->len = len;
65 desc->data[0] = len;
66 desc->data[1] = type;
67
68 return desc;
69}
70
71static struct udc_descriptor *desc_list = 0;
72static unsigned next_string_id = 1;
73
74void udc_descriptor_register(struct udc_descriptor *desc)
75{
76 desc->next = desc_list;
77 desc_list = desc;
78}
79
80unsigned udc_string_desc_alloc(const char *str)
81{
82 unsigned len;
83 struct udc_descriptor *desc;
84 unsigned char *data;
85
86 if (next_string_id > 255)
87 return 0;
88
89 if (!str)
90 return 0;
91
92 len = strlen(str);
93 desc = udc_descriptor_alloc(TYPE_STRING, next_string_id, len * 2 + 2);
94 if (!desc)
95 return 0;
96 next_string_id++;
97
98 /* expand ascii string to utf16 */
99 data = desc->data + 2;
100 while (len-- > 0) {
101 *data++ = *str++;
102 *data++ = 0;
103 }
104
105 udc_descriptor_register(desc);
106 return desc->tag & 0xff;
107}
108
109/* end of common code */
110
Ajay Dudani7d605522010-10-01 19:52:37 -0700111__WEAK void hsusb_clock_init(void)
112{
Greg Griscod6250552011-06-29 14:40:23 -0700113 return;
Ajay Dudani7d605522010-10-01 19:52:37 -0700114}
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800115
116#if 1
117#define DBG(x...) do {} while(0)
118#else
119#define DBG(x...) dprintf(INFO, x)
120#endif
121
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800122#define usb_status(a,b)
123
124struct usb_request {
125 struct udc_request req;
126 struct ept_queue_item *item;
127};
Amol Jadi4421e652011-06-16 15:00:48 -0700128
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800129struct udc_endpoint
130{
131 struct udc_endpoint *next;
132 unsigned bit;
133 struct ept_queue_head *head;
134 struct usb_request *req;
135 unsigned char num;
136 unsigned char in;
137 unsigned short maxpkt;
138};
139
140struct udc_endpoint *ept_list = 0;
141struct ept_queue_head *epts = 0;
142
143static int usb_online = 0;
144static int usb_highspeed = 0;
145
146static struct udc_device *the_device;
147static struct udc_gadget *the_gadget;
148
149struct udc_endpoint *_udc_endpoint_alloc(unsigned num, unsigned in, unsigned max_pkt)
150{
151 struct udc_endpoint *ept;
152 unsigned cfg;
153
154 ept = malloc(sizeof(*ept));
Amol Jadi4421e652011-06-16 15:00:48 -0700155
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800156 ept->maxpkt = max_pkt;
157 ept->num = num;
158 ept->in = !!in;
159 ept->req = 0;
160
161 cfg = CONFIG_MAX_PKT(max_pkt) | CONFIG_ZLT;
162
163 if(ept->in) {
164 ept->bit = EPT_TX(ept->num);
165 } else {
166 ept->bit = EPT_RX(ept->num);
Amol Jadi4421e652011-06-16 15:00:48 -0700167 if(num == 0)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800168 cfg |= CONFIG_IOS;
169 }
170
171 ept->head = epts + (num * 2) + (ept->in);
172 ept->head->config = cfg;
173
174 ept->next = ept_list;
175 ept_list = ept;
Amol Jadi4421e652011-06-16 15:00:48 -0700176
177 DBG("ept%d %s @%p/%p max=%d bit=%x\n",
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800178 num, in ? "in":"out", ept, ept->head, max_pkt, ept->bit);
179
180 return ept;
181}
182
183static unsigned ept_alloc_table = EPT_TX(0) | EPT_RX(0);
184
185struct udc_endpoint *udc_endpoint_alloc(unsigned type, unsigned maxpkt)
186{
187 struct udc_endpoint *ept;
188 unsigned n;
189 unsigned in;
190
191 if (type == UDC_TYPE_BULK_IN) {
192 in = 1;
193 } else if (type == UDC_TYPE_BULK_OUT) {
194 in = 0;
195 } else {
196 return 0;
197 }
198
199 for (n = 1; n < 16; n++) {
200 unsigned bit = in ? EPT_TX(n) : EPT_RX(n);
201 if (ept_alloc_table & bit)
202 continue;
203 ept = _udc_endpoint_alloc(n, in, maxpkt);
204 if (ept)
205 ept_alloc_table |= bit;
206 return ept;
207 }
208 return 0;
209}
210
211void udc_endpoint_free(struct udc_endpoint *ept)
212{
213 /* todo */
214}
215
216static void endpoint_enable(struct udc_endpoint *ept, unsigned yes)
217{
218 unsigned n = readl(USB_ENDPTCTRL(ept->num));
219
220 if(yes) {
221 if(ept->in) {
222 n |= (CTRL_TXE | CTRL_TXR | CTRL_TXT_BULK);
223 } else {
224 n |= (CTRL_RXE | CTRL_RXR | CTRL_RXT_BULK);
225 }
226
227 if(ept->num != 0) {
228 /* XXX should be more dynamic... */
229 if(usb_highspeed) {
230 ept->head->config = CONFIG_MAX_PKT(512) | CONFIG_ZLT;
231 } else {
232 ept->head->config = CONFIG_MAX_PKT(64) | CONFIG_ZLT;
233 }
234 }
235 }
236 writel(n, USB_ENDPTCTRL(ept->num));
237}
238
239struct udc_request *udc_request_alloc(void)
240{
241 struct usb_request *req;
242 req = malloc(sizeof(*req));
243 req->req.buf = 0;
244 req->req.length = 0;
245 req->item = memalign(32, 32);
246 return &req->req;
247}
248
249void udc_request_free(struct udc_request *req)
250{
251 free(req);
252}
253
254int udc_request_queue(struct udc_endpoint *ept, struct udc_request *_req)
255{
256 struct usb_request *req = (struct usb_request *) _req;
257 struct ept_queue_item *item = req->item;
258 unsigned phys = (unsigned) req->req.buf;
Amol Jadi4421e652011-06-16 15:00:48 -0700259
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800260 item->next = TERMINATE;
261 item->info = INFO_BYTES(req->req.length) | INFO_IOC | INFO_ACTIVE;
262 item->page0 = phys;
263 item->page1 = (phys & 0xfffff000) + 0x1000;
264
265 enter_critical_section();
266 ept->head->next = (unsigned) item;
267 ept->head->info = 0;
268 ept->req = req;
269
Amol Jadi4421e652011-06-16 15:00:48 -0700270 arch_clean_invalidate_cache_range(ept, sizeof(struct udc_endpoint));
271 arch_clean_invalidate_cache_range(ept->head, sizeof(struct ept_queue_head));
272 arch_clean_invalidate_cache_range(ept->req, sizeof(struct usb_request));
273 arch_clean_invalidate_cache_range(req->req.buf, req->req.length);
274 arch_clean_invalidate_cache_range(ept->req->item, sizeof(struct ept_queue_item));
275
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800276 DBG("ept%d %s queue req=%p\n",
277 ept->num, ept->in ? "in" : "out", req);
278
279 writel(ept->bit, USB_ENDPTPRIME);
280 exit_critical_section();
281 return 0;
282}
283
284static void handle_ept_complete(struct udc_endpoint *ept)
285{
286 struct ept_queue_item *item;
287 unsigned actual;
288 int status;
289 struct usb_request *req;
Amol Jadi4421e652011-06-16 15:00:48 -0700290
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800291 DBG("ept%d %s complete req=%p\n",
292 ept->num, ept->in ? "in" : "out", ept->req);
Amol Jadi4421e652011-06-16 15:00:48 -0700293
294 arch_clean_invalidate_cache_range(ept, sizeof(struct udc_endpoint));
295 arch_clean_invalidate_cache_range(ept->req, sizeof(struct usb_request));
296
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800297 req = ept->req;
298 if(req) {
299 ept->req = 0;
Amol Jadi4421e652011-06-16 15:00:48 -0700300
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800301 item = req->item;
302
303 /* For some reason we are getting the notification for
304 * transfer completion before the active bit has cleared.
305 * HACK: wait for the ACTIVE bit to clear:
306 */
Amol Jadi4421e652011-06-16 15:00:48 -0700307 do
308 {
309 /* Must clean/invalidate cached item data before checking
310 * the status every time.
311 */
312 arch_clean_invalidate_cache_range(item, sizeof(struct ept_queue_item));
313 } while (readl(&(item->info)) & INFO_ACTIVE);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800314
Amol Jadi4421e652011-06-16 15:00:48 -0700315 arch_clean_invalidate_cache_range(req->req.buf, req->req.length);
316
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800317 if(item->info & 0xff) {
318 actual = 0;
319 status = -1;
320 dprintf(INFO, "EP%d/%s FAIL nfo=%x pg0=%x\n",
321 ept->num, ept->in ? "in" : "out", item->info, item->page0);
322 } else {
323 actual = req->req.length - ((item->info >> 16) & 0x7fff);
324 status = 0;
325 }
326 if(req->req.complete)
327 req->req.complete(&req->req, actual, status);
328 }
329}
330
331static const char *reqname(unsigned r)
332{
333 switch(r) {
334 case GET_STATUS: return "GET_STATUS";
335 case CLEAR_FEATURE: return "CLEAR_FEATURE";
336 case SET_FEATURE: return "SET_FEATURE";
337 case SET_ADDRESS: return "SET_ADDRESS";
338 case GET_DESCRIPTOR: return "GET_DESCRIPTOR";
339 case SET_DESCRIPTOR: return "SET_DESCRIPTOR";
340 case GET_CONFIGURATION: return "GET_CONFIGURATION";
341 case SET_CONFIGURATION: return "SET_CONFIGURATION";
342 case GET_INTERFACE: return "GET_INTERFACE";
343 case SET_INTERFACE: return "SET_INTERFACE";
344 default: return "*UNKNOWN*";
345 }
346}
347
348static struct udc_endpoint *ep0in, *ep0out;
349static struct udc_request *ep0req;
350
351static void setup_ack(void)
352{
353 ep0req->complete = 0;
354 ep0req->length = 0;
355 udc_request_queue(ep0in, ep0req);
356}
357
358static void ep0in_complete(struct udc_request *req, unsigned actual, int status)
359{
360 DBG("ep0in_complete %p %d %d\n", req, actual, status);
361 if(status == 0) {
362 req->length = 0;
363 req->complete = 0;
364 udc_request_queue(ep0out, req);
365 }
366}
367
368static void setup_tx(void *buf, unsigned len)
369{
370 DBG("setup_tx %p %d\n", buf, len);
371 memcpy(ep0req->buf, buf, len);
372 ep0req->complete = ep0in_complete;
373 ep0req->length = len;
374 udc_request_queue(ep0in, ep0req);
375}
376
377static unsigned char usb_config_value = 0;
378
379#define SETUP(type,request) (((type) << 8) | (request))
380
Subbaraman Narayanamurthyd8b7afc2011-06-30 15:42:41 -0700381static void set_test_mode(uint16_t value)
382{
383 uint32_t mode;
384 if(value == TEST_PACKET)
385 {
386 dprintf(ALWAYS,"Entering test mode for TST_PKT\n");
387 mode = readl(USB_PORTSC) & (~PORTSC_PTC);
388 writel(mode | PORTSC_PTC_TST_PKT, USB_PORTSC);
389 }
390}
391
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800392static void handle_setup(struct udc_endpoint *ept)
393{
394 struct setup_packet s;
Amol Jadi4421e652011-06-16 15:00:48 -0700395
396 arch_clean_invalidate_cache_range(ept->head->setup_data, sizeof(struct ept_queue_head));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800397 memcpy(&s, ept->head->setup_data, sizeof(s));
398 writel(ept->bit, USB_ENDPTSETUPSTAT);
399
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800400 DBG("handle_setup type=0x%02x req=0x%02x val=%d idx=%d len=%d (%s)\n",
401 s.type, s.request, s.value, s.index, s.length,
402 reqname(s.request));
Amol Jadi4421e652011-06-16 15:00:48 -0700403
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800404 switch (SETUP(s.type,s.request)) {
405 case SETUP(DEVICE_READ, GET_STATUS): {
406 unsigned zero = 0;
407 if (s.length == 2) {
408 setup_tx(&zero, 2);
409 return;
410 }
411 break;
412 }
413 case SETUP(DEVICE_READ, GET_DESCRIPTOR): {
414 struct udc_descriptor *desc;
415 /* usb_highspeed? */
416 for (desc = desc_list; desc; desc = desc->next) {
417 if (desc->tag == s.value) {
418 unsigned len = desc->len;
419 if (len > s.length) len = s.length;
420 setup_tx(desc->data, len);
421 return;
422 }
423 }
424 break;
425 }
426 case SETUP(DEVICE_READ, GET_CONFIGURATION):
427 /* disabling this causes data transaction failures on OSX. Why? */
428 if ((s.value == 0) && (s.index == 0) && (s.length == 1)) {
429 setup_tx(&usb_config_value, 1);
430 return;
431 }
432 break;
433 case SETUP(DEVICE_WRITE, SET_CONFIGURATION):
434 if (s.value == 1) {
435 struct udc_endpoint *ept;
436 /* enable endpoints */
437 for (ept = ept_list; ept; ept = ept->next){
Amol Jadi4421e652011-06-16 15:00:48 -0700438 if (ept->num == 0)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800439 continue;
440 endpoint_enable(ept, s.value);
441 }
442 usb_config_value = 1;
443 the_gadget->notify(the_gadget, UDC_EVENT_ONLINE);
444 } else {
445 writel(0, USB_ENDPTCTRL(1));
446 usb_config_value = 0;
447 the_gadget->notify(the_gadget, UDC_EVENT_OFFLINE);
448 }
449 setup_ack();
450 usb_online = s.value ? 1 : 0;
451 usb_status(s.value ? 1 : 0, usb_highspeed);
452 return;
453 case SETUP(DEVICE_WRITE, SET_ADDRESS):
454 /* write address delayed (will take effect
455 ** after the next IN txn)
456 */
457 writel((s.value << 25) | (1 << 24), USB_DEVICEADDR);
458 setup_ack();
459 return;
460 case SETUP(INTERFACE_WRITE, SET_INTERFACE):
461 /* if we ack this everything hangs */
462 /* per spec, STALL is valid if there is not alt func */
463 goto stall;
Subbaraman Narayanamurthyd8b7afc2011-06-30 15:42:41 -0700464 case SETUP(DEVICE_WRITE, SET_FEATURE):
465 setup_ack();
466 /* TODO: Use s.value and fix byte ordering */
467 set_test_mode(s.index);
468 return;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800469 case SETUP(ENDPOINT_WRITE, CLEAR_FEATURE): {
470 struct udc_endpoint *ept;
471 unsigned num = s.index & 15;
472 unsigned in = !!(s.index & 0x80);
Amol Jadi4421e652011-06-16 15:00:48 -0700473
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800474 if ((s.value == 0) && (s.length == 0)) {
475 DBG("clr feat %d %d\n", num, in);
476 for (ept = ept_list; ept; ept = ept->next) {
477 if ((ept->num == num) && (ept->in == in)) {
478 endpoint_enable(ept, 1);
479 setup_ack();
480 return;
481 }
482 }
483 }
484 break;
485 }
486 }
487
488 dprintf(INFO, "STALL %s %d %d %d %d %d\n",
489 reqname(s.request),
490 s.type, s.request, s.value, s.index, s.length);
491
492stall:
Amol Jadi4421e652011-06-16 15:00:48 -0700493 writel((1<<16) | (1 << 0), USB_ENDPTCTRL(ept->num));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800494}
495
496unsigned ulpi_read(unsigned reg)
497{
498 /* initiate read operation */
499 writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg),
500 USB_ULPI_VIEWPORT);
501
502 /* wait for completion */
503 while(readl(USB_ULPI_VIEWPORT) & ULPI_RUN) ;
Amol Jadi4421e652011-06-16 15:00:48 -0700504
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800505 return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT));
506}
507
508void ulpi_write(unsigned val, unsigned reg)
509{
510 /* initiate write operation */
Amol Jadi4421e652011-06-16 15:00:48 -0700511 writel(ULPI_RUN | ULPI_WRITE |
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800512 ULPI_ADDR(reg) | ULPI_DATA(val),
513 USB_ULPI_VIEWPORT);
514
515 /* wait for completion */
516 while(readl(USB_ULPI_VIEWPORT) & ULPI_RUN) ;
517}
Ajay Dudani5a1e3302009-12-05 13:19:17 -0800518
Shashank Mittal23b8f422010-04-16 19:27:21 -0700519#define USB_CLK 0x00902910
520#define USB_PHY_CLK 0x00902E20
521#define CLK_RESET_ASSERT 0x1
522#define CLK_RESET_DEASSERT 0x0
523#define CLK_RESET(x,y) writel((y), (x));
524
525static int msm_otg_xceiv_reset()
526{
527 CLK_RESET(USB_CLK, CLK_RESET_ASSERT);
528 CLK_RESET(USB_PHY_CLK, CLK_RESET_ASSERT);
529 mdelay(20);
530 CLK_RESET(USB_PHY_CLK, CLK_RESET_DEASSERT);
531 CLK_RESET(USB_CLK, CLK_RESET_DEASSERT);
532 mdelay(20);
533
534 /* select ULPI phy */
535 writel(0x81000000, USB_PORTSC);
536 return 0;
537}
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800538
539void board_usb_init(void);
540void board_ulpi_init(void);
541
Amol Jadi4421e652011-06-16 15:00:48 -0700542int udc_init(struct udc_device *dev)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800543{
Amol Jadi4421e652011-06-16 15:00:48 -0700544 DBG("udc_init():\n");
545
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800546 hsusb_clock_init();
547
Subbaraman Narayanamurthyb0111a12011-02-03 14:24:16 -0800548#ifdef PLATFORM_MSM8X60
Subbaraman Narayanamurthyb0111a12011-02-03 14:24:16 -0800549 /* Configure GPIOs for HS_USB */
550 hsusb_gpio_init();
551#endif
Subbaraman Narayanamurthyb0111a12011-02-03 14:24:16 -0800552
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800553 epts = memalign(4096, 4096);
554
555 dprintf(INFO, "USB init ept @ %p\n", epts);
556 memset(epts, 0, 32 * sizeof(struct ept_queue_head));
Amol Jadi4421e652011-06-16 15:00:48 -0700557 arch_clean_invalidate_cache_range(epts, 32 * sizeof(struct ept_queue_head));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800558
Ajay Dudani232ce812009-12-02 00:14:11 -0800559 //dprintf(INFO, "USB ID %08x\n", readl(USB_ID));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800560// board_usb_init();
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800561
562 /* select ULPI phy */
Shashank Mittal23b8f422010-04-16 19:27:21 -0700563#ifdef PLATFORM_MSM8X60
564 msm_otg_xceiv_reset();
565#else
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800566 writel(0x81000000, USB_PORTSC);
Shashank Mittal23b8f422010-04-16 19:27:21 -0700567#endif
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800568 /* RESET */
569 writel(0x00080002, USB_USBCMD);
570
571 thread_sleep(20);
572
573// board_ulpi_init();
574
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800575 writel((unsigned) epts, USB_ENDPOINTLISTADDR);
576
577 /* select DEVICE mode */
578 writel(0x02, USB_USBMODE);
579
580 writel(0xffffffff, USB_ENDPTFLUSH);
581 thread_sleep(20);
582
583 ep0out = _udc_endpoint_alloc(0, 0, 64);
584 ep0in = _udc_endpoint_alloc(0, 1, 64);
585 ep0req = udc_request_alloc();
586 ep0req->buf = malloc(4096);
587
588 {
589 /* create and register a language table descriptor */
590 /* language 0x0409 is US English */
591 struct udc_descriptor *desc = udc_descriptor_alloc(TYPE_STRING, 0, 4);
592 desc->data[2] = 0x09;
593 desc->data[3] = 0x04;
594 udc_descriptor_register(desc);
595 }
Amol Jadi4421e652011-06-16 15:00:48 -0700596
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800597 the_device = dev;
598 return 0;
599}
600
601enum handler_return udc_interrupt(void *arg)
602{
603 struct udc_endpoint *ept;
604 unsigned ret = INT_NO_RESCHEDULE;
605 unsigned n = readl(USB_USBSTS);
606 writel(n, USB_USBSTS);
Amol Jadi4421e652011-06-16 15:00:48 -0700607
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800608 n &= (STS_SLI | STS_URI | STS_PCI | STS_UI | STS_UEI);
609
Amol Jadi4421e652011-06-16 15:00:48 -0700610 DBG("\nudc_interrupt():\n");
611
612 if (n == 0) {
613 DBG("n = 0\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800614 return ret;
Amol Jadi4421e652011-06-16 15:00:48 -0700615 }
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800616
617 if (n & STS_URI) {
Amol Jadi4421e652011-06-16 15:00:48 -0700618 DBG("STS_URI\n");
619
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800620 writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE);
621 writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT);
622 writel(0xffffffff, USB_ENDPTFLUSH);
623 writel(0, USB_ENDPTCTRL(1));
Amol Jadi4421e652011-06-16 15:00:48 -0700624 dprintf(INFO, "-- reset --\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800625 usb_online = 0;
626 usb_config_value = 0;
627 the_gadget->notify(the_gadget, UDC_EVENT_OFFLINE);
628
629 /* error out any pending reqs */
630 for (ept = ept_list; ept; ept = ept->next) {
631 /* ensure that ept_complete considers
632 * this to be an error state
633 */
634 if (ept->req) {
635 ept->req->item->info = INFO_HALTED;
636 handle_ept_complete(ept);
637 }
638 }
639 usb_status(0, usb_highspeed);
640 }
641 if (n & STS_SLI) {
Ajay Dudani35f686f2011-07-22 13:12:09 -0700642 DBG("-- suspend --\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800643 }
644 if (n & STS_PCI) {
Amol Jadi4421e652011-06-16 15:00:48 -0700645 dprintf(INFO, "-- portchange --\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800646 unsigned spd = (readl(USB_PORTSC) >> 26) & 3;
647 if(spd == 2) {
648 usb_highspeed = 1;
649 } else {
650 usb_highspeed = 0;
651 }
652 }
653 if (n & STS_UEI) {
Amol Jadi4421e652011-06-16 15:00:48 -0700654 DBG("STS_UEI\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800655 dprintf(INFO, "<UEI %x>\n", readl(USB_ENDPTCOMPLETE));
656 }
657#if 0
658 DBG("STS: ");
659 if (n & STS_UEI) DBG("ERROR ");
660 if (n & STS_SLI) DBG("SUSPEND ");
661 if (n & STS_URI) DBG("RESET ");
662 if (n & STS_PCI) DBG("PORTCHANGE ");
663 if (n & STS_UI) DBG("USB ");
664 DBG("\n");
665#endif
666 if ((n & STS_UI) || (n & STS_UEI)) {
Amol Jadi4421e652011-06-16 15:00:48 -0700667 DBG("STS_UI and UEI \n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800668 n = readl(USB_ENDPTSETUPSTAT);
669 if (n & EPT_RX(0)) {
670 handle_setup(ep0out);
671 ret = INT_RESCHEDULE;
672 }
673
674 n = readl(USB_ENDPTCOMPLETE);
675 if (n != 0) {
676 writel(n, USB_ENDPTCOMPLETE);
677 }
678
679 for (ept = ept_list; ept; ept = ept->next){
680 if (n & ept->bit) {
681 handle_ept_complete(ept);
682 ret = INT_RESCHEDULE;
683 }
684 }
685 }
686 return ret;
687}
688
689int udc_register_gadget(struct udc_gadget *gadget)
690{
691 if (the_gadget) {
692 dprintf(CRITICAL, "only one gadget supported\n");
693 return -1;
694 }
695 the_gadget = gadget;
696 return 0;
697}
698
699static void udc_ept_desc_fill(struct udc_endpoint *ept, unsigned char *data)
700{
701 data[0] = 7;
702 data[1] = TYPE_ENDPOINT;
703 data[2] = ept->num | (ept->in ? 0x80 : 0x00);
704 data[3] = 0x02; /* bulk -- the only kind we support */
705 data[4] = ept->maxpkt;
706 data[5] = ept->maxpkt >> 8;
707 data[6] = ept->in ? 0x00 : 0x01;
708}
709
710static unsigned udc_ifc_desc_size(struct udc_gadget *g)
711{
712 return 9 + g->ifc_endpoints * 7;
713}
714
715static void udc_ifc_desc_fill(struct udc_gadget *g, unsigned char *data)
716{
717 unsigned n;
718
719 data[0] = 0x09;
720 data[1] = TYPE_INTERFACE;
721 data[2] = 0x00; /* ifc number */
722 data[3] = 0x00; /* alt number */
723 data[4] = g->ifc_endpoints;
724 data[5] = g->ifc_class;
725 data[6] = g->ifc_subclass;
726 data[7] = g->ifc_protocol;
727 data[8] = udc_string_desc_alloc(g->ifc_string);
728
729 data += 9;
730 for (n = 0; n < g->ifc_endpoints; n++) {
731 udc_ept_desc_fill(g->ept[n], data);
732 data += 7;
733 }
734}
735
736int udc_start(void)
737{
738 struct udc_descriptor *desc;
739 unsigned char *data;
740 unsigned size;
741
Chandan Uddaraju40b227d2010-08-03 19:25:41 -0700742 dprintf(ALWAYS, "udc_start()\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800743
744 if (!the_device) {
745 dprintf(CRITICAL, "udc cannot start before init\n");
746 return -1;
747 }
748 if (!the_gadget) {
749 dprintf(CRITICAL, "udc has no gadget registered\n");
750 return -1;
751 }
752
753 /* create our device descriptor */
754 desc = udc_descriptor_alloc(TYPE_DEVICE, 0, 18);
755 data = desc->data;
Amol Jadica4f4c92011-01-13 20:19:34 -0800756 data[2] = 0x00; /* usb spec minor rev */
757 data[3] = 0x02; /* usb spec major rev */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800758 data[4] = 0x00; /* class */
759 data[5] = 0x00; /* subclass */
760 data[6] = 0x00; /* protocol */
761 data[7] = 0x40; /* max packet size on ept 0 */
762 memcpy(data + 8, &the_device->vendor_id, sizeof(short));
763 memcpy(data + 10, &the_device->product_id, sizeof(short));
764 memcpy(data + 12, &the_device->version_id, sizeof(short));
765 data[14] = udc_string_desc_alloc(the_device->manufacturer);
766 data[15] = udc_string_desc_alloc(the_device->product);
767 data[16] = udc_string_desc_alloc(the_device->serialno);
768 data[17] = 1; /* number of configurations */
769 udc_descriptor_register(desc);
770
771 /* create our configuration descriptor */
772 size = 9 + udc_ifc_desc_size(the_gadget);
773 desc = udc_descriptor_alloc(TYPE_CONFIGURATION, 0, size);
774 data = desc->data;
775 data[0] = 0x09;
776 data[2] = size;
777 data[3] = size >> 8;
778 data[4] = 0x01; /* number of interfaces */
779 data[5] = 0x01; /* configuration value */
780 data[6] = 0x00; /* configuration string */
781 data[7] = 0x80; /* attributes */
782 data[8] = 0x80; /* max power (250ma) -- todo fix this */
783 udc_ifc_desc_fill(the_gadget, data + 9);
784 udc_descriptor_register(desc);
785
Amol Jadica4f4c92011-01-13 20:19:34 -0800786 register_int_handler(INT_USB_HS, udc_interrupt, (void*) 0);
787 writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR);
788 unmask_interrupt(INT_USB_HS);
789
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800790 /* go to RUN mode (D+ pullup enable) */
791 writel(0x00080001, USB_USBCMD);
Amol Jadica4f4c92011-01-13 20:19:34 -0800792
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800793 return 0;
794}
795
796int udc_stop(void)
797{
Shashank Mittal237301c2010-08-24 19:11:46 -0700798 int val;
799 writel(0, USB_USBINTR);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800800 mask_interrupt(INT_USB_HS);
801
802 /* disable pullup */
Shashank Mittald8c42bf2010-06-09 15:44:28 -0700803 writel(0x00080000, USB_USBCMD);
Shashank Mittal237301c2010-08-24 19:11:46 -0700804#ifdef PLATFORM_MSM8X60
805 /* Voting down PLL8 */
806 val = readl(0x009034C0);
807 val &= ~(1<<8);
808 writel(val, 0x009034C0);
809#endif
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800810 thread_sleep(10);
811
812 return 0;
813}
814