18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Faraday FTGMAC100 Gigabit Ethernet
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * (C) Copyright 2009-2011 Faraday Technology
68c2ecf20Sopenharmony_ci * Po-Yu Chuang <ratbert@faraday-tech.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/clk.h>
128c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
138c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
148c2ecf20Sopenharmony_ci#include <linux/ethtool.h>
158c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
168c2ecf20Sopenharmony_ci#include <linux/io.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
198c2ecf20Sopenharmony_ci#include <linux/of.h>
208c2ecf20Sopenharmony_ci#include <linux/of_mdio.h>
218c2ecf20Sopenharmony_ci#include <linux/phy.h>
228c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
238c2ecf20Sopenharmony_ci#include <linux/property.h>
248c2ecf20Sopenharmony_ci#include <linux/crc32.h>
258c2ecf20Sopenharmony_ci#include <linux/if_vlan.h>
268c2ecf20Sopenharmony_ci#include <linux/of_net.h>
278c2ecf20Sopenharmony_ci#include <net/ip.h>
288c2ecf20Sopenharmony_ci#include <net/ncsi.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include "ftgmac100.h"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define DRV_NAME	"ftgmac100"
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* Arbitrary values, I am not sure the HW has limits */
358c2ecf20Sopenharmony_ci#define MAX_RX_QUEUE_ENTRIES	1024
368c2ecf20Sopenharmony_ci#define MAX_TX_QUEUE_ENTRIES	1024
378c2ecf20Sopenharmony_ci#define MIN_RX_QUEUE_ENTRIES	32
388c2ecf20Sopenharmony_ci#define MIN_TX_QUEUE_ENTRIES	32
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/* Defaults */
418c2ecf20Sopenharmony_ci#define DEF_RX_QUEUE_ENTRIES	128
428c2ecf20Sopenharmony_ci#define DEF_TX_QUEUE_ENTRIES	128
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define MAX_PKT_SIZE		1536
458c2ecf20Sopenharmony_ci#define RX_BUF_SIZE		MAX_PKT_SIZE	/* must be smaller than 0x3fff */
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* Min number of tx ring entries before stopping queue */
488c2ecf20Sopenharmony_ci#define TX_THRESHOLD		(MAX_SKB_FRAGS + 1)
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#define FTGMAC_100MHZ		100000000
518c2ecf20Sopenharmony_ci#define FTGMAC_25MHZ		25000000
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistruct ftgmac100 {
548c2ecf20Sopenharmony_ci	/* Registers */
558c2ecf20Sopenharmony_ci	struct resource *res;
568c2ecf20Sopenharmony_ci	void __iomem *base;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	/* Rx ring */
598c2ecf20Sopenharmony_ci	unsigned int rx_q_entries;
608c2ecf20Sopenharmony_ci	struct ftgmac100_rxdes *rxdes;
618c2ecf20Sopenharmony_ci	dma_addr_t rxdes_dma;
628c2ecf20Sopenharmony_ci	struct sk_buff **rx_skbs;
638c2ecf20Sopenharmony_ci	unsigned int rx_pointer;
648c2ecf20Sopenharmony_ci	u32 rxdes0_edorr_mask;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	/* Tx ring */
678c2ecf20Sopenharmony_ci	unsigned int tx_q_entries;
688c2ecf20Sopenharmony_ci	struct ftgmac100_txdes *txdes;
698c2ecf20Sopenharmony_ci	dma_addr_t txdes_dma;
708c2ecf20Sopenharmony_ci	struct sk_buff **tx_skbs;
718c2ecf20Sopenharmony_ci	unsigned int tx_clean_pointer;
728c2ecf20Sopenharmony_ci	unsigned int tx_pointer;
738c2ecf20Sopenharmony_ci	u32 txdes0_edotr_mask;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	/* Used to signal the reset task of ring change request */
768c2ecf20Sopenharmony_ci	unsigned int new_rx_q_entries;
778c2ecf20Sopenharmony_ci	unsigned int new_tx_q_entries;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	/* Scratch page to use when rx skb alloc fails */
808c2ecf20Sopenharmony_ci	void *rx_scratch;
818c2ecf20Sopenharmony_ci	dma_addr_t rx_scratch_dma;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	/* Component structures */
848c2ecf20Sopenharmony_ci	struct net_device *netdev;
858c2ecf20Sopenharmony_ci	struct device *dev;
868c2ecf20Sopenharmony_ci	struct ncsi_dev *ndev;
878c2ecf20Sopenharmony_ci	struct napi_struct napi;
888c2ecf20Sopenharmony_ci	struct work_struct reset_task;
898c2ecf20Sopenharmony_ci	struct mii_bus *mii_bus;
908c2ecf20Sopenharmony_ci	struct clk *clk;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	/* AST2500/AST2600 RMII ref clock gate */
938c2ecf20Sopenharmony_ci	struct clk *rclk;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	/* Link management */
968c2ecf20Sopenharmony_ci	int cur_speed;
978c2ecf20Sopenharmony_ci	int cur_duplex;
988c2ecf20Sopenharmony_ci	bool use_ncsi;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	/* Multicast filter settings */
1018c2ecf20Sopenharmony_ci	u32 maht0;
1028c2ecf20Sopenharmony_ci	u32 maht1;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	/* Flow control settings */
1058c2ecf20Sopenharmony_ci	bool tx_pause;
1068c2ecf20Sopenharmony_ci	bool rx_pause;
1078c2ecf20Sopenharmony_ci	bool aneg_pause;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	/* Misc */
1108c2ecf20Sopenharmony_ci	bool need_mac_restart;
1118c2ecf20Sopenharmony_ci	bool is_aspeed;
1128c2ecf20Sopenharmony_ci};
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic int ftgmac100_reset_mac(struct ftgmac100 *priv, u32 maccr)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	struct net_device *netdev = priv->netdev;
1178c2ecf20Sopenharmony_ci	int i;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	/* NOTE: reset clears all registers */
1208c2ecf20Sopenharmony_ci	iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR);
1218c2ecf20Sopenharmony_ci	iowrite32(maccr | FTGMAC100_MACCR_SW_RST,
1228c2ecf20Sopenharmony_ci		  priv->base + FTGMAC100_OFFSET_MACCR);
1238c2ecf20Sopenharmony_ci	for (i = 0; i < 200; i++) {
1248c2ecf20Sopenharmony_ci		unsigned int maccr;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci		maccr = ioread32(priv->base + FTGMAC100_OFFSET_MACCR);
1278c2ecf20Sopenharmony_ci		if (!(maccr & FTGMAC100_MACCR_SW_RST))
1288c2ecf20Sopenharmony_ci			return 0;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci		udelay(1);
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	netdev_err(netdev, "Hardware reset failed\n");
1348c2ecf20Sopenharmony_ci	return -EIO;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic int ftgmac100_reset_and_config_mac(struct ftgmac100 *priv)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	u32 maccr = 0;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	switch (priv->cur_speed) {
1428c2ecf20Sopenharmony_ci	case SPEED_10:
1438c2ecf20Sopenharmony_ci	case 0: /* no link */
1448c2ecf20Sopenharmony_ci		break;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	case SPEED_100:
1478c2ecf20Sopenharmony_ci		maccr |= FTGMAC100_MACCR_FAST_MODE;
1488c2ecf20Sopenharmony_ci		break;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	case SPEED_1000:
1518c2ecf20Sopenharmony_ci		maccr |= FTGMAC100_MACCR_GIGA_MODE;
1528c2ecf20Sopenharmony_ci		break;
1538c2ecf20Sopenharmony_ci	default:
1548c2ecf20Sopenharmony_ci		netdev_err(priv->netdev, "Unknown speed %d !\n",
1558c2ecf20Sopenharmony_ci			   priv->cur_speed);
1568c2ecf20Sopenharmony_ci		break;
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/* (Re)initialize the queue pointers */
1608c2ecf20Sopenharmony_ci	priv->rx_pointer = 0;
1618c2ecf20Sopenharmony_ci	priv->tx_clean_pointer = 0;
1628c2ecf20Sopenharmony_ci	priv->tx_pointer = 0;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	/* The doc says reset twice with 10us interval */
1658c2ecf20Sopenharmony_ci	if (ftgmac100_reset_mac(priv, maccr))
1668c2ecf20Sopenharmony_ci		return -EIO;
1678c2ecf20Sopenharmony_ci	usleep_range(10, 1000);
1688c2ecf20Sopenharmony_ci	return ftgmac100_reset_mac(priv, maccr);
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic void ftgmac100_write_mac_addr(struct ftgmac100 *priv, const u8 *mac)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	unsigned int maddr = mac[0] << 8 | mac[1];
1748c2ecf20Sopenharmony_ci	unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5];
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	iowrite32(maddr, priv->base + FTGMAC100_OFFSET_MAC_MADR);
1778c2ecf20Sopenharmony_ci	iowrite32(laddr, priv->base + FTGMAC100_OFFSET_MAC_LADR);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic void ftgmac100_initial_mac(struct ftgmac100 *priv)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	u8 mac[ETH_ALEN];
1838c2ecf20Sopenharmony_ci	unsigned int m;
1848c2ecf20Sopenharmony_ci	unsigned int l;
1858c2ecf20Sopenharmony_ci	void *addr;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	addr = device_get_mac_address(priv->dev, mac, ETH_ALEN);
1888c2ecf20Sopenharmony_ci	if (addr) {
1898c2ecf20Sopenharmony_ci		ether_addr_copy(priv->netdev->dev_addr, mac);
1908c2ecf20Sopenharmony_ci		dev_info(priv->dev, "Read MAC address %pM from device tree\n",
1918c2ecf20Sopenharmony_ci			 mac);
1928c2ecf20Sopenharmony_ci		return;
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	m = ioread32(priv->base + FTGMAC100_OFFSET_MAC_MADR);
1968c2ecf20Sopenharmony_ci	l = ioread32(priv->base + FTGMAC100_OFFSET_MAC_LADR);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	mac[0] = (m >> 8) & 0xff;
1998c2ecf20Sopenharmony_ci	mac[1] = m & 0xff;
2008c2ecf20Sopenharmony_ci	mac[2] = (l >> 24) & 0xff;
2018c2ecf20Sopenharmony_ci	mac[3] = (l >> 16) & 0xff;
2028c2ecf20Sopenharmony_ci	mac[4] = (l >> 8) & 0xff;
2038c2ecf20Sopenharmony_ci	mac[5] = l & 0xff;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (is_valid_ether_addr(mac)) {
2068c2ecf20Sopenharmony_ci		ether_addr_copy(priv->netdev->dev_addr, mac);
2078c2ecf20Sopenharmony_ci		dev_info(priv->dev, "Read MAC address %pM from chip\n", mac);
2088c2ecf20Sopenharmony_ci	} else {
2098c2ecf20Sopenharmony_ci		eth_hw_addr_random(priv->netdev);
2108c2ecf20Sopenharmony_ci		dev_info(priv->dev, "Generated random MAC address %pM\n",
2118c2ecf20Sopenharmony_ci			 priv->netdev->dev_addr);
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic int ftgmac100_set_mac_addr(struct net_device *dev, void *p)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	int ret;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	ret = eth_prepare_mac_addr_change(dev, p);
2208c2ecf20Sopenharmony_ci	if (ret < 0)
2218c2ecf20Sopenharmony_ci		return ret;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	eth_commit_mac_addr_change(dev, p);
2248c2ecf20Sopenharmony_ci	ftgmac100_write_mac_addr(netdev_priv(dev), dev->dev_addr);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	return 0;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic void ftgmac100_config_pause(struct ftgmac100 *priv)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	u32 fcr = FTGMAC100_FCR_PAUSE_TIME(16);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/* Throttle tx queue when receiving pause frames */
2348c2ecf20Sopenharmony_ci	if (priv->rx_pause)
2358c2ecf20Sopenharmony_ci		fcr |= FTGMAC100_FCR_FC_EN;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	/* Enables sending pause frames when the RX queue is past a
2388c2ecf20Sopenharmony_ci	 * certain threshold.
2398c2ecf20Sopenharmony_ci	 */
2408c2ecf20Sopenharmony_ci	if (priv->tx_pause)
2418c2ecf20Sopenharmony_ci		fcr |= FTGMAC100_FCR_FCTHR_EN;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	iowrite32(fcr, priv->base + FTGMAC100_OFFSET_FCR);
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic void ftgmac100_init_hw(struct ftgmac100 *priv)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	u32 reg, rfifo_sz, tfifo_sz;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	/* Clear stale interrupts */
2518c2ecf20Sopenharmony_ci	reg = ioread32(priv->base + FTGMAC100_OFFSET_ISR);
2528c2ecf20Sopenharmony_ci	iowrite32(reg, priv->base + FTGMAC100_OFFSET_ISR);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/* Setup RX ring buffer base */
2558c2ecf20Sopenharmony_ci	iowrite32(priv->rxdes_dma, priv->base + FTGMAC100_OFFSET_RXR_BADR);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	/* Setup TX ring buffer base */
2588c2ecf20Sopenharmony_ci	iowrite32(priv->txdes_dma, priv->base + FTGMAC100_OFFSET_NPTXR_BADR);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	/* Configure RX buffer size */
2618c2ecf20Sopenharmony_ci	iowrite32(FTGMAC100_RBSR_SIZE(RX_BUF_SIZE),
2628c2ecf20Sopenharmony_ci		  priv->base + FTGMAC100_OFFSET_RBSR);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	/* Set RX descriptor autopoll */
2658c2ecf20Sopenharmony_ci	iowrite32(FTGMAC100_APTC_RXPOLL_CNT(1),
2668c2ecf20Sopenharmony_ci		  priv->base + FTGMAC100_OFFSET_APTC);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	/* Write MAC address */
2698c2ecf20Sopenharmony_ci	ftgmac100_write_mac_addr(priv, priv->netdev->dev_addr);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	/* Write multicast filter */
2728c2ecf20Sopenharmony_ci	iowrite32(priv->maht0, priv->base + FTGMAC100_OFFSET_MAHT0);
2738c2ecf20Sopenharmony_ci	iowrite32(priv->maht1, priv->base + FTGMAC100_OFFSET_MAHT1);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	/* Configure descriptor sizes and increase burst sizes according
2768c2ecf20Sopenharmony_ci	 * to values in Aspeed SDK. The FIFO arbitration is enabled and
2778c2ecf20Sopenharmony_ci	 * the thresholds set based on the recommended values in the
2788c2ecf20Sopenharmony_ci	 * AST2400 specification.
2798c2ecf20Sopenharmony_ci	 */
2808c2ecf20Sopenharmony_ci	iowrite32(FTGMAC100_DBLAC_RXDES_SIZE(2) |   /* 2*8 bytes RX descs */
2818c2ecf20Sopenharmony_ci		  FTGMAC100_DBLAC_TXDES_SIZE(2) |   /* 2*8 bytes TX descs */
2828c2ecf20Sopenharmony_ci		  FTGMAC100_DBLAC_RXBURST_SIZE(3) | /* 512 bytes max RX bursts */
2838c2ecf20Sopenharmony_ci		  FTGMAC100_DBLAC_TXBURST_SIZE(3) | /* 512 bytes max TX bursts */
2848c2ecf20Sopenharmony_ci		  FTGMAC100_DBLAC_RX_THR_EN |       /* Enable fifo threshold arb */
2858c2ecf20Sopenharmony_ci		  FTGMAC100_DBLAC_RXFIFO_HTHR(6) |  /* 6/8 of FIFO high threshold */
2868c2ecf20Sopenharmony_ci		  FTGMAC100_DBLAC_RXFIFO_LTHR(2),   /* 2/8 of FIFO low threshold */
2878c2ecf20Sopenharmony_ci		  priv->base + FTGMAC100_OFFSET_DBLAC);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	/* Interrupt mitigation configured for 1 interrupt/packet. HW interrupt
2908c2ecf20Sopenharmony_ci	 * mitigation doesn't seem to provide any benefit with NAPI so leave
2918c2ecf20Sopenharmony_ci	 * it at that.
2928c2ecf20Sopenharmony_ci	 */
2938c2ecf20Sopenharmony_ci	iowrite32(FTGMAC100_ITC_RXINT_THR(1) |
2948c2ecf20Sopenharmony_ci		  FTGMAC100_ITC_TXINT_THR(1),
2958c2ecf20Sopenharmony_ci		  priv->base + FTGMAC100_OFFSET_ITC);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	/* Configure FIFO sizes in the TPAFCR register */
2988c2ecf20Sopenharmony_ci	reg = ioread32(priv->base + FTGMAC100_OFFSET_FEAR);
2998c2ecf20Sopenharmony_ci	rfifo_sz = reg & 0x00000007;
3008c2ecf20Sopenharmony_ci	tfifo_sz = (reg >> 3) & 0x00000007;
3018c2ecf20Sopenharmony_ci	reg = ioread32(priv->base + FTGMAC100_OFFSET_TPAFCR);
3028c2ecf20Sopenharmony_ci	reg &= ~0x3f000000;
3038c2ecf20Sopenharmony_ci	reg |= (tfifo_sz << 27);
3048c2ecf20Sopenharmony_ci	reg |= (rfifo_sz << 24);
3058c2ecf20Sopenharmony_ci	iowrite32(reg, priv->base + FTGMAC100_OFFSET_TPAFCR);
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic void ftgmac100_start_hw(struct ftgmac100 *priv)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	u32 maccr = ioread32(priv->base + FTGMAC100_OFFSET_MACCR);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	/* Keep the original GMAC and FAST bits */
3138c2ecf20Sopenharmony_ci	maccr &= (FTGMAC100_MACCR_FAST_MODE | FTGMAC100_MACCR_GIGA_MODE);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	/* Add all the main enable bits */
3168c2ecf20Sopenharmony_ci	maccr |= FTGMAC100_MACCR_TXDMA_EN	|
3178c2ecf20Sopenharmony_ci		 FTGMAC100_MACCR_RXDMA_EN	|
3188c2ecf20Sopenharmony_ci		 FTGMAC100_MACCR_TXMAC_EN	|
3198c2ecf20Sopenharmony_ci		 FTGMAC100_MACCR_RXMAC_EN	|
3208c2ecf20Sopenharmony_ci		 FTGMAC100_MACCR_CRC_APD	|
3218c2ecf20Sopenharmony_ci		 FTGMAC100_MACCR_PHY_LINK_LEVEL	|
3228c2ecf20Sopenharmony_ci		 FTGMAC100_MACCR_RX_RUNT	|
3238c2ecf20Sopenharmony_ci		 FTGMAC100_MACCR_RX_BROADPKT;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	/* Add other bits as needed */
3268c2ecf20Sopenharmony_ci	if (priv->cur_duplex == DUPLEX_FULL)
3278c2ecf20Sopenharmony_ci		maccr |= FTGMAC100_MACCR_FULLDUP;
3288c2ecf20Sopenharmony_ci	if (priv->netdev->flags & IFF_PROMISC)
3298c2ecf20Sopenharmony_ci		maccr |= FTGMAC100_MACCR_RX_ALL;
3308c2ecf20Sopenharmony_ci	if (priv->netdev->flags & IFF_ALLMULTI)
3318c2ecf20Sopenharmony_ci		maccr |= FTGMAC100_MACCR_RX_MULTIPKT;
3328c2ecf20Sopenharmony_ci	else if (netdev_mc_count(priv->netdev))
3338c2ecf20Sopenharmony_ci		maccr |= FTGMAC100_MACCR_HT_MULTI_EN;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/* Vlan filtering enabled */
3368c2ecf20Sopenharmony_ci	if (priv->netdev->features & NETIF_F_HW_VLAN_CTAG_RX)
3378c2ecf20Sopenharmony_ci		maccr |= FTGMAC100_MACCR_RM_VLAN;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	/* Hit the HW */
3408c2ecf20Sopenharmony_ci	iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR);
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic void ftgmac100_stop_hw(struct ftgmac100 *priv)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	iowrite32(0, priv->base + FTGMAC100_OFFSET_MACCR);
3468c2ecf20Sopenharmony_ci}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cistatic void ftgmac100_calc_mc_hash(struct ftgmac100 *priv)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	struct netdev_hw_addr *ha;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	priv->maht1 = 0;
3538c2ecf20Sopenharmony_ci	priv->maht0 = 0;
3548c2ecf20Sopenharmony_ci	netdev_for_each_mc_addr(ha, priv->netdev) {
3558c2ecf20Sopenharmony_ci		u32 crc_val = ether_crc_le(ETH_ALEN, ha->addr);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci		crc_val = (~(crc_val >> 2)) & 0x3f;
3588c2ecf20Sopenharmony_ci		if (crc_val >= 32)
3598c2ecf20Sopenharmony_ci			priv->maht1 |= 1ul << (crc_val - 32);
3608c2ecf20Sopenharmony_ci		else
3618c2ecf20Sopenharmony_ci			priv->maht0 |= 1ul << (crc_val);
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic void ftgmac100_set_rx_mode(struct net_device *netdev)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	struct ftgmac100 *priv = netdev_priv(netdev);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	/* Setup the hash filter */
3708c2ecf20Sopenharmony_ci	ftgmac100_calc_mc_hash(priv);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	/* Interface down ? that's all there is to do */
3738c2ecf20Sopenharmony_ci	if (!netif_running(netdev))
3748c2ecf20Sopenharmony_ci		return;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	/* Update the HW */
3778c2ecf20Sopenharmony_ci	iowrite32(priv->maht0, priv->base + FTGMAC100_OFFSET_MAHT0);
3788c2ecf20Sopenharmony_ci	iowrite32(priv->maht1, priv->base + FTGMAC100_OFFSET_MAHT1);
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	/* Reconfigure MACCR */
3818c2ecf20Sopenharmony_ci	ftgmac100_start_hw(priv);
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic int ftgmac100_alloc_rx_buf(struct ftgmac100 *priv, unsigned int entry,
3858c2ecf20Sopenharmony_ci				  struct ftgmac100_rxdes *rxdes, gfp_t gfp)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	struct net_device *netdev = priv->netdev;
3888c2ecf20Sopenharmony_ci	struct sk_buff *skb;
3898c2ecf20Sopenharmony_ci	dma_addr_t map;
3908c2ecf20Sopenharmony_ci	int err = 0;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	skb = netdev_alloc_skb_ip_align(netdev, RX_BUF_SIZE);
3938c2ecf20Sopenharmony_ci	if (unlikely(!skb)) {
3948c2ecf20Sopenharmony_ci		if (net_ratelimit())
3958c2ecf20Sopenharmony_ci			netdev_warn(netdev, "failed to allocate rx skb\n");
3968c2ecf20Sopenharmony_ci		err = -ENOMEM;
3978c2ecf20Sopenharmony_ci		map = priv->rx_scratch_dma;
3988c2ecf20Sopenharmony_ci	} else {
3998c2ecf20Sopenharmony_ci		map = dma_map_single(priv->dev, skb->data, RX_BUF_SIZE,
4008c2ecf20Sopenharmony_ci				     DMA_FROM_DEVICE);
4018c2ecf20Sopenharmony_ci		if (unlikely(dma_mapping_error(priv->dev, map))) {
4028c2ecf20Sopenharmony_ci			if (net_ratelimit())
4038c2ecf20Sopenharmony_ci				netdev_err(netdev, "failed to map rx page\n");
4048c2ecf20Sopenharmony_ci			dev_kfree_skb_any(skb);
4058c2ecf20Sopenharmony_ci			map = priv->rx_scratch_dma;
4068c2ecf20Sopenharmony_ci			skb = NULL;
4078c2ecf20Sopenharmony_ci			err = -ENOMEM;
4088c2ecf20Sopenharmony_ci		}
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	/* Store skb */
4128c2ecf20Sopenharmony_ci	priv->rx_skbs[entry] = skb;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	/* Store DMA address into RX desc */
4158c2ecf20Sopenharmony_ci	rxdes->rxdes3 = cpu_to_le32(map);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	/* Ensure the above is ordered vs clearing the OWN bit */
4188c2ecf20Sopenharmony_ci	dma_wmb();
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	/* Clean status (which resets own bit) */
4218c2ecf20Sopenharmony_ci	if (entry == (priv->rx_q_entries - 1))
4228c2ecf20Sopenharmony_ci		rxdes->rxdes0 = cpu_to_le32(priv->rxdes0_edorr_mask);
4238c2ecf20Sopenharmony_ci	else
4248c2ecf20Sopenharmony_ci		rxdes->rxdes0 = 0;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	return err;
4278c2ecf20Sopenharmony_ci}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic unsigned int ftgmac100_next_rx_pointer(struct ftgmac100 *priv,
4308c2ecf20Sopenharmony_ci					      unsigned int pointer)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	return (pointer + 1) & (priv->rx_q_entries - 1);
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic void ftgmac100_rx_packet_error(struct ftgmac100 *priv, u32 status)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	struct net_device *netdev = priv->netdev;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	if (status & FTGMAC100_RXDES0_RX_ERR)
4408c2ecf20Sopenharmony_ci		netdev->stats.rx_errors++;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	if (status & FTGMAC100_RXDES0_CRC_ERR)
4438c2ecf20Sopenharmony_ci		netdev->stats.rx_crc_errors++;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	if (status & (FTGMAC100_RXDES0_FTL |
4468c2ecf20Sopenharmony_ci		      FTGMAC100_RXDES0_RUNT |
4478c2ecf20Sopenharmony_ci		      FTGMAC100_RXDES0_RX_ODD_NB))
4488c2ecf20Sopenharmony_ci		netdev->stats.rx_length_errors++;
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	struct net_device *netdev = priv->netdev;
4548c2ecf20Sopenharmony_ci	struct ftgmac100_rxdes *rxdes;
4558c2ecf20Sopenharmony_ci	struct sk_buff *skb;
4568c2ecf20Sopenharmony_ci	unsigned int pointer, size;
4578c2ecf20Sopenharmony_ci	u32 status, csum_vlan;
4588c2ecf20Sopenharmony_ci	dma_addr_t map;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	/* Grab next RX descriptor */
4618c2ecf20Sopenharmony_ci	pointer = priv->rx_pointer;
4628c2ecf20Sopenharmony_ci	rxdes = &priv->rxdes[pointer];
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	/* Grab descriptor status */
4658c2ecf20Sopenharmony_ci	status = le32_to_cpu(rxdes->rxdes0);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	/* Do we have a packet ? */
4688c2ecf20Sopenharmony_ci	if (!(status & FTGMAC100_RXDES0_RXPKT_RDY))
4698c2ecf20Sopenharmony_ci		return false;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	/* Order subsequent reads with the test for the ready bit */
4728c2ecf20Sopenharmony_ci	dma_rmb();
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	/* We don't cope with fragmented RX packets */
4758c2ecf20Sopenharmony_ci	if (unlikely(!(status & FTGMAC100_RXDES0_FRS) ||
4768c2ecf20Sopenharmony_ci		     !(status & FTGMAC100_RXDES0_LRS)))
4778c2ecf20Sopenharmony_ci		goto drop;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	/* Grab received size and csum vlan field in the descriptor */
4808c2ecf20Sopenharmony_ci	size = status & FTGMAC100_RXDES0_VDBC;
4818c2ecf20Sopenharmony_ci	csum_vlan = le32_to_cpu(rxdes->rxdes1);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	/* Any error (other than csum offload) flagged ? */
4848c2ecf20Sopenharmony_ci	if (unlikely(status & RXDES0_ANY_ERROR)) {
4858c2ecf20Sopenharmony_ci		/* Correct for incorrect flagging of runt packets
4868c2ecf20Sopenharmony_ci		 * with vlan tags... Just accept a runt packet that
4878c2ecf20Sopenharmony_ci		 * has been flagged as vlan and whose size is at
4888c2ecf20Sopenharmony_ci		 * least 60 bytes.
4898c2ecf20Sopenharmony_ci		 */
4908c2ecf20Sopenharmony_ci		if ((status & FTGMAC100_RXDES0_RUNT) &&
4918c2ecf20Sopenharmony_ci		    (csum_vlan & FTGMAC100_RXDES1_VLANTAG_AVAIL) &&
4928c2ecf20Sopenharmony_ci		    (size >= 60))
4938c2ecf20Sopenharmony_ci			status &= ~FTGMAC100_RXDES0_RUNT;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci		/* Any error still in there ? */
4968c2ecf20Sopenharmony_ci		if (status & RXDES0_ANY_ERROR) {
4978c2ecf20Sopenharmony_ci			ftgmac100_rx_packet_error(priv, status);
4988c2ecf20Sopenharmony_ci			goto drop;
4998c2ecf20Sopenharmony_ci		}
5008c2ecf20Sopenharmony_ci	}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	/* If the packet had no skb (failed to allocate earlier)
5038c2ecf20Sopenharmony_ci	 * then try to allocate one and skip
5048c2ecf20Sopenharmony_ci	 */
5058c2ecf20Sopenharmony_ci	skb = priv->rx_skbs[pointer];
5068c2ecf20Sopenharmony_ci	if (!unlikely(skb)) {
5078c2ecf20Sopenharmony_ci		ftgmac100_alloc_rx_buf(priv, pointer, rxdes, GFP_ATOMIC);
5088c2ecf20Sopenharmony_ci		goto drop;
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	if (unlikely(status & FTGMAC100_RXDES0_MULTICAST))
5128c2ecf20Sopenharmony_ci		netdev->stats.multicast++;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	/* If the HW found checksum errors, bounce it to software.
5158c2ecf20Sopenharmony_ci	 *
5168c2ecf20Sopenharmony_ci	 * If we didn't, we need to see if the packet was recognized
5178c2ecf20Sopenharmony_ci	 * by HW as one of the supported checksummed protocols before
5188c2ecf20Sopenharmony_ci	 * we accept the HW test results.
5198c2ecf20Sopenharmony_ci	 */
5208c2ecf20Sopenharmony_ci	if (netdev->features & NETIF_F_RXCSUM) {
5218c2ecf20Sopenharmony_ci		u32 err_bits = FTGMAC100_RXDES1_TCP_CHKSUM_ERR |
5228c2ecf20Sopenharmony_ci			FTGMAC100_RXDES1_UDP_CHKSUM_ERR |
5238c2ecf20Sopenharmony_ci			FTGMAC100_RXDES1_IP_CHKSUM_ERR;
5248c2ecf20Sopenharmony_ci		if ((csum_vlan & err_bits) ||
5258c2ecf20Sopenharmony_ci		    !(csum_vlan & FTGMAC100_RXDES1_PROT_MASK))
5268c2ecf20Sopenharmony_ci			skb->ip_summed = CHECKSUM_NONE;
5278c2ecf20Sopenharmony_ci		else
5288c2ecf20Sopenharmony_ci			skb->ip_summed = CHECKSUM_UNNECESSARY;
5298c2ecf20Sopenharmony_ci	}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	/* Transfer received size to skb */
5328c2ecf20Sopenharmony_ci	skb_put(skb, size);
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	/* Extract vlan tag */
5358c2ecf20Sopenharmony_ci	if ((netdev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
5368c2ecf20Sopenharmony_ci	    (csum_vlan & FTGMAC100_RXDES1_VLANTAG_AVAIL))
5378c2ecf20Sopenharmony_ci		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
5388c2ecf20Sopenharmony_ci				       csum_vlan & 0xffff);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	/* Tear down DMA mapping, do necessary cache management */
5418c2ecf20Sopenharmony_ci	map = le32_to_cpu(rxdes->rxdes3);
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci#if defined(CONFIG_ARM) && !defined(CONFIG_ARM_DMA_USE_IOMMU)
5448c2ecf20Sopenharmony_ci	/* When we don't have an iommu, we can save cycles by not
5458c2ecf20Sopenharmony_ci	 * invalidating the cache for the part of the packet that
5468c2ecf20Sopenharmony_ci	 * wasn't received.
5478c2ecf20Sopenharmony_ci	 */
5488c2ecf20Sopenharmony_ci	dma_unmap_single(priv->dev, map, size, DMA_FROM_DEVICE);
5498c2ecf20Sopenharmony_ci#else
5508c2ecf20Sopenharmony_ci	dma_unmap_single(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE);
5518c2ecf20Sopenharmony_ci#endif
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	/* Resplenish rx ring */
5558c2ecf20Sopenharmony_ci	ftgmac100_alloc_rx_buf(priv, pointer, rxdes, GFP_ATOMIC);
5568c2ecf20Sopenharmony_ci	priv->rx_pointer = ftgmac100_next_rx_pointer(priv, pointer);
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	skb->protocol = eth_type_trans(skb, netdev);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	netdev->stats.rx_packets++;
5618c2ecf20Sopenharmony_ci	netdev->stats.rx_bytes += size;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	/* push packet to protocol stack */
5648c2ecf20Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_NONE)
5658c2ecf20Sopenharmony_ci		netif_receive_skb(skb);
5668c2ecf20Sopenharmony_ci	else
5678c2ecf20Sopenharmony_ci		napi_gro_receive(&priv->napi, skb);
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	(*processed)++;
5708c2ecf20Sopenharmony_ci	return true;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci drop:
5738c2ecf20Sopenharmony_ci	/* Clean rxdes0 (which resets own bit) */
5748c2ecf20Sopenharmony_ci	rxdes->rxdes0 = cpu_to_le32(status & priv->rxdes0_edorr_mask);
5758c2ecf20Sopenharmony_ci	priv->rx_pointer = ftgmac100_next_rx_pointer(priv, pointer);
5768c2ecf20Sopenharmony_ci	netdev->stats.rx_dropped++;
5778c2ecf20Sopenharmony_ci	return true;
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_cistatic u32 ftgmac100_base_tx_ctlstat(struct ftgmac100 *priv,
5818c2ecf20Sopenharmony_ci				     unsigned int index)
5828c2ecf20Sopenharmony_ci{
5838c2ecf20Sopenharmony_ci	if (index == (priv->tx_q_entries - 1))
5848c2ecf20Sopenharmony_ci		return priv->txdes0_edotr_mask;
5858c2ecf20Sopenharmony_ci	else
5868c2ecf20Sopenharmony_ci		return 0;
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_cistatic unsigned int ftgmac100_next_tx_pointer(struct ftgmac100 *priv,
5908c2ecf20Sopenharmony_ci					      unsigned int pointer)
5918c2ecf20Sopenharmony_ci{
5928c2ecf20Sopenharmony_ci	return (pointer + 1) & (priv->tx_q_entries - 1);
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_cistatic u32 ftgmac100_tx_buf_avail(struct ftgmac100 *priv)
5968c2ecf20Sopenharmony_ci{
5978c2ecf20Sopenharmony_ci	/* Returns the number of available slots in the TX queue
5988c2ecf20Sopenharmony_ci	 *
5998c2ecf20Sopenharmony_ci	 * This always leaves one free slot so we don't have to
6008c2ecf20Sopenharmony_ci	 * worry about empty vs. full, and this simplifies the
6018c2ecf20Sopenharmony_ci	 * test for ftgmac100_tx_buf_cleanable() below
6028c2ecf20Sopenharmony_ci	 */
6038c2ecf20Sopenharmony_ci	return (priv->tx_clean_pointer - priv->tx_pointer - 1) &
6048c2ecf20Sopenharmony_ci		(priv->tx_q_entries - 1);
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cistatic bool ftgmac100_tx_buf_cleanable(struct ftgmac100 *priv)
6088c2ecf20Sopenharmony_ci{
6098c2ecf20Sopenharmony_ci	return priv->tx_pointer != priv->tx_clean_pointer;
6108c2ecf20Sopenharmony_ci}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_cistatic void ftgmac100_free_tx_packet(struct ftgmac100 *priv,
6138c2ecf20Sopenharmony_ci				     unsigned int pointer,
6148c2ecf20Sopenharmony_ci				     struct sk_buff *skb,
6158c2ecf20Sopenharmony_ci				     struct ftgmac100_txdes *txdes,
6168c2ecf20Sopenharmony_ci				     u32 ctl_stat)
6178c2ecf20Sopenharmony_ci{
6188c2ecf20Sopenharmony_ci	dma_addr_t map = le32_to_cpu(txdes->txdes3);
6198c2ecf20Sopenharmony_ci	size_t len;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	if (ctl_stat & FTGMAC100_TXDES0_FTS) {
6228c2ecf20Sopenharmony_ci		len = skb_headlen(skb);
6238c2ecf20Sopenharmony_ci		dma_unmap_single(priv->dev, map, len, DMA_TO_DEVICE);
6248c2ecf20Sopenharmony_ci	} else {
6258c2ecf20Sopenharmony_ci		len = FTGMAC100_TXDES0_TXBUF_SIZE(ctl_stat);
6268c2ecf20Sopenharmony_ci		dma_unmap_page(priv->dev, map, len, DMA_TO_DEVICE);
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	/* Free SKB on last segment */
6308c2ecf20Sopenharmony_ci	if (ctl_stat & FTGMAC100_TXDES0_LTS)
6318c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
6328c2ecf20Sopenharmony_ci	priv->tx_skbs[pointer] = NULL;
6338c2ecf20Sopenharmony_ci}
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_cistatic bool ftgmac100_tx_complete_packet(struct ftgmac100 *priv)
6368c2ecf20Sopenharmony_ci{
6378c2ecf20Sopenharmony_ci	struct net_device *netdev = priv->netdev;
6388c2ecf20Sopenharmony_ci	struct ftgmac100_txdes *txdes;
6398c2ecf20Sopenharmony_ci	struct sk_buff *skb;
6408c2ecf20Sopenharmony_ci	unsigned int pointer;
6418c2ecf20Sopenharmony_ci	u32 ctl_stat;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	pointer = priv->tx_clean_pointer;
6448c2ecf20Sopenharmony_ci	txdes = &priv->txdes[pointer];
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	ctl_stat = le32_to_cpu(txdes->txdes0);
6478c2ecf20Sopenharmony_ci	if (ctl_stat & FTGMAC100_TXDES0_TXDMA_OWN)
6488c2ecf20Sopenharmony_ci		return false;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	skb = priv->tx_skbs[pointer];
6518c2ecf20Sopenharmony_ci	netdev->stats.tx_packets++;
6528c2ecf20Sopenharmony_ci	netdev->stats.tx_bytes += skb->len;
6538c2ecf20Sopenharmony_ci	ftgmac100_free_tx_packet(priv, pointer, skb, txdes, ctl_stat);
6548c2ecf20Sopenharmony_ci	txdes->txdes0 = cpu_to_le32(ctl_stat & priv->txdes0_edotr_mask);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	priv->tx_clean_pointer = ftgmac100_next_tx_pointer(priv, pointer);
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	return true;
6598c2ecf20Sopenharmony_ci}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_cistatic void ftgmac100_tx_complete(struct ftgmac100 *priv)
6628c2ecf20Sopenharmony_ci{
6638c2ecf20Sopenharmony_ci	struct net_device *netdev = priv->netdev;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	/* Process all completed packets */
6668c2ecf20Sopenharmony_ci	while (ftgmac100_tx_buf_cleanable(priv) &&
6678c2ecf20Sopenharmony_ci	       ftgmac100_tx_complete_packet(priv))
6688c2ecf20Sopenharmony_ci		;
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	/* Restart queue if needed */
6718c2ecf20Sopenharmony_ci	smp_mb();
6728c2ecf20Sopenharmony_ci	if (unlikely(netif_queue_stopped(netdev) &&
6738c2ecf20Sopenharmony_ci		     ftgmac100_tx_buf_avail(priv) >= TX_THRESHOLD)) {
6748c2ecf20Sopenharmony_ci		struct netdev_queue *txq;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci		txq = netdev_get_tx_queue(netdev, 0);
6778c2ecf20Sopenharmony_ci		__netif_tx_lock(txq, smp_processor_id());
6788c2ecf20Sopenharmony_ci		if (netif_queue_stopped(netdev) &&
6798c2ecf20Sopenharmony_ci		    ftgmac100_tx_buf_avail(priv) >= TX_THRESHOLD)
6808c2ecf20Sopenharmony_ci			netif_wake_queue(netdev);
6818c2ecf20Sopenharmony_ci		__netif_tx_unlock(txq);
6828c2ecf20Sopenharmony_ci	}
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_cistatic bool ftgmac100_prep_tx_csum(struct sk_buff *skb, u32 *csum_vlan)
6868c2ecf20Sopenharmony_ci{
6878c2ecf20Sopenharmony_ci	if (skb->protocol == cpu_to_be16(ETH_P_IP)) {
6888c2ecf20Sopenharmony_ci		u8 ip_proto = ip_hdr(skb)->protocol;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci		*csum_vlan |= FTGMAC100_TXDES1_IP_CHKSUM;
6918c2ecf20Sopenharmony_ci		switch(ip_proto) {
6928c2ecf20Sopenharmony_ci		case IPPROTO_TCP:
6938c2ecf20Sopenharmony_ci			*csum_vlan |= FTGMAC100_TXDES1_TCP_CHKSUM;
6948c2ecf20Sopenharmony_ci			return true;
6958c2ecf20Sopenharmony_ci		case IPPROTO_UDP:
6968c2ecf20Sopenharmony_ci			*csum_vlan |= FTGMAC100_TXDES1_UDP_CHKSUM;
6978c2ecf20Sopenharmony_ci			return true;
6988c2ecf20Sopenharmony_ci		case IPPROTO_IP:
6998c2ecf20Sopenharmony_ci			return true;
7008c2ecf20Sopenharmony_ci		}
7018c2ecf20Sopenharmony_ci	}
7028c2ecf20Sopenharmony_ci	return skb_checksum_help(skb) == 0;
7038c2ecf20Sopenharmony_ci}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_cistatic netdev_tx_t ftgmac100_hard_start_xmit(struct sk_buff *skb,
7068c2ecf20Sopenharmony_ci					     struct net_device *netdev)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	struct ftgmac100 *priv = netdev_priv(netdev);
7098c2ecf20Sopenharmony_ci	struct ftgmac100_txdes *txdes, *first;
7108c2ecf20Sopenharmony_ci	unsigned int pointer, nfrags, len, i, j;
7118c2ecf20Sopenharmony_ci	u32 f_ctl_stat, ctl_stat, csum_vlan;
7128c2ecf20Sopenharmony_ci	dma_addr_t map;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	/* The HW doesn't pad small frames */
7158c2ecf20Sopenharmony_ci	if (eth_skb_pad(skb)) {
7168c2ecf20Sopenharmony_ci		netdev->stats.tx_dropped++;
7178c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
7188c2ecf20Sopenharmony_ci	}
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	/* Reject oversize packets */
7218c2ecf20Sopenharmony_ci	if (unlikely(skb->len > MAX_PKT_SIZE)) {
7228c2ecf20Sopenharmony_ci		if (net_ratelimit())
7238c2ecf20Sopenharmony_ci			netdev_dbg(netdev, "tx packet too big\n");
7248c2ecf20Sopenharmony_ci		goto drop;
7258c2ecf20Sopenharmony_ci	}
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	/* Do we have a limit on #fragments ? I yet have to get a reply
7288c2ecf20Sopenharmony_ci	 * from Aspeed. If there's one I haven't hit it.
7298c2ecf20Sopenharmony_ci	 */
7308c2ecf20Sopenharmony_ci	nfrags = skb_shinfo(skb)->nr_frags;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	/* Setup HW checksumming */
7338c2ecf20Sopenharmony_ci	csum_vlan = 0;
7348c2ecf20Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_PARTIAL &&
7358c2ecf20Sopenharmony_ci	    !ftgmac100_prep_tx_csum(skb, &csum_vlan))
7368c2ecf20Sopenharmony_ci		goto drop;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	/* Add VLAN tag */
7398c2ecf20Sopenharmony_ci	if (skb_vlan_tag_present(skb)) {
7408c2ecf20Sopenharmony_ci		csum_vlan |= FTGMAC100_TXDES1_INS_VLANTAG;
7418c2ecf20Sopenharmony_ci		csum_vlan |= skb_vlan_tag_get(skb) & 0xffff;
7428c2ecf20Sopenharmony_ci	}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	/* Get header len */
7458c2ecf20Sopenharmony_ci	len = skb_headlen(skb);
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	/* Map the packet head */
7488c2ecf20Sopenharmony_ci	map = dma_map_single(priv->dev, skb->data, len, DMA_TO_DEVICE);
7498c2ecf20Sopenharmony_ci	if (dma_mapping_error(priv->dev, map)) {
7508c2ecf20Sopenharmony_ci		if (net_ratelimit())
7518c2ecf20Sopenharmony_ci			netdev_err(netdev, "map tx packet head failed\n");
7528c2ecf20Sopenharmony_ci		goto drop;
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	/* Grab the next free tx descriptor */
7568c2ecf20Sopenharmony_ci	pointer = priv->tx_pointer;
7578c2ecf20Sopenharmony_ci	txdes = first = &priv->txdes[pointer];
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	/* Setup it up with the packet head. Don't write the head to the
7608c2ecf20Sopenharmony_ci	 * ring just yet
7618c2ecf20Sopenharmony_ci	 */
7628c2ecf20Sopenharmony_ci	priv->tx_skbs[pointer] = skb;
7638c2ecf20Sopenharmony_ci	f_ctl_stat = ftgmac100_base_tx_ctlstat(priv, pointer);
7648c2ecf20Sopenharmony_ci	f_ctl_stat |= FTGMAC100_TXDES0_TXDMA_OWN;
7658c2ecf20Sopenharmony_ci	f_ctl_stat |= FTGMAC100_TXDES0_TXBUF_SIZE(len);
7668c2ecf20Sopenharmony_ci	f_ctl_stat |= FTGMAC100_TXDES0_FTS;
7678c2ecf20Sopenharmony_ci	if (nfrags == 0)
7688c2ecf20Sopenharmony_ci		f_ctl_stat |= FTGMAC100_TXDES0_LTS;
7698c2ecf20Sopenharmony_ci	txdes->txdes3 = cpu_to_le32(map);
7708c2ecf20Sopenharmony_ci	txdes->txdes1 = cpu_to_le32(csum_vlan);
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	/* Next descriptor */
7738c2ecf20Sopenharmony_ci	pointer = ftgmac100_next_tx_pointer(priv, pointer);
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	/* Add the fragments */
7768c2ecf20Sopenharmony_ci	for (i = 0; i < nfrags; i++) {
7778c2ecf20Sopenharmony_ci		skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci		len = skb_frag_size(frag);
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci		/* Map it */
7828c2ecf20Sopenharmony_ci		map = skb_frag_dma_map(priv->dev, frag, 0, len,
7838c2ecf20Sopenharmony_ci				       DMA_TO_DEVICE);
7848c2ecf20Sopenharmony_ci		if (dma_mapping_error(priv->dev, map))
7858c2ecf20Sopenharmony_ci			goto dma_err;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci		/* Setup descriptor */
7888c2ecf20Sopenharmony_ci		priv->tx_skbs[pointer] = skb;
7898c2ecf20Sopenharmony_ci		txdes = &priv->txdes[pointer];
7908c2ecf20Sopenharmony_ci		ctl_stat = ftgmac100_base_tx_ctlstat(priv, pointer);
7918c2ecf20Sopenharmony_ci		ctl_stat |= FTGMAC100_TXDES0_TXDMA_OWN;
7928c2ecf20Sopenharmony_ci		ctl_stat |= FTGMAC100_TXDES0_TXBUF_SIZE(len);
7938c2ecf20Sopenharmony_ci		if (i == (nfrags - 1))
7948c2ecf20Sopenharmony_ci			ctl_stat |= FTGMAC100_TXDES0_LTS;
7958c2ecf20Sopenharmony_ci		txdes->txdes0 = cpu_to_le32(ctl_stat);
7968c2ecf20Sopenharmony_ci		txdes->txdes1 = 0;
7978c2ecf20Sopenharmony_ci		txdes->txdes3 = cpu_to_le32(map);
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci		/* Next one */
8008c2ecf20Sopenharmony_ci		pointer = ftgmac100_next_tx_pointer(priv, pointer);
8018c2ecf20Sopenharmony_ci	}
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	/* Order the previous packet and descriptor udpates
8048c2ecf20Sopenharmony_ci	 * before setting the OWN bit on the first descriptor.
8058c2ecf20Sopenharmony_ci	 */
8068c2ecf20Sopenharmony_ci	dma_wmb();
8078c2ecf20Sopenharmony_ci	first->txdes0 = cpu_to_le32(f_ctl_stat);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	/* Update next TX pointer */
8108c2ecf20Sopenharmony_ci	priv->tx_pointer = pointer;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	/* If there isn't enough room for all the fragments of a new packet
8138c2ecf20Sopenharmony_ci	 * in the TX ring, stop the queue. The sequence below is race free
8148c2ecf20Sopenharmony_ci	 * vs. a concurrent restart in ftgmac100_poll()
8158c2ecf20Sopenharmony_ci	 */
8168c2ecf20Sopenharmony_ci	if (unlikely(ftgmac100_tx_buf_avail(priv) < TX_THRESHOLD)) {
8178c2ecf20Sopenharmony_ci		netif_stop_queue(netdev);
8188c2ecf20Sopenharmony_ci		/* Order the queue stop with the test below */
8198c2ecf20Sopenharmony_ci		smp_mb();
8208c2ecf20Sopenharmony_ci		if (ftgmac100_tx_buf_avail(priv) >= TX_THRESHOLD)
8218c2ecf20Sopenharmony_ci			netif_wake_queue(netdev);
8228c2ecf20Sopenharmony_ci	}
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	/* Poke transmitter to read the updated TX descriptors */
8258c2ecf20Sopenharmony_ci	iowrite32(1, priv->base + FTGMAC100_OFFSET_NPTXPD);
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci dma_err:
8308c2ecf20Sopenharmony_ci	if (net_ratelimit())
8318c2ecf20Sopenharmony_ci		netdev_err(netdev, "map tx fragment failed\n");
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	/* Free head */
8348c2ecf20Sopenharmony_ci	pointer = priv->tx_pointer;
8358c2ecf20Sopenharmony_ci	ftgmac100_free_tx_packet(priv, pointer, skb, first, f_ctl_stat);
8368c2ecf20Sopenharmony_ci	first->txdes0 = cpu_to_le32(f_ctl_stat & priv->txdes0_edotr_mask);
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	/* Then all fragments */
8398c2ecf20Sopenharmony_ci	for (j = 0; j < i; j++) {
8408c2ecf20Sopenharmony_ci		pointer = ftgmac100_next_tx_pointer(priv, pointer);
8418c2ecf20Sopenharmony_ci		txdes = &priv->txdes[pointer];
8428c2ecf20Sopenharmony_ci		ctl_stat = le32_to_cpu(txdes->txdes0);
8438c2ecf20Sopenharmony_ci		ftgmac100_free_tx_packet(priv, pointer, skb, txdes, ctl_stat);
8448c2ecf20Sopenharmony_ci		txdes->txdes0 = cpu_to_le32(ctl_stat & priv->txdes0_edotr_mask);
8458c2ecf20Sopenharmony_ci	}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	/* This cannot be reached if we successfully mapped the
8488c2ecf20Sopenharmony_ci	 * last fragment, so we know ftgmac100_free_tx_packet()
8498c2ecf20Sopenharmony_ci	 * hasn't freed the skb yet.
8508c2ecf20Sopenharmony_ci	 */
8518c2ecf20Sopenharmony_ci drop:
8528c2ecf20Sopenharmony_ci	/* Drop the packet */
8538c2ecf20Sopenharmony_ci	dev_kfree_skb_any(skb);
8548c2ecf20Sopenharmony_ci	netdev->stats.tx_dropped++;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
8578c2ecf20Sopenharmony_ci}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_cistatic void ftgmac100_free_buffers(struct ftgmac100 *priv)
8608c2ecf20Sopenharmony_ci{
8618c2ecf20Sopenharmony_ci	int i;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	/* Free all RX buffers */
8648c2ecf20Sopenharmony_ci	for (i = 0; i < priv->rx_q_entries; i++) {
8658c2ecf20Sopenharmony_ci		struct ftgmac100_rxdes *rxdes = &priv->rxdes[i];
8668c2ecf20Sopenharmony_ci		struct sk_buff *skb = priv->rx_skbs[i];
8678c2ecf20Sopenharmony_ci		dma_addr_t map = le32_to_cpu(rxdes->rxdes3);
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci		if (!skb)
8708c2ecf20Sopenharmony_ci			continue;
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci		priv->rx_skbs[i] = NULL;
8738c2ecf20Sopenharmony_ci		dma_unmap_single(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE);
8748c2ecf20Sopenharmony_ci		dev_kfree_skb_any(skb);
8758c2ecf20Sopenharmony_ci	}
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	/* Free all TX buffers */
8788c2ecf20Sopenharmony_ci	for (i = 0; i < priv->tx_q_entries; i++) {
8798c2ecf20Sopenharmony_ci		struct ftgmac100_txdes *txdes = &priv->txdes[i];
8808c2ecf20Sopenharmony_ci		struct sk_buff *skb = priv->tx_skbs[i];
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci		if (!skb)
8838c2ecf20Sopenharmony_ci			continue;
8848c2ecf20Sopenharmony_ci		ftgmac100_free_tx_packet(priv, i, skb, txdes,
8858c2ecf20Sopenharmony_ci					 le32_to_cpu(txdes->txdes0));
8868c2ecf20Sopenharmony_ci	}
8878c2ecf20Sopenharmony_ci}
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_cistatic void ftgmac100_free_rings(struct ftgmac100 *priv)
8908c2ecf20Sopenharmony_ci{
8918c2ecf20Sopenharmony_ci	/* Free skb arrays */
8928c2ecf20Sopenharmony_ci	kfree(priv->rx_skbs);
8938c2ecf20Sopenharmony_ci	kfree(priv->tx_skbs);
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	/* Free descriptors */
8968c2ecf20Sopenharmony_ci	if (priv->rxdes)
8978c2ecf20Sopenharmony_ci		dma_free_coherent(priv->dev, MAX_RX_QUEUE_ENTRIES *
8988c2ecf20Sopenharmony_ci				  sizeof(struct ftgmac100_rxdes),
8998c2ecf20Sopenharmony_ci				  priv->rxdes, priv->rxdes_dma);
9008c2ecf20Sopenharmony_ci	priv->rxdes = NULL;
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	if (priv->txdes)
9038c2ecf20Sopenharmony_ci		dma_free_coherent(priv->dev, MAX_TX_QUEUE_ENTRIES *
9048c2ecf20Sopenharmony_ci				  sizeof(struct ftgmac100_txdes),
9058c2ecf20Sopenharmony_ci				  priv->txdes, priv->txdes_dma);
9068c2ecf20Sopenharmony_ci	priv->txdes = NULL;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	/* Free scratch packet buffer */
9098c2ecf20Sopenharmony_ci	if (priv->rx_scratch)
9108c2ecf20Sopenharmony_ci		dma_free_coherent(priv->dev, RX_BUF_SIZE,
9118c2ecf20Sopenharmony_ci				  priv->rx_scratch, priv->rx_scratch_dma);
9128c2ecf20Sopenharmony_ci}
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_cistatic int ftgmac100_alloc_rings(struct ftgmac100 *priv)
9158c2ecf20Sopenharmony_ci{
9168c2ecf20Sopenharmony_ci	/* Allocate skb arrays */
9178c2ecf20Sopenharmony_ci	priv->rx_skbs = kcalloc(MAX_RX_QUEUE_ENTRIES, sizeof(void *),
9188c2ecf20Sopenharmony_ci				GFP_KERNEL);
9198c2ecf20Sopenharmony_ci	if (!priv->rx_skbs)
9208c2ecf20Sopenharmony_ci		return -ENOMEM;
9218c2ecf20Sopenharmony_ci	priv->tx_skbs = kcalloc(MAX_TX_QUEUE_ENTRIES, sizeof(void *),
9228c2ecf20Sopenharmony_ci				GFP_KERNEL);
9238c2ecf20Sopenharmony_ci	if (!priv->tx_skbs)
9248c2ecf20Sopenharmony_ci		return -ENOMEM;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	/* Allocate descriptors */
9278c2ecf20Sopenharmony_ci	priv->rxdes = dma_alloc_coherent(priv->dev,
9288c2ecf20Sopenharmony_ci					 MAX_RX_QUEUE_ENTRIES * sizeof(struct ftgmac100_rxdes),
9298c2ecf20Sopenharmony_ci					 &priv->rxdes_dma, GFP_KERNEL);
9308c2ecf20Sopenharmony_ci	if (!priv->rxdes)
9318c2ecf20Sopenharmony_ci		return -ENOMEM;
9328c2ecf20Sopenharmony_ci	priv->txdes = dma_alloc_coherent(priv->dev,
9338c2ecf20Sopenharmony_ci					 MAX_TX_QUEUE_ENTRIES * sizeof(struct ftgmac100_txdes),
9348c2ecf20Sopenharmony_ci					 &priv->txdes_dma, GFP_KERNEL);
9358c2ecf20Sopenharmony_ci	if (!priv->txdes)
9368c2ecf20Sopenharmony_ci		return -ENOMEM;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	/* Allocate scratch packet buffer */
9398c2ecf20Sopenharmony_ci	priv->rx_scratch = dma_alloc_coherent(priv->dev,
9408c2ecf20Sopenharmony_ci					      RX_BUF_SIZE,
9418c2ecf20Sopenharmony_ci					      &priv->rx_scratch_dma,
9428c2ecf20Sopenharmony_ci					      GFP_KERNEL);
9438c2ecf20Sopenharmony_ci	if (!priv->rx_scratch)
9448c2ecf20Sopenharmony_ci		return -ENOMEM;
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	return 0;
9478c2ecf20Sopenharmony_ci}
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_cistatic void ftgmac100_init_rings(struct ftgmac100 *priv)
9508c2ecf20Sopenharmony_ci{
9518c2ecf20Sopenharmony_ci	struct ftgmac100_rxdes *rxdes = NULL;
9528c2ecf20Sopenharmony_ci	struct ftgmac100_txdes *txdes = NULL;
9538c2ecf20Sopenharmony_ci	int i;
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	/* Update entries counts */
9568c2ecf20Sopenharmony_ci	priv->rx_q_entries = priv->new_rx_q_entries;
9578c2ecf20Sopenharmony_ci	priv->tx_q_entries = priv->new_tx_q_entries;
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	if (WARN_ON(priv->rx_q_entries < MIN_RX_QUEUE_ENTRIES))
9608c2ecf20Sopenharmony_ci		return;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	/* Initialize RX ring */
9638c2ecf20Sopenharmony_ci	for (i = 0; i < priv->rx_q_entries; i++) {
9648c2ecf20Sopenharmony_ci		rxdes = &priv->rxdes[i];
9658c2ecf20Sopenharmony_ci		rxdes->rxdes0 = 0;
9668c2ecf20Sopenharmony_ci		rxdes->rxdes3 = cpu_to_le32(priv->rx_scratch_dma);
9678c2ecf20Sopenharmony_ci	}
9688c2ecf20Sopenharmony_ci	/* Mark the end of the ring */
9698c2ecf20Sopenharmony_ci	rxdes->rxdes0 |= cpu_to_le32(priv->rxdes0_edorr_mask);
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	if (WARN_ON(priv->tx_q_entries < MIN_RX_QUEUE_ENTRIES))
9728c2ecf20Sopenharmony_ci		return;
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	/* Initialize TX ring */
9758c2ecf20Sopenharmony_ci	for (i = 0; i < priv->tx_q_entries; i++) {
9768c2ecf20Sopenharmony_ci		txdes = &priv->txdes[i];
9778c2ecf20Sopenharmony_ci		txdes->txdes0 = 0;
9788c2ecf20Sopenharmony_ci	}
9798c2ecf20Sopenharmony_ci	txdes->txdes0 |= cpu_to_le32(priv->txdes0_edotr_mask);
9808c2ecf20Sopenharmony_ci}
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_cistatic int ftgmac100_alloc_rx_buffers(struct ftgmac100 *priv)
9838c2ecf20Sopenharmony_ci{
9848c2ecf20Sopenharmony_ci	int i;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	for (i = 0; i < priv->rx_q_entries; i++) {
9878c2ecf20Sopenharmony_ci		struct ftgmac100_rxdes *rxdes = &priv->rxdes[i];
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci		if (ftgmac100_alloc_rx_buf(priv, i, rxdes, GFP_KERNEL))
9908c2ecf20Sopenharmony_ci			return -ENOMEM;
9918c2ecf20Sopenharmony_ci	}
9928c2ecf20Sopenharmony_ci	return 0;
9938c2ecf20Sopenharmony_ci}
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_cistatic void ftgmac100_adjust_link(struct net_device *netdev)
9968c2ecf20Sopenharmony_ci{
9978c2ecf20Sopenharmony_ci	struct ftgmac100 *priv = netdev_priv(netdev);
9988c2ecf20Sopenharmony_ci	struct phy_device *phydev = netdev->phydev;
9998c2ecf20Sopenharmony_ci	bool tx_pause, rx_pause;
10008c2ecf20Sopenharmony_ci	int new_speed;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	/* We store "no link" as speed 0 */
10038c2ecf20Sopenharmony_ci	if (!phydev->link)
10048c2ecf20Sopenharmony_ci		new_speed = 0;
10058c2ecf20Sopenharmony_ci	else
10068c2ecf20Sopenharmony_ci		new_speed = phydev->speed;
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	/* Grab pause settings from PHY if configured to do so */
10098c2ecf20Sopenharmony_ci	if (priv->aneg_pause) {
10108c2ecf20Sopenharmony_ci		rx_pause = tx_pause = phydev->pause;
10118c2ecf20Sopenharmony_ci		if (phydev->asym_pause)
10128c2ecf20Sopenharmony_ci			tx_pause = !rx_pause;
10138c2ecf20Sopenharmony_ci	} else {
10148c2ecf20Sopenharmony_ci		rx_pause = priv->rx_pause;
10158c2ecf20Sopenharmony_ci		tx_pause = priv->tx_pause;
10168c2ecf20Sopenharmony_ci	}
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	/* Link hasn't changed, do nothing */
10198c2ecf20Sopenharmony_ci	if (phydev->speed == priv->cur_speed &&
10208c2ecf20Sopenharmony_ci	    phydev->duplex == priv->cur_duplex &&
10218c2ecf20Sopenharmony_ci	    rx_pause == priv->rx_pause &&
10228c2ecf20Sopenharmony_ci	    tx_pause == priv->tx_pause)
10238c2ecf20Sopenharmony_ci		return;
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	/* Print status if we have a link or we had one and just lost it,
10268c2ecf20Sopenharmony_ci	 * don't print otherwise.
10278c2ecf20Sopenharmony_ci	 */
10288c2ecf20Sopenharmony_ci	if (new_speed || priv->cur_speed)
10298c2ecf20Sopenharmony_ci		phy_print_status(phydev);
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	priv->cur_speed = new_speed;
10328c2ecf20Sopenharmony_ci	priv->cur_duplex = phydev->duplex;
10338c2ecf20Sopenharmony_ci	priv->rx_pause = rx_pause;
10348c2ecf20Sopenharmony_ci	priv->tx_pause = tx_pause;
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	/* Link is down, do nothing else */
10378c2ecf20Sopenharmony_ci	if (!new_speed)
10388c2ecf20Sopenharmony_ci		return;
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci	/* Disable all interrupts */
10418c2ecf20Sopenharmony_ci	iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci	/* Reset the adapter asynchronously */
10448c2ecf20Sopenharmony_ci	schedule_work(&priv->reset_task);
10458c2ecf20Sopenharmony_ci}
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_cistatic int ftgmac100_mii_probe(struct ftgmac100 *priv, phy_interface_t intf)
10488c2ecf20Sopenharmony_ci{
10498c2ecf20Sopenharmony_ci	struct net_device *netdev = priv->netdev;
10508c2ecf20Sopenharmony_ci	struct phy_device *phydev;
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci	phydev = phy_find_first(priv->mii_bus);
10538c2ecf20Sopenharmony_ci	if (!phydev) {
10548c2ecf20Sopenharmony_ci		netdev_info(netdev, "%s: no PHY found\n", netdev->name);
10558c2ecf20Sopenharmony_ci		return -ENODEV;
10568c2ecf20Sopenharmony_ci	}
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	phydev = phy_connect(netdev, phydev_name(phydev),
10598c2ecf20Sopenharmony_ci			     &ftgmac100_adjust_link, intf);
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	if (IS_ERR(phydev)) {
10628c2ecf20Sopenharmony_ci		netdev_err(netdev, "%s: Could not attach to PHY\n", netdev->name);
10638c2ecf20Sopenharmony_ci		return PTR_ERR(phydev);
10648c2ecf20Sopenharmony_ci	}
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	/* Indicate that we support PAUSE frames (see comment in
10678c2ecf20Sopenharmony_ci	 * Documentation/networking/phy.rst)
10688c2ecf20Sopenharmony_ci	 */
10698c2ecf20Sopenharmony_ci	phy_support_asym_pause(phydev);
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	/* Display what we found */
10728c2ecf20Sopenharmony_ci	phy_attached_info(phydev);
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	return 0;
10758c2ecf20Sopenharmony_ci}
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_cistatic int ftgmac100_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum)
10788c2ecf20Sopenharmony_ci{
10798c2ecf20Sopenharmony_ci	struct net_device *netdev = bus->priv;
10808c2ecf20Sopenharmony_ci	struct ftgmac100 *priv = netdev_priv(netdev);
10818c2ecf20Sopenharmony_ci	unsigned int phycr;
10828c2ecf20Sopenharmony_ci	int i;
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	phycr = ioread32(priv->base + FTGMAC100_OFFSET_PHYCR);
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	/* preserve MDC cycle threshold */
10878c2ecf20Sopenharmony_ci	phycr &= FTGMAC100_PHYCR_MDC_CYCTHR_MASK;
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	phycr |= FTGMAC100_PHYCR_PHYAD(phy_addr) |
10908c2ecf20Sopenharmony_ci		 FTGMAC100_PHYCR_REGAD(regnum) |
10918c2ecf20Sopenharmony_ci		 FTGMAC100_PHYCR_MIIRD;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	iowrite32(phycr, priv->base + FTGMAC100_OFFSET_PHYCR);
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	for (i = 0; i < 10; i++) {
10968c2ecf20Sopenharmony_ci		phycr = ioread32(priv->base + FTGMAC100_OFFSET_PHYCR);
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci		if ((phycr & FTGMAC100_PHYCR_MIIRD) == 0) {
10998c2ecf20Sopenharmony_ci			int data;
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci			data = ioread32(priv->base + FTGMAC100_OFFSET_PHYDATA);
11028c2ecf20Sopenharmony_ci			return FTGMAC100_PHYDATA_MIIRDATA(data);
11038c2ecf20Sopenharmony_ci		}
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci		udelay(100);
11068c2ecf20Sopenharmony_ci	}
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	netdev_err(netdev, "mdio read timed out\n");
11098c2ecf20Sopenharmony_ci	return -EIO;
11108c2ecf20Sopenharmony_ci}
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_cistatic int ftgmac100_mdiobus_write(struct mii_bus *bus, int phy_addr,
11138c2ecf20Sopenharmony_ci				   int regnum, u16 value)
11148c2ecf20Sopenharmony_ci{
11158c2ecf20Sopenharmony_ci	struct net_device *netdev = bus->priv;
11168c2ecf20Sopenharmony_ci	struct ftgmac100 *priv = netdev_priv(netdev);
11178c2ecf20Sopenharmony_ci	unsigned int phycr;
11188c2ecf20Sopenharmony_ci	int data;
11198c2ecf20Sopenharmony_ci	int i;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	phycr = ioread32(priv->base + FTGMAC100_OFFSET_PHYCR);
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci	/* preserve MDC cycle threshold */
11248c2ecf20Sopenharmony_ci	phycr &= FTGMAC100_PHYCR_MDC_CYCTHR_MASK;
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	phycr |= FTGMAC100_PHYCR_PHYAD(phy_addr) |
11278c2ecf20Sopenharmony_ci		 FTGMAC100_PHYCR_REGAD(regnum) |
11288c2ecf20Sopenharmony_ci		 FTGMAC100_PHYCR_MIIWR;
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	data = FTGMAC100_PHYDATA_MIIWDATA(value);
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	iowrite32(data, priv->base + FTGMAC100_OFFSET_PHYDATA);
11338c2ecf20Sopenharmony_ci	iowrite32(phycr, priv->base + FTGMAC100_OFFSET_PHYCR);
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	for (i = 0; i < 10; i++) {
11368c2ecf20Sopenharmony_ci		phycr = ioread32(priv->base + FTGMAC100_OFFSET_PHYCR);
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci		if ((phycr & FTGMAC100_PHYCR_MIIWR) == 0)
11398c2ecf20Sopenharmony_ci			return 0;
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci		udelay(100);
11428c2ecf20Sopenharmony_ci	}
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	netdev_err(netdev, "mdio write timed out\n");
11458c2ecf20Sopenharmony_ci	return -EIO;
11468c2ecf20Sopenharmony_ci}
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_cistatic void ftgmac100_get_drvinfo(struct net_device *netdev,
11498c2ecf20Sopenharmony_ci				  struct ethtool_drvinfo *info)
11508c2ecf20Sopenharmony_ci{
11518c2ecf20Sopenharmony_ci	strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
11528c2ecf20Sopenharmony_ci	strlcpy(info->bus_info, dev_name(&netdev->dev), sizeof(info->bus_info));
11538c2ecf20Sopenharmony_ci}
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_cistatic void ftgmac100_get_ringparam(struct net_device *netdev,
11568c2ecf20Sopenharmony_ci				    struct ethtool_ringparam *ering)
11578c2ecf20Sopenharmony_ci{
11588c2ecf20Sopenharmony_ci	struct ftgmac100 *priv = netdev_priv(netdev);
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	memset(ering, 0, sizeof(*ering));
11618c2ecf20Sopenharmony_ci	ering->rx_max_pending = MAX_RX_QUEUE_ENTRIES;
11628c2ecf20Sopenharmony_ci	ering->tx_max_pending = MAX_TX_QUEUE_ENTRIES;
11638c2ecf20Sopenharmony_ci	ering->rx_pending = priv->rx_q_entries;
11648c2ecf20Sopenharmony_ci	ering->tx_pending = priv->tx_q_entries;
11658c2ecf20Sopenharmony_ci}
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_cistatic int ftgmac100_set_ringparam(struct net_device *netdev,
11688c2ecf20Sopenharmony_ci				   struct ethtool_ringparam *ering)
11698c2ecf20Sopenharmony_ci{
11708c2ecf20Sopenharmony_ci	struct ftgmac100 *priv = netdev_priv(netdev);
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci	if (ering->rx_pending > MAX_RX_QUEUE_ENTRIES ||
11738c2ecf20Sopenharmony_ci	    ering->tx_pending > MAX_TX_QUEUE_ENTRIES ||
11748c2ecf20Sopenharmony_ci	    ering->rx_pending < MIN_RX_QUEUE_ENTRIES ||
11758c2ecf20Sopenharmony_ci	    ering->tx_pending < MIN_TX_QUEUE_ENTRIES ||
11768c2ecf20Sopenharmony_ci	    !is_power_of_2(ering->rx_pending) ||
11778c2ecf20Sopenharmony_ci	    !is_power_of_2(ering->tx_pending))
11788c2ecf20Sopenharmony_ci		return -EINVAL;
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	priv->new_rx_q_entries = ering->rx_pending;
11818c2ecf20Sopenharmony_ci	priv->new_tx_q_entries = ering->tx_pending;
11828c2ecf20Sopenharmony_ci	if (netif_running(netdev))
11838c2ecf20Sopenharmony_ci		schedule_work(&priv->reset_task);
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	return 0;
11868c2ecf20Sopenharmony_ci}
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_cistatic void ftgmac100_get_pauseparam(struct net_device *netdev,
11898c2ecf20Sopenharmony_ci				     struct ethtool_pauseparam *pause)
11908c2ecf20Sopenharmony_ci{
11918c2ecf20Sopenharmony_ci	struct ftgmac100 *priv = netdev_priv(netdev);
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	pause->autoneg = priv->aneg_pause;
11948c2ecf20Sopenharmony_ci	pause->tx_pause = priv->tx_pause;
11958c2ecf20Sopenharmony_ci	pause->rx_pause = priv->rx_pause;
11968c2ecf20Sopenharmony_ci}
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_cistatic int ftgmac100_set_pauseparam(struct net_device *netdev,
11998c2ecf20Sopenharmony_ci				    struct ethtool_pauseparam *pause)
12008c2ecf20Sopenharmony_ci{
12018c2ecf20Sopenharmony_ci	struct ftgmac100 *priv = netdev_priv(netdev);
12028c2ecf20Sopenharmony_ci	struct phy_device *phydev = netdev->phydev;
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	priv->aneg_pause = pause->autoneg;
12058c2ecf20Sopenharmony_ci	priv->tx_pause = pause->tx_pause;
12068c2ecf20Sopenharmony_ci	priv->rx_pause = pause->rx_pause;
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	if (phydev)
12098c2ecf20Sopenharmony_ci		phy_set_asym_pause(phydev, pause->rx_pause, pause->tx_pause);
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	if (netif_running(netdev)) {
12128c2ecf20Sopenharmony_ci		if (!(phydev && priv->aneg_pause))
12138c2ecf20Sopenharmony_ci			ftgmac100_config_pause(priv);
12148c2ecf20Sopenharmony_ci	}
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	return 0;
12178c2ecf20Sopenharmony_ci}
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_cistatic const struct ethtool_ops ftgmac100_ethtool_ops = {
12208c2ecf20Sopenharmony_ci	.get_drvinfo		= ftgmac100_get_drvinfo,
12218c2ecf20Sopenharmony_ci	.get_link		= ethtool_op_get_link,
12228c2ecf20Sopenharmony_ci	.get_link_ksettings	= phy_ethtool_get_link_ksettings,
12238c2ecf20Sopenharmony_ci	.set_link_ksettings	= phy_ethtool_set_link_ksettings,
12248c2ecf20Sopenharmony_ci	.nway_reset		= phy_ethtool_nway_reset,
12258c2ecf20Sopenharmony_ci	.get_ringparam		= ftgmac100_get_ringparam,
12268c2ecf20Sopenharmony_ci	.set_ringparam		= ftgmac100_set_ringparam,
12278c2ecf20Sopenharmony_ci	.get_pauseparam		= ftgmac100_get_pauseparam,
12288c2ecf20Sopenharmony_ci	.set_pauseparam		= ftgmac100_set_pauseparam,
12298c2ecf20Sopenharmony_ci};
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_cistatic irqreturn_t ftgmac100_interrupt(int irq, void *dev_id)
12328c2ecf20Sopenharmony_ci{
12338c2ecf20Sopenharmony_ci	struct net_device *netdev = dev_id;
12348c2ecf20Sopenharmony_ci	struct ftgmac100 *priv = netdev_priv(netdev);
12358c2ecf20Sopenharmony_ci	unsigned int status, new_mask = FTGMAC100_INT_BAD;
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	/* Fetch and clear interrupt bits, process abnormal ones */
12388c2ecf20Sopenharmony_ci	status = ioread32(priv->base + FTGMAC100_OFFSET_ISR);
12398c2ecf20Sopenharmony_ci	iowrite32(status, priv->base + FTGMAC100_OFFSET_ISR);
12408c2ecf20Sopenharmony_ci	if (unlikely(status & FTGMAC100_INT_BAD)) {
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci		/* RX buffer unavailable */
12438c2ecf20Sopenharmony_ci		if (status & FTGMAC100_INT_NO_RXBUF)
12448c2ecf20Sopenharmony_ci			netdev->stats.rx_over_errors++;
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci		/* received packet lost due to RX FIFO full */
12478c2ecf20Sopenharmony_ci		if (status & FTGMAC100_INT_RPKT_LOST)
12488c2ecf20Sopenharmony_ci			netdev->stats.rx_fifo_errors++;
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci		/* sent packet lost due to excessive TX collision */
12518c2ecf20Sopenharmony_ci		if (status & FTGMAC100_INT_XPKT_LOST)
12528c2ecf20Sopenharmony_ci			netdev->stats.tx_fifo_errors++;
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci		/* AHB error -> Reset the chip */
12558c2ecf20Sopenharmony_ci		if (status & FTGMAC100_INT_AHB_ERR) {
12568c2ecf20Sopenharmony_ci			if (net_ratelimit())
12578c2ecf20Sopenharmony_ci				netdev_warn(netdev,
12588c2ecf20Sopenharmony_ci					   "AHB bus error ! Resetting chip.\n");
12598c2ecf20Sopenharmony_ci			iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
12608c2ecf20Sopenharmony_ci			schedule_work(&priv->reset_task);
12618c2ecf20Sopenharmony_ci			return IRQ_HANDLED;
12628c2ecf20Sopenharmony_ci		}
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci		/* We may need to restart the MAC after such errors, delay
12658c2ecf20Sopenharmony_ci		 * this until after we have freed some Rx buffers though
12668c2ecf20Sopenharmony_ci		 */
12678c2ecf20Sopenharmony_ci		priv->need_mac_restart = true;
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci		/* Disable those errors until we restart */
12708c2ecf20Sopenharmony_ci		new_mask &= ~status;
12718c2ecf20Sopenharmony_ci	}
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	/* Only enable "bad" interrupts while NAPI is on */
12748c2ecf20Sopenharmony_ci	iowrite32(new_mask, priv->base + FTGMAC100_OFFSET_IER);
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	/* Schedule NAPI bh */
12778c2ecf20Sopenharmony_ci	napi_schedule_irqoff(&priv->napi);
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
12808c2ecf20Sopenharmony_ci}
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_cistatic bool ftgmac100_check_rx(struct ftgmac100 *priv)
12838c2ecf20Sopenharmony_ci{
12848c2ecf20Sopenharmony_ci	struct ftgmac100_rxdes *rxdes = &priv->rxdes[priv->rx_pointer];
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	/* Do we have a packet ? */
12878c2ecf20Sopenharmony_ci	return !!(rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_RXPKT_RDY));
12888c2ecf20Sopenharmony_ci}
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_cistatic int ftgmac100_poll(struct napi_struct *napi, int budget)
12918c2ecf20Sopenharmony_ci{
12928c2ecf20Sopenharmony_ci	struct ftgmac100 *priv = container_of(napi, struct ftgmac100, napi);
12938c2ecf20Sopenharmony_ci	int work_done = 0;
12948c2ecf20Sopenharmony_ci	bool more;
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci	/* Handle TX completions */
12978c2ecf20Sopenharmony_ci	if (ftgmac100_tx_buf_cleanable(priv))
12988c2ecf20Sopenharmony_ci		ftgmac100_tx_complete(priv);
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	/* Handle RX packets */
13018c2ecf20Sopenharmony_ci	do {
13028c2ecf20Sopenharmony_ci		more = ftgmac100_rx_packet(priv, &work_done);
13038c2ecf20Sopenharmony_ci	} while (more && work_done < budget);
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci	/* The interrupt is telling us to kick the MAC back to life
13078c2ecf20Sopenharmony_ci	 * after an RX overflow
13088c2ecf20Sopenharmony_ci	 */
13098c2ecf20Sopenharmony_ci	if (unlikely(priv->need_mac_restart)) {
13108c2ecf20Sopenharmony_ci		ftgmac100_start_hw(priv);
13118c2ecf20Sopenharmony_ci		priv->need_mac_restart = false;
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci		/* Re-enable "bad" interrupts */
13148c2ecf20Sopenharmony_ci		iowrite32(FTGMAC100_INT_BAD,
13158c2ecf20Sopenharmony_ci			  priv->base + FTGMAC100_OFFSET_IER);
13168c2ecf20Sopenharmony_ci	}
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	/* As long as we are waiting for transmit packets to be
13198c2ecf20Sopenharmony_ci	 * completed we keep NAPI going
13208c2ecf20Sopenharmony_ci	 */
13218c2ecf20Sopenharmony_ci	if (ftgmac100_tx_buf_cleanable(priv))
13228c2ecf20Sopenharmony_ci		work_done = budget;
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci	if (work_done < budget) {
13258c2ecf20Sopenharmony_ci		/* We are about to re-enable all interrupts. However
13268c2ecf20Sopenharmony_ci		 * the HW has been latching RX/TX packet interrupts while
13278c2ecf20Sopenharmony_ci		 * they were masked. So we clear them first, then we need
13288c2ecf20Sopenharmony_ci		 * to re-check if there's something to process
13298c2ecf20Sopenharmony_ci		 */
13308c2ecf20Sopenharmony_ci		iowrite32(FTGMAC100_INT_RXTX,
13318c2ecf20Sopenharmony_ci			  priv->base + FTGMAC100_OFFSET_ISR);
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci		/* Push the above (and provides a barrier vs. subsequent
13348c2ecf20Sopenharmony_ci		 * reads of the descriptor).
13358c2ecf20Sopenharmony_ci		 */
13368c2ecf20Sopenharmony_ci		ioread32(priv->base + FTGMAC100_OFFSET_ISR);
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci		/* Check RX and TX descriptors for more work to do */
13398c2ecf20Sopenharmony_ci		if (ftgmac100_check_rx(priv) ||
13408c2ecf20Sopenharmony_ci		    ftgmac100_tx_buf_cleanable(priv))
13418c2ecf20Sopenharmony_ci			return budget;
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci		/* deschedule NAPI */
13448c2ecf20Sopenharmony_ci		napi_complete(napi);
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci		/* enable all interrupts */
13478c2ecf20Sopenharmony_ci		iowrite32(FTGMAC100_INT_ALL,
13488c2ecf20Sopenharmony_ci			  priv->base + FTGMAC100_OFFSET_IER);
13498c2ecf20Sopenharmony_ci	}
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci	return work_done;
13528c2ecf20Sopenharmony_ci}
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_cistatic int ftgmac100_init_all(struct ftgmac100 *priv, bool ignore_alloc_err)
13558c2ecf20Sopenharmony_ci{
13568c2ecf20Sopenharmony_ci	int err = 0;
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci	/* Re-init descriptors (adjust queue sizes) */
13598c2ecf20Sopenharmony_ci	ftgmac100_init_rings(priv);
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	/* Realloc rx descriptors */
13628c2ecf20Sopenharmony_ci	err = ftgmac100_alloc_rx_buffers(priv);
13638c2ecf20Sopenharmony_ci	if (err && !ignore_alloc_err)
13648c2ecf20Sopenharmony_ci		return err;
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	/* Reinit and restart HW */
13678c2ecf20Sopenharmony_ci	ftgmac100_init_hw(priv);
13688c2ecf20Sopenharmony_ci	ftgmac100_config_pause(priv);
13698c2ecf20Sopenharmony_ci	ftgmac100_start_hw(priv);
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci	/* Re-enable the device */
13728c2ecf20Sopenharmony_ci	napi_enable(&priv->napi);
13738c2ecf20Sopenharmony_ci	netif_start_queue(priv->netdev);
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	/* Enable all interrupts */
13768c2ecf20Sopenharmony_ci	iowrite32(FTGMAC100_INT_ALL, priv->base + FTGMAC100_OFFSET_IER);
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	return err;
13798c2ecf20Sopenharmony_ci}
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_cistatic void ftgmac100_reset_task(struct work_struct *work)
13828c2ecf20Sopenharmony_ci{
13838c2ecf20Sopenharmony_ci	struct ftgmac100 *priv = container_of(work, struct ftgmac100,
13848c2ecf20Sopenharmony_ci					      reset_task);
13858c2ecf20Sopenharmony_ci	struct net_device *netdev = priv->netdev;
13868c2ecf20Sopenharmony_ci	int err;
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci	netdev_dbg(netdev, "Resetting NIC...\n");
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci	/* Lock the world */
13918c2ecf20Sopenharmony_ci	rtnl_lock();
13928c2ecf20Sopenharmony_ci	if (netdev->phydev)
13938c2ecf20Sopenharmony_ci		mutex_lock(&netdev->phydev->lock);
13948c2ecf20Sopenharmony_ci	if (priv->mii_bus)
13958c2ecf20Sopenharmony_ci		mutex_lock(&priv->mii_bus->mdio_lock);
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci	/* Check if the interface is still up */
13998c2ecf20Sopenharmony_ci	if (!netif_running(netdev))
14008c2ecf20Sopenharmony_ci		goto bail;
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci	/* Stop the network stack */
14038c2ecf20Sopenharmony_ci	netif_trans_update(netdev);
14048c2ecf20Sopenharmony_ci	napi_disable(&priv->napi);
14058c2ecf20Sopenharmony_ci	netif_tx_disable(netdev);
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci	/* Stop and reset the MAC */
14088c2ecf20Sopenharmony_ci	ftgmac100_stop_hw(priv);
14098c2ecf20Sopenharmony_ci	err = ftgmac100_reset_and_config_mac(priv);
14108c2ecf20Sopenharmony_ci	if (err) {
14118c2ecf20Sopenharmony_ci		/* Not much we can do ... it might come back... */
14128c2ecf20Sopenharmony_ci		netdev_err(netdev, "attempting to continue...\n");
14138c2ecf20Sopenharmony_ci	}
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci	/* Free all rx and tx buffers */
14168c2ecf20Sopenharmony_ci	ftgmac100_free_buffers(priv);
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci	/* Setup everything again and restart chip */
14198c2ecf20Sopenharmony_ci	ftgmac100_init_all(priv, true);
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	netdev_dbg(netdev, "Reset done !\n");
14228c2ecf20Sopenharmony_ci bail:
14238c2ecf20Sopenharmony_ci	if (priv->mii_bus)
14248c2ecf20Sopenharmony_ci		mutex_unlock(&priv->mii_bus->mdio_lock);
14258c2ecf20Sopenharmony_ci	if (netdev->phydev)
14268c2ecf20Sopenharmony_ci		mutex_unlock(&netdev->phydev->lock);
14278c2ecf20Sopenharmony_ci	rtnl_unlock();
14288c2ecf20Sopenharmony_ci}
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_cistatic int ftgmac100_open(struct net_device *netdev)
14318c2ecf20Sopenharmony_ci{
14328c2ecf20Sopenharmony_ci	struct ftgmac100 *priv = netdev_priv(netdev);
14338c2ecf20Sopenharmony_ci	int err;
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci	/* Allocate ring buffers  */
14368c2ecf20Sopenharmony_ci	err = ftgmac100_alloc_rings(priv);
14378c2ecf20Sopenharmony_ci	if (err) {
14388c2ecf20Sopenharmony_ci		netdev_err(netdev, "Failed to allocate descriptors\n");
14398c2ecf20Sopenharmony_ci		return err;
14408c2ecf20Sopenharmony_ci	}
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci	/* When using NC-SI we force the speed to 100Mbit/s full duplex,
14438c2ecf20Sopenharmony_ci	 *
14448c2ecf20Sopenharmony_ci	 * Otherwise we leave it set to 0 (no link), the link
14458c2ecf20Sopenharmony_ci	 * message from the PHY layer will handle setting it up to
14468c2ecf20Sopenharmony_ci	 * something else if needed.
14478c2ecf20Sopenharmony_ci	 */
14488c2ecf20Sopenharmony_ci	if (priv->use_ncsi) {
14498c2ecf20Sopenharmony_ci		priv->cur_duplex = DUPLEX_FULL;
14508c2ecf20Sopenharmony_ci		priv->cur_speed = SPEED_100;
14518c2ecf20Sopenharmony_ci	} else {
14528c2ecf20Sopenharmony_ci		priv->cur_duplex = 0;
14538c2ecf20Sopenharmony_ci		priv->cur_speed = 0;
14548c2ecf20Sopenharmony_ci	}
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	/* Reset the hardware */
14578c2ecf20Sopenharmony_ci	err = ftgmac100_reset_and_config_mac(priv);
14588c2ecf20Sopenharmony_ci	if (err)
14598c2ecf20Sopenharmony_ci		goto err_hw;
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci	/* Initialize NAPI */
14628c2ecf20Sopenharmony_ci	netif_napi_add(netdev, &priv->napi, ftgmac100_poll, 64);
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_ci	/* Grab our interrupt */
14658c2ecf20Sopenharmony_ci	err = request_irq(netdev->irq, ftgmac100_interrupt, 0, netdev->name, netdev);
14668c2ecf20Sopenharmony_ci	if (err) {
14678c2ecf20Sopenharmony_ci		netdev_err(netdev, "failed to request irq %d\n", netdev->irq);
14688c2ecf20Sopenharmony_ci		goto err_irq;
14698c2ecf20Sopenharmony_ci	}
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	/* Start things up */
14728c2ecf20Sopenharmony_ci	err = ftgmac100_init_all(priv, false);
14738c2ecf20Sopenharmony_ci	if (err) {
14748c2ecf20Sopenharmony_ci		netdev_err(netdev, "Failed to allocate packet buffers\n");
14758c2ecf20Sopenharmony_ci		goto err_alloc;
14768c2ecf20Sopenharmony_ci	}
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci	if (netdev->phydev) {
14798c2ecf20Sopenharmony_ci		/* If we have a PHY, start polling */
14808c2ecf20Sopenharmony_ci		phy_start(netdev->phydev);
14818c2ecf20Sopenharmony_ci	} else if (priv->use_ncsi) {
14828c2ecf20Sopenharmony_ci		/* If using NC-SI, set our carrier on and start the stack */
14838c2ecf20Sopenharmony_ci		netif_carrier_on(netdev);
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_ci		/* Start the NCSI device */
14868c2ecf20Sopenharmony_ci		err = ncsi_start_dev(priv->ndev);
14878c2ecf20Sopenharmony_ci		if (err)
14888c2ecf20Sopenharmony_ci			goto err_ncsi;
14898c2ecf20Sopenharmony_ci	}
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci	return 0;
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci err_ncsi:
14948c2ecf20Sopenharmony_ci	napi_disable(&priv->napi);
14958c2ecf20Sopenharmony_ci	netif_stop_queue(netdev);
14968c2ecf20Sopenharmony_ci err_alloc:
14978c2ecf20Sopenharmony_ci	ftgmac100_free_buffers(priv);
14988c2ecf20Sopenharmony_ci	free_irq(netdev->irq, netdev);
14998c2ecf20Sopenharmony_ci err_irq:
15008c2ecf20Sopenharmony_ci	netif_napi_del(&priv->napi);
15018c2ecf20Sopenharmony_ci err_hw:
15028c2ecf20Sopenharmony_ci	iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
15038c2ecf20Sopenharmony_ci	ftgmac100_free_rings(priv);
15048c2ecf20Sopenharmony_ci	return err;
15058c2ecf20Sopenharmony_ci}
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_cistatic int ftgmac100_stop(struct net_device *netdev)
15088c2ecf20Sopenharmony_ci{
15098c2ecf20Sopenharmony_ci	struct ftgmac100 *priv = netdev_priv(netdev);
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci	/* Note about the reset task: We are called with the rtnl lock
15128c2ecf20Sopenharmony_ci	 * held, so we are synchronized against the core of the reset
15138c2ecf20Sopenharmony_ci	 * task. We must not try to synchronously cancel it otherwise
15148c2ecf20Sopenharmony_ci	 * we can deadlock. But since it will test for netif_running()
15158c2ecf20Sopenharmony_ci	 * which has already been cleared by the net core, we don't
15168c2ecf20Sopenharmony_ci	 * anything special to do.
15178c2ecf20Sopenharmony_ci	 */
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci	/* disable all interrupts */
15208c2ecf20Sopenharmony_ci	iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci	netif_stop_queue(netdev);
15238c2ecf20Sopenharmony_ci	napi_disable(&priv->napi);
15248c2ecf20Sopenharmony_ci	netif_napi_del(&priv->napi);
15258c2ecf20Sopenharmony_ci	if (netdev->phydev)
15268c2ecf20Sopenharmony_ci		phy_stop(netdev->phydev);
15278c2ecf20Sopenharmony_ci	else if (priv->use_ncsi)
15288c2ecf20Sopenharmony_ci		ncsi_stop_dev(priv->ndev);
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci	ftgmac100_stop_hw(priv);
15318c2ecf20Sopenharmony_ci	free_irq(netdev->irq, netdev);
15328c2ecf20Sopenharmony_ci	ftgmac100_free_buffers(priv);
15338c2ecf20Sopenharmony_ci	ftgmac100_free_rings(priv);
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	return 0;
15368c2ecf20Sopenharmony_ci}
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_cistatic void ftgmac100_tx_timeout(struct net_device *netdev, unsigned int txqueue)
15398c2ecf20Sopenharmony_ci{
15408c2ecf20Sopenharmony_ci	struct ftgmac100 *priv = netdev_priv(netdev);
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci	/* Disable all interrupts */
15438c2ecf20Sopenharmony_ci	iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci	/* Do the reset outside of interrupt context */
15468c2ecf20Sopenharmony_ci	schedule_work(&priv->reset_task);
15478c2ecf20Sopenharmony_ci}
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_cistatic int ftgmac100_set_features(struct net_device *netdev,
15508c2ecf20Sopenharmony_ci				  netdev_features_t features)
15518c2ecf20Sopenharmony_ci{
15528c2ecf20Sopenharmony_ci	struct ftgmac100 *priv = netdev_priv(netdev);
15538c2ecf20Sopenharmony_ci	netdev_features_t changed = netdev->features ^ features;
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	if (!netif_running(netdev))
15568c2ecf20Sopenharmony_ci		return 0;
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_ci	/* Update the vlan filtering bit */
15598c2ecf20Sopenharmony_ci	if (changed & NETIF_F_HW_VLAN_CTAG_RX) {
15608c2ecf20Sopenharmony_ci		u32 maccr;
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci		maccr = ioread32(priv->base + FTGMAC100_OFFSET_MACCR);
15638c2ecf20Sopenharmony_ci		if (priv->netdev->features & NETIF_F_HW_VLAN_CTAG_RX)
15648c2ecf20Sopenharmony_ci			maccr |= FTGMAC100_MACCR_RM_VLAN;
15658c2ecf20Sopenharmony_ci		else
15668c2ecf20Sopenharmony_ci			maccr &= ~FTGMAC100_MACCR_RM_VLAN;
15678c2ecf20Sopenharmony_ci		iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR);
15688c2ecf20Sopenharmony_ci	}
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci	return 0;
15718c2ecf20Sopenharmony_ci}
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
15748c2ecf20Sopenharmony_cistatic void ftgmac100_poll_controller(struct net_device *netdev)
15758c2ecf20Sopenharmony_ci{
15768c2ecf20Sopenharmony_ci	unsigned long flags;
15778c2ecf20Sopenharmony_ci
15788c2ecf20Sopenharmony_ci	local_irq_save(flags);
15798c2ecf20Sopenharmony_ci	ftgmac100_interrupt(netdev->irq, netdev);
15808c2ecf20Sopenharmony_ci	local_irq_restore(flags);
15818c2ecf20Sopenharmony_ci}
15828c2ecf20Sopenharmony_ci#endif
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_cistatic const struct net_device_ops ftgmac100_netdev_ops = {
15858c2ecf20Sopenharmony_ci	.ndo_open		= ftgmac100_open,
15868c2ecf20Sopenharmony_ci	.ndo_stop		= ftgmac100_stop,
15878c2ecf20Sopenharmony_ci	.ndo_start_xmit		= ftgmac100_hard_start_xmit,
15888c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= ftgmac100_set_mac_addr,
15898c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
15908c2ecf20Sopenharmony_ci	.ndo_do_ioctl		= phy_do_ioctl,
15918c2ecf20Sopenharmony_ci	.ndo_tx_timeout		= ftgmac100_tx_timeout,
15928c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= ftgmac100_set_rx_mode,
15938c2ecf20Sopenharmony_ci	.ndo_set_features	= ftgmac100_set_features,
15948c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
15958c2ecf20Sopenharmony_ci	.ndo_poll_controller	= ftgmac100_poll_controller,
15968c2ecf20Sopenharmony_ci#endif
15978c2ecf20Sopenharmony_ci	.ndo_vlan_rx_add_vid	= ncsi_vlan_rx_add_vid,
15988c2ecf20Sopenharmony_ci	.ndo_vlan_rx_kill_vid	= ncsi_vlan_rx_kill_vid,
15998c2ecf20Sopenharmony_ci};
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_cistatic int ftgmac100_setup_mdio(struct net_device *netdev)
16028c2ecf20Sopenharmony_ci{
16038c2ecf20Sopenharmony_ci	struct ftgmac100 *priv = netdev_priv(netdev);
16048c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(priv->dev);
16058c2ecf20Sopenharmony_ci	phy_interface_t phy_intf = PHY_INTERFACE_MODE_RGMII;
16068c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
16078c2ecf20Sopenharmony_ci	int i, err = 0;
16088c2ecf20Sopenharmony_ci	u32 reg;
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_ci	/* initialize mdio bus */
16118c2ecf20Sopenharmony_ci	priv->mii_bus = mdiobus_alloc();
16128c2ecf20Sopenharmony_ci	if (!priv->mii_bus)
16138c2ecf20Sopenharmony_ci		return -EIO;
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci	if (of_device_is_compatible(np, "aspeed,ast2400-mac") ||
16168c2ecf20Sopenharmony_ci	    of_device_is_compatible(np, "aspeed,ast2500-mac")) {
16178c2ecf20Sopenharmony_ci		/* The AST2600 has a separate MDIO controller */
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_ci		/* For the AST2400 and AST2500 this driver only supports the
16208c2ecf20Sopenharmony_ci		 * old MDIO interface
16218c2ecf20Sopenharmony_ci		 */
16228c2ecf20Sopenharmony_ci		reg = ioread32(priv->base + FTGMAC100_OFFSET_REVR);
16238c2ecf20Sopenharmony_ci		reg &= ~FTGMAC100_REVR_NEW_MDIO_INTERFACE;
16248c2ecf20Sopenharmony_ci		iowrite32(reg, priv->base + FTGMAC100_OFFSET_REVR);
16258c2ecf20Sopenharmony_ci	}
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci	/* Get PHY mode from device-tree */
16288c2ecf20Sopenharmony_ci	if (np) {
16298c2ecf20Sopenharmony_ci		/* Default to RGMII. It's a gigabit part after all */
16308c2ecf20Sopenharmony_ci		err = of_get_phy_mode(np, &phy_intf);
16318c2ecf20Sopenharmony_ci		if (err)
16328c2ecf20Sopenharmony_ci			phy_intf = PHY_INTERFACE_MODE_RGMII;
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci		/* Aspeed only supports these. I don't know about other IP
16358c2ecf20Sopenharmony_ci		 * block vendors so I'm going to just let them through for
16368c2ecf20Sopenharmony_ci		 * now. Note that this is only a warning if for some obscure
16378c2ecf20Sopenharmony_ci		 * reason the DT really means to lie about it or it's a newer
16388c2ecf20Sopenharmony_ci		 * part we don't know about.
16398c2ecf20Sopenharmony_ci		 *
16408c2ecf20Sopenharmony_ci		 * On the Aspeed SoC there are additionally straps and SCU
16418c2ecf20Sopenharmony_ci		 * control bits that could tell us what the interface is
16428c2ecf20Sopenharmony_ci		 * (or allow us to configure it while the IP block is held
16438c2ecf20Sopenharmony_ci		 * in reset). For now I chose to keep this driver away from
16448c2ecf20Sopenharmony_ci		 * those SoC specific bits and assume the device-tree is
16458c2ecf20Sopenharmony_ci		 * right and the SCU has been configured properly by pinmux
16468c2ecf20Sopenharmony_ci		 * or the firmware.
16478c2ecf20Sopenharmony_ci		 */
16488c2ecf20Sopenharmony_ci		if (priv->is_aspeed &&
16498c2ecf20Sopenharmony_ci		    phy_intf != PHY_INTERFACE_MODE_RMII &&
16508c2ecf20Sopenharmony_ci		    phy_intf != PHY_INTERFACE_MODE_RGMII &&
16518c2ecf20Sopenharmony_ci		    phy_intf != PHY_INTERFACE_MODE_RGMII_ID &&
16528c2ecf20Sopenharmony_ci		    phy_intf != PHY_INTERFACE_MODE_RGMII_RXID &&
16538c2ecf20Sopenharmony_ci		    phy_intf != PHY_INTERFACE_MODE_RGMII_TXID) {
16548c2ecf20Sopenharmony_ci			netdev_warn(netdev,
16558c2ecf20Sopenharmony_ci				   "Unsupported PHY mode %s !\n",
16568c2ecf20Sopenharmony_ci				   phy_modes(phy_intf));
16578c2ecf20Sopenharmony_ci		}
16588c2ecf20Sopenharmony_ci	}
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci	priv->mii_bus->name = "ftgmac100_mdio";
16618c2ecf20Sopenharmony_ci	snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s-%d",
16628c2ecf20Sopenharmony_ci		 pdev->name, pdev->id);
16638c2ecf20Sopenharmony_ci	priv->mii_bus->parent = priv->dev;
16648c2ecf20Sopenharmony_ci	priv->mii_bus->priv = priv->netdev;
16658c2ecf20Sopenharmony_ci	priv->mii_bus->read = ftgmac100_mdiobus_read;
16668c2ecf20Sopenharmony_ci	priv->mii_bus->write = ftgmac100_mdiobus_write;
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci	for (i = 0; i < PHY_MAX_ADDR; i++)
16698c2ecf20Sopenharmony_ci		priv->mii_bus->irq[i] = PHY_POLL;
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_ci	err = mdiobus_register(priv->mii_bus);
16728c2ecf20Sopenharmony_ci	if (err) {
16738c2ecf20Sopenharmony_ci		dev_err(priv->dev, "Cannot register MDIO bus!\n");
16748c2ecf20Sopenharmony_ci		goto err_register_mdiobus;
16758c2ecf20Sopenharmony_ci	}
16768c2ecf20Sopenharmony_ci
16778c2ecf20Sopenharmony_ci	err = ftgmac100_mii_probe(priv, phy_intf);
16788c2ecf20Sopenharmony_ci	if (err) {
16798c2ecf20Sopenharmony_ci		dev_err(priv->dev, "MII Probe failed!\n");
16808c2ecf20Sopenharmony_ci		goto err_mii_probe;
16818c2ecf20Sopenharmony_ci	}
16828c2ecf20Sopenharmony_ci
16838c2ecf20Sopenharmony_ci	return 0;
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_cierr_mii_probe:
16868c2ecf20Sopenharmony_ci	mdiobus_unregister(priv->mii_bus);
16878c2ecf20Sopenharmony_cierr_register_mdiobus:
16888c2ecf20Sopenharmony_ci	mdiobus_free(priv->mii_bus);
16898c2ecf20Sopenharmony_ci	return err;
16908c2ecf20Sopenharmony_ci}
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_cistatic void ftgmac100_destroy_mdio(struct net_device *netdev)
16938c2ecf20Sopenharmony_ci{
16948c2ecf20Sopenharmony_ci	struct ftgmac100 *priv = netdev_priv(netdev);
16958c2ecf20Sopenharmony_ci
16968c2ecf20Sopenharmony_ci	if (!netdev->phydev)
16978c2ecf20Sopenharmony_ci		return;
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ci	phy_disconnect(netdev->phydev);
17008c2ecf20Sopenharmony_ci	mdiobus_unregister(priv->mii_bus);
17018c2ecf20Sopenharmony_ci	mdiobus_free(priv->mii_bus);
17028c2ecf20Sopenharmony_ci}
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_cistatic void ftgmac100_ncsi_handler(struct ncsi_dev *nd)
17058c2ecf20Sopenharmony_ci{
17068c2ecf20Sopenharmony_ci	if (unlikely(nd->state != ncsi_dev_state_functional))
17078c2ecf20Sopenharmony_ci		return;
17088c2ecf20Sopenharmony_ci
17098c2ecf20Sopenharmony_ci	netdev_dbg(nd->dev, "NCSI interface %s\n",
17108c2ecf20Sopenharmony_ci		   nd->link_up ? "up" : "down");
17118c2ecf20Sopenharmony_ci}
17128c2ecf20Sopenharmony_ci
17138c2ecf20Sopenharmony_cistatic int ftgmac100_setup_clk(struct ftgmac100 *priv)
17148c2ecf20Sopenharmony_ci{
17158c2ecf20Sopenharmony_ci	struct clk *clk;
17168c2ecf20Sopenharmony_ci	int rc;
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_ci	clk = devm_clk_get(priv->dev, NULL /* MACCLK */);
17198c2ecf20Sopenharmony_ci	if (IS_ERR(clk))
17208c2ecf20Sopenharmony_ci		return PTR_ERR(clk);
17218c2ecf20Sopenharmony_ci	priv->clk = clk;
17228c2ecf20Sopenharmony_ci	rc = clk_prepare_enable(priv->clk);
17238c2ecf20Sopenharmony_ci	if (rc)
17248c2ecf20Sopenharmony_ci		return rc;
17258c2ecf20Sopenharmony_ci
17268c2ecf20Sopenharmony_ci	/* Aspeed specifies a 100MHz clock is required for up to
17278c2ecf20Sopenharmony_ci	 * 1000Mbit link speeds. As NCSI is limited to 100Mbit, 25MHz
17288c2ecf20Sopenharmony_ci	 * is sufficient
17298c2ecf20Sopenharmony_ci	 */
17308c2ecf20Sopenharmony_ci	rc = clk_set_rate(priv->clk, priv->use_ncsi ? FTGMAC_25MHZ :
17318c2ecf20Sopenharmony_ci			  FTGMAC_100MHZ);
17328c2ecf20Sopenharmony_ci	if (rc)
17338c2ecf20Sopenharmony_ci		goto cleanup_clk;
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci	/* RCLK is for RMII, typically used for NCSI. Optional because it's not
17368c2ecf20Sopenharmony_ci	 * necessary if it's the AST2400 MAC, or the MAC is configured for
17378c2ecf20Sopenharmony_ci	 * RGMII, or the controller is not an ASPEED-based controller.
17388c2ecf20Sopenharmony_ci	 */
17398c2ecf20Sopenharmony_ci	priv->rclk = devm_clk_get_optional(priv->dev, "RCLK");
17408c2ecf20Sopenharmony_ci	rc = clk_prepare_enable(priv->rclk);
17418c2ecf20Sopenharmony_ci	if (!rc)
17428c2ecf20Sopenharmony_ci		return 0;
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_cicleanup_clk:
17458c2ecf20Sopenharmony_ci	clk_disable_unprepare(priv->clk);
17468c2ecf20Sopenharmony_ci
17478c2ecf20Sopenharmony_ci	return rc;
17488c2ecf20Sopenharmony_ci}
17498c2ecf20Sopenharmony_ci
17508c2ecf20Sopenharmony_cistatic bool ftgmac100_has_child_node(struct device_node *np, const char *name)
17518c2ecf20Sopenharmony_ci{
17528c2ecf20Sopenharmony_ci	struct device_node *child_np = of_get_child_by_name(np, name);
17538c2ecf20Sopenharmony_ci	bool ret = false;
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci	if (child_np) {
17568c2ecf20Sopenharmony_ci		ret = true;
17578c2ecf20Sopenharmony_ci		of_node_put(child_np);
17588c2ecf20Sopenharmony_ci	}
17598c2ecf20Sopenharmony_ci
17608c2ecf20Sopenharmony_ci	return ret;
17618c2ecf20Sopenharmony_ci}
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_cistatic int ftgmac100_probe(struct platform_device *pdev)
17648c2ecf20Sopenharmony_ci{
17658c2ecf20Sopenharmony_ci	struct resource *res;
17668c2ecf20Sopenharmony_ci	int irq;
17678c2ecf20Sopenharmony_ci	struct net_device *netdev;
17688c2ecf20Sopenharmony_ci	struct ftgmac100 *priv;
17698c2ecf20Sopenharmony_ci	struct device_node *np;
17708c2ecf20Sopenharmony_ci	int err = 0;
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
17738c2ecf20Sopenharmony_ci	if (!res)
17748c2ecf20Sopenharmony_ci		return -ENXIO;
17758c2ecf20Sopenharmony_ci
17768c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
17778c2ecf20Sopenharmony_ci	if (irq < 0)
17788c2ecf20Sopenharmony_ci		return irq;
17798c2ecf20Sopenharmony_ci
17808c2ecf20Sopenharmony_ci	/* setup net_device */
17818c2ecf20Sopenharmony_ci	netdev = alloc_etherdev(sizeof(*priv));
17828c2ecf20Sopenharmony_ci	if (!netdev) {
17838c2ecf20Sopenharmony_ci		err = -ENOMEM;
17848c2ecf20Sopenharmony_ci		goto err_alloc_etherdev;
17858c2ecf20Sopenharmony_ci	}
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(netdev, &pdev->dev);
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_ci	netdev->ethtool_ops = &ftgmac100_ethtool_ops;
17908c2ecf20Sopenharmony_ci	netdev->netdev_ops = &ftgmac100_netdev_ops;
17918c2ecf20Sopenharmony_ci	netdev->watchdog_timeo = 5 * HZ;
17928c2ecf20Sopenharmony_ci
17938c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, netdev);
17948c2ecf20Sopenharmony_ci
17958c2ecf20Sopenharmony_ci	/* setup private data */
17968c2ecf20Sopenharmony_ci	priv = netdev_priv(netdev);
17978c2ecf20Sopenharmony_ci	priv->netdev = netdev;
17988c2ecf20Sopenharmony_ci	priv->dev = &pdev->dev;
17998c2ecf20Sopenharmony_ci	INIT_WORK(&priv->reset_task, ftgmac100_reset_task);
18008c2ecf20Sopenharmony_ci
18018c2ecf20Sopenharmony_ci	/* map io memory */
18028c2ecf20Sopenharmony_ci	priv->res = request_mem_region(res->start, resource_size(res),
18038c2ecf20Sopenharmony_ci				       dev_name(&pdev->dev));
18048c2ecf20Sopenharmony_ci	if (!priv->res) {
18058c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Could not reserve memory region\n");
18068c2ecf20Sopenharmony_ci		err = -ENOMEM;
18078c2ecf20Sopenharmony_ci		goto err_req_mem;
18088c2ecf20Sopenharmony_ci	}
18098c2ecf20Sopenharmony_ci
18108c2ecf20Sopenharmony_ci	priv->base = ioremap(res->start, resource_size(res));
18118c2ecf20Sopenharmony_ci	if (!priv->base) {
18128c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to ioremap ethernet registers\n");
18138c2ecf20Sopenharmony_ci		err = -EIO;
18148c2ecf20Sopenharmony_ci		goto err_ioremap;
18158c2ecf20Sopenharmony_ci	}
18168c2ecf20Sopenharmony_ci
18178c2ecf20Sopenharmony_ci	netdev->irq = irq;
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci	/* Enable pause */
18208c2ecf20Sopenharmony_ci	priv->tx_pause = true;
18218c2ecf20Sopenharmony_ci	priv->rx_pause = true;
18228c2ecf20Sopenharmony_ci	priv->aneg_pause = true;
18238c2ecf20Sopenharmony_ci
18248c2ecf20Sopenharmony_ci	/* MAC address from chip or random one */
18258c2ecf20Sopenharmony_ci	ftgmac100_initial_mac(priv);
18268c2ecf20Sopenharmony_ci
18278c2ecf20Sopenharmony_ci	np = pdev->dev.of_node;
18288c2ecf20Sopenharmony_ci	if (np && (of_device_is_compatible(np, "aspeed,ast2400-mac") ||
18298c2ecf20Sopenharmony_ci		   of_device_is_compatible(np, "aspeed,ast2500-mac") ||
18308c2ecf20Sopenharmony_ci		   of_device_is_compatible(np, "aspeed,ast2600-mac"))) {
18318c2ecf20Sopenharmony_ci		priv->rxdes0_edorr_mask = BIT(30);
18328c2ecf20Sopenharmony_ci		priv->txdes0_edotr_mask = BIT(30);
18338c2ecf20Sopenharmony_ci		priv->is_aspeed = true;
18348c2ecf20Sopenharmony_ci		/* Disable ast2600 problematic HW arbitration */
18358c2ecf20Sopenharmony_ci		if (of_device_is_compatible(np, "aspeed,ast2600-mac")) {
18368c2ecf20Sopenharmony_ci			iowrite32(FTGMAC100_TM_DEFAULT,
18378c2ecf20Sopenharmony_ci				  priv->base + FTGMAC100_OFFSET_TM);
18388c2ecf20Sopenharmony_ci		}
18398c2ecf20Sopenharmony_ci	} else {
18408c2ecf20Sopenharmony_ci		priv->rxdes0_edorr_mask = BIT(15);
18418c2ecf20Sopenharmony_ci		priv->txdes0_edotr_mask = BIT(15);
18428c2ecf20Sopenharmony_ci	}
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_ci	if (np && of_get_property(np, "use-ncsi", NULL)) {
18458c2ecf20Sopenharmony_ci		if (!IS_ENABLED(CONFIG_NET_NCSI)) {
18468c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "NCSI stack not enabled\n");
18478c2ecf20Sopenharmony_ci			err = -EINVAL;
18488c2ecf20Sopenharmony_ci			goto err_ncsi_dev;
18498c2ecf20Sopenharmony_ci		}
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci		dev_info(&pdev->dev, "Using NCSI interface\n");
18528c2ecf20Sopenharmony_ci		priv->use_ncsi = true;
18538c2ecf20Sopenharmony_ci		priv->ndev = ncsi_register_dev(netdev, ftgmac100_ncsi_handler);
18548c2ecf20Sopenharmony_ci		if (!priv->ndev) {
18558c2ecf20Sopenharmony_ci			err = -EINVAL;
18568c2ecf20Sopenharmony_ci			goto err_ncsi_dev;
18578c2ecf20Sopenharmony_ci		}
18588c2ecf20Sopenharmony_ci	} else if (np && of_get_property(np, "phy-handle", NULL)) {
18598c2ecf20Sopenharmony_ci		struct phy_device *phy;
18608c2ecf20Sopenharmony_ci
18618c2ecf20Sopenharmony_ci		phy = of_phy_get_and_connect(priv->netdev, np,
18628c2ecf20Sopenharmony_ci					     &ftgmac100_adjust_link);
18638c2ecf20Sopenharmony_ci		if (!phy) {
18648c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "Failed to connect to phy\n");
18658c2ecf20Sopenharmony_ci			err = -EINVAL;
18668c2ecf20Sopenharmony_ci			goto err_setup_mdio;
18678c2ecf20Sopenharmony_ci		}
18688c2ecf20Sopenharmony_ci
18698c2ecf20Sopenharmony_ci		/* Indicate that we support PAUSE frames (see comment in
18708c2ecf20Sopenharmony_ci		 * Documentation/networking/phy.rst)
18718c2ecf20Sopenharmony_ci		 */
18728c2ecf20Sopenharmony_ci		phy_support_asym_pause(phy);
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci		/* Display what we found */
18758c2ecf20Sopenharmony_ci		phy_attached_info(phy);
18768c2ecf20Sopenharmony_ci	} else if (np && !ftgmac100_has_child_node(np, "mdio")) {
18778c2ecf20Sopenharmony_ci		/* Support legacy ASPEED devicetree descriptions that decribe a
18788c2ecf20Sopenharmony_ci		 * MAC with an embedded MDIO controller but have no "mdio"
18798c2ecf20Sopenharmony_ci		 * child node. Automatically scan the MDIO bus for available
18808c2ecf20Sopenharmony_ci		 * PHYs.
18818c2ecf20Sopenharmony_ci		 */
18828c2ecf20Sopenharmony_ci		priv->use_ncsi = false;
18838c2ecf20Sopenharmony_ci		err = ftgmac100_setup_mdio(netdev);
18848c2ecf20Sopenharmony_ci		if (err)
18858c2ecf20Sopenharmony_ci			goto err_setup_mdio;
18868c2ecf20Sopenharmony_ci	}
18878c2ecf20Sopenharmony_ci
18888c2ecf20Sopenharmony_ci	if (priv->is_aspeed) {
18898c2ecf20Sopenharmony_ci		err = ftgmac100_setup_clk(priv);
18908c2ecf20Sopenharmony_ci		if (err)
18918c2ecf20Sopenharmony_ci			goto err_ncsi_dev;
18928c2ecf20Sopenharmony_ci	}
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_ci	/* Default ring sizes */
18958c2ecf20Sopenharmony_ci	priv->rx_q_entries = priv->new_rx_q_entries = DEF_RX_QUEUE_ENTRIES;
18968c2ecf20Sopenharmony_ci	priv->tx_q_entries = priv->new_tx_q_entries = DEF_TX_QUEUE_ENTRIES;
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci	/* Base feature set */
18998c2ecf20Sopenharmony_ci	netdev->hw_features = NETIF_F_RXCSUM | NETIF_F_HW_CSUM |
19008c2ecf20Sopenharmony_ci		NETIF_F_GRO | NETIF_F_SG | NETIF_F_HW_VLAN_CTAG_RX |
19018c2ecf20Sopenharmony_ci		NETIF_F_HW_VLAN_CTAG_TX;
19028c2ecf20Sopenharmony_ci
19038c2ecf20Sopenharmony_ci	if (priv->use_ncsi)
19048c2ecf20Sopenharmony_ci		netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER;
19058c2ecf20Sopenharmony_ci
19068c2ecf20Sopenharmony_ci	/* AST2400  doesn't have working HW checksum generation */
19078c2ecf20Sopenharmony_ci	if (np && (of_device_is_compatible(np, "aspeed,ast2400-mac")))
19088c2ecf20Sopenharmony_ci		netdev->hw_features &= ~NETIF_F_HW_CSUM;
19098c2ecf20Sopenharmony_ci
19108c2ecf20Sopenharmony_ci	/* AST2600 tx checksum with NCSI is broken */
19118c2ecf20Sopenharmony_ci	if (priv->use_ncsi && of_device_is_compatible(np, "aspeed,ast2600-mac"))
19128c2ecf20Sopenharmony_ci		netdev->hw_features &= ~NETIF_F_HW_CSUM;
19138c2ecf20Sopenharmony_ci
19148c2ecf20Sopenharmony_ci	if (np && of_get_property(np, "no-hw-checksum", NULL))
19158c2ecf20Sopenharmony_ci		netdev->hw_features &= ~(NETIF_F_HW_CSUM | NETIF_F_RXCSUM);
19168c2ecf20Sopenharmony_ci	netdev->features |= netdev->hw_features;
19178c2ecf20Sopenharmony_ci
19188c2ecf20Sopenharmony_ci	/* register network device */
19198c2ecf20Sopenharmony_ci	err = register_netdev(netdev);
19208c2ecf20Sopenharmony_ci	if (err) {
19218c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to register netdev\n");
19228c2ecf20Sopenharmony_ci		goto err_register_netdev;
19238c2ecf20Sopenharmony_ci	}
19248c2ecf20Sopenharmony_ci
19258c2ecf20Sopenharmony_ci	netdev_info(netdev, "irq %d, mapped at %p\n", netdev->irq, priv->base);
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci	return 0;
19288c2ecf20Sopenharmony_ci
19298c2ecf20Sopenharmony_cierr_register_netdev:
19308c2ecf20Sopenharmony_ci	clk_disable_unprepare(priv->rclk);
19318c2ecf20Sopenharmony_ci	clk_disable_unprepare(priv->clk);
19328c2ecf20Sopenharmony_cierr_ncsi_dev:
19338c2ecf20Sopenharmony_ci	if (priv->ndev)
19348c2ecf20Sopenharmony_ci		ncsi_unregister_dev(priv->ndev);
19358c2ecf20Sopenharmony_ci	ftgmac100_destroy_mdio(netdev);
19368c2ecf20Sopenharmony_cierr_setup_mdio:
19378c2ecf20Sopenharmony_ci	iounmap(priv->base);
19388c2ecf20Sopenharmony_cierr_ioremap:
19398c2ecf20Sopenharmony_ci	release_resource(priv->res);
19408c2ecf20Sopenharmony_cierr_req_mem:
19418c2ecf20Sopenharmony_ci	free_netdev(netdev);
19428c2ecf20Sopenharmony_cierr_alloc_etherdev:
19438c2ecf20Sopenharmony_ci	return err;
19448c2ecf20Sopenharmony_ci}
19458c2ecf20Sopenharmony_ci
19468c2ecf20Sopenharmony_cistatic int ftgmac100_remove(struct platform_device *pdev)
19478c2ecf20Sopenharmony_ci{
19488c2ecf20Sopenharmony_ci	struct net_device *netdev;
19498c2ecf20Sopenharmony_ci	struct ftgmac100 *priv;
19508c2ecf20Sopenharmony_ci
19518c2ecf20Sopenharmony_ci	netdev = platform_get_drvdata(pdev);
19528c2ecf20Sopenharmony_ci	priv = netdev_priv(netdev);
19538c2ecf20Sopenharmony_ci
19548c2ecf20Sopenharmony_ci	if (priv->ndev)
19558c2ecf20Sopenharmony_ci		ncsi_unregister_dev(priv->ndev);
19568c2ecf20Sopenharmony_ci	unregister_netdev(netdev);
19578c2ecf20Sopenharmony_ci
19588c2ecf20Sopenharmony_ci	clk_disable_unprepare(priv->rclk);
19598c2ecf20Sopenharmony_ci	clk_disable_unprepare(priv->clk);
19608c2ecf20Sopenharmony_ci
19618c2ecf20Sopenharmony_ci	/* There's a small chance the reset task will have been re-queued,
19628c2ecf20Sopenharmony_ci	 * during stop, make sure it's gone before we free the structure.
19638c2ecf20Sopenharmony_ci	 */
19648c2ecf20Sopenharmony_ci	cancel_work_sync(&priv->reset_task);
19658c2ecf20Sopenharmony_ci
19668c2ecf20Sopenharmony_ci	ftgmac100_destroy_mdio(netdev);
19678c2ecf20Sopenharmony_ci
19688c2ecf20Sopenharmony_ci	iounmap(priv->base);
19698c2ecf20Sopenharmony_ci	release_resource(priv->res);
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci	netif_napi_del(&priv->napi);
19728c2ecf20Sopenharmony_ci	free_netdev(netdev);
19738c2ecf20Sopenharmony_ci	return 0;
19748c2ecf20Sopenharmony_ci}
19758c2ecf20Sopenharmony_ci
19768c2ecf20Sopenharmony_cistatic const struct of_device_id ftgmac100_of_match[] = {
19778c2ecf20Sopenharmony_ci	{ .compatible = "faraday,ftgmac100" },
19788c2ecf20Sopenharmony_ci	{ }
19798c2ecf20Sopenharmony_ci};
19808c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ftgmac100_of_match);
19818c2ecf20Sopenharmony_ci
19828c2ecf20Sopenharmony_cistatic struct platform_driver ftgmac100_driver = {
19838c2ecf20Sopenharmony_ci	.probe	= ftgmac100_probe,
19848c2ecf20Sopenharmony_ci	.remove	= ftgmac100_remove,
19858c2ecf20Sopenharmony_ci	.driver	= {
19868c2ecf20Sopenharmony_ci		.name		= DRV_NAME,
19878c2ecf20Sopenharmony_ci		.of_match_table	= ftgmac100_of_match,
19888c2ecf20Sopenharmony_ci	},
19898c2ecf20Sopenharmony_ci};
19908c2ecf20Sopenharmony_cimodule_platform_driver(ftgmac100_driver);
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_ciMODULE_AUTHOR("Po-Yu Chuang <ratbert@faraday-tech.com>");
19938c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("FTGMAC100 driver");
19948c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1995