Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : *
3 : : * Copyright(c) 2019-2021 Xilinx, Inc.
4 : : * Copyright(c) 2019 Solarflare Communications Inc.
5 : : */
6 : :
7 : : #include "efx.h"
8 : : #include "efx_impl.h"
9 : :
10 : : #if EFSYS_OPT_PCI
11 : :
12 : : __checkReturn efx_rc_t
13 : 0 : efx_pci_config_next_ext_cap(
14 : : __in efsys_pci_config_t *espcp,
15 : : __in const efx_pci_ops_t *epop,
16 : : __inout size_t *offsetp)
17 : : {
18 : : efx_dword_t hdr;
19 : : efx_rc_t rc = 0;
20 : : size_t next;
21 : :
22 [ # # ]: 0 : if (offsetp == NULL) {
23 : : rc = EINVAL;
24 : 0 : goto fail1;
25 : : }
26 : :
27 [ # # ]: 0 : if (*offsetp == 0) {
28 : 0 : *offsetp = ESE_GZ_PCI_BASE_CONFIG_SPACE_SIZE;
29 : : } else {
30 : 0 : rc = epop->epo_config_readd(espcp, *offsetp +
31 : : (EFX_LOW_BIT(ESF_GZ_PCI_EXPRESS_XCAP_ID) / 8),
32 : : &hdr);
33 [ # # ]: 0 : if (rc != 0) {
34 : : rc = EIO;
35 : 0 : goto fail2;
36 : : }
37 : :
38 : 0 : next = EFX_DWORD_FIELD(hdr, ESF_GZ_PCI_EXPRESS_XCAP_NEXT);
39 [ # # ]: 0 : if (next < ESE_GZ_PCI_BASE_CONFIG_SPACE_SIZE)
40 : : rc = ENOENT;
41 : : else
42 : 0 : *offsetp = next;
43 : : }
44 : :
45 : : /*
46 : : * Returns 0 if the next capability is present otherwise ENOENT
47 : : * indicating that the function finished correctly.
48 : : */
49 : : return (rc);
50 : :
51 : : fail2:
52 : : EFSYS_PROBE(fail2);
53 : : fail1:
54 : : EFSYS_PROBE1(fail1, efx_rc_t, rc);
55 : :
56 : : return (rc);
57 : : }
58 : :
59 : : __checkReturn efx_rc_t
60 : 0 : efx_pci_config_find_next_ext_cap(
61 : : __in efsys_pci_config_t *espcp,
62 : : __in const efx_pci_ops_t *epop,
63 : : __in uint16_t cap_id,
64 : : __inout size_t *offsetp)
65 : : {
66 : : efx_dword_t hdr;
67 : : size_t position;
68 : : efx_rc_t rc;
69 : :
70 [ # # ]: 0 : if (offsetp == NULL) {
71 : : rc = EINVAL;
72 : 0 : goto fail1;
73 : : }
74 : :
75 : 0 : position = *offsetp;
76 : :
77 : : while (1) {
78 : 0 : rc = efx_pci_config_next_ext_cap(espcp, epop, &position);
79 [ # # ]: 0 : if (rc != 0) {
80 [ # # ]: 0 : if (rc == ENOENT)
81 : : break;
82 : : else
83 : 0 : goto fail2;
84 : : }
85 : :
86 : 0 : rc = epop->epo_config_readd(espcp, position +
87 : : (EFX_LOW_BIT(ESF_GZ_PCI_EXPRESS_XCAP_ID) / 8),
88 : : &hdr);
89 [ # # ]: 0 : if (rc != 0) {
90 : : rc = EIO;
91 : 0 : goto fail3;
92 : : }
93 : :
94 [ # # ]: 0 : if (EFX_DWORD_FIELD(hdr, ESF_GZ_PCI_EXPRESS_XCAP_ID) ==
95 : : cap_id) {
96 : 0 : *offsetp = position;
97 : : rc = 0;
98 : 0 : break;
99 : : }
100 : : }
101 : :
102 : : /*
103 : : * Returns 0 if found otherwise ENOENT indicating that search finished
104 : : * correctly.
105 : : */
106 : : return (rc);
107 : :
108 : : fail3:
109 : : EFSYS_PROBE(fail3);
110 : : fail2:
111 : : EFSYS_PROBE(fail2);
112 : : fail1:
113 : : EFSYS_PROBE1(fail1, efx_rc_t, rc);
114 : :
115 : : return (rc);
116 : : }
117 : :
118 : : __checkReturn efx_rc_t
119 : 0 : efx_pci_find_next_xilinx_cap_table(
120 : : __in efsys_pci_config_t *espcp,
121 : : __in const efx_pci_ops_t *epop,
122 : : __inout size_t *pci_cap_offsetp,
123 : : __out unsigned int *xilinx_tbl_barp,
124 : : __out efsys_dma_addr_t *xilinx_tbl_offsetp)
125 : : {
126 : : size_t cap_offset;
127 : : efx_rc_t rc;
128 : :
129 [ # # ]: 0 : if (pci_cap_offsetp == NULL) {
130 : : rc = EINVAL;
131 : 0 : goto fail1;
132 : : }
133 : :
134 : 0 : cap_offset = *pci_cap_offsetp;
135 : :
136 : : while (1) {
137 : : unsigned int tbl_bar;
138 : : efsys_dma_addr_t tbl_offset;
139 : :
140 : 0 : rc = efx_pci_config_find_next_ext_cap(espcp, epop,
141 : : ESE_GZ_PCI_EXPRESS_XCAP_ID_VNDR, &cap_offset);
142 [ # # ]: 0 : if (rc != 0) {
143 [ # # ]: 0 : if (rc == ENOENT)
144 : : break;
145 : : else
146 : 0 : goto fail2;
147 : : }
148 : :
149 : : /*
150 : : * The found extended PCI capability is a vendor-specific
151 : : * capability, but not necessarily a Xilinx capabilities table
152 : : * locator. Try to read it and skip it if the capability is
153 : : * not the locator.
154 : : */
155 : 0 : rc = efx_pci_read_ext_cap_xilinx_table(espcp, epop, cap_offset,
156 : : &tbl_bar, &tbl_offset);
157 [ # # ]: 0 : if (rc == 0) {
158 : 0 : *xilinx_tbl_barp = tbl_bar;
159 : 0 : *xilinx_tbl_offsetp = tbl_offset;
160 : 0 : *pci_cap_offsetp = cap_offset;
161 : 0 : break;
162 : : } else {
163 [ # # ]: 0 : if (rc == ENOENT)
164 : 0 : continue;
165 : : else
166 : 0 : goto fail3;
167 : : }
168 : : }
169 : :
170 : : /*
171 : : * Returns 0 if found otherwise ENOENT indicating that search finished
172 : : * correctly.
173 : : */
174 : 0 : return (rc);
175 : :
176 : : fail3:
177 : : EFSYS_PROBE(fail3);
178 : : fail2:
179 : : EFSYS_PROBE(fail2);
180 : : fail1:
181 : : EFSYS_PROBE1(fail1, efx_rc_t, rc);
182 : :
183 : : return (rc);
184 : : }
185 : :
186 : : __checkReturn efx_rc_t
187 : 0 : efx_pci_read_ext_cap_xilinx_table(
188 : : __in efsys_pci_config_t *espcp,
189 : : __in const efx_pci_ops_t *epop,
190 : : __in size_t cap_offset,
191 : : __out unsigned int *barp,
192 : : __out efsys_dma_addr_t *offsetp)
193 : : {
194 : 0 : size_t vsec_offset = cap_offset + ESE_GZ_PCI_EXPRESS_XCAP_HDR_SIZE;
195 : : efx_dword_t cap_hdr;
196 : : efx_oword_t vsec;
197 : : uint32_t vsec_len;
198 : : uint32_t vsec_id;
199 : : uint32_t vsec_rev;
200 : : uint32_t offset_low;
201 : : uint32_t offset_high = 0;
202 : : unsigned int bar;
203 : : efsys_dma_addr_t offset;
204 : : efx_rc_t rc;
205 : :
206 : 0 : rc = epop->epo_config_readd(espcp, cap_offset +
207 : : (EFX_LOW_BIT(ESF_GZ_PCI_EXPRESS_XCAP_ID) / 8),
208 : : &cap_hdr);
209 [ # # ]: 0 : if (rc != 0) {
210 : : rc = EIO;
211 : 0 : goto fail1;
212 : : }
213 : :
214 [ # # ]: 0 : if (EFX_DWORD_FIELD(cap_hdr, ESF_GZ_PCI_EXPRESS_XCAP_VER) !=
215 : : ESE_GZ_PCI_EXPRESS_XCAP_VER_VSEC) {
216 : : rc = EINVAL;
217 : 0 : goto fail2;
218 : : }
219 : :
220 : 0 : rc = epop->epo_config_readd(espcp, vsec_offset +
221 : : (EFX_LOW_BIT(ESF_GZ_VSEC_ID) / 8),
222 : : &vsec.eo_dword[0]);
223 [ # # ]: 0 : if (rc != 0) {
224 : : rc = EIO;
225 : 0 : goto fail3;
226 : : }
227 : :
228 : 0 : vsec_len = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_LEN);
229 : 0 : vsec_id = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_ID);
230 : 0 : vsec_rev = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_VER);
231 : :
232 : : /*
233 : : * Condition of the vendor-specific extended PCI capability not being
234 : : * a Xilinx capabilities table locator.
235 : : */
236 [ # # ]: 0 : if (vsec_id != ESE_GZ_XILINX_VSEC_ID) {
237 : : rc = ENOENT;
238 : 0 : goto fail4;
239 : : }
240 : :
241 : 0 : if (vsec_rev != ESE_GZ_VSEC_VER_XIL_CFGBAR ||
242 [ # # ]: 0 : vsec_len < ESE_GZ_VSEC_LEN_MIN) {
243 : : rc = EINVAL;
244 : 0 : goto fail5;
245 : : }
246 : :
247 : 0 : rc = epop->epo_config_readd(espcp, vsec_offset +
248 : : (EFX_LOW_BIT(ESF_GZ_VSEC_TBL_BAR) / 8),
249 : : &vsec.eo_dword[1]);
250 [ # # ]: 0 : if (rc != 0) {
251 : : rc = EIO;
252 : 0 : goto fail6;
253 : : }
254 : :
255 : 0 : bar = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_TBL_BAR);
256 : : offset_low = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_TBL_OFF_LO);
257 : :
258 [ # # ]: 0 : if (vsec_len >= ESE_GZ_VSEC_LEN_HIGH_OFFT) {
259 : 0 : rc = epop->epo_config_readd(espcp, vsec_offset +
260 : : (EFX_LOW_BIT(ESF_GZ_VSEC_TBL_OFF_HI) / 8),
261 : : &vsec.eo_dword[2]);
262 [ # # ]: 0 : if (rc != 0) {
263 : : rc = EIO;
264 : 0 : goto fail7;
265 : : }
266 : :
267 : 0 : offset_high = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_TBL_OFF_HI);
268 : : }
269 : :
270 : : /* High bits of low offset are discarded by the shift */
271 : 0 : offset = offset_low << ESE_GZ_VSEC_TBL_OFF_LO_BYTES_SHIFT;
272 : :
273 : : /*
274 : : * Avoid the 'left shift count >= width of type' warning on systems
275 : : * without uint64_t support.
276 : : */
277 : : #if EFSYS_HAS_UINT64
278 : 0 : offset |= (uint64_t)offset_high << ESE_GZ_VSEC_TBL_OFF_HI_BYTES_SHIFT;
279 : : #else
280 : : _NOTE(ARGUNUSED(offset_high))
281 : : #endif
282 : :
283 : 0 : *offsetp = offset;
284 : 0 : *barp = bar;
285 : :
286 : 0 : return (0);
287 : :
288 : : fail7:
289 : : EFSYS_PROBE(fail7);
290 : : fail6:
291 : : EFSYS_PROBE(fail6);
292 : : fail5:
293 : : EFSYS_PROBE(fail5);
294 : : fail4:
295 : : EFSYS_PROBE(fail4);
296 : : fail3:
297 : : EFSYS_PROBE(fail3);
298 : : fail2:
299 : : EFSYS_PROBE(fail2);
300 : : fail1:
301 : : EFSYS_PROBE1(fail1, efx_rc_t, rc);
302 : :
303 : : return (rc);
304 : : }
305 : :
306 : : __checkReturn efx_rc_t
307 : 0 : efx_pci_xilinx_cap_tbl_find(
308 : : __in efsys_bar_t *esbp,
309 : : __in uint32_t format_id,
310 : : __in boolean_t skip_first,
311 : : __inout efsys_dma_addr_t *entry_offsetp)
312 : : {
313 : : efsys_dma_addr_t offset;
314 : : boolean_t skip = skip_first;
315 : : efx_qword_t header;
316 : : uint32_t format;
317 : : uint32_t last;
318 : : efx_rc_t rc;
319 : :
320 [ # # ]: 0 : if (entry_offsetp == NULL) {
321 : : rc = EINVAL;
322 : 0 : goto fail1;
323 : : }
324 : :
325 : 0 : offset = *entry_offsetp;
326 : : rc = ENOENT;
327 : : /*
328 : : * SF-119689-TC Riverhead Host Interface section 4.2.2.
329 : : * describes the following discovery steps.
330 : : */
331 : : do {
332 : : /*
333 : : * Xilinx Capabilities Table requires 32bit aligned reads.
334 : : * See SF-119689-TC section 4.2.2 "Discovery Steps".
335 : : */
336 [ # # ]: 0 : EFSYS_BAR_READD(esbp, offset +
337 : : (EFX_LOW_BIT(ESF_GZ_CFGBAR_ENTRY_FORMAT) / 8),
338 : : &header.eq_dword[0], B_FALSE);
339 : 0 : EFSYS_BAR_READD(esbp, offset +
340 : : (EFX_LOW_BIT(ESF_GZ_CFGBAR_ENTRY_SIZE) / 8),
341 : : &header.eq_dword[1], B_FALSE);
342 : :
343 : 0 : format = EFX_QWORD_FIELD32(header, ESF_GZ_CFGBAR_ENTRY_FORMAT);
344 : 0 : last = EFX_QWORD_FIELD32(header, ESF_GZ_CFGBAR_ENTRY_LAST);
345 : :
346 [ # # ]: 0 : if (skip == B_FALSE && format == format_id) {
347 : 0 : *entry_offsetp = offset;
348 : : rc = 0;
349 : 0 : break;
350 : : }
351 : :
352 : 0 : offset += EFX_QWORD_FIELD32(header, ESF_GZ_CFGBAR_ENTRY_SIZE);
353 : : skip = B_FALSE;
354 [ # # ]: 0 : } while (last == B_FALSE);
355 : :
356 : : /*
357 : : * Returns 0 if found otherwise ENOENT indicating that
358 : : * search finished correctly.
359 : : */
360 : : return (rc);
361 : :
362 : : fail1:
363 : : EFSYS_PROBE1(fail1, efx_rc_t, rc);
364 : :
365 : 0 : return (rc);
366 : : }
367 : :
368 : : #endif /* EFSYS_OPT_PCI */
|