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