162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * EP93xx ethernet network device driver
462306a36Sopenharmony_ci * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
562306a36Sopenharmony_ci * Dedicated to Marija Kulikova.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/netdevice.h>
1462306a36Sopenharmony_ci#include <linux/mii.h>
1562306a36Sopenharmony_ci#include <linux/etherdevice.h>
1662306a36Sopenharmony_ci#include <linux/ethtool.h>
1762306a36Sopenharmony_ci#include <linux/interrupt.h>
1862306a36Sopenharmony_ci#include <linux/moduleparam.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <linux/delay.h>
2162306a36Sopenharmony_ci#include <linux/io.h>
2262306a36Sopenharmony_ci#include <linux/slab.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <linux/platform_data/eth-ep93xx.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define DRV_MODULE_NAME		"ep93xx-eth"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define RX_QUEUE_ENTRIES	64
2962306a36Sopenharmony_ci#define TX_QUEUE_ENTRIES	8
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define MAX_PKT_SIZE		2044
3262306a36Sopenharmony_ci#define PKT_BUF_SIZE		2048
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define REG_RXCTL		0x0000
3562306a36Sopenharmony_ci#define  REG_RXCTL_DEFAULT	0x00073800
3662306a36Sopenharmony_ci#define REG_TXCTL		0x0004
3762306a36Sopenharmony_ci#define  REG_TXCTL_ENABLE	0x00000001
3862306a36Sopenharmony_ci#define REG_MIICMD		0x0010
3962306a36Sopenharmony_ci#define  REG_MIICMD_READ	0x00008000
4062306a36Sopenharmony_ci#define  REG_MIICMD_WRITE	0x00004000
4162306a36Sopenharmony_ci#define REG_MIIDATA		0x0014
4262306a36Sopenharmony_ci#define REG_MIISTS		0x0018
4362306a36Sopenharmony_ci#define  REG_MIISTS_BUSY	0x00000001
4462306a36Sopenharmony_ci#define REG_SELFCTL		0x0020
4562306a36Sopenharmony_ci#define  REG_SELFCTL_RESET	0x00000001
4662306a36Sopenharmony_ci#define REG_INTEN		0x0024
4762306a36Sopenharmony_ci#define  REG_INTEN_TX		0x00000008
4862306a36Sopenharmony_ci#define  REG_INTEN_RX		0x00000007
4962306a36Sopenharmony_ci#define REG_INTSTSP		0x0028
5062306a36Sopenharmony_ci#define  REG_INTSTS_TX		0x00000008
5162306a36Sopenharmony_ci#define  REG_INTSTS_RX		0x00000004
5262306a36Sopenharmony_ci#define REG_INTSTSC		0x002c
5362306a36Sopenharmony_ci#define REG_AFP			0x004c
5462306a36Sopenharmony_ci#define REG_INDAD0		0x0050
5562306a36Sopenharmony_ci#define REG_INDAD1		0x0051
5662306a36Sopenharmony_ci#define REG_INDAD2		0x0052
5762306a36Sopenharmony_ci#define REG_INDAD3		0x0053
5862306a36Sopenharmony_ci#define REG_INDAD4		0x0054
5962306a36Sopenharmony_ci#define REG_INDAD5		0x0055
6062306a36Sopenharmony_ci#define REG_GIINTMSK		0x0064
6162306a36Sopenharmony_ci#define  REG_GIINTMSK_ENABLE	0x00008000
6262306a36Sopenharmony_ci#define REG_BMCTL		0x0080
6362306a36Sopenharmony_ci#define  REG_BMCTL_ENABLE_TX	0x00000100
6462306a36Sopenharmony_ci#define  REG_BMCTL_ENABLE_RX	0x00000001
6562306a36Sopenharmony_ci#define REG_BMSTS		0x0084
6662306a36Sopenharmony_ci#define  REG_BMSTS_RX_ACTIVE	0x00000008
6762306a36Sopenharmony_ci#define REG_RXDQBADD		0x0090
6862306a36Sopenharmony_ci#define REG_RXDQBLEN		0x0094
6962306a36Sopenharmony_ci#define REG_RXDCURADD		0x0098
7062306a36Sopenharmony_ci#define REG_RXDENQ		0x009c
7162306a36Sopenharmony_ci#define REG_RXSTSQBADD		0x00a0
7262306a36Sopenharmony_ci#define REG_RXSTSQBLEN		0x00a4
7362306a36Sopenharmony_ci#define REG_RXSTSQCURADD	0x00a8
7462306a36Sopenharmony_ci#define REG_RXSTSENQ		0x00ac
7562306a36Sopenharmony_ci#define REG_TXDQBADD		0x00b0
7662306a36Sopenharmony_ci#define REG_TXDQBLEN		0x00b4
7762306a36Sopenharmony_ci#define REG_TXDQCURADD		0x00b8
7862306a36Sopenharmony_ci#define REG_TXDENQ		0x00bc
7962306a36Sopenharmony_ci#define REG_TXSTSQBADD		0x00c0
8062306a36Sopenharmony_ci#define REG_TXSTSQBLEN		0x00c4
8162306a36Sopenharmony_ci#define REG_TXSTSQCURADD	0x00c8
8262306a36Sopenharmony_ci#define REG_MAXFRMLEN		0x00e8
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistruct ep93xx_rdesc
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	u32	buf_addr;
8762306a36Sopenharmony_ci	u32	rdesc1;
8862306a36Sopenharmony_ci};
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci#define RDESC1_NSOF		0x80000000
9162306a36Sopenharmony_ci#define RDESC1_BUFFER_INDEX	0x7fff0000
9262306a36Sopenharmony_ci#define RDESC1_BUFFER_LENGTH	0x0000ffff
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistruct ep93xx_rstat
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	u32	rstat0;
9762306a36Sopenharmony_ci	u32	rstat1;
9862306a36Sopenharmony_ci};
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci#define RSTAT0_RFP		0x80000000
10162306a36Sopenharmony_ci#define RSTAT0_RWE		0x40000000
10262306a36Sopenharmony_ci#define RSTAT0_EOF		0x20000000
10362306a36Sopenharmony_ci#define RSTAT0_EOB		0x10000000
10462306a36Sopenharmony_ci#define RSTAT0_AM		0x00c00000
10562306a36Sopenharmony_ci#define RSTAT0_RX_ERR		0x00200000
10662306a36Sopenharmony_ci#define RSTAT0_OE		0x00100000
10762306a36Sopenharmony_ci#define RSTAT0_FE		0x00080000
10862306a36Sopenharmony_ci#define RSTAT0_RUNT		0x00040000
10962306a36Sopenharmony_ci#define RSTAT0_EDATA		0x00020000
11062306a36Sopenharmony_ci#define RSTAT0_CRCE		0x00010000
11162306a36Sopenharmony_ci#define RSTAT0_CRCI		0x00008000
11262306a36Sopenharmony_ci#define RSTAT0_HTI		0x00003f00
11362306a36Sopenharmony_ci#define RSTAT1_RFP		0x80000000
11462306a36Sopenharmony_ci#define RSTAT1_BUFFER_INDEX	0x7fff0000
11562306a36Sopenharmony_ci#define RSTAT1_FRAME_LENGTH	0x0000ffff
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistruct ep93xx_tdesc
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	u32	buf_addr;
12062306a36Sopenharmony_ci	u32	tdesc1;
12162306a36Sopenharmony_ci};
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci#define TDESC1_EOF		0x80000000
12462306a36Sopenharmony_ci#define TDESC1_BUFFER_INDEX	0x7fff0000
12562306a36Sopenharmony_ci#define TDESC1_BUFFER_ABORT	0x00008000
12662306a36Sopenharmony_ci#define TDESC1_BUFFER_LENGTH	0x00000fff
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistruct ep93xx_tstat
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	u32	tstat0;
13162306a36Sopenharmony_ci};
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci#define TSTAT0_TXFP		0x80000000
13462306a36Sopenharmony_ci#define TSTAT0_TXWE		0x40000000
13562306a36Sopenharmony_ci#define TSTAT0_FA		0x20000000
13662306a36Sopenharmony_ci#define TSTAT0_LCRS		0x10000000
13762306a36Sopenharmony_ci#define TSTAT0_OW		0x04000000
13862306a36Sopenharmony_ci#define TSTAT0_TXU		0x02000000
13962306a36Sopenharmony_ci#define TSTAT0_ECOLL		0x01000000
14062306a36Sopenharmony_ci#define TSTAT0_NCOLL		0x001f0000
14162306a36Sopenharmony_ci#define TSTAT0_BUFFER_INDEX	0x00007fff
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistruct ep93xx_descs
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	struct ep93xx_rdesc	rdesc[RX_QUEUE_ENTRIES];
14662306a36Sopenharmony_ci	struct ep93xx_tdesc	tdesc[TX_QUEUE_ENTRIES];
14762306a36Sopenharmony_ci	struct ep93xx_rstat	rstat[RX_QUEUE_ENTRIES];
14862306a36Sopenharmony_ci	struct ep93xx_tstat	tstat[TX_QUEUE_ENTRIES];
14962306a36Sopenharmony_ci};
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistruct ep93xx_priv
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct resource		*res;
15462306a36Sopenharmony_ci	void __iomem		*base_addr;
15562306a36Sopenharmony_ci	int			irq;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	struct ep93xx_descs	*descs;
15862306a36Sopenharmony_ci	dma_addr_t		descs_dma_addr;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	void			*rx_buf[RX_QUEUE_ENTRIES];
16162306a36Sopenharmony_ci	void			*tx_buf[TX_QUEUE_ENTRIES];
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	spinlock_t		rx_lock;
16462306a36Sopenharmony_ci	unsigned int		rx_pointer;
16562306a36Sopenharmony_ci	unsigned int		tx_clean_pointer;
16662306a36Sopenharmony_ci	unsigned int		tx_pointer;
16762306a36Sopenharmony_ci	spinlock_t		tx_pending_lock;
16862306a36Sopenharmony_ci	unsigned int		tx_pending;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	struct net_device	*dev;
17162306a36Sopenharmony_ci	struct napi_struct	napi;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	struct mii_if_info	mii;
17462306a36Sopenharmony_ci	u8			mdc_divisor;
17562306a36Sopenharmony_ci};
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci#define rdb(ep, off)		__raw_readb((ep)->base_addr + (off))
17862306a36Sopenharmony_ci#define rdw(ep, off)		__raw_readw((ep)->base_addr + (off))
17962306a36Sopenharmony_ci#define rdl(ep, off)		__raw_readl((ep)->base_addr + (off))
18062306a36Sopenharmony_ci#define wrb(ep, off, val)	__raw_writeb((val), (ep)->base_addr + (off))
18162306a36Sopenharmony_ci#define wrw(ep, off, val)	__raw_writew((val), (ep)->base_addr + (off))
18262306a36Sopenharmony_ci#define wrl(ep, off, val)	__raw_writel((val), (ep)->base_addr + (off))
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic int ep93xx_mdio_read(struct net_device *dev, int phy_id, int reg)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct ep93xx_priv *ep = netdev_priv(dev);
18762306a36Sopenharmony_ci	int data;
18862306a36Sopenharmony_ci	int i;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	wrl(ep, REG_MIICMD, REG_MIICMD_READ | (phy_id << 5) | reg);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	for (i = 0; i < 10; i++) {
19362306a36Sopenharmony_ci		if ((rdl(ep, REG_MIISTS) & REG_MIISTS_BUSY) == 0)
19462306a36Sopenharmony_ci			break;
19562306a36Sopenharmony_ci		msleep(1);
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	if (i == 10) {
19962306a36Sopenharmony_ci		pr_info("mdio read timed out\n");
20062306a36Sopenharmony_ci		data = 0xffff;
20162306a36Sopenharmony_ci	} else {
20262306a36Sopenharmony_ci		data = rdl(ep, REG_MIIDATA);
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return data;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic void ep93xx_mdio_write(struct net_device *dev, int phy_id, int reg, int data)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	struct ep93xx_priv *ep = netdev_priv(dev);
21162306a36Sopenharmony_ci	int i;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	wrl(ep, REG_MIIDATA, data);
21462306a36Sopenharmony_ci	wrl(ep, REG_MIICMD, REG_MIICMD_WRITE | (phy_id << 5) | reg);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	for (i = 0; i < 10; i++) {
21762306a36Sopenharmony_ci		if ((rdl(ep, REG_MIISTS) & REG_MIISTS_BUSY) == 0)
21862306a36Sopenharmony_ci			break;
21962306a36Sopenharmony_ci		msleep(1);
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	if (i == 10)
22362306a36Sopenharmony_ci		pr_info("mdio write timed out\n");
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic int ep93xx_rx(struct net_device *dev, int budget)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	struct ep93xx_priv *ep = netdev_priv(dev);
22962306a36Sopenharmony_ci	int processed = 0;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	while (processed < budget) {
23262306a36Sopenharmony_ci		int entry;
23362306a36Sopenharmony_ci		struct ep93xx_rstat *rstat;
23462306a36Sopenharmony_ci		u32 rstat0;
23562306a36Sopenharmony_ci		u32 rstat1;
23662306a36Sopenharmony_ci		int length;
23762306a36Sopenharmony_ci		struct sk_buff *skb;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci		entry = ep->rx_pointer;
24062306a36Sopenharmony_ci		rstat = ep->descs->rstat + entry;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci		rstat0 = rstat->rstat0;
24362306a36Sopenharmony_ci		rstat1 = rstat->rstat1;
24462306a36Sopenharmony_ci		if (!(rstat0 & RSTAT0_RFP) || !(rstat1 & RSTAT1_RFP))
24562306a36Sopenharmony_ci			break;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci		rstat->rstat0 = 0;
24862306a36Sopenharmony_ci		rstat->rstat1 = 0;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci		if (!(rstat0 & RSTAT0_EOF))
25162306a36Sopenharmony_ci			pr_crit("not end-of-frame %.8x %.8x\n", rstat0, rstat1);
25262306a36Sopenharmony_ci		if (!(rstat0 & RSTAT0_EOB))
25362306a36Sopenharmony_ci			pr_crit("not end-of-buffer %.8x %.8x\n", rstat0, rstat1);
25462306a36Sopenharmony_ci		if ((rstat1 & RSTAT1_BUFFER_INDEX) >> 16 != entry)
25562306a36Sopenharmony_ci			pr_crit("entry mismatch %.8x %.8x\n", rstat0, rstat1);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		if (!(rstat0 & RSTAT0_RWE)) {
25862306a36Sopenharmony_ci			dev->stats.rx_errors++;
25962306a36Sopenharmony_ci			if (rstat0 & RSTAT0_OE)
26062306a36Sopenharmony_ci				dev->stats.rx_fifo_errors++;
26162306a36Sopenharmony_ci			if (rstat0 & RSTAT0_FE)
26262306a36Sopenharmony_ci				dev->stats.rx_frame_errors++;
26362306a36Sopenharmony_ci			if (rstat0 & (RSTAT0_RUNT | RSTAT0_EDATA))
26462306a36Sopenharmony_ci				dev->stats.rx_length_errors++;
26562306a36Sopenharmony_ci			if (rstat0 & RSTAT0_CRCE)
26662306a36Sopenharmony_ci				dev->stats.rx_crc_errors++;
26762306a36Sopenharmony_ci			goto err;
26862306a36Sopenharmony_ci		}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		length = rstat1 & RSTAT1_FRAME_LENGTH;
27162306a36Sopenharmony_ci		if (length > MAX_PKT_SIZE) {
27262306a36Sopenharmony_ci			pr_notice("invalid length %.8x %.8x\n", rstat0, rstat1);
27362306a36Sopenharmony_ci			goto err;
27462306a36Sopenharmony_ci		}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci		/* Strip FCS.  */
27762306a36Sopenharmony_ci		if (rstat0 & RSTAT0_CRCI)
27862306a36Sopenharmony_ci			length -= 4;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci		skb = netdev_alloc_skb(dev, length + 2);
28162306a36Sopenharmony_ci		if (likely(skb != NULL)) {
28262306a36Sopenharmony_ci			struct ep93xx_rdesc *rxd = &ep->descs->rdesc[entry];
28362306a36Sopenharmony_ci			skb_reserve(skb, 2);
28462306a36Sopenharmony_ci			dma_sync_single_for_cpu(dev->dev.parent, rxd->buf_addr,
28562306a36Sopenharmony_ci						length, DMA_FROM_DEVICE);
28662306a36Sopenharmony_ci			skb_copy_to_linear_data(skb, ep->rx_buf[entry], length);
28762306a36Sopenharmony_ci			dma_sync_single_for_device(dev->dev.parent,
28862306a36Sopenharmony_ci						   rxd->buf_addr, length,
28962306a36Sopenharmony_ci						   DMA_FROM_DEVICE);
29062306a36Sopenharmony_ci			skb_put(skb, length);
29162306a36Sopenharmony_ci			skb->protocol = eth_type_trans(skb, dev);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci			napi_gro_receive(&ep->napi, skb);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci			dev->stats.rx_packets++;
29662306a36Sopenharmony_ci			dev->stats.rx_bytes += length;
29762306a36Sopenharmony_ci		} else {
29862306a36Sopenharmony_ci			dev->stats.rx_dropped++;
29962306a36Sopenharmony_ci		}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cierr:
30262306a36Sopenharmony_ci		ep->rx_pointer = (entry + 1) & (RX_QUEUE_ENTRIES - 1);
30362306a36Sopenharmony_ci		processed++;
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	return processed;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic int ep93xx_poll(struct napi_struct *napi, int budget)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	struct ep93xx_priv *ep = container_of(napi, struct ep93xx_priv, napi);
31262306a36Sopenharmony_ci	struct net_device *dev = ep->dev;
31362306a36Sopenharmony_ci	int rx;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	rx = ep93xx_rx(dev, budget);
31662306a36Sopenharmony_ci	if (rx < budget && napi_complete_done(napi, rx)) {
31762306a36Sopenharmony_ci		spin_lock_irq(&ep->rx_lock);
31862306a36Sopenharmony_ci		wrl(ep, REG_INTEN, REG_INTEN_TX | REG_INTEN_RX);
31962306a36Sopenharmony_ci		spin_unlock_irq(&ep->rx_lock);
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	if (rx) {
32362306a36Sopenharmony_ci		wrw(ep, REG_RXDENQ, rx);
32462306a36Sopenharmony_ci		wrw(ep, REG_RXSTSENQ, rx);
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	return rx;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic netdev_tx_t ep93xx_xmit(struct sk_buff *skb, struct net_device *dev)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct ep93xx_priv *ep = netdev_priv(dev);
33362306a36Sopenharmony_ci	struct ep93xx_tdesc *txd;
33462306a36Sopenharmony_ci	int entry;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (unlikely(skb->len > MAX_PKT_SIZE)) {
33762306a36Sopenharmony_ci		dev->stats.tx_dropped++;
33862306a36Sopenharmony_ci		dev_kfree_skb(skb);
33962306a36Sopenharmony_ci		return NETDEV_TX_OK;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	entry = ep->tx_pointer;
34362306a36Sopenharmony_ci	ep->tx_pointer = (ep->tx_pointer + 1) & (TX_QUEUE_ENTRIES - 1);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	txd = &ep->descs->tdesc[entry];
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	txd->tdesc1 = TDESC1_EOF | (entry << 16) | (skb->len & 0xfff);
34862306a36Sopenharmony_ci	dma_sync_single_for_cpu(dev->dev.parent, txd->buf_addr, skb->len,
34962306a36Sopenharmony_ci				DMA_TO_DEVICE);
35062306a36Sopenharmony_ci	skb_copy_and_csum_dev(skb, ep->tx_buf[entry]);
35162306a36Sopenharmony_ci	dma_sync_single_for_device(dev->dev.parent, txd->buf_addr, skb->len,
35262306a36Sopenharmony_ci				   DMA_TO_DEVICE);
35362306a36Sopenharmony_ci	dev_kfree_skb(skb);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	spin_lock_irq(&ep->tx_pending_lock);
35662306a36Sopenharmony_ci	ep->tx_pending++;
35762306a36Sopenharmony_ci	if (ep->tx_pending == TX_QUEUE_ENTRIES)
35862306a36Sopenharmony_ci		netif_stop_queue(dev);
35962306a36Sopenharmony_ci	spin_unlock_irq(&ep->tx_pending_lock);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	wrl(ep, REG_TXDENQ, 1);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	return NETDEV_TX_OK;
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic void ep93xx_tx_complete(struct net_device *dev)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	struct ep93xx_priv *ep = netdev_priv(dev);
36962306a36Sopenharmony_ci	int wake;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	wake = 0;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	spin_lock(&ep->tx_pending_lock);
37462306a36Sopenharmony_ci	while (1) {
37562306a36Sopenharmony_ci		int entry;
37662306a36Sopenharmony_ci		struct ep93xx_tstat *tstat;
37762306a36Sopenharmony_ci		u32 tstat0;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci		entry = ep->tx_clean_pointer;
38062306a36Sopenharmony_ci		tstat = ep->descs->tstat + entry;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci		tstat0 = tstat->tstat0;
38362306a36Sopenharmony_ci		if (!(tstat0 & TSTAT0_TXFP))
38462306a36Sopenharmony_ci			break;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		tstat->tstat0 = 0;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci		if (tstat0 & TSTAT0_FA)
38962306a36Sopenharmony_ci			pr_crit("frame aborted %.8x\n", tstat0);
39062306a36Sopenharmony_ci		if ((tstat0 & TSTAT0_BUFFER_INDEX) != entry)
39162306a36Sopenharmony_ci			pr_crit("entry mismatch %.8x\n", tstat0);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		if (tstat0 & TSTAT0_TXWE) {
39462306a36Sopenharmony_ci			int length = ep->descs->tdesc[entry].tdesc1 & 0xfff;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci			dev->stats.tx_packets++;
39762306a36Sopenharmony_ci			dev->stats.tx_bytes += length;
39862306a36Sopenharmony_ci		} else {
39962306a36Sopenharmony_ci			dev->stats.tx_errors++;
40062306a36Sopenharmony_ci		}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci		if (tstat0 & TSTAT0_OW)
40362306a36Sopenharmony_ci			dev->stats.tx_window_errors++;
40462306a36Sopenharmony_ci		if (tstat0 & TSTAT0_TXU)
40562306a36Sopenharmony_ci			dev->stats.tx_fifo_errors++;
40662306a36Sopenharmony_ci		dev->stats.collisions += (tstat0 >> 16) & 0x1f;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		ep->tx_clean_pointer = (entry + 1) & (TX_QUEUE_ENTRIES - 1);
40962306a36Sopenharmony_ci		if (ep->tx_pending == TX_QUEUE_ENTRIES)
41062306a36Sopenharmony_ci			wake = 1;
41162306a36Sopenharmony_ci		ep->tx_pending--;
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci	spin_unlock(&ep->tx_pending_lock);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	if (wake)
41662306a36Sopenharmony_ci		netif_wake_queue(dev);
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistatic irqreturn_t ep93xx_irq(int irq, void *dev_id)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	struct net_device *dev = dev_id;
42262306a36Sopenharmony_ci	struct ep93xx_priv *ep = netdev_priv(dev);
42362306a36Sopenharmony_ci	u32 status;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	status = rdl(ep, REG_INTSTSC);
42662306a36Sopenharmony_ci	if (status == 0)
42762306a36Sopenharmony_ci		return IRQ_NONE;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (status & REG_INTSTS_RX) {
43062306a36Sopenharmony_ci		spin_lock(&ep->rx_lock);
43162306a36Sopenharmony_ci		if (likely(napi_schedule_prep(&ep->napi))) {
43262306a36Sopenharmony_ci			wrl(ep, REG_INTEN, REG_INTEN_TX);
43362306a36Sopenharmony_ci			__napi_schedule(&ep->napi);
43462306a36Sopenharmony_ci		}
43562306a36Sopenharmony_ci		spin_unlock(&ep->rx_lock);
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	if (status & REG_INTSTS_TX)
43962306a36Sopenharmony_ci		ep93xx_tx_complete(dev);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	return IRQ_HANDLED;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic void ep93xx_free_buffers(struct ep93xx_priv *ep)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct device *dev = ep->dev->dev.parent;
44762306a36Sopenharmony_ci	int i;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	if (!ep->descs)
45062306a36Sopenharmony_ci		return;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
45362306a36Sopenharmony_ci		dma_addr_t d;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci		d = ep->descs->rdesc[i].buf_addr;
45662306a36Sopenharmony_ci		if (d)
45762306a36Sopenharmony_ci			dma_unmap_single(dev, d, PKT_BUF_SIZE, DMA_FROM_DEVICE);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci		kfree(ep->rx_buf[i]);
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	for (i = 0; i < TX_QUEUE_ENTRIES; i++) {
46362306a36Sopenharmony_ci		dma_addr_t d;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		d = ep->descs->tdesc[i].buf_addr;
46662306a36Sopenharmony_ci		if (d)
46762306a36Sopenharmony_ci			dma_unmap_single(dev, d, PKT_BUF_SIZE, DMA_TO_DEVICE);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci		kfree(ep->tx_buf[i]);
47062306a36Sopenharmony_ci	}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	dma_free_coherent(dev, sizeof(struct ep93xx_descs), ep->descs,
47362306a36Sopenharmony_ci							ep->descs_dma_addr);
47462306a36Sopenharmony_ci	ep->descs = NULL;
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic int ep93xx_alloc_buffers(struct ep93xx_priv *ep)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	struct device *dev = ep->dev->dev.parent;
48062306a36Sopenharmony_ci	int i;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	ep->descs = dma_alloc_coherent(dev, sizeof(struct ep93xx_descs),
48362306a36Sopenharmony_ci				&ep->descs_dma_addr, GFP_KERNEL);
48462306a36Sopenharmony_ci	if (ep->descs == NULL)
48562306a36Sopenharmony_ci		return 1;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
48862306a36Sopenharmony_ci		void *buf;
48962306a36Sopenharmony_ci		dma_addr_t d;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci		buf = kmalloc(PKT_BUF_SIZE, GFP_KERNEL);
49262306a36Sopenharmony_ci		if (buf == NULL)
49362306a36Sopenharmony_ci			goto err;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci		d = dma_map_single(dev, buf, PKT_BUF_SIZE, DMA_FROM_DEVICE);
49662306a36Sopenharmony_ci		if (dma_mapping_error(dev, d)) {
49762306a36Sopenharmony_ci			kfree(buf);
49862306a36Sopenharmony_ci			goto err;
49962306a36Sopenharmony_ci		}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci		ep->rx_buf[i] = buf;
50262306a36Sopenharmony_ci		ep->descs->rdesc[i].buf_addr = d;
50362306a36Sopenharmony_ci		ep->descs->rdesc[i].rdesc1 = (i << 16) | PKT_BUF_SIZE;
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	for (i = 0; i < TX_QUEUE_ENTRIES; i++) {
50762306a36Sopenharmony_ci		void *buf;
50862306a36Sopenharmony_ci		dma_addr_t d;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci		buf = kmalloc(PKT_BUF_SIZE, GFP_KERNEL);
51162306a36Sopenharmony_ci		if (buf == NULL)
51262306a36Sopenharmony_ci			goto err;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci		d = dma_map_single(dev, buf, PKT_BUF_SIZE, DMA_TO_DEVICE);
51562306a36Sopenharmony_ci		if (dma_mapping_error(dev, d)) {
51662306a36Sopenharmony_ci			kfree(buf);
51762306a36Sopenharmony_ci			goto err;
51862306a36Sopenharmony_ci		}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		ep->tx_buf[i] = buf;
52162306a36Sopenharmony_ci		ep->descs->tdesc[i].buf_addr = d;
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	return 0;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cierr:
52762306a36Sopenharmony_ci	ep93xx_free_buffers(ep);
52862306a36Sopenharmony_ci	return 1;
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic int ep93xx_start_hw(struct net_device *dev)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	struct ep93xx_priv *ep = netdev_priv(dev);
53462306a36Sopenharmony_ci	unsigned long addr;
53562306a36Sopenharmony_ci	int i;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	wrl(ep, REG_SELFCTL, REG_SELFCTL_RESET);
53862306a36Sopenharmony_ci	for (i = 0; i < 10; i++) {
53962306a36Sopenharmony_ci		if ((rdl(ep, REG_SELFCTL) & REG_SELFCTL_RESET) == 0)
54062306a36Sopenharmony_ci			break;
54162306a36Sopenharmony_ci		msleep(1);
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (i == 10) {
54562306a36Sopenharmony_ci		pr_crit("hw failed to reset\n");
54662306a36Sopenharmony_ci		return 1;
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	wrl(ep, REG_SELFCTL, ((ep->mdc_divisor - 1) << 9));
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	/* Does the PHY support preamble suppress?  */
55262306a36Sopenharmony_ci	if ((ep93xx_mdio_read(dev, ep->mii.phy_id, MII_BMSR) & 0x0040) != 0)
55362306a36Sopenharmony_ci		wrl(ep, REG_SELFCTL, ((ep->mdc_divisor - 1) << 9) | (1 << 8));
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	/* Receive descriptor ring.  */
55662306a36Sopenharmony_ci	addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, rdesc);
55762306a36Sopenharmony_ci	wrl(ep, REG_RXDQBADD, addr);
55862306a36Sopenharmony_ci	wrl(ep, REG_RXDCURADD, addr);
55962306a36Sopenharmony_ci	wrw(ep, REG_RXDQBLEN, RX_QUEUE_ENTRIES * sizeof(struct ep93xx_rdesc));
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	/* Receive status ring.  */
56262306a36Sopenharmony_ci	addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, rstat);
56362306a36Sopenharmony_ci	wrl(ep, REG_RXSTSQBADD, addr);
56462306a36Sopenharmony_ci	wrl(ep, REG_RXSTSQCURADD, addr);
56562306a36Sopenharmony_ci	wrw(ep, REG_RXSTSQBLEN, RX_QUEUE_ENTRIES * sizeof(struct ep93xx_rstat));
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	/* Transmit descriptor ring.  */
56862306a36Sopenharmony_ci	addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, tdesc);
56962306a36Sopenharmony_ci	wrl(ep, REG_TXDQBADD, addr);
57062306a36Sopenharmony_ci	wrl(ep, REG_TXDQCURADD, addr);
57162306a36Sopenharmony_ci	wrw(ep, REG_TXDQBLEN, TX_QUEUE_ENTRIES * sizeof(struct ep93xx_tdesc));
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	/* Transmit status ring.  */
57462306a36Sopenharmony_ci	addr = ep->descs_dma_addr + offsetof(struct ep93xx_descs, tstat);
57562306a36Sopenharmony_ci	wrl(ep, REG_TXSTSQBADD, addr);
57662306a36Sopenharmony_ci	wrl(ep, REG_TXSTSQCURADD, addr);
57762306a36Sopenharmony_ci	wrw(ep, REG_TXSTSQBLEN, TX_QUEUE_ENTRIES * sizeof(struct ep93xx_tstat));
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	wrl(ep, REG_BMCTL, REG_BMCTL_ENABLE_TX | REG_BMCTL_ENABLE_RX);
58062306a36Sopenharmony_ci	wrl(ep, REG_INTEN, REG_INTEN_TX | REG_INTEN_RX);
58162306a36Sopenharmony_ci	wrl(ep, REG_GIINTMSK, 0);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	for (i = 0; i < 10; i++) {
58462306a36Sopenharmony_ci		if ((rdl(ep, REG_BMSTS) & REG_BMSTS_RX_ACTIVE) != 0)
58562306a36Sopenharmony_ci			break;
58662306a36Sopenharmony_ci		msleep(1);
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	if (i == 10) {
59062306a36Sopenharmony_ci		pr_crit("hw failed to start\n");
59162306a36Sopenharmony_ci		return 1;
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	wrl(ep, REG_RXDENQ, RX_QUEUE_ENTRIES);
59562306a36Sopenharmony_ci	wrl(ep, REG_RXSTSENQ, RX_QUEUE_ENTRIES);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	wrb(ep, REG_INDAD0, dev->dev_addr[0]);
59862306a36Sopenharmony_ci	wrb(ep, REG_INDAD1, dev->dev_addr[1]);
59962306a36Sopenharmony_ci	wrb(ep, REG_INDAD2, dev->dev_addr[2]);
60062306a36Sopenharmony_ci	wrb(ep, REG_INDAD3, dev->dev_addr[3]);
60162306a36Sopenharmony_ci	wrb(ep, REG_INDAD4, dev->dev_addr[4]);
60262306a36Sopenharmony_ci	wrb(ep, REG_INDAD5, dev->dev_addr[5]);
60362306a36Sopenharmony_ci	wrl(ep, REG_AFP, 0);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	wrl(ep, REG_MAXFRMLEN, (MAX_PKT_SIZE << 16) | MAX_PKT_SIZE);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	wrl(ep, REG_RXCTL, REG_RXCTL_DEFAULT);
60862306a36Sopenharmony_ci	wrl(ep, REG_TXCTL, REG_TXCTL_ENABLE);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	return 0;
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cistatic void ep93xx_stop_hw(struct net_device *dev)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	struct ep93xx_priv *ep = netdev_priv(dev);
61662306a36Sopenharmony_ci	int i;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	wrl(ep, REG_SELFCTL, REG_SELFCTL_RESET);
61962306a36Sopenharmony_ci	for (i = 0; i < 10; i++) {
62062306a36Sopenharmony_ci		if ((rdl(ep, REG_SELFCTL) & REG_SELFCTL_RESET) == 0)
62162306a36Sopenharmony_ci			break;
62262306a36Sopenharmony_ci		msleep(1);
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	if (i == 10)
62662306a36Sopenharmony_ci		pr_crit("hw failed to reset\n");
62762306a36Sopenharmony_ci}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_cistatic int ep93xx_open(struct net_device *dev)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	struct ep93xx_priv *ep = netdev_priv(dev);
63262306a36Sopenharmony_ci	int err;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	if (ep93xx_alloc_buffers(ep))
63562306a36Sopenharmony_ci		return -ENOMEM;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	napi_enable(&ep->napi);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	if (ep93xx_start_hw(dev)) {
64062306a36Sopenharmony_ci		napi_disable(&ep->napi);
64162306a36Sopenharmony_ci		ep93xx_free_buffers(ep);
64262306a36Sopenharmony_ci		return -EIO;
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	spin_lock_init(&ep->rx_lock);
64662306a36Sopenharmony_ci	ep->rx_pointer = 0;
64762306a36Sopenharmony_ci	ep->tx_clean_pointer = 0;
64862306a36Sopenharmony_ci	ep->tx_pointer = 0;
64962306a36Sopenharmony_ci	spin_lock_init(&ep->tx_pending_lock);
65062306a36Sopenharmony_ci	ep->tx_pending = 0;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	err = request_irq(ep->irq, ep93xx_irq, IRQF_SHARED, dev->name, dev);
65362306a36Sopenharmony_ci	if (err) {
65462306a36Sopenharmony_ci		napi_disable(&ep->napi);
65562306a36Sopenharmony_ci		ep93xx_stop_hw(dev);
65662306a36Sopenharmony_ci		ep93xx_free_buffers(ep);
65762306a36Sopenharmony_ci		return err;
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	wrl(ep, REG_GIINTMSK, REG_GIINTMSK_ENABLE);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	netif_start_queue(dev);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	return 0;
66562306a36Sopenharmony_ci}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_cistatic int ep93xx_close(struct net_device *dev)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	struct ep93xx_priv *ep = netdev_priv(dev);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	napi_disable(&ep->napi);
67262306a36Sopenharmony_ci	netif_stop_queue(dev);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	wrl(ep, REG_GIINTMSK, 0);
67562306a36Sopenharmony_ci	free_irq(ep->irq, dev);
67662306a36Sopenharmony_ci	ep93xx_stop_hw(dev);
67762306a36Sopenharmony_ci	ep93xx_free_buffers(ep);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	return 0;
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic int ep93xx_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
68362306a36Sopenharmony_ci{
68462306a36Sopenharmony_ci	struct ep93xx_priv *ep = netdev_priv(dev);
68562306a36Sopenharmony_ci	struct mii_ioctl_data *data = if_mii(ifr);
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	return generic_mii_ioctl(&ep->mii, data, cmd, NULL);
68862306a36Sopenharmony_ci}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_cistatic void ep93xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
69162306a36Sopenharmony_ci{
69262306a36Sopenharmony_ci	strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver));
69362306a36Sopenharmony_ci}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_cistatic int ep93xx_get_link_ksettings(struct net_device *dev,
69662306a36Sopenharmony_ci				     struct ethtool_link_ksettings *cmd)
69762306a36Sopenharmony_ci{
69862306a36Sopenharmony_ci	struct ep93xx_priv *ep = netdev_priv(dev);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	mii_ethtool_get_link_ksettings(&ep->mii, cmd);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	return 0;
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic int ep93xx_set_link_ksettings(struct net_device *dev,
70662306a36Sopenharmony_ci				     const struct ethtool_link_ksettings *cmd)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	struct ep93xx_priv *ep = netdev_priv(dev);
70962306a36Sopenharmony_ci	return mii_ethtool_set_link_ksettings(&ep->mii, cmd);
71062306a36Sopenharmony_ci}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistatic int ep93xx_nway_reset(struct net_device *dev)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	struct ep93xx_priv *ep = netdev_priv(dev);
71562306a36Sopenharmony_ci	return mii_nway_restart(&ep->mii);
71662306a36Sopenharmony_ci}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_cistatic u32 ep93xx_get_link(struct net_device *dev)
71962306a36Sopenharmony_ci{
72062306a36Sopenharmony_ci	struct ep93xx_priv *ep = netdev_priv(dev);
72162306a36Sopenharmony_ci	return mii_link_ok(&ep->mii);
72262306a36Sopenharmony_ci}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_cistatic const struct ethtool_ops ep93xx_ethtool_ops = {
72562306a36Sopenharmony_ci	.get_drvinfo		= ep93xx_get_drvinfo,
72662306a36Sopenharmony_ci	.nway_reset		= ep93xx_nway_reset,
72762306a36Sopenharmony_ci	.get_link		= ep93xx_get_link,
72862306a36Sopenharmony_ci	.get_link_ksettings	= ep93xx_get_link_ksettings,
72962306a36Sopenharmony_ci	.set_link_ksettings	= ep93xx_set_link_ksettings,
73062306a36Sopenharmony_ci};
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_cistatic const struct net_device_ops ep93xx_netdev_ops = {
73362306a36Sopenharmony_ci	.ndo_open		= ep93xx_open,
73462306a36Sopenharmony_ci	.ndo_stop		= ep93xx_close,
73562306a36Sopenharmony_ci	.ndo_start_xmit		= ep93xx_xmit,
73662306a36Sopenharmony_ci	.ndo_eth_ioctl		= ep93xx_ioctl,
73762306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
73862306a36Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
73962306a36Sopenharmony_ci};
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_cistatic struct net_device *ep93xx_dev_alloc(struct ep93xx_eth_data *data)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	struct net_device *dev;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof(struct ep93xx_priv));
74662306a36Sopenharmony_ci	if (dev == NULL)
74762306a36Sopenharmony_ci		return NULL;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	eth_hw_addr_set(dev, data->dev_addr);
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	dev->ethtool_ops = &ep93xx_ethtool_ops;
75262306a36Sopenharmony_ci	dev->netdev_ops = &ep93xx_netdev_ops;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	return dev;
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_cistatic int ep93xx_eth_remove(struct platform_device *pdev)
76162306a36Sopenharmony_ci{
76262306a36Sopenharmony_ci	struct net_device *dev;
76362306a36Sopenharmony_ci	struct ep93xx_priv *ep;
76462306a36Sopenharmony_ci	struct resource *mem;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	dev = platform_get_drvdata(pdev);
76762306a36Sopenharmony_ci	if (dev == NULL)
76862306a36Sopenharmony_ci		return 0;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	ep = netdev_priv(dev);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	/* @@@ Force down.  */
77362306a36Sopenharmony_ci	unregister_netdev(dev);
77462306a36Sopenharmony_ci	ep93xx_free_buffers(ep);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	if (ep->base_addr != NULL)
77762306a36Sopenharmony_ci		iounmap(ep->base_addr);
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	if (ep->res != NULL) {
78062306a36Sopenharmony_ci		mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
78162306a36Sopenharmony_ci		release_mem_region(mem->start, resource_size(mem));
78262306a36Sopenharmony_ci	}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	free_netdev(dev);
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	return 0;
78762306a36Sopenharmony_ci}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_cistatic int ep93xx_eth_probe(struct platform_device *pdev)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	struct ep93xx_eth_data *data;
79262306a36Sopenharmony_ci	struct net_device *dev;
79362306a36Sopenharmony_ci	struct ep93xx_priv *ep;
79462306a36Sopenharmony_ci	struct resource *mem;
79562306a36Sopenharmony_ci	int irq;
79662306a36Sopenharmony_ci	int err;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	if (pdev == NULL)
79962306a36Sopenharmony_ci		return -ENODEV;
80062306a36Sopenharmony_ci	data = dev_get_platdata(&pdev->dev);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
80362306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
80462306a36Sopenharmony_ci	if (!mem || irq < 0)
80562306a36Sopenharmony_ci		return -ENXIO;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	dev = ep93xx_dev_alloc(data);
80862306a36Sopenharmony_ci	if (dev == NULL) {
80962306a36Sopenharmony_ci		err = -ENOMEM;
81062306a36Sopenharmony_ci		goto err_out;
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci	ep = netdev_priv(dev);
81362306a36Sopenharmony_ci	ep->dev = dev;
81462306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, &pdev->dev);
81562306a36Sopenharmony_ci	netif_napi_add(dev, &ep->napi, ep93xx_poll);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	platform_set_drvdata(pdev, dev);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	ep->res = request_mem_region(mem->start, resource_size(mem),
82062306a36Sopenharmony_ci				     dev_name(&pdev->dev));
82162306a36Sopenharmony_ci	if (ep->res == NULL) {
82262306a36Sopenharmony_ci		dev_err(&pdev->dev, "Could not reserve memory region\n");
82362306a36Sopenharmony_ci		err = -ENOMEM;
82462306a36Sopenharmony_ci		goto err_out;
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	ep->base_addr = ioremap(mem->start, resource_size(mem));
82862306a36Sopenharmony_ci	if (ep->base_addr == NULL) {
82962306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to ioremap ethernet registers\n");
83062306a36Sopenharmony_ci		err = -EIO;
83162306a36Sopenharmony_ci		goto err_out;
83262306a36Sopenharmony_ci	}
83362306a36Sopenharmony_ci	ep->irq = irq;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	ep->mii.phy_id = data->phy_id;
83662306a36Sopenharmony_ci	ep->mii.phy_id_mask = 0x1f;
83762306a36Sopenharmony_ci	ep->mii.reg_num_mask = 0x1f;
83862306a36Sopenharmony_ci	ep->mii.dev = dev;
83962306a36Sopenharmony_ci	ep->mii.mdio_read = ep93xx_mdio_read;
84062306a36Sopenharmony_ci	ep->mii.mdio_write = ep93xx_mdio_write;
84162306a36Sopenharmony_ci	ep->mdc_divisor = 40;	/* Max HCLK 100 MHz, min MDIO clk 2.5 MHz.  */
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	if (is_zero_ether_addr(dev->dev_addr))
84462306a36Sopenharmony_ci		eth_hw_addr_random(dev);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	err = register_netdev(dev);
84762306a36Sopenharmony_ci	if (err) {
84862306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to register netdev\n");
84962306a36Sopenharmony_ci		goto err_out;
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	printk(KERN_INFO "%s: ep93xx on-chip ethernet, IRQ %d, %pM\n",
85362306a36Sopenharmony_ci			dev->name, ep->irq, dev->dev_addr);
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	return 0;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_cierr_out:
85862306a36Sopenharmony_ci	ep93xx_eth_remove(pdev);
85962306a36Sopenharmony_ci	return err;
86062306a36Sopenharmony_ci}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_cistatic struct platform_driver ep93xx_eth_driver = {
86462306a36Sopenharmony_ci	.probe		= ep93xx_eth_probe,
86562306a36Sopenharmony_ci	.remove		= ep93xx_eth_remove,
86662306a36Sopenharmony_ci	.driver		= {
86762306a36Sopenharmony_ci		.name	= "ep93xx-eth",
86862306a36Sopenharmony_ci	},
86962306a36Sopenharmony_ci};
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_cimodule_platform_driver(ep93xx_eth_driver);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
87462306a36Sopenharmony_ciMODULE_ALIAS("platform:ep93xx-eth");
875