efx_nic.c revision 283514
1227569Sphilip/*-
2283514Sarybchik * Copyright (c) 2007-2015 Solarflare Communications Inc.
3283514Sarybchik * All rights reserved.
4227569Sphilip *
5227569Sphilip * Redistribution and use in source and binary forms, with or without
6283514Sarybchik * modification, are permitted provided that the following conditions are met:
7227569Sphilip *
8283514Sarybchik * 1. Redistributions of source code must retain the above copyright notice,
9283514Sarybchik *    this list of conditions and the following disclaimer.
10283514Sarybchik * 2. Redistributions in binary form must reproduce the above copyright notice,
11283514Sarybchik *    this list of conditions and the following disclaimer in the documentation
12283514Sarybchik *    and/or other materials provided with the distribution.
13283514Sarybchik *
14283514Sarybchik * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15283514Sarybchik * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16283514Sarybchik * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17283514Sarybchik * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18283514Sarybchik * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19283514Sarybchik * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20283514Sarybchik * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21283514Sarybchik * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22283514Sarybchik * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23283514Sarybchik * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24283514Sarybchik * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25283514Sarybchik *
26283514Sarybchik * The views and conclusions contained in the software and documentation are
27283514Sarybchik * those of the authors and should not be interpreted as representing official
28283514Sarybchik * policies, either expressed or implied, of the FreeBSD Project.
29227569Sphilip */
30227569Sphilip
31228078Sphilip#include <sys/cdefs.h>
32228078Sphilip__FBSDID("$FreeBSD: head/sys/dev/sfxge/common/efx_nic.c 283514 2015-05-25 08:34:55Z arybchik $");
33228078Sphilip
34227569Sphilip#include "efsys.h"
35227569Sphilip#include "efx.h"
36227569Sphilip#include "efx_types.h"
37227569Sphilip#include "efx_regs.h"
38227569Sphilip#include "efx_impl.h"
39227569Sphilip
40227569Sphilip	__checkReturn	int
41227569Sphilipefx_family(
42227569Sphilip	__in		uint16_t venid,
43227569Sphilip	__in		uint16_t devid,
44227569Sphilip	__out		efx_family_t *efp)
45227569Sphilip{
46283514Sarybchik	if (venid == EFX_PCI_VENID_SFC) {
47283514Sarybchik		switch (devid) {
48227569Sphilip#if EFSYS_OPT_FALCON
49283514Sarybchik		case EFX_PCI_DEVID_FALCON:
50283514Sarybchik			*efp = EFX_FAMILY_FALCON;
51283514Sarybchik			return (0);
52227569Sphilip#endif
53227569Sphilip#if EFSYS_OPT_SIENA
54283514Sarybchik		case EFX_PCI_DEVID_SIENA_F1_UNINIT:
55283514Sarybchik			/*
56283514Sarybchik			 * Hardware default for PF0 of uninitialised Siena.
57283514Sarybchik			 * manftest must be able to cope with this device id.
58283514Sarybchik			 */
59283514Sarybchik			*efp = EFX_FAMILY_SIENA;
60283514Sarybchik			return (0);
61283514Sarybchik
62283514Sarybchik		case EFX_PCI_DEVID_BETHPAGE:
63283514Sarybchik		case EFX_PCI_DEVID_SIENA:
64283514Sarybchik			*efp = EFX_FAMILY_SIENA;
65283514Sarybchik			return (0);
66283514Sarybchik#endif
67283514Sarybchik
68283514Sarybchik#if EFSYS_OPT_HUNTINGTON
69283514Sarybchik		case EFX_PCI_DEVID_HUNTINGTON_PF_UNINIT:
70283514Sarybchik			/*
71283514Sarybchik			 * Hardware default for PF0 of uninitialised Huntington.
72283514Sarybchik			 * manftest must be able to cope with this device id.
73283514Sarybchik			 */
74283514Sarybchik			*efp = EFX_FAMILY_HUNTINGTON;
75283514Sarybchik			return (0);
76283514Sarybchik
77283514Sarybchik		case EFX_PCI_DEVID_FARMINGDALE:
78283514Sarybchik		case EFX_PCI_DEVID_GREENPORT:
79283514Sarybchik		case EFX_PCI_DEVID_HUNTINGTON:
80283514Sarybchik			*efp = EFX_FAMILY_HUNTINGTON;
81283514Sarybchik			return (0);
82283514Sarybchik
83283514Sarybchik		case EFX_PCI_DEVID_FARMINGDALE_VF:
84283514Sarybchik		case EFX_PCI_DEVID_GREENPORT_VF:
85283514Sarybchik		case EFX_PCI_DEVID_HUNTINGTON_VF:
86283514Sarybchik			*efp = EFX_FAMILY_HUNTINGTON;
87283514Sarybchik			return (0);
88283514Sarybchik#endif
89283514Sarybchik		default:
90283514Sarybchik			break;
91283514Sarybchik		}
92227569Sphilip	}
93283514Sarybchik
94283514Sarybchik	*efp = EFX_FAMILY_INVALID;
95227569Sphilip	return (ENOTSUP);
96227569Sphilip}
97227569Sphilip
98227569Sphilip/*
99227569Sphilip * To support clients which aren't provided with any PCI context infer
100227569Sphilip * the hardware family by inspecting the hardware. Obviously the caller
101227569Sphilip * must be damn sure they're really talking to a supported device.
102227569Sphilip */
103227569Sphilip	__checkReturn	int
104227569Sphilipefx_infer_family(
105227569Sphilip	__in		efsys_bar_t *esbp,
106227569Sphilip	__out		efx_family_t *efp)
107227569Sphilip{
108227569Sphilip	efx_family_t family;
109227569Sphilip	efx_oword_t oword;
110227569Sphilip	unsigned int portnum;
111227569Sphilip	int rc;
112227569Sphilip
113227569Sphilip	EFSYS_BAR_READO(esbp, FR_AZ_CS_DEBUG_REG_OFST, &oword, B_TRUE);
114227569Sphilip	portnum = EFX_OWORD_FIELD(oword, FRF_CZ_CS_PORT_NUM);
115227569Sphilip	switch (portnum) {
116283514Sarybchik	case 0: {
117283514Sarybchik		efx_dword_t dword;
118283514Sarybchik		uint32_t hw_rev;
119283514Sarybchik
120283514Sarybchik		EFSYS_BAR_READD(esbp, ER_DZ_BIU_HW_REV_ID_REG_OFST, &dword,
121283514Sarybchik		    B_TRUE);
122283514Sarybchik		hw_rev = EFX_DWORD_FIELD(dword, ERF_DZ_HW_REV_ID);
123283514Sarybchik		if (hw_rev == ER_DZ_BIU_HW_REV_ID_REG_RESET) {
124283514Sarybchik#if EFSYS_OPT_HUNTINGTON
125283514Sarybchik			family = EFX_FAMILY_HUNTINGTON;
126283514Sarybchik			break;
127283514Sarybchik#endif
128283514Sarybchik		} else {
129227569Sphilip#if EFSYS_OPT_FALCON
130283514Sarybchik			family = EFX_FAMILY_FALCON;
131283514Sarybchik			break;
132227569Sphilip#endif
133283514Sarybchik		}
134283514Sarybchik		rc = ENOTSUP;
135283514Sarybchik		goto fail1;
136283514Sarybchik	}
137283514Sarybchik
138227569Sphilip#if EFSYS_OPT_SIENA
139227569Sphilip	case 1:
140227569Sphilip	case 2:
141227569Sphilip		family = EFX_FAMILY_SIENA;
142227569Sphilip		break;
143227569Sphilip#endif
144227569Sphilip	default:
145227569Sphilip		rc = ENOTSUP;
146227569Sphilip		goto fail1;
147227569Sphilip	}
148227569Sphilip
149227569Sphilip	if (efp != NULL)
150227569Sphilip		*efp = family;
151227569Sphilip	return (0);
152227569Sphilip
153227569Sphilipfail1:
154227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
155227569Sphilip
156227569Sphilip	return (rc);
157227569Sphilip}
158227569Sphilip
159227569Sphilip#define	EFX_BIU_MAGIC0	0x01234567
160227569Sphilip#define	EFX_BIU_MAGIC1	0xfedcba98
161227569Sphilip
162283514Sarybchik	__checkReturn	int
163227569Sphilipefx_nic_biu_test(
164227569Sphilip	__in		efx_nic_t *enp)
165227569Sphilip{
166227569Sphilip	efx_oword_t oword;
167227569Sphilip	int rc;
168227569Sphilip
169227569Sphilip	/*
170227569Sphilip	 * Write magic values to scratch registers 0 and 1, then
171227569Sphilip	 * verify that the values were written correctly.  Interleave
172227569Sphilip	 * the accesses to ensure that the BIU is not just reading
173227569Sphilip	 * back the cached value that was last written.
174227569Sphilip	 */
175227569Sphilip	EFX_POPULATE_OWORD_1(oword, FRF_AZ_DRIVER_DW0, EFX_BIU_MAGIC0);
176283514Sarybchik	EFX_BAR_TBL_WRITEO(enp, FR_AZ_DRIVER_REG, 0, &oword, B_TRUE);
177227569Sphilip
178227569Sphilip	EFX_POPULATE_OWORD_1(oword, FRF_AZ_DRIVER_DW0, EFX_BIU_MAGIC1);
179283514Sarybchik	EFX_BAR_TBL_WRITEO(enp, FR_AZ_DRIVER_REG, 1, &oword, B_TRUE);
180227569Sphilip
181283514Sarybchik	EFX_BAR_TBL_READO(enp, FR_AZ_DRIVER_REG, 0, &oword, B_TRUE);
182227569Sphilip	if (EFX_OWORD_FIELD(oword, FRF_AZ_DRIVER_DW0) != EFX_BIU_MAGIC0) {
183227569Sphilip		rc = EIO;
184227569Sphilip		goto fail1;
185227569Sphilip	}
186227569Sphilip
187283514Sarybchik	EFX_BAR_TBL_READO(enp, FR_AZ_DRIVER_REG, 1, &oword, B_TRUE);
188227569Sphilip	if (EFX_OWORD_FIELD(oword, FRF_AZ_DRIVER_DW0) != EFX_BIU_MAGIC1) {
189227569Sphilip		rc = EIO;
190227569Sphilip		goto fail2;
191227569Sphilip	}
192227569Sphilip
193227569Sphilip	/*
194227569Sphilip	 * Perform the same test, with the values swapped.  This
195227569Sphilip	 * ensures that subsequent tests don't start with the correct
196227569Sphilip	 * values already written into the scratch registers.
197227569Sphilip	 */
198227569Sphilip	EFX_POPULATE_OWORD_1(oword, FRF_AZ_DRIVER_DW0, EFX_BIU_MAGIC1);
199283514Sarybchik	EFX_BAR_TBL_WRITEO(enp, FR_AZ_DRIVER_REG, 0, &oword, B_TRUE);
200227569Sphilip
201227569Sphilip	EFX_POPULATE_OWORD_1(oword, FRF_AZ_DRIVER_DW0, EFX_BIU_MAGIC0);
202283514Sarybchik	EFX_BAR_TBL_WRITEO(enp, FR_AZ_DRIVER_REG, 1, &oword, B_TRUE);
203227569Sphilip
204283514Sarybchik	EFX_BAR_TBL_READO(enp, FR_AZ_DRIVER_REG, 0, &oword, B_TRUE);
205227569Sphilip	if (EFX_OWORD_FIELD(oword, FRF_AZ_DRIVER_DW0) != EFX_BIU_MAGIC1) {
206227569Sphilip		rc = EIO;
207227569Sphilip		goto fail3;
208227569Sphilip	}
209227569Sphilip
210283514Sarybchik	EFX_BAR_TBL_READO(enp, FR_AZ_DRIVER_REG, 1, &oword, B_TRUE);
211227569Sphilip	if (EFX_OWORD_FIELD(oword, FRF_AZ_DRIVER_DW0) != EFX_BIU_MAGIC0) {
212227569Sphilip		rc = EIO;
213227569Sphilip		goto fail4;
214227569Sphilip	}
215227569Sphilip
216227569Sphilip	return (0);
217227569Sphilip
218227569Sphilipfail4:
219227569Sphilip	EFSYS_PROBE(fail4);
220227569Sphilipfail3:
221227569Sphilip	EFSYS_PROBE(fail3);
222227569Sphilipfail2:
223227569Sphilip	EFSYS_PROBE(fail2);
224227569Sphilipfail1:
225227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
226227569Sphilip
227227569Sphilip	return (rc);
228227569Sphilip}
229227569Sphilip
230227569Sphilip#if EFSYS_OPT_FALCON
231227569Sphilip
232283514Sarybchikstatic efx_nic_ops_t	__efx_nic_falcon_ops = {
233227569Sphilip	falcon_nic_probe,		/* eno_probe */
234283514Sarybchik	NULL,				/* eno_set_drv_limits */
235227569Sphilip	falcon_nic_reset,		/* eno_reset */
236227569Sphilip	falcon_nic_init,		/* eno_init */
237283514Sarybchik	NULL,				/* eno_get_vi_pool */
238283514Sarybchik	NULL,				/* eno_get_bar_region */
239227569Sphilip#if EFSYS_OPT_DIAG
240227569Sphilip	falcon_sram_test,		/* eno_sram_test */
241227569Sphilip	falcon_nic_register_test,	/* eno_register_test */
242227569Sphilip#endif	/* EFSYS_OPT_DIAG */
243227569Sphilip	falcon_nic_fini,		/* eno_fini */
244227569Sphilip	falcon_nic_unprobe,		/* eno_unprobe */
245227569Sphilip};
246227569Sphilip
247227569Sphilip#endif	/* EFSYS_OPT_FALCON */
248227569Sphilip
249227569Sphilip#if EFSYS_OPT_SIENA
250227569Sphilip
251283514Sarybchikstatic efx_nic_ops_t	__efx_nic_siena_ops = {
252227569Sphilip	siena_nic_probe,		/* eno_probe */
253283514Sarybchik	NULL,				/* eno_set_drv_limits */
254227569Sphilip	siena_nic_reset,		/* eno_reset */
255227569Sphilip	siena_nic_init,			/* eno_init */
256283514Sarybchik	NULL,				/* eno_get_vi_pool */
257283514Sarybchik	NULL,				/* eno_get_bar_region */
258227569Sphilip#if EFSYS_OPT_DIAG
259227569Sphilip	siena_sram_test,		/* eno_sram_test */
260227569Sphilip	siena_nic_register_test,	/* eno_register_test */
261227569Sphilip#endif	/* EFSYS_OPT_DIAG */
262227569Sphilip	siena_nic_fini,			/* eno_fini */
263227569Sphilip	siena_nic_unprobe,		/* eno_unprobe */
264227569Sphilip};
265227569Sphilip
266227569Sphilip#endif	/* EFSYS_OPT_SIENA */
267227569Sphilip
268283514Sarybchik#if EFSYS_OPT_HUNTINGTON
269283514Sarybchik
270283514Sarybchikstatic efx_nic_ops_t	__efx_nic_hunt_ops = {
271283514Sarybchik	hunt_nic_probe,			/* eno_probe */
272283514Sarybchik	hunt_nic_set_drv_limits,	/* eno_set_drv_limits */
273283514Sarybchik	hunt_nic_reset,			/* eno_reset */
274283514Sarybchik	hunt_nic_init,			/* eno_init */
275283514Sarybchik	hunt_nic_get_vi_pool,		/* eno_get_vi_pool */
276283514Sarybchik	hunt_nic_get_bar_region,	/* eno_get_bar_region */
277283514Sarybchik#if EFSYS_OPT_DIAG
278283514Sarybchik	hunt_sram_test,			/* eno_sram_test */
279283514Sarybchik	hunt_nic_register_test,		/* eno_register_test */
280283514Sarybchik#endif	/* EFSYS_OPT_DIAG */
281283514Sarybchik	hunt_nic_fini,			/* eno_fini */
282283514Sarybchik	hunt_nic_unprobe,		/* eno_unprobe */
283283514Sarybchik};
284283514Sarybchik
285283514Sarybchik#endif	/* EFSYS_OPT_HUNTINGTON */
286283514Sarybchik
287227569Sphilip	__checkReturn	int
288227569Sphilipefx_nic_create(
289227569Sphilip	__in		efx_family_t family,
290227569Sphilip	__in		efsys_identifier_t *esip,
291227569Sphilip	__in		efsys_bar_t *esbp,
292227569Sphilip	__in		efsys_lock_t *eslp,
293227569Sphilip	__deref_out	efx_nic_t **enpp)
294227569Sphilip{
295227569Sphilip	efx_nic_t *enp;
296227569Sphilip	int rc;
297227569Sphilip
298227569Sphilip	EFSYS_ASSERT3U(family, >, EFX_FAMILY_INVALID);
299227569Sphilip	EFSYS_ASSERT3U(family, <, EFX_FAMILY_NTYPES);
300227569Sphilip
301227569Sphilip	/* Allocate a NIC object */
302227569Sphilip	EFSYS_KMEM_ALLOC(esip, sizeof (efx_nic_t), enp);
303227569Sphilip
304227569Sphilip	if (enp == NULL) {
305227569Sphilip		rc = ENOMEM;
306227569Sphilip		goto fail1;
307227569Sphilip	}
308227569Sphilip
309227569Sphilip	enp->en_magic = EFX_NIC_MAGIC;
310227569Sphilip
311227569Sphilip	switch (family) {
312227569Sphilip#if EFSYS_OPT_FALCON
313227569Sphilip	case EFX_FAMILY_FALCON:
314227569Sphilip		enp->en_enop = (efx_nic_ops_t *)&__efx_nic_falcon_ops;
315227569Sphilip		enp->en_features = 0;
316227569Sphilip		break;
317227569Sphilip#endif	/* EFSYS_OPT_FALCON */
318227569Sphilip
319227569Sphilip#if EFSYS_OPT_SIENA
320227569Sphilip	case EFX_FAMILY_SIENA:
321227569Sphilip		enp->en_enop = (efx_nic_ops_t *)&__efx_nic_siena_ops;
322279141Sarybchik		enp->en_features =
323279141Sarybchik		    EFX_FEATURE_IPV6 |
324227569Sphilip		    EFX_FEATURE_LFSR_HASH_INSERT |
325279141Sarybchik		    EFX_FEATURE_LINK_EVENTS |
326279141Sarybchik		    EFX_FEATURE_PERIODIC_MAC_STATS |
327279141Sarybchik		    EFX_FEATURE_WOL |
328279141Sarybchik		    EFX_FEATURE_MCDI |
329278839Sarybchik		    EFX_FEATURE_LOOKAHEAD_SPLIT |
330283514Sarybchik		    EFX_FEATURE_MAC_HEADER_FILTERS |
331283514Sarybchik		    EFX_FEATURE_TX_SRC_FILTERS;
332227569Sphilip		break;
333227569Sphilip#endif	/* EFSYS_OPT_SIENA */
334227569Sphilip
335283514Sarybchik#if EFSYS_OPT_HUNTINGTON
336283514Sarybchik	case EFX_FAMILY_HUNTINGTON:
337283514Sarybchik		enp->en_enop = (efx_nic_ops_t *)&__efx_nic_hunt_ops;
338283514Sarybchik		/* FIXME: Add WOL support */
339283514Sarybchik		enp->en_features =
340283514Sarybchik		    EFX_FEATURE_IPV6 |
341283514Sarybchik		    EFX_FEATURE_LINK_EVENTS |
342283514Sarybchik		    EFX_FEATURE_PERIODIC_MAC_STATS |
343283514Sarybchik		    EFX_FEATURE_MCDI |
344283514Sarybchik		    EFX_FEATURE_MAC_HEADER_FILTERS |
345283514Sarybchik		    EFX_FEATURE_MCDI_DMA |
346283514Sarybchik		    EFX_FEATURE_PIO_BUFFERS |
347283514Sarybchik		    EFX_FEATURE_FW_ASSISTED_TSO;
348283514Sarybchik		break;
349283514Sarybchik#endif	/* EFSYS_OPT_HUNTINGTON */
350283514Sarybchik
351227569Sphilip	default:
352227569Sphilip		rc = ENOTSUP;
353227569Sphilip		goto fail2;
354227569Sphilip	}
355227569Sphilip
356227569Sphilip	enp->en_family = family;
357227569Sphilip	enp->en_esip = esip;
358227569Sphilip	enp->en_esbp = esbp;
359227569Sphilip	enp->en_eslp = eslp;
360227569Sphilip
361227569Sphilip	*enpp = enp;
362227569Sphilip
363227569Sphilip	return (0);
364227569Sphilip
365227569Sphilipfail2:
366283514Sarybchik	EFSYS_PROBE(fail2);
367227569Sphilip
368227569Sphilip	enp->en_magic = 0;
369227569Sphilip
370227569Sphilip	/* Free the NIC object */
371227569Sphilip	EFSYS_KMEM_FREE(esip, sizeof (efx_nic_t), enp);
372227569Sphilip
373227569Sphilipfail1:
374227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
375227569Sphilip
376227569Sphilip	return (rc);
377227569Sphilip}
378227569Sphilip
379227569Sphilip	__checkReturn	int
380227569Sphilipefx_nic_probe(
381227569Sphilip	__in		efx_nic_t *enp)
382227569Sphilip{
383227569Sphilip	efx_nic_ops_t *enop;
384227569Sphilip	int rc;
385227569Sphilip
386227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
387227569Sphilip#if EFSYS_OPT_MCDI
388227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
389227569Sphilip#endif	/* EFSYS_OPT_MCDI */
390227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_PROBE));
391227569Sphilip
392227569Sphilip	enop = enp->en_enop;
393227569Sphilip	if ((rc = enop->eno_probe(enp)) != 0)
394283514Sarybchik		goto fail1;
395227569Sphilip
396227569Sphilip	if ((rc = efx_phy_probe(enp)) != 0)
397283514Sarybchik		goto fail2;
398227569Sphilip
399227569Sphilip	enp->en_mod_flags |= EFX_MOD_PROBE;
400227569Sphilip
401227569Sphilip	return (0);
402227569Sphilip
403283514Sarybchikfail2:
404283514Sarybchik	EFSYS_PROBE(fail2);
405227569Sphilip
406227569Sphilip	enop->eno_unprobe(enp);
407227569Sphilip
408227569Sphilipfail1:
409227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
410227569Sphilip
411227569Sphilip	return (rc);
412227569Sphilip}
413227569Sphilip
414227569Sphilip#if EFSYS_OPT_PCIE_TUNE
415227569Sphilip
416227569Sphilip	__checkReturn	int
417227569Sphilipefx_nic_pcie_tune(
418227569Sphilip	__in		efx_nic_t *enp,
419227569Sphilip	unsigned int	nlanes)
420227569Sphilip{
421227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
422227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
423227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_NIC));
424227569Sphilip
425227569Sphilip#if EFSYS_OPT_FALCON
426227569Sphilip	if (enp->en_family == EFX_FAMILY_FALCON)
427227569Sphilip		return (falcon_nic_pcie_tune(enp, nlanes));
428227569Sphilip#endif
429227569Sphilip	return (ENOTSUP);
430227569Sphilip}
431227569Sphilip
432227569Sphilip	__checkReturn	int
433227569Sphilipefx_nic_pcie_extended_sync(
434227569Sphilip	__in		efx_nic_t *enp)
435227569Sphilip{
436227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
437227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
438227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_NIC));
439227569Sphilip
440227569Sphilip#if EFSYS_OPT_SIENA
441227569Sphilip	if (enp->en_family == EFX_FAMILY_SIENA)
442227569Sphilip		return (siena_nic_pcie_extended_sync(enp));
443227569Sphilip#endif
444227569Sphilip
445227569Sphilip	return (ENOTSUP);
446227569Sphilip}
447227569Sphilip
448227569Sphilip#endif	/* EFSYS_OPT_PCIE_TUNE */
449227569Sphilip
450227569Sphilip	__checkReturn	int
451283514Sarybchikefx_nic_set_drv_limits(
452283514Sarybchik	__inout		efx_nic_t *enp,
453283514Sarybchik	__in		efx_drv_limits_t *edlp)
454283514Sarybchik{
455283514Sarybchik	efx_nic_ops_t *enop = enp->en_enop;
456283514Sarybchik	int rc;
457283514Sarybchik
458283514Sarybchik	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
459283514Sarybchik	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
460283514Sarybchik
461283514Sarybchik	if (enop->eno_set_drv_limits != NULL) {
462283514Sarybchik		if ((rc = enop->eno_set_drv_limits(enp, edlp)) != 0)
463283514Sarybchik			goto fail1;
464283514Sarybchik	}
465283514Sarybchik
466283514Sarybchik	return (0);
467283514Sarybchik
468283514Sarybchikfail1:
469283514Sarybchik	EFSYS_PROBE1(fail1, int, rc);
470283514Sarybchik
471283514Sarybchik	return (rc);
472283514Sarybchik}
473283514Sarybchik
474283514Sarybchik	__checkReturn	int
475283514Sarybchikefx_nic_get_bar_region(
476283514Sarybchik	__in		efx_nic_t *enp,
477283514Sarybchik	__in		efx_nic_region_t region,
478283514Sarybchik	__out		uint32_t *offsetp,
479283514Sarybchik	__out		size_t *sizep)
480283514Sarybchik{
481283514Sarybchik	efx_nic_ops_t *enop = enp->en_enop;
482283514Sarybchik	int rc;
483283514Sarybchik
484283514Sarybchik	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
485283514Sarybchik	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
486283514Sarybchik	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NIC);
487283514Sarybchik
488283514Sarybchik	if (enop->eno_get_bar_region == NULL) {
489283514Sarybchik		rc = ENOTSUP;
490283514Sarybchik		goto fail1;
491283514Sarybchik	}
492283514Sarybchik	if ((rc = (enop->eno_get_bar_region)(enp,
493283514Sarybchik		    region, offsetp, sizep)) != 0) {
494283514Sarybchik		goto fail2;
495283514Sarybchik	}
496283514Sarybchik
497283514Sarybchik	return (0);
498283514Sarybchik
499283514Sarybchikfail2:
500283514Sarybchik	EFSYS_PROBE(fail2);
501283514Sarybchik
502283514Sarybchikfail1:
503283514Sarybchik	EFSYS_PROBE1(fail1, int, rc);
504283514Sarybchik
505283514Sarybchik	return (rc);
506283514Sarybchik}
507283514Sarybchik
508283514Sarybchik
509283514Sarybchik	__checkReturn	int
510283514Sarybchikefx_nic_get_vi_pool(
511283514Sarybchik	__in		efx_nic_t *enp,
512283514Sarybchik	__out		uint32_t *evq_countp,
513283514Sarybchik	__out		uint32_t *rxq_countp,
514283514Sarybchik	__out		uint32_t *txq_countp)
515283514Sarybchik{
516283514Sarybchik	efx_nic_ops_t *enop = enp->en_enop;
517283514Sarybchik	efx_nic_cfg_t *encp = &enp->en_nic_cfg;
518283514Sarybchik	int rc;
519283514Sarybchik
520283514Sarybchik	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
521283514Sarybchik	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
522283514Sarybchik	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NIC);
523283514Sarybchik
524283514Sarybchik	if (enop->eno_get_vi_pool != NULL) {
525283514Sarybchik		uint32_t vi_count = 0;
526283514Sarybchik
527283514Sarybchik		if ((rc = (enop->eno_get_vi_pool)(enp, &vi_count)) != 0)
528283514Sarybchik			goto fail1;
529283514Sarybchik
530283514Sarybchik		*evq_countp = vi_count;
531283514Sarybchik		*rxq_countp = vi_count;
532283514Sarybchik		*txq_countp = vi_count;
533283514Sarybchik	} else {
534283514Sarybchik		/* Use NIC limits as default value */
535283514Sarybchik		*evq_countp = encp->enc_evq_limit;
536283514Sarybchik		*rxq_countp = encp->enc_rxq_limit;
537283514Sarybchik		*txq_countp = encp->enc_txq_limit;
538283514Sarybchik	}
539283514Sarybchik
540283514Sarybchik	return (0);
541283514Sarybchik
542283514Sarybchikfail1:
543283514Sarybchik	EFSYS_PROBE1(fail1, int, rc);
544283514Sarybchik
545283514Sarybchik	return (rc);
546283514Sarybchik}
547283514Sarybchik
548283514Sarybchik
549283514Sarybchik	__checkReturn	int
550227569Sphilipefx_nic_init(
551227569Sphilip	__in		efx_nic_t *enp)
552227569Sphilip{
553227569Sphilip	efx_nic_ops_t *enop = enp->en_enop;
554227569Sphilip	int rc;
555227569Sphilip
556227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
557227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
558227569Sphilip
559227569Sphilip	if (enp->en_mod_flags & EFX_MOD_NIC) {
560227569Sphilip		rc = EINVAL;
561227569Sphilip		goto fail1;
562227569Sphilip	}
563227569Sphilip
564227569Sphilip	if ((rc = enop->eno_init(enp)) != 0)
565227569Sphilip		goto fail2;
566227569Sphilip
567227569Sphilip	enp->en_mod_flags |= EFX_MOD_NIC;
568227569Sphilip
569227569Sphilip	return (0);
570227569Sphilip
571227569Sphilipfail2:
572227569Sphilip	EFSYS_PROBE(fail2);
573227569Sphilipfail1:
574227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
575227569Sphilip
576227569Sphilip	return (rc);
577227569Sphilip}
578227569Sphilip
579227569Sphilip			void
580227569Sphilipefx_nic_fini(
581227569Sphilip	__in		efx_nic_t *enp)
582227569Sphilip{
583227569Sphilip	efx_nic_ops_t *enop = enp->en_enop;
584227569Sphilip
585227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
586227569Sphilip	EFSYS_ASSERT(enp->en_mod_flags & EFX_MOD_PROBE);
587227569Sphilip	EFSYS_ASSERT(enp->en_mod_flags & EFX_MOD_NIC);
588227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_INTR));
589227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_EV));
590227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_RX));
591227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_TX));
592227569Sphilip
593227569Sphilip	enop->eno_fini(enp);
594227569Sphilip
595227569Sphilip	enp->en_mod_flags &= ~EFX_MOD_NIC;
596227569Sphilip}
597227569Sphilip
598227569Sphilip			void
599227569Sphilipefx_nic_unprobe(
600227569Sphilip	__in		efx_nic_t *enp)
601227569Sphilip{
602227569Sphilip	efx_nic_ops_t *enop = enp->en_enop;
603227569Sphilip
604227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
605227569Sphilip#if EFSYS_OPT_MCDI
606227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
607227569Sphilip#endif	/* EFSYS_OPT_MCDI */
608227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
609227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_NIC));
610227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_INTR));
611227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_EV));
612227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_RX));
613227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_TX));
614227569Sphilip
615227569Sphilip	efx_phy_unprobe(enp);
616227569Sphilip
617227569Sphilip	enop->eno_unprobe(enp);
618227569Sphilip
619227569Sphilip	enp->en_mod_flags &= ~EFX_MOD_PROBE;
620227569Sphilip}
621227569Sphilip
622227569Sphilip			void
623227569Sphilipefx_nic_destroy(
624227569Sphilip	__in	efx_nic_t *enp)
625227569Sphilip{
626227569Sphilip	efsys_identifier_t *esip = enp->en_esip;
627227569Sphilip
628227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
629227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, ==, 0);
630227569Sphilip
631227569Sphilip	enp->en_family = 0;
632227569Sphilip	enp->en_esip = NULL;
633227569Sphilip	enp->en_esbp = NULL;
634227569Sphilip	enp->en_eslp = NULL;
635227569Sphilip
636227569Sphilip	enp->en_enop = NULL;
637227569Sphilip
638227569Sphilip	enp->en_magic = 0;
639227569Sphilip
640227569Sphilip	/* Free the NIC object */
641227569Sphilip	EFSYS_KMEM_FREE(esip, sizeof (efx_nic_t), enp);
642227569Sphilip}
643227569Sphilip
644227569Sphilip	__checkReturn	int
645227569Sphilipefx_nic_reset(
646227569Sphilip	__in		efx_nic_t *enp)
647227569Sphilip{
648227569Sphilip	efx_nic_ops_t *enop = enp->en_enop;
649227569Sphilip	unsigned int mod_flags;
650227569Sphilip	int rc;
651227569Sphilip
652227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
653227569Sphilip	EFSYS_ASSERT(enp->en_mod_flags & EFX_MOD_PROBE);
654227569Sphilip	/*
655227569Sphilip	 * All modules except the MCDI, PROBE, NVRAM, VPD, MON (which we
656227569Sphilip	 * do not reset here) must have been shut down or never initialized.
657227569Sphilip	 *
658227569Sphilip	 * A rule of thumb here is: If the controller or MC reboots, is *any*
659227569Sphilip	 * state lost. If it's lost and needs reapplying, then the module
660227569Sphilip	 * *must* not be initialised during the reset.
661227569Sphilip	 */
662227569Sphilip	mod_flags = enp->en_mod_flags;
663227569Sphilip	mod_flags &= ~(EFX_MOD_MCDI | EFX_MOD_PROBE | EFX_MOD_NVRAM |
664227569Sphilip		    EFX_MOD_VPD | EFX_MOD_MON);
665227569Sphilip	EFSYS_ASSERT3U(mod_flags, ==, 0);
666227569Sphilip	if (mod_flags != 0) {
667227569Sphilip		rc = EINVAL;
668227569Sphilip		goto fail1;
669227569Sphilip	}
670227569Sphilip
671227569Sphilip	if ((rc = enop->eno_reset(enp)) != 0)
672227569Sphilip		goto fail2;
673227569Sphilip
674227569Sphilip	enp->en_reset_flags |= EFX_RESET_MAC;
675227569Sphilip
676227569Sphilip	return (0);
677227569Sphilip
678227569Sphilipfail2:
679227569Sphilip	EFSYS_PROBE(fail2);
680227569Sphilipfail1:
681227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
682227569Sphilip
683227569Sphilip	return (rc);
684227569Sphilip}
685227569Sphilip
686227569Sphilip			const efx_nic_cfg_t *
687227569Sphilipefx_nic_cfg_get(
688227569Sphilip	__in		efx_nic_t *enp)
689227569Sphilip{
690227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
691227569Sphilip
692227569Sphilip	return (&(enp->en_nic_cfg));
693227569Sphilip}
694227569Sphilip
695227569Sphilip#if EFSYS_OPT_DIAG
696227569Sphilip
697227569Sphilip	__checkReturn	int
698227569Sphilipefx_nic_register_test(
699227569Sphilip	__in		efx_nic_t *enp)
700227569Sphilip{
701227569Sphilip	efx_nic_ops_t *enop = enp->en_enop;
702227569Sphilip	int rc;
703227569Sphilip
704227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
705227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
706227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_NIC));
707227569Sphilip
708227569Sphilip	if ((rc = enop->eno_register_test(enp)) != 0)
709227569Sphilip		goto fail1;
710227569Sphilip
711227569Sphilip	return (0);
712227569Sphilip
713227569Sphilipfail1:
714227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
715227569Sphilip
716227569Sphilip	return (rc);
717227569Sphilip}
718227569Sphilip
719227569Sphilip	__checkReturn	int
720227569Sphilipefx_nic_test_registers(
721227569Sphilip	__in		efx_nic_t *enp,
722227569Sphilip	__in		efx_register_set_t *rsp,
723227569Sphilip	__in		size_t count)
724227569Sphilip{
725227569Sphilip	unsigned int bit;
726227569Sphilip	efx_oword_t original;
727227569Sphilip	efx_oword_t reg;
728227569Sphilip	efx_oword_t buf;
729227569Sphilip	int rc;
730227569Sphilip
731227569Sphilip	while (count > 0) {
732227569Sphilip		/* This function is only suitable for registers */
733227569Sphilip		EFSYS_ASSERT(rsp->rows == 1);
734227569Sphilip
735227569Sphilip		/* bit sweep on and off */
736227569Sphilip		EFSYS_BAR_READO(enp->en_esbp, rsp->address, &original,
737227569Sphilip			    B_TRUE);
738227569Sphilip		for (bit = 0; bit < 128; bit++) {
739227569Sphilip			/* Is this bit in the mask? */
740227569Sphilip			if (~(rsp->mask.eo_u32[bit >> 5]) & (1 << bit))
741227569Sphilip				continue;
742227569Sphilip
743227569Sphilip			/* Test this bit can be set in isolation */
744227569Sphilip			reg = original;
745227569Sphilip			EFX_AND_OWORD(reg, rsp->mask);
746227569Sphilip			EFX_SET_OWORD_BIT(reg, bit);
747227569Sphilip
748227569Sphilip			EFSYS_BAR_WRITEO(enp->en_esbp, rsp->address, &reg,
749227569Sphilip				    B_TRUE);
750227569Sphilip			EFSYS_BAR_READO(enp->en_esbp, rsp->address, &buf,
751227569Sphilip				    B_TRUE);
752227569Sphilip
753227569Sphilip			EFX_AND_OWORD(buf, rsp->mask);
754227569Sphilip			if (memcmp(&reg, &buf, sizeof (reg))) {
755227569Sphilip				rc = EIO;
756227569Sphilip				goto fail1;
757227569Sphilip			}
758227569Sphilip
759227569Sphilip			/* Test this bit can be cleared in isolation */
760227569Sphilip			EFX_OR_OWORD(reg, rsp->mask);
761227569Sphilip			EFX_CLEAR_OWORD_BIT(reg, bit);
762227569Sphilip
763227569Sphilip			EFSYS_BAR_WRITEO(enp->en_esbp, rsp->address, &reg,
764227569Sphilip				    B_TRUE);
765227569Sphilip			EFSYS_BAR_READO(enp->en_esbp, rsp->address, &buf,
766227569Sphilip				    B_TRUE);
767227569Sphilip
768227569Sphilip			EFX_AND_OWORD(buf, rsp->mask);
769227569Sphilip			if (memcmp(&reg, &buf, sizeof (reg))) {
770227569Sphilip				rc = EIO;
771227569Sphilip				goto fail2;
772227569Sphilip			}
773227569Sphilip		}
774227569Sphilip
775227569Sphilip		/* Restore the old value */
776227569Sphilip		EFSYS_BAR_WRITEO(enp->en_esbp, rsp->address, &original,
777227569Sphilip			    B_TRUE);
778227569Sphilip
779227569Sphilip		--count;
780227569Sphilip		++rsp;
781227569Sphilip	}
782227569Sphilip
783227569Sphilip	return (0);
784227569Sphilip
785227569Sphilipfail2:
786227569Sphilip	EFSYS_PROBE(fail2);
787227569Sphilipfail1:
788227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
789227569Sphilip
790227569Sphilip	/* Restore the old value */
791227569Sphilip	EFSYS_BAR_WRITEO(enp->en_esbp, rsp->address, &original, B_TRUE);
792227569Sphilip
793227569Sphilip	return (rc);
794227569Sphilip}
795227569Sphilip
796227569Sphilip	__checkReturn	int
797227569Sphilipefx_nic_test_tables(
798227569Sphilip	__in		efx_nic_t *enp,
799227569Sphilip	__in		efx_register_set_t *rsp,
800227569Sphilip	__in		efx_pattern_type_t pattern,
801227569Sphilip	__in		size_t count)
802227569Sphilip{
803227569Sphilip	efx_sram_pattern_fn_t func;
804227569Sphilip	unsigned int index;
805227569Sphilip	unsigned int address;
806227569Sphilip	efx_oword_t reg;
807227569Sphilip	efx_oword_t buf;
808227569Sphilip	int rc;
809227569Sphilip
810227569Sphilip	EFSYS_ASSERT(pattern < EFX_PATTERN_NTYPES);
811227569Sphilip	func = __efx_sram_pattern_fns[pattern];
812227569Sphilip
813227569Sphilip	while (count > 0) {
814227569Sphilip		/* Write */
815227569Sphilip		address = rsp->address;
816227569Sphilip		for (index = 0; index < rsp->rows; ++index) {
817227569Sphilip			func(2 * index + 0, B_FALSE, &reg.eo_qword[0]);
818227569Sphilip			func(2 * index + 1, B_FALSE, &reg.eo_qword[1]);
819227569Sphilip			EFX_AND_OWORD(reg, rsp->mask);
820227569Sphilip			EFSYS_BAR_WRITEO(enp->en_esbp, address, &reg, B_TRUE);
821227569Sphilip
822227569Sphilip			address += rsp->step;
823227569Sphilip		}
824227569Sphilip
825227569Sphilip		/* Read */
826227569Sphilip		address = rsp->address;
827227569Sphilip		for (index = 0; index < rsp->rows; ++index) {
828227569Sphilip			func(2 * index + 0, B_FALSE, &reg.eo_qword[0]);
829227569Sphilip			func(2 * index + 1, B_FALSE, &reg.eo_qword[1]);
830227569Sphilip			EFX_AND_OWORD(reg, rsp->mask);
831227569Sphilip			EFSYS_BAR_READO(enp->en_esbp, address, &buf, B_TRUE);
832227569Sphilip			if (memcmp(&reg, &buf, sizeof (reg))) {
833227569Sphilip				rc = EIO;
834227569Sphilip				goto fail1;
835227569Sphilip			}
836227569Sphilip
837227569Sphilip			address += rsp->step;
838227569Sphilip		}
839227569Sphilip
840227569Sphilip		++rsp;
841227569Sphilip		--count;
842227569Sphilip	}
843227569Sphilip
844227569Sphilip	return (0);
845227569Sphilip
846227569Sphilipfail1:
847227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
848227569Sphilip
849227569Sphilip	return (rc);
850227569Sphilip}
851227569Sphilip
852227569Sphilip#endif	/* EFSYS_OPT_DIAG */
853283514Sarybchik
854283514Sarybchik#if EFSYS_OPT_LOOPBACK
855283514Sarybchik
856283514Sarybchikextern			void
857283514Sarybchikefx_loopback_mask(
858283514Sarybchik	__in	efx_loopback_kind_t loopback_kind,
859283514Sarybchik	__out	efx_qword_t *maskp)
860283514Sarybchik{
861283514Sarybchik	efx_qword_t mask;
862283514Sarybchik
863283514Sarybchik	EFSYS_ASSERT3U(loopback_kind, <, EFX_LOOPBACK_NKINDS);
864283514Sarybchik	EFSYS_ASSERT(maskp != NULL);
865283514Sarybchik
866283514Sarybchik	/* Assert the MC_CMD_LOOPBACK and EFX_LOOPBACK namespace agree */
867283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_NONE == EFX_LOOPBACK_OFF);
868283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_DATA == EFX_LOOPBACK_DATA);
869283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMAC == EFX_LOOPBACK_GMAC);
870283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGMII == EFX_LOOPBACK_XGMII);
871283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGXS == EFX_LOOPBACK_XGXS);
872283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI == EFX_LOOPBACK_XAUI);
873283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMII == EFX_LOOPBACK_GMII);
874283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SGMII == EFX_LOOPBACK_SGMII);
875283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGBR == EFX_LOOPBACK_XGBR);
876283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XFI == EFX_LOOPBACK_XFI);
877283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI_FAR == EFX_LOOPBACK_XAUI_FAR);
878283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMII_FAR == EFX_LOOPBACK_GMII_FAR);
879283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SGMII_FAR == EFX_LOOPBACK_SGMII_FAR);
880283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XFI_FAR == EFX_LOOPBACK_XFI_FAR);
881283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GPHY == EFX_LOOPBACK_GPHY);
882283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PHYXS == EFX_LOOPBACK_PHY_XS);
883283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PCS == EFX_LOOPBACK_PCS);
884283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PMAPMD == EFX_LOOPBACK_PMA_PMD);
885283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XPORT == EFX_LOOPBACK_XPORT);
886283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGMII_WS == EFX_LOOPBACK_XGMII_WS);
887283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI_WS == EFX_LOOPBACK_XAUI_WS);
888283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI_WS_FAR ==
889283514Sarybchik	    EFX_LOOPBACK_XAUI_WS_FAR);
890283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI_WS_NEAR ==
891283514Sarybchik	    EFX_LOOPBACK_XAUI_WS_NEAR);
892283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMII_WS == EFX_LOOPBACK_GMII_WS);
893283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XFI_WS == EFX_LOOPBACK_XFI_WS);
894283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XFI_WS_FAR ==
895283514Sarybchik	    EFX_LOOPBACK_XFI_WS_FAR);
896283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PHYXS_WS == EFX_LOOPBACK_PHYXS_WS);
897283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PMA_INT == EFX_LOOPBACK_PMA_INT);
898283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SD_NEAR == EFX_LOOPBACK_SD_NEAR);
899283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SD_FAR == EFX_LOOPBACK_SD_FAR);
900283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PMA_INT_WS ==
901283514Sarybchik	    EFX_LOOPBACK_PMA_INT_WS);
902283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SD_FEP2_WS ==
903283514Sarybchik	    EFX_LOOPBACK_SD_FEP2_WS);
904283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SD_FEP1_5_WS ==
905283514Sarybchik	    EFX_LOOPBACK_SD_FEP1_5_WS);
906283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SD_FEP_WS == EFX_LOOPBACK_SD_FEP_WS);
907283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SD_FES_WS == EFX_LOOPBACK_SD_FES_WS);
908283514Sarybchik
909283514Sarybchik	/* Build bitmask of possible loopback types */
910283514Sarybchik	EFX_ZERO_QWORD(mask);
911283514Sarybchik
912283514Sarybchik	if ((loopback_kind == EFX_LOOPBACK_KIND_OFF) ||
913283514Sarybchik	    (loopback_kind == EFX_LOOPBACK_KIND_ALL)) {
914283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_OFF);
915283514Sarybchik	}
916283514Sarybchik
917283514Sarybchik	if ((loopback_kind == EFX_LOOPBACK_KIND_MAC) ||
918283514Sarybchik	    (loopback_kind == EFX_LOOPBACK_KIND_ALL)) {
919283514Sarybchik		/*
920283514Sarybchik		 * The "MAC" grouping has historically been used by drivers to
921283514Sarybchik		 * mean loopbacks supported by on-chip hardware. Keep that
922283514Sarybchik		 * meaning here, and include on-chip PHY layer loopbacks.
923283514Sarybchik		 */
924283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_DATA);
925283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_GMAC);
926283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XGMII);
927283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XGXS);
928283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XAUI);
929283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_GMII);
930283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_SGMII);
931283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XGBR);
932283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XFI);
933283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XAUI_FAR);
934283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_GMII_FAR);
935283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_SGMII_FAR);
936283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XFI_FAR);
937283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_PMA_INT);
938283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_SD_NEAR);
939283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_SD_FAR);
940283514Sarybchik	}
941283514Sarybchik
942283514Sarybchik	if ((loopback_kind == EFX_LOOPBACK_KIND_PHY) ||
943283514Sarybchik	    (loopback_kind == EFX_LOOPBACK_KIND_ALL)) {
944283514Sarybchik		/*
945283514Sarybchik		 * The "PHY" grouping has historically been used by drivers to
946283514Sarybchik		 * mean loopbacks supported by off-chip hardware. Keep that
947283514Sarybchik		 * meaning here.
948283514Sarybchik		 */
949283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_GPHY);
950283514Sarybchik		EFX_SET_QWORD_BIT(mask,	EFX_LOOPBACK_PHY_XS);
951283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_PCS);
952283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_PMA_PMD);
953283514Sarybchik	}
954283514Sarybchik
955283514Sarybchik	*maskp = mask;
956283514Sarybchik}
957283514Sarybchik
958283514Sarybchik__checkReturn	int
959283514Sarybchikefx_mcdi_get_loopback_modes(
960283514Sarybchik	__in		efx_nic_t *enp)
961283514Sarybchik{
962283514Sarybchik	efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
963283514Sarybchik	efx_mcdi_req_t req;
964283514Sarybchik	uint8_t payload[MAX(MC_CMD_GET_LOOPBACK_MODES_IN_LEN,
965283514Sarybchik			    MC_CMD_GET_LOOPBACK_MODES_OUT_LEN)];
966283514Sarybchik	efx_qword_t mask;
967283514Sarybchik	efx_qword_t modes;
968283514Sarybchik	int rc;
969283514Sarybchik
970283514Sarybchik	(void) memset(payload, 0, sizeof (payload));
971283514Sarybchik	req.emr_cmd = MC_CMD_GET_LOOPBACK_MODES;
972283514Sarybchik	req.emr_in_buf = payload;
973283514Sarybchik	req.emr_in_length = MC_CMD_GET_LOOPBACK_MODES_IN_LEN;
974283514Sarybchik	req.emr_out_buf = payload;
975283514Sarybchik	req.emr_out_length = MC_CMD_GET_LOOPBACK_MODES_OUT_LEN;
976283514Sarybchik
977283514Sarybchik	efx_mcdi_execute(enp, &req);
978283514Sarybchik
979283514Sarybchik	if (req.emr_rc != 0) {
980283514Sarybchik		rc = req.emr_rc;
981283514Sarybchik		goto fail1;
982283514Sarybchik	}
983283514Sarybchik
984283514Sarybchik	if (req.emr_out_length_used <
985283514Sarybchik	    MC_CMD_GET_LOOPBACK_MODES_OUT_SUGGESTED_OFST +
986283514Sarybchik	    MC_CMD_GET_LOOPBACK_MODES_OUT_SUGGESTED_LEN) {
987283514Sarybchik		rc = EMSGSIZE;
988283514Sarybchik		goto fail2;
989283514Sarybchik	}
990283514Sarybchik
991283514Sarybchik	/*
992283514Sarybchik	 * We assert the MC_CMD_LOOPBACK and EFX_LOOPBACK namespaces agree
993283514Sarybchik	 * in efx_loopback_mask() and in siena_phy.c:siena_phy_get_link().
994283514Sarybchik	 */
995283514Sarybchik	efx_loopback_mask(EFX_LOOPBACK_KIND_ALL, &mask);
996283514Sarybchik
997283514Sarybchik	EFX_AND_QWORD(mask,
998283514Sarybchik	    *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_SUGGESTED));
999283514Sarybchik
1000283514Sarybchik	modes = *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_100M);
1001283514Sarybchik	EFX_AND_QWORD(modes, mask);
1002283514Sarybchik	encp->enc_loopback_types[EFX_LINK_100FDX] = modes;
1003283514Sarybchik
1004283514Sarybchik	modes = *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_1G);
1005283514Sarybchik	EFX_AND_QWORD(modes, mask);
1006283514Sarybchik	encp->enc_loopback_types[EFX_LINK_1000FDX] = modes;
1007283514Sarybchik
1008283514Sarybchik	modes = *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_10G);
1009283514Sarybchik	EFX_AND_QWORD(modes, mask);
1010283514Sarybchik	encp->enc_loopback_types[EFX_LINK_10000FDX] = modes;
1011283514Sarybchik
1012283514Sarybchik	if (req.emr_out_length_used >=
1013283514Sarybchik	    MC_CMD_GET_LOOPBACK_MODES_OUT_40G_OFST +
1014283514Sarybchik	    MC_CMD_GET_LOOPBACK_MODES_OUT_40G_LEN) {
1015283514Sarybchik		/* Response includes 40G loopback modes */
1016283514Sarybchik		modes =
1017283514Sarybchik		    *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_40G);
1018283514Sarybchik		EFX_AND_QWORD(modes, mask);
1019283514Sarybchik		encp->enc_loopback_types[EFX_LINK_40000FDX] = modes;
1020283514Sarybchik	}
1021283514Sarybchik
1022283514Sarybchik	EFX_ZERO_QWORD(modes);
1023283514Sarybchik	EFX_SET_QWORD_BIT(modes, EFX_LOOPBACK_OFF);
1024283514Sarybchik	EFX_OR_QWORD(modes, encp->enc_loopback_types[EFX_LINK_100FDX]);
1025283514Sarybchik	EFX_OR_QWORD(modes, encp->enc_loopback_types[EFX_LINK_1000FDX]);
1026283514Sarybchik	EFX_OR_QWORD(modes, encp->enc_loopback_types[EFX_LINK_10000FDX]);
1027283514Sarybchik	EFX_OR_QWORD(modes, encp->enc_loopback_types[EFX_LINK_40000FDX]);
1028283514Sarybchik	encp->enc_loopback_types[EFX_LINK_UNKNOWN] = modes;
1029283514Sarybchik
1030283514Sarybchik	return (0);
1031283514Sarybchik
1032283514Sarybchikfail2:
1033283514Sarybchik	EFSYS_PROBE(fail2);
1034283514Sarybchikfail1:
1035283514Sarybchik	EFSYS_PROBE1(fail1, int, rc);
1036283514Sarybchik
1037283514Sarybchik	return (rc);
1038283514Sarybchik}
1039283514Sarybchik
1040283514Sarybchik#endif /* EFSYS_OPT_LOOPBACK */
1041