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