• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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