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