1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2008-2016 Solarflare Communications Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 *    this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 *    this list of conditions and the following disclaimer in the documentation
14 *    and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * The views and conclusions contained in the software and documentation are
29 * those of the authors and should not be interpreted as representing official
30 * policies, either expressed or implied, of the FreeBSD Project.
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD$");
35
36#include "efx.h"
37#include "efx_impl.h"
38
39#if EFSYS_OPT_MCDI
40
41/*
42 * There are three versions of the MCDI interface:
43 *  - MCDIv0: Siena BootROM. Transport uses MCDIv1 headers.
44 *  - MCDIv1: Siena firmware and Huntington BootROM.
45 *  - MCDIv2: EF10 firmware (Huntington/Medford) and Medford BootROM.
46 *            Transport uses MCDIv2 headers.
47 *
48 * MCDIv2 Header NOT_EPOCH flag
49 * ----------------------------
50 * A new epoch begins at initial startup or after an MC reboot, and defines when
51 * the MC should reject stale MCDI requests.
52 *
53 * The first MCDI request sent by the host should contain NOT_EPOCH=0, and all
54 * subsequent requests (until the next MC reboot) should contain NOT_EPOCH=1.
55 *
56 * After rebooting the MC will fail all requests with NOT_EPOCH=1 by writing a
57 * response with ERROR=1 and DATALEN=0 until a request is seen with NOT_EPOCH=0.
58 */
59
60
61
62#if EFSYS_OPT_SIENA
63
64static const efx_mcdi_ops_t	__efx_mcdi_siena_ops = {
65	siena_mcdi_init,		/* emco_init */
66	siena_mcdi_send_request,	/* emco_send_request */
67	siena_mcdi_poll_reboot,		/* emco_poll_reboot */
68	siena_mcdi_poll_response,	/* emco_poll_response */
69	siena_mcdi_read_response,	/* emco_read_response */
70	siena_mcdi_fini,		/* emco_fini */
71	siena_mcdi_feature_supported,	/* emco_feature_supported */
72	siena_mcdi_get_timeout,		/* emco_get_timeout */
73};
74
75#endif	/* EFSYS_OPT_SIENA */
76
77#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
78
79static const efx_mcdi_ops_t	__efx_mcdi_ef10_ops = {
80	ef10_mcdi_init,			/* emco_init */
81	ef10_mcdi_send_request,		/* emco_send_request */
82	ef10_mcdi_poll_reboot,		/* emco_poll_reboot */
83	ef10_mcdi_poll_response,	/* emco_poll_response */
84	ef10_mcdi_read_response,	/* emco_read_response */
85	ef10_mcdi_fini,			/* emco_fini */
86	ef10_mcdi_feature_supported,	/* emco_feature_supported */
87	ef10_mcdi_get_timeout,		/* emco_get_timeout */
88};
89
90#endif	/* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
91
92
93
94	__checkReturn	efx_rc_t
95efx_mcdi_init(
96	__in		efx_nic_t *enp,
97	__in		const efx_mcdi_transport_t *emtp)
98{
99	const efx_mcdi_ops_t *emcop;
100	efx_rc_t rc;
101
102	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
103	EFSYS_ASSERT3U(enp->en_mod_flags, ==, 0);
104
105	switch (enp->en_family) {
106#if EFSYS_OPT_SIENA
107	case EFX_FAMILY_SIENA:
108		emcop = &__efx_mcdi_siena_ops;
109		break;
110#endif	/* EFSYS_OPT_SIENA */
111
112#if EFSYS_OPT_HUNTINGTON
113	case EFX_FAMILY_HUNTINGTON:
114		emcop = &__efx_mcdi_ef10_ops;
115		break;
116#endif	/* EFSYS_OPT_HUNTINGTON */
117
118#if EFSYS_OPT_MEDFORD
119	case EFX_FAMILY_MEDFORD:
120		emcop = &__efx_mcdi_ef10_ops;
121		break;
122#endif	/* EFSYS_OPT_MEDFORD */
123
124	default:
125		EFSYS_ASSERT(0);
126		rc = ENOTSUP;
127		goto fail1;
128	}
129
130	if (enp->en_features & EFX_FEATURE_MCDI_DMA) {
131		/* MCDI requires a DMA buffer in host memory */
132		if ((emtp == NULL) || (emtp->emt_dma_mem) == NULL) {
133			rc = EINVAL;
134			goto fail2;
135		}
136	}
137	enp->en_mcdi.em_emtp = emtp;
138
139	if (emcop != NULL && emcop->emco_init != NULL) {
140		if ((rc = emcop->emco_init(enp, emtp)) != 0)
141			goto fail3;
142	}
143
144	enp->en_mcdi.em_emcop = emcop;
145	enp->en_mod_flags |= EFX_MOD_MCDI;
146
147	return (0);
148
149fail3:
150	EFSYS_PROBE(fail3);
151fail2:
152	EFSYS_PROBE(fail2);
153fail1:
154	EFSYS_PROBE1(fail1, efx_rc_t, rc);
155
156	enp->en_mcdi.em_emcop = NULL;
157	enp->en_mcdi.em_emtp = NULL;
158	enp->en_mod_flags &= ~EFX_MOD_MCDI;
159
160	return (rc);
161}
162
163			void
164efx_mcdi_fini(
165	__in		efx_nic_t *enp)
166{
167	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
168	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
169
170	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
171	EFSYS_ASSERT3U(enp->en_mod_flags, ==, EFX_MOD_MCDI);
172
173	if (emcop != NULL && emcop->emco_fini != NULL)
174		emcop->emco_fini(enp);
175
176	emip->emi_port = 0;
177	emip->emi_aborted = 0;
178
179	enp->en_mcdi.em_emcop = NULL;
180	enp->en_mod_flags &= ~EFX_MOD_MCDI;
181}
182
183			void
184efx_mcdi_new_epoch(
185	__in		efx_nic_t *enp)
186{
187	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
188	efsys_lock_state_t state;
189
190	/* Start a new epoch (allow fresh MCDI requests to succeed) */
191	EFSYS_LOCK(enp->en_eslp, state);
192	emip->emi_new_epoch = B_TRUE;
193	EFSYS_UNLOCK(enp->en_eslp, state);
194}
195
196static			void
197efx_mcdi_send_request(
198	__in		efx_nic_t *enp,
199	__in		void *hdrp,
200	__in		size_t hdr_len,
201	__in		void *sdup,
202	__in		size_t sdu_len)
203{
204	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
205
206	emcop->emco_send_request(enp, hdrp, hdr_len, sdup, sdu_len);
207}
208
209static			efx_rc_t
210efx_mcdi_poll_reboot(
211	__in		efx_nic_t *enp)
212{
213	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
214	efx_rc_t rc;
215
216	rc = emcop->emco_poll_reboot(enp);
217	return (rc);
218}
219
220static			boolean_t
221efx_mcdi_poll_response(
222	__in		efx_nic_t *enp)
223{
224	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
225	boolean_t available;
226
227	available = emcop->emco_poll_response(enp);
228	return (available);
229}
230
231static			void
232efx_mcdi_read_response(
233	__in		efx_nic_t *enp,
234	__out		void *bufferp,
235	__in		size_t offset,
236	__in		size_t length)
237{
238	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
239
240	emcop->emco_read_response(enp, bufferp, offset, length);
241}
242
243			void
244efx_mcdi_request_start(
245	__in		efx_nic_t *enp,
246	__in		efx_mcdi_req_t *emrp,
247	__in		boolean_t ev_cpl)
248{
249#if EFSYS_OPT_MCDI_LOGGING
250	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
251#endif
252	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
253	efx_dword_t hdr[2];
254	size_t hdr_len;
255	unsigned int max_version;
256	unsigned int seq;
257	unsigned int xflags;
258	boolean_t new_epoch;
259	efsys_lock_state_t state;
260
261	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
262	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
263	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
264
265	/*
266	 * efx_mcdi_request_start() is naturally serialised against both
267	 * efx_mcdi_request_poll() and efx_mcdi_ev_cpl()/efx_mcdi_ev_death(),
268	 * by virtue of there only being one outstanding MCDI request.
269	 * Unfortunately, upper layers may also call efx_mcdi_request_abort()
270	 * at any time, to timeout a pending mcdi request, That request may
271	 * then subsequently complete, meaning efx_mcdi_ev_cpl() or
272	 * efx_mcdi_ev_death() may end up running in parallel with
273	 * efx_mcdi_request_start(). This race is handled by ensuring that
274	 * %emi_pending_req, %emi_ev_cpl and %emi_seq are protected by the
275	 * en_eslp lock.
276	 */
277	EFSYS_LOCK(enp->en_eslp, state);
278	EFSYS_ASSERT(emip->emi_pending_req == NULL);
279	emip->emi_pending_req = emrp;
280	emip->emi_ev_cpl = ev_cpl;
281	emip->emi_poll_cnt = 0;
282	seq = emip->emi_seq++ & EFX_MASK32(MCDI_HEADER_SEQ);
283	new_epoch = emip->emi_new_epoch;
284	max_version = emip->emi_max_version;
285	EFSYS_UNLOCK(enp->en_eslp, state);
286
287	xflags = 0;
288	if (ev_cpl)
289		xflags |= MCDI_HEADER_XFLAGS_EVREQ;
290
291	/*
292	 * Huntington firmware supports MCDIv2, but the Huntington BootROM only
293	 * supports MCDIv1. Use MCDIv1 headers for MCDIv1 commands where
294	 * possible to support this.
295	 */
296	if ((max_version >= 2) &&
297	    ((emrp->emr_cmd > MC_CMD_CMD_SPACE_ESCAPE_7) ||
298	    (emrp->emr_in_length > MCDI_CTL_SDU_LEN_MAX_V1))) {
299		/* Construct MCDI v2 header */
300		hdr_len = sizeof (hdr);
301		EFX_POPULATE_DWORD_8(hdr[0],
302		    MCDI_HEADER_CODE, MC_CMD_V2_EXTN,
303		    MCDI_HEADER_RESYNC, 1,
304		    MCDI_HEADER_DATALEN, 0,
305		    MCDI_HEADER_SEQ, seq,
306		    MCDI_HEADER_NOT_EPOCH, new_epoch ? 0 : 1,
307		    MCDI_HEADER_ERROR, 0,
308		    MCDI_HEADER_RESPONSE, 0,
309		    MCDI_HEADER_XFLAGS, xflags);
310
311		EFX_POPULATE_DWORD_2(hdr[1],
312		    MC_CMD_V2_EXTN_IN_EXTENDED_CMD, emrp->emr_cmd,
313		    MC_CMD_V2_EXTN_IN_ACTUAL_LEN, emrp->emr_in_length);
314	} else {
315		/* Construct MCDI v1 header */
316		hdr_len = sizeof (hdr[0]);
317		EFX_POPULATE_DWORD_8(hdr[0],
318		    MCDI_HEADER_CODE, emrp->emr_cmd,
319		    MCDI_HEADER_RESYNC, 1,
320		    MCDI_HEADER_DATALEN, emrp->emr_in_length,
321		    MCDI_HEADER_SEQ, seq,
322		    MCDI_HEADER_NOT_EPOCH, new_epoch ? 0 : 1,
323		    MCDI_HEADER_ERROR, 0,
324		    MCDI_HEADER_RESPONSE, 0,
325		    MCDI_HEADER_XFLAGS, xflags);
326	}
327
328#if EFSYS_OPT_MCDI_LOGGING
329	if (emtp->emt_logger != NULL) {
330		emtp->emt_logger(emtp->emt_context, EFX_LOG_MCDI_REQUEST,
331		    &hdr, hdr_len,
332		    emrp->emr_in_buf, emrp->emr_in_length);
333	}
334#endif /* EFSYS_OPT_MCDI_LOGGING */
335
336	efx_mcdi_send_request(enp, &hdr[0], hdr_len,
337	    emrp->emr_in_buf, emrp->emr_in_length);
338}
339
340
341static			void
342efx_mcdi_read_response_header(
343	__in		efx_nic_t *enp,
344	__inout		efx_mcdi_req_t *emrp)
345{
346#if EFSYS_OPT_MCDI_LOGGING
347	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
348#endif /* EFSYS_OPT_MCDI_LOGGING */
349	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
350	efx_dword_t hdr[2];
351	unsigned int hdr_len;
352	unsigned int data_len;
353	unsigned int seq;
354	unsigned int cmd;
355	unsigned int error;
356	efx_rc_t rc;
357
358	EFSYS_ASSERT(emrp != NULL);
359
360	efx_mcdi_read_response(enp, &hdr[0], 0, sizeof (hdr[0]));
361	hdr_len = sizeof (hdr[0]);
362
363	cmd = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_CODE);
364	seq = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_SEQ);
365	error = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_ERROR);
366
367	if (cmd != MC_CMD_V2_EXTN) {
368		data_len = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_DATALEN);
369	} else {
370		efx_mcdi_read_response(enp, &hdr[1], hdr_len, sizeof (hdr[1]));
371		hdr_len += sizeof (hdr[1]);
372
373		cmd = EFX_DWORD_FIELD(hdr[1], MC_CMD_V2_EXTN_IN_EXTENDED_CMD);
374		data_len =
375		    EFX_DWORD_FIELD(hdr[1], MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
376	}
377
378	if (error && (data_len == 0)) {
379		/* The MC has rebooted since the request was sent. */
380		EFSYS_SPIN(EFX_MCDI_STATUS_SLEEP_US);
381		efx_mcdi_poll_reboot(enp);
382		rc = EIO;
383		goto fail1;
384	}
385	if ((cmd != emrp->emr_cmd) ||
386	    (seq != ((emip->emi_seq - 1) & EFX_MASK32(MCDI_HEADER_SEQ)))) {
387		/* Response is for a different request */
388		rc = EIO;
389		goto fail2;
390	}
391	if (error) {
392		efx_dword_t err[2];
393		unsigned int err_len = MIN(data_len, sizeof (err));
394		int err_code = MC_CMD_ERR_EPROTO;
395		int err_arg = 0;
396
397		/* Read error code (and arg num for MCDI v2 commands) */
398		efx_mcdi_read_response(enp, &err, hdr_len, err_len);
399
400		if (err_len >= (MC_CMD_ERR_CODE_OFST + sizeof (efx_dword_t)))
401			err_code = EFX_DWORD_FIELD(err[0], EFX_DWORD_0);
402#ifdef WITH_MCDI_V2
403		if (err_len >= (MC_CMD_ERR_ARG_OFST + sizeof (efx_dword_t)))
404			err_arg = EFX_DWORD_FIELD(err[1], EFX_DWORD_0);
405#endif
406		emrp->emr_err_code = err_code;
407		emrp->emr_err_arg = err_arg;
408
409#if EFSYS_OPT_MCDI_PROXY_AUTH
410		if ((err_code == MC_CMD_ERR_PROXY_PENDING) &&
411		    (err_len == sizeof (err))) {
412			/*
413			 * The MCDI request would normally fail with EPERM, but
414			 * firmware has forwarded it to an authorization agent
415			 * attached to a privileged PF.
416			 *
417			 * Save the authorization request handle. The client
418			 * must wait for a PROXY_RESPONSE event, or timeout.
419			 */
420			emrp->emr_proxy_handle = err_arg;
421		}
422#endif /* EFSYS_OPT_MCDI_PROXY_AUTH */
423
424#if EFSYS_OPT_MCDI_LOGGING
425		if (emtp->emt_logger != NULL) {
426			emtp->emt_logger(emtp->emt_context,
427			    EFX_LOG_MCDI_RESPONSE,
428			    &hdr, hdr_len,
429			    &err, err_len);
430		}
431#endif /* EFSYS_OPT_MCDI_LOGGING */
432
433		if (!emrp->emr_quiet) {
434			EFSYS_PROBE3(mcdi_err_arg, int, emrp->emr_cmd,
435			    int, err_code, int, err_arg);
436		}
437
438		rc = efx_mcdi_request_errcode(err_code);
439		goto fail3;
440	}
441
442	emrp->emr_rc = 0;
443	emrp->emr_out_length_used = data_len;
444#if EFSYS_OPT_MCDI_PROXY_AUTH
445	emrp->emr_proxy_handle = 0;
446#endif /* EFSYS_OPT_MCDI_PROXY_AUTH */
447	return;
448
449fail3:
450fail2:
451fail1:
452	emrp->emr_rc = rc;
453	emrp->emr_out_length_used = 0;
454}
455
456static			void
457efx_mcdi_finish_response(
458	__in		efx_nic_t *enp,
459	__in		efx_mcdi_req_t *emrp)
460{
461#if EFSYS_OPT_MCDI_LOGGING
462	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
463#endif /* EFSYS_OPT_MCDI_LOGGING */
464	efx_dword_t hdr[2];
465	unsigned int hdr_len;
466	size_t bytes;
467
468	if (emrp->emr_out_buf == NULL)
469		return;
470
471	/* Read the command header to detect MCDI response format */
472	hdr_len = sizeof (hdr[0]);
473	efx_mcdi_read_response(enp, &hdr[0], 0, hdr_len);
474	if (EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_CODE) == MC_CMD_V2_EXTN) {
475		/*
476		 * Read the actual payload length. The length given in the event
477		 * is only correct for responses with the V1 format.
478		 */
479		efx_mcdi_read_response(enp, &hdr[1], hdr_len, sizeof (hdr[1]));
480		hdr_len += sizeof (hdr[1]);
481
482		emrp->emr_out_length_used = EFX_DWORD_FIELD(hdr[1],
483					    MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
484	}
485
486	/* Copy payload out into caller supplied buffer */
487	bytes = MIN(emrp->emr_out_length_used, emrp->emr_out_length);
488	efx_mcdi_read_response(enp, emrp->emr_out_buf, hdr_len, bytes);
489
490#if EFSYS_OPT_MCDI_LOGGING
491	if (emtp->emt_logger != NULL) {
492		emtp->emt_logger(emtp->emt_context,
493		    EFX_LOG_MCDI_RESPONSE,
494		    &hdr, hdr_len,
495		    emrp->emr_out_buf, bytes);
496	}
497#endif /* EFSYS_OPT_MCDI_LOGGING */
498}
499
500
501	__checkReturn	boolean_t
502efx_mcdi_request_poll(
503	__in		efx_nic_t *enp)
504{
505	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
506	efx_mcdi_req_t *emrp;
507	efsys_lock_state_t state;
508	efx_rc_t rc;
509
510	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
511	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
512	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
513
514	/* Serialise against post-watchdog efx_mcdi_ev* */
515	EFSYS_LOCK(enp->en_eslp, state);
516
517	EFSYS_ASSERT(emip->emi_pending_req != NULL);
518	EFSYS_ASSERT(!emip->emi_ev_cpl);
519	emrp = emip->emi_pending_req;
520
521	/* Check for reboot atomically w.r.t efx_mcdi_request_start */
522	if (emip->emi_poll_cnt++ == 0) {
523		if ((rc = efx_mcdi_poll_reboot(enp)) != 0) {
524			emip->emi_pending_req = NULL;
525			EFSYS_UNLOCK(enp->en_eslp, state);
526
527			/* Reboot/Assertion */
528			if (rc == EIO || rc == EINTR)
529				efx_mcdi_raise_exception(enp, emrp, rc);
530
531			goto fail1;
532		}
533	}
534
535	/* Check if a response is available */
536	if (efx_mcdi_poll_response(enp) == B_FALSE) {
537		EFSYS_UNLOCK(enp->en_eslp, state);
538		return (B_FALSE);
539	}
540
541	/* Read the response header */
542	efx_mcdi_read_response_header(enp, emrp);
543
544	/* Request complete */
545	emip->emi_pending_req = NULL;
546
547	/* Ensure stale MCDI requests fail after an MC reboot. */
548	emip->emi_new_epoch = B_FALSE;
549
550	EFSYS_UNLOCK(enp->en_eslp, state);
551
552	if ((rc = emrp->emr_rc) != 0)
553		goto fail2;
554
555	efx_mcdi_finish_response(enp, emrp);
556	return (B_TRUE);
557
558fail2:
559	if (!emrp->emr_quiet)
560		EFSYS_PROBE(fail2);
561fail1:
562	if (!emrp->emr_quiet)
563		EFSYS_PROBE1(fail1, efx_rc_t, rc);
564
565	return (B_TRUE);
566}
567
568	__checkReturn	boolean_t
569efx_mcdi_request_abort(
570	__in		efx_nic_t *enp)
571{
572	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
573	efx_mcdi_req_t *emrp;
574	boolean_t aborted;
575	efsys_lock_state_t state;
576
577	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
578	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
579	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
580
581	/*
582	 * efx_mcdi_ev_* may have already completed this event, and be
583	 * spinning/blocked on the upper layer lock. So it *is* legitimate
584	 * to for emi_pending_req to be NULL. If there is a pending event
585	 * completed request, then provide a "credit" to allow
586	 * efx_mcdi_ev_cpl() to accept a single spurious completion.
587	 */
588	EFSYS_LOCK(enp->en_eslp, state);
589	emrp = emip->emi_pending_req;
590	aborted = (emrp != NULL);
591	if (aborted) {
592		emip->emi_pending_req = NULL;
593
594		/* Error the request */
595		emrp->emr_out_length_used = 0;
596		emrp->emr_rc = ETIMEDOUT;
597
598		/* Provide a credit for seqno/emr_pending_req mismatches */
599		if (emip->emi_ev_cpl)
600			++emip->emi_aborted;
601
602		/*
603		 * The upper layer has called us, so we don't
604		 * need to complete the request.
605		 */
606	}
607	EFSYS_UNLOCK(enp->en_eslp, state);
608
609	return (aborted);
610}
611
612			void
613efx_mcdi_get_timeout(
614	__in		efx_nic_t *enp,
615	__in		efx_mcdi_req_t *emrp,
616	__out		uint32_t *timeoutp)
617{
618	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
619
620	emcop->emco_get_timeout(enp, emrp, timeoutp);
621}
622
623	__checkReturn	efx_rc_t
624efx_mcdi_request_errcode(
625	__in		unsigned int err)
626{
627
628	switch (err) {
629		/* MCDI v1 */
630	case MC_CMD_ERR_EPERM:
631		return (EACCES);
632	case MC_CMD_ERR_ENOENT:
633		return (ENOENT);
634	case MC_CMD_ERR_EINTR:
635		return (EINTR);
636	case MC_CMD_ERR_EACCES:
637		return (EACCES);
638	case MC_CMD_ERR_EBUSY:
639		return (EBUSY);
640	case MC_CMD_ERR_EINVAL:
641		return (EINVAL);
642	case MC_CMD_ERR_EDEADLK:
643		return (EDEADLK);
644	case MC_CMD_ERR_ENOSYS:
645		return (ENOTSUP);
646	case MC_CMD_ERR_ETIME:
647		return (ETIMEDOUT);
648	case MC_CMD_ERR_ENOTSUP:
649		return (ENOTSUP);
650	case MC_CMD_ERR_EALREADY:
651		return (EALREADY);
652
653		/* MCDI v2 */
654	case MC_CMD_ERR_EEXIST:
655		return (EEXIST);
656#ifdef MC_CMD_ERR_EAGAIN
657	case MC_CMD_ERR_EAGAIN:
658		return (EAGAIN);
659#endif
660#ifdef MC_CMD_ERR_ENOSPC
661	case MC_CMD_ERR_ENOSPC:
662		return (ENOSPC);
663#endif
664	case MC_CMD_ERR_ERANGE:
665		return (ERANGE);
666
667	case MC_CMD_ERR_ALLOC_FAIL:
668		return (ENOMEM);
669	case MC_CMD_ERR_NO_VADAPTOR:
670		return (ENOENT);
671	case MC_CMD_ERR_NO_EVB_PORT:
672		return (ENOENT);
673	case MC_CMD_ERR_NO_VSWITCH:
674		return (ENODEV);
675	case MC_CMD_ERR_VLAN_LIMIT:
676		return (EINVAL);
677	case MC_CMD_ERR_BAD_PCI_FUNC:
678		return (ENODEV);
679	case MC_CMD_ERR_BAD_VLAN_MODE:
680		return (EINVAL);
681	case MC_CMD_ERR_BAD_VSWITCH_TYPE:
682		return (EINVAL);
683	case MC_CMD_ERR_BAD_VPORT_TYPE:
684		return (EINVAL);
685	case MC_CMD_ERR_MAC_EXIST:
686		return (EEXIST);
687
688	case MC_CMD_ERR_PROXY_PENDING:
689		return (EAGAIN);
690
691	default:
692		EFSYS_PROBE1(mc_pcol_error, int, err);
693		return (EIO);
694	}
695}
696
697			void
698efx_mcdi_raise_exception(
699	__in		efx_nic_t *enp,
700	__in_opt	efx_mcdi_req_t *emrp,
701	__in		int rc)
702{
703	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
704	efx_mcdi_exception_t exception;
705
706	/* Reboot or Assertion failure only */
707	EFSYS_ASSERT(rc == EIO || rc == EINTR);
708
709	/*
710	 * If MC_CMD_REBOOT causes a reboot (dependent on parameters),
711	 * then the EIO is not worthy of an exception.
712	 */
713	if (emrp != NULL && emrp->emr_cmd == MC_CMD_REBOOT && rc == EIO)
714		return;
715
716	exception = (rc == EIO)
717		? EFX_MCDI_EXCEPTION_MC_REBOOT
718		: EFX_MCDI_EXCEPTION_MC_BADASSERT;
719
720	emtp->emt_exception(emtp->emt_context, exception);
721}
722
723			void
724efx_mcdi_execute(
725	__in		efx_nic_t *enp,
726	__inout		efx_mcdi_req_t *emrp)
727{
728	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
729
730	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
731	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
732
733	emrp->emr_quiet = B_FALSE;
734	emtp->emt_execute(emtp->emt_context, emrp);
735}
736
737			void
738efx_mcdi_execute_quiet(
739	__in		efx_nic_t *enp,
740	__inout		efx_mcdi_req_t *emrp)
741{
742	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
743
744	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
745	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
746
747	emrp->emr_quiet = B_TRUE;
748	emtp->emt_execute(emtp->emt_context, emrp);
749}
750
751			void
752efx_mcdi_ev_cpl(
753	__in		efx_nic_t *enp,
754	__in		unsigned int seq,
755	__in		unsigned int outlen,
756	__in		int errcode)
757{
758	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
759	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
760	efx_mcdi_req_t *emrp;
761	efsys_lock_state_t state;
762
763	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
764	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
765
766	/*
767	 * Serialise against efx_mcdi_request_poll()/efx_mcdi_request_start()
768	 * when we're completing an aborted request.
769	 */
770	EFSYS_LOCK(enp->en_eslp, state);
771	if (emip->emi_pending_req == NULL || !emip->emi_ev_cpl ||
772	    (seq != ((emip->emi_seq - 1) & EFX_MASK32(MCDI_HEADER_SEQ)))) {
773		EFSYS_ASSERT(emip->emi_aborted > 0);
774		if (emip->emi_aborted > 0)
775			--emip->emi_aborted;
776		EFSYS_UNLOCK(enp->en_eslp, state);
777		return;
778	}
779
780	emrp = emip->emi_pending_req;
781	emip->emi_pending_req = NULL;
782	EFSYS_UNLOCK(enp->en_eslp, state);
783
784	if (emip->emi_max_version >= 2) {
785		/* MCDIv2 response details do not fit into an event. */
786		efx_mcdi_read_response_header(enp, emrp);
787	} else {
788		if (errcode != 0) {
789			if (!emrp->emr_quiet) {
790				EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd,
791				    int, errcode);
792			}
793			emrp->emr_out_length_used = 0;
794			emrp->emr_rc = efx_mcdi_request_errcode(errcode);
795		} else {
796			emrp->emr_out_length_used = outlen;
797			emrp->emr_rc = 0;
798		}
799	}
800	if (errcode == 0) {
801		efx_mcdi_finish_response(enp, emrp);
802	}
803
804	emtp->emt_ev_cpl(emtp->emt_context);
805}
806
807#if EFSYS_OPT_MCDI_PROXY_AUTH
808
809	__checkReturn	efx_rc_t
810efx_mcdi_get_proxy_handle(
811	__in		efx_nic_t *enp,
812	__in		efx_mcdi_req_t *emrp,
813	__out		uint32_t *handlep)
814{
815	efx_rc_t rc;
816
817	_NOTE(ARGUNUSED(enp))
818
819	/*
820	 * Return proxy handle from MCDI request that returned with error
821	 * MC_MCD_ERR_PROXY_PENDING. This handle is used to wait for a matching
822	 * PROXY_RESPONSE event.
823	 */
824	if ((emrp == NULL) || (handlep == NULL)) {
825		rc = EINVAL;
826		goto fail1;
827	}
828	if ((emrp->emr_rc != 0) &&
829	    (emrp->emr_err_code == MC_CMD_ERR_PROXY_PENDING)) {
830		*handlep = emrp->emr_proxy_handle;
831		rc = 0;
832	} else {
833		*handlep = 0;
834		rc = ENOENT;
835	}
836	return (rc);
837
838fail1:
839	EFSYS_PROBE1(fail1, efx_rc_t, rc);
840	return (rc);
841}
842
843			void
844efx_mcdi_ev_proxy_response(
845	__in		efx_nic_t *enp,
846	__in		unsigned int handle,
847	__in		unsigned int status)
848{
849	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
850	efx_rc_t rc;
851
852	/*
853	 * Handle results of an authorization request for a privileged MCDI
854	 * command. If authorization was granted then we must re-issue the
855	 * original MCDI request. If authorization failed or timed out,
856	 * then the original MCDI request should be completed with the
857	 * result code from this event.
858	 */
859	rc = (status == 0) ? 0 : efx_mcdi_request_errcode(status);
860
861	emtp->emt_ev_proxy_response(emtp->emt_context, handle, rc);
862}
863#endif /* EFSYS_OPT_MCDI_PROXY_AUTH */
864
865			void
866efx_mcdi_ev_death(
867	__in		efx_nic_t *enp,
868	__in		int rc)
869{
870	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
871	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
872	efx_mcdi_req_t *emrp = NULL;
873	boolean_t ev_cpl;
874	efsys_lock_state_t state;
875
876	/*
877	 * The MCDI request (if there is one) has been terminated, either
878	 * by a BADASSERT or REBOOT event.
879	 *
880	 * If there is an outstanding event-completed MCDI operation, then we
881	 * will never receive the completion event (because both MCDI
882	 * completions and BADASSERT events are sent to the same evq). So
883	 * complete this MCDI op.
884	 *
885	 * This function might run in parallel with efx_mcdi_request_poll()
886	 * for poll completed mcdi requests, and also with
887	 * efx_mcdi_request_start() for post-watchdog completions.
888	 */
889	EFSYS_LOCK(enp->en_eslp, state);
890	emrp = emip->emi_pending_req;
891	ev_cpl = emip->emi_ev_cpl;
892	if (emrp != NULL && emip->emi_ev_cpl) {
893		emip->emi_pending_req = NULL;
894
895		emrp->emr_out_length_used = 0;
896		emrp->emr_rc = rc;
897		++emip->emi_aborted;
898	}
899
900	/*
901	 * Since we're running in parallel with a request, consume the
902	 * status word before dropping the lock.
903	 */
904	if (rc == EIO || rc == EINTR) {
905		EFSYS_SPIN(EFX_MCDI_STATUS_SLEEP_US);
906		(void) efx_mcdi_poll_reboot(enp);
907		emip->emi_new_epoch = B_TRUE;
908	}
909
910	EFSYS_UNLOCK(enp->en_eslp, state);
911
912	efx_mcdi_raise_exception(enp, emrp, rc);
913
914	if (emrp != NULL && ev_cpl)
915		emtp->emt_ev_cpl(emtp->emt_context);
916}
917
918	__checkReturn		efx_rc_t
919efx_mcdi_version(
920	__in			efx_nic_t *enp,
921	__out_ecount_opt(4)	uint16_t versionp[4],
922	__out_opt		uint32_t *buildp,
923	__out_opt		efx_mcdi_boot_t *statusp)
924{
925	efx_mcdi_req_t req;
926	EFX_MCDI_DECLARE_BUF(payload,
927		MAX(MC_CMD_GET_VERSION_IN_LEN, MC_CMD_GET_BOOT_STATUS_IN_LEN),
928		MAX(MC_CMD_GET_VERSION_OUT_LEN,
929			MC_CMD_GET_BOOT_STATUS_OUT_LEN));
930	efx_word_t *ver_words;
931	uint16_t version[4];
932	uint32_t build;
933	efx_mcdi_boot_t status;
934	efx_rc_t rc;
935
936	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
937
938	req.emr_cmd = MC_CMD_GET_VERSION;
939	req.emr_in_buf = payload;
940	req.emr_in_length = MC_CMD_GET_VERSION_IN_LEN;
941	req.emr_out_buf = payload;
942	req.emr_out_length = MC_CMD_GET_VERSION_OUT_LEN;
943
944	efx_mcdi_execute(enp, &req);
945
946	if (req.emr_rc != 0) {
947		rc = req.emr_rc;
948		goto fail1;
949	}
950
951	/* bootrom support */
952	if (req.emr_out_length_used == MC_CMD_GET_VERSION_V0_OUT_LEN) {
953		version[0] = version[1] = version[2] = version[3] = 0;
954		build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE);
955
956		goto version;
957	}
958
959	if (req.emr_out_length_used < MC_CMD_GET_VERSION_OUT_LEN) {
960		rc = EMSGSIZE;
961		goto fail2;
962	}
963
964	ver_words = MCDI_OUT2(req, efx_word_t, GET_VERSION_OUT_VERSION);
965	version[0] = EFX_WORD_FIELD(ver_words[0], EFX_WORD_0);
966	version[1] = EFX_WORD_FIELD(ver_words[1], EFX_WORD_0);
967	version[2] = EFX_WORD_FIELD(ver_words[2], EFX_WORD_0);
968	version[3] = EFX_WORD_FIELD(ver_words[3], EFX_WORD_0);
969	build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE);
970
971version:
972	/* The bootrom doesn't understand BOOT_STATUS */
973	if (MC_FW_VERSION_IS_BOOTLOADER(build)) {
974		status = EFX_MCDI_BOOT_ROM;
975		goto out;
976	}
977
978	(void) memset(payload, 0, sizeof (payload));
979	req.emr_cmd = MC_CMD_GET_BOOT_STATUS;
980	req.emr_in_buf = payload;
981	req.emr_in_length = MC_CMD_GET_BOOT_STATUS_IN_LEN;
982	req.emr_out_buf = payload;
983	req.emr_out_length = MC_CMD_GET_BOOT_STATUS_OUT_LEN;
984
985	efx_mcdi_execute_quiet(enp, &req);
986
987	if (req.emr_rc == EACCES) {
988		/* Unprivileged functions cannot access BOOT_STATUS */
989		status = EFX_MCDI_BOOT_PRIMARY;
990		version[0] = version[1] = version[2] = version[3] = 0;
991		build = 0;
992		goto out;
993	}
994
995	if (req.emr_rc != 0) {
996		rc = req.emr_rc;
997		goto fail3;
998	}
999
1000	if (req.emr_out_length_used < MC_CMD_GET_BOOT_STATUS_OUT_LEN) {
1001		rc = EMSGSIZE;
1002		goto fail4;
1003	}
1004
1005	if (MCDI_OUT_DWORD_FIELD(req, GET_BOOT_STATUS_OUT_FLAGS,
1006	    GET_BOOT_STATUS_OUT_FLAGS_PRIMARY))
1007		status = EFX_MCDI_BOOT_PRIMARY;
1008	else
1009		status = EFX_MCDI_BOOT_SECONDARY;
1010
1011out:
1012	if (versionp != NULL)
1013		memcpy(versionp, version, sizeof (version));
1014	if (buildp != NULL)
1015		*buildp = build;
1016	if (statusp != NULL)
1017		*statusp = status;
1018
1019	return (0);
1020
1021fail4:
1022	EFSYS_PROBE(fail4);
1023fail3:
1024	EFSYS_PROBE(fail3);
1025fail2:
1026	EFSYS_PROBE(fail2);
1027fail1:
1028	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1029
1030	return (rc);
1031}
1032
1033static	__checkReturn	efx_rc_t
1034efx_mcdi_do_reboot(
1035	__in		efx_nic_t *enp,
1036	__in		boolean_t after_assertion)
1037{
1038	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_REBOOT_IN_LEN,
1039		MC_CMD_REBOOT_OUT_LEN);
1040	efx_mcdi_req_t req;
1041	efx_rc_t rc;
1042
1043	/*
1044	 * We could require the caller to have caused en_mod_flags=0 to
1045	 * call this function. This doesn't help the other port though,
1046	 * who's about to get the MC ripped out from underneath them.
1047	 * Since they have to cope with the subsequent fallout of MCDI
1048	 * failures, we should as well.
1049	 */
1050	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
1051
1052	req.emr_cmd = MC_CMD_REBOOT;
1053	req.emr_in_buf = payload;
1054	req.emr_in_length = MC_CMD_REBOOT_IN_LEN;
1055	req.emr_out_buf = payload;
1056	req.emr_out_length = MC_CMD_REBOOT_OUT_LEN;
1057
1058	MCDI_IN_SET_DWORD(req, REBOOT_IN_FLAGS,
1059	    (after_assertion ? MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION : 0));
1060
1061	efx_mcdi_execute_quiet(enp, &req);
1062
1063	if (req.emr_rc == EACCES) {
1064		/* Unprivileged functions cannot reboot the MC. */
1065		goto out;
1066	}
1067
1068	/* A successful reboot request returns EIO. */
1069	if (req.emr_rc != 0 && req.emr_rc != EIO) {
1070		rc = req.emr_rc;
1071		goto fail1;
1072	}
1073
1074out:
1075	return (0);
1076
1077fail1:
1078	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1079
1080	return (rc);
1081}
1082
1083	__checkReturn	efx_rc_t
1084efx_mcdi_reboot(
1085	__in		efx_nic_t *enp)
1086{
1087	return (efx_mcdi_do_reboot(enp, B_FALSE));
1088}
1089
1090	__checkReturn	efx_rc_t
1091efx_mcdi_exit_assertion_handler(
1092	__in		efx_nic_t *enp)
1093{
1094	return (efx_mcdi_do_reboot(enp, B_TRUE));
1095}
1096
1097	__checkReturn	efx_rc_t
1098efx_mcdi_read_assertion(
1099	__in		efx_nic_t *enp)
1100{
1101	efx_mcdi_req_t req;
1102	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_ASSERTS_IN_LEN,
1103		MC_CMD_GET_ASSERTS_OUT_LEN);
1104	const char *reason;
1105	unsigned int flags;
1106	unsigned int index;
1107	unsigned int ofst;
1108	int retry;
1109	efx_rc_t rc;
1110
1111	/*
1112	 * Before we attempt to chat to the MC, we should verify that the MC
1113	 * isn't in its assertion handler, either due to a previous reboot,
1114	 * or because we're reinitializing due to an eec_exception().
1115	 *
1116	 * Use GET_ASSERTS to read any assertion state that may be present.
1117	 * Retry this command twice. Once because a boot-time assertion failure
1118	 * might cause the 1st MCDI request to fail. And once again because
1119	 * we might race with efx_mcdi_exit_assertion_handler() running on
1120	 * partner port(s) on the same NIC.
1121	 */
1122	retry = 2;
1123	do {
1124		(void) memset(payload, 0, sizeof (payload));
1125		req.emr_cmd = MC_CMD_GET_ASSERTS;
1126		req.emr_in_buf = payload;
1127		req.emr_in_length = MC_CMD_GET_ASSERTS_IN_LEN;
1128		req.emr_out_buf = payload;
1129		req.emr_out_length = MC_CMD_GET_ASSERTS_OUT_LEN;
1130
1131		MCDI_IN_SET_DWORD(req, GET_ASSERTS_IN_CLEAR, 1);
1132		efx_mcdi_execute_quiet(enp, &req);
1133
1134	} while ((req.emr_rc == EINTR || req.emr_rc == EIO) && retry-- > 0);
1135
1136	if (req.emr_rc != 0) {
1137		if (req.emr_rc == EACCES) {
1138			/* Unprivileged functions cannot clear assertions. */
1139			goto out;
1140		}
1141		rc = req.emr_rc;
1142		goto fail1;
1143	}
1144
1145	if (req.emr_out_length_used < MC_CMD_GET_ASSERTS_OUT_LEN) {
1146		rc = EMSGSIZE;
1147		goto fail2;
1148	}
1149
1150	/* Print out any assertion state recorded */
1151	flags = MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_GLOBAL_FLAGS);
1152	if (flags == MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS)
1153		return (0);
1154
1155	reason = (flags == MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL)
1156		? "system-level assertion"
1157		: (flags == MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL)
1158		? "thread-level assertion"
1159		: (flags == MC_CMD_GET_ASSERTS_FLAGS_WDOG_FIRED)
1160		? "watchdog reset"
1161		: (flags == MC_CMD_GET_ASSERTS_FLAGS_ADDR_TRAP)
1162		? "illegal address trap"
1163		: "unknown assertion";
1164	EFSYS_PROBE3(mcpu_assertion,
1165	    const char *, reason, unsigned int,
1166	    MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_SAVED_PC_OFFS),
1167	    unsigned int,
1168	    MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_THREAD_OFFS));
1169
1170	/* Print out the registers (r1 ... r31) */
1171	ofst = MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST;
1172	for (index = 1;
1173		index < 1 + MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_NUM;
1174		index++) {
1175		EFSYS_PROBE2(mcpu_register, unsigned int, index, unsigned int,
1176			    EFX_DWORD_FIELD(*MCDI_OUT(req, efx_dword_t, ofst),
1177					    EFX_DWORD_0));
1178		ofst += sizeof (efx_dword_t);
1179	}
1180	EFSYS_ASSERT(ofst <= MC_CMD_GET_ASSERTS_OUT_LEN);
1181
1182out:
1183	return (0);
1184
1185fail2:
1186	EFSYS_PROBE(fail2);
1187fail1:
1188	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1189
1190	return (rc);
1191}
1192
1193
1194/*
1195 * Internal routines for for specific MCDI requests.
1196 */
1197
1198	__checkReturn	efx_rc_t
1199efx_mcdi_drv_attach(
1200	__in		efx_nic_t *enp,
1201	__in		boolean_t attach)
1202{
1203	efx_mcdi_req_t req;
1204	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_DRV_ATTACH_IN_LEN,
1205		MC_CMD_DRV_ATTACH_EXT_OUT_LEN);
1206	efx_rc_t rc;
1207
1208	req.emr_cmd = MC_CMD_DRV_ATTACH;
1209	req.emr_in_buf = payload;
1210	req.emr_in_length = MC_CMD_DRV_ATTACH_IN_LEN;
1211	req.emr_out_buf = payload;
1212	req.emr_out_length = MC_CMD_DRV_ATTACH_EXT_OUT_LEN;
1213
1214	/*
1215	 * Use DONT_CARE for the datapath firmware type to ensure that the
1216	 * driver can attach to an unprivileged function. The datapath firmware
1217	 * type to use is controlled by the 'sfboot' utility.
1218	 */
1219	MCDI_IN_SET_DWORD(req, DRV_ATTACH_IN_NEW_STATE, attach ? 1 : 0);
1220	MCDI_IN_SET_DWORD(req, DRV_ATTACH_IN_UPDATE, 1);
1221	MCDI_IN_SET_DWORD(req, DRV_ATTACH_IN_FIRMWARE_ID, MC_CMD_FW_DONT_CARE);
1222
1223	efx_mcdi_execute(enp, &req);
1224
1225	if (req.emr_rc != 0) {
1226		rc = req.emr_rc;
1227		goto fail1;
1228	}
1229
1230	if (req.emr_out_length_used < MC_CMD_DRV_ATTACH_OUT_LEN) {
1231		rc = EMSGSIZE;
1232		goto fail2;
1233	}
1234
1235	return (0);
1236
1237fail2:
1238	EFSYS_PROBE(fail2);
1239fail1:
1240	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1241
1242	return (rc);
1243}
1244
1245	__checkReturn		efx_rc_t
1246efx_mcdi_get_board_cfg(
1247	__in			efx_nic_t *enp,
1248	__out_opt		uint32_t *board_typep,
1249	__out_opt		efx_dword_t *capabilitiesp,
1250	__out_ecount_opt(6)	uint8_t mac_addrp[6])
1251{
1252	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1253	efx_mcdi_req_t req;
1254	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_BOARD_CFG_IN_LEN,
1255		MC_CMD_GET_BOARD_CFG_OUT_LENMIN);
1256	efx_rc_t rc;
1257
1258	req.emr_cmd = MC_CMD_GET_BOARD_CFG;
1259	req.emr_in_buf = payload;
1260	req.emr_in_length = MC_CMD_GET_BOARD_CFG_IN_LEN;
1261	req.emr_out_buf = payload;
1262	req.emr_out_length = MC_CMD_GET_BOARD_CFG_OUT_LENMIN;
1263
1264	efx_mcdi_execute(enp, &req);
1265
1266	if (req.emr_rc != 0) {
1267		rc = req.emr_rc;
1268		goto fail1;
1269	}
1270
1271	if (req.emr_out_length_used < MC_CMD_GET_BOARD_CFG_OUT_LENMIN) {
1272		rc = EMSGSIZE;
1273		goto fail2;
1274	}
1275
1276	if (mac_addrp != NULL) {
1277		uint8_t *addrp;
1278
1279		if (emip->emi_port == 1) {
1280			addrp = MCDI_OUT2(req, uint8_t,
1281			    GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0);
1282		} else if (emip->emi_port == 2) {
1283			addrp = MCDI_OUT2(req, uint8_t,
1284			    GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1);
1285		} else {
1286			rc = EINVAL;
1287			goto fail3;
1288		}
1289
1290		EFX_MAC_ADDR_COPY(mac_addrp, addrp);
1291	}
1292
1293	if (capabilitiesp != NULL) {
1294		if (emip->emi_port == 1) {
1295			*capabilitiesp = *MCDI_OUT2(req, efx_dword_t,
1296			    GET_BOARD_CFG_OUT_CAPABILITIES_PORT0);
1297		} else if (emip->emi_port == 2) {
1298			*capabilitiesp = *MCDI_OUT2(req, efx_dword_t,
1299			    GET_BOARD_CFG_OUT_CAPABILITIES_PORT1);
1300		} else {
1301			rc = EINVAL;
1302			goto fail4;
1303		}
1304	}
1305
1306	if (board_typep != NULL) {
1307		*board_typep = MCDI_OUT_DWORD(req,
1308		    GET_BOARD_CFG_OUT_BOARD_TYPE);
1309	}
1310
1311	return (0);
1312
1313fail4:
1314	EFSYS_PROBE(fail4);
1315fail3:
1316	EFSYS_PROBE(fail3);
1317fail2:
1318	EFSYS_PROBE(fail2);
1319fail1:
1320	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1321
1322	return (rc);
1323}
1324
1325	__checkReturn	efx_rc_t
1326efx_mcdi_get_resource_limits(
1327	__in		efx_nic_t *enp,
1328	__out_opt	uint32_t *nevqp,
1329	__out_opt	uint32_t *nrxqp,
1330	__out_opt	uint32_t *ntxqp)
1331{
1332	efx_mcdi_req_t req;
1333	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_RESOURCE_LIMITS_IN_LEN,
1334		MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN);
1335	efx_rc_t rc;
1336
1337	req.emr_cmd = MC_CMD_GET_RESOURCE_LIMITS;
1338	req.emr_in_buf = payload;
1339	req.emr_in_length = MC_CMD_GET_RESOURCE_LIMITS_IN_LEN;
1340	req.emr_out_buf = payload;
1341	req.emr_out_length = MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN;
1342
1343	efx_mcdi_execute(enp, &req);
1344
1345	if (req.emr_rc != 0) {
1346		rc = req.emr_rc;
1347		goto fail1;
1348	}
1349
1350	if (req.emr_out_length_used < MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN) {
1351		rc = EMSGSIZE;
1352		goto fail2;
1353	}
1354
1355	if (nevqp != NULL)
1356		*nevqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_EVQ);
1357	if (nrxqp != NULL)
1358		*nrxqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_RXQ);
1359	if (ntxqp != NULL)
1360		*ntxqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_TXQ);
1361
1362	return (0);
1363
1364fail2:
1365	EFSYS_PROBE(fail2);
1366fail1:
1367	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1368
1369	return (rc);
1370}
1371
1372	__checkReturn	efx_rc_t
1373efx_mcdi_get_phy_cfg(
1374	__in		efx_nic_t *enp)
1375{
1376	efx_port_t *epp = &(enp->en_port);
1377	efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
1378	efx_mcdi_req_t req;
1379	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_PHY_CFG_IN_LEN,
1380		MC_CMD_GET_PHY_CFG_OUT_LEN);
1381	efx_rc_t rc;
1382
1383	req.emr_cmd = MC_CMD_GET_PHY_CFG;
1384	req.emr_in_buf = payload;
1385	req.emr_in_length = MC_CMD_GET_PHY_CFG_IN_LEN;
1386	req.emr_out_buf = payload;
1387	req.emr_out_length = MC_CMD_GET_PHY_CFG_OUT_LEN;
1388
1389	efx_mcdi_execute(enp, &req);
1390
1391	if (req.emr_rc != 0) {
1392		rc = req.emr_rc;
1393		goto fail1;
1394	}
1395
1396	if (req.emr_out_length_used < MC_CMD_GET_PHY_CFG_OUT_LEN) {
1397		rc = EMSGSIZE;
1398		goto fail2;
1399	}
1400
1401	encp->enc_phy_type = MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_TYPE);
1402#if EFSYS_OPT_NAMES
1403	(void) strncpy(encp->enc_phy_name,
1404		MCDI_OUT2(req, char, GET_PHY_CFG_OUT_NAME),
1405		MIN(sizeof (encp->enc_phy_name) - 1,
1406		    MC_CMD_GET_PHY_CFG_OUT_NAME_LEN));
1407#endif	/* EFSYS_OPT_NAMES */
1408	(void) memset(encp->enc_phy_revision, 0,
1409	    sizeof (encp->enc_phy_revision));
1410	memcpy(encp->enc_phy_revision,
1411		MCDI_OUT2(req, char, GET_PHY_CFG_OUT_REVISION),
1412		MIN(sizeof (encp->enc_phy_revision) - 1,
1413		    MC_CMD_GET_PHY_CFG_OUT_REVISION_LEN));
1414#if EFSYS_OPT_PHY_LED_CONTROL
1415	encp->enc_led_mask = ((1 << EFX_PHY_LED_DEFAULT) |
1416			    (1 << EFX_PHY_LED_OFF) |
1417			    (1 << EFX_PHY_LED_ON));
1418#endif	/* EFSYS_OPT_PHY_LED_CONTROL */
1419
1420	/* Get the media type of the fixed port, if recognised. */
1421	EFX_STATIC_ASSERT(MC_CMD_MEDIA_XAUI == EFX_PHY_MEDIA_XAUI);
1422	EFX_STATIC_ASSERT(MC_CMD_MEDIA_CX4 == EFX_PHY_MEDIA_CX4);
1423	EFX_STATIC_ASSERT(MC_CMD_MEDIA_KX4 == EFX_PHY_MEDIA_KX4);
1424	EFX_STATIC_ASSERT(MC_CMD_MEDIA_XFP == EFX_PHY_MEDIA_XFP);
1425	EFX_STATIC_ASSERT(MC_CMD_MEDIA_SFP_PLUS == EFX_PHY_MEDIA_SFP_PLUS);
1426	EFX_STATIC_ASSERT(MC_CMD_MEDIA_BASE_T == EFX_PHY_MEDIA_BASE_T);
1427	EFX_STATIC_ASSERT(MC_CMD_MEDIA_QSFP_PLUS == EFX_PHY_MEDIA_QSFP_PLUS);
1428	epp->ep_fixed_port_type =
1429		MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_MEDIA_TYPE);
1430	if (epp->ep_fixed_port_type >= EFX_PHY_MEDIA_NTYPES)
1431		epp->ep_fixed_port_type = EFX_PHY_MEDIA_INVALID;
1432
1433	epp->ep_phy_cap_mask =
1434		MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_SUPPORTED_CAP);
1435#if EFSYS_OPT_PHY_FLAGS
1436	encp->enc_phy_flags_mask = MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_FLAGS);
1437#endif	/* EFSYS_OPT_PHY_FLAGS */
1438
1439	encp->enc_port = (uint8_t)MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_PRT);
1440
1441	/* Populate internal state */
1442	encp->enc_mcdi_mdio_channel =
1443		(uint8_t)MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_CHANNEL);
1444
1445#if EFSYS_OPT_PHY_STATS
1446	encp->enc_mcdi_phy_stat_mask =
1447		MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_STATS_MASK);
1448#endif	/* EFSYS_OPT_PHY_STATS */
1449
1450#if EFSYS_OPT_BIST
1451	encp->enc_bist_mask = 0;
1452	if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS,
1453	    GET_PHY_CFG_OUT_BIST_CABLE_SHORT))
1454		encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_CABLE_SHORT);
1455	if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS,
1456	    GET_PHY_CFG_OUT_BIST_CABLE_LONG))
1457		encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_CABLE_LONG);
1458	if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS,
1459	    GET_PHY_CFG_OUT_BIST))
1460		encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_NORMAL);
1461#endif  /* EFSYS_OPT_BIST */
1462
1463	return (0);
1464
1465fail2:
1466	EFSYS_PROBE(fail2);
1467fail1:
1468	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1469
1470	return (rc);
1471}
1472
1473	__checkReturn		efx_rc_t
1474efx_mcdi_firmware_update_supported(
1475	__in			efx_nic_t *enp,
1476	__out			boolean_t *supportedp)
1477{
1478	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
1479	efx_rc_t rc;
1480
1481	if (emcop != NULL) {
1482		if ((rc = emcop->emco_feature_supported(enp,
1483			    EFX_MCDI_FEATURE_FW_UPDATE, supportedp)) != 0)
1484			goto fail1;
1485	} else {
1486		/* Earlier devices always supported updates */
1487		*supportedp = B_TRUE;
1488	}
1489
1490	return (0);
1491
1492fail1:
1493	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1494
1495	return (rc);
1496}
1497
1498	__checkReturn		efx_rc_t
1499efx_mcdi_macaddr_change_supported(
1500	__in			efx_nic_t *enp,
1501	__out			boolean_t *supportedp)
1502{
1503	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
1504	efx_rc_t rc;
1505
1506	if (emcop != NULL) {
1507		if ((rc = emcop->emco_feature_supported(enp,
1508			    EFX_MCDI_FEATURE_MACADDR_CHANGE, supportedp)) != 0)
1509			goto fail1;
1510	} else {
1511		/* Earlier devices always supported MAC changes */
1512		*supportedp = B_TRUE;
1513	}
1514
1515	return (0);
1516
1517fail1:
1518	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1519
1520	return (rc);
1521}
1522
1523	__checkReturn		efx_rc_t
1524efx_mcdi_link_control_supported(
1525	__in			efx_nic_t *enp,
1526	__out			boolean_t *supportedp)
1527{
1528	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
1529	efx_rc_t rc;
1530
1531	if (emcop != NULL) {
1532		if ((rc = emcop->emco_feature_supported(enp,
1533			    EFX_MCDI_FEATURE_LINK_CONTROL, supportedp)) != 0)
1534			goto fail1;
1535	} else {
1536		/* Earlier devices always supported link control */
1537		*supportedp = B_TRUE;
1538	}
1539
1540	return (0);
1541
1542fail1:
1543	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1544
1545	return (rc);
1546}
1547
1548	__checkReturn		efx_rc_t
1549efx_mcdi_mac_spoofing_supported(
1550	__in			efx_nic_t *enp,
1551	__out			boolean_t *supportedp)
1552{
1553	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
1554	efx_rc_t rc;
1555
1556	if (emcop != NULL) {
1557		if ((rc = emcop->emco_feature_supported(enp,
1558			    EFX_MCDI_FEATURE_MAC_SPOOFING, supportedp)) != 0)
1559			goto fail1;
1560	} else {
1561		/* Earlier devices always supported MAC spoofing */
1562		*supportedp = B_TRUE;
1563	}
1564
1565	return (0);
1566
1567fail1:
1568	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1569
1570	return (rc);
1571}
1572
1573#if EFSYS_OPT_BIST
1574
1575#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
1576/*
1577 * Enter bist offline mode. This is a fw mode which puts the NIC into a state
1578 * where memory BIST tests can be run and not much else can interfere or happen.
1579 * A reboot is required to exit this mode.
1580 */
1581	__checkReturn		efx_rc_t
1582efx_mcdi_bist_enable_offline(
1583	__in			efx_nic_t *enp)
1584{
1585	efx_mcdi_req_t req;
1586	efx_rc_t rc;
1587
1588	EFX_STATIC_ASSERT(MC_CMD_ENABLE_OFFLINE_BIST_IN_LEN == 0);
1589	EFX_STATIC_ASSERT(MC_CMD_ENABLE_OFFLINE_BIST_OUT_LEN == 0);
1590
1591	req.emr_cmd = MC_CMD_ENABLE_OFFLINE_BIST;
1592	req.emr_in_buf = NULL;
1593	req.emr_in_length = 0;
1594	req.emr_out_buf = NULL;
1595	req.emr_out_length = 0;
1596
1597	efx_mcdi_execute(enp, &req);
1598
1599	if (req.emr_rc != 0) {
1600		rc = req.emr_rc;
1601		goto fail1;
1602	}
1603
1604	return (0);
1605
1606fail1:
1607	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1608
1609	return (rc);
1610}
1611#endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
1612
1613	__checkReturn		efx_rc_t
1614efx_mcdi_bist_start(
1615	__in			efx_nic_t *enp,
1616	__in			efx_bist_type_t type)
1617{
1618	efx_mcdi_req_t req;
1619	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_START_BIST_IN_LEN,
1620		MC_CMD_START_BIST_OUT_LEN);
1621	efx_rc_t rc;
1622
1623	req.emr_cmd = MC_CMD_START_BIST;
1624	req.emr_in_buf = payload;
1625	req.emr_in_length = MC_CMD_START_BIST_IN_LEN;
1626	req.emr_out_buf = payload;
1627	req.emr_out_length = MC_CMD_START_BIST_OUT_LEN;
1628
1629	switch (type) {
1630	case EFX_BIST_TYPE_PHY_NORMAL:
1631		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE, MC_CMD_PHY_BIST);
1632		break;
1633	case EFX_BIST_TYPE_PHY_CABLE_SHORT:
1634		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1635		    MC_CMD_PHY_BIST_CABLE_SHORT);
1636		break;
1637	case EFX_BIST_TYPE_PHY_CABLE_LONG:
1638		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1639		    MC_CMD_PHY_BIST_CABLE_LONG);
1640		break;
1641	case EFX_BIST_TYPE_MC_MEM:
1642		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1643		    MC_CMD_MC_MEM_BIST);
1644		break;
1645	case EFX_BIST_TYPE_SAT_MEM:
1646		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1647		    MC_CMD_PORT_MEM_BIST);
1648		break;
1649	case EFX_BIST_TYPE_REG:
1650		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
1651		    MC_CMD_REG_BIST);
1652		break;
1653	default:
1654		EFSYS_ASSERT(0);
1655	}
1656
1657	efx_mcdi_execute(enp, &req);
1658
1659	if (req.emr_rc != 0) {
1660		rc = req.emr_rc;
1661		goto fail1;
1662	}
1663
1664	return (0);
1665
1666fail1:
1667	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1668
1669	return (rc);
1670}
1671
1672#endif /* EFSYS_OPT_BIST */
1673
1674
1675/* Enable logging of some events (e.g. link state changes) */
1676	__checkReturn	efx_rc_t
1677efx_mcdi_log_ctrl(
1678	__in		efx_nic_t *enp)
1679{
1680	efx_mcdi_req_t req;
1681	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_LOG_CTRL_IN_LEN,
1682		MC_CMD_LOG_CTRL_OUT_LEN);
1683	efx_rc_t rc;
1684
1685	req.emr_cmd = MC_CMD_LOG_CTRL;
1686	req.emr_in_buf = payload;
1687	req.emr_in_length = MC_CMD_LOG_CTRL_IN_LEN;
1688	req.emr_out_buf = payload;
1689	req.emr_out_length = MC_CMD_LOG_CTRL_OUT_LEN;
1690
1691	MCDI_IN_SET_DWORD(req, LOG_CTRL_IN_LOG_DEST,
1692		    MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ);
1693	MCDI_IN_SET_DWORD(req, LOG_CTRL_IN_LOG_DEST_EVQ, 0);
1694
1695	efx_mcdi_execute(enp, &req);
1696
1697	if (req.emr_rc != 0) {
1698		rc = req.emr_rc;
1699		goto fail1;
1700	}
1701
1702	return (0);
1703
1704fail1:
1705	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1706
1707	return (rc);
1708}
1709
1710
1711#if EFSYS_OPT_MAC_STATS
1712
1713typedef enum efx_stats_action_e {
1714	EFX_STATS_CLEAR,
1715	EFX_STATS_UPLOAD,
1716	EFX_STATS_ENABLE_NOEVENTS,
1717	EFX_STATS_ENABLE_EVENTS,
1718	EFX_STATS_DISABLE,
1719} efx_stats_action_t;
1720
1721static	__checkReturn	efx_rc_t
1722efx_mcdi_mac_stats(
1723	__in		efx_nic_t *enp,
1724	__in_opt	efsys_mem_t *esmp,
1725	__in		efx_stats_action_t action,
1726	__in		uint16_t period_ms)
1727{
1728	efx_mcdi_req_t req;
1729	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_MAC_STATS_IN_LEN,
1730		MC_CMD_MAC_STATS_OUT_DMA_LEN);
1731	int clear = (action == EFX_STATS_CLEAR);
1732	int upload = (action == EFX_STATS_UPLOAD);
1733	int enable = (action == EFX_STATS_ENABLE_NOEVENTS);
1734	int events = (action == EFX_STATS_ENABLE_EVENTS);
1735	int disable = (action == EFX_STATS_DISABLE);
1736	efx_rc_t rc;
1737
1738	req.emr_cmd = MC_CMD_MAC_STATS;
1739	req.emr_in_buf = payload;
1740	req.emr_in_length = MC_CMD_MAC_STATS_IN_LEN;
1741	req.emr_out_buf = payload;
1742	req.emr_out_length = MC_CMD_MAC_STATS_OUT_DMA_LEN;
1743
1744	MCDI_IN_POPULATE_DWORD_6(req, MAC_STATS_IN_CMD,
1745	    MAC_STATS_IN_DMA, upload,
1746	    MAC_STATS_IN_CLEAR, clear,
1747	    MAC_STATS_IN_PERIODIC_CHANGE, enable | events | disable,
1748	    MAC_STATS_IN_PERIODIC_ENABLE, enable | events,
1749	    MAC_STATS_IN_PERIODIC_NOEVENT, !events,
1750	    MAC_STATS_IN_PERIOD_MS, (enable | events) ? period_ms : 0);
1751
1752	if (esmp != NULL) {
1753		int bytes = MC_CMD_MAC_NSTATS * sizeof (uint64_t);
1754
1755		EFX_STATIC_ASSERT(MC_CMD_MAC_NSTATS * sizeof (uint64_t) <=
1756		    EFX_MAC_STATS_SIZE);
1757
1758		MCDI_IN_SET_DWORD(req, MAC_STATS_IN_DMA_ADDR_LO,
1759			    EFSYS_MEM_ADDR(esmp) & 0xffffffff);
1760		MCDI_IN_SET_DWORD(req, MAC_STATS_IN_DMA_ADDR_HI,
1761			    EFSYS_MEM_ADDR(esmp) >> 32);
1762		MCDI_IN_SET_DWORD(req, MAC_STATS_IN_DMA_LEN, bytes);
1763	} else {
1764		EFSYS_ASSERT(!upload && !enable && !events);
1765	}
1766
1767	/*
1768	 * NOTE: Do not use EVB_PORT_ID_ASSIGNED when disabling periodic stats,
1769	 *	 as this may fail (and leave periodic DMA enabled) if the
1770	 *	 vadapter has already been deleted.
1771	 */
1772	MCDI_IN_SET_DWORD(req, MAC_STATS_IN_PORT_ID,
1773	    (disable ? EVB_PORT_ID_NULL : enp->en_vport_id));
1774
1775	efx_mcdi_execute(enp, &req);
1776
1777	if (req.emr_rc != 0) {
1778		/* EF10: Expect ENOENT if no DMA queues are initialised */
1779		if ((req.emr_rc != ENOENT) ||
1780		    (enp->en_rx_qcount + enp->en_tx_qcount != 0)) {
1781			rc = req.emr_rc;
1782			goto fail1;
1783		}
1784	}
1785
1786	return (0);
1787
1788fail1:
1789	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1790
1791	return (rc);
1792}
1793
1794	__checkReturn	efx_rc_t
1795efx_mcdi_mac_stats_clear(
1796	__in		efx_nic_t *enp)
1797{
1798	efx_rc_t rc;
1799
1800	if ((rc = efx_mcdi_mac_stats(enp, NULL, EFX_STATS_CLEAR, 0)) != 0)
1801		goto fail1;
1802
1803	return (0);
1804
1805fail1:
1806	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1807
1808	return (rc);
1809}
1810
1811	__checkReturn	efx_rc_t
1812efx_mcdi_mac_stats_upload(
1813	__in		efx_nic_t *enp,
1814	__in		efsys_mem_t *esmp)
1815{
1816	efx_rc_t rc;
1817
1818	/*
1819	 * The MC DMAs aggregate statistics for our convenience, so we can
1820	 * avoid having to pull the statistics buffer into the cache to
1821	 * maintain cumulative statistics.
1822	 */
1823	if ((rc = efx_mcdi_mac_stats(enp, esmp, EFX_STATS_UPLOAD, 0)) != 0)
1824		goto fail1;
1825
1826	return (0);
1827
1828fail1:
1829	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1830
1831	return (rc);
1832}
1833
1834	__checkReturn	efx_rc_t
1835efx_mcdi_mac_stats_periodic(
1836	__in		efx_nic_t *enp,
1837	__in		efsys_mem_t *esmp,
1838	__in		uint16_t period_ms,
1839	__in		boolean_t events)
1840{
1841	efx_rc_t rc;
1842
1843	/*
1844	 * The MC DMAs aggregate statistics for our convenience, so we can
1845	 * avoid having to pull the statistics buffer into the cache to
1846	 * maintain cumulative statistics.
1847	 * Huntington uses a fixed 1sec period.
1848	 * Medford uses a fixed 1sec period before v6.2.1.1033 firmware.
1849	 */
1850	if (period_ms == 0)
1851		rc = efx_mcdi_mac_stats(enp, NULL, EFX_STATS_DISABLE, 0);
1852	else if (events)
1853		rc = efx_mcdi_mac_stats(enp, esmp, EFX_STATS_ENABLE_EVENTS,
1854		    period_ms);
1855	else
1856		rc = efx_mcdi_mac_stats(enp, esmp, EFX_STATS_ENABLE_NOEVENTS,
1857		    period_ms);
1858
1859	if (rc != 0)
1860		goto fail1;
1861
1862	return (0);
1863
1864fail1:
1865	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1866
1867	return (rc);
1868}
1869
1870#endif	/* EFSYS_OPT_MAC_STATS */
1871
1872#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
1873
1874/*
1875 * This function returns the pf and vf number of a function.  If it is a pf the
1876 * vf number is 0xffff.  The vf number is the index of the vf on that
1877 * function. So if you have 3 vfs on pf 0 the 3 vfs will return (pf=0,vf=0),
1878 * (pf=0,vf=1), (pf=0,vf=2) aand the pf will return (pf=0, vf=0xffff).
1879 */
1880	__checkReturn		efx_rc_t
1881efx_mcdi_get_function_info(
1882	__in			efx_nic_t *enp,
1883	__out			uint32_t *pfp,
1884	__out_opt		uint32_t *vfp)
1885{
1886	efx_mcdi_req_t req;
1887	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_FUNCTION_INFO_IN_LEN,
1888		MC_CMD_GET_FUNCTION_INFO_OUT_LEN);
1889	efx_rc_t rc;
1890
1891	req.emr_cmd = MC_CMD_GET_FUNCTION_INFO;
1892	req.emr_in_buf = payload;
1893	req.emr_in_length = MC_CMD_GET_FUNCTION_INFO_IN_LEN;
1894	req.emr_out_buf = payload;
1895	req.emr_out_length = MC_CMD_GET_FUNCTION_INFO_OUT_LEN;
1896
1897	efx_mcdi_execute(enp, &req);
1898
1899	if (req.emr_rc != 0) {
1900		rc = req.emr_rc;
1901		goto fail1;
1902	}
1903
1904	if (req.emr_out_length_used < MC_CMD_GET_FUNCTION_INFO_OUT_LEN) {
1905		rc = EMSGSIZE;
1906		goto fail2;
1907	}
1908
1909	*pfp = MCDI_OUT_DWORD(req, GET_FUNCTION_INFO_OUT_PF);
1910	if (vfp != NULL)
1911		*vfp = MCDI_OUT_DWORD(req, GET_FUNCTION_INFO_OUT_VF);
1912
1913	return (0);
1914
1915fail2:
1916	EFSYS_PROBE(fail2);
1917fail1:
1918	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1919
1920	return (rc);
1921}
1922
1923	__checkReturn		efx_rc_t
1924efx_mcdi_privilege_mask(
1925	__in			efx_nic_t *enp,
1926	__in			uint32_t pf,
1927	__in			uint32_t vf,
1928	__out			uint32_t *maskp)
1929{
1930	efx_mcdi_req_t req;
1931	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_PRIVILEGE_MASK_IN_LEN,
1932		MC_CMD_PRIVILEGE_MASK_OUT_LEN);
1933	efx_rc_t rc;
1934
1935	req.emr_cmd = MC_CMD_PRIVILEGE_MASK;
1936	req.emr_in_buf = payload;
1937	req.emr_in_length = MC_CMD_PRIVILEGE_MASK_IN_LEN;
1938	req.emr_out_buf = payload;
1939	req.emr_out_length = MC_CMD_PRIVILEGE_MASK_OUT_LEN;
1940
1941	MCDI_IN_POPULATE_DWORD_2(req, PRIVILEGE_MASK_IN_FUNCTION,
1942	    PRIVILEGE_MASK_IN_FUNCTION_PF, pf,
1943	    PRIVILEGE_MASK_IN_FUNCTION_VF, vf);
1944
1945	efx_mcdi_execute(enp, &req);
1946
1947	if (req.emr_rc != 0) {
1948		rc = req.emr_rc;
1949		goto fail1;
1950	}
1951
1952	if (req.emr_out_length_used < MC_CMD_PRIVILEGE_MASK_OUT_LEN) {
1953		rc = EMSGSIZE;
1954		goto fail2;
1955	}
1956
1957	*maskp = MCDI_OUT_DWORD(req, PRIVILEGE_MASK_OUT_OLD_MASK);
1958
1959	return (0);
1960
1961fail2:
1962	EFSYS_PROBE(fail2);
1963fail1:
1964	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1965
1966	return (rc);
1967}
1968
1969#endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
1970
1971	__checkReturn		efx_rc_t
1972efx_mcdi_set_workaround(
1973	__in			efx_nic_t *enp,
1974	__in			uint32_t type,
1975	__in			boolean_t enabled,
1976	__out_opt		uint32_t *flagsp)
1977{
1978	efx_mcdi_req_t req;
1979	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_WORKAROUND_IN_LEN,
1980		MC_CMD_WORKAROUND_EXT_OUT_LEN);
1981	efx_rc_t rc;
1982
1983	req.emr_cmd = MC_CMD_WORKAROUND;
1984	req.emr_in_buf = payload;
1985	req.emr_in_length = MC_CMD_WORKAROUND_IN_LEN;
1986	req.emr_out_buf = payload;
1987	req.emr_out_length = MC_CMD_WORKAROUND_OUT_LEN;
1988
1989	MCDI_IN_SET_DWORD(req, WORKAROUND_IN_TYPE, type);
1990	MCDI_IN_SET_DWORD(req, WORKAROUND_IN_ENABLED, enabled ? 1 : 0);
1991
1992	efx_mcdi_execute_quiet(enp, &req);
1993
1994	if (req.emr_rc != 0) {
1995		rc = req.emr_rc;
1996		goto fail1;
1997	}
1998
1999	if (flagsp != NULL) {
2000		if (req.emr_out_length_used >= MC_CMD_WORKAROUND_EXT_OUT_LEN)
2001			*flagsp = MCDI_OUT_DWORD(req, WORKAROUND_EXT_OUT_FLAGS);
2002		else
2003			*flagsp = 0;
2004	}
2005
2006	return (0);
2007
2008fail1:
2009	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2010
2011	return (rc);
2012}
2013
2014
2015	__checkReturn		efx_rc_t
2016efx_mcdi_get_workarounds(
2017	__in			efx_nic_t *enp,
2018	__out_opt		uint32_t *implementedp,
2019	__out_opt		uint32_t *enabledp)
2020{
2021	efx_mcdi_req_t req;
2022	EFX_MCDI_DECLARE_BUF(payload, 0, MC_CMD_GET_WORKAROUNDS_OUT_LEN);
2023	efx_rc_t rc;
2024
2025	req.emr_cmd = MC_CMD_GET_WORKAROUNDS;
2026	req.emr_in_buf = NULL;
2027	req.emr_in_length = 0;
2028	req.emr_out_buf = payload;
2029	req.emr_out_length = MC_CMD_GET_WORKAROUNDS_OUT_LEN;
2030
2031	efx_mcdi_execute(enp, &req);
2032
2033	if (req.emr_rc != 0) {
2034		rc = req.emr_rc;
2035		goto fail1;
2036	}
2037
2038	if (implementedp != NULL) {
2039		*implementedp =
2040		    MCDI_OUT_DWORD(req, GET_WORKAROUNDS_OUT_IMPLEMENTED);
2041	}
2042
2043	if (enabledp != NULL) {
2044		*enabledp = MCDI_OUT_DWORD(req, GET_WORKAROUNDS_OUT_ENABLED);
2045	}
2046
2047	return (0);
2048
2049fail1:
2050	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2051
2052	return (rc);
2053}
2054
2055/*
2056 * Size of media information page in accordance with SFF-8472 and SFF-8436.
2057 * It is used in MCDI interface as well.
2058 */
2059#define	EFX_PHY_MEDIA_INFO_PAGE_SIZE		0x80
2060
2061static	__checkReturn		efx_rc_t
2062efx_mcdi_get_phy_media_info(
2063	__in			efx_nic_t *enp,
2064	__in			uint32_t mcdi_page,
2065	__in			uint8_t offset,
2066	__in			uint8_t len,
2067	__out_bcount(len)	uint8_t *data)
2068{
2069	efx_mcdi_req_t req;
2070	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN,
2071		MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(
2072			EFX_PHY_MEDIA_INFO_PAGE_SIZE));
2073	efx_rc_t rc;
2074
2075	EFSYS_ASSERT((uint32_t)offset + len <= EFX_PHY_MEDIA_INFO_PAGE_SIZE);
2076
2077	req.emr_cmd = MC_CMD_GET_PHY_MEDIA_INFO;
2078	req.emr_in_buf = payload;
2079	req.emr_in_length = MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN;
2080	req.emr_out_buf = payload;
2081	req.emr_out_length =
2082	    MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(EFX_PHY_MEDIA_INFO_PAGE_SIZE);
2083
2084	MCDI_IN_SET_DWORD(req, GET_PHY_MEDIA_INFO_IN_PAGE, mcdi_page);
2085
2086	efx_mcdi_execute(enp, &req);
2087
2088	if (req.emr_rc != 0) {
2089		rc = req.emr_rc;
2090		goto fail1;
2091	}
2092
2093	if (req.emr_out_length_used !=
2094	    MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(EFX_PHY_MEDIA_INFO_PAGE_SIZE)) {
2095		rc = EMSGSIZE;
2096		goto fail2;
2097	}
2098
2099	if (MCDI_OUT_DWORD(req, GET_PHY_MEDIA_INFO_OUT_DATALEN) !=
2100	    EFX_PHY_MEDIA_INFO_PAGE_SIZE) {
2101		rc = EIO;
2102		goto fail3;
2103	}
2104
2105	memcpy(data,
2106	    MCDI_OUT2(req, uint8_t, GET_PHY_MEDIA_INFO_OUT_DATA) + offset,
2107	    len);
2108
2109	return (0);
2110
2111fail3:
2112	EFSYS_PROBE(fail3);
2113fail2:
2114	EFSYS_PROBE(fail2);
2115fail1:
2116	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2117
2118	return (rc);
2119}
2120
2121/*
2122 * 2-wire device address of the base information in accordance with SFF-8472
2123 * Diagnostic Monitoring Interface for Optical Transceivers section
2124 * 4 Memory Organization.
2125 */
2126#define	EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_BASE	0xA0
2127
2128/*
2129 * 2-wire device address of the digital diagnostics monitoring interface
2130 * in accordance with SFF-8472 Diagnostic Monitoring Interface for Optical
2131 * Transceivers section 4 Memory Organization.
2132 */
2133#define	EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_DDM	0xA2
2134
2135/*
2136 * Hard wired 2-wire device address for QSFP+ in accordance with SFF-8436
2137 * QSFP+ 10 Gbs 4X PLUGGABLE TRANSCEIVER section 7.4 Device Addressing and
2138 * Operation.
2139 */
2140#define	EFX_PHY_MEDIA_INFO_DEV_ADDR_QSFP	0xA0
2141
2142	__checkReturn		efx_rc_t
2143efx_mcdi_phy_module_get_info(
2144	__in			efx_nic_t *enp,
2145	__in			uint8_t dev_addr,
2146	__in			uint8_t offset,
2147	__in			uint8_t len,
2148	__out_bcount(len)	uint8_t *data)
2149{
2150	efx_port_t *epp = &(enp->en_port);
2151	efx_rc_t rc;
2152	uint32_t mcdi_lower_page;
2153	uint32_t mcdi_upper_page;
2154
2155	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
2156
2157	/*
2158	 * Map device address to MC_CMD_GET_PHY_MEDIA_INFO pages.
2159	 * Offset plus length interface allows to access page 0 only.
2160	 * I.e. non-zero upper pages are not accessible.
2161	 * See SFF-8472 section 4 Memory Organization and SFF-8436 section 7.6
2162	 * QSFP+ Memory Map for details on how information is structured
2163	 * and accessible.
2164	 */
2165	switch (epp->ep_fixed_port_type) {
2166	case EFX_PHY_MEDIA_SFP_PLUS:
2167		/*
2168		 * In accordance with SFF-8472 Diagnostic Monitoring
2169		 * Interface for Optical Transceivers section 4 Memory
2170		 * Organization two 2-wire addresses are defined.
2171		 */
2172		switch (dev_addr) {
2173		/* Base information */
2174		case EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_BASE:
2175			/*
2176			 * MCDI page 0 should be used to access lower
2177			 * page 0 (0x00 - 0x7f) at the device address 0xA0.
2178			 */
2179			mcdi_lower_page = 0;
2180			/*
2181			 * MCDI page 1 should be used to access  upper
2182			 * page 0 (0x80 - 0xff) at the device address 0xA0.
2183			 */
2184			mcdi_upper_page = 1;
2185			break;
2186		/* Diagnostics */
2187		case EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_DDM:
2188			/*
2189			 * MCDI page 2 should be used to access lower
2190			 * page 0 (0x00 - 0x7f) at the device address 0xA2.
2191			 */
2192			mcdi_lower_page = 2;
2193			/*
2194			 * MCDI page 3 should be used to access upper
2195			 * page 0 (0x80 - 0xff) at the device address 0xA2.
2196			 */
2197			mcdi_upper_page = 3;
2198			break;
2199		default:
2200			rc = ENOTSUP;
2201			goto fail1;
2202		}
2203		break;
2204	case EFX_PHY_MEDIA_QSFP_PLUS:
2205		switch (dev_addr) {
2206		case EFX_PHY_MEDIA_INFO_DEV_ADDR_QSFP:
2207			/*
2208			 * MCDI page -1 should be used to access lower page 0
2209			 * (0x00 - 0x7f).
2210			 */
2211			mcdi_lower_page = (uint32_t)-1;
2212			/*
2213			 * MCDI page 0 should be used to access upper page 0
2214			 * (0x80h - 0xff).
2215			 */
2216			mcdi_upper_page = 0;
2217			break;
2218		default:
2219			rc = ENOTSUP;
2220			goto fail1;
2221		}
2222		break;
2223	default:
2224		rc = ENOTSUP;
2225		goto fail1;
2226	}
2227
2228	if (offset < EFX_PHY_MEDIA_INFO_PAGE_SIZE) {
2229		uint8_t read_len =
2230		    MIN(len, EFX_PHY_MEDIA_INFO_PAGE_SIZE - offset);
2231
2232		rc = efx_mcdi_get_phy_media_info(enp,
2233		    mcdi_lower_page, offset, read_len, data);
2234		if (rc != 0)
2235			goto fail2;
2236
2237		data += read_len;
2238		len -= read_len;
2239
2240		offset = 0;
2241	} else {
2242		offset -= EFX_PHY_MEDIA_INFO_PAGE_SIZE;
2243	}
2244
2245	if (len > 0) {
2246		EFSYS_ASSERT3U(len, <=, EFX_PHY_MEDIA_INFO_PAGE_SIZE);
2247		EFSYS_ASSERT3U(offset, <, EFX_PHY_MEDIA_INFO_PAGE_SIZE);
2248
2249		rc = efx_mcdi_get_phy_media_info(enp,
2250		    mcdi_upper_page, offset, len, data);
2251		if (rc != 0)
2252			goto fail3;
2253	}
2254
2255	return (0);
2256
2257fail3:
2258	EFSYS_PROBE(fail3);
2259fail2:
2260	EFSYS_PROBE(fail2);
2261fail1:
2262	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2263
2264	return (rc);
2265}
2266
2267#endif	/* EFSYS_OPT_MCDI */
2268