blob: dacb5d8618cb86a7f7b8faff71999801b1d63aca [file] [log] [blame]
Nico Huber90292652013-06-13 14:37:15 +02001/*
2 * This file is part of the libpayload project.
3 *
4 * Copyright (C) 2013 secunet Security Networks AG
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//#define XHCI_SPEW_DEBUG
31
32#include <inttypes.h>
33#include <arch/virtual.h>
34#include "xhci_private.h"
35
36void
37xhci_reset_event_ring(event_ring_t *const er)
38{
39 int i;
40 for (i = 0; i < EVENT_RING_SIZE; ++i)
41 er->ring[i].control &= ~TRB_CYCLE;
42 er->cur = er->ring;
43 er->last = er->ring + EVENT_RING_SIZE;
44 er->ccs = 1;
45 er->adv = 1;
46}
47
48static inline int
49xhci_event_ready(const event_ring_t *const er)
50{
51 return (er->cur->control & TRB_CYCLE) == er->ccs;
52}
53
54void
55xhci_update_event_dq(xhci_t *const xhci)
56{
57 if (xhci->er.adv) {
58 xhci_spew("Updating dq ptr: @%p(0x%08"PRIx32") -> %p\n",
59 phys_to_virt(xhci->hcrreg->intrrs[0].erdp_lo),
60 xhci->hcrreg->intrrs[0].erdp_lo, xhci->er.cur);
61 xhci->hcrreg->intrrs[0].erdp_lo = virt_to_phys(xhci->er.cur);
62 xhci->hcrreg->intrrs[0].erdp_hi = 0;
63 xhci->er.adv = 0;
64 }
65}
66
67void
68xhci_advance_event_ring(xhci_t *const xhci)
69{
70 xhci->er.cur++;
71 xhci->er.adv = 1;
72 if (xhci->er.cur == xhci->er.last) {
73 xhci_spew("Roll over in event ring\n");
74 xhci->er.cur = xhci->er.ring;
75 xhci->er.ccs ^= 1;
76 xhci_update_event_dq(xhci);
77 }
78}
79
80static void
81xhci_handle_transfer_event(xhci_t *const xhci)
82{
83 const trb_t *const ev = xhci->er.cur;
84
85 const int cc = TRB_GET(CC, ev);
86 const int id = TRB_GET(ID, ev);
87 const int ep = TRB_GET(EP, ev);
88
Nico Huber90292652013-06-13 14:37:15 +020089 intrq_t *intrq;
90
91 if (id && id <= xhci->max_slots_en &&
Julius Werner1f864342013-09-03 17:15:31 -070092 (intrq = xhci->dev[id].interrupt_queues[ep])) {
Nico Huber90292652013-06-13 14:37:15 +020093 /* It's a running interrupt endpoint */
94 intrq->ready = phys_to_virt(ev->ptr_low);
95 if (cc == CC_SUCCESS || cc == CC_SHORT_PACKET) {
96 TRB_SET(TL, intrq->ready,
97 intrq->size - TRB_GET(EVTL, ev));
98 } else {
99 xhci_debug("Interrupt Transfer failed: %d\n",
100 cc);
101 TRB_SET(TL, intrq->ready, 0);
102 }
103 } else if (cc == CC_STOPPED || cc == CC_STOPPED_LENGTH_INVALID) {
104 /* Ignore 'Forced Stop Events' */
105 } else {
106 xhci_debug("Warning: "
107 "Spurious transfer event for ID %d, EP %d:\n"
108 " Pointer: 0x%08x%08x\n"
109 " TL: 0x%06x\n"
110 " CC: %d\n",
111 id, ep,
112 ev->ptr_high, ev->ptr_low,
113 TRB_GET(EVTL, ev), cc);
114 }
115 xhci_advance_event_ring(xhci);
116}
117
118static void
119xhci_handle_command_completion_event(xhci_t *const xhci)
120{
121 const trb_t *const ev = xhci->er.cur;
122
123 xhci_debug("Warning: Spurious command completion event:\n"
124 " Pointer: 0x%08x%08x\n"
125 " CC: %d\n"
126 " Slot ID: %d\n"
127 " Cycle: %d\n",
128 ev->ptr_high, ev->ptr_low,
129 TRB_GET(CC, ev), TRB_GET(ID, ev), ev->control & TRB_CYCLE);
130 xhci_advance_event_ring(xhci);
131}
132
133static void
134xhci_handle_host_controller_event(xhci_t *const xhci)
135{
136 const trb_t *const ev = xhci->er.cur;
137
138 const int cc = TRB_GET(CC, ev);
139 switch (cc) {
140 case CC_EVENT_RING_FULL_ERROR:
141 xhci_debug("Event ring full! (@%p)\n", xhci->er.cur);
142 /*
143 * If we get here, we have processed the whole queue:
144 * xHC pushes this event, when it sees the ring full,
145 * full of other events.
146 * IMO it's save and necessary to update the dequeue
147 * pointer here.
148 */
149 xhci_advance_event_ring(xhci);
150 xhci_update_event_dq(xhci);
151 break;
152 default:
153 xhci_debug("Warning: Spurious host controller event: %d\n", cc);
154 xhci_advance_event_ring(xhci);
155 break;
156 }
157}
158
159/* handle standard types:
160 * - command completion event
161 * - port status change event
162 * - transfer event
163 * - host controller event
164 */
165static void
166xhci_handle_event(xhci_t *const xhci)
167{
168 const trb_t *const ev = xhci->er.cur;
169
170 const int trb_type = TRB_GET(TT, ev);
171 switch (trb_type) {
172 /* Either pass along the event or advance event ring */
173 case TRB_EV_TRANSFER:
174 xhci_handle_transfer_event(xhci);
175 break;
176 case TRB_EV_CMD_CMPL:
177 xhci_handle_command_completion_event(xhci);
178 break;
179 case TRB_EV_PORTSC:
180 xhci_debug("Port Status Change Event for %d: %d\n",
181 TRB_GET(PORT, ev), TRB_GET(CC, ev));
182 /* We ignore the event as we look for the PORTSC
183 registers instead, at a time when it suits _us_. */
184 xhci_advance_event_ring(xhci);
185 break;
186 case TRB_EV_HOST:
187 xhci_handle_host_controller_event(xhci);
188 break;
189 default:
190 xhci_debug("Warning: Spurious event: %d, Completion Code: %d\n",
191 trb_type, TRB_GET(CC, ev));
192 xhci_advance_event_ring(xhci);
193 break;
194 }
195}
196
197void
198xhci_handle_events(xhci_t *const xhci)
199{
200 while (xhci_event_ready(&xhci->er))
201 xhci_handle_event(xhci);
202 xhci_update_event_dq(xhci);
203}
204
205static unsigned long
206xhci_wait_for_event(const event_ring_t *const er,
207 unsigned long *const timeout_us)
208{
209 while (!xhci_event_ready(er) && *timeout_us) {
210 --*timeout_us;
211 udelay(1);
212 }
213 return *timeout_us;
214}
215
216static unsigned long
217xhci_wait_for_event_type(xhci_t *const xhci,
218 const int trb_type,
219 unsigned long *const timeout_us)
220{
221 while (xhci_wait_for_event(&xhci->er, timeout_us)) {
222 if (TRB_GET(TT, xhci->er.cur) == trb_type)
223 break;
224
225 xhci_handle_event(xhci);
226 }
227 return *timeout_us;
228}
229
230/* returns cc of command in question (pointed to by `address`) */
231int
232xhci_wait_for_command_aborted(xhci_t *const xhci, const trb_t *const address)
233{
234 /*
235 * Specification says that something might be seriously wrong, if
236 * we don't get a response after 5s. Still, let the caller decide,
237 * what to do then.
238 */
239 unsigned long timeout_us = 5 * 1000 * 1000; /* 5s */
240 int cc = TIMEOUT;
241 /*
242 * Expects two command completion events:
243 * The first with CC == COMMAND_ABORTED should point to address,
244 * the second with CC == COMMAND_RING_STOPPED should point to new dq.
245 */
246 while (xhci_wait_for_event_type(xhci, TRB_EV_CMD_CMPL, &timeout_us)) {
247 if ((xhci->er.cur->ptr_low == virt_to_phys(address)) &&
248 (xhci->er.cur->ptr_high == 0)) {
249 cc = TRB_GET(CC, xhci->er.cur);
250 xhci_advance_event_ring(xhci);
251 break;
252 }
253
254 xhci_handle_command_completion_event(xhci);
255 }
256 if (!timeout_us)
257 xhci_debug("Warning: Timed out waiting for COMMAND_ABORTED.\n");
258 while (xhci_wait_for_event_type(xhci, TRB_EV_CMD_CMPL, &timeout_us)) {
259 if (TRB_GET(CC, xhci->er.cur) == CC_COMMAND_RING_STOPPED) {
260 xhci->cr.cur = phys_to_virt(xhci->er.cur->ptr_low);
261 xhci_advance_event_ring(xhci);
262 break;
263 }
264
265 xhci_handle_command_completion_event(xhci);
266 }
267 if (!timeout_us)
268 xhci_debug("Warning: Timed out "
269 "waiting for COMMAND_RING_STOPPED.\n");
270 xhci_update_event_dq(xhci);
271 return cc;
272}
273
274/*
275 * returns cc of command in question (pointed to by `address`)
276 * caller should abort command if cc is TIMEOUT
277 */
278int
279xhci_wait_for_command_done(xhci_t *const xhci,
280 const trb_t *const address,
281 const int clear_event)
282{
283 /*
284 * The Address Device Command should take most time, as it has to
285 * communicate with the USB device. Set address processing shouldn't
286 * take longer than 50ms (at the slave). Let's take a timeout of
287 * 100ms.
288 */
289 unsigned long timeout_us = 100 * 1000; /* 100ms */
290 int cc = TIMEOUT;
291 while (xhci_wait_for_event_type(xhci, TRB_EV_CMD_CMPL, &timeout_us)) {
292 if ((xhci->er.cur->ptr_low == virt_to_phys(address)) &&
293 (xhci->er.cur->ptr_high == 0)) {
294 cc = TRB_GET(CC, xhci->er.cur);
295 break;
296 }
297
298 xhci_handle_command_completion_event(xhci);
299 }
300 if (!timeout_us) {
301 xhci_debug("Warning: Timed out waiting for TRB_EV_CMD_CMPL.\n");
302 } else if (clear_event) {
303 xhci_advance_event_ring(xhci);
304 }
305 xhci_update_event_dq(xhci);
306 return cc;
307}
308
Julius Wernere9738db2013-02-21 13:41:40 -0800309/* returns amount of bytes transferred on success, negative CC on error */
Nico Huber90292652013-06-13 14:37:15 +0200310int
311xhci_wait_for_transfer(xhci_t *const xhci, const int slot_id, const int ep_id)
312{
313 xhci_spew("Waiting for transfer on ID %d EP %d\n", slot_id, ep_id);
Julius Werner5f9a3f72015-08-03 14:21:07 -0700314 /* 3s for all types of transfers */ /* TODO: test, wait longer? */
315 unsigned long timeout_us = 3 * 1000 * 1000;
316 int ret = TIMEOUT;
Nico Huber90292652013-06-13 14:37:15 +0200317 while (xhci_wait_for_event_type(xhci, TRB_EV_TRANSFER, &timeout_us)) {
318 if (TRB_GET(ID, xhci->er.cur) == slot_id &&
319 TRB_GET(EP, xhci->er.cur) == ep_id) {
Julius Werner5f9a3f72015-08-03 14:21:07 -0700320 ret = -TRB_GET(CC, xhci->er.cur);
321 if (ret == -CC_SUCCESS || ret == -CC_SHORT_PACKET)
322 ret = TRB_GET(EVTL, xhci->er.cur);
Nico Huber90292652013-06-13 14:37:15 +0200323 xhci_advance_event_ring(xhci);
324 break;
325 }
326
327 xhci_handle_transfer_event(xhci);
328 }
329 if (!timeout_us)
330 xhci_debug("Warning: Timed out waiting for TRB_EV_TRANSFER.\n");
331 xhci_update_event_dq(xhci);
Julius Werner5f9a3f72015-08-03 14:21:07 -0700332 return ret;
Nico Huber90292652013-06-13 14:37:15 +0200333}