mlxbf_gige_rx.c (9567B)
1// SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause 2 3/* Packet receive logic for Mellanox Gigabit Ethernet driver 4 * 5 * Copyright (C) 2020-2021 NVIDIA CORPORATION & AFFILIATES 6 */ 7 8#include <linux/etherdevice.h> 9#include <linux/skbuff.h> 10 11#include "mlxbf_gige.h" 12#include "mlxbf_gige_regs.h" 13 14void mlxbf_gige_set_mac_rx_filter(struct mlxbf_gige *priv, 15 unsigned int index, u64 dmac) 16{ 17 void __iomem *base = priv->base; 18 u64 control; 19 20 /* Write destination MAC to specified MAC RX filter */ 21 writeq(dmac, base + MLXBF_GIGE_RX_MAC_FILTER + 22 (index * MLXBF_GIGE_RX_MAC_FILTER_STRIDE)); 23 24 /* Enable MAC receive filter mask for specified index */ 25 control = readq(base + MLXBF_GIGE_CONTROL); 26 control |= (MLXBF_GIGE_CONTROL_EN_SPECIFIC_MAC << index); 27 writeq(control, base + MLXBF_GIGE_CONTROL); 28} 29 30void mlxbf_gige_get_mac_rx_filter(struct mlxbf_gige *priv, 31 unsigned int index, u64 *dmac) 32{ 33 void __iomem *base = priv->base; 34 35 /* Read destination MAC from specified MAC RX filter */ 36 *dmac = readq(base + MLXBF_GIGE_RX_MAC_FILTER + 37 (index * MLXBF_GIGE_RX_MAC_FILTER_STRIDE)); 38} 39 40void mlxbf_gige_enable_promisc(struct mlxbf_gige *priv) 41{ 42 void __iomem *base = priv->base; 43 u64 control; 44 u64 end_mac; 45 46 /* Enable MAC_ID_RANGE match functionality */ 47 control = readq(base + MLXBF_GIGE_CONTROL); 48 control |= MLXBF_GIGE_CONTROL_MAC_ID_RANGE_EN; 49 writeq(control, base + MLXBF_GIGE_CONTROL); 50 51 /* Set start of destination MAC range check to 0 */ 52 writeq(0, base + MLXBF_GIGE_RX_MAC_FILTER_DMAC_RANGE_START); 53 54 /* Set end of destination MAC range check to all FFs */ 55 end_mac = BCAST_MAC_ADDR; 56 writeq(end_mac, base + MLXBF_GIGE_RX_MAC_FILTER_DMAC_RANGE_END); 57} 58 59void mlxbf_gige_disable_promisc(struct mlxbf_gige *priv) 60{ 61 void __iomem *base = priv->base; 62 u64 control; 63 64 /* Disable MAC_ID_RANGE match functionality */ 65 control = readq(base + MLXBF_GIGE_CONTROL); 66 control &= ~MLXBF_GIGE_CONTROL_MAC_ID_RANGE_EN; 67 writeq(control, base + MLXBF_GIGE_CONTROL); 68 69 /* NOTE: no need to change DMAC_RANGE_START or END; 70 * those values are ignored since MAC_ID_RANGE_EN=0 71 */ 72} 73 74/* Receive Initialization 75 * 1) Configures RX MAC filters via MMIO registers 76 * 2) Allocates RX WQE array using coherent DMA mapping 77 * 3) Initializes each element of RX WQE array with a receive 78 * buffer pointer (also using coherent DMA mapping) 79 * 4) Allocates RX CQE array using coherent DMA mapping 80 * 5) Completes other misc receive initialization 81 */ 82int mlxbf_gige_rx_init(struct mlxbf_gige *priv) 83{ 84 size_t wq_size, cq_size; 85 dma_addr_t *rx_wqe_ptr; 86 dma_addr_t rx_buf_dma; 87 u64 data; 88 int i, j; 89 90 /* Configure MAC RX filter #0 to allow RX of broadcast pkts */ 91 mlxbf_gige_set_mac_rx_filter(priv, MLXBF_GIGE_BCAST_MAC_FILTER_IDX, 92 BCAST_MAC_ADDR); 93 94 wq_size = MLXBF_GIGE_RX_WQE_SZ * priv->rx_q_entries; 95 priv->rx_wqe_base = dma_alloc_coherent(priv->dev, wq_size, 96 &priv->rx_wqe_base_dma, 97 GFP_KERNEL); 98 if (!priv->rx_wqe_base) 99 return -ENOMEM; 100 101 /* Initialize 'rx_wqe_ptr' to point to first RX WQE in array 102 * Each RX WQE is simply a receive buffer pointer, so walk 103 * the entire array, allocating a 2KB buffer for each element 104 */ 105 rx_wqe_ptr = priv->rx_wqe_base; 106 107 for (i = 0; i < priv->rx_q_entries; i++) { 108 priv->rx_skb[i] = mlxbf_gige_alloc_skb(priv, MLXBF_GIGE_DEFAULT_BUF_SZ, 109 &rx_buf_dma, DMA_FROM_DEVICE); 110 if (!priv->rx_skb[i]) 111 goto free_wqe_and_skb; 112 *rx_wqe_ptr++ = rx_buf_dma; 113 } 114 115 /* Write RX WQE base address into MMIO reg */ 116 writeq(priv->rx_wqe_base_dma, priv->base + MLXBF_GIGE_RX_WQ_BASE); 117 118 cq_size = MLXBF_GIGE_RX_CQE_SZ * priv->rx_q_entries; 119 priv->rx_cqe_base = dma_alloc_coherent(priv->dev, cq_size, 120 &priv->rx_cqe_base_dma, 121 GFP_KERNEL); 122 if (!priv->rx_cqe_base) 123 goto free_wqe_and_skb; 124 125 for (i = 0; i < priv->rx_q_entries; i++) 126 priv->rx_cqe_base[i] |= MLXBF_GIGE_RX_CQE_VALID_MASK; 127 128 /* Write RX CQE base address into MMIO reg */ 129 writeq(priv->rx_cqe_base_dma, priv->base + MLXBF_GIGE_RX_CQ_BASE); 130 131 /* Write RX_WQE_PI with current number of replenished buffers */ 132 writeq(priv->rx_q_entries, priv->base + MLXBF_GIGE_RX_WQE_PI); 133 134 /* Enable removal of CRC during RX */ 135 data = readq(priv->base + MLXBF_GIGE_RX); 136 data |= MLXBF_GIGE_RX_STRIP_CRC_EN; 137 writeq(data, priv->base + MLXBF_GIGE_RX); 138 139 /* Enable RX MAC filter pass and discard counters */ 140 writeq(MLXBF_GIGE_RX_MAC_FILTER_COUNT_DISC_EN, 141 priv->base + MLXBF_GIGE_RX_MAC_FILTER_COUNT_DISC); 142 writeq(MLXBF_GIGE_RX_MAC_FILTER_COUNT_PASS_EN, 143 priv->base + MLXBF_GIGE_RX_MAC_FILTER_COUNT_PASS); 144 145 /* Clear MLXBF_GIGE_INT_MASK 'receive pkt' bit to 146 * indicate readiness to receive interrupts 147 */ 148 data = readq(priv->base + MLXBF_GIGE_INT_MASK); 149 data &= ~MLXBF_GIGE_INT_MASK_RX_RECEIVE_PACKET; 150 writeq(data, priv->base + MLXBF_GIGE_INT_MASK); 151 152 /* Enable RX DMA to write new packets to memory */ 153 data = readq(priv->base + MLXBF_GIGE_RX_DMA); 154 data |= MLXBF_GIGE_RX_DMA_EN; 155 writeq(data, priv->base + MLXBF_GIGE_RX_DMA); 156 157 writeq(ilog2(priv->rx_q_entries), 158 priv->base + MLXBF_GIGE_RX_WQE_SIZE_LOG2); 159 160 return 0; 161 162free_wqe_and_skb: 163 rx_wqe_ptr = priv->rx_wqe_base; 164 for (j = 0; j < i; j++) { 165 dma_unmap_single(priv->dev, *rx_wqe_ptr, 166 MLXBF_GIGE_DEFAULT_BUF_SZ, DMA_FROM_DEVICE); 167 dev_kfree_skb(priv->rx_skb[j]); 168 rx_wqe_ptr++; 169 } 170 dma_free_coherent(priv->dev, wq_size, 171 priv->rx_wqe_base, priv->rx_wqe_base_dma); 172 return -ENOMEM; 173} 174 175/* Receive Deinitialization 176 * This routine will free allocations done by mlxbf_gige_rx_init(), 177 * namely the RX WQE and RX CQE arrays, as well as all RX buffers 178 */ 179void mlxbf_gige_rx_deinit(struct mlxbf_gige *priv) 180{ 181 dma_addr_t *rx_wqe_ptr; 182 size_t size; 183 u64 data; 184 int i; 185 186 /* Disable RX DMA to prevent packet transfers to memory */ 187 data = readq(priv->base + MLXBF_GIGE_RX_DMA); 188 data &= ~MLXBF_GIGE_RX_DMA_EN; 189 writeq(data, priv->base + MLXBF_GIGE_RX_DMA); 190 191 rx_wqe_ptr = priv->rx_wqe_base; 192 193 for (i = 0; i < priv->rx_q_entries; i++) { 194 dma_unmap_single(priv->dev, *rx_wqe_ptr, MLXBF_GIGE_DEFAULT_BUF_SZ, 195 DMA_FROM_DEVICE); 196 dev_kfree_skb(priv->rx_skb[i]); 197 rx_wqe_ptr++; 198 } 199 200 size = MLXBF_GIGE_RX_WQE_SZ * priv->rx_q_entries; 201 dma_free_coherent(priv->dev, size, 202 priv->rx_wqe_base, priv->rx_wqe_base_dma); 203 204 size = MLXBF_GIGE_RX_CQE_SZ * priv->rx_q_entries; 205 dma_free_coherent(priv->dev, size, 206 priv->rx_cqe_base, priv->rx_cqe_base_dma); 207 208 priv->rx_wqe_base = NULL; 209 priv->rx_wqe_base_dma = 0; 210 priv->rx_cqe_base = NULL; 211 priv->rx_cqe_base_dma = 0; 212 writeq(0, priv->base + MLXBF_GIGE_RX_WQ_BASE); 213 writeq(0, priv->base + MLXBF_GIGE_RX_CQ_BASE); 214} 215 216static bool mlxbf_gige_rx_packet(struct mlxbf_gige *priv, int *rx_pkts) 217{ 218 struct net_device *netdev = priv->netdev; 219 struct sk_buff *skb = NULL, *rx_skb; 220 u16 rx_pi_rem, rx_ci_rem; 221 dma_addr_t *rx_wqe_addr; 222 dma_addr_t rx_buf_dma; 223 u64 *rx_cqe_addr; 224 u64 datalen; 225 u64 rx_cqe; 226 u16 rx_ci; 227 u16 rx_pi; 228 229 /* Index into RX buffer array is rx_pi w/wrap based on RX_CQE_SIZE */ 230 rx_pi = readq(priv->base + MLXBF_GIGE_RX_WQE_PI); 231 rx_pi_rem = rx_pi % priv->rx_q_entries; 232 233 rx_wqe_addr = priv->rx_wqe_base + rx_pi_rem; 234 rx_cqe_addr = priv->rx_cqe_base + rx_pi_rem; 235 rx_cqe = *rx_cqe_addr; 236 237 if ((!!(rx_cqe & MLXBF_GIGE_RX_CQE_VALID_MASK)) != priv->valid_polarity) 238 return false; 239 240 if ((rx_cqe & MLXBF_GIGE_RX_CQE_PKT_STATUS_MASK) == 0) { 241 /* Packet is OK, increment stats */ 242 datalen = rx_cqe & MLXBF_GIGE_RX_CQE_PKT_LEN_MASK; 243 netdev->stats.rx_packets++; 244 netdev->stats.rx_bytes += datalen; 245 246 skb = priv->rx_skb[rx_pi_rem]; 247 248 skb_put(skb, datalen); 249 250 skb->ip_summed = CHECKSUM_NONE; /* device did not checksum packet */ 251 252 skb->protocol = eth_type_trans(skb, netdev); 253 254 /* Alloc another RX SKB for this same index */ 255 rx_skb = mlxbf_gige_alloc_skb(priv, MLXBF_GIGE_DEFAULT_BUF_SZ, 256 &rx_buf_dma, DMA_FROM_DEVICE); 257 if (!rx_skb) 258 return false; 259 priv->rx_skb[rx_pi_rem] = rx_skb; 260 dma_unmap_single(priv->dev, *rx_wqe_addr, 261 MLXBF_GIGE_DEFAULT_BUF_SZ, DMA_FROM_DEVICE); 262 *rx_wqe_addr = rx_buf_dma; 263 } else if (rx_cqe & MLXBF_GIGE_RX_CQE_PKT_STATUS_MAC_ERR) { 264 priv->stats.rx_mac_errors++; 265 } else if (rx_cqe & MLXBF_GIGE_RX_CQE_PKT_STATUS_TRUNCATED) { 266 priv->stats.rx_truncate_errors++; 267 } 268 269 /* Let hardware know we've replenished one buffer */ 270 rx_pi++; 271 272 /* Ensure completion of all writes before notifying HW of replenish */ 273 wmb(); 274 writeq(rx_pi, priv->base + MLXBF_GIGE_RX_WQE_PI); 275 276 (*rx_pkts)++; 277 278 rx_pi_rem = rx_pi % priv->rx_q_entries; 279 if (rx_pi_rem == 0) 280 priv->valid_polarity ^= 1; 281 rx_ci = readq(priv->base + MLXBF_GIGE_RX_CQE_PACKET_CI); 282 rx_ci_rem = rx_ci % priv->rx_q_entries; 283 284 if (skb) 285 netif_receive_skb(skb); 286 287 return rx_pi_rem != rx_ci_rem; 288} 289 290/* Driver poll() function called by NAPI infrastructure */ 291int mlxbf_gige_poll(struct napi_struct *napi, int budget) 292{ 293 struct mlxbf_gige *priv; 294 bool remaining_pkts; 295 int work_done = 0; 296 u64 data; 297 298 priv = container_of(napi, struct mlxbf_gige, napi); 299 300 mlxbf_gige_handle_tx_complete(priv); 301 302 do { 303 remaining_pkts = mlxbf_gige_rx_packet(priv, &work_done); 304 } while (remaining_pkts && work_done < budget); 305 306 /* If amount of work done < budget, turn off NAPI polling 307 * via napi_complete_done(napi, work_done) and then 308 * re-enable interrupts. 309 */ 310 if (work_done < budget && napi_complete_done(napi, work_done)) { 311 /* Clear MLXBF_GIGE_INT_MASK 'receive pkt' bit to 312 * indicate receive readiness 313 */ 314 data = readq(priv->base + MLXBF_GIGE_INT_MASK); 315 data &= ~MLXBF_GIGE_INT_MASK_RX_RECEIVE_PACKET; 316 writeq(data, priv->base + MLXBF_GIGE_INT_MASK); 317 } 318 319 return work_done; 320}