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