xref: /dpdk/drivers/common/sfc_efx/base/siena_mcdi.c (revision 672386c1e9e1f64f7aa3b1360ad22dc737ea8d72)
15e111ed8SAndrew Rybchenko /* SPDX-License-Identifier: BSD-3-Clause
25e111ed8SAndrew Rybchenko  *
3*672386c1SAndrew Rybchenko  * Copyright(c) 2019-2021 Xilinx, Inc.
45e111ed8SAndrew Rybchenko  * Copyright(c) 2012-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_SIENA && EFSYS_OPT_MCDI
115e111ed8SAndrew Rybchenko 
125e111ed8SAndrew Rybchenko #define	SIENA_MCDI_PDU(_emip)			\
135e111ed8SAndrew Rybchenko 	(((emip)->emi_port == 1)		\
145e111ed8SAndrew Rybchenko 	? MC_SMEM_P0_PDU_OFST >> 2		\
155e111ed8SAndrew Rybchenko 	: MC_SMEM_P1_PDU_OFST >> 2)
165e111ed8SAndrew Rybchenko 
175e111ed8SAndrew Rybchenko #define	SIENA_MCDI_DOORBELL(_emip)		\
185e111ed8SAndrew Rybchenko 	(((emip)->emi_port == 1)		\
195e111ed8SAndrew Rybchenko 	? MC_SMEM_P0_DOORBELL_OFST >> 2		\
205e111ed8SAndrew Rybchenko 	: MC_SMEM_P1_DOORBELL_OFST >> 2)
215e111ed8SAndrew Rybchenko 
225e111ed8SAndrew Rybchenko #define	SIENA_MCDI_STATUS(_emip)		\
235e111ed8SAndrew Rybchenko 	(((emip)->emi_port == 1)		\
245e111ed8SAndrew Rybchenko 	? MC_SMEM_P0_STATUS_OFST >> 2		\
255e111ed8SAndrew Rybchenko 	: MC_SMEM_P1_STATUS_OFST >> 2)
265e111ed8SAndrew Rybchenko 
275e111ed8SAndrew Rybchenko 
285e111ed8SAndrew Rybchenko 			void
siena_mcdi_send_request(__in efx_nic_t * enp,__in_bcount (hdr_len)void * hdrp,__in size_t hdr_len,__in_bcount (sdu_len)void * sdup,__in size_t sdu_len)295e111ed8SAndrew Rybchenko siena_mcdi_send_request(
305e111ed8SAndrew Rybchenko 	__in			efx_nic_t *enp,
315e111ed8SAndrew Rybchenko 	__in_bcount(hdr_len)	void *hdrp,
325e111ed8SAndrew Rybchenko 	__in			size_t hdr_len,
335e111ed8SAndrew Rybchenko 	__in_bcount(sdu_len)	void *sdup,
345e111ed8SAndrew Rybchenko 	__in			size_t sdu_len)
355e111ed8SAndrew Rybchenko {
365e111ed8SAndrew Rybchenko 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
375e111ed8SAndrew Rybchenko 	efx_dword_t dword;
385e111ed8SAndrew Rybchenko 	unsigned int pdur;
395e111ed8SAndrew Rybchenko 	unsigned int dbr;
405e111ed8SAndrew Rybchenko 	unsigned int pos;
415e111ed8SAndrew Rybchenko 
425e111ed8SAndrew Rybchenko 	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
435e111ed8SAndrew Rybchenko 
445e111ed8SAndrew Rybchenko 	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
455e111ed8SAndrew Rybchenko 	pdur = SIENA_MCDI_PDU(emip);
465e111ed8SAndrew Rybchenko 	dbr = SIENA_MCDI_DOORBELL(emip);
475e111ed8SAndrew Rybchenko 
485e111ed8SAndrew Rybchenko 	/* Write the header */
495e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(hdr_len, ==, sizeof (efx_dword_t));
505e111ed8SAndrew Rybchenko 	dword = *(efx_dword_t *)hdrp;
515e111ed8SAndrew Rybchenko 	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_TRUE);
525e111ed8SAndrew Rybchenko 
535e111ed8SAndrew Rybchenko 	/* Write the payload */
545e111ed8SAndrew Rybchenko 	for (pos = 0; pos < sdu_len; pos += sizeof (efx_dword_t)) {
555e111ed8SAndrew Rybchenko 		dword = *(efx_dword_t *)((uint8_t *)sdup + pos);
565e111ed8SAndrew Rybchenko 		EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM,
575e111ed8SAndrew Rybchenko 		    pdur + 1 + (pos >> 2), &dword, B_FALSE);
585e111ed8SAndrew Rybchenko 	}
595e111ed8SAndrew Rybchenko 
605e111ed8SAndrew Rybchenko 	/* Ring the doorbell */
615e111ed8SAndrew Rybchenko 	EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 0xd004be11);
625e111ed8SAndrew Rybchenko 	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, dbr, &dword, B_FALSE);
635e111ed8SAndrew Rybchenko }
645e111ed8SAndrew Rybchenko 
655e111ed8SAndrew Rybchenko 			efx_rc_t
siena_mcdi_poll_reboot(__in efx_nic_t * enp)665e111ed8SAndrew Rybchenko siena_mcdi_poll_reboot(
675e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp)
685e111ed8SAndrew Rybchenko {
695e111ed8SAndrew Rybchenko #if 1
705e111ed8SAndrew Rybchenko 	/*
715e111ed8SAndrew Rybchenko 	 * XXX Bug 25922, bug 26099: This function is not being used
725e111ed8SAndrew Rybchenko 	 * properly.  Until its callers are fixed, it should always
735e111ed8SAndrew Rybchenko 	 * return 0.
745e111ed8SAndrew Rybchenko 	 */
755e111ed8SAndrew Rybchenko 	_NOTE(ARGUNUSED(enp))
765e111ed8SAndrew Rybchenko 	return (0);
775e111ed8SAndrew Rybchenko #else
785e111ed8SAndrew Rybchenko 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
795e111ed8SAndrew Rybchenko 	unsigned int rebootr;
805e111ed8SAndrew Rybchenko 	efx_dword_t dword;
815e111ed8SAndrew Rybchenko 	uint32_t value;
825e111ed8SAndrew Rybchenko 
835e111ed8SAndrew Rybchenko 	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
845e111ed8SAndrew Rybchenko 	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
855e111ed8SAndrew Rybchenko 	rebootr = SIENA_MCDI_STATUS(emip);
865e111ed8SAndrew Rybchenko 
875e111ed8SAndrew Rybchenko 	EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
885e111ed8SAndrew Rybchenko 	value = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
895e111ed8SAndrew Rybchenko 
905e111ed8SAndrew Rybchenko 	if (value == 0)
915e111ed8SAndrew Rybchenko 		return (0);
925e111ed8SAndrew Rybchenko 
935e111ed8SAndrew Rybchenko 	EFX_ZERO_DWORD(dword);
945e111ed8SAndrew Rybchenko 	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
955e111ed8SAndrew Rybchenko 
965e111ed8SAndrew Rybchenko 	if (value == MC_STATUS_DWORD_ASSERT)
975e111ed8SAndrew Rybchenko 		return (EINTR);
985e111ed8SAndrew Rybchenko 	else
995e111ed8SAndrew Rybchenko 		return (EIO);
1005e111ed8SAndrew Rybchenko #endif
1015e111ed8SAndrew Rybchenko }
1025e111ed8SAndrew Rybchenko 
1035e111ed8SAndrew Rybchenko extern	__checkReturn	boolean_t
siena_mcdi_poll_response(__in efx_nic_t * enp)1045e111ed8SAndrew Rybchenko siena_mcdi_poll_response(
1055e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp)
1065e111ed8SAndrew Rybchenko {
1075e111ed8SAndrew Rybchenko 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1085e111ed8SAndrew Rybchenko 	efx_dword_t hdr;
1095e111ed8SAndrew Rybchenko 	unsigned int pdur;
1105e111ed8SAndrew Rybchenko 
1115e111ed8SAndrew Rybchenko 	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
1125e111ed8SAndrew Rybchenko 	pdur = SIENA_MCDI_PDU(emip);
1135e111ed8SAndrew Rybchenko 
1145e111ed8SAndrew Rybchenko 	EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, pdur, &hdr, B_FALSE);
1155e111ed8SAndrew Rybchenko 	return (EFX_DWORD_FIELD(hdr, MCDI_HEADER_RESPONSE) ? B_TRUE : B_FALSE);
1165e111ed8SAndrew Rybchenko }
1175e111ed8SAndrew Rybchenko 
1185e111ed8SAndrew Rybchenko 			void
siena_mcdi_read_response(__in efx_nic_t * enp,__out_bcount (length)void * bufferp,__in size_t offset,__in size_t length)1195e111ed8SAndrew Rybchenko siena_mcdi_read_response(
1205e111ed8SAndrew Rybchenko 	__in			efx_nic_t *enp,
1215e111ed8SAndrew Rybchenko 	__out_bcount(length)	void *bufferp,
1225e111ed8SAndrew Rybchenko 	__in			size_t offset,
1235e111ed8SAndrew Rybchenko 	__in			size_t length)
1245e111ed8SAndrew Rybchenko {
1255e111ed8SAndrew Rybchenko 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1265e111ed8SAndrew Rybchenko 	unsigned int pdur;
1275e111ed8SAndrew Rybchenko 	unsigned int pos = 0;
1285e111ed8SAndrew Rybchenko 	efx_dword_t data;
1295e111ed8SAndrew Rybchenko 	size_t remaining = length;
1305e111ed8SAndrew Rybchenko 
1315e111ed8SAndrew Rybchenko 	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
1325e111ed8SAndrew Rybchenko 	pdur = SIENA_MCDI_PDU(emip);
1335e111ed8SAndrew Rybchenko 
1345e111ed8SAndrew Rybchenko 	while (remaining > 0) {
1355e111ed8SAndrew Rybchenko 		size_t chunk = MIN(remaining, sizeof (data));
1365e111ed8SAndrew Rybchenko 
1375e111ed8SAndrew Rybchenko 		EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM,
1385e111ed8SAndrew Rybchenko 		    pdur + ((offset + pos) >> 2), &data, B_FALSE);
1395e111ed8SAndrew Rybchenko 		memcpy((uint8_t *)bufferp + pos, &data, chunk);
1405e111ed8SAndrew Rybchenko 		pos += chunk;
1415e111ed8SAndrew Rybchenko 		remaining -= chunk;
1425e111ed8SAndrew Rybchenko 	}
1435e111ed8SAndrew Rybchenko }
1445e111ed8SAndrew Rybchenko 
1455e111ed8SAndrew Rybchenko 	__checkReturn	efx_rc_t
siena_mcdi_init(__in efx_nic_t * enp,__in const efx_mcdi_transport_t * mtp)1465e111ed8SAndrew Rybchenko siena_mcdi_init(
1475e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
1485e111ed8SAndrew Rybchenko 	__in		const efx_mcdi_transport_t *mtp)
1495e111ed8SAndrew Rybchenko {
1505e111ed8SAndrew Rybchenko 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1515e111ed8SAndrew Rybchenko 	efx_oword_t oword;
1525e111ed8SAndrew Rybchenko 	unsigned int portnum;
1535e111ed8SAndrew Rybchenko 	efx_rc_t rc;
1545e111ed8SAndrew Rybchenko 
1555e111ed8SAndrew Rybchenko 	_NOTE(ARGUNUSED(mtp))
1565e111ed8SAndrew Rybchenko 
1575e111ed8SAndrew Rybchenko 	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
1585e111ed8SAndrew Rybchenko 
1595e111ed8SAndrew Rybchenko 	/* Determine the port number to use for MCDI */
1605e111ed8SAndrew Rybchenko 	EFX_BAR_READO(enp, FR_AZ_CS_DEBUG_REG, &oword);
1615e111ed8SAndrew Rybchenko 	portnum = EFX_OWORD_FIELD(oword, FRF_CZ_CS_PORT_NUM);
1625e111ed8SAndrew Rybchenko 
1635e111ed8SAndrew Rybchenko 	if (portnum == 0) {
1645e111ed8SAndrew Rybchenko 		/* Presumably booted from ROM; only MCDI port 1 will work */
1655e111ed8SAndrew Rybchenko 		emip->emi_port = 1;
1665e111ed8SAndrew Rybchenko 	} else if (portnum <= 2) {
1675e111ed8SAndrew Rybchenko 		emip->emi_port = portnum;
1685e111ed8SAndrew Rybchenko 	} else {
1695e111ed8SAndrew Rybchenko 		rc = EINVAL;
1705e111ed8SAndrew Rybchenko 		goto fail1;
1715e111ed8SAndrew Rybchenko 	}
1725e111ed8SAndrew Rybchenko 
1735e111ed8SAndrew Rybchenko 	/* Siena BootROM and firmware only support MCDIv1 */
1745e111ed8SAndrew Rybchenko 	emip->emi_max_version = 1;
1755e111ed8SAndrew Rybchenko 
1765e111ed8SAndrew Rybchenko 	/*
1775e111ed8SAndrew Rybchenko 	 * Wipe the atomic reboot status so subsequent MCDI requests succeed.
1785e111ed8SAndrew Rybchenko 	 * BOOT_STATUS is preserved so eno_nic_probe() can boot out of the
1795e111ed8SAndrew Rybchenko 	 * assertion handler.
1805e111ed8SAndrew Rybchenko 	 */
1815e111ed8SAndrew Rybchenko 	(void) siena_mcdi_poll_reboot(enp);
1825e111ed8SAndrew Rybchenko 
1835e111ed8SAndrew Rybchenko 	return (0);
1845e111ed8SAndrew Rybchenko 
1855e111ed8SAndrew Rybchenko fail1:
1865e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1875e111ed8SAndrew Rybchenko 
1885e111ed8SAndrew Rybchenko 	return (rc);
1895e111ed8SAndrew Rybchenko }
1905e111ed8SAndrew Rybchenko 
1915e111ed8SAndrew Rybchenko 			void
siena_mcdi_fini(__in efx_nic_t * enp)1925e111ed8SAndrew Rybchenko siena_mcdi_fini(
1935e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp)
1945e111ed8SAndrew Rybchenko {
1955e111ed8SAndrew Rybchenko 	_NOTE(ARGUNUSED(enp))
1965e111ed8SAndrew Rybchenko }
1975e111ed8SAndrew Rybchenko 
1985e111ed8SAndrew Rybchenko 	__checkReturn	efx_rc_t
siena_mcdi_feature_supported(__in efx_nic_t * enp,__in efx_mcdi_feature_id_t id,__out boolean_t * supportedp)1995e111ed8SAndrew Rybchenko siena_mcdi_feature_supported(
2005e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
2015e111ed8SAndrew Rybchenko 	__in		efx_mcdi_feature_id_t id,
2025e111ed8SAndrew Rybchenko 	__out		boolean_t *supportedp)
2035e111ed8SAndrew Rybchenko {
2045e111ed8SAndrew Rybchenko 	efx_rc_t rc;
2055e111ed8SAndrew Rybchenko 
2065e111ed8SAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
2075e111ed8SAndrew Rybchenko 
2085e111ed8SAndrew Rybchenko 	switch (id) {
2095e111ed8SAndrew Rybchenko 	case EFX_MCDI_FEATURE_FW_UPDATE:
2105e111ed8SAndrew Rybchenko 	case EFX_MCDI_FEATURE_LINK_CONTROL:
2115e111ed8SAndrew Rybchenko 	case EFX_MCDI_FEATURE_MACADDR_CHANGE:
2125e111ed8SAndrew Rybchenko 	case EFX_MCDI_FEATURE_MAC_SPOOFING:
2135e111ed8SAndrew Rybchenko 		*supportedp = B_TRUE;
2145e111ed8SAndrew Rybchenko 		break;
2155e111ed8SAndrew Rybchenko 	default:
2165e111ed8SAndrew Rybchenko 		rc = ENOTSUP;
2175e111ed8SAndrew Rybchenko 		goto fail1;
2185e111ed8SAndrew Rybchenko 	}
2195e111ed8SAndrew Rybchenko 
2205e111ed8SAndrew Rybchenko 	return (0);
2215e111ed8SAndrew Rybchenko 
2225e111ed8SAndrew Rybchenko fail1:
2235e111ed8SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2245e111ed8SAndrew Rybchenko 
2255e111ed8SAndrew Rybchenko 	return (rc);
2265e111ed8SAndrew Rybchenko }
2275e111ed8SAndrew Rybchenko 
2285e111ed8SAndrew Rybchenko /* Default timeout for MCDI command processing. */
2295e111ed8SAndrew Rybchenko #define	SIENA_MCDI_CMD_TIMEOUT_US	(10 * 1000 * 1000)
2305e111ed8SAndrew Rybchenko 
2315e111ed8SAndrew Rybchenko 			void
siena_mcdi_get_timeout(__in efx_nic_t * enp,__in efx_mcdi_req_t * emrp,__out uint32_t * timeoutp)2325e111ed8SAndrew Rybchenko siena_mcdi_get_timeout(
2335e111ed8SAndrew Rybchenko 	__in		efx_nic_t *enp,
2345e111ed8SAndrew Rybchenko 	__in		efx_mcdi_req_t *emrp,
2355e111ed8SAndrew Rybchenko 	__out		uint32_t *timeoutp)
2365e111ed8SAndrew Rybchenko {
2375e111ed8SAndrew Rybchenko 	_NOTE(ARGUNUSED(enp, emrp))
2385e111ed8SAndrew Rybchenko 
2395e111ed8SAndrew Rybchenko 	*timeoutp = SIENA_MCDI_CMD_TIMEOUT_US;
2405e111ed8SAndrew Rybchenko }
2415e111ed8SAndrew Rybchenko 
2425e111ed8SAndrew Rybchenko 
2435e111ed8SAndrew Rybchenko #endif	/* EFSYS_OPT_SIENA && EFSYS_OPT_MCDI */
244