162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright (C) 2021 Gerhard Engleder <gerhard@engleder-embedded.com> */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci/* TSN endpoint Ethernet MAC driver
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * The TSN endpoint Ethernet MAC is a FPGA based network device for real-time
762306a36Sopenharmony_ci * communication. It is designed for endpoints within TSN (Time Sensitive
862306a36Sopenharmony_ci * Networking) networks; e.g., for PLCs in the industrial automation case.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * It supports multiple TX/RX queue pairs. The first TX/RX queue pair is used
1162306a36Sopenharmony_ci * by the driver.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * More information can be found here:
1462306a36Sopenharmony_ci * - www.embedded-experts.at/tsn
1562306a36Sopenharmony_ci * - www.engleder-embedded.com
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "tsnep.h"
1962306a36Sopenharmony_ci#include "tsnep_hw.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/module.h>
2262306a36Sopenharmony_ci#include <linux/of.h>
2362306a36Sopenharmony_ci#include <linux/of_net.h>
2462306a36Sopenharmony_ci#include <linux/of_mdio.h>
2562306a36Sopenharmony_ci#include <linux/interrupt.h>
2662306a36Sopenharmony_ci#include <linux/etherdevice.h>
2762306a36Sopenharmony_ci#include <linux/phy.h>
2862306a36Sopenharmony_ci#include <linux/iopoll.h>
2962306a36Sopenharmony_ci#include <linux/bpf.h>
3062306a36Sopenharmony_ci#include <linux/bpf_trace.h>
3162306a36Sopenharmony_ci#include <net/page_pool/helpers.h>
3262306a36Sopenharmony_ci#include <net/xdp_sock_drv.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define TSNEP_RX_OFFSET (max(NET_SKB_PAD, XDP_PACKET_HEADROOM) + NET_IP_ALIGN)
3562306a36Sopenharmony_ci#define TSNEP_HEADROOM ALIGN(TSNEP_RX_OFFSET, 4)
3662306a36Sopenharmony_ci#define TSNEP_MAX_RX_BUF_SIZE (PAGE_SIZE - TSNEP_HEADROOM - \
3762306a36Sopenharmony_ci			       SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
3862306a36Sopenharmony_ci/* XSK buffer shall store at least Q-in-Q frame */
3962306a36Sopenharmony_ci#define TSNEP_XSK_RX_BUF_SIZE (ALIGN(TSNEP_RX_INLINE_METADATA_SIZE + \
4062306a36Sopenharmony_ci				     ETH_FRAME_LEN + ETH_FCS_LEN + \
4162306a36Sopenharmony_ci				     VLAN_HLEN * 2, 4))
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
4462306a36Sopenharmony_ci#define DMA_ADDR_HIGH(dma_addr) ((u32)(((dma_addr) >> 32) & 0xFFFFFFFF))
4562306a36Sopenharmony_ci#else
4662306a36Sopenharmony_ci#define DMA_ADDR_HIGH(dma_addr) ((u32)(0))
4762306a36Sopenharmony_ci#endif
4862306a36Sopenharmony_ci#define DMA_ADDR_LOW(dma_addr) ((u32)((dma_addr) & 0xFFFFFFFF))
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#define TSNEP_COALESCE_USECS_DEFAULT 64
5162306a36Sopenharmony_ci#define TSNEP_COALESCE_USECS_MAX     ((ECM_INT_DELAY_MASK >> ECM_INT_DELAY_SHIFT) * \
5262306a36Sopenharmony_ci				      ECM_INT_DELAY_BASE_US + ECM_INT_DELAY_BASE_US - 1)
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#define TSNEP_TX_TYPE_SKB	BIT(0)
5562306a36Sopenharmony_ci#define TSNEP_TX_TYPE_SKB_FRAG	BIT(1)
5662306a36Sopenharmony_ci#define TSNEP_TX_TYPE_XDP_TX	BIT(2)
5762306a36Sopenharmony_ci#define TSNEP_TX_TYPE_XDP_NDO	BIT(3)
5862306a36Sopenharmony_ci#define TSNEP_TX_TYPE_XDP	(TSNEP_TX_TYPE_XDP_TX | TSNEP_TX_TYPE_XDP_NDO)
5962306a36Sopenharmony_ci#define TSNEP_TX_TYPE_XSK	BIT(4)
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define TSNEP_XDP_TX		BIT(0)
6262306a36Sopenharmony_ci#define TSNEP_XDP_REDIRECT	BIT(1)
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic void tsnep_enable_irq(struct tsnep_adapter *adapter, u32 mask)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	iowrite32(mask, adapter->addr + ECM_INT_ENABLE);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic void tsnep_disable_irq(struct tsnep_adapter *adapter, u32 mask)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	mask |= ECM_INT_DISABLE;
7262306a36Sopenharmony_ci	iowrite32(mask, adapter->addr + ECM_INT_ENABLE);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic irqreturn_t tsnep_irq(int irq, void *arg)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct tsnep_adapter *adapter = arg;
7862306a36Sopenharmony_ci	u32 active = ioread32(adapter->addr + ECM_INT_ACTIVE);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/* acknowledge interrupt */
8162306a36Sopenharmony_ci	if (active != 0)
8262306a36Sopenharmony_ci		iowrite32(active, adapter->addr + ECM_INT_ACKNOWLEDGE);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/* handle link interrupt */
8562306a36Sopenharmony_ci	if ((active & ECM_INT_LINK) != 0)
8662306a36Sopenharmony_ci		phy_mac_interrupt(adapter->netdev->phydev);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/* handle TX/RX queue 0 interrupt */
8962306a36Sopenharmony_ci	if ((active & adapter->queue[0].irq_mask) != 0) {
9062306a36Sopenharmony_ci		if (napi_schedule_prep(&adapter->queue[0].napi)) {
9162306a36Sopenharmony_ci			tsnep_disable_irq(adapter, adapter->queue[0].irq_mask);
9262306a36Sopenharmony_ci			/* schedule after masking to avoid races */
9362306a36Sopenharmony_ci			__napi_schedule(&adapter->queue[0].napi);
9462306a36Sopenharmony_ci		}
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return IRQ_HANDLED;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic irqreturn_t tsnep_irq_txrx(int irq, void *arg)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct tsnep_queue *queue = arg;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/* handle TX/RX queue interrupt */
10562306a36Sopenharmony_ci	if (napi_schedule_prep(&queue->napi)) {
10662306a36Sopenharmony_ci		tsnep_disable_irq(queue->adapter, queue->irq_mask);
10762306a36Sopenharmony_ci		/* schedule after masking to avoid races */
10862306a36Sopenharmony_ci		__napi_schedule(&queue->napi);
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return IRQ_HANDLED;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ciint tsnep_set_irq_coalesce(struct tsnep_queue *queue, u32 usecs)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	if (usecs > TSNEP_COALESCE_USECS_MAX)
11762306a36Sopenharmony_ci		return -ERANGE;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	usecs /= ECM_INT_DELAY_BASE_US;
12062306a36Sopenharmony_ci	usecs <<= ECM_INT_DELAY_SHIFT;
12162306a36Sopenharmony_ci	usecs &= ECM_INT_DELAY_MASK;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	queue->irq_delay &= ~ECM_INT_DELAY_MASK;
12462306a36Sopenharmony_ci	queue->irq_delay |= usecs;
12562306a36Sopenharmony_ci	iowrite8(queue->irq_delay, queue->irq_delay_addr);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return 0;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ciu32 tsnep_get_irq_coalesce(struct tsnep_queue *queue)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	u32 usecs;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	usecs = (queue->irq_delay & ECM_INT_DELAY_MASK);
13562306a36Sopenharmony_ci	usecs >>= ECM_INT_DELAY_SHIFT;
13662306a36Sopenharmony_ci	usecs *= ECM_INT_DELAY_BASE_US;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return usecs;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic int tsnep_mdiobus_read(struct mii_bus *bus, int addr, int regnum)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	struct tsnep_adapter *adapter = bus->priv;
14462306a36Sopenharmony_ci	u32 md;
14562306a36Sopenharmony_ci	int retval;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	md = ECM_MD_READ;
14862306a36Sopenharmony_ci	if (!adapter->suppress_preamble)
14962306a36Sopenharmony_ci		md |= ECM_MD_PREAMBLE;
15062306a36Sopenharmony_ci	md |= (regnum << ECM_MD_ADDR_SHIFT) & ECM_MD_ADDR_MASK;
15162306a36Sopenharmony_ci	md |= (addr << ECM_MD_PHY_ADDR_SHIFT) & ECM_MD_PHY_ADDR_MASK;
15262306a36Sopenharmony_ci	iowrite32(md, adapter->addr + ECM_MD_CONTROL);
15362306a36Sopenharmony_ci	retval = readl_poll_timeout_atomic(adapter->addr + ECM_MD_STATUS, md,
15462306a36Sopenharmony_ci					   !(md & ECM_MD_BUSY), 16, 1000);
15562306a36Sopenharmony_ci	if (retval != 0)
15662306a36Sopenharmony_ci		return retval;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return (md & ECM_MD_DATA_MASK) >> ECM_MD_DATA_SHIFT;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic int tsnep_mdiobus_write(struct mii_bus *bus, int addr, int regnum,
16262306a36Sopenharmony_ci			       u16 val)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct tsnep_adapter *adapter = bus->priv;
16562306a36Sopenharmony_ci	u32 md;
16662306a36Sopenharmony_ci	int retval;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	md = ECM_MD_WRITE;
16962306a36Sopenharmony_ci	if (!adapter->suppress_preamble)
17062306a36Sopenharmony_ci		md |= ECM_MD_PREAMBLE;
17162306a36Sopenharmony_ci	md |= (regnum << ECM_MD_ADDR_SHIFT) & ECM_MD_ADDR_MASK;
17262306a36Sopenharmony_ci	md |= (addr << ECM_MD_PHY_ADDR_SHIFT) & ECM_MD_PHY_ADDR_MASK;
17362306a36Sopenharmony_ci	md |= ((u32)val << ECM_MD_DATA_SHIFT) & ECM_MD_DATA_MASK;
17462306a36Sopenharmony_ci	iowrite32(md, adapter->addr + ECM_MD_CONTROL);
17562306a36Sopenharmony_ci	retval = readl_poll_timeout_atomic(adapter->addr + ECM_MD_STATUS, md,
17662306a36Sopenharmony_ci					   !(md & ECM_MD_BUSY), 16, 1000);
17762306a36Sopenharmony_ci	if (retval != 0)
17862306a36Sopenharmony_ci		return retval;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	return 0;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic void tsnep_set_link_mode(struct tsnep_adapter *adapter)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	u32 mode;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	switch (adapter->phydev->speed) {
18862306a36Sopenharmony_ci	case SPEED_100:
18962306a36Sopenharmony_ci		mode = ECM_LINK_MODE_100;
19062306a36Sopenharmony_ci		break;
19162306a36Sopenharmony_ci	case SPEED_1000:
19262306a36Sopenharmony_ci		mode = ECM_LINK_MODE_1000;
19362306a36Sopenharmony_ci		break;
19462306a36Sopenharmony_ci	default:
19562306a36Sopenharmony_ci		mode = ECM_LINK_MODE_OFF;
19662306a36Sopenharmony_ci		break;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci	iowrite32(mode, adapter->addr + ECM_STATUS);
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic void tsnep_phy_link_status_change(struct net_device *netdev)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
20462306a36Sopenharmony_ci	struct phy_device *phydev = netdev->phydev;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	if (phydev->link)
20762306a36Sopenharmony_ci		tsnep_set_link_mode(adapter);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	phy_print_status(netdev->phydev);
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic int tsnep_phy_loopback(struct tsnep_adapter *adapter, bool enable)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	int retval;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	retval = phy_loopback(adapter->phydev, enable);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	/* PHY link state change is not signaled if loopback is enabled, it
21962306a36Sopenharmony_ci	 * would delay a working loopback anyway, let's ensure that loopback
22062306a36Sopenharmony_ci	 * is working immediately by setting link mode directly
22162306a36Sopenharmony_ci	 */
22262306a36Sopenharmony_ci	if (!retval && enable)
22362306a36Sopenharmony_ci		tsnep_set_link_mode(adapter);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	return retval;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic int tsnep_phy_open(struct tsnep_adapter *adapter)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct phy_device *phydev;
23162306a36Sopenharmony_ci	struct ethtool_eee ethtool_eee;
23262306a36Sopenharmony_ci	int retval;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	retval = phy_connect_direct(adapter->netdev, adapter->phydev,
23562306a36Sopenharmony_ci				    tsnep_phy_link_status_change,
23662306a36Sopenharmony_ci				    adapter->phy_mode);
23762306a36Sopenharmony_ci	if (retval)
23862306a36Sopenharmony_ci		return retval;
23962306a36Sopenharmony_ci	phydev = adapter->netdev->phydev;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* MAC supports only 100Mbps|1000Mbps full duplex
24262306a36Sopenharmony_ci	 * SPE (Single Pair Ethernet) is also an option but not implemented yet
24362306a36Sopenharmony_ci	 */
24462306a36Sopenharmony_ci	phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_10baseT_Half_BIT);
24562306a36Sopenharmony_ci	phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_10baseT_Full_BIT);
24662306a36Sopenharmony_ci	phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_100baseT_Half_BIT);
24762306a36Sopenharmony_ci	phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Half_BIT);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	/* disable EEE autoneg, EEE not supported by TSNEP */
25062306a36Sopenharmony_ci	memset(&ethtool_eee, 0, sizeof(ethtool_eee));
25162306a36Sopenharmony_ci	phy_ethtool_set_eee(adapter->phydev, &ethtool_eee);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	adapter->phydev->irq = PHY_MAC_INTERRUPT;
25462306a36Sopenharmony_ci	phy_start(adapter->phydev);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return 0;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic void tsnep_phy_close(struct tsnep_adapter *adapter)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	phy_stop(adapter->netdev->phydev);
26262306a36Sopenharmony_ci	phy_disconnect(adapter->netdev->phydev);
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic void tsnep_tx_ring_cleanup(struct tsnep_tx *tx)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	struct device *dmadev = tx->adapter->dmadev;
26862306a36Sopenharmony_ci	int i;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	memset(tx->entry, 0, sizeof(tx->entry));
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	for (i = 0; i < TSNEP_RING_PAGE_COUNT; i++) {
27362306a36Sopenharmony_ci		if (tx->page[i]) {
27462306a36Sopenharmony_ci			dma_free_coherent(dmadev, PAGE_SIZE, tx->page[i],
27562306a36Sopenharmony_ci					  tx->page_dma[i]);
27662306a36Sopenharmony_ci			tx->page[i] = NULL;
27762306a36Sopenharmony_ci			tx->page_dma[i] = 0;
27862306a36Sopenharmony_ci		}
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic int tsnep_tx_ring_create(struct tsnep_tx *tx)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	struct device *dmadev = tx->adapter->dmadev;
28562306a36Sopenharmony_ci	struct tsnep_tx_entry *entry;
28662306a36Sopenharmony_ci	struct tsnep_tx_entry *next_entry;
28762306a36Sopenharmony_ci	int i, j;
28862306a36Sopenharmony_ci	int retval;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	for (i = 0; i < TSNEP_RING_PAGE_COUNT; i++) {
29162306a36Sopenharmony_ci		tx->page[i] =
29262306a36Sopenharmony_ci			dma_alloc_coherent(dmadev, PAGE_SIZE, &tx->page_dma[i],
29362306a36Sopenharmony_ci					   GFP_KERNEL);
29462306a36Sopenharmony_ci		if (!tx->page[i]) {
29562306a36Sopenharmony_ci			retval = -ENOMEM;
29662306a36Sopenharmony_ci			goto alloc_failed;
29762306a36Sopenharmony_ci		}
29862306a36Sopenharmony_ci		for (j = 0; j < TSNEP_RING_ENTRIES_PER_PAGE; j++) {
29962306a36Sopenharmony_ci			entry = &tx->entry[TSNEP_RING_ENTRIES_PER_PAGE * i + j];
30062306a36Sopenharmony_ci			entry->desc_wb = (struct tsnep_tx_desc_wb *)
30162306a36Sopenharmony_ci				(((u8 *)tx->page[i]) + TSNEP_DESC_SIZE * j);
30262306a36Sopenharmony_ci			entry->desc = (struct tsnep_tx_desc *)
30362306a36Sopenharmony_ci				(((u8 *)entry->desc_wb) + TSNEP_DESC_OFFSET);
30462306a36Sopenharmony_ci			entry->desc_dma = tx->page_dma[i] + TSNEP_DESC_SIZE * j;
30562306a36Sopenharmony_ci			entry->owner_user_flag = false;
30662306a36Sopenharmony_ci		}
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci	for (i = 0; i < TSNEP_RING_SIZE; i++) {
30962306a36Sopenharmony_ci		entry = &tx->entry[i];
31062306a36Sopenharmony_ci		next_entry = &tx->entry[(i + 1) & TSNEP_RING_MASK];
31162306a36Sopenharmony_ci		entry->desc->next = __cpu_to_le64(next_entry->desc_dma);
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	return 0;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cialloc_failed:
31762306a36Sopenharmony_ci	tsnep_tx_ring_cleanup(tx);
31862306a36Sopenharmony_ci	return retval;
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic void tsnep_tx_init(struct tsnep_tx *tx)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	dma_addr_t dma;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	dma = tx->entry[0].desc_dma | TSNEP_RESET_OWNER_COUNTER;
32662306a36Sopenharmony_ci	iowrite32(DMA_ADDR_LOW(dma), tx->addr + TSNEP_TX_DESC_ADDR_LOW);
32762306a36Sopenharmony_ci	iowrite32(DMA_ADDR_HIGH(dma), tx->addr + TSNEP_TX_DESC_ADDR_HIGH);
32862306a36Sopenharmony_ci	tx->write = 0;
32962306a36Sopenharmony_ci	tx->read = 0;
33062306a36Sopenharmony_ci	tx->owner_counter = 1;
33162306a36Sopenharmony_ci	tx->increment_owner_counter = TSNEP_RING_SIZE - 1;
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic void tsnep_tx_enable(struct tsnep_tx *tx)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct netdev_queue *nq;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	nq = netdev_get_tx_queue(tx->adapter->netdev, tx->queue_index);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	__netif_tx_lock_bh(nq);
34162306a36Sopenharmony_ci	netif_tx_wake_queue(nq);
34262306a36Sopenharmony_ci	__netif_tx_unlock_bh(nq);
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic void tsnep_tx_disable(struct tsnep_tx *tx, struct napi_struct *napi)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	struct netdev_queue *nq;
34862306a36Sopenharmony_ci	u32 val;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	nq = netdev_get_tx_queue(tx->adapter->netdev, tx->queue_index);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	__netif_tx_lock_bh(nq);
35362306a36Sopenharmony_ci	netif_tx_stop_queue(nq);
35462306a36Sopenharmony_ci	__netif_tx_unlock_bh(nq);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	/* wait until TX is done in hardware */
35762306a36Sopenharmony_ci	readx_poll_timeout(ioread32, tx->addr + TSNEP_CONTROL, val,
35862306a36Sopenharmony_ci			   ((val & TSNEP_CONTROL_TX_ENABLE) == 0), 10000,
35962306a36Sopenharmony_ci			   1000000);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	/* wait until TX is also done in software */
36262306a36Sopenharmony_ci	while (READ_ONCE(tx->read) != tx->write) {
36362306a36Sopenharmony_ci		napi_schedule(napi);
36462306a36Sopenharmony_ci		napi_synchronize(napi);
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic void tsnep_tx_activate(struct tsnep_tx *tx, int index, int length,
36962306a36Sopenharmony_ci			      bool last)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	struct tsnep_tx_entry *entry = &tx->entry[index];
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	entry->properties = 0;
37462306a36Sopenharmony_ci	/* xdpf and zc are union with skb */
37562306a36Sopenharmony_ci	if (entry->skb) {
37662306a36Sopenharmony_ci		entry->properties = length & TSNEP_DESC_LENGTH_MASK;
37762306a36Sopenharmony_ci		entry->properties |= TSNEP_DESC_INTERRUPT_FLAG;
37862306a36Sopenharmony_ci		if ((entry->type & TSNEP_TX_TYPE_SKB) &&
37962306a36Sopenharmony_ci		    (skb_shinfo(entry->skb)->tx_flags & SKBTX_IN_PROGRESS))
38062306a36Sopenharmony_ci			entry->properties |= TSNEP_DESC_EXTENDED_WRITEBACK_FLAG;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci		/* toggle user flag to prevent false acknowledge
38362306a36Sopenharmony_ci		 *
38462306a36Sopenharmony_ci		 * Only the first fragment is acknowledged. For all other
38562306a36Sopenharmony_ci		 * fragments no acknowledge is done and the last written owner
38662306a36Sopenharmony_ci		 * counter stays in the writeback descriptor. Therefore, it is
38762306a36Sopenharmony_ci		 * possible that the last written owner counter is identical to
38862306a36Sopenharmony_ci		 * the new incremented owner counter and a false acknowledge is
38962306a36Sopenharmony_ci		 * detected before the real acknowledge has been done by
39062306a36Sopenharmony_ci		 * hardware.
39162306a36Sopenharmony_ci		 *
39262306a36Sopenharmony_ci		 * The user flag is used to prevent this situation. The user
39362306a36Sopenharmony_ci		 * flag is copied to the writeback descriptor by the hardware
39462306a36Sopenharmony_ci		 * and is used as additional acknowledge data. By toggeling the
39562306a36Sopenharmony_ci		 * user flag only for the first fragment (which is
39662306a36Sopenharmony_ci		 * acknowledged), it is guaranteed that the last acknowledge
39762306a36Sopenharmony_ci		 * done for this descriptor has used a different user flag and
39862306a36Sopenharmony_ci		 * cannot be detected as false acknowledge.
39962306a36Sopenharmony_ci		 */
40062306a36Sopenharmony_ci		entry->owner_user_flag = !entry->owner_user_flag;
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci	if (last)
40362306a36Sopenharmony_ci		entry->properties |= TSNEP_TX_DESC_LAST_FRAGMENT_FLAG;
40462306a36Sopenharmony_ci	if (index == tx->increment_owner_counter) {
40562306a36Sopenharmony_ci		tx->owner_counter++;
40662306a36Sopenharmony_ci		if (tx->owner_counter == 4)
40762306a36Sopenharmony_ci			tx->owner_counter = 1;
40862306a36Sopenharmony_ci		tx->increment_owner_counter--;
40962306a36Sopenharmony_ci		if (tx->increment_owner_counter < 0)
41062306a36Sopenharmony_ci			tx->increment_owner_counter = TSNEP_RING_SIZE - 1;
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci	entry->properties |=
41362306a36Sopenharmony_ci		(tx->owner_counter << TSNEP_DESC_OWNER_COUNTER_SHIFT) &
41462306a36Sopenharmony_ci		TSNEP_DESC_OWNER_COUNTER_MASK;
41562306a36Sopenharmony_ci	if (entry->owner_user_flag)
41662306a36Sopenharmony_ci		entry->properties |= TSNEP_TX_DESC_OWNER_USER_FLAG;
41762306a36Sopenharmony_ci	entry->desc->more_properties =
41862306a36Sopenharmony_ci		__cpu_to_le32(entry->len & TSNEP_DESC_LENGTH_MASK);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	/* descriptor properties shall be written last, because valid data is
42162306a36Sopenharmony_ci	 * signaled there
42262306a36Sopenharmony_ci	 */
42362306a36Sopenharmony_ci	dma_wmb();
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	entry->desc->properties = __cpu_to_le32(entry->properties);
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic int tsnep_tx_desc_available(struct tsnep_tx *tx)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	if (tx->read <= tx->write)
43162306a36Sopenharmony_ci		return TSNEP_RING_SIZE - tx->write + tx->read - 1;
43262306a36Sopenharmony_ci	else
43362306a36Sopenharmony_ci		return tx->read - tx->write - 1;
43462306a36Sopenharmony_ci}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_cistatic int tsnep_tx_map(struct sk_buff *skb, struct tsnep_tx *tx, int count)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	struct device *dmadev = tx->adapter->dmadev;
43962306a36Sopenharmony_ci	struct tsnep_tx_entry *entry;
44062306a36Sopenharmony_ci	unsigned int len;
44162306a36Sopenharmony_ci	dma_addr_t dma;
44262306a36Sopenharmony_ci	int map_len = 0;
44362306a36Sopenharmony_ci	int i;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
44662306a36Sopenharmony_ci		entry = &tx->entry[(tx->write + i) & TSNEP_RING_MASK];
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci		if (!i) {
44962306a36Sopenharmony_ci			len = skb_headlen(skb);
45062306a36Sopenharmony_ci			dma = dma_map_single(dmadev, skb->data, len,
45162306a36Sopenharmony_ci					     DMA_TO_DEVICE);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci			entry->type = TSNEP_TX_TYPE_SKB;
45462306a36Sopenharmony_ci		} else {
45562306a36Sopenharmony_ci			len = skb_frag_size(&skb_shinfo(skb)->frags[i - 1]);
45662306a36Sopenharmony_ci			dma = skb_frag_dma_map(dmadev,
45762306a36Sopenharmony_ci					       &skb_shinfo(skb)->frags[i - 1],
45862306a36Sopenharmony_ci					       0, len, DMA_TO_DEVICE);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci			entry->type = TSNEP_TX_TYPE_SKB_FRAG;
46162306a36Sopenharmony_ci		}
46262306a36Sopenharmony_ci		if (dma_mapping_error(dmadev, dma))
46362306a36Sopenharmony_ci			return -ENOMEM;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		entry->len = len;
46662306a36Sopenharmony_ci		dma_unmap_addr_set(entry, dma, dma);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci		entry->desc->tx = __cpu_to_le64(dma);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci		map_len += len;
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	return map_len;
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic int tsnep_tx_unmap(struct tsnep_tx *tx, int index, int count)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	struct device *dmadev = tx->adapter->dmadev;
47962306a36Sopenharmony_ci	struct tsnep_tx_entry *entry;
48062306a36Sopenharmony_ci	int map_len = 0;
48162306a36Sopenharmony_ci	int i;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
48462306a36Sopenharmony_ci		entry = &tx->entry[(index + i) & TSNEP_RING_MASK];
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci		if (entry->len) {
48762306a36Sopenharmony_ci			if (entry->type & TSNEP_TX_TYPE_SKB)
48862306a36Sopenharmony_ci				dma_unmap_single(dmadev,
48962306a36Sopenharmony_ci						 dma_unmap_addr(entry, dma),
49062306a36Sopenharmony_ci						 dma_unmap_len(entry, len),
49162306a36Sopenharmony_ci						 DMA_TO_DEVICE);
49262306a36Sopenharmony_ci			else if (entry->type &
49362306a36Sopenharmony_ci				 (TSNEP_TX_TYPE_SKB_FRAG | TSNEP_TX_TYPE_XDP_NDO))
49462306a36Sopenharmony_ci				dma_unmap_page(dmadev,
49562306a36Sopenharmony_ci					       dma_unmap_addr(entry, dma),
49662306a36Sopenharmony_ci					       dma_unmap_len(entry, len),
49762306a36Sopenharmony_ci					       DMA_TO_DEVICE);
49862306a36Sopenharmony_ci			map_len += entry->len;
49962306a36Sopenharmony_ci			entry->len = 0;
50062306a36Sopenharmony_ci		}
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	return map_len;
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic netdev_tx_t tsnep_xmit_frame_ring(struct sk_buff *skb,
50762306a36Sopenharmony_ci					 struct tsnep_tx *tx)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	int count = 1;
51062306a36Sopenharmony_ci	struct tsnep_tx_entry *entry;
51162306a36Sopenharmony_ci	int length;
51262306a36Sopenharmony_ci	int i;
51362306a36Sopenharmony_ci	int retval;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (skb_shinfo(skb)->nr_frags > 0)
51662306a36Sopenharmony_ci		count += skb_shinfo(skb)->nr_frags;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	if (tsnep_tx_desc_available(tx) < count) {
51962306a36Sopenharmony_ci		/* ring full, shall not happen because queue is stopped if full
52062306a36Sopenharmony_ci		 * below
52162306a36Sopenharmony_ci		 */
52262306a36Sopenharmony_ci		netif_stop_subqueue(tx->adapter->netdev, tx->queue_index);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	entry = &tx->entry[tx->write];
52862306a36Sopenharmony_ci	entry->skb = skb;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	retval = tsnep_tx_map(skb, tx, count);
53162306a36Sopenharmony_ci	if (retval < 0) {
53262306a36Sopenharmony_ci		tsnep_tx_unmap(tx, tx->write, count);
53362306a36Sopenharmony_ci		dev_kfree_skb_any(entry->skb);
53462306a36Sopenharmony_ci		entry->skb = NULL;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci		tx->dropped++;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci		return NETDEV_TX_OK;
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci	length = retval;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)
54362306a36Sopenharmony_ci		skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	for (i = 0; i < count; i++)
54662306a36Sopenharmony_ci		tsnep_tx_activate(tx, (tx->write + i) & TSNEP_RING_MASK, length,
54762306a36Sopenharmony_ci				  i == count - 1);
54862306a36Sopenharmony_ci	tx->write = (tx->write + count) & TSNEP_RING_MASK;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	skb_tx_timestamp(skb);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	/* descriptor properties shall be valid before hardware is notified */
55362306a36Sopenharmony_ci	dma_wmb();
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	iowrite32(TSNEP_CONTROL_TX_ENABLE, tx->addr + TSNEP_CONTROL);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	if (tsnep_tx_desc_available(tx) < (MAX_SKB_FRAGS + 1)) {
55862306a36Sopenharmony_ci		/* ring can get full with next frame */
55962306a36Sopenharmony_ci		netif_stop_subqueue(tx->adapter->netdev, tx->queue_index);
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	return NETDEV_TX_OK;
56362306a36Sopenharmony_ci}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_cistatic int tsnep_xdp_tx_map(struct xdp_frame *xdpf, struct tsnep_tx *tx,
56662306a36Sopenharmony_ci			    struct skb_shared_info *shinfo, int count, u32 type)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	struct device *dmadev = tx->adapter->dmadev;
56962306a36Sopenharmony_ci	struct tsnep_tx_entry *entry;
57062306a36Sopenharmony_ci	struct page *page;
57162306a36Sopenharmony_ci	skb_frag_t *frag;
57262306a36Sopenharmony_ci	unsigned int len;
57362306a36Sopenharmony_ci	int map_len = 0;
57462306a36Sopenharmony_ci	dma_addr_t dma;
57562306a36Sopenharmony_ci	void *data;
57662306a36Sopenharmony_ci	int i;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	frag = NULL;
57962306a36Sopenharmony_ci	len = xdpf->len;
58062306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
58162306a36Sopenharmony_ci		entry = &tx->entry[(tx->write + i) & TSNEP_RING_MASK];
58262306a36Sopenharmony_ci		if (type & TSNEP_TX_TYPE_XDP_NDO) {
58362306a36Sopenharmony_ci			data = unlikely(frag) ? skb_frag_address(frag) :
58462306a36Sopenharmony_ci						xdpf->data;
58562306a36Sopenharmony_ci			dma = dma_map_single(dmadev, data, len, DMA_TO_DEVICE);
58662306a36Sopenharmony_ci			if (dma_mapping_error(dmadev, dma))
58762306a36Sopenharmony_ci				return -ENOMEM;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci			entry->type = TSNEP_TX_TYPE_XDP_NDO;
59062306a36Sopenharmony_ci		} else {
59162306a36Sopenharmony_ci			page = unlikely(frag) ? skb_frag_page(frag) :
59262306a36Sopenharmony_ci						virt_to_page(xdpf->data);
59362306a36Sopenharmony_ci			dma = page_pool_get_dma_addr(page);
59462306a36Sopenharmony_ci			if (unlikely(frag))
59562306a36Sopenharmony_ci				dma += skb_frag_off(frag);
59662306a36Sopenharmony_ci			else
59762306a36Sopenharmony_ci				dma += sizeof(*xdpf) + xdpf->headroom;
59862306a36Sopenharmony_ci			dma_sync_single_for_device(dmadev, dma, len,
59962306a36Sopenharmony_ci						   DMA_BIDIRECTIONAL);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci			entry->type = TSNEP_TX_TYPE_XDP_TX;
60262306a36Sopenharmony_ci		}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		entry->len = len;
60562306a36Sopenharmony_ci		dma_unmap_addr_set(entry, dma, dma);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci		entry->desc->tx = __cpu_to_le64(dma);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci		map_len += len;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci		if (i + 1 < count) {
61262306a36Sopenharmony_ci			frag = &shinfo->frags[i];
61362306a36Sopenharmony_ci			len = skb_frag_size(frag);
61462306a36Sopenharmony_ci		}
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	return map_len;
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci/* This function requires __netif_tx_lock is held by the caller. */
62162306a36Sopenharmony_cistatic bool tsnep_xdp_xmit_frame_ring(struct xdp_frame *xdpf,
62262306a36Sopenharmony_ci				      struct tsnep_tx *tx, u32 type)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	struct skb_shared_info *shinfo = xdp_get_shared_info_from_frame(xdpf);
62562306a36Sopenharmony_ci	struct tsnep_tx_entry *entry;
62662306a36Sopenharmony_ci	int count, length, retval, i;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	count = 1;
62962306a36Sopenharmony_ci	if (unlikely(xdp_frame_has_frags(xdpf)))
63062306a36Sopenharmony_ci		count += shinfo->nr_frags;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	/* ensure that TX ring is not filled up by XDP, always MAX_SKB_FRAGS
63362306a36Sopenharmony_ci	 * will be available for normal TX path and queue is stopped there if
63462306a36Sopenharmony_ci	 * necessary
63562306a36Sopenharmony_ci	 */
63662306a36Sopenharmony_ci	if (tsnep_tx_desc_available(tx) < (MAX_SKB_FRAGS + 1 + count))
63762306a36Sopenharmony_ci		return false;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	entry = &tx->entry[tx->write];
64062306a36Sopenharmony_ci	entry->xdpf = xdpf;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	retval = tsnep_xdp_tx_map(xdpf, tx, shinfo, count, type);
64362306a36Sopenharmony_ci	if (retval < 0) {
64462306a36Sopenharmony_ci		tsnep_tx_unmap(tx, tx->write, count);
64562306a36Sopenharmony_ci		entry->xdpf = NULL;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci		tx->dropped++;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci		return false;
65062306a36Sopenharmony_ci	}
65162306a36Sopenharmony_ci	length = retval;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	for (i = 0; i < count; i++)
65462306a36Sopenharmony_ci		tsnep_tx_activate(tx, (tx->write + i) & TSNEP_RING_MASK, length,
65562306a36Sopenharmony_ci				  i == count - 1);
65662306a36Sopenharmony_ci	tx->write = (tx->write + count) & TSNEP_RING_MASK;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	/* descriptor properties shall be valid before hardware is notified */
65962306a36Sopenharmony_ci	dma_wmb();
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	return true;
66262306a36Sopenharmony_ci}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_cistatic void tsnep_xdp_xmit_flush(struct tsnep_tx *tx)
66562306a36Sopenharmony_ci{
66662306a36Sopenharmony_ci	iowrite32(TSNEP_CONTROL_TX_ENABLE, tx->addr + TSNEP_CONTROL);
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cistatic bool tsnep_xdp_xmit_back(struct tsnep_adapter *adapter,
67062306a36Sopenharmony_ci				struct xdp_buff *xdp,
67162306a36Sopenharmony_ci				struct netdev_queue *tx_nq, struct tsnep_tx *tx,
67262306a36Sopenharmony_ci				bool zc)
67362306a36Sopenharmony_ci{
67462306a36Sopenharmony_ci	struct xdp_frame *xdpf = xdp_convert_buff_to_frame(xdp);
67562306a36Sopenharmony_ci	bool xmit;
67662306a36Sopenharmony_ci	u32 type;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	if (unlikely(!xdpf))
67962306a36Sopenharmony_ci		return false;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	/* no page pool for zero copy */
68262306a36Sopenharmony_ci	if (zc)
68362306a36Sopenharmony_ci		type = TSNEP_TX_TYPE_XDP_NDO;
68462306a36Sopenharmony_ci	else
68562306a36Sopenharmony_ci		type = TSNEP_TX_TYPE_XDP_TX;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	__netif_tx_lock(tx_nq, smp_processor_id());
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	xmit = tsnep_xdp_xmit_frame_ring(xdpf, tx, type);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	/* Avoid transmit queue timeout since we share it with the slow path */
69262306a36Sopenharmony_ci	if (xmit)
69362306a36Sopenharmony_ci		txq_trans_cond_update(tx_nq);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	__netif_tx_unlock(tx_nq);
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	return xmit;
69862306a36Sopenharmony_ci}
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_cistatic int tsnep_xdp_tx_map_zc(struct xdp_desc *xdpd, struct tsnep_tx *tx)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	struct tsnep_tx_entry *entry;
70362306a36Sopenharmony_ci	dma_addr_t dma;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	entry = &tx->entry[tx->write];
70662306a36Sopenharmony_ci	entry->zc = true;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	dma = xsk_buff_raw_get_dma(tx->xsk_pool, xdpd->addr);
70962306a36Sopenharmony_ci	xsk_buff_raw_dma_sync_for_device(tx->xsk_pool, dma, xdpd->len);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	entry->type = TSNEP_TX_TYPE_XSK;
71262306a36Sopenharmony_ci	entry->len = xdpd->len;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	entry->desc->tx = __cpu_to_le64(dma);
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	return xdpd->len;
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cistatic void tsnep_xdp_xmit_frame_ring_zc(struct xdp_desc *xdpd,
72062306a36Sopenharmony_ci					 struct tsnep_tx *tx)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	int length;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	length = tsnep_xdp_tx_map_zc(xdpd, tx);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	tsnep_tx_activate(tx, tx->write, length, true);
72762306a36Sopenharmony_ci	tx->write = (tx->write + 1) & TSNEP_RING_MASK;
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_cistatic void tsnep_xdp_xmit_zc(struct tsnep_tx *tx)
73162306a36Sopenharmony_ci{
73262306a36Sopenharmony_ci	int desc_available = tsnep_tx_desc_available(tx);
73362306a36Sopenharmony_ci	struct xdp_desc *descs = tx->xsk_pool->tx_descs;
73462306a36Sopenharmony_ci	int batch, i;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	/* ensure that TX ring is not filled up by XDP, always MAX_SKB_FRAGS
73762306a36Sopenharmony_ci	 * will be available for normal TX path and queue is stopped there if
73862306a36Sopenharmony_ci	 * necessary
73962306a36Sopenharmony_ci	 */
74062306a36Sopenharmony_ci	if (desc_available <= (MAX_SKB_FRAGS + 1))
74162306a36Sopenharmony_ci		return;
74262306a36Sopenharmony_ci	desc_available -= MAX_SKB_FRAGS + 1;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	batch = xsk_tx_peek_release_desc_batch(tx->xsk_pool, desc_available);
74562306a36Sopenharmony_ci	for (i = 0; i < batch; i++)
74662306a36Sopenharmony_ci		tsnep_xdp_xmit_frame_ring_zc(&descs[i], tx);
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	if (batch) {
74962306a36Sopenharmony_ci		/* descriptor properties shall be valid before hardware is
75062306a36Sopenharmony_ci		 * notified
75162306a36Sopenharmony_ci		 */
75262306a36Sopenharmony_ci		dma_wmb();
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci		tsnep_xdp_xmit_flush(tx);
75562306a36Sopenharmony_ci	}
75662306a36Sopenharmony_ci}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_cistatic bool tsnep_tx_poll(struct tsnep_tx *tx, int napi_budget)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	struct tsnep_tx_entry *entry;
76162306a36Sopenharmony_ci	struct netdev_queue *nq;
76262306a36Sopenharmony_ci	int xsk_frames = 0;
76362306a36Sopenharmony_ci	int budget = 128;
76462306a36Sopenharmony_ci	int length;
76562306a36Sopenharmony_ci	int count;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	nq = netdev_get_tx_queue(tx->adapter->netdev, tx->queue_index);
76862306a36Sopenharmony_ci	__netif_tx_lock(nq, smp_processor_id());
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	do {
77162306a36Sopenharmony_ci		if (tx->read == tx->write)
77262306a36Sopenharmony_ci			break;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci		entry = &tx->entry[tx->read];
77562306a36Sopenharmony_ci		if ((__le32_to_cpu(entry->desc_wb->properties) &
77662306a36Sopenharmony_ci		     TSNEP_TX_DESC_OWNER_MASK) !=
77762306a36Sopenharmony_ci		    (entry->properties & TSNEP_TX_DESC_OWNER_MASK))
77862306a36Sopenharmony_ci			break;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci		/* descriptor properties shall be read first, because valid data
78162306a36Sopenharmony_ci		 * is signaled there
78262306a36Sopenharmony_ci		 */
78362306a36Sopenharmony_ci		dma_rmb();
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci		count = 1;
78662306a36Sopenharmony_ci		if ((entry->type & TSNEP_TX_TYPE_SKB) &&
78762306a36Sopenharmony_ci		    skb_shinfo(entry->skb)->nr_frags > 0)
78862306a36Sopenharmony_ci			count += skb_shinfo(entry->skb)->nr_frags;
78962306a36Sopenharmony_ci		else if ((entry->type & TSNEP_TX_TYPE_XDP) &&
79062306a36Sopenharmony_ci			 xdp_frame_has_frags(entry->xdpf))
79162306a36Sopenharmony_ci			count += xdp_get_shared_info_from_frame(entry->xdpf)->nr_frags;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci		length = tsnep_tx_unmap(tx, tx->read, count);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci		if ((entry->type & TSNEP_TX_TYPE_SKB) &&
79662306a36Sopenharmony_ci		    (skb_shinfo(entry->skb)->tx_flags & SKBTX_IN_PROGRESS) &&
79762306a36Sopenharmony_ci		    (__le32_to_cpu(entry->desc_wb->properties) &
79862306a36Sopenharmony_ci		     TSNEP_DESC_EXTENDED_WRITEBACK_FLAG)) {
79962306a36Sopenharmony_ci			struct skb_shared_hwtstamps hwtstamps;
80062306a36Sopenharmony_ci			u64 timestamp;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci			if (skb_shinfo(entry->skb)->tx_flags &
80362306a36Sopenharmony_ci			    SKBTX_HW_TSTAMP_USE_CYCLES)
80462306a36Sopenharmony_ci				timestamp =
80562306a36Sopenharmony_ci					__le64_to_cpu(entry->desc_wb->counter);
80662306a36Sopenharmony_ci			else
80762306a36Sopenharmony_ci				timestamp =
80862306a36Sopenharmony_ci					__le64_to_cpu(entry->desc_wb->timestamp);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci			memset(&hwtstamps, 0, sizeof(hwtstamps));
81162306a36Sopenharmony_ci			hwtstamps.hwtstamp = ns_to_ktime(timestamp);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci			skb_tstamp_tx(entry->skb, &hwtstamps);
81462306a36Sopenharmony_ci		}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci		if (entry->type & TSNEP_TX_TYPE_SKB)
81762306a36Sopenharmony_ci			napi_consume_skb(entry->skb, napi_budget);
81862306a36Sopenharmony_ci		else if (entry->type & TSNEP_TX_TYPE_XDP)
81962306a36Sopenharmony_ci			xdp_return_frame_rx_napi(entry->xdpf);
82062306a36Sopenharmony_ci		else
82162306a36Sopenharmony_ci			xsk_frames++;
82262306a36Sopenharmony_ci		/* xdpf and zc are union with skb */
82362306a36Sopenharmony_ci		entry->skb = NULL;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci		tx->read = (tx->read + count) & TSNEP_RING_MASK;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci		tx->packets++;
82862306a36Sopenharmony_ci		tx->bytes += length + ETH_FCS_LEN;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci		budget--;
83162306a36Sopenharmony_ci	} while (likely(budget));
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	if (tx->xsk_pool) {
83462306a36Sopenharmony_ci		if (xsk_frames)
83562306a36Sopenharmony_ci			xsk_tx_completed(tx->xsk_pool, xsk_frames);
83662306a36Sopenharmony_ci		if (xsk_uses_need_wakeup(tx->xsk_pool))
83762306a36Sopenharmony_ci			xsk_set_tx_need_wakeup(tx->xsk_pool);
83862306a36Sopenharmony_ci		tsnep_xdp_xmit_zc(tx);
83962306a36Sopenharmony_ci	}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	if ((tsnep_tx_desc_available(tx) >= ((MAX_SKB_FRAGS + 1) * 2)) &&
84262306a36Sopenharmony_ci	    netif_tx_queue_stopped(nq)) {
84362306a36Sopenharmony_ci		netif_tx_wake_queue(nq);
84462306a36Sopenharmony_ci	}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	__netif_tx_unlock(nq);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	return budget != 0;
84962306a36Sopenharmony_ci}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_cistatic bool tsnep_tx_pending(struct tsnep_tx *tx)
85262306a36Sopenharmony_ci{
85362306a36Sopenharmony_ci	struct tsnep_tx_entry *entry;
85462306a36Sopenharmony_ci	struct netdev_queue *nq;
85562306a36Sopenharmony_ci	bool pending = false;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	nq = netdev_get_tx_queue(tx->adapter->netdev, tx->queue_index);
85862306a36Sopenharmony_ci	__netif_tx_lock(nq, smp_processor_id());
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	if (tx->read != tx->write) {
86162306a36Sopenharmony_ci		entry = &tx->entry[tx->read];
86262306a36Sopenharmony_ci		if ((__le32_to_cpu(entry->desc_wb->properties) &
86362306a36Sopenharmony_ci		     TSNEP_TX_DESC_OWNER_MASK) ==
86462306a36Sopenharmony_ci		    (entry->properties & TSNEP_TX_DESC_OWNER_MASK))
86562306a36Sopenharmony_ci			pending = true;
86662306a36Sopenharmony_ci	}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	__netif_tx_unlock(nq);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	return pending;
87162306a36Sopenharmony_ci}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_cistatic int tsnep_tx_open(struct tsnep_tx *tx)
87462306a36Sopenharmony_ci{
87562306a36Sopenharmony_ci	int retval;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	retval = tsnep_tx_ring_create(tx);
87862306a36Sopenharmony_ci	if (retval)
87962306a36Sopenharmony_ci		return retval;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	tsnep_tx_init(tx);
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	return 0;
88462306a36Sopenharmony_ci}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_cistatic void tsnep_tx_close(struct tsnep_tx *tx)
88762306a36Sopenharmony_ci{
88862306a36Sopenharmony_ci	tsnep_tx_ring_cleanup(tx);
88962306a36Sopenharmony_ci}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_cistatic void tsnep_rx_ring_cleanup(struct tsnep_rx *rx)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	struct device *dmadev = rx->adapter->dmadev;
89462306a36Sopenharmony_ci	struct tsnep_rx_entry *entry;
89562306a36Sopenharmony_ci	int i;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	for (i = 0; i < TSNEP_RING_SIZE; i++) {
89862306a36Sopenharmony_ci		entry = &rx->entry[i];
89962306a36Sopenharmony_ci		if (!rx->xsk_pool && entry->page)
90062306a36Sopenharmony_ci			page_pool_put_full_page(rx->page_pool, entry->page,
90162306a36Sopenharmony_ci						false);
90262306a36Sopenharmony_ci		if (rx->xsk_pool && entry->xdp)
90362306a36Sopenharmony_ci			xsk_buff_free(entry->xdp);
90462306a36Sopenharmony_ci		/* xdp is union with page */
90562306a36Sopenharmony_ci		entry->page = NULL;
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	if (rx->page_pool)
90962306a36Sopenharmony_ci		page_pool_destroy(rx->page_pool);
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	memset(rx->entry, 0, sizeof(rx->entry));
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	for (i = 0; i < TSNEP_RING_PAGE_COUNT; i++) {
91462306a36Sopenharmony_ci		if (rx->page[i]) {
91562306a36Sopenharmony_ci			dma_free_coherent(dmadev, PAGE_SIZE, rx->page[i],
91662306a36Sopenharmony_ci					  rx->page_dma[i]);
91762306a36Sopenharmony_ci			rx->page[i] = NULL;
91862306a36Sopenharmony_ci			rx->page_dma[i] = 0;
91962306a36Sopenharmony_ci		}
92062306a36Sopenharmony_ci	}
92162306a36Sopenharmony_ci}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_cistatic int tsnep_rx_ring_create(struct tsnep_rx *rx)
92462306a36Sopenharmony_ci{
92562306a36Sopenharmony_ci	struct device *dmadev = rx->adapter->dmadev;
92662306a36Sopenharmony_ci	struct tsnep_rx_entry *entry;
92762306a36Sopenharmony_ci	struct page_pool_params pp_params = { 0 };
92862306a36Sopenharmony_ci	struct tsnep_rx_entry *next_entry;
92962306a36Sopenharmony_ci	int i, j;
93062306a36Sopenharmony_ci	int retval;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	for (i = 0; i < TSNEP_RING_PAGE_COUNT; i++) {
93362306a36Sopenharmony_ci		rx->page[i] =
93462306a36Sopenharmony_ci			dma_alloc_coherent(dmadev, PAGE_SIZE, &rx->page_dma[i],
93562306a36Sopenharmony_ci					   GFP_KERNEL);
93662306a36Sopenharmony_ci		if (!rx->page[i]) {
93762306a36Sopenharmony_ci			retval = -ENOMEM;
93862306a36Sopenharmony_ci			goto failed;
93962306a36Sopenharmony_ci		}
94062306a36Sopenharmony_ci		for (j = 0; j < TSNEP_RING_ENTRIES_PER_PAGE; j++) {
94162306a36Sopenharmony_ci			entry = &rx->entry[TSNEP_RING_ENTRIES_PER_PAGE * i + j];
94262306a36Sopenharmony_ci			entry->desc_wb = (struct tsnep_rx_desc_wb *)
94362306a36Sopenharmony_ci				(((u8 *)rx->page[i]) + TSNEP_DESC_SIZE * j);
94462306a36Sopenharmony_ci			entry->desc = (struct tsnep_rx_desc *)
94562306a36Sopenharmony_ci				(((u8 *)entry->desc_wb) + TSNEP_DESC_OFFSET);
94662306a36Sopenharmony_ci			entry->desc_dma = rx->page_dma[i] + TSNEP_DESC_SIZE * j;
94762306a36Sopenharmony_ci		}
94862306a36Sopenharmony_ci	}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;
95162306a36Sopenharmony_ci	pp_params.order = 0;
95262306a36Sopenharmony_ci	pp_params.pool_size = TSNEP_RING_SIZE;
95362306a36Sopenharmony_ci	pp_params.nid = dev_to_node(dmadev);
95462306a36Sopenharmony_ci	pp_params.dev = dmadev;
95562306a36Sopenharmony_ci	pp_params.dma_dir = DMA_BIDIRECTIONAL;
95662306a36Sopenharmony_ci	pp_params.max_len = TSNEP_MAX_RX_BUF_SIZE;
95762306a36Sopenharmony_ci	pp_params.offset = TSNEP_RX_OFFSET;
95862306a36Sopenharmony_ci	rx->page_pool = page_pool_create(&pp_params);
95962306a36Sopenharmony_ci	if (IS_ERR(rx->page_pool)) {
96062306a36Sopenharmony_ci		retval = PTR_ERR(rx->page_pool);
96162306a36Sopenharmony_ci		rx->page_pool = NULL;
96262306a36Sopenharmony_ci		goto failed;
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	for (i = 0; i < TSNEP_RING_SIZE; i++) {
96662306a36Sopenharmony_ci		entry = &rx->entry[i];
96762306a36Sopenharmony_ci		next_entry = &rx->entry[(i + 1) & TSNEP_RING_MASK];
96862306a36Sopenharmony_ci		entry->desc->next = __cpu_to_le64(next_entry->desc_dma);
96962306a36Sopenharmony_ci	}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	return 0;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_cifailed:
97462306a36Sopenharmony_ci	tsnep_rx_ring_cleanup(rx);
97562306a36Sopenharmony_ci	return retval;
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_cistatic void tsnep_rx_init(struct tsnep_rx *rx)
97962306a36Sopenharmony_ci{
98062306a36Sopenharmony_ci	dma_addr_t dma;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	dma = rx->entry[0].desc_dma | TSNEP_RESET_OWNER_COUNTER;
98362306a36Sopenharmony_ci	iowrite32(DMA_ADDR_LOW(dma), rx->addr + TSNEP_RX_DESC_ADDR_LOW);
98462306a36Sopenharmony_ci	iowrite32(DMA_ADDR_HIGH(dma), rx->addr + TSNEP_RX_DESC_ADDR_HIGH);
98562306a36Sopenharmony_ci	rx->write = 0;
98662306a36Sopenharmony_ci	rx->read = 0;
98762306a36Sopenharmony_ci	rx->owner_counter = 1;
98862306a36Sopenharmony_ci	rx->increment_owner_counter = TSNEP_RING_SIZE - 1;
98962306a36Sopenharmony_ci}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_cistatic void tsnep_rx_enable(struct tsnep_rx *rx)
99262306a36Sopenharmony_ci{
99362306a36Sopenharmony_ci	/* descriptor properties shall be valid before hardware is notified */
99462306a36Sopenharmony_ci	dma_wmb();
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	iowrite32(TSNEP_CONTROL_RX_ENABLE, rx->addr + TSNEP_CONTROL);
99762306a36Sopenharmony_ci}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_cistatic void tsnep_rx_disable(struct tsnep_rx *rx)
100062306a36Sopenharmony_ci{
100162306a36Sopenharmony_ci	u32 val;
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	iowrite32(TSNEP_CONTROL_RX_DISABLE, rx->addr + TSNEP_CONTROL);
100462306a36Sopenharmony_ci	readx_poll_timeout(ioread32, rx->addr + TSNEP_CONTROL, val,
100562306a36Sopenharmony_ci			   ((val & TSNEP_CONTROL_RX_ENABLE) == 0), 10000,
100662306a36Sopenharmony_ci			   1000000);
100762306a36Sopenharmony_ci}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_cistatic int tsnep_rx_desc_available(struct tsnep_rx *rx)
101062306a36Sopenharmony_ci{
101162306a36Sopenharmony_ci	if (rx->read <= rx->write)
101262306a36Sopenharmony_ci		return TSNEP_RING_SIZE - rx->write + rx->read - 1;
101362306a36Sopenharmony_ci	else
101462306a36Sopenharmony_ci		return rx->read - rx->write - 1;
101562306a36Sopenharmony_ci}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_cistatic void tsnep_rx_free_page_buffer(struct tsnep_rx *rx)
101862306a36Sopenharmony_ci{
101962306a36Sopenharmony_ci	struct page **page;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	/* last entry of page_buffer is always zero, because ring cannot be
102262306a36Sopenharmony_ci	 * filled completely
102362306a36Sopenharmony_ci	 */
102462306a36Sopenharmony_ci	page = rx->page_buffer;
102562306a36Sopenharmony_ci	while (*page) {
102662306a36Sopenharmony_ci		page_pool_put_full_page(rx->page_pool, *page, false);
102762306a36Sopenharmony_ci		*page = NULL;
102862306a36Sopenharmony_ci		page++;
102962306a36Sopenharmony_ci	}
103062306a36Sopenharmony_ci}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_cistatic int tsnep_rx_alloc_page_buffer(struct tsnep_rx *rx)
103362306a36Sopenharmony_ci{
103462306a36Sopenharmony_ci	int i;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	/* alloc for all ring entries except the last one, because ring cannot
103762306a36Sopenharmony_ci	 * be filled completely
103862306a36Sopenharmony_ci	 */
103962306a36Sopenharmony_ci	for (i = 0; i < TSNEP_RING_SIZE - 1; i++) {
104062306a36Sopenharmony_ci		rx->page_buffer[i] = page_pool_dev_alloc_pages(rx->page_pool);
104162306a36Sopenharmony_ci		if (!rx->page_buffer[i]) {
104262306a36Sopenharmony_ci			tsnep_rx_free_page_buffer(rx);
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci			return -ENOMEM;
104562306a36Sopenharmony_ci		}
104662306a36Sopenharmony_ci	}
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	return 0;
104962306a36Sopenharmony_ci}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_cistatic void tsnep_rx_set_page(struct tsnep_rx *rx, struct tsnep_rx_entry *entry,
105262306a36Sopenharmony_ci			      struct page *page)
105362306a36Sopenharmony_ci{
105462306a36Sopenharmony_ci	entry->page = page;
105562306a36Sopenharmony_ci	entry->len = TSNEP_MAX_RX_BUF_SIZE;
105662306a36Sopenharmony_ci	entry->dma = page_pool_get_dma_addr(entry->page);
105762306a36Sopenharmony_ci	entry->desc->rx = __cpu_to_le64(entry->dma + TSNEP_RX_OFFSET);
105862306a36Sopenharmony_ci}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_cistatic int tsnep_rx_alloc_buffer(struct tsnep_rx *rx, int index)
106162306a36Sopenharmony_ci{
106262306a36Sopenharmony_ci	struct tsnep_rx_entry *entry = &rx->entry[index];
106362306a36Sopenharmony_ci	struct page *page;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	page = page_pool_dev_alloc_pages(rx->page_pool);
106662306a36Sopenharmony_ci	if (unlikely(!page))
106762306a36Sopenharmony_ci		return -ENOMEM;
106862306a36Sopenharmony_ci	tsnep_rx_set_page(rx, entry, page);
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	return 0;
107162306a36Sopenharmony_ci}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_cistatic void tsnep_rx_reuse_buffer(struct tsnep_rx *rx, int index)
107462306a36Sopenharmony_ci{
107562306a36Sopenharmony_ci	struct tsnep_rx_entry *entry = &rx->entry[index];
107662306a36Sopenharmony_ci	struct tsnep_rx_entry *read = &rx->entry[rx->read];
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	tsnep_rx_set_page(rx, entry, read->page);
107962306a36Sopenharmony_ci	read->page = NULL;
108062306a36Sopenharmony_ci}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_cistatic void tsnep_rx_activate(struct tsnep_rx *rx, int index)
108362306a36Sopenharmony_ci{
108462306a36Sopenharmony_ci	struct tsnep_rx_entry *entry = &rx->entry[index];
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	/* TSNEP_MAX_RX_BUF_SIZE and TSNEP_XSK_RX_BUF_SIZE are multiple of 4 */
108762306a36Sopenharmony_ci	entry->properties = entry->len & TSNEP_DESC_LENGTH_MASK;
108862306a36Sopenharmony_ci	entry->properties |= TSNEP_DESC_INTERRUPT_FLAG;
108962306a36Sopenharmony_ci	if (index == rx->increment_owner_counter) {
109062306a36Sopenharmony_ci		rx->owner_counter++;
109162306a36Sopenharmony_ci		if (rx->owner_counter == 4)
109262306a36Sopenharmony_ci			rx->owner_counter = 1;
109362306a36Sopenharmony_ci		rx->increment_owner_counter--;
109462306a36Sopenharmony_ci		if (rx->increment_owner_counter < 0)
109562306a36Sopenharmony_ci			rx->increment_owner_counter = TSNEP_RING_SIZE - 1;
109662306a36Sopenharmony_ci	}
109762306a36Sopenharmony_ci	entry->properties |=
109862306a36Sopenharmony_ci		(rx->owner_counter << TSNEP_DESC_OWNER_COUNTER_SHIFT) &
109962306a36Sopenharmony_ci		TSNEP_DESC_OWNER_COUNTER_MASK;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	/* descriptor properties shall be written last, because valid data is
110262306a36Sopenharmony_ci	 * signaled there
110362306a36Sopenharmony_ci	 */
110462306a36Sopenharmony_ci	dma_wmb();
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	entry->desc->properties = __cpu_to_le32(entry->properties);
110762306a36Sopenharmony_ci}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_cistatic int tsnep_rx_alloc(struct tsnep_rx *rx, int count, bool reuse)
111062306a36Sopenharmony_ci{
111162306a36Sopenharmony_ci	bool alloc_failed = false;
111262306a36Sopenharmony_ci	int i, index;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	for (i = 0; i < count && !alloc_failed; i++) {
111562306a36Sopenharmony_ci		index = (rx->write + i) & TSNEP_RING_MASK;
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci		if (unlikely(tsnep_rx_alloc_buffer(rx, index))) {
111862306a36Sopenharmony_ci			rx->alloc_failed++;
111962306a36Sopenharmony_ci			alloc_failed = true;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci			/* reuse only if no other allocation was successful */
112262306a36Sopenharmony_ci			if (i == 0 && reuse)
112362306a36Sopenharmony_ci				tsnep_rx_reuse_buffer(rx, index);
112462306a36Sopenharmony_ci			else
112562306a36Sopenharmony_ci				break;
112662306a36Sopenharmony_ci		}
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci		tsnep_rx_activate(rx, index);
112962306a36Sopenharmony_ci	}
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	if (i)
113262306a36Sopenharmony_ci		rx->write = (rx->write + i) & TSNEP_RING_MASK;
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	return i;
113562306a36Sopenharmony_ci}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_cistatic int tsnep_rx_refill(struct tsnep_rx *rx, int count, bool reuse)
113862306a36Sopenharmony_ci{
113962306a36Sopenharmony_ci	int desc_refilled;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	desc_refilled = tsnep_rx_alloc(rx, count, reuse);
114262306a36Sopenharmony_ci	if (desc_refilled)
114362306a36Sopenharmony_ci		tsnep_rx_enable(rx);
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	return desc_refilled;
114662306a36Sopenharmony_ci}
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_cistatic void tsnep_rx_set_xdp(struct tsnep_rx *rx, struct tsnep_rx_entry *entry,
114962306a36Sopenharmony_ci			     struct xdp_buff *xdp)
115062306a36Sopenharmony_ci{
115162306a36Sopenharmony_ci	entry->xdp = xdp;
115262306a36Sopenharmony_ci	entry->len = TSNEP_XSK_RX_BUF_SIZE;
115362306a36Sopenharmony_ci	entry->dma = xsk_buff_xdp_get_dma(entry->xdp);
115462306a36Sopenharmony_ci	entry->desc->rx = __cpu_to_le64(entry->dma);
115562306a36Sopenharmony_ci}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_cistatic void tsnep_rx_reuse_buffer_zc(struct tsnep_rx *rx, int index)
115862306a36Sopenharmony_ci{
115962306a36Sopenharmony_ci	struct tsnep_rx_entry *entry = &rx->entry[index];
116062306a36Sopenharmony_ci	struct tsnep_rx_entry *read = &rx->entry[rx->read];
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	tsnep_rx_set_xdp(rx, entry, read->xdp);
116362306a36Sopenharmony_ci	read->xdp = NULL;
116462306a36Sopenharmony_ci}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_cistatic int tsnep_rx_alloc_zc(struct tsnep_rx *rx, int count, bool reuse)
116762306a36Sopenharmony_ci{
116862306a36Sopenharmony_ci	u32 allocated;
116962306a36Sopenharmony_ci	int i;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	allocated = xsk_buff_alloc_batch(rx->xsk_pool, rx->xdp_batch, count);
117262306a36Sopenharmony_ci	for (i = 0; i < allocated; i++) {
117362306a36Sopenharmony_ci		int index = (rx->write + i) & TSNEP_RING_MASK;
117462306a36Sopenharmony_ci		struct tsnep_rx_entry *entry = &rx->entry[index];
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci		tsnep_rx_set_xdp(rx, entry, rx->xdp_batch[i]);
117762306a36Sopenharmony_ci		tsnep_rx_activate(rx, index);
117862306a36Sopenharmony_ci	}
117962306a36Sopenharmony_ci	if (i == 0) {
118062306a36Sopenharmony_ci		rx->alloc_failed++;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci		if (reuse) {
118362306a36Sopenharmony_ci			tsnep_rx_reuse_buffer_zc(rx, rx->write);
118462306a36Sopenharmony_ci			tsnep_rx_activate(rx, rx->write);
118562306a36Sopenharmony_ci		}
118662306a36Sopenharmony_ci	}
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	if (i)
118962306a36Sopenharmony_ci		rx->write = (rx->write + i) & TSNEP_RING_MASK;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	return i;
119262306a36Sopenharmony_ci}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_cistatic void tsnep_rx_free_zc(struct tsnep_rx *rx)
119562306a36Sopenharmony_ci{
119662306a36Sopenharmony_ci	int i;
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	for (i = 0; i < TSNEP_RING_SIZE; i++) {
119962306a36Sopenharmony_ci		struct tsnep_rx_entry *entry = &rx->entry[i];
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci		if (entry->xdp)
120262306a36Sopenharmony_ci			xsk_buff_free(entry->xdp);
120362306a36Sopenharmony_ci		entry->xdp = NULL;
120462306a36Sopenharmony_ci	}
120562306a36Sopenharmony_ci}
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_cistatic int tsnep_rx_refill_zc(struct tsnep_rx *rx, int count, bool reuse)
120862306a36Sopenharmony_ci{
120962306a36Sopenharmony_ci	int desc_refilled;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	desc_refilled = tsnep_rx_alloc_zc(rx, count, reuse);
121262306a36Sopenharmony_ci	if (desc_refilled)
121362306a36Sopenharmony_ci		tsnep_rx_enable(rx);
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	return desc_refilled;
121662306a36Sopenharmony_ci}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_cistatic bool tsnep_xdp_run_prog(struct tsnep_rx *rx, struct bpf_prog *prog,
121962306a36Sopenharmony_ci			       struct xdp_buff *xdp, int *status,
122062306a36Sopenharmony_ci			       struct netdev_queue *tx_nq, struct tsnep_tx *tx)
122162306a36Sopenharmony_ci{
122262306a36Sopenharmony_ci	unsigned int length;
122362306a36Sopenharmony_ci	unsigned int sync;
122462306a36Sopenharmony_ci	u32 act;
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	length = xdp->data_end - xdp->data_hard_start - XDP_PACKET_HEADROOM;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	act = bpf_prog_run_xdp(prog, xdp);
122962306a36Sopenharmony_ci	switch (act) {
123062306a36Sopenharmony_ci	case XDP_PASS:
123162306a36Sopenharmony_ci		return false;
123262306a36Sopenharmony_ci	case XDP_TX:
123362306a36Sopenharmony_ci		if (!tsnep_xdp_xmit_back(rx->adapter, xdp, tx_nq, tx, false))
123462306a36Sopenharmony_ci			goto out_failure;
123562306a36Sopenharmony_ci		*status |= TSNEP_XDP_TX;
123662306a36Sopenharmony_ci		return true;
123762306a36Sopenharmony_ci	case XDP_REDIRECT:
123862306a36Sopenharmony_ci		if (xdp_do_redirect(rx->adapter->netdev, xdp, prog) < 0)
123962306a36Sopenharmony_ci			goto out_failure;
124062306a36Sopenharmony_ci		*status |= TSNEP_XDP_REDIRECT;
124162306a36Sopenharmony_ci		return true;
124262306a36Sopenharmony_ci	default:
124362306a36Sopenharmony_ci		bpf_warn_invalid_xdp_action(rx->adapter->netdev, prog, act);
124462306a36Sopenharmony_ci		fallthrough;
124562306a36Sopenharmony_ci	case XDP_ABORTED:
124662306a36Sopenharmony_ciout_failure:
124762306a36Sopenharmony_ci		trace_xdp_exception(rx->adapter->netdev, prog, act);
124862306a36Sopenharmony_ci		fallthrough;
124962306a36Sopenharmony_ci	case XDP_DROP:
125062306a36Sopenharmony_ci		/* Due xdp_adjust_tail: DMA sync for_device cover max len CPU
125162306a36Sopenharmony_ci		 * touch
125262306a36Sopenharmony_ci		 */
125362306a36Sopenharmony_ci		sync = xdp->data_end - xdp->data_hard_start -
125462306a36Sopenharmony_ci		       XDP_PACKET_HEADROOM;
125562306a36Sopenharmony_ci		sync = max(sync, length);
125662306a36Sopenharmony_ci		page_pool_put_page(rx->page_pool, virt_to_head_page(xdp->data),
125762306a36Sopenharmony_ci				   sync, true);
125862306a36Sopenharmony_ci		return true;
125962306a36Sopenharmony_ci	}
126062306a36Sopenharmony_ci}
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_cistatic bool tsnep_xdp_run_prog_zc(struct tsnep_rx *rx, struct bpf_prog *prog,
126362306a36Sopenharmony_ci				  struct xdp_buff *xdp, int *status,
126462306a36Sopenharmony_ci				  struct netdev_queue *tx_nq,
126562306a36Sopenharmony_ci				  struct tsnep_tx *tx)
126662306a36Sopenharmony_ci{
126762306a36Sopenharmony_ci	u32 act;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	act = bpf_prog_run_xdp(prog, xdp);
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	/* XDP_REDIRECT is the main action for zero-copy */
127262306a36Sopenharmony_ci	if (likely(act == XDP_REDIRECT)) {
127362306a36Sopenharmony_ci		if (xdp_do_redirect(rx->adapter->netdev, xdp, prog) < 0)
127462306a36Sopenharmony_ci			goto out_failure;
127562306a36Sopenharmony_ci		*status |= TSNEP_XDP_REDIRECT;
127662306a36Sopenharmony_ci		return true;
127762306a36Sopenharmony_ci	}
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	switch (act) {
128062306a36Sopenharmony_ci	case XDP_PASS:
128162306a36Sopenharmony_ci		return false;
128262306a36Sopenharmony_ci	case XDP_TX:
128362306a36Sopenharmony_ci		if (!tsnep_xdp_xmit_back(rx->adapter, xdp, tx_nq, tx, true))
128462306a36Sopenharmony_ci			goto out_failure;
128562306a36Sopenharmony_ci		*status |= TSNEP_XDP_TX;
128662306a36Sopenharmony_ci		return true;
128762306a36Sopenharmony_ci	default:
128862306a36Sopenharmony_ci		bpf_warn_invalid_xdp_action(rx->adapter->netdev, prog, act);
128962306a36Sopenharmony_ci		fallthrough;
129062306a36Sopenharmony_ci	case XDP_ABORTED:
129162306a36Sopenharmony_ciout_failure:
129262306a36Sopenharmony_ci		trace_xdp_exception(rx->adapter->netdev, prog, act);
129362306a36Sopenharmony_ci		fallthrough;
129462306a36Sopenharmony_ci	case XDP_DROP:
129562306a36Sopenharmony_ci		xsk_buff_free(xdp);
129662306a36Sopenharmony_ci		return true;
129762306a36Sopenharmony_ci	}
129862306a36Sopenharmony_ci}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_cistatic void tsnep_finalize_xdp(struct tsnep_adapter *adapter, int status,
130162306a36Sopenharmony_ci			       struct netdev_queue *tx_nq, struct tsnep_tx *tx)
130262306a36Sopenharmony_ci{
130362306a36Sopenharmony_ci	if (status & TSNEP_XDP_TX) {
130462306a36Sopenharmony_ci		__netif_tx_lock(tx_nq, smp_processor_id());
130562306a36Sopenharmony_ci		tsnep_xdp_xmit_flush(tx);
130662306a36Sopenharmony_ci		__netif_tx_unlock(tx_nq);
130762306a36Sopenharmony_ci	}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	if (status & TSNEP_XDP_REDIRECT)
131062306a36Sopenharmony_ci		xdp_do_flush();
131162306a36Sopenharmony_ci}
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_cistatic struct sk_buff *tsnep_build_skb(struct tsnep_rx *rx, struct page *page,
131462306a36Sopenharmony_ci				       int length)
131562306a36Sopenharmony_ci{
131662306a36Sopenharmony_ci	struct sk_buff *skb;
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	skb = napi_build_skb(page_address(page), PAGE_SIZE);
131962306a36Sopenharmony_ci	if (unlikely(!skb))
132062306a36Sopenharmony_ci		return NULL;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	/* update pointers within the skb to store the data */
132362306a36Sopenharmony_ci	skb_reserve(skb, TSNEP_RX_OFFSET + TSNEP_RX_INLINE_METADATA_SIZE);
132462306a36Sopenharmony_ci	__skb_put(skb, length - ETH_FCS_LEN);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	if (rx->adapter->hwtstamp_config.rx_filter == HWTSTAMP_FILTER_ALL) {
132762306a36Sopenharmony_ci		struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb);
132862306a36Sopenharmony_ci		struct tsnep_rx_inline *rx_inline =
132962306a36Sopenharmony_ci			(struct tsnep_rx_inline *)(page_address(page) +
133062306a36Sopenharmony_ci						   TSNEP_RX_OFFSET);
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci		skb_shinfo(skb)->tx_flags |=
133362306a36Sopenharmony_ci			SKBTX_HW_TSTAMP_NETDEV;
133462306a36Sopenharmony_ci		memset(hwtstamps, 0, sizeof(*hwtstamps));
133562306a36Sopenharmony_ci		hwtstamps->netdev_data = rx_inline;
133662306a36Sopenharmony_ci	}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	skb_record_rx_queue(skb, rx->queue_index);
133962306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, rx->adapter->netdev);
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	return skb;
134262306a36Sopenharmony_ci}
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_cistatic void tsnep_rx_page(struct tsnep_rx *rx, struct napi_struct *napi,
134562306a36Sopenharmony_ci			  struct page *page, int length)
134662306a36Sopenharmony_ci{
134762306a36Sopenharmony_ci	struct sk_buff *skb;
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	skb = tsnep_build_skb(rx, page, length);
135062306a36Sopenharmony_ci	if (skb) {
135162306a36Sopenharmony_ci		skb_mark_for_recycle(skb);
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci		rx->packets++;
135462306a36Sopenharmony_ci		rx->bytes += length;
135562306a36Sopenharmony_ci		if (skb->pkt_type == PACKET_MULTICAST)
135662306a36Sopenharmony_ci			rx->multicast++;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci		napi_gro_receive(napi, skb);
135962306a36Sopenharmony_ci	} else {
136062306a36Sopenharmony_ci		page_pool_recycle_direct(rx->page_pool, page);
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci		rx->dropped++;
136362306a36Sopenharmony_ci	}
136462306a36Sopenharmony_ci}
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_cistatic int tsnep_rx_poll(struct tsnep_rx *rx, struct napi_struct *napi,
136762306a36Sopenharmony_ci			 int budget)
136862306a36Sopenharmony_ci{
136962306a36Sopenharmony_ci	struct device *dmadev = rx->adapter->dmadev;
137062306a36Sopenharmony_ci	enum dma_data_direction dma_dir;
137162306a36Sopenharmony_ci	struct tsnep_rx_entry *entry;
137262306a36Sopenharmony_ci	struct netdev_queue *tx_nq;
137362306a36Sopenharmony_ci	struct bpf_prog *prog;
137462306a36Sopenharmony_ci	struct xdp_buff xdp;
137562306a36Sopenharmony_ci	struct tsnep_tx *tx;
137662306a36Sopenharmony_ci	int desc_available;
137762306a36Sopenharmony_ci	int xdp_status = 0;
137862306a36Sopenharmony_ci	int done = 0;
137962306a36Sopenharmony_ci	int length;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	desc_available = tsnep_rx_desc_available(rx);
138262306a36Sopenharmony_ci	dma_dir = page_pool_get_dma_dir(rx->page_pool);
138362306a36Sopenharmony_ci	prog = READ_ONCE(rx->adapter->xdp_prog);
138462306a36Sopenharmony_ci	if (prog) {
138562306a36Sopenharmony_ci		tx_nq = netdev_get_tx_queue(rx->adapter->netdev,
138662306a36Sopenharmony_ci					    rx->tx_queue_index);
138762306a36Sopenharmony_ci		tx = &rx->adapter->tx[rx->tx_queue_index];
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci		xdp_init_buff(&xdp, PAGE_SIZE, &rx->xdp_rxq);
139062306a36Sopenharmony_ci	}
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	while (likely(done < budget) && (rx->read != rx->write)) {
139362306a36Sopenharmony_ci		entry = &rx->entry[rx->read];
139462306a36Sopenharmony_ci		if ((__le32_to_cpu(entry->desc_wb->properties) &
139562306a36Sopenharmony_ci		     TSNEP_DESC_OWNER_COUNTER_MASK) !=
139662306a36Sopenharmony_ci		    (entry->properties & TSNEP_DESC_OWNER_COUNTER_MASK))
139762306a36Sopenharmony_ci			break;
139862306a36Sopenharmony_ci		done++;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci		if (desc_available >= TSNEP_RING_RX_REFILL) {
140162306a36Sopenharmony_ci			bool reuse = desc_available >= TSNEP_RING_RX_REUSE;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci			desc_available -= tsnep_rx_refill(rx, desc_available,
140462306a36Sopenharmony_ci							  reuse);
140562306a36Sopenharmony_ci			if (!entry->page) {
140662306a36Sopenharmony_ci				/* buffer has been reused for refill to prevent
140762306a36Sopenharmony_ci				 * empty RX ring, thus buffer cannot be used for
140862306a36Sopenharmony_ci				 * RX processing
140962306a36Sopenharmony_ci				 */
141062306a36Sopenharmony_ci				rx->read = (rx->read + 1) & TSNEP_RING_MASK;
141162306a36Sopenharmony_ci				desc_available++;
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci				rx->dropped++;
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci				continue;
141662306a36Sopenharmony_ci			}
141762306a36Sopenharmony_ci		}
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci		/* descriptor properties shall be read first, because valid data
142062306a36Sopenharmony_ci		 * is signaled there
142162306a36Sopenharmony_ci		 */
142262306a36Sopenharmony_ci		dma_rmb();
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci		prefetch(page_address(entry->page) + TSNEP_RX_OFFSET);
142562306a36Sopenharmony_ci		length = __le32_to_cpu(entry->desc_wb->properties) &
142662306a36Sopenharmony_ci			 TSNEP_DESC_LENGTH_MASK;
142762306a36Sopenharmony_ci		dma_sync_single_range_for_cpu(dmadev, entry->dma,
142862306a36Sopenharmony_ci					      TSNEP_RX_OFFSET, length, dma_dir);
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci		/* RX metadata with timestamps is in front of actual data,
143162306a36Sopenharmony_ci		 * subtract metadata size to get length of actual data and
143262306a36Sopenharmony_ci		 * consider metadata size as offset of actual data during RX
143362306a36Sopenharmony_ci		 * processing
143462306a36Sopenharmony_ci		 */
143562306a36Sopenharmony_ci		length -= TSNEP_RX_INLINE_METADATA_SIZE;
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci		rx->read = (rx->read + 1) & TSNEP_RING_MASK;
143862306a36Sopenharmony_ci		desc_available++;
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci		if (prog) {
144162306a36Sopenharmony_ci			bool consume;
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci			xdp_prepare_buff(&xdp, page_address(entry->page),
144462306a36Sopenharmony_ci					 XDP_PACKET_HEADROOM + TSNEP_RX_INLINE_METADATA_SIZE,
144562306a36Sopenharmony_ci					 length - ETH_FCS_LEN, false);
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci			consume = tsnep_xdp_run_prog(rx, prog, &xdp,
144862306a36Sopenharmony_ci						     &xdp_status, tx_nq, tx);
144962306a36Sopenharmony_ci			if (consume) {
145062306a36Sopenharmony_ci				rx->packets++;
145162306a36Sopenharmony_ci				rx->bytes += length;
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci				entry->page = NULL;
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci				continue;
145662306a36Sopenharmony_ci			}
145762306a36Sopenharmony_ci		}
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci		tsnep_rx_page(rx, napi, entry->page, length);
146062306a36Sopenharmony_ci		entry->page = NULL;
146162306a36Sopenharmony_ci	}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	if (xdp_status)
146462306a36Sopenharmony_ci		tsnep_finalize_xdp(rx->adapter, xdp_status, tx_nq, tx);
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	if (desc_available)
146762306a36Sopenharmony_ci		tsnep_rx_refill(rx, desc_available, false);
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	return done;
147062306a36Sopenharmony_ci}
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_cistatic int tsnep_rx_poll_zc(struct tsnep_rx *rx, struct napi_struct *napi,
147362306a36Sopenharmony_ci			    int budget)
147462306a36Sopenharmony_ci{
147562306a36Sopenharmony_ci	struct tsnep_rx_entry *entry;
147662306a36Sopenharmony_ci	struct netdev_queue *tx_nq;
147762306a36Sopenharmony_ci	struct bpf_prog *prog;
147862306a36Sopenharmony_ci	struct tsnep_tx *tx;
147962306a36Sopenharmony_ci	int desc_available;
148062306a36Sopenharmony_ci	int xdp_status = 0;
148162306a36Sopenharmony_ci	struct page *page;
148262306a36Sopenharmony_ci	int done = 0;
148362306a36Sopenharmony_ci	int length;
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	desc_available = tsnep_rx_desc_available(rx);
148662306a36Sopenharmony_ci	prog = READ_ONCE(rx->adapter->xdp_prog);
148762306a36Sopenharmony_ci	if (prog) {
148862306a36Sopenharmony_ci		tx_nq = netdev_get_tx_queue(rx->adapter->netdev,
148962306a36Sopenharmony_ci					    rx->tx_queue_index);
149062306a36Sopenharmony_ci		tx = &rx->adapter->tx[rx->tx_queue_index];
149162306a36Sopenharmony_ci	}
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	while (likely(done < budget) && (rx->read != rx->write)) {
149462306a36Sopenharmony_ci		entry = &rx->entry[rx->read];
149562306a36Sopenharmony_ci		if ((__le32_to_cpu(entry->desc_wb->properties) &
149662306a36Sopenharmony_ci		     TSNEP_DESC_OWNER_COUNTER_MASK) !=
149762306a36Sopenharmony_ci		    (entry->properties & TSNEP_DESC_OWNER_COUNTER_MASK))
149862306a36Sopenharmony_ci			break;
149962306a36Sopenharmony_ci		done++;
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci		if (desc_available >= TSNEP_RING_RX_REFILL) {
150262306a36Sopenharmony_ci			bool reuse = desc_available >= TSNEP_RING_RX_REUSE;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci			desc_available -= tsnep_rx_refill_zc(rx, desc_available,
150562306a36Sopenharmony_ci							     reuse);
150662306a36Sopenharmony_ci			if (!entry->xdp) {
150762306a36Sopenharmony_ci				/* buffer has been reused for refill to prevent
150862306a36Sopenharmony_ci				 * empty RX ring, thus buffer cannot be used for
150962306a36Sopenharmony_ci				 * RX processing
151062306a36Sopenharmony_ci				 */
151162306a36Sopenharmony_ci				rx->read = (rx->read + 1) & TSNEP_RING_MASK;
151262306a36Sopenharmony_ci				desc_available++;
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci				rx->dropped++;
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci				continue;
151762306a36Sopenharmony_ci			}
151862306a36Sopenharmony_ci		}
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci		/* descriptor properties shall be read first, because valid data
152162306a36Sopenharmony_ci		 * is signaled there
152262306a36Sopenharmony_ci		 */
152362306a36Sopenharmony_ci		dma_rmb();
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci		prefetch(entry->xdp->data);
152662306a36Sopenharmony_ci		length = __le32_to_cpu(entry->desc_wb->properties) &
152762306a36Sopenharmony_ci			 TSNEP_DESC_LENGTH_MASK;
152862306a36Sopenharmony_ci		xsk_buff_set_size(entry->xdp, length - ETH_FCS_LEN);
152962306a36Sopenharmony_ci		xsk_buff_dma_sync_for_cpu(entry->xdp, rx->xsk_pool);
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci		/* RX metadata with timestamps is in front of actual data,
153262306a36Sopenharmony_ci		 * subtract metadata size to get length of actual data and
153362306a36Sopenharmony_ci		 * consider metadata size as offset of actual data during RX
153462306a36Sopenharmony_ci		 * processing
153562306a36Sopenharmony_ci		 */
153662306a36Sopenharmony_ci		length -= TSNEP_RX_INLINE_METADATA_SIZE;
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci		rx->read = (rx->read + 1) & TSNEP_RING_MASK;
153962306a36Sopenharmony_ci		desc_available++;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci		if (prog) {
154262306a36Sopenharmony_ci			bool consume;
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci			entry->xdp->data += TSNEP_RX_INLINE_METADATA_SIZE;
154562306a36Sopenharmony_ci			entry->xdp->data_meta += TSNEP_RX_INLINE_METADATA_SIZE;
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci			consume = tsnep_xdp_run_prog_zc(rx, prog, entry->xdp,
154862306a36Sopenharmony_ci							&xdp_status, tx_nq, tx);
154962306a36Sopenharmony_ci			if (consume) {
155062306a36Sopenharmony_ci				rx->packets++;
155162306a36Sopenharmony_ci				rx->bytes += length;
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci				entry->xdp = NULL;
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci				continue;
155662306a36Sopenharmony_ci			}
155762306a36Sopenharmony_ci		}
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci		page = page_pool_dev_alloc_pages(rx->page_pool);
156062306a36Sopenharmony_ci		if (page) {
156162306a36Sopenharmony_ci			memcpy(page_address(page) + TSNEP_RX_OFFSET,
156262306a36Sopenharmony_ci			       entry->xdp->data - TSNEP_RX_INLINE_METADATA_SIZE,
156362306a36Sopenharmony_ci			       length + TSNEP_RX_INLINE_METADATA_SIZE);
156462306a36Sopenharmony_ci			tsnep_rx_page(rx, napi, page, length);
156562306a36Sopenharmony_ci		} else {
156662306a36Sopenharmony_ci			rx->dropped++;
156762306a36Sopenharmony_ci		}
156862306a36Sopenharmony_ci		xsk_buff_free(entry->xdp);
156962306a36Sopenharmony_ci		entry->xdp = NULL;
157062306a36Sopenharmony_ci	}
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	if (xdp_status)
157362306a36Sopenharmony_ci		tsnep_finalize_xdp(rx->adapter, xdp_status, tx_nq, tx);
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci	if (desc_available)
157662306a36Sopenharmony_ci		desc_available -= tsnep_rx_refill_zc(rx, desc_available, false);
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	if (xsk_uses_need_wakeup(rx->xsk_pool)) {
157962306a36Sopenharmony_ci		if (desc_available)
158062306a36Sopenharmony_ci			xsk_set_rx_need_wakeup(rx->xsk_pool);
158162306a36Sopenharmony_ci		else
158262306a36Sopenharmony_ci			xsk_clear_rx_need_wakeup(rx->xsk_pool);
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci		return done;
158562306a36Sopenharmony_ci	}
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	return desc_available ? budget : done;
158862306a36Sopenharmony_ci}
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_cistatic bool tsnep_rx_pending(struct tsnep_rx *rx)
159162306a36Sopenharmony_ci{
159262306a36Sopenharmony_ci	struct tsnep_rx_entry *entry;
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	if (rx->read != rx->write) {
159562306a36Sopenharmony_ci		entry = &rx->entry[rx->read];
159662306a36Sopenharmony_ci		if ((__le32_to_cpu(entry->desc_wb->properties) &
159762306a36Sopenharmony_ci		     TSNEP_DESC_OWNER_COUNTER_MASK) ==
159862306a36Sopenharmony_ci		    (entry->properties & TSNEP_DESC_OWNER_COUNTER_MASK))
159962306a36Sopenharmony_ci			return true;
160062306a36Sopenharmony_ci	}
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	return false;
160362306a36Sopenharmony_ci}
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_cistatic int tsnep_rx_open(struct tsnep_rx *rx)
160662306a36Sopenharmony_ci{
160762306a36Sopenharmony_ci	int desc_available;
160862306a36Sopenharmony_ci	int retval;
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	retval = tsnep_rx_ring_create(rx);
161162306a36Sopenharmony_ci	if (retval)
161262306a36Sopenharmony_ci		return retval;
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	tsnep_rx_init(rx);
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	desc_available = tsnep_rx_desc_available(rx);
161762306a36Sopenharmony_ci	if (rx->xsk_pool)
161862306a36Sopenharmony_ci		retval = tsnep_rx_alloc_zc(rx, desc_available, false);
161962306a36Sopenharmony_ci	else
162062306a36Sopenharmony_ci		retval = tsnep_rx_alloc(rx, desc_available, false);
162162306a36Sopenharmony_ci	if (retval != desc_available) {
162262306a36Sopenharmony_ci		retval = -ENOMEM;
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci		goto alloc_failed;
162562306a36Sopenharmony_ci	}
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	/* prealloc pages to prevent allocation failures when XSK pool is
162862306a36Sopenharmony_ci	 * disabled at runtime
162962306a36Sopenharmony_ci	 */
163062306a36Sopenharmony_ci	if (rx->xsk_pool) {
163162306a36Sopenharmony_ci		retval = tsnep_rx_alloc_page_buffer(rx);
163262306a36Sopenharmony_ci		if (retval)
163362306a36Sopenharmony_ci			goto alloc_failed;
163462306a36Sopenharmony_ci	}
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci	return 0;
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_cialloc_failed:
163962306a36Sopenharmony_ci	tsnep_rx_ring_cleanup(rx);
164062306a36Sopenharmony_ci	return retval;
164162306a36Sopenharmony_ci}
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_cistatic void tsnep_rx_close(struct tsnep_rx *rx)
164462306a36Sopenharmony_ci{
164562306a36Sopenharmony_ci	if (rx->xsk_pool)
164662306a36Sopenharmony_ci		tsnep_rx_free_page_buffer(rx);
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	tsnep_rx_ring_cleanup(rx);
164962306a36Sopenharmony_ci}
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_cistatic void tsnep_rx_reopen(struct tsnep_rx *rx)
165262306a36Sopenharmony_ci{
165362306a36Sopenharmony_ci	struct page **page = rx->page_buffer;
165462306a36Sopenharmony_ci	int i;
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	tsnep_rx_init(rx);
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci	for (i = 0; i < TSNEP_RING_SIZE; i++) {
165962306a36Sopenharmony_ci		struct tsnep_rx_entry *entry = &rx->entry[i];
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci		/* defined initial values for properties are required for
166262306a36Sopenharmony_ci		 * correct owner counter checking
166362306a36Sopenharmony_ci		 */
166462306a36Sopenharmony_ci		entry->desc->properties = 0;
166562306a36Sopenharmony_ci		entry->desc_wb->properties = 0;
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci		/* prevent allocation failures by reusing kept pages */
166862306a36Sopenharmony_ci		if (*page) {
166962306a36Sopenharmony_ci			tsnep_rx_set_page(rx, entry, *page);
167062306a36Sopenharmony_ci			tsnep_rx_activate(rx, rx->write);
167162306a36Sopenharmony_ci			rx->write++;
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci			*page = NULL;
167462306a36Sopenharmony_ci			page++;
167562306a36Sopenharmony_ci		}
167662306a36Sopenharmony_ci	}
167762306a36Sopenharmony_ci}
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_cistatic void tsnep_rx_reopen_xsk(struct tsnep_rx *rx)
168062306a36Sopenharmony_ci{
168162306a36Sopenharmony_ci	struct page **page = rx->page_buffer;
168262306a36Sopenharmony_ci	u32 allocated;
168362306a36Sopenharmony_ci	int i;
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	tsnep_rx_init(rx);
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci	/* alloc all ring entries except the last one, because ring cannot be
168862306a36Sopenharmony_ci	 * filled completely, as many buffers as possible is enough as wakeup is
168962306a36Sopenharmony_ci	 * done if new buffers are available
169062306a36Sopenharmony_ci	 */
169162306a36Sopenharmony_ci	allocated = xsk_buff_alloc_batch(rx->xsk_pool, rx->xdp_batch,
169262306a36Sopenharmony_ci					 TSNEP_RING_SIZE - 1);
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci	for (i = 0; i < TSNEP_RING_SIZE; i++) {
169562306a36Sopenharmony_ci		struct tsnep_rx_entry *entry = &rx->entry[i];
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci		/* keep pages to prevent allocation failures when xsk is
169862306a36Sopenharmony_ci		 * disabled
169962306a36Sopenharmony_ci		 */
170062306a36Sopenharmony_ci		if (entry->page) {
170162306a36Sopenharmony_ci			*page = entry->page;
170262306a36Sopenharmony_ci			entry->page = NULL;
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci			page++;
170562306a36Sopenharmony_ci		}
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci		/* defined initial values for properties are required for
170862306a36Sopenharmony_ci		 * correct owner counter checking
170962306a36Sopenharmony_ci		 */
171062306a36Sopenharmony_ci		entry->desc->properties = 0;
171162306a36Sopenharmony_ci		entry->desc_wb->properties = 0;
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci		if (allocated) {
171462306a36Sopenharmony_ci			tsnep_rx_set_xdp(rx, entry,
171562306a36Sopenharmony_ci					 rx->xdp_batch[allocated - 1]);
171662306a36Sopenharmony_ci			tsnep_rx_activate(rx, rx->write);
171762306a36Sopenharmony_ci			rx->write++;
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci			allocated--;
172062306a36Sopenharmony_ci		}
172162306a36Sopenharmony_ci	}
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci	/* set need wakeup flag immediately if ring is not filled completely,
172462306a36Sopenharmony_ci	 * first polling would be too late as need wakeup signalisation would
172562306a36Sopenharmony_ci	 * be delayed for an indefinite time
172662306a36Sopenharmony_ci	 */
172762306a36Sopenharmony_ci	if (xsk_uses_need_wakeup(rx->xsk_pool)) {
172862306a36Sopenharmony_ci		int desc_available = tsnep_rx_desc_available(rx);
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci		if (desc_available)
173162306a36Sopenharmony_ci			xsk_set_rx_need_wakeup(rx->xsk_pool);
173262306a36Sopenharmony_ci		else
173362306a36Sopenharmony_ci			xsk_clear_rx_need_wakeup(rx->xsk_pool);
173462306a36Sopenharmony_ci	}
173562306a36Sopenharmony_ci}
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_cistatic bool tsnep_pending(struct tsnep_queue *queue)
173862306a36Sopenharmony_ci{
173962306a36Sopenharmony_ci	if (queue->tx && tsnep_tx_pending(queue->tx))
174062306a36Sopenharmony_ci		return true;
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci	if (queue->rx && tsnep_rx_pending(queue->rx))
174362306a36Sopenharmony_ci		return true;
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	return false;
174662306a36Sopenharmony_ci}
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_cistatic int tsnep_poll(struct napi_struct *napi, int budget)
174962306a36Sopenharmony_ci{
175062306a36Sopenharmony_ci	struct tsnep_queue *queue = container_of(napi, struct tsnep_queue,
175162306a36Sopenharmony_ci						 napi);
175262306a36Sopenharmony_ci	bool complete = true;
175362306a36Sopenharmony_ci	int done = 0;
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	if (queue->tx)
175662306a36Sopenharmony_ci		complete = tsnep_tx_poll(queue->tx, budget);
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	/* handle case where we are called by netpoll with a budget of 0 */
175962306a36Sopenharmony_ci	if (unlikely(budget <= 0))
176062306a36Sopenharmony_ci		return budget;
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_ci	if (queue->rx) {
176362306a36Sopenharmony_ci		done = queue->rx->xsk_pool ?
176462306a36Sopenharmony_ci		       tsnep_rx_poll_zc(queue->rx, napi, budget) :
176562306a36Sopenharmony_ci		       tsnep_rx_poll(queue->rx, napi, budget);
176662306a36Sopenharmony_ci		if (done >= budget)
176762306a36Sopenharmony_ci			complete = false;
176862306a36Sopenharmony_ci	}
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	/* if all work not completed, return budget and keep polling */
177162306a36Sopenharmony_ci	if (!complete)
177262306a36Sopenharmony_ci		return budget;
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	if (likely(napi_complete_done(napi, done))) {
177562306a36Sopenharmony_ci		tsnep_enable_irq(queue->adapter, queue->irq_mask);
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci		/* reschedule if work is already pending, prevent rotten packets
177862306a36Sopenharmony_ci		 * which are transmitted or received after polling but before
177962306a36Sopenharmony_ci		 * interrupt enable
178062306a36Sopenharmony_ci		 */
178162306a36Sopenharmony_ci		if (tsnep_pending(queue)) {
178262306a36Sopenharmony_ci			tsnep_disable_irq(queue->adapter, queue->irq_mask);
178362306a36Sopenharmony_ci			napi_schedule(napi);
178462306a36Sopenharmony_ci		}
178562306a36Sopenharmony_ci	}
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci	return min(done, budget - 1);
178862306a36Sopenharmony_ci}
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_cistatic int tsnep_request_irq(struct tsnep_queue *queue, bool first)
179162306a36Sopenharmony_ci{
179262306a36Sopenharmony_ci	const char *name = netdev_name(queue->adapter->netdev);
179362306a36Sopenharmony_ci	irq_handler_t handler;
179462306a36Sopenharmony_ci	void *dev;
179562306a36Sopenharmony_ci	int retval;
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci	if (first) {
179862306a36Sopenharmony_ci		sprintf(queue->name, "%s-mac", name);
179962306a36Sopenharmony_ci		handler = tsnep_irq;
180062306a36Sopenharmony_ci		dev = queue->adapter;
180162306a36Sopenharmony_ci	} else {
180262306a36Sopenharmony_ci		if (queue->tx && queue->rx)
180362306a36Sopenharmony_ci			snprintf(queue->name, sizeof(queue->name), "%s-txrx-%d",
180462306a36Sopenharmony_ci				 name, queue->rx->queue_index);
180562306a36Sopenharmony_ci		else if (queue->tx)
180662306a36Sopenharmony_ci			snprintf(queue->name, sizeof(queue->name), "%s-tx-%d",
180762306a36Sopenharmony_ci				 name, queue->tx->queue_index);
180862306a36Sopenharmony_ci		else
180962306a36Sopenharmony_ci			snprintf(queue->name, sizeof(queue->name), "%s-rx-%d",
181062306a36Sopenharmony_ci				 name, queue->rx->queue_index);
181162306a36Sopenharmony_ci		handler = tsnep_irq_txrx;
181262306a36Sopenharmony_ci		dev = queue;
181362306a36Sopenharmony_ci	}
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci	retval = request_irq(queue->irq, handler, 0, queue->name, dev);
181662306a36Sopenharmony_ci	if (retval) {
181762306a36Sopenharmony_ci		/* if name is empty, then interrupt won't be freed */
181862306a36Sopenharmony_ci		memset(queue->name, 0, sizeof(queue->name));
181962306a36Sopenharmony_ci	}
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci	return retval;
182262306a36Sopenharmony_ci}
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_cistatic void tsnep_free_irq(struct tsnep_queue *queue, bool first)
182562306a36Sopenharmony_ci{
182662306a36Sopenharmony_ci	void *dev;
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ci	if (!strlen(queue->name))
182962306a36Sopenharmony_ci		return;
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	if (first)
183262306a36Sopenharmony_ci		dev = queue->adapter;
183362306a36Sopenharmony_ci	else
183462306a36Sopenharmony_ci		dev = queue;
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci	free_irq(queue->irq, dev);
183762306a36Sopenharmony_ci	memset(queue->name, 0, sizeof(queue->name));
183862306a36Sopenharmony_ci}
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_cistatic void tsnep_queue_close(struct tsnep_queue *queue, bool first)
184162306a36Sopenharmony_ci{
184262306a36Sopenharmony_ci	struct tsnep_rx *rx = queue->rx;
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci	tsnep_free_irq(queue, first);
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci	if (rx) {
184762306a36Sopenharmony_ci		if (xdp_rxq_info_is_reg(&rx->xdp_rxq))
184862306a36Sopenharmony_ci			xdp_rxq_info_unreg(&rx->xdp_rxq);
184962306a36Sopenharmony_ci		if (xdp_rxq_info_is_reg(&rx->xdp_rxq_zc))
185062306a36Sopenharmony_ci			xdp_rxq_info_unreg(&rx->xdp_rxq_zc);
185162306a36Sopenharmony_ci	}
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ci	netif_napi_del(&queue->napi);
185462306a36Sopenharmony_ci}
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_cistatic int tsnep_queue_open(struct tsnep_adapter *adapter,
185762306a36Sopenharmony_ci			    struct tsnep_queue *queue, bool first)
185862306a36Sopenharmony_ci{
185962306a36Sopenharmony_ci	struct tsnep_rx *rx = queue->rx;
186062306a36Sopenharmony_ci	struct tsnep_tx *tx = queue->tx;
186162306a36Sopenharmony_ci	int retval;
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	netif_napi_add(adapter->netdev, &queue->napi, tsnep_poll);
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	if (rx) {
186662306a36Sopenharmony_ci		/* choose TX queue for XDP_TX */
186762306a36Sopenharmony_ci		if (tx)
186862306a36Sopenharmony_ci			rx->tx_queue_index = tx->queue_index;
186962306a36Sopenharmony_ci		else if (rx->queue_index < adapter->num_tx_queues)
187062306a36Sopenharmony_ci			rx->tx_queue_index = rx->queue_index;
187162306a36Sopenharmony_ci		else
187262306a36Sopenharmony_ci			rx->tx_queue_index = 0;
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci		/* prepare both memory models to eliminate possible registration
187562306a36Sopenharmony_ci		 * errors when memory model is switched between page pool and
187662306a36Sopenharmony_ci		 * XSK pool during runtime
187762306a36Sopenharmony_ci		 */
187862306a36Sopenharmony_ci		retval = xdp_rxq_info_reg(&rx->xdp_rxq, adapter->netdev,
187962306a36Sopenharmony_ci					  rx->queue_index, queue->napi.napi_id);
188062306a36Sopenharmony_ci		if (retval)
188162306a36Sopenharmony_ci			goto failed;
188262306a36Sopenharmony_ci		retval = xdp_rxq_info_reg_mem_model(&rx->xdp_rxq,
188362306a36Sopenharmony_ci						    MEM_TYPE_PAGE_POOL,
188462306a36Sopenharmony_ci						    rx->page_pool);
188562306a36Sopenharmony_ci		if (retval)
188662306a36Sopenharmony_ci			goto failed;
188762306a36Sopenharmony_ci		retval = xdp_rxq_info_reg(&rx->xdp_rxq_zc, adapter->netdev,
188862306a36Sopenharmony_ci					  rx->queue_index, queue->napi.napi_id);
188962306a36Sopenharmony_ci		if (retval)
189062306a36Sopenharmony_ci			goto failed;
189162306a36Sopenharmony_ci		retval = xdp_rxq_info_reg_mem_model(&rx->xdp_rxq_zc,
189262306a36Sopenharmony_ci						    MEM_TYPE_XSK_BUFF_POOL,
189362306a36Sopenharmony_ci						    NULL);
189462306a36Sopenharmony_ci		if (retval)
189562306a36Sopenharmony_ci			goto failed;
189662306a36Sopenharmony_ci		if (rx->xsk_pool)
189762306a36Sopenharmony_ci			xsk_pool_set_rxq_info(rx->xsk_pool, &rx->xdp_rxq_zc);
189862306a36Sopenharmony_ci	}
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci	retval = tsnep_request_irq(queue, first);
190162306a36Sopenharmony_ci	if (retval) {
190262306a36Sopenharmony_ci		netif_err(adapter, drv, adapter->netdev,
190362306a36Sopenharmony_ci			  "can't get assigned irq %d.\n", queue->irq);
190462306a36Sopenharmony_ci		goto failed;
190562306a36Sopenharmony_ci	}
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_ci	return 0;
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_cifailed:
191062306a36Sopenharmony_ci	tsnep_queue_close(queue, first);
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	return retval;
191362306a36Sopenharmony_ci}
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_cistatic void tsnep_queue_enable(struct tsnep_queue *queue)
191662306a36Sopenharmony_ci{
191762306a36Sopenharmony_ci	napi_enable(&queue->napi);
191862306a36Sopenharmony_ci	tsnep_enable_irq(queue->adapter, queue->irq_mask);
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci	if (queue->tx)
192162306a36Sopenharmony_ci		tsnep_tx_enable(queue->tx);
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci	if (queue->rx)
192462306a36Sopenharmony_ci		tsnep_rx_enable(queue->rx);
192562306a36Sopenharmony_ci}
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_cistatic void tsnep_queue_disable(struct tsnep_queue *queue)
192862306a36Sopenharmony_ci{
192962306a36Sopenharmony_ci	if (queue->tx)
193062306a36Sopenharmony_ci		tsnep_tx_disable(queue->tx, &queue->napi);
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci	napi_disable(&queue->napi);
193362306a36Sopenharmony_ci	tsnep_disable_irq(queue->adapter, queue->irq_mask);
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	/* disable RX after NAPI polling has been disabled, because RX can be
193662306a36Sopenharmony_ci	 * enabled during NAPI polling
193762306a36Sopenharmony_ci	 */
193862306a36Sopenharmony_ci	if (queue->rx)
193962306a36Sopenharmony_ci		tsnep_rx_disable(queue->rx);
194062306a36Sopenharmony_ci}
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_cistatic int tsnep_netdev_open(struct net_device *netdev)
194362306a36Sopenharmony_ci{
194462306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
194562306a36Sopenharmony_ci	int i, retval;
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	for (i = 0; i < adapter->num_queues; i++) {
194862306a36Sopenharmony_ci		if (adapter->queue[i].tx) {
194962306a36Sopenharmony_ci			retval = tsnep_tx_open(adapter->queue[i].tx);
195062306a36Sopenharmony_ci			if (retval)
195162306a36Sopenharmony_ci				goto failed;
195262306a36Sopenharmony_ci		}
195362306a36Sopenharmony_ci		if (adapter->queue[i].rx) {
195462306a36Sopenharmony_ci			retval = tsnep_rx_open(adapter->queue[i].rx);
195562306a36Sopenharmony_ci			if (retval)
195662306a36Sopenharmony_ci				goto failed;
195762306a36Sopenharmony_ci		}
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci		retval = tsnep_queue_open(adapter, &adapter->queue[i], i == 0);
196062306a36Sopenharmony_ci		if (retval)
196162306a36Sopenharmony_ci			goto failed;
196262306a36Sopenharmony_ci	}
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	retval = netif_set_real_num_tx_queues(adapter->netdev,
196562306a36Sopenharmony_ci					      adapter->num_tx_queues);
196662306a36Sopenharmony_ci	if (retval)
196762306a36Sopenharmony_ci		goto failed;
196862306a36Sopenharmony_ci	retval = netif_set_real_num_rx_queues(adapter->netdev,
196962306a36Sopenharmony_ci					      adapter->num_rx_queues);
197062306a36Sopenharmony_ci	if (retval)
197162306a36Sopenharmony_ci		goto failed;
197262306a36Sopenharmony_ci
197362306a36Sopenharmony_ci	tsnep_enable_irq(adapter, ECM_INT_LINK);
197462306a36Sopenharmony_ci	retval = tsnep_phy_open(adapter);
197562306a36Sopenharmony_ci	if (retval)
197662306a36Sopenharmony_ci		goto phy_failed;
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	for (i = 0; i < adapter->num_queues; i++)
197962306a36Sopenharmony_ci		tsnep_queue_enable(&adapter->queue[i]);
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	return 0;
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ciphy_failed:
198462306a36Sopenharmony_ci	tsnep_disable_irq(adapter, ECM_INT_LINK);
198562306a36Sopenharmony_cifailed:
198662306a36Sopenharmony_ci	for (i = 0; i < adapter->num_queues; i++) {
198762306a36Sopenharmony_ci		tsnep_queue_close(&adapter->queue[i], i == 0);
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ci		if (adapter->queue[i].rx)
199062306a36Sopenharmony_ci			tsnep_rx_close(adapter->queue[i].rx);
199162306a36Sopenharmony_ci		if (adapter->queue[i].tx)
199262306a36Sopenharmony_ci			tsnep_tx_close(adapter->queue[i].tx);
199362306a36Sopenharmony_ci	}
199462306a36Sopenharmony_ci	return retval;
199562306a36Sopenharmony_ci}
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_cistatic int tsnep_netdev_close(struct net_device *netdev)
199862306a36Sopenharmony_ci{
199962306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
200062306a36Sopenharmony_ci	int i;
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	tsnep_disable_irq(adapter, ECM_INT_LINK);
200362306a36Sopenharmony_ci	tsnep_phy_close(adapter);
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci	for (i = 0; i < adapter->num_queues; i++) {
200662306a36Sopenharmony_ci		tsnep_queue_disable(&adapter->queue[i]);
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci		tsnep_queue_close(&adapter->queue[i], i == 0);
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_ci		if (adapter->queue[i].rx)
201162306a36Sopenharmony_ci			tsnep_rx_close(adapter->queue[i].rx);
201262306a36Sopenharmony_ci		if (adapter->queue[i].tx)
201362306a36Sopenharmony_ci			tsnep_tx_close(adapter->queue[i].tx);
201462306a36Sopenharmony_ci	}
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci	return 0;
201762306a36Sopenharmony_ci}
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ciint tsnep_enable_xsk(struct tsnep_queue *queue, struct xsk_buff_pool *pool)
202062306a36Sopenharmony_ci{
202162306a36Sopenharmony_ci	bool running = netif_running(queue->adapter->netdev);
202262306a36Sopenharmony_ci	u32 frame_size;
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_ci	frame_size = xsk_pool_get_rx_frame_size(pool);
202562306a36Sopenharmony_ci	if (frame_size < TSNEP_XSK_RX_BUF_SIZE)
202662306a36Sopenharmony_ci		return -EOPNOTSUPP;
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_ci	queue->rx->page_buffer = kcalloc(TSNEP_RING_SIZE,
202962306a36Sopenharmony_ci					 sizeof(*queue->rx->page_buffer),
203062306a36Sopenharmony_ci					 GFP_KERNEL);
203162306a36Sopenharmony_ci	if (!queue->rx->page_buffer)
203262306a36Sopenharmony_ci		return -ENOMEM;
203362306a36Sopenharmony_ci	queue->rx->xdp_batch = kcalloc(TSNEP_RING_SIZE,
203462306a36Sopenharmony_ci				       sizeof(*queue->rx->xdp_batch),
203562306a36Sopenharmony_ci				       GFP_KERNEL);
203662306a36Sopenharmony_ci	if (!queue->rx->xdp_batch) {
203762306a36Sopenharmony_ci		kfree(queue->rx->page_buffer);
203862306a36Sopenharmony_ci		queue->rx->page_buffer = NULL;
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_ci		return -ENOMEM;
204162306a36Sopenharmony_ci	}
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_ci	xsk_pool_set_rxq_info(pool, &queue->rx->xdp_rxq_zc);
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_ci	if (running)
204662306a36Sopenharmony_ci		tsnep_queue_disable(queue);
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	queue->tx->xsk_pool = pool;
204962306a36Sopenharmony_ci	queue->rx->xsk_pool = pool;
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci	if (running) {
205262306a36Sopenharmony_ci		tsnep_rx_reopen_xsk(queue->rx);
205362306a36Sopenharmony_ci		tsnep_queue_enable(queue);
205462306a36Sopenharmony_ci	}
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci	return 0;
205762306a36Sopenharmony_ci}
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_civoid tsnep_disable_xsk(struct tsnep_queue *queue)
206062306a36Sopenharmony_ci{
206162306a36Sopenharmony_ci	bool running = netif_running(queue->adapter->netdev);
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	if (running)
206462306a36Sopenharmony_ci		tsnep_queue_disable(queue);
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	tsnep_rx_free_zc(queue->rx);
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_ci	queue->rx->xsk_pool = NULL;
206962306a36Sopenharmony_ci	queue->tx->xsk_pool = NULL;
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci	if (running) {
207262306a36Sopenharmony_ci		tsnep_rx_reopen(queue->rx);
207362306a36Sopenharmony_ci		tsnep_queue_enable(queue);
207462306a36Sopenharmony_ci	}
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci	kfree(queue->rx->xdp_batch);
207762306a36Sopenharmony_ci	queue->rx->xdp_batch = NULL;
207862306a36Sopenharmony_ci	kfree(queue->rx->page_buffer);
207962306a36Sopenharmony_ci	queue->rx->page_buffer = NULL;
208062306a36Sopenharmony_ci}
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_cistatic netdev_tx_t tsnep_netdev_xmit_frame(struct sk_buff *skb,
208362306a36Sopenharmony_ci					   struct net_device *netdev)
208462306a36Sopenharmony_ci{
208562306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
208662306a36Sopenharmony_ci	u16 queue_mapping = skb_get_queue_mapping(skb);
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_ci	if (queue_mapping >= adapter->num_tx_queues)
208962306a36Sopenharmony_ci		queue_mapping = 0;
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci	return tsnep_xmit_frame_ring(skb, &adapter->tx[queue_mapping]);
209262306a36Sopenharmony_ci}
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_cistatic int tsnep_netdev_ioctl(struct net_device *netdev, struct ifreq *ifr,
209562306a36Sopenharmony_ci			      int cmd)
209662306a36Sopenharmony_ci{
209762306a36Sopenharmony_ci	if (!netif_running(netdev))
209862306a36Sopenharmony_ci		return -EINVAL;
209962306a36Sopenharmony_ci	if (cmd == SIOCSHWTSTAMP || cmd == SIOCGHWTSTAMP)
210062306a36Sopenharmony_ci		return tsnep_ptp_ioctl(netdev, ifr, cmd);
210162306a36Sopenharmony_ci	return phy_mii_ioctl(netdev->phydev, ifr, cmd);
210262306a36Sopenharmony_ci}
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_cistatic void tsnep_netdev_set_multicast(struct net_device *netdev)
210562306a36Sopenharmony_ci{
210662306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_ci	u16 rx_filter = 0;
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci	/* configured MAC address and broadcasts are never filtered */
211162306a36Sopenharmony_ci	if (netdev->flags & IFF_PROMISC) {
211262306a36Sopenharmony_ci		rx_filter |= TSNEP_RX_FILTER_ACCEPT_ALL_MULTICASTS;
211362306a36Sopenharmony_ci		rx_filter |= TSNEP_RX_FILTER_ACCEPT_ALL_UNICASTS;
211462306a36Sopenharmony_ci	} else if (!netdev_mc_empty(netdev) || (netdev->flags & IFF_ALLMULTI)) {
211562306a36Sopenharmony_ci		rx_filter |= TSNEP_RX_FILTER_ACCEPT_ALL_MULTICASTS;
211662306a36Sopenharmony_ci	}
211762306a36Sopenharmony_ci	iowrite16(rx_filter, adapter->addr + TSNEP_RX_FILTER);
211862306a36Sopenharmony_ci}
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_cistatic void tsnep_netdev_get_stats64(struct net_device *netdev,
212162306a36Sopenharmony_ci				     struct rtnl_link_stats64 *stats)
212262306a36Sopenharmony_ci{
212362306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
212462306a36Sopenharmony_ci	u32 reg;
212562306a36Sopenharmony_ci	u32 val;
212662306a36Sopenharmony_ci	int i;
212762306a36Sopenharmony_ci
212862306a36Sopenharmony_ci	for (i = 0; i < adapter->num_tx_queues; i++) {
212962306a36Sopenharmony_ci		stats->tx_packets += adapter->tx[i].packets;
213062306a36Sopenharmony_ci		stats->tx_bytes += adapter->tx[i].bytes;
213162306a36Sopenharmony_ci		stats->tx_dropped += adapter->tx[i].dropped;
213262306a36Sopenharmony_ci	}
213362306a36Sopenharmony_ci	for (i = 0; i < adapter->num_rx_queues; i++) {
213462306a36Sopenharmony_ci		stats->rx_packets += adapter->rx[i].packets;
213562306a36Sopenharmony_ci		stats->rx_bytes += adapter->rx[i].bytes;
213662306a36Sopenharmony_ci		stats->rx_dropped += adapter->rx[i].dropped;
213762306a36Sopenharmony_ci		stats->multicast += adapter->rx[i].multicast;
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci		reg = ioread32(adapter->addr + TSNEP_QUEUE(i) +
214062306a36Sopenharmony_ci			       TSNEP_RX_STATISTIC);
214162306a36Sopenharmony_ci		val = (reg & TSNEP_RX_STATISTIC_NO_DESC_MASK) >>
214262306a36Sopenharmony_ci		      TSNEP_RX_STATISTIC_NO_DESC_SHIFT;
214362306a36Sopenharmony_ci		stats->rx_dropped += val;
214462306a36Sopenharmony_ci		val = (reg & TSNEP_RX_STATISTIC_BUFFER_TOO_SMALL_MASK) >>
214562306a36Sopenharmony_ci		      TSNEP_RX_STATISTIC_BUFFER_TOO_SMALL_SHIFT;
214662306a36Sopenharmony_ci		stats->rx_dropped += val;
214762306a36Sopenharmony_ci		val = (reg & TSNEP_RX_STATISTIC_FIFO_OVERFLOW_MASK) >>
214862306a36Sopenharmony_ci		      TSNEP_RX_STATISTIC_FIFO_OVERFLOW_SHIFT;
214962306a36Sopenharmony_ci		stats->rx_errors += val;
215062306a36Sopenharmony_ci		stats->rx_fifo_errors += val;
215162306a36Sopenharmony_ci		val = (reg & TSNEP_RX_STATISTIC_INVALID_FRAME_MASK) >>
215262306a36Sopenharmony_ci		      TSNEP_RX_STATISTIC_INVALID_FRAME_SHIFT;
215362306a36Sopenharmony_ci		stats->rx_errors += val;
215462306a36Sopenharmony_ci		stats->rx_frame_errors += val;
215562306a36Sopenharmony_ci	}
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_ci	reg = ioread32(adapter->addr + ECM_STAT);
215862306a36Sopenharmony_ci	val = (reg & ECM_STAT_RX_ERR_MASK) >> ECM_STAT_RX_ERR_SHIFT;
215962306a36Sopenharmony_ci	stats->rx_errors += val;
216062306a36Sopenharmony_ci	val = (reg & ECM_STAT_INV_FRM_MASK) >> ECM_STAT_INV_FRM_SHIFT;
216162306a36Sopenharmony_ci	stats->rx_errors += val;
216262306a36Sopenharmony_ci	stats->rx_crc_errors += val;
216362306a36Sopenharmony_ci	val = (reg & ECM_STAT_FWD_RX_ERR_MASK) >> ECM_STAT_FWD_RX_ERR_SHIFT;
216462306a36Sopenharmony_ci	stats->rx_errors += val;
216562306a36Sopenharmony_ci}
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_cistatic void tsnep_mac_set_address(struct tsnep_adapter *adapter, u8 *addr)
216862306a36Sopenharmony_ci{
216962306a36Sopenharmony_ci	iowrite32(*(u32 *)addr, adapter->addr + TSNEP_MAC_ADDRESS_LOW);
217062306a36Sopenharmony_ci	iowrite16(*(u16 *)(addr + sizeof(u32)),
217162306a36Sopenharmony_ci		  adapter->addr + TSNEP_MAC_ADDRESS_HIGH);
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci	ether_addr_copy(adapter->mac_address, addr);
217462306a36Sopenharmony_ci	netif_info(adapter, drv, adapter->netdev, "MAC address set to %pM\n",
217562306a36Sopenharmony_ci		   addr);
217662306a36Sopenharmony_ci}
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_cistatic int tsnep_netdev_set_mac_address(struct net_device *netdev, void *addr)
217962306a36Sopenharmony_ci{
218062306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
218162306a36Sopenharmony_ci	struct sockaddr *sock_addr = addr;
218262306a36Sopenharmony_ci	int retval;
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_ci	retval = eth_prepare_mac_addr_change(netdev, sock_addr);
218562306a36Sopenharmony_ci	if (retval)
218662306a36Sopenharmony_ci		return retval;
218762306a36Sopenharmony_ci	eth_hw_addr_set(netdev, sock_addr->sa_data);
218862306a36Sopenharmony_ci	tsnep_mac_set_address(adapter, sock_addr->sa_data);
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	return 0;
219162306a36Sopenharmony_ci}
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_cistatic int tsnep_netdev_set_features(struct net_device *netdev,
219462306a36Sopenharmony_ci				     netdev_features_t features)
219562306a36Sopenharmony_ci{
219662306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(netdev);
219762306a36Sopenharmony_ci	netdev_features_t changed = netdev->features ^ features;
219862306a36Sopenharmony_ci	bool enable;
219962306a36Sopenharmony_ci	int retval = 0;
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci	if (changed & NETIF_F_LOOPBACK) {
220262306a36Sopenharmony_ci		enable = !!(features & NETIF_F_LOOPBACK);
220362306a36Sopenharmony_ci		retval = tsnep_phy_loopback(adapter, enable);
220462306a36Sopenharmony_ci	}
220562306a36Sopenharmony_ci
220662306a36Sopenharmony_ci	return retval;
220762306a36Sopenharmony_ci}
220862306a36Sopenharmony_ci
220962306a36Sopenharmony_cistatic ktime_t tsnep_netdev_get_tstamp(struct net_device *netdev,
221062306a36Sopenharmony_ci				       const struct skb_shared_hwtstamps *hwtstamps,
221162306a36Sopenharmony_ci				       bool cycles)
221262306a36Sopenharmony_ci{
221362306a36Sopenharmony_ci	struct tsnep_rx_inline *rx_inline = hwtstamps->netdev_data;
221462306a36Sopenharmony_ci	u64 timestamp;
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	if (cycles)
221762306a36Sopenharmony_ci		timestamp = __le64_to_cpu(rx_inline->counter);
221862306a36Sopenharmony_ci	else
221962306a36Sopenharmony_ci		timestamp = __le64_to_cpu(rx_inline->timestamp);
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_ci	return ns_to_ktime(timestamp);
222262306a36Sopenharmony_ci}
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_cistatic int tsnep_netdev_bpf(struct net_device *dev, struct netdev_bpf *bpf)
222562306a36Sopenharmony_ci{
222662306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(dev);
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_ci	switch (bpf->command) {
222962306a36Sopenharmony_ci	case XDP_SETUP_PROG:
223062306a36Sopenharmony_ci		return tsnep_xdp_setup_prog(adapter, bpf->prog, bpf->extack);
223162306a36Sopenharmony_ci	case XDP_SETUP_XSK_POOL:
223262306a36Sopenharmony_ci		return tsnep_xdp_setup_pool(adapter, bpf->xsk.pool,
223362306a36Sopenharmony_ci					    bpf->xsk.queue_id);
223462306a36Sopenharmony_ci	default:
223562306a36Sopenharmony_ci		return -EOPNOTSUPP;
223662306a36Sopenharmony_ci	}
223762306a36Sopenharmony_ci}
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_cistatic struct tsnep_tx *tsnep_xdp_get_tx(struct tsnep_adapter *adapter, u32 cpu)
224062306a36Sopenharmony_ci{
224162306a36Sopenharmony_ci	if (cpu >= TSNEP_MAX_QUEUES)
224262306a36Sopenharmony_ci		cpu &= TSNEP_MAX_QUEUES - 1;
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_ci	while (cpu >= adapter->num_tx_queues)
224562306a36Sopenharmony_ci		cpu -= adapter->num_tx_queues;
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_ci	return &adapter->tx[cpu];
224862306a36Sopenharmony_ci}
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_cistatic int tsnep_netdev_xdp_xmit(struct net_device *dev, int n,
225162306a36Sopenharmony_ci				 struct xdp_frame **xdp, u32 flags)
225262306a36Sopenharmony_ci{
225362306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(dev);
225462306a36Sopenharmony_ci	u32 cpu = smp_processor_id();
225562306a36Sopenharmony_ci	struct netdev_queue *nq;
225662306a36Sopenharmony_ci	struct tsnep_tx *tx;
225762306a36Sopenharmony_ci	int nxmit;
225862306a36Sopenharmony_ci	bool xmit;
225962306a36Sopenharmony_ci
226062306a36Sopenharmony_ci	if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK))
226162306a36Sopenharmony_ci		return -EINVAL;
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci	tx = tsnep_xdp_get_tx(adapter, cpu);
226462306a36Sopenharmony_ci	nq = netdev_get_tx_queue(adapter->netdev, tx->queue_index);
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	__netif_tx_lock(nq, cpu);
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci	for (nxmit = 0; nxmit < n; nxmit++) {
226962306a36Sopenharmony_ci		xmit = tsnep_xdp_xmit_frame_ring(xdp[nxmit], tx,
227062306a36Sopenharmony_ci						 TSNEP_TX_TYPE_XDP_NDO);
227162306a36Sopenharmony_ci		if (!xmit)
227262306a36Sopenharmony_ci			break;
227362306a36Sopenharmony_ci
227462306a36Sopenharmony_ci		/* avoid transmit queue timeout since we share it with the slow
227562306a36Sopenharmony_ci		 * path
227662306a36Sopenharmony_ci		 */
227762306a36Sopenharmony_ci		txq_trans_cond_update(nq);
227862306a36Sopenharmony_ci	}
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci	if (flags & XDP_XMIT_FLUSH)
228162306a36Sopenharmony_ci		tsnep_xdp_xmit_flush(tx);
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci	__netif_tx_unlock(nq);
228462306a36Sopenharmony_ci
228562306a36Sopenharmony_ci	return nxmit;
228662306a36Sopenharmony_ci}
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_cistatic int tsnep_netdev_xsk_wakeup(struct net_device *dev, u32 queue_id,
228962306a36Sopenharmony_ci				   u32 flags)
229062306a36Sopenharmony_ci{
229162306a36Sopenharmony_ci	struct tsnep_adapter *adapter = netdev_priv(dev);
229262306a36Sopenharmony_ci	struct tsnep_queue *queue;
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci	if (queue_id >= adapter->num_rx_queues ||
229562306a36Sopenharmony_ci	    queue_id >= adapter->num_tx_queues)
229662306a36Sopenharmony_ci		return -EINVAL;
229762306a36Sopenharmony_ci
229862306a36Sopenharmony_ci	queue = &adapter->queue[queue_id];
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_ci	if (!napi_if_scheduled_mark_missed(&queue->napi))
230162306a36Sopenharmony_ci		napi_schedule(&queue->napi);
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_ci	return 0;
230462306a36Sopenharmony_ci}
230562306a36Sopenharmony_ci
230662306a36Sopenharmony_cistatic const struct net_device_ops tsnep_netdev_ops = {
230762306a36Sopenharmony_ci	.ndo_open = tsnep_netdev_open,
230862306a36Sopenharmony_ci	.ndo_stop = tsnep_netdev_close,
230962306a36Sopenharmony_ci	.ndo_start_xmit = tsnep_netdev_xmit_frame,
231062306a36Sopenharmony_ci	.ndo_eth_ioctl = tsnep_netdev_ioctl,
231162306a36Sopenharmony_ci	.ndo_set_rx_mode = tsnep_netdev_set_multicast,
231262306a36Sopenharmony_ci	.ndo_get_stats64 = tsnep_netdev_get_stats64,
231362306a36Sopenharmony_ci	.ndo_set_mac_address = tsnep_netdev_set_mac_address,
231462306a36Sopenharmony_ci	.ndo_set_features = tsnep_netdev_set_features,
231562306a36Sopenharmony_ci	.ndo_get_tstamp = tsnep_netdev_get_tstamp,
231662306a36Sopenharmony_ci	.ndo_setup_tc = tsnep_tc_setup,
231762306a36Sopenharmony_ci	.ndo_bpf = tsnep_netdev_bpf,
231862306a36Sopenharmony_ci	.ndo_xdp_xmit = tsnep_netdev_xdp_xmit,
231962306a36Sopenharmony_ci	.ndo_xsk_wakeup = tsnep_netdev_xsk_wakeup,
232062306a36Sopenharmony_ci};
232162306a36Sopenharmony_ci
232262306a36Sopenharmony_cistatic int tsnep_mac_init(struct tsnep_adapter *adapter)
232362306a36Sopenharmony_ci{
232462306a36Sopenharmony_ci	int retval;
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_ci	/* initialize RX filtering, at least configured MAC address and
232762306a36Sopenharmony_ci	 * broadcast are not filtered
232862306a36Sopenharmony_ci	 */
232962306a36Sopenharmony_ci	iowrite16(0, adapter->addr + TSNEP_RX_FILTER);
233062306a36Sopenharmony_ci
233162306a36Sopenharmony_ci	/* try to get MAC address in the following order:
233262306a36Sopenharmony_ci	 * - device tree
233362306a36Sopenharmony_ci	 * - valid MAC address already set
233462306a36Sopenharmony_ci	 * - MAC address register if valid
233562306a36Sopenharmony_ci	 * - random MAC address
233662306a36Sopenharmony_ci	 */
233762306a36Sopenharmony_ci	retval = of_get_mac_address(adapter->pdev->dev.of_node,
233862306a36Sopenharmony_ci				    adapter->mac_address);
233962306a36Sopenharmony_ci	if (retval == -EPROBE_DEFER)
234062306a36Sopenharmony_ci		return retval;
234162306a36Sopenharmony_ci	if (retval && !is_valid_ether_addr(adapter->mac_address)) {
234262306a36Sopenharmony_ci		*(u32 *)adapter->mac_address =
234362306a36Sopenharmony_ci			ioread32(adapter->addr + TSNEP_MAC_ADDRESS_LOW);
234462306a36Sopenharmony_ci		*(u16 *)(adapter->mac_address + sizeof(u32)) =
234562306a36Sopenharmony_ci			ioread16(adapter->addr + TSNEP_MAC_ADDRESS_HIGH);
234662306a36Sopenharmony_ci		if (!is_valid_ether_addr(adapter->mac_address))
234762306a36Sopenharmony_ci			eth_random_addr(adapter->mac_address);
234862306a36Sopenharmony_ci	}
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_ci	tsnep_mac_set_address(adapter, adapter->mac_address);
235162306a36Sopenharmony_ci	eth_hw_addr_set(adapter->netdev, adapter->mac_address);
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci	return 0;
235462306a36Sopenharmony_ci}
235562306a36Sopenharmony_ci
235662306a36Sopenharmony_cistatic int tsnep_mdio_init(struct tsnep_adapter *adapter)
235762306a36Sopenharmony_ci{
235862306a36Sopenharmony_ci	struct device_node *np = adapter->pdev->dev.of_node;
235962306a36Sopenharmony_ci	int retval;
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci	if (np) {
236262306a36Sopenharmony_ci		np = of_get_child_by_name(np, "mdio");
236362306a36Sopenharmony_ci		if (!np)
236462306a36Sopenharmony_ci			return 0;
236562306a36Sopenharmony_ci
236662306a36Sopenharmony_ci		adapter->suppress_preamble =
236762306a36Sopenharmony_ci			of_property_read_bool(np, "suppress-preamble");
236862306a36Sopenharmony_ci	}
236962306a36Sopenharmony_ci
237062306a36Sopenharmony_ci	adapter->mdiobus = devm_mdiobus_alloc(&adapter->pdev->dev);
237162306a36Sopenharmony_ci	if (!adapter->mdiobus) {
237262306a36Sopenharmony_ci		retval = -ENOMEM;
237362306a36Sopenharmony_ci
237462306a36Sopenharmony_ci		goto out;
237562306a36Sopenharmony_ci	}
237662306a36Sopenharmony_ci
237762306a36Sopenharmony_ci	adapter->mdiobus->priv = (void *)adapter;
237862306a36Sopenharmony_ci	adapter->mdiobus->parent = &adapter->pdev->dev;
237962306a36Sopenharmony_ci	adapter->mdiobus->read = tsnep_mdiobus_read;
238062306a36Sopenharmony_ci	adapter->mdiobus->write = tsnep_mdiobus_write;
238162306a36Sopenharmony_ci	adapter->mdiobus->name = TSNEP "-mdiobus";
238262306a36Sopenharmony_ci	snprintf(adapter->mdiobus->id, MII_BUS_ID_SIZE, "%s",
238362306a36Sopenharmony_ci		 adapter->pdev->name);
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_ci	/* do not scan broadcast address */
238662306a36Sopenharmony_ci	adapter->mdiobus->phy_mask = 0x0000001;
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci	retval = of_mdiobus_register(adapter->mdiobus, np);
238962306a36Sopenharmony_ci
239062306a36Sopenharmony_ciout:
239162306a36Sopenharmony_ci	of_node_put(np);
239262306a36Sopenharmony_ci
239362306a36Sopenharmony_ci	return retval;
239462306a36Sopenharmony_ci}
239562306a36Sopenharmony_ci
239662306a36Sopenharmony_cistatic int tsnep_phy_init(struct tsnep_adapter *adapter)
239762306a36Sopenharmony_ci{
239862306a36Sopenharmony_ci	struct device_node *phy_node;
239962306a36Sopenharmony_ci	int retval;
240062306a36Sopenharmony_ci
240162306a36Sopenharmony_ci	retval = of_get_phy_mode(adapter->pdev->dev.of_node,
240262306a36Sopenharmony_ci				 &adapter->phy_mode);
240362306a36Sopenharmony_ci	if (retval)
240462306a36Sopenharmony_ci		adapter->phy_mode = PHY_INTERFACE_MODE_GMII;
240562306a36Sopenharmony_ci
240662306a36Sopenharmony_ci	phy_node = of_parse_phandle(adapter->pdev->dev.of_node, "phy-handle",
240762306a36Sopenharmony_ci				    0);
240862306a36Sopenharmony_ci	adapter->phydev = of_phy_find_device(phy_node);
240962306a36Sopenharmony_ci	of_node_put(phy_node);
241062306a36Sopenharmony_ci	if (!adapter->phydev && adapter->mdiobus)
241162306a36Sopenharmony_ci		adapter->phydev = phy_find_first(adapter->mdiobus);
241262306a36Sopenharmony_ci	if (!adapter->phydev)
241362306a36Sopenharmony_ci		return -EIO;
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_ci	return 0;
241662306a36Sopenharmony_ci}
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_cistatic int tsnep_queue_init(struct tsnep_adapter *adapter, int queue_count)
241962306a36Sopenharmony_ci{
242062306a36Sopenharmony_ci	u32 irq_mask = ECM_INT_TX_0 | ECM_INT_RX_0;
242162306a36Sopenharmony_ci	char name[8];
242262306a36Sopenharmony_ci	int i;
242362306a36Sopenharmony_ci	int retval;
242462306a36Sopenharmony_ci
242562306a36Sopenharmony_ci	/* one TX/RX queue pair for netdev is mandatory */
242662306a36Sopenharmony_ci	if (platform_irq_count(adapter->pdev) == 1)
242762306a36Sopenharmony_ci		retval = platform_get_irq(adapter->pdev, 0);
242862306a36Sopenharmony_ci	else
242962306a36Sopenharmony_ci		retval = platform_get_irq_byname(adapter->pdev, "mac");
243062306a36Sopenharmony_ci	if (retval < 0)
243162306a36Sopenharmony_ci		return retval;
243262306a36Sopenharmony_ci	adapter->num_tx_queues = 1;
243362306a36Sopenharmony_ci	adapter->num_rx_queues = 1;
243462306a36Sopenharmony_ci	adapter->num_queues = 1;
243562306a36Sopenharmony_ci	adapter->queue[0].adapter = adapter;
243662306a36Sopenharmony_ci	adapter->queue[0].irq = retval;
243762306a36Sopenharmony_ci	adapter->queue[0].tx = &adapter->tx[0];
243862306a36Sopenharmony_ci	adapter->queue[0].tx->adapter = adapter;
243962306a36Sopenharmony_ci	adapter->queue[0].tx->addr = adapter->addr + TSNEP_QUEUE(0);
244062306a36Sopenharmony_ci	adapter->queue[0].tx->queue_index = 0;
244162306a36Sopenharmony_ci	adapter->queue[0].rx = &adapter->rx[0];
244262306a36Sopenharmony_ci	adapter->queue[0].rx->adapter = adapter;
244362306a36Sopenharmony_ci	adapter->queue[0].rx->addr = adapter->addr + TSNEP_QUEUE(0);
244462306a36Sopenharmony_ci	adapter->queue[0].rx->queue_index = 0;
244562306a36Sopenharmony_ci	adapter->queue[0].irq_mask = irq_mask;
244662306a36Sopenharmony_ci	adapter->queue[0].irq_delay_addr = adapter->addr + ECM_INT_DELAY;
244762306a36Sopenharmony_ci	retval = tsnep_set_irq_coalesce(&adapter->queue[0],
244862306a36Sopenharmony_ci					TSNEP_COALESCE_USECS_DEFAULT);
244962306a36Sopenharmony_ci	if (retval < 0)
245062306a36Sopenharmony_ci		return retval;
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci	adapter->netdev->irq = adapter->queue[0].irq;
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci	/* add additional TX/RX queue pairs only if dedicated interrupt is
245562306a36Sopenharmony_ci	 * available
245662306a36Sopenharmony_ci	 */
245762306a36Sopenharmony_ci	for (i = 1; i < queue_count; i++) {
245862306a36Sopenharmony_ci		sprintf(name, "txrx-%d", i);
245962306a36Sopenharmony_ci		retval = platform_get_irq_byname_optional(adapter->pdev, name);
246062306a36Sopenharmony_ci		if (retval < 0)
246162306a36Sopenharmony_ci			break;
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_ci		adapter->num_tx_queues++;
246462306a36Sopenharmony_ci		adapter->num_rx_queues++;
246562306a36Sopenharmony_ci		adapter->num_queues++;
246662306a36Sopenharmony_ci		adapter->queue[i].adapter = adapter;
246762306a36Sopenharmony_ci		adapter->queue[i].irq = retval;
246862306a36Sopenharmony_ci		adapter->queue[i].tx = &adapter->tx[i];
246962306a36Sopenharmony_ci		adapter->queue[i].tx->adapter = adapter;
247062306a36Sopenharmony_ci		adapter->queue[i].tx->addr = adapter->addr + TSNEP_QUEUE(i);
247162306a36Sopenharmony_ci		adapter->queue[i].tx->queue_index = i;
247262306a36Sopenharmony_ci		adapter->queue[i].rx = &adapter->rx[i];
247362306a36Sopenharmony_ci		adapter->queue[i].rx->adapter = adapter;
247462306a36Sopenharmony_ci		adapter->queue[i].rx->addr = adapter->addr + TSNEP_QUEUE(i);
247562306a36Sopenharmony_ci		adapter->queue[i].rx->queue_index = i;
247662306a36Sopenharmony_ci		adapter->queue[i].irq_mask =
247762306a36Sopenharmony_ci			irq_mask << (ECM_INT_TXRX_SHIFT * i);
247862306a36Sopenharmony_ci		adapter->queue[i].irq_delay_addr =
247962306a36Sopenharmony_ci			adapter->addr + ECM_INT_DELAY + ECM_INT_DELAY_OFFSET * i;
248062306a36Sopenharmony_ci		retval = tsnep_set_irq_coalesce(&adapter->queue[i],
248162306a36Sopenharmony_ci						TSNEP_COALESCE_USECS_DEFAULT);
248262306a36Sopenharmony_ci		if (retval < 0)
248362306a36Sopenharmony_ci			return retval;
248462306a36Sopenharmony_ci	}
248562306a36Sopenharmony_ci
248662306a36Sopenharmony_ci	return 0;
248762306a36Sopenharmony_ci}
248862306a36Sopenharmony_ci
248962306a36Sopenharmony_cistatic int tsnep_probe(struct platform_device *pdev)
249062306a36Sopenharmony_ci{
249162306a36Sopenharmony_ci	struct tsnep_adapter *adapter;
249262306a36Sopenharmony_ci	struct net_device *netdev;
249362306a36Sopenharmony_ci	struct resource *io;
249462306a36Sopenharmony_ci	u32 type;
249562306a36Sopenharmony_ci	int revision;
249662306a36Sopenharmony_ci	int version;
249762306a36Sopenharmony_ci	int queue_count;
249862306a36Sopenharmony_ci	int retval;
249962306a36Sopenharmony_ci
250062306a36Sopenharmony_ci	netdev = devm_alloc_etherdev_mqs(&pdev->dev,
250162306a36Sopenharmony_ci					 sizeof(struct tsnep_adapter),
250262306a36Sopenharmony_ci					 TSNEP_MAX_QUEUES, TSNEP_MAX_QUEUES);
250362306a36Sopenharmony_ci	if (!netdev)
250462306a36Sopenharmony_ci		return -ENODEV;
250562306a36Sopenharmony_ci	SET_NETDEV_DEV(netdev, &pdev->dev);
250662306a36Sopenharmony_ci	adapter = netdev_priv(netdev);
250762306a36Sopenharmony_ci	platform_set_drvdata(pdev, adapter);
250862306a36Sopenharmony_ci	adapter->pdev = pdev;
250962306a36Sopenharmony_ci	adapter->dmadev = &pdev->dev;
251062306a36Sopenharmony_ci	adapter->netdev = netdev;
251162306a36Sopenharmony_ci	adapter->msg_enable = NETIF_MSG_DRV | NETIF_MSG_PROBE |
251262306a36Sopenharmony_ci			      NETIF_MSG_LINK | NETIF_MSG_IFUP |
251362306a36Sopenharmony_ci			      NETIF_MSG_IFDOWN | NETIF_MSG_TX_QUEUED;
251462306a36Sopenharmony_ci
251562306a36Sopenharmony_ci	netdev->min_mtu = ETH_MIN_MTU;
251662306a36Sopenharmony_ci	netdev->max_mtu = TSNEP_MAX_FRAME_SIZE;
251762306a36Sopenharmony_ci
251862306a36Sopenharmony_ci	mutex_init(&adapter->gate_control_lock);
251962306a36Sopenharmony_ci	mutex_init(&adapter->rxnfc_lock);
252062306a36Sopenharmony_ci	INIT_LIST_HEAD(&adapter->rxnfc_rules);
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_ci	io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
252362306a36Sopenharmony_ci	adapter->addr = devm_ioremap_resource(&pdev->dev, io);
252462306a36Sopenharmony_ci	if (IS_ERR(adapter->addr))
252562306a36Sopenharmony_ci		return PTR_ERR(adapter->addr);
252662306a36Sopenharmony_ci	netdev->mem_start = io->start;
252762306a36Sopenharmony_ci	netdev->mem_end = io->end;
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_ci	type = ioread32(adapter->addr + ECM_TYPE);
253062306a36Sopenharmony_ci	revision = (type & ECM_REVISION_MASK) >> ECM_REVISION_SHIFT;
253162306a36Sopenharmony_ci	version = (type & ECM_VERSION_MASK) >> ECM_VERSION_SHIFT;
253262306a36Sopenharmony_ci	queue_count = (type & ECM_QUEUE_COUNT_MASK) >> ECM_QUEUE_COUNT_SHIFT;
253362306a36Sopenharmony_ci	adapter->gate_control = type & ECM_GATE_CONTROL;
253462306a36Sopenharmony_ci	adapter->rxnfc_max = TSNEP_RX_ASSIGN_ETHER_TYPE_COUNT;
253562306a36Sopenharmony_ci
253662306a36Sopenharmony_ci	tsnep_disable_irq(adapter, ECM_INT_ALL);
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci	retval = tsnep_queue_init(adapter, queue_count);
253962306a36Sopenharmony_ci	if (retval)
254062306a36Sopenharmony_ci		return retval;
254162306a36Sopenharmony_ci
254262306a36Sopenharmony_ci	retval = dma_set_mask_and_coherent(&adapter->pdev->dev,
254362306a36Sopenharmony_ci					   DMA_BIT_MASK(64));
254462306a36Sopenharmony_ci	if (retval) {
254562306a36Sopenharmony_ci		dev_err(&adapter->pdev->dev, "no usable DMA configuration.\n");
254662306a36Sopenharmony_ci		return retval;
254762306a36Sopenharmony_ci	}
254862306a36Sopenharmony_ci
254962306a36Sopenharmony_ci	retval = tsnep_mac_init(adapter);
255062306a36Sopenharmony_ci	if (retval)
255162306a36Sopenharmony_ci		return retval;
255262306a36Sopenharmony_ci
255362306a36Sopenharmony_ci	retval = tsnep_mdio_init(adapter);
255462306a36Sopenharmony_ci	if (retval)
255562306a36Sopenharmony_ci		goto mdio_init_failed;
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_ci	retval = tsnep_phy_init(adapter);
255862306a36Sopenharmony_ci	if (retval)
255962306a36Sopenharmony_ci		goto phy_init_failed;
256062306a36Sopenharmony_ci
256162306a36Sopenharmony_ci	retval = tsnep_ptp_init(adapter);
256262306a36Sopenharmony_ci	if (retval)
256362306a36Sopenharmony_ci		goto ptp_init_failed;
256462306a36Sopenharmony_ci
256562306a36Sopenharmony_ci	retval = tsnep_tc_init(adapter);
256662306a36Sopenharmony_ci	if (retval)
256762306a36Sopenharmony_ci		goto tc_init_failed;
256862306a36Sopenharmony_ci
256962306a36Sopenharmony_ci	retval = tsnep_rxnfc_init(adapter);
257062306a36Sopenharmony_ci	if (retval)
257162306a36Sopenharmony_ci		goto rxnfc_init_failed;
257262306a36Sopenharmony_ci
257362306a36Sopenharmony_ci	netdev->netdev_ops = &tsnep_netdev_ops;
257462306a36Sopenharmony_ci	netdev->ethtool_ops = &tsnep_ethtool_ops;
257562306a36Sopenharmony_ci	netdev->features = NETIF_F_SG;
257662306a36Sopenharmony_ci	netdev->hw_features = netdev->features | NETIF_F_LOOPBACK;
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_ci	netdev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT |
257962306a36Sopenharmony_ci			       NETDEV_XDP_ACT_NDO_XMIT |
258062306a36Sopenharmony_ci			       NETDEV_XDP_ACT_NDO_XMIT_SG |
258162306a36Sopenharmony_ci			       NETDEV_XDP_ACT_XSK_ZEROCOPY;
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci	/* carrier off reporting is important to ethtool even BEFORE open */
258462306a36Sopenharmony_ci	netif_carrier_off(netdev);
258562306a36Sopenharmony_ci
258662306a36Sopenharmony_ci	retval = register_netdev(netdev);
258762306a36Sopenharmony_ci	if (retval)
258862306a36Sopenharmony_ci		goto register_failed;
258962306a36Sopenharmony_ci
259062306a36Sopenharmony_ci	dev_info(&adapter->pdev->dev, "device version %d.%02d\n", version,
259162306a36Sopenharmony_ci		 revision);
259262306a36Sopenharmony_ci	if (adapter->gate_control)
259362306a36Sopenharmony_ci		dev_info(&adapter->pdev->dev, "gate control detected\n");
259462306a36Sopenharmony_ci
259562306a36Sopenharmony_ci	return 0;
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_ciregister_failed:
259862306a36Sopenharmony_ci	tsnep_rxnfc_cleanup(adapter);
259962306a36Sopenharmony_cirxnfc_init_failed:
260062306a36Sopenharmony_ci	tsnep_tc_cleanup(adapter);
260162306a36Sopenharmony_citc_init_failed:
260262306a36Sopenharmony_ci	tsnep_ptp_cleanup(adapter);
260362306a36Sopenharmony_ciptp_init_failed:
260462306a36Sopenharmony_ciphy_init_failed:
260562306a36Sopenharmony_ci	if (adapter->mdiobus)
260662306a36Sopenharmony_ci		mdiobus_unregister(adapter->mdiobus);
260762306a36Sopenharmony_cimdio_init_failed:
260862306a36Sopenharmony_ci	return retval;
260962306a36Sopenharmony_ci}
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_cistatic int tsnep_remove(struct platform_device *pdev)
261262306a36Sopenharmony_ci{
261362306a36Sopenharmony_ci	struct tsnep_adapter *adapter = platform_get_drvdata(pdev);
261462306a36Sopenharmony_ci
261562306a36Sopenharmony_ci	unregister_netdev(adapter->netdev);
261662306a36Sopenharmony_ci
261762306a36Sopenharmony_ci	tsnep_rxnfc_cleanup(adapter);
261862306a36Sopenharmony_ci
261962306a36Sopenharmony_ci	tsnep_tc_cleanup(adapter);
262062306a36Sopenharmony_ci
262162306a36Sopenharmony_ci	tsnep_ptp_cleanup(adapter);
262262306a36Sopenharmony_ci
262362306a36Sopenharmony_ci	if (adapter->mdiobus)
262462306a36Sopenharmony_ci		mdiobus_unregister(adapter->mdiobus);
262562306a36Sopenharmony_ci
262662306a36Sopenharmony_ci	tsnep_disable_irq(adapter, ECM_INT_ALL);
262762306a36Sopenharmony_ci
262862306a36Sopenharmony_ci	return 0;
262962306a36Sopenharmony_ci}
263062306a36Sopenharmony_ci
263162306a36Sopenharmony_cistatic const struct of_device_id tsnep_of_match[] = {
263262306a36Sopenharmony_ci	{ .compatible = "engleder,tsnep", },
263362306a36Sopenharmony_ci{ },
263462306a36Sopenharmony_ci};
263562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tsnep_of_match);
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_cistatic struct platform_driver tsnep_driver = {
263862306a36Sopenharmony_ci	.driver = {
263962306a36Sopenharmony_ci		.name = TSNEP,
264062306a36Sopenharmony_ci		.of_match_table = tsnep_of_match,
264162306a36Sopenharmony_ci	},
264262306a36Sopenharmony_ci	.probe = tsnep_probe,
264362306a36Sopenharmony_ci	.remove = tsnep_remove,
264462306a36Sopenharmony_ci};
264562306a36Sopenharmony_cimodule_platform_driver(tsnep_driver);
264662306a36Sopenharmony_ci
264762306a36Sopenharmony_ciMODULE_AUTHOR("Gerhard Engleder <gerhard@engleder-embedded.com>");
264862306a36Sopenharmony_ciMODULE_DESCRIPTION("TSN endpoint Ethernet MAC driver");
264962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2650