162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/****************************************************************************
362306a36Sopenharmony_ci * Driver for Solarflare network controllers and boards
462306a36Sopenharmony_ci * Copyright 2008-2013 Solarflare Communications Inc.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/delay.h>
862306a36Sopenharmony_ci#include <linux/moduleparam.h>
962306a36Sopenharmony_ci#include <linux/atomic.h>
1062306a36Sopenharmony_ci#include "net_driver.h"
1162306a36Sopenharmony_ci#include "nic.h"
1262306a36Sopenharmony_ci#include "io.h"
1362306a36Sopenharmony_ci#include "farch_regs.h"
1462306a36Sopenharmony_ci#include "mcdi_pcol.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/**************************************************************************
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * Management-Controller-to-Driver Interface
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci **************************************************************************
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define MCDI_RPC_TIMEOUT       (10 * HZ)
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/* A reboot/assertion causes the MCDI status word to be set after the
2662306a36Sopenharmony_ci * command word is set or a REBOOT event is sent. If we notice a reboot
2762306a36Sopenharmony_ci * via these mechanisms then wait 250ms for the status word to be set.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_ci#define MCDI_STATUS_DELAY_US		100
3062306a36Sopenharmony_ci#define MCDI_STATUS_DELAY_COUNT		2500
3162306a36Sopenharmony_ci#define MCDI_STATUS_SLEEP_MS						\
3262306a36Sopenharmony_ci	(MCDI_STATUS_DELAY_US * MCDI_STATUS_DELAY_COUNT / 1000)
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define SEQ_MASK							\
3562306a36Sopenharmony_ci	EFX_MASK32(EFX_WIDTH(MCDI_HEADER_SEQ))
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistruct efx_mcdi_async_param {
3862306a36Sopenharmony_ci	struct list_head list;
3962306a36Sopenharmony_ci	unsigned int cmd;
4062306a36Sopenharmony_ci	size_t inlen;
4162306a36Sopenharmony_ci	size_t outlen;
4262306a36Sopenharmony_ci	bool quiet;
4362306a36Sopenharmony_ci	efx_mcdi_async_completer *complete;
4462306a36Sopenharmony_ci	unsigned long cookie;
4562306a36Sopenharmony_ci	/* followed by request/response buffer */
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic void efx_mcdi_timeout_async(struct timer_list *t);
4962306a36Sopenharmony_cistatic int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating,
5062306a36Sopenharmony_ci			       bool *was_attached_out);
5162306a36Sopenharmony_cistatic bool efx_mcdi_poll_once(struct efx_nic *efx);
5262306a36Sopenharmony_cistatic void efx_mcdi_abandon(struct efx_nic *efx);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#ifdef CONFIG_SFC_SIENA_MCDI_LOGGING
5562306a36Sopenharmony_cistatic bool efx_siena_mcdi_logging_default;
5662306a36Sopenharmony_cimodule_param_named(mcdi_logging_default, efx_siena_mcdi_logging_default,
5762306a36Sopenharmony_ci		   bool, 0644);
5862306a36Sopenharmony_ciMODULE_PARM_DESC(mcdi_logging_default,
5962306a36Sopenharmony_ci		 "Enable MCDI logging on newly-probed functions");
6062306a36Sopenharmony_ci#endif
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ciint efx_siena_mcdi_init(struct efx_nic *efx)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	struct efx_mcdi_iface *mcdi;
6562306a36Sopenharmony_ci	bool already_attached;
6662306a36Sopenharmony_ci	int rc = -ENOMEM;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	efx->mcdi = kzalloc(sizeof(*efx->mcdi), GFP_KERNEL);
6962306a36Sopenharmony_ci	if (!efx->mcdi)
7062306a36Sopenharmony_ci		goto fail;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	mcdi = efx_mcdi(efx);
7362306a36Sopenharmony_ci	mcdi->efx = efx;
7462306a36Sopenharmony_ci#ifdef CONFIG_SFC_SIENA_MCDI_LOGGING
7562306a36Sopenharmony_ci	/* consuming code assumes buffer is page-sized */
7662306a36Sopenharmony_ci	mcdi->logging_buffer = (char *)__get_free_page(GFP_KERNEL);
7762306a36Sopenharmony_ci	if (!mcdi->logging_buffer)
7862306a36Sopenharmony_ci		goto fail1;
7962306a36Sopenharmony_ci	mcdi->logging_enabled = efx_siena_mcdi_logging_default;
8062306a36Sopenharmony_ci#endif
8162306a36Sopenharmony_ci	init_waitqueue_head(&mcdi->wq);
8262306a36Sopenharmony_ci	init_waitqueue_head(&mcdi->proxy_rx_wq);
8362306a36Sopenharmony_ci	spin_lock_init(&mcdi->iface_lock);
8462306a36Sopenharmony_ci	mcdi->state = MCDI_STATE_QUIESCENT;
8562306a36Sopenharmony_ci	mcdi->mode = MCDI_MODE_POLL;
8662306a36Sopenharmony_ci	spin_lock_init(&mcdi->async_lock);
8762306a36Sopenharmony_ci	INIT_LIST_HEAD(&mcdi->async_list);
8862306a36Sopenharmony_ci	timer_setup(&mcdi->async_timer, efx_mcdi_timeout_async, 0);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	(void)efx_siena_mcdi_poll_reboot(efx);
9162306a36Sopenharmony_ci	mcdi->new_epoch = true;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/* Recover from a failed assertion before probing */
9462306a36Sopenharmony_ci	rc = efx_siena_mcdi_handle_assertion(efx);
9562306a36Sopenharmony_ci	if (rc)
9662306a36Sopenharmony_ci		goto fail2;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	/* Let the MC (and BMC, if this is a LOM) know that the driver
9962306a36Sopenharmony_ci	 * is loaded. We should do this before we reset the NIC.
10062306a36Sopenharmony_ci	 */
10162306a36Sopenharmony_ci	rc = efx_mcdi_drv_attach(efx, true, &already_attached);
10262306a36Sopenharmony_ci	if (rc) {
10362306a36Sopenharmony_ci		netif_err(efx, probe, efx->net_dev,
10462306a36Sopenharmony_ci			  "Unable to register driver with MCPU\n");
10562306a36Sopenharmony_ci		goto fail2;
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci	if (already_attached)
10862306a36Sopenharmony_ci		/* Not a fatal error */
10962306a36Sopenharmony_ci		netif_err(efx, probe, efx->net_dev,
11062306a36Sopenharmony_ci			  "Host already registered with MCPU\n");
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (efx->mcdi->fn_flags &
11362306a36Sopenharmony_ci	    (1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_PRIMARY))
11462306a36Sopenharmony_ci		efx->primary = efx;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return 0;
11762306a36Sopenharmony_cifail2:
11862306a36Sopenharmony_ci#ifdef CONFIG_SFC_SIENA_MCDI_LOGGING
11962306a36Sopenharmony_ci	free_page((unsigned long)mcdi->logging_buffer);
12062306a36Sopenharmony_cifail1:
12162306a36Sopenharmony_ci#endif
12262306a36Sopenharmony_ci	kfree(efx->mcdi);
12362306a36Sopenharmony_ci	efx->mcdi = NULL;
12462306a36Sopenharmony_cifail:
12562306a36Sopenharmony_ci	return rc;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_civoid efx_siena_mcdi_detach(struct efx_nic *efx)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	if (!efx->mcdi)
13162306a36Sopenharmony_ci		return;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	BUG_ON(efx->mcdi->iface.state != MCDI_STATE_QUIESCENT);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	/* Relinquish the device (back to the BMC, if this is a LOM) */
13662306a36Sopenharmony_ci	efx_mcdi_drv_attach(efx, false, NULL);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_civoid efx_siena_mcdi_fini(struct efx_nic *efx)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	if (!efx->mcdi)
14262306a36Sopenharmony_ci		return;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci#ifdef CONFIG_SFC_SIENA_MCDI_LOGGING
14562306a36Sopenharmony_ci	free_page((unsigned long)efx->mcdi->iface.logging_buffer);
14662306a36Sopenharmony_ci#endif
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	kfree(efx->mcdi);
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic void efx_mcdi_send_request(struct efx_nic *efx, unsigned cmd,
15262306a36Sopenharmony_ci				  const efx_dword_t *inbuf, size_t inlen)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
15562306a36Sopenharmony_ci#ifdef CONFIG_SFC_SIENA_MCDI_LOGGING
15662306a36Sopenharmony_ci	char *buf = mcdi->logging_buffer; /* page-sized */
15762306a36Sopenharmony_ci#endif
15862306a36Sopenharmony_ci	efx_dword_t hdr[2];
15962306a36Sopenharmony_ci	size_t hdr_len;
16062306a36Sopenharmony_ci	u32 xflags, seqno;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	BUG_ON(mcdi->state == MCDI_STATE_QUIESCENT);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* Serialise with efx_mcdi_ev_cpl() and efx_mcdi_ev_death() */
16562306a36Sopenharmony_ci	spin_lock_bh(&mcdi->iface_lock);
16662306a36Sopenharmony_ci	++mcdi->seqno;
16762306a36Sopenharmony_ci	seqno = mcdi->seqno & SEQ_MASK;
16862306a36Sopenharmony_ci	spin_unlock_bh(&mcdi->iface_lock);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	xflags = 0;
17162306a36Sopenharmony_ci	if (mcdi->mode == MCDI_MODE_EVENTS)
17262306a36Sopenharmony_ci		xflags |= MCDI_HEADER_XFLAGS_EVREQ;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (efx->type->mcdi_max_ver == 1) {
17562306a36Sopenharmony_ci		/* MCDI v1 */
17662306a36Sopenharmony_ci		EFX_POPULATE_DWORD_7(hdr[0],
17762306a36Sopenharmony_ci				     MCDI_HEADER_RESPONSE, 0,
17862306a36Sopenharmony_ci				     MCDI_HEADER_RESYNC, 1,
17962306a36Sopenharmony_ci				     MCDI_HEADER_CODE, cmd,
18062306a36Sopenharmony_ci				     MCDI_HEADER_DATALEN, inlen,
18162306a36Sopenharmony_ci				     MCDI_HEADER_SEQ, seqno,
18262306a36Sopenharmony_ci				     MCDI_HEADER_XFLAGS, xflags,
18362306a36Sopenharmony_ci				     MCDI_HEADER_NOT_EPOCH, !mcdi->new_epoch);
18462306a36Sopenharmony_ci		hdr_len = 4;
18562306a36Sopenharmony_ci	} else {
18662306a36Sopenharmony_ci		/* MCDI v2 */
18762306a36Sopenharmony_ci		BUG_ON(inlen > MCDI_CTL_SDU_LEN_MAX_V2);
18862306a36Sopenharmony_ci		EFX_POPULATE_DWORD_7(hdr[0],
18962306a36Sopenharmony_ci				     MCDI_HEADER_RESPONSE, 0,
19062306a36Sopenharmony_ci				     MCDI_HEADER_RESYNC, 1,
19162306a36Sopenharmony_ci				     MCDI_HEADER_CODE, MC_CMD_V2_EXTN,
19262306a36Sopenharmony_ci				     MCDI_HEADER_DATALEN, 0,
19362306a36Sopenharmony_ci				     MCDI_HEADER_SEQ, seqno,
19462306a36Sopenharmony_ci				     MCDI_HEADER_XFLAGS, xflags,
19562306a36Sopenharmony_ci				     MCDI_HEADER_NOT_EPOCH, !mcdi->new_epoch);
19662306a36Sopenharmony_ci		EFX_POPULATE_DWORD_2(hdr[1],
19762306a36Sopenharmony_ci				     MC_CMD_V2_EXTN_IN_EXTENDED_CMD, cmd,
19862306a36Sopenharmony_ci				     MC_CMD_V2_EXTN_IN_ACTUAL_LEN, inlen);
19962306a36Sopenharmony_ci		hdr_len = 8;
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci#ifdef CONFIG_SFC_SIENA_MCDI_LOGGING
20362306a36Sopenharmony_ci	if (mcdi->logging_enabled && !WARN_ON_ONCE(!buf)) {
20462306a36Sopenharmony_ci		int bytes = 0;
20562306a36Sopenharmony_ci		int i;
20662306a36Sopenharmony_ci		/* Lengths should always be a whole number of dwords, so scream
20762306a36Sopenharmony_ci		 * if they're not.
20862306a36Sopenharmony_ci		 */
20962306a36Sopenharmony_ci		WARN_ON_ONCE(hdr_len % 4);
21062306a36Sopenharmony_ci		WARN_ON_ONCE(inlen % 4);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci		/* We own the logging buffer, as only one MCDI can be in
21362306a36Sopenharmony_ci		 * progress on a NIC at any one time.  So no need for locking.
21462306a36Sopenharmony_ci		 */
21562306a36Sopenharmony_ci		for (i = 0; i < hdr_len / 4 && bytes < PAGE_SIZE; i++)
21662306a36Sopenharmony_ci			bytes += scnprintf(buf + bytes, PAGE_SIZE - bytes,
21762306a36Sopenharmony_ci					   " %08x",
21862306a36Sopenharmony_ci					   le32_to_cpu(hdr[i].u32[0]));
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		for (i = 0; i < inlen / 4 && bytes < PAGE_SIZE; i++)
22162306a36Sopenharmony_ci			bytes += scnprintf(buf + bytes, PAGE_SIZE - bytes,
22262306a36Sopenharmony_ci					   " %08x",
22362306a36Sopenharmony_ci					   le32_to_cpu(inbuf[i].u32[0]));
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		netif_info(efx, hw, efx->net_dev, "MCDI RPC REQ:%s\n", buf);
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci#endif
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	efx->type->mcdi_request(efx, hdr, hdr_len, inbuf, inlen);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	mcdi->new_epoch = false;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic int efx_mcdi_errno(unsigned int mcdi_err)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	switch (mcdi_err) {
23762306a36Sopenharmony_ci	case 0:
23862306a36Sopenharmony_ci		return 0;
23962306a36Sopenharmony_ci#define TRANSLATE_ERROR(name)					\
24062306a36Sopenharmony_ci	case MC_CMD_ERR_ ## name:				\
24162306a36Sopenharmony_ci		return -name;
24262306a36Sopenharmony_ci	TRANSLATE_ERROR(EPERM);
24362306a36Sopenharmony_ci	TRANSLATE_ERROR(ENOENT);
24462306a36Sopenharmony_ci	TRANSLATE_ERROR(EINTR);
24562306a36Sopenharmony_ci	TRANSLATE_ERROR(EAGAIN);
24662306a36Sopenharmony_ci	TRANSLATE_ERROR(EACCES);
24762306a36Sopenharmony_ci	TRANSLATE_ERROR(EBUSY);
24862306a36Sopenharmony_ci	TRANSLATE_ERROR(EINVAL);
24962306a36Sopenharmony_ci	TRANSLATE_ERROR(EDEADLK);
25062306a36Sopenharmony_ci	TRANSLATE_ERROR(ENOSYS);
25162306a36Sopenharmony_ci	TRANSLATE_ERROR(ETIME);
25262306a36Sopenharmony_ci	TRANSLATE_ERROR(EALREADY);
25362306a36Sopenharmony_ci	TRANSLATE_ERROR(ENOSPC);
25462306a36Sopenharmony_ci#undef TRANSLATE_ERROR
25562306a36Sopenharmony_ci	case MC_CMD_ERR_ENOTSUP:
25662306a36Sopenharmony_ci		return -EOPNOTSUPP;
25762306a36Sopenharmony_ci	case MC_CMD_ERR_ALLOC_FAIL:
25862306a36Sopenharmony_ci		return -ENOBUFS;
25962306a36Sopenharmony_ci	case MC_CMD_ERR_MAC_EXIST:
26062306a36Sopenharmony_ci		return -EADDRINUSE;
26162306a36Sopenharmony_ci	default:
26262306a36Sopenharmony_ci		return -EPROTO;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic void efx_mcdi_read_response_header(struct efx_nic *efx)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
26962306a36Sopenharmony_ci	unsigned int respseq, respcmd, error;
27062306a36Sopenharmony_ci#ifdef CONFIG_SFC_SIENA_MCDI_LOGGING
27162306a36Sopenharmony_ci	char *buf = mcdi->logging_buffer; /* page-sized */
27262306a36Sopenharmony_ci#endif
27362306a36Sopenharmony_ci	efx_dword_t hdr;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	efx->type->mcdi_read_response(efx, &hdr, 0, 4);
27662306a36Sopenharmony_ci	respseq = EFX_DWORD_FIELD(hdr, MCDI_HEADER_SEQ);
27762306a36Sopenharmony_ci	respcmd = EFX_DWORD_FIELD(hdr, MCDI_HEADER_CODE);
27862306a36Sopenharmony_ci	error = EFX_DWORD_FIELD(hdr, MCDI_HEADER_ERROR);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (respcmd != MC_CMD_V2_EXTN) {
28162306a36Sopenharmony_ci		mcdi->resp_hdr_len = 4;
28262306a36Sopenharmony_ci		mcdi->resp_data_len = EFX_DWORD_FIELD(hdr, MCDI_HEADER_DATALEN);
28362306a36Sopenharmony_ci	} else {
28462306a36Sopenharmony_ci		efx->type->mcdi_read_response(efx, &hdr, 4, 4);
28562306a36Sopenharmony_ci		mcdi->resp_hdr_len = 8;
28662306a36Sopenharmony_ci		mcdi->resp_data_len =
28762306a36Sopenharmony_ci			EFX_DWORD_FIELD(hdr, MC_CMD_V2_EXTN_IN_ACTUAL_LEN);
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci#ifdef CONFIG_SFC_SIENA_MCDI_LOGGING
29162306a36Sopenharmony_ci	if (mcdi->logging_enabled && !WARN_ON_ONCE(!buf)) {
29262306a36Sopenharmony_ci		size_t hdr_len, data_len;
29362306a36Sopenharmony_ci		int bytes = 0;
29462306a36Sopenharmony_ci		int i;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci		WARN_ON_ONCE(mcdi->resp_hdr_len % 4);
29762306a36Sopenharmony_ci		hdr_len = mcdi->resp_hdr_len / 4;
29862306a36Sopenharmony_ci		/* MCDI_DECLARE_BUF ensures that underlying buffer is padded
29962306a36Sopenharmony_ci		 * to dword size, and the MCDI buffer is always dword size
30062306a36Sopenharmony_ci		 */
30162306a36Sopenharmony_ci		data_len = DIV_ROUND_UP(mcdi->resp_data_len, 4);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		/* We own the logging buffer, as only one MCDI can be in
30462306a36Sopenharmony_ci		 * progress on a NIC at any one time.  So no need for locking.
30562306a36Sopenharmony_ci		 */
30662306a36Sopenharmony_ci		for (i = 0; i < hdr_len && bytes < PAGE_SIZE; i++) {
30762306a36Sopenharmony_ci			efx->type->mcdi_read_response(efx, &hdr, (i * 4), 4);
30862306a36Sopenharmony_ci			bytes += scnprintf(buf + bytes, PAGE_SIZE - bytes,
30962306a36Sopenharmony_ci					   " %08x", le32_to_cpu(hdr.u32[0]));
31062306a36Sopenharmony_ci		}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci		for (i = 0; i < data_len && bytes < PAGE_SIZE; i++) {
31362306a36Sopenharmony_ci			efx->type->mcdi_read_response(efx, &hdr,
31462306a36Sopenharmony_ci					mcdi->resp_hdr_len + (i * 4), 4);
31562306a36Sopenharmony_ci			bytes += scnprintf(buf + bytes, PAGE_SIZE - bytes,
31662306a36Sopenharmony_ci					   " %08x", le32_to_cpu(hdr.u32[0]));
31762306a36Sopenharmony_ci		}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci		netif_info(efx, hw, efx->net_dev, "MCDI RPC RESP:%s\n", buf);
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci#endif
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	mcdi->resprc_raw = 0;
32462306a36Sopenharmony_ci	if (error && mcdi->resp_data_len == 0) {
32562306a36Sopenharmony_ci		netif_err(efx, hw, efx->net_dev, "MC rebooted\n");
32662306a36Sopenharmony_ci		mcdi->resprc = -EIO;
32762306a36Sopenharmony_ci	} else if ((respseq ^ mcdi->seqno) & SEQ_MASK) {
32862306a36Sopenharmony_ci		netif_err(efx, hw, efx->net_dev,
32962306a36Sopenharmony_ci			  "MC response mismatch tx seq 0x%x rx seq 0x%x\n",
33062306a36Sopenharmony_ci			  respseq, mcdi->seqno);
33162306a36Sopenharmony_ci		mcdi->resprc = -EIO;
33262306a36Sopenharmony_ci	} else if (error) {
33362306a36Sopenharmony_ci		efx->type->mcdi_read_response(efx, &hdr, mcdi->resp_hdr_len, 4);
33462306a36Sopenharmony_ci		mcdi->resprc_raw = EFX_DWORD_FIELD(hdr, EFX_DWORD_0);
33562306a36Sopenharmony_ci		mcdi->resprc = efx_mcdi_errno(mcdi->resprc_raw);
33662306a36Sopenharmony_ci	} else {
33762306a36Sopenharmony_ci		mcdi->resprc = 0;
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic bool efx_mcdi_poll_once(struct efx_nic *efx)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	rmb();
34662306a36Sopenharmony_ci	if (!efx->type->mcdi_poll_response(efx))
34762306a36Sopenharmony_ci		return false;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	spin_lock_bh(&mcdi->iface_lock);
35062306a36Sopenharmony_ci	efx_mcdi_read_response_header(efx);
35162306a36Sopenharmony_ci	spin_unlock_bh(&mcdi->iface_lock);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	return true;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic int efx_mcdi_poll(struct efx_nic *efx)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
35962306a36Sopenharmony_ci	unsigned long time, finish;
36062306a36Sopenharmony_ci	unsigned int spins;
36162306a36Sopenharmony_ci	int rc;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	/* Check for a reboot atomically with respect to efx_mcdi_copyout() */
36462306a36Sopenharmony_ci	rc = efx_siena_mcdi_poll_reboot(efx);
36562306a36Sopenharmony_ci	if (rc) {
36662306a36Sopenharmony_ci		spin_lock_bh(&mcdi->iface_lock);
36762306a36Sopenharmony_ci		mcdi->resprc = rc;
36862306a36Sopenharmony_ci		mcdi->resp_hdr_len = 0;
36962306a36Sopenharmony_ci		mcdi->resp_data_len = 0;
37062306a36Sopenharmony_ci		spin_unlock_bh(&mcdi->iface_lock);
37162306a36Sopenharmony_ci		return 0;
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	/* Poll for completion. Poll quickly (once a us) for the 1st jiffy,
37562306a36Sopenharmony_ci	 * because generally mcdi responses are fast. After that, back off
37662306a36Sopenharmony_ci	 * and poll once a jiffy (approximately)
37762306a36Sopenharmony_ci	 */
37862306a36Sopenharmony_ci	spins = USER_TICK_USEC;
37962306a36Sopenharmony_ci	finish = jiffies + MCDI_RPC_TIMEOUT;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	while (1) {
38262306a36Sopenharmony_ci		if (spins != 0) {
38362306a36Sopenharmony_ci			--spins;
38462306a36Sopenharmony_ci			udelay(1);
38562306a36Sopenharmony_ci		} else {
38662306a36Sopenharmony_ci			schedule_timeout_uninterruptible(1);
38762306a36Sopenharmony_ci		}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci		time = jiffies;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci		if (efx_mcdi_poll_once(efx))
39262306a36Sopenharmony_ci			break;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci		if (time_after(time, finish))
39562306a36Sopenharmony_ci			return -ETIMEDOUT;
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	/* Return rc=0 like wait_event_timeout() */
39962306a36Sopenharmony_ci	return 0;
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci/* Test and clear MC-rebooted flag for this port/function; reset
40362306a36Sopenharmony_ci * software state as necessary.
40462306a36Sopenharmony_ci */
40562306a36Sopenharmony_ciint efx_siena_mcdi_poll_reboot(struct efx_nic *efx)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	if (!efx->mcdi)
40862306a36Sopenharmony_ci		return 0;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	return efx->type->mcdi_poll_reboot(efx);
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic bool efx_mcdi_acquire_async(struct efx_mcdi_iface *mcdi)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	return cmpxchg(&mcdi->state,
41662306a36Sopenharmony_ci		       MCDI_STATE_QUIESCENT, MCDI_STATE_RUNNING_ASYNC) ==
41762306a36Sopenharmony_ci		MCDI_STATE_QUIESCENT;
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic void efx_mcdi_acquire_sync(struct efx_mcdi_iface *mcdi)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	/* Wait until the interface becomes QUIESCENT and we win the race
42362306a36Sopenharmony_ci	 * to mark it RUNNING_SYNC.
42462306a36Sopenharmony_ci	 */
42562306a36Sopenharmony_ci	wait_event(mcdi->wq,
42662306a36Sopenharmony_ci		   cmpxchg(&mcdi->state,
42762306a36Sopenharmony_ci			   MCDI_STATE_QUIESCENT, MCDI_STATE_RUNNING_SYNC) ==
42862306a36Sopenharmony_ci		   MCDI_STATE_QUIESCENT);
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic int efx_mcdi_await_completion(struct efx_nic *efx)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (wait_event_timeout(mcdi->wq, mcdi->state == MCDI_STATE_COMPLETED,
43662306a36Sopenharmony_ci			       MCDI_RPC_TIMEOUT) == 0)
43762306a36Sopenharmony_ci		return -ETIMEDOUT;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	/* Check if efx_mcdi_set_mode() switched us back to polled completions.
44062306a36Sopenharmony_ci	 * In which case, poll for completions directly. If efx_mcdi_ev_cpl()
44162306a36Sopenharmony_ci	 * completed the request first, then we'll just end up completing the
44262306a36Sopenharmony_ci	 * request again, which is safe.
44362306a36Sopenharmony_ci	 *
44462306a36Sopenharmony_ci	 * We need an smp_rmb() to synchronise with efx_siena_mcdi_mode_poll(), which
44562306a36Sopenharmony_ci	 * wait_event_timeout() implicitly provides.
44662306a36Sopenharmony_ci	 */
44762306a36Sopenharmony_ci	if (mcdi->mode == MCDI_MODE_POLL)
44862306a36Sopenharmony_ci		return efx_mcdi_poll(efx);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	return 0;
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci/* If the interface is RUNNING_SYNC, switch to COMPLETED and wake the
45462306a36Sopenharmony_ci * requester.  Return whether this was done.  Does not take any locks.
45562306a36Sopenharmony_ci */
45662306a36Sopenharmony_cistatic bool efx_mcdi_complete_sync(struct efx_mcdi_iface *mcdi)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	if (cmpxchg(&mcdi->state,
45962306a36Sopenharmony_ci		    MCDI_STATE_RUNNING_SYNC, MCDI_STATE_COMPLETED) ==
46062306a36Sopenharmony_ci	    MCDI_STATE_RUNNING_SYNC) {
46162306a36Sopenharmony_ci		wake_up(&mcdi->wq);
46262306a36Sopenharmony_ci		return true;
46362306a36Sopenharmony_ci	}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	return false;
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_cistatic void efx_mcdi_release(struct efx_mcdi_iface *mcdi)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	if (mcdi->mode == MCDI_MODE_EVENTS) {
47162306a36Sopenharmony_ci		struct efx_mcdi_async_param *async;
47262306a36Sopenharmony_ci		struct efx_nic *efx = mcdi->efx;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci		/* Process the asynchronous request queue */
47562306a36Sopenharmony_ci		spin_lock_bh(&mcdi->async_lock);
47662306a36Sopenharmony_ci		async = list_first_entry_or_null(
47762306a36Sopenharmony_ci			&mcdi->async_list, struct efx_mcdi_async_param, list);
47862306a36Sopenharmony_ci		if (async) {
47962306a36Sopenharmony_ci			mcdi->state = MCDI_STATE_RUNNING_ASYNC;
48062306a36Sopenharmony_ci			efx_mcdi_send_request(efx, async->cmd,
48162306a36Sopenharmony_ci					      (const efx_dword_t *)(async + 1),
48262306a36Sopenharmony_ci					      async->inlen);
48362306a36Sopenharmony_ci			mod_timer(&mcdi->async_timer,
48462306a36Sopenharmony_ci				  jiffies + MCDI_RPC_TIMEOUT);
48562306a36Sopenharmony_ci		}
48662306a36Sopenharmony_ci		spin_unlock_bh(&mcdi->async_lock);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci		if (async)
48962306a36Sopenharmony_ci			return;
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	mcdi->state = MCDI_STATE_QUIESCENT;
49362306a36Sopenharmony_ci	wake_up(&mcdi->wq);
49462306a36Sopenharmony_ci}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci/* If the interface is RUNNING_ASYNC, switch to COMPLETED, call the
49762306a36Sopenharmony_ci * asynchronous completion function, and release the interface.
49862306a36Sopenharmony_ci * Return whether this was done.  Must be called in bh-disabled
49962306a36Sopenharmony_ci * context.  Will take iface_lock and async_lock.
50062306a36Sopenharmony_ci */
50162306a36Sopenharmony_cistatic bool efx_mcdi_complete_async(struct efx_mcdi_iface *mcdi, bool timeout)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	struct efx_nic *efx = mcdi->efx;
50462306a36Sopenharmony_ci	struct efx_mcdi_async_param *async;
50562306a36Sopenharmony_ci	size_t hdr_len, data_len, err_len;
50662306a36Sopenharmony_ci	efx_dword_t *outbuf;
50762306a36Sopenharmony_ci	MCDI_DECLARE_BUF_ERR(errbuf);
50862306a36Sopenharmony_ci	int rc;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	if (cmpxchg(&mcdi->state,
51162306a36Sopenharmony_ci		    MCDI_STATE_RUNNING_ASYNC, MCDI_STATE_COMPLETED) !=
51262306a36Sopenharmony_ci	    MCDI_STATE_RUNNING_ASYNC)
51362306a36Sopenharmony_ci		return false;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	spin_lock(&mcdi->iface_lock);
51662306a36Sopenharmony_ci	if (timeout) {
51762306a36Sopenharmony_ci		/* Ensure that if the completion event arrives later,
51862306a36Sopenharmony_ci		 * the seqno check in efx_mcdi_ev_cpl() will fail
51962306a36Sopenharmony_ci		 */
52062306a36Sopenharmony_ci		++mcdi->seqno;
52162306a36Sopenharmony_ci		++mcdi->credits;
52262306a36Sopenharmony_ci		rc = -ETIMEDOUT;
52362306a36Sopenharmony_ci		hdr_len = 0;
52462306a36Sopenharmony_ci		data_len = 0;
52562306a36Sopenharmony_ci	} else {
52662306a36Sopenharmony_ci		rc = mcdi->resprc;
52762306a36Sopenharmony_ci		hdr_len = mcdi->resp_hdr_len;
52862306a36Sopenharmony_ci		data_len = mcdi->resp_data_len;
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci	spin_unlock(&mcdi->iface_lock);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	/* Stop the timer.  In case the timer function is running, we
53362306a36Sopenharmony_ci	 * must wait for it to return so that there is no possibility
53462306a36Sopenharmony_ci	 * of it aborting the next request.
53562306a36Sopenharmony_ci	 */
53662306a36Sopenharmony_ci	if (!timeout)
53762306a36Sopenharmony_ci		del_timer_sync(&mcdi->async_timer);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	spin_lock(&mcdi->async_lock);
54062306a36Sopenharmony_ci	async = list_first_entry(&mcdi->async_list,
54162306a36Sopenharmony_ci				 struct efx_mcdi_async_param, list);
54262306a36Sopenharmony_ci	list_del(&async->list);
54362306a36Sopenharmony_ci	spin_unlock(&mcdi->async_lock);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	outbuf = (efx_dword_t *)(async + 1);
54662306a36Sopenharmony_ci	efx->type->mcdi_read_response(efx, outbuf, hdr_len,
54762306a36Sopenharmony_ci				      min(async->outlen, data_len));
54862306a36Sopenharmony_ci	if (!timeout && rc && !async->quiet) {
54962306a36Sopenharmony_ci		err_len = min(sizeof(errbuf), data_len);
55062306a36Sopenharmony_ci		efx->type->mcdi_read_response(efx, errbuf, hdr_len,
55162306a36Sopenharmony_ci					      sizeof(errbuf));
55262306a36Sopenharmony_ci		efx_siena_mcdi_display_error(efx, async->cmd, async->inlen,
55362306a36Sopenharmony_ci					     errbuf, err_len, rc);
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	if (async->complete)
55762306a36Sopenharmony_ci		async->complete(efx, async->cookie, rc, outbuf,
55862306a36Sopenharmony_ci				min(async->outlen, data_len));
55962306a36Sopenharmony_ci	kfree(async);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	efx_mcdi_release(mcdi);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	return true;
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic void efx_mcdi_ev_cpl(struct efx_nic *efx, unsigned int seqno,
56762306a36Sopenharmony_ci			    unsigned int datalen, unsigned int mcdi_err)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
57062306a36Sopenharmony_ci	bool wake = false;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	spin_lock(&mcdi->iface_lock);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	if ((seqno ^ mcdi->seqno) & SEQ_MASK) {
57562306a36Sopenharmony_ci		if (mcdi->credits)
57662306a36Sopenharmony_ci			/* The request has been cancelled */
57762306a36Sopenharmony_ci			--mcdi->credits;
57862306a36Sopenharmony_ci		else
57962306a36Sopenharmony_ci			netif_err(efx, hw, efx->net_dev,
58062306a36Sopenharmony_ci				  "MC response mismatch tx seq 0x%x rx "
58162306a36Sopenharmony_ci				  "seq 0x%x\n", seqno, mcdi->seqno);
58262306a36Sopenharmony_ci	} else {
58362306a36Sopenharmony_ci		if (efx->type->mcdi_max_ver >= 2) {
58462306a36Sopenharmony_ci			/* MCDI v2 responses don't fit in an event */
58562306a36Sopenharmony_ci			efx_mcdi_read_response_header(efx);
58662306a36Sopenharmony_ci		} else {
58762306a36Sopenharmony_ci			mcdi->resprc = efx_mcdi_errno(mcdi_err);
58862306a36Sopenharmony_ci			mcdi->resp_hdr_len = 4;
58962306a36Sopenharmony_ci			mcdi->resp_data_len = datalen;
59062306a36Sopenharmony_ci		}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci		wake = true;
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	spin_unlock(&mcdi->iface_lock);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if (wake) {
59862306a36Sopenharmony_ci		if (!efx_mcdi_complete_async(mcdi, false))
59962306a36Sopenharmony_ci			(void) efx_mcdi_complete_sync(mcdi);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci		/* If the interface isn't RUNNING_ASYNC or
60262306a36Sopenharmony_ci		 * RUNNING_SYNC then we've received a duplicate
60362306a36Sopenharmony_ci		 * completion after we've already transitioned back to
60462306a36Sopenharmony_ci		 * QUIESCENT. [A subsequent invocation would increment
60562306a36Sopenharmony_ci		 * seqno, so would have failed the seqno check].
60662306a36Sopenharmony_ci		 */
60762306a36Sopenharmony_ci	}
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic void efx_mcdi_timeout_async(struct timer_list *t)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct efx_mcdi_iface *mcdi = from_timer(mcdi, t, async_timer);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	efx_mcdi_complete_async(mcdi, true);
61562306a36Sopenharmony_ci}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_cistatic int
61862306a36Sopenharmony_ciefx_mcdi_check_supported(struct efx_nic *efx, unsigned int cmd, size_t inlen)
61962306a36Sopenharmony_ci{
62062306a36Sopenharmony_ci	if (efx->type->mcdi_max_ver < 0 ||
62162306a36Sopenharmony_ci	     (efx->type->mcdi_max_ver < 2 &&
62262306a36Sopenharmony_ci	      cmd > MC_CMD_CMD_SPACE_ESCAPE_7))
62362306a36Sopenharmony_ci		return -EINVAL;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	if (inlen > MCDI_CTL_SDU_LEN_MAX_V2 ||
62662306a36Sopenharmony_ci	    (efx->type->mcdi_max_ver < 2 &&
62762306a36Sopenharmony_ci	     inlen > MCDI_CTL_SDU_LEN_MAX_V1))
62862306a36Sopenharmony_ci		return -EMSGSIZE;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	return 0;
63162306a36Sopenharmony_ci}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_cistatic bool efx_mcdi_get_proxy_handle(struct efx_nic *efx,
63462306a36Sopenharmony_ci				      size_t hdr_len, size_t data_len,
63562306a36Sopenharmony_ci				      u32 *proxy_handle)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	MCDI_DECLARE_BUF_ERR(testbuf);
63862306a36Sopenharmony_ci	const size_t buflen = sizeof(testbuf);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	if (!proxy_handle || data_len < buflen)
64162306a36Sopenharmony_ci		return false;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	efx->type->mcdi_read_response(efx, testbuf, hdr_len, buflen);
64462306a36Sopenharmony_ci	if (MCDI_DWORD(testbuf, ERR_CODE) == MC_CMD_ERR_PROXY_PENDING) {
64562306a36Sopenharmony_ci		*proxy_handle = MCDI_DWORD(testbuf, ERR_PROXY_PENDING_HANDLE);
64662306a36Sopenharmony_ci		return true;
64762306a36Sopenharmony_ci	}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	return false;
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_cistatic int _efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned int cmd,
65362306a36Sopenharmony_ci				size_t inlen,
65462306a36Sopenharmony_ci				efx_dword_t *outbuf, size_t outlen,
65562306a36Sopenharmony_ci				size_t *outlen_actual, bool quiet,
65662306a36Sopenharmony_ci				u32 *proxy_handle, int *raw_rc)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
65962306a36Sopenharmony_ci	MCDI_DECLARE_BUF_ERR(errbuf);
66062306a36Sopenharmony_ci	int rc;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	if (mcdi->mode == MCDI_MODE_POLL)
66362306a36Sopenharmony_ci		rc = efx_mcdi_poll(efx);
66462306a36Sopenharmony_ci	else
66562306a36Sopenharmony_ci		rc = efx_mcdi_await_completion(efx);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	if (rc != 0) {
66862306a36Sopenharmony_ci		netif_err(efx, hw, efx->net_dev,
66962306a36Sopenharmony_ci			  "MC command 0x%x inlen %d mode %d timed out\n",
67062306a36Sopenharmony_ci			  cmd, (int)inlen, mcdi->mode);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci		if (mcdi->mode == MCDI_MODE_EVENTS && efx_mcdi_poll_once(efx)) {
67362306a36Sopenharmony_ci			netif_err(efx, hw, efx->net_dev,
67462306a36Sopenharmony_ci				  "MCDI request was completed without an event\n");
67562306a36Sopenharmony_ci			rc = 0;
67662306a36Sopenharmony_ci		}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci		efx_mcdi_abandon(efx);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci		/* Close the race with efx_mcdi_ev_cpl() executing just too late
68162306a36Sopenharmony_ci		 * and completing a request we've just cancelled, by ensuring
68262306a36Sopenharmony_ci		 * that the seqno check therein fails.
68362306a36Sopenharmony_ci		 */
68462306a36Sopenharmony_ci		spin_lock_bh(&mcdi->iface_lock);
68562306a36Sopenharmony_ci		++mcdi->seqno;
68662306a36Sopenharmony_ci		++mcdi->credits;
68762306a36Sopenharmony_ci		spin_unlock_bh(&mcdi->iface_lock);
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	if (proxy_handle)
69162306a36Sopenharmony_ci		*proxy_handle = 0;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	if (rc != 0) {
69462306a36Sopenharmony_ci		if (outlen_actual)
69562306a36Sopenharmony_ci			*outlen_actual = 0;
69662306a36Sopenharmony_ci	} else {
69762306a36Sopenharmony_ci		size_t hdr_len, data_len, err_len;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci		/* At the very least we need a memory barrier here to ensure
70062306a36Sopenharmony_ci		 * we pick up changes from efx_mcdi_ev_cpl(). Protect against
70162306a36Sopenharmony_ci		 * a spurious efx_mcdi_ev_cpl() running concurrently by
70262306a36Sopenharmony_ci		 * acquiring the iface_lock. */
70362306a36Sopenharmony_ci		spin_lock_bh(&mcdi->iface_lock);
70462306a36Sopenharmony_ci		rc = mcdi->resprc;
70562306a36Sopenharmony_ci		if (raw_rc)
70662306a36Sopenharmony_ci			*raw_rc = mcdi->resprc_raw;
70762306a36Sopenharmony_ci		hdr_len = mcdi->resp_hdr_len;
70862306a36Sopenharmony_ci		data_len = mcdi->resp_data_len;
70962306a36Sopenharmony_ci		err_len = min(sizeof(errbuf), data_len);
71062306a36Sopenharmony_ci		spin_unlock_bh(&mcdi->iface_lock);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci		BUG_ON(rc > 0);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci		efx->type->mcdi_read_response(efx, outbuf, hdr_len,
71562306a36Sopenharmony_ci					      min(outlen, data_len));
71662306a36Sopenharmony_ci		if (outlen_actual)
71762306a36Sopenharmony_ci			*outlen_actual = data_len;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci		efx->type->mcdi_read_response(efx, errbuf, hdr_len, err_len);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci		if (cmd == MC_CMD_REBOOT && rc == -EIO) {
72262306a36Sopenharmony_ci			/* Don't reset if MC_CMD_REBOOT returns EIO */
72362306a36Sopenharmony_ci		} else if (rc == -EIO || rc == -EINTR) {
72462306a36Sopenharmony_ci			netif_err(efx, hw, efx->net_dev, "MC reboot detected\n");
72562306a36Sopenharmony_ci			netif_dbg(efx, hw, efx->net_dev, "MC rebooted during command %d rc %d\n",
72662306a36Sopenharmony_ci				  cmd, -rc);
72762306a36Sopenharmony_ci			if (efx->type->mcdi_reboot_detected)
72862306a36Sopenharmony_ci				efx->type->mcdi_reboot_detected(efx);
72962306a36Sopenharmony_ci			efx_siena_schedule_reset(efx, RESET_TYPE_MC_FAILURE);
73062306a36Sopenharmony_ci		} else if (proxy_handle && (rc == -EPROTO) &&
73162306a36Sopenharmony_ci			   efx_mcdi_get_proxy_handle(efx, hdr_len, data_len,
73262306a36Sopenharmony_ci						     proxy_handle)) {
73362306a36Sopenharmony_ci			mcdi->proxy_rx_status = 0;
73462306a36Sopenharmony_ci			mcdi->proxy_rx_handle = 0;
73562306a36Sopenharmony_ci			mcdi->state = MCDI_STATE_PROXY_WAIT;
73662306a36Sopenharmony_ci		} else if (rc && !quiet) {
73762306a36Sopenharmony_ci			efx_siena_mcdi_display_error(efx, cmd, inlen, errbuf,
73862306a36Sopenharmony_ci						     err_len, rc);
73962306a36Sopenharmony_ci		}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci		if (rc == -EIO || rc == -EINTR) {
74262306a36Sopenharmony_ci			msleep(MCDI_STATUS_SLEEP_MS);
74362306a36Sopenharmony_ci			efx_siena_mcdi_poll_reboot(efx);
74462306a36Sopenharmony_ci			mcdi->new_epoch = true;
74562306a36Sopenharmony_ci		}
74662306a36Sopenharmony_ci	}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	if (!proxy_handle || !*proxy_handle)
74962306a36Sopenharmony_ci		efx_mcdi_release(mcdi);
75062306a36Sopenharmony_ci	return rc;
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_cistatic void efx_mcdi_proxy_abort(struct efx_mcdi_iface *mcdi)
75462306a36Sopenharmony_ci{
75562306a36Sopenharmony_ci	if (mcdi->state == MCDI_STATE_PROXY_WAIT) {
75662306a36Sopenharmony_ci		/* Interrupt the proxy wait. */
75762306a36Sopenharmony_ci		mcdi->proxy_rx_status = -EINTR;
75862306a36Sopenharmony_ci		wake_up(&mcdi->proxy_rx_wq);
75962306a36Sopenharmony_ci	}
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistatic void efx_mcdi_ev_proxy_response(struct efx_nic *efx,
76362306a36Sopenharmony_ci				       u32 handle, int status)
76462306a36Sopenharmony_ci{
76562306a36Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	WARN_ON(mcdi->state != MCDI_STATE_PROXY_WAIT);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	mcdi->proxy_rx_status = efx_mcdi_errno(status);
77062306a36Sopenharmony_ci	/* Ensure the status is written before we update the handle, since the
77162306a36Sopenharmony_ci	 * latter is used to check if we've finished.
77262306a36Sopenharmony_ci	 */
77362306a36Sopenharmony_ci	wmb();
77462306a36Sopenharmony_ci	mcdi->proxy_rx_handle = handle;
77562306a36Sopenharmony_ci	wake_up(&mcdi->proxy_rx_wq);
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_cistatic int efx_mcdi_proxy_wait(struct efx_nic *efx, u32 handle, bool quiet)
77962306a36Sopenharmony_ci{
78062306a36Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
78162306a36Sopenharmony_ci	int rc;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	/* Wait for a proxy event, or timeout. */
78462306a36Sopenharmony_ci	rc = wait_event_timeout(mcdi->proxy_rx_wq,
78562306a36Sopenharmony_ci				mcdi->proxy_rx_handle != 0 ||
78662306a36Sopenharmony_ci				mcdi->proxy_rx_status == -EINTR,
78762306a36Sopenharmony_ci				MCDI_RPC_TIMEOUT);
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	if (rc <= 0) {
79062306a36Sopenharmony_ci		netif_dbg(efx, hw, efx->net_dev,
79162306a36Sopenharmony_ci			  "MCDI proxy timeout %d\n", handle);
79262306a36Sopenharmony_ci		return -ETIMEDOUT;
79362306a36Sopenharmony_ci	} else if (mcdi->proxy_rx_handle != handle) {
79462306a36Sopenharmony_ci		netif_warn(efx, hw, efx->net_dev,
79562306a36Sopenharmony_ci			   "MCDI proxy unexpected handle %d (expected %d)\n",
79662306a36Sopenharmony_ci			   mcdi->proxy_rx_handle, handle);
79762306a36Sopenharmony_ci		return -EINVAL;
79862306a36Sopenharmony_ci	}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	return mcdi->proxy_rx_status;
80162306a36Sopenharmony_ci}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cistatic int _efx_mcdi_rpc(struct efx_nic *efx, unsigned int cmd,
80462306a36Sopenharmony_ci			 const efx_dword_t *inbuf, size_t inlen,
80562306a36Sopenharmony_ci			 efx_dword_t *outbuf, size_t outlen,
80662306a36Sopenharmony_ci			 size_t *outlen_actual, bool quiet, int *raw_rc)
80762306a36Sopenharmony_ci{
80862306a36Sopenharmony_ci	u32 proxy_handle = 0; /* Zero is an invalid proxy handle. */
80962306a36Sopenharmony_ci	int rc;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	if (inbuf && inlen && (inbuf == outbuf)) {
81262306a36Sopenharmony_ci		/* The input buffer can't be aliased with the output. */
81362306a36Sopenharmony_ci		WARN_ON(1);
81462306a36Sopenharmony_ci		return -EINVAL;
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	rc = efx_siena_mcdi_rpc_start(efx, cmd, inbuf, inlen);
81862306a36Sopenharmony_ci	if (rc)
81962306a36Sopenharmony_ci		return rc;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	rc = _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen,
82262306a36Sopenharmony_ci				  outlen_actual, quiet, &proxy_handle, raw_rc);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	if (proxy_handle) {
82562306a36Sopenharmony_ci		/* Handle proxy authorisation. This allows approval of MCDI
82662306a36Sopenharmony_ci		 * operations to be delegated to the admin function, allowing
82762306a36Sopenharmony_ci		 * fine control over (eg) multicast subscriptions.
82862306a36Sopenharmony_ci		 */
82962306a36Sopenharmony_ci		struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci		netif_dbg(efx, hw, efx->net_dev,
83262306a36Sopenharmony_ci			  "MCDI waiting for proxy auth %d\n",
83362306a36Sopenharmony_ci			  proxy_handle);
83462306a36Sopenharmony_ci		rc = efx_mcdi_proxy_wait(efx, proxy_handle, quiet);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci		if (rc == 0) {
83762306a36Sopenharmony_ci			netif_dbg(efx, hw, efx->net_dev,
83862306a36Sopenharmony_ci				  "MCDI proxy retry %d\n", proxy_handle);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci			/* We now retry the original request. */
84162306a36Sopenharmony_ci			mcdi->state = MCDI_STATE_RUNNING_SYNC;
84262306a36Sopenharmony_ci			efx_mcdi_send_request(efx, cmd, inbuf, inlen);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci			rc = _efx_mcdi_rpc_finish(efx, cmd, inlen,
84562306a36Sopenharmony_ci						  outbuf, outlen, outlen_actual,
84662306a36Sopenharmony_ci						  quiet, NULL, raw_rc);
84762306a36Sopenharmony_ci		} else {
84862306a36Sopenharmony_ci			netif_cond_dbg(efx, hw, efx->net_dev, rc == -EPERM, err,
84962306a36Sopenharmony_ci				       "MC command 0x%x failed after proxy auth rc=%d\n",
85062306a36Sopenharmony_ci				       cmd, rc);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci			if (rc == -EINTR || rc == -EIO)
85362306a36Sopenharmony_ci				efx_siena_schedule_reset(efx, RESET_TYPE_MC_FAILURE);
85462306a36Sopenharmony_ci			efx_mcdi_release(mcdi);
85562306a36Sopenharmony_ci		}
85662306a36Sopenharmony_ci	}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	return rc;
85962306a36Sopenharmony_ci}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_cistatic int _efx_mcdi_rpc_evb_retry(struct efx_nic *efx, unsigned cmd,
86262306a36Sopenharmony_ci				   const efx_dword_t *inbuf, size_t inlen,
86362306a36Sopenharmony_ci				   efx_dword_t *outbuf, size_t outlen,
86462306a36Sopenharmony_ci				   size_t *outlen_actual, bool quiet)
86562306a36Sopenharmony_ci{
86662306a36Sopenharmony_ci	int raw_rc = 0;
86762306a36Sopenharmony_ci	int rc;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	rc = _efx_mcdi_rpc(efx, cmd, inbuf, inlen,
87062306a36Sopenharmony_ci			   outbuf, outlen, outlen_actual, true, &raw_rc);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	if ((rc == -EPROTO) && (raw_rc == MC_CMD_ERR_NO_EVB_PORT) &&
87362306a36Sopenharmony_ci	    efx->type->is_vf) {
87462306a36Sopenharmony_ci		/* If the EVB port isn't available within a VF this may
87562306a36Sopenharmony_ci		 * mean the PF is still bringing the switch up. We should
87662306a36Sopenharmony_ci		 * retry our request shortly.
87762306a36Sopenharmony_ci		 */
87862306a36Sopenharmony_ci		unsigned long abort_time = jiffies + MCDI_RPC_TIMEOUT;
87962306a36Sopenharmony_ci		unsigned int delay_us = 10000;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci		netif_dbg(efx, hw, efx->net_dev,
88262306a36Sopenharmony_ci			  "%s: NO_EVB_PORT; will retry request\n",
88362306a36Sopenharmony_ci			  __func__);
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci		do {
88662306a36Sopenharmony_ci			usleep_range(delay_us, delay_us + 10000);
88762306a36Sopenharmony_ci			rc = _efx_mcdi_rpc(efx, cmd, inbuf, inlen,
88862306a36Sopenharmony_ci					   outbuf, outlen, outlen_actual,
88962306a36Sopenharmony_ci					   true, &raw_rc);
89062306a36Sopenharmony_ci			if (delay_us < 100000)
89162306a36Sopenharmony_ci				delay_us <<= 1;
89262306a36Sopenharmony_ci		} while ((rc == -EPROTO) &&
89362306a36Sopenharmony_ci			 (raw_rc == MC_CMD_ERR_NO_EVB_PORT) &&
89462306a36Sopenharmony_ci			 time_before(jiffies, abort_time));
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	if (rc && !quiet && !(cmd == MC_CMD_REBOOT && rc == -EIO))
89862306a36Sopenharmony_ci		efx_siena_mcdi_display_error(efx, cmd, inlen,
89962306a36Sopenharmony_ci					     outbuf, outlen, rc);
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	return rc;
90262306a36Sopenharmony_ci}
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci/**
90562306a36Sopenharmony_ci * efx_siena_mcdi_rpc - Issue an MCDI command and wait for completion
90662306a36Sopenharmony_ci * @efx: NIC through which to issue the command
90762306a36Sopenharmony_ci * @cmd: Command type number
90862306a36Sopenharmony_ci * @inbuf: Command parameters
90962306a36Sopenharmony_ci * @inlen: Length of command parameters, in bytes.  Must be a multiple
91062306a36Sopenharmony_ci *	of 4 and no greater than %MCDI_CTL_SDU_LEN_MAX_V1.
91162306a36Sopenharmony_ci * @outbuf: Response buffer.  May be %NULL if @outlen is 0.
91262306a36Sopenharmony_ci * @outlen: Length of response buffer, in bytes.  If the actual
91362306a36Sopenharmony_ci *	response is longer than @outlen & ~3, it will be truncated
91462306a36Sopenharmony_ci *	to that length.
91562306a36Sopenharmony_ci * @outlen_actual: Pointer through which to return the actual response
91662306a36Sopenharmony_ci *	length.  May be %NULL if this is not needed.
91762306a36Sopenharmony_ci *
91862306a36Sopenharmony_ci * This function may sleep and therefore must be called in an appropriate
91962306a36Sopenharmony_ci * context.
92062306a36Sopenharmony_ci *
92162306a36Sopenharmony_ci * Return: A negative error code, or zero if successful.  The error
92262306a36Sopenharmony_ci *	code may come from the MCDI response or may indicate a failure
92362306a36Sopenharmony_ci *	to communicate with the MC.  In the former case, the response
92462306a36Sopenharmony_ci *	will still be copied to @outbuf and *@outlen_actual will be
92562306a36Sopenharmony_ci *	set accordingly.  In the latter case, *@outlen_actual will be
92662306a36Sopenharmony_ci *	set to zero.
92762306a36Sopenharmony_ci */
92862306a36Sopenharmony_ciint efx_siena_mcdi_rpc(struct efx_nic *efx, unsigned int cmd,
92962306a36Sopenharmony_ci		       const efx_dword_t *inbuf, size_t inlen,
93062306a36Sopenharmony_ci		       efx_dword_t *outbuf, size_t outlen,
93162306a36Sopenharmony_ci		       size_t *outlen_actual)
93262306a36Sopenharmony_ci{
93362306a36Sopenharmony_ci	return _efx_mcdi_rpc_evb_retry(efx, cmd, inbuf, inlen, outbuf, outlen,
93462306a36Sopenharmony_ci				       outlen_actual, false);
93562306a36Sopenharmony_ci}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci/* Normally, on receiving an error code in the MCDI response,
93862306a36Sopenharmony_ci * efx_siena_mcdi_rpc will log an error message containing (among other
93962306a36Sopenharmony_ci * things) the raw error code, by means of efx_siena_mcdi_display_error.
94062306a36Sopenharmony_ci * This _quiet version suppresses that; if the caller wishes to log
94162306a36Sopenharmony_ci * the error conditionally on the return code, it should call this
94262306a36Sopenharmony_ci * function and is then responsible for calling efx_siena_mcdi_display_error
94362306a36Sopenharmony_ci * as needed.
94462306a36Sopenharmony_ci */
94562306a36Sopenharmony_ciint efx_siena_mcdi_rpc_quiet(struct efx_nic *efx, unsigned int cmd,
94662306a36Sopenharmony_ci			     const efx_dword_t *inbuf, size_t inlen,
94762306a36Sopenharmony_ci			     efx_dword_t *outbuf, size_t outlen,
94862306a36Sopenharmony_ci			     size_t *outlen_actual)
94962306a36Sopenharmony_ci{
95062306a36Sopenharmony_ci	return _efx_mcdi_rpc_evb_retry(efx, cmd, inbuf, inlen, outbuf, outlen,
95162306a36Sopenharmony_ci				       outlen_actual, true);
95262306a36Sopenharmony_ci}
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ciint efx_siena_mcdi_rpc_start(struct efx_nic *efx, unsigned int cmd,
95562306a36Sopenharmony_ci			     const efx_dword_t *inbuf, size_t inlen)
95662306a36Sopenharmony_ci{
95762306a36Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
95862306a36Sopenharmony_ci	int rc;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	rc = efx_mcdi_check_supported(efx, cmd, inlen);
96162306a36Sopenharmony_ci	if (rc)
96262306a36Sopenharmony_ci		return rc;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	if (efx->mc_bist_for_other_fn)
96562306a36Sopenharmony_ci		return -ENETDOWN;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	if (mcdi->mode == MCDI_MODE_FAIL)
96862306a36Sopenharmony_ci		return -ENETDOWN;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	efx_mcdi_acquire_sync(mcdi);
97162306a36Sopenharmony_ci	efx_mcdi_send_request(efx, cmd, inbuf, inlen);
97262306a36Sopenharmony_ci	return 0;
97362306a36Sopenharmony_ci}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_cistatic int _efx_mcdi_rpc_async(struct efx_nic *efx, unsigned int cmd,
97662306a36Sopenharmony_ci			       const efx_dword_t *inbuf, size_t inlen,
97762306a36Sopenharmony_ci			       size_t outlen,
97862306a36Sopenharmony_ci			       efx_mcdi_async_completer *complete,
97962306a36Sopenharmony_ci			       unsigned long cookie, bool quiet)
98062306a36Sopenharmony_ci{
98162306a36Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
98262306a36Sopenharmony_ci	struct efx_mcdi_async_param *async;
98362306a36Sopenharmony_ci	int rc;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	rc = efx_mcdi_check_supported(efx, cmd, inlen);
98662306a36Sopenharmony_ci	if (rc)
98762306a36Sopenharmony_ci		return rc;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	if (efx->mc_bist_for_other_fn)
99062306a36Sopenharmony_ci		return -ENETDOWN;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	async = kmalloc(sizeof(*async) + ALIGN(max(inlen, outlen), 4),
99362306a36Sopenharmony_ci			GFP_ATOMIC);
99462306a36Sopenharmony_ci	if (!async)
99562306a36Sopenharmony_ci		return -ENOMEM;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	async->cmd = cmd;
99862306a36Sopenharmony_ci	async->inlen = inlen;
99962306a36Sopenharmony_ci	async->outlen = outlen;
100062306a36Sopenharmony_ci	async->quiet = quiet;
100162306a36Sopenharmony_ci	async->complete = complete;
100262306a36Sopenharmony_ci	async->cookie = cookie;
100362306a36Sopenharmony_ci	memcpy(async + 1, inbuf, inlen);
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	spin_lock_bh(&mcdi->async_lock);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	if (mcdi->mode == MCDI_MODE_EVENTS) {
100862306a36Sopenharmony_ci		list_add_tail(&async->list, &mcdi->async_list);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci		/* If this is at the front of the queue, try to start it
101162306a36Sopenharmony_ci		 * immediately
101262306a36Sopenharmony_ci		 */
101362306a36Sopenharmony_ci		if (mcdi->async_list.next == &async->list &&
101462306a36Sopenharmony_ci		    efx_mcdi_acquire_async(mcdi)) {
101562306a36Sopenharmony_ci			efx_mcdi_send_request(efx, cmd, inbuf, inlen);
101662306a36Sopenharmony_ci			mod_timer(&mcdi->async_timer,
101762306a36Sopenharmony_ci				  jiffies + MCDI_RPC_TIMEOUT);
101862306a36Sopenharmony_ci		}
101962306a36Sopenharmony_ci	} else {
102062306a36Sopenharmony_ci		kfree(async);
102162306a36Sopenharmony_ci		rc = -ENETDOWN;
102262306a36Sopenharmony_ci	}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	spin_unlock_bh(&mcdi->async_lock);
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	return rc;
102762306a36Sopenharmony_ci}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci/**
103062306a36Sopenharmony_ci * efx_siena_mcdi_rpc_async - Schedule an MCDI command to run asynchronously
103162306a36Sopenharmony_ci * @efx: NIC through which to issue the command
103262306a36Sopenharmony_ci * @cmd: Command type number
103362306a36Sopenharmony_ci * @inbuf: Command parameters
103462306a36Sopenharmony_ci * @inlen: Length of command parameters, in bytes
103562306a36Sopenharmony_ci * @outlen: Length to allocate for response buffer, in bytes
103662306a36Sopenharmony_ci * @complete: Function to be called on completion or cancellation.
103762306a36Sopenharmony_ci * @cookie: Arbitrary value to be passed to @complete.
103862306a36Sopenharmony_ci *
103962306a36Sopenharmony_ci * This function does not sleep and therefore may be called in atomic
104062306a36Sopenharmony_ci * context.  It will fail if event queues are disabled or if MCDI
104162306a36Sopenharmony_ci * event completions have been disabled due to an error.
104262306a36Sopenharmony_ci *
104362306a36Sopenharmony_ci * If it succeeds, the @complete function will be called exactly once
104462306a36Sopenharmony_ci * in atomic context, when one of the following occurs:
104562306a36Sopenharmony_ci * (a) the completion event is received (in NAPI context)
104662306a36Sopenharmony_ci * (b) event queues are disabled (in the process that disables them)
104762306a36Sopenharmony_ci * (c) the request times-out (in timer context)
104862306a36Sopenharmony_ci */
104962306a36Sopenharmony_ciint
105062306a36Sopenharmony_ciefx_siena_mcdi_rpc_async(struct efx_nic *efx, unsigned int cmd,
105162306a36Sopenharmony_ci			 const efx_dword_t *inbuf, size_t inlen, size_t outlen,
105262306a36Sopenharmony_ci			 efx_mcdi_async_completer *complete,
105362306a36Sopenharmony_ci			 unsigned long cookie)
105462306a36Sopenharmony_ci{
105562306a36Sopenharmony_ci	return _efx_mcdi_rpc_async(efx, cmd, inbuf, inlen, outlen, complete,
105662306a36Sopenharmony_ci				   cookie, false);
105762306a36Sopenharmony_ci}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ciint efx_siena_mcdi_rpc_async_quiet(struct efx_nic *efx, unsigned int cmd,
106062306a36Sopenharmony_ci				   const efx_dword_t *inbuf, size_t inlen,
106162306a36Sopenharmony_ci				   size_t outlen,
106262306a36Sopenharmony_ci				   efx_mcdi_async_completer *complete,
106362306a36Sopenharmony_ci				   unsigned long cookie)
106462306a36Sopenharmony_ci{
106562306a36Sopenharmony_ci	return _efx_mcdi_rpc_async(efx, cmd, inbuf, inlen, outlen, complete,
106662306a36Sopenharmony_ci				   cookie, true);
106762306a36Sopenharmony_ci}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ciint efx_siena_mcdi_rpc_finish(struct efx_nic *efx, unsigned int cmd,
107062306a36Sopenharmony_ci			      size_t inlen, efx_dword_t *outbuf, size_t outlen,
107162306a36Sopenharmony_ci			      size_t *outlen_actual)
107262306a36Sopenharmony_ci{
107362306a36Sopenharmony_ci	return _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen,
107462306a36Sopenharmony_ci				    outlen_actual, false, NULL, NULL);
107562306a36Sopenharmony_ci}
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ciint efx_siena_mcdi_rpc_finish_quiet(struct efx_nic *efx, unsigned int cmd,
107862306a36Sopenharmony_ci				    size_t inlen, efx_dword_t *outbuf,
107962306a36Sopenharmony_ci				    size_t outlen, size_t *outlen_actual)
108062306a36Sopenharmony_ci{
108162306a36Sopenharmony_ci	return _efx_mcdi_rpc_finish(efx, cmd, inlen, outbuf, outlen,
108262306a36Sopenharmony_ci				    outlen_actual, true, NULL, NULL);
108362306a36Sopenharmony_ci}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_civoid efx_siena_mcdi_display_error(struct efx_nic *efx, unsigned int cmd,
108662306a36Sopenharmony_ci				  size_t inlen, efx_dword_t *outbuf,
108762306a36Sopenharmony_ci				  size_t outlen, int rc)
108862306a36Sopenharmony_ci{
108962306a36Sopenharmony_ci	int code = 0, err_arg = 0;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	if (outlen >= MC_CMD_ERR_CODE_OFST + 4)
109262306a36Sopenharmony_ci		code = MCDI_DWORD(outbuf, ERR_CODE);
109362306a36Sopenharmony_ci	if (outlen >= MC_CMD_ERR_ARG_OFST + 4)
109462306a36Sopenharmony_ci		err_arg = MCDI_DWORD(outbuf, ERR_ARG);
109562306a36Sopenharmony_ci	netif_cond_dbg(efx, hw, efx->net_dev, rc == -EPERM, err,
109662306a36Sopenharmony_ci		       "MC command 0x%x inlen %zu failed rc=%d (raw=%d) arg=%d\n",
109762306a36Sopenharmony_ci		       cmd, inlen, rc, code, err_arg);
109862306a36Sopenharmony_ci}
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci/* Switch to polled MCDI completions.  This can be called in various
110162306a36Sopenharmony_ci * error conditions with various locks held, so it must be lockless.
110262306a36Sopenharmony_ci * Caller is responsible for flushing asynchronous requests later.
110362306a36Sopenharmony_ci */
110462306a36Sopenharmony_civoid efx_siena_mcdi_mode_poll(struct efx_nic *efx)
110562306a36Sopenharmony_ci{
110662306a36Sopenharmony_ci	struct efx_mcdi_iface *mcdi;
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	if (!efx->mcdi)
110962306a36Sopenharmony_ci		return;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	mcdi = efx_mcdi(efx);
111262306a36Sopenharmony_ci	/* If already in polling mode, nothing to do.
111362306a36Sopenharmony_ci	 * If in fail-fast state, don't switch to polled completion.
111462306a36Sopenharmony_ci	 * FLR recovery will do that later.
111562306a36Sopenharmony_ci	 */
111662306a36Sopenharmony_ci	if (mcdi->mode == MCDI_MODE_POLL || mcdi->mode == MCDI_MODE_FAIL)
111762306a36Sopenharmony_ci		return;
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	/* We can switch from event completion to polled completion, because
112062306a36Sopenharmony_ci	 * mcdi requests are always completed in shared memory. We do this by
112162306a36Sopenharmony_ci	 * switching the mode to POLL'd then completing the request.
112262306a36Sopenharmony_ci	 * efx_mcdi_await_completion() will then call efx_mcdi_poll().
112362306a36Sopenharmony_ci	 *
112462306a36Sopenharmony_ci	 * We need an smp_wmb() to synchronise with efx_mcdi_await_completion(),
112562306a36Sopenharmony_ci	 * which efx_mcdi_complete_sync() provides for us.
112662306a36Sopenharmony_ci	 */
112762306a36Sopenharmony_ci	mcdi->mode = MCDI_MODE_POLL;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	efx_mcdi_complete_sync(mcdi);
113062306a36Sopenharmony_ci}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci/* Flush any running or queued asynchronous requests, after event processing
113362306a36Sopenharmony_ci * is stopped
113462306a36Sopenharmony_ci */
113562306a36Sopenharmony_civoid efx_siena_mcdi_flush_async(struct efx_nic *efx)
113662306a36Sopenharmony_ci{
113762306a36Sopenharmony_ci	struct efx_mcdi_async_param *async, *next;
113862306a36Sopenharmony_ci	struct efx_mcdi_iface *mcdi;
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	if (!efx->mcdi)
114162306a36Sopenharmony_ci		return;
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	mcdi = efx_mcdi(efx);
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	/* We must be in poll or fail mode so no more requests can be queued */
114662306a36Sopenharmony_ci	BUG_ON(mcdi->mode == MCDI_MODE_EVENTS);
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	del_timer_sync(&mcdi->async_timer);
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	/* If a request is still running, make sure we give the MC
115162306a36Sopenharmony_ci	 * time to complete it so that the response won't overwrite our
115262306a36Sopenharmony_ci	 * next request.
115362306a36Sopenharmony_ci	 */
115462306a36Sopenharmony_ci	if (mcdi->state == MCDI_STATE_RUNNING_ASYNC) {
115562306a36Sopenharmony_ci		efx_mcdi_poll(efx);
115662306a36Sopenharmony_ci		mcdi->state = MCDI_STATE_QUIESCENT;
115762306a36Sopenharmony_ci	}
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	/* Nothing else will access the async list now, so it is safe
116062306a36Sopenharmony_ci	 * to walk it without holding async_lock.  If we hold it while
116162306a36Sopenharmony_ci	 * calling a completer then lockdep may warn that we have
116262306a36Sopenharmony_ci	 * acquired locks in the wrong order.
116362306a36Sopenharmony_ci	 */
116462306a36Sopenharmony_ci	list_for_each_entry_safe(async, next, &mcdi->async_list, list) {
116562306a36Sopenharmony_ci		if (async->complete)
116662306a36Sopenharmony_ci			async->complete(efx, async->cookie, -ENETDOWN, NULL, 0);
116762306a36Sopenharmony_ci		list_del(&async->list);
116862306a36Sopenharmony_ci		kfree(async);
116962306a36Sopenharmony_ci	}
117062306a36Sopenharmony_ci}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_civoid efx_siena_mcdi_mode_event(struct efx_nic *efx)
117362306a36Sopenharmony_ci{
117462306a36Sopenharmony_ci	struct efx_mcdi_iface *mcdi;
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	if (!efx->mcdi)
117762306a36Sopenharmony_ci		return;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	mcdi = efx_mcdi(efx);
118062306a36Sopenharmony_ci	/* If already in event completion mode, nothing to do.
118162306a36Sopenharmony_ci	 * If in fail-fast state, don't switch to event completion.  FLR
118262306a36Sopenharmony_ci	 * recovery will do that later.
118362306a36Sopenharmony_ci	 */
118462306a36Sopenharmony_ci	if (mcdi->mode == MCDI_MODE_EVENTS || mcdi->mode == MCDI_MODE_FAIL)
118562306a36Sopenharmony_ci		return;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	/* We can't switch from polled to event completion in the middle of a
118862306a36Sopenharmony_ci	 * request, because the completion method is specified in the request.
118962306a36Sopenharmony_ci	 * So acquire the interface to serialise the requestors. We don't need
119062306a36Sopenharmony_ci	 * to acquire the iface_lock to change the mode here, but we do need a
119162306a36Sopenharmony_ci	 * write memory barrier ensure that efx_siena_mcdi_rpc() sees it, which
119262306a36Sopenharmony_ci	 * efx_mcdi_acquire() provides.
119362306a36Sopenharmony_ci	 */
119462306a36Sopenharmony_ci	efx_mcdi_acquire_sync(mcdi);
119562306a36Sopenharmony_ci	mcdi->mode = MCDI_MODE_EVENTS;
119662306a36Sopenharmony_ci	efx_mcdi_release(mcdi);
119762306a36Sopenharmony_ci}
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_cistatic void efx_mcdi_ev_death(struct efx_nic *efx, int rc)
120062306a36Sopenharmony_ci{
120162306a36Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	/* If there is an outstanding MCDI request, it has been terminated
120462306a36Sopenharmony_ci	 * either by a BADASSERT or REBOOT event. If the mcdi interface is
120562306a36Sopenharmony_ci	 * in polled mode, then do nothing because the MC reboot handler will
120662306a36Sopenharmony_ci	 * set the header correctly. However, if the mcdi interface is waiting
120762306a36Sopenharmony_ci	 * for a CMDDONE event it won't receive it [and since all MCDI events
120862306a36Sopenharmony_ci	 * are sent to the same queue, we can't be racing with
120962306a36Sopenharmony_ci	 * efx_mcdi_ev_cpl()]
121062306a36Sopenharmony_ci	 *
121162306a36Sopenharmony_ci	 * If there is an outstanding asynchronous request, we can't
121262306a36Sopenharmony_ci	 * complete it now (efx_mcdi_complete() would deadlock).  The
121362306a36Sopenharmony_ci	 * reset process will take care of this.
121462306a36Sopenharmony_ci	 *
121562306a36Sopenharmony_ci	 * There's a race here with efx_mcdi_send_request(), because
121662306a36Sopenharmony_ci	 * we might receive a REBOOT event *before* the request has
121762306a36Sopenharmony_ci	 * been copied out. In polled mode (during startup) this is
121862306a36Sopenharmony_ci	 * irrelevant, because efx_mcdi_complete_sync() is ignored. In
121962306a36Sopenharmony_ci	 * event mode, this condition is just an edge-case of
122062306a36Sopenharmony_ci	 * receiving a REBOOT event after posting the MCDI
122162306a36Sopenharmony_ci	 * request. Did the mc reboot before or after the copyout? The
122262306a36Sopenharmony_ci	 * best we can do always is just return failure.
122362306a36Sopenharmony_ci	 *
122462306a36Sopenharmony_ci	 * If there is an outstanding proxy response expected it is not going
122562306a36Sopenharmony_ci	 * to arrive. We should thus abort it.
122662306a36Sopenharmony_ci	 */
122762306a36Sopenharmony_ci	spin_lock(&mcdi->iface_lock);
122862306a36Sopenharmony_ci	efx_mcdi_proxy_abort(mcdi);
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	if (efx_mcdi_complete_sync(mcdi)) {
123162306a36Sopenharmony_ci		if (mcdi->mode == MCDI_MODE_EVENTS) {
123262306a36Sopenharmony_ci			mcdi->resprc = rc;
123362306a36Sopenharmony_ci			mcdi->resp_hdr_len = 0;
123462306a36Sopenharmony_ci			mcdi->resp_data_len = 0;
123562306a36Sopenharmony_ci			++mcdi->credits;
123662306a36Sopenharmony_ci		}
123762306a36Sopenharmony_ci	} else {
123862306a36Sopenharmony_ci		int count;
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci		/* Consume the status word since efx_siena_mcdi_rpc_finish() won't */
124162306a36Sopenharmony_ci		for (count = 0; count < MCDI_STATUS_DELAY_COUNT; ++count) {
124262306a36Sopenharmony_ci			rc = efx_siena_mcdi_poll_reboot(efx);
124362306a36Sopenharmony_ci			if (rc)
124462306a36Sopenharmony_ci				break;
124562306a36Sopenharmony_ci			udelay(MCDI_STATUS_DELAY_US);
124662306a36Sopenharmony_ci		}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci		/* On EF10, a CODE_MC_REBOOT event can be received without the
124962306a36Sopenharmony_ci		 * reboot detection in efx_siena_mcdi_poll_reboot() being triggered.
125062306a36Sopenharmony_ci		 * If zero was returned from the final call to
125162306a36Sopenharmony_ci		 * efx_siena_mcdi_poll_reboot(), the MC reboot wasn't noticed but the
125262306a36Sopenharmony_ci		 * MC has definitely rebooted so prepare for the reset.
125362306a36Sopenharmony_ci		 */
125462306a36Sopenharmony_ci		if (!rc && efx->type->mcdi_reboot_detected)
125562306a36Sopenharmony_ci			efx->type->mcdi_reboot_detected(efx);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci		mcdi->new_epoch = true;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci		/* Nobody was waiting for an MCDI request, so trigger a reset */
126062306a36Sopenharmony_ci		efx_siena_schedule_reset(efx, RESET_TYPE_MC_FAILURE);
126162306a36Sopenharmony_ci	}
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	spin_unlock(&mcdi->iface_lock);
126462306a36Sopenharmony_ci}
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci/* The MC is going down in to BIST mode. set the BIST flag to block
126762306a36Sopenharmony_ci * new MCDI, cancel any outstanding MCDI and schedule a BIST-type reset
126862306a36Sopenharmony_ci * (which doesn't actually execute a reset, it waits for the controlling
126962306a36Sopenharmony_ci * function to reset it).
127062306a36Sopenharmony_ci */
127162306a36Sopenharmony_cistatic void efx_mcdi_ev_bist(struct efx_nic *efx)
127262306a36Sopenharmony_ci{
127362306a36Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	spin_lock(&mcdi->iface_lock);
127662306a36Sopenharmony_ci	efx->mc_bist_for_other_fn = true;
127762306a36Sopenharmony_ci	efx_mcdi_proxy_abort(mcdi);
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	if (efx_mcdi_complete_sync(mcdi)) {
128062306a36Sopenharmony_ci		if (mcdi->mode == MCDI_MODE_EVENTS) {
128162306a36Sopenharmony_ci			mcdi->resprc = -EIO;
128262306a36Sopenharmony_ci			mcdi->resp_hdr_len = 0;
128362306a36Sopenharmony_ci			mcdi->resp_data_len = 0;
128462306a36Sopenharmony_ci			++mcdi->credits;
128562306a36Sopenharmony_ci		}
128662306a36Sopenharmony_ci	}
128762306a36Sopenharmony_ci	mcdi->new_epoch = true;
128862306a36Sopenharmony_ci	efx_siena_schedule_reset(efx, RESET_TYPE_MC_BIST);
128962306a36Sopenharmony_ci	spin_unlock(&mcdi->iface_lock);
129062306a36Sopenharmony_ci}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci/* MCDI timeouts seen, so make all MCDI calls fail-fast and issue an FLR to try
129362306a36Sopenharmony_ci * to recover.
129462306a36Sopenharmony_ci */
129562306a36Sopenharmony_cistatic void efx_mcdi_abandon(struct efx_nic *efx)
129662306a36Sopenharmony_ci{
129762306a36Sopenharmony_ci	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	if (xchg(&mcdi->mode, MCDI_MODE_FAIL) == MCDI_MODE_FAIL)
130062306a36Sopenharmony_ci		return; /* it had already been done */
130162306a36Sopenharmony_ci	netif_dbg(efx, hw, efx->net_dev, "MCDI is timing out; trying to recover\n");
130262306a36Sopenharmony_ci	efx_siena_schedule_reset(efx, RESET_TYPE_MCDI_TIMEOUT);
130362306a36Sopenharmony_ci}
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_cistatic void efx_handle_drain_event(struct efx_nic *efx)
130662306a36Sopenharmony_ci{
130762306a36Sopenharmony_ci	if (atomic_dec_and_test(&efx->active_queues))
130862306a36Sopenharmony_ci		wake_up(&efx->flush_wq);
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	WARN_ON(atomic_read(&efx->active_queues) < 0);
131162306a36Sopenharmony_ci}
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci/* Called from efx_farch_ev_process and efx_ef10_ev_process for MCDI events */
131462306a36Sopenharmony_civoid efx_siena_mcdi_process_event(struct efx_channel *channel,
131562306a36Sopenharmony_ci				  efx_qword_t *event)
131662306a36Sopenharmony_ci{
131762306a36Sopenharmony_ci	struct efx_nic *efx = channel->efx;
131862306a36Sopenharmony_ci	int code = EFX_QWORD_FIELD(*event, MCDI_EVENT_CODE);
131962306a36Sopenharmony_ci	u32 data = EFX_QWORD_FIELD(*event, MCDI_EVENT_DATA);
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	switch (code) {
132262306a36Sopenharmony_ci	case MCDI_EVENT_CODE_BADSSERT:
132362306a36Sopenharmony_ci		netif_err(efx, hw, efx->net_dev,
132462306a36Sopenharmony_ci			  "MC watchdog or assertion failure at 0x%x\n", data);
132562306a36Sopenharmony_ci		efx_mcdi_ev_death(efx, -EINTR);
132662306a36Sopenharmony_ci		break;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	case MCDI_EVENT_CODE_PMNOTICE:
132962306a36Sopenharmony_ci		netif_info(efx, wol, efx->net_dev, "MCDI PM event.\n");
133062306a36Sopenharmony_ci		break;
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	case MCDI_EVENT_CODE_CMDDONE:
133362306a36Sopenharmony_ci		efx_mcdi_ev_cpl(efx,
133462306a36Sopenharmony_ci				MCDI_EVENT_FIELD(*event, CMDDONE_SEQ),
133562306a36Sopenharmony_ci				MCDI_EVENT_FIELD(*event, CMDDONE_DATALEN),
133662306a36Sopenharmony_ci				MCDI_EVENT_FIELD(*event, CMDDONE_ERRNO));
133762306a36Sopenharmony_ci		break;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	case MCDI_EVENT_CODE_LINKCHANGE:
134062306a36Sopenharmony_ci		efx_siena_mcdi_process_link_change(efx, event);
134162306a36Sopenharmony_ci		break;
134262306a36Sopenharmony_ci	case MCDI_EVENT_CODE_SENSOREVT:
134362306a36Sopenharmony_ci		efx_sensor_event(efx, event);
134462306a36Sopenharmony_ci		break;
134562306a36Sopenharmony_ci	case MCDI_EVENT_CODE_SCHEDERR:
134662306a36Sopenharmony_ci		netif_dbg(efx, hw, efx->net_dev,
134762306a36Sopenharmony_ci			  "MC Scheduler alert (0x%x)\n", data);
134862306a36Sopenharmony_ci		break;
134962306a36Sopenharmony_ci	case MCDI_EVENT_CODE_REBOOT:
135062306a36Sopenharmony_ci	case MCDI_EVENT_CODE_MC_REBOOT:
135162306a36Sopenharmony_ci		netif_info(efx, hw, efx->net_dev, "MC Reboot\n");
135262306a36Sopenharmony_ci		efx_mcdi_ev_death(efx, -EIO);
135362306a36Sopenharmony_ci		break;
135462306a36Sopenharmony_ci	case MCDI_EVENT_CODE_MC_BIST:
135562306a36Sopenharmony_ci		netif_info(efx, hw, efx->net_dev, "MC entered BIST mode\n");
135662306a36Sopenharmony_ci		efx_mcdi_ev_bist(efx);
135762306a36Sopenharmony_ci		break;
135862306a36Sopenharmony_ci	case MCDI_EVENT_CODE_MAC_STATS_DMA:
135962306a36Sopenharmony_ci		/* MAC stats are gather lazily.  We can ignore this. */
136062306a36Sopenharmony_ci		break;
136162306a36Sopenharmony_ci	case MCDI_EVENT_CODE_FLR:
136262306a36Sopenharmony_ci		if (efx->type->sriov_flr)
136362306a36Sopenharmony_ci			efx->type->sriov_flr(efx,
136462306a36Sopenharmony_ci					     MCDI_EVENT_FIELD(*event, FLR_VF));
136562306a36Sopenharmony_ci		break;
136662306a36Sopenharmony_ci	case MCDI_EVENT_CODE_PTP_RX:
136762306a36Sopenharmony_ci	case MCDI_EVENT_CODE_PTP_FAULT:
136862306a36Sopenharmony_ci	case MCDI_EVENT_CODE_PTP_PPS:
136962306a36Sopenharmony_ci		efx_siena_ptp_event(efx, event);
137062306a36Sopenharmony_ci		break;
137162306a36Sopenharmony_ci	case MCDI_EVENT_CODE_PTP_TIME:
137262306a36Sopenharmony_ci		efx_siena_time_sync_event(channel, event);
137362306a36Sopenharmony_ci		break;
137462306a36Sopenharmony_ci	case MCDI_EVENT_CODE_TX_FLUSH:
137562306a36Sopenharmony_ci	case MCDI_EVENT_CODE_RX_FLUSH:
137662306a36Sopenharmony_ci		/* Two flush events will be sent: one to the same event
137762306a36Sopenharmony_ci		 * queue as completions, and one to event queue 0.
137862306a36Sopenharmony_ci		 * In the latter case the {RX,TX}_FLUSH_TO_DRIVER
137962306a36Sopenharmony_ci		 * flag will be set, and we should ignore the event
138062306a36Sopenharmony_ci		 * because we want to wait for all completions.
138162306a36Sopenharmony_ci		 */
138262306a36Sopenharmony_ci		BUILD_BUG_ON(MCDI_EVENT_TX_FLUSH_TO_DRIVER_LBN !=
138362306a36Sopenharmony_ci			     MCDI_EVENT_RX_FLUSH_TO_DRIVER_LBN);
138462306a36Sopenharmony_ci		if (!MCDI_EVENT_FIELD(*event, TX_FLUSH_TO_DRIVER))
138562306a36Sopenharmony_ci			efx_handle_drain_event(efx);
138662306a36Sopenharmony_ci		break;
138762306a36Sopenharmony_ci	case MCDI_EVENT_CODE_TX_ERR:
138862306a36Sopenharmony_ci	case MCDI_EVENT_CODE_RX_ERR:
138962306a36Sopenharmony_ci		netif_err(efx, hw, efx->net_dev,
139062306a36Sopenharmony_ci			  "%s DMA error (event: "EFX_QWORD_FMT")\n",
139162306a36Sopenharmony_ci			  code == MCDI_EVENT_CODE_TX_ERR ? "TX" : "RX",
139262306a36Sopenharmony_ci			  EFX_QWORD_VAL(*event));
139362306a36Sopenharmony_ci		efx_siena_schedule_reset(efx, RESET_TYPE_DMA_ERROR);
139462306a36Sopenharmony_ci		break;
139562306a36Sopenharmony_ci	case MCDI_EVENT_CODE_PROXY_RESPONSE:
139662306a36Sopenharmony_ci		efx_mcdi_ev_proxy_response(efx,
139762306a36Sopenharmony_ci				MCDI_EVENT_FIELD(*event, PROXY_RESPONSE_HANDLE),
139862306a36Sopenharmony_ci				MCDI_EVENT_FIELD(*event, PROXY_RESPONSE_RC));
139962306a36Sopenharmony_ci		break;
140062306a36Sopenharmony_ci	default:
140162306a36Sopenharmony_ci		netif_err(efx, hw, efx->net_dev,
140262306a36Sopenharmony_ci			  "Unknown MCDI event " EFX_QWORD_FMT "\n",
140362306a36Sopenharmony_ci			  EFX_QWORD_VAL(*event));
140462306a36Sopenharmony_ci	}
140562306a36Sopenharmony_ci}
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci/**************************************************************************
140862306a36Sopenharmony_ci *
140962306a36Sopenharmony_ci * Specific request functions
141062306a36Sopenharmony_ci *
141162306a36Sopenharmony_ci **************************************************************************
141262306a36Sopenharmony_ci */
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_civoid efx_siena_mcdi_print_fwver(struct efx_nic *efx, char *buf, size_t len)
141562306a36Sopenharmony_ci{
141662306a36Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_VERSION_OUT_LEN);
141762306a36Sopenharmony_ci	size_t outlength;
141862306a36Sopenharmony_ci	const __le16 *ver_words;
141962306a36Sopenharmony_ci	size_t offset;
142062306a36Sopenharmony_ci	int rc;
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_GET_VERSION_IN_LEN != 0);
142362306a36Sopenharmony_ci	rc = efx_siena_mcdi_rpc(efx, MC_CMD_GET_VERSION, NULL, 0,
142462306a36Sopenharmony_ci				outbuf, sizeof(outbuf), &outlength);
142562306a36Sopenharmony_ci	if (rc)
142662306a36Sopenharmony_ci		goto fail;
142762306a36Sopenharmony_ci	if (outlength < MC_CMD_GET_VERSION_OUT_LEN) {
142862306a36Sopenharmony_ci		rc = -EIO;
142962306a36Sopenharmony_ci		goto fail;
143062306a36Sopenharmony_ci	}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	ver_words = (__le16 *)MCDI_PTR(outbuf, GET_VERSION_OUT_VERSION);
143362306a36Sopenharmony_ci	offset = scnprintf(buf, len, "%u.%u.%u.%u",
143462306a36Sopenharmony_ci			   le16_to_cpu(ver_words[0]),
143562306a36Sopenharmony_ci			   le16_to_cpu(ver_words[1]),
143662306a36Sopenharmony_ci			   le16_to_cpu(ver_words[2]),
143762306a36Sopenharmony_ci			   le16_to_cpu(ver_words[3]));
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	if (efx->type->print_additional_fwver)
144062306a36Sopenharmony_ci		offset += efx->type->print_additional_fwver(efx, buf + offset,
144162306a36Sopenharmony_ci							    len - offset);
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	/* It's theoretically possible for the string to exceed 31
144462306a36Sopenharmony_ci	 * characters, though in practice the first three version
144562306a36Sopenharmony_ci	 * components are short enough that this doesn't happen.
144662306a36Sopenharmony_ci	 */
144762306a36Sopenharmony_ci	if (WARN_ON(offset >= len))
144862306a36Sopenharmony_ci		buf[0] = 0;
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	return;
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_cifail:
145362306a36Sopenharmony_ci	netif_err(efx, probe, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
145462306a36Sopenharmony_ci	buf[0] = 0;
145562306a36Sopenharmony_ci}
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_cistatic int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating,
145862306a36Sopenharmony_ci			       bool *was_attached)
145962306a36Sopenharmony_ci{
146062306a36Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_DRV_ATTACH_IN_LEN);
146162306a36Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_DRV_ATTACH_EXT_OUT_LEN);
146262306a36Sopenharmony_ci	size_t outlen;
146362306a36Sopenharmony_ci	int rc;
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, DRV_ATTACH_IN_NEW_STATE,
146662306a36Sopenharmony_ci		       driver_operating ? 1 : 0);
146762306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, DRV_ATTACH_IN_UPDATE, 1);
146862306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, DRV_ATTACH_IN_FIRMWARE_ID, MC_CMD_FW_LOW_LATENCY);
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	rc = efx_siena_mcdi_rpc_quiet(efx, MC_CMD_DRV_ATTACH, inbuf,
147162306a36Sopenharmony_ci				      sizeof(inbuf), outbuf, sizeof(outbuf),
147262306a36Sopenharmony_ci				      &outlen);
147362306a36Sopenharmony_ci	/* If we're not the primary PF, trying to ATTACH with a FIRMWARE_ID
147462306a36Sopenharmony_ci	 * specified will fail with EPERM, and we have to tell the MC we don't
147562306a36Sopenharmony_ci	 * care what firmware we get.
147662306a36Sopenharmony_ci	 */
147762306a36Sopenharmony_ci	if (rc == -EPERM) {
147862306a36Sopenharmony_ci		netif_dbg(efx, probe, efx->net_dev,
147962306a36Sopenharmony_ci			  "efx_mcdi_drv_attach with fw-variant setting failed EPERM, trying without it\n");
148062306a36Sopenharmony_ci		MCDI_SET_DWORD(inbuf, DRV_ATTACH_IN_FIRMWARE_ID,
148162306a36Sopenharmony_ci			       MC_CMD_FW_DONT_CARE);
148262306a36Sopenharmony_ci		rc = efx_siena_mcdi_rpc_quiet(efx, MC_CMD_DRV_ATTACH, inbuf,
148362306a36Sopenharmony_ci					      sizeof(inbuf), outbuf,
148462306a36Sopenharmony_ci					      sizeof(outbuf), &outlen);
148562306a36Sopenharmony_ci	}
148662306a36Sopenharmony_ci	if (rc) {
148762306a36Sopenharmony_ci		efx_siena_mcdi_display_error(efx, MC_CMD_DRV_ATTACH,
148862306a36Sopenharmony_ci					     sizeof(inbuf), outbuf, outlen, rc);
148962306a36Sopenharmony_ci		goto fail;
149062306a36Sopenharmony_ci	}
149162306a36Sopenharmony_ci	if (outlen < MC_CMD_DRV_ATTACH_OUT_LEN) {
149262306a36Sopenharmony_ci		rc = -EIO;
149362306a36Sopenharmony_ci		goto fail;
149462306a36Sopenharmony_ci	}
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	if (driver_operating) {
149762306a36Sopenharmony_ci		if (outlen >= MC_CMD_DRV_ATTACH_EXT_OUT_LEN) {
149862306a36Sopenharmony_ci			efx->mcdi->fn_flags =
149962306a36Sopenharmony_ci				MCDI_DWORD(outbuf,
150062306a36Sopenharmony_ci					   DRV_ATTACH_EXT_OUT_FUNC_FLAGS);
150162306a36Sopenharmony_ci		} else {
150262306a36Sopenharmony_ci			/* Synthesise flags for Siena */
150362306a36Sopenharmony_ci			efx->mcdi->fn_flags =
150462306a36Sopenharmony_ci				1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_LINKCTRL |
150562306a36Sopenharmony_ci				1 << MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_TRUSTED |
150662306a36Sopenharmony_ci				(efx_port_num(efx) == 0) <<
150762306a36Sopenharmony_ci				MC_CMD_DRV_ATTACH_EXT_OUT_FLAG_PRIMARY;
150862306a36Sopenharmony_ci		}
150962306a36Sopenharmony_ci	}
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	/* We currently assume we have control of the external link
151262306a36Sopenharmony_ci	 * and are completely trusted by firmware.  Abort probing
151362306a36Sopenharmony_ci	 * if that's not true for this function.
151462306a36Sopenharmony_ci	 */
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	if (was_attached != NULL)
151762306a36Sopenharmony_ci		*was_attached = MCDI_DWORD(outbuf, DRV_ATTACH_OUT_OLD_STATE);
151862306a36Sopenharmony_ci	return 0;
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_cifail:
152162306a36Sopenharmony_ci	netif_err(efx, probe, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
152262306a36Sopenharmony_ci	return rc;
152362306a36Sopenharmony_ci}
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ciint efx_siena_mcdi_get_board_cfg(struct efx_nic *efx, u8 *mac_address,
152662306a36Sopenharmony_ci				 u16 *fw_subtype_list, u32 *capabilities)
152762306a36Sopenharmony_ci{
152862306a36Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_BOARD_CFG_OUT_LENMAX);
152962306a36Sopenharmony_ci	size_t outlen, i;
153062306a36Sopenharmony_ci	int port_num = efx_port_num(efx);
153162306a36Sopenharmony_ci	int rc;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_GET_BOARD_CFG_IN_LEN != 0);
153462306a36Sopenharmony_ci	/* we need __aligned(2) for ether_addr_copy */
153562306a36Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0_OFST & 1);
153662306a36Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1_OFST & 1);
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	rc = efx_siena_mcdi_rpc(efx, MC_CMD_GET_BOARD_CFG, NULL, 0,
153962306a36Sopenharmony_ci				outbuf, sizeof(outbuf), &outlen);
154062306a36Sopenharmony_ci	if (rc)
154162306a36Sopenharmony_ci		goto fail;
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	if (outlen < MC_CMD_GET_BOARD_CFG_OUT_LENMIN) {
154462306a36Sopenharmony_ci		rc = -EIO;
154562306a36Sopenharmony_ci		goto fail;
154662306a36Sopenharmony_ci	}
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	if (mac_address)
154962306a36Sopenharmony_ci		ether_addr_copy(mac_address,
155062306a36Sopenharmony_ci				port_num ?
155162306a36Sopenharmony_ci				MCDI_PTR(outbuf, GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1) :
155262306a36Sopenharmony_ci				MCDI_PTR(outbuf, GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0));
155362306a36Sopenharmony_ci	if (fw_subtype_list) {
155462306a36Sopenharmony_ci		for (i = 0;
155562306a36Sopenharmony_ci		     i < MCDI_VAR_ARRAY_LEN(outlen,
155662306a36Sopenharmony_ci					    GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST);
155762306a36Sopenharmony_ci		     i++)
155862306a36Sopenharmony_ci			fw_subtype_list[i] = MCDI_ARRAY_WORD(
155962306a36Sopenharmony_ci				outbuf, GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST, i);
156062306a36Sopenharmony_ci		for (; i < MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_MAXNUM; i++)
156162306a36Sopenharmony_ci			fw_subtype_list[i] = 0;
156262306a36Sopenharmony_ci	}
156362306a36Sopenharmony_ci	if (capabilities) {
156462306a36Sopenharmony_ci		if (port_num)
156562306a36Sopenharmony_ci			*capabilities = MCDI_DWORD(outbuf,
156662306a36Sopenharmony_ci					GET_BOARD_CFG_OUT_CAPABILITIES_PORT1);
156762306a36Sopenharmony_ci		else
156862306a36Sopenharmony_ci			*capabilities = MCDI_DWORD(outbuf,
156962306a36Sopenharmony_ci					GET_BOARD_CFG_OUT_CAPABILITIES_PORT0);
157062306a36Sopenharmony_ci	}
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	return 0;
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_cifail:
157562306a36Sopenharmony_ci	netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d len=%d\n",
157662306a36Sopenharmony_ci		  __func__, rc, (int)outlen);
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	return rc;
157962306a36Sopenharmony_ci}
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ciint efx_siena_mcdi_log_ctrl(struct efx_nic *efx, bool evq, bool uart,
158262306a36Sopenharmony_ci			    u32 dest_evq)
158362306a36Sopenharmony_ci{
158462306a36Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_LOG_CTRL_IN_LEN);
158562306a36Sopenharmony_ci	u32 dest = 0;
158662306a36Sopenharmony_ci	int rc;
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	if (uart)
158962306a36Sopenharmony_ci		dest |= MC_CMD_LOG_CTRL_IN_LOG_DEST_UART;
159062306a36Sopenharmony_ci	if (evq)
159162306a36Sopenharmony_ci		dest |= MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ;
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, LOG_CTRL_IN_LOG_DEST, dest);
159462306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, LOG_CTRL_IN_LOG_DEST_EVQ, dest_evq);
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_LOG_CTRL_OUT_LEN != 0);
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	rc = efx_siena_mcdi_rpc(efx, MC_CMD_LOG_CTRL, inbuf, sizeof(inbuf),
159962306a36Sopenharmony_ci				NULL, 0, NULL);
160062306a36Sopenharmony_ci	return rc;
160162306a36Sopenharmony_ci}
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ciint efx_siena_mcdi_nvram_types(struct efx_nic *efx, u32 *nvram_types_out)
160462306a36Sopenharmony_ci{
160562306a36Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_TYPES_OUT_LEN);
160662306a36Sopenharmony_ci	size_t outlen;
160762306a36Sopenharmony_ci	int rc;
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_NVRAM_TYPES_IN_LEN != 0);
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	rc = efx_siena_mcdi_rpc(efx, MC_CMD_NVRAM_TYPES, NULL, 0,
161262306a36Sopenharmony_ci				outbuf, sizeof(outbuf), &outlen);
161362306a36Sopenharmony_ci	if (rc)
161462306a36Sopenharmony_ci		goto fail;
161562306a36Sopenharmony_ci	if (outlen < MC_CMD_NVRAM_TYPES_OUT_LEN) {
161662306a36Sopenharmony_ci		rc = -EIO;
161762306a36Sopenharmony_ci		goto fail;
161862306a36Sopenharmony_ci	}
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	*nvram_types_out = MCDI_DWORD(outbuf, NVRAM_TYPES_OUT_TYPES);
162162306a36Sopenharmony_ci	return 0;
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_cifail:
162462306a36Sopenharmony_ci	netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n",
162562306a36Sopenharmony_ci		  __func__, rc);
162662306a36Sopenharmony_ci	return rc;
162762306a36Sopenharmony_ci}
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ciint efx_siena_mcdi_nvram_info(struct efx_nic *efx, unsigned int type,
163062306a36Sopenharmony_ci			      size_t *size_out, size_t *erase_size_out,
163162306a36Sopenharmony_ci			      bool *protected_out)
163262306a36Sopenharmony_ci{
163362306a36Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_INFO_IN_LEN);
163462306a36Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_INFO_OUT_LEN);
163562306a36Sopenharmony_ci	size_t outlen;
163662306a36Sopenharmony_ci	int rc;
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_INFO_IN_TYPE, type);
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	rc = efx_siena_mcdi_rpc(efx, MC_CMD_NVRAM_INFO, inbuf, sizeof(inbuf),
164162306a36Sopenharmony_ci				outbuf, sizeof(outbuf), &outlen);
164262306a36Sopenharmony_ci	if (rc)
164362306a36Sopenharmony_ci		goto fail;
164462306a36Sopenharmony_ci	if (outlen < MC_CMD_NVRAM_INFO_OUT_LEN) {
164562306a36Sopenharmony_ci		rc = -EIO;
164662306a36Sopenharmony_ci		goto fail;
164762306a36Sopenharmony_ci	}
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci	*size_out = MCDI_DWORD(outbuf, NVRAM_INFO_OUT_SIZE);
165062306a36Sopenharmony_ci	*erase_size_out = MCDI_DWORD(outbuf, NVRAM_INFO_OUT_ERASESIZE);
165162306a36Sopenharmony_ci	*protected_out = !!(MCDI_DWORD(outbuf, NVRAM_INFO_OUT_FLAGS) &
165262306a36Sopenharmony_ci				(1 << MC_CMD_NVRAM_INFO_OUT_PROTECTED_LBN));
165362306a36Sopenharmony_ci	return 0;
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_cifail:
165662306a36Sopenharmony_ci	netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
165762306a36Sopenharmony_ci	return rc;
165862306a36Sopenharmony_ci}
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_cistatic int efx_mcdi_nvram_test(struct efx_nic *efx, unsigned int type)
166162306a36Sopenharmony_ci{
166262306a36Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_TEST_IN_LEN);
166362306a36Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_TEST_OUT_LEN);
166462306a36Sopenharmony_ci	int rc;
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_TEST_IN_TYPE, type);
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	rc = efx_siena_mcdi_rpc(efx, MC_CMD_NVRAM_TEST, inbuf, sizeof(inbuf),
166962306a36Sopenharmony_ci				outbuf, sizeof(outbuf), NULL);
167062306a36Sopenharmony_ci	if (rc)
167162306a36Sopenharmony_ci		return rc;
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	switch (MCDI_DWORD(outbuf, NVRAM_TEST_OUT_RESULT)) {
167462306a36Sopenharmony_ci	case MC_CMD_NVRAM_TEST_PASS:
167562306a36Sopenharmony_ci	case MC_CMD_NVRAM_TEST_NOTSUPP:
167662306a36Sopenharmony_ci		return 0;
167762306a36Sopenharmony_ci	default:
167862306a36Sopenharmony_ci		return -EIO;
167962306a36Sopenharmony_ci	}
168062306a36Sopenharmony_ci}
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ciint efx_siena_mcdi_nvram_test_all(struct efx_nic *efx)
168362306a36Sopenharmony_ci{
168462306a36Sopenharmony_ci	u32 nvram_types;
168562306a36Sopenharmony_ci	unsigned int type;
168662306a36Sopenharmony_ci	int rc;
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci	rc = efx_siena_mcdi_nvram_types(efx, &nvram_types);
168962306a36Sopenharmony_ci	if (rc)
169062306a36Sopenharmony_ci		goto fail1;
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	type = 0;
169362306a36Sopenharmony_ci	while (nvram_types != 0) {
169462306a36Sopenharmony_ci		if (nvram_types & 1) {
169562306a36Sopenharmony_ci			rc = efx_mcdi_nvram_test(efx, type);
169662306a36Sopenharmony_ci			if (rc)
169762306a36Sopenharmony_ci				goto fail2;
169862306a36Sopenharmony_ci		}
169962306a36Sopenharmony_ci		type++;
170062306a36Sopenharmony_ci		nvram_types >>= 1;
170162306a36Sopenharmony_ci	}
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci	return 0;
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_cifail2:
170662306a36Sopenharmony_ci	netif_err(efx, hw, efx->net_dev, "%s: failed type=%u\n",
170762306a36Sopenharmony_ci		  __func__, type);
170862306a36Sopenharmony_cifail1:
170962306a36Sopenharmony_ci	netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
171062306a36Sopenharmony_ci	return rc;
171162306a36Sopenharmony_ci}
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci/* Returns 1 if an assertion was read, 0 if no assertion had fired,
171462306a36Sopenharmony_ci * negative on error.
171562306a36Sopenharmony_ci */
171662306a36Sopenharmony_cistatic int efx_mcdi_read_assertion(struct efx_nic *efx)
171762306a36Sopenharmony_ci{
171862306a36Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_GET_ASSERTS_IN_LEN);
171962306a36Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_ASSERTS_OUT_LEN);
172062306a36Sopenharmony_ci	unsigned int flags, index;
172162306a36Sopenharmony_ci	const char *reason;
172262306a36Sopenharmony_ci	size_t outlen;
172362306a36Sopenharmony_ci	int retry;
172462306a36Sopenharmony_ci	int rc;
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	/* Attempt to read any stored assertion state before we reboot
172762306a36Sopenharmony_ci	 * the mcfw out of the assertion handler. Retry twice, once
172862306a36Sopenharmony_ci	 * because a boot-time assertion might cause this command to fail
172962306a36Sopenharmony_ci	 * with EINTR. And once again because GET_ASSERTS can race with
173062306a36Sopenharmony_ci	 * MC_CMD_REBOOT running on the other port. */
173162306a36Sopenharmony_ci	retry = 2;
173262306a36Sopenharmony_ci	do {
173362306a36Sopenharmony_ci		MCDI_SET_DWORD(inbuf, GET_ASSERTS_IN_CLEAR, 1);
173462306a36Sopenharmony_ci		rc = efx_siena_mcdi_rpc_quiet(efx, MC_CMD_GET_ASSERTS,
173562306a36Sopenharmony_ci					      inbuf, MC_CMD_GET_ASSERTS_IN_LEN,
173662306a36Sopenharmony_ci					      outbuf, sizeof(outbuf), &outlen);
173762306a36Sopenharmony_ci		if (rc == -EPERM)
173862306a36Sopenharmony_ci			return 0;
173962306a36Sopenharmony_ci	} while ((rc == -EINTR || rc == -EIO) && retry-- > 0);
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	if (rc) {
174262306a36Sopenharmony_ci		efx_siena_mcdi_display_error(efx, MC_CMD_GET_ASSERTS,
174362306a36Sopenharmony_ci					     MC_CMD_GET_ASSERTS_IN_LEN, outbuf,
174462306a36Sopenharmony_ci					     outlen, rc);
174562306a36Sopenharmony_ci		return rc;
174662306a36Sopenharmony_ci	}
174762306a36Sopenharmony_ci	if (outlen < MC_CMD_GET_ASSERTS_OUT_LEN)
174862306a36Sopenharmony_ci		return -EIO;
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	/* Print out any recorded assertion state */
175162306a36Sopenharmony_ci	flags = MCDI_DWORD(outbuf, GET_ASSERTS_OUT_GLOBAL_FLAGS);
175262306a36Sopenharmony_ci	if (flags == MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS)
175362306a36Sopenharmony_ci		return 0;
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	reason = (flags == MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL)
175662306a36Sopenharmony_ci		? "system-level assertion"
175762306a36Sopenharmony_ci		: (flags == MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL)
175862306a36Sopenharmony_ci		? "thread-level assertion"
175962306a36Sopenharmony_ci		: (flags == MC_CMD_GET_ASSERTS_FLAGS_WDOG_FIRED)
176062306a36Sopenharmony_ci		? "watchdog reset"
176162306a36Sopenharmony_ci		: "unknown assertion";
176262306a36Sopenharmony_ci	netif_err(efx, hw, efx->net_dev,
176362306a36Sopenharmony_ci		  "MCPU %s at PC = 0x%.8x in thread 0x%.8x\n", reason,
176462306a36Sopenharmony_ci		  MCDI_DWORD(outbuf, GET_ASSERTS_OUT_SAVED_PC_OFFS),
176562306a36Sopenharmony_ci		  MCDI_DWORD(outbuf, GET_ASSERTS_OUT_THREAD_OFFS));
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	/* Print out the registers */
176862306a36Sopenharmony_ci	for (index = 0;
176962306a36Sopenharmony_ci	     index < MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_NUM;
177062306a36Sopenharmony_ci	     index++)
177162306a36Sopenharmony_ci		netif_err(efx, hw, efx->net_dev, "R%.2d (?): 0x%.8x\n",
177262306a36Sopenharmony_ci			  1 + index,
177362306a36Sopenharmony_ci			  MCDI_ARRAY_DWORD(outbuf, GET_ASSERTS_OUT_GP_REGS_OFFS,
177462306a36Sopenharmony_ci					   index));
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	return 1;
177762306a36Sopenharmony_ci}
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_cistatic int efx_mcdi_exit_assertion(struct efx_nic *efx)
178062306a36Sopenharmony_ci{
178162306a36Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_REBOOT_IN_LEN);
178262306a36Sopenharmony_ci	int rc;
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	/* If the MC is running debug firmware, it might now be
178562306a36Sopenharmony_ci	 * waiting for a debugger to attach, but we just want it to
178662306a36Sopenharmony_ci	 * reboot.  We set a flag that makes the command a no-op if it
178762306a36Sopenharmony_ci	 * has already done so.
178862306a36Sopenharmony_ci	 * The MCDI will thus return either 0 or -EIO.
178962306a36Sopenharmony_ci	 */
179062306a36Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_REBOOT_OUT_LEN != 0);
179162306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, REBOOT_IN_FLAGS,
179262306a36Sopenharmony_ci		       MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION);
179362306a36Sopenharmony_ci	rc = efx_siena_mcdi_rpc_quiet(efx, MC_CMD_REBOOT, inbuf,
179462306a36Sopenharmony_ci				      MC_CMD_REBOOT_IN_LEN, NULL, 0, NULL);
179562306a36Sopenharmony_ci	if (rc == -EIO)
179662306a36Sopenharmony_ci		rc = 0;
179762306a36Sopenharmony_ci	if (rc)
179862306a36Sopenharmony_ci		efx_siena_mcdi_display_error(efx, MC_CMD_REBOOT,
179962306a36Sopenharmony_ci					     MC_CMD_REBOOT_IN_LEN, NULL, 0, rc);
180062306a36Sopenharmony_ci	return rc;
180162306a36Sopenharmony_ci}
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ciint efx_siena_mcdi_handle_assertion(struct efx_nic *efx)
180462306a36Sopenharmony_ci{
180562306a36Sopenharmony_ci	int rc;
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci	rc = efx_mcdi_read_assertion(efx);
180862306a36Sopenharmony_ci	if (rc <= 0)
180962306a36Sopenharmony_ci		return rc;
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	return efx_mcdi_exit_assertion(efx);
181262306a36Sopenharmony_ci}
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ciint efx_siena_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode)
181562306a36Sopenharmony_ci{
181662306a36Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_SET_ID_LED_IN_LEN);
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci	BUILD_BUG_ON(EFX_LED_OFF != MC_CMD_LED_OFF);
181962306a36Sopenharmony_ci	BUILD_BUG_ON(EFX_LED_ON != MC_CMD_LED_ON);
182062306a36Sopenharmony_ci	BUILD_BUG_ON(EFX_LED_DEFAULT != MC_CMD_LED_DEFAULT);
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_SET_ID_LED_OUT_LEN != 0);
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, SET_ID_LED_IN_STATE, mode);
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	return efx_siena_mcdi_rpc(efx, MC_CMD_SET_ID_LED, inbuf, sizeof(inbuf),
182762306a36Sopenharmony_ci				  NULL, 0, NULL);
182862306a36Sopenharmony_ci}
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_cistatic int efx_mcdi_reset_func(struct efx_nic *efx)
183162306a36Sopenharmony_ci{
183262306a36Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_ENTITY_RESET_IN_LEN);
183362306a36Sopenharmony_ci	int rc;
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_ENTITY_RESET_OUT_LEN != 0);
183662306a36Sopenharmony_ci	MCDI_POPULATE_DWORD_1(inbuf, ENTITY_RESET_IN_FLAG,
183762306a36Sopenharmony_ci			      ENTITY_RESET_IN_FUNCTION_RESOURCE_RESET, 1);
183862306a36Sopenharmony_ci	rc = efx_siena_mcdi_rpc(efx, MC_CMD_ENTITY_RESET, inbuf, sizeof(inbuf),
183962306a36Sopenharmony_ci				NULL, 0, NULL);
184062306a36Sopenharmony_ci	return rc;
184162306a36Sopenharmony_ci}
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_cistatic int efx_mcdi_reset_mc(struct efx_nic *efx)
184462306a36Sopenharmony_ci{
184562306a36Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_REBOOT_IN_LEN);
184662306a36Sopenharmony_ci	int rc;
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_REBOOT_OUT_LEN != 0);
184962306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, REBOOT_IN_FLAGS, 0);
185062306a36Sopenharmony_ci	rc = efx_siena_mcdi_rpc(efx, MC_CMD_REBOOT, inbuf, sizeof(inbuf),
185162306a36Sopenharmony_ci				NULL, 0, NULL);
185262306a36Sopenharmony_ci	/* White is black, and up is down */
185362306a36Sopenharmony_ci	if (rc == -EIO)
185462306a36Sopenharmony_ci		return 0;
185562306a36Sopenharmony_ci	if (rc == 0)
185662306a36Sopenharmony_ci		rc = -EIO;
185762306a36Sopenharmony_ci	return rc;
185862306a36Sopenharmony_ci}
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_cienum reset_type efx_siena_mcdi_map_reset_reason(enum reset_type reason)
186162306a36Sopenharmony_ci{
186262306a36Sopenharmony_ci	return RESET_TYPE_RECOVER_OR_ALL;
186362306a36Sopenharmony_ci}
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ciint efx_siena_mcdi_reset(struct efx_nic *efx, enum reset_type method)
186662306a36Sopenharmony_ci{
186762306a36Sopenharmony_ci	int rc;
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	/* If MCDI is down, we can't handle_assertion */
187062306a36Sopenharmony_ci	if (method == RESET_TYPE_MCDI_TIMEOUT) {
187162306a36Sopenharmony_ci		rc = pci_reset_function(efx->pci_dev);
187262306a36Sopenharmony_ci		if (rc)
187362306a36Sopenharmony_ci			return rc;
187462306a36Sopenharmony_ci		/* Re-enable polled MCDI completion */
187562306a36Sopenharmony_ci		if (efx->mcdi) {
187662306a36Sopenharmony_ci			struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
187762306a36Sopenharmony_ci			mcdi->mode = MCDI_MODE_POLL;
187862306a36Sopenharmony_ci		}
187962306a36Sopenharmony_ci		return 0;
188062306a36Sopenharmony_ci	}
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci	/* Recover from a failed assertion pre-reset */
188362306a36Sopenharmony_ci	rc = efx_siena_mcdi_handle_assertion(efx);
188462306a36Sopenharmony_ci	if (rc)
188562306a36Sopenharmony_ci		return rc;
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	if (method == RESET_TYPE_DATAPATH)
188862306a36Sopenharmony_ci		return 0;
188962306a36Sopenharmony_ci	else if (method == RESET_TYPE_WORLD)
189062306a36Sopenharmony_ci		return efx_mcdi_reset_mc(efx);
189162306a36Sopenharmony_ci	else
189262306a36Sopenharmony_ci		return efx_mcdi_reset_func(efx);
189362306a36Sopenharmony_ci}
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_cistatic int efx_mcdi_wol_filter_set(struct efx_nic *efx, u32 type,
189662306a36Sopenharmony_ci				   const u8 *mac, int *id_out)
189762306a36Sopenharmony_ci{
189862306a36Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_WOL_FILTER_SET_IN_LEN);
189962306a36Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_WOL_FILTER_SET_OUT_LEN);
190062306a36Sopenharmony_ci	size_t outlen;
190162306a36Sopenharmony_ci	int rc;
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, WOL_FILTER_SET_IN_WOL_TYPE, type);
190462306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, WOL_FILTER_SET_IN_FILTER_MODE,
190562306a36Sopenharmony_ci		       MC_CMD_FILTER_MODE_SIMPLE);
190662306a36Sopenharmony_ci	ether_addr_copy(MCDI_PTR(inbuf, WOL_FILTER_SET_IN_MAGIC_MAC), mac);
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci	rc = efx_siena_mcdi_rpc(efx, MC_CMD_WOL_FILTER_SET, inbuf,
190962306a36Sopenharmony_ci				sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);
191062306a36Sopenharmony_ci	if (rc)
191162306a36Sopenharmony_ci		goto fail;
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	if (outlen < MC_CMD_WOL_FILTER_SET_OUT_LEN) {
191462306a36Sopenharmony_ci		rc = -EIO;
191562306a36Sopenharmony_ci		goto fail;
191662306a36Sopenharmony_ci	}
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ci	*id_out = (int)MCDI_DWORD(outbuf, WOL_FILTER_SET_OUT_FILTER_ID);
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci	return 0;
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_cifail:
192362306a36Sopenharmony_ci	*id_out = -1;
192462306a36Sopenharmony_ci	netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
192562306a36Sopenharmony_ci	return rc;
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci}
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ciint efx_siena_mcdi_wol_filter_set_magic(struct efx_nic *efx,  const u8 *mac,
193162306a36Sopenharmony_ci					int *id_out)
193262306a36Sopenharmony_ci{
193362306a36Sopenharmony_ci	return efx_mcdi_wol_filter_set(efx, MC_CMD_WOL_TYPE_MAGIC, mac, id_out);
193462306a36Sopenharmony_ci}
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ciint efx_siena_mcdi_wol_filter_get_magic(struct efx_nic *efx, int *id_out)
193862306a36Sopenharmony_ci{
193962306a36Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_WOL_FILTER_GET_OUT_LEN);
194062306a36Sopenharmony_ci	size_t outlen;
194162306a36Sopenharmony_ci	int rc;
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci	rc = efx_siena_mcdi_rpc(efx, MC_CMD_WOL_FILTER_GET, NULL, 0,
194462306a36Sopenharmony_ci				outbuf, sizeof(outbuf), &outlen);
194562306a36Sopenharmony_ci	if (rc)
194662306a36Sopenharmony_ci		goto fail;
194762306a36Sopenharmony_ci
194862306a36Sopenharmony_ci	if (outlen < MC_CMD_WOL_FILTER_GET_OUT_LEN) {
194962306a36Sopenharmony_ci		rc = -EIO;
195062306a36Sopenharmony_ci		goto fail;
195162306a36Sopenharmony_ci	}
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci	*id_out = (int)MCDI_DWORD(outbuf, WOL_FILTER_GET_OUT_FILTER_ID);
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ci	return 0;
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_cifail:
195862306a36Sopenharmony_ci	*id_out = -1;
195962306a36Sopenharmony_ci	netif_err(efx, hw, efx->net_dev, "%s: failed rc=%d\n", __func__, rc);
196062306a36Sopenharmony_ci	return rc;
196162306a36Sopenharmony_ci}
196262306a36Sopenharmony_ci
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ciint efx_siena_mcdi_wol_filter_remove(struct efx_nic *efx, int id)
196562306a36Sopenharmony_ci{
196662306a36Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_WOL_FILTER_REMOVE_IN_LEN);
196762306a36Sopenharmony_ci	int rc;
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, WOL_FILTER_REMOVE_IN_FILTER_ID, (u32)id);
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	rc = efx_siena_mcdi_rpc(efx, MC_CMD_WOL_FILTER_REMOVE, inbuf,
197262306a36Sopenharmony_ci				sizeof(inbuf), NULL, 0, NULL);
197362306a36Sopenharmony_ci	return rc;
197462306a36Sopenharmony_ci}
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_ciint efx_siena_mcdi_flush_rxqs(struct efx_nic *efx)
197762306a36Sopenharmony_ci{
197862306a36Sopenharmony_ci	struct efx_channel *channel;
197962306a36Sopenharmony_ci	struct efx_rx_queue *rx_queue;
198062306a36Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf,
198162306a36Sopenharmony_ci			 MC_CMD_FLUSH_RX_QUEUES_IN_LEN(EFX_MAX_CHANNELS));
198262306a36Sopenharmony_ci	int rc, count;
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_ci	BUILD_BUG_ON(EFX_MAX_CHANNELS >
198562306a36Sopenharmony_ci		     MC_CMD_FLUSH_RX_QUEUES_IN_QID_OFST_MAXNUM);
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci	count = 0;
198862306a36Sopenharmony_ci	efx_for_each_channel(channel, efx) {
198962306a36Sopenharmony_ci		efx_for_each_channel_rx_queue(rx_queue, channel) {
199062306a36Sopenharmony_ci			if (rx_queue->flush_pending) {
199162306a36Sopenharmony_ci				rx_queue->flush_pending = false;
199262306a36Sopenharmony_ci				atomic_dec(&efx->rxq_flush_pending);
199362306a36Sopenharmony_ci				MCDI_SET_ARRAY_DWORD(
199462306a36Sopenharmony_ci					inbuf, FLUSH_RX_QUEUES_IN_QID_OFST,
199562306a36Sopenharmony_ci					count, efx_rx_queue_index(rx_queue));
199662306a36Sopenharmony_ci				count++;
199762306a36Sopenharmony_ci			}
199862306a36Sopenharmony_ci		}
199962306a36Sopenharmony_ci	}
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_ci	rc = efx_siena_mcdi_rpc(efx, MC_CMD_FLUSH_RX_QUEUES, inbuf,
200262306a36Sopenharmony_ci				MC_CMD_FLUSH_RX_QUEUES_IN_LEN(count),
200362306a36Sopenharmony_ci				NULL, 0, NULL);
200462306a36Sopenharmony_ci	WARN_ON(rc < 0);
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ci	return rc;
200762306a36Sopenharmony_ci}
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ciint efx_siena_mcdi_wol_filter_reset(struct efx_nic *efx)
201062306a36Sopenharmony_ci{
201162306a36Sopenharmony_ci	int rc;
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci	rc = efx_siena_mcdi_rpc(efx, MC_CMD_WOL_FILTER_RESET, NULL, 0,
201462306a36Sopenharmony_ci				NULL, 0, NULL);
201562306a36Sopenharmony_ci	return rc;
201662306a36Sopenharmony_ci}
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci#ifdef CONFIG_SFC_SIENA_MTD
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci#define EFX_MCDI_NVRAM_LEN_MAX 128
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_cistatic int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type)
202362306a36Sopenharmony_ci{
202462306a36Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_START_V2_IN_LEN);
202562306a36Sopenharmony_ci	int rc;
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_START_IN_TYPE, type);
202862306a36Sopenharmony_ci	MCDI_POPULATE_DWORD_1(inbuf, NVRAM_UPDATE_START_V2_IN_FLAGS,
202962306a36Sopenharmony_ci			      NVRAM_UPDATE_START_V2_IN_FLAG_REPORT_VERIFY_RESULT,
203062306a36Sopenharmony_ci			      1);
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_START_OUT_LEN != 0);
203362306a36Sopenharmony_ci
203462306a36Sopenharmony_ci	rc = efx_siena_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_START, inbuf,
203562306a36Sopenharmony_ci				sizeof(inbuf), NULL, 0, NULL);
203662306a36Sopenharmony_ci
203762306a36Sopenharmony_ci	return rc;
203862306a36Sopenharmony_ci}
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_cistatic int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type,
204162306a36Sopenharmony_ci			       loff_t offset, u8 *buffer, size_t length)
204262306a36Sopenharmony_ci{
204362306a36Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_READ_IN_V2_LEN);
204462306a36Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf,
204562306a36Sopenharmony_ci			 MC_CMD_NVRAM_READ_OUT_LEN(EFX_MCDI_NVRAM_LEN_MAX));
204662306a36Sopenharmony_ci	size_t outlen;
204762306a36Sopenharmony_ci	int rc;
204862306a36Sopenharmony_ci
204962306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_TYPE, type);
205062306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_OFFSET, offset);
205162306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_LENGTH, length);
205262306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_V2_MODE,
205362306a36Sopenharmony_ci		       MC_CMD_NVRAM_READ_IN_V2_DEFAULT);
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	rc = efx_siena_mcdi_rpc(efx, MC_CMD_NVRAM_READ, inbuf, sizeof(inbuf),
205662306a36Sopenharmony_ci				outbuf, sizeof(outbuf), &outlen);
205762306a36Sopenharmony_ci	if (rc)
205862306a36Sopenharmony_ci		return rc;
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci	memcpy(buffer, MCDI_PTR(outbuf, NVRAM_READ_OUT_READ_BUFFER), length);
206162306a36Sopenharmony_ci	return 0;
206262306a36Sopenharmony_ci}
206362306a36Sopenharmony_ci
206462306a36Sopenharmony_cistatic int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type,
206562306a36Sopenharmony_ci				loff_t offset, const u8 *buffer, size_t length)
206662306a36Sopenharmony_ci{
206762306a36Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf,
206862306a36Sopenharmony_ci			 MC_CMD_NVRAM_WRITE_IN_LEN(EFX_MCDI_NVRAM_LEN_MAX));
206962306a36Sopenharmony_ci	int rc;
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_TYPE, type);
207262306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_OFFSET, offset);
207362306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_LENGTH, length);
207462306a36Sopenharmony_ci	memcpy(MCDI_PTR(inbuf, NVRAM_WRITE_IN_WRITE_BUFFER), buffer, length);
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_NVRAM_WRITE_OUT_LEN != 0);
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	rc = efx_siena_mcdi_rpc(efx, MC_CMD_NVRAM_WRITE, inbuf,
207962306a36Sopenharmony_ci				ALIGN(MC_CMD_NVRAM_WRITE_IN_LEN(length), 4),
208062306a36Sopenharmony_ci				NULL, 0, NULL);
208162306a36Sopenharmony_ci	return rc;
208262306a36Sopenharmony_ci}
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_cistatic int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type,
208562306a36Sopenharmony_ci				loff_t offset, size_t length)
208662306a36Sopenharmony_ci{
208762306a36Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_ERASE_IN_LEN);
208862306a36Sopenharmony_ci	int rc;
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_TYPE, type);
209162306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_OFFSET, offset);
209262306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_LENGTH, length);
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_ci	BUILD_BUG_ON(MC_CMD_NVRAM_ERASE_OUT_LEN != 0);
209562306a36Sopenharmony_ci
209662306a36Sopenharmony_ci	rc = efx_siena_mcdi_rpc(efx, MC_CMD_NVRAM_ERASE, inbuf, sizeof(inbuf),
209762306a36Sopenharmony_ci				NULL, 0, NULL);
209862306a36Sopenharmony_ci	return rc;
209962306a36Sopenharmony_ci}
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_cistatic int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type)
210262306a36Sopenharmony_ci{
210362306a36Sopenharmony_ci	MCDI_DECLARE_BUF(inbuf, MC_CMD_NVRAM_UPDATE_FINISH_V2_IN_LEN);
210462306a36Sopenharmony_ci	MCDI_DECLARE_BUF(outbuf, MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT_LEN);
210562306a36Sopenharmony_ci	size_t outlen;
210662306a36Sopenharmony_ci	int rc, rc2;
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_ci	MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_FINISH_IN_TYPE, type);
210962306a36Sopenharmony_ci	/* Always set this flag. Old firmware ignores it */
211062306a36Sopenharmony_ci	MCDI_POPULATE_DWORD_1(inbuf, NVRAM_UPDATE_FINISH_V2_IN_FLAGS,
211162306a36Sopenharmony_ci			      NVRAM_UPDATE_FINISH_V2_IN_FLAG_REPORT_VERIFY_RESULT,
211262306a36Sopenharmony_ci			      1);
211362306a36Sopenharmony_ci
211462306a36Sopenharmony_ci	rc = efx_siena_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_FINISH, inbuf,
211562306a36Sopenharmony_ci				sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);
211662306a36Sopenharmony_ci	if (!rc && outlen >= MC_CMD_NVRAM_UPDATE_FINISH_V2_OUT_LEN) {
211762306a36Sopenharmony_ci		rc2 = MCDI_DWORD(outbuf, NVRAM_UPDATE_FINISH_V2_OUT_RESULT_CODE);
211862306a36Sopenharmony_ci		if (rc2 != MC_CMD_NVRAM_VERIFY_RC_SUCCESS)
211962306a36Sopenharmony_ci			netif_err(efx, drv, efx->net_dev,
212062306a36Sopenharmony_ci				  "NVRAM update failed verification with code 0x%x\n",
212162306a36Sopenharmony_ci				  rc2);
212262306a36Sopenharmony_ci		switch (rc2) {
212362306a36Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_SUCCESS:
212462306a36Sopenharmony_ci			break;
212562306a36Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_CMS_CHECK_FAILED:
212662306a36Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_MESSAGE_DIGEST_CHECK_FAILED:
212762306a36Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_SIGNATURE_CHECK_FAILED:
212862306a36Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_TRUSTED_APPROVERS_CHECK_FAILED:
212962306a36Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_SIGNATURE_CHAIN_CHECK_FAILED:
213062306a36Sopenharmony_ci			rc = -EIO;
213162306a36Sopenharmony_ci			break;
213262306a36Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_INVALID_CMS_FORMAT:
213362306a36Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_BAD_MESSAGE_DIGEST:
213462306a36Sopenharmony_ci			rc = -EINVAL;
213562306a36Sopenharmony_ci			break;
213662306a36Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_NO_VALID_SIGNATURES:
213762306a36Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_NO_TRUSTED_APPROVERS:
213862306a36Sopenharmony_ci		case MC_CMD_NVRAM_VERIFY_RC_NO_SIGNATURE_MATCH:
213962306a36Sopenharmony_ci			rc = -EPERM;
214062306a36Sopenharmony_ci			break;
214162306a36Sopenharmony_ci		default:
214262306a36Sopenharmony_ci			netif_err(efx, drv, efx->net_dev,
214362306a36Sopenharmony_ci				  "Unknown response to NVRAM_UPDATE_FINISH\n");
214462306a36Sopenharmony_ci			rc = -EIO;
214562306a36Sopenharmony_ci		}
214662306a36Sopenharmony_ci	}
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_ci	return rc;
214962306a36Sopenharmony_ci}
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ciint efx_siena_mcdi_mtd_read(struct mtd_info *mtd, loff_t start,
215262306a36Sopenharmony_ci			    size_t len, size_t *retlen, u8 *buffer)
215362306a36Sopenharmony_ci{
215462306a36Sopenharmony_ci	struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
215562306a36Sopenharmony_ci	struct efx_nic *efx = mtd->priv;
215662306a36Sopenharmony_ci	loff_t offset = start;
215762306a36Sopenharmony_ci	loff_t end = min_t(loff_t, start + len, mtd->size);
215862306a36Sopenharmony_ci	size_t chunk;
215962306a36Sopenharmony_ci	int rc = 0;
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_ci	while (offset < end) {
216262306a36Sopenharmony_ci		chunk = min_t(size_t, end - offset, EFX_MCDI_NVRAM_LEN_MAX);
216362306a36Sopenharmony_ci		rc = efx_mcdi_nvram_read(efx, part->nvram_type, offset,
216462306a36Sopenharmony_ci					 buffer, chunk);
216562306a36Sopenharmony_ci		if (rc)
216662306a36Sopenharmony_ci			goto out;
216762306a36Sopenharmony_ci		offset += chunk;
216862306a36Sopenharmony_ci		buffer += chunk;
216962306a36Sopenharmony_ci	}
217062306a36Sopenharmony_ciout:
217162306a36Sopenharmony_ci	*retlen = offset - start;
217262306a36Sopenharmony_ci	return rc;
217362306a36Sopenharmony_ci}
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ciint efx_siena_mcdi_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len)
217662306a36Sopenharmony_ci{
217762306a36Sopenharmony_ci	struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
217862306a36Sopenharmony_ci	struct efx_nic *efx = mtd->priv;
217962306a36Sopenharmony_ci	loff_t offset = start & ~((loff_t)(mtd->erasesize - 1));
218062306a36Sopenharmony_ci	loff_t end = min_t(loff_t, start + len, mtd->size);
218162306a36Sopenharmony_ci	size_t chunk = part->common.mtd.erasesize;
218262306a36Sopenharmony_ci	int rc = 0;
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_ci	if (!part->updating) {
218562306a36Sopenharmony_ci		rc = efx_mcdi_nvram_update_start(efx, part->nvram_type);
218662306a36Sopenharmony_ci		if (rc)
218762306a36Sopenharmony_ci			goto out;
218862306a36Sopenharmony_ci		part->updating = true;
218962306a36Sopenharmony_ci	}
219062306a36Sopenharmony_ci
219162306a36Sopenharmony_ci	/* The MCDI interface can in fact do multiple erase blocks at once;
219262306a36Sopenharmony_ci	 * but erasing may be slow, so we make multiple calls here to avoid
219362306a36Sopenharmony_ci	 * tripping the MCDI RPC timeout. */
219462306a36Sopenharmony_ci	while (offset < end) {
219562306a36Sopenharmony_ci		rc = efx_mcdi_nvram_erase(efx, part->nvram_type, offset,
219662306a36Sopenharmony_ci					  chunk);
219762306a36Sopenharmony_ci		if (rc)
219862306a36Sopenharmony_ci			goto out;
219962306a36Sopenharmony_ci		offset += chunk;
220062306a36Sopenharmony_ci	}
220162306a36Sopenharmony_ciout:
220262306a36Sopenharmony_ci	return rc;
220362306a36Sopenharmony_ci}
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ciint efx_siena_mcdi_mtd_write(struct mtd_info *mtd, loff_t start,
220662306a36Sopenharmony_ci			     size_t len, size_t *retlen, const u8 *buffer)
220762306a36Sopenharmony_ci{
220862306a36Sopenharmony_ci	struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
220962306a36Sopenharmony_ci	struct efx_nic *efx = mtd->priv;
221062306a36Sopenharmony_ci	loff_t offset = start;
221162306a36Sopenharmony_ci	loff_t end = min_t(loff_t, start + len, mtd->size);
221262306a36Sopenharmony_ci	size_t chunk;
221362306a36Sopenharmony_ci	int rc = 0;
221462306a36Sopenharmony_ci
221562306a36Sopenharmony_ci	if (!part->updating) {
221662306a36Sopenharmony_ci		rc = efx_mcdi_nvram_update_start(efx, part->nvram_type);
221762306a36Sopenharmony_ci		if (rc)
221862306a36Sopenharmony_ci			goto out;
221962306a36Sopenharmony_ci		part->updating = true;
222062306a36Sopenharmony_ci	}
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci	while (offset < end) {
222362306a36Sopenharmony_ci		chunk = min_t(size_t, end - offset, EFX_MCDI_NVRAM_LEN_MAX);
222462306a36Sopenharmony_ci		rc = efx_mcdi_nvram_write(efx, part->nvram_type, offset,
222562306a36Sopenharmony_ci					  buffer, chunk);
222662306a36Sopenharmony_ci		if (rc)
222762306a36Sopenharmony_ci			goto out;
222862306a36Sopenharmony_ci		offset += chunk;
222962306a36Sopenharmony_ci		buffer += chunk;
223062306a36Sopenharmony_ci	}
223162306a36Sopenharmony_ciout:
223262306a36Sopenharmony_ci	*retlen = offset - start;
223362306a36Sopenharmony_ci	return rc;
223462306a36Sopenharmony_ci}
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_ciint efx_siena_mcdi_mtd_sync(struct mtd_info *mtd)
223762306a36Sopenharmony_ci{
223862306a36Sopenharmony_ci	struct efx_mcdi_mtd_partition *part = to_efx_mcdi_mtd_partition(mtd);
223962306a36Sopenharmony_ci	struct efx_nic *efx = mtd->priv;
224062306a36Sopenharmony_ci	int rc = 0;
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_ci	if (part->updating) {
224362306a36Sopenharmony_ci		part->updating = false;
224462306a36Sopenharmony_ci		rc = efx_mcdi_nvram_update_finish(efx, part->nvram_type);
224562306a36Sopenharmony_ci	}
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_ci	return rc;
224862306a36Sopenharmony_ci}
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_civoid efx_siena_mcdi_mtd_rename(struct efx_mtd_partition *part)
225162306a36Sopenharmony_ci{
225262306a36Sopenharmony_ci	struct efx_mcdi_mtd_partition *mcdi_part =
225362306a36Sopenharmony_ci		container_of(part, struct efx_mcdi_mtd_partition, common);
225462306a36Sopenharmony_ci	struct efx_nic *efx = part->mtd.priv;
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci	snprintf(part->name, sizeof(part->name), "%s %s:%02x",
225762306a36Sopenharmony_ci		 efx->name, part->type_name, mcdi_part->fw_subtype);
225862306a36Sopenharmony_ci}
225962306a36Sopenharmony_ci
226062306a36Sopenharmony_ci#endif /* CONFIG_SFC_SIENA_MTD */
2261