1/*-
2 * Copyright (c) 2012-2015 Solarflare Communications Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 *    this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 *    this list of conditions and the following disclaimer in the documentation
12 *    and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * The views and conclusions contained in the software and documentation are
27 * those of the authors and should not be interpreted as representing official
28 * policies, either expressed or implied, of the FreeBSD Project.
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD: releng/10.2/sys/dev/sfxge/common/hunt_phy.c 284555 2015-06-18 15:46:39Z arybchik $");
33
34#include "efsys.h"
35#include "efx.h"
36#include "efx_impl.h"
37
38#if EFSYS_OPT_HUNTINGTON
39
40static			void
41hunt_phy_decode_cap(
42	__in		uint32_t mcdi_cap,
43	__out		uint32_t *maskp)
44{
45	/*
46	 * TBD: consider common Siena/Hunt function: Hunt is a superset of
47	 * Siena here (adds 40G)
48	 */
49
50	uint32_t mask;
51
52	mask = 0;
53	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_10HDX_LBN))
54		mask |= (1 << EFX_PHY_CAP_10HDX);
55	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_10FDX_LBN))
56		mask |= (1 << EFX_PHY_CAP_10FDX);
57	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_100HDX_LBN))
58		mask |= (1 << EFX_PHY_CAP_100HDX);
59	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_100FDX_LBN))
60		mask |= (1 << EFX_PHY_CAP_100FDX);
61	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_1000HDX_LBN))
62		mask |= (1 << EFX_PHY_CAP_1000HDX);
63	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN))
64		mask |= (1 << EFX_PHY_CAP_1000FDX);
65	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN))
66		mask |= (1 << EFX_PHY_CAP_10000FDX);
67	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_40000FDX_LBN))
68		mask |= (1 << EFX_PHY_CAP_40000FDX);
69	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_PAUSE_LBN))
70		mask |= (1 << EFX_PHY_CAP_PAUSE);
71	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_ASYM_LBN))
72		mask |= (1 << EFX_PHY_CAP_ASYM);
73	if (mcdi_cap & (1 << MC_CMD_PHY_CAP_AN_LBN))
74		mask |= (1 << EFX_PHY_CAP_AN);
75
76	*maskp = mask;
77}
78
79static			void
80hunt_phy_decode_link_mode(
81	__in		efx_nic_t *enp,
82	__in		uint32_t link_flags,
83	__in		unsigned int speed,
84	__in		unsigned int fcntl,
85	__out		efx_link_mode_t *link_modep,
86	__out		unsigned int *fcntlp)
87{
88	/*
89	 * TBD: consider common Siena/Hunt function: Hunt is a superset of
90	 * Siena here (adds 40G and generate-only flow control)
91	 */
92
93	boolean_t fd = !!(link_flags &
94		    (1 << MC_CMD_GET_LINK_OUT_FULL_DUPLEX_LBN));
95	boolean_t up = !!(link_flags &
96		    (1 << MC_CMD_GET_LINK_OUT_LINK_UP_LBN));
97
98	_NOTE(ARGUNUSED(enp))
99
100	if (!up)
101		*link_modep = EFX_LINK_DOWN;
102	else if (speed == 40000 && fd)
103		*link_modep = EFX_LINK_40000FDX;
104	else if (speed == 10000 && fd)
105		*link_modep = EFX_LINK_10000FDX;
106	else if (speed == 1000)
107		*link_modep = fd ? EFX_LINK_1000FDX : EFX_LINK_1000HDX;
108	else if (speed == 100)
109		*link_modep = fd ? EFX_LINK_100FDX : EFX_LINK_100HDX;
110	else if (speed == 10)
111		*link_modep = fd ? EFX_LINK_10FDX : EFX_LINK_10HDX;
112	else
113		*link_modep = EFX_LINK_UNKNOWN;
114
115	if (fcntl == MC_CMD_FCNTL_OFF)
116		*fcntlp = 0;
117	else if (fcntl == MC_CMD_FCNTL_RESPOND)
118		*fcntlp = EFX_FCNTL_RESPOND;
119	else if (fcntl == MC_CMD_FCNTL_GENERATE)
120		*fcntlp = EFX_FCNTL_GENERATE;
121	else if (fcntl == MC_CMD_FCNTL_BIDIR)
122		*fcntlp = EFX_FCNTL_RESPOND | EFX_FCNTL_GENERATE;
123	else {
124		EFSYS_PROBE1(mc_pcol_error, int, fcntl);
125		*fcntlp = 0;
126	}
127}
128
129
130			void
131hunt_phy_link_ev(
132	__in		efx_nic_t *enp,
133	__in		efx_qword_t *eqp,
134	__out		efx_link_mode_t *link_modep)
135{
136	/*
137	 * TBD: consider common Siena/Hunt function: Hunt is a superset of
138	 * Siena here (adds 40G)
139	 */
140
141	efx_port_t *epp = &(enp->en_port);
142	unsigned int link_flags;
143	unsigned int speed;
144	unsigned int fcntl;
145	efx_link_mode_t link_mode;
146	uint32_t lp_cap_mask;
147
148	/*
149	 * Convert the LINKCHANGE speed enumeration into mbit/s, in the
150	 * same way as GET_LINK encodes the speed
151	 */
152	switch (MCDI_EV_FIELD(eqp, LINKCHANGE_SPEED)) {
153	case MCDI_EVENT_LINKCHANGE_SPEED_100M:
154		speed = 100;
155		break;
156	case MCDI_EVENT_LINKCHANGE_SPEED_1G:
157		speed = 1000;
158		break;
159	case MCDI_EVENT_LINKCHANGE_SPEED_10G:
160		speed = 10000;
161		break;
162	case MCDI_EVENT_LINKCHANGE_SPEED_40G:
163		speed = 40000;
164		break;
165	default:
166		speed = 0;
167		break;
168	}
169
170	link_flags = MCDI_EV_FIELD(eqp, LINKCHANGE_LINK_FLAGS);
171	hunt_phy_decode_link_mode(enp, link_flags, speed,
172				    MCDI_EV_FIELD(eqp, LINKCHANGE_FCNTL),
173				    &link_mode, &fcntl);
174	hunt_phy_decode_cap(MCDI_EV_FIELD(eqp, LINKCHANGE_LP_CAP),
175			    &lp_cap_mask);
176
177	/*
178	 * It's safe to update ep_lp_cap_mask without the driver's port lock
179	 * because presumably any concurrently running efx_port_poll() is
180	 * only going to arrive at the same value.
181	 *
182	 * ep_fcntl has two meanings. It's either the link common fcntl
183	 * (if the PHY supports AN), or it's the forced link state. If
184	 * the former, it's safe to update the value for the same reason as
185	 * for ep_lp_cap_mask. If the latter, then just ignore the value,
186	 * because we can race with efx_mac_fcntl_set().
187	 */
188	epp->ep_lp_cap_mask = lp_cap_mask;
189	epp->ep_fcntl = fcntl;
190
191	*link_modep = link_mode;
192}
193
194	__checkReturn	int
195hunt_phy_power(
196	__in		efx_nic_t *enp,
197	__in		boolean_t power)
198{
199	/* TBD: consider common Siena/Hunt function: essentially identical */
200
201	int rc;
202
203	if (!power)
204		return (0);
205
206	/* Check if the PHY is a zombie */
207	if ((rc = hunt_phy_verify(enp)) != 0)
208		goto fail1;
209
210	enp->en_reset_flags |= EFX_RESET_PHY;
211
212	return (0);
213
214fail1:
215	EFSYS_PROBE1(fail1, int, rc);
216
217	return (rc);
218}
219
220	__checkReturn	int
221hunt_phy_get_link(
222	__in		efx_nic_t *enp,
223	__out		hunt_link_state_t *hlsp)
224{
225	/*
226	 * TBD: consider common Siena/Hunt function: Hunt is very similar
227	 * (at least for now; not clear that the loopbacks should necessarily
228	 * be quite the same...)
229	 */
230
231	efx_mcdi_req_t req;
232	uint8_t payload[MAX(MC_CMD_GET_LINK_IN_LEN,
233			    MC_CMD_GET_LINK_OUT_LEN)];
234	int rc;
235
236	(void) memset(payload, 0, sizeof (payload));
237	req.emr_cmd = MC_CMD_GET_LINK;
238	req.emr_in_buf = payload;
239	req.emr_in_length = MC_CMD_GET_LINK_IN_LEN;
240	req.emr_out_buf = payload;
241	req.emr_out_length = MC_CMD_GET_LINK_OUT_LEN;
242
243	efx_mcdi_execute(enp, &req);
244
245	if (req.emr_rc != 0) {
246		rc = req.emr_rc;
247		goto fail1;
248	}
249
250	if (req.emr_out_length_used < MC_CMD_GET_LINK_OUT_LEN) {
251		rc = EMSGSIZE;
252		goto fail2;
253	}
254
255	hunt_phy_decode_cap(MCDI_OUT_DWORD(req, GET_LINK_OUT_CAP),
256			    &hlsp->hls_adv_cap_mask);
257	hunt_phy_decode_cap(MCDI_OUT_DWORD(req, GET_LINK_OUT_LP_CAP),
258			    &hlsp->hls_lp_cap_mask);
259
260	hunt_phy_decode_link_mode(enp, MCDI_OUT_DWORD(req, GET_LINK_OUT_FLAGS),
261			    MCDI_OUT_DWORD(req, GET_LINK_OUT_LINK_SPEED),
262			    MCDI_OUT_DWORD(req, GET_LINK_OUT_FCNTL),
263			    &hlsp->hls_link_mode, &hlsp->hls_fcntl);
264
265#if EFSYS_OPT_LOOPBACK
266	/* Assert the MC_CMD_LOOPBACK and EFX_LOOPBACK namespace agree */
267	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_NONE == EFX_LOOPBACK_OFF);
268	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_DATA == EFX_LOOPBACK_DATA);
269	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMAC == EFX_LOOPBACK_GMAC);
270	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGMII == EFX_LOOPBACK_XGMII);
271	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGXS == EFX_LOOPBACK_XGXS);
272	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI == EFX_LOOPBACK_XAUI);
273	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMII == EFX_LOOPBACK_GMII);
274	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SGMII == EFX_LOOPBACK_SGMII);
275	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XGBR == EFX_LOOPBACK_XGBR);
276	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XFI == EFX_LOOPBACK_XFI);
277	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XAUI_FAR == EFX_LOOPBACK_XAUI_FAR);
278	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GMII_FAR == EFX_LOOPBACK_GMII_FAR);
279	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_SGMII_FAR == EFX_LOOPBACK_SGMII_FAR);
280	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_XFI_FAR == EFX_LOOPBACK_XFI_FAR);
281	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_GPHY == EFX_LOOPBACK_GPHY);
282	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PHYXS == EFX_LOOPBACK_PHY_XS);
283	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PCS == EFX_LOOPBACK_PCS);
284	EFX_STATIC_ASSERT(MC_CMD_LOOPBACK_PMAPMD == EFX_LOOPBACK_PMA_PMD);
285
286	hlsp->hls_loopback = MCDI_OUT_DWORD(req, GET_LINK_OUT_LOOPBACK_MODE);
287#endif	/* EFSYS_OPT_LOOPBACK */
288
289	hlsp->hls_mac_up = MCDI_OUT_DWORD(req, GET_LINK_OUT_MAC_FAULT) == 0;
290
291	return (0);
292
293fail2:
294	EFSYS_PROBE(fail2);
295fail1:
296	EFSYS_PROBE1(fail1, int, rc);
297
298	return (rc);
299}
300
301	__checkReturn	int
302hunt_phy_reconfigure(
303	__in		efx_nic_t *enp)
304{
305	/*
306	 * TBD: this is a little different for now (no LED support for Hunt
307	 * yet), but ultimately should consider common Siena/Hunt function:
308	 * Hunt should be a superset of Siena here (adds 40G)
309	 */
310
311	efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
312	efx_port_t *epp = &(enp->en_port);
313	efx_mcdi_req_t req;
314	uint8_t payload[MAX(MC_CMD_SET_LINK_IN_LEN,
315			    MC_CMD_SET_LINK_OUT_LEN)];
316	uint32_t cap_mask;
317	unsigned int led_mode;
318	unsigned int speed;
319	int rc;
320
321	if (~encp->enc_func_flags & EFX_NIC_FUNC_LINKCTRL)
322		goto out;
323
324	(void) memset(payload, 0, sizeof (payload));
325	req.emr_cmd = MC_CMD_SET_LINK;
326	req.emr_in_buf = payload;
327	req.emr_in_length = MC_CMD_SET_LINK_IN_LEN;
328	req.emr_out_buf = payload;
329	req.emr_out_length = MC_CMD_SET_LINK_OUT_LEN;
330
331	cap_mask = epp->ep_adv_cap_mask;
332	MCDI_IN_POPULATE_DWORD_10(req, SET_LINK_IN_CAP,
333		PHY_CAP_10HDX, (cap_mask >> EFX_PHY_CAP_10HDX) & 0x1,
334		PHY_CAP_10FDX, (cap_mask >> EFX_PHY_CAP_10FDX) & 0x1,
335		PHY_CAP_100HDX, (cap_mask >> EFX_PHY_CAP_100HDX) & 0x1,
336		PHY_CAP_100FDX, (cap_mask >> EFX_PHY_CAP_100FDX) & 0x1,
337		PHY_CAP_1000HDX, (cap_mask >> EFX_PHY_CAP_1000HDX) & 0x1,
338		PHY_CAP_1000FDX, (cap_mask >> EFX_PHY_CAP_1000FDX) & 0x1,
339		PHY_CAP_10000FDX, (cap_mask >> EFX_PHY_CAP_10000FDX) & 0x1,
340		PHY_CAP_PAUSE, (cap_mask >> EFX_PHY_CAP_PAUSE) & 0x1,
341		PHY_CAP_ASYM, (cap_mask >> EFX_PHY_CAP_ASYM) & 0x1,
342		PHY_CAP_AN, (cap_mask >> EFX_PHY_CAP_AN) & 0x1);
343	/* Too many fields for for POPULATE macros, so insert this afterwards */
344	MCDI_IN_SET_DWORD_FIELD(req, SET_LINK_IN_CAP,
345	    PHY_CAP_40000FDX, (cap_mask >> EFX_PHY_CAP_40000FDX) & 0x1);
346
347#if EFSYS_OPT_LOOPBACK
348	MCDI_IN_SET_DWORD(req, SET_LINK_IN_LOOPBACK_MODE,
349		    epp->ep_loopback_type);
350	switch (epp->ep_loopback_link_mode) {
351	case EFX_LINK_100FDX:
352		speed = 100;
353		break;
354	case EFX_LINK_1000FDX:
355		speed = 1000;
356		break;
357	case EFX_LINK_10000FDX:
358		speed = 10000;
359		break;
360	case EFX_LINK_40000FDX:
361		speed = 40000;
362		break;
363	default:
364		speed = 0;
365	}
366#else
367	MCDI_IN_SET_DWORD(req, SET_LINK_IN_LOOPBACK_MODE, MC_CMD_LOOPBACK_NONE);
368	speed = 0;
369#endif	/* EFSYS_OPT_LOOPBACK */
370	MCDI_IN_SET_DWORD(req, SET_LINK_IN_LOOPBACK_SPEED, speed);
371
372#if EFSYS_OPT_PHY_FLAGS
373	MCDI_IN_SET_DWORD(req, SET_LINK_IN_FLAGS, epp->ep_phy_flags);
374#else
375	MCDI_IN_SET_DWORD(req, SET_LINK_IN_FLAGS, 0);
376#endif	/* EFSYS_OPT_PHY_FLAGS */
377
378	efx_mcdi_execute(enp, &req);
379
380	if (req.emr_rc != 0) {
381		rc = req.emr_rc;
382		goto fail1;
383	}
384
385	/* And set the blink mode */
386	(void) memset(payload, 0, sizeof (payload));
387	req.emr_cmd = MC_CMD_SET_ID_LED;
388	req.emr_in_buf = payload;
389	req.emr_in_length = MC_CMD_SET_ID_LED_IN_LEN;
390	req.emr_out_buf = payload;
391	req.emr_out_length = MC_CMD_SET_ID_LED_OUT_LEN;
392
393#if EFSYS_OPT_PHY_LED_CONTROL
394	switch (epp->ep_phy_led_mode) {
395	case EFX_PHY_LED_DEFAULT:
396		led_mode = MC_CMD_LED_DEFAULT;
397		break;
398	case EFX_PHY_LED_OFF:
399		led_mode = MC_CMD_LED_OFF;
400		break;
401	case EFX_PHY_LED_ON:
402		led_mode = MC_CMD_LED_ON;
403		break;
404	default:
405		EFSYS_ASSERT(0);
406		led_mode = MC_CMD_LED_DEFAULT;
407	}
408
409	MCDI_IN_SET_DWORD(req, SET_ID_LED_IN_STATE, led_mode);
410#else
411	MCDI_IN_SET_DWORD(req, SET_ID_LED_IN_STATE, MC_CMD_LED_DEFAULT);
412#endif	/* EFSYS_OPT_PHY_LED_CONTROL */
413
414	efx_mcdi_execute(enp, &req);
415
416	if (req.emr_rc != 0) {
417		rc = req.emr_rc;
418		goto fail2;
419	}
420out:
421	return (0);
422
423fail2:
424	EFSYS_PROBE(fail2);
425fail1:
426	EFSYS_PROBE1(fail1, int, rc);
427
428	return (rc);
429}
430
431	__checkReturn	int
432hunt_phy_verify(
433	__in		efx_nic_t *enp)
434{
435	/* TBD: consider common Siena/Hunt function: essentially identical */
436
437	efx_mcdi_req_t req;
438	uint8_t payload[MAX(MC_CMD_GET_PHY_STATE_IN_LEN,
439			    MC_CMD_GET_PHY_STATE_OUT_LEN)];
440	uint32_t state;
441	int rc;
442
443	(void) memset(payload, 0, sizeof (payload));
444	req.emr_cmd = MC_CMD_GET_PHY_STATE;
445	req.emr_in_buf = payload;
446	req.emr_in_length = MC_CMD_GET_PHY_STATE_IN_LEN;
447	req.emr_out_buf = payload;
448	req.emr_out_length = MC_CMD_GET_PHY_STATE_OUT_LEN;
449
450	efx_mcdi_execute(enp, &req);
451
452	if (req.emr_rc != 0) {
453		rc = req.emr_rc;
454		goto fail1;
455	}
456
457	if (req.emr_out_length_used < MC_CMD_GET_PHY_STATE_OUT_LEN) {
458		rc = EMSGSIZE;
459		goto fail2;
460	}
461
462	state = MCDI_OUT_DWORD(req, GET_PHY_STATE_OUT_STATE);
463	if (state != MC_CMD_PHY_STATE_OK) {
464		if (state != MC_CMD_PHY_STATE_ZOMBIE)
465			EFSYS_PROBE1(mc_pcol_error, int, state);
466		rc = ENOTACTIVE;
467		goto fail3;
468	}
469
470	return (0);
471
472fail3:
473	EFSYS_PROBE(fail3);
474fail2:
475	EFSYS_PROBE(fail2);
476fail1:
477	EFSYS_PROBE1(fail1, int, rc);
478
479	return (rc);
480}
481
482	__checkReturn	int
483hunt_phy_oui_get(
484	__in		efx_nic_t *enp,
485	__out		uint32_t *ouip)
486{
487	_NOTE(ARGUNUSED(enp, ouip))
488
489	return (ENOTSUP);
490}
491
492#if EFSYS_OPT_PHY_STATS
493
494	__checkReturn				int
495hunt_phy_stats_update(
496	__in					efx_nic_t *enp,
497	__in					efsys_mem_t *esmp,
498	__out_ecount(EFX_PHY_NSTATS)		uint32_t *stat)
499{
500	/* TBD: no stats support in firmware yet */
501	_NOTE(ARGUNUSED(enp, esmp))
502	memset(stat, 0, EFX_PHY_NSTATS * sizeof (*stat));
503
504	return (0);
505}
506
507#endif	/* EFSYS_OPT_PHY_STATS */
508
509#if EFSYS_OPT_PHY_PROPS
510
511#if EFSYS_OPT_NAMES
512
513extern		const char *
514hunt_phy_prop_name(
515	__in	efx_nic_t *enp,
516	__in	unsigned int id)
517{
518	_NOTE(ARGUNUSED(enp, id))
519
520	return (NULL);
521}
522
523#endif	/* EFSYS_OPT_NAMES */
524
525extern	__checkReturn	int
526hunt_phy_prop_get(
527	__in		efx_nic_t *enp,
528	__in		unsigned int id,
529	__in		uint32_t flags,
530	__out		uint32_t *valp)
531{
532	_NOTE(ARGUNUSED(enp, id, flags, valp))
533
534	return (ENOTSUP);
535}
536
537extern	__checkReturn	int
538hunt_phy_prop_set(
539	__in		efx_nic_t *enp,
540	__in		unsigned int id,
541	__in		uint32_t val)
542{
543	_NOTE(ARGUNUSED(enp, id, val))
544
545	return (ENOTSUP);
546}
547
548#endif	/* EFSYS_OPT_PHY_PROPS */
549
550#if EFSYS_OPT_BIST
551
552	__checkReturn		int
553hunt_bist_enable_offline(
554	__in			efx_nic_t *enp)
555{
556	int rc;
557
558	if ((rc = efx_mcdi_bist_enable_offline(enp)) != 0)
559		goto fail1;
560
561	return (0);
562
563fail1:
564	EFSYS_PROBE1(fail1, int, rc);
565
566	return (rc);
567}
568
569	__checkReturn		int
570hunt_bist_start(
571	__in			efx_nic_t *enp,
572	__in			efx_bist_type_t type)
573{
574	int rc;
575
576	if ((rc = efx_mcdi_bist_start(enp, type)) != 0)
577		goto fail1;
578
579	return (0);
580
581fail1:
582	EFSYS_PROBE1(fail1, int, rc);
583
584	return (rc);
585}
586
587	__checkReturn		int
588hunt_bist_poll(
589	__in			efx_nic_t *enp,
590	__in			efx_bist_type_t type,
591	__out			efx_bist_result_t *resultp,
592	__out_opt __drv_when(count > 0, __notnull)
593	uint32_t *value_maskp,
594	__out_ecount_opt(count)	__drv_when(count > 0, __notnull)
595	unsigned long *valuesp,
596	__in			size_t count)
597{
598	efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
599	efx_mcdi_req_t req;
600	uint8_t payload[MAX(MC_CMD_POLL_BIST_IN_LEN,
601			    MCDI_CTL_SDU_LEN_MAX)];
602	uint32_t value_mask = 0;
603	uint32_t result;
604	int rc;
605
606	(void) memset(payload, 0, sizeof (payload));
607	req.emr_cmd = MC_CMD_POLL_BIST;
608	req.emr_in_buf = payload;
609	req.emr_in_length = MC_CMD_POLL_BIST_IN_LEN;
610	req.emr_out_buf = payload;
611	req.emr_out_length = MCDI_CTL_SDU_LEN_MAX;
612
613	efx_mcdi_execute(enp, &req);
614
615	if (req.emr_rc != 0) {
616		rc = req.emr_rc;
617		goto fail1;
618	}
619
620	if (req.emr_out_length_used < MC_CMD_POLL_BIST_OUT_RESULT_OFST + 4) {
621		rc = EMSGSIZE;
622		goto fail2;
623	}
624
625	if (count > 0)
626		(void) memset(valuesp, '\0', count * sizeof (unsigned long));
627
628	result = MCDI_OUT_DWORD(req, POLL_BIST_OUT_RESULT);
629
630	if (result == MC_CMD_POLL_BIST_FAILED &&
631	    req.emr_out_length >= MC_CMD_POLL_BIST_OUT_MEM_LEN &&
632	    count > EFX_BIST_MEM_ECC_FATAL) {
633		if (valuesp != NULL) {
634			valuesp[EFX_BIST_MEM_TEST] =
635			    MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_TEST);
636			valuesp[EFX_BIST_MEM_ADDR] =
637			    MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_ADDR);
638			valuesp[EFX_BIST_MEM_BUS] =
639			    MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_BUS);
640			valuesp[EFX_BIST_MEM_EXPECT] =
641			    MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_EXPECT);
642			valuesp[EFX_BIST_MEM_ACTUAL] =
643			    MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_ACTUAL);
644			valuesp[EFX_BIST_MEM_ECC] =
645			    MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_ECC);
646			valuesp[EFX_BIST_MEM_ECC_PARITY] =
647			    MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_ECC_PARITY);
648			valuesp[EFX_BIST_MEM_ECC_FATAL] =
649			    MCDI_OUT_DWORD(req, POLL_BIST_OUT_MEM_ECC_FATAL);
650		}
651		value_mask |= (1 << EFX_BIST_MEM_TEST) |
652		    (1 << EFX_BIST_MEM_ADDR) |
653		    (1 << EFX_BIST_MEM_BUS) |
654		    (1 << EFX_BIST_MEM_EXPECT) |
655		    (1 << EFX_BIST_MEM_ACTUAL) |
656		    (1 << EFX_BIST_MEM_ECC) |
657		    (1 << EFX_BIST_MEM_ECC_PARITY) |
658		    (1 << EFX_BIST_MEM_ECC_FATAL);
659	} else if (result == MC_CMD_POLL_BIST_FAILED &&
660	    encp->enc_phy_type == EFX_PHY_XFI_FARMI &&
661	    req.emr_out_length >= MC_CMD_POLL_BIST_OUT_MRSFP_LEN &&
662	    count > EFX_BIST_FAULT_CODE) {
663		if (valuesp != NULL)
664			valuesp[EFX_BIST_FAULT_CODE] =
665			    MCDI_OUT_DWORD(req, POLL_BIST_OUT_MRSFP_TEST);
666		value_mask |= 1 << EFX_BIST_FAULT_CODE;
667	}
668
669	if (value_maskp != NULL)
670		*value_maskp = value_mask;
671
672	EFSYS_ASSERT(resultp != NULL);
673	if (result == MC_CMD_POLL_BIST_RUNNING)
674		*resultp = EFX_BIST_RESULT_RUNNING;
675	else if (result == MC_CMD_POLL_BIST_PASSED)
676		*resultp = EFX_BIST_RESULT_PASSED;
677	else
678		*resultp = EFX_BIST_RESULT_FAILED;
679
680	return (0);
681
682fail2:
683	EFSYS_PROBE(fail2);
684fail1:
685	EFSYS_PROBE1(fail1, int, rc);
686
687	return (rc);
688}
689
690			void
691hunt_bist_stop(
692	__in		efx_nic_t *enp,
693	__in		efx_bist_type_t type)
694{
695	/* There is no way to stop BIST on Huntinton. */
696	_NOTE(ARGUNUSED(enp, type))
697}
698
699#endif	/* EFSYS_OPT_BIST */
700
701#endif	/* EFSYS_OPT_HUNTINGTON */
702