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