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