1403f69bbSGerhard Engleder // SPDX-License-Identifier: GPL-2.0
2403f69bbSGerhard Engleder /* Copyright (C) 2021 Gerhard Engleder <gerhard@engleder-embedded.com> */
3403f69bbSGerhard Engleder
4403f69bbSGerhard Engleder /* TSN endpoint Ethernet MAC driver
5403f69bbSGerhard Engleder *
6403f69bbSGerhard Engleder * The TSN endpoint Ethernet MAC is a FPGA based network device for real-time
7403f69bbSGerhard Engleder * communication. It is designed for endpoints within TSN (Time Sensitive
8403f69bbSGerhard Engleder * Networking) networks; e.g., for PLCs in the industrial automation case.
9403f69bbSGerhard Engleder *
10403f69bbSGerhard Engleder * It supports multiple TX/RX queue pairs. The first TX/RX queue pair is used
11403f69bbSGerhard Engleder * by the driver.
12403f69bbSGerhard Engleder *
13403f69bbSGerhard Engleder * More information can be found here:
14403f69bbSGerhard Engleder * - www.embedded-experts.at/tsn
15403f69bbSGerhard Engleder * - www.engleder-embedded.com
16403f69bbSGerhard Engleder */
17403f69bbSGerhard Engleder
18403f69bbSGerhard Engleder #include "tsnep.h"
19403f69bbSGerhard Engleder #include "tsnep_hw.h"
20403f69bbSGerhard Engleder
21403f69bbSGerhard Engleder #include <linux/module.h>
22403f69bbSGerhard Engleder #include <linux/of.h>
23403f69bbSGerhard Engleder #include <linux/of_net.h>
24403f69bbSGerhard Engleder #include <linux/of_mdio.h>
25403f69bbSGerhard Engleder #include <linux/interrupt.h>
26403f69bbSGerhard Engleder #include <linux/etherdevice.h>
27403f69bbSGerhard Engleder #include <linux/phy.h>
28403f69bbSGerhard Engleder #include <linux/iopoll.h>
29cc3e254fSGerhard Engleder #include <linux/bpf.h>
3065b28c81SGerhard Engleder #include <linux/bpf_trace.h>
31a9ca9f9cSYunsheng Lin #include <net/page_pool/helpers.h>
323fc23339SGerhard Engleder #include <net/xdp_sock_drv.h>
33403f69bbSGerhard Engleder
34cc3e254fSGerhard Engleder #define TSNEP_RX_OFFSET (max(NET_SKB_PAD, XDP_PACKET_HEADROOM) + NET_IP_ALIGN)
35cc3e254fSGerhard Engleder #define TSNEP_HEADROOM ALIGN(TSNEP_RX_OFFSET, 4)
36bb837a37SGerhard Engleder #define TSNEP_MAX_RX_BUF_SIZE (PAGE_SIZE - TSNEP_HEADROOM - \
37bb837a37SGerhard Engleder SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
383fc23339SGerhard Engleder /* XSK buffer shall store at least Q-in-Q frame */
393fc23339SGerhard Engleder #define TSNEP_XSK_RX_BUF_SIZE (ALIGN(TSNEP_RX_INLINE_METADATA_SIZE + \
403fc23339SGerhard Engleder ETH_FRAME_LEN + ETH_FCS_LEN + \
413fc23339SGerhard Engleder VLAN_HLEN * 2, 4))
42403f69bbSGerhard Engleder
43403f69bbSGerhard Engleder #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
44403f69bbSGerhard Engleder #define DMA_ADDR_HIGH(dma_addr) ((u32)(((dma_addr) >> 32) & 0xFFFFFFFF))
45403f69bbSGerhard Engleder #else
46403f69bbSGerhard Engleder #define DMA_ADDR_HIGH(dma_addr) ((u32)(0))
47403f69bbSGerhard Engleder #endif
48403f69bbSGerhard Engleder #define DMA_ADDR_LOW(dma_addr) ((u32)((dma_addr) & 0xFFFFFFFF))
49403f69bbSGerhard Engleder
50d3dfe8d6SGerhard Engleder #define TSNEP_COALESCE_USECS_DEFAULT 64
51d3dfe8d6SGerhard Engleder #define TSNEP_COALESCE_USECS_MAX ((ECM_INT_DELAY_MASK >> ECM_INT_DELAY_SHIFT) * \
52d3dfe8d6SGerhard Engleder ECM_INT_DELAY_BASE_US + ECM_INT_DELAY_BASE_US - 1)
53d3dfe8d6SGerhard Engleder
54d24bc0bcSGerhard Engleder #define TSNEP_TX_TYPE_SKB BIT(0)
55d24bc0bcSGerhard Engleder #define TSNEP_TX_TYPE_SKB_FRAG BIT(1)
56d24bc0bcSGerhard Engleder #define TSNEP_TX_TYPE_XDP_TX BIT(2)
57d24bc0bcSGerhard Engleder #define TSNEP_TX_TYPE_XDP_NDO BIT(3)
58cd275c23SGerhard Engleder #define TSNEP_TX_TYPE_XDP (TSNEP_TX_TYPE_XDP_TX | TSNEP_TX_TYPE_XDP_NDO)
59cd275c23SGerhard Engleder #define TSNEP_TX_TYPE_XSK BIT(4)
60d24bc0bcSGerhard Engleder
6165b28c81SGerhard Engleder #define TSNEP_XDP_TX BIT(0)
6265b28c81SGerhard Engleder #define TSNEP_XDP_REDIRECT BIT(1)
6365b28c81SGerhard Engleder
tsnep_enable_irq(struct tsnep_adapter * adapter,u32 mask)64403f69bbSGerhard Engleder static void tsnep_enable_irq(struct tsnep_adapter *adapter, u32 mask)
65403f69bbSGerhard Engleder {
66403f69bbSGerhard Engleder iowrite32(mask, adapter->addr + ECM_INT_ENABLE);
67403f69bbSGerhard Engleder }
68403f69bbSGerhard Engleder
tsnep_disable_irq(struct tsnep_adapter * adapter,u32 mask)69403f69bbSGerhard Engleder static void tsnep_disable_irq(struct tsnep_adapter *adapter, u32 mask)
70403f69bbSGerhard Engleder {
71403f69bbSGerhard Engleder mask |= ECM_INT_DISABLE;
72403f69bbSGerhard Engleder iowrite32(mask, adapter->addr + ECM_INT_ENABLE);
73403f69bbSGerhard Engleder }
74403f69bbSGerhard Engleder
tsnep_irq(int irq,void * arg)75403f69bbSGerhard Engleder static irqreturn_t tsnep_irq(int irq, void *arg)
76403f69bbSGerhard Engleder {
77403f69bbSGerhard Engleder struct tsnep_adapter *adapter = arg;
78403f69bbSGerhard Engleder u32 active = ioread32(adapter->addr + ECM_INT_ACTIVE);
79403f69bbSGerhard Engleder
80403f69bbSGerhard Engleder /* acknowledge interrupt */
81403f69bbSGerhard Engleder if (active != 0)
82403f69bbSGerhard Engleder iowrite32(active, adapter->addr + ECM_INT_ACKNOWLEDGE);
83403f69bbSGerhard Engleder
84403f69bbSGerhard Engleder /* handle link interrupt */
8558eaa8abSGerhard Engleder if ((active & ECM_INT_LINK) != 0)
86403f69bbSGerhard Engleder phy_mac_interrupt(adapter->netdev->phydev);
87403f69bbSGerhard Engleder
88403f69bbSGerhard Engleder /* handle TX/RX queue 0 interrupt */
89403f69bbSGerhard Engleder if ((active & adapter->queue[0].irq_mask) != 0) {
90ea852c17SGerhard Engleder if (napi_schedule_prep(&adapter->queue[0].napi)) {
91403f69bbSGerhard Engleder tsnep_disable_irq(adapter, adapter->queue[0].irq_mask);
92ea852c17SGerhard Engleder /* schedule after masking to avoid races */
93ea852c17SGerhard Engleder __napi_schedule(&adapter->queue[0].napi);
94ea852c17SGerhard Engleder }
95403f69bbSGerhard Engleder }
9658eaa8abSGerhard Engleder
9758eaa8abSGerhard Engleder return IRQ_HANDLED;
98403f69bbSGerhard Engleder }
99403f69bbSGerhard Engleder
tsnep_irq_txrx(int irq,void * arg)10058eaa8abSGerhard Engleder static irqreturn_t tsnep_irq_txrx(int irq, void *arg)
10158eaa8abSGerhard Engleder {
10258eaa8abSGerhard Engleder struct tsnep_queue *queue = arg;
10358eaa8abSGerhard Engleder
10458eaa8abSGerhard Engleder /* handle TX/RX queue interrupt */
105ea852c17SGerhard Engleder if (napi_schedule_prep(&queue->napi)) {
10658eaa8abSGerhard Engleder tsnep_disable_irq(queue->adapter, queue->irq_mask);
107ea852c17SGerhard Engleder /* schedule after masking to avoid races */
108ea852c17SGerhard Engleder __napi_schedule(&queue->napi);
109ea852c17SGerhard Engleder }
11058eaa8abSGerhard Engleder
111403f69bbSGerhard Engleder return IRQ_HANDLED;
112403f69bbSGerhard Engleder }
113403f69bbSGerhard Engleder
tsnep_set_irq_coalesce(struct tsnep_queue * queue,u32 usecs)114d3dfe8d6SGerhard Engleder int tsnep_set_irq_coalesce(struct tsnep_queue *queue, u32 usecs)
115d3dfe8d6SGerhard Engleder {
116d3dfe8d6SGerhard Engleder if (usecs > TSNEP_COALESCE_USECS_MAX)
117d3dfe8d6SGerhard Engleder return -ERANGE;
118d3dfe8d6SGerhard Engleder
119d3dfe8d6SGerhard Engleder usecs /= ECM_INT_DELAY_BASE_US;
120d3dfe8d6SGerhard Engleder usecs <<= ECM_INT_DELAY_SHIFT;
121d3dfe8d6SGerhard Engleder usecs &= ECM_INT_DELAY_MASK;
122d3dfe8d6SGerhard Engleder
123d3dfe8d6SGerhard Engleder queue->irq_delay &= ~ECM_INT_DELAY_MASK;
124d3dfe8d6SGerhard Engleder queue->irq_delay |= usecs;
125d3dfe8d6SGerhard Engleder iowrite8(queue->irq_delay, queue->irq_delay_addr);
126d3dfe8d6SGerhard Engleder
127d3dfe8d6SGerhard Engleder return 0;
128d3dfe8d6SGerhard Engleder }
129d3dfe8d6SGerhard Engleder
tsnep_get_irq_coalesce(struct tsnep_queue * queue)130d3dfe8d6SGerhard Engleder u32 tsnep_get_irq_coalesce(struct tsnep_queue *queue)
131d3dfe8d6SGerhard Engleder {
132d3dfe8d6SGerhard Engleder u32 usecs;
133d3dfe8d6SGerhard Engleder
134d3dfe8d6SGerhard Engleder usecs = (queue->irq_delay & ECM_INT_DELAY_MASK);
135d3dfe8d6SGerhard Engleder usecs >>= ECM_INT_DELAY_SHIFT;
136d3dfe8d6SGerhard Engleder usecs *= ECM_INT_DELAY_BASE_US;
137d3dfe8d6SGerhard Engleder
138d3dfe8d6SGerhard Engleder return usecs;
139d3dfe8d6SGerhard Engleder }
140d3dfe8d6SGerhard Engleder
tsnep_mdiobus_read(struct mii_bus * bus,int addr,int regnum)141403f69bbSGerhard Engleder static int tsnep_mdiobus_read(struct mii_bus *bus, int addr, int regnum)
142403f69bbSGerhard Engleder {
143403f69bbSGerhard Engleder struct tsnep_adapter *adapter = bus->priv;
144403f69bbSGerhard Engleder u32 md;
145403f69bbSGerhard Engleder int retval;
146403f69bbSGerhard Engleder
147403f69bbSGerhard Engleder md = ECM_MD_READ;
148403f69bbSGerhard Engleder if (!adapter->suppress_preamble)
149403f69bbSGerhard Engleder md |= ECM_MD_PREAMBLE;
150403f69bbSGerhard Engleder md |= (regnum << ECM_MD_ADDR_SHIFT) & ECM_MD_ADDR_MASK;
151403f69bbSGerhard Engleder md |= (addr << ECM_MD_PHY_ADDR_SHIFT) & ECM_MD_PHY_ADDR_MASK;
152403f69bbSGerhard Engleder iowrite32(md, adapter->addr + ECM_MD_CONTROL);
153403f69bbSGerhard Engleder retval = readl_poll_timeout_atomic(adapter->addr + ECM_MD_STATUS, md,
154403f69bbSGerhard Engleder !(md & ECM_MD_BUSY), 16, 1000);
155403f69bbSGerhard Engleder if (retval != 0)
156403f69bbSGerhard Engleder return retval;
157403f69bbSGerhard Engleder
158403f69bbSGerhard Engleder return (md & ECM_MD_DATA_MASK) >> ECM_MD_DATA_SHIFT;
159403f69bbSGerhard Engleder }
160403f69bbSGerhard Engleder
tsnep_mdiobus_write(struct mii_bus * bus,int addr,int regnum,u16 val)161403f69bbSGerhard Engleder static int tsnep_mdiobus_write(struct mii_bus *bus, int addr, int regnum,
162403f69bbSGerhard Engleder u16 val)
163403f69bbSGerhard Engleder {
164403f69bbSGerhard Engleder struct tsnep_adapter *adapter = bus->priv;
165403f69bbSGerhard Engleder u32 md;
166403f69bbSGerhard Engleder int retval;
167403f69bbSGerhard Engleder
168403f69bbSGerhard Engleder md = ECM_MD_WRITE;
169403f69bbSGerhard Engleder if (!adapter->suppress_preamble)
170403f69bbSGerhard Engleder md |= ECM_MD_PREAMBLE;
171403f69bbSGerhard Engleder md |= (regnum << ECM_MD_ADDR_SHIFT) & ECM_MD_ADDR_MASK;
172403f69bbSGerhard Engleder md |= (addr << ECM_MD_PHY_ADDR_SHIFT) & ECM_MD_PHY_ADDR_MASK;
173403f69bbSGerhard Engleder md |= ((u32)val << ECM_MD_DATA_SHIFT) & ECM_MD_DATA_MASK;
174403f69bbSGerhard Engleder iowrite32(md, adapter->addr + ECM_MD_CONTROL);
175403f69bbSGerhard Engleder retval = readl_poll_timeout_atomic(adapter->addr + ECM_MD_STATUS, md,
176403f69bbSGerhard Engleder !(md & ECM_MD_BUSY), 16, 1000);
177403f69bbSGerhard Engleder if (retval != 0)
178403f69bbSGerhard Engleder return retval;
179403f69bbSGerhard Engleder
180403f69bbSGerhard Engleder return 0;
181403f69bbSGerhard Engleder }
182403f69bbSGerhard Engleder
tsnep_set_link_mode(struct tsnep_adapter * adapter)1834b222008SGerhard Engleder static void tsnep_set_link_mode(struct tsnep_adapter *adapter)
184403f69bbSGerhard Engleder {
185403f69bbSGerhard Engleder u32 mode;
186403f69bbSGerhard Engleder
1874b222008SGerhard Engleder switch (adapter->phydev->speed) {
188403f69bbSGerhard Engleder case SPEED_100:
189403f69bbSGerhard Engleder mode = ECM_LINK_MODE_100;
190403f69bbSGerhard Engleder break;
191403f69bbSGerhard Engleder case SPEED_1000:
192403f69bbSGerhard Engleder mode = ECM_LINK_MODE_1000;
193403f69bbSGerhard Engleder break;
194403f69bbSGerhard Engleder default:
195403f69bbSGerhard Engleder mode = ECM_LINK_MODE_OFF;
196403f69bbSGerhard Engleder break;
197403f69bbSGerhard Engleder }
198403f69bbSGerhard Engleder iowrite32(mode, adapter->addr + ECM_STATUS);
199403f69bbSGerhard Engleder }
200403f69bbSGerhard Engleder
tsnep_phy_link_status_change(struct net_device * netdev)2014b222008SGerhard Engleder static void tsnep_phy_link_status_change(struct net_device *netdev)
2024b222008SGerhard Engleder {
2034b222008SGerhard Engleder struct tsnep_adapter *adapter = netdev_priv(netdev);
2044b222008SGerhard Engleder struct phy_device *phydev = netdev->phydev;
2054b222008SGerhard Engleder
2064b222008SGerhard Engleder if (phydev->link)
2074b222008SGerhard Engleder tsnep_set_link_mode(adapter);
2084b222008SGerhard Engleder
209403f69bbSGerhard Engleder phy_print_status(netdev->phydev);
210403f69bbSGerhard Engleder }
211403f69bbSGerhard Engleder
tsnep_phy_loopback(struct tsnep_adapter * adapter,bool enable)2124b222008SGerhard Engleder static int tsnep_phy_loopback(struct tsnep_adapter *adapter, bool enable)
2134b222008SGerhard Engleder {
2144b222008SGerhard Engleder int retval;
2154b222008SGerhard Engleder
2164b222008SGerhard Engleder retval = phy_loopback(adapter->phydev, enable);
2174b222008SGerhard Engleder
2184b222008SGerhard Engleder /* PHY link state change is not signaled if loopback is enabled, it
2194b222008SGerhard Engleder * would delay a working loopback anyway, let's ensure that loopback
2204b222008SGerhard Engleder * is working immediately by setting link mode directly
2214b222008SGerhard Engleder */
2224b222008SGerhard Engleder if (!retval && enable)
2234b222008SGerhard Engleder tsnep_set_link_mode(adapter);
2244b222008SGerhard Engleder
2254b222008SGerhard Engleder return retval;
2264b222008SGerhard Engleder }
2274b222008SGerhard Engleder
tsnep_phy_open(struct tsnep_adapter * adapter)228403f69bbSGerhard Engleder static int tsnep_phy_open(struct tsnep_adapter *adapter)
229403f69bbSGerhard Engleder {
230403f69bbSGerhard Engleder struct phy_device *phydev;
231403f69bbSGerhard Engleder struct ethtool_eee ethtool_eee;
232403f69bbSGerhard Engleder int retval;
233403f69bbSGerhard Engleder
234403f69bbSGerhard Engleder retval = phy_connect_direct(adapter->netdev, adapter->phydev,
235403f69bbSGerhard Engleder tsnep_phy_link_status_change,
236403f69bbSGerhard Engleder adapter->phy_mode);
237403f69bbSGerhard Engleder if (retval)
238403f69bbSGerhard Engleder return retval;
239403f69bbSGerhard Engleder phydev = adapter->netdev->phydev;
240403f69bbSGerhard Engleder
241403f69bbSGerhard Engleder /* MAC supports only 100Mbps|1000Mbps full duplex
242403f69bbSGerhard Engleder * SPE (Single Pair Ethernet) is also an option but not implemented yet
243403f69bbSGerhard Engleder */
244403f69bbSGerhard Engleder phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_10baseT_Half_BIT);
245403f69bbSGerhard Engleder phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_10baseT_Full_BIT);
246403f69bbSGerhard Engleder phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_100baseT_Half_BIT);
247403f69bbSGerhard Engleder phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Half_BIT);
248403f69bbSGerhard Engleder
249403f69bbSGerhard Engleder /* disable EEE autoneg, EEE not supported by TSNEP */
250403f69bbSGerhard Engleder memset(ðtool_eee, 0, sizeof(ethtool_eee));
251403f69bbSGerhard Engleder phy_ethtool_set_eee(adapter->phydev, ðtool_eee);
252403f69bbSGerhard Engleder
253403f69bbSGerhard Engleder adapter->phydev->irq = PHY_MAC_INTERRUPT;
254403f69bbSGerhard Engleder phy_start(adapter->phydev);
255403f69bbSGerhard Engleder
256403f69bbSGerhard Engleder return 0;
257403f69bbSGerhard Engleder }
258403f69bbSGerhard Engleder
tsnep_phy_close(struct tsnep_adapter * adapter)259403f69bbSGerhard Engleder static void tsnep_phy_close(struct tsnep_adapter *adapter)
260403f69bbSGerhard Engleder {
261403f69bbSGerhard Engleder phy_stop(adapter->netdev->phydev);
262403f69bbSGerhard Engleder phy_disconnect(adapter->netdev->phydev);
263403f69bbSGerhard Engleder }
264403f69bbSGerhard Engleder
tsnep_tx_ring_cleanup(struct tsnep_tx * tx)265403f69bbSGerhard Engleder static void tsnep_tx_ring_cleanup(struct tsnep_tx *tx)
266403f69bbSGerhard Engleder {
267403f69bbSGerhard Engleder struct device *dmadev = tx->adapter->dmadev;
268403f69bbSGerhard Engleder int i;
269403f69bbSGerhard Engleder
270403f69bbSGerhard Engleder memset(tx->entry, 0, sizeof(tx->entry));
271403f69bbSGerhard Engleder
272403f69bbSGerhard Engleder for (i = 0; i < TSNEP_RING_PAGE_COUNT; i++) {
273403f69bbSGerhard Engleder if (tx->page[i]) {
274403f69bbSGerhard Engleder dma_free_coherent(dmadev, PAGE_SIZE, tx->page[i],
275403f69bbSGerhard Engleder tx->page_dma[i]);
276403f69bbSGerhard Engleder tx->page[i] = NULL;
277403f69bbSGerhard Engleder tx->page_dma[i] = 0;
278403f69bbSGerhard Engleder }
279403f69bbSGerhard Engleder }
280403f69bbSGerhard Engleder }
281403f69bbSGerhard Engleder
tsnep_tx_ring_create(struct tsnep_tx * tx)28233b0ee02SGerhard Engleder static int tsnep_tx_ring_create(struct tsnep_tx *tx)
283403f69bbSGerhard Engleder {
284403f69bbSGerhard Engleder struct device *dmadev = tx->adapter->dmadev;
285403f69bbSGerhard Engleder struct tsnep_tx_entry *entry;
286403f69bbSGerhard Engleder struct tsnep_tx_entry *next_entry;
287403f69bbSGerhard Engleder int i, j;
288403f69bbSGerhard Engleder int retval;
289403f69bbSGerhard Engleder
290403f69bbSGerhard Engleder for (i = 0; i < TSNEP_RING_PAGE_COUNT; i++) {
291403f69bbSGerhard Engleder tx->page[i] =
292403f69bbSGerhard Engleder dma_alloc_coherent(dmadev, PAGE_SIZE, &tx->page_dma[i],
293403f69bbSGerhard Engleder GFP_KERNEL);
294403f69bbSGerhard Engleder if (!tx->page[i]) {
295403f69bbSGerhard Engleder retval = -ENOMEM;
296403f69bbSGerhard Engleder goto alloc_failed;
297403f69bbSGerhard Engleder }
298403f69bbSGerhard Engleder for (j = 0; j < TSNEP_RING_ENTRIES_PER_PAGE; j++) {
299403f69bbSGerhard Engleder entry = &tx->entry[TSNEP_RING_ENTRIES_PER_PAGE * i + j];
300403f69bbSGerhard Engleder entry->desc_wb = (struct tsnep_tx_desc_wb *)
301403f69bbSGerhard Engleder (((u8 *)tx->page[i]) + TSNEP_DESC_SIZE * j);
302403f69bbSGerhard Engleder entry->desc = (struct tsnep_tx_desc *)
303403f69bbSGerhard Engleder (((u8 *)entry->desc_wb) + TSNEP_DESC_OFFSET);
304403f69bbSGerhard Engleder entry->desc_dma = tx->page_dma[i] + TSNEP_DESC_SIZE * j;
30533b0ee02SGerhard Engleder entry->owner_user_flag = false;
306403f69bbSGerhard Engleder }
307403f69bbSGerhard Engleder }
308403f69bbSGerhard Engleder for (i = 0; i < TSNEP_RING_SIZE; i++) {
309403f69bbSGerhard Engleder entry = &tx->entry[i];
31042fb2962SGerhard Engleder next_entry = &tx->entry[(i + 1) & TSNEP_RING_MASK];
311403f69bbSGerhard Engleder entry->desc->next = __cpu_to_le64(next_entry->desc_dma);
312403f69bbSGerhard Engleder }
313403f69bbSGerhard Engleder
314403f69bbSGerhard Engleder return 0;
315403f69bbSGerhard Engleder
316403f69bbSGerhard Engleder alloc_failed:
317403f69bbSGerhard Engleder tsnep_tx_ring_cleanup(tx);
318403f69bbSGerhard Engleder return retval;
319403f69bbSGerhard Engleder }
320403f69bbSGerhard Engleder
tsnep_tx_init(struct tsnep_tx * tx)32133b0ee02SGerhard Engleder static void tsnep_tx_init(struct tsnep_tx *tx)
32233b0ee02SGerhard Engleder {
32333b0ee02SGerhard Engleder dma_addr_t dma;
32433b0ee02SGerhard Engleder
32533b0ee02SGerhard Engleder dma = tx->entry[0].desc_dma | TSNEP_RESET_OWNER_COUNTER;
32633b0ee02SGerhard Engleder iowrite32(DMA_ADDR_LOW(dma), tx->addr + TSNEP_TX_DESC_ADDR_LOW);
32733b0ee02SGerhard Engleder iowrite32(DMA_ADDR_HIGH(dma), tx->addr + TSNEP_TX_DESC_ADDR_HIGH);
32833b0ee02SGerhard Engleder tx->write = 0;
32933b0ee02SGerhard Engleder tx->read = 0;
33033b0ee02SGerhard Engleder tx->owner_counter = 1;
33133b0ee02SGerhard Engleder tx->increment_owner_counter = TSNEP_RING_SIZE - 1;
33233b0ee02SGerhard Engleder }
33333b0ee02SGerhard Engleder
tsnep_tx_enable(struct tsnep_tx * tx)334cd275c23SGerhard Engleder static void tsnep_tx_enable(struct tsnep_tx *tx)
335cd275c23SGerhard Engleder {
336cd275c23SGerhard Engleder struct netdev_queue *nq;
337cd275c23SGerhard Engleder
338cd275c23SGerhard Engleder nq = netdev_get_tx_queue(tx->adapter->netdev, tx->queue_index);
339cd275c23SGerhard Engleder
340cd275c23SGerhard Engleder __netif_tx_lock_bh(nq);
341cd275c23SGerhard Engleder netif_tx_wake_queue(nq);
342cd275c23SGerhard Engleder __netif_tx_unlock_bh(nq);
343cd275c23SGerhard Engleder }
344cd275c23SGerhard Engleder
tsnep_tx_disable(struct tsnep_tx * tx,struct napi_struct * napi)345cd275c23SGerhard Engleder static void tsnep_tx_disable(struct tsnep_tx *tx, struct napi_struct *napi)
346cd275c23SGerhard Engleder {
347cd275c23SGerhard Engleder struct netdev_queue *nq;
348cd275c23SGerhard Engleder u32 val;
349cd275c23SGerhard Engleder
350cd275c23SGerhard Engleder nq = netdev_get_tx_queue(tx->adapter->netdev, tx->queue_index);
351cd275c23SGerhard Engleder
352cd275c23SGerhard Engleder __netif_tx_lock_bh(nq);
353cd275c23SGerhard Engleder netif_tx_stop_queue(nq);
354cd275c23SGerhard Engleder __netif_tx_unlock_bh(nq);
355cd275c23SGerhard Engleder
356cd275c23SGerhard Engleder /* wait until TX is done in hardware */
357cd275c23SGerhard Engleder readx_poll_timeout(ioread32, tx->addr + TSNEP_CONTROL, val,
358cd275c23SGerhard Engleder ((val & TSNEP_CONTROL_TX_ENABLE) == 0), 10000,
359cd275c23SGerhard Engleder 1000000);
360cd275c23SGerhard Engleder
361cd275c23SGerhard Engleder /* wait until TX is also done in software */
362cd275c23SGerhard Engleder while (READ_ONCE(tx->read) != tx->write) {
363cd275c23SGerhard Engleder napi_schedule(napi);
364cd275c23SGerhard Engleder napi_synchronize(napi);
365cd275c23SGerhard Engleder }
366cd275c23SGerhard Engleder }
367cd275c23SGerhard Engleder
tsnep_tx_activate(struct tsnep_tx * tx,int index,int length,bool last)368b99ac751SGerhard Engleder static void tsnep_tx_activate(struct tsnep_tx *tx, int index, int length,
369b99ac751SGerhard Engleder bool last)
370403f69bbSGerhard Engleder {
371403f69bbSGerhard Engleder struct tsnep_tx_entry *entry = &tx->entry[index];
372403f69bbSGerhard Engleder
373403f69bbSGerhard Engleder entry->properties = 0;
374cd275c23SGerhard Engleder /* xdpf and zc are union with skb */
375403f69bbSGerhard Engleder if (entry->skb) {
376b99ac751SGerhard Engleder entry->properties = length & TSNEP_DESC_LENGTH_MASK;
377403f69bbSGerhard Engleder entry->properties |= TSNEP_DESC_INTERRUPT_FLAG;
378d24bc0bcSGerhard Engleder if ((entry->type & TSNEP_TX_TYPE_SKB) &&
379d24bc0bcSGerhard Engleder (skb_shinfo(entry->skb)->tx_flags & SKBTX_IN_PROGRESS))
380403f69bbSGerhard Engleder entry->properties |= TSNEP_DESC_EXTENDED_WRITEBACK_FLAG;
381403f69bbSGerhard Engleder
382403f69bbSGerhard Engleder /* toggle user flag to prevent false acknowledge
383403f69bbSGerhard Engleder *
384403f69bbSGerhard Engleder * Only the first fragment is acknowledged. For all other
385403f69bbSGerhard Engleder * fragments no acknowledge is done and the last written owner
386403f69bbSGerhard Engleder * counter stays in the writeback descriptor. Therefore, it is
387403f69bbSGerhard Engleder * possible that the last written owner counter is identical to
388403f69bbSGerhard Engleder * the new incremented owner counter and a false acknowledge is
389403f69bbSGerhard Engleder * detected before the real acknowledge has been done by
390403f69bbSGerhard Engleder * hardware.
391403f69bbSGerhard Engleder *
392403f69bbSGerhard Engleder * The user flag is used to prevent this situation. The user
393403f69bbSGerhard Engleder * flag is copied to the writeback descriptor by the hardware
394403f69bbSGerhard Engleder * and is used as additional acknowledge data. By toggeling the
395403f69bbSGerhard Engleder * user flag only for the first fragment (which is
396403f69bbSGerhard Engleder * acknowledged), it is guaranteed that the last acknowledge
397403f69bbSGerhard Engleder * done for this descriptor has used a different user flag and
398403f69bbSGerhard Engleder * cannot be detected as false acknowledge.
399403f69bbSGerhard Engleder */
400403f69bbSGerhard Engleder entry->owner_user_flag = !entry->owner_user_flag;
401403f69bbSGerhard Engleder }
402403f69bbSGerhard Engleder if (last)
403403f69bbSGerhard Engleder entry->properties |= TSNEP_TX_DESC_LAST_FRAGMENT_FLAG;
404403f69bbSGerhard Engleder if (index == tx->increment_owner_counter) {
405403f69bbSGerhard Engleder tx->owner_counter++;
406403f69bbSGerhard Engleder if (tx->owner_counter == 4)
407403f69bbSGerhard Engleder tx->owner_counter = 1;
408403f69bbSGerhard Engleder tx->increment_owner_counter--;
409403f69bbSGerhard Engleder if (tx->increment_owner_counter < 0)
410403f69bbSGerhard Engleder tx->increment_owner_counter = TSNEP_RING_SIZE - 1;
411403f69bbSGerhard Engleder }
412403f69bbSGerhard Engleder entry->properties |=
413403f69bbSGerhard Engleder (tx->owner_counter << TSNEP_DESC_OWNER_COUNTER_SHIFT) &
414403f69bbSGerhard Engleder TSNEP_DESC_OWNER_COUNTER_MASK;
415403f69bbSGerhard Engleder if (entry->owner_user_flag)
416403f69bbSGerhard Engleder entry->properties |= TSNEP_TX_DESC_OWNER_USER_FLAG;
417403f69bbSGerhard Engleder entry->desc->more_properties =
418403f69bbSGerhard Engleder __cpu_to_le32(entry->len & TSNEP_DESC_LENGTH_MASK);
419403f69bbSGerhard Engleder
420403f69bbSGerhard Engleder /* descriptor properties shall be written last, because valid data is
421403f69bbSGerhard Engleder * signaled there
422403f69bbSGerhard Engleder */
423403f69bbSGerhard Engleder dma_wmb();
424403f69bbSGerhard Engleder
425403f69bbSGerhard Engleder entry->desc->properties = __cpu_to_le32(entry->properties);
426403f69bbSGerhard Engleder }
427403f69bbSGerhard Engleder
tsnep_tx_desc_available(struct tsnep_tx * tx)428403f69bbSGerhard Engleder static int tsnep_tx_desc_available(struct tsnep_tx *tx)
429403f69bbSGerhard Engleder {
430403f69bbSGerhard Engleder if (tx->read <= tx->write)
431403f69bbSGerhard Engleder return TSNEP_RING_SIZE - tx->write + tx->read - 1;
432403f69bbSGerhard Engleder else
433403f69bbSGerhard Engleder return tx->read - tx->write - 1;
434403f69bbSGerhard Engleder }
435403f69bbSGerhard Engleder
tsnep_tx_map(struct sk_buff * skb,struct tsnep_tx * tx,int count)436403f69bbSGerhard Engleder static int tsnep_tx_map(struct sk_buff *skb, struct tsnep_tx *tx, int count)
437403f69bbSGerhard Engleder {
438403f69bbSGerhard Engleder struct device *dmadev = tx->adapter->dmadev;
439403f69bbSGerhard Engleder struct tsnep_tx_entry *entry;
440403f69bbSGerhard Engleder unsigned int len;
441403f69bbSGerhard Engleder dma_addr_t dma;
442b99ac751SGerhard Engleder int map_len = 0;
443403f69bbSGerhard Engleder int i;
444403f69bbSGerhard Engleder
445403f69bbSGerhard Engleder for (i = 0; i < count; i++) {
44642fb2962SGerhard Engleder entry = &tx->entry[(tx->write + i) & TSNEP_RING_MASK];
447403f69bbSGerhard Engleder
448d24bc0bcSGerhard Engleder if (!i) {
449403f69bbSGerhard Engleder len = skb_headlen(skb);
450403f69bbSGerhard Engleder dma = dma_map_single(dmadev, skb->data, len,
451403f69bbSGerhard Engleder DMA_TO_DEVICE);
452d24bc0bcSGerhard Engleder
453d24bc0bcSGerhard Engleder entry->type = TSNEP_TX_TYPE_SKB;
454403f69bbSGerhard Engleder } else {
455403f69bbSGerhard Engleder len = skb_frag_size(&skb_shinfo(skb)->frags[i - 1]);
456403f69bbSGerhard Engleder dma = skb_frag_dma_map(dmadev,
457403f69bbSGerhard Engleder &skb_shinfo(skb)->frags[i - 1],
458403f69bbSGerhard Engleder 0, len, DMA_TO_DEVICE);
459d24bc0bcSGerhard Engleder
460d24bc0bcSGerhard Engleder entry->type = TSNEP_TX_TYPE_SKB_FRAG;
461403f69bbSGerhard Engleder }
462403f69bbSGerhard Engleder if (dma_mapping_error(dmadev, dma))
463403f69bbSGerhard Engleder return -ENOMEM;
464403f69bbSGerhard Engleder
465403f69bbSGerhard Engleder entry->len = len;
466403f69bbSGerhard Engleder dma_unmap_addr_set(entry, dma, dma);
467403f69bbSGerhard Engleder
468403f69bbSGerhard Engleder entry->desc->tx = __cpu_to_le64(dma);
469b99ac751SGerhard Engleder
470b99ac751SGerhard Engleder map_len += len;
471403f69bbSGerhard Engleder }
472403f69bbSGerhard Engleder
473b99ac751SGerhard Engleder return map_len;
474403f69bbSGerhard Engleder }
475403f69bbSGerhard Engleder
tsnep_tx_unmap(struct tsnep_tx * tx,int index,int count)476b99ac751SGerhard Engleder static int tsnep_tx_unmap(struct tsnep_tx *tx, int index, int count)
477403f69bbSGerhard Engleder {
478403f69bbSGerhard Engleder struct device *dmadev = tx->adapter->dmadev;
479403f69bbSGerhard Engleder struct tsnep_tx_entry *entry;
480b99ac751SGerhard Engleder int map_len = 0;
481403f69bbSGerhard Engleder int i;
482403f69bbSGerhard Engleder
483403f69bbSGerhard Engleder for (i = 0; i < count; i++) {
48442fb2962SGerhard Engleder entry = &tx->entry[(index + i) & TSNEP_RING_MASK];
485403f69bbSGerhard Engleder
486403f69bbSGerhard Engleder if (entry->len) {
487d24bc0bcSGerhard Engleder if (entry->type & TSNEP_TX_TYPE_SKB)
488403f69bbSGerhard Engleder dma_unmap_single(dmadev,
489403f69bbSGerhard Engleder dma_unmap_addr(entry, dma),
490403f69bbSGerhard Engleder dma_unmap_len(entry, len),
491403f69bbSGerhard Engleder DMA_TO_DEVICE);
492d24bc0bcSGerhard Engleder else if (entry->type &
493d24bc0bcSGerhard Engleder (TSNEP_TX_TYPE_SKB_FRAG | TSNEP_TX_TYPE_XDP_NDO))
494403f69bbSGerhard Engleder dma_unmap_page(dmadev,
495403f69bbSGerhard Engleder dma_unmap_addr(entry, dma),
496403f69bbSGerhard Engleder dma_unmap_len(entry, len),
497403f69bbSGerhard Engleder DMA_TO_DEVICE);
498b99ac751SGerhard Engleder map_len += entry->len;
499403f69bbSGerhard Engleder entry->len = 0;
500403f69bbSGerhard Engleder }
501403f69bbSGerhard Engleder }
502b99ac751SGerhard Engleder
503b99ac751SGerhard Engleder return map_len;
504403f69bbSGerhard Engleder }
505403f69bbSGerhard Engleder
tsnep_xmit_frame_ring(struct sk_buff * skb,struct tsnep_tx * tx)506403f69bbSGerhard Engleder static netdev_tx_t tsnep_xmit_frame_ring(struct sk_buff *skb,
507403f69bbSGerhard Engleder struct tsnep_tx *tx)
508403f69bbSGerhard Engleder {
509403f69bbSGerhard Engleder int count = 1;
510403f69bbSGerhard Engleder struct tsnep_tx_entry *entry;
511b99ac751SGerhard Engleder int length;
512403f69bbSGerhard Engleder int i;
513403f69bbSGerhard Engleder int retval;
514403f69bbSGerhard Engleder
515403f69bbSGerhard Engleder if (skb_shinfo(skb)->nr_frags > 0)
516403f69bbSGerhard Engleder count += skb_shinfo(skb)->nr_frags;
517403f69bbSGerhard Engleder
518403f69bbSGerhard Engleder if (tsnep_tx_desc_available(tx) < count) {
519403f69bbSGerhard Engleder /* ring full, shall not happen because queue is stopped if full
520403f69bbSGerhard Engleder * below
521403f69bbSGerhard Engleder */
5223d53aaefSGerhard Engleder netif_stop_subqueue(tx->adapter->netdev, tx->queue_index);
523403f69bbSGerhard Engleder
524403f69bbSGerhard Engleder return NETDEV_TX_BUSY;
525403f69bbSGerhard Engleder }
526403f69bbSGerhard Engleder
527403f69bbSGerhard Engleder entry = &tx->entry[tx->write];
528403f69bbSGerhard Engleder entry->skb = skb;
529403f69bbSGerhard Engleder
530403f69bbSGerhard Engleder retval = tsnep_tx_map(skb, tx, count);
531b99ac751SGerhard Engleder if (retval < 0) {
532b3bb8628SGerhard Engleder tsnep_tx_unmap(tx, tx->write, count);
533403f69bbSGerhard Engleder dev_kfree_skb_any(entry->skb);
534403f69bbSGerhard Engleder entry->skb = NULL;
535403f69bbSGerhard Engleder
536403f69bbSGerhard Engleder tx->dropped++;
537403f69bbSGerhard Engleder
538403f69bbSGerhard Engleder return NETDEV_TX_OK;
539403f69bbSGerhard Engleder }
540b99ac751SGerhard Engleder length = retval;
541403f69bbSGerhard Engleder
542403f69bbSGerhard Engleder if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)
543403f69bbSGerhard Engleder skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
544403f69bbSGerhard Engleder
545403f69bbSGerhard Engleder for (i = 0; i < count; i++)
54642fb2962SGerhard Engleder tsnep_tx_activate(tx, (tx->write + i) & TSNEP_RING_MASK, length,
547d24bc0bcSGerhard Engleder i == count - 1);
54842fb2962SGerhard Engleder tx->write = (tx->write + count) & TSNEP_RING_MASK;
549403f69bbSGerhard Engleder
550403f69bbSGerhard Engleder skb_tx_timestamp(skb);
551403f69bbSGerhard Engleder
552403f69bbSGerhard Engleder /* descriptor properties shall be valid before hardware is notified */
553403f69bbSGerhard Engleder dma_wmb();
554403f69bbSGerhard Engleder
555403f69bbSGerhard Engleder iowrite32(TSNEP_CONTROL_TX_ENABLE, tx->addr + TSNEP_CONTROL);
556403f69bbSGerhard Engleder
557403f69bbSGerhard Engleder if (tsnep_tx_desc_available(tx) < (MAX_SKB_FRAGS + 1)) {
558403f69bbSGerhard Engleder /* ring can get full with next frame */
5593d53aaefSGerhard Engleder netif_stop_subqueue(tx->adapter->netdev, tx->queue_index);
560403f69bbSGerhard Engleder }
561403f69bbSGerhard Engleder
562403f69bbSGerhard Engleder return NETDEV_TX_OK;
563403f69bbSGerhard Engleder }
564403f69bbSGerhard Engleder
tsnep_xdp_tx_map(struct xdp_frame * xdpf,struct tsnep_tx * tx,struct skb_shared_info * shinfo,int count,u32 type)565d24bc0bcSGerhard Engleder static int tsnep_xdp_tx_map(struct xdp_frame *xdpf, struct tsnep_tx *tx,
566d24bc0bcSGerhard Engleder struct skb_shared_info *shinfo, int count, u32 type)
567d24bc0bcSGerhard Engleder {
568d24bc0bcSGerhard Engleder struct device *dmadev = tx->adapter->dmadev;
569d24bc0bcSGerhard Engleder struct tsnep_tx_entry *entry;
570d24bc0bcSGerhard Engleder struct page *page;
571d24bc0bcSGerhard Engleder skb_frag_t *frag;
572d24bc0bcSGerhard Engleder unsigned int len;
573d24bc0bcSGerhard Engleder int map_len = 0;
574d24bc0bcSGerhard Engleder dma_addr_t dma;
575d24bc0bcSGerhard Engleder void *data;
576d24bc0bcSGerhard Engleder int i;
577d24bc0bcSGerhard Engleder
578d24bc0bcSGerhard Engleder frag = NULL;
579d24bc0bcSGerhard Engleder len = xdpf->len;
580d24bc0bcSGerhard Engleder for (i = 0; i < count; i++) {
58142fb2962SGerhard Engleder entry = &tx->entry[(tx->write + i) & TSNEP_RING_MASK];
582d24bc0bcSGerhard Engleder if (type & TSNEP_TX_TYPE_XDP_NDO) {
583d24bc0bcSGerhard Engleder data = unlikely(frag) ? skb_frag_address(frag) :
584d24bc0bcSGerhard Engleder xdpf->data;
585d24bc0bcSGerhard Engleder dma = dma_map_single(dmadev, data, len, DMA_TO_DEVICE);
586d24bc0bcSGerhard Engleder if (dma_mapping_error(dmadev, dma))
587d24bc0bcSGerhard Engleder return -ENOMEM;
588d24bc0bcSGerhard Engleder
589d24bc0bcSGerhard Engleder entry->type = TSNEP_TX_TYPE_XDP_NDO;
590d24bc0bcSGerhard Engleder } else {
591d24bc0bcSGerhard Engleder page = unlikely(frag) ? skb_frag_page(frag) :
592d24bc0bcSGerhard Engleder virt_to_page(xdpf->data);
593d24bc0bcSGerhard Engleder dma = page_pool_get_dma_addr(page);
594d24bc0bcSGerhard Engleder if (unlikely(frag))
595d24bc0bcSGerhard Engleder dma += skb_frag_off(frag);
596d24bc0bcSGerhard Engleder else
597d24bc0bcSGerhard Engleder dma += sizeof(*xdpf) + xdpf->headroom;
598d24bc0bcSGerhard Engleder dma_sync_single_for_device(dmadev, dma, len,
599d24bc0bcSGerhard Engleder DMA_BIDIRECTIONAL);
600d24bc0bcSGerhard Engleder
601d24bc0bcSGerhard Engleder entry->type = TSNEP_TX_TYPE_XDP_TX;
602d24bc0bcSGerhard Engleder }
603d24bc0bcSGerhard Engleder
604d24bc0bcSGerhard Engleder entry->len = len;
605d24bc0bcSGerhard Engleder dma_unmap_addr_set(entry, dma, dma);
606d24bc0bcSGerhard Engleder
607d24bc0bcSGerhard Engleder entry->desc->tx = __cpu_to_le64(dma);
608d24bc0bcSGerhard Engleder
609d24bc0bcSGerhard Engleder map_len += len;
610d24bc0bcSGerhard Engleder
611d24bc0bcSGerhard Engleder if (i + 1 < count) {
612d24bc0bcSGerhard Engleder frag = &shinfo->frags[i];
613d24bc0bcSGerhard Engleder len = skb_frag_size(frag);
614d24bc0bcSGerhard Engleder }
615d24bc0bcSGerhard Engleder }
616d24bc0bcSGerhard Engleder
617d24bc0bcSGerhard Engleder return map_len;
618d24bc0bcSGerhard Engleder }
619d24bc0bcSGerhard Engleder
620d24bc0bcSGerhard Engleder /* This function requires __netif_tx_lock is held by the caller. */
tsnep_xdp_xmit_frame_ring(struct xdp_frame * xdpf,struct tsnep_tx * tx,u32 type)621d24bc0bcSGerhard Engleder static bool tsnep_xdp_xmit_frame_ring(struct xdp_frame *xdpf,
622d24bc0bcSGerhard Engleder struct tsnep_tx *tx, u32 type)
623d24bc0bcSGerhard Engleder {
624d24bc0bcSGerhard Engleder struct skb_shared_info *shinfo = xdp_get_shared_info_from_frame(xdpf);
625d24bc0bcSGerhard Engleder struct tsnep_tx_entry *entry;
626d24bc0bcSGerhard Engleder int count, length, retval, i;
627d24bc0bcSGerhard Engleder
628d24bc0bcSGerhard Engleder count = 1;
629d24bc0bcSGerhard Engleder if (unlikely(xdp_frame_has_frags(xdpf)))
630d24bc0bcSGerhard Engleder count += shinfo->nr_frags;
631d24bc0bcSGerhard Engleder
632d24bc0bcSGerhard Engleder /* ensure that TX ring is not filled up by XDP, always MAX_SKB_FRAGS
633d24bc0bcSGerhard Engleder * will be available for normal TX path and queue is stopped there if
634d24bc0bcSGerhard Engleder * necessary
635d24bc0bcSGerhard Engleder */
636d24bc0bcSGerhard Engleder if (tsnep_tx_desc_available(tx) < (MAX_SKB_FRAGS + 1 + count))
637d24bc0bcSGerhard Engleder return false;
638d24bc0bcSGerhard Engleder
639d24bc0bcSGerhard Engleder entry = &tx->entry[tx->write];
640d24bc0bcSGerhard Engleder entry->xdpf = xdpf;
641d24bc0bcSGerhard Engleder
642d24bc0bcSGerhard Engleder retval = tsnep_xdp_tx_map(xdpf, tx, shinfo, count, type);
643d24bc0bcSGerhard Engleder if (retval < 0) {
644d24bc0bcSGerhard Engleder tsnep_tx_unmap(tx, tx->write, count);
645d24bc0bcSGerhard Engleder entry->xdpf = NULL;
646d24bc0bcSGerhard Engleder
647d24bc0bcSGerhard Engleder tx->dropped++;
648d24bc0bcSGerhard Engleder
649d24bc0bcSGerhard Engleder return false;
650d24bc0bcSGerhard Engleder }
651d24bc0bcSGerhard Engleder length = retval;
652d24bc0bcSGerhard Engleder
653d24bc0bcSGerhard Engleder for (i = 0; i < count; i++)
65442fb2962SGerhard Engleder tsnep_tx_activate(tx, (tx->write + i) & TSNEP_RING_MASK, length,
655d24bc0bcSGerhard Engleder i == count - 1);
65642fb2962SGerhard Engleder tx->write = (tx->write + count) & TSNEP_RING_MASK;
657d24bc0bcSGerhard Engleder
658d24bc0bcSGerhard Engleder /* descriptor properties shall be valid before hardware is notified */
659d24bc0bcSGerhard Engleder dma_wmb();
660d24bc0bcSGerhard Engleder
661d24bc0bcSGerhard Engleder return true;
662d24bc0bcSGerhard Engleder }
663d24bc0bcSGerhard Engleder
tsnep_xdp_xmit_flush(struct tsnep_tx * tx)664d24bc0bcSGerhard Engleder static void tsnep_xdp_xmit_flush(struct tsnep_tx *tx)
665d24bc0bcSGerhard Engleder {
666d24bc0bcSGerhard Engleder iowrite32(TSNEP_CONTROL_TX_ENABLE, tx->addr + TSNEP_CONTROL);
667d24bc0bcSGerhard Engleder }
668d24bc0bcSGerhard Engleder
tsnep_xdp_xmit_back(struct tsnep_adapter * adapter,struct xdp_buff * xdp,struct netdev_queue * tx_nq,struct tsnep_tx * tx,bool zc)66965b28c81SGerhard Engleder static bool tsnep_xdp_xmit_back(struct tsnep_adapter *adapter,
67065b28c81SGerhard Engleder struct xdp_buff *xdp,
671*10db3a7eSGerhard Engleder struct netdev_queue *tx_nq, struct tsnep_tx *tx,
672*10db3a7eSGerhard Engleder bool zc)
67365b28c81SGerhard Engleder {
67465b28c81SGerhard Engleder struct xdp_frame *xdpf = xdp_convert_buff_to_frame(xdp);
67565b28c81SGerhard Engleder bool xmit;
676*10db3a7eSGerhard Engleder u32 type;
67765b28c81SGerhard Engleder
67865b28c81SGerhard Engleder if (unlikely(!xdpf))
67965b28c81SGerhard Engleder return false;
68065b28c81SGerhard Engleder
681*10db3a7eSGerhard Engleder /* no page pool for zero copy */
682*10db3a7eSGerhard Engleder if (zc)
683*10db3a7eSGerhard Engleder type = TSNEP_TX_TYPE_XDP_NDO;
684*10db3a7eSGerhard Engleder else
685*10db3a7eSGerhard Engleder type = TSNEP_TX_TYPE_XDP_TX;
686*10db3a7eSGerhard Engleder
68765b28c81SGerhard Engleder __netif_tx_lock(tx_nq, smp_processor_id());
68865b28c81SGerhard Engleder
689*10db3a7eSGerhard Engleder xmit = tsnep_xdp_xmit_frame_ring(xdpf, tx, type);
69065b28c81SGerhard Engleder
69165b28c81SGerhard Engleder /* Avoid transmit queue timeout since we share it with the slow path */
69265b28c81SGerhard Engleder if (xmit)
69365b28c81SGerhard Engleder txq_trans_cond_update(tx_nq);
69465b28c81SGerhard Engleder
69565b28c81SGerhard Engleder __netif_tx_unlock(tx_nq);
69665b28c81SGerhard Engleder
69765b28c81SGerhard Engleder return xmit;
69865b28c81SGerhard Engleder }
69965b28c81SGerhard Engleder
tsnep_xdp_tx_map_zc(struct xdp_desc * xdpd,struct tsnep_tx * tx)700cd275c23SGerhard Engleder static int tsnep_xdp_tx_map_zc(struct xdp_desc *xdpd, struct tsnep_tx *tx)
701cd275c23SGerhard Engleder {
702cd275c23SGerhard Engleder struct tsnep_tx_entry *entry;
703cd275c23SGerhard Engleder dma_addr_t dma;
704cd275c23SGerhard Engleder
705cd275c23SGerhard Engleder entry = &tx->entry[tx->write];
706cd275c23SGerhard Engleder entry->zc = true;
707cd275c23SGerhard Engleder
708cd275c23SGerhard Engleder dma = xsk_buff_raw_get_dma(tx->xsk_pool, xdpd->addr);
709cd275c23SGerhard Engleder xsk_buff_raw_dma_sync_for_device(tx->xsk_pool, dma, xdpd->len);
710cd275c23SGerhard Engleder
711cd275c23SGerhard Engleder entry->type = TSNEP_TX_TYPE_XSK;
712cd275c23SGerhard Engleder entry->len = xdpd->len;
713cd275c23SGerhard Engleder
714cd275c23SGerhard Engleder entry->desc->tx = __cpu_to_le64(dma);
715cd275c23SGerhard Engleder
716cd275c23SGerhard Engleder return xdpd->len;
717cd275c23SGerhard Engleder }
718cd275c23SGerhard Engleder
tsnep_xdp_xmit_frame_ring_zc(struct xdp_desc * xdpd,struct tsnep_tx * tx)719cd275c23SGerhard Engleder static void tsnep_xdp_xmit_frame_ring_zc(struct xdp_desc *xdpd,
720cd275c23SGerhard Engleder struct tsnep_tx *tx)
721cd275c23SGerhard Engleder {
722cd275c23SGerhard Engleder int length;
723cd275c23SGerhard Engleder
724cd275c23SGerhard Engleder length = tsnep_xdp_tx_map_zc(xdpd, tx);
725cd275c23SGerhard Engleder
726cd275c23SGerhard Engleder tsnep_tx_activate(tx, tx->write, length, true);
727cd275c23SGerhard Engleder tx->write = (tx->write + 1) & TSNEP_RING_MASK;
728cd275c23SGerhard Engleder }
729cd275c23SGerhard Engleder
tsnep_xdp_xmit_zc(struct tsnep_tx * tx)730cd275c23SGerhard Engleder static void tsnep_xdp_xmit_zc(struct tsnep_tx *tx)
731cd275c23SGerhard Engleder {
732cd275c23SGerhard Engleder int desc_available = tsnep_tx_desc_available(tx);
733cd275c23SGerhard Engleder struct xdp_desc *descs = tx->xsk_pool->tx_descs;
734cd275c23SGerhard Engleder int batch, i;
735cd275c23SGerhard Engleder
736cd275c23SGerhard Engleder /* ensure that TX ring is not filled up by XDP, always MAX_SKB_FRAGS
737cd275c23SGerhard Engleder * will be available for normal TX path and queue is stopped there if
738cd275c23SGerhard Engleder * necessary
739cd275c23SGerhard Engleder */
740cd275c23SGerhard Engleder if (desc_available <= (MAX_SKB_FRAGS + 1))
741cd275c23SGerhard Engleder return;
742cd275c23SGerhard Engleder desc_available -= MAX_SKB_FRAGS + 1;
743cd275c23SGerhard Engleder
744cd275c23SGerhard Engleder batch = xsk_tx_peek_release_desc_batch(tx->xsk_pool, desc_available);
745cd275c23SGerhard Engleder for (i = 0; i < batch; i++)
746cd275c23SGerhard Engleder tsnep_xdp_xmit_frame_ring_zc(&descs[i], tx);
747cd275c23SGerhard Engleder
748cd275c23SGerhard Engleder if (batch) {
749cd275c23SGerhard Engleder /* descriptor properties shall be valid before hardware is
750cd275c23SGerhard Engleder * notified
751cd275c23SGerhard Engleder */
752cd275c23SGerhard Engleder dma_wmb();
753cd275c23SGerhard Engleder
754cd275c23SGerhard Engleder tsnep_xdp_xmit_flush(tx);
755cd275c23SGerhard Engleder }
756cd275c23SGerhard Engleder }
757cd275c23SGerhard Engleder
tsnep_tx_poll(struct tsnep_tx * tx,int napi_budget)758403f69bbSGerhard Engleder static bool tsnep_tx_poll(struct tsnep_tx *tx, int napi_budget)
759403f69bbSGerhard Engleder {
760403f69bbSGerhard Engleder struct tsnep_tx_entry *entry;
76125faa6a4SGerhard Engleder struct netdev_queue *nq;
762cd275c23SGerhard Engleder int xsk_frames = 0;
76325faa6a4SGerhard Engleder int budget = 128;
764b99ac751SGerhard Engleder int length;
76525faa6a4SGerhard Engleder int count;
766403f69bbSGerhard Engleder
76725faa6a4SGerhard Engleder nq = netdev_get_tx_queue(tx->adapter->netdev, tx->queue_index);
76825faa6a4SGerhard Engleder __netif_tx_lock(nq, smp_processor_id());
769403f69bbSGerhard Engleder
770403f69bbSGerhard Engleder do {
771403f69bbSGerhard Engleder if (tx->read == tx->write)
772403f69bbSGerhard Engleder break;
773403f69bbSGerhard Engleder
774403f69bbSGerhard Engleder entry = &tx->entry[tx->read];
775403f69bbSGerhard Engleder if ((__le32_to_cpu(entry->desc_wb->properties) &
776403f69bbSGerhard Engleder TSNEP_TX_DESC_OWNER_MASK) !=
777403f69bbSGerhard Engleder (entry->properties & TSNEP_TX_DESC_OWNER_MASK))
778403f69bbSGerhard Engleder break;
779403f69bbSGerhard Engleder
780403f69bbSGerhard Engleder /* descriptor properties shall be read first, because valid data
781403f69bbSGerhard Engleder * is signaled there
782403f69bbSGerhard Engleder */
783403f69bbSGerhard Engleder dma_rmb();
784403f69bbSGerhard Engleder
785403f69bbSGerhard Engleder count = 1;
786d24bc0bcSGerhard Engleder if ((entry->type & TSNEP_TX_TYPE_SKB) &&
787d24bc0bcSGerhard Engleder skb_shinfo(entry->skb)->nr_frags > 0)
788403f69bbSGerhard Engleder count += skb_shinfo(entry->skb)->nr_frags;
789cd275c23SGerhard Engleder else if ((entry->type & TSNEP_TX_TYPE_XDP) &&
790d24bc0bcSGerhard Engleder xdp_frame_has_frags(entry->xdpf))
791d24bc0bcSGerhard Engleder count += xdp_get_shared_info_from_frame(entry->xdpf)->nr_frags;
792403f69bbSGerhard Engleder
793b99ac751SGerhard Engleder length = tsnep_tx_unmap(tx, tx->read, count);
794403f69bbSGerhard Engleder
795d24bc0bcSGerhard Engleder if ((entry->type & TSNEP_TX_TYPE_SKB) &&
796d24bc0bcSGerhard Engleder (skb_shinfo(entry->skb)->tx_flags & SKBTX_IN_PROGRESS) &&
797403f69bbSGerhard Engleder (__le32_to_cpu(entry->desc_wb->properties) &
798403f69bbSGerhard Engleder TSNEP_DESC_EXTENDED_WRITEBACK_FLAG)) {
799403f69bbSGerhard Engleder struct skb_shared_hwtstamps hwtstamps;
8000abb62b6SGerhard Engleder u64 timestamp;
8010abb62b6SGerhard Engleder
8020abb62b6SGerhard Engleder if (skb_shinfo(entry->skb)->tx_flags &
8030abb62b6SGerhard Engleder SKBTX_HW_TSTAMP_USE_CYCLES)
8040abb62b6SGerhard Engleder timestamp =
8050abb62b6SGerhard Engleder __le64_to_cpu(entry->desc_wb->counter);
8060abb62b6SGerhard Engleder else
8070abb62b6SGerhard Engleder timestamp =
808403f69bbSGerhard Engleder __le64_to_cpu(entry->desc_wb->timestamp);
809403f69bbSGerhard Engleder
810403f69bbSGerhard Engleder memset(&hwtstamps, 0, sizeof(hwtstamps));
811403f69bbSGerhard Engleder hwtstamps.hwtstamp = ns_to_ktime(timestamp);
812403f69bbSGerhard Engleder
813403f69bbSGerhard Engleder skb_tstamp_tx(entry->skb, &hwtstamps);
814403f69bbSGerhard Engleder }
815403f69bbSGerhard Engleder
816d24bc0bcSGerhard Engleder if (entry->type & TSNEP_TX_TYPE_SKB)
8170625dff3SGerhard Engleder napi_consume_skb(entry->skb, napi_budget);
818cd275c23SGerhard Engleder else if (entry->type & TSNEP_TX_TYPE_XDP)
819d24bc0bcSGerhard Engleder xdp_return_frame_rx_napi(entry->xdpf);
820cd275c23SGerhard Engleder else
821cd275c23SGerhard Engleder xsk_frames++;
822cd275c23SGerhard Engleder /* xdpf and zc are union with skb */
823403f69bbSGerhard Engleder entry->skb = NULL;
824403f69bbSGerhard Engleder
82542fb2962SGerhard Engleder tx->read = (tx->read + count) & TSNEP_RING_MASK;
826403f69bbSGerhard Engleder
827b99ac751SGerhard Engleder tx->packets++;
828b99ac751SGerhard Engleder tx->bytes += length + ETH_FCS_LEN;
829b99ac751SGerhard Engleder
830403f69bbSGerhard Engleder budget--;
831403f69bbSGerhard Engleder } while (likely(budget));
832403f69bbSGerhard Engleder
833cd275c23SGerhard Engleder if (tx->xsk_pool) {
834cd275c23SGerhard Engleder if (xsk_frames)
835cd275c23SGerhard Engleder xsk_tx_completed(tx->xsk_pool, xsk_frames);
836cd275c23SGerhard Engleder if (xsk_uses_need_wakeup(tx->xsk_pool))
837cd275c23SGerhard Engleder xsk_set_tx_need_wakeup(tx->xsk_pool);
838cd275c23SGerhard Engleder tsnep_xdp_xmit_zc(tx);
839cd275c23SGerhard Engleder }
840cd275c23SGerhard Engleder
841403f69bbSGerhard Engleder if ((tsnep_tx_desc_available(tx) >= ((MAX_SKB_FRAGS + 1) * 2)) &&
8423d53aaefSGerhard Engleder netif_tx_queue_stopped(nq)) {
8433d53aaefSGerhard Engleder netif_tx_wake_queue(nq);
844403f69bbSGerhard Engleder }
845403f69bbSGerhard Engleder
84625faa6a4SGerhard Engleder __netif_tx_unlock(nq);
847403f69bbSGerhard Engleder
848d24bc0bcSGerhard Engleder return budget != 0;
849403f69bbSGerhard Engleder }
850403f69bbSGerhard Engleder
tsnep_tx_pending(struct tsnep_tx * tx)8512dc4ac91SGerhard Engleder static bool tsnep_tx_pending(struct tsnep_tx *tx)
8522dc4ac91SGerhard Engleder {
8532dc4ac91SGerhard Engleder struct tsnep_tx_entry *entry;
85425faa6a4SGerhard Engleder struct netdev_queue *nq;
8552dc4ac91SGerhard Engleder bool pending = false;
8562dc4ac91SGerhard Engleder
85725faa6a4SGerhard Engleder nq = netdev_get_tx_queue(tx->adapter->netdev, tx->queue_index);
85825faa6a4SGerhard Engleder __netif_tx_lock(nq, smp_processor_id());
8592dc4ac91SGerhard Engleder
8602dc4ac91SGerhard Engleder if (tx->read != tx->write) {
8612dc4ac91SGerhard Engleder entry = &tx->entry[tx->read];
8622dc4ac91SGerhard Engleder if ((__le32_to_cpu(entry->desc_wb->properties) &
8632dc4ac91SGerhard Engleder TSNEP_TX_DESC_OWNER_MASK) ==
8642dc4ac91SGerhard Engleder (entry->properties & TSNEP_TX_DESC_OWNER_MASK))
8652dc4ac91SGerhard Engleder pending = true;
8662dc4ac91SGerhard Engleder }
8672dc4ac91SGerhard Engleder
86825faa6a4SGerhard Engleder __netif_tx_unlock(nq);
8692dc4ac91SGerhard Engleder
8702dc4ac91SGerhard Engleder return pending;
8712dc4ac91SGerhard Engleder }
8722dc4ac91SGerhard Engleder
tsnep_tx_open(struct tsnep_tx * tx)87333b0ee02SGerhard Engleder static int tsnep_tx_open(struct tsnep_tx *tx)
874403f69bbSGerhard Engleder {
875403f69bbSGerhard Engleder int retval;
876403f69bbSGerhard Engleder
87733b0ee02SGerhard Engleder retval = tsnep_tx_ring_create(tx);
878403f69bbSGerhard Engleder if (retval)
879403f69bbSGerhard Engleder return retval;
880403f69bbSGerhard Engleder
88133b0ee02SGerhard Engleder tsnep_tx_init(tx);
882403f69bbSGerhard Engleder
883403f69bbSGerhard Engleder return 0;
884403f69bbSGerhard Engleder }
885403f69bbSGerhard Engleder
tsnep_tx_close(struct tsnep_tx * tx)886403f69bbSGerhard Engleder static void tsnep_tx_close(struct tsnep_tx *tx)
887403f69bbSGerhard Engleder {
888403f69bbSGerhard Engleder tsnep_tx_ring_cleanup(tx);
889403f69bbSGerhard Engleder }
890403f69bbSGerhard Engleder
tsnep_rx_ring_cleanup(struct tsnep_rx * rx)891403f69bbSGerhard Engleder static void tsnep_rx_ring_cleanup(struct tsnep_rx *rx)
892403f69bbSGerhard Engleder {
893403f69bbSGerhard Engleder struct device *dmadev = rx->adapter->dmadev;
894403f69bbSGerhard Engleder struct tsnep_rx_entry *entry;
895403f69bbSGerhard Engleder int i;
896403f69bbSGerhard Engleder
897403f69bbSGerhard Engleder for (i = 0; i < TSNEP_RING_SIZE; i++) {
898403f69bbSGerhard Engleder entry = &rx->entry[i];
8993fc23339SGerhard Engleder if (!rx->xsk_pool && entry->page)
900bb837a37SGerhard Engleder page_pool_put_full_page(rx->page_pool, entry->page,
901bb837a37SGerhard Engleder false);
9023fc23339SGerhard Engleder if (rx->xsk_pool && entry->xdp)
9033fc23339SGerhard Engleder xsk_buff_free(entry->xdp);
9043fc23339SGerhard Engleder /* xdp is union with page */
905bb837a37SGerhard Engleder entry->page = NULL;
906403f69bbSGerhard Engleder }
907403f69bbSGerhard Engleder
908bb837a37SGerhard Engleder if (rx->page_pool)
909bb837a37SGerhard Engleder page_pool_destroy(rx->page_pool);
910bb837a37SGerhard Engleder
911403f69bbSGerhard Engleder memset(rx->entry, 0, sizeof(rx->entry));
912403f69bbSGerhard Engleder
913403f69bbSGerhard Engleder for (i = 0; i < TSNEP_RING_PAGE_COUNT; i++) {
914403f69bbSGerhard Engleder if (rx->page[i]) {
915403f69bbSGerhard Engleder dma_free_coherent(dmadev, PAGE_SIZE, rx->page[i],
916403f69bbSGerhard Engleder rx->page_dma[i]);
917403f69bbSGerhard Engleder rx->page[i] = NULL;
918403f69bbSGerhard Engleder rx->page_dma[i] = 0;
919403f69bbSGerhard Engleder }
920403f69bbSGerhard Engleder }
921403f69bbSGerhard Engleder }
922403f69bbSGerhard Engleder
tsnep_rx_ring_create(struct tsnep_rx * rx)92333b0ee02SGerhard Engleder static int tsnep_rx_ring_create(struct tsnep_rx *rx)
924403f69bbSGerhard Engleder {
925403f69bbSGerhard Engleder struct device *dmadev = rx->adapter->dmadev;
926403f69bbSGerhard Engleder struct tsnep_rx_entry *entry;
927bb837a37SGerhard Engleder struct page_pool_params pp_params = { 0 };
928403f69bbSGerhard Engleder struct tsnep_rx_entry *next_entry;
929403f69bbSGerhard Engleder int i, j;
930403f69bbSGerhard Engleder int retval;
931403f69bbSGerhard Engleder
932403f69bbSGerhard Engleder for (i = 0; i < TSNEP_RING_PAGE_COUNT; i++) {
933403f69bbSGerhard Engleder rx->page[i] =
934403f69bbSGerhard Engleder dma_alloc_coherent(dmadev, PAGE_SIZE, &rx->page_dma[i],
935403f69bbSGerhard Engleder GFP_KERNEL);
936403f69bbSGerhard Engleder if (!rx->page[i]) {
937403f69bbSGerhard Engleder retval = -ENOMEM;
938403f69bbSGerhard Engleder goto failed;
939403f69bbSGerhard Engleder }
940403f69bbSGerhard Engleder for (j = 0; j < TSNEP_RING_ENTRIES_PER_PAGE; j++) {
941403f69bbSGerhard Engleder entry = &rx->entry[TSNEP_RING_ENTRIES_PER_PAGE * i + j];
942403f69bbSGerhard Engleder entry->desc_wb = (struct tsnep_rx_desc_wb *)
943403f69bbSGerhard Engleder (((u8 *)rx->page[i]) + TSNEP_DESC_SIZE * j);
944403f69bbSGerhard Engleder entry->desc = (struct tsnep_rx_desc *)
945403f69bbSGerhard Engleder (((u8 *)entry->desc_wb) + TSNEP_DESC_OFFSET);
946403f69bbSGerhard Engleder entry->desc_dma = rx->page_dma[i] + TSNEP_DESC_SIZE * j;
947403f69bbSGerhard Engleder }
948403f69bbSGerhard Engleder }
949bb837a37SGerhard Engleder
950bb837a37SGerhard Engleder pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;
951bb837a37SGerhard Engleder pp_params.order = 0;
952bb837a37SGerhard Engleder pp_params.pool_size = TSNEP_RING_SIZE;
953bb837a37SGerhard Engleder pp_params.nid = dev_to_node(dmadev);
954bb837a37SGerhard Engleder pp_params.dev = dmadev;
955cc3e254fSGerhard Engleder pp_params.dma_dir = DMA_BIDIRECTIONAL;
956bb837a37SGerhard Engleder pp_params.max_len = TSNEP_MAX_RX_BUF_SIZE;
957cc3e254fSGerhard Engleder pp_params.offset = TSNEP_RX_OFFSET;
958bb837a37SGerhard Engleder rx->page_pool = page_pool_create(&pp_params);
959bb837a37SGerhard Engleder if (IS_ERR(rx->page_pool)) {
960bb837a37SGerhard Engleder retval = PTR_ERR(rx->page_pool);
961bb837a37SGerhard Engleder rx->page_pool = NULL;
962bb837a37SGerhard Engleder goto failed;
963bb837a37SGerhard Engleder }
964bb837a37SGerhard Engleder
965403f69bbSGerhard Engleder for (i = 0; i < TSNEP_RING_SIZE; i++) {
966403f69bbSGerhard Engleder entry = &rx->entry[i];
96742fb2962SGerhard Engleder next_entry = &rx->entry[(i + 1) & TSNEP_RING_MASK];
968403f69bbSGerhard Engleder entry->desc->next = __cpu_to_le64(next_entry->desc_dma);
969403f69bbSGerhard Engleder }
970403f69bbSGerhard Engleder
971403f69bbSGerhard Engleder return 0;
972403f69bbSGerhard Engleder
973403f69bbSGerhard Engleder failed:
974403f69bbSGerhard Engleder tsnep_rx_ring_cleanup(rx);
975403f69bbSGerhard Engleder return retval;
976403f69bbSGerhard Engleder }
977403f69bbSGerhard Engleder
tsnep_rx_init(struct tsnep_rx * rx)97833b0ee02SGerhard Engleder static void tsnep_rx_init(struct tsnep_rx *rx)
97933b0ee02SGerhard Engleder {
98033b0ee02SGerhard Engleder dma_addr_t dma;
98133b0ee02SGerhard Engleder
98233b0ee02SGerhard Engleder dma = rx->entry[0].desc_dma | TSNEP_RESET_OWNER_COUNTER;
98333b0ee02SGerhard Engleder iowrite32(DMA_ADDR_LOW(dma), rx->addr + TSNEP_RX_DESC_ADDR_LOW);
98433b0ee02SGerhard Engleder iowrite32(DMA_ADDR_HIGH(dma), rx->addr + TSNEP_RX_DESC_ADDR_HIGH);
98533b0ee02SGerhard Engleder rx->write = 0;
98633b0ee02SGerhard Engleder rx->read = 0;
98733b0ee02SGerhard Engleder rx->owner_counter = 1;
98833b0ee02SGerhard Engleder rx->increment_owner_counter = TSNEP_RING_SIZE - 1;
98933b0ee02SGerhard Engleder }
99033b0ee02SGerhard Engleder
tsnep_rx_enable(struct tsnep_rx * rx)9912ea0a282SGerhard Engleder static void tsnep_rx_enable(struct tsnep_rx *rx)
9922ea0a282SGerhard Engleder {
9932ea0a282SGerhard Engleder /* descriptor properties shall be valid before hardware is notified */
9942ea0a282SGerhard Engleder dma_wmb();
9952ea0a282SGerhard Engleder
9962ea0a282SGerhard Engleder iowrite32(TSNEP_CONTROL_RX_ENABLE, rx->addr + TSNEP_CONTROL);
9972ea0a282SGerhard Engleder }
9982ea0a282SGerhard Engleder
tsnep_rx_disable(struct tsnep_rx * rx)9992ea0a282SGerhard Engleder static void tsnep_rx_disable(struct tsnep_rx *rx)
10002ea0a282SGerhard Engleder {
10012ea0a282SGerhard Engleder u32 val;
10022ea0a282SGerhard Engleder
10032ea0a282SGerhard Engleder iowrite32(TSNEP_CONTROL_RX_DISABLE, rx->addr + TSNEP_CONTROL);
10042ea0a282SGerhard Engleder readx_poll_timeout(ioread32, rx->addr + TSNEP_CONTROL, val,
10052ea0a282SGerhard Engleder ((val & TSNEP_CONTROL_RX_ENABLE) == 0), 10000,
10062ea0a282SGerhard Engleder 1000000);
10072ea0a282SGerhard Engleder }
10082ea0a282SGerhard Engleder
tsnep_rx_desc_available(struct tsnep_rx * rx)1009dbadae92SGerhard Engleder static int tsnep_rx_desc_available(struct tsnep_rx *rx)
1010dbadae92SGerhard Engleder {
1011dbadae92SGerhard Engleder if (rx->read <= rx->write)
1012dbadae92SGerhard Engleder return TSNEP_RING_SIZE - rx->write + rx->read - 1;
1013dbadae92SGerhard Engleder else
1014dbadae92SGerhard Engleder return rx->read - rx->write - 1;
1015dbadae92SGerhard Engleder }
1016dbadae92SGerhard Engleder
tsnep_rx_free_page_buffer(struct tsnep_rx * rx)10173fc23339SGerhard Engleder static void tsnep_rx_free_page_buffer(struct tsnep_rx *rx)
10183fc23339SGerhard Engleder {
10193fc23339SGerhard Engleder struct page **page;
10203fc23339SGerhard Engleder
10213fc23339SGerhard Engleder /* last entry of page_buffer is always zero, because ring cannot be
10223fc23339SGerhard Engleder * filled completely
10233fc23339SGerhard Engleder */
10243fc23339SGerhard Engleder page = rx->page_buffer;
10253fc23339SGerhard Engleder while (*page) {
10263fc23339SGerhard Engleder page_pool_put_full_page(rx->page_pool, *page, false);
10273fc23339SGerhard Engleder *page = NULL;
10283fc23339SGerhard Engleder page++;
10293fc23339SGerhard Engleder }
10303fc23339SGerhard Engleder }
10313fc23339SGerhard Engleder
tsnep_rx_alloc_page_buffer(struct tsnep_rx * rx)10323fc23339SGerhard Engleder static int tsnep_rx_alloc_page_buffer(struct tsnep_rx *rx)
10333fc23339SGerhard Engleder {
10343fc23339SGerhard Engleder int i;
10353fc23339SGerhard Engleder
10363fc23339SGerhard Engleder /* alloc for all ring entries except the last one, because ring cannot
10373fc23339SGerhard Engleder * be filled completely
10383fc23339SGerhard Engleder */
10393fc23339SGerhard Engleder for (i = 0; i < TSNEP_RING_SIZE - 1; i++) {
10403fc23339SGerhard Engleder rx->page_buffer[i] = page_pool_dev_alloc_pages(rx->page_pool);
10413fc23339SGerhard Engleder if (!rx->page_buffer[i]) {
10423fc23339SGerhard Engleder tsnep_rx_free_page_buffer(rx);
10433fc23339SGerhard Engleder
10443fc23339SGerhard Engleder return -ENOMEM;
10453fc23339SGerhard Engleder }
10463fc23339SGerhard Engleder }
10473fc23339SGerhard Engleder
10483fc23339SGerhard Engleder return 0;
10493fc23339SGerhard Engleder }
10503fc23339SGerhard Engleder
tsnep_rx_set_page(struct tsnep_rx * rx,struct tsnep_rx_entry * entry,struct page * page)1051dbadae92SGerhard Engleder static void tsnep_rx_set_page(struct tsnep_rx *rx, struct tsnep_rx_entry *entry,
1052dbadae92SGerhard Engleder struct page *page)
1053dbadae92SGerhard Engleder {
1054dbadae92SGerhard Engleder entry->page = page;
1055dbadae92SGerhard Engleder entry->len = TSNEP_MAX_RX_BUF_SIZE;
1056dbadae92SGerhard Engleder entry->dma = page_pool_get_dma_addr(entry->page);
1057cc3e254fSGerhard Engleder entry->desc->rx = __cpu_to_le64(entry->dma + TSNEP_RX_OFFSET);
1058dbadae92SGerhard Engleder }
1059dbadae92SGerhard Engleder
tsnep_rx_alloc_buffer(struct tsnep_rx * rx,int index)1060dbadae92SGerhard Engleder static int tsnep_rx_alloc_buffer(struct tsnep_rx *rx, int index)
1061dbadae92SGerhard Engleder {
1062dbadae92SGerhard Engleder struct tsnep_rx_entry *entry = &rx->entry[index];
1063dbadae92SGerhard Engleder struct page *page;
1064dbadae92SGerhard Engleder
1065dbadae92SGerhard Engleder page = page_pool_dev_alloc_pages(rx->page_pool);
1066dbadae92SGerhard Engleder if (unlikely(!page))
1067dbadae92SGerhard Engleder return -ENOMEM;
1068dbadae92SGerhard Engleder tsnep_rx_set_page(rx, entry, page);
1069dbadae92SGerhard Engleder
1070dbadae92SGerhard Engleder return 0;
1071dbadae92SGerhard Engleder }
1072dbadae92SGerhard Engleder
tsnep_rx_reuse_buffer(struct tsnep_rx * rx,int index)1073dbadae92SGerhard Engleder static void tsnep_rx_reuse_buffer(struct tsnep_rx *rx, int index)
1074dbadae92SGerhard Engleder {
1075dbadae92SGerhard Engleder struct tsnep_rx_entry *entry = &rx->entry[index];
1076dbadae92SGerhard Engleder struct tsnep_rx_entry *read = &rx->entry[rx->read];
1077dbadae92SGerhard Engleder
1078dbadae92SGerhard Engleder tsnep_rx_set_page(rx, entry, read->page);
1079dbadae92SGerhard Engleder read->page = NULL;
1080dbadae92SGerhard Engleder }
1081dbadae92SGerhard Engleder
tsnep_rx_activate(struct tsnep_rx * rx,int index)1082403f69bbSGerhard Engleder static void tsnep_rx_activate(struct tsnep_rx *rx, int index)
1083403f69bbSGerhard Engleder {
1084403f69bbSGerhard Engleder struct tsnep_rx_entry *entry = &rx->entry[index];
1085403f69bbSGerhard Engleder
10863fc23339SGerhard Engleder /* TSNEP_MAX_RX_BUF_SIZE and TSNEP_XSK_RX_BUF_SIZE are multiple of 4 */
1087403f69bbSGerhard Engleder entry->properties = entry->len & TSNEP_DESC_LENGTH_MASK;
1088403f69bbSGerhard Engleder entry->properties |= TSNEP_DESC_INTERRUPT_FLAG;
1089403f69bbSGerhard Engleder if (index == rx->increment_owner_counter) {
1090403f69bbSGerhard Engleder rx->owner_counter++;
1091403f69bbSGerhard Engleder if (rx->owner_counter == 4)
1092403f69bbSGerhard Engleder rx->owner_counter = 1;
1093403f69bbSGerhard Engleder rx->increment_owner_counter--;
1094403f69bbSGerhard Engleder if (rx->increment_owner_counter < 0)
1095403f69bbSGerhard Engleder rx->increment_owner_counter = TSNEP_RING_SIZE - 1;
1096403f69bbSGerhard Engleder }
1097403f69bbSGerhard Engleder entry->properties |=
1098403f69bbSGerhard Engleder (rx->owner_counter << TSNEP_DESC_OWNER_COUNTER_SHIFT) &
1099403f69bbSGerhard Engleder TSNEP_DESC_OWNER_COUNTER_MASK;
1100403f69bbSGerhard Engleder
1101403f69bbSGerhard Engleder /* descriptor properties shall be written last, because valid data is
1102403f69bbSGerhard Engleder * signaled there
1103403f69bbSGerhard Engleder */
1104403f69bbSGerhard Engleder dma_wmb();
1105403f69bbSGerhard Engleder
1106403f69bbSGerhard Engleder entry->desc->properties = __cpu_to_le32(entry->properties);
1107403f69bbSGerhard Engleder }
1108403f69bbSGerhard Engleder
tsnep_rx_alloc(struct tsnep_rx * rx,int count,bool reuse)11092ea0a282SGerhard Engleder static int tsnep_rx_alloc(struct tsnep_rx *rx, int count, bool reuse)
1110dbadae92SGerhard Engleder {
1111dbadae92SGerhard Engleder bool alloc_failed = false;
11122ea0a282SGerhard Engleder int i, index;
1113dbadae92SGerhard Engleder
1114dbadae92SGerhard Engleder for (i = 0; i < count && !alloc_failed; i++) {
111542fb2962SGerhard Engleder index = (rx->write + i) & TSNEP_RING_MASK;
1116dbadae92SGerhard Engleder
11172ea0a282SGerhard Engleder if (unlikely(tsnep_rx_alloc_buffer(rx, index))) {
1118dbadae92SGerhard Engleder rx->alloc_failed++;
1119dbadae92SGerhard Engleder alloc_failed = true;
1120dbadae92SGerhard Engleder
1121dbadae92SGerhard Engleder /* reuse only if no other allocation was successful */
1122dbadae92SGerhard Engleder if (i == 0 && reuse)
1123dbadae92SGerhard Engleder tsnep_rx_reuse_buffer(rx, index);
1124dbadae92SGerhard Engleder else
1125dbadae92SGerhard Engleder break;
1126dbadae92SGerhard Engleder }
1127dbadae92SGerhard Engleder
1128dbadae92SGerhard Engleder tsnep_rx_activate(rx, index);
1129dbadae92SGerhard Engleder }
1130dbadae92SGerhard Engleder
11312ea0a282SGerhard Engleder if (i)
113242fb2962SGerhard Engleder rx->write = (rx->write + i) & TSNEP_RING_MASK;
1133dbadae92SGerhard Engleder
11342ea0a282SGerhard Engleder return i;
1135dbadae92SGerhard Engleder }
1136dbadae92SGerhard Engleder
tsnep_rx_refill(struct tsnep_rx * rx,int count,bool reuse)11372ea0a282SGerhard Engleder static int tsnep_rx_refill(struct tsnep_rx *rx, int count, bool reuse)
11382ea0a282SGerhard Engleder {
11392ea0a282SGerhard Engleder int desc_refilled;
11402ea0a282SGerhard Engleder
11412ea0a282SGerhard Engleder desc_refilled = tsnep_rx_alloc(rx, count, reuse);
11422ea0a282SGerhard Engleder if (desc_refilled)
11432ea0a282SGerhard Engleder tsnep_rx_enable(rx);
11442ea0a282SGerhard Engleder
11452ea0a282SGerhard Engleder return desc_refilled;
1146dbadae92SGerhard Engleder }
1147dbadae92SGerhard Engleder
tsnep_rx_set_xdp(struct tsnep_rx * rx,struct tsnep_rx_entry * entry,struct xdp_buff * xdp)11483fc23339SGerhard Engleder static void tsnep_rx_set_xdp(struct tsnep_rx *rx, struct tsnep_rx_entry *entry,
11493fc23339SGerhard Engleder struct xdp_buff *xdp)
11503fc23339SGerhard Engleder {
11513fc23339SGerhard Engleder entry->xdp = xdp;
11523fc23339SGerhard Engleder entry->len = TSNEP_XSK_RX_BUF_SIZE;
11533fc23339SGerhard Engleder entry->dma = xsk_buff_xdp_get_dma(entry->xdp);
11543fc23339SGerhard Engleder entry->desc->rx = __cpu_to_le64(entry->dma);
11553fc23339SGerhard Engleder }
11563fc23339SGerhard Engleder
tsnep_rx_reuse_buffer_zc(struct tsnep_rx * rx,int index)11573fc23339SGerhard Engleder static void tsnep_rx_reuse_buffer_zc(struct tsnep_rx *rx, int index)
11583fc23339SGerhard Engleder {
11593fc23339SGerhard Engleder struct tsnep_rx_entry *entry = &rx->entry[index];
11603fc23339SGerhard Engleder struct tsnep_rx_entry *read = &rx->entry[rx->read];
11613fc23339SGerhard Engleder
11623fc23339SGerhard Engleder tsnep_rx_set_xdp(rx, entry, read->xdp);
11633fc23339SGerhard Engleder read->xdp = NULL;
11643fc23339SGerhard Engleder }
11653fc23339SGerhard Engleder
tsnep_rx_alloc_zc(struct tsnep_rx * rx,int count,bool reuse)11663fc23339SGerhard Engleder static int tsnep_rx_alloc_zc(struct tsnep_rx *rx, int count, bool reuse)
11673fc23339SGerhard Engleder {
11683fc23339SGerhard Engleder u32 allocated;
11693fc23339SGerhard Engleder int i;
11703fc23339SGerhard Engleder
11713fc23339SGerhard Engleder allocated = xsk_buff_alloc_batch(rx->xsk_pool, rx->xdp_batch, count);
11723fc23339SGerhard Engleder for (i = 0; i < allocated; i++) {
11733fc23339SGerhard Engleder int index = (rx->write + i) & TSNEP_RING_MASK;
11743fc23339SGerhard Engleder struct tsnep_rx_entry *entry = &rx->entry[index];
11753fc23339SGerhard Engleder
11763fc23339SGerhard Engleder tsnep_rx_set_xdp(rx, entry, rx->xdp_batch[i]);
11773fc23339SGerhard Engleder tsnep_rx_activate(rx, index);
11783fc23339SGerhard Engleder }
11793fc23339SGerhard Engleder if (i == 0) {
11803fc23339SGerhard Engleder rx->alloc_failed++;
11813fc23339SGerhard Engleder
11823fc23339SGerhard Engleder if (reuse) {
11833fc23339SGerhard Engleder tsnep_rx_reuse_buffer_zc(rx, rx->write);
11843fc23339SGerhard Engleder tsnep_rx_activate(rx, rx->write);
11853fc23339SGerhard Engleder }
11863fc23339SGerhard Engleder }
11873fc23339SGerhard Engleder
11883fc23339SGerhard Engleder if (i)
11893fc23339SGerhard Engleder rx->write = (rx->write + i) & TSNEP_RING_MASK;
11903fc23339SGerhard Engleder
11913fc23339SGerhard Engleder return i;
11923fc23339SGerhard Engleder }
11933fc23339SGerhard Engleder
tsnep_rx_free_zc(struct tsnep_rx * rx)11943fc23339SGerhard Engleder static void tsnep_rx_free_zc(struct tsnep_rx *rx)
11953fc23339SGerhard Engleder {
11963fc23339SGerhard Engleder int i;
11973fc23339SGerhard Engleder
11983fc23339SGerhard Engleder for (i = 0; i < TSNEP_RING_SIZE; i++) {
11993fc23339SGerhard Engleder struct tsnep_rx_entry *entry = &rx->entry[i];
12003fc23339SGerhard Engleder
12013fc23339SGerhard Engleder if (entry->xdp)
12023fc23339SGerhard Engleder xsk_buff_free(entry->xdp);
12033fc23339SGerhard Engleder entry->xdp = NULL;
12043fc23339SGerhard Engleder }
12053fc23339SGerhard Engleder }
12063fc23339SGerhard Engleder
tsnep_rx_refill_zc(struct tsnep_rx * rx,int count,bool reuse)12073fc23339SGerhard Engleder static int tsnep_rx_refill_zc(struct tsnep_rx *rx, int count, bool reuse)
12083fc23339SGerhard Engleder {
12093fc23339SGerhard Engleder int desc_refilled;
12103fc23339SGerhard Engleder
12113fc23339SGerhard Engleder desc_refilled = tsnep_rx_alloc_zc(rx, count, reuse);
12123fc23339SGerhard Engleder if (desc_refilled)
12133fc23339SGerhard Engleder tsnep_rx_enable(rx);
12143fc23339SGerhard Engleder
12153fc23339SGerhard Engleder return desc_refilled;
12163fc23339SGerhard Engleder }
12173fc23339SGerhard Engleder
tsnep_xdp_run_prog(struct tsnep_rx * rx,struct bpf_prog * prog,struct xdp_buff * xdp,int * status,struct netdev_queue * tx_nq,struct tsnep_tx * tx)121865b28c81SGerhard Engleder static bool tsnep_xdp_run_prog(struct tsnep_rx *rx, struct bpf_prog *prog,
121965b28c81SGerhard Engleder struct xdp_buff *xdp, int *status,
122065b28c81SGerhard Engleder struct netdev_queue *tx_nq, struct tsnep_tx *tx)
122165b28c81SGerhard Engleder {
122265b28c81SGerhard Engleder unsigned int length;
122365b28c81SGerhard Engleder unsigned int sync;
122465b28c81SGerhard Engleder u32 act;
122565b28c81SGerhard Engleder
122665b28c81SGerhard Engleder length = xdp->data_end - xdp->data_hard_start - XDP_PACKET_HEADROOM;
122765b28c81SGerhard Engleder
122865b28c81SGerhard Engleder act = bpf_prog_run_xdp(prog, xdp);
122965b28c81SGerhard Engleder switch (act) {
123065b28c81SGerhard Engleder case XDP_PASS:
123165b28c81SGerhard Engleder return false;
123265b28c81SGerhard Engleder case XDP_TX:
1233*10db3a7eSGerhard Engleder if (!tsnep_xdp_xmit_back(rx->adapter, xdp, tx_nq, tx, false))
123465b28c81SGerhard Engleder goto out_failure;
123565b28c81SGerhard Engleder *status |= TSNEP_XDP_TX;
123665b28c81SGerhard Engleder return true;
123765b28c81SGerhard Engleder case XDP_REDIRECT:
123865b28c81SGerhard Engleder if (xdp_do_redirect(rx->adapter->netdev, xdp, prog) < 0)
123965b28c81SGerhard Engleder goto out_failure;
124065b28c81SGerhard Engleder *status |= TSNEP_XDP_REDIRECT;
124165b28c81SGerhard Engleder return true;
124265b28c81SGerhard Engleder default:
124365b28c81SGerhard Engleder bpf_warn_invalid_xdp_action(rx->adapter->netdev, prog, act);
124465b28c81SGerhard Engleder fallthrough;
124565b28c81SGerhard Engleder case XDP_ABORTED:
124665b28c81SGerhard Engleder out_failure:
124765b28c81SGerhard Engleder trace_xdp_exception(rx->adapter->netdev, prog, act);
124865b28c81SGerhard Engleder fallthrough;
124965b28c81SGerhard Engleder case XDP_DROP:
12503fc23339SGerhard Engleder /* Due xdp_adjust_tail: DMA sync for_device cover max len CPU
12513fc23339SGerhard Engleder * touch
12523fc23339SGerhard Engleder */
12533fc23339SGerhard Engleder sync = xdp->data_end - xdp->data_hard_start -
12543fc23339SGerhard Engleder XDP_PACKET_HEADROOM;
12553fc23339SGerhard Engleder sync = max(sync, length);
125665b28c81SGerhard Engleder page_pool_put_page(rx->page_pool, virt_to_head_page(xdp->data),
125765b28c81SGerhard Engleder sync, true);
125865b28c81SGerhard Engleder return true;
125965b28c81SGerhard Engleder }
126065b28c81SGerhard Engleder }
126165b28c81SGerhard Engleder
tsnep_xdp_run_prog_zc(struct tsnep_rx * rx,struct bpf_prog * prog,struct xdp_buff * xdp,int * status,struct netdev_queue * tx_nq,struct tsnep_tx * tx)12623fc23339SGerhard Engleder static bool tsnep_xdp_run_prog_zc(struct tsnep_rx *rx, struct bpf_prog *prog,
12633fc23339SGerhard Engleder struct xdp_buff *xdp, int *status,
12643fc23339SGerhard Engleder struct netdev_queue *tx_nq,
12653fc23339SGerhard Engleder struct tsnep_tx *tx)
12663fc23339SGerhard Engleder {
12673fc23339SGerhard Engleder u32 act;
12683fc23339SGerhard Engleder
12693fc23339SGerhard Engleder act = bpf_prog_run_xdp(prog, xdp);
12703fc23339SGerhard Engleder
12713fc23339SGerhard Engleder /* XDP_REDIRECT is the main action for zero-copy */
12723fc23339SGerhard Engleder if (likely(act == XDP_REDIRECT)) {
12733fc23339SGerhard Engleder if (xdp_do_redirect(rx->adapter->netdev, xdp, prog) < 0)
12743fc23339SGerhard Engleder goto out_failure;
12753fc23339SGerhard Engleder *status |= TSNEP_XDP_REDIRECT;
12763fc23339SGerhard Engleder return true;
12773fc23339SGerhard Engleder }
12783fc23339SGerhard Engleder
12793fc23339SGerhard Engleder switch (act) {
12803fc23339SGerhard Engleder case XDP_PASS:
12813fc23339SGerhard Engleder return false;
12823fc23339SGerhard Engleder case XDP_TX:
1283*10db3a7eSGerhard Engleder if (!tsnep_xdp_xmit_back(rx->adapter, xdp, tx_nq, tx, true))
12843fc23339SGerhard Engleder goto out_failure;
12853fc23339SGerhard Engleder *status |= TSNEP_XDP_TX;
12863fc23339SGerhard Engleder return true;
12873fc23339SGerhard Engleder default:
12883fc23339SGerhard Engleder bpf_warn_invalid_xdp_action(rx->adapter->netdev, prog, act);
12893fc23339SGerhard Engleder fallthrough;
12903fc23339SGerhard Engleder case XDP_ABORTED:
12913fc23339SGerhard Engleder out_failure:
12923fc23339SGerhard Engleder trace_xdp_exception(rx->adapter->netdev, prog, act);
12933fc23339SGerhard Engleder fallthrough;
12943fc23339SGerhard Engleder case XDP_DROP:
12953fc23339SGerhard Engleder xsk_buff_free(xdp);
12963fc23339SGerhard Engleder return true;
12973fc23339SGerhard Engleder }
12983fc23339SGerhard Engleder }
12993fc23339SGerhard Engleder
tsnep_finalize_xdp(struct tsnep_adapter * adapter,int status,struct netdev_queue * tx_nq,struct tsnep_tx * tx)130065b28c81SGerhard Engleder static void tsnep_finalize_xdp(struct tsnep_adapter *adapter, int status,
130165b28c81SGerhard Engleder struct netdev_queue *tx_nq, struct tsnep_tx *tx)
130265b28c81SGerhard Engleder {
130365b28c81SGerhard Engleder if (status & TSNEP_XDP_TX) {
130465b28c81SGerhard Engleder __netif_tx_lock(tx_nq, smp_processor_id());
130565b28c81SGerhard Engleder tsnep_xdp_xmit_flush(tx);
130665b28c81SGerhard Engleder __netif_tx_unlock(tx_nq);
130765b28c81SGerhard Engleder }
130865b28c81SGerhard Engleder
130965b28c81SGerhard Engleder if (status & TSNEP_XDP_REDIRECT)
131065b28c81SGerhard Engleder xdp_do_flush();
131165b28c81SGerhard Engleder }
131265b28c81SGerhard Engleder
tsnep_build_skb(struct tsnep_rx * rx,struct page * page,int length)1313bb837a37SGerhard Engleder static struct sk_buff *tsnep_build_skb(struct tsnep_rx *rx, struct page *page,
1314bb837a37SGerhard Engleder int length)
1315bb837a37SGerhard Engleder {
1316bb837a37SGerhard Engleder struct sk_buff *skb;
1317bb837a37SGerhard Engleder
1318bb837a37SGerhard Engleder skb = napi_build_skb(page_address(page), PAGE_SIZE);
1319bb837a37SGerhard Engleder if (unlikely(!skb))
1320bb837a37SGerhard Engleder return NULL;
1321bb837a37SGerhard Engleder
1322bb837a37SGerhard Engleder /* update pointers within the skb to store the data */
1323cc3e254fSGerhard Engleder skb_reserve(skb, TSNEP_RX_OFFSET + TSNEP_RX_INLINE_METADATA_SIZE);
132459d562aaSGerhard Engleder __skb_put(skb, length - ETH_FCS_LEN);
1325bb837a37SGerhard Engleder
1326bb837a37SGerhard Engleder if (rx->adapter->hwtstamp_config.rx_filter == HWTSTAMP_FILTER_ALL) {
1327bb837a37SGerhard Engleder struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb);
1328bb837a37SGerhard Engleder struct tsnep_rx_inline *rx_inline =
1329bb837a37SGerhard Engleder (struct tsnep_rx_inline *)(page_address(page) +
1330cc3e254fSGerhard Engleder TSNEP_RX_OFFSET);
1331bb837a37SGerhard Engleder
1332bb837a37SGerhard Engleder skb_shinfo(skb)->tx_flags |=
1333bb837a37SGerhard Engleder SKBTX_HW_TSTAMP_NETDEV;
1334bb837a37SGerhard Engleder memset(hwtstamps, 0, sizeof(*hwtstamps));
1335bb837a37SGerhard Engleder hwtstamps->netdev_data = rx_inline;
1336bb837a37SGerhard Engleder }
1337bb837a37SGerhard Engleder
1338bb837a37SGerhard Engleder skb_record_rx_queue(skb, rx->queue_index);
1339bb837a37SGerhard Engleder skb->protocol = eth_type_trans(skb, rx->adapter->netdev);
1340bb837a37SGerhard Engleder
1341bb837a37SGerhard Engleder return skb;
1342bb837a37SGerhard Engleder }
1343bb837a37SGerhard Engleder
tsnep_rx_page(struct tsnep_rx * rx,struct napi_struct * napi,struct page * page,int length)1344c2d64697SGerhard Engleder static void tsnep_rx_page(struct tsnep_rx *rx, struct napi_struct *napi,
1345c2d64697SGerhard Engleder struct page *page, int length)
1346c2d64697SGerhard Engleder {
1347c2d64697SGerhard Engleder struct sk_buff *skb;
1348c2d64697SGerhard Engleder
1349c2d64697SGerhard Engleder skb = tsnep_build_skb(rx, page, length);
1350c2d64697SGerhard Engleder if (skb) {
1351b03f68baSJakub Kicinski skb_mark_for_recycle(skb);
1352c2d64697SGerhard Engleder
1353c2d64697SGerhard Engleder rx->packets++;
1354c2d64697SGerhard Engleder rx->bytes += length;
1355c2d64697SGerhard Engleder if (skb->pkt_type == PACKET_MULTICAST)
1356c2d64697SGerhard Engleder rx->multicast++;
1357c2d64697SGerhard Engleder
1358c2d64697SGerhard Engleder napi_gro_receive(napi, skb);
1359c2d64697SGerhard Engleder } else {
1360c2d64697SGerhard Engleder page_pool_recycle_direct(rx->page_pool, page);
1361c2d64697SGerhard Engleder
1362c2d64697SGerhard Engleder rx->dropped++;
1363c2d64697SGerhard Engleder }
1364c2d64697SGerhard Engleder }
1365c2d64697SGerhard Engleder
tsnep_rx_poll(struct tsnep_rx * rx,struct napi_struct * napi,int budget)1366403f69bbSGerhard Engleder static int tsnep_rx_poll(struct tsnep_rx *rx, struct napi_struct *napi,
1367403f69bbSGerhard Engleder int budget)
1368403f69bbSGerhard Engleder {
1369403f69bbSGerhard Engleder struct device *dmadev = rx->adapter->dmadev;
1370bb837a37SGerhard Engleder enum dma_data_direction dma_dir;
1371403f69bbSGerhard Engleder struct tsnep_rx_entry *entry;
137265b28c81SGerhard Engleder struct netdev_queue *tx_nq;
137365b28c81SGerhard Engleder struct bpf_prog *prog;
137465b28c81SGerhard Engleder struct xdp_buff xdp;
137565b28c81SGerhard Engleder struct tsnep_tx *tx;
137665b28c81SGerhard Engleder int desc_available;
137765b28c81SGerhard Engleder int xdp_status = 0;
137865b28c81SGerhard Engleder int done = 0;
1379403f69bbSGerhard Engleder int length;
1380403f69bbSGerhard Engleder
1381dbadae92SGerhard Engleder desc_available = tsnep_rx_desc_available(rx);
1382bb837a37SGerhard Engleder dma_dir = page_pool_get_dma_dir(rx->page_pool);
138365b28c81SGerhard Engleder prog = READ_ONCE(rx->adapter->xdp_prog);
138465b28c81SGerhard Engleder if (prog) {
138565b28c81SGerhard Engleder tx_nq = netdev_get_tx_queue(rx->adapter->netdev,
138665b28c81SGerhard Engleder rx->tx_queue_index);
138765b28c81SGerhard Engleder tx = &rx->adapter->tx[rx->tx_queue_index];
138865b28c81SGerhard Engleder
138965b28c81SGerhard Engleder xdp_init_buff(&xdp, PAGE_SIZE, &rx->xdp_rxq);
139065b28c81SGerhard Engleder }
1391bb837a37SGerhard Engleder
1392dbadae92SGerhard Engleder while (likely(done < budget) && (rx->read != rx->write)) {
1393403f69bbSGerhard Engleder entry = &rx->entry[rx->read];
1394403f69bbSGerhard Engleder if ((__le32_to_cpu(entry->desc_wb->properties) &
1395403f69bbSGerhard Engleder TSNEP_DESC_OWNER_COUNTER_MASK) !=
1396403f69bbSGerhard Engleder (entry->properties & TSNEP_DESC_OWNER_COUNTER_MASK))
1397403f69bbSGerhard Engleder break;
1398dbadae92SGerhard Engleder done++;
1399dbadae92SGerhard Engleder
1400dbadae92SGerhard Engleder if (desc_available >= TSNEP_RING_RX_REFILL) {
1401dbadae92SGerhard Engleder bool reuse = desc_available >= TSNEP_RING_RX_REUSE;
1402dbadae92SGerhard Engleder
1403dbadae92SGerhard Engleder desc_available -= tsnep_rx_refill(rx, desc_available,
1404dbadae92SGerhard Engleder reuse);
1405dbadae92SGerhard Engleder if (!entry->page) {
1406dbadae92SGerhard Engleder /* buffer has been reused for refill to prevent
1407dbadae92SGerhard Engleder * empty RX ring, thus buffer cannot be used for
1408dbadae92SGerhard Engleder * RX processing
1409dbadae92SGerhard Engleder */
141042fb2962SGerhard Engleder rx->read = (rx->read + 1) & TSNEP_RING_MASK;
1411dbadae92SGerhard Engleder desc_available++;
1412dbadae92SGerhard Engleder
1413dbadae92SGerhard Engleder rx->dropped++;
1414dbadae92SGerhard Engleder
1415dbadae92SGerhard Engleder continue;
1416dbadae92SGerhard Engleder }
1417dbadae92SGerhard Engleder }
1418403f69bbSGerhard Engleder
1419403f69bbSGerhard Engleder /* descriptor properties shall be read first, because valid data
1420403f69bbSGerhard Engleder * is signaled there
1421403f69bbSGerhard Engleder */
1422403f69bbSGerhard Engleder dma_rmb();
1423403f69bbSGerhard Engleder
1424cc3e254fSGerhard Engleder prefetch(page_address(entry->page) + TSNEP_RX_OFFSET);
1425403f69bbSGerhard Engleder length = __le32_to_cpu(entry->desc_wb->properties) &
1426403f69bbSGerhard Engleder TSNEP_DESC_LENGTH_MASK;
1427cc3e254fSGerhard Engleder dma_sync_single_range_for_cpu(dmadev, entry->dma,
1428cc3e254fSGerhard Engleder TSNEP_RX_OFFSET, length, dma_dir);
1429403f69bbSGerhard Engleder
143059d562aaSGerhard Engleder /* RX metadata with timestamps is in front of actual data,
143159d562aaSGerhard Engleder * subtract metadata size to get length of actual data and
143259d562aaSGerhard Engleder * consider metadata size as offset of actual data during RX
143359d562aaSGerhard Engleder * processing
143459d562aaSGerhard Engleder */
143559d562aaSGerhard Engleder length -= TSNEP_RX_INLINE_METADATA_SIZE;
143659d562aaSGerhard Engleder
143742fb2962SGerhard Engleder rx->read = (rx->read + 1) & TSNEP_RING_MASK;
1438dbadae92SGerhard Engleder desc_available++;
1439dbadae92SGerhard Engleder
144065b28c81SGerhard Engleder if (prog) {
144165b28c81SGerhard Engleder bool consume;
144265b28c81SGerhard Engleder
144365b28c81SGerhard Engleder xdp_prepare_buff(&xdp, page_address(entry->page),
144465b28c81SGerhard Engleder XDP_PACKET_HEADROOM + TSNEP_RX_INLINE_METADATA_SIZE,
14453fc1e534SGerhard Engleder length - ETH_FCS_LEN, false);
144665b28c81SGerhard Engleder
144765b28c81SGerhard Engleder consume = tsnep_xdp_run_prog(rx, prog, &xdp,
144865b28c81SGerhard Engleder &xdp_status, tx_nq, tx);
144965b28c81SGerhard Engleder if (consume) {
145065b28c81SGerhard Engleder rx->packets++;
145165b28c81SGerhard Engleder rx->bytes += length;
145265b28c81SGerhard Engleder
145365b28c81SGerhard Engleder entry->page = NULL;
145465b28c81SGerhard Engleder
145565b28c81SGerhard Engleder continue;
145665b28c81SGerhard Engleder }
145765b28c81SGerhard Engleder }
145865b28c81SGerhard Engleder
1459c2d64697SGerhard Engleder tsnep_rx_page(rx, napi, entry->page, length);
1460dbadae92SGerhard Engleder entry->page = NULL;
1461403f69bbSGerhard Engleder }
1462403f69bbSGerhard Engleder
146365b28c81SGerhard Engleder if (xdp_status)
146465b28c81SGerhard Engleder tsnep_finalize_xdp(rx->adapter, xdp_status, tx_nq, tx);
146565b28c81SGerhard Engleder
1466dbadae92SGerhard Engleder if (desc_available)
1467dbadae92SGerhard Engleder tsnep_rx_refill(rx, desc_available, false);
1468403f69bbSGerhard Engleder
1469403f69bbSGerhard Engleder return done;
1470403f69bbSGerhard Engleder }
1471403f69bbSGerhard Engleder
tsnep_rx_poll_zc(struct tsnep_rx * rx,struct napi_struct * napi,int budget)14723fc23339SGerhard Engleder static int tsnep_rx_poll_zc(struct tsnep_rx *rx, struct napi_struct *napi,
14733fc23339SGerhard Engleder int budget)
14743fc23339SGerhard Engleder {
14753fc23339SGerhard Engleder struct tsnep_rx_entry *entry;
14763fc23339SGerhard Engleder struct netdev_queue *tx_nq;
14773fc23339SGerhard Engleder struct bpf_prog *prog;
14783fc23339SGerhard Engleder struct tsnep_tx *tx;
14793fc23339SGerhard Engleder int desc_available;
14803fc23339SGerhard Engleder int xdp_status = 0;
14813fc23339SGerhard Engleder struct page *page;
14823fc23339SGerhard Engleder int done = 0;
14833fc23339SGerhard Engleder int length;
14843fc23339SGerhard Engleder
14853fc23339SGerhard Engleder desc_available = tsnep_rx_desc_available(rx);
14863fc23339SGerhard Engleder prog = READ_ONCE(rx->adapter->xdp_prog);
14873fc23339SGerhard Engleder if (prog) {
14883fc23339SGerhard Engleder tx_nq = netdev_get_tx_queue(rx->adapter->netdev,
14893fc23339SGerhard Engleder rx->tx_queue_index);
14903fc23339SGerhard Engleder tx = &rx->adapter->tx[rx->tx_queue_index];
14913fc23339SGerhard Engleder }
14923fc23339SGerhard Engleder
14933fc23339SGerhard Engleder while (likely(done < budget) && (rx->read != rx->write)) {
14943fc23339SGerhard Engleder entry = &rx->entry[rx->read];
14953fc23339SGerhard Engleder if ((__le32_to_cpu(entry->desc_wb->properties) &
14963fc23339SGerhard Engleder TSNEP_DESC_OWNER_COUNTER_MASK) !=
14973fc23339SGerhard Engleder (entry->properties & TSNEP_DESC_OWNER_COUNTER_MASK))
14983fc23339SGerhard Engleder break;
14993fc23339SGerhard Engleder done++;
15003fc23339SGerhard Engleder
15013fc23339SGerhard Engleder if (desc_available >= TSNEP_RING_RX_REFILL) {
15023fc23339SGerhard Engleder bool reuse = desc_available >= TSNEP_RING_RX_REUSE;
15033fc23339SGerhard Engleder
15043fc23339SGerhard Engleder desc_available -= tsnep_rx_refill_zc(rx, desc_available,
15053fc23339SGerhard Engleder reuse);
15063fc23339SGerhard Engleder if (!entry->xdp) {
15073fc23339SGerhard Engleder /* buffer has been reused for refill to prevent
15083fc23339SGerhard Engleder * empty RX ring, thus buffer cannot be used for
15093fc23339SGerhard Engleder * RX processing
15103fc23339SGerhard Engleder */
15113fc23339SGerhard Engleder rx->read = (rx->read + 1) & TSNEP_RING_MASK;
15123fc23339SGerhard Engleder desc_available++;
15133fc23339SGerhard Engleder
15143fc23339SGerhard Engleder rx->dropped++;
15153fc23339SGerhard Engleder
15163fc23339SGerhard Engleder continue;
15173fc23339SGerhard Engleder }
15183fc23339SGerhard Engleder }
15193fc23339SGerhard Engleder
15203fc23339SGerhard Engleder /* descriptor properties shall be read first, because valid data
15213fc23339SGerhard Engleder * is signaled there
15223fc23339SGerhard Engleder */
15233fc23339SGerhard Engleder dma_rmb();
15243fc23339SGerhard Engleder
15253fc23339SGerhard Engleder prefetch(entry->xdp->data);
15263fc23339SGerhard Engleder length = __le32_to_cpu(entry->desc_wb->properties) &
15273fc23339SGerhard Engleder TSNEP_DESC_LENGTH_MASK;
15283fc1e534SGerhard Engleder xsk_buff_set_size(entry->xdp, length - ETH_FCS_LEN);
15293fc23339SGerhard Engleder xsk_buff_dma_sync_for_cpu(entry->xdp, rx->xsk_pool);
15303fc23339SGerhard Engleder
15313fc23339SGerhard Engleder /* RX metadata with timestamps is in front of actual data,
15323fc23339SGerhard Engleder * subtract metadata size to get length of actual data and
15333fc23339SGerhard Engleder * consider metadata size as offset of actual data during RX
15343fc23339SGerhard Engleder * processing
15353fc23339SGerhard Engleder */
15363fc23339SGerhard Engleder length -= TSNEP_RX_INLINE_METADATA_SIZE;
15373fc23339SGerhard Engleder
15383fc23339SGerhard Engleder rx->read = (rx->read + 1) & TSNEP_RING_MASK;
15393fc23339SGerhard Engleder desc_available++;
15403fc23339SGerhard Engleder
15413fc23339SGerhard Engleder if (prog) {
15423fc23339SGerhard Engleder bool consume;
15433fc23339SGerhard Engleder
15443fc23339SGerhard Engleder entry->xdp->data += TSNEP_RX_INLINE_METADATA_SIZE;
15453fc23339SGerhard Engleder entry->xdp->data_meta += TSNEP_RX_INLINE_METADATA_SIZE;
15463fc23339SGerhard Engleder
15473fc23339SGerhard Engleder consume = tsnep_xdp_run_prog_zc(rx, prog, entry->xdp,
15483fc23339SGerhard Engleder &xdp_status, tx_nq, tx);
15493fc23339SGerhard Engleder if (consume) {
15503fc23339SGerhard Engleder rx->packets++;
15513fc23339SGerhard Engleder rx->bytes += length;
15523fc23339SGerhard Engleder
15533fc23339SGerhard Engleder entry->xdp = NULL;
15543fc23339SGerhard Engleder
15553fc23339SGerhard Engleder continue;
15563fc23339SGerhard Engleder }
15573fc23339SGerhard Engleder }
15583fc23339SGerhard Engleder
15593fc23339SGerhard Engleder page = page_pool_dev_alloc_pages(rx->page_pool);
15603fc23339SGerhard Engleder if (page) {
15613fc23339SGerhard Engleder memcpy(page_address(page) + TSNEP_RX_OFFSET,
15623fc23339SGerhard Engleder entry->xdp->data - TSNEP_RX_INLINE_METADATA_SIZE,
15633fc23339SGerhard Engleder length + TSNEP_RX_INLINE_METADATA_SIZE);
15643fc23339SGerhard Engleder tsnep_rx_page(rx, napi, page, length);
15653fc23339SGerhard Engleder } else {
15663fc23339SGerhard Engleder rx->dropped++;
15673fc23339SGerhard Engleder }
15683fc23339SGerhard Engleder xsk_buff_free(entry->xdp);
15693fc23339SGerhard Engleder entry->xdp = NULL;
15703fc23339SGerhard Engleder }
15713fc23339SGerhard Engleder
15723fc23339SGerhard Engleder if (xdp_status)
15733fc23339SGerhard Engleder tsnep_finalize_xdp(rx->adapter, xdp_status, tx_nq, tx);
15743fc23339SGerhard Engleder
15753fc23339SGerhard Engleder if (desc_available)
15763fc23339SGerhard Engleder desc_available -= tsnep_rx_refill_zc(rx, desc_available, false);
15773fc23339SGerhard Engleder
15783fc23339SGerhard Engleder if (xsk_uses_need_wakeup(rx->xsk_pool)) {
15793fc23339SGerhard Engleder if (desc_available)
15803fc23339SGerhard Engleder xsk_set_rx_need_wakeup(rx->xsk_pool);
15813fc23339SGerhard Engleder else
15823fc23339SGerhard Engleder xsk_clear_rx_need_wakeup(rx->xsk_pool);
15833fc23339SGerhard Engleder
15843fc23339SGerhard Engleder return done;
15853fc23339SGerhard Engleder }
15863fc23339SGerhard Engleder
15873fc23339SGerhard Engleder return desc_available ? budget : done;
15883fc23339SGerhard Engleder }
15893fc23339SGerhard Engleder
tsnep_rx_pending(struct tsnep_rx * rx)15902dc4ac91SGerhard Engleder static bool tsnep_rx_pending(struct tsnep_rx *rx)
15912dc4ac91SGerhard Engleder {
15922dc4ac91SGerhard Engleder struct tsnep_rx_entry *entry;
15932dc4ac91SGerhard Engleder
1594dbadae92SGerhard Engleder if (rx->read != rx->write) {
15952dc4ac91SGerhard Engleder entry = &rx->entry[rx->read];
15962dc4ac91SGerhard Engleder if ((__le32_to_cpu(entry->desc_wb->properties) &
15972dc4ac91SGerhard Engleder TSNEP_DESC_OWNER_COUNTER_MASK) ==
15982dc4ac91SGerhard Engleder (entry->properties & TSNEP_DESC_OWNER_COUNTER_MASK))
15992dc4ac91SGerhard Engleder return true;
1600dbadae92SGerhard Engleder }
16012dc4ac91SGerhard Engleder
16022dc4ac91SGerhard Engleder return false;
16032dc4ac91SGerhard Engleder }
16042dc4ac91SGerhard Engleder
tsnep_rx_open(struct tsnep_rx * rx)160533b0ee02SGerhard Engleder static int tsnep_rx_open(struct tsnep_rx *rx)
1606403f69bbSGerhard Engleder {
16072ea0a282SGerhard Engleder int desc_available;
1608403f69bbSGerhard Engleder int retval;
1609403f69bbSGerhard Engleder
161033b0ee02SGerhard Engleder retval = tsnep_rx_ring_create(rx);
1611403f69bbSGerhard Engleder if (retval)
1612403f69bbSGerhard Engleder return retval;
1613403f69bbSGerhard Engleder
161433b0ee02SGerhard Engleder tsnep_rx_init(rx);
1615403f69bbSGerhard Engleder
16162ea0a282SGerhard Engleder desc_available = tsnep_rx_desc_available(rx);
16173fc23339SGerhard Engleder if (rx->xsk_pool)
16183fc23339SGerhard Engleder retval = tsnep_rx_alloc_zc(rx, desc_available, false);
16193fc23339SGerhard Engleder else
16202ea0a282SGerhard Engleder retval = tsnep_rx_alloc(rx, desc_available, false);
16212ea0a282SGerhard Engleder if (retval != desc_available) {
16223fc23339SGerhard Engleder retval = -ENOMEM;
16232ea0a282SGerhard Engleder
16243fc23339SGerhard Engleder goto alloc_failed;
16253fc23339SGerhard Engleder }
16263fc23339SGerhard Engleder
16273fc23339SGerhard Engleder /* prealloc pages to prevent allocation failures when XSK pool is
16283fc23339SGerhard Engleder * disabled at runtime
16293fc23339SGerhard Engleder */
16303fc23339SGerhard Engleder if (rx->xsk_pool) {
16313fc23339SGerhard Engleder retval = tsnep_rx_alloc_page_buffer(rx);
16323fc23339SGerhard Engleder if (retval)
16333fc23339SGerhard Engleder goto alloc_failed;
16342ea0a282SGerhard Engleder }
1635403f69bbSGerhard Engleder
1636403f69bbSGerhard Engleder return 0;
16373fc23339SGerhard Engleder
16383fc23339SGerhard Engleder alloc_failed:
16393fc23339SGerhard Engleder tsnep_rx_ring_cleanup(rx);
16403fc23339SGerhard Engleder return retval;
1641403f69bbSGerhard Engleder }
1642403f69bbSGerhard Engleder
tsnep_rx_close(struct tsnep_rx * rx)1643403f69bbSGerhard Engleder static void tsnep_rx_close(struct tsnep_rx *rx)
1644403f69bbSGerhard Engleder {
16453fc23339SGerhard Engleder if (rx->xsk_pool)
16463fc23339SGerhard Engleder tsnep_rx_free_page_buffer(rx);
16473fc23339SGerhard Engleder
1648403f69bbSGerhard Engleder tsnep_rx_ring_cleanup(rx);
1649403f69bbSGerhard Engleder }
1650403f69bbSGerhard Engleder
tsnep_rx_reopen(struct tsnep_rx * rx)16513fc23339SGerhard Engleder static void tsnep_rx_reopen(struct tsnep_rx *rx)
16523fc23339SGerhard Engleder {
16533fc23339SGerhard Engleder struct page **page = rx->page_buffer;
16543fc23339SGerhard Engleder int i;
16553fc23339SGerhard Engleder
16563fc23339SGerhard Engleder tsnep_rx_init(rx);
16573fc23339SGerhard Engleder
16583fc23339SGerhard Engleder for (i = 0; i < TSNEP_RING_SIZE; i++) {
16593fc23339SGerhard Engleder struct tsnep_rx_entry *entry = &rx->entry[i];
16603fc23339SGerhard Engleder
16613fc23339SGerhard Engleder /* defined initial values for properties are required for
16623fc23339SGerhard Engleder * correct owner counter checking
16633fc23339SGerhard Engleder */
16643fc23339SGerhard Engleder entry->desc->properties = 0;
16653fc23339SGerhard Engleder entry->desc_wb->properties = 0;
16663fc23339SGerhard Engleder
16673fc23339SGerhard Engleder /* prevent allocation failures by reusing kept pages */
16683fc23339SGerhard Engleder if (*page) {
16693fc23339SGerhard Engleder tsnep_rx_set_page(rx, entry, *page);
16703fc23339SGerhard Engleder tsnep_rx_activate(rx, rx->write);
16713fc23339SGerhard Engleder rx->write++;
16723fc23339SGerhard Engleder
16733fc23339SGerhard Engleder *page = NULL;
16743fc23339SGerhard Engleder page++;
16753fc23339SGerhard Engleder }
16763fc23339SGerhard Engleder }
16773fc23339SGerhard Engleder }
16783fc23339SGerhard Engleder
tsnep_rx_reopen_xsk(struct tsnep_rx * rx)16793fc23339SGerhard Engleder static void tsnep_rx_reopen_xsk(struct tsnep_rx *rx)
16803fc23339SGerhard Engleder {
16813fc23339SGerhard Engleder struct page **page = rx->page_buffer;
16823fc23339SGerhard Engleder u32 allocated;
16833fc23339SGerhard Engleder int i;
16843fc23339SGerhard Engleder
16853fc23339SGerhard Engleder tsnep_rx_init(rx);
16863fc23339SGerhard Engleder
16873fc23339SGerhard Engleder /* alloc all ring entries except the last one, because ring cannot be
16883fc23339SGerhard Engleder * filled completely, as many buffers as possible is enough as wakeup is
16893fc23339SGerhard Engleder * done if new buffers are available
16903fc23339SGerhard Engleder */
16913fc23339SGerhard Engleder allocated = xsk_buff_alloc_batch(rx->xsk_pool, rx->xdp_batch,
16923fc23339SGerhard Engleder TSNEP_RING_SIZE - 1);
16933fc23339SGerhard Engleder
16943fc23339SGerhard Engleder for (i = 0; i < TSNEP_RING_SIZE; i++) {
16953fc23339SGerhard Engleder struct tsnep_rx_entry *entry = &rx->entry[i];
16963fc23339SGerhard Engleder
16973fc23339SGerhard Engleder /* keep pages to prevent allocation failures when xsk is
16983fc23339SGerhard Engleder * disabled
16993fc23339SGerhard Engleder */
17003fc23339SGerhard Engleder if (entry->page) {
17013fc23339SGerhard Engleder *page = entry->page;
17023fc23339SGerhard Engleder entry->page = NULL;
17033fc23339SGerhard Engleder
17043fc23339SGerhard Engleder page++;
17053fc23339SGerhard Engleder }
17063fc23339SGerhard Engleder
17073fc23339SGerhard Engleder /* defined initial values for properties are required for
17083fc23339SGerhard Engleder * correct owner counter checking
17093fc23339SGerhard Engleder */
17103fc23339SGerhard Engleder entry->desc->properties = 0;
17113fc23339SGerhard Engleder entry->desc_wb->properties = 0;
17123fc23339SGerhard Engleder
17133fc23339SGerhard Engleder if (allocated) {
17143fc23339SGerhard Engleder tsnep_rx_set_xdp(rx, entry,
17153fc23339SGerhard Engleder rx->xdp_batch[allocated - 1]);
17163fc23339SGerhard Engleder tsnep_rx_activate(rx, rx->write);
17173fc23339SGerhard Engleder rx->write++;
17183fc23339SGerhard Engleder
17193fc23339SGerhard Engleder allocated--;
17203fc23339SGerhard Engleder }
17213fc23339SGerhard Engleder }
1722d60ff1d3SGerhard Engleder
1723d60ff1d3SGerhard Engleder /* set need wakeup flag immediately if ring is not filled completely,
1724d60ff1d3SGerhard Engleder * first polling would be too late as need wakeup signalisation would
1725d60ff1d3SGerhard Engleder * be delayed for an indefinite time
1726d60ff1d3SGerhard Engleder */
1727d60ff1d3SGerhard Engleder if (xsk_uses_need_wakeup(rx->xsk_pool)) {
1728d60ff1d3SGerhard Engleder int desc_available = tsnep_rx_desc_available(rx);
1729d60ff1d3SGerhard Engleder
1730d60ff1d3SGerhard Engleder if (desc_available)
1731d60ff1d3SGerhard Engleder xsk_set_rx_need_wakeup(rx->xsk_pool);
1732d60ff1d3SGerhard Engleder else
1733d60ff1d3SGerhard Engleder xsk_clear_rx_need_wakeup(rx->xsk_pool);
1734d60ff1d3SGerhard Engleder }
17353fc23339SGerhard Engleder }
17363fc23339SGerhard Engleder
tsnep_pending(struct tsnep_queue * queue)17372dc4ac91SGerhard Engleder static bool tsnep_pending(struct tsnep_queue *queue)
17382dc4ac91SGerhard Engleder {
17392dc4ac91SGerhard Engleder if (queue->tx && tsnep_tx_pending(queue->tx))
17402dc4ac91SGerhard Engleder return true;
17412dc4ac91SGerhard Engleder
17422dc4ac91SGerhard Engleder if (queue->rx && tsnep_rx_pending(queue->rx))
17432dc4ac91SGerhard Engleder return true;
17442dc4ac91SGerhard Engleder
17452dc4ac91SGerhard Engleder return false;
17462dc4ac91SGerhard Engleder }
17472dc4ac91SGerhard Engleder
tsnep_poll(struct napi_struct * napi,int budget)1748403f69bbSGerhard Engleder static int tsnep_poll(struct napi_struct *napi, int budget)
1749403f69bbSGerhard Engleder {
1750403f69bbSGerhard Engleder struct tsnep_queue *queue = container_of(napi, struct tsnep_queue,
1751403f69bbSGerhard Engleder napi);
1752403f69bbSGerhard Engleder bool complete = true;
1753403f69bbSGerhard Engleder int done = 0;
1754403f69bbSGerhard Engleder
1755403f69bbSGerhard Engleder if (queue->tx)
1756403f69bbSGerhard Engleder complete = tsnep_tx_poll(queue->tx, budget);
1757403f69bbSGerhard Engleder
175846589db3SGerhard Engleder /* handle case where we are called by netpoll with a budget of 0 */
175946589db3SGerhard Engleder if (unlikely(budget <= 0))
176046589db3SGerhard Engleder return budget;
176146589db3SGerhard Engleder
1762403f69bbSGerhard Engleder if (queue->rx) {
17633fc23339SGerhard Engleder done = queue->rx->xsk_pool ?
17643fc23339SGerhard Engleder tsnep_rx_poll_zc(queue->rx, napi, budget) :
17653fc23339SGerhard Engleder tsnep_rx_poll(queue->rx, napi, budget);
1766403f69bbSGerhard Engleder if (done >= budget)
1767403f69bbSGerhard Engleder complete = false;
1768403f69bbSGerhard Engleder }
1769403f69bbSGerhard Engleder
1770403f69bbSGerhard Engleder /* if all work not completed, return budget and keep polling */
1771403f69bbSGerhard Engleder if (!complete)
1772403f69bbSGerhard Engleder return budget;
1773403f69bbSGerhard Engleder
17742dc4ac91SGerhard Engleder if (likely(napi_complete_done(napi, done))) {
1775403f69bbSGerhard Engleder tsnep_enable_irq(queue->adapter, queue->irq_mask);
1776403f69bbSGerhard Engleder
17772dc4ac91SGerhard Engleder /* reschedule if work is already pending, prevent rotten packets
17782dc4ac91SGerhard Engleder * which are transmitted or received after polling but before
17792dc4ac91SGerhard Engleder * interrupt enable
17802dc4ac91SGerhard Engleder */
17812dc4ac91SGerhard Engleder if (tsnep_pending(queue)) {
17822dc4ac91SGerhard Engleder tsnep_disable_irq(queue->adapter, queue->irq_mask);
17832dc4ac91SGerhard Engleder napi_schedule(napi);
17842dc4ac91SGerhard Engleder }
17852dc4ac91SGerhard Engleder }
17862dc4ac91SGerhard Engleder
1787403f69bbSGerhard Engleder return min(done, budget - 1);
1788403f69bbSGerhard Engleder }
1789403f69bbSGerhard Engleder
tsnep_request_irq(struct tsnep_queue * queue,bool first)179058eaa8abSGerhard Engleder static int tsnep_request_irq(struct tsnep_queue *queue, bool first)
179158eaa8abSGerhard Engleder {
179258eaa8abSGerhard Engleder const char *name = netdev_name(queue->adapter->netdev);
179358eaa8abSGerhard Engleder irq_handler_t handler;
179458eaa8abSGerhard Engleder void *dev;
179558eaa8abSGerhard Engleder int retval;
179658eaa8abSGerhard Engleder
179758eaa8abSGerhard Engleder if (first) {
179858eaa8abSGerhard Engleder sprintf(queue->name, "%s-mac", name);
179958eaa8abSGerhard Engleder handler = tsnep_irq;
180058eaa8abSGerhard Engleder dev = queue->adapter;
180158eaa8abSGerhard Engleder } else {
180258eaa8abSGerhard Engleder if (queue->tx && queue->rx)
18036bb50965SGerhard Engleder snprintf(queue->name, sizeof(queue->name), "%s-txrx-%d",
18046bb50965SGerhard Engleder name, queue->rx->queue_index);
180558eaa8abSGerhard Engleder else if (queue->tx)
18066bb50965SGerhard Engleder snprintf(queue->name, sizeof(queue->name), "%s-tx-%d",
18076bb50965SGerhard Engleder name, queue->tx->queue_index);
180858eaa8abSGerhard Engleder else
18096bb50965SGerhard Engleder snprintf(queue->name, sizeof(queue->name), "%s-rx-%d",
18106bb50965SGerhard Engleder name, queue->rx->queue_index);
181158eaa8abSGerhard Engleder handler = tsnep_irq_txrx;
181258eaa8abSGerhard Engleder dev = queue;
181358eaa8abSGerhard Engleder }
181458eaa8abSGerhard Engleder
181558eaa8abSGerhard Engleder retval = request_irq(queue->irq, handler, 0, queue->name, dev);
181658eaa8abSGerhard Engleder if (retval) {
181758eaa8abSGerhard Engleder /* if name is empty, then interrupt won't be freed */
181858eaa8abSGerhard Engleder memset(queue->name, 0, sizeof(queue->name));
181958eaa8abSGerhard Engleder }
182058eaa8abSGerhard Engleder
182158eaa8abSGerhard Engleder return retval;
182258eaa8abSGerhard Engleder }
182358eaa8abSGerhard Engleder
tsnep_free_irq(struct tsnep_queue * queue,bool first)182458eaa8abSGerhard Engleder static void tsnep_free_irq(struct tsnep_queue *queue, bool first)
182558eaa8abSGerhard Engleder {
182658eaa8abSGerhard Engleder void *dev;
182758eaa8abSGerhard Engleder
182858eaa8abSGerhard Engleder if (!strlen(queue->name))
182958eaa8abSGerhard Engleder return;
183058eaa8abSGerhard Engleder
183158eaa8abSGerhard Engleder if (first)
183258eaa8abSGerhard Engleder dev = queue->adapter;
183358eaa8abSGerhard Engleder else
183458eaa8abSGerhard Engleder dev = queue;
183558eaa8abSGerhard Engleder
183658eaa8abSGerhard Engleder free_irq(queue->irq, dev);
183758eaa8abSGerhard Engleder memset(queue->name, 0, sizeof(queue->name));
183858eaa8abSGerhard Engleder }
183958eaa8abSGerhard Engleder
tsnep_queue_close(struct tsnep_queue * queue,bool first)1840e77832abSGerhard Engleder static void tsnep_queue_close(struct tsnep_queue *queue, bool first)
1841e77832abSGerhard Engleder {
1842e77832abSGerhard Engleder struct tsnep_rx *rx = queue->rx;
1843e77832abSGerhard Engleder
1844e77832abSGerhard Engleder tsnep_free_irq(queue, first);
1845e77832abSGerhard Engleder
18463fc23339SGerhard Engleder if (rx) {
18473fc23339SGerhard Engleder if (xdp_rxq_info_is_reg(&rx->xdp_rxq))
1848e77832abSGerhard Engleder xdp_rxq_info_unreg(&rx->xdp_rxq);
18493fc23339SGerhard Engleder if (xdp_rxq_info_is_reg(&rx->xdp_rxq_zc))
18503fc23339SGerhard Engleder xdp_rxq_info_unreg(&rx->xdp_rxq_zc);
18513fc23339SGerhard Engleder }
1852e77832abSGerhard Engleder
1853e77832abSGerhard Engleder netif_napi_del(&queue->napi);
1854e77832abSGerhard Engleder }
1855e77832abSGerhard Engleder
tsnep_queue_open(struct tsnep_adapter * adapter,struct tsnep_queue * queue,bool first)1856e77832abSGerhard Engleder static int tsnep_queue_open(struct tsnep_adapter *adapter,
1857e77832abSGerhard Engleder struct tsnep_queue *queue, bool first)
1858e77832abSGerhard Engleder {
1859e77832abSGerhard Engleder struct tsnep_rx *rx = queue->rx;
186065b28c81SGerhard Engleder struct tsnep_tx *tx = queue->tx;
1861e77832abSGerhard Engleder int retval;
1862e77832abSGerhard Engleder
1863e77832abSGerhard Engleder netif_napi_add(adapter->netdev, &queue->napi, tsnep_poll);
1864e77832abSGerhard Engleder
1865e77832abSGerhard Engleder if (rx) {
186665b28c81SGerhard Engleder /* choose TX queue for XDP_TX */
186765b28c81SGerhard Engleder if (tx)
186865b28c81SGerhard Engleder rx->tx_queue_index = tx->queue_index;
186965b28c81SGerhard Engleder else if (rx->queue_index < adapter->num_tx_queues)
187065b28c81SGerhard Engleder rx->tx_queue_index = rx->queue_index;
187165b28c81SGerhard Engleder else
187265b28c81SGerhard Engleder rx->tx_queue_index = 0;
187365b28c81SGerhard Engleder
18743fc23339SGerhard Engleder /* prepare both memory models to eliminate possible registration
18753fc23339SGerhard Engleder * errors when memory model is switched between page pool and
18763fc23339SGerhard Engleder * XSK pool during runtime
18773fc23339SGerhard Engleder */
1878e77832abSGerhard Engleder retval = xdp_rxq_info_reg(&rx->xdp_rxq, adapter->netdev,
1879e77832abSGerhard Engleder rx->queue_index, queue->napi.napi_id);
1880e77832abSGerhard Engleder if (retval)
1881e77832abSGerhard Engleder goto failed;
1882e77832abSGerhard Engleder retval = xdp_rxq_info_reg_mem_model(&rx->xdp_rxq,
1883e77832abSGerhard Engleder MEM_TYPE_PAGE_POOL,
1884e77832abSGerhard Engleder rx->page_pool);
1885e77832abSGerhard Engleder if (retval)
1886e77832abSGerhard Engleder goto failed;
18873fc23339SGerhard Engleder retval = xdp_rxq_info_reg(&rx->xdp_rxq_zc, adapter->netdev,
18883fc23339SGerhard Engleder rx->queue_index, queue->napi.napi_id);
18893fc23339SGerhard Engleder if (retval)
18903fc23339SGerhard Engleder goto failed;
18913fc23339SGerhard Engleder retval = xdp_rxq_info_reg_mem_model(&rx->xdp_rxq_zc,
18923fc23339SGerhard Engleder MEM_TYPE_XSK_BUFF_POOL,
18933fc23339SGerhard Engleder NULL);
18943fc23339SGerhard Engleder if (retval)
18953fc23339SGerhard Engleder goto failed;
18963fc23339SGerhard Engleder if (rx->xsk_pool)
18973fc23339SGerhard Engleder xsk_pool_set_rxq_info(rx->xsk_pool, &rx->xdp_rxq_zc);
1898e77832abSGerhard Engleder }
1899e77832abSGerhard Engleder
1900e77832abSGerhard Engleder retval = tsnep_request_irq(queue, first);
1901e77832abSGerhard Engleder if (retval) {
1902e77832abSGerhard Engleder netif_err(adapter, drv, adapter->netdev,
1903e77832abSGerhard Engleder "can't get assigned irq %d.\n", queue->irq);
1904e77832abSGerhard Engleder goto failed;
1905e77832abSGerhard Engleder }
1906e77832abSGerhard Engleder
1907e77832abSGerhard Engleder return 0;
1908e77832abSGerhard Engleder
1909e77832abSGerhard Engleder failed:
1910e77832abSGerhard Engleder tsnep_queue_close(queue, first);
1911e77832abSGerhard Engleder
1912e77832abSGerhard Engleder return retval;
1913e77832abSGerhard Engleder }
1914e77832abSGerhard Engleder
tsnep_queue_enable(struct tsnep_queue * queue)19152ea0a282SGerhard Engleder static void tsnep_queue_enable(struct tsnep_queue *queue)
19162ea0a282SGerhard Engleder {
19172ea0a282SGerhard Engleder napi_enable(&queue->napi);
19182ea0a282SGerhard Engleder tsnep_enable_irq(queue->adapter, queue->irq_mask);
19192ea0a282SGerhard Engleder
1920cd275c23SGerhard Engleder if (queue->tx)
1921cd275c23SGerhard Engleder tsnep_tx_enable(queue->tx);
1922cd275c23SGerhard Engleder
19232ea0a282SGerhard Engleder if (queue->rx)
19242ea0a282SGerhard Engleder tsnep_rx_enable(queue->rx);
19252ea0a282SGerhard Engleder }
19262ea0a282SGerhard Engleder
tsnep_queue_disable(struct tsnep_queue * queue)19272ea0a282SGerhard Engleder static void tsnep_queue_disable(struct tsnep_queue *queue)
19282ea0a282SGerhard Engleder {
1929cd275c23SGerhard Engleder if (queue->tx)
1930cd275c23SGerhard Engleder tsnep_tx_disable(queue->tx, &queue->napi);
1931cd275c23SGerhard Engleder
19322ea0a282SGerhard Engleder napi_disable(&queue->napi);
19332ea0a282SGerhard Engleder tsnep_disable_irq(queue->adapter, queue->irq_mask);
19342ea0a282SGerhard Engleder
19352ea0a282SGerhard Engleder /* disable RX after NAPI polling has been disabled, because RX can be
19362ea0a282SGerhard Engleder * enabled during NAPI polling
19372ea0a282SGerhard Engleder */
19382ea0a282SGerhard Engleder if (queue->rx)
19392ea0a282SGerhard Engleder tsnep_rx_disable(queue->rx);
19402ea0a282SGerhard Engleder }
19412ea0a282SGerhard Engleder
tsnep_netdev_open(struct net_device * netdev)1942403f69bbSGerhard Engleder static int tsnep_netdev_open(struct net_device *netdev)
1943403f69bbSGerhard Engleder {
1944403f69bbSGerhard Engleder struct tsnep_adapter *adapter = netdev_priv(netdev);
1945e77832abSGerhard Engleder int i, retval;
1946403f69bbSGerhard Engleder
1947403f69bbSGerhard Engleder for (i = 0; i < adapter->num_queues; i++) {
1948403f69bbSGerhard Engleder if (adapter->queue[i].tx) {
194933b0ee02SGerhard Engleder retval = tsnep_tx_open(adapter->queue[i].tx);
1950403f69bbSGerhard Engleder if (retval)
1951403f69bbSGerhard Engleder goto failed;
1952403f69bbSGerhard Engleder }
1953403f69bbSGerhard Engleder if (adapter->queue[i].rx) {
195433b0ee02SGerhard Engleder retval = tsnep_rx_open(adapter->queue[i].rx);
1955403f69bbSGerhard Engleder if (retval)
1956403f69bbSGerhard Engleder goto failed;
1957403f69bbSGerhard Engleder }
195858eaa8abSGerhard Engleder
1959e77832abSGerhard Engleder retval = tsnep_queue_open(adapter, &adapter->queue[i], i == 0);
1960e77832abSGerhard Engleder if (retval)
196158eaa8abSGerhard Engleder goto failed;
196258eaa8abSGerhard Engleder }
1963403f69bbSGerhard Engleder
1964403f69bbSGerhard Engleder retval = netif_set_real_num_tx_queues(adapter->netdev,
1965403f69bbSGerhard Engleder adapter->num_tx_queues);
1966403f69bbSGerhard Engleder if (retval)
1967403f69bbSGerhard Engleder goto failed;
1968403f69bbSGerhard Engleder retval = netif_set_real_num_rx_queues(adapter->netdev,
1969403f69bbSGerhard Engleder adapter->num_rx_queues);
1970403f69bbSGerhard Engleder if (retval)
1971403f69bbSGerhard Engleder goto failed;
1972403f69bbSGerhard Engleder
197358eaa8abSGerhard Engleder tsnep_enable_irq(adapter, ECM_INT_LINK);
197458eaa8abSGerhard Engleder retval = tsnep_phy_open(adapter);
197558eaa8abSGerhard Engleder if (retval)
197658eaa8abSGerhard Engleder goto phy_failed;
197758eaa8abSGerhard Engleder
19782ea0a282SGerhard Engleder for (i = 0; i < adapter->num_queues; i++)
19792ea0a282SGerhard Engleder tsnep_queue_enable(&adapter->queue[i]);
1980403f69bbSGerhard Engleder
1981403f69bbSGerhard Engleder return 0;
1982403f69bbSGerhard Engleder
198358eaa8abSGerhard Engleder phy_failed:
198458eaa8abSGerhard Engleder tsnep_disable_irq(adapter, ECM_INT_LINK);
1985403f69bbSGerhard Engleder failed:
1986403f69bbSGerhard Engleder for (i = 0; i < adapter->num_queues; i++) {
1987e77832abSGerhard Engleder tsnep_queue_close(&adapter->queue[i], i == 0);
198858eaa8abSGerhard Engleder
1989403f69bbSGerhard Engleder if (adapter->queue[i].rx)
1990403f69bbSGerhard Engleder tsnep_rx_close(adapter->queue[i].rx);
1991403f69bbSGerhard Engleder if (adapter->queue[i].tx)
1992403f69bbSGerhard Engleder tsnep_tx_close(adapter->queue[i].tx);
1993403f69bbSGerhard Engleder }
1994403f69bbSGerhard Engleder return retval;
1995403f69bbSGerhard Engleder }
1996403f69bbSGerhard Engleder
tsnep_netdev_close(struct net_device * netdev)1997403f69bbSGerhard Engleder static int tsnep_netdev_close(struct net_device *netdev)
1998403f69bbSGerhard Engleder {
1999403f69bbSGerhard Engleder struct tsnep_adapter *adapter = netdev_priv(netdev);
2000403f69bbSGerhard Engleder int i;
2001403f69bbSGerhard Engleder
200258eaa8abSGerhard Engleder tsnep_disable_irq(adapter, ECM_INT_LINK);
200358eaa8abSGerhard Engleder tsnep_phy_close(adapter);
200458eaa8abSGerhard Engleder
2005403f69bbSGerhard Engleder for (i = 0; i < adapter->num_queues; i++) {
20062ea0a282SGerhard Engleder tsnep_queue_disable(&adapter->queue[i]);
2007403f69bbSGerhard Engleder
2008e77832abSGerhard Engleder tsnep_queue_close(&adapter->queue[i], i == 0);
200958eaa8abSGerhard Engleder
2010403f69bbSGerhard Engleder if (adapter->queue[i].rx)
2011403f69bbSGerhard Engleder tsnep_rx_close(adapter->queue[i].rx);
2012403f69bbSGerhard Engleder if (adapter->queue[i].tx)
2013403f69bbSGerhard Engleder tsnep_tx_close(adapter->queue[i].tx);
2014403f69bbSGerhard Engleder }
2015403f69bbSGerhard Engleder
2016403f69bbSGerhard Engleder return 0;
2017403f69bbSGerhard Engleder }
2018403f69bbSGerhard Engleder
tsnep_enable_xsk(struct tsnep_queue * queue,struct xsk_buff_pool * pool)20193fc23339SGerhard Engleder int tsnep_enable_xsk(struct tsnep_queue *queue, struct xsk_buff_pool *pool)
20203fc23339SGerhard Engleder {
20213fc23339SGerhard Engleder bool running = netif_running(queue->adapter->netdev);
20223fc23339SGerhard Engleder u32 frame_size;
20233fc23339SGerhard Engleder
20243fc23339SGerhard Engleder frame_size = xsk_pool_get_rx_frame_size(pool);
20253fc23339SGerhard Engleder if (frame_size < TSNEP_XSK_RX_BUF_SIZE)
20263fc23339SGerhard Engleder return -EOPNOTSUPP;
20273fc23339SGerhard Engleder
20283fc23339SGerhard Engleder queue->rx->page_buffer = kcalloc(TSNEP_RING_SIZE,
20293fc23339SGerhard Engleder sizeof(*queue->rx->page_buffer),
20303fc23339SGerhard Engleder GFP_KERNEL);
20313fc23339SGerhard Engleder if (!queue->rx->page_buffer)
20323fc23339SGerhard Engleder return -ENOMEM;
20333fc23339SGerhard Engleder queue->rx->xdp_batch = kcalloc(TSNEP_RING_SIZE,
20343fc23339SGerhard Engleder sizeof(*queue->rx->xdp_batch),
20353fc23339SGerhard Engleder GFP_KERNEL);
20363fc23339SGerhard Engleder if (!queue->rx->xdp_batch) {
20373fc23339SGerhard Engleder kfree(queue->rx->page_buffer);
20383fc23339SGerhard Engleder queue->rx->page_buffer = NULL;
20393fc23339SGerhard Engleder
20403fc23339SGerhard Engleder return -ENOMEM;
20413fc23339SGerhard Engleder }
20423fc23339SGerhard Engleder
20433fc23339SGerhard Engleder xsk_pool_set_rxq_info(pool, &queue->rx->xdp_rxq_zc);
20443fc23339SGerhard Engleder
20453fc23339SGerhard Engleder if (running)
20463fc23339SGerhard Engleder tsnep_queue_disable(queue);
20473fc23339SGerhard Engleder
2048cd275c23SGerhard Engleder queue->tx->xsk_pool = pool;
20493fc23339SGerhard Engleder queue->rx->xsk_pool = pool;
20503fc23339SGerhard Engleder
20513fc23339SGerhard Engleder if (running) {
20523fc23339SGerhard Engleder tsnep_rx_reopen_xsk(queue->rx);
20533fc23339SGerhard Engleder tsnep_queue_enable(queue);
20543fc23339SGerhard Engleder }
20553fc23339SGerhard Engleder
20563fc23339SGerhard Engleder return 0;
20573fc23339SGerhard Engleder }
20583fc23339SGerhard Engleder
tsnep_disable_xsk(struct tsnep_queue * queue)20593fc23339SGerhard Engleder void tsnep_disable_xsk(struct tsnep_queue *queue)
20603fc23339SGerhard Engleder {
20613fc23339SGerhard Engleder bool running = netif_running(queue->adapter->netdev);
20623fc23339SGerhard Engleder
20633fc23339SGerhard Engleder if (running)
20643fc23339SGerhard Engleder tsnep_queue_disable(queue);
20653fc23339SGerhard Engleder
20663fc23339SGerhard Engleder tsnep_rx_free_zc(queue->rx);
20673fc23339SGerhard Engleder
20683fc23339SGerhard Engleder queue->rx->xsk_pool = NULL;
2069cd275c23SGerhard Engleder queue->tx->xsk_pool = NULL;
20703fc23339SGerhard Engleder
20713fc23339SGerhard Engleder if (running) {
20723fc23339SGerhard Engleder tsnep_rx_reopen(queue->rx);
20733fc23339SGerhard Engleder tsnep_queue_enable(queue);
20743fc23339SGerhard Engleder }
20753fc23339SGerhard Engleder
20763fc23339SGerhard Engleder kfree(queue->rx->xdp_batch);
20773fc23339SGerhard Engleder queue->rx->xdp_batch = NULL;
20783fc23339SGerhard Engleder kfree(queue->rx->page_buffer);
20793fc23339SGerhard Engleder queue->rx->page_buffer = NULL;
20803fc23339SGerhard Engleder }
20813fc23339SGerhard Engleder
tsnep_netdev_xmit_frame(struct sk_buff * skb,struct net_device * netdev)2082403f69bbSGerhard Engleder static netdev_tx_t tsnep_netdev_xmit_frame(struct sk_buff *skb,
2083403f69bbSGerhard Engleder struct net_device *netdev)
2084403f69bbSGerhard Engleder {
2085403f69bbSGerhard Engleder struct tsnep_adapter *adapter = netdev_priv(netdev);
2086403f69bbSGerhard Engleder u16 queue_mapping = skb_get_queue_mapping(skb);
2087403f69bbSGerhard Engleder
2088403f69bbSGerhard Engleder if (queue_mapping >= adapter->num_tx_queues)
2089403f69bbSGerhard Engleder queue_mapping = 0;
2090403f69bbSGerhard Engleder
2091403f69bbSGerhard Engleder return tsnep_xmit_frame_ring(skb, &adapter->tx[queue_mapping]);
2092403f69bbSGerhard Engleder }
2093403f69bbSGerhard Engleder
tsnep_netdev_ioctl(struct net_device * netdev,struct ifreq * ifr,int cmd)2094403f69bbSGerhard Engleder static int tsnep_netdev_ioctl(struct net_device *netdev, struct ifreq *ifr,
2095403f69bbSGerhard Engleder int cmd)
2096403f69bbSGerhard Engleder {
2097403f69bbSGerhard Engleder if (!netif_running(netdev))
2098403f69bbSGerhard Engleder return -EINVAL;
2099403f69bbSGerhard Engleder if (cmd == SIOCSHWTSTAMP || cmd == SIOCGHWTSTAMP)
2100403f69bbSGerhard Engleder return tsnep_ptp_ioctl(netdev, ifr, cmd);
2101403f69bbSGerhard Engleder return phy_mii_ioctl(netdev->phydev, ifr, cmd);
2102403f69bbSGerhard Engleder }
2103403f69bbSGerhard Engleder
tsnep_netdev_set_multicast(struct net_device * netdev)2104403f69bbSGerhard Engleder static void tsnep_netdev_set_multicast(struct net_device *netdev)
2105403f69bbSGerhard Engleder {
2106403f69bbSGerhard Engleder struct tsnep_adapter *adapter = netdev_priv(netdev);
2107403f69bbSGerhard Engleder
2108403f69bbSGerhard Engleder u16 rx_filter = 0;
2109403f69bbSGerhard Engleder
2110403f69bbSGerhard Engleder /* configured MAC address and broadcasts are never filtered */
2111403f69bbSGerhard Engleder if (netdev->flags & IFF_PROMISC) {
2112403f69bbSGerhard Engleder rx_filter |= TSNEP_RX_FILTER_ACCEPT_ALL_MULTICASTS;
2113403f69bbSGerhard Engleder rx_filter |= TSNEP_RX_FILTER_ACCEPT_ALL_UNICASTS;
2114403f69bbSGerhard Engleder } else if (!netdev_mc_empty(netdev) || (netdev->flags & IFF_ALLMULTI)) {
2115403f69bbSGerhard Engleder rx_filter |= TSNEP_RX_FILTER_ACCEPT_ALL_MULTICASTS;
2116403f69bbSGerhard Engleder }
2117403f69bbSGerhard Engleder iowrite16(rx_filter, adapter->addr + TSNEP_RX_FILTER);
2118403f69bbSGerhard Engleder }
2119403f69bbSGerhard Engleder
tsnep_netdev_get_stats64(struct net_device * netdev,struct rtnl_link_stats64 * stats)2120403f69bbSGerhard Engleder static void tsnep_netdev_get_stats64(struct net_device *netdev,
2121403f69bbSGerhard Engleder struct rtnl_link_stats64 *stats)
2122403f69bbSGerhard Engleder {
2123403f69bbSGerhard Engleder struct tsnep_adapter *adapter = netdev_priv(netdev);
2124403f69bbSGerhard Engleder u32 reg;
2125403f69bbSGerhard Engleder u32 val;
2126403f69bbSGerhard Engleder int i;
2127403f69bbSGerhard Engleder
2128403f69bbSGerhard Engleder for (i = 0; i < adapter->num_tx_queues; i++) {
2129403f69bbSGerhard Engleder stats->tx_packets += adapter->tx[i].packets;
2130403f69bbSGerhard Engleder stats->tx_bytes += adapter->tx[i].bytes;
2131403f69bbSGerhard Engleder stats->tx_dropped += adapter->tx[i].dropped;
2132403f69bbSGerhard Engleder }
2133403f69bbSGerhard Engleder for (i = 0; i < adapter->num_rx_queues; i++) {
2134403f69bbSGerhard Engleder stats->rx_packets += adapter->rx[i].packets;
2135403f69bbSGerhard Engleder stats->rx_bytes += adapter->rx[i].bytes;
2136403f69bbSGerhard Engleder stats->rx_dropped += adapter->rx[i].dropped;
2137403f69bbSGerhard Engleder stats->multicast += adapter->rx[i].multicast;
2138403f69bbSGerhard Engleder
2139403f69bbSGerhard Engleder reg = ioread32(adapter->addr + TSNEP_QUEUE(i) +
2140403f69bbSGerhard Engleder TSNEP_RX_STATISTIC);
2141403f69bbSGerhard Engleder val = (reg & TSNEP_RX_STATISTIC_NO_DESC_MASK) >>
2142403f69bbSGerhard Engleder TSNEP_RX_STATISTIC_NO_DESC_SHIFT;
2143403f69bbSGerhard Engleder stats->rx_dropped += val;
2144403f69bbSGerhard Engleder val = (reg & TSNEP_RX_STATISTIC_BUFFER_TOO_SMALL_MASK) >>
2145403f69bbSGerhard Engleder TSNEP_RX_STATISTIC_BUFFER_TOO_SMALL_SHIFT;
2146403f69bbSGerhard Engleder stats->rx_dropped += val;
2147403f69bbSGerhard Engleder val = (reg & TSNEP_RX_STATISTIC_FIFO_OVERFLOW_MASK) >>
2148403f69bbSGerhard Engleder TSNEP_RX_STATISTIC_FIFO_OVERFLOW_SHIFT;
2149403f69bbSGerhard Engleder stats->rx_errors += val;
2150403f69bbSGerhard Engleder stats->rx_fifo_errors += val;
2151403f69bbSGerhard Engleder val = (reg & TSNEP_RX_STATISTIC_INVALID_FRAME_MASK) >>
2152403f69bbSGerhard Engleder TSNEP_RX_STATISTIC_INVALID_FRAME_SHIFT;
2153403f69bbSGerhard Engleder stats->rx_errors += val;
2154403f69bbSGerhard Engleder stats->rx_frame_errors += val;
2155403f69bbSGerhard Engleder }
2156403f69bbSGerhard Engleder
2157403f69bbSGerhard Engleder reg = ioread32(adapter->addr + ECM_STAT);
2158403f69bbSGerhard Engleder val = (reg & ECM_STAT_RX_ERR_MASK) >> ECM_STAT_RX_ERR_SHIFT;
2159403f69bbSGerhard Engleder stats->rx_errors += val;
2160403f69bbSGerhard Engleder val = (reg & ECM_STAT_INV_FRM_MASK) >> ECM_STAT_INV_FRM_SHIFT;
2161403f69bbSGerhard Engleder stats->rx_errors += val;
2162403f69bbSGerhard Engleder stats->rx_crc_errors += val;
2163403f69bbSGerhard Engleder val = (reg & ECM_STAT_FWD_RX_ERR_MASK) >> ECM_STAT_FWD_RX_ERR_SHIFT;
2164403f69bbSGerhard Engleder stats->rx_errors += val;
2165403f69bbSGerhard Engleder }
2166403f69bbSGerhard Engleder
tsnep_mac_set_address(struct tsnep_adapter * adapter,u8 * addr)2167403f69bbSGerhard Engleder static void tsnep_mac_set_address(struct tsnep_adapter *adapter, u8 *addr)
2168403f69bbSGerhard Engleder {
2169403f69bbSGerhard Engleder iowrite32(*(u32 *)addr, adapter->addr + TSNEP_MAC_ADDRESS_LOW);
2170403f69bbSGerhard Engleder iowrite16(*(u16 *)(addr + sizeof(u32)),
2171403f69bbSGerhard Engleder adapter->addr + TSNEP_MAC_ADDRESS_HIGH);
2172403f69bbSGerhard Engleder
2173403f69bbSGerhard Engleder ether_addr_copy(adapter->mac_address, addr);
2174403f69bbSGerhard Engleder netif_info(adapter, drv, adapter->netdev, "MAC address set to %pM\n",
2175403f69bbSGerhard Engleder addr);
2176403f69bbSGerhard Engleder }
2177403f69bbSGerhard Engleder
tsnep_netdev_set_mac_address(struct net_device * netdev,void * addr)2178403f69bbSGerhard Engleder static int tsnep_netdev_set_mac_address(struct net_device *netdev, void *addr)
2179403f69bbSGerhard Engleder {
2180403f69bbSGerhard Engleder struct tsnep_adapter *adapter = netdev_priv(netdev);
2181403f69bbSGerhard Engleder struct sockaddr *sock_addr = addr;
2182403f69bbSGerhard Engleder int retval;
2183403f69bbSGerhard Engleder
2184403f69bbSGerhard Engleder retval = eth_prepare_mac_addr_change(netdev, sock_addr);
2185403f69bbSGerhard Engleder if (retval)
2186403f69bbSGerhard Engleder return retval;
218775e47206SGerhard Engleder eth_hw_addr_set(netdev, sock_addr->sa_data);
2188403f69bbSGerhard Engleder tsnep_mac_set_address(adapter, sock_addr->sa_data);
2189403f69bbSGerhard Engleder
2190403f69bbSGerhard Engleder return 0;
2191403f69bbSGerhard Engleder }
2192403f69bbSGerhard Engleder
tsnep_netdev_set_features(struct net_device * netdev,netdev_features_t features)21934b222008SGerhard Engleder static int tsnep_netdev_set_features(struct net_device *netdev,
21944b222008SGerhard Engleder netdev_features_t features)
21954b222008SGerhard Engleder {
21964b222008SGerhard Engleder struct tsnep_adapter *adapter = netdev_priv(netdev);
21974b222008SGerhard Engleder netdev_features_t changed = netdev->features ^ features;
21984b222008SGerhard Engleder bool enable;
21994b222008SGerhard Engleder int retval = 0;
22004b222008SGerhard Engleder
22014b222008SGerhard Engleder if (changed & NETIF_F_LOOPBACK) {
22024b222008SGerhard Engleder enable = !!(features & NETIF_F_LOOPBACK);
22034b222008SGerhard Engleder retval = tsnep_phy_loopback(adapter, enable);
22044b222008SGerhard Engleder }
22054b222008SGerhard Engleder
22064b222008SGerhard Engleder return retval;
22074b222008SGerhard Engleder }
22084b222008SGerhard Engleder
tsnep_netdev_get_tstamp(struct net_device * netdev,const struct skb_shared_hwtstamps * hwtstamps,bool cycles)22090abb62b6SGerhard Engleder static ktime_t tsnep_netdev_get_tstamp(struct net_device *netdev,
22100abb62b6SGerhard Engleder const struct skb_shared_hwtstamps *hwtstamps,
22110abb62b6SGerhard Engleder bool cycles)
22120abb62b6SGerhard Engleder {
22130abb62b6SGerhard Engleder struct tsnep_rx_inline *rx_inline = hwtstamps->netdev_data;
22140abb62b6SGerhard Engleder u64 timestamp;
22150abb62b6SGerhard Engleder
22160abb62b6SGerhard Engleder if (cycles)
22170abb62b6SGerhard Engleder timestamp = __le64_to_cpu(rx_inline->counter);
22180abb62b6SGerhard Engleder else
22190abb62b6SGerhard Engleder timestamp = __le64_to_cpu(rx_inline->timestamp);
22200abb62b6SGerhard Engleder
22210abb62b6SGerhard Engleder return ns_to_ktime(timestamp);
22220abb62b6SGerhard Engleder }
22230abb62b6SGerhard Engleder
tsnep_netdev_bpf(struct net_device * dev,struct netdev_bpf * bpf)2224f0f6460fSGerhard Engleder static int tsnep_netdev_bpf(struct net_device *dev, struct netdev_bpf *bpf)
2225f0f6460fSGerhard Engleder {
2226f0f6460fSGerhard Engleder struct tsnep_adapter *adapter = netdev_priv(dev);
2227f0f6460fSGerhard Engleder
2228f0f6460fSGerhard Engleder switch (bpf->command) {
2229f0f6460fSGerhard Engleder case XDP_SETUP_PROG:
2230f0f6460fSGerhard Engleder return tsnep_xdp_setup_prog(adapter, bpf->prog, bpf->extack);
22313fc23339SGerhard Engleder case XDP_SETUP_XSK_POOL:
22323fc23339SGerhard Engleder return tsnep_xdp_setup_pool(adapter, bpf->xsk.pool,
22333fc23339SGerhard Engleder bpf->xsk.queue_id);
2234f0f6460fSGerhard Engleder default:
2235f0f6460fSGerhard Engleder return -EOPNOTSUPP;
2236f0f6460fSGerhard Engleder }
2237f0f6460fSGerhard Engleder }
2238f0f6460fSGerhard Engleder
tsnep_xdp_get_tx(struct tsnep_adapter * adapter,u32 cpu)2239d24bc0bcSGerhard Engleder static struct tsnep_tx *tsnep_xdp_get_tx(struct tsnep_adapter *adapter, u32 cpu)
2240d24bc0bcSGerhard Engleder {
2241d24bc0bcSGerhard Engleder if (cpu >= TSNEP_MAX_QUEUES)
2242d24bc0bcSGerhard Engleder cpu &= TSNEP_MAX_QUEUES - 1;
2243d24bc0bcSGerhard Engleder
2244d24bc0bcSGerhard Engleder while (cpu >= adapter->num_tx_queues)
2245d24bc0bcSGerhard Engleder cpu -= adapter->num_tx_queues;
2246d24bc0bcSGerhard Engleder
2247d24bc0bcSGerhard Engleder return &adapter->tx[cpu];
2248d24bc0bcSGerhard Engleder }
2249d24bc0bcSGerhard Engleder
tsnep_netdev_xdp_xmit(struct net_device * dev,int n,struct xdp_frame ** xdp,u32 flags)2250d24bc0bcSGerhard Engleder static int tsnep_netdev_xdp_xmit(struct net_device *dev, int n,
2251d24bc0bcSGerhard Engleder struct xdp_frame **xdp, u32 flags)
2252d24bc0bcSGerhard Engleder {
2253d24bc0bcSGerhard Engleder struct tsnep_adapter *adapter = netdev_priv(dev);
2254d24bc0bcSGerhard Engleder u32 cpu = smp_processor_id();
2255d24bc0bcSGerhard Engleder struct netdev_queue *nq;
2256d24bc0bcSGerhard Engleder struct tsnep_tx *tx;
2257d24bc0bcSGerhard Engleder int nxmit;
2258d24bc0bcSGerhard Engleder bool xmit;
2259d24bc0bcSGerhard Engleder
2260d24bc0bcSGerhard Engleder if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK))
2261d24bc0bcSGerhard Engleder return -EINVAL;
2262d24bc0bcSGerhard Engleder
2263d24bc0bcSGerhard Engleder tx = tsnep_xdp_get_tx(adapter, cpu);
2264d24bc0bcSGerhard Engleder nq = netdev_get_tx_queue(adapter->netdev, tx->queue_index);
2265d24bc0bcSGerhard Engleder
2266d24bc0bcSGerhard Engleder __netif_tx_lock(nq, cpu);
2267d24bc0bcSGerhard Engleder
2268d24bc0bcSGerhard Engleder for (nxmit = 0; nxmit < n; nxmit++) {
2269d24bc0bcSGerhard Engleder xmit = tsnep_xdp_xmit_frame_ring(xdp[nxmit], tx,
2270d24bc0bcSGerhard Engleder TSNEP_TX_TYPE_XDP_NDO);
2271d24bc0bcSGerhard Engleder if (!xmit)
2272d24bc0bcSGerhard Engleder break;
2273d24bc0bcSGerhard Engleder
2274d24bc0bcSGerhard Engleder /* avoid transmit queue timeout since we share it with the slow
2275d24bc0bcSGerhard Engleder * path
2276d24bc0bcSGerhard Engleder */
2277d24bc0bcSGerhard Engleder txq_trans_cond_update(nq);
2278d24bc0bcSGerhard Engleder }
2279d24bc0bcSGerhard Engleder
2280d24bc0bcSGerhard Engleder if (flags & XDP_XMIT_FLUSH)
2281d24bc0bcSGerhard Engleder tsnep_xdp_xmit_flush(tx);
2282d24bc0bcSGerhard Engleder
2283d24bc0bcSGerhard Engleder __netif_tx_unlock(nq);
2284d24bc0bcSGerhard Engleder
2285d24bc0bcSGerhard Engleder return nxmit;
2286d24bc0bcSGerhard Engleder }
2287d24bc0bcSGerhard Engleder
tsnep_netdev_xsk_wakeup(struct net_device * dev,u32 queue_id,u32 flags)22883fc23339SGerhard Engleder static int tsnep_netdev_xsk_wakeup(struct net_device *dev, u32 queue_id,
22893fc23339SGerhard Engleder u32 flags)
22903fc23339SGerhard Engleder {
22913fc23339SGerhard Engleder struct tsnep_adapter *adapter = netdev_priv(dev);
22923fc23339SGerhard Engleder struct tsnep_queue *queue;
22933fc23339SGerhard Engleder
22943fc23339SGerhard Engleder if (queue_id >= adapter->num_rx_queues ||
22953fc23339SGerhard Engleder queue_id >= adapter->num_tx_queues)
22963fc23339SGerhard Engleder return -EINVAL;
22973fc23339SGerhard Engleder
22983fc23339SGerhard Engleder queue = &adapter->queue[queue_id];
22993fc23339SGerhard Engleder
23003fc23339SGerhard Engleder if (!napi_if_scheduled_mark_missed(&queue->napi))
23013fc23339SGerhard Engleder napi_schedule(&queue->napi);
23023fc23339SGerhard Engleder
23033fc23339SGerhard Engleder return 0;
23043fc23339SGerhard Engleder }
23053fc23339SGerhard Engleder
2306403f69bbSGerhard Engleder static const struct net_device_ops tsnep_netdev_ops = {
2307403f69bbSGerhard Engleder .ndo_open = tsnep_netdev_open,
2308403f69bbSGerhard Engleder .ndo_stop = tsnep_netdev_close,
2309403f69bbSGerhard Engleder .ndo_start_xmit = tsnep_netdev_xmit_frame,
2310403f69bbSGerhard Engleder .ndo_eth_ioctl = tsnep_netdev_ioctl,
2311403f69bbSGerhard Engleder .ndo_set_rx_mode = tsnep_netdev_set_multicast,
2312403f69bbSGerhard Engleder .ndo_get_stats64 = tsnep_netdev_get_stats64,
2313403f69bbSGerhard Engleder .ndo_set_mac_address = tsnep_netdev_set_mac_address,
23144b222008SGerhard Engleder .ndo_set_features = tsnep_netdev_set_features,
23150abb62b6SGerhard Engleder .ndo_get_tstamp = tsnep_netdev_get_tstamp,
2316403f69bbSGerhard Engleder .ndo_setup_tc = tsnep_tc_setup,
2317f0f6460fSGerhard Engleder .ndo_bpf = tsnep_netdev_bpf,
2318d24bc0bcSGerhard Engleder .ndo_xdp_xmit = tsnep_netdev_xdp_xmit,
23193fc23339SGerhard Engleder .ndo_xsk_wakeup = tsnep_netdev_xsk_wakeup,
2320403f69bbSGerhard Engleder };
2321403f69bbSGerhard Engleder
tsnep_mac_init(struct tsnep_adapter * adapter)2322403f69bbSGerhard Engleder static int tsnep_mac_init(struct tsnep_adapter *adapter)
2323403f69bbSGerhard Engleder {
2324403f69bbSGerhard Engleder int retval;
2325403f69bbSGerhard Engleder
2326403f69bbSGerhard Engleder /* initialize RX filtering, at least configured MAC address and
2327403f69bbSGerhard Engleder * broadcast are not filtered
2328403f69bbSGerhard Engleder */
2329403f69bbSGerhard Engleder iowrite16(0, adapter->addr + TSNEP_RX_FILTER);
2330403f69bbSGerhard Engleder
2331403f69bbSGerhard Engleder /* try to get MAC address in the following order:
2332403f69bbSGerhard Engleder * - device tree
2333403f69bbSGerhard Engleder * - valid MAC address already set
2334403f69bbSGerhard Engleder * - MAC address register if valid
2335403f69bbSGerhard Engleder * - random MAC address
2336403f69bbSGerhard Engleder */
2337403f69bbSGerhard Engleder retval = of_get_mac_address(adapter->pdev->dev.of_node,
2338403f69bbSGerhard Engleder adapter->mac_address);
2339403f69bbSGerhard Engleder if (retval == -EPROBE_DEFER)
2340403f69bbSGerhard Engleder return retval;
2341403f69bbSGerhard Engleder if (retval && !is_valid_ether_addr(adapter->mac_address)) {
2342403f69bbSGerhard Engleder *(u32 *)adapter->mac_address =
2343403f69bbSGerhard Engleder ioread32(adapter->addr + TSNEP_MAC_ADDRESS_LOW);
2344403f69bbSGerhard Engleder *(u16 *)(adapter->mac_address + sizeof(u32)) =
2345403f69bbSGerhard Engleder ioread16(adapter->addr + TSNEP_MAC_ADDRESS_HIGH);
2346403f69bbSGerhard Engleder if (!is_valid_ether_addr(adapter->mac_address))
2347403f69bbSGerhard Engleder eth_random_addr(adapter->mac_address);
2348403f69bbSGerhard Engleder }
2349403f69bbSGerhard Engleder
2350403f69bbSGerhard Engleder tsnep_mac_set_address(adapter, adapter->mac_address);
23514dfb9982SDavid S. Miller eth_hw_addr_set(adapter->netdev, adapter->mac_address);
2352403f69bbSGerhard Engleder
2353403f69bbSGerhard Engleder return 0;
2354403f69bbSGerhard Engleder }
2355403f69bbSGerhard Engleder
tsnep_mdio_init(struct tsnep_adapter * adapter)2356403f69bbSGerhard Engleder static int tsnep_mdio_init(struct tsnep_adapter *adapter)
2357403f69bbSGerhard Engleder {
2358403f69bbSGerhard Engleder struct device_node *np = adapter->pdev->dev.of_node;
2359403f69bbSGerhard Engleder int retval;
2360403f69bbSGerhard Engleder
2361403f69bbSGerhard Engleder if (np) {
2362403f69bbSGerhard Engleder np = of_get_child_by_name(np, "mdio");
2363403f69bbSGerhard Engleder if (!np)
2364403f69bbSGerhard Engleder return 0;
2365403f69bbSGerhard Engleder
2366403f69bbSGerhard Engleder adapter->suppress_preamble =
2367403f69bbSGerhard Engleder of_property_read_bool(np, "suppress-preamble");
2368403f69bbSGerhard Engleder }
2369403f69bbSGerhard Engleder
2370403f69bbSGerhard Engleder adapter->mdiobus = devm_mdiobus_alloc(&adapter->pdev->dev);
2371403f69bbSGerhard Engleder if (!adapter->mdiobus) {
2372403f69bbSGerhard Engleder retval = -ENOMEM;
2373403f69bbSGerhard Engleder
2374403f69bbSGerhard Engleder goto out;
2375403f69bbSGerhard Engleder }
2376403f69bbSGerhard Engleder
2377403f69bbSGerhard Engleder adapter->mdiobus->priv = (void *)adapter;
2378403f69bbSGerhard Engleder adapter->mdiobus->parent = &adapter->pdev->dev;
2379403f69bbSGerhard Engleder adapter->mdiobus->read = tsnep_mdiobus_read;
2380403f69bbSGerhard Engleder adapter->mdiobus->write = tsnep_mdiobus_write;
2381403f69bbSGerhard Engleder adapter->mdiobus->name = TSNEP "-mdiobus";
2382403f69bbSGerhard Engleder snprintf(adapter->mdiobus->id, MII_BUS_ID_SIZE, "%s",
2383403f69bbSGerhard Engleder adapter->pdev->name);
2384403f69bbSGerhard Engleder
2385403f69bbSGerhard Engleder /* do not scan broadcast address */
2386403f69bbSGerhard Engleder adapter->mdiobus->phy_mask = 0x0000001;
2387403f69bbSGerhard Engleder
2388403f69bbSGerhard Engleder retval = of_mdiobus_register(adapter->mdiobus, np);
2389739752d6SYang Yingliang
2390739752d6SYang Yingliang out:
2391403f69bbSGerhard Engleder of_node_put(np);
2392403f69bbSGerhard Engleder
2393403f69bbSGerhard Engleder return retval;
2394403f69bbSGerhard Engleder }
2395403f69bbSGerhard Engleder
tsnep_phy_init(struct tsnep_adapter * adapter)2396403f69bbSGerhard Engleder static int tsnep_phy_init(struct tsnep_adapter *adapter)
2397403f69bbSGerhard Engleder {
2398403f69bbSGerhard Engleder struct device_node *phy_node;
2399403f69bbSGerhard Engleder int retval;
2400403f69bbSGerhard Engleder
2401403f69bbSGerhard Engleder retval = of_get_phy_mode(adapter->pdev->dev.of_node,
2402403f69bbSGerhard Engleder &adapter->phy_mode);
2403403f69bbSGerhard Engleder if (retval)
2404403f69bbSGerhard Engleder adapter->phy_mode = PHY_INTERFACE_MODE_GMII;
2405403f69bbSGerhard Engleder
2406403f69bbSGerhard Engleder phy_node = of_parse_phandle(adapter->pdev->dev.of_node, "phy-handle",
2407403f69bbSGerhard Engleder 0);
2408403f69bbSGerhard Engleder adapter->phydev = of_phy_find_device(phy_node);
2409403f69bbSGerhard Engleder of_node_put(phy_node);
2410403f69bbSGerhard Engleder if (!adapter->phydev && adapter->mdiobus)
2411403f69bbSGerhard Engleder adapter->phydev = phy_find_first(adapter->mdiobus);
2412403f69bbSGerhard Engleder if (!adapter->phydev)
2413403f69bbSGerhard Engleder return -EIO;
2414403f69bbSGerhard Engleder
2415403f69bbSGerhard Engleder return 0;
2416403f69bbSGerhard Engleder }
2417403f69bbSGerhard Engleder
tsnep_queue_init(struct tsnep_adapter * adapter,int queue_count)241876203137SGerhard Engleder static int tsnep_queue_init(struct tsnep_adapter *adapter, int queue_count)
241976203137SGerhard Engleder {
242076203137SGerhard Engleder u32 irq_mask = ECM_INT_TX_0 | ECM_INT_RX_0;
242176203137SGerhard Engleder char name[8];
242276203137SGerhard Engleder int i;
242376203137SGerhard Engleder int retval;
242476203137SGerhard Engleder
242576203137SGerhard Engleder /* one TX/RX queue pair for netdev is mandatory */
242676203137SGerhard Engleder if (platform_irq_count(adapter->pdev) == 1)
242776203137SGerhard Engleder retval = platform_get_irq(adapter->pdev, 0);
242876203137SGerhard Engleder else
242976203137SGerhard Engleder retval = platform_get_irq_byname(adapter->pdev, "mac");
243076203137SGerhard Engleder if (retval < 0)
243176203137SGerhard Engleder return retval;
243276203137SGerhard Engleder adapter->num_tx_queues = 1;
243376203137SGerhard Engleder adapter->num_rx_queues = 1;
243476203137SGerhard Engleder adapter->num_queues = 1;
243533b0ee02SGerhard Engleder adapter->queue[0].adapter = adapter;
243676203137SGerhard Engleder adapter->queue[0].irq = retval;
243776203137SGerhard Engleder adapter->queue[0].tx = &adapter->tx[0];
243833b0ee02SGerhard Engleder adapter->queue[0].tx->adapter = adapter;
243933b0ee02SGerhard Engleder adapter->queue[0].tx->addr = adapter->addr + TSNEP_QUEUE(0);
244033b0ee02SGerhard Engleder adapter->queue[0].tx->queue_index = 0;
244176203137SGerhard Engleder adapter->queue[0].rx = &adapter->rx[0];
244233b0ee02SGerhard Engleder adapter->queue[0].rx->adapter = adapter;
244333b0ee02SGerhard Engleder adapter->queue[0].rx->addr = adapter->addr + TSNEP_QUEUE(0);
244433b0ee02SGerhard Engleder adapter->queue[0].rx->queue_index = 0;
244576203137SGerhard Engleder adapter->queue[0].irq_mask = irq_mask;
2446d3dfe8d6SGerhard Engleder adapter->queue[0].irq_delay_addr = adapter->addr + ECM_INT_DELAY;
2447d3dfe8d6SGerhard Engleder retval = tsnep_set_irq_coalesce(&adapter->queue[0],
2448d3dfe8d6SGerhard Engleder TSNEP_COALESCE_USECS_DEFAULT);
2449d3dfe8d6SGerhard Engleder if (retval < 0)
2450d3dfe8d6SGerhard Engleder return retval;
245176203137SGerhard Engleder
245276203137SGerhard Engleder adapter->netdev->irq = adapter->queue[0].irq;
245376203137SGerhard Engleder
245476203137SGerhard Engleder /* add additional TX/RX queue pairs only if dedicated interrupt is
245576203137SGerhard Engleder * available
245676203137SGerhard Engleder */
245776203137SGerhard Engleder for (i = 1; i < queue_count; i++) {
245876203137SGerhard Engleder sprintf(name, "txrx-%d", i);
245976203137SGerhard Engleder retval = platform_get_irq_byname_optional(adapter->pdev, name);
246076203137SGerhard Engleder if (retval < 0)
246176203137SGerhard Engleder break;
246276203137SGerhard Engleder
246376203137SGerhard Engleder adapter->num_tx_queues++;
246476203137SGerhard Engleder adapter->num_rx_queues++;
246576203137SGerhard Engleder adapter->num_queues++;
246633b0ee02SGerhard Engleder adapter->queue[i].adapter = adapter;
246776203137SGerhard Engleder adapter->queue[i].irq = retval;
246876203137SGerhard Engleder adapter->queue[i].tx = &adapter->tx[i];
246933b0ee02SGerhard Engleder adapter->queue[i].tx->adapter = adapter;
247033b0ee02SGerhard Engleder adapter->queue[i].tx->addr = adapter->addr + TSNEP_QUEUE(i);
247133b0ee02SGerhard Engleder adapter->queue[i].tx->queue_index = i;
247276203137SGerhard Engleder adapter->queue[i].rx = &adapter->rx[i];
247333b0ee02SGerhard Engleder adapter->queue[i].rx->adapter = adapter;
247433b0ee02SGerhard Engleder adapter->queue[i].rx->addr = adapter->addr + TSNEP_QUEUE(i);
247533b0ee02SGerhard Engleder adapter->queue[i].rx->queue_index = i;
247676203137SGerhard Engleder adapter->queue[i].irq_mask =
247776203137SGerhard Engleder irq_mask << (ECM_INT_TXRX_SHIFT * i);
2478d3dfe8d6SGerhard Engleder adapter->queue[i].irq_delay_addr =
2479d3dfe8d6SGerhard Engleder adapter->addr + ECM_INT_DELAY + ECM_INT_DELAY_OFFSET * i;
2480d3dfe8d6SGerhard Engleder retval = tsnep_set_irq_coalesce(&adapter->queue[i],
2481d3dfe8d6SGerhard Engleder TSNEP_COALESCE_USECS_DEFAULT);
2482d3dfe8d6SGerhard Engleder if (retval < 0)
2483d3dfe8d6SGerhard Engleder return retval;
248476203137SGerhard Engleder }
248576203137SGerhard Engleder
248676203137SGerhard Engleder return 0;
248776203137SGerhard Engleder }
248876203137SGerhard Engleder
tsnep_probe(struct platform_device * pdev)2489403f69bbSGerhard Engleder static int tsnep_probe(struct platform_device *pdev)
2490403f69bbSGerhard Engleder {
2491403f69bbSGerhard Engleder struct tsnep_adapter *adapter;
2492403f69bbSGerhard Engleder struct net_device *netdev;
2493403f69bbSGerhard Engleder struct resource *io;
2494403f69bbSGerhard Engleder u32 type;
2495403f69bbSGerhard Engleder int revision;
2496403f69bbSGerhard Engleder int version;
249776203137SGerhard Engleder int queue_count;
2498403f69bbSGerhard Engleder int retval;
2499403f69bbSGerhard Engleder
2500403f69bbSGerhard Engleder netdev = devm_alloc_etherdev_mqs(&pdev->dev,
2501403f69bbSGerhard Engleder sizeof(struct tsnep_adapter),
2502403f69bbSGerhard Engleder TSNEP_MAX_QUEUES, TSNEP_MAX_QUEUES);
2503403f69bbSGerhard Engleder if (!netdev)
2504403f69bbSGerhard Engleder return -ENODEV;
2505403f69bbSGerhard Engleder SET_NETDEV_DEV(netdev, &pdev->dev);
2506403f69bbSGerhard Engleder adapter = netdev_priv(netdev);
2507403f69bbSGerhard Engleder platform_set_drvdata(pdev, adapter);
2508403f69bbSGerhard Engleder adapter->pdev = pdev;
2509403f69bbSGerhard Engleder adapter->dmadev = &pdev->dev;
2510403f69bbSGerhard Engleder adapter->netdev = netdev;
2511403f69bbSGerhard Engleder adapter->msg_enable = NETIF_MSG_DRV | NETIF_MSG_PROBE |
2512403f69bbSGerhard Engleder NETIF_MSG_LINK | NETIF_MSG_IFUP |
2513403f69bbSGerhard Engleder NETIF_MSG_IFDOWN | NETIF_MSG_TX_QUEUED;
2514403f69bbSGerhard Engleder
2515403f69bbSGerhard Engleder netdev->min_mtu = ETH_MIN_MTU;
2516403f69bbSGerhard Engleder netdev->max_mtu = TSNEP_MAX_FRAME_SIZE;
2517403f69bbSGerhard Engleder
2518403f69bbSGerhard Engleder mutex_init(&adapter->gate_control_lock);
2519308ce142SGerhard Engleder mutex_init(&adapter->rxnfc_lock);
2520308ce142SGerhard Engleder INIT_LIST_HEAD(&adapter->rxnfc_rules);
2521403f69bbSGerhard Engleder
2522403f69bbSGerhard Engleder io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
2523403f69bbSGerhard Engleder adapter->addr = devm_ioremap_resource(&pdev->dev, io);
2524403f69bbSGerhard Engleder if (IS_ERR(adapter->addr))
2525403f69bbSGerhard Engleder return PTR_ERR(adapter->addr);
2526403f69bbSGerhard Engleder netdev->mem_start = io->start;
2527403f69bbSGerhard Engleder netdev->mem_end = io->end;
2528403f69bbSGerhard Engleder
2529403f69bbSGerhard Engleder type = ioread32(adapter->addr + ECM_TYPE);
2530403f69bbSGerhard Engleder revision = (type & ECM_REVISION_MASK) >> ECM_REVISION_SHIFT;
2531403f69bbSGerhard Engleder version = (type & ECM_VERSION_MASK) >> ECM_VERSION_SHIFT;
253276203137SGerhard Engleder queue_count = (type & ECM_QUEUE_COUNT_MASK) >> ECM_QUEUE_COUNT_SHIFT;
2533403f69bbSGerhard Engleder adapter->gate_control = type & ECM_GATE_CONTROL;
2534308ce142SGerhard Engleder adapter->rxnfc_max = TSNEP_RX_ASSIGN_ETHER_TYPE_COUNT;
2535403f69bbSGerhard Engleder
253658eaa8abSGerhard Engleder tsnep_disable_irq(adapter, ECM_INT_ALL);
253758eaa8abSGerhard Engleder
253876203137SGerhard Engleder retval = tsnep_queue_init(adapter, queue_count);
253976203137SGerhard Engleder if (retval)
254076203137SGerhard Engleder return retval;
254176203137SGerhard Engleder
254217531519SGerhard Engleder retval = dma_set_mask_and_coherent(&adapter->pdev->dev,
254317531519SGerhard Engleder DMA_BIT_MASK(64));
254417531519SGerhard Engleder if (retval) {
254517531519SGerhard Engleder dev_err(&adapter->pdev->dev, "no usable DMA configuration.\n");
254617531519SGerhard Engleder return retval;
254717531519SGerhard Engleder }
254817531519SGerhard Engleder
2549403f69bbSGerhard Engleder retval = tsnep_mac_init(adapter);
2550403f69bbSGerhard Engleder if (retval)
255158eaa8abSGerhard Engleder return retval;
2552403f69bbSGerhard Engleder
2553403f69bbSGerhard Engleder retval = tsnep_mdio_init(adapter);
2554403f69bbSGerhard Engleder if (retval)
2555403f69bbSGerhard Engleder goto mdio_init_failed;
2556403f69bbSGerhard Engleder
2557403f69bbSGerhard Engleder retval = tsnep_phy_init(adapter);
2558403f69bbSGerhard Engleder if (retval)
2559403f69bbSGerhard Engleder goto phy_init_failed;
2560403f69bbSGerhard Engleder
2561403f69bbSGerhard Engleder retval = tsnep_ptp_init(adapter);
2562403f69bbSGerhard Engleder if (retval)
2563403f69bbSGerhard Engleder goto ptp_init_failed;
2564403f69bbSGerhard Engleder
2565403f69bbSGerhard Engleder retval = tsnep_tc_init(adapter);
2566403f69bbSGerhard Engleder if (retval)
2567403f69bbSGerhard Engleder goto tc_init_failed;
2568403f69bbSGerhard Engleder
2569308ce142SGerhard Engleder retval = tsnep_rxnfc_init(adapter);
2570308ce142SGerhard Engleder if (retval)
2571308ce142SGerhard Engleder goto rxnfc_init_failed;
2572308ce142SGerhard Engleder
2573403f69bbSGerhard Engleder netdev->netdev_ops = &tsnep_netdev_ops;
2574403f69bbSGerhard Engleder netdev->ethtool_ops = &tsnep_ethtool_ops;
2575403f69bbSGerhard Engleder netdev->features = NETIF_F_SG;
25764b222008SGerhard Engleder netdev->hw_features = netdev->features | NETIF_F_LOOPBACK;
2577403f69bbSGerhard Engleder
257866c0e13aSMarek Majtyka netdev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT |
257966c0e13aSMarek Majtyka NETDEV_XDP_ACT_NDO_XMIT |
2580cd275c23SGerhard Engleder NETDEV_XDP_ACT_NDO_XMIT_SG |
2581cd275c23SGerhard Engleder NETDEV_XDP_ACT_XSK_ZEROCOPY;
258266c0e13aSMarek Majtyka
2583403f69bbSGerhard Engleder /* carrier off reporting is important to ethtool even BEFORE open */
2584403f69bbSGerhard Engleder netif_carrier_off(netdev);
2585403f69bbSGerhard Engleder
2586403f69bbSGerhard Engleder retval = register_netdev(netdev);
2587403f69bbSGerhard Engleder if (retval)
2588403f69bbSGerhard Engleder goto register_failed;
2589403f69bbSGerhard Engleder
2590403f69bbSGerhard Engleder dev_info(&adapter->pdev->dev, "device version %d.%02d\n", version,
2591403f69bbSGerhard Engleder revision);
2592403f69bbSGerhard Engleder if (adapter->gate_control)
2593403f69bbSGerhard Engleder dev_info(&adapter->pdev->dev, "gate control detected\n");
2594403f69bbSGerhard Engleder
2595403f69bbSGerhard Engleder return 0;
2596403f69bbSGerhard Engleder
2597403f69bbSGerhard Engleder register_failed:
2598308ce142SGerhard Engleder tsnep_rxnfc_cleanup(adapter);
2599308ce142SGerhard Engleder rxnfc_init_failed:
2600403f69bbSGerhard Engleder tsnep_tc_cleanup(adapter);
2601403f69bbSGerhard Engleder tc_init_failed:
2602403f69bbSGerhard Engleder tsnep_ptp_cleanup(adapter);
2603403f69bbSGerhard Engleder ptp_init_failed:
2604403f69bbSGerhard Engleder phy_init_failed:
2605403f69bbSGerhard Engleder if (adapter->mdiobus)
2606403f69bbSGerhard Engleder mdiobus_unregister(adapter->mdiobus);
2607403f69bbSGerhard Engleder mdio_init_failed:
2608403f69bbSGerhard Engleder return retval;
2609403f69bbSGerhard Engleder }
2610403f69bbSGerhard Engleder
tsnep_remove(struct platform_device * pdev)2611403f69bbSGerhard Engleder static int tsnep_remove(struct platform_device *pdev)
2612403f69bbSGerhard Engleder {
2613403f69bbSGerhard Engleder struct tsnep_adapter *adapter = platform_get_drvdata(pdev);
2614403f69bbSGerhard Engleder
2615403f69bbSGerhard Engleder unregister_netdev(adapter->netdev);
2616403f69bbSGerhard Engleder
2617308ce142SGerhard Engleder tsnep_rxnfc_cleanup(adapter);
2618308ce142SGerhard Engleder
2619403f69bbSGerhard Engleder tsnep_tc_cleanup(adapter);
2620403f69bbSGerhard Engleder
2621403f69bbSGerhard Engleder tsnep_ptp_cleanup(adapter);
2622403f69bbSGerhard Engleder
2623403f69bbSGerhard Engleder if (adapter->mdiobus)
2624403f69bbSGerhard Engleder mdiobus_unregister(adapter->mdiobus);
2625403f69bbSGerhard Engleder
2626403f69bbSGerhard Engleder tsnep_disable_irq(adapter, ECM_INT_ALL);
2627403f69bbSGerhard Engleder
2628403f69bbSGerhard Engleder return 0;
2629403f69bbSGerhard Engleder }
2630403f69bbSGerhard Engleder
2631403f69bbSGerhard Engleder static const struct of_device_id tsnep_of_match[] = {
2632403f69bbSGerhard Engleder { .compatible = "engleder,tsnep", },
2633403f69bbSGerhard Engleder { },
2634403f69bbSGerhard Engleder };
2635403f69bbSGerhard Engleder MODULE_DEVICE_TABLE(of, tsnep_of_match);
2636403f69bbSGerhard Engleder
2637403f69bbSGerhard Engleder static struct platform_driver tsnep_driver = {
2638403f69bbSGerhard Engleder .driver = {
2639403f69bbSGerhard Engleder .name = TSNEP,
264073afd781SGerhard Engleder .of_match_table = tsnep_of_match,
2641403f69bbSGerhard Engleder },
2642403f69bbSGerhard Engleder .probe = tsnep_probe,
2643403f69bbSGerhard Engleder .remove = tsnep_remove,
2644403f69bbSGerhard Engleder };
2645403f69bbSGerhard Engleder module_platform_driver(tsnep_driver);
2646403f69bbSGerhard Engleder
2647403f69bbSGerhard Engleder MODULE_AUTHOR("Gerhard Engleder <gerhard@engleder-embedded.com>");
2648403f69bbSGerhard Engleder MODULE_DESCRIPTION("TSN endpoint Ethernet MAC driver");
2649403f69bbSGerhard Engleder MODULE_LICENSE("GPL");
2650