xref: /freebsd/sys/dev/sfxge/common/siena_mcdi.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
13c838a9fSAndrew Rybchenko /*-
2929c7febSAndrew Rybchenko  * Copyright (c) 2012-2016 Solarflare Communications Inc.
33c838a9fSAndrew Rybchenko  * All rights reserved.
43c838a9fSAndrew Rybchenko  *
53c838a9fSAndrew Rybchenko  * Redistribution and use in source and binary forms, with or without
63c838a9fSAndrew Rybchenko  * modification, are permitted provided that the following conditions are met:
73c838a9fSAndrew Rybchenko  *
83c838a9fSAndrew Rybchenko  * 1. Redistributions of source code must retain the above copyright notice,
93c838a9fSAndrew Rybchenko  *    this list of conditions and the following disclaimer.
103c838a9fSAndrew Rybchenko  * 2. Redistributions in binary form must reproduce the above copyright notice,
113c838a9fSAndrew Rybchenko  *    this list of conditions and the following disclaimer in the documentation
123c838a9fSAndrew Rybchenko  *    and/or other materials provided with the distribution.
133c838a9fSAndrew Rybchenko  *
143c838a9fSAndrew Rybchenko  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
153c838a9fSAndrew Rybchenko  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
163c838a9fSAndrew Rybchenko  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
173c838a9fSAndrew Rybchenko  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
183c838a9fSAndrew Rybchenko  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
193c838a9fSAndrew Rybchenko  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
203c838a9fSAndrew Rybchenko  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
213c838a9fSAndrew Rybchenko  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
223c838a9fSAndrew Rybchenko  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
233c838a9fSAndrew Rybchenko  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
243c838a9fSAndrew Rybchenko  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
253c838a9fSAndrew Rybchenko  *
263c838a9fSAndrew Rybchenko  * The views and conclusions contained in the software and documentation are
273c838a9fSAndrew Rybchenko  * those of the authors and should not be interpreted as representing official
283c838a9fSAndrew Rybchenko  * policies, either expressed or implied, of the FreeBSD Project.
293c838a9fSAndrew Rybchenko  */
303c838a9fSAndrew Rybchenko 
313c838a9fSAndrew Rybchenko #include <sys/cdefs.h>
323c838a9fSAndrew Rybchenko #include "efx.h"
333c838a9fSAndrew Rybchenko #include "efx_impl.h"
343c838a9fSAndrew Rybchenko 
353c838a9fSAndrew Rybchenko #if EFSYS_OPT_SIENA && EFSYS_OPT_MCDI
363c838a9fSAndrew Rybchenko 
373c838a9fSAndrew Rybchenko #define	SIENA_MCDI_PDU(_emip)			\
383c838a9fSAndrew Rybchenko 	(((emip)->emi_port == 1)		\
393c838a9fSAndrew Rybchenko 	? MC_SMEM_P0_PDU_OFST >> 2		\
403c838a9fSAndrew Rybchenko 	: MC_SMEM_P1_PDU_OFST >> 2)
413c838a9fSAndrew Rybchenko 
423c838a9fSAndrew Rybchenko #define	SIENA_MCDI_DOORBELL(_emip)		\
433c838a9fSAndrew Rybchenko 	(((emip)->emi_port == 1)		\
443c838a9fSAndrew Rybchenko 	? MC_SMEM_P0_DOORBELL_OFST >> 2		\
453c838a9fSAndrew Rybchenko 	: MC_SMEM_P1_DOORBELL_OFST >> 2)
463c838a9fSAndrew Rybchenko 
473c838a9fSAndrew Rybchenko #define	SIENA_MCDI_STATUS(_emip)		\
483c838a9fSAndrew Rybchenko 	(((emip)->emi_port == 1)		\
493c838a9fSAndrew Rybchenko 	? MC_SMEM_P0_STATUS_OFST >> 2		\
503c838a9fSAndrew Rybchenko 	: MC_SMEM_P1_STATUS_OFST >> 2)
513c838a9fSAndrew Rybchenko 
52fd7501bfSAndrew 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)530a687580SAndrew Rybchenko siena_mcdi_send_request(
540a687580SAndrew Rybchenko 	__in			efx_nic_t *enp,
553222b9deSAndrew Rybchenko 	__in_bcount(hdr_len)	void *hdrp,
560a687580SAndrew Rybchenko 	__in			size_t hdr_len,
573222b9deSAndrew Rybchenko 	__in_bcount(sdu_len)	void *sdup,
580a687580SAndrew Rybchenko 	__in			size_t sdu_len)
590a687580SAndrew Rybchenko {
600a687580SAndrew Rybchenko 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
610a687580SAndrew Rybchenko 	efx_dword_t dword;
620a687580SAndrew Rybchenko 	unsigned int pdur;
630a687580SAndrew Rybchenko 	unsigned int dbr;
640a687580SAndrew Rybchenko 	unsigned int pos;
650a687580SAndrew Rybchenko 
660a687580SAndrew Rybchenko 	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
670a687580SAndrew Rybchenko 
680a687580SAndrew Rybchenko 	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
690a687580SAndrew Rybchenko 	pdur = SIENA_MCDI_PDU(emip);
700a687580SAndrew Rybchenko 	dbr = SIENA_MCDI_DOORBELL(emip);
710a687580SAndrew Rybchenko 
720a687580SAndrew Rybchenko 	/* Write the header */
730a687580SAndrew Rybchenko 	EFSYS_ASSERT3U(hdr_len, ==, sizeof (efx_dword_t));
740a687580SAndrew Rybchenko 	dword = *(efx_dword_t *)hdrp;
750a687580SAndrew Rybchenko 	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_TRUE);
760a687580SAndrew Rybchenko 
770a687580SAndrew Rybchenko 	/* Write the payload */
780a687580SAndrew Rybchenko 	for (pos = 0; pos < sdu_len; pos += sizeof (efx_dword_t)) {
790a687580SAndrew Rybchenko 		dword = *(efx_dword_t *)((uint8_t *)sdup + pos);
800a687580SAndrew Rybchenko 		EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM,
810a687580SAndrew Rybchenko 		    pdur + 1 + (pos >> 2), &dword, B_FALSE);
820a687580SAndrew Rybchenko 	}
830a687580SAndrew Rybchenko 
840a687580SAndrew Rybchenko 	/* Ring the doorbell */
850a687580SAndrew Rybchenko 	EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 0xd004be11);
860a687580SAndrew Rybchenko 	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, dbr, &dword, B_FALSE);
870a687580SAndrew Rybchenko }
880a687580SAndrew Rybchenko 
89460cb568SAndrew Rybchenko 			efx_rc_t
siena_mcdi_poll_reboot(__in efx_nic_t * enp)903c838a9fSAndrew Rybchenko siena_mcdi_poll_reboot(
913c838a9fSAndrew Rybchenko 	__in		efx_nic_t *enp)
923c838a9fSAndrew Rybchenko {
933c838a9fSAndrew Rybchenko #ifndef EFX_GRACEFUL_MC_REBOOT
943c838a9fSAndrew Rybchenko  	/*
953c838a9fSAndrew Rybchenko 	 * This function is not being used properly.
963c838a9fSAndrew Rybchenko 	 * Until its callers are fixed, it should always return 0.
973c838a9fSAndrew Rybchenko 	 */
983c838a9fSAndrew Rybchenko 	_NOTE(ARGUNUSED(enp))
993c838a9fSAndrew Rybchenko 	return (0);
1003c838a9fSAndrew Rybchenko #else
1013c838a9fSAndrew Rybchenko 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1023c838a9fSAndrew Rybchenko 	unsigned int rebootr;
1033c838a9fSAndrew Rybchenko 	efx_dword_t dword;
1043c838a9fSAndrew Rybchenko 	uint32_t value;
1053c838a9fSAndrew Rybchenko 
1063c838a9fSAndrew Rybchenko 	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
1073c838a9fSAndrew Rybchenko 	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
1083c838a9fSAndrew Rybchenko 	rebootr = SIENA_MCDI_STATUS(emip);
1093c838a9fSAndrew Rybchenko 
1103c838a9fSAndrew Rybchenko 	EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
1113c838a9fSAndrew Rybchenko 	value = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
1123c838a9fSAndrew Rybchenko 
1133c838a9fSAndrew Rybchenko 	if (value == 0)
1143c838a9fSAndrew Rybchenko 		return (0);
1153c838a9fSAndrew Rybchenko 
1163c838a9fSAndrew Rybchenko 	EFX_ZERO_DWORD(dword);
1173c838a9fSAndrew Rybchenko 	EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
1183c838a9fSAndrew Rybchenko 
1193c838a9fSAndrew Rybchenko 	if (value == MC_STATUS_DWORD_ASSERT)
1203c838a9fSAndrew Rybchenko 		return (EINTR);
1213c838a9fSAndrew Rybchenko 	else
1223c838a9fSAndrew Rybchenko 		return (EIO);
1233c838a9fSAndrew Rybchenko #endif
1243c838a9fSAndrew Rybchenko }
1253c838a9fSAndrew Rybchenko 
126548ebee5SAndrew Rybchenko extern	__checkReturn	boolean_t
siena_mcdi_poll_response(__in efx_nic_t * enp)12706af9686SAndrew Rybchenko siena_mcdi_poll_response(
12806af9686SAndrew Rybchenko 	__in		efx_nic_t *enp)
12906af9686SAndrew Rybchenko {
13006af9686SAndrew Rybchenko 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
13106af9686SAndrew Rybchenko 	efx_dword_t hdr;
13206af9686SAndrew Rybchenko 	unsigned int pdur;
13306af9686SAndrew Rybchenko 
13406af9686SAndrew Rybchenko 	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
13506af9686SAndrew Rybchenko 	pdur = SIENA_MCDI_PDU(emip);
13606af9686SAndrew Rybchenko 
13706af9686SAndrew Rybchenko 	EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, pdur, &hdr, B_FALSE);
13806af9686SAndrew Rybchenko 	return (EFX_DWORD_FIELD(hdr, MCDI_HEADER_RESPONSE) ? B_TRUE : B_FALSE);
13906af9686SAndrew Rybchenko }
14006af9686SAndrew Rybchenko 
14159bfa22aSAndrew Rybchenko 			void
siena_mcdi_read_response(__in efx_nic_t * enp,__out_bcount (length)void * bufferp,__in size_t offset,__in size_t length)14259bfa22aSAndrew Rybchenko siena_mcdi_read_response(
14359bfa22aSAndrew Rybchenko 	__in			efx_nic_t *enp,
14486ec4b85SAndrew Rybchenko 	__out_bcount(length)	void *bufferp,
14559bfa22aSAndrew Rybchenko 	__in			size_t offset,
14659bfa22aSAndrew Rybchenko 	__in			size_t length)
14759bfa22aSAndrew Rybchenko {
14859bfa22aSAndrew Rybchenko 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
14959bfa22aSAndrew Rybchenko 	unsigned int pdur;
150*b20c54ffSAndrew Rybchenko 	unsigned int pos = 0;
15159bfa22aSAndrew Rybchenko 	efx_dword_t data;
152*b20c54ffSAndrew Rybchenko 	size_t remaining = length;
15359bfa22aSAndrew Rybchenko 
15459bfa22aSAndrew Rybchenko 	EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
15559bfa22aSAndrew Rybchenko 	pdur = SIENA_MCDI_PDU(emip);
15659bfa22aSAndrew Rybchenko 
157*b20c54ffSAndrew Rybchenko 	while (remaining > 0) {
158*b20c54ffSAndrew Rybchenko 		size_t chunk = MIN(remaining, sizeof (data));
159*b20c54ffSAndrew Rybchenko 
16059bfa22aSAndrew Rybchenko 		EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM,
16159bfa22aSAndrew Rybchenko 		    pdur + ((offset + pos) >> 2), &data, B_FALSE);
162*b20c54ffSAndrew Rybchenko 		memcpy((uint8_t *)bufferp + pos, &data, chunk);
163*b20c54ffSAndrew Rybchenko 		pos += chunk;
164*b20c54ffSAndrew Rybchenko 		remaining -= chunk;
16559bfa22aSAndrew Rybchenko 	}
16659bfa22aSAndrew Rybchenko }
16759bfa22aSAndrew Rybchenko 
168460cb568SAndrew Rybchenko 	__checkReturn	efx_rc_t
siena_mcdi_init(__in efx_nic_t * enp,__in const efx_mcdi_transport_t * mtp)1693c838a9fSAndrew Rybchenko siena_mcdi_init(
1703c838a9fSAndrew Rybchenko 	__in		efx_nic_t *enp,
1713c838a9fSAndrew Rybchenko 	__in		const efx_mcdi_transport_t *mtp)
1723c838a9fSAndrew Rybchenko {
1733c838a9fSAndrew Rybchenko 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1743c838a9fSAndrew Rybchenko 	efx_oword_t oword;
1753c838a9fSAndrew Rybchenko 	unsigned int portnum;
176460cb568SAndrew Rybchenko 	efx_rc_t rc;
1773c838a9fSAndrew Rybchenko 
178a92a2133SAndrew Rybchenko 	_NOTE(ARGUNUSED(mtp))
179a92a2133SAndrew Rybchenko 
1803c838a9fSAndrew Rybchenko 	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
1813c838a9fSAndrew Rybchenko 
1823c838a9fSAndrew Rybchenko 	/* Determine the port number to use for MCDI */
1833c838a9fSAndrew Rybchenko 	EFX_BAR_READO(enp, FR_AZ_CS_DEBUG_REG, &oword);
1843c838a9fSAndrew Rybchenko 	portnum = EFX_OWORD_FIELD(oword, FRF_CZ_CS_PORT_NUM);
1853c838a9fSAndrew Rybchenko 
1863c838a9fSAndrew Rybchenko 	if (portnum == 0) {
1873c838a9fSAndrew Rybchenko 		/* Presumably booted from ROM; only MCDI port 1 will work */
1883c838a9fSAndrew Rybchenko 		emip->emi_port = 1;
1893c838a9fSAndrew Rybchenko 	} else if (portnum <= 2) {
1903c838a9fSAndrew Rybchenko 		emip->emi_port = portnum;
1913c838a9fSAndrew Rybchenko 	} else {
1923c838a9fSAndrew Rybchenko 		rc = EINVAL;
1933c838a9fSAndrew Rybchenko 		goto fail1;
1943c838a9fSAndrew Rybchenko 	}
1953c838a9fSAndrew Rybchenko 
19614d6f73eSAndrew Rybchenko 	/* Siena BootROM and firmware only support MCDIv1 */
19714d6f73eSAndrew Rybchenko 	emip->emi_max_version = 1;
19814d6f73eSAndrew Rybchenko 
1993c838a9fSAndrew Rybchenko 	/*
2003c838a9fSAndrew Rybchenko 	 * Wipe the atomic reboot status so subsequent MCDI requests succeed.
2013c838a9fSAndrew Rybchenko 	 * BOOT_STATUS is preserved so eno_nic_probe() can boot out of the
2023c838a9fSAndrew Rybchenko 	 * assertion handler.
2033c838a9fSAndrew Rybchenko 	 */
2043c838a9fSAndrew Rybchenko 	(void) siena_mcdi_poll_reboot(enp);
2053c838a9fSAndrew Rybchenko 
2063c838a9fSAndrew Rybchenko 	return (0);
2073c838a9fSAndrew Rybchenko 
2083c838a9fSAndrew Rybchenko fail1:
209460cb568SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2103c838a9fSAndrew Rybchenko 
2113c838a9fSAndrew Rybchenko 	return (rc);
2123c838a9fSAndrew Rybchenko }
2133c838a9fSAndrew Rybchenko 
2143c838a9fSAndrew Rybchenko 			void
siena_mcdi_fini(__in efx_nic_t * enp)2153c838a9fSAndrew Rybchenko siena_mcdi_fini(
2163c838a9fSAndrew Rybchenko 	__in		efx_nic_t *enp)
2173c838a9fSAndrew Rybchenko {
218a92a2133SAndrew Rybchenko 	_NOTE(ARGUNUSED(enp))
2193c838a9fSAndrew Rybchenko }
2203c838a9fSAndrew Rybchenko 
221460cb568SAndrew 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)222af986c75SAndrew Rybchenko siena_mcdi_feature_supported(
2233c838a9fSAndrew Rybchenko 	__in		efx_nic_t *enp,
224af986c75SAndrew Rybchenko 	__in		efx_mcdi_feature_id_t id,
2253c838a9fSAndrew Rybchenko 	__out		boolean_t *supportedp)
2263c838a9fSAndrew Rybchenko {
227af986c75SAndrew Rybchenko 	efx_rc_t rc;
228af986c75SAndrew Rybchenko 
2293c838a9fSAndrew Rybchenko 	EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
2303c838a9fSAndrew Rybchenko 
231af986c75SAndrew Rybchenko 	switch (id) {
232af986c75SAndrew Rybchenko 	case EFX_MCDI_FEATURE_FW_UPDATE:
233af986c75SAndrew Rybchenko 	case EFX_MCDI_FEATURE_LINK_CONTROL:
234af986c75SAndrew Rybchenko 	case EFX_MCDI_FEATURE_MACADDR_CHANGE:
235af986c75SAndrew Rybchenko 	case EFX_MCDI_FEATURE_MAC_SPOOFING:
2363c838a9fSAndrew Rybchenko 		*supportedp = B_TRUE;
237af986c75SAndrew Rybchenko 		break;
238af986c75SAndrew Rybchenko 	default:
239af986c75SAndrew Rybchenko 		rc = ENOTSUP;
240af986c75SAndrew Rybchenko 		goto fail1;
2413c838a9fSAndrew Rybchenko 	}
2423c838a9fSAndrew Rybchenko 
2433c838a9fSAndrew Rybchenko 	return (0);
2443c838a9fSAndrew Rybchenko 
245af986c75SAndrew Rybchenko fail1:
246af986c75SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
247d486ce4bSAndrew Rybchenko 
248af986c75SAndrew Rybchenko 	return (rc);
249d486ce4bSAndrew Rybchenko }
250d486ce4bSAndrew Rybchenko 
2518a4fcbd4SAndrew Rybchenko /* Default timeout for MCDI command processing. */
2528a4fcbd4SAndrew Rybchenko #define	SIENA_MCDI_CMD_TIMEOUT_US	(10 * 1000 * 1000)
2538a4fcbd4SAndrew Rybchenko 
2548a4fcbd4SAndrew Rybchenko 			void
siena_mcdi_get_timeout(__in efx_nic_t * enp,__in efx_mcdi_req_t * emrp,__out uint32_t * timeoutp)2558a4fcbd4SAndrew Rybchenko siena_mcdi_get_timeout(
2568a4fcbd4SAndrew Rybchenko 	__in		efx_nic_t *enp,
2578a4fcbd4SAndrew Rybchenko 	__in		efx_mcdi_req_t *emrp,
2588a4fcbd4SAndrew Rybchenko 	__out		uint32_t *timeoutp)
2598a4fcbd4SAndrew Rybchenko {
2608a4fcbd4SAndrew Rybchenko 	_NOTE(ARGUNUSED(enp, emrp))
2618a4fcbd4SAndrew Rybchenko 
2628a4fcbd4SAndrew Rybchenko 	*timeoutp = SIENA_MCDI_CMD_TIMEOUT_US;
2638a4fcbd4SAndrew Rybchenko }
2648a4fcbd4SAndrew Rybchenko 
2653c838a9fSAndrew Rybchenko #endif	/* EFSYS_OPT_SIENA && EFSYS_OPT_MCDI */
266