162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) 262306a36Sopenharmony_ci/* Copyright 2014-2016 Freescale Semiconductor Inc. 362306a36Sopenharmony_ci * Copyright 2016-2022 NXP 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/init.h> 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/platform_device.h> 862306a36Sopenharmony_ci#include <linux/etherdevice.h> 962306a36Sopenharmony_ci#include <linux/of_net.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/kthread.h> 1262306a36Sopenharmony_ci#include <linux/iommu.h> 1362306a36Sopenharmony_ci#include <linux/fsl/mc.h> 1462306a36Sopenharmony_ci#include <linux/bpf.h> 1562306a36Sopenharmony_ci#include <linux/bpf_trace.h> 1662306a36Sopenharmony_ci#include <linux/fsl/ptp_qoriq.h> 1762306a36Sopenharmony_ci#include <linux/ptp_classify.h> 1862306a36Sopenharmony_ci#include <net/pkt_cls.h> 1962306a36Sopenharmony_ci#include <net/sock.h> 2062306a36Sopenharmony_ci#include <net/tso.h> 2162306a36Sopenharmony_ci#include <net/xdp_sock_drv.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "dpaa2-eth.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* CREATE_TRACE_POINTS only needs to be defined once. Other dpa files 2662306a36Sopenharmony_ci * using trace events only need to #include <trace/events/sched.h> 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci#define CREATE_TRACE_POINTS 2962306a36Sopenharmony_ci#include "dpaa2-eth-trace.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 3262306a36Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor, Inc"); 3362306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale DPAA2 Ethernet Driver"); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct ptp_qoriq *dpaa2_ptp; 3662306a36Sopenharmony_ciEXPORT_SYMBOL(dpaa2_ptp); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic void dpaa2_eth_detect_features(struct dpaa2_eth_priv *priv) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci priv->features = 0; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (dpaa2_eth_cmp_dpni_ver(priv, DPNI_PTP_ONESTEP_VER_MAJOR, 4362306a36Sopenharmony_ci DPNI_PTP_ONESTEP_VER_MINOR) >= 0) 4462306a36Sopenharmony_ci priv->features |= DPAA2_ETH_FEATURE_ONESTEP_CFG_DIRECT; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic void dpaa2_update_ptp_onestep_indirect(struct dpaa2_eth_priv *priv, 4862306a36Sopenharmony_ci u32 offset, u8 udp) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct dpni_single_step_cfg cfg; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci cfg.en = 1; 5362306a36Sopenharmony_ci cfg.ch_update = udp; 5462306a36Sopenharmony_ci cfg.offset = offset; 5562306a36Sopenharmony_ci cfg.peer_delay = 0; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (dpni_set_single_step_cfg(priv->mc_io, 0, priv->mc_token, &cfg)) 5862306a36Sopenharmony_ci WARN_ONCE(1, "Failed to set single step register"); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic void dpaa2_update_ptp_onestep_direct(struct dpaa2_eth_priv *priv, 6262306a36Sopenharmony_ci u32 offset, u8 udp) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci u32 val = 0; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci val = DPAA2_PTP_SINGLE_STEP_ENABLE | 6762306a36Sopenharmony_ci DPAA2_PTP_SINGLE_CORRECTION_OFF(offset); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (udp) 7062306a36Sopenharmony_ci val |= DPAA2_PTP_SINGLE_STEP_CH; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (priv->onestep_reg_base) 7362306a36Sopenharmony_ci writel(val, priv->onestep_reg_base); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic void dpaa2_ptp_onestep_reg_update_method(struct dpaa2_eth_priv *priv) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 7962306a36Sopenharmony_ci struct dpni_single_step_cfg ptp_cfg; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci priv->dpaa2_set_onestep_params_cb = dpaa2_update_ptp_onestep_indirect; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (!(priv->features & DPAA2_ETH_FEATURE_ONESTEP_CFG_DIRECT)) 8462306a36Sopenharmony_ci return; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (dpni_get_single_step_cfg(priv->mc_io, 0, 8762306a36Sopenharmony_ci priv->mc_token, &ptp_cfg)) { 8862306a36Sopenharmony_ci dev_err(dev, "dpni_get_single_step_cfg cannot retrieve onestep reg, falling back to indirect update\n"); 8962306a36Sopenharmony_ci return; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (!ptp_cfg.ptp_onestep_reg_base) { 9362306a36Sopenharmony_ci dev_err(dev, "1588 onestep reg not available, falling back to indirect update\n"); 9462306a36Sopenharmony_ci return; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci priv->onestep_reg_base = ioremap(ptp_cfg.ptp_onestep_reg_base, 9862306a36Sopenharmony_ci sizeof(u32)); 9962306a36Sopenharmony_ci if (!priv->onestep_reg_base) { 10062306a36Sopenharmony_ci dev_err(dev, "1588 onestep reg cannot be mapped, falling back to indirect update\n"); 10162306a36Sopenharmony_ci return; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci priv->dpaa2_set_onestep_params_cb = dpaa2_update_ptp_onestep_direct; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_civoid *dpaa2_iova_to_virt(struct iommu_domain *domain, 10862306a36Sopenharmony_ci dma_addr_t iova_addr) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci phys_addr_t phys_addr; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci phys_addr = domain ? iommu_iova_to_phys(domain, iova_addr) : iova_addr; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return phys_to_virt(phys_addr); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void dpaa2_eth_validate_rx_csum(struct dpaa2_eth_priv *priv, 11862306a36Sopenharmony_ci u32 fd_status, 11962306a36Sopenharmony_ci struct sk_buff *skb) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci skb_checksum_none_assert(skb); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* HW checksum validation is disabled, nothing to do here */ 12462306a36Sopenharmony_ci if (!(priv->net_dev->features & NETIF_F_RXCSUM)) 12562306a36Sopenharmony_ci return; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* Read checksum validation bits */ 12862306a36Sopenharmony_ci if (!((fd_status & DPAA2_FAS_L3CV) && 12962306a36Sopenharmony_ci (fd_status & DPAA2_FAS_L4CV))) 13062306a36Sopenharmony_ci return; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* Inform the stack there's no need to compute L3/L4 csum anymore */ 13362306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/* Free a received FD. 13762306a36Sopenharmony_ci * Not to be used for Tx conf FDs or on any other paths. 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_cistatic void dpaa2_eth_free_rx_fd(struct dpaa2_eth_priv *priv, 14062306a36Sopenharmony_ci const struct dpaa2_fd *fd, 14162306a36Sopenharmony_ci void *vaddr) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 14462306a36Sopenharmony_ci dma_addr_t addr = dpaa2_fd_get_addr(fd); 14562306a36Sopenharmony_ci u8 fd_format = dpaa2_fd_get_format(fd); 14662306a36Sopenharmony_ci struct dpaa2_sg_entry *sgt; 14762306a36Sopenharmony_ci void *sg_vaddr; 14862306a36Sopenharmony_ci int i; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* If single buffer frame, just free the data buffer */ 15162306a36Sopenharmony_ci if (fd_format == dpaa2_fd_single) 15262306a36Sopenharmony_ci goto free_buf; 15362306a36Sopenharmony_ci else if (fd_format != dpaa2_fd_sg) 15462306a36Sopenharmony_ci /* We don't support any other format */ 15562306a36Sopenharmony_ci return; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* For S/G frames, we first need to free all SG entries 15862306a36Sopenharmony_ci * except the first one, which was taken care of already 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_ci sgt = vaddr + dpaa2_fd_get_offset(fd); 16162306a36Sopenharmony_ci for (i = 1; i < DPAA2_ETH_MAX_SG_ENTRIES; i++) { 16262306a36Sopenharmony_ci addr = dpaa2_sg_get_addr(&sgt[i]); 16362306a36Sopenharmony_ci sg_vaddr = dpaa2_iova_to_virt(priv->iommu_domain, addr); 16462306a36Sopenharmony_ci dma_unmap_page(dev, addr, priv->rx_buf_size, 16562306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci free_pages((unsigned long)sg_vaddr, 0); 16862306a36Sopenharmony_ci if (dpaa2_sg_is_final(&sgt[i])) 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cifree_buf: 17362306a36Sopenharmony_ci free_pages((unsigned long)vaddr, 0); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci/* Build a linear skb based on a single-buffer frame descriptor */ 17762306a36Sopenharmony_cistatic struct sk_buff *dpaa2_eth_build_linear_skb(struct dpaa2_eth_channel *ch, 17862306a36Sopenharmony_ci const struct dpaa2_fd *fd, 17962306a36Sopenharmony_ci void *fd_vaddr) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct sk_buff *skb = NULL; 18262306a36Sopenharmony_ci u16 fd_offset = dpaa2_fd_get_offset(fd); 18362306a36Sopenharmony_ci u32 fd_length = dpaa2_fd_get_len(fd); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci ch->buf_count--; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci skb = build_skb(fd_vaddr, DPAA2_ETH_RX_BUF_RAW_SIZE); 18862306a36Sopenharmony_ci if (unlikely(!skb)) 18962306a36Sopenharmony_ci return NULL; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci skb_reserve(skb, fd_offset); 19262306a36Sopenharmony_ci skb_put(skb, fd_length); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return skb; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/* Build a non linear (fragmented) skb based on a S/G table */ 19862306a36Sopenharmony_cistatic struct sk_buff *dpaa2_eth_build_frag_skb(struct dpaa2_eth_priv *priv, 19962306a36Sopenharmony_ci struct dpaa2_eth_channel *ch, 20062306a36Sopenharmony_ci struct dpaa2_sg_entry *sgt) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct sk_buff *skb = NULL; 20362306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 20462306a36Sopenharmony_ci void *sg_vaddr; 20562306a36Sopenharmony_ci dma_addr_t sg_addr; 20662306a36Sopenharmony_ci u16 sg_offset; 20762306a36Sopenharmony_ci u32 sg_length; 20862306a36Sopenharmony_ci struct page *page, *head_page; 20962306a36Sopenharmony_ci int page_offset; 21062306a36Sopenharmony_ci int i; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci for (i = 0; i < DPAA2_ETH_MAX_SG_ENTRIES; i++) { 21362306a36Sopenharmony_ci struct dpaa2_sg_entry *sge = &sgt[i]; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* NOTE: We only support SG entries in dpaa2_sg_single format, 21662306a36Sopenharmony_ci * but this is the only format we may receive from HW anyway 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* Get the address and length from the S/G entry */ 22062306a36Sopenharmony_ci sg_addr = dpaa2_sg_get_addr(sge); 22162306a36Sopenharmony_ci sg_vaddr = dpaa2_iova_to_virt(priv->iommu_domain, sg_addr); 22262306a36Sopenharmony_ci dma_unmap_page(dev, sg_addr, priv->rx_buf_size, 22362306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci sg_length = dpaa2_sg_get_len(sge); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (i == 0) { 22862306a36Sopenharmony_ci /* We build the skb around the first data buffer */ 22962306a36Sopenharmony_ci skb = build_skb(sg_vaddr, DPAA2_ETH_RX_BUF_RAW_SIZE); 23062306a36Sopenharmony_ci if (unlikely(!skb)) { 23162306a36Sopenharmony_ci /* Free the first SG entry now, since we already 23262306a36Sopenharmony_ci * unmapped it and obtained the virtual address 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_ci free_pages((unsigned long)sg_vaddr, 0); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* We still need to subtract the buffers used 23762306a36Sopenharmony_ci * by this FD from our software counter 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_ci while (!dpaa2_sg_is_final(&sgt[i]) && 24062306a36Sopenharmony_ci i < DPAA2_ETH_MAX_SG_ENTRIES) 24162306a36Sopenharmony_ci i++; 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci sg_offset = dpaa2_sg_get_offset(sge); 24662306a36Sopenharmony_ci skb_reserve(skb, sg_offset); 24762306a36Sopenharmony_ci skb_put(skb, sg_length); 24862306a36Sopenharmony_ci } else { 24962306a36Sopenharmony_ci /* Rest of the data buffers are stored as skb frags */ 25062306a36Sopenharmony_ci page = virt_to_page(sg_vaddr); 25162306a36Sopenharmony_ci head_page = virt_to_head_page(sg_vaddr); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* Offset in page (which may be compound). 25462306a36Sopenharmony_ci * Data in subsequent SG entries is stored from the 25562306a36Sopenharmony_ci * beginning of the buffer, so we don't need to add the 25662306a36Sopenharmony_ci * sg_offset. 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ci page_offset = ((unsigned long)sg_vaddr & 25962306a36Sopenharmony_ci (PAGE_SIZE - 1)) + 26062306a36Sopenharmony_ci (page_address(page) - page_address(head_page)); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci skb_add_rx_frag(skb, i - 1, head_page, page_offset, 26362306a36Sopenharmony_ci sg_length, priv->rx_buf_size); 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (dpaa2_sg_is_final(sge)) 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci WARN_ONCE(i == DPAA2_ETH_MAX_SG_ENTRIES, "Final bit not set in SGT"); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* Count all data buffers + SG table buffer */ 27362306a36Sopenharmony_ci ch->buf_count -= i + 2; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return skb; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci/* Free buffers acquired from the buffer pool or which were meant to 27962306a36Sopenharmony_ci * be released in the pool 28062306a36Sopenharmony_ci */ 28162306a36Sopenharmony_cistatic void dpaa2_eth_free_bufs(struct dpaa2_eth_priv *priv, u64 *buf_array, 28262306a36Sopenharmony_ci int count, bool xsk_zc) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 28562306a36Sopenharmony_ci struct dpaa2_eth_swa *swa; 28662306a36Sopenharmony_ci struct xdp_buff *xdp_buff; 28762306a36Sopenharmony_ci void *vaddr; 28862306a36Sopenharmony_ci int i; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci for (i = 0; i < count; i++) { 29162306a36Sopenharmony_ci vaddr = dpaa2_iova_to_virt(priv->iommu_domain, buf_array[i]); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (!xsk_zc) { 29462306a36Sopenharmony_ci dma_unmap_page(dev, buf_array[i], priv->rx_buf_size, 29562306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 29662306a36Sopenharmony_ci free_pages((unsigned long)vaddr, 0); 29762306a36Sopenharmony_ci } else { 29862306a36Sopenharmony_ci swa = (struct dpaa2_eth_swa *) 29962306a36Sopenharmony_ci (vaddr + DPAA2_ETH_RX_HWA_SIZE); 30062306a36Sopenharmony_ci xdp_buff = swa->xsk.xdp_buff; 30162306a36Sopenharmony_ci xsk_buff_free(xdp_buff); 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_civoid dpaa2_eth_recycle_buf(struct dpaa2_eth_priv *priv, 30762306a36Sopenharmony_ci struct dpaa2_eth_channel *ch, 30862306a36Sopenharmony_ci dma_addr_t addr) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci int retries = 0; 31162306a36Sopenharmony_ci int err; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci ch->recycled_bufs[ch->recycled_bufs_cnt++] = addr; 31462306a36Sopenharmony_ci if (ch->recycled_bufs_cnt < DPAA2_ETH_BUFS_PER_CMD) 31562306a36Sopenharmony_ci return; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci while ((err = dpaa2_io_service_release(ch->dpio, ch->bp->bpid, 31862306a36Sopenharmony_ci ch->recycled_bufs, 31962306a36Sopenharmony_ci ch->recycled_bufs_cnt)) == -EBUSY) { 32062306a36Sopenharmony_ci if (retries++ >= DPAA2_ETH_SWP_BUSY_RETRIES) 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci cpu_relax(); 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (err) { 32662306a36Sopenharmony_ci dpaa2_eth_free_bufs(priv, ch->recycled_bufs, 32762306a36Sopenharmony_ci ch->recycled_bufs_cnt, ch->xsk_zc); 32862306a36Sopenharmony_ci ch->buf_count -= ch->recycled_bufs_cnt; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci ch->recycled_bufs_cnt = 0; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic int dpaa2_eth_xdp_flush(struct dpaa2_eth_priv *priv, 33562306a36Sopenharmony_ci struct dpaa2_eth_fq *fq, 33662306a36Sopenharmony_ci struct dpaa2_eth_xdp_fds *xdp_fds) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci int total_enqueued = 0, retries = 0, enqueued; 33962306a36Sopenharmony_ci struct dpaa2_eth_drv_stats *percpu_extras; 34062306a36Sopenharmony_ci int num_fds, err, max_retries; 34162306a36Sopenharmony_ci struct dpaa2_fd *fds; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci percpu_extras = this_cpu_ptr(priv->percpu_extras); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* try to enqueue all the FDs until the max number of retries is hit */ 34662306a36Sopenharmony_ci fds = xdp_fds->fds; 34762306a36Sopenharmony_ci num_fds = xdp_fds->num; 34862306a36Sopenharmony_ci max_retries = num_fds * DPAA2_ETH_ENQUEUE_RETRIES; 34962306a36Sopenharmony_ci while (total_enqueued < num_fds && retries < max_retries) { 35062306a36Sopenharmony_ci err = priv->enqueue(priv, fq, &fds[total_enqueued], 35162306a36Sopenharmony_ci 0, num_fds - total_enqueued, &enqueued); 35262306a36Sopenharmony_ci if (err == -EBUSY) { 35362306a36Sopenharmony_ci percpu_extras->tx_portal_busy += ++retries; 35462306a36Sopenharmony_ci continue; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci total_enqueued += enqueued; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci xdp_fds->num = 0; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return total_enqueued; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic void dpaa2_eth_xdp_tx_flush(struct dpaa2_eth_priv *priv, 36462306a36Sopenharmony_ci struct dpaa2_eth_channel *ch, 36562306a36Sopenharmony_ci struct dpaa2_eth_fq *fq) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci struct rtnl_link_stats64 *percpu_stats; 36862306a36Sopenharmony_ci struct dpaa2_fd *fds; 36962306a36Sopenharmony_ci int enqueued, i; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci percpu_stats = this_cpu_ptr(priv->percpu_stats); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci // enqueue the array of XDP_TX frames 37462306a36Sopenharmony_ci enqueued = dpaa2_eth_xdp_flush(priv, fq, &fq->xdp_tx_fds); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* update statistics */ 37762306a36Sopenharmony_ci percpu_stats->tx_packets += enqueued; 37862306a36Sopenharmony_ci fds = fq->xdp_tx_fds.fds; 37962306a36Sopenharmony_ci for (i = 0; i < enqueued; i++) { 38062306a36Sopenharmony_ci percpu_stats->tx_bytes += dpaa2_fd_get_len(&fds[i]); 38162306a36Sopenharmony_ci ch->stats.xdp_tx++; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci for (i = enqueued; i < fq->xdp_tx_fds.num; i++) { 38462306a36Sopenharmony_ci dpaa2_eth_recycle_buf(priv, ch, dpaa2_fd_get_addr(&fds[i])); 38562306a36Sopenharmony_ci percpu_stats->tx_errors++; 38662306a36Sopenharmony_ci ch->stats.xdp_tx_err++; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci fq->xdp_tx_fds.num = 0; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_civoid dpaa2_eth_xdp_enqueue(struct dpaa2_eth_priv *priv, 39262306a36Sopenharmony_ci struct dpaa2_eth_channel *ch, 39362306a36Sopenharmony_ci struct dpaa2_fd *fd, 39462306a36Sopenharmony_ci void *buf_start, u16 queue_id) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct dpaa2_faead *faead; 39762306a36Sopenharmony_ci struct dpaa2_fd *dest_fd; 39862306a36Sopenharmony_ci struct dpaa2_eth_fq *fq; 39962306a36Sopenharmony_ci u32 ctrl, frc; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* Mark the egress frame hardware annotation area as valid */ 40262306a36Sopenharmony_ci frc = dpaa2_fd_get_frc(fd); 40362306a36Sopenharmony_ci dpaa2_fd_set_frc(fd, frc | DPAA2_FD_FRC_FAEADV); 40462306a36Sopenharmony_ci dpaa2_fd_set_ctrl(fd, DPAA2_FD_CTRL_ASAL); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* Instruct hardware to release the FD buffer directly into 40762306a36Sopenharmony_ci * the buffer pool once transmission is completed, instead of 40862306a36Sopenharmony_ci * sending a Tx confirmation frame to us 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_ci ctrl = DPAA2_FAEAD_A4V | DPAA2_FAEAD_A2V | DPAA2_FAEAD_EBDDV; 41162306a36Sopenharmony_ci faead = dpaa2_get_faead(buf_start, false); 41262306a36Sopenharmony_ci faead->ctrl = cpu_to_le32(ctrl); 41362306a36Sopenharmony_ci faead->conf_fqid = 0; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci fq = &priv->fq[queue_id]; 41662306a36Sopenharmony_ci dest_fd = &fq->xdp_tx_fds.fds[fq->xdp_tx_fds.num++]; 41762306a36Sopenharmony_ci memcpy(dest_fd, fd, sizeof(*dest_fd)); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (fq->xdp_tx_fds.num < DEV_MAP_BULK_SIZE) 42062306a36Sopenharmony_ci return; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci dpaa2_eth_xdp_tx_flush(priv, ch, fq); 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic u32 dpaa2_eth_run_xdp(struct dpaa2_eth_priv *priv, 42662306a36Sopenharmony_ci struct dpaa2_eth_channel *ch, 42762306a36Sopenharmony_ci struct dpaa2_eth_fq *rx_fq, 42862306a36Sopenharmony_ci struct dpaa2_fd *fd, void *vaddr) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci dma_addr_t addr = dpaa2_fd_get_addr(fd); 43162306a36Sopenharmony_ci struct bpf_prog *xdp_prog; 43262306a36Sopenharmony_ci struct xdp_buff xdp; 43362306a36Sopenharmony_ci u32 xdp_act = XDP_PASS; 43462306a36Sopenharmony_ci int err, offset; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci xdp_prog = READ_ONCE(ch->xdp.prog); 43762306a36Sopenharmony_ci if (!xdp_prog) 43862306a36Sopenharmony_ci goto out; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci offset = dpaa2_fd_get_offset(fd) - XDP_PACKET_HEADROOM; 44162306a36Sopenharmony_ci xdp_init_buff(&xdp, DPAA2_ETH_RX_BUF_RAW_SIZE - offset, &ch->xdp_rxq); 44262306a36Sopenharmony_ci xdp_prepare_buff(&xdp, vaddr + offset, XDP_PACKET_HEADROOM, 44362306a36Sopenharmony_ci dpaa2_fd_get_len(fd), false); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci xdp_act = bpf_prog_run_xdp(xdp_prog, &xdp); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* xdp.data pointer may have changed */ 44862306a36Sopenharmony_ci dpaa2_fd_set_offset(fd, xdp.data - vaddr); 44962306a36Sopenharmony_ci dpaa2_fd_set_len(fd, xdp.data_end - xdp.data); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci switch (xdp_act) { 45262306a36Sopenharmony_ci case XDP_PASS: 45362306a36Sopenharmony_ci break; 45462306a36Sopenharmony_ci case XDP_TX: 45562306a36Sopenharmony_ci dpaa2_eth_xdp_enqueue(priv, ch, fd, vaddr, rx_fq->flowid); 45662306a36Sopenharmony_ci break; 45762306a36Sopenharmony_ci default: 45862306a36Sopenharmony_ci bpf_warn_invalid_xdp_action(priv->net_dev, xdp_prog, xdp_act); 45962306a36Sopenharmony_ci fallthrough; 46062306a36Sopenharmony_ci case XDP_ABORTED: 46162306a36Sopenharmony_ci trace_xdp_exception(priv->net_dev, xdp_prog, xdp_act); 46262306a36Sopenharmony_ci fallthrough; 46362306a36Sopenharmony_ci case XDP_DROP: 46462306a36Sopenharmony_ci dpaa2_eth_recycle_buf(priv, ch, addr); 46562306a36Sopenharmony_ci ch->stats.xdp_drop++; 46662306a36Sopenharmony_ci break; 46762306a36Sopenharmony_ci case XDP_REDIRECT: 46862306a36Sopenharmony_ci dma_unmap_page(priv->net_dev->dev.parent, addr, 46962306a36Sopenharmony_ci priv->rx_buf_size, DMA_BIDIRECTIONAL); 47062306a36Sopenharmony_ci ch->buf_count--; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* Allow redirect use of full headroom */ 47362306a36Sopenharmony_ci xdp.data_hard_start = vaddr; 47462306a36Sopenharmony_ci xdp.frame_sz = DPAA2_ETH_RX_BUF_RAW_SIZE; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci err = xdp_do_redirect(priv->net_dev, &xdp, xdp_prog); 47762306a36Sopenharmony_ci if (unlikely(err)) { 47862306a36Sopenharmony_ci addr = dma_map_page(priv->net_dev->dev.parent, 47962306a36Sopenharmony_ci virt_to_page(vaddr), 0, 48062306a36Sopenharmony_ci priv->rx_buf_size, DMA_BIDIRECTIONAL); 48162306a36Sopenharmony_ci if (unlikely(dma_mapping_error(priv->net_dev->dev.parent, addr))) { 48262306a36Sopenharmony_ci free_pages((unsigned long)vaddr, 0); 48362306a36Sopenharmony_ci } else { 48462306a36Sopenharmony_ci ch->buf_count++; 48562306a36Sopenharmony_ci dpaa2_eth_recycle_buf(priv, ch, addr); 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci ch->stats.xdp_drop++; 48862306a36Sopenharmony_ci } else { 48962306a36Sopenharmony_ci ch->stats.xdp_redirect++; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci break; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci ch->xdp.res |= xdp_act; 49562306a36Sopenharmony_ciout: 49662306a36Sopenharmony_ci return xdp_act; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistruct sk_buff *dpaa2_eth_alloc_skb(struct dpaa2_eth_priv *priv, 50062306a36Sopenharmony_ci struct dpaa2_eth_channel *ch, 50162306a36Sopenharmony_ci const struct dpaa2_fd *fd, u32 fd_length, 50262306a36Sopenharmony_ci void *fd_vaddr) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci u16 fd_offset = dpaa2_fd_get_offset(fd); 50562306a36Sopenharmony_ci struct sk_buff *skb = NULL; 50662306a36Sopenharmony_ci unsigned int skb_len; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci skb_len = fd_length + dpaa2_eth_needed_headroom(NULL); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci skb = napi_alloc_skb(&ch->napi, skb_len); 51162306a36Sopenharmony_ci if (!skb) 51262306a36Sopenharmony_ci return NULL; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci skb_reserve(skb, dpaa2_eth_needed_headroom(NULL)); 51562306a36Sopenharmony_ci skb_put(skb, fd_length); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci memcpy(skb->data, fd_vaddr + fd_offset, fd_length); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci return skb; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic struct sk_buff *dpaa2_eth_copybreak(struct dpaa2_eth_channel *ch, 52362306a36Sopenharmony_ci const struct dpaa2_fd *fd, 52462306a36Sopenharmony_ci void *fd_vaddr) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = ch->priv; 52762306a36Sopenharmony_ci u32 fd_length = dpaa2_fd_get_len(fd); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (fd_length > priv->rx_copybreak) 53062306a36Sopenharmony_ci return NULL; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci return dpaa2_eth_alloc_skb(priv, ch, fd, fd_length, fd_vaddr); 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_civoid dpaa2_eth_receive_skb(struct dpaa2_eth_priv *priv, 53662306a36Sopenharmony_ci struct dpaa2_eth_channel *ch, 53762306a36Sopenharmony_ci const struct dpaa2_fd *fd, void *vaddr, 53862306a36Sopenharmony_ci struct dpaa2_eth_fq *fq, 53962306a36Sopenharmony_ci struct rtnl_link_stats64 *percpu_stats, 54062306a36Sopenharmony_ci struct sk_buff *skb) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci struct dpaa2_fas *fas; 54362306a36Sopenharmony_ci u32 status = 0; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci fas = dpaa2_get_fas(vaddr, false); 54662306a36Sopenharmony_ci prefetch(fas); 54762306a36Sopenharmony_ci prefetch(skb->data); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* Get the timestamp value */ 55062306a36Sopenharmony_ci if (priv->rx_tstamp) { 55162306a36Sopenharmony_ci struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); 55262306a36Sopenharmony_ci __le64 *ts = dpaa2_get_ts(vaddr, false); 55362306a36Sopenharmony_ci u64 ns; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci memset(shhwtstamps, 0, sizeof(*shhwtstamps)); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci ns = DPAA2_PTP_CLK_PERIOD_NS * le64_to_cpup(ts); 55862306a36Sopenharmony_ci shhwtstamps->hwtstamp = ns_to_ktime(ns); 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* Check if we need to validate the L4 csum */ 56262306a36Sopenharmony_ci if (likely(dpaa2_fd_get_frc(fd) & DPAA2_FD_FRC_FASV)) { 56362306a36Sopenharmony_ci status = le32_to_cpu(fas->status); 56462306a36Sopenharmony_ci dpaa2_eth_validate_rx_csum(priv, status, skb); 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, priv->net_dev); 56862306a36Sopenharmony_ci skb_record_rx_queue(skb, fq->flowid); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci percpu_stats->rx_packets++; 57162306a36Sopenharmony_ci percpu_stats->rx_bytes += dpaa2_fd_get_len(fd); 57262306a36Sopenharmony_ci ch->stats.bytes_per_cdan += dpaa2_fd_get_len(fd); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci list_add_tail(&skb->list, ch->rx_list); 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci/* Main Rx frame processing routine */ 57862306a36Sopenharmony_civoid dpaa2_eth_rx(struct dpaa2_eth_priv *priv, 57962306a36Sopenharmony_ci struct dpaa2_eth_channel *ch, 58062306a36Sopenharmony_ci const struct dpaa2_fd *fd, 58162306a36Sopenharmony_ci struct dpaa2_eth_fq *fq) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci dma_addr_t addr = dpaa2_fd_get_addr(fd); 58462306a36Sopenharmony_ci u8 fd_format = dpaa2_fd_get_format(fd); 58562306a36Sopenharmony_ci void *vaddr; 58662306a36Sopenharmony_ci struct sk_buff *skb; 58762306a36Sopenharmony_ci struct rtnl_link_stats64 *percpu_stats; 58862306a36Sopenharmony_ci struct dpaa2_eth_drv_stats *percpu_extras; 58962306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 59062306a36Sopenharmony_ci bool recycle_rx_buf = false; 59162306a36Sopenharmony_ci void *buf_data; 59262306a36Sopenharmony_ci u32 xdp_act; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci /* Tracing point */ 59562306a36Sopenharmony_ci trace_dpaa2_rx_fd(priv->net_dev, fd); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci vaddr = dpaa2_iova_to_virt(priv->iommu_domain, addr); 59862306a36Sopenharmony_ci dma_sync_single_for_cpu(dev, addr, priv->rx_buf_size, 59962306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci buf_data = vaddr + dpaa2_fd_get_offset(fd); 60262306a36Sopenharmony_ci prefetch(buf_data); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci percpu_stats = this_cpu_ptr(priv->percpu_stats); 60562306a36Sopenharmony_ci percpu_extras = this_cpu_ptr(priv->percpu_extras); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (fd_format == dpaa2_fd_single) { 60862306a36Sopenharmony_ci xdp_act = dpaa2_eth_run_xdp(priv, ch, fq, (struct dpaa2_fd *)fd, vaddr); 60962306a36Sopenharmony_ci if (xdp_act != XDP_PASS) { 61062306a36Sopenharmony_ci percpu_stats->rx_packets++; 61162306a36Sopenharmony_ci percpu_stats->rx_bytes += dpaa2_fd_get_len(fd); 61262306a36Sopenharmony_ci return; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci skb = dpaa2_eth_copybreak(ch, fd, vaddr); 61662306a36Sopenharmony_ci if (!skb) { 61762306a36Sopenharmony_ci dma_unmap_page(dev, addr, priv->rx_buf_size, 61862306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 61962306a36Sopenharmony_ci skb = dpaa2_eth_build_linear_skb(ch, fd, vaddr); 62062306a36Sopenharmony_ci } else { 62162306a36Sopenharmony_ci recycle_rx_buf = true; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci } else if (fd_format == dpaa2_fd_sg) { 62462306a36Sopenharmony_ci WARN_ON(priv->xdp_prog); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci dma_unmap_page(dev, addr, priv->rx_buf_size, 62762306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 62862306a36Sopenharmony_ci skb = dpaa2_eth_build_frag_skb(priv, ch, buf_data); 62962306a36Sopenharmony_ci free_pages((unsigned long)vaddr, 0); 63062306a36Sopenharmony_ci percpu_extras->rx_sg_frames++; 63162306a36Sopenharmony_ci percpu_extras->rx_sg_bytes += dpaa2_fd_get_len(fd); 63262306a36Sopenharmony_ci } else { 63362306a36Sopenharmony_ci /* We don't support any other format */ 63462306a36Sopenharmony_ci goto err_frame_format; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (unlikely(!skb)) 63862306a36Sopenharmony_ci goto err_build_skb; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci dpaa2_eth_receive_skb(priv, ch, fd, vaddr, fq, percpu_stats, skb); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (recycle_rx_buf) 64362306a36Sopenharmony_ci dpaa2_eth_recycle_buf(priv, ch, dpaa2_fd_get_addr(fd)); 64462306a36Sopenharmony_ci return; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cierr_build_skb: 64762306a36Sopenharmony_ci dpaa2_eth_free_rx_fd(priv, fd, vaddr); 64862306a36Sopenharmony_cierr_frame_format: 64962306a36Sopenharmony_ci percpu_stats->rx_dropped++; 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci/* Processing of Rx frames received on the error FQ 65362306a36Sopenharmony_ci * We check and print the error bits and then free the frame 65462306a36Sopenharmony_ci */ 65562306a36Sopenharmony_cistatic void dpaa2_eth_rx_err(struct dpaa2_eth_priv *priv, 65662306a36Sopenharmony_ci struct dpaa2_eth_channel *ch, 65762306a36Sopenharmony_ci const struct dpaa2_fd *fd, 65862306a36Sopenharmony_ci struct dpaa2_eth_fq *fq __always_unused) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 66162306a36Sopenharmony_ci dma_addr_t addr = dpaa2_fd_get_addr(fd); 66262306a36Sopenharmony_ci u8 fd_format = dpaa2_fd_get_format(fd); 66362306a36Sopenharmony_ci struct rtnl_link_stats64 *percpu_stats; 66462306a36Sopenharmony_ci struct dpaa2_eth_trap_item *trap_item; 66562306a36Sopenharmony_ci struct dpaa2_fapr *fapr; 66662306a36Sopenharmony_ci struct sk_buff *skb; 66762306a36Sopenharmony_ci void *buf_data; 66862306a36Sopenharmony_ci void *vaddr; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci vaddr = dpaa2_iova_to_virt(priv->iommu_domain, addr); 67162306a36Sopenharmony_ci dma_sync_single_for_cpu(dev, addr, priv->rx_buf_size, 67262306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci buf_data = vaddr + dpaa2_fd_get_offset(fd); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (fd_format == dpaa2_fd_single) { 67762306a36Sopenharmony_ci dma_unmap_page(dev, addr, priv->rx_buf_size, 67862306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 67962306a36Sopenharmony_ci skb = dpaa2_eth_build_linear_skb(ch, fd, vaddr); 68062306a36Sopenharmony_ci } else if (fd_format == dpaa2_fd_sg) { 68162306a36Sopenharmony_ci dma_unmap_page(dev, addr, priv->rx_buf_size, 68262306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 68362306a36Sopenharmony_ci skb = dpaa2_eth_build_frag_skb(priv, ch, buf_data); 68462306a36Sopenharmony_ci free_pages((unsigned long)vaddr, 0); 68562306a36Sopenharmony_ci } else { 68662306a36Sopenharmony_ci /* We don't support any other format */ 68762306a36Sopenharmony_ci dpaa2_eth_free_rx_fd(priv, fd, vaddr); 68862306a36Sopenharmony_ci goto err_frame_format; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci fapr = dpaa2_get_fapr(vaddr, false); 69262306a36Sopenharmony_ci trap_item = dpaa2_eth_dl_get_trap(priv, fapr); 69362306a36Sopenharmony_ci if (trap_item) 69462306a36Sopenharmony_ci devlink_trap_report(priv->devlink, skb, trap_item->trap_ctx, 69562306a36Sopenharmony_ci &priv->devlink_port, NULL); 69662306a36Sopenharmony_ci consume_skb(skb); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cierr_frame_format: 69962306a36Sopenharmony_ci percpu_stats = this_cpu_ptr(priv->percpu_stats); 70062306a36Sopenharmony_ci percpu_stats->rx_errors++; 70162306a36Sopenharmony_ci ch->buf_count--; 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci/* Consume all frames pull-dequeued into the store. This is the simplest way to 70562306a36Sopenharmony_ci * make sure we don't accidentally issue another volatile dequeue which would 70662306a36Sopenharmony_ci * overwrite (leak) frames already in the store. 70762306a36Sopenharmony_ci * 70862306a36Sopenharmony_ci * Observance of NAPI budget is not our concern, leaving that to the caller. 70962306a36Sopenharmony_ci */ 71062306a36Sopenharmony_cistatic int dpaa2_eth_consume_frames(struct dpaa2_eth_channel *ch, 71162306a36Sopenharmony_ci struct dpaa2_eth_fq **src) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = ch->priv; 71462306a36Sopenharmony_ci struct dpaa2_eth_fq *fq = NULL; 71562306a36Sopenharmony_ci struct dpaa2_dq *dq; 71662306a36Sopenharmony_ci const struct dpaa2_fd *fd; 71762306a36Sopenharmony_ci int cleaned = 0, retries = 0; 71862306a36Sopenharmony_ci int is_last; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci do { 72162306a36Sopenharmony_ci dq = dpaa2_io_store_next(ch->store, &is_last); 72262306a36Sopenharmony_ci if (unlikely(!dq)) { 72362306a36Sopenharmony_ci /* If we're here, we *must* have placed a 72462306a36Sopenharmony_ci * volatile dequeue comnmand, so keep reading through 72562306a36Sopenharmony_ci * the store until we get some sort of valid response 72662306a36Sopenharmony_ci * token (either a valid frame or an "empty dequeue") 72762306a36Sopenharmony_ci */ 72862306a36Sopenharmony_ci if (retries++ >= DPAA2_ETH_SWP_BUSY_RETRIES) { 72962306a36Sopenharmony_ci netdev_err_once(priv->net_dev, 73062306a36Sopenharmony_ci "Unable to read a valid dequeue response\n"); 73162306a36Sopenharmony_ci return -ETIMEDOUT; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci continue; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci fd = dpaa2_dq_fd(dq); 73762306a36Sopenharmony_ci fq = (struct dpaa2_eth_fq *)(uintptr_t)dpaa2_dq_fqd_ctx(dq); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci fq->consume(priv, ch, fd, fq); 74062306a36Sopenharmony_ci cleaned++; 74162306a36Sopenharmony_ci retries = 0; 74262306a36Sopenharmony_ci } while (!is_last); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci if (!cleaned) 74562306a36Sopenharmony_ci return 0; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci fq->stats.frames += cleaned; 74862306a36Sopenharmony_ci ch->stats.frames += cleaned; 74962306a36Sopenharmony_ci ch->stats.frames_per_cdan += cleaned; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci /* A dequeue operation only pulls frames from a single queue 75262306a36Sopenharmony_ci * into the store. Return the frame queue as an out param. 75362306a36Sopenharmony_ci */ 75462306a36Sopenharmony_ci if (src) 75562306a36Sopenharmony_ci *src = fq; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci return cleaned; 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_cistatic int dpaa2_eth_ptp_parse(struct sk_buff *skb, 76162306a36Sopenharmony_ci u8 *msgtype, u8 *twostep, u8 *udp, 76262306a36Sopenharmony_ci u16 *correction_offset, 76362306a36Sopenharmony_ci u16 *origintimestamp_offset) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci unsigned int ptp_class; 76662306a36Sopenharmony_ci struct ptp_header *hdr; 76762306a36Sopenharmony_ci unsigned int type; 76862306a36Sopenharmony_ci u8 *base; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci ptp_class = ptp_classify_raw(skb); 77162306a36Sopenharmony_ci if (ptp_class == PTP_CLASS_NONE) 77262306a36Sopenharmony_ci return -EINVAL; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci hdr = ptp_parse_header(skb, ptp_class); 77562306a36Sopenharmony_ci if (!hdr) 77662306a36Sopenharmony_ci return -EINVAL; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci *msgtype = ptp_get_msgtype(hdr, ptp_class); 77962306a36Sopenharmony_ci *twostep = hdr->flag_field[0] & 0x2; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci type = ptp_class & PTP_CLASS_PMASK; 78262306a36Sopenharmony_ci if (type == PTP_CLASS_IPV4 || 78362306a36Sopenharmony_ci type == PTP_CLASS_IPV6) 78462306a36Sopenharmony_ci *udp = 1; 78562306a36Sopenharmony_ci else 78662306a36Sopenharmony_ci *udp = 0; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci base = skb_mac_header(skb); 78962306a36Sopenharmony_ci *correction_offset = (u8 *)&hdr->correction - base; 79062306a36Sopenharmony_ci *origintimestamp_offset = (u8 *)hdr + sizeof(struct ptp_header) - base; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci return 0; 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci/* Configure the egress frame annotation for timestamp update */ 79662306a36Sopenharmony_cistatic void dpaa2_eth_enable_tx_tstamp(struct dpaa2_eth_priv *priv, 79762306a36Sopenharmony_ci struct dpaa2_fd *fd, 79862306a36Sopenharmony_ci void *buf_start, 79962306a36Sopenharmony_ci struct sk_buff *skb) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci struct ptp_tstamp origin_timestamp; 80262306a36Sopenharmony_ci u8 msgtype, twostep, udp; 80362306a36Sopenharmony_ci struct dpaa2_faead *faead; 80462306a36Sopenharmony_ci struct dpaa2_fas *fas; 80562306a36Sopenharmony_ci struct timespec64 ts; 80662306a36Sopenharmony_ci u16 offset1, offset2; 80762306a36Sopenharmony_ci u32 ctrl, frc; 80862306a36Sopenharmony_ci __le64 *ns; 80962306a36Sopenharmony_ci u8 *data; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci /* Mark the egress frame annotation area as valid */ 81262306a36Sopenharmony_ci frc = dpaa2_fd_get_frc(fd); 81362306a36Sopenharmony_ci dpaa2_fd_set_frc(fd, frc | DPAA2_FD_FRC_FAEADV); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci /* Set hardware annotation size */ 81662306a36Sopenharmony_ci ctrl = dpaa2_fd_get_ctrl(fd); 81762306a36Sopenharmony_ci dpaa2_fd_set_ctrl(fd, ctrl | DPAA2_FD_CTRL_ASAL); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci /* enable UPD (update prepanded data) bit in FAEAD field of 82062306a36Sopenharmony_ci * hardware frame annotation area 82162306a36Sopenharmony_ci */ 82262306a36Sopenharmony_ci ctrl = DPAA2_FAEAD_A2V | DPAA2_FAEAD_UPDV | DPAA2_FAEAD_UPD; 82362306a36Sopenharmony_ci faead = dpaa2_get_faead(buf_start, true); 82462306a36Sopenharmony_ci faead->ctrl = cpu_to_le32(ctrl); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci if (skb->cb[0] == TX_TSTAMP_ONESTEP_SYNC) { 82762306a36Sopenharmony_ci if (dpaa2_eth_ptp_parse(skb, &msgtype, &twostep, &udp, 82862306a36Sopenharmony_ci &offset1, &offset2) || 82962306a36Sopenharmony_ci msgtype != PTP_MSGTYPE_SYNC || twostep) { 83062306a36Sopenharmony_ci WARN_ONCE(1, "Bad packet for one-step timestamping\n"); 83162306a36Sopenharmony_ci return; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci /* Mark the frame annotation status as valid */ 83562306a36Sopenharmony_ci frc = dpaa2_fd_get_frc(fd); 83662306a36Sopenharmony_ci dpaa2_fd_set_frc(fd, frc | DPAA2_FD_FRC_FASV); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* Mark the PTP flag for one step timestamping */ 83962306a36Sopenharmony_ci fas = dpaa2_get_fas(buf_start, true); 84062306a36Sopenharmony_ci fas->status = cpu_to_le32(DPAA2_FAS_PTP); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci dpaa2_ptp->caps.gettime64(&dpaa2_ptp->caps, &ts); 84362306a36Sopenharmony_ci ns = dpaa2_get_ts(buf_start, true); 84462306a36Sopenharmony_ci *ns = cpu_to_le64(timespec64_to_ns(&ts) / 84562306a36Sopenharmony_ci DPAA2_PTP_CLK_PERIOD_NS); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci /* Update current time to PTP message originTimestamp field */ 84862306a36Sopenharmony_ci ns_to_ptp_tstamp(&origin_timestamp, le64_to_cpup(ns)); 84962306a36Sopenharmony_ci data = skb_mac_header(skb); 85062306a36Sopenharmony_ci *(__be16 *)(data + offset2) = htons(origin_timestamp.sec_msb); 85162306a36Sopenharmony_ci *(__be32 *)(data + offset2 + 2) = 85262306a36Sopenharmony_ci htonl(origin_timestamp.sec_lsb); 85362306a36Sopenharmony_ci *(__be32 *)(data + offset2 + 6) = htonl(origin_timestamp.nsec); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci if (priv->ptp_correction_off == offset1) 85662306a36Sopenharmony_ci return; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci priv->dpaa2_set_onestep_params_cb(priv, offset1, udp); 85962306a36Sopenharmony_ci priv->ptp_correction_off = offset1; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci} 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_civoid *dpaa2_eth_sgt_get(struct dpaa2_eth_priv *priv) 86562306a36Sopenharmony_ci{ 86662306a36Sopenharmony_ci struct dpaa2_eth_sgt_cache *sgt_cache; 86762306a36Sopenharmony_ci void *sgt_buf = NULL; 86862306a36Sopenharmony_ci int sgt_buf_size; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci sgt_cache = this_cpu_ptr(priv->sgt_cache); 87162306a36Sopenharmony_ci sgt_buf_size = priv->tx_data_offset + 87262306a36Sopenharmony_ci DPAA2_ETH_SG_ENTRIES_MAX * sizeof(struct dpaa2_sg_entry); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if (sgt_cache->count == 0) 87562306a36Sopenharmony_ci sgt_buf = napi_alloc_frag_align(sgt_buf_size, DPAA2_ETH_TX_BUF_ALIGN); 87662306a36Sopenharmony_ci else 87762306a36Sopenharmony_ci sgt_buf = sgt_cache->buf[--sgt_cache->count]; 87862306a36Sopenharmony_ci if (!sgt_buf) 87962306a36Sopenharmony_ci return NULL; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci memset(sgt_buf, 0, sgt_buf_size); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci return sgt_buf; 88462306a36Sopenharmony_ci} 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_civoid dpaa2_eth_sgt_recycle(struct dpaa2_eth_priv *priv, void *sgt_buf) 88762306a36Sopenharmony_ci{ 88862306a36Sopenharmony_ci struct dpaa2_eth_sgt_cache *sgt_cache; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci sgt_cache = this_cpu_ptr(priv->sgt_cache); 89162306a36Sopenharmony_ci if (sgt_cache->count >= DPAA2_ETH_SGT_CACHE_SIZE) 89262306a36Sopenharmony_ci skb_free_frag(sgt_buf); 89362306a36Sopenharmony_ci else 89462306a36Sopenharmony_ci sgt_cache->buf[sgt_cache->count++] = sgt_buf; 89562306a36Sopenharmony_ci} 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci/* Create a frame descriptor based on a fragmented skb */ 89862306a36Sopenharmony_cistatic int dpaa2_eth_build_sg_fd(struct dpaa2_eth_priv *priv, 89962306a36Sopenharmony_ci struct sk_buff *skb, 90062306a36Sopenharmony_ci struct dpaa2_fd *fd, 90162306a36Sopenharmony_ci void **swa_addr) 90262306a36Sopenharmony_ci{ 90362306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 90462306a36Sopenharmony_ci void *sgt_buf = NULL; 90562306a36Sopenharmony_ci dma_addr_t addr; 90662306a36Sopenharmony_ci int nr_frags = skb_shinfo(skb)->nr_frags; 90762306a36Sopenharmony_ci struct dpaa2_sg_entry *sgt; 90862306a36Sopenharmony_ci int i, err; 90962306a36Sopenharmony_ci int sgt_buf_size; 91062306a36Sopenharmony_ci struct scatterlist *scl, *crt_scl; 91162306a36Sopenharmony_ci int num_sg; 91262306a36Sopenharmony_ci int num_dma_bufs; 91362306a36Sopenharmony_ci struct dpaa2_eth_swa *swa; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci /* Create and map scatterlist. 91662306a36Sopenharmony_ci * We don't advertise NETIF_F_FRAGLIST, so skb_to_sgvec() will not have 91762306a36Sopenharmony_ci * to go beyond nr_frags+1. 91862306a36Sopenharmony_ci * Note: We don't support chained scatterlists 91962306a36Sopenharmony_ci */ 92062306a36Sopenharmony_ci if (unlikely(PAGE_SIZE / sizeof(struct scatterlist) < nr_frags + 1)) 92162306a36Sopenharmony_ci return -EINVAL; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci scl = kmalloc_array(nr_frags + 1, sizeof(struct scatterlist), GFP_ATOMIC); 92462306a36Sopenharmony_ci if (unlikely(!scl)) 92562306a36Sopenharmony_ci return -ENOMEM; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci sg_init_table(scl, nr_frags + 1); 92862306a36Sopenharmony_ci num_sg = skb_to_sgvec(skb, scl, 0, skb->len); 92962306a36Sopenharmony_ci if (unlikely(num_sg < 0)) { 93062306a36Sopenharmony_ci err = -ENOMEM; 93162306a36Sopenharmony_ci goto dma_map_sg_failed; 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci num_dma_bufs = dma_map_sg(dev, scl, num_sg, DMA_BIDIRECTIONAL); 93462306a36Sopenharmony_ci if (unlikely(!num_dma_bufs)) { 93562306a36Sopenharmony_ci err = -ENOMEM; 93662306a36Sopenharmony_ci goto dma_map_sg_failed; 93762306a36Sopenharmony_ci } 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci /* Prepare the HW SGT structure */ 94062306a36Sopenharmony_ci sgt_buf_size = priv->tx_data_offset + 94162306a36Sopenharmony_ci sizeof(struct dpaa2_sg_entry) * num_dma_bufs; 94262306a36Sopenharmony_ci sgt_buf = dpaa2_eth_sgt_get(priv); 94362306a36Sopenharmony_ci if (unlikely(!sgt_buf)) { 94462306a36Sopenharmony_ci err = -ENOMEM; 94562306a36Sopenharmony_ci goto sgt_buf_alloc_failed; 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci sgt = (struct dpaa2_sg_entry *)(sgt_buf + priv->tx_data_offset); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci /* Fill in the HW SGT structure. 95162306a36Sopenharmony_ci * 95262306a36Sopenharmony_ci * sgt_buf is zeroed out, so the following fields are implicit 95362306a36Sopenharmony_ci * in all sgt entries: 95462306a36Sopenharmony_ci * - offset is 0 95562306a36Sopenharmony_ci * - format is 'dpaa2_sg_single' 95662306a36Sopenharmony_ci */ 95762306a36Sopenharmony_ci for_each_sg(scl, crt_scl, num_dma_bufs, i) { 95862306a36Sopenharmony_ci dpaa2_sg_set_addr(&sgt[i], sg_dma_address(crt_scl)); 95962306a36Sopenharmony_ci dpaa2_sg_set_len(&sgt[i], sg_dma_len(crt_scl)); 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci dpaa2_sg_set_final(&sgt[i - 1], true); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci /* Store the skb backpointer in the SGT buffer. 96462306a36Sopenharmony_ci * Fit the scatterlist and the number of buffers alongside the 96562306a36Sopenharmony_ci * skb backpointer in the software annotation area. We'll need 96662306a36Sopenharmony_ci * all of them on Tx Conf. 96762306a36Sopenharmony_ci */ 96862306a36Sopenharmony_ci *swa_addr = (void *)sgt_buf; 96962306a36Sopenharmony_ci swa = (struct dpaa2_eth_swa *)sgt_buf; 97062306a36Sopenharmony_ci swa->type = DPAA2_ETH_SWA_SG; 97162306a36Sopenharmony_ci swa->sg.skb = skb; 97262306a36Sopenharmony_ci swa->sg.scl = scl; 97362306a36Sopenharmony_ci swa->sg.num_sg = num_sg; 97462306a36Sopenharmony_ci swa->sg.sgt_size = sgt_buf_size; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* Separately map the SGT buffer */ 97762306a36Sopenharmony_ci addr = dma_map_single(dev, sgt_buf, sgt_buf_size, DMA_BIDIRECTIONAL); 97862306a36Sopenharmony_ci if (unlikely(dma_mapping_error(dev, addr))) { 97962306a36Sopenharmony_ci err = -ENOMEM; 98062306a36Sopenharmony_ci goto dma_map_single_failed; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci memset(fd, 0, sizeof(struct dpaa2_fd)); 98362306a36Sopenharmony_ci dpaa2_fd_set_offset(fd, priv->tx_data_offset); 98462306a36Sopenharmony_ci dpaa2_fd_set_format(fd, dpaa2_fd_sg); 98562306a36Sopenharmony_ci dpaa2_fd_set_addr(fd, addr); 98662306a36Sopenharmony_ci dpaa2_fd_set_len(fd, skb->len); 98762306a36Sopenharmony_ci dpaa2_fd_set_ctrl(fd, FD_CTRL_PTA); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci return 0; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_cidma_map_single_failed: 99262306a36Sopenharmony_ci dpaa2_eth_sgt_recycle(priv, sgt_buf); 99362306a36Sopenharmony_cisgt_buf_alloc_failed: 99462306a36Sopenharmony_ci dma_unmap_sg(dev, scl, num_sg, DMA_BIDIRECTIONAL); 99562306a36Sopenharmony_cidma_map_sg_failed: 99662306a36Sopenharmony_ci kfree(scl); 99762306a36Sopenharmony_ci return err; 99862306a36Sopenharmony_ci} 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci/* Create a SG frame descriptor based on a linear skb. 100162306a36Sopenharmony_ci * 100262306a36Sopenharmony_ci * This function is used on the Tx path when the skb headroom is not large 100362306a36Sopenharmony_ci * enough for the HW requirements, thus instead of realloc-ing the skb we 100462306a36Sopenharmony_ci * create a SG frame descriptor with only one entry. 100562306a36Sopenharmony_ci */ 100662306a36Sopenharmony_cistatic int dpaa2_eth_build_sg_fd_single_buf(struct dpaa2_eth_priv *priv, 100762306a36Sopenharmony_ci struct sk_buff *skb, 100862306a36Sopenharmony_ci struct dpaa2_fd *fd, 100962306a36Sopenharmony_ci void **swa_addr) 101062306a36Sopenharmony_ci{ 101162306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 101262306a36Sopenharmony_ci struct dpaa2_sg_entry *sgt; 101362306a36Sopenharmony_ci struct dpaa2_eth_swa *swa; 101462306a36Sopenharmony_ci dma_addr_t addr, sgt_addr; 101562306a36Sopenharmony_ci void *sgt_buf = NULL; 101662306a36Sopenharmony_ci int sgt_buf_size; 101762306a36Sopenharmony_ci int err; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci /* Prepare the HW SGT structure */ 102062306a36Sopenharmony_ci sgt_buf_size = priv->tx_data_offset + sizeof(struct dpaa2_sg_entry); 102162306a36Sopenharmony_ci sgt_buf = dpaa2_eth_sgt_get(priv); 102262306a36Sopenharmony_ci if (unlikely(!sgt_buf)) 102362306a36Sopenharmony_ci return -ENOMEM; 102462306a36Sopenharmony_ci sgt = (struct dpaa2_sg_entry *)(sgt_buf + priv->tx_data_offset); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci addr = dma_map_single(dev, skb->data, skb->len, DMA_BIDIRECTIONAL); 102762306a36Sopenharmony_ci if (unlikely(dma_mapping_error(dev, addr))) { 102862306a36Sopenharmony_ci err = -ENOMEM; 102962306a36Sopenharmony_ci goto data_map_failed; 103062306a36Sopenharmony_ci } 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci /* Fill in the HW SGT structure */ 103362306a36Sopenharmony_ci dpaa2_sg_set_addr(sgt, addr); 103462306a36Sopenharmony_ci dpaa2_sg_set_len(sgt, skb->len); 103562306a36Sopenharmony_ci dpaa2_sg_set_final(sgt, true); 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci /* Store the skb backpointer in the SGT buffer */ 103862306a36Sopenharmony_ci *swa_addr = (void *)sgt_buf; 103962306a36Sopenharmony_ci swa = (struct dpaa2_eth_swa *)sgt_buf; 104062306a36Sopenharmony_ci swa->type = DPAA2_ETH_SWA_SINGLE; 104162306a36Sopenharmony_ci swa->single.skb = skb; 104262306a36Sopenharmony_ci swa->single.sgt_size = sgt_buf_size; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci /* Separately map the SGT buffer */ 104562306a36Sopenharmony_ci sgt_addr = dma_map_single(dev, sgt_buf, sgt_buf_size, DMA_BIDIRECTIONAL); 104662306a36Sopenharmony_ci if (unlikely(dma_mapping_error(dev, sgt_addr))) { 104762306a36Sopenharmony_ci err = -ENOMEM; 104862306a36Sopenharmony_ci goto sgt_map_failed; 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci memset(fd, 0, sizeof(struct dpaa2_fd)); 105262306a36Sopenharmony_ci dpaa2_fd_set_offset(fd, priv->tx_data_offset); 105362306a36Sopenharmony_ci dpaa2_fd_set_format(fd, dpaa2_fd_sg); 105462306a36Sopenharmony_ci dpaa2_fd_set_addr(fd, sgt_addr); 105562306a36Sopenharmony_ci dpaa2_fd_set_len(fd, skb->len); 105662306a36Sopenharmony_ci dpaa2_fd_set_ctrl(fd, FD_CTRL_PTA); 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci return 0; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_cisgt_map_failed: 106162306a36Sopenharmony_ci dma_unmap_single(dev, addr, skb->len, DMA_BIDIRECTIONAL); 106262306a36Sopenharmony_cidata_map_failed: 106362306a36Sopenharmony_ci dpaa2_eth_sgt_recycle(priv, sgt_buf); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci return err; 106662306a36Sopenharmony_ci} 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci/* Create a frame descriptor based on a linear skb */ 106962306a36Sopenharmony_cistatic int dpaa2_eth_build_single_fd(struct dpaa2_eth_priv *priv, 107062306a36Sopenharmony_ci struct sk_buff *skb, 107162306a36Sopenharmony_ci struct dpaa2_fd *fd, 107262306a36Sopenharmony_ci void **swa_addr) 107362306a36Sopenharmony_ci{ 107462306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 107562306a36Sopenharmony_ci u8 *buffer_start, *aligned_start; 107662306a36Sopenharmony_ci struct dpaa2_eth_swa *swa; 107762306a36Sopenharmony_ci dma_addr_t addr; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci buffer_start = skb->data - dpaa2_eth_needed_headroom(skb); 108062306a36Sopenharmony_ci aligned_start = PTR_ALIGN(buffer_start - DPAA2_ETH_TX_BUF_ALIGN, 108162306a36Sopenharmony_ci DPAA2_ETH_TX_BUF_ALIGN); 108262306a36Sopenharmony_ci if (aligned_start >= skb->head) 108362306a36Sopenharmony_ci buffer_start = aligned_start; 108462306a36Sopenharmony_ci else 108562306a36Sopenharmony_ci return -ENOMEM; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci /* Store a backpointer to the skb at the beginning of the buffer 108862306a36Sopenharmony_ci * (in the private data area) such that we can release it 108962306a36Sopenharmony_ci * on Tx confirm 109062306a36Sopenharmony_ci */ 109162306a36Sopenharmony_ci *swa_addr = (void *)buffer_start; 109262306a36Sopenharmony_ci swa = (struct dpaa2_eth_swa *)buffer_start; 109362306a36Sopenharmony_ci swa->type = DPAA2_ETH_SWA_SINGLE; 109462306a36Sopenharmony_ci swa->single.skb = skb; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci addr = dma_map_single(dev, buffer_start, 109762306a36Sopenharmony_ci skb_tail_pointer(skb) - buffer_start, 109862306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 109962306a36Sopenharmony_ci if (unlikely(dma_mapping_error(dev, addr))) 110062306a36Sopenharmony_ci return -ENOMEM; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci memset(fd, 0, sizeof(struct dpaa2_fd)); 110362306a36Sopenharmony_ci dpaa2_fd_set_addr(fd, addr); 110462306a36Sopenharmony_ci dpaa2_fd_set_offset(fd, (u16)(skb->data - buffer_start)); 110562306a36Sopenharmony_ci dpaa2_fd_set_len(fd, skb->len); 110662306a36Sopenharmony_ci dpaa2_fd_set_format(fd, dpaa2_fd_single); 110762306a36Sopenharmony_ci dpaa2_fd_set_ctrl(fd, FD_CTRL_PTA); 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci return 0; 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci/* FD freeing routine on the Tx path 111362306a36Sopenharmony_ci * 111462306a36Sopenharmony_ci * DMA-unmap and free FD and possibly SGT buffer allocated on Tx. The skb 111562306a36Sopenharmony_ci * back-pointed to is also freed. 111662306a36Sopenharmony_ci * This can be called either from dpaa2_eth_tx_conf() or on the error path of 111762306a36Sopenharmony_ci * dpaa2_eth_tx(). 111862306a36Sopenharmony_ci */ 111962306a36Sopenharmony_civoid dpaa2_eth_free_tx_fd(struct dpaa2_eth_priv *priv, 112062306a36Sopenharmony_ci struct dpaa2_eth_channel *ch, 112162306a36Sopenharmony_ci struct dpaa2_eth_fq *fq, 112262306a36Sopenharmony_ci const struct dpaa2_fd *fd, bool in_napi) 112362306a36Sopenharmony_ci{ 112462306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 112562306a36Sopenharmony_ci dma_addr_t fd_addr, sg_addr; 112662306a36Sopenharmony_ci struct sk_buff *skb = NULL; 112762306a36Sopenharmony_ci unsigned char *buffer_start; 112862306a36Sopenharmony_ci struct dpaa2_eth_swa *swa; 112962306a36Sopenharmony_ci u8 fd_format = dpaa2_fd_get_format(fd); 113062306a36Sopenharmony_ci u32 fd_len = dpaa2_fd_get_len(fd); 113162306a36Sopenharmony_ci struct dpaa2_sg_entry *sgt; 113262306a36Sopenharmony_ci int should_free_skb = 1; 113362306a36Sopenharmony_ci void *tso_hdr; 113462306a36Sopenharmony_ci int i; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci fd_addr = dpaa2_fd_get_addr(fd); 113762306a36Sopenharmony_ci buffer_start = dpaa2_iova_to_virt(priv->iommu_domain, fd_addr); 113862306a36Sopenharmony_ci swa = (struct dpaa2_eth_swa *)buffer_start; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci if (fd_format == dpaa2_fd_single) { 114162306a36Sopenharmony_ci if (swa->type == DPAA2_ETH_SWA_SINGLE) { 114262306a36Sopenharmony_ci skb = swa->single.skb; 114362306a36Sopenharmony_ci /* Accessing the skb buffer is safe before dma unmap, 114462306a36Sopenharmony_ci * because we didn't map the actual skb shell. 114562306a36Sopenharmony_ci */ 114662306a36Sopenharmony_ci dma_unmap_single(dev, fd_addr, 114762306a36Sopenharmony_ci skb_tail_pointer(skb) - buffer_start, 114862306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 114962306a36Sopenharmony_ci } else { 115062306a36Sopenharmony_ci WARN_ONCE(swa->type != DPAA2_ETH_SWA_XDP, "Wrong SWA type"); 115162306a36Sopenharmony_ci dma_unmap_single(dev, fd_addr, swa->xdp.dma_size, 115262306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 115362306a36Sopenharmony_ci } 115462306a36Sopenharmony_ci } else if (fd_format == dpaa2_fd_sg) { 115562306a36Sopenharmony_ci if (swa->type == DPAA2_ETH_SWA_SG) { 115662306a36Sopenharmony_ci skb = swa->sg.skb; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci /* Unmap the scatterlist */ 115962306a36Sopenharmony_ci dma_unmap_sg(dev, swa->sg.scl, swa->sg.num_sg, 116062306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 116162306a36Sopenharmony_ci kfree(swa->sg.scl); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci /* Unmap the SGT buffer */ 116462306a36Sopenharmony_ci dma_unmap_single(dev, fd_addr, swa->sg.sgt_size, 116562306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 116662306a36Sopenharmony_ci } else if (swa->type == DPAA2_ETH_SWA_SW_TSO) { 116762306a36Sopenharmony_ci skb = swa->tso.skb; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci sgt = (struct dpaa2_sg_entry *)(buffer_start + 117062306a36Sopenharmony_ci priv->tx_data_offset); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci /* Unmap the SGT buffer */ 117362306a36Sopenharmony_ci dma_unmap_single(dev, fd_addr, swa->tso.sgt_size, 117462306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci /* Unmap and free the header */ 117762306a36Sopenharmony_ci tso_hdr = dpaa2_iova_to_virt(priv->iommu_domain, dpaa2_sg_get_addr(sgt)); 117862306a36Sopenharmony_ci dma_unmap_single(dev, dpaa2_sg_get_addr(sgt), TSO_HEADER_SIZE, 117962306a36Sopenharmony_ci DMA_TO_DEVICE); 118062306a36Sopenharmony_ci kfree(tso_hdr); 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci /* Unmap the other SG entries for the data */ 118362306a36Sopenharmony_ci for (i = 1; i < swa->tso.num_sg; i++) 118462306a36Sopenharmony_ci dma_unmap_single(dev, dpaa2_sg_get_addr(&sgt[i]), 118562306a36Sopenharmony_ci dpaa2_sg_get_len(&sgt[i]), DMA_TO_DEVICE); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci if (!swa->tso.is_last_fd) 118862306a36Sopenharmony_ci should_free_skb = 0; 118962306a36Sopenharmony_ci } else if (swa->type == DPAA2_ETH_SWA_XSK) { 119062306a36Sopenharmony_ci /* Unmap the SGT Buffer */ 119162306a36Sopenharmony_ci dma_unmap_single(dev, fd_addr, swa->xsk.sgt_size, 119262306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 119362306a36Sopenharmony_ci } else { 119462306a36Sopenharmony_ci skb = swa->single.skb; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci /* Unmap the SGT Buffer */ 119762306a36Sopenharmony_ci dma_unmap_single(dev, fd_addr, swa->single.sgt_size, 119862306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci sgt = (struct dpaa2_sg_entry *)(buffer_start + 120162306a36Sopenharmony_ci priv->tx_data_offset); 120262306a36Sopenharmony_ci sg_addr = dpaa2_sg_get_addr(sgt); 120362306a36Sopenharmony_ci dma_unmap_single(dev, sg_addr, skb->len, DMA_BIDIRECTIONAL); 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci } else { 120662306a36Sopenharmony_ci netdev_dbg(priv->net_dev, "Invalid FD format\n"); 120762306a36Sopenharmony_ci return; 120862306a36Sopenharmony_ci } 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci if (swa->type == DPAA2_ETH_SWA_XSK) { 121162306a36Sopenharmony_ci ch->xsk_tx_pkts_sent++; 121262306a36Sopenharmony_ci dpaa2_eth_sgt_recycle(priv, buffer_start); 121362306a36Sopenharmony_ci return; 121462306a36Sopenharmony_ci } 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci if (swa->type != DPAA2_ETH_SWA_XDP && in_napi) { 121762306a36Sopenharmony_ci fq->dq_frames++; 121862306a36Sopenharmony_ci fq->dq_bytes += fd_len; 121962306a36Sopenharmony_ci } 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci if (swa->type == DPAA2_ETH_SWA_XDP) { 122262306a36Sopenharmony_ci xdp_return_frame(swa->xdp.xdpf); 122362306a36Sopenharmony_ci return; 122462306a36Sopenharmony_ci } 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci /* Get the timestamp value */ 122762306a36Sopenharmony_ci if (swa->type != DPAA2_ETH_SWA_SW_TSO) { 122862306a36Sopenharmony_ci if (skb->cb[0] == TX_TSTAMP) { 122962306a36Sopenharmony_ci struct skb_shared_hwtstamps shhwtstamps; 123062306a36Sopenharmony_ci __le64 *ts = dpaa2_get_ts(buffer_start, true); 123162306a36Sopenharmony_ci u64 ns; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci memset(&shhwtstamps, 0, sizeof(shhwtstamps)); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci ns = DPAA2_PTP_CLK_PERIOD_NS * le64_to_cpup(ts); 123662306a36Sopenharmony_ci shhwtstamps.hwtstamp = ns_to_ktime(ns); 123762306a36Sopenharmony_ci skb_tstamp_tx(skb, &shhwtstamps); 123862306a36Sopenharmony_ci } else if (skb->cb[0] == TX_TSTAMP_ONESTEP_SYNC) { 123962306a36Sopenharmony_ci mutex_unlock(&priv->onestep_tstamp_lock); 124062306a36Sopenharmony_ci } 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci /* Free SGT buffer allocated on tx */ 124462306a36Sopenharmony_ci if (fd_format != dpaa2_fd_single) 124562306a36Sopenharmony_ci dpaa2_eth_sgt_recycle(priv, buffer_start); 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci /* Move on with skb release. If we are just confirming multiple FDs 124862306a36Sopenharmony_ci * from the same TSO skb then only the last one will need to free the 124962306a36Sopenharmony_ci * skb. 125062306a36Sopenharmony_ci */ 125162306a36Sopenharmony_ci if (should_free_skb) 125262306a36Sopenharmony_ci napi_consume_skb(skb, in_napi); 125362306a36Sopenharmony_ci} 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_cistatic int dpaa2_eth_build_gso_fd(struct dpaa2_eth_priv *priv, 125662306a36Sopenharmony_ci struct sk_buff *skb, struct dpaa2_fd *fd, 125762306a36Sopenharmony_ci int *num_fds, u32 *total_fds_len) 125862306a36Sopenharmony_ci{ 125962306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 126062306a36Sopenharmony_ci int hdr_len, total_len, data_left, fd_len; 126162306a36Sopenharmony_ci int num_sge, err, i, sgt_buf_size; 126262306a36Sopenharmony_ci struct dpaa2_fd *fd_start = fd; 126362306a36Sopenharmony_ci struct dpaa2_sg_entry *sgt; 126462306a36Sopenharmony_ci struct dpaa2_eth_swa *swa; 126562306a36Sopenharmony_ci dma_addr_t sgt_addr, addr; 126662306a36Sopenharmony_ci dma_addr_t tso_hdr_dma; 126762306a36Sopenharmony_ci unsigned int index = 0; 126862306a36Sopenharmony_ci struct tso_t tso; 126962306a36Sopenharmony_ci char *tso_hdr; 127062306a36Sopenharmony_ci void *sgt_buf; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci /* Initialize the TSO handler, and prepare the first payload */ 127362306a36Sopenharmony_ci hdr_len = tso_start(skb, &tso); 127462306a36Sopenharmony_ci *total_fds_len = 0; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci total_len = skb->len - hdr_len; 127762306a36Sopenharmony_ci while (total_len > 0) { 127862306a36Sopenharmony_ci /* Prepare the HW SGT structure for this frame */ 127962306a36Sopenharmony_ci sgt_buf = dpaa2_eth_sgt_get(priv); 128062306a36Sopenharmony_ci if (unlikely(!sgt_buf)) { 128162306a36Sopenharmony_ci netdev_err(priv->net_dev, "dpaa2_eth_sgt_get() failed\n"); 128262306a36Sopenharmony_ci err = -ENOMEM; 128362306a36Sopenharmony_ci goto err_sgt_get; 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci sgt = (struct dpaa2_sg_entry *)(sgt_buf + priv->tx_data_offset); 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci /* Determine the data length of this frame */ 128862306a36Sopenharmony_ci data_left = min_t(int, skb_shinfo(skb)->gso_size, total_len); 128962306a36Sopenharmony_ci total_len -= data_left; 129062306a36Sopenharmony_ci fd_len = data_left + hdr_len; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci /* Prepare packet headers: MAC + IP + TCP */ 129362306a36Sopenharmony_ci tso_hdr = kmalloc(TSO_HEADER_SIZE, GFP_ATOMIC); 129462306a36Sopenharmony_ci if (!tso_hdr) { 129562306a36Sopenharmony_ci err = -ENOMEM; 129662306a36Sopenharmony_ci goto err_alloc_tso_hdr; 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci tso_build_hdr(skb, tso_hdr, &tso, data_left, total_len == 0); 130062306a36Sopenharmony_ci tso_hdr_dma = dma_map_single(dev, tso_hdr, TSO_HEADER_SIZE, DMA_TO_DEVICE); 130162306a36Sopenharmony_ci if (dma_mapping_error(dev, tso_hdr_dma)) { 130262306a36Sopenharmony_ci netdev_err(priv->net_dev, "dma_map_single(tso_hdr) failed\n"); 130362306a36Sopenharmony_ci err = -ENOMEM; 130462306a36Sopenharmony_ci goto err_map_tso_hdr; 130562306a36Sopenharmony_ci } 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci /* Setup the SG entry for the header */ 130862306a36Sopenharmony_ci dpaa2_sg_set_addr(sgt, tso_hdr_dma); 130962306a36Sopenharmony_ci dpaa2_sg_set_len(sgt, hdr_len); 131062306a36Sopenharmony_ci dpaa2_sg_set_final(sgt, data_left <= 0); 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci /* Compose the SG entries for each fragment of data */ 131362306a36Sopenharmony_ci num_sge = 1; 131462306a36Sopenharmony_ci while (data_left > 0) { 131562306a36Sopenharmony_ci int size; 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci /* Move to the next SG entry */ 131862306a36Sopenharmony_ci sgt++; 131962306a36Sopenharmony_ci size = min_t(int, tso.size, data_left); 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci addr = dma_map_single(dev, tso.data, size, DMA_TO_DEVICE); 132262306a36Sopenharmony_ci if (dma_mapping_error(dev, addr)) { 132362306a36Sopenharmony_ci netdev_err(priv->net_dev, "dma_map_single(tso.data) failed\n"); 132462306a36Sopenharmony_ci err = -ENOMEM; 132562306a36Sopenharmony_ci goto err_map_data; 132662306a36Sopenharmony_ci } 132762306a36Sopenharmony_ci dpaa2_sg_set_addr(sgt, addr); 132862306a36Sopenharmony_ci dpaa2_sg_set_len(sgt, size); 132962306a36Sopenharmony_ci dpaa2_sg_set_final(sgt, size == data_left); 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci num_sge++; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci /* Build the data for the __next__ fragment */ 133462306a36Sopenharmony_ci data_left -= size; 133562306a36Sopenharmony_ci tso_build_data(skb, &tso, size); 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci /* Store the skb backpointer in the SGT buffer */ 133962306a36Sopenharmony_ci sgt_buf_size = priv->tx_data_offset + num_sge * sizeof(struct dpaa2_sg_entry); 134062306a36Sopenharmony_ci swa = (struct dpaa2_eth_swa *)sgt_buf; 134162306a36Sopenharmony_ci swa->type = DPAA2_ETH_SWA_SW_TSO; 134262306a36Sopenharmony_ci swa->tso.skb = skb; 134362306a36Sopenharmony_ci swa->tso.num_sg = num_sge; 134462306a36Sopenharmony_ci swa->tso.sgt_size = sgt_buf_size; 134562306a36Sopenharmony_ci swa->tso.is_last_fd = total_len == 0 ? 1 : 0; 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci /* Separately map the SGT buffer */ 134862306a36Sopenharmony_ci sgt_addr = dma_map_single(dev, sgt_buf, sgt_buf_size, DMA_BIDIRECTIONAL); 134962306a36Sopenharmony_ci if (unlikely(dma_mapping_error(dev, sgt_addr))) { 135062306a36Sopenharmony_ci netdev_err(priv->net_dev, "dma_map_single(sgt_buf) failed\n"); 135162306a36Sopenharmony_ci err = -ENOMEM; 135262306a36Sopenharmony_ci goto err_map_sgt; 135362306a36Sopenharmony_ci } 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci /* Setup the frame descriptor */ 135662306a36Sopenharmony_ci memset(fd, 0, sizeof(struct dpaa2_fd)); 135762306a36Sopenharmony_ci dpaa2_fd_set_offset(fd, priv->tx_data_offset); 135862306a36Sopenharmony_ci dpaa2_fd_set_format(fd, dpaa2_fd_sg); 135962306a36Sopenharmony_ci dpaa2_fd_set_addr(fd, sgt_addr); 136062306a36Sopenharmony_ci dpaa2_fd_set_len(fd, fd_len); 136162306a36Sopenharmony_ci dpaa2_fd_set_ctrl(fd, FD_CTRL_PTA); 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci *total_fds_len += fd_len; 136462306a36Sopenharmony_ci /* Advance to the next frame descriptor */ 136562306a36Sopenharmony_ci fd++; 136662306a36Sopenharmony_ci index++; 136762306a36Sopenharmony_ci } 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci *num_fds = index; 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci return 0; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_cierr_map_sgt: 137462306a36Sopenharmony_cierr_map_data: 137562306a36Sopenharmony_ci /* Unmap all the data S/G entries for the current FD */ 137662306a36Sopenharmony_ci sgt = (struct dpaa2_sg_entry *)(sgt_buf + priv->tx_data_offset); 137762306a36Sopenharmony_ci for (i = 1; i < num_sge; i++) 137862306a36Sopenharmony_ci dma_unmap_single(dev, dpaa2_sg_get_addr(&sgt[i]), 137962306a36Sopenharmony_ci dpaa2_sg_get_len(&sgt[i]), DMA_TO_DEVICE); 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci /* Unmap the header entry */ 138262306a36Sopenharmony_ci dma_unmap_single(dev, tso_hdr_dma, TSO_HEADER_SIZE, DMA_TO_DEVICE); 138362306a36Sopenharmony_cierr_map_tso_hdr: 138462306a36Sopenharmony_ci kfree(tso_hdr); 138562306a36Sopenharmony_cierr_alloc_tso_hdr: 138662306a36Sopenharmony_ci dpaa2_eth_sgt_recycle(priv, sgt_buf); 138762306a36Sopenharmony_cierr_sgt_get: 138862306a36Sopenharmony_ci /* Free all the other FDs that were already fully created */ 138962306a36Sopenharmony_ci for (i = 0; i < index; i++) 139062306a36Sopenharmony_ci dpaa2_eth_free_tx_fd(priv, NULL, NULL, &fd_start[i], false); 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci return err; 139362306a36Sopenharmony_ci} 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_cistatic netdev_tx_t __dpaa2_eth_tx(struct sk_buff *skb, 139662306a36Sopenharmony_ci struct net_device *net_dev) 139762306a36Sopenharmony_ci{ 139862306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 139962306a36Sopenharmony_ci int total_enqueued = 0, retries = 0, enqueued; 140062306a36Sopenharmony_ci struct dpaa2_eth_drv_stats *percpu_extras; 140162306a36Sopenharmony_ci struct rtnl_link_stats64 *percpu_stats; 140262306a36Sopenharmony_ci unsigned int needed_headroom; 140362306a36Sopenharmony_ci int num_fds = 1, max_retries; 140462306a36Sopenharmony_ci struct dpaa2_eth_fq *fq; 140562306a36Sopenharmony_ci struct netdev_queue *nq; 140662306a36Sopenharmony_ci struct dpaa2_fd *fd; 140762306a36Sopenharmony_ci u16 queue_mapping; 140862306a36Sopenharmony_ci void *swa = NULL; 140962306a36Sopenharmony_ci u8 prio = 0; 141062306a36Sopenharmony_ci int err, i; 141162306a36Sopenharmony_ci u32 fd_len; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci percpu_stats = this_cpu_ptr(priv->percpu_stats); 141462306a36Sopenharmony_ci percpu_extras = this_cpu_ptr(priv->percpu_extras); 141562306a36Sopenharmony_ci fd = (this_cpu_ptr(priv->fd))->array; 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci needed_headroom = dpaa2_eth_needed_headroom(skb); 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci /* We'll be holding a back-reference to the skb until Tx Confirmation; 142062306a36Sopenharmony_ci * we don't want that overwritten by a concurrent Tx with a cloned skb. 142162306a36Sopenharmony_ci */ 142262306a36Sopenharmony_ci skb = skb_unshare(skb, GFP_ATOMIC); 142362306a36Sopenharmony_ci if (unlikely(!skb)) { 142462306a36Sopenharmony_ci /* skb_unshare() has already freed the skb */ 142562306a36Sopenharmony_ci percpu_stats->tx_dropped++; 142662306a36Sopenharmony_ci return NETDEV_TX_OK; 142762306a36Sopenharmony_ci } 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci /* Setup the FD fields */ 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci if (skb_is_gso(skb)) { 143262306a36Sopenharmony_ci err = dpaa2_eth_build_gso_fd(priv, skb, fd, &num_fds, &fd_len); 143362306a36Sopenharmony_ci percpu_extras->tx_sg_frames += num_fds; 143462306a36Sopenharmony_ci percpu_extras->tx_sg_bytes += fd_len; 143562306a36Sopenharmony_ci percpu_extras->tx_tso_frames += num_fds; 143662306a36Sopenharmony_ci percpu_extras->tx_tso_bytes += fd_len; 143762306a36Sopenharmony_ci } else if (skb_is_nonlinear(skb)) { 143862306a36Sopenharmony_ci err = dpaa2_eth_build_sg_fd(priv, skb, fd, &swa); 143962306a36Sopenharmony_ci percpu_extras->tx_sg_frames++; 144062306a36Sopenharmony_ci percpu_extras->tx_sg_bytes += skb->len; 144162306a36Sopenharmony_ci fd_len = dpaa2_fd_get_len(fd); 144262306a36Sopenharmony_ci } else if (skb_headroom(skb) < needed_headroom) { 144362306a36Sopenharmony_ci err = dpaa2_eth_build_sg_fd_single_buf(priv, skb, fd, &swa); 144462306a36Sopenharmony_ci percpu_extras->tx_sg_frames++; 144562306a36Sopenharmony_ci percpu_extras->tx_sg_bytes += skb->len; 144662306a36Sopenharmony_ci percpu_extras->tx_converted_sg_frames++; 144762306a36Sopenharmony_ci percpu_extras->tx_converted_sg_bytes += skb->len; 144862306a36Sopenharmony_ci fd_len = dpaa2_fd_get_len(fd); 144962306a36Sopenharmony_ci } else { 145062306a36Sopenharmony_ci err = dpaa2_eth_build_single_fd(priv, skb, fd, &swa); 145162306a36Sopenharmony_ci fd_len = dpaa2_fd_get_len(fd); 145262306a36Sopenharmony_ci } 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci if (unlikely(err)) { 145562306a36Sopenharmony_ci percpu_stats->tx_dropped++; 145662306a36Sopenharmony_ci goto err_build_fd; 145762306a36Sopenharmony_ci } 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci if (swa && skb->cb[0]) 146062306a36Sopenharmony_ci dpaa2_eth_enable_tx_tstamp(priv, fd, swa, skb); 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci /* Tracing point */ 146362306a36Sopenharmony_ci for (i = 0; i < num_fds; i++) 146462306a36Sopenharmony_ci trace_dpaa2_tx_fd(net_dev, &fd[i]); 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci /* TxConf FQ selection relies on queue id from the stack. 146762306a36Sopenharmony_ci * In case of a forwarded frame from another DPNI interface, we choose 146862306a36Sopenharmony_ci * a queue affined to the same core that processed the Rx frame 146962306a36Sopenharmony_ci */ 147062306a36Sopenharmony_ci queue_mapping = skb_get_queue_mapping(skb); 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci if (net_dev->num_tc) { 147362306a36Sopenharmony_ci prio = netdev_txq_to_tc(net_dev, queue_mapping); 147462306a36Sopenharmony_ci /* Hardware interprets priority level 0 as being the highest, 147562306a36Sopenharmony_ci * so we need to do a reverse mapping to the netdev tc index 147662306a36Sopenharmony_ci */ 147762306a36Sopenharmony_ci prio = net_dev->num_tc - prio - 1; 147862306a36Sopenharmony_ci /* We have only one FQ array entry for all Tx hardware queues 147962306a36Sopenharmony_ci * with the same flow id (but different priority levels) 148062306a36Sopenharmony_ci */ 148162306a36Sopenharmony_ci queue_mapping %= dpaa2_eth_queue_count(priv); 148262306a36Sopenharmony_ci } 148362306a36Sopenharmony_ci fq = &priv->fq[queue_mapping]; 148462306a36Sopenharmony_ci nq = netdev_get_tx_queue(net_dev, queue_mapping); 148562306a36Sopenharmony_ci netdev_tx_sent_queue(nq, fd_len); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci /* Everything that happens after this enqueues might race with 148862306a36Sopenharmony_ci * the Tx confirmation callback for this frame 148962306a36Sopenharmony_ci */ 149062306a36Sopenharmony_ci max_retries = num_fds * DPAA2_ETH_ENQUEUE_RETRIES; 149162306a36Sopenharmony_ci while (total_enqueued < num_fds && retries < max_retries) { 149262306a36Sopenharmony_ci err = priv->enqueue(priv, fq, &fd[total_enqueued], 149362306a36Sopenharmony_ci prio, num_fds - total_enqueued, &enqueued); 149462306a36Sopenharmony_ci if (err == -EBUSY) { 149562306a36Sopenharmony_ci retries++; 149662306a36Sopenharmony_ci continue; 149762306a36Sopenharmony_ci } 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci total_enqueued += enqueued; 150062306a36Sopenharmony_ci } 150162306a36Sopenharmony_ci percpu_extras->tx_portal_busy += retries; 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci if (unlikely(err < 0)) { 150462306a36Sopenharmony_ci percpu_stats->tx_errors++; 150562306a36Sopenharmony_ci /* Clean up everything, including freeing the skb */ 150662306a36Sopenharmony_ci dpaa2_eth_free_tx_fd(priv, NULL, fq, fd, false); 150762306a36Sopenharmony_ci netdev_tx_completed_queue(nq, 1, fd_len); 150862306a36Sopenharmony_ci } else { 150962306a36Sopenharmony_ci percpu_stats->tx_packets += total_enqueued; 151062306a36Sopenharmony_ci percpu_stats->tx_bytes += fd_len; 151162306a36Sopenharmony_ci } 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci return NETDEV_TX_OK; 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_cierr_build_fd: 151662306a36Sopenharmony_ci dev_kfree_skb(skb); 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci return NETDEV_TX_OK; 151962306a36Sopenharmony_ci} 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_cistatic void dpaa2_eth_tx_onestep_tstamp(struct work_struct *work) 152262306a36Sopenharmony_ci{ 152362306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = container_of(work, struct dpaa2_eth_priv, 152462306a36Sopenharmony_ci tx_onestep_tstamp); 152562306a36Sopenharmony_ci struct sk_buff *skb; 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci while (true) { 152862306a36Sopenharmony_ci skb = skb_dequeue(&priv->tx_skbs); 152962306a36Sopenharmony_ci if (!skb) 153062306a36Sopenharmony_ci return; 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci /* Lock just before TX one-step timestamping packet, 153362306a36Sopenharmony_ci * and release the lock in dpaa2_eth_free_tx_fd when 153462306a36Sopenharmony_ci * confirm the packet has been sent on hardware, or 153562306a36Sopenharmony_ci * when clean up during transmit failure. 153662306a36Sopenharmony_ci */ 153762306a36Sopenharmony_ci mutex_lock(&priv->onestep_tstamp_lock); 153862306a36Sopenharmony_ci __dpaa2_eth_tx(skb, priv->net_dev); 153962306a36Sopenharmony_ci } 154062306a36Sopenharmony_ci} 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_cistatic netdev_tx_t dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev) 154362306a36Sopenharmony_ci{ 154462306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 154562306a36Sopenharmony_ci u8 msgtype, twostep, udp; 154662306a36Sopenharmony_ci u16 offset1, offset2; 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci /* Utilize skb->cb[0] for timestamping request per skb */ 154962306a36Sopenharmony_ci skb->cb[0] = 0; 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && dpaa2_ptp) { 155262306a36Sopenharmony_ci if (priv->tx_tstamp_type == HWTSTAMP_TX_ON) 155362306a36Sopenharmony_ci skb->cb[0] = TX_TSTAMP; 155462306a36Sopenharmony_ci else if (priv->tx_tstamp_type == HWTSTAMP_TX_ONESTEP_SYNC) 155562306a36Sopenharmony_ci skb->cb[0] = TX_TSTAMP_ONESTEP_SYNC; 155662306a36Sopenharmony_ci } 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci /* TX for one-step timestamping PTP Sync packet */ 155962306a36Sopenharmony_ci if (skb->cb[0] == TX_TSTAMP_ONESTEP_SYNC) { 156062306a36Sopenharmony_ci if (!dpaa2_eth_ptp_parse(skb, &msgtype, &twostep, &udp, 156162306a36Sopenharmony_ci &offset1, &offset2)) 156262306a36Sopenharmony_ci if (msgtype == PTP_MSGTYPE_SYNC && twostep == 0) { 156362306a36Sopenharmony_ci skb_queue_tail(&priv->tx_skbs, skb); 156462306a36Sopenharmony_ci queue_work(priv->dpaa2_ptp_wq, 156562306a36Sopenharmony_ci &priv->tx_onestep_tstamp); 156662306a36Sopenharmony_ci return NETDEV_TX_OK; 156762306a36Sopenharmony_ci } 156862306a36Sopenharmony_ci /* Use two-step timestamping if not one-step timestamping 156962306a36Sopenharmony_ci * PTP Sync packet 157062306a36Sopenharmony_ci */ 157162306a36Sopenharmony_ci skb->cb[0] = TX_TSTAMP; 157262306a36Sopenharmony_ci } 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci /* TX for other packets */ 157562306a36Sopenharmony_ci return __dpaa2_eth_tx(skb, net_dev); 157662306a36Sopenharmony_ci} 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci/* Tx confirmation frame processing routine */ 157962306a36Sopenharmony_cistatic void dpaa2_eth_tx_conf(struct dpaa2_eth_priv *priv, 158062306a36Sopenharmony_ci struct dpaa2_eth_channel *ch, 158162306a36Sopenharmony_ci const struct dpaa2_fd *fd, 158262306a36Sopenharmony_ci struct dpaa2_eth_fq *fq) 158362306a36Sopenharmony_ci{ 158462306a36Sopenharmony_ci struct rtnl_link_stats64 *percpu_stats; 158562306a36Sopenharmony_ci struct dpaa2_eth_drv_stats *percpu_extras; 158662306a36Sopenharmony_ci u32 fd_len = dpaa2_fd_get_len(fd); 158762306a36Sopenharmony_ci u32 fd_errors; 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci /* Tracing point */ 159062306a36Sopenharmony_ci trace_dpaa2_tx_conf_fd(priv->net_dev, fd); 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci percpu_extras = this_cpu_ptr(priv->percpu_extras); 159362306a36Sopenharmony_ci percpu_extras->tx_conf_frames++; 159462306a36Sopenharmony_ci percpu_extras->tx_conf_bytes += fd_len; 159562306a36Sopenharmony_ci ch->stats.bytes_per_cdan += fd_len; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci /* Check frame errors in the FD field */ 159862306a36Sopenharmony_ci fd_errors = dpaa2_fd_get_ctrl(fd) & DPAA2_FD_TX_ERR_MASK; 159962306a36Sopenharmony_ci dpaa2_eth_free_tx_fd(priv, ch, fq, fd, true); 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci if (likely(!fd_errors)) 160262306a36Sopenharmony_ci return; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci if (net_ratelimit()) 160562306a36Sopenharmony_ci netdev_dbg(priv->net_dev, "TX frame FD error: 0x%08x\n", 160662306a36Sopenharmony_ci fd_errors); 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci percpu_stats = this_cpu_ptr(priv->percpu_stats); 160962306a36Sopenharmony_ci /* Tx-conf logically pertains to the egress path. */ 161062306a36Sopenharmony_ci percpu_stats->tx_errors++; 161162306a36Sopenharmony_ci} 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_cistatic int dpaa2_eth_set_rx_vlan_filtering(struct dpaa2_eth_priv *priv, 161462306a36Sopenharmony_ci bool enable) 161562306a36Sopenharmony_ci{ 161662306a36Sopenharmony_ci int err; 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci err = dpni_enable_vlan_filter(priv->mc_io, 0, priv->mc_token, enable); 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci if (err) { 162162306a36Sopenharmony_ci netdev_err(priv->net_dev, 162262306a36Sopenharmony_ci "dpni_enable_vlan_filter failed\n"); 162362306a36Sopenharmony_ci return err; 162462306a36Sopenharmony_ci } 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci return 0; 162762306a36Sopenharmony_ci} 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_cistatic int dpaa2_eth_set_rx_csum(struct dpaa2_eth_priv *priv, bool enable) 163062306a36Sopenharmony_ci{ 163162306a36Sopenharmony_ci int err; 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci err = dpni_set_offload(priv->mc_io, 0, priv->mc_token, 163462306a36Sopenharmony_ci DPNI_OFF_RX_L3_CSUM, enable); 163562306a36Sopenharmony_ci if (err) { 163662306a36Sopenharmony_ci netdev_err(priv->net_dev, 163762306a36Sopenharmony_ci "dpni_set_offload(RX_L3_CSUM) failed\n"); 163862306a36Sopenharmony_ci return err; 163962306a36Sopenharmony_ci } 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci err = dpni_set_offload(priv->mc_io, 0, priv->mc_token, 164262306a36Sopenharmony_ci DPNI_OFF_RX_L4_CSUM, enable); 164362306a36Sopenharmony_ci if (err) { 164462306a36Sopenharmony_ci netdev_err(priv->net_dev, 164562306a36Sopenharmony_ci "dpni_set_offload(RX_L4_CSUM) failed\n"); 164662306a36Sopenharmony_ci return err; 164762306a36Sopenharmony_ci } 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci return 0; 165062306a36Sopenharmony_ci} 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_cistatic int dpaa2_eth_set_tx_csum(struct dpaa2_eth_priv *priv, bool enable) 165362306a36Sopenharmony_ci{ 165462306a36Sopenharmony_ci int err; 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci err = dpni_set_offload(priv->mc_io, 0, priv->mc_token, 165762306a36Sopenharmony_ci DPNI_OFF_TX_L3_CSUM, enable); 165862306a36Sopenharmony_ci if (err) { 165962306a36Sopenharmony_ci netdev_err(priv->net_dev, "dpni_set_offload(TX_L3_CSUM) failed\n"); 166062306a36Sopenharmony_ci return err; 166162306a36Sopenharmony_ci } 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci err = dpni_set_offload(priv->mc_io, 0, priv->mc_token, 166462306a36Sopenharmony_ci DPNI_OFF_TX_L4_CSUM, enable); 166562306a36Sopenharmony_ci if (err) { 166662306a36Sopenharmony_ci netdev_err(priv->net_dev, "dpni_set_offload(TX_L4_CSUM) failed\n"); 166762306a36Sopenharmony_ci return err; 166862306a36Sopenharmony_ci } 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci return 0; 167162306a36Sopenharmony_ci} 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci/* Perform a single release command to add buffers 167462306a36Sopenharmony_ci * to the specified buffer pool 167562306a36Sopenharmony_ci */ 167662306a36Sopenharmony_cistatic int dpaa2_eth_add_bufs(struct dpaa2_eth_priv *priv, 167762306a36Sopenharmony_ci struct dpaa2_eth_channel *ch) 167862306a36Sopenharmony_ci{ 167962306a36Sopenharmony_ci struct xdp_buff *xdp_buffs[DPAA2_ETH_BUFS_PER_CMD]; 168062306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 168162306a36Sopenharmony_ci u64 buf_array[DPAA2_ETH_BUFS_PER_CMD]; 168262306a36Sopenharmony_ci struct dpaa2_eth_swa *swa; 168362306a36Sopenharmony_ci struct page *page; 168462306a36Sopenharmony_ci dma_addr_t addr; 168562306a36Sopenharmony_ci int retries = 0; 168662306a36Sopenharmony_ci int i = 0, err; 168762306a36Sopenharmony_ci u32 batch; 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci /* Allocate buffers visible to WRIOP */ 169062306a36Sopenharmony_ci if (!ch->xsk_zc) { 169162306a36Sopenharmony_ci for (i = 0; i < DPAA2_ETH_BUFS_PER_CMD; i++) { 169262306a36Sopenharmony_ci /* Also allocate skb shared info and alignment padding. 169362306a36Sopenharmony_ci * There is one page for each Rx buffer. WRIOP sees 169462306a36Sopenharmony_ci * the entire page except for a tailroom reserved for 169562306a36Sopenharmony_ci * skb shared info 169662306a36Sopenharmony_ci */ 169762306a36Sopenharmony_ci page = dev_alloc_pages(0); 169862306a36Sopenharmony_ci if (!page) 169962306a36Sopenharmony_ci goto err_alloc; 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci addr = dma_map_page(dev, page, 0, priv->rx_buf_size, 170262306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 170362306a36Sopenharmony_ci if (unlikely(dma_mapping_error(dev, addr))) 170462306a36Sopenharmony_ci goto err_map; 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci buf_array[i] = addr; 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci /* tracing point */ 170962306a36Sopenharmony_ci trace_dpaa2_eth_buf_seed(priv->net_dev, 171062306a36Sopenharmony_ci page_address(page), 171162306a36Sopenharmony_ci DPAA2_ETH_RX_BUF_RAW_SIZE, 171262306a36Sopenharmony_ci addr, priv->rx_buf_size, 171362306a36Sopenharmony_ci ch->bp->bpid); 171462306a36Sopenharmony_ci } 171562306a36Sopenharmony_ci } else if (xsk_buff_can_alloc(ch->xsk_pool, DPAA2_ETH_BUFS_PER_CMD)) { 171662306a36Sopenharmony_ci /* Allocate XSK buffers for AF_XDP fast path in batches 171762306a36Sopenharmony_ci * of DPAA2_ETH_BUFS_PER_CMD. Bail out if the UMEM cannot 171862306a36Sopenharmony_ci * provide enough buffers at the moment 171962306a36Sopenharmony_ci */ 172062306a36Sopenharmony_ci batch = xsk_buff_alloc_batch(ch->xsk_pool, xdp_buffs, 172162306a36Sopenharmony_ci DPAA2_ETH_BUFS_PER_CMD); 172262306a36Sopenharmony_ci if (!batch) 172362306a36Sopenharmony_ci goto err_alloc; 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci for (i = 0; i < batch; i++) { 172662306a36Sopenharmony_ci swa = (struct dpaa2_eth_swa *)(xdp_buffs[i]->data_hard_start + 172762306a36Sopenharmony_ci DPAA2_ETH_RX_HWA_SIZE); 172862306a36Sopenharmony_ci swa->xsk.xdp_buff = xdp_buffs[i]; 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci addr = xsk_buff_xdp_get_frame_dma(xdp_buffs[i]); 173162306a36Sopenharmony_ci if (unlikely(dma_mapping_error(dev, addr))) 173262306a36Sopenharmony_ci goto err_map; 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci buf_array[i] = addr; 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci trace_dpaa2_xsk_buf_seed(priv->net_dev, 173762306a36Sopenharmony_ci xdp_buffs[i]->data_hard_start, 173862306a36Sopenharmony_ci DPAA2_ETH_RX_BUF_RAW_SIZE, 173962306a36Sopenharmony_ci addr, priv->rx_buf_size, 174062306a36Sopenharmony_ci ch->bp->bpid); 174162306a36Sopenharmony_ci } 174262306a36Sopenharmony_ci } 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_cirelease_bufs: 174562306a36Sopenharmony_ci /* In case the portal is busy, retry until successful */ 174662306a36Sopenharmony_ci while ((err = dpaa2_io_service_release(ch->dpio, ch->bp->bpid, 174762306a36Sopenharmony_ci buf_array, i)) == -EBUSY) { 174862306a36Sopenharmony_ci if (retries++ >= DPAA2_ETH_SWP_BUSY_RETRIES) 174962306a36Sopenharmony_ci break; 175062306a36Sopenharmony_ci cpu_relax(); 175162306a36Sopenharmony_ci } 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci /* If release command failed, clean up and bail out; 175462306a36Sopenharmony_ci * not much else we can do about it 175562306a36Sopenharmony_ci */ 175662306a36Sopenharmony_ci if (err) { 175762306a36Sopenharmony_ci dpaa2_eth_free_bufs(priv, buf_array, i, ch->xsk_zc); 175862306a36Sopenharmony_ci return 0; 175962306a36Sopenharmony_ci } 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci return i; 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_cierr_map: 176462306a36Sopenharmony_ci if (!ch->xsk_zc) { 176562306a36Sopenharmony_ci __free_pages(page, 0); 176662306a36Sopenharmony_ci } else { 176762306a36Sopenharmony_ci for (; i < batch; i++) 176862306a36Sopenharmony_ci xsk_buff_free(xdp_buffs[i]); 176962306a36Sopenharmony_ci } 177062306a36Sopenharmony_cierr_alloc: 177162306a36Sopenharmony_ci /* If we managed to allocate at least some buffers, 177262306a36Sopenharmony_ci * release them to hardware 177362306a36Sopenharmony_ci */ 177462306a36Sopenharmony_ci if (i) 177562306a36Sopenharmony_ci goto release_bufs; 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci return 0; 177862306a36Sopenharmony_ci} 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_cistatic int dpaa2_eth_seed_pool(struct dpaa2_eth_priv *priv, 178162306a36Sopenharmony_ci struct dpaa2_eth_channel *ch) 178262306a36Sopenharmony_ci{ 178362306a36Sopenharmony_ci int i; 178462306a36Sopenharmony_ci int new_count; 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci for (i = 0; i < DPAA2_ETH_NUM_BUFS; i += DPAA2_ETH_BUFS_PER_CMD) { 178762306a36Sopenharmony_ci new_count = dpaa2_eth_add_bufs(priv, ch); 178862306a36Sopenharmony_ci ch->buf_count += new_count; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci if (new_count < DPAA2_ETH_BUFS_PER_CMD) 179162306a36Sopenharmony_ci return -ENOMEM; 179262306a36Sopenharmony_ci } 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci return 0; 179562306a36Sopenharmony_ci} 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_cistatic void dpaa2_eth_seed_pools(struct dpaa2_eth_priv *priv) 179862306a36Sopenharmony_ci{ 179962306a36Sopenharmony_ci struct net_device *net_dev = priv->net_dev; 180062306a36Sopenharmony_ci struct dpaa2_eth_channel *channel; 180162306a36Sopenharmony_ci int i, err = 0; 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci for (i = 0; i < priv->num_channels; i++) { 180462306a36Sopenharmony_ci channel = priv->channel[i]; 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci err = dpaa2_eth_seed_pool(priv, channel); 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci /* Not much to do; the buffer pool, though not filled up, 180962306a36Sopenharmony_ci * may still contain some buffers which would enable us 181062306a36Sopenharmony_ci * to limp on. 181162306a36Sopenharmony_ci */ 181262306a36Sopenharmony_ci if (err) 181362306a36Sopenharmony_ci netdev_err(net_dev, "Buffer seeding failed for DPBP %d (bpid=%d)\n", 181462306a36Sopenharmony_ci channel->bp->dev->obj_desc.id, 181562306a36Sopenharmony_ci channel->bp->bpid); 181662306a36Sopenharmony_ci } 181762306a36Sopenharmony_ci} 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci/* 182062306a36Sopenharmony_ci * Drain the specified number of buffers from one of the DPNI's private buffer 182162306a36Sopenharmony_ci * pools. 182262306a36Sopenharmony_ci * @count must not exceeed DPAA2_ETH_BUFS_PER_CMD 182362306a36Sopenharmony_ci */ 182462306a36Sopenharmony_cistatic void dpaa2_eth_drain_bufs(struct dpaa2_eth_priv *priv, int bpid, 182562306a36Sopenharmony_ci int count) 182662306a36Sopenharmony_ci{ 182762306a36Sopenharmony_ci u64 buf_array[DPAA2_ETH_BUFS_PER_CMD]; 182862306a36Sopenharmony_ci bool xsk_zc = false; 182962306a36Sopenharmony_ci int retries = 0; 183062306a36Sopenharmony_ci int i, ret; 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci for (i = 0; i < priv->num_channels; i++) 183362306a36Sopenharmony_ci if (priv->channel[i]->bp->bpid == bpid) 183462306a36Sopenharmony_ci xsk_zc = priv->channel[i]->xsk_zc; 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci do { 183762306a36Sopenharmony_ci ret = dpaa2_io_service_acquire(NULL, bpid, buf_array, count); 183862306a36Sopenharmony_ci if (ret < 0) { 183962306a36Sopenharmony_ci if (ret == -EBUSY && 184062306a36Sopenharmony_ci retries++ < DPAA2_ETH_SWP_BUSY_RETRIES) 184162306a36Sopenharmony_ci continue; 184262306a36Sopenharmony_ci netdev_err(priv->net_dev, "dpaa2_io_service_acquire() failed\n"); 184362306a36Sopenharmony_ci return; 184462306a36Sopenharmony_ci } 184562306a36Sopenharmony_ci dpaa2_eth_free_bufs(priv, buf_array, ret, xsk_zc); 184662306a36Sopenharmony_ci retries = 0; 184762306a36Sopenharmony_ci } while (ret); 184862306a36Sopenharmony_ci} 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_cistatic void dpaa2_eth_drain_pool(struct dpaa2_eth_priv *priv, int bpid) 185162306a36Sopenharmony_ci{ 185262306a36Sopenharmony_ci int i; 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci /* Drain the buffer pool */ 185562306a36Sopenharmony_ci dpaa2_eth_drain_bufs(priv, bpid, DPAA2_ETH_BUFS_PER_CMD); 185662306a36Sopenharmony_ci dpaa2_eth_drain_bufs(priv, bpid, 1); 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci /* Setup to zero the buffer count of all channels which were 185962306a36Sopenharmony_ci * using this buffer pool. 186062306a36Sopenharmony_ci */ 186162306a36Sopenharmony_ci for (i = 0; i < priv->num_channels; i++) 186262306a36Sopenharmony_ci if (priv->channel[i]->bp->bpid == bpid) 186362306a36Sopenharmony_ci priv->channel[i]->buf_count = 0; 186462306a36Sopenharmony_ci} 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_cistatic void dpaa2_eth_drain_pools(struct dpaa2_eth_priv *priv) 186762306a36Sopenharmony_ci{ 186862306a36Sopenharmony_ci int i; 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci for (i = 0; i < priv->num_bps; i++) 187162306a36Sopenharmony_ci dpaa2_eth_drain_pool(priv, priv->bp[i]->bpid); 187262306a36Sopenharmony_ci} 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci/* Function is called from softirq context only, so we don't need to guard 187562306a36Sopenharmony_ci * the access to percpu count 187662306a36Sopenharmony_ci */ 187762306a36Sopenharmony_cistatic int dpaa2_eth_refill_pool(struct dpaa2_eth_priv *priv, 187862306a36Sopenharmony_ci struct dpaa2_eth_channel *ch) 187962306a36Sopenharmony_ci{ 188062306a36Sopenharmony_ci int new_count; 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci if (likely(ch->buf_count >= DPAA2_ETH_REFILL_THRESH)) 188362306a36Sopenharmony_ci return 0; 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci do { 188662306a36Sopenharmony_ci new_count = dpaa2_eth_add_bufs(priv, ch); 188762306a36Sopenharmony_ci if (unlikely(!new_count)) { 188862306a36Sopenharmony_ci /* Out of memory; abort for now, we'll try later on */ 188962306a36Sopenharmony_ci break; 189062306a36Sopenharmony_ci } 189162306a36Sopenharmony_ci ch->buf_count += new_count; 189262306a36Sopenharmony_ci } while (ch->buf_count < DPAA2_ETH_NUM_BUFS); 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci if (unlikely(ch->buf_count < DPAA2_ETH_NUM_BUFS)) 189562306a36Sopenharmony_ci return -ENOMEM; 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci return 0; 189862306a36Sopenharmony_ci} 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_cistatic void dpaa2_eth_sgt_cache_drain(struct dpaa2_eth_priv *priv) 190162306a36Sopenharmony_ci{ 190262306a36Sopenharmony_ci struct dpaa2_eth_sgt_cache *sgt_cache; 190362306a36Sopenharmony_ci u16 count; 190462306a36Sopenharmony_ci int k, i; 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci for_each_possible_cpu(k) { 190762306a36Sopenharmony_ci sgt_cache = per_cpu_ptr(priv->sgt_cache, k); 190862306a36Sopenharmony_ci count = sgt_cache->count; 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci for (i = 0; i < count; i++) 191162306a36Sopenharmony_ci skb_free_frag(sgt_cache->buf[i]); 191262306a36Sopenharmony_ci sgt_cache->count = 0; 191362306a36Sopenharmony_ci } 191462306a36Sopenharmony_ci} 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_cistatic int dpaa2_eth_pull_channel(struct dpaa2_eth_channel *ch) 191762306a36Sopenharmony_ci{ 191862306a36Sopenharmony_ci int err; 191962306a36Sopenharmony_ci int dequeues = -1; 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci /* Retry while portal is busy */ 192262306a36Sopenharmony_ci do { 192362306a36Sopenharmony_ci err = dpaa2_io_service_pull_channel(ch->dpio, ch->ch_id, 192462306a36Sopenharmony_ci ch->store); 192562306a36Sopenharmony_ci dequeues++; 192662306a36Sopenharmony_ci cpu_relax(); 192762306a36Sopenharmony_ci } while (err == -EBUSY && dequeues < DPAA2_ETH_SWP_BUSY_RETRIES); 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci ch->stats.dequeue_portal_busy += dequeues; 193062306a36Sopenharmony_ci if (unlikely(err)) 193162306a36Sopenharmony_ci ch->stats.pull_err++; 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_ci return err; 193462306a36Sopenharmony_ci} 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci/* NAPI poll routine 193762306a36Sopenharmony_ci * 193862306a36Sopenharmony_ci * Frames are dequeued from the QMan channel associated with this NAPI context. 193962306a36Sopenharmony_ci * Rx, Tx confirmation and (if configured) Rx error frames all count 194062306a36Sopenharmony_ci * towards the NAPI budget. 194162306a36Sopenharmony_ci */ 194262306a36Sopenharmony_cistatic int dpaa2_eth_poll(struct napi_struct *napi, int budget) 194362306a36Sopenharmony_ci{ 194462306a36Sopenharmony_ci struct dpaa2_eth_channel *ch; 194562306a36Sopenharmony_ci struct dpaa2_eth_priv *priv; 194662306a36Sopenharmony_ci int rx_cleaned = 0, txconf_cleaned = 0; 194762306a36Sopenharmony_ci struct dpaa2_eth_fq *fq, *txc_fq = NULL; 194862306a36Sopenharmony_ci struct netdev_queue *nq; 194962306a36Sopenharmony_ci int store_cleaned, work_done; 195062306a36Sopenharmony_ci bool work_done_zc = false; 195162306a36Sopenharmony_ci struct list_head rx_list; 195262306a36Sopenharmony_ci int retries = 0; 195362306a36Sopenharmony_ci u16 flowid; 195462306a36Sopenharmony_ci int err; 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci ch = container_of(napi, struct dpaa2_eth_channel, napi); 195762306a36Sopenharmony_ci ch->xdp.res = 0; 195862306a36Sopenharmony_ci priv = ch->priv; 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_ci INIT_LIST_HEAD(&rx_list); 196162306a36Sopenharmony_ci ch->rx_list = &rx_list; 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci if (ch->xsk_zc) { 196462306a36Sopenharmony_ci work_done_zc = dpaa2_xsk_tx(priv, ch); 196562306a36Sopenharmony_ci /* If we reached the XSK Tx per NAPI threshold, we're done */ 196662306a36Sopenharmony_ci if (work_done_zc) { 196762306a36Sopenharmony_ci work_done = budget; 196862306a36Sopenharmony_ci goto out; 196962306a36Sopenharmony_ci } 197062306a36Sopenharmony_ci } 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci do { 197362306a36Sopenharmony_ci err = dpaa2_eth_pull_channel(ch); 197462306a36Sopenharmony_ci if (unlikely(err)) 197562306a36Sopenharmony_ci break; 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci /* Refill pool if appropriate */ 197862306a36Sopenharmony_ci dpaa2_eth_refill_pool(priv, ch); 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci store_cleaned = dpaa2_eth_consume_frames(ch, &fq); 198162306a36Sopenharmony_ci if (store_cleaned <= 0) 198262306a36Sopenharmony_ci break; 198362306a36Sopenharmony_ci if (fq->type == DPAA2_RX_FQ) { 198462306a36Sopenharmony_ci rx_cleaned += store_cleaned; 198562306a36Sopenharmony_ci flowid = fq->flowid; 198662306a36Sopenharmony_ci } else { 198762306a36Sopenharmony_ci txconf_cleaned += store_cleaned; 198862306a36Sopenharmony_ci /* We have a single Tx conf FQ on this channel */ 198962306a36Sopenharmony_ci txc_fq = fq; 199062306a36Sopenharmony_ci } 199162306a36Sopenharmony_ci 199262306a36Sopenharmony_ci /* If we either consumed the whole NAPI budget with Rx frames 199362306a36Sopenharmony_ci * or we reached the Tx confirmations threshold, we're done. 199462306a36Sopenharmony_ci */ 199562306a36Sopenharmony_ci if (rx_cleaned >= budget || 199662306a36Sopenharmony_ci txconf_cleaned >= DPAA2_ETH_TXCONF_PER_NAPI) { 199762306a36Sopenharmony_ci work_done = budget; 199862306a36Sopenharmony_ci if (ch->xdp.res & XDP_REDIRECT) 199962306a36Sopenharmony_ci xdp_do_flush(); 200062306a36Sopenharmony_ci goto out; 200162306a36Sopenharmony_ci } 200262306a36Sopenharmony_ci } while (store_cleaned); 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci if (ch->xdp.res & XDP_REDIRECT) 200562306a36Sopenharmony_ci xdp_do_flush(); 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci /* Update NET DIM with the values for this CDAN */ 200862306a36Sopenharmony_ci dpaa2_io_update_net_dim(ch->dpio, ch->stats.frames_per_cdan, 200962306a36Sopenharmony_ci ch->stats.bytes_per_cdan); 201062306a36Sopenharmony_ci ch->stats.frames_per_cdan = 0; 201162306a36Sopenharmony_ci ch->stats.bytes_per_cdan = 0; 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci /* We didn't consume the entire budget, so finish napi and 201462306a36Sopenharmony_ci * re-enable data availability notifications 201562306a36Sopenharmony_ci */ 201662306a36Sopenharmony_ci napi_complete_done(napi, rx_cleaned); 201762306a36Sopenharmony_ci do { 201862306a36Sopenharmony_ci err = dpaa2_io_service_rearm(ch->dpio, &ch->nctx); 201962306a36Sopenharmony_ci cpu_relax(); 202062306a36Sopenharmony_ci } while (err == -EBUSY && retries++ < DPAA2_ETH_SWP_BUSY_RETRIES); 202162306a36Sopenharmony_ci WARN_ONCE(err, "CDAN notifications rearm failed on core %d", 202262306a36Sopenharmony_ci ch->nctx.desired_cpu); 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci work_done = max(rx_cleaned, 1); 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ciout: 202762306a36Sopenharmony_ci netif_receive_skb_list(ch->rx_list); 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_ci if (ch->xsk_tx_pkts_sent) { 203062306a36Sopenharmony_ci xsk_tx_completed(ch->xsk_pool, ch->xsk_tx_pkts_sent); 203162306a36Sopenharmony_ci ch->xsk_tx_pkts_sent = 0; 203262306a36Sopenharmony_ci } 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_ci if (txc_fq && txc_fq->dq_frames) { 203562306a36Sopenharmony_ci nq = netdev_get_tx_queue(priv->net_dev, txc_fq->flowid); 203662306a36Sopenharmony_ci netdev_tx_completed_queue(nq, txc_fq->dq_frames, 203762306a36Sopenharmony_ci txc_fq->dq_bytes); 203862306a36Sopenharmony_ci txc_fq->dq_frames = 0; 203962306a36Sopenharmony_ci txc_fq->dq_bytes = 0; 204062306a36Sopenharmony_ci } 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci if (rx_cleaned && ch->xdp.res & XDP_TX) 204362306a36Sopenharmony_ci dpaa2_eth_xdp_tx_flush(priv, ch, &priv->fq[flowid]); 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci return work_done; 204662306a36Sopenharmony_ci} 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_cistatic void dpaa2_eth_enable_ch_napi(struct dpaa2_eth_priv *priv) 204962306a36Sopenharmony_ci{ 205062306a36Sopenharmony_ci struct dpaa2_eth_channel *ch; 205162306a36Sopenharmony_ci int i; 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci for (i = 0; i < priv->num_channels; i++) { 205462306a36Sopenharmony_ci ch = priv->channel[i]; 205562306a36Sopenharmony_ci napi_enable(&ch->napi); 205662306a36Sopenharmony_ci } 205762306a36Sopenharmony_ci} 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_cistatic void dpaa2_eth_disable_ch_napi(struct dpaa2_eth_priv *priv) 206062306a36Sopenharmony_ci{ 206162306a36Sopenharmony_ci struct dpaa2_eth_channel *ch; 206262306a36Sopenharmony_ci int i; 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_ci for (i = 0; i < priv->num_channels; i++) { 206562306a36Sopenharmony_ci ch = priv->channel[i]; 206662306a36Sopenharmony_ci napi_disable(&ch->napi); 206762306a36Sopenharmony_ci } 206862306a36Sopenharmony_ci} 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_civoid dpaa2_eth_set_rx_taildrop(struct dpaa2_eth_priv *priv, 207162306a36Sopenharmony_ci bool tx_pause, bool pfc) 207262306a36Sopenharmony_ci{ 207362306a36Sopenharmony_ci struct dpni_taildrop td = {0}; 207462306a36Sopenharmony_ci struct dpaa2_eth_fq *fq; 207562306a36Sopenharmony_ci int i, err; 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_ci /* FQ taildrop: threshold is in bytes, per frame queue. Enabled if 207862306a36Sopenharmony_ci * flow control is disabled (as it might interfere with either the 207962306a36Sopenharmony_ci * buffer pool depletion trigger for pause frames or with the group 208062306a36Sopenharmony_ci * congestion trigger for PFC frames) 208162306a36Sopenharmony_ci */ 208262306a36Sopenharmony_ci td.enable = !tx_pause; 208362306a36Sopenharmony_ci if (priv->rx_fqtd_enabled == td.enable) 208462306a36Sopenharmony_ci goto set_cgtd; 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci td.threshold = DPAA2_ETH_FQ_TAILDROP_THRESH; 208762306a36Sopenharmony_ci td.units = DPNI_CONGESTION_UNIT_BYTES; 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci for (i = 0; i < priv->num_fqs; i++) { 209062306a36Sopenharmony_ci fq = &priv->fq[i]; 209162306a36Sopenharmony_ci if (fq->type != DPAA2_RX_FQ) 209262306a36Sopenharmony_ci continue; 209362306a36Sopenharmony_ci err = dpni_set_taildrop(priv->mc_io, 0, priv->mc_token, 209462306a36Sopenharmony_ci DPNI_CP_QUEUE, DPNI_QUEUE_RX, 209562306a36Sopenharmony_ci fq->tc, fq->flowid, &td); 209662306a36Sopenharmony_ci if (err) { 209762306a36Sopenharmony_ci netdev_err(priv->net_dev, 209862306a36Sopenharmony_ci "dpni_set_taildrop(FQ) failed\n"); 209962306a36Sopenharmony_ci return; 210062306a36Sopenharmony_ci } 210162306a36Sopenharmony_ci } 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_ci priv->rx_fqtd_enabled = td.enable; 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ciset_cgtd: 210662306a36Sopenharmony_ci /* Congestion group taildrop: threshold is in frames, per group 210762306a36Sopenharmony_ci * of FQs belonging to the same traffic class 210862306a36Sopenharmony_ci * Enabled if general Tx pause disabled or if PFCs are enabled 210962306a36Sopenharmony_ci * (congestion group threhsold for PFC generation is lower than the 211062306a36Sopenharmony_ci * CG taildrop threshold, so it won't interfere with it; we also 211162306a36Sopenharmony_ci * want frames in non-PFC enabled traffic classes to be kept in check) 211262306a36Sopenharmony_ci */ 211362306a36Sopenharmony_ci td.enable = !tx_pause || pfc; 211462306a36Sopenharmony_ci if (priv->rx_cgtd_enabled == td.enable) 211562306a36Sopenharmony_ci return; 211662306a36Sopenharmony_ci 211762306a36Sopenharmony_ci td.threshold = DPAA2_ETH_CG_TAILDROP_THRESH(priv); 211862306a36Sopenharmony_ci td.units = DPNI_CONGESTION_UNIT_FRAMES; 211962306a36Sopenharmony_ci for (i = 0; i < dpaa2_eth_tc_count(priv); i++) { 212062306a36Sopenharmony_ci err = dpni_set_taildrop(priv->mc_io, 0, priv->mc_token, 212162306a36Sopenharmony_ci DPNI_CP_GROUP, DPNI_QUEUE_RX, 212262306a36Sopenharmony_ci i, 0, &td); 212362306a36Sopenharmony_ci if (err) { 212462306a36Sopenharmony_ci netdev_err(priv->net_dev, 212562306a36Sopenharmony_ci "dpni_set_taildrop(CG) failed\n"); 212662306a36Sopenharmony_ci return; 212762306a36Sopenharmony_ci } 212862306a36Sopenharmony_ci } 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci priv->rx_cgtd_enabled = td.enable; 213162306a36Sopenharmony_ci} 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_cistatic int dpaa2_eth_link_state_update(struct dpaa2_eth_priv *priv) 213462306a36Sopenharmony_ci{ 213562306a36Sopenharmony_ci struct dpni_link_state state = {0}; 213662306a36Sopenharmony_ci bool tx_pause; 213762306a36Sopenharmony_ci int err; 213862306a36Sopenharmony_ci 213962306a36Sopenharmony_ci err = dpni_get_link_state(priv->mc_io, 0, priv->mc_token, &state); 214062306a36Sopenharmony_ci if (unlikely(err)) { 214162306a36Sopenharmony_ci netdev_err(priv->net_dev, 214262306a36Sopenharmony_ci "dpni_get_link_state() failed\n"); 214362306a36Sopenharmony_ci return err; 214462306a36Sopenharmony_ci } 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci /* If Tx pause frame settings have changed, we need to update 214762306a36Sopenharmony_ci * Rx FQ taildrop configuration as well. We configure taildrop 214862306a36Sopenharmony_ci * only when pause frame generation is disabled. 214962306a36Sopenharmony_ci */ 215062306a36Sopenharmony_ci tx_pause = dpaa2_eth_tx_pause_enabled(state.options); 215162306a36Sopenharmony_ci dpaa2_eth_set_rx_taildrop(priv, tx_pause, priv->pfc_enabled); 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci /* When we manage the MAC/PHY using phylink there is no need 215462306a36Sopenharmony_ci * to manually update the netif_carrier. 215562306a36Sopenharmony_ci * We can avoid locking because we are called from the "link changed" 215662306a36Sopenharmony_ci * IRQ handler, which is the same as the "endpoint changed" IRQ handler 215762306a36Sopenharmony_ci * (the writer to priv->mac), so we cannot race with it. 215862306a36Sopenharmony_ci */ 215962306a36Sopenharmony_ci if (dpaa2_mac_is_type_phy(priv->mac)) 216062306a36Sopenharmony_ci goto out; 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci /* Chech link state; speed / duplex changes are not treated yet */ 216362306a36Sopenharmony_ci if (priv->link_state.up == state.up) 216462306a36Sopenharmony_ci goto out; 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci if (state.up) { 216762306a36Sopenharmony_ci netif_carrier_on(priv->net_dev); 216862306a36Sopenharmony_ci netif_tx_start_all_queues(priv->net_dev); 216962306a36Sopenharmony_ci } else { 217062306a36Sopenharmony_ci netif_tx_stop_all_queues(priv->net_dev); 217162306a36Sopenharmony_ci netif_carrier_off(priv->net_dev); 217262306a36Sopenharmony_ci } 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci netdev_info(priv->net_dev, "Link Event: state %s\n", 217562306a36Sopenharmony_ci state.up ? "up" : "down"); 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ciout: 217862306a36Sopenharmony_ci priv->link_state = state; 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci return 0; 218162306a36Sopenharmony_ci} 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_cistatic int dpaa2_eth_open(struct net_device *net_dev) 218462306a36Sopenharmony_ci{ 218562306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 218662306a36Sopenharmony_ci int err; 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_ci dpaa2_eth_seed_pools(priv); 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci mutex_lock(&priv->mac_lock); 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci if (!dpaa2_eth_is_type_phy(priv)) { 219362306a36Sopenharmony_ci /* We'll only start the txqs when the link is actually ready; 219462306a36Sopenharmony_ci * make sure we don't race against the link up notification, 219562306a36Sopenharmony_ci * which may come immediately after dpni_enable(); 219662306a36Sopenharmony_ci */ 219762306a36Sopenharmony_ci netif_tx_stop_all_queues(net_dev); 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci /* Also, explicitly set carrier off, otherwise 220062306a36Sopenharmony_ci * netif_carrier_ok() will return true and cause 'ip link show' 220162306a36Sopenharmony_ci * to report the LOWER_UP flag, even though the link 220262306a36Sopenharmony_ci * notification wasn't even received. 220362306a36Sopenharmony_ci */ 220462306a36Sopenharmony_ci netif_carrier_off(net_dev); 220562306a36Sopenharmony_ci } 220662306a36Sopenharmony_ci dpaa2_eth_enable_ch_napi(priv); 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ci err = dpni_enable(priv->mc_io, 0, priv->mc_token); 220962306a36Sopenharmony_ci if (err < 0) { 221062306a36Sopenharmony_ci mutex_unlock(&priv->mac_lock); 221162306a36Sopenharmony_ci netdev_err(net_dev, "dpni_enable() failed\n"); 221262306a36Sopenharmony_ci goto enable_err; 221362306a36Sopenharmony_ci } 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci if (dpaa2_eth_is_type_phy(priv)) 221662306a36Sopenharmony_ci dpaa2_mac_start(priv->mac); 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci mutex_unlock(&priv->mac_lock); 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci return 0; 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_cienable_err: 222362306a36Sopenharmony_ci dpaa2_eth_disable_ch_napi(priv); 222462306a36Sopenharmony_ci dpaa2_eth_drain_pools(priv); 222562306a36Sopenharmony_ci return err; 222662306a36Sopenharmony_ci} 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci/* Total number of in-flight frames on ingress queues */ 222962306a36Sopenharmony_cistatic u32 dpaa2_eth_ingress_fq_count(struct dpaa2_eth_priv *priv) 223062306a36Sopenharmony_ci{ 223162306a36Sopenharmony_ci struct dpaa2_eth_fq *fq; 223262306a36Sopenharmony_ci u32 fcnt = 0, bcnt = 0, total = 0; 223362306a36Sopenharmony_ci int i, err; 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_ci for (i = 0; i < priv->num_fqs; i++) { 223662306a36Sopenharmony_ci fq = &priv->fq[i]; 223762306a36Sopenharmony_ci err = dpaa2_io_query_fq_count(NULL, fq->fqid, &fcnt, &bcnt); 223862306a36Sopenharmony_ci if (err) { 223962306a36Sopenharmony_ci netdev_warn(priv->net_dev, "query_fq_count failed"); 224062306a36Sopenharmony_ci break; 224162306a36Sopenharmony_ci } 224262306a36Sopenharmony_ci total += fcnt; 224362306a36Sopenharmony_ci } 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_ci return total; 224662306a36Sopenharmony_ci} 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_cistatic void dpaa2_eth_wait_for_ingress_fq_empty(struct dpaa2_eth_priv *priv) 224962306a36Sopenharmony_ci{ 225062306a36Sopenharmony_ci int retries = 10; 225162306a36Sopenharmony_ci u32 pending; 225262306a36Sopenharmony_ci 225362306a36Sopenharmony_ci do { 225462306a36Sopenharmony_ci pending = dpaa2_eth_ingress_fq_count(priv); 225562306a36Sopenharmony_ci if (pending) 225662306a36Sopenharmony_ci msleep(100); 225762306a36Sopenharmony_ci } while (pending && --retries); 225862306a36Sopenharmony_ci} 225962306a36Sopenharmony_ci 226062306a36Sopenharmony_ci#define DPNI_TX_PENDING_VER_MAJOR 7 226162306a36Sopenharmony_ci#define DPNI_TX_PENDING_VER_MINOR 13 226262306a36Sopenharmony_cistatic void dpaa2_eth_wait_for_egress_fq_empty(struct dpaa2_eth_priv *priv) 226362306a36Sopenharmony_ci{ 226462306a36Sopenharmony_ci union dpni_statistics stats; 226562306a36Sopenharmony_ci int retries = 10; 226662306a36Sopenharmony_ci int err; 226762306a36Sopenharmony_ci 226862306a36Sopenharmony_ci if (dpaa2_eth_cmp_dpni_ver(priv, DPNI_TX_PENDING_VER_MAJOR, 226962306a36Sopenharmony_ci DPNI_TX_PENDING_VER_MINOR) < 0) 227062306a36Sopenharmony_ci goto out; 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci do { 227362306a36Sopenharmony_ci err = dpni_get_statistics(priv->mc_io, 0, priv->mc_token, 6, 227462306a36Sopenharmony_ci &stats); 227562306a36Sopenharmony_ci if (err) 227662306a36Sopenharmony_ci goto out; 227762306a36Sopenharmony_ci if (stats.page_6.tx_pending_frames == 0) 227862306a36Sopenharmony_ci return; 227962306a36Sopenharmony_ci } while (--retries); 228062306a36Sopenharmony_ci 228162306a36Sopenharmony_ciout: 228262306a36Sopenharmony_ci msleep(500); 228362306a36Sopenharmony_ci} 228462306a36Sopenharmony_ci 228562306a36Sopenharmony_cistatic int dpaa2_eth_stop(struct net_device *net_dev) 228662306a36Sopenharmony_ci{ 228762306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 228862306a36Sopenharmony_ci int dpni_enabled = 0; 228962306a36Sopenharmony_ci int retries = 10; 229062306a36Sopenharmony_ci 229162306a36Sopenharmony_ci mutex_lock(&priv->mac_lock); 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci if (dpaa2_eth_is_type_phy(priv)) { 229462306a36Sopenharmony_ci dpaa2_mac_stop(priv->mac); 229562306a36Sopenharmony_ci } else { 229662306a36Sopenharmony_ci netif_tx_stop_all_queues(net_dev); 229762306a36Sopenharmony_ci netif_carrier_off(net_dev); 229862306a36Sopenharmony_ci } 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_ci mutex_unlock(&priv->mac_lock); 230162306a36Sopenharmony_ci 230262306a36Sopenharmony_ci /* On dpni_disable(), the MC firmware will: 230362306a36Sopenharmony_ci * - stop MAC Rx and wait for all Rx frames to be enqueued to software 230462306a36Sopenharmony_ci * - cut off WRIOP dequeues from egress FQs and wait until transmission 230562306a36Sopenharmony_ci * of all in flight Tx frames is finished (and corresponding Tx conf 230662306a36Sopenharmony_ci * frames are enqueued back to software) 230762306a36Sopenharmony_ci * 230862306a36Sopenharmony_ci * Before calling dpni_disable(), we wait for all Tx frames to arrive 230962306a36Sopenharmony_ci * on WRIOP. After it finishes, wait until all remaining frames on Rx 231062306a36Sopenharmony_ci * and Tx conf queues are consumed on NAPI poll. 231162306a36Sopenharmony_ci */ 231262306a36Sopenharmony_ci dpaa2_eth_wait_for_egress_fq_empty(priv); 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ci do { 231562306a36Sopenharmony_ci dpni_disable(priv->mc_io, 0, priv->mc_token); 231662306a36Sopenharmony_ci dpni_is_enabled(priv->mc_io, 0, priv->mc_token, &dpni_enabled); 231762306a36Sopenharmony_ci if (dpni_enabled) 231862306a36Sopenharmony_ci /* Allow the hardware some slack */ 231962306a36Sopenharmony_ci msleep(100); 232062306a36Sopenharmony_ci } while (dpni_enabled && --retries); 232162306a36Sopenharmony_ci if (!retries) { 232262306a36Sopenharmony_ci netdev_warn(net_dev, "Retry count exceeded disabling DPNI\n"); 232362306a36Sopenharmony_ci /* Must go on and disable NAPI nonetheless, so we don't crash at 232462306a36Sopenharmony_ci * the next "ifconfig up" 232562306a36Sopenharmony_ci */ 232662306a36Sopenharmony_ci } 232762306a36Sopenharmony_ci 232862306a36Sopenharmony_ci dpaa2_eth_wait_for_ingress_fq_empty(priv); 232962306a36Sopenharmony_ci dpaa2_eth_disable_ch_napi(priv); 233062306a36Sopenharmony_ci 233162306a36Sopenharmony_ci /* Empty the buffer pool */ 233262306a36Sopenharmony_ci dpaa2_eth_drain_pools(priv); 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_ci /* Empty the Scatter-Gather Buffer cache */ 233562306a36Sopenharmony_ci dpaa2_eth_sgt_cache_drain(priv); 233662306a36Sopenharmony_ci 233762306a36Sopenharmony_ci return 0; 233862306a36Sopenharmony_ci} 233962306a36Sopenharmony_ci 234062306a36Sopenharmony_cistatic int dpaa2_eth_set_addr(struct net_device *net_dev, void *addr) 234162306a36Sopenharmony_ci{ 234262306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 234362306a36Sopenharmony_ci struct device *dev = net_dev->dev.parent; 234462306a36Sopenharmony_ci int err; 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci err = eth_mac_addr(net_dev, addr); 234762306a36Sopenharmony_ci if (err < 0) { 234862306a36Sopenharmony_ci dev_err(dev, "eth_mac_addr() failed (%d)\n", err); 234962306a36Sopenharmony_ci return err; 235062306a36Sopenharmony_ci } 235162306a36Sopenharmony_ci 235262306a36Sopenharmony_ci err = dpni_set_primary_mac_addr(priv->mc_io, 0, priv->mc_token, 235362306a36Sopenharmony_ci net_dev->dev_addr); 235462306a36Sopenharmony_ci if (err) { 235562306a36Sopenharmony_ci dev_err(dev, "dpni_set_primary_mac_addr() failed (%d)\n", err); 235662306a36Sopenharmony_ci return err; 235762306a36Sopenharmony_ci } 235862306a36Sopenharmony_ci 235962306a36Sopenharmony_ci return 0; 236062306a36Sopenharmony_ci} 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_ci/** Fill in counters maintained by the GPP driver. These may be different from 236362306a36Sopenharmony_ci * the hardware counters obtained by ethtool. 236462306a36Sopenharmony_ci */ 236562306a36Sopenharmony_cistatic void dpaa2_eth_get_stats(struct net_device *net_dev, 236662306a36Sopenharmony_ci struct rtnl_link_stats64 *stats) 236762306a36Sopenharmony_ci{ 236862306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 236962306a36Sopenharmony_ci struct rtnl_link_stats64 *percpu_stats; 237062306a36Sopenharmony_ci u64 *cpustats; 237162306a36Sopenharmony_ci u64 *netstats = (u64 *)stats; 237262306a36Sopenharmony_ci int i, j; 237362306a36Sopenharmony_ci int num = sizeof(struct rtnl_link_stats64) / sizeof(u64); 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci for_each_possible_cpu(i) { 237662306a36Sopenharmony_ci percpu_stats = per_cpu_ptr(priv->percpu_stats, i); 237762306a36Sopenharmony_ci cpustats = (u64 *)percpu_stats; 237862306a36Sopenharmony_ci for (j = 0; j < num; j++) 237962306a36Sopenharmony_ci netstats[j] += cpustats[j]; 238062306a36Sopenharmony_ci } 238162306a36Sopenharmony_ci} 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci/* Copy mac unicast addresses from @net_dev to @priv. 238462306a36Sopenharmony_ci * Its sole purpose is to make dpaa2_eth_set_rx_mode() more readable. 238562306a36Sopenharmony_ci */ 238662306a36Sopenharmony_cistatic void dpaa2_eth_add_uc_hw_addr(const struct net_device *net_dev, 238762306a36Sopenharmony_ci struct dpaa2_eth_priv *priv) 238862306a36Sopenharmony_ci{ 238962306a36Sopenharmony_ci struct netdev_hw_addr *ha; 239062306a36Sopenharmony_ci int err; 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_ci netdev_for_each_uc_addr(ha, net_dev) { 239362306a36Sopenharmony_ci err = dpni_add_mac_addr(priv->mc_io, 0, priv->mc_token, 239462306a36Sopenharmony_ci ha->addr); 239562306a36Sopenharmony_ci if (err) 239662306a36Sopenharmony_ci netdev_warn(priv->net_dev, 239762306a36Sopenharmony_ci "Could not add ucast MAC %pM to the filtering table (err %d)\n", 239862306a36Sopenharmony_ci ha->addr, err); 239962306a36Sopenharmony_ci } 240062306a36Sopenharmony_ci} 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_ci/* Copy mac multicast addresses from @net_dev to @priv 240362306a36Sopenharmony_ci * Its sole purpose is to make dpaa2_eth_set_rx_mode() more readable. 240462306a36Sopenharmony_ci */ 240562306a36Sopenharmony_cistatic void dpaa2_eth_add_mc_hw_addr(const struct net_device *net_dev, 240662306a36Sopenharmony_ci struct dpaa2_eth_priv *priv) 240762306a36Sopenharmony_ci{ 240862306a36Sopenharmony_ci struct netdev_hw_addr *ha; 240962306a36Sopenharmony_ci int err; 241062306a36Sopenharmony_ci 241162306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, net_dev) { 241262306a36Sopenharmony_ci err = dpni_add_mac_addr(priv->mc_io, 0, priv->mc_token, 241362306a36Sopenharmony_ci ha->addr); 241462306a36Sopenharmony_ci if (err) 241562306a36Sopenharmony_ci netdev_warn(priv->net_dev, 241662306a36Sopenharmony_ci "Could not add mcast MAC %pM to the filtering table (err %d)\n", 241762306a36Sopenharmony_ci ha->addr, err); 241862306a36Sopenharmony_ci } 241962306a36Sopenharmony_ci} 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_cistatic int dpaa2_eth_rx_add_vid(struct net_device *net_dev, 242262306a36Sopenharmony_ci __be16 vlan_proto, u16 vid) 242362306a36Sopenharmony_ci{ 242462306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 242562306a36Sopenharmony_ci int err; 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci err = dpni_add_vlan_id(priv->mc_io, 0, priv->mc_token, 242862306a36Sopenharmony_ci vid, 0, 0, 0); 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci if (err) { 243162306a36Sopenharmony_ci netdev_warn(priv->net_dev, 243262306a36Sopenharmony_ci "Could not add the vlan id %u\n", 243362306a36Sopenharmony_ci vid); 243462306a36Sopenharmony_ci return err; 243562306a36Sopenharmony_ci } 243662306a36Sopenharmony_ci 243762306a36Sopenharmony_ci return 0; 243862306a36Sopenharmony_ci} 243962306a36Sopenharmony_ci 244062306a36Sopenharmony_cistatic int dpaa2_eth_rx_kill_vid(struct net_device *net_dev, 244162306a36Sopenharmony_ci __be16 vlan_proto, u16 vid) 244262306a36Sopenharmony_ci{ 244362306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 244462306a36Sopenharmony_ci int err; 244562306a36Sopenharmony_ci 244662306a36Sopenharmony_ci err = dpni_remove_vlan_id(priv->mc_io, 0, priv->mc_token, vid); 244762306a36Sopenharmony_ci 244862306a36Sopenharmony_ci if (err) { 244962306a36Sopenharmony_ci netdev_warn(priv->net_dev, 245062306a36Sopenharmony_ci "Could not remove the vlan id %u\n", 245162306a36Sopenharmony_ci vid); 245262306a36Sopenharmony_ci return err; 245362306a36Sopenharmony_ci } 245462306a36Sopenharmony_ci 245562306a36Sopenharmony_ci return 0; 245662306a36Sopenharmony_ci} 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_cistatic void dpaa2_eth_set_rx_mode(struct net_device *net_dev) 245962306a36Sopenharmony_ci{ 246062306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 246162306a36Sopenharmony_ci int uc_count = netdev_uc_count(net_dev); 246262306a36Sopenharmony_ci int mc_count = netdev_mc_count(net_dev); 246362306a36Sopenharmony_ci u8 max_mac = priv->dpni_attrs.mac_filter_entries; 246462306a36Sopenharmony_ci u32 options = priv->dpni_attrs.options; 246562306a36Sopenharmony_ci u16 mc_token = priv->mc_token; 246662306a36Sopenharmony_ci struct fsl_mc_io *mc_io = priv->mc_io; 246762306a36Sopenharmony_ci int err; 246862306a36Sopenharmony_ci 246962306a36Sopenharmony_ci /* Basic sanity checks; these probably indicate a misconfiguration */ 247062306a36Sopenharmony_ci if (options & DPNI_OPT_NO_MAC_FILTER && max_mac != 0) 247162306a36Sopenharmony_ci netdev_info(net_dev, 247262306a36Sopenharmony_ci "mac_filter_entries=%d, DPNI_OPT_NO_MAC_FILTER option must be disabled\n", 247362306a36Sopenharmony_ci max_mac); 247462306a36Sopenharmony_ci 247562306a36Sopenharmony_ci /* Force promiscuous if the uc or mc counts exceed our capabilities. */ 247662306a36Sopenharmony_ci if (uc_count > max_mac) { 247762306a36Sopenharmony_ci netdev_info(net_dev, 247862306a36Sopenharmony_ci "Unicast addr count reached %d, max allowed is %d; forcing promisc\n", 247962306a36Sopenharmony_ci uc_count, max_mac); 248062306a36Sopenharmony_ci goto force_promisc; 248162306a36Sopenharmony_ci } 248262306a36Sopenharmony_ci if (mc_count + uc_count > max_mac) { 248362306a36Sopenharmony_ci netdev_info(net_dev, 248462306a36Sopenharmony_ci "Unicast + multicast addr count reached %d, max allowed is %d; forcing promisc\n", 248562306a36Sopenharmony_ci uc_count + mc_count, max_mac); 248662306a36Sopenharmony_ci goto force_mc_promisc; 248762306a36Sopenharmony_ci } 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_ci /* Adjust promisc settings due to flag combinations */ 249062306a36Sopenharmony_ci if (net_dev->flags & IFF_PROMISC) 249162306a36Sopenharmony_ci goto force_promisc; 249262306a36Sopenharmony_ci if (net_dev->flags & IFF_ALLMULTI) { 249362306a36Sopenharmony_ci /* First, rebuild unicast filtering table. This should be done 249462306a36Sopenharmony_ci * in promisc mode, in order to avoid frame loss while we 249562306a36Sopenharmony_ci * progressively add entries to the table. 249662306a36Sopenharmony_ci * We don't know whether we had been in promisc already, and 249762306a36Sopenharmony_ci * making an MC call to find out is expensive; so set uc promisc 249862306a36Sopenharmony_ci * nonetheless. 249962306a36Sopenharmony_ci */ 250062306a36Sopenharmony_ci err = dpni_set_unicast_promisc(mc_io, 0, mc_token, 1); 250162306a36Sopenharmony_ci if (err) 250262306a36Sopenharmony_ci netdev_warn(net_dev, "Can't set uc promisc\n"); 250362306a36Sopenharmony_ci 250462306a36Sopenharmony_ci /* Actual uc table reconstruction. */ 250562306a36Sopenharmony_ci err = dpni_clear_mac_filters(mc_io, 0, mc_token, 1, 0); 250662306a36Sopenharmony_ci if (err) 250762306a36Sopenharmony_ci netdev_warn(net_dev, "Can't clear uc filters\n"); 250862306a36Sopenharmony_ci dpaa2_eth_add_uc_hw_addr(net_dev, priv); 250962306a36Sopenharmony_ci 251062306a36Sopenharmony_ci /* Finally, clear uc promisc and set mc promisc as requested. */ 251162306a36Sopenharmony_ci err = dpni_set_unicast_promisc(mc_io, 0, mc_token, 0); 251262306a36Sopenharmony_ci if (err) 251362306a36Sopenharmony_ci netdev_warn(net_dev, "Can't clear uc promisc\n"); 251462306a36Sopenharmony_ci goto force_mc_promisc; 251562306a36Sopenharmony_ci } 251662306a36Sopenharmony_ci 251762306a36Sopenharmony_ci /* Neither unicast, nor multicast promisc will be on... eventually. 251862306a36Sopenharmony_ci * For now, rebuild mac filtering tables while forcing both of them on. 251962306a36Sopenharmony_ci */ 252062306a36Sopenharmony_ci err = dpni_set_unicast_promisc(mc_io, 0, mc_token, 1); 252162306a36Sopenharmony_ci if (err) 252262306a36Sopenharmony_ci netdev_warn(net_dev, "Can't set uc promisc (%d)\n", err); 252362306a36Sopenharmony_ci err = dpni_set_multicast_promisc(mc_io, 0, mc_token, 1); 252462306a36Sopenharmony_ci if (err) 252562306a36Sopenharmony_ci netdev_warn(net_dev, "Can't set mc promisc (%d)\n", err); 252662306a36Sopenharmony_ci 252762306a36Sopenharmony_ci /* Actual mac filtering tables reconstruction */ 252862306a36Sopenharmony_ci err = dpni_clear_mac_filters(mc_io, 0, mc_token, 1, 1); 252962306a36Sopenharmony_ci if (err) 253062306a36Sopenharmony_ci netdev_warn(net_dev, "Can't clear mac filters\n"); 253162306a36Sopenharmony_ci dpaa2_eth_add_mc_hw_addr(net_dev, priv); 253262306a36Sopenharmony_ci dpaa2_eth_add_uc_hw_addr(net_dev, priv); 253362306a36Sopenharmony_ci 253462306a36Sopenharmony_ci /* Now we can clear both ucast and mcast promisc, without risking 253562306a36Sopenharmony_ci * to drop legitimate frames anymore. 253662306a36Sopenharmony_ci */ 253762306a36Sopenharmony_ci err = dpni_set_unicast_promisc(mc_io, 0, mc_token, 0); 253862306a36Sopenharmony_ci if (err) 253962306a36Sopenharmony_ci netdev_warn(net_dev, "Can't clear ucast promisc\n"); 254062306a36Sopenharmony_ci err = dpni_set_multicast_promisc(mc_io, 0, mc_token, 0); 254162306a36Sopenharmony_ci if (err) 254262306a36Sopenharmony_ci netdev_warn(net_dev, "Can't clear mcast promisc\n"); 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_ci return; 254562306a36Sopenharmony_ci 254662306a36Sopenharmony_ciforce_promisc: 254762306a36Sopenharmony_ci err = dpni_set_unicast_promisc(mc_io, 0, mc_token, 1); 254862306a36Sopenharmony_ci if (err) 254962306a36Sopenharmony_ci netdev_warn(net_dev, "Can't set ucast promisc\n"); 255062306a36Sopenharmony_ciforce_mc_promisc: 255162306a36Sopenharmony_ci err = dpni_set_multicast_promisc(mc_io, 0, mc_token, 1); 255262306a36Sopenharmony_ci if (err) 255362306a36Sopenharmony_ci netdev_warn(net_dev, "Can't set mcast promisc\n"); 255462306a36Sopenharmony_ci} 255562306a36Sopenharmony_ci 255662306a36Sopenharmony_cistatic int dpaa2_eth_set_features(struct net_device *net_dev, 255762306a36Sopenharmony_ci netdev_features_t features) 255862306a36Sopenharmony_ci{ 255962306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 256062306a36Sopenharmony_ci netdev_features_t changed = features ^ net_dev->features; 256162306a36Sopenharmony_ci bool enable; 256262306a36Sopenharmony_ci int err; 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) { 256562306a36Sopenharmony_ci enable = !!(features & NETIF_F_HW_VLAN_CTAG_FILTER); 256662306a36Sopenharmony_ci err = dpaa2_eth_set_rx_vlan_filtering(priv, enable); 256762306a36Sopenharmony_ci if (err) 256862306a36Sopenharmony_ci return err; 256962306a36Sopenharmony_ci } 257062306a36Sopenharmony_ci 257162306a36Sopenharmony_ci if (changed & NETIF_F_RXCSUM) { 257262306a36Sopenharmony_ci enable = !!(features & NETIF_F_RXCSUM); 257362306a36Sopenharmony_ci err = dpaa2_eth_set_rx_csum(priv, enable); 257462306a36Sopenharmony_ci if (err) 257562306a36Sopenharmony_ci return err; 257662306a36Sopenharmony_ci } 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ci if (changed & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) { 257962306a36Sopenharmony_ci enable = !!(features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)); 258062306a36Sopenharmony_ci err = dpaa2_eth_set_tx_csum(priv, enable); 258162306a36Sopenharmony_ci if (err) 258262306a36Sopenharmony_ci return err; 258362306a36Sopenharmony_ci } 258462306a36Sopenharmony_ci 258562306a36Sopenharmony_ci return 0; 258662306a36Sopenharmony_ci} 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_cistatic int dpaa2_eth_ts_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 258962306a36Sopenharmony_ci{ 259062306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(dev); 259162306a36Sopenharmony_ci struct hwtstamp_config config; 259262306a36Sopenharmony_ci 259362306a36Sopenharmony_ci if (!dpaa2_ptp) 259462306a36Sopenharmony_ci return -EINVAL; 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_ci if (copy_from_user(&config, rq->ifr_data, sizeof(config))) 259762306a36Sopenharmony_ci return -EFAULT; 259862306a36Sopenharmony_ci 259962306a36Sopenharmony_ci switch (config.tx_type) { 260062306a36Sopenharmony_ci case HWTSTAMP_TX_OFF: 260162306a36Sopenharmony_ci case HWTSTAMP_TX_ON: 260262306a36Sopenharmony_ci case HWTSTAMP_TX_ONESTEP_SYNC: 260362306a36Sopenharmony_ci priv->tx_tstamp_type = config.tx_type; 260462306a36Sopenharmony_ci break; 260562306a36Sopenharmony_ci default: 260662306a36Sopenharmony_ci return -ERANGE; 260762306a36Sopenharmony_ci } 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_ci if (config.rx_filter == HWTSTAMP_FILTER_NONE) { 261062306a36Sopenharmony_ci priv->rx_tstamp = false; 261162306a36Sopenharmony_ci } else { 261262306a36Sopenharmony_ci priv->rx_tstamp = true; 261362306a36Sopenharmony_ci /* TS is set for all frame types, not only those requested */ 261462306a36Sopenharmony_ci config.rx_filter = HWTSTAMP_FILTER_ALL; 261562306a36Sopenharmony_ci } 261662306a36Sopenharmony_ci 261762306a36Sopenharmony_ci if (priv->tx_tstamp_type == HWTSTAMP_TX_ONESTEP_SYNC) 261862306a36Sopenharmony_ci dpaa2_ptp_onestep_reg_update_method(priv); 261962306a36Sopenharmony_ci 262062306a36Sopenharmony_ci return copy_to_user(rq->ifr_data, &config, sizeof(config)) ? 262162306a36Sopenharmony_ci -EFAULT : 0; 262262306a36Sopenharmony_ci} 262362306a36Sopenharmony_ci 262462306a36Sopenharmony_cistatic int dpaa2_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 262562306a36Sopenharmony_ci{ 262662306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(dev); 262762306a36Sopenharmony_ci int err; 262862306a36Sopenharmony_ci 262962306a36Sopenharmony_ci if (cmd == SIOCSHWTSTAMP) 263062306a36Sopenharmony_ci return dpaa2_eth_ts_ioctl(dev, rq, cmd); 263162306a36Sopenharmony_ci 263262306a36Sopenharmony_ci mutex_lock(&priv->mac_lock); 263362306a36Sopenharmony_ci 263462306a36Sopenharmony_ci if (dpaa2_eth_is_type_phy(priv)) { 263562306a36Sopenharmony_ci err = phylink_mii_ioctl(priv->mac->phylink, rq, cmd); 263662306a36Sopenharmony_ci mutex_unlock(&priv->mac_lock); 263762306a36Sopenharmony_ci return err; 263862306a36Sopenharmony_ci } 263962306a36Sopenharmony_ci 264062306a36Sopenharmony_ci mutex_unlock(&priv->mac_lock); 264162306a36Sopenharmony_ci 264262306a36Sopenharmony_ci return -EOPNOTSUPP; 264362306a36Sopenharmony_ci} 264462306a36Sopenharmony_ci 264562306a36Sopenharmony_cistatic bool xdp_mtu_valid(struct dpaa2_eth_priv *priv, int mtu) 264662306a36Sopenharmony_ci{ 264762306a36Sopenharmony_ci int mfl, linear_mfl; 264862306a36Sopenharmony_ci 264962306a36Sopenharmony_ci mfl = DPAA2_ETH_L2_MAX_FRM(mtu); 265062306a36Sopenharmony_ci linear_mfl = priv->rx_buf_size - DPAA2_ETH_RX_HWA_SIZE - 265162306a36Sopenharmony_ci dpaa2_eth_rx_head_room(priv) - XDP_PACKET_HEADROOM; 265262306a36Sopenharmony_ci 265362306a36Sopenharmony_ci if (mfl > linear_mfl) { 265462306a36Sopenharmony_ci netdev_warn(priv->net_dev, "Maximum MTU for XDP is %d\n", 265562306a36Sopenharmony_ci linear_mfl - VLAN_ETH_HLEN); 265662306a36Sopenharmony_ci return false; 265762306a36Sopenharmony_ci } 265862306a36Sopenharmony_ci 265962306a36Sopenharmony_ci return true; 266062306a36Sopenharmony_ci} 266162306a36Sopenharmony_ci 266262306a36Sopenharmony_cistatic int dpaa2_eth_set_rx_mfl(struct dpaa2_eth_priv *priv, int mtu, bool has_xdp) 266362306a36Sopenharmony_ci{ 266462306a36Sopenharmony_ci int mfl, err; 266562306a36Sopenharmony_ci 266662306a36Sopenharmony_ci /* We enforce a maximum Rx frame length based on MTU only if we have 266762306a36Sopenharmony_ci * an XDP program attached (in order to avoid Rx S/G frames). 266862306a36Sopenharmony_ci * Otherwise, we accept all incoming frames as long as they are not 266962306a36Sopenharmony_ci * larger than maximum size supported in hardware 267062306a36Sopenharmony_ci */ 267162306a36Sopenharmony_ci if (has_xdp) 267262306a36Sopenharmony_ci mfl = DPAA2_ETH_L2_MAX_FRM(mtu); 267362306a36Sopenharmony_ci else 267462306a36Sopenharmony_ci mfl = DPAA2_ETH_MFL; 267562306a36Sopenharmony_ci 267662306a36Sopenharmony_ci err = dpni_set_max_frame_length(priv->mc_io, 0, priv->mc_token, mfl); 267762306a36Sopenharmony_ci if (err) { 267862306a36Sopenharmony_ci netdev_err(priv->net_dev, "dpni_set_max_frame_length failed\n"); 267962306a36Sopenharmony_ci return err; 268062306a36Sopenharmony_ci } 268162306a36Sopenharmony_ci 268262306a36Sopenharmony_ci return 0; 268362306a36Sopenharmony_ci} 268462306a36Sopenharmony_ci 268562306a36Sopenharmony_cistatic int dpaa2_eth_change_mtu(struct net_device *dev, int new_mtu) 268662306a36Sopenharmony_ci{ 268762306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(dev); 268862306a36Sopenharmony_ci int err; 268962306a36Sopenharmony_ci 269062306a36Sopenharmony_ci if (!priv->xdp_prog) 269162306a36Sopenharmony_ci goto out; 269262306a36Sopenharmony_ci 269362306a36Sopenharmony_ci if (!xdp_mtu_valid(priv, new_mtu)) 269462306a36Sopenharmony_ci return -EINVAL; 269562306a36Sopenharmony_ci 269662306a36Sopenharmony_ci err = dpaa2_eth_set_rx_mfl(priv, new_mtu, true); 269762306a36Sopenharmony_ci if (err) 269862306a36Sopenharmony_ci return err; 269962306a36Sopenharmony_ci 270062306a36Sopenharmony_ciout: 270162306a36Sopenharmony_ci dev->mtu = new_mtu; 270262306a36Sopenharmony_ci return 0; 270362306a36Sopenharmony_ci} 270462306a36Sopenharmony_ci 270562306a36Sopenharmony_cistatic int dpaa2_eth_update_rx_buffer_headroom(struct dpaa2_eth_priv *priv, bool has_xdp) 270662306a36Sopenharmony_ci{ 270762306a36Sopenharmony_ci struct dpni_buffer_layout buf_layout = {0}; 270862306a36Sopenharmony_ci int err; 270962306a36Sopenharmony_ci 271062306a36Sopenharmony_ci err = dpni_get_buffer_layout(priv->mc_io, 0, priv->mc_token, 271162306a36Sopenharmony_ci DPNI_QUEUE_RX, &buf_layout); 271262306a36Sopenharmony_ci if (err) { 271362306a36Sopenharmony_ci netdev_err(priv->net_dev, "dpni_get_buffer_layout failed\n"); 271462306a36Sopenharmony_ci return err; 271562306a36Sopenharmony_ci } 271662306a36Sopenharmony_ci 271762306a36Sopenharmony_ci /* Reserve extra headroom for XDP header size changes */ 271862306a36Sopenharmony_ci buf_layout.data_head_room = dpaa2_eth_rx_head_room(priv) + 271962306a36Sopenharmony_ci (has_xdp ? XDP_PACKET_HEADROOM : 0); 272062306a36Sopenharmony_ci buf_layout.options = DPNI_BUF_LAYOUT_OPT_DATA_HEAD_ROOM; 272162306a36Sopenharmony_ci err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token, 272262306a36Sopenharmony_ci DPNI_QUEUE_RX, &buf_layout); 272362306a36Sopenharmony_ci if (err) { 272462306a36Sopenharmony_ci netdev_err(priv->net_dev, "dpni_set_buffer_layout failed\n"); 272562306a36Sopenharmony_ci return err; 272662306a36Sopenharmony_ci } 272762306a36Sopenharmony_ci 272862306a36Sopenharmony_ci return 0; 272962306a36Sopenharmony_ci} 273062306a36Sopenharmony_ci 273162306a36Sopenharmony_cistatic int dpaa2_eth_setup_xdp(struct net_device *dev, struct bpf_prog *prog) 273262306a36Sopenharmony_ci{ 273362306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(dev); 273462306a36Sopenharmony_ci struct dpaa2_eth_channel *ch; 273562306a36Sopenharmony_ci struct bpf_prog *old; 273662306a36Sopenharmony_ci bool up, need_update; 273762306a36Sopenharmony_ci int i, err; 273862306a36Sopenharmony_ci 273962306a36Sopenharmony_ci if (prog && !xdp_mtu_valid(priv, dev->mtu)) 274062306a36Sopenharmony_ci return -EINVAL; 274162306a36Sopenharmony_ci 274262306a36Sopenharmony_ci if (prog) 274362306a36Sopenharmony_ci bpf_prog_add(prog, priv->num_channels); 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci up = netif_running(dev); 274662306a36Sopenharmony_ci need_update = (!!priv->xdp_prog != !!prog); 274762306a36Sopenharmony_ci 274862306a36Sopenharmony_ci if (up) 274962306a36Sopenharmony_ci dev_close(dev); 275062306a36Sopenharmony_ci 275162306a36Sopenharmony_ci /* While in xdp mode, enforce a maximum Rx frame size based on MTU. 275262306a36Sopenharmony_ci * Also, when switching between xdp/non-xdp modes we need to reconfigure 275362306a36Sopenharmony_ci * our Rx buffer layout. Buffer pool was drained on dpaa2_eth_stop, 275462306a36Sopenharmony_ci * so we are sure no old format buffers will be used from now on. 275562306a36Sopenharmony_ci */ 275662306a36Sopenharmony_ci if (need_update) { 275762306a36Sopenharmony_ci err = dpaa2_eth_set_rx_mfl(priv, dev->mtu, !!prog); 275862306a36Sopenharmony_ci if (err) 275962306a36Sopenharmony_ci goto out_err; 276062306a36Sopenharmony_ci err = dpaa2_eth_update_rx_buffer_headroom(priv, !!prog); 276162306a36Sopenharmony_ci if (err) 276262306a36Sopenharmony_ci goto out_err; 276362306a36Sopenharmony_ci } 276462306a36Sopenharmony_ci 276562306a36Sopenharmony_ci old = xchg(&priv->xdp_prog, prog); 276662306a36Sopenharmony_ci if (old) 276762306a36Sopenharmony_ci bpf_prog_put(old); 276862306a36Sopenharmony_ci 276962306a36Sopenharmony_ci for (i = 0; i < priv->num_channels; i++) { 277062306a36Sopenharmony_ci ch = priv->channel[i]; 277162306a36Sopenharmony_ci old = xchg(&ch->xdp.prog, prog); 277262306a36Sopenharmony_ci if (old) 277362306a36Sopenharmony_ci bpf_prog_put(old); 277462306a36Sopenharmony_ci } 277562306a36Sopenharmony_ci 277662306a36Sopenharmony_ci if (up) { 277762306a36Sopenharmony_ci err = dev_open(dev, NULL); 277862306a36Sopenharmony_ci if (err) 277962306a36Sopenharmony_ci return err; 278062306a36Sopenharmony_ci } 278162306a36Sopenharmony_ci 278262306a36Sopenharmony_ci return 0; 278362306a36Sopenharmony_ci 278462306a36Sopenharmony_ciout_err: 278562306a36Sopenharmony_ci if (prog) 278662306a36Sopenharmony_ci bpf_prog_sub(prog, priv->num_channels); 278762306a36Sopenharmony_ci if (up) 278862306a36Sopenharmony_ci dev_open(dev, NULL); 278962306a36Sopenharmony_ci 279062306a36Sopenharmony_ci return err; 279162306a36Sopenharmony_ci} 279262306a36Sopenharmony_ci 279362306a36Sopenharmony_cistatic int dpaa2_eth_xdp(struct net_device *dev, struct netdev_bpf *xdp) 279462306a36Sopenharmony_ci{ 279562306a36Sopenharmony_ci switch (xdp->command) { 279662306a36Sopenharmony_ci case XDP_SETUP_PROG: 279762306a36Sopenharmony_ci return dpaa2_eth_setup_xdp(dev, xdp->prog); 279862306a36Sopenharmony_ci case XDP_SETUP_XSK_POOL: 279962306a36Sopenharmony_ci return dpaa2_xsk_setup_pool(dev, xdp->xsk.pool, xdp->xsk.queue_id); 280062306a36Sopenharmony_ci default: 280162306a36Sopenharmony_ci return -EINVAL; 280262306a36Sopenharmony_ci } 280362306a36Sopenharmony_ci 280462306a36Sopenharmony_ci return 0; 280562306a36Sopenharmony_ci} 280662306a36Sopenharmony_ci 280762306a36Sopenharmony_cistatic int dpaa2_eth_xdp_create_fd(struct net_device *net_dev, 280862306a36Sopenharmony_ci struct xdp_frame *xdpf, 280962306a36Sopenharmony_ci struct dpaa2_fd *fd) 281062306a36Sopenharmony_ci{ 281162306a36Sopenharmony_ci struct device *dev = net_dev->dev.parent; 281262306a36Sopenharmony_ci unsigned int needed_headroom; 281362306a36Sopenharmony_ci struct dpaa2_eth_swa *swa; 281462306a36Sopenharmony_ci void *buffer_start, *aligned_start; 281562306a36Sopenharmony_ci dma_addr_t addr; 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_ci /* We require a minimum headroom to be able to transmit the frame. 281862306a36Sopenharmony_ci * Otherwise return an error and let the original net_device handle it 281962306a36Sopenharmony_ci */ 282062306a36Sopenharmony_ci needed_headroom = dpaa2_eth_needed_headroom(NULL); 282162306a36Sopenharmony_ci if (xdpf->headroom < needed_headroom) 282262306a36Sopenharmony_ci return -EINVAL; 282362306a36Sopenharmony_ci 282462306a36Sopenharmony_ci /* Setup the FD fields */ 282562306a36Sopenharmony_ci memset(fd, 0, sizeof(*fd)); 282662306a36Sopenharmony_ci 282762306a36Sopenharmony_ci /* Align FD address, if possible */ 282862306a36Sopenharmony_ci buffer_start = xdpf->data - needed_headroom; 282962306a36Sopenharmony_ci aligned_start = PTR_ALIGN(buffer_start - DPAA2_ETH_TX_BUF_ALIGN, 283062306a36Sopenharmony_ci DPAA2_ETH_TX_BUF_ALIGN); 283162306a36Sopenharmony_ci if (aligned_start >= xdpf->data - xdpf->headroom) 283262306a36Sopenharmony_ci buffer_start = aligned_start; 283362306a36Sopenharmony_ci 283462306a36Sopenharmony_ci swa = (struct dpaa2_eth_swa *)buffer_start; 283562306a36Sopenharmony_ci /* fill in necessary fields here */ 283662306a36Sopenharmony_ci swa->type = DPAA2_ETH_SWA_XDP; 283762306a36Sopenharmony_ci swa->xdp.dma_size = xdpf->data + xdpf->len - buffer_start; 283862306a36Sopenharmony_ci swa->xdp.xdpf = xdpf; 283962306a36Sopenharmony_ci 284062306a36Sopenharmony_ci addr = dma_map_single(dev, buffer_start, 284162306a36Sopenharmony_ci swa->xdp.dma_size, 284262306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 284362306a36Sopenharmony_ci if (unlikely(dma_mapping_error(dev, addr))) 284462306a36Sopenharmony_ci return -ENOMEM; 284562306a36Sopenharmony_ci 284662306a36Sopenharmony_ci dpaa2_fd_set_addr(fd, addr); 284762306a36Sopenharmony_ci dpaa2_fd_set_offset(fd, xdpf->data - buffer_start); 284862306a36Sopenharmony_ci dpaa2_fd_set_len(fd, xdpf->len); 284962306a36Sopenharmony_ci dpaa2_fd_set_format(fd, dpaa2_fd_single); 285062306a36Sopenharmony_ci dpaa2_fd_set_ctrl(fd, FD_CTRL_PTA); 285162306a36Sopenharmony_ci 285262306a36Sopenharmony_ci return 0; 285362306a36Sopenharmony_ci} 285462306a36Sopenharmony_ci 285562306a36Sopenharmony_cistatic int dpaa2_eth_xdp_xmit(struct net_device *net_dev, int n, 285662306a36Sopenharmony_ci struct xdp_frame **frames, u32 flags) 285762306a36Sopenharmony_ci{ 285862306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 285962306a36Sopenharmony_ci struct dpaa2_eth_xdp_fds *xdp_redirect_fds; 286062306a36Sopenharmony_ci struct rtnl_link_stats64 *percpu_stats; 286162306a36Sopenharmony_ci struct dpaa2_eth_fq *fq; 286262306a36Sopenharmony_ci struct dpaa2_fd *fds; 286362306a36Sopenharmony_ci int enqueued, i, err; 286462306a36Sopenharmony_ci 286562306a36Sopenharmony_ci if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) 286662306a36Sopenharmony_ci return -EINVAL; 286762306a36Sopenharmony_ci 286862306a36Sopenharmony_ci if (!netif_running(net_dev)) 286962306a36Sopenharmony_ci return -ENETDOWN; 287062306a36Sopenharmony_ci 287162306a36Sopenharmony_ci fq = &priv->fq[smp_processor_id()]; 287262306a36Sopenharmony_ci xdp_redirect_fds = &fq->xdp_redirect_fds; 287362306a36Sopenharmony_ci fds = xdp_redirect_fds->fds; 287462306a36Sopenharmony_ci 287562306a36Sopenharmony_ci percpu_stats = this_cpu_ptr(priv->percpu_stats); 287662306a36Sopenharmony_ci 287762306a36Sopenharmony_ci /* create a FD for each xdp_frame in the list received */ 287862306a36Sopenharmony_ci for (i = 0; i < n; i++) { 287962306a36Sopenharmony_ci err = dpaa2_eth_xdp_create_fd(net_dev, frames[i], &fds[i]); 288062306a36Sopenharmony_ci if (err) 288162306a36Sopenharmony_ci break; 288262306a36Sopenharmony_ci } 288362306a36Sopenharmony_ci xdp_redirect_fds->num = i; 288462306a36Sopenharmony_ci 288562306a36Sopenharmony_ci /* enqueue all the frame descriptors */ 288662306a36Sopenharmony_ci enqueued = dpaa2_eth_xdp_flush(priv, fq, xdp_redirect_fds); 288762306a36Sopenharmony_ci 288862306a36Sopenharmony_ci /* update statistics */ 288962306a36Sopenharmony_ci percpu_stats->tx_packets += enqueued; 289062306a36Sopenharmony_ci for (i = 0; i < enqueued; i++) 289162306a36Sopenharmony_ci percpu_stats->tx_bytes += dpaa2_fd_get_len(&fds[i]); 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ci return enqueued; 289462306a36Sopenharmony_ci} 289562306a36Sopenharmony_ci 289662306a36Sopenharmony_cistatic int update_xps(struct dpaa2_eth_priv *priv) 289762306a36Sopenharmony_ci{ 289862306a36Sopenharmony_ci struct net_device *net_dev = priv->net_dev; 289962306a36Sopenharmony_ci struct cpumask xps_mask; 290062306a36Sopenharmony_ci struct dpaa2_eth_fq *fq; 290162306a36Sopenharmony_ci int i, num_queues, netdev_queues; 290262306a36Sopenharmony_ci int err = 0; 290362306a36Sopenharmony_ci 290462306a36Sopenharmony_ci num_queues = dpaa2_eth_queue_count(priv); 290562306a36Sopenharmony_ci netdev_queues = (net_dev->num_tc ? : 1) * num_queues; 290662306a36Sopenharmony_ci 290762306a36Sopenharmony_ci /* The first <num_queues> entries in priv->fq array are Tx/Tx conf 290862306a36Sopenharmony_ci * queues, so only process those 290962306a36Sopenharmony_ci */ 291062306a36Sopenharmony_ci for (i = 0; i < netdev_queues; i++) { 291162306a36Sopenharmony_ci fq = &priv->fq[i % num_queues]; 291262306a36Sopenharmony_ci 291362306a36Sopenharmony_ci cpumask_clear(&xps_mask); 291462306a36Sopenharmony_ci cpumask_set_cpu(fq->target_cpu, &xps_mask); 291562306a36Sopenharmony_ci 291662306a36Sopenharmony_ci err = netif_set_xps_queue(net_dev, &xps_mask, i); 291762306a36Sopenharmony_ci if (err) { 291862306a36Sopenharmony_ci netdev_warn_once(net_dev, "Error setting XPS queue\n"); 291962306a36Sopenharmony_ci break; 292062306a36Sopenharmony_ci } 292162306a36Sopenharmony_ci } 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_ci return err; 292462306a36Sopenharmony_ci} 292562306a36Sopenharmony_ci 292662306a36Sopenharmony_cistatic int dpaa2_eth_setup_mqprio(struct net_device *net_dev, 292762306a36Sopenharmony_ci struct tc_mqprio_qopt *mqprio) 292862306a36Sopenharmony_ci{ 292962306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 293062306a36Sopenharmony_ci u8 num_tc, num_queues; 293162306a36Sopenharmony_ci int i; 293262306a36Sopenharmony_ci 293362306a36Sopenharmony_ci mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; 293462306a36Sopenharmony_ci num_queues = dpaa2_eth_queue_count(priv); 293562306a36Sopenharmony_ci num_tc = mqprio->num_tc; 293662306a36Sopenharmony_ci 293762306a36Sopenharmony_ci if (num_tc == net_dev->num_tc) 293862306a36Sopenharmony_ci return 0; 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_ci if (num_tc > dpaa2_eth_tc_count(priv)) { 294162306a36Sopenharmony_ci netdev_err(net_dev, "Max %d traffic classes supported\n", 294262306a36Sopenharmony_ci dpaa2_eth_tc_count(priv)); 294362306a36Sopenharmony_ci return -EOPNOTSUPP; 294462306a36Sopenharmony_ci } 294562306a36Sopenharmony_ci 294662306a36Sopenharmony_ci if (!num_tc) { 294762306a36Sopenharmony_ci netdev_reset_tc(net_dev); 294862306a36Sopenharmony_ci netif_set_real_num_tx_queues(net_dev, num_queues); 294962306a36Sopenharmony_ci goto out; 295062306a36Sopenharmony_ci } 295162306a36Sopenharmony_ci 295262306a36Sopenharmony_ci netdev_set_num_tc(net_dev, num_tc); 295362306a36Sopenharmony_ci netif_set_real_num_tx_queues(net_dev, num_tc * num_queues); 295462306a36Sopenharmony_ci 295562306a36Sopenharmony_ci for (i = 0; i < num_tc; i++) 295662306a36Sopenharmony_ci netdev_set_tc_queue(net_dev, i, num_queues, i * num_queues); 295762306a36Sopenharmony_ci 295862306a36Sopenharmony_ciout: 295962306a36Sopenharmony_ci update_xps(priv); 296062306a36Sopenharmony_ci 296162306a36Sopenharmony_ci return 0; 296262306a36Sopenharmony_ci} 296362306a36Sopenharmony_ci 296462306a36Sopenharmony_ci#define bps_to_mbits(rate) (div_u64((rate), 1000000) * 8) 296562306a36Sopenharmony_ci 296662306a36Sopenharmony_cistatic int dpaa2_eth_setup_tbf(struct net_device *net_dev, struct tc_tbf_qopt_offload *p) 296762306a36Sopenharmony_ci{ 296862306a36Sopenharmony_ci struct tc_tbf_qopt_offload_replace_params *cfg = &p->replace_params; 296962306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 297062306a36Sopenharmony_ci struct dpni_tx_shaping_cfg tx_cr_shaper = { 0 }; 297162306a36Sopenharmony_ci struct dpni_tx_shaping_cfg tx_er_shaper = { 0 }; 297262306a36Sopenharmony_ci int err; 297362306a36Sopenharmony_ci 297462306a36Sopenharmony_ci if (p->command == TC_TBF_STATS) 297562306a36Sopenharmony_ci return -EOPNOTSUPP; 297662306a36Sopenharmony_ci 297762306a36Sopenharmony_ci /* Only per port Tx shaping */ 297862306a36Sopenharmony_ci if (p->parent != TC_H_ROOT) 297962306a36Sopenharmony_ci return -EOPNOTSUPP; 298062306a36Sopenharmony_ci 298162306a36Sopenharmony_ci if (p->command == TC_TBF_REPLACE) { 298262306a36Sopenharmony_ci if (cfg->max_size > DPAA2_ETH_MAX_BURST_SIZE) { 298362306a36Sopenharmony_ci netdev_err(net_dev, "burst size cannot be greater than %d\n", 298462306a36Sopenharmony_ci DPAA2_ETH_MAX_BURST_SIZE); 298562306a36Sopenharmony_ci return -EINVAL; 298662306a36Sopenharmony_ci } 298762306a36Sopenharmony_ci 298862306a36Sopenharmony_ci tx_cr_shaper.max_burst_size = cfg->max_size; 298962306a36Sopenharmony_ci /* The TBF interface is in bytes/s, whereas DPAA2 expects the 299062306a36Sopenharmony_ci * rate in Mbits/s 299162306a36Sopenharmony_ci */ 299262306a36Sopenharmony_ci tx_cr_shaper.rate_limit = bps_to_mbits(cfg->rate.rate_bytes_ps); 299362306a36Sopenharmony_ci } 299462306a36Sopenharmony_ci 299562306a36Sopenharmony_ci err = dpni_set_tx_shaping(priv->mc_io, 0, priv->mc_token, &tx_cr_shaper, 299662306a36Sopenharmony_ci &tx_er_shaper, 0); 299762306a36Sopenharmony_ci if (err) { 299862306a36Sopenharmony_ci netdev_err(net_dev, "dpni_set_tx_shaping() = %d\n", err); 299962306a36Sopenharmony_ci return err; 300062306a36Sopenharmony_ci } 300162306a36Sopenharmony_ci 300262306a36Sopenharmony_ci return 0; 300362306a36Sopenharmony_ci} 300462306a36Sopenharmony_ci 300562306a36Sopenharmony_cistatic int dpaa2_eth_setup_tc(struct net_device *net_dev, 300662306a36Sopenharmony_ci enum tc_setup_type type, void *type_data) 300762306a36Sopenharmony_ci{ 300862306a36Sopenharmony_ci switch (type) { 300962306a36Sopenharmony_ci case TC_SETUP_QDISC_MQPRIO: 301062306a36Sopenharmony_ci return dpaa2_eth_setup_mqprio(net_dev, type_data); 301162306a36Sopenharmony_ci case TC_SETUP_QDISC_TBF: 301262306a36Sopenharmony_ci return dpaa2_eth_setup_tbf(net_dev, type_data); 301362306a36Sopenharmony_ci default: 301462306a36Sopenharmony_ci return -EOPNOTSUPP; 301562306a36Sopenharmony_ci } 301662306a36Sopenharmony_ci} 301762306a36Sopenharmony_ci 301862306a36Sopenharmony_cistatic const struct net_device_ops dpaa2_eth_ops = { 301962306a36Sopenharmony_ci .ndo_open = dpaa2_eth_open, 302062306a36Sopenharmony_ci .ndo_start_xmit = dpaa2_eth_tx, 302162306a36Sopenharmony_ci .ndo_stop = dpaa2_eth_stop, 302262306a36Sopenharmony_ci .ndo_set_mac_address = dpaa2_eth_set_addr, 302362306a36Sopenharmony_ci .ndo_get_stats64 = dpaa2_eth_get_stats, 302462306a36Sopenharmony_ci .ndo_set_rx_mode = dpaa2_eth_set_rx_mode, 302562306a36Sopenharmony_ci .ndo_set_features = dpaa2_eth_set_features, 302662306a36Sopenharmony_ci .ndo_eth_ioctl = dpaa2_eth_ioctl, 302762306a36Sopenharmony_ci .ndo_change_mtu = dpaa2_eth_change_mtu, 302862306a36Sopenharmony_ci .ndo_bpf = dpaa2_eth_xdp, 302962306a36Sopenharmony_ci .ndo_xdp_xmit = dpaa2_eth_xdp_xmit, 303062306a36Sopenharmony_ci .ndo_xsk_wakeup = dpaa2_xsk_wakeup, 303162306a36Sopenharmony_ci .ndo_setup_tc = dpaa2_eth_setup_tc, 303262306a36Sopenharmony_ci .ndo_vlan_rx_add_vid = dpaa2_eth_rx_add_vid, 303362306a36Sopenharmony_ci .ndo_vlan_rx_kill_vid = dpaa2_eth_rx_kill_vid 303462306a36Sopenharmony_ci}; 303562306a36Sopenharmony_ci 303662306a36Sopenharmony_cistatic void dpaa2_eth_cdan_cb(struct dpaa2_io_notification_ctx *ctx) 303762306a36Sopenharmony_ci{ 303862306a36Sopenharmony_ci struct dpaa2_eth_channel *ch; 303962306a36Sopenharmony_ci 304062306a36Sopenharmony_ci ch = container_of(ctx, struct dpaa2_eth_channel, nctx); 304162306a36Sopenharmony_ci 304262306a36Sopenharmony_ci /* Update NAPI statistics */ 304362306a36Sopenharmony_ci ch->stats.cdan++; 304462306a36Sopenharmony_ci 304562306a36Sopenharmony_ci /* NAPI can also be scheduled from the AF_XDP Tx path. Mark a missed 304662306a36Sopenharmony_ci * so that it can be rescheduled again. 304762306a36Sopenharmony_ci */ 304862306a36Sopenharmony_ci if (!napi_if_scheduled_mark_missed(&ch->napi)) 304962306a36Sopenharmony_ci napi_schedule(&ch->napi); 305062306a36Sopenharmony_ci} 305162306a36Sopenharmony_ci 305262306a36Sopenharmony_ci/* Allocate and configure a DPCON object */ 305362306a36Sopenharmony_cistatic struct fsl_mc_device *dpaa2_eth_setup_dpcon(struct dpaa2_eth_priv *priv) 305462306a36Sopenharmony_ci{ 305562306a36Sopenharmony_ci struct fsl_mc_device *dpcon; 305662306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 305762306a36Sopenharmony_ci int err; 305862306a36Sopenharmony_ci 305962306a36Sopenharmony_ci err = fsl_mc_object_allocate(to_fsl_mc_device(dev), 306062306a36Sopenharmony_ci FSL_MC_POOL_DPCON, &dpcon); 306162306a36Sopenharmony_ci if (err) { 306262306a36Sopenharmony_ci if (err == -ENXIO) { 306362306a36Sopenharmony_ci dev_dbg(dev, "Waiting for DPCON\n"); 306462306a36Sopenharmony_ci err = -EPROBE_DEFER; 306562306a36Sopenharmony_ci } else { 306662306a36Sopenharmony_ci dev_info(dev, "Not enough DPCONs, will go on as-is\n"); 306762306a36Sopenharmony_ci } 306862306a36Sopenharmony_ci return ERR_PTR(err); 306962306a36Sopenharmony_ci } 307062306a36Sopenharmony_ci 307162306a36Sopenharmony_ci err = dpcon_open(priv->mc_io, 0, dpcon->obj_desc.id, &dpcon->mc_handle); 307262306a36Sopenharmony_ci if (err) { 307362306a36Sopenharmony_ci dev_err(dev, "dpcon_open() failed\n"); 307462306a36Sopenharmony_ci goto free; 307562306a36Sopenharmony_ci } 307662306a36Sopenharmony_ci 307762306a36Sopenharmony_ci err = dpcon_reset(priv->mc_io, 0, dpcon->mc_handle); 307862306a36Sopenharmony_ci if (err) { 307962306a36Sopenharmony_ci dev_err(dev, "dpcon_reset() failed\n"); 308062306a36Sopenharmony_ci goto close; 308162306a36Sopenharmony_ci } 308262306a36Sopenharmony_ci 308362306a36Sopenharmony_ci err = dpcon_enable(priv->mc_io, 0, dpcon->mc_handle); 308462306a36Sopenharmony_ci if (err) { 308562306a36Sopenharmony_ci dev_err(dev, "dpcon_enable() failed\n"); 308662306a36Sopenharmony_ci goto close; 308762306a36Sopenharmony_ci } 308862306a36Sopenharmony_ci 308962306a36Sopenharmony_ci return dpcon; 309062306a36Sopenharmony_ci 309162306a36Sopenharmony_ciclose: 309262306a36Sopenharmony_ci dpcon_close(priv->mc_io, 0, dpcon->mc_handle); 309362306a36Sopenharmony_cifree: 309462306a36Sopenharmony_ci fsl_mc_object_free(dpcon); 309562306a36Sopenharmony_ci 309662306a36Sopenharmony_ci return ERR_PTR(err); 309762306a36Sopenharmony_ci} 309862306a36Sopenharmony_ci 309962306a36Sopenharmony_cistatic void dpaa2_eth_free_dpcon(struct dpaa2_eth_priv *priv, 310062306a36Sopenharmony_ci struct fsl_mc_device *dpcon) 310162306a36Sopenharmony_ci{ 310262306a36Sopenharmony_ci dpcon_disable(priv->mc_io, 0, dpcon->mc_handle); 310362306a36Sopenharmony_ci dpcon_close(priv->mc_io, 0, dpcon->mc_handle); 310462306a36Sopenharmony_ci fsl_mc_object_free(dpcon); 310562306a36Sopenharmony_ci} 310662306a36Sopenharmony_ci 310762306a36Sopenharmony_cistatic struct dpaa2_eth_channel *dpaa2_eth_alloc_channel(struct dpaa2_eth_priv *priv) 310862306a36Sopenharmony_ci{ 310962306a36Sopenharmony_ci struct dpaa2_eth_channel *channel; 311062306a36Sopenharmony_ci struct dpcon_attr attr; 311162306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 311262306a36Sopenharmony_ci int err; 311362306a36Sopenharmony_ci 311462306a36Sopenharmony_ci channel = kzalloc(sizeof(*channel), GFP_KERNEL); 311562306a36Sopenharmony_ci if (!channel) 311662306a36Sopenharmony_ci return NULL; 311762306a36Sopenharmony_ci 311862306a36Sopenharmony_ci channel->dpcon = dpaa2_eth_setup_dpcon(priv); 311962306a36Sopenharmony_ci if (IS_ERR(channel->dpcon)) { 312062306a36Sopenharmony_ci err = PTR_ERR(channel->dpcon); 312162306a36Sopenharmony_ci goto err_setup; 312262306a36Sopenharmony_ci } 312362306a36Sopenharmony_ci 312462306a36Sopenharmony_ci err = dpcon_get_attributes(priv->mc_io, 0, channel->dpcon->mc_handle, 312562306a36Sopenharmony_ci &attr); 312662306a36Sopenharmony_ci if (err) { 312762306a36Sopenharmony_ci dev_err(dev, "dpcon_get_attributes() failed\n"); 312862306a36Sopenharmony_ci goto err_get_attr; 312962306a36Sopenharmony_ci } 313062306a36Sopenharmony_ci 313162306a36Sopenharmony_ci channel->dpcon_id = attr.id; 313262306a36Sopenharmony_ci channel->ch_id = attr.qbman_ch_id; 313362306a36Sopenharmony_ci channel->priv = priv; 313462306a36Sopenharmony_ci 313562306a36Sopenharmony_ci return channel; 313662306a36Sopenharmony_ci 313762306a36Sopenharmony_cierr_get_attr: 313862306a36Sopenharmony_ci dpaa2_eth_free_dpcon(priv, channel->dpcon); 313962306a36Sopenharmony_cierr_setup: 314062306a36Sopenharmony_ci kfree(channel); 314162306a36Sopenharmony_ci return ERR_PTR(err); 314262306a36Sopenharmony_ci} 314362306a36Sopenharmony_ci 314462306a36Sopenharmony_cistatic void dpaa2_eth_free_channel(struct dpaa2_eth_priv *priv, 314562306a36Sopenharmony_ci struct dpaa2_eth_channel *channel) 314662306a36Sopenharmony_ci{ 314762306a36Sopenharmony_ci dpaa2_eth_free_dpcon(priv, channel->dpcon); 314862306a36Sopenharmony_ci kfree(channel); 314962306a36Sopenharmony_ci} 315062306a36Sopenharmony_ci 315162306a36Sopenharmony_ci/* DPIO setup: allocate and configure QBMan channels, setup core affinity 315262306a36Sopenharmony_ci * and register data availability notifications 315362306a36Sopenharmony_ci */ 315462306a36Sopenharmony_cistatic int dpaa2_eth_setup_dpio(struct dpaa2_eth_priv *priv) 315562306a36Sopenharmony_ci{ 315662306a36Sopenharmony_ci struct dpaa2_io_notification_ctx *nctx; 315762306a36Sopenharmony_ci struct dpaa2_eth_channel *channel; 315862306a36Sopenharmony_ci struct dpcon_notification_cfg dpcon_notif_cfg; 315962306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 316062306a36Sopenharmony_ci int i, err; 316162306a36Sopenharmony_ci 316262306a36Sopenharmony_ci /* We want the ability to spread ingress traffic (RX, TX conf) to as 316362306a36Sopenharmony_ci * many cores as possible, so we need one channel for each core 316462306a36Sopenharmony_ci * (unless there's fewer queues than cores, in which case the extra 316562306a36Sopenharmony_ci * channels would be wasted). 316662306a36Sopenharmony_ci * Allocate one channel per core and register it to the core's 316762306a36Sopenharmony_ci * affine DPIO. If not enough channels are available for all cores 316862306a36Sopenharmony_ci * or if some cores don't have an affine DPIO, there will be no 316962306a36Sopenharmony_ci * ingress frame processing on those cores. 317062306a36Sopenharmony_ci */ 317162306a36Sopenharmony_ci cpumask_clear(&priv->dpio_cpumask); 317262306a36Sopenharmony_ci for_each_online_cpu(i) { 317362306a36Sopenharmony_ci /* Try to allocate a channel */ 317462306a36Sopenharmony_ci channel = dpaa2_eth_alloc_channel(priv); 317562306a36Sopenharmony_ci if (IS_ERR_OR_NULL(channel)) { 317662306a36Sopenharmony_ci err = PTR_ERR_OR_ZERO(channel); 317762306a36Sopenharmony_ci if (err == -EPROBE_DEFER) 317862306a36Sopenharmony_ci dev_dbg(dev, "waiting for affine channel\n"); 317962306a36Sopenharmony_ci else 318062306a36Sopenharmony_ci dev_info(dev, 318162306a36Sopenharmony_ci "No affine channel for cpu %d and above\n", i); 318262306a36Sopenharmony_ci goto err_alloc_ch; 318362306a36Sopenharmony_ci } 318462306a36Sopenharmony_ci 318562306a36Sopenharmony_ci priv->channel[priv->num_channels] = channel; 318662306a36Sopenharmony_ci 318762306a36Sopenharmony_ci nctx = &channel->nctx; 318862306a36Sopenharmony_ci nctx->is_cdan = 1; 318962306a36Sopenharmony_ci nctx->cb = dpaa2_eth_cdan_cb; 319062306a36Sopenharmony_ci nctx->id = channel->ch_id; 319162306a36Sopenharmony_ci nctx->desired_cpu = i; 319262306a36Sopenharmony_ci 319362306a36Sopenharmony_ci /* Register the new context */ 319462306a36Sopenharmony_ci channel->dpio = dpaa2_io_service_select(i); 319562306a36Sopenharmony_ci err = dpaa2_io_service_register(channel->dpio, nctx, dev); 319662306a36Sopenharmony_ci if (err) { 319762306a36Sopenharmony_ci dev_dbg(dev, "No affine DPIO for cpu %d\n", i); 319862306a36Sopenharmony_ci /* If no affine DPIO for this core, there's probably 319962306a36Sopenharmony_ci * none available for next cores either. Signal we want 320062306a36Sopenharmony_ci * to retry later, in case the DPIO devices weren't 320162306a36Sopenharmony_ci * probed yet. 320262306a36Sopenharmony_ci */ 320362306a36Sopenharmony_ci err = -EPROBE_DEFER; 320462306a36Sopenharmony_ci goto err_service_reg; 320562306a36Sopenharmony_ci } 320662306a36Sopenharmony_ci 320762306a36Sopenharmony_ci /* Register DPCON notification with MC */ 320862306a36Sopenharmony_ci dpcon_notif_cfg.dpio_id = nctx->dpio_id; 320962306a36Sopenharmony_ci dpcon_notif_cfg.priority = 0; 321062306a36Sopenharmony_ci dpcon_notif_cfg.user_ctx = nctx->qman64; 321162306a36Sopenharmony_ci err = dpcon_set_notification(priv->mc_io, 0, 321262306a36Sopenharmony_ci channel->dpcon->mc_handle, 321362306a36Sopenharmony_ci &dpcon_notif_cfg); 321462306a36Sopenharmony_ci if (err) { 321562306a36Sopenharmony_ci dev_err(dev, "dpcon_set_notification failed()\n"); 321662306a36Sopenharmony_ci goto err_set_cdan; 321762306a36Sopenharmony_ci } 321862306a36Sopenharmony_ci 321962306a36Sopenharmony_ci /* If we managed to allocate a channel and also found an affine 322062306a36Sopenharmony_ci * DPIO for this core, add it to the final mask 322162306a36Sopenharmony_ci */ 322262306a36Sopenharmony_ci cpumask_set_cpu(i, &priv->dpio_cpumask); 322362306a36Sopenharmony_ci priv->num_channels++; 322462306a36Sopenharmony_ci 322562306a36Sopenharmony_ci /* Stop if we already have enough channels to accommodate all 322662306a36Sopenharmony_ci * RX and TX conf queues 322762306a36Sopenharmony_ci */ 322862306a36Sopenharmony_ci if (priv->num_channels == priv->dpni_attrs.num_queues) 322962306a36Sopenharmony_ci break; 323062306a36Sopenharmony_ci } 323162306a36Sopenharmony_ci 323262306a36Sopenharmony_ci return 0; 323362306a36Sopenharmony_ci 323462306a36Sopenharmony_cierr_set_cdan: 323562306a36Sopenharmony_ci dpaa2_io_service_deregister(channel->dpio, nctx, dev); 323662306a36Sopenharmony_cierr_service_reg: 323762306a36Sopenharmony_ci dpaa2_eth_free_channel(priv, channel); 323862306a36Sopenharmony_cierr_alloc_ch: 323962306a36Sopenharmony_ci if (err == -EPROBE_DEFER) { 324062306a36Sopenharmony_ci for (i = 0; i < priv->num_channels; i++) { 324162306a36Sopenharmony_ci channel = priv->channel[i]; 324262306a36Sopenharmony_ci nctx = &channel->nctx; 324362306a36Sopenharmony_ci dpaa2_io_service_deregister(channel->dpio, nctx, dev); 324462306a36Sopenharmony_ci dpaa2_eth_free_channel(priv, channel); 324562306a36Sopenharmony_ci } 324662306a36Sopenharmony_ci priv->num_channels = 0; 324762306a36Sopenharmony_ci return err; 324862306a36Sopenharmony_ci } 324962306a36Sopenharmony_ci 325062306a36Sopenharmony_ci if (cpumask_empty(&priv->dpio_cpumask)) { 325162306a36Sopenharmony_ci dev_err(dev, "No cpu with an affine DPIO/DPCON\n"); 325262306a36Sopenharmony_ci return -ENODEV; 325362306a36Sopenharmony_ci } 325462306a36Sopenharmony_ci 325562306a36Sopenharmony_ci dev_info(dev, "Cores %*pbl available for processing ingress traffic\n", 325662306a36Sopenharmony_ci cpumask_pr_args(&priv->dpio_cpumask)); 325762306a36Sopenharmony_ci 325862306a36Sopenharmony_ci return 0; 325962306a36Sopenharmony_ci} 326062306a36Sopenharmony_ci 326162306a36Sopenharmony_cistatic void dpaa2_eth_free_dpio(struct dpaa2_eth_priv *priv) 326262306a36Sopenharmony_ci{ 326362306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 326462306a36Sopenharmony_ci struct dpaa2_eth_channel *ch; 326562306a36Sopenharmony_ci int i; 326662306a36Sopenharmony_ci 326762306a36Sopenharmony_ci /* deregister CDAN notifications and free channels */ 326862306a36Sopenharmony_ci for (i = 0; i < priv->num_channels; i++) { 326962306a36Sopenharmony_ci ch = priv->channel[i]; 327062306a36Sopenharmony_ci dpaa2_io_service_deregister(ch->dpio, &ch->nctx, dev); 327162306a36Sopenharmony_ci dpaa2_eth_free_channel(priv, ch); 327262306a36Sopenharmony_ci } 327362306a36Sopenharmony_ci} 327462306a36Sopenharmony_ci 327562306a36Sopenharmony_cistatic struct dpaa2_eth_channel *dpaa2_eth_get_affine_channel(struct dpaa2_eth_priv *priv, 327662306a36Sopenharmony_ci int cpu) 327762306a36Sopenharmony_ci{ 327862306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 327962306a36Sopenharmony_ci int i; 328062306a36Sopenharmony_ci 328162306a36Sopenharmony_ci for (i = 0; i < priv->num_channels; i++) 328262306a36Sopenharmony_ci if (priv->channel[i]->nctx.desired_cpu == cpu) 328362306a36Sopenharmony_ci return priv->channel[i]; 328462306a36Sopenharmony_ci 328562306a36Sopenharmony_ci /* We should never get here. Issue a warning and return 328662306a36Sopenharmony_ci * the first channel, because it's still better than nothing 328762306a36Sopenharmony_ci */ 328862306a36Sopenharmony_ci dev_warn(dev, "No affine channel found for cpu %d\n", cpu); 328962306a36Sopenharmony_ci 329062306a36Sopenharmony_ci return priv->channel[0]; 329162306a36Sopenharmony_ci} 329262306a36Sopenharmony_ci 329362306a36Sopenharmony_cistatic void dpaa2_eth_set_fq_affinity(struct dpaa2_eth_priv *priv) 329462306a36Sopenharmony_ci{ 329562306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 329662306a36Sopenharmony_ci struct dpaa2_eth_fq *fq; 329762306a36Sopenharmony_ci int rx_cpu, txc_cpu; 329862306a36Sopenharmony_ci int i; 329962306a36Sopenharmony_ci 330062306a36Sopenharmony_ci /* For each FQ, pick one channel/CPU to deliver frames to. 330162306a36Sopenharmony_ci * This may well change at runtime, either through irqbalance or 330262306a36Sopenharmony_ci * through direct user intervention. 330362306a36Sopenharmony_ci */ 330462306a36Sopenharmony_ci rx_cpu = txc_cpu = cpumask_first(&priv->dpio_cpumask); 330562306a36Sopenharmony_ci 330662306a36Sopenharmony_ci for (i = 0; i < priv->num_fqs; i++) { 330762306a36Sopenharmony_ci fq = &priv->fq[i]; 330862306a36Sopenharmony_ci switch (fq->type) { 330962306a36Sopenharmony_ci case DPAA2_RX_FQ: 331062306a36Sopenharmony_ci case DPAA2_RX_ERR_FQ: 331162306a36Sopenharmony_ci fq->target_cpu = rx_cpu; 331262306a36Sopenharmony_ci rx_cpu = cpumask_next(rx_cpu, &priv->dpio_cpumask); 331362306a36Sopenharmony_ci if (rx_cpu >= nr_cpu_ids) 331462306a36Sopenharmony_ci rx_cpu = cpumask_first(&priv->dpio_cpumask); 331562306a36Sopenharmony_ci break; 331662306a36Sopenharmony_ci case DPAA2_TX_CONF_FQ: 331762306a36Sopenharmony_ci fq->target_cpu = txc_cpu; 331862306a36Sopenharmony_ci txc_cpu = cpumask_next(txc_cpu, &priv->dpio_cpumask); 331962306a36Sopenharmony_ci if (txc_cpu >= nr_cpu_ids) 332062306a36Sopenharmony_ci txc_cpu = cpumask_first(&priv->dpio_cpumask); 332162306a36Sopenharmony_ci break; 332262306a36Sopenharmony_ci default: 332362306a36Sopenharmony_ci dev_err(dev, "Unknown FQ type: %d\n", fq->type); 332462306a36Sopenharmony_ci } 332562306a36Sopenharmony_ci fq->channel = dpaa2_eth_get_affine_channel(priv, fq->target_cpu); 332662306a36Sopenharmony_ci } 332762306a36Sopenharmony_ci 332862306a36Sopenharmony_ci update_xps(priv); 332962306a36Sopenharmony_ci} 333062306a36Sopenharmony_ci 333162306a36Sopenharmony_cistatic void dpaa2_eth_setup_fqs(struct dpaa2_eth_priv *priv) 333262306a36Sopenharmony_ci{ 333362306a36Sopenharmony_ci int i, j; 333462306a36Sopenharmony_ci 333562306a36Sopenharmony_ci /* We have one TxConf FQ per Tx flow. 333662306a36Sopenharmony_ci * The number of Tx and Rx queues is the same. 333762306a36Sopenharmony_ci * Tx queues come first in the fq array. 333862306a36Sopenharmony_ci */ 333962306a36Sopenharmony_ci for (i = 0; i < dpaa2_eth_queue_count(priv); i++) { 334062306a36Sopenharmony_ci priv->fq[priv->num_fqs].type = DPAA2_TX_CONF_FQ; 334162306a36Sopenharmony_ci priv->fq[priv->num_fqs].consume = dpaa2_eth_tx_conf; 334262306a36Sopenharmony_ci priv->fq[priv->num_fqs++].flowid = (u16)i; 334362306a36Sopenharmony_ci } 334462306a36Sopenharmony_ci 334562306a36Sopenharmony_ci for (j = 0; j < dpaa2_eth_tc_count(priv); j++) { 334662306a36Sopenharmony_ci for (i = 0; i < dpaa2_eth_queue_count(priv); i++) { 334762306a36Sopenharmony_ci priv->fq[priv->num_fqs].type = DPAA2_RX_FQ; 334862306a36Sopenharmony_ci priv->fq[priv->num_fqs].consume = dpaa2_eth_rx; 334962306a36Sopenharmony_ci priv->fq[priv->num_fqs].tc = (u8)j; 335062306a36Sopenharmony_ci priv->fq[priv->num_fqs++].flowid = (u16)i; 335162306a36Sopenharmony_ci } 335262306a36Sopenharmony_ci } 335362306a36Sopenharmony_ci 335462306a36Sopenharmony_ci /* We have exactly one Rx error queue per DPNI */ 335562306a36Sopenharmony_ci priv->fq[priv->num_fqs].type = DPAA2_RX_ERR_FQ; 335662306a36Sopenharmony_ci priv->fq[priv->num_fqs++].consume = dpaa2_eth_rx_err; 335762306a36Sopenharmony_ci 335862306a36Sopenharmony_ci /* For each FQ, decide on which core to process incoming frames */ 335962306a36Sopenharmony_ci dpaa2_eth_set_fq_affinity(priv); 336062306a36Sopenharmony_ci} 336162306a36Sopenharmony_ci 336262306a36Sopenharmony_ci/* Allocate and configure a buffer pool */ 336362306a36Sopenharmony_cistruct dpaa2_eth_bp *dpaa2_eth_allocate_dpbp(struct dpaa2_eth_priv *priv) 336462306a36Sopenharmony_ci{ 336562306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 336662306a36Sopenharmony_ci struct fsl_mc_device *dpbp_dev; 336762306a36Sopenharmony_ci struct dpbp_attr dpbp_attrs; 336862306a36Sopenharmony_ci struct dpaa2_eth_bp *bp; 336962306a36Sopenharmony_ci int err; 337062306a36Sopenharmony_ci 337162306a36Sopenharmony_ci err = fsl_mc_object_allocate(to_fsl_mc_device(dev), FSL_MC_POOL_DPBP, 337262306a36Sopenharmony_ci &dpbp_dev); 337362306a36Sopenharmony_ci if (err) { 337462306a36Sopenharmony_ci if (err == -ENXIO) 337562306a36Sopenharmony_ci err = -EPROBE_DEFER; 337662306a36Sopenharmony_ci else 337762306a36Sopenharmony_ci dev_err(dev, "DPBP device allocation failed\n"); 337862306a36Sopenharmony_ci return ERR_PTR(err); 337962306a36Sopenharmony_ci } 338062306a36Sopenharmony_ci 338162306a36Sopenharmony_ci bp = kzalloc(sizeof(*bp), GFP_KERNEL); 338262306a36Sopenharmony_ci if (!bp) { 338362306a36Sopenharmony_ci err = -ENOMEM; 338462306a36Sopenharmony_ci goto err_alloc; 338562306a36Sopenharmony_ci } 338662306a36Sopenharmony_ci 338762306a36Sopenharmony_ci err = dpbp_open(priv->mc_io, 0, dpbp_dev->obj_desc.id, 338862306a36Sopenharmony_ci &dpbp_dev->mc_handle); 338962306a36Sopenharmony_ci if (err) { 339062306a36Sopenharmony_ci dev_err(dev, "dpbp_open() failed\n"); 339162306a36Sopenharmony_ci goto err_open; 339262306a36Sopenharmony_ci } 339362306a36Sopenharmony_ci 339462306a36Sopenharmony_ci err = dpbp_reset(priv->mc_io, 0, dpbp_dev->mc_handle); 339562306a36Sopenharmony_ci if (err) { 339662306a36Sopenharmony_ci dev_err(dev, "dpbp_reset() failed\n"); 339762306a36Sopenharmony_ci goto err_reset; 339862306a36Sopenharmony_ci } 339962306a36Sopenharmony_ci 340062306a36Sopenharmony_ci err = dpbp_enable(priv->mc_io, 0, dpbp_dev->mc_handle); 340162306a36Sopenharmony_ci if (err) { 340262306a36Sopenharmony_ci dev_err(dev, "dpbp_enable() failed\n"); 340362306a36Sopenharmony_ci goto err_enable; 340462306a36Sopenharmony_ci } 340562306a36Sopenharmony_ci 340662306a36Sopenharmony_ci err = dpbp_get_attributes(priv->mc_io, 0, dpbp_dev->mc_handle, 340762306a36Sopenharmony_ci &dpbp_attrs); 340862306a36Sopenharmony_ci if (err) { 340962306a36Sopenharmony_ci dev_err(dev, "dpbp_get_attributes() failed\n"); 341062306a36Sopenharmony_ci goto err_get_attr; 341162306a36Sopenharmony_ci } 341262306a36Sopenharmony_ci 341362306a36Sopenharmony_ci bp->dev = dpbp_dev; 341462306a36Sopenharmony_ci bp->bpid = dpbp_attrs.bpid; 341562306a36Sopenharmony_ci 341662306a36Sopenharmony_ci return bp; 341762306a36Sopenharmony_ci 341862306a36Sopenharmony_cierr_get_attr: 341962306a36Sopenharmony_ci dpbp_disable(priv->mc_io, 0, dpbp_dev->mc_handle); 342062306a36Sopenharmony_cierr_enable: 342162306a36Sopenharmony_cierr_reset: 342262306a36Sopenharmony_ci dpbp_close(priv->mc_io, 0, dpbp_dev->mc_handle); 342362306a36Sopenharmony_cierr_open: 342462306a36Sopenharmony_ci kfree(bp); 342562306a36Sopenharmony_cierr_alloc: 342662306a36Sopenharmony_ci fsl_mc_object_free(dpbp_dev); 342762306a36Sopenharmony_ci 342862306a36Sopenharmony_ci return ERR_PTR(err); 342962306a36Sopenharmony_ci} 343062306a36Sopenharmony_ci 343162306a36Sopenharmony_cistatic int dpaa2_eth_setup_default_dpbp(struct dpaa2_eth_priv *priv) 343262306a36Sopenharmony_ci{ 343362306a36Sopenharmony_ci struct dpaa2_eth_bp *bp; 343462306a36Sopenharmony_ci int i; 343562306a36Sopenharmony_ci 343662306a36Sopenharmony_ci bp = dpaa2_eth_allocate_dpbp(priv); 343762306a36Sopenharmony_ci if (IS_ERR(bp)) 343862306a36Sopenharmony_ci return PTR_ERR(bp); 343962306a36Sopenharmony_ci 344062306a36Sopenharmony_ci priv->bp[DPAA2_ETH_DEFAULT_BP_IDX] = bp; 344162306a36Sopenharmony_ci priv->num_bps++; 344262306a36Sopenharmony_ci 344362306a36Sopenharmony_ci for (i = 0; i < priv->num_channels; i++) 344462306a36Sopenharmony_ci priv->channel[i]->bp = bp; 344562306a36Sopenharmony_ci 344662306a36Sopenharmony_ci return 0; 344762306a36Sopenharmony_ci} 344862306a36Sopenharmony_ci 344962306a36Sopenharmony_civoid dpaa2_eth_free_dpbp(struct dpaa2_eth_priv *priv, struct dpaa2_eth_bp *bp) 345062306a36Sopenharmony_ci{ 345162306a36Sopenharmony_ci int idx_bp; 345262306a36Sopenharmony_ci 345362306a36Sopenharmony_ci /* Find the index at which this BP is stored */ 345462306a36Sopenharmony_ci for (idx_bp = 0; idx_bp < priv->num_bps; idx_bp++) 345562306a36Sopenharmony_ci if (priv->bp[idx_bp] == bp) 345662306a36Sopenharmony_ci break; 345762306a36Sopenharmony_ci 345862306a36Sopenharmony_ci /* Drain the pool and disable the associated MC object */ 345962306a36Sopenharmony_ci dpaa2_eth_drain_pool(priv, bp->bpid); 346062306a36Sopenharmony_ci dpbp_disable(priv->mc_io, 0, bp->dev->mc_handle); 346162306a36Sopenharmony_ci dpbp_close(priv->mc_io, 0, bp->dev->mc_handle); 346262306a36Sopenharmony_ci fsl_mc_object_free(bp->dev); 346362306a36Sopenharmony_ci kfree(bp); 346462306a36Sopenharmony_ci 346562306a36Sopenharmony_ci /* Move the last in use DPBP over in this position */ 346662306a36Sopenharmony_ci priv->bp[idx_bp] = priv->bp[priv->num_bps - 1]; 346762306a36Sopenharmony_ci priv->num_bps--; 346862306a36Sopenharmony_ci} 346962306a36Sopenharmony_ci 347062306a36Sopenharmony_cistatic void dpaa2_eth_free_dpbps(struct dpaa2_eth_priv *priv) 347162306a36Sopenharmony_ci{ 347262306a36Sopenharmony_ci int i; 347362306a36Sopenharmony_ci 347462306a36Sopenharmony_ci for (i = 0; i < priv->num_bps; i++) 347562306a36Sopenharmony_ci dpaa2_eth_free_dpbp(priv, priv->bp[i]); 347662306a36Sopenharmony_ci} 347762306a36Sopenharmony_ci 347862306a36Sopenharmony_cistatic int dpaa2_eth_set_buffer_layout(struct dpaa2_eth_priv *priv) 347962306a36Sopenharmony_ci{ 348062306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 348162306a36Sopenharmony_ci struct dpni_buffer_layout buf_layout = {0}; 348262306a36Sopenharmony_ci u16 rx_buf_align; 348362306a36Sopenharmony_ci int err; 348462306a36Sopenharmony_ci 348562306a36Sopenharmony_ci /* We need to check for WRIOP version 1.0.0, but depending on the MC 348662306a36Sopenharmony_ci * version, this number is not always provided correctly on rev1. 348762306a36Sopenharmony_ci * We need to check for both alternatives in this situation. 348862306a36Sopenharmony_ci */ 348962306a36Sopenharmony_ci if (priv->dpni_attrs.wriop_version == DPAA2_WRIOP_VERSION(0, 0, 0) || 349062306a36Sopenharmony_ci priv->dpni_attrs.wriop_version == DPAA2_WRIOP_VERSION(1, 0, 0)) 349162306a36Sopenharmony_ci rx_buf_align = DPAA2_ETH_RX_BUF_ALIGN_REV1; 349262306a36Sopenharmony_ci else 349362306a36Sopenharmony_ci rx_buf_align = DPAA2_ETH_RX_BUF_ALIGN; 349462306a36Sopenharmony_ci 349562306a36Sopenharmony_ci /* We need to ensure that the buffer size seen by WRIOP is a multiple 349662306a36Sopenharmony_ci * of 64 or 256 bytes depending on the WRIOP version. 349762306a36Sopenharmony_ci */ 349862306a36Sopenharmony_ci priv->rx_buf_size = ALIGN_DOWN(DPAA2_ETH_RX_BUF_SIZE, rx_buf_align); 349962306a36Sopenharmony_ci 350062306a36Sopenharmony_ci /* tx buffer */ 350162306a36Sopenharmony_ci buf_layout.private_data_size = DPAA2_ETH_SWA_SIZE; 350262306a36Sopenharmony_ci buf_layout.pass_timestamp = true; 350362306a36Sopenharmony_ci buf_layout.pass_frame_status = true; 350462306a36Sopenharmony_ci buf_layout.options = DPNI_BUF_LAYOUT_OPT_PRIVATE_DATA_SIZE | 350562306a36Sopenharmony_ci DPNI_BUF_LAYOUT_OPT_TIMESTAMP | 350662306a36Sopenharmony_ci DPNI_BUF_LAYOUT_OPT_FRAME_STATUS; 350762306a36Sopenharmony_ci err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token, 350862306a36Sopenharmony_ci DPNI_QUEUE_TX, &buf_layout); 350962306a36Sopenharmony_ci if (err) { 351062306a36Sopenharmony_ci dev_err(dev, "dpni_set_buffer_layout(TX) failed\n"); 351162306a36Sopenharmony_ci return err; 351262306a36Sopenharmony_ci } 351362306a36Sopenharmony_ci 351462306a36Sopenharmony_ci /* tx-confirm buffer */ 351562306a36Sopenharmony_ci buf_layout.options = DPNI_BUF_LAYOUT_OPT_TIMESTAMP | 351662306a36Sopenharmony_ci DPNI_BUF_LAYOUT_OPT_FRAME_STATUS; 351762306a36Sopenharmony_ci err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token, 351862306a36Sopenharmony_ci DPNI_QUEUE_TX_CONFIRM, &buf_layout); 351962306a36Sopenharmony_ci if (err) { 352062306a36Sopenharmony_ci dev_err(dev, "dpni_set_buffer_layout(TX_CONF) failed\n"); 352162306a36Sopenharmony_ci return err; 352262306a36Sopenharmony_ci } 352362306a36Sopenharmony_ci 352462306a36Sopenharmony_ci /* Now that we've set our tx buffer layout, retrieve the minimum 352562306a36Sopenharmony_ci * required tx data offset. 352662306a36Sopenharmony_ci */ 352762306a36Sopenharmony_ci err = dpni_get_tx_data_offset(priv->mc_io, 0, priv->mc_token, 352862306a36Sopenharmony_ci &priv->tx_data_offset); 352962306a36Sopenharmony_ci if (err) { 353062306a36Sopenharmony_ci dev_err(dev, "dpni_get_tx_data_offset() failed\n"); 353162306a36Sopenharmony_ci return err; 353262306a36Sopenharmony_ci } 353362306a36Sopenharmony_ci 353462306a36Sopenharmony_ci if ((priv->tx_data_offset % 64) != 0) 353562306a36Sopenharmony_ci dev_warn(dev, "Tx data offset (%d) not a multiple of 64B\n", 353662306a36Sopenharmony_ci priv->tx_data_offset); 353762306a36Sopenharmony_ci 353862306a36Sopenharmony_ci /* rx buffer */ 353962306a36Sopenharmony_ci buf_layout.pass_frame_status = true; 354062306a36Sopenharmony_ci buf_layout.pass_parser_result = true; 354162306a36Sopenharmony_ci buf_layout.data_align = rx_buf_align; 354262306a36Sopenharmony_ci buf_layout.data_head_room = dpaa2_eth_rx_head_room(priv); 354362306a36Sopenharmony_ci buf_layout.private_data_size = 0; 354462306a36Sopenharmony_ci buf_layout.options = DPNI_BUF_LAYOUT_OPT_PARSER_RESULT | 354562306a36Sopenharmony_ci DPNI_BUF_LAYOUT_OPT_FRAME_STATUS | 354662306a36Sopenharmony_ci DPNI_BUF_LAYOUT_OPT_DATA_ALIGN | 354762306a36Sopenharmony_ci DPNI_BUF_LAYOUT_OPT_DATA_HEAD_ROOM | 354862306a36Sopenharmony_ci DPNI_BUF_LAYOUT_OPT_TIMESTAMP; 354962306a36Sopenharmony_ci err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token, 355062306a36Sopenharmony_ci DPNI_QUEUE_RX, &buf_layout); 355162306a36Sopenharmony_ci if (err) { 355262306a36Sopenharmony_ci dev_err(dev, "dpni_set_buffer_layout(RX) failed\n"); 355362306a36Sopenharmony_ci return err; 355462306a36Sopenharmony_ci } 355562306a36Sopenharmony_ci 355662306a36Sopenharmony_ci return 0; 355762306a36Sopenharmony_ci} 355862306a36Sopenharmony_ci 355962306a36Sopenharmony_ci#define DPNI_ENQUEUE_FQID_VER_MAJOR 7 356062306a36Sopenharmony_ci#define DPNI_ENQUEUE_FQID_VER_MINOR 9 356162306a36Sopenharmony_ci 356262306a36Sopenharmony_cistatic inline int dpaa2_eth_enqueue_qd(struct dpaa2_eth_priv *priv, 356362306a36Sopenharmony_ci struct dpaa2_eth_fq *fq, 356462306a36Sopenharmony_ci struct dpaa2_fd *fd, u8 prio, 356562306a36Sopenharmony_ci u32 num_frames __always_unused, 356662306a36Sopenharmony_ci int *frames_enqueued) 356762306a36Sopenharmony_ci{ 356862306a36Sopenharmony_ci int err; 356962306a36Sopenharmony_ci 357062306a36Sopenharmony_ci err = dpaa2_io_service_enqueue_qd(fq->channel->dpio, 357162306a36Sopenharmony_ci priv->tx_qdid, prio, 357262306a36Sopenharmony_ci fq->tx_qdbin, fd); 357362306a36Sopenharmony_ci if (!err && frames_enqueued) 357462306a36Sopenharmony_ci *frames_enqueued = 1; 357562306a36Sopenharmony_ci return err; 357662306a36Sopenharmony_ci} 357762306a36Sopenharmony_ci 357862306a36Sopenharmony_cistatic inline int dpaa2_eth_enqueue_fq_multiple(struct dpaa2_eth_priv *priv, 357962306a36Sopenharmony_ci struct dpaa2_eth_fq *fq, 358062306a36Sopenharmony_ci struct dpaa2_fd *fd, 358162306a36Sopenharmony_ci u8 prio, u32 num_frames, 358262306a36Sopenharmony_ci int *frames_enqueued) 358362306a36Sopenharmony_ci{ 358462306a36Sopenharmony_ci int err; 358562306a36Sopenharmony_ci 358662306a36Sopenharmony_ci err = dpaa2_io_service_enqueue_multiple_fq(fq->channel->dpio, 358762306a36Sopenharmony_ci fq->tx_fqid[prio], 358862306a36Sopenharmony_ci fd, num_frames); 358962306a36Sopenharmony_ci 359062306a36Sopenharmony_ci if (err == 0) 359162306a36Sopenharmony_ci return -EBUSY; 359262306a36Sopenharmony_ci 359362306a36Sopenharmony_ci if (frames_enqueued) 359462306a36Sopenharmony_ci *frames_enqueued = err; 359562306a36Sopenharmony_ci return 0; 359662306a36Sopenharmony_ci} 359762306a36Sopenharmony_ci 359862306a36Sopenharmony_cistatic void dpaa2_eth_set_enqueue_mode(struct dpaa2_eth_priv *priv) 359962306a36Sopenharmony_ci{ 360062306a36Sopenharmony_ci if (dpaa2_eth_cmp_dpni_ver(priv, DPNI_ENQUEUE_FQID_VER_MAJOR, 360162306a36Sopenharmony_ci DPNI_ENQUEUE_FQID_VER_MINOR) < 0) 360262306a36Sopenharmony_ci priv->enqueue = dpaa2_eth_enqueue_qd; 360362306a36Sopenharmony_ci else 360462306a36Sopenharmony_ci priv->enqueue = dpaa2_eth_enqueue_fq_multiple; 360562306a36Sopenharmony_ci} 360662306a36Sopenharmony_ci 360762306a36Sopenharmony_cistatic int dpaa2_eth_set_pause(struct dpaa2_eth_priv *priv) 360862306a36Sopenharmony_ci{ 360962306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 361062306a36Sopenharmony_ci struct dpni_link_cfg link_cfg = {0}; 361162306a36Sopenharmony_ci int err; 361262306a36Sopenharmony_ci 361362306a36Sopenharmony_ci /* Get the default link options so we don't override other flags */ 361462306a36Sopenharmony_ci err = dpni_get_link_cfg(priv->mc_io, 0, priv->mc_token, &link_cfg); 361562306a36Sopenharmony_ci if (err) { 361662306a36Sopenharmony_ci dev_err(dev, "dpni_get_link_cfg() failed\n"); 361762306a36Sopenharmony_ci return err; 361862306a36Sopenharmony_ci } 361962306a36Sopenharmony_ci 362062306a36Sopenharmony_ci /* By default, enable both Rx and Tx pause frames */ 362162306a36Sopenharmony_ci link_cfg.options |= DPNI_LINK_OPT_PAUSE; 362262306a36Sopenharmony_ci link_cfg.options &= ~DPNI_LINK_OPT_ASYM_PAUSE; 362362306a36Sopenharmony_ci err = dpni_set_link_cfg(priv->mc_io, 0, priv->mc_token, &link_cfg); 362462306a36Sopenharmony_ci if (err) { 362562306a36Sopenharmony_ci dev_err(dev, "dpni_set_link_cfg() failed\n"); 362662306a36Sopenharmony_ci return err; 362762306a36Sopenharmony_ci } 362862306a36Sopenharmony_ci 362962306a36Sopenharmony_ci priv->link_state.options = link_cfg.options; 363062306a36Sopenharmony_ci 363162306a36Sopenharmony_ci return 0; 363262306a36Sopenharmony_ci} 363362306a36Sopenharmony_ci 363462306a36Sopenharmony_cistatic void dpaa2_eth_update_tx_fqids(struct dpaa2_eth_priv *priv) 363562306a36Sopenharmony_ci{ 363662306a36Sopenharmony_ci struct dpni_queue_id qid = {0}; 363762306a36Sopenharmony_ci struct dpaa2_eth_fq *fq; 363862306a36Sopenharmony_ci struct dpni_queue queue; 363962306a36Sopenharmony_ci int i, j, err; 364062306a36Sopenharmony_ci 364162306a36Sopenharmony_ci /* We only use Tx FQIDs for FQID-based enqueue, so check 364262306a36Sopenharmony_ci * if DPNI version supports it before updating FQIDs 364362306a36Sopenharmony_ci */ 364462306a36Sopenharmony_ci if (dpaa2_eth_cmp_dpni_ver(priv, DPNI_ENQUEUE_FQID_VER_MAJOR, 364562306a36Sopenharmony_ci DPNI_ENQUEUE_FQID_VER_MINOR) < 0) 364662306a36Sopenharmony_ci return; 364762306a36Sopenharmony_ci 364862306a36Sopenharmony_ci for (i = 0; i < priv->num_fqs; i++) { 364962306a36Sopenharmony_ci fq = &priv->fq[i]; 365062306a36Sopenharmony_ci if (fq->type != DPAA2_TX_CONF_FQ) 365162306a36Sopenharmony_ci continue; 365262306a36Sopenharmony_ci for (j = 0; j < dpaa2_eth_tc_count(priv); j++) { 365362306a36Sopenharmony_ci err = dpni_get_queue(priv->mc_io, 0, priv->mc_token, 365462306a36Sopenharmony_ci DPNI_QUEUE_TX, j, fq->flowid, 365562306a36Sopenharmony_ci &queue, &qid); 365662306a36Sopenharmony_ci if (err) 365762306a36Sopenharmony_ci goto out_err; 365862306a36Sopenharmony_ci 365962306a36Sopenharmony_ci fq->tx_fqid[j] = qid.fqid; 366062306a36Sopenharmony_ci if (fq->tx_fqid[j] == 0) 366162306a36Sopenharmony_ci goto out_err; 366262306a36Sopenharmony_ci } 366362306a36Sopenharmony_ci } 366462306a36Sopenharmony_ci 366562306a36Sopenharmony_ci priv->enqueue = dpaa2_eth_enqueue_fq_multiple; 366662306a36Sopenharmony_ci 366762306a36Sopenharmony_ci return; 366862306a36Sopenharmony_ci 366962306a36Sopenharmony_ciout_err: 367062306a36Sopenharmony_ci netdev_info(priv->net_dev, 367162306a36Sopenharmony_ci "Error reading Tx FQID, fallback to QDID-based enqueue\n"); 367262306a36Sopenharmony_ci priv->enqueue = dpaa2_eth_enqueue_qd; 367362306a36Sopenharmony_ci} 367462306a36Sopenharmony_ci 367562306a36Sopenharmony_ci/* Configure ingress classification based on VLAN PCP */ 367662306a36Sopenharmony_cistatic int dpaa2_eth_set_vlan_qos(struct dpaa2_eth_priv *priv) 367762306a36Sopenharmony_ci{ 367862306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 367962306a36Sopenharmony_ci struct dpkg_profile_cfg kg_cfg = {0}; 368062306a36Sopenharmony_ci struct dpni_qos_tbl_cfg qos_cfg = {0}; 368162306a36Sopenharmony_ci struct dpni_rule_cfg key_params; 368262306a36Sopenharmony_ci void *dma_mem, *key, *mask; 368362306a36Sopenharmony_ci u8 key_size = 2; /* VLAN TCI field */ 368462306a36Sopenharmony_ci int i, pcp, err; 368562306a36Sopenharmony_ci 368662306a36Sopenharmony_ci /* VLAN-based classification only makes sense if we have multiple 368762306a36Sopenharmony_ci * traffic classes. 368862306a36Sopenharmony_ci * Also, we need to extract just the 3-bit PCP field from the VLAN 368962306a36Sopenharmony_ci * header and we can only do that by using a mask 369062306a36Sopenharmony_ci */ 369162306a36Sopenharmony_ci if (dpaa2_eth_tc_count(priv) == 1 || !dpaa2_eth_fs_mask_enabled(priv)) { 369262306a36Sopenharmony_ci dev_dbg(dev, "VLAN-based QoS classification not supported\n"); 369362306a36Sopenharmony_ci return -EOPNOTSUPP; 369462306a36Sopenharmony_ci } 369562306a36Sopenharmony_ci 369662306a36Sopenharmony_ci dma_mem = kzalloc(DPAA2_CLASSIFIER_DMA_SIZE, GFP_KERNEL); 369762306a36Sopenharmony_ci if (!dma_mem) 369862306a36Sopenharmony_ci return -ENOMEM; 369962306a36Sopenharmony_ci 370062306a36Sopenharmony_ci kg_cfg.num_extracts = 1; 370162306a36Sopenharmony_ci kg_cfg.extracts[0].type = DPKG_EXTRACT_FROM_HDR; 370262306a36Sopenharmony_ci kg_cfg.extracts[0].extract.from_hdr.prot = NET_PROT_VLAN; 370362306a36Sopenharmony_ci kg_cfg.extracts[0].extract.from_hdr.type = DPKG_FULL_FIELD; 370462306a36Sopenharmony_ci kg_cfg.extracts[0].extract.from_hdr.field = NH_FLD_VLAN_TCI; 370562306a36Sopenharmony_ci 370662306a36Sopenharmony_ci err = dpni_prepare_key_cfg(&kg_cfg, dma_mem); 370762306a36Sopenharmony_ci if (err) { 370862306a36Sopenharmony_ci dev_err(dev, "dpni_prepare_key_cfg failed\n"); 370962306a36Sopenharmony_ci goto out_free_tbl; 371062306a36Sopenharmony_ci } 371162306a36Sopenharmony_ci 371262306a36Sopenharmony_ci /* set QoS table */ 371362306a36Sopenharmony_ci qos_cfg.default_tc = 0; 371462306a36Sopenharmony_ci qos_cfg.discard_on_miss = 0; 371562306a36Sopenharmony_ci qos_cfg.key_cfg_iova = dma_map_single(dev, dma_mem, 371662306a36Sopenharmony_ci DPAA2_CLASSIFIER_DMA_SIZE, 371762306a36Sopenharmony_ci DMA_TO_DEVICE); 371862306a36Sopenharmony_ci if (dma_mapping_error(dev, qos_cfg.key_cfg_iova)) { 371962306a36Sopenharmony_ci dev_err(dev, "QoS table DMA mapping failed\n"); 372062306a36Sopenharmony_ci err = -ENOMEM; 372162306a36Sopenharmony_ci goto out_free_tbl; 372262306a36Sopenharmony_ci } 372362306a36Sopenharmony_ci 372462306a36Sopenharmony_ci err = dpni_set_qos_table(priv->mc_io, 0, priv->mc_token, &qos_cfg); 372562306a36Sopenharmony_ci if (err) { 372662306a36Sopenharmony_ci dev_err(dev, "dpni_set_qos_table failed\n"); 372762306a36Sopenharmony_ci goto out_unmap_tbl; 372862306a36Sopenharmony_ci } 372962306a36Sopenharmony_ci 373062306a36Sopenharmony_ci /* Add QoS table entries */ 373162306a36Sopenharmony_ci key = kzalloc(key_size * 2, GFP_KERNEL); 373262306a36Sopenharmony_ci if (!key) { 373362306a36Sopenharmony_ci err = -ENOMEM; 373462306a36Sopenharmony_ci goto out_unmap_tbl; 373562306a36Sopenharmony_ci } 373662306a36Sopenharmony_ci mask = key + key_size; 373762306a36Sopenharmony_ci *(__be16 *)mask = cpu_to_be16(VLAN_PRIO_MASK); 373862306a36Sopenharmony_ci 373962306a36Sopenharmony_ci key_params.key_iova = dma_map_single(dev, key, key_size * 2, 374062306a36Sopenharmony_ci DMA_TO_DEVICE); 374162306a36Sopenharmony_ci if (dma_mapping_error(dev, key_params.key_iova)) { 374262306a36Sopenharmony_ci dev_err(dev, "Qos table entry DMA mapping failed\n"); 374362306a36Sopenharmony_ci err = -ENOMEM; 374462306a36Sopenharmony_ci goto out_free_key; 374562306a36Sopenharmony_ci } 374662306a36Sopenharmony_ci 374762306a36Sopenharmony_ci key_params.mask_iova = key_params.key_iova + key_size; 374862306a36Sopenharmony_ci key_params.key_size = key_size; 374962306a36Sopenharmony_ci 375062306a36Sopenharmony_ci /* We add rules for PCP-based distribution starting with highest 375162306a36Sopenharmony_ci * priority (VLAN PCP = 7). If this DPNI doesn't have enough traffic 375262306a36Sopenharmony_ci * classes to accommodate all priority levels, the lowest ones end up 375362306a36Sopenharmony_ci * on TC 0 which was configured as default 375462306a36Sopenharmony_ci */ 375562306a36Sopenharmony_ci for (i = dpaa2_eth_tc_count(priv) - 1, pcp = 7; i >= 0; i--, pcp--) { 375662306a36Sopenharmony_ci *(__be16 *)key = cpu_to_be16(pcp << VLAN_PRIO_SHIFT); 375762306a36Sopenharmony_ci dma_sync_single_for_device(dev, key_params.key_iova, 375862306a36Sopenharmony_ci key_size * 2, DMA_TO_DEVICE); 375962306a36Sopenharmony_ci 376062306a36Sopenharmony_ci err = dpni_add_qos_entry(priv->mc_io, 0, priv->mc_token, 376162306a36Sopenharmony_ci &key_params, i, i); 376262306a36Sopenharmony_ci if (err) { 376362306a36Sopenharmony_ci dev_err(dev, "dpni_add_qos_entry failed\n"); 376462306a36Sopenharmony_ci dpni_clear_qos_table(priv->mc_io, 0, priv->mc_token); 376562306a36Sopenharmony_ci goto out_unmap_key; 376662306a36Sopenharmony_ci } 376762306a36Sopenharmony_ci } 376862306a36Sopenharmony_ci 376962306a36Sopenharmony_ci priv->vlan_cls_enabled = true; 377062306a36Sopenharmony_ci 377162306a36Sopenharmony_ci /* Table and key memory is not persistent, clean everything up after 377262306a36Sopenharmony_ci * configuration is finished 377362306a36Sopenharmony_ci */ 377462306a36Sopenharmony_ciout_unmap_key: 377562306a36Sopenharmony_ci dma_unmap_single(dev, key_params.key_iova, key_size * 2, DMA_TO_DEVICE); 377662306a36Sopenharmony_ciout_free_key: 377762306a36Sopenharmony_ci kfree(key); 377862306a36Sopenharmony_ciout_unmap_tbl: 377962306a36Sopenharmony_ci dma_unmap_single(dev, qos_cfg.key_cfg_iova, DPAA2_CLASSIFIER_DMA_SIZE, 378062306a36Sopenharmony_ci DMA_TO_DEVICE); 378162306a36Sopenharmony_ciout_free_tbl: 378262306a36Sopenharmony_ci kfree(dma_mem); 378362306a36Sopenharmony_ci 378462306a36Sopenharmony_ci return err; 378562306a36Sopenharmony_ci} 378662306a36Sopenharmony_ci 378762306a36Sopenharmony_ci/* Configure the DPNI object this interface is associated with */ 378862306a36Sopenharmony_cistatic int dpaa2_eth_setup_dpni(struct fsl_mc_device *ls_dev) 378962306a36Sopenharmony_ci{ 379062306a36Sopenharmony_ci struct device *dev = &ls_dev->dev; 379162306a36Sopenharmony_ci struct dpaa2_eth_priv *priv; 379262306a36Sopenharmony_ci struct net_device *net_dev; 379362306a36Sopenharmony_ci int err; 379462306a36Sopenharmony_ci 379562306a36Sopenharmony_ci net_dev = dev_get_drvdata(dev); 379662306a36Sopenharmony_ci priv = netdev_priv(net_dev); 379762306a36Sopenharmony_ci 379862306a36Sopenharmony_ci /* get a handle for the DPNI object */ 379962306a36Sopenharmony_ci err = dpni_open(priv->mc_io, 0, ls_dev->obj_desc.id, &priv->mc_token); 380062306a36Sopenharmony_ci if (err) { 380162306a36Sopenharmony_ci dev_err(dev, "dpni_open() failed\n"); 380262306a36Sopenharmony_ci return err; 380362306a36Sopenharmony_ci } 380462306a36Sopenharmony_ci 380562306a36Sopenharmony_ci /* Check if we can work with this DPNI object */ 380662306a36Sopenharmony_ci err = dpni_get_api_version(priv->mc_io, 0, &priv->dpni_ver_major, 380762306a36Sopenharmony_ci &priv->dpni_ver_minor); 380862306a36Sopenharmony_ci if (err) { 380962306a36Sopenharmony_ci dev_err(dev, "dpni_get_api_version() failed\n"); 381062306a36Sopenharmony_ci goto close; 381162306a36Sopenharmony_ci } 381262306a36Sopenharmony_ci if (dpaa2_eth_cmp_dpni_ver(priv, DPNI_VER_MAJOR, DPNI_VER_MINOR) < 0) { 381362306a36Sopenharmony_ci dev_err(dev, "DPNI version %u.%u not supported, need >= %u.%u\n", 381462306a36Sopenharmony_ci priv->dpni_ver_major, priv->dpni_ver_minor, 381562306a36Sopenharmony_ci DPNI_VER_MAJOR, DPNI_VER_MINOR); 381662306a36Sopenharmony_ci err = -EOPNOTSUPP; 381762306a36Sopenharmony_ci goto close; 381862306a36Sopenharmony_ci } 381962306a36Sopenharmony_ci 382062306a36Sopenharmony_ci ls_dev->mc_io = priv->mc_io; 382162306a36Sopenharmony_ci ls_dev->mc_handle = priv->mc_token; 382262306a36Sopenharmony_ci 382362306a36Sopenharmony_ci err = dpni_reset(priv->mc_io, 0, priv->mc_token); 382462306a36Sopenharmony_ci if (err) { 382562306a36Sopenharmony_ci dev_err(dev, "dpni_reset() failed\n"); 382662306a36Sopenharmony_ci goto close; 382762306a36Sopenharmony_ci } 382862306a36Sopenharmony_ci 382962306a36Sopenharmony_ci err = dpni_get_attributes(priv->mc_io, 0, priv->mc_token, 383062306a36Sopenharmony_ci &priv->dpni_attrs); 383162306a36Sopenharmony_ci if (err) { 383262306a36Sopenharmony_ci dev_err(dev, "dpni_get_attributes() failed (err=%d)\n", err); 383362306a36Sopenharmony_ci goto close; 383462306a36Sopenharmony_ci } 383562306a36Sopenharmony_ci 383662306a36Sopenharmony_ci err = dpaa2_eth_set_buffer_layout(priv); 383762306a36Sopenharmony_ci if (err) 383862306a36Sopenharmony_ci goto close; 383962306a36Sopenharmony_ci 384062306a36Sopenharmony_ci dpaa2_eth_set_enqueue_mode(priv); 384162306a36Sopenharmony_ci 384262306a36Sopenharmony_ci /* Enable pause frame support */ 384362306a36Sopenharmony_ci if (dpaa2_eth_has_pause_support(priv)) { 384462306a36Sopenharmony_ci err = dpaa2_eth_set_pause(priv); 384562306a36Sopenharmony_ci if (err) 384662306a36Sopenharmony_ci goto close; 384762306a36Sopenharmony_ci } 384862306a36Sopenharmony_ci 384962306a36Sopenharmony_ci err = dpaa2_eth_set_vlan_qos(priv); 385062306a36Sopenharmony_ci if (err && err != -EOPNOTSUPP) 385162306a36Sopenharmony_ci goto close; 385262306a36Sopenharmony_ci 385362306a36Sopenharmony_ci priv->cls_rules = devm_kcalloc(dev, dpaa2_eth_fs_count(priv), 385462306a36Sopenharmony_ci sizeof(struct dpaa2_eth_cls_rule), 385562306a36Sopenharmony_ci GFP_KERNEL); 385662306a36Sopenharmony_ci if (!priv->cls_rules) { 385762306a36Sopenharmony_ci err = -ENOMEM; 385862306a36Sopenharmony_ci goto close; 385962306a36Sopenharmony_ci } 386062306a36Sopenharmony_ci 386162306a36Sopenharmony_ci return 0; 386262306a36Sopenharmony_ci 386362306a36Sopenharmony_ciclose: 386462306a36Sopenharmony_ci dpni_close(priv->mc_io, 0, priv->mc_token); 386562306a36Sopenharmony_ci 386662306a36Sopenharmony_ci return err; 386762306a36Sopenharmony_ci} 386862306a36Sopenharmony_ci 386962306a36Sopenharmony_cistatic void dpaa2_eth_free_dpni(struct dpaa2_eth_priv *priv) 387062306a36Sopenharmony_ci{ 387162306a36Sopenharmony_ci int err; 387262306a36Sopenharmony_ci 387362306a36Sopenharmony_ci err = dpni_reset(priv->mc_io, 0, priv->mc_token); 387462306a36Sopenharmony_ci if (err) 387562306a36Sopenharmony_ci netdev_warn(priv->net_dev, "dpni_reset() failed (err %d)\n", 387662306a36Sopenharmony_ci err); 387762306a36Sopenharmony_ci 387862306a36Sopenharmony_ci dpni_close(priv->mc_io, 0, priv->mc_token); 387962306a36Sopenharmony_ci} 388062306a36Sopenharmony_ci 388162306a36Sopenharmony_cistatic int dpaa2_eth_setup_rx_flow(struct dpaa2_eth_priv *priv, 388262306a36Sopenharmony_ci struct dpaa2_eth_fq *fq) 388362306a36Sopenharmony_ci{ 388462306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 388562306a36Sopenharmony_ci struct dpni_queue queue; 388662306a36Sopenharmony_ci struct dpni_queue_id qid; 388762306a36Sopenharmony_ci int err; 388862306a36Sopenharmony_ci 388962306a36Sopenharmony_ci err = dpni_get_queue(priv->mc_io, 0, priv->mc_token, 389062306a36Sopenharmony_ci DPNI_QUEUE_RX, fq->tc, fq->flowid, &queue, &qid); 389162306a36Sopenharmony_ci if (err) { 389262306a36Sopenharmony_ci dev_err(dev, "dpni_get_queue(RX) failed\n"); 389362306a36Sopenharmony_ci return err; 389462306a36Sopenharmony_ci } 389562306a36Sopenharmony_ci 389662306a36Sopenharmony_ci fq->fqid = qid.fqid; 389762306a36Sopenharmony_ci 389862306a36Sopenharmony_ci queue.destination.id = fq->channel->dpcon_id; 389962306a36Sopenharmony_ci queue.destination.type = DPNI_DEST_DPCON; 390062306a36Sopenharmony_ci queue.destination.priority = 1; 390162306a36Sopenharmony_ci queue.user_context = (u64)(uintptr_t)fq; 390262306a36Sopenharmony_ci err = dpni_set_queue(priv->mc_io, 0, priv->mc_token, 390362306a36Sopenharmony_ci DPNI_QUEUE_RX, fq->tc, fq->flowid, 390462306a36Sopenharmony_ci DPNI_QUEUE_OPT_USER_CTX | DPNI_QUEUE_OPT_DEST, 390562306a36Sopenharmony_ci &queue); 390662306a36Sopenharmony_ci if (err) { 390762306a36Sopenharmony_ci dev_err(dev, "dpni_set_queue(RX) failed\n"); 390862306a36Sopenharmony_ci return err; 390962306a36Sopenharmony_ci } 391062306a36Sopenharmony_ci 391162306a36Sopenharmony_ci /* xdp_rxq setup */ 391262306a36Sopenharmony_ci /* only once for each channel */ 391362306a36Sopenharmony_ci if (fq->tc > 0) 391462306a36Sopenharmony_ci return 0; 391562306a36Sopenharmony_ci 391662306a36Sopenharmony_ci err = xdp_rxq_info_reg(&fq->channel->xdp_rxq, priv->net_dev, 391762306a36Sopenharmony_ci fq->flowid, 0); 391862306a36Sopenharmony_ci if (err) { 391962306a36Sopenharmony_ci dev_err(dev, "xdp_rxq_info_reg failed\n"); 392062306a36Sopenharmony_ci return err; 392162306a36Sopenharmony_ci } 392262306a36Sopenharmony_ci 392362306a36Sopenharmony_ci err = xdp_rxq_info_reg_mem_model(&fq->channel->xdp_rxq, 392462306a36Sopenharmony_ci MEM_TYPE_PAGE_ORDER0, NULL); 392562306a36Sopenharmony_ci if (err) { 392662306a36Sopenharmony_ci dev_err(dev, "xdp_rxq_info_reg_mem_model failed\n"); 392762306a36Sopenharmony_ci return err; 392862306a36Sopenharmony_ci } 392962306a36Sopenharmony_ci 393062306a36Sopenharmony_ci return 0; 393162306a36Sopenharmony_ci} 393262306a36Sopenharmony_ci 393362306a36Sopenharmony_cistatic int dpaa2_eth_setup_tx_flow(struct dpaa2_eth_priv *priv, 393462306a36Sopenharmony_ci struct dpaa2_eth_fq *fq) 393562306a36Sopenharmony_ci{ 393662306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 393762306a36Sopenharmony_ci struct dpni_queue queue; 393862306a36Sopenharmony_ci struct dpni_queue_id qid; 393962306a36Sopenharmony_ci int i, err; 394062306a36Sopenharmony_ci 394162306a36Sopenharmony_ci for (i = 0; i < dpaa2_eth_tc_count(priv); i++) { 394262306a36Sopenharmony_ci err = dpni_get_queue(priv->mc_io, 0, priv->mc_token, 394362306a36Sopenharmony_ci DPNI_QUEUE_TX, i, fq->flowid, 394462306a36Sopenharmony_ci &queue, &qid); 394562306a36Sopenharmony_ci if (err) { 394662306a36Sopenharmony_ci dev_err(dev, "dpni_get_queue(TX) failed\n"); 394762306a36Sopenharmony_ci return err; 394862306a36Sopenharmony_ci } 394962306a36Sopenharmony_ci fq->tx_fqid[i] = qid.fqid; 395062306a36Sopenharmony_ci } 395162306a36Sopenharmony_ci 395262306a36Sopenharmony_ci /* All Tx queues belonging to the same flowid have the same qdbin */ 395362306a36Sopenharmony_ci fq->tx_qdbin = qid.qdbin; 395462306a36Sopenharmony_ci 395562306a36Sopenharmony_ci err = dpni_get_queue(priv->mc_io, 0, priv->mc_token, 395662306a36Sopenharmony_ci DPNI_QUEUE_TX_CONFIRM, 0, fq->flowid, 395762306a36Sopenharmony_ci &queue, &qid); 395862306a36Sopenharmony_ci if (err) { 395962306a36Sopenharmony_ci dev_err(dev, "dpni_get_queue(TX_CONF) failed\n"); 396062306a36Sopenharmony_ci return err; 396162306a36Sopenharmony_ci } 396262306a36Sopenharmony_ci 396362306a36Sopenharmony_ci fq->fqid = qid.fqid; 396462306a36Sopenharmony_ci 396562306a36Sopenharmony_ci queue.destination.id = fq->channel->dpcon_id; 396662306a36Sopenharmony_ci queue.destination.type = DPNI_DEST_DPCON; 396762306a36Sopenharmony_ci queue.destination.priority = 0; 396862306a36Sopenharmony_ci queue.user_context = (u64)(uintptr_t)fq; 396962306a36Sopenharmony_ci err = dpni_set_queue(priv->mc_io, 0, priv->mc_token, 397062306a36Sopenharmony_ci DPNI_QUEUE_TX_CONFIRM, 0, fq->flowid, 397162306a36Sopenharmony_ci DPNI_QUEUE_OPT_USER_CTX | DPNI_QUEUE_OPT_DEST, 397262306a36Sopenharmony_ci &queue); 397362306a36Sopenharmony_ci if (err) { 397462306a36Sopenharmony_ci dev_err(dev, "dpni_set_queue(TX_CONF) failed\n"); 397562306a36Sopenharmony_ci return err; 397662306a36Sopenharmony_ci } 397762306a36Sopenharmony_ci 397862306a36Sopenharmony_ci return 0; 397962306a36Sopenharmony_ci} 398062306a36Sopenharmony_ci 398162306a36Sopenharmony_cistatic int setup_rx_err_flow(struct dpaa2_eth_priv *priv, 398262306a36Sopenharmony_ci struct dpaa2_eth_fq *fq) 398362306a36Sopenharmony_ci{ 398462306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 398562306a36Sopenharmony_ci struct dpni_queue q = { { 0 } }; 398662306a36Sopenharmony_ci struct dpni_queue_id qid; 398762306a36Sopenharmony_ci u8 q_opt = DPNI_QUEUE_OPT_USER_CTX | DPNI_QUEUE_OPT_DEST; 398862306a36Sopenharmony_ci int err; 398962306a36Sopenharmony_ci 399062306a36Sopenharmony_ci err = dpni_get_queue(priv->mc_io, 0, priv->mc_token, 399162306a36Sopenharmony_ci DPNI_QUEUE_RX_ERR, 0, 0, &q, &qid); 399262306a36Sopenharmony_ci if (err) { 399362306a36Sopenharmony_ci dev_err(dev, "dpni_get_queue() failed (%d)\n", err); 399462306a36Sopenharmony_ci return err; 399562306a36Sopenharmony_ci } 399662306a36Sopenharmony_ci 399762306a36Sopenharmony_ci fq->fqid = qid.fqid; 399862306a36Sopenharmony_ci 399962306a36Sopenharmony_ci q.destination.id = fq->channel->dpcon_id; 400062306a36Sopenharmony_ci q.destination.type = DPNI_DEST_DPCON; 400162306a36Sopenharmony_ci q.destination.priority = 1; 400262306a36Sopenharmony_ci q.user_context = (u64)(uintptr_t)fq; 400362306a36Sopenharmony_ci err = dpni_set_queue(priv->mc_io, 0, priv->mc_token, 400462306a36Sopenharmony_ci DPNI_QUEUE_RX_ERR, 0, 0, q_opt, &q); 400562306a36Sopenharmony_ci if (err) { 400662306a36Sopenharmony_ci dev_err(dev, "dpni_set_queue() failed (%d)\n", err); 400762306a36Sopenharmony_ci return err; 400862306a36Sopenharmony_ci } 400962306a36Sopenharmony_ci 401062306a36Sopenharmony_ci return 0; 401162306a36Sopenharmony_ci} 401262306a36Sopenharmony_ci 401362306a36Sopenharmony_ci/* Supported header fields for Rx hash distribution key */ 401462306a36Sopenharmony_cistatic const struct dpaa2_eth_dist_fields dist_fields[] = { 401562306a36Sopenharmony_ci { 401662306a36Sopenharmony_ci /* L2 header */ 401762306a36Sopenharmony_ci .rxnfc_field = RXH_L2DA, 401862306a36Sopenharmony_ci .cls_prot = NET_PROT_ETH, 401962306a36Sopenharmony_ci .cls_field = NH_FLD_ETH_DA, 402062306a36Sopenharmony_ci .id = DPAA2_ETH_DIST_ETHDST, 402162306a36Sopenharmony_ci .size = 6, 402262306a36Sopenharmony_ci }, { 402362306a36Sopenharmony_ci .cls_prot = NET_PROT_ETH, 402462306a36Sopenharmony_ci .cls_field = NH_FLD_ETH_SA, 402562306a36Sopenharmony_ci .id = DPAA2_ETH_DIST_ETHSRC, 402662306a36Sopenharmony_ci .size = 6, 402762306a36Sopenharmony_ci }, { 402862306a36Sopenharmony_ci /* This is the last ethertype field parsed: 402962306a36Sopenharmony_ci * depending on frame format, it can be the MAC ethertype 403062306a36Sopenharmony_ci * or the VLAN etype. 403162306a36Sopenharmony_ci */ 403262306a36Sopenharmony_ci .cls_prot = NET_PROT_ETH, 403362306a36Sopenharmony_ci .cls_field = NH_FLD_ETH_TYPE, 403462306a36Sopenharmony_ci .id = DPAA2_ETH_DIST_ETHTYPE, 403562306a36Sopenharmony_ci .size = 2, 403662306a36Sopenharmony_ci }, { 403762306a36Sopenharmony_ci /* VLAN header */ 403862306a36Sopenharmony_ci .rxnfc_field = RXH_VLAN, 403962306a36Sopenharmony_ci .cls_prot = NET_PROT_VLAN, 404062306a36Sopenharmony_ci .cls_field = NH_FLD_VLAN_TCI, 404162306a36Sopenharmony_ci .id = DPAA2_ETH_DIST_VLAN, 404262306a36Sopenharmony_ci .size = 2, 404362306a36Sopenharmony_ci }, { 404462306a36Sopenharmony_ci /* IP header */ 404562306a36Sopenharmony_ci .rxnfc_field = RXH_IP_SRC, 404662306a36Sopenharmony_ci .cls_prot = NET_PROT_IP, 404762306a36Sopenharmony_ci .cls_field = NH_FLD_IP_SRC, 404862306a36Sopenharmony_ci .id = DPAA2_ETH_DIST_IPSRC, 404962306a36Sopenharmony_ci .size = 4, 405062306a36Sopenharmony_ci }, { 405162306a36Sopenharmony_ci .rxnfc_field = RXH_IP_DST, 405262306a36Sopenharmony_ci .cls_prot = NET_PROT_IP, 405362306a36Sopenharmony_ci .cls_field = NH_FLD_IP_DST, 405462306a36Sopenharmony_ci .id = DPAA2_ETH_DIST_IPDST, 405562306a36Sopenharmony_ci .size = 4, 405662306a36Sopenharmony_ci }, { 405762306a36Sopenharmony_ci .rxnfc_field = RXH_L3_PROTO, 405862306a36Sopenharmony_ci .cls_prot = NET_PROT_IP, 405962306a36Sopenharmony_ci .cls_field = NH_FLD_IP_PROTO, 406062306a36Sopenharmony_ci .id = DPAA2_ETH_DIST_IPPROTO, 406162306a36Sopenharmony_ci .size = 1, 406262306a36Sopenharmony_ci }, { 406362306a36Sopenharmony_ci /* Using UDP ports, this is functionally equivalent to raw 406462306a36Sopenharmony_ci * byte pairs from L4 header. 406562306a36Sopenharmony_ci */ 406662306a36Sopenharmony_ci .rxnfc_field = RXH_L4_B_0_1, 406762306a36Sopenharmony_ci .cls_prot = NET_PROT_UDP, 406862306a36Sopenharmony_ci .cls_field = NH_FLD_UDP_PORT_SRC, 406962306a36Sopenharmony_ci .id = DPAA2_ETH_DIST_L4SRC, 407062306a36Sopenharmony_ci .size = 2, 407162306a36Sopenharmony_ci }, { 407262306a36Sopenharmony_ci .rxnfc_field = RXH_L4_B_2_3, 407362306a36Sopenharmony_ci .cls_prot = NET_PROT_UDP, 407462306a36Sopenharmony_ci .cls_field = NH_FLD_UDP_PORT_DST, 407562306a36Sopenharmony_ci .id = DPAA2_ETH_DIST_L4DST, 407662306a36Sopenharmony_ci .size = 2, 407762306a36Sopenharmony_ci }, 407862306a36Sopenharmony_ci}; 407962306a36Sopenharmony_ci 408062306a36Sopenharmony_ci/* Configure the Rx hash key using the legacy API */ 408162306a36Sopenharmony_cistatic int dpaa2_eth_config_legacy_hash_key(struct dpaa2_eth_priv *priv, dma_addr_t key) 408262306a36Sopenharmony_ci{ 408362306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 408462306a36Sopenharmony_ci struct dpni_rx_tc_dist_cfg dist_cfg; 408562306a36Sopenharmony_ci int i, err = 0; 408662306a36Sopenharmony_ci 408762306a36Sopenharmony_ci memset(&dist_cfg, 0, sizeof(dist_cfg)); 408862306a36Sopenharmony_ci 408962306a36Sopenharmony_ci dist_cfg.key_cfg_iova = key; 409062306a36Sopenharmony_ci dist_cfg.dist_size = dpaa2_eth_queue_count(priv); 409162306a36Sopenharmony_ci dist_cfg.dist_mode = DPNI_DIST_MODE_HASH; 409262306a36Sopenharmony_ci 409362306a36Sopenharmony_ci for (i = 0; i < dpaa2_eth_tc_count(priv); i++) { 409462306a36Sopenharmony_ci err = dpni_set_rx_tc_dist(priv->mc_io, 0, priv->mc_token, 409562306a36Sopenharmony_ci i, &dist_cfg); 409662306a36Sopenharmony_ci if (err) { 409762306a36Sopenharmony_ci dev_err(dev, "dpni_set_rx_tc_dist failed\n"); 409862306a36Sopenharmony_ci break; 409962306a36Sopenharmony_ci } 410062306a36Sopenharmony_ci } 410162306a36Sopenharmony_ci 410262306a36Sopenharmony_ci return err; 410362306a36Sopenharmony_ci} 410462306a36Sopenharmony_ci 410562306a36Sopenharmony_ci/* Configure the Rx hash key using the new API */ 410662306a36Sopenharmony_cistatic int dpaa2_eth_config_hash_key(struct dpaa2_eth_priv *priv, dma_addr_t key) 410762306a36Sopenharmony_ci{ 410862306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 410962306a36Sopenharmony_ci struct dpni_rx_dist_cfg dist_cfg; 411062306a36Sopenharmony_ci int i, err = 0; 411162306a36Sopenharmony_ci 411262306a36Sopenharmony_ci memset(&dist_cfg, 0, sizeof(dist_cfg)); 411362306a36Sopenharmony_ci 411462306a36Sopenharmony_ci dist_cfg.key_cfg_iova = key; 411562306a36Sopenharmony_ci dist_cfg.dist_size = dpaa2_eth_queue_count(priv); 411662306a36Sopenharmony_ci dist_cfg.enable = 1; 411762306a36Sopenharmony_ci 411862306a36Sopenharmony_ci for (i = 0; i < dpaa2_eth_tc_count(priv); i++) { 411962306a36Sopenharmony_ci dist_cfg.tc = i; 412062306a36Sopenharmony_ci err = dpni_set_rx_hash_dist(priv->mc_io, 0, priv->mc_token, 412162306a36Sopenharmony_ci &dist_cfg); 412262306a36Sopenharmony_ci if (err) { 412362306a36Sopenharmony_ci dev_err(dev, "dpni_set_rx_hash_dist failed\n"); 412462306a36Sopenharmony_ci break; 412562306a36Sopenharmony_ci } 412662306a36Sopenharmony_ci 412762306a36Sopenharmony_ci /* If the flow steering / hashing key is shared between all 412862306a36Sopenharmony_ci * traffic classes, install it just once 412962306a36Sopenharmony_ci */ 413062306a36Sopenharmony_ci if (priv->dpni_attrs.options & DPNI_OPT_SHARED_FS) 413162306a36Sopenharmony_ci break; 413262306a36Sopenharmony_ci } 413362306a36Sopenharmony_ci 413462306a36Sopenharmony_ci return err; 413562306a36Sopenharmony_ci} 413662306a36Sopenharmony_ci 413762306a36Sopenharmony_ci/* Configure the Rx flow classification key */ 413862306a36Sopenharmony_cistatic int dpaa2_eth_config_cls_key(struct dpaa2_eth_priv *priv, dma_addr_t key) 413962306a36Sopenharmony_ci{ 414062306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 414162306a36Sopenharmony_ci struct dpni_rx_dist_cfg dist_cfg; 414262306a36Sopenharmony_ci int i, err = 0; 414362306a36Sopenharmony_ci 414462306a36Sopenharmony_ci memset(&dist_cfg, 0, sizeof(dist_cfg)); 414562306a36Sopenharmony_ci 414662306a36Sopenharmony_ci dist_cfg.key_cfg_iova = key; 414762306a36Sopenharmony_ci dist_cfg.dist_size = dpaa2_eth_queue_count(priv); 414862306a36Sopenharmony_ci dist_cfg.enable = 1; 414962306a36Sopenharmony_ci 415062306a36Sopenharmony_ci for (i = 0; i < dpaa2_eth_tc_count(priv); i++) { 415162306a36Sopenharmony_ci dist_cfg.tc = i; 415262306a36Sopenharmony_ci err = dpni_set_rx_fs_dist(priv->mc_io, 0, priv->mc_token, 415362306a36Sopenharmony_ci &dist_cfg); 415462306a36Sopenharmony_ci if (err) { 415562306a36Sopenharmony_ci dev_err(dev, "dpni_set_rx_fs_dist failed\n"); 415662306a36Sopenharmony_ci break; 415762306a36Sopenharmony_ci } 415862306a36Sopenharmony_ci 415962306a36Sopenharmony_ci /* If the flow steering / hashing key is shared between all 416062306a36Sopenharmony_ci * traffic classes, install it just once 416162306a36Sopenharmony_ci */ 416262306a36Sopenharmony_ci if (priv->dpni_attrs.options & DPNI_OPT_SHARED_FS) 416362306a36Sopenharmony_ci break; 416462306a36Sopenharmony_ci } 416562306a36Sopenharmony_ci 416662306a36Sopenharmony_ci return err; 416762306a36Sopenharmony_ci} 416862306a36Sopenharmony_ci 416962306a36Sopenharmony_ci/* Size of the Rx flow classification key */ 417062306a36Sopenharmony_ciint dpaa2_eth_cls_key_size(u64 fields) 417162306a36Sopenharmony_ci{ 417262306a36Sopenharmony_ci int i, size = 0; 417362306a36Sopenharmony_ci 417462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dist_fields); i++) { 417562306a36Sopenharmony_ci if (!(fields & dist_fields[i].id)) 417662306a36Sopenharmony_ci continue; 417762306a36Sopenharmony_ci size += dist_fields[i].size; 417862306a36Sopenharmony_ci } 417962306a36Sopenharmony_ci 418062306a36Sopenharmony_ci return size; 418162306a36Sopenharmony_ci} 418262306a36Sopenharmony_ci 418362306a36Sopenharmony_ci/* Offset of header field in Rx classification key */ 418462306a36Sopenharmony_ciint dpaa2_eth_cls_fld_off(int prot, int field) 418562306a36Sopenharmony_ci{ 418662306a36Sopenharmony_ci int i, off = 0; 418762306a36Sopenharmony_ci 418862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dist_fields); i++) { 418962306a36Sopenharmony_ci if (dist_fields[i].cls_prot == prot && 419062306a36Sopenharmony_ci dist_fields[i].cls_field == field) 419162306a36Sopenharmony_ci return off; 419262306a36Sopenharmony_ci off += dist_fields[i].size; 419362306a36Sopenharmony_ci } 419462306a36Sopenharmony_ci 419562306a36Sopenharmony_ci WARN_ONCE(1, "Unsupported header field used for Rx flow cls\n"); 419662306a36Sopenharmony_ci return 0; 419762306a36Sopenharmony_ci} 419862306a36Sopenharmony_ci 419962306a36Sopenharmony_ci/* Prune unused fields from the classification rule. 420062306a36Sopenharmony_ci * Used when masking is not supported 420162306a36Sopenharmony_ci */ 420262306a36Sopenharmony_civoid dpaa2_eth_cls_trim_rule(void *key_mem, u64 fields) 420362306a36Sopenharmony_ci{ 420462306a36Sopenharmony_ci int off = 0, new_off = 0; 420562306a36Sopenharmony_ci int i, size; 420662306a36Sopenharmony_ci 420762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dist_fields); i++) { 420862306a36Sopenharmony_ci size = dist_fields[i].size; 420962306a36Sopenharmony_ci if (dist_fields[i].id & fields) { 421062306a36Sopenharmony_ci memcpy(key_mem + new_off, key_mem + off, size); 421162306a36Sopenharmony_ci new_off += size; 421262306a36Sopenharmony_ci } 421362306a36Sopenharmony_ci off += size; 421462306a36Sopenharmony_ci } 421562306a36Sopenharmony_ci} 421662306a36Sopenharmony_ci 421762306a36Sopenharmony_ci/* Set Rx distribution (hash or flow classification) key 421862306a36Sopenharmony_ci * flags is a combination of RXH_ bits 421962306a36Sopenharmony_ci */ 422062306a36Sopenharmony_cistatic int dpaa2_eth_set_dist_key(struct net_device *net_dev, 422162306a36Sopenharmony_ci enum dpaa2_eth_rx_dist type, u64 flags) 422262306a36Sopenharmony_ci{ 422362306a36Sopenharmony_ci struct device *dev = net_dev->dev.parent; 422462306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 422562306a36Sopenharmony_ci struct dpkg_profile_cfg cls_cfg; 422662306a36Sopenharmony_ci u32 rx_hash_fields = 0; 422762306a36Sopenharmony_ci dma_addr_t key_iova; 422862306a36Sopenharmony_ci u8 *dma_mem; 422962306a36Sopenharmony_ci int i; 423062306a36Sopenharmony_ci int err = 0; 423162306a36Sopenharmony_ci 423262306a36Sopenharmony_ci memset(&cls_cfg, 0, sizeof(cls_cfg)); 423362306a36Sopenharmony_ci 423462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dist_fields); i++) { 423562306a36Sopenharmony_ci struct dpkg_extract *key = 423662306a36Sopenharmony_ci &cls_cfg.extracts[cls_cfg.num_extracts]; 423762306a36Sopenharmony_ci 423862306a36Sopenharmony_ci /* For both Rx hashing and classification keys 423962306a36Sopenharmony_ci * we set only the selected fields. 424062306a36Sopenharmony_ci */ 424162306a36Sopenharmony_ci if (!(flags & dist_fields[i].id)) 424262306a36Sopenharmony_ci continue; 424362306a36Sopenharmony_ci if (type == DPAA2_ETH_RX_DIST_HASH) 424462306a36Sopenharmony_ci rx_hash_fields |= dist_fields[i].rxnfc_field; 424562306a36Sopenharmony_ci 424662306a36Sopenharmony_ci if (cls_cfg.num_extracts >= DPKG_MAX_NUM_OF_EXTRACTS) { 424762306a36Sopenharmony_ci dev_err(dev, "error adding key extraction rule, too many rules?\n"); 424862306a36Sopenharmony_ci return -E2BIG; 424962306a36Sopenharmony_ci } 425062306a36Sopenharmony_ci 425162306a36Sopenharmony_ci key->type = DPKG_EXTRACT_FROM_HDR; 425262306a36Sopenharmony_ci key->extract.from_hdr.prot = dist_fields[i].cls_prot; 425362306a36Sopenharmony_ci key->extract.from_hdr.type = DPKG_FULL_FIELD; 425462306a36Sopenharmony_ci key->extract.from_hdr.field = dist_fields[i].cls_field; 425562306a36Sopenharmony_ci cls_cfg.num_extracts++; 425662306a36Sopenharmony_ci } 425762306a36Sopenharmony_ci 425862306a36Sopenharmony_ci dma_mem = kzalloc(DPAA2_CLASSIFIER_DMA_SIZE, GFP_KERNEL); 425962306a36Sopenharmony_ci if (!dma_mem) 426062306a36Sopenharmony_ci return -ENOMEM; 426162306a36Sopenharmony_ci 426262306a36Sopenharmony_ci err = dpni_prepare_key_cfg(&cls_cfg, dma_mem); 426362306a36Sopenharmony_ci if (err) { 426462306a36Sopenharmony_ci dev_err(dev, "dpni_prepare_key_cfg error %d\n", err); 426562306a36Sopenharmony_ci goto free_key; 426662306a36Sopenharmony_ci } 426762306a36Sopenharmony_ci 426862306a36Sopenharmony_ci /* Prepare for setting the rx dist */ 426962306a36Sopenharmony_ci key_iova = dma_map_single(dev, dma_mem, DPAA2_CLASSIFIER_DMA_SIZE, 427062306a36Sopenharmony_ci DMA_TO_DEVICE); 427162306a36Sopenharmony_ci if (dma_mapping_error(dev, key_iova)) { 427262306a36Sopenharmony_ci dev_err(dev, "DMA mapping failed\n"); 427362306a36Sopenharmony_ci err = -ENOMEM; 427462306a36Sopenharmony_ci goto free_key; 427562306a36Sopenharmony_ci } 427662306a36Sopenharmony_ci 427762306a36Sopenharmony_ci if (type == DPAA2_ETH_RX_DIST_HASH) { 427862306a36Sopenharmony_ci if (dpaa2_eth_has_legacy_dist(priv)) 427962306a36Sopenharmony_ci err = dpaa2_eth_config_legacy_hash_key(priv, key_iova); 428062306a36Sopenharmony_ci else 428162306a36Sopenharmony_ci err = dpaa2_eth_config_hash_key(priv, key_iova); 428262306a36Sopenharmony_ci } else { 428362306a36Sopenharmony_ci err = dpaa2_eth_config_cls_key(priv, key_iova); 428462306a36Sopenharmony_ci } 428562306a36Sopenharmony_ci 428662306a36Sopenharmony_ci dma_unmap_single(dev, key_iova, DPAA2_CLASSIFIER_DMA_SIZE, 428762306a36Sopenharmony_ci DMA_TO_DEVICE); 428862306a36Sopenharmony_ci if (!err && type == DPAA2_ETH_RX_DIST_HASH) 428962306a36Sopenharmony_ci priv->rx_hash_fields = rx_hash_fields; 429062306a36Sopenharmony_ci 429162306a36Sopenharmony_cifree_key: 429262306a36Sopenharmony_ci kfree(dma_mem); 429362306a36Sopenharmony_ci return err; 429462306a36Sopenharmony_ci} 429562306a36Sopenharmony_ci 429662306a36Sopenharmony_ciint dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags) 429762306a36Sopenharmony_ci{ 429862306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 429962306a36Sopenharmony_ci u64 key = 0; 430062306a36Sopenharmony_ci int i; 430162306a36Sopenharmony_ci 430262306a36Sopenharmony_ci if (!dpaa2_eth_hash_enabled(priv)) 430362306a36Sopenharmony_ci return -EOPNOTSUPP; 430462306a36Sopenharmony_ci 430562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dist_fields); i++) 430662306a36Sopenharmony_ci if (dist_fields[i].rxnfc_field & flags) 430762306a36Sopenharmony_ci key |= dist_fields[i].id; 430862306a36Sopenharmony_ci 430962306a36Sopenharmony_ci return dpaa2_eth_set_dist_key(net_dev, DPAA2_ETH_RX_DIST_HASH, key); 431062306a36Sopenharmony_ci} 431162306a36Sopenharmony_ci 431262306a36Sopenharmony_ciint dpaa2_eth_set_cls(struct net_device *net_dev, u64 flags) 431362306a36Sopenharmony_ci{ 431462306a36Sopenharmony_ci return dpaa2_eth_set_dist_key(net_dev, DPAA2_ETH_RX_DIST_CLS, flags); 431562306a36Sopenharmony_ci} 431662306a36Sopenharmony_ci 431762306a36Sopenharmony_cistatic int dpaa2_eth_set_default_cls(struct dpaa2_eth_priv *priv) 431862306a36Sopenharmony_ci{ 431962306a36Sopenharmony_ci struct device *dev = priv->net_dev->dev.parent; 432062306a36Sopenharmony_ci int err; 432162306a36Sopenharmony_ci 432262306a36Sopenharmony_ci /* Check if we actually support Rx flow classification */ 432362306a36Sopenharmony_ci if (dpaa2_eth_has_legacy_dist(priv)) { 432462306a36Sopenharmony_ci dev_dbg(dev, "Rx cls not supported by current MC version\n"); 432562306a36Sopenharmony_ci return -EOPNOTSUPP; 432662306a36Sopenharmony_ci } 432762306a36Sopenharmony_ci 432862306a36Sopenharmony_ci if (!dpaa2_eth_fs_enabled(priv)) { 432962306a36Sopenharmony_ci dev_dbg(dev, "Rx cls disabled in DPNI options\n"); 433062306a36Sopenharmony_ci return -EOPNOTSUPP; 433162306a36Sopenharmony_ci } 433262306a36Sopenharmony_ci 433362306a36Sopenharmony_ci if (!dpaa2_eth_hash_enabled(priv)) { 433462306a36Sopenharmony_ci dev_dbg(dev, "Rx cls disabled for single queue DPNIs\n"); 433562306a36Sopenharmony_ci return -EOPNOTSUPP; 433662306a36Sopenharmony_ci } 433762306a36Sopenharmony_ci 433862306a36Sopenharmony_ci /* If there is no support for masking in the classification table, 433962306a36Sopenharmony_ci * we don't set a default key, as it will depend on the rules 434062306a36Sopenharmony_ci * added by the user at runtime. 434162306a36Sopenharmony_ci */ 434262306a36Sopenharmony_ci if (!dpaa2_eth_fs_mask_enabled(priv)) 434362306a36Sopenharmony_ci goto out; 434462306a36Sopenharmony_ci 434562306a36Sopenharmony_ci err = dpaa2_eth_set_cls(priv->net_dev, DPAA2_ETH_DIST_ALL); 434662306a36Sopenharmony_ci if (err) 434762306a36Sopenharmony_ci return err; 434862306a36Sopenharmony_ci 434962306a36Sopenharmony_ciout: 435062306a36Sopenharmony_ci priv->rx_cls_enabled = 1; 435162306a36Sopenharmony_ci 435262306a36Sopenharmony_ci return 0; 435362306a36Sopenharmony_ci} 435462306a36Sopenharmony_ci 435562306a36Sopenharmony_ci/* Bind the DPNI to its needed objects and resources: buffer pool, DPIOs, 435662306a36Sopenharmony_ci * frame queues and channels 435762306a36Sopenharmony_ci */ 435862306a36Sopenharmony_cistatic int dpaa2_eth_bind_dpni(struct dpaa2_eth_priv *priv) 435962306a36Sopenharmony_ci{ 436062306a36Sopenharmony_ci struct dpaa2_eth_bp *bp = priv->bp[DPAA2_ETH_DEFAULT_BP_IDX]; 436162306a36Sopenharmony_ci struct net_device *net_dev = priv->net_dev; 436262306a36Sopenharmony_ci struct dpni_pools_cfg pools_params = { 0 }; 436362306a36Sopenharmony_ci struct device *dev = net_dev->dev.parent; 436462306a36Sopenharmony_ci struct dpni_error_cfg err_cfg; 436562306a36Sopenharmony_ci int err = 0; 436662306a36Sopenharmony_ci int i; 436762306a36Sopenharmony_ci 436862306a36Sopenharmony_ci pools_params.num_dpbp = 1; 436962306a36Sopenharmony_ci pools_params.pools[0].dpbp_id = bp->dev->obj_desc.id; 437062306a36Sopenharmony_ci pools_params.pools[0].backup_pool = 0; 437162306a36Sopenharmony_ci pools_params.pools[0].buffer_size = priv->rx_buf_size; 437262306a36Sopenharmony_ci err = dpni_set_pools(priv->mc_io, 0, priv->mc_token, &pools_params); 437362306a36Sopenharmony_ci if (err) { 437462306a36Sopenharmony_ci dev_err(dev, "dpni_set_pools() failed\n"); 437562306a36Sopenharmony_ci return err; 437662306a36Sopenharmony_ci } 437762306a36Sopenharmony_ci 437862306a36Sopenharmony_ci /* have the interface implicitly distribute traffic based on 437962306a36Sopenharmony_ci * the default hash key 438062306a36Sopenharmony_ci */ 438162306a36Sopenharmony_ci err = dpaa2_eth_set_hash(net_dev, DPAA2_RXH_DEFAULT); 438262306a36Sopenharmony_ci if (err && err != -EOPNOTSUPP) 438362306a36Sopenharmony_ci dev_err(dev, "Failed to configure hashing\n"); 438462306a36Sopenharmony_ci 438562306a36Sopenharmony_ci /* Configure the flow classification key; it includes all 438662306a36Sopenharmony_ci * supported header fields and cannot be modified at runtime 438762306a36Sopenharmony_ci */ 438862306a36Sopenharmony_ci err = dpaa2_eth_set_default_cls(priv); 438962306a36Sopenharmony_ci if (err && err != -EOPNOTSUPP) 439062306a36Sopenharmony_ci dev_err(dev, "Failed to configure Rx classification key\n"); 439162306a36Sopenharmony_ci 439262306a36Sopenharmony_ci /* Configure handling of error frames */ 439362306a36Sopenharmony_ci err_cfg.errors = DPAA2_FAS_RX_ERR_MASK; 439462306a36Sopenharmony_ci err_cfg.set_frame_annotation = 1; 439562306a36Sopenharmony_ci err_cfg.error_action = DPNI_ERROR_ACTION_DISCARD; 439662306a36Sopenharmony_ci err = dpni_set_errors_behavior(priv->mc_io, 0, priv->mc_token, 439762306a36Sopenharmony_ci &err_cfg); 439862306a36Sopenharmony_ci if (err) { 439962306a36Sopenharmony_ci dev_err(dev, "dpni_set_errors_behavior failed\n"); 440062306a36Sopenharmony_ci return err; 440162306a36Sopenharmony_ci } 440262306a36Sopenharmony_ci 440362306a36Sopenharmony_ci /* Configure Rx and Tx conf queues to generate CDANs */ 440462306a36Sopenharmony_ci for (i = 0; i < priv->num_fqs; i++) { 440562306a36Sopenharmony_ci switch (priv->fq[i].type) { 440662306a36Sopenharmony_ci case DPAA2_RX_FQ: 440762306a36Sopenharmony_ci err = dpaa2_eth_setup_rx_flow(priv, &priv->fq[i]); 440862306a36Sopenharmony_ci break; 440962306a36Sopenharmony_ci case DPAA2_TX_CONF_FQ: 441062306a36Sopenharmony_ci err = dpaa2_eth_setup_tx_flow(priv, &priv->fq[i]); 441162306a36Sopenharmony_ci break; 441262306a36Sopenharmony_ci case DPAA2_RX_ERR_FQ: 441362306a36Sopenharmony_ci err = setup_rx_err_flow(priv, &priv->fq[i]); 441462306a36Sopenharmony_ci break; 441562306a36Sopenharmony_ci default: 441662306a36Sopenharmony_ci dev_err(dev, "Invalid FQ type %d\n", priv->fq[i].type); 441762306a36Sopenharmony_ci return -EINVAL; 441862306a36Sopenharmony_ci } 441962306a36Sopenharmony_ci if (err) 442062306a36Sopenharmony_ci return err; 442162306a36Sopenharmony_ci } 442262306a36Sopenharmony_ci 442362306a36Sopenharmony_ci err = dpni_get_qdid(priv->mc_io, 0, priv->mc_token, 442462306a36Sopenharmony_ci DPNI_QUEUE_TX, &priv->tx_qdid); 442562306a36Sopenharmony_ci if (err) { 442662306a36Sopenharmony_ci dev_err(dev, "dpni_get_qdid() failed\n"); 442762306a36Sopenharmony_ci return err; 442862306a36Sopenharmony_ci } 442962306a36Sopenharmony_ci 443062306a36Sopenharmony_ci return 0; 443162306a36Sopenharmony_ci} 443262306a36Sopenharmony_ci 443362306a36Sopenharmony_ci/* Allocate rings for storing incoming frame descriptors */ 443462306a36Sopenharmony_cistatic int dpaa2_eth_alloc_rings(struct dpaa2_eth_priv *priv) 443562306a36Sopenharmony_ci{ 443662306a36Sopenharmony_ci struct net_device *net_dev = priv->net_dev; 443762306a36Sopenharmony_ci struct device *dev = net_dev->dev.parent; 443862306a36Sopenharmony_ci int i; 443962306a36Sopenharmony_ci 444062306a36Sopenharmony_ci for (i = 0; i < priv->num_channels; i++) { 444162306a36Sopenharmony_ci priv->channel[i]->store = 444262306a36Sopenharmony_ci dpaa2_io_store_create(DPAA2_ETH_STORE_SIZE, dev); 444362306a36Sopenharmony_ci if (!priv->channel[i]->store) { 444462306a36Sopenharmony_ci netdev_err(net_dev, "dpaa2_io_store_create() failed\n"); 444562306a36Sopenharmony_ci goto err_ring; 444662306a36Sopenharmony_ci } 444762306a36Sopenharmony_ci } 444862306a36Sopenharmony_ci 444962306a36Sopenharmony_ci return 0; 445062306a36Sopenharmony_ci 445162306a36Sopenharmony_cierr_ring: 445262306a36Sopenharmony_ci for (i = 0; i < priv->num_channels; i++) { 445362306a36Sopenharmony_ci if (!priv->channel[i]->store) 445462306a36Sopenharmony_ci break; 445562306a36Sopenharmony_ci dpaa2_io_store_destroy(priv->channel[i]->store); 445662306a36Sopenharmony_ci } 445762306a36Sopenharmony_ci 445862306a36Sopenharmony_ci return -ENOMEM; 445962306a36Sopenharmony_ci} 446062306a36Sopenharmony_ci 446162306a36Sopenharmony_cistatic void dpaa2_eth_free_rings(struct dpaa2_eth_priv *priv) 446262306a36Sopenharmony_ci{ 446362306a36Sopenharmony_ci int i; 446462306a36Sopenharmony_ci 446562306a36Sopenharmony_ci for (i = 0; i < priv->num_channels; i++) 446662306a36Sopenharmony_ci dpaa2_io_store_destroy(priv->channel[i]->store); 446762306a36Sopenharmony_ci} 446862306a36Sopenharmony_ci 446962306a36Sopenharmony_cistatic int dpaa2_eth_set_mac_addr(struct dpaa2_eth_priv *priv) 447062306a36Sopenharmony_ci{ 447162306a36Sopenharmony_ci struct net_device *net_dev = priv->net_dev; 447262306a36Sopenharmony_ci struct device *dev = net_dev->dev.parent; 447362306a36Sopenharmony_ci u8 mac_addr[ETH_ALEN], dpni_mac_addr[ETH_ALEN]; 447462306a36Sopenharmony_ci int err; 447562306a36Sopenharmony_ci 447662306a36Sopenharmony_ci /* Get firmware address, if any */ 447762306a36Sopenharmony_ci err = dpni_get_port_mac_addr(priv->mc_io, 0, priv->mc_token, mac_addr); 447862306a36Sopenharmony_ci if (err) { 447962306a36Sopenharmony_ci dev_err(dev, "dpni_get_port_mac_addr() failed\n"); 448062306a36Sopenharmony_ci return err; 448162306a36Sopenharmony_ci } 448262306a36Sopenharmony_ci 448362306a36Sopenharmony_ci /* Get DPNI attributes address, if any */ 448462306a36Sopenharmony_ci err = dpni_get_primary_mac_addr(priv->mc_io, 0, priv->mc_token, 448562306a36Sopenharmony_ci dpni_mac_addr); 448662306a36Sopenharmony_ci if (err) { 448762306a36Sopenharmony_ci dev_err(dev, "dpni_get_primary_mac_addr() failed\n"); 448862306a36Sopenharmony_ci return err; 448962306a36Sopenharmony_ci } 449062306a36Sopenharmony_ci 449162306a36Sopenharmony_ci /* First check if firmware has any address configured by bootloader */ 449262306a36Sopenharmony_ci if (!is_zero_ether_addr(mac_addr)) { 449362306a36Sopenharmony_ci /* If the DPMAC addr != DPNI addr, update it */ 449462306a36Sopenharmony_ci if (!ether_addr_equal(mac_addr, dpni_mac_addr)) { 449562306a36Sopenharmony_ci err = dpni_set_primary_mac_addr(priv->mc_io, 0, 449662306a36Sopenharmony_ci priv->mc_token, 449762306a36Sopenharmony_ci mac_addr); 449862306a36Sopenharmony_ci if (err) { 449962306a36Sopenharmony_ci dev_err(dev, "dpni_set_primary_mac_addr() failed\n"); 450062306a36Sopenharmony_ci return err; 450162306a36Sopenharmony_ci } 450262306a36Sopenharmony_ci } 450362306a36Sopenharmony_ci eth_hw_addr_set(net_dev, mac_addr); 450462306a36Sopenharmony_ci } else if (is_zero_ether_addr(dpni_mac_addr)) { 450562306a36Sopenharmony_ci /* No MAC address configured, fill in net_dev->dev_addr 450662306a36Sopenharmony_ci * with a random one 450762306a36Sopenharmony_ci */ 450862306a36Sopenharmony_ci eth_hw_addr_random(net_dev); 450962306a36Sopenharmony_ci dev_dbg_once(dev, "device(s) have all-zero hwaddr, replaced with random\n"); 451062306a36Sopenharmony_ci 451162306a36Sopenharmony_ci err = dpni_set_primary_mac_addr(priv->mc_io, 0, priv->mc_token, 451262306a36Sopenharmony_ci net_dev->dev_addr); 451362306a36Sopenharmony_ci if (err) { 451462306a36Sopenharmony_ci dev_err(dev, "dpni_set_primary_mac_addr() failed\n"); 451562306a36Sopenharmony_ci return err; 451662306a36Sopenharmony_ci } 451762306a36Sopenharmony_ci 451862306a36Sopenharmony_ci /* Override NET_ADDR_RANDOM set by eth_hw_addr_random(); for all 451962306a36Sopenharmony_ci * practical purposes, this will be our "permanent" mac address, 452062306a36Sopenharmony_ci * at least until the next reboot. This move will also permit 452162306a36Sopenharmony_ci * register_netdevice() to properly fill up net_dev->perm_addr. 452262306a36Sopenharmony_ci */ 452362306a36Sopenharmony_ci net_dev->addr_assign_type = NET_ADDR_PERM; 452462306a36Sopenharmony_ci } else { 452562306a36Sopenharmony_ci /* NET_ADDR_PERM is default, all we have to do is 452662306a36Sopenharmony_ci * fill in the device addr. 452762306a36Sopenharmony_ci */ 452862306a36Sopenharmony_ci eth_hw_addr_set(net_dev, dpni_mac_addr); 452962306a36Sopenharmony_ci } 453062306a36Sopenharmony_ci 453162306a36Sopenharmony_ci return 0; 453262306a36Sopenharmony_ci} 453362306a36Sopenharmony_ci 453462306a36Sopenharmony_cistatic int dpaa2_eth_netdev_init(struct net_device *net_dev) 453562306a36Sopenharmony_ci{ 453662306a36Sopenharmony_ci struct device *dev = net_dev->dev.parent; 453762306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 453862306a36Sopenharmony_ci u32 options = priv->dpni_attrs.options; 453962306a36Sopenharmony_ci u64 supported = 0, not_supported = 0; 454062306a36Sopenharmony_ci u8 bcast_addr[ETH_ALEN]; 454162306a36Sopenharmony_ci u8 num_queues; 454262306a36Sopenharmony_ci int err; 454362306a36Sopenharmony_ci 454462306a36Sopenharmony_ci net_dev->netdev_ops = &dpaa2_eth_ops; 454562306a36Sopenharmony_ci net_dev->ethtool_ops = &dpaa2_ethtool_ops; 454662306a36Sopenharmony_ci 454762306a36Sopenharmony_ci err = dpaa2_eth_set_mac_addr(priv); 454862306a36Sopenharmony_ci if (err) 454962306a36Sopenharmony_ci return err; 455062306a36Sopenharmony_ci 455162306a36Sopenharmony_ci /* Explicitly add the broadcast address to the MAC filtering table */ 455262306a36Sopenharmony_ci eth_broadcast_addr(bcast_addr); 455362306a36Sopenharmony_ci err = dpni_add_mac_addr(priv->mc_io, 0, priv->mc_token, bcast_addr); 455462306a36Sopenharmony_ci if (err) { 455562306a36Sopenharmony_ci dev_err(dev, "dpni_add_mac_addr() failed\n"); 455662306a36Sopenharmony_ci return err; 455762306a36Sopenharmony_ci } 455862306a36Sopenharmony_ci 455962306a36Sopenharmony_ci /* Set MTU upper limit; lower limit is 68B (default value) */ 456062306a36Sopenharmony_ci net_dev->max_mtu = DPAA2_ETH_MAX_MTU; 456162306a36Sopenharmony_ci err = dpni_set_max_frame_length(priv->mc_io, 0, priv->mc_token, 456262306a36Sopenharmony_ci DPAA2_ETH_MFL); 456362306a36Sopenharmony_ci if (err) { 456462306a36Sopenharmony_ci dev_err(dev, "dpni_set_max_frame_length() failed\n"); 456562306a36Sopenharmony_ci return err; 456662306a36Sopenharmony_ci } 456762306a36Sopenharmony_ci 456862306a36Sopenharmony_ci /* Set actual number of queues in the net device */ 456962306a36Sopenharmony_ci num_queues = dpaa2_eth_queue_count(priv); 457062306a36Sopenharmony_ci err = netif_set_real_num_tx_queues(net_dev, num_queues); 457162306a36Sopenharmony_ci if (err) { 457262306a36Sopenharmony_ci dev_err(dev, "netif_set_real_num_tx_queues() failed\n"); 457362306a36Sopenharmony_ci return err; 457462306a36Sopenharmony_ci } 457562306a36Sopenharmony_ci err = netif_set_real_num_rx_queues(net_dev, num_queues); 457662306a36Sopenharmony_ci if (err) { 457762306a36Sopenharmony_ci dev_err(dev, "netif_set_real_num_rx_queues() failed\n"); 457862306a36Sopenharmony_ci return err; 457962306a36Sopenharmony_ci } 458062306a36Sopenharmony_ci 458162306a36Sopenharmony_ci dpaa2_eth_detect_features(priv); 458262306a36Sopenharmony_ci 458362306a36Sopenharmony_ci /* Capabilities listing */ 458462306a36Sopenharmony_ci supported |= IFF_LIVE_ADDR_CHANGE; 458562306a36Sopenharmony_ci 458662306a36Sopenharmony_ci if (options & DPNI_OPT_NO_MAC_FILTER) 458762306a36Sopenharmony_ci not_supported |= IFF_UNICAST_FLT; 458862306a36Sopenharmony_ci else 458962306a36Sopenharmony_ci supported |= IFF_UNICAST_FLT; 459062306a36Sopenharmony_ci 459162306a36Sopenharmony_ci net_dev->priv_flags |= supported; 459262306a36Sopenharmony_ci net_dev->priv_flags &= ~not_supported; 459362306a36Sopenharmony_ci 459462306a36Sopenharmony_ci /* Features */ 459562306a36Sopenharmony_ci net_dev->features = NETIF_F_RXCSUM | 459662306a36Sopenharmony_ci NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | 459762306a36Sopenharmony_ci NETIF_F_SG | NETIF_F_HIGHDMA | 459862306a36Sopenharmony_ci NETIF_F_LLTX | NETIF_F_HW_TC | NETIF_F_TSO; 459962306a36Sopenharmony_ci net_dev->gso_max_segs = DPAA2_ETH_ENQUEUE_MAX_FDS; 460062306a36Sopenharmony_ci net_dev->hw_features = net_dev->features; 460162306a36Sopenharmony_ci net_dev->xdp_features = NETDEV_XDP_ACT_BASIC | 460262306a36Sopenharmony_ci NETDEV_XDP_ACT_REDIRECT | 460362306a36Sopenharmony_ci NETDEV_XDP_ACT_NDO_XMIT; 460462306a36Sopenharmony_ci if (priv->dpni_attrs.wriop_version >= DPAA2_WRIOP_VERSION(3, 0, 0) && 460562306a36Sopenharmony_ci priv->dpni_attrs.num_queues <= 8) 460662306a36Sopenharmony_ci net_dev->xdp_features |= NETDEV_XDP_ACT_XSK_ZEROCOPY; 460762306a36Sopenharmony_ci 460862306a36Sopenharmony_ci if (priv->dpni_attrs.vlan_filter_entries) 460962306a36Sopenharmony_ci net_dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER; 461062306a36Sopenharmony_ci 461162306a36Sopenharmony_ci return 0; 461262306a36Sopenharmony_ci} 461362306a36Sopenharmony_ci 461462306a36Sopenharmony_cistatic int dpaa2_eth_poll_link_state(void *arg) 461562306a36Sopenharmony_ci{ 461662306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)arg; 461762306a36Sopenharmony_ci int err; 461862306a36Sopenharmony_ci 461962306a36Sopenharmony_ci while (!kthread_should_stop()) { 462062306a36Sopenharmony_ci err = dpaa2_eth_link_state_update(priv); 462162306a36Sopenharmony_ci if (unlikely(err)) 462262306a36Sopenharmony_ci return err; 462362306a36Sopenharmony_ci 462462306a36Sopenharmony_ci msleep(DPAA2_ETH_LINK_STATE_REFRESH); 462562306a36Sopenharmony_ci } 462662306a36Sopenharmony_ci 462762306a36Sopenharmony_ci return 0; 462862306a36Sopenharmony_ci} 462962306a36Sopenharmony_ci 463062306a36Sopenharmony_cistatic int dpaa2_eth_connect_mac(struct dpaa2_eth_priv *priv) 463162306a36Sopenharmony_ci{ 463262306a36Sopenharmony_ci struct fsl_mc_device *dpni_dev, *dpmac_dev; 463362306a36Sopenharmony_ci struct dpaa2_mac *mac; 463462306a36Sopenharmony_ci int err; 463562306a36Sopenharmony_ci 463662306a36Sopenharmony_ci dpni_dev = to_fsl_mc_device(priv->net_dev->dev.parent); 463762306a36Sopenharmony_ci dpmac_dev = fsl_mc_get_endpoint(dpni_dev, 0); 463862306a36Sopenharmony_ci 463962306a36Sopenharmony_ci if (PTR_ERR(dpmac_dev) == -EPROBE_DEFER) { 464062306a36Sopenharmony_ci netdev_dbg(priv->net_dev, "waiting for mac\n"); 464162306a36Sopenharmony_ci return PTR_ERR(dpmac_dev); 464262306a36Sopenharmony_ci } 464362306a36Sopenharmony_ci 464462306a36Sopenharmony_ci if (IS_ERR(dpmac_dev) || dpmac_dev->dev.type != &fsl_mc_bus_dpmac_type) 464562306a36Sopenharmony_ci return 0; 464662306a36Sopenharmony_ci 464762306a36Sopenharmony_ci mac = kzalloc(sizeof(struct dpaa2_mac), GFP_KERNEL); 464862306a36Sopenharmony_ci if (!mac) 464962306a36Sopenharmony_ci return -ENOMEM; 465062306a36Sopenharmony_ci 465162306a36Sopenharmony_ci mac->mc_dev = dpmac_dev; 465262306a36Sopenharmony_ci mac->mc_io = priv->mc_io; 465362306a36Sopenharmony_ci mac->net_dev = priv->net_dev; 465462306a36Sopenharmony_ci 465562306a36Sopenharmony_ci err = dpaa2_mac_open(mac); 465662306a36Sopenharmony_ci if (err) 465762306a36Sopenharmony_ci goto err_free_mac; 465862306a36Sopenharmony_ci 465962306a36Sopenharmony_ci if (dpaa2_mac_is_type_phy(mac)) { 466062306a36Sopenharmony_ci err = dpaa2_mac_connect(mac); 466162306a36Sopenharmony_ci if (err) { 466262306a36Sopenharmony_ci if (err == -EPROBE_DEFER) 466362306a36Sopenharmony_ci netdev_dbg(priv->net_dev, 466462306a36Sopenharmony_ci "could not connect to MAC\n"); 466562306a36Sopenharmony_ci else 466662306a36Sopenharmony_ci netdev_err(priv->net_dev, 466762306a36Sopenharmony_ci "Error connecting to the MAC endpoint: %pe", 466862306a36Sopenharmony_ci ERR_PTR(err)); 466962306a36Sopenharmony_ci goto err_close_mac; 467062306a36Sopenharmony_ci } 467162306a36Sopenharmony_ci } 467262306a36Sopenharmony_ci 467362306a36Sopenharmony_ci mutex_lock(&priv->mac_lock); 467462306a36Sopenharmony_ci priv->mac = mac; 467562306a36Sopenharmony_ci mutex_unlock(&priv->mac_lock); 467662306a36Sopenharmony_ci 467762306a36Sopenharmony_ci return 0; 467862306a36Sopenharmony_ci 467962306a36Sopenharmony_cierr_close_mac: 468062306a36Sopenharmony_ci dpaa2_mac_close(mac); 468162306a36Sopenharmony_cierr_free_mac: 468262306a36Sopenharmony_ci kfree(mac); 468362306a36Sopenharmony_ci return err; 468462306a36Sopenharmony_ci} 468562306a36Sopenharmony_ci 468662306a36Sopenharmony_cistatic void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv) 468762306a36Sopenharmony_ci{ 468862306a36Sopenharmony_ci struct dpaa2_mac *mac; 468962306a36Sopenharmony_ci 469062306a36Sopenharmony_ci mutex_lock(&priv->mac_lock); 469162306a36Sopenharmony_ci mac = priv->mac; 469262306a36Sopenharmony_ci priv->mac = NULL; 469362306a36Sopenharmony_ci mutex_unlock(&priv->mac_lock); 469462306a36Sopenharmony_ci 469562306a36Sopenharmony_ci if (!mac) 469662306a36Sopenharmony_ci return; 469762306a36Sopenharmony_ci 469862306a36Sopenharmony_ci if (dpaa2_mac_is_type_phy(mac)) 469962306a36Sopenharmony_ci dpaa2_mac_disconnect(mac); 470062306a36Sopenharmony_ci 470162306a36Sopenharmony_ci dpaa2_mac_close(mac); 470262306a36Sopenharmony_ci kfree(mac); 470362306a36Sopenharmony_ci} 470462306a36Sopenharmony_ci 470562306a36Sopenharmony_cistatic irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg) 470662306a36Sopenharmony_ci{ 470762306a36Sopenharmony_ci u32 status = ~0; 470862306a36Sopenharmony_ci struct device *dev = (struct device *)arg; 470962306a36Sopenharmony_ci struct fsl_mc_device *dpni_dev = to_fsl_mc_device(dev); 471062306a36Sopenharmony_ci struct net_device *net_dev = dev_get_drvdata(dev); 471162306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 471262306a36Sopenharmony_ci bool had_mac; 471362306a36Sopenharmony_ci int err; 471462306a36Sopenharmony_ci 471562306a36Sopenharmony_ci err = dpni_get_irq_status(dpni_dev->mc_io, 0, dpni_dev->mc_handle, 471662306a36Sopenharmony_ci DPNI_IRQ_INDEX, &status); 471762306a36Sopenharmony_ci if (unlikely(err)) { 471862306a36Sopenharmony_ci netdev_err(net_dev, "Can't get irq status (err %d)\n", err); 471962306a36Sopenharmony_ci return IRQ_HANDLED; 472062306a36Sopenharmony_ci } 472162306a36Sopenharmony_ci 472262306a36Sopenharmony_ci if (status & DPNI_IRQ_EVENT_LINK_CHANGED) 472362306a36Sopenharmony_ci dpaa2_eth_link_state_update(netdev_priv(net_dev)); 472462306a36Sopenharmony_ci 472562306a36Sopenharmony_ci if (status & DPNI_IRQ_EVENT_ENDPOINT_CHANGED) { 472662306a36Sopenharmony_ci dpaa2_eth_set_mac_addr(netdev_priv(net_dev)); 472762306a36Sopenharmony_ci dpaa2_eth_update_tx_fqids(priv); 472862306a36Sopenharmony_ci 472962306a36Sopenharmony_ci /* We can avoid locking because the "endpoint changed" IRQ 473062306a36Sopenharmony_ci * handler is the only one who changes priv->mac at runtime, 473162306a36Sopenharmony_ci * so we are not racing with anyone. 473262306a36Sopenharmony_ci */ 473362306a36Sopenharmony_ci had_mac = !!priv->mac; 473462306a36Sopenharmony_ci if (had_mac) 473562306a36Sopenharmony_ci dpaa2_eth_disconnect_mac(priv); 473662306a36Sopenharmony_ci else 473762306a36Sopenharmony_ci dpaa2_eth_connect_mac(priv); 473862306a36Sopenharmony_ci } 473962306a36Sopenharmony_ci 474062306a36Sopenharmony_ci return IRQ_HANDLED; 474162306a36Sopenharmony_ci} 474262306a36Sopenharmony_ci 474362306a36Sopenharmony_cistatic int dpaa2_eth_setup_irqs(struct fsl_mc_device *ls_dev) 474462306a36Sopenharmony_ci{ 474562306a36Sopenharmony_ci int err = 0; 474662306a36Sopenharmony_ci struct fsl_mc_device_irq *irq; 474762306a36Sopenharmony_ci 474862306a36Sopenharmony_ci err = fsl_mc_allocate_irqs(ls_dev); 474962306a36Sopenharmony_ci if (err) { 475062306a36Sopenharmony_ci dev_err(&ls_dev->dev, "MC irqs allocation failed\n"); 475162306a36Sopenharmony_ci return err; 475262306a36Sopenharmony_ci } 475362306a36Sopenharmony_ci 475462306a36Sopenharmony_ci irq = ls_dev->irqs[0]; 475562306a36Sopenharmony_ci err = devm_request_threaded_irq(&ls_dev->dev, irq->virq, 475662306a36Sopenharmony_ci NULL, dpni_irq0_handler_thread, 475762306a36Sopenharmony_ci IRQF_NO_SUSPEND | IRQF_ONESHOT, 475862306a36Sopenharmony_ci dev_name(&ls_dev->dev), &ls_dev->dev); 475962306a36Sopenharmony_ci if (err < 0) { 476062306a36Sopenharmony_ci dev_err(&ls_dev->dev, "devm_request_threaded_irq(): %d\n", err); 476162306a36Sopenharmony_ci goto free_mc_irq; 476262306a36Sopenharmony_ci } 476362306a36Sopenharmony_ci 476462306a36Sopenharmony_ci err = dpni_set_irq_mask(ls_dev->mc_io, 0, ls_dev->mc_handle, 476562306a36Sopenharmony_ci DPNI_IRQ_INDEX, DPNI_IRQ_EVENT_LINK_CHANGED | 476662306a36Sopenharmony_ci DPNI_IRQ_EVENT_ENDPOINT_CHANGED); 476762306a36Sopenharmony_ci if (err < 0) { 476862306a36Sopenharmony_ci dev_err(&ls_dev->dev, "dpni_set_irq_mask(): %d\n", err); 476962306a36Sopenharmony_ci goto free_irq; 477062306a36Sopenharmony_ci } 477162306a36Sopenharmony_ci 477262306a36Sopenharmony_ci err = dpni_set_irq_enable(ls_dev->mc_io, 0, ls_dev->mc_handle, 477362306a36Sopenharmony_ci DPNI_IRQ_INDEX, 1); 477462306a36Sopenharmony_ci if (err < 0) { 477562306a36Sopenharmony_ci dev_err(&ls_dev->dev, "dpni_set_irq_enable(): %d\n", err); 477662306a36Sopenharmony_ci goto free_irq; 477762306a36Sopenharmony_ci } 477862306a36Sopenharmony_ci 477962306a36Sopenharmony_ci return 0; 478062306a36Sopenharmony_ci 478162306a36Sopenharmony_cifree_irq: 478262306a36Sopenharmony_ci devm_free_irq(&ls_dev->dev, irq->virq, &ls_dev->dev); 478362306a36Sopenharmony_cifree_mc_irq: 478462306a36Sopenharmony_ci fsl_mc_free_irqs(ls_dev); 478562306a36Sopenharmony_ci 478662306a36Sopenharmony_ci return err; 478762306a36Sopenharmony_ci} 478862306a36Sopenharmony_ci 478962306a36Sopenharmony_cistatic void dpaa2_eth_add_ch_napi(struct dpaa2_eth_priv *priv) 479062306a36Sopenharmony_ci{ 479162306a36Sopenharmony_ci int i; 479262306a36Sopenharmony_ci struct dpaa2_eth_channel *ch; 479362306a36Sopenharmony_ci 479462306a36Sopenharmony_ci for (i = 0; i < priv->num_channels; i++) { 479562306a36Sopenharmony_ci ch = priv->channel[i]; 479662306a36Sopenharmony_ci /* NAPI weight *MUST* be a multiple of DPAA2_ETH_STORE_SIZE */ 479762306a36Sopenharmony_ci netif_napi_add(priv->net_dev, &ch->napi, dpaa2_eth_poll); 479862306a36Sopenharmony_ci } 479962306a36Sopenharmony_ci} 480062306a36Sopenharmony_ci 480162306a36Sopenharmony_cistatic void dpaa2_eth_del_ch_napi(struct dpaa2_eth_priv *priv) 480262306a36Sopenharmony_ci{ 480362306a36Sopenharmony_ci int i; 480462306a36Sopenharmony_ci struct dpaa2_eth_channel *ch; 480562306a36Sopenharmony_ci 480662306a36Sopenharmony_ci for (i = 0; i < priv->num_channels; i++) { 480762306a36Sopenharmony_ci ch = priv->channel[i]; 480862306a36Sopenharmony_ci netif_napi_del(&ch->napi); 480962306a36Sopenharmony_ci } 481062306a36Sopenharmony_ci} 481162306a36Sopenharmony_ci 481262306a36Sopenharmony_cistatic int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev) 481362306a36Sopenharmony_ci{ 481462306a36Sopenharmony_ci struct device *dev; 481562306a36Sopenharmony_ci struct net_device *net_dev = NULL; 481662306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = NULL; 481762306a36Sopenharmony_ci int err = 0; 481862306a36Sopenharmony_ci 481962306a36Sopenharmony_ci dev = &dpni_dev->dev; 482062306a36Sopenharmony_ci 482162306a36Sopenharmony_ci /* Net device */ 482262306a36Sopenharmony_ci net_dev = alloc_etherdev_mq(sizeof(*priv), DPAA2_ETH_MAX_NETDEV_QUEUES); 482362306a36Sopenharmony_ci if (!net_dev) { 482462306a36Sopenharmony_ci dev_err(dev, "alloc_etherdev_mq() failed\n"); 482562306a36Sopenharmony_ci return -ENOMEM; 482662306a36Sopenharmony_ci } 482762306a36Sopenharmony_ci 482862306a36Sopenharmony_ci SET_NETDEV_DEV(net_dev, dev); 482962306a36Sopenharmony_ci dev_set_drvdata(dev, net_dev); 483062306a36Sopenharmony_ci 483162306a36Sopenharmony_ci priv = netdev_priv(net_dev); 483262306a36Sopenharmony_ci priv->net_dev = net_dev; 483362306a36Sopenharmony_ci SET_NETDEV_DEVLINK_PORT(net_dev, &priv->devlink_port); 483462306a36Sopenharmony_ci 483562306a36Sopenharmony_ci mutex_init(&priv->mac_lock); 483662306a36Sopenharmony_ci 483762306a36Sopenharmony_ci priv->iommu_domain = iommu_get_domain_for_dev(dev); 483862306a36Sopenharmony_ci 483962306a36Sopenharmony_ci priv->tx_tstamp_type = HWTSTAMP_TX_OFF; 484062306a36Sopenharmony_ci priv->rx_tstamp = false; 484162306a36Sopenharmony_ci 484262306a36Sopenharmony_ci priv->dpaa2_ptp_wq = alloc_workqueue("dpaa2_ptp_wq", 0, 0); 484362306a36Sopenharmony_ci if (!priv->dpaa2_ptp_wq) { 484462306a36Sopenharmony_ci err = -ENOMEM; 484562306a36Sopenharmony_ci goto err_wq_alloc; 484662306a36Sopenharmony_ci } 484762306a36Sopenharmony_ci 484862306a36Sopenharmony_ci INIT_WORK(&priv->tx_onestep_tstamp, dpaa2_eth_tx_onestep_tstamp); 484962306a36Sopenharmony_ci mutex_init(&priv->onestep_tstamp_lock); 485062306a36Sopenharmony_ci skb_queue_head_init(&priv->tx_skbs); 485162306a36Sopenharmony_ci 485262306a36Sopenharmony_ci priv->rx_copybreak = DPAA2_ETH_DEFAULT_COPYBREAK; 485362306a36Sopenharmony_ci 485462306a36Sopenharmony_ci /* Obtain a MC portal */ 485562306a36Sopenharmony_ci err = fsl_mc_portal_allocate(dpni_dev, FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, 485662306a36Sopenharmony_ci &priv->mc_io); 485762306a36Sopenharmony_ci if (err) { 485862306a36Sopenharmony_ci if (err == -ENXIO) { 485962306a36Sopenharmony_ci dev_dbg(dev, "waiting for MC portal\n"); 486062306a36Sopenharmony_ci err = -EPROBE_DEFER; 486162306a36Sopenharmony_ci } else { 486262306a36Sopenharmony_ci dev_err(dev, "MC portal allocation failed\n"); 486362306a36Sopenharmony_ci } 486462306a36Sopenharmony_ci goto err_portal_alloc; 486562306a36Sopenharmony_ci } 486662306a36Sopenharmony_ci 486762306a36Sopenharmony_ci /* MC objects initialization and configuration */ 486862306a36Sopenharmony_ci err = dpaa2_eth_setup_dpni(dpni_dev); 486962306a36Sopenharmony_ci if (err) 487062306a36Sopenharmony_ci goto err_dpni_setup; 487162306a36Sopenharmony_ci 487262306a36Sopenharmony_ci err = dpaa2_eth_setup_dpio(priv); 487362306a36Sopenharmony_ci if (err) 487462306a36Sopenharmony_ci goto err_dpio_setup; 487562306a36Sopenharmony_ci 487662306a36Sopenharmony_ci dpaa2_eth_setup_fqs(priv); 487762306a36Sopenharmony_ci 487862306a36Sopenharmony_ci err = dpaa2_eth_setup_default_dpbp(priv); 487962306a36Sopenharmony_ci if (err) 488062306a36Sopenharmony_ci goto err_dpbp_setup; 488162306a36Sopenharmony_ci 488262306a36Sopenharmony_ci err = dpaa2_eth_bind_dpni(priv); 488362306a36Sopenharmony_ci if (err) 488462306a36Sopenharmony_ci goto err_bind; 488562306a36Sopenharmony_ci 488662306a36Sopenharmony_ci /* Add a NAPI context for each channel */ 488762306a36Sopenharmony_ci dpaa2_eth_add_ch_napi(priv); 488862306a36Sopenharmony_ci 488962306a36Sopenharmony_ci /* Percpu statistics */ 489062306a36Sopenharmony_ci priv->percpu_stats = alloc_percpu(*priv->percpu_stats); 489162306a36Sopenharmony_ci if (!priv->percpu_stats) { 489262306a36Sopenharmony_ci dev_err(dev, "alloc_percpu(percpu_stats) failed\n"); 489362306a36Sopenharmony_ci err = -ENOMEM; 489462306a36Sopenharmony_ci goto err_alloc_percpu_stats; 489562306a36Sopenharmony_ci } 489662306a36Sopenharmony_ci priv->percpu_extras = alloc_percpu(*priv->percpu_extras); 489762306a36Sopenharmony_ci if (!priv->percpu_extras) { 489862306a36Sopenharmony_ci dev_err(dev, "alloc_percpu(percpu_extras) failed\n"); 489962306a36Sopenharmony_ci err = -ENOMEM; 490062306a36Sopenharmony_ci goto err_alloc_percpu_extras; 490162306a36Sopenharmony_ci } 490262306a36Sopenharmony_ci 490362306a36Sopenharmony_ci priv->sgt_cache = alloc_percpu(*priv->sgt_cache); 490462306a36Sopenharmony_ci if (!priv->sgt_cache) { 490562306a36Sopenharmony_ci dev_err(dev, "alloc_percpu(sgt_cache) failed\n"); 490662306a36Sopenharmony_ci err = -ENOMEM; 490762306a36Sopenharmony_ci goto err_alloc_sgt_cache; 490862306a36Sopenharmony_ci } 490962306a36Sopenharmony_ci 491062306a36Sopenharmony_ci priv->fd = alloc_percpu(*priv->fd); 491162306a36Sopenharmony_ci if (!priv->fd) { 491262306a36Sopenharmony_ci dev_err(dev, "alloc_percpu(fds) failed\n"); 491362306a36Sopenharmony_ci err = -ENOMEM; 491462306a36Sopenharmony_ci goto err_alloc_fds; 491562306a36Sopenharmony_ci } 491662306a36Sopenharmony_ci 491762306a36Sopenharmony_ci err = dpaa2_eth_netdev_init(net_dev); 491862306a36Sopenharmony_ci if (err) 491962306a36Sopenharmony_ci goto err_netdev_init; 492062306a36Sopenharmony_ci 492162306a36Sopenharmony_ci /* Configure checksum offload based on current interface flags */ 492262306a36Sopenharmony_ci err = dpaa2_eth_set_rx_csum(priv, !!(net_dev->features & NETIF_F_RXCSUM)); 492362306a36Sopenharmony_ci if (err) 492462306a36Sopenharmony_ci goto err_csum; 492562306a36Sopenharmony_ci 492662306a36Sopenharmony_ci err = dpaa2_eth_set_tx_csum(priv, 492762306a36Sopenharmony_ci !!(net_dev->features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))); 492862306a36Sopenharmony_ci if (err) 492962306a36Sopenharmony_ci goto err_csum; 493062306a36Sopenharmony_ci 493162306a36Sopenharmony_ci err = dpaa2_eth_alloc_rings(priv); 493262306a36Sopenharmony_ci if (err) 493362306a36Sopenharmony_ci goto err_alloc_rings; 493462306a36Sopenharmony_ci 493562306a36Sopenharmony_ci#ifdef CONFIG_FSL_DPAA2_ETH_DCB 493662306a36Sopenharmony_ci if (dpaa2_eth_has_pause_support(priv) && priv->vlan_cls_enabled) { 493762306a36Sopenharmony_ci priv->dcbx_mode = DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE; 493862306a36Sopenharmony_ci net_dev->dcbnl_ops = &dpaa2_eth_dcbnl_ops; 493962306a36Sopenharmony_ci } else { 494062306a36Sopenharmony_ci dev_dbg(dev, "PFC not supported\n"); 494162306a36Sopenharmony_ci } 494262306a36Sopenharmony_ci#endif 494362306a36Sopenharmony_ci 494462306a36Sopenharmony_ci err = dpaa2_eth_connect_mac(priv); 494562306a36Sopenharmony_ci if (err) 494662306a36Sopenharmony_ci goto err_connect_mac; 494762306a36Sopenharmony_ci 494862306a36Sopenharmony_ci err = dpaa2_eth_setup_irqs(dpni_dev); 494962306a36Sopenharmony_ci if (err) { 495062306a36Sopenharmony_ci netdev_warn(net_dev, "Failed to set link interrupt, fall back to polling\n"); 495162306a36Sopenharmony_ci priv->poll_thread = kthread_run(dpaa2_eth_poll_link_state, priv, 495262306a36Sopenharmony_ci "%s_poll_link", net_dev->name); 495362306a36Sopenharmony_ci if (IS_ERR(priv->poll_thread)) { 495462306a36Sopenharmony_ci dev_err(dev, "Error starting polling thread\n"); 495562306a36Sopenharmony_ci goto err_poll_thread; 495662306a36Sopenharmony_ci } 495762306a36Sopenharmony_ci priv->do_link_poll = true; 495862306a36Sopenharmony_ci } 495962306a36Sopenharmony_ci 496062306a36Sopenharmony_ci err = dpaa2_eth_dl_alloc(priv); 496162306a36Sopenharmony_ci if (err) 496262306a36Sopenharmony_ci goto err_dl_register; 496362306a36Sopenharmony_ci 496462306a36Sopenharmony_ci err = dpaa2_eth_dl_traps_register(priv); 496562306a36Sopenharmony_ci if (err) 496662306a36Sopenharmony_ci goto err_dl_trap_register; 496762306a36Sopenharmony_ci 496862306a36Sopenharmony_ci err = dpaa2_eth_dl_port_add(priv); 496962306a36Sopenharmony_ci if (err) 497062306a36Sopenharmony_ci goto err_dl_port_add; 497162306a36Sopenharmony_ci 497262306a36Sopenharmony_ci net_dev->needed_headroom = DPAA2_ETH_SWA_SIZE + DPAA2_ETH_TX_BUF_ALIGN; 497362306a36Sopenharmony_ci 497462306a36Sopenharmony_ci err = register_netdev(net_dev); 497562306a36Sopenharmony_ci if (err < 0) { 497662306a36Sopenharmony_ci dev_err(dev, "register_netdev() failed\n"); 497762306a36Sopenharmony_ci goto err_netdev_reg; 497862306a36Sopenharmony_ci } 497962306a36Sopenharmony_ci 498062306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 498162306a36Sopenharmony_ci dpaa2_dbg_add(priv); 498262306a36Sopenharmony_ci#endif 498362306a36Sopenharmony_ci 498462306a36Sopenharmony_ci dpaa2_eth_dl_register(priv); 498562306a36Sopenharmony_ci dev_info(dev, "Probed interface %s\n", net_dev->name); 498662306a36Sopenharmony_ci return 0; 498762306a36Sopenharmony_ci 498862306a36Sopenharmony_cierr_netdev_reg: 498962306a36Sopenharmony_ci dpaa2_eth_dl_port_del(priv); 499062306a36Sopenharmony_cierr_dl_port_add: 499162306a36Sopenharmony_ci dpaa2_eth_dl_traps_unregister(priv); 499262306a36Sopenharmony_cierr_dl_trap_register: 499362306a36Sopenharmony_ci dpaa2_eth_dl_free(priv); 499462306a36Sopenharmony_cierr_dl_register: 499562306a36Sopenharmony_ci if (priv->do_link_poll) 499662306a36Sopenharmony_ci kthread_stop(priv->poll_thread); 499762306a36Sopenharmony_ci else 499862306a36Sopenharmony_ci fsl_mc_free_irqs(dpni_dev); 499962306a36Sopenharmony_cierr_poll_thread: 500062306a36Sopenharmony_ci dpaa2_eth_disconnect_mac(priv); 500162306a36Sopenharmony_cierr_connect_mac: 500262306a36Sopenharmony_ci dpaa2_eth_free_rings(priv); 500362306a36Sopenharmony_cierr_alloc_rings: 500462306a36Sopenharmony_cierr_csum: 500562306a36Sopenharmony_cierr_netdev_init: 500662306a36Sopenharmony_ci free_percpu(priv->fd); 500762306a36Sopenharmony_cierr_alloc_fds: 500862306a36Sopenharmony_ci free_percpu(priv->sgt_cache); 500962306a36Sopenharmony_cierr_alloc_sgt_cache: 501062306a36Sopenharmony_ci free_percpu(priv->percpu_extras); 501162306a36Sopenharmony_cierr_alloc_percpu_extras: 501262306a36Sopenharmony_ci free_percpu(priv->percpu_stats); 501362306a36Sopenharmony_cierr_alloc_percpu_stats: 501462306a36Sopenharmony_ci dpaa2_eth_del_ch_napi(priv); 501562306a36Sopenharmony_cierr_bind: 501662306a36Sopenharmony_ci dpaa2_eth_free_dpbps(priv); 501762306a36Sopenharmony_cierr_dpbp_setup: 501862306a36Sopenharmony_ci dpaa2_eth_free_dpio(priv); 501962306a36Sopenharmony_cierr_dpio_setup: 502062306a36Sopenharmony_ci dpaa2_eth_free_dpni(priv); 502162306a36Sopenharmony_cierr_dpni_setup: 502262306a36Sopenharmony_ci fsl_mc_portal_free(priv->mc_io); 502362306a36Sopenharmony_cierr_portal_alloc: 502462306a36Sopenharmony_ci destroy_workqueue(priv->dpaa2_ptp_wq); 502562306a36Sopenharmony_cierr_wq_alloc: 502662306a36Sopenharmony_ci dev_set_drvdata(dev, NULL); 502762306a36Sopenharmony_ci free_netdev(net_dev); 502862306a36Sopenharmony_ci 502962306a36Sopenharmony_ci return err; 503062306a36Sopenharmony_ci} 503162306a36Sopenharmony_ci 503262306a36Sopenharmony_cistatic void dpaa2_eth_remove(struct fsl_mc_device *ls_dev) 503362306a36Sopenharmony_ci{ 503462306a36Sopenharmony_ci struct device *dev; 503562306a36Sopenharmony_ci struct net_device *net_dev; 503662306a36Sopenharmony_ci struct dpaa2_eth_priv *priv; 503762306a36Sopenharmony_ci 503862306a36Sopenharmony_ci dev = &ls_dev->dev; 503962306a36Sopenharmony_ci net_dev = dev_get_drvdata(dev); 504062306a36Sopenharmony_ci priv = netdev_priv(net_dev); 504162306a36Sopenharmony_ci 504262306a36Sopenharmony_ci dpaa2_eth_dl_unregister(priv); 504362306a36Sopenharmony_ci 504462306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 504562306a36Sopenharmony_ci dpaa2_dbg_remove(priv); 504662306a36Sopenharmony_ci#endif 504762306a36Sopenharmony_ci 504862306a36Sopenharmony_ci unregister_netdev(net_dev); 504962306a36Sopenharmony_ci 505062306a36Sopenharmony_ci dpaa2_eth_dl_port_del(priv); 505162306a36Sopenharmony_ci dpaa2_eth_dl_traps_unregister(priv); 505262306a36Sopenharmony_ci dpaa2_eth_dl_free(priv); 505362306a36Sopenharmony_ci 505462306a36Sopenharmony_ci if (priv->do_link_poll) 505562306a36Sopenharmony_ci kthread_stop(priv->poll_thread); 505662306a36Sopenharmony_ci else 505762306a36Sopenharmony_ci fsl_mc_free_irqs(ls_dev); 505862306a36Sopenharmony_ci 505962306a36Sopenharmony_ci dpaa2_eth_disconnect_mac(priv); 506062306a36Sopenharmony_ci dpaa2_eth_free_rings(priv); 506162306a36Sopenharmony_ci free_percpu(priv->fd); 506262306a36Sopenharmony_ci free_percpu(priv->sgt_cache); 506362306a36Sopenharmony_ci free_percpu(priv->percpu_stats); 506462306a36Sopenharmony_ci free_percpu(priv->percpu_extras); 506562306a36Sopenharmony_ci 506662306a36Sopenharmony_ci dpaa2_eth_del_ch_napi(priv); 506762306a36Sopenharmony_ci dpaa2_eth_free_dpbps(priv); 506862306a36Sopenharmony_ci dpaa2_eth_free_dpio(priv); 506962306a36Sopenharmony_ci dpaa2_eth_free_dpni(priv); 507062306a36Sopenharmony_ci if (priv->onestep_reg_base) 507162306a36Sopenharmony_ci iounmap(priv->onestep_reg_base); 507262306a36Sopenharmony_ci 507362306a36Sopenharmony_ci fsl_mc_portal_free(priv->mc_io); 507462306a36Sopenharmony_ci 507562306a36Sopenharmony_ci destroy_workqueue(priv->dpaa2_ptp_wq); 507662306a36Sopenharmony_ci 507762306a36Sopenharmony_ci dev_dbg(net_dev->dev.parent, "Removed interface %s\n", net_dev->name); 507862306a36Sopenharmony_ci 507962306a36Sopenharmony_ci free_netdev(net_dev); 508062306a36Sopenharmony_ci} 508162306a36Sopenharmony_ci 508262306a36Sopenharmony_cistatic const struct fsl_mc_device_id dpaa2_eth_match_id_table[] = { 508362306a36Sopenharmony_ci { 508462306a36Sopenharmony_ci .vendor = FSL_MC_VENDOR_FREESCALE, 508562306a36Sopenharmony_ci .obj_type = "dpni", 508662306a36Sopenharmony_ci }, 508762306a36Sopenharmony_ci { .vendor = 0x0 } 508862306a36Sopenharmony_ci}; 508962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(fslmc, dpaa2_eth_match_id_table); 509062306a36Sopenharmony_ci 509162306a36Sopenharmony_cistatic struct fsl_mc_driver dpaa2_eth_driver = { 509262306a36Sopenharmony_ci .driver = { 509362306a36Sopenharmony_ci .name = KBUILD_MODNAME, 509462306a36Sopenharmony_ci }, 509562306a36Sopenharmony_ci .probe = dpaa2_eth_probe, 509662306a36Sopenharmony_ci .remove = dpaa2_eth_remove, 509762306a36Sopenharmony_ci .match_id_table = dpaa2_eth_match_id_table 509862306a36Sopenharmony_ci}; 509962306a36Sopenharmony_ci 510062306a36Sopenharmony_cistatic int __init dpaa2_eth_driver_init(void) 510162306a36Sopenharmony_ci{ 510262306a36Sopenharmony_ci int err; 510362306a36Sopenharmony_ci 510462306a36Sopenharmony_ci dpaa2_eth_dbg_init(); 510562306a36Sopenharmony_ci err = fsl_mc_driver_register(&dpaa2_eth_driver); 510662306a36Sopenharmony_ci if (err) { 510762306a36Sopenharmony_ci dpaa2_eth_dbg_exit(); 510862306a36Sopenharmony_ci return err; 510962306a36Sopenharmony_ci } 511062306a36Sopenharmony_ci 511162306a36Sopenharmony_ci return 0; 511262306a36Sopenharmony_ci} 511362306a36Sopenharmony_ci 511462306a36Sopenharmony_cistatic void __exit dpaa2_eth_driver_exit(void) 511562306a36Sopenharmony_ci{ 511662306a36Sopenharmony_ci dpaa2_eth_dbg_exit(); 511762306a36Sopenharmony_ci fsl_mc_driver_unregister(&dpaa2_eth_driver); 511862306a36Sopenharmony_ci} 511962306a36Sopenharmony_ci 512062306a36Sopenharmony_cimodule_init(dpaa2_eth_driver_init); 512162306a36Sopenharmony_cimodule_exit(dpaa2_eth_driver_exit); 5122