18c2ecf20Sopenharmony_ci/* natsemi.c: A Linux PCI Ethernet driver for the NatSemi DP8381x series. */
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci	Written/copyright 1999-2001 by Donald Becker.
48c2ecf20Sopenharmony_ci	Portions copyright (c) 2001,2002 Sun Microsystems (thockin@sun.com)
58c2ecf20Sopenharmony_ci	Portions copyright 2001,2002 Manfred Spraul (manfred@colorfullife.com)
68c2ecf20Sopenharmony_ci	Portions copyright 2004 Harald Welte <laforge@gnumonks.org>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci	This software may be used and distributed according to the terms of
98c2ecf20Sopenharmony_ci	the GNU General Public License (GPL), incorporated herein by reference.
108c2ecf20Sopenharmony_ci	Drivers based on or derived from this code fall under the GPL and must
118c2ecf20Sopenharmony_ci	retain the authorship, copyright and license notice.  This file is not
128c2ecf20Sopenharmony_ci	a complete program and may only be used when the entire operating
138c2ecf20Sopenharmony_ci	system is licensed under the GPL.  License for under other terms may be
148c2ecf20Sopenharmony_ci	available.  Contact the original author for details.
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci	The original author may be reached as becker@scyld.com, or at
178c2ecf20Sopenharmony_ci	Scyld Computing Corporation
188c2ecf20Sopenharmony_ci	410 Severn Ave., Suite 210
198c2ecf20Sopenharmony_ci	Annapolis MD 21403
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	Support information and updates available at
228c2ecf20Sopenharmony_ci	http://www.scyld.com/network/netsemi.html
238c2ecf20Sopenharmony_ci	[link no longer provides useful info -jgarzik]
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	TODO:
278c2ecf20Sopenharmony_ci	* big endian support with CFG:BEM instead of cpu_to_le32
288c2ecf20Sopenharmony_ci*/
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include <linux/module.h>
318c2ecf20Sopenharmony_ci#include <linux/kernel.h>
328c2ecf20Sopenharmony_ci#include <linux/string.h>
338c2ecf20Sopenharmony_ci#include <linux/timer.h>
348c2ecf20Sopenharmony_ci#include <linux/errno.h>
358c2ecf20Sopenharmony_ci#include <linux/ioport.h>
368c2ecf20Sopenharmony_ci#include <linux/slab.h>
378c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
388c2ecf20Sopenharmony_ci#include <linux/pci.h>
398c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
408c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
418c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
428c2ecf20Sopenharmony_ci#include <linux/init.h>
438c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
448c2ecf20Sopenharmony_ci#include <linux/ethtool.h>
458c2ecf20Sopenharmony_ci#include <linux/delay.h>
468c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
478c2ecf20Sopenharmony_ci#include <linux/mii.h>
488c2ecf20Sopenharmony_ci#include <linux/crc32.h>
498c2ecf20Sopenharmony_ci#include <linux/bitops.h>
508c2ecf20Sopenharmony_ci#include <linux/prefetch.h>
518c2ecf20Sopenharmony_ci#include <asm/processor.h>	/* Processor type for cache alignment. */
528c2ecf20Sopenharmony_ci#include <asm/io.h>
538c2ecf20Sopenharmony_ci#include <asm/irq.h>
548c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#define DRV_NAME	"natsemi"
578c2ecf20Sopenharmony_ci#define DRV_VERSION	"2.1"
588c2ecf20Sopenharmony_ci#define DRV_RELDATE	"Sept 11, 2006"
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define RX_OFFSET	2
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* Updated to recommendations in pci-skeleton v2.03. */
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/* The user-configurable values.
658c2ecf20Sopenharmony_ci   These may be modified when a driver module is loaded.*/
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#define NATSEMI_DEF_MSG		(NETIF_MSG_DRV		| \
688c2ecf20Sopenharmony_ci				 NETIF_MSG_LINK		| \
698c2ecf20Sopenharmony_ci				 NETIF_MSG_WOL		| \
708c2ecf20Sopenharmony_ci				 NETIF_MSG_RX_ERR	| \
718c2ecf20Sopenharmony_ci				 NETIF_MSG_TX_ERR)
728c2ecf20Sopenharmony_cistatic int debug = -1;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic int mtu;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
778c2ecf20Sopenharmony_ci   This chip uses a 512 element hash table based on the Ethernet CRC.  */
788c2ecf20Sopenharmony_cistatic const int multicast_filter_limit = 100;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
818c2ecf20Sopenharmony_ci   Setting to > 1518 effectively disables this feature. */
828c2ecf20Sopenharmony_cistatic int rx_copybreak;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic int dspcfg_workaround = 1;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/* Used to pass the media type, etc.
878c2ecf20Sopenharmony_ci   Both 'options[]' and 'full_duplex[]' should exist for driver
888c2ecf20Sopenharmony_ci   interoperability.
898c2ecf20Sopenharmony_ci   The media type is usually passed in 'options[]'.
908c2ecf20Sopenharmony_ci*/
918c2ecf20Sopenharmony_ci#define MAX_UNITS 8		/* More are supported, limit only on options */
928c2ecf20Sopenharmony_cistatic int options[MAX_UNITS];
938c2ecf20Sopenharmony_cistatic int full_duplex[MAX_UNITS];
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/* Operational parameters that are set at compile time. */
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/* Keep the ring sizes a power of two for compile efficiency.
988c2ecf20Sopenharmony_ci   The compiler will convert <unsigned>'%'<2^N> into a bit mask.
998c2ecf20Sopenharmony_ci   Making the Tx ring too large decreases the effectiveness of channel
1008c2ecf20Sopenharmony_ci   bonding and packet priority.
1018c2ecf20Sopenharmony_ci   There are no ill effects from too-large receive rings. */
1028c2ecf20Sopenharmony_ci#define TX_RING_SIZE	16
1038c2ecf20Sopenharmony_ci#define TX_QUEUE_LEN	10 /* Limit ring entries actually used, min 4. */
1048c2ecf20Sopenharmony_ci#define RX_RING_SIZE	32
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/* Operational parameters that usually are not changed. */
1078c2ecf20Sopenharmony_ci/* Time in jiffies before concluding the transmitter is hung. */
1088c2ecf20Sopenharmony_ci#define TX_TIMEOUT  (2*HZ)
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci#define NATSEMI_HW_TIMEOUT	400
1118c2ecf20Sopenharmony_ci#define NATSEMI_TIMER_FREQ	5*HZ
1128c2ecf20Sopenharmony_ci#define NATSEMI_PG0_NREGS	64
1138c2ecf20Sopenharmony_ci#define NATSEMI_RFDR_NREGS	8
1148c2ecf20Sopenharmony_ci#define NATSEMI_PG1_NREGS	4
1158c2ecf20Sopenharmony_ci#define NATSEMI_NREGS		(NATSEMI_PG0_NREGS + NATSEMI_RFDR_NREGS + \
1168c2ecf20Sopenharmony_ci				 NATSEMI_PG1_NREGS)
1178c2ecf20Sopenharmony_ci#define NATSEMI_REGS_VER	1 /* v1 added RFDR registers */
1188c2ecf20Sopenharmony_ci#define NATSEMI_REGS_SIZE	(NATSEMI_NREGS * sizeof(u32))
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci/* Buffer sizes:
1218c2ecf20Sopenharmony_ci * The nic writes 32-bit values, even if the upper bytes of
1228c2ecf20Sopenharmony_ci * a 32-bit value are beyond the end of the buffer.
1238c2ecf20Sopenharmony_ci */
1248c2ecf20Sopenharmony_ci#define NATSEMI_HEADERS		22	/* 2*mac,type,vlan,crc */
1258c2ecf20Sopenharmony_ci#define NATSEMI_PADDING		16	/* 2 bytes should be sufficient */
1268c2ecf20Sopenharmony_ci#define NATSEMI_LONGPKT		1518	/* limit for normal packets */
1278c2ecf20Sopenharmony_ci#define NATSEMI_RX_LIMIT	2046	/* maximum supported by hardware */
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/* These identify the driver base version and may not be removed. */
1308c2ecf20Sopenharmony_cistatic const char version[] =
1318c2ecf20Sopenharmony_ci  KERN_INFO DRV_NAME " dp8381x driver, version "
1328c2ecf20Sopenharmony_ci      DRV_VERSION ", " DRV_RELDATE "\n"
1338c2ecf20Sopenharmony_ci  "  originally by Donald Becker <becker@scyld.com>\n"
1348c2ecf20Sopenharmony_ci  "  2.4.x kernel port by Jeff Garzik, Tjeerd Mulder\n";
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ciMODULE_AUTHOR("Donald Becker <becker@scyld.com>");
1378c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("National Semiconductor DP8381x series PCI Ethernet driver");
1388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cimodule_param(mtu, int, 0);
1418c2ecf20Sopenharmony_cimodule_param(debug, int, 0);
1428c2ecf20Sopenharmony_cimodule_param(rx_copybreak, int, 0);
1438c2ecf20Sopenharmony_cimodule_param(dspcfg_workaround, int, 0);
1448c2ecf20Sopenharmony_cimodule_param_array(options, int, NULL, 0);
1458c2ecf20Sopenharmony_cimodule_param_array(full_duplex, int, NULL, 0);
1468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mtu, "DP8381x MTU (all boards)");
1478c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "DP8381x default debug level");
1488c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rx_copybreak,
1498c2ecf20Sopenharmony_ci	"DP8381x copy breakpoint for copy-only-tiny-frames");
1508c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dspcfg_workaround, "DP8381x: control DspCfg workaround");
1518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(options,
1528c2ecf20Sopenharmony_ci	"DP8381x: Bits 0-3: media type, bit 17: full duplex");
1538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(full_duplex, "DP8381x full duplex setting(s) (1)");
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci/*
1568c2ecf20Sopenharmony_ci				Theory of Operation
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ciI. Board Compatibility
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ciThis driver is designed for National Semiconductor DP83815 PCI Ethernet NIC.
1618c2ecf20Sopenharmony_ciIt also works with other chips in in the DP83810 series.
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ciII. Board-specific settings
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ciThis driver requires the PCI interrupt line to be valid.
1668c2ecf20Sopenharmony_ciIt honors the EEPROM-set values.
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ciIII. Driver operation
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ciIIIa. Ring buffers
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ciThis driver uses two statically allocated fixed-size descriptor lists
1738c2ecf20Sopenharmony_ciformed into rings by a branch from the final descriptor to the beginning of
1748c2ecf20Sopenharmony_cithe list.  The ring sizes are set at compile time by RX/TX_RING_SIZE.
1758c2ecf20Sopenharmony_ciThe NatSemi design uses a 'next descriptor' pointer that the driver forms
1768c2ecf20Sopenharmony_ciinto a list.
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ciIIIb/c. Transmit/Receive Structure
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ciThis driver uses a zero-copy receive and transmit scheme.
1818c2ecf20Sopenharmony_ciThe driver allocates full frame size skbuffs for the Rx ring buffers at
1828c2ecf20Sopenharmony_ciopen() time and passes the skb->data field to the chip as receive data
1838c2ecf20Sopenharmony_cibuffers.  When an incoming frame is less than RX_COPYBREAK bytes long,
1848c2ecf20Sopenharmony_cia fresh skbuff is allocated and the frame is copied to the new skbuff.
1858c2ecf20Sopenharmony_ciWhen the incoming frame is larger, the skbuff is passed directly up the
1868c2ecf20Sopenharmony_ciprotocol stack.  Buffers consumed this way are replaced by newly allocated
1878c2ecf20Sopenharmony_ciskbuffs in a later phase of receives.
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ciThe RX_COPYBREAK value is chosen to trade-off the memory wasted by
1908c2ecf20Sopenharmony_ciusing a full-sized skbuff for small frames vs. the copying costs of larger
1918c2ecf20Sopenharmony_ciframes.  New boards are typically used in generously configured machines
1928c2ecf20Sopenharmony_ciand the underfilled buffers have negligible impact compared to the benefit of
1938c2ecf20Sopenharmony_cia single allocation size, so the default value of zero results in never
1948c2ecf20Sopenharmony_cicopying packets.  When copying is done, the cost is usually mitigated by using
1958c2ecf20Sopenharmony_cia combined copy/checksum routine.  Copying also preloads the cache, which is
1968c2ecf20Sopenharmony_cimost useful with small frames.
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ciA subtle aspect of the operation is that unaligned buffers are not permitted
1998c2ecf20Sopenharmony_ciby the hardware.  Thus the IP header at offset 14 in an ethernet frame isn't
2008c2ecf20Sopenharmony_cilongword aligned for further processing.  On copies frames are put into the
2018c2ecf20Sopenharmony_ciskbuff at an offset of "+2", 16-byte aligning the IP header.
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ciIIId. Synchronization
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ciMost operations are synchronized on the np->lock irq spinlock, except the
2068c2ecf20Sopenharmony_cireceive and transmit paths which are synchronised using a combination of
2078c2ecf20Sopenharmony_cihardware descriptor ownership, disabling interrupts and NAPI poll scheduling.
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ciIVb. References
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cihttp://www.scyld.com/expert/100mbps.html
2128c2ecf20Sopenharmony_cihttp://www.scyld.com/expert/NWay.html
2138c2ecf20Sopenharmony_ciDatasheet is available from:
2148c2ecf20Sopenharmony_cihttp://www.national.com/pf/DP/DP83815.html
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ciIVc. Errata
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ciNone characterised.
2198c2ecf20Sopenharmony_ci*/
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci/*
2248c2ecf20Sopenharmony_ci * Support for fibre connections on Am79C874:
2258c2ecf20Sopenharmony_ci * This phy needs a special setup when connected to a fibre cable.
2268c2ecf20Sopenharmony_ci * http://www.amd.com/files/connectivitysolutions/networking/archivednetworking/22235.pdf
2278c2ecf20Sopenharmony_ci */
2288c2ecf20Sopenharmony_ci#define PHYID_AM79C874	0x0022561b
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cienum {
2318c2ecf20Sopenharmony_ci	MII_MCTRL	= 0x15,		/* mode control register */
2328c2ecf20Sopenharmony_ci	MII_FX_SEL	= 0x0001,	/* 100BASE-FX (fiber) */
2338c2ecf20Sopenharmony_ci	MII_EN_SCRM	= 0x0004,	/* enable scrambler (tp) */
2348c2ecf20Sopenharmony_ci};
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cienum {
2378c2ecf20Sopenharmony_ci	NATSEMI_FLAG_IGNORE_PHY		= 0x1,
2388c2ecf20Sopenharmony_ci};
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci/* array of board data directly indexed by pci_tbl[x].driver_data */
2418c2ecf20Sopenharmony_cistatic struct {
2428c2ecf20Sopenharmony_ci	const char *name;
2438c2ecf20Sopenharmony_ci	unsigned long flags;
2448c2ecf20Sopenharmony_ci	unsigned int eeprom_size;
2458c2ecf20Sopenharmony_ci} natsemi_pci_info[] = {
2468c2ecf20Sopenharmony_ci	{ "Aculab E1/T1 PMXc cPCI carrier card", NATSEMI_FLAG_IGNORE_PHY, 128 },
2478c2ecf20Sopenharmony_ci	{ "NatSemi DP8381[56]", 0, 24 },
2488c2ecf20Sopenharmony_ci};
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic const struct pci_device_id natsemi_pci_tbl[] = {
2518c2ecf20Sopenharmony_ci	{ PCI_VENDOR_ID_NS, 0x0020, 0x12d9,     0x000c,     0, 0, 0 },
2528c2ecf20Sopenharmony_ci	{ PCI_VENDOR_ID_NS, 0x0020, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },
2538c2ecf20Sopenharmony_ci	{ }	/* terminate list */
2548c2ecf20Sopenharmony_ci};
2558c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, natsemi_pci_tbl);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci/* Offsets to the device registers.
2588c2ecf20Sopenharmony_ci   Unlike software-only systems, device drivers interact with complex hardware.
2598c2ecf20Sopenharmony_ci   It's not useful to define symbolic names for every register bit in the
2608c2ecf20Sopenharmony_ci   device.
2618c2ecf20Sopenharmony_ci*/
2628c2ecf20Sopenharmony_cienum register_offsets {
2638c2ecf20Sopenharmony_ci	ChipCmd			= 0x00,
2648c2ecf20Sopenharmony_ci	ChipConfig		= 0x04,
2658c2ecf20Sopenharmony_ci	EECtrl			= 0x08,
2668c2ecf20Sopenharmony_ci	PCIBusCfg		= 0x0C,
2678c2ecf20Sopenharmony_ci	IntrStatus		= 0x10,
2688c2ecf20Sopenharmony_ci	IntrMask		= 0x14,
2698c2ecf20Sopenharmony_ci	IntrEnable		= 0x18,
2708c2ecf20Sopenharmony_ci	IntrHoldoff		= 0x1C, /* DP83816 only */
2718c2ecf20Sopenharmony_ci	TxRingPtr		= 0x20,
2728c2ecf20Sopenharmony_ci	TxConfig		= 0x24,
2738c2ecf20Sopenharmony_ci	RxRingPtr		= 0x30,
2748c2ecf20Sopenharmony_ci	RxConfig		= 0x34,
2758c2ecf20Sopenharmony_ci	ClkRun			= 0x3C,
2768c2ecf20Sopenharmony_ci	WOLCmd			= 0x40,
2778c2ecf20Sopenharmony_ci	PauseCmd		= 0x44,
2788c2ecf20Sopenharmony_ci	RxFilterAddr		= 0x48,
2798c2ecf20Sopenharmony_ci	RxFilterData		= 0x4C,
2808c2ecf20Sopenharmony_ci	BootRomAddr		= 0x50,
2818c2ecf20Sopenharmony_ci	BootRomData		= 0x54,
2828c2ecf20Sopenharmony_ci	SiliconRev		= 0x58,
2838c2ecf20Sopenharmony_ci	StatsCtrl		= 0x5C,
2848c2ecf20Sopenharmony_ci	StatsData		= 0x60,
2858c2ecf20Sopenharmony_ci	RxPktErrs		= 0x60,
2868c2ecf20Sopenharmony_ci	RxMissed		= 0x68,
2878c2ecf20Sopenharmony_ci	RxCRCErrs		= 0x64,
2888c2ecf20Sopenharmony_ci	BasicControl		= 0x80,
2898c2ecf20Sopenharmony_ci	BasicStatus		= 0x84,
2908c2ecf20Sopenharmony_ci	AnegAdv			= 0x90,
2918c2ecf20Sopenharmony_ci	AnegPeer		= 0x94,
2928c2ecf20Sopenharmony_ci	PhyStatus		= 0xC0,
2938c2ecf20Sopenharmony_ci	MIntrCtrl		= 0xC4,
2948c2ecf20Sopenharmony_ci	MIntrStatus		= 0xC8,
2958c2ecf20Sopenharmony_ci	PhyCtrl			= 0xE4,
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	/* These are from the spec, around page 78... on a separate table.
2988c2ecf20Sopenharmony_ci	 * The meaning of these registers depend on the value of PGSEL. */
2998c2ecf20Sopenharmony_ci	PGSEL			= 0xCC,
3008c2ecf20Sopenharmony_ci	PMDCSR			= 0xE4,
3018c2ecf20Sopenharmony_ci	TSTDAT			= 0xFC,
3028c2ecf20Sopenharmony_ci	DSPCFG			= 0xF4,
3038c2ecf20Sopenharmony_ci	SDCFG			= 0xF8
3048c2ecf20Sopenharmony_ci};
3058c2ecf20Sopenharmony_ci/* the values for the 'magic' registers above (PGSEL=1) */
3068c2ecf20Sopenharmony_ci#define PMDCSR_VAL	0x189c	/* enable preferred adaptation circuitry */
3078c2ecf20Sopenharmony_ci#define TSTDAT_VAL	0x0
3088c2ecf20Sopenharmony_ci#define DSPCFG_VAL	0x5040
3098c2ecf20Sopenharmony_ci#define SDCFG_VAL	0x008c	/* set voltage thresholds for Signal Detect */
3108c2ecf20Sopenharmony_ci#define DSPCFG_LOCK	0x20	/* coefficient lock bit in DSPCFG */
3118c2ecf20Sopenharmony_ci#define DSPCFG_COEF	0x1000	/* see coefficient (in TSTDAT) bit in DSPCFG */
3128c2ecf20Sopenharmony_ci#define TSTDAT_FIXED	0xe8	/* magic number for bad coefficients */
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci/* misc PCI space registers */
3158c2ecf20Sopenharmony_cienum pci_register_offsets {
3168c2ecf20Sopenharmony_ci	PCIPM			= 0x44,
3178c2ecf20Sopenharmony_ci};
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cienum ChipCmd_bits {
3208c2ecf20Sopenharmony_ci	ChipReset		= 0x100,
3218c2ecf20Sopenharmony_ci	RxReset			= 0x20,
3228c2ecf20Sopenharmony_ci	TxReset			= 0x10,
3238c2ecf20Sopenharmony_ci	RxOff			= 0x08,
3248c2ecf20Sopenharmony_ci	RxOn			= 0x04,
3258c2ecf20Sopenharmony_ci	TxOff			= 0x02,
3268c2ecf20Sopenharmony_ci	TxOn			= 0x01,
3278c2ecf20Sopenharmony_ci};
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cienum ChipConfig_bits {
3308c2ecf20Sopenharmony_ci	CfgPhyDis		= 0x200,
3318c2ecf20Sopenharmony_ci	CfgPhyRst		= 0x400,
3328c2ecf20Sopenharmony_ci	CfgExtPhy		= 0x1000,
3338c2ecf20Sopenharmony_ci	CfgAnegEnable		= 0x2000,
3348c2ecf20Sopenharmony_ci	CfgAneg100		= 0x4000,
3358c2ecf20Sopenharmony_ci	CfgAnegFull		= 0x8000,
3368c2ecf20Sopenharmony_ci	CfgAnegDone		= 0x8000000,
3378c2ecf20Sopenharmony_ci	CfgFullDuplex		= 0x20000000,
3388c2ecf20Sopenharmony_ci	CfgSpeed100		= 0x40000000,
3398c2ecf20Sopenharmony_ci	CfgLink			= 0x80000000,
3408c2ecf20Sopenharmony_ci};
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cienum EECtrl_bits {
3438c2ecf20Sopenharmony_ci	EE_ShiftClk		= 0x04,
3448c2ecf20Sopenharmony_ci	EE_DataIn		= 0x01,
3458c2ecf20Sopenharmony_ci	EE_ChipSelect		= 0x08,
3468c2ecf20Sopenharmony_ci	EE_DataOut		= 0x02,
3478c2ecf20Sopenharmony_ci	MII_Data 		= 0x10,
3488c2ecf20Sopenharmony_ci	MII_Write		= 0x20,
3498c2ecf20Sopenharmony_ci	MII_ShiftClk		= 0x40,
3508c2ecf20Sopenharmony_ci};
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cienum PCIBusCfg_bits {
3538c2ecf20Sopenharmony_ci	EepromReload		= 0x4,
3548c2ecf20Sopenharmony_ci};
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci/* Bits in the interrupt status/mask registers. */
3578c2ecf20Sopenharmony_cienum IntrStatus_bits {
3588c2ecf20Sopenharmony_ci	IntrRxDone		= 0x0001,
3598c2ecf20Sopenharmony_ci	IntrRxIntr		= 0x0002,
3608c2ecf20Sopenharmony_ci	IntrRxErr		= 0x0004,
3618c2ecf20Sopenharmony_ci	IntrRxEarly		= 0x0008,
3628c2ecf20Sopenharmony_ci	IntrRxIdle		= 0x0010,
3638c2ecf20Sopenharmony_ci	IntrRxOverrun		= 0x0020,
3648c2ecf20Sopenharmony_ci	IntrTxDone		= 0x0040,
3658c2ecf20Sopenharmony_ci	IntrTxIntr		= 0x0080,
3668c2ecf20Sopenharmony_ci	IntrTxErr		= 0x0100,
3678c2ecf20Sopenharmony_ci	IntrTxIdle		= 0x0200,
3688c2ecf20Sopenharmony_ci	IntrTxUnderrun		= 0x0400,
3698c2ecf20Sopenharmony_ci	StatsMax		= 0x0800,
3708c2ecf20Sopenharmony_ci	SWInt			= 0x1000,
3718c2ecf20Sopenharmony_ci	WOLPkt			= 0x2000,
3728c2ecf20Sopenharmony_ci	LinkChange		= 0x4000,
3738c2ecf20Sopenharmony_ci	IntrHighBits		= 0x8000,
3748c2ecf20Sopenharmony_ci	RxStatusFIFOOver	= 0x10000,
3758c2ecf20Sopenharmony_ci	IntrPCIErr		= 0xf00000,
3768c2ecf20Sopenharmony_ci	RxResetDone		= 0x1000000,
3778c2ecf20Sopenharmony_ci	TxResetDone		= 0x2000000,
3788c2ecf20Sopenharmony_ci	IntrAbnormalSummary	= 0xCD20,
3798c2ecf20Sopenharmony_ci};
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci/*
3828c2ecf20Sopenharmony_ci * Default Interrupts:
3838c2ecf20Sopenharmony_ci * Rx OK, Rx Packet Error, Rx Overrun,
3848c2ecf20Sopenharmony_ci * Tx OK, Tx Packet Error, Tx Underrun,
3858c2ecf20Sopenharmony_ci * MIB Service, Phy Interrupt, High Bits,
3868c2ecf20Sopenharmony_ci * Rx Status FIFO overrun,
3878c2ecf20Sopenharmony_ci * Received Target Abort, Received Master Abort,
3888c2ecf20Sopenharmony_ci * Signalled System Error, Received Parity Error
3898c2ecf20Sopenharmony_ci */
3908c2ecf20Sopenharmony_ci#define DEFAULT_INTR 0x00f1cd65
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cienum TxConfig_bits {
3938c2ecf20Sopenharmony_ci	TxDrthMask		= 0x3f,
3948c2ecf20Sopenharmony_ci	TxFlthMask		= 0x3f00,
3958c2ecf20Sopenharmony_ci	TxMxdmaMask		= 0x700000,
3968c2ecf20Sopenharmony_ci	TxMxdma_512		= 0x0,
3978c2ecf20Sopenharmony_ci	TxMxdma_4		= 0x100000,
3988c2ecf20Sopenharmony_ci	TxMxdma_8		= 0x200000,
3998c2ecf20Sopenharmony_ci	TxMxdma_16		= 0x300000,
4008c2ecf20Sopenharmony_ci	TxMxdma_32		= 0x400000,
4018c2ecf20Sopenharmony_ci	TxMxdma_64		= 0x500000,
4028c2ecf20Sopenharmony_ci	TxMxdma_128		= 0x600000,
4038c2ecf20Sopenharmony_ci	TxMxdma_256		= 0x700000,
4048c2ecf20Sopenharmony_ci	TxCollRetry		= 0x800000,
4058c2ecf20Sopenharmony_ci	TxAutoPad		= 0x10000000,
4068c2ecf20Sopenharmony_ci	TxMacLoop		= 0x20000000,
4078c2ecf20Sopenharmony_ci	TxHeartIgn		= 0x40000000,
4088c2ecf20Sopenharmony_ci	TxCarrierIgn		= 0x80000000
4098c2ecf20Sopenharmony_ci};
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci/*
4128c2ecf20Sopenharmony_ci * Tx Configuration:
4138c2ecf20Sopenharmony_ci * - 256 byte DMA burst length
4148c2ecf20Sopenharmony_ci * - fill threshold 512 bytes (i.e. restart DMA when 512 bytes are free)
4158c2ecf20Sopenharmony_ci * - 64 bytes initial drain threshold (i.e. begin actual transmission
4168c2ecf20Sopenharmony_ci *   when 64 byte are in the fifo)
4178c2ecf20Sopenharmony_ci * - on tx underruns, increase drain threshold by 64.
4188c2ecf20Sopenharmony_ci * - at most use a drain threshold of 1472 bytes: The sum of the fill
4198c2ecf20Sopenharmony_ci *   threshold and the drain threshold must be less than 2016 bytes.
4208c2ecf20Sopenharmony_ci *
4218c2ecf20Sopenharmony_ci */
4228c2ecf20Sopenharmony_ci#define TX_FLTH_VAL		((512/32) << 8)
4238c2ecf20Sopenharmony_ci#define TX_DRTH_VAL_START	(64/32)
4248c2ecf20Sopenharmony_ci#define TX_DRTH_VAL_INC		2
4258c2ecf20Sopenharmony_ci#define TX_DRTH_VAL_LIMIT	(1472/32)
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cienum RxConfig_bits {
4288c2ecf20Sopenharmony_ci	RxDrthMask		= 0x3e,
4298c2ecf20Sopenharmony_ci	RxMxdmaMask		= 0x700000,
4308c2ecf20Sopenharmony_ci	RxMxdma_512		= 0x0,
4318c2ecf20Sopenharmony_ci	RxMxdma_4		= 0x100000,
4328c2ecf20Sopenharmony_ci	RxMxdma_8		= 0x200000,
4338c2ecf20Sopenharmony_ci	RxMxdma_16		= 0x300000,
4348c2ecf20Sopenharmony_ci	RxMxdma_32		= 0x400000,
4358c2ecf20Sopenharmony_ci	RxMxdma_64		= 0x500000,
4368c2ecf20Sopenharmony_ci	RxMxdma_128		= 0x600000,
4378c2ecf20Sopenharmony_ci	RxMxdma_256		= 0x700000,
4388c2ecf20Sopenharmony_ci	RxAcceptLong		= 0x8000000,
4398c2ecf20Sopenharmony_ci	RxAcceptTx		= 0x10000000,
4408c2ecf20Sopenharmony_ci	RxAcceptRunt		= 0x40000000,
4418c2ecf20Sopenharmony_ci	RxAcceptErr		= 0x80000000
4428c2ecf20Sopenharmony_ci};
4438c2ecf20Sopenharmony_ci#define RX_DRTH_VAL		(128/8)
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_cienum ClkRun_bits {
4468c2ecf20Sopenharmony_ci	PMEEnable		= 0x100,
4478c2ecf20Sopenharmony_ci	PMEStatus		= 0x8000,
4488c2ecf20Sopenharmony_ci};
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cienum WolCmd_bits {
4518c2ecf20Sopenharmony_ci	WakePhy			= 0x1,
4528c2ecf20Sopenharmony_ci	WakeUnicast		= 0x2,
4538c2ecf20Sopenharmony_ci	WakeMulticast		= 0x4,
4548c2ecf20Sopenharmony_ci	WakeBroadcast		= 0x8,
4558c2ecf20Sopenharmony_ci	WakeArp			= 0x10,
4568c2ecf20Sopenharmony_ci	WakePMatch0		= 0x20,
4578c2ecf20Sopenharmony_ci	WakePMatch1		= 0x40,
4588c2ecf20Sopenharmony_ci	WakePMatch2		= 0x80,
4598c2ecf20Sopenharmony_ci	WakePMatch3		= 0x100,
4608c2ecf20Sopenharmony_ci	WakeMagic		= 0x200,
4618c2ecf20Sopenharmony_ci	WakeMagicSecure		= 0x400,
4628c2ecf20Sopenharmony_ci	SecureHack		= 0x100000,
4638c2ecf20Sopenharmony_ci	WokePhy			= 0x400000,
4648c2ecf20Sopenharmony_ci	WokeUnicast		= 0x800000,
4658c2ecf20Sopenharmony_ci	WokeMulticast		= 0x1000000,
4668c2ecf20Sopenharmony_ci	WokeBroadcast		= 0x2000000,
4678c2ecf20Sopenharmony_ci	WokeArp			= 0x4000000,
4688c2ecf20Sopenharmony_ci	WokePMatch0		= 0x8000000,
4698c2ecf20Sopenharmony_ci	WokePMatch1		= 0x10000000,
4708c2ecf20Sopenharmony_ci	WokePMatch2		= 0x20000000,
4718c2ecf20Sopenharmony_ci	WokePMatch3		= 0x40000000,
4728c2ecf20Sopenharmony_ci	WokeMagic		= 0x80000000,
4738c2ecf20Sopenharmony_ci	WakeOptsSummary		= 0x7ff
4748c2ecf20Sopenharmony_ci};
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cienum RxFilterAddr_bits {
4778c2ecf20Sopenharmony_ci	RFCRAddressMask		= 0x3ff,
4788c2ecf20Sopenharmony_ci	AcceptMulticast		= 0x00200000,
4798c2ecf20Sopenharmony_ci	AcceptMyPhys		= 0x08000000,
4808c2ecf20Sopenharmony_ci	AcceptAllPhys		= 0x10000000,
4818c2ecf20Sopenharmony_ci	AcceptAllMulticast	= 0x20000000,
4828c2ecf20Sopenharmony_ci	AcceptBroadcast		= 0x40000000,
4838c2ecf20Sopenharmony_ci	RxFilterEnable		= 0x80000000
4848c2ecf20Sopenharmony_ci};
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cienum StatsCtrl_bits {
4878c2ecf20Sopenharmony_ci	StatsWarn		= 0x1,
4888c2ecf20Sopenharmony_ci	StatsFreeze		= 0x2,
4898c2ecf20Sopenharmony_ci	StatsClear		= 0x4,
4908c2ecf20Sopenharmony_ci	StatsStrobe		= 0x8,
4918c2ecf20Sopenharmony_ci};
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_cienum MIntrCtrl_bits {
4948c2ecf20Sopenharmony_ci	MICRIntEn		= 0x2,
4958c2ecf20Sopenharmony_ci};
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_cienum PhyCtrl_bits {
4988c2ecf20Sopenharmony_ci	PhyAddrMask		= 0x1f,
4998c2ecf20Sopenharmony_ci};
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci#define PHY_ADDR_NONE		32
5028c2ecf20Sopenharmony_ci#define PHY_ADDR_INTERNAL	1
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci/* values we might find in the silicon revision register */
5058c2ecf20Sopenharmony_ci#define SRR_DP83815_C	0x0302
5068c2ecf20Sopenharmony_ci#define SRR_DP83815_D	0x0403
5078c2ecf20Sopenharmony_ci#define SRR_DP83816_A4	0x0504
5088c2ecf20Sopenharmony_ci#define SRR_DP83816_A5	0x0505
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci/* The Rx and Tx buffer descriptors. */
5118c2ecf20Sopenharmony_ci/* Note that using only 32 bit fields simplifies conversion to big-endian
5128c2ecf20Sopenharmony_ci   architectures. */
5138c2ecf20Sopenharmony_cistruct netdev_desc {
5148c2ecf20Sopenharmony_ci	__le32 next_desc;
5158c2ecf20Sopenharmony_ci	__le32 cmd_status;
5168c2ecf20Sopenharmony_ci	__le32 addr;
5178c2ecf20Sopenharmony_ci	__le32 software_use;
5188c2ecf20Sopenharmony_ci};
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci/* Bits in network_desc.status */
5218c2ecf20Sopenharmony_cienum desc_status_bits {
5228c2ecf20Sopenharmony_ci	DescOwn=0x80000000, DescMore=0x40000000, DescIntr=0x20000000,
5238c2ecf20Sopenharmony_ci	DescNoCRC=0x10000000, DescPktOK=0x08000000,
5248c2ecf20Sopenharmony_ci	DescSizeMask=0xfff,
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	DescTxAbort=0x04000000, DescTxFIFO=0x02000000,
5278c2ecf20Sopenharmony_ci	DescTxCarrier=0x01000000, DescTxDefer=0x00800000,
5288c2ecf20Sopenharmony_ci	DescTxExcDefer=0x00400000, DescTxOOWCol=0x00200000,
5298c2ecf20Sopenharmony_ci	DescTxExcColl=0x00100000, DescTxCollCount=0x000f0000,
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	DescRxAbort=0x04000000, DescRxOver=0x02000000,
5328c2ecf20Sopenharmony_ci	DescRxDest=0x01800000, DescRxLong=0x00400000,
5338c2ecf20Sopenharmony_ci	DescRxRunt=0x00200000, DescRxInvalid=0x00100000,
5348c2ecf20Sopenharmony_ci	DescRxCRC=0x00080000, DescRxAlign=0x00040000,
5358c2ecf20Sopenharmony_ci	DescRxLoop=0x00020000, DesRxColl=0x00010000,
5368c2ecf20Sopenharmony_ci};
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_cistruct netdev_private {
5398c2ecf20Sopenharmony_ci	/* Descriptor rings first for alignment */
5408c2ecf20Sopenharmony_ci	dma_addr_t ring_dma;
5418c2ecf20Sopenharmony_ci	struct netdev_desc *rx_ring;
5428c2ecf20Sopenharmony_ci	struct netdev_desc *tx_ring;
5438c2ecf20Sopenharmony_ci	/* The addresses of receive-in-place skbuffs */
5448c2ecf20Sopenharmony_ci	struct sk_buff *rx_skbuff[RX_RING_SIZE];
5458c2ecf20Sopenharmony_ci	dma_addr_t rx_dma[RX_RING_SIZE];
5468c2ecf20Sopenharmony_ci	/* address of a sent-in-place packet/buffer, for later free() */
5478c2ecf20Sopenharmony_ci	struct sk_buff *tx_skbuff[TX_RING_SIZE];
5488c2ecf20Sopenharmony_ci	dma_addr_t tx_dma[TX_RING_SIZE];
5498c2ecf20Sopenharmony_ci	struct net_device *dev;
5508c2ecf20Sopenharmony_ci	void __iomem *ioaddr;
5518c2ecf20Sopenharmony_ci	struct napi_struct napi;
5528c2ecf20Sopenharmony_ci	/* Media monitoring timer */
5538c2ecf20Sopenharmony_ci	struct timer_list timer;
5548c2ecf20Sopenharmony_ci	/* Frequently used values: keep some adjacent for cache effect */
5558c2ecf20Sopenharmony_ci	struct pci_dev *pci_dev;
5568c2ecf20Sopenharmony_ci	struct netdev_desc *rx_head_desc;
5578c2ecf20Sopenharmony_ci	/* Producer/consumer ring indices */
5588c2ecf20Sopenharmony_ci	unsigned int cur_rx, dirty_rx;
5598c2ecf20Sopenharmony_ci	unsigned int cur_tx, dirty_tx;
5608c2ecf20Sopenharmony_ci	/* Based on MTU+slack. */
5618c2ecf20Sopenharmony_ci	unsigned int rx_buf_sz;
5628c2ecf20Sopenharmony_ci	int oom;
5638c2ecf20Sopenharmony_ci	/* Interrupt status */
5648c2ecf20Sopenharmony_ci	u32 intr_status;
5658c2ecf20Sopenharmony_ci	/* Do not touch the nic registers */
5668c2ecf20Sopenharmony_ci	int hands_off;
5678c2ecf20Sopenharmony_ci	/* Don't pay attention to the reported link state. */
5688c2ecf20Sopenharmony_ci	int ignore_phy;
5698c2ecf20Sopenharmony_ci	/* external phy that is used: only valid if dev->if_port != PORT_TP */
5708c2ecf20Sopenharmony_ci	int mii;
5718c2ecf20Sopenharmony_ci	int phy_addr_external;
5728c2ecf20Sopenharmony_ci	unsigned int full_duplex;
5738c2ecf20Sopenharmony_ci	/* Rx filter */
5748c2ecf20Sopenharmony_ci	u32 cur_rx_mode;
5758c2ecf20Sopenharmony_ci	u32 rx_filter[16];
5768c2ecf20Sopenharmony_ci	/* FIFO and PCI burst thresholds */
5778c2ecf20Sopenharmony_ci	u32 tx_config, rx_config;
5788c2ecf20Sopenharmony_ci	/* original contents of ClkRun register */
5798c2ecf20Sopenharmony_ci	u32 SavedClkRun;
5808c2ecf20Sopenharmony_ci	/* silicon revision */
5818c2ecf20Sopenharmony_ci	u32 srr;
5828c2ecf20Sopenharmony_ci	/* expected DSPCFG value */
5838c2ecf20Sopenharmony_ci	u16 dspcfg;
5848c2ecf20Sopenharmony_ci	int dspcfg_workaround;
5858c2ecf20Sopenharmony_ci	/* parms saved in ethtool format */
5868c2ecf20Sopenharmony_ci	u16	speed;		/* The forced speed, 10Mb, 100Mb, gigabit */
5878c2ecf20Sopenharmony_ci	u8	duplex;		/* Duplex, half or full */
5888c2ecf20Sopenharmony_ci	u8	autoneg;	/* Autonegotiation enabled */
5898c2ecf20Sopenharmony_ci	/* MII transceiver section */
5908c2ecf20Sopenharmony_ci	u16 advertising;
5918c2ecf20Sopenharmony_ci	unsigned int iosize;
5928c2ecf20Sopenharmony_ci	spinlock_t lock;
5938c2ecf20Sopenharmony_ci	u32 msg_enable;
5948c2ecf20Sopenharmony_ci	/* EEPROM data */
5958c2ecf20Sopenharmony_ci	int eeprom_size;
5968c2ecf20Sopenharmony_ci};
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_cistatic void move_int_phy(struct net_device *dev, int addr);
5998c2ecf20Sopenharmony_cistatic int eeprom_read(void __iomem *ioaddr, int location);
6008c2ecf20Sopenharmony_cistatic int mdio_read(struct net_device *dev, int reg);
6018c2ecf20Sopenharmony_cistatic void mdio_write(struct net_device *dev, int reg, u16 data);
6028c2ecf20Sopenharmony_cistatic void init_phy_fixup(struct net_device *dev);
6038c2ecf20Sopenharmony_cistatic int miiport_read(struct net_device *dev, int phy_id, int reg);
6048c2ecf20Sopenharmony_cistatic void miiport_write(struct net_device *dev, int phy_id, int reg, u16 data);
6058c2ecf20Sopenharmony_cistatic int find_mii(struct net_device *dev);
6068c2ecf20Sopenharmony_cistatic void natsemi_reset(struct net_device *dev);
6078c2ecf20Sopenharmony_cistatic void natsemi_reload_eeprom(struct net_device *dev);
6088c2ecf20Sopenharmony_cistatic void natsemi_stop_rxtx(struct net_device *dev);
6098c2ecf20Sopenharmony_cistatic int netdev_open(struct net_device *dev);
6108c2ecf20Sopenharmony_cistatic void do_cable_magic(struct net_device *dev);
6118c2ecf20Sopenharmony_cistatic void undo_cable_magic(struct net_device *dev);
6128c2ecf20Sopenharmony_cistatic void check_link(struct net_device *dev);
6138c2ecf20Sopenharmony_cistatic void netdev_timer(struct timer_list *t);
6148c2ecf20Sopenharmony_cistatic void dump_ring(struct net_device *dev);
6158c2ecf20Sopenharmony_cistatic void ns_tx_timeout(struct net_device *dev, unsigned int txqueue);
6168c2ecf20Sopenharmony_cistatic int alloc_ring(struct net_device *dev);
6178c2ecf20Sopenharmony_cistatic void refill_rx(struct net_device *dev);
6188c2ecf20Sopenharmony_cistatic void init_ring(struct net_device *dev);
6198c2ecf20Sopenharmony_cistatic void drain_tx(struct net_device *dev);
6208c2ecf20Sopenharmony_cistatic void drain_ring(struct net_device *dev);
6218c2ecf20Sopenharmony_cistatic void free_ring(struct net_device *dev);
6228c2ecf20Sopenharmony_cistatic void reinit_ring(struct net_device *dev);
6238c2ecf20Sopenharmony_cistatic void init_registers(struct net_device *dev);
6248c2ecf20Sopenharmony_cistatic netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev);
6258c2ecf20Sopenharmony_cistatic irqreturn_t intr_handler(int irq, void *dev_instance);
6268c2ecf20Sopenharmony_cistatic void netdev_error(struct net_device *dev, int intr_status);
6278c2ecf20Sopenharmony_cistatic int natsemi_poll(struct napi_struct *napi, int budget);
6288c2ecf20Sopenharmony_cistatic void netdev_rx(struct net_device *dev, int *work_done, int work_to_do);
6298c2ecf20Sopenharmony_cistatic void netdev_tx_done(struct net_device *dev);
6308c2ecf20Sopenharmony_cistatic int natsemi_change_mtu(struct net_device *dev, int new_mtu);
6318c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
6328c2ecf20Sopenharmony_cistatic void natsemi_poll_controller(struct net_device *dev);
6338c2ecf20Sopenharmony_ci#endif
6348c2ecf20Sopenharmony_cistatic void __set_rx_mode(struct net_device *dev);
6358c2ecf20Sopenharmony_cistatic void set_rx_mode(struct net_device *dev);
6368c2ecf20Sopenharmony_cistatic void __get_stats(struct net_device *dev);
6378c2ecf20Sopenharmony_cistatic struct net_device_stats *get_stats(struct net_device *dev);
6388c2ecf20Sopenharmony_cistatic int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
6398c2ecf20Sopenharmony_cistatic int netdev_set_wol(struct net_device *dev, u32 newval);
6408c2ecf20Sopenharmony_cistatic int netdev_get_wol(struct net_device *dev, u32 *supported, u32 *cur);
6418c2ecf20Sopenharmony_cistatic int netdev_set_sopass(struct net_device *dev, u8 *newval);
6428c2ecf20Sopenharmony_cistatic int netdev_get_sopass(struct net_device *dev, u8 *data);
6438c2ecf20Sopenharmony_cistatic int netdev_get_ecmd(struct net_device *dev,
6448c2ecf20Sopenharmony_ci			   struct ethtool_link_ksettings *ecmd);
6458c2ecf20Sopenharmony_cistatic int netdev_set_ecmd(struct net_device *dev,
6468c2ecf20Sopenharmony_ci			   const struct ethtool_link_ksettings *ecmd);
6478c2ecf20Sopenharmony_cistatic void enable_wol_mode(struct net_device *dev, int enable_intr);
6488c2ecf20Sopenharmony_cistatic int netdev_close(struct net_device *dev);
6498c2ecf20Sopenharmony_cistatic int netdev_get_regs(struct net_device *dev, u8 *buf);
6508c2ecf20Sopenharmony_cistatic int netdev_get_eeprom(struct net_device *dev, u8 *buf);
6518c2ecf20Sopenharmony_cistatic const struct ethtool_ops ethtool_ops;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci#define NATSEMI_ATTR(_name) \
6548c2ecf20Sopenharmony_cistatic ssize_t natsemi_show_##_name(struct device *dev, \
6558c2ecf20Sopenharmony_ci         struct device_attribute *attr, char *buf); \
6568c2ecf20Sopenharmony_ci	 static ssize_t natsemi_set_##_name(struct device *dev, \
6578c2ecf20Sopenharmony_ci		struct device_attribute *attr, \
6588c2ecf20Sopenharmony_ci	        const char *buf, size_t count); \
6598c2ecf20Sopenharmony_ci	 static DEVICE_ATTR(_name, 0644, natsemi_show_##_name, natsemi_set_##_name)
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci#define NATSEMI_CREATE_FILE(_dev, _name) \
6628c2ecf20Sopenharmony_ci         device_create_file(&_dev->dev, &dev_attr_##_name)
6638c2ecf20Sopenharmony_ci#define NATSEMI_REMOVE_FILE(_dev, _name) \
6648c2ecf20Sopenharmony_ci         device_remove_file(&_dev->dev, &dev_attr_##_name)
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ciNATSEMI_ATTR(dspcfg_workaround);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_cistatic ssize_t natsemi_show_dspcfg_workaround(struct device *dev,
6698c2ecf20Sopenharmony_ci				  	      struct device_attribute *attr,
6708c2ecf20Sopenharmony_ci					      char *buf)
6718c2ecf20Sopenharmony_ci{
6728c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(to_net_dev(dev));
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	return sprintf(buf, "%s\n", np->dspcfg_workaround ? "on" : "off");
6758c2ecf20Sopenharmony_ci}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_cistatic ssize_t natsemi_set_dspcfg_workaround(struct device *dev,
6788c2ecf20Sopenharmony_ci					     struct device_attribute *attr,
6798c2ecf20Sopenharmony_ci					     const char *buf, size_t count)
6808c2ecf20Sopenharmony_ci{
6818c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(to_net_dev(dev));
6828c2ecf20Sopenharmony_ci	int new_setting;
6838c2ecf20Sopenharmony_ci	unsigned long flags;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci        /* Find out the new setting */
6868c2ecf20Sopenharmony_ci        if (!strncmp("on", buf, count - 1) || !strncmp("1", buf, count - 1))
6878c2ecf20Sopenharmony_ci                new_setting = 1;
6888c2ecf20Sopenharmony_ci        else if (!strncmp("off", buf, count - 1) ||
6898c2ecf20Sopenharmony_ci                 !strncmp("0", buf, count - 1))
6908c2ecf20Sopenharmony_ci		new_setting = 0;
6918c2ecf20Sopenharmony_ci	else
6928c2ecf20Sopenharmony_ci                 return count;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	spin_lock_irqsave(&np->lock, flags);
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	np->dspcfg_workaround = new_setting;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&np->lock, flags);
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	return count;
7018c2ecf20Sopenharmony_ci}
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_cistatic inline void __iomem *ns_ioaddr(struct net_device *dev)
7048c2ecf20Sopenharmony_ci{
7058c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	return np->ioaddr;
7088c2ecf20Sopenharmony_ci}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_cistatic inline void natsemi_irq_enable(struct net_device *dev)
7118c2ecf20Sopenharmony_ci{
7128c2ecf20Sopenharmony_ci	writel(1, ns_ioaddr(dev) + IntrEnable);
7138c2ecf20Sopenharmony_ci	readl(ns_ioaddr(dev) + IntrEnable);
7148c2ecf20Sopenharmony_ci}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_cistatic inline void natsemi_irq_disable(struct net_device *dev)
7178c2ecf20Sopenharmony_ci{
7188c2ecf20Sopenharmony_ci	writel(0, ns_ioaddr(dev) + IntrEnable);
7198c2ecf20Sopenharmony_ci	readl(ns_ioaddr(dev) + IntrEnable);
7208c2ecf20Sopenharmony_ci}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_cistatic void move_int_phy(struct net_device *dev, int addr)
7238c2ecf20Sopenharmony_ci{
7248c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
7258c2ecf20Sopenharmony_ci	void __iomem *ioaddr = ns_ioaddr(dev);
7268c2ecf20Sopenharmony_ci	int target = 31;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	/*
7298c2ecf20Sopenharmony_ci	 * The internal phy is visible on the external mii bus. Therefore we must
7308c2ecf20Sopenharmony_ci	 * move it away before we can send commands to an external phy.
7318c2ecf20Sopenharmony_ci	 * There are two addresses we must avoid:
7328c2ecf20Sopenharmony_ci	 * - the address on the external phy that is used for transmission.
7338c2ecf20Sopenharmony_ci	 * - the address that we want to access. User space can access phys
7348c2ecf20Sopenharmony_ci	 *   on the mii bus with SIOCGMIIREG/SIOCSMIIREG, independent from the
7358c2ecf20Sopenharmony_ci	 *   phy that is used for transmission.
7368c2ecf20Sopenharmony_ci	 */
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	if (target == addr)
7398c2ecf20Sopenharmony_ci		target--;
7408c2ecf20Sopenharmony_ci	if (target == np->phy_addr_external)
7418c2ecf20Sopenharmony_ci		target--;
7428c2ecf20Sopenharmony_ci	writew(target, ioaddr + PhyCtrl);
7438c2ecf20Sopenharmony_ci	readw(ioaddr + PhyCtrl);
7448c2ecf20Sopenharmony_ci	udelay(1);
7458c2ecf20Sopenharmony_ci}
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_cistatic void natsemi_init_media(struct net_device *dev)
7488c2ecf20Sopenharmony_ci{
7498c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
7508c2ecf20Sopenharmony_ci	u32 tmp;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	if (np->ignore_phy)
7538c2ecf20Sopenharmony_ci		netif_carrier_on(dev);
7548c2ecf20Sopenharmony_ci	else
7558c2ecf20Sopenharmony_ci		netif_carrier_off(dev);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	/* get the initial settings from hardware */
7588c2ecf20Sopenharmony_ci	tmp            = mdio_read(dev, MII_BMCR);
7598c2ecf20Sopenharmony_ci	np->speed      = (tmp & BMCR_SPEED100)? SPEED_100     : SPEED_10;
7608c2ecf20Sopenharmony_ci	np->duplex     = (tmp & BMCR_FULLDPLX)? DUPLEX_FULL   : DUPLEX_HALF;
7618c2ecf20Sopenharmony_ci	np->autoneg    = (tmp & BMCR_ANENABLE)? AUTONEG_ENABLE: AUTONEG_DISABLE;
7628c2ecf20Sopenharmony_ci	np->advertising= mdio_read(dev, MII_ADVERTISE);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	if ((np->advertising & ADVERTISE_ALL) != ADVERTISE_ALL &&
7658c2ecf20Sopenharmony_ci	    netif_msg_probe(np)) {
7668c2ecf20Sopenharmony_ci		printk(KERN_INFO "natsemi %s: Transceiver default autonegotiation %s "
7678c2ecf20Sopenharmony_ci			"10%s %s duplex.\n",
7688c2ecf20Sopenharmony_ci			pci_name(np->pci_dev),
7698c2ecf20Sopenharmony_ci			(mdio_read(dev, MII_BMCR) & BMCR_ANENABLE)?
7708c2ecf20Sopenharmony_ci			  "enabled, advertise" : "disabled, force",
7718c2ecf20Sopenharmony_ci			(np->advertising &
7728c2ecf20Sopenharmony_ci			  (ADVERTISE_100FULL|ADVERTISE_100HALF))?
7738c2ecf20Sopenharmony_ci			    "0" : "",
7748c2ecf20Sopenharmony_ci			(np->advertising &
7758c2ecf20Sopenharmony_ci			  (ADVERTISE_100FULL|ADVERTISE_10FULL))?
7768c2ecf20Sopenharmony_ci			    "full" : "half");
7778c2ecf20Sopenharmony_ci	}
7788c2ecf20Sopenharmony_ci	if (netif_msg_probe(np))
7798c2ecf20Sopenharmony_ci		printk(KERN_INFO
7808c2ecf20Sopenharmony_ci			"natsemi %s: Transceiver status %#04x advertising %#04x.\n",
7818c2ecf20Sopenharmony_ci			pci_name(np->pci_dev), mdio_read(dev, MII_BMSR),
7828c2ecf20Sopenharmony_ci			np->advertising);
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci}
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_cistatic const struct net_device_ops natsemi_netdev_ops = {
7878c2ecf20Sopenharmony_ci	.ndo_open		= netdev_open,
7888c2ecf20Sopenharmony_ci	.ndo_stop		= netdev_close,
7898c2ecf20Sopenharmony_ci	.ndo_start_xmit		= start_tx,
7908c2ecf20Sopenharmony_ci	.ndo_get_stats		= get_stats,
7918c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= set_rx_mode,
7928c2ecf20Sopenharmony_ci	.ndo_change_mtu		= natsemi_change_mtu,
7938c2ecf20Sopenharmony_ci	.ndo_do_ioctl		= netdev_ioctl,
7948c2ecf20Sopenharmony_ci	.ndo_tx_timeout 	= ns_tx_timeout,
7958c2ecf20Sopenharmony_ci	.ndo_set_mac_address 	= eth_mac_addr,
7968c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
7978c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
7988c2ecf20Sopenharmony_ci	.ndo_poll_controller	= natsemi_poll_controller,
7998c2ecf20Sopenharmony_ci#endif
8008c2ecf20Sopenharmony_ci};
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_cistatic int natsemi_probe1(struct pci_dev *pdev, const struct pci_device_id *ent)
8038c2ecf20Sopenharmony_ci{
8048c2ecf20Sopenharmony_ci	struct net_device *dev;
8058c2ecf20Sopenharmony_ci	struct netdev_private *np;
8068c2ecf20Sopenharmony_ci	int i, option, irq, chip_idx = ent->driver_data;
8078c2ecf20Sopenharmony_ci	static int find_cnt = -1;
8088c2ecf20Sopenharmony_ci	resource_size_t iostart;
8098c2ecf20Sopenharmony_ci	unsigned long iosize;
8108c2ecf20Sopenharmony_ci	void __iomem *ioaddr;
8118c2ecf20Sopenharmony_ci	const int pcibar = 1; /* PCI base address register */
8128c2ecf20Sopenharmony_ci	int prev_eedata;
8138c2ecf20Sopenharmony_ci	u32 tmp;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci/* when built into the kernel, we only print version if device is found */
8168c2ecf20Sopenharmony_ci#ifndef MODULE
8178c2ecf20Sopenharmony_ci	static int printed_version;
8188c2ecf20Sopenharmony_ci	if (!printed_version++)
8198c2ecf20Sopenharmony_ci		printk(version);
8208c2ecf20Sopenharmony_ci#endif
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	i = pcim_enable_device(pdev);
8238c2ecf20Sopenharmony_ci	if (i) return i;
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	/* natsemi has a non-standard PM control register
8268c2ecf20Sopenharmony_ci	 * in PCI config space.  Some boards apparently need
8278c2ecf20Sopenharmony_ci	 * to be brought to D0 in this manner.
8288c2ecf20Sopenharmony_ci	 */
8298c2ecf20Sopenharmony_ci	pci_read_config_dword(pdev, PCIPM, &tmp);
8308c2ecf20Sopenharmony_ci	if (tmp & PCI_PM_CTRL_STATE_MASK) {
8318c2ecf20Sopenharmony_ci		/* D0 state, disable PME assertion */
8328c2ecf20Sopenharmony_ci		u32 newtmp = tmp & ~PCI_PM_CTRL_STATE_MASK;
8338c2ecf20Sopenharmony_ci		pci_write_config_dword(pdev, PCIPM, newtmp);
8348c2ecf20Sopenharmony_ci	}
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	find_cnt++;
8378c2ecf20Sopenharmony_ci	iostart = pci_resource_start(pdev, pcibar);
8388c2ecf20Sopenharmony_ci	iosize = pci_resource_len(pdev, pcibar);
8398c2ecf20Sopenharmony_ci	irq = pdev->irq;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	pci_set_master(pdev);
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	dev = alloc_etherdev(sizeof (struct netdev_private));
8448c2ecf20Sopenharmony_ci	if (!dev)
8458c2ecf20Sopenharmony_ci		return -ENOMEM;
8468c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(dev, &pdev->dev);
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	i = pci_request_regions(pdev, DRV_NAME);
8498c2ecf20Sopenharmony_ci	if (i)
8508c2ecf20Sopenharmony_ci		goto err_pci_request_regions;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	ioaddr = ioremap(iostart, iosize);
8538c2ecf20Sopenharmony_ci	if (!ioaddr) {
8548c2ecf20Sopenharmony_ci		i = -ENOMEM;
8558c2ecf20Sopenharmony_ci		goto err_pci_request_regions;
8568c2ecf20Sopenharmony_ci	}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	/* Work around the dropped serial bit. */
8598c2ecf20Sopenharmony_ci	prev_eedata = eeprom_read(ioaddr, 6);
8608c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
8618c2ecf20Sopenharmony_ci		int eedata = eeprom_read(ioaddr, i + 7);
8628c2ecf20Sopenharmony_ci		dev->dev_addr[i*2] = (eedata << 1) + (prev_eedata >> 15);
8638c2ecf20Sopenharmony_ci		dev->dev_addr[i*2+1] = eedata >> 7;
8648c2ecf20Sopenharmony_ci		prev_eedata = eedata;
8658c2ecf20Sopenharmony_ci	}
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	np = netdev_priv(dev);
8688c2ecf20Sopenharmony_ci	np->ioaddr = ioaddr;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	netif_napi_add(dev, &np->napi, natsemi_poll, 64);
8718c2ecf20Sopenharmony_ci	np->dev = dev;
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	np->pci_dev = pdev;
8748c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, dev);
8758c2ecf20Sopenharmony_ci	np->iosize = iosize;
8768c2ecf20Sopenharmony_ci	spin_lock_init(&np->lock);
8778c2ecf20Sopenharmony_ci	np->msg_enable = (debug >= 0) ? (1<<debug)-1 : NATSEMI_DEF_MSG;
8788c2ecf20Sopenharmony_ci	np->hands_off = 0;
8798c2ecf20Sopenharmony_ci	np->intr_status = 0;
8808c2ecf20Sopenharmony_ci	np->eeprom_size = natsemi_pci_info[chip_idx].eeprom_size;
8818c2ecf20Sopenharmony_ci	if (natsemi_pci_info[chip_idx].flags & NATSEMI_FLAG_IGNORE_PHY)
8828c2ecf20Sopenharmony_ci		np->ignore_phy = 1;
8838c2ecf20Sopenharmony_ci	else
8848c2ecf20Sopenharmony_ci		np->ignore_phy = 0;
8858c2ecf20Sopenharmony_ci	np->dspcfg_workaround = dspcfg_workaround;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	/* Initial port:
8888c2ecf20Sopenharmony_ci	 * - If configured to ignore the PHY set up for external.
8898c2ecf20Sopenharmony_ci	 * - If the nic was configured to use an external phy and if find_mii
8908c2ecf20Sopenharmony_ci	 *   finds a phy: use external port, first phy that replies.
8918c2ecf20Sopenharmony_ci	 * - Otherwise: internal port.
8928c2ecf20Sopenharmony_ci	 * Note that the phy address for the internal phy doesn't matter:
8938c2ecf20Sopenharmony_ci	 * The address would be used to access a phy over the mii bus, but
8948c2ecf20Sopenharmony_ci	 * the internal phy is accessed through mapped registers.
8958c2ecf20Sopenharmony_ci	 */
8968c2ecf20Sopenharmony_ci	if (np->ignore_phy || readl(ioaddr + ChipConfig) & CfgExtPhy)
8978c2ecf20Sopenharmony_ci		dev->if_port = PORT_MII;
8988c2ecf20Sopenharmony_ci	else
8998c2ecf20Sopenharmony_ci		dev->if_port = PORT_TP;
9008c2ecf20Sopenharmony_ci	/* Reset the chip to erase previous misconfiguration. */
9018c2ecf20Sopenharmony_ci	natsemi_reload_eeprom(dev);
9028c2ecf20Sopenharmony_ci	natsemi_reset(dev);
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	if (dev->if_port != PORT_TP) {
9058c2ecf20Sopenharmony_ci		np->phy_addr_external = find_mii(dev);
9068c2ecf20Sopenharmony_ci		/* If we're ignoring the PHY it doesn't matter if we can't
9078c2ecf20Sopenharmony_ci		 * find one. */
9088c2ecf20Sopenharmony_ci		if (!np->ignore_phy && np->phy_addr_external == PHY_ADDR_NONE) {
9098c2ecf20Sopenharmony_ci			dev->if_port = PORT_TP;
9108c2ecf20Sopenharmony_ci			np->phy_addr_external = PHY_ADDR_INTERNAL;
9118c2ecf20Sopenharmony_ci		}
9128c2ecf20Sopenharmony_ci	} else {
9138c2ecf20Sopenharmony_ci		np->phy_addr_external = PHY_ADDR_INTERNAL;
9148c2ecf20Sopenharmony_ci	}
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	option = find_cnt < MAX_UNITS ? options[find_cnt] : 0;
9178c2ecf20Sopenharmony_ci	/* The lower four bits are the media type. */
9188c2ecf20Sopenharmony_ci	if (option) {
9198c2ecf20Sopenharmony_ci		if (option & 0x200)
9208c2ecf20Sopenharmony_ci			np->full_duplex = 1;
9218c2ecf20Sopenharmony_ci		if (option & 15)
9228c2ecf20Sopenharmony_ci			printk(KERN_INFO
9238c2ecf20Sopenharmony_ci				"natsemi %s: ignoring user supplied media type %d",
9248c2ecf20Sopenharmony_ci				pci_name(np->pci_dev), option & 15);
9258c2ecf20Sopenharmony_ci	}
9268c2ecf20Sopenharmony_ci	if (find_cnt < MAX_UNITS  &&  full_duplex[find_cnt])
9278c2ecf20Sopenharmony_ci		np->full_duplex = 1;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	dev->netdev_ops = &natsemi_netdev_ops;
9308c2ecf20Sopenharmony_ci	dev->watchdog_timeo = TX_TIMEOUT;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	dev->ethtool_ops = &ethtool_ops;
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	/* MTU range: 64 - 2024 */
9358c2ecf20Sopenharmony_ci	dev->min_mtu = ETH_ZLEN + ETH_FCS_LEN;
9368c2ecf20Sopenharmony_ci	dev->max_mtu = NATSEMI_RX_LIMIT - NATSEMI_HEADERS;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	if (mtu)
9398c2ecf20Sopenharmony_ci		dev->mtu = mtu;
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	natsemi_init_media(dev);
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	/* save the silicon revision for later querying */
9448c2ecf20Sopenharmony_ci	np->srr = readl(ioaddr + SiliconRev);
9458c2ecf20Sopenharmony_ci	if (netif_msg_hw(np))
9468c2ecf20Sopenharmony_ci		printk(KERN_INFO "natsemi %s: silicon revision %#04x.\n",
9478c2ecf20Sopenharmony_ci				pci_name(np->pci_dev), np->srr);
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	i = register_netdev(dev);
9508c2ecf20Sopenharmony_ci	if (i)
9518c2ecf20Sopenharmony_ci		goto err_register_netdev;
9528c2ecf20Sopenharmony_ci	i = NATSEMI_CREATE_FILE(pdev, dspcfg_workaround);
9538c2ecf20Sopenharmony_ci	if (i)
9548c2ecf20Sopenharmony_ci		goto err_create_file;
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	if (netif_msg_drv(np)) {
9578c2ecf20Sopenharmony_ci		printk(KERN_INFO "natsemi %s: %s at %#08llx "
9588c2ecf20Sopenharmony_ci		       "(%s), %pM, IRQ %d",
9598c2ecf20Sopenharmony_ci		       dev->name, natsemi_pci_info[chip_idx].name,
9608c2ecf20Sopenharmony_ci		       (unsigned long long)iostart, pci_name(np->pci_dev),
9618c2ecf20Sopenharmony_ci		       dev->dev_addr, irq);
9628c2ecf20Sopenharmony_ci		if (dev->if_port == PORT_TP)
9638c2ecf20Sopenharmony_ci			printk(", port TP.\n");
9648c2ecf20Sopenharmony_ci		else if (np->ignore_phy)
9658c2ecf20Sopenharmony_ci			printk(", port MII, ignoring PHY\n");
9668c2ecf20Sopenharmony_ci		else
9678c2ecf20Sopenharmony_ci			printk(", port MII, phy ad %d.\n", np->phy_addr_external);
9688c2ecf20Sopenharmony_ci	}
9698c2ecf20Sopenharmony_ci	return 0;
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci err_create_file:
9728c2ecf20Sopenharmony_ci 	unregister_netdev(dev);
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci err_register_netdev:
9758c2ecf20Sopenharmony_ci	iounmap(ioaddr);
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci err_pci_request_regions:
9788c2ecf20Sopenharmony_ci	free_netdev(dev);
9798c2ecf20Sopenharmony_ci	return i;
9808c2ecf20Sopenharmony_ci}
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces.
9848c2ecf20Sopenharmony_ci   The EEPROM code is for the common 93c06/46 EEPROMs with 6 bit addresses. */
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci/* Delay between EEPROM clock transitions.
9878c2ecf20Sopenharmony_ci   No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need
9888c2ecf20Sopenharmony_ci   a delay.  Note that pre-2.0.34 kernels had a cache-alignment bug that
9898c2ecf20Sopenharmony_ci   made udelay() unreliable.
9908c2ecf20Sopenharmony_ci   The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is
9918c2ecf20Sopenharmony_ci   deprecated.
9928c2ecf20Sopenharmony_ci*/
9938c2ecf20Sopenharmony_ci#define eeprom_delay(ee_addr)	readl(ee_addr)
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci#define EE_Write0 (EE_ChipSelect)
9968c2ecf20Sopenharmony_ci#define EE_Write1 (EE_ChipSelect | EE_DataIn)
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci/* The EEPROM commands include the alway-set leading bit. */
9998c2ecf20Sopenharmony_cienum EEPROM_Cmds {
10008c2ecf20Sopenharmony_ci	EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6),
10018c2ecf20Sopenharmony_ci};
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_cistatic int eeprom_read(void __iomem *addr, int location)
10048c2ecf20Sopenharmony_ci{
10058c2ecf20Sopenharmony_ci	int i;
10068c2ecf20Sopenharmony_ci	int retval = 0;
10078c2ecf20Sopenharmony_ci	void __iomem *ee_addr = addr + EECtrl;
10088c2ecf20Sopenharmony_ci	int read_cmd = location | EE_ReadCmd;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	writel(EE_Write0, ee_addr);
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	/* Shift the read command bits out. */
10138c2ecf20Sopenharmony_ci	for (i = 10; i >= 0; i--) {
10148c2ecf20Sopenharmony_ci		short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0;
10158c2ecf20Sopenharmony_ci		writel(dataval, ee_addr);
10168c2ecf20Sopenharmony_ci		eeprom_delay(ee_addr);
10178c2ecf20Sopenharmony_ci		writel(dataval | EE_ShiftClk, ee_addr);
10188c2ecf20Sopenharmony_ci		eeprom_delay(ee_addr);
10198c2ecf20Sopenharmony_ci	}
10208c2ecf20Sopenharmony_ci	writel(EE_ChipSelect, ee_addr);
10218c2ecf20Sopenharmony_ci	eeprom_delay(ee_addr);
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++) {
10248c2ecf20Sopenharmony_ci		writel(EE_ChipSelect | EE_ShiftClk, ee_addr);
10258c2ecf20Sopenharmony_ci		eeprom_delay(ee_addr);
10268c2ecf20Sopenharmony_ci		retval |= (readl(ee_addr) & EE_DataOut) ? 1 << i : 0;
10278c2ecf20Sopenharmony_ci		writel(EE_ChipSelect, ee_addr);
10288c2ecf20Sopenharmony_ci		eeprom_delay(ee_addr);
10298c2ecf20Sopenharmony_ci	}
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	/* Terminate the EEPROM access. */
10328c2ecf20Sopenharmony_ci	writel(EE_Write0, ee_addr);
10338c2ecf20Sopenharmony_ci	writel(0, ee_addr);
10348c2ecf20Sopenharmony_ci	return retval;
10358c2ecf20Sopenharmony_ci}
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci/* MII transceiver control section.
10388c2ecf20Sopenharmony_ci * The 83815 series has an internal transceiver, and we present the
10398c2ecf20Sopenharmony_ci * internal management registers as if they were MII connected.
10408c2ecf20Sopenharmony_ci * External Phy registers are referenced through the MII interface.
10418c2ecf20Sopenharmony_ci */
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci/* clock transitions >= 20ns (25MHz)
10448c2ecf20Sopenharmony_ci * One readl should be good to PCI @ 100MHz
10458c2ecf20Sopenharmony_ci */
10468c2ecf20Sopenharmony_ci#define mii_delay(ioaddr)  readl(ioaddr + EECtrl)
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_cistatic int mii_getbit (struct net_device *dev)
10498c2ecf20Sopenharmony_ci{
10508c2ecf20Sopenharmony_ci	int data;
10518c2ecf20Sopenharmony_ci	void __iomem *ioaddr = ns_ioaddr(dev);
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	writel(MII_ShiftClk, ioaddr + EECtrl);
10548c2ecf20Sopenharmony_ci	data = readl(ioaddr + EECtrl);
10558c2ecf20Sopenharmony_ci	writel(0, ioaddr + EECtrl);
10568c2ecf20Sopenharmony_ci	mii_delay(ioaddr);
10578c2ecf20Sopenharmony_ci	return (data & MII_Data)? 1 : 0;
10588c2ecf20Sopenharmony_ci}
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_cistatic void mii_send_bits (struct net_device *dev, u32 data, int len)
10618c2ecf20Sopenharmony_ci{
10628c2ecf20Sopenharmony_ci	u32 i;
10638c2ecf20Sopenharmony_ci	void __iomem *ioaddr = ns_ioaddr(dev);
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	for (i = (1 << (len-1)); i; i >>= 1)
10668c2ecf20Sopenharmony_ci	{
10678c2ecf20Sopenharmony_ci		u32 mdio_val = MII_Write | ((data & i)? MII_Data : 0);
10688c2ecf20Sopenharmony_ci		writel(mdio_val, ioaddr + EECtrl);
10698c2ecf20Sopenharmony_ci		mii_delay(ioaddr);
10708c2ecf20Sopenharmony_ci		writel(mdio_val | MII_ShiftClk, ioaddr + EECtrl);
10718c2ecf20Sopenharmony_ci		mii_delay(ioaddr);
10728c2ecf20Sopenharmony_ci	}
10738c2ecf20Sopenharmony_ci	writel(0, ioaddr + EECtrl);
10748c2ecf20Sopenharmony_ci	mii_delay(ioaddr);
10758c2ecf20Sopenharmony_ci}
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_cistatic int miiport_read(struct net_device *dev, int phy_id, int reg)
10788c2ecf20Sopenharmony_ci{
10798c2ecf20Sopenharmony_ci	u32 cmd;
10808c2ecf20Sopenharmony_ci	int i;
10818c2ecf20Sopenharmony_ci	u32 retval = 0;
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	/* Ensure sync */
10848c2ecf20Sopenharmony_ci	mii_send_bits (dev, 0xffffffff, 32);
10858c2ecf20Sopenharmony_ci	/* ST(2), OP(2), ADDR(5), REG#(5), TA(2), Data(16) total 32 bits */
10868c2ecf20Sopenharmony_ci	/* ST,OP = 0110'b for read operation */
10878c2ecf20Sopenharmony_ci	cmd = (0x06 << 10) | (phy_id << 5) | reg;
10888c2ecf20Sopenharmony_ci	mii_send_bits (dev, cmd, 14);
10898c2ecf20Sopenharmony_ci	/* Turnaround */
10908c2ecf20Sopenharmony_ci	if (mii_getbit (dev))
10918c2ecf20Sopenharmony_ci		return 0;
10928c2ecf20Sopenharmony_ci	/* Read data */
10938c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++) {
10948c2ecf20Sopenharmony_ci		retval <<= 1;
10958c2ecf20Sopenharmony_ci		retval |= mii_getbit (dev);
10968c2ecf20Sopenharmony_ci	}
10978c2ecf20Sopenharmony_ci	/* End cycle */
10988c2ecf20Sopenharmony_ci	mii_getbit (dev);
10998c2ecf20Sopenharmony_ci	return retval;
11008c2ecf20Sopenharmony_ci}
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_cistatic void miiport_write(struct net_device *dev, int phy_id, int reg, u16 data)
11038c2ecf20Sopenharmony_ci{
11048c2ecf20Sopenharmony_ci	u32 cmd;
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	/* Ensure sync */
11078c2ecf20Sopenharmony_ci	mii_send_bits (dev, 0xffffffff, 32);
11088c2ecf20Sopenharmony_ci	/* ST(2), OP(2), ADDR(5), REG#(5), TA(2), Data(16) total 32 bits */
11098c2ecf20Sopenharmony_ci	/* ST,OP,AAAAA,RRRRR,TA = 0101xxxxxxxxxx10'b = 0x5002 for write */
11108c2ecf20Sopenharmony_ci	cmd = (0x5002 << 16) | (phy_id << 23) | (reg << 18) | data;
11118c2ecf20Sopenharmony_ci	mii_send_bits (dev, cmd, 32);
11128c2ecf20Sopenharmony_ci	/* End cycle */
11138c2ecf20Sopenharmony_ci	mii_getbit (dev);
11148c2ecf20Sopenharmony_ci}
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_cistatic int mdio_read(struct net_device *dev, int reg)
11178c2ecf20Sopenharmony_ci{
11188c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
11198c2ecf20Sopenharmony_ci	void __iomem *ioaddr = ns_ioaddr(dev);
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	/* The 83815 series has two ports:
11228c2ecf20Sopenharmony_ci	 * - an internal transceiver
11238c2ecf20Sopenharmony_ci	 * - an external mii bus
11248c2ecf20Sopenharmony_ci	 */
11258c2ecf20Sopenharmony_ci	if (dev->if_port == PORT_TP)
11268c2ecf20Sopenharmony_ci		return readw(ioaddr+BasicControl+(reg<<2));
11278c2ecf20Sopenharmony_ci	else
11288c2ecf20Sopenharmony_ci		return miiport_read(dev, np->phy_addr_external, reg);
11298c2ecf20Sopenharmony_ci}
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_cistatic void mdio_write(struct net_device *dev, int reg, u16 data)
11328c2ecf20Sopenharmony_ci{
11338c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
11348c2ecf20Sopenharmony_ci	void __iomem *ioaddr = ns_ioaddr(dev);
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	/* The 83815 series has an internal transceiver; handle separately */
11378c2ecf20Sopenharmony_ci	if (dev->if_port == PORT_TP)
11388c2ecf20Sopenharmony_ci		writew(data, ioaddr+BasicControl+(reg<<2));
11398c2ecf20Sopenharmony_ci	else
11408c2ecf20Sopenharmony_ci		miiport_write(dev, np->phy_addr_external, reg, data);
11418c2ecf20Sopenharmony_ci}
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_cistatic void init_phy_fixup(struct net_device *dev)
11448c2ecf20Sopenharmony_ci{
11458c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
11468c2ecf20Sopenharmony_ci	void __iomem *ioaddr = ns_ioaddr(dev);
11478c2ecf20Sopenharmony_ci	int i;
11488c2ecf20Sopenharmony_ci	u32 cfg;
11498c2ecf20Sopenharmony_ci	u16 tmp;
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	/* restore stuff lost when power was out */
11528c2ecf20Sopenharmony_ci	tmp = mdio_read(dev, MII_BMCR);
11538c2ecf20Sopenharmony_ci	if (np->autoneg == AUTONEG_ENABLE) {
11548c2ecf20Sopenharmony_ci		/* renegotiate if something changed */
11558c2ecf20Sopenharmony_ci		if ((tmp & BMCR_ANENABLE) == 0 ||
11568c2ecf20Sopenharmony_ci		    np->advertising != mdio_read(dev, MII_ADVERTISE))
11578c2ecf20Sopenharmony_ci		{
11588c2ecf20Sopenharmony_ci			/* turn on autonegotiation and force negotiation */
11598c2ecf20Sopenharmony_ci			tmp |= (BMCR_ANENABLE | BMCR_ANRESTART);
11608c2ecf20Sopenharmony_ci			mdio_write(dev, MII_ADVERTISE, np->advertising);
11618c2ecf20Sopenharmony_ci		}
11628c2ecf20Sopenharmony_ci	} else {
11638c2ecf20Sopenharmony_ci		/* turn off auto negotiation, set speed and duplexity */
11648c2ecf20Sopenharmony_ci		tmp &= ~(BMCR_ANENABLE | BMCR_SPEED100 | BMCR_FULLDPLX);
11658c2ecf20Sopenharmony_ci		if (np->speed == SPEED_100)
11668c2ecf20Sopenharmony_ci			tmp |= BMCR_SPEED100;
11678c2ecf20Sopenharmony_ci		if (np->duplex == DUPLEX_FULL)
11688c2ecf20Sopenharmony_ci			tmp |= BMCR_FULLDPLX;
11698c2ecf20Sopenharmony_ci		/*
11708c2ecf20Sopenharmony_ci		 * Note: there is no good way to inform the link partner
11718c2ecf20Sopenharmony_ci		 * that our capabilities changed. The user has to unplug
11728c2ecf20Sopenharmony_ci		 * and replug the network cable after some changes, e.g.
11738c2ecf20Sopenharmony_ci		 * after switching from 10HD, autoneg off to 100 HD,
11748c2ecf20Sopenharmony_ci		 * autoneg off.
11758c2ecf20Sopenharmony_ci		 */
11768c2ecf20Sopenharmony_ci	}
11778c2ecf20Sopenharmony_ci	mdio_write(dev, MII_BMCR, tmp);
11788c2ecf20Sopenharmony_ci	readl(ioaddr + ChipConfig);
11798c2ecf20Sopenharmony_ci	udelay(1);
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	/* find out what phy this is */
11828c2ecf20Sopenharmony_ci	np->mii = (mdio_read(dev, MII_PHYSID1) << 16)
11838c2ecf20Sopenharmony_ci				+ mdio_read(dev, MII_PHYSID2);
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	/* handle external phys here */
11868c2ecf20Sopenharmony_ci	switch (np->mii) {
11878c2ecf20Sopenharmony_ci	case PHYID_AM79C874:
11888c2ecf20Sopenharmony_ci		/* phy specific configuration for fibre/tp operation */
11898c2ecf20Sopenharmony_ci		tmp = mdio_read(dev, MII_MCTRL);
11908c2ecf20Sopenharmony_ci		tmp &= ~(MII_FX_SEL | MII_EN_SCRM);
11918c2ecf20Sopenharmony_ci		if (dev->if_port == PORT_FIBRE)
11928c2ecf20Sopenharmony_ci			tmp |= MII_FX_SEL;
11938c2ecf20Sopenharmony_ci		else
11948c2ecf20Sopenharmony_ci			tmp |= MII_EN_SCRM;
11958c2ecf20Sopenharmony_ci		mdio_write(dev, MII_MCTRL, tmp);
11968c2ecf20Sopenharmony_ci		break;
11978c2ecf20Sopenharmony_ci	default:
11988c2ecf20Sopenharmony_ci		break;
11998c2ecf20Sopenharmony_ci	}
12008c2ecf20Sopenharmony_ci	cfg = readl(ioaddr + ChipConfig);
12018c2ecf20Sopenharmony_ci	if (cfg & CfgExtPhy)
12028c2ecf20Sopenharmony_ci		return;
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	/* On page 78 of the spec, they recommend some settings for "optimum
12058c2ecf20Sopenharmony_ci	   performance" to be done in sequence.  These settings optimize some
12068c2ecf20Sopenharmony_ci	   of the 100Mbit autodetection circuitry.  They say we only want to
12078c2ecf20Sopenharmony_ci	   do this for rev C of the chip, but engineers at NSC (Bradley
12088c2ecf20Sopenharmony_ci	   Kennedy) recommends always setting them.  If you don't, you get
12098c2ecf20Sopenharmony_ci	   errors on some autonegotiations that make the device unusable.
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	   It seems that the DSP needs a few usec to reinitialize after
12128c2ecf20Sopenharmony_ci	   the start of the phy. Just retry writing these values until they
12138c2ecf20Sopenharmony_ci	   stick.
12148c2ecf20Sopenharmony_ci	*/
12158c2ecf20Sopenharmony_ci	for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci		int dspcfg;
12188c2ecf20Sopenharmony_ci		writew(1, ioaddr + PGSEL);
12198c2ecf20Sopenharmony_ci		writew(PMDCSR_VAL, ioaddr + PMDCSR);
12208c2ecf20Sopenharmony_ci		writew(TSTDAT_VAL, ioaddr + TSTDAT);
12218c2ecf20Sopenharmony_ci		np->dspcfg = (np->srr <= SRR_DP83815_C)?
12228c2ecf20Sopenharmony_ci			DSPCFG_VAL : (DSPCFG_COEF | readw(ioaddr + DSPCFG));
12238c2ecf20Sopenharmony_ci		writew(np->dspcfg, ioaddr + DSPCFG);
12248c2ecf20Sopenharmony_ci		writew(SDCFG_VAL, ioaddr + SDCFG);
12258c2ecf20Sopenharmony_ci		writew(0, ioaddr + PGSEL);
12268c2ecf20Sopenharmony_ci		readl(ioaddr + ChipConfig);
12278c2ecf20Sopenharmony_ci		udelay(10);
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci		writew(1, ioaddr + PGSEL);
12308c2ecf20Sopenharmony_ci		dspcfg = readw(ioaddr + DSPCFG);
12318c2ecf20Sopenharmony_ci		writew(0, ioaddr + PGSEL);
12328c2ecf20Sopenharmony_ci		if (np->dspcfg == dspcfg)
12338c2ecf20Sopenharmony_ci			break;
12348c2ecf20Sopenharmony_ci	}
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	if (netif_msg_link(np)) {
12378c2ecf20Sopenharmony_ci		if (i==NATSEMI_HW_TIMEOUT) {
12388c2ecf20Sopenharmony_ci			printk(KERN_INFO
12398c2ecf20Sopenharmony_ci				"%s: DSPCFG mismatch after retrying for %d usec.\n",
12408c2ecf20Sopenharmony_ci				dev->name, i*10);
12418c2ecf20Sopenharmony_ci		} else {
12428c2ecf20Sopenharmony_ci			printk(KERN_INFO
12438c2ecf20Sopenharmony_ci				"%s: DSPCFG accepted after %d usec.\n",
12448c2ecf20Sopenharmony_ci				dev->name, i*10);
12458c2ecf20Sopenharmony_ci		}
12468c2ecf20Sopenharmony_ci	}
12478c2ecf20Sopenharmony_ci	/*
12488c2ecf20Sopenharmony_ci	 * Enable PHY Specific event based interrupts.  Link state change
12498c2ecf20Sopenharmony_ci	 * and Auto-Negotiation Completion are among the affected.
12508c2ecf20Sopenharmony_ci	 * Read the intr status to clear it (needed for wake events).
12518c2ecf20Sopenharmony_ci	 */
12528c2ecf20Sopenharmony_ci	readw(ioaddr + MIntrStatus);
12538c2ecf20Sopenharmony_ci	writew(MICRIntEn, ioaddr + MIntrCtrl);
12548c2ecf20Sopenharmony_ci}
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_cistatic int switch_port_external(struct net_device *dev)
12578c2ecf20Sopenharmony_ci{
12588c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
12598c2ecf20Sopenharmony_ci	void __iomem *ioaddr = ns_ioaddr(dev);
12608c2ecf20Sopenharmony_ci	u32 cfg;
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	cfg = readl(ioaddr + ChipConfig);
12638c2ecf20Sopenharmony_ci	if (cfg & CfgExtPhy)
12648c2ecf20Sopenharmony_ci		return 0;
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	if (netif_msg_link(np)) {
12678c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: switching to external transceiver.\n",
12688c2ecf20Sopenharmony_ci				dev->name);
12698c2ecf20Sopenharmony_ci	}
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	/* 1) switch back to external phy */
12728c2ecf20Sopenharmony_ci	writel(cfg | (CfgExtPhy | CfgPhyDis), ioaddr + ChipConfig);
12738c2ecf20Sopenharmony_ci	readl(ioaddr + ChipConfig);
12748c2ecf20Sopenharmony_ci	udelay(1);
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	/* 2) reset the external phy: */
12778c2ecf20Sopenharmony_ci	/* resetting the external PHY has been known to cause a hub supplying
12788c2ecf20Sopenharmony_ci	 * power over Ethernet to kill the power.  We don't want to kill
12798c2ecf20Sopenharmony_ci	 * power to this computer, so we avoid resetting the phy.
12808c2ecf20Sopenharmony_ci	 */
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	/* 3) reinit the phy fixup, it got lost during power down. */
12838c2ecf20Sopenharmony_ci	move_int_phy(dev, np->phy_addr_external);
12848c2ecf20Sopenharmony_ci	init_phy_fixup(dev);
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	return 1;
12878c2ecf20Sopenharmony_ci}
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_cistatic int switch_port_internal(struct net_device *dev)
12908c2ecf20Sopenharmony_ci{
12918c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
12928c2ecf20Sopenharmony_ci	void __iomem *ioaddr = ns_ioaddr(dev);
12938c2ecf20Sopenharmony_ci	int i;
12948c2ecf20Sopenharmony_ci	u32 cfg;
12958c2ecf20Sopenharmony_ci	u16 bmcr;
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci	cfg = readl(ioaddr + ChipConfig);
12988c2ecf20Sopenharmony_ci	if (!(cfg &CfgExtPhy))
12998c2ecf20Sopenharmony_ci		return 0;
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	if (netif_msg_link(np)) {
13028c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: switching to internal transceiver.\n",
13038c2ecf20Sopenharmony_ci				dev->name);
13048c2ecf20Sopenharmony_ci	}
13058c2ecf20Sopenharmony_ci	/* 1) switch back to internal phy: */
13068c2ecf20Sopenharmony_ci	cfg = cfg & ~(CfgExtPhy | CfgPhyDis);
13078c2ecf20Sopenharmony_ci	writel(cfg, ioaddr + ChipConfig);
13088c2ecf20Sopenharmony_ci	readl(ioaddr + ChipConfig);
13098c2ecf20Sopenharmony_ci	udelay(1);
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci	/* 2) reset the internal phy: */
13128c2ecf20Sopenharmony_ci	bmcr = readw(ioaddr+BasicControl+(MII_BMCR<<2));
13138c2ecf20Sopenharmony_ci	writel(bmcr | BMCR_RESET, ioaddr+BasicControl+(MII_BMCR<<2));
13148c2ecf20Sopenharmony_ci	readl(ioaddr + ChipConfig);
13158c2ecf20Sopenharmony_ci	udelay(10);
13168c2ecf20Sopenharmony_ci	for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
13178c2ecf20Sopenharmony_ci		bmcr = readw(ioaddr+BasicControl+(MII_BMCR<<2));
13188c2ecf20Sopenharmony_ci		if (!(bmcr & BMCR_RESET))
13198c2ecf20Sopenharmony_ci			break;
13208c2ecf20Sopenharmony_ci		udelay(10);
13218c2ecf20Sopenharmony_ci	}
13228c2ecf20Sopenharmony_ci	if (i==NATSEMI_HW_TIMEOUT && netif_msg_link(np)) {
13238c2ecf20Sopenharmony_ci		printk(KERN_INFO
13248c2ecf20Sopenharmony_ci			"%s: phy reset did not complete in %d usec.\n",
13258c2ecf20Sopenharmony_ci			dev->name, i*10);
13268c2ecf20Sopenharmony_ci	}
13278c2ecf20Sopenharmony_ci	/* 3) reinit the phy fixup, it got lost during power down. */
13288c2ecf20Sopenharmony_ci	init_phy_fixup(dev);
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci	return 1;
13318c2ecf20Sopenharmony_ci}
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci/* Scan for a PHY on the external mii bus.
13348c2ecf20Sopenharmony_ci * There are two tricky points:
13358c2ecf20Sopenharmony_ci * - Do not scan while the internal phy is enabled. The internal phy will
13368c2ecf20Sopenharmony_ci *   crash: e.g. reads from the DSPCFG register will return odd values and
13378c2ecf20Sopenharmony_ci *   the nasty random phy reset code will reset the nic every few seconds.
13388c2ecf20Sopenharmony_ci * - The internal phy must be moved around, an external phy could
13398c2ecf20Sopenharmony_ci *   have the same address as the internal phy.
13408c2ecf20Sopenharmony_ci */
13418c2ecf20Sopenharmony_cistatic int find_mii(struct net_device *dev)
13428c2ecf20Sopenharmony_ci{
13438c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
13448c2ecf20Sopenharmony_ci	int tmp;
13458c2ecf20Sopenharmony_ci	int i;
13468c2ecf20Sopenharmony_ci	int did_switch;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	/* Switch to external phy */
13498c2ecf20Sopenharmony_ci	did_switch = switch_port_external(dev);
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci	/* Scan the possible phy addresses:
13528c2ecf20Sopenharmony_ci	 *
13538c2ecf20Sopenharmony_ci	 * PHY address 0 means that the phy is in isolate mode. Not yet
13548c2ecf20Sopenharmony_ci	 * supported due to lack of test hardware. User space should
13558c2ecf20Sopenharmony_ci	 * handle it through ethtool.
13568c2ecf20Sopenharmony_ci	 */
13578c2ecf20Sopenharmony_ci	for (i = 1; i <= 31; i++) {
13588c2ecf20Sopenharmony_ci		move_int_phy(dev, i);
13598c2ecf20Sopenharmony_ci		tmp = miiport_read(dev, i, MII_BMSR);
13608c2ecf20Sopenharmony_ci		if (tmp != 0xffff && tmp != 0x0000) {
13618c2ecf20Sopenharmony_ci			/* found something! */
13628c2ecf20Sopenharmony_ci			np->mii = (mdio_read(dev, MII_PHYSID1) << 16)
13638c2ecf20Sopenharmony_ci					+ mdio_read(dev, MII_PHYSID2);
13648c2ecf20Sopenharmony_ci	 		if (netif_msg_probe(np)) {
13658c2ecf20Sopenharmony_ci				printk(KERN_INFO "natsemi %s: found external phy %08x at address %d.\n",
13668c2ecf20Sopenharmony_ci						pci_name(np->pci_dev), np->mii, i);
13678c2ecf20Sopenharmony_ci			}
13688c2ecf20Sopenharmony_ci			break;
13698c2ecf20Sopenharmony_ci		}
13708c2ecf20Sopenharmony_ci	}
13718c2ecf20Sopenharmony_ci	/* And switch back to internal phy: */
13728c2ecf20Sopenharmony_ci	if (did_switch)
13738c2ecf20Sopenharmony_ci		switch_port_internal(dev);
13748c2ecf20Sopenharmony_ci	return i;
13758c2ecf20Sopenharmony_ci}
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci/* CFG bits [13:16] [18:23] */
13788c2ecf20Sopenharmony_ci#define CFG_RESET_SAVE 0xfde000
13798c2ecf20Sopenharmony_ci/* WCSR bits [0:4] [9:10] */
13808c2ecf20Sopenharmony_ci#define WCSR_RESET_SAVE 0x61f
13818c2ecf20Sopenharmony_ci/* RFCR bits [20] [22] [27:31] */
13828c2ecf20Sopenharmony_ci#define RFCR_RESET_SAVE 0xf8500000
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_cistatic void natsemi_reset(struct net_device *dev)
13858c2ecf20Sopenharmony_ci{
13868c2ecf20Sopenharmony_ci	int i;
13878c2ecf20Sopenharmony_ci	u32 cfg;
13888c2ecf20Sopenharmony_ci	u32 wcsr;
13898c2ecf20Sopenharmony_ci	u32 rfcr;
13908c2ecf20Sopenharmony_ci	u16 pmatch[3];
13918c2ecf20Sopenharmony_ci	u16 sopass[3];
13928c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
13938c2ecf20Sopenharmony_ci	void __iomem *ioaddr = ns_ioaddr(dev);
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci	/*
13968c2ecf20Sopenharmony_ci	 * Resetting the chip causes some registers to be lost.
13978c2ecf20Sopenharmony_ci	 * Natsemi suggests NOT reloading the EEPROM while live, so instead
13988c2ecf20Sopenharmony_ci	 * we save the state that would have been loaded from EEPROM
13998c2ecf20Sopenharmony_ci	 * on a normal power-up (see the spec EEPROM map).  This assumes
14008c2ecf20Sopenharmony_ci	 * whoever calls this will follow up with init_registers() eventually.
14018c2ecf20Sopenharmony_ci	 */
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	/* CFG */
14048c2ecf20Sopenharmony_ci	cfg = readl(ioaddr + ChipConfig) & CFG_RESET_SAVE;
14058c2ecf20Sopenharmony_ci	/* WCSR */
14068c2ecf20Sopenharmony_ci	wcsr = readl(ioaddr + WOLCmd) & WCSR_RESET_SAVE;
14078c2ecf20Sopenharmony_ci	/* RFCR */
14088c2ecf20Sopenharmony_ci	rfcr = readl(ioaddr + RxFilterAddr) & RFCR_RESET_SAVE;
14098c2ecf20Sopenharmony_ci	/* PMATCH */
14108c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
14118c2ecf20Sopenharmony_ci		writel(i*2, ioaddr + RxFilterAddr);
14128c2ecf20Sopenharmony_ci		pmatch[i] = readw(ioaddr + RxFilterData);
14138c2ecf20Sopenharmony_ci	}
14148c2ecf20Sopenharmony_ci	/* SOPAS */
14158c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
14168c2ecf20Sopenharmony_ci		writel(0xa+(i*2), ioaddr + RxFilterAddr);
14178c2ecf20Sopenharmony_ci		sopass[i] = readw(ioaddr + RxFilterData);
14188c2ecf20Sopenharmony_ci	}
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci	/* now whack the chip */
14218c2ecf20Sopenharmony_ci	writel(ChipReset, ioaddr + ChipCmd);
14228c2ecf20Sopenharmony_ci	for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
14238c2ecf20Sopenharmony_ci		if (!(readl(ioaddr + ChipCmd) & ChipReset))
14248c2ecf20Sopenharmony_ci			break;
14258c2ecf20Sopenharmony_ci		udelay(5);
14268c2ecf20Sopenharmony_ci	}
14278c2ecf20Sopenharmony_ci	if (i==NATSEMI_HW_TIMEOUT) {
14288c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s: reset did not complete in %d usec.\n",
14298c2ecf20Sopenharmony_ci			dev->name, i*5);
14308c2ecf20Sopenharmony_ci	} else if (netif_msg_hw(np)) {
14318c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: reset completed in %d usec.\n",
14328c2ecf20Sopenharmony_ci			dev->name, i*5);
14338c2ecf20Sopenharmony_ci	}
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci	/* restore CFG */
14368c2ecf20Sopenharmony_ci	cfg |= readl(ioaddr + ChipConfig) & ~CFG_RESET_SAVE;
14378c2ecf20Sopenharmony_ci	/* turn on external phy if it was selected */
14388c2ecf20Sopenharmony_ci	if (dev->if_port == PORT_TP)
14398c2ecf20Sopenharmony_ci		cfg &= ~(CfgExtPhy | CfgPhyDis);
14408c2ecf20Sopenharmony_ci	else
14418c2ecf20Sopenharmony_ci		cfg |= (CfgExtPhy | CfgPhyDis);
14428c2ecf20Sopenharmony_ci	writel(cfg, ioaddr + ChipConfig);
14438c2ecf20Sopenharmony_ci	/* restore WCSR */
14448c2ecf20Sopenharmony_ci	wcsr |= readl(ioaddr + WOLCmd) & ~WCSR_RESET_SAVE;
14458c2ecf20Sopenharmony_ci	writel(wcsr, ioaddr + WOLCmd);
14468c2ecf20Sopenharmony_ci	/* read RFCR */
14478c2ecf20Sopenharmony_ci	rfcr |= readl(ioaddr + RxFilterAddr) & ~RFCR_RESET_SAVE;
14488c2ecf20Sopenharmony_ci	/* restore PMATCH */
14498c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
14508c2ecf20Sopenharmony_ci		writel(i*2, ioaddr + RxFilterAddr);
14518c2ecf20Sopenharmony_ci		writew(pmatch[i], ioaddr + RxFilterData);
14528c2ecf20Sopenharmony_ci	}
14538c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
14548c2ecf20Sopenharmony_ci		writel(0xa+(i*2), ioaddr + RxFilterAddr);
14558c2ecf20Sopenharmony_ci		writew(sopass[i], ioaddr + RxFilterData);
14568c2ecf20Sopenharmony_ci	}
14578c2ecf20Sopenharmony_ci	/* restore RFCR */
14588c2ecf20Sopenharmony_ci	writel(rfcr, ioaddr + RxFilterAddr);
14598c2ecf20Sopenharmony_ci}
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_cistatic void reset_rx(struct net_device *dev)
14628c2ecf20Sopenharmony_ci{
14638c2ecf20Sopenharmony_ci	int i;
14648c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
14658c2ecf20Sopenharmony_ci	void __iomem *ioaddr = ns_ioaddr(dev);
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci	np->intr_status &= ~RxResetDone;
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	writel(RxReset, ioaddr + ChipCmd);
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
14728c2ecf20Sopenharmony_ci		np->intr_status |= readl(ioaddr + IntrStatus);
14738c2ecf20Sopenharmony_ci		if (np->intr_status & RxResetDone)
14748c2ecf20Sopenharmony_ci			break;
14758c2ecf20Sopenharmony_ci		udelay(15);
14768c2ecf20Sopenharmony_ci	}
14778c2ecf20Sopenharmony_ci	if (i==NATSEMI_HW_TIMEOUT) {
14788c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s: RX reset did not complete in %d usec.\n",
14798c2ecf20Sopenharmony_ci		       dev->name, i*15);
14808c2ecf20Sopenharmony_ci	} else if (netif_msg_hw(np)) {
14818c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s: RX reset took %d usec.\n",
14828c2ecf20Sopenharmony_ci		       dev->name, i*15);
14838c2ecf20Sopenharmony_ci	}
14848c2ecf20Sopenharmony_ci}
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_cistatic void natsemi_reload_eeprom(struct net_device *dev)
14878c2ecf20Sopenharmony_ci{
14888c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
14898c2ecf20Sopenharmony_ci	void __iomem *ioaddr = ns_ioaddr(dev);
14908c2ecf20Sopenharmony_ci	int i;
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci	writel(EepromReload, ioaddr + PCIBusCfg);
14938c2ecf20Sopenharmony_ci	for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
14948c2ecf20Sopenharmony_ci		udelay(50);
14958c2ecf20Sopenharmony_ci		if (!(readl(ioaddr + PCIBusCfg) & EepromReload))
14968c2ecf20Sopenharmony_ci			break;
14978c2ecf20Sopenharmony_ci	}
14988c2ecf20Sopenharmony_ci	if (i==NATSEMI_HW_TIMEOUT) {
14998c2ecf20Sopenharmony_ci		printk(KERN_WARNING "natsemi %s: EEPROM did not reload in %d usec.\n",
15008c2ecf20Sopenharmony_ci			pci_name(np->pci_dev), i*50);
15018c2ecf20Sopenharmony_ci	} else if (netif_msg_hw(np)) {
15028c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "natsemi %s: EEPROM reloaded in %d usec.\n",
15038c2ecf20Sopenharmony_ci			pci_name(np->pci_dev), i*50);
15048c2ecf20Sopenharmony_ci	}
15058c2ecf20Sopenharmony_ci}
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_cistatic void natsemi_stop_rxtx(struct net_device *dev)
15088c2ecf20Sopenharmony_ci{
15098c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
15108c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
15118c2ecf20Sopenharmony_ci	int i;
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci	writel(RxOff | TxOff, ioaddr + ChipCmd);
15148c2ecf20Sopenharmony_ci	for(i=0;i< NATSEMI_HW_TIMEOUT;i++) {
15158c2ecf20Sopenharmony_ci		if ((readl(ioaddr + ChipCmd) & (TxOn|RxOn)) == 0)
15168c2ecf20Sopenharmony_ci			break;
15178c2ecf20Sopenharmony_ci		udelay(5);
15188c2ecf20Sopenharmony_ci	}
15198c2ecf20Sopenharmony_ci	if (i==NATSEMI_HW_TIMEOUT) {
15208c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s: Tx/Rx process did not stop in %d usec.\n",
15218c2ecf20Sopenharmony_ci			dev->name, i*5);
15228c2ecf20Sopenharmony_ci	} else if (netif_msg_hw(np)) {
15238c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: Tx/Rx process stopped in %d usec.\n",
15248c2ecf20Sopenharmony_ci			dev->name, i*5);
15258c2ecf20Sopenharmony_ci	}
15268c2ecf20Sopenharmony_ci}
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_cistatic int netdev_open(struct net_device *dev)
15298c2ecf20Sopenharmony_ci{
15308c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
15318c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
15328c2ecf20Sopenharmony_ci	const int irq = np->pci_dev->irq;
15338c2ecf20Sopenharmony_ci	int i;
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	/* Reset the chip, just in case. */
15368c2ecf20Sopenharmony_ci	natsemi_reset(dev);
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_ci	i = request_irq(irq, intr_handler, IRQF_SHARED, dev->name, dev);
15398c2ecf20Sopenharmony_ci	if (i) return i;
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_ci	if (netif_msg_ifup(np))
15428c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: netdev_open() irq %d.\n",
15438c2ecf20Sopenharmony_ci			dev->name, irq);
15448c2ecf20Sopenharmony_ci	i = alloc_ring(dev);
15458c2ecf20Sopenharmony_ci	if (i < 0) {
15468c2ecf20Sopenharmony_ci		free_irq(irq, dev);
15478c2ecf20Sopenharmony_ci		return i;
15488c2ecf20Sopenharmony_ci	}
15498c2ecf20Sopenharmony_ci	napi_enable(&np->napi);
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci	init_ring(dev);
15528c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
15538c2ecf20Sopenharmony_ci	init_registers(dev);
15548c2ecf20Sopenharmony_ci	/* now set the MAC address according to dev->dev_addr */
15558c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
15568c2ecf20Sopenharmony_ci		u16 mac = (dev->dev_addr[2*i+1]<<8) + dev->dev_addr[2*i];
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_ci		writel(i*2, ioaddr + RxFilterAddr);
15598c2ecf20Sopenharmony_ci		writew(mac, ioaddr + RxFilterData);
15608c2ecf20Sopenharmony_ci	}
15618c2ecf20Sopenharmony_ci	writel(np->cur_rx_mode, ioaddr + RxFilterAddr);
15628c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci	netif_start_queue(dev);
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_ci	if (netif_msg_ifup(np))
15678c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: Done netdev_open(), status: %#08x.\n",
15688c2ecf20Sopenharmony_ci			dev->name, (int)readl(ioaddr + ChipCmd));
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci	/* Set the timer to check for link beat. */
15718c2ecf20Sopenharmony_ci	timer_setup(&np->timer, netdev_timer, 0);
15728c2ecf20Sopenharmony_ci	np->timer.expires = round_jiffies(jiffies + NATSEMI_TIMER_FREQ);
15738c2ecf20Sopenharmony_ci	add_timer(&np->timer);
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_ci	return 0;
15768c2ecf20Sopenharmony_ci}
15778c2ecf20Sopenharmony_ci
15788c2ecf20Sopenharmony_cistatic void do_cable_magic(struct net_device *dev)
15798c2ecf20Sopenharmony_ci{
15808c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
15818c2ecf20Sopenharmony_ci	void __iomem *ioaddr = ns_ioaddr(dev);
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci	if (dev->if_port != PORT_TP)
15848c2ecf20Sopenharmony_ci		return;
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_ci	if (np->srr >= SRR_DP83816_A5)
15878c2ecf20Sopenharmony_ci		return;
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_ci	/*
15908c2ecf20Sopenharmony_ci	 * 100 MBit links with short cables can trip an issue with the chip.
15918c2ecf20Sopenharmony_ci	 * The problem manifests as lots of CRC errors and/or flickering
15928c2ecf20Sopenharmony_ci	 * activity LED while idle.  This process is based on instructions
15938c2ecf20Sopenharmony_ci	 * from engineers at National.
15948c2ecf20Sopenharmony_ci	 */
15958c2ecf20Sopenharmony_ci	if (readl(ioaddr + ChipConfig) & CfgSpeed100) {
15968c2ecf20Sopenharmony_ci		u16 data;
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_ci		writew(1, ioaddr + PGSEL);
15998c2ecf20Sopenharmony_ci		/*
16008c2ecf20Sopenharmony_ci		 * coefficient visibility should already be enabled via
16018c2ecf20Sopenharmony_ci		 * DSPCFG | 0x1000
16028c2ecf20Sopenharmony_ci		 */
16038c2ecf20Sopenharmony_ci		data = readw(ioaddr + TSTDAT) & 0xff;
16048c2ecf20Sopenharmony_ci		/*
16058c2ecf20Sopenharmony_ci		 * the value must be negative, and within certain values
16068c2ecf20Sopenharmony_ci		 * (these values all come from National)
16078c2ecf20Sopenharmony_ci		 */
16088c2ecf20Sopenharmony_ci		if (!(data & 0x80) || ((data >= 0xd8) && (data <= 0xff))) {
16098c2ecf20Sopenharmony_ci			np = netdev_priv(dev);
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci			/* the bug has been triggered - fix the coefficient */
16128c2ecf20Sopenharmony_ci			writew(TSTDAT_FIXED, ioaddr + TSTDAT);
16138c2ecf20Sopenharmony_ci			/* lock the value */
16148c2ecf20Sopenharmony_ci			data = readw(ioaddr + DSPCFG);
16158c2ecf20Sopenharmony_ci			np->dspcfg = data | DSPCFG_LOCK;
16168c2ecf20Sopenharmony_ci			writew(np->dspcfg, ioaddr + DSPCFG);
16178c2ecf20Sopenharmony_ci		}
16188c2ecf20Sopenharmony_ci		writew(0, ioaddr + PGSEL);
16198c2ecf20Sopenharmony_ci	}
16208c2ecf20Sopenharmony_ci}
16218c2ecf20Sopenharmony_ci
16228c2ecf20Sopenharmony_cistatic void undo_cable_magic(struct net_device *dev)
16238c2ecf20Sopenharmony_ci{
16248c2ecf20Sopenharmony_ci	u16 data;
16258c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
16268c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	if (dev->if_port != PORT_TP)
16298c2ecf20Sopenharmony_ci		return;
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci	if (np->srr >= SRR_DP83816_A5)
16328c2ecf20Sopenharmony_ci		return;
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	writew(1, ioaddr + PGSEL);
16358c2ecf20Sopenharmony_ci	/* make sure the lock bit is clear */
16368c2ecf20Sopenharmony_ci	data = readw(ioaddr + DSPCFG);
16378c2ecf20Sopenharmony_ci	np->dspcfg = data & ~DSPCFG_LOCK;
16388c2ecf20Sopenharmony_ci	writew(np->dspcfg, ioaddr + DSPCFG);
16398c2ecf20Sopenharmony_ci	writew(0, ioaddr + PGSEL);
16408c2ecf20Sopenharmony_ci}
16418c2ecf20Sopenharmony_ci
16428c2ecf20Sopenharmony_cistatic void check_link(struct net_device *dev)
16438c2ecf20Sopenharmony_ci{
16448c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
16458c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
16468c2ecf20Sopenharmony_ci	int duplex = np->duplex;
16478c2ecf20Sopenharmony_ci	u16 bmsr;
16488c2ecf20Sopenharmony_ci
16498c2ecf20Sopenharmony_ci	/* If we are ignoring the PHY then don't try reading it. */
16508c2ecf20Sopenharmony_ci	if (np->ignore_phy)
16518c2ecf20Sopenharmony_ci		goto propagate_state;
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_ci	/* The link status field is latched: it remains low after a temporary
16548c2ecf20Sopenharmony_ci	 * link failure until it's read. We need the current link status,
16558c2ecf20Sopenharmony_ci	 * thus read twice.
16568c2ecf20Sopenharmony_ci	 */
16578c2ecf20Sopenharmony_ci	mdio_read(dev, MII_BMSR);
16588c2ecf20Sopenharmony_ci	bmsr = mdio_read(dev, MII_BMSR);
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci	if (!(bmsr & BMSR_LSTATUS)) {
16618c2ecf20Sopenharmony_ci		if (netif_carrier_ok(dev)) {
16628c2ecf20Sopenharmony_ci			if (netif_msg_link(np))
16638c2ecf20Sopenharmony_ci				printk(KERN_NOTICE "%s: link down.\n",
16648c2ecf20Sopenharmony_ci				       dev->name);
16658c2ecf20Sopenharmony_ci			netif_carrier_off(dev);
16668c2ecf20Sopenharmony_ci			undo_cable_magic(dev);
16678c2ecf20Sopenharmony_ci		}
16688c2ecf20Sopenharmony_ci		return;
16698c2ecf20Sopenharmony_ci	}
16708c2ecf20Sopenharmony_ci	if (!netif_carrier_ok(dev)) {
16718c2ecf20Sopenharmony_ci		if (netif_msg_link(np))
16728c2ecf20Sopenharmony_ci			printk(KERN_NOTICE "%s: link up.\n", dev->name);
16738c2ecf20Sopenharmony_ci		netif_carrier_on(dev);
16748c2ecf20Sopenharmony_ci		do_cable_magic(dev);
16758c2ecf20Sopenharmony_ci	}
16768c2ecf20Sopenharmony_ci
16778c2ecf20Sopenharmony_ci	duplex = np->full_duplex;
16788c2ecf20Sopenharmony_ci	if (!duplex) {
16798c2ecf20Sopenharmony_ci		if (bmsr & BMSR_ANEGCOMPLETE) {
16808c2ecf20Sopenharmony_ci			int tmp = mii_nway_result(
16818c2ecf20Sopenharmony_ci				np->advertising & mdio_read(dev, MII_LPA));
16828c2ecf20Sopenharmony_ci			if (tmp == LPA_100FULL || tmp == LPA_10FULL)
16838c2ecf20Sopenharmony_ci				duplex = 1;
16848c2ecf20Sopenharmony_ci		} else if (mdio_read(dev, MII_BMCR) & BMCR_FULLDPLX)
16858c2ecf20Sopenharmony_ci			duplex = 1;
16868c2ecf20Sopenharmony_ci	}
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_cipropagate_state:
16898c2ecf20Sopenharmony_ci	/* if duplex is set then bit 28 must be set, too */
16908c2ecf20Sopenharmony_ci	if (duplex ^ !!(np->rx_config & RxAcceptTx)) {
16918c2ecf20Sopenharmony_ci		if (netif_msg_link(np))
16928c2ecf20Sopenharmony_ci			printk(KERN_INFO
16938c2ecf20Sopenharmony_ci				"%s: Setting %s-duplex based on negotiated "
16948c2ecf20Sopenharmony_ci				"link capability.\n", dev->name,
16958c2ecf20Sopenharmony_ci				duplex ? "full" : "half");
16968c2ecf20Sopenharmony_ci		if (duplex) {
16978c2ecf20Sopenharmony_ci			np->rx_config |= RxAcceptTx;
16988c2ecf20Sopenharmony_ci			np->tx_config |= TxCarrierIgn | TxHeartIgn;
16998c2ecf20Sopenharmony_ci		} else {
17008c2ecf20Sopenharmony_ci			np->rx_config &= ~RxAcceptTx;
17018c2ecf20Sopenharmony_ci			np->tx_config &= ~(TxCarrierIgn | TxHeartIgn);
17028c2ecf20Sopenharmony_ci		}
17038c2ecf20Sopenharmony_ci		writel(np->tx_config, ioaddr + TxConfig);
17048c2ecf20Sopenharmony_ci		writel(np->rx_config, ioaddr + RxConfig);
17058c2ecf20Sopenharmony_ci	}
17068c2ecf20Sopenharmony_ci}
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_cistatic void init_registers(struct net_device *dev)
17098c2ecf20Sopenharmony_ci{
17108c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
17118c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
17128c2ecf20Sopenharmony_ci
17138c2ecf20Sopenharmony_ci	init_phy_fixup(dev);
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_ci	/* clear any interrupts that are pending, such as wake events */
17168c2ecf20Sopenharmony_ci	readl(ioaddr + IntrStatus);
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_ci	writel(np->ring_dma, ioaddr + RxRingPtr);
17198c2ecf20Sopenharmony_ci	writel(np->ring_dma + RX_RING_SIZE * sizeof(struct netdev_desc),
17208c2ecf20Sopenharmony_ci		ioaddr + TxRingPtr);
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci	/* Initialize other registers.
17238c2ecf20Sopenharmony_ci	 * Configure the PCI bus bursts and FIFO thresholds.
17248c2ecf20Sopenharmony_ci	 * Configure for standard, in-spec Ethernet.
17258c2ecf20Sopenharmony_ci	 * Start with half-duplex. check_link will update
17268c2ecf20Sopenharmony_ci	 * to the correct settings.
17278c2ecf20Sopenharmony_ci	 */
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci	/* DRTH: 2: start tx if 64 bytes are in the fifo
17308c2ecf20Sopenharmony_ci	 * FLTH: 0x10: refill with next packet if 512 bytes are free
17318c2ecf20Sopenharmony_ci	 * MXDMA: 0: up to 256 byte bursts.
17328c2ecf20Sopenharmony_ci	 * 	MXDMA must be <= FLTH
17338c2ecf20Sopenharmony_ci	 * ECRETRY=1
17348c2ecf20Sopenharmony_ci	 * ATP=1
17358c2ecf20Sopenharmony_ci	 */
17368c2ecf20Sopenharmony_ci	np->tx_config = TxAutoPad | TxCollRetry | TxMxdma_256 |
17378c2ecf20Sopenharmony_ci				TX_FLTH_VAL | TX_DRTH_VAL_START;
17388c2ecf20Sopenharmony_ci	writel(np->tx_config, ioaddr + TxConfig);
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	/* DRTH 0x10: start copying to memory if 128 bytes are in the fifo
17418c2ecf20Sopenharmony_ci	 * MXDMA 0: up to 256 byte bursts
17428c2ecf20Sopenharmony_ci	 */
17438c2ecf20Sopenharmony_ci	np->rx_config = RxMxdma_256 | RX_DRTH_VAL;
17448c2ecf20Sopenharmony_ci	/* if receive ring now has bigger buffers than normal, enable jumbo */
17458c2ecf20Sopenharmony_ci	if (np->rx_buf_sz > NATSEMI_LONGPKT)
17468c2ecf20Sopenharmony_ci		np->rx_config |= RxAcceptLong;
17478c2ecf20Sopenharmony_ci
17488c2ecf20Sopenharmony_ci	writel(np->rx_config, ioaddr + RxConfig);
17498c2ecf20Sopenharmony_ci
17508c2ecf20Sopenharmony_ci	/* Disable PME:
17518c2ecf20Sopenharmony_ci	 * The PME bit is initialized from the EEPROM contents.
17528c2ecf20Sopenharmony_ci	 * PCI cards probably have PME disabled, but motherboard
17538c2ecf20Sopenharmony_ci	 * implementations may have PME set to enable WakeOnLan.
17548c2ecf20Sopenharmony_ci	 * With PME set the chip will scan incoming packets but
17558c2ecf20Sopenharmony_ci	 * nothing will be written to memory. */
17568c2ecf20Sopenharmony_ci	np->SavedClkRun = readl(ioaddr + ClkRun);
17578c2ecf20Sopenharmony_ci	writel(np->SavedClkRun & ~PMEEnable, ioaddr + ClkRun);
17588c2ecf20Sopenharmony_ci	if (np->SavedClkRun & PMEStatus && netif_msg_wol(np)) {
17598c2ecf20Sopenharmony_ci		printk(KERN_NOTICE "%s: Wake-up event %#08x\n",
17608c2ecf20Sopenharmony_ci			dev->name, readl(ioaddr + WOLCmd));
17618c2ecf20Sopenharmony_ci	}
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci	check_link(dev);
17648c2ecf20Sopenharmony_ci	__set_rx_mode(dev);
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci	/* Enable interrupts by setting the interrupt mask. */
17678c2ecf20Sopenharmony_ci	writel(DEFAULT_INTR, ioaddr + IntrMask);
17688c2ecf20Sopenharmony_ci	natsemi_irq_enable(dev);
17698c2ecf20Sopenharmony_ci
17708c2ecf20Sopenharmony_ci	writel(RxOn | TxOn, ioaddr + ChipCmd);
17718c2ecf20Sopenharmony_ci	writel(StatsClear, ioaddr + StatsCtrl); /* Clear Stats */
17728c2ecf20Sopenharmony_ci}
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_ci/*
17758c2ecf20Sopenharmony_ci * netdev_timer:
17768c2ecf20Sopenharmony_ci * Purpose:
17778c2ecf20Sopenharmony_ci * 1) check for link changes. Usually they are handled by the MII interrupt
17788c2ecf20Sopenharmony_ci *    but it doesn't hurt to check twice.
17798c2ecf20Sopenharmony_ci * 2) check for sudden death of the NIC:
17808c2ecf20Sopenharmony_ci *    It seems that a reference set for this chip went out with incorrect info,
17818c2ecf20Sopenharmony_ci *    and there exist boards that aren't quite right.  An unexpected voltage
17828c2ecf20Sopenharmony_ci *    drop can cause the PHY to get itself in a weird state (basically reset).
17838c2ecf20Sopenharmony_ci *    NOTE: this only seems to affect revC chips.  The user can disable
17848c2ecf20Sopenharmony_ci *    this check via dspcfg_workaround sysfs option.
17858c2ecf20Sopenharmony_ci * 3) check of death of the RX path due to OOM
17868c2ecf20Sopenharmony_ci */
17878c2ecf20Sopenharmony_cistatic void netdev_timer(struct timer_list *t)
17888c2ecf20Sopenharmony_ci{
17898c2ecf20Sopenharmony_ci	struct netdev_private *np = from_timer(np, t, timer);
17908c2ecf20Sopenharmony_ci	struct net_device *dev = np->dev;
17918c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
17928c2ecf20Sopenharmony_ci	int next_tick = NATSEMI_TIMER_FREQ;
17938c2ecf20Sopenharmony_ci	const int irq = np->pci_dev->irq;
17948c2ecf20Sopenharmony_ci
17958c2ecf20Sopenharmony_ci	if (netif_msg_timer(np)) {
17968c2ecf20Sopenharmony_ci		/* DO NOT read the IntrStatus register,
17978c2ecf20Sopenharmony_ci		 * a read clears any pending interrupts.
17988c2ecf20Sopenharmony_ci		 */
17998c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: Media selection timer tick.\n",
18008c2ecf20Sopenharmony_ci			dev->name);
18018c2ecf20Sopenharmony_ci	}
18028c2ecf20Sopenharmony_ci
18038c2ecf20Sopenharmony_ci	if (dev->if_port == PORT_TP) {
18048c2ecf20Sopenharmony_ci		u16 dspcfg;
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_ci		spin_lock_irq(&np->lock);
18078c2ecf20Sopenharmony_ci		/* check for a nasty random phy-reset - use dspcfg as a flag */
18088c2ecf20Sopenharmony_ci		writew(1, ioaddr+PGSEL);
18098c2ecf20Sopenharmony_ci		dspcfg = readw(ioaddr+DSPCFG);
18108c2ecf20Sopenharmony_ci		writew(0, ioaddr+PGSEL);
18118c2ecf20Sopenharmony_ci		if (np->dspcfg_workaround && dspcfg != np->dspcfg) {
18128c2ecf20Sopenharmony_ci			if (!netif_queue_stopped(dev)) {
18138c2ecf20Sopenharmony_ci				spin_unlock_irq(&np->lock);
18148c2ecf20Sopenharmony_ci				if (netif_msg_drv(np))
18158c2ecf20Sopenharmony_ci					printk(KERN_NOTICE "%s: possible phy reset: "
18168c2ecf20Sopenharmony_ci						"re-initializing\n", dev->name);
18178c2ecf20Sopenharmony_ci				disable_irq(irq);
18188c2ecf20Sopenharmony_ci				spin_lock_irq(&np->lock);
18198c2ecf20Sopenharmony_ci				natsemi_stop_rxtx(dev);
18208c2ecf20Sopenharmony_ci				dump_ring(dev);
18218c2ecf20Sopenharmony_ci				reinit_ring(dev);
18228c2ecf20Sopenharmony_ci				init_registers(dev);
18238c2ecf20Sopenharmony_ci				spin_unlock_irq(&np->lock);
18248c2ecf20Sopenharmony_ci				enable_irq(irq);
18258c2ecf20Sopenharmony_ci			} else {
18268c2ecf20Sopenharmony_ci				/* hurry back */
18278c2ecf20Sopenharmony_ci				next_tick = HZ;
18288c2ecf20Sopenharmony_ci				spin_unlock_irq(&np->lock);
18298c2ecf20Sopenharmony_ci			}
18308c2ecf20Sopenharmony_ci		} else {
18318c2ecf20Sopenharmony_ci			/* init_registers() calls check_link() for the above case */
18328c2ecf20Sopenharmony_ci			check_link(dev);
18338c2ecf20Sopenharmony_ci			spin_unlock_irq(&np->lock);
18348c2ecf20Sopenharmony_ci		}
18358c2ecf20Sopenharmony_ci	} else {
18368c2ecf20Sopenharmony_ci		spin_lock_irq(&np->lock);
18378c2ecf20Sopenharmony_ci		check_link(dev);
18388c2ecf20Sopenharmony_ci		spin_unlock_irq(&np->lock);
18398c2ecf20Sopenharmony_ci	}
18408c2ecf20Sopenharmony_ci	if (np->oom) {
18418c2ecf20Sopenharmony_ci		disable_irq(irq);
18428c2ecf20Sopenharmony_ci		np->oom = 0;
18438c2ecf20Sopenharmony_ci		refill_rx(dev);
18448c2ecf20Sopenharmony_ci		enable_irq(irq);
18458c2ecf20Sopenharmony_ci		if (!np->oom) {
18468c2ecf20Sopenharmony_ci			writel(RxOn, ioaddr + ChipCmd);
18478c2ecf20Sopenharmony_ci		} else {
18488c2ecf20Sopenharmony_ci			next_tick = 1;
18498c2ecf20Sopenharmony_ci		}
18508c2ecf20Sopenharmony_ci	}
18518c2ecf20Sopenharmony_ci
18528c2ecf20Sopenharmony_ci	if (next_tick > 1)
18538c2ecf20Sopenharmony_ci		mod_timer(&np->timer, round_jiffies(jiffies + next_tick));
18548c2ecf20Sopenharmony_ci	else
18558c2ecf20Sopenharmony_ci		mod_timer(&np->timer, jiffies + next_tick);
18568c2ecf20Sopenharmony_ci}
18578c2ecf20Sopenharmony_ci
18588c2ecf20Sopenharmony_cistatic void dump_ring(struct net_device *dev)
18598c2ecf20Sopenharmony_ci{
18608c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
18618c2ecf20Sopenharmony_ci
18628c2ecf20Sopenharmony_ci	if (netif_msg_pktdata(np)) {
18638c2ecf20Sopenharmony_ci		int i;
18648c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "  Tx ring at %p:\n", np->tx_ring);
18658c2ecf20Sopenharmony_ci		for (i = 0; i < TX_RING_SIZE; i++) {
18668c2ecf20Sopenharmony_ci			printk(KERN_DEBUG " #%d desc. %#08x %#08x %#08x.\n",
18678c2ecf20Sopenharmony_ci				i, np->tx_ring[i].next_desc,
18688c2ecf20Sopenharmony_ci				np->tx_ring[i].cmd_status,
18698c2ecf20Sopenharmony_ci				np->tx_ring[i].addr);
18708c2ecf20Sopenharmony_ci		}
18718c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "  Rx ring %p:\n", np->rx_ring);
18728c2ecf20Sopenharmony_ci		for (i = 0; i < RX_RING_SIZE; i++) {
18738c2ecf20Sopenharmony_ci			printk(KERN_DEBUG " #%d desc. %#08x %#08x %#08x.\n",
18748c2ecf20Sopenharmony_ci				i, np->rx_ring[i].next_desc,
18758c2ecf20Sopenharmony_ci				np->rx_ring[i].cmd_status,
18768c2ecf20Sopenharmony_ci				np->rx_ring[i].addr);
18778c2ecf20Sopenharmony_ci		}
18788c2ecf20Sopenharmony_ci	}
18798c2ecf20Sopenharmony_ci}
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_cistatic void ns_tx_timeout(struct net_device *dev, unsigned int txqueue)
18828c2ecf20Sopenharmony_ci{
18838c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
18848c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
18858c2ecf20Sopenharmony_ci	const int irq = np->pci_dev->irq;
18868c2ecf20Sopenharmony_ci
18878c2ecf20Sopenharmony_ci	disable_irq(irq);
18888c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
18898c2ecf20Sopenharmony_ci	if (!np->hands_off) {
18908c2ecf20Sopenharmony_ci		if (netif_msg_tx_err(np))
18918c2ecf20Sopenharmony_ci			printk(KERN_WARNING
18928c2ecf20Sopenharmony_ci				"%s: Transmit timed out, status %#08x,"
18938c2ecf20Sopenharmony_ci				" resetting...\n",
18948c2ecf20Sopenharmony_ci				dev->name, readl(ioaddr + IntrStatus));
18958c2ecf20Sopenharmony_ci		dump_ring(dev);
18968c2ecf20Sopenharmony_ci
18978c2ecf20Sopenharmony_ci		natsemi_reset(dev);
18988c2ecf20Sopenharmony_ci		reinit_ring(dev);
18998c2ecf20Sopenharmony_ci		init_registers(dev);
19008c2ecf20Sopenharmony_ci	} else {
19018c2ecf20Sopenharmony_ci		printk(KERN_WARNING
19028c2ecf20Sopenharmony_ci			"%s: tx_timeout while in hands_off state?\n",
19038c2ecf20Sopenharmony_ci			dev->name);
19048c2ecf20Sopenharmony_ci	}
19058c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
19068c2ecf20Sopenharmony_ci	enable_irq(irq);
19078c2ecf20Sopenharmony_ci
19088c2ecf20Sopenharmony_ci	netif_trans_update(dev); /* prevent tx timeout */
19098c2ecf20Sopenharmony_ci	dev->stats.tx_errors++;
19108c2ecf20Sopenharmony_ci	netif_wake_queue(dev);
19118c2ecf20Sopenharmony_ci}
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_cistatic int alloc_ring(struct net_device *dev)
19148c2ecf20Sopenharmony_ci{
19158c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
19168c2ecf20Sopenharmony_ci	np->rx_ring = dma_alloc_coherent(&np->pci_dev->dev,
19178c2ecf20Sopenharmony_ci					 sizeof(struct netdev_desc) * (RX_RING_SIZE + TX_RING_SIZE),
19188c2ecf20Sopenharmony_ci					 &np->ring_dma, GFP_KERNEL);
19198c2ecf20Sopenharmony_ci	if (!np->rx_ring)
19208c2ecf20Sopenharmony_ci		return -ENOMEM;
19218c2ecf20Sopenharmony_ci	np->tx_ring = &np->rx_ring[RX_RING_SIZE];
19228c2ecf20Sopenharmony_ci	return 0;
19238c2ecf20Sopenharmony_ci}
19248c2ecf20Sopenharmony_ci
19258c2ecf20Sopenharmony_cistatic void refill_rx(struct net_device *dev)
19268c2ecf20Sopenharmony_ci{
19278c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
19288c2ecf20Sopenharmony_ci
19298c2ecf20Sopenharmony_ci	/* Refill the Rx ring buffers. */
19308c2ecf20Sopenharmony_ci	for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) {
19318c2ecf20Sopenharmony_ci		struct sk_buff *skb;
19328c2ecf20Sopenharmony_ci		int entry = np->dirty_rx % RX_RING_SIZE;
19338c2ecf20Sopenharmony_ci		if (np->rx_skbuff[entry] == NULL) {
19348c2ecf20Sopenharmony_ci			unsigned int buflen = np->rx_buf_sz+NATSEMI_PADDING;
19358c2ecf20Sopenharmony_ci			skb = netdev_alloc_skb(dev, buflen);
19368c2ecf20Sopenharmony_ci			np->rx_skbuff[entry] = skb;
19378c2ecf20Sopenharmony_ci			if (skb == NULL)
19388c2ecf20Sopenharmony_ci				break; /* Better luck next round. */
19398c2ecf20Sopenharmony_ci			np->rx_dma[entry] = dma_map_single(&np->pci_dev->dev,
19408c2ecf20Sopenharmony_ci							   skb->data, buflen,
19418c2ecf20Sopenharmony_ci							   DMA_FROM_DEVICE);
19428c2ecf20Sopenharmony_ci			if (dma_mapping_error(&np->pci_dev->dev, np->rx_dma[entry])) {
19438c2ecf20Sopenharmony_ci				dev_kfree_skb_any(skb);
19448c2ecf20Sopenharmony_ci				np->rx_skbuff[entry] = NULL;
19458c2ecf20Sopenharmony_ci				break; /* Better luck next round. */
19468c2ecf20Sopenharmony_ci			}
19478c2ecf20Sopenharmony_ci			np->rx_ring[entry].addr = cpu_to_le32(np->rx_dma[entry]);
19488c2ecf20Sopenharmony_ci		}
19498c2ecf20Sopenharmony_ci		np->rx_ring[entry].cmd_status = cpu_to_le32(np->rx_buf_sz);
19508c2ecf20Sopenharmony_ci	}
19518c2ecf20Sopenharmony_ci	if (np->cur_rx - np->dirty_rx == RX_RING_SIZE) {
19528c2ecf20Sopenharmony_ci		if (netif_msg_rx_err(np))
19538c2ecf20Sopenharmony_ci			printk(KERN_WARNING "%s: going OOM.\n", dev->name);
19548c2ecf20Sopenharmony_ci		np->oom = 1;
19558c2ecf20Sopenharmony_ci	}
19568c2ecf20Sopenharmony_ci}
19578c2ecf20Sopenharmony_ci
19588c2ecf20Sopenharmony_cistatic void set_bufsize(struct net_device *dev)
19598c2ecf20Sopenharmony_ci{
19608c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
19618c2ecf20Sopenharmony_ci	if (dev->mtu <= ETH_DATA_LEN)
19628c2ecf20Sopenharmony_ci		np->rx_buf_sz = ETH_DATA_LEN + NATSEMI_HEADERS;
19638c2ecf20Sopenharmony_ci	else
19648c2ecf20Sopenharmony_ci		np->rx_buf_sz = dev->mtu + NATSEMI_HEADERS;
19658c2ecf20Sopenharmony_ci}
19668c2ecf20Sopenharmony_ci
19678c2ecf20Sopenharmony_ci/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
19688c2ecf20Sopenharmony_cistatic void init_ring(struct net_device *dev)
19698c2ecf20Sopenharmony_ci{
19708c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
19718c2ecf20Sopenharmony_ci	int i;
19728c2ecf20Sopenharmony_ci
19738c2ecf20Sopenharmony_ci	/* 1) TX ring */
19748c2ecf20Sopenharmony_ci	np->dirty_tx = np->cur_tx = 0;
19758c2ecf20Sopenharmony_ci	for (i = 0; i < TX_RING_SIZE; i++) {
19768c2ecf20Sopenharmony_ci		np->tx_skbuff[i] = NULL;
19778c2ecf20Sopenharmony_ci		np->tx_ring[i].next_desc = cpu_to_le32(np->ring_dma
19788c2ecf20Sopenharmony_ci			+sizeof(struct netdev_desc)
19798c2ecf20Sopenharmony_ci			*((i+1)%TX_RING_SIZE+RX_RING_SIZE));
19808c2ecf20Sopenharmony_ci		np->tx_ring[i].cmd_status = 0;
19818c2ecf20Sopenharmony_ci	}
19828c2ecf20Sopenharmony_ci
19838c2ecf20Sopenharmony_ci	/* 2) RX ring */
19848c2ecf20Sopenharmony_ci	np->dirty_rx = 0;
19858c2ecf20Sopenharmony_ci	np->cur_rx = RX_RING_SIZE;
19868c2ecf20Sopenharmony_ci	np->oom = 0;
19878c2ecf20Sopenharmony_ci	set_bufsize(dev);
19888c2ecf20Sopenharmony_ci
19898c2ecf20Sopenharmony_ci	np->rx_head_desc = &np->rx_ring[0];
19908c2ecf20Sopenharmony_ci
19918c2ecf20Sopenharmony_ci	/* Please be careful before changing this loop - at least gcc-2.95.1
19928c2ecf20Sopenharmony_ci	 * miscompiles it otherwise.
19938c2ecf20Sopenharmony_ci	 */
19948c2ecf20Sopenharmony_ci	/* Initialize all Rx descriptors. */
19958c2ecf20Sopenharmony_ci	for (i = 0; i < RX_RING_SIZE; i++) {
19968c2ecf20Sopenharmony_ci		np->rx_ring[i].next_desc = cpu_to_le32(np->ring_dma
19978c2ecf20Sopenharmony_ci				+sizeof(struct netdev_desc)
19988c2ecf20Sopenharmony_ci				*((i+1)%RX_RING_SIZE));
19998c2ecf20Sopenharmony_ci		np->rx_ring[i].cmd_status = cpu_to_le32(DescOwn);
20008c2ecf20Sopenharmony_ci		np->rx_skbuff[i] = NULL;
20018c2ecf20Sopenharmony_ci	}
20028c2ecf20Sopenharmony_ci	refill_rx(dev);
20038c2ecf20Sopenharmony_ci	dump_ring(dev);
20048c2ecf20Sopenharmony_ci}
20058c2ecf20Sopenharmony_ci
20068c2ecf20Sopenharmony_cistatic void drain_tx(struct net_device *dev)
20078c2ecf20Sopenharmony_ci{
20088c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
20098c2ecf20Sopenharmony_ci	int i;
20108c2ecf20Sopenharmony_ci
20118c2ecf20Sopenharmony_ci	for (i = 0; i < TX_RING_SIZE; i++) {
20128c2ecf20Sopenharmony_ci		if (np->tx_skbuff[i]) {
20138c2ecf20Sopenharmony_ci			dma_unmap_single(&np->pci_dev->dev, np->tx_dma[i],
20148c2ecf20Sopenharmony_ci					 np->tx_skbuff[i]->len, DMA_TO_DEVICE);
20158c2ecf20Sopenharmony_ci			dev_kfree_skb(np->tx_skbuff[i]);
20168c2ecf20Sopenharmony_ci			dev->stats.tx_dropped++;
20178c2ecf20Sopenharmony_ci		}
20188c2ecf20Sopenharmony_ci		np->tx_skbuff[i] = NULL;
20198c2ecf20Sopenharmony_ci	}
20208c2ecf20Sopenharmony_ci}
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_cistatic void drain_rx(struct net_device *dev)
20238c2ecf20Sopenharmony_ci{
20248c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
20258c2ecf20Sopenharmony_ci	unsigned int buflen = np->rx_buf_sz;
20268c2ecf20Sopenharmony_ci	int i;
20278c2ecf20Sopenharmony_ci
20288c2ecf20Sopenharmony_ci	/* Free all the skbuffs in the Rx queue. */
20298c2ecf20Sopenharmony_ci	for (i = 0; i < RX_RING_SIZE; i++) {
20308c2ecf20Sopenharmony_ci		np->rx_ring[i].cmd_status = 0;
20318c2ecf20Sopenharmony_ci		np->rx_ring[i].addr = cpu_to_le32(0xBADF00D0); /* An invalid address. */
20328c2ecf20Sopenharmony_ci		if (np->rx_skbuff[i]) {
20338c2ecf20Sopenharmony_ci			dma_unmap_single(&np->pci_dev->dev, np->rx_dma[i],
20348c2ecf20Sopenharmony_ci					 buflen + NATSEMI_PADDING,
20358c2ecf20Sopenharmony_ci					 DMA_FROM_DEVICE);
20368c2ecf20Sopenharmony_ci			dev_kfree_skb(np->rx_skbuff[i]);
20378c2ecf20Sopenharmony_ci		}
20388c2ecf20Sopenharmony_ci		np->rx_skbuff[i] = NULL;
20398c2ecf20Sopenharmony_ci	}
20408c2ecf20Sopenharmony_ci}
20418c2ecf20Sopenharmony_ci
20428c2ecf20Sopenharmony_cistatic void drain_ring(struct net_device *dev)
20438c2ecf20Sopenharmony_ci{
20448c2ecf20Sopenharmony_ci	drain_rx(dev);
20458c2ecf20Sopenharmony_ci	drain_tx(dev);
20468c2ecf20Sopenharmony_ci}
20478c2ecf20Sopenharmony_ci
20488c2ecf20Sopenharmony_cistatic void free_ring(struct net_device *dev)
20498c2ecf20Sopenharmony_ci{
20508c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
20518c2ecf20Sopenharmony_ci	dma_free_coherent(&np->pci_dev->dev,
20528c2ecf20Sopenharmony_ci			  sizeof(struct netdev_desc) * (RX_RING_SIZE + TX_RING_SIZE),
20538c2ecf20Sopenharmony_ci			  np->rx_ring, np->ring_dma);
20548c2ecf20Sopenharmony_ci}
20558c2ecf20Sopenharmony_ci
20568c2ecf20Sopenharmony_cistatic void reinit_rx(struct net_device *dev)
20578c2ecf20Sopenharmony_ci{
20588c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
20598c2ecf20Sopenharmony_ci	int i;
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_ci	/* RX Ring */
20628c2ecf20Sopenharmony_ci	np->dirty_rx = 0;
20638c2ecf20Sopenharmony_ci	np->cur_rx = RX_RING_SIZE;
20648c2ecf20Sopenharmony_ci	np->rx_head_desc = &np->rx_ring[0];
20658c2ecf20Sopenharmony_ci	/* Initialize all Rx descriptors. */
20668c2ecf20Sopenharmony_ci	for (i = 0; i < RX_RING_SIZE; i++)
20678c2ecf20Sopenharmony_ci		np->rx_ring[i].cmd_status = cpu_to_le32(DescOwn);
20688c2ecf20Sopenharmony_ci
20698c2ecf20Sopenharmony_ci	refill_rx(dev);
20708c2ecf20Sopenharmony_ci}
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_cistatic void reinit_ring(struct net_device *dev)
20738c2ecf20Sopenharmony_ci{
20748c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
20758c2ecf20Sopenharmony_ci	int i;
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ci	/* drain TX ring */
20788c2ecf20Sopenharmony_ci	drain_tx(dev);
20798c2ecf20Sopenharmony_ci	np->dirty_tx = np->cur_tx = 0;
20808c2ecf20Sopenharmony_ci	for (i=0;i<TX_RING_SIZE;i++)
20818c2ecf20Sopenharmony_ci		np->tx_ring[i].cmd_status = 0;
20828c2ecf20Sopenharmony_ci
20838c2ecf20Sopenharmony_ci	reinit_rx(dev);
20848c2ecf20Sopenharmony_ci}
20858c2ecf20Sopenharmony_ci
20868c2ecf20Sopenharmony_cistatic netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev)
20878c2ecf20Sopenharmony_ci{
20888c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
20898c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
20908c2ecf20Sopenharmony_ci	unsigned entry;
20918c2ecf20Sopenharmony_ci	unsigned long flags;
20928c2ecf20Sopenharmony_ci
20938c2ecf20Sopenharmony_ci	/* Note: Ordering is important here, set the field with the
20948c2ecf20Sopenharmony_ci	   "ownership" bit last, and only then increment cur_tx. */
20958c2ecf20Sopenharmony_ci
20968c2ecf20Sopenharmony_ci	/* Calculate the next Tx descriptor entry. */
20978c2ecf20Sopenharmony_ci	entry = np->cur_tx % TX_RING_SIZE;
20988c2ecf20Sopenharmony_ci
20998c2ecf20Sopenharmony_ci	np->tx_skbuff[entry] = skb;
21008c2ecf20Sopenharmony_ci	np->tx_dma[entry] = dma_map_single(&np->pci_dev->dev, skb->data,
21018c2ecf20Sopenharmony_ci					   skb->len, DMA_TO_DEVICE);
21028c2ecf20Sopenharmony_ci	if (dma_mapping_error(&np->pci_dev->dev, np->tx_dma[entry])) {
21038c2ecf20Sopenharmony_ci		np->tx_skbuff[entry] = NULL;
21048c2ecf20Sopenharmony_ci		dev_kfree_skb_irq(skb);
21058c2ecf20Sopenharmony_ci		dev->stats.tx_dropped++;
21068c2ecf20Sopenharmony_ci		return NETDEV_TX_OK;
21078c2ecf20Sopenharmony_ci	}
21088c2ecf20Sopenharmony_ci
21098c2ecf20Sopenharmony_ci	np->tx_ring[entry].addr = cpu_to_le32(np->tx_dma[entry]);
21108c2ecf20Sopenharmony_ci
21118c2ecf20Sopenharmony_ci	spin_lock_irqsave(&np->lock, flags);
21128c2ecf20Sopenharmony_ci
21138c2ecf20Sopenharmony_ci	if (!np->hands_off) {
21148c2ecf20Sopenharmony_ci		np->tx_ring[entry].cmd_status = cpu_to_le32(DescOwn | skb->len);
21158c2ecf20Sopenharmony_ci		/* StrongARM: Explicitly cache flush np->tx_ring and
21168c2ecf20Sopenharmony_ci		 * skb->data,skb->len. */
21178c2ecf20Sopenharmony_ci		wmb();
21188c2ecf20Sopenharmony_ci		np->cur_tx++;
21198c2ecf20Sopenharmony_ci		if (np->cur_tx - np->dirty_tx >= TX_QUEUE_LEN - 1) {
21208c2ecf20Sopenharmony_ci			netdev_tx_done(dev);
21218c2ecf20Sopenharmony_ci			if (np->cur_tx - np->dirty_tx >= TX_QUEUE_LEN - 1)
21228c2ecf20Sopenharmony_ci				netif_stop_queue(dev);
21238c2ecf20Sopenharmony_ci		}
21248c2ecf20Sopenharmony_ci		/* Wake the potentially-idle transmit channel. */
21258c2ecf20Sopenharmony_ci		writel(TxOn, ioaddr + ChipCmd);
21268c2ecf20Sopenharmony_ci	} else {
21278c2ecf20Sopenharmony_ci		dev_kfree_skb_irq(skb);
21288c2ecf20Sopenharmony_ci		dev->stats.tx_dropped++;
21298c2ecf20Sopenharmony_ci	}
21308c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&np->lock, flags);
21318c2ecf20Sopenharmony_ci
21328c2ecf20Sopenharmony_ci	if (netif_msg_tx_queued(np)) {
21338c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n",
21348c2ecf20Sopenharmony_ci			dev->name, np->cur_tx, entry);
21358c2ecf20Sopenharmony_ci	}
21368c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
21378c2ecf20Sopenharmony_ci}
21388c2ecf20Sopenharmony_ci
21398c2ecf20Sopenharmony_cistatic void netdev_tx_done(struct net_device *dev)
21408c2ecf20Sopenharmony_ci{
21418c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
21428c2ecf20Sopenharmony_ci
21438c2ecf20Sopenharmony_ci	for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
21448c2ecf20Sopenharmony_ci		int entry = np->dirty_tx % TX_RING_SIZE;
21458c2ecf20Sopenharmony_ci		if (np->tx_ring[entry].cmd_status & cpu_to_le32(DescOwn))
21468c2ecf20Sopenharmony_ci			break;
21478c2ecf20Sopenharmony_ci		if (netif_msg_tx_done(np))
21488c2ecf20Sopenharmony_ci			printk(KERN_DEBUG
21498c2ecf20Sopenharmony_ci				"%s: tx frame #%d finished, status %#08x.\n",
21508c2ecf20Sopenharmony_ci					dev->name, np->dirty_tx,
21518c2ecf20Sopenharmony_ci					le32_to_cpu(np->tx_ring[entry].cmd_status));
21528c2ecf20Sopenharmony_ci		if (np->tx_ring[entry].cmd_status & cpu_to_le32(DescPktOK)) {
21538c2ecf20Sopenharmony_ci			dev->stats.tx_packets++;
21548c2ecf20Sopenharmony_ci			dev->stats.tx_bytes += np->tx_skbuff[entry]->len;
21558c2ecf20Sopenharmony_ci		} else { /* Various Tx errors */
21568c2ecf20Sopenharmony_ci			int tx_status =
21578c2ecf20Sopenharmony_ci				le32_to_cpu(np->tx_ring[entry].cmd_status);
21588c2ecf20Sopenharmony_ci			if (tx_status & (DescTxAbort|DescTxExcColl))
21598c2ecf20Sopenharmony_ci				dev->stats.tx_aborted_errors++;
21608c2ecf20Sopenharmony_ci			if (tx_status & DescTxFIFO)
21618c2ecf20Sopenharmony_ci				dev->stats.tx_fifo_errors++;
21628c2ecf20Sopenharmony_ci			if (tx_status & DescTxCarrier)
21638c2ecf20Sopenharmony_ci				dev->stats.tx_carrier_errors++;
21648c2ecf20Sopenharmony_ci			if (tx_status & DescTxOOWCol)
21658c2ecf20Sopenharmony_ci				dev->stats.tx_window_errors++;
21668c2ecf20Sopenharmony_ci			dev->stats.tx_errors++;
21678c2ecf20Sopenharmony_ci		}
21688c2ecf20Sopenharmony_ci		dma_unmap_single(&np->pci_dev->dev, np->tx_dma[entry],
21698c2ecf20Sopenharmony_ci				 np->tx_skbuff[entry]->len, DMA_TO_DEVICE);
21708c2ecf20Sopenharmony_ci		/* Free the original skb. */
21718c2ecf20Sopenharmony_ci		dev_consume_skb_irq(np->tx_skbuff[entry]);
21728c2ecf20Sopenharmony_ci		np->tx_skbuff[entry] = NULL;
21738c2ecf20Sopenharmony_ci	}
21748c2ecf20Sopenharmony_ci	if (netif_queue_stopped(dev) &&
21758c2ecf20Sopenharmony_ci	    np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) {
21768c2ecf20Sopenharmony_ci		/* The ring is no longer full, wake queue. */
21778c2ecf20Sopenharmony_ci		netif_wake_queue(dev);
21788c2ecf20Sopenharmony_ci	}
21798c2ecf20Sopenharmony_ci}
21808c2ecf20Sopenharmony_ci
21818c2ecf20Sopenharmony_ci/* The interrupt handler doesn't actually handle interrupts itself, it
21828c2ecf20Sopenharmony_ci * schedules a NAPI poll if there is anything to do. */
21838c2ecf20Sopenharmony_cistatic irqreturn_t intr_handler(int irq, void *dev_instance)
21848c2ecf20Sopenharmony_ci{
21858c2ecf20Sopenharmony_ci	struct net_device *dev = dev_instance;
21868c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
21878c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
21888c2ecf20Sopenharmony_ci
21898c2ecf20Sopenharmony_ci	/* Reading IntrStatus automatically acknowledges so don't do
21908c2ecf20Sopenharmony_ci	 * that while interrupts are disabled, (for example, while a
21918c2ecf20Sopenharmony_ci	 * poll is scheduled).  */
21928c2ecf20Sopenharmony_ci	if (np->hands_off || !readl(ioaddr + IntrEnable))
21938c2ecf20Sopenharmony_ci		return IRQ_NONE;
21948c2ecf20Sopenharmony_ci
21958c2ecf20Sopenharmony_ci	np->intr_status = readl(ioaddr + IntrStatus);
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_ci	if (!np->intr_status)
21988c2ecf20Sopenharmony_ci		return IRQ_NONE;
21998c2ecf20Sopenharmony_ci
22008c2ecf20Sopenharmony_ci	if (netif_msg_intr(np))
22018c2ecf20Sopenharmony_ci		printk(KERN_DEBUG
22028c2ecf20Sopenharmony_ci		       "%s: Interrupt, status %#08x, mask %#08x.\n",
22038c2ecf20Sopenharmony_ci		       dev->name, np->intr_status,
22048c2ecf20Sopenharmony_ci		       readl(ioaddr + IntrMask));
22058c2ecf20Sopenharmony_ci
22068c2ecf20Sopenharmony_ci	prefetch(&np->rx_skbuff[np->cur_rx % RX_RING_SIZE]);
22078c2ecf20Sopenharmony_ci
22088c2ecf20Sopenharmony_ci	if (napi_schedule_prep(&np->napi)) {
22098c2ecf20Sopenharmony_ci		/* Disable interrupts and register for poll */
22108c2ecf20Sopenharmony_ci		natsemi_irq_disable(dev);
22118c2ecf20Sopenharmony_ci		__napi_schedule(&np->napi);
22128c2ecf20Sopenharmony_ci	} else
22138c2ecf20Sopenharmony_ci		printk(KERN_WARNING
22148c2ecf20Sopenharmony_ci	       	       "%s: Ignoring interrupt, status %#08x, mask %#08x.\n",
22158c2ecf20Sopenharmony_ci		       dev->name, np->intr_status,
22168c2ecf20Sopenharmony_ci		       readl(ioaddr + IntrMask));
22178c2ecf20Sopenharmony_ci
22188c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
22198c2ecf20Sopenharmony_ci}
22208c2ecf20Sopenharmony_ci
22218c2ecf20Sopenharmony_ci/* This is the NAPI poll routine.  As well as the standard RX handling
22228c2ecf20Sopenharmony_ci * it also handles all other interrupts that the chip might raise.
22238c2ecf20Sopenharmony_ci */
22248c2ecf20Sopenharmony_cistatic int natsemi_poll(struct napi_struct *napi, int budget)
22258c2ecf20Sopenharmony_ci{
22268c2ecf20Sopenharmony_ci	struct netdev_private *np = container_of(napi, struct netdev_private, napi);
22278c2ecf20Sopenharmony_ci	struct net_device *dev = np->dev;
22288c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
22298c2ecf20Sopenharmony_ci	int work_done = 0;
22308c2ecf20Sopenharmony_ci
22318c2ecf20Sopenharmony_ci	do {
22328c2ecf20Sopenharmony_ci		if (netif_msg_intr(np))
22338c2ecf20Sopenharmony_ci			printk(KERN_DEBUG
22348c2ecf20Sopenharmony_ci			       "%s: Poll, status %#08x, mask %#08x.\n",
22358c2ecf20Sopenharmony_ci			       dev->name, np->intr_status,
22368c2ecf20Sopenharmony_ci			       readl(ioaddr + IntrMask));
22378c2ecf20Sopenharmony_ci
22388c2ecf20Sopenharmony_ci		/* netdev_rx() may read IntrStatus again if the RX state
22398c2ecf20Sopenharmony_ci		 * machine falls over so do it first. */
22408c2ecf20Sopenharmony_ci		if (np->intr_status &
22418c2ecf20Sopenharmony_ci		    (IntrRxDone | IntrRxIntr | RxStatusFIFOOver |
22428c2ecf20Sopenharmony_ci		     IntrRxErr | IntrRxOverrun)) {
22438c2ecf20Sopenharmony_ci			netdev_rx(dev, &work_done, budget);
22448c2ecf20Sopenharmony_ci		}
22458c2ecf20Sopenharmony_ci
22468c2ecf20Sopenharmony_ci		if (np->intr_status &
22478c2ecf20Sopenharmony_ci		    (IntrTxDone | IntrTxIntr | IntrTxIdle | IntrTxErr)) {
22488c2ecf20Sopenharmony_ci			spin_lock(&np->lock);
22498c2ecf20Sopenharmony_ci			netdev_tx_done(dev);
22508c2ecf20Sopenharmony_ci			spin_unlock(&np->lock);
22518c2ecf20Sopenharmony_ci		}
22528c2ecf20Sopenharmony_ci
22538c2ecf20Sopenharmony_ci		/* Abnormal error summary/uncommon events handlers. */
22548c2ecf20Sopenharmony_ci		if (np->intr_status & IntrAbnormalSummary)
22558c2ecf20Sopenharmony_ci			netdev_error(dev, np->intr_status);
22568c2ecf20Sopenharmony_ci
22578c2ecf20Sopenharmony_ci		if (work_done >= budget)
22588c2ecf20Sopenharmony_ci			return work_done;
22598c2ecf20Sopenharmony_ci
22608c2ecf20Sopenharmony_ci		np->intr_status = readl(ioaddr + IntrStatus);
22618c2ecf20Sopenharmony_ci	} while (np->intr_status);
22628c2ecf20Sopenharmony_ci
22638c2ecf20Sopenharmony_ci	napi_complete_done(napi, work_done);
22648c2ecf20Sopenharmony_ci
22658c2ecf20Sopenharmony_ci	/* Reenable interrupts providing nothing is trying to shut
22668c2ecf20Sopenharmony_ci	 * the chip down. */
22678c2ecf20Sopenharmony_ci	spin_lock(&np->lock);
22688c2ecf20Sopenharmony_ci	if (!np->hands_off)
22698c2ecf20Sopenharmony_ci		natsemi_irq_enable(dev);
22708c2ecf20Sopenharmony_ci	spin_unlock(&np->lock);
22718c2ecf20Sopenharmony_ci
22728c2ecf20Sopenharmony_ci	return work_done;
22738c2ecf20Sopenharmony_ci}
22748c2ecf20Sopenharmony_ci
22758c2ecf20Sopenharmony_ci/* This routine is logically part of the interrupt handler, but separated
22768c2ecf20Sopenharmony_ci   for clarity and better register allocation. */
22778c2ecf20Sopenharmony_cistatic void netdev_rx(struct net_device *dev, int *work_done, int work_to_do)
22788c2ecf20Sopenharmony_ci{
22798c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
22808c2ecf20Sopenharmony_ci	int entry = np->cur_rx % RX_RING_SIZE;
22818c2ecf20Sopenharmony_ci	int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx;
22828c2ecf20Sopenharmony_ci	s32 desc_status = le32_to_cpu(np->rx_head_desc->cmd_status);
22838c2ecf20Sopenharmony_ci	unsigned int buflen = np->rx_buf_sz;
22848c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
22858c2ecf20Sopenharmony_ci
22868c2ecf20Sopenharmony_ci	/* If the driver owns the next entry it's a new packet. Send it up. */
22878c2ecf20Sopenharmony_ci	while (desc_status < 0) { /* e.g. & DescOwn */
22888c2ecf20Sopenharmony_ci		int pkt_len;
22898c2ecf20Sopenharmony_ci		if (netif_msg_rx_status(np))
22908c2ecf20Sopenharmony_ci			printk(KERN_DEBUG
22918c2ecf20Sopenharmony_ci				"  netdev_rx() entry %d status was %#08x.\n",
22928c2ecf20Sopenharmony_ci				entry, desc_status);
22938c2ecf20Sopenharmony_ci		if (--boguscnt < 0)
22948c2ecf20Sopenharmony_ci			break;
22958c2ecf20Sopenharmony_ci
22968c2ecf20Sopenharmony_ci		if (*work_done >= work_to_do)
22978c2ecf20Sopenharmony_ci			break;
22988c2ecf20Sopenharmony_ci
22998c2ecf20Sopenharmony_ci		(*work_done)++;
23008c2ecf20Sopenharmony_ci
23018c2ecf20Sopenharmony_ci		pkt_len = (desc_status & DescSizeMask) - 4;
23028c2ecf20Sopenharmony_ci		if ((desc_status&(DescMore|DescPktOK|DescRxLong)) != DescPktOK){
23038c2ecf20Sopenharmony_ci			if (desc_status & DescMore) {
23048c2ecf20Sopenharmony_ci				unsigned long flags;
23058c2ecf20Sopenharmony_ci
23068c2ecf20Sopenharmony_ci				if (netif_msg_rx_err(np))
23078c2ecf20Sopenharmony_ci					printk(KERN_WARNING
23088c2ecf20Sopenharmony_ci						"%s: Oversized(?) Ethernet "
23098c2ecf20Sopenharmony_ci						"frame spanned multiple "
23108c2ecf20Sopenharmony_ci						"buffers, entry %#08x "
23118c2ecf20Sopenharmony_ci						"status %#08x.\n", dev->name,
23128c2ecf20Sopenharmony_ci						np->cur_rx, desc_status);
23138c2ecf20Sopenharmony_ci				dev->stats.rx_length_errors++;
23148c2ecf20Sopenharmony_ci
23158c2ecf20Sopenharmony_ci				/* The RX state machine has probably
23168c2ecf20Sopenharmony_ci				 * locked up beneath us.  Follow the
23178c2ecf20Sopenharmony_ci				 * reset procedure documented in
23188c2ecf20Sopenharmony_ci				 * AN-1287. */
23198c2ecf20Sopenharmony_ci
23208c2ecf20Sopenharmony_ci				spin_lock_irqsave(&np->lock, flags);
23218c2ecf20Sopenharmony_ci				reset_rx(dev);
23228c2ecf20Sopenharmony_ci				reinit_rx(dev);
23238c2ecf20Sopenharmony_ci				writel(np->ring_dma, ioaddr + RxRingPtr);
23248c2ecf20Sopenharmony_ci				check_link(dev);
23258c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&np->lock, flags);
23268c2ecf20Sopenharmony_ci
23278c2ecf20Sopenharmony_ci				/* We'll enable RX on exit from this
23288c2ecf20Sopenharmony_ci				 * function. */
23298c2ecf20Sopenharmony_ci				break;
23308c2ecf20Sopenharmony_ci
23318c2ecf20Sopenharmony_ci			} else {
23328c2ecf20Sopenharmony_ci				/* There was an error. */
23338c2ecf20Sopenharmony_ci				dev->stats.rx_errors++;
23348c2ecf20Sopenharmony_ci				if (desc_status & (DescRxAbort|DescRxOver))
23358c2ecf20Sopenharmony_ci					dev->stats.rx_over_errors++;
23368c2ecf20Sopenharmony_ci				if (desc_status & (DescRxLong|DescRxRunt))
23378c2ecf20Sopenharmony_ci					dev->stats.rx_length_errors++;
23388c2ecf20Sopenharmony_ci				if (desc_status & (DescRxInvalid|DescRxAlign))
23398c2ecf20Sopenharmony_ci					dev->stats.rx_frame_errors++;
23408c2ecf20Sopenharmony_ci				if (desc_status & DescRxCRC)
23418c2ecf20Sopenharmony_ci					dev->stats.rx_crc_errors++;
23428c2ecf20Sopenharmony_ci			}
23438c2ecf20Sopenharmony_ci		} else if (pkt_len > np->rx_buf_sz) {
23448c2ecf20Sopenharmony_ci			/* if this is the tail of a double buffer
23458c2ecf20Sopenharmony_ci			 * packet, we've already counted the error
23468c2ecf20Sopenharmony_ci			 * on the first part.  Ignore the second half.
23478c2ecf20Sopenharmony_ci			 */
23488c2ecf20Sopenharmony_ci		} else {
23498c2ecf20Sopenharmony_ci			struct sk_buff *skb;
23508c2ecf20Sopenharmony_ci			/* Omit CRC size. */
23518c2ecf20Sopenharmony_ci			/* Check if the packet is long enough to accept
23528c2ecf20Sopenharmony_ci			 * without copying to a minimally-sized skbuff. */
23538c2ecf20Sopenharmony_ci			if (pkt_len < rx_copybreak &&
23548c2ecf20Sopenharmony_ci			    (skb = netdev_alloc_skb(dev, pkt_len + RX_OFFSET)) != NULL) {
23558c2ecf20Sopenharmony_ci				/* 16 byte align the IP header */
23568c2ecf20Sopenharmony_ci				skb_reserve(skb, RX_OFFSET);
23578c2ecf20Sopenharmony_ci				dma_sync_single_for_cpu(&np->pci_dev->dev,
23588c2ecf20Sopenharmony_ci							np->rx_dma[entry],
23598c2ecf20Sopenharmony_ci							buflen,
23608c2ecf20Sopenharmony_ci							DMA_FROM_DEVICE);
23618c2ecf20Sopenharmony_ci				skb_copy_to_linear_data(skb,
23628c2ecf20Sopenharmony_ci					np->rx_skbuff[entry]->data, pkt_len);
23638c2ecf20Sopenharmony_ci				skb_put(skb, pkt_len);
23648c2ecf20Sopenharmony_ci				dma_sync_single_for_device(&np->pci_dev->dev,
23658c2ecf20Sopenharmony_ci							   np->rx_dma[entry],
23668c2ecf20Sopenharmony_ci							   buflen,
23678c2ecf20Sopenharmony_ci							   DMA_FROM_DEVICE);
23688c2ecf20Sopenharmony_ci			} else {
23698c2ecf20Sopenharmony_ci				dma_unmap_single(&np->pci_dev->dev,
23708c2ecf20Sopenharmony_ci						 np->rx_dma[entry],
23718c2ecf20Sopenharmony_ci						 buflen + NATSEMI_PADDING,
23728c2ecf20Sopenharmony_ci						 DMA_FROM_DEVICE);
23738c2ecf20Sopenharmony_ci				skb_put(skb = np->rx_skbuff[entry], pkt_len);
23748c2ecf20Sopenharmony_ci				np->rx_skbuff[entry] = NULL;
23758c2ecf20Sopenharmony_ci			}
23768c2ecf20Sopenharmony_ci			skb->protocol = eth_type_trans(skb, dev);
23778c2ecf20Sopenharmony_ci			netif_receive_skb(skb);
23788c2ecf20Sopenharmony_ci			dev->stats.rx_packets++;
23798c2ecf20Sopenharmony_ci			dev->stats.rx_bytes += pkt_len;
23808c2ecf20Sopenharmony_ci		}
23818c2ecf20Sopenharmony_ci		entry = (++np->cur_rx) % RX_RING_SIZE;
23828c2ecf20Sopenharmony_ci		np->rx_head_desc = &np->rx_ring[entry];
23838c2ecf20Sopenharmony_ci		desc_status = le32_to_cpu(np->rx_head_desc->cmd_status);
23848c2ecf20Sopenharmony_ci	}
23858c2ecf20Sopenharmony_ci	refill_rx(dev);
23868c2ecf20Sopenharmony_ci
23878c2ecf20Sopenharmony_ci	/* Restart Rx engine if stopped. */
23888c2ecf20Sopenharmony_ci	if (np->oom)
23898c2ecf20Sopenharmony_ci		mod_timer(&np->timer, jiffies + 1);
23908c2ecf20Sopenharmony_ci	else
23918c2ecf20Sopenharmony_ci		writel(RxOn, ioaddr + ChipCmd);
23928c2ecf20Sopenharmony_ci}
23938c2ecf20Sopenharmony_ci
23948c2ecf20Sopenharmony_cistatic void netdev_error(struct net_device *dev, int intr_status)
23958c2ecf20Sopenharmony_ci{
23968c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
23978c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
23988c2ecf20Sopenharmony_ci
23998c2ecf20Sopenharmony_ci	spin_lock(&np->lock);
24008c2ecf20Sopenharmony_ci	if (intr_status & LinkChange) {
24018c2ecf20Sopenharmony_ci		u16 lpa = mdio_read(dev, MII_LPA);
24028c2ecf20Sopenharmony_ci		if (mdio_read(dev, MII_BMCR) & BMCR_ANENABLE &&
24038c2ecf20Sopenharmony_ci		    netif_msg_link(np)) {
24048c2ecf20Sopenharmony_ci			printk(KERN_INFO
24058c2ecf20Sopenharmony_ci				"%s: Autonegotiation advertising"
24068c2ecf20Sopenharmony_ci				" %#04x  partner %#04x.\n", dev->name,
24078c2ecf20Sopenharmony_ci				np->advertising, lpa);
24088c2ecf20Sopenharmony_ci		}
24098c2ecf20Sopenharmony_ci
24108c2ecf20Sopenharmony_ci		/* read MII int status to clear the flag */
24118c2ecf20Sopenharmony_ci		readw(ioaddr + MIntrStatus);
24128c2ecf20Sopenharmony_ci		check_link(dev);
24138c2ecf20Sopenharmony_ci	}
24148c2ecf20Sopenharmony_ci	if (intr_status & StatsMax) {
24158c2ecf20Sopenharmony_ci		__get_stats(dev);
24168c2ecf20Sopenharmony_ci	}
24178c2ecf20Sopenharmony_ci	if (intr_status & IntrTxUnderrun) {
24188c2ecf20Sopenharmony_ci		if ((np->tx_config & TxDrthMask) < TX_DRTH_VAL_LIMIT) {
24198c2ecf20Sopenharmony_ci			np->tx_config += TX_DRTH_VAL_INC;
24208c2ecf20Sopenharmony_ci			if (netif_msg_tx_err(np))
24218c2ecf20Sopenharmony_ci				printk(KERN_NOTICE
24228c2ecf20Sopenharmony_ci					"%s: increased tx threshold, txcfg %#08x.\n",
24238c2ecf20Sopenharmony_ci					dev->name, np->tx_config);
24248c2ecf20Sopenharmony_ci		} else {
24258c2ecf20Sopenharmony_ci			if (netif_msg_tx_err(np))
24268c2ecf20Sopenharmony_ci				printk(KERN_NOTICE
24278c2ecf20Sopenharmony_ci					"%s: tx underrun with maximum tx threshold, txcfg %#08x.\n",
24288c2ecf20Sopenharmony_ci					dev->name, np->tx_config);
24298c2ecf20Sopenharmony_ci		}
24308c2ecf20Sopenharmony_ci		writel(np->tx_config, ioaddr + TxConfig);
24318c2ecf20Sopenharmony_ci	}
24328c2ecf20Sopenharmony_ci	if (intr_status & WOLPkt && netif_msg_wol(np)) {
24338c2ecf20Sopenharmony_ci		int wol_status = readl(ioaddr + WOLCmd);
24348c2ecf20Sopenharmony_ci		printk(KERN_NOTICE "%s: Link wake-up event %#08x\n",
24358c2ecf20Sopenharmony_ci			dev->name, wol_status);
24368c2ecf20Sopenharmony_ci	}
24378c2ecf20Sopenharmony_ci	if (intr_status & RxStatusFIFOOver) {
24388c2ecf20Sopenharmony_ci		if (netif_msg_rx_err(np) && netif_msg_intr(np)) {
24398c2ecf20Sopenharmony_ci			printk(KERN_NOTICE "%s: Rx status FIFO overrun\n",
24408c2ecf20Sopenharmony_ci				dev->name);
24418c2ecf20Sopenharmony_ci		}
24428c2ecf20Sopenharmony_ci		dev->stats.rx_fifo_errors++;
24438c2ecf20Sopenharmony_ci		dev->stats.rx_errors++;
24448c2ecf20Sopenharmony_ci	}
24458c2ecf20Sopenharmony_ci	/* Hmmmmm, it's not clear how to recover from PCI faults. */
24468c2ecf20Sopenharmony_ci	if (intr_status & IntrPCIErr) {
24478c2ecf20Sopenharmony_ci		printk(KERN_NOTICE "%s: PCI error %#08x\n", dev->name,
24488c2ecf20Sopenharmony_ci			intr_status & IntrPCIErr);
24498c2ecf20Sopenharmony_ci		dev->stats.tx_fifo_errors++;
24508c2ecf20Sopenharmony_ci		dev->stats.tx_errors++;
24518c2ecf20Sopenharmony_ci		dev->stats.rx_fifo_errors++;
24528c2ecf20Sopenharmony_ci		dev->stats.rx_errors++;
24538c2ecf20Sopenharmony_ci	}
24548c2ecf20Sopenharmony_ci	spin_unlock(&np->lock);
24558c2ecf20Sopenharmony_ci}
24568c2ecf20Sopenharmony_ci
24578c2ecf20Sopenharmony_cistatic void __get_stats(struct net_device *dev)
24588c2ecf20Sopenharmony_ci{
24598c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
24608c2ecf20Sopenharmony_ci
24618c2ecf20Sopenharmony_ci	/* The chip only need report frame silently dropped. */
24628c2ecf20Sopenharmony_ci	dev->stats.rx_crc_errors += readl(ioaddr + RxCRCErrs);
24638c2ecf20Sopenharmony_ci	dev->stats.rx_missed_errors += readl(ioaddr + RxMissed);
24648c2ecf20Sopenharmony_ci}
24658c2ecf20Sopenharmony_ci
24668c2ecf20Sopenharmony_cistatic struct net_device_stats *get_stats(struct net_device *dev)
24678c2ecf20Sopenharmony_ci{
24688c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
24698c2ecf20Sopenharmony_ci
24708c2ecf20Sopenharmony_ci	/* The chip only need report frame silently dropped. */
24718c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
24728c2ecf20Sopenharmony_ci	if (netif_running(dev) && !np->hands_off)
24738c2ecf20Sopenharmony_ci		__get_stats(dev);
24748c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
24758c2ecf20Sopenharmony_ci
24768c2ecf20Sopenharmony_ci	return &dev->stats;
24778c2ecf20Sopenharmony_ci}
24788c2ecf20Sopenharmony_ci
24798c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
24808c2ecf20Sopenharmony_cistatic void natsemi_poll_controller(struct net_device *dev)
24818c2ecf20Sopenharmony_ci{
24828c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
24838c2ecf20Sopenharmony_ci	const int irq = np->pci_dev->irq;
24848c2ecf20Sopenharmony_ci
24858c2ecf20Sopenharmony_ci	disable_irq(irq);
24868c2ecf20Sopenharmony_ci	intr_handler(irq, dev);
24878c2ecf20Sopenharmony_ci	enable_irq(irq);
24888c2ecf20Sopenharmony_ci}
24898c2ecf20Sopenharmony_ci#endif
24908c2ecf20Sopenharmony_ci
24918c2ecf20Sopenharmony_ci#define HASH_TABLE	0x200
24928c2ecf20Sopenharmony_cistatic void __set_rx_mode(struct net_device *dev)
24938c2ecf20Sopenharmony_ci{
24948c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
24958c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
24968c2ecf20Sopenharmony_ci	u8 mc_filter[64]; /* Multicast hash filter */
24978c2ecf20Sopenharmony_ci	u32 rx_mode;
24988c2ecf20Sopenharmony_ci
24998c2ecf20Sopenharmony_ci	if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
25008c2ecf20Sopenharmony_ci		rx_mode = RxFilterEnable | AcceptBroadcast
25018c2ecf20Sopenharmony_ci			| AcceptAllMulticast | AcceptAllPhys | AcceptMyPhys;
25028c2ecf20Sopenharmony_ci	} else if ((netdev_mc_count(dev) > multicast_filter_limit) ||
25038c2ecf20Sopenharmony_ci		   (dev->flags & IFF_ALLMULTI)) {
25048c2ecf20Sopenharmony_ci		rx_mode = RxFilterEnable | AcceptBroadcast
25058c2ecf20Sopenharmony_ci			| AcceptAllMulticast | AcceptMyPhys;
25068c2ecf20Sopenharmony_ci	} else {
25078c2ecf20Sopenharmony_ci		struct netdev_hw_addr *ha;
25088c2ecf20Sopenharmony_ci		int i;
25098c2ecf20Sopenharmony_ci
25108c2ecf20Sopenharmony_ci		memset(mc_filter, 0, sizeof(mc_filter));
25118c2ecf20Sopenharmony_ci		netdev_for_each_mc_addr(ha, dev) {
25128c2ecf20Sopenharmony_ci			int b = (ether_crc(ETH_ALEN, ha->addr) >> 23) & 0x1ff;
25138c2ecf20Sopenharmony_ci			mc_filter[b/8] |= (1 << (b & 0x07));
25148c2ecf20Sopenharmony_ci		}
25158c2ecf20Sopenharmony_ci		rx_mode = RxFilterEnable | AcceptBroadcast
25168c2ecf20Sopenharmony_ci			| AcceptMulticast | AcceptMyPhys;
25178c2ecf20Sopenharmony_ci		for (i = 0; i < 64; i += 2) {
25188c2ecf20Sopenharmony_ci			writel(HASH_TABLE + i, ioaddr + RxFilterAddr);
25198c2ecf20Sopenharmony_ci			writel((mc_filter[i + 1] << 8) + mc_filter[i],
25208c2ecf20Sopenharmony_ci			       ioaddr + RxFilterData);
25218c2ecf20Sopenharmony_ci		}
25228c2ecf20Sopenharmony_ci	}
25238c2ecf20Sopenharmony_ci	writel(rx_mode, ioaddr + RxFilterAddr);
25248c2ecf20Sopenharmony_ci	np->cur_rx_mode = rx_mode;
25258c2ecf20Sopenharmony_ci}
25268c2ecf20Sopenharmony_ci
25278c2ecf20Sopenharmony_cistatic int natsemi_change_mtu(struct net_device *dev, int new_mtu)
25288c2ecf20Sopenharmony_ci{
25298c2ecf20Sopenharmony_ci	dev->mtu = new_mtu;
25308c2ecf20Sopenharmony_ci
25318c2ecf20Sopenharmony_ci	/* synchronized against open : rtnl_lock() held by caller */
25328c2ecf20Sopenharmony_ci	if (netif_running(dev)) {
25338c2ecf20Sopenharmony_ci		struct netdev_private *np = netdev_priv(dev);
25348c2ecf20Sopenharmony_ci		void __iomem * ioaddr = ns_ioaddr(dev);
25358c2ecf20Sopenharmony_ci		const int irq = np->pci_dev->irq;
25368c2ecf20Sopenharmony_ci
25378c2ecf20Sopenharmony_ci		disable_irq(irq);
25388c2ecf20Sopenharmony_ci		spin_lock(&np->lock);
25398c2ecf20Sopenharmony_ci		/* stop engines */
25408c2ecf20Sopenharmony_ci		natsemi_stop_rxtx(dev);
25418c2ecf20Sopenharmony_ci		/* drain rx queue */
25428c2ecf20Sopenharmony_ci		drain_rx(dev);
25438c2ecf20Sopenharmony_ci		/* change buffers */
25448c2ecf20Sopenharmony_ci		set_bufsize(dev);
25458c2ecf20Sopenharmony_ci		reinit_rx(dev);
25468c2ecf20Sopenharmony_ci		writel(np->ring_dma, ioaddr + RxRingPtr);
25478c2ecf20Sopenharmony_ci		/* restart engines */
25488c2ecf20Sopenharmony_ci		writel(RxOn | TxOn, ioaddr + ChipCmd);
25498c2ecf20Sopenharmony_ci		spin_unlock(&np->lock);
25508c2ecf20Sopenharmony_ci		enable_irq(irq);
25518c2ecf20Sopenharmony_ci	}
25528c2ecf20Sopenharmony_ci	return 0;
25538c2ecf20Sopenharmony_ci}
25548c2ecf20Sopenharmony_ci
25558c2ecf20Sopenharmony_cistatic void set_rx_mode(struct net_device *dev)
25568c2ecf20Sopenharmony_ci{
25578c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
25588c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
25598c2ecf20Sopenharmony_ci	if (!np->hands_off)
25608c2ecf20Sopenharmony_ci		__set_rx_mode(dev);
25618c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
25628c2ecf20Sopenharmony_ci}
25638c2ecf20Sopenharmony_ci
25648c2ecf20Sopenharmony_cistatic void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
25658c2ecf20Sopenharmony_ci{
25668c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
25678c2ecf20Sopenharmony_ci	strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
25688c2ecf20Sopenharmony_ci	strlcpy(info->version, DRV_VERSION, sizeof(info->version));
25698c2ecf20Sopenharmony_ci	strlcpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info));
25708c2ecf20Sopenharmony_ci}
25718c2ecf20Sopenharmony_ci
25728c2ecf20Sopenharmony_cistatic int get_regs_len(struct net_device *dev)
25738c2ecf20Sopenharmony_ci{
25748c2ecf20Sopenharmony_ci	return NATSEMI_REGS_SIZE;
25758c2ecf20Sopenharmony_ci}
25768c2ecf20Sopenharmony_ci
25778c2ecf20Sopenharmony_cistatic int get_eeprom_len(struct net_device *dev)
25788c2ecf20Sopenharmony_ci{
25798c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
25808c2ecf20Sopenharmony_ci	return np->eeprom_size;
25818c2ecf20Sopenharmony_ci}
25828c2ecf20Sopenharmony_ci
25838c2ecf20Sopenharmony_cistatic int get_link_ksettings(struct net_device *dev,
25848c2ecf20Sopenharmony_ci			      struct ethtool_link_ksettings *ecmd)
25858c2ecf20Sopenharmony_ci{
25868c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
25878c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
25888c2ecf20Sopenharmony_ci	netdev_get_ecmd(dev, ecmd);
25898c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
25908c2ecf20Sopenharmony_ci	return 0;
25918c2ecf20Sopenharmony_ci}
25928c2ecf20Sopenharmony_ci
25938c2ecf20Sopenharmony_cistatic int set_link_ksettings(struct net_device *dev,
25948c2ecf20Sopenharmony_ci			      const struct ethtool_link_ksettings *ecmd)
25958c2ecf20Sopenharmony_ci{
25968c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
25978c2ecf20Sopenharmony_ci	int res;
25988c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
25998c2ecf20Sopenharmony_ci	res = netdev_set_ecmd(dev, ecmd);
26008c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
26018c2ecf20Sopenharmony_ci	return res;
26028c2ecf20Sopenharmony_ci}
26038c2ecf20Sopenharmony_ci
26048c2ecf20Sopenharmony_cistatic void get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
26058c2ecf20Sopenharmony_ci{
26068c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
26078c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
26088c2ecf20Sopenharmony_ci	netdev_get_wol(dev, &wol->supported, &wol->wolopts);
26098c2ecf20Sopenharmony_ci	netdev_get_sopass(dev, wol->sopass);
26108c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
26118c2ecf20Sopenharmony_ci}
26128c2ecf20Sopenharmony_ci
26138c2ecf20Sopenharmony_cistatic int set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
26148c2ecf20Sopenharmony_ci{
26158c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
26168c2ecf20Sopenharmony_ci	int res;
26178c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
26188c2ecf20Sopenharmony_ci	netdev_set_wol(dev, wol->wolopts);
26198c2ecf20Sopenharmony_ci	res = netdev_set_sopass(dev, wol->sopass);
26208c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
26218c2ecf20Sopenharmony_ci	return res;
26228c2ecf20Sopenharmony_ci}
26238c2ecf20Sopenharmony_ci
26248c2ecf20Sopenharmony_cistatic void get_regs(struct net_device *dev, struct ethtool_regs *regs, void *buf)
26258c2ecf20Sopenharmony_ci{
26268c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
26278c2ecf20Sopenharmony_ci	regs->version = NATSEMI_REGS_VER;
26288c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
26298c2ecf20Sopenharmony_ci	netdev_get_regs(dev, buf);
26308c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
26318c2ecf20Sopenharmony_ci}
26328c2ecf20Sopenharmony_ci
26338c2ecf20Sopenharmony_cistatic u32 get_msglevel(struct net_device *dev)
26348c2ecf20Sopenharmony_ci{
26358c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
26368c2ecf20Sopenharmony_ci	return np->msg_enable;
26378c2ecf20Sopenharmony_ci}
26388c2ecf20Sopenharmony_ci
26398c2ecf20Sopenharmony_cistatic void set_msglevel(struct net_device *dev, u32 val)
26408c2ecf20Sopenharmony_ci{
26418c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
26428c2ecf20Sopenharmony_ci	np->msg_enable = val;
26438c2ecf20Sopenharmony_ci}
26448c2ecf20Sopenharmony_ci
26458c2ecf20Sopenharmony_cistatic int nway_reset(struct net_device *dev)
26468c2ecf20Sopenharmony_ci{
26478c2ecf20Sopenharmony_ci	int tmp;
26488c2ecf20Sopenharmony_ci	int r = -EINVAL;
26498c2ecf20Sopenharmony_ci	/* if autoneg is off, it's an error */
26508c2ecf20Sopenharmony_ci	tmp = mdio_read(dev, MII_BMCR);
26518c2ecf20Sopenharmony_ci	if (tmp & BMCR_ANENABLE) {
26528c2ecf20Sopenharmony_ci		tmp |= (BMCR_ANRESTART);
26538c2ecf20Sopenharmony_ci		mdio_write(dev, MII_BMCR, tmp);
26548c2ecf20Sopenharmony_ci		r = 0;
26558c2ecf20Sopenharmony_ci	}
26568c2ecf20Sopenharmony_ci	return r;
26578c2ecf20Sopenharmony_ci}
26588c2ecf20Sopenharmony_ci
26598c2ecf20Sopenharmony_cistatic u32 get_link(struct net_device *dev)
26608c2ecf20Sopenharmony_ci{
26618c2ecf20Sopenharmony_ci	/* LSTATUS is latched low until a read - so read twice */
26628c2ecf20Sopenharmony_ci	mdio_read(dev, MII_BMSR);
26638c2ecf20Sopenharmony_ci	return (mdio_read(dev, MII_BMSR)&BMSR_LSTATUS) ? 1:0;
26648c2ecf20Sopenharmony_ci}
26658c2ecf20Sopenharmony_ci
26668c2ecf20Sopenharmony_cistatic int get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data)
26678c2ecf20Sopenharmony_ci{
26688c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
26698c2ecf20Sopenharmony_ci	u8 *eebuf;
26708c2ecf20Sopenharmony_ci	int res;
26718c2ecf20Sopenharmony_ci
26728c2ecf20Sopenharmony_ci	eebuf = kmalloc(np->eeprom_size, GFP_KERNEL);
26738c2ecf20Sopenharmony_ci	if (!eebuf)
26748c2ecf20Sopenharmony_ci		return -ENOMEM;
26758c2ecf20Sopenharmony_ci
26768c2ecf20Sopenharmony_ci	eeprom->magic = PCI_VENDOR_ID_NS | (PCI_DEVICE_ID_NS_83815<<16);
26778c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
26788c2ecf20Sopenharmony_ci	res = netdev_get_eeprom(dev, eebuf);
26798c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
26808c2ecf20Sopenharmony_ci	if (!res)
26818c2ecf20Sopenharmony_ci		memcpy(data, eebuf+eeprom->offset, eeprom->len);
26828c2ecf20Sopenharmony_ci	kfree(eebuf);
26838c2ecf20Sopenharmony_ci	return res;
26848c2ecf20Sopenharmony_ci}
26858c2ecf20Sopenharmony_ci
26868c2ecf20Sopenharmony_cistatic const struct ethtool_ops ethtool_ops = {
26878c2ecf20Sopenharmony_ci	.get_drvinfo = get_drvinfo,
26888c2ecf20Sopenharmony_ci	.get_regs_len = get_regs_len,
26898c2ecf20Sopenharmony_ci	.get_eeprom_len = get_eeprom_len,
26908c2ecf20Sopenharmony_ci	.get_wol = get_wol,
26918c2ecf20Sopenharmony_ci	.set_wol = set_wol,
26928c2ecf20Sopenharmony_ci	.get_regs = get_regs,
26938c2ecf20Sopenharmony_ci	.get_msglevel = get_msglevel,
26948c2ecf20Sopenharmony_ci	.set_msglevel = set_msglevel,
26958c2ecf20Sopenharmony_ci	.nway_reset = nway_reset,
26968c2ecf20Sopenharmony_ci	.get_link = get_link,
26978c2ecf20Sopenharmony_ci	.get_eeprom = get_eeprom,
26988c2ecf20Sopenharmony_ci	.get_link_ksettings = get_link_ksettings,
26998c2ecf20Sopenharmony_ci	.set_link_ksettings = set_link_ksettings,
27008c2ecf20Sopenharmony_ci};
27018c2ecf20Sopenharmony_ci
27028c2ecf20Sopenharmony_cistatic int netdev_set_wol(struct net_device *dev, u32 newval)
27038c2ecf20Sopenharmony_ci{
27048c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
27058c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
27068c2ecf20Sopenharmony_ci	u32 data = readl(ioaddr + WOLCmd) & ~WakeOptsSummary;
27078c2ecf20Sopenharmony_ci
27088c2ecf20Sopenharmony_ci	/* translate to bitmasks this chip understands */
27098c2ecf20Sopenharmony_ci	if (newval & WAKE_PHY)
27108c2ecf20Sopenharmony_ci		data |= WakePhy;
27118c2ecf20Sopenharmony_ci	if (newval & WAKE_UCAST)
27128c2ecf20Sopenharmony_ci		data |= WakeUnicast;
27138c2ecf20Sopenharmony_ci	if (newval & WAKE_MCAST)
27148c2ecf20Sopenharmony_ci		data |= WakeMulticast;
27158c2ecf20Sopenharmony_ci	if (newval & WAKE_BCAST)
27168c2ecf20Sopenharmony_ci		data |= WakeBroadcast;
27178c2ecf20Sopenharmony_ci	if (newval & WAKE_ARP)
27188c2ecf20Sopenharmony_ci		data |= WakeArp;
27198c2ecf20Sopenharmony_ci	if (newval & WAKE_MAGIC)
27208c2ecf20Sopenharmony_ci		data |= WakeMagic;
27218c2ecf20Sopenharmony_ci	if (np->srr >= SRR_DP83815_D) {
27228c2ecf20Sopenharmony_ci		if (newval & WAKE_MAGICSECURE) {
27238c2ecf20Sopenharmony_ci			data |= WakeMagicSecure;
27248c2ecf20Sopenharmony_ci		}
27258c2ecf20Sopenharmony_ci	}
27268c2ecf20Sopenharmony_ci
27278c2ecf20Sopenharmony_ci	writel(data, ioaddr + WOLCmd);
27288c2ecf20Sopenharmony_ci
27298c2ecf20Sopenharmony_ci	return 0;
27308c2ecf20Sopenharmony_ci}
27318c2ecf20Sopenharmony_ci
27328c2ecf20Sopenharmony_cistatic int netdev_get_wol(struct net_device *dev, u32 *supported, u32 *cur)
27338c2ecf20Sopenharmony_ci{
27348c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
27358c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
27368c2ecf20Sopenharmony_ci	u32 regval = readl(ioaddr + WOLCmd);
27378c2ecf20Sopenharmony_ci
27388c2ecf20Sopenharmony_ci	*supported = (WAKE_PHY | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST
27398c2ecf20Sopenharmony_ci			| WAKE_ARP | WAKE_MAGIC);
27408c2ecf20Sopenharmony_ci
27418c2ecf20Sopenharmony_ci	if (np->srr >= SRR_DP83815_D) {
27428c2ecf20Sopenharmony_ci		/* SOPASS works on revD and higher */
27438c2ecf20Sopenharmony_ci		*supported |= WAKE_MAGICSECURE;
27448c2ecf20Sopenharmony_ci	}
27458c2ecf20Sopenharmony_ci	*cur = 0;
27468c2ecf20Sopenharmony_ci
27478c2ecf20Sopenharmony_ci	/* translate from chip bitmasks */
27488c2ecf20Sopenharmony_ci	if (regval & WakePhy)
27498c2ecf20Sopenharmony_ci		*cur |= WAKE_PHY;
27508c2ecf20Sopenharmony_ci	if (regval & WakeUnicast)
27518c2ecf20Sopenharmony_ci		*cur |= WAKE_UCAST;
27528c2ecf20Sopenharmony_ci	if (regval & WakeMulticast)
27538c2ecf20Sopenharmony_ci		*cur |= WAKE_MCAST;
27548c2ecf20Sopenharmony_ci	if (regval & WakeBroadcast)
27558c2ecf20Sopenharmony_ci		*cur |= WAKE_BCAST;
27568c2ecf20Sopenharmony_ci	if (regval & WakeArp)
27578c2ecf20Sopenharmony_ci		*cur |= WAKE_ARP;
27588c2ecf20Sopenharmony_ci	if (regval & WakeMagic)
27598c2ecf20Sopenharmony_ci		*cur |= WAKE_MAGIC;
27608c2ecf20Sopenharmony_ci	if (regval & WakeMagicSecure) {
27618c2ecf20Sopenharmony_ci		/* this can be on in revC, but it's broken */
27628c2ecf20Sopenharmony_ci		*cur |= WAKE_MAGICSECURE;
27638c2ecf20Sopenharmony_ci	}
27648c2ecf20Sopenharmony_ci
27658c2ecf20Sopenharmony_ci	return 0;
27668c2ecf20Sopenharmony_ci}
27678c2ecf20Sopenharmony_ci
27688c2ecf20Sopenharmony_cistatic int netdev_set_sopass(struct net_device *dev, u8 *newval)
27698c2ecf20Sopenharmony_ci{
27708c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
27718c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
27728c2ecf20Sopenharmony_ci	u16 *sval = (u16 *)newval;
27738c2ecf20Sopenharmony_ci	u32 addr;
27748c2ecf20Sopenharmony_ci
27758c2ecf20Sopenharmony_ci	if (np->srr < SRR_DP83815_D) {
27768c2ecf20Sopenharmony_ci		return 0;
27778c2ecf20Sopenharmony_ci	}
27788c2ecf20Sopenharmony_ci
27798c2ecf20Sopenharmony_ci	/* enable writing to these registers by disabling the RX filter */
27808c2ecf20Sopenharmony_ci	addr = readl(ioaddr + RxFilterAddr) & ~RFCRAddressMask;
27818c2ecf20Sopenharmony_ci	addr &= ~RxFilterEnable;
27828c2ecf20Sopenharmony_ci	writel(addr, ioaddr + RxFilterAddr);
27838c2ecf20Sopenharmony_ci
27848c2ecf20Sopenharmony_ci	/* write the three words to (undocumented) RFCR vals 0xa, 0xc, 0xe */
27858c2ecf20Sopenharmony_ci	writel(addr | 0xa, ioaddr + RxFilterAddr);
27868c2ecf20Sopenharmony_ci	writew(sval[0], ioaddr + RxFilterData);
27878c2ecf20Sopenharmony_ci
27888c2ecf20Sopenharmony_ci	writel(addr | 0xc, ioaddr + RxFilterAddr);
27898c2ecf20Sopenharmony_ci	writew(sval[1], ioaddr + RxFilterData);
27908c2ecf20Sopenharmony_ci
27918c2ecf20Sopenharmony_ci	writel(addr | 0xe, ioaddr + RxFilterAddr);
27928c2ecf20Sopenharmony_ci	writew(sval[2], ioaddr + RxFilterData);
27938c2ecf20Sopenharmony_ci
27948c2ecf20Sopenharmony_ci	/* re-enable the RX filter */
27958c2ecf20Sopenharmony_ci	writel(addr | RxFilterEnable, ioaddr + RxFilterAddr);
27968c2ecf20Sopenharmony_ci
27978c2ecf20Sopenharmony_ci	return 0;
27988c2ecf20Sopenharmony_ci}
27998c2ecf20Sopenharmony_ci
28008c2ecf20Sopenharmony_cistatic int netdev_get_sopass(struct net_device *dev, u8 *data)
28018c2ecf20Sopenharmony_ci{
28028c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
28038c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
28048c2ecf20Sopenharmony_ci	u16 *sval = (u16 *)data;
28058c2ecf20Sopenharmony_ci	u32 addr;
28068c2ecf20Sopenharmony_ci
28078c2ecf20Sopenharmony_ci	if (np->srr < SRR_DP83815_D) {
28088c2ecf20Sopenharmony_ci		sval[0] = sval[1] = sval[2] = 0;
28098c2ecf20Sopenharmony_ci		return 0;
28108c2ecf20Sopenharmony_ci	}
28118c2ecf20Sopenharmony_ci
28128c2ecf20Sopenharmony_ci	/* read the three words from (undocumented) RFCR vals 0xa, 0xc, 0xe */
28138c2ecf20Sopenharmony_ci	addr = readl(ioaddr + RxFilterAddr) & ~RFCRAddressMask;
28148c2ecf20Sopenharmony_ci
28158c2ecf20Sopenharmony_ci	writel(addr | 0xa, ioaddr + RxFilterAddr);
28168c2ecf20Sopenharmony_ci	sval[0] = readw(ioaddr + RxFilterData);
28178c2ecf20Sopenharmony_ci
28188c2ecf20Sopenharmony_ci	writel(addr | 0xc, ioaddr + RxFilterAddr);
28198c2ecf20Sopenharmony_ci	sval[1] = readw(ioaddr + RxFilterData);
28208c2ecf20Sopenharmony_ci
28218c2ecf20Sopenharmony_ci	writel(addr | 0xe, ioaddr + RxFilterAddr);
28228c2ecf20Sopenharmony_ci	sval[2] = readw(ioaddr + RxFilterData);
28238c2ecf20Sopenharmony_ci
28248c2ecf20Sopenharmony_ci	writel(addr, ioaddr + RxFilterAddr);
28258c2ecf20Sopenharmony_ci
28268c2ecf20Sopenharmony_ci	return 0;
28278c2ecf20Sopenharmony_ci}
28288c2ecf20Sopenharmony_ci
28298c2ecf20Sopenharmony_cistatic int netdev_get_ecmd(struct net_device *dev,
28308c2ecf20Sopenharmony_ci			   struct ethtool_link_ksettings *ecmd)
28318c2ecf20Sopenharmony_ci{
28328c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
28338c2ecf20Sopenharmony_ci	u32 supported, advertising;
28348c2ecf20Sopenharmony_ci	u32 tmp;
28358c2ecf20Sopenharmony_ci
28368c2ecf20Sopenharmony_ci	ecmd->base.port   = dev->if_port;
28378c2ecf20Sopenharmony_ci	ecmd->base.speed  = np->speed;
28388c2ecf20Sopenharmony_ci	ecmd->base.duplex = np->duplex;
28398c2ecf20Sopenharmony_ci	ecmd->base.autoneg = np->autoneg;
28408c2ecf20Sopenharmony_ci	advertising = 0;
28418c2ecf20Sopenharmony_ci
28428c2ecf20Sopenharmony_ci	if (np->advertising & ADVERTISE_10HALF)
28438c2ecf20Sopenharmony_ci		advertising |= ADVERTISED_10baseT_Half;
28448c2ecf20Sopenharmony_ci	if (np->advertising & ADVERTISE_10FULL)
28458c2ecf20Sopenharmony_ci		advertising |= ADVERTISED_10baseT_Full;
28468c2ecf20Sopenharmony_ci	if (np->advertising & ADVERTISE_100HALF)
28478c2ecf20Sopenharmony_ci		advertising |= ADVERTISED_100baseT_Half;
28488c2ecf20Sopenharmony_ci	if (np->advertising & ADVERTISE_100FULL)
28498c2ecf20Sopenharmony_ci		advertising |= ADVERTISED_100baseT_Full;
28508c2ecf20Sopenharmony_ci	supported   = (SUPPORTED_Autoneg |
28518c2ecf20Sopenharmony_ci		SUPPORTED_10baseT_Half  | SUPPORTED_10baseT_Full  |
28528c2ecf20Sopenharmony_ci		SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
28538c2ecf20Sopenharmony_ci		SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_FIBRE);
28548c2ecf20Sopenharmony_ci	ecmd->base.phy_address = np->phy_addr_external;
28558c2ecf20Sopenharmony_ci	/*
28568c2ecf20Sopenharmony_ci	 * We intentionally report the phy address of the external
28578c2ecf20Sopenharmony_ci	 * phy, even if the internal phy is used. This is necessary
28588c2ecf20Sopenharmony_ci	 * to work around a deficiency of the ethtool interface:
28598c2ecf20Sopenharmony_ci	 * It's only possible to query the settings of the active
28608c2ecf20Sopenharmony_ci	 * port. Therefore
28618c2ecf20Sopenharmony_ci	 * # ethtool -s ethX port mii
28628c2ecf20Sopenharmony_ci	 * actually sends an ioctl to switch to port mii with the
28638c2ecf20Sopenharmony_ci	 * settings that are used for the current active port.
28648c2ecf20Sopenharmony_ci	 * If we would report a different phy address in this
28658c2ecf20Sopenharmony_ci	 * command, then
28668c2ecf20Sopenharmony_ci	 * # ethtool -s ethX port tp;ethtool -s ethX port mii
28678c2ecf20Sopenharmony_ci	 * would unintentionally change the phy address.
28688c2ecf20Sopenharmony_ci	 *
28698c2ecf20Sopenharmony_ci	 * Fortunately the phy address doesn't matter with the
28708c2ecf20Sopenharmony_ci	 * internal phy...
28718c2ecf20Sopenharmony_ci	 */
28728c2ecf20Sopenharmony_ci
28738c2ecf20Sopenharmony_ci	/* set information based on active port type */
28748c2ecf20Sopenharmony_ci	switch (ecmd->base.port) {
28758c2ecf20Sopenharmony_ci	default:
28768c2ecf20Sopenharmony_ci	case PORT_TP:
28778c2ecf20Sopenharmony_ci		advertising |= ADVERTISED_TP;
28788c2ecf20Sopenharmony_ci		break;
28798c2ecf20Sopenharmony_ci	case PORT_MII:
28808c2ecf20Sopenharmony_ci		advertising |= ADVERTISED_MII;
28818c2ecf20Sopenharmony_ci		break;
28828c2ecf20Sopenharmony_ci	case PORT_FIBRE:
28838c2ecf20Sopenharmony_ci		advertising |= ADVERTISED_FIBRE;
28848c2ecf20Sopenharmony_ci		break;
28858c2ecf20Sopenharmony_ci	}
28868c2ecf20Sopenharmony_ci
28878c2ecf20Sopenharmony_ci	/* if autonegotiation is on, try to return the active speed/duplex */
28888c2ecf20Sopenharmony_ci	if (ecmd->base.autoneg == AUTONEG_ENABLE) {
28898c2ecf20Sopenharmony_ci		advertising |= ADVERTISED_Autoneg;
28908c2ecf20Sopenharmony_ci		tmp = mii_nway_result(
28918c2ecf20Sopenharmony_ci			np->advertising & mdio_read(dev, MII_LPA));
28928c2ecf20Sopenharmony_ci		if (tmp == LPA_100FULL || tmp == LPA_100HALF)
28938c2ecf20Sopenharmony_ci			ecmd->base.speed = SPEED_100;
28948c2ecf20Sopenharmony_ci		else
28958c2ecf20Sopenharmony_ci			ecmd->base.speed = SPEED_10;
28968c2ecf20Sopenharmony_ci		if (tmp == LPA_100FULL || tmp == LPA_10FULL)
28978c2ecf20Sopenharmony_ci			ecmd->base.duplex = DUPLEX_FULL;
28988c2ecf20Sopenharmony_ci		else
28998c2ecf20Sopenharmony_ci			ecmd->base.duplex = DUPLEX_HALF;
29008c2ecf20Sopenharmony_ci	}
29018c2ecf20Sopenharmony_ci
29028c2ecf20Sopenharmony_ci	/* ignore maxtxpkt, maxrxpkt for now */
29038c2ecf20Sopenharmony_ci
29048c2ecf20Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(ecmd->link_modes.supported,
29058c2ecf20Sopenharmony_ci						supported);
29068c2ecf20Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(ecmd->link_modes.advertising,
29078c2ecf20Sopenharmony_ci						advertising);
29088c2ecf20Sopenharmony_ci
29098c2ecf20Sopenharmony_ci	return 0;
29108c2ecf20Sopenharmony_ci}
29118c2ecf20Sopenharmony_ci
29128c2ecf20Sopenharmony_cistatic int netdev_set_ecmd(struct net_device *dev,
29138c2ecf20Sopenharmony_ci			   const struct ethtool_link_ksettings *ecmd)
29148c2ecf20Sopenharmony_ci{
29158c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
29168c2ecf20Sopenharmony_ci	u32 advertising;
29178c2ecf20Sopenharmony_ci
29188c2ecf20Sopenharmony_ci	ethtool_convert_link_mode_to_legacy_u32(&advertising,
29198c2ecf20Sopenharmony_ci						ecmd->link_modes.advertising);
29208c2ecf20Sopenharmony_ci
29218c2ecf20Sopenharmony_ci	if (ecmd->base.port != PORT_TP &&
29228c2ecf20Sopenharmony_ci	    ecmd->base.port != PORT_MII &&
29238c2ecf20Sopenharmony_ci	    ecmd->base.port != PORT_FIBRE)
29248c2ecf20Sopenharmony_ci		return -EINVAL;
29258c2ecf20Sopenharmony_ci	if (ecmd->base.autoneg == AUTONEG_ENABLE) {
29268c2ecf20Sopenharmony_ci		if ((advertising & (ADVERTISED_10baseT_Half |
29278c2ecf20Sopenharmony_ci					  ADVERTISED_10baseT_Full |
29288c2ecf20Sopenharmony_ci					  ADVERTISED_100baseT_Half |
29298c2ecf20Sopenharmony_ci					  ADVERTISED_100baseT_Full)) == 0) {
29308c2ecf20Sopenharmony_ci			return -EINVAL;
29318c2ecf20Sopenharmony_ci		}
29328c2ecf20Sopenharmony_ci	} else if (ecmd->base.autoneg == AUTONEG_DISABLE) {
29338c2ecf20Sopenharmony_ci		u32 speed = ecmd->base.speed;
29348c2ecf20Sopenharmony_ci		if (speed != SPEED_10 && speed != SPEED_100)
29358c2ecf20Sopenharmony_ci			return -EINVAL;
29368c2ecf20Sopenharmony_ci		if (ecmd->base.duplex != DUPLEX_HALF &&
29378c2ecf20Sopenharmony_ci		    ecmd->base.duplex != DUPLEX_FULL)
29388c2ecf20Sopenharmony_ci			return -EINVAL;
29398c2ecf20Sopenharmony_ci	} else {
29408c2ecf20Sopenharmony_ci		return -EINVAL;
29418c2ecf20Sopenharmony_ci	}
29428c2ecf20Sopenharmony_ci
29438c2ecf20Sopenharmony_ci	/*
29448c2ecf20Sopenharmony_ci	 * If we're ignoring the PHY then autoneg and the internal
29458c2ecf20Sopenharmony_ci	 * transceiver are really not going to work so don't let the
29468c2ecf20Sopenharmony_ci	 * user select them.
29478c2ecf20Sopenharmony_ci	 */
29488c2ecf20Sopenharmony_ci	if (np->ignore_phy && (ecmd->base.autoneg == AUTONEG_ENABLE ||
29498c2ecf20Sopenharmony_ci			       ecmd->base.port == PORT_TP))
29508c2ecf20Sopenharmony_ci		return -EINVAL;
29518c2ecf20Sopenharmony_ci
29528c2ecf20Sopenharmony_ci	/*
29538c2ecf20Sopenharmony_ci	 * maxtxpkt, maxrxpkt: ignored for now.
29548c2ecf20Sopenharmony_ci	 *
29558c2ecf20Sopenharmony_ci	 * transceiver:
29568c2ecf20Sopenharmony_ci	 * PORT_TP is always XCVR_INTERNAL, PORT_MII and PORT_FIBRE are always
29578c2ecf20Sopenharmony_ci	 * XCVR_EXTERNAL. The implementation thus ignores ecmd->transceiver and
29588c2ecf20Sopenharmony_ci	 * selects based on ecmd->port.
29598c2ecf20Sopenharmony_ci	 *
29608c2ecf20Sopenharmony_ci	 * Actually PORT_FIBRE is nearly identical to PORT_MII: it's for fibre
29618c2ecf20Sopenharmony_ci	 * phys that are connected to the mii bus. It's used to apply fibre
29628c2ecf20Sopenharmony_ci	 * specific updates.
29638c2ecf20Sopenharmony_ci	 */
29648c2ecf20Sopenharmony_ci
29658c2ecf20Sopenharmony_ci	/* WHEW! now lets bang some bits */
29668c2ecf20Sopenharmony_ci
29678c2ecf20Sopenharmony_ci	/* save the parms */
29688c2ecf20Sopenharmony_ci	dev->if_port          = ecmd->base.port;
29698c2ecf20Sopenharmony_ci	np->autoneg           = ecmd->base.autoneg;
29708c2ecf20Sopenharmony_ci	np->phy_addr_external = ecmd->base.phy_address & PhyAddrMask;
29718c2ecf20Sopenharmony_ci	if (np->autoneg == AUTONEG_ENABLE) {
29728c2ecf20Sopenharmony_ci		/* advertise only what has been requested */
29738c2ecf20Sopenharmony_ci		np->advertising &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
29748c2ecf20Sopenharmony_ci		if (advertising & ADVERTISED_10baseT_Half)
29758c2ecf20Sopenharmony_ci			np->advertising |= ADVERTISE_10HALF;
29768c2ecf20Sopenharmony_ci		if (advertising & ADVERTISED_10baseT_Full)
29778c2ecf20Sopenharmony_ci			np->advertising |= ADVERTISE_10FULL;
29788c2ecf20Sopenharmony_ci		if (advertising & ADVERTISED_100baseT_Half)
29798c2ecf20Sopenharmony_ci			np->advertising |= ADVERTISE_100HALF;
29808c2ecf20Sopenharmony_ci		if (advertising & ADVERTISED_100baseT_Full)
29818c2ecf20Sopenharmony_ci			np->advertising |= ADVERTISE_100FULL;
29828c2ecf20Sopenharmony_ci	} else {
29838c2ecf20Sopenharmony_ci		np->speed  = ecmd->base.speed;
29848c2ecf20Sopenharmony_ci		np->duplex = ecmd->base.duplex;
29858c2ecf20Sopenharmony_ci		/* user overriding the initial full duplex parm? */
29868c2ecf20Sopenharmony_ci		if (np->duplex == DUPLEX_HALF)
29878c2ecf20Sopenharmony_ci			np->full_duplex = 0;
29888c2ecf20Sopenharmony_ci	}
29898c2ecf20Sopenharmony_ci
29908c2ecf20Sopenharmony_ci	/* get the right phy enabled */
29918c2ecf20Sopenharmony_ci	if (ecmd->base.port == PORT_TP)
29928c2ecf20Sopenharmony_ci		switch_port_internal(dev);
29938c2ecf20Sopenharmony_ci	else
29948c2ecf20Sopenharmony_ci		switch_port_external(dev);
29958c2ecf20Sopenharmony_ci
29968c2ecf20Sopenharmony_ci	/* set parms and see how this affected our link status */
29978c2ecf20Sopenharmony_ci	init_phy_fixup(dev);
29988c2ecf20Sopenharmony_ci	check_link(dev);
29998c2ecf20Sopenharmony_ci	return 0;
30008c2ecf20Sopenharmony_ci}
30018c2ecf20Sopenharmony_ci
30028c2ecf20Sopenharmony_cistatic int netdev_get_regs(struct net_device *dev, u8 *buf)
30038c2ecf20Sopenharmony_ci{
30048c2ecf20Sopenharmony_ci	int i;
30058c2ecf20Sopenharmony_ci	int j;
30068c2ecf20Sopenharmony_ci	u32 rfcr;
30078c2ecf20Sopenharmony_ci	u32 *rbuf = (u32 *)buf;
30088c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
30098c2ecf20Sopenharmony_ci
30108c2ecf20Sopenharmony_ci	/* read non-mii page 0 of registers */
30118c2ecf20Sopenharmony_ci	for (i = 0; i < NATSEMI_PG0_NREGS/2; i++) {
30128c2ecf20Sopenharmony_ci		rbuf[i] = readl(ioaddr + i*4);
30138c2ecf20Sopenharmony_ci	}
30148c2ecf20Sopenharmony_ci
30158c2ecf20Sopenharmony_ci	/* read current mii registers */
30168c2ecf20Sopenharmony_ci	for (i = NATSEMI_PG0_NREGS/2; i < NATSEMI_PG0_NREGS; i++)
30178c2ecf20Sopenharmony_ci		rbuf[i] = mdio_read(dev, i & 0x1f);
30188c2ecf20Sopenharmony_ci
30198c2ecf20Sopenharmony_ci	/* read only the 'magic' registers from page 1 */
30208c2ecf20Sopenharmony_ci	writew(1, ioaddr + PGSEL);
30218c2ecf20Sopenharmony_ci	rbuf[i++] = readw(ioaddr + PMDCSR);
30228c2ecf20Sopenharmony_ci	rbuf[i++] = readw(ioaddr + TSTDAT);
30238c2ecf20Sopenharmony_ci	rbuf[i++] = readw(ioaddr + DSPCFG);
30248c2ecf20Sopenharmony_ci	rbuf[i++] = readw(ioaddr + SDCFG);
30258c2ecf20Sopenharmony_ci	writew(0, ioaddr + PGSEL);
30268c2ecf20Sopenharmony_ci
30278c2ecf20Sopenharmony_ci	/* read RFCR indexed registers */
30288c2ecf20Sopenharmony_ci	rfcr = readl(ioaddr + RxFilterAddr);
30298c2ecf20Sopenharmony_ci	for (j = 0; j < NATSEMI_RFDR_NREGS; j++) {
30308c2ecf20Sopenharmony_ci		writel(j*2, ioaddr + RxFilterAddr);
30318c2ecf20Sopenharmony_ci		rbuf[i++] = readw(ioaddr + RxFilterData);
30328c2ecf20Sopenharmony_ci	}
30338c2ecf20Sopenharmony_ci	writel(rfcr, ioaddr + RxFilterAddr);
30348c2ecf20Sopenharmony_ci
30358c2ecf20Sopenharmony_ci	/* the interrupt status is clear-on-read - see if we missed any */
30368c2ecf20Sopenharmony_ci	if (rbuf[4] & rbuf[5]) {
30378c2ecf20Sopenharmony_ci		printk(KERN_WARNING
30388c2ecf20Sopenharmony_ci			"%s: shoot, we dropped an interrupt (%#08x)\n",
30398c2ecf20Sopenharmony_ci			dev->name, rbuf[4] & rbuf[5]);
30408c2ecf20Sopenharmony_ci	}
30418c2ecf20Sopenharmony_ci
30428c2ecf20Sopenharmony_ci	return 0;
30438c2ecf20Sopenharmony_ci}
30448c2ecf20Sopenharmony_ci
30458c2ecf20Sopenharmony_ci#define SWAP_BITS(x)	( (((x) & 0x0001) << 15) | (((x) & 0x0002) << 13) \
30468c2ecf20Sopenharmony_ci			| (((x) & 0x0004) << 11) | (((x) & 0x0008) << 9)  \
30478c2ecf20Sopenharmony_ci			| (((x) & 0x0010) << 7)  | (((x) & 0x0020) << 5)  \
30488c2ecf20Sopenharmony_ci			| (((x) & 0x0040) << 3)  | (((x) & 0x0080) << 1)  \
30498c2ecf20Sopenharmony_ci			| (((x) & 0x0100) >> 1)  | (((x) & 0x0200) >> 3)  \
30508c2ecf20Sopenharmony_ci			| (((x) & 0x0400) >> 5)  | (((x) & 0x0800) >> 7)  \
30518c2ecf20Sopenharmony_ci			| (((x) & 0x1000) >> 9)  | (((x) & 0x2000) >> 11) \
30528c2ecf20Sopenharmony_ci			| (((x) & 0x4000) >> 13) | (((x) & 0x8000) >> 15) )
30538c2ecf20Sopenharmony_ci
30548c2ecf20Sopenharmony_cistatic int netdev_get_eeprom(struct net_device *dev, u8 *buf)
30558c2ecf20Sopenharmony_ci{
30568c2ecf20Sopenharmony_ci	int i;
30578c2ecf20Sopenharmony_ci	u16 *ebuf = (u16 *)buf;
30588c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
30598c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
30608c2ecf20Sopenharmony_ci
30618c2ecf20Sopenharmony_ci	/* eeprom_read reads 16 bits, and indexes by 16 bits */
30628c2ecf20Sopenharmony_ci	for (i = 0; i < np->eeprom_size/2; i++) {
30638c2ecf20Sopenharmony_ci		ebuf[i] = eeprom_read(ioaddr, i);
30648c2ecf20Sopenharmony_ci		/* The EEPROM itself stores data bit-swapped, but eeprom_read
30658c2ecf20Sopenharmony_ci		 * reads it back "sanely". So we swap it back here in order to
30668c2ecf20Sopenharmony_ci		 * present it to userland as it is stored. */
30678c2ecf20Sopenharmony_ci		ebuf[i] = SWAP_BITS(ebuf[i]);
30688c2ecf20Sopenharmony_ci	}
30698c2ecf20Sopenharmony_ci	return 0;
30708c2ecf20Sopenharmony_ci}
30718c2ecf20Sopenharmony_ci
30728c2ecf20Sopenharmony_cistatic int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
30738c2ecf20Sopenharmony_ci{
30748c2ecf20Sopenharmony_ci	struct mii_ioctl_data *data = if_mii(rq);
30758c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
30768c2ecf20Sopenharmony_ci
30778c2ecf20Sopenharmony_ci	switch(cmd) {
30788c2ecf20Sopenharmony_ci	case SIOCGMIIPHY:		/* Get address of MII PHY in use. */
30798c2ecf20Sopenharmony_ci		data->phy_id = np->phy_addr_external;
30808c2ecf20Sopenharmony_ci		fallthrough;
30818c2ecf20Sopenharmony_ci
30828c2ecf20Sopenharmony_ci	case SIOCGMIIREG:		/* Read MII PHY register. */
30838c2ecf20Sopenharmony_ci		/* The phy_id is not enough to uniquely identify
30848c2ecf20Sopenharmony_ci		 * the intended target. Therefore the command is sent to
30858c2ecf20Sopenharmony_ci		 * the given mii on the current port.
30868c2ecf20Sopenharmony_ci		 */
30878c2ecf20Sopenharmony_ci		if (dev->if_port == PORT_TP) {
30888c2ecf20Sopenharmony_ci			if ((data->phy_id & 0x1f) == np->phy_addr_external)
30898c2ecf20Sopenharmony_ci				data->val_out = mdio_read(dev,
30908c2ecf20Sopenharmony_ci							data->reg_num & 0x1f);
30918c2ecf20Sopenharmony_ci			else
30928c2ecf20Sopenharmony_ci				data->val_out = 0;
30938c2ecf20Sopenharmony_ci		} else {
30948c2ecf20Sopenharmony_ci			move_int_phy(dev, data->phy_id & 0x1f);
30958c2ecf20Sopenharmony_ci			data->val_out = miiport_read(dev, data->phy_id & 0x1f,
30968c2ecf20Sopenharmony_ci							data->reg_num & 0x1f);
30978c2ecf20Sopenharmony_ci		}
30988c2ecf20Sopenharmony_ci		return 0;
30998c2ecf20Sopenharmony_ci
31008c2ecf20Sopenharmony_ci	case SIOCSMIIREG:		/* Write MII PHY register. */
31018c2ecf20Sopenharmony_ci		if (dev->if_port == PORT_TP) {
31028c2ecf20Sopenharmony_ci			if ((data->phy_id & 0x1f) == np->phy_addr_external) {
31038c2ecf20Sopenharmony_ci 				if ((data->reg_num & 0x1f) == MII_ADVERTISE)
31048c2ecf20Sopenharmony_ci					np->advertising = data->val_in;
31058c2ecf20Sopenharmony_ci				mdio_write(dev, data->reg_num & 0x1f,
31068c2ecf20Sopenharmony_ci							data->val_in);
31078c2ecf20Sopenharmony_ci			}
31088c2ecf20Sopenharmony_ci		} else {
31098c2ecf20Sopenharmony_ci			if ((data->phy_id & 0x1f) == np->phy_addr_external) {
31108c2ecf20Sopenharmony_ci 				if ((data->reg_num & 0x1f) == MII_ADVERTISE)
31118c2ecf20Sopenharmony_ci					np->advertising = data->val_in;
31128c2ecf20Sopenharmony_ci			}
31138c2ecf20Sopenharmony_ci			move_int_phy(dev, data->phy_id & 0x1f);
31148c2ecf20Sopenharmony_ci			miiport_write(dev, data->phy_id & 0x1f,
31158c2ecf20Sopenharmony_ci						data->reg_num & 0x1f,
31168c2ecf20Sopenharmony_ci						data->val_in);
31178c2ecf20Sopenharmony_ci		}
31188c2ecf20Sopenharmony_ci		return 0;
31198c2ecf20Sopenharmony_ci	default:
31208c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
31218c2ecf20Sopenharmony_ci	}
31228c2ecf20Sopenharmony_ci}
31238c2ecf20Sopenharmony_ci
31248c2ecf20Sopenharmony_cistatic void enable_wol_mode(struct net_device *dev, int enable_intr)
31258c2ecf20Sopenharmony_ci{
31268c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
31278c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
31288c2ecf20Sopenharmony_ci
31298c2ecf20Sopenharmony_ci	if (netif_msg_wol(np))
31308c2ecf20Sopenharmony_ci		printk(KERN_INFO "%s: remaining active for wake-on-lan\n",
31318c2ecf20Sopenharmony_ci			dev->name);
31328c2ecf20Sopenharmony_ci
31338c2ecf20Sopenharmony_ci	/* For WOL we must restart the rx process in silent mode.
31348c2ecf20Sopenharmony_ci	 * Write NULL to the RxRingPtr. Only possible if
31358c2ecf20Sopenharmony_ci	 * rx process is stopped
31368c2ecf20Sopenharmony_ci	 */
31378c2ecf20Sopenharmony_ci	writel(0, ioaddr + RxRingPtr);
31388c2ecf20Sopenharmony_ci
31398c2ecf20Sopenharmony_ci	/* read WoL status to clear */
31408c2ecf20Sopenharmony_ci	readl(ioaddr + WOLCmd);
31418c2ecf20Sopenharmony_ci
31428c2ecf20Sopenharmony_ci	/* PME on, clear status */
31438c2ecf20Sopenharmony_ci	writel(np->SavedClkRun | PMEEnable | PMEStatus, ioaddr + ClkRun);
31448c2ecf20Sopenharmony_ci
31458c2ecf20Sopenharmony_ci	/* and restart the rx process */
31468c2ecf20Sopenharmony_ci	writel(RxOn, ioaddr + ChipCmd);
31478c2ecf20Sopenharmony_ci
31488c2ecf20Sopenharmony_ci	if (enable_intr) {
31498c2ecf20Sopenharmony_ci		/* enable the WOL interrupt.
31508c2ecf20Sopenharmony_ci		 * Could be used to send a netlink message.
31518c2ecf20Sopenharmony_ci		 */
31528c2ecf20Sopenharmony_ci		writel(WOLPkt | LinkChange, ioaddr + IntrMask);
31538c2ecf20Sopenharmony_ci		natsemi_irq_enable(dev);
31548c2ecf20Sopenharmony_ci	}
31558c2ecf20Sopenharmony_ci}
31568c2ecf20Sopenharmony_ci
31578c2ecf20Sopenharmony_cistatic int netdev_close(struct net_device *dev)
31588c2ecf20Sopenharmony_ci{
31598c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
31608c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
31618c2ecf20Sopenharmony_ci	const int irq = np->pci_dev->irq;
31628c2ecf20Sopenharmony_ci
31638c2ecf20Sopenharmony_ci	if (netif_msg_ifdown(np))
31648c2ecf20Sopenharmony_ci		printk(KERN_DEBUG
31658c2ecf20Sopenharmony_ci			"%s: Shutting down ethercard, status was %#04x.\n",
31668c2ecf20Sopenharmony_ci			dev->name, (int)readl(ioaddr + ChipCmd));
31678c2ecf20Sopenharmony_ci	if (netif_msg_pktdata(np))
31688c2ecf20Sopenharmony_ci		printk(KERN_DEBUG
31698c2ecf20Sopenharmony_ci			"%s: Queue pointers were Tx %d / %d,  Rx %d / %d.\n",
31708c2ecf20Sopenharmony_ci			dev->name, np->cur_tx, np->dirty_tx,
31718c2ecf20Sopenharmony_ci			np->cur_rx, np->dirty_rx);
31728c2ecf20Sopenharmony_ci
31738c2ecf20Sopenharmony_ci	napi_disable(&np->napi);
31748c2ecf20Sopenharmony_ci
31758c2ecf20Sopenharmony_ci	/*
31768c2ecf20Sopenharmony_ci	 * FIXME: what if someone tries to close a device
31778c2ecf20Sopenharmony_ci	 * that is suspended?
31788c2ecf20Sopenharmony_ci	 * Should we reenable the nic to switch to
31798c2ecf20Sopenharmony_ci	 * the final WOL settings?
31808c2ecf20Sopenharmony_ci	 */
31818c2ecf20Sopenharmony_ci
31828c2ecf20Sopenharmony_ci	del_timer_sync(&np->timer);
31838c2ecf20Sopenharmony_ci	disable_irq(irq);
31848c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
31858c2ecf20Sopenharmony_ci	natsemi_irq_disable(dev);
31868c2ecf20Sopenharmony_ci	np->hands_off = 1;
31878c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
31888c2ecf20Sopenharmony_ci	enable_irq(irq);
31898c2ecf20Sopenharmony_ci
31908c2ecf20Sopenharmony_ci	free_irq(irq, dev);
31918c2ecf20Sopenharmony_ci
31928c2ecf20Sopenharmony_ci	/* Interrupt disabled, interrupt handler released,
31938c2ecf20Sopenharmony_ci	 * queue stopped, timer deleted, rtnl_lock held
31948c2ecf20Sopenharmony_ci	 * All async codepaths that access the driver are disabled.
31958c2ecf20Sopenharmony_ci	 */
31968c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
31978c2ecf20Sopenharmony_ci	np->hands_off = 0;
31988c2ecf20Sopenharmony_ci	readl(ioaddr + IntrMask);
31998c2ecf20Sopenharmony_ci	readw(ioaddr + MIntrStatus);
32008c2ecf20Sopenharmony_ci
32018c2ecf20Sopenharmony_ci	/* Freeze Stats */
32028c2ecf20Sopenharmony_ci	writel(StatsFreeze, ioaddr + StatsCtrl);
32038c2ecf20Sopenharmony_ci
32048c2ecf20Sopenharmony_ci	/* Stop the chip's Tx and Rx processes. */
32058c2ecf20Sopenharmony_ci	natsemi_stop_rxtx(dev);
32068c2ecf20Sopenharmony_ci
32078c2ecf20Sopenharmony_ci	__get_stats(dev);
32088c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
32098c2ecf20Sopenharmony_ci
32108c2ecf20Sopenharmony_ci	/* clear the carrier last - an interrupt could reenable it otherwise */
32118c2ecf20Sopenharmony_ci	netif_carrier_off(dev);
32128c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
32138c2ecf20Sopenharmony_ci
32148c2ecf20Sopenharmony_ci	dump_ring(dev);
32158c2ecf20Sopenharmony_ci	drain_ring(dev);
32168c2ecf20Sopenharmony_ci	free_ring(dev);
32178c2ecf20Sopenharmony_ci
32188c2ecf20Sopenharmony_ci	{
32198c2ecf20Sopenharmony_ci		u32 wol = readl(ioaddr + WOLCmd) & WakeOptsSummary;
32208c2ecf20Sopenharmony_ci		if (wol) {
32218c2ecf20Sopenharmony_ci			/* restart the NIC in WOL mode.
32228c2ecf20Sopenharmony_ci			 * The nic must be stopped for this.
32238c2ecf20Sopenharmony_ci			 */
32248c2ecf20Sopenharmony_ci			enable_wol_mode(dev, 0);
32258c2ecf20Sopenharmony_ci		} else {
32268c2ecf20Sopenharmony_ci			/* Restore PME enable bit unmolested */
32278c2ecf20Sopenharmony_ci			writel(np->SavedClkRun, ioaddr + ClkRun);
32288c2ecf20Sopenharmony_ci		}
32298c2ecf20Sopenharmony_ci	}
32308c2ecf20Sopenharmony_ci	return 0;
32318c2ecf20Sopenharmony_ci}
32328c2ecf20Sopenharmony_ci
32338c2ecf20Sopenharmony_ci
32348c2ecf20Sopenharmony_cistatic void natsemi_remove1(struct pci_dev *pdev)
32358c2ecf20Sopenharmony_ci{
32368c2ecf20Sopenharmony_ci	struct net_device *dev = pci_get_drvdata(pdev);
32378c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
32388c2ecf20Sopenharmony_ci
32398c2ecf20Sopenharmony_ci	NATSEMI_REMOVE_FILE(pdev, dspcfg_workaround);
32408c2ecf20Sopenharmony_ci	unregister_netdev (dev);
32418c2ecf20Sopenharmony_ci	iounmap(ioaddr);
32428c2ecf20Sopenharmony_ci	free_netdev (dev);
32438c2ecf20Sopenharmony_ci}
32448c2ecf20Sopenharmony_ci
32458c2ecf20Sopenharmony_ci/*
32468c2ecf20Sopenharmony_ci * The ns83815 chip doesn't have explicit RxStop bits.
32478c2ecf20Sopenharmony_ci * Kicking the Rx or Tx process for a new packet reenables the Rx process
32488c2ecf20Sopenharmony_ci * of the nic, thus this function must be very careful:
32498c2ecf20Sopenharmony_ci *
32508c2ecf20Sopenharmony_ci * suspend/resume synchronization:
32518c2ecf20Sopenharmony_ci * entry points:
32528c2ecf20Sopenharmony_ci *   netdev_open, netdev_close, netdev_ioctl, set_rx_mode, intr_handler,
32538c2ecf20Sopenharmony_ci *   start_tx, ns_tx_timeout
32548c2ecf20Sopenharmony_ci *
32558c2ecf20Sopenharmony_ci * No function accesses the hardware without checking np->hands_off.
32568c2ecf20Sopenharmony_ci *	the check occurs under spin_lock_irq(&np->lock);
32578c2ecf20Sopenharmony_ci * exceptions:
32588c2ecf20Sopenharmony_ci *	* netdev_ioctl: noncritical access.
32598c2ecf20Sopenharmony_ci *	* netdev_open: cannot happen due to the device_detach
32608c2ecf20Sopenharmony_ci *	* netdev_close: doesn't hurt.
32618c2ecf20Sopenharmony_ci *	* netdev_timer: timer stopped by natsemi_suspend.
32628c2ecf20Sopenharmony_ci *	* intr_handler: doesn't acquire the spinlock. suspend calls
32638c2ecf20Sopenharmony_ci *		disable_irq() to enforce synchronization.
32648c2ecf20Sopenharmony_ci *      * natsemi_poll: checks before reenabling interrupts.  suspend
32658c2ecf20Sopenharmony_ci *              sets hands_off, disables interrupts and then waits with
32668c2ecf20Sopenharmony_ci *              napi_disable().
32678c2ecf20Sopenharmony_ci *
32688c2ecf20Sopenharmony_ci * Interrupts must be disabled, otherwise hands_off can cause irq storms.
32698c2ecf20Sopenharmony_ci */
32708c2ecf20Sopenharmony_ci
32718c2ecf20Sopenharmony_cistatic int __maybe_unused natsemi_suspend(struct device *dev_d)
32728c2ecf20Sopenharmony_ci{
32738c2ecf20Sopenharmony_ci	struct net_device *dev = dev_get_drvdata(dev_d);
32748c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
32758c2ecf20Sopenharmony_ci	void __iomem * ioaddr = ns_ioaddr(dev);
32768c2ecf20Sopenharmony_ci
32778c2ecf20Sopenharmony_ci	rtnl_lock();
32788c2ecf20Sopenharmony_ci	if (netif_running (dev)) {
32798c2ecf20Sopenharmony_ci		const int irq = np->pci_dev->irq;
32808c2ecf20Sopenharmony_ci
32818c2ecf20Sopenharmony_ci		del_timer_sync(&np->timer);
32828c2ecf20Sopenharmony_ci
32838c2ecf20Sopenharmony_ci		disable_irq(irq);
32848c2ecf20Sopenharmony_ci		spin_lock_irq(&np->lock);
32858c2ecf20Sopenharmony_ci
32868c2ecf20Sopenharmony_ci		natsemi_irq_disable(dev);
32878c2ecf20Sopenharmony_ci		np->hands_off = 1;
32888c2ecf20Sopenharmony_ci		natsemi_stop_rxtx(dev);
32898c2ecf20Sopenharmony_ci		netif_stop_queue(dev);
32908c2ecf20Sopenharmony_ci
32918c2ecf20Sopenharmony_ci		spin_unlock_irq(&np->lock);
32928c2ecf20Sopenharmony_ci		enable_irq(irq);
32938c2ecf20Sopenharmony_ci
32948c2ecf20Sopenharmony_ci		napi_disable(&np->napi);
32958c2ecf20Sopenharmony_ci
32968c2ecf20Sopenharmony_ci		/* Update the error counts. */
32978c2ecf20Sopenharmony_ci		__get_stats(dev);
32988c2ecf20Sopenharmony_ci
32998c2ecf20Sopenharmony_ci		/* pci_power_off(pdev, -1); */
33008c2ecf20Sopenharmony_ci		drain_ring(dev);
33018c2ecf20Sopenharmony_ci		{
33028c2ecf20Sopenharmony_ci			u32 wol = readl(ioaddr + WOLCmd) & WakeOptsSummary;
33038c2ecf20Sopenharmony_ci			/* Restore PME enable bit */
33048c2ecf20Sopenharmony_ci			if (wol) {
33058c2ecf20Sopenharmony_ci				/* restart the NIC in WOL mode.
33068c2ecf20Sopenharmony_ci				 * The nic must be stopped for this.
33078c2ecf20Sopenharmony_ci				 * FIXME: use the WOL interrupt
33088c2ecf20Sopenharmony_ci				 */
33098c2ecf20Sopenharmony_ci				enable_wol_mode(dev, 0);
33108c2ecf20Sopenharmony_ci			} else {
33118c2ecf20Sopenharmony_ci				/* Restore PME enable bit unmolested */
33128c2ecf20Sopenharmony_ci				writel(np->SavedClkRun, ioaddr + ClkRun);
33138c2ecf20Sopenharmony_ci			}
33148c2ecf20Sopenharmony_ci		}
33158c2ecf20Sopenharmony_ci	}
33168c2ecf20Sopenharmony_ci	netif_device_detach(dev);
33178c2ecf20Sopenharmony_ci	rtnl_unlock();
33188c2ecf20Sopenharmony_ci	return 0;
33198c2ecf20Sopenharmony_ci}
33208c2ecf20Sopenharmony_ci
33218c2ecf20Sopenharmony_ci
33228c2ecf20Sopenharmony_cistatic int __maybe_unused natsemi_resume(struct device *dev_d)
33238c2ecf20Sopenharmony_ci{
33248c2ecf20Sopenharmony_ci	struct net_device *dev = dev_get_drvdata(dev_d);
33258c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
33268c2ecf20Sopenharmony_ci
33278c2ecf20Sopenharmony_ci	rtnl_lock();
33288c2ecf20Sopenharmony_ci	if (netif_device_present(dev))
33298c2ecf20Sopenharmony_ci		goto out;
33308c2ecf20Sopenharmony_ci	if (netif_running(dev)) {
33318c2ecf20Sopenharmony_ci		const int irq = np->pci_dev->irq;
33328c2ecf20Sopenharmony_ci
33338c2ecf20Sopenharmony_ci		BUG_ON(!np->hands_off);
33348c2ecf20Sopenharmony_ci	/*	pci_power_on(pdev); */
33358c2ecf20Sopenharmony_ci
33368c2ecf20Sopenharmony_ci		napi_enable(&np->napi);
33378c2ecf20Sopenharmony_ci
33388c2ecf20Sopenharmony_ci		natsemi_reset(dev);
33398c2ecf20Sopenharmony_ci		init_ring(dev);
33408c2ecf20Sopenharmony_ci		disable_irq(irq);
33418c2ecf20Sopenharmony_ci		spin_lock_irq(&np->lock);
33428c2ecf20Sopenharmony_ci		np->hands_off = 0;
33438c2ecf20Sopenharmony_ci		init_registers(dev);
33448c2ecf20Sopenharmony_ci		netif_device_attach(dev);
33458c2ecf20Sopenharmony_ci		spin_unlock_irq(&np->lock);
33468c2ecf20Sopenharmony_ci		enable_irq(irq);
33478c2ecf20Sopenharmony_ci
33488c2ecf20Sopenharmony_ci		mod_timer(&np->timer, round_jiffies(jiffies + 1*HZ));
33498c2ecf20Sopenharmony_ci	}
33508c2ecf20Sopenharmony_ci	netif_device_attach(dev);
33518c2ecf20Sopenharmony_ciout:
33528c2ecf20Sopenharmony_ci	rtnl_unlock();
33538c2ecf20Sopenharmony_ci	return 0;
33548c2ecf20Sopenharmony_ci}
33558c2ecf20Sopenharmony_ci
33568c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(natsemi_pm_ops, natsemi_suspend, natsemi_resume);
33578c2ecf20Sopenharmony_ci
33588c2ecf20Sopenharmony_cistatic struct pci_driver natsemi_driver = {
33598c2ecf20Sopenharmony_ci	.name		= DRV_NAME,
33608c2ecf20Sopenharmony_ci	.id_table	= natsemi_pci_tbl,
33618c2ecf20Sopenharmony_ci	.probe		= natsemi_probe1,
33628c2ecf20Sopenharmony_ci	.remove		= natsemi_remove1,
33638c2ecf20Sopenharmony_ci	.driver.pm	= &natsemi_pm_ops,
33648c2ecf20Sopenharmony_ci};
33658c2ecf20Sopenharmony_ci
33668c2ecf20Sopenharmony_cistatic int __init natsemi_init_mod (void)
33678c2ecf20Sopenharmony_ci{
33688c2ecf20Sopenharmony_ci/* when a module, this is printed whether or not devices are found in probe */
33698c2ecf20Sopenharmony_ci#ifdef MODULE
33708c2ecf20Sopenharmony_ci	printk(version);
33718c2ecf20Sopenharmony_ci#endif
33728c2ecf20Sopenharmony_ci
33738c2ecf20Sopenharmony_ci	return pci_register_driver(&natsemi_driver);
33748c2ecf20Sopenharmony_ci}
33758c2ecf20Sopenharmony_ci
33768c2ecf20Sopenharmony_cistatic void __exit natsemi_exit_mod (void)
33778c2ecf20Sopenharmony_ci{
33788c2ecf20Sopenharmony_ci	pci_unregister_driver (&natsemi_driver);
33798c2ecf20Sopenharmony_ci}
33808c2ecf20Sopenharmony_ci
33818c2ecf20Sopenharmony_cimodule_init(natsemi_init_mod);
33828c2ecf20Sopenharmony_cimodule_exit(natsemi_exit_mod);
33838c2ecf20Sopenharmony_ci
3384