xref: /dpdk/drivers/common/sfc_efx/base/efx_mcdi.c (revision 09b59c7da7f4c22e015969651c61b100ed690ea2)
15e111ed8SAndrew Rybchenko /* SPDX-License-Identifier: BSD-3-Clause
25e111ed8SAndrew Rybchenko  *
35e111ed8SAndrew Rybchenko  * Copyright(c) 2019-2020 Xilinx, Inc.
45e111ed8SAndrew Rybchenko  * Copyright(c) 2008-2019 Solarflare Communications Inc.
55e111ed8SAndrew Rybchenko  */
65e111ed8SAndrew Rybchenko 
75e111ed8SAndrew Rybchenko #include "efx.h"
85e111ed8SAndrew Rybchenko #include "efx_impl.h"
95e111ed8SAndrew Rybchenko 
105e111ed8SAndrew Rybchenko #if EFSYS_OPT_MCDI
115e111ed8SAndrew Rybchenko 
125e111ed8SAndrew Rybchenko /*
135e111ed8SAndrew Rybchenko  * There are three versions of the MCDI interface:
145e111ed8SAndrew Rybchenko  *  - MCDIv0: Siena BootROM. Transport uses MCDIv1 headers.
155e111ed8SAndrew Rybchenko  *  - MCDIv1: Siena firmware and Huntington BootROM.
165e111ed8SAndrew Rybchenko  *  - MCDIv2: EF10 firmware (Huntington/Medford) and Medford BootROM.
175e111ed8SAndrew Rybchenko  *            Transport uses MCDIv2 headers.
185e111ed8SAndrew Rybchenko  *
195e111ed8SAndrew Rybchenko  * MCDIv2 Header NOT_EPOCH flag
205e111ed8SAndrew Rybchenko  * ----------------------------
215e111ed8SAndrew Rybchenko  * A new epoch begins at initial startup or after an MC reboot, and defines when
225e111ed8SAndrew Rybchenko  * the MC should reject stale MCDI requests.
235e111ed8SAndrew Rybchenko  *
245e111ed8SAndrew Rybchenko  * The first MCDI request sent by the host should contain NOT_EPOCH=0, and all
255e111ed8SAndrew Rybchenko  * subsequent requests (until the next MC reboot) should contain NOT_EPOCH=1.
265e111ed8SAndrew Rybchenko  *
275e111ed8SAndrew Rybchenko  * After rebooting the MC will fail all requests with NOT_EPOCH=1 by writing a
285e111ed8SAndrew Rybchenko  * response with ERROR=1 and DATALEN=0 until a request is seen with NOT_EPOCH=0.
295e111ed8SAndrew Rybchenko  */
305e111ed8SAndrew Rybchenko 
315e111ed8SAndrew Rybchenko 
325e111ed8SAndrew Rybchenko 
335e111ed8SAndrew Rybchenko #if EFSYS_OPT_SIENA
345e111ed8SAndrew Rybchenko 
355e111ed8SAndrew Rybchenko static const efx_mcdi_ops_t	__efx_mcdi_siena_ops = {
365e111ed8SAndrew Rybchenko 	siena_mcdi_init,		/* emco_init */
375e111ed8SAndrew Rybchenko 	siena_mcdi_send_request,	/* emco_send_request */
385e111ed8SAndrew Rybchenko 	siena_mcdi_poll_reboot,		/* emco_poll_reboot */
395e111ed8SAndrew Rybchenko 	siena_mcdi_poll_response,	/* emco_poll_response */
405e111ed8SAndrew Rybchenko 	siena_mcdi_read_response,	/* emco_read_response */
415e111ed8SAndrew Rybchenko 	siena_mcdi_fini,		/* emco_fini */
425e111ed8SAndrew Rybchenko 	siena_mcdi_feature_supported,	/* emco_feature_supported */
435e111ed8SAndrew Rybchenko 	siena_mcdi_get_timeout,		/* emco_get_timeout */
445e111ed8SAndrew Rybchenko };
455e111ed8SAndrew Rybchenko 
465e111ed8SAndrew Rybchenko #endif	/* EFSYS_OPT_SIENA */
475e111ed8SAndrew Rybchenko 
485e111ed8SAndrew Rybchenko #if EFX_OPTS_EF10()
495e111ed8SAndrew Rybchenko 
505e111ed8SAndrew Rybchenko static const efx_mcdi_ops_t	__efx_mcdi_ef10_ops = {
515e111ed8SAndrew Rybchenko 	ef10_mcdi_init,			/* emco_init */
525e111ed8SAndrew Rybchenko 	ef10_mcdi_send_request,		/* emco_send_request */
535e111ed8SAndrew Rybchenko 	ef10_mcdi_poll_reboot,		/* emco_poll_reboot */
545e111ed8SAndrew Rybchenko 	ef10_mcdi_poll_response,	/* emco_poll_response */
555e111ed8SAndrew Rybchenko 	ef10_mcdi_read_response,	/* emco_read_response */
565e111ed8SAndrew Rybchenko 	ef10_mcdi_fini,			/* emco_fini */
575e111ed8SAndrew Rybchenko 	ef10_mcdi_feature_supported,	/* emco_feature_supported */
585e111ed8SAndrew Rybchenko 	ef10_mcdi_get_timeout,		/* emco_get_timeout */
595e111ed8SAndrew Rybchenko };
605e111ed8SAndrew Rybchenko 
615e111ed8SAndrew Rybchenko #endif	/* EFX_OPTS_EF10() */
625e111ed8SAndrew Rybchenko 
639b5b182dSAndrew Rybchenko #if EFSYS_OPT_RIVERHEAD
649b5b182dSAndrew Rybchenko 
659b5b182dSAndrew Rybchenko static const efx_mcdi_ops_t	__efx_mcdi_rhead_ops = {
669b5b182dSAndrew Rybchenko 	ef10_mcdi_init,			/* emco_init */
679b5b182dSAndrew Rybchenko 	ef10_mcdi_send_request,		/* emco_send_request */
689b5b182dSAndrew Rybchenko 	ef10_mcdi_poll_reboot,		/* emco_poll_reboot */
699b5b182dSAndrew Rybchenko 	ef10_mcdi_poll_response,	/* emco_poll_response */
709b5b182dSAndrew Rybchenko 	ef10_mcdi_read_response,	/* emco_read_response */
719b5b182dSAndrew Rybchenko 	ef10_mcdi_fini,			/* emco_fini */
729b5b182dSAndrew Rybchenko 	ef10_mcdi_feature_supported,	/* emco_feature_supported */
739b5b182dSAndrew Rybchenko 	ef10_mcdi_get_timeout,		/* emco_get_timeout */
749b5b182dSAndrew Rybchenko };
759b5b182dSAndrew Rybchenko 
769b5b182dSAndrew Rybchenko #endif	/* EFSYS_OPT_RIVERHEAD */
779b5b182dSAndrew Rybchenko 
785e111ed8SAndrew Rybchenko 
795e111ed8SAndrew Rybchenko 
805e111ed8SAndrew Rybchenko 	__checkReturn	efx_rc_t
815e111ed8SAndrew Rybchenko efx_mcdi_init(
825e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
835e111ed8SAndrew Rybchenko 	__in		const efx_mcdi_transport_t *emtp)
845e111ed8SAndrew Rybchenko {
855e111ed8SAndrew Rybchenko 	const efx_mcdi_ops_t *emcop;
865e111ed8SAndrew Rybchenko 	efx_rc_t rc;
875e111ed8SAndrew Rybchenko 
885e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
895e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_mod_flags, ==, 0);
905e111ed8SAndrew Rybchenko 
915e111ed8SAndrew Rybchenko 	switch (enp->en_family) {
925e111ed8SAndrew Rybchenko #if EFSYS_OPT_SIENA
935e111ed8SAndrew Rybchenko 	case EFX_FAMILY_SIENA:
945e111ed8SAndrew Rybchenko 		emcop = &__efx_mcdi_siena_ops;
955e111ed8SAndrew Rybchenko 		break;
965e111ed8SAndrew Rybchenko #endif	/* EFSYS_OPT_SIENA */
975e111ed8SAndrew Rybchenko 
985e111ed8SAndrew Rybchenko #if EFSYS_OPT_HUNTINGTON
995e111ed8SAndrew Rybchenko 	case EFX_FAMILY_HUNTINGTON:
1005e111ed8SAndrew Rybchenko 		emcop = &__efx_mcdi_ef10_ops;
1015e111ed8SAndrew Rybchenko 		break;
1025e111ed8SAndrew Rybchenko #endif	/* EFSYS_OPT_HUNTINGTON */
1035e111ed8SAndrew Rybchenko 
1045e111ed8SAndrew Rybchenko #if EFSYS_OPT_MEDFORD
1055e111ed8SAndrew Rybchenko 	case EFX_FAMILY_MEDFORD:
1065e111ed8SAndrew Rybchenko 		emcop = &__efx_mcdi_ef10_ops;
1075e111ed8SAndrew Rybchenko 		break;
1085e111ed8SAndrew Rybchenko #endif	/* EFSYS_OPT_MEDFORD */
1095e111ed8SAndrew Rybchenko 
1105e111ed8SAndrew Rybchenko #if EFSYS_OPT_MEDFORD2
1115e111ed8SAndrew Rybchenko 	case EFX_FAMILY_MEDFORD2:
1125e111ed8SAndrew Rybchenko 		emcop = &__efx_mcdi_ef10_ops;
1135e111ed8SAndrew Rybchenko 		break;
1145e111ed8SAndrew Rybchenko #endif	/* EFSYS_OPT_MEDFORD2 */
1155e111ed8SAndrew Rybchenko 
1169b5b182dSAndrew Rybchenko #if EFSYS_OPT_RIVERHEAD
1179b5b182dSAndrew Rybchenko 	case EFX_FAMILY_RIVERHEAD:
1189b5b182dSAndrew Rybchenko 		emcop = &__efx_mcdi_rhead_ops;
1199b5b182dSAndrew Rybchenko 		break;
1209b5b182dSAndrew Rybchenko #endif	/* EFSYS_OPT_RIVERHEAD */
1219b5b182dSAndrew Rybchenko 
1225e111ed8SAndrew Rybchenko 	default:
1235e111ed8SAndrew Rybchenko 		EFSYS_ASSERT(0);
1245e111ed8SAndrew Rybchenko 		rc = ENOTSUP;
1255e111ed8SAndrew Rybchenko 		goto fail1;
1265e111ed8SAndrew Rybchenko 	}
1275e111ed8SAndrew Rybchenko 
1285e111ed8SAndrew Rybchenko 	if (enp->en_features & EFX_FEATURE_MCDI_DMA) {
1295e111ed8SAndrew Rybchenko 		/* MCDI requires a DMA buffer in host memory */
1305e111ed8SAndrew Rybchenko 		if ((emtp == NULL) || (emtp->emt_dma_mem) == NULL) {
1315e111ed8SAndrew Rybchenko 			rc = EINVAL;
1325e111ed8SAndrew Rybchenko 			goto fail2;
1335e111ed8SAndrew Rybchenko 		}
1345e111ed8SAndrew Rybchenko 	}
1355e111ed8SAndrew Rybchenko 	enp->en_mcdi.em_emtp = emtp;
1365e111ed8SAndrew Rybchenko 
1375e111ed8SAndrew Rybchenko 	if (emcop != NULL && emcop->emco_init != NULL) {
1385e111ed8SAndrew Rybchenko 		if ((rc = emcop->emco_init(enp, emtp)) != 0)
1395e111ed8SAndrew Rybchenko 			goto fail3;
1405e111ed8SAndrew Rybchenko 	}
1415e111ed8SAndrew Rybchenko 
1425e111ed8SAndrew Rybchenko 	enp->en_mcdi.em_emcop = emcop;
1435e111ed8SAndrew Rybchenko 	enp->en_mod_flags |= EFX_MOD_MCDI;
1445e111ed8SAndrew Rybchenko 
1455e111ed8SAndrew Rybchenko 	return (0);
1465e111ed8SAndrew Rybchenko 
1475e111ed8SAndrew Rybchenko fail3:
1485e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail3);
1495e111ed8SAndrew Rybchenko fail2:
1505e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail2);
1515e111ed8SAndrew Rybchenko fail1:
1525e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1535e111ed8SAndrew Rybchenko 
1545e111ed8SAndrew Rybchenko 	enp->en_mcdi.em_emcop = NULL;
1555e111ed8SAndrew Rybchenko 	enp->en_mcdi.em_emtp = NULL;
1565e111ed8SAndrew Rybchenko 	enp->en_mod_flags &= ~EFX_MOD_MCDI;
1575e111ed8SAndrew Rybchenko 
1585e111ed8SAndrew Rybchenko 	return (rc);
1595e111ed8SAndrew Rybchenko }
1605e111ed8SAndrew Rybchenko 
1615e111ed8SAndrew Rybchenko 			void
1625e111ed8SAndrew Rybchenko efx_mcdi_fini(
1635e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp)
1645e111ed8SAndrew Rybchenko {
1655e111ed8SAndrew Rybchenko 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1665e111ed8SAndrew Rybchenko 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
1675e111ed8SAndrew Rybchenko 
1685e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
1695e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_mod_flags, ==, EFX_MOD_MCDI);
1705e111ed8SAndrew Rybchenko 
1715e111ed8SAndrew Rybchenko 	if (emcop != NULL && emcop->emco_fini != NULL)
1725e111ed8SAndrew Rybchenko 		emcop->emco_fini(enp);
1735e111ed8SAndrew Rybchenko 
1745e111ed8SAndrew Rybchenko 	emip->emi_port = 0;
1755e111ed8SAndrew Rybchenko 	emip->emi_aborted = 0;
1765e111ed8SAndrew Rybchenko 
1775e111ed8SAndrew Rybchenko 	enp->en_mcdi.em_emcop = NULL;
1785e111ed8SAndrew Rybchenko 	enp->en_mod_flags &= ~EFX_MOD_MCDI;
1795e111ed8SAndrew Rybchenko }
1805e111ed8SAndrew Rybchenko 
1815e111ed8SAndrew Rybchenko 			void
1825e111ed8SAndrew Rybchenko efx_mcdi_new_epoch(
1835e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp)
1845e111ed8SAndrew Rybchenko {
1855e111ed8SAndrew Rybchenko 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1865e111ed8SAndrew Rybchenko 	efsys_lock_state_t state;
1875e111ed8SAndrew Rybchenko 
1885e111ed8SAndrew Rybchenko 	/* Start a new epoch (allow fresh MCDI requests to succeed) */
1895e111ed8SAndrew Rybchenko 	EFSYS_LOCK(enp->en_eslp, state);
1905e111ed8SAndrew Rybchenko 	emip->emi_new_epoch = B_TRUE;
1915e111ed8SAndrew Rybchenko 	EFSYS_UNLOCK(enp->en_eslp, state);
1925e111ed8SAndrew Rybchenko }
1935e111ed8SAndrew Rybchenko 
1945e111ed8SAndrew Rybchenko static			void
1955e111ed8SAndrew Rybchenko efx_mcdi_send_request(
1965e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
1975e111ed8SAndrew Rybchenko 	__in		void *hdrp,
1985e111ed8SAndrew Rybchenko 	__in		size_t hdr_len,
1995e111ed8SAndrew Rybchenko 	__in		void *sdup,
2005e111ed8SAndrew Rybchenko 	__in		size_t sdu_len)
2015e111ed8SAndrew Rybchenko {
2025e111ed8SAndrew Rybchenko 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
2035e111ed8SAndrew Rybchenko 
2045e111ed8SAndrew Rybchenko 	emcop->emco_send_request(enp, hdrp, hdr_len, sdup, sdu_len);
2055e111ed8SAndrew Rybchenko }
2065e111ed8SAndrew Rybchenko 
2075e111ed8SAndrew Rybchenko static			efx_rc_t
2085e111ed8SAndrew Rybchenko efx_mcdi_poll_reboot(
2095e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp)
2105e111ed8SAndrew Rybchenko {
2115e111ed8SAndrew Rybchenko 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
2125e111ed8SAndrew Rybchenko 	efx_rc_t rc;
2135e111ed8SAndrew Rybchenko 
2145e111ed8SAndrew Rybchenko 	rc = emcop->emco_poll_reboot(enp);
2155e111ed8SAndrew Rybchenko 	return (rc);
2165e111ed8SAndrew Rybchenko }
2175e111ed8SAndrew Rybchenko 
2185e111ed8SAndrew Rybchenko static			boolean_t
2195e111ed8SAndrew Rybchenko efx_mcdi_poll_response(
2205e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp)
2215e111ed8SAndrew Rybchenko {
2225e111ed8SAndrew Rybchenko 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
2235e111ed8SAndrew Rybchenko 	boolean_t available;
2245e111ed8SAndrew Rybchenko 
2255e111ed8SAndrew Rybchenko 	available = emcop->emco_poll_response(enp);
2265e111ed8SAndrew Rybchenko 	return (available);
2275e111ed8SAndrew Rybchenko }
2285e111ed8SAndrew Rybchenko 
2295e111ed8SAndrew Rybchenko static			void
2305e111ed8SAndrew Rybchenko efx_mcdi_read_response(
2315e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
2325e111ed8SAndrew Rybchenko 	__out		void *bufferp,
2335e111ed8SAndrew Rybchenko 	__in		size_t offset,
2345e111ed8SAndrew Rybchenko 	__in		size_t length)
2355e111ed8SAndrew Rybchenko {
2365e111ed8SAndrew Rybchenko 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
2375e111ed8SAndrew Rybchenko 
2385e111ed8SAndrew Rybchenko 	emcop->emco_read_response(enp, bufferp, offset, length);
2395e111ed8SAndrew Rybchenko }
2405e111ed8SAndrew Rybchenko 
2415e111ed8SAndrew Rybchenko 			void
2425e111ed8SAndrew Rybchenko efx_mcdi_request_start(
2435e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
2445e111ed8SAndrew Rybchenko 	__in		efx_mcdi_req_t *emrp,
2455e111ed8SAndrew Rybchenko 	__in		boolean_t ev_cpl)
2465e111ed8SAndrew Rybchenko {
2475e111ed8SAndrew Rybchenko #if EFSYS_OPT_MCDI_LOGGING
2485e111ed8SAndrew Rybchenko 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
2495e111ed8SAndrew Rybchenko #endif
2505e111ed8SAndrew Rybchenko 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2515e111ed8SAndrew Rybchenko 	efx_dword_t hdr[2];
2525e111ed8SAndrew Rybchenko 	size_t hdr_len;
2535e111ed8SAndrew Rybchenko 	unsigned int max_version;
2545e111ed8SAndrew Rybchenko 	unsigned int seq;
2555e111ed8SAndrew Rybchenko 	unsigned int xflags;
2565e111ed8SAndrew Rybchenko 	boolean_t new_epoch;
2575e111ed8SAndrew Rybchenko 	efsys_lock_state_t state;
2585e111ed8SAndrew Rybchenko 
2595e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
2605e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
2615e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
2625e111ed8SAndrew Rybchenko 
2635e111ed8SAndrew Rybchenko 	/*
2645e111ed8SAndrew Rybchenko 	 * efx_mcdi_request_start() is naturally serialised against both
2655e111ed8SAndrew Rybchenko 	 * efx_mcdi_request_poll() and efx_mcdi_ev_cpl()/efx_mcdi_ev_death(),
2665e111ed8SAndrew Rybchenko 	 * by virtue of there only being one outstanding MCDI request.
2675e111ed8SAndrew Rybchenko 	 * Unfortunately, upper layers may also call efx_mcdi_request_abort()
2685e111ed8SAndrew Rybchenko 	 * at any time, to timeout a pending mcdi request, That request may
2695e111ed8SAndrew Rybchenko 	 * then subsequently complete, meaning efx_mcdi_ev_cpl() or
2705e111ed8SAndrew Rybchenko 	 * efx_mcdi_ev_death() may end up running in parallel with
2715e111ed8SAndrew Rybchenko 	 * efx_mcdi_request_start(). This race is handled by ensuring that
2725e111ed8SAndrew Rybchenko 	 * %emi_pending_req, %emi_ev_cpl and %emi_seq are protected by the
2735e111ed8SAndrew Rybchenko 	 * en_eslp lock.
2745e111ed8SAndrew Rybchenko 	 */
2755e111ed8SAndrew Rybchenko 	EFSYS_LOCK(enp->en_eslp, state);
2765e111ed8SAndrew Rybchenko 	EFSYS_ASSERT(emip->emi_pending_req == NULL);
2775e111ed8SAndrew Rybchenko 	emip->emi_pending_req = emrp;
2785e111ed8SAndrew Rybchenko 	emip->emi_ev_cpl = ev_cpl;
2795e111ed8SAndrew Rybchenko 	emip->emi_poll_cnt = 0;
2805e111ed8SAndrew Rybchenko 	seq = emip->emi_seq++ & EFX_MASK32(MCDI_HEADER_SEQ);
2815e111ed8SAndrew Rybchenko 	new_epoch = emip->emi_new_epoch;
2825e111ed8SAndrew Rybchenko 	max_version = emip->emi_max_version;
2835e111ed8SAndrew Rybchenko 	EFSYS_UNLOCK(enp->en_eslp, state);
2845e111ed8SAndrew Rybchenko 
2855e111ed8SAndrew Rybchenko 	xflags = 0;
2865e111ed8SAndrew Rybchenko 	if (ev_cpl)
2875e111ed8SAndrew Rybchenko 		xflags |= MCDI_HEADER_XFLAGS_EVREQ;
2885e111ed8SAndrew Rybchenko 
2895e111ed8SAndrew Rybchenko 	/*
2905e111ed8SAndrew Rybchenko 	 * Huntington firmware supports MCDIv2, but the Huntington BootROM only
2915e111ed8SAndrew Rybchenko 	 * supports MCDIv1. Use MCDIv1 headers for MCDIv1 commands where
2925e111ed8SAndrew Rybchenko 	 * possible to support this.
2935e111ed8SAndrew Rybchenko 	 */
2945e111ed8SAndrew Rybchenko 	if ((max_version >= 2) &&
2955e111ed8SAndrew Rybchenko 	    ((emrp->emr_cmd > MC_CMD_CMD_SPACE_ESCAPE_7) ||
2965e111ed8SAndrew Rybchenko 	    (emrp->emr_in_length > MCDI_CTL_SDU_LEN_MAX_V1) ||
2975e111ed8SAndrew Rybchenko 	    (emrp->emr_out_length > MCDI_CTL_SDU_LEN_MAX_V1))) {
2985e111ed8SAndrew Rybchenko 		/* Construct MCDI v2 header */
2995e111ed8SAndrew Rybchenko 		hdr_len = sizeof (hdr);
3005e111ed8SAndrew Rybchenko 		EFX_POPULATE_DWORD_8(hdr[0],
3015e111ed8SAndrew Rybchenko 		    MCDI_HEADER_CODE, MC_CMD_V2_EXTN,
3025e111ed8SAndrew Rybchenko 		    MCDI_HEADER_RESYNC, 1,
3035e111ed8SAndrew Rybchenko 		    MCDI_HEADER_DATALEN, 0,
3045e111ed8SAndrew Rybchenko 		    MCDI_HEADER_SEQ, seq,
3055e111ed8SAndrew Rybchenko 		    MCDI_HEADER_NOT_EPOCH, new_epoch ? 0 : 1,
3065e111ed8SAndrew Rybchenko 		    MCDI_HEADER_ERROR, 0,
3075e111ed8SAndrew Rybchenko 		    MCDI_HEADER_RESPONSE, 0,
3085e111ed8SAndrew Rybchenko 		    MCDI_HEADER_XFLAGS, xflags);
3095e111ed8SAndrew Rybchenko 
3105e111ed8SAndrew Rybchenko 		EFX_POPULATE_DWORD_2(hdr[1],
3115e111ed8SAndrew Rybchenko 		    MC_CMD_V2_EXTN_IN_EXTENDED_CMD, emrp->emr_cmd,
3125e111ed8SAndrew Rybchenko 		    MC_CMD_V2_EXTN_IN_ACTUAL_LEN, emrp->emr_in_length);
3135e111ed8SAndrew Rybchenko 	} else {
3145e111ed8SAndrew Rybchenko 		/* Construct MCDI v1 header */
3155e111ed8SAndrew Rybchenko 		hdr_len = sizeof (hdr[0]);
3165e111ed8SAndrew Rybchenko 		EFX_POPULATE_DWORD_8(hdr[0],
3175e111ed8SAndrew Rybchenko 		    MCDI_HEADER_CODE, emrp->emr_cmd,
3185e111ed8SAndrew Rybchenko 		    MCDI_HEADER_RESYNC, 1,
3195e111ed8SAndrew Rybchenko 		    MCDI_HEADER_DATALEN, emrp->emr_in_length,
3205e111ed8SAndrew Rybchenko 		    MCDI_HEADER_SEQ, seq,
3215e111ed8SAndrew Rybchenko 		    MCDI_HEADER_NOT_EPOCH, new_epoch ? 0 : 1,
3225e111ed8SAndrew Rybchenko 		    MCDI_HEADER_ERROR, 0,
3235e111ed8SAndrew Rybchenko 		    MCDI_HEADER_RESPONSE, 0,
3245e111ed8SAndrew Rybchenko 		    MCDI_HEADER_XFLAGS, xflags);
3255e111ed8SAndrew Rybchenko 	}
3265e111ed8SAndrew Rybchenko 
3275e111ed8SAndrew Rybchenko #if EFSYS_OPT_MCDI_LOGGING
3285e111ed8SAndrew Rybchenko 	if (emtp->emt_logger != NULL) {
3295e111ed8SAndrew Rybchenko 		emtp->emt_logger(emtp->emt_context, EFX_LOG_MCDI_REQUEST,
3305e111ed8SAndrew Rybchenko 		    &hdr[0], hdr_len,
3315e111ed8SAndrew Rybchenko 		    emrp->emr_in_buf, emrp->emr_in_length);
3325e111ed8SAndrew Rybchenko 	}
3335e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_MCDI_LOGGING */
3345e111ed8SAndrew Rybchenko 
3355e111ed8SAndrew Rybchenko 	efx_mcdi_send_request(enp, &hdr[0], hdr_len,
3365e111ed8SAndrew Rybchenko 	    emrp->emr_in_buf, emrp->emr_in_length);
3375e111ed8SAndrew Rybchenko }
3385e111ed8SAndrew Rybchenko 
3395e111ed8SAndrew Rybchenko 
3405e111ed8SAndrew Rybchenko static			void
3415e111ed8SAndrew Rybchenko efx_mcdi_read_response_header(
3425e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
3435e111ed8SAndrew Rybchenko 	__inout		efx_mcdi_req_t *emrp)
3445e111ed8SAndrew Rybchenko {
3455e111ed8SAndrew Rybchenko #if EFSYS_OPT_MCDI_LOGGING
3465e111ed8SAndrew Rybchenko 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
3475e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_MCDI_LOGGING */
3485e111ed8SAndrew Rybchenko 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
3495e111ed8SAndrew Rybchenko 	efx_dword_t hdr[2];
3505e111ed8SAndrew Rybchenko 	unsigned int hdr_len;
3515e111ed8SAndrew Rybchenko 	unsigned int data_len;
3525e111ed8SAndrew Rybchenko 	unsigned int seq;
3535e111ed8SAndrew Rybchenko 	unsigned int cmd;
3545e111ed8SAndrew Rybchenko 	unsigned int error;
3555e111ed8SAndrew Rybchenko 	efx_rc_t rc;
3565e111ed8SAndrew Rybchenko 
3575e111ed8SAndrew Rybchenko 	EFSYS_ASSERT(emrp != NULL);
3585e111ed8SAndrew Rybchenko 
3595e111ed8SAndrew Rybchenko 	efx_mcdi_read_response(enp, &hdr[0], 0, sizeof (hdr[0]));
3605e111ed8SAndrew Rybchenko 	hdr_len = sizeof (hdr[0]);
3615e111ed8SAndrew Rybchenko 
3625e111ed8SAndrew Rybchenko 	cmd = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_CODE);
3635e111ed8SAndrew Rybchenko 	seq = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_SEQ);
3645e111ed8SAndrew Rybchenko 	error = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_ERROR);
3655e111ed8SAndrew Rybchenko 
3665e111ed8SAndrew Rybchenko 	if (cmd != MC_CMD_V2_EXTN) {
3675e111ed8SAndrew Rybchenko 		data_len = EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_DATALEN);
3685e111ed8SAndrew Rybchenko 	} else {
3695e111ed8SAndrew Rybchenko 		efx_mcdi_read_response(enp, &hdr[1], hdr_len, sizeof (hdr[1]));
3705e111ed8SAndrew Rybchenko 		hdr_len += sizeof (hdr[1]);
3715e111ed8SAndrew Rybchenko 
3725e111ed8SAndrew Rybchenko 		cmd = EFX_DWORD_FIELD(hdr[1], MC_CMD_V2_EXTN_IN_EXTENDED_CMD);
3735e111ed8SAndrew Rybchenko 		data_len =
3745e111ed8SAndrew Rybchenko 		    EFX_DWORD_FIELD(hdr[1], MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
3755e111ed8SAndrew Rybchenko 	}
3765e111ed8SAndrew Rybchenko 
3775e111ed8SAndrew Rybchenko 	if (error && (data_len == 0)) {
3785e111ed8SAndrew Rybchenko 		/* The MC has rebooted since the request was sent. */
3795e111ed8SAndrew Rybchenko 		EFSYS_SPIN(EFX_MCDI_STATUS_SLEEP_US);
3805e111ed8SAndrew Rybchenko 		efx_mcdi_poll_reboot(enp);
3815e111ed8SAndrew Rybchenko 		rc = EIO;
3825e111ed8SAndrew Rybchenko 		goto fail1;
3835e111ed8SAndrew Rybchenko 	}
3845e111ed8SAndrew Rybchenko #if EFSYS_OPT_MCDI_PROXY_AUTH_SERVER
3855e111ed8SAndrew Rybchenko 	if (((cmd != emrp->emr_cmd) && (emrp->emr_cmd != MC_CMD_PROXY_CMD)) ||
3865e111ed8SAndrew Rybchenko #else
3875e111ed8SAndrew Rybchenko 	if ((cmd != emrp->emr_cmd) ||
3885e111ed8SAndrew Rybchenko #endif
3895e111ed8SAndrew Rybchenko 	    (seq != ((emip->emi_seq - 1) & EFX_MASK32(MCDI_HEADER_SEQ)))) {
3905e111ed8SAndrew Rybchenko 		/* Response is for a different request */
3915e111ed8SAndrew Rybchenko 		rc = EIO;
3925e111ed8SAndrew Rybchenko 		goto fail2;
3935e111ed8SAndrew Rybchenko 	}
3945e111ed8SAndrew Rybchenko 	if (error) {
3955e111ed8SAndrew Rybchenko 		efx_dword_t err[2];
3965e111ed8SAndrew Rybchenko 		unsigned int err_len = MIN(data_len, sizeof (err));
3975e111ed8SAndrew Rybchenko 		int err_code = MC_CMD_ERR_EPROTO;
3985e111ed8SAndrew Rybchenko 		int err_arg = 0;
3995e111ed8SAndrew Rybchenko 
4005e111ed8SAndrew Rybchenko 		/* Read error code (and arg num for MCDI v2 commands) */
4015e111ed8SAndrew Rybchenko 		efx_mcdi_read_response(enp, &err, hdr_len, err_len);
4025e111ed8SAndrew Rybchenko 
4035e111ed8SAndrew Rybchenko 		if (err_len >= (MC_CMD_ERR_CODE_OFST + sizeof (efx_dword_t)))
4045e111ed8SAndrew Rybchenko 			err_code = EFX_DWORD_FIELD(err[0], EFX_DWORD_0);
4055e111ed8SAndrew Rybchenko #ifdef WITH_MCDI_V2
4065e111ed8SAndrew Rybchenko 		if (err_len >= (MC_CMD_ERR_ARG_OFST + sizeof (efx_dword_t)))
4075e111ed8SAndrew Rybchenko 			err_arg = EFX_DWORD_FIELD(err[1], EFX_DWORD_0);
4085e111ed8SAndrew Rybchenko #endif
4095e111ed8SAndrew Rybchenko 		emrp->emr_err_code = err_code;
4105e111ed8SAndrew Rybchenko 		emrp->emr_err_arg = err_arg;
4115e111ed8SAndrew Rybchenko 
4125e111ed8SAndrew Rybchenko #if EFSYS_OPT_MCDI_PROXY_AUTH
4135e111ed8SAndrew Rybchenko 		if ((err_code == MC_CMD_ERR_PROXY_PENDING) &&
4145e111ed8SAndrew Rybchenko 		    (err_len == sizeof (err))) {
4155e111ed8SAndrew Rybchenko 			/*
4165e111ed8SAndrew Rybchenko 			 * The MCDI request would normally fail with EPERM, but
4175e111ed8SAndrew Rybchenko 			 * firmware has forwarded it to an authorization agent
4185e111ed8SAndrew Rybchenko 			 * attached to a privileged PF.
4195e111ed8SAndrew Rybchenko 			 *
4205e111ed8SAndrew Rybchenko 			 * Save the authorization request handle. The client
4215e111ed8SAndrew Rybchenko 			 * must wait for a PROXY_RESPONSE event, or timeout.
4225e111ed8SAndrew Rybchenko 			 */
4235e111ed8SAndrew Rybchenko 			emrp->emr_proxy_handle = err_arg;
4245e111ed8SAndrew Rybchenko 		}
4255e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_MCDI_PROXY_AUTH */
4265e111ed8SAndrew Rybchenko 
4275e111ed8SAndrew Rybchenko #if EFSYS_OPT_MCDI_LOGGING
4285e111ed8SAndrew Rybchenko 		if (emtp->emt_logger != NULL) {
4295e111ed8SAndrew Rybchenko 			emtp->emt_logger(emtp->emt_context,
4305e111ed8SAndrew Rybchenko 			    EFX_LOG_MCDI_RESPONSE,
4315e111ed8SAndrew Rybchenko 			    &hdr[0], hdr_len,
4325e111ed8SAndrew Rybchenko 			    &err[0], err_len);
4335e111ed8SAndrew Rybchenko 		}
4345e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_MCDI_LOGGING */
4355e111ed8SAndrew Rybchenko 
4365e111ed8SAndrew Rybchenko 		if (!emrp->emr_quiet) {
4375e111ed8SAndrew Rybchenko 			EFSYS_PROBE3(mcdi_err_arg, int, emrp->emr_cmd,
4385e111ed8SAndrew Rybchenko 			    int, err_code, int, err_arg);
4395e111ed8SAndrew Rybchenko 		}
4405e111ed8SAndrew Rybchenko 
4415e111ed8SAndrew Rybchenko 		rc = efx_mcdi_request_errcode(err_code);
4425e111ed8SAndrew Rybchenko 		goto fail3;
4435e111ed8SAndrew Rybchenko 	}
4445e111ed8SAndrew Rybchenko 
4455e111ed8SAndrew Rybchenko 	emrp->emr_rc = 0;
4465e111ed8SAndrew Rybchenko 	emrp->emr_out_length_used = data_len;
4475e111ed8SAndrew Rybchenko #if EFSYS_OPT_MCDI_PROXY_AUTH
4485e111ed8SAndrew Rybchenko 	emrp->emr_proxy_handle = 0;
4495e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_MCDI_PROXY_AUTH */
4505e111ed8SAndrew Rybchenko 	return;
4515e111ed8SAndrew Rybchenko 
4525e111ed8SAndrew Rybchenko fail3:
4535e111ed8SAndrew Rybchenko fail2:
4545e111ed8SAndrew Rybchenko fail1:
4555e111ed8SAndrew Rybchenko 	emrp->emr_rc = rc;
4565e111ed8SAndrew Rybchenko 	emrp->emr_out_length_used = 0;
4575e111ed8SAndrew Rybchenko }
4585e111ed8SAndrew Rybchenko 
4595e111ed8SAndrew Rybchenko static			void
4605e111ed8SAndrew Rybchenko efx_mcdi_finish_response(
4615e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
4625e111ed8SAndrew Rybchenko 	__in		efx_mcdi_req_t *emrp)
4635e111ed8SAndrew Rybchenko {
4645e111ed8SAndrew Rybchenko #if EFSYS_OPT_MCDI_LOGGING
4655e111ed8SAndrew Rybchenko 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
4665e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_MCDI_LOGGING */
4675e111ed8SAndrew Rybchenko 	efx_dword_t hdr[2];
4685e111ed8SAndrew Rybchenko 	unsigned int hdr_len;
4695e111ed8SAndrew Rybchenko 	size_t bytes;
4705e111ed8SAndrew Rybchenko 	unsigned int resp_off;
4715e111ed8SAndrew Rybchenko #if EFSYS_OPT_MCDI_PROXY_AUTH_SERVER
4725e111ed8SAndrew Rybchenko 	unsigned int resp_cmd;
4735e111ed8SAndrew Rybchenko 	boolean_t proxied_cmd_resp = B_FALSE;
4745e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_MCDI_PROXY_AUTH_SERVER */
4755e111ed8SAndrew Rybchenko 
4765e111ed8SAndrew Rybchenko 	if (emrp->emr_out_buf == NULL)
4775e111ed8SAndrew Rybchenko 		return;
4785e111ed8SAndrew Rybchenko 
4795e111ed8SAndrew Rybchenko 	/* Read the command header to detect MCDI response format */
4805e111ed8SAndrew Rybchenko 	hdr_len = sizeof (hdr[0]);
4815e111ed8SAndrew Rybchenko 	efx_mcdi_read_response(enp, &hdr[0], 0, hdr_len);
4825e111ed8SAndrew Rybchenko 	if (EFX_DWORD_FIELD(hdr[0], MCDI_HEADER_CODE) == MC_CMD_V2_EXTN) {
4835e111ed8SAndrew Rybchenko 		/*
4845e111ed8SAndrew Rybchenko 		 * Read the actual payload length. The length given in the event
4855e111ed8SAndrew Rybchenko 		 * is only correct for responses with the V1 format.
4865e111ed8SAndrew Rybchenko 		 */
4875e111ed8SAndrew Rybchenko 		efx_mcdi_read_response(enp, &hdr[1], hdr_len, sizeof (hdr[1]));
4885e111ed8SAndrew Rybchenko 		hdr_len += sizeof (hdr[1]);
4895e111ed8SAndrew Rybchenko 		resp_off = hdr_len;
4905e111ed8SAndrew Rybchenko 
4915e111ed8SAndrew Rybchenko 		emrp->emr_out_length_used = EFX_DWORD_FIELD(hdr[1],
4925e111ed8SAndrew Rybchenko 						MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
4935e111ed8SAndrew Rybchenko #if EFSYS_OPT_MCDI_PROXY_AUTH_SERVER
4945e111ed8SAndrew Rybchenko 		/*
4955e111ed8SAndrew Rybchenko 		 * A proxy MCDI command is executed by PF on behalf of
4965e111ed8SAndrew Rybchenko 		 * one of its VFs. The command to be proxied follows
4975e111ed8SAndrew Rybchenko 		 * immediately afterward in the host buffer.
4985e111ed8SAndrew Rybchenko 		 * PROXY_CMD inner call complete response should be copied to
4995e111ed8SAndrew Rybchenko 		 * output buffer so that it can be returned to the requesting
5005e111ed8SAndrew Rybchenko 		 * function in MC_CMD_PROXY_COMPLETE payload.
5015e111ed8SAndrew Rybchenko 		 */
5025e111ed8SAndrew Rybchenko 		resp_cmd =
5035e111ed8SAndrew Rybchenko 			EFX_DWORD_FIELD(hdr[1], MC_CMD_V2_EXTN_IN_EXTENDED_CMD);
5045e111ed8SAndrew Rybchenko 		proxied_cmd_resp = ((emrp->emr_cmd == MC_CMD_PROXY_CMD) &&
5055e111ed8SAndrew Rybchenko 					(resp_cmd != MC_CMD_PROXY_CMD));
5065e111ed8SAndrew Rybchenko 		if (proxied_cmd_resp) {
5075e111ed8SAndrew Rybchenko 			resp_off = 0;
5085e111ed8SAndrew Rybchenko 			emrp->emr_out_length_used += hdr_len;
5095e111ed8SAndrew Rybchenko 		}
5105e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_MCDI_PROXY_AUTH_SERVER */
5115e111ed8SAndrew Rybchenko 	} else {
5125e111ed8SAndrew Rybchenko 		resp_off = hdr_len;
5135e111ed8SAndrew Rybchenko 	}
5145e111ed8SAndrew Rybchenko 
5155e111ed8SAndrew Rybchenko 	/* Copy payload out into caller supplied buffer */
5165e111ed8SAndrew Rybchenko 	bytes = MIN(emrp->emr_out_length_used, emrp->emr_out_length);
5175e111ed8SAndrew Rybchenko 	efx_mcdi_read_response(enp, emrp->emr_out_buf, resp_off, bytes);
5185e111ed8SAndrew Rybchenko 
5195e111ed8SAndrew Rybchenko #if EFSYS_OPT_MCDI_LOGGING
5205e111ed8SAndrew Rybchenko 	if (emtp->emt_logger != NULL) {
5215e111ed8SAndrew Rybchenko 		emtp->emt_logger(emtp->emt_context,
5225e111ed8SAndrew Rybchenko 		    EFX_LOG_MCDI_RESPONSE,
5235e111ed8SAndrew Rybchenko 		    &hdr[0], hdr_len,
5245e111ed8SAndrew Rybchenko 		    emrp->emr_out_buf, bytes);
5255e111ed8SAndrew Rybchenko 	}
5265e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_MCDI_LOGGING */
5275e111ed8SAndrew Rybchenko }
5285e111ed8SAndrew Rybchenko 
5295e111ed8SAndrew Rybchenko 
5305e111ed8SAndrew Rybchenko 	__checkReturn	boolean_t
5315e111ed8SAndrew Rybchenko efx_mcdi_request_poll(
5325e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp)
5335e111ed8SAndrew Rybchenko {
5345e111ed8SAndrew Rybchenko 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
5355e111ed8SAndrew Rybchenko 	efx_mcdi_req_t *emrp;
5365e111ed8SAndrew Rybchenko 	efsys_lock_state_t state;
5375e111ed8SAndrew Rybchenko 	efx_rc_t rc;
5385e111ed8SAndrew Rybchenko 
5395e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
5405e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
5415e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
5425e111ed8SAndrew Rybchenko 
5435e111ed8SAndrew Rybchenko 	/* Serialise against post-watchdog efx_mcdi_ev* */
5445e111ed8SAndrew Rybchenko 	EFSYS_LOCK(enp->en_eslp, state);
5455e111ed8SAndrew Rybchenko 
5465e111ed8SAndrew Rybchenko 	EFSYS_ASSERT(emip->emi_pending_req != NULL);
5475e111ed8SAndrew Rybchenko 	EFSYS_ASSERT(!emip->emi_ev_cpl);
5485e111ed8SAndrew Rybchenko 	emrp = emip->emi_pending_req;
5495e111ed8SAndrew Rybchenko 
5505e111ed8SAndrew Rybchenko 	/* Check if hardware is unavailable */
5515e111ed8SAndrew Rybchenko 	if (efx_nic_hw_unavailable(enp)) {
5525e111ed8SAndrew Rybchenko 		EFSYS_UNLOCK(enp->en_eslp, state);
5535e111ed8SAndrew Rybchenko 		return (B_FALSE);
5545e111ed8SAndrew Rybchenko 	}
5555e111ed8SAndrew Rybchenko 
5565e111ed8SAndrew Rybchenko 	/* Check for reboot atomically w.r.t efx_mcdi_request_start */
5575e111ed8SAndrew Rybchenko 	if (emip->emi_poll_cnt++ == 0) {
5585e111ed8SAndrew Rybchenko 		if ((rc = efx_mcdi_poll_reboot(enp)) != 0) {
5595e111ed8SAndrew Rybchenko 			emip->emi_pending_req = NULL;
5605e111ed8SAndrew Rybchenko 			EFSYS_UNLOCK(enp->en_eslp, state);
5615e111ed8SAndrew Rybchenko 
5625e111ed8SAndrew Rybchenko 			/* Reboot/Assertion */
5635e111ed8SAndrew Rybchenko 			if (rc == EIO || rc == EINTR)
5645e111ed8SAndrew Rybchenko 				efx_mcdi_raise_exception(enp, emrp, rc);
5655e111ed8SAndrew Rybchenko 
5665e111ed8SAndrew Rybchenko 			goto fail1;
5675e111ed8SAndrew Rybchenko 		}
5685e111ed8SAndrew Rybchenko 	}
5695e111ed8SAndrew Rybchenko 
5705e111ed8SAndrew Rybchenko 	/* Check if a response is available */
5715e111ed8SAndrew Rybchenko 	if (efx_mcdi_poll_response(enp) == B_FALSE) {
5725e111ed8SAndrew Rybchenko 		EFSYS_UNLOCK(enp->en_eslp, state);
5735e111ed8SAndrew Rybchenko 		return (B_FALSE);
5745e111ed8SAndrew Rybchenko 	}
5755e111ed8SAndrew Rybchenko 
5765e111ed8SAndrew Rybchenko 	/* Read the response header */
5775e111ed8SAndrew Rybchenko 	efx_mcdi_read_response_header(enp, emrp);
5785e111ed8SAndrew Rybchenko 
5795e111ed8SAndrew Rybchenko 	/* Request complete */
5805e111ed8SAndrew Rybchenko 	emip->emi_pending_req = NULL;
5815e111ed8SAndrew Rybchenko 
5825e111ed8SAndrew Rybchenko 	/* Ensure stale MCDI requests fail after an MC reboot. */
5835e111ed8SAndrew Rybchenko 	emip->emi_new_epoch = B_FALSE;
5845e111ed8SAndrew Rybchenko 
5855e111ed8SAndrew Rybchenko 	EFSYS_UNLOCK(enp->en_eslp, state);
5865e111ed8SAndrew Rybchenko 
5875e111ed8SAndrew Rybchenko 	if ((rc = emrp->emr_rc) != 0)
5885e111ed8SAndrew Rybchenko 		goto fail2;
5895e111ed8SAndrew Rybchenko 
5905e111ed8SAndrew Rybchenko 	efx_mcdi_finish_response(enp, emrp);
5915e111ed8SAndrew Rybchenko 	return (B_TRUE);
5925e111ed8SAndrew Rybchenko 
5935e111ed8SAndrew Rybchenko fail2:
5945e111ed8SAndrew Rybchenko 	if (!emrp->emr_quiet)
5955e111ed8SAndrew Rybchenko 		EFSYS_PROBE(fail2);
5965e111ed8SAndrew Rybchenko fail1:
5975e111ed8SAndrew Rybchenko 	if (!emrp->emr_quiet)
5985e111ed8SAndrew Rybchenko 		EFSYS_PROBE1(fail1, efx_rc_t, rc);
5995e111ed8SAndrew Rybchenko 
6005e111ed8SAndrew Rybchenko 	return (B_TRUE);
6015e111ed8SAndrew Rybchenko }
6025e111ed8SAndrew Rybchenko 
6035e111ed8SAndrew Rybchenko 	__checkReturn	boolean_t
6045e111ed8SAndrew Rybchenko efx_mcdi_request_abort(
6055e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp)
6065e111ed8SAndrew Rybchenko {
6075e111ed8SAndrew Rybchenko 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
6085e111ed8SAndrew Rybchenko 	efx_mcdi_req_t *emrp;
6095e111ed8SAndrew Rybchenko 	boolean_t aborted;
6105e111ed8SAndrew Rybchenko 	efsys_lock_state_t state;
6115e111ed8SAndrew Rybchenko 
6125e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
6135e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
6145e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
6155e111ed8SAndrew Rybchenko 
6165e111ed8SAndrew Rybchenko 	/*
6175e111ed8SAndrew Rybchenko 	 * efx_mcdi_ev_* may have already completed this event, and be
6185e111ed8SAndrew Rybchenko 	 * spinning/blocked on the upper layer lock. So it *is* legitimate
6195e111ed8SAndrew Rybchenko 	 * to for emi_pending_req to be NULL. If there is a pending event
6205e111ed8SAndrew Rybchenko 	 * completed request, then provide a "credit" to allow
6215e111ed8SAndrew Rybchenko 	 * efx_mcdi_ev_cpl() to accept a single spurious completion.
6225e111ed8SAndrew Rybchenko 	 */
6235e111ed8SAndrew Rybchenko 	EFSYS_LOCK(enp->en_eslp, state);
6245e111ed8SAndrew Rybchenko 	emrp = emip->emi_pending_req;
6255e111ed8SAndrew Rybchenko 	aborted = (emrp != NULL);
6265e111ed8SAndrew Rybchenko 	if (aborted) {
6275e111ed8SAndrew Rybchenko 		emip->emi_pending_req = NULL;
6285e111ed8SAndrew Rybchenko 
6295e111ed8SAndrew Rybchenko 		/* Error the request */
6305e111ed8SAndrew Rybchenko 		emrp->emr_out_length_used = 0;
6315e111ed8SAndrew Rybchenko 		emrp->emr_rc = ETIMEDOUT;
6325e111ed8SAndrew Rybchenko 
6335e111ed8SAndrew Rybchenko 		/* Provide a credit for seqno/emr_pending_req mismatches */
6345e111ed8SAndrew Rybchenko 		if (emip->emi_ev_cpl)
6355e111ed8SAndrew Rybchenko 			++emip->emi_aborted;
6365e111ed8SAndrew Rybchenko 
6375e111ed8SAndrew Rybchenko 		/*
6385e111ed8SAndrew Rybchenko 		 * The upper layer has called us, so we don't
6395e111ed8SAndrew Rybchenko 		 * need to complete the request.
6405e111ed8SAndrew Rybchenko 		 */
6415e111ed8SAndrew Rybchenko 	}
6425e111ed8SAndrew Rybchenko 	EFSYS_UNLOCK(enp->en_eslp, state);
6435e111ed8SAndrew Rybchenko 
6445e111ed8SAndrew Rybchenko 	return (aborted);
6455e111ed8SAndrew Rybchenko }
6465e111ed8SAndrew Rybchenko 
6475e111ed8SAndrew Rybchenko 			void
6485e111ed8SAndrew Rybchenko efx_mcdi_get_timeout(
6495e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
6505e111ed8SAndrew Rybchenko 	__in		efx_mcdi_req_t *emrp,
6515e111ed8SAndrew Rybchenko 	__out		uint32_t *timeoutp)
6525e111ed8SAndrew Rybchenko {
6535e111ed8SAndrew Rybchenko 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
6545e111ed8SAndrew Rybchenko 
6555e111ed8SAndrew Rybchenko 	emcop->emco_get_timeout(enp, emrp, timeoutp);
6565e111ed8SAndrew Rybchenko }
6575e111ed8SAndrew Rybchenko 
6585e111ed8SAndrew Rybchenko 	__checkReturn	efx_rc_t
6595e111ed8SAndrew Rybchenko efx_mcdi_request_errcode(
6605e111ed8SAndrew Rybchenko 	__in		unsigned int err)
6615e111ed8SAndrew Rybchenko {
6625e111ed8SAndrew Rybchenko 
6635e111ed8SAndrew Rybchenko 	switch (err) {
6645e111ed8SAndrew Rybchenko 		/* MCDI v1 */
6655e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_EPERM:
6665e111ed8SAndrew Rybchenko 		return (EACCES);
6675e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_ENOENT:
6685e111ed8SAndrew Rybchenko 		return (ENOENT);
6695e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_EINTR:
6705e111ed8SAndrew Rybchenko 		return (EINTR);
6715e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_EACCES:
6725e111ed8SAndrew Rybchenko 		return (EACCES);
6735e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_EBUSY:
6745e111ed8SAndrew Rybchenko 		return (EBUSY);
6755e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_EINVAL:
6765e111ed8SAndrew Rybchenko 		return (EINVAL);
6775e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_EDEADLK:
6785e111ed8SAndrew Rybchenko 		return (EDEADLK);
6795e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_ENOSYS:
6805e111ed8SAndrew Rybchenko 		return (ENOTSUP);
6815e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_ETIME:
6825e111ed8SAndrew Rybchenko 		return (ETIMEDOUT);
6835e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_ENOTSUP:
6845e111ed8SAndrew Rybchenko 		return (ENOTSUP);
6855e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_EALREADY:
6865e111ed8SAndrew Rybchenko 		return (EALREADY);
6875e111ed8SAndrew Rybchenko 
6885e111ed8SAndrew Rybchenko 		/* MCDI v2 */
6895e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_EEXIST:
6905e111ed8SAndrew Rybchenko 		return (EEXIST);
6915e111ed8SAndrew Rybchenko #ifdef MC_CMD_ERR_EAGAIN
6925e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_EAGAIN:
6935e111ed8SAndrew Rybchenko 		return (EAGAIN);
6945e111ed8SAndrew Rybchenko #endif
6955e111ed8SAndrew Rybchenko #ifdef MC_CMD_ERR_ENOSPC
6965e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_ENOSPC:
6975e111ed8SAndrew Rybchenko 		return (ENOSPC);
6985e111ed8SAndrew Rybchenko #endif
6995e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_ERANGE:
7005e111ed8SAndrew Rybchenko 		return (ERANGE);
7015e111ed8SAndrew Rybchenko 
7025e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_ALLOC_FAIL:
7035e111ed8SAndrew Rybchenko 		return (ENOMEM);
7045e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_NO_VADAPTOR:
7055e111ed8SAndrew Rybchenko 		return (ENOENT);
7065e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_NO_EVB_PORT:
7075e111ed8SAndrew Rybchenko 		return (ENOENT);
7085e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_NO_VSWITCH:
7095e111ed8SAndrew Rybchenko 		return (ENODEV);
7105e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_VLAN_LIMIT:
7115e111ed8SAndrew Rybchenko 		return (EINVAL);
7125e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_BAD_PCI_FUNC:
7135e111ed8SAndrew Rybchenko 		return (ENODEV);
7145e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_BAD_VLAN_MODE:
7155e111ed8SAndrew Rybchenko 		return (EINVAL);
7165e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_BAD_VSWITCH_TYPE:
7175e111ed8SAndrew Rybchenko 		return (EINVAL);
7185e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_BAD_VPORT_TYPE:
7195e111ed8SAndrew Rybchenko 		return (EINVAL);
7205e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_MAC_EXIST:
7215e111ed8SAndrew Rybchenko 		return (EEXIST);
7225e111ed8SAndrew Rybchenko 
7235e111ed8SAndrew Rybchenko 	case MC_CMD_ERR_PROXY_PENDING:
7245e111ed8SAndrew Rybchenko 		return (EAGAIN);
7255e111ed8SAndrew Rybchenko 
7265e111ed8SAndrew Rybchenko 	default:
7275e111ed8SAndrew Rybchenko 		EFSYS_PROBE1(mc_pcol_error, int, err);
7285e111ed8SAndrew Rybchenko 		return (EIO);
7295e111ed8SAndrew Rybchenko 	}
7305e111ed8SAndrew Rybchenko }
7315e111ed8SAndrew Rybchenko 
7325e111ed8SAndrew Rybchenko 			void
7335e111ed8SAndrew Rybchenko efx_mcdi_raise_exception(
7345e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
7355e111ed8SAndrew Rybchenko 	__in_opt	efx_mcdi_req_t *emrp,
7365e111ed8SAndrew Rybchenko 	__in		int rc)
7375e111ed8SAndrew Rybchenko {
7385e111ed8SAndrew Rybchenko 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
7395e111ed8SAndrew Rybchenko 	efx_mcdi_exception_t exception;
7405e111ed8SAndrew Rybchenko 
7415e111ed8SAndrew Rybchenko 	/* Reboot or Assertion failure only */
7425e111ed8SAndrew Rybchenko 	EFSYS_ASSERT(rc == EIO || rc == EINTR);
7435e111ed8SAndrew Rybchenko 
7445e111ed8SAndrew Rybchenko 	/*
7455e111ed8SAndrew Rybchenko 	 * If MC_CMD_REBOOT causes a reboot (dependent on parameters),
7465e111ed8SAndrew Rybchenko 	 * then the EIO is not worthy of an exception.
7475e111ed8SAndrew Rybchenko 	 */
7485e111ed8SAndrew Rybchenko 	if (emrp != NULL && emrp->emr_cmd == MC_CMD_REBOOT && rc == EIO)
7495e111ed8SAndrew Rybchenko 		return;
7505e111ed8SAndrew Rybchenko 
7515e111ed8SAndrew Rybchenko 	exception = (rc == EIO)
7525e111ed8SAndrew Rybchenko 		? EFX_MCDI_EXCEPTION_MC_REBOOT
7535e111ed8SAndrew Rybchenko 		: EFX_MCDI_EXCEPTION_MC_BADASSERT;
7545e111ed8SAndrew Rybchenko 
7555e111ed8SAndrew Rybchenko 	emtp->emt_exception(emtp->emt_context, exception);
7565e111ed8SAndrew Rybchenko }
7575e111ed8SAndrew Rybchenko 
7585e111ed8SAndrew Rybchenko 			void
7595e111ed8SAndrew Rybchenko efx_mcdi_execute(
7605e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
7615e111ed8SAndrew Rybchenko 	__inout		efx_mcdi_req_t *emrp)
7625e111ed8SAndrew Rybchenko {
7635e111ed8SAndrew Rybchenko 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
7645e111ed8SAndrew Rybchenko 
7655e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
7665e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
7675e111ed8SAndrew Rybchenko 
7685e111ed8SAndrew Rybchenko 	emrp->emr_quiet = B_FALSE;
7695e111ed8SAndrew Rybchenko 	emtp->emt_execute(emtp->emt_context, emrp);
7705e111ed8SAndrew Rybchenko }
7715e111ed8SAndrew Rybchenko 
7725e111ed8SAndrew Rybchenko 			void
7735e111ed8SAndrew Rybchenko efx_mcdi_execute_quiet(
7745e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
7755e111ed8SAndrew Rybchenko 	__inout		efx_mcdi_req_t *emrp)
7765e111ed8SAndrew Rybchenko {
7775e111ed8SAndrew Rybchenko 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
7785e111ed8SAndrew Rybchenko 
7795e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
7805e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
7815e111ed8SAndrew Rybchenko 
7825e111ed8SAndrew Rybchenko 	emrp->emr_quiet = B_TRUE;
7835e111ed8SAndrew Rybchenko 	emtp->emt_execute(emtp->emt_context, emrp);
7845e111ed8SAndrew Rybchenko }
7855e111ed8SAndrew Rybchenko 
7865e111ed8SAndrew Rybchenko 			void
7875e111ed8SAndrew Rybchenko efx_mcdi_ev_cpl(
7885e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
7895e111ed8SAndrew Rybchenko 	__in		unsigned int seq,
7905e111ed8SAndrew Rybchenko 	__in		unsigned int outlen,
7915e111ed8SAndrew Rybchenko 	__in		int errcode)
7925e111ed8SAndrew Rybchenko {
7935e111ed8SAndrew Rybchenko 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
7945e111ed8SAndrew Rybchenko 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
7955e111ed8SAndrew Rybchenko 	efx_mcdi_req_t *emrp;
7965e111ed8SAndrew Rybchenko 	efsys_lock_state_t state;
7975e111ed8SAndrew Rybchenko 
7985e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_MCDI);
7995e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
8005e111ed8SAndrew Rybchenko 
8015e111ed8SAndrew Rybchenko 	/*
8025e111ed8SAndrew Rybchenko 	 * Serialise against efx_mcdi_request_poll()/efx_mcdi_request_start()
8035e111ed8SAndrew Rybchenko 	 * when we're completing an aborted request.
8045e111ed8SAndrew Rybchenko 	 */
8055e111ed8SAndrew Rybchenko 	EFSYS_LOCK(enp->en_eslp, state);
8065e111ed8SAndrew Rybchenko 	if (emip->emi_pending_req == NULL || !emip->emi_ev_cpl ||
8075e111ed8SAndrew Rybchenko 	    (seq != ((emip->emi_seq - 1) & EFX_MASK32(MCDI_HEADER_SEQ)))) {
8085e111ed8SAndrew Rybchenko 		EFSYS_ASSERT(emip->emi_aborted > 0);
8095e111ed8SAndrew Rybchenko 		if (emip->emi_aborted > 0)
8105e111ed8SAndrew Rybchenko 			--emip->emi_aborted;
8115e111ed8SAndrew Rybchenko 		EFSYS_UNLOCK(enp->en_eslp, state);
8125e111ed8SAndrew Rybchenko 		return;
8135e111ed8SAndrew Rybchenko 	}
8145e111ed8SAndrew Rybchenko 
8155e111ed8SAndrew Rybchenko 	emrp = emip->emi_pending_req;
8165e111ed8SAndrew Rybchenko 	emip->emi_pending_req = NULL;
8175e111ed8SAndrew Rybchenko 	EFSYS_UNLOCK(enp->en_eslp, state);
8185e111ed8SAndrew Rybchenko 
8195e111ed8SAndrew Rybchenko 	if (emip->emi_max_version >= 2) {
8205e111ed8SAndrew Rybchenko 		/* MCDIv2 response details do not fit into an event. */
8215e111ed8SAndrew Rybchenko 		efx_mcdi_read_response_header(enp, emrp);
8225e111ed8SAndrew Rybchenko 	} else {
8235e111ed8SAndrew Rybchenko 		if (errcode != 0) {
8245e111ed8SAndrew Rybchenko 			if (!emrp->emr_quiet) {
8255e111ed8SAndrew Rybchenko 				EFSYS_PROBE2(mcdi_err, int, emrp->emr_cmd,
8265e111ed8SAndrew Rybchenko 				    int, errcode);
8275e111ed8SAndrew Rybchenko 			}
8285e111ed8SAndrew Rybchenko 			emrp->emr_out_length_used = 0;
8295e111ed8SAndrew Rybchenko 			emrp->emr_rc = efx_mcdi_request_errcode(errcode);
8305e111ed8SAndrew Rybchenko 		} else {
8315e111ed8SAndrew Rybchenko 			emrp->emr_out_length_used = outlen;
8325e111ed8SAndrew Rybchenko 			emrp->emr_rc = 0;
8335e111ed8SAndrew Rybchenko 		}
8345e111ed8SAndrew Rybchenko 	}
8355e111ed8SAndrew Rybchenko 	if (emrp->emr_rc == 0)
8365e111ed8SAndrew Rybchenko 		efx_mcdi_finish_response(enp, emrp);
8375e111ed8SAndrew Rybchenko 
8385e111ed8SAndrew Rybchenko 	emtp->emt_ev_cpl(emtp->emt_context);
8395e111ed8SAndrew Rybchenko }
8405e111ed8SAndrew Rybchenko 
8415e111ed8SAndrew Rybchenko #if EFSYS_OPT_MCDI_PROXY_AUTH
8425e111ed8SAndrew Rybchenko 
8435e111ed8SAndrew Rybchenko 	__checkReturn	efx_rc_t
8445e111ed8SAndrew Rybchenko efx_mcdi_get_proxy_handle(
8455e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
8465e111ed8SAndrew Rybchenko 	__in		efx_mcdi_req_t *emrp,
8475e111ed8SAndrew Rybchenko 	__out		uint32_t *handlep)
8485e111ed8SAndrew Rybchenko {
8495e111ed8SAndrew Rybchenko 	efx_rc_t rc;
8505e111ed8SAndrew Rybchenko 
8515e111ed8SAndrew Rybchenko 	_NOTE(ARGUNUSED(enp))
8525e111ed8SAndrew Rybchenko 
8535e111ed8SAndrew Rybchenko 	/*
8545e111ed8SAndrew Rybchenko 	 * Return proxy handle from MCDI request that returned with error
8555e111ed8SAndrew Rybchenko 	 * MC_MCD_ERR_PROXY_PENDING. This handle is used to wait for a matching
8565e111ed8SAndrew Rybchenko 	 * PROXY_RESPONSE event.
8575e111ed8SAndrew Rybchenko 	 */
8585e111ed8SAndrew Rybchenko 	if ((emrp == NULL) || (handlep == NULL)) {
8595e111ed8SAndrew Rybchenko 		rc = EINVAL;
8605e111ed8SAndrew Rybchenko 		goto fail1;
8615e111ed8SAndrew Rybchenko 	}
8625e111ed8SAndrew Rybchenko 	if ((emrp->emr_rc != 0) &&
8635e111ed8SAndrew Rybchenko 	    (emrp->emr_err_code == MC_CMD_ERR_PROXY_PENDING)) {
8645e111ed8SAndrew Rybchenko 		*handlep = emrp->emr_proxy_handle;
8655e111ed8SAndrew Rybchenko 		rc = 0;
8665e111ed8SAndrew Rybchenko 	} else {
8675e111ed8SAndrew Rybchenko 		*handlep = 0;
8685e111ed8SAndrew Rybchenko 		rc = ENOENT;
8695e111ed8SAndrew Rybchenko 	}
8705e111ed8SAndrew Rybchenko 	return (rc);
8715e111ed8SAndrew Rybchenko 
8725e111ed8SAndrew Rybchenko fail1:
8735e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
8745e111ed8SAndrew Rybchenko 	return (rc);
8755e111ed8SAndrew Rybchenko }
8765e111ed8SAndrew Rybchenko 
8775e111ed8SAndrew Rybchenko 			void
8785e111ed8SAndrew Rybchenko efx_mcdi_ev_proxy_response(
8795e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
8805e111ed8SAndrew Rybchenko 	__in		unsigned int handle,
8815e111ed8SAndrew Rybchenko 	__in		unsigned int status)
8825e111ed8SAndrew Rybchenko {
8835e111ed8SAndrew Rybchenko 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
8845e111ed8SAndrew Rybchenko 	efx_rc_t rc;
8855e111ed8SAndrew Rybchenko 
8865e111ed8SAndrew Rybchenko 	/*
8875e111ed8SAndrew Rybchenko 	 * Handle results of an authorization request for a privileged MCDI
8885e111ed8SAndrew Rybchenko 	 * command. If authorization was granted then we must re-issue the
8895e111ed8SAndrew Rybchenko 	 * original MCDI request. If authorization failed or timed out,
8905e111ed8SAndrew Rybchenko 	 * then the original MCDI request should be completed with the
8915e111ed8SAndrew Rybchenko 	 * result code from this event.
8925e111ed8SAndrew Rybchenko 	 */
8935e111ed8SAndrew Rybchenko 	rc = (status == 0) ? 0 : efx_mcdi_request_errcode(status);
8945e111ed8SAndrew Rybchenko 
8955e111ed8SAndrew Rybchenko 	emtp->emt_ev_proxy_response(emtp->emt_context, handle, rc);
8965e111ed8SAndrew Rybchenko }
8975e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_MCDI_PROXY_AUTH */
8985e111ed8SAndrew Rybchenko 
8995e111ed8SAndrew Rybchenko #if EFSYS_OPT_MCDI_PROXY_AUTH_SERVER
9005e111ed8SAndrew Rybchenko 			void
9015e111ed8SAndrew Rybchenko efx_mcdi_ev_proxy_request(
9025e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
9035e111ed8SAndrew Rybchenko 	__in		unsigned int index)
9045e111ed8SAndrew Rybchenko {
9055e111ed8SAndrew Rybchenko 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
9065e111ed8SAndrew Rybchenko 
9075e111ed8SAndrew Rybchenko 	if (emtp->emt_ev_proxy_request != NULL)
9085e111ed8SAndrew Rybchenko 		emtp->emt_ev_proxy_request(emtp->emt_context, index);
9095e111ed8SAndrew Rybchenko }
9105e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_MCDI_PROXY_AUTH_SERVER */
9115e111ed8SAndrew Rybchenko 			void
9125e111ed8SAndrew Rybchenko efx_mcdi_ev_death(
9135e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
9145e111ed8SAndrew Rybchenko 	__in		int rc)
9155e111ed8SAndrew Rybchenko {
9165e111ed8SAndrew Rybchenko 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
9175e111ed8SAndrew Rybchenko 	const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp;
9185e111ed8SAndrew Rybchenko 	efx_mcdi_req_t *emrp = NULL;
9195e111ed8SAndrew Rybchenko 	boolean_t ev_cpl;
9205e111ed8SAndrew Rybchenko 	efsys_lock_state_t state;
9215e111ed8SAndrew Rybchenko 
9225e111ed8SAndrew Rybchenko 	/*
9235e111ed8SAndrew Rybchenko 	 * The MCDI request (if there is one) has been terminated, either
9245e111ed8SAndrew Rybchenko 	 * by a BADASSERT or REBOOT event.
9255e111ed8SAndrew Rybchenko 	 *
9265e111ed8SAndrew Rybchenko 	 * If there is an outstanding event-completed MCDI operation, then we
9275e111ed8SAndrew Rybchenko 	 * will never receive the completion event (because both MCDI
9285e111ed8SAndrew Rybchenko 	 * completions and BADASSERT events are sent to the same evq). So
9295e111ed8SAndrew Rybchenko 	 * complete this MCDI op.
9305e111ed8SAndrew Rybchenko 	 *
9315e111ed8SAndrew Rybchenko 	 * This function might run in parallel with efx_mcdi_request_poll()
9325e111ed8SAndrew Rybchenko 	 * for poll completed mcdi requests, and also with
9335e111ed8SAndrew Rybchenko 	 * efx_mcdi_request_start() for post-watchdog completions.
9345e111ed8SAndrew Rybchenko 	 */
9355e111ed8SAndrew Rybchenko 	EFSYS_LOCK(enp->en_eslp, state);
9365e111ed8SAndrew Rybchenko 	emrp = emip->emi_pending_req;
9375e111ed8SAndrew Rybchenko 	ev_cpl = emip->emi_ev_cpl;
9385e111ed8SAndrew Rybchenko 	if (emrp != NULL && emip->emi_ev_cpl) {
9395e111ed8SAndrew Rybchenko 		emip->emi_pending_req = NULL;
9405e111ed8SAndrew Rybchenko 
9415e111ed8SAndrew Rybchenko 		emrp->emr_out_length_used = 0;
9425e111ed8SAndrew Rybchenko 		emrp->emr_rc = rc;
9435e111ed8SAndrew Rybchenko 		++emip->emi_aborted;
9445e111ed8SAndrew Rybchenko 	}
9455e111ed8SAndrew Rybchenko 
9465e111ed8SAndrew Rybchenko 	/*
9475e111ed8SAndrew Rybchenko 	 * Since we're running in parallel with a request, consume the
9485e111ed8SAndrew Rybchenko 	 * status word before dropping the lock.
9495e111ed8SAndrew Rybchenko 	 */
9505e111ed8SAndrew Rybchenko 	if (rc == EIO || rc == EINTR) {
9515e111ed8SAndrew Rybchenko 		EFSYS_SPIN(EFX_MCDI_STATUS_SLEEP_US);
9525e111ed8SAndrew Rybchenko 		(void) efx_mcdi_poll_reboot(enp);
9535e111ed8SAndrew Rybchenko 		emip->emi_new_epoch = B_TRUE;
9545e111ed8SAndrew Rybchenko 	}
9555e111ed8SAndrew Rybchenko 
9565e111ed8SAndrew Rybchenko 	EFSYS_UNLOCK(enp->en_eslp, state);
9575e111ed8SAndrew Rybchenko 
9585e111ed8SAndrew Rybchenko 	efx_mcdi_raise_exception(enp, emrp, rc);
9595e111ed8SAndrew Rybchenko 
9605e111ed8SAndrew Rybchenko 	if (emrp != NULL && ev_cpl)
9615e111ed8SAndrew Rybchenko 		emtp->emt_ev_cpl(emtp->emt_context);
9625e111ed8SAndrew Rybchenko }
9635e111ed8SAndrew Rybchenko 
9645e111ed8SAndrew Rybchenko 	__checkReturn		efx_rc_t
9655e111ed8SAndrew Rybchenko efx_mcdi_version(
9665e111ed8SAndrew Rybchenko 	__in			efx_nic_t *enp,
9675e111ed8SAndrew Rybchenko 	__out_ecount_opt(4)	uint16_t versionp[4],
9685e111ed8SAndrew Rybchenko 	__out_opt		uint32_t *buildp,
9695e111ed8SAndrew Rybchenko 	__out_opt		efx_mcdi_boot_t *statusp)
9705e111ed8SAndrew Rybchenko {
9715e111ed8SAndrew Rybchenko 	efx_mcdi_req_t req;
9725e111ed8SAndrew Rybchenko 	EFX_MCDI_DECLARE_BUF(payload,
9735e111ed8SAndrew Rybchenko 		MAX(MC_CMD_GET_VERSION_IN_LEN, MC_CMD_GET_BOOT_STATUS_IN_LEN),
9745e111ed8SAndrew Rybchenko 		MAX(MC_CMD_GET_VERSION_OUT_LEN,
9755e111ed8SAndrew Rybchenko 			MC_CMD_GET_BOOT_STATUS_OUT_LEN));
9765e111ed8SAndrew Rybchenko 	efx_word_t *ver_words;
9775e111ed8SAndrew Rybchenko 	uint16_t version[4];
9785e111ed8SAndrew Rybchenko 	uint32_t build;
9795e111ed8SAndrew Rybchenko 	efx_mcdi_boot_t status;
9805e111ed8SAndrew Rybchenko 	efx_rc_t rc;
9815e111ed8SAndrew Rybchenko 
9825e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_features, &, EFX_FEATURE_MCDI);
9835e111ed8SAndrew Rybchenko 
9845e111ed8SAndrew Rybchenko 	req.emr_cmd = MC_CMD_GET_VERSION;
9855e111ed8SAndrew Rybchenko 	req.emr_in_buf = payload;
9865e111ed8SAndrew Rybchenko 	req.emr_in_length = MC_CMD_GET_VERSION_IN_LEN;
9875e111ed8SAndrew Rybchenko 	req.emr_out_buf = payload;
9885e111ed8SAndrew Rybchenko 	req.emr_out_length = MC_CMD_GET_VERSION_OUT_LEN;
9895e111ed8SAndrew Rybchenko 
9905e111ed8SAndrew Rybchenko 	efx_mcdi_execute(enp, &req);
9915e111ed8SAndrew Rybchenko 
9925e111ed8SAndrew Rybchenko 	if (req.emr_rc != 0) {
9935e111ed8SAndrew Rybchenko 		rc = req.emr_rc;
9945e111ed8SAndrew Rybchenko 		goto fail1;
9955e111ed8SAndrew Rybchenko 	}
9965e111ed8SAndrew Rybchenko 
9975e111ed8SAndrew Rybchenko 	/* bootrom support */
9985e111ed8SAndrew Rybchenko 	if (req.emr_out_length_used == MC_CMD_GET_VERSION_V0_OUT_LEN) {
9995e111ed8SAndrew Rybchenko 		version[0] = version[1] = version[2] = version[3] = 0;
10005e111ed8SAndrew Rybchenko 		build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE);
10015e111ed8SAndrew Rybchenko 
10025e111ed8SAndrew Rybchenko 		goto version;
10035e111ed8SAndrew Rybchenko 	}
10045e111ed8SAndrew Rybchenko 
10055e111ed8SAndrew Rybchenko 	if (req.emr_out_length_used < MC_CMD_GET_VERSION_OUT_LEN) {
10065e111ed8SAndrew Rybchenko 		rc = EMSGSIZE;
10075e111ed8SAndrew Rybchenko 		goto fail2;
10085e111ed8SAndrew Rybchenko 	}
10095e111ed8SAndrew Rybchenko 
10105e111ed8SAndrew Rybchenko 	ver_words = MCDI_OUT2(req, efx_word_t, GET_VERSION_OUT_VERSION);
10115e111ed8SAndrew Rybchenko 	version[0] = EFX_WORD_FIELD(ver_words[0], EFX_WORD_0);
10125e111ed8SAndrew Rybchenko 	version[1] = EFX_WORD_FIELD(ver_words[1], EFX_WORD_0);
10135e111ed8SAndrew Rybchenko 	version[2] = EFX_WORD_FIELD(ver_words[2], EFX_WORD_0);
10145e111ed8SAndrew Rybchenko 	version[3] = EFX_WORD_FIELD(ver_words[3], EFX_WORD_0);
10155e111ed8SAndrew Rybchenko 	build = MCDI_OUT_DWORD(req, GET_VERSION_OUT_FIRMWARE);
10165e111ed8SAndrew Rybchenko 
10175e111ed8SAndrew Rybchenko version:
10185e111ed8SAndrew Rybchenko 	/* The bootrom doesn't understand BOOT_STATUS */
10195e111ed8SAndrew Rybchenko 	if (MC_FW_VERSION_IS_BOOTLOADER(build)) {
10205e111ed8SAndrew Rybchenko 		status = EFX_MCDI_BOOT_ROM;
10215e111ed8SAndrew Rybchenko 		goto out;
10225e111ed8SAndrew Rybchenko 	}
10235e111ed8SAndrew Rybchenko 
10245e111ed8SAndrew Rybchenko 	(void) memset(payload, 0, sizeof (payload));
10255e111ed8SAndrew Rybchenko 	req.emr_cmd = MC_CMD_GET_BOOT_STATUS;
10265e111ed8SAndrew Rybchenko 	req.emr_in_buf = payload;
10275e111ed8SAndrew Rybchenko 	req.emr_in_length = MC_CMD_GET_BOOT_STATUS_IN_LEN;
10285e111ed8SAndrew Rybchenko 	req.emr_out_buf = payload;
10295e111ed8SAndrew Rybchenko 	req.emr_out_length = MC_CMD_GET_BOOT_STATUS_OUT_LEN;
10305e111ed8SAndrew Rybchenko 
10315e111ed8SAndrew Rybchenko 	efx_mcdi_execute_quiet(enp, &req);
10325e111ed8SAndrew Rybchenko 
10335e111ed8SAndrew Rybchenko 	if (req.emr_rc == EACCES) {
10345e111ed8SAndrew Rybchenko 		/* Unprivileged functions cannot access BOOT_STATUS */
10355e111ed8SAndrew Rybchenko 		status = EFX_MCDI_BOOT_PRIMARY;
10365e111ed8SAndrew Rybchenko 		version[0] = version[1] = version[2] = version[3] = 0;
10375e111ed8SAndrew Rybchenko 		build = 0;
10385e111ed8SAndrew Rybchenko 		goto out;
10395e111ed8SAndrew Rybchenko 	}
10405e111ed8SAndrew Rybchenko 
10415e111ed8SAndrew Rybchenko 	if (req.emr_rc != 0) {
10425e111ed8SAndrew Rybchenko 		rc = req.emr_rc;
10435e111ed8SAndrew Rybchenko 		goto fail3;
10445e111ed8SAndrew Rybchenko 	}
10455e111ed8SAndrew Rybchenko 
10465e111ed8SAndrew Rybchenko 	if (req.emr_out_length_used < MC_CMD_GET_BOOT_STATUS_OUT_LEN) {
10475e111ed8SAndrew Rybchenko 		rc = EMSGSIZE;
10485e111ed8SAndrew Rybchenko 		goto fail4;
10495e111ed8SAndrew Rybchenko 	}
10505e111ed8SAndrew Rybchenko 
10515e111ed8SAndrew Rybchenko 	if (MCDI_OUT_DWORD_FIELD(req, GET_BOOT_STATUS_OUT_FLAGS,
10525e111ed8SAndrew Rybchenko 	    GET_BOOT_STATUS_OUT_FLAGS_PRIMARY))
10535e111ed8SAndrew Rybchenko 		status = EFX_MCDI_BOOT_PRIMARY;
10545e111ed8SAndrew Rybchenko 	else
10555e111ed8SAndrew Rybchenko 		status = EFX_MCDI_BOOT_SECONDARY;
10565e111ed8SAndrew Rybchenko 
10575e111ed8SAndrew Rybchenko out:
10585e111ed8SAndrew Rybchenko 	if (versionp != NULL)
10595e111ed8SAndrew Rybchenko 		memcpy(versionp, version, sizeof (version));
10605e111ed8SAndrew Rybchenko 	if (buildp != NULL)
10615e111ed8SAndrew Rybchenko 		*buildp = build;
10625e111ed8SAndrew Rybchenko 	if (statusp != NULL)
10635e111ed8SAndrew Rybchenko 		*statusp = status;
10645e111ed8SAndrew Rybchenko 
10655e111ed8SAndrew Rybchenko 	return (0);
10665e111ed8SAndrew Rybchenko 
10675e111ed8SAndrew Rybchenko fail4:
10685e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail4);
10695e111ed8SAndrew Rybchenko fail3:
10705e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail3);
10715e111ed8SAndrew Rybchenko fail2:
10725e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail2);
10735e111ed8SAndrew Rybchenko fail1:
10745e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
10755e111ed8SAndrew Rybchenko 
10765e111ed8SAndrew Rybchenko 	return (rc);
10775e111ed8SAndrew Rybchenko }
10785e111ed8SAndrew Rybchenko 
10795e111ed8SAndrew Rybchenko 	__checkReturn	efx_rc_t
10805e111ed8SAndrew Rybchenko efx_mcdi_get_capabilities(
10815e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
10825e111ed8SAndrew Rybchenko 	__out_opt	uint32_t *flagsp,
10835e111ed8SAndrew Rybchenko 	__out_opt	uint16_t *rx_dpcpu_fw_idp,
10845e111ed8SAndrew Rybchenko 	__out_opt	uint16_t *tx_dpcpu_fw_idp,
10855e111ed8SAndrew Rybchenko 	__out_opt	uint32_t *flags2p,
10865e111ed8SAndrew Rybchenko 	__out_opt	uint32_t *tso2ncp)
10875e111ed8SAndrew Rybchenko {
10885e111ed8SAndrew Rybchenko 	efx_mcdi_req_t req;
10895e111ed8SAndrew Rybchenko 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_CAPABILITIES_IN_LEN,
10905e111ed8SAndrew Rybchenko 		MC_CMD_GET_CAPABILITIES_V2_OUT_LEN);
10915e111ed8SAndrew Rybchenko 	boolean_t v2_capable;
10925e111ed8SAndrew Rybchenko 	efx_rc_t rc;
10935e111ed8SAndrew Rybchenko 
10945e111ed8SAndrew Rybchenko 	req.emr_cmd = MC_CMD_GET_CAPABILITIES;
10955e111ed8SAndrew Rybchenko 	req.emr_in_buf = payload;
10965e111ed8SAndrew Rybchenko 	req.emr_in_length = MC_CMD_GET_CAPABILITIES_IN_LEN;
10975e111ed8SAndrew Rybchenko 	req.emr_out_buf = payload;
10985e111ed8SAndrew Rybchenko 	req.emr_out_length = MC_CMD_GET_CAPABILITIES_V2_OUT_LEN;
10995e111ed8SAndrew Rybchenko 
11005e111ed8SAndrew Rybchenko 	efx_mcdi_execute_quiet(enp, &req);
11015e111ed8SAndrew Rybchenko 
11025e111ed8SAndrew Rybchenko 	if (req.emr_rc != 0) {
11035e111ed8SAndrew Rybchenko 		rc = req.emr_rc;
11045e111ed8SAndrew Rybchenko 		goto fail1;
11055e111ed8SAndrew Rybchenko 	}
11065e111ed8SAndrew Rybchenko 
11075e111ed8SAndrew Rybchenko 	if (req.emr_out_length_used < MC_CMD_GET_CAPABILITIES_OUT_LEN) {
11085e111ed8SAndrew Rybchenko 		rc = EMSGSIZE;
11095e111ed8SAndrew Rybchenko 		goto fail2;
11105e111ed8SAndrew Rybchenko 	}
11115e111ed8SAndrew Rybchenko 
11125e111ed8SAndrew Rybchenko 	if (flagsp != NULL)
11135e111ed8SAndrew Rybchenko 		*flagsp = MCDI_OUT_DWORD(req, GET_CAPABILITIES_OUT_FLAGS1);
11145e111ed8SAndrew Rybchenko 
11155e111ed8SAndrew Rybchenko 	if (rx_dpcpu_fw_idp != NULL)
11165e111ed8SAndrew Rybchenko 		*rx_dpcpu_fw_idp = MCDI_OUT_WORD(req,
11175e111ed8SAndrew Rybchenko 					GET_CAPABILITIES_OUT_RX_DPCPU_FW_ID);
11185e111ed8SAndrew Rybchenko 
11195e111ed8SAndrew Rybchenko 	if (tx_dpcpu_fw_idp != NULL)
11205e111ed8SAndrew Rybchenko 		*tx_dpcpu_fw_idp = MCDI_OUT_WORD(req,
11215e111ed8SAndrew Rybchenko 					GET_CAPABILITIES_OUT_TX_DPCPU_FW_ID);
11225e111ed8SAndrew Rybchenko 
11235e111ed8SAndrew Rybchenko 	if (req.emr_out_length_used < MC_CMD_GET_CAPABILITIES_V2_OUT_LEN)
11245e111ed8SAndrew Rybchenko 		v2_capable = B_FALSE;
11255e111ed8SAndrew Rybchenko 	else
11265e111ed8SAndrew Rybchenko 		v2_capable = B_TRUE;
11275e111ed8SAndrew Rybchenko 
11285e111ed8SAndrew Rybchenko 	if (flags2p != NULL) {
11295e111ed8SAndrew Rybchenko 		*flags2p = (v2_capable) ?
11305e111ed8SAndrew Rybchenko 			MCDI_OUT_DWORD(req, GET_CAPABILITIES_V2_OUT_FLAGS2) :
11315e111ed8SAndrew Rybchenko 			0;
11325e111ed8SAndrew Rybchenko 	}
11335e111ed8SAndrew Rybchenko 
11345e111ed8SAndrew Rybchenko 	if (tso2ncp != NULL) {
11355e111ed8SAndrew Rybchenko 		*tso2ncp = (v2_capable) ?
11365e111ed8SAndrew Rybchenko 			MCDI_OUT_WORD(req,
11375e111ed8SAndrew Rybchenko 				GET_CAPABILITIES_V2_OUT_TX_TSO_V2_N_CONTEXTS) :
11385e111ed8SAndrew Rybchenko 			0;
11395e111ed8SAndrew Rybchenko 	}
11405e111ed8SAndrew Rybchenko 
11415e111ed8SAndrew Rybchenko 	return (0);
11425e111ed8SAndrew Rybchenko 
11435e111ed8SAndrew Rybchenko fail2:
11445e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail2);
11455e111ed8SAndrew Rybchenko fail1:
11465e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
11475e111ed8SAndrew Rybchenko 
11485e111ed8SAndrew Rybchenko 	return (rc);
11495e111ed8SAndrew Rybchenko }
11505e111ed8SAndrew Rybchenko 
11515e111ed8SAndrew Rybchenko static	__checkReturn	efx_rc_t
11525e111ed8SAndrew Rybchenko efx_mcdi_do_reboot(
11535e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
11545e111ed8SAndrew Rybchenko 	__in		boolean_t after_assertion)
11555e111ed8SAndrew Rybchenko {
11565e111ed8SAndrew Rybchenko 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_REBOOT_IN_LEN,
11575e111ed8SAndrew Rybchenko 		MC_CMD_REBOOT_OUT_LEN);
11585e111ed8SAndrew Rybchenko 	efx_mcdi_req_t req;
11595e111ed8SAndrew Rybchenko 	efx_rc_t rc;
11605e111ed8SAndrew Rybchenko 
11615e111ed8SAndrew Rybchenko 	/*
11625e111ed8SAndrew Rybchenko 	 * We could require the caller to have caused en_mod_flags=0 to
11635e111ed8SAndrew Rybchenko 	 * call this function. This doesn't help the other port though,
11645e111ed8SAndrew Rybchenko 	 * who's about to get the MC ripped out from underneath them.
11655e111ed8SAndrew Rybchenko 	 * Since they have to cope with the subsequent fallout of MCDI
11665e111ed8SAndrew Rybchenko 	 * failures, we should as well.
11675e111ed8SAndrew Rybchenko 	 */
11685e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC);
11695e111ed8SAndrew Rybchenko 
11705e111ed8SAndrew Rybchenko 	req.emr_cmd = MC_CMD_REBOOT;
11715e111ed8SAndrew Rybchenko 	req.emr_in_buf = payload;
11725e111ed8SAndrew Rybchenko 	req.emr_in_length = MC_CMD_REBOOT_IN_LEN;
11735e111ed8SAndrew Rybchenko 	req.emr_out_buf = payload;
11745e111ed8SAndrew Rybchenko 	req.emr_out_length = MC_CMD_REBOOT_OUT_LEN;
11755e111ed8SAndrew Rybchenko 
11765e111ed8SAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, REBOOT_IN_FLAGS,
11775e111ed8SAndrew Rybchenko 	    (after_assertion ? MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION : 0));
11785e111ed8SAndrew Rybchenko 
11795e111ed8SAndrew Rybchenko 	efx_mcdi_execute_quiet(enp, &req);
11805e111ed8SAndrew Rybchenko 
11815e111ed8SAndrew Rybchenko 	if (req.emr_rc == EACCES) {
11825e111ed8SAndrew Rybchenko 		/* Unprivileged functions cannot reboot the MC. */
11835e111ed8SAndrew Rybchenko 		goto out;
11845e111ed8SAndrew Rybchenko 	}
11855e111ed8SAndrew Rybchenko 
11865e111ed8SAndrew Rybchenko 	/* A successful reboot request returns EIO. */
11875e111ed8SAndrew Rybchenko 	if (req.emr_rc != 0 && req.emr_rc != EIO) {
11885e111ed8SAndrew Rybchenko 		rc = req.emr_rc;
11895e111ed8SAndrew Rybchenko 		goto fail1;
11905e111ed8SAndrew Rybchenko 	}
11915e111ed8SAndrew Rybchenko 
11925e111ed8SAndrew Rybchenko out:
11935e111ed8SAndrew Rybchenko 	return (0);
11945e111ed8SAndrew Rybchenko 
11955e111ed8SAndrew Rybchenko fail1:
11965e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
11975e111ed8SAndrew Rybchenko 
11985e111ed8SAndrew Rybchenko 	return (rc);
11995e111ed8SAndrew Rybchenko }
12005e111ed8SAndrew Rybchenko 
12015e111ed8SAndrew Rybchenko 	__checkReturn	efx_rc_t
12025e111ed8SAndrew Rybchenko efx_mcdi_reboot(
12035e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp)
12045e111ed8SAndrew Rybchenko {
12055e111ed8SAndrew Rybchenko 	return (efx_mcdi_do_reboot(enp, B_FALSE));
12065e111ed8SAndrew Rybchenko }
12075e111ed8SAndrew Rybchenko 
12085e111ed8SAndrew Rybchenko 	__checkReturn	efx_rc_t
12095e111ed8SAndrew Rybchenko efx_mcdi_exit_assertion_handler(
12105e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp)
12115e111ed8SAndrew Rybchenko {
12125e111ed8SAndrew Rybchenko 	return (efx_mcdi_do_reboot(enp, B_TRUE));
12135e111ed8SAndrew Rybchenko }
12145e111ed8SAndrew Rybchenko 
12155e111ed8SAndrew Rybchenko 	__checkReturn	efx_rc_t
12165e111ed8SAndrew Rybchenko efx_mcdi_read_assertion(
12175e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp)
12185e111ed8SAndrew Rybchenko {
12195e111ed8SAndrew Rybchenko 	efx_mcdi_req_t req;
12205e111ed8SAndrew Rybchenko 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_ASSERTS_IN_LEN,
12215e111ed8SAndrew Rybchenko 		MC_CMD_GET_ASSERTS_OUT_LEN);
12225e111ed8SAndrew Rybchenko 	const char *reason;
12235e111ed8SAndrew Rybchenko 	unsigned int flags;
12245e111ed8SAndrew Rybchenko 	unsigned int index;
12255e111ed8SAndrew Rybchenko 	unsigned int ofst;
12265e111ed8SAndrew Rybchenko 	int retry;
12275e111ed8SAndrew Rybchenko 	efx_rc_t rc;
12285e111ed8SAndrew Rybchenko 
12295e111ed8SAndrew Rybchenko 	/*
12305e111ed8SAndrew Rybchenko 	 * Before we attempt to chat to the MC, we should verify that the MC
12315e111ed8SAndrew Rybchenko 	 * isn't in it's assertion handler, either due to a previous reboot,
12325e111ed8SAndrew Rybchenko 	 * or because we're reinitializing due to an eec_exception().
12335e111ed8SAndrew Rybchenko 	 *
12345e111ed8SAndrew Rybchenko 	 * Use GET_ASSERTS to read any assertion state that may be present.
12355e111ed8SAndrew Rybchenko 	 * Retry this command twice. Once because a boot-time assertion failure
12365e111ed8SAndrew Rybchenko 	 * might cause the 1st MCDI request to fail. And once again because
12375e111ed8SAndrew Rybchenko 	 * we might race with efx_mcdi_exit_assertion_handler() running on
12385e111ed8SAndrew Rybchenko 	 * partner port(s) on the same NIC.
12395e111ed8SAndrew Rybchenko 	 */
12405e111ed8SAndrew Rybchenko 	retry = 2;
12415e111ed8SAndrew Rybchenko 	do {
12425e111ed8SAndrew Rybchenko 		(void) memset(payload, 0, sizeof (payload));
12435e111ed8SAndrew Rybchenko 		req.emr_cmd = MC_CMD_GET_ASSERTS;
12445e111ed8SAndrew Rybchenko 		req.emr_in_buf = payload;
12455e111ed8SAndrew Rybchenko 		req.emr_in_length = MC_CMD_GET_ASSERTS_IN_LEN;
12465e111ed8SAndrew Rybchenko 		req.emr_out_buf = payload;
12475e111ed8SAndrew Rybchenko 		req.emr_out_length = MC_CMD_GET_ASSERTS_OUT_LEN;
12485e111ed8SAndrew Rybchenko 
12495e111ed8SAndrew Rybchenko 		MCDI_IN_SET_DWORD(req, GET_ASSERTS_IN_CLEAR, 1);
12505e111ed8SAndrew Rybchenko 		efx_mcdi_execute_quiet(enp, &req);
12515e111ed8SAndrew Rybchenko 
12525e111ed8SAndrew Rybchenko 	} while ((req.emr_rc == EINTR || req.emr_rc == EIO) && retry-- > 0);
12535e111ed8SAndrew Rybchenko 
12545e111ed8SAndrew Rybchenko 	if (req.emr_rc != 0) {
12555e111ed8SAndrew Rybchenko 		if (req.emr_rc == EACCES) {
12565e111ed8SAndrew Rybchenko 			/* Unprivileged functions cannot clear assertions. */
12575e111ed8SAndrew Rybchenko 			goto out;
12585e111ed8SAndrew Rybchenko 		}
12595e111ed8SAndrew Rybchenko 		rc = req.emr_rc;
12605e111ed8SAndrew Rybchenko 		goto fail1;
12615e111ed8SAndrew Rybchenko 	}
12625e111ed8SAndrew Rybchenko 
12635e111ed8SAndrew Rybchenko 	if (req.emr_out_length_used < MC_CMD_GET_ASSERTS_OUT_LEN) {
12645e111ed8SAndrew Rybchenko 		rc = EMSGSIZE;
12655e111ed8SAndrew Rybchenko 		goto fail2;
12665e111ed8SAndrew Rybchenko 	}
12675e111ed8SAndrew Rybchenko 
12685e111ed8SAndrew Rybchenko 	/* Print out any assertion state recorded */
12695e111ed8SAndrew Rybchenko 	flags = MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_GLOBAL_FLAGS);
12705e111ed8SAndrew Rybchenko 	if (flags == MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS)
12715e111ed8SAndrew Rybchenko 		return (0);
12725e111ed8SAndrew Rybchenko 
12735e111ed8SAndrew Rybchenko 	reason = (flags == MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL)
12745e111ed8SAndrew Rybchenko 		? "system-level assertion"
12755e111ed8SAndrew Rybchenko 		: (flags == MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL)
12765e111ed8SAndrew Rybchenko 		? "thread-level assertion"
12775e111ed8SAndrew Rybchenko 		: (flags == MC_CMD_GET_ASSERTS_FLAGS_WDOG_FIRED)
12785e111ed8SAndrew Rybchenko 		? "watchdog reset"
12795e111ed8SAndrew Rybchenko 		: (flags == MC_CMD_GET_ASSERTS_FLAGS_ADDR_TRAP)
12805e111ed8SAndrew Rybchenko 		? "illegal address trap"
12815e111ed8SAndrew Rybchenko 		: "unknown assertion";
12825e111ed8SAndrew Rybchenko 	EFSYS_PROBE3(mcpu_assertion,
12835e111ed8SAndrew Rybchenko 	    const char *, reason, unsigned int,
12845e111ed8SAndrew Rybchenko 	    MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_SAVED_PC_OFFS),
12855e111ed8SAndrew Rybchenko 	    unsigned int,
12865e111ed8SAndrew Rybchenko 	    MCDI_OUT_DWORD(req, GET_ASSERTS_OUT_THREAD_OFFS));
12875e111ed8SAndrew Rybchenko 
12885e111ed8SAndrew Rybchenko 	/* Print out the registers (r1 ... r31) */
12895e111ed8SAndrew Rybchenko 	ofst = MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST;
12905e111ed8SAndrew Rybchenko 	for (index = 1;
12915e111ed8SAndrew Rybchenko 		index < 1 + MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_NUM;
12925e111ed8SAndrew Rybchenko 		index++) {
12935e111ed8SAndrew Rybchenko 		EFSYS_PROBE2(mcpu_register, unsigned int, index, unsigned int,
12945e111ed8SAndrew Rybchenko 			    EFX_DWORD_FIELD(*MCDI_OUT(req, efx_dword_t, ofst),
12955e111ed8SAndrew Rybchenko 					    EFX_DWORD_0));
12965e111ed8SAndrew Rybchenko 		ofst += sizeof (efx_dword_t);
12975e111ed8SAndrew Rybchenko 	}
12985e111ed8SAndrew Rybchenko 	EFSYS_ASSERT(ofst <= MC_CMD_GET_ASSERTS_OUT_LEN);
12995e111ed8SAndrew Rybchenko 
13005e111ed8SAndrew Rybchenko out:
13015e111ed8SAndrew Rybchenko 	return (0);
13025e111ed8SAndrew Rybchenko 
13035e111ed8SAndrew Rybchenko fail2:
13045e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail2);
13055e111ed8SAndrew Rybchenko fail1:
13065e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
13075e111ed8SAndrew Rybchenko 
13085e111ed8SAndrew Rybchenko 	return (rc);
13095e111ed8SAndrew Rybchenko }
13105e111ed8SAndrew Rybchenko 
13115e111ed8SAndrew Rybchenko 
13125e111ed8SAndrew Rybchenko /*
13135e111ed8SAndrew Rybchenko  * Internal routines for for specific MCDI requests.
13145e111ed8SAndrew Rybchenko  */
13155e111ed8SAndrew Rybchenko 
13165e111ed8SAndrew Rybchenko 	__checkReturn	efx_rc_t
13175e111ed8SAndrew Rybchenko efx_mcdi_drv_attach(
13185e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
13195e111ed8SAndrew Rybchenko 	__in		boolean_t attach)
13205e111ed8SAndrew Rybchenko {
13215e111ed8SAndrew Rybchenko 	efx_mcdi_req_t req;
13225e111ed8SAndrew Rybchenko 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_DRV_ATTACH_IN_V2_LEN,
13235e111ed8SAndrew Rybchenko 		MC_CMD_DRV_ATTACH_EXT_OUT_LEN);
13245e111ed8SAndrew Rybchenko 	efx_rc_t rc;
13255e111ed8SAndrew Rybchenko 
13265e111ed8SAndrew Rybchenko 	req.emr_cmd = MC_CMD_DRV_ATTACH;
13275e111ed8SAndrew Rybchenko 	req.emr_in_buf = payload;
13285e111ed8SAndrew Rybchenko 	if (enp->en_drv_version[0] == '\0') {
13295e111ed8SAndrew Rybchenko 		req.emr_in_length = MC_CMD_DRV_ATTACH_IN_LEN;
13305e111ed8SAndrew Rybchenko 	} else {
13315e111ed8SAndrew Rybchenko 		req.emr_in_length = MC_CMD_DRV_ATTACH_IN_V2_LEN;
13325e111ed8SAndrew Rybchenko 	}
13335e111ed8SAndrew Rybchenko 	req.emr_out_buf = payload;
13345e111ed8SAndrew Rybchenko 	req.emr_out_length = MC_CMD_DRV_ATTACH_EXT_OUT_LEN;
13355e111ed8SAndrew Rybchenko 
13365e111ed8SAndrew Rybchenko 	/*
13375e111ed8SAndrew Rybchenko 	 * Typically, client drivers use DONT_CARE for the datapath firmware
13385e111ed8SAndrew Rybchenko 	 * type to ensure that the driver can attach to an unprivileged
13395e111ed8SAndrew Rybchenko 	 * function. The datapath firmware type to use is controlled by the
13405e111ed8SAndrew Rybchenko 	 * 'sfboot' utility.
13415e111ed8SAndrew Rybchenko 	 * If a client driver wishes to attach with a specific datapath firmware
13425e111ed8SAndrew Rybchenko 	 * type, that can be passed in second argument of efx_nic_probe API. One
13435e111ed8SAndrew Rybchenko 	 * such example is the ESXi native driver that attempts attaching with
13445e111ed8SAndrew Rybchenko 	 * FULL_FEATURED datapath firmware type first and fall backs to
13455e111ed8SAndrew Rybchenko 	 * DONT_CARE datapath firmware type if MC_CMD_DRV_ATTACH fails.
13465e111ed8SAndrew Rybchenko 	 */
13475e111ed8SAndrew Rybchenko 	MCDI_IN_POPULATE_DWORD_2(req, DRV_ATTACH_IN_NEW_STATE,
13485e111ed8SAndrew Rybchenko 	    DRV_ATTACH_IN_ATTACH, attach ? 1 : 0,
13495e111ed8SAndrew Rybchenko 	    DRV_ATTACH_IN_SUBVARIANT_AWARE, EFSYS_OPT_FW_SUBVARIANT_AWARE);
13505e111ed8SAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, DRV_ATTACH_IN_UPDATE, 1);
13515e111ed8SAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, DRV_ATTACH_IN_FIRMWARE_ID, enp->efv);
13525e111ed8SAndrew Rybchenko 
13535e111ed8SAndrew Rybchenko 	if (req.emr_in_length >= MC_CMD_DRV_ATTACH_IN_V2_LEN) {
13545e111ed8SAndrew Rybchenko 		EFX_STATIC_ASSERT(sizeof (enp->en_drv_version) ==
13555e111ed8SAndrew Rybchenko 		    MC_CMD_DRV_ATTACH_IN_V2_DRIVER_VERSION_LEN);
13565e111ed8SAndrew Rybchenko 		memcpy(MCDI_IN2(req, char, DRV_ATTACH_IN_V2_DRIVER_VERSION),
13575e111ed8SAndrew Rybchenko 		    enp->en_drv_version,
13585e111ed8SAndrew Rybchenko 		    MC_CMD_DRV_ATTACH_IN_V2_DRIVER_VERSION_LEN);
13595e111ed8SAndrew Rybchenko 	}
13605e111ed8SAndrew Rybchenko 
13615e111ed8SAndrew Rybchenko 	efx_mcdi_execute(enp, &req);
13625e111ed8SAndrew Rybchenko 
13635e111ed8SAndrew Rybchenko 	if (req.emr_rc != 0) {
13645e111ed8SAndrew Rybchenko 		rc = req.emr_rc;
13655e111ed8SAndrew Rybchenko 		goto fail1;
13665e111ed8SAndrew Rybchenko 	}
13675e111ed8SAndrew Rybchenko 
13685e111ed8SAndrew Rybchenko 	if (req.emr_out_length_used < MC_CMD_DRV_ATTACH_OUT_LEN) {
13695e111ed8SAndrew Rybchenko 		rc = EMSGSIZE;
13705e111ed8SAndrew Rybchenko 		goto fail2;
13715e111ed8SAndrew Rybchenko 	}
13725e111ed8SAndrew Rybchenko 
13735e111ed8SAndrew Rybchenko 	return (0);
13745e111ed8SAndrew Rybchenko 
13755e111ed8SAndrew Rybchenko fail2:
13765e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail2);
13775e111ed8SAndrew Rybchenko fail1:
13785e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
13795e111ed8SAndrew Rybchenko 
13805e111ed8SAndrew Rybchenko 	return (rc);
13815e111ed8SAndrew Rybchenko }
13825e111ed8SAndrew Rybchenko 
13835e111ed8SAndrew Rybchenko 	__checkReturn		efx_rc_t
13845e111ed8SAndrew Rybchenko efx_mcdi_get_board_cfg(
13855e111ed8SAndrew Rybchenko 	__in			efx_nic_t *enp,
13865e111ed8SAndrew Rybchenko 	__out_opt		uint32_t *board_typep,
13875e111ed8SAndrew Rybchenko 	__out_opt		efx_dword_t *capabilitiesp,
13885e111ed8SAndrew Rybchenko 	__out_ecount_opt(6)	uint8_t mac_addrp[6])
13895e111ed8SAndrew Rybchenko {
13905e111ed8SAndrew Rybchenko 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
13915e111ed8SAndrew Rybchenko 	efx_mcdi_req_t req;
13925e111ed8SAndrew Rybchenko 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_BOARD_CFG_IN_LEN,
13935e111ed8SAndrew Rybchenko 		MC_CMD_GET_BOARD_CFG_OUT_LENMIN);
13945e111ed8SAndrew Rybchenko 	efx_rc_t rc;
13955e111ed8SAndrew Rybchenko 
13965e111ed8SAndrew Rybchenko 	req.emr_cmd = MC_CMD_GET_BOARD_CFG;
13975e111ed8SAndrew Rybchenko 	req.emr_in_buf = payload;
13985e111ed8SAndrew Rybchenko 	req.emr_in_length = MC_CMD_GET_BOARD_CFG_IN_LEN;
13995e111ed8SAndrew Rybchenko 	req.emr_out_buf = payload;
14005e111ed8SAndrew Rybchenko 	req.emr_out_length = MC_CMD_GET_BOARD_CFG_OUT_LENMIN;
14015e111ed8SAndrew Rybchenko 
14025e111ed8SAndrew Rybchenko 	efx_mcdi_execute(enp, &req);
14035e111ed8SAndrew Rybchenko 
14045e111ed8SAndrew Rybchenko 	if (req.emr_rc != 0) {
14055e111ed8SAndrew Rybchenko 		rc = req.emr_rc;
14065e111ed8SAndrew Rybchenko 		goto fail1;
14075e111ed8SAndrew Rybchenko 	}
14085e111ed8SAndrew Rybchenko 
14095e111ed8SAndrew Rybchenko 	if (req.emr_out_length_used < MC_CMD_GET_BOARD_CFG_OUT_LENMIN) {
14105e111ed8SAndrew Rybchenko 		rc = EMSGSIZE;
14115e111ed8SAndrew Rybchenko 		goto fail2;
14125e111ed8SAndrew Rybchenko 	}
14135e111ed8SAndrew Rybchenko 
14145e111ed8SAndrew Rybchenko 	if (mac_addrp != NULL) {
14155e111ed8SAndrew Rybchenko 		uint8_t *addrp;
14165e111ed8SAndrew Rybchenko 
14175e111ed8SAndrew Rybchenko 		if (emip->emi_port == 1) {
14185e111ed8SAndrew Rybchenko 			addrp = MCDI_OUT2(req, uint8_t,
14195e111ed8SAndrew Rybchenko 			    GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0);
14205e111ed8SAndrew Rybchenko 		} else if (emip->emi_port == 2) {
14215e111ed8SAndrew Rybchenko 			addrp = MCDI_OUT2(req, uint8_t,
14225e111ed8SAndrew Rybchenko 			    GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1);
14235e111ed8SAndrew Rybchenko 		} else {
14245e111ed8SAndrew Rybchenko 			rc = EINVAL;
14255e111ed8SAndrew Rybchenko 			goto fail3;
14265e111ed8SAndrew Rybchenko 		}
14275e111ed8SAndrew Rybchenko 
14285e111ed8SAndrew Rybchenko 		EFX_MAC_ADDR_COPY(mac_addrp, addrp);
14295e111ed8SAndrew Rybchenko 	}
14305e111ed8SAndrew Rybchenko 
14315e111ed8SAndrew Rybchenko 	if (capabilitiesp != NULL) {
14325e111ed8SAndrew Rybchenko 		if (emip->emi_port == 1) {
14335e111ed8SAndrew Rybchenko 			*capabilitiesp = *MCDI_OUT2(req, efx_dword_t,
14345e111ed8SAndrew Rybchenko 			    GET_BOARD_CFG_OUT_CAPABILITIES_PORT0);
14355e111ed8SAndrew Rybchenko 		} else if (emip->emi_port == 2) {
14365e111ed8SAndrew Rybchenko 			*capabilitiesp = *MCDI_OUT2(req, efx_dword_t,
14375e111ed8SAndrew Rybchenko 			    GET_BOARD_CFG_OUT_CAPABILITIES_PORT1);
14385e111ed8SAndrew Rybchenko 		} else {
14395e111ed8SAndrew Rybchenko 			rc = EINVAL;
14405e111ed8SAndrew Rybchenko 			goto fail4;
14415e111ed8SAndrew Rybchenko 		}
14425e111ed8SAndrew Rybchenko 	}
14435e111ed8SAndrew Rybchenko 
14445e111ed8SAndrew Rybchenko 	if (board_typep != NULL) {
14455e111ed8SAndrew Rybchenko 		*board_typep = MCDI_OUT_DWORD(req,
14465e111ed8SAndrew Rybchenko 		    GET_BOARD_CFG_OUT_BOARD_TYPE);
14475e111ed8SAndrew Rybchenko 	}
14485e111ed8SAndrew Rybchenko 
14495e111ed8SAndrew Rybchenko 	return (0);
14505e111ed8SAndrew Rybchenko 
14515e111ed8SAndrew Rybchenko fail4:
14525e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail4);
14535e111ed8SAndrew Rybchenko fail3:
14545e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail3);
14555e111ed8SAndrew Rybchenko fail2:
14565e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail2);
14575e111ed8SAndrew Rybchenko fail1:
14585e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
14595e111ed8SAndrew Rybchenko 
14605e111ed8SAndrew Rybchenko 	return (rc);
14615e111ed8SAndrew Rybchenko }
14625e111ed8SAndrew Rybchenko 
14635e111ed8SAndrew Rybchenko 	__checkReturn	efx_rc_t
14645e111ed8SAndrew Rybchenko efx_mcdi_get_resource_limits(
14655e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
14665e111ed8SAndrew Rybchenko 	__out_opt	uint32_t *nevqp,
14675e111ed8SAndrew Rybchenko 	__out_opt	uint32_t *nrxqp,
14685e111ed8SAndrew Rybchenko 	__out_opt	uint32_t *ntxqp)
14695e111ed8SAndrew Rybchenko {
14705e111ed8SAndrew Rybchenko 	efx_mcdi_req_t req;
14715e111ed8SAndrew Rybchenko 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_RESOURCE_LIMITS_IN_LEN,
14725e111ed8SAndrew Rybchenko 		MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN);
14735e111ed8SAndrew Rybchenko 	efx_rc_t rc;
14745e111ed8SAndrew Rybchenko 
14755e111ed8SAndrew Rybchenko 	req.emr_cmd = MC_CMD_GET_RESOURCE_LIMITS;
14765e111ed8SAndrew Rybchenko 	req.emr_in_buf = payload;
14775e111ed8SAndrew Rybchenko 	req.emr_in_length = MC_CMD_GET_RESOURCE_LIMITS_IN_LEN;
14785e111ed8SAndrew Rybchenko 	req.emr_out_buf = payload;
14795e111ed8SAndrew Rybchenko 	req.emr_out_length = MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN;
14805e111ed8SAndrew Rybchenko 
14815e111ed8SAndrew Rybchenko 	efx_mcdi_execute(enp, &req);
14825e111ed8SAndrew Rybchenko 
14835e111ed8SAndrew Rybchenko 	if (req.emr_rc != 0) {
14845e111ed8SAndrew Rybchenko 		rc = req.emr_rc;
14855e111ed8SAndrew Rybchenko 		goto fail1;
14865e111ed8SAndrew Rybchenko 	}
14875e111ed8SAndrew Rybchenko 
14885e111ed8SAndrew Rybchenko 	if (req.emr_out_length_used < MC_CMD_GET_RESOURCE_LIMITS_OUT_LEN) {
14895e111ed8SAndrew Rybchenko 		rc = EMSGSIZE;
14905e111ed8SAndrew Rybchenko 		goto fail2;
14915e111ed8SAndrew Rybchenko 	}
14925e111ed8SAndrew Rybchenko 
14935e111ed8SAndrew Rybchenko 	if (nevqp != NULL)
14945e111ed8SAndrew Rybchenko 		*nevqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_EVQ);
14955e111ed8SAndrew Rybchenko 	if (nrxqp != NULL)
14965e111ed8SAndrew Rybchenko 		*nrxqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_RXQ);
14975e111ed8SAndrew Rybchenko 	if (ntxqp != NULL)
14985e111ed8SAndrew Rybchenko 		*ntxqp = MCDI_OUT_DWORD(req, GET_RESOURCE_LIMITS_OUT_TXQ);
14995e111ed8SAndrew Rybchenko 
15005e111ed8SAndrew Rybchenko 	return (0);
15015e111ed8SAndrew Rybchenko 
15025e111ed8SAndrew Rybchenko fail2:
15035e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail2);
15045e111ed8SAndrew Rybchenko fail1:
15055e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
15065e111ed8SAndrew Rybchenko 
15075e111ed8SAndrew Rybchenko 	return (rc);
15085e111ed8SAndrew Rybchenko }
15095e111ed8SAndrew Rybchenko 
15105e111ed8SAndrew Rybchenko 	__checkReturn	efx_rc_t
15115e111ed8SAndrew Rybchenko efx_mcdi_get_phy_cfg(
15125e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp)
15135e111ed8SAndrew Rybchenko {
15145e111ed8SAndrew Rybchenko 	efx_port_t *epp = &(enp->en_port);
15155e111ed8SAndrew Rybchenko 	efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
15165e111ed8SAndrew Rybchenko 	efx_mcdi_req_t req;
15175e111ed8SAndrew Rybchenko 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_PHY_CFG_IN_LEN,
15185e111ed8SAndrew Rybchenko 		MC_CMD_GET_PHY_CFG_OUT_LEN);
15195e111ed8SAndrew Rybchenko #if EFSYS_OPT_NAMES
15205e111ed8SAndrew Rybchenko 	const char *namep;
15215e111ed8SAndrew Rybchenko 	size_t namelen;
15225e111ed8SAndrew Rybchenko #endif
15235e111ed8SAndrew Rybchenko 	uint32_t phy_media_type;
15245e111ed8SAndrew Rybchenko 	efx_rc_t rc;
15255e111ed8SAndrew Rybchenko 
15265e111ed8SAndrew Rybchenko 	req.emr_cmd = MC_CMD_GET_PHY_CFG;
15275e111ed8SAndrew Rybchenko 	req.emr_in_buf = payload;
15285e111ed8SAndrew Rybchenko 	req.emr_in_length = MC_CMD_GET_PHY_CFG_IN_LEN;
15295e111ed8SAndrew Rybchenko 	req.emr_out_buf = payload;
15305e111ed8SAndrew Rybchenko 	req.emr_out_length = MC_CMD_GET_PHY_CFG_OUT_LEN;
15315e111ed8SAndrew Rybchenko 
15325e111ed8SAndrew Rybchenko 	efx_mcdi_execute(enp, &req);
15335e111ed8SAndrew Rybchenko 
15345e111ed8SAndrew Rybchenko 	if (req.emr_rc != 0) {
15355e111ed8SAndrew Rybchenko 		rc = req.emr_rc;
15365e111ed8SAndrew Rybchenko 		goto fail1;
15375e111ed8SAndrew Rybchenko 	}
15385e111ed8SAndrew Rybchenko 
15395e111ed8SAndrew Rybchenko 	if (req.emr_out_length_used < MC_CMD_GET_PHY_CFG_OUT_LEN) {
15405e111ed8SAndrew Rybchenko 		rc = EMSGSIZE;
15415e111ed8SAndrew Rybchenko 		goto fail2;
15425e111ed8SAndrew Rybchenko 	}
15435e111ed8SAndrew Rybchenko 
15445e111ed8SAndrew Rybchenko 	encp->enc_phy_type = MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_TYPE);
15455e111ed8SAndrew Rybchenko #if EFSYS_OPT_NAMES
15465e111ed8SAndrew Rybchenko 	namep = MCDI_OUT2(req, char, GET_PHY_CFG_OUT_NAME);
15475e111ed8SAndrew Rybchenko 	namelen = MIN(sizeof (encp->enc_phy_name) - 1,
15485e111ed8SAndrew Rybchenko 		    strnlen(namep, MC_CMD_GET_PHY_CFG_OUT_NAME_LEN));
15495e111ed8SAndrew Rybchenko 	(void) memset(encp->enc_phy_name, 0,
15505e111ed8SAndrew Rybchenko 	    sizeof (encp->enc_phy_name));
15515e111ed8SAndrew Rybchenko 	memcpy(encp->enc_phy_name, namep, namelen);
15525e111ed8SAndrew Rybchenko #endif	/* EFSYS_OPT_NAMES */
15535e111ed8SAndrew Rybchenko 	(void) memset(encp->enc_phy_revision, 0,
15545e111ed8SAndrew Rybchenko 	    sizeof (encp->enc_phy_revision));
15555e111ed8SAndrew Rybchenko 	memcpy(encp->enc_phy_revision,
15565e111ed8SAndrew Rybchenko 		MCDI_OUT2(req, char, GET_PHY_CFG_OUT_REVISION),
15575e111ed8SAndrew Rybchenko 		MIN(sizeof (encp->enc_phy_revision) - 1,
15585e111ed8SAndrew Rybchenko 		    MC_CMD_GET_PHY_CFG_OUT_REVISION_LEN));
15595e111ed8SAndrew Rybchenko #if EFSYS_OPT_PHY_LED_CONTROL
15605e111ed8SAndrew Rybchenko 	encp->enc_led_mask = ((1 << EFX_PHY_LED_DEFAULT) |
15615e111ed8SAndrew Rybchenko 			    (1 << EFX_PHY_LED_OFF) |
15625e111ed8SAndrew Rybchenko 			    (1 << EFX_PHY_LED_ON));
15635e111ed8SAndrew Rybchenko #endif	/* EFSYS_OPT_PHY_LED_CONTROL */
15645e111ed8SAndrew Rybchenko 
15655e111ed8SAndrew Rybchenko 	/* Get the media type of the fixed port, if recognised. */
15665e111ed8SAndrew Rybchenko 	EFX_STATIC_ASSERT(MC_CMD_MEDIA_XAUI == EFX_PHY_MEDIA_XAUI);
15675e111ed8SAndrew Rybchenko 	EFX_STATIC_ASSERT(MC_CMD_MEDIA_CX4 == EFX_PHY_MEDIA_CX4);
15685e111ed8SAndrew Rybchenko 	EFX_STATIC_ASSERT(MC_CMD_MEDIA_KX4 == EFX_PHY_MEDIA_KX4);
15695e111ed8SAndrew Rybchenko 	EFX_STATIC_ASSERT(MC_CMD_MEDIA_XFP == EFX_PHY_MEDIA_XFP);
15705e111ed8SAndrew Rybchenko 	EFX_STATIC_ASSERT(MC_CMD_MEDIA_SFP_PLUS == EFX_PHY_MEDIA_SFP_PLUS);
15715e111ed8SAndrew Rybchenko 	EFX_STATIC_ASSERT(MC_CMD_MEDIA_BASE_T == EFX_PHY_MEDIA_BASE_T);
15725e111ed8SAndrew Rybchenko 	EFX_STATIC_ASSERT(MC_CMD_MEDIA_QSFP_PLUS == EFX_PHY_MEDIA_QSFP_PLUS);
15735e111ed8SAndrew Rybchenko 	phy_media_type = MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_MEDIA_TYPE);
15745e111ed8SAndrew Rybchenko 	epp->ep_fixed_port_type = (efx_phy_media_type_t)phy_media_type;
15755e111ed8SAndrew Rybchenko 	if (epp->ep_fixed_port_type >= EFX_PHY_MEDIA_NTYPES)
15765e111ed8SAndrew Rybchenko 		epp->ep_fixed_port_type = EFX_PHY_MEDIA_INVALID;
15775e111ed8SAndrew Rybchenko 
15785e111ed8SAndrew Rybchenko 	epp->ep_phy_cap_mask =
15795e111ed8SAndrew Rybchenko 		MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_SUPPORTED_CAP);
15805e111ed8SAndrew Rybchenko #if EFSYS_OPT_PHY_FLAGS
15815e111ed8SAndrew Rybchenko 	encp->enc_phy_flags_mask = MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_FLAGS);
15825e111ed8SAndrew Rybchenko #endif	/* EFSYS_OPT_PHY_FLAGS */
15835e111ed8SAndrew Rybchenko 
15845e111ed8SAndrew Rybchenko 	encp->enc_port = (uint8_t)MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_PRT);
15855e111ed8SAndrew Rybchenko 
15865e111ed8SAndrew Rybchenko 	/* Populate internal state */
15875e111ed8SAndrew Rybchenko 	encp->enc_mcdi_mdio_channel =
15885e111ed8SAndrew Rybchenko 		(uint8_t)MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_CHANNEL);
15895e111ed8SAndrew Rybchenko 
15905e111ed8SAndrew Rybchenko #if EFSYS_OPT_PHY_STATS
15915e111ed8SAndrew Rybchenko 	encp->enc_mcdi_phy_stat_mask =
15925e111ed8SAndrew Rybchenko 		MCDI_OUT_DWORD(req, GET_PHY_CFG_OUT_STATS_MASK);
15935e111ed8SAndrew Rybchenko #endif	/* EFSYS_OPT_PHY_STATS */
15945e111ed8SAndrew Rybchenko 
15955e111ed8SAndrew Rybchenko #if EFSYS_OPT_BIST
15965e111ed8SAndrew Rybchenko 	encp->enc_bist_mask = 0;
15975e111ed8SAndrew Rybchenko 	if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS,
15985e111ed8SAndrew Rybchenko 	    GET_PHY_CFG_OUT_BIST_CABLE_SHORT))
15995e111ed8SAndrew Rybchenko 		encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_CABLE_SHORT);
16005e111ed8SAndrew Rybchenko 	if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS,
16015e111ed8SAndrew Rybchenko 	    GET_PHY_CFG_OUT_BIST_CABLE_LONG))
16025e111ed8SAndrew Rybchenko 		encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_CABLE_LONG);
16035e111ed8SAndrew Rybchenko 	if (MCDI_OUT_DWORD_FIELD(req, GET_PHY_CFG_OUT_FLAGS,
16045e111ed8SAndrew Rybchenko 	    GET_PHY_CFG_OUT_BIST))
16055e111ed8SAndrew Rybchenko 		encp->enc_bist_mask |= (1 << EFX_BIST_TYPE_PHY_NORMAL);
16065e111ed8SAndrew Rybchenko #endif  /* EFSYS_OPT_BIST */
16075e111ed8SAndrew Rybchenko 
16085e111ed8SAndrew Rybchenko 	return (0);
16095e111ed8SAndrew Rybchenko 
16105e111ed8SAndrew Rybchenko fail2:
16115e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail2);
16125e111ed8SAndrew Rybchenko fail1:
16135e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
16145e111ed8SAndrew Rybchenko 
16155e111ed8SAndrew Rybchenko 	return (rc);
16165e111ed8SAndrew Rybchenko }
16175e111ed8SAndrew Rybchenko 
16185e111ed8SAndrew Rybchenko 	__checkReturn		efx_rc_t
16195e111ed8SAndrew Rybchenko efx_mcdi_firmware_update_supported(
16205e111ed8SAndrew Rybchenko 	__in			efx_nic_t *enp,
16215e111ed8SAndrew Rybchenko 	__out			boolean_t *supportedp)
16225e111ed8SAndrew Rybchenko {
16235e111ed8SAndrew Rybchenko 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
16245e111ed8SAndrew Rybchenko 	efx_rc_t rc;
16255e111ed8SAndrew Rybchenko 
16265e111ed8SAndrew Rybchenko 	if (emcop != NULL) {
16275e111ed8SAndrew Rybchenko 		if ((rc = emcop->emco_feature_supported(enp,
16285e111ed8SAndrew Rybchenko 			    EFX_MCDI_FEATURE_FW_UPDATE, supportedp)) != 0)
16295e111ed8SAndrew Rybchenko 			goto fail1;
16305e111ed8SAndrew Rybchenko 	} else {
16315e111ed8SAndrew Rybchenko 		/* Earlier devices always supported updates */
16325e111ed8SAndrew Rybchenko 		*supportedp = B_TRUE;
16335e111ed8SAndrew Rybchenko 	}
16345e111ed8SAndrew Rybchenko 
16355e111ed8SAndrew Rybchenko 	return (0);
16365e111ed8SAndrew Rybchenko 
16375e111ed8SAndrew Rybchenko fail1:
16385e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
16395e111ed8SAndrew Rybchenko 
16405e111ed8SAndrew Rybchenko 	return (rc);
16415e111ed8SAndrew Rybchenko }
16425e111ed8SAndrew Rybchenko 
16435e111ed8SAndrew Rybchenko 	__checkReturn		efx_rc_t
16445e111ed8SAndrew Rybchenko efx_mcdi_macaddr_change_supported(
16455e111ed8SAndrew Rybchenko 	__in			efx_nic_t *enp,
16465e111ed8SAndrew Rybchenko 	__out			boolean_t *supportedp)
16475e111ed8SAndrew Rybchenko {
16485e111ed8SAndrew Rybchenko 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
16495e111ed8SAndrew Rybchenko 	efx_rc_t rc;
16505e111ed8SAndrew Rybchenko 
16515e111ed8SAndrew Rybchenko 	if (emcop != NULL) {
16525e111ed8SAndrew Rybchenko 		if ((rc = emcop->emco_feature_supported(enp,
16535e111ed8SAndrew Rybchenko 			    EFX_MCDI_FEATURE_MACADDR_CHANGE, supportedp)) != 0)
16545e111ed8SAndrew Rybchenko 			goto fail1;
16555e111ed8SAndrew Rybchenko 	} else {
16565e111ed8SAndrew Rybchenko 		/* Earlier devices always supported MAC changes */
16575e111ed8SAndrew Rybchenko 		*supportedp = B_TRUE;
16585e111ed8SAndrew Rybchenko 	}
16595e111ed8SAndrew Rybchenko 
16605e111ed8SAndrew Rybchenko 	return (0);
16615e111ed8SAndrew Rybchenko 
16625e111ed8SAndrew Rybchenko fail1:
16635e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
16645e111ed8SAndrew Rybchenko 
16655e111ed8SAndrew Rybchenko 	return (rc);
16665e111ed8SAndrew Rybchenko }
16675e111ed8SAndrew Rybchenko 
16685e111ed8SAndrew Rybchenko 	__checkReturn		efx_rc_t
16695e111ed8SAndrew Rybchenko efx_mcdi_link_control_supported(
16705e111ed8SAndrew Rybchenko 	__in			efx_nic_t *enp,
16715e111ed8SAndrew Rybchenko 	__out			boolean_t *supportedp)
16725e111ed8SAndrew Rybchenko {
16735e111ed8SAndrew Rybchenko 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
16745e111ed8SAndrew Rybchenko 	efx_rc_t rc;
16755e111ed8SAndrew Rybchenko 
16765e111ed8SAndrew Rybchenko 	if (emcop != NULL) {
16775e111ed8SAndrew Rybchenko 		if ((rc = emcop->emco_feature_supported(enp,
16785e111ed8SAndrew Rybchenko 			    EFX_MCDI_FEATURE_LINK_CONTROL, supportedp)) != 0)
16795e111ed8SAndrew Rybchenko 			goto fail1;
16805e111ed8SAndrew Rybchenko 	} else {
16815e111ed8SAndrew Rybchenko 		/* Earlier devices always supported link control */
16825e111ed8SAndrew Rybchenko 		*supportedp = B_TRUE;
16835e111ed8SAndrew Rybchenko 	}
16845e111ed8SAndrew Rybchenko 
16855e111ed8SAndrew Rybchenko 	return (0);
16865e111ed8SAndrew Rybchenko 
16875e111ed8SAndrew Rybchenko fail1:
16885e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
16895e111ed8SAndrew Rybchenko 
16905e111ed8SAndrew Rybchenko 	return (rc);
16915e111ed8SAndrew Rybchenko }
16925e111ed8SAndrew Rybchenko 
16935e111ed8SAndrew Rybchenko 	__checkReturn		efx_rc_t
16945e111ed8SAndrew Rybchenko efx_mcdi_mac_spoofing_supported(
16955e111ed8SAndrew Rybchenko 	__in			efx_nic_t *enp,
16965e111ed8SAndrew Rybchenko 	__out			boolean_t *supportedp)
16975e111ed8SAndrew Rybchenko {
16985e111ed8SAndrew Rybchenko 	const efx_mcdi_ops_t *emcop = enp->en_mcdi.em_emcop;
16995e111ed8SAndrew Rybchenko 	efx_rc_t rc;
17005e111ed8SAndrew Rybchenko 
17015e111ed8SAndrew Rybchenko 	if (emcop != NULL) {
17025e111ed8SAndrew Rybchenko 		if ((rc = emcop->emco_feature_supported(enp,
17035e111ed8SAndrew Rybchenko 			    EFX_MCDI_FEATURE_MAC_SPOOFING, supportedp)) != 0)
17045e111ed8SAndrew Rybchenko 			goto fail1;
17055e111ed8SAndrew Rybchenko 	} else {
17065e111ed8SAndrew Rybchenko 		/* Earlier devices always supported MAC spoofing */
17075e111ed8SAndrew Rybchenko 		*supportedp = B_TRUE;
17085e111ed8SAndrew Rybchenko 	}
17095e111ed8SAndrew Rybchenko 
17105e111ed8SAndrew Rybchenko 	return (0);
17115e111ed8SAndrew Rybchenko 
17125e111ed8SAndrew Rybchenko fail1:
17135e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
17145e111ed8SAndrew Rybchenko 
17155e111ed8SAndrew Rybchenko 	return (rc);
17165e111ed8SAndrew Rybchenko }
17175e111ed8SAndrew Rybchenko 
17185e111ed8SAndrew Rybchenko #if EFSYS_OPT_BIST
17195e111ed8SAndrew Rybchenko 
17205e111ed8SAndrew Rybchenko #if EFX_OPTS_EF10()
17215e111ed8SAndrew Rybchenko /*
17225e111ed8SAndrew Rybchenko  * Enter bist offline mode. This is a fw mode which puts the NIC into a state
17235e111ed8SAndrew Rybchenko  * where memory BIST tests can be run and not much else can interfere or happen.
17245e111ed8SAndrew Rybchenko  * A reboot is required to exit this mode.
17255e111ed8SAndrew Rybchenko  */
17265e111ed8SAndrew Rybchenko 	__checkReturn		efx_rc_t
17275e111ed8SAndrew Rybchenko efx_mcdi_bist_enable_offline(
17285e111ed8SAndrew Rybchenko 	__in			efx_nic_t *enp)
17295e111ed8SAndrew Rybchenko {
17305e111ed8SAndrew Rybchenko 	efx_mcdi_req_t req;
17315e111ed8SAndrew Rybchenko 	efx_rc_t rc;
17325e111ed8SAndrew Rybchenko 
17335e111ed8SAndrew Rybchenko 	EFX_STATIC_ASSERT(MC_CMD_ENABLE_OFFLINE_BIST_IN_LEN == 0);
17345e111ed8SAndrew Rybchenko 	EFX_STATIC_ASSERT(MC_CMD_ENABLE_OFFLINE_BIST_OUT_LEN == 0);
17355e111ed8SAndrew Rybchenko 
17365e111ed8SAndrew Rybchenko 	req.emr_cmd = MC_CMD_ENABLE_OFFLINE_BIST;
17375e111ed8SAndrew Rybchenko 	req.emr_in_buf = NULL;
17385e111ed8SAndrew Rybchenko 	req.emr_in_length = 0;
17395e111ed8SAndrew Rybchenko 	req.emr_out_buf = NULL;
17405e111ed8SAndrew Rybchenko 	req.emr_out_length = 0;
17415e111ed8SAndrew Rybchenko 
17425e111ed8SAndrew Rybchenko 	efx_mcdi_execute(enp, &req);
17435e111ed8SAndrew Rybchenko 
17445e111ed8SAndrew Rybchenko 	if (req.emr_rc != 0) {
17455e111ed8SAndrew Rybchenko 		rc = req.emr_rc;
17465e111ed8SAndrew Rybchenko 		goto fail1;
17475e111ed8SAndrew Rybchenko 	}
17485e111ed8SAndrew Rybchenko 
17495e111ed8SAndrew Rybchenko 	return (0);
17505e111ed8SAndrew Rybchenko 
17515e111ed8SAndrew Rybchenko fail1:
17525e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
17535e111ed8SAndrew Rybchenko 
17545e111ed8SAndrew Rybchenko 	return (rc);
17555e111ed8SAndrew Rybchenko }
17565e111ed8SAndrew Rybchenko #endif /* EFX_OPTS_EF10() */
17575e111ed8SAndrew Rybchenko 
17585e111ed8SAndrew Rybchenko 	__checkReturn		efx_rc_t
17595e111ed8SAndrew Rybchenko efx_mcdi_bist_start(
17605e111ed8SAndrew Rybchenko 	__in			efx_nic_t *enp,
17615e111ed8SAndrew Rybchenko 	__in			efx_bist_type_t type)
17625e111ed8SAndrew Rybchenko {
17635e111ed8SAndrew Rybchenko 	efx_mcdi_req_t req;
17645e111ed8SAndrew Rybchenko 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_START_BIST_IN_LEN,
17655e111ed8SAndrew Rybchenko 		MC_CMD_START_BIST_OUT_LEN);
17665e111ed8SAndrew Rybchenko 	efx_rc_t rc;
17675e111ed8SAndrew Rybchenko 
17685e111ed8SAndrew Rybchenko 	req.emr_cmd = MC_CMD_START_BIST;
17695e111ed8SAndrew Rybchenko 	req.emr_in_buf = payload;
17705e111ed8SAndrew Rybchenko 	req.emr_in_length = MC_CMD_START_BIST_IN_LEN;
17715e111ed8SAndrew Rybchenko 	req.emr_out_buf = payload;
17725e111ed8SAndrew Rybchenko 	req.emr_out_length = MC_CMD_START_BIST_OUT_LEN;
17735e111ed8SAndrew Rybchenko 
17745e111ed8SAndrew Rybchenko 	switch (type) {
17755e111ed8SAndrew Rybchenko 	case EFX_BIST_TYPE_PHY_NORMAL:
17765e111ed8SAndrew Rybchenko 		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE, MC_CMD_PHY_BIST);
17775e111ed8SAndrew Rybchenko 		break;
17785e111ed8SAndrew Rybchenko 	case EFX_BIST_TYPE_PHY_CABLE_SHORT:
17795e111ed8SAndrew Rybchenko 		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
17805e111ed8SAndrew Rybchenko 		    MC_CMD_PHY_BIST_CABLE_SHORT);
17815e111ed8SAndrew Rybchenko 		break;
17825e111ed8SAndrew Rybchenko 	case EFX_BIST_TYPE_PHY_CABLE_LONG:
17835e111ed8SAndrew Rybchenko 		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
17845e111ed8SAndrew Rybchenko 		    MC_CMD_PHY_BIST_CABLE_LONG);
17855e111ed8SAndrew Rybchenko 		break;
17865e111ed8SAndrew Rybchenko 	case EFX_BIST_TYPE_MC_MEM:
17875e111ed8SAndrew Rybchenko 		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
17885e111ed8SAndrew Rybchenko 		    MC_CMD_MC_MEM_BIST);
17895e111ed8SAndrew Rybchenko 		break;
17905e111ed8SAndrew Rybchenko 	case EFX_BIST_TYPE_SAT_MEM:
17915e111ed8SAndrew Rybchenko 		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
17925e111ed8SAndrew Rybchenko 		    MC_CMD_PORT_MEM_BIST);
17935e111ed8SAndrew Rybchenko 		break;
17945e111ed8SAndrew Rybchenko 	case EFX_BIST_TYPE_REG:
17955e111ed8SAndrew Rybchenko 		MCDI_IN_SET_DWORD(req, START_BIST_IN_TYPE,
17965e111ed8SAndrew Rybchenko 		    MC_CMD_REG_BIST);
17975e111ed8SAndrew Rybchenko 		break;
17985e111ed8SAndrew Rybchenko 	default:
17995e111ed8SAndrew Rybchenko 		EFSYS_ASSERT(0);
18005e111ed8SAndrew Rybchenko 	}
18015e111ed8SAndrew Rybchenko 
18025e111ed8SAndrew Rybchenko 	efx_mcdi_execute(enp, &req);
18035e111ed8SAndrew Rybchenko 
18045e111ed8SAndrew Rybchenko 	if (req.emr_rc != 0) {
18055e111ed8SAndrew Rybchenko 		rc = req.emr_rc;
18065e111ed8SAndrew Rybchenko 		goto fail1;
18075e111ed8SAndrew Rybchenko 	}
18085e111ed8SAndrew Rybchenko 
18095e111ed8SAndrew Rybchenko 	return (0);
18105e111ed8SAndrew Rybchenko 
18115e111ed8SAndrew Rybchenko fail1:
18125e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
18135e111ed8SAndrew Rybchenko 
18145e111ed8SAndrew Rybchenko 	return (rc);
18155e111ed8SAndrew Rybchenko }
18165e111ed8SAndrew Rybchenko 
18175e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_BIST */
18185e111ed8SAndrew Rybchenko 
18195e111ed8SAndrew Rybchenko 
18205e111ed8SAndrew Rybchenko /* Enable logging of some events (e.g. link state changes) */
18215e111ed8SAndrew Rybchenko 	__checkReturn	efx_rc_t
18225e111ed8SAndrew Rybchenko efx_mcdi_log_ctrl(
18235e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp)
18245e111ed8SAndrew Rybchenko {
18255e111ed8SAndrew Rybchenko 	efx_mcdi_req_t req;
18265e111ed8SAndrew Rybchenko 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_LOG_CTRL_IN_LEN,
18275e111ed8SAndrew Rybchenko 		MC_CMD_LOG_CTRL_OUT_LEN);
18285e111ed8SAndrew Rybchenko 	efx_rc_t rc;
18295e111ed8SAndrew Rybchenko 
18305e111ed8SAndrew Rybchenko 	req.emr_cmd = MC_CMD_LOG_CTRL;
18315e111ed8SAndrew Rybchenko 	req.emr_in_buf = payload;
18325e111ed8SAndrew Rybchenko 	req.emr_in_length = MC_CMD_LOG_CTRL_IN_LEN;
18335e111ed8SAndrew Rybchenko 	req.emr_out_buf = payload;
18345e111ed8SAndrew Rybchenko 	req.emr_out_length = MC_CMD_LOG_CTRL_OUT_LEN;
18355e111ed8SAndrew Rybchenko 
18365e111ed8SAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, LOG_CTRL_IN_LOG_DEST,
18375e111ed8SAndrew Rybchenko 		    MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ);
18385e111ed8SAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, LOG_CTRL_IN_LOG_DEST_EVQ, 0);
18395e111ed8SAndrew Rybchenko 
18405e111ed8SAndrew Rybchenko 	efx_mcdi_execute(enp, &req);
18415e111ed8SAndrew Rybchenko 
18425e111ed8SAndrew Rybchenko 	if (req.emr_rc != 0) {
18435e111ed8SAndrew Rybchenko 		rc = req.emr_rc;
18445e111ed8SAndrew Rybchenko 		goto fail1;
18455e111ed8SAndrew Rybchenko 	}
18465e111ed8SAndrew Rybchenko 
18475e111ed8SAndrew Rybchenko 	return (0);
18485e111ed8SAndrew Rybchenko 
18495e111ed8SAndrew Rybchenko fail1:
18505e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
18515e111ed8SAndrew Rybchenko 
18525e111ed8SAndrew Rybchenko 	return (rc);
18535e111ed8SAndrew Rybchenko }
18545e111ed8SAndrew Rybchenko 
18555e111ed8SAndrew Rybchenko 
18565e111ed8SAndrew Rybchenko #if EFSYS_OPT_MAC_STATS
18575e111ed8SAndrew Rybchenko 
18585e111ed8SAndrew Rybchenko 	__checkReturn	efx_rc_t
18595e111ed8SAndrew Rybchenko efx_mcdi_mac_stats(
18605e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
18615e111ed8SAndrew Rybchenko 	__in		uint32_t vport_id,
18625e111ed8SAndrew Rybchenko 	__in_opt	efsys_mem_t *esmp,
18635e111ed8SAndrew Rybchenko 	__in		efx_stats_action_t action,
18645e111ed8SAndrew Rybchenko 	__in		uint16_t period_ms)
18655e111ed8SAndrew Rybchenko {
18665e111ed8SAndrew Rybchenko 	efx_mcdi_req_t req;
18675e111ed8SAndrew Rybchenko 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_MAC_STATS_IN_LEN,
18685e111ed8SAndrew Rybchenko 		MC_CMD_MAC_STATS_V2_OUT_DMA_LEN);
18695e111ed8SAndrew Rybchenko 	int clear = (action == EFX_STATS_CLEAR);
18705e111ed8SAndrew Rybchenko 	int upload = (action == EFX_STATS_UPLOAD);
18715e111ed8SAndrew Rybchenko 	int enable = (action == EFX_STATS_ENABLE_NOEVENTS);
18725e111ed8SAndrew Rybchenko 	int events = (action == EFX_STATS_ENABLE_EVENTS);
18735e111ed8SAndrew Rybchenko 	int disable = (action == EFX_STATS_DISABLE);
18745e111ed8SAndrew Rybchenko 	efx_rc_t rc;
18755e111ed8SAndrew Rybchenko 
18765e111ed8SAndrew Rybchenko 	req.emr_cmd = MC_CMD_MAC_STATS;
18775e111ed8SAndrew Rybchenko 	req.emr_in_buf = payload;
18785e111ed8SAndrew Rybchenko 	req.emr_in_length = MC_CMD_MAC_STATS_IN_LEN;
18795e111ed8SAndrew Rybchenko 	req.emr_out_buf = payload;
18805e111ed8SAndrew Rybchenko 	req.emr_out_length = MC_CMD_MAC_STATS_V2_OUT_DMA_LEN;
18815e111ed8SAndrew Rybchenko 
18825e111ed8SAndrew Rybchenko 	MCDI_IN_POPULATE_DWORD_6(req, MAC_STATS_IN_CMD,
18835e111ed8SAndrew Rybchenko 	    MAC_STATS_IN_DMA, upload,
18845e111ed8SAndrew Rybchenko 	    MAC_STATS_IN_CLEAR, clear,
18855e111ed8SAndrew Rybchenko 	    MAC_STATS_IN_PERIODIC_CHANGE, enable | events | disable,
18865e111ed8SAndrew Rybchenko 	    MAC_STATS_IN_PERIODIC_ENABLE, enable | events,
18875e111ed8SAndrew Rybchenko 	    MAC_STATS_IN_PERIODIC_NOEVENT, !events,
18885e111ed8SAndrew Rybchenko 	    MAC_STATS_IN_PERIOD_MS, (enable | events) ? period_ms : 0);
18895e111ed8SAndrew Rybchenko 
18905e111ed8SAndrew Rybchenko 	if (enable || events || upload) {
18915e111ed8SAndrew Rybchenko 		const efx_nic_cfg_t *encp = &enp->en_nic_cfg;
18925e111ed8SAndrew Rybchenko 		uint32_t bytes;
18935e111ed8SAndrew Rybchenko 
18945e111ed8SAndrew Rybchenko 		/* Periodic stats or stats upload require a DMA buffer */
18955e111ed8SAndrew Rybchenko 		if (esmp == NULL) {
18965e111ed8SAndrew Rybchenko 			rc = EINVAL;
18975e111ed8SAndrew Rybchenko 			goto fail1;
18985e111ed8SAndrew Rybchenko 		}
18995e111ed8SAndrew Rybchenko 
19005e111ed8SAndrew Rybchenko 		if (encp->enc_mac_stats_nstats < MC_CMD_MAC_NSTATS) {
19015e111ed8SAndrew Rybchenko 			/* MAC stats count too small for legacy MAC stats */
19025e111ed8SAndrew Rybchenko 			rc = ENOSPC;
19035e111ed8SAndrew Rybchenko 			goto fail2;
19045e111ed8SAndrew Rybchenko 		}
19055e111ed8SAndrew Rybchenko 
19065e111ed8SAndrew Rybchenko 		bytes = encp->enc_mac_stats_nstats * sizeof (efx_qword_t);
19075e111ed8SAndrew Rybchenko 
19085e111ed8SAndrew Rybchenko 		if (EFSYS_MEM_SIZE(esmp) < bytes) {
19095e111ed8SAndrew Rybchenko 			/* DMA buffer too small */
19105e111ed8SAndrew Rybchenko 			rc = ENOSPC;
19115e111ed8SAndrew Rybchenko 			goto fail3;
19125e111ed8SAndrew Rybchenko 		}
19135e111ed8SAndrew Rybchenko 
19145e111ed8SAndrew Rybchenko 		MCDI_IN_SET_DWORD(req, MAC_STATS_IN_DMA_ADDR_LO,
19155e111ed8SAndrew Rybchenko 			    EFSYS_MEM_ADDR(esmp) & 0xffffffff);
19165e111ed8SAndrew Rybchenko 		MCDI_IN_SET_DWORD(req, MAC_STATS_IN_DMA_ADDR_HI,
19175e111ed8SAndrew Rybchenko 			    EFSYS_MEM_ADDR(esmp) >> 32);
19185e111ed8SAndrew Rybchenko 		MCDI_IN_SET_DWORD(req, MAC_STATS_IN_DMA_LEN, bytes);
19195e111ed8SAndrew Rybchenko 	}
19205e111ed8SAndrew Rybchenko 
19215e111ed8SAndrew Rybchenko 	/*
19225e111ed8SAndrew Rybchenko 	 * NOTE: Do not use EVB_PORT_ID_ASSIGNED when disabling periodic stats,
19235e111ed8SAndrew Rybchenko 	 *	 as this may fail (and leave periodic DMA enabled) if the
19245e111ed8SAndrew Rybchenko 	 *	 vadapter has already been deleted.
19255e111ed8SAndrew Rybchenko 	 */
19265e111ed8SAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, MAC_STATS_IN_PORT_ID,
19275e111ed8SAndrew Rybchenko 		(disable ? EVB_PORT_ID_NULL : vport_id));
19285e111ed8SAndrew Rybchenko 
19295e111ed8SAndrew Rybchenko 	efx_mcdi_execute(enp, &req);
19305e111ed8SAndrew Rybchenko 
19315e111ed8SAndrew Rybchenko 	if (req.emr_rc != 0) {
19325e111ed8SAndrew Rybchenko 		/* EF10: Expect ENOENT if no DMA queues are initialised */
19335e111ed8SAndrew Rybchenko 		if ((req.emr_rc != ENOENT) ||
19345e111ed8SAndrew Rybchenko 		    (enp->en_rx_qcount + enp->en_tx_qcount != 0)) {
19355e111ed8SAndrew Rybchenko 			rc = req.emr_rc;
19365e111ed8SAndrew Rybchenko 			goto fail4;
19375e111ed8SAndrew Rybchenko 		}
19385e111ed8SAndrew Rybchenko 	}
19395e111ed8SAndrew Rybchenko 
19405e111ed8SAndrew Rybchenko 	return (0);
19415e111ed8SAndrew Rybchenko 
19425e111ed8SAndrew Rybchenko fail4:
19435e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail4);
19445e111ed8SAndrew Rybchenko fail3:
19455e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail3);
19465e111ed8SAndrew Rybchenko fail2:
19475e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail2);
19485e111ed8SAndrew Rybchenko fail1:
19495e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
19505e111ed8SAndrew Rybchenko 
19515e111ed8SAndrew Rybchenko 	return (rc);
19525e111ed8SAndrew Rybchenko }
19535e111ed8SAndrew Rybchenko 
19545e111ed8SAndrew Rybchenko 	__checkReturn	efx_rc_t
19555e111ed8SAndrew Rybchenko efx_mcdi_mac_stats_clear(
19565e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp)
19575e111ed8SAndrew Rybchenko {
19585e111ed8SAndrew Rybchenko 	efx_rc_t rc;
19595e111ed8SAndrew Rybchenko 
19605e111ed8SAndrew Rybchenko 	if ((rc = efx_mcdi_mac_stats(enp, enp->en_vport_id, NULL,
19615e111ed8SAndrew Rybchenko 			EFX_STATS_CLEAR, 0)) != 0)
19625e111ed8SAndrew Rybchenko 		goto fail1;
19635e111ed8SAndrew Rybchenko 
19645e111ed8SAndrew Rybchenko 	return (0);
19655e111ed8SAndrew Rybchenko 
19665e111ed8SAndrew Rybchenko fail1:
19675e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
19685e111ed8SAndrew Rybchenko 
19695e111ed8SAndrew Rybchenko 	return (rc);
19705e111ed8SAndrew Rybchenko }
19715e111ed8SAndrew Rybchenko 
19725e111ed8SAndrew Rybchenko 	__checkReturn	efx_rc_t
19735e111ed8SAndrew Rybchenko efx_mcdi_mac_stats_upload(
19745e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
19755e111ed8SAndrew Rybchenko 	__in		efsys_mem_t *esmp)
19765e111ed8SAndrew Rybchenko {
19775e111ed8SAndrew Rybchenko 	efx_rc_t rc;
19785e111ed8SAndrew Rybchenko 
19795e111ed8SAndrew Rybchenko 	/*
19805e111ed8SAndrew Rybchenko 	 * The MC DMAs aggregate statistics for our convenience, so we can
19815e111ed8SAndrew Rybchenko 	 * avoid having to pull the statistics buffer into the cache to
19825e111ed8SAndrew Rybchenko 	 * maintain cumulative statistics.
19835e111ed8SAndrew Rybchenko 	 */
19845e111ed8SAndrew Rybchenko 	if ((rc = efx_mcdi_mac_stats(enp, enp->en_vport_id, esmp,
19855e111ed8SAndrew Rybchenko 			EFX_STATS_UPLOAD, 0)) != 0)
19865e111ed8SAndrew Rybchenko 		goto fail1;
19875e111ed8SAndrew Rybchenko 
19885e111ed8SAndrew Rybchenko 	return (0);
19895e111ed8SAndrew Rybchenko 
19905e111ed8SAndrew Rybchenko fail1:
19915e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
19925e111ed8SAndrew Rybchenko 
19935e111ed8SAndrew Rybchenko 	return (rc);
19945e111ed8SAndrew Rybchenko }
19955e111ed8SAndrew Rybchenko 
19965e111ed8SAndrew Rybchenko 	__checkReturn	efx_rc_t
19975e111ed8SAndrew Rybchenko efx_mcdi_mac_stats_periodic(
19985e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
19995e111ed8SAndrew Rybchenko 	__in		efsys_mem_t *esmp,
20005e111ed8SAndrew Rybchenko 	__in		uint16_t period_ms,
20015e111ed8SAndrew Rybchenko 	__in		boolean_t events)
20025e111ed8SAndrew Rybchenko {
20035e111ed8SAndrew Rybchenko 	efx_rc_t rc;
20045e111ed8SAndrew Rybchenko 
20055e111ed8SAndrew Rybchenko 	/*
20065e111ed8SAndrew Rybchenko 	 * The MC DMAs aggregate statistics for our convenience, so we can
20075e111ed8SAndrew Rybchenko 	 * avoid having to pull the statistics buffer into the cache to
20085e111ed8SAndrew Rybchenko 	 * maintain cumulative statistics.
20095e111ed8SAndrew Rybchenko 	 * Huntington uses a fixed 1sec period.
20105e111ed8SAndrew Rybchenko 	 * Medford uses a fixed 1sec period before v6.2.1.1033 firmware.
20115e111ed8SAndrew Rybchenko 	 */
20125e111ed8SAndrew Rybchenko 	if (period_ms == 0)
20135e111ed8SAndrew Rybchenko 		rc = efx_mcdi_mac_stats(enp, enp->en_vport_id, NULL,
20145e111ed8SAndrew Rybchenko 			EFX_STATS_DISABLE, 0);
20155e111ed8SAndrew Rybchenko 	else if (events)
20165e111ed8SAndrew Rybchenko 		rc = efx_mcdi_mac_stats(enp, enp->en_vport_id, esmp,
20175e111ed8SAndrew Rybchenko 			EFX_STATS_ENABLE_EVENTS, period_ms);
20185e111ed8SAndrew Rybchenko 	else
20195e111ed8SAndrew Rybchenko 		rc = efx_mcdi_mac_stats(enp, enp->en_vport_id, esmp,
20205e111ed8SAndrew Rybchenko 			EFX_STATS_ENABLE_NOEVENTS, period_ms);
20215e111ed8SAndrew Rybchenko 
20225e111ed8SAndrew Rybchenko 	if (rc != 0)
20235e111ed8SAndrew Rybchenko 		goto fail1;
20245e111ed8SAndrew Rybchenko 
20255e111ed8SAndrew Rybchenko 	return (0);
20265e111ed8SAndrew Rybchenko 
20275e111ed8SAndrew Rybchenko fail1:
20285e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
20295e111ed8SAndrew Rybchenko 
20305e111ed8SAndrew Rybchenko 	return (rc);
20315e111ed8SAndrew Rybchenko }
20325e111ed8SAndrew Rybchenko 
20335e111ed8SAndrew Rybchenko #endif	/* EFSYS_OPT_MAC_STATS */
20345e111ed8SAndrew Rybchenko 
20353c1c5cc4SAndrew Rybchenko #if EFSYS_OPT_RIVERHEAD || EFX_OPTS_EF10()
20365e111ed8SAndrew Rybchenko 
20375e111ed8SAndrew Rybchenko /*
20385e111ed8SAndrew Rybchenko  * This function returns the pf and vf number of a function.  If it is a pf the
20395e111ed8SAndrew Rybchenko  * vf number is 0xffff.  The vf number is the index of the vf on that
20405e111ed8SAndrew Rybchenko  * function. So if you have 3 vfs on pf 0 the 3 vfs will return (pf=0,vf=0),
20415e111ed8SAndrew Rybchenko  * (pf=0,vf=1), (pf=0,vf=2) aand the pf will return (pf=0, vf=0xffff).
20425e111ed8SAndrew Rybchenko  */
20435e111ed8SAndrew Rybchenko 	__checkReturn		efx_rc_t
20445e111ed8SAndrew Rybchenko efx_mcdi_get_function_info(
20455e111ed8SAndrew Rybchenko 	__in			efx_nic_t *enp,
20465e111ed8SAndrew Rybchenko 	__out			uint32_t *pfp,
20475e111ed8SAndrew Rybchenko 	__out_opt		uint32_t *vfp)
20485e111ed8SAndrew Rybchenko {
20495e111ed8SAndrew Rybchenko 	efx_mcdi_req_t req;
20505e111ed8SAndrew Rybchenko 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_FUNCTION_INFO_IN_LEN,
20515e111ed8SAndrew Rybchenko 		MC_CMD_GET_FUNCTION_INFO_OUT_LEN);
20525e111ed8SAndrew Rybchenko 	efx_rc_t rc;
20535e111ed8SAndrew Rybchenko 
20545e111ed8SAndrew Rybchenko 	req.emr_cmd = MC_CMD_GET_FUNCTION_INFO;
20555e111ed8SAndrew Rybchenko 	req.emr_in_buf = payload;
20565e111ed8SAndrew Rybchenko 	req.emr_in_length = MC_CMD_GET_FUNCTION_INFO_IN_LEN;
20575e111ed8SAndrew Rybchenko 	req.emr_out_buf = payload;
20585e111ed8SAndrew Rybchenko 	req.emr_out_length = MC_CMD_GET_FUNCTION_INFO_OUT_LEN;
20595e111ed8SAndrew Rybchenko 
20605e111ed8SAndrew Rybchenko 	efx_mcdi_execute(enp, &req);
20615e111ed8SAndrew Rybchenko 
20625e111ed8SAndrew Rybchenko 	if (req.emr_rc != 0) {
20635e111ed8SAndrew Rybchenko 		rc = req.emr_rc;
20645e111ed8SAndrew Rybchenko 		goto fail1;
20655e111ed8SAndrew Rybchenko 	}
20665e111ed8SAndrew Rybchenko 
20675e111ed8SAndrew Rybchenko 	if (req.emr_out_length_used < MC_CMD_GET_FUNCTION_INFO_OUT_LEN) {
20685e111ed8SAndrew Rybchenko 		rc = EMSGSIZE;
20695e111ed8SAndrew Rybchenko 		goto fail2;
20705e111ed8SAndrew Rybchenko 	}
20715e111ed8SAndrew Rybchenko 
20725e111ed8SAndrew Rybchenko 	*pfp = MCDI_OUT_DWORD(req, GET_FUNCTION_INFO_OUT_PF);
20735e111ed8SAndrew Rybchenko 	if (vfp != NULL)
20745e111ed8SAndrew Rybchenko 		*vfp = MCDI_OUT_DWORD(req, GET_FUNCTION_INFO_OUT_VF);
20755e111ed8SAndrew Rybchenko 
20765e111ed8SAndrew Rybchenko 	return (0);
20775e111ed8SAndrew Rybchenko 
20785e111ed8SAndrew Rybchenko fail2:
20795e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail2);
20805e111ed8SAndrew Rybchenko fail1:
20815e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
20825e111ed8SAndrew Rybchenko 
20835e111ed8SAndrew Rybchenko 	return (rc);
20845e111ed8SAndrew Rybchenko }
20855e111ed8SAndrew Rybchenko 
20865e111ed8SAndrew Rybchenko 	__checkReturn		efx_rc_t
20875e111ed8SAndrew Rybchenko efx_mcdi_privilege_mask(
20885e111ed8SAndrew Rybchenko 	__in			efx_nic_t *enp,
20895e111ed8SAndrew Rybchenko 	__in			uint32_t pf,
20905e111ed8SAndrew Rybchenko 	__in			uint32_t vf,
20915e111ed8SAndrew Rybchenko 	__out			uint32_t *maskp)
20925e111ed8SAndrew Rybchenko {
20935e111ed8SAndrew Rybchenko 	efx_mcdi_req_t req;
20945e111ed8SAndrew Rybchenko 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_PRIVILEGE_MASK_IN_LEN,
20955e111ed8SAndrew Rybchenko 		MC_CMD_PRIVILEGE_MASK_OUT_LEN);
20965e111ed8SAndrew Rybchenko 	efx_rc_t rc;
20975e111ed8SAndrew Rybchenko 
20985e111ed8SAndrew Rybchenko 	req.emr_cmd = MC_CMD_PRIVILEGE_MASK;
20995e111ed8SAndrew Rybchenko 	req.emr_in_buf = payload;
21005e111ed8SAndrew Rybchenko 	req.emr_in_length = MC_CMD_PRIVILEGE_MASK_IN_LEN;
21015e111ed8SAndrew Rybchenko 	req.emr_out_buf = payload;
21025e111ed8SAndrew Rybchenko 	req.emr_out_length = MC_CMD_PRIVILEGE_MASK_OUT_LEN;
21035e111ed8SAndrew Rybchenko 
21045e111ed8SAndrew Rybchenko 	MCDI_IN_POPULATE_DWORD_2(req, PRIVILEGE_MASK_IN_FUNCTION,
21055e111ed8SAndrew Rybchenko 	    PRIVILEGE_MASK_IN_FUNCTION_PF, pf,
21065e111ed8SAndrew Rybchenko 	    PRIVILEGE_MASK_IN_FUNCTION_VF, vf);
21075e111ed8SAndrew Rybchenko 
21085e111ed8SAndrew Rybchenko 	efx_mcdi_execute(enp, &req);
21095e111ed8SAndrew Rybchenko 
21105e111ed8SAndrew Rybchenko 	if (req.emr_rc != 0) {
21115e111ed8SAndrew Rybchenko 		rc = req.emr_rc;
21125e111ed8SAndrew Rybchenko 		goto fail1;
21135e111ed8SAndrew Rybchenko 	}
21145e111ed8SAndrew Rybchenko 
21155e111ed8SAndrew Rybchenko 	if (req.emr_out_length_used < MC_CMD_PRIVILEGE_MASK_OUT_LEN) {
21165e111ed8SAndrew Rybchenko 		rc = EMSGSIZE;
21175e111ed8SAndrew Rybchenko 		goto fail2;
21185e111ed8SAndrew Rybchenko 	}
21195e111ed8SAndrew Rybchenko 
21205e111ed8SAndrew Rybchenko 	*maskp = MCDI_OUT_DWORD(req, PRIVILEGE_MASK_OUT_OLD_MASK);
21215e111ed8SAndrew Rybchenko 
21225e111ed8SAndrew Rybchenko 	return (0);
21235e111ed8SAndrew Rybchenko 
21245e111ed8SAndrew Rybchenko fail2:
21255e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail2);
21265e111ed8SAndrew Rybchenko fail1:
21275e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
21285e111ed8SAndrew Rybchenko 
21295e111ed8SAndrew Rybchenko 	return (rc);
21305e111ed8SAndrew Rybchenko }
21315e111ed8SAndrew Rybchenko 
21323c1c5cc4SAndrew Rybchenko #endif /* EFSYS_OPT_RIVERHEAD || EFX_OPTS_EF10() */
21335e111ed8SAndrew Rybchenko 
21345e111ed8SAndrew Rybchenko 	__checkReturn		efx_rc_t
21355e111ed8SAndrew Rybchenko efx_mcdi_set_workaround(
21365e111ed8SAndrew Rybchenko 	__in			efx_nic_t *enp,
21375e111ed8SAndrew Rybchenko 	__in			uint32_t type,
21385e111ed8SAndrew Rybchenko 	__in			boolean_t enabled,
21395e111ed8SAndrew Rybchenko 	__out_opt		uint32_t *flagsp)
21405e111ed8SAndrew Rybchenko {
21415e111ed8SAndrew Rybchenko 	efx_mcdi_req_t req;
21425e111ed8SAndrew Rybchenko 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_WORKAROUND_IN_LEN,
21435e111ed8SAndrew Rybchenko 		MC_CMD_WORKAROUND_EXT_OUT_LEN);
21445e111ed8SAndrew Rybchenko 	efx_rc_t rc;
21455e111ed8SAndrew Rybchenko 
21465e111ed8SAndrew Rybchenko 	req.emr_cmd = MC_CMD_WORKAROUND;
21475e111ed8SAndrew Rybchenko 	req.emr_in_buf = payload;
21485e111ed8SAndrew Rybchenko 	req.emr_in_length = MC_CMD_WORKAROUND_IN_LEN;
21495e111ed8SAndrew Rybchenko 	req.emr_out_buf = payload;
21505e111ed8SAndrew Rybchenko 	req.emr_out_length = MC_CMD_WORKAROUND_OUT_LEN;
21515e111ed8SAndrew Rybchenko 
21525e111ed8SAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, WORKAROUND_IN_TYPE, type);
21535e111ed8SAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, WORKAROUND_IN_ENABLED, enabled ? 1 : 0);
21545e111ed8SAndrew Rybchenko 
21555e111ed8SAndrew Rybchenko 	efx_mcdi_execute_quiet(enp, &req);
21565e111ed8SAndrew Rybchenko 
21575e111ed8SAndrew Rybchenko 	if (req.emr_rc != 0) {
21585e111ed8SAndrew Rybchenko 		rc = req.emr_rc;
21595e111ed8SAndrew Rybchenko 		goto fail1;
21605e111ed8SAndrew Rybchenko 	}
21615e111ed8SAndrew Rybchenko 
21625e111ed8SAndrew Rybchenko 	if (flagsp != NULL) {
21635e111ed8SAndrew Rybchenko 		if (req.emr_out_length_used >= MC_CMD_WORKAROUND_EXT_OUT_LEN)
21645e111ed8SAndrew Rybchenko 			*flagsp = MCDI_OUT_DWORD(req, WORKAROUND_EXT_OUT_FLAGS);
21655e111ed8SAndrew Rybchenko 		else
21665e111ed8SAndrew Rybchenko 			*flagsp = 0;
21675e111ed8SAndrew Rybchenko 	}
21685e111ed8SAndrew Rybchenko 
21695e111ed8SAndrew Rybchenko 	return (0);
21705e111ed8SAndrew Rybchenko 
21715e111ed8SAndrew Rybchenko fail1:
21725e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
21735e111ed8SAndrew Rybchenko 
21745e111ed8SAndrew Rybchenko 	return (rc);
21755e111ed8SAndrew Rybchenko }
21765e111ed8SAndrew Rybchenko 
21775e111ed8SAndrew Rybchenko 
21785e111ed8SAndrew Rybchenko 	__checkReturn		efx_rc_t
21795e111ed8SAndrew Rybchenko efx_mcdi_get_workarounds(
21805e111ed8SAndrew Rybchenko 	__in			efx_nic_t *enp,
21815e111ed8SAndrew Rybchenko 	__out_opt		uint32_t *implementedp,
21825e111ed8SAndrew Rybchenko 	__out_opt		uint32_t *enabledp)
21835e111ed8SAndrew Rybchenko {
21845e111ed8SAndrew Rybchenko 	efx_mcdi_req_t req;
21855e111ed8SAndrew Rybchenko 	EFX_MCDI_DECLARE_BUF(payload, 0, MC_CMD_GET_WORKAROUNDS_OUT_LEN);
21865e111ed8SAndrew Rybchenko 	efx_rc_t rc;
21875e111ed8SAndrew Rybchenko 
21885e111ed8SAndrew Rybchenko 	req.emr_cmd = MC_CMD_GET_WORKAROUNDS;
21895e111ed8SAndrew Rybchenko 	req.emr_in_buf = NULL;
21905e111ed8SAndrew Rybchenko 	req.emr_in_length = 0;
21915e111ed8SAndrew Rybchenko 	req.emr_out_buf = payload;
21925e111ed8SAndrew Rybchenko 	req.emr_out_length = MC_CMD_GET_WORKAROUNDS_OUT_LEN;
21935e111ed8SAndrew Rybchenko 
21945e111ed8SAndrew Rybchenko 	efx_mcdi_execute(enp, &req);
21955e111ed8SAndrew Rybchenko 
21965e111ed8SAndrew Rybchenko 	if (req.emr_rc != 0) {
21975e111ed8SAndrew Rybchenko 		rc = req.emr_rc;
21985e111ed8SAndrew Rybchenko 		goto fail1;
21995e111ed8SAndrew Rybchenko 	}
22005e111ed8SAndrew Rybchenko 
22015e111ed8SAndrew Rybchenko 	if (implementedp != NULL) {
22025e111ed8SAndrew Rybchenko 		*implementedp =
22035e111ed8SAndrew Rybchenko 		    MCDI_OUT_DWORD(req, GET_WORKAROUNDS_OUT_IMPLEMENTED);
22045e111ed8SAndrew Rybchenko 	}
22055e111ed8SAndrew Rybchenko 
22065e111ed8SAndrew Rybchenko 	if (enabledp != NULL) {
22075e111ed8SAndrew Rybchenko 		*enabledp = MCDI_OUT_DWORD(req, GET_WORKAROUNDS_OUT_ENABLED);
22085e111ed8SAndrew Rybchenko 	}
22095e111ed8SAndrew Rybchenko 
22105e111ed8SAndrew Rybchenko 	return (0);
22115e111ed8SAndrew Rybchenko 
22125e111ed8SAndrew Rybchenko fail1:
22135e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
22145e111ed8SAndrew Rybchenko 
22155e111ed8SAndrew Rybchenko 	return (rc);
22165e111ed8SAndrew Rybchenko }
22175e111ed8SAndrew Rybchenko 
22185e111ed8SAndrew Rybchenko /*
22195e111ed8SAndrew Rybchenko  * Size of media information page in accordance with SFF-8472 and SFF-8436.
22205e111ed8SAndrew Rybchenko  * It is used in MCDI interface as well.
22215e111ed8SAndrew Rybchenko  */
22225e111ed8SAndrew Rybchenko #define	EFX_PHY_MEDIA_INFO_PAGE_SIZE		0x80
22235e111ed8SAndrew Rybchenko 
22245e111ed8SAndrew Rybchenko /*
22255e111ed8SAndrew Rybchenko  * Transceiver identifiers from SFF-8024 Table 4-1.
22265e111ed8SAndrew Rybchenko  */
22275e111ed8SAndrew Rybchenko #define	EFX_SFF_TRANSCEIVER_ID_SFP		0x03 /* SFP/SFP+/SFP28 */
22285e111ed8SAndrew Rybchenko #define	EFX_SFF_TRANSCEIVER_ID_QSFP		0x0c /* QSFP */
22295e111ed8SAndrew Rybchenko #define	EFX_SFF_TRANSCEIVER_ID_QSFP_PLUS	0x0d /* QSFP+ or later */
22305e111ed8SAndrew Rybchenko #define	EFX_SFF_TRANSCEIVER_ID_QSFP28		0x11 /* QSFP28 or later */
22315e111ed8SAndrew Rybchenko 
22325e111ed8SAndrew Rybchenko static	__checkReturn		efx_rc_t
22335e111ed8SAndrew Rybchenko efx_mcdi_get_phy_media_info(
22345e111ed8SAndrew Rybchenko 	__in			efx_nic_t *enp,
22355e111ed8SAndrew Rybchenko 	__in			uint32_t mcdi_page,
22365e111ed8SAndrew Rybchenko 	__in			uint8_t offset,
22375e111ed8SAndrew Rybchenko 	__in			uint8_t len,
22385e111ed8SAndrew Rybchenko 	__out_bcount(len)	uint8_t *data)
22395e111ed8SAndrew Rybchenko {
22405e111ed8SAndrew Rybchenko 	efx_mcdi_req_t req;
22415e111ed8SAndrew Rybchenko 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN,
22425e111ed8SAndrew Rybchenko 		MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(
22435e111ed8SAndrew Rybchenko 			EFX_PHY_MEDIA_INFO_PAGE_SIZE));
22445e111ed8SAndrew Rybchenko 	efx_rc_t rc;
22455e111ed8SAndrew Rybchenko 
22465e111ed8SAndrew Rybchenko 	EFSYS_ASSERT((uint32_t)offset + len <= EFX_PHY_MEDIA_INFO_PAGE_SIZE);
22475e111ed8SAndrew Rybchenko 
22485e111ed8SAndrew Rybchenko 	req.emr_cmd = MC_CMD_GET_PHY_MEDIA_INFO;
22495e111ed8SAndrew Rybchenko 	req.emr_in_buf = payload;
22505e111ed8SAndrew Rybchenko 	req.emr_in_length = MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN;
22515e111ed8SAndrew Rybchenko 	req.emr_out_buf = payload;
22525e111ed8SAndrew Rybchenko 	req.emr_out_length =
22535e111ed8SAndrew Rybchenko 	    MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(EFX_PHY_MEDIA_INFO_PAGE_SIZE);
22545e111ed8SAndrew Rybchenko 
22555e111ed8SAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, GET_PHY_MEDIA_INFO_IN_PAGE, mcdi_page);
22565e111ed8SAndrew Rybchenko 
22575e111ed8SAndrew Rybchenko 	efx_mcdi_execute(enp, &req);
22585e111ed8SAndrew Rybchenko 
22595e111ed8SAndrew Rybchenko 	if (req.emr_rc != 0) {
22605e111ed8SAndrew Rybchenko 		rc = req.emr_rc;
22615e111ed8SAndrew Rybchenko 		goto fail1;
22625e111ed8SAndrew Rybchenko 	}
22635e111ed8SAndrew Rybchenko 
22645e111ed8SAndrew Rybchenko 	if (req.emr_out_length_used !=
22655e111ed8SAndrew Rybchenko 	    MC_CMD_GET_PHY_MEDIA_INFO_OUT_LEN(EFX_PHY_MEDIA_INFO_PAGE_SIZE)) {
22665e111ed8SAndrew Rybchenko 		rc = EMSGSIZE;
22675e111ed8SAndrew Rybchenko 		goto fail2;
22685e111ed8SAndrew Rybchenko 	}
22695e111ed8SAndrew Rybchenko 
22705e111ed8SAndrew Rybchenko 	if (MCDI_OUT_DWORD(req, GET_PHY_MEDIA_INFO_OUT_DATALEN) !=
22715e111ed8SAndrew Rybchenko 	    EFX_PHY_MEDIA_INFO_PAGE_SIZE) {
22725e111ed8SAndrew Rybchenko 		rc = EIO;
22735e111ed8SAndrew Rybchenko 		goto fail3;
22745e111ed8SAndrew Rybchenko 	}
22755e111ed8SAndrew Rybchenko 
22765e111ed8SAndrew Rybchenko 	memcpy(data,
22775e111ed8SAndrew Rybchenko 	    MCDI_OUT2(req, uint8_t, GET_PHY_MEDIA_INFO_OUT_DATA) + offset,
22785e111ed8SAndrew Rybchenko 	    len);
22795e111ed8SAndrew Rybchenko 
22805e111ed8SAndrew Rybchenko 	return (0);
22815e111ed8SAndrew Rybchenko 
22825e111ed8SAndrew Rybchenko fail3:
22835e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail3);
22845e111ed8SAndrew Rybchenko fail2:
22855e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail2);
22865e111ed8SAndrew Rybchenko fail1:
22875e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
22885e111ed8SAndrew Rybchenko 
22895e111ed8SAndrew Rybchenko 	return (rc);
22905e111ed8SAndrew Rybchenko }
22915e111ed8SAndrew Rybchenko 
22925e111ed8SAndrew Rybchenko 	__checkReturn		efx_rc_t
22935e111ed8SAndrew Rybchenko efx_mcdi_phy_module_get_info(
22945e111ed8SAndrew Rybchenko 	__in			efx_nic_t *enp,
22955e111ed8SAndrew Rybchenko 	__in			uint8_t dev_addr,
22965e111ed8SAndrew Rybchenko 	__in			size_t offset,
22975e111ed8SAndrew Rybchenko 	__in			size_t len,
22985e111ed8SAndrew Rybchenko 	__out_bcount(len)	uint8_t *data)
22995e111ed8SAndrew Rybchenko {
23005e111ed8SAndrew Rybchenko 	efx_port_t *epp = &(enp->en_port);
23015e111ed8SAndrew Rybchenko 	efx_rc_t rc;
23025e111ed8SAndrew Rybchenko 	uint32_t mcdi_lower_page;
23035e111ed8SAndrew Rybchenko 	uint32_t mcdi_upper_page;
23045e111ed8SAndrew Rybchenko 	uint8_t id;
23055e111ed8SAndrew Rybchenko 
23065e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
23075e111ed8SAndrew Rybchenko 
23085e111ed8SAndrew Rybchenko 	/*
23095e111ed8SAndrew Rybchenko 	 * Map device address to MC_CMD_GET_PHY_MEDIA_INFO pages.
23105e111ed8SAndrew Rybchenko 	 * Offset plus length interface allows to access page 0 only.
23115e111ed8SAndrew Rybchenko 	 * I.e. non-zero upper pages are not accessible.
23125e111ed8SAndrew Rybchenko 	 * See SFF-8472 section 4 Memory Organization and SFF-8436 section 7.6
23135e111ed8SAndrew Rybchenko 	 * QSFP+ Memory Map for details on how information is structured
23145e111ed8SAndrew Rybchenko 	 * and accessible.
23155e111ed8SAndrew Rybchenko 	 */
23165e111ed8SAndrew Rybchenko 	switch (epp->ep_fixed_port_type) {
23175e111ed8SAndrew Rybchenko 	case EFX_PHY_MEDIA_SFP_PLUS:
23185e111ed8SAndrew Rybchenko 	case EFX_PHY_MEDIA_QSFP_PLUS:
23195e111ed8SAndrew Rybchenko 		/* Port type supports modules */
23205e111ed8SAndrew Rybchenko 		break;
23215e111ed8SAndrew Rybchenko 	default:
23225e111ed8SAndrew Rybchenko 		rc = ENOTSUP;
23235e111ed8SAndrew Rybchenko 		goto fail1;
23245e111ed8SAndrew Rybchenko 	}
23255e111ed8SAndrew Rybchenko 
23265e111ed8SAndrew Rybchenko 	/*
23275e111ed8SAndrew Rybchenko 	 * For all supported port types, MCDI page 0 offset 0 holds the
23285e111ed8SAndrew Rybchenko 	 * transceiver identifier. Probe to determine the data layout.
23295e111ed8SAndrew Rybchenko 	 * Definitions from SFF-8024 Table 4-1.
23305e111ed8SAndrew Rybchenko 	 */
23315e111ed8SAndrew Rybchenko 	rc = efx_mcdi_get_phy_media_info(enp,
23325e111ed8SAndrew Rybchenko 		    0, 0, sizeof(id), &id);
23335e111ed8SAndrew Rybchenko 	if (rc != 0)
23345e111ed8SAndrew Rybchenko 		goto fail2;
23355e111ed8SAndrew Rybchenko 
23365e111ed8SAndrew Rybchenko 	switch (id) {
23375e111ed8SAndrew Rybchenko 	case EFX_SFF_TRANSCEIVER_ID_SFP:
23385e111ed8SAndrew Rybchenko 		/*
23395e111ed8SAndrew Rybchenko 		 * In accordance with SFF-8472 Diagnostic Monitoring
23405e111ed8SAndrew Rybchenko 		 * Interface for Optical Transceivers section 4 Memory
23415e111ed8SAndrew Rybchenko 		 * Organization two 2-wire addresses are defined.
23425e111ed8SAndrew Rybchenko 		 */
23435e111ed8SAndrew Rybchenko 		switch (dev_addr) {
23445e111ed8SAndrew Rybchenko 		/* Base information */
23455e111ed8SAndrew Rybchenko 		case EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_BASE:
23465e111ed8SAndrew Rybchenko 			/*
23475e111ed8SAndrew Rybchenko 			 * MCDI page 0 should be used to access lower
23485e111ed8SAndrew Rybchenko 			 * page 0 (0x00 - 0x7f) at the device address 0xA0.
23495e111ed8SAndrew Rybchenko 			 */
23505e111ed8SAndrew Rybchenko 			mcdi_lower_page = 0;
23515e111ed8SAndrew Rybchenko 			/*
23525e111ed8SAndrew Rybchenko 			 * MCDI page 1 should be used to access  upper
23535e111ed8SAndrew Rybchenko 			 * page 0 (0x80 - 0xff) at the device address 0xA0.
23545e111ed8SAndrew Rybchenko 			 */
23555e111ed8SAndrew Rybchenko 			mcdi_upper_page = 1;
23565e111ed8SAndrew Rybchenko 			break;
23575e111ed8SAndrew Rybchenko 		/* Diagnostics */
23585e111ed8SAndrew Rybchenko 		case EFX_PHY_MEDIA_INFO_DEV_ADDR_SFP_DDM:
23595e111ed8SAndrew Rybchenko 			/*
23605e111ed8SAndrew Rybchenko 			 * MCDI page 2 should be used to access lower
23615e111ed8SAndrew Rybchenko 			 * page 0 (0x00 - 0x7f) at the device address 0xA2.
23625e111ed8SAndrew Rybchenko 			 */
23635e111ed8SAndrew Rybchenko 			mcdi_lower_page = 2;
23645e111ed8SAndrew Rybchenko 			/*
23655e111ed8SAndrew Rybchenko 			 * MCDI page 3 should be used to access upper
23665e111ed8SAndrew Rybchenko 			 * page 0 (0x80 - 0xff) at the device address 0xA2.
23675e111ed8SAndrew Rybchenko 			 */
23685e111ed8SAndrew Rybchenko 			mcdi_upper_page = 3;
23695e111ed8SAndrew Rybchenko 			break;
23705e111ed8SAndrew Rybchenko 		default:
23715e111ed8SAndrew Rybchenko 			rc = ENOTSUP;
23725e111ed8SAndrew Rybchenko 			goto fail3;
23735e111ed8SAndrew Rybchenko 		}
23745e111ed8SAndrew Rybchenko 		break;
23755e111ed8SAndrew Rybchenko 	case EFX_SFF_TRANSCEIVER_ID_QSFP:
23765e111ed8SAndrew Rybchenko 	case EFX_SFF_TRANSCEIVER_ID_QSFP_PLUS:
23775e111ed8SAndrew Rybchenko 	case EFX_SFF_TRANSCEIVER_ID_QSFP28:
23785e111ed8SAndrew Rybchenko 		switch (dev_addr) {
23795e111ed8SAndrew Rybchenko 		case EFX_PHY_MEDIA_INFO_DEV_ADDR_QSFP:
23805e111ed8SAndrew Rybchenko 			/*
23815e111ed8SAndrew Rybchenko 			 * MCDI page -1 should be used to access lower page 0
23825e111ed8SAndrew Rybchenko 			 * (0x00 - 0x7f).
23835e111ed8SAndrew Rybchenko 			 */
23845e111ed8SAndrew Rybchenko 			mcdi_lower_page = (uint32_t)-1;
23855e111ed8SAndrew Rybchenko 			/*
23865e111ed8SAndrew Rybchenko 			 * MCDI page 0 should be used to access upper page 0
23875e111ed8SAndrew Rybchenko 			 * (0x80h - 0xff).
23885e111ed8SAndrew Rybchenko 			 */
23895e111ed8SAndrew Rybchenko 			mcdi_upper_page = 0;
23905e111ed8SAndrew Rybchenko 			break;
23915e111ed8SAndrew Rybchenko 		default:
23925e111ed8SAndrew Rybchenko 			rc = ENOTSUP;
23935e111ed8SAndrew Rybchenko 			goto fail3;
23945e111ed8SAndrew Rybchenko 		}
23955e111ed8SAndrew Rybchenko 		break;
23965e111ed8SAndrew Rybchenko 	default:
23975e111ed8SAndrew Rybchenko 		rc = ENOTSUP;
23985e111ed8SAndrew Rybchenko 		goto fail3;
23995e111ed8SAndrew Rybchenko 	}
24005e111ed8SAndrew Rybchenko 
24015e111ed8SAndrew Rybchenko 	EFX_STATIC_ASSERT(EFX_PHY_MEDIA_INFO_PAGE_SIZE <= 0xFF);
24025e111ed8SAndrew Rybchenko 
24035e111ed8SAndrew Rybchenko 	if (offset < EFX_PHY_MEDIA_INFO_PAGE_SIZE) {
24045e111ed8SAndrew Rybchenko 		size_t read_len =
24055e111ed8SAndrew Rybchenko 		    MIN(len, EFX_PHY_MEDIA_INFO_PAGE_SIZE - offset);
24065e111ed8SAndrew Rybchenko 
24075e111ed8SAndrew Rybchenko 		rc = efx_mcdi_get_phy_media_info(enp,
24085e111ed8SAndrew Rybchenko 		    mcdi_lower_page, (uint8_t)offset, (uint8_t)read_len, data);
24095e111ed8SAndrew Rybchenko 		if (rc != 0)
24105e111ed8SAndrew Rybchenko 			goto fail4;
24115e111ed8SAndrew Rybchenko 
24125e111ed8SAndrew Rybchenko 		data += read_len;
24135e111ed8SAndrew Rybchenko 		len -= read_len;
24145e111ed8SAndrew Rybchenko 
24155e111ed8SAndrew Rybchenko 		offset = 0;
24165e111ed8SAndrew Rybchenko 	} else {
24175e111ed8SAndrew Rybchenko 		offset -= EFX_PHY_MEDIA_INFO_PAGE_SIZE;
24185e111ed8SAndrew Rybchenko 	}
24195e111ed8SAndrew Rybchenko 
24205e111ed8SAndrew Rybchenko 	if (len > 0) {
24215e111ed8SAndrew Rybchenko 		EFSYS_ASSERT3U(len, <=, EFX_PHY_MEDIA_INFO_PAGE_SIZE);
24225e111ed8SAndrew Rybchenko 		EFSYS_ASSERT3U(offset, <, EFX_PHY_MEDIA_INFO_PAGE_SIZE);
24235e111ed8SAndrew Rybchenko 
24245e111ed8SAndrew Rybchenko 		rc = efx_mcdi_get_phy_media_info(enp,
24255e111ed8SAndrew Rybchenko 		    mcdi_upper_page, (uint8_t)offset, (uint8_t)len, data);
24265e111ed8SAndrew Rybchenko 		if (rc != 0)
24275e111ed8SAndrew Rybchenko 			goto fail5;
24285e111ed8SAndrew Rybchenko 	}
24295e111ed8SAndrew Rybchenko 
24305e111ed8SAndrew Rybchenko 	return (0);
24315e111ed8SAndrew Rybchenko 
24325e111ed8SAndrew Rybchenko fail5:
24335e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail5);
24345e111ed8SAndrew Rybchenko fail4:
24355e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail4);
24365e111ed8SAndrew Rybchenko fail3:
24375e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail3);
24385e111ed8SAndrew Rybchenko fail2:
24395e111ed8SAndrew Rybchenko 	EFSYS_PROBE(fail2);
24405e111ed8SAndrew Rybchenko fail1:
24415e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
24425e111ed8SAndrew Rybchenko 
24435e111ed8SAndrew Rybchenko 	return (rc);
24445e111ed8SAndrew Rybchenko }
24455e111ed8SAndrew Rybchenko 
2446b97bf1caSAndrew Rybchenko #if EFSYS_OPT_RIVERHEAD || EFX_OPTS_EF10()
2447b97bf1caSAndrew Rybchenko 
2448b97bf1caSAndrew Rybchenko #define	INIT_EVQ_MAXNBUFS	MC_CMD_INIT_EVQ_V2_IN_DMA_ADDR_MAXNUM
2449b97bf1caSAndrew Rybchenko 
245085270581SAndrew Rybchenko #if EFX_OPTS_EF10()
2451b97bf1caSAndrew Rybchenko # if (INIT_EVQ_MAXNBUFS < EF10_EVQ_MAXNBUFS)
2452b97bf1caSAndrew Rybchenko #  error "INIT_EVQ_MAXNBUFS too small"
2453b97bf1caSAndrew Rybchenko # endif
2454b97bf1caSAndrew Rybchenko #endif /* EFX_OPTS_EF10 */
2455b97bf1caSAndrew Rybchenko #if EFSYS_OPT_RIVERHEAD
2456b97bf1caSAndrew Rybchenko # if (INIT_EVQ_MAXNBUFS < RHEAD_EVQ_MAXNBUFS)
2457b97bf1caSAndrew Rybchenko #  error "INIT_EVQ_MAXNBUFS too small"
2458b97bf1caSAndrew Rybchenko # endif
2459b97bf1caSAndrew Rybchenko #endif /* EFSYS_OPT_RIVERHEAD */
246085270581SAndrew Rybchenko 
246185270581SAndrew Rybchenko 	__checkReturn	efx_rc_t
246285270581SAndrew Rybchenko efx_mcdi_init_evq(
246385270581SAndrew Rybchenko 	__in		efx_nic_t *enp,
246485270581SAndrew Rybchenko 	__in		unsigned int instance,
246585270581SAndrew Rybchenko 	__in		efsys_mem_t *esmp,
246685270581SAndrew Rybchenko 	__in		size_t nevs,
246785270581SAndrew Rybchenko 	__in		uint32_t irq,
246885270581SAndrew Rybchenko 	__in		uint32_t us,
246985270581SAndrew Rybchenko 	__in		uint32_t flags,
247085270581SAndrew Rybchenko 	__in		boolean_t low_latency)
247185270581SAndrew Rybchenko {
24728aad1149SAndrew Rybchenko 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(enp);
247385270581SAndrew Rybchenko 	efx_mcdi_req_t req;
247485270581SAndrew Rybchenko 	EFX_MCDI_DECLARE_BUF(payload,
2475b97bf1caSAndrew Rybchenko 		MC_CMD_INIT_EVQ_V2_IN_LEN(INIT_EVQ_MAXNBUFS),
247613a300a5SAndrew Rybchenko 		MC_CMD_INIT_EVQ_V2_OUT_LEN);
247785270581SAndrew Rybchenko 	boolean_t interrupting;
247885270581SAndrew Rybchenko 	int ev_cut_through;
24798aad1149SAndrew Rybchenko 	int ev_merge;
248085270581SAndrew Rybchenko 	unsigned int evq_type;
248185270581SAndrew Rybchenko 	efx_qword_t *dma_addr;
248285270581SAndrew Rybchenko 	uint64_t addr;
248385270581SAndrew Rybchenko 	int npages;
248485270581SAndrew Rybchenko 	int i;
248585270581SAndrew Rybchenko 	efx_rc_t rc;
248685270581SAndrew Rybchenko 
248785270581SAndrew Rybchenko 	npages = efx_evq_nbufs(enp, nevs);
2488b97bf1caSAndrew Rybchenko 	if (npages > INIT_EVQ_MAXNBUFS) {
248985270581SAndrew Rybchenko 		rc = EINVAL;
249085270581SAndrew Rybchenko 		goto fail1;
249185270581SAndrew Rybchenko 	}
249285270581SAndrew Rybchenko 
249385270581SAndrew Rybchenko 	req.emr_cmd = MC_CMD_INIT_EVQ;
249485270581SAndrew Rybchenko 	req.emr_in_buf = payload;
249585270581SAndrew Rybchenko 	req.emr_in_length = MC_CMD_INIT_EVQ_V2_IN_LEN(npages);
249685270581SAndrew Rybchenko 	req.emr_out_buf = payload;
249785270581SAndrew Rybchenko 	req.emr_out_length = MC_CMD_INIT_EVQ_V2_OUT_LEN;
249885270581SAndrew Rybchenko 
249985270581SAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_SIZE, nevs);
250085270581SAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_INSTANCE, instance);
250185270581SAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_IRQ_NUM, irq);
250285270581SAndrew Rybchenko 
250385270581SAndrew Rybchenko 	interrupting = ((flags & EFX_EVQ_FLAGS_NOTIFY_MASK) ==
250485270581SAndrew Rybchenko 	    EFX_EVQ_FLAGS_NOTIFY_INTERRUPT);
250585270581SAndrew Rybchenko 
25068aad1149SAndrew Rybchenko 	if (encp->enc_init_evq_v2_supported) {
25078aad1149SAndrew Rybchenko 		/*
25088aad1149SAndrew Rybchenko 		 * On Medford the low latency license is required to enable RX
25098aad1149SAndrew Rybchenko 		 * and event cut through and to disable RX batching.  If event
25108aad1149SAndrew Rybchenko 		 * queue type in flags is auto, we let the firmware decide the
25118aad1149SAndrew Rybchenko 		 * settings to use. If the adapter has a low latency license,
25128aad1149SAndrew Rybchenko 		 * it will choose the best settings for low latency, otherwise
25138aad1149SAndrew Rybchenko 		 * it will choose the best settings for throughput.
25148aad1149SAndrew Rybchenko 		 */
251585270581SAndrew Rybchenko 		switch (flags & EFX_EVQ_FLAGS_TYPE_MASK) {
251685270581SAndrew Rybchenko 		case EFX_EVQ_FLAGS_TYPE_AUTO:
251785270581SAndrew Rybchenko 			evq_type = MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_AUTO;
251885270581SAndrew Rybchenko 			break;
251985270581SAndrew Rybchenko 		case EFX_EVQ_FLAGS_TYPE_THROUGHPUT:
252085270581SAndrew Rybchenko 			evq_type = MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_THROUGHPUT;
252185270581SAndrew Rybchenko 			break;
252285270581SAndrew Rybchenko 		case EFX_EVQ_FLAGS_TYPE_LOW_LATENCY:
252385270581SAndrew Rybchenko 			evq_type = MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_LOW_LATENCY;
252485270581SAndrew Rybchenko 			break;
252585270581SAndrew Rybchenko 		default:
252685270581SAndrew Rybchenko 			rc = EINVAL;
252785270581SAndrew Rybchenko 			goto fail2;
252885270581SAndrew Rybchenko 		}
25298aad1149SAndrew Rybchenko 		/* EvQ type controls merging, no manual settings */
25308aad1149SAndrew Rybchenko 		ev_merge = 0;
25318aad1149SAndrew Rybchenko 		ev_cut_through = 0;
25328aad1149SAndrew Rybchenko 	} else {
25338aad1149SAndrew Rybchenko 		/* EvQ types other than manual are not supported */
25348aad1149SAndrew Rybchenko 		evq_type = MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_MANUAL;
25358aad1149SAndrew Rybchenko 		/*
25368aad1149SAndrew Rybchenko 		 * On Huntington RX and TX event batching can only be requested
25378aad1149SAndrew Rybchenko 		 * together (even if the datapath firmware doesn't actually
25388aad1149SAndrew Rybchenko 		 * support RX batching). If event cut through is enabled no RX
25398aad1149SAndrew Rybchenko 		 * batching will occur.
25408aad1149SAndrew Rybchenko 		 *
25418aad1149SAndrew Rybchenko 		 * So always enable RX and TX event batching, and enable event
25428aad1149SAndrew Rybchenko 		 * cut through if we want low latency operation.
25438aad1149SAndrew Rybchenko 		 */
25448aad1149SAndrew Rybchenko 		ev_merge = 1;
25458aad1149SAndrew Rybchenko 		switch (flags & EFX_EVQ_FLAGS_TYPE_MASK) {
25468aad1149SAndrew Rybchenko 		case EFX_EVQ_FLAGS_TYPE_AUTO:
25478aad1149SAndrew Rybchenko 			ev_cut_through = low_latency ? 1 : 0;
25488aad1149SAndrew Rybchenko 			break;
25498aad1149SAndrew Rybchenko 		case EFX_EVQ_FLAGS_TYPE_THROUGHPUT:
25508aad1149SAndrew Rybchenko 			ev_cut_through = 0;
25518aad1149SAndrew Rybchenko 			break;
25528aad1149SAndrew Rybchenko 		case EFX_EVQ_FLAGS_TYPE_LOW_LATENCY:
25538aad1149SAndrew Rybchenko 			ev_cut_through = 1;
25548aad1149SAndrew Rybchenko 			break;
25558aad1149SAndrew Rybchenko 		default:
25568aad1149SAndrew Rybchenko 			rc = EINVAL;
25578aad1149SAndrew Rybchenko 			goto fail2;
25588aad1149SAndrew Rybchenko 		}
25598aad1149SAndrew Rybchenko 	}
25608aad1149SAndrew Rybchenko 
25618aad1149SAndrew Rybchenko 	MCDI_IN_POPULATE_DWORD_7(req, INIT_EVQ_V2_IN_FLAGS,
256285270581SAndrew Rybchenko 	    INIT_EVQ_V2_IN_FLAG_INTERRUPTING, interrupting,
256385270581SAndrew Rybchenko 	    INIT_EVQ_V2_IN_FLAG_RPTR_DOS, 0,
256485270581SAndrew Rybchenko 	    INIT_EVQ_V2_IN_FLAG_INT_ARMD, 0,
25658aad1149SAndrew Rybchenko 	    INIT_EVQ_V2_IN_FLAG_CUT_THRU, ev_cut_through,
25668aad1149SAndrew Rybchenko 	    INIT_EVQ_V2_IN_FLAG_RX_MERGE, ev_merge,
25678aad1149SAndrew Rybchenko 	    INIT_EVQ_V2_IN_FLAG_TX_MERGE, ev_merge,
256885270581SAndrew Rybchenko 	    INIT_EVQ_V2_IN_FLAG_TYPE, evq_type);
256985270581SAndrew Rybchenko 
257085270581SAndrew Rybchenko 	/* If the value is zero then disable the timer */
257185270581SAndrew Rybchenko 	if (us == 0) {
257285270581SAndrew Rybchenko 		MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_TMR_MODE,
257385270581SAndrew Rybchenko 		    MC_CMD_INIT_EVQ_V2_IN_TMR_MODE_DIS);
257485270581SAndrew Rybchenko 		MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_TMR_LOAD, 0);
257585270581SAndrew Rybchenko 		MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_TMR_RELOAD, 0);
257685270581SAndrew Rybchenko 	} else {
257785270581SAndrew Rybchenko 		unsigned int ticks;
257885270581SAndrew Rybchenko 
257985270581SAndrew Rybchenko 		if ((rc = efx_ev_usecs_to_ticks(enp, us, &ticks)) != 0)
258085270581SAndrew Rybchenko 			goto fail3;
258185270581SAndrew Rybchenko 
258285270581SAndrew Rybchenko 		MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_TMR_MODE,
258385270581SAndrew Rybchenko 		    MC_CMD_INIT_EVQ_V2_IN_TMR_INT_HLDOFF);
258485270581SAndrew Rybchenko 		MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_TMR_LOAD, ticks);
258585270581SAndrew Rybchenko 		MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_TMR_RELOAD, ticks);
258685270581SAndrew Rybchenko 	}
258785270581SAndrew Rybchenko 
258885270581SAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_COUNT_MODE,
258985270581SAndrew Rybchenko 	    MC_CMD_INIT_EVQ_V2_IN_COUNT_MODE_DIS);
259085270581SAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, INIT_EVQ_V2_IN_COUNT_THRSHLD, 0);
259185270581SAndrew Rybchenko 
259285270581SAndrew Rybchenko 	dma_addr = MCDI_IN2(req, efx_qword_t, INIT_EVQ_V2_IN_DMA_ADDR);
259385270581SAndrew Rybchenko 	addr = EFSYS_MEM_ADDR(esmp);
259485270581SAndrew Rybchenko 
259585270581SAndrew Rybchenko 	for (i = 0; i < npages; i++) {
259685270581SAndrew Rybchenko 		EFX_POPULATE_QWORD_2(*dma_addr,
259785270581SAndrew Rybchenko 		    EFX_DWORD_1, (uint32_t)(addr >> 32),
259885270581SAndrew Rybchenko 		    EFX_DWORD_0, (uint32_t)(addr & 0xffffffff));
259985270581SAndrew Rybchenko 
260085270581SAndrew Rybchenko 		dma_addr++;
260185270581SAndrew Rybchenko 		addr += EFX_BUF_SIZE;
260285270581SAndrew Rybchenko 	}
260385270581SAndrew Rybchenko 
260485270581SAndrew Rybchenko 	efx_mcdi_execute(enp, &req);
260585270581SAndrew Rybchenko 
260685270581SAndrew Rybchenko 	if (req.emr_rc != 0) {
260785270581SAndrew Rybchenko 		rc = req.emr_rc;
260885270581SAndrew Rybchenko 		goto fail4;
260985270581SAndrew Rybchenko 	}
261085270581SAndrew Rybchenko 
26118aad1149SAndrew Rybchenko 	if (encp->enc_init_evq_v2_supported) {
261285270581SAndrew Rybchenko 		if (req.emr_out_length_used < MC_CMD_INIT_EVQ_V2_OUT_LEN) {
261385270581SAndrew Rybchenko 			rc = EMSGSIZE;
261485270581SAndrew Rybchenko 			goto fail5;
261585270581SAndrew Rybchenko 		}
26168aad1149SAndrew Rybchenko 		EFSYS_PROBE1(mcdi_evq_flags, uint32_t,
26178aad1149SAndrew Rybchenko 			    MCDI_OUT_DWORD(req, INIT_EVQ_V2_OUT_FLAGS));
26188aad1149SAndrew Rybchenko 	} else {
26198aad1149SAndrew Rybchenko 		if (req.emr_out_length_used < MC_CMD_INIT_EVQ_OUT_LEN) {
26208aad1149SAndrew Rybchenko 			rc = EMSGSIZE;
26218aad1149SAndrew Rybchenko 			goto fail6;
26228aad1149SAndrew Rybchenko 		}
26238aad1149SAndrew Rybchenko 	}
262485270581SAndrew Rybchenko 
262585270581SAndrew Rybchenko 	/* NOTE: ignore the returned IRQ param as firmware does not set it. */
262685270581SAndrew Rybchenko 
262785270581SAndrew Rybchenko 	return (0);
262885270581SAndrew Rybchenko 
26298aad1149SAndrew Rybchenko fail6:
26308aad1149SAndrew Rybchenko 	EFSYS_PROBE(fail6);
263185270581SAndrew Rybchenko fail5:
263285270581SAndrew Rybchenko 	EFSYS_PROBE(fail5);
263385270581SAndrew Rybchenko fail4:
263485270581SAndrew Rybchenko 	EFSYS_PROBE(fail4);
263585270581SAndrew Rybchenko fail3:
263685270581SAndrew Rybchenko 	EFSYS_PROBE(fail3);
263785270581SAndrew Rybchenko fail2:
263885270581SAndrew Rybchenko 	EFSYS_PROBE(fail2);
263985270581SAndrew Rybchenko fail1:
264085270581SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
264185270581SAndrew Rybchenko 
264285270581SAndrew Rybchenko 	return (rc);
264385270581SAndrew Rybchenko }
264485270581SAndrew Rybchenko 
264585270581SAndrew Rybchenko 	__checkReturn	efx_rc_t
264685270581SAndrew Rybchenko efx_mcdi_fini_evq(
264785270581SAndrew Rybchenko 	__in		efx_nic_t *enp,
264885270581SAndrew Rybchenko 	__in		uint32_t instance)
264985270581SAndrew Rybchenko {
265085270581SAndrew Rybchenko 	efx_mcdi_req_t req;
265185270581SAndrew Rybchenko 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_FINI_EVQ_IN_LEN,
265285270581SAndrew Rybchenko 		MC_CMD_FINI_EVQ_OUT_LEN);
265385270581SAndrew Rybchenko 	efx_rc_t rc;
265485270581SAndrew Rybchenko 
265585270581SAndrew Rybchenko 	req.emr_cmd = MC_CMD_FINI_EVQ;
265685270581SAndrew Rybchenko 	req.emr_in_buf = payload;
265785270581SAndrew Rybchenko 	req.emr_in_length = MC_CMD_FINI_EVQ_IN_LEN;
265885270581SAndrew Rybchenko 	req.emr_out_buf = payload;
265985270581SAndrew Rybchenko 	req.emr_out_length = MC_CMD_FINI_EVQ_OUT_LEN;
266085270581SAndrew Rybchenko 
266185270581SAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, FINI_EVQ_IN_INSTANCE, instance);
266285270581SAndrew Rybchenko 
266385270581SAndrew Rybchenko 	efx_mcdi_execute_quiet(enp, &req);
266485270581SAndrew Rybchenko 
266585270581SAndrew Rybchenko 	if (req.emr_rc != 0) {
266685270581SAndrew Rybchenko 		rc = req.emr_rc;
266785270581SAndrew Rybchenko 		goto fail1;
266885270581SAndrew Rybchenko 	}
266985270581SAndrew Rybchenko 
267085270581SAndrew Rybchenko 	return (0);
267185270581SAndrew Rybchenko 
267285270581SAndrew Rybchenko fail1:
267385270581SAndrew Rybchenko 	/*
267485270581SAndrew Rybchenko 	 * EALREADY is not an error, but indicates that the MC has rebooted and
267585270581SAndrew Rybchenko 	 * that the EVQ has already been destroyed.
267685270581SAndrew Rybchenko 	 */
267785270581SAndrew Rybchenko 	if (rc != EALREADY)
267885270581SAndrew Rybchenko 		EFSYS_PROBE1(fail1, efx_rc_t, rc);
267985270581SAndrew Rybchenko 
268085270581SAndrew Rybchenko 	return (rc);
268185270581SAndrew Rybchenko }
268285270581SAndrew Rybchenko 
2683b97bf1caSAndrew Rybchenko #endif	/* EFSYS_OPT_RIVERHEAD || EFX_OPTS_EF10() */
268485270581SAndrew Rybchenko 
2685*09b59c7dSAndrew Rybchenko #if EFX_OPTS_EF10()
2686*09b59c7dSAndrew Rybchenko 
2687*09b59c7dSAndrew Rybchenko 	__checkReturn	efx_rc_t
2688*09b59c7dSAndrew Rybchenko efx_mcdi_init_rxq(
2689*09b59c7dSAndrew Rybchenko 	__in		efx_nic_t *enp,
2690*09b59c7dSAndrew Rybchenko 	__in		uint32_t ndescs,
2691*09b59c7dSAndrew Rybchenko 	__in		efx_evq_t *eep,
2692*09b59c7dSAndrew Rybchenko 	__in		uint32_t label,
2693*09b59c7dSAndrew Rybchenko 	__in		uint32_t instance,
2694*09b59c7dSAndrew Rybchenko 	__in		efsys_mem_t *esmp,
2695*09b59c7dSAndrew Rybchenko 	__in		boolean_t disable_scatter,
2696*09b59c7dSAndrew Rybchenko 	__in		boolean_t want_inner_classes,
2697*09b59c7dSAndrew Rybchenko 	__in		uint32_t buf_size,
2698*09b59c7dSAndrew Rybchenko 	__in		uint32_t ps_bufsize,
2699*09b59c7dSAndrew Rybchenko 	__in		uint32_t es_bufs_per_desc,
2700*09b59c7dSAndrew Rybchenko 	__in		uint32_t es_max_dma_len,
2701*09b59c7dSAndrew Rybchenko 	__in		uint32_t es_buf_stride,
2702*09b59c7dSAndrew Rybchenko 	__in		uint32_t hol_block_timeout)
2703*09b59c7dSAndrew Rybchenko {
2704*09b59c7dSAndrew Rybchenko 	efx_nic_cfg_t *encp = &(enp->en_nic_cfg);
2705*09b59c7dSAndrew Rybchenko 	efx_mcdi_req_t req;
2706*09b59c7dSAndrew Rybchenko 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_INIT_RXQ_V4_IN_LEN,
2707*09b59c7dSAndrew Rybchenko 		MC_CMD_INIT_RXQ_V4_OUT_LEN);
2708*09b59c7dSAndrew Rybchenko 	int npages = efx_rxq_nbufs(enp, ndescs);
2709*09b59c7dSAndrew Rybchenko 	int i;
2710*09b59c7dSAndrew Rybchenko 	efx_qword_t *dma_addr;
2711*09b59c7dSAndrew Rybchenko 	uint64_t addr;
2712*09b59c7dSAndrew Rybchenko 	efx_rc_t rc;
2713*09b59c7dSAndrew Rybchenko 	uint32_t dma_mode;
2714*09b59c7dSAndrew Rybchenko 	boolean_t want_outer_classes;
2715*09b59c7dSAndrew Rybchenko 	boolean_t no_cont_ev;
2716*09b59c7dSAndrew Rybchenko 
2717*09b59c7dSAndrew Rybchenko 	EFSYS_ASSERT3U(ndescs, <=, encp->enc_rxq_max_ndescs);
2718*09b59c7dSAndrew Rybchenko 
2719*09b59c7dSAndrew Rybchenko 	if ((esmp == NULL) ||
2720*09b59c7dSAndrew Rybchenko 	    (EFSYS_MEM_SIZE(esmp) < efx_rxq_size(enp, ndescs))) {
2721*09b59c7dSAndrew Rybchenko 		rc = EINVAL;
2722*09b59c7dSAndrew Rybchenko 		goto fail1;
2723*09b59c7dSAndrew Rybchenko 	}
2724*09b59c7dSAndrew Rybchenko 
2725*09b59c7dSAndrew Rybchenko 	no_cont_ev = (eep->ee_flags & EFX_EVQ_FLAGS_NO_CONT_EV);
2726*09b59c7dSAndrew Rybchenko 	if ((no_cont_ev == B_TRUE) && (disable_scatter == B_FALSE)) {
2727*09b59c7dSAndrew Rybchenko 		/* TODO: Support scatter in NO_CONT_EV mode */
2728*09b59c7dSAndrew Rybchenko 		rc = EINVAL;
2729*09b59c7dSAndrew Rybchenko 		goto fail2;
2730*09b59c7dSAndrew Rybchenko 	}
2731*09b59c7dSAndrew Rybchenko 
2732*09b59c7dSAndrew Rybchenko 	if (ps_bufsize > 0)
2733*09b59c7dSAndrew Rybchenko 		dma_mode = MC_CMD_INIT_RXQ_EXT_IN_PACKED_STREAM;
2734*09b59c7dSAndrew Rybchenko 	else if (es_bufs_per_desc > 0)
2735*09b59c7dSAndrew Rybchenko 		dma_mode = MC_CMD_INIT_RXQ_V3_IN_EQUAL_STRIDE_SUPER_BUFFER;
2736*09b59c7dSAndrew Rybchenko 	else
2737*09b59c7dSAndrew Rybchenko 		dma_mode = MC_CMD_INIT_RXQ_EXT_IN_SINGLE_PACKET;
2738*09b59c7dSAndrew Rybchenko 
2739*09b59c7dSAndrew Rybchenko 	if (encp->enc_tunnel_encapsulations_supported != 0 &&
2740*09b59c7dSAndrew Rybchenko 	    !want_inner_classes) {
2741*09b59c7dSAndrew Rybchenko 		/*
2742*09b59c7dSAndrew Rybchenko 		 * WANT_OUTER_CLASSES can only be specified on hardware which
2743*09b59c7dSAndrew Rybchenko 		 * supports tunnel encapsulation offloads, even though it is
2744*09b59c7dSAndrew Rybchenko 		 * effectively the behaviour the hardware gives.
2745*09b59c7dSAndrew Rybchenko 		 *
2746*09b59c7dSAndrew Rybchenko 		 * Also, on hardware which does support such offloads, older
2747*09b59c7dSAndrew Rybchenko 		 * firmware rejects the flag if the offloads are not supported
2748*09b59c7dSAndrew Rybchenko 		 * by the current firmware variant, which means this may fail if
2749*09b59c7dSAndrew Rybchenko 		 * the capabilities are not updated when the firmware variant
2750*09b59c7dSAndrew Rybchenko 		 * changes. This is not an issue on newer firmware, as it was
2751*09b59c7dSAndrew Rybchenko 		 * changed in bug 69842 (v6.4.2.1007) to permit this flag to be
2752*09b59c7dSAndrew Rybchenko 		 * specified on all firmware variants.
2753*09b59c7dSAndrew Rybchenko 		 */
2754*09b59c7dSAndrew Rybchenko 		want_outer_classes = B_TRUE;
2755*09b59c7dSAndrew Rybchenko 	} else {
2756*09b59c7dSAndrew Rybchenko 		want_outer_classes = B_FALSE;
2757*09b59c7dSAndrew Rybchenko 	}
2758*09b59c7dSAndrew Rybchenko 
2759*09b59c7dSAndrew Rybchenko 	req.emr_cmd = MC_CMD_INIT_RXQ;
2760*09b59c7dSAndrew Rybchenko 	req.emr_in_buf = payload;
2761*09b59c7dSAndrew Rybchenko 	req.emr_in_length = MC_CMD_INIT_RXQ_V4_IN_LEN;
2762*09b59c7dSAndrew Rybchenko 	req.emr_out_buf = payload;
2763*09b59c7dSAndrew Rybchenko 	req.emr_out_length = MC_CMD_INIT_RXQ_V4_OUT_LEN;
2764*09b59c7dSAndrew Rybchenko 
2765*09b59c7dSAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, INIT_RXQ_EXT_IN_SIZE, ndescs);
2766*09b59c7dSAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, INIT_RXQ_EXT_IN_TARGET_EVQ, eep->ee_index);
2767*09b59c7dSAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, INIT_RXQ_EXT_IN_LABEL, label);
2768*09b59c7dSAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, INIT_RXQ_EXT_IN_INSTANCE, instance);
2769*09b59c7dSAndrew Rybchenko 	MCDI_IN_POPULATE_DWORD_10(req, INIT_RXQ_EXT_IN_FLAGS,
2770*09b59c7dSAndrew Rybchenko 	    INIT_RXQ_EXT_IN_FLAG_BUFF_MODE, 0,
2771*09b59c7dSAndrew Rybchenko 	    INIT_RXQ_EXT_IN_FLAG_HDR_SPLIT, 0,
2772*09b59c7dSAndrew Rybchenko 	    INIT_RXQ_EXT_IN_FLAG_TIMESTAMP, 0,
2773*09b59c7dSAndrew Rybchenko 	    INIT_RXQ_EXT_IN_CRC_MODE, 0,
2774*09b59c7dSAndrew Rybchenko 	    INIT_RXQ_EXT_IN_FLAG_PREFIX, 1,
2775*09b59c7dSAndrew Rybchenko 	    INIT_RXQ_EXT_IN_FLAG_DISABLE_SCATTER, disable_scatter,
2776*09b59c7dSAndrew Rybchenko 	    INIT_RXQ_EXT_IN_DMA_MODE,
2777*09b59c7dSAndrew Rybchenko 	    dma_mode,
2778*09b59c7dSAndrew Rybchenko 	    INIT_RXQ_EXT_IN_PACKED_STREAM_BUFF_SIZE, ps_bufsize,
2779*09b59c7dSAndrew Rybchenko 	    INIT_RXQ_EXT_IN_FLAG_WANT_OUTER_CLASSES, want_outer_classes,
2780*09b59c7dSAndrew Rybchenko 	    INIT_RXQ_EXT_IN_FLAG_NO_CONT_EV, no_cont_ev);
2781*09b59c7dSAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, INIT_RXQ_EXT_IN_OWNER_ID, 0);
2782*09b59c7dSAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, INIT_RXQ_EXT_IN_PORT_ID, enp->en_vport_id);
2783*09b59c7dSAndrew Rybchenko 
2784*09b59c7dSAndrew Rybchenko 	if (es_bufs_per_desc > 0) {
2785*09b59c7dSAndrew Rybchenko 		MCDI_IN_SET_DWORD(req,
2786*09b59c7dSAndrew Rybchenko 		    INIT_RXQ_V3_IN_ES_PACKET_BUFFERS_PER_BUCKET,
2787*09b59c7dSAndrew Rybchenko 		    es_bufs_per_desc);
2788*09b59c7dSAndrew Rybchenko 		MCDI_IN_SET_DWORD(req,
2789*09b59c7dSAndrew Rybchenko 		    INIT_RXQ_V3_IN_ES_MAX_DMA_LEN, es_max_dma_len);
2790*09b59c7dSAndrew Rybchenko 		MCDI_IN_SET_DWORD(req,
2791*09b59c7dSAndrew Rybchenko 		    INIT_RXQ_V3_IN_ES_PACKET_STRIDE, es_buf_stride);
2792*09b59c7dSAndrew Rybchenko 		MCDI_IN_SET_DWORD(req,
2793*09b59c7dSAndrew Rybchenko 		    INIT_RXQ_V3_IN_ES_HEAD_OF_LINE_BLOCK_TIMEOUT,
2794*09b59c7dSAndrew Rybchenko 		    hol_block_timeout);
2795*09b59c7dSAndrew Rybchenko 	}
2796*09b59c7dSAndrew Rybchenko 
2797*09b59c7dSAndrew Rybchenko 	if (encp->enc_init_rxq_with_buffer_size)
2798*09b59c7dSAndrew Rybchenko 		MCDI_IN_SET_DWORD(req, INIT_RXQ_V4_IN_BUFFER_SIZE_BYTES,
2799*09b59c7dSAndrew Rybchenko 		    buf_size);
2800*09b59c7dSAndrew Rybchenko 
2801*09b59c7dSAndrew Rybchenko 	dma_addr = MCDI_IN2(req, efx_qword_t, INIT_RXQ_IN_DMA_ADDR);
2802*09b59c7dSAndrew Rybchenko 	addr = EFSYS_MEM_ADDR(esmp);
2803*09b59c7dSAndrew Rybchenko 
2804*09b59c7dSAndrew Rybchenko 	for (i = 0; i < npages; i++) {
2805*09b59c7dSAndrew Rybchenko 		EFX_POPULATE_QWORD_2(*dma_addr,
2806*09b59c7dSAndrew Rybchenko 		    EFX_DWORD_1, (uint32_t)(addr >> 32),
2807*09b59c7dSAndrew Rybchenko 		    EFX_DWORD_0, (uint32_t)(addr & 0xffffffff));
2808*09b59c7dSAndrew Rybchenko 
2809*09b59c7dSAndrew Rybchenko 		dma_addr++;
2810*09b59c7dSAndrew Rybchenko 		addr += EFX_BUF_SIZE;
2811*09b59c7dSAndrew Rybchenko 	}
2812*09b59c7dSAndrew Rybchenko 
2813*09b59c7dSAndrew Rybchenko 	efx_mcdi_execute(enp, &req);
2814*09b59c7dSAndrew Rybchenko 
2815*09b59c7dSAndrew Rybchenko 	if (req.emr_rc != 0) {
2816*09b59c7dSAndrew Rybchenko 		rc = req.emr_rc;
2817*09b59c7dSAndrew Rybchenko 		goto fail3;
2818*09b59c7dSAndrew Rybchenko 	}
2819*09b59c7dSAndrew Rybchenko 
2820*09b59c7dSAndrew Rybchenko 	return (0);
2821*09b59c7dSAndrew Rybchenko 
2822*09b59c7dSAndrew Rybchenko fail3:
2823*09b59c7dSAndrew Rybchenko 	EFSYS_PROBE(fail3);
2824*09b59c7dSAndrew Rybchenko fail2:
2825*09b59c7dSAndrew Rybchenko 	EFSYS_PROBE(fail2);
2826*09b59c7dSAndrew Rybchenko fail1:
2827*09b59c7dSAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2828*09b59c7dSAndrew Rybchenko 
2829*09b59c7dSAndrew Rybchenko 	return (rc);
2830*09b59c7dSAndrew Rybchenko }
2831*09b59c7dSAndrew Rybchenko 
2832*09b59c7dSAndrew Rybchenko 	__checkReturn	efx_rc_t
2833*09b59c7dSAndrew Rybchenko efx_mcdi_fini_rxq(
2834*09b59c7dSAndrew Rybchenko 	__in		efx_nic_t *enp,
2835*09b59c7dSAndrew Rybchenko 	__in		uint32_t instance)
2836*09b59c7dSAndrew Rybchenko {
2837*09b59c7dSAndrew Rybchenko 	efx_mcdi_req_t req;
2838*09b59c7dSAndrew Rybchenko 	EFX_MCDI_DECLARE_BUF(payload, MC_CMD_FINI_RXQ_IN_LEN,
2839*09b59c7dSAndrew Rybchenko 		MC_CMD_FINI_RXQ_OUT_LEN);
2840*09b59c7dSAndrew Rybchenko 	efx_rc_t rc;
2841*09b59c7dSAndrew Rybchenko 
2842*09b59c7dSAndrew Rybchenko 	req.emr_cmd = MC_CMD_FINI_RXQ;
2843*09b59c7dSAndrew Rybchenko 	req.emr_in_buf = payload;
2844*09b59c7dSAndrew Rybchenko 	req.emr_in_length = MC_CMD_FINI_RXQ_IN_LEN;
2845*09b59c7dSAndrew Rybchenko 	req.emr_out_buf = payload;
2846*09b59c7dSAndrew Rybchenko 	req.emr_out_length = MC_CMD_FINI_RXQ_OUT_LEN;
2847*09b59c7dSAndrew Rybchenko 
2848*09b59c7dSAndrew Rybchenko 	MCDI_IN_SET_DWORD(req, FINI_RXQ_IN_INSTANCE, instance);
2849*09b59c7dSAndrew Rybchenko 
2850*09b59c7dSAndrew Rybchenko 	efx_mcdi_execute_quiet(enp, &req);
2851*09b59c7dSAndrew Rybchenko 
2852*09b59c7dSAndrew Rybchenko 	if (req.emr_rc != 0) {
2853*09b59c7dSAndrew Rybchenko 		rc = req.emr_rc;
2854*09b59c7dSAndrew Rybchenko 		goto fail1;
2855*09b59c7dSAndrew Rybchenko 	}
2856*09b59c7dSAndrew Rybchenko 
2857*09b59c7dSAndrew Rybchenko 	return (0);
2858*09b59c7dSAndrew Rybchenko 
2859*09b59c7dSAndrew Rybchenko fail1:
2860*09b59c7dSAndrew Rybchenko 	/*
2861*09b59c7dSAndrew Rybchenko 	 * EALREADY is not an error, but indicates that the MC has rebooted and
2862*09b59c7dSAndrew Rybchenko 	 * that the RXQ has already been destroyed.
2863*09b59c7dSAndrew Rybchenko 	 */
2864*09b59c7dSAndrew Rybchenko 	if (rc != EALREADY)
2865*09b59c7dSAndrew Rybchenko 		EFSYS_PROBE1(fail1, efx_rc_t, rc);
2866*09b59c7dSAndrew Rybchenko 
2867*09b59c7dSAndrew Rybchenko 	return (rc);
2868*09b59c7dSAndrew Rybchenko }
2869*09b59c7dSAndrew Rybchenko 
2870*09b59c7dSAndrew Rybchenko #endif	/* EFX_OPTS_EF10() */
2871*09b59c7dSAndrew Rybchenko 
28725e111ed8SAndrew Rybchenko #endif	/* EFSYS_OPT_MCDI */
2873