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