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