blob: 60bd326b6e4068363e6e99ad6bb5f9fdba3408a2 [file] [log] [blame]
Brian Swetland3e7e21a2009-01-19 19:41:24 -08001/*
2 * Copyright (c) 2008, Google Inc.
3 * All rights reserved.
4 *
Shashank Mittal23b8f422010-04-16 19:27:21 -07005 * Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved.
6 *
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
14 * the documentation and/or other materials provided with the
15 * 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
24 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25 * 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
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -080044int charger_usb_disconnected(void);
45int charger_usb_i(unsigned current);
46int charger_usb_is_pc_connected(void);
47int charger_usb_is_charger_connected(void);
48
Brian Swetland3e7e21a2009-01-19 19:41:24 -080049/* common code - factor out into a shared file */
50
51struct udc_descriptor {
52 struct udc_descriptor *next;
53 unsigned short tag; /* ((TYPE << 8) | NUM) */
54 unsigned short len; /* total length */
55 unsigned char data[0];
56};
57
58struct udc_descriptor *udc_descriptor_alloc(unsigned type, unsigned num, unsigned len)
59{
60 struct udc_descriptor *desc;
61 if ((len > 255) || (len < 2) || (num > 255) || (type > 255))
62 return 0;
63
64 if(!(desc = malloc(sizeof(struct udc_descriptor) + len)))
65 return 0;
66
67 desc->next = 0;
68 desc->tag = (type << 8) | num;
69 desc->len = len;
70 desc->data[0] = len;
71 desc->data[1] = type;
72
73 return desc;
74}
75
76static struct udc_descriptor *desc_list = 0;
77static unsigned next_string_id = 1;
78
79void udc_descriptor_register(struct udc_descriptor *desc)
80{
81 desc->next = desc_list;
82 desc_list = desc;
83}
84
85unsigned udc_string_desc_alloc(const char *str)
86{
87 unsigned len;
88 struct udc_descriptor *desc;
89 unsigned char *data;
90
91 if (next_string_id > 255)
92 return 0;
93
94 if (!str)
95 return 0;
96
97 len = strlen(str);
98 desc = udc_descriptor_alloc(TYPE_STRING, next_string_id, len * 2 + 2);
99 if (!desc)
100 return 0;
101 next_string_id++;
102
103 /* expand ascii string to utf16 */
104 data = desc->data + 2;
105 while (len-- > 0) {
106 *data++ = *str++;
107 *data++ = 0;
108 }
109
110 udc_descriptor_register(desc);
111 return desc->tag & 0xff;
112}
113
114/* end of common code */
115
Ajay Dudani7d605522010-10-01 19:52:37 -0700116__WEAK void hsusb_clock_init(void)
117{
118 return 0;
119}
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800120
121#if 1
122#define DBG(x...) do {} while(0)
123#else
124#define DBG(x...) dprintf(INFO, x)
125#endif
126
127#define DBG1(x...) dprintf(INFO, x)
128
129#define usb_status(a,b)
130
131struct usb_request {
132 struct udc_request req;
133 struct ept_queue_item *item;
134};
135
136struct udc_endpoint
137{
138 struct udc_endpoint *next;
139 unsigned bit;
140 struct ept_queue_head *head;
141 struct usb_request *req;
142 unsigned char num;
143 unsigned char in;
144 unsigned short maxpkt;
145};
146
147struct udc_endpoint *ept_list = 0;
148struct ept_queue_head *epts = 0;
149
150static int usb_online = 0;
151static int usb_highspeed = 0;
152
153static struct udc_device *the_device;
154static struct udc_gadget *the_gadget;
155
156struct udc_endpoint *_udc_endpoint_alloc(unsigned num, unsigned in, unsigned max_pkt)
157{
158 struct udc_endpoint *ept;
159 unsigned cfg;
160
161 ept = malloc(sizeof(*ept));
162
163 ept->maxpkt = max_pkt;
164 ept->num = num;
165 ept->in = !!in;
166 ept->req = 0;
167
168 cfg = CONFIG_MAX_PKT(max_pkt) | CONFIG_ZLT;
169
170 if(ept->in) {
171 ept->bit = EPT_TX(ept->num);
172 } else {
173 ept->bit = EPT_RX(ept->num);
174 if(num == 0)
175 cfg |= CONFIG_IOS;
176 }
177
178 ept->head = epts + (num * 2) + (ept->in);
179 ept->head->config = cfg;
180
181 ept->next = ept_list;
182 ept_list = ept;
183
184// arch_clean_invalidate_cache_range(ept->head, 64);
185 DBG("ept%d %s @%p/%p max=%d bit=%x\n",
186 num, in ? "in":"out", ept, ept->head, max_pkt, ept->bit);
187
188 return ept;
189}
190
191static unsigned ept_alloc_table = EPT_TX(0) | EPT_RX(0);
192
193struct udc_endpoint *udc_endpoint_alloc(unsigned type, unsigned maxpkt)
194{
195 struct udc_endpoint *ept;
196 unsigned n;
197 unsigned in;
198
199 if (type == UDC_TYPE_BULK_IN) {
200 in = 1;
201 } else if (type == UDC_TYPE_BULK_OUT) {
202 in = 0;
203 } else {
204 return 0;
205 }
206
207 for (n = 1; n < 16; n++) {
208 unsigned bit = in ? EPT_TX(n) : EPT_RX(n);
209 if (ept_alloc_table & bit)
210 continue;
211 ept = _udc_endpoint_alloc(n, in, maxpkt);
212 if (ept)
213 ept_alloc_table |= bit;
214 return ept;
215 }
216 return 0;
217}
218
219void udc_endpoint_free(struct udc_endpoint *ept)
220{
221 /* todo */
222}
223
224static void endpoint_enable(struct udc_endpoint *ept, unsigned yes)
225{
226 unsigned n = readl(USB_ENDPTCTRL(ept->num));
227
228 if(yes) {
229 if(ept->in) {
230 n |= (CTRL_TXE | CTRL_TXR | CTRL_TXT_BULK);
231 } else {
232 n |= (CTRL_RXE | CTRL_RXR | CTRL_RXT_BULK);
233 }
234
235 if(ept->num != 0) {
236 /* XXX should be more dynamic... */
237 if(usb_highspeed) {
238 ept->head->config = CONFIG_MAX_PKT(512) | CONFIG_ZLT;
239 } else {
240 ept->head->config = CONFIG_MAX_PKT(64) | CONFIG_ZLT;
241 }
242 }
243 }
244 writel(n, USB_ENDPTCTRL(ept->num));
245}
246
247struct udc_request *udc_request_alloc(void)
248{
249 struct usb_request *req;
250 req = malloc(sizeof(*req));
251 req->req.buf = 0;
252 req->req.length = 0;
253 req->item = memalign(32, 32);
254 return &req->req;
255}
256
257void udc_request_free(struct udc_request *req)
258{
259 free(req);
260}
261
262int udc_request_queue(struct udc_endpoint *ept, struct udc_request *_req)
263{
264 struct usb_request *req = (struct usb_request *) _req;
265 struct ept_queue_item *item = req->item;
266 unsigned phys = (unsigned) req->req.buf;
267
268 item->next = TERMINATE;
269 item->info = INFO_BYTES(req->req.length) | INFO_IOC | INFO_ACTIVE;
270 item->page0 = phys;
271 item->page1 = (phys & 0xfffff000) + 0x1000;
272
273 enter_critical_section();
274 ept->head->next = (unsigned) item;
275 ept->head->info = 0;
276 ept->req = req;
277
278// arch_clean_invalidate_cache_range(item, 32);
279// arch_clean_invalidate_cache_range(ept->head, 64);
280// arch_clean_invalidate_cache_range(req->req.buf, req->req.length);
281 DBG("ept%d %s queue req=%p\n",
282 ept->num, ept->in ? "in" : "out", req);
283
284 writel(ept->bit, USB_ENDPTPRIME);
285 exit_critical_section();
286 return 0;
287}
288
289static void handle_ept_complete(struct udc_endpoint *ept)
290{
291 struct ept_queue_item *item;
292 unsigned actual;
293 int status;
294 struct usb_request *req;
295
296 DBG("ept%d %s complete req=%p\n",
297 ept->num, ept->in ? "in" : "out", ept->req);
298
299 req = ept->req;
300 if(req) {
301 ept->req = 0;
302
303 item = req->item;
304
305 /* For some reason we are getting the notification for
306 * transfer completion before the active bit has cleared.
307 * HACK: wait for the ACTIVE bit to clear:
308 */
309 while (readl(&(item->info)) & INFO_ACTIVE) ;
310
311// arch_clean_invalidate_cache_range(item, 32);
312// arch_clean_invalidate_cache_range(req->req.buf, req->req.length);
313
314 if(item->info & 0xff) {
315 actual = 0;
316 status = -1;
317 dprintf(INFO, "EP%d/%s FAIL nfo=%x pg0=%x\n",
318 ept->num, ept->in ? "in" : "out", item->info, item->page0);
319 } else {
320 actual = req->req.length - ((item->info >> 16) & 0x7fff);
321 status = 0;
322 }
323 if(req->req.complete)
324 req->req.complete(&req->req, actual, status);
325 }
326}
327
328static const char *reqname(unsigned r)
329{
330 switch(r) {
331 case GET_STATUS: return "GET_STATUS";
332 case CLEAR_FEATURE: return "CLEAR_FEATURE";
333 case SET_FEATURE: return "SET_FEATURE";
334 case SET_ADDRESS: return "SET_ADDRESS";
335 case GET_DESCRIPTOR: return "GET_DESCRIPTOR";
336 case SET_DESCRIPTOR: return "SET_DESCRIPTOR";
337 case GET_CONFIGURATION: return "GET_CONFIGURATION";
338 case SET_CONFIGURATION: return "SET_CONFIGURATION";
339 case GET_INTERFACE: return "GET_INTERFACE";
340 case SET_INTERFACE: return "SET_INTERFACE";
341 default: return "*UNKNOWN*";
342 }
343}
344
345static struct udc_endpoint *ep0in, *ep0out;
346static struct udc_request *ep0req;
347
348static void setup_ack(void)
349{
350 ep0req->complete = 0;
351 ep0req->length = 0;
352 udc_request_queue(ep0in, ep0req);
353}
354
355static void ep0in_complete(struct udc_request *req, unsigned actual, int status)
356{
357 DBG("ep0in_complete %p %d %d\n", req, actual, status);
358 if(status == 0) {
359 req->length = 0;
360 req->complete = 0;
361 udc_request_queue(ep0out, req);
362 }
363}
364
365static void setup_tx(void *buf, unsigned len)
366{
367 DBG("setup_tx %p %d\n", buf, len);
368 memcpy(ep0req->buf, buf, len);
369 ep0req->complete = ep0in_complete;
370 ep0req->length = len;
371 udc_request_queue(ep0in, ep0req);
372}
373
374static unsigned char usb_config_value = 0;
375
376#define SETUP(type,request) (((type) << 8) | (request))
377
378static void handle_setup(struct udc_endpoint *ept)
379{
380 struct setup_packet s;
381
382 memcpy(&s, ept->head->setup_data, sizeof(s));
383 writel(ept->bit, USB_ENDPTSETUPSTAT);
384
385#if 0
386 DBG("handle_setup type=0x%02x req=0x%02x val=%d idx=%d len=%d (%s)\n",
387 s.type, s.request, s.value, s.index, s.length,
388 reqname(s.request));
389#endif
390 switch (SETUP(s.type,s.request)) {
391 case SETUP(DEVICE_READ, GET_STATUS): {
392 unsigned zero = 0;
393 if (s.length == 2) {
394 setup_tx(&zero, 2);
395 return;
396 }
397 break;
398 }
399 case SETUP(DEVICE_READ, GET_DESCRIPTOR): {
400 struct udc_descriptor *desc;
401 /* usb_highspeed? */
402 for (desc = desc_list; desc; desc = desc->next) {
403 if (desc->tag == s.value) {
404 unsigned len = desc->len;
405 if (len > s.length) len = s.length;
406 setup_tx(desc->data, len);
407 return;
408 }
409 }
410 break;
411 }
412 case SETUP(DEVICE_READ, GET_CONFIGURATION):
413 /* disabling this causes data transaction failures on OSX. Why? */
414 if ((s.value == 0) && (s.index == 0) && (s.length == 1)) {
415 setup_tx(&usb_config_value, 1);
416 return;
417 }
418 break;
419 case SETUP(DEVICE_WRITE, SET_CONFIGURATION):
420 if (s.value == 1) {
421 struct udc_endpoint *ept;
422 /* enable endpoints */
423 for (ept = ept_list; ept; ept = ept->next){
424 if (ept->num == 0)
425 continue;
426 endpoint_enable(ept, s.value);
427 }
428 usb_config_value = 1;
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -0800429#ifdef ENABLE_BATTERY_CHARGING
430 if(HOST_CHARGER == TRUE) {
431 charger_usb_i(500);
432 }
433#endif
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800434 the_gadget->notify(the_gadget, UDC_EVENT_ONLINE);
435 } else {
436 writel(0, USB_ENDPTCTRL(1));
437 usb_config_value = 0;
438 the_gadget->notify(the_gadget, UDC_EVENT_OFFLINE);
439 }
440 setup_ack();
441 usb_online = s.value ? 1 : 0;
442 usb_status(s.value ? 1 : 0, usb_highspeed);
443 return;
444 case SETUP(DEVICE_WRITE, SET_ADDRESS):
445 /* write address delayed (will take effect
446 ** after the next IN txn)
447 */
448 writel((s.value << 25) | (1 << 24), USB_DEVICEADDR);
449 setup_ack();
450 return;
451 case SETUP(INTERFACE_WRITE, SET_INTERFACE):
452 /* if we ack this everything hangs */
453 /* per spec, STALL is valid if there is not alt func */
454 goto stall;
455 case SETUP(ENDPOINT_WRITE, CLEAR_FEATURE): {
456 struct udc_endpoint *ept;
457 unsigned num = s.index & 15;
458 unsigned in = !!(s.index & 0x80);
459
460 if ((s.value == 0) && (s.length == 0)) {
461 DBG("clr feat %d %d\n", num, in);
462 for (ept = ept_list; ept; ept = ept->next) {
463 if ((ept->num == num) && (ept->in == in)) {
464 endpoint_enable(ept, 1);
465 setup_ack();
466 return;
467 }
468 }
469 }
470 break;
471 }
472 }
473
474 dprintf(INFO, "STALL %s %d %d %d %d %d\n",
475 reqname(s.request),
476 s.type, s.request, s.value, s.index, s.length);
477
478stall:
479 writel((1<<16) | (1 << 0), USB_ENDPTCTRL(ept->num));
480}
481
482unsigned ulpi_read(unsigned reg)
483{
484 /* initiate read operation */
485 writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg),
486 USB_ULPI_VIEWPORT);
487
488 /* wait for completion */
489 while(readl(USB_ULPI_VIEWPORT) & ULPI_RUN) ;
490
491 return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT));
492}
493
494void ulpi_write(unsigned val, unsigned reg)
495{
496 /* initiate write operation */
497 writel(ULPI_RUN | ULPI_WRITE |
498 ULPI_ADDR(reg) | ULPI_DATA(val),
499 USB_ULPI_VIEWPORT);
500
501 /* wait for completion */
502 while(readl(USB_ULPI_VIEWPORT) & ULPI_RUN) ;
503}
Ajay Dudani5a1e3302009-12-05 13:19:17 -0800504
Shashank Mittal23b8f422010-04-16 19:27:21 -0700505#define USB_CLK 0x00902910
506#define USB_PHY_CLK 0x00902E20
507#define CLK_RESET_ASSERT 0x1
508#define CLK_RESET_DEASSERT 0x0
509#define CLK_RESET(x,y) writel((y), (x));
510
511static int msm_otg_xceiv_reset()
512{
513 CLK_RESET(USB_CLK, CLK_RESET_ASSERT);
514 CLK_RESET(USB_PHY_CLK, CLK_RESET_ASSERT);
515 mdelay(20);
516 CLK_RESET(USB_PHY_CLK, CLK_RESET_DEASSERT);
517 CLK_RESET(USB_CLK, CLK_RESET_DEASSERT);
518 mdelay(20);
519
520 /* select ULPI phy */
521 writel(0x81000000, USB_PORTSC);
522 return 0;
523}
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800524
525void board_usb_init(void);
526void board_ulpi_init(void);
527
528int udc_init(struct udc_device *dev)
529{
530 hsusb_clock_init();
531
Subbaraman Narayanamurthyb0111a12011-02-03 14:24:16 -0800532#ifdef PLATFORM_MSM8X60
Subbaraman Narayanamurthyb0111a12011-02-03 14:24:16 -0800533 /* Configure GPIOs for HS_USB */
534 hsusb_gpio_init();
535#endif
Subbaraman Narayanamurthyb0111a12011-02-03 14:24:16 -0800536
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800537 epts = memalign(4096, 4096);
538
539 dprintf(INFO, "USB init ept @ %p\n", epts);
540 memset(epts, 0, 32 * sizeof(struct ept_queue_head));
541
Ajay Dudani232ce812009-12-02 00:14:11 -0800542 //dprintf(INFO, "USB ID %08x\n", readl(USB_ID));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800543// board_usb_init();
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800544
545 /* select ULPI phy */
Shashank Mittal23b8f422010-04-16 19:27:21 -0700546#ifdef PLATFORM_MSM8X60
547 msm_otg_xceiv_reset();
548#else
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800549 writel(0x81000000, USB_PORTSC);
Shashank Mittal23b8f422010-04-16 19:27:21 -0700550#endif
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800551 /* RESET */
552 writel(0x00080002, USB_USBCMD);
553
554 thread_sleep(20);
555
556// board_ulpi_init();
557
558// arch_clean_invalidate_cache_range(epts, 32 * sizeof(struct ept_queue_head));
559 writel((unsigned) epts, USB_ENDPOINTLISTADDR);
560
561 /* select DEVICE mode */
562 writel(0x02, USB_USBMODE);
563
564 writel(0xffffffff, USB_ENDPTFLUSH);
565 thread_sleep(20);
566
567 ep0out = _udc_endpoint_alloc(0, 0, 64);
568 ep0in = _udc_endpoint_alloc(0, 1, 64);
569 ep0req = udc_request_alloc();
570 ep0req->buf = malloc(4096);
571
572 {
573 /* create and register a language table descriptor */
574 /* language 0x0409 is US English */
575 struct udc_descriptor *desc = udc_descriptor_alloc(TYPE_STRING, 0, 4);
576 desc->data[2] = 0x09;
577 desc->data[3] = 0x04;
578 udc_descriptor_register(desc);
579 }
580
581 the_device = dev;
582 return 0;
583}
584
585enum handler_return udc_interrupt(void *arg)
586{
587 struct udc_endpoint *ept;
588 unsigned ret = INT_NO_RESCHEDULE;
589 unsigned n = readl(USB_USBSTS);
590 writel(n, USB_USBSTS);
591
592 n &= (STS_SLI | STS_URI | STS_PCI | STS_UI | STS_UEI);
593
594 if (n == 0)
595 return ret;
596
597 if (n & STS_URI) {
598 writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE);
599 writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT);
600 writel(0xffffffff, USB_ENDPTFLUSH);
601 writel(0, USB_ENDPTCTRL(1));
602 DBG1("-- reset --\n");
603 usb_online = 0;
604 usb_config_value = 0;
605 the_gadget->notify(the_gadget, UDC_EVENT_OFFLINE);
606
607 /* error out any pending reqs */
608 for (ept = ept_list; ept; ept = ept->next) {
609 /* ensure that ept_complete considers
610 * this to be an error state
611 */
612 if (ept->req) {
613 ept->req->item->info = INFO_HALTED;
614 handle_ept_complete(ept);
615 }
616 }
617 usb_status(0, usb_highspeed);
618 }
619 if (n & STS_SLI) {
620 DBG1("-- suspend --\n");
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -0800621#ifdef ENABLE_BATTERY_CHARGING
622 if(HOST_CHARGER == TRUE){
623 charger_usb_i(2);
624 }
625#endif
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800626 }
627 if (n & STS_PCI) {
628 DBG1("-- portchange --\n");
629 unsigned spd = (readl(USB_PORTSC) >> 26) & 3;
630 if(spd == 2) {
631 usb_highspeed = 1;
632 } else {
633 usb_highspeed = 0;
634 }
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -0800635#ifdef ENABLE_BATTERY_CHARGING
636 if(HOST_CHARGER == TRUE && usb_config_value){
637 charger_usb_i(500);
638 }
639 if(HOST_CHARGER == TRUE && !usb_config_value){
640 charger_usb_i(100);
641 }
642#endif
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800643 }
644 if (n & STS_UEI) {
645 dprintf(INFO, "<UEI %x>\n", readl(USB_ENDPTCOMPLETE));
646 }
647#if 0
648 DBG("STS: ");
649 if (n & STS_UEI) DBG("ERROR ");
650 if (n & STS_SLI) DBG("SUSPEND ");
651 if (n & STS_URI) DBG("RESET ");
652 if (n & STS_PCI) DBG("PORTCHANGE ");
653 if (n & STS_UI) DBG("USB ");
654 DBG("\n");
655#endif
656 if ((n & STS_UI) || (n & STS_UEI)) {
657 n = readl(USB_ENDPTSETUPSTAT);
658 if (n & EPT_RX(0)) {
659 handle_setup(ep0out);
660 ret = INT_RESCHEDULE;
661 }
662
663 n = readl(USB_ENDPTCOMPLETE);
664 if (n != 0) {
665 writel(n, USB_ENDPTCOMPLETE);
666 }
667
668 for (ept = ept_list; ept; ept = ept->next){
669 if (n & ept->bit) {
670 handle_ept_complete(ept);
671 ret = INT_RESCHEDULE;
672 }
673 }
674 }
675 return ret;
676}
677
678int udc_register_gadget(struct udc_gadget *gadget)
679{
680 if (the_gadget) {
681 dprintf(CRITICAL, "only one gadget supported\n");
682 return -1;
683 }
684 the_gadget = gadget;
685 return 0;
686}
687
688static void udc_ept_desc_fill(struct udc_endpoint *ept, unsigned char *data)
689{
690 data[0] = 7;
691 data[1] = TYPE_ENDPOINT;
692 data[2] = ept->num | (ept->in ? 0x80 : 0x00);
693 data[3] = 0x02; /* bulk -- the only kind we support */
694 data[4] = ept->maxpkt;
695 data[5] = ept->maxpkt >> 8;
696 data[6] = ept->in ? 0x00 : 0x01;
697}
698
699static unsigned udc_ifc_desc_size(struct udc_gadget *g)
700{
701 return 9 + g->ifc_endpoints * 7;
702}
703
704static void udc_ifc_desc_fill(struct udc_gadget *g, unsigned char *data)
705{
706 unsigned n;
707
708 data[0] = 0x09;
709 data[1] = TYPE_INTERFACE;
710 data[2] = 0x00; /* ifc number */
711 data[3] = 0x00; /* alt number */
712 data[4] = g->ifc_endpoints;
713 data[5] = g->ifc_class;
714 data[6] = g->ifc_subclass;
715 data[7] = g->ifc_protocol;
716 data[8] = udc_string_desc_alloc(g->ifc_string);
717
718 data += 9;
719 for (n = 0; n < g->ifc_endpoints; n++) {
720 udc_ept_desc_fill(g->ept[n], data);
721 data += 7;
722 }
723}
724
725int udc_start(void)
726{
727 struct udc_descriptor *desc;
728 unsigned char *data;
729 unsigned size;
730
Chandan Uddaraju40b227d2010-08-03 19:25:41 -0700731 dprintf(ALWAYS, "udc_start()\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800732
733 if (!the_device) {
734 dprintf(CRITICAL, "udc cannot start before init\n");
735 return -1;
736 }
737 if (!the_gadget) {
738 dprintf(CRITICAL, "udc has no gadget registered\n");
739 return -1;
740 }
741
742 /* create our device descriptor */
743 desc = udc_descriptor_alloc(TYPE_DEVICE, 0, 18);
744 data = desc->data;
Amol Jadica4f4c92011-01-13 20:19:34 -0800745 data[2] = 0x00; /* usb spec minor rev */
746 data[3] = 0x02; /* usb spec major rev */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800747 data[4] = 0x00; /* class */
748 data[5] = 0x00; /* subclass */
749 data[6] = 0x00; /* protocol */
750 data[7] = 0x40; /* max packet size on ept 0 */
751 memcpy(data + 8, &the_device->vendor_id, sizeof(short));
752 memcpy(data + 10, &the_device->product_id, sizeof(short));
753 memcpy(data + 12, &the_device->version_id, sizeof(short));
754 data[14] = udc_string_desc_alloc(the_device->manufacturer);
755 data[15] = udc_string_desc_alloc(the_device->product);
756 data[16] = udc_string_desc_alloc(the_device->serialno);
757 data[17] = 1; /* number of configurations */
758 udc_descriptor_register(desc);
759
760 /* create our configuration descriptor */
761 size = 9 + udc_ifc_desc_size(the_gadget);
762 desc = udc_descriptor_alloc(TYPE_CONFIGURATION, 0, size);
763 data = desc->data;
764 data[0] = 0x09;
765 data[2] = size;
766 data[3] = size >> 8;
767 data[4] = 0x01; /* number of interfaces */
768 data[5] = 0x01; /* configuration value */
769 data[6] = 0x00; /* configuration string */
770 data[7] = 0x80; /* attributes */
771 data[8] = 0x80; /* max power (250ma) -- todo fix this */
772 udc_ifc_desc_fill(the_gadget, data + 9);
773 udc_descriptor_register(desc);
774
Amol Jadica4f4c92011-01-13 20:19:34 -0800775 register_int_handler(INT_USB_HS, udc_interrupt, (void*) 0);
776 writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR);
777 unmask_interrupt(INT_USB_HS);
778
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800779 /* go to RUN mode (D+ pullup enable) */
780 writel(0x00080001, USB_USBCMD);
Amol Jadica4f4c92011-01-13 20:19:34 -0800781
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800782 return 0;
783}
784
785int udc_stop(void)
786{
Shashank Mittal237301c2010-08-24 19:11:46 -0700787 int val;
788 writel(0, USB_USBINTR);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800789 mask_interrupt(INT_USB_HS);
790
791 /* disable pullup */
Shashank Mittald8c42bf2010-06-09 15:44:28 -0700792 writel(0x00080000, USB_USBCMD);
Shashank Mittal237301c2010-08-24 19:11:46 -0700793#ifdef PLATFORM_MSM8X60
794 /* Voting down PLL8 */
795 val = readl(0x009034C0);
796 val &= ~(1<<8);
797 writel(val, 0x009034C0);
798#endif
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800799 thread_sleep(10);
800
801 return 0;
802}
803
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -0800804void usb_stop_charging(unsigned stop_charging)
805{
806 ENABLE_CHARGING = !stop_charging;
807}
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800808
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -0800809static inline unsigned is_usb_charging(void)
810{
811 return ENABLE_CHARGING;
812}
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800813
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -0800814void usb_charger_reset(void)
815{
816 usb_stop_charging(TRUE);
817 charger_usb_disconnected();
818}
819
820/* Charger detection code
821 * Set global flags WALL_CHARGER and
822 * RETURN: type of charger connected
823 * CHG_WALL
824 * CHG_HOST_PC
825 * */
826int usb_chg_detect_type(void)
827{
828 int ret = CHG_UNDEFINED;
829
830 if ((readl(USB_PORTSC) & PORTSC_LS) == PORTSC_LS)
831 {
832 if(charger_usb_is_charger_connected() == TRUE) {
833 WALL_CHARGER = TRUE;
834 HOST_CHARGER = FALSE;
835 charger_usb_i(1500);
836 ret = CHG_WALL;
837 }
838 }
839 else
840 {
841 if(charger_usb_is_pc_connected() == TRUE) {
842 WALL_CHARGER = FALSE;
843 HOST_CHARGER = TRUE;
844 ret = CHG_HOST_PC;
845 }
846 }
847 return ret;
848}
849
850/* check if USB cable is connected
851 *
852 * RETURN: If cable connected return 1
853 * If cable disconnected return 0
854 */
855int is_usb_cable_connected(void)
856{
857 /*Verify B Session Valid Bit to verify vbus status*/
858 if (B_SESSION_VALID & readl(USB_OTGSC)) {
859 return 1;
860 } else {
861 return 0;
862 }
863}
864
Shashank Mittald8c42bf2010-06-09 15:44:28 -0700865/* check for USB connection assuming USB is not pulled up.
866 * It looks for suspend state bit in PORTSC register.
867 *
868 * RETURN: If cable connected return 1
869 * If cable disconnected return 0
870 */
871
872int usb_cable_status(void)
873{
874 unsigned ret = 0;
875 /*Verify B Session Valid Bit to verify vbus status*/
876 writel(0x00080001, USB_USBCMD);
877 thread_sleep(100);
878
879 /*Check reset value of suspend state bit*/
880 if (!((1<<7) & readl(USB_PORTSC))) {
881 ret=1;
882 }
883 udc_stop();
884 return ret;
885}
886
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -0800887void usb_charger_change_state(void)
888{
889 int usb_connected;
890
891 //User might have switched from host pc to wall charger. So keep checking
892 //every time we are in the loop
893
894 if(ENABLE_CHARGING == TRUE)
895 {
896 usb_connected = is_usb_cable_connected();
897
898 if(usb_connected && !charger_connected)
899 {
900 //mdelay(20);
901 thread_sleep(20);
902 /* go to RUN mode (D+ pullup enable) */
903 writel(0x00080001, USB_USBCMD);
904 //mdelay(10);
905 thread_sleep(10);
906 usb_chg_detect_type();
907 charger_connected = TRUE;
908 }
909 else if(!usb_connected && charger_connected)
910 {
911 /* disable D+ pull-up */
912 writel(0x00080000, USB_USBCMD);
913
914 /* Applicable only for 8k target */
915 /*USB Spoof Disconnect Failure
916 Symptoms:
917 In USB peripheral mode, writing '0' to Run/Stop bit of the
918 USBCMD register doesn't cause USB disconnection (spoof disconnect).
919 The PC host doesn't detect the disconnection and the phone remains
920 active on Windows device manager.
921
922 Suggested Workaround:
923 After writing '0' to Run/Stop bit of USBCMD, also write 0x48 to ULPI
924 "Function Control" register. This can be done via the ULPI VIEWPORT
925 register (offset 0x170) by writing a value of 0x60040048.
926 */
927 ulpi_write(0x48, 0x04);
928 //usb_charger_reset();
929 WALL_CHARGER = FALSE;
930 HOST_CHARGER = FALSE;
931 charger_usb_i(0);
932 charger_usb_disconnected();
933 charger_connected = FALSE;
934 }
935 if(WALL_CHARGER == TRUE || HOST_CHARGER == TRUE){
936 //battery_charging_image();
937 }
938 }
939 else if ((readl(USB_USBCMD) & 0x01) == 0){
940 writel(0x00080001, USB_USBCMD);
941 }
942}