162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Xilinx Axi Ethernet device driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2008 Nissin Systems Co., Ltd., Yoshio Kashiwagi 662306a36Sopenharmony_ci * Copyright (c) 2005-2008 DLA Systems, David H. Lynch Jr. <dhlii@dlasys.net> 762306a36Sopenharmony_ci * Copyright (c) 2008-2009 Secret Lab Technologies Ltd. 862306a36Sopenharmony_ci * Copyright (c) 2010 - 2011 Michal Simek <monstr@monstr.eu> 962306a36Sopenharmony_ci * Copyright (c) 2010 - 2011 PetaLogix 1062306a36Sopenharmony_ci * Copyright (c) 2019 - 2022 Calian Advanced Technologies 1162306a36Sopenharmony_ci * Copyright (c) 2010 - 2012 Xilinx, Inc. All rights reserved. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * This is a driver for the Xilinx Axi Ethernet which is used in the Virtex6 1462306a36Sopenharmony_ci * and Spartan6. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * TODO: 1762306a36Sopenharmony_ci * - Add Axi Fifo support. 1862306a36Sopenharmony_ci * - Factor out Axi DMA code into separate driver. 1962306a36Sopenharmony_ci * - Test and fix basic multicast filtering. 2062306a36Sopenharmony_ci * - Add support for extended multicast filtering. 2162306a36Sopenharmony_ci * - Test basic VLAN support. 2262306a36Sopenharmony_ci * - Add support for extended VLAN support. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/clk.h> 2662306a36Sopenharmony_ci#include <linux/delay.h> 2762306a36Sopenharmony_ci#include <linux/etherdevice.h> 2862306a36Sopenharmony_ci#include <linux/module.h> 2962306a36Sopenharmony_ci#include <linux/netdevice.h> 3062306a36Sopenharmony_ci#include <linux/of.h> 3162306a36Sopenharmony_ci#include <linux/of_mdio.h> 3262306a36Sopenharmony_ci#include <linux/of_net.h> 3362306a36Sopenharmony_ci#include <linux/of_irq.h> 3462306a36Sopenharmony_ci#include <linux/of_address.h> 3562306a36Sopenharmony_ci#include <linux/platform_device.h> 3662306a36Sopenharmony_ci#include <linux/skbuff.h> 3762306a36Sopenharmony_ci#include <linux/math64.h> 3862306a36Sopenharmony_ci#include <linux/phy.h> 3962306a36Sopenharmony_ci#include <linux/mii.h> 4062306a36Sopenharmony_ci#include <linux/ethtool.h> 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#include "xilinx_axienet.h" 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* Descriptors defines for Tx and Rx DMA */ 4562306a36Sopenharmony_ci#define TX_BD_NUM_DEFAULT 128 4662306a36Sopenharmony_ci#define RX_BD_NUM_DEFAULT 1024 4762306a36Sopenharmony_ci#define TX_BD_NUM_MIN (MAX_SKB_FRAGS + 1) 4862306a36Sopenharmony_ci#define TX_BD_NUM_MAX 4096 4962306a36Sopenharmony_ci#define RX_BD_NUM_MAX 4096 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* Must be shorter than length of ethtool_drvinfo.driver field to fit */ 5262306a36Sopenharmony_ci#define DRIVER_NAME "xaxienet" 5362306a36Sopenharmony_ci#define DRIVER_DESCRIPTION "Xilinx Axi Ethernet driver" 5462306a36Sopenharmony_ci#define DRIVER_VERSION "1.00a" 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define AXIENET_REGS_N 40 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* Match table for of_platform binding */ 5962306a36Sopenharmony_cistatic const struct of_device_id axienet_of_match[] = { 6062306a36Sopenharmony_ci { .compatible = "xlnx,axi-ethernet-1.00.a", }, 6162306a36Sopenharmony_ci { .compatible = "xlnx,axi-ethernet-1.01.a", }, 6262306a36Sopenharmony_ci { .compatible = "xlnx,axi-ethernet-2.01.a", }, 6362306a36Sopenharmony_ci {}, 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, axienet_of_match); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* Option table for setting up Axi Ethernet hardware options */ 6962306a36Sopenharmony_cistatic struct axienet_option axienet_options[] = { 7062306a36Sopenharmony_ci /* Turn on jumbo packet support for both Rx and Tx */ 7162306a36Sopenharmony_ci { 7262306a36Sopenharmony_ci .opt = XAE_OPTION_JUMBO, 7362306a36Sopenharmony_ci .reg = XAE_TC_OFFSET, 7462306a36Sopenharmony_ci .m_or = XAE_TC_JUM_MASK, 7562306a36Sopenharmony_ci }, { 7662306a36Sopenharmony_ci .opt = XAE_OPTION_JUMBO, 7762306a36Sopenharmony_ci .reg = XAE_RCW1_OFFSET, 7862306a36Sopenharmony_ci .m_or = XAE_RCW1_JUM_MASK, 7962306a36Sopenharmony_ci }, { /* Turn on VLAN packet support for both Rx and Tx */ 8062306a36Sopenharmony_ci .opt = XAE_OPTION_VLAN, 8162306a36Sopenharmony_ci .reg = XAE_TC_OFFSET, 8262306a36Sopenharmony_ci .m_or = XAE_TC_VLAN_MASK, 8362306a36Sopenharmony_ci }, { 8462306a36Sopenharmony_ci .opt = XAE_OPTION_VLAN, 8562306a36Sopenharmony_ci .reg = XAE_RCW1_OFFSET, 8662306a36Sopenharmony_ci .m_or = XAE_RCW1_VLAN_MASK, 8762306a36Sopenharmony_ci }, { /* Turn on FCS stripping on receive packets */ 8862306a36Sopenharmony_ci .opt = XAE_OPTION_FCS_STRIP, 8962306a36Sopenharmony_ci .reg = XAE_RCW1_OFFSET, 9062306a36Sopenharmony_ci .m_or = XAE_RCW1_FCS_MASK, 9162306a36Sopenharmony_ci }, { /* Turn on FCS insertion on transmit packets */ 9262306a36Sopenharmony_ci .opt = XAE_OPTION_FCS_INSERT, 9362306a36Sopenharmony_ci .reg = XAE_TC_OFFSET, 9462306a36Sopenharmony_ci .m_or = XAE_TC_FCS_MASK, 9562306a36Sopenharmony_ci }, { /* Turn off length/type field checking on receive packets */ 9662306a36Sopenharmony_ci .opt = XAE_OPTION_LENTYPE_ERR, 9762306a36Sopenharmony_ci .reg = XAE_RCW1_OFFSET, 9862306a36Sopenharmony_ci .m_or = XAE_RCW1_LT_DIS_MASK, 9962306a36Sopenharmony_ci }, { /* Turn on Rx flow control */ 10062306a36Sopenharmony_ci .opt = XAE_OPTION_FLOW_CONTROL, 10162306a36Sopenharmony_ci .reg = XAE_FCC_OFFSET, 10262306a36Sopenharmony_ci .m_or = XAE_FCC_FCRX_MASK, 10362306a36Sopenharmony_ci }, { /* Turn on Tx flow control */ 10462306a36Sopenharmony_ci .opt = XAE_OPTION_FLOW_CONTROL, 10562306a36Sopenharmony_ci .reg = XAE_FCC_OFFSET, 10662306a36Sopenharmony_ci .m_or = XAE_FCC_FCTX_MASK, 10762306a36Sopenharmony_ci }, { /* Turn on promiscuous frame filtering */ 10862306a36Sopenharmony_ci .opt = XAE_OPTION_PROMISC, 10962306a36Sopenharmony_ci .reg = XAE_FMI_OFFSET, 11062306a36Sopenharmony_ci .m_or = XAE_FMI_PM_MASK, 11162306a36Sopenharmony_ci }, { /* Enable transmitter */ 11262306a36Sopenharmony_ci .opt = XAE_OPTION_TXEN, 11362306a36Sopenharmony_ci .reg = XAE_TC_OFFSET, 11462306a36Sopenharmony_ci .m_or = XAE_TC_TX_MASK, 11562306a36Sopenharmony_ci }, { /* Enable receiver */ 11662306a36Sopenharmony_ci .opt = XAE_OPTION_RXEN, 11762306a36Sopenharmony_ci .reg = XAE_RCW1_OFFSET, 11862306a36Sopenharmony_ci .m_or = XAE_RCW1_RX_MASK, 11962306a36Sopenharmony_ci }, 12062306a36Sopenharmony_ci {} 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/** 12462306a36Sopenharmony_ci * axienet_dma_in32 - Memory mapped Axi DMA register read 12562306a36Sopenharmony_ci * @lp: Pointer to axienet local structure 12662306a36Sopenharmony_ci * @reg: Address offset from the base address of the Axi DMA core 12762306a36Sopenharmony_ci * 12862306a36Sopenharmony_ci * Return: The contents of the Axi DMA register 12962306a36Sopenharmony_ci * 13062306a36Sopenharmony_ci * This function returns the contents of the corresponding Axi DMA register. 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_cistatic inline u32 axienet_dma_in32(struct axienet_local *lp, off_t reg) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci return ioread32(lp->dma_regs + reg); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic void desc_set_phys_addr(struct axienet_local *lp, dma_addr_t addr, 13862306a36Sopenharmony_ci struct axidma_bd *desc) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci desc->phys = lower_32_bits(addr); 14162306a36Sopenharmony_ci if (lp->features & XAE_FEATURE_DMA_64BIT) 14262306a36Sopenharmony_ci desc->phys_msb = upper_32_bits(addr); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic dma_addr_t desc_get_phys_addr(struct axienet_local *lp, 14662306a36Sopenharmony_ci struct axidma_bd *desc) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci dma_addr_t ret = desc->phys; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (lp->features & XAE_FEATURE_DMA_64BIT) 15162306a36Sopenharmony_ci ret |= ((dma_addr_t)desc->phys_msb << 16) << 16; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return ret; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/** 15762306a36Sopenharmony_ci * axienet_dma_bd_release - Release buffer descriptor rings 15862306a36Sopenharmony_ci * @ndev: Pointer to the net_device structure 15962306a36Sopenharmony_ci * 16062306a36Sopenharmony_ci * This function is used to release the descriptors allocated in 16162306a36Sopenharmony_ci * axienet_dma_bd_init. axienet_dma_bd_release is called when Axi Ethernet 16262306a36Sopenharmony_ci * driver stop api is called. 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_cistatic void axienet_dma_bd_release(struct net_device *ndev) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci int i; 16762306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* If we end up here, tx_bd_v must have been DMA allocated. */ 17062306a36Sopenharmony_ci dma_free_coherent(lp->dev, 17162306a36Sopenharmony_ci sizeof(*lp->tx_bd_v) * lp->tx_bd_num, 17262306a36Sopenharmony_ci lp->tx_bd_v, 17362306a36Sopenharmony_ci lp->tx_bd_p); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (!lp->rx_bd_v) 17662306a36Sopenharmony_ci return; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci for (i = 0; i < lp->rx_bd_num; i++) { 17962306a36Sopenharmony_ci dma_addr_t phys; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* A NULL skb means this descriptor has not been initialised 18262306a36Sopenharmony_ci * at all. 18362306a36Sopenharmony_ci */ 18462306a36Sopenharmony_ci if (!lp->rx_bd_v[i].skb) 18562306a36Sopenharmony_ci break; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci dev_kfree_skb(lp->rx_bd_v[i].skb); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* For each descriptor, we programmed cntrl with the (non-zero) 19062306a36Sopenharmony_ci * descriptor size, after it had been successfully allocated. 19162306a36Sopenharmony_ci * So a non-zero value in there means we need to unmap it. 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ci if (lp->rx_bd_v[i].cntrl) { 19462306a36Sopenharmony_ci phys = desc_get_phys_addr(lp, &lp->rx_bd_v[i]); 19562306a36Sopenharmony_ci dma_unmap_single(lp->dev, phys, 19662306a36Sopenharmony_ci lp->max_frm_size, DMA_FROM_DEVICE); 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci dma_free_coherent(lp->dev, 20162306a36Sopenharmony_ci sizeof(*lp->rx_bd_v) * lp->rx_bd_num, 20262306a36Sopenharmony_ci lp->rx_bd_v, 20362306a36Sopenharmony_ci lp->rx_bd_p); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/** 20762306a36Sopenharmony_ci * axienet_usec_to_timer - Calculate IRQ delay timer value 20862306a36Sopenharmony_ci * @lp: Pointer to the axienet_local structure 20962306a36Sopenharmony_ci * @coalesce_usec: Microseconds to convert into timer value 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_cistatic u32 axienet_usec_to_timer(struct axienet_local *lp, u32 coalesce_usec) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci u32 result; 21462306a36Sopenharmony_ci u64 clk_rate = 125000000; /* arbitrary guess if no clock rate set */ 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (lp->axi_clk) 21762306a36Sopenharmony_ci clk_rate = clk_get_rate(lp->axi_clk); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* 1 Timeout Interval = 125 * (clock period of SG clock) */ 22062306a36Sopenharmony_ci result = DIV64_U64_ROUND_CLOSEST((u64)coalesce_usec * clk_rate, 22162306a36Sopenharmony_ci (u64)125000000); 22262306a36Sopenharmony_ci if (result > 255) 22362306a36Sopenharmony_ci result = 255; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return result; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci/** 22962306a36Sopenharmony_ci * axienet_dma_start - Set up DMA registers and start DMA operation 23062306a36Sopenharmony_ci * @lp: Pointer to the axienet_local structure 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_cistatic void axienet_dma_start(struct axienet_local *lp) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci /* Start updating the Rx channel control register */ 23562306a36Sopenharmony_ci lp->rx_dma_cr = (lp->coalesce_count_rx << XAXIDMA_COALESCE_SHIFT) | 23662306a36Sopenharmony_ci XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_ERROR_MASK; 23762306a36Sopenharmony_ci /* Only set interrupt delay timer if not generating an interrupt on 23862306a36Sopenharmony_ci * the first RX packet. Otherwise leave at 0 to disable delay interrupt. 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ci if (lp->coalesce_count_rx > 1) 24162306a36Sopenharmony_ci lp->rx_dma_cr |= (axienet_usec_to_timer(lp, lp->coalesce_usec_rx) 24262306a36Sopenharmony_ci << XAXIDMA_DELAY_SHIFT) | 24362306a36Sopenharmony_ci XAXIDMA_IRQ_DELAY_MASK; 24462306a36Sopenharmony_ci axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, lp->rx_dma_cr); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* Start updating the Tx channel control register */ 24762306a36Sopenharmony_ci lp->tx_dma_cr = (lp->coalesce_count_tx << XAXIDMA_COALESCE_SHIFT) | 24862306a36Sopenharmony_ci XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_ERROR_MASK; 24962306a36Sopenharmony_ci /* Only set interrupt delay timer if not generating an interrupt on 25062306a36Sopenharmony_ci * the first TX packet. Otherwise leave at 0 to disable delay interrupt. 25162306a36Sopenharmony_ci */ 25262306a36Sopenharmony_ci if (lp->coalesce_count_tx > 1) 25362306a36Sopenharmony_ci lp->tx_dma_cr |= (axienet_usec_to_timer(lp, lp->coalesce_usec_tx) 25462306a36Sopenharmony_ci << XAXIDMA_DELAY_SHIFT) | 25562306a36Sopenharmony_ci XAXIDMA_IRQ_DELAY_MASK; 25662306a36Sopenharmony_ci axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, lp->tx_dma_cr); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Populate the tail pointer and bring the Rx Axi DMA engine out of 25962306a36Sopenharmony_ci * halted state. This will make the Rx side ready for reception. 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_ci axienet_dma_out_addr(lp, XAXIDMA_RX_CDESC_OFFSET, lp->rx_bd_p); 26262306a36Sopenharmony_ci lp->rx_dma_cr |= XAXIDMA_CR_RUNSTOP_MASK; 26362306a36Sopenharmony_ci axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, lp->rx_dma_cr); 26462306a36Sopenharmony_ci axienet_dma_out_addr(lp, XAXIDMA_RX_TDESC_OFFSET, lp->rx_bd_p + 26562306a36Sopenharmony_ci (sizeof(*lp->rx_bd_v) * (lp->rx_bd_num - 1))); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* Write to the RS (Run-stop) bit in the Tx channel control register. 26862306a36Sopenharmony_ci * Tx channel is now ready to run. But only after we write to the 26962306a36Sopenharmony_ci * tail pointer register that the Tx channel will start transmitting. 27062306a36Sopenharmony_ci */ 27162306a36Sopenharmony_ci axienet_dma_out_addr(lp, XAXIDMA_TX_CDESC_OFFSET, lp->tx_bd_p); 27262306a36Sopenharmony_ci lp->tx_dma_cr |= XAXIDMA_CR_RUNSTOP_MASK; 27362306a36Sopenharmony_ci axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, lp->tx_dma_cr); 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci/** 27762306a36Sopenharmony_ci * axienet_dma_bd_init - Setup buffer descriptor rings for Axi DMA 27862306a36Sopenharmony_ci * @ndev: Pointer to the net_device structure 27962306a36Sopenharmony_ci * 28062306a36Sopenharmony_ci * Return: 0, on success -ENOMEM, on failure 28162306a36Sopenharmony_ci * 28262306a36Sopenharmony_ci * This function is called to initialize the Rx and Tx DMA descriptor 28362306a36Sopenharmony_ci * rings. This initializes the descriptors with required default values 28462306a36Sopenharmony_ci * and is called when Axi Ethernet driver reset is called. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_cistatic int axienet_dma_bd_init(struct net_device *ndev) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci int i; 28962306a36Sopenharmony_ci struct sk_buff *skb; 29062306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* Reset the indexes which are used for accessing the BDs */ 29362306a36Sopenharmony_ci lp->tx_bd_ci = 0; 29462306a36Sopenharmony_ci lp->tx_bd_tail = 0; 29562306a36Sopenharmony_ci lp->rx_bd_ci = 0; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* Allocate the Tx and Rx buffer descriptors. */ 29862306a36Sopenharmony_ci lp->tx_bd_v = dma_alloc_coherent(lp->dev, 29962306a36Sopenharmony_ci sizeof(*lp->tx_bd_v) * lp->tx_bd_num, 30062306a36Sopenharmony_ci &lp->tx_bd_p, GFP_KERNEL); 30162306a36Sopenharmony_ci if (!lp->tx_bd_v) 30262306a36Sopenharmony_ci return -ENOMEM; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci lp->rx_bd_v = dma_alloc_coherent(lp->dev, 30562306a36Sopenharmony_ci sizeof(*lp->rx_bd_v) * lp->rx_bd_num, 30662306a36Sopenharmony_ci &lp->rx_bd_p, GFP_KERNEL); 30762306a36Sopenharmony_ci if (!lp->rx_bd_v) 30862306a36Sopenharmony_ci goto out; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci for (i = 0; i < lp->tx_bd_num; i++) { 31162306a36Sopenharmony_ci dma_addr_t addr = lp->tx_bd_p + 31262306a36Sopenharmony_ci sizeof(*lp->tx_bd_v) * 31362306a36Sopenharmony_ci ((i + 1) % lp->tx_bd_num); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci lp->tx_bd_v[i].next = lower_32_bits(addr); 31662306a36Sopenharmony_ci if (lp->features & XAE_FEATURE_DMA_64BIT) 31762306a36Sopenharmony_ci lp->tx_bd_v[i].next_msb = upper_32_bits(addr); 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci for (i = 0; i < lp->rx_bd_num; i++) { 32162306a36Sopenharmony_ci dma_addr_t addr; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci addr = lp->rx_bd_p + sizeof(*lp->rx_bd_v) * 32462306a36Sopenharmony_ci ((i + 1) % lp->rx_bd_num); 32562306a36Sopenharmony_ci lp->rx_bd_v[i].next = lower_32_bits(addr); 32662306a36Sopenharmony_ci if (lp->features & XAE_FEATURE_DMA_64BIT) 32762306a36Sopenharmony_ci lp->rx_bd_v[i].next_msb = upper_32_bits(addr); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci skb = netdev_alloc_skb_ip_align(ndev, lp->max_frm_size); 33062306a36Sopenharmony_ci if (!skb) 33162306a36Sopenharmony_ci goto out; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci lp->rx_bd_v[i].skb = skb; 33462306a36Sopenharmony_ci addr = dma_map_single(lp->dev, skb->data, 33562306a36Sopenharmony_ci lp->max_frm_size, DMA_FROM_DEVICE); 33662306a36Sopenharmony_ci if (dma_mapping_error(lp->dev, addr)) { 33762306a36Sopenharmony_ci netdev_err(ndev, "DMA mapping error\n"); 33862306a36Sopenharmony_ci goto out; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci desc_set_phys_addr(lp, addr, &lp->rx_bd_v[i]); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci lp->rx_bd_v[i].cntrl = lp->max_frm_size; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci axienet_dma_start(lp); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return 0; 34862306a36Sopenharmony_ciout: 34962306a36Sopenharmony_ci axienet_dma_bd_release(ndev); 35062306a36Sopenharmony_ci return -ENOMEM; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci/** 35462306a36Sopenharmony_ci * axienet_set_mac_address - Write the MAC address 35562306a36Sopenharmony_ci * @ndev: Pointer to the net_device structure 35662306a36Sopenharmony_ci * @address: 6 byte Address to be written as MAC address 35762306a36Sopenharmony_ci * 35862306a36Sopenharmony_ci * This function is called to initialize the MAC address of the Axi Ethernet 35962306a36Sopenharmony_ci * core. It writes to the UAW0 and UAW1 registers of the core. 36062306a36Sopenharmony_ci */ 36162306a36Sopenharmony_cistatic void axienet_set_mac_address(struct net_device *ndev, 36262306a36Sopenharmony_ci const void *address) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (address) 36762306a36Sopenharmony_ci eth_hw_addr_set(ndev, address); 36862306a36Sopenharmony_ci if (!is_valid_ether_addr(ndev->dev_addr)) 36962306a36Sopenharmony_ci eth_hw_addr_random(ndev); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* Set up unicast MAC address filter set its mac address */ 37262306a36Sopenharmony_ci axienet_iow(lp, XAE_UAW0_OFFSET, 37362306a36Sopenharmony_ci (ndev->dev_addr[0]) | 37462306a36Sopenharmony_ci (ndev->dev_addr[1] << 8) | 37562306a36Sopenharmony_ci (ndev->dev_addr[2] << 16) | 37662306a36Sopenharmony_ci (ndev->dev_addr[3] << 24)); 37762306a36Sopenharmony_ci axienet_iow(lp, XAE_UAW1_OFFSET, 37862306a36Sopenharmony_ci (((axienet_ior(lp, XAE_UAW1_OFFSET)) & 37962306a36Sopenharmony_ci ~XAE_UAW1_UNICASTADDR_MASK) | 38062306a36Sopenharmony_ci (ndev->dev_addr[4] | 38162306a36Sopenharmony_ci (ndev->dev_addr[5] << 8)))); 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci/** 38562306a36Sopenharmony_ci * netdev_set_mac_address - Write the MAC address (from outside the driver) 38662306a36Sopenharmony_ci * @ndev: Pointer to the net_device structure 38762306a36Sopenharmony_ci * @p: 6 byte Address to be written as MAC address 38862306a36Sopenharmony_ci * 38962306a36Sopenharmony_ci * Return: 0 for all conditions. Presently, there is no failure case. 39062306a36Sopenharmony_ci * 39162306a36Sopenharmony_ci * This function is called to initialize the MAC address of the Axi Ethernet 39262306a36Sopenharmony_ci * core. It calls the core specific axienet_set_mac_address. This is the 39362306a36Sopenharmony_ci * function that goes into net_device_ops structure entry ndo_set_mac_address. 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_cistatic int netdev_set_mac_address(struct net_device *ndev, void *p) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct sockaddr *addr = p; 39862306a36Sopenharmony_ci axienet_set_mac_address(ndev, addr->sa_data); 39962306a36Sopenharmony_ci return 0; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci/** 40362306a36Sopenharmony_ci * axienet_set_multicast_list - Prepare the multicast table 40462306a36Sopenharmony_ci * @ndev: Pointer to the net_device structure 40562306a36Sopenharmony_ci * 40662306a36Sopenharmony_ci * This function is called to initialize the multicast table during 40762306a36Sopenharmony_ci * initialization. The Axi Ethernet basic multicast support has a four-entry 40862306a36Sopenharmony_ci * multicast table which is initialized here. Additionally this function 40962306a36Sopenharmony_ci * goes into the net_device_ops structure entry ndo_set_multicast_list. This 41062306a36Sopenharmony_ci * means whenever the multicast table entries need to be updated this 41162306a36Sopenharmony_ci * function gets called. 41262306a36Sopenharmony_ci */ 41362306a36Sopenharmony_cistatic void axienet_set_multicast_list(struct net_device *ndev) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci int i; 41662306a36Sopenharmony_ci u32 reg, af0reg, af1reg; 41762306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (ndev->flags & (IFF_ALLMULTI | IFF_PROMISC) || 42062306a36Sopenharmony_ci netdev_mc_count(ndev) > XAE_MULTICAST_CAM_TABLE_NUM) { 42162306a36Sopenharmony_ci /* We must make the kernel realize we had to move into 42262306a36Sopenharmony_ci * promiscuous mode. If it was a promiscuous mode request 42362306a36Sopenharmony_ci * the flag is already set. If not we set it. 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_ci ndev->flags |= IFF_PROMISC; 42662306a36Sopenharmony_ci reg = axienet_ior(lp, XAE_FMI_OFFSET); 42762306a36Sopenharmony_ci reg |= XAE_FMI_PM_MASK; 42862306a36Sopenharmony_ci axienet_iow(lp, XAE_FMI_OFFSET, reg); 42962306a36Sopenharmony_ci dev_info(&ndev->dev, "Promiscuous mode enabled.\n"); 43062306a36Sopenharmony_ci } else if (!netdev_mc_empty(ndev)) { 43162306a36Sopenharmony_ci struct netdev_hw_addr *ha; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci i = 0; 43462306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, ndev) { 43562306a36Sopenharmony_ci if (i >= XAE_MULTICAST_CAM_TABLE_NUM) 43662306a36Sopenharmony_ci break; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci af0reg = (ha->addr[0]); 43962306a36Sopenharmony_ci af0reg |= (ha->addr[1] << 8); 44062306a36Sopenharmony_ci af0reg |= (ha->addr[2] << 16); 44162306a36Sopenharmony_ci af0reg |= (ha->addr[3] << 24); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci af1reg = (ha->addr[4]); 44462306a36Sopenharmony_ci af1reg |= (ha->addr[5] << 8); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci reg = axienet_ior(lp, XAE_FMI_OFFSET) & 0xFFFFFF00; 44762306a36Sopenharmony_ci reg |= i; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci axienet_iow(lp, XAE_FMI_OFFSET, reg); 45062306a36Sopenharmony_ci axienet_iow(lp, XAE_AF0_OFFSET, af0reg); 45162306a36Sopenharmony_ci axienet_iow(lp, XAE_AF1_OFFSET, af1reg); 45262306a36Sopenharmony_ci i++; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci } else { 45562306a36Sopenharmony_ci reg = axienet_ior(lp, XAE_FMI_OFFSET); 45662306a36Sopenharmony_ci reg &= ~XAE_FMI_PM_MASK; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci axienet_iow(lp, XAE_FMI_OFFSET, reg); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci for (i = 0; i < XAE_MULTICAST_CAM_TABLE_NUM; i++) { 46162306a36Sopenharmony_ci reg = axienet_ior(lp, XAE_FMI_OFFSET) & 0xFFFFFF00; 46262306a36Sopenharmony_ci reg |= i; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci axienet_iow(lp, XAE_FMI_OFFSET, reg); 46562306a36Sopenharmony_ci axienet_iow(lp, XAE_AF0_OFFSET, 0); 46662306a36Sopenharmony_ci axienet_iow(lp, XAE_AF1_OFFSET, 0); 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci dev_info(&ndev->dev, "Promiscuous mode disabled.\n"); 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci/** 47462306a36Sopenharmony_ci * axienet_setoptions - Set an Axi Ethernet option 47562306a36Sopenharmony_ci * @ndev: Pointer to the net_device structure 47662306a36Sopenharmony_ci * @options: Option to be enabled/disabled 47762306a36Sopenharmony_ci * 47862306a36Sopenharmony_ci * The Axi Ethernet core has multiple features which can be selectively turned 47962306a36Sopenharmony_ci * on or off. The typical options could be jumbo frame option, basic VLAN 48062306a36Sopenharmony_ci * option, promiscuous mode option etc. This function is used to set or clear 48162306a36Sopenharmony_ci * these options in the Axi Ethernet hardware. This is done through 48262306a36Sopenharmony_ci * axienet_option structure . 48362306a36Sopenharmony_ci */ 48462306a36Sopenharmony_cistatic void axienet_setoptions(struct net_device *ndev, u32 options) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci int reg; 48762306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 48862306a36Sopenharmony_ci struct axienet_option *tp = &axienet_options[0]; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci while (tp->opt) { 49162306a36Sopenharmony_ci reg = ((axienet_ior(lp, tp->reg)) & ~(tp->m_or)); 49262306a36Sopenharmony_ci if (options & tp->opt) 49362306a36Sopenharmony_ci reg |= tp->m_or; 49462306a36Sopenharmony_ci axienet_iow(lp, tp->reg, reg); 49562306a36Sopenharmony_ci tp++; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci lp->options |= options; 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic int __axienet_device_reset(struct axienet_local *lp) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci u32 value; 50462306a36Sopenharmony_ci int ret; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci /* Reset Axi DMA. This would reset Axi Ethernet core as well. The reset 50762306a36Sopenharmony_ci * process of Axi DMA takes a while to complete as all pending 50862306a36Sopenharmony_ci * commands/transfers will be flushed or completed during this 50962306a36Sopenharmony_ci * reset process. 51062306a36Sopenharmony_ci * Note that even though both TX and RX have their own reset register, 51162306a36Sopenharmony_ci * they both reset the entire DMA core, so only one needs to be used. 51262306a36Sopenharmony_ci */ 51362306a36Sopenharmony_ci axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, XAXIDMA_CR_RESET_MASK); 51462306a36Sopenharmony_ci ret = read_poll_timeout(axienet_dma_in32, value, 51562306a36Sopenharmony_ci !(value & XAXIDMA_CR_RESET_MASK), 51662306a36Sopenharmony_ci DELAY_OF_ONE_MILLISEC, 50000, false, lp, 51762306a36Sopenharmony_ci XAXIDMA_TX_CR_OFFSET); 51862306a36Sopenharmony_ci if (ret) { 51962306a36Sopenharmony_ci dev_err(lp->dev, "%s: DMA reset timeout!\n", __func__); 52062306a36Sopenharmony_ci return ret; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Wait for PhyRstCmplt bit to be set, indicating the PHY reset has finished */ 52462306a36Sopenharmony_ci ret = read_poll_timeout(axienet_ior, value, 52562306a36Sopenharmony_ci value & XAE_INT_PHYRSTCMPLT_MASK, 52662306a36Sopenharmony_ci DELAY_OF_ONE_MILLISEC, 50000, false, lp, 52762306a36Sopenharmony_ci XAE_IS_OFFSET); 52862306a36Sopenharmony_ci if (ret) { 52962306a36Sopenharmony_ci dev_err(lp->dev, "%s: timeout waiting for PhyRstCmplt\n", __func__); 53062306a36Sopenharmony_ci return ret; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci return 0; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci/** 53762306a36Sopenharmony_ci * axienet_dma_stop - Stop DMA operation 53862306a36Sopenharmony_ci * @lp: Pointer to the axienet_local structure 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_cistatic void axienet_dma_stop(struct axienet_local *lp) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci int count; 54362306a36Sopenharmony_ci u32 cr, sr; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci cr = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET); 54662306a36Sopenharmony_ci cr &= ~(XAXIDMA_CR_RUNSTOP_MASK | XAXIDMA_IRQ_ALL_MASK); 54762306a36Sopenharmony_ci axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr); 54862306a36Sopenharmony_ci synchronize_irq(lp->rx_irq); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci cr = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET); 55162306a36Sopenharmony_ci cr &= ~(XAXIDMA_CR_RUNSTOP_MASK | XAXIDMA_IRQ_ALL_MASK); 55262306a36Sopenharmony_ci axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr); 55362306a36Sopenharmony_ci synchronize_irq(lp->tx_irq); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* Give DMAs a chance to halt gracefully */ 55662306a36Sopenharmony_ci sr = axienet_dma_in32(lp, XAXIDMA_RX_SR_OFFSET); 55762306a36Sopenharmony_ci for (count = 0; !(sr & XAXIDMA_SR_HALT_MASK) && count < 5; ++count) { 55862306a36Sopenharmony_ci msleep(20); 55962306a36Sopenharmony_ci sr = axienet_dma_in32(lp, XAXIDMA_RX_SR_OFFSET); 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci sr = axienet_dma_in32(lp, XAXIDMA_TX_SR_OFFSET); 56362306a36Sopenharmony_ci for (count = 0; !(sr & XAXIDMA_SR_HALT_MASK) && count < 5; ++count) { 56462306a36Sopenharmony_ci msleep(20); 56562306a36Sopenharmony_ci sr = axienet_dma_in32(lp, XAXIDMA_TX_SR_OFFSET); 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* Do a reset to ensure DMA is really stopped */ 56962306a36Sopenharmony_ci axienet_lock_mii(lp); 57062306a36Sopenharmony_ci __axienet_device_reset(lp); 57162306a36Sopenharmony_ci axienet_unlock_mii(lp); 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci/** 57562306a36Sopenharmony_ci * axienet_device_reset - Reset and initialize the Axi Ethernet hardware. 57662306a36Sopenharmony_ci * @ndev: Pointer to the net_device structure 57762306a36Sopenharmony_ci * 57862306a36Sopenharmony_ci * This function is called to reset and initialize the Axi Ethernet core. This 57962306a36Sopenharmony_ci * is typically called during initialization. It does a reset of the Axi DMA 58062306a36Sopenharmony_ci * Rx/Tx channels and initializes the Axi DMA BDs. Since Axi DMA reset lines 58162306a36Sopenharmony_ci * are connected to Axi Ethernet reset lines, this in turn resets the Axi 58262306a36Sopenharmony_ci * Ethernet core. No separate hardware reset is done for the Axi Ethernet 58362306a36Sopenharmony_ci * core. 58462306a36Sopenharmony_ci * Returns 0 on success or a negative error number otherwise. 58562306a36Sopenharmony_ci */ 58662306a36Sopenharmony_cistatic int axienet_device_reset(struct net_device *ndev) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci u32 axienet_status; 58962306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 59062306a36Sopenharmony_ci int ret; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci ret = __axienet_device_reset(lp); 59362306a36Sopenharmony_ci if (ret) 59462306a36Sopenharmony_ci return ret; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci lp->max_frm_size = XAE_MAX_VLAN_FRAME_SIZE; 59762306a36Sopenharmony_ci lp->options |= XAE_OPTION_VLAN; 59862306a36Sopenharmony_ci lp->options &= (~XAE_OPTION_JUMBO); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if ((ndev->mtu > XAE_MTU) && 60162306a36Sopenharmony_ci (ndev->mtu <= XAE_JUMBO_MTU)) { 60262306a36Sopenharmony_ci lp->max_frm_size = ndev->mtu + VLAN_ETH_HLEN + 60362306a36Sopenharmony_ci XAE_TRL_SIZE; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if (lp->max_frm_size <= lp->rxmem) 60662306a36Sopenharmony_ci lp->options |= XAE_OPTION_JUMBO; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci ret = axienet_dma_bd_init(ndev); 61062306a36Sopenharmony_ci if (ret) { 61162306a36Sopenharmony_ci netdev_err(ndev, "%s: descriptor allocation failed\n", 61262306a36Sopenharmony_ci __func__); 61362306a36Sopenharmony_ci return ret; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci axienet_status = axienet_ior(lp, XAE_RCW1_OFFSET); 61762306a36Sopenharmony_ci axienet_status &= ~XAE_RCW1_RX_MASK; 61862306a36Sopenharmony_ci axienet_iow(lp, XAE_RCW1_OFFSET, axienet_status); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci axienet_status = axienet_ior(lp, XAE_IP_OFFSET); 62162306a36Sopenharmony_ci if (axienet_status & XAE_INT_RXRJECT_MASK) 62262306a36Sopenharmony_ci axienet_iow(lp, XAE_IS_OFFSET, XAE_INT_RXRJECT_MASK); 62362306a36Sopenharmony_ci axienet_iow(lp, XAE_IE_OFFSET, lp->eth_irq > 0 ? 62462306a36Sopenharmony_ci XAE_INT_RECV_ERROR_MASK : 0); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci axienet_iow(lp, XAE_FCC_OFFSET, XAE_FCC_FCRX_MASK); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci /* Sync default options with HW but leave receiver and 62962306a36Sopenharmony_ci * transmitter disabled. 63062306a36Sopenharmony_ci */ 63162306a36Sopenharmony_ci axienet_setoptions(ndev, lp->options & 63262306a36Sopenharmony_ci ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN)); 63362306a36Sopenharmony_ci axienet_set_mac_address(ndev, NULL); 63462306a36Sopenharmony_ci axienet_set_multicast_list(ndev); 63562306a36Sopenharmony_ci axienet_setoptions(ndev, lp->options); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci netif_trans_update(ndev); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci return 0; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci/** 64362306a36Sopenharmony_ci * axienet_free_tx_chain - Clean up a series of linked TX descriptors. 64462306a36Sopenharmony_ci * @lp: Pointer to the axienet_local structure 64562306a36Sopenharmony_ci * @first_bd: Index of first descriptor to clean up 64662306a36Sopenharmony_ci * @nr_bds: Max number of descriptors to clean up 64762306a36Sopenharmony_ci * @force: Whether to clean descriptors even if not complete 64862306a36Sopenharmony_ci * @sizep: Pointer to a u32 filled with the total sum of all bytes 64962306a36Sopenharmony_ci * in all cleaned-up descriptors. Ignored if NULL. 65062306a36Sopenharmony_ci * @budget: NAPI budget (use 0 when not called from NAPI poll) 65162306a36Sopenharmony_ci * 65262306a36Sopenharmony_ci * Would either be called after a successful transmit operation, or after 65362306a36Sopenharmony_ci * there was an error when setting up the chain. 65462306a36Sopenharmony_ci * Returns the number of descriptors handled. 65562306a36Sopenharmony_ci */ 65662306a36Sopenharmony_cistatic int axienet_free_tx_chain(struct axienet_local *lp, u32 first_bd, 65762306a36Sopenharmony_ci int nr_bds, bool force, u32 *sizep, int budget) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci struct axidma_bd *cur_p; 66062306a36Sopenharmony_ci unsigned int status; 66162306a36Sopenharmony_ci dma_addr_t phys; 66262306a36Sopenharmony_ci int i; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci for (i = 0; i < nr_bds; i++) { 66562306a36Sopenharmony_ci cur_p = &lp->tx_bd_v[(first_bd + i) % lp->tx_bd_num]; 66662306a36Sopenharmony_ci status = cur_p->status; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* If force is not specified, clean up only descriptors 66962306a36Sopenharmony_ci * that have been completed by the MAC. 67062306a36Sopenharmony_ci */ 67162306a36Sopenharmony_ci if (!force && !(status & XAXIDMA_BD_STS_COMPLETE_MASK)) 67262306a36Sopenharmony_ci break; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* Ensure we see complete descriptor update */ 67562306a36Sopenharmony_ci dma_rmb(); 67662306a36Sopenharmony_ci phys = desc_get_phys_addr(lp, cur_p); 67762306a36Sopenharmony_ci dma_unmap_single(lp->dev, phys, 67862306a36Sopenharmony_ci (cur_p->cntrl & XAXIDMA_BD_CTRL_LENGTH_MASK), 67962306a36Sopenharmony_ci DMA_TO_DEVICE); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (cur_p->skb && (status & XAXIDMA_BD_STS_COMPLETE_MASK)) 68262306a36Sopenharmony_ci napi_consume_skb(cur_p->skb, budget); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci cur_p->app0 = 0; 68562306a36Sopenharmony_ci cur_p->app1 = 0; 68662306a36Sopenharmony_ci cur_p->app2 = 0; 68762306a36Sopenharmony_ci cur_p->app4 = 0; 68862306a36Sopenharmony_ci cur_p->skb = NULL; 68962306a36Sopenharmony_ci /* ensure our transmit path and device don't prematurely see status cleared */ 69062306a36Sopenharmony_ci wmb(); 69162306a36Sopenharmony_ci cur_p->cntrl = 0; 69262306a36Sopenharmony_ci cur_p->status = 0; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci if (sizep) 69562306a36Sopenharmony_ci *sizep += status & XAXIDMA_BD_STS_ACTUAL_LEN_MASK; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci return i; 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci/** 70262306a36Sopenharmony_ci * axienet_check_tx_bd_space - Checks if a BD/group of BDs are currently busy 70362306a36Sopenharmony_ci * @lp: Pointer to the axienet_local structure 70462306a36Sopenharmony_ci * @num_frag: The number of BDs to check for 70562306a36Sopenharmony_ci * 70662306a36Sopenharmony_ci * Return: 0, on success 70762306a36Sopenharmony_ci * NETDEV_TX_BUSY, if any of the descriptors are not free 70862306a36Sopenharmony_ci * 70962306a36Sopenharmony_ci * This function is invoked before BDs are allocated and transmission starts. 71062306a36Sopenharmony_ci * This function returns 0 if a BD or group of BDs can be allocated for 71162306a36Sopenharmony_ci * transmission. If the BD or any of the BDs are not free the function 71262306a36Sopenharmony_ci * returns a busy status. 71362306a36Sopenharmony_ci */ 71462306a36Sopenharmony_cistatic inline int axienet_check_tx_bd_space(struct axienet_local *lp, 71562306a36Sopenharmony_ci int num_frag) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci struct axidma_bd *cur_p; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci /* Ensure we see all descriptor updates from device or TX polling */ 72062306a36Sopenharmony_ci rmb(); 72162306a36Sopenharmony_ci cur_p = &lp->tx_bd_v[(READ_ONCE(lp->tx_bd_tail) + num_frag) % 72262306a36Sopenharmony_ci lp->tx_bd_num]; 72362306a36Sopenharmony_ci if (cur_p->cntrl) 72462306a36Sopenharmony_ci return NETDEV_TX_BUSY; 72562306a36Sopenharmony_ci return 0; 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci/** 72962306a36Sopenharmony_ci * axienet_tx_poll - Invoked once a transmit is completed by the 73062306a36Sopenharmony_ci * Axi DMA Tx channel. 73162306a36Sopenharmony_ci * @napi: Pointer to NAPI structure. 73262306a36Sopenharmony_ci * @budget: Max number of TX packets to process. 73362306a36Sopenharmony_ci * 73462306a36Sopenharmony_ci * Return: Number of TX packets processed. 73562306a36Sopenharmony_ci * 73662306a36Sopenharmony_ci * This function is invoked from the NAPI processing to notify the completion 73762306a36Sopenharmony_ci * of transmit operation. It clears fields in the corresponding Tx BDs and 73862306a36Sopenharmony_ci * unmaps the corresponding buffer so that CPU can regain ownership of the 73962306a36Sopenharmony_ci * buffer. It finally invokes "netif_wake_queue" to restart transmission if 74062306a36Sopenharmony_ci * required. 74162306a36Sopenharmony_ci */ 74262306a36Sopenharmony_cistatic int axienet_tx_poll(struct napi_struct *napi, int budget) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci struct axienet_local *lp = container_of(napi, struct axienet_local, napi_tx); 74562306a36Sopenharmony_ci struct net_device *ndev = lp->ndev; 74662306a36Sopenharmony_ci u32 size = 0; 74762306a36Sopenharmony_ci int packets; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci packets = axienet_free_tx_chain(lp, lp->tx_bd_ci, budget, false, &size, budget); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci if (packets) { 75262306a36Sopenharmony_ci lp->tx_bd_ci += packets; 75362306a36Sopenharmony_ci if (lp->tx_bd_ci >= lp->tx_bd_num) 75462306a36Sopenharmony_ci lp->tx_bd_ci %= lp->tx_bd_num; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci u64_stats_update_begin(&lp->tx_stat_sync); 75762306a36Sopenharmony_ci u64_stats_add(&lp->tx_packets, packets); 75862306a36Sopenharmony_ci u64_stats_add(&lp->tx_bytes, size); 75962306a36Sopenharmony_ci u64_stats_update_end(&lp->tx_stat_sync); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci /* Matches barrier in axienet_start_xmit */ 76262306a36Sopenharmony_ci smp_mb(); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (!axienet_check_tx_bd_space(lp, MAX_SKB_FRAGS + 1)) 76562306a36Sopenharmony_ci netif_wake_queue(ndev); 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci if (packets < budget && napi_complete_done(napi, packets)) { 76962306a36Sopenharmony_ci /* Re-enable TX completion interrupts. This should 77062306a36Sopenharmony_ci * cause an immediate interrupt if any TX packets are 77162306a36Sopenharmony_ci * already pending. 77262306a36Sopenharmony_ci */ 77362306a36Sopenharmony_ci axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, lp->tx_dma_cr); 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci return packets; 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci/** 77962306a36Sopenharmony_ci * axienet_start_xmit - Starts the transmission. 78062306a36Sopenharmony_ci * @skb: sk_buff pointer that contains data to be Txed. 78162306a36Sopenharmony_ci * @ndev: Pointer to net_device structure. 78262306a36Sopenharmony_ci * 78362306a36Sopenharmony_ci * Return: NETDEV_TX_OK, on success 78462306a36Sopenharmony_ci * NETDEV_TX_BUSY, if any of the descriptors are not free 78562306a36Sopenharmony_ci * 78662306a36Sopenharmony_ci * This function is invoked from upper layers to initiate transmission. The 78762306a36Sopenharmony_ci * function uses the next available free BDs and populates their fields to 78862306a36Sopenharmony_ci * start the transmission. Additionally if checksum offloading is supported, 78962306a36Sopenharmony_ci * it populates AXI Stream Control fields with appropriate values. 79062306a36Sopenharmony_ci */ 79162306a36Sopenharmony_cistatic netdev_tx_t 79262306a36Sopenharmony_ciaxienet_start_xmit(struct sk_buff *skb, struct net_device *ndev) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci u32 ii; 79562306a36Sopenharmony_ci u32 num_frag; 79662306a36Sopenharmony_ci u32 csum_start_off; 79762306a36Sopenharmony_ci u32 csum_index_off; 79862306a36Sopenharmony_ci skb_frag_t *frag; 79962306a36Sopenharmony_ci dma_addr_t tail_p, phys; 80062306a36Sopenharmony_ci u32 orig_tail_ptr, new_tail_ptr; 80162306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 80262306a36Sopenharmony_ci struct axidma_bd *cur_p; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci orig_tail_ptr = lp->tx_bd_tail; 80562306a36Sopenharmony_ci new_tail_ptr = orig_tail_ptr; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci num_frag = skb_shinfo(skb)->nr_frags; 80862306a36Sopenharmony_ci cur_p = &lp->tx_bd_v[orig_tail_ptr]; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (axienet_check_tx_bd_space(lp, num_frag + 1)) { 81162306a36Sopenharmony_ci /* Should not happen as last start_xmit call should have 81262306a36Sopenharmony_ci * checked for sufficient space and queue should only be 81362306a36Sopenharmony_ci * woken when sufficient space is available. 81462306a36Sopenharmony_ci */ 81562306a36Sopenharmony_ci netif_stop_queue(ndev); 81662306a36Sopenharmony_ci if (net_ratelimit()) 81762306a36Sopenharmony_ci netdev_warn(ndev, "TX ring unexpectedly full\n"); 81862306a36Sopenharmony_ci return NETDEV_TX_BUSY; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) { 82262306a36Sopenharmony_ci if (lp->features & XAE_FEATURE_FULL_TX_CSUM) { 82362306a36Sopenharmony_ci /* Tx Full Checksum Offload Enabled */ 82462306a36Sopenharmony_ci cur_p->app0 |= 2; 82562306a36Sopenharmony_ci } else if (lp->features & XAE_FEATURE_PARTIAL_TX_CSUM) { 82662306a36Sopenharmony_ci csum_start_off = skb_transport_offset(skb); 82762306a36Sopenharmony_ci csum_index_off = csum_start_off + skb->csum_offset; 82862306a36Sopenharmony_ci /* Tx Partial Checksum Offload Enabled */ 82962306a36Sopenharmony_ci cur_p->app0 |= 1; 83062306a36Sopenharmony_ci cur_p->app1 = (csum_start_off << 16) | csum_index_off; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) { 83362306a36Sopenharmony_ci cur_p->app0 |= 2; /* Tx Full Checksum Offload Enabled */ 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci phys = dma_map_single(lp->dev, skb->data, 83762306a36Sopenharmony_ci skb_headlen(skb), DMA_TO_DEVICE); 83862306a36Sopenharmony_ci if (unlikely(dma_mapping_error(lp->dev, phys))) { 83962306a36Sopenharmony_ci if (net_ratelimit()) 84062306a36Sopenharmony_ci netdev_err(ndev, "TX DMA mapping error\n"); 84162306a36Sopenharmony_ci ndev->stats.tx_dropped++; 84262306a36Sopenharmony_ci return NETDEV_TX_OK; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci desc_set_phys_addr(lp, phys, cur_p); 84562306a36Sopenharmony_ci cur_p->cntrl = skb_headlen(skb) | XAXIDMA_BD_CTRL_TXSOF_MASK; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci for (ii = 0; ii < num_frag; ii++) { 84862306a36Sopenharmony_ci if (++new_tail_ptr >= lp->tx_bd_num) 84962306a36Sopenharmony_ci new_tail_ptr = 0; 85062306a36Sopenharmony_ci cur_p = &lp->tx_bd_v[new_tail_ptr]; 85162306a36Sopenharmony_ci frag = &skb_shinfo(skb)->frags[ii]; 85262306a36Sopenharmony_ci phys = dma_map_single(lp->dev, 85362306a36Sopenharmony_ci skb_frag_address(frag), 85462306a36Sopenharmony_ci skb_frag_size(frag), 85562306a36Sopenharmony_ci DMA_TO_DEVICE); 85662306a36Sopenharmony_ci if (unlikely(dma_mapping_error(lp->dev, phys))) { 85762306a36Sopenharmony_ci if (net_ratelimit()) 85862306a36Sopenharmony_ci netdev_err(ndev, "TX DMA mapping error\n"); 85962306a36Sopenharmony_ci ndev->stats.tx_dropped++; 86062306a36Sopenharmony_ci axienet_free_tx_chain(lp, orig_tail_ptr, ii + 1, 86162306a36Sopenharmony_ci true, NULL, 0); 86262306a36Sopenharmony_ci return NETDEV_TX_OK; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci desc_set_phys_addr(lp, phys, cur_p); 86562306a36Sopenharmony_ci cur_p->cntrl = skb_frag_size(frag); 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci cur_p->cntrl |= XAXIDMA_BD_CTRL_TXEOF_MASK; 86962306a36Sopenharmony_ci cur_p->skb = skb; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci tail_p = lp->tx_bd_p + sizeof(*lp->tx_bd_v) * new_tail_ptr; 87262306a36Sopenharmony_ci if (++new_tail_ptr >= lp->tx_bd_num) 87362306a36Sopenharmony_ci new_tail_ptr = 0; 87462306a36Sopenharmony_ci WRITE_ONCE(lp->tx_bd_tail, new_tail_ptr); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci /* Start the transfer */ 87762306a36Sopenharmony_ci axienet_dma_out_addr(lp, XAXIDMA_TX_TDESC_OFFSET, tail_p); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci /* Stop queue if next transmit may not have space */ 88062306a36Sopenharmony_ci if (axienet_check_tx_bd_space(lp, MAX_SKB_FRAGS + 1)) { 88162306a36Sopenharmony_ci netif_stop_queue(ndev); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci /* Matches barrier in axienet_tx_poll */ 88462306a36Sopenharmony_ci smp_mb(); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci /* Space might have just been freed - check again */ 88762306a36Sopenharmony_ci if (!axienet_check_tx_bd_space(lp, MAX_SKB_FRAGS + 1)) 88862306a36Sopenharmony_ci netif_wake_queue(ndev); 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci return NETDEV_TX_OK; 89262306a36Sopenharmony_ci} 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci/** 89562306a36Sopenharmony_ci * axienet_rx_poll - Triggered by RX ISR to complete the BD processing. 89662306a36Sopenharmony_ci * @napi: Pointer to NAPI structure. 89762306a36Sopenharmony_ci * @budget: Max number of RX packets to process. 89862306a36Sopenharmony_ci * 89962306a36Sopenharmony_ci * Return: Number of RX packets processed. 90062306a36Sopenharmony_ci */ 90162306a36Sopenharmony_cistatic int axienet_rx_poll(struct napi_struct *napi, int budget) 90262306a36Sopenharmony_ci{ 90362306a36Sopenharmony_ci u32 length; 90462306a36Sopenharmony_ci u32 csumstatus; 90562306a36Sopenharmony_ci u32 size = 0; 90662306a36Sopenharmony_ci int packets = 0; 90762306a36Sopenharmony_ci dma_addr_t tail_p = 0; 90862306a36Sopenharmony_ci struct axidma_bd *cur_p; 90962306a36Sopenharmony_ci struct sk_buff *skb, *new_skb; 91062306a36Sopenharmony_ci struct axienet_local *lp = container_of(napi, struct axienet_local, napi_rx); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci cur_p = &lp->rx_bd_v[lp->rx_bd_ci]; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci while (packets < budget && (cur_p->status & XAXIDMA_BD_STS_COMPLETE_MASK)) { 91562306a36Sopenharmony_ci dma_addr_t phys; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci /* Ensure we see complete descriptor update */ 91862306a36Sopenharmony_ci dma_rmb(); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci skb = cur_p->skb; 92162306a36Sopenharmony_ci cur_p->skb = NULL; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* skb could be NULL if a previous pass already received the 92462306a36Sopenharmony_ci * packet for this slot in the ring, but failed to refill it 92562306a36Sopenharmony_ci * with a newly allocated buffer. In this case, don't try to 92662306a36Sopenharmony_ci * receive it again. 92762306a36Sopenharmony_ci */ 92862306a36Sopenharmony_ci if (likely(skb)) { 92962306a36Sopenharmony_ci length = cur_p->app4 & 0x0000FFFF; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci phys = desc_get_phys_addr(lp, cur_p); 93262306a36Sopenharmony_ci dma_unmap_single(lp->dev, phys, lp->max_frm_size, 93362306a36Sopenharmony_ci DMA_FROM_DEVICE); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci skb_put(skb, length); 93662306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, lp->ndev); 93762306a36Sopenharmony_ci /*skb_checksum_none_assert(skb);*/ 93862306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci /* if we're doing Rx csum offload, set it up */ 94162306a36Sopenharmony_ci if (lp->features & XAE_FEATURE_FULL_RX_CSUM) { 94262306a36Sopenharmony_ci csumstatus = (cur_p->app2 & 94362306a36Sopenharmony_ci XAE_FULL_CSUM_STATUS_MASK) >> 3; 94462306a36Sopenharmony_ci if (csumstatus == XAE_IP_TCP_CSUM_VALIDATED || 94562306a36Sopenharmony_ci csumstatus == XAE_IP_UDP_CSUM_VALIDATED) { 94662306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci } else if ((lp->features & XAE_FEATURE_PARTIAL_RX_CSUM) != 0 && 94962306a36Sopenharmony_ci skb->protocol == htons(ETH_P_IP) && 95062306a36Sopenharmony_ci skb->len > 64) { 95162306a36Sopenharmony_ci skb->csum = be32_to_cpu(cur_p->app3 & 0xFFFF); 95262306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_COMPLETE; 95362306a36Sopenharmony_ci } 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci napi_gro_receive(napi, skb); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci size += length; 95862306a36Sopenharmony_ci packets++; 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci new_skb = napi_alloc_skb(napi, lp->max_frm_size); 96262306a36Sopenharmony_ci if (!new_skb) 96362306a36Sopenharmony_ci break; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci phys = dma_map_single(lp->dev, new_skb->data, 96662306a36Sopenharmony_ci lp->max_frm_size, 96762306a36Sopenharmony_ci DMA_FROM_DEVICE); 96862306a36Sopenharmony_ci if (unlikely(dma_mapping_error(lp->dev, phys))) { 96962306a36Sopenharmony_ci if (net_ratelimit()) 97062306a36Sopenharmony_ci netdev_err(lp->ndev, "RX DMA mapping error\n"); 97162306a36Sopenharmony_ci dev_kfree_skb(new_skb); 97262306a36Sopenharmony_ci break; 97362306a36Sopenharmony_ci } 97462306a36Sopenharmony_ci desc_set_phys_addr(lp, phys, cur_p); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci cur_p->cntrl = lp->max_frm_size; 97762306a36Sopenharmony_ci cur_p->status = 0; 97862306a36Sopenharmony_ci cur_p->skb = new_skb; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci /* Only update tail_p to mark this slot as usable after it has 98162306a36Sopenharmony_ci * been successfully refilled. 98262306a36Sopenharmony_ci */ 98362306a36Sopenharmony_ci tail_p = lp->rx_bd_p + sizeof(*lp->rx_bd_v) * lp->rx_bd_ci; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci if (++lp->rx_bd_ci >= lp->rx_bd_num) 98662306a36Sopenharmony_ci lp->rx_bd_ci = 0; 98762306a36Sopenharmony_ci cur_p = &lp->rx_bd_v[lp->rx_bd_ci]; 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci u64_stats_update_begin(&lp->rx_stat_sync); 99162306a36Sopenharmony_ci u64_stats_add(&lp->rx_packets, packets); 99262306a36Sopenharmony_ci u64_stats_add(&lp->rx_bytes, size); 99362306a36Sopenharmony_ci u64_stats_update_end(&lp->rx_stat_sync); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci if (tail_p) 99662306a36Sopenharmony_ci axienet_dma_out_addr(lp, XAXIDMA_RX_TDESC_OFFSET, tail_p); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci if (packets < budget && napi_complete_done(napi, packets)) { 99962306a36Sopenharmony_ci /* Re-enable RX completion interrupts. This should 100062306a36Sopenharmony_ci * cause an immediate interrupt if any RX packets are 100162306a36Sopenharmony_ci * already pending. 100262306a36Sopenharmony_ci */ 100362306a36Sopenharmony_ci axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, lp->rx_dma_cr); 100462306a36Sopenharmony_ci } 100562306a36Sopenharmony_ci return packets; 100662306a36Sopenharmony_ci} 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci/** 100962306a36Sopenharmony_ci * axienet_tx_irq - Tx Done Isr. 101062306a36Sopenharmony_ci * @irq: irq number 101162306a36Sopenharmony_ci * @_ndev: net_device pointer 101262306a36Sopenharmony_ci * 101362306a36Sopenharmony_ci * Return: IRQ_HANDLED if device generated a TX interrupt, IRQ_NONE otherwise. 101462306a36Sopenharmony_ci * 101562306a36Sopenharmony_ci * This is the Axi DMA Tx done Isr. It invokes NAPI polling to complete the 101662306a36Sopenharmony_ci * TX BD processing. 101762306a36Sopenharmony_ci */ 101862306a36Sopenharmony_cistatic irqreturn_t axienet_tx_irq(int irq, void *_ndev) 101962306a36Sopenharmony_ci{ 102062306a36Sopenharmony_ci unsigned int status; 102162306a36Sopenharmony_ci struct net_device *ndev = _ndev; 102262306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci status = axienet_dma_in32(lp, XAXIDMA_TX_SR_OFFSET); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci if (!(status & XAXIDMA_IRQ_ALL_MASK)) 102762306a36Sopenharmony_ci return IRQ_NONE; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci axienet_dma_out32(lp, XAXIDMA_TX_SR_OFFSET, status); 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci if (unlikely(status & XAXIDMA_IRQ_ERROR_MASK)) { 103262306a36Sopenharmony_ci netdev_err(ndev, "DMA Tx error 0x%x\n", status); 103362306a36Sopenharmony_ci netdev_err(ndev, "Current BD is at: 0x%x%08x\n", 103462306a36Sopenharmony_ci (lp->tx_bd_v[lp->tx_bd_ci]).phys_msb, 103562306a36Sopenharmony_ci (lp->tx_bd_v[lp->tx_bd_ci]).phys); 103662306a36Sopenharmony_ci schedule_work(&lp->dma_err_task); 103762306a36Sopenharmony_ci } else { 103862306a36Sopenharmony_ci /* Disable further TX completion interrupts and schedule 103962306a36Sopenharmony_ci * NAPI to handle the completions. 104062306a36Sopenharmony_ci */ 104162306a36Sopenharmony_ci u32 cr = lp->tx_dma_cr; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci cr &= ~(XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK); 104462306a36Sopenharmony_ci axienet_dma_out32(lp, XAXIDMA_TX_CR_OFFSET, cr); 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci napi_schedule(&lp->napi_tx); 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci return IRQ_HANDLED; 105062306a36Sopenharmony_ci} 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci/** 105362306a36Sopenharmony_ci * axienet_rx_irq - Rx Isr. 105462306a36Sopenharmony_ci * @irq: irq number 105562306a36Sopenharmony_ci * @_ndev: net_device pointer 105662306a36Sopenharmony_ci * 105762306a36Sopenharmony_ci * Return: IRQ_HANDLED if device generated a RX interrupt, IRQ_NONE otherwise. 105862306a36Sopenharmony_ci * 105962306a36Sopenharmony_ci * This is the Axi DMA Rx Isr. It invokes NAPI polling to complete the RX BD 106062306a36Sopenharmony_ci * processing. 106162306a36Sopenharmony_ci */ 106262306a36Sopenharmony_cistatic irqreturn_t axienet_rx_irq(int irq, void *_ndev) 106362306a36Sopenharmony_ci{ 106462306a36Sopenharmony_ci unsigned int status; 106562306a36Sopenharmony_ci struct net_device *ndev = _ndev; 106662306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci status = axienet_dma_in32(lp, XAXIDMA_RX_SR_OFFSET); 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci if (!(status & XAXIDMA_IRQ_ALL_MASK)) 107162306a36Sopenharmony_ci return IRQ_NONE; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci axienet_dma_out32(lp, XAXIDMA_RX_SR_OFFSET, status); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci if (unlikely(status & XAXIDMA_IRQ_ERROR_MASK)) { 107662306a36Sopenharmony_ci netdev_err(ndev, "DMA Rx error 0x%x\n", status); 107762306a36Sopenharmony_ci netdev_err(ndev, "Current BD is at: 0x%x%08x\n", 107862306a36Sopenharmony_ci (lp->rx_bd_v[lp->rx_bd_ci]).phys_msb, 107962306a36Sopenharmony_ci (lp->rx_bd_v[lp->rx_bd_ci]).phys); 108062306a36Sopenharmony_ci schedule_work(&lp->dma_err_task); 108162306a36Sopenharmony_ci } else { 108262306a36Sopenharmony_ci /* Disable further RX completion interrupts and schedule 108362306a36Sopenharmony_ci * NAPI receive. 108462306a36Sopenharmony_ci */ 108562306a36Sopenharmony_ci u32 cr = lp->rx_dma_cr; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci cr &= ~(XAXIDMA_IRQ_IOC_MASK | XAXIDMA_IRQ_DELAY_MASK); 108862306a36Sopenharmony_ci axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr); 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci napi_schedule(&lp->napi_rx); 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci return IRQ_HANDLED; 109462306a36Sopenharmony_ci} 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci/** 109762306a36Sopenharmony_ci * axienet_eth_irq - Ethernet core Isr. 109862306a36Sopenharmony_ci * @irq: irq number 109962306a36Sopenharmony_ci * @_ndev: net_device pointer 110062306a36Sopenharmony_ci * 110162306a36Sopenharmony_ci * Return: IRQ_HANDLED if device generated a core interrupt, IRQ_NONE otherwise. 110262306a36Sopenharmony_ci * 110362306a36Sopenharmony_ci * Handle miscellaneous conditions indicated by Ethernet core IRQ. 110462306a36Sopenharmony_ci */ 110562306a36Sopenharmony_cistatic irqreturn_t axienet_eth_irq(int irq, void *_ndev) 110662306a36Sopenharmony_ci{ 110762306a36Sopenharmony_ci struct net_device *ndev = _ndev; 110862306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 110962306a36Sopenharmony_ci unsigned int pending; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci pending = axienet_ior(lp, XAE_IP_OFFSET); 111262306a36Sopenharmony_ci if (!pending) 111362306a36Sopenharmony_ci return IRQ_NONE; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci if (pending & XAE_INT_RXFIFOOVR_MASK) 111662306a36Sopenharmony_ci ndev->stats.rx_missed_errors++; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci if (pending & XAE_INT_RXRJECT_MASK) 111962306a36Sopenharmony_ci ndev->stats.rx_frame_errors++; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci axienet_iow(lp, XAE_IS_OFFSET, pending); 112262306a36Sopenharmony_ci return IRQ_HANDLED; 112362306a36Sopenharmony_ci} 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_cistatic void axienet_dma_err_handler(struct work_struct *work); 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci/** 112862306a36Sopenharmony_ci * axienet_open - Driver open routine. 112962306a36Sopenharmony_ci * @ndev: Pointer to net_device structure 113062306a36Sopenharmony_ci * 113162306a36Sopenharmony_ci * Return: 0, on success. 113262306a36Sopenharmony_ci * non-zero error value on failure 113362306a36Sopenharmony_ci * 113462306a36Sopenharmony_ci * This is the driver open routine. It calls phylink_start to start the 113562306a36Sopenharmony_ci * PHY device. 113662306a36Sopenharmony_ci * It also allocates interrupt service routines, enables the interrupt lines 113762306a36Sopenharmony_ci * and ISR handling. Axi Ethernet core is reset through Axi DMA core. Buffer 113862306a36Sopenharmony_ci * descriptors are initialized. 113962306a36Sopenharmony_ci */ 114062306a36Sopenharmony_cistatic int axienet_open(struct net_device *ndev) 114162306a36Sopenharmony_ci{ 114262306a36Sopenharmony_ci int ret; 114362306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci dev_dbg(&ndev->dev, "axienet_open()\n"); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci /* When we do an Axi Ethernet reset, it resets the complete core 114862306a36Sopenharmony_ci * including the MDIO. MDIO must be disabled before resetting. 114962306a36Sopenharmony_ci * Hold MDIO bus lock to avoid MDIO accesses during the reset. 115062306a36Sopenharmony_ci */ 115162306a36Sopenharmony_ci axienet_lock_mii(lp); 115262306a36Sopenharmony_ci ret = axienet_device_reset(ndev); 115362306a36Sopenharmony_ci axienet_unlock_mii(lp); 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci ret = phylink_of_phy_connect(lp->phylink, lp->dev->of_node, 0); 115662306a36Sopenharmony_ci if (ret) { 115762306a36Sopenharmony_ci dev_err(lp->dev, "phylink_of_phy_connect() failed: %d\n", ret); 115862306a36Sopenharmony_ci return ret; 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci phylink_start(lp->phylink); 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci /* Enable worker thread for Axi DMA error handling */ 116462306a36Sopenharmony_ci INIT_WORK(&lp->dma_err_task, axienet_dma_err_handler); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci napi_enable(&lp->napi_rx); 116762306a36Sopenharmony_ci napi_enable(&lp->napi_tx); 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci /* Enable interrupts for Axi DMA Tx */ 117062306a36Sopenharmony_ci ret = request_irq(lp->tx_irq, axienet_tx_irq, IRQF_SHARED, 117162306a36Sopenharmony_ci ndev->name, ndev); 117262306a36Sopenharmony_ci if (ret) 117362306a36Sopenharmony_ci goto err_tx_irq; 117462306a36Sopenharmony_ci /* Enable interrupts for Axi DMA Rx */ 117562306a36Sopenharmony_ci ret = request_irq(lp->rx_irq, axienet_rx_irq, IRQF_SHARED, 117662306a36Sopenharmony_ci ndev->name, ndev); 117762306a36Sopenharmony_ci if (ret) 117862306a36Sopenharmony_ci goto err_rx_irq; 117962306a36Sopenharmony_ci /* Enable interrupts for Axi Ethernet core (if defined) */ 118062306a36Sopenharmony_ci if (lp->eth_irq > 0) { 118162306a36Sopenharmony_ci ret = request_irq(lp->eth_irq, axienet_eth_irq, IRQF_SHARED, 118262306a36Sopenharmony_ci ndev->name, ndev); 118362306a36Sopenharmony_ci if (ret) 118462306a36Sopenharmony_ci goto err_eth_irq; 118562306a36Sopenharmony_ci } 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci return 0; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_cierr_eth_irq: 119062306a36Sopenharmony_ci free_irq(lp->rx_irq, ndev); 119162306a36Sopenharmony_cierr_rx_irq: 119262306a36Sopenharmony_ci free_irq(lp->tx_irq, ndev); 119362306a36Sopenharmony_cierr_tx_irq: 119462306a36Sopenharmony_ci napi_disable(&lp->napi_tx); 119562306a36Sopenharmony_ci napi_disable(&lp->napi_rx); 119662306a36Sopenharmony_ci phylink_stop(lp->phylink); 119762306a36Sopenharmony_ci phylink_disconnect_phy(lp->phylink); 119862306a36Sopenharmony_ci cancel_work_sync(&lp->dma_err_task); 119962306a36Sopenharmony_ci dev_err(lp->dev, "request_irq() failed\n"); 120062306a36Sopenharmony_ci return ret; 120162306a36Sopenharmony_ci} 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci/** 120462306a36Sopenharmony_ci * axienet_stop - Driver stop routine. 120562306a36Sopenharmony_ci * @ndev: Pointer to net_device structure 120662306a36Sopenharmony_ci * 120762306a36Sopenharmony_ci * Return: 0, on success. 120862306a36Sopenharmony_ci * 120962306a36Sopenharmony_ci * This is the driver stop routine. It calls phylink_disconnect to stop the PHY 121062306a36Sopenharmony_ci * device. It also removes the interrupt handlers and disables the interrupts. 121162306a36Sopenharmony_ci * The Axi DMA Tx/Rx BDs are released. 121262306a36Sopenharmony_ci */ 121362306a36Sopenharmony_cistatic int axienet_stop(struct net_device *ndev) 121462306a36Sopenharmony_ci{ 121562306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci dev_dbg(&ndev->dev, "axienet_close()\n"); 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci napi_disable(&lp->napi_tx); 122062306a36Sopenharmony_ci napi_disable(&lp->napi_rx); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci phylink_stop(lp->phylink); 122362306a36Sopenharmony_ci phylink_disconnect_phy(lp->phylink); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci axienet_setoptions(ndev, lp->options & 122662306a36Sopenharmony_ci ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN)); 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci axienet_dma_stop(lp); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci axienet_iow(lp, XAE_IE_OFFSET, 0); 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci cancel_work_sync(&lp->dma_err_task); 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci if (lp->eth_irq > 0) 123562306a36Sopenharmony_ci free_irq(lp->eth_irq, ndev); 123662306a36Sopenharmony_ci free_irq(lp->tx_irq, ndev); 123762306a36Sopenharmony_ci free_irq(lp->rx_irq, ndev); 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci axienet_dma_bd_release(ndev); 124062306a36Sopenharmony_ci return 0; 124162306a36Sopenharmony_ci} 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci/** 124462306a36Sopenharmony_ci * axienet_change_mtu - Driver change mtu routine. 124562306a36Sopenharmony_ci * @ndev: Pointer to net_device structure 124662306a36Sopenharmony_ci * @new_mtu: New mtu value to be applied 124762306a36Sopenharmony_ci * 124862306a36Sopenharmony_ci * Return: Always returns 0 (success). 124962306a36Sopenharmony_ci * 125062306a36Sopenharmony_ci * This is the change mtu driver routine. It checks if the Axi Ethernet 125162306a36Sopenharmony_ci * hardware supports jumbo frames before changing the mtu. This can be 125262306a36Sopenharmony_ci * called only when the device is not up. 125362306a36Sopenharmony_ci */ 125462306a36Sopenharmony_cistatic int axienet_change_mtu(struct net_device *ndev, int new_mtu) 125562306a36Sopenharmony_ci{ 125662306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci if (netif_running(ndev)) 125962306a36Sopenharmony_ci return -EBUSY; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci if ((new_mtu + VLAN_ETH_HLEN + 126262306a36Sopenharmony_ci XAE_TRL_SIZE) > lp->rxmem) 126362306a36Sopenharmony_ci return -EINVAL; 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci ndev->mtu = new_mtu; 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci return 0; 126862306a36Sopenharmony_ci} 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 127162306a36Sopenharmony_ci/** 127262306a36Sopenharmony_ci * axienet_poll_controller - Axi Ethernet poll mechanism. 127362306a36Sopenharmony_ci * @ndev: Pointer to net_device structure 127462306a36Sopenharmony_ci * 127562306a36Sopenharmony_ci * This implements Rx/Tx ISR poll mechanisms. The interrupts are disabled prior 127662306a36Sopenharmony_ci * to polling the ISRs and are enabled back after the polling is done. 127762306a36Sopenharmony_ci */ 127862306a36Sopenharmony_cistatic void axienet_poll_controller(struct net_device *ndev) 127962306a36Sopenharmony_ci{ 128062306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 128162306a36Sopenharmony_ci disable_irq(lp->tx_irq); 128262306a36Sopenharmony_ci disable_irq(lp->rx_irq); 128362306a36Sopenharmony_ci axienet_rx_irq(lp->tx_irq, ndev); 128462306a36Sopenharmony_ci axienet_tx_irq(lp->rx_irq, ndev); 128562306a36Sopenharmony_ci enable_irq(lp->tx_irq); 128662306a36Sopenharmony_ci enable_irq(lp->rx_irq); 128762306a36Sopenharmony_ci} 128862306a36Sopenharmony_ci#endif 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_cistatic int axienet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 129162306a36Sopenharmony_ci{ 129262306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(dev); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci if (!netif_running(dev)) 129562306a36Sopenharmony_ci return -EINVAL; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci return phylink_mii_ioctl(lp->phylink, rq, cmd); 129862306a36Sopenharmony_ci} 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_cistatic void 130162306a36Sopenharmony_ciaxienet_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) 130262306a36Sopenharmony_ci{ 130362306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(dev); 130462306a36Sopenharmony_ci unsigned int start; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci netdev_stats_to_stats64(stats, &dev->stats); 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci do { 130962306a36Sopenharmony_ci start = u64_stats_fetch_begin(&lp->rx_stat_sync); 131062306a36Sopenharmony_ci stats->rx_packets = u64_stats_read(&lp->rx_packets); 131162306a36Sopenharmony_ci stats->rx_bytes = u64_stats_read(&lp->rx_bytes); 131262306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&lp->rx_stat_sync, start)); 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci do { 131562306a36Sopenharmony_ci start = u64_stats_fetch_begin(&lp->tx_stat_sync); 131662306a36Sopenharmony_ci stats->tx_packets = u64_stats_read(&lp->tx_packets); 131762306a36Sopenharmony_ci stats->tx_bytes = u64_stats_read(&lp->tx_bytes); 131862306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&lp->tx_stat_sync, start)); 131962306a36Sopenharmony_ci} 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_cistatic const struct net_device_ops axienet_netdev_ops = { 132262306a36Sopenharmony_ci .ndo_open = axienet_open, 132362306a36Sopenharmony_ci .ndo_stop = axienet_stop, 132462306a36Sopenharmony_ci .ndo_start_xmit = axienet_start_xmit, 132562306a36Sopenharmony_ci .ndo_get_stats64 = axienet_get_stats64, 132662306a36Sopenharmony_ci .ndo_change_mtu = axienet_change_mtu, 132762306a36Sopenharmony_ci .ndo_set_mac_address = netdev_set_mac_address, 132862306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 132962306a36Sopenharmony_ci .ndo_eth_ioctl = axienet_ioctl, 133062306a36Sopenharmony_ci .ndo_set_rx_mode = axienet_set_multicast_list, 133162306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 133262306a36Sopenharmony_ci .ndo_poll_controller = axienet_poll_controller, 133362306a36Sopenharmony_ci#endif 133462306a36Sopenharmony_ci}; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci/** 133762306a36Sopenharmony_ci * axienet_ethtools_get_drvinfo - Get various Axi Ethernet driver information. 133862306a36Sopenharmony_ci * @ndev: Pointer to net_device structure 133962306a36Sopenharmony_ci * @ed: Pointer to ethtool_drvinfo structure 134062306a36Sopenharmony_ci * 134162306a36Sopenharmony_ci * This implements ethtool command for getting the driver information. 134262306a36Sopenharmony_ci * Issue "ethtool -i ethX" under linux prompt to execute this function. 134362306a36Sopenharmony_ci */ 134462306a36Sopenharmony_cistatic void axienet_ethtools_get_drvinfo(struct net_device *ndev, 134562306a36Sopenharmony_ci struct ethtool_drvinfo *ed) 134662306a36Sopenharmony_ci{ 134762306a36Sopenharmony_ci strscpy(ed->driver, DRIVER_NAME, sizeof(ed->driver)); 134862306a36Sopenharmony_ci strscpy(ed->version, DRIVER_VERSION, sizeof(ed->version)); 134962306a36Sopenharmony_ci} 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci/** 135262306a36Sopenharmony_ci * axienet_ethtools_get_regs_len - Get the total regs length present in the 135362306a36Sopenharmony_ci * AxiEthernet core. 135462306a36Sopenharmony_ci * @ndev: Pointer to net_device structure 135562306a36Sopenharmony_ci * 135662306a36Sopenharmony_ci * This implements ethtool command for getting the total register length 135762306a36Sopenharmony_ci * information. 135862306a36Sopenharmony_ci * 135962306a36Sopenharmony_ci * Return: the total regs length 136062306a36Sopenharmony_ci */ 136162306a36Sopenharmony_cistatic int axienet_ethtools_get_regs_len(struct net_device *ndev) 136262306a36Sopenharmony_ci{ 136362306a36Sopenharmony_ci return sizeof(u32) * AXIENET_REGS_N; 136462306a36Sopenharmony_ci} 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci/** 136762306a36Sopenharmony_ci * axienet_ethtools_get_regs - Dump the contents of all registers present 136862306a36Sopenharmony_ci * in AxiEthernet core. 136962306a36Sopenharmony_ci * @ndev: Pointer to net_device structure 137062306a36Sopenharmony_ci * @regs: Pointer to ethtool_regs structure 137162306a36Sopenharmony_ci * @ret: Void pointer used to return the contents of the registers. 137262306a36Sopenharmony_ci * 137362306a36Sopenharmony_ci * This implements ethtool command for getting the Axi Ethernet register dump. 137462306a36Sopenharmony_ci * Issue "ethtool -d ethX" to execute this function. 137562306a36Sopenharmony_ci */ 137662306a36Sopenharmony_cistatic void axienet_ethtools_get_regs(struct net_device *ndev, 137762306a36Sopenharmony_ci struct ethtool_regs *regs, void *ret) 137862306a36Sopenharmony_ci{ 137962306a36Sopenharmony_ci u32 *data = (u32 *)ret; 138062306a36Sopenharmony_ci size_t len = sizeof(u32) * AXIENET_REGS_N; 138162306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci regs->version = 0; 138462306a36Sopenharmony_ci regs->len = len; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci memset(data, 0, len); 138762306a36Sopenharmony_ci data[0] = axienet_ior(lp, XAE_RAF_OFFSET); 138862306a36Sopenharmony_ci data[1] = axienet_ior(lp, XAE_TPF_OFFSET); 138962306a36Sopenharmony_ci data[2] = axienet_ior(lp, XAE_IFGP_OFFSET); 139062306a36Sopenharmony_ci data[3] = axienet_ior(lp, XAE_IS_OFFSET); 139162306a36Sopenharmony_ci data[4] = axienet_ior(lp, XAE_IP_OFFSET); 139262306a36Sopenharmony_ci data[5] = axienet_ior(lp, XAE_IE_OFFSET); 139362306a36Sopenharmony_ci data[6] = axienet_ior(lp, XAE_TTAG_OFFSET); 139462306a36Sopenharmony_ci data[7] = axienet_ior(lp, XAE_RTAG_OFFSET); 139562306a36Sopenharmony_ci data[8] = axienet_ior(lp, XAE_UAWL_OFFSET); 139662306a36Sopenharmony_ci data[9] = axienet_ior(lp, XAE_UAWU_OFFSET); 139762306a36Sopenharmony_ci data[10] = axienet_ior(lp, XAE_TPID0_OFFSET); 139862306a36Sopenharmony_ci data[11] = axienet_ior(lp, XAE_TPID1_OFFSET); 139962306a36Sopenharmony_ci data[12] = axienet_ior(lp, XAE_PPST_OFFSET); 140062306a36Sopenharmony_ci data[13] = axienet_ior(lp, XAE_RCW0_OFFSET); 140162306a36Sopenharmony_ci data[14] = axienet_ior(lp, XAE_RCW1_OFFSET); 140262306a36Sopenharmony_ci data[15] = axienet_ior(lp, XAE_TC_OFFSET); 140362306a36Sopenharmony_ci data[16] = axienet_ior(lp, XAE_FCC_OFFSET); 140462306a36Sopenharmony_ci data[17] = axienet_ior(lp, XAE_EMMC_OFFSET); 140562306a36Sopenharmony_ci data[18] = axienet_ior(lp, XAE_PHYC_OFFSET); 140662306a36Sopenharmony_ci data[19] = axienet_ior(lp, XAE_MDIO_MC_OFFSET); 140762306a36Sopenharmony_ci data[20] = axienet_ior(lp, XAE_MDIO_MCR_OFFSET); 140862306a36Sopenharmony_ci data[21] = axienet_ior(lp, XAE_MDIO_MWD_OFFSET); 140962306a36Sopenharmony_ci data[22] = axienet_ior(lp, XAE_MDIO_MRD_OFFSET); 141062306a36Sopenharmony_ci data[27] = axienet_ior(lp, XAE_UAW0_OFFSET); 141162306a36Sopenharmony_ci data[28] = axienet_ior(lp, XAE_UAW1_OFFSET); 141262306a36Sopenharmony_ci data[29] = axienet_ior(lp, XAE_FMI_OFFSET); 141362306a36Sopenharmony_ci data[30] = axienet_ior(lp, XAE_AF0_OFFSET); 141462306a36Sopenharmony_ci data[31] = axienet_ior(lp, XAE_AF1_OFFSET); 141562306a36Sopenharmony_ci data[32] = axienet_dma_in32(lp, XAXIDMA_TX_CR_OFFSET); 141662306a36Sopenharmony_ci data[33] = axienet_dma_in32(lp, XAXIDMA_TX_SR_OFFSET); 141762306a36Sopenharmony_ci data[34] = axienet_dma_in32(lp, XAXIDMA_TX_CDESC_OFFSET); 141862306a36Sopenharmony_ci data[35] = axienet_dma_in32(lp, XAXIDMA_TX_TDESC_OFFSET); 141962306a36Sopenharmony_ci data[36] = axienet_dma_in32(lp, XAXIDMA_RX_CR_OFFSET); 142062306a36Sopenharmony_ci data[37] = axienet_dma_in32(lp, XAXIDMA_RX_SR_OFFSET); 142162306a36Sopenharmony_ci data[38] = axienet_dma_in32(lp, XAXIDMA_RX_CDESC_OFFSET); 142262306a36Sopenharmony_ci data[39] = axienet_dma_in32(lp, XAXIDMA_RX_TDESC_OFFSET); 142362306a36Sopenharmony_ci} 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_cistatic void 142662306a36Sopenharmony_ciaxienet_ethtools_get_ringparam(struct net_device *ndev, 142762306a36Sopenharmony_ci struct ethtool_ringparam *ering, 142862306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ering, 142962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 143062306a36Sopenharmony_ci{ 143162306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci ering->rx_max_pending = RX_BD_NUM_MAX; 143462306a36Sopenharmony_ci ering->rx_mini_max_pending = 0; 143562306a36Sopenharmony_ci ering->rx_jumbo_max_pending = 0; 143662306a36Sopenharmony_ci ering->tx_max_pending = TX_BD_NUM_MAX; 143762306a36Sopenharmony_ci ering->rx_pending = lp->rx_bd_num; 143862306a36Sopenharmony_ci ering->rx_mini_pending = 0; 143962306a36Sopenharmony_ci ering->rx_jumbo_pending = 0; 144062306a36Sopenharmony_ci ering->tx_pending = lp->tx_bd_num; 144162306a36Sopenharmony_ci} 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_cistatic int 144462306a36Sopenharmony_ciaxienet_ethtools_set_ringparam(struct net_device *ndev, 144562306a36Sopenharmony_ci struct ethtool_ringparam *ering, 144662306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ering, 144762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 144862306a36Sopenharmony_ci{ 144962306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci if (ering->rx_pending > RX_BD_NUM_MAX || 145262306a36Sopenharmony_ci ering->rx_mini_pending || 145362306a36Sopenharmony_ci ering->rx_jumbo_pending || 145462306a36Sopenharmony_ci ering->tx_pending < TX_BD_NUM_MIN || 145562306a36Sopenharmony_ci ering->tx_pending > TX_BD_NUM_MAX) 145662306a36Sopenharmony_ci return -EINVAL; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci if (netif_running(ndev)) 145962306a36Sopenharmony_ci return -EBUSY; 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci lp->rx_bd_num = ering->rx_pending; 146262306a36Sopenharmony_ci lp->tx_bd_num = ering->tx_pending; 146362306a36Sopenharmony_ci return 0; 146462306a36Sopenharmony_ci} 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci/** 146762306a36Sopenharmony_ci * axienet_ethtools_get_pauseparam - Get the pause parameter setting for 146862306a36Sopenharmony_ci * Tx and Rx paths. 146962306a36Sopenharmony_ci * @ndev: Pointer to net_device structure 147062306a36Sopenharmony_ci * @epauseparm: Pointer to ethtool_pauseparam structure. 147162306a36Sopenharmony_ci * 147262306a36Sopenharmony_ci * This implements ethtool command for getting axi ethernet pause frame 147362306a36Sopenharmony_ci * setting. Issue "ethtool -a ethX" to execute this function. 147462306a36Sopenharmony_ci */ 147562306a36Sopenharmony_cistatic void 147662306a36Sopenharmony_ciaxienet_ethtools_get_pauseparam(struct net_device *ndev, 147762306a36Sopenharmony_ci struct ethtool_pauseparam *epauseparm) 147862306a36Sopenharmony_ci{ 147962306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci phylink_ethtool_get_pauseparam(lp->phylink, epauseparm); 148262306a36Sopenharmony_ci} 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci/** 148562306a36Sopenharmony_ci * axienet_ethtools_set_pauseparam - Set device pause parameter(flow control) 148662306a36Sopenharmony_ci * settings. 148762306a36Sopenharmony_ci * @ndev: Pointer to net_device structure 148862306a36Sopenharmony_ci * @epauseparm:Pointer to ethtool_pauseparam structure 148962306a36Sopenharmony_ci * 149062306a36Sopenharmony_ci * This implements ethtool command for enabling flow control on Rx and Tx 149162306a36Sopenharmony_ci * paths. Issue "ethtool -A ethX tx on|off" under linux prompt to execute this 149262306a36Sopenharmony_ci * function. 149362306a36Sopenharmony_ci * 149462306a36Sopenharmony_ci * Return: 0 on success, -EFAULT if device is running 149562306a36Sopenharmony_ci */ 149662306a36Sopenharmony_cistatic int 149762306a36Sopenharmony_ciaxienet_ethtools_set_pauseparam(struct net_device *ndev, 149862306a36Sopenharmony_ci struct ethtool_pauseparam *epauseparm) 149962306a36Sopenharmony_ci{ 150062306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci return phylink_ethtool_set_pauseparam(lp->phylink, epauseparm); 150362306a36Sopenharmony_ci} 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci/** 150662306a36Sopenharmony_ci * axienet_ethtools_get_coalesce - Get DMA interrupt coalescing count. 150762306a36Sopenharmony_ci * @ndev: Pointer to net_device structure 150862306a36Sopenharmony_ci * @ecoalesce: Pointer to ethtool_coalesce structure 150962306a36Sopenharmony_ci * @kernel_coal: ethtool CQE mode setting structure 151062306a36Sopenharmony_ci * @extack: extack for reporting error messages 151162306a36Sopenharmony_ci * 151262306a36Sopenharmony_ci * This implements ethtool command for getting the DMA interrupt coalescing 151362306a36Sopenharmony_ci * count on Tx and Rx paths. Issue "ethtool -c ethX" under linux prompt to 151462306a36Sopenharmony_ci * execute this function. 151562306a36Sopenharmony_ci * 151662306a36Sopenharmony_ci * Return: 0 always 151762306a36Sopenharmony_ci */ 151862306a36Sopenharmony_cistatic int 151962306a36Sopenharmony_ciaxienet_ethtools_get_coalesce(struct net_device *ndev, 152062306a36Sopenharmony_ci struct ethtool_coalesce *ecoalesce, 152162306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 152262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 152362306a36Sopenharmony_ci{ 152462306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci ecoalesce->rx_max_coalesced_frames = lp->coalesce_count_rx; 152762306a36Sopenharmony_ci ecoalesce->rx_coalesce_usecs = lp->coalesce_usec_rx; 152862306a36Sopenharmony_ci ecoalesce->tx_max_coalesced_frames = lp->coalesce_count_tx; 152962306a36Sopenharmony_ci ecoalesce->tx_coalesce_usecs = lp->coalesce_usec_tx; 153062306a36Sopenharmony_ci return 0; 153162306a36Sopenharmony_ci} 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci/** 153462306a36Sopenharmony_ci * axienet_ethtools_set_coalesce - Set DMA interrupt coalescing count. 153562306a36Sopenharmony_ci * @ndev: Pointer to net_device structure 153662306a36Sopenharmony_ci * @ecoalesce: Pointer to ethtool_coalesce structure 153762306a36Sopenharmony_ci * @kernel_coal: ethtool CQE mode setting structure 153862306a36Sopenharmony_ci * @extack: extack for reporting error messages 153962306a36Sopenharmony_ci * 154062306a36Sopenharmony_ci * This implements ethtool command for setting the DMA interrupt coalescing 154162306a36Sopenharmony_ci * count on Tx and Rx paths. Issue "ethtool -C ethX rx-frames 5" under linux 154262306a36Sopenharmony_ci * prompt to execute this function. 154362306a36Sopenharmony_ci * 154462306a36Sopenharmony_ci * Return: 0, on success, Non-zero error value on failure. 154562306a36Sopenharmony_ci */ 154662306a36Sopenharmony_cistatic int 154762306a36Sopenharmony_ciaxienet_ethtools_set_coalesce(struct net_device *ndev, 154862306a36Sopenharmony_ci struct ethtool_coalesce *ecoalesce, 154962306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 155062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 155162306a36Sopenharmony_ci{ 155262306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci if (netif_running(ndev)) { 155562306a36Sopenharmony_ci netdev_err(ndev, 155662306a36Sopenharmony_ci "Please stop netif before applying configuration\n"); 155762306a36Sopenharmony_ci return -EFAULT; 155862306a36Sopenharmony_ci } 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci if (ecoalesce->rx_max_coalesced_frames) 156162306a36Sopenharmony_ci lp->coalesce_count_rx = ecoalesce->rx_max_coalesced_frames; 156262306a36Sopenharmony_ci if (ecoalesce->rx_coalesce_usecs) 156362306a36Sopenharmony_ci lp->coalesce_usec_rx = ecoalesce->rx_coalesce_usecs; 156462306a36Sopenharmony_ci if (ecoalesce->tx_max_coalesced_frames) 156562306a36Sopenharmony_ci lp->coalesce_count_tx = ecoalesce->tx_max_coalesced_frames; 156662306a36Sopenharmony_ci if (ecoalesce->tx_coalesce_usecs) 156762306a36Sopenharmony_ci lp->coalesce_usec_tx = ecoalesce->tx_coalesce_usecs; 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci return 0; 157062306a36Sopenharmony_ci} 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_cistatic int 157362306a36Sopenharmony_ciaxienet_ethtools_get_link_ksettings(struct net_device *ndev, 157462306a36Sopenharmony_ci struct ethtool_link_ksettings *cmd) 157562306a36Sopenharmony_ci{ 157662306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci return phylink_ethtool_ksettings_get(lp->phylink, cmd); 157962306a36Sopenharmony_ci} 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_cistatic int 158262306a36Sopenharmony_ciaxienet_ethtools_set_link_ksettings(struct net_device *ndev, 158362306a36Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 158462306a36Sopenharmony_ci{ 158562306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci return phylink_ethtool_ksettings_set(lp->phylink, cmd); 158862306a36Sopenharmony_ci} 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_cistatic int axienet_ethtools_nway_reset(struct net_device *dev) 159162306a36Sopenharmony_ci{ 159262306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(dev); 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci return phylink_ethtool_nway_reset(lp->phylink); 159562306a36Sopenharmony_ci} 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_cistatic const struct ethtool_ops axienet_ethtool_ops = { 159862306a36Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_MAX_FRAMES | 159962306a36Sopenharmony_ci ETHTOOL_COALESCE_USECS, 160062306a36Sopenharmony_ci .get_drvinfo = axienet_ethtools_get_drvinfo, 160162306a36Sopenharmony_ci .get_regs_len = axienet_ethtools_get_regs_len, 160262306a36Sopenharmony_ci .get_regs = axienet_ethtools_get_regs, 160362306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 160462306a36Sopenharmony_ci .get_ringparam = axienet_ethtools_get_ringparam, 160562306a36Sopenharmony_ci .set_ringparam = axienet_ethtools_set_ringparam, 160662306a36Sopenharmony_ci .get_pauseparam = axienet_ethtools_get_pauseparam, 160762306a36Sopenharmony_ci .set_pauseparam = axienet_ethtools_set_pauseparam, 160862306a36Sopenharmony_ci .get_coalesce = axienet_ethtools_get_coalesce, 160962306a36Sopenharmony_ci .set_coalesce = axienet_ethtools_set_coalesce, 161062306a36Sopenharmony_ci .get_link_ksettings = axienet_ethtools_get_link_ksettings, 161162306a36Sopenharmony_ci .set_link_ksettings = axienet_ethtools_set_link_ksettings, 161262306a36Sopenharmony_ci .nway_reset = axienet_ethtools_nway_reset, 161362306a36Sopenharmony_ci}; 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_cistatic struct axienet_local *pcs_to_axienet_local(struct phylink_pcs *pcs) 161662306a36Sopenharmony_ci{ 161762306a36Sopenharmony_ci return container_of(pcs, struct axienet_local, pcs); 161862306a36Sopenharmony_ci} 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_cistatic void axienet_pcs_get_state(struct phylink_pcs *pcs, 162162306a36Sopenharmony_ci struct phylink_link_state *state) 162262306a36Sopenharmony_ci{ 162362306a36Sopenharmony_ci struct mdio_device *pcs_phy = pcs_to_axienet_local(pcs)->pcs_phy; 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci phylink_mii_c22_pcs_get_state(pcs_phy, state); 162662306a36Sopenharmony_ci} 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_cistatic void axienet_pcs_an_restart(struct phylink_pcs *pcs) 162962306a36Sopenharmony_ci{ 163062306a36Sopenharmony_ci struct mdio_device *pcs_phy = pcs_to_axienet_local(pcs)->pcs_phy; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci phylink_mii_c22_pcs_an_restart(pcs_phy); 163362306a36Sopenharmony_ci} 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_cistatic int axienet_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, 163662306a36Sopenharmony_ci phy_interface_t interface, 163762306a36Sopenharmony_ci const unsigned long *advertising, 163862306a36Sopenharmony_ci bool permit_pause_to_mac) 163962306a36Sopenharmony_ci{ 164062306a36Sopenharmony_ci struct mdio_device *pcs_phy = pcs_to_axienet_local(pcs)->pcs_phy; 164162306a36Sopenharmony_ci struct net_device *ndev = pcs_to_axienet_local(pcs)->ndev; 164262306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 164362306a36Sopenharmony_ci int ret; 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci if (lp->switch_x_sgmii) { 164662306a36Sopenharmony_ci ret = mdiodev_write(pcs_phy, XLNX_MII_STD_SELECT_REG, 164762306a36Sopenharmony_ci interface == PHY_INTERFACE_MODE_SGMII ? 164862306a36Sopenharmony_ci XLNX_MII_STD_SELECT_SGMII : 0); 164962306a36Sopenharmony_ci if (ret < 0) { 165062306a36Sopenharmony_ci netdev_warn(ndev, 165162306a36Sopenharmony_ci "Failed to switch PHY interface: %d\n", 165262306a36Sopenharmony_ci ret); 165362306a36Sopenharmony_ci return ret; 165462306a36Sopenharmony_ci } 165562306a36Sopenharmony_ci } 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci ret = phylink_mii_c22_pcs_config(pcs_phy, interface, advertising, 165862306a36Sopenharmony_ci neg_mode); 165962306a36Sopenharmony_ci if (ret < 0) 166062306a36Sopenharmony_ci netdev_warn(ndev, "Failed to configure PCS: %d\n", ret); 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci return ret; 166362306a36Sopenharmony_ci} 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_cistatic const struct phylink_pcs_ops axienet_pcs_ops = { 166662306a36Sopenharmony_ci .pcs_get_state = axienet_pcs_get_state, 166762306a36Sopenharmony_ci .pcs_config = axienet_pcs_config, 166862306a36Sopenharmony_ci .pcs_an_restart = axienet_pcs_an_restart, 166962306a36Sopenharmony_ci}; 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_cistatic struct phylink_pcs *axienet_mac_select_pcs(struct phylink_config *config, 167262306a36Sopenharmony_ci phy_interface_t interface) 167362306a36Sopenharmony_ci{ 167462306a36Sopenharmony_ci struct net_device *ndev = to_net_dev(config->dev); 167562306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci if (interface == PHY_INTERFACE_MODE_1000BASEX || 167862306a36Sopenharmony_ci interface == PHY_INTERFACE_MODE_SGMII) 167962306a36Sopenharmony_ci return &lp->pcs; 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci return NULL; 168262306a36Sopenharmony_ci} 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_cistatic void axienet_mac_config(struct phylink_config *config, unsigned int mode, 168562306a36Sopenharmony_ci const struct phylink_link_state *state) 168662306a36Sopenharmony_ci{ 168762306a36Sopenharmony_ci /* nothing meaningful to do */ 168862306a36Sopenharmony_ci} 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_cistatic void axienet_mac_link_down(struct phylink_config *config, 169162306a36Sopenharmony_ci unsigned int mode, 169262306a36Sopenharmony_ci phy_interface_t interface) 169362306a36Sopenharmony_ci{ 169462306a36Sopenharmony_ci /* nothing meaningful to do */ 169562306a36Sopenharmony_ci} 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_cistatic void axienet_mac_link_up(struct phylink_config *config, 169862306a36Sopenharmony_ci struct phy_device *phy, 169962306a36Sopenharmony_ci unsigned int mode, phy_interface_t interface, 170062306a36Sopenharmony_ci int speed, int duplex, 170162306a36Sopenharmony_ci bool tx_pause, bool rx_pause) 170262306a36Sopenharmony_ci{ 170362306a36Sopenharmony_ci struct net_device *ndev = to_net_dev(config->dev); 170462306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 170562306a36Sopenharmony_ci u32 emmc_reg, fcc_reg; 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci emmc_reg = axienet_ior(lp, XAE_EMMC_OFFSET); 170862306a36Sopenharmony_ci emmc_reg &= ~XAE_EMMC_LINKSPEED_MASK; 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci switch (speed) { 171162306a36Sopenharmony_ci case SPEED_1000: 171262306a36Sopenharmony_ci emmc_reg |= XAE_EMMC_LINKSPD_1000; 171362306a36Sopenharmony_ci break; 171462306a36Sopenharmony_ci case SPEED_100: 171562306a36Sopenharmony_ci emmc_reg |= XAE_EMMC_LINKSPD_100; 171662306a36Sopenharmony_ci break; 171762306a36Sopenharmony_ci case SPEED_10: 171862306a36Sopenharmony_ci emmc_reg |= XAE_EMMC_LINKSPD_10; 171962306a36Sopenharmony_ci break; 172062306a36Sopenharmony_ci default: 172162306a36Sopenharmony_ci dev_err(&ndev->dev, 172262306a36Sopenharmony_ci "Speed other than 10, 100 or 1Gbps is not supported\n"); 172362306a36Sopenharmony_ci break; 172462306a36Sopenharmony_ci } 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci axienet_iow(lp, XAE_EMMC_OFFSET, emmc_reg); 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci fcc_reg = axienet_ior(lp, XAE_FCC_OFFSET); 172962306a36Sopenharmony_ci if (tx_pause) 173062306a36Sopenharmony_ci fcc_reg |= XAE_FCC_FCTX_MASK; 173162306a36Sopenharmony_ci else 173262306a36Sopenharmony_ci fcc_reg &= ~XAE_FCC_FCTX_MASK; 173362306a36Sopenharmony_ci if (rx_pause) 173462306a36Sopenharmony_ci fcc_reg |= XAE_FCC_FCRX_MASK; 173562306a36Sopenharmony_ci else 173662306a36Sopenharmony_ci fcc_reg &= ~XAE_FCC_FCRX_MASK; 173762306a36Sopenharmony_ci axienet_iow(lp, XAE_FCC_OFFSET, fcc_reg); 173862306a36Sopenharmony_ci} 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_cistatic const struct phylink_mac_ops axienet_phylink_ops = { 174162306a36Sopenharmony_ci .mac_select_pcs = axienet_mac_select_pcs, 174262306a36Sopenharmony_ci .mac_config = axienet_mac_config, 174362306a36Sopenharmony_ci .mac_link_down = axienet_mac_link_down, 174462306a36Sopenharmony_ci .mac_link_up = axienet_mac_link_up, 174562306a36Sopenharmony_ci}; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci/** 174862306a36Sopenharmony_ci * axienet_dma_err_handler - Work queue task for Axi DMA Error 174962306a36Sopenharmony_ci * @work: pointer to work_struct 175062306a36Sopenharmony_ci * 175162306a36Sopenharmony_ci * Resets the Axi DMA and Axi Ethernet devices, and reconfigures the 175262306a36Sopenharmony_ci * Tx/Rx BDs. 175362306a36Sopenharmony_ci */ 175462306a36Sopenharmony_cistatic void axienet_dma_err_handler(struct work_struct *work) 175562306a36Sopenharmony_ci{ 175662306a36Sopenharmony_ci u32 i; 175762306a36Sopenharmony_ci u32 axienet_status; 175862306a36Sopenharmony_ci struct axidma_bd *cur_p; 175962306a36Sopenharmony_ci struct axienet_local *lp = container_of(work, struct axienet_local, 176062306a36Sopenharmony_ci dma_err_task); 176162306a36Sopenharmony_ci struct net_device *ndev = lp->ndev; 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci napi_disable(&lp->napi_tx); 176462306a36Sopenharmony_ci napi_disable(&lp->napi_rx); 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci axienet_setoptions(ndev, lp->options & 176762306a36Sopenharmony_ci ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN)); 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci axienet_dma_stop(lp); 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci for (i = 0; i < lp->tx_bd_num; i++) { 177262306a36Sopenharmony_ci cur_p = &lp->tx_bd_v[i]; 177362306a36Sopenharmony_ci if (cur_p->cntrl) { 177462306a36Sopenharmony_ci dma_addr_t addr = desc_get_phys_addr(lp, cur_p); 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci dma_unmap_single(lp->dev, addr, 177762306a36Sopenharmony_ci (cur_p->cntrl & 177862306a36Sopenharmony_ci XAXIDMA_BD_CTRL_LENGTH_MASK), 177962306a36Sopenharmony_ci DMA_TO_DEVICE); 178062306a36Sopenharmony_ci } 178162306a36Sopenharmony_ci if (cur_p->skb) 178262306a36Sopenharmony_ci dev_kfree_skb_irq(cur_p->skb); 178362306a36Sopenharmony_ci cur_p->phys = 0; 178462306a36Sopenharmony_ci cur_p->phys_msb = 0; 178562306a36Sopenharmony_ci cur_p->cntrl = 0; 178662306a36Sopenharmony_ci cur_p->status = 0; 178762306a36Sopenharmony_ci cur_p->app0 = 0; 178862306a36Sopenharmony_ci cur_p->app1 = 0; 178962306a36Sopenharmony_ci cur_p->app2 = 0; 179062306a36Sopenharmony_ci cur_p->app3 = 0; 179162306a36Sopenharmony_ci cur_p->app4 = 0; 179262306a36Sopenharmony_ci cur_p->skb = NULL; 179362306a36Sopenharmony_ci } 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci for (i = 0; i < lp->rx_bd_num; i++) { 179662306a36Sopenharmony_ci cur_p = &lp->rx_bd_v[i]; 179762306a36Sopenharmony_ci cur_p->status = 0; 179862306a36Sopenharmony_ci cur_p->app0 = 0; 179962306a36Sopenharmony_ci cur_p->app1 = 0; 180062306a36Sopenharmony_ci cur_p->app2 = 0; 180162306a36Sopenharmony_ci cur_p->app3 = 0; 180262306a36Sopenharmony_ci cur_p->app4 = 0; 180362306a36Sopenharmony_ci } 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci lp->tx_bd_ci = 0; 180662306a36Sopenharmony_ci lp->tx_bd_tail = 0; 180762306a36Sopenharmony_ci lp->rx_bd_ci = 0; 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci axienet_dma_start(lp); 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci axienet_status = axienet_ior(lp, XAE_RCW1_OFFSET); 181262306a36Sopenharmony_ci axienet_status &= ~XAE_RCW1_RX_MASK; 181362306a36Sopenharmony_ci axienet_iow(lp, XAE_RCW1_OFFSET, axienet_status); 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci axienet_status = axienet_ior(lp, XAE_IP_OFFSET); 181662306a36Sopenharmony_ci if (axienet_status & XAE_INT_RXRJECT_MASK) 181762306a36Sopenharmony_ci axienet_iow(lp, XAE_IS_OFFSET, XAE_INT_RXRJECT_MASK); 181862306a36Sopenharmony_ci axienet_iow(lp, XAE_IE_OFFSET, lp->eth_irq > 0 ? 181962306a36Sopenharmony_ci XAE_INT_RECV_ERROR_MASK : 0); 182062306a36Sopenharmony_ci axienet_iow(lp, XAE_FCC_OFFSET, XAE_FCC_FCRX_MASK); 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci /* Sync default options with HW but leave receiver and 182362306a36Sopenharmony_ci * transmitter disabled. 182462306a36Sopenharmony_ci */ 182562306a36Sopenharmony_ci axienet_setoptions(ndev, lp->options & 182662306a36Sopenharmony_ci ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN)); 182762306a36Sopenharmony_ci axienet_set_mac_address(ndev, NULL); 182862306a36Sopenharmony_ci axienet_set_multicast_list(ndev); 182962306a36Sopenharmony_ci axienet_setoptions(ndev, lp->options); 183062306a36Sopenharmony_ci napi_enable(&lp->napi_rx); 183162306a36Sopenharmony_ci napi_enable(&lp->napi_tx); 183262306a36Sopenharmony_ci} 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci/** 183562306a36Sopenharmony_ci * axienet_probe - Axi Ethernet probe function. 183662306a36Sopenharmony_ci * @pdev: Pointer to platform device structure. 183762306a36Sopenharmony_ci * 183862306a36Sopenharmony_ci * Return: 0, on success 183962306a36Sopenharmony_ci * Non-zero error value on failure. 184062306a36Sopenharmony_ci * 184162306a36Sopenharmony_ci * This is the probe routine for Axi Ethernet driver. This is called before 184262306a36Sopenharmony_ci * any other driver routines are invoked. It allocates and sets up the Ethernet 184362306a36Sopenharmony_ci * device. Parses through device tree and populates fields of 184462306a36Sopenharmony_ci * axienet_local. It registers the Ethernet device. 184562306a36Sopenharmony_ci */ 184662306a36Sopenharmony_cistatic int axienet_probe(struct platform_device *pdev) 184762306a36Sopenharmony_ci{ 184862306a36Sopenharmony_ci int ret; 184962306a36Sopenharmony_ci struct device_node *np; 185062306a36Sopenharmony_ci struct axienet_local *lp; 185162306a36Sopenharmony_ci struct net_device *ndev; 185262306a36Sopenharmony_ci struct resource *ethres; 185362306a36Sopenharmony_ci u8 mac_addr[ETH_ALEN]; 185462306a36Sopenharmony_ci int addr_width = 32; 185562306a36Sopenharmony_ci u32 value; 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci ndev = alloc_etherdev(sizeof(*lp)); 185862306a36Sopenharmony_ci if (!ndev) 185962306a36Sopenharmony_ci return -ENOMEM; 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci platform_set_drvdata(pdev, ndev); 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci SET_NETDEV_DEV(ndev, &pdev->dev); 186462306a36Sopenharmony_ci ndev->flags &= ~IFF_MULTICAST; /* clear multicast */ 186562306a36Sopenharmony_ci ndev->features = NETIF_F_SG; 186662306a36Sopenharmony_ci ndev->netdev_ops = &axienet_netdev_ops; 186762306a36Sopenharmony_ci ndev->ethtool_ops = &axienet_ethtool_ops; 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci /* MTU range: 64 - 9000 */ 187062306a36Sopenharmony_ci ndev->min_mtu = 64; 187162306a36Sopenharmony_ci ndev->max_mtu = XAE_JUMBO_MTU; 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci lp = netdev_priv(ndev); 187462306a36Sopenharmony_ci lp->ndev = ndev; 187562306a36Sopenharmony_ci lp->dev = &pdev->dev; 187662306a36Sopenharmony_ci lp->options = XAE_OPTION_DEFAULTS; 187762306a36Sopenharmony_ci lp->rx_bd_num = RX_BD_NUM_DEFAULT; 187862306a36Sopenharmony_ci lp->tx_bd_num = TX_BD_NUM_DEFAULT; 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci u64_stats_init(&lp->rx_stat_sync); 188162306a36Sopenharmony_ci u64_stats_init(&lp->tx_stat_sync); 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci netif_napi_add(ndev, &lp->napi_rx, axienet_rx_poll); 188462306a36Sopenharmony_ci netif_napi_add(ndev, &lp->napi_tx, axienet_tx_poll); 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci lp->axi_clk = devm_clk_get_optional(&pdev->dev, "s_axi_lite_clk"); 188762306a36Sopenharmony_ci if (!lp->axi_clk) { 188862306a36Sopenharmony_ci /* For backward compatibility, if named AXI clock is not present, 188962306a36Sopenharmony_ci * treat the first clock specified as the AXI clock. 189062306a36Sopenharmony_ci */ 189162306a36Sopenharmony_ci lp->axi_clk = devm_clk_get_optional(&pdev->dev, NULL); 189262306a36Sopenharmony_ci } 189362306a36Sopenharmony_ci if (IS_ERR(lp->axi_clk)) { 189462306a36Sopenharmony_ci ret = PTR_ERR(lp->axi_clk); 189562306a36Sopenharmony_ci goto free_netdev; 189662306a36Sopenharmony_ci } 189762306a36Sopenharmony_ci ret = clk_prepare_enable(lp->axi_clk); 189862306a36Sopenharmony_ci if (ret) { 189962306a36Sopenharmony_ci dev_err(&pdev->dev, "Unable to enable AXI clock: %d\n", ret); 190062306a36Sopenharmony_ci goto free_netdev; 190162306a36Sopenharmony_ci } 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci lp->misc_clks[0].id = "axis_clk"; 190462306a36Sopenharmony_ci lp->misc_clks[1].id = "ref_clk"; 190562306a36Sopenharmony_ci lp->misc_clks[2].id = "mgt_clk"; 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ci ret = devm_clk_bulk_get_optional(&pdev->dev, XAE_NUM_MISC_CLOCKS, lp->misc_clks); 190862306a36Sopenharmony_ci if (ret) 190962306a36Sopenharmony_ci goto cleanup_clk; 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci ret = clk_bulk_prepare_enable(XAE_NUM_MISC_CLOCKS, lp->misc_clks); 191262306a36Sopenharmony_ci if (ret) 191362306a36Sopenharmony_ci goto cleanup_clk; 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci /* Map device registers */ 191662306a36Sopenharmony_ci lp->regs = devm_platform_get_and_ioremap_resource(pdev, 0, ðres); 191762306a36Sopenharmony_ci if (IS_ERR(lp->regs)) { 191862306a36Sopenharmony_ci ret = PTR_ERR(lp->regs); 191962306a36Sopenharmony_ci goto cleanup_clk; 192062306a36Sopenharmony_ci } 192162306a36Sopenharmony_ci lp->regs_start = ethres->start; 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci /* Setup checksum offload, but default to off if not specified */ 192462306a36Sopenharmony_ci lp->features = 0; 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci ret = of_property_read_u32(pdev->dev.of_node, "xlnx,txcsum", &value); 192762306a36Sopenharmony_ci if (!ret) { 192862306a36Sopenharmony_ci switch (value) { 192962306a36Sopenharmony_ci case 1: 193062306a36Sopenharmony_ci lp->csum_offload_on_tx_path = 193162306a36Sopenharmony_ci XAE_FEATURE_PARTIAL_TX_CSUM; 193262306a36Sopenharmony_ci lp->features |= XAE_FEATURE_PARTIAL_TX_CSUM; 193362306a36Sopenharmony_ci /* Can checksum TCP/UDP over IPv4. */ 193462306a36Sopenharmony_ci ndev->features |= NETIF_F_IP_CSUM; 193562306a36Sopenharmony_ci break; 193662306a36Sopenharmony_ci case 2: 193762306a36Sopenharmony_ci lp->csum_offload_on_tx_path = 193862306a36Sopenharmony_ci XAE_FEATURE_FULL_TX_CSUM; 193962306a36Sopenharmony_ci lp->features |= XAE_FEATURE_FULL_TX_CSUM; 194062306a36Sopenharmony_ci /* Can checksum TCP/UDP over IPv4. */ 194162306a36Sopenharmony_ci ndev->features |= NETIF_F_IP_CSUM; 194262306a36Sopenharmony_ci break; 194362306a36Sopenharmony_ci default: 194462306a36Sopenharmony_ci lp->csum_offload_on_tx_path = XAE_NO_CSUM_OFFLOAD; 194562306a36Sopenharmony_ci } 194662306a36Sopenharmony_ci } 194762306a36Sopenharmony_ci ret = of_property_read_u32(pdev->dev.of_node, "xlnx,rxcsum", &value); 194862306a36Sopenharmony_ci if (!ret) { 194962306a36Sopenharmony_ci switch (value) { 195062306a36Sopenharmony_ci case 1: 195162306a36Sopenharmony_ci lp->csum_offload_on_rx_path = 195262306a36Sopenharmony_ci XAE_FEATURE_PARTIAL_RX_CSUM; 195362306a36Sopenharmony_ci lp->features |= XAE_FEATURE_PARTIAL_RX_CSUM; 195462306a36Sopenharmony_ci break; 195562306a36Sopenharmony_ci case 2: 195662306a36Sopenharmony_ci lp->csum_offload_on_rx_path = 195762306a36Sopenharmony_ci XAE_FEATURE_FULL_RX_CSUM; 195862306a36Sopenharmony_ci lp->features |= XAE_FEATURE_FULL_RX_CSUM; 195962306a36Sopenharmony_ci break; 196062306a36Sopenharmony_ci default: 196162306a36Sopenharmony_ci lp->csum_offload_on_rx_path = XAE_NO_CSUM_OFFLOAD; 196262306a36Sopenharmony_ci } 196362306a36Sopenharmony_ci } 196462306a36Sopenharmony_ci /* For supporting jumbo frames, the Axi Ethernet hardware must have 196562306a36Sopenharmony_ci * a larger Rx/Tx Memory. Typically, the size must be large so that 196662306a36Sopenharmony_ci * we can enable jumbo option and start supporting jumbo frames. 196762306a36Sopenharmony_ci * Here we check for memory allocated for Rx/Tx in the hardware from 196862306a36Sopenharmony_ci * the device-tree and accordingly set flags. 196962306a36Sopenharmony_ci */ 197062306a36Sopenharmony_ci of_property_read_u32(pdev->dev.of_node, "xlnx,rxmem", &lp->rxmem); 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci lp->switch_x_sgmii = of_property_read_bool(pdev->dev.of_node, 197362306a36Sopenharmony_ci "xlnx,switch-x-sgmii"); 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci /* Start with the proprietary, and broken phy_type */ 197662306a36Sopenharmony_ci ret = of_property_read_u32(pdev->dev.of_node, "xlnx,phy-type", &value); 197762306a36Sopenharmony_ci if (!ret) { 197862306a36Sopenharmony_ci netdev_warn(ndev, "Please upgrade your device tree binary blob to use phy-mode"); 197962306a36Sopenharmony_ci switch (value) { 198062306a36Sopenharmony_ci case XAE_PHY_TYPE_MII: 198162306a36Sopenharmony_ci lp->phy_mode = PHY_INTERFACE_MODE_MII; 198262306a36Sopenharmony_ci break; 198362306a36Sopenharmony_ci case XAE_PHY_TYPE_GMII: 198462306a36Sopenharmony_ci lp->phy_mode = PHY_INTERFACE_MODE_GMII; 198562306a36Sopenharmony_ci break; 198662306a36Sopenharmony_ci case XAE_PHY_TYPE_RGMII_2_0: 198762306a36Sopenharmony_ci lp->phy_mode = PHY_INTERFACE_MODE_RGMII_ID; 198862306a36Sopenharmony_ci break; 198962306a36Sopenharmony_ci case XAE_PHY_TYPE_SGMII: 199062306a36Sopenharmony_ci lp->phy_mode = PHY_INTERFACE_MODE_SGMII; 199162306a36Sopenharmony_ci break; 199262306a36Sopenharmony_ci case XAE_PHY_TYPE_1000BASE_X: 199362306a36Sopenharmony_ci lp->phy_mode = PHY_INTERFACE_MODE_1000BASEX; 199462306a36Sopenharmony_ci break; 199562306a36Sopenharmony_ci default: 199662306a36Sopenharmony_ci ret = -EINVAL; 199762306a36Sopenharmony_ci goto cleanup_clk; 199862306a36Sopenharmony_ci } 199962306a36Sopenharmony_ci } else { 200062306a36Sopenharmony_ci ret = of_get_phy_mode(pdev->dev.of_node, &lp->phy_mode); 200162306a36Sopenharmony_ci if (ret) 200262306a36Sopenharmony_ci goto cleanup_clk; 200362306a36Sopenharmony_ci } 200462306a36Sopenharmony_ci if (lp->switch_x_sgmii && lp->phy_mode != PHY_INTERFACE_MODE_SGMII && 200562306a36Sopenharmony_ci lp->phy_mode != PHY_INTERFACE_MODE_1000BASEX) { 200662306a36Sopenharmony_ci dev_err(&pdev->dev, "xlnx,switch-x-sgmii only supported with SGMII or 1000BaseX\n"); 200762306a36Sopenharmony_ci ret = -EINVAL; 200862306a36Sopenharmony_ci goto cleanup_clk; 200962306a36Sopenharmony_ci } 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_ci /* Find the DMA node, map the DMA registers, and decode the DMA IRQs */ 201262306a36Sopenharmony_ci np = of_parse_phandle(pdev->dev.of_node, "axistream-connected", 0); 201362306a36Sopenharmony_ci if (np) { 201462306a36Sopenharmony_ci struct resource dmares; 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci ret = of_address_to_resource(np, 0, &dmares); 201762306a36Sopenharmony_ci if (ret) { 201862306a36Sopenharmony_ci dev_err(&pdev->dev, 201962306a36Sopenharmony_ci "unable to get DMA resource\n"); 202062306a36Sopenharmony_ci of_node_put(np); 202162306a36Sopenharmony_ci goto cleanup_clk; 202262306a36Sopenharmony_ci } 202362306a36Sopenharmony_ci lp->dma_regs = devm_ioremap_resource(&pdev->dev, 202462306a36Sopenharmony_ci &dmares); 202562306a36Sopenharmony_ci lp->rx_irq = irq_of_parse_and_map(np, 1); 202662306a36Sopenharmony_ci lp->tx_irq = irq_of_parse_and_map(np, 0); 202762306a36Sopenharmony_ci of_node_put(np); 202862306a36Sopenharmony_ci lp->eth_irq = platform_get_irq_optional(pdev, 0); 202962306a36Sopenharmony_ci } else { 203062306a36Sopenharmony_ci /* Check for these resources directly on the Ethernet node. */ 203162306a36Sopenharmony_ci lp->dma_regs = devm_platform_get_and_ioremap_resource(pdev, 1, NULL); 203262306a36Sopenharmony_ci lp->rx_irq = platform_get_irq(pdev, 1); 203362306a36Sopenharmony_ci lp->tx_irq = platform_get_irq(pdev, 0); 203462306a36Sopenharmony_ci lp->eth_irq = platform_get_irq_optional(pdev, 2); 203562306a36Sopenharmony_ci } 203662306a36Sopenharmony_ci if (IS_ERR(lp->dma_regs)) { 203762306a36Sopenharmony_ci dev_err(&pdev->dev, "could not map DMA regs\n"); 203862306a36Sopenharmony_ci ret = PTR_ERR(lp->dma_regs); 203962306a36Sopenharmony_ci goto cleanup_clk; 204062306a36Sopenharmony_ci } 204162306a36Sopenharmony_ci if ((lp->rx_irq <= 0) || (lp->tx_irq <= 0)) { 204262306a36Sopenharmony_ci dev_err(&pdev->dev, "could not determine irqs\n"); 204362306a36Sopenharmony_ci ret = -ENOMEM; 204462306a36Sopenharmony_ci goto cleanup_clk; 204562306a36Sopenharmony_ci } 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci /* Reset core now that clocks are enabled, prior to accessing MDIO */ 204862306a36Sopenharmony_ci ret = __axienet_device_reset(lp); 204962306a36Sopenharmony_ci if (ret) 205062306a36Sopenharmony_ci goto cleanup_clk; 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci /* Autodetect the need for 64-bit DMA pointers. 205362306a36Sopenharmony_ci * When the IP is configured for a bus width bigger than 32 bits, 205462306a36Sopenharmony_ci * writing the MSB registers is mandatory, even if they are all 0. 205562306a36Sopenharmony_ci * We can detect this case by writing all 1's to one such register 205662306a36Sopenharmony_ci * and see if that sticks: when the IP is configured for 32 bits 205762306a36Sopenharmony_ci * only, those registers are RES0. 205862306a36Sopenharmony_ci * Those MSB registers were introduced in IP v7.1, which we check first. 205962306a36Sopenharmony_ci */ 206062306a36Sopenharmony_ci if ((axienet_ior(lp, XAE_ID_OFFSET) >> 24) >= 0x9) { 206162306a36Sopenharmony_ci void __iomem *desc = lp->dma_regs + XAXIDMA_TX_CDESC_OFFSET + 4; 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_ci iowrite32(0x0, desc); 206462306a36Sopenharmony_ci if (ioread32(desc) == 0) { /* sanity check */ 206562306a36Sopenharmony_ci iowrite32(0xffffffff, desc); 206662306a36Sopenharmony_ci if (ioread32(desc) > 0) { 206762306a36Sopenharmony_ci lp->features |= XAE_FEATURE_DMA_64BIT; 206862306a36Sopenharmony_ci addr_width = 64; 206962306a36Sopenharmony_ci dev_info(&pdev->dev, 207062306a36Sopenharmony_ci "autodetected 64-bit DMA range\n"); 207162306a36Sopenharmony_ci } 207262306a36Sopenharmony_ci iowrite32(0x0, desc); 207362306a36Sopenharmony_ci } 207462306a36Sopenharmony_ci } 207562306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_64BIT) && lp->features & XAE_FEATURE_DMA_64BIT) { 207662306a36Sopenharmony_ci dev_err(&pdev->dev, "64-bit addressable DMA is not compatible with 32-bit archecture\n"); 207762306a36Sopenharmony_ci ret = -EINVAL; 207862306a36Sopenharmony_ci goto cleanup_clk; 207962306a36Sopenharmony_ci } 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(addr_width)); 208262306a36Sopenharmony_ci if (ret) { 208362306a36Sopenharmony_ci dev_err(&pdev->dev, "No suitable DMA available\n"); 208462306a36Sopenharmony_ci goto cleanup_clk; 208562306a36Sopenharmony_ci } 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci /* Check for Ethernet core IRQ (optional) */ 208862306a36Sopenharmony_ci if (lp->eth_irq <= 0) 208962306a36Sopenharmony_ci dev_info(&pdev->dev, "Ethernet core IRQ not defined\n"); 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci /* Retrieve the MAC address */ 209262306a36Sopenharmony_ci ret = of_get_mac_address(pdev->dev.of_node, mac_addr); 209362306a36Sopenharmony_ci if (!ret) { 209462306a36Sopenharmony_ci axienet_set_mac_address(ndev, mac_addr); 209562306a36Sopenharmony_ci } else { 209662306a36Sopenharmony_ci dev_warn(&pdev->dev, "could not find MAC address property: %d\n", 209762306a36Sopenharmony_ci ret); 209862306a36Sopenharmony_ci axienet_set_mac_address(ndev, NULL); 209962306a36Sopenharmony_ci } 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci lp->coalesce_count_rx = XAXIDMA_DFT_RX_THRESHOLD; 210262306a36Sopenharmony_ci lp->coalesce_usec_rx = XAXIDMA_DFT_RX_USEC; 210362306a36Sopenharmony_ci lp->coalesce_count_tx = XAXIDMA_DFT_TX_THRESHOLD; 210462306a36Sopenharmony_ci lp->coalesce_usec_tx = XAXIDMA_DFT_TX_USEC; 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci ret = axienet_mdio_setup(lp); 210762306a36Sopenharmony_ci if (ret) 210862306a36Sopenharmony_ci dev_warn(&pdev->dev, 210962306a36Sopenharmony_ci "error registering MDIO bus: %d\n", ret); 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci if (lp->phy_mode == PHY_INTERFACE_MODE_SGMII || 211262306a36Sopenharmony_ci lp->phy_mode == PHY_INTERFACE_MODE_1000BASEX) { 211362306a36Sopenharmony_ci np = of_parse_phandle(pdev->dev.of_node, "pcs-handle", 0); 211462306a36Sopenharmony_ci if (!np) { 211562306a36Sopenharmony_ci /* Deprecated: Always use "pcs-handle" for pcs_phy. 211662306a36Sopenharmony_ci * Falling back to "phy-handle" here is only for 211762306a36Sopenharmony_ci * backward compatibility with old device trees. 211862306a36Sopenharmony_ci */ 211962306a36Sopenharmony_ci np = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0); 212062306a36Sopenharmony_ci } 212162306a36Sopenharmony_ci if (!np) { 212262306a36Sopenharmony_ci dev_err(&pdev->dev, "pcs-handle (preferred) or phy-handle required for 1000BaseX/SGMII\n"); 212362306a36Sopenharmony_ci ret = -EINVAL; 212462306a36Sopenharmony_ci goto cleanup_mdio; 212562306a36Sopenharmony_ci } 212662306a36Sopenharmony_ci lp->pcs_phy = of_mdio_find_device(np); 212762306a36Sopenharmony_ci if (!lp->pcs_phy) { 212862306a36Sopenharmony_ci ret = -EPROBE_DEFER; 212962306a36Sopenharmony_ci of_node_put(np); 213062306a36Sopenharmony_ci goto cleanup_mdio; 213162306a36Sopenharmony_ci } 213262306a36Sopenharmony_ci of_node_put(np); 213362306a36Sopenharmony_ci lp->pcs.ops = &axienet_pcs_ops; 213462306a36Sopenharmony_ci lp->pcs.neg_mode = true; 213562306a36Sopenharmony_ci lp->pcs.poll = true; 213662306a36Sopenharmony_ci } 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_ci lp->phylink_config.dev = &ndev->dev; 213962306a36Sopenharmony_ci lp->phylink_config.type = PHYLINK_NETDEV; 214062306a36Sopenharmony_ci lp->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE | 214162306a36Sopenharmony_ci MAC_10FD | MAC_100FD | MAC_1000FD; 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci __set_bit(lp->phy_mode, lp->phylink_config.supported_interfaces); 214462306a36Sopenharmony_ci if (lp->switch_x_sgmii) { 214562306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_1000BASEX, 214662306a36Sopenharmony_ci lp->phylink_config.supported_interfaces); 214762306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_SGMII, 214862306a36Sopenharmony_ci lp->phylink_config.supported_interfaces); 214962306a36Sopenharmony_ci } 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_ci lp->phylink = phylink_create(&lp->phylink_config, pdev->dev.fwnode, 215262306a36Sopenharmony_ci lp->phy_mode, 215362306a36Sopenharmony_ci &axienet_phylink_ops); 215462306a36Sopenharmony_ci if (IS_ERR(lp->phylink)) { 215562306a36Sopenharmony_ci ret = PTR_ERR(lp->phylink); 215662306a36Sopenharmony_ci dev_err(&pdev->dev, "phylink_create error (%i)\n", ret); 215762306a36Sopenharmony_ci goto cleanup_mdio; 215862306a36Sopenharmony_ci } 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci ret = register_netdev(lp->ndev); 216162306a36Sopenharmony_ci if (ret) { 216262306a36Sopenharmony_ci dev_err(lp->dev, "register_netdev() error (%i)\n", ret); 216362306a36Sopenharmony_ci goto cleanup_phylink; 216462306a36Sopenharmony_ci } 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci return 0; 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_cicleanup_phylink: 216962306a36Sopenharmony_ci phylink_destroy(lp->phylink); 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_cicleanup_mdio: 217262306a36Sopenharmony_ci if (lp->pcs_phy) 217362306a36Sopenharmony_ci put_device(&lp->pcs_phy->dev); 217462306a36Sopenharmony_ci if (lp->mii_bus) 217562306a36Sopenharmony_ci axienet_mdio_teardown(lp); 217662306a36Sopenharmony_cicleanup_clk: 217762306a36Sopenharmony_ci clk_bulk_disable_unprepare(XAE_NUM_MISC_CLOCKS, lp->misc_clks); 217862306a36Sopenharmony_ci clk_disable_unprepare(lp->axi_clk); 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_cifree_netdev: 218162306a36Sopenharmony_ci free_netdev(ndev); 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci return ret; 218462306a36Sopenharmony_ci} 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_cistatic int axienet_remove(struct platform_device *pdev) 218762306a36Sopenharmony_ci{ 218862306a36Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(pdev); 218962306a36Sopenharmony_ci struct axienet_local *lp = netdev_priv(ndev); 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci unregister_netdev(ndev); 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci if (lp->phylink) 219462306a36Sopenharmony_ci phylink_destroy(lp->phylink); 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ci if (lp->pcs_phy) 219762306a36Sopenharmony_ci put_device(&lp->pcs_phy->dev); 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_ci axienet_mdio_teardown(lp); 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci clk_bulk_disable_unprepare(XAE_NUM_MISC_CLOCKS, lp->misc_clks); 220262306a36Sopenharmony_ci clk_disable_unprepare(lp->axi_clk); 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci free_netdev(ndev); 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_ci return 0; 220762306a36Sopenharmony_ci} 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_cistatic void axienet_shutdown(struct platform_device *pdev) 221062306a36Sopenharmony_ci{ 221162306a36Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(pdev); 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_ci rtnl_lock(); 221462306a36Sopenharmony_ci netif_device_detach(ndev); 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_ci if (netif_running(ndev)) 221762306a36Sopenharmony_ci dev_close(ndev); 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci rtnl_unlock(); 222062306a36Sopenharmony_ci} 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_cistatic int axienet_suspend(struct device *dev) 222362306a36Sopenharmony_ci{ 222462306a36Sopenharmony_ci struct net_device *ndev = dev_get_drvdata(dev); 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci if (!netif_running(ndev)) 222762306a36Sopenharmony_ci return 0; 222862306a36Sopenharmony_ci 222962306a36Sopenharmony_ci netif_device_detach(ndev); 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci rtnl_lock(); 223262306a36Sopenharmony_ci axienet_stop(ndev); 223362306a36Sopenharmony_ci rtnl_unlock(); 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_ci return 0; 223662306a36Sopenharmony_ci} 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_cistatic int axienet_resume(struct device *dev) 223962306a36Sopenharmony_ci{ 224062306a36Sopenharmony_ci struct net_device *ndev = dev_get_drvdata(dev); 224162306a36Sopenharmony_ci 224262306a36Sopenharmony_ci if (!netif_running(ndev)) 224362306a36Sopenharmony_ci return 0; 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_ci rtnl_lock(); 224662306a36Sopenharmony_ci axienet_open(ndev); 224762306a36Sopenharmony_ci rtnl_unlock(); 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci netif_device_attach(ndev); 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci return 0; 225262306a36Sopenharmony_ci} 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(axienet_pm_ops, 225562306a36Sopenharmony_ci axienet_suspend, axienet_resume); 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_cistatic struct platform_driver axienet_driver = { 225862306a36Sopenharmony_ci .probe = axienet_probe, 225962306a36Sopenharmony_ci .remove = axienet_remove, 226062306a36Sopenharmony_ci .shutdown = axienet_shutdown, 226162306a36Sopenharmony_ci .driver = { 226262306a36Sopenharmony_ci .name = "xilinx_axienet", 226362306a36Sopenharmony_ci .pm = &axienet_pm_ops, 226462306a36Sopenharmony_ci .of_match_table = axienet_of_match, 226562306a36Sopenharmony_ci }, 226662306a36Sopenharmony_ci}; 226762306a36Sopenharmony_ci 226862306a36Sopenharmony_cimodule_platform_driver(axienet_driver); 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ciMODULE_DESCRIPTION("Xilinx Axi Ethernet driver"); 227162306a36Sopenharmony_ciMODULE_AUTHOR("Xilinx"); 227262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2273