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