162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Ethernet driver for the WIZnet W5100 chip.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2006-2008 WIZnet Co.,Ltd.
662306a36Sopenharmony_ci * Copyright (C) 2012 Mike Sinkovsky <msink@permonline.ru>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/netdevice.h>
1262306a36Sopenharmony_ci#include <linux/etherdevice.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci#include <linux/platform_data/wiznet.h>
1562306a36Sopenharmony_ci#include <linux/ethtool.h>
1662306a36Sopenharmony_ci#include <linux/skbuff.h>
1762306a36Sopenharmony_ci#include <linux/types.h>
1862306a36Sopenharmony_ci#include <linux/errno.h>
1962306a36Sopenharmony_ci#include <linux/delay.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci#include <linux/spinlock.h>
2262306a36Sopenharmony_ci#include <linux/io.h>
2362306a36Sopenharmony_ci#include <linux/ioport.h>
2462306a36Sopenharmony_ci#include <linux/interrupt.h>
2562306a36Sopenharmony_ci#include <linux/irq.h>
2662306a36Sopenharmony_ci#include <linux/gpio.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "w5100.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define DRV_NAME	"w5100"
3162306a36Sopenharmony_ci#define DRV_VERSION	"2012-04-04"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ciMODULE_DESCRIPTION("WIZnet W5100 Ethernet driver v"DRV_VERSION);
3462306a36Sopenharmony_ciMODULE_AUTHOR("Mike Sinkovsky <msink@permonline.ru>");
3562306a36Sopenharmony_ciMODULE_ALIAS("platform:"DRV_NAME);
3662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/*
3962306a36Sopenharmony_ci * W5100/W5200/W5500 common registers
4062306a36Sopenharmony_ci */
4162306a36Sopenharmony_ci#define W5100_COMMON_REGS	0x0000
4262306a36Sopenharmony_ci#define W5100_MR		0x0000 /* Mode Register */
4362306a36Sopenharmony_ci#define   MR_RST		  0x80 /* S/W reset */
4462306a36Sopenharmony_ci#define   MR_PB			  0x10 /* Ping block */
4562306a36Sopenharmony_ci#define   MR_AI			  0x02 /* Address Auto-Increment */
4662306a36Sopenharmony_ci#define   MR_IND		  0x01 /* Indirect mode */
4762306a36Sopenharmony_ci#define W5100_SHAR		0x0009 /* Source MAC address */
4862306a36Sopenharmony_ci#define W5100_IR		0x0015 /* Interrupt Register */
4962306a36Sopenharmony_ci#define W5100_COMMON_REGS_LEN	0x0040
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define W5100_Sn_MR		0x0000 /* Sn Mode Register */
5262306a36Sopenharmony_ci#define W5100_Sn_CR		0x0001 /* Sn Command Register */
5362306a36Sopenharmony_ci#define W5100_Sn_IR		0x0002 /* Sn Interrupt Register */
5462306a36Sopenharmony_ci#define W5100_Sn_SR		0x0003 /* Sn Status Register */
5562306a36Sopenharmony_ci#define W5100_Sn_TX_FSR		0x0020 /* Sn Transmit free memory size */
5662306a36Sopenharmony_ci#define W5100_Sn_TX_RD		0x0022 /* Sn Transmit memory read pointer */
5762306a36Sopenharmony_ci#define W5100_Sn_TX_WR		0x0024 /* Sn Transmit memory write pointer */
5862306a36Sopenharmony_ci#define W5100_Sn_RX_RSR		0x0026 /* Sn Receive free memory size */
5962306a36Sopenharmony_ci#define W5100_Sn_RX_RD		0x0028 /* Sn Receive memory read pointer */
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define S0_REGS(priv)		((priv)->s0_regs)
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#define W5100_S0_MR(priv)	(S0_REGS(priv) + W5100_Sn_MR)
6462306a36Sopenharmony_ci#define   S0_MR_MACRAW		  0x04 /* MAC RAW mode */
6562306a36Sopenharmony_ci#define   S0_MR_MF		  0x40 /* MAC Filter for W5100 and W5200 */
6662306a36Sopenharmony_ci#define   W5500_S0_MR_MF	  0x80 /* MAC Filter for W5500 */
6762306a36Sopenharmony_ci#define W5100_S0_CR(priv)	(S0_REGS(priv) + W5100_Sn_CR)
6862306a36Sopenharmony_ci#define   S0_CR_OPEN		  0x01 /* OPEN command */
6962306a36Sopenharmony_ci#define   S0_CR_CLOSE		  0x10 /* CLOSE command */
7062306a36Sopenharmony_ci#define   S0_CR_SEND		  0x20 /* SEND command */
7162306a36Sopenharmony_ci#define   S0_CR_RECV		  0x40 /* RECV command */
7262306a36Sopenharmony_ci#define W5100_S0_IR(priv)	(S0_REGS(priv) + W5100_Sn_IR)
7362306a36Sopenharmony_ci#define   S0_IR_SENDOK		  0x10 /* complete sending */
7462306a36Sopenharmony_ci#define   S0_IR_RECV		  0x04 /* receiving data */
7562306a36Sopenharmony_ci#define W5100_S0_SR(priv)	(S0_REGS(priv) + W5100_Sn_SR)
7662306a36Sopenharmony_ci#define   S0_SR_MACRAW		  0x42 /* mac raw mode */
7762306a36Sopenharmony_ci#define W5100_S0_TX_FSR(priv)	(S0_REGS(priv) + W5100_Sn_TX_FSR)
7862306a36Sopenharmony_ci#define W5100_S0_TX_RD(priv)	(S0_REGS(priv) + W5100_Sn_TX_RD)
7962306a36Sopenharmony_ci#define W5100_S0_TX_WR(priv)	(S0_REGS(priv) + W5100_Sn_TX_WR)
8062306a36Sopenharmony_ci#define W5100_S0_RX_RSR(priv)	(S0_REGS(priv) + W5100_Sn_RX_RSR)
8162306a36Sopenharmony_ci#define W5100_S0_RX_RD(priv)	(S0_REGS(priv) + W5100_Sn_RX_RD)
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci#define W5100_S0_REGS_LEN	0x0040
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/*
8662306a36Sopenharmony_ci * W5100 and W5200 common registers
8762306a36Sopenharmony_ci */
8862306a36Sopenharmony_ci#define W5100_IMR		0x0016 /* Interrupt Mask Register */
8962306a36Sopenharmony_ci#define   IR_S0			  0x01 /* S0 interrupt */
9062306a36Sopenharmony_ci#define W5100_RTR		0x0017 /* Retry Time-value Register */
9162306a36Sopenharmony_ci#define   RTR_DEFAULT		  2000 /* =0x07d0 (2000) */
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/*
9462306a36Sopenharmony_ci * W5100 specific register and memory
9562306a36Sopenharmony_ci */
9662306a36Sopenharmony_ci#define W5100_RMSR		0x001a /* Receive Memory Size */
9762306a36Sopenharmony_ci#define W5100_TMSR		0x001b /* Transmit Memory Size */
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci#define W5100_S0_REGS		0x0400
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci#define W5100_TX_MEM_START	0x4000
10262306a36Sopenharmony_ci#define W5100_TX_MEM_SIZE	0x2000
10362306a36Sopenharmony_ci#define W5100_RX_MEM_START	0x6000
10462306a36Sopenharmony_ci#define W5100_RX_MEM_SIZE	0x2000
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/*
10762306a36Sopenharmony_ci * W5200 specific register and memory
10862306a36Sopenharmony_ci */
10962306a36Sopenharmony_ci#define W5200_S0_REGS		0x4000
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci#define W5200_Sn_RXMEM_SIZE(n)	(0x401e + (n) * 0x0100) /* Sn RX Memory Size */
11262306a36Sopenharmony_ci#define W5200_Sn_TXMEM_SIZE(n)	(0x401f + (n) * 0x0100) /* Sn TX Memory Size */
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci#define W5200_TX_MEM_START	0x8000
11562306a36Sopenharmony_ci#define W5200_TX_MEM_SIZE	0x4000
11662306a36Sopenharmony_ci#define W5200_RX_MEM_START	0xc000
11762306a36Sopenharmony_ci#define W5200_RX_MEM_SIZE	0x4000
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/*
12062306a36Sopenharmony_ci * W5500 specific register and memory
12162306a36Sopenharmony_ci *
12262306a36Sopenharmony_ci * W5500 register and memory are organized by multiple blocks.  Each one is
12362306a36Sopenharmony_ci * selected by 16bits offset address and 5bits block select bits.  So we
12462306a36Sopenharmony_ci * encode it into 32bits address. (lower 16bits is offset address and
12562306a36Sopenharmony_ci * upper 16bits is block select bits)
12662306a36Sopenharmony_ci */
12762306a36Sopenharmony_ci#define W5500_SIMR		0x0018 /* Socket Interrupt Mask Register */
12862306a36Sopenharmony_ci#define W5500_RTR		0x0019 /* Retry Time-value Register */
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci#define W5500_S0_REGS		0x10000
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci#define W5500_Sn_RXMEM_SIZE(n)	\
13362306a36Sopenharmony_ci		(0x1001e + (n) * 0x40000) /* Sn RX Memory Size */
13462306a36Sopenharmony_ci#define W5500_Sn_TXMEM_SIZE(n)	\
13562306a36Sopenharmony_ci		(0x1001f + (n) * 0x40000) /* Sn TX Memory Size */
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci#define W5500_TX_MEM_START	0x20000
13862306a36Sopenharmony_ci#define W5500_TX_MEM_SIZE	0x04000
13962306a36Sopenharmony_ci#define W5500_RX_MEM_START	0x30000
14062306a36Sopenharmony_ci#define W5500_RX_MEM_SIZE	0x04000
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/*
14362306a36Sopenharmony_ci * Device driver private data structure
14462306a36Sopenharmony_ci */
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistruct w5100_priv {
14762306a36Sopenharmony_ci	const struct w5100_ops *ops;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/* Socket 0 register offset address */
15062306a36Sopenharmony_ci	u32 s0_regs;
15162306a36Sopenharmony_ci	/* Socket 0 TX buffer offset address and size */
15262306a36Sopenharmony_ci	u32 s0_tx_buf;
15362306a36Sopenharmony_ci	u16 s0_tx_buf_size;
15462306a36Sopenharmony_ci	/* Socket 0 RX buffer offset address and size */
15562306a36Sopenharmony_ci	u32 s0_rx_buf;
15662306a36Sopenharmony_ci	u16 s0_rx_buf_size;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	int irq;
15962306a36Sopenharmony_ci	int link_irq;
16062306a36Sopenharmony_ci	int link_gpio;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	struct napi_struct napi;
16362306a36Sopenharmony_ci	struct net_device *ndev;
16462306a36Sopenharmony_ci	bool promisc;
16562306a36Sopenharmony_ci	u32 msg_enable;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	struct workqueue_struct *xfer_wq;
16862306a36Sopenharmony_ci	struct work_struct rx_work;
16962306a36Sopenharmony_ci	struct sk_buff *tx_skb;
17062306a36Sopenharmony_ci	struct work_struct tx_work;
17162306a36Sopenharmony_ci	struct work_struct setrx_work;
17262306a36Sopenharmony_ci	struct work_struct restart_work;
17362306a36Sopenharmony_ci};
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/************************************************************************
17662306a36Sopenharmony_ci *
17762306a36Sopenharmony_ci *  Lowlevel I/O functions
17862306a36Sopenharmony_ci *
17962306a36Sopenharmony_ci ***********************************************************************/
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistruct w5100_mmio_priv {
18262306a36Sopenharmony_ci	void __iomem *base;
18362306a36Sopenharmony_ci	/* Serialize access in indirect address mode */
18462306a36Sopenharmony_ci	spinlock_t reg_lock;
18562306a36Sopenharmony_ci};
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic inline struct w5100_mmio_priv *w5100_mmio_priv(struct net_device *dev)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	return w5100_ops_priv(dev);
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic inline void __iomem *w5100_mmio(struct net_device *ndev)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	return mmio_priv->base;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci/*
20062306a36Sopenharmony_ci * In direct address mode host system can directly access W5100 registers
20162306a36Sopenharmony_ci * after mapping to Memory-Mapped I/O space.
20262306a36Sopenharmony_ci *
20362306a36Sopenharmony_ci * 0x8000 bytes are required for memory space.
20462306a36Sopenharmony_ci */
20562306a36Sopenharmony_cistatic inline int w5100_read_direct(struct net_device *ndev, u32 addr)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	return ioread8(w5100_mmio(ndev) + (addr << CONFIG_WIZNET_BUS_SHIFT));
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic inline int __w5100_write_direct(struct net_device *ndev, u32 addr,
21162306a36Sopenharmony_ci				       u8 data)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	iowrite8(data, w5100_mmio(ndev) + (addr << CONFIG_WIZNET_BUS_SHIFT));
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	return 0;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic inline int w5100_write_direct(struct net_device *ndev, u32 addr, u8 data)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	__w5100_write_direct(ndev, addr, data);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	return 0;
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic int w5100_read16_direct(struct net_device *ndev, u32 addr)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	u16 data;
22862306a36Sopenharmony_ci	data  = w5100_read_direct(ndev, addr) << 8;
22962306a36Sopenharmony_ci	data |= w5100_read_direct(ndev, addr + 1);
23062306a36Sopenharmony_ci	return data;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic int w5100_write16_direct(struct net_device *ndev, u32 addr, u16 data)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	__w5100_write_direct(ndev, addr, data >> 8);
23662306a36Sopenharmony_ci	__w5100_write_direct(ndev, addr + 1, data);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return 0;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic int w5100_readbulk_direct(struct net_device *ndev, u32 addr, u8 *buf,
24262306a36Sopenharmony_ci				 int len)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	int i;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	for (i = 0; i < len; i++, addr++)
24762306a36Sopenharmony_ci		*buf++ = w5100_read_direct(ndev, addr);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	return 0;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic int w5100_writebulk_direct(struct net_device *ndev, u32 addr,
25362306a36Sopenharmony_ci				  const u8 *buf, int len)
25462306a36Sopenharmony_ci{
25562306a36Sopenharmony_ci	int i;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	for (i = 0; i < len; i++, addr++)
25862306a36Sopenharmony_ci		__w5100_write_direct(ndev, addr, *buf++);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	return 0;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic int w5100_mmio_init(struct net_device *ndev)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(ndev->dev.parent);
26662306a36Sopenharmony_ci	struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	spin_lock_init(&mmio_priv->reg_lock);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	mmio_priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
27162306a36Sopenharmony_ci	if (IS_ERR(mmio_priv->base))
27262306a36Sopenharmony_ci		return PTR_ERR(mmio_priv->base);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	return 0;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic const struct w5100_ops w5100_mmio_direct_ops = {
27862306a36Sopenharmony_ci	.chip_id = W5100,
27962306a36Sopenharmony_ci	.read = w5100_read_direct,
28062306a36Sopenharmony_ci	.write = w5100_write_direct,
28162306a36Sopenharmony_ci	.read16 = w5100_read16_direct,
28262306a36Sopenharmony_ci	.write16 = w5100_write16_direct,
28362306a36Sopenharmony_ci	.readbulk = w5100_readbulk_direct,
28462306a36Sopenharmony_ci	.writebulk = w5100_writebulk_direct,
28562306a36Sopenharmony_ci	.init = w5100_mmio_init,
28662306a36Sopenharmony_ci};
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci/*
28962306a36Sopenharmony_ci * In indirect address mode host system indirectly accesses registers by
29062306a36Sopenharmony_ci * using Indirect Mode Address Register (IDM_AR) and Indirect Mode Data
29162306a36Sopenharmony_ci * Register (IDM_DR), which are directly mapped to Memory-Mapped I/O space.
29262306a36Sopenharmony_ci * Mode Register (MR) is directly accessible.
29362306a36Sopenharmony_ci *
29462306a36Sopenharmony_ci * Only 0x04 bytes are required for memory space.
29562306a36Sopenharmony_ci */
29662306a36Sopenharmony_ci#define W5100_IDM_AR		0x01   /* Indirect Mode Address Register */
29762306a36Sopenharmony_ci#define W5100_IDM_DR		0x03   /* Indirect Mode Data Register */
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic int w5100_read_indirect(struct net_device *ndev, u32 addr)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev);
30262306a36Sopenharmony_ci	unsigned long flags;
30362306a36Sopenharmony_ci	u8 data;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	spin_lock_irqsave(&mmio_priv->reg_lock, flags);
30662306a36Sopenharmony_ci	w5100_write16_direct(ndev, W5100_IDM_AR, addr);
30762306a36Sopenharmony_ci	data = w5100_read_direct(ndev, W5100_IDM_DR);
30862306a36Sopenharmony_ci	spin_unlock_irqrestore(&mmio_priv->reg_lock, flags);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	return data;
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic int w5100_write_indirect(struct net_device *ndev, u32 addr, u8 data)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev);
31662306a36Sopenharmony_ci	unsigned long flags;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	spin_lock_irqsave(&mmio_priv->reg_lock, flags);
31962306a36Sopenharmony_ci	w5100_write16_direct(ndev, W5100_IDM_AR, addr);
32062306a36Sopenharmony_ci	w5100_write_direct(ndev, W5100_IDM_DR, data);
32162306a36Sopenharmony_ci	spin_unlock_irqrestore(&mmio_priv->reg_lock, flags);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	return 0;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic int w5100_read16_indirect(struct net_device *ndev, u32 addr)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev);
32962306a36Sopenharmony_ci	unsigned long flags;
33062306a36Sopenharmony_ci	u16 data;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	spin_lock_irqsave(&mmio_priv->reg_lock, flags);
33362306a36Sopenharmony_ci	w5100_write16_direct(ndev, W5100_IDM_AR, addr);
33462306a36Sopenharmony_ci	data  = w5100_read_direct(ndev, W5100_IDM_DR) << 8;
33562306a36Sopenharmony_ci	data |= w5100_read_direct(ndev, W5100_IDM_DR);
33662306a36Sopenharmony_ci	spin_unlock_irqrestore(&mmio_priv->reg_lock, flags);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	return data;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic int w5100_write16_indirect(struct net_device *ndev, u32 addr, u16 data)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev);
34462306a36Sopenharmony_ci	unsigned long flags;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	spin_lock_irqsave(&mmio_priv->reg_lock, flags);
34762306a36Sopenharmony_ci	w5100_write16_direct(ndev, W5100_IDM_AR, addr);
34862306a36Sopenharmony_ci	__w5100_write_direct(ndev, W5100_IDM_DR, data >> 8);
34962306a36Sopenharmony_ci	w5100_write_direct(ndev, W5100_IDM_DR, data);
35062306a36Sopenharmony_ci	spin_unlock_irqrestore(&mmio_priv->reg_lock, flags);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	return 0;
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic int w5100_readbulk_indirect(struct net_device *ndev, u32 addr, u8 *buf,
35662306a36Sopenharmony_ci				   int len)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev);
35962306a36Sopenharmony_ci	unsigned long flags;
36062306a36Sopenharmony_ci	int i;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	spin_lock_irqsave(&mmio_priv->reg_lock, flags);
36362306a36Sopenharmony_ci	w5100_write16_direct(ndev, W5100_IDM_AR, addr);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	for (i = 0; i < len; i++)
36662306a36Sopenharmony_ci		*buf++ = w5100_read_direct(ndev, W5100_IDM_DR);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	spin_unlock_irqrestore(&mmio_priv->reg_lock, flags);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	return 0;
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic int w5100_writebulk_indirect(struct net_device *ndev, u32 addr,
37462306a36Sopenharmony_ci				    const u8 *buf, int len)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	struct w5100_mmio_priv *mmio_priv = w5100_mmio_priv(ndev);
37762306a36Sopenharmony_ci	unsigned long flags;
37862306a36Sopenharmony_ci	int i;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	spin_lock_irqsave(&mmio_priv->reg_lock, flags);
38162306a36Sopenharmony_ci	w5100_write16_direct(ndev, W5100_IDM_AR, addr);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	for (i = 0; i < len; i++)
38462306a36Sopenharmony_ci		__w5100_write_direct(ndev, W5100_IDM_DR, *buf++);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	spin_unlock_irqrestore(&mmio_priv->reg_lock, flags);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	return 0;
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic int w5100_reset_indirect(struct net_device *ndev)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	w5100_write_direct(ndev, W5100_MR, MR_RST);
39462306a36Sopenharmony_ci	mdelay(5);
39562306a36Sopenharmony_ci	w5100_write_direct(ndev, W5100_MR, MR_PB | MR_AI | MR_IND);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	return 0;
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic const struct w5100_ops w5100_mmio_indirect_ops = {
40162306a36Sopenharmony_ci	.chip_id = W5100,
40262306a36Sopenharmony_ci	.read = w5100_read_indirect,
40362306a36Sopenharmony_ci	.write = w5100_write_indirect,
40462306a36Sopenharmony_ci	.read16 = w5100_read16_indirect,
40562306a36Sopenharmony_ci	.write16 = w5100_write16_indirect,
40662306a36Sopenharmony_ci	.readbulk = w5100_readbulk_indirect,
40762306a36Sopenharmony_ci	.writebulk = w5100_writebulk_indirect,
40862306a36Sopenharmony_ci	.init = w5100_mmio_init,
40962306a36Sopenharmony_ci	.reset = w5100_reset_indirect,
41062306a36Sopenharmony_ci};
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci#if defined(CONFIG_WIZNET_BUS_DIRECT)
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic int w5100_read(struct w5100_priv *priv, u32 addr)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	return w5100_read_direct(priv->ndev, addr);
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistatic int w5100_write(struct w5100_priv *priv, u32 addr, u8 data)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	return w5100_write_direct(priv->ndev, addr, data);
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic int w5100_read16(struct w5100_priv *priv, u32 addr)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	return w5100_read16_direct(priv->ndev, addr);
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistatic int w5100_write16(struct w5100_priv *priv, u32 addr, u16 data)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	return w5100_write16_direct(priv->ndev, addr, data);
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic int w5100_readbulk(struct w5100_priv *priv, u32 addr, u8 *buf, int len)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	return w5100_readbulk_direct(priv->ndev, addr, buf, len);
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic int w5100_writebulk(struct w5100_priv *priv, u32 addr, const u8 *buf,
44062306a36Sopenharmony_ci			   int len)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	return w5100_writebulk_direct(priv->ndev, addr, buf, len);
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci#elif defined(CONFIG_WIZNET_BUS_INDIRECT)
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic int w5100_read(struct w5100_priv *priv, u32 addr)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	return w5100_read_indirect(priv->ndev, addr);
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic int w5100_write(struct w5100_priv *priv, u32 addr, u8 data)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	return w5100_write_indirect(priv->ndev, addr, data);
45562306a36Sopenharmony_ci}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic int w5100_read16(struct w5100_priv *priv, u32 addr)
45862306a36Sopenharmony_ci{
45962306a36Sopenharmony_ci	return w5100_read16_indirect(priv->ndev, addr);
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic int w5100_write16(struct w5100_priv *priv, u32 addr, u16 data)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	return w5100_write16_indirect(priv->ndev, addr, data);
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic int w5100_readbulk(struct w5100_priv *priv, u32 addr, u8 *buf, int len)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	return w5100_readbulk_indirect(priv->ndev, addr, buf, len);
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic int w5100_writebulk(struct w5100_priv *priv, u32 addr, const u8 *buf,
47362306a36Sopenharmony_ci			   int len)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	return w5100_writebulk_indirect(priv->ndev, addr, buf, len);
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci#else /* CONFIG_WIZNET_BUS_ANY */
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic int w5100_read(struct w5100_priv *priv, u32 addr)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	return priv->ops->read(priv->ndev, addr);
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic int w5100_write(struct w5100_priv *priv, u32 addr, u8 data)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	return priv->ops->write(priv->ndev, addr, data);
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic int w5100_read16(struct w5100_priv *priv, u32 addr)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	return priv->ops->read16(priv->ndev, addr);
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_cistatic int w5100_write16(struct w5100_priv *priv, u32 addr, u16 data)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	return priv->ops->write16(priv->ndev, addr, data);
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistatic int w5100_readbulk(struct w5100_priv *priv, u32 addr, u8 *buf, int len)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	return priv->ops->readbulk(priv->ndev, addr, buf, len);
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic int w5100_writebulk(struct w5100_priv *priv, u32 addr, const u8 *buf,
50662306a36Sopenharmony_ci			   int len)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	return priv->ops->writebulk(priv->ndev, addr, buf, len);
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci#endif
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_cistatic int w5100_readbuf(struct w5100_priv *priv, u16 offset, u8 *buf, int len)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	u32 addr;
51662306a36Sopenharmony_ci	int remain = 0;
51762306a36Sopenharmony_ci	int ret;
51862306a36Sopenharmony_ci	const u32 mem_start = priv->s0_rx_buf;
51962306a36Sopenharmony_ci	const u16 mem_size = priv->s0_rx_buf_size;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	offset %= mem_size;
52262306a36Sopenharmony_ci	addr = mem_start + offset;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	if (offset + len > mem_size) {
52562306a36Sopenharmony_ci		remain = (offset + len) % mem_size;
52662306a36Sopenharmony_ci		len = mem_size - offset;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	ret = w5100_readbulk(priv, addr, buf, len);
53062306a36Sopenharmony_ci	if (ret || !remain)
53162306a36Sopenharmony_ci		return ret;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	return w5100_readbulk(priv, mem_start, buf + len, remain);
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_cistatic int w5100_writebuf(struct w5100_priv *priv, u16 offset, const u8 *buf,
53762306a36Sopenharmony_ci			  int len)
53862306a36Sopenharmony_ci{
53962306a36Sopenharmony_ci	u32 addr;
54062306a36Sopenharmony_ci	int ret;
54162306a36Sopenharmony_ci	int remain = 0;
54262306a36Sopenharmony_ci	const u32 mem_start = priv->s0_tx_buf;
54362306a36Sopenharmony_ci	const u16 mem_size = priv->s0_tx_buf_size;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	offset %= mem_size;
54662306a36Sopenharmony_ci	addr = mem_start + offset;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	if (offset + len > mem_size) {
54962306a36Sopenharmony_ci		remain = (offset + len) % mem_size;
55062306a36Sopenharmony_ci		len = mem_size - offset;
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	ret = w5100_writebulk(priv, addr, buf, len);
55462306a36Sopenharmony_ci	if (ret || !remain)
55562306a36Sopenharmony_ci		return ret;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	return w5100_writebulk(priv, mem_start, buf + len, remain);
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistatic int w5100_reset(struct w5100_priv *priv)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	if (priv->ops->reset)
56362306a36Sopenharmony_ci		return priv->ops->reset(priv->ndev);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	w5100_write(priv, W5100_MR, MR_RST);
56662306a36Sopenharmony_ci	mdelay(5);
56762306a36Sopenharmony_ci	w5100_write(priv, W5100_MR, MR_PB);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	return 0;
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cistatic int w5100_command(struct w5100_priv *priv, u16 cmd)
57362306a36Sopenharmony_ci{
57462306a36Sopenharmony_ci	unsigned long timeout;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	w5100_write(priv, W5100_S0_CR(priv), cmd);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(100);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	while (w5100_read(priv, W5100_S0_CR(priv)) != 0) {
58162306a36Sopenharmony_ci		if (time_after(jiffies, timeout))
58262306a36Sopenharmony_ci			return -EIO;
58362306a36Sopenharmony_ci		cpu_relax();
58462306a36Sopenharmony_ci	}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	return 0;
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_cistatic void w5100_write_macaddr(struct w5100_priv *priv)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	struct net_device *ndev = priv->ndev;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	w5100_writebulk(priv, W5100_SHAR, ndev->dev_addr, ETH_ALEN);
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic void w5100_socket_intr_mask(struct w5100_priv *priv, u8 mask)
59762306a36Sopenharmony_ci{
59862306a36Sopenharmony_ci	u32 imr;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	if (priv->ops->chip_id == W5500)
60162306a36Sopenharmony_ci		imr = W5500_SIMR;
60262306a36Sopenharmony_ci	else
60362306a36Sopenharmony_ci		imr = W5100_IMR;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	w5100_write(priv, imr, mask);
60662306a36Sopenharmony_ci}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_cistatic void w5100_enable_intr(struct w5100_priv *priv)
60962306a36Sopenharmony_ci{
61062306a36Sopenharmony_ci	w5100_socket_intr_mask(priv, IR_S0);
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cistatic void w5100_disable_intr(struct w5100_priv *priv)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	w5100_socket_intr_mask(priv, 0);
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cistatic void w5100_memory_configure(struct w5100_priv *priv)
61962306a36Sopenharmony_ci{
62062306a36Sopenharmony_ci	/* Configure 16K of internal memory
62162306a36Sopenharmony_ci	 * as 8K RX buffer and 8K TX buffer
62262306a36Sopenharmony_ci	 */
62362306a36Sopenharmony_ci	w5100_write(priv, W5100_RMSR, 0x03);
62462306a36Sopenharmony_ci	w5100_write(priv, W5100_TMSR, 0x03);
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_cistatic void w5200_memory_configure(struct w5100_priv *priv)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	int i;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	/* Configure internal RX memory as 16K RX buffer and
63262306a36Sopenharmony_ci	 * internal TX memory as 16K TX buffer
63362306a36Sopenharmony_ci	 */
63462306a36Sopenharmony_ci	w5100_write(priv, W5200_Sn_RXMEM_SIZE(0), 0x10);
63562306a36Sopenharmony_ci	w5100_write(priv, W5200_Sn_TXMEM_SIZE(0), 0x10);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	for (i = 1; i < 8; i++) {
63862306a36Sopenharmony_ci		w5100_write(priv, W5200_Sn_RXMEM_SIZE(i), 0);
63962306a36Sopenharmony_ci		w5100_write(priv, W5200_Sn_TXMEM_SIZE(i), 0);
64062306a36Sopenharmony_ci	}
64162306a36Sopenharmony_ci}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_cistatic void w5500_memory_configure(struct w5100_priv *priv)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	int i;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	/* Configure internal RX memory as 16K RX buffer and
64862306a36Sopenharmony_ci	 * internal TX memory as 16K TX buffer
64962306a36Sopenharmony_ci	 */
65062306a36Sopenharmony_ci	w5100_write(priv, W5500_Sn_RXMEM_SIZE(0), 0x10);
65162306a36Sopenharmony_ci	w5100_write(priv, W5500_Sn_TXMEM_SIZE(0), 0x10);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	for (i = 1; i < 8; i++) {
65462306a36Sopenharmony_ci		w5100_write(priv, W5500_Sn_RXMEM_SIZE(i), 0);
65562306a36Sopenharmony_ci		w5100_write(priv, W5500_Sn_TXMEM_SIZE(i), 0);
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_cistatic int w5100_hw_reset(struct w5100_priv *priv)
66062306a36Sopenharmony_ci{
66162306a36Sopenharmony_ci	u32 rtr;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	w5100_reset(priv);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	w5100_disable_intr(priv);
66662306a36Sopenharmony_ci	w5100_write_macaddr(priv);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	switch (priv->ops->chip_id) {
66962306a36Sopenharmony_ci	case W5100:
67062306a36Sopenharmony_ci		w5100_memory_configure(priv);
67162306a36Sopenharmony_ci		rtr = W5100_RTR;
67262306a36Sopenharmony_ci		break;
67362306a36Sopenharmony_ci	case W5200:
67462306a36Sopenharmony_ci		w5200_memory_configure(priv);
67562306a36Sopenharmony_ci		rtr = W5100_RTR;
67662306a36Sopenharmony_ci		break;
67762306a36Sopenharmony_ci	case W5500:
67862306a36Sopenharmony_ci		w5500_memory_configure(priv);
67962306a36Sopenharmony_ci		rtr = W5500_RTR;
68062306a36Sopenharmony_ci		break;
68162306a36Sopenharmony_ci	default:
68262306a36Sopenharmony_ci		return -EINVAL;
68362306a36Sopenharmony_ci	}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	if (w5100_read16(priv, rtr) != RTR_DEFAULT)
68662306a36Sopenharmony_ci		return -ENODEV;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	return 0;
68962306a36Sopenharmony_ci}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_cistatic void w5100_hw_start(struct w5100_priv *priv)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	u8 mode = S0_MR_MACRAW;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	if (!priv->promisc) {
69662306a36Sopenharmony_ci		if (priv->ops->chip_id == W5500)
69762306a36Sopenharmony_ci			mode |= W5500_S0_MR_MF;
69862306a36Sopenharmony_ci		else
69962306a36Sopenharmony_ci			mode |= S0_MR_MF;
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	w5100_write(priv, W5100_S0_MR(priv), mode);
70362306a36Sopenharmony_ci	w5100_command(priv, S0_CR_OPEN);
70462306a36Sopenharmony_ci	w5100_enable_intr(priv);
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic void w5100_hw_close(struct w5100_priv *priv)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	w5100_disable_intr(priv);
71062306a36Sopenharmony_ci	w5100_command(priv, S0_CR_CLOSE);
71162306a36Sopenharmony_ci}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci/***********************************************************************
71462306a36Sopenharmony_ci *
71562306a36Sopenharmony_ci *   Device driver functions / callbacks
71662306a36Sopenharmony_ci *
71762306a36Sopenharmony_ci ***********************************************************************/
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cistatic void w5100_get_drvinfo(struct net_device *ndev,
72062306a36Sopenharmony_ci			      struct ethtool_drvinfo *info)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
72362306a36Sopenharmony_ci	strscpy(info->version, DRV_VERSION, sizeof(info->version));
72462306a36Sopenharmony_ci	strscpy(info->bus_info, dev_name(ndev->dev.parent),
72562306a36Sopenharmony_ci		sizeof(info->bus_info));
72662306a36Sopenharmony_ci}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_cistatic u32 w5100_get_link(struct net_device *ndev)
72962306a36Sopenharmony_ci{
73062306a36Sopenharmony_ci	struct w5100_priv *priv = netdev_priv(ndev);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	if (gpio_is_valid(priv->link_gpio))
73362306a36Sopenharmony_ci		return !!gpio_get_value(priv->link_gpio);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	return 1;
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cistatic u32 w5100_get_msglevel(struct net_device *ndev)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	struct w5100_priv *priv = netdev_priv(ndev);
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	return priv->msg_enable;
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_cistatic void w5100_set_msglevel(struct net_device *ndev, u32 value)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	struct w5100_priv *priv = netdev_priv(ndev);
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	priv->msg_enable = value;
75062306a36Sopenharmony_ci}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_cistatic int w5100_get_regs_len(struct net_device *ndev)
75362306a36Sopenharmony_ci{
75462306a36Sopenharmony_ci	return W5100_COMMON_REGS_LEN + W5100_S0_REGS_LEN;
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cistatic void w5100_get_regs(struct net_device *ndev,
75862306a36Sopenharmony_ci			   struct ethtool_regs *regs, void *buf)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	struct w5100_priv *priv = netdev_priv(ndev);
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	regs->version = 1;
76362306a36Sopenharmony_ci	w5100_readbulk(priv, W5100_COMMON_REGS, buf, W5100_COMMON_REGS_LEN);
76462306a36Sopenharmony_ci	buf += W5100_COMMON_REGS_LEN;
76562306a36Sopenharmony_ci	w5100_readbulk(priv, S0_REGS(priv), buf, W5100_S0_REGS_LEN);
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_cistatic void w5100_restart(struct net_device *ndev)
76962306a36Sopenharmony_ci{
77062306a36Sopenharmony_ci	struct w5100_priv *priv = netdev_priv(ndev);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	netif_stop_queue(ndev);
77362306a36Sopenharmony_ci	w5100_hw_reset(priv);
77462306a36Sopenharmony_ci	w5100_hw_start(priv);
77562306a36Sopenharmony_ci	ndev->stats.tx_errors++;
77662306a36Sopenharmony_ci	netif_trans_update(ndev);
77762306a36Sopenharmony_ci	netif_wake_queue(ndev);
77862306a36Sopenharmony_ci}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_cistatic void w5100_restart_work(struct work_struct *work)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci	struct w5100_priv *priv = container_of(work, struct w5100_priv,
78362306a36Sopenharmony_ci					       restart_work);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	w5100_restart(priv->ndev);
78662306a36Sopenharmony_ci}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_cistatic void w5100_tx_timeout(struct net_device *ndev, unsigned int txqueue)
78962306a36Sopenharmony_ci{
79062306a36Sopenharmony_ci	struct w5100_priv *priv = netdev_priv(ndev);
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	if (priv->ops->may_sleep)
79362306a36Sopenharmony_ci		schedule_work(&priv->restart_work);
79462306a36Sopenharmony_ci	else
79562306a36Sopenharmony_ci		w5100_restart(ndev);
79662306a36Sopenharmony_ci}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_cistatic void w5100_tx_skb(struct net_device *ndev, struct sk_buff *skb)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	struct w5100_priv *priv = netdev_priv(ndev);
80162306a36Sopenharmony_ci	u16 offset;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	offset = w5100_read16(priv, W5100_S0_TX_WR(priv));
80462306a36Sopenharmony_ci	w5100_writebuf(priv, offset, skb->data, skb->len);
80562306a36Sopenharmony_ci	w5100_write16(priv, W5100_S0_TX_WR(priv), offset + skb->len);
80662306a36Sopenharmony_ci	ndev->stats.tx_bytes += skb->len;
80762306a36Sopenharmony_ci	ndev->stats.tx_packets++;
80862306a36Sopenharmony_ci	dev_kfree_skb(skb);
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	w5100_command(priv, S0_CR_SEND);
81162306a36Sopenharmony_ci}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_cistatic void w5100_tx_work(struct work_struct *work)
81462306a36Sopenharmony_ci{
81562306a36Sopenharmony_ci	struct w5100_priv *priv = container_of(work, struct w5100_priv,
81662306a36Sopenharmony_ci					       tx_work);
81762306a36Sopenharmony_ci	struct sk_buff *skb = priv->tx_skb;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	priv->tx_skb = NULL;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	if (WARN_ON(!skb))
82262306a36Sopenharmony_ci		return;
82362306a36Sopenharmony_ci	w5100_tx_skb(priv->ndev, skb);
82462306a36Sopenharmony_ci}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_cistatic netdev_tx_t w5100_start_tx(struct sk_buff *skb, struct net_device *ndev)
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci	struct w5100_priv *priv = netdev_priv(ndev);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	netif_stop_queue(ndev);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	if (priv->ops->may_sleep) {
83362306a36Sopenharmony_ci		WARN_ON(priv->tx_skb);
83462306a36Sopenharmony_ci		priv->tx_skb = skb;
83562306a36Sopenharmony_ci		queue_work(priv->xfer_wq, &priv->tx_work);
83662306a36Sopenharmony_ci	} else {
83762306a36Sopenharmony_ci		w5100_tx_skb(ndev, skb);
83862306a36Sopenharmony_ci	}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	return NETDEV_TX_OK;
84162306a36Sopenharmony_ci}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_cistatic struct sk_buff *w5100_rx_skb(struct net_device *ndev)
84462306a36Sopenharmony_ci{
84562306a36Sopenharmony_ci	struct w5100_priv *priv = netdev_priv(ndev);
84662306a36Sopenharmony_ci	struct sk_buff *skb;
84762306a36Sopenharmony_ci	u16 rx_len;
84862306a36Sopenharmony_ci	u16 offset;
84962306a36Sopenharmony_ci	u8 header[2];
85062306a36Sopenharmony_ci	u16 rx_buf_len = w5100_read16(priv, W5100_S0_RX_RSR(priv));
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	if (rx_buf_len == 0)
85362306a36Sopenharmony_ci		return NULL;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	offset = w5100_read16(priv, W5100_S0_RX_RD(priv));
85662306a36Sopenharmony_ci	w5100_readbuf(priv, offset, header, 2);
85762306a36Sopenharmony_ci	rx_len = get_unaligned_be16(header) - 2;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	skb = netdev_alloc_skb_ip_align(ndev, rx_len);
86062306a36Sopenharmony_ci	if (unlikely(!skb)) {
86162306a36Sopenharmony_ci		w5100_write16(priv, W5100_S0_RX_RD(priv), offset + rx_buf_len);
86262306a36Sopenharmony_ci		w5100_command(priv, S0_CR_RECV);
86362306a36Sopenharmony_ci		ndev->stats.rx_dropped++;
86462306a36Sopenharmony_ci		return NULL;
86562306a36Sopenharmony_ci	}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	skb_put(skb, rx_len);
86862306a36Sopenharmony_ci	w5100_readbuf(priv, offset + 2, skb->data, rx_len);
86962306a36Sopenharmony_ci	w5100_write16(priv, W5100_S0_RX_RD(priv), offset + 2 + rx_len);
87062306a36Sopenharmony_ci	w5100_command(priv, S0_CR_RECV);
87162306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, ndev);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	ndev->stats.rx_packets++;
87462306a36Sopenharmony_ci	ndev->stats.rx_bytes += rx_len;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	return skb;
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cistatic void w5100_rx_work(struct work_struct *work)
88062306a36Sopenharmony_ci{
88162306a36Sopenharmony_ci	struct w5100_priv *priv = container_of(work, struct w5100_priv,
88262306a36Sopenharmony_ci					       rx_work);
88362306a36Sopenharmony_ci	struct sk_buff *skb;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	while ((skb = w5100_rx_skb(priv->ndev)))
88662306a36Sopenharmony_ci		netif_rx(skb);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	w5100_enable_intr(priv);
88962306a36Sopenharmony_ci}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_cistatic int w5100_napi_poll(struct napi_struct *napi, int budget)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	struct w5100_priv *priv = container_of(napi, struct w5100_priv, napi);
89462306a36Sopenharmony_ci	int rx_count;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	for (rx_count = 0; rx_count < budget; rx_count++) {
89762306a36Sopenharmony_ci		struct sk_buff *skb = w5100_rx_skb(priv->ndev);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci		if (skb)
90062306a36Sopenharmony_ci			netif_receive_skb(skb);
90162306a36Sopenharmony_ci		else
90262306a36Sopenharmony_ci			break;
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	if (rx_count < budget) {
90662306a36Sopenharmony_ci		napi_complete_done(napi, rx_count);
90762306a36Sopenharmony_ci		w5100_enable_intr(priv);
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	return rx_count;
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_cistatic irqreturn_t w5100_interrupt(int irq, void *ndev_instance)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	struct net_device *ndev = ndev_instance;
91662306a36Sopenharmony_ci	struct w5100_priv *priv = netdev_priv(ndev);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	int ir = w5100_read(priv, W5100_S0_IR(priv));
91962306a36Sopenharmony_ci	if (!ir)
92062306a36Sopenharmony_ci		return IRQ_NONE;
92162306a36Sopenharmony_ci	w5100_write(priv, W5100_S0_IR(priv), ir);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	if (ir & S0_IR_SENDOK) {
92462306a36Sopenharmony_ci		netif_dbg(priv, tx_done, ndev, "tx done\n");
92562306a36Sopenharmony_ci		netif_wake_queue(ndev);
92662306a36Sopenharmony_ci	}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	if (ir & S0_IR_RECV) {
92962306a36Sopenharmony_ci		w5100_disable_intr(priv);
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci		if (priv->ops->may_sleep)
93262306a36Sopenharmony_ci			queue_work(priv->xfer_wq, &priv->rx_work);
93362306a36Sopenharmony_ci		else if (napi_schedule_prep(&priv->napi))
93462306a36Sopenharmony_ci			__napi_schedule(&priv->napi);
93562306a36Sopenharmony_ci	}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	return IRQ_HANDLED;
93862306a36Sopenharmony_ci}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_cistatic irqreturn_t w5100_detect_link(int irq, void *ndev_instance)
94162306a36Sopenharmony_ci{
94262306a36Sopenharmony_ci	struct net_device *ndev = ndev_instance;
94362306a36Sopenharmony_ci	struct w5100_priv *priv = netdev_priv(ndev);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	if (netif_running(ndev)) {
94662306a36Sopenharmony_ci		if (gpio_get_value(priv->link_gpio) != 0) {
94762306a36Sopenharmony_ci			netif_info(priv, link, ndev, "link is up\n");
94862306a36Sopenharmony_ci			netif_carrier_on(ndev);
94962306a36Sopenharmony_ci		} else {
95062306a36Sopenharmony_ci			netif_info(priv, link, ndev, "link is down\n");
95162306a36Sopenharmony_ci			netif_carrier_off(ndev);
95262306a36Sopenharmony_ci		}
95362306a36Sopenharmony_ci	}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	return IRQ_HANDLED;
95662306a36Sopenharmony_ci}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_cistatic void w5100_setrx_work(struct work_struct *work)
95962306a36Sopenharmony_ci{
96062306a36Sopenharmony_ci	struct w5100_priv *priv = container_of(work, struct w5100_priv,
96162306a36Sopenharmony_ci					       setrx_work);
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	w5100_hw_start(priv);
96462306a36Sopenharmony_ci}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_cistatic void w5100_set_rx_mode(struct net_device *ndev)
96762306a36Sopenharmony_ci{
96862306a36Sopenharmony_ci	struct w5100_priv *priv = netdev_priv(ndev);
96962306a36Sopenharmony_ci	bool set_promisc = (ndev->flags & IFF_PROMISC) != 0;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	if (priv->promisc != set_promisc) {
97262306a36Sopenharmony_ci		priv->promisc = set_promisc;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci		if (priv->ops->may_sleep)
97562306a36Sopenharmony_ci			schedule_work(&priv->setrx_work);
97662306a36Sopenharmony_ci		else
97762306a36Sopenharmony_ci			w5100_hw_start(priv);
97862306a36Sopenharmony_ci	}
97962306a36Sopenharmony_ci}
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_cistatic int w5100_set_macaddr(struct net_device *ndev, void *addr)
98262306a36Sopenharmony_ci{
98362306a36Sopenharmony_ci	struct w5100_priv *priv = netdev_priv(ndev);
98462306a36Sopenharmony_ci	struct sockaddr *sock_addr = addr;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	if (!is_valid_ether_addr(sock_addr->sa_data))
98762306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
98862306a36Sopenharmony_ci	eth_hw_addr_set(ndev, sock_addr->sa_data);
98962306a36Sopenharmony_ci	w5100_write_macaddr(priv);
99062306a36Sopenharmony_ci	return 0;
99162306a36Sopenharmony_ci}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_cistatic int w5100_open(struct net_device *ndev)
99462306a36Sopenharmony_ci{
99562306a36Sopenharmony_ci	struct w5100_priv *priv = netdev_priv(ndev);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	netif_info(priv, ifup, ndev, "enabling\n");
99862306a36Sopenharmony_ci	w5100_hw_start(priv);
99962306a36Sopenharmony_ci	napi_enable(&priv->napi);
100062306a36Sopenharmony_ci	netif_start_queue(ndev);
100162306a36Sopenharmony_ci	if (!gpio_is_valid(priv->link_gpio) ||
100262306a36Sopenharmony_ci	    gpio_get_value(priv->link_gpio) != 0)
100362306a36Sopenharmony_ci		netif_carrier_on(ndev);
100462306a36Sopenharmony_ci	return 0;
100562306a36Sopenharmony_ci}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_cistatic int w5100_stop(struct net_device *ndev)
100862306a36Sopenharmony_ci{
100962306a36Sopenharmony_ci	struct w5100_priv *priv = netdev_priv(ndev);
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	netif_info(priv, ifdown, ndev, "shutting down\n");
101262306a36Sopenharmony_ci	w5100_hw_close(priv);
101362306a36Sopenharmony_ci	netif_carrier_off(ndev);
101462306a36Sopenharmony_ci	netif_stop_queue(ndev);
101562306a36Sopenharmony_ci	napi_disable(&priv->napi);
101662306a36Sopenharmony_ci	return 0;
101762306a36Sopenharmony_ci}
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_cistatic const struct ethtool_ops w5100_ethtool_ops = {
102062306a36Sopenharmony_ci	.get_drvinfo		= w5100_get_drvinfo,
102162306a36Sopenharmony_ci	.get_msglevel		= w5100_get_msglevel,
102262306a36Sopenharmony_ci	.set_msglevel		= w5100_set_msglevel,
102362306a36Sopenharmony_ci	.get_link		= w5100_get_link,
102462306a36Sopenharmony_ci	.get_regs_len		= w5100_get_regs_len,
102562306a36Sopenharmony_ci	.get_regs		= w5100_get_regs,
102662306a36Sopenharmony_ci};
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_cistatic const struct net_device_ops w5100_netdev_ops = {
102962306a36Sopenharmony_ci	.ndo_open		= w5100_open,
103062306a36Sopenharmony_ci	.ndo_stop		= w5100_stop,
103162306a36Sopenharmony_ci	.ndo_start_xmit		= w5100_start_tx,
103262306a36Sopenharmony_ci	.ndo_tx_timeout		= w5100_tx_timeout,
103362306a36Sopenharmony_ci	.ndo_set_rx_mode	= w5100_set_rx_mode,
103462306a36Sopenharmony_ci	.ndo_set_mac_address	= w5100_set_macaddr,
103562306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
103662306a36Sopenharmony_ci};
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_cistatic int w5100_mmio_probe(struct platform_device *pdev)
103962306a36Sopenharmony_ci{
104062306a36Sopenharmony_ci	struct wiznet_platform_data *data = dev_get_platdata(&pdev->dev);
104162306a36Sopenharmony_ci	const void *mac_addr = NULL;
104262306a36Sopenharmony_ci	struct resource *mem;
104362306a36Sopenharmony_ci	const struct w5100_ops *ops;
104462306a36Sopenharmony_ci	int irq;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	if (data && is_valid_ether_addr(data->mac_addr))
104762306a36Sopenharmony_ci		mac_addr = data->mac_addr;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
105062306a36Sopenharmony_ci	if (!mem)
105162306a36Sopenharmony_ci		return -EINVAL;
105262306a36Sopenharmony_ci	if (resource_size(mem) < W5100_BUS_DIRECT_SIZE)
105362306a36Sopenharmony_ci		ops = &w5100_mmio_indirect_ops;
105462306a36Sopenharmony_ci	else
105562306a36Sopenharmony_ci		ops = &w5100_mmio_direct_ops;
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
105862306a36Sopenharmony_ci	if (irq < 0)
105962306a36Sopenharmony_ci		return irq;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	return w5100_probe(&pdev->dev, ops, sizeof(struct w5100_mmio_priv),
106262306a36Sopenharmony_ci			   mac_addr, irq, data ? data->link_gpio : -EINVAL);
106362306a36Sopenharmony_ci}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_cistatic int w5100_mmio_remove(struct platform_device *pdev)
106662306a36Sopenharmony_ci{
106762306a36Sopenharmony_ci	w5100_remove(&pdev->dev);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	return 0;
107062306a36Sopenharmony_ci}
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_civoid *w5100_ops_priv(const struct net_device *ndev)
107362306a36Sopenharmony_ci{
107462306a36Sopenharmony_ci	return netdev_priv(ndev) +
107562306a36Sopenharmony_ci	       ALIGN(sizeof(struct w5100_priv), NETDEV_ALIGN);
107662306a36Sopenharmony_ci}
107762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(w5100_ops_priv);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ciint w5100_probe(struct device *dev, const struct w5100_ops *ops,
108062306a36Sopenharmony_ci		int sizeof_ops_priv, const void *mac_addr, int irq,
108162306a36Sopenharmony_ci		int link_gpio)
108262306a36Sopenharmony_ci{
108362306a36Sopenharmony_ci	struct w5100_priv *priv;
108462306a36Sopenharmony_ci	struct net_device *ndev;
108562306a36Sopenharmony_ci	int err;
108662306a36Sopenharmony_ci	size_t alloc_size;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	alloc_size = sizeof(*priv);
108962306a36Sopenharmony_ci	if (sizeof_ops_priv) {
109062306a36Sopenharmony_ci		alloc_size = ALIGN(alloc_size, NETDEV_ALIGN);
109162306a36Sopenharmony_ci		alloc_size += sizeof_ops_priv;
109262306a36Sopenharmony_ci	}
109362306a36Sopenharmony_ci	alloc_size += NETDEV_ALIGN - 1;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	ndev = alloc_etherdev(alloc_size);
109662306a36Sopenharmony_ci	if (!ndev)
109762306a36Sopenharmony_ci		return -ENOMEM;
109862306a36Sopenharmony_ci	SET_NETDEV_DEV(ndev, dev);
109962306a36Sopenharmony_ci	dev_set_drvdata(dev, ndev);
110062306a36Sopenharmony_ci	priv = netdev_priv(ndev);
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	switch (ops->chip_id) {
110362306a36Sopenharmony_ci	case W5100:
110462306a36Sopenharmony_ci		priv->s0_regs = W5100_S0_REGS;
110562306a36Sopenharmony_ci		priv->s0_tx_buf = W5100_TX_MEM_START;
110662306a36Sopenharmony_ci		priv->s0_tx_buf_size = W5100_TX_MEM_SIZE;
110762306a36Sopenharmony_ci		priv->s0_rx_buf = W5100_RX_MEM_START;
110862306a36Sopenharmony_ci		priv->s0_rx_buf_size = W5100_RX_MEM_SIZE;
110962306a36Sopenharmony_ci		break;
111062306a36Sopenharmony_ci	case W5200:
111162306a36Sopenharmony_ci		priv->s0_regs = W5200_S0_REGS;
111262306a36Sopenharmony_ci		priv->s0_tx_buf = W5200_TX_MEM_START;
111362306a36Sopenharmony_ci		priv->s0_tx_buf_size = W5200_TX_MEM_SIZE;
111462306a36Sopenharmony_ci		priv->s0_rx_buf = W5200_RX_MEM_START;
111562306a36Sopenharmony_ci		priv->s0_rx_buf_size = W5200_RX_MEM_SIZE;
111662306a36Sopenharmony_ci		break;
111762306a36Sopenharmony_ci	case W5500:
111862306a36Sopenharmony_ci		priv->s0_regs = W5500_S0_REGS;
111962306a36Sopenharmony_ci		priv->s0_tx_buf = W5500_TX_MEM_START;
112062306a36Sopenharmony_ci		priv->s0_tx_buf_size = W5500_TX_MEM_SIZE;
112162306a36Sopenharmony_ci		priv->s0_rx_buf = W5500_RX_MEM_START;
112262306a36Sopenharmony_ci		priv->s0_rx_buf_size = W5500_RX_MEM_SIZE;
112362306a36Sopenharmony_ci		break;
112462306a36Sopenharmony_ci	default:
112562306a36Sopenharmony_ci		err = -EINVAL;
112662306a36Sopenharmony_ci		goto err_register;
112762306a36Sopenharmony_ci	}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	priv->ndev = ndev;
113062306a36Sopenharmony_ci	priv->ops = ops;
113162306a36Sopenharmony_ci	priv->irq = irq;
113262306a36Sopenharmony_ci	priv->link_gpio = link_gpio;
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	ndev->netdev_ops = &w5100_netdev_ops;
113562306a36Sopenharmony_ci	ndev->ethtool_ops = &w5100_ethtool_ops;
113662306a36Sopenharmony_ci	netif_napi_add_weight(ndev, &priv->napi, w5100_napi_poll, 16);
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	/* This chip doesn't support VLAN packets with normal MTU,
113962306a36Sopenharmony_ci	 * so disable VLAN for this device.
114062306a36Sopenharmony_ci	 */
114162306a36Sopenharmony_ci	ndev->features |= NETIF_F_VLAN_CHALLENGED;
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	err = register_netdev(ndev);
114462306a36Sopenharmony_ci	if (err < 0)
114562306a36Sopenharmony_ci		goto err_register;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	priv->xfer_wq = alloc_workqueue("%s", WQ_MEM_RECLAIM, 0,
114862306a36Sopenharmony_ci					netdev_name(ndev));
114962306a36Sopenharmony_ci	if (!priv->xfer_wq) {
115062306a36Sopenharmony_ci		err = -ENOMEM;
115162306a36Sopenharmony_ci		goto err_wq;
115262306a36Sopenharmony_ci	}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	INIT_WORK(&priv->rx_work, w5100_rx_work);
115562306a36Sopenharmony_ci	INIT_WORK(&priv->tx_work, w5100_tx_work);
115662306a36Sopenharmony_ci	INIT_WORK(&priv->setrx_work, w5100_setrx_work);
115762306a36Sopenharmony_ci	INIT_WORK(&priv->restart_work, w5100_restart_work);
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	if (mac_addr)
116062306a36Sopenharmony_ci		eth_hw_addr_set(ndev, mac_addr);
116162306a36Sopenharmony_ci	else
116262306a36Sopenharmony_ci		eth_hw_addr_random(ndev);
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	if (priv->ops->init) {
116562306a36Sopenharmony_ci		err = priv->ops->init(priv->ndev);
116662306a36Sopenharmony_ci		if (err)
116762306a36Sopenharmony_ci			goto err_hw;
116862306a36Sopenharmony_ci	}
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	err = w5100_hw_reset(priv);
117162306a36Sopenharmony_ci	if (err)
117262306a36Sopenharmony_ci		goto err_hw;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	if (ops->may_sleep) {
117562306a36Sopenharmony_ci		err = request_threaded_irq(priv->irq, NULL, w5100_interrupt,
117662306a36Sopenharmony_ci					   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
117762306a36Sopenharmony_ci					   netdev_name(ndev), ndev);
117862306a36Sopenharmony_ci	} else {
117962306a36Sopenharmony_ci		err = request_irq(priv->irq, w5100_interrupt,
118062306a36Sopenharmony_ci				  IRQF_TRIGGER_LOW, netdev_name(ndev), ndev);
118162306a36Sopenharmony_ci	}
118262306a36Sopenharmony_ci	if (err)
118362306a36Sopenharmony_ci		goto err_hw;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	if (gpio_is_valid(priv->link_gpio)) {
118662306a36Sopenharmony_ci		char *link_name = devm_kzalloc(dev, 16, GFP_KERNEL);
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci		if (!link_name) {
118962306a36Sopenharmony_ci			err = -ENOMEM;
119062306a36Sopenharmony_ci			goto err_gpio;
119162306a36Sopenharmony_ci		}
119262306a36Sopenharmony_ci		snprintf(link_name, 16, "%s-link", netdev_name(ndev));
119362306a36Sopenharmony_ci		priv->link_irq = gpio_to_irq(priv->link_gpio);
119462306a36Sopenharmony_ci		if (request_any_context_irq(priv->link_irq, w5100_detect_link,
119562306a36Sopenharmony_ci					    IRQF_TRIGGER_RISING |
119662306a36Sopenharmony_ci					    IRQF_TRIGGER_FALLING,
119762306a36Sopenharmony_ci					    link_name, priv->ndev) < 0)
119862306a36Sopenharmony_ci			priv->link_gpio = -EINVAL;
119962306a36Sopenharmony_ci	}
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	return 0;
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_cierr_gpio:
120462306a36Sopenharmony_ci	free_irq(priv->irq, ndev);
120562306a36Sopenharmony_cierr_hw:
120662306a36Sopenharmony_ci	destroy_workqueue(priv->xfer_wq);
120762306a36Sopenharmony_cierr_wq:
120862306a36Sopenharmony_ci	unregister_netdev(ndev);
120962306a36Sopenharmony_cierr_register:
121062306a36Sopenharmony_ci	free_netdev(ndev);
121162306a36Sopenharmony_ci	return err;
121262306a36Sopenharmony_ci}
121362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(w5100_probe);
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_civoid w5100_remove(struct device *dev)
121662306a36Sopenharmony_ci{
121762306a36Sopenharmony_ci	struct net_device *ndev = dev_get_drvdata(dev);
121862306a36Sopenharmony_ci	struct w5100_priv *priv = netdev_priv(ndev);
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	w5100_hw_reset(priv);
122162306a36Sopenharmony_ci	free_irq(priv->irq, ndev);
122262306a36Sopenharmony_ci	if (gpio_is_valid(priv->link_gpio))
122362306a36Sopenharmony_ci		free_irq(priv->link_irq, ndev);
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	flush_work(&priv->setrx_work);
122662306a36Sopenharmony_ci	flush_work(&priv->restart_work);
122762306a36Sopenharmony_ci	destroy_workqueue(priv->xfer_wq);
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	unregister_netdev(ndev);
123062306a36Sopenharmony_ci	free_netdev(ndev);
123162306a36Sopenharmony_ci}
123262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(w5100_remove);
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
123562306a36Sopenharmony_cistatic int w5100_suspend(struct device *dev)
123662306a36Sopenharmony_ci{
123762306a36Sopenharmony_ci	struct net_device *ndev = dev_get_drvdata(dev);
123862306a36Sopenharmony_ci	struct w5100_priv *priv = netdev_priv(ndev);
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	if (netif_running(ndev)) {
124162306a36Sopenharmony_ci		netif_carrier_off(ndev);
124262306a36Sopenharmony_ci		netif_device_detach(ndev);
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci		w5100_hw_close(priv);
124562306a36Sopenharmony_ci	}
124662306a36Sopenharmony_ci	return 0;
124762306a36Sopenharmony_ci}
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_cistatic int w5100_resume(struct device *dev)
125062306a36Sopenharmony_ci{
125162306a36Sopenharmony_ci	struct net_device *ndev = dev_get_drvdata(dev);
125262306a36Sopenharmony_ci	struct w5100_priv *priv = netdev_priv(ndev);
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	if (netif_running(ndev)) {
125562306a36Sopenharmony_ci		w5100_hw_reset(priv);
125662306a36Sopenharmony_ci		w5100_hw_start(priv);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci		netif_device_attach(ndev);
125962306a36Sopenharmony_ci		if (!gpio_is_valid(priv->link_gpio) ||
126062306a36Sopenharmony_ci		    gpio_get_value(priv->link_gpio) != 0)
126162306a36Sopenharmony_ci			netif_carrier_on(ndev);
126262306a36Sopenharmony_ci	}
126362306a36Sopenharmony_ci	return 0;
126462306a36Sopenharmony_ci}
126562306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ciSIMPLE_DEV_PM_OPS(w5100_pm_ops, w5100_suspend, w5100_resume);
126862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(w5100_pm_ops);
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_cistatic struct platform_driver w5100_mmio_driver = {
127162306a36Sopenharmony_ci	.driver		= {
127262306a36Sopenharmony_ci		.name	= DRV_NAME,
127362306a36Sopenharmony_ci		.pm	= &w5100_pm_ops,
127462306a36Sopenharmony_ci	},
127562306a36Sopenharmony_ci	.probe		= w5100_mmio_probe,
127662306a36Sopenharmony_ci	.remove		= w5100_mmio_remove,
127762306a36Sopenharmony_ci};
127862306a36Sopenharmony_cimodule_platform_driver(w5100_mmio_driver);
1279