18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci	Written 1998-2000 by Donald Becker.
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci	This software may be used and distributed according to the terms of
58c2ecf20Sopenharmony_ci	the GNU General Public License (GPL), incorporated herein by reference.
68c2ecf20Sopenharmony_ci	Drivers based on or derived from this code fall under the GPL and must
78c2ecf20Sopenharmony_ci	retain the authorship, copyright and license notice.  This file is not
88c2ecf20Sopenharmony_ci	a complete program and may only be used when the entire operating
98c2ecf20Sopenharmony_ci	system is licensed under the GPL.
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci	The author may be reached as becker@scyld.com, or C/O
128c2ecf20Sopenharmony_ci	Scyld Computing Corporation
138c2ecf20Sopenharmony_ci	410 Severn Ave., Suite 210
148c2ecf20Sopenharmony_ci	Annapolis MD 21403
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci	Support information and updates available at
178c2ecf20Sopenharmony_ci	http://www.scyld.com/network/pci-skeleton.html
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci	Linux kernel updates:
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	Version 2.51, Nov 17, 2001 (jgarzik):
228c2ecf20Sopenharmony_ci	- Add ethtool support
238c2ecf20Sopenharmony_ci	- Replace some MII-related magic numbers with constants
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci*/
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define DRV_NAME	"fealnx"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic int debug;		/* 1-> print debug message */
308c2ecf20Sopenharmony_cistatic int max_interrupt_work = 20;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). */
338c2ecf20Sopenharmony_cistatic int multicast_filter_limit = 32;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* Set the copy breakpoint for the copy-only-tiny-frames scheme. */
368c2ecf20Sopenharmony_ci/* Setting to > 1518 effectively disables this feature.          */
378c2ecf20Sopenharmony_cistatic int rx_copybreak;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/* Used to pass the media type, etc.                            */
408c2ecf20Sopenharmony_ci/* Both 'options[]' and 'full_duplex[]' should exist for driver */
418c2ecf20Sopenharmony_ci/* interoperability.                                            */
428c2ecf20Sopenharmony_ci/* The media type is usually passed in 'options[]'.             */
438c2ecf20Sopenharmony_ci#define MAX_UNITS 8		/* More are supported, limit only on options */
448c2ecf20Sopenharmony_cistatic int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 };
458c2ecf20Sopenharmony_cistatic int full_duplex[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 };
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* Operational parameters that are set at compile time.                 */
488c2ecf20Sopenharmony_ci/* Keep the ring sizes a power of two for compile efficiency.           */
498c2ecf20Sopenharmony_ci/* The compiler will convert <unsigned>'%'<2^N> into a bit mask.        */
508c2ecf20Sopenharmony_ci/* Making the Tx ring too large decreases the effectiveness of channel  */
518c2ecf20Sopenharmony_ci/* bonding and packet priority.                                         */
528c2ecf20Sopenharmony_ci/* There are no ill effects from too-large receive rings.               */
538c2ecf20Sopenharmony_ci// 88-12-9 modify,
548c2ecf20Sopenharmony_ci// #define TX_RING_SIZE    16
558c2ecf20Sopenharmony_ci// #define RX_RING_SIZE    32
568c2ecf20Sopenharmony_ci#define TX_RING_SIZE    6
578c2ecf20Sopenharmony_ci#define RX_RING_SIZE    12
588c2ecf20Sopenharmony_ci#define TX_TOTAL_SIZE	TX_RING_SIZE*sizeof(struct fealnx_desc)
598c2ecf20Sopenharmony_ci#define RX_TOTAL_SIZE	RX_RING_SIZE*sizeof(struct fealnx_desc)
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/* Operational parameters that usually are not changed. */
628c2ecf20Sopenharmony_ci/* Time in jiffies before concluding the transmitter is hung. */
638c2ecf20Sopenharmony_ci#define TX_TIMEOUT      (2*HZ)
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#define PKT_BUF_SZ      1536	/* Size of each temporary Rx buffer. */
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/* Include files, designed to support most kernel versions 2.0.0 and later. */
698c2ecf20Sopenharmony_ci#include <linux/module.h>
708c2ecf20Sopenharmony_ci#include <linux/kernel.h>
718c2ecf20Sopenharmony_ci#include <linux/string.h>
728c2ecf20Sopenharmony_ci#include <linux/timer.h>
738c2ecf20Sopenharmony_ci#include <linux/errno.h>
748c2ecf20Sopenharmony_ci#include <linux/ioport.h>
758c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
768c2ecf20Sopenharmony_ci#include <linux/pci.h>
778c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
788c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
798c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
808c2ecf20Sopenharmony_ci#include <linux/init.h>
818c2ecf20Sopenharmony_ci#include <linux/mii.h>
828c2ecf20Sopenharmony_ci#include <linux/ethtool.h>
838c2ecf20Sopenharmony_ci#include <linux/crc32.h>
848c2ecf20Sopenharmony_ci#include <linux/delay.h>
858c2ecf20Sopenharmony_ci#include <linux/bitops.h>
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci#include <asm/processor.h>	/* Processor type for cache alignment. */
888c2ecf20Sopenharmony_ci#include <asm/io.h>
898c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
908c2ecf20Sopenharmony_ci#include <asm/byteorder.h>
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci/* This driver was written to use PCI memory space, however some x86 systems
938c2ecf20Sopenharmony_ci   work only with I/O space accesses. */
948c2ecf20Sopenharmony_ci#ifndef __alpha__
958c2ecf20Sopenharmony_ci#define USE_IO_OPS
968c2ecf20Sopenharmony_ci#endif
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/* Kernel compatibility defines, some common to David Hinds' PCMCIA package. */
998c2ecf20Sopenharmony_ci/* This is only in the support-all-kernels source code. */
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci#define RUN_AT(x) (jiffies + (x))
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Myson or whoever");
1048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Myson MTD-8xx 100/10M Ethernet PCI Adapter Driver");
1058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1068c2ecf20Sopenharmony_cimodule_param(max_interrupt_work, int, 0);
1078c2ecf20Sopenharmony_cimodule_param(debug, int, 0);
1088c2ecf20Sopenharmony_cimodule_param(rx_copybreak, int, 0);
1098c2ecf20Sopenharmony_cimodule_param(multicast_filter_limit, int, 0);
1108c2ecf20Sopenharmony_cimodule_param_array(options, int, NULL, 0);
1118c2ecf20Sopenharmony_cimodule_param_array(full_duplex, int, NULL, 0);
1128c2ecf20Sopenharmony_ciMODULE_PARM_DESC(max_interrupt_work, "fealnx maximum events handled per interrupt");
1138c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "fealnx enable debugging (0-1)");
1148c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rx_copybreak, "fealnx copy breakpoint for copy-only-tiny-frames");
1158c2ecf20Sopenharmony_ciMODULE_PARM_DESC(multicast_filter_limit, "fealnx maximum number of filtered multicast addresses");
1168c2ecf20Sopenharmony_ciMODULE_PARM_DESC(options, "fealnx: Bits 0-3: media type, bit 17: full duplex");
1178c2ecf20Sopenharmony_ciMODULE_PARM_DESC(full_duplex, "fealnx full duplex setting(s) (1)");
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cienum {
1208c2ecf20Sopenharmony_ci	MIN_REGION_SIZE		= 136,
1218c2ecf20Sopenharmony_ci};
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/* A chip capabilities table, matching the entries in pci_tbl[] above. */
1248c2ecf20Sopenharmony_cienum chip_capability_flags {
1258c2ecf20Sopenharmony_ci	HAS_MII_XCVR,
1268c2ecf20Sopenharmony_ci	HAS_CHIP_XCVR,
1278c2ecf20Sopenharmony_ci};
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/* 89/6/13 add, */
1308c2ecf20Sopenharmony_ci/* for different PHY */
1318c2ecf20Sopenharmony_cienum phy_type_flags {
1328c2ecf20Sopenharmony_ci	MysonPHY = 1,
1338c2ecf20Sopenharmony_ci	AhdocPHY = 2,
1348c2ecf20Sopenharmony_ci	SeeqPHY = 3,
1358c2ecf20Sopenharmony_ci	MarvellPHY = 4,
1368c2ecf20Sopenharmony_ci	Myson981 = 5,
1378c2ecf20Sopenharmony_ci	LevelOnePHY = 6,
1388c2ecf20Sopenharmony_ci	OtherPHY = 10,
1398c2ecf20Sopenharmony_ci};
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistruct chip_info {
1428c2ecf20Sopenharmony_ci	char *chip_name;
1438c2ecf20Sopenharmony_ci	int flags;
1448c2ecf20Sopenharmony_ci};
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic const struct chip_info skel_netdrv_tbl[] = {
1478c2ecf20Sopenharmony_ci 	{ "100/10M Ethernet PCI Adapter",	HAS_MII_XCVR },
1488c2ecf20Sopenharmony_ci	{ "100/10M Ethernet PCI Adapter",	HAS_CHIP_XCVR },
1498c2ecf20Sopenharmony_ci	{ "1000/100/10M Ethernet PCI Adapter",	HAS_MII_XCVR },
1508c2ecf20Sopenharmony_ci};
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci/* Offsets to the Command and Status Registers. */
1538c2ecf20Sopenharmony_cienum fealnx_offsets {
1548c2ecf20Sopenharmony_ci	PAR0 = 0x0,		/* physical address 0-3 */
1558c2ecf20Sopenharmony_ci	PAR1 = 0x04,		/* physical address 4-5 */
1568c2ecf20Sopenharmony_ci	MAR0 = 0x08,		/* multicast address 0-3 */
1578c2ecf20Sopenharmony_ci	MAR1 = 0x0C,		/* multicast address 4-7 */
1588c2ecf20Sopenharmony_ci	FAR0 = 0x10,		/* flow-control address 0-3 */
1598c2ecf20Sopenharmony_ci	FAR1 = 0x14,		/* flow-control address 4-5 */
1608c2ecf20Sopenharmony_ci	TCRRCR = 0x18,		/* receive & transmit configuration */
1618c2ecf20Sopenharmony_ci	BCR = 0x1C,		/* bus command */
1628c2ecf20Sopenharmony_ci	TXPDR = 0x20,		/* transmit polling demand */
1638c2ecf20Sopenharmony_ci	RXPDR = 0x24,		/* receive polling demand */
1648c2ecf20Sopenharmony_ci	RXCWP = 0x28,		/* receive current word pointer */
1658c2ecf20Sopenharmony_ci	TXLBA = 0x2C,		/* transmit list base address */
1668c2ecf20Sopenharmony_ci	RXLBA = 0x30,		/* receive list base address */
1678c2ecf20Sopenharmony_ci	ISR = 0x34,		/* interrupt status */
1688c2ecf20Sopenharmony_ci	IMR = 0x38,		/* interrupt mask */
1698c2ecf20Sopenharmony_ci	FTH = 0x3C,		/* flow control high/low threshold */
1708c2ecf20Sopenharmony_ci	MANAGEMENT = 0x40,	/* bootrom/eeprom and mii management */
1718c2ecf20Sopenharmony_ci	TALLY = 0x44,		/* tally counters for crc and mpa */
1728c2ecf20Sopenharmony_ci	TSR = 0x48,		/* tally counter for transmit status */
1738c2ecf20Sopenharmony_ci	BMCRSR = 0x4c,		/* basic mode control and status */
1748c2ecf20Sopenharmony_ci	PHYIDENTIFIER = 0x50,	/* phy identifier */
1758c2ecf20Sopenharmony_ci	ANARANLPAR = 0x54,	/* auto-negotiation advertisement and link
1768c2ecf20Sopenharmony_ci				   partner ability */
1778c2ecf20Sopenharmony_ci	ANEROCR = 0x58,		/* auto-negotiation expansion and pci conf. */
1788c2ecf20Sopenharmony_ci	BPREMRPSR = 0x5c,	/* bypass & receive error mask and phy status */
1798c2ecf20Sopenharmony_ci};
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci/* Bits in the interrupt status/enable registers. */
1828c2ecf20Sopenharmony_ci/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */
1838c2ecf20Sopenharmony_cienum intr_status_bits {
1848c2ecf20Sopenharmony_ci	RFCON = 0x00020000,	/* receive flow control xon packet */
1858c2ecf20Sopenharmony_ci	RFCOFF = 0x00010000,	/* receive flow control xoff packet */
1868c2ecf20Sopenharmony_ci	LSCStatus = 0x00008000,	/* link status change */
1878c2ecf20Sopenharmony_ci	ANCStatus = 0x00004000,	/* autonegotiation completed */
1888c2ecf20Sopenharmony_ci	FBE = 0x00002000,	/* fatal bus error */
1898c2ecf20Sopenharmony_ci	FBEMask = 0x00001800,	/* mask bit12-11 */
1908c2ecf20Sopenharmony_ci	ParityErr = 0x00000000,	/* parity error */
1918c2ecf20Sopenharmony_ci	TargetErr = 0x00001000,	/* target abort */
1928c2ecf20Sopenharmony_ci	MasterErr = 0x00000800,	/* master error */
1938c2ecf20Sopenharmony_ci	TUNF = 0x00000400,	/* transmit underflow */
1948c2ecf20Sopenharmony_ci	ROVF = 0x00000200,	/* receive overflow */
1958c2ecf20Sopenharmony_ci	ETI = 0x00000100,	/* transmit early int */
1968c2ecf20Sopenharmony_ci	ERI = 0x00000080,	/* receive early int */
1978c2ecf20Sopenharmony_ci	CNTOVF = 0x00000040,	/* counter overflow */
1988c2ecf20Sopenharmony_ci	RBU = 0x00000020,	/* receive buffer unavailable */
1998c2ecf20Sopenharmony_ci	TBU = 0x00000010,	/* transmit buffer unavilable */
2008c2ecf20Sopenharmony_ci	TI = 0x00000008,	/* transmit interrupt */
2018c2ecf20Sopenharmony_ci	RI = 0x00000004,	/* receive interrupt */
2028c2ecf20Sopenharmony_ci	RxErr = 0x00000002,	/* receive error */
2038c2ecf20Sopenharmony_ci};
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci/* Bits in the NetworkConfig register, W for writing, R for reading */
2068c2ecf20Sopenharmony_ci/* FIXME: some names are invented by me. Marked with (name?) */
2078c2ecf20Sopenharmony_ci/* If you have docs and know bit names, please fix 'em */
2088c2ecf20Sopenharmony_cienum rx_mode_bits {
2098c2ecf20Sopenharmony_ci	CR_W_ENH	= 0x02000000,	/* enhanced mode (name?) */
2108c2ecf20Sopenharmony_ci	CR_W_FD		= 0x00100000,	/* full duplex */
2118c2ecf20Sopenharmony_ci	CR_W_PS10	= 0x00080000,	/* 10 mbit */
2128c2ecf20Sopenharmony_ci	CR_W_TXEN	= 0x00040000,	/* tx enable (name?) */
2138c2ecf20Sopenharmony_ci	CR_W_PS1000	= 0x00010000,	/* 1000 mbit */
2148c2ecf20Sopenharmony_ci     /* CR_W_RXBURSTMASK= 0x00000e00, Im unsure about this */
2158c2ecf20Sopenharmony_ci	CR_W_RXMODEMASK	= 0x000000e0,
2168c2ecf20Sopenharmony_ci	CR_W_PROM	= 0x00000080,	/* promiscuous mode */
2178c2ecf20Sopenharmony_ci	CR_W_AB		= 0x00000040,	/* accept broadcast */
2188c2ecf20Sopenharmony_ci	CR_W_AM		= 0x00000020,	/* accept mutlicast */
2198c2ecf20Sopenharmony_ci	CR_W_ARP	= 0x00000008,	/* receive runt pkt */
2208c2ecf20Sopenharmony_ci	CR_W_ALP	= 0x00000004,	/* receive long pkt */
2218c2ecf20Sopenharmony_ci	CR_W_SEP	= 0x00000002,	/* receive error pkt */
2228c2ecf20Sopenharmony_ci	CR_W_RXEN	= 0x00000001,	/* rx enable (unicast?) (name?) */
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	CR_R_TXSTOP	= 0x04000000,	/* tx stopped (name?) */
2258c2ecf20Sopenharmony_ci	CR_R_FD		= 0x00100000,	/* full duplex detected */
2268c2ecf20Sopenharmony_ci	CR_R_PS10	= 0x00080000,	/* 10 mbit detected */
2278c2ecf20Sopenharmony_ci	CR_R_RXSTOP	= 0x00008000,	/* rx stopped (name?) */
2288c2ecf20Sopenharmony_ci};
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci/* The Tulip Rx and Tx buffer descriptors. */
2318c2ecf20Sopenharmony_cistruct fealnx_desc {
2328c2ecf20Sopenharmony_ci	s32 status;
2338c2ecf20Sopenharmony_ci	s32 control;
2348c2ecf20Sopenharmony_ci	u32 buffer;
2358c2ecf20Sopenharmony_ci	u32 next_desc;
2368c2ecf20Sopenharmony_ci	struct fealnx_desc *next_desc_logical;
2378c2ecf20Sopenharmony_ci	struct sk_buff *skbuff;
2388c2ecf20Sopenharmony_ci	u32 reserved1;
2398c2ecf20Sopenharmony_ci	u32 reserved2;
2408c2ecf20Sopenharmony_ci};
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci/* Bits in network_desc.status */
2438c2ecf20Sopenharmony_cienum rx_desc_status_bits {
2448c2ecf20Sopenharmony_ci	RXOWN = 0x80000000,	/* own bit */
2458c2ecf20Sopenharmony_ci	FLNGMASK = 0x0fff0000,	/* frame length */
2468c2ecf20Sopenharmony_ci	FLNGShift = 16,
2478c2ecf20Sopenharmony_ci	MARSTATUS = 0x00004000,	/* multicast address received */
2488c2ecf20Sopenharmony_ci	BARSTATUS = 0x00002000,	/* broadcast address received */
2498c2ecf20Sopenharmony_ci	PHYSTATUS = 0x00001000,	/* physical address received */
2508c2ecf20Sopenharmony_ci	RXFSD = 0x00000800,	/* first descriptor */
2518c2ecf20Sopenharmony_ci	RXLSD = 0x00000400,	/* last descriptor */
2528c2ecf20Sopenharmony_ci	ErrorSummary = 0x80,	/* error summary */
2538c2ecf20Sopenharmony_ci	RUNTPKT = 0x40,		/* runt packet received */
2548c2ecf20Sopenharmony_ci	LONGPKT = 0x20,		/* long packet received */
2558c2ecf20Sopenharmony_ci	FAE = 0x10,		/* frame align error */
2568c2ecf20Sopenharmony_ci	CRC = 0x08,		/* crc error */
2578c2ecf20Sopenharmony_ci	RXER = 0x04,		/* receive error */
2588c2ecf20Sopenharmony_ci};
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cienum rx_desc_control_bits {
2618c2ecf20Sopenharmony_ci	RXIC = 0x00800000,	/* interrupt control */
2628c2ecf20Sopenharmony_ci	RBSShift = 0,
2638c2ecf20Sopenharmony_ci};
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cienum tx_desc_status_bits {
2668c2ecf20Sopenharmony_ci	TXOWN = 0x80000000,	/* own bit */
2678c2ecf20Sopenharmony_ci	JABTO = 0x00004000,	/* jabber timeout */
2688c2ecf20Sopenharmony_ci	CSL = 0x00002000,	/* carrier sense lost */
2698c2ecf20Sopenharmony_ci	LC = 0x00001000,	/* late collision */
2708c2ecf20Sopenharmony_ci	EC = 0x00000800,	/* excessive collision */
2718c2ecf20Sopenharmony_ci	UDF = 0x00000400,	/* fifo underflow */
2728c2ecf20Sopenharmony_ci	DFR = 0x00000200,	/* deferred */
2738c2ecf20Sopenharmony_ci	HF = 0x00000100,	/* heartbeat fail */
2748c2ecf20Sopenharmony_ci	NCRMask = 0x000000ff,	/* collision retry count */
2758c2ecf20Sopenharmony_ci	NCRShift = 0,
2768c2ecf20Sopenharmony_ci};
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cienum tx_desc_control_bits {
2798c2ecf20Sopenharmony_ci	TXIC = 0x80000000,	/* interrupt control */
2808c2ecf20Sopenharmony_ci	ETIControl = 0x40000000,	/* early transmit interrupt */
2818c2ecf20Sopenharmony_ci	TXLD = 0x20000000,	/* last descriptor */
2828c2ecf20Sopenharmony_ci	TXFD = 0x10000000,	/* first descriptor */
2838c2ecf20Sopenharmony_ci	CRCEnable = 0x08000000,	/* crc control */
2848c2ecf20Sopenharmony_ci	PADEnable = 0x04000000,	/* padding control */
2858c2ecf20Sopenharmony_ci	RetryTxLC = 0x02000000,	/* retry late collision */
2868c2ecf20Sopenharmony_ci	PKTSMask = 0x3ff800,	/* packet size bit21-11 */
2878c2ecf20Sopenharmony_ci	PKTSShift = 11,
2888c2ecf20Sopenharmony_ci	TBSMask = 0x000007ff,	/* transmit buffer bit 10-0 */
2898c2ecf20Sopenharmony_ci	TBSShift = 0,
2908c2ecf20Sopenharmony_ci};
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci/* BootROM/EEPROM/MII Management Register */
2938c2ecf20Sopenharmony_ci#define MASK_MIIR_MII_READ       0x00000000
2948c2ecf20Sopenharmony_ci#define MASK_MIIR_MII_WRITE      0x00000008
2958c2ecf20Sopenharmony_ci#define MASK_MIIR_MII_MDO        0x00000004
2968c2ecf20Sopenharmony_ci#define MASK_MIIR_MII_MDI        0x00000002
2978c2ecf20Sopenharmony_ci#define MASK_MIIR_MII_MDC        0x00000001
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci/* ST+OP+PHYAD+REGAD+TA */
3008c2ecf20Sopenharmony_ci#define OP_READ             0x6000	/* ST:01+OP:10+PHYAD+REGAD+TA:Z0 */
3018c2ecf20Sopenharmony_ci#define OP_WRITE            0x5002	/* ST:01+OP:01+PHYAD+REGAD+TA:10 */
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */
3048c2ecf20Sopenharmony_ci/*      Constants for Myson PHY                                              */
3058c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */
3068c2ecf20Sopenharmony_ci#define MysonPHYID      0xd0000302
3078c2ecf20Sopenharmony_ci/* 89-7-27 add, (begin) */
3088c2ecf20Sopenharmony_ci#define MysonPHYID0     0x0302
3098c2ecf20Sopenharmony_ci#define StatusRegister  18
3108c2ecf20Sopenharmony_ci#define SPEED100        0x0400	// bit10
3118c2ecf20Sopenharmony_ci#define FULLMODE        0x0800	// bit11
3128c2ecf20Sopenharmony_ci/* 89-7-27 add, (end) */
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */
3158c2ecf20Sopenharmony_ci/*      Constants for Seeq 80225 PHY                                         */
3168c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */
3178c2ecf20Sopenharmony_ci#define SeeqPHYID0      0x0016
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci#define MIIRegister18   18
3208c2ecf20Sopenharmony_ci#define SPD_DET_100     0x80
3218c2ecf20Sopenharmony_ci#define DPLX_DET_FULL   0x40
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */
3248c2ecf20Sopenharmony_ci/*      Constants for Ahdoc 101 PHY                                          */
3258c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */
3268c2ecf20Sopenharmony_ci#define AhdocPHYID0     0x0022
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci#define DiagnosticReg   18
3298c2ecf20Sopenharmony_ci#define DPLX_FULL       0x0800
3308c2ecf20Sopenharmony_ci#define Speed_100       0x0400
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci/* 89/6/13 add, */
3338c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */
3348c2ecf20Sopenharmony_ci/*      Constants                                                             */
3358c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */
3368c2ecf20Sopenharmony_ci#define MarvellPHYID0           0x0141
3378c2ecf20Sopenharmony_ci#define LevelOnePHYID0		0x0013
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci#define MII1000BaseTControlReg  9
3408c2ecf20Sopenharmony_ci#define MII1000BaseTStatusReg   10
3418c2ecf20Sopenharmony_ci#define SpecificReg		17
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci/* for 1000BaseT Control Register */
3448c2ecf20Sopenharmony_ci#define PHYAbletoPerform1000FullDuplex  0x0200
3458c2ecf20Sopenharmony_ci#define PHYAbletoPerform1000HalfDuplex  0x0100
3468c2ecf20Sopenharmony_ci#define PHY1000AbilityMask              0x300
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci// for phy specific status register, marvell phy.
3498c2ecf20Sopenharmony_ci#define SpeedMask       0x0c000
3508c2ecf20Sopenharmony_ci#define Speed_1000M     0x08000
3518c2ecf20Sopenharmony_ci#define Speed_100M      0x4000
3528c2ecf20Sopenharmony_ci#define Speed_10M       0
3538c2ecf20Sopenharmony_ci#define Full_Duplex     0x2000
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci// 89/12/29 add, for phy specific status register, levelone phy, (begin)
3568c2ecf20Sopenharmony_ci#define LXT1000_100M    0x08000
3578c2ecf20Sopenharmony_ci#define LXT1000_1000M   0x0c000
3588c2ecf20Sopenharmony_ci#define LXT1000_Full    0x200
3598c2ecf20Sopenharmony_ci// 89/12/29 add, for phy specific status register, levelone phy, (end)
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci/* for 3-in-1 case, BMCRSR register */
3628c2ecf20Sopenharmony_ci#define LinkIsUp2	0x00040000
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci/* for PHY */
3658c2ecf20Sopenharmony_ci#define LinkIsUp        0x0004
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistruct netdev_private {
3698c2ecf20Sopenharmony_ci	/* Descriptor rings first for alignment. */
3708c2ecf20Sopenharmony_ci	struct fealnx_desc *rx_ring;
3718c2ecf20Sopenharmony_ci	struct fealnx_desc *tx_ring;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	dma_addr_t rx_ring_dma;
3748c2ecf20Sopenharmony_ci	dma_addr_t tx_ring_dma;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	spinlock_t lock;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	/* Media monitoring timer. */
3798c2ecf20Sopenharmony_ci	struct timer_list timer;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	/* Reset timer */
3828c2ecf20Sopenharmony_ci	struct timer_list reset_timer;
3838c2ecf20Sopenharmony_ci	int reset_timer_armed;
3848c2ecf20Sopenharmony_ci	unsigned long crvalue_sv;
3858c2ecf20Sopenharmony_ci	unsigned long imrvalue_sv;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	/* Frequently used values: keep some adjacent for cache effect. */
3888c2ecf20Sopenharmony_ci	int flags;
3898c2ecf20Sopenharmony_ci	struct pci_dev *pci_dev;
3908c2ecf20Sopenharmony_ci	unsigned long crvalue;
3918c2ecf20Sopenharmony_ci	unsigned long bcrvalue;
3928c2ecf20Sopenharmony_ci	unsigned long imrvalue;
3938c2ecf20Sopenharmony_ci	struct fealnx_desc *cur_rx;
3948c2ecf20Sopenharmony_ci	struct fealnx_desc *lack_rxbuf;
3958c2ecf20Sopenharmony_ci	int really_rx_count;
3968c2ecf20Sopenharmony_ci	struct fealnx_desc *cur_tx;
3978c2ecf20Sopenharmony_ci	struct fealnx_desc *cur_tx_copy;
3988c2ecf20Sopenharmony_ci	int really_tx_count;
3998c2ecf20Sopenharmony_ci	int free_tx_count;
4008c2ecf20Sopenharmony_ci	unsigned int rx_buf_sz;	/* Based on MTU+slack. */
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	/* These values are keep track of the transceiver/media in use. */
4038c2ecf20Sopenharmony_ci	unsigned int linkok;
4048c2ecf20Sopenharmony_ci	unsigned int line_speed;
4058c2ecf20Sopenharmony_ci	unsigned int duplexmode;
4068c2ecf20Sopenharmony_ci	unsigned int default_port:4;	/* Last dev->if_port value. */
4078c2ecf20Sopenharmony_ci	unsigned int PHYType;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	/* MII transceiver section. */
4108c2ecf20Sopenharmony_ci	int mii_cnt;		/* MII device addresses. */
4118c2ecf20Sopenharmony_ci	unsigned char phys[2];	/* MII device addresses. */
4128c2ecf20Sopenharmony_ci	struct mii_if_info mii;
4138c2ecf20Sopenharmony_ci	void __iomem *mem;
4148c2ecf20Sopenharmony_ci};
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic int mdio_read(struct net_device *dev, int phy_id, int location);
4188c2ecf20Sopenharmony_cistatic void mdio_write(struct net_device *dev, int phy_id, int location, int value);
4198c2ecf20Sopenharmony_cistatic int netdev_open(struct net_device *dev);
4208c2ecf20Sopenharmony_cistatic void getlinktype(struct net_device *dev);
4218c2ecf20Sopenharmony_cistatic void getlinkstatus(struct net_device *dev);
4228c2ecf20Sopenharmony_cistatic void netdev_timer(struct timer_list *t);
4238c2ecf20Sopenharmony_cistatic void reset_timer(struct timer_list *t);
4248c2ecf20Sopenharmony_cistatic void fealnx_tx_timeout(struct net_device *dev, unsigned int txqueue);
4258c2ecf20Sopenharmony_cistatic void init_ring(struct net_device *dev);
4268c2ecf20Sopenharmony_cistatic netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev);
4278c2ecf20Sopenharmony_cistatic irqreturn_t intr_handler(int irq, void *dev_instance);
4288c2ecf20Sopenharmony_cistatic int netdev_rx(struct net_device *dev);
4298c2ecf20Sopenharmony_cistatic void set_rx_mode(struct net_device *dev);
4308c2ecf20Sopenharmony_cistatic void __set_rx_mode(struct net_device *dev);
4318c2ecf20Sopenharmony_cistatic struct net_device_stats *get_stats(struct net_device *dev);
4328c2ecf20Sopenharmony_cistatic int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
4338c2ecf20Sopenharmony_cistatic const struct ethtool_ops netdev_ethtool_ops;
4348c2ecf20Sopenharmony_cistatic int netdev_close(struct net_device *dev);
4358c2ecf20Sopenharmony_cistatic void reset_rx_descriptors(struct net_device *dev);
4368c2ecf20Sopenharmony_cistatic void reset_tx_descriptors(struct net_device *dev);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic void stop_nic_rx(void __iomem *ioaddr, long crvalue)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	int delay = 0x1000;
4418c2ecf20Sopenharmony_ci	iowrite32(crvalue & ~(CR_W_RXEN), ioaddr + TCRRCR);
4428c2ecf20Sopenharmony_ci	while (--delay) {
4438c2ecf20Sopenharmony_ci		if ( (ioread32(ioaddr + TCRRCR) & CR_R_RXSTOP) == CR_R_RXSTOP)
4448c2ecf20Sopenharmony_ci			break;
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_cistatic void stop_nic_rxtx(void __iomem *ioaddr, long crvalue)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	int delay = 0x1000;
4528c2ecf20Sopenharmony_ci	iowrite32(crvalue & ~(CR_W_RXEN+CR_W_TXEN), ioaddr + TCRRCR);
4538c2ecf20Sopenharmony_ci	while (--delay) {
4548c2ecf20Sopenharmony_ci		if ( (ioread32(ioaddr + TCRRCR) & (CR_R_RXSTOP+CR_R_TXSTOP))
4558c2ecf20Sopenharmony_ci					    == (CR_R_RXSTOP+CR_R_TXSTOP) )
4568c2ecf20Sopenharmony_ci			break;
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cistatic const struct net_device_ops netdev_ops = {
4618c2ecf20Sopenharmony_ci	.ndo_open		= netdev_open,
4628c2ecf20Sopenharmony_ci	.ndo_stop		= netdev_close,
4638c2ecf20Sopenharmony_ci	.ndo_start_xmit		= start_tx,
4648c2ecf20Sopenharmony_ci	.ndo_get_stats 		= get_stats,
4658c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= set_rx_mode,
4668c2ecf20Sopenharmony_ci	.ndo_do_ioctl		= mii_ioctl,
4678c2ecf20Sopenharmony_ci	.ndo_tx_timeout		= fealnx_tx_timeout,
4688c2ecf20Sopenharmony_ci	.ndo_set_mac_address 	= eth_mac_addr,
4698c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
4708c2ecf20Sopenharmony_ci};
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_cistatic int fealnx_init_one(struct pci_dev *pdev,
4738c2ecf20Sopenharmony_ci			   const struct pci_device_id *ent)
4748c2ecf20Sopenharmony_ci{
4758c2ecf20Sopenharmony_ci	struct netdev_private *np;
4768c2ecf20Sopenharmony_ci	int i, option, err, irq;
4778c2ecf20Sopenharmony_ci	static int card_idx = -1;
4788c2ecf20Sopenharmony_ci	char boardname[12];
4798c2ecf20Sopenharmony_ci	void __iomem *ioaddr;
4808c2ecf20Sopenharmony_ci	unsigned long len;
4818c2ecf20Sopenharmony_ci	unsigned int chip_id = ent->driver_data;
4828c2ecf20Sopenharmony_ci	struct net_device *dev;
4838c2ecf20Sopenharmony_ci	void *ring_space;
4848c2ecf20Sopenharmony_ci	dma_addr_t ring_dma;
4858c2ecf20Sopenharmony_ci#ifdef USE_IO_OPS
4868c2ecf20Sopenharmony_ci	int bar = 0;
4878c2ecf20Sopenharmony_ci#else
4888c2ecf20Sopenharmony_ci	int bar = 1;
4898c2ecf20Sopenharmony_ci#endif
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	card_idx++;
4928c2ecf20Sopenharmony_ci	sprintf(boardname, "fealnx%d", card_idx);
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	option = card_idx < MAX_UNITS ? options[card_idx] : 0;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	i = pci_enable_device(pdev);
4978c2ecf20Sopenharmony_ci	if (i) return i;
4988c2ecf20Sopenharmony_ci	pci_set_master(pdev);
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	len = pci_resource_len(pdev, bar);
5018c2ecf20Sopenharmony_ci	if (len < MIN_REGION_SIZE) {
5028c2ecf20Sopenharmony_ci		dev_err(&pdev->dev,
5038c2ecf20Sopenharmony_ci			   "region size %ld too small, aborting\n", len);
5048c2ecf20Sopenharmony_ci		return -ENODEV;
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	i = pci_request_regions(pdev, boardname);
5088c2ecf20Sopenharmony_ci	if (i)
5098c2ecf20Sopenharmony_ci		return i;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	irq = pdev->irq;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	ioaddr = pci_iomap(pdev, bar, len);
5148c2ecf20Sopenharmony_ci	if (!ioaddr) {
5158c2ecf20Sopenharmony_ci		err = -ENOMEM;
5168c2ecf20Sopenharmony_ci		goto err_out_res;
5178c2ecf20Sopenharmony_ci	}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	dev = alloc_etherdev(sizeof(struct netdev_private));
5208c2ecf20Sopenharmony_ci	if (!dev) {
5218c2ecf20Sopenharmony_ci		err = -ENOMEM;
5228c2ecf20Sopenharmony_ci		goto err_out_unmap;
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(dev, &pdev->dev);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	/* read ethernet id */
5278c2ecf20Sopenharmony_ci	for (i = 0; i < 6; ++i)
5288c2ecf20Sopenharmony_ci		dev->dev_addr[i] = ioread8(ioaddr + PAR0 + i);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	/* Reset the chip to erase previous misconfiguration. */
5318c2ecf20Sopenharmony_ci	iowrite32(0x00000001, ioaddr + BCR);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	/* Make certain the descriptor lists are aligned. */
5348c2ecf20Sopenharmony_ci	np = netdev_priv(dev);
5358c2ecf20Sopenharmony_ci	np->mem = ioaddr;
5368c2ecf20Sopenharmony_ci	spin_lock_init(&np->lock);
5378c2ecf20Sopenharmony_ci	np->pci_dev = pdev;
5388c2ecf20Sopenharmony_ci	np->flags = skel_netdrv_tbl[chip_id].flags;
5398c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, dev);
5408c2ecf20Sopenharmony_ci	np->mii.dev = dev;
5418c2ecf20Sopenharmony_ci	np->mii.mdio_read = mdio_read;
5428c2ecf20Sopenharmony_ci	np->mii.mdio_write = mdio_write;
5438c2ecf20Sopenharmony_ci	np->mii.phy_id_mask = 0x1f;
5448c2ecf20Sopenharmony_ci	np->mii.reg_num_mask = 0x1f;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	ring_space = dma_alloc_coherent(&pdev->dev, RX_TOTAL_SIZE, &ring_dma,
5478c2ecf20Sopenharmony_ci					GFP_KERNEL);
5488c2ecf20Sopenharmony_ci	if (!ring_space) {
5498c2ecf20Sopenharmony_ci		err = -ENOMEM;
5508c2ecf20Sopenharmony_ci		goto err_out_free_dev;
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci	np->rx_ring = ring_space;
5538c2ecf20Sopenharmony_ci	np->rx_ring_dma = ring_dma;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	ring_space = dma_alloc_coherent(&pdev->dev, TX_TOTAL_SIZE, &ring_dma,
5568c2ecf20Sopenharmony_ci					GFP_KERNEL);
5578c2ecf20Sopenharmony_ci	if (!ring_space) {
5588c2ecf20Sopenharmony_ci		err = -ENOMEM;
5598c2ecf20Sopenharmony_ci		goto err_out_free_rx;
5608c2ecf20Sopenharmony_ci	}
5618c2ecf20Sopenharmony_ci	np->tx_ring = ring_space;
5628c2ecf20Sopenharmony_ci	np->tx_ring_dma = ring_dma;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	/* find the connected MII xcvrs */
5658c2ecf20Sopenharmony_ci	if (np->flags == HAS_MII_XCVR) {
5668c2ecf20Sopenharmony_ci		int phy, phy_idx = 0;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci		for (phy = 1; phy < 32 && phy_idx < ARRAY_SIZE(np->phys);
5698c2ecf20Sopenharmony_ci			       phy++) {
5708c2ecf20Sopenharmony_ci			int mii_status = mdio_read(dev, phy, 1);
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci			if (mii_status != 0xffff && mii_status != 0x0000) {
5738c2ecf20Sopenharmony_ci				np->phys[phy_idx++] = phy;
5748c2ecf20Sopenharmony_ci				dev_info(&pdev->dev,
5758c2ecf20Sopenharmony_ci				       "MII PHY found at address %d, status "
5768c2ecf20Sopenharmony_ci				       "0x%4.4x.\n", phy, mii_status);
5778c2ecf20Sopenharmony_ci				/* get phy type */
5788c2ecf20Sopenharmony_ci				{
5798c2ecf20Sopenharmony_ci					unsigned int data;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci					data = mdio_read(dev, np->phys[0], 2);
5828c2ecf20Sopenharmony_ci					if (data == SeeqPHYID0)
5838c2ecf20Sopenharmony_ci						np->PHYType = SeeqPHY;
5848c2ecf20Sopenharmony_ci					else if (data == AhdocPHYID0)
5858c2ecf20Sopenharmony_ci						np->PHYType = AhdocPHY;
5868c2ecf20Sopenharmony_ci					else if (data == MarvellPHYID0)
5878c2ecf20Sopenharmony_ci						np->PHYType = MarvellPHY;
5888c2ecf20Sopenharmony_ci					else if (data == MysonPHYID0)
5898c2ecf20Sopenharmony_ci						np->PHYType = Myson981;
5908c2ecf20Sopenharmony_ci					else if (data == LevelOnePHYID0)
5918c2ecf20Sopenharmony_ci						np->PHYType = LevelOnePHY;
5928c2ecf20Sopenharmony_ci					else
5938c2ecf20Sopenharmony_ci						np->PHYType = OtherPHY;
5948c2ecf20Sopenharmony_ci				}
5958c2ecf20Sopenharmony_ci			}
5968c2ecf20Sopenharmony_ci		}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci		np->mii_cnt = phy_idx;
5998c2ecf20Sopenharmony_ci		if (phy_idx == 0)
6008c2ecf20Sopenharmony_ci			dev_warn(&pdev->dev,
6018c2ecf20Sopenharmony_ci				"MII PHY not found -- this device may "
6028c2ecf20Sopenharmony_ci			       "not operate correctly.\n");
6038c2ecf20Sopenharmony_ci	} else {
6048c2ecf20Sopenharmony_ci		np->phys[0] = 32;
6058c2ecf20Sopenharmony_ci/* 89/6/23 add, (begin) */
6068c2ecf20Sopenharmony_ci		/* get phy type */
6078c2ecf20Sopenharmony_ci		if (ioread32(ioaddr + PHYIDENTIFIER) == MysonPHYID)
6088c2ecf20Sopenharmony_ci			np->PHYType = MysonPHY;
6098c2ecf20Sopenharmony_ci		else
6108c2ecf20Sopenharmony_ci			np->PHYType = OtherPHY;
6118c2ecf20Sopenharmony_ci	}
6128c2ecf20Sopenharmony_ci	np->mii.phy_id = np->phys[0];
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	if (dev->mem_start)
6158c2ecf20Sopenharmony_ci		option = dev->mem_start;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	/* The lower four bits are the media type. */
6188c2ecf20Sopenharmony_ci	if (option > 0) {
6198c2ecf20Sopenharmony_ci		if (option & 0x200)
6208c2ecf20Sopenharmony_ci			np->mii.full_duplex = 1;
6218c2ecf20Sopenharmony_ci		np->default_port = option & 15;
6228c2ecf20Sopenharmony_ci	}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	if (card_idx < MAX_UNITS && full_duplex[card_idx] > 0)
6258c2ecf20Sopenharmony_ci		np->mii.full_duplex = full_duplex[card_idx];
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	if (np->mii.full_duplex) {
6288c2ecf20Sopenharmony_ci		dev_info(&pdev->dev, "Media type forced to Full Duplex.\n");
6298c2ecf20Sopenharmony_ci/* 89/6/13 add, (begin) */
6308c2ecf20Sopenharmony_ci//      if (np->PHYType==MarvellPHY)
6318c2ecf20Sopenharmony_ci		if ((np->PHYType == MarvellPHY) || (np->PHYType == LevelOnePHY)) {
6328c2ecf20Sopenharmony_ci			unsigned int data;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci			data = mdio_read(dev, np->phys[0], 9);
6358c2ecf20Sopenharmony_ci			data = (data & 0xfcff) | 0x0200;
6368c2ecf20Sopenharmony_ci			mdio_write(dev, np->phys[0], 9, data);
6378c2ecf20Sopenharmony_ci		}
6388c2ecf20Sopenharmony_ci/* 89/6/13 add, (end) */
6398c2ecf20Sopenharmony_ci		if (np->flags == HAS_MII_XCVR)
6408c2ecf20Sopenharmony_ci			mdio_write(dev, np->phys[0], MII_ADVERTISE, ADVERTISE_FULL);
6418c2ecf20Sopenharmony_ci		else
6428c2ecf20Sopenharmony_ci			iowrite32(ADVERTISE_FULL, ioaddr + ANARANLPAR);
6438c2ecf20Sopenharmony_ci		np->mii.force_media = 1;
6448c2ecf20Sopenharmony_ci	}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	dev->netdev_ops = &netdev_ops;
6478c2ecf20Sopenharmony_ci	dev->ethtool_ops = &netdev_ethtool_ops;
6488c2ecf20Sopenharmony_ci	dev->watchdog_timeo = TX_TIMEOUT;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	err = register_netdev(dev);
6518c2ecf20Sopenharmony_ci	if (err)
6528c2ecf20Sopenharmony_ci		goto err_out_free_tx;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	printk(KERN_INFO "%s: %s at %p, %pM, IRQ %d.\n",
6558c2ecf20Sopenharmony_ci	       dev->name, skel_netdrv_tbl[chip_id].chip_name, ioaddr,
6568c2ecf20Sopenharmony_ci	       dev->dev_addr, irq);
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	return 0;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_cierr_out_free_tx:
6618c2ecf20Sopenharmony_ci	dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, np->tx_ring,
6628c2ecf20Sopenharmony_ci			  np->tx_ring_dma);
6638c2ecf20Sopenharmony_cierr_out_free_rx:
6648c2ecf20Sopenharmony_ci	dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, np->rx_ring,
6658c2ecf20Sopenharmony_ci			  np->rx_ring_dma);
6668c2ecf20Sopenharmony_cierr_out_free_dev:
6678c2ecf20Sopenharmony_ci	free_netdev(dev);
6688c2ecf20Sopenharmony_cierr_out_unmap:
6698c2ecf20Sopenharmony_ci	pci_iounmap(pdev, ioaddr);
6708c2ecf20Sopenharmony_cierr_out_res:
6718c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
6728c2ecf20Sopenharmony_ci	return err;
6738c2ecf20Sopenharmony_ci}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_cistatic void fealnx_remove_one(struct pci_dev *pdev)
6778c2ecf20Sopenharmony_ci{
6788c2ecf20Sopenharmony_ci	struct net_device *dev = pci_get_drvdata(pdev);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	if (dev) {
6818c2ecf20Sopenharmony_ci		struct netdev_private *np = netdev_priv(dev);
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci		dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, np->tx_ring,
6848c2ecf20Sopenharmony_ci				  np->tx_ring_dma);
6858c2ecf20Sopenharmony_ci		dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, np->rx_ring,
6868c2ecf20Sopenharmony_ci				  np->rx_ring_dma);
6878c2ecf20Sopenharmony_ci		unregister_netdev(dev);
6888c2ecf20Sopenharmony_ci		pci_iounmap(pdev, np->mem);
6898c2ecf20Sopenharmony_ci		free_netdev(dev);
6908c2ecf20Sopenharmony_ci		pci_release_regions(pdev);
6918c2ecf20Sopenharmony_ci	} else
6928c2ecf20Sopenharmony_ci		printk(KERN_ERR "fealnx: remove for unknown device\n");
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_cistatic ulong m80x_send_cmd_to_phy(void __iomem *miiport, int opcode, int phyad, int regad)
6978c2ecf20Sopenharmony_ci{
6988c2ecf20Sopenharmony_ci	ulong miir;
6998c2ecf20Sopenharmony_ci	int i;
7008c2ecf20Sopenharmony_ci	unsigned int mask, data;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	/* enable MII output */
7038c2ecf20Sopenharmony_ci	miir = (ulong) ioread32(miiport);
7048c2ecf20Sopenharmony_ci	miir &= 0xfffffff0;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	miir |= MASK_MIIR_MII_WRITE + MASK_MIIR_MII_MDO;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	/* send 32 1's preamble */
7098c2ecf20Sopenharmony_ci	for (i = 0; i < 32; i++) {
7108c2ecf20Sopenharmony_ci		/* low MDC; MDO is already high (miir) */
7118c2ecf20Sopenharmony_ci		miir &= ~MASK_MIIR_MII_MDC;
7128c2ecf20Sopenharmony_ci		iowrite32(miir, miiport);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci		/* high MDC */
7158c2ecf20Sopenharmony_ci		miir |= MASK_MIIR_MII_MDC;
7168c2ecf20Sopenharmony_ci		iowrite32(miir, miiport);
7178c2ecf20Sopenharmony_ci	}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	/* calculate ST+OP+PHYAD+REGAD+TA */
7208c2ecf20Sopenharmony_ci	data = opcode | (phyad << 7) | (regad << 2);
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	/* sent out */
7238c2ecf20Sopenharmony_ci	mask = 0x8000;
7248c2ecf20Sopenharmony_ci	while (mask) {
7258c2ecf20Sopenharmony_ci		/* low MDC, prepare MDO */
7268c2ecf20Sopenharmony_ci		miir &= ~(MASK_MIIR_MII_MDC + MASK_MIIR_MII_MDO);
7278c2ecf20Sopenharmony_ci		if (mask & data)
7288c2ecf20Sopenharmony_ci			miir |= MASK_MIIR_MII_MDO;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci		iowrite32(miir, miiport);
7318c2ecf20Sopenharmony_ci		/* high MDC */
7328c2ecf20Sopenharmony_ci		miir |= MASK_MIIR_MII_MDC;
7338c2ecf20Sopenharmony_ci		iowrite32(miir, miiport);
7348c2ecf20Sopenharmony_ci		udelay(30);
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci		/* next */
7378c2ecf20Sopenharmony_ci		mask >>= 1;
7388c2ecf20Sopenharmony_ci		if (mask == 0x2 && opcode == OP_READ)
7398c2ecf20Sopenharmony_ci			miir &= ~MASK_MIIR_MII_WRITE;
7408c2ecf20Sopenharmony_ci	}
7418c2ecf20Sopenharmony_ci	return miir;
7428c2ecf20Sopenharmony_ci}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_cistatic int mdio_read(struct net_device *dev, int phyad, int regad)
7468c2ecf20Sopenharmony_ci{
7478c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
7488c2ecf20Sopenharmony_ci	void __iomem *miiport = np->mem + MANAGEMENT;
7498c2ecf20Sopenharmony_ci	ulong miir;
7508c2ecf20Sopenharmony_ci	unsigned int mask, data;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	miir = m80x_send_cmd_to_phy(miiport, OP_READ, phyad, regad);
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	/* read data */
7558c2ecf20Sopenharmony_ci	mask = 0x8000;
7568c2ecf20Sopenharmony_ci	data = 0;
7578c2ecf20Sopenharmony_ci	while (mask) {
7588c2ecf20Sopenharmony_ci		/* low MDC */
7598c2ecf20Sopenharmony_ci		miir &= ~MASK_MIIR_MII_MDC;
7608c2ecf20Sopenharmony_ci		iowrite32(miir, miiport);
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci		/* read MDI */
7638c2ecf20Sopenharmony_ci		miir = ioread32(miiport);
7648c2ecf20Sopenharmony_ci		if (miir & MASK_MIIR_MII_MDI)
7658c2ecf20Sopenharmony_ci			data |= mask;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci		/* high MDC, and wait */
7688c2ecf20Sopenharmony_ci		miir |= MASK_MIIR_MII_MDC;
7698c2ecf20Sopenharmony_ci		iowrite32(miir, miiport);
7708c2ecf20Sopenharmony_ci		udelay(30);
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci		/* next */
7738c2ecf20Sopenharmony_ci		mask >>= 1;
7748c2ecf20Sopenharmony_ci	}
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	/* low MDC */
7778c2ecf20Sopenharmony_ci	miir &= ~MASK_MIIR_MII_MDC;
7788c2ecf20Sopenharmony_ci	iowrite32(miir, miiport);
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	return data & 0xffff;
7818c2ecf20Sopenharmony_ci}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_cistatic void mdio_write(struct net_device *dev, int phyad, int regad, int data)
7858c2ecf20Sopenharmony_ci{
7868c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
7878c2ecf20Sopenharmony_ci	void __iomem *miiport = np->mem + MANAGEMENT;
7888c2ecf20Sopenharmony_ci	ulong miir;
7898c2ecf20Sopenharmony_ci	unsigned int mask;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	miir = m80x_send_cmd_to_phy(miiport, OP_WRITE, phyad, regad);
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	/* write data */
7948c2ecf20Sopenharmony_ci	mask = 0x8000;
7958c2ecf20Sopenharmony_ci	while (mask) {
7968c2ecf20Sopenharmony_ci		/* low MDC, prepare MDO */
7978c2ecf20Sopenharmony_ci		miir &= ~(MASK_MIIR_MII_MDC + MASK_MIIR_MII_MDO);
7988c2ecf20Sopenharmony_ci		if (mask & data)
7998c2ecf20Sopenharmony_ci			miir |= MASK_MIIR_MII_MDO;
8008c2ecf20Sopenharmony_ci		iowrite32(miir, miiport);
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci		/* high MDC */
8038c2ecf20Sopenharmony_ci		miir |= MASK_MIIR_MII_MDC;
8048c2ecf20Sopenharmony_ci		iowrite32(miir, miiport);
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci		/* next */
8078c2ecf20Sopenharmony_ci		mask >>= 1;
8088c2ecf20Sopenharmony_ci	}
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	/* low MDC */
8118c2ecf20Sopenharmony_ci	miir &= ~MASK_MIIR_MII_MDC;
8128c2ecf20Sopenharmony_ci	iowrite32(miir, miiport);
8138c2ecf20Sopenharmony_ci}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_cistatic int netdev_open(struct net_device *dev)
8178c2ecf20Sopenharmony_ci{
8188c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
8198c2ecf20Sopenharmony_ci	void __iomem *ioaddr = np->mem;
8208c2ecf20Sopenharmony_ci	const int irq = np->pci_dev->irq;
8218c2ecf20Sopenharmony_ci	int rc, i;
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	iowrite32(0x00000001, ioaddr + BCR);	/* Reset */
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	rc = request_irq(irq, intr_handler, IRQF_SHARED, dev->name, dev);
8268c2ecf20Sopenharmony_ci	if (rc)
8278c2ecf20Sopenharmony_ci		return -EAGAIN;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++)
8308c2ecf20Sopenharmony_ci		iowrite16(((unsigned short*)dev->dev_addr)[i],
8318c2ecf20Sopenharmony_ci				ioaddr + PAR0 + i*2);
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	init_ring(dev);
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	iowrite32(np->rx_ring_dma, ioaddr + RXLBA);
8368c2ecf20Sopenharmony_ci	iowrite32(np->tx_ring_dma, ioaddr + TXLBA);
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	/* Initialize other registers. */
8398c2ecf20Sopenharmony_ci	/* Configure the PCI bus bursts and FIFO thresholds.
8408c2ecf20Sopenharmony_ci	   486: Set 8 longword burst.
8418c2ecf20Sopenharmony_ci	   586: no burst limit.
8428c2ecf20Sopenharmony_ci	   Burst length 5:3
8438c2ecf20Sopenharmony_ci	   0 0 0   1
8448c2ecf20Sopenharmony_ci	   0 0 1   4
8458c2ecf20Sopenharmony_ci	   0 1 0   8
8468c2ecf20Sopenharmony_ci	   0 1 1   16
8478c2ecf20Sopenharmony_ci	   1 0 0   32
8488c2ecf20Sopenharmony_ci	   1 0 1   64
8498c2ecf20Sopenharmony_ci	   1 1 0   128
8508c2ecf20Sopenharmony_ci	   1 1 1   256
8518c2ecf20Sopenharmony_ci	   Wait the specified 50 PCI cycles after a reset by initializing
8528c2ecf20Sopenharmony_ci	   Tx and Rx queues and the address filter list.
8538c2ecf20Sopenharmony_ci	   FIXME (Ueimor): optimistic for alpha + posted writes ? */
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	np->bcrvalue = 0x10;	/* little-endian, 8 burst length */
8568c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN
8578c2ecf20Sopenharmony_ci	np->bcrvalue |= 0x04;	/* big-endian */
8588c2ecf20Sopenharmony_ci#endif
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci#if defined(__i386__) && !defined(MODULE)
8618c2ecf20Sopenharmony_ci	if (boot_cpu_data.x86 <= 4)
8628c2ecf20Sopenharmony_ci		np->crvalue = 0xa00;
8638c2ecf20Sopenharmony_ci	else
8648c2ecf20Sopenharmony_ci#endif
8658c2ecf20Sopenharmony_ci		np->crvalue = 0xe00;	/* rx 128 burst length */
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci// 89/12/29 add,
8698c2ecf20Sopenharmony_ci// 90/1/16 modify,
8708c2ecf20Sopenharmony_ci//   np->imrvalue=FBE|TUNF|CNTOVF|RBU|TI|RI;
8718c2ecf20Sopenharmony_ci	np->imrvalue = TUNF | CNTOVF | RBU | TI | RI;
8728c2ecf20Sopenharmony_ci	if (np->pci_dev->device == 0x891) {
8738c2ecf20Sopenharmony_ci		np->bcrvalue |= 0x200;	/* set PROG bit */
8748c2ecf20Sopenharmony_ci		np->crvalue |= CR_W_ENH;	/* set enhanced bit */
8758c2ecf20Sopenharmony_ci		np->imrvalue |= ETI;
8768c2ecf20Sopenharmony_ci	}
8778c2ecf20Sopenharmony_ci	iowrite32(np->bcrvalue, ioaddr + BCR);
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	if (dev->if_port == 0)
8808c2ecf20Sopenharmony_ci		dev->if_port = np->default_port;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	iowrite32(0, ioaddr + RXPDR);
8838c2ecf20Sopenharmony_ci// 89/9/1 modify,
8848c2ecf20Sopenharmony_ci//   np->crvalue = 0x00e40001;    /* tx store and forward, tx/rx enable */
8858c2ecf20Sopenharmony_ci	np->crvalue |= 0x00e40001;	/* tx store and forward, tx/rx enable */
8868c2ecf20Sopenharmony_ci	np->mii.full_duplex = np->mii.force_media;
8878c2ecf20Sopenharmony_ci	getlinkstatus(dev);
8888c2ecf20Sopenharmony_ci	if (np->linkok)
8898c2ecf20Sopenharmony_ci		getlinktype(dev);
8908c2ecf20Sopenharmony_ci	__set_rx_mode(dev);
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	netif_start_queue(dev);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	/* Clear and Enable interrupts by setting the interrupt mask. */
8958c2ecf20Sopenharmony_ci	iowrite32(FBE | TUNF | CNTOVF | RBU | TI | RI, ioaddr + ISR);
8968c2ecf20Sopenharmony_ci	iowrite32(np->imrvalue, ioaddr + IMR);
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	if (debug)
8998c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: Done netdev_open().\n", dev->name);
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	/* Set the timer to check for link beat. */
9028c2ecf20Sopenharmony_ci	timer_setup(&np->timer, netdev_timer, 0);
9038c2ecf20Sopenharmony_ci	np->timer.expires = RUN_AT(3 * HZ);
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	/* timer handler */
9068c2ecf20Sopenharmony_ci	add_timer(&np->timer);
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	timer_setup(&np->reset_timer, reset_timer, 0);
9098c2ecf20Sopenharmony_ci	np->reset_timer_armed = 0;
9108c2ecf20Sopenharmony_ci	return rc;
9118c2ecf20Sopenharmony_ci}
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_cistatic void getlinkstatus(struct net_device *dev)
9158c2ecf20Sopenharmony_ci/* function: Routine will read MII Status Register to get link status.       */
9168c2ecf20Sopenharmony_ci/* input   : dev... pointer to the adapter block.                            */
9178c2ecf20Sopenharmony_ci/* output  : none.                                                           */
9188c2ecf20Sopenharmony_ci{
9198c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
9208c2ecf20Sopenharmony_ci	unsigned int i, DelayTime = 0x1000;
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	np->linkok = 0;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	if (np->PHYType == MysonPHY) {
9258c2ecf20Sopenharmony_ci		for (i = 0; i < DelayTime; ++i) {
9268c2ecf20Sopenharmony_ci			if (ioread32(np->mem + BMCRSR) & LinkIsUp2) {
9278c2ecf20Sopenharmony_ci				np->linkok = 1;
9288c2ecf20Sopenharmony_ci				return;
9298c2ecf20Sopenharmony_ci			}
9308c2ecf20Sopenharmony_ci			udelay(100);
9318c2ecf20Sopenharmony_ci		}
9328c2ecf20Sopenharmony_ci	} else {
9338c2ecf20Sopenharmony_ci		for (i = 0; i < DelayTime; ++i) {
9348c2ecf20Sopenharmony_ci			if (mdio_read(dev, np->phys[0], MII_BMSR) & BMSR_LSTATUS) {
9358c2ecf20Sopenharmony_ci				np->linkok = 1;
9368c2ecf20Sopenharmony_ci				return;
9378c2ecf20Sopenharmony_ci			}
9388c2ecf20Sopenharmony_ci			udelay(100);
9398c2ecf20Sopenharmony_ci		}
9408c2ecf20Sopenharmony_ci	}
9418c2ecf20Sopenharmony_ci}
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_cistatic void getlinktype(struct net_device *dev)
9458c2ecf20Sopenharmony_ci{
9468c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	if (np->PHYType == MysonPHY) {	/* 3-in-1 case */
9498c2ecf20Sopenharmony_ci		if (ioread32(np->mem + TCRRCR) & CR_R_FD)
9508c2ecf20Sopenharmony_ci			np->duplexmode = 2;	/* full duplex */
9518c2ecf20Sopenharmony_ci		else
9528c2ecf20Sopenharmony_ci			np->duplexmode = 1;	/* half duplex */
9538c2ecf20Sopenharmony_ci		if (ioread32(np->mem + TCRRCR) & CR_R_PS10)
9548c2ecf20Sopenharmony_ci			np->line_speed = 1;	/* 10M */
9558c2ecf20Sopenharmony_ci		else
9568c2ecf20Sopenharmony_ci			np->line_speed = 2;	/* 100M */
9578c2ecf20Sopenharmony_ci	} else {
9588c2ecf20Sopenharmony_ci		if (np->PHYType == SeeqPHY) {	/* this PHY is SEEQ 80225 */
9598c2ecf20Sopenharmony_ci			unsigned int data;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci			data = mdio_read(dev, np->phys[0], MIIRegister18);
9628c2ecf20Sopenharmony_ci			if (data & SPD_DET_100)
9638c2ecf20Sopenharmony_ci				np->line_speed = 2;	/* 100M */
9648c2ecf20Sopenharmony_ci			else
9658c2ecf20Sopenharmony_ci				np->line_speed = 1;	/* 10M */
9668c2ecf20Sopenharmony_ci			if (data & DPLX_DET_FULL)
9678c2ecf20Sopenharmony_ci				np->duplexmode = 2;	/* full duplex mode */
9688c2ecf20Sopenharmony_ci			else
9698c2ecf20Sopenharmony_ci				np->duplexmode = 1;	/* half duplex mode */
9708c2ecf20Sopenharmony_ci		} else if (np->PHYType == AhdocPHY) {
9718c2ecf20Sopenharmony_ci			unsigned int data;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci			data = mdio_read(dev, np->phys[0], DiagnosticReg);
9748c2ecf20Sopenharmony_ci			if (data & Speed_100)
9758c2ecf20Sopenharmony_ci				np->line_speed = 2;	/* 100M */
9768c2ecf20Sopenharmony_ci			else
9778c2ecf20Sopenharmony_ci				np->line_speed = 1;	/* 10M */
9788c2ecf20Sopenharmony_ci			if (data & DPLX_FULL)
9798c2ecf20Sopenharmony_ci				np->duplexmode = 2;	/* full duplex mode */
9808c2ecf20Sopenharmony_ci			else
9818c2ecf20Sopenharmony_ci				np->duplexmode = 1;	/* half duplex mode */
9828c2ecf20Sopenharmony_ci		}
9838c2ecf20Sopenharmony_ci/* 89/6/13 add, (begin) */
9848c2ecf20Sopenharmony_ci		else if (np->PHYType == MarvellPHY) {
9858c2ecf20Sopenharmony_ci			unsigned int data;
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci			data = mdio_read(dev, np->phys[0], SpecificReg);
9888c2ecf20Sopenharmony_ci			if (data & Full_Duplex)
9898c2ecf20Sopenharmony_ci				np->duplexmode = 2;	/* full duplex mode */
9908c2ecf20Sopenharmony_ci			else
9918c2ecf20Sopenharmony_ci				np->duplexmode = 1;	/* half duplex mode */
9928c2ecf20Sopenharmony_ci			data &= SpeedMask;
9938c2ecf20Sopenharmony_ci			if (data == Speed_1000M)
9948c2ecf20Sopenharmony_ci				np->line_speed = 3;	/* 1000M */
9958c2ecf20Sopenharmony_ci			else if (data == Speed_100M)
9968c2ecf20Sopenharmony_ci				np->line_speed = 2;	/* 100M */
9978c2ecf20Sopenharmony_ci			else
9988c2ecf20Sopenharmony_ci				np->line_speed = 1;	/* 10M */
9998c2ecf20Sopenharmony_ci		}
10008c2ecf20Sopenharmony_ci/* 89/6/13 add, (end) */
10018c2ecf20Sopenharmony_ci/* 89/7/27 add, (begin) */
10028c2ecf20Sopenharmony_ci		else if (np->PHYType == Myson981) {
10038c2ecf20Sopenharmony_ci			unsigned int data;
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci			data = mdio_read(dev, np->phys[0], StatusRegister);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci			if (data & SPEED100)
10088c2ecf20Sopenharmony_ci				np->line_speed = 2;
10098c2ecf20Sopenharmony_ci			else
10108c2ecf20Sopenharmony_ci				np->line_speed = 1;
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci			if (data & FULLMODE)
10138c2ecf20Sopenharmony_ci				np->duplexmode = 2;
10148c2ecf20Sopenharmony_ci			else
10158c2ecf20Sopenharmony_ci				np->duplexmode = 1;
10168c2ecf20Sopenharmony_ci		}
10178c2ecf20Sopenharmony_ci/* 89/7/27 add, (end) */
10188c2ecf20Sopenharmony_ci/* 89/12/29 add */
10198c2ecf20Sopenharmony_ci		else if (np->PHYType == LevelOnePHY) {
10208c2ecf20Sopenharmony_ci			unsigned int data;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci			data = mdio_read(dev, np->phys[0], SpecificReg);
10238c2ecf20Sopenharmony_ci			if (data & LXT1000_Full)
10248c2ecf20Sopenharmony_ci				np->duplexmode = 2;	/* full duplex mode */
10258c2ecf20Sopenharmony_ci			else
10268c2ecf20Sopenharmony_ci				np->duplexmode = 1;	/* half duplex mode */
10278c2ecf20Sopenharmony_ci			data &= SpeedMask;
10288c2ecf20Sopenharmony_ci			if (data == LXT1000_1000M)
10298c2ecf20Sopenharmony_ci				np->line_speed = 3;	/* 1000M */
10308c2ecf20Sopenharmony_ci			else if (data == LXT1000_100M)
10318c2ecf20Sopenharmony_ci				np->line_speed = 2;	/* 100M */
10328c2ecf20Sopenharmony_ci			else
10338c2ecf20Sopenharmony_ci				np->line_speed = 1;	/* 10M */
10348c2ecf20Sopenharmony_ci		}
10358c2ecf20Sopenharmony_ci		np->crvalue &= (~CR_W_PS10) & (~CR_W_FD) & (~CR_W_PS1000);
10368c2ecf20Sopenharmony_ci		if (np->line_speed == 1)
10378c2ecf20Sopenharmony_ci			np->crvalue |= CR_W_PS10;
10388c2ecf20Sopenharmony_ci		else if (np->line_speed == 3)
10398c2ecf20Sopenharmony_ci			np->crvalue |= CR_W_PS1000;
10408c2ecf20Sopenharmony_ci		if (np->duplexmode == 2)
10418c2ecf20Sopenharmony_ci			np->crvalue |= CR_W_FD;
10428c2ecf20Sopenharmony_ci	}
10438c2ecf20Sopenharmony_ci}
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci/* Take lock before calling this */
10478c2ecf20Sopenharmony_cistatic void allocate_rx_buffers(struct net_device *dev)
10488c2ecf20Sopenharmony_ci{
10498c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	/*  allocate skb for rx buffers */
10528c2ecf20Sopenharmony_ci	while (np->really_rx_count != RX_RING_SIZE) {
10538c2ecf20Sopenharmony_ci		struct sk_buff *skb;
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci		skb = netdev_alloc_skb(dev, np->rx_buf_sz);
10568c2ecf20Sopenharmony_ci		if (skb == NULL)
10578c2ecf20Sopenharmony_ci			break;	/* Better luck next round. */
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci		while (np->lack_rxbuf->skbuff)
10608c2ecf20Sopenharmony_ci			np->lack_rxbuf = np->lack_rxbuf->next_desc_logical;
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci		np->lack_rxbuf->skbuff = skb;
10638c2ecf20Sopenharmony_ci		np->lack_rxbuf->buffer = dma_map_single(&np->pci_dev->dev,
10648c2ecf20Sopenharmony_ci							skb->data,
10658c2ecf20Sopenharmony_ci							np->rx_buf_sz,
10668c2ecf20Sopenharmony_ci							DMA_FROM_DEVICE);
10678c2ecf20Sopenharmony_ci		np->lack_rxbuf->status = RXOWN;
10688c2ecf20Sopenharmony_ci		++np->really_rx_count;
10698c2ecf20Sopenharmony_ci	}
10708c2ecf20Sopenharmony_ci}
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_cistatic void netdev_timer(struct timer_list *t)
10748c2ecf20Sopenharmony_ci{
10758c2ecf20Sopenharmony_ci	struct netdev_private *np = from_timer(np, t, timer);
10768c2ecf20Sopenharmony_ci	struct net_device *dev = np->mii.dev;
10778c2ecf20Sopenharmony_ci	void __iomem *ioaddr = np->mem;
10788c2ecf20Sopenharmony_ci	int old_crvalue = np->crvalue;
10798c2ecf20Sopenharmony_ci	unsigned int old_linkok = np->linkok;
10808c2ecf20Sopenharmony_ci	unsigned long flags;
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	if (debug)
10838c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: Media selection timer tick, status %8.8x "
10848c2ecf20Sopenharmony_ci		       "config %8.8x.\n", dev->name, ioread32(ioaddr + ISR),
10858c2ecf20Sopenharmony_ci		       ioread32(ioaddr + TCRRCR));
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	spin_lock_irqsave(&np->lock, flags);
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	if (np->flags == HAS_MII_XCVR) {
10908c2ecf20Sopenharmony_ci		getlinkstatus(dev);
10918c2ecf20Sopenharmony_ci		if ((old_linkok == 0) && (np->linkok == 1)) {	/* we need to detect the media type again */
10928c2ecf20Sopenharmony_ci			getlinktype(dev);
10938c2ecf20Sopenharmony_ci			if (np->crvalue != old_crvalue) {
10948c2ecf20Sopenharmony_ci				stop_nic_rxtx(ioaddr, np->crvalue);
10958c2ecf20Sopenharmony_ci				iowrite32(np->crvalue, ioaddr + TCRRCR);
10968c2ecf20Sopenharmony_ci			}
10978c2ecf20Sopenharmony_ci		}
10988c2ecf20Sopenharmony_ci	}
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	allocate_rx_buffers(dev);
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&np->lock, flags);
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	np->timer.expires = RUN_AT(10 * HZ);
11058c2ecf20Sopenharmony_ci	add_timer(&np->timer);
11068c2ecf20Sopenharmony_ci}
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci/* Take lock before calling */
11108c2ecf20Sopenharmony_ci/* Reset chip and disable rx, tx and interrupts */
11118c2ecf20Sopenharmony_cistatic void reset_and_disable_rxtx(struct net_device *dev)
11128c2ecf20Sopenharmony_ci{
11138c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
11148c2ecf20Sopenharmony_ci	void __iomem *ioaddr = np->mem;
11158c2ecf20Sopenharmony_ci	int delay=51;
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	/* Reset the chip's Tx and Rx processes. */
11188c2ecf20Sopenharmony_ci	stop_nic_rxtx(ioaddr, 0);
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	/* Disable interrupts by clearing the interrupt mask. */
11218c2ecf20Sopenharmony_ci	iowrite32(0, ioaddr + IMR);
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci	/* Reset the chip to erase previous misconfiguration. */
11248c2ecf20Sopenharmony_ci	iowrite32(0x00000001, ioaddr + BCR);
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	/* Ueimor: wait for 50 PCI cycles (and flush posted writes btw).
11278c2ecf20Sopenharmony_ci	   We surely wait too long (address+data phase). Who cares? */
11288c2ecf20Sopenharmony_ci	while (--delay) {
11298c2ecf20Sopenharmony_ci		ioread32(ioaddr + BCR);
11308c2ecf20Sopenharmony_ci		rmb();
11318c2ecf20Sopenharmony_ci	}
11328c2ecf20Sopenharmony_ci}
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci/* Take lock before calling */
11368c2ecf20Sopenharmony_ci/* Restore chip after reset */
11378c2ecf20Sopenharmony_cistatic void enable_rxtx(struct net_device *dev)
11388c2ecf20Sopenharmony_ci{
11398c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
11408c2ecf20Sopenharmony_ci	void __iomem *ioaddr = np->mem;
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	reset_rx_descriptors(dev);
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	iowrite32(np->tx_ring_dma + ((char*)np->cur_tx - (char*)np->tx_ring),
11458c2ecf20Sopenharmony_ci		ioaddr + TXLBA);
11468c2ecf20Sopenharmony_ci	iowrite32(np->rx_ring_dma + ((char*)np->cur_rx - (char*)np->rx_ring),
11478c2ecf20Sopenharmony_ci		ioaddr + RXLBA);
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci	iowrite32(np->bcrvalue, ioaddr + BCR);
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	iowrite32(0, ioaddr + RXPDR);
11528c2ecf20Sopenharmony_ci	__set_rx_mode(dev); /* changes np->crvalue, writes it into TCRRCR */
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	/* Clear and Enable interrupts by setting the interrupt mask. */
11558c2ecf20Sopenharmony_ci	iowrite32(FBE | TUNF | CNTOVF | RBU | TI | RI, ioaddr + ISR);
11568c2ecf20Sopenharmony_ci	iowrite32(np->imrvalue, ioaddr + IMR);
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci	iowrite32(0, ioaddr + TXPDR);
11598c2ecf20Sopenharmony_ci}
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_cistatic void reset_timer(struct timer_list *t)
11638c2ecf20Sopenharmony_ci{
11648c2ecf20Sopenharmony_ci	struct netdev_private *np = from_timer(np, t, reset_timer);
11658c2ecf20Sopenharmony_ci	struct net_device *dev = np->mii.dev;
11668c2ecf20Sopenharmony_ci	unsigned long flags;
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	printk(KERN_WARNING "%s: resetting tx and rx machinery\n", dev->name);
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	spin_lock_irqsave(&np->lock, flags);
11718c2ecf20Sopenharmony_ci	np->crvalue = np->crvalue_sv;
11728c2ecf20Sopenharmony_ci	np->imrvalue = np->imrvalue_sv;
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	reset_and_disable_rxtx(dev);
11758c2ecf20Sopenharmony_ci	/* works for me without this:
11768c2ecf20Sopenharmony_ci	reset_tx_descriptors(dev); */
11778c2ecf20Sopenharmony_ci	enable_rxtx(dev);
11788c2ecf20Sopenharmony_ci	netif_start_queue(dev); /* FIXME: or netif_wake_queue(dev); ? */
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	np->reset_timer_armed = 0;
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&np->lock, flags);
11838c2ecf20Sopenharmony_ci}
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_cistatic void fealnx_tx_timeout(struct net_device *dev, unsigned int txqueue)
11878c2ecf20Sopenharmony_ci{
11888c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
11898c2ecf20Sopenharmony_ci	void __iomem *ioaddr = np->mem;
11908c2ecf20Sopenharmony_ci	unsigned long flags;
11918c2ecf20Sopenharmony_ci	int i;
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	printk(KERN_WARNING
11948c2ecf20Sopenharmony_ci	       "%s: Transmit timed out, status %8.8x, resetting...\n",
11958c2ecf20Sopenharmony_ci	       dev->name, ioread32(ioaddr + ISR));
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci	{
11988c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "  Rx ring %p: ", np->rx_ring);
11998c2ecf20Sopenharmony_ci		for (i = 0; i < RX_RING_SIZE; i++)
12008c2ecf20Sopenharmony_ci			printk(KERN_CONT " %8.8x",
12018c2ecf20Sopenharmony_ci			       (unsigned int) np->rx_ring[i].status);
12028c2ecf20Sopenharmony_ci		printk(KERN_CONT "\n");
12038c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "  Tx ring %p: ", np->tx_ring);
12048c2ecf20Sopenharmony_ci		for (i = 0; i < TX_RING_SIZE; i++)
12058c2ecf20Sopenharmony_ci			printk(KERN_CONT " %4.4x", np->tx_ring[i].status);
12068c2ecf20Sopenharmony_ci		printk(KERN_CONT "\n");
12078c2ecf20Sopenharmony_ci	}
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	spin_lock_irqsave(&np->lock, flags);
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	reset_and_disable_rxtx(dev);
12128c2ecf20Sopenharmony_ci	reset_tx_descriptors(dev);
12138c2ecf20Sopenharmony_ci	enable_rxtx(dev);
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&np->lock, flags);
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci	netif_trans_update(dev); /* prevent tx timeout */
12188c2ecf20Sopenharmony_ci	dev->stats.tx_errors++;
12198c2ecf20Sopenharmony_ci	netif_wake_queue(dev); /* or .._start_.. ?? */
12208c2ecf20Sopenharmony_ci}
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
12248c2ecf20Sopenharmony_cistatic void init_ring(struct net_device *dev)
12258c2ecf20Sopenharmony_ci{
12268c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
12278c2ecf20Sopenharmony_ci	int i;
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	/* initialize rx variables */
12308c2ecf20Sopenharmony_ci	np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
12318c2ecf20Sopenharmony_ci	np->cur_rx = &np->rx_ring[0];
12328c2ecf20Sopenharmony_ci	np->lack_rxbuf = np->rx_ring;
12338c2ecf20Sopenharmony_ci	np->really_rx_count = 0;
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	/* initial rx descriptors. */
12368c2ecf20Sopenharmony_ci	for (i = 0; i < RX_RING_SIZE; i++) {
12378c2ecf20Sopenharmony_ci		np->rx_ring[i].status = 0;
12388c2ecf20Sopenharmony_ci		np->rx_ring[i].control = np->rx_buf_sz << RBSShift;
12398c2ecf20Sopenharmony_ci		np->rx_ring[i].next_desc = np->rx_ring_dma +
12408c2ecf20Sopenharmony_ci			(i + 1)*sizeof(struct fealnx_desc);
12418c2ecf20Sopenharmony_ci		np->rx_ring[i].next_desc_logical = &np->rx_ring[i + 1];
12428c2ecf20Sopenharmony_ci		np->rx_ring[i].skbuff = NULL;
12438c2ecf20Sopenharmony_ci	}
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	/* for the last rx descriptor */
12468c2ecf20Sopenharmony_ci	np->rx_ring[i - 1].next_desc = np->rx_ring_dma;
12478c2ecf20Sopenharmony_ci	np->rx_ring[i - 1].next_desc_logical = np->rx_ring;
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	/* allocate skb for rx buffers */
12508c2ecf20Sopenharmony_ci	for (i = 0; i < RX_RING_SIZE; i++) {
12518c2ecf20Sopenharmony_ci		struct sk_buff *skb = netdev_alloc_skb(dev, np->rx_buf_sz);
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci		if (skb == NULL) {
12548c2ecf20Sopenharmony_ci			np->lack_rxbuf = &np->rx_ring[i];
12558c2ecf20Sopenharmony_ci			break;
12568c2ecf20Sopenharmony_ci		}
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci		++np->really_rx_count;
12598c2ecf20Sopenharmony_ci		np->rx_ring[i].skbuff = skb;
12608c2ecf20Sopenharmony_ci		np->rx_ring[i].buffer = dma_map_single(&np->pci_dev->dev,
12618c2ecf20Sopenharmony_ci						       skb->data,
12628c2ecf20Sopenharmony_ci						       np->rx_buf_sz,
12638c2ecf20Sopenharmony_ci						       DMA_FROM_DEVICE);
12648c2ecf20Sopenharmony_ci		np->rx_ring[i].status = RXOWN;
12658c2ecf20Sopenharmony_ci		np->rx_ring[i].control |= RXIC;
12668c2ecf20Sopenharmony_ci	}
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_ci	/* initialize tx variables */
12698c2ecf20Sopenharmony_ci	np->cur_tx = &np->tx_ring[0];
12708c2ecf20Sopenharmony_ci	np->cur_tx_copy = &np->tx_ring[0];
12718c2ecf20Sopenharmony_ci	np->really_tx_count = 0;
12728c2ecf20Sopenharmony_ci	np->free_tx_count = TX_RING_SIZE;
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci	for (i = 0; i < TX_RING_SIZE; i++) {
12758c2ecf20Sopenharmony_ci		np->tx_ring[i].status = 0;
12768c2ecf20Sopenharmony_ci		/* do we need np->tx_ring[i].control = XXX; ?? */
12778c2ecf20Sopenharmony_ci		np->tx_ring[i].next_desc = np->tx_ring_dma +
12788c2ecf20Sopenharmony_ci			(i + 1)*sizeof(struct fealnx_desc);
12798c2ecf20Sopenharmony_ci		np->tx_ring[i].next_desc_logical = &np->tx_ring[i + 1];
12808c2ecf20Sopenharmony_ci		np->tx_ring[i].skbuff = NULL;
12818c2ecf20Sopenharmony_ci	}
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	/* for the last tx descriptor */
12848c2ecf20Sopenharmony_ci	np->tx_ring[i - 1].next_desc = np->tx_ring_dma;
12858c2ecf20Sopenharmony_ci	np->tx_ring[i - 1].next_desc_logical = &np->tx_ring[0];
12868c2ecf20Sopenharmony_ci}
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_cistatic netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev)
12908c2ecf20Sopenharmony_ci{
12918c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
12928c2ecf20Sopenharmony_ci	unsigned long flags;
12938c2ecf20Sopenharmony_ci
12948c2ecf20Sopenharmony_ci	spin_lock_irqsave(&np->lock, flags);
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci	np->cur_tx_copy->skbuff = skb;
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci#define one_buffer
12998c2ecf20Sopenharmony_ci#define BPT 1022
13008c2ecf20Sopenharmony_ci#if defined(one_buffer)
13018c2ecf20Sopenharmony_ci	np->cur_tx_copy->buffer = dma_map_single(&np->pci_dev->dev, skb->data,
13028c2ecf20Sopenharmony_ci						 skb->len, DMA_TO_DEVICE);
13038c2ecf20Sopenharmony_ci	np->cur_tx_copy->control = TXIC | TXLD | TXFD | CRCEnable | PADEnable;
13048c2ecf20Sopenharmony_ci	np->cur_tx_copy->control |= (skb->len << PKTSShift);	/* pkt size */
13058c2ecf20Sopenharmony_ci	np->cur_tx_copy->control |= (skb->len << TBSShift);	/* buffer size */
13068c2ecf20Sopenharmony_ci// 89/12/29 add,
13078c2ecf20Sopenharmony_ci	if (np->pci_dev->device == 0x891)
13088c2ecf20Sopenharmony_ci		np->cur_tx_copy->control |= ETIControl | RetryTxLC;
13098c2ecf20Sopenharmony_ci	np->cur_tx_copy->status = TXOWN;
13108c2ecf20Sopenharmony_ci	np->cur_tx_copy = np->cur_tx_copy->next_desc_logical;
13118c2ecf20Sopenharmony_ci	--np->free_tx_count;
13128c2ecf20Sopenharmony_ci#elif defined(two_buffer)
13138c2ecf20Sopenharmony_ci	if (skb->len > BPT) {
13148c2ecf20Sopenharmony_ci		struct fealnx_desc *next;
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci		/* for the first descriptor */
13178c2ecf20Sopenharmony_ci		np->cur_tx_copy->buffer = dma_map_single(&np->pci_dev->dev,
13188c2ecf20Sopenharmony_ci							 skb->data, BPT,
13198c2ecf20Sopenharmony_ci							 DMA_TO_DEVICE);
13208c2ecf20Sopenharmony_ci		np->cur_tx_copy->control = TXIC | TXFD | CRCEnable | PADEnable;
13218c2ecf20Sopenharmony_ci		np->cur_tx_copy->control |= (skb->len << PKTSShift);	/* pkt size */
13228c2ecf20Sopenharmony_ci		np->cur_tx_copy->control |= (BPT << TBSShift);	/* buffer size */
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci		/* for the last descriptor */
13258c2ecf20Sopenharmony_ci		next = np->cur_tx_copy->next_desc_logical;
13268c2ecf20Sopenharmony_ci		next->skbuff = skb;
13278c2ecf20Sopenharmony_ci		next->control = TXIC | TXLD | CRCEnable | PADEnable;
13288c2ecf20Sopenharmony_ci		next->control |= (skb->len << PKTSShift);	/* pkt size */
13298c2ecf20Sopenharmony_ci		next->control |= ((skb->len - BPT) << TBSShift);	/* buf size */
13308c2ecf20Sopenharmony_ci// 89/12/29 add,
13318c2ecf20Sopenharmony_ci		if (np->pci_dev->device == 0x891)
13328c2ecf20Sopenharmony_ci			np->cur_tx_copy->control |= ETIControl | RetryTxLC;
13338c2ecf20Sopenharmony_ci		next->buffer = dma_map_single(&ep->pci_dev->dev,
13348c2ecf20Sopenharmony_ci					      skb->data + BPT, skb->len - BPT,
13358c2ecf20Sopenharmony_ci					      DMA_TO_DEVICE);
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci		next->status = TXOWN;
13388c2ecf20Sopenharmony_ci		np->cur_tx_copy->status = TXOWN;
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci		np->cur_tx_copy = next->next_desc_logical;
13418c2ecf20Sopenharmony_ci		np->free_tx_count -= 2;
13428c2ecf20Sopenharmony_ci	} else {
13438c2ecf20Sopenharmony_ci		np->cur_tx_copy->buffer = dma_map_single(&np->pci_dev->dev,
13448c2ecf20Sopenharmony_ci							 skb->data, skb->len,
13458c2ecf20Sopenharmony_ci							 DMA_TO_DEVICE);
13468c2ecf20Sopenharmony_ci		np->cur_tx_copy->control = TXIC | TXLD | TXFD | CRCEnable | PADEnable;
13478c2ecf20Sopenharmony_ci		np->cur_tx_copy->control |= (skb->len << PKTSShift);	/* pkt size */
13488c2ecf20Sopenharmony_ci		np->cur_tx_copy->control |= (skb->len << TBSShift);	/* buffer size */
13498c2ecf20Sopenharmony_ci// 89/12/29 add,
13508c2ecf20Sopenharmony_ci		if (np->pci_dev->device == 0x891)
13518c2ecf20Sopenharmony_ci			np->cur_tx_copy->control |= ETIControl | RetryTxLC;
13528c2ecf20Sopenharmony_ci		np->cur_tx_copy->status = TXOWN;
13538c2ecf20Sopenharmony_ci		np->cur_tx_copy = np->cur_tx_copy->next_desc_logical;
13548c2ecf20Sopenharmony_ci		--np->free_tx_count;
13558c2ecf20Sopenharmony_ci	}
13568c2ecf20Sopenharmony_ci#endif
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci	if (np->free_tx_count < 2)
13598c2ecf20Sopenharmony_ci		netif_stop_queue(dev);
13608c2ecf20Sopenharmony_ci	++np->really_tx_count;
13618c2ecf20Sopenharmony_ci	iowrite32(0, np->mem + TXPDR);
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&np->lock, flags);
13648c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
13658c2ecf20Sopenharmony_ci}
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci/* Take lock before calling */
13698c2ecf20Sopenharmony_ci/* Chip probably hosed tx ring. Clean up. */
13708c2ecf20Sopenharmony_cistatic void reset_tx_descriptors(struct net_device *dev)
13718c2ecf20Sopenharmony_ci{
13728c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
13738c2ecf20Sopenharmony_ci	struct fealnx_desc *cur;
13748c2ecf20Sopenharmony_ci	int i;
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci	/* initialize tx variables */
13778c2ecf20Sopenharmony_ci	np->cur_tx = &np->tx_ring[0];
13788c2ecf20Sopenharmony_ci	np->cur_tx_copy = &np->tx_ring[0];
13798c2ecf20Sopenharmony_ci	np->really_tx_count = 0;
13808c2ecf20Sopenharmony_ci	np->free_tx_count = TX_RING_SIZE;
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	for (i = 0; i < TX_RING_SIZE; i++) {
13838c2ecf20Sopenharmony_ci		cur = &np->tx_ring[i];
13848c2ecf20Sopenharmony_ci		if (cur->skbuff) {
13858c2ecf20Sopenharmony_ci			dma_unmap_single(&np->pci_dev->dev, cur->buffer,
13868c2ecf20Sopenharmony_ci					 cur->skbuff->len, DMA_TO_DEVICE);
13878c2ecf20Sopenharmony_ci			dev_kfree_skb_any(cur->skbuff);
13888c2ecf20Sopenharmony_ci			cur->skbuff = NULL;
13898c2ecf20Sopenharmony_ci		}
13908c2ecf20Sopenharmony_ci		cur->status = 0;
13918c2ecf20Sopenharmony_ci		cur->control = 0;	/* needed? */
13928c2ecf20Sopenharmony_ci		/* probably not needed. We do it for purely paranoid reasons */
13938c2ecf20Sopenharmony_ci		cur->next_desc = np->tx_ring_dma +
13948c2ecf20Sopenharmony_ci			(i + 1)*sizeof(struct fealnx_desc);
13958c2ecf20Sopenharmony_ci		cur->next_desc_logical = &np->tx_ring[i + 1];
13968c2ecf20Sopenharmony_ci	}
13978c2ecf20Sopenharmony_ci	/* for the last tx descriptor */
13988c2ecf20Sopenharmony_ci	np->tx_ring[TX_RING_SIZE - 1].next_desc = np->tx_ring_dma;
13998c2ecf20Sopenharmony_ci	np->tx_ring[TX_RING_SIZE - 1].next_desc_logical = &np->tx_ring[0];
14008c2ecf20Sopenharmony_ci}
14018c2ecf20Sopenharmony_ci
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci/* Take lock and stop rx before calling this */
14048c2ecf20Sopenharmony_cistatic void reset_rx_descriptors(struct net_device *dev)
14058c2ecf20Sopenharmony_ci{
14068c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
14078c2ecf20Sopenharmony_ci	struct fealnx_desc *cur = np->cur_rx;
14088c2ecf20Sopenharmony_ci	int i;
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci	allocate_rx_buffers(dev);
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci	for (i = 0; i < RX_RING_SIZE; i++) {
14138c2ecf20Sopenharmony_ci		if (cur->skbuff)
14148c2ecf20Sopenharmony_ci			cur->status = RXOWN;
14158c2ecf20Sopenharmony_ci		cur = cur->next_desc_logical;
14168c2ecf20Sopenharmony_ci	}
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci	iowrite32(np->rx_ring_dma + ((char*)np->cur_rx - (char*)np->rx_ring),
14198c2ecf20Sopenharmony_ci		np->mem + RXLBA);
14208c2ecf20Sopenharmony_ci}
14218c2ecf20Sopenharmony_ci
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci/* The interrupt handler does all of the Rx thread work and cleans up
14248c2ecf20Sopenharmony_ci   after the Tx thread. */
14258c2ecf20Sopenharmony_cistatic irqreturn_t intr_handler(int irq, void *dev_instance)
14268c2ecf20Sopenharmony_ci{
14278c2ecf20Sopenharmony_ci	struct net_device *dev = (struct net_device *) dev_instance;
14288c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
14298c2ecf20Sopenharmony_ci	void __iomem *ioaddr = np->mem;
14308c2ecf20Sopenharmony_ci	long boguscnt = max_interrupt_work;
14318c2ecf20Sopenharmony_ci	unsigned int num_tx = 0;
14328c2ecf20Sopenharmony_ci	int handled = 0;
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci	spin_lock(&np->lock);
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci	iowrite32(0, ioaddr + IMR);
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci	do {
14398c2ecf20Sopenharmony_ci		u32 intr_status = ioread32(ioaddr + ISR);
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci		/* Acknowledge all of the current interrupt sources ASAP. */
14428c2ecf20Sopenharmony_ci		iowrite32(intr_status, ioaddr + ISR);
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci		if (debug)
14458c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n", dev->name,
14468c2ecf20Sopenharmony_ci			       intr_status);
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci		if (!(intr_status & np->imrvalue))
14498c2ecf20Sopenharmony_ci			break;
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci		handled = 1;
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci// 90/1/16 delete,
14548c2ecf20Sopenharmony_ci//
14558c2ecf20Sopenharmony_ci//      if (intr_status & FBE)
14568c2ecf20Sopenharmony_ci//      {   /* fatal error */
14578c2ecf20Sopenharmony_ci//          stop_nic_tx(ioaddr, 0);
14588c2ecf20Sopenharmony_ci//          stop_nic_rx(ioaddr, 0);
14598c2ecf20Sopenharmony_ci//          break;
14608c2ecf20Sopenharmony_ci//      };
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci		if (intr_status & TUNF)
14638c2ecf20Sopenharmony_ci			iowrite32(0, ioaddr + TXPDR);
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci		if (intr_status & CNTOVF) {
14668c2ecf20Sopenharmony_ci			/* missed pkts */
14678c2ecf20Sopenharmony_ci			dev->stats.rx_missed_errors +=
14688c2ecf20Sopenharmony_ci				ioread32(ioaddr + TALLY) & 0x7fff;
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci			/* crc error */
14718c2ecf20Sopenharmony_ci			dev->stats.rx_crc_errors +=
14728c2ecf20Sopenharmony_ci			    (ioread32(ioaddr + TALLY) & 0x7fff0000) >> 16;
14738c2ecf20Sopenharmony_ci		}
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ci		if (intr_status & (RI | RBU)) {
14768c2ecf20Sopenharmony_ci			if (intr_status & RI)
14778c2ecf20Sopenharmony_ci				netdev_rx(dev);
14788c2ecf20Sopenharmony_ci			else {
14798c2ecf20Sopenharmony_ci				stop_nic_rx(ioaddr, np->crvalue);
14808c2ecf20Sopenharmony_ci				reset_rx_descriptors(dev);
14818c2ecf20Sopenharmony_ci				iowrite32(np->crvalue, ioaddr + TCRRCR);
14828c2ecf20Sopenharmony_ci			}
14838c2ecf20Sopenharmony_ci		}
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_ci		while (np->really_tx_count) {
14868c2ecf20Sopenharmony_ci			long tx_status = np->cur_tx->status;
14878c2ecf20Sopenharmony_ci			long tx_control = np->cur_tx->control;
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci			if (!(tx_control & TXLD)) {	/* this pkt is combined by two tx descriptors */
14908c2ecf20Sopenharmony_ci				struct fealnx_desc *next;
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci				next = np->cur_tx->next_desc_logical;
14938c2ecf20Sopenharmony_ci				tx_status = next->status;
14948c2ecf20Sopenharmony_ci				tx_control = next->control;
14958c2ecf20Sopenharmony_ci			}
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci			if (tx_status & TXOWN)
14988c2ecf20Sopenharmony_ci				break;
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci			if (!(np->crvalue & CR_W_ENH)) {
15018c2ecf20Sopenharmony_ci				if (tx_status & (CSL | LC | EC | UDF | HF)) {
15028c2ecf20Sopenharmony_ci					dev->stats.tx_errors++;
15038c2ecf20Sopenharmony_ci					if (tx_status & EC)
15048c2ecf20Sopenharmony_ci						dev->stats.tx_aborted_errors++;
15058c2ecf20Sopenharmony_ci					if (tx_status & CSL)
15068c2ecf20Sopenharmony_ci						dev->stats.tx_carrier_errors++;
15078c2ecf20Sopenharmony_ci					if (tx_status & LC)
15088c2ecf20Sopenharmony_ci						dev->stats.tx_window_errors++;
15098c2ecf20Sopenharmony_ci					if (tx_status & UDF)
15108c2ecf20Sopenharmony_ci						dev->stats.tx_fifo_errors++;
15118c2ecf20Sopenharmony_ci					if ((tx_status & HF) && np->mii.full_duplex == 0)
15128c2ecf20Sopenharmony_ci						dev->stats.tx_heartbeat_errors++;
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci				} else {
15158c2ecf20Sopenharmony_ci					dev->stats.tx_bytes +=
15168c2ecf20Sopenharmony_ci					    ((tx_control & PKTSMask) >> PKTSShift);
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci					dev->stats.collisions +=
15198c2ecf20Sopenharmony_ci					    ((tx_status & NCRMask) >> NCRShift);
15208c2ecf20Sopenharmony_ci					dev->stats.tx_packets++;
15218c2ecf20Sopenharmony_ci				}
15228c2ecf20Sopenharmony_ci			} else {
15238c2ecf20Sopenharmony_ci				dev->stats.tx_bytes +=
15248c2ecf20Sopenharmony_ci				    ((tx_control & PKTSMask) >> PKTSShift);
15258c2ecf20Sopenharmony_ci				dev->stats.tx_packets++;
15268c2ecf20Sopenharmony_ci			}
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci			/* Free the original skb. */
15298c2ecf20Sopenharmony_ci			dma_unmap_single(&np->pci_dev->dev,
15308c2ecf20Sopenharmony_ci					 np->cur_tx->buffer,
15318c2ecf20Sopenharmony_ci					 np->cur_tx->skbuff->len,
15328c2ecf20Sopenharmony_ci					 DMA_TO_DEVICE);
15338c2ecf20Sopenharmony_ci			dev_consume_skb_irq(np->cur_tx->skbuff);
15348c2ecf20Sopenharmony_ci			np->cur_tx->skbuff = NULL;
15358c2ecf20Sopenharmony_ci			--np->really_tx_count;
15368c2ecf20Sopenharmony_ci			if (np->cur_tx->control & TXLD) {
15378c2ecf20Sopenharmony_ci				np->cur_tx = np->cur_tx->next_desc_logical;
15388c2ecf20Sopenharmony_ci				++np->free_tx_count;
15398c2ecf20Sopenharmony_ci			} else {
15408c2ecf20Sopenharmony_ci				np->cur_tx = np->cur_tx->next_desc_logical;
15418c2ecf20Sopenharmony_ci				np->cur_tx = np->cur_tx->next_desc_logical;
15428c2ecf20Sopenharmony_ci				np->free_tx_count += 2;
15438c2ecf20Sopenharmony_ci			}
15448c2ecf20Sopenharmony_ci			num_tx++;
15458c2ecf20Sopenharmony_ci		}		/* end of for loop */
15468c2ecf20Sopenharmony_ci
15478c2ecf20Sopenharmony_ci		if (num_tx && np->free_tx_count >= 2)
15488c2ecf20Sopenharmony_ci			netif_wake_queue(dev);
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_ci		/* read transmit status for enhanced mode only */
15518c2ecf20Sopenharmony_ci		if (np->crvalue & CR_W_ENH) {
15528c2ecf20Sopenharmony_ci			long data;
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci			data = ioread32(ioaddr + TSR);
15558c2ecf20Sopenharmony_ci			dev->stats.tx_errors += (data & 0xff000000) >> 24;
15568c2ecf20Sopenharmony_ci			dev->stats.tx_aborted_errors +=
15578c2ecf20Sopenharmony_ci				(data & 0xff000000) >> 24;
15588c2ecf20Sopenharmony_ci			dev->stats.tx_window_errors +=
15598c2ecf20Sopenharmony_ci				(data & 0x00ff0000) >> 16;
15608c2ecf20Sopenharmony_ci			dev->stats.collisions += (data & 0x0000ffff);
15618c2ecf20Sopenharmony_ci		}
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_ci		if (--boguscnt < 0) {
15648c2ecf20Sopenharmony_ci			printk(KERN_WARNING "%s: Too much work at interrupt, "
15658c2ecf20Sopenharmony_ci			       "status=0x%4.4x.\n", dev->name, intr_status);
15668c2ecf20Sopenharmony_ci			if (!np->reset_timer_armed) {
15678c2ecf20Sopenharmony_ci				np->reset_timer_armed = 1;
15688c2ecf20Sopenharmony_ci				np->reset_timer.expires = RUN_AT(HZ/2);
15698c2ecf20Sopenharmony_ci				add_timer(&np->reset_timer);
15708c2ecf20Sopenharmony_ci				stop_nic_rxtx(ioaddr, 0);
15718c2ecf20Sopenharmony_ci				netif_stop_queue(dev);
15728c2ecf20Sopenharmony_ci				/* or netif_tx_disable(dev); ?? */
15738c2ecf20Sopenharmony_ci				/* Prevent other paths from enabling tx,rx,intrs */
15748c2ecf20Sopenharmony_ci				np->crvalue_sv = np->crvalue;
15758c2ecf20Sopenharmony_ci				np->imrvalue_sv = np->imrvalue;
15768c2ecf20Sopenharmony_ci				np->crvalue &= ~(CR_W_TXEN | CR_W_RXEN); /* or simply = 0? */
15778c2ecf20Sopenharmony_ci				np->imrvalue = 0;
15788c2ecf20Sopenharmony_ci			}
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci			break;
15818c2ecf20Sopenharmony_ci		}
15828c2ecf20Sopenharmony_ci	} while (1);
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci	/* read the tally counters */
15858c2ecf20Sopenharmony_ci	/* missed pkts */
15868c2ecf20Sopenharmony_ci	dev->stats.rx_missed_errors += ioread32(ioaddr + TALLY) & 0x7fff;
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci	/* crc error */
15898c2ecf20Sopenharmony_ci	dev->stats.rx_crc_errors +=
15908c2ecf20Sopenharmony_ci		(ioread32(ioaddr + TALLY) & 0x7fff0000) >> 16;
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_ci	if (debug)
15938c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
15948c2ecf20Sopenharmony_ci		       dev->name, ioread32(ioaddr + ISR));
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci	iowrite32(np->imrvalue, ioaddr + IMR);
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_ci	spin_unlock(&np->lock);
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci	return IRQ_RETVAL(handled);
16018c2ecf20Sopenharmony_ci}
16028c2ecf20Sopenharmony_ci
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_ci/* This routine is logically part of the interrupt handler, but separated
16058c2ecf20Sopenharmony_ci   for clarity and better register allocation. */
16068c2ecf20Sopenharmony_cistatic int netdev_rx(struct net_device *dev)
16078c2ecf20Sopenharmony_ci{
16088c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
16098c2ecf20Sopenharmony_ci	void __iomem *ioaddr = np->mem;
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci	/* If EOP is set on the next entry, it's a new packet. Send it up. */
16128c2ecf20Sopenharmony_ci	while (!(np->cur_rx->status & RXOWN) && np->cur_rx->skbuff) {
16138c2ecf20Sopenharmony_ci		s32 rx_status = np->cur_rx->status;
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci		if (np->really_rx_count == 0)
16168c2ecf20Sopenharmony_ci			break;
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci		if (debug)
16198c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "  netdev_rx() status was %8.8x.\n", rx_status);
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci		if ((!((rx_status & RXFSD) && (rx_status & RXLSD))) ||
16228c2ecf20Sopenharmony_ci		    (rx_status & ErrorSummary)) {
16238c2ecf20Sopenharmony_ci			if (rx_status & ErrorSummary) {	/* there was a fatal error */
16248c2ecf20Sopenharmony_ci				if (debug)
16258c2ecf20Sopenharmony_ci					printk(KERN_DEBUG
16268c2ecf20Sopenharmony_ci					       "%s: Receive error, Rx status %8.8x.\n",
16278c2ecf20Sopenharmony_ci					       dev->name, rx_status);
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci				dev->stats.rx_errors++;	/* end of a packet. */
16308c2ecf20Sopenharmony_ci				if (rx_status & (LONGPKT | RUNTPKT))
16318c2ecf20Sopenharmony_ci					dev->stats.rx_length_errors++;
16328c2ecf20Sopenharmony_ci				if (rx_status & RXER)
16338c2ecf20Sopenharmony_ci					dev->stats.rx_frame_errors++;
16348c2ecf20Sopenharmony_ci				if (rx_status & CRC)
16358c2ecf20Sopenharmony_ci					dev->stats.rx_crc_errors++;
16368c2ecf20Sopenharmony_ci			} else {
16378c2ecf20Sopenharmony_ci				int need_to_reset = 0;
16388c2ecf20Sopenharmony_ci				int desno = 0;
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci				if (rx_status & RXFSD) {	/* this pkt is too long, over one rx buffer */
16418c2ecf20Sopenharmony_ci					struct fealnx_desc *cur;
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci					/* check this packet is received completely? */
16448c2ecf20Sopenharmony_ci					cur = np->cur_rx;
16458c2ecf20Sopenharmony_ci					while (desno <= np->really_rx_count) {
16468c2ecf20Sopenharmony_ci						++desno;
16478c2ecf20Sopenharmony_ci						if ((!(cur->status & RXOWN)) &&
16488c2ecf20Sopenharmony_ci						    (cur->status & RXLSD))
16498c2ecf20Sopenharmony_ci							break;
16508c2ecf20Sopenharmony_ci						/* goto next rx descriptor */
16518c2ecf20Sopenharmony_ci						cur = cur->next_desc_logical;
16528c2ecf20Sopenharmony_ci					}
16538c2ecf20Sopenharmony_ci					if (desno > np->really_rx_count)
16548c2ecf20Sopenharmony_ci						need_to_reset = 1;
16558c2ecf20Sopenharmony_ci				} else	/* RXLSD did not find, something error */
16568c2ecf20Sopenharmony_ci					need_to_reset = 1;
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci				if (need_to_reset == 0) {
16598c2ecf20Sopenharmony_ci					int i;
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_ci					dev->stats.rx_length_errors++;
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci					/* free all rx descriptors related this long pkt */
16648c2ecf20Sopenharmony_ci					for (i = 0; i < desno; ++i) {
16658c2ecf20Sopenharmony_ci						if (!np->cur_rx->skbuff) {
16668c2ecf20Sopenharmony_ci							printk(KERN_DEBUG
16678c2ecf20Sopenharmony_ci								"%s: I'm scared\n", dev->name);
16688c2ecf20Sopenharmony_ci							break;
16698c2ecf20Sopenharmony_ci						}
16708c2ecf20Sopenharmony_ci						np->cur_rx->status = RXOWN;
16718c2ecf20Sopenharmony_ci						np->cur_rx = np->cur_rx->next_desc_logical;
16728c2ecf20Sopenharmony_ci					}
16738c2ecf20Sopenharmony_ci					continue;
16748c2ecf20Sopenharmony_ci				} else {        /* rx error, need to reset this chip */
16758c2ecf20Sopenharmony_ci					stop_nic_rx(ioaddr, np->crvalue);
16768c2ecf20Sopenharmony_ci					reset_rx_descriptors(dev);
16778c2ecf20Sopenharmony_ci					iowrite32(np->crvalue, ioaddr + TCRRCR);
16788c2ecf20Sopenharmony_ci				}
16798c2ecf20Sopenharmony_ci				break;	/* exit the while loop */
16808c2ecf20Sopenharmony_ci			}
16818c2ecf20Sopenharmony_ci		} else {	/* this received pkt is ok */
16828c2ecf20Sopenharmony_ci
16838c2ecf20Sopenharmony_ci			struct sk_buff *skb;
16848c2ecf20Sopenharmony_ci			/* Omit the four octet CRC from the length. */
16858c2ecf20Sopenharmony_ci			short pkt_len = ((rx_status & FLNGMASK) >> FLNGShift) - 4;
16868c2ecf20Sopenharmony_ci
16878c2ecf20Sopenharmony_ci#ifndef final_version
16888c2ecf20Sopenharmony_ci			if (debug)
16898c2ecf20Sopenharmony_ci				printk(KERN_DEBUG "  netdev_rx() normal Rx pkt length %d"
16908c2ecf20Sopenharmony_ci				       " status %x.\n", pkt_len, rx_status);
16918c2ecf20Sopenharmony_ci#endif
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_ci			/* Check if the packet is long enough to accept without copying
16948c2ecf20Sopenharmony_ci			   to a minimally-sized skbuff. */
16958c2ecf20Sopenharmony_ci			if (pkt_len < rx_copybreak &&
16968c2ecf20Sopenharmony_ci			    (skb = netdev_alloc_skb(dev, pkt_len + 2)) != NULL) {
16978c2ecf20Sopenharmony_ci				skb_reserve(skb, 2);	/* 16 byte align the IP header */
16988c2ecf20Sopenharmony_ci				dma_sync_single_for_cpu(&np->pci_dev->dev,
16998c2ecf20Sopenharmony_ci							np->cur_rx->buffer,
17008c2ecf20Sopenharmony_ci							np->rx_buf_sz,
17018c2ecf20Sopenharmony_ci							DMA_FROM_DEVICE);
17028c2ecf20Sopenharmony_ci				/* Call copy + cksum if available. */
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ci#if ! defined(__alpha__)
17058c2ecf20Sopenharmony_ci				skb_copy_to_linear_data(skb,
17068c2ecf20Sopenharmony_ci					np->cur_rx->skbuff->data, pkt_len);
17078c2ecf20Sopenharmony_ci				skb_put(skb, pkt_len);
17088c2ecf20Sopenharmony_ci#else
17098c2ecf20Sopenharmony_ci				skb_put_data(skb, np->cur_rx->skbuff->data,
17108c2ecf20Sopenharmony_ci					     pkt_len);
17118c2ecf20Sopenharmony_ci#endif
17128c2ecf20Sopenharmony_ci				dma_sync_single_for_device(&np->pci_dev->dev,
17138c2ecf20Sopenharmony_ci							   np->cur_rx->buffer,
17148c2ecf20Sopenharmony_ci							   np->rx_buf_sz,
17158c2ecf20Sopenharmony_ci							   DMA_FROM_DEVICE);
17168c2ecf20Sopenharmony_ci			} else {
17178c2ecf20Sopenharmony_ci				dma_unmap_single(&np->pci_dev->dev,
17188c2ecf20Sopenharmony_ci						 np->cur_rx->buffer,
17198c2ecf20Sopenharmony_ci						 np->rx_buf_sz,
17208c2ecf20Sopenharmony_ci						 DMA_FROM_DEVICE);
17218c2ecf20Sopenharmony_ci				skb_put(skb = np->cur_rx->skbuff, pkt_len);
17228c2ecf20Sopenharmony_ci				np->cur_rx->skbuff = NULL;
17238c2ecf20Sopenharmony_ci				--np->really_rx_count;
17248c2ecf20Sopenharmony_ci			}
17258c2ecf20Sopenharmony_ci			skb->protocol = eth_type_trans(skb, dev);
17268c2ecf20Sopenharmony_ci			netif_rx(skb);
17278c2ecf20Sopenharmony_ci			dev->stats.rx_packets++;
17288c2ecf20Sopenharmony_ci			dev->stats.rx_bytes += pkt_len;
17298c2ecf20Sopenharmony_ci		}
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_ci		np->cur_rx = np->cur_rx->next_desc_logical;
17328c2ecf20Sopenharmony_ci	}			/* end of while loop */
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci	/*  allocate skb for rx buffers */
17358c2ecf20Sopenharmony_ci	allocate_rx_buffers(dev);
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci	return 0;
17388c2ecf20Sopenharmony_ci}
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci
17418c2ecf20Sopenharmony_cistatic struct net_device_stats *get_stats(struct net_device *dev)
17428c2ecf20Sopenharmony_ci{
17438c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
17448c2ecf20Sopenharmony_ci	void __iomem *ioaddr = np->mem;
17458c2ecf20Sopenharmony_ci
17468c2ecf20Sopenharmony_ci	/* The chip only need report frame silently dropped. */
17478c2ecf20Sopenharmony_ci	if (netif_running(dev)) {
17488c2ecf20Sopenharmony_ci		dev->stats.rx_missed_errors +=
17498c2ecf20Sopenharmony_ci			ioread32(ioaddr + TALLY) & 0x7fff;
17508c2ecf20Sopenharmony_ci		dev->stats.rx_crc_errors +=
17518c2ecf20Sopenharmony_ci			(ioread32(ioaddr + TALLY) & 0x7fff0000) >> 16;
17528c2ecf20Sopenharmony_ci	}
17538c2ecf20Sopenharmony_ci
17548c2ecf20Sopenharmony_ci	return &dev->stats;
17558c2ecf20Sopenharmony_ci}
17568c2ecf20Sopenharmony_ci
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_ci/* for dev->set_multicast_list */
17598c2ecf20Sopenharmony_cistatic void set_rx_mode(struct net_device *dev)
17608c2ecf20Sopenharmony_ci{
17618c2ecf20Sopenharmony_ci	spinlock_t *lp = &((struct netdev_private *)netdev_priv(dev))->lock;
17628c2ecf20Sopenharmony_ci	unsigned long flags;
17638c2ecf20Sopenharmony_ci	spin_lock_irqsave(lp, flags);
17648c2ecf20Sopenharmony_ci	__set_rx_mode(dev);
17658c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(lp, flags);
17668c2ecf20Sopenharmony_ci}
17678c2ecf20Sopenharmony_ci
17688c2ecf20Sopenharmony_ci
17698c2ecf20Sopenharmony_ci/* Take lock before calling */
17708c2ecf20Sopenharmony_cistatic void __set_rx_mode(struct net_device *dev)
17718c2ecf20Sopenharmony_ci{
17728c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
17738c2ecf20Sopenharmony_ci	void __iomem *ioaddr = np->mem;
17748c2ecf20Sopenharmony_ci	u32 mc_filter[2];	/* Multicast hash filter */
17758c2ecf20Sopenharmony_ci	u32 rx_mode;
17768c2ecf20Sopenharmony_ci
17778c2ecf20Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {	/* Set promiscuous. */
17788c2ecf20Sopenharmony_ci		memset(mc_filter, 0xff, sizeof(mc_filter));
17798c2ecf20Sopenharmony_ci		rx_mode = CR_W_PROM | CR_W_AB | CR_W_AM;
17808c2ecf20Sopenharmony_ci	} else if ((netdev_mc_count(dev) > multicast_filter_limit) ||
17818c2ecf20Sopenharmony_ci		   (dev->flags & IFF_ALLMULTI)) {
17828c2ecf20Sopenharmony_ci		/* Too many to match, or accept all multicasts. */
17838c2ecf20Sopenharmony_ci		memset(mc_filter, 0xff, sizeof(mc_filter));
17848c2ecf20Sopenharmony_ci		rx_mode = CR_W_AB | CR_W_AM;
17858c2ecf20Sopenharmony_ci	} else {
17868c2ecf20Sopenharmony_ci		struct netdev_hw_addr *ha;
17878c2ecf20Sopenharmony_ci
17888c2ecf20Sopenharmony_ci		memset(mc_filter, 0, sizeof(mc_filter));
17898c2ecf20Sopenharmony_ci		netdev_for_each_mc_addr(ha, dev) {
17908c2ecf20Sopenharmony_ci			unsigned int bit;
17918c2ecf20Sopenharmony_ci			bit = (ether_crc(ETH_ALEN, ha->addr) >> 26) ^ 0x3F;
17928c2ecf20Sopenharmony_ci			mc_filter[bit >> 5] |= (1 << bit);
17938c2ecf20Sopenharmony_ci		}
17948c2ecf20Sopenharmony_ci		rx_mode = CR_W_AB | CR_W_AM;
17958c2ecf20Sopenharmony_ci	}
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_ci	stop_nic_rxtx(ioaddr, np->crvalue);
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci	iowrite32(mc_filter[0], ioaddr + MAR0);
18008c2ecf20Sopenharmony_ci	iowrite32(mc_filter[1], ioaddr + MAR1);
18018c2ecf20Sopenharmony_ci	np->crvalue &= ~CR_W_RXMODEMASK;
18028c2ecf20Sopenharmony_ci	np->crvalue |= rx_mode;
18038c2ecf20Sopenharmony_ci	iowrite32(np->crvalue, ioaddr + TCRRCR);
18048c2ecf20Sopenharmony_ci}
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_cistatic void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
18078c2ecf20Sopenharmony_ci{
18088c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
18098c2ecf20Sopenharmony_ci
18108c2ecf20Sopenharmony_ci	strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
18118c2ecf20Sopenharmony_ci	strlcpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info));
18128c2ecf20Sopenharmony_ci}
18138c2ecf20Sopenharmony_ci
18148c2ecf20Sopenharmony_cistatic int netdev_get_link_ksettings(struct net_device *dev,
18158c2ecf20Sopenharmony_ci				     struct ethtool_link_ksettings *cmd)
18168c2ecf20Sopenharmony_ci{
18178c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
18208c2ecf20Sopenharmony_ci	mii_ethtool_get_link_ksettings(&np->mii, cmd);
18218c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_ci	return 0;
18248c2ecf20Sopenharmony_ci}
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_cistatic int netdev_set_link_ksettings(struct net_device *dev,
18278c2ecf20Sopenharmony_ci				     const struct ethtool_link_ksettings *cmd)
18288c2ecf20Sopenharmony_ci{
18298c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
18308c2ecf20Sopenharmony_ci	int rc;
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
18338c2ecf20Sopenharmony_ci	rc = mii_ethtool_set_link_ksettings(&np->mii, cmd);
18348c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_ci	return rc;
18378c2ecf20Sopenharmony_ci}
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_cistatic int netdev_nway_reset(struct net_device *dev)
18408c2ecf20Sopenharmony_ci{
18418c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
18428c2ecf20Sopenharmony_ci	return mii_nway_restart(&np->mii);
18438c2ecf20Sopenharmony_ci}
18448c2ecf20Sopenharmony_ci
18458c2ecf20Sopenharmony_cistatic u32 netdev_get_link(struct net_device *dev)
18468c2ecf20Sopenharmony_ci{
18478c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
18488c2ecf20Sopenharmony_ci	return mii_link_ok(&np->mii);
18498c2ecf20Sopenharmony_ci}
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_cistatic u32 netdev_get_msglevel(struct net_device *dev)
18528c2ecf20Sopenharmony_ci{
18538c2ecf20Sopenharmony_ci	return debug;
18548c2ecf20Sopenharmony_ci}
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_cistatic void netdev_set_msglevel(struct net_device *dev, u32 value)
18578c2ecf20Sopenharmony_ci{
18588c2ecf20Sopenharmony_ci	debug = value;
18598c2ecf20Sopenharmony_ci}
18608c2ecf20Sopenharmony_ci
18618c2ecf20Sopenharmony_cistatic const struct ethtool_ops netdev_ethtool_ops = {
18628c2ecf20Sopenharmony_ci	.get_drvinfo		= netdev_get_drvinfo,
18638c2ecf20Sopenharmony_ci	.nway_reset		= netdev_nway_reset,
18648c2ecf20Sopenharmony_ci	.get_link		= netdev_get_link,
18658c2ecf20Sopenharmony_ci	.get_msglevel		= netdev_get_msglevel,
18668c2ecf20Sopenharmony_ci	.set_msglevel		= netdev_set_msglevel,
18678c2ecf20Sopenharmony_ci	.get_link_ksettings	= netdev_get_link_ksettings,
18688c2ecf20Sopenharmony_ci	.set_link_ksettings	= netdev_set_link_ksettings,
18698c2ecf20Sopenharmony_ci};
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_cistatic int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
18728c2ecf20Sopenharmony_ci{
18738c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
18748c2ecf20Sopenharmony_ci	int rc;
18758c2ecf20Sopenharmony_ci
18768c2ecf20Sopenharmony_ci	if (!netif_running(dev))
18778c2ecf20Sopenharmony_ci		return -EINVAL;
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
18808c2ecf20Sopenharmony_ci	rc = generic_mii_ioctl(&np->mii, if_mii(rq), cmd, NULL);
18818c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_ci	return rc;
18848c2ecf20Sopenharmony_ci}
18858c2ecf20Sopenharmony_ci
18868c2ecf20Sopenharmony_ci
18878c2ecf20Sopenharmony_cistatic int netdev_close(struct net_device *dev)
18888c2ecf20Sopenharmony_ci{
18898c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
18908c2ecf20Sopenharmony_ci	void __iomem *ioaddr = np->mem;
18918c2ecf20Sopenharmony_ci	int i;
18928c2ecf20Sopenharmony_ci
18938c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_ci	/* Disable interrupts by clearing the interrupt mask. */
18968c2ecf20Sopenharmony_ci	iowrite32(0x0000, ioaddr + IMR);
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci	/* Stop the chip's Tx and Rx processes. */
18998c2ecf20Sopenharmony_ci	stop_nic_rxtx(ioaddr, 0);
19008c2ecf20Sopenharmony_ci
19018c2ecf20Sopenharmony_ci	del_timer_sync(&np->timer);
19028c2ecf20Sopenharmony_ci	del_timer_sync(&np->reset_timer);
19038c2ecf20Sopenharmony_ci
19048c2ecf20Sopenharmony_ci	free_irq(np->pci_dev->irq, dev);
19058c2ecf20Sopenharmony_ci
19068c2ecf20Sopenharmony_ci	/* Free all the skbuffs in the Rx queue. */
19078c2ecf20Sopenharmony_ci	for (i = 0; i < RX_RING_SIZE; i++) {
19088c2ecf20Sopenharmony_ci		struct sk_buff *skb = np->rx_ring[i].skbuff;
19098c2ecf20Sopenharmony_ci
19108c2ecf20Sopenharmony_ci		np->rx_ring[i].status = 0;
19118c2ecf20Sopenharmony_ci		if (skb) {
19128c2ecf20Sopenharmony_ci			dma_unmap_single(&np->pci_dev->dev,
19138c2ecf20Sopenharmony_ci					 np->rx_ring[i].buffer, np->rx_buf_sz,
19148c2ecf20Sopenharmony_ci					 DMA_FROM_DEVICE);
19158c2ecf20Sopenharmony_ci			dev_kfree_skb(skb);
19168c2ecf20Sopenharmony_ci			np->rx_ring[i].skbuff = NULL;
19178c2ecf20Sopenharmony_ci		}
19188c2ecf20Sopenharmony_ci	}
19198c2ecf20Sopenharmony_ci
19208c2ecf20Sopenharmony_ci	for (i = 0; i < TX_RING_SIZE; i++) {
19218c2ecf20Sopenharmony_ci		struct sk_buff *skb = np->tx_ring[i].skbuff;
19228c2ecf20Sopenharmony_ci
19238c2ecf20Sopenharmony_ci		if (skb) {
19248c2ecf20Sopenharmony_ci			dma_unmap_single(&np->pci_dev->dev,
19258c2ecf20Sopenharmony_ci					 np->tx_ring[i].buffer, skb->len,
19268c2ecf20Sopenharmony_ci					 DMA_TO_DEVICE);
19278c2ecf20Sopenharmony_ci			dev_kfree_skb(skb);
19288c2ecf20Sopenharmony_ci			np->tx_ring[i].skbuff = NULL;
19298c2ecf20Sopenharmony_ci		}
19308c2ecf20Sopenharmony_ci	}
19318c2ecf20Sopenharmony_ci
19328c2ecf20Sopenharmony_ci	return 0;
19338c2ecf20Sopenharmony_ci}
19348c2ecf20Sopenharmony_ci
19358c2ecf20Sopenharmony_cistatic const struct pci_device_id fealnx_pci_tbl[] = {
19368c2ecf20Sopenharmony_ci	{0x1516, 0x0800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
19378c2ecf20Sopenharmony_ci	{0x1516, 0x0803, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1},
19388c2ecf20Sopenharmony_ci	{0x1516, 0x0891, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2},
19398c2ecf20Sopenharmony_ci	{} /* terminate list */
19408c2ecf20Sopenharmony_ci};
19418c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, fealnx_pci_tbl);
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_cistatic struct pci_driver fealnx_driver = {
19458c2ecf20Sopenharmony_ci	.name		= "fealnx",
19468c2ecf20Sopenharmony_ci	.id_table	= fealnx_pci_tbl,
19478c2ecf20Sopenharmony_ci	.probe		= fealnx_init_one,
19488c2ecf20Sopenharmony_ci	.remove		= fealnx_remove_one,
19498c2ecf20Sopenharmony_ci};
19508c2ecf20Sopenharmony_ci
19518c2ecf20Sopenharmony_cistatic int __init fealnx_init(void)
19528c2ecf20Sopenharmony_ci{
19538c2ecf20Sopenharmony_ci	return pci_register_driver(&fealnx_driver);
19548c2ecf20Sopenharmony_ci}
19558c2ecf20Sopenharmony_ci
19568c2ecf20Sopenharmony_cistatic void __exit fealnx_exit(void)
19578c2ecf20Sopenharmony_ci{
19588c2ecf20Sopenharmony_ci	pci_unregister_driver(&fealnx_driver);
19598c2ecf20Sopenharmony_ci}
19608c2ecf20Sopenharmony_ci
19618c2ecf20Sopenharmony_cimodule_init(fealnx_init);
19628c2ecf20Sopenharmony_cimodule_exit(fealnx_exit);
1963