18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Alchemy Au1x00 ethernet driver
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright 2001-2003, 2006 MontaVista Software Inc.
78c2ecf20Sopenharmony_ci * Copyright 2002 TimeSys Corp.
88c2ecf20Sopenharmony_ci * Added ethtool/mii-tool support,
98c2ecf20Sopenharmony_ci * Copyright 2004 Matt Porter <mporter@kernel.crashing.org>
108c2ecf20Sopenharmony_ci * Update: 2004 Bjoern Riemer, riemer@fokus.fraunhofer.de
118c2ecf20Sopenharmony_ci * or riemer@riemer-nt.de: fixed the link beat detection with
128c2ecf20Sopenharmony_ci * ioctls (SIOCGMIIPHY)
138c2ecf20Sopenharmony_ci * Copyright 2006 Herbert Valerio Riedel <hvr@gnu.org>
148c2ecf20Sopenharmony_ci *  converted to use linux-2.6.x's PHY framework
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * Author: MontaVista Software, Inc.
178c2ecf20Sopenharmony_ci *		ppopov@mvista.com or source@mvista.com
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <linux/capability.h>
228c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
238c2ecf20Sopenharmony_ci#include <linux/module.h>
248c2ecf20Sopenharmony_ci#include <linux/kernel.h>
258c2ecf20Sopenharmony_ci#include <linux/string.h>
268c2ecf20Sopenharmony_ci#include <linux/timer.h>
278c2ecf20Sopenharmony_ci#include <linux/errno.h>
288c2ecf20Sopenharmony_ci#include <linux/in.h>
298c2ecf20Sopenharmony_ci#include <linux/ioport.h>
308c2ecf20Sopenharmony_ci#include <linux/bitops.h>
318c2ecf20Sopenharmony_ci#include <linux/slab.h>
328c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
338c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
348c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
358c2ecf20Sopenharmony_ci#include <linux/ethtool.h>
368c2ecf20Sopenharmony_ci#include <linux/mii.h>
378c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
388c2ecf20Sopenharmony_ci#include <linux/delay.h>
398c2ecf20Sopenharmony_ci#include <linux/crc32.h>
408c2ecf20Sopenharmony_ci#include <linux/phy.h>
418c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
428c2ecf20Sopenharmony_ci#include <linux/cpu.h>
438c2ecf20Sopenharmony_ci#include <linux/io.h>
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#include <asm/mipsregs.h>
468c2ecf20Sopenharmony_ci#include <asm/irq.h>
478c2ecf20Sopenharmony_ci#include <asm/processor.h>
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#include <au1000.h>
508c2ecf20Sopenharmony_ci#include <au1xxx_eth.h>
518c2ecf20Sopenharmony_ci#include <prom.h>
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#include "au1000_eth.h"
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#ifdef AU1000_ETH_DEBUG
568c2ecf20Sopenharmony_cistatic int au1000_debug = 5;
578c2ecf20Sopenharmony_ci#else
588c2ecf20Sopenharmony_cistatic int au1000_debug = 3;
598c2ecf20Sopenharmony_ci#endif
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#define AU1000_DEF_MSG_ENABLE	(NETIF_MSG_DRV	| \
628c2ecf20Sopenharmony_ci				NETIF_MSG_PROBE	| \
638c2ecf20Sopenharmony_ci				NETIF_MSG_LINK)
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#define DRV_NAME	"au1000_eth"
668c2ecf20Sopenharmony_ci#define DRV_AUTHOR	"Pete Popov <ppopov@embeddedalley.com>"
678c2ecf20Sopenharmony_ci#define DRV_DESC	"Au1xxx on-chip Ethernet driver"
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRV_AUTHOR);
708c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESC);
718c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci/* AU1000 MAC registers and bits */
748c2ecf20Sopenharmony_ci#define MAC_CONTROL		0x0
758c2ecf20Sopenharmony_ci#  define MAC_RX_ENABLE		(1 << 2)
768c2ecf20Sopenharmony_ci#  define MAC_TX_ENABLE		(1 << 3)
778c2ecf20Sopenharmony_ci#  define MAC_DEF_CHECK		(1 << 5)
788c2ecf20Sopenharmony_ci#  define MAC_SET_BL(X)		(((X) & 0x3) << 6)
798c2ecf20Sopenharmony_ci#  define MAC_AUTO_PAD		(1 << 8)
808c2ecf20Sopenharmony_ci#  define MAC_DISABLE_RETRY	(1 << 10)
818c2ecf20Sopenharmony_ci#  define MAC_DISABLE_BCAST	(1 << 11)
828c2ecf20Sopenharmony_ci#  define MAC_LATE_COL		(1 << 12)
838c2ecf20Sopenharmony_ci#  define MAC_HASH_MODE		(1 << 13)
848c2ecf20Sopenharmony_ci#  define MAC_HASH_ONLY		(1 << 15)
858c2ecf20Sopenharmony_ci#  define MAC_PASS_ALL		(1 << 16)
868c2ecf20Sopenharmony_ci#  define MAC_INVERSE_FILTER	(1 << 17)
878c2ecf20Sopenharmony_ci#  define MAC_PROMISCUOUS	(1 << 18)
888c2ecf20Sopenharmony_ci#  define MAC_PASS_ALL_MULTI	(1 << 19)
898c2ecf20Sopenharmony_ci#  define MAC_FULL_DUPLEX	(1 << 20)
908c2ecf20Sopenharmony_ci#  define MAC_NORMAL_MODE	0
918c2ecf20Sopenharmony_ci#  define MAC_INT_LOOPBACK	(1 << 21)
928c2ecf20Sopenharmony_ci#  define MAC_EXT_LOOPBACK	(1 << 22)
938c2ecf20Sopenharmony_ci#  define MAC_DISABLE_RX_OWN	(1 << 23)
948c2ecf20Sopenharmony_ci#  define MAC_BIG_ENDIAN	(1 << 30)
958c2ecf20Sopenharmony_ci#  define MAC_RX_ALL		(1 << 31)
968c2ecf20Sopenharmony_ci#define MAC_ADDRESS_HIGH	0x4
978c2ecf20Sopenharmony_ci#define MAC_ADDRESS_LOW		0x8
988c2ecf20Sopenharmony_ci#define MAC_MCAST_HIGH		0xC
998c2ecf20Sopenharmony_ci#define MAC_MCAST_LOW		0x10
1008c2ecf20Sopenharmony_ci#define MAC_MII_CNTRL		0x14
1018c2ecf20Sopenharmony_ci#  define MAC_MII_BUSY		(1 << 0)
1028c2ecf20Sopenharmony_ci#  define MAC_MII_READ		0
1038c2ecf20Sopenharmony_ci#  define MAC_MII_WRITE		(1 << 1)
1048c2ecf20Sopenharmony_ci#  define MAC_SET_MII_SELECT_REG(X) (((X) & 0x1f) << 6)
1058c2ecf20Sopenharmony_ci#  define MAC_SET_MII_SELECT_PHY(X) (((X) & 0x1f) << 11)
1068c2ecf20Sopenharmony_ci#define MAC_MII_DATA		0x18
1078c2ecf20Sopenharmony_ci#define MAC_FLOW_CNTRL		0x1C
1088c2ecf20Sopenharmony_ci#  define MAC_FLOW_CNTRL_BUSY	(1 << 0)
1098c2ecf20Sopenharmony_ci#  define MAC_FLOW_CNTRL_ENABLE (1 << 1)
1108c2ecf20Sopenharmony_ci#  define MAC_PASS_CONTROL	(1 << 2)
1118c2ecf20Sopenharmony_ci#  define MAC_SET_PAUSE(X)	(((X) & 0xffff) << 16)
1128c2ecf20Sopenharmony_ci#define MAC_VLAN1_TAG		0x20
1138c2ecf20Sopenharmony_ci#define MAC_VLAN2_TAG		0x24
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/* Ethernet Controller Enable */
1168c2ecf20Sopenharmony_ci#  define MAC_EN_CLOCK_ENABLE	(1 << 0)
1178c2ecf20Sopenharmony_ci#  define MAC_EN_RESET0		(1 << 1)
1188c2ecf20Sopenharmony_ci#  define MAC_EN_TOSS		(0 << 2)
1198c2ecf20Sopenharmony_ci#  define MAC_EN_CACHEABLE	(1 << 3)
1208c2ecf20Sopenharmony_ci#  define MAC_EN_RESET1		(1 << 4)
1218c2ecf20Sopenharmony_ci#  define MAC_EN_RESET2		(1 << 5)
1228c2ecf20Sopenharmony_ci#  define MAC_DMA_RESET		(1 << 6)
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci/* Ethernet Controller DMA Channels */
1258c2ecf20Sopenharmony_ci/* offsets from MAC_TX_RING_ADDR address */
1268c2ecf20Sopenharmony_ci#define MAC_TX_BUFF0_STATUS	0x0
1278c2ecf20Sopenharmony_ci#  define TX_FRAME_ABORTED	(1 << 0)
1288c2ecf20Sopenharmony_ci#  define TX_JAB_TIMEOUT	(1 << 1)
1298c2ecf20Sopenharmony_ci#  define TX_NO_CARRIER		(1 << 2)
1308c2ecf20Sopenharmony_ci#  define TX_LOSS_CARRIER	(1 << 3)
1318c2ecf20Sopenharmony_ci#  define TX_EXC_DEF		(1 << 4)
1328c2ecf20Sopenharmony_ci#  define TX_LATE_COLL_ABORT	(1 << 5)
1338c2ecf20Sopenharmony_ci#  define TX_EXC_COLL		(1 << 6)
1348c2ecf20Sopenharmony_ci#  define TX_UNDERRUN		(1 << 7)
1358c2ecf20Sopenharmony_ci#  define TX_DEFERRED		(1 << 8)
1368c2ecf20Sopenharmony_ci#  define TX_LATE_COLL		(1 << 9)
1378c2ecf20Sopenharmony_ci#  define TX_COLL_CNT_MASK	(0xF << 10)
1388c2ecf20Sopenharmony_ci#  define TX_PKT_RETRY		(1 << 31)
1398c2ecf20Sopenharmony_ci#define MAC_TX_BUFF0_ADDR	0x4
1408c2ecf20Sopenharmony_ci#  define TX_DMA_ENABLE		(1 << 0)
1418c2ecf20Sopenharmony_ci#  define TX_T_DONE		(1 << 1)
1428c2ecf20Sopenharmony_ci#  define TX_GET_DMA_BUFFER(X)	(((X) >> 2) & 0x3)
1438c2ecf20Sopenharmony_ci#define MAC_TX_BUFF0_LEN	0x8
1448c2ecf20Sopenharmony_ci#define MAC_TX_BUFF1_STATUS	0x10
1458c2ecf20Sopenharmony_ci#define MAC_TX_BUFF1_ADDR	0x14
1468c2ecf20Sopenharmony_ci#define MAC_TX_BUFF1_LEN	0x18
1478c2ecf20Sopenharmony_ci#define MAC_TX_BUFF2_STATUS	0x20
1488c2ecf20Sopenharmony_ci#define MAC_TX_BUFF2_ADDR	0x24
1498c2ecf20Sopenharmony_ci#define MAC_TX_BUFF2_LEN	0x28
1508c2ecf20Sopenharmony_ci#define MAC_TX_BUFF3_STATUS	0x30
1518c2ecf20Sopenharmony_ci#define MAC_TX_BUFF3_ADDR	0x34
1528c2ecf20Sopenharmony_ci#define MAC_TX_BUFF3_LEN	0x38
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci/* offsets from MAC_RX_RING_ADDR */
1558c2ecf20Sopenharmony_ci#define MAC_RX_BUFF0_STATUS	0x0
1568c2ecf20Sopenharmony_ci#  define RX_FRAME_LEN_MASK	0x3fff
1578c2ecf20Sopenharmony_ci#  define RX_WDOG_TIMER		(1 << 14)
1588c2ecf20Sopenharmony_ci#  define RX_RUNT		(1 << 15)
1598c2ecf20Sopenharmony_ci#  define RX_OVERLEN		(1 << 16)
1608c2ecf20Sopenharmony_ci#  define RX_COLL		(1 << 17)
1618c2ecf20Sopenharmony_ci#  define RX_ETHER		(1 << 18)
1628c2ecf20Sopenharmony_ci#  define RX_MII_ERROR		(1 << 19)
1638c2ecf20Sopenharmony_ci#  define RX_DRIBBLING		(1 << 20)
1648c2ecf20Sopenharmony_ci#  define RX_CRC_ERROR		(1 << 21)
1658c2ecf20Sopenharmony_ci#  define RX_VLAN1		(1 << 22)
1668c2ecf20Sopenharmony_ci#  define RX_VLAN2		(1 << 23)
1678c2ecf20Sopenharmony_ci#  define RX_LEN_ERROR		(1 << 24)
1688c2ecf20Sopenharmony_ci#  define RX_CNTRL_FRAME	(1 << 25)
1698c2ecf20Sopenharmony_ci#  define RX_U_CNTRL_FRAME	(1 << 26)
1708c2ecf20Sopenharmony_ci#  define RX_MCAST_FRAME	(1 << 27)
1718c2ecf20Sopenharmony_ci#  define RX_BCAST_FRAME	(1 << 28)
1728c2ecf20Sopenharmony_ci#  define RX_FILTER_FAIL	(1 << 29)
1738c2ecf20Sopenharmony_ci#  define RX_PACKET_FILTER	(1 << 30)
1748c2ecf20Sopenharmony_ci#  define RX_MISSED_FRAME	(1 << 31)
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci#  define RX_ERROR (RX_WDOG_TIMER | RX_RUNT | RX_OVERLEN |  \
1778c2ecf20Sopenharmony_ci		    RX_COLL | RX_MII_ERROR | RX_CRC_ERROR | \
1788c2ecf20Sopenharmony_ci		    RX_LEN_ERROR | RX_U_CNTRL_FRAME | RX_MISSED_FRAME)
1798c2ecf20Sopenharmony_ci#define MAC_RX_BUFF0_ADDR	0x4
1808c2ecf20Sopenharmony_ci#  define RX_DMA_ENABLE		(1 << 0)
1818c2ecf20Sopenharmony_ci#  define RX_T_DONE		(1 << 1)
1828c2ecf20Sopenharmony_ci#  define RX_GET_DMA_BUFFER(X)	(((X) >> 2) & 0x3)
1838c2ecf20Sopenharmony_ci#  define RX_SET_BUFF_ADDR(X)	((X) & 0xffffffc0)
1848c2ecf20Sopenharmony_ci#define MAC_RX_BUFF1_STATUS	0x10
1858c2ecf20Sopenharmony_ci#define MAC_RX_BUFF1_ADDR	0x14
1868c2ecf20Sopenharmony_ci#define MAC_RX_BUFF2_STATUS	0x20
1878c2ecf20Sopenharmony_ci#define MAC_RX_BUFF2_ADDR	0x24
1888c2ecf20Sopenharmony_ci#define MAC_RX_BUFF3_STATUS	0x30
1898c2ecf20Sopenharmony_ci#define MAC_RX_BUFF3_ADDR	0x34
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci/*
1928c2ecf20Sopenharmony_ci * Theory of operation
1938c2ecf20Sopenharmony_ci *
1948c2ecf20Sopenharmony_ci * The Au1000 MACs use a simple rx and tx descriptor ring scheme.
1958c2ecf20Sopenharmony_ci * There are four receive and four transmit descriptors.  These
1968c2ecf20Sopenharmony_ci * descriptors are not in memory; rather, they are just a set of
1978c2ecf20Sopenharmony_ci * hardware registers.
1988c2ecf20Sopenharmony_ci *
1998c2ecf20Sopenharmony_ci * Since the Au1000 has a coherent data cache, the receive and
2008c2ecf20Sopenharmony_ci * transmit buffers are allocated from the KSEG0 segment. The
2018c2ecf20Sopenharmony_ci * hardware registers, however, are still mapped at KSEG1 to
2028c2ecf20Sopenharmony_ci * make sure there's no out-of-order writes, and that all writes
2038c2ecf20Sopenharmony_ci * complete immediately.
2048c2ecf20Sopenharmony_ci */
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci/*
2078c2ecf20Sopenharmony_ci * board-specific configurations
2088c2ecf20Sopenharmony_ci *
2098c2ecf20Sopenharmony_ci * PHY detection algorithm
2108c2ecf20Sopenharmony_ci *
2118c2ecf20Sopenharmony_ci * If phy_static_config is undefined, the PHY setup is
2128c2ecf20Sopenharmony_ci * autodetected:
2138c2ecf20Sopenharmony_ci *
2148c2ecf20Sopenharmony_ci * mii_probe() first searches the current MAC's MII bus for a PHY,
2158c2ecf20Sopenharmony_ci * selecting the first (or last, if phy_search_highest_addr is
2168c2ecf20Sopenharmony_ci * defined) PHY address not already claimed by another netdev.
2178c2ecf20Sopenharmony_ci *
2188c2ecf20Sopenharmony_ci * If nothing was found that way when searching for the 2nd ethernet
2198c2ecf20Sopenharmony_ci * controller's PHY and phy1_search_mac0 is defined, then
2208c2ecf20Sopenharmony_ci * the first MII bus is searched as well for an unclaimed PHY; this is
2218c2ecf20Sopenharmony_ci * needed in case of a dual-PHY accessible only through the MAC0's MII
2228c2ecf20Sopenharmony_ci * bus.
2238c2ecf20Sopenharmony_ci *
2248c2ecf20Sopenharmony_ci * Finally, if no PHY is found, then the corresponding ethernet
2258c2ecf20Sopenharmony_ci * controller is not registered to the network subsystem.
2268c2ecf20Sopenharmony_ci */
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci/* autodetection defaults: phy1_search_mac0 */
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci/* static PHY setup
2318c2ecf20Sopenharmony_ci *
2328c2ecf20Sopenharmony_ci * most boards PHY setup should be detectable properly with the
2338c2ecf20Sopenharmony_ci * autodetection algorithm in mii_probe(), but in some cases (e.g. if
2348c2ecf20Sopenharmony_ci * you have a switch attached, or want to use the PHY's interrupt
2358c2ecf20Sopenharmony_ci * notification capabilities) you can provide a static PHY
2368c2ecf20Sopenharmony_ci * configuration here
2378c2ecf20Sopenharmony_ci *
2388c2ecf20Sopenharmony_ci * IRQs may only be set, if a PHY address was configured
2398c2ecf20Sopenharmony_ci * If a PHY address is given, also a bus id is required to be set
2408c2ecf20Sopenharmony_ci *
2418c2ecf20Sopenharmony_ci * ps: make sure the used irqs are configured properly in the board
2428c2ecf20Sopenharmony_ci * specific irq-map
2438c2ecf20Sopenharmony_ci */
2448c2ecf20Sopenharmony_cistatic void au1000_enable_mac(struct net_device *dev, int force_reset)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	unsigned long flags;
2478c2ecf20Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	spin_lock_irqsave(&aup->lock, flags);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (force_reset || (!aup->mac_enabled)) {
2528c2ecf20Sopenharmony_ci		writel(MAC_EN_CLOCK_ENABLE, aup->enable);
2538c2ecf20Sopenharmony_ci		wmb(); /* drain writebuffer */
2548c2ecf20Sopenharmony_ci		mdelay(2);
2558c2ecf20Sopenharmony_ci		writel((MAC_EN_RESET0 | MAC_EN_RESET1 | MAC_EN_RESET2
2568c2ecf20Sopenharmony_ci				| MAC_EN_CLOCK_ENABLE), aup->enable);
2578c2ecf20Sopenharmony_ci		wmb(); /* drain writebuffer */
2588c2ecf20Sopenharmony_ci		mdelay(2);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		aup->mac_enabled = 1;
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&aup->lock, flags);
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci/*
2678c2ecf20Sopenharmony_ci * MII operations
2688c2ecf20Sopenharmony_ci */
2698c2ecf20Sopenharmony_cistatic int au1000_mdio_read(struct net_device *dev, int phy_addr, int reg)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
2728c2ecf20Sopenharmony_ci	u32 *const mii_control_reg = &aup->mac->mii_control;
2738c2ecf20Sopenharmony_ci	u32 *const mii_data_reg = &aup->mac->mii_data;
2748c2ecf20Sopenharmony_ci	u32 timedout = 20;
2758c2ecf20Sopenharmony_ci	u32 mii_control;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	while (readl(mii_control_reg) & MAC_MII_BUSY) {
2788c2ecf20Sopenharmony_ci		mdelay(1);
2798c2ecf20Sopenharmony_ci		if (--timedout == 0) {
2808c2ecf20Sopenharmony_ci			netdev_err(dev, "read_MII busy timeout!!\n");
2818c2ecf20Sopenharmony_ci			return -1;
2828c2ecf20Sopenharmony_ci		}
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	mii_control = MAC_SET_MII_SELECT_REG(reg) |
2868c2ecf20Sopenharmony_ci		MAC_SET_MII_SELECT_PHY(phy_addr) | MAC_MII_READ;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	writel(mii_control, mii_control_reg);
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	timedout = 20;
2918c2ecf20Sopenharmony_ci	while (readl(mii_control_reg) & MAC_MII_BUSY) {
2928c2ecf20Sopenharmony_ci		mdelay(1);
2938c2ecf20Sopenharmony_ci		if (--timedout == 0) {
2948c2ecf20Sopenharmony_ci			netdev_err(dev, "mdio_read busy timeout!!\n");
2958c2ecf20Sopenharmony_ci			return -1;
2968c2ecf20Sopenharmony_ci		}
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci	return readl(mii_data_reg);
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic void au1000_mdio_write(struct net_device *dev, int phy_addr,
3028c2ecf20Sopenharmony_ci			      int reg, u16 value)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
3058c2ecf20Sopenharmony_ci	u32 *const mii_control_reg = &aup->mac->mii_control;
3068c2ecf20Sopenharmony_ci	u32 *const mii_data_reg = &aup->mac->mii_data;
3078c2ecf20Sopenharmony_ci	u32 timedout = 20;
3088c2ecf20Sopenharmony_ci	u32 mii_control;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	while (readl(mii_control_reg) & MAC_MII_BUSY) {
3118c2ecf20Sopenharmony_ci		mdelay(1);
3128c2ecf20Sopenharmony_ci		if (--timedout == 0) {
3138c2ecf20Sopenharmony_ci			netdev_err(dev, "mdio_write busy timeout!!\n");
3148c2ecf20Sopenharmony_ci			return;
3158c2ecf20Sopenharmony_ci		}
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	mii_control = MAC_SET_MII_SELECT_REG(reg) |
3198c2ecf20Sopenharmony_ci		MAC_SET_MII_SELECT_PHY(phy_addr) | MAC_MII_WRITE;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	writel(value, mii_data_reg);
3228c2ecf20Sopenharmony_ci	writel(mii_control, mii_control_reg);
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic int au1000_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	struct net_device *const dev = bus->priv;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	/* make sure the MAC associated with this
3308c2ecf20Sopenharmony_ci	 * mii_bus is enabled
3318c2ecf20Sopenharmony_ci	 */
3328c2ecf20Sopenharmony_ci	au1000_enable_mac(dev, 0);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	return au1000_mdio_read(dev, phy_addr, regnum);
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic int au1000_mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum,
3388c2ecf20Sopenharmony_ci				u16 value)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	struct net_device *const dev = bus->priv;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	/* make sure the MAC associated with this
3438c2ecf20Sopenharmony_ci	 * mii_bus is enabled
3448c2ecf20Sopenharmony_ci	 */
3458c2ecf20Sopenharmony_ci	au1000_enable_mac(dev, 0);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	au1000_mdio_write(dev, phy_addr, regnum, value);
3488c2ecf20Sopenharmony_ci	return 0;
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic int au1000_mdiobus_reset(struct mii_bus *bus)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	struct net_device *const dev = bus->priv;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	/* make sure the MAC associated with this
3568c2ecf20Sopenharmony_ci	 * mii_bus is enabled
3578c2ecf20Sopenharmony_ci	 */
3588c2ecf20Sopenharmony_ci	au1000_enable_mac(dev, 0);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	return 0;
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic void au1000_hard_stop(struct net_device *dev)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
3668c2ecf20Sopenharmony_ci	u32 reg;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	netif_dbg(aup, drv, dev, "hard stop\n");
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	reg = readl(&aup->mac->control);
3718c2ecf20Sopenharmony_ci	reg &= ~(MAC_RX_ENABLE | MAC_TX_ENABLE);
3728c2ecf20Sopenharmony_ci	writel(reg, &aup->mac->control);
3738c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
3748c2ecf20Sopenharmony_ci	mdelay(10);
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic void au1000_enable_rx_tx(struct net_device *dev)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
3808c2ecf20Sopenharmony_ci	u32 reg;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	netif_dbg(aup, hw, dev, "enable_rx_tx\n");
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	reg = readl(&aup->mac->control);
3858c2ecf20Sopenharmony_ci	reg |= (MAC_RX_ENABLE | MAC_TX_ENABLE);
3868c2ecf20Sopenharmony_ci	writel(reg, &aup->mac->control);
3878c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
3888c2ecf20Sopenharmony_ci	mdelay(10);
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic void
3928c2ecf20Sopenharmony_ciau1000_adjust_link(struct net_device *dev)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
3958c2ecf20Sopenharmony_ci	struct phy_device *phydev = dev->phydev;
3968c2ecf20Sopenharmony_ci	unsigned long flags;
3978c2ecf20Sopenharmony_ci	u32 reg;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	int status_change = 0;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	BUG_ON(!phydev);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	spin_lock_irqsave(&aup->lock, flags);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	if (phydev->link && (aup->old_speed != phydev->speed)) {
4068c2ecf20Sopenharmony_ci		/* speed changed */
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci		switch (phydev->speed) {
4098c2ecf20Sopenharmony_ci		case SPEED_10:
4108c2ecf20Sopenharmony_ci		case SPEED_100:
4118c2ecf20Sopenharmony_ci			break;
4128c2ecf20Sopenharmony_ci		default:
4138c2ecf20Sopenharmony_ci			netdev_warn(dev, "Speed (%d) is not 10/100 ???\n",
4148c2ecf20Sopenharmony_ci							phydev->speed);
4158c2ecf20Sopenharmony_ci			break;
4168c2ecf20Sopenharmony_ci		}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci		aup->old_speed = phydev->speed;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci		status_change = 1;
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	if (phydev->link && (aup->old_duplex != phydev->duplex)) {
4248c2ecf20Sopenharmony_ci		/* duplex mode changed */
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci		/* switching duplex mode requires to disable rx and tx! */
4278c2ecf20Sopenharmony_ci		au1000_hard_stop(dev);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci		reg = readl(&aup->mac->control);
4308c2ecf20Sopenharmony_ci		if (DUPLEX_FULL == phydev->duplex) {
4318c2ecf20Sopenharmony_ci			reg |= MAC_FULL_DUPLEX;
4328c2ecf20Sopenharmony_ci			reg &= ~MAC_DISABLE_RX_OWN;
4338c2ecf20Sopenharmony_ci		} else {
4348c2ecf20Sopenharmony_ci			reg &= ~MAC_FULL_DUPLEX;
4358c2ecf20Sopenharmony_ci			reg |= MAC_DISABLE_RX_OWN;
4368c2ecf20Sopenharmony_ci		}
4378c2ecf20Sopenharmony_ci		writel(reg, &aup->mac->control);
4388c2ecf20Sopenharmony_ci		wmb(); /* drain writebuffer */
4398c2ecf20Sopenharmony_ci		mdelay(1);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci		au1000_enable_rx_tx(dev);
4428c2ecf20Sopenharmony_ci		aup->old_duplex = phydev->duplex;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci		status_change = 1;
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	if (phydev->link != aup->old_link) {
4488c2ecf20Sopenharmony_ci		/* link state changed */
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci		if (!phydev->link) {
4518c2ecf20Sopenharmony_ci			/* link went down */
4528c2ecf20Sopenharmony_ci			aup->old_speed = 0;
4538c2ecf20Sopenharmony_ci			aup->old_duplex = -1;
4548c2ecf20Sopenharmony_ci		}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci		aup->old_link = phydev->link;
4578c2ecf20Sopenharmony_ci		status_change = 1;
4588c2ecf20Sopenharmony_ci	}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&aup->lock, flags);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	if (status_change) {
4638c2ecf20Sopenharmony_ci		if (phydev->link)
4648c2ecf20Sopenharmony_ci			netdev_info(dev, "link up (%d/%s)\n",
4658c2ecf20Sopenharmony_ci			       phydev->speed,
4668c2ecf20Sopenharmony_ci			       DUPLEX_FULL == phydev->duplex ? "Full" : "Half");
4678c2ecf20Sopenharmony_ci		else
4688c2ecf20Sopenharmony_ci			netdev_info(dev, "link down\n");
4698c2ecf20Sopenharmony_ci	}
4708c2ecf20Sopenharmony_ci}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_cistatic int au1000_mii_probe(struct net_device *dev)
4738c2ecf20Sopenharmony_ci{
4748c2ecf20Sopenharmony_ci	struct au1000_private *const aup = netdev_priv(dev);
4758c2ecf20Sopenharmony_ci	struct phy_device *phydev = NULL;
4768c2ecf20Sopenharmony_ci	int phy_addr;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	if (aup->phy_static_config) {
4798c2ecf20Sopenharmony_ci		BUG_ON(aup->mac_id < 0 || aup->mac_id > 1);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci		if (aup->phy_addr)
4828c2ecf20Sopenharmony_ci			phydev = mdiobus_get_phy(aup->mii_bus, aup->phy_addr);
4838c2ecf20Sopenharmony_ci		else
4848c2ecf20Sopenharmony_ci			netdev_info(dev, "using PHY-less setup\n");
4858c2ecf20Sopenharmony_ci		return 0;
4868c2ecf20Sopenharmony_ci	}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	/* find the first (lowest address) PHY
4898c2ecf20Sopenharmony_ci	 * on the current MAC's MII bus
4908c2ecf20Sopenharmony_ci	 */
4918c2ecf20Sopenharmony_ci	for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++)
4928c2ecf20Sopenharmony_ci		if (mdiobus_get_phy(aup->mii_bus, phy_addr)) {
4938c2ecf20Sopenharmony_ci			phydev = mdiobus_get_phy(aup->mii_bus, phy_addr);
4948c2ecf20Sopenharmony_ci			if (!aup->phy_search_highest_addr)
4958c2ecf20Sopenharmony_ci				/* break out with first one found */
4968c2ecf20Sopenharmony_ci				break;
4978c2ecf20Sopenharmony_ci		}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	if (aup->phy1_search_mac0) {
5008c2ecf20Sopenharmony_ci		/* try harder to find a PHY */
5018c2ecf20Sopenharmony_ci		if (!phydev && (aup->mac_id == 1)) {
5028c2ecf20Sopenharmony_ci			/* no PHY found, maybe we have a dual PHY? */
5038c2ecf20Sopenharmony_ci			dev_info(&dev->dev, ": no PHY found on MAC1, "
5048c2ecf20Sopenharmony_ci				"let's see if it's attached to MAC0...\n");
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci			/* find the first (lowest address) non-attached
5078c2ecf20Sopenharmony_ci			 * PHY on the MAC0 MII bus
5088c2ecf20Sopenharmony_ci			 */
5098c2ecf20Sopenharmony_ci			for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
5108c2ecf20Sopenharmony_ci				struct phy_device *const tmp_phydev =
5118c2ecf20Sopenharmony_ci					mdiobus_get_phy(aup->mii_bus,
5128c2ecf20Sopenharmony_ci							phy_addr);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci				if (aup->mac_id == 1)
5158c2ecf20Sopenharmony_ci					break;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci				/* no PHY here... */
5188c2ecf20Sopenharmony_ci				if (!tmp_phydev)
5198c2ecf20Sopenharmony_ci					continue;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci				/* already claimed by MAC0 */
5228c2ecf20Sopenharmony_ci				if (tmp_phydev->attached_dev)
5238c2ecf20Sopenharmony_ci					continue;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci				phydev = tmp_phydev;
5268c2ecf20Sopenharmony_ci				break; /* found it */
5278c2ecf20Sopenharmony_ci			}
5288c2ecf20Sopenharmony_ci		}
5298c2ecf20Sopenharmony_ci	}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	if (!phydev) {
5328c2ecf20Sopenharmony_ci		netdev_err(dev, "no PHY found\n");
5338c2ecf20Sopenharmony_ci		return -1;
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	/* now we are supposed to have a proper phydev, to attach to... */
5378c2ecf20Sopenharmony_ci	BUG_ON(phydev->attached_dev);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	phydev = phy_connect(dev, phydev_name(phydev),
5408c2ecf20Sopenharmony_ci			     &au1000_adjust_link, PHY_INTERFACE_MODE_MII);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	if (IS_ERR(phydev)) {
5438c2ecf20Sopenharmony_ci		netdev_err(dev, "Could not attach to PHY\n");
5448c2ecf20Sopenharmony_ci		return PTR_ERR(phydev);
5458c2ecf20Sopenharmony_ci	}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	phy_set_max_speed(phydev, SPEED_100);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	aup->old_link = 0;
5508c2ecf20Sopenharmony_ci	aup->old_speed = 0;
5518c2ecf20Sopenharmony_ci	aup->old_duplex = -1;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	phy_attached_info(phydev);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	return 0;
5568c2ecf20Sopenharmony_ci}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci/*
5598c2ecf20Sopenharmony_ci * Buffer allocation/deallocation routines. The buffer descriptor returned
5608c2ecf20Sopenharmony_ci * has the virtual and dma address of a buffer suitable for
5618c2ecf20Sopenharmony_ci * both, receive and transmit operations.
5628c2ecf20Sopenharmony_ci */
5638c2ecf20Sopenharmony_cistatic struct db_dest *au1000_GetFreeDB(struct au1000_private *aup)
5648c2ecf20Sopenharmony_ci{
5658c2ecf20Sopenharmony_ci	struct db_dest *pDB;
5668c2ecf20Sopenharmony_ci	pDB = aup->pDBfree;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (pDB)
5698c2ecf20Sopenharmony_ci		aup->pDBfree = pDB->pnext;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	return pDB;
5728c2ecf20Sopenharmony_ci}
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_civoid au1000_ReleaseDB(struct au1000_private *aup, struct db_dest *pDB)
5758c2ecf20Sopenharmony_ci{
5768c2ecf20Sopenharmony_ci	struct db_dest *pDBfree = aup->pDBfree;
5778c2ecf20Sopenharmony_ci	if (pDBfree)
5788c2ecf20Sopenharmony_ci		pDBfree->pnext = pDB;
5798c2ecf20Sopenharmony_ci	aup->pDBfree = pDB;
5808c2ecf20Sopenharmony_ci}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_cistatic void au1000_reset_mac_unlocked(struct net_device *dev)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	struct au1000_private *const aup = netdev_priv(dev);
5858c2ecf20Sopenharmony_ci	int i;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	au1000_hard_stop(dev);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	writel(MAC_EN_CLOCK_ENABLE, aup->enable);
5908c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
5918c2ecf20Sopenharmony_ci	mdelay(2);
5928c2ecf20Sopenharmony_ci	writel(0, aup->enable);
5938c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
5948c2ecf20Sopenharmony_ci	mdelay(2);
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	aup->tx_full = 0;
5978c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_RX_DMA; i++) {
5988c2ecf20Sopenharmony_ci		/* reset control bits */
5998c2ecf20Sopenharmony_ci		aup->rx_dma_ring[i]->buff_stat &= ~0xf;
6008c2ecf20Sopenharmony_ci	}
6018c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_TX_DMA; i++) {
6028c2ecf20Sopenharmony_ci		/* reset control bits */
6038c2ecf20Sopenharmony_ci		aup->tx_dma_ring[i]->buff_stat &= ~0xf;
6048c2ecf20Sopenharmony_ci	}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	aup->mac_enabled = 0;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_cistatic void au1000_reset_mac(struct net_device *dev)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	struct au1000_private *const aup = netdev_priv(dev);
6138c2ecf20Sopenharmony_ci	unsigned long flags;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	netif_dbg(aup, hw, dev, "reset mac, aup %x\n",
6168c2ecf20Sopenharmony_ci					(unsigned)aup);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	spin_lock_irqsave(&aup->lock, flags);
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	au1000_reset_mac_unlocked(dev);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&aup->lock, flags);
6238c2ecf20Sopenharmony_ci}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci/*
6268c2ecf20Sopenharmony_ci * Setup the receive and transmit "rings".  These pointers are the addresses
6278c2ecf20Sopenharmony_ci * of the rx and tx MAC DMA registers so they are fixed by the hardware --
6288c2ecf20Sopenharmony_ci * these are not descriptors sitting in memory.
6298c2ecf20Sopenharmony_ci */
6308c2ecf20Sopenharmony_cistatic void
6318c2ecf20Sopenharmony_ciau1000_setup_hw_rings(struct au1000_private *aup, void __iomem *tx_base)
6328c2ecf20Sopenharmony_ci{
6338c2ecf20Sopenharmony_ci	int i;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_RX_DMA; i++) {
6368c2ecf20Sopenharmony_ci		aup->rx_dma_ring[i] = (struct rx_dma *)
6378c2ecf20Sopenharmony_ci			(tx_base + 0x100 + sizeof(struct rx_dma) * i);
6388c2ecf20Sopenharmony_ci	}
6398c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_TX_DMA; i++) {
6408c2ecf20Sopenharmony_ci		aup->tx_dma_ring[i] = (struct tx_dma *)
6418c2ecf20Sopenharmony_ci			(tx_base + sizeof(struct tx_dma) * i);
6428c2ecf20Sopenharmony_ci	}
6438c2ecf20Sopenharmony_ci}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci/*
6468c2ecf20Sopenharmony_ci * ethtool operations
6478c2ecf20Sopenharmony_ci */
6488c2ecf20Sopenharmony_cistatic void
6498c2ecf20Sopenharmony_ciau1000_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
6548c2ecf20Sopenharmony_ci	snprintf(info->bus_info, sizeof(info->bus_info), "%s %d", DRV_NAME,
6558c2ecf20Sopenharmony_ci		 aup->mac_id);
6568c2ecf20Sopenharmony_ci}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_cistatic void au1000_set_msglevel(struct net_device *dev, u32 value)
6598c2ecf20Sopenharmony_ci{
6608c2ecf20Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
6618c2ecf20Sopenharmony_ci	aup->msg_enable = value;
6628c2ecf20Sopenharmony_ci}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_cistatic u32 au1000_get_msglevel(struct net_device *dev)
6658c2ecf20Sopenharmony_ci{
6668c2ecf20Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
6678c2ecf20Sopenharmony_ci	return aup->msg_enable;
6688c2ecf20Sopenharmony_ci}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_cistatic const struct ethtool_ops au1000_ethtool_ops = {
6718c2ecf20Sopenharmony_ci	.get_drvinfo = au1000_get_drvinfo,
6728c2ecf20Sopenharmony_ci	.get_link = ethtool_op_get_link,
6738c2ecf20Sopenharmony_ci	.get_msglevel = au1000_get_msglevel,
6748c2ecf20Sopenharmony_ci	.set_msglevel = au1000_set_msglevel,
6758c2ecf20Sopenharmony_ci	.get_link_ksettings = phy_ethtool_get_link_ksettings,
6768c2ecf20Sopenharmony_ci	.set_link_ksettings = phy_ethtool_set_link_ksettings,
6778c2ecf20Sopenharmony_ci};
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci/*
6808c2ecf20Sopenharmony_ci * Initialize the interface.
6818c2ecf20Sopenharmony_ci *
6828c2ecf20Sopenharmony_ci * When the device powers up, the clocks are disabled and the
6838c2ecf20Sopenharmony_ci * mac is in reset state.  When the interface is closed, we
6848c2ecf20Sopenharmony_ci * do the same -- reset the device and disable the clocks to
6858c2ecf20Sopenharmony_ci * conserve power. Thus, whenever au1000_init() is called,
6868c2ecf20Sopenharmony_ci * the device should already be in reset state.
6878c2ecf20Sopenharmony_ci */
6888c2ecf20Sopenharmony_cistatic int au1000_init(struct net_device *dev)
6898c2ecf20Sopenharmony_ci{
6908c2ecf20Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
6918c2ecf20Sopenharmony_ci	unsigned long flags;
6928c2ecf20Sopenharmony_ci	int i;
6938c2ecf20Sopenharmony_ci	u32 control;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	netif_dbg(aup, hw, dev, "au1000_init\n");
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	/* bring the device out of reset */
6988c2ecf20Sopenharmony_ci	au1000_enable_mac(dev, 1);
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	spin_lock_irqsave(&aup->lock, flags);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	writel(0, &aup->mac->control);
7038c2ecf20Sopenharmony_ci	aup->tx_head = (aup->tx_dma_ring[0]->buff_stat & 0xC) >> 2;
7048c2ecf20Sopenharmony_ci	aup->tx_tail = aup->tx_head;
7058c2ecf20Sopenharmony_ci	aup->rx_head = (aup->rx_dma_ring[0]->buff_stat & 0xC) >> 2;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	writel(dev->dev_addr[5]<<8 | dev->dev_addr[4],
7088c2ecf20Sopenharmony_ci					&aup->mac->mac_addr_high);
7098c2ecf20Sopenharmony_ci	writel(dev->dev_addr[3]<<24 | dev->dev_addr[2]<<16 |
7108c2ecf20Sopenharmony_ci		dev->dev_addr[1]<<8 | dev->dev_addr[0],
7118c2ecf20Sopenharmony_ci					&aup->mac->mac_addr_low);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_RX_DMA; i++)
7158c2ecf20Sopenharmony_ci		aup->rx_dma_ring[i]->buff_stat |= RX_DMA_ENABLE;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	control = MAC_RX_ENABLE | MAC_TX_ENABLE;
7208c2ecf20Sopenharmony_ci#ifndef CONFIG_CPU_LITTLE_ENDIAN
7218c2ecf20Sopenharmony_ci	control |= MAC_BIG_ENDIAN;
7228c2ecf20Sopenharmony_ci#endif
7238c2ecf20Sopenharmony_ci	if (dev->phydev) {
7248c2ecf20Sopenharmony_ci		if (dev->phydev->link && (DUPLEX_FULL == dev->phydev->duplex))
7258c2ecf20Sopenharmony_ci			control |= MAC_FULL_DUPLEX;
7268c2ecf20Sopenharmony_ci		else
7278c2ecf20Sopenharmony_ci			control |= MAC_DISABLE_RX_OWN;
7288c2ecf20Sopenharmony_ci	} else { /* PHY-less op, assume full-duplex */
7298c2ecf20Sopenharmony_ci		control |= MAC_FULL_DUPLEX;
7308c2ecf20Sopenharmony_ci	}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	writel(control, &aup->mac->control);
7338c2ecf20Sopenharmony_ci	writel(0x8100, &aup->mac->vlan1_tag); /* activate vlan support */
7348c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&aup->lock, flags);
7378c2ecf20Sopenharmony_ci	return 0;
7388c2ecf20Sopenharmony_ci}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_cistatic inline void au1000_update_rx_stats(struct net_device *dev, u32 status)
7418c2ecf20Sopenharmony_ci{
7428c2ecf20Sopenharmony_ci	struct net_device_stats *ps = &dev->stats;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	ps->rx_packets++;
7458c2ecf20Sopenharmony_ci	if (status & RX_MCAST_FRAME)
7468c2ecf20Sopenharmony_ci		ps->multicast++;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	if (status & RX_ERROR) {
7498c2ecf20Sopenharmony_ci		ps->rx_errors++;
7508c2ecf20Sopenharmony_ci		if (status & RX_MISSED_FRAME)
7518c2ecf20Sopenharmony_ci			ps->rx_missed_errors++;
7528c2ecf20Sopenharmony_ci		if (status & (RX_OVERLEN | RX_RUNT | RX_LEN_ERROR))
7538c2ecf20Sopenharmony_ci			ps->rx_length_errors++;
7548c2ecf20Sopenharmony_ci		if (status & RX_CRC_ERROR)
7558c2ecf20Sopenharmony_ci			ps->rx_crc_errors++;
7568c2ecf20Sopenharmony_ci		if (status & RX_COLL)
7578c2ecf20Sopenharmony_ci			ps->collisions++;
7588c2ecf20Sopenharmony_ci	} else
7598c2ecf20Sopenharmony_ci		ps->rx_bytes += status & RX_FRAME_LEN_MASK;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci/*
7648c2ecf20Sopenharmony_ci * Au1000 receive routine.
7658c2ecf20Sopenharmony_ci */
7668c2ecf20Sopenharmony_cistatic int au1000_rx(struct net_device *dev)
7678c2ecf20Sopenharmony_ci{
7688c2ecf20Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
7698c2ecf20Sopenharmony_ci	struct sk_buff *skb;
7708c2ecf20Sopenharmony_ci	struct rx_dma *prxd;
7718c2ecf20Sopenharmony_ci	u32 buff_stat, status;
7728c2ecf20Sopenharmony_ci	struct db_dest *pDB;
7738c2ecf20Sopenharmony_ci	u32	frmlen;
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	netif_dbg(aup, rx_status, dev, "au1000_rx head %d\n", aup->rx_head);
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	prxd = aup->rx_dma_ring[aup->rx_head];
7788c2ecf20Sopenharmony_ci	buff_stat = prxd->buff_stat;
7798c2ecf20Sopenharmony_ci	while (buff_stat & RX_T_DONE)  {
7808c2ecf20Sopenharmony_ci		status = prxd->status;
7818c2ecf20Sopenharmony_ci		pDB = aup->rx_db_inuse[aup->rx_head];
7828c2ecf20Sopenharmony_ci		au1000_update_rx_stats(dev, status);
7838c2ecf20Sopenharmony_ci		if (!(status & RX_ERROR))  {
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci			/* good frame */
7868c2ecf20Sopenharmony_ci			frmlen = (status & RX_FRAME_LEN_MASK);
7878c2ecf20Sopenharmony_ci			frmlen -= 4; /* Remove FCS */
7888c2ecf20Sopenharmony_ci			skb = netdev_alloc_skb(dev, frmlen + 2);
7898c2ecf20Sopenharmony_ci			if (skb == NULL) {
7908c2ecf20Sopenharmony_ci				dev->stats.rx_dropped++;
7918c2ecf20Sopenharmony_ci				continue;
7928c2ecf20Sopenharmony_ci			}
7938c2ecf20Sopenharmony_ci			skb_reserve(skb, 2);	/* 16 byte IP header align */
7948c2ecf20Sopenharmony_ci			skb_copy_to_linear_data(skb,
7958c2ecf20Sopenharmony_ci				(unsigned char *)pDB->vaddr, frmlen);
7968c2ecf20Sopenharmony_ci			skb_put(skb, frmlen);
7978c2ecf20Sopenharmony_ci			skb->protocol = eth_type_trans(skb, dev);
7988c2ecf20Sopenharmony_ci			netif_rx(skb);	/* pass the packet to upper layers */
7998c2ecf20Sopenharmony_ci		} else {
8008c2ecf20Sopenharmony_ci			if (au1000_debug > 4) {
8018c2ecf20Sopenharmony_ci				pr_err("rx_error(s):");
8028c2ecf20Sopenharmony_ci				if (status & RX_MISSED_FRAME)
8038c2ecf20Sopenharmony_ci					pr_cont(" miss");
8048c2ecf20Sopenharmony_ci				if (status & RX_WDOG_TIMER)
8058c2ecf20Sopenharmony_ci					pr_cont(" wdog");
8068c2ecf20Sopenharmony_ci				if (status & RX_RUNT)
8078c2ecf20Sopenharmony_ci					pr_cont(" runt");
8088c2ecf20Sopenharmony_ci				if (status & RX_OVERLEN)
8098c2ecf20Sopenharmony_ci					pr_cont(" overlen");
8108c2ecf20Sopenharmony_ci				if (status & RX_COLL)
8118c2ecf20Sopenharmony_ci					pr_cont(" coll");
8128c2ecf20Sopenharmony_ci				if (status & RX_MII_ERROR)
8138c2ecf20Sopenharmony_ci					pr_cont(" mii error");
8148c2ecf20Sopenharmony_ci				if (status & RX_CRC_ERROR)
8158c2ecf20Sopenharmony_ci					pr_cont(" crc error");
8168c2ecf20Sopenharmony_ci				if (status & RX_LEN_ERROR)
8178c2ecf20Sopenharmony_ci					pr_cont(" len error");
8188c2ecf20Sopenharmony_ci				if (status & RX_U_CNTRL_FRAME)
8198c2ecf20Sopenharmony_ci					pr_cont(" u control frame");
8208c2ecf20Sopenharmony_ci				pr_cont("\n");
8218c2ecf20Sopenharmony_ci			}
8228c2ecf20Sopenharmony_ci		}
8238c2ecf20Sopenharmony_ci		prxd->buff_stat = (u32)(pDB->dma_addr | RX_DMA_ENABLE);
8248c2ecf20Sopenharmony_ci		aup->rx_head = (aup->rx_head + 1) & (NUM_RX_DMA - 1);
8258c2ecf20Sopenharmony_ci		wmb(); /* drain writebuffer */
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci		/* next descriptor */
8288c2ecf20Sopenharmony_ci		prxd = aup->rx_dma_ring[aup->rx_head];
8298c2ecf20Sopenharmony_ci		buff_stat = prxd->buff_stat;
8308c2ecf20Sopenharmony_ci	}
8318c2ecf20Sopenharmony_ci	return 0;
8328c2ecf20Sopenharmony_ci}
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_cistatic void au1000_update_tx_stats(struct net_device *dev, u32 status)
8358c2ecf20Sopenharmony_ci{
8368c2ecf20Sopenharmony_ci	struct net_device_stats *ps = &dev->stats;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	if (status & TX_FRAME_ABORTED) {
8398c2ecf20Sopenharmony_ci		if (!dev->phydev || (DUPLEX_FULL == dev->phydev->duplex)) {
8408c2ecf20Sopenharmony_ci			if (status & (TX_JAB_TIMEOUT | TX_UNDERRUN)) {
8418c2ecf20Sopenharmony_ci				/* any other tx errors are only valid
8428c2ecf20Sopenharmony_ci				 * in half duplex mode
8438c2ecf20Sopenharmony_ci				 */
8448c2ecf20Sopenharmony_ci				ps->tx_errors++;
8458c2ecf20Sopenharmony_ci				ps->tx_aborted_errors++;
8468c2ecf20Sopenharmony_ci			}
8478c2ecf20Sopenharmony_ci		} else {
8488c2ecf20Sopenharmony_ci			ps->tx_errors++;
8498c2ecf20Sopenharmony_ci			ps->tx_aborted_errors++;
8508c2ecf20Sopenharmony_ci			if (status & (TX_NO_CARRIER | TX_LOSS_CARRIER))
8518c2ecf20Sopenharmony_ci				ps->tx_carrier_errors++;
8528c2ecf20Sopenharmony_ci		}
8538c2ecf20Sopenharmony_ci	}
8548c2ecf20Sopenharmony_ci}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci/*
8578c2ecf20Sopenharmony_ci * Called from the interrupt service routine to acknowledge
8588c2ecf20Sopenharmony_ci * the TX DONE bits.  This is a must if the irq is setup as
8598c2ecf20Sopenharmony_ci * edge triggered.
8608c2ecf20Sopenharmony_ci */
8618c2ecf20Sopenharmony_cistatic void au1000_tx_ack(struct net_device *dev)
8628c2ecf20Sopenharmony_ci{
8638c2ecf20Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
8648c2ecf20Sopenharmony_ci	struct tx_dma *ptxd;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	ptxd = aup->tx_dma_ring[aup->tx_tail];
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	while (ptxd->buff_stat & TX_T_DONE) {
8698c2ecf20Sopenharmony_ci		au1000_update_tx_stats(dev, ptxd->status);
8708c2ecf20Sopenharmony_ci		ptxd->buff_stat &= ~TX_T_DONE;
8718c2ecf20Sopenharmony_ci		ptxd->len = 0;
8728c2ecf20Sopenharmony_ci		wmb(); /* drain writebuffer */
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci		aup->tx_tail = (aup->tx_tail + 1) & (NUM_TX_DMA - 1);
8758c2ecf20Sopenharmony_ci		ptxd = aup->tx_dma_ring[aup->tx_tail];
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci		if (aup->tx_full) {
8788c2ecf20Sopenharmony_ci			aup->tx_full = 0;
8798c2ecf20Sopenharmony_ci			netif_wake_queue(dev);
8808c2ecf20Sopenharmony_ci		}
8818c2ecf20Sopenharmony_ci	}
8828c2ecf20Sopenharmony_ci}
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci/*
8858c2ecf20Sopenharmony_ci * Au1000 interrupt service routine.
8868c2ecf20Sopenharmony_ci */
8878c2ecf20Sopenharmony_cistatic irqreturn_t au1000_interrupt(int irq, void *dev_id)
8888c2ecf20Sopenharmony_ci{
8898c2ecf20Sopenharmony_ci	struct net_device *dev = dev_id;
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	/* Handle RX interrupts first to minimize chance of overrun */
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	au1000_rx(dev);
8948c2ecf20Sopenharmony_ci	au1000_tx_ack(dev);
8958c2ecf20Sopenharmony_ci	return IRQ_RETVAL(1);
8968c2ecf20Sopenharmony_ci}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_cistatic int au1000_open(struct net_device *dev)
8998c2ecf20Sopenharmony_ci{
9008c2ecf20Sopenharmony_ci	int retval;
9018c2ecf20Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	netif_dbg(aup, drv, dev, "open: dev=%p\n", dev);
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	retval = request_irq(dev->irq, au1000_interrupt, 0,
9068c2ecf20Sopenharmony_ci					dev->name, dev);
9078c2ecf20Sopenharmony_ci	if (retval) {
9088c2ecf20Sopenharmony_ci		netdev_err(dev, "unable to get IRQ %d\n", dev->irq);
9098c2ecf20Sopenharmony_ci		return retval;
9108c2ecf20Sopenharmony_ci	}
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci	retval = au1000_init(dev);
9138c2ecf20Sopenharmony_ci	if (retval) {
9148c2ecf20Sopenharmony_ci		netdev_err(dev, "error in au1000_init\n");
9158c2ecf20Sopenharmony_ci		free_irq(dev->irq, dev);
9168c2ecf20Sopenharmony_ci		return retval;
9178c2ecf20Sopenharmony_ci	}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	if (dev->phydev)
9208c2ecf20Sopenharmony_ci		phy_start(dev->phydev);
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	netif_start_queue(dev);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	netif_dbg(aup, drv, dev, "open: Initialization done.\n");
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	return 0;
9278c2ecf20Sopenharmony_ci}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_cistatic int au1000_close(struct net_device *dev)
9308c2ecf20Sopenharmony_ci{
9318c2ecf20Sopenharmony_ci	unsigned long flags;
9328c2ecf20Sopenharmony_ci	struct au1000_private *const aup = netdev_priv(dev);
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	netif_dbg(aup, drv, dev, "close: dev=%p\n", dev);
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci	if (dev->phydev)
9378c2ecf20Sopenharmony_ci		phy_stop(dev->phydev);
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci	spin_lock_irqsave(&aup->lock, flags);
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	au1000_reset_mac_unlocked(dev);
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	/* stop the device */
9448c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	/* disable the interrupt */
9478c2ecf20Sopenharmony_ci	free_irq(dev->irq, dev);
9488c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&aup->lock, flags);
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	return 0;
9518c2ecf20Sopenharmony_ci}
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci/*
9548c2ecf20Sopenharmony_ci * Au1000 transmit routine.
9558c2ecf20Sopenharmony_ci */
9568c2ecf20Sopenharmony_cistatic netdev_tx_t au1000_tx(struct sk_buff *skb, struct net_device *dev)
9578c2ecf20Sopenharmony_ci{
9588c2ecf20Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
9598c2ecf20Sopenharmony_ci	struct net_device_stats *ps = &dev->stats;
9608c2ecf20Sopenharmony_ci	struct tx_dma *ptxd;
9618c2ecf20Sopenharmony_ci	u32 buff_stat;
9628c2ecf20Sopenharmony_ci	struct db_dest *pDB;
9638c2ecf20Sopenharmony_ci	int i;
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	netif_dbg(aup, tx_queued, dev, "tx: aup %x len=%d, data=%p, head %d\n",
9668c2ecf20Sopenharmony_ci				(unsigned)aup, skb->len,
9678c2ecf20Sopenharmony_ci				skb->data, aup->tx_head);
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	ptxd = aup->tx_dma_ring[aup->tx_head];
9708c2ecf20Sopenharmony_ci	buff_stat = ptxd->buff_stat;
9718c2ecf20Sopenharmony_ci	if (buff_stat & TX_DMA_ENABLE) {
9728c2ecf20Sopenharmony_ci		/* We've wrapped around and the transmitter is still busy */
9738c2ecf20Sopenharmony_ci		netif_stop_queue(dev);
9748c2ecf20Sopenharmony_ci		aup->tx_full = 1;
9758c2ecf20Sopenharmony_ci		return NETDEV_TX_BUSY;
9768c2ecf20Sopenharmony_ci	} else if (buff_stat & TX_T_DONE) {
9778c2ecf20Sopenharmony_ci		au1000_update_tx_stats(dev, ptxd->status);
9788c2ecf20Sopenharmony_ci		ptxd->len = 0;
9798c2ecf20Sopenharmony_ci	}
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	if (aup->tx_full) {
9828c2ecf20Sopenharmony_ci		aup->tx_full = 0;
9838c2ecf20Sopenharmony_ci		netif_wake_queue(dev);
9848c2ecf20Sopenharmony_ci	}
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	pDB = aup->tx_db_inuse[aup->tx_head];
9878c2ecf20Sopenharmony_ci	skb_copy_from_linear_data(skb, (void *)pDB->vaddr, skb->len);
9888c2ecf20Sopenharmony_ci	if (skb->len < ETH_ZLEN) {
9898c2ecf20Sopenharmony_ci		for (i = skb->len; i < ETH_ZLEN; i++)
9908c2ecf20Sopenharmony_ci			((char *)pDB->vaddr)[i] = 0;
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci		ptxd->len = ETH_ZLEN;
9938c2ecf20Sopenharmony_ci	} else
9948c2ecf20Sopenharmony_ci		ptxd->len = skb->len;
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	ps->tx_packets++;
9978c2ecf20Sopenharmony_ci	ps->tx_bytes += ptxd->len;
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	ptxd->buff_stat = pDB->dma_addr | TX_DMA_ENABLE;
10008c2ecf20Sopenharmony_ci	wmb(); /* drain writebuffer */
10018c2ecf20Sopenharmony_ci	dev_kfree_skb(skb);
10028c2ecf20Sopenharmony_ci	aup->tx_head = (aup->tx_head + 1) & (NUM_TX_DMA - 1);
10038c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
10048c2ecf20Sopenharmony_ci}
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci/*
10078c2ecf20Sopenharmony_ci * The Tx ring has been full longer than the watchdog timeout
10088c2ecf20Sopenharmony_ci * value. The transmitter must be hung?
10098c2ecf20Sopenharmony_ci */
10108c2ecf20Sopenharmony_cistatic void au1000_tx_timeout(struct net_device *dev, unsigned int txqueue)
10118c2ecf20Sopenharmony_ci{
10128c2ecf20Sopenharmony_ci	netdev_err(dev, "au1000_tx_timeout: dev=%p\n", dev);
10138c2ecf20Sopenharmony_ci	au1000_reset_mac(dev);
10148c2ecf20Sopenharmony_ci	au1000_init(dev);
10158c2ecf20Sopenharmony_ci	netif_trans_update(dev); /* prevent tx timeout */
10168c2ecf20Sopenharmony_ci	netif_wake_queue(dev);
10178c2ecf20Sopenharmony_ci}
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_cistatic void au1000_multicast_list(struct net_device *dev)
10208c2ecf20Sopenharmony_ci{
10218c2ecf20Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
10228c2ecf20Sopenharmony_ci	u32 reg;
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	netif_dbg(aup, drv, dev, "%s: flags=%x\n", __func__, dev->flags);
10258c2ecf20Sopenharmony_ci	reg = readl(&aup->mac->control);
10268c2ecf20Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
10278c2ecf20Sopenharmony_ci		reg |= MAC_PROMISCUOUS;
10288c2ecf20Sopenharmony_ci	} else if ((dev->flags & IFF_ALLMULTI)  ||
10298c2ecf20Sopenharmony_ci			   netdev_mc_count(dev) > MULTICAST_FILTER_LIMIT) {
10308c2ecf20Sopenharmony_ci		reg |= MAC_PASS_ALL_MULTI;
10318c2ecf20Sopenharmony_ci		reg &= ~MAC_PROMISCUOUS;
10328c2ecf20Sopenharmony_ci		netdev_info(dev, "Pass all multicast\n");
10338c2ecf20Sopenharmony_ci	} else {
10348c2ecf20Sopenharmony_ci		struct netdev_hw_addr *ha;
10358c2ecf20Sopenharmony_ci		u32 mc_filter[2];	/* Multicast hash filter */
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci		mc_filter[1] = mc_filter[0] = 0;
10388c2ecf20Sopenharmony_ci		netdev_for_each_mc_addr(ha, dev)
10398c2ecf20Sopenharmony_ci			set_bit(ether_crc(ETH_ALEN, ha->addr)>>26,
10408c2ecf20Sopenharmony_ci					(long *)mc_filter);
10418c2ecf20Sopenharmony_ci		writel(mc_filter[1], &aup->mac->multi_hash_high);
10428c2ecf20Sopenharmony_ci		writel(mc_filter[0], &aup->mac->multi_hash_low);
10438c2ecf20Sopenharmony_ci		reg &= ~MAC_PROMISCUOUS;
10448c2ecf20Sopenharmony_ci		reg |= MAC_HASH_MODE;
10458c2ecf20Sopenharmony_ci	}
10468c2ecf20Sopenharmony_ci	writel(reg, &aup->mac->control);
10478c2ecf20Sopenharmony_ci}
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_cistatic const struct net_device_ops au1000_netdev_ops = {
10508c2ecf20Sopenharmony_ci	.ndo_open		= au1000_open,
10518c2ecf20Sopenharmony_ci	.ndo_stop		= au1000_close,
10528c2ecf20Sopenharmony_ci	.ndo_start_xmit		= au1000_tx,
10538c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= au1000_multicast_list,
10548c2ecf20Sopenharmony_ci	.ndo_do_ioctl		= phy_do_ioctl_running,
10558c2ecf20Sopenharmony_ci	.ndo_tx_timeout		= au1000_tx_timeout,
10568c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
10578c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
10588c2ecf20Sopenharmony_ci};
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_cistatic int au1000_probe(struct platform_device *pdev)
10618c2ecf20Sopenharmony_ci{
10628c2ecf20Sopenharmony_ci	struct au1000_private *aup = NULL;
10638c2ecf20Sopenharmony_ci	struct au1000_eth_platform_data *pd;
10648c2ecf20Sopenharmony_ci	struct net_device *dev = NULL;
10658c2ecf20Sopenharmony_ci	struct db_dest *pDB, *pDBfree;
10668c2ecf20Sopenharmony_ci	int irq, i, err = 0;
10678c2ecf20Sopenharmony_ci	struct resource *base, *macen, *macdma;
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
10708c2ecf20Sopenharmony_ci	if (!base) {
10718c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to retrieve base register\n");
10728c2ecf20Sopenharmony_ci		err = -ENODEV;
10738c2ecf20Sopenharmony_ci		goto out;
10748c2ecf20Sopenharmony_ci	}
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	macen = platform_get_resource(pdev, IORESOURCE_MEM, 1);
10778c2ecf20Sopenharmony_ci	if (!macen) {
10788c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to retrieve MAC Enable register\n");
10798c2ecf20Sopenharmony_ci		err = -ENODEV;
10808c2ecf20Sopenharmony_ci		goto out;
10818c2ecf20Sopenharmony_ci	}
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
10848c2ecf20Sopenharmony_ci	if (irq < 0) {
10858c2ecf20Sopenharmony_ci		err = -ENODEV;
10868c2ecf20Sopenharmony_ci		goto out;
10878c2ecf20Sopenharmony_ci	}
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	macdma = platform_get_resource(pdev, IORESOURCE_MEM, 2);
10908c2ecf20Sopenharmony_ci	if (!macdma) {
10918c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to retrieve MACDMA registers\n");
10928c2ecf20Sopenharmony_ci		err = -ENODEV;
10938c2ecf20Sopenharmony_ci		goto out;
10948c2ecf20Sopenharmony_ci	}
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	if (!request_mem_region(base->start, resource_size(base),
10978c2ecf20Sopenharmony_ci							pdev->name)) {
10988c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to request memory region for base registers\n");
10998c2ecf20Sopenharmony_ci		err = -ENXIO;
11008c2ecf20Sopenharmony_ci		goto out;
11018c2ecf20Sopenharmony_ci	}
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	if (!request_mem_region(macen->start, resource_size(macen),
11048c2ecf20Sopenharmony_ci							pdev->name)) {
11058c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to request memory region for MAC enable register\n");
11068c2ecf20Sopenharmony_ci		err = -ENXIO;
11078c2ecf20Sopenharmony_ci		goto err_request;
11088c2ecf20Sopenharmony_ci	}
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci	if (!request_mem_region(macdma->start, resource_size(macdma),
11118c2ecf20Sopenharmony_ci							pdev->name)) {
11128c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to request MACDMA memory region\n");
11138c2ecf20Sopenharmony_ci		err = -ENXIO;
11148c2ecf20Sopenharmony_ci		goto err_macdma;
11158c2ecf20Sopenharmony_ci	}
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	dev = alloc_etherdev(sizeof(struct au1000_private));
11188c2ecf20Sopenharmony_ci	if (!dev) {
11198c2ecf20Sopenharmony_ci		err = -ENOMEM;
11208c2ecf20Sopenharmony_ci		goto err_alloc;
11218c2ecf20Sopenharmony_ci	}
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(dev, &pdev->dev);
11248c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, dev);
11258c2ecf20Sopenharmony_ci	aup = netdev_priv(dev);
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	spin_lock_init(&aup->lock);
11288c2ecf20Sopenharmony_ci	aup->msg_enable = (au1000_debug < 4 ?
11298c2ecf20Sopenharmony_ci				AU1000_DEF_MSG_ENABLE : au1000_debug);
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci	/* Allocate the data buffers
11328c2ecf20Sopenharmony_ci	 * Snooping works fine with eth on all au1xxx
11338c2ecf20Sopenharmony_ci	 */
11348c2ecf20Sopenharmony_ci	aup->vaddr = (u32)dma_alloc_coherent(&pdev->dev, MAX_BUF_SIZE *
11358c2ecf20Sopenharmony_ci					  (NUM_TX_BUFFS + NUM_RX_BUFFS),
11368c2ecf20Sopenharmony_ci					  &aup->dma_addr, 0);
11378c2ecf20Sopenharmony_ci	if (!aup->vaddr) {
11388c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to allocate data buffers\n");
11398c2ecf20Sopenharmony_ci		err = -ENOMEM;
11408c2ecf20Sopenharmony_ci		goto err_vaddr;
11418c2ecf20Sopenharmony_ci	}
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	/* aup->mac is the base address of the MAC's registers */
11448c2ecf20Sopenharmony_ci	aup->mac = (struct mac_reg *)
11458c2ecf20Sopenharmony_ci			ioremap(base->start, resource_size(base));
11468c2ecf20Sopenharmony_ci	if (!aup->mac) {
11478c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to ioremap MAC registers\n");
11488c2ecf20Sopenharmony_ci		err = -ENXIO;
11498c2ecf20Sopenharmony_ci		goto err_remap1;
11508c2ecf20Sopenharmony_ci	}
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci	/* Setup some variables for quick register address access */
11538c2ecf20Sopenharmony_ci	aup->enable = (u32 *)ioremap(macen->start,
11548c2ecf20Sopenharmony_ci						resource_size(macen));
11558c2ecf20Sopenharmony_ci	if (!aup->enable) {
11568c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to ioremap MAC enable register\n");
11578c2ecf20Sopenharmony_ci		err = -ENXIO;
11588c2ecf20Sopenharmony_ci		goto err_remap2;
11598c2ecf20Sopenharmony_ci	}
11608c2ecf20Sopenharmony_ci	aup->mac_id = pdev->id;
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	aup->macdma = ioremap(macdma->start, resource_size(macdma));
11638c2ecf20Sopenharmony_ci	if (!aup->macdma) {
11648c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to ioremap MACDMA registers\n");
11658c2ecf20Sopenharmony_ci		err = -ENXIO;
11668c2ecf20Sopenharmony_ci		goto err_remap3;
11678c2ecf20Sopenharmony_ci	}
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	au1000_setup_hw_rings(aup, aup->macdma);
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	writel(0, aup->enable);
11728c2ecf20Sopenharmony_ci	aup->mac_enabled = 0;
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	pd = dev_get_platdata(&pdev->dev);
11758c2ecf20Sopenharmony_ci	if (!pd) {
11768c2ecf20Sopenharmony_ci		dev_info(&pdev->dev, "no platform_data passed,"
11778c2ecf20Sopenharmony_ci					" PHY search on MAC0\n");
11788c2ecf20Sopenharmony_ci		aup->phy1_search_mac0 = 1;
11798c2ecf20Sopenharmony_ci	} else {
11808c2ecf20Sopenharmony_ci		if (is_valid_ether_addr(pd->mac)) {
11818c2ecf20Sopenharmony_ci			memcpy(dev->dev_addr, pd->mac, ETH_ALEN);
11828c2ecf20Sopenharmony_ci		} else {
11838c2ecf20Sopenharmony_ci			/* Set a random MAC since no valid provided by platform_data. */
11848c2ecf20Sopenharmony_ci			eth_hw_addr_random(dev);
11858c2ecf20Sopenharmony_ci		}
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci		aup->phy_static_config = pd->phy_static_config;
11888c2ecf20Sopenharmony_ci		aup->phy_search_highest_addr = pd->phy_search_highest_addr;
11898c2ecf20Sopenharmony_ci		aup->phy1_search_mac0 = pd->phy1_search_mac0;
11908c2ecf20Sopenharmony_ci		aup->phy_addr = pd->phy_addr;
11918c2ecf20Sopenharmony_ci		aup->phy_busid = pd->phy_busid;
11928c2ecf20Sopenharmony_ci		aup->phy_irq = pd->phy_irq;
11938c2ecf20Sopenharmony_ci	}
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	if (aup->phy_busid > 0) {
11968c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "MAC0-associated PHY attached 2nd MACs MII bus not supported yet\n");
11978c2ecf20Sopenharmony_ci		err = -ENODEV;
11988c2ecf20Sopenharmony_ci		goto err_mdiobus_alloc;
11998c2ecf20Sopenharmony_ci	}
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	aup->mii_bus = mdiobus_alloc();
12028c2ecf20Sopenharmony_ci	if (aup->mii_bus == NULL) {
12038c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to allocate mdiobus structure\n");
12048c2ecf20Sopenharmony_ci		err = -ENOMEM;
12058c2ecf20Sopenharmony_ci		goto err_mdiobus_alloc;
12068c2ecf20Sopenharmony_ci	}
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci	aup->mii_bus->priv = dev;
12098c2ecf20Sopenharmony_ci	aup->mii_bus->read = au1000_mdiobus_read;
12108c2ecf20Sopenharmony_ci	aup->mii_bus->write = au1000_mdiobus_write;
12118c2ecf20Sopenharmony_ci	aup->mii_bus->reset = au1000_mdiobus_reset;
12128c2ecf20Sopenharmony_ci	aup->mii_bus->name = "au1000_eth_mii";
12138c2ecf20Sopenharmony_ci	snprintf(aup->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
12148c2ecf20Sopenharmony_ci		pdev->name, aup->mac_id);
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	/* if known, set corresponding PHY IRQs */
12178c2ecf20Sopenharmony_ci	if (aup->phy_static_config)
12188c2ecf20Sopenharmony_ci		if (aup->phy_irq && aup->phy_busid == aup->mac_id)
12198c2ecf20Sopenharmony_ci			aup->mii_bus->irq[aup->phy_addr] = aup->phy_irq;
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	err = mdiobus_register(aup->mii_bus);
12228c2ecf20Sopenharmony_ci	if (err) {
12238c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to register MDIO bus\n");
12248c2ecf20Sopenharmony_ci		goto err_mdiobus_reg;
12258c2ecf20Sopenharmony_ci	}
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci	err = au1000_mii_probe(dev);
12288c2ecf20Sopenharmony_ci	if (err != 0)
12298c2ecf20Sopenharmony_ci		goto err_out;
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	pDBfree = NULL;
12328c2ecf20Sopenharmony_ci	/* setup the data buffer descriptors and attach a buffer to each one */
12338c2ecf20Sopenharmony_ci	pDB = aup->db;
12348c2ecf20Sopenharmony_ci	for (i = 0; i < (NUM_TX_BUFFS+NUM_RX_BUFFS); i++) {
12358c2ecf20Sopenharmony_ci		pDB->pnext = pDBfree;
12368c2ecf20Sopenharmony_ci		pDBfree = pDB;
12378c2ecf20Sopenharmony_ci		pDB->vaddr = (u32 *)((unsigned)aup->vaddr + MAX_BUF_SIZE*i);
12388c2ecf20Sopenharmony_ci		pDB->dma_addr = (dma_addr_t)virt_to_bus(pDB->vaddr);
12398c2ecf20Sopenharmony_ci		pDB++;
12408c2ecf20Sopenharmony_ci	}
12418c2ecf20Sopenharmony_ci	aup->pDBfree = pDBfree;
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci	err = -ENODEV;
12448c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_RX_DMA; i++) {
12458c2ecf20Sopenharmony_ci		pDB = au1000_GetFreeDB(aup);
12468c2ecf20Sopenharmony_ci		if (!pDB)
12478c2ecf20Sopenharmony_ci			goto err_out;
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci		aup->rx_dma_ring[i]->buff_stat = (unsigned)pDB->dma_addr;
12508c2ecf20Sopenharmony_ci		aup->rx_db_inuse[i] = pDB;
12518c2ecf20Sopenharmony_ci	}
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_TX_DMA; i++) {
12548c2ecf20Sopenharmony_ci		pDB = au1000_GetFreeDB(aup);
12558c2ecf20Sopenharmony_ci		if (!pDB)
12568c2ecf20Sopenharmony_ci			goto err_out;
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci		aup->tx_dma_ring[i]->buff_stat = (unsigned)pDB->dma_addr;
12598c2ecf20Sopenharmony_ci		aup->tx_dma_ring[i]->len = 0;
12608c2ecf20Sopenharmony_ci		aup->tx_db_inuse[i] = pDB;
12618c2ecf20Sopenharmony_ci	}
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci	dev->base_addr = base->start;
12648c2ecf20Sopenharmony_ci	dev->irq = irq;
12658c2ecf20Sopenharmony_ci	dev->netdev_ops = &au1000_netdev_ops;
12668c2ecf20Sopenharmony_ci	dev->ethtool_ops = &au1000_ethtool_ops;
12678c2ecf20Sopenharmony_ci	dev->watchdog_timeo = ETH_TX_TIMEOUT;
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	/*
12708c2ecf20Sopenharmony_ci	 * The boot code uses the ethernet controller, so reset it to start
12718c2ecf20Sopenharmony_ci	 * fresh.  au1000_init() expects that the device is in reset state.
12728c2ecf20Sopenharmony_ci	 */
12738c2ecf20Sopenharmony_ci	au1000_reset_mac(dev);
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	err = register_netdev(dev);
12768c2ecf20Sopenharmony_ci	if (err) {
12778c2ecf20Sopenharmony_ci		netdev_err(dev, "Cannot register net device, aborting.\n");
12788c2ecf20Sopenharmony_ci		goto err_out;
12798c2ecf20Sopenharmony_ci	}
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ci	netdev_info(dev, "Au1xx0 Ethernet found at 0x%lx, irq %d\n",
12828c2ecf20Sopenharmony_ci			(unsigned long)base->start, irq);
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	return 0;
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_cierr_out:
12878c2ecf20Sopenharmony_ci	if (aup->mii_bus != NULL)
12888c2ecf20Sopenharmony_ci		mdiobus_unregister(aup->mii_bus);
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	/* here we should have a valid dev plus aup-> register addresses
12918c2ecf20Sopenharmony_ci	 * so we can reset the mac properly.
12928c2ecf20Sopenharmony_ci	 */
12938c2ecf20Sopenharmony_ci	au1000_reset_mac(dev);
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_RX_DMA; i++) {
12968c2ecf20Sopenharmony_ci		if (aup->rx_db_inuse[i])
12978c2ecf20Sopenharmony_ci			au1000_ReleaseDB(aup, aup->rx_db_inuse[i]);
12988c2ecf20Sopenharmony_ci	}
12998c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_TX_DMA; i++) {
13008c2ecf20Sopenharmony_ci		if (aup->tx_db_inuse[i])
13018c2ecf20Sopenharmony_ci			au1000_ReleaseDB(aup, aup->tx_db_inuse[i]);
13028c2ecf20Sopenharmony_ci	}
13038c2ecf20Sopenharmony_cierr_mdiobus_reg:
13048c2ecf20Sopenharmony_ci	mdiobus_free(aup->mii_bus);
13058c2ecf20Sopenharmony_cierr_mdiobus_alloc:
13068c2ecf20Sopenharmony_ci	iounmap(aup->macdma);
13078c2ecf20Sopenharmony_cierr_remap3:
13088c2ecf20Sopenharmony_ci	iounmap(aup->enable);
13098c2ecf20Sopenharmony_cierr_remap2:
13108c2ecf20Sopenharmony_ci	iounmap(aup->mac);
13118c2ecf20Sopenharmony_cierr_remap1:
13128c2ecf20Sopenharmony_ci	dma_free_coherent(&pdev->dev, MAX_BUF_SIZE * (NUM_TX_BUFFS + NUM_RX_BUFFS),
13138c2ecf20Sopenharmony_ci			(void *)aup->vaddr, aup->dma_addr);
13148c2ecf20Sopenharmony_cierr_vaddr:
13158c2ecf20Sopenharmony_ci	free_netdev(dev);
13168c2ecf20Sopenharmony_cierr_alloc:
13178c2ecf20Sopenharmony_ci	release_mem_region(macdma->start, resource_size(macdma));
13188c2ecf20Sopenharmony_cierr_macdma:
13198c2ecf20Sopenharmony_ci	release_mem_region(macen->start, resource_size(macen));
13208c2ecf20Sopenharmony_cierr_request:
13218c2ecf20Sopenharmony_ci	release_mem_region(base->start, resource_size(base));
13228c2ecf20Sopenharmony_ciout:
13238c2ecf20Sopenharmony_ci	return err;
13248c2ecf20Sopenharmony_ci}
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_cistatic int au1000_remove(struct platform_device *pdev)
13278c2ecf20Sopenharmony_ci{
13288c2ecf20Sopenharmony_ci	struct net_device *dev = platform_get_drvdata(pdev);
13298c2ecf20Sopenharmony_ci	struct au1000_private *aup = netdev_priv(dev);
13308c2ecf20Sopenharmony_ci	int i;
13318c2ecf20Sopenharmony_ci	struct resource *base, *macen;
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci	unregister_netdev(dev);
13348c2ecf20Sopenharmony_ci	mdiobus_unregister(aup->mii_bus);
13358c2ecf20Sopenharmony_ci	mdiobus_free(aup->mii_bus);
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_RX_DMA; i++)
13388c2ecf20Sopenharmony_ci		if (aup->rx_db_inuse[i])
13398c2ecf20Sopenharmony_ci			au1000_ReleaseDB(aup, aup->rx_db_inuse[i]);
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_TX_DMA; i++)
13428c2ecf20Sopenharmony_ci		if (aup->tx_db_inuse[i])
13438c2ecf20Sopenharmony_ci			au1000_ReleaseDB(aup, aup->tx_db_inuse[i]);
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	dma_free_coherent(&pdev->dev, MAX_BUF_SIZE * (NUM_TX_BUFFS + NUM_RX_BUFFS),
13468c2ecf20Sopenharmony_ci			(void *)aup->vaddr, aup->dma_addr);
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	iounmap(aup->macdma);
13498c2ecf20Sopenharmony_ci	iounmap(aup->mac);
13508c2ecf20Sopenharmony_ci	iounmap(aup->enable);
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	base = platform_get_resource(pdev, IORESOURCE_MEM, 2);
13538c2ecf20Sopenharmony_ci	release_mem_region(base->start, resource_size(base));
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
13568c2ecf20Sopenharmony_ci	release_mem_region(base->start, resource_size(base));
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci	macen = platform_get_resource(pdev, IORESOURCE_MEM, 1);
13598c2ecf20Sopenharmony_ci	release_mem_region(macen->start, resource_size(macen));
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	free_netdev(dev);
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	return 0;
13648c2ecf20Sopenharmony_ci}
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_cistatic struct platform_driver au1000_eth_driver = {
13678c2ecf20Sopenharmony_ci	.probe  = au1000_probe,
13688c2ecf20Sopenharmony_ci	.remove = au1000_remove,
13698c2ecf20Sopenharmony_ci	.driver = {
13708c2ecf20Sopenharmony_ci		.name   = "au1000-eth",
13718c2ecf20Sopenharmony_ci	},
13728c2ecf20Sopenharmony_ci};
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_cimodule_platform_driver(au1000_eth_driver);
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:au1000-eth");
1377