blob: d8643f0f1bb69d27a7cfebd54aee52d8d236a4ad [file] [log] [blame]
Brian Swetland3e7e21a2009-01-19 19:41:24 -08001/*
2 * Copyright (c) 2008, Google Inc.
3 * All rights reserved.
4 *
Amol Jadida118b92012-07-06 19:53:18 -07005 * Copyright (c) 2009-2012, 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
Amol Jadida118b92012-07-06 19:53:18 -0700121#define DBG(x...) dprintf(ALWAYS, x)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800122#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;
Shashank Mittal1cc65b02011-12-20 15:30:17 -0800269 item->page2 = (phys & 0xfffff000) + 0x2000;
270 item->page3 = (phys & 0xfffff000) + 0x3000;
271 item->page4 = (phys & 0xfffff000) + 0x4000;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800272
273 enter_critical_section();
Ajay Dudanib01e5062011-12-03 23:23:42 -0800274 ept->head->next = (unsigned)item;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800275 ept->head->info = 0;
276 ept->req = req;
277
Ajay Dudanib01e5062011-12-03 23:23:42 -0800278 arch_clean_invalidate_cache_range((addr_t) ept,
279 sizeof(struct udc_endpoint));
280 arch_clean_invalidate_cache_range((addr_t) ept->head,
281 sizeof(struct ept_queue_head));
282 arch_clean_invalidate_cache_range((addr_t) ept->req,
283 sizeof(struct usb_request));
284 arch_clean_invalidate_cache_range((addr_t) req->req.buf,
285 req->req.length);
286 arch_clean_invalidate_cache_range((addr_t) ept->req->item,
287 sizeof(struct ept_queue_item));
Amol Jadi4421e652011-06-16 15:00:48 -0700288
Ajay Dudanib01e5062011-12-03 23:23:42 -0800289 DBG("ept%d %s queue req=%p\n", ept->num, ept->in ? "in" : "out", req);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800290
291 writel(ept->bit, USB_ENDPTPRIME);
292 exit_critical_section();
293 return 0;
294}
295
296static void handle_ept_complete(struct udc_endpoint *ept)
297{
298 struct ept_queue_item *item;
299 unsigned actual;
300 int status;
301 struct usb_request *req;
Amol Jadi4421e652011-06-16 15:00:48 -0700302
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800303 DBG("ept%d %s complete req=%p\n",
Ajay Dudanib01e5062011-12-03 23:23:42 -0800304 ept->num, ept->in ? "in" : "out", ept->req);
Amol Jadi4421e652011-06-16 15:00:48 -0700305
Ajay Dudanib01e5062011-12-03 23:23:42 -0800306 arch_clean_invalidate_cache_range((addr_t) ept,
307 sizeof(struct udc_endpoint));
308 arch_clean_invalidate_cache_range((addr_t) ept->req,
309 sizeof(struct usb_request));
Amol Jadi4421e652011-06-16 15:00:48 -0700310
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800311 req = ept->req;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800312 if (req) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800313 ept->req = 0;
Amol Jadi4421e652011-06-16 15:00:48 -0700314
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800315 item = req->item;
316
317 /* For some reason we are getting the notification for
318 * transfer completion before the active bit has cleared.
319 * HACK: wait for the ACTIVE bit to clear:
320 */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800321 do {
Amol Jadi4421e652011-06-16 15:00:48 -0700322 /* Must clean/invalidate cached item data before checking
323 * the status every time.
324 */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800325 arch_clean_invalidate_cache_range((addr_t) item,
326 sizeof(struct
327 ept_queue_item));
328 }
329 while (readl(&(item->info)) & INFO_ACTIVE);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800330
Ajay Dudanib01e5062011-12-03 23:23:42 -0800331 arch_clean_invalidate_cache_range((addr_t) req->req.buf,
332 req->req.length);
Amol Jadi4421e652011-06-16 15:00:48 -0700333
Ajay Dudanib01e5062011-12-03 23:23:42 -0800334 if (item->info & 0xff) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800335 actual = 0;
336 status = -1;
337 dprintf(INFO, "EP%d/%s FAIL nfo=%x pg0=%x\n",
Ajay Dudanib01e5062011-12-03 23:23:42 -0800338 ept->num, ept->in ? "in" : "out", item->info,
339 item->page0);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800340 } else {
Ajay Dudanib01e5062011-12-03 23:23:42 -0800341 actual =
342 req->req.length - ((item->info >> 16) & 0x7fff);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800343 status = 0;
344 }
Ajay Dudanib01e5062011-12-03 23:23:42 -0800345 if (req->req.complete)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800346 req->req.complete(&req->req, actual, status);
347 }
348}
349
350static const char *reqname(unsigned r)
351{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800352 switch (r) {
353 case GET_STATUS:
354 return "GET_STATUS";
355 case CLEAR_FEATURE:
356 return "CLEAR_FEATURE";
357 case SET_FEATURE:
358 return "SET_FEATURE";
359 case SET_ADDRESS:
360 return "SET_ADDRESS";
361 case GET_DESCRIPTOR:
362 return "GET_DESCRIPTOR";
363 case SET_DESCRIPTOR:
364 return "SET_DESCRIPTOR";
365 case GET_CONFIGURATION:
366 return "GET_CONFIGURATION";
367 case SET_CONFIGURATION:
368 return "SET_CONFIGURATION";
369 case GET_INTERFACE:
370 return "GET_INTERFACE";
371 case SET_INTERFACE:
372 return "SET_INTERFACE";
373 default:
374 return "*UNKNOWN*";
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800375 }
376}
377
378static struct udc_endpoint *ep0in, *ep0out;
379static struct udc_request *ep0req;
380
Ajay Dudanib01e5062011-12-03 23:23:42 -0800381static void
382ep0_setup_ack_complete(struct udc_endpoint *ep, struct usb_request *req)
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700383{
384 uint32_t mode;
385
Ajay Dudanib01e5062011-12-03 23:23:42 -0800386 if (!test_mode)
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700387 return;
388
Ajay Dudanib01e5062011-12-03 23:23:42 -0800389 switch (test_mode) {
390 case TEST_PACKET:
391 dprintf(INFO, "Entering test mode for TST_PKT\n");
392 mode = readl(USB_PORTSC) & (~PORTSC_PTC);
393 writel(mode | PORTSC_PTC_TST_PKT, USB_PORTSC);
394 break;
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700395
Ajay Dudanib01e5062011-12-03 23:23:42 -0800396 case TEST_SE0_NAK:
397 dprintf(INFO, "Entering test mode for SE0-NAK\n");
398 mode = readl(USB_PORTSC) & (~PORTSC_PTC);
399 writel(mode | PORTSC_PTC_SE0_NAK, USB_PORTSC);
400 break;
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700401 }
402
403}
404
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800405static void setup_ack(void)
406{
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700407 ep0req->complete = ep0_setup_ack_complete;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800408 ep0req->length = 0;
409 udc_request_queue(ep0in, ep0req);
410}
411
412static void ep0in_complete(struct udc_request *req, unsigned actual, int status)
413{
414 DBG("ep0in_complete %p %d %d\n", req, actual, status);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800415 if (status == 0) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800416 req->length = 0;
417 req->complete = 0;
418 udc_request_queue(ep0out, req);
419 }
420}
421
422static void setup_tx(void *buf, unsigned len)
423{
424 DBG("setup_tx %p %d\n", buf, len);
425 memcpy(ep0req->buf, buf, len);
426 ep0req->complete = ep0in_complete;
427 ep0req->length = len;
428 udc_request_queue(ep0in, ep0req);
429}
430
431static unsigned char usb_config_value = 0;
432
433#define SETUP(type,request) (((type) << 8) | (request))
434
435static void handle_setup(struct udc_endpoint *ept)
436{
437 struct setup_packet s;
Amol Jadi4421e652011-06-16 15:00:48 -0700438
Ajay Dudanib01e5062011-12-03 23:23:42 -0800439 arch_clean_invalidate_cache_range((addr_t) ept->head->setup_data,
440 sizeof(struct ept_queue_head));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800441 memcpy(&s, ept->head->setup_data, sizeof(s));
442 writel(ept->bit, USB_ENDPTSETUPSTAT);
443
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800444 DBG("handle_setup type=0x%02x req=0x%02x val=%d idx=%d len=%d (%s)\n",
Ajay Dudanib01e5062011-12-03 23:23:42 -0800445 s.type, s.request, s.value, s.index, s.length, reqname(s.request));
Amol Jadi4421e652011-06-16 15:00:48 -0700446
Ajay Dudanib01e5062011-12-03 23:23:42 -0800447 switch (SETUP(s.type, s.request)) {
448 case SETUP(DEVICE_READ, GET_STATUS):
449 {
450 unsigned zero = 0;
451 if (s.length == 2) {
452 setup_tx(&zero, 2);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800453 return;
454 }
Ajay Dudanib01e5062011-12-03 23:23:42 -0800455 break;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800456 }
Ajay Dudanib01e5062011-12-03 23:23:42 -0800457 case SETUP(DEVICE_READ, GET_DESCRIPTOR):
458 {
459 struct udc_descriptor *desc;
460 /* usb_highspeed? */
461 for (desc = desc_list; desc; desc = desc->next) {
462 if (desc->tag == s.value) {
463 unsigned len = desc->len;
464 if (len > s.length)
465 len = s.length;
466 setup_tx(desc->data, len);
467 return;
468 }
469 }
470 break;
471 }
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800472 case SETUP(DEVICE_READ, GET_CONFIGURATION):
473 /* disabling this causes data transaction failures on OSX. Why? */
474 if ((s.value == 0) && (s.index == 0) && (s.length == 1)) {
475 setup_tx(&usb_config_value, 1);
476 return;
477 }
478 break;
479 case SETUP(DEVICE_WRITE, SET_CONFIGURATION):
480 if (s.value == 1) {
481 struct udc_endpoint *ept;
482 /* enable endpoints */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800483 for (ept = ept_list; ept; ept = ept->next) {
Amol Jadi4421e652011-06-16 15:00:48 -0700484 if (ept->num == 0)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800485 continue;
486 endpoint_enable(ept, s.value);
487 }
488 usb_config_value = 1;
489 the_gadget->notify(the_gadget, UDC_EVENT_ONLINE);
490 } else {
491 writel(0, USB_ENDPTCTRL(1));
492 usb_config_value = 0;
493 the_gadget->notify(the_gadget, UDC_EVENT_OFFLINE);
494 }
495 setup_ack();
496 usb_online = s.value ? 1 : 0;
497 usb_status(s.value ? 1 : 0, usb_highspeed);
498 return;
499 case SETUP(DEVICE_WRITE, SET_ADDRESS):
500 /* write address delayed (will take effect
Ajay Dudanib01e5062011-12-03 23:23:42 -0800501 ** after the next IN txn)
502 */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800503 writel((s.value << 25) | (1 << 24), USB_DEVICEADDR);
504 setup_ack();
505 return;
506 case SETUP(INTERFACE_WRITE, SET_INTERFACE):
507 /* if we ack this everything hangs */
508 /* per spec, STALL is valid if there is not alt func */
509 goto stall;
Subbaraman Narayanamurthyd8b7afc2011-06-30 15:42:41 -0700510 case SETUP(DEVICE_WRITE, SET_FEATURE):
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700511 test_mode = s.index;
Subbaraman Narayanamurthyd8b7afc2011-06-30 15:42:41 -0700512 setup_ack();
Subbaraman Narayanamurthyd8b7afc2011-06-30 15:42:41 -0700513 return;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800514 case SETUP(ENDPOINT_WRITE, CLEAR_FEATURE):
515 {
516 struct udc_endpoint *ept;
517 unsigned num = s.index & 15;
518 unsigned in = !!(s.index & 0x80);
Amol Jadi4421e652011-06-16 15:00:48 -0700519
Ajay Dudanib01e5062011-12-03 23:23:42 -0800520 if ((s.value == 0) && (s.length == 0)) {
521 DBG("clr feat %d %d\n", num, in);
522 for (ept = ept_list; ept; ept = ept->next) {
523 if ((ept->num == num)
524 && (ept->in == in)) {
525 endpoint_enable(ept, 1);
526 setup_ack();
527 return;
528 }
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800529 }
530 }
Ajay Dudanib01e5062011-12-03 23:23:42 -0800531 break;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800532 }
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800533 }
534
535 dprintf(INFO, "STALL %s %d %d %d %d %d\n",
536 reqname(s.request),
537 s.type, s.request, s.value, s.index, s.length);
538
Ajay Dudanib01e5062011-12-03 23:23:42 -0800539 stall:
540 writel((1 << 16) | (1 << 0), USB_ENDPTCTRL(ept->num));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800541}
542
543unsigned ulpi_read(unsigned reg)
544{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800545 /* initiate read operation */
546 writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg), USB_ULPI_VIEWPORT);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800547
Ajay Dudanib01e5062011-12-03 23:23:42 -0800548 /* wait for completion */
549 while (readl(USB_ULPI_VIEWPORT) & ULPI_RUN) ;
Amol Jadi4421e652011-06-16 15:00:48 -0700550
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800551 return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT));
552}
553
554void ulpi_write(unsigned val, unsigned reg)
555{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800556 /* initiate write operation */
Amol Jadi4421e652011-06-16 15:00:48 -0700557 writel(ULPI_RUN | ULPI_WRITE |
Ajay Dudanib01e5062011-12-03 23:23:42 -0800558 ULPI_ADDR(reg) | ULPI_DATA(val), USB_ULPI_VIEWPORT);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800559
Ajay Dudanib01e5062011-12-03 23:23:42 -0800560 /* wait for completion */
561 while (readl(USB_ULPI_VIEWPORT) & ULPI_RUN) ;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800562}
Ajay Dudani5a1e3302009-12-05 13:19:17 -0800563
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800564
Amol Jadi4421e652011-06-16 15:00:48 -0700565int udc_init(struct udc_device *dev)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800566{
Amol Jadi4421e652011-06-16 15:00:48 -0700567 DBG("udc_init():\n");
568
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800569 hsusb_clock_init();
570
Amol Jadida118b92012-07-06 19:53:18 -0700571 /* Do any target specific intialization like GPIO settings,
572 * LDO, PHY configuration etc. needed before USB port can be used.
573 */
574 target_usb_init();
575
Amol Jadida118b92012-07-06 19:53:18 -0700576 /* RESET */
Deepa Dinamani0687ecd2012-08-10 16:00:26 -0700577 writel(0x00080000, USB_USBCMD);
Amol Jadida118b92012-07-06 19:53:18 -0700578
579 thread_sleep(20);
Subbaraman Narayanamurthyb0111a12011-02-03 14:24:16 -0800580
Deepa Dinamani0687ecd2012-08-10 16:00:26 -0700581 while((readl(USB_USBCMD)&2));
582
583 /* select ULPI phy */
584 writel(0x80000000, USB_PORTSC);
585
586 /* USB_OTG_HS_AHB_BURST */
587 writel(0x0, USB_SBUSCFG);
588
589 /* USB_OTG_HS_AHB_MODE: HPROT_MODE */
590 /* Bus access related config. */
591 writel(0x08, USB_AHB_MODE);
592
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800593 epts = memalign(4096, 4096);
594
595 dprintf(INFO, "USB init ept @ %p\n", epts);
596 memset(epts, 0, 32 * sizeof(struct ept_queue_head));
Ajay Dudanib01e5062011-12-03 23:23:42 -0800597 arch_clean_invalidate_cache_range((addr_t) epts,
598 32 * sizeof(struct ept_queue_head));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800599
Ajay Dudanib01e5062011-12-03 23:23:42 -0800600 writel((unsigned)epts, USB_ENDPOINTLISTADDR);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800601
Ajay Dudanib01e5062011-12-03 23:23:42 -0800602 /* select DEVICE mode */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800603 writel(0x02, USB_USBMODE);
604
605 writel(0xffffffff, USB_ENDPTFLUSH);
606 thread_sleep(20);
607
608 ep0out = _udc_endpoint_alloc(0, 0, 64);
609 ep0in = _udc_endpoint_alloc(0, 1, 64);
610 ep0req = udc_request_alloc();
611 ep0req->buf = malloc(4096);
612
613 {
614 /* create and register a language table descriptor */
615 /* language 0x0409 is US English */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800616 struct udc_descriptor *desc =
617 udc_descriptor_alloc(TYPE_STRING, 0, 4);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800618 desc->data[2] = 0x09;
619 desc->data[3] = 0x04;
620 udc_descriptor_register(desc);
621 }
Amol Jadi4421e652011-06-16 15:00:48 -0700622
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800623 the_device = dev;
624 return 0;
625}
626
627enum handler_return udc_interrupt(void *arg)
628{
629 struct udc_endpoint *ept;
630 unsigned ret = INT_NO_RESCHEDULE;
631 unsigned n = readl(USB_USBSTS);
632 writel(n, USB_USBSTS);
Amol Jadi4421e652011-06-16 15:00:48 -0700633
Amol Jadida118b92012-07-06 19:53:18 -0700634 DBG("\nudc_interrupt(): status = 0x%x\n", n);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800635
Amol Jadida118b92012-07-06 19:53:18 -0700636 n &= (STS_SLI | STS_URI | STS_PCI | STS_UI | STS_UEI);
Amol Jadi4421e652011-06-16 15:00:48 -0700637
638 if (n == 0) {
639 DBG("n = 0\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800640 return ret;
Amol Jadi4421e652011-06-16 15:00:48 -0700641 }
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800642
643 if (n & STS_URI) {
Amol Jadi4421e652011-06-16 15:00:48 -0700644 DBG("STS_URI\n");
645
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800646 writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE);
647 writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT);
648 writel(0xffffffff, USB_ENDPTFLUSH);
649 writel(0, USB_ENDPTCTRL(1));
Amol Jadi4421e652011-06-16 15:00:48 -0700650 dprintf(INFO, "-- reset --\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800651 usb_online = 0;
652 usb_config_value = 0;
653 the_gadget->notify(the_gadget, UDC_EVENT_OFFLINE);
654
655 /* error out any pending reqs */
656 for (ept = ept_list; ept; ept = ept->next) {
657 /* ensure that ept_complete considers
658 * this to be an error state
659 */
660 if (ept->req) {
661 ept->req->item->info = INFO_HALTED;
662 handle_ept_complete(ept);
663 }
664 }
665 usb_status(0, usb_highspeed);
666 }
667 if (n & STS_SLI) {
Ajay Dudani35f686f2011-07-22 13:12:09 -0700668 DBG("-- suspend --\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800669 }
670 if (n & STS_PCI) {
Amol Jadi4421e652011-06-16 15:00:48 -0700671 dprintf(INFO, "-- portchange --\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800672 unsigned spd = (readl(USB_PORTSC) >> 26) & 3;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800673 if (spd == 2) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800674 usb_highspeed = 1;
675 } else {
676 usb_highspeed = 0;
677 }
678 }
679 if (n & STS_UEI) {
Amol Jadi4421e652011-06-16 15:00:48 -0700680 DBG("STS_UEI\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800681 dprintf(INFO, "<UEI %x>\n", readl(USB_ENDPTCOMPLETE));
682 }
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800683 if ((n & STS_UI) || (n & STS_UEI)) {
Amol Jadida118b92012-07-06 19:53:18 -0700684
685 if (n & STS_UEI)
686 DBG("ERROR ");
687 if (n & STS_UI)
688 DBG("USB ");
689
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800690 n = readl(USB_ENDPTSETUPSTAT);
691 if (n & EPT_RX(0)) {
692 handle_setup(ep0out);
693 ret = INT_RESCHEDULE;
694 }
695
696 n = readl(USB_ENDPTCOMPLETE);
697 if (n != 0) {
698 writel(n, USB_ENDPTCOMPLETE);
699 }
700
Ajay Dudanib01e5062011-12-03 23:23:42 -0800701 for (ept = ept_list; ept; ept = ept->next) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800702 if (n & ept->bit) {
703 handle_ept_complete(ept);
704 ret = INT_RESCHEDULE;
705 }
706 }
707 }
708 return ret;
709}
710
711int udc_register_gadget(struct udc_gadget *gadget)
712{
713 if (the_gadget) {
714 dprintf(CRITICAL, "only one gadget supported\n");
715 return -1;
716 }
717 the_gadget = gadget;
718 return 0;
719}
720
721static void udc_ept_desc_fill(struct udc_endpoint *ept, unsigned char *data)
722{
723 data[0] = 7;
724 data[1] = TYPE_ENDPOINT;
725 data[2] = ept->num | (ept->in ? 0x80 : 0x00);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800726 data[3] = 0x02; /* bulk -- the only kind we support */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800727 data[4] = ept->maxpkt;
728 data[5] = ept->maxpkt >> 8;
729 data[6] = ept->in ? 0x00 : 0x01;
730}
731
732static unsigned udc_ifc_desc_size(struct udc_gadget *g)
733{
734 return 9 + g->ifc_endpoints * 7;
735}
736
737static void udc_ifc_desc_fill(struct udc_gadget *g, unsigned char *data)
738{
739 unsigned n;
740
741 data[0] = 0x09;
742 data[1] = TYPE_INTERFACE;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800743 data[2] = 0x00; /* ifc number */
744 data[3] = 0x00; /* alt number */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800745 data[4] = g->ifc_endpoints;
746 data[5] = g->ifc_class;
747 data[6] = g->ifc_subclass;
748 data[7] = g->ifc_protocol;
749 data[8] = udc_string_desc_alloc(g->ifc_string);
750
751 data += 9;
752 for (n = 0; n < g->ifc_endpoints; n++) {
753 udc_ept_desc_fill(g->ept[n], data);
754 data += 7;
755 }
756}
757
758int udc_start(void)
759{
760 struct udc_descriptor *desc;
761 unsigned char *data;
762 unsigned size;
763
Chandan Uddaraju40b227d2010-08-03 19:25:41 -0700764 dprintf(ALWAYS, "udc_start()\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800765
766 if (!the_device) {
767 dprintf(CRITICAL, "udc cannot start before init\n");
768 return -1;
769 }
770 if (!the_gadget) {
771 dprintf(CRITICAL, "udc has no gadget registered\n");
772 return -1;
773 }
774
775 /* create our device descriptor */
776 desc = udc_descriptor_alloc(TYPE_DEVICE, 0, 18);
777 data = desc->data;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800778 data[2] = 0x00; /* usb spec minor rev */
779 data[3] = 0x02; /* usb spec major rev */
780 data[4] = 0x00; /* class */
781 data[5] = 0x00; /* subclass */
782 data[6] = 0x00; /* protocol */
783 data[7] = 0x40; /* max packet size on ept 0 */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800784 memcpy(data + 8, &the_device->vendor_id, sizeof(short));
785 memcpy(data + 10, &the_device->product_id, sizeof(short));
786 memcpy(data + 12, &the_device->version_id, sizeof(short));
787 data[14] = udc_string_desc_alloc(the_device->manufacturer);
788 data[15] = udc_string_desc_alloc(the_device->product);
789 data[16] = udc_string_desc_alloc(the_device->serialno);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800790 data[17] = 1; /* number of configurations */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800791 udc_descriptor_register(desc);
792
793 /* create our configuration descriptor */
794 size = 9 + udc_ifc_desc_size(the_gadget);
795 desc = udc_descriptor_alloc(TYPE_CONFIGURATION, 0, size);
796 data = desc->data;
797 data[0] = 0x09;
798 data[2] = size;
799 data[3] = size >> 8;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800800 data[4] = 0x01; /* number of interfaces */
801 data[5] = 0x01; /* configuration value */
802 data[6] = 0x00; /* configuration string */
803 data[7] = 0x80; /* attributes */
804 data[8] = 0x80; /* max power (250ma) -- todo fix this */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800805 udc_ifc_desc_fill(the_gadget, data + 9);
806 udc_descriptor_register(desc);
807
Ajay Dudanib01e5062011-12-03 23:23:42 -0800808 register_int_handler(INT_USB_HS, udc_interrupt, (void *)0);
Amol Jadica4f4c92011-01-13 20:19:34 -0800809 writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR);
810 unmask_interrupt(INT_USB_HS);
811
Ajay Dudanib01e5062011-12-03 23:23:42 -0800812 /* go to RUN mode (D+ pullup enable) */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800813 writel(0x00080001, USB_USBCMD);
Amol Jadica4f4c92011-01-13 20:19:34 -0800814
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800815 return 0;
816}
817
818int udc_stop(void)
819{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800820 writel(0, USB_USBINTR);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800821 mask_interrupt(INT_USB_HS);
822
Ajay Dudanib01e5062011-12-03 23:23:42 -0800823 /* disable pullup */
Shashank Mittald8c42bf2010-06-09 15:44:28 -0700824 writel(0x00080000, USB_USBCMD);
Amol Jadida118b92012-07-06 19:53:18 -0700825
826 target_usb_stop();
827
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800828 thread_sleep(10);
829
830 return 0;
831}