efx_nic.c revision 298955
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 298955 2016-05-03 03:41:25Z pfg $");
33228078Sphilip
34227569Sphilip#include "efx.h"
35227569Sphilip#include "efx_impl.h"
36227569Sphilip
37291436Sarybchik	__checkReturn	efx_rc_t
38227569Sphilipefx_family(
39227569Sphilip	__in		uint16_t venid,
40227569Sphilip	__in		uint16_t devid,
41227569Sphilip	__out		efx_family_t *efp)
42227569Sphilip{
43283514Sarybchik	if (venid == EFX_PCI_VENID_SFC) {
44283514Sarybchik		switch (devid) {
45227569Sphilip#if EFSYS_OPT_FALCON
46283514Sarybchik		case EFX_PCI_DEVID_FALCON:
47283514Sarybchik			*efp = EFX_FAMILY_FALCON;
48283514Sarybchik			return (0);
49293731Sarybchik#endif /* EFSYS_OPT_FALCON */
50293731Sarybchik
51227569Sphilip#if EFSYS_OPT_SIENA
52283514Sarybchik		case EFX_PCI_DEVID_SIENA_F1_UNINIT:
53283514Sarybchik			/*
54283514Sarybchik			 * Hardware default for PF0 of uninitialised Siena.
55283514Sarybchik			 * manftest must be able to cope with this device id.
56283514Sarybchik			 */
57283514Sarybchik			*efp = EFX_FAMILY_SIENA;
58283514Sarybchik			return (0);
59283514Sarybchik
60283514Sarybchik		case EFX_PCI_DEVID_BETHPAGE:
61283514Sarybchik		case EFX_PCI_DEVID_SIENA:
62283514Sarybchik			*efp = EFX_FAMILY_SIENA;
63283514Sarybchik			return (0);
64293731Sarybchik#endif /* EFSYS_OPT_SIENA */
65283514Sarybchik
66283514Sarybchik#if EFSYS_OPT_HUNTINGTON
67283514Sarybchik		case EFX_PCI_DEVID_HUNTINGTON_PF_UNINIT:
68283514Sarybchik			/*
69283514Sarybchik			 * Hardware default for PF0 of uninitialised Huntington.
70283514Sarybchik			 * manftest must be able to cope with this device id.
71283514Sarybchik			 */
72283514Sarybchik			*efp = EFX_FAMILY_HUNTINGTON;
73283514Sarybchik			return (0);
74283514Sarybchik
75283514Sarybchik		case EFX_PCI_DEVID_FARMINGDALE:
76283514Sarybchik		case EFX_PCI_DEVID_GREENPORT:
77283514Sarybchik			*efp = EFX_FAMILY_HUNTINGTON;
78283514Sarybchik			return (0);
79283514Sarybchik
80283514Sarybchik		case EFX_PCI_DEVID_FARMINGDALE_VF:
81283514Sarybchik		case EFX_PCI_DEVID_GREENPORT_VF:
82283514Sarybchik			*efp = EFX_FAMILY_HUNTINGTON;
83283514Sarybchik			return (0);
84293731Sarybchik#endif /* EFSYS_OPT_HUNTINGTON */
85293731Sarybchik
86293731Sarybchik#if EFSYS_OPT_MEDFORD
87293731Sarybchik		case EFX_PCI_DEVID_MEDFORD_PF_UNINIT:
88293731Sarybchik			/*
89293731Sarybchik			 * Hardware default for PF0 of uninitialised Medford.
90293731Sarybchik			 * manftest must be able to cope with this device id.
91293731Sarybchik			 */
92293731Sarybchik			*efp = EFX_FAMILY_MEDFORD;
93293731Sarybchik			return (0);
94293731Sarybchik
95293731Sarybchik		case EFX_PCI_DEVID_MEDFORD:
96293731Sarybchik			*efp = EFX_FAMILY_MEDFORD;
97293731Sarybchik			return (0);
98293731Sarybchik
99293731Sarybchik		case EFX_PCI_DEVID_MEDFORD_VF:
100293731Sarybchik			*efp = EFX_FAMILY_MEDFORD;
101293731Sarybchik			return (0);
102293731Sarybchik#endif /* EFSYS_OPT_MEDFORD */
103293731Sarybchik
104283514Sarybchik		default:
105283514Sarybchik			break;
106283514Sarybchik		}
107227569Sphilip	}
108283514Sarybchik
109283514Sarybchik	*efp = EFX_FAMILY_INVALID;
110227569Sphilip	return (ENOTSUP);
111227569Sphilip}
112227569Sphilip
113227569Sphilip/*
114227569Sphilip * To support clients which aren't provided with any PCI context infer
115227569Sphilip * the hardware family by inspecting the hardware. Obviously the caller
116227569Sphilip * must be damn sure they're really talking to a supported device.
117227569Sphilip */
118291436Sarybchik	__checkReturn	efx_rc_t
119227569Sphilipefx_infer_family(
120227569Sphilip	__in		efsys_bar_t *esbp,
121227569Sphilip	__out		efx_family_t *efp)
122227569Sphilip{
123227569Sphilip	efx_family_t family;
124227569Sphilip	efx_oword_t oword;
125227569Sphilip	unsigned int portnum;
126291436Sarybchik	efx_rc_t rc;
127227569Sphilip
128227569Sphilip	EFSYS_BAR_READO(esbp, FR_AZ_CS_DEBUG_REG_OFST, &oword, B_TRUE);
129227569Sphilip	portnum = EFX_OWORD_FIELD(oword, FRF_CZ_CS_PORT_NUM);
130293731Sarybchik	if ((portnum == 1) || (portnum == 2)) {
131293731Sarybchik#if EFSYS_OPT_SIENA
132293731Sarybchik		family = EFX_FAMILY_SIENA;
133293731Sarybchik		goto out;
134293731Sarybchik#endif
135293731Sarybchik	} else if (portnum == 0) {
136283514Sarybchik		efx_dword_t dword;
137283514Sarybchik		uint32_t hw_rev;
138283514Sarybchik
139283514Sarybchik		EFSYS_BAR_READD(esbp, ER_DZ_BIU_HW_REV_ID_REG_OFST, &dword,
140283514Sarybchik		    B_TRUE);
141283514Sarybchik		hw_rev = EFX_DWORD_FIELD(dword, ERF_DZ_HW_REV_ID);
142283514Sarybchik		if (hw_rev == ER_DZ_BIU_HW_REV_ID_REG_RESET) {
143293731Sarybchik#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
144293731Sarybchik			/*
145293731Sarybchik			 * BIU_HW_REV_ID is the same for Huntington and Medford.
146293731Sarybchik			 * Assume Huntington, as Medford is very similar.
147293731Sarybchik			 */
148283514Sarybchik			family = EFX_FAMILY_HUNTINGTON;
149293731Sarybchik			goto out;
150283514Sarybchik#endif
151283514Sarybchik		} else {
152227569Sphilip#if EFSYS_OPT_FALCON
153283514Sarybchik			family = EFX_FAMILY_FALCON;
154293731Sarybchik			goto out;
155227569Sphilip#endif
156283514Sarybchik		}
157283514Sarybchik	}
158293731Sarybchik	rc = ENOTSUP;
159293731Sarybchik	goto fail1;
160283514Sarybchik
161293731Sarybchikout:
162227569Sphilip	if (efp != NULL)
163227569Sphilip		*efp = family;
164227569Sphilip	return (0);
165227569Sphilip
166227569Sphilipfail1:
167291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
168227569Sphilip
169227569Sphilip	return (rc);
170227569Sphilip}
171227569Sphilip
172227569Sphilip#define	EFX_BIU_MAGIC0	0x01234567
173227569Sphilip#define	EFX_BIU_MAGIC1	0xfedcba98
174227569Sphilip
175291436Sarybchik	__checkReturn	efx_rc_t
176227569Sphilipefx_nic_biu_test(
177227569Sphilip	__in		efx_nic_t *enp)
178227569Sphilip{
179227569Sphilip	efx_oword_t oword;
180291436Sarybchik	efx_rc_t rc;
181227569Sphilip
182227569Sphilip	/*
183227569Sphilip	 * Write magic values to scratch registers 0 and 1, then
184227569Sphilip	 * verify that the values were written correctly.  Interleave
185227569Sphilip	 * the accesses to ensure that the BIU is not just reading
186227569Sphilip	 * back the cached value that was last written.
187227569Sphilip	 */
188227569Sphilip	EFX_POPULATE_OWORD_1(oword, FRF_AZ_DRIVER_DW0, EFX_BIU_MAGIC0);
189283514Sarybchik	EFX_BAR_TBL_WRITEO(enp, FR_AZ_DRIVER_REG, 0, &oword, B_TRUE);
190227569Sphilip
191227569Sphilip	EFX_POPULATE_OWORD_1(oword, FRF_AZ_DRIVER_DW0, EFX_BIU_MAGIC1);
192283514Sarybchik	EFX_BAR_TBL_WRITEO(enp, FR_AZ_DRIVER_REG, 1, &oword, B_TRUE);
193227569Sphilip
194283514Sarybchik	EFX_BAR_TBL_READO(enp, FR_AZ_DRIVER_REG, 0, &oword, B_TRUE);
195227569Sphilip	if (EFX_OWORD_FIELD(oword, FRF_AZ_DRIVER_DW0) != EFX_BIU_MAGIC0) {
196227569Sphilip		rc = EIO;
197227569Sphilip		goto fail1;
198227569Sphilip	}
199227569Sphilip
200283514Sarybchik	EFX_BAR_TBL_READO(enp, FR_AZ_DRIVER_REG, 1, &oword, B_TRUE);
201227569Sphilip	if (EFX_OWORD_FIELD(oword, FRF_AZ_DRIVER_DW0) != EFX_BIU_MAGIC1) {
202227569Sphilip		rc = EIO;
203227569Sphilip		goto fail2;
204227569Sphilip	}
205227569Sphilip
206227569Sphilip	/*
207227569Sphilip	 * Perform the same test, with the values swapped.  This
208227569Sphilip	 * ensures that subsequent tests don't start with the correct
209227569Sphilip	 * values already written into the scratch registers.
210227569Sphilip	 */
211227569Sphilip	EFX_POPULATE_OWORD_1(oword, FRF_AZ_DRIVER_DW0, EFX_BIU_MAGIC1);
212283514Sarybchik	EFX_BAR_TBL_WRITEO(enp, FR_AZ_DRIVER_REG, 0, &oword, B_TRUE);
213227569Sphilip
214227569Sphilip	EFX_POPULATE_OWORD_1(oword, FRF_AZ_DRIVER_DW0, EFX_BIU_MAGIC0);
215283514Sarybchik	EFX_BAR_TBL_WRITEO(enp, FR_AZ_DRIVER_REG, 1, &oword, B_TRUE);
216227569Sphilip
217283514Sarybchik	EFX_BAR_TBL_READO(enp, FR_AZ_DRIVER_REG, 0, &oword, B_TRUE);
218227569Sphilip	if (EFX_OWORD_FIELD(oword, FRF_AZ_DRIVER_DW0) != EFX_BIU_MAGIC1) {
219227569Sphilip		rc = EIO;
220227569Sphilip		goto fail3;
221227569Sphilip	}
222227569Sphilip
223283514Sarybchik	EFX_BAR_TBL_READO(enp, FR_AZ_DRIVER_REG, 1, &oword, B_TRUE);
224227569Sphilip	if (EFX_OWORD_FIELD(oword, FRF_AZ_DRIVER_DW0) != EFX_BIU_MAGIC0) {
225227569Sphilip		rc = EIO;
226227569Sphilip		goto fail4;
227227569Sphilip	}
228227569Sphilip
229227569Sphilip	return (0);
230227569Sphilip
231227569Sphilipfail4:
232227569Sphilip	EFSYS_PROBE(fail4);
233227569Sphilipfail3:
234227569Sphilip	EFSYS_PROBE(fail3);
235227569Sphilipfail2:
236227569Sphilip	EFSYS_PROBE(fail2);
237227569Sphilipfail1:
238291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
239227569Sphilip
240227569Sphilip	return (rc);
241227569Sphilip}
242227569Sphilip
243227569Sphilip#if EFSYS_OPT_FALCON
244227569Sphilip
245283514Sarybchikstatic efx_nic_ops_t	__efx_nic_falcon_ops = {
246227569Sphilip	falcon_nic_probe,		/* eno_probe */
247293887Sarybchik	NULL,				/* eno_board_cfg */
248283514Sarybchik	NULL,				/* eno_set_drv_limits */
249227569Sphilip	falcon_nic_reset,		/* eno_reset */
250227569Sphilip	falcon_nic_init,		/* eno_init */
251283514Sarybchik	NULL,				/* eno_get_vi_pool */
252283514Sarybchik	NULL,				/* eno_get_bar_region */
253227569Sphilip#if EFSYS_OPT_DIAG
254227569Sphilip	falcon_sram_test,		/* eno_sram_test */
255227569Sphilip	falcon_nic_register_test,	/* eno_register_test */
256227569Sphilip#endif	/* EFSYS_OPT_DIAG */
257227569Sphilip	falcon_nic_fini,		/* eno_fini */
258227569Sphilip	falcon_nic_unprobe,		/* eno_unprobe */
259227569Sphilip};
260227569Sphilip
261227569Sphilip#endif	/* EFSYS_OPT_FALCON */
262227569Sphilip
263227569Sphilip#if EFSYS_OPT_SIENA
264227569Sphilip
265283514Sarybchikstatic efx_nic_ops_t	__efx_nic_siena_ops = {
266227569Sphilip	siena_nic_probe,		/* eno_probe */
267293887Sarybchik	NULL,				/* eno_board_cfg */
268283514Sarybchik	NULL,				/* eno_set_drv_limits */
269227569Sphilip	siena_nic_reset,		/* eno_reset */
270227569Sphilip	siena_nic_init,			/* eno_init */
271283514Sarybchik	NULL,				/* eno_get_vi_pool */
272283514Sarybchik	NULL,				/* eno_get_bar_region */
273227569Sphilip#if EFSYS_OPT_DIAG
274227569Sphilip	siena_sram_test,		/* eno_sram_test */
275227569Sphilip	siena_nic_register_test,	/* eno_register_test */
276227569Sphilip#endif	/* EFSYS_OPT_DIAG */
277227569Sphilip	siena_nic_fini,			/* eno_fini */
278227569Sphilip	siena_nic_unprobe,		/* eno_unprobe */
279227569Sphilip};
280227569Sphilip
281227569Sphilip#endif	/* EFSYS_OPT_SIENA */
282227569Sphilip
283283514Sarybchik#if EFSYS_OPT_HUNTINGTON
284283514Sarybchik
285283514Sarybchikstatic efx_nic_ops_t	__efx_nic_hunt_ops = {
286293805Sarybchik	ef10_nic_probe,			/* eno_probe */
287293887Sarybchik	hunt_board_cfg,			/* eno_board_cfg */
288293805Sarybchik	ef10_nic_set_drv_limits,	/* eno_set_drv_limits */
289293805Sarybchik	ef10_nic_reset,			/* eno_reset */
290293805Sarybchik	ef10_nic_init,			/* eno_init */
291293805Sarybchik	ef10_nic_get_vi_pool,		/* eno_get_vi_pool */
292293805Sarybchik	ef10_nic_get_bar_region,	/* eno_get_bar_region */
293283514Sarybchik#if EFSYS_OPT_DIAG
294293750Sarybchik	ef10_sram_test,			/* eno_sram_test */
295293805Sarybchik	ef10_nic_register_test,		/* eno_register_test */
296283514Sarybchik#endif	/* EFSYS_OPT_DIAG */
297293805Sarybchik	ef10_nic_fini,			/* eno_fini */
298293805Sarybchik	ef10_nic_unprobe,		/* eno_unprobe */
299283514Sarybchik};
300283514Sarybchik
301283514Sarybchik#endif	/* EFSYS_OPT_HUNTINGTON */
302283514Sarybchik
303293887Sarybchik#if EFSYS_OPT_MEDFORD
304293887Sarybchik
305293887Sarybchikstatic efx_nic_ops_t	__efx_nic_medford_ops = {
306293887Sarybchik	ef10_nic_probe,			/* eno_probe */
307293887Sarybchik	medford_board_cfg,		/* eno_board_cfg */
308293887Sarybchik	ef10_nic_set_drv_limits,	/* eno_set_drv_limits */
309293887Sarybchik	ef10_nic_reset,			/* eno_reset */
310293887Sarybchik	ef10_nic_init,			/* eno_init */
311293887Sarybchik	ef10_nic_get_vi_pool,		/* eno_get_vi_pool */
312293887Sarybchik	ef10_nic_get_bar_region,	/* eno_get_bar_region */
313293887Sarybchik#if EFSYS_OPT_DIAG
314293887Sarybchik	ef10_sram_test,			/* eno_sram_test */
315293887Sarybchik	ef10_nic_register_test,		/* eno_register_test */
316293887Sarybchik#endif	/* EFSYS_OPT_DIAG */
317293887Sarybchik	ef10_nic_fini,			/* eno_fini */
318293887Sarybchik	ef10_nic_unprobe,		/* eno_unprobe */
319293887Sarybchik};
320293887Sarybchik
321293887Sarybchik#endif	/* EFSYS_OPT_MEDFORD */
322293887Sarybchik
323293887Sarybchik
324291436Sarybchik	__checkReturn	efx_rc_t
325227569Sphilipefx_nic_create(
326227569Sphilip	__in		efx_family_t family,
327227569Sphilip	__in		efsys_identifier_t *esip,
328227569Sphilip	__in		efsys_bar_t *esbp,
329227569Sphilip	__in		efsys_lock_t *eslp,
330227569Sphilip	__deref_out	efx_nic_t **enpp)
331227569Sphilip{
332227569Sphilip	efx_nic_t *enp;
333291436Sarybchik	efx_rc_t rc;
334227569Sphilip
335227569Sphilip	EFSYS_ASSERT3U(family, >, EFX_FAMILY_INVALID);
336227569Sphilip	EFSYS_ASSERT3U(family, <, EFX_FAMILY_NTYPES);
337227569Sphilip
338227569Sphilip	/* Allocate a NIC object */
339227569Sphilip	EFSYS_KMEM_ALLOC(esip, sizeof (efx_nic_t), enp);
340227569Sphilip
341227569Sphilip	if (enp == NULL) {
342227569Sphilip		rc = ENOMEM;
343227569Sphilip		goto fail1;
344227569Sphilip	}
345227569Sphilip
346227569Sphilip	enp->en_magic = EFX_NIC_MAGIC;
347227569Sphilip
348227569Sphilip	switch (family) {
349227569Sphilip#if EFSYS_OPT_FALCON
350227569Sphilip	case EFX_FAMILY_FALCON:
351227569Sphilip		enp->en_enop = (efx_nic_ops_t *)&__efx_nic_falcon_ops;
352227569Sphilip		enp->en_features = 0;
353227569Sphilip		break;
354227569Sphilip#endif	/* EFSYS_OPT_FALCON */
355227569Sphilip
356227569Sphilip#if EFSYS_OPT_SIENA
357227569Sphilip	case EFX_FAMILY_SIENA:
358227569Sphilip		enp->en_enop = (efx_nic_ops_t *)&__efx_nic_siena_ops;
359279141Sarybchik		enp->en_features =
360279141Sarybchik		    EFX_FEATURE_IPV6 |
361227569Sphilip		    EFX_FEATURE_LFSR_HASH_INSERT |
362279141Sarybchik		    EFX_FEATURE_LINK_EVENTS |
363279141Sarybchik		    EFX_FEATURE_PERIODIC_MAC_STATS |
364279141Sarybchik		    EFX_FEATURE_WOL |
365279141Sarybchik		    EFX_FEATURE_MCDI |
366278839Sarybchik		    EFX_FEATURE_LOOKAHEAD_SPLIT |
367283514Sarybchik		    EFX_FEATURE_MAC_HEADER_FILTERS |
368283514Sarybchik		    EFX_FEATURE_TX_SRC_FILTERS;
369227569Sphilip		break;
370227569Sphilip#endif	/* EFSYS_OPT_SIENA */
371227569Sphilip
372283514Sarybchik#if EFSYS_OPT_HUNTINGTON
373283514Sarybchik	case EFX_FAMILY_HUNTINGTON:
374283514Sarybchik		enp->en_enop = (efx_nic_ops_t *)&__efx_nic_hunt_ops;
375283514Sarybchik		/* FIXME: Add WOL support */
376283514Sarybchik		enp->en_features =
377283514Sarybchik		    EFX_FEATURE_IPV6 |
378283514Sarybchik		    EFX_FEATURE_LINK_EVENTS |
379283514Sarybchik		    EFX_FEATURE_PERIODIC_MAC_STATS |
380283514Sarybchik		    EFX_FEATURE_MCDI |
381283514Sarybchik		    EFX_FEATURE_MAC_HEADER_FILTERS |
382283514Sarybchik		    EFX_FEATURE_MCDI_DMA |
383283514Sarybchik		    EFX_FEATURE_PIO_BUFFERS |
384293891Sarybchik		    EFX_FEATURE_FW_ASSISTED_TSO |
385293891Sarybchik		    EFX_FEATURE_FW_ASSISTED_TSO_V2;
386283514Sarybchik		break;
387283514Sarybchik#endif	/* EFSYS_OPT_HUNTINGTON */
388283514Sarybchik
389293887Sarybchik#if EFSYS_OPT_MEDFORD
390293887Sarybchik	case EFX_FAMILY_MEDFORD:
391293887Sarybchik		enp->en_enop = (efx_nic_ops_t *)&__efx_nic_medford_ops;
392293887Sarybchik		/*
393298955Spfg		 * FW_ASSISTED_TSO omitted as Medford only supports firmware
394293887Sarybchik		 * assisted TSO version 2, not the v1 scheme used on Huntington.
395293887Sarybchik		 */
396293887Sarybchik		enp->en_features =
397293887Sarybchik		    EFX_FEATURE_IPV6 |
398293887Sarybchik		    EFX_FEATURE_LINK_EVENTS |
399293887Sarybchik		    EFX_FEATURE_PERIODIC_MAC_STATS |
400293887Sarybchik		    EFX_FEATURE_MCDI |
401293887Sarybchik		    EFX_FEATURE_MAC_HEADER_FILTERS |
402293887Sarybchik		    EFX_FEATURE_MCDI_DMA |
403293887Sarybchik		    EFX_FEATURE_PIO_BUFFERS;
404293887Sarybchik		break;
405293887Sarybchik#endif	/* EFSYS_OPT_MEDFORD */
406293887Sarybchik
407227569Sphilip	default:
408227569Sphilip		rc = ENOTSUP;
409227569Sphilip		goto fail2;
410227569Sphilip	}
411227569Sphilip
412227569Sphilip	enp->en_family = family;
413227569Sphilip	enp->en_esip = esip;
414227569Sphilip	enp->en_esbp = esbp;
415227569Sphilip	enp->en_eslp = eslp;
416227569Sphilip
417227569Sphilip	*enpp = enp;
418227569Sphilip
419227569Sphilip	return (0);
420227569Sphilip
421227569Sphilipfail2:
422283514Sarybchik	EFSYS_PROBE(fail2);
423227569Sphilip
424227569Sphilip	enp->en_magic = 0;
425227569Sphilip
426227569Sphilip	/* Free the NIC object */
427227569Sphilip	EFSYS_KMEM_FREE(esip, sizeof (efx_nic_t), enp);
428227569Sphilip
429227569Sphilipfail1:
430291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
431227569Sphilip
432227569Sphilip	return (rc);
433227569Sphilip}
434227569Sphilip
435291436Sarybchik	__checkReturn	efx_rc_t
436227569Sphilipefx_nic_probe(
437227569Sphilip	__in		efx_nic_t *enp)
438227569Sphilip{
439227569Sphilip	efx_nic_ops_t *enop;
440291436Sarybchik	efx_rc_t rc;
441227569Sphilip
442227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
443227569Sphilip#if EFSYS_OPT_MCDI
444227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
445227569Sphilip#endif	/* EFSYS_OPT_MCDI */
446227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_PROBE));
447227569Sphilip
448227569Sphilip	enop = enp->en_enop;
449227569Sphilip	if ((rc = enop->eno_probe(enp)) != 0)
450283514Sarybchik		goto fail1;
451227569Sphilip
452227569Sphilip	if ((rc = efx_phy_probe(enp)) != 0)
453283514Sarybchik		goto fail2;
454227569Sphilip
455227569Sphilip	enp->en_mod_flags |= EFX_MOD_PROBE;
456227569Sphilip
457227569Sphilip	return (0);
458227569Sphilip
459283514Sarybchikfail2:
460283514Sarybchik	EFSYS_PROBE(fail2);
461227569Sphilip
462227569Sphilip	enop->eno_unprobe(enp);
463227569Sphilip
464227569Sphilipfail1:
465291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
466227569Sphilip
467227569Sphilip	return (rc);
468227569Sphilip}
469227569Sphilip
470227569Sphilip#if EFSYS_OPT_PCIE_TUNE
471227569Sphilip
472291436Sarybchik	__checkReturn	efx_rc_t
473227569Sphilipefx_nic_pcie_tune(
474227569Sphilip	__in		efx_nic_t *enp,
475227569Sphilip	unsigned int	nlanes)
476227569Sphilip{
477227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
478227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
479227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_NIC));
480227569Sphilip
481227569Sphilip#if EFSYS_OPT_FALCON
482227569Sphilip	if (enp->en_family == EFX_FAMILY_FALCON)
483227569Sphilip		return (falcon_nic_pcie_tune(enp, nlanes));
484227569Sphilip#endif
485227569Sphilip	return (ENOTSUP);
486227569Sphilip}
487227569Sphilip
488291436Sarybchik	__checkReturn	efx_rc_t
489227569Sphilipefx_nic_pcie_extended_sync(
490227569Sphilip	__in		efx_nic_t *enp)
491227569Sphilip{
492227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
493227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
494227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_NIC));
495227569Sphilip
496227569Sphilip#if EFSYS_OPT_SIENA
497227569Sphilip	if (enp->en_family == EFX_FAMILY_SIENA)
498227569Sphilip		return (siena_nic_pcie_extended_sync(enp));
499227569Sphilip#endif
500227569Sphilip
501227569Sphilip	return (ENOTSUP);
502227569Sphilip}
503227569Sphilip
504227569Sphilip#endif	/* EFSYS_OPT_PCIE_TUNE */
505227569Sphilip
506291436Sarybchik	__checkReturn	efx_rc_t
507283514Sarybchikefx_nic_set_drv_limits(
508283514Sarybchik	__inout		efx_nic_t *enp,
509283514Sarybchik	__in		efx_drv_limits_t *edlp)
510283514Sarybchik{
511283514Sarybchik	efx_nic_ops_t *enop = enp->en_enop;
512291436Sarybchik	efx_rc_t rc;
513283514Sarybchik
514283514Sarybchik	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
515283514Sarybchik	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
516283514Sarybchik
517283514Sarybchik	if (enop->eno_set_drv_limits != NULL) {
518283514Sarybchik		if ((rc = enop->eno_set_drv_limits(enp, edlp)) != 0)
519283514Sarybchik			goto fail1;
520283514Sarybchik	}
521283514Sarybchik
522283514Sarybchik	return (0);
523283514Sarybchik
524283514Sarybchikfail1:
525291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
526283514Sarybchik
527283514Sarybchik	return (rc);
528283514Sarybchik}
529283514Sarybchik
530291436Sarybchik	__checkReturn	efx_rc_t
531283514Sarybchikefx_nic_get_bar_region(
532283514Sarybchik	__in		efx_nic_t *enp,
533283514Sarybchik	__in		efx_nic_region_t region,
534283514Sarybchik	__out		uint32_t *offsetp,
535283514Sarybchik	__out		size_t *sizep)
536283514Sarybchik{
537283514Sarybchik	efx_nic_ops_t *enop = enp->en_enop;
538291436Sarybchik	efx_rc_t rc;
539283514Sarybchik
540283514Sarybchik	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
541283514Sarybchik	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
542283514Sarybchik	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NIC);
543283514Sarybchik
544283514Sarybchik	if (enop->eno_get_bar_region == NULL) {
545283514Sarybchik		rc = ENOTSUP;
546283514Sarybchik		goto fail1;
547283514Sarybchik	}
548283514Sarybchik	if ((rc = (enop->eno_get_bar_region)(enp,
549283514Sarybchik		    region, offsetp, sizep)) != 0) {
550283514Sarybchik		goto fail2;
551283514Sarybchik	}
552283514Sarybchik
553283514Sarybchik	return (0);
554283514Sarybchik
555283514Sarybchikfail2:
556283514Sarybchik	EFSYS_PROBE(fail2);
557283514Sarybchik
558283514Sarybchikfail1:
559291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
560283514Sarybchik
561283514Sarybchik	return (rc);
562283514Sarybchik}
563283514Sarybchik
564283514Sarybchik
565291436Sarybchik	__checkReturn	efx_rc_t
566283514Sarybchikefx_nic_get_vi_pool(
567283514Sarybchik	__in		efx_nic_t *enp,
568283514Sarybchik	__out		uint32_t *evq_countp,
569283514Sarybchik	__out		uint32_t *rxq_countp,
570283514Sarybchik	__out		uint32_t *txq_countp)
571283514Sarybchik{
572283514Sarybchik	efx_nic_ops_t *enop = enp->en_enop;
573283514Sarybchik	efx_nic_cfg_t *encp = &enp->en_nic_cfg;
574291436Sarybchik	efx_rc_t rc;
575283514Sarybchik
576283514Sarybchik	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
577283514Sarybchik	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
578283514Sarybchik	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_NIC);
579283514Sarybchik
580283514Sarybchik	if (enop->eno_get_vi_pool != NULL) {
581283514Sarybchik		uint32_t vi_count = 0;
582283514Sarybchik
583283514Sarybchik		if ((rc = (enop->eno_get_vi_pool)(enp, &vi_count)) != 0)
584283514Sarybchik			goto fail1;
585283514Sarybchik
586283514Sarybchik		*evq_countp = vi_count;
587283514Sarybchik		*rxq_countp = vi_count;
588283514Sarybchik		*txq_countp = vi_count;
589283514Sarybchik	} else {
590283514Sarybchik		/* Use NIC limits as default value */
591283514Sarybchik		*evq_countp = encp->enc_evq_limit;
592283514Sarybchik		*rxq_countp = encp->enc_rxq_limit;
593283514Sarybchik		*txq_countp = encp->enc_txq_limit;
594283514Sarybchik	}
595283514Sarybchik
596283514Sarybchik	return (0);
597283514Sarybchik
598283514Sarybchikfail1:
599291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
600283514Sarybchik
601283514Sarybchik	return (rc);
602283514Sarybchik}
603283514Sarybchik
604283514Sarybchik
605291436Sarybchik	__checkReturn	efx_rc_t
606227569Sphilipefx_nic_init(
607227569Sphilip	__in		efx_nic_t *enp)
608227569Sphilip{
609227569Sphilip	efx_nic_ops_t *enop = enp->en_enop;
610291436Sarybchik	efx_rc_t rc;
611227569Sphilip
612227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
613227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
614227569Sphilip
615227569Sphilip	if (enp->en_mod_flags & EFX_MOD_NIC) {
616227569Sphilip		rc = EINVAL;
617227569Sphilip		goto fail1;
618227569Sphilip	}
619227569Sphilip
620227569Sphilip	if ((rc = enop->eno_init(enp)) != 0)
621227569Sphilip		goto fail2;
622227569Sphilip
623227569Sphilip	enp->en_mod_flags |= EFX_MOD_NIC;
624227569Sphilip
625227569Sphilip	return (0);
626227569Sphilip
627227569Sphilipfail2:
628227569Sphilip	EFSYS_PROBE(fail2);
629227569Sphilipfail1:
630291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
631227569Sphilip
632227569Sphilip	return (rc);
633227569Sphilip}
634227569Sphilip
635227569Sphilip			void
636227569Sphilipefx_nic_fini(
637227569Sphilip	__in		efx_nic_t *enp)
638227569Sphilip{
639227569Sphilip	efx_nic_ops_t *enop = enp->en_enop;
640227569Sphilip
641227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
642227569Sphilip	EFSYS_ASSERT(enp->en_mod_flags & EFX_MOD_PROBE);
643227569Sphilip	EFSYS_ASSERT(enp->en_mod_flags & EFX_MOD_NIC);
644227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_INTR));
645227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_EV));
646227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_RX));
647227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_TX));
648227569Sphilip
649227569Sphilip	enop->eno_fini(enp);
650227569Sphilip
651227569Sphilip	enp->en_mod_flags &= ~EFX_MOD_NIC;
652227569Sphilip}
653227569Sphilip
654227569Sphilip			void
655227569Sphilipefx_nic_unprobe(
656227569Sphilip	__in		efx_nic_t *enp)
657227569Sphilip{
658227569Sphilip	efx_nic_ops_t *enop = enp->en_enop;
659227569Sphilip
660227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
661227569Sphilip#if EFSYS_OPT_MCDI
662227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
663227569Sphilip#endif	/* EFSYS_OPT_MCDI */
664227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
665227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_NIC));
666227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_INTR));
667227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_EV));
668227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_RX));
669227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_TX));
670227569Sphilip
671227569Sphilip	efx_phy_unprobe(enp);
672227569Sphilip
673227569Sphilip	enop->eno_unprobe(enp);
674227569Sphilip
675227569Sphilip	enp->en_mod_flags &= ~EFX_MOD_PROBE;
676227569Sphilip}
677227569Sphilip
678227569Sphilip			void
679227569Sphilipefx_nic_destroy(
680227569Sphilip	__in	efx_nic_t *enp)
681227569Sphilip{
682227569Sphilip	efsys_identifier_t *esip = enp->en_esip;
683227569Sphilip
684227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
685227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, ==, 0);
686227569Sphilip
687227569Sphilip	enp->en_family = 0;
688227569Sphilip	enp->en_esip = NULL;
689227569Sphilip	enp->en_esbp = NULL;
690227569Sphilip	enp->en_eslp = NULL;
691227569Sphilip
692227569Sphilip	enp->en_enop = NULL;
693227569Sphilip
694227569Sphilip	enp->en_magic = 0;
695227569Sphilip
696227569Sphilip	/* Free the NIC object */
697227569Sphilip	EFSYS_KMEM_FREE(esip, sizeof (efx_nic_t), enp);
698227569Sphilip}
699227569Sphilip
700291436Sarybchik	__checkReturn	efx_rc_t
701227569Sphilipefx_nic_reset(
702227569Sphilip	__in		efx_nic_t *enp)
703227569Sphilip{
704227569Sphilip	efx_nic_ops_t *enop = enp->en_enop;
705227569Sphilip	unsigned int mod_flags;
706291436Sarybchik	efx_rc_t rc;
707227569Sphilip
708227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
709227569Sphilip	EFSYS_ASSERT(enp->en_mod_flags & EFX_MOD_PROBE);
710227569Sphilip	/*
711293901Sarybchik	 * All modules except the MCDI, PROBE, NVRAM, VPD, MON, LIC
712293901Sarybchik	 * (which we do not reset here) must have been shut down or never
713293901Sarybchik	 * initialized.
714227569Sphilip	 *
715227569Sphilip	 * A rule of thumb here is: If the controller or MC reboots, is *any*
716227569Sphilip	 * state lost. If it's lost and needs reapplying, then the module
717227569Sphilip	 * *must* not be initialised during the reset.
718227569Sphilip	 */
719227569Sphilip	mod_flags = enp->en_mod_flags;
720227569Sphilip	mod_flags &= ~(EFX_MOD_MCDI | EFX_MOD_PROBE | EFX_MOD_NVRAM |
721293901Sarybchik		    EFX_MOD_VPD | EFX_MOD_MON | EFX_MOD_LIC);
722227569Sphilip	EFSYS_ASSERT3U(mod_flags, ==, 0);
723227569Sphilip	if (mod_flags != 0) {
724227569Sphilip		rc = EINVAL;
725227569Sphilip		goto fail1;
726227569Sphilip	}
727227569Sphilip
728227569Sphilip	if ((rc = enop->eno_reset(enp)) != 0)
729227569Sphilip		goto fail2;
730227569Sphilip
731227569Sphilip	enp->en_reset_flags |= EFX_RESET_MAC;
732227569Sphilip
733227569Sphilip	return (0);
734227569Sphilip
735227569Sphilipfail2:
736227569Sphilip	EFSYS_PROBE(fail2);
737227569Sphilipfail1:
738291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
739227569Sphilip
740227569Sphilip	return (rc);
741227569Sphilip}
742227569Sphilip
743227569Sphilip			const efx_nic_cfg_t *
744227569Sphilipefx_nic_cfg_get(
745227569Sphilip	__in		efx_nic_t *enp)
746227569Sphilip{
747227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
748227569Sphilip
749227569Sphilip	return (&(enp->en_nic_cfg));
750227569Sphilip}
751227569Sphilip
752227569Sphilip#if EFSYS_OPT_DIAG
753227569Sphilip
754291436Sarybchik	__checkReturn	efx_rc_t
755227569Sphilipefx_nic_register_test(
756227569Sphilip	__in		efx_nic_t *enp)
757227569Sphilip{
758227569Sphilip	efx_nic_ops_t *enop = enp->en_enop;
759291436Sarybchik	efx_rc_t rc;
760227569Sphilip
761227569Sphilip	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
762227569Sphilip	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
763227569Sphilip	EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_NIC));
764227569Sphilip
765227569Sphilip	if ((rc = enop->eno_register_test(enp)) != 0)
766227569Sphilip		goto fail1;
767227569Sphilip
768227569Sphilip	return (0);
769227569Sphilip
770227569Sphilipfail1:
771291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
772227569Sphilip
773227569Sphilip	return (rc);
774227569Sphilip}
775227569Sphilip
776291436Sarybchik	__checkReturn	efx_rc_t
777227569Sphilipefx_nic_test_registers(
778227569Sphilip	__in		efx_nic_t *enp,
779227569Sphilip	__in		efx_register_set_t *rsp,
780227569Sphilip	__in		size_t count)
781227569Sphilip{
782227569Sphilip	unsigned int bit;
783227569Sphilip	efx_oword_t original;
784227569Sphilip	efx_oword_t reg;
785227569Sphilip	efx_oword_t buf;
786291436Sarybchik	efx_rc_t rc;
787227569Sphilip
788227569Sphilip	while (count > 0) {
789227569Sphilip		/* This function is only suitable for registers */
790227569Sphilip		EFSYS_ASSERT(rsp->rows == 1);
791227569Sphilip
792227569Sphilip		/* bit sweep on and off */
793227569Sphilip		EFSYS_BAR_READO(enp->en_esbp, rsp->address, &original,
794227569Sphilip			    B_TRUE);
795227569Sphilip		for (bit = 0; bit < 128; bit++) {
796227569Sphilip			/* Is this bit in the mask? */
797227569Sphilip			if (~(rsp->mask.eo_u32[bit >> 5]) & (1 << bit))
798227569Sphilip				continue;
799227569Sphilip
800227569Sphilip			/* Test this bit can be set in isolation */
801227569Sphilip			reg = original;
802227569Sphilip			EFX_AND_OWORD(reg, rsp->mask);
803227569Sphilip			EFX_SET_OWORD_BIT(reg, bit);
804227569Sphilip
805227569Sphilip			EFSYS_BAR_WRITEO(enp->en_esbp, rsp->address, &reg,
806227569Sphilip				    B_TRUE);
807227569Sphilip			EFSYS_BAR_READO(enp->en_esbp, rsp->address, &buf,
808227569Sphilip				    B_TRUE);
809227569Sphilip
810227569Sphilip			EFX_AND_OWORD(buf, rsp->mask);
811227569Sphilip			if (memcmp(&reg, &buf, sizeof (reg))) {
812227569Sphilip				rc = EIO;
813227569Sphilip				goto fail1;
814227569Sphilip			}
815227569Sphilip
816227569Sphilip			/* Test this bit can be cleared in isolation */
817227569Sphilip			EFX_OR_OWORD(reg, rsp->mask);
818227569Sphilip			EFX_CLEAR_OWORD_BIT(reg, bit);
819227569Sphilip
820227569Sphilip			EFSYS_BAR_WRITEO(enp->en_esbp, rsp->address, &reg,
821227569Sphilip				    B_TRUE);
822227569Sphilip			EFSYS_BAR_READO(enp->en_esbp, rsp->address, &buf,
823227569Sphilip				    B_TRUE);
824227569Sphilip
825227569Sphilip			EFX_AND_OWORD(buf, rsp->mask);
826227569Sphilip			if (memcmp(&reg, &buf, sizeof (reg))) {
827227569Sphilip				rc = EIO;
828227569Sphilip				goto fail2;
829227569Sphilip			}
830227569Sphilip		}
831227569Sphilip
832227569Sphilip		/* Restore the old value */
833227569Sphilip		EFSYS_BAR_WRITEO(enp->en_esbp, rsp->address, &original,
834227569Sphilip			    B_TRUE);
835227569Sphilip
836227569Sphilip		--count;
837227569Sphilip		++rsp;
838227569Sphilip	}
839227569Sphilip
840227569Sphilip	return (0);
841227569Sphilip
842227569Sphilipfail2:
843227569Sphilip	EFSYS_PROBE(fail2);
844227569Sphilipfail1:
845291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
846227569Sphilip
847227569Sphilip	/* Restore the old value */
848227569Sphilip	EFSYS_BAR_WRITEO(enp->en_esbp, rsp->address, &original, B_TRUE);
849227569Sphilip
850227569Sphilip	return (rc);
851227569Sphilip}
852227569Sphilip
853291436Sarybchik	__checkReturn	efx_rc_t
854227569Sphilipefx_nic_test_tables(
855227569Sphilip	__in		efx_nic_t *enp,
856227569Sphilip	__in		efx_register_set_t *rsp,
857227569Sphilip	__in		efx_pattern_type_t pattern,
858227569Sphilip	__in		size_t count)
859227569Sphilip{
860227569Sphilip	efx_sram_pattern_fn_t func;
861227569Sphilip	unsigned int index;
862227569Sphilip	unsigned int address;
863227569Sphilip	efx_oword_t reg;
864227569Sphilip	efx_oword_t buf;
865291436Sarybchik	efx_rc_t rc;
866227569Sphilip
867227569Sphilip	EFSYS_ASSERT(pattern < EFX_PATTERN_NTYPES);
868227569Sphilip	func = __efx_sram_pattern_fns[pattern];
869227569Sphilip
870227569Sphilip	while (count > 0) {
871227569Sphilip		/* Write */
872227569Sphilip		address = rsp->address;
873227569Sphilip		for (index = 0; index < rsp->rows; ++index) {
874227569Sphilip			func(2 * index + 0, B_FALSE, &reg.eo_qword[0]);
875227569Sphilip			func(2 * index + 1, B_FALSE, &reg.eo_qword[1]);
876227569Sphilip			EFX_AND_OWORD(reg, rsp->mask);
877227569Sphilip			EFSYS_BAR_WRITEO(enp->en_esbp, address, &reg, B_TRUE);
878227569Sphilip
879227569Sphilip			address += rsp->step;
880227569Sphilip		}
881227569Sphilip
882227569Sphilip		/* Read */
883227569Sphilip		address = rsp->address;
884227569Sphilip		for (index = 0; index < rsp->rows; ++index) {
885227569Sphilip			func(2 * index + 0, B_FALSE, &reg.eo_qword[0]);
886227569Sphilip			func(2 * index + 1, B_FALSE, &reg.eo_qword[1]);
887227569Sphilip			EFX_AND_OWORD(reg, rsp->mask);
888227569Sphilip			EFSYS_BAR_READO(enp->en_esbp, address, &buf, B_TRUE);
889227569Sphilip			if (memcmp(&reg, &buf, sizeof (reg))) {
890227569Sphilip				rc = EIO;
891227569Sphilip				goto fail1;
892227569Sphilip			}
893227569Sphilip
894227569Sphilip			address += rsp->step;
895227569Sphilip		}
896227569Sphilip
897227569Sphilip		++rsp;
898227569Sphilip		--count;
899227569Sphilip	}
900227569Sphilip
901227569Sphilip	return (0);
902227569Sphilip
903227569Sphilipfail1:
904291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
905227569Sphilip
906227569Sphilip	return (rc);
907227569Sphilip}
908227569Sphilip
909227569Sphilip#endif	/* EFSYS_OPT_DIAG */
910283514Sarybchik
911283514Sarybchik#if EFSYS_OPT_LOOPBACK
912283514Sarybchik
913283514Sarybchikextern			void
914283514Sarybchikefx_loopback_mask(
915283514Sarybchik	__in	efx_loopback_kind_t loopback_kind,
916283514Sarybchik	__out	efx_qword_t *maskp)
917283514Sarybchik{
918283514Sarybchik	efx_qword_t mask;
919283514Sarybchik
920283514Sarybchik	EFSYS_ASSERT3U(loopback_kind, <, EFX_LOOPBACK_NKINDS);
921283514Sarybchik	EFSYS_ASSERT(maskp != NULL);
922283514Sarybchik
923283514Sarybchik	/* Assert the MC_CMD_LOOPBACK and EFX_LOOPBACK namespace agree */
924283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_NONE == EFX_LOOPBACK_OFF);
925283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_DATA == EFX_LOOPBACK_DATA);
926283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMAC == EFX_LOOPBACK_GMAC);
927283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGMII == EFX_LOOPBACK_XGMII);
928283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGXS == EFX_LOOPBACK_XGXS);
929283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI == EFX_LOOPBACK_XAUI);
930283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMII == EFX_LOOPBACK_GMII);
931283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SGMII == EFX_LOOPBACK_SGMII);
932283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGBR == EFX_LOOPBACK_XGBR);
933283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XFI == EFX_LOOPBACK_XFI);
934283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI_FAR == EFX_LOOPBACK_XAUI_FAR);
935283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMII_FAR == EFX_LOOPBACK_GMII_FAR);
936283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SGMII_FAR == EFX_LOOPBACK_SGMII_FAR);
937283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XFI_FAR == EFX_LOOPBACK_XFI_FAR);
938283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GPHY == EFX_LOOPBACK_GPHY);
939283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PHYXS == EFX_LOOPBACK_PHY_XS);
940283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PCS == EFX_LOOPBACK_PCS);
941283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PMAPMD == EFX_LOOPBACK_PMA_PMD);
942283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XPORT == EFX_LOOPBACK_XPORT);
943283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGMII_WS == EFX_LOOPBACK_XGMII_WS);
944283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI_WS == EFX_LOOPBACK_XAUI_WS);
945283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI_WS_FAR ==
946283514Sarybchik	    EFX_LOOPBACK_XAUI_WS_FAR);
947283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI_WS_NEAR ==
948283514Sarybchik	    EFX_LOOPBACK_XAUI_WS_NEAR);
949283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMII_WS == EFX_LOOPBACK_GMII_WS);
950283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XFI_WS == EFX_LOOPBACK_XFI_WS);
951283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XFI_WS_FAR ==
952283514Sarybchik	    EFX_LOOPBACK_XFI_WS_FAR);
953283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PHYXS_WS == EFX_LOOPBACK_PHYXS_WS);
954283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PMA_INT == EFX_LOOPBACK_PMA_INT);
955283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SD_NEAR == EFX_LOOPBACK_SD_NEAR);
956283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SD_FAR == EFX_LOOPBACK_SD_FAR);
957283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PMA_INT_WS ==
958283514Sarybchik	    EFX_LOOPBACK_PMA_INT_WS);
959283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SD_FEP2_WS ==
960283514Sarybchik	    EFX_LOOPBACK_SD_FEP2_WS);
961283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SD_FEP1_5_WS ==
962283514Sarybchik	    EFX_LOOPBACK_SD_FEP1_5_WS);
963283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SD_FEP_WS == EFX_LOOPBACK_SD_FEP_WS);
964283514Sarybchik	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SD_FES_WS == EFX_LOOPBACK_SD_FES_WS);
965283514Sarybchik
966283514Sarybchik	/* Build bitmask of possible loopback types */
967283514Sarybchik	EFX_ZERO_QWORD(mask);
968283514Sarybchik
969283514Sarybchik	if ((loopback_kind == EFX_LOOPBACK_KIND_OFF) ||
970283514Sarybchik	    (loopback_kind == EFX_LOOPBACK_KIND_ALL)) {
971283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_OFF);
972283514Sarybchik	}
973283514Sarybchik
974283514Sarybchik	if ((loopback_kind == EFX_LOOPBACK_KIND_MAC) ||
975283514Sarybchik	    (loopback_kind == EFX_LOOPBACK_KIND_ALL)) {
976283514Sarybchik		/*
977283514Sarybchik		 * The "MAC" grouping has historically been used by drivers to
978283514Sarybchik		 * mean loopbacks supported by on-chip hardware. Keep that
979283514Sarybchik		 * meaning here, and include on-chip PHY layer loopbacks.
980283514Sarybchik		 */
981283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_DATA);
982283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_GMAC);
983283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XGMII);
984283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XGXS);
985283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XAUI);
986283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_GMII);
987283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_SGMII);
988283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XGBR);
989283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XFI);
990283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XAUI_FAR);
991283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_GMII_FAR);
992283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_SGMII_FAR);
993283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_XFI_FAR);
994283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_PMA_INT);
995283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_SD_NEAR);
996283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_SD_FAR);
997283514Sarybchik	}
998283514Sarybchik
999283514Sarybchik	if ((loopback_kind == EFX_LOOPBACK_KIND_PHY) ||
1000283514Sarybchik	    (loopback_kind == EFX_LOOPBACK_KIND_ALL)) {
1001283514Sarybchik		/*
1002283514Sarybchik		 * The "PHY" grouping has historically been used by drivers to
1003283514Sarybchik		 * mean loopbacks supported by off-chip hardware. Keep that
1004283514Sarybchik		 * meaning here.
1005283514Sarybchik		 */
1006283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_GPHY);
1007283514Sarybchik		EFX_SET_QWORD_BIT(mask,	EFX_LOOPBACK_PHY_XS);
1008283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_PCS);
1009283514Sarybchik		EFX_SET_QWORD_BIT(mask, EFX_LOOPBACK_PMA_PMD);
1010283514Sarybchik	}
1011283514Sarybchik
1012283514Sarybchik	*maskp = mask;
1013283514Sarybchik}
1014283514Sarybchik
1015291436Sarybchik	__checkReturn	efx_rc_t
1016283514Sarybchikefx_mcdi_get_loopback_modes(
1017283514Sarybchik	__in		efx_nic_t *enp)
1018283514Sarybchik{
1019283514Sarybchik	efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
1020283514Sarybchik	efx_mcdi_req_t req;
1021283514Sarybchik	uint8_t payload[MAX(MC_CMD_GET_LOOPBACK_MODES_IN_LEN,
1022283514Sarybchik			    MC_CMD_GET_LOOPBACK_MODES_OUT_LEN)];
1023283514Sarybchik	efx_qword_t mask;
1024283514Sarybchik	efx_qword_t modes;
1025291436Sarybchik	efx_rc_t rc;
1026283514Sarybchik
1027283514Sarybchik	(void) memset(payload, 0, sizeof (payload));
1028283514Sarybchik	req.emr_cmd = MC_CMD_GET_LOOPBACK_MODES;
1029283514Sarybchik	req.emr_in_buf = payload;
1030283514Sarybchik	req.emr_in_length = MC_CMD_GET_LOOPBACK_MODES_IN_LEN;
1031283514Sarybchik	req.emr_out_buf = payload;
1032283514Sarybchik	req.emr_out_length = MC_CMD_GET_LOOPBACK_MODES_OUT_LEN;
1033283514Sarybchik
1034283514Sarybchik	efx_mcdi_execute(enp, &req);
1035283514Sarybchik
1036283514Sarybchik	if (req.emr_rc != 0) {
1037283514Sarybchik		rc = req.emr_rc;
1038283514Sarybchik		goto fail1;
1039283514Sarybchik	}
1040283514Sarybchik
1041283514Sarybchik	if (req.emr_out_length_used <
1042283514Sarybchik	    MC_CMD_GET_LOOPBACK_MODES_OUT_SUGGESTED_OFST +
1043283514Sarybchik	    MC_CMD_GET_LOOPBACK_MODES_OUT_SUGGESTED_LEN) {
1044283514Sarybchik		rc = EMSGSIZE;
1045283514Sarybchik		goto fail2;
1046283514Sarybchik	}
1047283514Sarybchik
1048283514Sarybchik	/*
1049283514Sarybchik	 * We assert the MC_CMD_LOOPBACK and EFX_LOOPBACK namespaces agree
1050283514Sarybchik	 * in efx_loopback_mask() and in siena_phy.c:siena_phy_get_link().
1051283514Sarybchik	 */
1052283514Sarybchik	efx_loopback_mask(EFX_LOOPBACK_KIND_ALL, &mask);
1053283514Sarybchik
1054283514Sarybchik	EFX_AND_QWORD(mask,
1055283514Sarybchik	    *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_SUGGESTED));
1056283514Sarybchik
1057283514Sarybchik	modes = *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_100M);
1058283514Sarybchik	EFX_AND_QWORD(modes, mask);
1059283514Sarybchik	encp->enc_loopback_types[EFX_LINK_100FDX] = modes;
1060283514Sarybchik
1061283514Sarybchik	modes = *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_1G);
1062283514Sarybchik	EFX_AND_QWORD(modes, mask);
1063283514Sarybchik	encp->enc_loopback_types[EFX_LINK_1000FDX] = modes;
1064283514Sarybchik
1065283514Sarybchik	modes = *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_10G);
1066283514Sarybchik	EFX_AND_QWORD(modes, mask);
1067283514Sarybchik	encp->enc_loopback_types[EFX_LINK_10000FDX] = modes;
1068283514Sarybchik
1069283514Sarybchik	if (req.emr_out_length_used >=
1070283514Sarybchik	    MC_CMD_GET_LOOPBACK_MODES_OUT_40G_OFST +
1071283514Sarybchik	    MC_CMD_GET_LOOPBACK_MODES_OUT_40G_LEN) {
1072283514Sarybchik		/* Response includes 40G loopback modes */
1073283514Sarybchik		modes =
1074283514Sarybchik		    *MCDI_OUT2(req, efx_qword_t, GET_LOOPBACK_MODES_OUT_40G);
1075283514Sarybchik		EFX_AND_QWORD(modes, mask);
1076283514Sarybchik		encp->enc_loopback_types[EFX_LINK_40000FDX] = modes;
1077283514Sarybchik	}
1078283514Sarybchik
1079283514Sarybchik	EFX_ZERO_QWORD(modes);
1080283514Sarybchik	EFX_SET_QWORD_BIT(modes, EFX_LOOPBACK_OFF);
1081283514Sarybchik	EFX_OR_QWORD(modes, encp->enc_loopback_types[EFX_LINK_100FDX]);
1082283514Sarybchik	EFX_OR_QWORD(modes, encp->enc_loopback_types[EFX_LINK_1000FDX]);
1083283514Sarybchik	EFX_OR_QWORD(modes, encp->enc_loopback_types[EFX_LINK_10000FDX]);
1084283514Sarybchik	EFX_OR_QWORD(modes, encp->enc_loopback_types[EFX_LINK_40000FDX]);
1085283514Sarybchik	encp->enc_loopback_types[EFX_LINK_UNKNOWN] = modes;
1086283514Sarybchik
1087283514Sarybchik	return (0);
1088283514Sarybchik
1089283514Sarybchikfail2:
1090283514Sarybchik	EFSYS_PROBE(fail2);
1091283514Sarybchikfail1:
1092291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1093283514Sarybchik
1094283514Sarybchik	return (rc);
1095283514Sarybchik}
1096283514Sarybchik
1097283514Sarybchik#endif /* EFSYS_OPT_LOOPBACK */
1098