1 /*
2 *
3 * Copyright (C) 2013 secunet Security Networks AG
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <inttypes.h>
30 #include <arch/virtual.h>
31 #include "xhci_private.h"
32
33 trb_t *
xhci_next_command_trb(xhci_t * const xhci)34 xhci_next_command_trb(xhci_t *const xhci)
35 {
36 xhci_clear_trb(xhci->cr.cur, xhci->cr.pcs);
37 return xhci->cr.cur;
38 }
39
40 void
xhci_post_command(xhci_t * const xhci)41 xhci_post_command(xhci_t *const xhci)
42 {
43 xhci_debug("Command %d (@%p)\n",
44 TRB_GET(TT, xhci->cr.cur), xhci->cr.cur);
45
46 TRB_SET(C, xhci->cr.cur, xhci->cr.pcs);
47 ++xhci->cr.cur;
48
49 /* pass command trb to hardware */
50 wmb();
51 /* Ring the doorbell */
52 xhci->dbreg[0] = 0;
53
54 while (TRB_GET(TT, xhci->cr.cur) == TRB_LINK) {
55 xhci_debug("Handling LINK pointer (@%p)\n", xhci->cr.cur);
56 const int tc = TRB_GET(TC, xhci->cr.cur);
57 TRB_SET(C, xhci->cr.cur, xhci->cr.pcs);
58 xhci->cr.cur = phys_to_virt(xhci->cr.cur->ptr_low);
59 if (tc)
60 xhci->cr.pcs ^= 1;
61 }
62 }
63
64 static int
xhci_wait_for_command(xhci_t * const xhci,const trb_t * const cmd_trb,const int clear_event)65 xhci_wait_for_command(xhci_t *const xhci,
66 const trb_t *const cmd_trb,
67 const int clear_event)
68 {
69 int cc;
70
71 cc = xhci_wait_for_command_done(xhci, cmd_trb, clear_event);
72 if (cc != TIMEOUT)
73 return cc;
74
75 /* Abort command on timeout */
76 xhci_debug("Aborting command (@%p), CRCR: 0x%"PRIx32"\n",
77 cmd_trb, xhci->opreg->crcr_lo);
78 /*
79 * Ref. xHCI Specification Revision 1.2, May 2019.
80 * Section 5.4.5, Table 5-24.
81 *
82 * Abort the command and stop the ring.
83 */
84 xhci->opreg->crcr_lo |= CRCR_CA;
85 xhci->opreg->crcr_hi = 0;
86 cc = xhci_wait_for_command_aborted(xhci, cmd_trb);
87
88 if (xhci->opreg->crcr_lo & CRCR_CRR)
89 fatal("xhci_wait_for_command: Command ring still running\n");
90
91 return cc;
92 }
93
94 /*
95 * xhci_cmd_* return >= 0: xhci completion code (cc)
96 * < 0: driver error code
97 */
98
99 int
xhci_cmd_enable_slot(xhci_t * const xhci,int * const slot_id)100 xhci_cmd_enable_slot(xhci_t *const xhci, int *const slot_id)
101 {
102 trb_t *const cmd = xhci_next_command_trb(xhci);
103 TRB_SET(TT, cmd, TRB_CMD_ENABLE_SLOT);
104 xhci_post_command(xhci);
105
106 int cc = xhci_wait_for_command(xhci, cmd, 0);
107 if (cc >= 0) {
108 if (cc == CC_SUCCESS) {
109 *slot_id = TRB_GET(ID, xhci->er.cur);
110 if (*slot_id > xhci->max_slots_en)
111 cc = CONTROLLER_ERROR;
112 }
113 xhci_advance_event_ring(xhci);
114 xhci_handle_events(xhci);
115 }
116 return cc;
117 }
118
119 int
xhci_cmd_disable_slot(xhci_t * const xhci,const int slot_id)120 xhci_cmd_disable_slot(xhci_t *const xhci, const int slot_id)
121 {
122 trb_t *const cmd = xhci_next_command_trb(xhci);
123 TRB_SET(TT, cmd, TRB_CMD_DISABLE_SLOT);
124 TRB_SET(ID, cmd, slot_id);
125 xhci_post_command(xhci);
126
127 return xhci_wait_for_command(xhci, cmd, 1);
128 }
129
130 int
xhci_cmd_address_device(xhci_t * const xhci,const int slot_id,inputctx_t * const ic)131 xhci_cmd_address_device(xhci_t *const xhci,
132 const int slot_id,
133 inputctx_t *const ic)
134 {
135 trb_t *const cmd = xhci_next_command_trb(xhci);
136 TRB_SET(TT, cmd, TRB_CMD_ADDRESS_DEV);
137 TRB_SET(ID, cmd, slot_id);
138 cmd->ptr_low = virt_to_phys(ic->raw);
139 xhci_post_command(xhci);
140
141 return xhci_wait_for_command(xhci, cmd, 1);
142 }
143
144 int
xhci_cmd_configure_endpoint(xhci_t * const xhci,const int slot_id,const int config_id,inputctx_t * const ic)145 xhci_cmd_configure_endpoint(xhci_t *const xhci,
146 const int slot_id,
147 const int config_id,
148 inputctx_t *const ic)
149 {
150 trb_t *const cmd = xhci_next_command_trb(xhci);
151 TRB_SET(TT, cmd, TRB_CMD_CONFIGURE_EP);
152 TRB_SET(ID, cmd, slot_id);
153 cmd->ptr_low = virt_to_phys(ic->raw);
154 if (config_id == 0)
155 TRB_SET(DC, cmd, 1);
156 xhci_post_command(xhci);
157
158 return xhci_wait_for_command(xhci, cmd, 1);
159 }
160
161 int
xhci_cmd_evaluate_context(xhci_t * const xhci,const int slot_id,inputctx_t * const ic)162 xhci_cmd_evaluate_context(xhci_t *const xhci,
163 const int slot_id,
164 inputctx_t *const ic)
165 {
166 trb_t *const cmd = xhci_next_command_trb(xhci);
167 TRB_SET(TT, cmd, TRB_CMD_EVAL_CTX);
168 TRB_SET(ID, cmd, slot_id);
169 cmd->ptr_low = virt_to_phys(ic->raw);
170 xhci_post_command(xhci);
171
172 return xhci_wait_for_command(xhci, cmd, 1);
173 }
174
175 int
xhci_cmd_reset_endpoint(xhci_t * const xhci,const int slot_id,const int ep)176 xhci_cmd_reset_endpoint(xhci_t *const xhci, const int slot_id, const int ep)
177 {
178 trb_t *const cmd = xhci_next_command_trb(xhci);
179 TRB_SET(TT, cmd, TRB_CMD_RESET_EP);
180 TRB_SET(ID, cmd, slot_id);
181 TRB_SET(EP, cmd, ep);
182 xhci_post_command(xhci);
183
184 return xhci_wait_for_command(xhci, cmd, 1);
185 }
186
187 int
xhci_cmd_stop_endpoint(xhci_t * const xhci,const int slot_id,const int ep)188 xhci_cmd_stop_endpoint(xhci_t *const xhci, const int slot_id, const int ep)
189 {
190 trb_t *const cmd = xhci_next_command_trb(xhci);
191 TRB_SET(TT, cmd, TRB_CMD_STOP_EP);
192 TRB_SET(ID, cmd, slot_id);
193 TRB_SET(EP, cmd, ep);
194 xhci_post_command(xhci);
195
196 return xhci_wait_for_command(xhci, cmd, 1);
197 }
198
199 int
xhci_cmd_set_tr_dq(xhci_t * const xhci,const int slot_id,const int ep,trb_t * const dq_trb,const int dcs)200 xhci_cmd_set_tr_dq(xhci_t *const xhci, const int slot_id, const int ep,
201 trb_t *const dq_trb, const int dcs)
202 {
203 trb_t *const cmd = xhci_next_command_trb(xhci);
204 TRB_SET(TT, cmd, TRB_CMD_SET_TR_DQ);
205 TRB_SET(ID, cmd, slot_id);
206 TRB_SET(EP, cmd, ep);
207 cmd->ptr_low = virt_to_phys(dq_trb) | dcs;
208 xhci_post_command(xhci);
209
210 return xhci_wait_for_command(xhci, cmd, 1);
211 }
212