1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2016 Joyent, Inc.
14  */
15 
16 /*
17  * Device Context Base Address Array (DCBAA) Management and Scratchpad
18  * management. This is also used to manage the device slot contexts in shared
19  * memory.
20  *
21  * Please see the big theory statement in xhci.c for more information.
22  */
23 
24 #include <sys/usb/hcd/xhci/xhci.h>
25 #include <sys/byteorder.h>
26 
27 static void
xhci_scratchpad_fini(xhci_t * xhcip)28 xhci_scratchpad_fini(xhci_t *xhcip)
29 {
30 	xhci_scratchpad_t *xsp = &xhcip->xhci_scratchpad;
31 
32 	if (xsp->xsp_scratch_dma != NULL) {
33 		int i, npages;
34 		npages = xhcip->xhci_caps.xcap_max_scratch;
35 		for (i = 0; i < npages; i++) {
36 			xhci_dma_free(&xsp->xsp_scratch_dma[i]);
37 		}
38 		kmem_free(xsp->xsp_scratch_dma,
39 		    sizeof (xhci_dma_buffer_t) * npages);
40 		xsp->xsp_scratch_dma = NULL;
41 	}
42 	xhci_dma_free(&xsp->xsp_addr_dma);
43 	xsp->xsp_addrs = NULL;
44 }
45 
46 void
xhci_context_fini(xhci_t * xhcip)47 xhci_context_fini(xhci_t *xhcip)
48 {
49 	xhci_scratchpad_fini(xhcip);
50 	xhci_dma_free(&xhcip->xhci_dcbaa.xdc_dma);
51 	xhcip->xhci_dcbaa.xdc_base_addrs = NULL;
52 }
53 
54 static int
xhci_scratchpad_alloc(xhci_t * xhcip)55 xhci_scratchpad_alloc(xhci_t *xhcip)
56 {
57 	int npages, i;
58 	xhci_scratchpad_t *xsp;
59 	ddi_device_acc_attr_t acc;
60 	ddi_dma_attr_t attr;
61 
62 	/*
63 	 * First allocate the scratchpad table, then the actual pages.
64 	 */
65 	ASSERT(xhcip->xhci_caps.xcap_max_scratch > 0);
66 	npages = xhcip->xhci_caps.xcap_max_scratch;
67 	xhci_dma_acc_attr(xhcip, &acc);
68 	xhci_dma_dma_attr(xhcip, &attr);
69 	xsp = &xhcip->xhci_scratchpad;
70 	if (xhci_dma_alloc(xhcip, &xsp->xsp_addr_dma, &attr, &acc,
71 	    B_TRUE, sizeof (uint64_t) * npages, B_FALSE) == B_FALSE) {
72 		xhci_log(xhcip, "!failed to allocate DMA memory for device "
73 		    "context");
74 		return (ENOMEM);
75 	}
76 
77 	xsp->xsp_addrs = (void *)xsp->xsp_addr_dma.xdb_va;
78 
79 	/*
80 	 * Note that the scratchpad memory itself can actually be relaxed, which
81 	 * is almost better, since we'll never actually access this memory
82 	 * ourselves, only use it to tear things down. As such, we also bump up
83 	 * the segment boundary restrictions, since we don't really have any for
84 	 * this memory.
85 	 */
86 	xhci_dma_scratchpad_attr(xhcip, &attr);
87 	xsp->xsp_scratch_dma = kmem_zalloc(sizeof (xhci_dma_buffer_t) * npages,
88 	    KM_SLEEP);
89 	for (i = 0; i < npages; i++) {
90 		if (xhci_dma_alloc(xhcip, &xsp->xsp_scratch_dma[i], &attr, &acc,
91 		    B_TRUE, xhcip->xhci_caps.xcap_pagesize, B_FALSE) ==
92 		    B_FALSE) {
93 			/*
94 			 * It is safe for us to call xhci_scratchpad_fini() in a
95 			 * partially constructed state. Because we've zeroed the
96 			 * structures in the above allocation, the DMA buffer
97 			 * teardown code can handle these zeroed or partially
98 			 * initialized structures correctly.
99 			 */
100 			xhci_scratchpad_fini(xhcip);
101 			xhci_log(xhcip, "!failed to allocate DMA memory for "
102 			    "device scratchpad");
103 			return (ENOMEM);
104 		}
105 	}
106 
107 	return (0);
108 }
109 
110 /*
111  * We always allocate the DCBAA based on its maximum possible size, simplifying
112  * the code and at worst wasting only a couple hundred bytes.
113  */
114 static int
xhci_dcbaa_alloc(xhci_t * xhcip)115 xhci_dcbaa_alloc(xhci_t *xhcip)
116 {
117 	xhci_dcbaa_t *dcb;
118 	ddi_device_acc_attr_t acc;
119 	ddi_dma_attr_t attr;
120 
121 	dcb = &xhcip->xhci_dcbaa;
122 	xhci_dma_acc_attr(xhcip, &acc);
123 	xhci_dma_dma_attr(xhcip, &attr);
124 	if (xhci_dma_alloc(xhcip, &dcb->xdc_dma, &attr, &acc,
125 	    B_FALSE, sizeof (uint64_t) * XHCI_MAX_SLOTS, B_FALSE) == B_FALSE) {
126 		xhci_log(xhcip, "!failed to allocate DMA memory for device "
127 		    "context");
128 		return (ENOMEM);
129 	}
130 
131 	/*
132 	 * This lint gag is safe, because we always have at least a 64-byte
133 	 * alignment from the DMA attributes.
134 	 */
135 	/* LINTED: E_BAD_PTR_CAST_ALIGN */
136 	dcb->xdc_base_addrs = (uint64_t *)dcb->xdc_dma.xdb_va;
137 	return (0);
138 }
139 
140 /*
141  * We are called to initialize the DCBAA every time that we start the
142  * controller. This happens both the first time we bring it up and after we
143  * reset it from errors. Therefore to initialize the DCBAA we need to do the
144  * following:
145  *
146  *   o Allocate DMA memory (if it doesn't already exist)
147  *   o If scratchpad slots have been requested, allocate and program them if
148  *     necessary
149  *   o Program the DCBAAP register.
150  */
151 int
xhci_context_init(xhci_t * xhcip)152 xhci_context_init(xhci_t *xhcip)
153 {
154 	int ret;
155 	xhci_dcbaa_t *dcb = &xhcip->xhci_dcbaa;
156 
157 	if (dcb->xdc_base_addrs == NULL) {
158 		if ((ret = xhci_dcbaa_alloc(xhcip)) != 0)
159 			return (ret);
160 	}
161 
162 	bzero(dcb->xdc_base_addrs, sizeof (uint64_t) * XHCI_MAX_SLOTS);
163 	if (xhcip->xhci_caps.xcap_max_scratch != 0) {
164 		int i, npages;
165 		xhci_scratchpad_t *xsp = &xhcip->xhci_scratchpad;
166 
167 		if (xsp->xsp_addrs == NULL &&
168 		    (ret = xhci_scratchpad_alloc(xhcip)) != 0) {
169 			xhci_context_fini(xhcip);
170 			return (ret);
171 		}
172 
173 		dcb->xdc_base_addrs[XHCI_DCBAA_SCRATCHPAD_INDEX] =
174 		    LE_64(xhci_dma_pa(&xsp->xsp_addr_dma));
175 
176 		npages = xhcip->xhci_caps.xcap_max_scratch;
177 		for (i = 0; i < npages; i++) {
178 			xsp->xsp_addrs[i] =
179 			    LE_64(xhci_dma_pa(&xsp->xsp_scratch_dma[i]));
180 		}
181 
182 		XHCI_DMA_SYNC(xsp->xsp_addr_dma, DDI_DMA_SYNC_FORDEV);
183 		if (xhci_check_dma_handle(xhcip, &xsp->xsp_addr_dma) !=
184 		    DDI_FM_OK) {
185 			ddi_fm_service_impact(xhcip->xhci_dip,
186 			    DDI_SERVICE_LOST);
187 			return (EIO);
188 		}
189 	}
190 
191 	XHCI_DMA_SYNC(dcb->xdc_dma, DDI_DMA_SYNC_FORDEV);
192 	if (xhci_check_dma_handle(xhcip, &dcb->xdc_dma) != DDI_FM_OK) {
193 		ddi_fm_service_impact(xhcip->xhci_dip, DDI_SERVICE_LOST);
194 		return (EIO);
195 	}
196 
197 	xhci_put64(xhcip, XHCI_R_OPER, XHCI_DCBAAP,
198 	    LE_64(xhci_dma_pa(&dcb->xdc_dma)));
199 	if (xhci_check_regs_acc(xhcip) != DDI_FM_OK) {
200 		ddi_fm_service_impact(xhcip->xhci_dip,
201 		    DDI_SERVICE_LOST);
202 		return (EIO);
203 	}
204 
205 	return (0);
206 }
207 
208 /*
209  * Initialize the default output context. It should already have been zeroed, so
210  * all we need to do is insert it into the right place in the device context
211  * array.
212  */
213 boolean_t
xhci_context_slot_output_init(xhci_t * xhcip,xhci_device_t * xd)214 xhci_context_slot_output_init(xhci_t *xhcip, xhci_device_t *xd)
215 {
216 	xhci_dcbaa_t *dcb = &xhcip->xhci_dcbaa;
217 	VERIFY(xd->xd_slot > 0 &&
218 	    xd->xd_slot <= xhcip->xhci_caps.xcap_max_slots);
219 
220 	xhcip->xhci_dcbaa.xdc_base_addrs[xd->xd_slot] =
221 	    LE_64(xhci_dma_pa(&xd->xd_octx));
222 	XHCI_DMA_SYNC(dcb->xdc_dma, DDI_DMA_SYNC_FORDEV);
223 	if (xhci_check_dma_handle(xhcip, &dcb->xdc_dma) != DDI_FM_OK) {
224 		xhci_error(xhcip, "failed to initialize slot output context "
225 		    "for device on port %d, slot %d: fatal FM error "
226 		    "synchronizing DCBAA slot DMA memory", xd->xd_slot,
227 		    xd->xd_port);
228 		xhci_fm_runtime_reset(xhcip);
229 		return (B_FALSE);
230 	}
231 
232 	return (B_TRUE);
233 }
234 
235 void
xhci_context_slot_output_fini(xhci_t * xhcip,xhci_device_t * xd)236 xhci_context_slot_output_fini(xhci_t *xhcip, xhci_device_t *xd)
237 {
238 	xhci_dcbaa_t *dcb = &xhcip->xhci_dcbaa;
239 	VERIFY(xd->xd_slot > 0 &&
240 	    xd->xd_slot <= xhcip->xhci_caps.xcap_max_slots);
241 
242 	xhcip->xhci_dcbaa.xdc_base_addrs[xd->xd_slot] = 0ULL;
243 	XHCI_DMA_SYNC(dcb->xdc_dma, DDI_DMA_SYNC_FORDEV);
244 	if (xhci_check_dma_handle(xhcip, &dcb->xdc_dma) != DDI_FM_OK) {
245 		xhci_error(xhcip, "failed to finalize slot output context "
246 		    "for device on port %d, slot %d: fatal FM error "
247 		    "synchronizing DCBAA slot DMA memory", xd->xd_slot,
248 		    xd->xd_port);
249 		xhci_fm_runtime_reset(xhcip);
250 	}
251 }
252