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, &ethres);
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