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