blob: 9dcc55007709e842e6d479c93d2793b1051ea142 [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>
43
44#include <dev/udc.h>
45
46#include "hsusb.h"
47
48/* common code - factor out into a shared file */
49
50struct udc_descriptor {
51 struct udc_descriptor *next;
Ajay Dudanib01e5062011-12-03 23:23:42 -080052 unsigned short tag; /* ((TYPE << 8) | NUM) */
53 unsigned short len; /* total length */
Brian Swetland3e7e21a2009-01-19 19:41:24 -080054 unsigned char data[0];
55};
56
Ajay Dudanib01e5062011-12-03 23:23:42 -080057struct udc_descriptor *udc_descriptor_alloc(unsigned type, unsigned num,
58 unsigned len)
Brian Swetland3e7e21a2009-01-19 19:41:24 -080059{
60 struct udc_descriptor *desc;
61 if ((len > 255) || (len < 2) || (num > 255) || (type > 255))
62 return 0;
63
Ajay Dudanib01e5062011-12-03 23:23:42 -080064 if (!(desc = malloc(sizeof(struct udc_descriptor) + len)))
Brian Swetland3e7e21a2009-01-19 19:41:24 -080065 return 0;
66
67 desc->next = 0;
68 desc->tag = (type << 8) | num;
69 desc->len = len;
70 desc->data[0] = len;
71 desc->data[1] = type;
72
73 return desc;
74}
75
76static struct udc_descriptor *desc_list = 0;
77static unsigned next_string_id = 1;
78
79void udc_descriptor_register(struct udc_descriptor *desc)
80{
81 desc->next = desc_list;
82 desc_list = desc;
83}
84
85unsigned udc_string_desc_alloc(const char *str)
86{
87 unsigned len;
88 struct udc_descriptor *desc;
89 unsigned char *data;
90
91 if (next_string_id > 255)
92 return 0;
93
94 if (!str)
95 return 0;
96
97 len = strlen(str);
98 desc = udc_descriptor_alloc(TYPE_STRING, next_string_id, len * 2 + 2);
99 if (!desc)
100 return 0;
101 next_string_id++;
102
103 /* expand ascii string to utf16 */
104 data = desc->data + 2;
105 while (len-- > 0) {
106 *data++ = *str++;
107 *data++ = 0;
108 }
109
110 udc_descriptor_register(desc);
111 return desc->tag & 0xff;
112}
113
114/* end of common code */
115
Ajay Dudani7d605522010-10-01 19:52:37 -0700116__WEAK void hsusb_clock_init(void)
117{
Greg Griscod6250552011-06-29 14:40:23 -0700118 return;
Ajay Dudani7d605522010-10-01 19:52:37 -0700119}
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800120
121#if 1
122#define DBG(x...) do {} while(0)
123#else
Amol Jadida118b92012-07-06 19:53:18 -0700124#define DBG(x...) dprintf(ALWAYS, x)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800125#endif
126
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800127#define usb_status(a,b)
128
129struct usb_request {
130 struct udc_request req;
131 struct ept_queue_item *item;
132};
Amol Jadi4421e652011-06-16 15:00:48 -0700133
Ajay Dudanib01e5062011-12-03 23:23:42 -0800134struct udc_endpoint {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800135 struct udc_endpoint *next;
136 unsigned bit;
137 struct ept_queue_head *head;
138 struct usb_request *req;
139 unsigned char num;
140 unsigned char in;
141 unsigned short maxpkt;
142};
143
144struct udc_endpoint *ept_list = 0;
145struct ept_queue_head *epts = 0;
146
147static int usb_online = 0;
148static int usb_highspeed = 0;
149
150static struct udc_device *the_device;
151static struct udc_gadget *the_gadget;
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700152static unsigned test_mode = 0;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800153
Ajay Dudanib01e5062011-12-03 23:23:42 -0800154struct udc_endpoint *_udc_endpoint_alloc(unsigned num, unsigned in,
155 unsigned max_pkt)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800156{
157 struct udc_endpoint *ept;
158 unsigned cfg;
159
160 ept = malloc(sizeof(*ept));
Amol Jadi4421e652011-06-16 15:00:48 -0700161
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800162 ept->maxpkt = max_pkt;
163 ept->num = num;
164 ept->in = !!in;
165 ept->req = 0;
166
167 cfg = CONFIG_MAX_PKT(max_pkt) | CONFIG_ZLT;
168
Ajay Dudanib01e5062011-12-03 23:23:42 -0800169 if (ept->in) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800170 ept->bit = EPT_TX(ept->num);
171 } else {
172 ept->bit = EPT_RX(ept->num);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800173 if (num == 0)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800174 cfg |= CONFIG_IOS;
175 }
176
177 ept->head = epts + (num * 2) + (ept->in);
178 ept->head->config = cfg;
179
180 ept->next = ept_list;
181 ept_list = ept;
Amol Jadi4421e652011-06-16 15:00:48 -0700182
183 DBG("ept%d %s @%p/%p max=%d bit=%x\n",
Ajay Dudanib01e5062011-12-03 23:23:42 -0800184 num, in ? "in" : "out", ept, ept->head, max_pkt, ept->bit);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800185
186 return ept;
187}
188
189static unsigned ept_alloc_table = EPT_TX(0) | EPT_RX(0);
190
191struct udc_endpoint *udc_endpoint_alloc(unsigned type, unsigned maxpkt)
192{
193 struct udc_endpoint *ept;
194 unsigned n;
195 unsigned in;
196
197 if (type == UDC_TYPE_BULK_IN) {
198 in = 1;
199 } else if (type == UDC_TYPE_BULK_OUT) {
200 in = 0;
201 } else {
202 return 0;
203 }
204
205 for (n = 1; n < 16; n++) {
206 unsigned bit = in ? EPT_TX(n) : EPT_RX(n);
207 if (ept_alloc_table & bit)
208 continue;
209 ept = _udc_endpoint_alloc(n, in, maxpkt);
210 if (ept)
211 ept_alloc_table |= bit;
212 return ept;
213 }
214 return 0;
215}
216
217void udc_endpoint_free(struct udc_endpoint *ept)
218{
219 /* todo */
220}
221
222static void endpoint_enable(struct udc_endpoint *ept, unsigned yes)
223{
224 unsigned n = readl(USB_ENDPTCTRL(ept->num));
225
Ajay Dudanib01e5062011-12-03 23:23:42 -0800226 if (yes) {
227 if (ept->in) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800228 n |= (CTRL_TXE | CTRL_TXR | CTRL_TXT_BULK);
229 } else {
230 n |= (CTRL_RXE | CTRL_RXR | CTRL_RXT_BULK);
231 }
232
Ajay Dudanib01e5062011-12-03 23:23:42 -0800233 if (ept->num != 0) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800234 /* XXX should be more dynamic... */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800235 if (usb_highspeed) {
236 ept->head->config =
237 CONFIG_MAX_PKT(512) | CONFIG_ZLT;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800238 } else {
Ajay Dudanib01e5062011-12-03 23:23:42 -0800239 ept->head->config =
240 CONFIG_MAX_PKT(64) | CONFIG_ZLT;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800241 }
242 }
243 }
244 writel(n, USB_ENDPTCTRL(ept->num));
245}
246
247struct udc_request *udc_request_alloc(void)
248{
249 struct usb_request *req;
250 req = malloc(sizeof(*req));
251 req->req.buf = 0;
252 req->req.length = 0;
253 req->item = memalign(32, 32);
254 return &req->req;
255}
256
257void udc_request_free(struct udc_request *req)
258{
259 free(req);
260}
261
262int udc_request_queue(struct udc_endpoint *ept, struct udc_request *_req)
263{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800264 struct usb_request *req = (struct usb_request *)_req;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800265 struct ept_queue_item *item = req->item;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800266 unsigned phys = (unsigned)req->req.buf;
Amol Jadi4421e652011-06-16 15:00:48 -0700267
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800268 item->next = TERMINATE;
269 item->info = INFO_BYTES(req->req.length) | INFO_IOC | INFO_ACTIVE;
270 item->page0 = phys;
271 item->page1 = (phys & 0xfffff000) + 0x1000;
Shashank Mittal1cc65b02011-12-20 15:30:17 -0800272 item->page2 = (phys & 0xfffff000) + 0x2000;
273 item->page3 = (phys & 0xfffff000) + 0x3000;
274 item->page4 = (phys & 0xfffff000) + 0x4000;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800275
276 enter_critical_section();
Ajay Dudanib01e5062011-12-03 23:23:42 -0800277 ept->head->next = (unsigned)item;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800278 ept->head->info = 0;
279 ept->req = req;
280
Ajay Dudanib01e5062011-12-03 23:23:42 -0800281 arch_clean_invalidate_cache_range((addr_t) ept,
282 sizeof(struct udc_endpoint));
283 arch_clean_invalidate_cache_range((addr_t) ept->head,
284 sizeof(struct ept_queue_head));
285 arch_clean_invalidate_cache_range((addr_t) ept->req,
286 sizeof(struct usb_request));
Deepa Dinamani0bf2f442012-10-19 11:41:06 -0700287 arch_clean_invalidate_cache_range((addr_t) VA(req->req.buf),
Ajay Dudanib01e5062011-12-03 23:23:42 -0800288 req->req.length);
289 arch_clean_invalidate_cache_range((addr_t) ept->req->item,
290 sizeof(struct ept_queue_item));
Amol Jadi4421e652011-06-16 15:00:48 -0700291
Ajay Dudanib01e5062011-12-03 23:23:42 -0800292 DBG("ept%d %s queue req=%p\n", ept->num, ept->in ? "in" : "out", req);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800293
294 writel(ept->bit, USB_ENDPTPRIME);
295 exit_critical_section();
296 return 0;
297}
298
299static void handle_ept_complete(struct udc_endpoint *ept)
300{
301 struct ept_queue_item *item;
302 unsigned actual;
303 int status;
304 struct usb_request *req;
Amol Jadi4421e652011-06-16 15:00:48 -0700305
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800306 DBG("ept%d %s complete req=%p\n",
Ajay Dudanib01e5062011-12-03 23:23:42 -0800307 ept->num, ept->in ? "in" : "out", ept->req);
Amol Jadi4421e652011-06-16 15:00:48 -0700308
Ajay Dudanib01e5062011-12-03 23:23:42 -0800309 arch_clean_invalidate_cache_range((addr_t) ept,
310 sizeof(struct udc_endpoint));
311 arch_clean_invalidate_cache_range((addr_t) ept->req,
312 sizeof(struct usb_request));
Amol Jadi4421e652011-06-16 15:00:48 -0700313
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800314 req = ept->req;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800315 if (req) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800316 ept->req = 0;
Amol Jadi4421e652011-06-16 15:00:48 -0700317
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800318 item = req->item;
319
320 /* For some reason we are getting the notification for
321 * transfer completion before the active bit has cleared.
322 * HACK: wait for the ACTIVE bit to clear:
323 */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800324 do {
Amol Jadi4421e652011-06-16 15:00:48 -0700325 /* Must clean/invalidate cached item data before checking
326 * the status every time.
327 */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800328 arch_clean_invalidate_cache_range((addr_t) item,
329 sizeof(struct
330 ept_queue_item));
331 }
332 while (readl(&(item->info)) & INFO_ACTIVE);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800333
Deepa Dinamani0bf2f442012-10-19 11:41:06 -0700334 arch_clean_invalidate_cache_range(VA((addr_t) req->req.buf),
Ajay Dudanib01e5062011-12-03 23:23:42 -0800335 req->req.length);
Amol Jadi4421e652011-06-16 15:00:48 -0700336
Ajay Dudanib01e5062011-12-03 23:23:42 -0800337 if (item->info & 0xff) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800338 actual = 0;
339 status = -1;
340 dprintf(INFO, "EP%d/%s FAIL nfo=%x pg0=%x\n",
Ajay Dudanib01e5062011-12-03 23:23:42 -0800341 ept->num, ept->in ? "in" : "out", item->info,
342 item->page0);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800343 } else {
Ajay Dudanib01e5062011-12-03 23:23:42 -0800344 actual =
345 req->req.length - ((item->info >> 16) & 0x7fff);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800346 status = 0;
347 }
Ajay Dudanib01e5062011-12-03 23:23:42 -0800348 if (req->req.complete)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800349 req->req.complete(&req->req, actual, status);
350 }
351}
352
353static const char *reqname(unsigned r)
354{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800355 switch (r) {
356 case GET_STATUS:
357 return "GET_STATUS";
358 case CLEAR_FEATURE:
359 return "CLEAR_FEATURE";
360 case SET_FEATURE:
361 return "SET_FEATURE";
362 case SET_ADDRESS:
363 return "SET_ADDRESS";
364 case GET_DESCRIPTOR:
365 return "GET_DESCRIPTOR";
366 case SET_DESCRIPTOR:
367 return "SET_DESCRIPTOR";
368 case GET_CONFIGURATION:
369 return "GET_CONFIGURATION";
370 case SET_CONFIGURATION:
371 return "SET_CONFIGURATION";
372 case GET_INTERFACE:
373 return "GET_INTERFACE";
374 case SET_INTERFACE:
375 return "SET_INTERFACE";
376 default:
377 return "*UNKNOWN*";
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800378 }
379}
380
381static struct udc_endpoint *ep0in, *ep0out;
382static struct udc_request *ep0req;
383
Ajay Dudanib01e5062011-12-03 23:23:42 -0800384static void
385ep0_setup_ack_complete(struct udc_endpoint *ep, struct usb_request *req)
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700386{
387 uint32_t mode;
388
Ajay Dudanib01e5062011-12-03 23:23:42 -0800389 if (!test_mode)
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700390 return;
391
Ajay Dudanib01e5062011-12-03 23:23:42 -0800392 switch (test_mode) {
393 case TEST_PACKET:
394 dprintf(INFO, "Entering test mode for TST_PKT\n");
395 mode = readl(USB_PORTSC) & (~PORTSC_PTC);
396 writel(mode | PORTSC_PTC_TST_PKT, USB_PORTSC);
397 break;
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700398
Ajay Dudanib01e5062011-12-03 23:23:42 -0800399 case TEST_SE0_NAK:
400 dprintf(INFO, "Entering test mode for SE0-NAK\n");
401 mode = readl(USB_PORTSC) & (~PORTSC_PTC);
402 writel(mode | PORTSC_PTC_SE0_NAK, USB_PORTSC);
403 break;
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700404 }
405
406}
407
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800408static void setup_ack(void)
409{
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700410 ep0req->complete = ep0_setup_ack_complete;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800411 ep0req->length = 0;
412 udc_request_queue(ep0in, ep0req);
413}
414
415static void ep0in_complete(struct udc_request *req, unsigned actual, int status)
416{
417 DBG("ep0in_complete %p %d %d\n", req, actual, status);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800418 if (status == 0) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800419 req->length = 0;
420 req->complete = 0;
421 udc_request_queue(ep0out, req);
422 }
423}
424
425static void setup_tx(void *buf, unsigned len)
426{
427 DBG("setup_tx %p %d\n", buf, len);
428 memcpy(ep0req->buf, buf, len);
Deepa Dinamani0bf2f442012-10-19 11:41:06 -0700429 ep0req->buf = PA((addr_t)ep0req->buf);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800430 ep0req->complete = ep0in_complete;
431 ep0req->length = len;
432 udc_request_queue(ep0in, ep0req);
433}
434
435static unsigned char usb_config_value = 0;
436
437#define SETUP(type,request) (((type) << 8) | (request))
438
439static void handle_setup(struct udc_endpoint *ept)
440{
441 struct setup_packet s;
Amol Jadi4421e652011-06-16 15:00:48 -0700442
Ajay Dudanib01e5062011-12-03 23:23:42 -0800443 arch_clean_invalidate_cache_range((addr_t) ept->head->setup_data,
444 sizeof(struct ept_queue_head));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800445 memcpy(&s, ept->head->setup_data, sizeof(s));
446 writel(ept->bit, USB_ENDPTSETUPSTAT);
447
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800448 DBG("handle_setup type=0x%02x req=0x%02x val=%d idx=%d len=%d (%s)\n",
Ajay Dudanib01e5062011-12-03 23:23:42 -0800449 s.type, s.request, s.value, s.index, s.length, reqname(s.request));
Amol Jadi4421e652011-06-16 15:00:48 -0700450
Ajay Dudanib01e5062011-12-03 23:23:42 -0800451 switch (SETUP(s.type, s.request)) {
452 case SETUP(DEVICE_READ, GET_STATUS):
453 {
454 unsigned zero = 0;
455 if (s.length == 2) {
456 setup_tx(&zero, 2);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800457 return;
458 }
Ajay Dudanib01e5062011-12-03 23:23:42 -0800459 break;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800460 }
Ajay Dudanib01e5062011-12-03 23:23:42 -0800461 case SETUP(DEVICE_READ, GET_DESCRIPTOR):
462 {
463 struct udc_descriptor *desc;
464 /* usb_highspeed? */
465 for (desc = desc_list; desc; desc = desc->next) {
466 if (desc->tag == s.value) {
467 unsigned len = desc->len;
468 if (len > s.length)
469 len = s.length;
470 setup_tx(desc->data, len);
471 return;
472 }
473 }
474 break;
475 }
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800476 case SETUP(DEVICE_READ, GET_CONFIGURATION):
477 /* disabling this causes data transaction failures on OSX. Why? */
478 if ((s.value == 0) && (s.index == 0) && (s.length == 1)) {
479 setup_tx(&usb_config_value, 1);
480 return;
481 }
482 break;
483 case SETUP(DEVICE_WRITE, SET_CONFIGURATION):
484 if (s.value == 1) {
485 struct udc_endpoint *ept;
486 /* enable endpoints */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800487 for (ept = ept_list; ept; ept = ept->next) {
Amol Jadi4421e652011-06-16 15:00:48 -0700488 if (ept->num == 0)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800489 continue;
490 endpoint_enable(ept, s.value);
491 }
492 usb_config_value = 1;
493 the_gadget->notify(the_gadget, UDC_EVENT_ONLINE);
494 } else {
495 writel(0, USB_ENDPTCTRL(1));
496 usb_config_value = 0;
497 the_gadget->notify(the_gadget, UDC_EVENT_OFFLINE);
498 }
499 setup_ack();
500 usb_online = s.value ? 1 : 0;
501 usb_status(s.value ? 1 : 0, usb_highspeed);
502 return;
503 case SETUP(DEVICE_WRITE, SET_ADDRESS):
504 /* write address delayed (will take effect
Ajay Dudanib01e5062011-12-03 23:23:42 -0800505 ** after the next IN txn)
506 */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800507 writel((s.value << 25) | (1 << 24), USB_DEVICEADDR);
508 setup_ack();
509 return;
510 case SETUP(INTERFACE_WRITE, SET_INTERFACE):
511 /* if we ack this everything hangs */
512 /* per spec, STALL is valid if there is not alt func */
513 goto stall;
Subbaraman Narayanamurthyd8b7afc2011-06-30 15:42:41 -0700514 case SETUP(DEVICE_WRITE, SET_FEATURE):
Shashank Mittal2523e0b2011-10-14 17:32:46 -0700515 test_mode = s.index;
Subbaraman Narayanamurthyd8b7afc2011-06-30 15:42:41 -0700516 setup_ack();
Subbaraman Narayanamurthyd8b7afc2011-06-30 15:42:41 -0700517 return;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800518 case SETUP(ENDPOINT_WRITE, CLEAR_FEATURE):
519 {
520 struct udc_endpoint *ept;
521 unsigned num = s.index & 15;
522 unsigned in = !!(s.index & 0x80);
Amol Jadi4421e652011-06-16 15:00:48 -0700523
Ajay Dudanib01e5062011-12-03 23:23:42 -0800524 if ((s.value == 0) && (s.length == 0)) {
525 DBG("clr feat %d %d\n", num, in);
526 for (ept = ept_list; ept; ept = ept->next) {
527 if ((ept->num == num)
528 && (ept->in == in)) {
529 endpoint_enable(ept, 1);
530 setup_ack();
531 return;
532 }
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800533 }
534 }
Ajay Dudanib01e5062011-12-03 23:23:42 -0800535 break;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800536 }
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800537 }
538
539 dprintf(INFO, "STALL %s %d %d %d %d %d\n",
540 reqname(s.request),
541 s.type, s.request, s.value, s.index, s.length);
542
Ajay Dudanib01e5062011-12-03 23:23:42 -0800543 stall:
544 writel((1 << 16) | (1 << 0), USB_ENDPTCTRL(ept->num));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800545}
546
547unsigned ulpi_read(unsigned reg)
548{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800549 /* initiate read operation */
550 writel(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg), USB_ULPI_VIEWPORT);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800551
Ajay Dudanib01e5062011-12-03 23:23:42 -0800552 /* wait for completion */
553 while (readl(USB_ULPI_VIEWPORT) & ULPI_RUN) ;
Amol Jadi4421e652011-06-16 15:00:48 -0700554
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800555 return ULPI_DATA_READ(readl(USB_ULPI_VIEWPORT));
556}
557
558void ulpi_write(unsigned val, unsigned reg)
559{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800560 /* initiate write operation */
Amol Jadi4421e652011-06-16 15:00:48 -0700561 writel(ULPI_RUN | ULPI_WRITE |
Ajay Dudanib01e5062011-12-03 23:23:42 -0800562 ULPI_ADDR(reg) | ULPI_DATA(val), USB_ULPI_VIEWPORT);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800563
Ajay Dudanib01e5062011-12-03 23:23:42 -0800564 /* wait for completion */
565 while (readl(USB_ULPI_VIEWPORT) & ULPI_RUN) ;
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800566}
Ajay Dudani5a1e3302009-12-05 13:19:17 -0800567
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800568
Amol Jadi4421e652011-06-16 15:00:48 -0700569int udc_init(struct udc_device *dev)
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800570{
Amol Jadi4421e652011-06-16 15:00:48 -0700571 DBG("udc_init():\n");
572
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800573 hsusb_clock_init();
574
Amol Jadida118b92012-07-06 19:53:18 -0700575 /* Do any target specific intialization like GPIO settings,
576 * LDO, PHY configuration etc. needed before USB port can be used.
577 */
578 target_usb_init();
579
Amol Jadida118b92012-07-06 19:53:18 -0700580 /* RESET */
Deepa Dinamani0687ecd2012-08-10 16:00:26 -0700581 writel(0x00080000, USB_USBCMD);
Amol Jadida118b92012-07-06 19:53:18 -0700582
583 thread_sleep(20);
Subbaraman Narayanamurthyb0111a12011-02-03 14:24:16 -0800584
Deepa Dinamani0687ecd2012-08-10 16:00:26 -0700585 while((readl(USB_USBCMD)&2));
586
587 /* select ULPI phy */
588 writel(0x80000000, USB_PORTSC);
589
590 /* USB_OTG_HS_AHB_BURST */
591 writel(0x0, USB_SBUSCFG);
592
593 /* USB_OTG_HS_AHB_MODE: HPROT_MODE */
594 /* Bus access related config. */
595 writel(0x08, USB_AHB_MODE);
596
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800597 epts = memalign(4096, 4096);
598
599 dprintf(INFO, "USB init ept @ %p\n", epts);
600 memset(epts, 0, 32 * sizeof(struct ept_queue_head));
Ajay Dudanib01e5062011-12-03 23:23:42 -0800601 arch_clean_invalidate_cache_range((addr_t) epts,
602 32 * sizeof(struct ept_queue_head));
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800603
Deepa Dinamani0bf2f442012-10-19 11:41:06 -0700604 writel((unsigned)PA((addr_t)epts), USB_ENDPOINTLISTADDR);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800605
Ajay Dudanib01e5062011-12-03 23:23:42 -0800606 /* select DEVICE mode */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800607 writel(0x02, USB_USBMODE);
608
609 writel(0xffffffff, USB_ENDPTFLUSH);
610 thread_sleep(20);
611
612 ep0out = _udc_endpoint_alloc(0, 0, 64);
613 ep0in = _udc_endpoint_alloc(0, 1, 64);
614 ep0req = udc_request_alloc();
615 ep0req->buf = malloc(4096);
616
617 {
618 /* create and register a language table descriptor */
619 /* language 0x0409 is US English */
Ajay Dudanib01e5062011-12-03 23:23:42 -0800620 struct udc_descriptor *desc =
621 udc_descriptor_alloc(TYPE_STRING, 0, 4);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800622 desc->data[2] = 0x09;
623 desc->data[3] = 0x04;
624 udc_descriptor_register(desc);
625 }
Amol Jadi4421e652011-06-16 15:00:48 -0700626
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800627 the_device = dev;
628 return 0;
629}
630
631enum handler_return udc_interrupt(void *arg)
632{
633 struct udc_endpoint *ept;
634 unsigned ret = INT_NO_RESCHEDULE;
635 unsigned n = readl(USB_USBSTS);
636 writel(n, USB_USBSTS);
Amol Jadi4421e652011-06-16 15:00:48 -0700637
Amol Jadida118b92012-07-06 19:53:18 -0700638 DBG("\nudc_interrupt(): status = 0x%x\n", n);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800639
Amol Jadida118b92012-07-06 19:53:18 -0700640 n &= (STS_SLI | STS_URI | STS_PCI | STS_UI | STS_UEI);
Amol Jadi4421e652011-06-16 15:00:48 -0700641
642 if (n == 0) {
643 DBG("n = 0\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800644 return ret;
Amol Jadi4421e652011-06-16 15:00:48 -0700645 }
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800646
647 if (n & STS_URI) {
Amol Jadi4421e652011-06-16 15:00:48 -0700648 DBG("STS_URI\n");
649
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800650 writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE);
651 writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT);
652 writel(0xffffffff, USB_ENDPTFLUSH);
653 writel(0, USB_ENDPTCTRL(1));
Amol Jadi4421e652011-06-16 15:00:48 -0700654 dprintf(INFO, "-- reset --\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800655 usb_online = 0;
656 usb_config_value = 0;
657 the_gadget->notify(the_gadget, UDC_EVENT_OFFLINE);
658
659 /* error out any pending reqs */
660 for (ept = ept_list; ept; ept = ept->next) {
661 /* ensure that ept_complete considers
662 * this to be an error state
663 */
664 if (ept->req) {
665 ept->req->item->info = INFO_HALTED;
666 handle_ept_complete(ept);
667 }
668 }
669 usb_status(0, usb_highspeed);
670 }
671 if (n & STS_SLI) {
Ajay Dudani35f686f2011-07-22 13:12:09 -0700672 DBG("-- suspend --\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800673 }
674 if (n & STS_PCI) {
Amol Jadi4421e652011-06-16 15:00:48 -0700675 dprintf(INFO, "-- portchange --\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800676 unsigned spd = (readl(USB_PORTSC) >> 26) & 3;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800677 if (spd == 2) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800678 usb_highspeed = 1;
679 } else {
680 usb_highspeed = 0;
681 }
682 }
683 if (n & STS_UEI) {
Amol Jadi4421e652011-06-16 15:00:48 -0700684 DBG("STS_UEI\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800685 dprintf(INFO, "<UEI %x>\n", readl(USB_ENDPTCOMPLETE));
686 }
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800687 if ((n & STS_UI) || (n & STS_UEI)) {
Amol Jadida118b92012-07-06 19:53:18 -0700688
689 if (n & STS_UEI)
690 DBG("ERROR ");
691 if (n & STS_UI)
692 DBG("USB ");
693
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800694 n = readl(USB_ENDPTSETUPSTAT);
695 if (n & EPT_RX(0)) {
696 handle_setup(ep0out);
697 ret = INT_RESCHEDULE;
698 }
699
700 n = readl(USB_ENDPTCOMPLETE);
701 if (n != 0) {
702 writel(n, USB_ENDPTCOMPLETE);
703 }
704
Ajay Dudanib01e5062011-12-03 23:23:42 -0800705 for (ept = ept_list; ept; ept = ept->next) {
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800706 if (n & ept->bit) {
707 handle_ept_complete(ept);
708 ret = INT_RESCHEDULE;
709 }
710 }
711 }
712 return ret;
713}
714
715int udc_register_gadget(struct udc_gadget *gadget)
716{
717 if (the_gadget) {
718 dprintf(CRITICAL, "only one gadget supported\n");
719 return -1;
720 }
721 the_gadget = gadget;
722 return 0;
723}
724
725static void udc_ept_desc_fill(struct udc_endpoint *ept, unsigned char *data)
726{
727 data[0] = 7;
728 data[1] = TYPE_ENDPOINT;
729 data[2] = ept->num | (ept->in ? 0x80 : 0x00);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800730 data[3] = 0x02; /* bulk -- the only kind we support */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800731 data[4] = ept->maxpkt;
732 data[5] = ept->maxpkt >> 8;
733 data[6] = ept->in ? 0x00 : 0x01;
734}
735
736static unsigned udc_ifc_desc_size(struct udc_gadget *g)
737{
738 return 9 + g->ifc_endpoints * 7;
739}
740
741static void udc_ifc_desc_fill(struct udc_gadget *g, unsigned char *data)
742{
743 unsigned n;
744
745 data[0] = 0x09;
746 data[1] = TYPE_INTERFACE;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800747 data[2] = 0x00; /* ifc number */
748 data[3] = 0x00; /* alt number */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800749 data[4] = g->ifc_endpoints;
750 data[5] = g->ifc_class;
751 data[6] = g->ifc_subclass;
752 data[7] = g->ifc_protocol;
753 data[8] = udc_string_desc_alloc(g->ifc_string);
754
755 data += 9;
756 for (n = 0; n < g->ifc_endpoints; n++) {
757 udc_ept_desc_fill(g->ept[n], data);
758 data += 7;
759 }
760}
761
762int udc_start(void)
763{
764 struct udc_descriptor *desc;
765 unsigned char *data;
766 unsigned size;
767
Chandan Uddaraju40b227d2010-08-03 19:25:41 -0700768 dprintf(ALWAYS, "udc_start()\n");
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800769
770 if (!the_device) {
771 dprintf(CRITICAL, "udc cannot start before init\n");
772 return -1;
773 }
774 if (!the_gadget) {
775 dprintf(CRITICAL, "udc has no gadget registered\n");
776 return -1;
777 }
778
779 /* create our device descriptor */
780 desc = udc_descriptor_alloc(TYPE_DEVICE, 0, 18);
781 data = desc->data;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800782 data[2] = 0x00; /* usb spec minor rev */
783 data[3] = 0x02; /* usb spec major rev */
784 data[4] = 0x00; /* class */
785 data[5] = 0x00; /* subclass */
786 data[6] = 0x00; /* protocol */
787 data[7] = 0x40; /* max packet size on ept 0 */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800788 memcpy(data + 8, &the_device->vendor_id, sizeof(short));
789 memcpy(data + 10, &the_device->product_id, sizeof(short));
790 memcpy(data + 12, &the_device->version_id, sizeof(short));
791 data[14] = udc_string_desc_alloc(the_device->manufacturer);
792 data[15] = udc_string_desc_alloc(the_device->product);
793 data[16] = udc_string_desc_alloc(the_device->serialno);
Ajay Dudanib01e5062011-12-03 23:23:42 -0800794 data[17] = 1; /* number of configurations */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800795 udc_descriptor_register(desc);
796
797 /* create our configuration descriptor */
798 size = 9 + udc_ifc_desc_size(the_gadget);
799 desc = udc_descriptor_alloc(TYPE_CONFIGURATION, 0, size);
800 data = desc->data;
801 data[0] = 0x09;
802 data[2] = size;
803 data[3] = size >> 8;
Ajay Dudanib01e5062011-12-03 23:23:42 -0800804 data[4] = 0x01; /* number of interfaces */
805 data[5] = 0x01; /* configuration value */
806 data[6] = 0x00; /* configuration string */
807 data[7] = 0x80; /* attributes */
808 data[8] = 0x80; /* max power (250ma) -- todo fix this */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800809 udc_ifc_desc_fill(the_gadget, data + 9);
810 udc_descriptor_register(desc);
811
Ajay Dudanib01e5062011-12-03 23:23:42 -0800812 register_int_handler(INT_USB_HS, udc_interrupt, (void *)0);
Amol Jadica4f4c92011-01-13 20:19:34 -0800813 writel(STS_URI | STS_SLI | STS_UI | STS_PCI, USB_USBINTR);
814 unmask_interrupt(INT_USB_HS);
815
Ajay Dudanib01e5062011-12-03 23:23:42 -0800816 /* go to RUN mode (D+ pullup enable) */
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800817 writel(0x00080001, USB_USBCMD);
Amol Jadica4f4c92011-01-13 20:19:34 -0800818
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800819 return 0;
820}
821
822int udc_stop(void)
823{
Ajay Dudanib01e5062011-12-03 23:23:42 -0800824 writel(0, USB_USBINTR);
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800825 mask_interrupt(INT_USB_HS);
826
Ajay Dudanib01e5062011-12-03 23:23:42 -0800827 /* disable pullup */
Shashank Mittald8c42bf2010-06-09 15:44:28 -0700828 writel(0x00080000, USB_USBCMD);
Amol Jadida118b92012-07-06 19:53:18 -0700829
830 target_usb_stop();
831
Brian Swetland3e7e21a2009-01-19 19:41:24 -0800832 thread_sleep(10);
833
834 return 0;
835}