blob: 587bc72386c9d8cac797bc56bacef694fed80621 [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
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{
Greg Griscod6250552011-06-29 14:40:23 -0700118 return;
Ajay Dudani7d605522010-10-01 19:52:37 -0700119}
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
Subbaraman Narayanamurthyd8b7afc2011-06-30 15:42:41 -0700378static void set_test_mode(uint16_t value)
379{
380 uint32_t mode;
381 if(value == TEST_PACKET)
382 {
383 dprintf(ALWAYS,"Entering test mode for TST_PKT\n");
384 mode = readl(USB_PORTSC) & (~PORTSC_PTC);
385 writel(mode | PORTSC_PTC_TST_PKT, USB_PORTSC);
386 }
387}
388
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800389static void handle_setup(struct udc_endpoint *ept)
390{
391 struct setup_packet s;
392
393 memcpy(&s, ept->head->setup_data, sizeof(s));
394 writel(ept->bit, USB_ENDPTSETUPSTAT);
395
396#if 0
397 DBG("handle_setup type=0x%02x req=0x%02x val=%d idx=%d len=%d (%s)\n",
398 s.type, s.request, s.value, s.index, s.length,
399 reqname(s.request));
400#endif
401 switch (SETUP(s.type,s.request)) {
402 case SETUP(DEVICE_READ, GET_STATUS): {
403 unsigned zero = 0;
404 if (s.length == 2) {
405 setup_tx(&zero, 2);
406 return;
407 }
408 break;
409 }
410 case SETUP(DEVICE_READ, GET_DESCRIPTOR): {
411 struct udc_descriptor *desc;
412 /* usb_highspeed? */
413 for (desc = desc_list; desc; desc = desc->next) {
414 if (desc->tag == s.value) {
415 unsigned len = desc->len;
416 if (len > s.length) len = s.length;
417 setup_tx(desc->data, len);
418 return;
419 }
420 }
421 break;
422 }
423 case SETUP(DEVICE_READ, GET_CONFIGURATION):
424 /* disabling this causes data transaction failures on OSX. Why? */
425 if ((s.value == 0) && (s.index == 0) && (s.length == 1)) {
426 setup_tx(&usb_config_value, 1);
427 return;
428 }
429 break;
430 case SETUP(DEVICE_WRITE, SET_CONFIGURATION):
431 if (s.value == 1) {
432 struct udc_endpoint *ept;
433 /* enable endpoints */
434 for (ept = ept_list; ept; ept = ept->next){
435 if (ept->num == 0)
436 continue;
437 endpoint_enable(ept, s.value);
438 }
439 usb_config_value = 1;
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -0800440#ifdef ENABLE_BATTERY_CHARGING
441 if(HOST_CHARGER == TRUE) {
442 charger_usb_i(500);
443 }
444#endif
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800445 the_gadget->notify(the_gadget, UDC_EVENT_ONLINE);
446 } else {
447 writel(0, USB_ENDPTCTRL(1));
448 usb_config_value = 0;
449 the_gadget->notify(the_gadget, UDC_EVENT_OFFLINE);
450 }
451 setup_ack();
452 usb_online = s.value ? 1 : 0;
453 usb_status(s.value ? 1 : 0, usb_highspeed);
454 return;
455 case SETUP(DEVICE_WRITE, SET_ADDRESS):
456 /* write address delayed (will take effect
457 ** after the next IN txn)
458 */
459 writel((s.value << 25) | (1 << 24), USB_DEVICEADDR);
460 setup_ack();
461 return;
462 case SETUP(INTERFACE_WRITE, SET_INTERFACE):
463 /* if we ack this everything hangs */
464 /* per spec, STALL is valid if there is not alt func */
465 goto stall;
Subbaraman Narayanamurthyd8b7afc2011-06-30 15:42:41 -0700466 case SETUP(DEVICE_WRITE, SET_FEATURE):
467 setup_ack();
468 /* TODO: Use s.value and fix byte ordering */
469 set_test_mode(s.index);
470 return;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800471 case SETUP(ENDPOINT_WRITE, CLEAR_FEATURE): {
472 struct udc_endpoint *ept;
473 unsigned num = s.index & 15;
474 unsigned in = !!(s.index & 0x80);
475
476 if ((s.value == 0) && (s.length == 0)) {
477 DBG("clr feat %d %d\n", num, in);
478 for (ept = ept_list; ept; ept = ept->next) {
479 if ((ept->num == num) && (ept->in == in)) {
480 endpoint_enable(ept, 1);
481 setup_ack();
482 return;
483 }
484 }
485 }
486 break;
487 }
488 }
489
490 dprintf(INFO, "STALL %s %d %d %d %d %d\n",
491 reqname(s.request),
492 s.type, s.request, s.value, s.index, s.length);
493
494stall:
495 writel((1<<16) | (1 << 0), USB_ENDPTCTRL(ept->num));
496}
497
498unsigned ulpi_read(unsigned reg)
499{
500 /* initiate read operation */
501 writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg),
502 USB_ULPI_VIEWPORT);
503
504 /* wait for completion */
505 while(readl(USB_ULPI_VIEWPORT) & ULPI_RUN) ;
506
507 return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT));
508}
509
510void ulpi_write(unsigned val, unsigned reg)
511{
512 /* initiate write operation */
513 writel(ULPI_RUN | ULPI_WRITE |
514 ULPI_ADDR(reg) | ULPI_DATA(val),
515 USB_ULPI_VIEWPORT);
516
517 /* wait for completion */
518 while(readl(USB_ULPI_VIEWPORT) & ULPI_RUN) ;
519}
Ajay Dudani5a1e3302009-12-05 13:19:17 -0800520
Shashank Mittal23b8f422010-04-16 19:27:21 -0700521#define USB_CLK 0x00902910
522#define USB_PHY_CLK 0x00902E20
523#define CLK_RESET_ASSERT 0x1
524#define CLK_RESET_DEASSERT 0x0
525#define CLK_RESET(x,y) writel((y), (x));
526
527static int msm_otg_xceiv_reset()
528{
529 CLK_RESET(USB_CLK, CLK_RESET_ASSERT);
530 CLK_RESET(USB_PHY_CLK, CLK_RESET_ASSERT);
531 mdelay(20);
532 CLK_RESET(USB_PHY_CLK, CLK_RESET_DEASSERT);
533 CLK_RESET(USB_CLK, CLK_RESET_DEASSERT);
534 mdelay(20);
535
536 /* select ULPI phy */
537 writel(0x81000000, USB_PORTSC);
538 return 0;
539}
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800540
541void board_usb_init(void);
542void board_ulpi_init(void);
543
544int udc_init(struct udc_device *dev)
545{
546 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));
557
Ajay Dudani232ce812009-12-02 00:14:11 -0800558 //dprintf(INFO, "USB ID %08x\n", readl(USB_ID));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800559// board_usb_init();
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800560
561 /* select ULPI phy */
Shashank Mittal23b8f422010-04-16 19:27:21 -0700562#ifdef PLATFORM_MSM8X60
563 msm_otg_xceiv_reset();
564#else
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800565 writel(0x81000000, USB_PORTSC);
Shashank Mittal23b8f422010-04-16 19:27:21 -0700566#endif
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800567 /* RESET */
568 writel(0x00080002, USB_USBCMD);
569
570 thread_sleep(20);
571
572// board_ulpi_init();
573
574// arch_clean_invalidate_cache_range(epts, 32 * sizeof(struct ept_queue_head));
575 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 }
596
597 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);
607
608 n &= (STS_SLI | STS_URI | STS_PCI | STS_UI | STS_UEI);
609
610 if (n == 0)
611 return ret;
612
613 if (n & STS_URI) {
614 writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE);
615 writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT);
616 writel(0xffffffff, USB_ENDPTFLUSH);
617 writel(0, USB_ENDPTCTRL(1));
618 DBG1("-- reset --\n");
619 usb_online = 0;
620 usb_config_value = 0;
621 the_gadget->notify(the_gadget, UDC_EVENT_OFFLINE);
622
623 /* error out any pending reqs */
624 for (ept = ept_list; ept; ept = ept->next) {
625 /* ensure that ept_complete considers
626 * this to be an error state
627 */
628 if (ept->req) {
629 ept->req->item->info = INFO_HALTED;
630 handle_ept_complete(ept);
631 }
632 }
633 usb_status(0, usb_highspeed);
634 }
635 if (n & STS_SLI) {
636 DBG1("-- suspend --\n");
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -0800637#ifdef ENABLE_BATTERY_CHARGING
638 if(HOST_CHARGER == TRUE){
639 charger_usb_i(2);
640 }
641#endif
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800642 }
643 if (n & STS_PCI) {
644 DBG1("-- portchange --\n");
645 unsigned spd = (readl(USB_PORTSC) >> 26) & 3;
646 if(spd == 2) {
647 usb_highspeed = 1;
648 } else {
649 usb_highspeed = 0;
650 }
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -0800651#ifdef ENABLE_BATTERY_CHARGING
652 if(HOST_CHARGER == TRUE && usb_config_value){
653 charger_usb_i(500);
654 }
655 if(HOST_CHARGER == TRUE && !usb_config_value){
656 charger_usb_i(100);
657 }
658#endif
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800659 }
660 if (n & STS_UEI) {
661 dprintf(INFO, "<UEI %x>\n", readl(USB_ENDPTCOMPLETE));
662 }
663#if 0
664 DBG("STS: ");
665 if (n & STS_UEI) DBG("ERROR ");
666 if (n & STS_SLI) DBG("SUSPEND ");
667 if (n & STS_URI) DBG("RESET ");
668 if (n & STS_PCI) DBG("PORTCHANGE ");
669 if (n & STS_UI) DBG("USB ");
670 DBG("\n");
671#endif
672 if ((n & STS_UI) || (n & STS_UEI)) {
673 n = readl(USB_ENDPTSETUPSTAT);
674 if (n & EPT_RX(0)) {
675 handle_setup(ep0out);
676 ret = INT_RESCHEDULE;
677 }
678
679 n = readl(USB_ENDPTCOMPLETE);
680 if (n != 0) {
681 writel(n, USB_ENDPTCOMPLETE);
682 }
683
684 for (ept = ept_list; ept; ept = ept->next){
685 if (n & ept->bit) {
686 handle_ept_complete(ept);
687 ret = INT_RESCHEDULE;
688 }
689 }
690 }
691 return ret;
692}
693
694int udc_register_gadget(struct udc_gadget *gadget)
695{
696 if (the_gadget) {
697 dprintf(CRITICAL, "only one gadget supported\n");
698 return -1;
699 }
700 the_gadget = gadget;
701 return 0;
702}
703
704static void udc_ept_desc_fill(struct udc_endpoint *ept, unsigned char *data)
705{
706 data[0] = 7;
707 data[1] = TYPE_ENDPOINT;
708 data[2] = ept->num | (ept->in ? 0x80 : 0x00);
709 data[3] = 0x02; /* bulk -- the only kind we support */
710 data[4] = ept->maxpkt;
711 data[5] = ept->maxpkt >> 8;
712 data[6] = ept->in ? 0x00 : 0x01;
713}
714
715static unsigned udc_ifc_desc_size(struct udc_gadget *g)
716{
717 return 9 + g->ifc_endpoints * 7;
718}
719
720static void udc_ifc_desc_fill(struct udc_gadget *g, unsigned char *data)
721{
722 unsigned n;
723
724 data[0] = 0x09;
725 data[1] = TYPE_INTERFACE;
726 data[2] = 0x00; /* ifc number */
727 data[3] = 0x00; /* alt number */
728 data[4] = g->ifc_endpoints;
729 data[5] = g->ifc_class;
730 data[6] = g->ifc_subclass;
731 data[7] = g->ifc_protocol;
732 data[8] = udc_string_desc_alloc(g->ifc_string);
733
734 data += 9;
735 for (n = 0; n < g->ifc_endpoints; n++) {
736 udc_ept_desc_fill(g->ept[n], data);
737 data += 7;
738 }
739}
740
741int udc_start(void)
742{
743 struct udc_descriptor *desc;
744 unsigned char *data;
745 unsigned size;
746
Chandan Uddaraju40b227d2010-08-03 19:25:41 -0700747 dprintf(ALWAYS, "udc_start()\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800748
749 if (!the_device) {
750 dprintf(CRITICAL, "udc cannot start before init\n");
751 return -1;
752 }
753 if (!the_gadget) {
754 dprintf(CRITICAL, "udc has no gadget registered\n");
755 return -1;
756 }
757
758 /* create our device descriptor */
759 desc = udc_descriptor_alloc(TYPE_DEVICE, 0, 18);
760 data = desc->data;
Amol Jadica4f4c92011-01-13 20:19:34 -0800761 data[2] = 0x00; /* usb spec minor rev */
762 data[3] = 0x02; /* usb spec major rev */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800763 data[4] = 0x00; /* class */
764 data[5] = 0x00; /* subclass */
765 data[6] = 0x00; /* protocol */
766 data[7] = 0x40; /* max packet size on ept 0 */
767 memcpy(data + 8, &the_device->vendor_id, sizeof(short));
768 memcpy(data + 10, &the_device->product_id, sizeof(short));
769 memcpy(data + 12, &the_device->version_id, sizeof(short));
770 data[14] = udc_string_desc_alloc(the_device->manufacturer);
771 data[15] = udc_string_desc_alloc(the_device->product);
772 data[16] = udc_string_desc_alloc(the_device->serialno);
773 data[17] = 1; /* number of configurations */
774 udc_descriptor_register(desc);
775
776 /* create our configuration descriptor */
777 size = 9 + udc_ifc_desc_size(the_gadget);
778 desc = udc_descriptor_alloc(TYPE_CONFIGURATION, 0, size);
779 data = desc->data;
780 data[0] = 0x09;
781 data[2] = size;
782 data[3] = size >> 8;
783 data[4] = 0x01; /* number of interfaces */
784 data[5] = 0x01; /* configuration value */
785 data[6] = 0x00; /* configuration string */
786 data[7] = 0x80; /* attributes */
787 data[8] = 0x80; /* max power (250ma) -- todo fix this */
788 udc_ifc_desc_fill(the_gadget, data + 9);
789 udc_descriptor_register(desc);
790
Amol Jadica4f4c92011-01-13 20:19:34 -0800791 register_int_handler(INT_USB_HS, udc_interrupt, (void*) 0);
792 writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR);
793 unmask_interrupt(INT_USB_HS);
794
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800795 /* go to RUN mode (D+ pullup enable) */
796 writel(0x00080001, USB_USBCMD);
Amol Jadica4f4c92011-01-13 20:19:34 -0800797
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800798 return 0;
799}
800
801int udc_stop(void)
802{
Shashank Mittal237301c2010-08-24 19:11:46 -0700803 int val;
804 writel(0, USB_USBINTR);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800805 mask_interrupt(INT_USB_HS);
806
807 /* disable pullup */
Shashank Mittald8c42bf2010-06-09 15:44:28 -0700808 writel(0x00080000, USB_USBCMD);
Shashank Mittal237301c2010-08-24 19:11:46 -0700809#ifdef PLATFORM_MSM8X60
810 /* Voting down PLL8 */
811 val = readl(0x009034C0);
812 val &= ~(1<<8);
813 writel(val, 0x009034C0);
814#endif
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800815 thread_sleep(10);
816
817 return 0;
818}
819
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -0800820void usb_stop_charging(unsigned stop_charging)
821{
822 ENABLE_CHARGING = !stop_charging;
823}
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800824
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -0800825static inline unsigned is_usb_charging(void)
826{
827 return ENABLE_CHARGING;
828}
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800829
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -0800830void usb_charger_reset(void)
831{
832 usb_stop_charging(TRUE);
833 charger_usb_disconnected();
834}
835
836/* Charger detection code
837 * Set global flags WALL_CHARGER and
838 * RETURN: type of charger connected
839 * CHG_WALL
840 * CHG_HOST_PC
841 * */
842int usb_chg_detect_type(void)
843{
844 int ret = CHG_UNDEFINED;
845
846 if ((readl(USB_PORTSC) & PORTSC_LS) == PORTSC_LS)
847 {
848 if(charger_usb_is_charger_connected() == TRUE) {
849 WALL_CHARGER = TRUE;
850 HOST_CHARGER = FALSE;
851 charger_usb_i(1500);
852 ret = CHG_WALL;
853 }
854 }
855 else
856 {
857 if(charger_usb_is_pc_connected() == TRUE) {
858 WALL_CHARGER = FALSE;
859 HOST_CHARGER = TRUE;
860 ret = CHG_HOST_PC;
861 }
862 }
863 return ret;
864}
865
866/* check if USB cable is connected
867 *
868 * RETURN: If cable connected return 1
869 * If cable disconnected return 0
870 */
871int is_usb_cable_connected(void)
872{
873 /*Verify B Session Valid Bit to verify vbus status*/
874 if (B_SESSION_VALID & readl(USB_OTGSC)) {
875 return 1;
876 } else {
877 return 0;
878 }
879}
880
Shashank Mittald8c42bf2010-06-09 15:44:28 -0700881/* check for USB connection assuming USB is not pulled up.
882 * It looks for suspend state bit in PORTSC register.
883 *
884 * RETURN: If cable connected return 1
885 * If cable disconnected return 0
886 */
887
888int usb_cable_status(void)
889{
890 unsigned ret = 0;
891 /*Verify B Session Valid Bit to verify vbus status*/
892 writel(0x00080001, USB_USBCMD);
893 thread_sleep(100);
894
895 /*Check reset value of suspend state bit*/
896 if (!((1<<7) & readl(USB_PORTSC))) {
897 ret=1;
898 }
899 udc_stop();
900 return ret;
901}
902
Chandan Uddaraju7f5b9012010-02-06 16:37:48 -0800903void usb_charger_change_state(void)
904{
905 int usb_connected;
906
907 //User might have switched from host pc to wall charger. So keep checking
908 //every time we are in the loop
909
910 if(ENABLE_CHARGING == TRUE)
911 {
912 usb_connected = is_usb_cable_connected();
913
914 if(usb_connected && !charger_connected)
915 {
916 //mdelay(20);
917 thread_sleep(20);
918 /* go to RUN mode (D+ pullup enable) */
919 writel(0x00080001, USB_USBCMD);
920 //mdelay(10);
921 thread_sleep(10);
922 usb_chg_detect_type();
923 charger_connected = TRUE;
924 }
925 else if(!usb_connected && charger_connected)
926 {
927 /* disable D+ pull-up */
928 writel(0x00080000, USB_USBCMD);
929
930 /* Applicable only for 8k target */
931 /*USB Spoof Disconnect Failure
932 Symptoms:
933 In USB peripheral mode, writing '0' to Run/Stop bit of the
934 USBCMD register doesn't cause USB disconnection (spoof disconnect).
935 The PC host doesn't detect the disconnection and the phone remains
936 active on Windows device manager.
937
938 Suggested Workaround:
939 After writing '0' to Run/Stop bit of USBCMD, also write 0x48 to ULPI
940 "Function Control" register. This can be done via the ULPI VIEWPORT
941 register (offset 0x170) by writing a value of 0x60040048.
942 */
943 ulpi_write(0x48, 0x04);
944 //usb_charger_reset();
945 WALL_CHARGER = FALSE;
946 HOST_CHARGER = FALSE;
947 charger_usb_i(0);
948 charger_usb_disconnected();
949 charger_connected = FALSE;
950 }
951 if(WALL_CHARGER == TRUE || HOST_CHARGER == TRUE){
952 //battery_charging_image();
953 }
954 }
955 else if ((readl(USB_USBCMD) & 0x01) == 0){
956 writel(0x00080001, USB_USBCMD);
957 }
958}