xref: /dpdk/drivers/common/sfc_efx/base/efx_pci.c (revision 3de9af2aaa680b33a9a597b8ec4d76163dee2029)
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
efx_pci_config_next_ext_cap(__in efsys_pci_config_t * espcp,__in const efx_pci_ops_t * epop,__inout size_t * offsetp)13 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 	if (offsetp == NULL) {
23 		rc = EINVAL;
24 		goto fail1;
25 	}
26 
27 	if (*offsetp == 0) {
28 		*offsetp = ESE_GZ_PCI_BASE_CONFIG_SPACE_SIZE;
29 	} else {
30 		rc = epop->epo_config_readd(espcp, *offsetp +
31 				(EFX_LOW_BIT(ESF_GZ_PCI_EXPRESS_XCAP_ID) / 8),
32 				&hdr);
33 		if (rc != 0) {
34 			rc = EIO;
35 			goto fail2;
36 		}
37 
38 		next = EFX_DWORD_FIELD(hdr, ESF_GZ_PCI_EXPRESS_XCAP_NEXT);
39 		if (next < ESE_GZ_PCI_BASE_CONFIG_SPACE_SIZE)
40 			rc = ENOENT;
41 		else
42 			*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
efx_pci_config_find_next_ext_cap(__in efsys_pci_config_t * espcp,__in const efx_pci_ops_t * epop,__in uint16_t cap_id,__inout size_t * offsetp)60 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 	if (offsetp == NULL) {
71 		rc = EINVAL;
72 		goto fail1;
73 	}
74 
75 	position = *offsetp;
76 
77 	while (1) {
78 		rc = efx_pci_config_next_ext_cap(espcp, epop, &position);
79 		if (rc != 0) {
80 			if (rc == ENOENT)
81 				break;
82 			else
83 				goto fail2;
84 		}
85 
86 		rc = epop->epo_config_readd(espcp, position +
87 				(EFX_LOW_BIT(ESF_GZ_PCI_EXPRESS_XCAP_ID) / 8),
88 				&hdr);
89 		if (rc != 0) {
90 			rc = EIO;
91 			goto fail3;
92 		}
93 
94 		if (EFX_DWORD_FIELD(hdr, ESF_GZ_PCI_EXPRESS_XCAP_ID) ==
95 		    cap_id) {
96 			*offsetp = position;
97 			rc = 0;
98 			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
efx_pci_find_next_xilinx_cap_table(__in efsys_pci_config_t * espcp,__in const efx_pci_ops_t * epop,__inout size_t * pci_cap_offsetp,__out unsigned int * xilinx_tbl_barp,__out efsys_dma_addr_t * xilinx_tbl_offsetp)119 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 	if (pci_cap_offsetp == NULL) {
130 		rc = EINVAL;
131 		goto fail1;
132 	}
133 
134 	cap_offset = *pci_cap_offsetp;
135 
136 	while (1) {
137 		unsigned int tbl_bar;
138 		efsys_dma_addr_t tbl_offset;
139 
140 		rc = efx_pci_config_find_next_ext_cap(espcp, epop,
141 				ESE_GZ_PCI_EXPRESS_XCAP_ID_VNDR, &cap_offset);
142 		if (rc != 0) {
143 			if (rc == ENOENT)
144 				break;
145 			else
146 				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 		rc = efx_pci_read_ext_cap_xilinx_table(espcp, epop, cap_offset,
156 						       &tbl_bar, &tbl_offset);
157 		if (rc == 0) {
158 			*xilinx_tbl_barp = tbl_bar;
159 			*xilinx_tbl_offsetp = tbl_offset;
160 			*pci_cap_offsetp = cap_offset;
161 			break;
162 		} else {
163 			if (rc == ENOENT)
164 				continue;
165 			else
166 				goto fail3;
167 		}
168 	}
169 
170 	/*
171 	 * Returns 0 if found otherwise ENOENT indicating that search finished
172 	 * correctly.
173 	 */
174 	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
efx_pci_read_ext_cap_xilinx_table(__in efsys_pci_config_t * espcp,__in const efx_pci_ops_t * epop,__in size_t cap_offset,__out unsigned int * barp,__out efsys_dma_addr_t * offsetp)187 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 	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 	rc = epop->epo_config_readd(espcp, cap_offset +
207 			       (EFX_LOW_BIT(ESF_GZ_PCI_EXPRESS_XCAP_ID) / 8),
208 			       &cap_hdr);
209 	if (rc != 0) {
210 		rc = EIO;
211 		goto fail1;
212 	}
213 
214 	if (EFX_DWORD_FIELD(cap_hdr, ESF_GZ_PCI_EXPRESS_XCAP_VER) !=
215 	    ESE_GZ_PCI_EXPRESS_XCAP_VER_VSEC) {
216 		rc = EINVAL;
217 		goto fail2;
218 	}
219 
220 	rc = epop->epo_config_readd(espcp, vsec_offset +
221 			       (EFX_LOW_BIT(ESF_GZ_VSEC_ID) / 8),
222 			       &vsec.eo_dword[0]);
223 	if (rc != 0) {
224 		rc = EIO;
225 		goto fail3;
226 	}
227 
228 	vsec_len = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_LEN);
229 	vsec_id = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_ID);
230 	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 	if (vsec_id != ESE_GZ_XILINX_VSEC_ID) {
237 		rc = ENOENT;
238 		goto fail4;
239 	}
240 
241 	if (vsec_rev != ESE_GZ_VSEC_VER_XIL_CFGBAR ||
242 	    vsec_len < ESE_GZ_VSEC_LEN_MIN) {
243 		rc = EINVAL;
244 		goto fail5;
245 	}
246 
247 	rc = epop->epo_config_readd(espcp, vsec_offset +
248 			       (EFX_LOW_BIT(ESF_GZ_VSEC_TBL_BAR) / 8),
249 			       &vsec.eo_dword[1]);
250 	if (rc != 0) {
251 		rc = EIO;
252 		goto fail6;
253 	}
254 
255 	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 	if (vsec_len >= ESE_GZ_VSEC_LEN_HIGH_OFFT) {
259 		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 		if (rc != 0) {
263 			rc = EIO;
264 			goto fail7;
265 		}
266 
267 		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 	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 	offset |= (uint64_t)offset_high << ESE_GZ_VSEC_TBL_OFF_HI_BYTES_SHIFT;
279 #else
280 	_NOTE(ARGUNUSED(offset_high))
281 #endif
282 
283 	*offsetp = offset;
284 	*barp = bar;
285 
286 	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
efx_pci_xilinx_cap_tbl_find(__in efsys_bar_t * esbp,__in uint32_t format_id,__in boolean_t skip_first,__inout efsys_dma_addr_t * entry_offsetp)307 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 	if (entry_offsetp == NULL) {
321 		rc = EINVAL;
322 		goto fail1;
323 	}
324 
325 	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 		EFSYS_BAR_READD(esbp, offset +
337 				(EFX_LOW_BIT(ESF_GZ_CFGBAR_ENTRY_FORMAT) / 8),
338 				&header.eq_dword[0], B_FALSE);
339 		EFSYS_BAR_READD(esbp, offset +
340 				(EFX_LOW_BIT(ESF_GZ_CFGBAR_ENTRY_SIZE) / 8),
341 				&header.eq_dword[1], B_FALSE);
342 
343 		format = EFX_QWORD_FIELD32(header, ESF_GZ_CFGBAR_ENTRY_FORMAT);
344 		last = EFX_QWORD_FIELD32(header, ESF_GZ_CFGBAR_ENTRY_LAST);
345 
346 		if (skip == B_FALSE && format == format_id) {
347 			*entry_offsetp = offset;
348 			rc = 0;
349 			break;
350 		}
351 
352 		offset += EFX_QWORD_FIELD32(header, ESF_GZ_CFGBAR_ENTRY_SIZE);
353 		skip = B_FALSE;
354 	} 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 	return (rc);
366 }
367 
368 #endif /* EFSYS_OPT_PCI */
369