xref: /dpdk/drivers/common/sfc_efx/base/efx_pci.c (revision f5057be340e44f3edc0fe90fa875eb89a4c49b4f)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  *
3  * Copyright(c) 2019-2020 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 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
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
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
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
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 = *entry_offsetp;
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 	rc = ENOENT;
326 	/*
327 	 * SF-119689-TC Riverhead Host Interface section 4.2.2.
328 	 * describes the following discovery steps.
329 	 */
330 	do {
331 		/*
332 		 * Xilinx Capabilities Table requires 32bit aligned reads.
333 		 * See SF-119689-TC section 4.2.2 "Discovery Steps".
334 		 */
335 		EFSYS_BAR_READD(esbp, offset +
336 				(EFX_LOW_BIT(ESF_GZ_CFGBAR_ENTRY_FORMAT) / 8),
337 				&header.eq_dword[0], B_FALSE);
338 		EFSYS_BAR_READD(esbp, offset +
339 				(EFX_LOW_BIT(ESF_GZ_CFGBAR_ENTRY_SIZE) / 8),
340 				&header.eq_dword[1], B_FALSE);
341 
342 		format = EFX_QWORD_FIELD32(header, ESF_GZ_CFGBAR_ENTRY_FORMAT);
343 		last = EFX_QWORD_FIELD32(header, ESF_GZ_CFGBAR_ENTRY_LAST);
344 
345 		if (skip == B_FALSE && format == format_id) {
346 			*entry_offsetp = offset;
347 			rc = 0;
348 			break;
349 		}
350 
351 		offset += EFX_QWORD_FIELD32(header, ESF_GZ_CFGBAR_ENTRY_SIZE);
352 		skip = B_FALSE;
353 	} while (last == B_FALSE);
354 
355 	/*
356 	 * Returns 0 if found otherwise ENOENT indicating that
357 	 * search finished correctly.
358 	 */
359 	return (rc);
360 
361 fail1:
362 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
363 
364 	return (rc);
365 }
366 
367 #endif /* EFSYS_OPT_PCI */
368