18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/**************************************************************************** 38c2ecf20Sopenharmony_ci * Driver for Solarflare network controllers and boards 48c2ecf20Sopenharmony_ci * Copyright 2019 Solarflare Communications Inc. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 78c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License version 2 as published 88c2ecf20Sopenharmony_ci * by the Free Software Foundation, incorporated herein by reference. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "net_driver.h" 128c2ecf20Sopenharmony_ci#include "efx.h" 138c2ecf20Sopenharmony_ci#include "nic.h" 148c2ecf20Sopenharmony_ci#include "mcdi_functions.h" 158c2ecf20Sopenharmony_ci#include "mcdi.h" 168c2ecf20Sopenharmony_ci#include "mcdi_pcol.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ciint efx_mcdi_free_vis(struct efx_nic *efx) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF_ERR(outbuf); 218c2ecf20Sopenharmony_ci size_t outlen; 228c2ecf20Sopenharmony_ci int rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FREE_VIS, NULL, 0, 238c2ecf20Sopenharmony_ci outbuf, sizeof(outbuf), &outlen); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci /* -EALREADY means nothing to free, so ignore */ 268c2ecf20Sopenharmony_ci if (rc == -EALREADY) 278c2ecf20Sopenharmony_ci rc = 0; 288c2ecf20Sopenharmony_ci if (rc) 298c2ecf20Sopenharmony_ci efx_mcdi_display_error(efx, MC_CMD_FREE_VIS, 0, outbuf, outlen, 308c2ecf20Sopenharmony_ci rc); 318c2ecf20Sopenharmony_ci return rc; 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ciint efx_mcdi_alloc_vis(struct efx_nic *efx, unsigned int min_vis, 358c2ecf20Sopenharmony_ci unsigned int max_vis, unsigned int *vi_base, 368c2ecf20Sopenharmony_ci unsigned int *allocated_vis) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(outbuf, MC_CMD_ALLOC_VIS_OUT_LEN); 398c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(inbuf, MC_CMD_ALLOC_VIS_IN_LEN); 408c2ecf20Sopenharmony_ci size_t outlen; 418c2ecf20Sopenharmony_ci int rc; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, ALLOC_VIS_IN_MIN_VI_COUNT, min_vis); 448c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, ALLOC_VIS_IN_MAX_VI_COUNT, max_vis); 458c2ecf20Sopenharmony_ci rc = efx_mcdi_rpc(efx, MC_CMD_ALLOC_VIS, inbuf, sizeof(inbuf), 468c2ecf20Sopenharmony_ci outbuf, sizeof(outbuf), &outlen); 478c2ecf20Sopenharmony_ci if (rc != 0) 488c2ecf20Sopenharmony_ci return rc; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (outlen < MC_CMD_ALLOC_VIS_OUT_LEN) 518c2ecf20Sopenharmony_ci return -EIO; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci netif_dbg(efx, drv, efx->net_dev, "base VI is A0x%03x\n", 548c2ecf20Sopenharmony_ci MCDI_DWORD(outbuf, ALLOC_VIS_OUT_VI_BASE)); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (vi_base) 578c2ecf20Sopenharmony_ci *vi_base = MCDI_DWORD(outbuf, ALLOC_VIS_OUT_VI_BASE); 588c2ecf20Sopenharmony_ci if (allocated_vis) 598c2ecf20Sopenharmony_ci *allocated_vis = MCDI_DWORD(outbuf, ALLOC_VIS_OUT_VI_COUNT); 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ciint efx_mcdi_ev_probe(struct efx_channel *channel) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci return efx_nic_alloc_buffer(channel->efx, &channel->eventq.buf, 668c2ecf20Sopenharmony_ci (channel->eventq_mask + 1) * 678c2ecf20Sopenharmony_ci sizeof(efx_qword_t), 688c2ecf20Sopenharmony_ci GFP_KERNEL); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ciint efx_mcdi_ev_init(struct efx_channel *channel, bool v1_cut_thru, bool v2) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(inbuf, 748c2ecf20Sopenharmony_ci MC_CMD_INIT_EVQ_V2_IN_LEN(EFX_MAX_EVQ_SIZE * 8 / 758c2ecf20Sopenharmony_ci EFX_BUF_SIZE)); 768c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(outbuf, MC_CMD_INIT_EVQ_V2_OUT_LEN); 778c2ecf20Sopenharmony_ci size_t entries = channel->eventq.buf.len / EFX_BUF_SIZE; 788c2ecf20Sopenharmony_ci struct efx_nic *efx = channel->efx; 798c2ecf20Sopenharmony_ci size_t inlen, outlen; 808c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 818c2ecf20Sopenharmony_ci int rc, i; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci /* Fill event queue with all ones (i.e. empty events) */ 848c2ecf20Sopenharmony_ci memset(channel->eventq.buf.addr, 0xff, channel->eventq.buf.len); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, INIT_EVQ_IN_SIZE, channel->eventq_mask + 1); 878c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, INIT_EVQ_IN_INSTANCE, channel->channel); 888c2ecf20Sopenharmony_ci /* INIT_EVQ expects index in vector table, not absolute */ 898c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, INIT_EVQ_IN_IRQ_NUM, channel->channel); 908c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, INIT_EVQ_IN_TMR_MODE, 918c2ecf20Sopenharmony_ci MC_CMD_INIT_EVQ_IN_TMR_MODE_DIS); 928c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, INIT_EVQ_IN_TMR_LOAD, 0); 938c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, INIT_EVQ_IN_TMR_RELOAD, 0); 948c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, INIT_EVQ_IN_COUNT_MODE, 958c2ecf20Sopenharmony_ci MC_CMD_INIT_EVQ_IN_COUNT_MODE_DIS); 968c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, INIT_EVQ_IN_COUNT_THRSHLD, 0); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (v2) { 998c2ecf20Sopenharmony_ci /* Use the new generic approach to specifying event queue 1008c2ecf20Sopenharmony_ci * configuration, requesting lower latency or higher throughput. 1018c2ecf20Sopenharmony_ci * The options that actually get used appear in the output. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci MCDI_POPULATE_DWORD_2(inbuf, INIT_EVQ_V2_IN_FLAGS, 1048c2ecf20Sopenharmony_ci INIT_EVQ_V2_IN_FLAG_INTERRUPTING, 1, 1058c2ecf20Sopenharmony_ci INIT_EVQ_V2_IN_FLAG_TYPE, 1068c2ecf20Sopenharmony_ci MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_AUTO); 1078c2ecf20Sopenharmony_ci } else { 1088c2ecf20Sopenharmony_ci MCDI_POPULATE_DWORD_4(inbuf, INIT_EVQ_IN_FLAGS, 1098c2ecf20Sopenharmony_ci INIT_EVQ_IN_FLAG_INTERRUPTING, 1, 1108c2ecf20Sopenharmony_ci INIT_EVQ_IN_FLAG_RX_MERGE, 1, 1118c2ecf20Sopenharmony_ci INIT_EVQ_IN_FLAG_TX_MERGE, 1, 1128c2ecf20Sopenharmony_ci INIT_EVQ_IN_FLAG_CUT_THRU, v1_cut_thru); 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci dma_addr = channel->eventq.buf.dma_addr; 1168c2ecf20Sopenharmony_ci for (i = 0; i < entries; ++i) { 1178c2ecf20Sopenharmony_ci MCDI_SET_ARRAY_QWORD(inbuf, INIT_EVQ_IN_DMA_ADDR, i, dma_addr); 1188c2ecf20Sopenharmony_ci dma_addr += EFX_BUF_SIZE; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci inlen = MC_CMD_INIT_EVQ_IN_LEN(entries); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci rc = efx_mcdi_rpc(efx, MC_CMD_INIT_EVQ, inbuf, inlen, 1248c2ecf20Sopenharmony_ci outbuf, sizeof(outbuf), &outlen); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (outlen >= MC_CMD_INIT_EVQ_V2_OUT_LEN) 1278c2ecf20Sopenharmony_ci netif_dbg(efx, drv, efx->net_dev, 1288c2ecf20Sopenharmony_ci "Channel %d using event queue flags %08x\n", 1298c2ecf20Sopenharmony_ci channel->channel, 1308c2ecf20Sopenharmony_ci MCDI_DWORD(outbuf, INIT_EVQ_V2_OUT_FLAGS)); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return rc; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_civoid efx_mcdi_ev_remove(struct efx_channel *channel) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci efx_nic_free_buffer(channel->efx, &channel->eventq.buf); 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_civoid efx_mcdi_ev_fini(struct efx_channel *channel) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(inbuf, MC_CMD_FINI_EVQ_IN_LEN); 1438c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF_ERR(outbuf); 1448c2ecf20Sopenharmony_ci struct efx_nic *efx = channel->efx; 1458c2ecf20Sopenharmony_ci size_t outlen; 1468c2ecf20Sopenharmony_ci int rc; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, FINI_EVQ_IN_INSTANCE, channel->channel); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FINI_EVQ, inbuf, sizeof(inbuf), 1518c2ecf20Sopenharmony_ci outbuf, sizeof(outbuf), &outlen); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (rc && rc != -EALREADY) 1548c2ecf20Sopenharmony_ci goto fail; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cifail: 1598c2ecf20Sopenharmony_ci efx_mcdi_display_error(efx, MC_CMD_FINI_EVQ, MC_CMD_FINI_EVQ_IN_LEN, 1608c2ecf20Sopenharmony_ci outbuf, outlen, rc); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ciint efx_mcdi_tx_init(struct efx_tx_queue *tx_queue) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(inbuf, MC_CMD_INIT_TXQ_IN_LEN(EFX_MAX_DMAQ_SIZE * 8 / 1668c2ecf20Sopenharmony_ci EFX_BUF_SIZE)); 1678c2ecf20Sopenharmony_ci bool csum_offload = tx_queue->type & EFX_TXQ_TYPE_OUTER_CSUM; 1688c2ecf20Sopenharmony_ci bool inner_csum = tx_queue->type & EFX_TXQ_TYPE_INNER_CSUM; 1698c2ecf20Sopenharmony_ci size_t entries = tx_queue->txd.buf.len / EFX_BUF_SIZE; 1708c2ecf20Sopenharmony_ci struct efx_channel *channel = tx_queue->channel; 1718c2ecf20Sopenharmony_ci struct efx_nic *efx = tx_queue->efx; 1728c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 1738c2ecf20Sopenharmony_ci size_t inlen; 1748c2ecf20Sopenharmony_ci int rc, i; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci BUILD_BUG_ON(MC_CMD_INIT_TXQ_OUT_LEN != 0); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_SIZE, tx_queue->ptr_mask + 1); 1798c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_TARGET_EVQ, channel->channel); 1808c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_LABEL, tx_queue->label); 1818c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_INSTANCE, tx_queue->queue); 1828c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_OWNER_ID, 0); 1838c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, INIT_TXQ_IN_PORT_ID, efx->vport_id); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci dma_addr = tx_queue->txd.buf.dma_addr; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci netif_dbg(efx, hw, efx->net_dev, "pushing TXQ %d. %zu entries (%llx)\n", 1888c2ecf20Sopenharmony_ci tx_queue->queue, entries, (u64)dma_addr); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci for (i = 0; i < entries; ++i) { 1918c2ecf20Sopenharmony_ci MCDI_SET_ARRAY_QWORD(inbuf, INIT_TXQ_IN_DMA_ADDR, i, dma_addr); 1928c2ecf20Sopenharmony_ci dma_addr += EFX_BUF_SIZE; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci inlen = MC_CMD_INIT_TXQ_IN_LEN(entries); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci do { 1988c2ecf20Sopenharmony_ci bool tso_v2 = tx_queue->tso_version == 2; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* TSOv2 implies IP header checksum offload for TSO frames, 2018c2ecf20Sopenharmony_ci * so we can safely disable IP header checksum offload for 2028c2ecf20Sopenharmony_ci * everything else. If we don't have TSOv2, then we have to 2038c2ecf20Sopenharmony_ci * enable IP header checksum offload, which is strictly 2048c2ecf20Sopenharmony_ci * incorrect but better than breaking TSO. 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_ci MCDI_POPULATE_DWORD_6(inbuf, INIT_TXQ_IN_FLAGS, 2078c2ecf20Sopenharmony_ci /* This flag was removed from mcdi_pcol.h for 2088c2ecf20Sopenharmony_ci * the non-_EXT version of INIT_TXQ. However, 2098c2ecf20Sopenharmony_ci * firmware still honours it. 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_ci INIT_TXQ_EXT_IN_FLAG_TSOV2_EN, tso_v2, 2128c2ecf20Sopenharmony_ci INIT_TXQ_IN_FLAG_IP_CSUM_DIS, !(csum_offload && tso_v2), 2138c2ecf20Sopenharmony_ci INIT_TXQ_IN_FLAG_TCP_CSUM_DIS, !csum_offload, 2148c2ecf20Sopenharmony_ci INIT_TXQ_EXT_IN_FLAG_TIMESTAMP, tx_queue->timestamping, 2158c2ecf20Sopenharmony_ci INIT_TXQ_IN_FLAG_INNER_IP_CSUM_EN, inner_csum && !tso_v2, 2168c2ecf20Sopenharmony_ci INIT_TXQ_IN_FLAG_INNER_TCP_CSUM_EN, inner_csum); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci rc = efx_mcdi_rpc_quiet(efx, MC_CMD_INIT_TXQ, inbuf, inlen, 2198c2ecf20Sopenharmony_ci NULL, 0, NULL); 2208c2ecf20Sopenharmony_ci if (rc == -ENOSPC && tso_v2) { 2218c2ecf20Sopenharmony_ci /* Retry without TSOv2 if we're short on contexts. */ 2228c2ecf20Sopenharmony_ci tx_queue->tso_version = 0; 2238c2ecf20Sopenharmony_ci netif_warn(efx, probe, efx->net_dev, 2248c2ecf20Sopenharmony_ci "TSOv2 context not available to segment in " 2258c2ecf20Sopenharmony_ci "hardware. TCP performance may be reduced.\n" 2268c2ecf20Sopenharmony_ci ); 2278c2ecf20Sopenharmony_ci } else if (rc) { 2288c2ecf20Sopenharmony_ci efx_mcdi_display_error(efx, MC_CMD_INIT_TXQ, 2298c2ecf20Sopenharmony_ci MC_CMD_INIT_TXQ_EXT_IN_LEN, 2308c2ecf20Sopenharmony_ci NULL, 0, rc); 2318c2ecf20Sopenharmony_ci goto fail; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci } while (rc); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cifail: 2388c2ecf20Sopenharmony_ci return rc; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_civoid efx_mcdi_tx_remove(struct efx_tx_queue *tx_queue) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci efx_nic_free_buffer(tx_queue->efx, &tx_queue->txd.buf); 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_civoid efx_mcdi_tx_fini(struct efx_tx_queue *tx_queue) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(inbuf, MC_CMD_FINI_TXQ_IN_LEN); 2498c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF_ERR(outbuf); 2508c2ecf20Sopenharmony_ci struct efx_nic *efx = tx_queue->efx; 2518c2ecf20Sopenharmony_ci size_t outlen; 2528c2ecf20Sopenharmony_ci int rc; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, FINI_TXQ_IN_INSTANCE, 2558c2ecf20Sopenharmony_ci tx_queue->queue); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FINI_TXQ, inbuf, sizeof(inbuf), 2588c2ecf20Sopenharmony_ci outbuf, sizeof(outbuf), &outlen); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (rc && rc != -EALREADY) 2618c2ecf20Sopenharmony_ci goto fail; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cifail: 2668c2ecf20Sopenharmony_ci efx_mcdi_display_error(efx, MC_CMD_FINI_TXQ, MC_CMD_FINI_TXQ_IN_LEN, 2678c2ecf20Sopenharmony_ci outbuf, outlen, rc); 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ciint efx_mcdi_rx_probe(struct efx_rx_queue *rx_queue) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci return efx_nic_alloc_buffer(rx_queue->efx, &rx_queue->rxd.buf, 2738c2ecf20Sopenharmony_ci (rx_queue->ptr_mask + 1) * 2748c2ecf20Sopenharmony_ci sizeof(efx_qword_t), 2758c2ecf20Sopenharmony_ci GFP_KERNEL); 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_civoid efx_mcdi_rx_init(struct efx_rx_queue *rx_queue) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct efx_channel *channel = efx_rx_queue_channel(rx_queue); 2818c2ecf20Sopenharmony_ci size_t entries = rx_queue->rxd.buf.len / EFX_BUF_SIZE; 2828c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(inbuf, MC_CMD_INIT_RXQ_V4_IN_LEN); 2838c2ecf20Sopenharmony_ci struct efx_nic *efx = rx_queue->efx; 2848c2ecf20Sopenharmony_ci unsigned int buffer_size; 2858c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 2868c2ecf20Sopenharmony_ci int rc; 2878c2ecf20Sopenharmony_ci int i; 2888c2ecf20Sopenharmony_ci BUILD_BUG_ON(MC_CMD_INIT_RXQ_OUT_LEN != 0); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci rx_queue->scatter_n = 0; 2918c2ecf20Sopenharmony_ci rx_queue->scatter_len = 0; 2928c2ecf20Sopenharmony_ci if (efx->type->revision == EFX_REV_EF100) 2938c2ecf20Sopenharmony_ci buffer_size = efx->rx_page_buf_step; 2948c2ecf20Sopenharmony_ci else 2958c2ecf20Sopenharmony_ci buffer_size = 0; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, INIT_RXQ_IN_SIZE, rx_queue->ptr_mask + 1); 2988c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, INIT_RXQ_IN_TARGET_EVQ, channel->channel); 2998c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, INIT_RXQ_IN_LABEL, efx_rx_queue_index(rx_queue)); 3008c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, INIT_RXQ_IN_INSTANCE, 3018c2ecf20Sopenharmony_ci efx_rx_queue_index(rx_queue)); 3028c2ecf20Sopenharmony_ci MCDI_POPULATE_DWORD_2(inbuf, INIT_RXQ_IN_FLAGS, 3038c2ecf20Sopenharmony_ci INIT_RXQ_IN_FLAG_PREFIX, 1, 3048c2ecf20Sopenharmony_ci INIT_RXQ_IN_FLAG_TIMESTAMP, 1); 3058c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, INIT_RXQ_IN_OWNER_ID, 0); 3068c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, INIT_RXQ_IN_PORT_ID, efx->vport_id); 3078c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, INIT_RXQ_V4_IN_BUFFER_SIZE_BYTES, buffer_size); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci dma_addr = rx_queue->rxd.buf.dma_addr; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci netif_dbg(efx, hw, efx->net_dev, "pushing RXQ %d. %zu entries (%llx)\n", 3128c2ecf20Sopenharmony_ci efx_rx_queue_index(rx_queue), entries, (u64)dma_addr); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci for (i = 0; i < entries; ++i) { 3158c2ecf20Sopenharmony_ci MCDI_SET_ARRAY_QWORD(inbuf, INIT_RXQ_IN_DMA_ADDR, i, dma_addr); 3168c2ecf20Sopenharmony_ci dma_addr += EFX_BUF_SIZE; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci rc = efx_mcdi_rpc(efx, MC_CMD_INIT_RXQ, inbuf, sizeof(inbuf), 3208c2ecf20Sopenharmony_ci NULL, 0, NULL); 3218c2ecf20Sopenharmony_ci if (rc) 3228c2ecf20Sopenharmony_ci netdev_WARN(efx->net_dev, "failed to initialise RXQ %d\n", 3238c2ecf20Sopenharmony_ci efx_rx_queue_index(rx_queue)); 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_civoid efx_mcdi_rx_remove(struct efx_rx_queue *rx_queue) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci efx_nic_free_buffer(rx_queue->efx, &rx_queue->rxd.buf); 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_civoid efx_mcdi_rx_fini(struct efx_rx_queue *rx_queue) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(inbuf, MC_CMD_FINI_RXQ_IN_LEN); 3348c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF_ERR(outbuf); 3358c2ecf20Sopenharmony_ci struct efx_nic *efx = rx_queue->efx; 3368c2ecf20Sopenharmony_ci size_t outlen; 3378c2ecf20Sopenharmony_ci int rc; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci MCDI_SET_DWORD(inbuf, FINI_RXQ_IN_INSTANCE, 3408c2ecf20Sopenharmony_ci efx_rx_queue_index(rx_queue)); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci rc = efx_mcdi_rpc_quiet(efx, MC_CMD_FINI_RXQ, inbuf, sizeof(inbuf), 3438c2ecf20Sopenharmony_ci outbuf, sizeof(outbuf), &outlen); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (rc && rc != -EALREADY) 3468c2ecf20Sopenharmony_ci goto fail; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cifail: 3518c2ecf20Sopenharmony_ci efx_mcdi_display_error(efx, MC_CMD_FINI_RXQ, MC_CMD_FINI_RXQ_IN_LEN, 3528c2ecf20Sopenharmony_ci outbuf, outlen, rc); 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ciint efx_fini_dmaq(struct efx_nic *efx) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct efx_tx_queue *tx_queue; 3588c2ecf20Sopenharmony_ci struct efx_rx_queue *rx_queue; 3598c2ecf20Sopenharmony_ci struct efx_channel *channel; 3608c2ecf20Sopenharmony_ci int pending; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* If the MC has just rebooted, the TX/RX queues will have already been 3638c2ecf20Sopenharmony_ci * torn down, but efx->active_queues needs to be set to zero. 3648c2ecf20Sopenharmony_ci */ 3658c2ecf20Sopenharmony_ci if (efx->must_realloc_vis) { 3668c2ecf20Sopenharmony_ci atomic_set(&efx->active_queues, 0); 3678c2ecf20Sopenharmony_ci return 0; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* Do not attempt to write to the NIC during EEH recovery */ 3718c2ecf20Sopenharmony_ci if (efx->state != STATE_RECOVERY) { 3728c2ecf20Sopenharmony_ci efx_for_each_channel(channel, efx) { 3738c2ecf20Sopenharmony_ci efx_for_each_channel_rx_queue(rx_queue, channel) 3748c2ecf20Sopenharmony_ci efx_mcdi_rx_fini(rx_queue); 3758c2ecf20Sopenharmony_ci efx_for_each_channel_tx_queue(tx_queue, channel) 3768c2ecf20Sopenharmony_ci efx_mcdi_tx_fini(tx_queue); 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci wait_event_timeout(efx->flush_wq, 3808c2ecf20Sopenharmony_ci atomic_read(&efx->active_queues) == 0, 3818c2ecf20Sopenharmony_ci msecs_to_jiffies(EFX_MAX_FLUSH_TIME)); 3828c2ecf20Sopenharmony_ci pending = atomic_read(&efx->active_queues); 3838c2ecf20Sopenharmony_ci if (pending) { 3848c2ecf20Sopenharmony_ci netif_err(efx, hw, efx->net_dev, "failed to flush %d queues\n", 3858c2ecf20Sopenharmony_ci pending); 3868c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci return 0; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ciint efx_mcdi_window_mode_to_stride(struct efx_nic *efx, u8 vi_window_mode) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci switch (vi_window_mode) { 3968c2ecf20Sopenharmony_ci case MC_CMD_GET_CAPABILITIES_V3_OUT_VI_WINDOW_MODE_8K: 3978c2ecf20Sopenharmony_ci efx->vi_stride = 8192; 3988c2ecf20Sopenharmony_ci break; 3998c2ecf20Sopenharmony_ci case MC_CMD_GET_CAPABILITIES_V3_OUT_VI_WINDOW_MODE_16K: 4008c2ecf20Sopenharmony_ci efx->vi_stride = 16384; 4018c2ecf20Sopenharmony_ci break; 4028c2ecf20Sopenharmony_ci case MC_CMD_GET_CAPABILITIES_V3_OUT_VI_WINDOW_MODE_64K: 4038c2ecf20Sopenharmony_ci efx->vi_stride = 65536; 4048c2ecf20Sopenharmony_ci break; 4058c2ecf20Sopenharmony_ci default: 4068c2ecf20Sopenharmony_ci netif_err(efx, probe, efx->net_dev, 4078c2ecf20Sopenharmony_ci "Unrecognised VI window mode %d\n", 4088c2ecf20Sopenharmony_ci vi_window_mode); 4098c2ecf20Sopenharmony_ci return -EIO; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci netif_dbg(efx, probe, efx->net_dev, "vi_stride = %u\n", 4128c2ecf20Sopenharmony_ci efx->vi_stride); 4138c2ecf20Sopenharmony_ci return 0; 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ciint efx_get_pf_index(struct efx_nic *efx, unsigned int *pf_index) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_FUNCTION_INFO_OUT_LEN); 4198c2ecf20Sopenharmony_ci size_t outlen; 4208c2ecf20Sopenharmony_ci int rc; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci rc = efx_mcdi_rpc(efx, MC_CMD_GET_FUNCTION_INFO, NULL, 0, outbuf, 4238c2ecf20Sopenharmony_ci sizeof(outbuf), &outlen); 4248c2ecf20Sopenharmony_ci if (rc) 4258c2ecf20Sopenharmony_ci return rc; 4268c2ecf20Sopenharmony_ci if (outlen < sizeof(outbuf)) 4278c2ecf20Sopenharmony_ci return -EIO; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci *pf_index = MCDI_DWORD(outbuf, GET_FUNCTION_INFO_OUT_PF); 4308c2ecf20Sopenharmony_ci return 0; 4318c2ecf20Sopenharmony_ci} 432