blob: 71410a269218b8faf610aff982824deca7f36ce6 [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;
Ajay Dudanib01e5062011-12-03 23:23:42 -080049 unsigned short tag; /* ((TYPE << 8) | NUM) */
50 unsigned short len; /* total length */
Brian Swetland3e7e21a2009-01-19 19:41:24 -080051 unsigned char data[0];
52};
53
Ajay Dudanib01e5062011-12-03 23:23:42 -080054struct udc_descriptor *udc_descriptor_alloc(unsigned type, unsigned num,
55 unsigned len)
Brian Swetland3e7e21a2009-01-19 19:41:24 -080056{
57 struct udc_descriptor *desc;
58 if ((len > 255) || (len < 2) || (num > 255) || (type > 255))
59 return 0;
60
Ajay Dudanib01e5062011-12-03 23:23:42 -080061 if (!(desc = malloc(sizeof(struct udc_descriptor) + len)))
Brian Swetland3e7e21a2009-01-19 19:41:24 -080062 return 0;
63
64 desc->next = 0;
65 desc->tag = (type << 8) | num;
66 desc->len = len;
67 desc->data[0] = len;
68 desc->data[1] = type;
69
70 return desc;
71}
72
73static struct udc_descriptor *desc_list = 0;
74static unsigned next_string_id = 1;
75
76void udc_descriptor_register(struct udc_descriptor *desc)
77{
78 desc->next = desc_list;
79 desc_list = desc;
80}
81
82unsigned udc_string_desc_alloc(const char *str)
83{
84 unsigned len;
85 struct udc_descriptor *desc;
86 unsigned char *data;
87
88 if (next_string_id > 255)
89 return 0;
90
91 if (!str)
92 return 0;
93
94 len = strlen(str);
95 desc = udc_descriptor_alloc(TYPE_STRING, next_string_id, len * 2 + 2);
96 if (!desc)
97 return 0;
98 next_string_id++;
99
100 /* expand ascii string to utf16 */
101 data = desc->data + 2;
102 while (len-- > 0) {
103 *data++ = *str++;
104 *data++ = 0;
105 }
106
107 udc_descriptor_register(desc);
108 return desc->tag & 0xff;
109}
110
111/* end of common code */
112
Ajay Dudani7d605522010-10-01 19:52:37 -0700113__WEAK void hsusb_clock_init(void)
114{
Greg Griscod6250552011-06-29 14:40:23 -0700115 return;
Ajay Dudani7d605522010-10-01 19:52:37 -0700116}
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800117
118#if 1
119#define DBG(x...) do {} while(0)
120#else
121#define DBG(x...) dprintf(INFO, x)
122#endif
123
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800124#define usb_status(a,b)
125
126struct usb_request {
127 struct udc_request req;
128 struct ept_queue_item *item;
129};
Amol Jadi4421e652011-06-16 15:00:48 -0700130
Ajay Dudanib01e5062011-12-03 23:23:42 -0800131struct udc_endpoint {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800132 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;
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700149static unsigned test_mode = 0;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800150
Ajay Dudanib01e5062011-12-03 23:23:42 -0800151struct udc_endpoint *_udc_endpoint_alloc(unsigned num, unsigned in,
152 unsigned max_pkt)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800153{
154 struct udc_endpoint *ept;
155 unsigned cfg;
156
157 ept = malloc(sizeof(*ept));
Amol Jadi4421e652011-06-16 15:00:48 -0700158
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800159 ept->maxpkt = max_pkt;
160 ept->num = num;
161 ept->in = !!in;
162 ept->req = 0;
163
164 cfg = CONFIG_MAX_PKT(max_pkt) | CONFIG_ZLT;
165
Ajay Dudanib01e5062011-12-03 23:23:42 -0800166 if (ept->in) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800167 ept->bit = EPT_TX(ept->num);
168 } else {
169 ept->bit = EPT_RX(ept->num);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800170 if (num == 0)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800171 cfg |= CONFIG_IOS;
172 }
173
174 ept->head = epts + (num * 2) + (ept->in);
175 ept->head->config = cfg;
176
177 ept->next = ept_list;
178 ept_list = ept;
Amol Jadi4421e652011-06-16 15:00:48 -0700179
180 DBG("ept%d %s @%p/%p max=%d bit=%x\n",
Ajay Dudanib01e5062011-12-03 23:23:42 -0800181 num, in ? "in" : "out", ept, ept->head, max_pkt, ept->bit);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800182
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
Ajay Dudanib01e5062011-12-03 23:23:42 -0800223 if (yes) {
224 if (ept->in) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800225 n |= (CTRL_TXE | CTRL_TXR | CTRL_TXT_BULK);
226 } else {
227 n |= (CTRL_RXE | CTRL_RXR | CTRL_RXT_BULK);
228 }
229
Ajay Dudanib01e5062011-12-03 23:23:42 -0800230 if (ept->num != 0) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800231 /* XXX should be more dynamic... */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800232 if (usb_highspeed) {
233 ept->head->config =
234 CONFIG_MAX_PKT(512) | CONFIG_ZLT;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800235 } else {
Ajay Dudanib01e5062011-12-03 23:23:42 -0800236 ept->head->config =
237 CONFIG_MAX_PKT(64) | CONFIG_ZLT;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800238 }
239 }
240 }
241 writel(n, USB_ENDPTCTRL(ept->num));
242}
243
244struct udc_request *udc_request_alloc(void)
245{
246 struct usb_request *req;
247 req = malloc(sizeof(*req));
248 req->req.buf = 0;
249 req->req.length = 0;
250 req->item = memalign(32, 32);
251 return &req->req;
252}
253
254void udc_request_free(struct udc_request *req)
255{
256 free(req);
257}
258
259int udc_request_queue(struct udc_endpoint *ept, struct udc_request *_req)
260{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800261 struct usb_request *req = (struct usb_request *)_req;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800262 struct ept_queue_item *item = req->item;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800263 unsigned phys = (unsigned)req->req.buf;
Amol Jadi4421e652011-06-16 15:00:48 -0700264
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800265 item->next = TERMINATE;
266 item->info = INFO_BYTES(req->req.length) | INFO_IOC | INFO_ACTIVE;
267 item->page0 = phys;
268 item->page1 = (phys & 0xfffff000) + 0x1000;
269
270 enter_critical_section();
Ajay Dudanib01e5062011-12-03 23:23:42 -0800271 ept->head->next = (unsigned)item;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800272 ept->head->info = 0;
273 ept->req = req;
274
Ajay Dudanib01e5062011-12-03 23:23:42 -0800275 arch_clean_invalidate_cache_range((addr_t) ept,
276 sizeof(struct udc_endpoint));
277 arch_clean_invalidate_cache_range((addr_t) ept->head,
278 sizeof(struct ept_queue_head));
279 arch_clean_invalidate_cache_range((addr_t) ept->req,
280 sizeof(struct usb_request));
281 arch_clean_invalidate_cache_range((addr_t) req->req.buf,
282 req->req.length);
283 arch_clean_invalidate_cache_range((addr_t) ept->req->item,
284 sizeof(struct ept_queue_item));
Amol Jadi4421e652011-06-16 15:00:48 -0700285
Ajay Dudanib01e5062011-12-03 23:23:42 -0800286 DBG("ept%d %s queue req=%p\n", ept->num, ept->in ? "in" : "out", req);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800287
288 writel(ept->bit, USB_ENDPTPRIME);
289 exit_critical_section();
290 return 0;
291}
292
293static void handle_ept_complete(struct udc_endpoint *ept)
294{
295 struct ept_queue_item *item;
296 unsigned actual;
297 int status;
298 struct usb_request *req;
Amol Jadi4421e652011-06-16 15:00:48 -0700299
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800300 DBG("ept%d %s complete req=%p\n",
Ajay Dudanib01e5062011-12-03 23:23:42 -0800301 ept->num, ept->in ? "in" : "out", ept->req);
Amol Jadi4421e652011-06-16 15:00:48 -0700302
Ajay Dudanib01e5062011-12-03 23:23:42 -0800303 arch_clean_invalidate_cache_range((addr_t) ept,
304 sizeof(struct udc_endpoint));
305 arch_clean_invalidate_cache_range((addr_t) ept->req,
306 sizeof(struct usb_request));
Amol Jadi4421e652011-06-16 15:00:48 -0700307
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800308 req = ept->req;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800309 if (req) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800310 ept->req = 0;
Amol Jadi4421e652011-06-16 15:00:48 -0700311
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800312 item = req->item;
313
314 /* For some reason we are getting the notification for
315 * transfer completion before the active bit has cleared.
316 * HACK: wait for the ACTIVE bit to clear:
317 */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800318 do {
Amol Jadi4421e652011-06-16 15:00:48 -0700319 /* Must clean/invalidate cached item data before checking
320 * the status every time.
321 */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800322 arch_clean_invalidate_cache_range((addr_t) item,
323 sizeof(struct
324 ept_queue_item));
325 }
326 while (readl(&(item->info)) & INFO_ACTIVE);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800327
Ajay Dudanib01e5062011-12-03 23:23:42 -0800328 arch_clean_invalidate_cache_range((addr_t) req->req.buf,
329 req->req.length);
Amol Jadi4421e652011-06-16 15:00:48 -0700330
Ajay Dudanib01e5062011-12-03 23:23:42 -0800331 if (item->info & 0xff) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800332 actual = 0;
333 status = -1;
334 dprintf(INFO, "EP%d/%s FAIL nfo=%x pg0=%x\n",
Ajay Dudanib01e5062011-12-03 23:23:42 -0800335 ept->num, ept->in ? "in" : "out", item->info,
336 item->page0);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800337 } else {
Ajay Dudanib01e5062011-12-03 23:23:42 -0800338 actual =
339 req->req.length - ((item->info >> 16) & 0x7fff);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800340 status = 0;
341 }
Ajay Dudanib01e5062011-12-03 23:23:42 -0800342 if (req->req.complete)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800343 req->req.complete(&req->req, actual, status);
344 }
345}
346
347static const char *reqname(unsigned r)
348{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800349 switch (r) {
350 case GET_STATUS:
351 return "GET_STATUS";
352 case CLEAR_FEATURE:
353 return "CLEAR_FEATURE";
354 case SET_FEATURE:
355 return "SET_FEATURE";
356 case SET_ADDRESS:
357 return "SET_ADDRESS";
358 case GET_DESCRIPTOR:
359 return "GET_DESCRIPTOR";
360 case SET_DESCRIPTOR:
361 return "SET_DESCRIPTOR";
362 case GET_CONFIGURATION:
363 return "GET_CONFIGURATION";
364 case SET_CONFIGURATION:
365 return "SET_CONFIGURATION";
366 case GET_INTERFACE:
367 return "GET_INTERFACE";
368 case SET_INTERFACE:
369 return "SET_INTERFACE";
370 default:
371 return "*UNKNOWN*";
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800372 }
373}
374
375static struct udc_endpoint *ep0in, *ep0out;
376static struct udc_request *ep0req;
377
Ajay Dudanib01e5062011-12-03 23:23:42 -0800378static void
379ep0_setup_ack_complete(struct udc_endpoint *ep, struct usb_request *req)
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700380{
381 uint32_t mode;
382
Ajay Dudanib01e5062011-12-03 23:23:42 -0800383 if (!test_mode)
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700384 return;
385
Ajay Dudanib01e5062011-12-03 23:23:42 -0800386 switch (test_mode) {
387 case TEST_PACKET:
388 dprintf(INFO, "Entering test mode for TST_PKT\n");
389 mode = readl(USB_PORTSC) & (~PORTSC_PTC);
390 writel(mode | PORTSC_PTC_TST_PKT, USB_PORTSC);
391 break;
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700392
Ajay Dudanib01e5062011-12-03 23:23:42 -0800393 case TEST_SE0_NAK:
394 dprintf(INFO, "Entering test mode for SE0-NAK\n");
395 mode = readl(USB_PORTSC) & (~PORTSC_PTC);
396 writel(mode | PORTSC_PTC_SE0_NAK, USB_PORTSC);
397 break;
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700398 }
399
400}
401
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800402static void setup_ack(void)
403{
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700404 ep0req->complete = ep0_setup_ack_complete;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800405 ep0req->length = 0;
406 udc_request_queue(ep0in, ep0req);
407}
408
409static void ep0in_complete(struct udc_request *req, unsigned actual, int status)
410{
411 DBG("ep0in_complete %p %d %d\n", req, actual, status);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800412 if (status == 0) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800413 req->length = 0;
414 req->complete = 0;
415 udc_request_queue(ep0out, req);
416 }
417}
418
419static void setup_tx(void *buf, unsigned len)
420{
421 DBG("setup_tx %p %d\n", buf, len);
422 memcpy(ep0req->buf, buf, len);
423 ep0req->complete = ep0in_complete;
424 ep0req->length = len;
425 udc_request_queue(ep0in, ep0req);
426}
427
428static unsigned char usb_config_value = 0;
429
430#define SETUP(type,request) (((type) << 8) | (request))
431
432static void handle_setup(struct udc_endpoint *ept)
433{
434 struct setup_packet s;
Amol Jadi4421e652011-06-16 15:00:48 -0700435
Ajay Dudanib01e5062011-12-03 23:23:42 -0800436 arch_clean_invalidate_cache_range((addr_t) ept->head->setup_data,
437 sizeof(struct ept_queue_head));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800438 memcpy(&s, ept->head->setup_data, sizeof(s));
439 writel(ept->bit, USB_ENDPTSETUPSTAT);
440
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800441 DBG("handle_setup type=0x%02x req=0x%02x val=%d idx=%d len=%d (%s)\n",
Ajay Dudanib01e5062011-12-03 23:23:42 -0800442 s.type, s.request, s.value, s.index, s.length, reqname(s.request));
Amol Jadi4421e652011-06-16 15:00:48 -0700443
Ajay Dudanib01e5062011-12-03 23:23:42 -0800444 switch (SETUP(s.type, s.request)) {
445 case SETUP(DEVICE_READ, GET_STATUS):
446 {
447 unsigned zero = 0;
448 if (s.length == 2) {
449 setup_tx(&zero, 2);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800450 return;
451 }
Ajay Dudanib01e5062011-12-03 23:23:42 -0800452 break;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800453 }
Ajay Dudanib01e5062011-12-03 23:23:42 -0800454 case SETUP(DEVICE_READ, GET_DESCRIPTOR):
455 {
456 struct udc_descriptor *desc;
457 /* usb_highspeed? */
458 for (desc = desc_list; desc; desc = desc->next) {
459 if (desc->tag == s.value) {
460 unsigned len = desc->len;
461 if (len > s.length)
462 len = s.length;
463 setup_tx(desc->data, len);
464 return;
465 }
466 }
467 break;
468 }
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800469 case SETUP(DEVICE_READ, GET_CONFIGURATION):
470 /* disabling this causes data transaction failures on OSX. Why? */
471 if ((s.value == 0) && (s.index == 0) && (s.length == 1)) {
472 setup_tx(&usb_config_value, 1);
473 return;
474 }
475 break;
476 case SETUP(DEVICE_WRITE, SET_CONFIGURATION):
477 if (s.value == 1) {
478 struct udc_endpoint *ept;
479 /* enable endpoints */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800480 for (ept = ept_list; ept; ept = ept->next) {
Amol Jadi4421e652011-06-16 15:00:48 -0700481 if (ept->num == 0)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800482 continue;
483 endpoint_enable(ept, s.value);
484 }
485 usb_config_value = 1;
486 the_gadget->notify(the_gadget, UDC_EVENT_ONLINE);
487 } else {
488 writel(0, USB_ENDPTCTRL(1));
489 usb_config_value = 0;
490 the_gadget->notify(the_gadget, UDC_EVENT_OFFLINE);
491 }
492 setup_ack();
493 usb_online = s.value ? 1 : 0;
494 usb_status(s.value ? 1 : 0, usb_highspeed);
495 return;
496 case SETUP(DEVICE_WRITE, SET_ADDRESS):
497 /* write address delayed (will take effect
Ajay Dudanib01e5062011-12-03 23:23:42 -0800498 ** after the next IN txn)
499 */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800500 writel((s.value << 25) | (1 << 24), USB_DEVICEADDR);
501 setup_ack();
502 return;
503 case SETUP(INTERFACE_WRITE, SET_INTERFACE):
504 /* if we ack this everything hangs */
505 /* per spec, STALL is valid if there is not alt func */
506 goto stall;
Subbaraman Narayanamurthyd8b7afc2011-06-30 15:42:41 -0700507 case SETUP(DEVICE_WRITE, SET_FEATURE):
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700508 test_mode = s.index;
Subbaraman Narayanamurthyd8b7afc2011-06-30 15:42:41 -0700509 setup_ack();
Subbaraman Narayanamurthyd8b7afc2011-06-30 15:42:41 -0700510 return;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800511 case SETUP(ENDPOINT_WRITE, CLEAR_FEATURE):
512 {
513 struct udc_endpoint *ept;
514 unsigned num = s.index & 15;
515 unsigned in = !!(s.index & 0x80);
Amol Jadi4421e652011-06-16 15:00:48 -0700516
Ajay Dudanib01e5062011-12-03 23:23:42 -0800517 if ((s.value == 0) && (s.length == 0)) {
518 DBG("clr feat %d %d\n", num, in);
519 for (ept = ept_list; ept; ept = ept->next) {
520 if ((ept->num == num)
521 && (ept->in == in)) {
522 endpoint_enable(ept, 1);
523 setup_ack();
524 return;
525 }
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800526 }
527 }
Ajay Dudanib01e5062011-12-03 23:23:42 -0800528 break;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800529 }
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800530 }
531
532 dprintf(INFO, "STALL %s %d %d %d %d %d\n",
533 reqname(s.request),
534 s.type, s.request, s.value, s.index, s.length);
535
Ajay Dudanib01e5062011-12-03 23:23:42 -0800536 stall:
537 writel((1 << 16) | (1 << 0), USB_ENDPTCTRL(ept->num));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800538}
539
540unsigned ulpi_read(unsigned reg)
541{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800542 /* initiate read operation */
543 writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg), USB_ULPI_VIEWPORT);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800544
Ajay Dudanib01e5062011-12-03 23:23:42 -0800545 /* wait for completion */
546 while (readl(USB_ULPI_VIEWPORT) & ULPI_RUN) ;
Amol Jadi4421e652011-06-16 15:00:48 -0700547
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800548 return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT));
549}
550
551void ulpi_write(unsigned val, unsigned reg)
552{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800553 /* initiate write operation */
Amol Jadi4421e652011-06-16 15:00:48 -0700554 writel(ULPI_RUN | ULPI_WRITE |
Ajay Dudanib01e5062011-12-03 23:23:42 -0800555 ULPI_ADDR(reg) | ULPI_DATA(val), USB_ULPI_VIEWPORT);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800556
Ajay Dudanib01e5062011-12-03 23:23:42 -0800557 /* wait for completion */
558 while (readl(USB_ULPI_VIEWPORT) & ULPI_RUN) ;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800559}
Ajay Dudani5a1e3302009-12-05 13:19:17 -0800560
Shashank Mittal23b8f422010-04-16 19:27:21 -0700561#define USB_CLK 0x00902910
562#define USB_PHY_CLK 0x00902E20
563#define CLK_RESET_ASSERT 0x1
564#define CLK_RESET_DEASSERT 0x0
565#define CLK_RESET(x,y) writel((y), (x));
566
567static int msm_otg_xceiv_reset()
568{
569 CLK_RESET(USB_CLK, CLK_RESET_ASSERT);
570 CLK_RESET(USB_PHY_CLK, CLK_RESET_ASSERT);
571 mdelay(20);
572 CLK_RESET(USB_PHY_CLK, CLK_RESET_DEASSERT);
573 CLK_RESET(USB_CLK, CLK_RESET_DEASSERT);
574 mdelay(20);
575
576 /* select ULPI phy */
577 writel(0x81000000, USB_PORTSC);
578 return 0;
579}
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800580
581void board_usb_init(void);
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700582void board_ulpi_init(void)
583{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800584 unsigned int reg;
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700585
586#ifdef PLATFORM_MSM7X27A
587 ulpi_read(0x31);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800588 dprintf(INFO, " Value of ulpi read 0x31 is %08x\n", reg);
589 /* todo : the write back value should be calculated according to
590 * reg &= 0xF3 but sometimes the value that is read initially
591 * doesnt look right
592 */
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700593 ulpi_write(0x4A, 0x31);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800594 reg = ulpi_read(0x31);
595 dprintf(INFO, " Value of ulpi read 0x31 after write is %08x\n", reg);
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700596
597 reg = ulpi_read(0x32);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800598 dprintf(INFO, " Value of ulpi read 0x32 is %08x\n", reg);
599 ulpi_write(0x30, 0x32);
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700600 reg = ulpi_read(0x32);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800601 dprintf(INFO, " Value of ulpi read 0x32 after write is %08x\n", reg);
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700602
603 reg = ulpi_read(0x36);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800604 dprintf(INFO, " Value of ulpi read 0x36 is %08x\n", reg);
605 ulpi_write(reg | 0x2, 0x36);
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700606 reg = ulpi_read(0x36);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800607 dprintf(INFO, " Value of ulpi read 0x36 after write is %08x\n", reg);
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700608
609#endif
610#ifdef PLATFORM_MSM8X60
611
Ajay Dudanib01e5062011-12-03 23:23:42 -0800612 reg = ulpi_read(0x32);
613 dprintf(INFO, " Value of ulpi read 0x32 is %08x\n", reg);
614 ulpi_write(0x30, 0x32);
615 reg = ulpi_read(0x32);
616 dprintf(INFO, " Value of ulpi read 0x32 after write is %08x\n", reg);
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700617
Ajay Dudanib01e5062011-12-03 23:23:42 -0800618 reg = ulpi_read(0x36);
619 dprintf(INFO, " Value of ulpi read 0x36 is %08x\n", reg);
620 ulpi_write(reg | 0x2, 0x36);
621 reg = ulpi_read(0x36);
622 dprintf(INFO, " Value of ulpi read 0x36 aafter write is %08x\n", reg);
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700623#endif
624}
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800625
Amol Jadi4421e652011-06-16 15:00:48 -0700626int udc_init(struct udc_device *dev)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800627{
Amol Jadi4421e652011-06-16 15:00:48 -0700628 DBG("udc_init():\n");
629
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800630 hsusb_clock_init();
631
Subbaraman Narayanamurthyb0111a12011-02-03 14:24:16 -0800632#ifdef PLATFORM_MSM8X60
Subbaraman Narayanamurthyb0111a12011-02-03 14:24:16 -0800633 /* Configure GPIOs for HS_USB */
634 hsusb_gpio_init();
635#endif
Subbaraman Narayanamurthyb0111a12011-02-03 14:24:16 -0800636
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800637 epts = memalign(4096, 4096);
638
639 dprintf(INFO, "USB init ept @ %p\n", epts);
640 memset(epts, 0, 32 * sizeof(struct ept_queue_head));
Ajay Dudanib01e5062011-12-03 23:23:42 -0800641 arch_clean_invalidate_cache_range((addr_t) epts,
642 32 * sizeof(struct ept_queue_head));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800643
Ajay Dudani232ce812009-12-02 00:14:11 -0800644 //dprintf(INFO, "USB ID %08x\n", readl(USB_ID));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800645// board_usb_init();
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800646
Ajay Dudanib01e5062011-12-03 23:23:42 -0800647 /* select ULPI phy */
Shashank Mittal23b8f422010-04-16 19:27:21 -0700648#ifdef PLATFORM_MSM8X60
649 msm_otg_xceiv_reset();
650#else
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800651 writel(0x81000000, USB_PORTSC);
Shashank Mittal23b8f422010-04-16 19:27:21 -0700652#endif
Ajay Dudanib01e5062011-12-03 23:23:42 -0800653 /* RESET */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800654 writel(0x00080002, USB_USBCMD);
655
656 thread_sleep(20);
657
Ajay Dudanib01e5062011-12-03 23:23:42 -0800658 board_ulpi_init();
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800659
Ajay Dudanib01e5062011-12-03 23:23:42 -0800660 writel((unsigned)epts, USB_ENDPOINTLISTADDR);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800661
Ajay Dudanib01e5062011-12-03 23:23:42 -0800662 /* select DEVICE mode */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800663 writel(0x02, USB_USBMODE);
664
665 writel(0xffffffff, USB_ENDPTFLUSH);
666 thread_sleep(20);
667
668 ep0out = _udc_endpoint_alloc(0, 0, 64);
669 ep0in = _udc_endpoint_alloc(0, 1, 64);
670 ep0req = udc_request_alloc();
671 ep0req->buf = malloc(4096);
672
673 {
674 /* create and register a language table descriptor */
675 /* language 0x0409 is US English */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800676 struct udc_descriptor *desc =
677 udc_descriptor_alloc(TYPE_STRING, 0, 4);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800678 desc->data[2] = 0x09;
679 desc->data[3] = 0x04;
680 udc_descriptor_register(desc);
681 }
Amol Jadi4421e652011-06-16 15:00:48 -0700682
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800683 the_device = dev;
684 return 0;
685}
686
687enum handler_return udc_interrupt(void *arg)
688{
689 struct udc_endpoint *ept;
690 unsigned ret = INT_NO_RESCHEDULE;
691 unsigned n = readl(USB_USBSTS);
692 writel(n, USB_USBSTS);
Amol Jadi4421e652011-06-16 15:00:48 -0700693
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800694 n &= (STS_SLI | STS_URI | STS_PCI | STS_UI | STS_UEI);
695
Amol Jadi4421e652011-06-16 15:00:48 -0700696 DBG("\nudc_interrupt():\n");
697
698 if (n == 0) {
699 DBG("n = 0\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800700 return ret;
Amol Jadi4421e652011-06-16 15:00:48 -0700701 }
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800702
703 if (n & STS_URI) {
Amol Jadi4421e652011-06-16 15:00:48 -0700704 DBG("STS_URI\n");
705
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800706 writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE);
707 writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT);
708 writel(0xffffffff, USB_ENDPTFLUSH);
709 writel(0, USB_ENDPTCTRL(1));
Amol Jadi4421e652011-06-16 15:00:48 -0700710 dprintf(INFO, "-- reset --\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800711 usb_online = 0;
712 usb_config_value = 0;
713 the_gadget->notify(the_gadget, UDC_EVENT_OFFLINE);
714
715 /* error out any pending reqs */
716 for (ept = ept_list; ept; ept = ept->next) {
717 /* ensure that ept_complete considers
718 * this to be an error state
719 */
720 if (ept->req) {
721 ept->req->item->info = INFO_HALTED;
722 handle_ept_complete(ept);
723 }
724 }
725 usb_status(0, usb_highspeed);
726 }
727 if (n & STS_SLI) {
Ajay Dudani35f686f2011-07-22 13:12:09 -0700728 DBG("-- suspend --\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800729 }
730 if (n & STS_PCI) {
Amol Jadi4421e652011-06-16 15:00:48 -0700731 dprintf(INFO, "-- portchange --\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800732 unsigned spd = (readl(USB_PORTSC) >> 26) & 3;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800733 if (spd == 2) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800734 usb_highspeed = 1;
735 } else {
736 usb_highspeed = 0;
737 }
738 }
739 if (n & STS_UEI) {
Amol Jadi4421e652011-06-16 15:00:48 -0700740 DBG("STS_UEI\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800741 dprintf(INFO, "<UEI %x>\n", readl(USB_ENDPTCOMPLETE));
742 }
743#if 0
744 DBG("STS: ");
Ajay Dudanib01e5062011-12-03 23:23:42 -0800745 if (n & STS_UEI)
746 DBG("ERROR ");
747 if (n & STS_SLI)
748 DBG("SUSPEND ");
749 if (n & STS_URI)
750 DBG("RESET ");
751 if (n & STS_PCI)
752 DBG("PORTCHANGE ");
753 if (n & STS_UI)
754 DBG("USB ");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800755 DBG("\n");
756#endif
757 if ((n & STS_UI) || (n & STS_UEI)) {
Amol Jadi4421e652011-06-16 15:00:48 -0700758 DBG("STS_UI and UEI \n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800759 n = readl(USB_ENDPTSETUPSTAT);
760 if (n & EPT_RX(0)) {
761 handle_setup(ep0out);
762 ret = INT_RESCHEDULE;
763 }
764
765 n = readl(USB_ENDPTCOMPLETE);
766 if (n != 0) {
767 writel(n, USB_ENDPTCOMPLETE);
768 }
769
Ajay Dudanib01e5062011-12-03 23:23:42 -0800770 for (ept = ept_list; ept; ept = ept->next) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800771 if (n & ept->bit) {
772 handle_ept_complete(ept);
773 ret = INT_RESCHEDULE;
774 }
775 }
776 }
777 return ret;
778}
779
780int udc_register_gadget(struct udc_gadget *gadget)
781{
782 if (the_gadget) {
783 dprintf(CRITICAL, "only one gadget supported\n");
784 return -1;
785 }
786 the_gadget = gadget;
787 return 0;
788}
789
790static void udc_ept_desc_fill(struct udc_endpoint *ept, unsigned char *data)
791{
792 data[0] = 7;
793 data[1] = TYPE_ENDPOINT;
794 data[2] = ept->num | (ept->in ? 0x80 : 0x00);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800795 data[3] = 0x02; /* bulk -- the only kind we support */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800796 data[4] = ept->maxpkt;
797 data[5] = ept->maxpkt >> 8;
798 data[6] = ept->in ? 0x00 : 0x01;
799}
800
801static unsigned udc_ifc_desc_size(struct udc_gadget *g)
802{
803 return 9 + g->ifc_endpoints * 7;
804}
805
806static void udc_ifc_desc_fill(struct udc_gadget *g, unsigned char *data)
807{
808 unsigned n;
809
810 data[0] = 0x09;
811 data[1] = TYPE_INTERFACE;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800812 data[2] = 0x00; /* ifc number */
813 data[3] = 0x00; /* alt number */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800814 data[4] = g->ifc_endpoints;
815 data[5] = g->ifc_class;
816 data[6] = g->ifc_subclass;
817 data[7] = g->ifc_protocol;
818 data[8] = udc_string_desc_alloc(g->ifc_string);
819
820 data += 9;
821 for (n = 0; n < g->ifc_endpoints; n++) {
822 udc_ept_desc_fill(g->ept[n], data);
823 data += 7;
824 }
825}
826
827int udc_start(void)
828{
829 struct udc_descriptor *desc;
830 unsigned char *data;
831 unsigned size;
832
Chandan Uddaraju40b227d2010-08-03 19:25:41 -0700833 dprintf(ALWAYS, "udc_start()\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800834
835 if (!the_device) {
836 dprintf(CRITICAL, "udc cannot start before init\n");
837 return -1;
838 }
839 if (!the_gadget) {
840 dprintf(CRITICAL, "udc has no gadget registered\n");
841 return -1;
842 }
843
844 /* create our device descriptor */
845 desc = udc_descriptor_alloc(TYPE_DEVICE, 0, 18);
846 data = desc->data;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800847 data[2] = 0x00; /* usb spec minor rev */
848 data[3] = 0x02; /* usb spec major rev */
849 data[4] = 0x00; /* class */
850 data[5] = 0x00; /* subclass */
851 data[6] = 0x00; /* protocol */
852 data[7] = 0x40; /* max packet size on ept 0 */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800853 memcpy(data + 8, &the_device->vendor_id, sizeof(short));
854 memcpy(data + 10, &the_device->product_id, sizeof(short));
855 memcpy(data + 12, &the_device->version_id, sizeof(short));
856 data[14] = udc_string_desc_alloc(the_device->manufacturer);
857 data[15] = udc_string_desc_alloc(the_device->product);
858 data[16] = udc_string_desc_alloc(the_device->serialno);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800859 data[17] = 1; /* number of configurations */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800860 udc_descriptor_register(desc);
861
862 /* create our configuration descriptor */
863 size = 9 + udc_ifc_desc_size(the_gadget);
864 desc = udc_descriptor_alloc(TYPE_CONFIGURATION, 0, size);
865 data = desc->data;
866 data[0] = 0x09;
867 data[2] = size;
868 data[3] = size >> 8;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800869 data[4] = 0x01; /* number of interfaces */
870 data[5] = 0x01; /* configuration value */
871 data[6] = 0x00; /* configuration string */
872 data[7] = 0x80; /* attributes */
873 data[8] = 0x80; /* max power (250ma) -- todo fix this */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800874 udc_ifc_desc_fill(the_gadget, data + 9);
875 udc_descriptor_register(desc);
876
Ajay Dudanib01e5062011-12-03 23:23:42 -0800877 register_int_handler(INT_USB_HS, udc_interrupt, (void *)0);
Amol Jadica4f4c92011-01-13 20:19:34 -0800878 writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR);
879 unmask_interrupt(INT_USB_HS);
880
Ajay Dudanib01e5062011-12-03 23:23:42 -0800881 /* go to RUN mode (D+ pullup enable) */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800882 writel(0x00080001, USB_USBCMD);
Amol Jadica4f4c92011-01-13 20:19:34 -0800883
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800884 return 0;
885}
886
887int udc_stop(void)
888{
Greg Griscod2471ef2011-07-14 13:00:42 -0700889#ifdef PLATFORM_MSM8X60
Shashank Mittal237301c2010-08-24 19:11:46 -0700890 int val;
Greg Griscod2471ef2011-07-14 13:00:42 -0700891#endif
Ajay Dudanib01e5062011-12-03 23:23:42 -0800892 writel(0, USB_USBINTR);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800893 mask_interrupt(INT_USB_HS);
894
Ajay Dudanib01e5062011-12-03 23:23:42 -0800895 /* disable pullup */
Shashank Mittald8c42bf2010-06-09 15:44:28 -0700896 writel(0x00080000, USB_USBCMD);
Shashank Mittal237301c2010-08-24 19:11:46 -0700897#ifdef PLATFORM_MSM8X60
898 /* Voting down PLL8 */
899 val = readl(0x009034C0);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800900 val &= ~(1 << 8);
Shashank Mittal237301c2010-08-24 19:11:46 -0700901 writel(val, 0x009034C0);
902#endif
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800903 thread_sleep(10);
904
905 return 0;
906}