blob: 20267ede62852a48518a40b8e8751ff5d80888ee [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
Patrick Georgi49a80ce2015-03-10 12:47:36 +0100131 this->stall(this, 0, 0, 0);
132 this->stall(this, 0, 1, 0);
133
Patrick Georgi1bd30502015-01-26 20:17:49 +0100134 return 1;
135}
136
137static void chipidea_halt_ep(struct usbdev_ctrl *this, int ep, int in_dir)
138{
139 struct chipidea_pdata *p = CI_PDATA(this);
140 writel(1 << ep_to_bits(ep, in_dir), &p->opreg->epflush);
141 while (readl(&p->opreg->epflush))
142 ;
143 clrbits_le32(&p->opreg->epctrl[ep], 1 << (7 + (in_dir ? 16 : 0)));
Patrick Georgibd6901e2015-02-16 17:00:59 +0100144
145 while (!SIMPLEQ_EMPTY(&p->job_queue[ep][in_dir])) {
146 struct job *job = SIMPLEQ_FIRST(&p->job_queue[ep][in_dir]);
147 if (job->autofree)
148 free(job->data);
149
150 SIMPLEQ_REMOVE_HEAD(&p->job_queue[ep][in_dir], queue);
151 }
Patrick Georgi1bd30502015-01-26 20:17:49 +0100152}
153
154static void chipidea_start_ep(struct usbdev_ctrl *this,
155 int ep, int in_dir, int ep_type, int mps)
156{
157 struct chipidea_pdata *p = CI_PDATA(this);
158 struct qh *qh = get_qh(p, ep, in_dir);
159 qh->config = (mps << 16) | QH_NO_AUTO_ZLT | QH_IOS;
160 dcache_clean_by_mva(qh, sizeof(*qh));
161 in_dir = in_dir ? 1 : 0;
162 debug("enabling %d-%d (type %d)\n", ep, in_dir, ep_type);
163 /* enable endpoint, reset data toggle */
164 setbits_le32(&p->opreg->epctrl[ep],
165 ((1 << 7) | (1 << 6) | (ep_type << 2)) << (in_dir*16));
166 p->ep_busy[ep][in_dir] = 0;
Patrick Georgiea0bdf22015-02-16 17:00:59 +0100167 this->ep_mps[ep][in_dir] = mps;
Patrick Georgi1bd30502015-01-26 20:17:49 +0100168}
169
170static void advance_endpoint(struct chipidea_pdata *p, int endpoint, int in_dir)
171{
172 if (p->ep_busy[endpoint][in_dir])
173 return;
174 if (SIMPLEQ_EMPTY(&p->job_queue[endpoint][in_dir]))
175 return;
176
177 struct job *job = SIMPLEQ_FIRST(&p->job_queue[endpoint][in_dir]);
178 struct qh *qh = get_qh(p, endpoint, in_dir);
179
180 uint32_t start = (uint32_t)(uintptr_t)job->data;
181 uint32_t offset = (start & 0xfff);
182 /* unlike with typical EHCI controllers,
183 * a full TD transfers either 0x5000 bytes if
184 * page aligned or 0x4000 bytes if not.
185 */
186 int maxsize = 0x5000;
187 if (offset > 0)
188 maxsize = 0x4000;
189 uint32_t td_count = (job->length + maxsize - 1) / maxsize;
190
191 /* special case for zero length packets */
192 if (td_count == 0)
193 td_count = 1;
194
195 if (job->zlp)
196 td_count++;
197
198 struct td *tds = dma_memalign(32, sizeof(struct td) * td_count);
199 memset(tds, 0, sizeof(struct td) * td_count);
200
201 int i;
202 int remaining = job->length;
203 for (i = 0; i < td_count; i++) {
204 int datacount = min(maxsize, remaining);
205
206 debug("td %d, %d bytes\n", i, datacount);
207 tds[i].next = (uint32_t)virt_to_phys(&tds[i+1]);
208 tds[i].info = TD_INFO_LEN(datacount) | TD_INFO_ACTIVE;
209 tds[i].page0 = start;
210 tds[i].page1 = (start & 0xfffff000) + 0x1000;
211 tds[i].page2 = (start & 0xfffff000) + 0x2000;
212 tds[i].page3 = (start & 0xfffff000) + 0x3000;
213 tds[i].page4 = (start & 0xfffff000) + 0x4000;
214 remaining -= datacount;
215 start = start + datacount;
216 }
217 tds[td_count - 1].next = TD_TERMINATE;
218 tds[td_count - 1].info |= TD_INFO_IOC;
219
220 qh->td.next = (uint32_t)virt_to_phys(tds);
221 qh->td.info = 0;
222
223 job->tds = tds;
224 job->td_count = td_count;
225
226 dcache_clean_by_mva(tds, sizeof(struct td) * td_count);
227 dcache_clean_by_mva(job->data, job->length);
228 dcache_clean_by_mva(qh, sizeof(*qh));
229
230 debug("priming EP %d-%d with %zx bytes starting at %x (%p)\n", endpoint,
231 in_dir, job->length, tds[0].page0, job->data);
232 writel(1 << ep_to_bits(endpoint, in_dir), &p->opreg->epprime);
233 while (readl(&p->opreg->epprime))
234 ;
235 p->ep_busy[endpoint][in_dir] = 1;
236}
237
238static void handle_endpoint(struct usbdev_ctrl *this, int endpoint, int in_dir)
239{
240 struct chipidea_pdata *p = CI_PDATA(this);
241 struct job *job = SIMPLEQ_FIRST(&p->job_queue[endpoint][in_dir]);
242 SIMPLEQ_REMOVE_HEAD(&p->job_queue[endpoint][in_dir], queue);
243
244 if (in_dir)
245 dcache_invalidate_by_mva(job->data, job->length);
246
247 int length = job->length;
248
249 int i = 0;
250 do {
251 int active;
252 do {
253 dcache_invalidate_by_mva(&job->tds[i],
254 sizeof(struct td));
255 active = job->tds[i].info & TD_INFO_ACTIVE;
256 debug("%d-%d: info %08x, page0 %x, next %x\n",
257 endpoint, in_dir, job->tds[i].info,
258 job->tds[i].page0, job->tds[i].next);
259 } while (active);
260 /*
261 * The controller writes back the length field in info
262 * with the number of bytes it did _not_ process.
263 * Hence, take the originally scheduled length and
264 * subtract whatever lengths we still find - that gives
265 * us the data that the controller did transfer.
266 */
267 int remaining = job->tds[i].info >> 16;
268 length -= remaining;
269 } while (job->tds[i++].next != TD_TERMINATE);
270 debug("%d-%d: scheduled %zd, now %d bytes\n", endpoint, in_dir,
271 job->length, length);
272
273 if (this->current_config &&
274 this->current_config->interfaces[0].handle_packet)
275 this->current_config->interfaces[0].handle_packet(this,
276 endpoint, in_dir, job->data, length);
277
278 free(job->tds);
279 if (job->autofree)
280 free(job->data);
281 free(job);
282 p->ep_busy[endpoint][in_dir] = 0;
283
284 advance_endpoint(p, endpoint, in_dir);
285}
286
287static void start_setup(struct usbdev_ctrl *this, int ep)
288{
289 dev_req_t dr;
290 struct chipidea_pdata *p = CI_PDATA(this);
291 struct qh *qh = get_qh(p, ep, 0);
292
293 dcache_invalidate_by_mva(qh, sizeof(*qh));
294 memcpy(&dr, qh->setup_data, sizeof(qh->setup_data));
295 clear_setup_ep(p, ep);
296
297#ifdef DEBUG
298 hexdump((unsigned long)&dr, sizeof(dr));
299#endif
300
301 udc_handle_setup(this, ep, &dr);
302}
303
304
305static void chipidea_enqueue_packet(struct usbdev_ctrl *this, int endpoint,
306 int in_dir, void *data, int len, int zlp, int autofree)
307{
308 struct chipidea_pdata *p = CI_PDATA(this);
309 struct job *job = malloc(sizeof(*job));
310
311 job->data = data;
312 job->length = len;
313 job->zlp = zlp;
314 job->autofree = autofree;
315
316 debug("adding job of %d bytes to EP %d-%d\n", len, endpoint, in_dir);
317 SIMPLEQ_INSERT_TAIL(&p->job_queue[endpoint][in_dir], job, queue);
318
319 if ((endpoint == 0) || (this->initialized))
320 advance_endpoint(p, endpoint, in_dir);
321}
322
323static int chipidea_poll(struct usbdev_ctrl *this)
324{
325 struct chipidea_pdata *p = CI_PDATA(this);
326 uint32_t sts = readl(&p->opreg->usbsts);
327 writel(sts, &p->opreg->usbsts); /* clear */
328
329 /* new information if the bus is high speed or not */
330 if (sts & USBSTS_PCI) {
331 debug("USB speed negotiation: ");
332 if ((readl(&p->opreg->devlc) & DEVLC_HOSTSPEED_MASK)
333 == DEVLC_HOSTSPEED(2)) {
334 debug("high speed\n");
335 // TODO: implement
336 } else {
337 debug("full speed\n");
338 // TODO: implement
339 }
340 }
341
342 /* reset requested. stop all activities */
343 if (sts & USBSTS_URI) {
344 int i;
345 debug("USB reset requested\n");
346 if (this->initialized) {
347 writel(readl(&p->opreg->epstat), &p->opreg->epstat);
348 writel(readl(&p->opreg->epsetupstat),
349 &p->opreg->epsetupstat);
350 writel(0xffffffff, &p->opreg->epflush);
351 for (i = 1; i < 16; i++)
352 writel(0, &p->opreg->epctrl[i]);
353 this->initialized = 0;
354 }
355 writel((1 << 22) | (1 << 6), &p->opreg->epctrl[0]);
356 p->qhlist[0].config = QH_MPS(64) | QH_NO_AUTO_ZLT | QH_IOS;
357 p->qhlist[1].config = QH_MPS(64) | QH_NO_AUTO_ZLT | QH_IOS;
358 dcache_clean_by_mva(p->qhlist, 2 * sizeof(struct qh));
359 }
360
361 if (sts & (USBSTS_UEI | USBSTS_UI)) {
Patrick Georgie17d57e2015-03-10 12:51:31 +0100362 uint32_t bitmap;
363 int ep;
364
365 /* This slightly deviates from the recommendation in the
366 * data sheets, but the strict ordering is to simplify
367 * handling control transfers, which are initialized in
368 * the third step with a SETUP packet, then proceed in
369 * the next poll loop with in transfers (either data or
370 * status phase), then optionally out transfers (status
371 * phase).
372 */
373
374 /* in transfers */
375 bitmap = (readl(&p->opreg->epcomplete) >> 16) & 0xffff;
376 ep = 0;
377 while (bitmap) {
378 if (bitmap & 1) {
379 debug("incoming packet on EP %d (in)\n", ep);
380 handle_endpoint(this, ep, 1);
381 clear_ep(p, ep & 0xf, 1);
382 }
383 bitmap >>= 1;
384 ep++;
385 }
386
387 /* out transfers */
388 bitmap = readl(&p->opreg->epcomplete) & 0xffff;
389 ep = 0;
390 while (bitmap) {
391 if (bitmap & 1) {
392 debug("incoming packet on EP %d (out)\n", ep);
393 handle_endpoint(this, ep, 0);
394 clear_ep(p, ep, 0);
395 }
396 bitmap >>= 1;
397 ep++;
398 }
399
400 /* setup transfers */
401 bitmap = readl(&p->opreg->epsetupstat);
402 ep = 0;
Patrick Georgi1bd30502015-01-26 20:17:49 +0100403 while (bitmap) {
404 if (bitmap & 1) {
405 debug("incoming packet on EP %d (setup)\n", ep);
406 start_setup(this, ep);
407 }
408 bitmap >>= 1;
409 ep++;
410 }
Patrick Georgi1bd30502015-01-26 20:17:49 +0100411 }
412
413 return 1;
414}
415
Furquan Shaikhed1a4bb2015-06-22 16:07:51 -0700416static void chipidea_force_shutdown(struct usbdev_ctrl *this)
417{
418 struct chipidea_pdata *p = CI_PDATA(this);
419 writel(0xffffffff, &p->opreg->epflush);
420 writel(USBCMD_8MICRO | USBCMD_RST, &p->opreg->usbcmd);
421 writel(0, &p->opreg->usbmode);
422 writel(USBCMD_8MICRO, &p->opreg->usbcmd);
423 free(p->qhlist);
424 free(p);
425 free(this);
426}
427
Patrick Georgi1bd30502015-01-26 20:17:49 +0100428static void chipidea_shutdown(struct usbdev_ctrl *this)
429{
430 struct chipidea_pdata *p = CI_PDATA(this);
431 int i, j;
432 int is_empty = 0;
433 while (!is_empty) {
434 is_empty = 1;
435 this->poll(this);
436 for (i = 0; i < 16; i++)
437 for (j = 0; j < 2; j++)
438 if (!SIMPLEQ_EMPTY(&p->job_queue[i][j]))
439 is_empty = 0;
440 }
Furquan Shaikhed1a4bb2015-06-22 16:07:51 -0700441 chipidea_force_shutdown(this);
Patrick Georgi1bd30502015-01-26 20:17:49 +0100442}
443
444static void chipidea_set_address(struct usbdev_ctrl *this, int address)
445{
446 struct chipidea_pdata *p = CI_PDATA(this);
447 writel((address << 25) | (1 << 24), &p->opreg->usbadr);
448}
449
450static void chipidea_stall(struct usbdev_ctrl *this,
451 uint8_t ep, int in_dir, int set)
452{
453 struct chipidea_pdata *p = CI_PDATA(this);
454 assert(ep < 16);
455 uint32_t *ctrl = &p->opreg->epctrl[ep];
456 in_dir = in_dir ? 1 : 0;
457 if (set) {
458 if (in_dir)
459 setbits_le32(ctrl, 1 << 16);
460 else
461 setbits_le32(ctrl, 1 << 0);
462 } else {
463 /* reset STALL bit, reset data toggle */
464 if (in_dir) {
465 setbits_le32(ctrl, 1 << 22);
466 clrbits_le32(ctrl, 1 << 16);
467 } else {
468 setbits_le32(ctrl, 1 << 6);
Patrick Georgi9a20a432015-03-10 12:45:28 +0100469 clrbits_le32(ctrl, 1 << 0);
Patrick Georgi1bd30502015-01-26 20:17:49 +0100470 }
471 }
472 this->ep_halted[ep][in_dir] = set;
473}
474
475static void *chipidea_malloc(size_t size)
476{
477 return dma_malloc(size);
478}
479
480static void chipidea_free(void *ptr)
481{
482 free(ptr);
483}
484
485struct usbdev_ctrl *chipidea_init(device_descriptor_t *dd)
486{
487 struct usbdev_ctrl *ctrl = calloc(1, sizeof(*ctrl));
488 if (ctrl == NULL)
489 return NULL;
490 ctrl->pdata = calloc(1, sizeof(struct chipidea_pdata));
491 if (ctrl->pdata == NULL) {
492 free(ctrl);
493 return NULL;
494 }
495
496 ctrl->poll = chipidea_poll;
497 ctrl->add_gadget = udc_add_gadget;
Patrick Georgif4227c42015-06-18 00:15:00 +0200498 ctrl->add_strings = udc_add_strings;
Patrick Georgi1bd30502015-01-26 20:17:49 +0100499 ctrl->enqueue_packet = chipidea_enqueue_packet;
Furquan Shaikhed1a4bb2015-06-22 16:07:51 -0700500 ctrl->force_shutdown = chipidea_force_shutdown;
Patrick Georgi1bd30502015-01-26 20:17:49 +0100501 ctrl->shutdown = chipidea_shutdown;
502 ctrl->set_address = chipidea_set_address;
503 ctrl->stall = chipidea_stall;
504 ctrl->halt_ep = chipidea_halt_ep;
505 ctrl->start_ep = chipidea_start_ep;
506 ctrl->alloc_data = chipidea_malloc;
507 ctrl->free_data = chipidea_free;
508 ctrl->initialized = 0;
509
Patrick Georgiea0bdf22015-02-16 17:00:59 +0100510 int i;
511 ctrl->ep_mps[0][0] = 64;
512 ctrl->ep_mps[0][1] = 64;
513 for (i = 1; i < 16; i++) {
514 ctrl->ep_mps[i][0] = 512;
515 ctrl->ep_mps[i][1] = 512;
516 }
517
Patrick Georgi1bd30502015-01-26 20:17:49 +0100518 if (!chipidea_hw_init(ctrl, (void *)0x7d000000, dd)) {
519 free(ctrl->pdata);
520 free(ctrl);
521 return NULL;
522 }
523 return ctrl;
524}