18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/****************************************************************************
38c2ecf20Sopenharmony_ci * Driver for Solarflare network controllers and boards
48c2ecf20Sopenharmony_ci * Copyright 2008-2013 Solarflare Communications Inc.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/delay.h>
88c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
98c2ecf20Sopenharmony_ci#include <linux/atomic.h>
108c2ecf20Sopenharmony_ci#include "net_driver.h"
118c2ecf20Sopenharmony_ci#include "nic.h"
128c2ecf20Sopenharmony_ci#include "io.h"
138c2ecf20Sopenharmony_ci#include "farch_regs.h"
148c2ecf20Sopenharmony_ci#include "mcdi_pcol.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/**************************************************************************
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * Management-Controller-to-Driver Interface
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci **************************************************************************
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define MCDI_RPC_TIMEOUT       (10 * HZ)
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* A reboot/assertion causes the MCDI status word to be set after the
268c2ecf20Sopenharmony_ci * command word is set or a REBOOT event is sent. If we notice a reboot
278c2ecf20Sopenharmony_ci * via these mechanisms then wait 250ms for the status word to be set.
288c2ecf20Sopenharmony_ci */
298c2ecf20Sopenharmony_ci#define MCDI_STATUS_DELAY_US		100
308c2ecf20Sopenharmony_ci#define MCDI_STATUS_DELAY_COUNT		2500
318c2ecf20Sopenharmony_ci#define MCDI_STATUS_SLEEP_MS						\
328c2ecf20Sopenharmony_ci	(MCDI_STATUS_DELAY_US * MCDI_STATUS_DELAY_COUNT / 1000)
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define SEQ_MASK							\
358c2ecf20Sopenharmony_ci	EFX_MASK32(EFX_WIDTH(MCDI_HEADER_SEQ))
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistruct efx_mcdi_async_param {
388c2ecf20Sopenharmony_ci	struct list_head list;
398c2ecf20Sopenharmony_ci	unsigned int cmd;
408c2ecf20Sopenharmony_ci	size_t inlen;
418c2ecf20Sopenharmony_ci	size_t outlen;
428c2ecf20Sopenharmony_ci	bool quiet;
438c2ecf20Sopenharmony_ci	efx_mcdi_async_completer *complete;
448c2ecf20Sopenharmony_ci	unsigned long cookie;
458c2ecf20Sopenharmony_ci	/* followed by request/response buffer */
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic void efx_mcdi_timeout_async(struct timer_list *t);
498c2ecf20Sopenharmony_cistatic int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating,
508c2ecf20Sopenharmony_ci			       bool *was_attached_out);
518c2ecf20Sopenharmony_cistatic bool efx_mcdi_poll_once(struct efx_nic *efx);
528c2ecf20Sopenharmony_cistatic void efx_mcdi_abandon(struct efx_nic *efx);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#ifdef CONFIG_SFC_MCDI_LOGGING
558c2ecf20Sopenharmony_cistatic bool mcdi_logging_default;
568c2ecf20Sopenharmony_cimodule_param(mcdi_logging_default, bool, 0644);
578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mcdi_logging_default,
588c2ecf20Sopenharmony_ci		 "Enable MCDI logging on newly-probed functions");
598c2ecf20Sopenharmony_ci#endif
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ciint efx_mcdi_init(struct efx_nic *efx)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct efx_mcdi_iface *mcdi;
648c2ecf20Sopenharmony_ci	bool already_attached;
658c2ecf20Sopenharmony_ci	int rc = -ENOMEM;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	efx->mcdi = kzalloc(sizeof(*efx->mcdi), GFP_KERNEL);
688c2ecf20Sopenharmony_ci	if (!efx->mcdi)
698c2ecf20Sopenharmony_ci		goto fail;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	mcdi = efx_mcdi(efx);
728c2ecf20Sopenharmony_ci	mcdi->efx = efx;
738c2ecf20Sopenharmony_ci#ifdef CONFIG_SFC_MCDI_LOGGING
748c2ecf20Sopenharmony_ci	/* consuming code assumes buffer is page-sized */
758c2ecf20Sopenharmony_ci	mcdi->logging_buffer = (char *)__get_free_page(GFP_KERNEL);
768c2ecf20Sopenharmony_ci	if (!mcdi->logging_buffer)
778c2ecf20Sopenharmony_ci		goto fail1;
788c2ecf20Sopenharmony_ci	mcdi->logging_enabled = mcdi_logging_default;
798c2ecf20Sopenharmony_ci#endif
808c2ecf20Sopenharmony_ci	init_waitqueue_head(&mcdi->wq);
818c2ecf20Sopenharmony_ci	init_waitqueue_head(&mcdi->proxy_rx_wq);
828c2ecf20Sopenharmony_ci	spin_lock_init(&mcdi->iface_lock);
838c2ecf20Sopenharmony_ci	mcdi->state = MCDI_STATE_QUIESCENT;
848c2ecf20Sopenharmony_ci	mcdi->mode = MCDI_MODE_POLL;
858c2ecf20Sopenharmony_ci	spin_lock_init(&mcdi->async_lock);
868c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mcdi->async_list);
878c2ecf20Sopenharmony_ci	timer_setup(&mcdi->async_timer, efx_mcdi_timeout_async, 0);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	(void) efx_mcdi_poll_reboot(efx);
908c2ecf20Sopenharmony_ci	mcdi->new_epoch = true;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	/* Recover from a failed assertion before probing */
938c2ecf20Sopenharmony_ci	rc = efx_mcdi_handle_assertion(efx);
948c2ecf20Sopenharmony_ci	if (rc)
958c2ecf20Sopenharmony_ci		goto fail2;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	/* Let the MC (and BMC, if this is a LOM) know that the driver
988c2ecf20Sopenharmony_ci	 * is loaded. We should do this before we reset the NIC.
998c2ecf20Sopenharmony_ci	 */
1008c2ecf20Sopenharmony_ci	rc = efx_mcdi_drv_attach(efx, true, &already_attached);
1018c2ecf20Sopenharmony_ci	if (rc) {
1028c2ecf20Sopenharmony_ci		netif_err(efx, probe, efx->net_dev,
1038c2ecf20Sopenharmony_ci			  "Unable to register driver with MCPU\n");
1048c2ecf20Sopenharmony_ci		goto fail2;
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci	if (already_attached)
1078c2ecf20Sopenharmony_ci		/* Not a fatal error */
1088c2ecf20Sopenharmony_ci		netif_err(efx, probe, efx->net_dev,
1098c2ecf20Sopenharmony_ci			  "Host already registered with MCPU\n");
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (efx->mcdi->fn_flags &
1128c2ecf20Sopenharmony_ci	    (1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_PRIMARY))
1138c2ecf20Sopenharmony_ci		efx->primary = efx;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return 0;
1168c2ecf20Sopenharmony_cifail2:
1178c2ecf20Sopenharmony_ci#ifdef CONFIG_SFC_MCDI_LOGGING
1188c2ecf20Sopenharmony_ci	free_page((unsigned long)mcdi->logging_buffer);
1198c2ecf20Sopenharmony_cifail1:
1208c2ecf20Sopenharmony_ci#endif
1218c2ecf20Sopenharmony_ci	kfree(efx->mcdi);
1228c2ecf20Sopenharmony_ci	efx->mcdi = NULL;
1238c2ecf20Sopenharmony_cifail:
1248c2ecf20Sopenharmony_ci	return rc;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_civoid efx_mcdi_detach(struct efx_nic *efx)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	if (!efx->mcdi)
1308c2ecf20Sopenharmony_ci		return;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	BUG_ON(efx->mcdi->iface.state != MCDI_STATE_QUIESCENT);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	/* Relinquish the device (back to the BMC, if this is a LOM) */
1358c2ecf20Sopenharmony_ci	efx_mcdi_drv_attach(efx, false, NULL);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_civoid efx_mcdi_fini(struct efx_nic *efx)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	if (!efx->mcdi)
1418c2ecf20Sopenharmony_ci		return;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci#ifdef CONFIG_SFC_MCDI_LOGGING
1448c2ecf20Sopenharmony_ci	free_page((unsigned long)efx->mcdi->iface.logging_buffer);
1458c2ecf20Sopenharmony_ci#endif
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	kfree(efx->mcdi);
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic void efx_mcdi_send_request(struct efx_nic *efx, unsigned cmd,
1518c2ecf20Sopenharmony_ci				  const efx_dword_t *inbuf, size_t inlen)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
1548c2ecf20Sopenharmony_ci#ifdef CONFIG_SFC_MCDI_LOGGING
1558c2ecf20Sopenharmony_ci	char *buf = mcdi->logging_buffer; /* page-sized */
1568c2ecf20Sopenharmony_ci#endif
1578c2ecf20Sopenharmony_ci	efx_dword_t hdr[2];
1588c2ecf20Sopenharmony_ci	size_t hdr_len;
1598c2ecf20Sopenharmony_ci	u32 xflags, seqno;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	BUG_ON(mcdi->state == MCDI_STATE_QUIESCENT);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	/* Serialise with efx_mcdi_ev_cpl() and efx_mcdi_ev_death() */
1648c2ecf20Sopenharmony_ci	spin_lock_bh(&mcdi->iface_lock);
1658c2ecf20Sopenharmony_ci	++mcdi->seqno;
1668c2ecf20Sopenharmony_ci	seqno = mcdi->seqno & SEQ_MASK;
1678c2ecf20Sopenharmony_ci	spin_unlock_bh(&mcdi->iface_lock);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	xflags = 0;
1708c2ecf20Sopenharmony_ci	if (mcdi->mode == MCDI_MODE_EVENTS)
1718c2ecf20Sopenharmony_ci		xflags |= MCDI_HEADER_XFLAGS_EVREQ;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	if (efx->type->mcdi_max_ver == 1) {
1748c2ecf20Sopenharmony_ci		/* MCDI v1 */
1758c2ecf20Sopenharmony_ci		EFX_POPULATE_DWORD_7(hdr[0],
1768c2ecf20Sopenharmony_ci				     MCDI_HEADER_RESPONSE, 0,
1778c2ecf20Sopenharmony_ci				     MCDI_HEADER_RESYNC, 1,
1788c2ecf20Sopenharmony_ci				     MCDI_HEADER_CODE, cmd,
1798c2ecf20Sopenharmony_ci				     MCDI_HEADER_DATALEN, inlen,
1808c2ecf20Sopenharmony_ci				     MCDI_HEADER_SEQ, seqno,
1818c2ecf20Sopenharmony_ci				     MCDI_HEADER_XFLAGS, xflags,
1828c2ecf20Sopenharmony_ci				     MCDI_HEADER_NOT_EPOCH, !mcdi->new_epoch);
1838c2ecf20Sopenharmony_ci		hdr_len = 4;
1848c2ecf20Sopenharmony_ci	} else {
1858c2ecf20Sopenharmony_ci		/* MCDI v2 */
1868c2ecf20Sopenharmony_ci		BUG_ON(inlen > MCDI_CTL_SDU_LEN_MAX_V2);
1878c2ecf20Sopenharmony_ci		EFX_POPULATE_DWORD_7(hdr[0],
1888c2ecf20Sopenharmony_ci				     MCDI_HEADER_RESPONSE, 0,
1898c2ecf20Sopenharmony_ci				     MCDI_HEADER_RESYNC, 1,
1908c2ecf20Sopenharmony_ci				     MCDI_HEADER_CODE, MC_CMD_V2_EXTN,
1918c2ecf20Sopenharmony_ci				     MCDI_HEADER_DATALEN, 0,
1928c2ecf20Sopenharmony_ci				     MCDI_HEADER_SEQ, seqno,
1938c2ecf20Sopenharmony_ci				     MCDI_HEADER_XFLAGS, xflags,
1948c2ecf20Sopenharmony_ci				     MCDI_HEADER_NOT_EPOCH, !mcdi->new_epoch);
1958c2ecf20Sopenharmony_ci		EFX_POPULATE_DWORD_2(hdr[1],
1968c2ecf20Sopenharmony_ci				     MC_CMD_V2_EXTN_IN_EXTENDED_CMD, cmd,
1978c2ecf20Sopenharmony_ci				     MC_CMD_V2_EXTN_IN_ACTUAL_LEN, inlen);
1988c2ecf20Sopenharmony_ci		hdr_len = 8;
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci#ifdef CONFIG_SFC_MCDI_LOGGING
2028c2ecf20Sopenharmony_ci	if (mcdi->logging_enabled && !WARN_ON_ONCE(!buf)) {
2038c2ecf20Sopenharmony_ci		int bytes = 0;
2048c2ecf20Sopenharmony_ci		int i;
2058c2ecf20Sopenharmony_ci		/* Lengths should always be a whole number of dwords, so scream
2068c2ecf20Sopenharmony_ci		 * if they're not.
2078c2ecf20Sopenharmony_ci		 */
2088c2ecf20Sopenharmony_ci		WARN_ON_ONCE(hdr_len % 4);
2098c2ecf20Sopenharmony_ci		WARN_ON_ONCE(inlen % 4);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci		/* We own the logging buffer, as only one MCDI can be in
2128c2ecf20Sopenharmony_ci		 * progress on a NIC at any one time.  So no need for locking.
2138c2ecf20Sopenharmony_ci		 */
2148c2ecf20Sopenharmony_ci		for (i = 0; i < hdr_len / 4 && bytes < PAGE_SIZE; i++)
2158c2ecf20Sopenharmony_ci			bytes += scnprintf(buf + bytes, PAGE_SIZE - bytes,
2168c2ecf20Sopenharmony_ci					   " %08x",
2178c2ecf20Sopenharmony_ci					   le32_to_cpu(hdr[i].u32[0]));
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci		for (i = 0; i < inlen / 4 && bytes < PAGE_SIZE; i++)
2208c2ecf20Sopenharmony_ci			bytes += scnprintf(buf + bytes, PAGE_SIZE - bytes,
2218c2ecf20Sopenharmony_ci					   " %08x",
2228c2ecf20Sopenharmony_ci					   le32_to_cpu(inbuf[i].u32[0]));
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci		netif_info(efx, hw, efx->net_dev, "MCDI RPC REQ:%s\n", buf);
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci#endif
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	efx->type->mcdi_request(efx, hdr, hdr_len, inbuf, inlen);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	mcdi->new_epoch = false;
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic int efx_mcdi_errno(unsigned int mcdi_err)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	switch (mcdi_err) {
2368c2ecf20Sopenharmony_ci	case 0:
2378c2ecf20Sopenharmony_ci		return 0;
2388c2ecf20Sopenharmony_ci#define TRANSLATE_ERROR(name)					\
2398c2ecf20Sopenharmony_ci	case MC_CMD_ERR_ ## name:				\
2408c2ecf20Sopenharmony_ci		return -name;
2418c2ecf20Sopenharmony_ci	TRANSLATE_ERROR(EPERM);
2428c2ecf20Sopenharmony_ci	TRANSLATE_ERROR(ENOENT);
2438c2ecf20Sopenharmony_ci	TRANSLATE_ERROR(EINTR);
2448c2ecf20Sopenharmony_ci	TRANSLATE_ERROR(EAGAIN);
2458c2ecf20Sopenharmony_ci	TRANSLATE_ERROR(EACCES);
2468c2ecf20Sopenharmony_ci	TRANSLATE_ERROR(EBUSY);
2478c2ecf20Sopenharmony_ci	TRANSLATE_ERROR(EINVAL);
2488c2ecf20Sopenharmony_ci	TRANSLATE_ERROR(EDEADLK);
2498c2ecf20Sopenharmony_ci	TRANSLATE_ERROR(ENOSYS);
2508c2ecf20Sopenharmony_ci	TRANSLATE_ERROR(ETIME);
2518c2ecf20Sopenharmony_ci	TRANSLATE_ERROR(EALREADY);
2528c2ecf20Sopenharmony_ci	TRANSLATE_ERROR(ENOSPC);
2538c2ecf20Sopenharmony_ci#undef TRANSLATE_ERROR
2548c2ecf20Sopenharmony_ci	case MC_CMD_ERR_ENOTSUP:
2558c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2568c2ecf20Sopenharmony_ci	case MC_CMD_ERR_ALLOC_FAIL:
2578c2ecf20Sopenharmony_ci		return -ENOBUFS;
2588c2ecf20Sopenharmony_ci	case MC_CMD_ERR_MAC_EXIST:
2598c2ecf20Sopenharmony_ci		return -EADDRINUSE;
2608c2ecf20Sopenharmony_ci	default:
2618c2ecf20Sopenharmony_ci		return -EPROTO;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic void efx_mcdi_read_response_header(struct efx_nic *efx)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
2688c2ecf20Sopenharmony_ci	unsigned int respseq, respcmd, error;
2698c2ecf20Sopenharmony_ci#ifdef CONFIG_SFC_MCDI_LOGGING
2708c2ecf20Sopenharmony_ci	char *buf = mcdi->logging_buffer; /* page-sized */
2718c2ecf20Sopenharmony_ci#endif
2728c2ecf20Sopenharmony_ci	efx_dword_t hdr;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	efx->type->mcdi_read_response(efx, &hdr, 0, 4);
2758c2ecf20Sopenharmony_ci	respseq = EFX_DWORD_FIELD(hdr, MCDI_HEADER_SEQ);
2768c2ecf20Sopenharmony_ci	respcmd = EFX_DWORD_FIELD(hdr, MCDI_HEADER_CODE);
2778c2ecf20Sopenharmony_ci	error = EFX_DWORD_FIELD(hdr, MCDI_HEADER_ERROR);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	if (respcmd != MC_CMD_V2_EXTN) {
2808c2ecf20Sopenharmony_ci		mcdi->resp_hdr_len = 4;
2818c2ecf20Sopenharmony_ci		mcdi->resp_data_len = EFX_DWORD_FIELD(hdr, MCDI_HEADER_DATALEN);
2828c2ecf20Sopenharmony_ci	} else {
2838c2ecf20Sopenharmony_ci		efx->type->mcdi_read_response(efx, &hdr, 4, 4);
2848c2ecf20Sopenharmony_ci		mcdi->resp_hdr_len = 8;
2858c2ecf20Sopenharmony_ci		mcdi->resp_data_len =
2868c2ecf20Sopenharmony_ci			EFX_DWORD_FIELD(hdr, MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci#ifdef CONFIG_SFC_MCDI_LOGGING
2908c2ecf20Sopenharmony_ci	if (mcdi->logging_enabled && !WARN_ON_ONCE(!buf)) {
2918c2ecf20Sopenharmony_ci		size_t hdr_len, data_len;
2928c2ecf20Sopenharmony_ci		int bytes = 0;
2938c2ecf20Sopenharmony_ci		int i;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci		WARN_ON_ONCE(mcdi->resp_hdr_len % 4);
2968c2ecf20Sopenharmony_ci		hdr_len = mcdi->resp_hdr_len / 4;
2978c2ecf20Sopenharmony_ci		/* MCDI_DECLARE_BUF ensures that underlying buffer is padded
2988c2ecf20Sopenharmony_ci		 * to dword size, and the MCDI buffer is always dword size
2998c2ecf20Sopenharmony_ci		 */
3008c2ecf20Sopenharmony_ci		data_len = DIV_ROUND_UP(mcdi->resp_data_len, 4);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci		/* We own the logging buffer, as only one MCDI can be in
3038c2ecf20Sopenharmony_ci		 * progress on a NIC at any one time.  So no need for locking.
3048c2ecf20Sopenharmony_ci		 */
3058c2ecf20Sopenharmony_ci		for (i = 0; i < hdr_len && bytes < PAGE_SIZE; i++) {
3068c2ecf20Sopenharmony_ci			efx->type->mcdi_read_response(efx, &hdr, (i * 4), 4);
3078c2ecf20Sopenharmony_ci			bytes += scnprintf(buf + bytes, PAGE_SIZE - bytes,
3088c2ecf20Sopenharmony_ci					   " %08x", le32_to_cpu(hdr.u32[0]));
3098c2ecf20Sopenharmony_ci		}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci		for (i = 0; i < data_len && bytes < PAGE_SIZE; i++) {
3128c2ecf20Sopenharmony_ci			efx->type->mcdi_read_response(efx, &hdr,
3138c2ecf20Sopenharmony_ci					mcdi->resp_hdr_len + (i * 4), 4);
3148c2ecf20Sopenharmony_ci			bytes += scnprintf(buf + bytes, PAGE_SIZE - bytes,
3158c2ecf20Sopenharmony_ci					   " %08x", le32_to_cpu(hdr.u32[0]));
3168c2ecf20Sopenharmony_ci		}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci		netif_info(efx, hw, efx->net_dev, "MCDI RPC RESP:%s\n", buf);
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci#endif
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	mcdi->resprc_raw = 0;
3238c2ecf20Sopenharmony_ci	if (error && mcdi->resp_data_len == 0) {
3248c2ecf20Sopenharmony_ci		netif_err(efx, hw, efx->net_dev, "MC rebooted\n");
3258c2ecf20Sopenharmony_ci		mcdi->resprc = -EIO;
3268c2ecf20Sopenharmony_ci	} else if ((respseq ^ mcdi->seqno) & SEQ_MASK) {
3278c2ecf20Sopenharmony_ci		netif_err(efx, hw, efx->net_dev,
3288c2ecf20Sopenharmony_ci			  "MC response mismatch tx seq 0x%x rx seq 0x%x\n",
3298c2ecf20Sopenharmony_ci			  respseq, mcdi->seqno);
3308c2ecf20Sopenharmony_ci		mcdi->resprc = -EIO;
3318c2ecf20Sopenharmony_ci	} else if (error) {
3328c2ecf20Sopenharmony_ci		efx->type->mcdi_read_response(efx, &hdr, mcdi->resp_hdr_len, 4);
3338c2ecf20Sopenharmony_ci		mcdi->resprc_raw = EFX_DWORD_FIELD(hdr, EFX_DWORD_0);
3348c2ecf20Sopenharmony_ci		mcdi->resprc = efx_mcdi_errno(mcdi->resprc_raw);
3358c2ecf20Sopenharmony_ci	} else {
3368c2ecf20Sopenharmony_ci		mcdi->resprc = 0;
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic bool efx_mcdi_poll_once(struct efx_nic *efx)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	rmb();
3458c2ecf20Sopenharmony_ci	if (!efx->type->mcdi_poll_response(efx))
3468c2ecf20Sopenharmony_ci		return false;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	spin_lock_bh(&mcdi->iface_lock);
3498c2ecf20Sopenharmony_ci	efx_mcdi_read_response_header(efx);
3508c2ecf20Sopenharmony_ci	spin_unlock_bh(&mcdi->iface_lock);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	return true;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic int efx_mcdi_poll(struct efx_nic *efx)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
3588c2ecf20Sopenharmony_ci	unsigned long time, finish;
3598c2ecf20Sopenharmony_ci	unsigned int spins;
3608c2ecf20Sopenharmony_ci	int rc;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	/* Check for a reboot atomically with respect to efx_mcdi_copyout() */
3638c2ecf20Sopenharmony_ci	rc = efx_mcdi_poll_reboot(efx);
3648c2ecf20Sopenharmony_ci	if (rc) {
3658c2ecf20Sopenharmony_ci		spin_lock_bh(&mcdi->iface_lock);
3668c2ecf20Sopenharmony_ci		mcdi->resprc = rc;
3678c2ecf20Sopenharmony_ci		mcdi->resp_hdr_len = 0;
3688c2ecf20Sopenharmony_ci		mcdi->resp_data_len = 0;
3698c2ecf20Sopenharmony_ci		spin_unlock_bh(&mcdi->iface_lock);
3708c2ecf20Sopenharmony_ci		return 0;
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	/* Poll for completion. Poll quickly (once a us) for the 1st jiffy,
3748c2ecf20Sopenharmony_ci	 * because generally mcdi responses are fast. After that, back off
3758c2ecf20Sopenharmony_ci	 * and poll once a jiffy (approximately)
3768c2ecf20Sopenharmony_ci	 */
3778c2ecf20Sopenharmony_ci	spins = USER_TICK_USEC;
3788c2ecf20Sopenharmony_ci	finish = jiffies + MCDI_RPC_TIMEOUT;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	while (1) {
3818c2ecf20Sopenharmony_ci		if (spins != 0) {
3828c2ecf20Sopenharmony_ci			--spins;
3838c2ecf20Sopenharmony_ci			udelay(1);
3848c2ecf20Sopenharmony_ci		} else {
3858c2ecf20Sopenharmony_ci			schedule_timeout_uninterruptible(1);
3868c2ecf20Sopenharmony_ci		}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci		time = jiffies;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci		if (efx_mcdi_poll_once(efx))
3918c2ecf20Sopenharmony_ci			break;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci		if (time_after(time, finish))
3948c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	/* Return rc=0 like wait_event_timeout() */
3988c2ecf20Sopenharmony_ci	return 0;
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci/* Test and clear MC-rebooted flag for this port/function; reset
4028c2ecf20Sopenharmony_ci * software state as necessary.
4038c2ecf20Sopenharmony_ci */
4048c2ecf20Sopenharmony_ciint efx_mcdi_poll_reboot(struct efx_nic *efx)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	if (!efx->mcdi)
4078c2ecf20Sopenharmony_ci		return 0;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	return efx->type->mcdi_poll_reboot(efx);
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic bool efx_mcdi_acquire_async(struct efx_mcdi_iface *mcdi)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	return cmpxchg(&mcdi->state,
4158c2ecf20Sopenharmony_ci		       MCDI_STATE_QUIESCENT, MCDI_STATE_RUNNING_ASYNC) ==
4168c2ecf20Sopenharmony_ci		MCDI_STATE_QUIESCENT;
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cistatic void efx_mcdi_acquire_sync(struct efx_mcdi_iface *mcdi)
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	/* Wait until the interface becomes QUIESCENT and we win the race
4228c2ecf20Sopenharmony_ci	 * to mark it RUNNING_SYNC.
4238c2ecf20Sopenharmony_ci	 */
4248c2ecf20Sopenharmony_ci	wait_event(mcdi->wq,
4258c2ecf20Sopenharmony_ci		   cmpxchg(&mcdi->state,
4268c2ecf20Sopenharmony_ci			   MCDI_STATE_QUIESCENT, MCDI_STATE_RUNNING_SYNC) ==
4278c2ecf20Sopenharmony_ci		   MCDI_STATE_QUIESCENT);
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic int efx_mcdi_await_completion(struct efx_nic *efx)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	if (wait_event_timeout(mcdi->wq, mcdi->state == MCDI_STATE_COMPLETED,
4358c2ecf20Sopenharmony_ci			       MCDI_RPC_TIMEOUT) == 0)
4368c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	/* Check if efx_mcdi_set_mode() switched us back to polled completions.
4398c2ecf20Sopenharmony_ci	 * In which case, poll for completions directly. If efx_mcdi_ev_cpl()
4408c2ecf20Sopenharmony_ci	 * completed the request first, then we'll just end up completing the
4418c2ecf20Sopenharmony_ci	 * request again, which is safe.
4428c2ecf20Sopenharmony_ci	 *
4438c2ecf20Sopenharmony_ci	 * We need an smp_rmb() to synchronise with efx_mcdi_mode_poll(), which
4448c2ecf20Sopenharmony_ci	 * wait_event_timeout() implicitly provides.
4458c2ecf20Sopenharmony_ci	 */
4468c2ecf20Sopenharmony_ci	if (mcdi->mode == MCDI_MODE_POLL)
4478c2ecf20Sopenharmony_ci		return efx_mcdi_poll(efx);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	return 0;
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci/* If the interface is RUNNING_SYNC, switch to COMPLETED and wake the
4538c2ecf20Sopenharmony_ci * requester.  Return whether this was done.  Does not take any locks.
4548c2ecf20Sopenharmony_ci */
4558c2ecf20Sopenharmony_cistatic bool efx_mcdi_complete_sync(struct efx_mcdi_iface *mcdi)
4568c2ecf20Sopenharmony_ci{
4578c2ecf20Sopenharmony_ci	if (cmpxchg(&mcdi->state,
4588c2ecf20Sopenharmony_ci		    MCDI_STATE_RUNNING_SYNC, MCDI_STATE_COMPLETED) ==
4598c2ecf20Sopenharmony_ci	    MCDI_STATE_RUNNING_SYNC) {
4608c2ecf20Sopenharmony_ci		wake_up(&mcdi->wq);
4618c2ecf20Sopenharmony_ci		return true;
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	return false;
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_cistatic void efx_mcdi_release(struct efx_mcdi_iface *mcdi)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	if (mcdi->mode == MCDI_MODE_EVENTS) {
4708c2ecf20Sopenharmony_ci		struct efx_mcdi_async_param *async;
4718c2ecf20Sopenharmony_ci		struct efx_nic *efx = mcdi->efx;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci		/* Process the asynchronous request queue */
4748c2ecf20Sopenharmony_ci		spin_lock_bh(&mcdi->async_lock);
4758c2ecf20Sopenharmony_ci		async = list_first_entry_or_null(
4768c2ecf20Sopenharmony_ci			&mcdi->async_list, struct efx_mcdi_async_param, list);
4778c2ecf20Sopenharmony_ci		if (async) {
4788c2ecf20Sopenharmony_ci			mcdi->state = MCDI_STATE_RUNNING_ASYNC;
4798c2ecf20Sopenharmony_ci			efx_mcdi_send_request(efx, async->cmd,
4808c2ecf20Sopenharmony_ci					      (const efx_dword_t *)(async + 1),
4818c2ecf20Sopenharmony_ci					      async->inlen);
4828c2ecf20Sopenharmony_ci			mod_timer(&mcdi->async_timer,
4838c2ecf20Sopenharmony_ci				  jiffies + MCDI_RPC_TIMEOUT);
4848c2ecf20Sopenharmony_ci		}
4858c2ecf20Sopenharmony_ci		spin_unlock_bh(&mcdi->async_lock);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci		if (async)
4888c2ecf20Sopenharmony_ci			return;
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	mcdi->state = MCDI_STATE_QUIESCENT;
4928c2ecf20Sopenharmony_ci	wake_up(&mcdi->wq);
4938c2ecf20Sopenharmony_ci}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci/* If the interface is RUNNING_ASYNC, switch to COMPLETED, call the
4968c2ecf20Sopenharmony_ci * asynchronous completion function, and release the interface.
4978c2ecf20Sopenharmony_ci * Return whether this was done.  Must be called in bh-disabled
4988c2ecf20Sopenharmony_ci * context.  Will take iface_lock and async_lock.
4998c2ecf20Sopenharmony_ci */
5008c2ecf20Sopenharmony_cistatic bool efx_mcdi_complete_async(struct efx_mcdi_iface *mcdi, bool timeout)
5018c2ecf20Sopenharmony_ci{
5028c2ecf20Sopenharmony_ci	struct efx_nic *efx = mcdi->efx;
5038c2ecf20Sopenharmony_ci	struct efx_mcdi_async_param *async;
5048c2ecf20Sopenharmony_ci	size_t hdr_len, data_len, err_len;
5058c2ecf20Sopenharmony_ci	efx_dword_t *outbuf;
5068c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF_ERR(errbuf);
5078c2ecf20Sopenharmony_ci	int rc;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	if (cmpxchg(&mcdi->state,
5108c2ecf20Sopenharmony_ci		    MCDI_STATE_RUNNING_ASYNC, MCDI_STATE_COMPLETED) !=
5118c2ecf20Sopenharmony_ci	    MCDI_STATE_RUNNING_ASYNC)
5128c2ecf20Sopenharmony_ci		return false;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	spin_lock(&mcdi->iface_lock);
5158c2ecf20Sopenharmony_ci	if (timeout) {
5168c2ecf20Sopenharmony_ci		/* Ensure that if the completion event arrives later,
5178c2ecf20Sopenharmony_ci		 * the seqno check in efx_mcdi_ev_cpl() will fail
5188c2ecf20Sopenharmony_ci		 */
5198c2ecf20Sopenharmony_ci		++mcdi->seqno;
5208c2ecf20Sopenharmony_ci		++mcdi->credits;
5218c2ecf20Sopenharmony_ci		rc = -ETIMEDOUT;
5228c2ecf20Sopenharmony_ci		hdr_len = 0;
5238c2ecf20Sopenharmony_ci		data_len = 0;
5248c2ecf20Sopenharmony_ci	} else {
5258c2ecf20Sopenharmony_ci		rc = mcdi->resprc;
5268c2ecf20Sopenharmony_ci		hdr_len = mcdi->resp_hdr_len;
5278c2ecf20Sopenharmony_ci		data_len = mcdi->resp_data_len;
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci	spin_unlock(&mcdi->iface_lock);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	/* Stop the timer.  In case the timer function is running, we
5328c2ecf20Sopenharmony_ci	 * must wait for it to return so that there is no possibility
5338c2ecf20Sopenharmony_ci	 * of it aborting the next request.
5348c2ecf20Sopenharmony_ci	 */
5358c2ecf20Sopenharmony_ci	if (!timeout)
5368c2ecf20Sopenharmony_ci		del_timer_sync(&mcdi->async_timer);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	spin_lock(&mcdi->async_lock);
5398c2ecf20Sopenharmony_ci	async = list_first_entry(&mcdi->async_list,
5408c2ecf20Sopenharmony_ci				 struct efx_mcdi_async_param, list);
5418c2ecf20Sopenharmony_ci	list_del(&async->list);
5428c2ecf20Sopenharmony_ci	spin_unlock(&mcdi->async_lock);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	outbuf = (efx_dword_t *)(async + 1);
5458c2ecf20Sopenharmony_ci	efx->type->mcdi_read_response(efx, outbuf, hdr_len,
5468c2ecf20Sopenharmony_ci				      min(async->outlen, data_len));
5478c2ecf20Sopenharmony_ci	if (!timeout && rc && !async->quiet) {
5488c2ecf20Sopenharmony_ci		err_len = min(sizeof(errbuf), data_len);
5498c2ecf20Sopenharmony_ci		efx->type->mcdi_read_response(efx, errbuf, hdr_len,
5508c2ecf20Sopenharmony_ci					      sizeof(errbuf));
5518c2ecf20Sopenharmony_ci		efx_mcdi_display_error(efx, async->cmd, async->inlen, errbuf,
5528c2ecf20Sopenharmony_ci				       err_len, rc);
5538c2ecf20Sopenharmony_ci	}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	if (async->complete)
5568c2ecf20Sopenharmony_ci		async->complete(efx, async->cookie, rc, outbuf,
5578c2ecf20Sopenharmony_ci				min(async->outlen, data_len));
5588c2ecf20Sopenharmony_ci	kfree(async);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	efx_mcdi_release(mcdi);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	return true;
5638c2ecf20Sopenharmony_ci}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_cistatic void efx_mcdi_ev_cpl(struct efx_nic *efx, unsigned int seqno,
5668c2ecf20Sopenharmony_ci			    unsigned int datalen, unsigned int mcdi_err)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
5698c2ecf20Sopenharmony_ci	bool wake = false;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	spin_lock(&mcdi->iface_lock);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	if ((seqno ^ mcdi->seqno) & SEQ_MASK) {
5748c2ecf20Sopenharmony_ci		if (mcdi->credits)
5758c2ecf20Sopenharmony_ci			/* The request has been cancelled */
5768c2ecf20Sopenharmony_ci			--mcdi->credits;
5778c2ecf20Sopenharmony_ci		else
5788c2ecf20Sopenharmony_ci			netif_err(efx, hw, efx->net_dev,
5798c2ecf20Sopenharmony_ci				  "MC response mismatch tx seq 0x%x rx "
5808c2ecf20Sopenharmony_ci				  "seq 0x%x\n", seqno, mcdi->seqno);
5818c2ecf20Sopenharmony_ci	} else {
5828c2ecf20Sopenharmony_ci		if (efx->type->mcdi_max_ver >= 2) {
5838c2ecf20Sopenharmony_ci			/* MCDI v2 responses don't fit in an event */
5848c2ecf20Sopenharmony_ci			efx_mcdi_read_response_header(efx);
5858c2ecf20Sopenharmony_ci		} else {
5868c2ecf20Sopenharmony_ci			mcdi->resprc = efx_mcdi_errno(mcdi_err);
5878c2ecf20Sopenharmony_ci			mcdi->resp_hdr_len = 4;
5888c2ecf20Sopenharmony_ci			mcdi->resp_data_len = datalen;
5898c2ecf20Sopenharmony_ci		}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci		wake = true;
5928c2ecf20Sopenharmony_ci	}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	spin_unlock(&mcdi->iface_lock);
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	if (wake) {
5978c2ecf20Sopenharmony_ci		if (!efx_mcdi_complete_async(mcdi, false))
5988c2ecf20Sopenharmony_ci			(void) efx_mcdi_complete_sync(mcdi);
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci		/* If the interface isn't RUNNING_ASYNC or
6018c2ecf20Sopenharmony_ci		 * RUNNING_SYNC then we've received a duplicate
6028c2ecf20Sopenharmony_ci		 * completion after we've already transitioned back to
6038c2ecf20Sopenharmony_ci		 * QUIESCENT. [A subsequent invocation would increment
6048c2ecf20Sopenharmony_ci		 * seqno, so would have failed the seqno check].
6058c2ecf20Sopenharmony_ci		 */
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_cistatic void efx_mcdi_timeout_async(struct timer_list *t)
6108c2ecf20Sopenharmony_ci{
6118c2ecf20Sopenharmony_ci	struct efx_mcdi_iface *mcdi = from_timer(mcdi, t, async_timer);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	efx_mcdi_complete_async(mcdi, true);
6148c2ecf20Sopenharmony_ci}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_cistatic int
6178c2ecf20Sopenharmony_ciefx_mcdi_check_supported(struct efx_nic *efx, unsigned int cmd, size_t inlen)
6188c2ecf20Sopenharmony_ci{
6198c2ecf20Sopenharmony_ci	if (efx->type->mcdi_max_ver < 0 ||
6208c2ecf20Sopenharmony_ci	     (efx->type->mcdi_max_ver < 2 &&
6218c2ecf20Sopenharmony_ci	      cmd > MC_CMD_CMD_SPACE_ESCAPE_7))
6228c2ecf20Sopenharmony_ci		return -EINVAL;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	if (inlen > MCDI_CTL_SDU_LEN_MAX_V2 ||
6258c2ecf20Sopenharmony_ci	    (efx->type->mcdi_max_ver < 2 &&
6268c2ecf20Sopenharmony_ci	     inlen > MCDI_CTL_SDU_LEN_MAX_V1))
6278c2ecf20Sopenharmony_ci		return -EMSGSIZE;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	return 0;
6308c2ecf20Sopenharmony_ci}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_cistatic bool efx_mcdi_get_proxy_handle(struct efx_nic *efx,
6338c2ecf20Sopenharmony_ci				      size_t hdr_len, size_t data_len,
6348c2ecf20Sopenharmony_ci				      u32 *proxy_handle)
6358c2ecf20Sopenharmony_ci{
6368c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF_ERR(testbuf);
6378c2ecf20Sopenharmony_ci	const size_t buflen = sizeof(testbuf);
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	if (!proxy_handle || data_len < buflen)
6408c2ecf20Sopenharmony_ci		return false;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	efx->type->mcdi_read_response(efx, testbuf, hdr_len, buflen);
6438c2ecf20Sopenharmony_ci	if (MCDI_DWORD(testbuf, ERR_CODE) == MC_CMD_ERR_PROXY_PENDING) {
6448c2ecf20Sopenharmony_ci		*proxy_handle = MCDI_DWORD(testbuf, ERR_PROXY_PENDING_HANDLE);
6458c2ecf20Sopenharmony_ci		return true;
6468c2ecf20Sopenharmony_ci	}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	return false;
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_cistatic int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned int cmd,
6528c2ecf20Sopenharmony_ci				size_t inlen,
6538c2ecf20Sopenharmony_ci				efx_dword_t *outbuf, size_t outlen,
6548c2ecf20Sopenharmony_ci				size_t *outlen_actual, bool quiet,
6558c2ecf20Sopenharmony_ci				u32 *proxy_handle, int *raw_rc)
6568c2ecf20Sopenharmony_ci{
6578c2ecf20Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
6588c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF_ERR(errbuf);
6598c2ecf20Sopenharmony_ci	int rc;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	if (mcdi->mode == MCDI_MODE_POLL)
6628c2ecf20Sopenharmony_ci		rc = efx_mcdi_poll(efx);
6638c2ecf20Sopenharmony_ci	else
6648c2ecf20Sopenharmony_ci		rc = efx_mcdi_await_completion(efx);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	if (rc != 0) {
6678c2ecf20Sopenharmony_ci		netif_err(efx, hw, efx->net_dev,
6688c2ecf20Sopenharmony_ci			  "MC command 0x%x inlen %d mode %d timed out\n",
6698c2ecf20Sopenharmony_ci			  cmd, (int)inlen, mcdi->mode);
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci		if (mcdi->mode == MCDI_MODE_EVENTS && efx_mcdi_poll_once(efx)) {
6728c2ecf20Sopenharmony_ci			netif_err(efx, hw, efx->net_dev,
6738c2ecf20Sopenharmony_ci				  "MCDI request was completed without an event\n");
6748c2ecf20Sopenharmony_ci			rc = 0;
6758c2ecf20Sopenharmony_ci		}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci		efx_mcdi_abandon(efx);
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci		/* Close the race with efx_mcdi_ev_cpl() executing just too late
6808c2ecf20Sopenharmony_ci		 * and completing a request we've just cancelled, by ensuring
6818c2ecf20Sopenharmony_ci		 * that the seqno check therein fails.
6828c2ecf20Sopenharmony_ci		 */
6838c2ecf20Sopenharmony_ci		spin_lock_bh(&mcdi->iface_lock);
6848c2ecf20Sopenharmony_ci		++mcdi->seqno;
6858c2ecf20Sopenharmony_ci		++mcdi->credits;
6868c2ecf20Sopenharmony_ci		spin_unlock_bh(&mcdi->iface_lock);
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	if (proxy_handle)
6908c2ecf20Sopenharmony_ci		*proxy_handle = 0;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	if (rc != 0) {
6938c2ecf20Sopenharmony_ci		if (outlen_actual)
6948c2ecf20Sopenharmony_ci			*outlen_actual = 0;
6958c2ecf20Sopenharmony_ci	} else {
6968c2ecf20Sopenharmony_ci		size_t hdr_len, data_len, err_len;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci		/* At the very least we need a memory barrier here to ensure
6998c2ecf20Sopenharmony_ci		 * we pick up changes from efx_mcdi_ev_cpl(). Protect against
7008c2ecf20Sopenharmony_ci		 * a spurious efx_mcdi_ev_cpl() running concurrently by
7018c2ecf20Sopenharmony_ci		 * acquiring the iface_lock. */
7028c2ecf20Sopenharmony_ci		spin_lock_bh(&mcdi->iface_lock);
7038c2ecf20Sopenharmony_ci		rc = mcdi->resprc;
7048c2ecf20Sopenharmony_ci		if (raw_rc)
7058c2ecf20Sopenharmony_ci			*raw_rc = mcdi->resprc_raw;
7068c2ecf20Sopenharmony_ci		hdr_len = mcdi->resp_hdr_len;
7078c2ecf20Sopenharmony_ci		data_len = mcdi->resp_data_len;
7088c2ecf20Sopenharmony_ci		err_len = min(sizeof(errbuf), data_len);
7098c2ecf20Sopenharmony_ci		spin_unlock_bh(&mcdi->iface_lock);
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci		BUG_ON(rc > 0);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci		efx->type->mcdi_read_response(efx, outbuf, hdr_len,
7148c2ecf20Sopenharmony_ci					      min(outlen, data_len));
7158c2ecf20Sopenharmony_ci		if (outlen_actual)
7168c2ecf20Sopenharmony_ci			*outlen_actual = data_len;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci		efx->type->mcdi_read_response(efx, errbuf, hdr_len, err_len);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci		if (cmd == MC_CMD_REBOOT && rc == -EIO) {
7218c2ecf20Sopenharmony_ci			/* Don't reset if MC_CMD_REBOOT returns EIO */
7228c2ecf20Sopenharmony_ci		} else if (rc == -EIO || rc == -EINTR) {
7238c2ecf20Sopenharmony_ci			netif_err(efx, hw, efx->net_dev, "MC reboot detected\n");
7248c2ecf20Sopenharmony_ci			netif_dbg(efx, hw, efx->net_dev, "MC rebooted during command %d rc %d\n",
7258c2ecf20Sopenharmony_ci				  cmd, -rc);
7268c2ecf20Sopenharmony_ci			if (efx->type->mcdi_reboot_detected)
7278c2ecf20Sopenharmony_ci				efx->type->mcdi_reboot_detected(efx);
7288c2ecf20Sopenharmony_ci			efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE);
7298c2ecf20Sopenharmony_ci		} else if (proxy_handle && (rc == -EPROTO) &&
7308c2ecf20Sopenharmony_ci			   efx_mcdi_get_proxy_handle(efx, hdr_len, data_len,
7318c2ecf20Sopenharmony_ci						     proxy_handle)) {
7328c2ecf20Sopenharmony_ci			mcdi->proxy_rx_status = 0;
7338c2ecf20Sopenharmony_ci			mcdi->proxy_rx_handle = 0;
7348c2ecf20Sopenharmony_ci			mcdi->state = MCDI_STATE_PROXY_WAIT;
7358c2ecf20Sopenharmony_ci		} else if (rc && !quiet) {
7368c2ecf20Sopenharmony_ci			efx_mcdi_display_error(efx, cmd, inlen, errbuf, err_len,
7378c2ecf20Sopenharmony_ci					       rc);
7388c2ecf20Sopenharmony_ci		}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci		if (rc == -EIO || rc == -EINTR) {
7418c2ecf20Sopenharmony_ci			msleep(MCDI_STATUS_SLEEP_MS);
7428c2ecf20Sopenharmony_ci			efx_mcdi_poll_reboot(efx);
7438c2ecf20Sopenharmony_ci			mcdi->new_epoch = true;
7448c2ecf20Sopenharmony_ci		}
7458c2ecf20Sopenharmony_ci	}
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	if (!proxy_handle || !*proxy_handle)
7488c2ecf20Sopenharmony_ci		efx_mcdi_release(mcdi);
7498c2ecf20Sopenharmony_ci	return rc;
7508c2ecf20Sopenharmony_ci}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_cistatic void efx_mcdi_proxy_abort(struct efx_mcdi_iface *mcdi)
7538c2ecf20Sopenharmony_ci{
7548c2ecf20Sopenharmony_ci	if (mcdi->state == MCDI_STATE_PROXY_WAIT) {
7558c2ecf20Sopenharmony_ci		/* Interrupt the proxy wait. */
7568c2ecf20Sopenharmony_ci		mcdi->proxy_rx_status = -EINTR;
7578c2ecf20Sopenharmony_ci		wake_up(&mcdi->proxy_rx_wq);
7588c2ecf20Sopenharmony_ci	}
7598c2ecf20Sopenharmony_ci}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_cistatic void efx_mcdi_ev_proxy_response(struct efx_nic *efx,
7628c2ecf20Sopenharmony_ci				       u32 handle, int status)
7638c2ecf20Sopenharmony_ci{
7648c2ecf20Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	WARN_ON(mcdi->state != MCDI_STATE_PROXY_WAIT);
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	mcdi->proxy_rx_status = efx_mcdi_errno(status);
7698c2ecf20Sopenharmony_ci	/* Ensure the status is written before we update the handle, since the
7708c2ecf20Sopenharmony_ci	 * latter is used to check if we've finished.
7718c2ecf20Sopenharmony_ci	 */
7728c2ecf20Sopenharmony_ci	wmb();
7738c2ecf20Sopenharmony_ci	mcdi->proxy_rx_handle = handle;
7748c2ecf20Sopenharmony_ci	wake_up(&mcdi->proxy_rx_wq);
7758c2ecf20Sopenharmony_ci}
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_cistatic int efx_mcdi_proxy_wait(struct efx_nic *efx, u32 handle, bool quiet)
7788c2ecf20Sopenharmony_ci{
7798c2ecf20Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
7808c2ecf20Sopenharmony_ci	int rc;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	/* Wait for a proxy event, or timeout. */
7838c2ecf20Sopenharmony_ci	rc = wait_event_timeout(mcdi->proxy_rx_wq,
7848c2ecf20Sopenharmony_ci				mcdi->proxy_rx_handle != 0 ||
7858c2ecf20Sopenharmony_ci				mcdi->proxy_rx_status == -EINTR,
7868c2ecf20Sopenharmony_ci				MCDI_RPC_TIMEOUT);
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	if (rc <= 0) {
7898c2ecf20Sopenharmony_ci		netif_dbg(efx, hw, efx->net_dev,
7908c2ecf20Sopenharmony_ci			  "MCDI proxy timeout %d\n", handle);
7918c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
7928c2ecf20Sopenharmony_ci	} else if (mcdi->proxy_rx_handle != handle) {
7938c2ecf20Sopenharmony_ci		netif_warn(efx, hw, efx->net_dev,
7948c2ecf20Sopenharmony_ci			   "MCDI proxy unexpected handle %d (expected %d)\n",
7958c2ecf20Sopenharmony_ci			   mcdi->proxy_rx_handle, handle);
7968c2ecf20Sopenharmony_ci		return -EINVAL;
7978c2ecf20Sopenharmony_ci	}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	return mcdi->proxy_rx_status;
8008c2ecf20Sopenharmony_ci}
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_cistatic int _efx_mcdi_rpc(struct efx_nic *efx, unsigned int cmd,
8038c2ecf20Sopenharmony_ci			 const efx_dword_t *inbuf, size_t inlen,
8048c2ecf20Sopenharmony_ci			 efx_dword_t *outbuf, size_t outlen,
8058c2ecf20Sopenharmony_ci			 size_t *outlen_actual, bool quiet, int *raw_rc)
8068c2ecf20Sopenharmony_ci{
8078c2ecf20Sopenharmony_ci	u32 proxy_handle = 0; /* Zero is an invalid proxy handle. */
8088c2ecf20Sopenharmony_ci	int rc;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	if (inbuf && inlen && (inbuf == outbuf)) {
8118c2ecf20Sopenharmony_ci		/* The input buffer can't be aliased with the output. */
8128c2ecf20Sopenharmony_ci		WARN_ON(1);
8138c2ecf20Sopenharmony_ci		return -EINVAL;
8148c2ecf20Sopenharmony_ci	}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc_start(efx, cmd, inbuf, inlen);
8178c2ecf20Sopenharmony_ci	if (rc)
8188c2ecf20Sopenharmony_ci		return rc;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	rc = _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen,
8218c2ecf20Sopenharmony_ci				  outlen_actual, quiet, &proxy_handle, raw_rc);
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	if (proxy_handle) {
8248c2ecf20Sopenharmony_ci		/* Handle proxy authorisation. This allows approval of MCDI
8258c2ecf20Sopenharmony_ci		 * operations to be delegated to the admin function, allowing
8268c2ecf20Sopenharmony_ci		 * fine control over (eg) multicast subscriptions.
8278c2ecf20Sopenharmony_ci		 */
8288c2ecf20Sopenharmony_ci		struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci		netif_dbg(efx, hw, efx->net_dev,
8318c2ecf20Sopenharmony_ci			  "MCDI waiting for proxy auth %d\n",
8328c2ecf20Sopenharmony_ci			  proxy_handle);
8338c2ecf20Sopenharmony_ci		rc = efx_mcdi_proxy_wait(efx, proxy_handle, quiet);
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci		if (rc == 0) {
8368c2ecf20Sopenharmony_ci			netif_dbg(efx, hw, efx->net_dev,
8378c2ecf20Sopenharmony_ci				  "MCDI proxy retry %d\n", proxy_handle);
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci			/* We now retry the original request. */
8408c2ecf20Sopenharmony_ci			mcdi->state = MCDI_STATE_RUNNING_SYNC;
8418c2ecf20Sopenharmony_ci			efx_mcdi_send_request(efx, cmd, inbuf, inlen);
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci			rc = _efx_mcdi_rpc_finish(efx, cmd, inlen,
8448c2ecf20Sopenharmony_ci						  outbuf, outlen, outlen_actual,
8458c2ecf20Sopenharmony_ci						  quiet, NULL, raw_rc);
8468c2ecf20Sopenharmony_ci		} else {
8478c2ecf20Sopenharmony_ci			netif_cond_dbg(efx, hw, efx->net_dev, rc == -EPERM, err,
8488c2ecf20Sopenharmony_ci				       "MC command 0x%x failed after proxy auth rc=%d\n",
8498c2ecf20Sopenharmony_ci				       cmd, rc);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci			if (rc == -EINTR || rc == -EIO)
8528c2ecf20Sopenharmony_ci				efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE);
8538c2ecf20Sopenharmony_ci			efx_mcdi_release(mcdi);
8548c2ecf20Sopenharmony_ci		}
8558c2ecf20Sopenharmony_ci	}
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	return rc;
8588c2ecf20Sopenharmony_ci}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_cistatic int _efx_mcdi_rpc_evb_retry(struct efx_nic *efx, unsigned cmd,
8618c2ecf20Sopenharmony_ci				   const efx_dword_t *inbuf, size_t inlen,
8628c2ecf20Sopenharmony_ci				   efx_dword_t *outbuf, size_t outlen,
8638c2ecf20Sopenharmony_ci				   size_t *outlen_actual, bool quiet)
8648c2ecf20Sopenharmony_ci{
8658c2ecf20Sopenharmony_ci	int raw_rc = 0;
8668c2ecf20Sopenharmony_ci	int rc;
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	rc = _efx_mcdi_rpc(efx, cmd, inbuf, inlen,
8698c2ecf20Sopenharmony_ci			   outbuf, outlen, outlen_actual, true, &raw_rc);
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	if ((rc == -EPROTO) && (raw_rc == MC_CMD_ERR_NO_EVB_PORT) &&
8728c2ecf20Sopenharmony_ci	    efx->type->is_vf) {
8738c2ecf20Sopenharmony_ci		/* If the EVB port isn't available within a VF this may
8748c2ecf20Sopenharmony_ci		 * mean the PF is still bringing the switch up. We should
8758c2ecf20Sopenharmony_ci		 * retry our request shortly.
8768c2ecf20Sopenharmony_ci		 */
8778c2ecf20Sopenharmony_ci		unsigned long abort_time = jiffies + MCDI_RPC_TIMEOUT;
8788c2ecf20Sopenharmony_ci		unsigned int delay_us = 10000;
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci		netif_dbg(efx, hw, efx->net_dev,
8818c2ecf20Sopenharmony_ci			  "%s: NO_EVB_PORT; will retry request\n",
8828c2ecf20Sopenharmony_ci			  __func__);
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci		do {
8858c2ecf20Sopenharmony_ci			usleep_range(delay_us, delay_us + 10000);
8868c2ecf20Sopenharmony_ci			rc = _efx_mcdi_rpc(efx, cmd, inbuf, inlen,
8878c2ecf20Sopenharmony_ci					   outbuf, outlen, outlen_actual,
8888c2ecf20Sopenharmony_ci					   true, &raw_rc);
8898c2ecf20Sopenharmony_ci			if (delay_us < 100000)
8908c2ecf20Sopenharmony_ci				delay_us <<= 1;
8918c2ecf20Sopenharmony_ci		} while ((rc == -EPROTO) &&
8928c2ecf20Sopenharmony_ci			 (raw_rc == MC_CMD_ERR_NO_EVB_PORT) &&
8938c2ecf20Sopenharmony_ci			 time_before(jiffies, abort_time));
8948c2ecf20Sopenharmony_ci	}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	if (rc && !quiet && !(cmd == MC_CMD_REBOOT && rc == -EIO))
8978c2ecf20Sopenharmony_ci		efx_mcdi_display_error(efx, cmd, inlen,
8988c2ecf20Sopenharmony_ci				       outbuf, outlen, rc);
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	return rc;
9018c2ecf20Sopenharmony_ci}
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci/**
9048c2ecf20Sopenharmony_ci * efx_mcdi_rpc - Issue an MCDI command and wait for completion
9058c2ecf20Sopenharmony_ci * @efx: NIC through which to issue the command
9068c2ecf20Sopenharmony_ci * @cmd: Command type number
9078c2ecf20Sopenharmony_ci * @inbuf: Command parameters
9088c2ecf20Sopenharmony_ci * @inlen: Length of command parameters, in bytes.  Must be a multiple
9098c2ecf20Sopenharmony_ci *	of 4 and no greater than %MCDI_CTL_SDU_LEN_MAX_V1.
9108c2ecf20Sopenharmony_ci * @outbuf: Response buffer.  May be %NULL if @outlen is 0.
9118c2ecf20Sopenharmony_ci * @outlen: Length of response buffer, in bytes.  If the actual
9128c2ecf20Sopenharmony_ci *	response is longer than @outlen & ~3, it will be truncated
9138c2ecf20Sopenharmony_ci *	to that length.
9148c2ecf20Sopenharmony_ci * @outlen_actual: Pointer through which to return the actual response
9158c2ecf20Sopenharmony_ci *	length.  May be %NULL if this is not needed.
9168c2ecf20Sopenharmony_ci *
9178c2ecf20Sopenharmony_ci * This function may sleep and therefore must be called in an appropriate
9188c2ecf20Sopenharmony_ci * context.
9198c2ecf20Sopenharmony_ci *
9208c2ecf20Sopenharmony_ci * Return: A negative error code, or zero if successful.  The error
9218c2ecf20Sopenharmony_ci *	code may come from the MCDI response or may indicate a failure
9228c2ecf20Sopenharmony_ci *	to communicate with the MC.  In the former case, the response
9238c2ecf20Sopenharmony_ci *	will still be copied to @outbuf and *@outlen_actual will be
9248c2ecf20Sopenharmony_ci *	set accordingly.  In the latter case, *@outlen_actual will be
9258c2ecf20Sopenharmony_ci *	set to zero.
9268c2ecf20Sopenharmony_ci */
9278c2ecf20Sopenharmony_ciint efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd,
9288c2ecf20Sopenharmony_ci		 const efx_dword_t *inbuf, size_t inlen,
9298c2ecf20Sopenharmony_ci		 efx_dword_t *outbuf, size_t outlen,
9308c2ecf20Sopenharmony_ci		 size_t *outlen_actual)
9318c2ecf20Sopenharmony_ci{
9328c2ecf20Sopenharmony_ci	return _efx_mcdi_rpc_evb_retry(efx, cmd, inbuf, inlen, outbuf, outlen,
9338c2ecf20Sopenharmony_ci				       outlen_actual, false);
9348c2ecf20Sopenharmony_ci}
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci/* Normally, on receiving an error code in the MCDI response,
9378c2ecf20Sopenharmony_ci * efx_mcdi_rpc will log an error message containing (among other
9388c2ecf20Sopenharmony_ci * things) the raw error code, by means of efx_mcdi_display_error.
9398c2ecf20Sopenharmony_ci * This _quiet version suppresses that; if the caller wishes to log
9408c2ecf20Sopenharmony_ci * the error conditionally on the return code, it should call this
9418c2ecf20Sopenharmony_ci * function and is then responsible for calling efx_mcdi_display_error
9428c2ecf20Sopenharmony_ci * as needed.
9438c2ecf20Sopenharmony_ci */
9448c2ecf20Sopenharmony_ciint efx_mcdi_rpc_quiet(struct efx_nic *efx, unsigned cmd,
9458c2ecf20Sopenharmony_ci		       const efx_dword_t *inbuf, size_t inlen,
9468c2ecf20Sopenharmony_ci		       efx_dword_t *outbuf, size_t outlen,
9478c2ecf20Sopenharmony_ci		       size_t *outlen_actual)
9488c2ecf20Sopenharmony_ci{
9498c2ecf20Sopenharmony_ci	return _efx_mcdi_rpc_evb_retry(efx, cmd, inbuf, inlen, outbuf, outlen,
9508c2ecf20Sopenharmony_ci				       outlen_actual, true);
9518c2ecf20Sopenharmony_ci}
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ciint efx_mcdi_rpc_start(struct efx_nic *efx, unsigned cmd,
9548c2ecf20Sopenharmony_ci		       const efx_dword_t *inbuf, size_t inlen)
9558c2ecf20Sopenharmony_ci{
9568c2ecf20Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
9578c2ecf20Sopenharmony_ci	int rc;
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	rc = efx_mcdi_check_supported(efx, cmd, inlen);
9608c2ecf20Sopenharmony_ci	if (rc)
9618c2ecf20Sopenharmony_ci		return rc;
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	if (efx->mc_bist_for_other_fn)
9648c2ecf20Sopenharmony_ci		return -ENETDOWN;
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	if (mcdi->mode == MCDI_MODE_FAIL)
9678c2ecf20Sopenharmony_ci		return -ENETDOWN;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	efx_mcdi_acquire_sync(mcdi);
9708c2ecf20Sopenharmony_ci	efx_mcdi_send_request(efx, cmd, inbuf, inlen);
9718c2ecf20Sopenharmony_ci	return 0;
9728c2ecf20Sopenharmony_ci}
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_cistatic int _efx_mcdi_rpc_async(struct efx_nic *efx, unsigned int cmd,
9758c2ecf20Sopenharmony_ci			       const efx_dword_t *inbuf, size_t inlen,
9768c2ecf20Sopenharmony_ci			       size_t outlen,
9778c2ecf20Sopenharmony_ci			       efx_mcdi_async_completer *complete,
9788c2ecf20Sopenharmony_ci			       unsigned long cookie, bool quiet)
9798c2ecf20Sopenharmony_ci{
9808c2ecf20Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
9818c2ecf20Sopenharmony_ci	struct efx_mcdi_async_param *async;
9828c2ecf20Sopenharmony_ci	int rc;
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci	rc = efx_mcdi_check_supported(efx, cmd, inlen);
9858c2ecf20Sopenharmony_ci	if (rc)
9868c2ecf20Sopenharmony_ci		return rc;
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	if (efx->mc_bist_for_other_fn)
9898c2ecf20Sopenharmony_ci		return -ENETDOWN;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	async = kmalloc(sizeof(*async) + ALIGN(max(inlen, outlen), 4),
9928c2ecf20Sopenharmony_ci			GFP_ATOMIC);
9938c2ecf20Sopenharmony_ci	if (!async)
9948c2ecf20Sopenharmony_ci		return -ENOMEM;
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	async->cmd = cmd;
9978c2ecf20Sopenharmony_ci	async->inlen = inlen;
9988c2ecf20Sopenharmony_ci	async->outlen = outlen;
9998c2ecf20Sopenharmony_ci	async->quiet = quiet;
10008c2ecf20Sopenharmony_ci	async->complete = complete;
10018c2ecf20Sopenharmony_ci	async->cookie = cookie;
10028c2ecf20Sopenharmony_ci	memcpy(async + 1, inbuf, inlen);
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	spin_lock_bh(&mcdi->async_lock);
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	if (mcdi->mode == MCDI_MODE_EVENTS) {
10078c2ecf20Sopenharmony_ci		list_add_tail(&async->list, &mcdi->async_list);
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci		/* If this is at the front of the queue, try to start it
10108c2ecf20Sopenharmony_ci		 * immediately
10118c2ecf20Sopenharmony_ci		 */
10128c2ecf20Sopenharmony_ci		if (mcdi->async_list.next == &async->list &&
10138c2ecf20Sopenharmony_ci		    efx_mcdi_acquire_async(mcdi)) {
10148c2ecf20Sopenharmony_ci			efx_mcdi_send_request(efx, cmd, inbuf, inlen);
10158c2ecf20Sopenharmony_ci			mod_timer(&mcdi->async_timer,
10168c2ecf20Sopenharmony_ci				  jiffies + MCDI_RPC_TIMEOUT);
10178c2ecf20Sopenharmony_ci		}
10188c2ecf20Sopenharmony_ci	} else {
10198c2ecf20Sopenharmony_ci		kfree(async);
10208c2ecf20Sopenharmony_ci		rc = -ENETDOWN;
10218c2ecf20Sopenharmony_ci	}
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	spin_unlock_bh(&mcdi->async_lock);
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	return rc;
10268c2ecf20Sopenharmony_ci}
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci/**
10298c2ecf20Sopenharmony_ci * efx_mcdi_rpc_async - Schedule an MCDI command to run asynchronously
10308c2ecf20Sopenharmony_ci * @efx: NIC through which to issue the command
10318c2ecf20Sopenharmony_ci * @cmd: Command type number
10328c2ecf20Sopenharmony_ci * @inbuf: Command parameters
10338c2ecf20Sopenharmony_ci * @inlen: Length of command parameters, in bytes
10348c2ecf20Sopenharmony_ci * @outlen: Length to allocate for response buffer, in bytes
10358c2ecf20Sopenharmony_ci * @complete: Function to be called on completion or cancellation.
10368c2ecf20Sopenharmony_ci * @cookie: Arbitrary value to be passed to @complete.
10378c2ecf20Sopenharmony_ci *
10388c2ecf20Sopenharmony_ci * This function does not sleep and therefore may be called in atomic
10398c2ecf20Sopenharmony_ci * context.  It will fail if event queues are disabled or if MCDI
10408c2ecf20Sopenharmony_ci * event completions have been disabled due to an error.
10418c2ecf20Sopenharmony_ci *
10428c2ecf20Sopenharmony_ci * If it succeeds, the @complete function will be called exactly once
10438c2ecf20Sopenharmony_ci * in atomic context, when one of the following occurs:
10448c2ecf20Sopenharmony_ci * (a) the completion event is received (in NAPI context)
10458c2ecf20Sopenharmony_ci * (b) event queues are disabled (in the process that disables them)
10468c2ecf20Sopenharmony_ci * (c) the request times-out (in timer context)
10478c2ecf20Sopenharmony_ci */
10488c2ecf20Sopenharmony_ciint
10498c2ecf20Sopenharmony_ciefx_mcdi_rpc_async(struct efx_nic *efx, unsigned int cmd,
10508c2ecf20Sopenharmony_ci		   const efx_dword_t *inbuf, size_t inlen, size_t outlen,
10518c2ecf20Sopenharmony_ci		   efx_mcdi_async_completer *complete, unsigned long cookie)
10528c2ecf20Sopenharmony_ci{
10538c2ecf20Sopenharmony_ci	return _efx_mcdi_rpc_async(efx, cmd, inbuf, inlen, outlen, complete,
10548c2ecf20Sopenharmony_ci				   cookie, false);
10558c2ecf20Sopenharmony_ci}
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ciint efx_mcdi_rpc_async_quiet(struct efx_nic *efx, unsigned int cmd,
10588c2ecf20Sopenharmony_ci			     const efx_dword_t *inbuf, size_t inlen,
10598c2ecf20Sopenharmony_ci			     size_t outlen, efx_mcdi_async_completer *complete,
10608c2ecf20Sopenharmony_ci			     unsigned long cookie)
10618c2ecf20Sopenharmony_ci{
10628c2ecf20Sopenharmony_ci	return _efx_mcdi_rpc_async(efx, cmd, inbuf, inlen, outlen, complete,
10638c2ecf20Sopenharmony_ci				   cookie, true);
10648c2ecf20Sopenharmony_ci}
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ciint efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen,
10678c2ecf20Sopenharmony_ci			efx_dword_t *outbuf, size_t outlen,
10688c2ecf20Sopenharmony_ci			size_t *outlen_actual)
10698c2ecf20Sopenharmony_ci{
10708c2ecf20Sopenharmony_ci	return _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen,
10718c2ecf20Sopenharmony_ci				    outlen_actual, false, NULL, NULL);
10728c2ecf20Sopenharmony_ci}
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ciint efx_mcdi_rpc_finish_quiet(struct efx_nic *efx, unsigned cmd, size_t inlen,
10758c2ecf20Sopenharmony_ci			      efx_dword_t *outbuf, size_t outlen,
10768c2ecf20Sopenharmony_ci			      size_t *outlen_actual)
10778c2ecf20Sopenharmony_ci{
10788c2ecf20Sopenharmony_ci	return _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen,
10798c2ecf20Sopenharmony_ci				    outlen_actual, true, NULL, NULL);
10808c2ecf20Sopenharmony_ci}
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_civoid efx_mcdi_display_error(struct efx_nic *efx, unsigned cmd,
10838c2ecf20Sopenharmony_ci			    size_t inlen, efx_dword_t *outbuf,
10848c2ecf20Sopenharmony_ci			    size_t outlen, int rc)
10858c2ecf20Sopenharmony_ci{
10868c2ecf20Sopenharmony_ci	int code = 0, err_arg = 0;
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	if (outlen >= MC_CMD_ERR_CODE_OFST + 4)
10898c2ecf20Sopenharmony_ci		code = MCDI_DWORD(outbuf, ERR_CODE);
10908c2ecf20Sopenharmony_ci	if (outlen >= MC_CMD_ERR_ARG_OFST + 4)
10918c2ecf20Sopenharmony_ci		err_arg = MCDI_DWORD(outbuf, ERR_ARG);
10928c2ecf20Sopenharmony_ci	netif_cond_dbg(efx, hw, efx->net_dev, rc == -EPERM, err,
10938c2ecf20Sopenharmony_ci		       "MC command 0x%x inlen %zu failed rc=%d (raw=%d) arg=%d\n",
10948c2ecf20Sopenharmony_ci		       cmd, inlen, rc, code, err_arg);
10958c2ecf20Sopenharmony_ci}
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci/* Switch to polled MCDI completions.  This can be called in various
10988c2ecf20Sopenharmony_ci * error conditions with various locks held, so it must be lockless.
10998c2ecf20Sopenharmony_ci * Caller is responsible for flushing asynchronous requests later.
11008c2ecf20Sopenharmony_ci */
11018c2ecf20Sopenharmony_civoid efx_mcdi_mode_poll(struct efx_nic *efx)
11028c2ecf20Sopenharmony_ci{
11038c2ecf20Sopenharmony_ci	struct efx_mcdi_iface *mcdi;
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	if (!efx->mcdi)
11068c2ecf20Sopenharmony_ci		return;
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	mcdi = efx_mcdi(efx);
11098c2ecf20Sopenharmony_ci	/* If already in polling mode, nothing to do.
11108c2ecf20Sopenharmony_ci	 * If in fail-fast state, don't switch to polled completion.
11118c2ecf20Sopenharmony_ci	 * FLR recovery will do that later.
11128c2ecf20Sopenharmony_ci	 */
11138c2ecf20Sopenharmony_ci	if (mcdi->mode == MCDI_MODE_POLL || mcdi->mode == MCDI_MODE_FAIL)
11148c2ecf20Sopenharmony_ci		return;
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	/* We can switch from event completion to polled completion, because
11178c2ecf20Sopenharmony_ci	 * mcdi requests are always completed in shared memory. We do this by
11188c2ecf20Sopenharmony_ci	 * switching the mode to POLL'd then completing the request.
11198c2ecf20Sopenharmony_ci	 * efx_mcdi_await_completion() will then call efx_mcdi_poll().
11208c2ecf20Sopenharmony_ci	 *
11218c2ecf20Sopenharmony_ci	 * We need an smp_wmb() to synchronise with efx_mcdi_await_completion(),
11228c2ecf20Sopenharmony_ci	 * which efx_mcdi_complete_sync() provides for us.
11238c2ecf20Sopenharmony_ci	 */
11248c2ecf20Sopenharmony_ci	mcdi->mode = MCDI_MODE_POLL;
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	efx_mcdi_complete_sync(mcdi);
11278c2ecf20Sopenharmony_ci}
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci/* Flush any running or queued asynchronous requests, after event processing
11308c2ecf20Sopenharmony_ci * is stopped
11318c2ecf20Sopenharmony_ci */
11328c2ecf20Sopenharmony_civoid efx_mcdi_flush_async(struct efx_nic *efx)
11338c2ecf20Sopenharmony_ci{
11348c2ecf20Sopenharmony_ci	struct efx_mcdi_async_param *async, *next;
11358c2ecf20Sopenharmony_ci	struct efx_mcdi_iface *mcdi;
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	if (!efx->mcdi)
11388c2ecf20Sopenharmony_ci		return;
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	mcdi = efx_mcdi(efx);
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	/* We must be in poll or fail mode so no more requests can be queued */
11438c2ecf20Sopenharmony_ci	BUG_ON(mcdi->mode == MCDI_MODE_EVENTS);
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	del_timer_sync(&mcdi->async_timer);
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	/* If a request is still running, make sure we give the MC
11488c2ecf20Sopenharmony_ci	 * time to complete it so that the response won't overwrite our
11498c2ecf20Sopenharmony_ci	 * next request.
11508c2ecf20Sopenharmony_ci	 */
11518c2ecf20Sopenharmony_ci	if (mcdi->state == MCDI_STATE_RUNNING_ASYNC) {
11528c2ecf20Sopenharmony_ci		efx_mcdi_poll(efx);
11538c2ecf20Sopenharmony_ci		mcdi->state = MCDI_STATE_QUIESCENT;
11548c2ecf20Sopenharmony_ci	}
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	/* Nothing else will access the async list now, so it is safe
11578c2ecf20Sopenharmony_ci	 * to walk it without holding async_lock.  If we hold it while
11588c2ecf20Sopenharmony_ci	 * calling a completer then lockdep may warn that we have
11598c2ecf20Sopenharmony_ci	 * acquired locks in the wrong order.
11608c2ecf20Sopenharmony_ci	 */
11618c2ecf20Sopenharmony_ci	list_for_each_entry_safe(async, next, &mcdi->async_list, list) {
11628c2ecf20Sopenharmony_ci		if (async->complete)
11638c2ecf20Sopenharmony_ci			async->complete(efx, async->cookie, -ENETDOWN, NULL, 0);
11648c2ecf20Sopenharmony_ci		list_del(&async->list);
11658c2ecf20Sopenharmony_ci		kfree(async);
11668c2ecf20Sopenharmony_ci	}
11678c2ecf20Sopenharmony_ci}
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_civoid efx_mcdi_mode_event(struct efx_nic *efx)
11708c2ecf20Sopenharmony_ci{
11718c2ecf20Sopenharmony_ci	struct efx_mcdi_iface *mcdi;
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci	if (!efx->mcdi)
11748c2ecf20Sopenharmony_ci		return;
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	mcdi = efx_mcdi(efx);
11778c2ecf20Sopenharmony_ci	/* If already in event completion mode, nothing to do.
11788c2ecf20Sopenharmony_ci	 * If in fail-fast state, don't switch to event completion.  FLR
11798c2ecf20Sopenharmony_ci	 * recovery will do that later.
11808c2ecf20Sopenharmony_ci	 */
11818c2ecf20Sopenharmony_ci	if (mcdi->mode == MCDI_MODE_EVENTS || mcdi->mode == MCDI_MODE_FAIL)
11828c2ecf20Sopenharmony_ci		return;
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	/* We can't switch from polled to event completion in the middle of a
11858c2ecf20Sopenharmony_ci	 * request, because the completion method is specified in the request.
11868c2ecf20Sopenharmony_ci	 * So acquire the interface to serialise the requestors. We don't need
11878c2ecf20Sopenharmony_ci	 * to acquire the iface_lock to change the mode here, but we do need a
11888c2ecf20Sopenharmony_ci	 * write memory barrier ensure that efx_mcdi_rpc() sees it, which
11898c2ecf20Sopenharmony_ci	 * efx_mcdi_acquire() provides.
11908c2ecf20Sopenharmony_ci	 */
11918c2ecf20Sopenharmony_ci	efx_mcdi_acquire_sync(mcdi);
11928c2ecf20Sopenharmony_ci	mcdi->mode = MCDI_MODE_EVENTS;
11938c2ecf20Sopenharmony_ci	efx_mcdi_release(mcdi);
11948c2ecf20Sopenharmony_ci}
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_cistatic void efx_mcdi_ev_death(struct efx_nic *efx, int rc)
11978c2ecf20Sopenharmony_ci{
11988c2ecf20Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	/* If there is an outstanding MCDI request, it has been terminated
12018c2ecf20Sopenharmony_ci	 * either by a BADASSERT or REBOOT event. If the mcdi interface is
12028c2ecf20Sopenharmony_ci	 * in polled mode, then do nothing because the MC reboot handler will
12038c2ecf20Sopenharmony_ci	 * set the header correctly. However, if the mcdi interface is waiting
12048c2ecf20Sopenharmony_ci	 * for a CMDDONE event it won't receive it [and since all MCDI events
12058c2ecf20Sopenharmony_ci	 * are sent to the same queue, we can't be racing with
12068c2ecf20Sopenharmony_ci	 * efx_mcdi_ev_cpl()]
12078c2ecf20Sopenharmony_ci	 *
12088c2ecf20Sopenharmony_ci	 * If there is an outstanding asynchronous request, we can't
12098c2ecf20Sopenharmony_ci	 * complete it now (efx_mcdi_complete() would deadlock).  The
12108c2ecf20Sopenharmony_ci	 * reset process will take care of this.
12118c2ecf20Sopenharmony_ci	 *
12128c2ecf20Sopenharmony_ci	 * There's a race here with efx_mcdi_send_request(), because
12138c2ecf20Sopenharmony_ci	 * we might receive a REBOOT event *before* the request has
12148c2ecf20Sopenharmony_ci	 * been copied out. In polled mode (during startup) this is
12158c2ecf20Sopenharmony_ci	 * irrelevant, because efx_mcdi_complete_sync() is ignored. In
12168c2ecf20Sopenharmony_ci	 * event mode, this condition is just an edge-case of
12178c2ecf20Sopenharmony_ci	 * receiving a REBOOT event after posting the MCDI
12188c2ecf20Sopenharmony_ci	 * request. Did the mc reboot before or after the copyout? The
12198c2ecf20Sopenharmony_ci	 * best we can do always is just return failure.
12208c2ecf20Sopenharmony_ci	 *
12218c2ecf20Sopenharmony_ci	 * If there is an outstanding proxy response expected it is not going
12228c2ecf20Sopenharmony_ci	 * to arrive. We should thus abort it.
12238c2ecf20Sopenharmony_ci	 */
12248c2ecf20Sopenharmony_ci	spin_lock(&mcdi->iface_lock);
12258c2ecf20Sopenharmony_ci	efx_mcdi_proxy_abort(mcdi);
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci	if (efx_mcdi_complete_sync(mcdi)) {
12288c2ecf20Sopenharmony_ci		if (mcdi->mode == MCDI_MODE_EVENTS) {
12298c2ecf20Sopenharmony_ci			mcdi->resprc = rc;
12308c2ecf20Sopenharmony_ci			mcdi->resp_hdr_len = 0;
12318c2ecf20Sopenharmony_ci			mcdi->resp_data_len = 0;
12328c2ecf20Sopenharmony_ci			++mcdi->credits;
12338c2ecf20Sopenharmony_ci		}
12348c2ecf20Sopenharmony_ci	} else {
12358c2ecf20Sopenharmony_ci		int count;
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci		/* Consume the status word since efx_mcdi_rpc_finish() won't */
12388c2ecf20Sopenharmony_ci		for (count = 0; count < MCDI_STATUS_DELAY_COUNT; ++count) {
12398c2ecf20Sopenharmony_ci			rc = efx_mcdi_poll_reboot(efx);
12408c2ecf20Sopenharmony_ci			if (rc)
12418c2ecf20Sopenharmony_ci				break;
12428c2ecf20Sopenharmony_ci			udelay(MCDI_STATUS_DELAY_US);
12438c2ecf20Sopenharmony_ci		}
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci		/* On EF10, a CODE_MC_REBOOT event can be received without the
12468c2ecf20Sopenharmony_ci		 * reboot detection in efx_mcdi_poll_reboot() being triggered.
12478c2ecf20Sopenharmony_ci		 * If zero was returned from the final call to
12488c2ecf20Sopenharmony_ci		 * efx_mcdi_poll_reboot(), the MC reboot wasn't noticed but the
12498c2ecf20Sopenharmony_ci		 * MC has definitely rebooted so prepare for the reset.
12508c2ecf20Sopenharmony_ci		 */
12518c2ecf20Sopenharmony_ci		if (!rc && efx->type->mcdi_reboot_detected)
12528c2ecf20Sopenharmony_ci			efx->type->mcdi_reboot_detected(efx);
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci		mcdi->new_epoch = true;
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci		/* Nobody was waiting for an MCDI request, so trigger a reset */
12578c2ecf20Sopenharmony_ci		efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE);
12588c2ecf20Sopenharmony_ci	}
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci	spin_unlock(&mcdi->iface_lock);
12618c2ecf20Sopenharmony_ci}
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci/* The MC is going down in to BIST mode. set the BIST flag to block
12648c2ecf20Sopenharmony_ci * new MCDI, cancel any outstanding MCDI and and schedule a BIST-type reset
12658c2ecf20Sopenharmony_ci * (which doesn't actually execute a reset, it waits for the controlling
12668c2ecf20Sopenharmony_ci * function to reset it).
12678c2ecf20Sopenharmony_ci */
12688c2ecf20Sopenharmony_cistatic void efx_mcdi_ev_bist(struct efx_nic *efx)
12698c2ecf20Sopenharmony_ci{
12708c2ecf20Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	spin_lock(&mcdi->iface_lock);
12738c2ecf20Sopenharmony_ci	efx->mc_bist_for_other_fn = true;
12748c2ecf20Sopenharmony_ci	efx_mcdi_proxy_abort(mcdi);
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	if (efx_mcdi_complete_sync(mcdi)) {
12778c2ecf20Sopenharmony_ci		if (mcdi->mode == MCDI_MODE_EVENTS) {
12788c2ecf20Sopenharmony_ci			mcdi->resprc = -EIO;
12798c2ecf20Sopenharmony_ci			mcdi->resp_hdr_len = 0;
12808c2ecf20Sopenharmony_ci			mcdi->resp_data_len = 0;
12818c2ecf20Sopenharmony_ci			++mcdi->credits;
12828c2ecf20Sopenharmony_ci		}
12838c2ecf20Sopenharmony_ci	}
12848c2ecf20Sopenharmony_ci	mcdi->new_epoch = true;
12858c2ecf20Sopenharmony_ci	efx_schedule_reset(efx, RESET_TYPE_MC_BIST);
12868c2ecf20Sopenharmony_ci	spin_unlock(&mcdi->iface_lock);
12878c2ecf20Sopenharmony_ci}
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci/* MCDI timeouts seen, so make all MCDI calls fail-fast and issue an FLR to try
12908c2ecf20Sopenharmony_ci * to recover.
12918c2ecf20Sopenharmony_ci */
12928c2ecf20Sopenharmony_cistatic void efx_mcdi_abandon(struct efx_nic *efx)
12938c2ecf20Sopenharmony_ci{
12948c2ecf20Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci	if (xchg(&mcdi->mode, MCDI_MODE_FAIL) == MCDI_MODE_FAIL)
12978c2ecf20Sopenharmony_ci		return; /* it had already been done */
12988c2ecf20Sopenharmony_ci	netif_dbg(efx, hw, efx->net_dev, "MCDI is timing out; trying to recover\n");
12998c2ecf20Sopenharmony_ci	efx_schedule_reset(efx, RESET_TYPE_MCDI_TIMEOUT);
13008c2ecf20Sopenharmony_ci}
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_cistatic void efx_handle_drain_event(struct efx_nic *efx)
13038c2ecf20Sopenharmony_ci{
13048c2ecf20Sopenharmony_ci	if (atomic_dec_and_test(&efx->active_queues))
13058c2ecf20Sopenharmony_ci		wake_up(&efx->flush_wq);
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	WARN_ON(atomic_read(&efx->active_queues) < 0);
13088c2ecf20Sopenharmony_ci}
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci/* Called from efx_farch_ev_process and efx_ef10_ev_process for MCDI events */
13118c2ecf20Sopenharmony_civoid efx_mcdi_process_event(struct efx_channel *channel,
13128c2ecf20Sopenharmony_ci			    efx_qword_t *event)
13138c2ecf20Sopenharmony_ci{
13148c2ecf20Sopenharmony_ci	struct efx_nic *efx = channel->efx;
13158c2ecf20Sopenharmony_ci	int code = EFX_QWORD_FIELD(*event, MCDI_EVENT_CODE);
13168c2ecf20Sopenharmony_ci	u32 data = EFX_QWORD_FIELD(*event, MCDI_EVENT_DATA);
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	switch (code) {
13198c2ecf20Sopenharmony_ci	case MCDI_EVENT_CODE_BADSSERT:
13208c2ecf20Sopenharmony_ci		netif_err(efx, hw, efx->net_dev,
13218c2ecf20Sopenharmony_ci			  "MC watchdog or assertion failure at 0x%x\n", data);
13228c2ecf20Sopenharmony_ci		efx_mcdi_ev_death(efx, -EINTR);
13238c2ecf20Sopenharmony_ci		break;
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	case MCDI_EVENT_CODE_PMNOTICE:
13268c2ecf20Sopenharmony_ci		netif_info(efx, wol, efx->net_dev, "MCDI PM event.\n");
13278c2ecf20Sopenharmony_ci		break;
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci	case MCDI_EVENT_CODE_CMDDONE:
13308c2ecf20Sopenharmony_ci		efx_mcdi_ev_cpl(efx,
13318c2ecf20Sopenharmony_ci				MCDI_EVENT_FIELD(*event, CMDDONE_SEQ),
13328c2ecf20Sopenharmony_ci				MCDI_EVENT_FIELD(*event, CMDDONE_DATALEN),
13338c2ecf20Sopenharmony_ci				MCDI_EVENT_FIELD(*event, CMDDONE_ERRNO));
13348c2ecf20Sopenharmony_ci		break;
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci	case MCDI_EVENT_CODE_LINKCHANGE:
13378c2ecf20Sopenharmony_ci		efx_mcdi_process_link_change(efx, event);
13388c2ecf20Sopenharmony_ci		break;
13398c2ecf20Sopenharmony_ci	case MCDI_EVENT_CODE_SENSOREVT:
13408c2ecf20Sopenharmony_ci		efx_sensor_event(efx, event);
13418c2ecf20Sopenharmony_ci		break;
13428c2ecf20Sopenharmony_ci	case MCDI_EVENT_CODE_SCHEDERR:
13438c2ecf20Sopenharmony_ci		netif_dbg(efx, hw, efx->net_dev,
13448c2ecf20Sopenharmony_ci			  "MC Scheduler alert (0x%x)\n", data);
13458c2ecf20Sopenharmony_ci		break;
13468c2ecf20Sopenharmony_ci	case MCDI_EVENT_CODE_REBOOT:
13478c2ecf20Sopenharmony_ci	case MCDI_EVENT_CODE_MC_REBOOT:
13488c2ecf20Sopenharmony_ci		netif_info(efx, hw, efx->net_dev, "MC Reboot\n");
13498c2ecf20Sopenharmony_ci		efx_mcdi_ev_death(efx, -EIO);
13508c2ecf20Sopenharmony_ci		break;
13518c2ecf20Sopenharmony_ci	case MCDI_EVENT_CODE_MC_BIST:
13528c2ecf20Sopenharmony_ci		netif_info(efx, hw, efx->net_dev, "MC entered BIST mode\n");
13538c2ecf20Sopenharmony_ci		efx_mcdi_ev_bist(efx);
13548c2ecf20Sopenharmony_ci		break;
13558c2ecf20Sopenharmony_ci	case MCDI_EVENT_CODE_MAC_STATS_DMA:
13568c2ecf20Sopenharmony_ci		/* MAC stats are gather lazily.  We can ignore this. */
13578c2ecf20Sopenharmony_ci		break;
13588c2ecf20Sopenharmony_ci	case MCDI_EVENT_CODE_FLR:
13598c2ecf20Sopenharmony_ci		if (efx->type->sriov_flr)
13608c2ecf20Sopenharmony_ci			efx->type->sriov_flr(efx,
13618c2ecf20Sopenharmony_ci					     MCDI_EVENT_FIELD(*event, FLR_VF));
13628c2ecf20Sopenharmony_ci		break;
13638c2ecf20Sopenharmony_ci	case MCDI_EVENT_CODE_PTP_RX:
13648c2ecf20Sopenharmony_ci	case MCDI_EVENT_CODE_PTP_FAULT:
13658c2ecf20Sopenharmony_ci	case MCDI_EVENT_CODE_PTP_PPS:
13668c2ecf20Sopenharmony_ci		efx_ptp_event(efx, event);
13678c2ecf20Sopenharmony_ci		break;
13688c2ecf20Sopenharmony_ci	case MCDI_EVENT_CODE_PTP_TIME:
13698c2ecf20Sopenharmony_ci		efx_time_sync_event(channel, event);
13708c2ecf20Sopenharmony_ci		break;
13718c2ecf20Sopenharmony_ci	case MCDI_EVENT_CODE_TX_FLUSH:
13728c2ecf20Sopenharmony_ci	case MCDI_EVENT_CODE_RX_FLUSH:
13738c2ecf20Sopenharmony_ci		/* Two flush events will be sent: one to the same event
13748c2ecf20Sopenharmony_ci		 * queue as completions, and one to event queue 0.
13758c2ecf20Sopenharmony_ci		 * In the latter case the {RX,TX}_FLUSH_TO_DRIVER
13768c2ecf20Sopenharmony_ci		 * flag will be set, and we should ignore the event
13778c2ecf20Sopenharmony_ci		 * because we want to wait for all completions.
13788c2ecf20Sopenharmony_ci		 */
13798c2ecf20Sopenharmony_ci		BUILD_BUG_ON(MCDI_EVENT_TX_FLUSH_TO_DRIVER_LBN !=
13808c2ecf20Sopenharmony_ci			     MCDI_EVENT_RX_FLUSH_TO_DRIVER_LBN);
13818c2ecf20Sopenharmony_ci		if (!MCDI_EVENT_FIELD(*event, TX_FLUSH_TO_DRIVER))
13828c2ecf20Sopenharmony_ci			efx_handle_drain_event(efx);
13838c2ecf20Sopenharmony_ci		break;
13848c2ecf20Sopenharmony_ci	case MCDI_EVENT_CODE_TX_ERR:
13858c2ecf20Sopenharmony_ci	case MCDI_EVENT_CODE_RX_ERR:
13868c2ecf20Sopenharmony_ci		netif_err(efx, hw, efx->net_dev,
13878c2ecf20Sopenharmony_ci			  "%s DMA error (event: "EFX_QWORD_FMT")\n",
13888c2ecf20Sopenharmony_ci			  code == MCDI_EVENT_CODE_TX_ERR ? "TX" : "RX",
13898c2ecf20Sopenharmony_ci			  EFX_QWORD_VAL(*event));
13908c2ecf20Sopenharmony_ci		efx_schedule_reset(efx, RESET_TYPE_DMA_ERROR);
13918c2ecf20Sopenharmony_ci		break;
13928c2ecf20Sopenharmony_ci	case MCDI_EVENT_CODE_PROXY_RESPONSE:
13938c2ecf20Sopenharmony_ci		efx_mcdi_ev_proxy_response(efx,
13948c2ecf20Sopenharmony_ci				MCDI_EVENT_FIELD(*event, PROXY_RESPONSE_HANDLE),
13958c2ecf20Sopenharmony_ci				MCDI_EVENT_FIELD(*event, PROXY_RESPONSE_RC));
13968c2ecf20Sopenharmony_ci		break;
13978c2ecf20Sopenharmony_ci	default:
13988c2ecf20Sopenharmony_ci		netif_err(efx, hw, efx->net_dev,
13998c2ecf20Sopenharmony_ci			  "Unknown MCDI event " EFX_QWORD_FMT "\n",
14008c2ecf20Sopenharmony_ci			  EFX_QWORD_VAL(*event));
14018c2ecf20Sopenharmony_ci	}
14028c2ecf20Sopenharmony_ci}
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_ci/**************************************************************************
14058c2ecf20Sopenharmony_ci *
14068c2ecf20Sopenharmony_ci * Specific request functions
14078c2ecf20Sopenharmony_ci *
14088c2ecf20Sopenharmony_ci **************************************************************************
14098c2ecf20Sopenharmony_ci */
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_civoid efx_mcdi_print_fwver(struct efx_nic *efx, char *buf, size_t len)
14128c2ecf20Sopenharmony_ci{
14138c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_VERSION_OUT_LEN);
14148c2ecf20Sopenharmony_ci	size_t outlength;
14158c2ecf20Sopenharmony_ci	const __le16 *ver_words;
14168c2ecf20Sopenharmony_ci	size_t offset;
14178c2ecf20Sopenharmony_ci	int rc;
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_GET_VERSION_IN_LEN != 0);
14208c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc(efx, MC_CMD_GET_VERSION, NULL, 0,
14218c2ecf20Sopenharmony_ci			  outbuf, sizeof(outbuf), &outlength);
14228c2ecf20Sopenharmony_ci	if (rc)
14238c2ecf20Sopenharmony_ci		goto fail;
14248c2ecf20Sopenharmony_ci	if (outlength < MC_CMD_GET_VERSION_OUT_LEN) {
14258c2ecf20Sopenharmony_ci		rc = -EIO;
14268c2ecf20Sopenharmony_ci		goto fail;
14278c2ecf20Sopenharmony_ci	}
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	ver_words = (__le16 *)MCDI_PTR(outbuf, GET_VERSION_OUT_VERSION);
14308c2ecf20Sopenharmony_ci	offset = scnprintf(buf, len, "%u.%u.%u.%u",
14318c2ecf20Sopenharmony_ci			   le16_to_cpu(ver_words[0]),
14328c2ecf20Sopenharmony_ci			   le16_to_cpu(ver_words[1]),
14338c2ecf20Sopenharmony_ci			   le16_to_cpu(ver_words[2]),
14348c2ecf20Sopenharmony_ci			   le16_to_cpu(ver_words[3]));
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci	if (efx->type->print_additional_fwver)
14378c2ecf20Sopenharmony_ci		offset += efx->type->print_additional_fwver(efx, buf + offset,
14388c2ecf20Sopenharmony_ci							    len - offset);
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_ci	/* It's theoretically possible for the string to exceed 31
14418c2ecf20Sopenharmony_ci	 * characters, though in practice the first three version
14428c2ecf20Sopenharmony_ci	 * components are short enough that this doesn't happen.
14438c2ecf20Sopenharmony_ci	 */
14448c2ecf20Sopenharmony_ci	if (WARN_ON(offset >= len))
14458c2ecf20Sopenharmony_ci		buf[0] = 0;
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci	return;
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_cifail:
14508c2ecf20Sopenharmony_ci	netif_err(efx, probe, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
14518c2ecf20Sopenharmony_ci	buf[0] = 0;
14528c2ecf20Sopenharmony_ci}
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_cistatic int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating,
14558c2ecf20Sopenharmony_ci			       bool *was_attached)
14568c2ecf20Sopenharmony_ci{
14578c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_DRV_ATTACH_IN_LEN);
14588c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_DRV_ATTACH_EXT_OUT_LEN);
14598c2ecf20Sopenharmony_ci	size_t outlen;
14608c2ecf20Sopenharmony_ci	int rc;
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, DRV_ATTACH_IN_NEW_STATE,
14638c2ecf20Sopenharmony_ci		       driver_operating ? 1 : 0);
14648c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, DRV_ATTACH_IN_UPDATE, 1);
14658c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, DRV_ATTACH_IN_FIRMWARE_ID, MC_CMD_FW_LOW_LATENCY);
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc_quiet(efx, MC_CMD_DRV_ATTACH, inbuf, sizeof(inbuf),
14688c2ecf20Sopenharmony_ci				outbuf, sizeof(outbuf), &outlen);
14698c2ecf20Sopenharmony_ci	/* If we're not the primary PF, trying to ATTACH with a FIRMWARE_ID
14708c2ecf20Sopenharmony_ci	 * specified will fail with EPERM, and we have to tell the MC we don't
14718c2ecf20Sopenharmony_ci	 * care what firmware we get.
14728c2ecf20Sopenharmony_ci	 */
14738c2ecf20Sopenharmony_ci	if (rc == -EPERM) {
14748c2ecf20Sopenharmony_ci		netif_dbg(efx, probe, efx->net_dev,
14758c2ecf20Sopenharmony_ci			  "efx_mcdi_drv_attach with fw-variant setting failed EPERM, trying without it\n");
14768c2ecf20Sopenharmony_ci		MCDI_SET_DWORD(inbuf, DRV_ATTACH_IN_FIRMWARE_ID,
14778c2ecf20Sopenharmony_ci			       MC_CMD_FW_DONT_CARE);
14788c2ecf20Sopenharmony_ci		rc = efx_mcdi_rpc_quiet(efx, MC_CMD_DRV_ATTACH, inbuf,
14798c2ecf20Sopenharmony_ci					sizeof(inbuf), outbuf, sizeof(outbuf),
14808c2ecf20Sopenharmony_ci					&outlen);
14818c2ecf20Sopenharmony_ci	}
14828c2ecf20Sopenharmony_ci	if (rc) {
14838c2ecf20Sopenharmony_ci		efx_mcdi_display_error(efx, MC_CMD_DRV_ATTACH, sizeof(inbuf),
14848c2ecf20Sopenharmony_ci				       outbuf, outlen, rc);
14858c2ecf20Sopenharmony_ci		goto fail;
14868c2ecf20Sopenharmony_ci	}
14878c2ecf20Sopenharmony_ci	if (outlen < MC_CMD_DRV_ATTACH_OUT_LEN) {
14888c2ecf20Sopenharmony_ci		rc = -EIO;
14898c2ecf20Sopenharmony_ci		goto fail;
14908c2ecf20Sopenharmony_ci	}
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci	if (driver_operating) {
14938c2ecf20Sopenharmony_ci		if (outlen >= MC_CMD_DRV_ATTACH_EXT_OUT_LEN) {
14948c2ecf20Sopenharmony_ci			efx->mcdi->fn_flags =
14958c2ecf20Sopenharmony_ci				MCDI_DWORD(outbuf,
14968c2ecf20Sopenharmony_ci					   DRV_ATTACH_EXT_OUT_FUNC_FLAGS);
14978c2ecf20Sopenharmony_ci		} else {
14988c2ecf20Sopenharmony_ci			/* Synthesise flags for Siena */
14998c2ecf20Sopenharmony_ci			efx->mcdi->fn_flags =
15008c2ecf20Sopenharmony_ci				1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_LINKCTRL |
15018c2ecf20Sopenharmony_ci				1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_TRUSTED |
15028c2ecf20Sopenharmony_ci				(efx_port_num(efx) == 0) <<
15038c2ecf20Sopenharmony_ci				MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_PRIMARY;
15048c2ecf20Sopenharmony_ci		}
15058c2ecf20Sopenharmony_ci	}
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	/* We currently assume we have control of the external link
15088c2ecf20Sopenharmony_ci	 * and are completely trusted by firmware.  Abort probing
15098c2ecf20Sopenharmony_ci	 * if that's not true for this function.
15108c2ecf20Sopenharmony_ci	 */
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	if (was_attached != NULL)
15138c2ecf20Sopenharmony_ci		*was_attached = MCDI_DWORD(outbuf, DRV_ATTACH_OUT_OLD_STATE);
15148c2ecf20Sopenharmony_ci	return 0;
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_cifail:
15178c2ecf20Sopenharmony_ci	netif_err(efx, probe, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
15188c2ecf20Sopenharmony_ci	return rc;
15198c2ecf20Sopenharmony_ci}
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_ciint efx_mcdi_get_board_cfg(struct efx_nic *efx, u8 *mac_address,
15228c2ecf20Sopenharmony_ci			   u16 *fw_subtype_list, u32 *capabilities)
15238c2ecf20Sopenharmony_ci{
15248c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_BOARD_CFG_OUT_LENMAX);
15258c2ecf20Sopenharmony_ci	size_t outlen, i;
15268c2ecf20Sopenharmony_ci	int port_num = efx_port_num(efx);
15278c2ecf20Sopenharmony_ci	int rc;
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_GET_BOARD_CFG_IN_LEN != 0);
15308c2ecf20Sopenharmony_ci	/* we need __aligned(2) for ether_addr_copy */
15318c2ecf20Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0_OFST & 1);
15328c2ecf20Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1_OFST & 1);
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc(efx, MC_CMD_GET_BOARD_CFG, NULL, 0,
15358c2ecf20Sopenharmony_ci			  outbuf, sizeof(outbuf), &outlen);
15368c2ecf20Sopenharmony_ci	if (rc)
15378c2ecf20Sopenharmony_ci		goto fail;
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci	if (outlen < MC_CMD_GET_BOARD_CFG_OUT_LENMIN) {
15408c2ecf20Sopenharmony_ci		rc = -EIO;
15418c2ecf20Sopenharmony_ci		goto fail;
15428c2ecf20Sopenharmony_ci	}
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci	if (mac_address)
15458c2ecf20Sopenharmony_ci		ether_addr_copy(mac_address,
15468c2ecf20Sopenharmony_ci				port_num ?
15478c2ecf20Sopenharmony_ci				MCDI_PTR(outbuf, GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1) :
15488c2ecf20Sopenharmony_ci				MCDI_PTR(outbuf, GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0));
15498c2ecf20Sopenharmony_ci	if (fw_subtype_list) {
15508c2ecf20Sopenharmony_ci		for (i = 0;
15518c2ecf20Sopenharmony_ci		     i < MCDI_VAR_ARRAY_LEN(outlen,
15528c2ecf20Sopenharmony_ci					    GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST);
15538c2ecf20Sopenharmony_ci		     i++)
15548c2ecf20Sopenharmony_ci			fw_subtype_list[i] = MCDI_ARRAY_WORD(
15558c2ecf20Sopenharmony_ci				outbuf, GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST, i);
15568c2ecf20Sopenharmony_ci		for (; i < MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_MAXNUM; i++)
15578c2ecf20Sopenharmony_ci			fw_subtype_list[i] = 0;
15588c2ecf20Sopenharmony_ci	}
15598c2ecf20Sopenharmony_ci	if (capabilities) {
15608c2ecf20Sopenharmony_ci		if (port_num)
15618c2ecf20Sopenharmony_ci			*capabilities = MCDI_DWORD(outbuf,
15628c2ecf20Sopenharmony_ci					GET_BOARD_CFG_OUT_CAPABILITIES_PORT1);
15638c2ecf20Sopenharmony_ci		else
15648c2ecf20Sopenharmony_ci			*capabilities = MCDI_DWORD(outbuf,
15658c2ecf20Sopenharmony_ci					GET_BOARD_CFG_OUT_CAPABILITIES_PORT0);
15668c2ecf20Sopenharmony_ci	}
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_ci	return 0;
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_cifail:
15718c2ecf20Sopenharmony_ci	netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d len=%d\n",
15728c2ecf20Sopenharmony_ci		  __func__, rc, (int)outlen);
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci	return rc;
15758c2ecf20Sopenharmony_ci}
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ciint efx_mcdi_log_ctrl(struct efx_nic *efx, bool evq, bool uart, u32 dest_evq)
15788c2ecf20Sopenharmony_ci{
15798c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_LOG_CTRL_IN_LEN);
15808c2ecf20Sopenharmony_ci	u32 dest = 0;
15818c2ecf20Sopenharmony_ci	int rc;
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci	if (uart)
15848c2ecf20Sopenharmony_ci		dest |= MC_CMD_LOG_CTRL_IN_LOG_DEST_UART;
15858c2ecf20Sopenharmony_ci	if (evq)
15868c2ecf20Sopenharmony_ci		dest |= MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ;
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, LOG_CTRL_IN_LOG_DEST, dest);
15898c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, LOG_CTRL_IN_LOG_DEST_EVQ, dest_evq);
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_LOG_CTRL_OUT_LEN != 0);
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc(efx, MC_CMD_LOG_CTRL, inbuf, sizeof(inbuf),
15948c2ecf20Sopenharmony_ci			  NULL, 0, NULL);
15958c2ecf20Sopenharmony_ci	return rc;
15968c2ecf20Sopenharmony_ci}
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_ciint efx_mcdi_nvram_types(struct efx_nic *efx, u32 *nvram_types_out)
15998c2ecf20Sopenharmony_ci{
16008c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_TYPES_OUT_LEN);
16018c2ecf20Sopenharmony_ci	size_t outlen;
16028c2ecf20Sopenharmony_ci	int rc;
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_NVRAM_TYPES_IN_LEN != 0);
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_TYPES, NULL, 0,
16078c2ecf20Sopenharmony_ci			  outbuf, sizeof(outbuf), &outlen);
16088c2ecf20Sopenharmony_ci	if (rc)
16098c2ecf20Sopenharmony_ci		goto fail;
16108c2ecf20Sopenharmony_ci	if (outlen < MC_CMD_NVRAM_TYPES_OUT_LEN) {
16118c2ecf20Sopenharmony_ci		rc = -EIO;
16128c2ecf20Sopenharmony_ci		goto fail;
16138c2ecf20Sopenharmony_ci	}
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci	*nvram_types_out = MCDI_DWORD(outbuf, NVRAM_TYPES_OUT_TYPES);
16168c2ecf20Sopenharmony_ci	return 0;
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_cifail:
16198c2ecf20Sopenharmony_ci	netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n",
16208c2ecf20Sopenharmony_ci		  __func__, rc);
16218c2ecf20Sopenharmony_ci	return rc;
16228c2ecf20Sopenharmony_ci}
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci/* This function finds types using the new NVRAM_PARTITIONS mcdi. */
16258c2ecf20Sopenharmony_cistatic int efx_new_mcdi_nvram_types(struct efx_nic *efx, u32 *number,
16268c2ecf20Sopenharmony_ci				    u32 *nvram_types)
16278c2ecf20Sopenharmony_ci{
16288c2ecf20Sopenharmony_ci	efx_dword_t *outbuf = kzalloc(MC_CMD_NVRAM_PARTITIONS_OUT_LENMAX_MCDI2,
16298c2ecf20Sopenharmony_ci				      GFP_KERNEL);
16308c2ecf20Sopenharmony_ci	size_t outlen;
16318c2ecf20Sopenharmony_ci	int rc;
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_ci	if (!outbuf)
16348c2ecf20Sopenharmony_ci		return -ENOMEM;
16358c2ecf20Sopenharmony_ci
16368c2ecf20Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_NVRAM_PARTITIONS_IN_LEN != 0);
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_PARTITIONS, NULL, 0,
16398c2ecf20Sopenharmony_ci			  outbuf, MC_CMD_NVRAM_PARTITIONS_OUT_LENMAX_MCDI2, &outlen);
16408c2ecf20Sopenharmony_ci	if (rc)
16418c2ecf20Sopenharmony_ci		goto fail;
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	*number = MCDI_DWORD(outbuf, NVRAM_PARTITIONS_OUT_NUM_PARTITIONS);
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_ci	memcpy(nvram_types, MCDI_PTR(outbuf, NVRAM_PARTITIONS_OUT_TYPE_ID),
16468c2ecf20Sopenharmony_ci	       *number * sizeof(u32));
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_cifail:
16498c2ecf20Sopenharmony_ci	kfree(outbuf);
16508c2ecf20Sopenharmony_ci	return rc;
16518c2ecf20Sopenharmony_ci}
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_ciint efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type,
16548c2ecf20Sopenharmony_ci			size_t *size_out, size_t *erase_size_out,
16558c2ecf20Sopenharmony_ci			bool *protected_out)
16568c2ecf20Sopenharmony_ci{
16578c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_INFO_IN_LEN);
16588c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_INFO_OUT_LEN);
16598c2ecf20Sopenharmony_ci	size_t outlen;
16608c2ecf20Sopenharmony_ci	int rc;
16618c2ecf20Sopenharmony_ci
16628c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_INFO_IN_TYPE, type);
16638c2ecf20Sopenharmony_ci
16648c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_INFO, inbuf, sizeof(inbuf),
16658c2ecf20Sopenharmony_ci			  outbuf, sizeof(outbuf), &outlen);
16668c2ecf20Sopenharmony_ci	if (rc)
16678c2ecf20Sopenharmony_ci		goto fail;
16688c2ecf20Sopenharmony_ci	if (outlen < MC_CMD_NVRAM_INFO_OUT_LEN) {
16698c2ecf20Sopenharmony_ci		rc = -EIO;
16708c2ecf20Sopenharmony_ci		goto fail;
16718c2ecf20Sopenharmony_ci	}
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci	*size_out = MCDI_DWORD(outbuf, NVRAM_INFO_OUT_SIZE);
16748c2ecf20Sopenharmony_ci	*erase_size_out = MCDI_DWORD(outbuf, NVRAM_INFO_OUT_ERASESIZE);
16758c2ecf20Sopenharmony_ci	*protected_out = !!(MCDI_DWORD(outbuf, NVRAM_INFO_OUT_FLAGS) &
16768c2ecf20Sopenharmony_ci				(1 << MC_CMD_NVRAM_INFO_OUT_PROTECTED_LBN));
16778c2ecf20Sopenharmony_ci	return 0;
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_cifail:
16808c2ecf20Sopenharmony_ci	netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
16818c2ecf20Sopenharmony_ci	return rc;
16828c2ecf20Sopenharmony_ci}
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_cistatic int efx_mcdi_nvram_test(struct efx_nic *efx, unsigned int type)
16858c2ecf20Sopenharmony_ci{
16868c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_TEST_IN_LEN);
16878c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_TEST_OUT_LEN);
16888c2ecf20Sopenharmony_ci	int rc;
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_TEST_IN_TYPE, type);
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_TEST, inbuf, sizeof(inbuf),
16938c2ecf20Sopenharmony_ci			  outbuf, sizeof(outbuf), NULL);
16948c2ecf20Sopenharmony_ci	if (rc)
16958c2ecf20Sopenharmony_ci		return rc;
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ci	switch (MCDI_DWORD(outbuf, NVRAM_TEST_OUT_RESULT)) {
16988c2ecf20Sopenharmony_ci	case MC_CMD_NVRAM_TEST_PASS:
16998c2ecf20Sopenharmony_ci	case MC_CMD_NVRAM_TEST_NOTSUPP:
17008c2ecf20Sopenharmony_ci		return 0;
17018c2ecf20Sopenharmony_ci	default:
17028c2ecf20Sopenharmony_ci		return -EIO;
17038c2ecf20Sopenharmony_ci	}
17048c2ecf20Sopenharmony_ci}
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_ci/* This function tests nvram partitions using the new mcdi partition lookup scheme */
17078c2ecf20Sopenharmony_ciint efx_new_mcdi_nvram_test_all(struct efx_nic *efx)
17088c2ecf20Sopenharmony_ci{
17098c2ecf20Sopenharmony_ci	u32 *nvram_types = kzalloc(MC_CMD_NVRAM_PARTITIONS_OUT_LENMAX_MCDI2,
17108c2ecf20Sopenharmony_ci				   GFP_KERNEL);
17118c2ecf20Sopenharmony_ci	unsigned int number;
17128c2ecf20Sopenharmony_ci	int rc, i;
17138c2ecf20Sopenharmony_ci
17148c2ecf20Sopenharmony_ci	if (!nvram_types)
17158c2ecf20Sopenharmony_ci		return -ENOMEM;
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci	rc = efx_new_mcdi_nvram_types(efx, &number, nvram_types);
17188c2ecf20Sopenharmony_ci	if (rc)
17198c2ecf20Sopenharmony_ci		goto fail;
17208c2ecf20Sopenharmony_ci
17218c2ecf20Sopenharmony_ci	/* Require at least one check */
17228c2ecf20Sopenharmony_ci	rc = -EAGAIN;
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci	for (i = 0; i < number; i++) {
17258c2ecf20Sopenharmony_ci		if (nvram_types[i] == NVRAM_PARTITION_TYPE_PARTITION_MAP ||
17268c2ecf20Sopenharmony_ci		    nvram_types[i] == NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG)
17278c2ecf20Sopenharmony_ci			continue;
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci		rc = efx_mcdi_nvram_test(efx, nvram_types[i]);
17308c2ecf20Sopenharmony_ci		if (rc)
17318c2ecf20Sopenharmony_ci			goto fail;
17328c2ecf20Sopenharmony_ci	}
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_cifail:
17358c2ecf20Sopenharmony_ci	kfree(nvram_types);
17368c2ecf20Sopenharmony_ci	return rc;
17378c2ecf20Sopenharmony_ci}
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_ciint efx_mcdi_nvram_test_all(struct efx_nic *efx)
17408c2ecf20Sopenharmony_ci{
17418c2ecf20Sopenharmony_ci	u32 nvram_types;
17428c2ecf20Sopenharmony_ci	unsigned int type;
17438c2ecf20Sopenharmony_ci	int rc;
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	rc = efx_mcdi_nvram_types(efx, &nvram_types);
17468c2ecf20Sopenharmony_ci	if (rc)
17478c2ecf20Sopenharmony_ci		goto fail1;
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci	type = 0;
17508c2ecf20Sopenharmony_ci	while (nvram_types != 0) {
17518c2ecf20Sopenharmony_ci		if (nvram_types & 1) {
17528c2ecf20Sopenharmony_ci			rc = efx_mcdi_nvram_test(efx, type);
17538c2ecf20Sopenharmony_ci			if (rc)
17548c2ecf20Sopenharmony_ci				goto fail2;
17558c2ecf20Sopenharmony_ci		}
17568c2ecf20Sopenharmony_ci		type++;
17578c2ecf20Sopenharmony_ci		nvram_types >>= 1;
17588c2ecf20Sopenharmony_ci	}
17598c2ecf20Sopenharmony_ci
17608c2ecf20Sopenharmony_ci	return 0;
17618c2ecf20Sopenharmony_ci
17628c2ecf20Sopenharmony_cifail2:
17638c2ecf20Sopenharmony_ci	netif_err(efx, hw, efx->net_dev, "%s: failed type=%u\n",
17648c2ecf20Sopenharmony_ci		  __func__, type);
17658c2ecf20Sopenharmony_cifail1:
17668c2ecf20Sopenharmony_ci	netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
17678c2ecf20Sopenharmony_ci	return rc;
17688c2ecf20Sopenharmony_ci}
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci/* Returns 1 if an assertion was read, 0 if no assertion had fired,
17718c2ecf20Sopenharmony_ci * negative on error.
17728c2ecf20Sopenharmony_ci */
17738c2ecf20Sopenharmony_cistatic int efx_mcdi_read_assertion(struct efx_nic *efx)
17748c2ecf20Sopenharmony_ci{
17758c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_GET_ASSERTS_IN_LEN);
17768c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_ASSERTS_OUT_LEN);
17778c2ecf20Sopenharmony_ci	unsigned int flags, index;
17788c2ecf20Sopenharmony_ci	const char *reason;
17798c2ecf20Sopenharmony_ci	size_t outlen;
17808c2ecf20Sopenharmony_ci	int retry;
17818c2ecf20Sopenharmony_ci	int rc;
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_ci	/* Attempt to read any stored assertion state before we reboot
17848c2ecf20Sopenharmony_ci	 * the mcfw out of the assertion handler. Retry twice, once
17858c2ecf20Sopenharmony_ci	 * because a boot-time assertion might cause this command to fail
17868c2ecf20Sopenharmony_ci	 * with EINTR. And once again because GET_ASSERTS can race with
17878c2ecf20Sopenharmony_ci	 * MC_CMD_REBOOT running on the other port. */
17888c2ecf20Sopenharmony_ci	retry = 2;
17898c2ecf20Sopenharmony_ci	do {
17908c2ecf20Sopenharmony_ci		MCDI_SET_DWORD(inbuf, GET_ASSERTS_IN_CLEAR, 1);
17918c2ecf20Sopenharmony_ci		rc = efx_mcdi_rpc_quiet(efx, MC_CMD_GET_ASSERTS,
17928c2ecf20Sopenharmony_ci					inbuf, MC_CMD_GET_ASSERTS_IN_LEN,
17938c2ecf20Sopenharmony_ci					outbuf, sizeof(outbuf), &outlen);
17948c2ecf20Sopenharmony_ci		if (rc == -EPERM)
17958c2ecf20Sopenharmony_ci			return 0;
17968c2ecf20Sopenharmony_ci	} while ((rc == -EINTR || rc == -EIO) && retry-- > 0);
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_ci	if (rc) {
17998c2ecf20Sopenharmony_ci		efx_mcdi_display_error(efx, MC_CMD_GET_ASSERTS,
18008c2ecf20Sopenharmony_ci				       MC_CMD_GET_ASSERTS_IN_LEN, outbuf,
18018c2ecf20Sopenharmony_ci				       outlen, rc);
18028c2ecf20Sopenharmony_ci		return rc;
18038c2ecf20Sopenharmony_ci	}
18048c2ecf20Sopenharmony_ci	if (outlen < MC_CMD_GET_ASSERTS_OUT_LEN)
18058c2ecf20Sopenharmony_ci		return -EIO;
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ci	/* Print out any recorded assertion state */
18088c2ecf20Sopenharmony_ci	flags = MCDI_DWORD(outbuf, GET_ASSERTS_OUT_GLOBAL_FLAGS);
18098c2ecf20Sopenharmony_ci	if (flags == MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS)
18108c2ecf20Sopenharmony_ci		return 0;
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_ci	reason = (flags == MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL)
18138c2ecf20Sopenharmony_ci		? "system-level assertion"
18148c2ecf20Sopenharmony_ci		: (flags == MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL)
18158c2ecf20Sopenharmony_ci		? "thread-level assertion"
18168c2ecf20Sopenharmony_ci		: (flags == MC_CMD_GET_ASSERTS_FLAGS_WDOG_FIRED)
18178c2ecf20Sopenharmony_ci		? "watchdog reset"
18188c2ecf20Sopenharmony_ci		: "unknown assertion";
18198c2ecf20Sopenharmony_ci	netif_err(efx, hw, efx->net_dev,
18208c2ecf20Sopenharmony_ci		  "MCPU %s at PC = 0x%.8x in thread 0x%.8x\n", reason,
18218c2ecf20Sopenharmony_ci		  MCDI_DWORD(outbuf, GET_ASSERTS_OUT_SAVED_PC_OFFS),
18228c2ecf20Sopenharmony_ci		  MCDI_DWORD(outbuf, GET_ASSERTS_OUT_THREAD_OFFS));
18238c2ecf20Sopenharmony_ci
18248c2ecf20Sopenharmony_ci	/* Print out the registers */
18258c2ecf20Sopenharmony_ci	for (index = 0;
18268c2ecf20Sopenharmony_ci	     index < MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_NUM;
18278c2ecf20Sopenharmony_ci	     index++)
18288c2ecf20Sopenharmony_ci		netif_err(efx, hw, efx->net_dev, "R%.2d (?): 0x%.8x\n",
18298c2ecf20Sopenharmony_ci			  1 + index,
18308c2ecf20Sopenharmony_ci			  MCDI_ARRAY_DWORD(outbuf, GET_ASSERTS_OUT_GP_REGS_OFFS,
18318c2ecf20Sopenharmony_ci					   index));
18328c2ecf20Sopenharmony_ci
18338c2ecf20Sopenharmony_ci	return 1;
18348c2ecf20Sopenharmony_ci}
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_cistatic int efx_mcdi_exit_assertion(struct efx_nic *efx)
18378c2ecf20Sopenharmony_ci{
18388c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_REBOOT_IN_LEN);
18398c2ecf20Sopenharmony_ci	int rc;
18408c2ecf20Sopenharmony_ci
18418c2ecf20Sopenharmony_ci	/* If the MC is running debug firmware, it might now be
18428c2ecf20Sopenharmony_ci	 * waiting for a debugger to attach, but we just want it to
18438c2ecf20Sopenharmony_ci	 * reboot.  We set a flag that makes the command a no-op if it
18448c2ecf20Sopenharmony_ci	 * has already done so.
18458c2ecf20Sopenharmony_ci	 * The MCDI will thus return either 0 or -EIO.
18468c2ecf20Sopenharmony_ci	 */
18478c2ecf20Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_REBOOT_OUT_LEN != 0);
18488c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, REBOOT_IN_FLAGS,
18498c2ecf20Sopenharmony_ci		       MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION);
18508c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc_quiet(efx, MC_CMD_REBOOT, inbuf, MC_CMD_REBOOT_IN_LEN,
18518c2ecf20Sopenharmony_ci				NULL, 0, NULL);
18528c2ecf20Sopenharmony_ci	if (rc == -EIO)
18538c2ecf20Sopenharmony_ci		rc = 0;
18548c2ecf20Sopenharmony_ci	if (rc)
18558c2ecf20Sopenharmony_ci		efx_mcdi_display_error(efx, MC_CMD_REBOOT, MC_CMD_REBOOT_IN_LEN,
18568c2ecf20Sopenharmony_ci				       NULL, 0, rc);
18578c2ecf20Sopenharmony_ci	return rc;
18588c2ecf20Sopenharmony_ci}
18598c2ecf20Sopenharmony_ci
18608c2ecf20Sopenharmony_ciint efx_mcdi_handle_assertion(struct efx_nic *efx)
18618c2ecf20Sopenharmony_ci{
18628c2ecf20Sopenharmony_ci	int rc;
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci	rc = efx_mcdi_read_assertion(efx);
18658c2ecf20Sopenharmony_ci	if (rc <= 0)
18668c2ecf20Sopenharmony_ci		return rc;
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci	return efx_mcdi_exit_assertion(efx);
18698c2ecf20Sopenharmony_ci}
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ciint efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode)
18728c2ecf20Sopenharmony_ci{
18738c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_SET_ID_LED_IN_LEN);
18748c2ecf20Sopenharmony_ci
18758c2ecf20Sopenharmony_ci	BUILD_BUG_ON(EFX_LED_OFF != MC_CMD_LED_OFF);
18768c2ecf20Sopenharmony_ci	BUILD_BUG_ON(EFX_LED_ON != MC_CMD_LED_ON);
18778c2ecf20Sopenharmony_ci	BUILD_BUG_ON(EFX_LED_DEFAULT != MC_CMD_LED_DEFAULT);
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_SET_ID_LED_OUT_LEN != 0);
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, SET_ID_LED_IN_STATE, mode);
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_ci	return efx_mcdi_rpc(efx, MC_CMD_SET_ID_LED, inbuf, sizeof(inbuf), NULL, 0, NULL);
18848c2ecf20Sopenharmony_ci}
18858c2ecf20Sopenharmony_ci
18868c2ecf20Sopenharmony_cistatic int efx_mcdi_reset_func(struct efx_nic *efx)
18878c2ecf20Sopenharmony_ci{
18888c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_ENTITY_RESET_IN_LEN);
18898c2ecf20Sopenharmony_ci	int rc;
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_ENTITY_RESET_OUT_LEN != 0);
18928c2ecf20Sopenharmony_ci	MCDI_POPULATE_DWORD_1(inbuf, ENTITY_RESET_IN_FLAG,
18938c2ecf20Sopenharmony_ci			      ENTITY_RESET_IN_FUNCTION_RESOURCE_RESET, 1);
18948c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc(efx, MC_CMD_ENTITY_RESET, inbuf, sizeof(inbuf),
18958c2ecf20Sopenharmony_ci			  NULL, 0, NULL);
18968c2ecf20Sopenharmony_ci	return rc;
18978c2ecf20Sopenharmony_ci}
18988c2ecf20Sopenharmony_ci
18998c2ecf20Sopenharmony_cistatic int efx_mcdi_reset_mc(struct efx_nic *efx)
19008c2ecf20Sopenharmony_ci{
19018c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_REBOOT_IN_LEN);
19028c2ecf20Sopenharmony_ci	int rc;
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_REBOOT_OUT_LEN != 0);
19058c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, REBOOT_IN_FLAGS, 0);
19068c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc(efx, MC_CMD_REBOOT, inbuf, sizeof(inbuf),
19078c2ecf20Sopenharmony_ci			  NULL, 0, NULL);
19088c2ecf20Sopenharmony_ci	/* White is black, and up is down */
19098c2ecf20Sopenharmony_ci	if (rc == -EIO)
19108c2ecf20Sopenharmony_ci		return 0;
19118c2ecf20Sopenharmony_ci	if (rc == 0)
19128c2ecf20Sopenharmony_ci		rc = -EIO;
19138c2ecf20Sopenharmony_ci	return rc;
19148c2ecf20Sopenharmony_ci}
19158c2ecf20Sopenharmony_ci
19168c2ecf20Sopenharmony_cienum reset_type efx_mcdi_map_reset_reason(enum reset_type reason)
19178c2ecf20Sopenharmony_ci{
19188c2ecf20Sopenharmony_ci	return RESET_TYPE_RECOVER_OR_ALL;
19198c2ecf20Sopenharmony_ci}
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_ciint efx_mcdi_reset(struct efx_nic *efx, enum reset_type method)
19228c2ecf20Sopenharmony_ci{
19238c2ecf20Sopenharmony_ci	int rc;
19248c2ecf20Sopenharmony_ci
19258c2ecf20Sopenharmony_ci	/* If MCDI is down, we can't handle_assertion */
19268c2ecf20Sopenharmony_ci	if (method == RESET_TYPE_MCDI_TIMEOUT) {
19278c2ecf20Sopenharmony_ci		rc = pci_reset_function(efx->pci_dev);
19288c2ecf20Sopenharmony_ci		if (rc)
19298c2ecf20Sopenharmony_ci			return rc;
19308c2ecf20Sopenharmony_ci		/* Re-enable polled MCDI completion */
19318c2ecf20Sopenharmony_ci		if (efx->mcdi) {
19328c2ecf20Sopenharmony_ci			struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
19338c2ecf20Sopenharmony_ci			mcdi->mode = MCDI_MODE_POLL;
19348c2ecf20Sopenharmony_ci		}
19358c2ecf20Sopenharmony_ci		return 0;
19368c2ecf20Sopenharmony_ci	}
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ci	/* Recover from a failed assertion pre-reset */
19398c2ecf20Sopenharmony_ci	rc = efx_mcdi_handle_assertion(efx);
19408c2ecf20Sopenharmony_ci	if (rc)
19418c2ecf20Sopenharmony_ci		return rc;
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ci	if (method == RESET_TYPE_DATAPATH)
19448c2ecf20Sopenharmony_ci		return 0;
19458c2ecf20Sopenharmony_ci	else if (method == RESET_TYPE_WORLD)
19468c2ecf20Sopenharmony_ci		return efx_mcdi_reset_mc(efx);
19478c2ecf20Sopenharmony_ci	else
19488c2ecf20Sopenharmony_ci		return efx_mcdi_reset_func(efx);
19498c2ecf20Sopenharmony_ci}
19508c2ecf20Sopenharmony_ci
19518c2ecf20Sopenharmony_cistatic int efx_mcdi_wol_filter_set(struct efx_nic *efx, u32 type,
19528c2ecf20Sopenharmony_ci				   const u8 *mac, int *id_out)
19538c2ecf20Sopenharmony_ci{
19548c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_WOL_FILTER_SET_IN_LEN);
19558c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_WOL_FILTER_SET_OUT_LEN);
19568c2ecf20Sopenharmony_ci	size_t outlen;
19578c2ecf20Sopenharmony_ci	int rc;
19588c2ecf20Sopenharmony_ci
19598c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, WOL_FILTER_SET_IN_WOL_TYPE, type);
19608c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, WOL_FILTER_SET_IN_FILTER_MODE,
19618c2ecf20Sopenharmony_ci		       MC_CMD_FILTER_MODE_SIMPLE);
19628c2ecf20Sopenharmony_ci	ether_addr_copy(MCDI_PTR(inbuf, WOL_FILTER_SET_IN_MAGIC_MAC), mac);
19638c2ecf20Sopenharmony_ci
19648c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_SET, inbuf, sizeof(inbuf),
19658c2ecf20Sopenharmony_ci			  outbuf, sizeof(outbuf), &outlen);
19668c2ecf20Sopenharmony_ci	if (rc)
19678c2ecf20Sopenharmony_ci		goto fail;
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_ci	if (outlen < MC_CMD_WOL_FILTER_SET_OUT_LEN) {
19708c2ecf20Sopenharmony_ci		rc = -EIO;
19718c2ecf20Sopenharmony_ci		goto fail;
19728c2ecf20Sopenharmony_ci	}
19738c2ecf20Sopenharmony_ci
19748c2ecf20Sopenharmony_ci	*id_out = (int)MCDI_DWORD(outbuf, WOL_FILTER_SET_OUT_FILTER_ID);
19758c2ecf20Sopenharmony_ci
19768c2ecf20Sopenharmony_ci	return 0;
19778c2ecf20Sopenharmony_ci
19788c2ecf20Sopenharmony_cifail:
19798c2ecf20Sopenharmony_ci	*id_out = -1;
19808c2ecf20Sopenharmony_ci	netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
19818c2ecf20Sopenharmony_ci	return rc;
19828c2ecf20Sopenharmony_ci
19838c2ecf20Sopenharmony_ci}
19848c2ecf20Sopenharmony_ci
19858c2ecf20Sopenharmony_ci
19868c2ecf20Sopenharmony_ciint
19878c2ecf20Sopenharmony_ciefx_mcdi_wol_filter_set_magic(struct efx_nic *efx,  const u8 *mac, int *id_out)
19888c2ecf20Sopenharmony_ci{
19898c2ecf20Sopenharmony_ci	return efx_mcdi_wol_filter_set(efx, MC_CMD_WOL_TYPE_MAGIC, mac, id_out);
19908c2ecf20Sopenharmony_ci}
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_ci
19938c2ecf20Sopenharmony_ciint efx_mcdi_wol_filter_get_magic(struct efx_nic *efx, int *id_out)
19948c2ecf20Sopenharmony_ci{
19958c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_WOL_FILTER_GET_OUT_LEN);
19968c2ecf20Sopenharmony_ci	size_t outlen;
19978c2ecf20Sopenharmony_ci	int rc;
19988c2ecf20Sopenharmony_ci
19998c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_GET, NULL, 0,
20008c2ecf20Sopenharmony_ci			  outbuf, sizeof(outbuf), &outlen);
20018c2ecf20Sopenharmony_ci	if (rc)
20028c2ecf20Sopenharmony_ci		goto fail;
20038c2ecf20Sopenharmony_ci
20048c2ecf20Sopenharmony_ci	if (outlen < MC_CMD_WOL_FILTER_GET_OUT_LEN) {
20058c2ecf20Sopenharmony_ci		rc = -EIO;
20068c2ecf20Sopenharmony_ci		goto fail;
20078c2ecf20Sopenharmony_ci	}
20088c2ecf20Sopenharmony_ci
20098c2ecf20Sopenharmony_ci	*id_out = (int)MCDI_DWORD(outbuf, WOL_FILTER_GET_OUT_FILTER_ID);
20108c2ecf20Sopenharmony_ci
20118c2ecf20Sopenharmony_ci	return 0;
20128c2ecf20Sopenharmony_ci
20138c2ecf20Sopenharmony_cifail:
20148c2ecf20Sopenharmony_ci	*id_out = -1;
20158c2ecf20Sopenharmony_ci	netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
20168c2ecf20Sopenharmony_ci	return rc;
20178c2ecf20Sopenharmony_ci}
20188c2ecf20Sopenharmony_ci
20198c2ecf20Sopenharmony_ci
20208c2ecf20Sopenharmony_ciint efx_mcdi_wol_filter_remove(struct efx_nic *efx, int id)
20218c2ecf20Sopenharmony_ci{
20228c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_WOL_FILTER_REMOVE_IN_LEN);
20238c2ecf20Sopenharmony_ci	int rc;
20248c2ecf20Sopenharmony_ci
20258c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, WOL_FILTER_REMOVE_IN_FILTER_ID, (u32)id);
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_REMOVE, inbuf, sizeof(inbuf),
20288c2ecf20Sopenharmony_ci			  NULL, 0, NULL);
20298c2ecf20Sopenharmony_ci	return rc;
20308c2ecf20Sopenharmony_ci}
20318c2ecf20Sopenharmony_ci
20328c2ecf20Sopenharmony_ciint efx_mcdi_flush_rxqs(struct efx_nic *efx)
20338c2ecf20Sopenharmony_ci{
20348c2ecf20Sopenharmony_ci	struct efx_channel *channel;
20358c2ecf20Sopenharmony_ci	struct efx_rx_queue *rx_queue;
20368c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf,
20378c2ecf20Sopenharmony_ci			 MC_CMD_FLUSH_RX_QUEUES_IN_LEN(EFX_MAX_CHANNELS));
20388c2ecf20Sopenharmony_ci	int rc, count;
20398c2ecf20Sopenharmony_ci
20408c2ecf20Sopenharmony_ci	BUILD_BUG_ON(EFX_MAX_CHANNELS >
20418c2ecf20Sopenharmony_ci		     MC_CMD_FLUSH_RX_QUEUES_IN_QID_OFST_MAXNUM);
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci	count = 0;
20448c2ecf20Sopenharmony_ci	efx_for_each_channel(channel, efx) {
20458c2ecf20Sopenharmony_ci		efx_for_each_channel_rx_queue(rx_queue, channel) {
20468c2ecf20Sopenharmony_ci			if (rx_queue->flush_pending) {
20478c2ecf20Sopenharmony_ci				rx_queue->flush_pending = false;
20488c2ecf20Sopenharmony_ci				atomic_dec(&efx->rxq_flush_pending);
20498c2ecf20Sopenharmony_ci				MCDI_SET_ARRAY_DWORD(
20508c2ecf20Sopenharmony_ci					inbuf, FLUSH_RX_QUEUES_IN_QID_OFST,
20518c2ecf20Sopenharmony_ci					count, efx_rx_queue_index(rx_queue));
20528c2ecf20Sopenharmony_ci				count++;
20538c2ecf20Sopenharmony_ci			}
20548c2ecf20Sopenharmony_ci		}
20558c2ecf20Sopenharmony_ci	}
20568c2ecf20Sopenharmony_ci
20578c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc(efx, MC_CMD_FLUSH_RX_QUEUES, inbuf,
20588c2ecf20Sopenharmony_ci			  MC_CMD_FLUSH_RX_QUEUES_IN_LEN(count), NULL, 0, NULL);
20598c2ecf20Sopenharmony_ci	WARN_ON(rc < 0);
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_ci	return rc;
20628c2ecf20Sopenharmony_ci}
20638c2ecf20Sopenharmony_ci
20648c2ecf20Sopenharmony_ciint efx_mcdi_wol_filter_reset(struct efx_nic *efx)
20658c2ecf20Sopenharmony_ci{
20668c2ecf20Sopenharmony_ci	int rc;
20678c2ecf20Sopenharmony_ci
20688c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_RESET, NULL, 0, NULL, 0, NULL);
20698c2ecf20Sopenharmony_ci	return rc;
20708c2ecf20Sopenharmony_ci}
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ciint efx_mcdi_set_workaround(struct efx_nic *efx, u32 type, bool enabled,
20738c2ecf20Sopenharmony_ci			    unsigned int *flags)
20748c2ecf20Sopenharmony_ci{
20758c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_WORKAROUND_IN_LEN);
20768c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_WORKAROUND_EXT_OUT_LEN);
20778c2ecf20Sopenharmony_ci	size_t outlen;
20788c2ecf20Sopenharmony_ci	int rc;
20798c2ecf20Sopenharmony_ci
20808c2ecf20Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_WORKAROUND_OUT_LEN != 0);
20818c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, WORKAROUND_IN_TYPE, type);
20828c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, WORKAROUND_IN_ENABLED, enabled);
20838c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc(efx, MC_CMD_WORKAROUND, inbuf, sizeof(inbuf),
20848c2ecf20Sopenharmony_ci			  outbuf, sizeof(outbuf), &outlen);
20858c2ecf20Sopenharmony_ci	if (rc)
20868c2ecf20Sopenharmony_ci		return rc;
20878c2ecf20Sopenharmony_ci
20888c2ecf20Sopenharmony_ci	if (!flags)
20898c2ecf20Sopenharmony_ci		return 0;
20908c2ecf20Sopenharmony_ci
20918c2ecf20Sopenharmony_ci	if (outlen >= MC_CMD_WORKAROUND_EXT_OUT_LEN)
20928c2ecf20Sopenharmony_ci		*flags = MCDI_DWORD(outbuf, WORKAROUND_EXT_OUT_FLAGS);
20938c2ecf20Sopenharmony_ci	else
20948c2ecf20Sopenharmony_ci		*flags = 0;
20958c2ecf20Sopenharmony_ci
20968c2ecf20Sopenharmony_ci	return 0;
20978c2ecf20Sopenharmony_ci}
20988c2ecf20Sopenharmony_ci
20998c2ecf20Sopenharmony_ciint efx_mcdi_get_workarounds(struct efx_nic *efx, unsigned int *impl_out,
21008c2ecf20Sopenharmony_ci			     unsigned int *enabled_out)
21018c2ecf20Sopenharmony_ci{
21028c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_WORKAROUNDS_OUT_LEN);
21038c2ecf20Sopenharmony_ci	size_t outlen;
21048c2ecf20Sopenharmony_ci	int rc;
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc(efx, MC_CMD_GET_WORKAROUNDS, NULL, 0,
21078c2ecf20Sopenharmony_ci			  outbuf, sizeof(outbuf), &outlen);
21088c2ecf20Sopenharmony_ci	if (rc)
21098c2ecf20Sopenharmony_ci		goto fail;
21108c2ecf20Sopenharmony_ci
21118c2ecf20Sopenharmony_ci	if (outlen < MC_CMD_GET_WORKAROUNDS_OUT_LEN) {
21128c2ecf20Sopenharmony_ci		rc = -EIO;
21138c2ecf20Sopenharmony_ci		goto fail;
21148c2ecf20Sopenharmony_ci	}
21158c2ecf20Sopenharmony_ci
21168c2ecf20Sopenharmony_ci	if (impl_out)
21178c2ecf20Sopenharmony_ci		*impl_out = MCDI_DWORD(outbuf, GET_WORKAROUNDS_OUT_IMPLEMENTED);
21188c2ecf20Sopenharmony_ci
21198c2ecf20Sopenharmony_ci	if (enabled_out)
21208c2ecf20Sopenharmony_ci		*enabled_out = MCDI_DWORD(outbuf, GET_WORKAROUNDS_OUT_ENABLED);
21218c2ecf20Sopenharmony_ci
21228c2ecf20Sopenharmony_ci	return 0;
21238c2ecf20Sopenharmony_ci
21248c2ecf20Sopenharmony_cifail:
21258c2ecf20Sopenharmony_ci	/* Older firmware lacks GET_WORKAROUNDS and this isn't especially
21268c2ecf20Sopenharmony_ci	 * terrifying.  The call site will have to deal with it though.
21278c2ecf20Sopenharmony_ci	 */
21288c2ecf20Sopenharmony_ci	netif_cond_dbg(efx, hw, efx->net_dev, rc == -ENOSYS, err,
21298c2ecf20Sopenharmony_ci		       "%s: failed rc=%d\n", __func__, rc);
21308c2ecf20Sopenharmony_ci	return rc;
21318c2ecf20Sopenharmony_ci}
21328c2ecf20Sopenharmony_ci
21338c2ecf20Sopenharmony_ci#ifdef CONFIG_SFC_MTD
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ci#define EFX_MCDI_NVRAM_LEN_MAX 128
21368c2ecf20Sopenharmony_ci
21378c2ecf20Sopenharmony_cistatic int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type)
21388c2ecf20Sopenharmony_ci{
21398c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_START_V2_IN_LEN);
21408c2ecf20Sopenharmony_ci	int rc;
21418c2ecf20Sopenharmony_ci
21428c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_START_IN_TYPE, type);
21438c2ecf20Sopenharmony_ci	MCDI_POPULATE_DWORD_1(inbuf, NVRAM_UPDATE_START_V2_IN_FLAGS,
21448c2ecf20Sopenharmony_ci			      NVRAM_UPDATE_START_V2_IN_FLAG_REPORT_VERIFY_RESULT,
21458c2ecf20Sopenharmony_ci			      1);
21468c2ecf20Sopenharmony_ci
21478c2ecf20Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_START_OUT_LEN != 0);
21488c2ecf20Sopenharmony_ci
21498c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_START, inbuf, sizeof(inbuf),
21508c2ecf20Sopenharmony_ci			  NULL, 0, NULL);
21518c2ecf20Sopenharmony_ci
21528c2ecf20Sopenharmony_ci	return rc;
21538c2ecf20Sopenharmony_ci}
21548c2ecf20Sopenharmony_ci
21558c2ecf20Sopenharmony_cistatic int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type,
21568c2ecf20Sopenharmony_ci			       loff_t offset, u8 *buffer, size_t length)
21578c2ecf20Sopenharmony_ci{
21588c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_READ_IN_V2_LEN);
21598c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf,
21608c2ecf20Sopenharmony_ci			 MC_CMD_NVRAM_READ_OUT_LEN(EFX_MCDI_NVRAM_LEN_MAX));
21618c2ecf20Sopenharmony_ci	size_t outlen;
21628c2ecf20Sopenharmony_ci	int rc;
21638c2ecf20Sopenharmony_ci
21648c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_TYPE, type);
21658c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_OFFSET, offset);
21668c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_LENGTH, length);
21678c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_V2_MODE,
21688c2ecf20Sopenharmony_ci		       MC_CMD_NVRAM_READ_IN_V2_DEFAULT);
21698c2ecf20Sopenharmony_ci
21708c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_READ, inbuf, sizeof(inbuf),
21718c2ecf20Sopenharmony_ci			  outbuf, sizeof(outbuf), &outlen);
21728c2ecf20Sopenharmony_ci	if (rc)
21738c2ecf20Sopenharmony_ci		return rc;
21748c2ecf20Sopenharmony_ci
21758c2ecf20Sopenharmony_ci	memcpy(buffer, MCDI_PTR(outbuf, NVRAM_READ_OUT_READ_BUFFER), length);
21768c2ecf20Sopenharmony_ci	return 0;
21778c2ecf20Sopenharmony_ci}
21788c2ecf20Sopenharmony_ci
21798c2ecf20Sopenharmony_cistatic int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type,
21808c2ecf20Sopenharmony_ci				loff_t offset, const u8 *buffer, size_t length)
21818c2ecf20Sopenharmony_ci{
21828c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf,
21838c2ecf20Sopenharmony_ci			 MC_CMD_NVRAM_WRITE_IN_LEN(EFX_MCDI_NVRAM_LEN_MAX));
21848c2ecf20Sopenharmony_ci	int rc;
21858c2ecf20Sopenharmony_ci
21868c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_TYPE, type);
21878c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_OFFSET, offset);
21888c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_LENGTH, length);
21898c2ecf20Sopenharmony_ci	memcpy(MCDI_PTR(inbuf, NVRAM_WRITE_IN_WRITE_BUFFER), buffer, length);
21908c2ecf20Sopenharmony_ci
21918c2ecf20Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_NVRAM_WRITE_OUT_LEN != 0);
21928c2ecf20Sopenharmony_ci
21938c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_WRITE, inbuf,
21948c2ecf20Sopenharmony_ci			  ALIGN(MC_CMD_NVRAM_WRITE_IN_LEN(length), 4),
21958c2ecf20Sopenharmony_ci			  NULL, 0, NULL);
21968c2ecf20Sopenharmony_ci	return rc;
21978c2ecf20Sopenharmony_ci}
21988c2ecf20Sopenharmony_ci
21998c2ecf20Sopenharmony_cistatic int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type,
22008c2ecf20Sopenharmony_ci				loff_t offset, size_t length)
22018c2ecf20Sopenharmony_ci{
22028c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_ERASE_IN_LEN);
22038c2ecf20Sopenharmony_ci	int rc;
22048c2ecf20Sopenharmony_ci
22058c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_TYPE, type);
22068c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_OFFSET, offset);
22078c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_LENGTH, length);
22088c2ecf20Sopenharmony_ci
22098c2ecf20Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_NVRAM_ERASE_OUT_LEN != 0);
22108c2ecf20Sopenharmony_ci
22118c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_ERASE, inbuf, sizeof(inbuf),
22128c2ecf20Sopenharmony_ci			  NULL, 0, NULL);
22138c2ecf20Sopenharmony_ci	return rc;
22148c2ecf20Sopenharmony_ci}
22158c2ecf20Sopenharmony_ci
22168c2ecf20Sopenharmony_cistatic int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type)
22178c2ecf20Sopenharmony_ci{
22188c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_FINISH_V2_IN_LEN);
22198c2ecf20Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT_LEN);
22208c2ecf20Sopenharmony_ci	size_t outlen;
22218c2ecf20Sopenharmony_ci	int rc, rc2;
22228c2ecf20Sopenharmony_ci
22238c2ecf20Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_FINISH_IN_TYPE, type);
22248c2ecf20Sopenharmony_ci	/* Always set this flag. Old firmware ignores it */
22258c2ecf20Sopenharmony_ci	MCDI_POPULATE_DWORD_1(inbuf, NVRAM_UPDATE_FINISH_V2_IN_FLAGS,
22268c2ecf20Sopenharmony_ci			      NVRAM_UPDATE_FINISH_V2_IN_FLAG_REPORT_VERIFY_RESULT,
22278c2ecf20Sopenharmony_ci			      1);
22288c2ecf20Sopenharmony_ci
22298c2ecf20Sopenharmony_ci	rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_FINISH, inbuf, sizeof(inbuf),
22308c2ecf20Sopenharmony_ci			  outbuf, sizeof(outbuf), &outlen);
22318c2ecf20Sopenharmony_ci	if (!rc && outlen >= MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT_LEN) {
22328c2ecf20Sopenharmony_ci		rc2 = MCDI_DWORD(outbuf, NVRAM_UPDATE_FINISH_V2_OUT_RESULT_CODE);
22338c2ecf20Sopenharmony_ci		if (rc2 != MC_CMD_NVRAM_VERIFY_RC_SUCCESS)
22348c2ecf20Sopenharmony_ci			netif_err(efx, drv, efx->net_dev,
22358c2ecf20Sopenharmony_ci				  "NVRAM update failed verification with code 0x%x\n",
22368c2ecf20Sopenharmony_ci				  rc2);
22378c2ecf20Sopenharmony_ci		switch (rc2) {
22388c2ecf20Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_SUCCESS:
22398c2ecf20Sopenharmony_ci			break;
22408c2ecf20Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_CMS_CHECK_FAILED:
22418c2ecf20Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_MESSAGE_DIGEST_CHECK_FAILED:
22428c2ecf20Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_SIGNATURE_CHECK_FAILED:
22438c2ecf20Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_TRUSTED_APPROVERS_CHECK_FAILED:
22448c2ecf20Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_SIGNATURE_CHAIN_CHECK_FAILED:
22458c2ecf20Sopenharmony_ci			rc = -EIO;
22468c2ecf20Sopenharmony_ci			break;
22478c2ecf20Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_INVALID_CMS_FORMAT:
22488c2ecf20Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_BAD_MESSAGE_DIGEST:
22498c2ecf20Sopenharmony_ci			rc = -EINVAL;
22508c2ecf20Sopenharmony_ci			break;
22518c2ecf20Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_NO_VALID_SIGNATURES:
22528c2ecf20Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_NO_TRUSTED_APPROVERS:
22538c2ecf20Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_NO_SIGNATURE_MATCH:
22548c2ecf20Sopenharmony_ci			rc = -EPERM;
22558c2ecf20Sopenharmony_ci			break;
22568c2ecf20Sopenharmony_ci		default:
22578c2ecf20Sopenharmony_ci			netif_err(efx, drv, efx->net_dev,
22588c2ecf20Sopenharmony_ci				  "Unknown response to NVRAM_UPDATE_FINISH\n");
22598c2ecf20Sopenharmony_ci			rc = -EIO;
22608c2ecf20Sopenharmony_ci		}
22618c2ecf20Sopenharmony_ci	}
22628c2ecf20Sopenharmony_ci
22638c2ecf20Sopenharmony_ci	return rc;
22648c2ecf20Sopenharmony_ci}
22658c2ecf20Sopenharmony_ci
22668c2ecf20Sopenharmony_ciint efx_mcdi_mtd_read(struct mtd_info *mtd, loff_t start,
22678c2ecf20Sopenharmony_ci		      size_t len, size_t *retlen, u8 *buffer)
22688c2ecf20Sopenharmony_ci{
22698c2ecf20Sopenharmony_ci	struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
22708c2ecf20Sopenharmony_ci	struct efx_nic *efx = mtd->priv;
22718c2ecf20Sopenharmony_ci	loff_t offset = start;
22728c2ecf20Sopenharmony_ci	loff_t end = min_t(loff_t, start + len, mtd->size);
22738c2ecf20Sopenharmony_ci	size_t chunk;
22748c2ecf20Sopenharmony_ci	int rc = 0;
22758c2ecf20Sopenharmony_ci
22768c2ecf20Sopenharmony_ci	while (offset < end) {
22778c2ecf20Sopenharmony_ci		chunk = min_t(size_t, end - offset, EFX_MCDI_NVRAM_LEN_MAX);
22788c2ecf20Sopenharmony_ci		rc = efx_mcdi_nvram_read(efx, part->nvram_type, offset,
22798c2ecf20Sopenharmony_ci					 buffer, chunk);
22808c2ecf20Sopenharmony_ci		if (rc)
22818c2ecf20Sopenharmony_ci			goto out;
22828c2ecf20Sopenharmony_ci		offset += chunk;
22838c2ecf20Sopenharmony_ci		buffer += chunk;
22848c2ecf20Sopenharmony_ci	}
22858c2ecf20Sopenharmony_ciout:
22868c2ecf20Sopenharmony_ci	*retlen = offset - start;
22878c2ecf20Sopenharmony_ci	return rc;
22888c2ecf20Sopenharmony_ci}
22898c2ecf20Sopenharmony_ci
22908c2ecf20Sopenharmony_ciint efx_mcdi_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len)
22918c2ecf20Sopenharmony_ci{
22928c2ecf20Sopenharmony_ci	struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
22938c2ecf20Sopenharmony_ci	struct efx_nic *efx = mtd->priv;
22948c2ecf20Sopenharmony_ci	loff_t offset = start & ~((loff_t)(mtd->erasesize - 1));
22958c2ecf20Sopenharmony_ci	loff_t end = min_t(loff_t, start + len, mtd->size);
22968c2ecf20Sopenharmony_ci	size_t chunk = part->common.mtd.erasesize;
22978c2ecf20Sopenharmony_ci	int rc = 0;
22988c2ecf20Sopenharmony_ci
22998c2ecf20Sopenharmony_ci	if (!part->updating) {
23008c2ecf20Sopenharmony_ci		rc = efx_mcdi_nvram_update_start(efx, part->nvram_type);
23018c2ecf20Sopenharmony_ci		if (rc)
23028c2ecf20Sopenharmony_ci			goto out;
23038c2ecf20Sopenharmony_ci		part->updating = true;
23048c2ecf20Sopenharmony_ci	}
23058c2ecf20Sopenharmony_ci
23068c2ecf20Sopenharmony_ci	/* The MCDI interface can in fact do multiple erase blocks at once;
23078c2ecf20Sopenharmony_ci	 * but erasing may be slow, so we make multiple calls here to avoid
23088c2ecf20Sopenharmony_ci	 * tripping the MCDI RPC timeout. */
23098c2ecf20Sopenharmony_ci	while (offset < end) {
23108c2ecf20Sopenharmony_ci		rc = efx_mcdi_nvram_erase(efx, part->nvram_type, offset,
23118c2ecf20Sopenharmony_ci					  chunk);
23128c2ecf20Sopenharmony_ci		if (rc)
23138c2ecf20Sopenharmony_ci			goto out;
23148c2ecf20Sopenharmony_ci		offset += chunk;
23158c2ecf20Sopenharmony_ci	}
23168c2ecf20Sopenharmony_ciout:
23178c2ecf20Sopenharmony_ci	return rc;
23188c2ecf20Sopenharmony_ci}
23198c2ecf20Sopenharmony_ci
23208c2ecf20Sopenharmony_ciint efx_mcdi_mtd_write(struct mtd_info *mtd, loff_t start,
23218c2ecf20Sopenharmony_ci		       size_t len, size_t *retlen, const u8 *buffer)
23228c2ecf20Sopenharmony_ci{
23238c2ecf20Sopenharmony_ci	struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
23248c2ecf20Sopenharmony_ci	struct efx_nic *efx = mtd->priv;
23258c2ecf20Sopenharmony_ci	loff_t offset = start;
23268c2ecf20Sopenharmony_ci	loff_t end = min_t(loff_t, start + len, mtd->size);
23278c2ecf20Sopenharmony_ci	size_t chunk;
23288c2ecf20Sopenharmony_ci	int rc = 0;
23298c2ecf20Sopenharmony_ci
23308c2ecf20Sopenharmony_ci	if (!part->updating) {
23318c2ecf20Sopenharmony_ci		rc = efx_mcdi_nvram_update_start(efx, part->nvram_type);
23328c2ecf20Sopenharmony_ci		if (rc)
23338c2ecf20Sopenharmony_ci			goto out;
23348c2ecf20Sopenharmony_ci		part->updating = true;
23358c2ecf20Sopenharmony_ci	}
23368c2ecf20Sopenharmony_ci
23378c2ecf20Sopenharmony_ci	while (offset < end) {
23388c2ecf20Sopenharmony_ci		chunk = min_t(size_t, end - offset, EFX_MCDI_NVRAM_LEN_MAX);
23398c2ecf20Sopenharmony_ci		rc = efx_mcdi_nvram_write(efx, part->nvram_type, offset,
23408c2ecf20Sopenharmony_ci					  buffer, chunk);
23418c2ecf20Sopenharmony_ci		if (rc)
23428c2ecf20Sopenharmony_ci			goto out;
23438c2ecf20Sopenharmony_ci		offset += chunk;
23448c2ecf20Sopenharmony_ci		buffer += chunk;
23458c2ecf20Sopenharmony_ci	}
23468c2ecf20Sopenharmony_ciout:
23478c2ecf20Sopenharmony_ci	*retlen = offset - start;
23488c2ecf20Sopenharmony_ci	return rc;
23498c2ecf20Sopenharmony_ci}
23508c2ecf20Sopenharmony_ci
23518c2ecf20Sopenharmony_ciint efx_mcdi_mtd_sync(struct mtd_info *mtd)
23528c2ecf20Sopenharmony_ci{
23538c2ecf20Sopenharmony_ci	struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
23548c2ecf20Sopenharmony_ci	struct efx_nic *efx = mtd->priv;
23558c2ecf20Sopenharmony_ci	int rc = 0;
23568c2ecf20Sopenharmony_ci
23578c2ecf20Sopenharmony_ci	if (part->updating) {
23588c2ecf20Sopenharmony_ci		part->updating = false;
23598c2ecf20Sopenharmony_ci		rc = efx_mcdi_nvram_update_finish(efx, part->nvram_type);
23608c2ecf20Sopenharmony_ci	}
23618c2ecf20Sopenharmony_ci
23628c2ecf20Sopenharmony_ci	return rc;
23638c2ecf20Sopenharmony_ci}
23648c2ecf20Sopenharmony_ci
23658c2ecf20Sopenharmony_civoid efx_mcdi_mtd_rename(struct efx_mtd_partition *part)
23668c2ecf20Sopenharmony_ci{
23678c2ecf20Sopenharmony_ci	struct efx_mcdi_mtd_partition *mcdi_part =
23688c2ecf20Sopenharmony_ci		container_of(part, struct efx_mcdi_mtd_partition, common);
23698c2ecf20Sopenharmony_ci	struct efx_nic *efx = part->mtd.priv;
23708c2ecf20Sopenharmony_ci
23718c2ecf20Sopenharmony_ci	snprintf(part->name, sizeof(part->name), "%s %s:%02x",
23728c2ecf20Sopenharmony_ci		 efx->name, part->type_name, mcdi_part->fw_subtype);
23738c2ecf20Sopenharmony_ci}
23748c2ecf20Sopenharmony_ci
23758c2ecf20Sopenharmony_ci#endif /* CONFIG_SFC_MTD */
2376