blob: 26c4f66d08a723090aea86b8fb5faebc457453cf [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>
Greg Griscod2471ef2011-07-14 13:00:42 -070037#include <platform/timer.h>
Brian Swetland3e7e21a2009-01-19 19:41:24 -080038#include <kernel/thread.h>
39#include <reg.h>
40
41#include <dev/udc.h>
42
43#include "hsusb.h"
44
45/* common code - factor out into a shared file */
46
47struct udc_descriptor {
48 struct udc_descriptor *next;
49 unsigned short tag; /* ((TYPE << 8) | NUM) */
50 unsigned short len; /* total length */
51 unsigned char data[0];
52};
53
54struct udc_descriptor *udc_descriptor_alloc(unsigned type, unsigned num, unsigned len)
55{
56 struct udc_descriptor *desc;
57 if ((len > 255) || (len < 2) || (num > 255) || (type > 255))
58 return 0;
59
60 if(!(desc = malloc(sizeof(struct udc_descriptor) + len)))
61 return 0;
62
63 desc->next = 0;
64 desc->tag = (type << 8) | num;
65 desc->len = len;
66 desc->data[0] = len;
67 desc->data[1] = type;
68
69 return desc;
70}
71
72static struct udc_descriptor *desc_list = 0;
73static unsigned next_string_id = 1;
74
75void udc_descriptor_register(struct udc_descriptor *desc)
76{
77 desc->next = desc_list;
78 desc_list = desc;
79}
80
81unsigned udc_string_desc_alloc(const char *str)
82{
83 unsigned len;
84 struct udc_descriptor *desc;
85 unsigned char *data;
86
87 if (next_string_id > 255)
88 return 0;
89
90 if (!str)
91 return 0;
92
93 len = strlen(str);
94 desc = udc_descriptor_alloc(TYPE_STRING, next_string_id, len * 2 + 2);
95 if (!desc)
96 return 0;
97 next_string_id++;
98
99 /* expand ascii string to utf16 */
100 data = desc->data + 2;
101 while (len-- > 0) {
102 *data++ = *str++;
103 *data++ = 0;
104 }
105
106 udc_descriptor_register(desc);
107 return desc->tag & 0xff;
108}
109
110/* end of common code */
111
Ajay Dudani7d605522010-10-01 19:52:37 -0700112__WEAK void hsusb_clock_init(void)
113{
Greg Griscod6250552011-06-29 14:40:23 -0700114 return;
Ajay Dudani7d605522010-10-01 19:52:37 -0700115}
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800116
117#if 1
118#define DBG(x...) do {} while(0)
119#else
120#define DBG(x...) dprintf(INFO, x)
121#endif
122
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800123#define usb_status(a,b)
124
125struct usb_request {
126 struct udc_request req;
127 struct ept_queue_item *item;
128};
Amol Jadi4421e652011-06-16 15:00:48 -0700129
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800130struct udc_endpoint
131{
132 struct udc_endpoint *next;
133 unsigned bit;
134 struct ept_queue_head *head;
135 struct usb_request *req;
136 unsigned char num;
137 unsigned char in;
138 unsigned short maxpkt;
139};
140
141struct udc_endpoint *ept_list = 0;
142struct ept_queue_head *epts = 0;
143
144static int usb_online = 0;
145static int usb_highspeed = 0;
146
147static struct udc_device *the_device;
148static struct udc_gadget *the_gadget;
149
150struct udc_endpoint *_udc_endpoint_alloc(unsigned num, unsigned in, unsigned max_pkt)
151{
152 struct udc_endpoint *ept;
153 unsigned cfg;
154
155 ept = malloc(sizeof(*ept));
Amol Jadi4421e652011-06-16 15:00:48 -0700156
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800157 ept->maxpkt = max_pkt;
158 ept->num = num;
159 ept->in = !!in;
160 ept->req = 0;
161
162 cfg = CONFIG_MAX_PKT(max_pkt) | CONFIG_ZLT;
163
164 if(ept->in) {
165 ept->bit = EPT_TX(ept->num);
166 } else {
167 ept->bit = EPT_RX(ept->num);
Amol Jadi4421e652011-06-16 15:00:48 -0700168 if(num == 0)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800169 cfg |= CONFIG_IOS;
170 }
171
172 ept->head = epts + (num * 2) + (ept->in);
173 ept->head->config = cfg;
174
175 ept->next = ept_list;
176 ept_list = ept;
Amol Jadi4421e652011-06-16 15:00:48 -0700177
178 DBG("ept%d %s @%p/%p max=%d bit=%x\n",
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800179 num, in ? "in":"out", ept, ept->head, max_pkt, ept->bit);
180
181 return ept;
182}
183
184static unsigned ept_alloc_table = EPT_TX(0) | EPT_RX(0);
185
186struct udc_endpoint *udc_endpoint_alloc(unsigned type, unsigned maxpkt)
187{
188 struct udc_endpoint *ept;
189 unsigned n;
190 unsigned in;
191
192 if (type == UDC_TYPE_BULK_IN) {
193 in = 1;
194 } else if (type == UDC_TYPE_BULK_OUT) {
195 in = 0;
196 } else {
197 return 0;
198 }
199
200 for (n = 1; n < 16; n++) {
201 unsigned bit = in ? EPT_TX(n) : EPT_RX(n);
202 if (ept_alloc_table & bit)
203 continue;
204 ept = _udc_endpoint_alloc(n, in, maxpkt);
205 if (ept)
206 ept_alloc_table |= bit;
207 return ept;
208 }
209 return 0;
210}
211
212void udc_endpoint_free(struct udc_endpoint *ept)
213{
214 /* todo */
215}
216
217static void endpoint_enable(struct udc_endpoint *ept, unsigned yes)
218{
219 unsigned n = readl(USB_ENDPTCTRL(ept->num));
220
221 if(yes) {
222 if(ept->in) {
223 n |= (CTRL_TXE | CTRL_TXR | CTRL_TXT_BULK);
224 } else {
225 n |= (CTRL_RXE | CTRL_RXR | CTRL_RXT_BULK);
226 }
227
228 if(ept->num != 0) {
229 /* XXX should be more dynamic... */
230 if(usb_highspeed) {
231 ept->head->config = CONFIG_MAX_PKT(512) | CONFIG_ZLT;
232 } else {
233 ept->head->config = CONFIG_MAX_PKT(64) | CONFIG_ZLT;
234 }
235 }
236 }
237 writel(n, USB_ENDPTCTRL(ept->num));
238}
239
240struct udc_request *udc_request_alloc(void)
241{
242 struct usb_request *req;
243 req = malloc(sizeof(*req));
244 req->req.buf = 0;
245 req->req.length = 0;
246 req->item = memalign(32, 32);
247 return &req->req;
248}
249
250void udc_request_free(struct udc_request *req)
251{
252 free(req);
253}
254
255int udc_request_queue(struct udc_endpoint *ept, struct udc_request *_req)
256{
257 struct usb_request *req = (struct usb_request *) _req;
258 struct ept_queue_item *item = req->item;
259 unsigned phys = (unsigned) req->req.buf;
Amol Jadi4421e652011-06-16 15:00:48 -0700260
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800261 item->next = TERMINATE;
262 item->info = INFO_BYTES(req->req.length) | INFO_IOC | INFO_ACTIVE;
263 item->page0 = phys;
264 item->page1 = (phys & 0xfffff000) + 0x1000;
265
266 enter_critical_section();
267 ept->head->next = (unsigned) item;
268 ept->head->info = 0;
269 ept->req = req;
270
Greg Grisco1073a5e2011-07-28 18:59:18 -0700271 arch_clean_invalidate_cache_range((addr_t) ept, sizeof(struct udc_endpoint));
272 arch_clean_invalidate_cache_range((addr_t) ept->head, sizeof(struct ept_queue_head));
273 arch_clean_invalidate_cache_range((addr_t) ept->req, sizeof(struct usb_request));
274 arch_clean_invalidate_cache_range((addr_t) req->req.buf, req->req.length);
275 arch_clean_invalidate_cache_range((addr_t) ept->req->item, sizeof(struct ept_queue_item));
Amol Jadi4421e652011-06-16 15:00:48 -0700276
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800277 DBG("ept%d %s queue req=%p\n",
278 ept->num, ept->in ? "in" : "out", req);
279
280 writel(ept->bit, USB_ENDPTPRIME);
281 exit_critical_section();
282 return 0;
283}
284
285static void handle_ept_complete(struct udc_endpoint *ept)
286{
287 struct ept_queue_item *item;
288 unsigned actual;
289 int status;
290 struct usb_request *req;
Amol Jadi4421e652011-06-16 15:00:48 -0700291
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800292 DBG("ept%d %s complete req=%p\n",
293 ept->num, ept->in ? "in" : "out", ept->req);
Amol Jadi4421e652011-06-16 15:00:48 -0700294
Greg Grisco1073a5e2011-07-28 18:59:18 -0700295 arch_clean_invalidate_cache_range((addr_t) ept, sizeof(struct udc_endpoint));
296 arch_clean_invalidate_cache_range((addr_t) ept->req, sizeof(struct usb_request));
Amol Jadi4421e652011-06-16 15:00:48 -0700297
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800298 req = ept->req;
299 if(req) {
300 ept->req = 0;
Amol Jadi4421e652011-06-16 15:00:48 -0700301
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800302 item = req->item;
303
304 /* For some reason we are getting the notification for
305 * transfer completion before the active bit has cleared.
306 * HACK: wait for the ACTIVE bit to clear:
307 */
Amol Jadi4421e652011-06-16 15:00:48 -0700308 do
309 {
310 /* Must clean/invalidate cached item data before checking
311 * the status every time.
312 */
Greg Grisco1073a5e2011-07-28 18:59:18 -0700313 arch_clean_invalidate_cache_range((addr_t) item, sizeof(struct ept_queue_item));
Amol Jadi4421e652011-06-16 15:00:48 -0700314 } while (readl(&(item->info)) & INFO_ACTIVE);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800315
Greg Grisco1073a5e2011-07-28 18:59:18 -0700316 arch_clean_invalidate_cache_range((addr_t) req->req.buf, req->req.length);
Amol Jadi4421e652011-06-16 15:00:48 -0700317
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800318 if(item->info & 0xff) {
319 actual = 0;
320 status = -1;
321 dprintf(INFO, "EP%d/%s FAIL nfo=%x pg0=%x\n",
322 ept->num, ept->in ? "in" : "out", item->info, item->page0);
323 } else {
324 actual = req->req.length - ((item->info >> 16) & 0x7fff);
325 status = 0;
326 }
327 if(req->req.complete)
328 req->req.complete(&req->req, actual, status);
329 }
330}
331
332static const char *reqname(unsigned r)
333{
334 switch(r) {
335 case GET_STATUS: return "GET_STATUS";
336 case CLEAR_FEATURE: return "CLEAR_FEATURE";
337 case SET_FEATURE: return "SET_FEATURE";
338 case SET_ADDRESS: return "SET_ADDRESS";
339 case GET_DESCRIPTOR: return "GET_DESCRIPTOR";
340 case SET_DESCRIPTOR: return "SET_DESCRIPTOR";
341 case GET_CONFIGURATION: return "GET_CONFIGURATION";
342 case SET_CONFIGURATION: return "SET_CONFIGURATION";
343 case GET_INTERFACE: return "GET_INTERFACE";
344 case SET_INTERFACE: return "SET_INTERFACE";
345 default: return "*UNKNOWN*";
346 }
347}
348
349static struct udc_endpoint *ep0in, *ep0out;
350static struct udc_request *ep0req;
351
352static void setup_ack(void)
353{
354 ep0req->complete = 0;
355 ep0req->length = 0;
356 udc_request_queue(ep0in, ep0req);
357}
358
359static void ep0in_complete(struct udc_request *req, unsigned actual, int status)
360{
361 DBG("ep0in_complete %p %d %d\n", req, actual, status);
362 if(status == 0) {
363 req->length = 0;
364 req->complete = 0;
365 udc_request_queue(ep0out, req);
366 }
367}
368
369static void setup_tx(void *buf, unsigned len)
370{
371 DBG("setup_tx %p %d\n", buf, len);
372 memcpy(ep0req->buf, buf, len);
373 ep0req->complete = ep0in_complete;
374 ep0req->length = len;
375 udc_request_queue(ep0in, ep0req);
376}
377
378static unsigned char usb_config_value = 0;
379
380#define SETUP(type,request) (((type) << 8) | (request))
381
Subbaraman Narayanamurthyd8b7afc2011-06-30 15:42:41 -0700382static void set_test_mode(uint16_t value)
383{
384 uint32_t mode;
385 if(value == TEST_PACKET)
386 {
387 dprintf(ALWAYS,"Entering test mode for TST_PKT\n");
388 mode = readl(USB_PORTSC) & (~PORTSC_PTC);
389 writel(mode | PORTSC_PTC_TST_PKT, USB_PORTSC);
390 }
391}
392
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800393static void handle_setup(struct udc_endpoint *ept)
394{
395 struct setup_packet s;
Amol Jadi4421e652011-06-16 15:00:48 -0700396
Greg Grisco1073a5e2011-07-28 18:59:18 -0700397 arch_clean_invalidate_cache_range((addr_t) ept->head->setup_data, sizeof(struct ept_queue_head));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800398 memcpy(&s, ept->head->setup_data, sizeof(s));
399 writel(ept->bit, USB_ENDPTSETUPSTAT);
400
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800401 DBG("handle_setup type=0x%02x req=0x%02x val=%d idx=%d len=%d (%s)\n",
402 s.type, s.request, s.value, s.index, s.length,
403 reqname(s.request));
Amol Jadi4421e652011-06-16 15:00:48 -0700404
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800405 switch (SETUP(s.type,s.request)) {
406 case SETUP(DEVICE_READ, GET_STATUS): {
407 unsigned zero = 0;
408 if (s.length == 2) {
409 setup_tx(&zero, 2);
410 return;
411 }
412 break;
413 }
414 case SETUP(DEVICE_READ, GET_DESCRIPTOR): {
415 struct udc_descriptor *desc;
416 /* usb_highspeed? */
417 for (desc = desc_list; desc; desc = desc->next) {
418 if (desc->tag == s.value) {
419 unsigned len = desc->len;
420 if (len > s.length) len = s.length;
421 setup_tx(desc->data, len);
422 return;
423 }
424 }
425 break;
426 }
427 case SETUP(DEVICE_READ, GET_CONFIGURATION):
428 /* disabling this causes data transaction failures on OSX. Why? */
429 if ((s.value == 0) && (s.index == 0) && (s.length == 1)) {
430 setup_tx(&usb_config_value, 1);
431 return;
432 }
433 break;
434 case SETUP(DEVICE_WRITE, SET_CONFIGURATION):
435 if (s.value == 1) {
436 struct udc_endpoint *ept;
437 /* enable endpoints */
438 for (ept = ept_list; ept; ept = ept->next){
Amol Jadi4421e652011-06-16 15:00:48 -0700439 if (ept->num == 0)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800440 continue;
441 endpoint_enable(ept, s.value);
442 }
443 usb_config_value = 1;
444 the_gadget->notify(the_gadget, UDC_EVENT_ONLINE);
445 } else {
446 writel(0, USB_ENDPTCTRL(1));
447 usb_config_value = 0;
448 the_gadget->notify(the_gadget, UDC_EVENT_OFFLINE);
449 }
450 setup_ack();
451 usb_online = s.value ? 1 : 0;
452 usb_status(s.value ? 1 : 0, usb_highspeed);
453 return;
454 case SETUP(DEVICE_WRITE, SET_ADDRESS):
455 /* write address delayed (will take effect
456 ** after the next IN txn)
457 */
458 writel((s.value << 25) | (1 << 24), USB_DEVICEADDR);
459 setup_ack();
460 return;
461 case SETUP(INTERFACE_WRITE, SET_INTERFACE):
462 /* if we ack this everything hangs */
463 /* per spec, STALL is valid if there is not alt func */
464 goto stall;
Subbaraman Narayanamurthyd8b7afc2011-06-30 15:42:41 -0700465 case SETUP(DEVICE_WRITE, SET_FEATURE):
466 setup_ack();
467 /* TODO: Use s.value and fix byte ordering */
468 set_test_mode(s.index);
469 return;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800470 case SETUP(ENDPOINT_WRITE, CLEAR_FEATURE): {
471 struct udc_endpoint *ept;
472 unsigned num = s.index & 15;
473 unsigned in = !!(s.index & 0x80);
Amol Jadi4421e652011-06-16 15:00:48 -0700474
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800475 if ((s.value == 0) && (s.length == 0)) {
476 DBG("clr feat %d %d\n", num, in);
477 for (ept = ept_list; ept; ept = ept->next) {
478 if ((ept->num == num) && (ept->in == in)) {
479 endpoint_enable(ept, 1);
480 setup_ack();
481 return;
482 }
483 }
484 }
485 break;
486 }
487 }
488
489 dprintf(INFO, "STALL %s %d %d %d %d %d\n",
490 reqname(s.request),
491 s.type, s.request, s.value, s.index, s.length);
492
493stall:
Amol Jadi4421e652011-06-16 15:00:48 -0700494 writel((1<<16) | (1 << 0), USB_ENDPTCTRL(ept->num));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800495}
496
497unsigned ulpi_read(unsigned reg)
498{
499 /* initiate read operation */
500 writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg),
501 USB_ULPI_VIEWPORT);
502
503 /* wait for completion */
504 while(readl(USB_ULPI_VIEWPORT) & ULPI_RUN) ;
Amol Jadi4421e652011-06-16 15:00:48 -0700505
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800506 return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT));
507}
508
509void ulpi_write(unsigned val, unsigned reg)
510{
511 /* initiate write operation */
Amol Jadi4421e652011-06-16 15:00:48 -0700512 writel(ULPI_RUN | ULPI_WRITE |
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800513 ULPI_ADDR(reg) | ULPI_DATA(val),
514 USB_ULPI_VIEWPORT);
515
516 /* wait for completion */
517 while(readl(USB_ULPI_VIEWPORT) & ULPI_RUN) ;
518}
Ajay Dudani5a1e3302009-12-05 13:19:17 -0800519
Shashank Mittal23b8f422010-04-16 19:27:21 -0700520#define USB_CLK 0x00902910
521#define USB_PHY_CLK 0x00902E20
522#define CLK_RESET_ASSERT 0x1
523#define CLK_RESET_DEASSERT 0x0
524#define CLK_RESET(x,y) writel((y), (x));
525
526static int msm_otg_xceiv_reset()
527{
528 CLK_RESET(USB_CLK, CLK_RESET_ASSERT);
529 CLK_RESET(USB_PHY_CLK, CLK_RESET_ASSERT);
530 mdelay(20);
531 CLK_RESET(USB_PHY_CLK, CLK_RESET_DEASSERT);
532 CLK_RESET(USB_CLK, CLK_RESET_DEASSERT);
533 mdelay(20);
534
535 /* select ULPI phy */
536 writel(0x81000000, USB_PORTSC);
537 return 0;
538}
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800539
540void board_usb_init(void);
541void board_ulpi_init(void);
542
Amol Jadi4421e652011-06-16 15:00:48 -0700543int udc_init(struct udc_device *dev)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800544{
Amol Jadi4421e652011-06-16 15:00:48 -0700545 DBG("udc_init():\n");
546
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800547 hsusb_clock_init();
548
Subbaraman Narayanamurthyb0111a12011-02-03 14:24:16 -0800549#ifdef PLATFORM_MSM8X60
Subbaraman Narayanamurthyb0111a12011-02-03 14:24:16 -0800550 /* Configure GPIOs for HS_USB */
551 hsusb_gpio_init();
552#endif
Subbaraman Narayanamurthyb0111a12011-02-03 14:24:16 -0800553
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800554 epts = memalign(4096, 4096);
555
556 dprintf(INFO, "USB init ept @ %p\n", epts);
557 memset(epts, 0, 32 * sizeof(struct ept_queue_head));
Greg Grisco1073a5e2011-07-28 18:59:18 -0700558 arch_clean_invalidate_cache_range((addr_t) epts, 32 * sizeof(struct ept_queue_head));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800559
Ajay Dudani232ce812009-12-02 00:14:11 -0800560 //dprintf(INFO, "USB ID %08x\n", readl(USB_ID));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800561// board_usb_init();
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800562
563 /* select ULPI phy */
Shashank Mittal23b8f422010-04-16 19:27:21 -0700564#ifdef PLATFORM_MSM8X60
565 msm_otg_xceiv_reset();
566#else
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800567 writel(0x81000000, USB_PORTSC);
Shashank Mittal23b8f422010-04-16 19:27:21 -0700568#endif
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800569 /* RESET */
570 writel(0x00080002, USB_USBCMD);
571
572 thread_sleep(20);
573
574// board_ulpi_init();
575
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800576 writel((unsigned) epts, USB_ENDPOINTLISTADDR);
577
578 /* select DEVICE mode */
579 writel(0x02, USB_USBMODE);
580
581 writel(0xffffffff, USB_ENDPTFLUSH);
582 thread_sleep(20);
583
584 ep0out = _udc_endpoint_alloc(0, 0, 64);
585 ep0in = _udc_endpoint_alloc(0, 1, 64);
586 ep0req = udc_request_alloc();
587 ep0req->buf = malloc(4096);
588
589 {
590 /* create and register a language table descriptor */
591 /* language 0x0409 is US English */
592 struct udc_descriptor *desc = udc_descriptor_alloc(TYPE_STRING, 0, 4);
593 desc->data[2] = 0x09;
594 desc->data[3] = 0x04;
595 udc_descriptor_register(desc);
596 }
Amol Jadi4421e652011-06-16 15:00:48 -0700597
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800598 the_device = dev;
599 return 0;
600}
601
602enum handler_return udc_interrupt(void *arg)
603{
604 struct udc_endpoint *ept;
605 unsigned ret = INT_NO_RESCHEDULE;
606 unsigned n = readl(USB_USBSTS);
607 writel(n, USB_USBSTS);
Amol Jadi4421e652011-06-16 15:00:48 -0700608
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800609 n &= (STS_SLI | STS_URI | STS_PCI | STS_UI | STS_UEI);
610
Amol Jadi4421e652011-06-16 15:00:48 -0700611 DBG("\nudc_interrupt():\n");
612
613 if (n == 0) {
614 DBG("n = 0\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800615 return ret;
Amol Jadi4421e652011-06-16 15:00:48 -0700616 }
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800617
618 if (n & STS_URI) {
Amol Jadi4421e652011-06-16 15:00:48 -0700619 DBG("STS_URI\n");
620
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800621 writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE);
622 writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT);
623 writel(0xffffffff, USB_ENDPTFLUSH);
624 writel(0, USB_ENDPTCTRL(1));
Amol Jadi4421e652011-06-16 15:00:48 -0700625 dprintf(INFO, "-- reset --\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800626 usb_online = 0;
627 usb_config_value = 0;
628 the_gadget->notify(the_gadget, UDC_EVENT_OFFLINE);
629
630 /* error out any pending reqs */
631 for (ept = ept_list; ept; ept = ept->next) {
632 /* ensure that ept_complete considers
633 * this to be an error state
634 */
635 if (ept->req) {
636 ept->req->item->info = INFO_HALTED;
637 handle_ept_complete(ept);
638 }
639 }
640 usb_status(0, usb_highspeed);
641 }
642 if (n & STS_SLI) {
Ajay Dudani35f686f2011-07-22 13:12:09 -0700643 DBG("-- suspend --\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800644 }
645 if (n & STS_PCI) {
Amol Jadi4421e652011-06-16 15:00:48 -0700646 dprintf(INFO, "-- portchange --\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800647 unsigned spd = (readl(USB_PORTSC) >> 26) & 3;
648 if(spd == 2) {
649 usb_highspeed = 1;
650 } else {
651 usb_highspeed = 0;
652 }
653 }
654 if (n & STS_UEI) {
Amol Jadi4421e652011-06-16 15:00:48 -0700655 DBG("STS_UEI\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800656 dprintf(INFO, "<UEI %x>\n", readl(USB_ENDPTCOMPLETE));
657 }
658#if 0
659 DBG("STS: ");
660 if (n & STS_UEI) DBG("ERROR ");
661 if (n & STS_SLI) DBG("SUSPEND ");
662 if (n & STS_URI) DBG("RESET ");
663 if (n & STS_PCI) DBG("PORTCHANGE ");
664 if (n & STS_UI) DBG("USB ");
665 DBG("\n");
666#endif
667 if ((n & STS_UI) || (n & STS_UEI)) {
Amol Jadi4421e652011-06-16 15:00:48 -0700668 DBG("STS_UI and UEI \n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800669 n = readl(USB_ENDPTSETUPSTAT);
670 if (n & EPT_RX(0)) {
671 handle_setup(ep0out);
672 ret = INT_RESCHEDULE;
673 }
674
675 n = readl(USB_ENDPTCOMPLETE);
676 if (n != 0) {
677 writel(n, USB_ENDPTCOMPLETE);
678 }
679
680 for (ept = ept_list; ept; ept = ept->next){
681 if (n & ept->bit) {
682 handle_ept_complete(ept);
683 ret = INT_RESCHEDULE;
684 }
685 }
686 }
687 return ret;
688}
689
690int udc_register_gadget(struct udc_gadget *gadget)
691{
692 if (the_gadget) {
693 dprintf(CRITICAL, "only one gadget supported\n");
694 return -1;
695 }
696 the_gadget = gadget;
697 return 0;
698}
699
700static void udc_ept_desc_fill(struct udc_endpoint *ept, unsigned char *data)
701{
702 data[0] = 7;
703 data[1] = TYPE_ENDPOINT;
704 data[2] = ept->num | (ept->in ? 0x80 : 0x00);
705 data[3] = 0x02; /* bulk -- the only kind we support */
706 data[4] = ept->maxpkt;
707 data[5] = ept->maxpkt >> 8;
708 data[6] = ept->in ? 0x00 : 0x01;
709}
710
711static unsigned udc_ifc_desc_size(struct udc_gadget *g)
712{
713 return 9 + g->ifc_endpoints * 7;
714}
715
716static void udc_ifc_desc_fill(struct udc_gadget *g, unsigned char *data)
717{
718 unsigned n;
719
720 data[0] = 0x09;
721 data[1] = TYPE_INTERFACE;
722 data[2] = 0x00; /* ifc number */
723 data[3] = 0x00; /* alt number */
724 data[4] = g->ifc_endpoints;
725 data[5] = g->ifc_class;
726 data[6] = g->ifc_subclass;
727 data[7] = g->ifc_protocol;
728 data[8] = udc_string_desc_alloc(g->ifc_string);
729
730 data += 9;
731 for (n = 0; n < g->ifc_endpoints; n++) {
732 udc_ept_desc_fill(g->ept[n], data);
733 data += 7;
734 }
735}
736
737int udc_start(void)
738{
739 struct udc_descriptor *desc;
740 unsigned char *data;
741 unsigned size;
742
Chandan Uddaraju40b227d2010-08-03 19:25:41 -0700743 dprintf(ALWAYS, "udc_start()\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800744
745 if (!the_device) {
746 dprintf(CRITICAL, "udc cannot start before init\n");
747 return -1;
748 }
749 if (!the_gadget) {
750 dprintf(CRITICAL, "udc has no gadget registered\n");
751 return -1;
752 }
753
754 /* create our device descriptor */
755 desc = udc_descriptor_alloc(TYPE_DEVICE, 0, 18);
756 data = desc->data;
Amol Jadica4f4c92011-01-13 20:19:34 -0800757 data[2] = 0x00; /* usb spec minor rev */
758 data[3] = 0x02; /* usb spec major rev */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800759 data[4] = 0x00; /* class */
760 data[5] = 0x00; /* subclass */
761 data[6] = 0x00; /* protocol */
762 data[7] = 0x40; /* max packet size on ept 0 */
763 memcpy(data + 8, &the_device->vendor_id, sizeof(short));
764 memcpy(data + 10, &the_device->product_id, sizeof(short));
765 memcpy(data + 12, &the_device->version_id, sizeof(short));
766 data[14] = udc_string_desc_alloc(the_device->manufacturer);
767 data[15] = udc_string_desc_alloc(the_device->product);
768 data[16] = udc_string_desc_alloc(the_device->serialno);
769 data[17] = 1; /* number of configurations */
770 udc_descriptor_register(desc);
771
772 /* create our configuration descriptor */
773 size = 9 + udc_ifc_desc_size(the_gadget);
774 desc = udc_descriptor_alloc(TYPE_CONFIGURATION, 0, size);
775 data = desc->data;
776 data[0] = 0x09;
777 data[2] = size;
778 data[3] = size >> 8;
779 data[4] = 0x01; /* number of interfaces */
780 data[5] = 0x01; /* configuration value */
781 data[6] = 0x00; /* configuration string */
782 data[7] = 0x80; /* attributes */
783 data[8] = 0x80; /* max power (250ma) -- todo fix this */
784 udc_ifc_desc_fill(the_gadget, data + 9);
785 udc_descriptor_register(desc);
786
Amol Jadica4f4c92011-01-13 20:19:34 -0800787 register_int_handler(INT_USB_HS, udc_interrupt, (void*) 0);
788 writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR);
789 unmask_interrupt(INT_USB_HS);
790
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800791 /* go to RUN mode (D+ pullup enable) */
792 writel(0x00080001, USB_USBCMD);
Amol Jadica4f4c92011-01-13 20:19:34 -0800793
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800794 return 0;
795}
796
797int udc_stop(void)
798{
Greg Griscod2471ef2011-07-14 13:00:42 -0700799#ifdef PLATFORM_MSM8X60
Shashank Mittal237301c2010-08-24 19:11:46 -0700800 int val;
Greg Griscod2471ef2011-07-14 13:00:42 -0700801#endif
Shashank Mittal237301c2010-08-24 19:11:46 -0700802 writel(0, USB_USBINTR);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800803 mask_interrupt(INT_USB_HS);
804
805 /* disable pullup */
Shashank Mittald8c42bf2010-06-09 15:44:28 -0700806 writel(0x00080000, USB_USBCMD);
Shashank Mittal237301c2010-08-24 19:11:46 -0700807#ifdef PLATFORM_MSM8X60
808 /* Voting down PLL8 */
809 val = readl(0x009034C0);
810 val &= ~(1<<8);
811 writel(val, 0x009034C0);
812#endif
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800813 thread_sleep(10);
814
815 return 0;
816}
817