162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Alchemy Au1x00 ethernet driver
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright 2001-2003, 2006 MontaVista Software Inc.
762306a36Sopenharmony_ci * Copyright 2002 TimeSys Corp.
862306a36Sopenharmony_ci * Added ethtool/mii-tool support,
962306a36Sopenharmony_ci * Copyright 2004 Matt Porter <mporter@kernel.crashing.org>
1062306a36Sopenharmony_ci * Update: 2004 Bjoern Riemer, riemer@fokus.fraunhofer.de
1162306a36Sopenharmony_ci * or riemer@riemer-nt.de: fixed the link beat detection with
1262306a36Sopenharmony_ci * ioctls (SIOCGMIIPHY)
1362306a36Sopenharmony_ci * Copyright 2006 Herbert Valerio Riedel <hvr@gnu.org>
1462306a36Sopenharmony_ci *  converted to use linux-2.6.x's PHY framework
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * Author: MontaVista Software, Inc.
1762306a36Sopenharmony_ci *		ppopov@mvista.com or source@mvista.com
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/capability.h>
2262306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2362306a36Sopenharmony_ci#include <linux/module.h>
2462306a36Sopenharmony_ci#include <linux/kernel.h>
2562306a36Sopenharmony_ci#include <linux/string.h>
2662306a36Sopenharmony_ci#include <linux/timer.h>
2762306a36Sopenharmony_ci#include <linux/errno.h>
2862306a36Sopenharmony_ci#include <linux/in.h>
2962306a36Sopenharmony_ci#include <linux/ioport.h>
3062306a36Sopenharmony_ci#include <linux/bitops.h>
3162306a36Sopenharmony_ci#include <linux/slab.h>
3262306a36Sopenharmony_ci#include <linux/interrupt.h>
3362306a36Sopenharmony_ci#include <linux/netdevice.h>
3462306a36Sopenharmony_ci#include <linux/etherdevice.h>
3562306a36Sopenharmony_ci#include <linux/ethtool.h>
3662306a36Sopenharmony_ci#include <linux/mii.h>
3762306a36Sopenharmony_ci#include <linux/skbuff.h>
3862306a36Sopenharmony_ci#include <linux/delay.h>
3962306a36Sopenharmony_ci#include <linux/crc32.h>
4062306a36Sopenharmony_ci#include <linux/phy.h>
4162306a36Sopenharmony_ci#include <linux/platform_device.h>
4262306a36Sopenharmony_ci#include <linux/cpu.h>
4362306a36Sopenharmony_ci#include <linux/io.h>
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#include <asm/mipsregs.h>
4662306a36Sopenharmony_ci#include <asm/irq.h>
4762306a36Sopenharmony_ci#include <asm/processor.h>
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#include <au1000.h>
5062306a36Sopenharmony_ci#include <au1xxx_eth.h>
5162306a36Sopenharmony_ci#include <prom.h>
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#include "au1000_eth.h"
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#ifdef AU1000_ETH_DEBUG
5662306a36Sopenharmony_cistatic int au1000_debug = 5;
5762306a36Sopenharmony_ci#else
5862306a36Sopenharmony_cistatic int au1000_debug = 3;
5962306a36Sopenharmony_ci#endif
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define AU1000_DEF_MSG_ENABLE	(NETIF_MSG_DRV	| \
6262306a36Sopenharmony_ci				NETIF_MSG_PROBE	| \
6362306a36Sopenharmony_ci				NETIF_MSG_LINK)
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#define DRV_NAME	"au1000_eth"
6662306a36Sopenharmony_ci#define DRV_AUTHOR	"Pete Popov <ppopov@embeddedalley.com>"
6762306a36Sopenharmony_ci#define DRV_DESC	"Au1xxx on-chip Ethernet driver"
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ciMODULE_AUTHOR(DRV_AUTHOR);
7062306a36Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESC);
7162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/* AU1000 MAC registers and bits */
7462306a36Sopenharmony_ci#define MAC_CONTROL		0x0
7562306a36Sopenharmony_ci#  define MAC_RX_ENABLE		(1 << 2)
7662306a36Sopenharmony_ci#  define MAC_TX_ENABLE		(1 << 3)
7762306a36Sopenharmony_ci#  define MAC_DEF_CHECK		(1 << 5)
7862306a36Sopenharmony_ci#  define MAC_SET_BL(X)		(((X) & 0x3) << 6)
7962306a36Sopenharmony_ci#  define MAC_AUTO_PAD		(1 << 8)
8062306a36Sopenharmony_ci#  define MAC_DISABLE_RETRY	(1 << 10)
8162306a36Sopenharmony_ci#  define MAC_DISABLE_BCAST	(1 << 11)
8262306a36Sopenharmony_ci#  define MAC_LATE_COL		(1 << 12)
8362306a36Sopenharmony_ci#  define MAC_HASH_MODE		(1 << 13)
8462306a36Sopenharmony_ci#  define MAC_HASH_ONLY		(1 << 15)
8562306a36Sopenharmony_ci#  define MAC_PASS_ALL		(1 << 16)
8662306a36Sopenharmony_ci#  define MAC_INVERSE_FILTER	(1 << 17)
8762306a36Sopenharmony_ci#  define MAC_PROMISCUOUS	(1 << 18)
8862306a36Sopenharmony_ci#  define MAC_PASS_ALL_MULTI	(1 << 19)
8962306a36Sopenharmony_ci#  define MAC_FULL_DUPLEX	(1 << 20)
9062306a36Sopenharmony_ci#  define MAC_NORMAL_MODE	0
9162306a36Sopenharmony_ci#  define MAC_INT_LOOPBACK	(1 << 21)
9262306a36Sopenharmony_ci#  define MAC_EXT_LOOPBACK	(1 << 22)
9362306a36Sopenharmony_ci#  define MAC_DISABLE_RX_OWN	(1 << 23)
9462306a36Sopenharmony_ci#  define MAC_BIG_ENDIAN	(1 << 30)
9562306a36Sopenharmony_ci#  define MAC_RX_ALL		(1 << 31)
9662306a36Sopenharmony_ci#define MAC_ADDRESS_HIGH	0x4
9762306a36Sopenharmony_ci#define MAC_ADDRESS_LOW		0x8
9862306a36Sopenharmony_ci#define MAC_MCAST_HIGH		0xC
9962306a36Sopenharmony_ci#define MAC_MCAST_LOW		0x10
10062306a36Sopenharmony_ci#define MAC_MII_CNTRL		0x14
10162306a36Sopenharmony_ci#  define MAC_MII_BUSY		(1 << 0)
10262306a36Sopenharmony_ci#  define MAC_MII_READ		0
10362306a36Sopenharmony_ci#  define MAC_MII_WRITE		(1 << 1)
10462306a36Sopenharmony_ci#  define MAC_SET_MII_SELECT_REG(X) (((X) & 0x1f) << 6)
10562306a36Sopenharmony_ci#  define MAC_SET_MII_SELECT_PHY(X) (((X) & 0x1f) << 11)
10662306a36Sopenharmony_ci#define MAC_MII_DATA		0x18
10762306a36Sopenharmony_ci#define MAC_FLOW_CNTRL		0x1C
10862306a36Sopenharmony_ci#  define MAC_FLOW_CNTRL_BUSY	(1 << 0)
10962306a36Sopenharmony_ci#  define MAC_FLOW_CNTRL_ENABLE (1 << 1)
11062306a36Sopenharmony_ci#  define MAC_PASS_CONTROL	(1 << 2)
11162306a36Sopenharmony_ci#  define MAC_SET_PAUSE(X)	(((X) & 0xffff) << 16)
11262306a36Sopenharmony_ci#define MAC_VLAN1_TAG		0x20
11362306a36Sopenharmony_ci#define MAC_VLAN2_TAG		0x24
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/* Ethernet Controller Enable */
11662306a36Sopenharmony_ci#  define MAC_EN_CLOCK_ENABLE	(1 << 0)
11762306a36Sopenharmony_ci#  define MAC_EN_RESET0		(1 << 1)
11862306a36Sopenharmony_ci#  define MAC_EN_TOSS		(0 << 2)
11962306a36Sopenharmony_ci#  define MAC_EN_CACHEABLE	(1 << 3)
12062306a36Sopenharmony_ci#  define MAC_EN_RESET1		(1 << 4)
12162306a36Sopenharmony_ci#  define MAC_EN_RESET2		(1 << 5)
12262306a36Sopenharmony_ci#  define MAC_DMA_RESET		(1 << 6)
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/* Ethernet Controller DMA Channels */
12562306a36Sopenharmony_ci/* offsets from MAC_TX_RING_ADDR address */
12662306a36Sopenharmony_ci#define MAC_TX_BUFF0_STATUS	0x0
12762306a36Sopenharmony_ci#  define TX_FRAME_ABORTED	(1 << 0)
12862306a36Sopenharmony_ci#  define TX_JAB_TIMEOUT	(1 << 1)
12962306a36Sopenharmony_ci#  define TX_NO_CARRIER		(1 << 2)
13062306a36Sopenharmony_ci#  define TX_LOSS_CARRIER	(1 << 3)
13162306a36Sopenharmony_ci#  define TX_EXC_DEF		(1 << 4)
13262306a36Sopenharmony_ci#  define TX_LATE_COLL_ABORT	(1 << 5)
13362306a36Sopenharmony_ci#  define TX_EXC_COLL		(1 << 6)
13462306a36Sopenharmony_ci#  define TX_UNDERRUN		(1 << 7)
13562306a36Sopenharmony_ci#  define TX_DEFERRED		(1 << 8)
13662306a36Sopenharmony_ci#  define TX_LATE_COLL		(1 << 9)
13762306a36Sopenharmony_ci#  define TX_COLL_CNT_MASK	(0xF << 10)
13862306a36Sopenharmony_ci#  define TX_PKT_RETRY		(1 << 31)
13962306a36Sopenharmony_ci#define MAC_TX_BUFF0_ADDR	0x4
14062306a36Sopenharmony_ci#  define TX_DMA_ENABLE		(1 << 0)
14162306a36Sopenharmony_ci#  define TX_T_DONE		(1 << 1)
14262306a36Sopenharmony_ci#  define TX_GET_DMA_BUFFER(X)	(((X) >> 2) & 0x3)
14362306a36Sopenharmony_ci#define MAC_TX_BUFF0_LEN	0x8
14462306a36Sopenharmony_ci#define MAC_TX_BUFF1_STATUS	0x10
14562306a36Sopenharmony_ci#define MAC_TX_BUFF1_ADDR	0x14
14662306a36Sopenharmony_ci#define MAC_TX_BUFF1_LEN	0x18
14762306a36Sopenharmony_ci#define MAC_TX_BUFF2_STATUS	0x20
14862306a36Sopenharmony_ci#define MAC_TX_BUFF2_ADDR	0x24
14962306a36Sopenharmony_ci#define MAC_TX_BUFF2_LEN	0x28
15062306a36Sopenharmony_ci#define MAC_TX_BUFF3_STATUS	0x30
15162306a36Sopenharmony_ci#define MAC_TX_BUFF3_ADDR	0x34
15262306a36Sopenharmony_ci#define MAC_TX_BUFF3_LEN	0x38
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci/* offsets from MAC_RX_RING_ADDR */
15562306a36Sopenharmony_ci#define MAC_RX_BUFF0_STATUS	0x0
15662306a36Sopenharmony_ci#  define RX_FRAME_LEN_MASK	0x3fff
15762306a36Sopenharmony_ci#  define RX_WDOG_TIMER		(1 << 14)
15862306a36Sopenharmony_ci#  define RX_RUNT		(1 << 15)
15962306a36Sopenharmony_ci#  define RX_OVERLEN		(1 << 16)
16062306a36Sopenharmony_ci#  define RX_COLL		(1 << 17)
16162306a36Sopenharmony_ci#  define RX_ETHER		(1 << 18)
16262306a36Sopenharmony_ci#  define RX_MII_ERROR		(1 << 19)
16362306a36Sopenharmony_ci#  define RX_DRIBBLING		(1 << 20)
16462306a36Sopenharmony_ci#  define RX_CRC_ERROR		(1 << 21)
16562306a36Sopenharmony_ci#  define RX_VLAN1		(1 << 22)
16662306a36Sopenharmony_ci#  define RX_VLAN2		(1 << 23)
16762306a36Sopenharmony_ci#  define RX_LEN_ERROR		(1 << 24)
16862306a36Sopenharmony_ci#  define RX_CNTRL_FRAME	(1 << 25)
16962306a36Sopenharmony_ci#  define RX_U_CNTRL_FRAME	(1 << 26)
17062306a36Sopenharmony_ci#  define RX_MCAST_FRAME	(1 << 27)
17162306a36Sopenharmony_ci#  define RX_BCAST_FRAME	(1 << 28)
17262306a36Sopenharmony_ci#  define RX_FILTER_FAIL	(1 << 29)
17362306a36Sopenharmony_ci#  define RX_PACKET_FILTER	(1 << 30)
17462306a36Sopenharmony_ci#  define RX_MISSED_FRAME	(1 << 31)
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci#  define RX_ERROR (RX_WDOG_TIMER | RX_RUNT | RX_OVERLEN |  \
17762306a36Sopenharmony_ci		    RX_COLL | RX_MII_ERROR | RX_CRC_ERROR | \
17862306a36Sopenharmony_ci		    RX_LEN_ERROR | RX_U_CNTRL_FRAME | RX_MISSED_FRAME)
17962306a36Sopenharmony_ci#define MAC_RX_BUFF0_ADDR	0x4
18062306a36Sopenharmony_ci#  define RX_DMA_ENABLE		(1 << 0)
18162306a36Sopenharmony_ci#  define RX_T_DONE		(1 << 1)
18262306a36Sopenharmony_ci#  define RX_GET_DMA_BUFFER(X)	(((X) >> 2) & 0x3)
18362306a36Sopenharmony_ci#  define RX_SET_BUFF_ADDR(X)	((X) & 0xffffffc0)
18462306a36Sopenharmony_ci#define MAC_RX_BUFF1_STATUS	0x10
18562306a36Sopenharmony_ci#define MAC_RX_BUFF1_ADDR	0x14
18662306a36Sopenharmony_ci#define MAC_RX_BUFF2_STATUS	0x20
18762306a36Sopenharmony_ci#define MAC_RX_BUFF2_ADDR	0x24
18862306a36Sopenharmony_ci#define MAC_RX_BUFF3_STATUS	0x30
18962306a36Sopenharmony_ci#define MAC_RX_BUFF3_ADDR	0x34
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci/*
19262306a36Sopenharmony_ci * Theory of operation
19362306a36Sopenharmony_ci *
19462306a36Sopenharmony_ci * The Au1000 MACs use a simple rx and tx descriptor ring scheme.
19562306a36Sopenharmony_ci * There are four receive and four transmit descriptors.  These
19662306a36Sopenharmony_ci * descriptors are not in memory; rather, they are just a set of
19762306a36Sopenharmony_ci * hardware registers.
19862306a36Sopenharmony_ci *
19962306a36Sopenharmony_ci * Since the Au1000 has a coherent data cache, the receive and
20062306a36Sopenharmony_ci * transmit buffers are allocated from the KSEG0 segment. The
20162306a36Sopenharmony_ci * hardware registers, however, are still mapped at KSEG1 to
20262306a36Sopenharmony_ci * make sure there's no out-of-order writes, and that all writes
20362306a36Sopenharmony_ci * complete immediately.
20462306a36Sopenharmony_ci */
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci/*
20762306a36Sopenharmony_ci * board-specific configurations
20862306a36Sopenharmony_ci *
20962306a36Sopenharmony_ci * PHY detection algorithm
21062306a36Sopenharmony_ci *
21162306a36Sopenharmony_ci * If phy_static_config is undefined, the PHY setup is
21262306a36Sopenharmony_ci * autodetected:
21362306a36Sopenharmony_ci *
21462306a36Sopenharmony_ci * mii_probe() first searches the current MAC's MII bus for a PHY,
21562306a36Sopenharmony_ci * selecting the first (or last, if phy_search_highest_addr is
21662306a36Sopenharmony_ci * defined) PHY address not already claimed by another netdev.
21762306a36Sopenharmony_ci *
21862306a36Sopenharmony_ci * If nothing was found that way when searching for the 2nd ethernet
21962306a36Sopenharmony_ci * controller's PHY and phy1_search_mac0 is defined, then
22062306a36Sopenharmony_ci * the first MII bus is searched as well for an unclaimed PHY; this is
22162306a36Sopenharmony_ci * needed in case of a dual-PHY accessible only through the MAC0's MII
22262306a36Sopenharmony_ci * bus.
22362306a36Sopenharmony_ci *
22462306a36Sopenharmony_ci * Finally, if no PHY is found, then the corresponding ethernet
22562306a36Sopenharmony_ci * controller is not registered to the network subsystem.
22662306a36Sopenharmony_ci */
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci/* autodetection defaults: phy1_search_mac0 */
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci/* static PHY setup
23162306a36Sopenharmony_ci *
23262306a36Sopenharmony_ci * most boards PHY setup should be detectable properly with the
23362306a36Sopenharmony_ci * autodetection algorithm in mii_probe(), but in some cases (e.g. if
23462306a36Sopenharmony_ci * you have a switch attached, or want to use the PHY's interrupt
23562306a36Sopenharmony_ci * notification capabilities) you can provide a static PHY
23662306a36Sopenharmony_ci * configuration here
23762306a36Sopenharmony_ci *
23862306a36Sopenharmony_ci * IRQs may only be set, if a PHY address was configured
23962306a36Sopenharmony_ci * If a PHY address is given, also a bus id is required to be set
24062306a36Sopenharmony_ci *
24162306a36Sopenharmony_ci * ps: make sure the used irqs are configured properly in the board
24262306a36Sopenharmony_ci * specific irq-map
24362306a36Sopenharmony_ci */
24462306a36Sopenharmony_cistatic void au1000_enable_mac(struct net_device *dev, int force_reset)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	unsigned long flags;
24762306a36Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	spin_lock_irqsave(&aup->lock, flags);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (force_reset || (!aup->mac_enabled)) {
25262306a36Sopenharmony_ci		writel(MAC_EN_CLOCK_ENABLE, aup->enable);
25362306a36Sopenharmony_ci		wmb(); /* drain writebuffer */
25462306a36Sopenharmony_ci		mdelay(2);
25562306a36Sopenharmony_ci		writel((MAC_EN_RESET0 | MAC_EN_RESET1 | MAC_EN_RESET2
25662306a36Sopenharmony_ci				| MAC_EN_CLOCK_ENABLE), aup->enable);
25762306a36Sopenharmony_ci		wmb(); /* drain writebuffer */
25862306a36Sopenharmony_ci		mdelay(2);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci		aup->mac_enabled = 1;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	spin_unlock_irqrestore(&aup->lock, flags);
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci/*
26762306a36Sopenharmony_ci * MII operations
26862306a36Sopenharmony_ci */
26962306a36Sopenharmony_cistatic int au1000_mdio_read(struct net_device *dev, int phy_addr, int reg)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
27262306a36Sopenharmony_ci	u32 *const mii_control_reg = &aup->mac->mii_control;
27362306a36Sopenharmony_ci	u32 *const mii_data_reg = &aup->mac->mii_data;
27462306a36Sopenharmony_ci	u32 timedout = 20;
27562306a36Sopenharmony_ci	u32 mii_control;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	while (readl(mii_control_reg) & MAC_MII_BUSY) {
27862306a36Sopenharmony_ci		mdelay(1);
27962306a36Sopenharmony_ci		if (--timedout == 0) {
28062306a36Sopenharmony_ci			netdev_err(dev, "read_MII busy timeout!!\n");
28162306a36Sopenharmony_ci			return -1;
28262306a36Sopenharmony_ci		}
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	mii_control = MAC_SET_MII_SELECT_REG(reg) |
28662306a36Sopenharmony_ci		MAC_SET_MII_SELECT_PHY(phy_addr) | MAC_MII_READ;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	writel(mii_control, mii_control_reg);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	timedout = 20;
29162306a36Sopenharmony_ci	while (readl(mii_control_reg) & MAC_MII_BUSY) {
29262306a36Sopenharmony_ci		mdelay(1);
29362306a36Sopenharmony_ci		if (--timedout == 0) {
29462306a36Sopenharmony_ci			netdev_err(dev, "mdio_read busy timeout!!\n");
29562306a36Sopenharmony_ci			return -1;
29662306a36Sopenharmony_ci		}
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci	return readl(mii_data_reg);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic void au1000_mdio_write(struct net_device *dev, int phy_addr,
30262306a36Sopenharmony_ci			      int reg, u16 value)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
30562306a36Sopenharmony_ci	u32 *const mii_control_reg = &aup->mac->mii_control;
30662306a36Sopenharmony_ci	u32 *const mii_data_reg = &aup->mac->mii_data;
30762306a36Sopenharmony_ci	u32 timedout = 20;
30862306a36Sopenharmony_ci	u32 mii_control;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	while (readl(mii_control_reg) & MAC_MII_BUSY) {
31162306a36Sopenharmony_ci		mdelay(1);
31262306a36Sopenharmony_ci		if (--timedout == 0) {
31362306a36Sopenharmony_ci			netdev_err(dev, "mdio_write busy timeout!!\n");
31462306a36Sopenharmony_ci			return;
31562306a36Sopenharmony_ci		}
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	mii_control = MAC_SET_MII_SELECT_REG(reg) |
31962306a36Sopenharmony_ci		MAC_SET_MII_SELECT_PHY(phy_addr) | MAC_MII_WRITE;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	writel(value, mii_data_reg);
32262306a36Sopenharmony_ci	writel(mii_control, mii_control_reg);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic int au1000_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	struct net_device *const dev = bus->priv;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	/* make sure the MAC associated with this
33062306a36Sopenharmony_ci	 * mii_bus is enabled
33162306a36Sopenharmony_ci	 */
33262306a36Sopenharmony_ci	au1000_enable_mac(dev, 0);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	return au1000_mdio_read(dev, phy_addr, regnum);
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic int au1000_mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum,
33862306a36Sopenharmony_ci				u16 value)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	struct net_device *const dev = bus->priv;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/* make sure the MAC associated with this
34362306a36Sopenharmony_ci	 * mii_bus is enabled
34462306a36Sopenharmony_ci	 */
34562306a36Sopenharmony_ci	au1000_enable_mac(dev, 0);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	au1000_mdio_write(dev, phy_addr, regnum, value);
34862306a36Sopenharmony_ci	return 0;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic int au1000_mdiobus_reset(struct mii_bus *bus)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	struct net_device *const dev = bus->priv;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	/* make sure the MAC associated with this
35662306a36Sopenharmony_ci	 * mii_bus is enabled
35762306a36Sopenharmony_ci	 */
35862306a36Sopenharmony_ci	au1000_enable_mac(dev, 0);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	return 0;
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic void au1000_hard_stop(struct net_device *dev)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
36662306a36Sopenharmony_ci	u32 reg;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	netif_dbg(aup, drv, dev, "hard stop\n");
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	reg = readl(&aup->mac->control);
37162306a36Sopenharmony_ci	reg &= ~(MAC_RX_ENABLE | MAC_TX_ENABLE);
37262306a36Sopenharmony_ci	writel(reg, &aup->mac->control);
37362306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
37462306a36Sopenharmony_ci	mdelay(10);
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic void au1000_enable_rx_tx(struct net_device *dev)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
38062306a36Sopenharmony_ci	u32 reg;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	netif_dbg(aup, hw, dev, "enable_rx_tx\n");
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	reg = readl(&aup->mac->control);
38562306a36Sopenharmony_ci	reg |= (MAC_RX_ENABLE | MAC_TX_ENABLE);
38662306a36Sopenharmony_ci	writel(reg, &aup->mac->control);
38762306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
38862306a36Sopenharmony_ci	mdelay(10);
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic void
39262306a36Sopenharmony_ciau1000_adjust_link(struct net_device *dev)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
39562306a36Sopenharmony_ci	struct phy_device *phydev = dev->phydev;
39662306a36Sopenharmony_ci	unsigned long flags;
39762306a36Sopenharmony_ci	u32 reg;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	int status_change = 0;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	BUG_ON(!phydev);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	spin_lock_irqsave(&aup->lock, flags);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (phydev->link && (aup->old_speed != phydev->speed)) {
40662306a36Sopenharmony_ci		/* speed changed */
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		switch (phydev->speed) {
40962306a36Sopenharmony_ci		case SPEED_10:
41062306a36Sopenharmony_ci		case SPEED_100:
41162306a36Sopenharmony_ci			break;
41262306a36Sopenharmony_ci		default:
41362306a36Sopenharmony_ci			netdev_warn(dev, "Speed (%d) is not 10/100 ???\n",
41462306a36Sopenharmony_ci							phydev->speed);
41562306a36Sopenharmony_ci			break;
41662306a36Sopenharmony_ci		}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci		aup->old_speed = phydev->speed;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci		status_change = 1;
42162306a36Sopenharmony_ci	}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (phydev->link && (aup->old_duplex != phydev->duplex)) {
42462306a36Sopenharmony_ci		/* duplex mode changed */
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci		/* switching duplex mode requires to disable rx and tx! */
42762306a36Sopenharmony_ci		au1000_hard_stop(dev);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci		reg = readl(&aup->mac->control);
43062306a36Sopenharmony_ci		if (DUPLEX_FULL == phydev->duplex) {
43162306a36Sopenharmony_ci			reg |= MAC_FULL_DUPLEX;
43262306a36Sopenharmony_ci			reg &= ~MAC_DISABLE_RX_OWN;
43362306a36Sopenharmony_ci		} else {
43462306a36Sopenharmony_ci			reg &= ~MAC_FULL_DUPLEX;
43562306a36Sopenharmony_ci			reg |= MAC_DISABLE_RX_OWN;
43662306a36Sopenharmony_ci		}
43762306a36Sopenharmony_ci		writel(reg, &aup->mac->control);
43862306a36Sopenharmony_ci		wmb(); /* drain writebuffer */
43962306a36Sopenharmony_ci		mdelay(1);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci		au1000_enable_rx_tx(dev);
44262306a36Sopenharmony_ci		aup->old_duplex = phydev->duplex;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci		status_change = 1;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	if (phydev->link != aup->old_link) {
44862306a36Sopenharmony_ci		/* link state changed */
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci		if (!phydev->link) {
45162306a36Sopenharmony_ci			/* link went down */
45262306a36Sopenharmony_ci			aup->old_speed = 0;
45362306a36Sopenharmony_ci			aup->old_duplex = -1;
45462306a36Sopenharmony_ci		}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		aup->old_link = phydev->link;
45762306a36Sopenharmony_ci		status_change = 1;
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	spin_unlock_irqrestore(&aup->lock, flags);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	if (status_change) {
46362306a36Sopenharmony_ci		if (phydev->link)
46462306a36Sopenharmony_ci			netdev_info(dev, "link up (%d/%s)\n",
46562306a36Sopenharmony_ci			       phydev->speed,
46662306a36Sopenharmony_ci			       DUPLEX_FULL == phydev->duplex ? "Full" : "Half");
46762306a36Sopenharmony_ci		else
46862306a36Sopenharmony_ci			netdev_info(dev, "link down\n");
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic int au1000_mii_probe(struct net_device *dev)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	struct au1000_private *const aup = netdev_priv(dev);
47562306a36Sopenharmony_ci	struct phy_device *phydev = NULL;
47662306a36Sopenharmony_ci	int phy_addr;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	if (aup->phy_static_config) {
47962306a36Sopenharmony_ci		BUG_ON(aup->mac_id < 0 || aup->mac_id > 1);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci		if (aup->phy_addr)
48262306a36Sopenharmony_ci			phydev = mdiobus_get_phy(aup->mii_bus, aup->phy_addr);
48362306a36Sopenharmony_ci		else
48462306a36Sopenharmony_ci			netdev_info(dev, "using PHY-less setup\n");
48562306a36Sopenharmony_ci		return 0;
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	/* find the first (lowest address) PHY
48962306a36Sopenharmony_ci	 * on the current MAC's MII bus
49062306a36Sopenharmony_ci	 */
49162306a36Sopenharmony_ci	for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++)
49262306a36Sopenharmony_ci		if (mdiobus_get_phy(aup->mii_bus, phy_addr)) {
49362306a36Sopenharmony_ci			phydev = mdiobus_get_phy(aup->mii_bus, phy_addr);
49462306a36Sopenharmony_ci			if (!aup->phy_search_highest_addr)
49562306a36Sopenharmony_ci				/* break out with first one found */
49662306a36Sopenharmony_ci				break;
49762306a36Sopenharmony_ci		}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (aup->phy1_search_mac0) {
50062306a36Sopenharmony_ci		/* try harder to find a PHY */
50162306a36Sopenharmony_ci		if (!phydev && (aup->mac_id == 1)) {
50262306a36Sopenharmony_ci			/* no PHY found, maybe we have a dual PHY? */
50362306a36Sopenharmony_ci			dev_info(&dev->dev, ": no PHY found on MAC1, "
50462306a36Sopenharmony_ci				"let's see if it's attached to MAC0...\n");
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci			/* find the first (lowest address) non-attached
50762306a36Sopenharmony_ci			 * PHY on the MAC0 MII bus
50862306a36Sopenharmony_ci			 */
50962306a36Sopenharmony_ci			for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
51062306a36Sopenharmony_ci				struct phy_device *const tmp_phydev =
51162306a36Sopenharmony_ci					mdiobus_get_phy(aup->mii_bus,
51262306a36Sopenharmony_ci							phy_addr);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci				if (aup->mac_id == 1)
51562306a36Sopenharmony_ci					break;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci				/* no PHY here... */
51862306a36Sopenharmony_ci				if (!tmp_phydev)
51962306a36Sopenharmony_ci					continue;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci				/* already claimed by MAC0 */
52262306a36Sopenharmony_ci				if (tmp_phydev->attached_dev)
52362306a36Sopenharmony_ci					continue;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci				phydev = tmp_phydev;
52662306a36Sopenharmony_ci				break; /* found it */
52762306a36Sopenharmony_ci			}
52862306a36Sopenharmony_ci		}
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	if (!phydev) {
53262306a36Sopenharmony_ci		netdev_err(dev, "no PHY found\n");
53362306a36Sopenharmony_ci		return -1;
53462306a36Sopenharmony_ci	}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	/* now we are supposed to have a proper phydev, to attach to... */
53762306a36Sopenharmony_ci	BUG_ON(phydev->attached_dev);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	phydev = phy_connect(dev, phydev_name(phydev),
54062306a36Sopenharmony_ci			     &au1000_adjust_link, PHY_INTERFACE_MODE_MII);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	if (IS_ERR(phydev)) {
54362306a36Sopenharmony_ci		netdev_err(dev, "Could not attach to PHY\n");
54462306a36Sopenharmony_ci		return PTR_ERR(phydev);
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	phy_set_max_speed(phydev, SPEED_100);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	aup->old_link = 0;
55062306a36Sopenharmony_ci	aup->old_speed = 0;
55162306a36Sopenharmony_ci	aup->old_duplex = -1;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	phy_attached_info(phydev);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	return 0;
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci/*
55962306a36Sopenharmony_ci * Buffer allocation/deallocation routines. The buffer descriptor returned
56062306a36Sopenharmony_ci * has the virtual and dma address of a buffer suitable for
56162306a36Sopenharmony_ci * both, receive and transmit operations.
56262306a36Sopenharmony_ci */
56362306a36Sopenharmony_cistatic struct db_dest *au1000_GetFreeDB(struct au1000_private *aup)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	struct db_dest *pDB;
56662306a36Sopenharmony_ci	pDB = aup->pDBfree;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	if (pDB)
56962306a36Sopenharmony_ci		aup->pDBfree = pDB->pnext;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	return pDB;
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_civoid au1000_ReleaseDB(struct au1000_private *aup, struct db_dest *pDB)
57562306a36Sopenharmony_ci{
57662306a36Sopenharmony_ci	struct db_dest *pDBfree = aup->pDBfree;
57762306a36Sopenharmony_ci	if (pDBfree)
57862306a36Sopenharmony_ci		pDBfree->pnext = pDB;
57962306a36Sopenharmony_ci	aup->pDBfree = pDB;
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic void au1000_reset_mac_unlocked(struct net_device *dev)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	struct au1000_private *const aup = netdev_priv(dev);
58562306a36Sopenharmony_ci	int i;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	au1000_hard_stop(dev);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	writel(MAC_EN_CLOCK_ENABLE, aup->enable);
59062306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
59162306a36Sopenharmony_ci	mdelay(2);
59262306a36Sopenharmony_ci	writel(0, aup->enable);
59362306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
59462306a36Sopenharmony_ci	mdelay(2);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	aup->tx_full = 0;
59762306a36Sopenharmony_ci	for (i = 0; i < NUM_RX_DMA; i++) {
59862306a36Sopenharmony_ci		/* reset control bits */
59962306a36Sopenharmony_ci		aup->rx_dma_ring[i]->buff_stat &= ~0xf;
60062306a36Sopenharmony_ci	}
60162306a36Sopenharmony_ci	for (i = 0; i < NUM_TX_DMA; i++) {
60262306a36Sopenharmony_ci		/* reset control bits */
60362306a36Sopenharmony_ci		aup->tx_dma_ring[i]->buff_stat &= ~0xf;
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	aup->mac_enabled = 0;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic void au1000_reset_mac(struct net_device *dev)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct au1000_private *const aup = netdev_priv(dev);
61362306a36Sopenharmony_ci	unsigned long flags;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	netif_dbg(aup, hw, dev, "reset mac, aup %x\n",
61662306a36Sopenharmony_ci					(unsigned)aup);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	spin_lock_irqsave(&aup->lock, flags);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	au1000_reset_mac_unlocked(dev);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	spin_unlock_irqrestore(&aup->lock, flags);
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci/*
62662306a36Sopenharmony_ci * Setup the receive and transmit "rings".  These pointers are the addresses
62762306a36Sopenharmony_ci * of the rx and tx MAC DMA registers so they are fixed by the hardware --
62862306a36Sopenharmony_ci * these are not descriptors sitting in memory.
62962306a36Sopenharmony_ci */
63062306a36Sopenharmony_cistatic void
63162306a36Sopenharmony_ciau1000_setup_hw_rings(struct au1000_private *aup, void __iomem *tx_base)
63262306a36Sopenharmony_ci{
63362306a36Sopenharmony_ci	int i;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	for (i = 0; i < NUM_RX_DMA; i++) {
63662306a36Sopenharmony_ci		aup->rx_dma_ring[i] = (struct rx_dma *)
63762306a36Sopenharmony_ci			(tx_base + 0x100 + sizeof(struct rx_dma) * i);
63862306a36Sopenharmony_ci	}
63962306a36Sopenharmony_ci	for (i = 0; i < NUM_TX_DMA; i++) {
64062306a36Sopenharmony_ci		aup->tx_dma_ring[i] = (struct tx_dma *)
64162306a36Sopenharmony_ci			(tx_base + sizeof(struct tx_dma) * i);
64262306a36Sopenharmony_ci	}
64362306a36Sopenharmony_ci}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci/*
64662306a36Sopenharmony_ci * ethtool operations
64762306a36Sopenharmony_ci */
64862306a36Sopenharmony_cistatic void
64962306a36Sopenharmony_ciau1000_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
65462306a36Sopenharmony_ci	snprintf(info->bus_info, sizeof(info->bus_info), "%s %d", DRV_NAME,
65562306a36Sopenharmony_ci		 aup->mac_id);
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_cistatic void au1000_set_msglevel(struct net_device *dev, u32 value)
65962306a36Sopenharmony_ci{
66062306a36Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
66162306a36Sopenharmony_ci	aup->msg_enable = value;
66262306a36Sopenharmony_ci}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_cistatic u32 au1000_get_msglevel(struct net_device *dev)
66562306a36Sopenharmony_ci{
66662306a36Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
66762306a36Sopenharmony_ci	return aup->msg_enable;
66862306a36Sopenharmony_ci}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_cistatic const struct ethtool_ops au1000_ethtool_ops = {
67162306a36Sopenharmony_ci	.get_drvinfo = au1000_get_drvinfo,
67262306a36Sopenharmony_ci	.get_link = ethtool_op_get_link,
67362306a36Sopenharmony_ci	.get_msglevel = au1000_get_msglevel,
67462306a36Sopenharmony_ci	.set_msglevel = au1000_set_msglevel,
67562306a36Sopenharmony_ci	.get_link_ksettings = phy_ethtool_get_link_ksettings,
67662306a36Sopenharmony_ci	.set_link_ksettings = phy_ethtool_set_link_ksettings,
67762306a36Sopenharmony_ci};
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci/*
68062306a36Sopenharmony_ci * Initialize the interface.
68162306a36Sopenharmony_ci *
68262306a36Sopenharmony_ci * When the device powers up, the clocks are disabled and the
68362306a36Sopenharmony_ci * mac is in reset state.  When the interface is closed, we
68462306a36Sopenharmony_ci * do the same -- reset the device and disable the clocks to
68562306a36Sopenharmony_ci * conserve power. Thus, whenever au1000_init() is called,
68662306a36Sopenharmony_ci * the device should already be in reset state.
68762306a36Sopenharmony_ci */
68862306a36Sopenharmony_cistatic int au1000_init(struct net_device *dev)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
69162306a36Sopenharmony_ci	unsigned long flags;
69262306a36Sopenharmony_ci	int i;
69362306a36Sopenharmony_ci	u32 control;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	netif_dbg(aup, hw, dev, "au1000_init\n");
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	/* bring the device out of reset */
69862306a36Sopenharmony_ci	au1000_enable_mac(dev, 1);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	spin_lock_irqsave(&aup->lock, flags);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	writel(0, &aup->mac->control);
70362306a36Sopenharmony_ci	aup->tx_head = (aup->tx_dma_ring[0]->buff_stat & 0xC) >> 2;
70462306a36Sopenharmony_ci	aup->tx_tail = aup->tx_head;
70562306a36Sopenharmony_ci	aup->rx_head = (aup->rx_dma_ring[0]->buff_stat & 0xC) >> 2;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	writel(dev->dev_addr[5]<<8 | dev->dev_addr[4],
70862306a36Sopenharmony_ci					&aup->mac->mac_addr_high);
70962306a36Sopenharmony_ci	writel(dev->dev_addr[3]<<24 | dev->dev_addr[2]<<16 |
71062306a36Sopenharmony_ci		dev->dev_addr[1]<<8 | dev->dev_addr[0],
71162306a36Sopenharmony_ci					&aup->mac->mac_addr_low);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	for (i = 0; i < NUM_RX_DMA; i++)
71562306a36Sopenharmony_ci		aup->rx_dma_ring[i]->buff_stat |= RX_DMA_ENABLE;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	control = MAC_RX_ENABLE | MAC_TX_ENABLE;
72062306a36Sopenharmony_ci#ifndef CONFIG_CPU_LITTLE_ENDIAN
72162306a36Sopenharmony_ci	control |= MAC_BIG_ENDIAN;
72262306a36Sopenharmony_ci#endif
72362306a36Sopenharmony_ci	if (dev->phydev) {
72462306a36Sopenharmony_ci		if (dev->phydev->link && (DUPLEX_FULL == dev->phydev->duplex))
72562306a36Sopenharmony_ci			control |= MAC_FULL_DUPLEX;
72662306a36Sopenharmony_ci		else
72762306a36Sopenharmony_ci			control |= MAC_DISABLE_RX_OWN;
72862306a36Sopenharmony_ci	} else { /* PHY-less op, assume full-duplex */
72962306a36Sopenharmony_ci		control |= MAC_FULL_DUPLEX;
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	writel(control, &aup->mac->control);
73362306a36Sopenharmony_ci	writel(0x8100, &aup->mac->vlan1_tag); /* activate vlan support */
73462306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	spin_unlock_irqrestore(&aup->lock, flags);
73762306a36Sopenharmony_ci	return 0;
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cistatic inline void au1000_update_rx_stats(struct net_device *dev, u32 status)
74162306a36Sopenharmony_ci{
74262306a36Sopenharmony_ci	struct net_device_stats *ps = &dev->stats;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	ps->rx_packets++;
74562306a36Sopenharmony_ci	if (status & RX_MCAST_FRAME)
74662306a36Sopenharmony_ci		ps->multicast++;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	if (status & RX_ERROR) {
74962306a36Sopenharmony_ci		ps->rx_errors++;
75062306a36Sopenharmony_ci		if (status & RX_MISSED_FRAME)
75162306a36Sopenharmony_ci			ps->rx_missed_errors++;
75262306a36Sopenharmony_ci		if (status & (RX_OVERLEN | RX_RUNT | RX_LEN_ERROR))
75362306a36Sopenharmony_ci			ps->rx_length_errors++;
75462306a36Sopenharmony_ci		if (status & RX_CRC_ERROR)
75562306a36Sopenharmony_ci			ps->rx_crc_errors++;
75662306a36Sopenharmony_ci		if (status & RX_COLL)
75762306a36Sopenharmony_ci			ps->collisions++;
75862306a36Sopenharmony_ci	} else
75962306a36Sopenharmony_ci		ps->rx_bytes += status & RX_FRAME_LEN_MASK;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci/*
76462306a36Sopenharmony_ci * Au1000 receive routine.
76562306a36Sopenharmony_ci */
76662306a36Sopenharmony_cistatic int au1000_rx(struct net_device *dev)
76762306a36Sopenharmony_ci{
76862306a36Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
76962306a36Sopenharmony_ci	struct sk_buff *skb;
77062306a36Sopenharmony_ci	struct rx_dma *prxd;
77162306a36Sopenharmony_ci	u32 buff_stat, status;
77262306a36Sopenharmony_ci	struct db_dest *pDB;
77362306a36Sopenharmony_ci	u32	frmlen;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	netif_dbg(aup, rx_status, dev, "au1000_rx head %d\n", aup->rx_head);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	prxd = aup->rx_dma_ring[aup->rx_head];
77862306a36Sopenharmony_ci	buff_stat = prxd->buff_stat;
77962306a36Sopenharmony_ci	while (buff_stat & RX_T_DONE)  {
78062306a36Sopenharmony_ci		status = prxd->status;
78162306a36Sopenharmony_ci		pDB = aup->rx_db_inuse[aup->rx_head];
78262306a36Sopenharmony_ci		au1000_update_rx_stats(dev, status);
78362306a36Sopenharmony_ci		if (!(status & RX_ERROR))  {
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci			/* good frame */
78662306a36Sopenharmony_ci			frmlen = (status & RX_FRAME_LEN_MASK);
78762306a36Sopenharmony_ci			frmlen -= 4; /* Remove FCS */
78862306a36Sopenharmony_ci			skb = netdev_alloc_skb(dev, frmlen + 2);
78962306a36Sopenharmony_ci			if (!skb) {
79062306a36Sopenharmony_ci				dev->stats.rx_dropped++;
79162306a36Sopenharmony_ci				continue;
79262306a36Sopenharmony_ci			}
79362306a36Sopenharmony_ci			skb_reserve(skb, 2);	/* 16 byte IP header align */
79462306a36Sopenharmony_ci			skb_copy_to_linear_data(skb,
79562306a36Sopenharmony_ci				(unsigned char *)pDB->vaddr, frmlen);
79662306a36Sopenharmony_ci			skb_put(skb, frmlen);
79762306a36Sopenharmony_ci			skb->protocol = eth_type_trans(skb, dev);
79862306a36Sopenharmony_ci			netif_rx(skb);	/* pass the packet to upper layers */
79962306a36Sopenharmony_ci		} else {
80062306a36Sopenharmony_ci			if (au1000_debug > 4) {
80162306a36Sopenharmony_ci				pr_err("rx_error(s):");
80262306a36Sopenharmony_ci				if (status & RX_MISSED_FRAME)
80362306a36Sopenharmony_ci					pr_cont(" miss");
80462306a36Sopenharmony_ci				if (status & RX_WDOG_TIMER)
80562306a36Sopenharmony_ci					pr_cont(" wdog");
80662306a36Sopenharmony_ci				if (status & RX_RUNT)
80762306a36Sopenharmony_ci					pr_cont(" runt");
80862306a36Sopenharmony_ci				if (status & RX_OVERLEN)
80962306a36Sopenharmony_ci					pr_cont(" overlen");
81062306a36Sopenharmony_ci				if (status & RX_COLL)
81162306a36Sopenharmony_ci					pr_cont(" coll");
81262306a36Sopenharmony_ci				if (status & RX_MII_ERROR)
81362306a36Sopenharmony_ci					pr_cont(" mii error");
81462306a36Sopenharmony_ci				if (status & RX_CRC_ERROR)
81562306a36Sopenharmony_ci					pr_cont(" crc error");
81662306a36Sopenharmony_ci				if (status & RX_LEN_ERROR)
81762306a36Sopenharmony_ci					pr_cont(" len error");
81862306a36Sopenharmony_ci				if (status & RX_U_CNTRL_FRAME)
81962306a36Sopenharmony_ci					pr_cont(" u control frame");
82062306a36Sopenharmony_ci				pr_cont("\n");
82162306a36Sopenharmony_ci			}
82262306a36Sopenharmony_ci		}
82362306a36Sopenharmony_ci		prxd->buff_stat = lower_32_bits(pDB->dma_addr) | RX_DMA_ENABLE;
82462306a36Sopenharmony_ci		aup->rx_head = (aup->rx_head + 1) & (NUM_RX_DMA - 1);
82562306a36Sopenharmony_ci		wmb(); /* drain writebuffer */
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci		/* next descriptor */
82862306a36Sopenharmony_ci		prxd = aup->rx_dma_ring[aup->rx_head];
82962306a36Sopenharmony_ci		buff_stat = prxd->buff_stat;
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci	return 0;
83262306a36Sopenharmony_ci}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_cistatic void au1000_update_tx_stats(struct net_device *dev, u32 status)
83562306a36Sopenharmony_ci{
83662306a36Sopenharmony_ci	struct net_device_stats *ps = &dev->stats;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	if (status & TX_FRAME_ABORTED) {
83962306a36Sopenharmony_ci		if (!dev->phydev || (DUPLEX_FULL == dev->phydev->duplex)) {
84062306a36Sopenharmony_ci			if (status & (TX_JAB_TIMEOUT | TX_UNDERRUN)) {
84162306a36Sopenharmony_ci				/* any other tx errors are only valid
84262306a36Sopenharmony_ci				 * in half duplex mode
84362306a36Sopenharmony_ci				 */
84462306a36Sopenharmony_ci				ps->tx_errors++;
84562306a36Sopenharmony_ci				ps->tx_aborted_errors++;
84662306a36Sopenharmony_ci			}
84762306a36Sopenharmony_ci		} else {
84862306a36Sopenharmony_ci			ps->tx_errors++;
84962306a36Sopenharmony_ci			ps->tx_aborted_errors++;
85062306a36Sopenharmony_ci			if (status & (TX_NO_CARRIER | TX_LOSS_CARRIER))
85162306a36Sopenharmony_ci				ps->tx_carrier_errors++;
85262306a36Sopenharmony_ci		}
85362306a36Sopenharmony_ci	}
85462306a36Sopenharmony_ci}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci/*
85762306a36Sopenharmony_ci * Called from the interrupt service routine to acknowledge
85862306a36Sopenharmony_ci * the TX DONE bits.  This is a must if the irq is setup as
85962306a36Sopenharmony_ci * edge triggered.
86062306a36Sopenharmony_ci */
86162306a36Sopenharmony_cistatic void au1000_tx_ack(struct net_device *dev)
86262306a36Sopenharmony_ci{
86362306a36Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
86462306a36Sopenharmony_ci	struct tx_dma *ptxd;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	ptxd = aup->tx_dma_ring[aup->tx_tail];
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	while (ptxd->buff_stat & TX_T_DONE) {
86962306a36Sopenharmony_ci		au1000_update_tx_stats(dev, ptxd->status);
87062306a36Sopenharmony_ci		ptxd->buff_stat &= ~TX_T_DONE;
87162306a36Sopenharmony_ci		ptxd->len = 0;
87262306a36Sopenharmony_ci		wmb(); /* drain writebuffer */
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci		aup->tx_tail = (aup->tx_tail + 1) & (NUM_TX_DMA - 1);
87562306a36Sopenharmony_ci		ptxd = aup->tx_dma_ring[aup->tx_tail];
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci		if (aup->tx_full) {
87862306a36Sopenharmony_ci			aup->tx_full = 0;
87962306a36Sopenharmony_ci			netif_wake_queue(dev);
88062306a36Sopenharmony_ci		}
88162306a36Sopenharmony_ci	}
88262306a36Sopenharmony_ci}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci/*
88562306a36Sopenharmony_ci * Au1000 interrupt service routine.
88662306a36Sopenharmony_ci */
88762306a36Sopenharmony_cistatic irqreturn_t au1000_interrupt(int irq, void *dev_id)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	struct net_device *dev = dev_id;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	/* Handle RX interrupts first to minimize chance of overrun */
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	au1000_rx(dev);
89462306a36Sopenharmony_ci	au1000_tx_ack(dev);
89562306a36Sopenharmony_ci	return IRQ_RETVAL(1);
89662306a36Sopenharmony_ci}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_cistatic int au1000_open(struct net_device *dev)
89962306a36Sopenharmony_ci{
90062306a36Sopenharmony_ci	int retval;
90162306a36Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	netif_dbg(aup, drv, dev, "open: dev=%p\n", dev);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	retval = request_irq(dev->irq, au1000_interrupt, 0,
90662306a36Sopenharmony_ci					dev->name, dev);
90762306a36Sopenharmony_ci	if (retval) {
90862306a36Sopenharmony_ci		netdev_err(dev, "unable to get IRQ %d\n", dev->irq);
90962306a36Sopenharmony_ci		return retval;
91062306a36Sopenharmony_ci	}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	retval = au1000_init(dev);
91362306a36Sopenharmony_ci	if (retval) {
91462306a36Sopenharmony_ci		netdev_err(dev, "error in au1000_init\n");
91562306a36Sopenharmony_ci		free_irq(dev->irq, dev);
91662306a36Sopenharmony_ci		return retval;
91762306a36Sopenharmony_ci	}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	if (dev->phydev)
92062306a36Sopenharmony_ci		phy_start(dev->phydev);
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	netif_start_queue(dev);
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	netif_dbg(aup, drv, dev, "open: Initialization done.\n");
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	return 0;
92762306a36Sopenharmony_ci}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_cistatic int au1000_close(struct net_device *dev)
93062306a36Sopenharmony_ci{
93162306a36Sopenharmony_ci	unsigned long flags;
93262306a36Sopenharmony_ci	struct au1000_private *const aup = netdev_priv(dev);
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	netif_dbg(aup, drv, dev, "close: dev=%p\n", dev);
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	if (dev->phydev)
93762306a36Sopenharmony_ci		phy_stop(dev->phydev);
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	spin_lock_irqsave(&aup->lock, flags);
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	au1000_reset_mac_unlocked(dev);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	/* stop the device */
94462306a36Sopenharmony_ci	netif_stop_queue(dev);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	/* disable the interrupt */
94762306a36Sopenharmony_ci	free_irq(dev->irq, dev);
94862306a36Sopenharmony_ci	spin_unlock_irqrestore(&aup->lock, flags);
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	return 0;
95162306a36Sopenharmony_ci}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci/*
95462306a36Sopenharmony_ci * Au1000 transmit routine.
95562306a36Sopenharmony_ci */
95662306a36Sopenharmony_cistatic netdev_tx_t au1000_tx(struct sk_buff *skb, struct net_device *dev)
95762306a36Sopenharmony_ci{
95862306a36Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
95962306a36Sopenharmony_ci	struct net_device_stats *ps = &dev->stats;
96062306a36Sopenharmony_ci	struct tx_dma *ptxd;
96162306a36Sopenharmony_ci	u32 buff_stat;
96262306a36Sopenharmony_ci	struct db_dest *pDB;
96362306a36Sopenharmony_ci	int i;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	netif_dbg(aup, tx_queued, dev, "tx: aup %x len=%d, data=%p, head %d\n",
96662306a36Sopenharmony_ci				(unsigned)aup, skb->len,
96762306a36Sopenharmony_ci				skb->data, aup->tx_head);
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	ptxd = aup->tx_dma_ring[aup->tx_head];
97062306a36Sopenharmony_ci	buff_stat = ptxd->buff_stat;
97162306a36Sopenharmony_ci	if (buff_stat & TX_DMA_ENABLE) {
97262306a36Sopenharmony_ci		/* We've wrapped around and the transmitter is still busy */
97362306a36Sopenharmony_ci		netif_stop_queue(dev);
97462306a36Sopenharmony_ci		aup->tx_full = 1;
97562306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
97662306a36Sopenharmony_ci	} else if (buff_stat & TX_T_DONE) {
97762306a36Sopenharmony_ci		au1000_update_tx_stats(dev, ptxd->status);
97862306a36Sopenharmony_ci		ptxd->len = 0;
97962306a36Sopenharmony_ci	}
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	if (aup->tx_full) {
98262306a36Sopenharmony_ci		aup->tx_full = 0;
98362306a36Sopenharmony_ci		netif_wake_queue(dev);
98462306a36Sopenharmony_ci	}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	pDB = aup->tx_db_inuse[aup->tx_head];
98762306a36Sopenharmony_ci	skb_copy_from_linear_data(skb, (void *)pDB->vaddr, skb->len);
98862306a36Sopenharmony_ci	if (skb->len < ETH_ZLEN) {
98962306a36Sopenharmony_ci		for (i = skb->len; i < ETH_ZLEN; i++)
99062306a36Sopenharmony_ci			((char *)pDB->vaddr)[i] = 0;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci		ptxd->len = ETH_ZLEN;
99362306a36Sopenharmony_ci	} else
99462306a36Sopenharmony_ci		ptxd->len = skb->len;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	ps->tx_packets++;
99762306a36Sopenharmony_ci	ps->tx_bytes += ptxd->len;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	ptxd->buff_stat = lower_32_bits(pDB->dma_addr) | TX_DMA_ENABLE;
100062306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
100162306a36Sopenharmony_ci	dev_kfree_skb(skb);
100262306a36Sopenharmony_ci	aup->tx_head = (aup->tx_head + 1) & (NUM_TX_DMA - 1);
100362306a36Sopenharmony_ci	return NETDEV_TX_OK;
100462306a36Sopenharmony_ci}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci/*
100762306a36Sopenharmony_ci * The Tx ring has been full longer than the watchdog timeout
100862306a36Sopenharmony_ci * value. The transmitter must be hung?
100962306a36Sopenharmony_ci */
101062306a36Sopenharmony_cistatic void au1000_tx_timeout(struct net_device *dev, unsigned int txqueue)
101162306a36Sopenharmony_ci{
101262306a36Sopenharmony_ci	netdev_err(dev, "au1000_tx_timeout: dev=%p\n", dev);
101362306a36Sopenharmony_ci	au1000_reset_mac(dev);
101462306a36Sopenharmony_ci	au1000_init(dev);
101562306a36Sopenharmony_ci	netif_trans_update(dev); /* prevent tx timeout */
101662306a36Sopenharmony_ci	netif_wake_queue(dev);
101762306a36Sopenharmony_ci}
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_cistatic void au1000_multicast_list(struct net_device *dev)
102062306a36Sopenharmony_ci{
102162306a36Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
102262306a36Sopenharmony_ci	u32 reg;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	netif_dbg(aup, drv, dev, "%s: flags=%x\n", __func__, dev->flags);
102562306a36Sopenharmony_ci	reg = readl(&aup->mac->control);
102662306a36Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
102762306a36Sopenharmony_ci		reg |= MAC_PROMISCUOUS;
102862306a36Sopenharmony_ci	} else if ((dev->flags & IFF_ALLMULTI)  ||
102962306a36Sopenharmony_ci			   netdev_mc_count(dev) > MULTICAST_FILTER_LIMIT) {
103062306a36Sopenharmony_ci		reg |= MAC_PASS_ALL_MULTI;
103162306a36Sopenharmony_ci		reg &= ~MAC_PROMISCUOUS;
103262306a36Sopenharmony_ci		netdev_info(dev, "Pass all multicast\n");
103362306a36Sopenharmony_ci	} else {
103462306a36Sopenharmony_ci		struct netdev_hw_addr *ha;
103562306a36Sopenharmony_ci		u32 mc_filter[2];	/* Multicast hash filter */
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci		mc_filter[1] = mc_filter[0] = 0;
103862306a36Sopenharmony_ci		netdev_for_each_mc_addr(ha, dev)
103962306a36Sopenharmony_ci			set_bit(ether_crc(ETH_ALEN, ha->addr)>>26,
104062306a36Sopenharmony_ci					(long *)mc_filter);
104162306a36Sopenharmony_ci		writel(mc_filter[1], &aup->mac->multi_hash_high);
104262306a36Sopenharmony_ci		writel(mc_filter[0], &aup->mac->multi_hash_low);
104362306a36Sopenharmony_ci		reg &= ~MAC_PROMISCUOUS;
104462306a36Sopenharmony_ci		reg |= MAC_HASH_MODE;
104562306a36Sopenharmony_ci	}
104662306a36Sopenharmony_ci	writel(reg, &aup->mac->control);
104762306a36Sopenharmony_ci}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_cistatic const struct net_device_ops au1000_netdev_ops = {
105062306a36Sopenharmony_ci	.ndo_open		= au1000_open,
105162306a36Sopenharmony_ci	.ndo_stop		= au1000_close,
105262306a36Sopenharmony_ci	.ndo_start_xmit		= au1000_tx,
105362306a36Sopenharmony_ci	.ndo_set_rx_mode	= au1000_multicast_list,
105462306a36Sopenharmony_ci	.ndo_eth_ioctl		= phy_do_ioctl_running,
105562306a36Sopenharmony_ci	.ndo_tx_timeout		= au1000_tx_timeout,
105662306a36Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
105762306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
105862306a36Sopenharmony_ci};
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_cistatic int au1000_probe(struct platform_device *pdev)
106162306a36Sopenharmony_ci{
106262306a36Sopenharmony_ci	struct au1000_private *aup = NULL;
106362306a36Sopenharmony_ci	struct au1000_eth_platform_data *pd;
106462306a36Sopenharmony_ci	struct net_device *dev = NULL;
106562306a36Sopenharmony_ci	struct db_dest *pDB, *pDBfree;
106662306a36Sopenharmony_ci	int irq, i, err = 0;
106762306a36Sopenharmony_ci	struct resource *base, *macen, *macdma;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
107062306a36Sopenharmony_ci	if (!base) {
107162306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to retrieve base register\n");
107262306a36Sopenharmony_ci		err = -ENODEV;
107362306a36Sopenharmony_ci		goto out;
107462306a36Sopenharmony_ci	}
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	macen = platform_get_resource(pdev, IORESOURCE_MEM, 1);
107762306a36Sopenharmony_ci	if (!macen) {
107862306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to retrieve MAC Enable register\n");
107962306a36Sopenharmony_ci		err = -ENODEV;
108062306a36Sopenharmony_ci		goto out;
108162306a36Sopenharmony_ci	}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
108462306a36Sopenharmony_ci	if (irq < 0) {
108562306a36Sopenharmony_ci		err = -ENODEV;
108662306a36Sopenharmony_ci		goto out;
108762306a36Sopenharmony_ci	}
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	macdma = platform_get_resource(pdev, IORESOURCE_MEM, 2);
109062306a36Sopenharmony_ci	if (!macdma) {
109162306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to retrieve MACDMA registers\n");
109262306a36Sopenharmony_ci		err = -ENODEV;
109362306a36Sopenharmony_ci		goto out;
109462306a36Sopenharmony_ci	}
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	if (!request_mem_region(base->start, resource_size(base),
109762306a36Sopenharmony_ci							pdev->name)) {
109862306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to request memory region for base registers\n");
109962306a36Sopenharmony_ci		err = -ENXIO;
110062306a36Sopenharmony_ci		goto out;
110162306a36Sopenharmony_ci	}
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	if (!request_mem_region(macen->start, resource_size(macen),
110462306a36Sopenharmony_ci							pdev->name)) {
110562306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to request memory region for MAC enable register\n");
110662306a36Sopenharmony_ci		err = -ENXIO;
110762306a36Sopenharmony_ci		goto err_request;
110862306a36Sopenharmony_ci	}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	if (!request_mem_region(macdma->start, resource_size(macdma),
111162306a36Sopenharmony_ci							pdev->name)) {
111262306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to request MACDMA memory region\n");
111362306a36Sopenharmony_ci		err = -ENXIO;
111462306a36Sopenharmony_ci		goto err_macdma;
111562306a36Sopenharmony_ci	}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof(struct au1000_private));
111862306a36Sopenharmony_ci	if (!dev) {
111962306a36Sopenharmony_ci		err = -ENOMEM;
112062306a36Sopenharmony_ci		goto err_alloc;
112162306a36Sopenharmony_ci	}
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, &pdev->dev);
112462306a36Sopenharmony_ci	platform_set_drvdata(pdev, dev);
112562306a36Sopenharmony_ci	aup = netdev_priv(dev);
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	spin_lock_init(&aup->lock);
112862306a36Sopenharmony_ci	aup->msg_enable = (au1000_debug < 4 ?
112962306a36Sopenharmony_ci				AU1000_DEF_MSG_ENABLE : au1000_debug);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	/* Allocate the data buffers
113262306a36Sopenharmony_ci	 * Snooping works fine with eth on all au1xxx
113362306a36Sopenharmony_ci	 */
113462306a36Sopenharmony_ci	aup->vaddr = dma_alloc_coherent(&pdev->dev, MAX_BUF_SIZE *
113562306a36Sopenharmony_ci					(NUM_TX_BUFFS + NUM_RX_BUFFS),
113662306a36Sopenharmony_ci					&aup->dma_addr, 0);
113762306a36Sopenharmony_ci	if (!aup->vaddr) {
113862306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to allocate data buffers\n");
113962306a36Sopenharmony_ci		err = -ENOMEM;
114062306a36Sopenharmony_ci		goto err_vaddr;
114162306a36Sopenharmony_ci	}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	/* aup->mac is the base address of the MAC's registers */
114462306a36Sopenharmony_ci	aup->mac = (struct mac_reg *)
114562306a36Sopenharmony_ci			ioremap(base->start, resource_size(base));
114662306a36Sopenharmony_ci	if (!aup->mac) {
114762306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to ioremap MAC registers\n");
114862306a36Sopenharmony_ci		err = -ENXIO;
114962306a36Sopenharmony_ci		goto err_remap1;
115062306a36Sopenharmony_ci	}
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	/* Setup some variables for quick register address access */
115362306a36Sopenharmony_ci	aup->enable = (u32 *)ioremap(macen->start,
115462306a36Sopenharmony_ci						resource_size(macen));
115562306a36Sopenharmony_ci	if (!aup->enable) {
115662306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to ioremap MAC enable register\n");
115762306a36Sopenharmony_ci		err = -ENXIO;
115862306a36Sopenharmony_ci		goto err_remap2;
115962306a36Sopenharmony_ci	}
116062306a36Sopenharmony_ci	aup->mac_id = pdev->id;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	aup->macdma = ioremap(macdma->start, resource_size(macdma));
116362306a36Sopenharmony_ci	if (!aup->macdma) {
116462306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to ioremap MACDMA registers\n");
116562306a36Sopenharmony_ci		err = -ENXIO;
116662306a36Sopenharmony_ci		goto err_remap3;
116762306a36Sopenharmony_ci	}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	au1000_setup_hw_rings(aup, aup->macdma);
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	writel(0, aup->enable);
117262306a36Sopenharmony_ci	aup->mac_enabled = 0;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	pd = dev_get_platdata(&pdev->dev);
117562306a36Sopenharmony_ci	if (!pd) {
117662306a36Sopenharmony_ci		dev_info(&pdev->dev, "no platform_data passed,"
117762306a36Sopenharmony_ci					" PHY search on MAC0\n");
117862306a36Sopenharmony_ci		aup->phy1_search_mac0 = 1;
117962306a36Sopenharmony_ci	} else {
118062306a36Sopenharmony_ci		if (is_valid_ether_addr(pd->mac)) {
118162306a36Sopenharmony_ci			eth_hw_addr_set(dev, pd->mac);
118262306a36Sopenharmony_ci		} else {
118362306a36Sopenharmony_ci			/* Set a random MAC since no valid provided by platform_data. */
118462306a36Sopenharmony_ci			eth_hw_addr_random(dev);
118562306a36Sopenharmony_ci		}
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci		aup->phy_static_config = pd->phy_static_config;
118862306a36Sopenharmony_ci		aup->phy_search_highest_addr = pd->phy_search_highest_addr;
118962306a36Sopenharmony_ci		aup->phy1_search_mac0 = pd->phy1_search_mac0;
119062306a36Sopenharmony_ci		aup->phy_addr = pd->phy_addr;
119162306a36Sopenharmony_ci		aup->phy_busid = pd->phy_busid;
119262306a36Sopenharmony_ci		aup->phy_irq = pd->phy_irq;
119362306a36Sopenharmony_ci	}
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	if (aup->phy_busid > 0) {
119662306a36Sopenharmony_ci		dev_err(&pdev->dev, "MAC0-associated PHY attached 2nd MACs MII bus not supported yet\n");
119762306a36Sopenharmony_ci		err = -ENODEV;
119862306a36Sopenharmony_ci		goto err_mdiobus_alloc;
119962306a36Sopenharmony_ci	}
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	aup->mii_bus = mdiobus_alloc();
120262306a36Sopenharmony_ci	if (!aup->mii_bus) {
120362306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to allocate mdiobus structure\n");
120462306a36Sopenharmony_ci		err = -ENOMEM;
120562306a36Sopenharmony_ci		goto err_mdiobus_alloc;
120662306a36Sopenharmony_ci	}
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	aup->mii_bus->priv = dev;
120962306a36Sopenharmony_ci	aup->mii_bus->read = au1000_mdiobus_read;
121062306a36Sopenharmony_ci	aup->mii_bus->write = au1000_mdiobus_write;
121162306a36Sopenharmony_ci	aup->mii_bus->reset = au1000_mdiobus_reset;
121262306a36Sopenharmony_ci	aup->mii_bus->name = "au1000_eth_mii";
121362306a36Sopenharmony_ci	snprintf(aup->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
121462306a36Sopenharmony_ci		pdev->name, aup->mac_id);
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	/* if known, set corresponding PHY IRQs */
121762306a36Sopenharmony_ci	if (aup->phy_static_config)
121862306a36Sopenharmony_ci		if (aup->phy_irq && aup->phy_busid == aup->mac_id)
121962306a36Sopenharmony_ci			aup->mii_bus->irq[aup->phy_addr] = aup->phy_irq;
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	err = mdiobus_register(aup->mii_bus);
122262306a36Sopenharmony_ci	if (err) {
122362306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to register MDIO bus\n");
122462306a36Sopenharmony_ci		goto err_mdiobus_reg;
122562306a36Sopenharmony_ci	}
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	err = au1000_mii_probe(dev);
122862306a36Sopenharmony_ci	if (err != 0)
122962306a36Sopenharmony_ci		goto err_out;
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	pDBfree = NULL;
123262306a36Sopenharmony_ci	/* setup the data buffer descriptors and attach a buffer to each one */
123362306a36Sopenharmony_ci	pDB = aup->db;
123462306a36Sopenharmony_ci	for (i = 0; i < (NUM_TX_BUFFS+NUM_RX_BUFFS); i++) {
123562306a36Sopenharmony_ci		pDB->pnext = pDBfree;
123662306a36Sopenharmony_ci		pDBfree = pDB;
123762306a36Sopenharmony_ci		pDB->vaddr = aup->vaddr + MAX_BUF_SIZE * i;
123862306a36Sopenharmony_ci		pDB->dma_addr = aup->dma_addr + MAX_BUF_SIZE * i;
123962306a36Sopenharmony_ci		pDB++;
124062306a36Sopenharmony_ci	}
124162306a36Sopenharmony_ci	aup->pDBfree = pDBfree;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	err = -ENODEV;
124462306a36Sopenharmony_ci	for (i = 0; i < NUM_RX_DMA; i++) {
124562306a36Sopenharmony_ci		pDB = au1000_GetFreeDB(aup);
124662306a36Sopenharmony_ci		if (!pDB)
124762306a36Sopenharmony_ci			goto err_out;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci		aup->rx_dma_ring[i]->buff_stat = lower_32_bits(pDB->dma_addr);
125062306a36Sopenharmony_ci		aup->rx_db_inuse[i] = pDB;
125162306a36Sopenharmony_ci	}
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	for (i = 0; i < NUM_TX_DMA; i++) {
125462306a36Sopenharmony_ci		pDB = au1000_GetFreeDB(aup);
125562306a36Sopenharmony_ci		if (!pDB)
125662306a36Sopenharmony_ci			goto err_out;
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci		aup->tx_dma_ring[i]->buff_stat = lower_32_bits(pDB->dma_addr);
125962306a36Sopenharmony_ci		aup->tx_dma_ring[i]->len = 0;
126062306a36Sopenharmony_ci		aup->tx_db_inuse[i] = pDB;
126162306a36Sopenharmony_ci	}
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	dev->base_addr = base->start;
126462306a36Sopenharmony_ci	dev->irq = irq;
126562306a36Sopenharmony_ci	dev->netdev_ops = &au1000_netdev_ops;
126662306a36Sopenharmony_ci	dev->ethtool_ops = &au1000_ethtool_ops;
126762306a36Sopenharmony_ci	dev->watchdog_timeo = ETH_TX_TIMEOUT;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	/*
127062306a36Sopenharmony_ci	 * The boot code uses the ethernet controller, so reset it to start
127162306a36Sopenharmony_ci	 * fresh.  au1000_init() expects that the device is in reset state.
127262306a36Sopenharmony_ci	 */
127362306a36Sopenharmony_ci	au1000_reset_mac(dev);
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	err = register_netdev(dev);
127662306a36Sopenharmony_ci	if (err) {
127762306a36Sopenharmony_ci		netdev_err(dev, "Cannot register net device, aborting.\n");
127862306a36Sopenharmony_ci		goto err_out;
127962306a36Sopenharmony_ci	}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	netdev_info(dev, "Au1xx0 Ethernet found at 0x%lx, irq %d\n",
128262306a36Sopenharmony_ci			(unsigned long)base->start, irq);
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	return 0;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_cierr_out:
128762306a36Sopenharmony_ci	if (aup->mii_bus)
128862306a36Sopenharmony_ci		mdiobus_unregister(aup->mii_bus);
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	/* here we should have a valid dev plus aup-> register addresses
129162306a36Sopenharmony_ci	 * so we can reset the mac properly.
129262306a36Sopenharmony_ci	 */
129362306a36Sopenharmony_ci	au1000_reset_mac(dev);
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	for (i = 0; i < NUM_RX_DMA; i++) {
129662306a36Sopenharmony_ci		if (aup->rx_db_inuse[i])
129762306a36Sopenharmony_ci			au1000_ReleaseDB(aup, aup->rx_db_inuse[i]);
129862306a36Sopenharmony_ci	}
129962306a36Sopenharmony_ci	for (i = 0; i < NUM_TX_DMA; i++) {
130062306a36Sopenharmony_ci		if (aup->tx_db_inuse[i])
130162306a36Sopenharmony_ci			au1000_ReleaseDB(aup, aup->tx_db_inuse[i]);
130262306a36Sopenharmony_ci	}
130362306a36Sopenharmony_cierr_mdiobus_reg:
130462306a36Sopenharmony_ci	mdiobus_free(aup->mii_bus);
130562306a36Sopenharmony_cierr_mdiobus_alloc:
130662306a36Sopenharmony_ci	iounmap(aup->macdma);
130762306a36Sopenharmony_cierr_remap3:
130862306a36Sopenharmony_ci	iounmap(aup->enable);
130962306a36Sopenharmony_cierr_remap2:
131062306a36Sopenharmony_ci	iounmap(aup->mac);
131162306a36Sopenharmony_cierr_remap1:
131262306a36Sopenharmony_ci	dma_free_coherent(&pdev->dev, MAX_BUF_SIZE * (NUM_TX_BUFFS + NUM_RX_BUFFS),
131362306a36Sopenharmony_ci			  aup->vaddr, aup->dma_addr);
131462306a36Sopenharmony_cierr_vaddr:
131562306a36Sopenharmony_ci	free_netdev(dev);
131662306a36Sopenharmony_cierr_alloc:
131762306a36Sopenharmony_ci	release_mem_region(macdma->start, resource_size(macdma));
131862306a36Sopenharmony_cierr_macdma:
131962306a36Sopenharmony_ci	release_mem_region(macen->start, resource_size(macen));
132062306a36Sopenharmony_cierr_request:
132162306a36Sopenharmony_ci	release_mem_region(base->start, resource_size(base));
132262306a36Sopenharmony_ciout:
132362306a36Sopenharmony_ci	return err;
132462306a36Sopenharmony_ci}
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_cistatic int au1000_remove(struct platform_device *pdev)
132762306a36Sopenharmony_ci{
132862306a36Sopenharmony_ci	struct net_device *dev = platform_get_drvdata(pdev);
132962306a36Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
133062306a36Sopenharmony_ci	int i;
133162306a36Sopenharmony_ci	struct resource *base, *macen;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	unregister_netdev(dev);
133462306a36Sopenharmony_ci	mdiobus_unregister(aup->mii_bus);
133562306a36Sopenharmony_ci	mdiobus_free(aup->mii_bus);
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	for (i = 0; i < NUM_RX_DMA; i++)
133862306a36Sopenharmony_ci		if (aup->rx_db_inuse[i])
133962306a36Sopenharmony_ci			au1000_ReleaseDB(aup, aup->rx_db_inuse[i]);
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	for (i = 0; i < NUM_TX_DMA; i++)
134262306a36Sopenharmony_ci		if (aup->tx_db_inuse[i])
134362306a36Sopenharmony_ci			au1000_ReleaseDB(aup, aup->tx_db_inuse[i]);
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	dma_free_coherent(&pdev->dev, MAX_BUF_SIZE * (NUM_TX_BUFFS + NUM_RX_BUFFS),
134662306a36Sopenharmony_ci			  aup->vaddr, aup->dma_addr);
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	iounmap(aup->macdma);
134962306a36Sopenharmony_ci	iounmap(aup->mac);
135062306a36Sopenharmony_ci	iounmap(aup->enable);
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	base = platform_get_resource(pdev, IORESOURCE_MEM, 2);
135362306a36Sopenharmony_ci	release_mem_region(base->start, resource_size(base));
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
135662306a36Sopenharmony_ci	release_mem_region(base->start, resource_size(base));
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	macen = platform_get_resource(pdev, IORESOURCE_MEM, 1);
135962306a36Sopenharmony_ci	release_mem_region(macen->start, resource_size(macen));
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	free_netdev(dev);
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	return 0;
136462306a36Sopenharmony_ci}
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_cistatic struct platform_driver au1000_eth_driver = {
136762306a36Sopenharmony_ci	.probe  = au1000_probe,
136862306a36Sopenharmony_ci	.remove = au1000_remove,
136962306a36Sopenharmony_ci	.driver = {
137062306a36Sopenharmony_ci		.name   = "au1000-eth",
137162306a36Sopenharmony_ci	},
137262306a36Sopenharmony_ci};
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_cimodule_platform_driver(au1000_eth_driver);
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ciMODULE_ALIAS("platform:au1000-eth");
1377