blob: bfceeffd4f12959dc8bc3a1b67eac4d789ee873b [file] [log] [blame]
Brian Swetland3e7e21a2009-01-19 19:41:24 -08001/*
2 * Copyright (c) 2008, Google Inc.
3 * All rights reserved.
4 *
Deepa Dinamani0bf2f442012-10-19 11:41:06 -07005 * Copyright (c) 2009-2012, The Linux Foundation. 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
Deepa Dinamani0bf2f442012-10-19 11:41:06 -07008 * modification, are permitted provided that the following conditions are
9 * 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
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials provided
15 * with the distribution.
16 * * Neither the name of The Linux Foundation nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
Brian Swetland3e7e21a2009-01-19 19:41:24 -080019 *
Deepa Dinamani0bf2f442012-10-19 11:41:06 -070020 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
21 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
27 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
29 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
30 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Brian Swetland3e7e21a2009-01-19 19:41:24 -080031 */
32
33#include <string.h>
34#include <stdlib.h>
35#include <debug.h>
Deepa Dinamani0bf2f442012-10-19 11:41:06 -070036#include <platform.h>
Brian Swetland3e7e21a2009-01-19 19:41:24 -080037#include <platform/iomap.h>
38#include <platform/irqs.h>
39#include <platform/interrupts.h>
Greg Griscod2471ef2011-07-14 13:00:42 -070040#include <platform/timer.h>
Brian Swetland3e7e21a2009-01-19 19:41:24 -080041#include <kernel/thread.h>
42#include <reg.h>
Brian Swetland3e7e21a2009-01-19 19:41:24 -080043#include <dev/udc.h>
Brian Swetland3e7e21a2009-01-19 19:41:24 -080044#include "hsusb.h"
45
Hanumant Singh5322c742012-12-12 15:40:09 -080046#define MAX_TD_XFER_SIZE (16 * 1024)
47
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;
Ajay Dudanib01e5062011-12-03 23:23:42 -080053 unsigned short tag; /* ((TYPE << 8) | NUM) */
54 unsigned short len; /* total length */
Brian Swetland3e7e21a2009-01-19 19:41:24 -080055 unsigned char data[0];
56};
57
Ajay Dudanib01e5062011-12-03 23:23:42 -080058struct udc_descriptor *udc_descriptor_alloc(unsigned type, unsigned num,
59 unsigned len)
Brian Swetland3e7e21a2009-01-19 19:41:24 -080060{
61 struct udc_descriptor *desc;
62 if ((len > 255) || (len < 2) || (num > 255) || (type > 255))
63 return 0;
64
Ajay Dudanib01e5062011-12-03 23:23:42 -080065 if (!(desc = malloc(sizeof(struct udc_descriptor) + len)))
Brian Swetland3e7e21a2009-01-19 19:41:24 -080066 return 0;
67
68 desc->next = 0;
69 desc->tag = (type << 8) | num;
70 desc->len = len;
71 desc->data[0] = len;
72 desc->data[1] = type;
73
74 return desc;
75}
76
77static struct udc_descriptor *desc_list = 0;
78static unsigned next_string_id = 1;
79
80void udc_descriptor_register(struct udc_descriptor *desc)
81{
82 desc->next = desc_list;
83 desc_list = desc;
84}
85
86unsigned udc_string_desc_alloc(const char *str)
87{
88 unsigned len;
89 struct udc_descriptor *desc;
90 unsigned char *data;
91
92 if (next_string_id > 255)
93 return 0;
94
95 if (!str)
96 return 0;
97
98 len = strlen(str);
99 desc = udc_descriptor_alloc(TYPE_STRING, next_string_id, len * 2 + 2);
100 if (!desc)
101 return 0;
102 next_string_id++;
103
104 /* expand ascii string to utf16 */
105 data = desc->data + 2;
106 while (len-- > 0) {
107 *data++ = *str++;
108 *data++ = 0;
109 }
110
111 udc_descriptor_register(desc);
112 return desc->tag & 0xff;
113}
114
115/* end of common code */
116
Ajay Dudani7d605522010-10-01 19:52:37 -0700117__WEAK void hsusb_clock_init(void)
118{
Greg Griscod6250552011-06-29 14:40:23 -0700119 return;
Ajay Dudani7d605522010-10-01 19:52:37 -0700120}
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800121
122#if 1
123#define DBG(x...) do {} while(0)
124#else
Amol Jadida118b92012-07-06 19:53:18 -0700125#define DBG(x...) dprintf(ALWAYS, x)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800126#endif
127
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800128#define usb_status(a,b)
129
130struct usb_request {
131 struct udc_request req;
132 struct ept_queue_item *item;
133};
Amol Jadi4421e652011-06-16 15:00:48 -0700134
Ajay Dudanib01e5062011-12-03 23:23:42 -0800135struct udc_endpoint {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800136 struct udc_endpoint *next;
137 unsigned bit;
138 struct ept_queue_head *head;
139 struct usb_request *req;
140 unsigned char num;
141 unsigned char in;
142 unsigned short maxpkt;
143};
144
145struct udc_endpoint *ept_list = 0;
146struct ept_queue_head *epts = 0;
147
148static int usb_online = 0;
149static int usb_highspeed = 0;
150
151static struct udc_device *the_device;
152static struct udc_gadget *the_gadget;
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700153static unsigned test_mode = 0;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800154
Ajay Dudanib01e5062011-12-03 23:23:42 -0800155struct udc_endpoint *_udc_endpoint_alloc(unsigned num, unsigned in,
156 unsigned max_pkt)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800157{
158 struct udc_endpoint *ept;
159 unsigned cfg;
160
Hanumant Singh9d519092012-12-06 21:59:52 -0800161 ept = memalign(CACHE_LINE, ROUNDUP(sizeof(*ept), CACHE_LINE));
Amol Jadi4421e652011-06-16 15:00:48 -0700162
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800163 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
Ajay Dudanib01e5062011-12-03 23:23:42 -0800170 if (ept->in) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800171 ept->bit = EPT_TX(ept->num);
172 } else {
173 ept->bit = EPT_RX(ept->num);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800174 if (num == 0)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800175 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;
Amol Jadi4421e652011-06-16 15:00:48 -0700183
184 DBG("ept%d %s @%p/%p max=%d bit=%x\n",
Ajay Dudanib01e5062011-12-03 23:23:42 -0800185 num, in ? "in" : "out", ept, ept->head, max_pkt, ept->bit);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800186
187 return ept;
188}
189
190static unsigned ept_alloc_table = EPT_TX(0) | EPT_RX(0);
191
192struct udc_endpoint *udc_endpoint_alloc(unsigned type, unsigned maxpkt)
193{
194 struct udc_endpoint *ept;
195 unsigned n;
196 unsigned in;
197
198 if (type == UDC_TYPE_BULK_IN) {
199 in = 1;
200 } else if (type == UDC_TYPE_BULK_OUT) {
201 in = 0;
202 } else {
203 return 0;
204 }
205
206 for (n = 1; n < 16; n++) {
207 unsigned bit = in ? EPT_TX(n) : EPT_RX(n);
208 if (ept_alloc_table & bit)
209 continue;
210 ept = _udc_endpoint_alloc(n, in, maxpkt);
211 if (ept)
212 ept_alloc_table |= bit;
213 return ept;
214 }
215 return 0;
216}
217
218void udc_endpoint_free(struct udc_endpoint *ept)
219{
220 /* todo */
221}
222
223static void endpoint_enable(struct udc_endpoint *ept, unsigned yes)
224{
225 unsigned n = readl(USB_ENDPTCTRL(ept->num));
226
Ajay Dudanib01e5062011-12-03 23:23:42 -0800227 if (yes) {
228 if (ept->in) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800229 n |= (CTRL_TXE | CTRL_TXR | CTRL_TXT_BULK);
230 } else {
231 n |= (CTRL_RXE | CTRL_RXR | CTRL_RXT_BULK);
232 }
233
Ajay Dudanib01e5062011-12-03 23:23:42 -0800234 if (ept->num != 0) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800235 /* XXX should be more dynamic... */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800236 if (usb_highspeed) {
237 ept->head->config =
238 CONFIG_MAX_PKT(512) | CONFIG_ZLT;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800239 } else {
Ajay Dudanib01e5062011-12-03 23:23:42 -0800240 ept->head->config =
241 CONFIG_MAX_PKT(64) | CONFIG_ZLT;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800242 }
243 }
244 }
245 writel(n, USB_ENDPTCTRL(ept->num));
246}
247
248struct udc_request *udc_request_alloc(void)
249{
250 struct usb_request *req;
Hanumant Singh9d519092012-12-06 21:59:52 -0800251 req = memalign(CACHE_LINE, ROUNDUP(sizeof(*req), CACHE_LINE));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800252 req->req.buf = 0;
253 req->req.length = 0;
Hanumant Singh9d519092012-12-06 21:59:52 -0800254 req->item = memalign(CACHE_LINE, ROUNDUP(sizeof(struct ept_queue_item),
255 CACHE_LINE));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800256 return &req->req;
257}
258
259void udc_request_free(struct udc_request *req)
260{
261 free(req);
262}
263
Hanumant Singh5322c742012-12-12 15:40:09 -0800264/*
265 * Assumes that TDs allocated already are not freed.
266 * But it can handle case where TDs are freed as well.
267 */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800268int udc_request_queue(struct udc_endpoint *ept, struct udc_request *_req)
269{
Hanumant Singh5322c742012-12-12 15:40:09 -0800270 unsigned xfer = 0;
271 struct ept_queue_item *item, *curr_item;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800272 struct usb_request *req = (struct usb_request *)_req;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800273 unsigned phys = (unsigned)req->req.buf;
Hanumant Singh5322c742012-12-12 15:40:09 -0800274 unsigned len = req->req.length;
275 unsigned int count = 0;
Amol Jadi4421e652011-06-16 15:00:48 -0700276
Hanumant Singh5322c742012-12-12 15:40:09 -0800277 curr_item = NULL;
278 xfer = (len > MAX_TD_XFER_SIZE) ? MAX_TD_XFER_SIZE : len;
279 /*
280 * First TD allocated during request allocation
281 */
282 item = req->item;
283 item->info = INFO_BYTES(xfer) | INFO_ACTIVE;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800284 item->page0 = phys;
285 item->page1 = (phys & 0xfffff000) + 0x1000;
Shashank Mittal1cc65b02011-12-20 15:30:17 -0800286 item->page2 = (phys & 0xfffff000) + 0x2000;
287 item->page3 = (phys & 0xfffff000) + 0x3000;
288 item->page4 = (phys & 0xfffff000) + 0x4000;
Hanumant Singh5322c742012-12-12 15:40:09 -0800289 phys += xfer;
290 curr_item = item;
291 len -= xfer;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800292
Hanumant Singh5322c742012-12-12 15:40:09 -0800293 /*
294 * If transfer length is more then
295 * accomodate by 1 TD
296 * we add more transfer descriptors
297 */
298 while (len > 0) {
299 xfer = (len > MAX_TD_XFER_SIZE) ? MAX_TD_XFER_SIZE : len;
300 if (curr_item->next == TERMINATE) {
301 /*
302 * Allocate new TD only if chain doesnot
303 * exist already
304 */
305 item = memalign(CACHE_LINE,
306 ROUNDUP(sizeof(struct ept_queue_item), CACHE_LINE));
307 if (!item) {
308 dprintf(ALWAYS, "allocate USB item fail ept%d"
309 "%s queue\n",
310 "td count = %d\n",
311 ept->num,
312 ept->in ? "in" : "out",
313 count);
314 return -1;
315 } else {
316 count ++;
317 curr_item->next = PA(item);
318 item->next = TERMINATE;
319 }
320 } else
321 /* Since next TD in chain already exists */
322 item = VA(curr_item->next);
323
324 /* Update TD with transfer information */
325 item->info = INFO_BYTES(xfer) | INFO_ACTIVE;
326 item->page0 = phys;
327 item->page1 = (phys & 0xfffff000) + 0x1000;
328 item->page2 = (phys & 0xfffff000) + 0x2000;
329 item->page3 = (phys & 0xfffff000) + 0x3000;
330 item->page4 = (phys & 0xfffff000) + 0x4000;
331
332 curr_item = item;
333 len -= xfer;
334 phys += xfer;
335 }
336
337 /* Terminate and set interrupt for last TD */
338 curr_item->next = TERMINATE;
339 curr_item->info |= INFO_IOC;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800340 enter_critical_section();
Hanumant Singh5322c742012-12-12 15:40:09 -0800341 ept->head->next = PA(req->item);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800342 ept->head->info = 0;
343 ept->req = req;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800344 arch_clean_invalidate_cache_range((addr_t) ept,
345 sizeof(struct udc_endpoint));
346 arch_clean_invalidate_cache_range((addr_t) ept->head,
347 sizeof(struct ept_queue_head));
348 arch_clean_invalidate_cache_range((addr_t) ept->req,
349 sizeof(struct usb_request));
Deepa Dinamani0bf2f442012-10-19 11:41:06 -0700350 arch_clean_invalidate_cache_range((addr_t) VA(req->req.buf),
Ajay Dudanib01e5062011-12-03 23:23:42 -0800351 req->req.length);
Hanumant Singh5322c742012-12-12 15:40:09 -0800352
353 item = req->item;
354 /* Write all TD's to memory from cache */
355 while (item != NULL) {
356 curr_item = item;
357 if (curr_item->next == TERMINATE)
358 item = NULL;
359 else
360 item = curr_item->next;
361 arch_clean_invalidate_cache_range((addr_t) curr_item,
Ajay Dudanib01e5062011-12-03 23:23:42 -0800362 sizeof(struct ept_queue_item));
Hanumant Singh5322c742012-12-12 15:40:09 -0800363 }
Amol Jadi4421e652011-06-16 15:00:48 -0700364
Ajay Dudanib01e5062011-12-03 23:23:42 -0800365 DBG("ept%d %s queue req=%p\n", ept->num, ept->in ? "in" : "out", req);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800366 writel(ept->bit, USB_ENDPTPRIME);
367 exit_critical_section();
368 return 0;
369}
370
371static void handle_ept_complete(struct udc_endpoint *ept)
372{
373 struct ept_queue_item *item;
Hanumant Singh5322c742012-12-12 15:40:09 -0800374 unsigned actual, total_len;
375 int status, len;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800376 struct usb_request *req;
Hanumant Singh5322c742012-12-12 15:40:09 -0800377 void *buf;
Amol Jadi4421e652011-06-16 15:00:48 -0700378
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800379 DBG("ept%d %s complete req=%p\n",
Ajay Dudanib01e5062011-12-03 23:23:42 -0800380 ept->num, ept->in ? "in" : "out", ept->req);
Amol Jadi4421e652011-06-16 15:00:48 -0700381
Hanumant Singh5322c742012-12-12 15:40:09 -0800382 arch_invalidate_cache_range((addr_t) ept,
Ajay Dudanib01e5062011-12-03 23:23:42 -0800383 sizeof(struct udc_endpoint));
Hanumant Singh5322c742012-12-12 15:40:09 -0800384 req = VA(ept->req);
385 arch_invalidate_cache_range((addr_t) ept->req,
Ajay Dudanib01e5062011-12-03 23:23:42 -0800386 sizeof(struct usb_request));
Amol Jadi4421e652011-06-16 15:00:48 -0700387
Ajay Dudanib01e5062011-12-03 23:23:42 -0800388 if (req) {
Hanumant Singh5322c742012-12-12 15:40:09 -0800389 item = VA(req->item);
390 /* total transfer length for transacation */
391 total_len = req->req.length;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800392 ept->req = 0;
Hanumant Singh5322c742012-12-12 15:40:09 -0800393 actual = 0;
394 while(1) {
Amol Jadi4421e652011-06-16 15:00:48 -0700395
Hanumant Singh5322c742012-12-12 15:40:09 -0800396 do {
397 /*
398 * Must clean/invalidate cached item
399 * data before checking the status
400 * every time.
401 */
402 arch_invalidate_cache_range((addr_t)(item),
403 sizeof(
404 struct ept_queue_item));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800405
Hanumant Singh5322c742012-12-12 15:40:09 -0800406 } while(readl(&item->info) & INFO_ACTIVE);
407
408 if ((item->info) & 0xff) {
409 /* error */
410 status = -1;
411 dprintf(INFO, "EP%d/%s FAIL nfo=%x pg0=%x\n",
412 ept->num, ept->in ? "in" : "out",
413 item->info,
414 item->page0);
415 goto out;
416 }
417
418 /* Check if we are processing last TD */
419 if (item->next == TERMINATE) {
420 /*
421 * Record the data transferred for the last TD
422 */
423 actual += total_len - (item->info >> 16)
424 & 0x7FFF;
425 total_len = 0;
426 break;
427 } else {
428 /*
429 * Since we are not in last TD
430 * the total assumed transfer ascribed to this
431 * TD woulb the max possible TD transfer size
432 * (16K)
433 */
434 actual += MAX_TD_XFER_SIZE - (item->info >> 16) & 0x7FFF;
435 total_len -= MAX_TD_XFER_SIZE - (item->info >> 16) & 0x7FFF;
436 /*Move to next item in chain*/
437 item = VA(item->next);
438 }
Ajay Dudanib01e5062011-12-03 23:23:42 -0800439 }
Hanumant Singh5322c742012-12-12 15:40:09 -0800440 status = 0;
441out:
Ajay Dudanib01e5062011-12-03 23:23:42 -0800442 if (req->req.complete)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800443 req->req.complete(&req->req, actual, status);
444 }
445}
446
447static const char *reqname(unsigned r)
448{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800449 switch (r) {
450 case GET_STATUS:
451 return "GET_STATUS";
452 case CLEAR_FEATURE:
453 return "CLEAR_FEATURE";
454 case SET_FEATURE:
455 return "SET_FEATURE";
456 case SET_ADDRESS:
457 return "SET_ADDRESS";
458 case GET_DESCRIPTOR:
459 return "GET_DESCRIPTOR";
460 case SET_DESCRIPTOR:
461 return "SET_DESCRIPTOR";
462 case GET_CONFIGURATION:
463 return "GET_CONFIGURATION";
464 case SET_CONFIGURATION:
465 return "SET_CONFIGURATION";
466 case GET_INTERFACE:
467 return "GET_INTERFACE";
468 case SET_INTERFACE:
469 return "SET_INTERFACE";
470 default:
471 return "*UNKNOWN*";
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800472 }
473}
474
475static struct udc_endpoint *ep0in, *ep0out;
476static struct udc_request *ep0req;
477
Ajay Dudanib01e5062011-12-03 23:23:42 -0800478static void
479ep0_setup_ack_complete(struct udc_endpoint *ep, struct usb_request *req)
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700480{
481 uint32_t mode;
482
Ajay Dudanib01e5062011-12-03 23:23:42 -0800483 if (!test_mode)
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700484 return;
485
Ajay Dudanib01e5062011-12-03 23:23:42 -0800486 switch (test_mode) {
487 case TEST_PACKET:
488 dprintf(INFO, "Entering test mode for TST_PKT\n");
489 mode = readl(USB_PORTSC) & (~PORTSC_PTC);
490 writel(mode | PORTSC_PTC_TST_PKT, USB_PORTSC);
491 break;
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700492
Ajay Dudanib01e5062011-12-03 23:23:42 -0800493 case TEST_SE0_NAK:
494 dprintf(INFO, "Entering test mode for SE0-NAK\n");
495 mode = readl(USB_PORTSC) & (~PORTSC_PTC);
496 writel(mode | PORTSC_PTC_SE0_NAK, USB_PORTSC);
497 break;
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700498 }
499
500}
501
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800502static void setup_ack(void)
503{
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700504 ep0req->complete = ep0_setup_ack_complete;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800505 ep0req->length = 0;
506 udc_request_queue(ep0in, ep0req);
507}
508
509static void ep0in_complete(struct udc_request *req, unsigned actual, int status)
510{
511 DBG("ep0in_complete %p %d %d\n", req, actual, status);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800512 if (status == 0) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800513 req->length = 0;
514 req->complete = 0;
515 udc_request_queue(ep0out, req);
516 }
517}
518
519static void setup_tx(void *buf, unsigned len)
520{
521 DBG("setup_tx %p %d\n", buf, len);
522 memcpy(ep0req->buf, buf, len);
Deepa Dinamani0bf2f442012-10-19 11:41:06 -0700523 ep0req->buf = PA((addr_t)ep0req->buf);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800524 ep0req->complete = ep0in_complete;
525 ep0req->length = len;
526 udc_request_queue(ep0in, ep0req);
527}
528
529static unsigned char usb_config_value = 0;
530
531#define SETUP(type,request) (((type) << 8) | (request))
532
533static void handle_setup(struct udc_endpoint *ept)
534{
535 struct setup_packet s;
Amol Jadi4421e652011-06-16 15:00:48 -0700536
Ajay Dudanib01e5062011-12-03 23:23:42 -0800537 arch_clean_invalidate_cache_range((addr_t) ept->head->setup_data,
538 sizeof(struct ept_queue_head));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800539 memcpy(&s, ept->head->setup_data, sizeof(s));
540 writel(ept->bit, USB_ENDPTSETUPSTAT);
541
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800542 DBG("handle_setup type=0x%02x req=0x%02x val=%d idx=%d len=%d (%s)\n",
Ajay Dudanib01e5062011-12-03 23:23:42 -0800543 s.type, s.request, s.value, s.index, s.length, reqname(s.request));
Amol Jadi4421e652011-06-16 15:00:48 -0700544
Ajay Dudanib01e5062011-12-03 23:23:42 -0800545 switch (SETUP(s.type, s.request)) {
546 case SETUP(DEVICE_READ, GET_STATUS):
547 {
548 unsigned zero = 0;
549 if (s.length == 2) {
550 setup_tx(&zero, 2);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800551 return;
552 }
Ajay Dudanib01e5062011-12-03 23:23:42 -0800553 break;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800554 }
Ajay Dudanib01e5062011-12-03 23:23:42 -0800555 case SETUP(DEVICE_READ, GET_DESCRIPTOR):
556 {
557 struct udc_descriptor *desc;
558 /* usb_highspeed? */
559 for (desc = desc_list; desc; desc = desc->next) {
560 if (desc->tag == s.value) {
561 unsigned len = desc->len;
562 if (len > s.length)
563 len = s.length;
564 setup_tx(desc->data, len);
565 return;
566 }
567 }
568 break;
569 }
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800570 case SETUP(DEVICE_READ, GET_CONFIGURATION):
571 /* disabling this causes data transaction failures on OSX. Why? */
572 if ((s.value == 0) && (s.index == 0) && (s.length == 1)) {
573 setup_tx(&usb_config_value, 1);
574 return;
575 }
576 break;
577 case SETUP(DEVICE_WRITE, SET_CONFIGURATION):
578 if (s.value == 1) {
579 struct udc_endpoint *ept;
580 /* enable endpoints */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800581 for (ept = ept_list; ept; ept = ept->next) {
Amol Jadi4421e652011-06-16 15:00:48 -0700582 if (ept->num == 0)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800583 continue;
584 endpoint_enable(ept, s.value);
585 }
586 usb_config_value = 1;
587 the_gadget->notify(the_gadget, UDC_EVENT_ONLINE);
588 } else {
589 writel(0, USB_ENDPTCTRL(1));
590 usb_config_value = 0;
591 the_gadget->notify(the_gadget, UDC_EVENT_OFFLINE);
592 }
593 setup_ack();
594 usb_online = s.value ? 1 : 0;
595 usb_status(s.value ? 1 : 0, usb_highspeed);
596 return;
597 case SETUP(DEVICE_WRITE, SET_ADDRESS):
598 /* write address delayed (will take effect
Ajay Dudanib01e5062011-12-03 23:23:42 -0800599 ** after the next IN txn)
600 */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800601 writel((s.value << 25) | (1 << 24), USB_DEVICEADDR);
602 setup_ack();
603 return;
604 case SETUP(INTERFACE_WRITE, SET_INTERFACE):
605 /* if we ack this everything hangs */
606 /* per spec, STALL is valid if there is not alt func */
607 goto stall;
Subbaraman Narayanamurthyd8b7afc2011-06-30 15:42:41 -0700608 case SETUP(DEVICE_WRITE, SET_FEATURE):
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700609 test_mode = s.index;
Subbaraman Narayanamurthyd8b7afc2011-06-30 15:42:41 -0700610 setup_ack();
Subbaraman Narayanamurthyd8b7afc2011-06-30 15:42:41 -0700611 return;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800612 case SETUP(ENDPOINT_WRITE, CLEAR_FEATURE):
613 {
614 struct udc_endpoint *ept;
615 unsigned num = s.index & 15;
616 unsigned in = !!(s.index & 0x80);
Amol Jadi4421e652011-06-16 15:00:48 -0700617
Ajay Dudanib01e5062011-12-03 23:23:42 -0800618 if ((s.value == 0) && (s.length == 0)) {
619 DBG("clr feat %d %d\n", num, in);
620 for (ept = ept_list; ept; ept = ept->next) {
621 if ((ept->num == num)
622 && (ept->in == in)) {
623 endpoint_enable(ept, 1);
624 setup_ack();
625 return;
626 }
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800627 }
628 }
Ajay Dudanib01e5062011-12-03 23:23:42 -0800629 break;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800630 }
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800631 }
632
633 dprintf(INFO, "STALL %s %d %d %d %d %d\n",
634 reqname(s.request),
635 s.type, s.request, s.value, s.index, s.length);
636
Ajay Dudanib01e5062011-12-03 23:23:42 -0800637 stall:
638 writel((1 << 16) | (1 << 0), USB_ENDPTCTRL(ept->num));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800639}
640
641unsigned ulpi_read(unsigned reg)
642{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800643 /* initiate read operation */
644 writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg), USB_ULPI_VIEWPORT);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800645
Ajay Dudanib01e5062011-12-03 23:23:42 -0800646 /* wait for completion */
647 while (readl(USB_ULPI_VIEWPORT) & ULPI_RUN) ;
Amol Jadi4421e652011-06-16 15:00:48 -0700648
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800649 return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT));
650}
651
652void ulpi_write(unsigned val, unsigned reg)
653{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800654 /* initiate write operation */
Amol Jadi4421e652011-06-16 15:00:48 -0700655 writel(ULPI_RUN | ULPI_WRITE |
Ajay Dudanib01e5062011-12-03 23:23:42 -0800656 ULPI_ADDR(reg) | ULPI_DATA(val), USB_ULPI_VIEWPORT);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800657
Ajay Dudanib01e5062011-12-03 23:23:42 -0800658 /* wait for completion */
659 while (readl(USB_ULPI_VIEWPORT) & ULPI_RUN) ;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800660}
Ajay Dudani5a1e3302009-12-05 13:19:17 -0800661
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800662
Amol Jadi4421e652011-06-16 15:00:48 -0700663int udc_init(struct udc_device *dev)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800664{
Amol Jadi4421e652011-06-16 15:00:48 -0700665 DBG("udc_init():\n");
666
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800667 hsusb_clock_init();
668
Amol Jadida118b92012-07-06 19:53:18 -0700669 /* Do any target specific intialization like GPIO settings,
670 * LDO, PHY configuration etc. needed before USB port can be used.
671 */
672 target_usb_init();
673
Amol Jadida118b92012-07-06 19:53:18 -0700674 /* RESET */
Deepa Dinamani0687ecd2012-08-10 16:00:26 -0700675 writel(0x00080000, USB_USBCMD);
Amol Jadida118b92012-07-06 19:53:18 -0700676
677 thread_sleep(20);
Subbaraman Narayanamurthyb0111a12011-02-03 14:24:16 -0800678
Deepa Dinamani0687ecd2012-08-10 16:00:26 -0700679 while((readl(USB_USBCMD)&2));
680
681 /* select ULPI phy */
682 writel(0x80000000, USB_PORTSC);
683
684 /* USB_OTG_HS_AHB_BURST */
685 writel(0x0, USB_SBUSCFG);
686
687 /* USB_OTG_HS_AHB_MODE: HPROT_MODE */
688 /* Bus access related config. */
689 writel(0x08, USB_AHB_MODE);
690
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800691 epts = memalign(4096, 4096);
692
693 dprintf(INFO, "USB init ept @ %p\n", epts);
694 memset(epts, 0, 32 * sizeof(struct ept_queue_head));
Ajay Dudanib01e5062011-12-03 23:23:42 -0800695 arch_clean_invalidate_cache_range((addr_t) epts,
696 32 * sizeof(struct ept_queue_head));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800697
Deepa Dinamani0bf2f442012-10-19 11:41:06 -0700698 writel((unsigned)PA((addr_t)epts), USB_ENDPOINTLISTADDR);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800699
Ajay Dudanib01e5062011-12-03 23:23:42 -0800700 /* select DEVICE mode */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800701 writel(0x02, USB_USBMODE);
702
703 writel(0xffffffff, USB_ENDPTFLUSH);
704 thread_sleep(20);
705
706 ep0out = _udc_endpoint_alloc(0, 0, 64);
707 ep0in = _udc_endpoint_alloc(0, 1, 64);
708 ep0req = udc_request_alloc();
Hanumant Singh9d519092012-12-06 21:59:52 -0800709 ep0req->buf = memalign(CACHE_LINE, 4096);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800710
711 {
712 /* create and register a language table descriptor */
713 /* language 0x0409 is US English */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800714 struct udc_descriptor *desc =
715 udc_descriptor_alloc(TYPE_STRING, 0, 4);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800716 desc->data[2] = 0x09;
717 desc->data[3] = 0x04;
718 udc_descriptor_register(desc);
719 }
Amol Jadi4421e652011-06-16 15:00:48 -0700720
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800721 the_device = dev;
722 return 0;
723}
724
725enum handler_return udc_interrupt(void *arg)
726{
727 struct udc_endpoint *ept;
728 unsigned ret = INT_NO_RESCHEDULE;
729 unsigned n = readl(USB_USBSTS);
730 writel(n, USB_USBSTS);
Amol Jadi4421e652011-06-16 15:00:48 -0700731
Amol Jadida118b92012-07-06 19:53:18 -0700732 DBG("\nudc_interrupt(): status = 0x%x\n", n);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800733
Amol Jadida118b92012-07-06 19:53:18 -0700734 n &= (STS_SLI | STS_URI | STS_PCI | STS_UI | STS_UEI);
Amol Jadi4421e652011-06-16 15:00:48 -0700735
736 if (n == 0) {
737 DBG("n = 0\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800738 return ret;
Amol Jadi4421e652011-06-16 15:00:48 -0700739 }
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800740
741 if (n & STS_URI) {
Amol Jadi4421e652011-06-16 15:00:48 -0700742 DBG("STS_URI\n");
743
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800744 writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE);
745 writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT);
746 writel(0xffffffff, USB_ENDPTFLUSH);
747 writel(0, USB_ENDPTCTRL(1));
Amol Jadi4421e652011-06-16 15:00:48 -0700748 dprintf(INFO, "-- reset --\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800749 usb_online = 0;
750 usb_config_value = 0;
751 the_gadget->notify(the_gadget, UDC_EVENT_OFFLINE);
752
753 /* error out any pending reqs */
754 for (ept = ept_list; ept; ept = ept->next) {
755 /* ensure that ept_complete considers
756 * this to be an error state
757 */
758 if (ept->req) {
759 ept->req->item->info = INFO_HALTED;
760 handle_ept_complete(ept);
761 }
762 }
763 usb_status(0, usb_highspeed);
764 }
765 if (n & STS_SLI) {
Ajay Dudani35f686f2011-07-22 13:12:09 -0700766 DBG("-- suspend --\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800767 }
768 if (n & STS_PCI) {
Amol Jadi4421e652011-06-16 15:00:48 -0700769 dprintf(INFO, "-- portchange --\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800770 unsigned spd = (readl(USB_PORTSC) >> 26) & 3;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800771 if (spd == 2) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800772 usb_highspeed = 1;
773 } else {
774 usb_highspeed = 0;
775 }
776 }
777 if (n & STS_UEI) {
Amol Jadi4421e652011-06-16 15:00:48 -0700778 DBG("STS_UEI\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800779 dprintf(INFO, "<UEI %x>\n", readl(USB_ENDPTCOMPLETE));
780 }
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800781 if ((n & STS_UI) || (n & STS_UEI)) {
Amol Jadida118b92012-07-06 19:53:18 -0700782
783 if (n & STS_UEI)
784 DBG("ERROR ");
785 if (n & STS_UI)
786 DBG("USB ");
787
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800788 n = readl(USB_ENDPTSETUPSTAT);
789 if (n & EPT_RX(0)) {
790 handle_setup(ep0out);
791 ret = INT_RESCHEDULE;
792 }
793
794 n = readl(USB_ENDPTCOMPLETE);
795 if (n != 0) {
796 writel(n, USB_ENDPTCOMPLETE);
797 }
798
Ajay Dudanib01e5062011-12-03 23:23:42 -0800799 for (ept = ept_list; ept; ept = ept->next) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800800 if (n & ept->bit) {
801 handle_ept_complete(ept);
802 ret = INT_RESCHEDULE;
803 }
804 }
805 }
806 return ret;
807}
808
809int udc_register_gadget(struct udc_gadget *gadget)
810{
811 if (the_gadget) {
812 dprintf(CRITICAL, "only one gadget supported\n");
813 return -1;
814 }
815 the_gadget = gadget;
816 return 0;
817}
818
819static void udc_ept_desc_fill(struct udc_endpoint *ept, unsigned char *data)
820{
821 data[0] = 7;
822 data[1] = TYPE_ENDPOINT;
823 data[2] = ept->num | (ept->in ? 0x80 : 0x00);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800824 data[3] = 0x02; /* bulk -- the only kind we support */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800825 data[4] = ept->maxpkt;
826 data[5] = ept->maxpkt >> 8;
827 data[6] = ept->in ? 0x00 : 0x01;
828}
829
830static unsigned udc_ifc_desc_size(struct udc_gadget *g)
831{
832 return 9 + g->ifc_endpoints * 7;
833}
834
835static void udc_ifc_desc_fill(struct udc_gadget *g, unsigned char *data)
836{
837 unsigned n;
838
839 data[0] = 0x09;
840 data[1] = TYPE_INTERFACE;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800841 data[2] = 0x00; /* ifc number */
842 data[3] = 0x00; /* alt number */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800843 data[4] = g->ifc_endpoints;
844 data[5] = g->ifc_class;
845 data[6] = g->ifc_subclass;
846 data[7] = g->ifc_protocol;
847 data[8] = udc_string_desc_alloc(g->ifc_string);
848
849 data += 9;
850 for (n = 0; n < g->ifc_endpoints; n++) {
851 udc_ept_desc_fill(g->ept[n], data);
852 data += 7;
853 }
854}
855
856int udc_start(void)
857{
858 struct udc_descriptor *desc;
859 unsigned char *data;
860 unsigned size;
861
Chandan Uddaraju40b227d2010-08-03 19:25:41 -0700862 dprintf(ALWAYS, "udc_start()\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800863
864 if (!the_device) {
865 dprintf(CRITICAL, "udc cannot start before init\n");
866 return -1;
867 }
868 if (!the_gadget) {
869 dprintf(CRITICAL, "udc has no gadget registered\n");
870 return -1;
871 }
872
873 /* create our device descriptor */
874 desc = udc_descriptor_alloc(TYPE_DEVICE, 0, 18);
875 data = desc->data;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800876 data[2] = 0x00; /* usb spec minor rev */
877 data[3] = 0x02; /* usb spec major rev */
878 data[4] = 0x00; /* class */
879 data[5] = 0x00; /* subclass */
880 data[6] = 0x00; /* protocol */
881 data[7] = 0x40; /* max packet size on ept 0 */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800882 memcpy(data + 8, &the_device->vendor_id, sizeof(short));
883 memcpy(data + 10, &the_device->product_id, sizeof(short));
884 memcpy(data + 12, &the_device->version_id, sizeof(short));
885 data[14] = udc_string_desc_alloc(the_device->manufacturer);
886 data[15] = udc_string_desc_alloc(the_device->product);
887 data[16] = udc_string_desc_alloc(the_device->serialno);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800888 data[17] = 1; /* number of configurations */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800889 udc_descriptor_register(desc);
890
891 /* create our configuration descriptor */
892 size = 9 + udc_ifc_desc_size(the_gadget);
893 desc = udc_descriptor_alloc(TYPE_CONFIGURATION, 0, size);
894 data = desc->data;
895 data[0] = 0x09;
896 data[2] = size;
897 data[3] = size >> 8;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800898 data[4] = 0x01; /* number of interfaces */
899 data[5] = 0x01; /* configuration value */
900 data[6] = 0x00; /* configuration string */
901 data[7] = 0x80; /* attributes */
902 data[8] = 0x80; /* max power (250ma) -- todo fix this */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800903 udc_ifc_desc_fill(the_gadget, data + 9);
904 udc_descriptor_register(desc);
905
Ajay Dudanib01e5062011-12-03 23:23:42 -0800906 register_int_handler(INT_USB_HS, udc_interrupt, (void *)0);
Amol Jadica4f4c92011-01-13 20:19:34 -0800907 writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR);
908 unmask_interrupt(INT_USB_HS);
909
Ajay Dudanib01e5062011-12-03 23:23:42 -0800910 /* go to RUN mode (D+ pullup enable) */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800911 writel(0x00080001, USB_USBCMD);
Amol Jadica4f4c92011-01-13 20:19:34 -0800912
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800913 return 0;
914}
915
916int udc_stop(void)
917{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800918 writel(0, USB_USBINTR);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800919 mask_interrupt(INT_USB_HS);
920
Ajay Dudanib01e5062011-12-03 23:23:42 -0800921 /* disable pullup */
Shashank Mittald8c42bf2010-06-09 15:44:28 -0700922 writel(0x00080000, USB_USBCMD);
Amol Jadida118b92012-07-06 19:53:18 -0700923
924 target_usb_stop();
925
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800926 thread_sleep(10);
927
928 return 0;
929}