blob: 3d953fa4f271d16d43f179228173f6e60171ab73 [file] [log] [blame]
Brian Swetland3e7e21a2009-01-19 19:41:24 -08001/*
2 * Copyright (c) 2008, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <string.h>
30#include <stdlib.h>
31#include <debug.h>
32#include <platform/iomap.h>
33#include <platform/irqs.h>
34#include <platform/interrupts.h>
35#include <kernel/thread.h>
36#include <reg.h>
37
38#include <dev/udc.h>
39
40#include "hsusb.h"
41
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -080042int charger_usb_disconnected(void);
43int charger_usb_i(unsigned current);
44int charger_usb_is_pc_connected(void);
45int charger_usb_is_charger_connected(void);
46
Brian Swetland3e7e21a2009-01-19 19:41:24 -080047/* common code - factor out into a shared file */
48
49struct udc_descriptor {
50 struct udc_descriptor *next;
51 unsigned short tag; /* ((TYPE << 8) | NUM) */
52 unsigned short len; /* total length */
53 unsigned char data[0];
54};
55
56struct udc_descriptor *udc_descriptor_alloc(unsigned type, unsigned num, unsigned len)
57{
58 struct udc_descriptor *desc;
59 if ((len > 255) || (len < 2) || (num > 255) || (type > 255))
60 return 0;
61
62 if(!(desc = malloc(sizeof(struct udc_descriptor) + len)))
63 return 0;
64
65 desc->next = 0;
66 desc->tag = (type << 8) | num;
67 desc->len = len;
68 desc->data[0] = len;
69 desc->data[1] = type;
70
71 return desc;
72}
73
74static struct udc_descriptor *desc_list = 0;
75static unsigned next_string_id = 1;
76
77void udc_descriptor_register(struct udc_descriptor *desc)
78{
79 desc->next = desc_list;
80 desc_list = desc;
81}
82
83unsigned udc_string_desc_alloc(const char *str)
84{
85 unsigned len;
86 struct udc_descriptor *desc;
87 unsigned char *data;
88
89 if (next_string_id > 255)
90 return 0;
91
92 if (!str)
93 return 0;
94
95 len = strlen(str);
96 desc = udc_descriptor_alloc(TYPE_STRING, next_string_id, len * 2 + 2);
97 if (!desc)
98 return 0;
99 next_string_id++;
100
101 /* expand ascii string to utf16 */
102 data = desc->data + 2;
103 while (len-- > 0) {
104 *data++ = *str++;
105 *data++ = 0;
106 }
107
108 udc_descriptor_register(desc);
109 return desc->tag & 0xff;
110}
111
112/* end of common code */
113
114void hsusb_clock_init(void);
115
116#if 1
117#define DBG(x...) do {} while(0)
118#else
119#define DBG(x...) dprintf(INFO, x)
120#endif
121
122#define DBG1(x...) dprintf(INFO, x)
123
124#define usb_status(a,b)
125
126struct usb_request {
127 struct udc_request req;
128 struct ept_queue_item *item;
129};
130
131struct udc_endpoint
132{
133 struct udc_endpoint *next;
134 unsigned bit;
135 struct ept_queue_head *head;
136 struct usb_request *req;
137 unsigned char num;
138 unsigned char in;
139 unsigned short maxpkt;
140};
141
142struct udc_endpoint *ept_list = 0;
143struct ept_queue_head *epts = 0;
144
145static int usb_online = 0;
146static int usb_highspeed = 0;
147
148static struct udc_device *the_device;
149static struct udc_gadget *the_gadget;
150
151struct udc_endpoint *_udc_endpoint_alloc(unsigned num, unsigned in, unsigned max_pkt)
152{
153 struct udc_endpoint *ept;
154 unsigned cfg;
155
156 ept = malloc(sizeof(*ept));
157
158 ept->maxpkt = max_pkt;
159 ept->num = num;
160 ept->in = !!in;
161 ept->req = 0;
162
163 cfg = CONFIG_MAX_PKT(max_pkt) | CONFIG_ZLT;
164
165 if(ept->in) {
166 ept->bit = EPT_TX(ept->num);
167 } else {
168 ept->bit = EPT_RX(ept->num);
169 if(num == 0)
170 cfg |= CONFIG_IOS;
171 }
172
173 ept->head = epts + (num * 2) + (ept->in);
174 ept->head->config = cfg;
175
176 ept->next = ept_list;
177 ept_list = ept;
178
179// arch_clean_invalidate_cache_range(ept->head, 64);
180 DBG("ept%d %s @%p/%p max=%d bit=%x\n",
181 num, in ? "in":"out", ept, ept->head, max_pkt, ept->bit);
182
183 return ept;
184}
185
186static unsigned ept_alloc_table = EPT_TX(0) | EPT_RX(0);
187
188struct udc_endpoint *udc_endpoint_alloc(unsigned type, unsigned maxpkt)
189{
190 struct udc_endpoint *ept;
191 unsigned n;
192 unsigned in;
193
194 if (type == UDC_TYPE_BULK_IN) {
195 in = 1;
196 } else if (type == UDC_TYPE_BULK_OUT) {
197 in = 0;
198 } else {
199 return 0;
200 }
201
202 for (n = 1; n < 16; n++) {
203 unsigned bit = in ? EPT_TX(n) : EPT_RX(n);
204 if (ept_alloc_table & bit)
205 continue;
206 ept = _udc_endpoint_alloc(n, in, maxpkt);
207 if (ept)
208 ept_alloc_table |= bit;
209 return ept;
210 }
211 return 0;
212}
213
214void udc_endpoint_free(struct udc_endpoint *ept)
215{
216 /* todo */
217}
218
219static void endpoint_enable(struct udc_endpoint *ept, unsigned yes)
220{
221 unsigned n = readl(USB_ENDPTCTRL(ept->num));
222
223 if(yes) {
224 if(ept->in) {
225 n |= (CTRL_TXE | CTRL_TXR | CTRL_TXT_BULK);
226 } else {
227 n |= (CTRL_RXE | CTRL_RXR | CTRL_RXT_BULK);
228 }
229
230 if(ept->num != 0) {
231 /* XXX should be more dynamic... */
232 if(usb_highspeed) {
233 ept->head->config = CONFIG_MAX_PKT(512) | CONFIG_ZLT;
234 } else {
235 ept->head->config = CONFIG_MAX_PKT(64) | CONFIG_ZLT;
236 }
237 }
238 }
239 writel(n, USB_ENDPTCTRL(ept->num));
240}
241
242struct udc_request *udc_request_alloc(void)
243{
244 struct usb_request *req;
245 req = malloc(sizeof(*req));
246 req->req.buf = 0;
247 req->req.length = 0;
248 req->item = memalign(32, 32);
249 return &req->req;
250}
251
252void udc_request_free(struct udc_request *req)
253{
254 free(req);
255}
256
257int udc_request_queue(struct udc_endpoint *ept, struct udc_request *_req)
258{
259 struct usb_request *req = (struct usb_request *) _req;
260 struct ept_queue_item *item = req->item;
261 unsigned phys = (unsigned) req->req.buf;
262
263 item->next = TERMINATE;
264 item->info = INFO_BYTES(req->req.length) | INFO_IOC | INFO_ACTIVE;
265 item->page0 = phys;
266 item->page1 = (phys & 0xfffff000) + 0x1000;
267
268 enter_critical_section();
269 ept->head->next = (unsigned) item;
270 ept->head->info = 0;
271 ept->req = req;
272
273// arch_clean_invalidate_cache_range(item, 32);
274// arch_clean_invalidate_cache_range(ept->head, 64);
275// arch_clean_invalidate_cache_range(req->req.buf, req->req.length);
276 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;
290
291 DBG("ept%d %s complete req=%p\n",
292 ept->num, ept->in ? "in" : "out", ept->req);
293
294 req = ept->req;
295 if(req) {
296 ept->req = 0;
297
298 item = req->item;
299
300 /* For some reason we are getting the notification for
301 * transfer completion before the active bit has cleared.
302 * HACK: wait for the ACTIVE bit to clear:
303 */
304 while (readl(&(item->info)) & INFO_ACTIVE) ;
305
306// arch_clean_invalidate_cache_range(item, 32);
307// arch_clean_invalidate_cache_range(req->req.buf, req->req.length);
308
309 if(item->info & 0xff) {
310 actual = 0;
311 status = -1;
312 dprintf(INFO, "EP%d/%s FAIL nfo=%x pg0=%x\n",
313 ept->num, ept->in ? "in" : "out", item->info, item->page0);
314 } else {
315 actual = req->req.length - ((item->info >> 16) & 0x7fff);
316 status = 0;
317 }
318 if(req->req.complete)
319 req->req.complete(&req->req, actual, status);
320 }
321}
322
323static const char *reqname(unsigned r)
324{
325 switch(r) {
326 case GET_STATUS: return "GET_STATUS";
327 case CLEAR_FEATURE: return "CLEAR_FEATURE";
328 case SET_FEATURE: return "SET_FEATURE";
329 case SET_ADDRESS: return "SET_ADDRESS";
330 case GET_DESCRIPTOR: return "GET_DESCRIPTOR";
331 case SET_DESCRIPTOR: return "SET_DESCRIPTOR";
332 case GET_CONFIGURATION: return "GET_CONFIGURATION";
333 case SET_CONFIGURATION: return "SET_CONFIGURATION";
334 case GET_INTERFACE: return "GET_INTERFACE";
335 case SET_INTERFACE: return "SET_INTERFACE";
336 default: return "*UNKNOWN*";
337 }
338}
339
340static struct udc_endpoint *ep0in, *ep0out;
341static struct udc_request *ep0req;
342
343static void setup_ack(void)
344{
345 ep0req->complete = 0;
346 ep0req->length = 0;
347 udc_request_queue(ep0in, ep0req);
348}
349
350static void ep0in_complete(struct udc_request *req, unsigned actual, int status)
351{
352 DBG("ep0in_complete %p %d %d\n", req, actual, status);
353 if(status == 0) {
354 req->length = 0;
355 req->complete = 0;
356 udc_request_queue(ep0out, req);
357 }
358}
359
360static void setup_tx(void *buf, unsigned len)
361{
362 DBG("setup_tx %p %d\n", buf, len);
363 memcpy(ep0req->buf, buf, len);
364 ep0req->complete = ep0in_complete;
365 ep0req->length = len;
366 udc_request_queue(ep0in, ep0req);
367}
368
369static unsigned char usb_config_value = 0;
370
371#define SETUP(type,request) (((type) << 8) | (request))
372
373static void handle_setup(struct udc_endpoint *ept)
374{
375 struct setup_packet s;
376
377 memcpy(&s, ept->head->setup_data, sizeof(s));
378 writel(ept->bit, USB_ENDPTSETUPSTAT);
379
380#if 0
381 DBG("handle_setup type=0x%02x req=0x%02x val=%d idx=%d len=%d (%s)\n",
382 s.type, s.request, s.value, s.index, s.length,
383 reqname(s.request));
384#endif
385 switch (SETUP(s.type,s.request)) {
386 case SETUP(DEVICE_READ, GET_STATUS): {
387 unsigned zero = 0;
388 if (s.length == 2) {
389 setup_tx(&zero, 2);
390 return;
391 }
392 break;
393 }
394 case SETUP(DEVICE_READ, GET_DESCRIPTOR): {
395 struct udc_descriptor *desc;
396 /* usb_highspeed? */
397 for (desc = desc_list; desc; desc = desc->next) {
398 if (desc->tag == s.value) {
399 unsigned len = desc->len;
400 if (len > s.length) len = s.length;
401 setup_tx(desc->data, len);
402 return;
403 }
404 }
405 break;
406 }
407 case SETUP(DEVICE_READ, GET_CONFIGURATION):
408 /* disabling this causes data transaction failures on OSX. Why? */
409 if ((s.value == 0) && (s.index == 0) && (s.length == 1)) {
410 setup_tx(&usb_config_value, 1);
411 return;
412 }
413 break;
414 case SETUP(DEVICE_WRITE, SET_CONFIGURATION):
415 if (s.value == 1) {
416 struct udc_endpoint *ept;
417 /* enable endpoints */
418 for (ept = ept_list; ept; ept = ept->next){
419 if (ept->num == 0)
420 continue;
421 endpoint_enable(ept, s.value);
422 }
423 usb_config_value = 1;
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -0800424#ifdef ENABLE_BATTERY_CHARGING
425 if(HOST_CHARGER == TRUE) {
426 charger_usb_i(500);
427 }
428#endif
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800429 the_gadget->notify(the_gadget, UDC_EVENT_ONLINE);
430 } else {
431 writel(0, USB_ENDPTCTRL(1));
432 usb_config_value = 0;
433 the_gadget->notify(the_gadget, UDC_EVENT_OFFLINE);
434 }
435 setup_ack();
436 usb_online = s.value ? 1 : 0;
437 usb_status(s.value ? 1 : 0, usb_highspeed);
438 return;
439 case SETUP(DEVICE_WRITE, SET_ADDRESS):
440 /* write address delayed (will take effect
441 ** after the next IN txn)
442 */
443 writel((s.value << 25) | (1 << 24), USB_DEVICEADDR);
444 setup_ack();
445 return;
446 case SETUP(INTERFACE_WRITE, SET_INTERFACE):
447 /* if we ack this everything hangs */
448 /* per spec, STALL is valid if there is not alt func */
449 goto stall;
450 case SETUP(ENDPOINT_WRITE, CLEAR_FEATURE): {
451 struct udc_endpoint *ept;
452 unsigned num = s.index & 15;
453 unsigned in = !!(s.index & 0x80);
454
455 if ((s.value == 0) && (s.length == 0)) {
456 DBG("clr feat %d %d\n", num, in);
457 for (ept = ept_list; ept; ept = ept->next) {
458 if ((ept->num == num) && (ept->in == in)) {
459 endpoint_enable(ept, 1);
460 setup_ack();
461 return;
462 }
463 }
464 }
465 break;
466 }
467 }
468
469 dprintf(INFO, "STALL %s %d %d %d %d %d\n",
470 reqname(s.request),
471 s.type, s.request, s.value, s.index, s.length);
472
473stall:
474 writel((1<<16) | (1 << 0), USB_ENDPTCTRL(ept->num));
475}
476
477unsigned ulpi_read(unsigned reg)
478{
479 /* initiate read operation */
480 writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg),
481 USB_ULPI_VIEWPORT);
482
483 /* wait for completion */
484 while(readl(USB_ULPI_VIEWPORT) & ULPI_RUN) ;
485
486 return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT));
487}
488
489void ulpi_write(unsigned val, unsigned reg)
490{
491 /* initiate write operation */
492 writel(ULPI_RUN | ULPI_WRITE |
493 ULPI_ADDR(reg) | ULPI_DATA(val),
494 USB_ULPI_VIEWPORT);
495
496 /* wait for completion */
497 while(readl(USB_ULPI_VIEWPORT) & ULPI_RUN) ;
498}
Ajay Dudani5a1e3302009-12-05 13:19:17 -0800499
500
501void hsusb_clock_init(void)
Ajay Dudani232ce812009-12-02 00:14:11 -0800502{
Ajay Dudani5a1e3302009-12-05 13:19:17 -0800503 // Enable usb clocks from apps processor for 7x30.
504 // USB clocks already initialized for other targets
505 // so skipping proc comm call to enable usb clocks.
506#ifdef PLATFORM_MSM7X30
Ajay Dudani232ce812009-12-02 00:14:11 -0800507 writel(0x00000100, USBH_NS_REG);
508 writel(0x00000900, USBH_NS_REG);
509 writel(0x00000A00, USBH_NS_REG);
510 writel(0x00002A00, USBH_NS_REG);
Ajay Dudani232ce812009-12-02 00:14:11 -0800511#endif
Ajay Dudani5a1e3302009-12-05 13:19:17 -0800512}
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800513
514void board_usb_init(void);
515void board_ulpi_init(void);
516
517int udc_init(struct udc_device *dev)
518{
519 hsusb_clock_init();
520
521 epts = memalign(4096, 4096);
522
523 dprintf(INFO, "USB init ept @ %p\n", epts);
524 memset(epts, 0, 32 * sizeof(struct ept_queue_head));
525
Ajay Dudani232ce812009-12-02 00:14:11 -0800526 //dprintf(INFO, "USB ID %08x\n", readl(USB_ID));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800527// board_usb_init();
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800528
529 /* select ULPI phy */
530 writel(0x81000000, USB_PORTSC);
531
532 /* RESET */
533 writel(0x00080002, USB_USBCMD);
534
535 thread_sleep(20);
536
537// board_ulpi_init();
538
539// arch_clean_invalidate_cache_range(epts, 32 * sizeof(struct ept_queue_head));
540 writel((unsigned) epts, USB_ENDPOINTLISTADDR);
541
542 /* select DEVICE mode */
543 writel(0x02, USB_USBMODE);
544
545 writel(0xffffffff, USB_ENDPTFLUSH);
546 thread_sleep(20);
547
548 ep0out = _udc_endpoint_alloc(0, 0, 64);
549 ep0in = _udc_endpoint_alloc(0, 1, 64);
550 ep0req = udc_request_alloc();
551 ep0req->buf = malloc(4096);
552
553 {
554 /* create and register a language table descriptor */
555 /* language 0x0409 is US English */
556 struct udc_descriptor *desc = udc_descriptor_alloc(TYPE_STRING, 0, 4);
557 desc->data[2] = 0x09;
558 desc->data[3] = 0x04;
559 udc_descriptor_register(desc);
560 }
561
562 the_device = dev;
563 return 0;
564}
565
566enum handler_return udc_interrupt(void *arg)
567{
568 struct udc_endpoint *ept;
569 unsigned ret = INT_NO_RESCHEDULE;
570 unsigned n = readl(USB_USBSTS);
571 writel(n, USB_USBSTS);
572
573 n &= (STS_SLI | STS_URI | STS_PCI | STS_UI | STS_UEI);
574
575 if (n == 0)
576 return ret;
577
578 if (n & STS_URI) {
579 writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE);
580 writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT);
581 writel(0xffffffff, USB_ENDPTFLUSH);
582 writel(0, USB_ENDPTCTRL(1));
583 DBG1("-- reset --\n");
584 usb_online = 0;
585 usb_config_value = 0;
586 the_gadget->notify(the_gadget, UDC_EVENT_OFFLINE);
587
588 /* error out any pending reqs */
589 for (ept = ept_list; ept; ept = ept->next) {
590 /* ensure that ept_complete considers
591 * this to be an error state
592 */
593 if (ept->req) {
594 ept->req->item->info = INFO_HALTED;
595 handle_ept_complete(ept);
596 }
597 }
598 usb_status(0, usb_highspeed);
599 }
600 if (n & STS_SLI) {
601 DBG1("-- suspend --\n");
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -0800602#ifdef ENABLE_BATTERY_CHARGING
603 if(HOST_CHARGER == TRUE){
604 charger_usb_i(2);
605 }
606#endif
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800607 }
608 if (n & STS_PCI) {
609 DBG1("-- portchange --\n");
610 unsigned spd = (readl(USB_PORTSC) >> 26) & 3;
611 if(spd == 2) {
612 usb_highspeed = 1;
613 } else {
614 usb_highspeed = 0;
615 }
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -0800616#ifdef ENABLE_BATTERY_CHARGING
617 if(HOST_CHARGER == TRUE && usb_config_value){
618 charger_usb_i(500);
619 }
620 if(HOST_CHARGER == TRUE && !usb_config_value){
621 charger_usb_i(100);
622 }
623#endif
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800624 }
625 if (n & STS_UEI) {
626 dprintf(INFO, "<UEI %x>\n", readl(USB_ENDPTCOMPLETE));
627 }
628#if 0
629 DBG("STS: ");
630 if (n & STS_UEI) DBG("ERROR ");
631 if (n & STS_SLI) DBG("SUSPEND ");
632 if (n & STS_URI) DBG("RESET ");
633 if (n & STS_PCI) DBG("PORTCHANGE ");
634 if (n & STS_UI) DBG("USB ");
635 DBG("\n");
636#endif
637 if ((n & STS_UI) || (n & STS_UEI)) {
638 n = readl(USB_ENDPTSETUPSTAT);
639 if (n & EPT_RX(0)) {
640 handle_setup(ep0out);
641 ret = INT_RESCHEDULE;
642 }
643
644 n = readl(USB_ENDPTCOMPLETE);
645 if (n != 0) {
646 writel(n, USB_ENDPTCOMPLETE);
647 }
648
649 for (ept = ept_list; ept; ept = ept->next){
650 if (n & ept->bit) {
651 handle_ept_complete(ept);
652 ret = INT_RESCHEDULE;
653 }
654 }
655 }
656 return ret;
657}
658
659int udc_register_gadget(struct udc_gadget *gadget)
660{
661 if (the_gadget) {
662 dprintf(CRITICAL, "only one gadget supported\n");
663 return -1;
664 }
665 the_gadget = gadget;
666 return 0;
667}
668
669static void udc_ept_desc_fill(struct udc_endpoint *ept, unsigned char *data)
670{
671 data[0] = 7;
672 data[1] = TYPE_ENDPOINT;
673 data[2] = ept->num | (ept->in ? 0x80 : 0x00);
674 data[3] = 0x02; /* bulk -- the only kind we support */
675 data[4] = ept->maxpkt;
676 data[5] = ept->maxpkt >> 8;
677 data[6] = ept->in ? 0x00 : 0x01;
678}
679
680static unsigned udc_ifc_desc_size(struct udc_gadget *g)
681{
682 return 9 + g->ifc_endpoints * 7;
683}
684
685static void udc_ifc_desc_fill(struct udc_gadget *g, unsigned char *data)
686{
687 unsigned n;
688
689 data[0] = 0x09;
690 data[1] = TYPE_INTERFACE;
691 data[2] = 0x00; /* ifc number */
692 data[3] = 0x00; /* alt number */
693 data[4] = g->ifc_endpoints;
694 data[5] = g->ifc_class;
695 data[6] = g->ifc_subclass;
696 data[7] = g->ifc_protocol;
697 data[8] = udc_string_desc_alloc(g->ifc_string);
698
699 data += 9;
700 for (n = 0; n < g->ifc_endpoints; n++) {
701 udc_ept_desc_fill(g->ept[n], data);
702 data += 7;
703 }
704}
705
706int udc_start(void)
707{
708 struct udc_descriptor *desc;
709 unsigned char *data;
710 unsigned size;
711
712 dprintf(INFO, "udc_start()\n");
713
714 if (!the_device) {
715 dprintf(CRITICAL, "udc cannot start before init\n");
716 return -1;
717 }
718 if (!the_gadget) {
719 dprintf(CRITICAL, "udc has no gadget registered\n");
720 return -1;
721 }
722
723 /* create our device descriptor */
724 desc = udc_descriptor_alloc(TYPE_DEVICE, 0, 18);
725 data = desc->data;
726 data[2] = 0x10; /* usb spec rev 2.10 */
727 data[3] = 0x02;
728 data[4] = 0x00; /* class */
729 data[5] = 0x00; /* subclass */
730 data[6] = 0x00; /* protocol */
731 data[7] = 0x40; /* max packet size on ept 0 */
732 memcpy(data + 8, &the_device->vendor_id, sizeof(short));
733 memcpy(data + 10, &the_device->product_id, sizeof(short));
734 memcpy(data + 12, &the_device->version_id, sizeof(short));
735 data[14] = udc_string_desc_alloc(the_device->manufacturer);
736 data[15] = udc_string_desc_alloc(the_device->product);
737 data[16] = udc_string_desc_alloc(the_device->serialno);
738 data[17] = 1; /* number of configurations */
739 udc_descriptor_register(desc);
740
741 /* create our configuration descriptor */
742 size = 9 + udc_ifc_desc_size(the_gadget);
743 desc = udc_descriptor_alloc(TYPE_CONFIGURATION, 0, size);
744 data = desc->data;
745 data[0] = 0x09;
746 data[2] = size;
747 data[3] = size >> 8;
748 data[4] = 0x01; /* number of interfaces */
749 data[5] = 0x01; /* configuration value */
750 data[6] = 0x00; /* configuration string */
751 data[7] = 0x80; /* attributes */
752 data[8] = 0x80; /* max power (250ma) -- todo fix this */
753 udc_ifc_desc_fill(the_gadget, data + 9);
754 udc_descriptor_register(desc);
755
756 /* go to RUN mode (D+ pullup enable) */
757 writel(0x00080001, USB_USBCMD);
758 register_int_handler(INT_USB_HS, udc_interrupt, (void*) 0);
759 unmask_interrupt(INT_USB_HS);
760 writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR);
761 return 0;
762}
763
764int udc_stop(void)
765{
766 writel(0, USB_USBINTR);
767 mask_interrupt(INT_USB_HS);
768
769 /* disable pullup */
770 writel(0x0008000, USB_USBCMD);
771 thread_sleep(10);
772
773 return 0;
774}
775
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -0800776void usb_stop_charging(unsigned stop_charging)
777{
778 ENABLE_CHARGING = !stop_charging;
779}
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800780
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -0800781static inline unsigned is_usb_charging(void)
782{
783 return ENABLE_CHARGING;
784}
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800785
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -0800786void usb_charger_reset(void)
787{
788 usb_stop_charging(TRUE);
789 charger_usb_disconnected();
790}
791
792/* Charger detection code
793 * Set global flags WALL_CHARGER and
794 * RETURN: type of charger connected
795 * CHG_WALL
796 * CHG_HOST_PC
797 * */
798int usb_chg_detect_type(void)
799{
800 int ret = CHG_UNDEFINED;
801
802 if ((readl(USB_PORTSC) & PORTSC_LS) == PORTSC_LS)
803 {
804 if(charger_usb_is_charger_connected() == TRUE) {
805 WALL_CHARGER = TRUE;
806 HOST_CHARGER = FALSE;
807 charger_usb_i(1500);
808 ret = CHG_WALL;
809 }
810 }
811 else
812 {
813 if(charger_usb_is_pc_connected() == TRUE) {
814 WALL_CHARGER = FALSE;
815 HOST_CHARGER = TRUE;
816 ret = CHG_HOST_PC;
817 }
818 }
819 return ret;
820}
821
822/* check if USB cable is connected
823 *
824 * RETURN: If cable connected return 1
825 * If cable disconnected return 0
826 */
827int is_usb_cable_connected(void)
828{
829 /*Verify B Session Valid Bit to verify vbus status*/
830 if (B_SESSION_VALID & readl(USB_OTGSC)) {
831 return 1;
832 } else {
833 return 0;
834 }
835}
836
837void usb_charger_change_state(void)
838{
839 int usb_connected;
840
841 //User might have switched from host pc to wall charger. So keep checking
842 //every time we are in the loop
843
844 if(ENABLE_CHARGING == TRUE)
845 {
846 usb_connected = is_usb_cable_connected();
847
848 if(usb_connected && !charger_connected)
849 {
850 //mdelay(20);
851 thread_sleep(20);
852 /* go to RUN mode (D+ pullup enable) */
853 writel(0x00080001, USB_USBCMD);
854 //mdelay(10);
855 thread_sleep(10);
856 usb_chg_detect_type();
857 charger_connected = TRUE;
858 }
859 else if(!usb_connected && charger_connected)
860 {
861 /* disable D+ pull-up */
862 writel(0x00080000, USB_USBCMD);
863
864 /* Applicable only for 8k target */
865 /*USB Spoof Disconnect Failure
866 Symptoms:
867 In USB peripheral mode, writing '0' to Run/Stop bit of the
868 USBCMD register doesn't cause USB disconnection (spoof disconnect).
869 The PC host doesn't detect the disconnection and the phone remains
870 active on Windows device manager.
871
872 Suggested Workaround:
873 After writing '0' to Run/Stop bit of USBCMD, also write 0x48 to ULPI
874 "Function Control" register. This can be done via the ULPI VIEWPORT
875 register (offset 0x170) by writing a value of 0x60040048.
876 */
877 ulpi_write(0x48, 0x04);
878 //usb_charger_reset();
879 WALL_CHARGER = FALSE;
880 HOST_CHARGER = FALSE;
881 charger_usb_i(0);
882 charger_usb_disconnected();
883 charger_connected = FALSE;
884 }
885 if(WALL_CHARGER == TRUE || HOST_CHARGER == TRUE){
886 //battery_charging_image();
887 }
888 }
889 else if ((readl(USB_USBCMD) & 0x01) == 0){
890 writel(0x00080001, USB_USBCMD);
891 }
892}