blob: 58bea063e793350dda4fbeaf9aef2c5714a43445 [file] [log] [blame]
Patrick Georgi1bd30502015-01-26 20:17:49 +01001/*
2 * This file is part of the libpayload project.
3 *
4 * Copyright (C) 2015 Google Inc.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <libpayload.h>
31#include <arch/cache.h>
32#include <assert.h>
33#include <endian.h>
34#include <queue.h>
35#include <stdio.h>
36#include <stdint.h>
37#include <stdlib.h>
38#include <usb/usb.h>
39
40#include <udc/udc.h>
41#include <udc/chipidea.h>
42#include "chipidea_priv.h"
43
44#ifdef DEBUG
45#define debug(x...) printf(x)
46#else
47#define debug(x...) do {} while (0)
48#endif
49
50#define min(a, b) (((a) < (b)) ? (a) : (b))
51
52static struct qh *get_qh(struct chipidea_pdata *p, int endpoint, int in_dir)
53{
54 assert(in_dir <= 1);
55 return &p->qhlist[2 * endpoint + in_dir];
56}
57
58static unsigned int ep_to_bits(int ep, int in_dir)
59{
60 return ep + (in_dir ? 16 : 0);
61}
62
63static void clear_setup_ep(struct chipidea_pdata *p, int endpoint)
64{
65 writel(1 << endpoint, &p->opreg->epsetupstat);
66}
67
68static void clear_ep(struct chipidea_pdata *p, int endpoint, int in_dir)
69{
70 writel(1 << ep_to_bits(endpoint, in_dir), &p->opreg->epcomplete);
71}
72
73static int chipidea_hw_init(struct usbdev_ctrl *this, void *_opreg,
74 const device_descriptor_t *dd)
75{
76 struct chipidea_opreg *opreg = _opreg;
77 struct chipidea_pdata *p = CI_PDATA(this);
78
79 p->opreg = phys_to_virt(opreg);
80 p->qhlist = dma_memalign(4096, sizeof(struct qh) * CI_QHELEMENTS);
81 memcpy(&this->device_descriptor, dd, sizeof(*dd));
82
83 if (p->qhlist == NULL)
84 die("failed to allocate memory for usb device mode");
85
86 memset(p->qhlist, 0, sizeof(struct qh) * CI_QHELEMENTS);
87
88 SLIST_INIT(&this->configs);
89
90 int i;
91 for (i = 0; i < 16; i++) {
92 SIMPLEQ_INIT(&p->job_queue[i][0]);
93 SIMPLEQ_INIT(&p->job_queue[i][1]);
94 }
95
96 for (i = 0; i < CI_QHELEMENTS; i++) {
97 p->qhlist[i].config = QH_MPS(512) | QH_NO_AUTO_ZLT | QH_IOS;
98 p->qhlist[i].td.next = TD_TERMINATE;
99 }
100 /* EP0 in/out are hardwired for SETUP */
101 p->qhlist[0].config = QH_MPS(64) | QH_NO_AUTO_ZLT | QH_IOS;
102 p->qhlist[1].config = QH_MPS(64) | QH_NO_AUTO_ZLT | QH_IOS;
103
104 do {
105 debug("waiting for usb phy clk valid: %x\n",
106 readl(&p->opreg->susp_ctrl));
107 mdelay(1);
108 } while ((readl(&p->opreg->susp_ctrl) & (1 << 7)) == 0);
109
110 writel(USBCMD_8MICRO | USBCMD_RST, &p->opreg->usbcmd);
111 mdelay(1);
112
113 /* enable device mode */
114 writel(2, &p->opreg->usbmode);
115
116 dcache_clean_by_mva(p->qhlist, sizeof(struct qh) * CI_QHELEMENTS);
117
118 writel(virt_to_phys(p->qhlist), &p->opreg->epbase);
119 writel(0xffffffff, &p->opreg->epflush);
120
121 /* enable EP0 */
122 writel((1 << 23) | (1 << 22) | (1 << 7) | (1 << 6),
123 &p->opreg->epctrl[0]);
124
125 /* clear status register */
126 writel(readl(&p->opreg->usbsts), &p->opreg->usbsts);
127
128 debug("taking controller out of reset\n");
129 writel(USBCMD_8MICRO | USBCMD_RUN, &p->opreg->usbcmd);
130
131 return 1;
132}
133
134static void chipidea_halt_ep(struct usbdev_ctrl *this, int ep, int in_dir)
135{
136 struct chipidea_pdata *p = CI_PDATA(this);
137 writel(1 << ep_to_bits(ep, in_dir), &p->opreg->epflush);
138 while (readl(&p->opreg->epflush))
139 ;
140 clrbits_le32(&p->opreg->epctrl[ep], 1 << (7 + (in_dir ? 16 : 0)));
141}
142
143static void chipidea_start_ep(struct usbdev_ctrl *this,
144 int ep, int in_dir, int ep_type, int mps)
145{
146 struct chipidea_pdata *p = CI_PDATA(this);
147 struct qh *qh = get_qh(p, ep, in_dir);
148 qh->config = (mps << 16) | QH_NO_AUTO_ZLT | QH_IOS;
149 dcache_clean_by_mva(qh, sizeof(*qh));
150 in_dir = in_dir ? 1 : 0;
151 debug("enabling %d-%d (type %d)\n", ep, in_dir, ep_type);
152 /* enable endpoint, reset data toggle */
153 setbits_le32(&p->opreg->epctrl[ep],
154 ((1 << 7) | (1 << 6) | (ep_type << 2)) << (in_dir*16));
155 p->ep_busy[ep][in_dir] = 0;
Patrick Georgiea0bdf22015-02-16 17:00:59 +0100156 this->ep_mps[ep][in_dir] = mps;
Patrick Georgi1bd30502015-01-26 20:17:49 +0100157}
158
159static void advance_endpoint(struct chipidea_pdata *p, int endpoint, int in_dir)
160{
161 if (p->ep_busy[endpoint][in_dir])
162 return;
163 if (SIMPLEQ_EMPTY(&p->job_queue[endpoint][in_dir]))
164 return;
165
166 struct job *job = SIMPLEQ_FIRST(&p->job_queue[endpoint][in_dir]);
167 struct qh *qh = get_qh(p, endpoint, in_dir);
168
169 uint32_t start = (uint32_t)(uintptr_t)job->data;
170 uint32_t offset = (start & 0xfff);
171 /* unlike with typical EHCI controllers,
172 * a full TD transfers either 0x5000 bytes if
173 * page aligned or 0x4000 bytes if not.
174 */
175 int maxsize = 0x5000;
176 if (offset > 0)
177 maxsize = 0x4000;
178 uint32_t td_count = (job->length + maxsize - 1) / maxsize;
179
180 /* special case for zero length packets */
181 if (td_count == 0)
182 td_count = 1;
183
184 if (job->zlp)
185 td_count++;
186
187 struct td *tds = dma_memalign(32, sizeof(struct td) * td_count);
188 memset(tds, 0, sizeof(struct td) * td_count);
189
190 int i;
191 int remaining = job->length;
192 for (i = 0; i < td_count; i++) {
193 int datacount = min(maxsize, remaining);
194
195 debug("td %d, %d bytes\n", i, datacount);
196 tds[i].next = (uint32_t)virt_to_phys(&tds[i+1]);
197 tds[i].info = TD_INFO_LEN(datacount) | TD_INFO_ACTIVE;
198 tds[i].page0 = start;
199 tds[i].page1 = (start & 0xfffff000) + 0x1000;
200 tds[i].page2 = (start & 0xfffff000) + 0x2000;
201 tds[i].page3 = (start & 0xfffff000) + 0x3000;
202 tds[i].page4 = (start & 0xfffff000) + 0x4000;
203 remaining -= datacount;
204 start = start + datacount;
205 }
206 tds[td_count - 1].next = TD_TERMINATE;
207 tds[td_count - 1].info |= TD_INFO_IOC;
208
209 qh->td.next = (uint32_t)virt_to_phys(tds);
210 qh->td.info = 0;
211
212 job->tds = tds;
213 job->td_count = td_count;
214
215 dcache_clean_by_mva(tds, sizeof(struct td) * td_count);
216 dcache_clean_by_mva(job->data, job->length);
217 dcache_clean_by_mva(qh, sizeof(*qh));
218
219 debug("priming EP %d-%d with %zx bytes starting at %x (%p)\n", endpoint,
220 in_dir, job->length, tds[0].page0, job->data);
221 writel(1 << ep_to_bits(endpoint, in_dir), &p->opreg->epprime);
222 while (readl(&p->opreg->epprime))
223 ;
224 p->ep_busy[endpoint][in_dir] = 1;
225}
226
227static void handle_endpoint(struct usbdev_ctrl *this, int endpoint, int in_dir)
228{
229 struct chipidea_pdata *p = CI_PDATA(this);
230 struct job *job = SIMPLEQ_FIRST(&p->job_queue[endpoint][in_dir]);
231 SIMPLEQ_REMOVE_HEAD(&p->job_queue[endpoint][in_dir], queue);
232
233 if (in_dir)
234 dcache_invalidate_by_mva(job->data, job->length);
235
236 int length = job->length;
237
238 int i = 0;
239 do {
240 int active;
241 do {
242 dcache_invalidate_by_mva(&job->tds[i],
243 sizeof(struct td));
244 active = job->tds[i].info & TD_INFO_ACTIVE;
245 debug("%d-%d: info %08x, page0 %x, next %x\n",
246 endpoint, in_dir, job->tds[i].info,
247 job->tds[i].page0, job->tds[i].next);
248 } while (active);
249 /*
250 * The controller writes back the length field in info
251 * with the number of bytes it did _not_ process.
252 * Hence, take the originally scheduled length and
253 * subtract whatever lengths we still find - that gives
254 * us the data that the controller did transfer.
255 */
256 int remaining = job->tds[i].info >> 16;
257 length -= remaining;
258 } while (job->tds[i++].next != TD_TERMINATE);
259 debug("%d-%d: scheduled %zd, now %d bytes\n", endpoint, in_dir,
260 job->length, length);
261
262 if (this->current_config &&
263 this->current_config->interfaces[0].handle_packet)
264 this->current_config->interfaces[0].handle_packet(this,
265 endpoint, in_dir, job->data, length);
266
267 free(job->tds);
268 if (job->autofree)
269 free(job->data);
270 free(job);
271 p->ep_busy[endpoint][in_dir] = 0;
272
273 advance_endpoint(p, endpoint, in_dir);
274}
275
276static void start_setup(struct usbdev_ctrl *this, int ep)
277{
278 dev_req_t dr;
279 struct chipidea_pdata *p = CI_PDATA(this);
280 struct qh *qh = get_qh(p, ep, 0);
281
282 dcache_invalidate_by_mva(qh, sizeof(*qh));
283 memcpy(&dr, qh->setup_data, sizeof(qh->setup_data));
284 clear_setup_ep(p, ep);
285
286#ifdef DEBUG
287 hexdump((unsigned long)&dr, sizeof(dr));
288#endif
289
290 udc_handle_setup(this, ep, &dr);
291}
292
293
294static void chipidea_enqueue_packet(struct usbdev_ctrl *this, int endpoint,
295 int in_dir, void *data, int len, int zlp, int autofree)
296{
297 struct chipidea_pdata *p = CI_PDATA(this);
298 struct job *job = malloc(sizeof(*job));
299
300 job->data = data;
301 job->length = len;
302 job->zlp = zlp;
303 job->autofree = autofree;
304
305 debug("adding job of %d bytes to EP %d-%d\n", len, endpoint, in_dir);
306 SIMPLEQ_INSERT_TAIL(&p->job_queue[endpoint][in_dir], job, queue);
307
308 if ((endpoint == 0) || (this->initialized))
309 advance_endpoint(p, endpoint, in_dir);
310}
311
312static int chipidea_poll(struct usbdev_ctrl *this)
313{
314 struct chipidea_pdata *p = CI_PDATA(this);
315 uint32_t sts = readl(&p->opreg->usbsts);
316 writel(sts, &p->opreg->usbsts); /* clear */
317
318 /* new information if the bus is high speed or not */
319 if (sts & USBSTS_PCI) {
320 debug("USB speed negotiation: ");
321 if ((readl(&p->opreg->devlc) & DEVLC_HOSTSPEED_MASK)
322 == DEVLC_HOSTSPEED(2)) {
323 debug("high speed\n");
324 // TODO: implement
325 } else {
326 debug("full speed\n");
327 // TODO: implement
328 }
329 }
330
331 /* reset requested. stop all activities */
332 if (sts & USBSTS_URI) {
333 int i;
334 debug("USB reset requested\n");
335 if (this->initialized) {
336 writel(readl(&p->opreg->epstat), &p->opreg->epstat);
337 writel(readl(&p->opreg->epsetupstat),
338 &p->opreg->epsetupstat);
339 writel(0xffffffff, &p->opreg->epflush);
340 for (i = 1; i < 16; i++)
341 writel(0, &p->opreg->epctrl[i]);
342 this->initialized = 0;
343 }
344 writel((1 << 22) | (1 << 6), &p->opreg->epctrl[0]);
345 p->qhlist[0].config = QH_MPS(64) | QH_NO_AUTO_ZLT | QH_IOS;
346 p->qhlist[1].config = QH_MPS(64) | QH_NO_AUTO_ZLT | QH_IOS;
347 dcache_clean_by_mva(p->qhlist, 2 * sizeof(struct qh));
348 }
349
350 if (sts & (USBSTS_UEI | USBSTS_UI)) {
351 uint32_t bitmap = readl(&p->opreg->epsetupstat);
352 int ep = 0;
353 while (bitmap) {
354 if (bitmap & 1) {
355 debug("incoming packet on EP %d (setup)\n", ep);
356 start_setup(this, ep);
357 }
358 bitmap >>= 1;
359 ep++;
360 }
361 bitmap = readl(&p->opreg->epcomplete);
362 ep = 0;
363 int dir_in = 0;
364 while (bitmap) {
365 if (bitmap & 1) {
366 debug("incoming packet on EP %d (%s)\n",
367 ep, dir_in ? "intr/in" : "out");
368 handle_endpoint(this, ep & 0xf, dir_in);
369 clear_ep(p, ep & 0xf, dir_in);
370 }
371 bitmap >>= 1;
372 ep++;
373 if (ep == 16)
374 dir_in = 1;
375 }
376 }
377
378 return 1;
379}
380
381static void chipidea_shutdown(struct usbdev_ctrl *this)
382{
383 struct chipidea_pdata *p = CI_PDATA(this);
384 int i, j;
385 int is_empty = 0;
386 while (!is_empty) {
387 is_empty = 1;
388 this->poll(this);
389 for (i = 0; i < 16; i++)
390 for (j = 0; j < 2; j++)
391 if (!SIMPLEQ_EMPTY(&p->job_queue[i][j]))
392 is_empty = 0;
393 }
394 writel(0xffffffff, &p->opreg->epflush);
395 writel(USBCMD_8MICRO | USBCMD_RST, &p->opreg->usbcmd);
396 writel(0, &p->opreg->usbmode);
397 writel(USBCMD_8MICRO, &p->opreg->usbcmd);
398 free(p->qhlist);
399 free(p);
400 free(this);
401}
402
403static void chipidea_set_address(struct usbdev_ctrl *this, int address)
404{
405 struct chipidea_pdata *p = CI_PDATA(this);
406 writel((address << 25) | (1 << 24), &p->opreg->usbadr);
407}
408
409static void chipidea_stall(struct usbdev_ctrl *this,
410 uint8_t ep, int in_dir, int set)
411{
412 struct chipidea_pdata *p = CI_PDATA(this);
413 assert(ep < 16);
414 uint32_t *ctrl = &p->opreg->epctrl[ep];
415 in_dir = in_dir ? 1 : 0;
416 if (set) {
417 if (in_dir)
418 setbits_le32(ctrl, 1 << 16);
419 else
420 setbits_le32(ctrl, 1 << 0);
421 } else {
422 /* reset STALL bit, reset data toggle */
423 if (in_dir) {
424 setbits_le32(ctrl, 1 << 22);
425 clrbits_le32(ctrl, 1 << 16);
426 } else {
427 setbits_le32(ctrl, 1 << 6);
428 setbits_le32(ctrl, 1 << 0);
429 }
430 }
431 this->ep_halted[ep][in_dir] = set;
432}
433
434static void *chipidea_malloc(size_t size)
435{
436 return dma_malloc(size);
437}
438
439static void chipidea_free(void *ptr)
440{
441 free(ptr);
442}
443
444struct usbdev_ctrl *chipidea_init(device_descriptor_t *dd)
445{
446 struct usbdev_ctrl *ctrl = calloc(1, sizeof(*ctrl));
447 if (ctrl == NULL)
448 return NULL;
449 ctrl->pdata = calloc(1, sizeof(struct chipidea_pdata));
450 if (ctrl->pdata == NULL) {
451 free(ctrl);
452 return NULL;
453 }
454
455 ctrl->poll = chipidea_poll;
456 ctrl->add_gadget = udc_add_gadget;
457 ctrl->enqueue_packet = chipidea_enqueue_packet;
458 ctrl->shutdown = chipidea_shutdown;
459 ctrl->set_address = chipidea_set_address;
460 ctrl->stall = chipidea_stall;
461 ctrl->halt_ep = chipidea_halt_ep;
462 ctrl->start_ep = chipidea_start_ep;
463 ctrl->alloc_data = chipidea_malloc;
464 ctrl->free_data = chipidea_free;
465 ctrl->initialized = 0;
466
Patrick Georgiea0bdf22015-02-16 17:00:59 +0100467 int i;
468 ctrl->ep_mps[0][0] = 64;
469 ctrl->ep_mps[0][1] = 64;
470 for (i = 1; i < 16; i++) {
471 ctrl->ep_mps[i][0] = 512;
472 ctrl->ep_mps[i][1] = 512;
473 }
474
Patrick Georgi1bd30502015-01-26 20:17:49 +0100475 if (!chipidea_hw_init(ctrl, (void *)0x7d000000, dd)) {
476 free(ctrl->pdata);
477 free(ctrl);
478 return NULL;
479 }
480 return ctrl;
481}