162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2006, 2007 Eugene Konev
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/interrupt.h>
962306a36Sopenharmony_ci#include <linux/moduleparam.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/sched.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/errno.h>
1562306a36Sopenharmony_ci#include <linux/types.h>
1662306a36Sopenharmony_ci#include <linux/delay.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/netdevice.h>
1962306a36Sopenharmony_ci#include <linux/if_vlan.h>
2062306a36Sopenharmony_ci#include <linux/etherdevice.h>
2162306a36Sopenharmony_ci#include <linux/ethtool.h>
2262306a36Sopenharmony_ci#include <linux/skbuff.h>
2362306a36Sopenharmony_ci#include <linux/mii.h>
2462306a36Sopenharmony_ci#include <linux/phy.h>
2562306a36Sopenharmony_ci#include <linux/phy_fixed.h>
2662306a36Sopenharmony_ci#include <linux/platform_device.h>
2762306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2862306a36Sopenharmony_ci#include <linux/clk.h>
2962306a36Sopenharmony_ci#include <linux/gpio.h>
3062306a36Sopenharmony_ci#include <linux/atomic.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include <asm/mach-ar7/ar7.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ciMODULE_AUTHOR("Eugene Konev <ejka@imfi.kspu.ru>");
3562306a36Sopenharmony_ciMODULE_DESCRIPTION("TI AR7 ethernet driver (CPMAC)");
3662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3762306a36Sopenharmony_ciMODULE_ALIAS("platform:cpmac");
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic int debug_level = 8;
4062306a36Sopenharmony_cistatic int dumb_switch;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* Next 2 are only used in cpmac_probe, so it's pointless to change them */
4362306a36Sopenharmony_cimodule_param(debug_level, int, 0444);
4462306a36Sopenharmony_cimodule_param(dumb_switch, int, 0444);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ciMODULE_PARM_DESC(debug_level, "Number of NETIF_MSG bits to enable");
4762306a36Sopenharmony_ciMODULE_PARM_DESC(dumb_switch, "Assume switch is not connected to MDIO bus");
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define CPMAC_VERSION "0.5.2"
5062306a36Sopenharmony_ci/* frame size + 802.1q tag + FCS size */
5162306a36Sopenharmony_ci#define CPMAC_SKB_SIZE		(ETH_FRAME_LEN + ETH_FCS_LEN + VLAN_HLEN)
5262306a36Sopenharmony_ci#define CPMAC_QUEUES	8
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/* Ethernet registers */
5562306a36Sopenharmony_ci#define CPMAC_TX_CONTROL		0x0004
5662306a36Sopenharmony_ci#define CPMAC_TX_TEARDOWN		0x0008
5762306a36Sopenharmony_ci#define CPMAC_RX_CONTROL		0x0014
5862306a36Sopenharmony_ci#define CPMAC_RX_TEARDOWN		0x0018
5962306a36Sopenharmony_ci#define CPMAC_MBP			0x0100
6062306a36Sopenharmony_ci#define MBP_RXPASSCRC			0x40000000
6162306a36Sopenharmony_ci#define MBP_RXQOS			0x20000000
6262306a36Sopenharmony_ci#define MBP_RXNOCHAIN			0x10000000
6362306a36Sopenharmony_ci#define MBP_RXCMF			0x01000000
6462306a36Sopenharmony_ci#define MBP_RXSHORT			0x00800000
6562306a36Sopenharmony_ci#define MBP_RXCEF			0x00400000
6662306a36Sopenharmony_ci#define MBP_RXPROMISC			0x00200000
6762306a36Sopenharmony_ci#define MBP_PROMISCCHAN(channel)	(((channel) & 0x7) << 16)
6862306a36Sopenharmony_ci#define MBP_RXBCAST			0x00002000
6962306a36Sopenharmony_ci#define MBP_BCASTCHAN(channel)		(((channel) & 0x7) << 8)
7062306a36Sopenharmony_ci#define MBP_RXMCAST			0x00000020
7162306a36Sopenharmony_ci#define MBP_MCASTCHAN(channel)		((channel) & 0x7)
7262306a36Sopenharmony_ci#define CPMAC_UNICAST_ENABLE		0x0104
7362306a36Sopenharmony_ci#define CPMAC_UNICAST_CLEAR		0x0108
7462306a36Sopenharmony_ci#define CPMAC_MAX_LENGTH		0x010c
7562306a36Sopenharmony_ci#define CPMAC_BUFFER_OFFSET		0x0110
7662306a36Sopenharmony_ci#define CPMAC_MAC_CONTROL		0x0160
7762306a36Sopenharmony_ci#define MAC_TXPTYPE			0x00000200
7862306a36Sopenharmony_ci#define MAC_TXPACE			0x00000040
7962306a36Sopenharmony_ci#define MAC_MII				0x00000020
8062306a36Sopenharmony_ci#define MAC_TXFLOW			0x00000010
8162306a36Sopenharmony_ci#define MAC_RXFLOW			0x00000008
8262306a36Sopenharmony_ci#define MAC_MTEST			0x00000004
8362306a36Sopenharmony_ci#define MAC_LOOPBACK			0x00000002
8462306a36Sopenharmony_ci#define MAC_FDX				0x00000001
8562306a36Sopenharmony_ci#define CPMAC_MAC_STATUS		0x0164
8662306a36Sopenharmony_ci#define MAC_STATUS_QOS			0x00000004
8762306a36Sopenharmony_ci#define MAC_STATUS_RXFLOW		0x00000002
8862306a36Sopenharmony_ci#define MAC_STATUS_TXFLOW		0x00000001
8962306a36Sopenharmony_ci#define CPMAC_TX_INT_ENABLE		0x0178
9062306a36Sopenharmony_ci#define CPMAC_TX_INT_CLEAR		0x017c
9162306a36Sopenharmony_ci#define CPMAC_MAC_INT_VECTOR		0x0180
9262306a36Sopenharmony_ci#define MAC_INT_STATUS			0x00080000
9362306a36Sopenharmony_ci#define MAC_INT_HOST			0x00040000
9462306a36Sopenharmony_ci#define MAC_INT_RX			0x00020000
9562306a36Sopenharmony_ci#define MAC_INT_TX			0x00010000
9662306a36Sopenharmony_ci#define CPMAC_MAC_EOI_VECTOR		0x0184
9762306a36Sopenharmony_ci#define CPMAC_RX_INT_ENABLE		0x0198
9862306a36Sopenharmony_ci#define CPMAC_RX_INT_CLEAR		0x019c
9962306a36Sopenharmony_ci#define CPMAC_MAC_INT_ENABLE		0x01a8
10062306a36Sopenharmony_ci#define CPMAC_MAC_INT_CLEAR		0x01ac
10162306a36Sopenharmony_ci#define CPMAC_MAC_ADDR_LO(channel)	(0x01b0 + (channel) * 4)
10262306a36Sopenharmony_ci#define CPMAC_MAC_ADDR_MID		0x01d0
10362306a36Sopenharmony_ci#define CPMAC_MAC_ADDR_HI		0x01d4
10462306a36Sopenharmony_ci#define CPMAC_MAC_HASH_LO		0x01d8
10562306a36Sopenharmony_ci#define CPMAC_MAC_HASH_HI		0x01dc
10662306a36Sopenharmony_ci#define CPMAC_TX_PTR(channel)		(0x0600 + (channel) * 4)
10762306a36Sopenharmony_ci#define CPMAC_RX_PTR(channel)		(0x0620 + (channel) * 4)
10862306a36Sopenharmony_ci#define CPMAC_TX_ACK(channel)		(0x0640 + (channel) * 4)
10962306a36Sopenharmony_ci#define CPMAC_RX_ACK(channel)		(0x0660 + (channel) * 4)
11062306a36Sopenharmony_ci#define CPMAC_REG_END			0x0680
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/* Rx/Tx statistics
11362306a36Sopenharmony_ci * TODO: use some of them to fill stats in cpmac_stats()
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_ci#define CPMAC_STATS_RX_GOOD		0x0200
11662306a36Sopenharmony_ci#define CPMAC_STATS_RX_BCAST		0x0204
11762306a36Sopenharmony_ci#define CPMAC_STATS_RX_MCAST		0x0208
11862306a36Sopenharmony_ci#define CPMAC_STATS_RX_PAUSE		0x020c
11962306a36Sopenharmony_ci#define CPMAC_STATS_RX_CRC		0x0210
12062306a36Sopenharmony_ci#define CPMAC_STATS_RX_ALIGN		0x0214
12162306a36Sopenharmony_ci#define CPMAC_STATS_RX_OVER		0x0218
12262306a36Sopenharmony_ci#define CPMAC_STATS_RX_JABBER		0x021c
12362306a36Sopenharmony_ci#define CPMAC_STATS_RX_UNDER		0x0220
12462306a36Sopenharmony_ci#define CPMAC_STATS_RX_FRAG		0x0224
12562306a36Sopenharmony_ci#define CPMAC_STATS_RX_FILTER		0x0228
12662306a36Sopenharmony_ci#define CPMAC_STATS_RX_QOSFILTER	0x022c
12762306a36Sopenharmony_ci#define CPMAC_STATS_RX_OCTETS		0x0230
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci#define CPMAC_STATS_TX_GOOD		0x0234
13062306a36Sopenharmony_ci#define CPMAC_STATS_TX_BCAST		0x0238
13162306a36Sopenharmony_ci#define CPMAC_STATS_TX_MCAST		0x023c
13262306a36Sopenharmony_ci#define CPMAC_STATS_TX_PAUSE		0x0240
13362306a36Sopenharmony_ci#define CPMAC_STATS_TX_DEFER		0x0244
13462306a36Sopenharmony_ci#define CPMAC_STATS_TX_COLLISION	0x0248
13562306a36Sopenharmony_ci#define CPMAC_STATS_TX_SINGLECOLL	0x024c
13662306a36Sopenharmony_ci#define CPMAC_STATS_TX_MULTICOLL	0x0250
13762306a36Sopenharmony_ci#define CPMAC_STATS_TX_EXCESSCOLL	0x0254
13862306a36Sopenharmony_ci#define CPMAC_STATS_TX_LATECOLL		0x0258
13962306a36Sopenharmony_ci#define CPMAC_STATS_TX_UNDERRUN		0x025c
14062306a36Sopenharmony_ci#define CPMAC_STATS_TX_CARRIERSENSE	0x0260
14162306a36Sopenharmony_ci#define CPMAC_STATS_TX_OCTETS		0x0264
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci#define cpmac_read(base, reg)		(readl((void __iomem *)(base) + (reg)))
14462306a36Sopenharmony_ci#define cpmac_write(base, reg, val)	(writel(val, (void __iomem *)(base) + \
14562306a36Sopenharmony_ci						(reg)))
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/* MDIO bus */
14862306a36Sopenharmony_ci#define CPMAC_MDIO_VERSION		0x0000
14962306a36Sopenharmony_ci#define CPMAC_MDIO_CONTROL		0x0004
15062306a36Sopenharmony_ci#define MDIOC_IDLE			0x80000000
15162306a36Sopenharmony_ci#define MDIOC_ENABLE			0x40000000
15262306a36Sopenharmony_ci#define MDIOC_PREAMBLE			0x00100000
15362306a36Sopenharmony_ci#define MDIOC_FAULT			0x00080000
15462306a36Sopenharmony_ci#define MDIOC_FAULTDETECT		0x00040000
15562306a36Sopenharmony_ci#define MDIOC_INTTEST			0x00020000
15662306a36Sopenharmony_ci#define MDIOC_CLKDIV(div)		((div) & 0xff)
15762306a36Sopenharmony_ci#define CPMAC_MDIO_ALIVE		0x0008
15862306a36Sopenharmony_ci#define CPMAC_MDIO_LINK			0x000c
15962306a36Sopenharmony_ci#define CPMAC_MDIO_ACCESS(channel)	(0x0080 + (channel) * 8)
16062306a36Sopenharmony_ci#define MDIO_BUSY			0x80000000
16162306a36Sopenharmony_ci#define MDIO_WRITE			0x40000000
16262306a36Sopenharmony_ci#define MDIO_REG(reg)			(((reg) & 0x1f) << 21)
16362306a36Sopenharmony_ci#define MDIO_PHY(phy)			(((phy) & 0x1f) << 16)
16462306a36Sopenharmony_ci#define MDIO_DATA(data)			((data) & 0xffff)
16562306a36Sopenharmony_ci#define CPMAC_MDIO_PHYSEL(channel)	(0x0084 + (channel) * 8)
16662306a36Sopenharmony_ci#define PHYSEL_LINKSEL			0x00000040
16762306a36Sopenharmony_ci#define PHYSEL_LINKINT			0x00000020
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistruct cpmac_desc {
17062306a36Sopenharmony_ci	u32 hw_next;
17162306a36Sopenharmony_ci	u32 hw_data;
17262306a36Sopenharmony_ci	u16 buflen;
17362306a36Sopenharmony_ci	u16 bufflags;
17462306a36Sopenharmony_ci	u16 datalen;
17562306a36Sopenharmony_ci	u16 dataflags;
17662306a36Sopenharmony_ci#define CPMAC_SOP			0x8000
17762306a36Sopenharmony_ci#define CPMAC_EOP			0x4000
17862306a36Sopenharmony_ci#define CPMAC_OWN			0x2000
17962306a36Sopenharmony_ci#define CPMAC_EOQ			0x1000
18062306a36Sopenharmony_ci	struct sk_buff *skb;
18162306a36Sopenharmony_ci	struct cpmac_desc *next;
18262306a36Sopenharmony_ci	struct cpmac_desc *prev;
18362306a36Sopenharmony_ci	dma_addr_t mapping;
18462306a36Sopenharmony_ci	dma_addr_t data_mapping;
18562306a36Sopenharmony_ci};
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistruct cpmac_priv {
18862306a36Sopenharmony_ci	spinlock_t lock;
18962306a36Sopenharmony_ci	spinlock_t rx_lock;
19062306a36Sopenharmony_ci	struct cpmac_desc *rx_head;
19162306a36Sopenharmony_ci	int ring_size;
19262306a36Sopenharmony_ci	struct cpmac_desc *desc_ring;
19362306a36Sopenharmony_ci	dma_addr_t dma_ring;
19462306a36Sopenharmony_ci	void __iomem *regs;
19562306a36Sopenharmony_ci	struct mii_bus *mii_bus;
19662306a36Sopenharmony_ci	char phy_name[MII_BUS_ID_SIZE + 3];
19762306a36Sopenharmony_ci	int oldlink, oldspeed, oldduplex;
19862306a36Sopenharmony_ci	u32 msg_enable;
19962306a36Sopenharmony_ci	struct net_device *dev;
20062306a36Sopenharmony_ci	struct work_struct reset_work;
20162306a36Sopenharmony_ci	struct platform_device *pdev;
20262306a36Sopenharmony_ci	struct napi_struct napi;
20362306a36Sopenharmony_ci	atomic_t reset_pending;
20462306a36Sopenharmony_ci};
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic irqreturn_t cpmac_irq(int, void *);
20762306a36Sopenharmony_cistatic void cpmac_hw_start(struct net_device *dev);
20862306a36Sopenharmony_cistatic void cpmac_hw_stop(struct net_device *dev);
20962306a36Sopenharmony_cistatic int cpmac_stop(struct net_device *dev);
21062306a36Sopenharmony_cistatic int cpmac_open(struct net_device *dev);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic void cpmac_dump_regs(struct net_device *dev)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	int i;
21562306a36Sopenharmony_ci	struct cpmac_priv *priv = netdev_priv(dev);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	for (i = 0; i < CPMAC_REG_END; i += 4) {
21862306a36Sopenharmony_ci		if (i % 16 == 0) {
21962306a36Sopenharmony_ci			if (i)
22062306a36Sopenharmony_ci				printk("\n");
22162306a36Sopenharmony_ci			printk("%s: reg[%p]:", dev->name, priv->regs + i);
22262306a36Sopenharmony_ci		}
22362306a36Sopenharmony_ci		printk(" %08x", cpmac_read(priv->regs, i));
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci	printk("\n");
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic void cpmac_dump_desc(struct net_device *dev, struct cpmac_desc *desc)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	int i;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	printk("%s: desc[%p]:", dev->name, desc);
23362306a36Sopenharmony_ci	for (i = 0; i < sizeof(*desc) / 4; i++)
23462306a36Sopenharmony_ci		printk(" %08x", ((u32 *)desc)[i]);
23562306a36Sopenharmony_ci	printk("\n");
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic void cpmac_dump_all_desc(struct net_device *dev)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	struct cpmac_priv *priv = netdev_priv(dev);
24162306a36Sopenharmony_ci	struct cpmac_desc *dump = priv->rx_head;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	do {
24462306a36Sopenharmony_ci		cpmac_dump_desc(dev, dump);
24562306a36Sopenharmony_ci		dump = dump->next;
24662306a36Sopenharmony_ci	} while (dump != priv->rx_head);
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic void cpmac_dump_skb(struct net_device *dev, struct sk_buff *skb)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	int i;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	printk("%s: skb 0x%p, len=%d\n", dev->name, skb, skb->len);
25462306a36Sopenharmony_ci	for (i = 0; i < skb->len; i++) {
25562306a36Sopenharmony_ci		if (i % 16 == 0) {
25662306a36Sopenharmony_ci			if (i)
25762306a36Sopenharmony_ci				printk("\n");
25862306a36Sopenharmony_ci			printk("%s: data[%p]:", dev->name, skb->data + i);
25962306a36Sopenharmony_ci		}
26062306a36Sopenharmony_ci		printk(" %02x", ((u8 *)skb->data)[i]);
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci	printk("\n");
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic int cpmac_mdio_read(struct mii_bus *bus, int phy_id, int reg)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	u32 val;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	while (cpmac_read(bus->priv, CPMAC_MDIO_ACCESS(0)) & MDIO_BUSY)
27062306a36Sopenharmony_ci		cpu_relax();
27162306a36Sopenharmony_ci	cpmac_write(bus->priv, CPMAC_MDIO_ACCESS(0), MDIO_BUSY | MDIO_REG(reg) |
27262306a36Sopenharmony_ci		    MDIO_PHY(phy_id));
27362306a36Sopenharmony_ci	while ((val = cpmac_read(bus->priv, CPMAC_MDIO_ACCESS(0))) & MDIO_BUSY)
27462306a36Sopenharmony_ci		cpu_relax();
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	return MDIO_DATA(val);
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic int cpmac_mdio_write(struct mii_bus *bus, int phy_id,
28062306a36Sopenharmony_ci			    int reg, u16 val)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	while (cpmac_read(bus->priv, CPMAC_MDIO_ACCESS(0)) & MDIO_BUSY)
28362306a36Sopenharmony_ci		cpu_relax();
28462306a36Sopenharmony_ci	cpmac_write(bus->priv, CPMAC_MDIO_ACCESS(0), MDIO_BUSY | MDIO_WRITE |
28562306a36Sopenharmony_ci		    MDIO_REG(reg) | MDIO_PHY(phy_id) | MDIO_DATA(val));
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	return 0;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic int cpmac_mdio_reset(struct mii_bus *bus)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	struct clk *cpmac_clk;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	cpmac_clk = clk_get(&bus->dev, "cpmac");
29562306a36Sopenharmony_ci	if (IS_ERR(cpmac_clk)) {
29662306a36Sopenharmony_ci		pr_err("unable to get cpmac clock\n");
29762306a36Sopenharmony_ci		return -1;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci	ar7_device_reset(AR7_RESET_BIT_MDIO);
30062306a36Sopenharmony_ci	cpmac_write(bus->priv, CPMAC_MDIO_CONTROL, MDIOC_ENABLE |
30162306a36Sopenharmony_ci		    MDIOC_CLKDIV(clk_get_rate(cpmac_clk) / 2200000 - 1));
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	return 0;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic struct mii_bus *cpmac_mii;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic void cpmac_set_multicast_list(struct net_device *dev)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
31162306a36Sopenharmony_ci	u8 tmp;
31262306a36Sopenharmony_ci	u32 mbp, bit, hash[2] = { 0, };
31362306a36Sopenharmony_ci	struct cpmac_priv *priv = netdev_priv(dev);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	mbp = cpmac_read(priv->regs, CPMAC_MBP);
31662306a36Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {
31762306a36Sopenharmony_ci		cpmac_write(priv->regs, CPMAC_MBP, (mbp & ~MBP_PROMISCCHAN(0)) |
31862306a36Sopenharmony_ci			    MBP_RXPROMISC);
31962306a36Sopenharmony_ci	} else {
32062306a36Sopenharmony_ci		cpmac_write(priv->regs, CPMAC_MBP, mbp & ~MBP_RXPROMISC);
32162306a36Sopenharmony_ci		if (dev->flags & IFF_ALLMULTI) {
32262306a36Sopenharmony_ci			/* enable all multicast mode */
32362306a36Sopenharmony_ci			cpmac_write(priv->regs, CPMAC_MAC_HASH_LO, 0xffffffff);
32462306a36Sopenharmony_ci			cpmac_write(priv->regs, CPMAC_MAC_HASH_HI, 0xffffffff);
32562306a36Sopenharmony_ci		} else {
32662306a36Sopenharmony_ci			/* cpmac uses some strange mac address hashing
32762306a36Sopenharmony_ci			 * (not crc32)
32862306a36Sopenharmony_ci			 */
32962306a36Sopenharmony_ci			netdev_for_each_mc_addr(ha, dev) {
33062306a36Sopenharmony_ci				bit = 0;
33162306a36Sopenharmony_ci				tmp = ha->addr[0];
33262306a36Sopenharmony_ci				bit  ^= (tmp >> 2) ^ (tmp << 4);
33362306a36Sopenharmony_ci				tmp = ha->addr[1];
33462306a36Sopenharmony_ci				bit  ^= (tmp >> 4) ^ (tmp << 2);
33562306a36Sopenharmony_ci				tmp = ha->addr[2];
33662306a36Sopenharmony_ci				bit  ^= (tmp >> 6) ^ tmp;
33762306a36Sopenharmony_ci				tmp = ha->addr[3];
33862306a36Sopenharmony_ci				bit  ^= (tmp >> 2) ^ (tmp << 4);
33962306a36Sopenharmony_ci				tmp = ha->addr[4];
34062306a36Sopenharmony_ci				bit  ^= (tmp >> 4) ^ (tmp << 2);
34162306a36Sopenharmony_ci				tmp = ha->addr[5];
34262306a36Sopenharmony_ci				bit  ^= (tmp >> 6) ^ tmp;
34362306a36Sopenharmony_ci				bit &= 0x3f;
34462306a36Sopenharmony_ci				hash[bit / 32] |= 1 << (bit % 32);
34562306a36Sopenharmony_ci			}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci			cpmac_write(priv->regs, CPMAC_MAC_HASH_LO, hash[0]);
34862306a36Sopenharmony_ci			cpmac_write(priv->regs, CPMAC_MAC_HASH_HI, hash[1]);
34962306a36Sopenharmony_ci		}
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic struct sk_buff *cpmac_rx_one(struct cpmac_priv *priv,
35462306a36Sopenharmony_ci				    struct cpmac_desc *desc)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	struct sk_buff *skb, *result = NULL;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	if (unlikely(netif_msg_hw(priv)))
35962306a36Sopenharmony_ci		cpmac_dump_desc(priv->dev, desc);
36062306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_RX_ACK(0), (u32)desc->mapping);
36162306a36Sopenharmony_ci	if (unlikely(!desc->datalen)) {
36262306a36Sopenharmony_ci		if (netif_msg_rx_err(priv) && net_ratelimit())
36362306a36Sopenharmony_ci			netdev_warn(priv->dev, "rx: spurious interrupt\n");
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		return NULL;
36662306a36Sopenharmony_ci	}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	skb = netdev_alloc_skb_ip_align(priv->dev, CPMAC_SKB_SIZE);
36962306a36Sopenharmony_ci	if (likely(skb)) {
37062306a36Sopenharmony_ci		skb_put(desc->skb, desc->datalen);
37162306a36Sopenharmony_ci		desc->skb->protocol = eth_type_trans(desc->skb, priv->dev);
37262306a36Sopenharmony_ci		skb_checksum_none_assert(desc->skb);
37362306a36Sopenharmony_ci		priv->dev->stats.rx_packets++;
37462306a36Sopenharmony_ci		priv->dev->stats.rx_bytes += desc->datalen;
37562306a36Sopenharmony_ci		result = desc->skb;
37662306a36Sopenharmony_ci		dma_unmap_single(&priv->dev->dev, desc->data_mapping,
37762306a36Sopenharmony_ci				 CPMAC_SKB_SIZE, DMA_FROM_DEVICE);
37862306a36Sopenharmony_ci		desc->skb = skb;
37962306a36Sopenharmony_ci		desc->data_mapping = dma_map_single(&priv->dev->dev, skb->data,
38062306a36Sopenharmony_ci						    CPMAC_SKB_SIZE,
38162306a36Sopenharmony_ci						    DMA_FROM_DEVICE);
38262306a36Sopenharmony_ci		desc->hw_data = (u32)desc->data_mapping;
38362306a36Sopenharmony_ci		if (unlikely(netif_msg_pktdata(priv))) {
38462306a36Sopenharmony_ci			netdev_dbg(priv->dev, "received packet:\n");
38562306a36Sopenharmony_ci			cpmac_dump_skb(priv->dev, result);
38662306a36Sopenharmony_ci		}
38762306a36Sopenharmony_ci	} else {
38862306a36Sopenharmony_ci		if (netif_msg_rx_err(priv) && net_ratelimit())
38962306a36Sopenharmony_ci			netdev_warn(priv->dev,
39062306a36Sopenharmony_ci				    "low on skbs, dropping packet\n");
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci		priv->dev->stats.rx_dropped++;
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	desc->buflen = CPMAC_SKB_SIZE;
39662306a36Sopenharmony_ci	desc->dataflags = CPMAC_OWN;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return result;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic int cpmac_poll(struct napi_struct *napi, int budget)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	struct sk_buff *skb;
40462306a36Sopenharmony_ci	struct cpmac_desc *desc, *restart;
40562306a36Sopenharmony_ci	struct cpmac_priv *priv = container_of(napi, struct cpmac_priv, napi);
40662306a36Sopenharmony_ci	int received = 0, processed = 0;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	spin_lock(&priv->rx_lock);
40962306a36Sopenharmony_ci	if (unlikely(!priv->rx_head)) {
41062306a36Sopenharmony_ci		if (netif_msg_rx_err(priv) && net_ratelimit())
41162306a36Sopenharmony_ci			netdev_warn(priv->dev, "rx: polling, but no queue\n");
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci		spin_unlock(&priv->rx_lock);
41462306a36Sopenharmony_ci		napi_complete(napi);
41562306a36Sopenharmony_ci		return 0;
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	desc = priv->rx_head;
41962306a36Sopenharmony_ci	restart = NULL;
42062306a36Sopenharmony_ci	while (((desc->dataflags & CPMAC_OWN) == 0) && (received < budget)) {
42162306a36Sopenharmony_ci		processed++;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci		if ((desc->dataflags & CPMAC_EOQ) != 0) {
42462306a36Sopenharmony_ci			/* The last update to eoq->hw_next didn't happen
42562306a36Sopenharmony_ci			 * soon enough, and the receiver stopped here.
42662306a36Sopenharmony_ci			 * Remember this descriptor so we can restart
42762306a36Sopenharmony_ci			 * the receiver after freeing some space.
42862306a36Sopenharmony_ci			 */
42962306a36Sopenharmony_ci			if (unlikely(restart)) {
43062306a36Sopenharmony_ci				if (netif_msg_rx_err(priv))
43162306a36Sopenharmony_ci					netdev_err(priv->dev, "poll found a"
43262306a36Sopenharmony_ci						   " duplicate EOQ: %p and %p\n",
43362306a36Sopenharmony_ci						   restart, desc);
43462306a36Sopenharmony_ci				goto fatal_error;
43562306a36Sopenharmony_ci			}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci			restart = desc->next;
43862306a36Sopenharmony_ci		}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci		skb = cpmac_rx_one(priv, desc);
44162306a36Sopenharmony_ci		if (likely(skb)) {
44262306a36Sopenharmony_ci			netif_receive_skb(skb);
44362306a36Sopenharmony_ci			received++;
44462306a36Sopenharmony_ci		}
44562306a36Sopenharmony_ci		desc = desc->next;
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	if (desc != priv->rx_head) {
44962306a36Sopenharmony_ci		/* We freed some buffers, but not the whole ring,
45062306a36Sopenharmony_ci		 * add what we did free to the rx list
45162306a36Sopenharmony_ci		 */
45262306a36Sopenharmony_ci		desc->prev->hw_next = (u32)0;
45362306a36Sopenharmony_ci		priv->rx_head->prev->hw_next = priv->rx_head->mapping;
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	/* Optimization: If we did not actually process an EOQ (perhaps because
45762306a36Sopenharmony_ci	 * of quota limits), check to see if the tail of the queue has EOQ set.
45862306a36Sopenharmony_ci	 * We should immediately restart in that case so that the receiver can
45962306a36Sopenharmony_ci	 * restart and run in parallel with more packet processing.
46062306a36Sopenharmony_ci	 * This lets us handle slightly larger bursts before running
46162306a36Sopenharmony_ci	 * out of ring space (assuming dev->weight < ring_size)
46262306a36Sopenharmony_ci	 */
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	if (!restart &&
46562306a36Sopenharmony_ci	     (priv->rx_head->prev->dataflags & (CPMAC_OWN|CPMAC_EOQ))
46662306a36Sopenharmony_ci		    == CPMAC_EOQ &&
46762306a36Sopenharmony_ci	     (priv->rx_head->dataflags & CPMAC_OWN) != 0) {
46862306a36Sopenharmony_ci		/* reset EOQ so the poll loop (above) doesn't try to
46962306a36Sopenharmony_ci		 * restart this when it eventually gets to this descriptor.
47062306a36Sopenharmony_ci		 */
47162306a36Sopenharmony_ci		priv->rx_head->prev->dataflags &= ~CPMAC_EOQ;
47262306a36Sopenharmony_ci		restart = priv->rx_head;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	if (restart) {
47662306a36Sopenharmony_ci		priv->dev->stats.rx_errors++;
47762306a36Sopenharmony_ci		priv->dev->stats.rx_fifo_errors++;
47862306a36Sopenharmony_ci		if (netif_msg_rx_err(priv) && net_ratelimit())
47962306a36Sopenharmony_ci			netdev_warn(priv->dev, "rx dma ring overrun\n");
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci		if (unlikely((restart->dataflags & CPMAC_OWN) == 0)) {
48262306a36Sopenharmony_ci			if (netif_msg_drv(priv))
48362306a36Sopenharmony_ci				netdev_err(priv->dev, "cpmac_poll is trying "
48462306a36Sopenharmony_ci					"to restart rx from a descriptor "
48562306a36Sopenharmony_ci					"that's not free: %p\n", restart);
48662306a36Sopenharmony_ci			goto fatal_error;
48762306a36Sopenharmony_ci		}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci		cpmac_write(priv->regs, CPMAC_RX_PTR(0), restart->mapping);
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	priv->rx_head = desc;
49362306a36Sopenharmony_ci	spin_unlock(&priv->rx_lock);
49462306a36Sopenharmony_ci	if (unlikely(netif_msg_rx_status(priv)))
49562306a36Sopenharmony_ci		netdev_dbg(priv->dev, "poll processed %d packets\n", received);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (processed == 0) {
49862306a36Sopenharmony_ci		/* we ran out of packets to read,
49962306a36Sopenharmony_ci		 * revert to interrupt-driven mode
50062306a36Sopenharmony_ci		 */
50162306a36Sopenharmony_ci		napi_complete(napi);
50262306a36Sopenharmony_ci		cpmac_write(priv->regs, CPMAC_RX_INT_ENABLE, 1);
50362306a36Sopenharmony_ci		return 0;
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	return 1;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_cifatal_error:
50962306a36Sopenharmony_ci	/* Something went horribly wrong.
51062306a36Sopenharmony_ci	 * Reset hardware to try to recover rather than wedging.
51162306a36Sopenharmony_ci	 */
51262306a36Sopenharmony_ci	if (netif_msg_drv(priv)) {
51362306a36Sopenharmony_ci		netdev_err(priv->dev, "cpmac_poll is confused. "
51462306a36Sopenharmony_ci			   "Resetting hardware\n");
51562306a36Sopenharmony_ci		cpmac_dump_all_desc(priv->dev);
51662306a36Sopenharmony_ci		netdev_dbg(priv->dev, "RX_PTR(0)=0x%08x RX_ACK(0)=0x%08x\n",
51762306a36Sopenharmony_ci			   cpmac_read(priv->regs, CPMAC_RX_PTR(0)),
51862306a36Sopenharmony_ci			   cpmac_read(priv->regs, CPMAC_RX_ACK(0)));
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	spin_unlock(&priv->rx_lock);
52262306a36Sopenharmony_ci	napi_complete(napi);
52362306a36Sopenharmony_ci	netif_tx_stop_all_queues(priv->dev);
52462306a36Sopenharmony_ci	napi_disable(&priv->napi);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	atomic_inc(&priv->reset_pending);
52762306a36Sopenharmony_ci	cpmac_hw_stop(priv->dev);
52862306a36Sopenharmony_ci	if (!schedule_work(&priv->reset_work))
52962306a36Sopenharmony_ci		atomic_dec(&priv->reset_pending);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	return 0;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic netdev_tx_t cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	int queue;
53862306a36Sopenharmony_ci	unsigned int len;
53962306a36Sopenharmony_ci	struct cpmac_desc *desc;
54062306a36Sopenharmony_ci	struct cpmac_priv *priv = netdev_priv(dev);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	if (unlikely(atomic_read(&priv->reset_pending)))
54362306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	if (unlikely(skb_padto(skb, ETH_ZLEN)))
54662306a36Sopenharmony_ci		return NETDEV_TX_OK;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	len = max_t(unsigned int, skb->len, ETH_ZLEN);
54962306a36Sopenharmony_ci	queue = skb_get_queue_mapping(skb);
55062306a36Sopenharmony_ci	netif_stop_subqueue(dev, queue);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	desc = &priv->desc_ring[queue];
55362306a36Sopenharmony_ci	if (unlikely(desc->dataflags & CPMAC_OWN)) {
55462306a36Sopenharmony_ci		if (netif_msg_tx_err(priv) && net_ratelimit())
55562306a36Sopenharmony_ci			netdev_warn(dev, "tx dma ring full\n");
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	spin_lock(&priv->lock);
56162306a36Sopenharmony_ci	spin_unlock(&priv->lock);
56262306a36Sopenharmony_ci	desc->dataflags = CPMAC_SOP | CPMAC_EOP | CPMAC_OWN;
56362306a36Sopenharmony_ci	desc->skb = skb;
56462306a36Sopenharmony_ci	desc->data_mapping = dma_map_single(&dev->dev, skb->data, len,
56562306a36Sopenharmony_ci					    DMA_TO_DEVICE);
56662306a36Sopenharmony_ci	desc->hw_data = (u32)desc->data_mapping;
56762306a36Sopenharmony_ci	desc->datalen = len;
56862306a36Sopenharmony_ci	desc->buflen = len;
56962306a36Sopenharmony_ci	if (unlikely(netif_msg_tx_queued(priv)))
57062306a36Sopenharmony_ci		netdev_dbg(dev, "sending 0x%p, len=%d\n", skb, skb->len);
57162306a36Sopenharmony_ci	if (unlikely(netif_msg_hw(priv)))
57262306a36Sopenharmony_ci		cpmac_dump_desc(dev, desc);
57362306a36Sopenharmony_ci	if (unlikely(netif_msg_pktdata(priv)))
57462306a36Sopenharmony_ci		cpmac_dump_skb(dev, skb);
57562306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_TX_PTR(queue), (u32)desc->mapping);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	return NETDEV_TX_OK;
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic void cpmac_end_xmit(struct net_device *dev, int queue)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	struct cpmac_desc *desc;
58362306a36Sopenharmony_ci	struct cpmac_priv *priv = netdev_priv(dev);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	desc = &priv->desc_ring[queue];
58662306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_TX_ACK(queue), (u32)desc->mapping);
58762306a36Sopenharmony_ci	if (likely(desc->skb)) {
58862306a36Sopenharmony_ci		spin_lock(&priv->lock);
58962306a36Sopenharmony_ci		dev->stats.tx_packets++;
59062306a36Sopenharmony_ci		dev->stats.tx_bytes += desc->skb->len;
59162306a36Sopenharmony_ci		spin_unlock(&priv->lock);
59262306a36Sopenharmony_ci		dma_unmap_single(&dev->dev, desc->data_mapping, desc->skb->len,
59362306a36Sopenharmony_ci				 DMA_TO_DEVICE);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci		if (unlikely(netif_msg_tx_done(priv)))
59662306a36Sopenharmony_ci			netdev_dbg(dev, "sent 0x%p, len=%d\n",
59762306a36Sopenharmony_ci				   desc->skb, desc->skb->len);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci		dev_consume_skb_irq(desc->skb);
60062306a36Sopenharmony_ci		desc->skb = NULL;
60162306a36Sopenharmony_ci		if (__netif_subqueue_stopped(dev, queue))
60262306a36Sopenharmony_ci			netif_wake_subqueue(dev, queue);
60362306a36Sopenharmony_ci	} else {
60462306a36Sopenharmony_ci		if (netif_msg_tx_err(priv) && net_ratelimit())
60562306a36Sopenharmony_ci			netdev_warn(dev, "end_xmit: spurious interrupt\n");
60662306a36Sopenharmony_ci		if (__netif_subqueue_stopped(dev, queue))
60762306a36Sopenharmony_ci			netif_wake_subqueue(dev, queue);
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_cistatic void cpmac_hw_stop(struct net_device *dev)
61262306a36Sopenharmony_ci{
61362306a36Sopenharmony_ci	int i;
61462306a36Sopenharmony_ci	struct cpmac_priv *priv = netdev_priv(dev);
61562306a36Sopenharmony_ci	struct plat_cpmac_data *pdata = dev_get_platdata(&priv->pdev->dev);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	ar7_device_reset(pdata->reset_bit);
61862306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_RX_CONTROL,
61962306a36Sopenharmony_ci		    cpmac_read(priv->regs, CPMAC_RX_CONTROL) & ~1);
62062306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_TX_CONTROL,
62162306a36Sopenharmony_ci		    cpmac_read(priv->regs, CPMAC_TX_CONTROL) & ~1);
62262306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
62362306a36Sopenharmony_ci		cpmac_write(priv->regs, CPMAC_TX_PTR(i), 0);
62462306a36Sopenharmony_ci		cpmac_write(priv->regs, CPMAC_RX_PTR(i), 0);
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_UNICAST_CLEAR, 0xff);
62762306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_RX_INT_CLEAR, 0xff);
62862306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_TX_INT_CLEAR, 0xff);
62962306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_MAC_INT_CLEAR, 0xff);
63062306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_MAC_CONTROL,
63162306a36Sopenharmony_ci		    cpmac_read(priv->regs, CPMAC_MAC_CONTROL) & ~MAC_MII);
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_cistatic void cpmac_hw_start(struct net_device *dev)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	int i;
63762306a36Sopenharmony_ci	struct cpmac_priv *priv = netdev_priv(dev);
63862306a36Sopenharmony_ci	struct plat_cpmac_data *pdata = dev_get_platdata(&priv->pdev->dev);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	ar7_device_reset(pdata->reset_bit);
64162306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
64262306a36Sopenharmony_ci		cpmac_write(priv->regs, CPMAC_TX_PTR(i), 0);
64362306a36Sopenharmony_ci		cpmac_write(priv->regs, CPMAC_RX_PTR(i), 0);
64462306a36Sopenharmony_ci	}
64562306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_RX_PTR(0), priv->rx_head->mapping);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_MBP, MBP_RXSHORT | MBP_RXBCAST |
64862306a36Sopenharmony_ci		    MBP_RXMCAST);
64962306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_BUFFER_OFFSET, 0);
65062306a36Sopenharmony_ci	for (i = 0; i < 8; i++)
65162306a36Sopenharmony_ci		cpmac_write(priv->regs, CPMAC_MAC_ADDR_LO(i), dev->dev_addr[5]);
65262306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_MAC_ADDR_MID, dev->dev_addr[4]);
65362306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_MAC_ADDR_HI, dev->dev_addr[0] |
65462306a36Sopenharmony_ci		    (dev->dev_addr[1] << 8) | (dev->dev_addr[2] << 16) |
65562306a36Sopenharmony_ci		    (dev->dev_addr[3] << 24));
65662306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_MAX_LENGTH, CPMAC_SKB_SIZE);
65762306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_UNICAST_CLEAR, 0xff);
65862306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_RX_INT_CLEAR, 0xff);
65962306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_TX_INT_CLEAR, 0xff);
66062306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_MAC_INT_CLEAR, 0xff);
66162306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_UNICAST_ENABLE, 1);
66262306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_RX_INT_ENABLE, 1);
66362306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_TX_INT_ENABLE, 0xff);
66462306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_MAC_INT_ENABLE, 3);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_RX_CONTROL,
66762306a36Sopenharmony_ci		    cpmac_read(priv->regs, CPMAC_RX_CONTROL) | 1);
66862306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_TX_CONTROL,
66962306a36Sopenharmony_ci		    cpmac_read(priv->regs, CPMAC_TX_CONTROL) | 1);
67062306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_MAC_CONTROL,
67162306a36Sopenharmony_ci		    cpmac_read(priv->regs, CPMAC_MAC_CONTROL) | MAC_MII |
67262306a36Sopenharmony_ci		    MAC_FDX);
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_cistatic void cpmac_clear_rx(struct net_device *dev)
67662306a36Sopenharmony_ci{
67762306a36Sopenharmony_ci	struct cpmac_priv *priv = netdev_priv(dev);
67862306a36Sopenharmony_ci	struct cpmac_desc *desc;
67962306a36Sopenharmony_ci	int i;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	if (unlikely(!priv->rx_head))
68262306a36Sopenharmony_ci		return;
68362306a36Sopenharmony_ci	desc = priv->rx_head;
68462306a36Sopenharmony_ci	for (i = 0; i < priv->ring_size; i++) {
68562306a36Sopenharmony_ci		if ((desc->dataflags & CPMAC_OWN) == 0) {
68662306a36Sopenharmony_ci			if (netif_msg_rx_err(priv) && net_ratelimit())
68762306a36Sopenharmony_ci				netdev_warn(dev, "packet dropped\n");
68862306a36Sopenharmony_ci			if (unlikely(netif_msg_hw(priv)))
68962306a36Sopenharmony_ci				cpmac_dump_desc(dev, desc);
69062306a36Sopenharmony_ci			desc->dataflags = CPMAC_OWN;
69162306a36Sopenharmony_ci			dev->stats.rx_dropped++;
69262306a36Sopenharmony_ci		}
69362306a36Sopenharmony_ci		desc->hw_next = desc->next->mapping;
69462306a36Sopenharmony_ci		desc = desc->next;
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci	priv->rx_head->prev->hw_next = 0;
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cistatic void cpmac_clear_tx(struct net_device *dev)
70062306a36Sopenharmony_ci{
70162306a36Sopenharmony_ci	struct cpmac_priv *priv = netdev_priv(dev);
70262306a36Sopenharmony_ci	int i;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	if (unlikely(!priv->desc_ring))
70562306a36Sopenharmony_ci		return;
70662306a36Sopenharmony_ci	for (i = 0; i < CPMAC_QUEUES; i++) {
70762306a36Sopenharmony_ci		priv->desc_ring[i].dataflags = 0;
70862306a36Sopenharmony_ci		if (priv->desc_ring[i].skb) {
70962306a36Sopenharmony_ci			dev_kfree_skb_any(priv->desc_ring[i].skb);
71062306a36Sopenharmony_ci			priv->desc_ring[i].skb = NULL;
71162306a36Sopenharmony_ci		}
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic void cpmac_hw_error(struct work_struct *work)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	struct cpmac_priv *priv =
71862306a36Sopenharmony_ci		container_of(work, struct cpmac_priv, reset_work);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	spin_lock(&priv->rx_lock);
72162306a36Sopenharmony_ci	cpmac_clear_rx(priv->dev);
72262306a36Sopenharmony_ci	spin_unlock(&priv->rx_lock);
72362306a36Sopenharmony_ci	cpmac_clear_tx(priv->dev);
72462306a36Sopenharmony_ci	cpmac_hw_start(priv->dev);
72562306a36Sopenharmony_ci	barrier();
72662306a36Sopenharmony_ci	atomic_dec(&priv->reset_pending);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	netif_tx_wake_all_queues(priv->dev);
72962306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_MAC_INT_ENABLE, 3);
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_cistatic void cpmac_check_status(struct net_device *dev)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	struct cpmac_priv *priv = netdev_priv(dev);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	u32 macstatus = cpmac_read(priv->regs, CPMAC_MAC_STATUS);
73762306a36Sopenharmony_ci	int rx_channel = (macstatus >> 8) & 7;
73862306a36Sopenharmony_ci	int rx_code = (macstatus >> 12) & 15;
73962306a36Sopenharmony_ci	int tx_channel = (macstatus >> 16) & 7;
74062306a36Sopenharmony_ci	int tx_code = (macstatus >> 20) & 15;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	if (rx_code || tx_code) {
74362306a36Sopenharmony_ci		if (netif_msg_drv(priv) && net_ratelimit()) {
74462306a36Sopenharmony_ci			/* Can't find any documentation on what these
74562306a36Sopenharmony_ci			 * error codes actually are. So just log them and hope..
74662306a36Sopenharmony_ci			 */
74762306a36Sopenharmony_ci			if (rx_code)
74862306a36Sopenharmony_ci				netdev_warn(dev, "host error %d on rx "
74962306a36Sopenharmony_ci					"channel %d (macstatus %08x), resetting\n",
75062306a36Sopenharmony_ci					rx_code, rx_channel, macstatus);
75162306a36Sopenharmony_ci			if (tx_code)
75262306a36Sopenharmony_ci				netdev_warn(dev, "host error %d on tx "
75362306a36Sopenharmony_ci					"channel %d (macstatus %08x), resetting\n",
75462306a36Sopenharmony_ci					tx_code, tx_channel, macstatus);
75562306a36Sopenharmony_ci		}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci		netif_tx_stop_all_queues(dev);
75862306a36Sopenharmony_ci		cpmac_hw_stop(dev);
75962306a36Sopenharmony_ci		if (schedule_work(&priv->reset_work))
76062306a36Sopenharmony_ci			atomic_inc(&priv->reset_pending);
76162306a36Sopenharmony_ci		if (unlikely(netif_msg_hw(priv)))
76262306a36Sopenharmony_ci			cpmac_dump_regs(dev);
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_MAC_INT_CLEAR, 0xff);
76562306a36Sopenharmony_ci}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_cistatic irqreturn_t cpmac_irq(int irq, void *dev_id)
76862306a36Sopenharmony_ci{
76962306a36Sopenharmony_ci	struct net_device *dev = dev_id;
77062306a36Sopenharmony_ci	struct cpmac_priv *priv;
77162306a36Sopenharmony_ci	int queue;
77262306a36Sopenharmony_ci	u32 status;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	priv = netdev_priv(dev);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	status = cpmac_read(priv->regs, CPMAC_MAC_INT_VECTOR);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	if (unlikely(netif_msg_intr(priv)))
77962306a36Sopenharmony_ci		netdev_dbg(dev, "interrupt status: 0x%08x\n", status);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	if (status & MAC_INT_TX)
78262306a36Sopenharmony_ci		cpmac_end_xmit(dev, (status & 7));
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	if (status & MAC_INT_RX) {
78562306a36Sopenharmony_ci		queue = (status >> 8) & 7;
78662306a36Sopenharmony_ci		if (napi_schedule_prep(&priv->napi)) {
78762306a36Sopenharmony_ci			cpmac_write(priv->regs, CPMAC_RX_INT_CLEAR, 1 << queue);
78862306a36Sopenharmony_ci			__napi_schedule(&priv->napi);
78962306a36Sopenharmony_ci		}
79062306a36Sopenharmony_ci	}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_MAC_EOI_VECTOR, 0);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	if (unlikely(status & (MAC_INT_HOST | MAC_INT_STATUS)))
79562306a36Sopenharmony_ci		cpmac_check_status(dev);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	return IRQ_HANDLED;
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistatic void cpmac_tx_timeout(struct net_device *dev, unsigned int txqueue)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	struct cpmac_priv *priv = netdev_priv(dev);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	spin_lock(&priv->lock);
80562306a36Sopenharmony_ci	dev->stats.tx_errors++;
80662306a36Sopenharmony_ci	spin_unlock(&priv->lock);
80762306a36Sopenharmony_ci	if (netif_msg_tx_err(priv) && net_ratelimit())
80862306a36Sopenharmony_ci		netdev_warn(dev, "transmit timeout\n");
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	atomic_inc(&priv->reset_pending);
81162306a36Sopenharmony_ci	barrier();
81262306a36Sopenharmony_ci	cpmac_clear_tx(dev);
81362306a36Sopenharmony_ci	barrier();
81462306a36Sopenharmony_ci	atomic_dec(&priv->reset_pending);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	netif_tx_wake_all_queues(priv->dev);
81762306a36Sopenharmony_ci}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_cistatic void cpmac_get_ringparam(struct net_device *dev,
82062306a36Sopenharmony_ci				struct ethtool_ringparam *ring,
82162306a36Sopenharmony_ci				struct kernel_ethtool_ringparam *kernel_ring,
82262306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
82362306a36Sopenharmony_ci{
82462306a36Sopenharmony_ci	struct cpmac_priv *priv = netdev_priv(dev);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	ring->rx_max_pending = 1024;
82762306a36Sopenharmony_ci	ring->rx_mini_max_pending = 1;
82862306a36Sopenharmony_ci	ring->rx_jumbo_max_pending = 1;
82962306a36Sopenharmony_ci	ring->tx_max_pending = 1;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	ring->rx_pending = priv->ring_size;
83262306a36Sopenharmony_ci	ring->rx_mini_pending = 1;
83362306a36Sopenharmony_ci	ring->rx_jumbo_pending = 1;
83462306a36Sopenharmony_ci	ring->tx_pending = 1;
83562306a36Sopenharmony_ci}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_cistatic int cpmac_set_ringparam(struct net_device *dev,
83862306a36Sopenharmony_ci			       struct ethtool_ringparam *ring,
83962306a36Sopenharmony_ci			       struct kernel_ethtool_ringparam *kernel_ring,
84062306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
84162306a36Sopenharmony_ci{
84262306a36Sopenharmony_ci	struct cpmac_priv *priv = netdev_priv(dev);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	if (netif_running(dev))
84562306a36Sopenharmony_ci		return -EBUSY;
84662306a36Sopenharmony_ci	priv->ring_size = ring->rx_pending;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	return 0;
84962306a36Sopenharmony_ci}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_cistatic void cpmac_get_drvinfo(struct net_device *dev,
85262306a36Sopenharmony_ci			      struct ethtool_drvinfo *info)
85362306a36Sopenharmony_ci{
85462306a36Sopenharmony_ci	strscpy(info->driver, "cpmac", sizeof(info->driver));
85562306a36Sopenharmony_ci	strscpy(info->version, CPMAC_VERSION, sizeof(info->version));
85662306a36Sopenharmony_ci	snprintf(info->bus_info, sizeof(info->bus_info), "%s", "cpmac");
85762306a36Sopenharmony_ci}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_cistatic const struct ethtool_ops cpmac_ethtool_ops = {
86062306a36Sopenharmony_ci	.get_drvinfo = cpmac_get_drvinfo,
86162306a36Sopenharmony_ci	.get_link = ethtool_op_get_link,
86262306a36Sopenharmony_ci	.get_ringparam = cpmac_get_ringparam,
86362306a36Sopenharmony_ci	.set_ringparam = cpmac_set_ringparam,
86462306a36Sopenharmony_ci	.get_link_ksettings = phy_ethtool_get_link_ksettings,
86562306a36Sopenharmony_ci	.set_link_ksettings = phy_ethtool_set_link_ksettings,
86662306a36Sopenharmony_ci};
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_cistatic void cpmac_adjust_link(struct net_device *dev)
86962306a36Sopenharmony_ci{
87062306a36Sopenharmony_ci	struct cpmac_priv *priv = netdev_priv(dev);
87162306a36Sopenharmony_ci	int new_state = 0;
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	spin_lock(&priv->lock);
87462306a36Sopenharmony_ci	if (dev->phydev->link) {
87562306a36Sopenharmony_ci		netif_tx_start_all_queues(dev);
87662306a36Sopenharmony_ci		if (dev->phydev->duplex != priv->oldduplex) {
87762306a36Sopenharmony_ci			new_state = 1;
87862306a36Sopenharmony_ci			priv->oldduplex = dev->phydev->duplex;
87962306a36Sopenharmony_ci		}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci		if (dev->phydev->speed != priv->oldspeed) {
88262306a36Sopenharmony_ci			new_state = 1;
88362306a36Sopenharmony_ci			priv->oldspeed = dev->phydev->speed;
88462306a36Sopenharmony_ci		}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci		if (!priv->oldlink) {
88762306a36Sopenharmony_ci			new_state = 1;
88862306a36Sopenharmony_ci			priv->oldlink = 1;
88962306a36Sopenharmony_ci		}
89062306a36Sopenharmony_ci	} else if (priv->oldlink) {
89162306a36Sopenharmony_ci		new_state = 1;
89262306a36Sopenharmony_ci		priv->oldlink = 0;
89362306a36Sopenharmony_ci		priv->oldspeed = 0;
89462306a36Sopenharmony_ci		priv->oldduplex = -1;
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	if (new_state && netif_msg_link(priv) && net_ratelimit())
89862306a36Sopenharmony_ci		phy_print_status(dev->phydev);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	spin_unlock(&priv->lock);
90162306a36Sopenharmony_ci}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_cistatic int cpmac_open(struct net_device *dev)
90462306a36Sopenharmony_ci{
90562306a36Sopenharmony_ci	int i, size, res;
90662306a36Sopenharmony_ci	struct cpmac_priv *priv = netdev_priv(dev);
90762306a36Sopenharmony_ci	struct resource *mem;
90862306a36Sopenharmony_ci	struct cpmac_desc *desc;
90962306a36Sopenharmony_ci	struct sk_buff *skb;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	mem = platform_get_resource_byname(priv->pdev, IORESOURCE_MEM, "regs");
91262306a36Sopenharmony_ci	if (!request_mem_region(mem->start, resource_size(mem), dev->name)) {
91362306a36Sopenharmony_ci		if (netif_msg_drv(priv))
91462306a36Sopenharmony_ci			netdev_err(dev, "failed to request registers\n");
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci		res = -ENXIO;
91762306a36Sopenharmony_ci		goto fail_reserve;
91862306a36Sopenharmony_ci	}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	priv->regs = ioremap(mem->start, resource_size(mem));
92162306a36Sopenharmony_ci	if (!priv->regs) {
92262306a36Sopenharmony_ci		if (netif_msg_drv(priv))
92362306a36Sopenharmony_ci			netdev_err(dev, "failed to remap registers\n");
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci		res = -ENXIO;
92662306a36Sopenharmony_ci		goto fail_remap;
92762306a36Sopenharmony_ci	}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	size = priv->ring_size + CPMAC_QUEUES;
93062306a36Sopenharmony_ci	priv->desc_ring = dma_alloc_coherent(&dev->dev,
93162306a36Sopenharmony_ci					     sizeof(struct cpmac_desc) * size,
93262306a36Sopenharmony_ci					     &priv->dma_ring,
93362306a36Sopenharmony_ci					     GFP_KERNEL);
93462306a36Sopenharmony_ci	if (!priv->desc_ring) {
93562306a36Sopenharmony_ci		res = -ENOMEM;
93662306a36Sopenharmony_ci		goto fail_alloc;
93762306a36Sopenharmony_ci	}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	for (i = 0; i < size; i++)
94062306a36Sopenharmony_ci		priv->desc_ring[i].mapping = priv->dma_ring + sizeof(*desc) * i;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	priv->rx_head = &priv->desc_ring[CPMAC_QUEUES];
94362306a36Sopenharmony_ci	for (i = 0, desc = priv->rx_head; i < priv->ring_size; i++, desc++) {
94462306a36Sopenharmony_ci		skb = netdev_alloc_skb_ip_align(dev, CPMAC_SKB_SIZE);
94562306a36Sopenharmony_ci		if (unlikely(!skb)) {
94662306a36Sopenharmony_ci			res = -ENOMEM;
94762306a36Sopenharmony_ci			goto fail_desc;
94862306a36Sopenharmony_ci		}
94962306a36Sopenharmony_ci		desc->skb = skb;
95062306a36Sopenharmony_ci		desc->data_mapping = dma_map_single(&dev->dev, skb->data,
95162306a36Sopenharmony_ci						    CPMAC_SKB_SIZE,
95262306a36Sopenharmony_ci						    DMA_FROM_DEVICE);
95362306a36Sopenharmony_ci		desc->hw_data = (u32)desc->data_mapping;
95462306a36Sopenharmony_ci		desc->buflen = CPMAC_SKB_SIZE;
95562306a36Sopenharmony_ci		desc->dataflags = CPMAC_OWN;
95662306a36Sopenharmony_ci		desc->next = &priv->rx_head[(i + 1) % priv->ring_size];
95762306a36Sopenharmony_ci		desc->next->prev = desc;
95862306a36Sopenharmony_ci		desc->hw_next = (u32)desc->next->mapping;
95962306a36Sopenharmony_ci	}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	priv->rx_head->prev->hw_next = (u32)0;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	res = request_irq(dev->irq, cpmac_irq, IRQF_SHARED, dev->name, dev);
96462306a36Sopenharmony_ci	if (res) {
96562306a36Sopenharmony_ci		if (netif_msg_drv(priv))
96662306a36Sopenharmony_ci			netdev_err(dev, "failed to obtain irq\n");
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci		goto fail_irq;
96962306a36Sopenharmony_ci	}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	atomic_set(&priv->reset_pending, 0);
97262306a36Sopenharmony_ci	INIT_WORK(&priv->reset_work, cpmac_hw_error);
97362306a36Sopenharmony_ci	cpmac_hw_start(dev);
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	napi_enable(&priv->napi);
97662306a36Sopenharmony_ci	phy_start(dev->phydev);
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	return 0;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_cifail_irq:
98162306a36Sopenharmony_cifail_desc:
98262306a36Sopenharmony_ci	for (i = 0; i < priv->ring_size; i++) {
98362306a36Sopenharmony_ci		if (priv->rx_head[i].skb) {
98462306a36Sopenharmony_ci			dma_unmap_single(&dev->dev,
98562306a36Sopenharmony_ci					 priv->rx_head[i].data_mapping,
98662306a36Sopenharmony_ci					 CPMAC_SKB_SIZE,
98762306a36Sopenharmony_ci					 DMA_FROM_DEVICE);
98862306a36Sopenharmony_ci			kfree_skb(priv->rx_head[i].skb);
98962306a36Sopenharmony_ci		}
99062306a36Sopenharmony_ci	}
99162306a36Sopenharmony_ci	dma_free_coherent(&dev->dev, sizeof(struct cpmac_desc) * size,
99262306a36Sopenharmony_ci			  priv->desc_ring, priv->dma_ring);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_cifail_alloc:
99562306a36Sopenharmony_ci	iounmap(priv->regs);
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_cifail_remap:
99862306a36Sopenharmony_ci	release_mem_region(mem->start, resource_size(mem));
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_cifail_reserve:
100162306a36Sopenharmony_ci	return res;
100262306a36Sopenharmony_ci}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_cistatic int cpmac_stop(struct net_device *dev)
100562306a36Sopenharmony_ci{
100662306a36Sopenharmony_ci	int i;
100762306a36Sopenharmony_ci	struct cpmac_priv *priv = netdev_priv(dev);
100862306a36Sopenharmony_ci	struct resource *mem;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	netif_tx_stop_all_queues(dev);
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	cancel_work_sync(&priv->reset_work);
101362306a36Sopenharmony_ci	napi_disable(&priv->napi);
101462306a36Sopenharmony_ci	phy_stop(dev->phydev);
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	cpmac_hw_stop(dev);
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	for (i = 0; i < 8; i++)
101962306a36Sopenharmony_ci		cpmac_write(priv->regs, CPMAC_TX_PTR(i), 0);
102062306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_RX_PTR(0), 0);
102162306a36Sopenharmony_ci	cpmac_write(priv->regs, CPMAC_MBP, 0);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	free_irq(dev->irq, dev);
102462306a36Sopenharmony_ci	iounmap(priv->regs);
102562306a36Sopenharmony_ci	mem = platform_get_resource_byname(priv->pdev, IORESOURCE_MEM, "regs");
102662306a36Sopenharmony_ci	release_mem_region(mem->start, resource_size(mem));
102762306a36Sopenharmony_ci	priv->rx_head = &priv->desc_ring[CPMAC_QUEUES];
102862306a36Sopenharmony_ci	for (i = 0; i < priv->ring_size; i++) {
102962306a36Sopenharmony_ci		if (priv->rx_head[i].skb) {
103062306a36Sopenharmony_ci			dma_unmap_single(&dev->dev,
103162306a36Sopenharmony_ci					 priv->rx_head[i].data_mapping,
103262306a36Sopenharmony_ci					 CPMAC_SKB_SIZE,
103362306a36Sopenharmony_ci					 DMA_FROM_DEVICE);
103462306a36Sopenharmony_ci			kfree_skb(priv->rx_head[i].skb);
103562306a36Sopenharmony_ci		}
103662306a36Sopenharmony_ci	}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	dma_free_coherent(&dev->dev, sizeof(struct cpmac_desc) *
103962306a36Sopenharmony_ci			  (CPMAC_QUEUES + priv->ring_size),
104062306a36Sopenharmony_ci			  priv->desc_ring, priv->dma_ring);
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	return 0;
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_cistatic const struct net_device_ops cpmac_netdev_ops = {
104662306a36Sopenharmony_ci	.ndo_open		= cpmac_open,
104762306a36Sopenharmony_ci	.ndo_stop		= cpmac_stop,
104862306a36Sopenharmony_ci	.ndo_start_xmit		= cpmac_start_xmit,
104962306a36Sopenharmony_ci	.ndo_tx_timeout		= cpmac_tx_timeout,
105062306a36Sopenharmony_ci	.ndo_set_rx_mode	= cpmac_set_multicast_list,
105162306a36Sopenharmony_ci	.ndo_eth_ioctl		= phy_do_ioctl_running,
105262306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
105362306a36Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
105462306a36Sopenharmony_ci};
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_cistatic int external_switch;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_cistatic int cpmac_probe(struct platform_device *pdev)
105962306a36Sopenharmony_ci{
106062306a36Sopenharmony_ci	int rc, phy_id;
106162306a36Sopenharmony_ci	char mdio_bus_id[MII_BUS_ID_SIZE];
106262306a36Sopenharmony_ci	struct resource *mem;
106362306a36Sopenharmony_ci	struct cpmac_priv *priv;
106462306a36Sopenharmony_ci	struct net_device *dev;
106562306a36Sopenharmony_ci	struct plat_cpmac_data *pdata;
106662306a36Sopenharmony_ci	struct phy_device *phydev = NULL;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	pdata = dev_get_platdata(&pdev->dev);
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	if (external_switch || dumb_switch) {
107162306a36Sopenharmony_ci		strncpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); /* fixed phys bus */
107262306a36Sopenharmony_ci		phy_id = pdev->id;
107362306a36Sopenharmony_ci	} else {
107462306a36Sopenharmony_ci		for (phy_id = 0; phy_id < PHY_MAX_ADDR; phy_id++) {
107562306a36Sopenharmony_ci			if (!(pdata->phy_mask & (1 << phy_id)))
107662306a36Sopenharmony_ci				continue;
107762306a36Sopenharmony_ci			if (!mdiobus_get_phy(cpmac_mii, phy_id))
107862306a36Sopenharmony_ci				continue;
107962306a36Sopenharmony_ci			strncpy(mdio_bus_id, cpmac_mii->id, MII_BUS_ID_SIZE);
108062306a36Sopenharmony_ci			break;
108162306a36Sopenharmony_ci		}
108262306a36Sopenharmony_ci	}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	if (phy_id == PHY_MAX_ADDR) {
108562306a36Sopenharmony_ci		dev_err(&pdev->dev, "no PHY present, falling back "
108662306a36Sopenharmony_ci			"to switch on MDIO bus 0\n");
108762306a36Sopenharmony_ci		strncpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE); /* fixed phys bus */
108862306a36Sopenharmony_ci		phy_id = pdev->id;
108962306a36Sopenharmony_ci	}
109062306a36Sopenharmony_ci	mdio_bus_id[sizeof(mdio_bus_id) - 1] = '\0';
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	dev = alloc_etherdev_mq(sizeof(*priv), CPMAC_QUEUES);
109362306a36Sopenharmony_ci	if (!dev)
109462306a36Sopenharmony_ci		return -ENOMEM;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, &pdev->dev);
109762306a36Sopenharmony_ci	platform_set_drvdata(pdev, dev);
109862306a36Sopenharmony_ci	priv = netdev_priv(dev);
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	priv->pdev = pdev;
110162306a36Sopenharmony_ci	mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
110262306a36Sopenharmony_ci	if (!mem) {
110362306a36Sopenharmony_ci		rc = -ENODEV;
110462306a36Sopenharmony_ci		goto fail;
110562306a36Sopenharmony_ci	}
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	dev->irq = platform_get_irq_byname(pdev, "irq");
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	dev->netdev_ops = &cpmac_netdev_ops;
111062306a36Sopenharmony_ci	dev->ethtool_ops = &cpmac_ethtool_ops;
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	netif_napi_add(dev, &priv->napi, cpmac_poll);
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	spin_lock_init(&priv->lock);
111562306a36Sopenharmony_ci	spin_lock_init(&priv->rx_lock);
111662306a36Sopenharmony_ci	priv->dev = dev;
111762306a36Sopenharmony_ci	priv->ring_size = 64;
111862306a36Sopenharmony_ci	priv->msg_enable = netif_msg_init(debug_level, 0xff);
111962306a36Sopenharmony_ci	eth_hw_addr_set(dev, pdata->dev_addr);
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	snprintf(priv->phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT,
112262306a36Sopenharmony_ci						mdio_bus_id, phy_id);
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	phydev = phy_connect(dev, priv->phy_name, cpmac_adjust_link,
112562306a36Sopenharmony_ci			     PHY_INTERFACE_MODE_MII);
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	if (IS_ERR(phydev)) {
112862306a36Sopenharmony_ci		if (netif_msg_drv(priv))
112962306a36Sopenharmony_ci			dev_err(&pdev->dev, "Could not attach to PHY\n");
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci		rc = PTR_ERR(phydev);
113262306a36Sopenharmony_ci		goto fail;
113362306a36Sopenharmony_ci	}
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	rc = register_netdev(dev);
113662306a36Sopenharmony_ci	if (rc) {
113762306a36Sopenharmony_ci		dev_err(&pdev->dev, "Could not register net device\n");
113862306a36Sopenharmony_ci		goto fail;
113962306a36Sopenharmony_ci	}
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	if (netif_msg_probe(priv)) {
114262306a36Sopenharmony_ci		dev_info(&pdev->dev, "regs: %p, irq: %d, phy: %s, "
114362306a36Sopenharmony_ci			 "mac: %pM\n", (void *)mem->start, dev->irq,
114462306a36Sopenharmony_ci			 priv->phy_name, dev->dev_addr);
114562306a36Sopenharmony_ci	}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	return 0;
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_cifail:
115062306a36Sopenharmony_ci	free_netdev(dev);
115162306a36Sopenharmony_ci	return rc;
115262306a36Sopenharmony_ci}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_cistatic int cpmac_remove(struct platform_device *pdev)
115562306a36Sopenharmony_ci{
115662306a36Sopenharmony_ci	struct net_device *dev = platform_get_drvdata(pdev);
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	unregister_netdev(dev);
115962306a36Sopenharmony_ci	free_netdev(dev);
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	return 0;
116262306a36Sopenharmony_ci}
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_cistatic struct platform_driver cpmac_driver = {
116562306a36Sopenharmony_ci	.driver = {
116662306a36Sopenharmony_ci		.name 	= "cpmac",
116762306a36Sopenharmony_ci	},
116862306a36Sopenharmony_ci	.probe 	= cpmac_probe,
116962306a36Sopenharmony_ci	.remove = cpmac_remove,
117062306a36Sopenharmony_ci};
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ciint __init cpmac_init(void)
117362306a36Sopenharmony_ci{
117462306a36Sopenharmony_ci	u32 mask;
117562306a36Sopenharmony_ci	int i, res;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	cpmac_mii = mdiobus_alloc();
117862306a36Sopenharmony_ci	if (cpmac_mii == NULL)
117962306a36Sopenharmony_ci		return -ENOMEM;
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	cpmac_mii->name = "cpmac-mii";
118262306a36Sopenharmony_ci	cpmac_mii->read = cpmac_mdio_read;
118362306a36Sopenharmony_ci	cpmac_mii->write = cpmac_mdio_write;
118462306a36Sopenharmony_ci	cpmac_mii->reset = cpmac_mdio_reset;
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	cpmac_mii->priv = ioremap(AR7_REGS_MDIO, 256);
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	if (!cpmac_mii->priv) {
118962306a36Sopenharmony_ci		pr_err("Can't ioremap mdio registers\n");
119062306a36Sopenharmony_ci		res = -ENXIO;
119162306a36Sopenharmony_ci		goto fail_alloc;
119262306a36Sopenharmony_ci	}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	/* FIXME: unhardcode gpio&reset bits */
119562306a36Sopenharmony_ci	ar7_gpio_disable(26);
119662306a36Sopenharmony_ci	ar7_gpio_disable(27);
119762306a36Sopenharmony_ci	ar7_device_reset(AR7_RESET_BIT_CPMAC_LO);
119862306a36Sopenharmony_ci	ar7_device_reset(AR7_RESET_BIT_CPMAC_HI);
119962306a36Sopenharmony_ci	ar7_device_reset(AR7_RESET_BIT_EPHY);
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	cpmac_mii->reset(cpmac_mii);
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	for (i = 0; i < 300; i++) {
120462306a36Sopenharmony_ci		mask = cpmac_read(cpmac_mii->priv, CPMAC_MDIO_ALIVE);
120562306a36Sopenharmony_ci		if (mask)
120662306a36Sopenharmony_ci			break;
120762306a36Sopenharmony_ci		else
120862306a36Sopenharmony_ci			msleep(10);
120962306a36Sopenharmony_ci	}
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	mask &= 0x7fffffff;
121262306a36Sopenharmony_ci	if (mask & (mask - 1)) {
121362306a36Sopenharmony_ci		external_switch = 1;
121462306a36Sopenharmony_ci		mask = 0;
121562306a36Sopenharmony_ci	}
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	cpmac_mii->phy_mask = ~(mask | 0x80000000);
121862306a36Sopenharmony_ci	snprintf(cpmac_mii->id, MII_BUS_ID_SIZE, "cpmac-1");
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	res = mdiobus_register(cpmac_mii);
122162306a36Sopenharmony_ci	if (res)
122262306a36Sopenharmony_ci		goto fail_mii;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	res = platform_driver_register(&cpmac_driver);
122562306a36Sopenharmony_ci	if (res)
122662306a36Sopenharmony_ci		goto fail_cpmac;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	return 0;
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_cifail_cpmac:
123162306a36Sopenharmony_ci	mdiobus_unregister(cpmac_mii);
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_cifail_mii:
123462306a36Sopenharmony_ci	iounmap(cpmac_mii->priv);
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_cifail_alloc:
123762306a36Sopenharmony_ci	mdiobus_free(cpmac_mii);
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	return res;
124062306a36Sopenharmony_ci}
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_civoid __exit cpmac_exit(void)
124362306a36Sopenharmony_ci{
124462306a36Sopenharmony_ci	platform_driver_unregister(&cpmac_driver);
124562306a36Sopenharmony_ci	mdiobus_unregister(cpmac_mii);
124662306a36Sopenharmony_ci	iounmap(cpmac_mii->priv);
124762306a36Sopenharmony_ci	mdiobus_free(cpmac_mii);
124862306a36Sopenharmony_ci}
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_cimodule_init(cpmac_init);
125162306a36Sopenharmony_cimodule_exit(cpmac_exit);
1252