1 // SPDX-License-Identifier: GPL-2.0-only
2 /****************************************************************************
3 * Driver for Solarflare network controllers and boards
4 * Copyright 2019 Solarflare Communications Inc.
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 as published
8 * by the Free Software Foundation, incorporated herein by reference.
11 #include "net_driver.h"
14 #include "mcdi_functions.h"
16 #include "mcdi_pcol.h"
18 int efx_mcdi_free_vis(struct efx_nic
*efx
)
20 MCDI_DECLARE_BUF_ERR(outbuf
);
22 int rc
= efx_mcdi_rpc_quiet(efx
, MC_CMD_FREE_VIS
, NULL
, 0,
23 outbuf
, sizeof(outbuf
), &outlen
);
25 /* -EALREADY means nothing to free, so ignore */
29 efx_mcdi_display_error(efx
, MC_CMD_FREE_VIS
, 0, outbuf
, outlen
,
34 int efx_mcdi_alloc_vis(struct efx_nic
*efx
, unsigned int min_vis
,
35 unsigned int max_vis
, unsigned int *vi_base
,
36 unsigned int *allocated_vis
)
38 MCDI_DECLARE_BUF(outbuf
, MC_CMD_ALLOC_VIS_OUT_LEN
);
39 MCDI_DECLARE_BUF(inbuf
, MC_CMD_ALLOC_VIS_IN_LEN
);
43 MCDI_SET_DWORD(inbuf
, ALLOC_VIS_IN_MIN_VI_COUNT
, min_vis
);
44 MCDI_SET_DWORD(inbuf
, ALLOC_VIS_IN_MAX_VI_COUNT
, max_vis
);
45 rc
= efx_mcdi_rpc(efx
, MC_CMD_ALLOC_VIS
, inbuf
, sizeof(inbuf
),
46 outbuf
, sizeof(outbuf
), &outlen
);
50 if (outlen
< MC_CMD_ALLOC_VIS_OUT_LEN
)
53 netif_dbg(efx
, drv
, efx
->net_dev
, "base VI is A0x%03x\n",
54 MCDI_DWORD(outbuf
, ALLOC_VIS_OUT_VI_BASE
));
57 *vi_base
= MCDI_DWORD(outbuf
, ALLOC_VIS_OUT_VI_BASE
);
59 *allocated_vis
= MCDI_DWORD(outbuf
, ALLOC_VIS_OUT_VI_COUNT
);
63 int efx_mcdi_ev_probe(struct efx_channel
*channel
)
65 return efx_nic_alloc_buffer(channel
->efx
, &channel
->eventq
.buf
,
66 (channel
->eventq_mask
+ 1) *
71 int efx_mcdi_ev_init(struct efx_channel
*channel
, bool v1_cut_thru
, bool v2
)
73 MCDI_DECLARE_BUF(inbuf
,
74 MC_CMD_INIT_EVQ_V2_IN_LEN(EFX_MAX_EVQ_SIZE
* 8 /
76 MCDI_DECLARE_BUF(outbuf
, MC_CMD_INIT_EVQ_V2_OUT_LEN
);
77 size_t entries
= channel
->eventq
.buf
.len
/ EFX_BUF_SIZE
;
78 struct efx_nic
*efx
= channel
->efx
;
83 /* Fill event queue with all ones (i.e. empty events) */
84 memset(channel
->eventq
.buf
.addr
, 0xff, channel
->eventq
.buf
.len
);
86 MCDI_SET_DWORD(inbuf
, INIT_EVQ_IN_SIZE
, channel
->eventq_mask
+ 1);
87 MCDI_SET_DWORD(inbuf
, INIT_EVQ_IN_INSTANCE
, channel
->channel
);
88 /* INIT_EVQ expects index in vector table, not absolute */
89 MCDI_SET_DWORD(inbuf
, INIT_EVQ_IN_IRQ_NUM
, channel
->channel
);
90 MCDI_SET_DWORD(inbuf
, INIT_EVQ_IN_TMR_MODE
,
91 MC_CMD_INIT_EVQ_IN_TMR_MODE_DIS
);
92 MCDI_SET_DWORD(inbuf
, INIT_EVQ_IN_TMR_LOAD
, 0);
93 MCDI_SET_DWORD(inbuf
, INIT_EVQ_IN_TMR_RELOAD
, 0);
94 MCDI_SET_DWORD(inbuf
, INIT_EVQ_IN_COUNT_MODE
,
95 MC_CMD_INIT_EVQ_IN_COUNT_MODE_DIS
);
96 MCDI_SET_DWORD(inbuf
, INIT_EVQ_IN_COUNT_THRSHLD
, 0);
99 /* Use the new generic approach to specifying event queue
100 * configuration, requesting lower latency or higher throughput.
101 * The options that actually get used appear in the output.
103 MCDI_POPULATE_DWORD_2(inbuf
, INIT_EVQ_V2_IN_FLAGS
,
104 INIT_EVQ_V2_IN_FLAG_INTERRUPTING
, 1,
105 INIT_EVQ_V2_IN_FLAG_TYPE
,
106 MC_CMD_INIT_EVQ_V2_IN_FLAG_TYPE_AUTO
);
108 MCDI_POPULATE_DWORD_4(inbuf
, INIT_EVQ_IN_FLAGS
,
109 INIT_EVQ_IN_FLAG_INTERRUPTING
, 1,
110 INIT_EVQ_IN_FLAG_RX_MERGE
, 1,
111 INIT_EVQ_IN_FLAG_TX_MERGE
, 1,
112 INIT_EVQ_IN_FLAG_CUT_THRU
, v1_cut_thru
);
115 dma_addr
= channel
->eventq
.buf
.dma_addr
;
116 for (i
= 0; i
< entries
; ++i
) {
117 MCDI_SET_ARRAY_QWORD(inbuf
, INIT_EVQ_IN_DMA_ADDR
, i
, dma_addr
);
118 dma_addr
+= EFX_BUF_SIZE
;
121 inlen
= MC_CMD_INIT_EVQ_IN_LEN(entries
);
123 rc
= efx_mcdi_rpc(efx
, MC_CMD_INIT_EVQ
, inbuf
, inlen
,
124 outbuf
, sizeof(outbuf
), &outlen
);
126 if (outlen
>= MC_CMD_INIT_EVQ_V2_OUT_LEN
)
127 netif_dbg(efx
, drv
, efx
->net_dev
,
128 "Channel %d using event queue flags %08x\n",
130 MCDI_DWORD(outbuf
, INIT_EVQ_V2_OUT_FLAGS
));
135 void efx_mcdi_ev_remove(struct efx_channel
*channel
)
137 efx_nic_free_buffer(channel
->efx
, &channel
->eventq
.buf
);
140 void efx_mcdi_ev_fini(struct efx_channel
*channel
)
142 MCDI_DECLARE_BUF(inbuf
, MC_CMD_FINI_EVQ_IN_LEN
);
143 MCDI_DECLARE_BUF_ERR(outbuf
);
144 struct efx_nic
*efx
= channel
->efx
;
148 MCDI_SET_DWORD(inbuf
, FINI_EVQ_IN_INSTANCE
, channel
->channel
);
150 rc
= efx_mcdi_rpc_quiet(efx
, MC_CMD_FINI_EVQ
, inbuf
, sizeof(inbuf
),
151 outbuf
, sizeof(outbuf
), &outlen
);
153 if (rc
&& rc
!= -EALREADY
)
159 efx_mcdi_display_error(efx
, MC_CMD_FINI_EVQ
, MC_CMD_FINI_EVQ_IN_LEN
,
163 int efx_mcdi_tx_init(struct efx_tx_queue
*tx_queue
, bool tso_v2
)
165 MCDI_DECLARE_BUF(inbuf
, MC_CMD_INIT_TXQ_IN_LEN(EFX_MAX_DMAQ_SIZE
* 8 /
167 bool csum_offload
= tx_queue
->queue
& EFX_TXQ_TYPE_OFFLOAD
;
168 size_t entries
= tx_queue
->txd
.buf
.len
/ EFX_BUF_SIZE
;
169 struct efx_channel
*channel
= tx_queue
->channel
;
170 struct efx_nic
*efx
= tx_queue
->efx
;
171 struct efx_ef10_nic_data
*nic_data
;
176 BUILD_BUG_ON(MC_CMD_INIT_TXQ_OUT_LEN
!= 0);
178 nic_data
= efx
->nic_data
;
180 MCDI_SET_DWORD(inbuf
, INIT_TXQ_IN_SIZE
, tx_queue
->ptr_mask
+ 1);
181 MCDI_SET_DWORD(inbuf
, INIT_TXQ_IN_TARGET_EVQ
, channel
->channel
);
182 MCDI_SET_DWORD(inbuf
, INIT_TXQ_IN_LABEL
, tx_queue
->queue
);
183 MCDI_SET_DWORD(inbuf
, INIT_TXQ_IN_INSTANCE
, tx_queue
->queue
);
184 MCDI_SET_DWORD(inbuf
, INIT_TXQ_IN_OWNER_ID
, 0);
185 MCDI_SET_DWORD(inbuf
, INIT_TXQ_IN_PORT_ID
, nic_data
->vport_id
);
187 dma_addr
= tx_queue
->txd
.buf
.dma_addr
;
189 netif_dbg(efx
, hw
, efx
->net_dev
, "pushing TXQ %d. %zu entries (%llx)\n",
190 tx_queue
->queue
, entries
, (u64
)dma_addr
);
192 for (i
= 0; i
< entries
; ++i
) {
193 MCDI_SET_ARRAY_QWORD(inbuf
, INIT_TXQ_IN_DMA_ADDR
, i
, dma_addr
);
194 dma_addr
+= EFX_BUF_SIZE
;
197 inlen
= MC_CMD_INIT_TXQ_IN_LEN(entries
);
200 MCDI_POPULATE_DWORD_4(inbuf
, INIT_TXQ_IN_FLAGS
,
201 /* This flag was removed from mcdi_pcol.h for
202 * the non-_EXT version of INIT_TXQ. However,
203 * firmware still honours it.
205 INIT_TXQ_EXT_IN_FLAG_TSOV2_EN
, tso_v2
,
206 INIT_TXQ_IN_FLAG_IP_CSUM_DIS
, !csum_offload
,
207 INIT_TXQ_IN_FLAG_TCP_CSUM_DIS
, !csum_offload
,
208 INIT_TXQ_EXT_IN_FLAG_TIMESTAMP
,
209 tx_queue
->timestamping
);
211 rc
= efx_mcdi_rpc_quiet(efx
, MC_CMD_INIT_TXQ
, inbuf
, inlen
,
213 if (rc
== -ENOSPC
&& tso_v2
) {
214 /* Retry without TSOv2 if we're short on contexts. */
216 netif_warn(efx
, probe
, efx
->net_dev
,
217 "TSOv2 context not available to segment in "
218 "hardware. TCP performance may be reduced.\n"
221 efx_mcdi_display_error(efx
, MC_CMD_INIT_TXQ
,
222 MC_CMD_INIT_TXQ_EXT_IN_LEN
,
234 void efx_mcdi_tx_remove(struct efx_tx_queue
*tx_queue
)
236 efx_nic_free_buffer(tx_queue
->efx
, &tx_queue
->txd
.buf
);
239 void efx_mcdi_tx_fini(struct efx_tx_queue
*tx_queue
)
241 MCDI_DECLARE_BUF(inbuf
, MC_CMD_FINI_TXQ_IN_LEN
);
242 MCDI_DECLARE_BUF_ERR(outbuf
);
243 struct efx_nic
*efx
= tx_queue
->efx
;
247 MCDI_SET_DWORD(inbuf
, FINI_TXQ_IN_INSTANCE
,
250 rc
= efx_mcdi_rpc_quiet(efx
, MC_CMD_FINI_TXQ
, inbuf
, sizeof(inbuf
),
251 outbuf
, sizeof(outbuf
), &outlen
);
253 if (rc
&& rc
!= -EALREADY
)
259 efx_mcdi_display_error(efx
, MC_CMD_FINI_TXQ
, MC_CMD_FINI_TXQ_IN_LEN
,
263 int efx_mcdi_rx_probe(struct efx_rx_queue
*rx_queue
)
265 return efx_nic_alloc_buffer(rx_queue
->efx
, &rx_queue
->rxd
.buf
,
266 (rx_queue
->ptr_mask
+ 1) *
271 void efx_mcdi_rx_init(struct efx_rx_queue
*rx_queue
)
273 MCDI_DECLARE_BUF(inbuf
,
274 MC_CMD_INIT_RXQ_IN_LEN(EFX_MAX_DMAQ_SIZE
* 8 /
276 struct efx_channel
*channel
= efx_rx_queue_channel(rx_queue
);
277 size_t entries
= rx_queue
->rxd
.buf
.len
/ EFX_BUF_SIZE
;
278 struct efx_nic
*efx
= rx_queue
->efx
;
279 struct efx_ef10_nic_data
*nic_data
= efx
->nic_data
;
284 BUILD_BUG_ON(MC_CMD_INIT_RXQ_OUT_LEN
!= 0);
286 rx_queue
->scatter_n
= 0;
287 rx_queue
->scatter_len
= 0;
289 MCDI_SET_DWORD(inbuf
, INIT_RXQ_IN_SIZE
, rx_queue
->ptr_mask
+ 1);
290 MCDI_SET_DWORD(inbuf
, INIT_RXQ_IN_TARGET_EVQ
, channel
->channel
);
291 MCDI_SET_DWORD(inbuf
, INIT_RXQ_IN_LABEL
, efx_rx_queue_index(rx_queue
));
292 MCDI_SET_DWORD(inbuf
, INIT_RXQ_IN_INSTANCE
,
293 efx_rx_queue_index(rx_queue
));
294 MCDI_POPULATE_DWORD_2(inbuf
, INIT_RXQ_IN_FLAGS
,
295 INIT_RXQ_IN_FLAG_PREFIX
, 1,
296 INIT_RXQ_IN_FLAG_TIMESTAMP
, 1);
297 MCDI_SET_DWORD(inbuf
, INIT_RXQ_IN_OWNER_ID
, 0);
298 MCDI_SET_DWORD(inbuf
, INIT_RXQ_IN_PORT_ID
, nic_data
->vport_id
);
300 dma_addr
= rx_queue
->rxd
.buf
.dma_addr
;
302 netif_dbg(efx
, hw
, efx
->net_dev
, "pushing RXQ %d. %zu entries (%llx)\n",
303 efx_rx_queue_index(rx_queue
), entries
, (u64
)dma_addr
);
305 for (i
= 0; i
< entries
; ++i
) {
306 MCDI_SET_ARRAY_QWORD(inbuf
, INIT_RXQ_IN_DMA_ADDR
, i
, dma_addr
);
307 dma_addr
+= EFX_BUF_SIZE
;
310 inlen
= MC_CMD_INIT_RXQ_IN_LEN(entries
);
312 rc
= efx_mcdi_rpc(efx
, MC_CMD_INIT_RXQ
, inbuf
, inlen
,
315 netdev_WARN(efx
->net_dev
, "failed to initialise RXQ %d\n",
316 efx_rx_queue_index(rx_queue
));
319 void efx_mcdi_rx_remove(struct efx_rx_queue
*rx_queue
)
321 efx_nic_free_buffer(rx_queue
->efx
, &rx_queue
->rxd
.buf
);
324 void efx_mcdi_rx_fini(struct efx_rx_queue
*rx_queue
)
326 MCDI_DECLARE_BUF(inbuf
, MC_CMD_FINI_RXQ_IN_LEN
);
327 MCDI_DECLARE_BUF_ERR(outbuf
);
328 struct efx_nic
*efx
= rx_queue
->efx
;
332 MCDI_SET_DWORD(inbuf
, FINI_RXQ_IN_INSTANCE
,
333 efx_rx_queue_index(rx_queue
));
335 rc
= efx_mcdi_rpc_quiet(efx
, MC_CMD_FINI_RXQ
, inbuf
, sizeof(inbuf
),
336 outbuf
, sizeof(outbuf
), &outlen
);
338 if (rc
&& rc
!= -EALREADY
)
344 efx_mcdi_display_error(efx
, MC_CMD_FINI_RXQ
, MC_CMD_FINI_RXQ_IN_LEN
,
348 int efx_mcdi_window_mode_to_stride(struct efx_nic
*efx
, u8 vi_window_mode
)
350 switch (vi_window_mode
) {
351 case MC_CMD_GET_CAPABILITIES_V3_OUT_VI_WINDOW_MODE_8K
:
352 efx
->vi_stride
= 8192;
354 case MC_CMD_GET_CAPABILITIES_V3_OUT_VI_WINDOW_MODE_16K
:
355 efx
->vi_stride
= 16384;
357 case MC_CMD_GET_CAPABILITIES_V3_OUT_VI_WINDOW_MODE_64K
:
358 efx
->vi_stride
= 65536;
361 netif_err(efx
, probe
, efx
->net_dev
,
362 "Unrecognised VI window mode %d\n",
366 netif_dbg(efx
, probe
, efx
->net_dev
, "vi_stride = %u\n",
371 int efx_get_pf_index(struct efx_nic
*efx
, unsigned int *pf_index
)
373 MCDI_DECLARE_BUF(outbuf
, MC_CMD_GET_FUNCTION_INFO_OUT_LEN
);
377 rc
= efx_mcdi_rpc(efx
, MC_CMD_GET_FUNCTION_INFO
, NULL
, 0, outbuf
,
378 sizeof(outbuf
), &outlen
);
381 if (outlen
< sizeof(outbuf
))
384 *pf_index
= MCDI_DWORD(outbuf
, GET_FUNCTION_INFO_OUT_PF
);