18c2ecf20Sopenharmony_ci/* winbond-840.c: A Linux PCI network adapter device driver. */
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci	Written 1998-2001 by Donald Becker.
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci	This software may be used and distributed according to the terms of
68c2ecf20Sopenharmony_ci	the GNU General Public License (GPL), incorporated herein by reference.
78c2ecf20Sopenharmony_ci	Drivers based on or derived from this code fall under the GPL and must
88c2ecf20Sopenharmony_ci	retain the authorship, copyright and license notice.  This file is not
98c2ecf20Sopenharmony_ci	a complete program and may only be used when the entire operating
108c2ecf20Sopenharmony_ci	system is licensed under the GPL.
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci	The author may be reached as becker@scyld.com, or C/O
138c2ecf20Sopenharmony_ci	Scyld Computing Corporation
148c2ecf20Sopenharmony_ci	410 Severn Ave., Suite 210
158c2ecf20Sopenharmony_ci	Annapolis MD 21403
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci	Support and updates available at
188c2ecf20Sopenharmony_ci	http://www.scyld.com/network/drivers.html
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	Do not remove the copyright information.
218c2ecf20Sopenharmony_ci	Do not change the version information unless an improvement has been made.
228c2ecf20Sopenharmony_ci	Merely removing my name, as Compex has done in the past, does not count
238c2ecf20Sopenharmony_ci	as an improvement.
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	Changelog:
268c2ecf20Sopenharmony_ci	* ported to 2.4
278c2ecf20Sopenharmony_ci		???
288c2ecf20Sopenharmony_ci	* spin lock update, memory barriers, new style dma mappings
298c2ecf20Sopenharmony_ci		limit each tx buffer to < 1024 bytes
308c2ecf20Sopenharmony_ci		remove DescIntr from Rx descriptors (that's an Tx flag)
318c2ecf20Sopenharmony_ci		remove next pointer from Tx descriptors
328c2ecf20Sopenharmony_ci		synchronize tx_q_bytes
338c2ecf20Sopenharmony_ci		software reset in tx_timeout
348c2ecf20Sopenharmony_ci			Copyright (C) 2000 Manfred Spraul
358c2ecf20Sopenharmony_ci	* further cleanups
368c2ecf20Sopenharmony_ci		power management.
378c2ecf20Sopenharmony_ci		support for big endian descriptors
388c2ecf20Sopenharmony_ci			Copyright (C) 2001 Manfred Spraul
398c2ecf20Sopenharmony_ci  	* ethtool support (jgarzik)
408c2ecf20Sopenharmony_ci	* Replace some MII-related magic numbers with constants (jgarzik)
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	TODO:
438c2ecf20Sopenharmony_ci	* enable pci_power_off
448c2ecf20Sopenharmony_ci	* Wake-On-LAN
458c2ecf20Sopenharmony_ci*/
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define DRV_NAME	"winbond-840"
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/* Automatically extracted configuration info:
528c2ecf20Sopenharmony_ciprobe-func: winbond840_probe
538c2ecf20Sopenharmony_ciconfig-in: tristate 'Winbond W89c840 Ethernet support' CONFIG_WINBOND_840
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cic-help-name: Winbond W89c840 PCI Ethernet support
568c2ecf20Sopenharmony_cic-help-symbol: CONFIG_WINBOND_840
578c2ecf20Sopenharmony_cic-help: This driver is for the Winbond W89c840 chip.  It also works with
588c2ecf20Sopenharmony_cic-help: the TX9882 chip on the Compex RL100-ATX board.
598c2ecf20Sopenharmony_cic-help: More specific information and updates are available from
608c2ecf20Sopenharmony_cic-help: http://www.scyld.com/network/drivers.html
618c2ecf20Sopenharmony_ci*/
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/* The user-configurable values.
648c2ecf20Sopenharmony_ci   These may be modified when a driver module is loaded.*/
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic int debug = 1;			/* 1 normal messages, 0 quiet .. 7 verbose. */
678c2ecf20Sopenharmony_cistatic int max_interrupt_work = 20;
688c2ecf20Sopenharmony_ci/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
698c2ecf20Sopenharmony_ci   The '840 uses a 64 element hash table based on the Ethernet CRC.  */
708c2ecf20Sopenharmony_cistatic int multicast_filter_limit = 32;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
738c2ecf20Sopenharmony_ci   Setting to > 1518 effectively disables this feature. */
748c2ecf20Sopenharmony_cistatic int rx_copybreak;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/* Used to pass the media type, etc.
778c2ecf20Sopenharmony_ci   Both 'options[]' and 'full_duplex[]' should exist for driver
788c2ecf20Sopenharmony_ci   interoperability.
798c2ecf20Sopenharmony_ci   The media type is usually passed in 'options[]'.
808c2ecf20Sopenharmony_ci*/
818c2ecf20Sopenharmony_ci#define MAX_UNITS 8		/* More are supported, limit only on options */
828c2ecf20Sopenharmony_cistatic int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
838c2ecf20Sopenharmony_cistatic int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/* Operational parameters that are set at compile time. */
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/* Keep the ring sizes a power of two for compile efficiency.
888c2ecf20Sopenharmony_ci   The compiler will convert <unsigned>'%'<2^N> into a bit mask.
898c2ecf20Sopenharmony_ci   Making the Tx ring too large decreases the effectiveness of channel
908c2ecf20Sopenharmony_ci   bonding and packet priority.
918c2ecf20Sopenharmony_ci   There are no ill effects from too-large receive rings. */
928c2ecf20Sopenharmony_ci#define TX_QUEUE_LEN	10		/* Limit ring entries actually used.  */
938c2ecf20Sopenharmony_ci#define TX_QUEUE_LEN_RESTART	5
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci#define TX_BUFLIMIT	(1024-128)
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/* The presumed FIFO size for working around the Tx-FIFO-overflow bug.
988c2ecf20Sopenharmony_ci   To avoid overflowing we don't queue again until we have room for a
998c2ecf20Sopenharmony_ci   full-size packet.
1008c2ecf20Sopenharmony_ci */
1018c2ecf20Sopenharmony_ci#define TX_FIFO_SIZE (2048)
1028c2ecf20Sopenharmony_ci#define TX_BUG_FIFO_LIMIT (TX_FIFO_SIZE-1514-16)
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/* Operational parameters that usually are not changed. */
1068c2ecf20Sopenharmony_ci/* Time in jiffies before concluding the transmitter is hung. */
1078c2ecf20Sopenharmony_ci#define TX_TIMEOUT  (2*HZ)
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/* Include files, designed to support most kernel versions 2.0.0 and later. */
1108c2ecf20Sopenharmony_ci#include <linux/module.h>
1118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
1128c2ecf20Sopenharmony_ci#include <linux/string.h>
1138c2ecf20Sopenharmony_ci#include <linux/timer.h>
1148c2ecf20Sopenharmony_ci#include <linux/errno.h>
1158c2ecf20Sopenharmony_ci#include <linux/ioport.h>
1168c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
1178c2ecf20Sopenharmony_ci#include <linux/pci.h>
1188c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
1198c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
1208c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
1218c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
1228c2ecf20Sopenharmony_ci#include <linux/init.h>
1238c2ecf20Sopenharmony_ci#include <linux/delay.h>
1248c2ecf20Sopenharmony_ci#include <linux/ethtool.h>
1258c2ecf20Sopenharmony_ci#include <linux/mii.h>
1268c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
1278c2ecf20Sopenharmony_ci#include <linux/crc32.h>
1288c2ecf20Sopenharmony_ci#include <linux/bitops.h>
1298c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
1308c2ecf20Sopenharmony_ci#include <asm/processor.h>		/* Processor type for cache alignment. */
1318c2ecf20Sopenharmony_ci#include <asm/io.h>
1328c2ecf20Sopenharmony_ci#include <asm/irq.h>
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci#include "tulip.h"
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci#undef PKT_BUF_SZ			/* tulip.h also defines this */
1378c2ecf20Sopenharmony_ci#define PKT_BUF_SZ		1536	/* Size of each temporary Rx buffer.*/
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ciMODULE_AUTHOR("Donald Becker <becker@scyld.com>");
1408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Winbond W89c840 Ethernet driver");
1418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cimodule_param(max_interrupt_work, int, 0);
1448c2ecf20Sopenharmony_cimodule_param(debug, int, 0);
1458c2ecf20Sopenharmony_cimodule_param(rx_copybreak, int, 0);
1468c2ecf20Sopenharmony_cimodule_param(multicast_filter_limit, int, 0);
1478c2ecf20Sopenharmony_cimodule_param_array(options, int, NULL, 0);
1488c2ecf20Sopenharmony_cimodule_param_array(full_duplex, int, NULL, 0);
1498c2ecf20Sopenharmony_ciMODULE_PARM_DESC(max_interrupt_work, "winbond-840 maximum events handled per interrupt");
1508c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "winbond-840 debug level (0-6)");
1518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rx_copybreak, "winbond-840 copy breakpoint for copy-only-tiny-frames");
1528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(multicast_filter_limit, "winbond-840 maximum number of filtered multicast addresses");
1538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(options, "winbond-840: Bits 0-3: media type, bit 17: full duplex");
1548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(full_duplex, "winbond-840 full duplex setting(s) (1)");
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci/*
1578c2ecf20Sopenharmony_ci				Theory of Operation
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ciI. Board Compatibility
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ciThis driver is for the Winbond w89c840 chip.
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ciII. Board-specific settings
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ciNone.
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ciIII. Driver operation
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ciThis chip is very similar to the Digital 21*4* "Tulip" family.  The first
1708c2ecf20Sopenharmony_citwelve registers and the descriptor format are nearly identical.  Read a
1718c2ecf20Sopenharmony_ciTulip manual for operational details.
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ciA significant difference is that the multicast filter and station address are
1748c2ecf20Sopenharmony_cistored in registers rather than loaded through a pseudo-transmit packet.
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ciUnlike the Tulip, transmit buffers are limited to 1KB.  To transmit a
1778c2ecf20Sopenharmony_cifull-sized packet we must use both data buffers in a descriptor.  Thus the
1788c2ecf20Sopenharmony_cidriver uses ring mode where descriptors are implicitly sequential in memory,
1798c2ecf20Sopenharmony_cirather than using the second descriptor address as a chain pointer to
1808c2ecf20Sopenharmony_cisubsequent descriptors.
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ciIV. Notes
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ciIf you are going to almost clone a Tulip, why not go all the way and avoid
1858c2ecf20Sopenharmony_cithe need for a new driver?
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ciIVb. References
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cihttp://www.scyld.com/expert/100mbps.html
1908c2ecf20Sopenharmony_cihttp://www.scyld.com/expert/NWay.html
1918c2ecf20Sopenharmony_cihttp://www.winbond.com.tw/
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ciIVc. Errata
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ciA horrible bug exists in the transmit FIFO.  Apparently the chip doesn't
1968c2ecf20Sopenharmony_cicorrectly detect a full FIFO, and queuing more than 2048 bytes may result in
1978c2ecf20Sopenharmony_cisilent data corruption.
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ciTest with 'ping -s 10000' on a fast computer.
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci*/
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci/*
2068c2ecf20Sopenharmony_ci  PCI probe table.
2078c2ecf20Sopenharmony_ci*/
2088c2ecf20Sopenharmony_cienum chip_capability_flags {
2098c2ecf20Sopenharmony_ci	CanHaveMII=1, HasBrokenTx=2, AlwaysFDX=4, FDXOnNoMII=8,
2108c2ecf20Sopenharmony_ci};
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic const struct pci_device_id w840_pci_tbl[] = {
2138c2ecf20Sopenharmony_ci	{ 0x1050, 0x0840, PCI_ANY_ID, 0x8153,     0, 0, 0 },
2148c2ecf20Sopenharmony_ci	{ 0x1050, 0x0840, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },
2158c2ecf20Sopenharmony_ci	{ 0x11f6, 0x2011, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 },
2168c2ecf20Sopenharmony_ci	{ }
2178c2ecf20Sopenharmony_ci};
2188c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, w840_pci_tbl);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cienum {
2218c2ecf20Sopenharmony_ci	netdev_res_size		= 128,	/* size of PCI BAR resource */
2228c2ecf20Sopenharmony_ci};
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistruct pci_id_info {
2258c2ecf20Sopenharmony_ci        const char *name;
2268c2ecf20Sopenharmony_ci        int drv_flags;		/* Driver use, intended as capability flags. */
2278c2ecf20Sopenharmony_ci};
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic const struct pci_id_info pci_id_tbl[] = {
2308c2ecf20Sopenharmony_ci	{ 				/* Sometime a Level-One switch card. */
2318c2ecf20Sopenharmony_ci	  "Winbond W89c840",	CanHaveMII | HasBrokenTx | FDXOnNoMII},
2328c2ecf20Sopenharmony_ci	{ "Winbond W89c840",	CanHaveMII | HasBrokenTx},
2338c2ecf20Sopenharmony_ci	{ "Compex RL100-ATX",	CanHaveMII | HasBrokenTx},
2348c2ecf20Sopenharmony_ci	{ }	/* terminate list. */
2358c2ecf20Sopenharmony_ci};
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci/* This driver was written to use PCI memory space, however some x86 systems
2388c2ecf20Sopenharmony_ci   work only with I/O space accesses. See CONFIG_TULIP_MMIO in .config
2398c2ecf20Sopenharmony_ci*/
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci/* Offsets to the Command and Status Registers, "CSRs".
2428c2ecf20Sopenharmony_ci   While similar to the Tulip, these registers are longword aligned.
2438c2ecf20Sopenharmony_ci   Note: It's not useful to define symbolic names for every register bit in
2448c2ecf20Sopenharmony_ci   the device.  The name can only partially document the semantics and make
2458c2ecf20Sopenharmony_ci   the driver longer and more difficult to read.
2468c2ecf20Sopenharmony_ci*/
2478c2ecf20Sopenharmony_cienum w840_offsets {
2488c2ecf20Sopenharmony_ci	PCIBusCfg=0x00, TxStartDemand=0x04, RxStartDemand=0x08,
2498c2ecf20Sopenharmony_ci	RxRingPtr=0x0C, TxRingPtr=0x10,
2508c2ecf20Sopenharmony_ci	IntrStatus=0x14, NetworkConfig=0x18, IntrEnable=0x1C,
2518c2ecf20Sopenharmony_ci	RxMissed=0x20, EECtrl=0x24, MIICtrl=0x24, BootRom=0x28, GPTimer=0x2C,
2528c2ecf20Sopenharmony_ci	CurRxDescAddr=0x30, CurRxBufAddr=0x34,			/* Debug use */
2538c2ecf20Sopenharmony_ci	MulticastFilter0=0x38, MulticastFilter1=0x3C, StationAddr=0x40,
2548c2ecf20Sopenharmony_ci	CurTxDescAddr=0x4C, CurTxBufAddr=0x50,
2558c2ecf20Sopenharmony_ci};
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci/* Bits in the NetworkConfig register. */
2588c2ecf20Sopenharmony_cienum rx_mode_bits {
2598c2ecf20Sopenharmony_ci	AcceptErr=0x80,
2608c2ecf20Sopenharmony_ci	RxAcceptBroadcast=0x20, AcceptMulticast=0x10,
2618c2ecf20Sopenharmony_ci	RxAcceptAllPhys=0x08, AcceptMyPhys=0x02,
2628c2ecf20Sopenharmony_ci};
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cienum mii_reg_bits {
2658c2ecf20Sopenharmony_ci	MDIO_ShiftClk=0x10000, MDIO_DataIn=0x80000, MDIO_DataOut=0x20000,
2668c2ecf20Sopenharmony_ci	MDIO_EnbOutput=0x40000, MDIO_EnbIn = 0x00000,
2678c2ecf20Sopenharmony_ci};
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci/* The Tulip Rx and Tx buffer descriptors. */
2708c2ecf20Sopenharmony_cistruct w840_rx_desc {
2718c2ecf20Sopenharmony_ci	s32 status;
2728c2ecf20Sopenharmony_ci	s32 length;
2738c2ecf20Sopenharmony_ci	u32 buffer1;
2748c2ecf20Sopenharmony_ci	u32 buffer2;
2758c2ecf20Sopenharmony_ci};
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistruct w840_tx_desc {
2788c2ecf20Sopenharmony_ci	s32 status;
2798c2ecf20Sopenharmony_ci	s32 length;
2808c2ecf20Sopenharmony_ci	u32 buffer1, buffer2;
2818c2ecf20Sopenharmony_ci};
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci#define MII_CNT		1 /* winbond only supports one MII */
2848c2ecf20Sopenharmony_cistruct netdev_private {
2858c2ecf20Sopenharmony_ci	struct w840_rx_desc *rx_ring;
2868c2ecf20Sopenharmony_ci	dma_addr_t	rx_addr[RX_RING_SIZE];
2878c2ecf20Sopenharmony_ci	struct w840_tx_desc *tx_ring;
2888c2ecf20Sopenharmony_ci	dma_addr_t	tx_addr[TX_RING_SIZE];
2898c2ecf20Sopenharmony_ci	dma_addr_t ring_dma_addr;
2908c2ecf20Sopenharmony_ci	/* The addresses of receive-in-place skbuffs. */
2918c2ecf20Sopenharmony_ci	struct sk_buff* rx_skbuff[RX_RING_SIZE];
2928c2ecf20Sopenharmony_ci	/* The saved address of a sent-in-place packet/buffer, for later free(). */
2938c2ecf20Sopenharmony_ci	struct sk_buff* tx_skbuff[TX_RING_SIZE];
2948c2ecf20Sopenharmony_ci	struct net_device_stats stats;
2958c2ecf20Sopenharmony_ci	struct timer_list timer;	/* Media monitoring timer. */
2968c2ecf20Sopenharmony_ci	/* Frequently used values: keep some adjacent for cache effect. */
2978c2ecf20Sopenharmony_ci	spinlock_t lock;
2988c2ecf20Sopenharmony_ci	int chip_id, drv_flags;
2998c2ecf20Sopenharmony_ci	struct pci_dev *pci_dev;
3008c2ecf20Sopenharmony_ci	int csr6;
3018c2ecf20Sopenharmony_ci	struct w840_rx_desc *rx_head_desc;
3028c2ecf20Sopenharmony_ci	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */
3038c2ecf20Sopenharmony_ci	unsigned int rx_buf_sz;				/* Based on MTU+slack. */
3048c2ecf20Sopenharmony_ci	unsigned int cur_tx, dirty_tx;
3058c2ecf20Sopenharmony_ci	unsigned int tx_q_bytes;
3068c2ecf20Sopenharmony_ci	unsigned int tx_full;				/* The Tx queue is full. */
3078c2ecf20Sopenharmony_ci	/* MII transceiver section. */
3088c2ecf20Sopenharmony_ci	int mii_cnt;						/* MII device addresses. */
3098c2ecf20Sopenharmony_ci	unsigned char phys[MII_CNT];		/* MII device addresses, but only the first is used */
3108c2ecf20Sopenharmony_ci	u32 mii;
3118c2ecf20Sopenharmony_ci	struct mii_if_info mii_if;
3128c2ecf20Sopenharmony_ci	void __iomem *base_addr;
3138c2ecf20Sopenharmony_ci};
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic int  eeprom_read(void __iomem *ioaddr, int location);
3168c2ecf20Sopenharmony_cistatic int  mdio_read(struct net_device *dev, int phy_id, int location);
3178c2ecf20Sopenharmony_cistatic void mdio_write(struct net_device *dev, int phy_id, int location, int value);
3188c2ecf20Sopenharmony_cistatic int  netdev_open(struct net_device *dev);
3198c2ecf20Sopenharmony_cistatic int  update_link(struct net_device *dev);
3208c2ecf20Sopenharmony_cistatic void netdev_timer(struct timer_list *t);
3218c2ecf20Sopenharmony_cistatic void init_rxtx_rings(struct net_device *dev);
3228c2ecf20Sopenharmony_cistatic void free_rxtx_rings(struct netdev_private *np);
3238c2ecf20Sopenharmony_cistatic void init_registers(struct net_device *dev);
3248c2ecf20Sopenharmony_cistatic void tx_timeout(struct net_device *dev, unsigned int txqueue);
3258c2ecf20Sopenharmony_cistatic int alloc_ringdesc(struct net_device *dev);
3268c2ecf20Sopenharmony_cistatic void free_ringdesc(struct netdev_private *np);
3278c2ecf20Sopenharmony_cistatic netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev);
3288c2ecf20Sopenharmony_cistatic irqreturn_t intr_handler(int irq, void *dev_instance);
3298c2ecf20Sopenharmony_cistatic void netdev_error(struct net_device *dev, int intr_status);
3308c2ecf20Sopenharmony_cistatic int  netdev_rx(struct net_device *dev);
3318c2ecf20Sopenharmony_cistatic u32 __set_rx_mode(struct net_device *dev);
3328c2ecf20Sopenharmony_cistatic void set_rx_mode(struct net_device *dev);
3338c2ecf20Sopenharmony_cistatic struct net_device_stats *get_stats(struct net_device *dev);
3348c2ecf20Sopenharmony_cistatic int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
3358c2ecf20Sopenharmony_cistatic const struct ethtool_ops netdev_ethtool_ops;
3368c2ecf20Sopenharmony_cistatic int  netdev_close(struct net_device *dev);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic const struct net_device_ops netdev_ops = {
3398c2ecf20Sopenharmony_ci	.ndo_open		= netdev_open,
3408c2ecf20Sopenharmony_ci	.ndo_stop		= netdev_close,
3418c2ecf20Sopenharmony_ci	.ndo_start_xmit		= start_tx,
3428c2ecf20Sopenharmony_ci	.ndo_get_stats		= get_stats,
3438c2ecf20Sopenharmony_ci	.ndo_set_rx_mode	= set_rx_mode,
3448c2ecf20Sopenharmony_ci	.ndo_do_ioctl		= netdev_ioctl,
3458c2ecf20Sopenharmony_ci	.ndo_tx_timeout		= tx_timeout,
3468c2ecf20Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
3478c2ecf20Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
3488c2ecf20Sopenharmony_ci};
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic int w840_probe1(struct pci_dev *pdev, const struct pci_device_id *ent)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	struct net_device *dev;
3538c2ecf20Sopenharmony_ci	struct netdev_private *np;
3548c2ecf20Sopenharmony_ci	static int find_cnt;
3558c2ecf20Sopenharmony_ci	int chip_idx = ent->driver_data;
3568c2ecf20Sopenharmony_ci	int irq;
3578c2ecf20Sopenharmony_ci	int i, option = find_cnt < MAX_UNITS ? options[find_cnt] : 0;
3588c2ecf20Sopenharmony_ci	void __iomem *ioaddr;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	i = pcim_enable_device(pdev);
3618c2ecf20Sopenharmony_ci	if (i) return i;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	pci_set_master(pdev);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	irq = pdev->irq;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) {
3688c2ecf20Sopenharmony_ci		pr_warn("Device %s disabled due to DMA limitations\n",
3698c2ecf20Sopenharmony_ci			pci_name(pdev));
3708c2ecf20Sopenharmony_ci		return -EIO;
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci	dev = alloc_etherdev(sizeof(*np));
3738c2ecf20Sopenharmony_ci	if (!dev)
3748c2ecf20Sopenharmony_ci		return -ENOMEM;
3758c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(dev, &pdev->dev);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	if (pci_request_regions(pdev, DRV_NAME))
3788c2ecf20Sopenharmony_ci		goto err_out_netdev;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	ioaddr = pci_iomap(pdev, TULIP_BAR, netdev_res_size);
3818c2ecf20Sopenharmony_ci	if (!ioaddr)
3828c2ecf20Sopenharmony_ci		goto err_out_netdev;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++)
3858c2ecf20Sopenharmony_ci		((__le16 *)dev->dev_addr)[i] = cpu_to_le16(eeprom_read(ioaddr, i));
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	/* Reset the chip to erase previous misconfiguration.
3888c2ecf20Sopenharmony_ci	   No hold time required! */
3898c2ecf20Sopenharmony_ci	iowrite32(0x00000001, ioaddr + PCIBusCfg);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	np = netdev_priv(dev);
3928c2ecf20Sopenharmony_ci	np->pci_dev = pdev;
3938c2ecf20Sopenharmony_ci	np->chip_id = chip_idx;
3948c2ecf20Sopenharmony_ci	np->drv_flags = pci_id_tbl[chip_idx].drv_flags;
3958c2ecf20Sopenharmony_ci	spin_lock_init(&np->lock);
3968c2ecf20Sopenharmony_ci	np->mii_if.dev = dev;
3978c2ecf20Sopenharmony_ci	np->mii_if.mdio_read = mdio_read;
3988c2ecf20Sopenharmony_ci	np->mii_if.mdio_write = mdio_write;
3998c2ecf20Sopenharmony_ci	np->base_addr = ioaddr;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, dev);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	if (dev->mem_start)
4048c2ecf20Sopenharmony_ci		option = dev->mem_start;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	/* The lower four bits are the media type. */
4078c2ecf20Sopenharmony_ci	if (option > 0) {
4088c2ecf20Sopenharmony_ci		if (option & 0x200)
4098c2ecf20Sopenharmony_ci			np->mii_if.full_duplex = 1;
4108c2ecf20Sopenharmony_ci		if (option & 15)
4118c2ecf20Sopenharmony_ci			dev_info(&dev->dev,
4128c2ecf20Sopenharmony_ci				 "ignoring user supplied media type %d",
4138c2ecf20Sopenharmony_ci				 option & 15);
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci	if (find_cnt < MAX_UNITS  &&  full_duplex[find_cnt] > 0)
4168c2ecf20Sopenharmony_ci		np->mii_if.full_duplex = 1;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	if (np->mii_if.full_duplex)
4198c2ecf20Sopenharmony_ci		np->mii_if.force_media = 1;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	/* The chip-specific entries in the device structure. */
4228c2ecf20Sopenharmony_ci	dev->netdev_ops = &netdev_ops;
4238c2ecf20Sopenharmony_ci	dev->ethtool_ops = &netdev_ethtool_ops;
4248c2ecf20Sopenharmony_ci	dev->watchdog_timeo = TX_TIMEOUT;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	i = register_netdev(dev);
4278c2ecf20Sopenharmony_ci	if (i)
4288c2ecf20Sopenharmony_ci		goto err_out_cleardev;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	dev_info(&dev->dev, "%s at %p, %pM, IRQ %d\n",
4318c2ecf20Sopenharmony_ci		 pci_id_tbl[chip_idx].name, ioaddr, dev->dev_addr, irq);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	if (np->drv_flags & CanHaveMII) {
4348c2ecf20Sopenharmony_ci		int phy, phy_idx = 0;
4358c2ecf20Sopenharmony_ci		for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) {
4368c2ecf20Sopenharmony_ci			int mii_status = mdio_read(dev, phy, MII_BMSR);
4378c2ecf20Sopenharmony_ci			if (mii_status != 0xffff  &&  mii_status != 0x0000) {
4388c2ecf20Sopenharmony_ci				np->phys[phy_idx++] = phy;
4398c2ecf20Sopenharmony_ci				np->mii_if.advertising = mdio_read(dev, phy, MII_ADVERTISE);
4408c2ecf20Sopenharmony_ci				np->mii = (mdio_read(dev, phy, MII_PHYSID1) << 16)+
4418c2ecf20Sopenharmony_ci						mdio_read(dev, phy, MII_PHYSID2);
4428c2ecf20Sopenharmony_ci				dev_info(&dev->dev,
4438c2ecf20Sopenharmony_ci					 "MII PHY %08xh found at address %d, status 0x%04x advertising %04x\n",
4448c2ecf20Sopenharmony_ci					 np->mii, phy, mii_status,
4458c2ecf20Sopenharmony_ci					 np->mii_if.advertising);
4468c2ecf20Sopenharmony_ci			}
4478c2ecf20Sopenharmony_ci		}
4488c2ecf20Sopenharmony_ci		np->mii_cnt = phy_idx;
4498c2ecf20Sopenharmony_ci		np->mii_if.phy_id = np->phys[0];
4508c2ecf20Sopenharmony_ci		if (phy_idx == 0) {
4518c2ecf20Sopenharmony_ci			dev_warn(&dev->dev,
4528c2ecf20Sopenharmony_ci				 "MII PHY not found -- this device may not operate correctly\n");
4538c2ecf20Sopenharmony_ci		}
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	find_cnt++;
4578c2ecf20Sopenharmony_ci	return 0;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_cierr_out_cleardev:
4608c2ecf20Sopenharmony_ci	pci_iounmap(pdev, ioaddr);
4618c2ecf20Sopenharmony_cierr_out_netdev:
4628c2ecf20Sopenharmony_ci	free_netdev (dev);
4638c2ecf20Sopenharmony_ci	return -ENODEV;
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces.  These are
4688c2ecf20Sopenharmony_ci   often serial bit streams generated by the host processor.
4698c2ecf20Sopenharmony_ci   The example below is for the common 93c46 EEPROM, 64 16 bit words. */
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci/* Delay between EEPROM clock transitions.
4728c2ecf20Sopenharmony_ci   No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need
4738c2ecf20Sopenharmony_ci   a delay.  Note that pre-2.0.34 kernels had a cache-alignment bug that
4748c2ecf20Sopenharmony_ci   made udelay() unreliable.
4758c2ecf20Sopenharmony_ci   The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is
4768c2ecf20Sopenharmony_ci   deprecated.
4778c2ecf20Sopenharmony_ci*/
4788c2ecf20Sopenharmony_ci#define eeprom_delay(ee_addr)	ioread32(ee_addr)
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_cienum EEPROM_Ctrl_Bits {
4818c2ecf20Sopenharmony_ci	EE_ShiftClk=0x02, EE_Write0=0x801, EE_Write1=0x805,
4828c2ecf20Sopenharmony_ci	EE_ChipSelect=0x801, EE_DataIn=0x08,
4838c2ecf20Sopenharmony_ci};
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci/* The EEPROM commands include the alway-set leading bit. */
4868c2ecf20Sopenharmony_cienum EEPROM_Cmds {
4878c2ecf20Sopenharmony_ci	EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6),
4888c2ecf20Sopenharmony_ci};
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_cistatic int eeprom_read(void __iomem *addr, int location)
4918c2ecf20Sopenharmony_ci{
4928c2ecf20Sopenharmony_ci	int i;
4938c2ecf20Sopenharmony_ci	int retval = 0;
4948c2ecf20Sopenharmony_ci	void __iomem *ee_addr = addr + EECtrl;
4958c2ecf20Sopenharmony_ci	int read_cmd = location | EE_ReadCmd;
4968c2ecf20Sopenharmony_ci	iowrite32(EE_ChipSelect, ee_addr);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	/* Shift the read command bits out. */
4998c2ecf20Sopenharmony_ci	for (i = 10; i >= 0; i--) {
5008c2ecf20Sopenharmony_ci		short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0;
5018c2ecf20Sopenharmony_ci		iowrite32(dataval, ee_addr);
5028c2ecf20Sopenharmony_ci		eeprom_delay(ee_addr);
5038c2ecf20Sopenharmony_ci		iowrite32(dataval | EE_ShiftClk, ee_addr);
5048c2ecf20Sopenharmony_ci		eeprom_delay(ee_addr);
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci	iowrite32(EE_ChipSelect, ee_addr);
5078c2ecf20Sopenharmony_ci	eeprom_delay(ee_addr);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	for (i = 16; i > 0; i--) {
5108c2ecf20Sopenharmony_ci		iowrite32(EE_ChipSelect | EE_ShiftClk, ee_addr);
5118c2ecf20Sopenharmony_ci		eeprom_delay(ee_addr);
5128c2ecf20Sopenharmony_ci		retval = (retval << 1) | ((ioread32(ee_addr) & EE_DataIn) ? 1 : 0);
5138c2ecf20Sopenharmony_ci		iowrite32(EE_ChipSelect, ee_addr);
5148c2ecf20Sopenharmony_ci		eeprom_delay(ee_addr);
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	/* Terminate the EEPROM access. */
5188c2ecf20Sopenharmony_ci	iowrite32(0, ee_addr);
5198c2ecf20Sopenharmony_ci	return retval;
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci/*  MII transceiver control section.
5238c2ecf20Sopenharmony_ci	Read and write the MII registers using software-generated serial
5248c2ecf20Sopenharmony_ci	MDIO protocol.  See the MII specifications or DP83840A data sheet
5258c2ecf20Sopenharmony_ci	for details.
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	The maximum data clock rate is 2.5 Mhz.  The minimum timing is usually
5288c2ecf20Sopenharmony_ci	met by back-to-back 33Mhz PCI cycles. */
5298c2ecf20Sopenharmony_ci#define mdio_delay(mdio_addr) ioread32(mdio_addr)
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci/* Set iff a MII transceiver on any interface requires mdio preamble.
5328c2ecf20Sopenharmony_ci   This only set with older transceivers, so the extra
5338c2ecf20Sopenharmony_ci   code size of a per-interface flag is not worthwhile. */
5348c2ecf20Sopenharmony_cistatic char mii_preamble_required = 1;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci#define MDIO_WRITE0 (MDIO_EnbOutput)
5378c2ecf20Sopenharmony_ci#define MDIO_WRITE1 (MDIO_DataOut | MDIO_EnbOutput)
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci/* Generate the preamble required for initial synchronization and
5408c2ecf20Sopenharmony_ci   a few older transceivers. */
5418c2ecf20Sopenharmony_cistatic void mdio_sync(void __iomem *mdio_addr)
5428c2ecf20Sopenharmony_ci{
5438c2ecf20Sopenharmony_ci	int bits = 32;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	/* Establish sync by sending at least 32 logic ones. */
5468c2ecf20Sopenharmony_ci	while (--bits >= 0) {
5478c2ecf20Sopenharmony_ci		iowrite32(MDIO_WRITE1, mdio_addr);
5488c2ecf20Sopenharmony_ci		mdio_delay(mdio_addr);
5498c2ecf20Sopenharmony_ci		iowrite32(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr);
5508c2ecf20Sopenharmony_ci		mdio_delay(mdio_addr);
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_cistatic int mdio_read(struct net_device *dev, int phy_id, int location)
5558c2ecf20Sopenharmony_ci{
5568c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
5578c2ecf20Sopenharmony_ci	void __iomem *mdio_addr = np->base_addr + MIICtrl;
5588c2ecf20Sopenharmony_ci	int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
5598c2ecf20Sopenharmony_ci	int i, retval = 0;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	if (mii_preamble_required)
5628c2ecf20Sopenharmony_ci		mdio_sync(mdio_addr);
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	/* Shift the read command bits out. */
5658c2ecf20Sopenharmony_ci	for (i = 15; i >= 0; i--) {
5668c2ecf20Sopenharmony_ci		int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci		iowrite32(dataval, mdio_addr);
5698c2ecf20Sopenharmony_ci		mdio_delay(mdio_addr);
5708c2ecf20Sopenharmony_ci		iowrite32(dataval | MDIO_ShiftClk, mdio_addr);
5718c2ecf20Sopenharmony_ci		mdio_delay(mdio_addr);
5728c2ecf20Sopenharmony_ci	}
5738c2ecf20Sopenharmony_ci	/* Read the two transition, 16 data, and wire-idle bits. */
5748c2ecf20Sopenharmony_ci	for (i = 20; i > 0; i--) {
5758c2ecf20Sopenharmony_ci		iowrite32(MDIO_EnbIn, mdio_addr);
5768c2ecf20Sopenharmony_ci		mdio_delay(mdio_addr);
5778c2ecf20Sopenharmony_ci		retval = (retval << 1) | ((ioread32(mdio_addr) & MDIO_DataIn) ? 1 : 0);
5788c2ecf20Sopenharmony_ci		iowrite32(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
5798c2ecf20Sopenharmony_ci		mdio_delay(mdio_addr);
5808c2ecf20Sopenharmony_ci	}
5818c2ecf20Sopenharmony_ci	return (retval>>1) & 0xffff;
5828c2ecf20Sopenharmony_ci}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_cistatic void mdio_write(struct net_device *dev, int phy_id, int location, int value)
5858c2ecf20Sopenharmony_ci{
5868c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
5878c2ecf20Sopenharmony_ci	void __iomem *mdio_addr = np->base_addr + MIICtrl;
5888c2ecf20Sopenharmony_ci	int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
5898c2ecf20Sopenharmony_ci	int i;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	if (location == 4  &&  phy_id == np->phys[0])
5928c2ecf20Sopenharmony_ci		np->mii_if.advertising = value;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	if (mii_preamble_required)
5958c2ecf20Sopenharmony_ci		mdio_sync(mdio_addr);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	/* Shift the command bits out. */
5988c2ecf20Sopenharmony_ci	for (i = 31; i >= 0; i--) {
5998c2ecf20Sopenharmony_ci		int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci		iowrite32(dataval, mdio_addr);
6028c2ecf20Sopenharmony_ci		mdio_delay(mdio_addr);
6038c2ecf20Sopenharmony_ci		iowrite32(dataval | MDIO_ShiftClk, mdio_addr);
6048c2ecf20Sopenharmony_ci		mdio_delay(mdio_addr);
6058c2ecf20Sopenharmony_ci	}
6068c2ecf20Sopenharmony_ci	/* Clear out extra bits. */
6078c2ecf20Sopenharmony_ci	for (i = 2; i > 0; i--) {
6088c2ecf20Sopenharmony_ci		iowrite32(MDIO_EnbIn, mdio_addr);
6098c2ecf20Sopenharmony_ci		mdio_delay(mdio_addr);
6108c2ecf20Sopenharmony_ci		iowrite32(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
6118c2ecf20Sopenharmony_ci		mdio_delay(mdio_addr);
6128c2ecf20Sopenharmony_ci	}
6138c2ecf20Sopenharmony_ci}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_cistatic int netdev_open(struct net_device *dev)
6178c2ecf20Sopenharmony_ci{
6188c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
6198c2ecf20Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
6208c2ecf20Sopenharmony_ci	const int irq = np->pci_dev->irq;
6218c2ecf20Sopenharmony_ci	int i;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	iowrite32(0x00000001, ioaddr + PCIBusCfg);		/* Reset */
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	netif_device_detach(dev);
6268c2ecf20Sopenharmony_ci	i = request_irq(irq, intr_handler, IRQF_SHARED, dev->name, dev);
6278c2ecf20Sopenharmony_ci	if (i)
6288c2ecf20Sopenharmony_ci		goto out_err;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	if (debug > 1)
6318c2ecf20Sopenharmony_ci		netdev_dbg(dev, "%s() irq %d\n", __func__, irq);
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	i = alloc_ringdesc(dev);
6348c2ecf20Sopenharmony_ci	if (i)
6358c2ecf20Sopenharmony_ci		goto out_err;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
6388c2ecf20Sopenharmony_ci	netif_device_attach(dev);
6398c2ecf20Sopenharmony_ci	init_registers(dev);
6408c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	netif_start_queue(dev);
6438c2ecf20Sopenharmony_ci	if (debug > 2)
6448c2ecf20Sopenharmony_ci		netdev_dbg(dev, "Done %s()\n", __func__);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	/* Set the timer to check for link beat. */
6478c2ecf20Sopenharmony_ci	timer_setup(&np->timer, netdev_timer, 0);
6488c2ecf20Sopenharmony_ci	np->timer.expires = jiffies + 1*HZ;
6498c2ecf20Sopenharmony_ci	add_timer(&np->timer);
6508c2ecf20Sopenharmony_ci	return 0;
6518c2ecf20Sopenharmony_ciout_err:
6528c2ecf20Sopenharmony_ci	netif_device_attach(dev);
6538c2ecf20Sopenharmony_ci	return i;
6548c2ecf20Sopenharmony_ci}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci#define MII_DAVICOM_DM9101	0x0181b800
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_cistatic int update_link(struct net_device *dev)
6598c2ecf20Sopenharmony_ci{
6608c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
6618c2ecf20Sopenharmony_ci	int duplex, fasteth, result, mii_reg;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	/* BSMR */
6648c2ecf20Sopenharmony_ci	mii_reg = mdio_read(dev, np->phys[0], MII_BMSR);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	if (mii_reg == 0xffff)
6678c2ecf20Sopenharmony_ci		return np->csr6;
6688c2ecf20Sopenharmony_ci	/* reread: the link status bit is sticky */
6698c2ecf20Sopenharmony_ci	mii_reg = mdio_read(dev, np->phys[0], MII_BMSR);
6708c2ecf20Sopenharmony_ci	if (!(mii_reg & 0x4)) {
6718c2ecf20Sopenharmony_ci		if (netif_carrier_ok(dev)) {
6728c2ecf20Sopenharmony_ci			if (debug)
6738c2ecf20Sopenharmony_ci				dev_info(&dev->dev,
6748c2ecf20Sopenharmony_ci					 "MII #%d reports no link. Disabling watchdog\n",
6758c2ecf20Sopenharmony_ci					 np->phys[0]);
6768c2ecf20Sopenharmony_ci			netif_carrier_off(dev);
6778c2ecf20Sopenharmony_ci		}
6788c2ecf20Sopenharmony_ci		return np->csr6;
6798c2ecf20Sopenharmony_ci	}
6808c2ecf20Sopenharmony_ci	if (!netif_carrier_ok(dev)) {
6818c2ecf20Sopenharmony_ci		if (debug)
6828c2ecf20Sopenharmony_ci			dev_info(&dev->dev,
6838c2ecf20Sopenharmony_ci				 "MII #%d link is back. Enabling watchdog\n",
6848c2ecf20Sopenharmony_ci				 np->phys[0]);
6858c2ecf20Sopenharmony_ci		netif_carrier_on(dev);
6868c2ecf20Sopenharmony_ci	}
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	if ((np->mii & ~0xf) == MII_DAVICOM_DM9101) {
6898c2ecf20Sopenharmony_ci		/* If the link partner doesn't support autonegotiation
6908c2ecf20Sopenharmony_ci		 * the MII detects it's abilities with the "parallel detection".
6918c2ecf20Sopenharmony_ci		 * Some MIIs update the LPA register to the result of the parallel
6928c2ecf20Sopenharmony_ci		 * detection, some don't.
6938c2ecf20Sopenharmony_ci		 * The Davicom PHY [at least 0181b800] doesn't.
6948c2ecf20Sopenharmony_ci		 * Instead bit 9 and 13 of the BMCR are updated to the result
6958c2ecf20Sopenharmony_ci		 * of the negotiation..
6968c2ecf20Sopenharmony_ci		 */
6978c2ecf20Sopenharmony_ci		mii_reg = mdio_read(dev, np->phys[0], MII_BMCR);
6988c2ecf20Sopenharmony_ci		duplex = mii_reg & BMCR_FULLDPLX;
6998c2ecf20Sopenharmony_ci		fasteth = mii_reg & BMCR_SPEED100;
7008c2ecf20Sopenharmony_ci	} else {
7018c2ecf20Sopenharmony_ci		int negotiated;
7028c2ecf20Sopenharmony_ci		mii_reg	= mdio_read(dev, np->phys[0], MII_LPA);
7038c2ecf20Sopenharmony_ci		negotiated = mii_reg & np->mii_if.advertising;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci		duplex = (negotiated & LPA_100FULL) || ((negotiated & 0x02C0) == LPA_10FULL);
7068c2ecf20Sopenharmony_ci		fasteth = negotiated & 0x380;
7078c2ecf20Sopenharmony_ci	}
7088c2ecf20Sopenharmony_ci	duplex |= np->mii_if.force_media;
7098c2ecf20Sopenharmony_ci	/* remove fastether and fullduplex */
7108c2ecf20Sopenharmony_ci	result = np->csr6 & ~0x20000200;
7118c2ecf20Sopenharmony_ci	if (duplex)
7128c2ecf20Sopenharmony_ci		result |= 0x200;
7138c2ecf20Sopenharmony_ci	if (fasteth)
7148c2ecf20Sopenharmony_ci		result |= 0x20000000;
7158c2ecf20Sopenharmony_ci	if (result != np->csr6 && debug)
7168c2ecf20Sopenharmony_ci		dev_info(&dev->dev,
7178c2ecf20Sopenharmony_ci			 "Setting %dMBit-%s-duplex based on MII#%d\n",
7188c2ecf20Sopenharmony_ci			 fasteth ? 100 : 10, duplex ? "full" : "half",
7198c2ecf20Sopenharmony_ci			 np->phys[0]);
7208c2ecf20Sopenharmony_ci	return result;
7218c2ecf20Sopenharmony_ci}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci#define RXTX_TIMEOUT	2000
7248c2ecf20Sopenharmony_cistatic inline void update_csr6(struct net_device *dev, int new)
7258c2ecf20Sopenharmony_ci{
7268c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
7278c2ecf20Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
7288c2ecf20Sopenharmony_ci	int limit = RXTX_TIMEOUT;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	if (!netif_device_present(dev))
7318c2ecf20Sopenharmony_ci		new = 0;
7328c2ecf20Sopenharmony_ci	if (new==np->csr6)
7338c2ecf20Sopenharmony_ci		return;
7348c2ecf20Sopenharmony_ci	/* stop both Tx and Rx processes */
7358c2ecf20Sopenharmony_ci	iowrite32(np->csr6 & ~0x2002, ioaddr + NetworkConfig);
7368c2ecf20Sopenharmony_ci	/* wait until they have really stopped */
7378c2ecf20Sopenharmony_ci	for (;;) {
7388c2ecf20Sopenharmony_ci		int csr5 = ioread32(ioaddr + IntrStatus);
7398c2ecf20Sopenharmony_ci		int t;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci		t = (csr5 >> 17) & 0x07;
7428c2ecf20Sopenharmony_ci		if (t==0||t==1) {
7438c2ecf20Sopenharmony_ci			/* rx stopped */
7448c2ecf20Sopenharmony_ci			t = (csr5 >> 20) & 0x07;
7458c2ecf20Sopenharmony_ci			if (t==0||t==1)
7468c2ecf20Sopenharmony_ci				break;
7478c2ecf20Sopenharmony_ci		}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci		limit--;
7508c2ecf20Sopenharmony_ci		if(!limit) {
7518c2ecf20Sopenharmony_ci			dev_info(&dev->dev,
7528c2ecf20Sopenharmony_ci				 "couldn't stop rxtx, IntrStatus %xh\n", csr5);
7538c2ecf20Sopenharmony_ci			break;
7548c2ecf20Sopenharmony_ci		}
7558c2ecf20Sopenharmony_ci		udelay(1);
7568c2ecf20Sopenharmony_ci	}
7578c2ecf20Sopenharmony_ci	np->csr6 = new;
7588c2ecf20Sopenharmony_ci	/* and restart them with the new configuration */
7598c2ecf20Sopenharmony_ci	iowrite32(np->csr6, ioaddr + NetworkConfig);
7608c2ecf20Sopenharmony_ci	if (new & 0x200)
7618c2ecf20Sopenharmony_ci		np->mii_if.full_duplex = 1;
7628c2ecf20Sopenharmony_ci}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_cistatic void netdev_timer(struct timer_list *t)
7658c2ecf20Sopenharmony_ci{
7668c2ecf20Sopenharmony_ci	struct netdev_private *np = from_timer(np, t, timer);
7678c2ecf20Sopenharmony_ci	struct net_device *dev = pci_get_drvdata(np->pci_dev);
7688c2ecf20Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	if (debug > 2)
7718c2ecf20Sopenharmony_ci		netdev_dbg(dev, "Media selection timer tick, status %08x config %08x\n",
7728c2ecf20Sopenharmony_ci			   ioread32(ioaddr + IntrStatus),
7738c2ecf20Sopenharmony_ci			   ioread32(ioaddr + NetworkConfig));
7748c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
7758c2ecf20Sopenharmony_ci	update_csr6(dev, update_link(dev));
7768c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
7778c2ecf20Sopenharmony_ci	np->timer.expires = jiffies + 10*HZ;
7788c2ecf20Sopenharmony_ci	add_timer(&np->timer);
7798c2ecf20Sopenharmony_ci}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_cistatic void init_rxtx_rings(struct net_device *dev)
7828c2ecf20Sopenharmony_ci{
7838c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
7848c2ecf20Sopenharmony_ci	int i;
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	np->rx_head_desc = &np->rx_ring[0];
7878c2ecf20Sopenharmony_ci	np->tx_ring = (struct w840_tx_desc*)&np->rx_ring[RX_RING_SIZE];
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	/* Initial all Rx descriptors. */
7908c2ecf20Sopenharmony_ci	for (i = 0; i < RX_RING_SIZE; i++) {
7918c2ecf20Sopenharmony_ci		np->rx_ring[i].length = np->rx_buf_sz;
7928c2ecf20Sopenharmony_ci		np->rx_ring[i].status = 0;
7938c2ecf20Sopenharmony_ci		np->rx_skbuff[i] = NULL;
7948c2ecf20Sopenharmony_ci	}
7958c2ecf20Sopenharmony_ci	/* Mark the last entry as wrapping the ring. */
7968c2ecf20Sopenharmony_ci	np->rx_ring[i-1].length |= DescEndRing;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	/* Fill in the Rx buffers.  Handle allocation failure gracefully. */
7998c2ecf20Sopenharmony_ci	for (i = 0; i < RX_RING_SIZE; i++) {
8008c2ecf20Sopenharmony_ci		struct sk_buff *skb = netdev_alloc_skb(dev, np->rx_buf_sz);
8018c2ecf20Sopenharmony_ci		np->rx_skbuff[i] = skb;
8028c2ecf20Sopenharmony_ci		if (skb == NULL)
8038c2ecf20Sopenharmony_ci			break;
8048c2ecf20Sopenharmony_ci		np->rx_addr[i] = dma_map_single(&np->pci_dev->dev, skb->data,
8058c2ecf20Sopenharmony_ci						np->rx_buf_sz,
8068c2ecf20Sopenharmony_ci						DMA_FROM_DEVICE);
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci		np->rx_ring[i].buffer1 = np->rx_addr[i];
8098c2ecf20Sopenharmony_ci		np->rx_ring[i].status = DescOwned;
8108c2ecf20Sopenharmony_ci	}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	np->cur_rx = 0;
8138c2ecf20Sopenharmony_ci	np->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	/* Initialize the Tx descriptors */
8168c2ecf20Sopenharmony_ci	for (i = 0; i < TX_RING_SIZE; i++) {
8178c2ecf20Sopenharmony_ci		np->tx_skbuff[i] = NULL;
8188c2ecf20Sopenharmony_ci		np->tx_ring[i].status = 0;
8198c2ecf20Sopenharmony_ci	}
8208c2ecf20Sopenharmony_ci	np->tx_full = 0;
8218c2ecf20Sopenharmony_ci	np->tx_q_bytes = np->dirty_tx = np->cur_tx = 0;
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	iowrite32(np->ring_dma_addr, np->base_addr + RxRingPtr);
8248c2ecf20Sopenharmony_ci	iowrite32(np->ring_dma_addr+sizeof(struct w840_rx_desc)*RX_RING_SIZE,
8258c2ecf20Sopenharmony_ci		np->base_addr + TxRingPtr);
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci}
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_cistatic void free_rxtx_rings(struct netdev_private* np)
8308c2ecf20Sopenharmony_ci{
8318c2ecf20Sopenharmony_ci	int i;
8328c2ecf20Sopenharmony_ci	/* Free all the skbuffs in the Rx queue. */
8338c2ecf20Sopenharmony_ci	for (i = 0; i < RX_RING_SIZE; i++) {
8348c2ecf20Sopenharmony_ci		np->rx_ring[i].status = 0;
8358c2ecf20Sopenharmony_ci		if (np->rx_skbuff[i]) {
8368c2ecf20Sopenharmony_ci			dma_unmap_single(&np->pci_dev->dev, np->rx_addr[i],
8378c2ecf20Sopenharmony_ci					 np->rx_skbuff[i]->len,
8388c2ecf20Sopenharmony_ci					 DMA_FROM_DEVICE);
8398c2ecf20Sopenharmony_ci			dev_kfree_skb(np->rx_skbuff[i]);
8408c2ecf20Sopenharmony_ci		}
8418c2ecf20Sopenharmony_ci		np->rx_skbuff[i] = NULL;
8428c2ecf20Sopenharmony_ci	}
8438c2ecf20Sopenharmony_ci	for (i = 0; i < TX_RING_SIZE; i++) {
8448c2ecf20Sopenharmony_ci		if (np->tx_skbuff[i]) {
8458c2ecf20Sopenharmony_ci			dma_unmap_single(&np->pci_dev->dev, np->tx_addr[i],
8468c2ecf20Sopenharmony_ci					 np->tx_skbuff[i]->len, DMA_TO_DEVICE);
8478c2ecf20Sopenharmony_ci			dev_kfree_skb(np->tx_skbuff[i]);
8488c2ecf20Sopenharmony_ci		}
8498c2ecf20Sopenharmony_ci		np->tx_skbuff[i] = NULL;
8508c2ecf20Sopenharmony_ci	}
8518c2ecf20Sopenharmony_ci}
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_cistatic void init_registers(struct net_device *dev)
8548c2ecf20Sopenharmony_ci{
8558c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
8568c2ecf20Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
8578c2ecf20Sopenharmony_ci	int i;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	for (i = 0; i < 6; i++)
8608c2ecf20Sopenharmony_ci		iowrite8(dev->dev_addr[i], ioaddr + StationAddr + i);
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	/* Initialize other registers. */
8638c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN
8648c2ecf20Sopenharmony_ci	i = (1<<20);	/* Big-endian descriptors */
8658c2ecf20Sopenharmony_ci#else
8668c2ecf20Sopenharmony_ci	i = 0;
8678c2ecf20Sopenharmony_ci#endif
8688c2ecf20Sopenharmony_ci	i |= (0x04<<2);		/* skip length 4 u32 */
8698c2ecf20Sopenharmony_ci	i |= 0x02;		/* give Rx priority */
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	/* Configure the PCI bus bursts and FIFO thresholds.
8728c2ecf20Sopenharmony_ci	   486: Set 8 longword cache alignment, 8 longword burst.
8738c2ecf20Sopenharmony_ci	   586: Set 16 longword cache alignment, no burst limit.
8748c2ecf20Sopenharmony_ci	   Cache alignment bits 15:14	     Burst length 13:8
8758c2ecf20Sopenharmony_ci		0000	<not allowed> 		0000 align to cache	0800 8 longwords
8768c2ecf20Sopenharmony_ci		4000	8  longwords		0100 1 longword		1000 16 longwords
8778c2ecf20Sopenharmony_ci		8000	16 longwords		0200 2 longwords	2000 32 longwords
8788c2ecf20Sopenharmony_ci		C000	32  longwords		0400 4 longwords */
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci#if defined (__i386__) && !defined(MODULE)
8818c2ecf20Sopenharmony_ci	/* When not a module we can work around broken '486 PCI boards. */
8828c2ecf20Sopenharmony_ci	if (boot_cpu_data.x86 <= 4) {
8838c2ecf20Sopenharmony_ci		i |= 0x4800;
8848c2ecf20Sopenharmony_ci		dev_info(&dev->dev,
8858c2ecf20Sopenharmony_ci			 "This is a 386/486 PCI system, setting cache alignment to 8 longwords\n");
8868c2ecf20Sopenharmony_ci	} else {
8878c2ecf20Sopenharmony_ci		i |= 0xE000;
8888c2ecf20Sopenharmony_ci	}
8898c2ecf20Sopenharmony_ci#elif defined(__powerpc__) || defined(__i386__) || defined(__alpha__) || defined(__ia64__) || defined(__x86_64__)
8908c2ecf20Sopenharmony_ci	i |= 0xE000;
8918c2ecf20Sopenharmony_ci#elif defined(CONFIG_SPARC) || defined (CONFIG_PARISC) || defined(CONFIG_ARM)
8928c2ecf20Sopenharmony_ci	i |= 0x4800;
8938c2ecf20Sopenharmony_ci#else
8948c2ecf20Sopenharmony_ci	dev_warn(&dev->dev, "unknown CPU architecture, using default csr0 setting\n");
8958c2ecf20Sopenharmony_ci	i |= 0x4800;
8968c2ecf20Sopenharmony_ci#endif
8978c2ecf20Sopenharmony_ci	iowrite32(i, ioaddr + PCIBusCfg);
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	np->csr6 = 0;
9008c2ecf20Sopenharmony_ci	/* 128 byte Tx threshold;
9018c2ecf20Sopenharmony_ci		Transmit on; Receive on; */
9028c2ecf20Sopenharmony_ci	update_csr6(dev, 0x00022002 | update_link(dev) | __set_rx_mode(dev));
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	/* Clear and Enable interrupts by setting the interrupt mask. */
9058c2ecf20Sopenharmony_ci	iowrite32(0x1A0F5, ioaddr + IntrStatus);
9068c2ecf20Sopenharmony_ci	iowrite32(0x1A0F5, ioaddr + IntrEnable);
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	iowrite32(0, ioaddr + RxStartDemand);
9098c2ecf20Sopenharmony_ci}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_cistatic void tx_timeout(struct net_device *dev, unsigned int txqueue)
9128c2ecf20Sopenharmony_ci{
9138c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
9148c2ecf20Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
9158c2ecf20Sopenharmony_ci	const int irq = np->pci_dev->irq;
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	dev_warn(&dev->dev, "Transmit timed out, status %08x, resetting...\n",
9188c2ecf20Sopenharmony_ci		 ioread32(ioaddr + IntrStatus));
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	{
9218c2ecf20Sopenharmony_ci		int i;
9228c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "  Rx ring %p: ", np->rx_ring);
9238c2ecf20Sopenharmony_ci		for (i = 0; i < RX_RING_SIZE; i++)
9248c2ecf20Sopenharmony_ci			printk(KERN_CONT " %08x", (unsigned int)np->rx_ring[i].status);
9258c2ecf20Sopenharmony_ci		printk(KERN_CONT "\n");
9268c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "  Tx ring %p: ", np->tx_ring);
9278c2ecf20Sopenharmony_ci		for (i = 0; i < TX_RING_SIZE; i++)
9288c2ecf20Sopenharmony_ci			printk(KERN_CONT " %08x", np->tx_ring[i].status);
9298c2ecf20Sopenharmony_ci		printk(KERN_CONT "\n");
9308c2ecf20Sopenharmony_ci	}
9318c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "Tx cur %d Tx dirty %d Tx Full %d, q bytes %d\n",
9328c2ecf20Sopenharmony_ci	       np->cur_tx, np->dirty_tx, np->tx_full, np->tx_q_bytes);
9338c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "Tx Descriptor addr %xh\n", ioread32(ioaddr+0x4C));
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	disable_irq(irq);
9368c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
9378c2ecf20Sopenharmony_ci	/*
9388c2ecf20Sopenharmony_ci	 * Under high load dirty_tx and the internal tx descriptor pointer
9398c2ecf20Sopenharmony_ci	 * come out of sync, thus perform a software reset and reinitialize
9408c2ecf20Sopenharmony_ci	 * everything.
9418c2ecf20Sopenharmony_ci	 */
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	iowrite32(1, np->base_addr+PCIBusCfg);
9448c2ecf20Sopenharmony_ci	udelay(1);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	free_rxtx_rings(np);
9478c2ecf20Sopenharmony_ci	init_rxtx_rings(dev);
9488c2ecf20Sopenharmony_ci	init_registers(dev);
9498c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
9508c2ecf20Sopenharmony_ci	enable_irq(irq);
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	netif_wake_queue(dev);
9538c2ecf20Sopenharmony_ci	netif_trans_update(dev); /* prevent tx timeout */
9548c2ecf20Sopenharmony_ci	np->stats.tx_errors++;
9558c2ecf20Sopenharmony_ci}
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
9588c2ecf20Sopenharmony_cistatic int alloc_ringdesc(struct net_device *dev)
9598c2ecf20Sopenharmony_ci{
9608c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	np->rx_ring = dma_alloc_coherent(&np->pci_dev->dev,
9658c2ecf20Sopenharmony_ci					 sizeof(struct w840_rx_desc) * RX_RING_SIZE +
9668c2ecf20Sopenharmony_ci					 sizeof(struct w840_tx_desc) * TX_RING_SIZE,
9678c2ecf20Sopenharmony_ci					 &np->ring_dma_addr, GFP_KERNEL);
9688c2ecf20Sopenharmony_ci	if(!np->rx_ring)
9698c2ecf20Sopenharmony_ci		return -ENOMEM;
9708c2ecf20Sopenharmony_ci	init_rxtx_rings(dev);
9718c2ecf20Sopenharmony_ci	return 0;
9728c2ecf20Sopenharmony_ci}
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_cistatic void free_ringdesc(struct netdev_private *np)
9758c2ecf20Sopenharmony_ci{
9768c2ecf20Sopenharmony_ci	dma_free_coherent(&np->pci_dev->dev,
9778c2ecf20Sopenharmony_ci			  sizeof(struct w840_rx_desc) * RX_RING_SIZE +
9788c2ecf20Sopenharmony_ci			  sizeof(struct w840_tx_desc) * TX_RING_SIZE,
9798c2ecf20Sopenharmony_ci			  np->rx_ring, np->ring_dma_addr);
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci}
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_cistatic netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev)
9848c2ecf20Sopenharmony_ci{
9858c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
9868c2ecf20Sopenharmony_ci	unsigned entry;
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	/* Caution: the write order is important here, set the field
9898c2ecf20Sopenharmony_ci	   with the "ownership" bits last. */
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	/* Calculate the next Tx descriptor entry. */
9928c2ecf20Sopenharmony_ci	entry = np->cur_tx % TX_RING_SIZE;
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	np->tx_addr[entry] = dma_map_single(&np->pci_dev->dev, skb->data,
9958c2ecf20Sopenharmony_ci					    skb->len, DMA_TO_DEVICE);
9968c2ecf20Sopenharmony_ci	np->tx_skbuff[entry] = skb;
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci	np->tx_ring[entry].buffer1 = np->tx_addr[entry];
9998c2ecf20Sopenharmony_ci	if (skb->len < TX_BUFLIMIT) {
10008c2ecf20Sopenharmony_ci		np->tx_ring[entry].length = DescWholePkt | skb->len;
10018c2ecf20Sopenharmony_ci	} else {
10028c2ecf20Sopenharmony_ci		int len = skb->len - TX_BUFLIMIT;
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci		np->tx_ring[entry].buffer2 = np->tx_addr[entry]+TX_BUFLIMIT;
10058c2ecf20Sopenharmony_ci		np->tx_ring[entry].length = DescWholePkt | (len << 11) | TX_BUFLIMIT;
10068c2ecf20Sopenharmony_ci	}
10078c2ecf20Sopenharmony_ci	if(entry == TX_RING_SIZE-1)
10088c2ecf20Sopenharmony_ci		np->tx_ring[entry].length |= DescEndRing;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	/* Now acquire the irq spinlock.
10118c2ecf20Sopenharmony_ci	 * The difficult race is the ordering between
10128c2ecf20Sopenharmony_ci	 * increasing np->cur_tx and setting DescOwned:
10138c2ecf20Sopenharmony_ci	 * - if np->cur_tx is increased first the interrupt
10148c2ecf20Sopenharmony_ci	 *   handler could consider the packet as transmitted
10158c2ecf20Sopenharmony_ci	 *   since DescOwned is cleared.
10168c2ecf20Sopenharmony_ci	 * - If DescOwned is set first the NIC could report the
10178c2ecf20Sopenharmony_ci	 *   packet as sent, but the interrupt handler would ignore it
10188c2ecf20Sopenharmony_ci	 *   since the np->cur_tx was not yet increased.
10198c2ecf20Sopenharmony_ci	 */
10208c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
10218c2ecf20Sopenharmony_ci	np->cur_tx++;
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci	wmb(); /* flush length, buffer1, buffer2 */
10248c2ecf20Sopenharmony_ci	np->tx_ring[entry].status = DescOwned;
10258c2ecf20Sopenharmony_ci	wmb(); /* flush status and kick the hardware */
10268c2ecf20Sopenharmony_ci	iowrite32(0, np->base_addr + TxStartDemand);
10278c2ecf20Sopenharmony_ci	np->tx_q_bytes += skb->len;
10288c2ecf20Sopenharmony_ci	/* Work around horrible bug in the chip by marking the queue as full
10298c2ecf20Sopenharmony_ci	   when we do not have FIFO room for a maximum sized packet. */
10308c2ecf20Sopenharmony_ci	if (np->cur_tx - np->dirty_tx > TX_QUEUE_LEN ||
10318c2ecf20Sopenharmony_ci		((np->drv_flags & HasBrokenTx) && np->tx_q_bytes > TX_BUG_FIFO_LIMIT)) {
10328c2ecf20Sopenharmony_ci		netif_stop_queue(dev);
10338c2ecf20Sopenharmony_ci		wmb();
10348c2ecf20Sopenharmony_ci		np->tx_full = 1;
10358c2ecf20Sopenharmony_ci	}
10368c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	if (debug > 4) {
10398c2ecf20Sopenharmony_ci		netdev_dbg(dev, "Transmit frame #%d queued in slot %d\n",
10408c2ecf20Sopenharmony_ci			   np->cur_tx, entry);
10418c2ecf20Sopenharmony_ci	}
10428c2ecf20Sopenharmony_ci	return NETDEV_TX_OK;
10438c2ecf20Sopenharmony_ci}
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_cistatic void netdev_tx_done(struct net_device *dev)
10468c2ecf20Sopenharmony_ci{
10478c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
10488c2ecf20Sopenharmony_ci	for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
10498c2ecf20Sopenharmony_ci		int entry = np->dirty_tx % TX_RING_SIZE;
10508c2ecf20Sopenharmony_ci		int tx_status = np->tx_ring[entry].status;
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci		if (tx_status < 0)
10538c2ecf20Sopenharmony_ci			break;
10548c2ecf20Sopenharmony_ci		if (tx_status & 0x8000) { 	/* There was an error, log it. */
10558c2ecf20Sopenharmony_ci#ifndef final_version
10568c2ecf20Sopenharmony_ci			if (debug > 1)
10578c2ecf20Sopenharmony_ci				netdev_dbg(dev, "Transmit error, Tx status %08x\n",
10588c2ecf20Sopenharmony_ci					   tx_status);
10598c2ecf20Sopenharmony_ci#endif
10608c2ecf20Sopenharmony_ci			np->stats.tx_errors++;
10618c2ecf20Sopenharmony_ci			if (tx_status & 0x0104) np->stats.tx_aborted_errors++;
10628c2ecf20Sopenharmony_ci			if (tx_status & 0x0C80) np->stats.tx_carrier_errors++;
10638c2ecf20Sopenharmony_ci			if (tx_status & 0x0200) np->stats.tx_window_errors++;
10648c2ecf20Sopenharmony_ci			if (tx_status & 0x0002) np->stats.tx_fifo_errors++;
10658c2ecf20Sopenharmony_ci			if ((tx_status & 0x0080) && np->mii_if.full_duplex == 0)
10668c2ecf20Sopenharmony_ci				np->stats.tx_heartbeat_errors++;
10678c2ecf20Sopenharmony_ci		} else {
10688c2ecf20Sopenharmony_ci#ifndef final_version
10698c2ecf20Sopenharmony_ci			if (debug > 3)
10708c2ecf20Sopenharmony_ci				netdev_dbg(dev, "Transmit slot %d ok, Tx status %08x\n",
10718c2ecf20Sopenharmony_ci					   entry, tx_status);
10728c2ecf20Sopenharmony_ci#endif
10738c2ecf20Sopenharmony_ci			np->stats.tx_bytes += np->tx_skbuff[entry]->len;
10748c2ecf20Sopenharmony_ci			np->stats.collisions += (tx_status >> 3) & 15;
10758c2ecf20Sopenharmony_ci			np->stats.tx_packets++;
10768c2ecf20Sopenharmony_ci		}
10778c2ecf20Sopenharmony_ci		/* Free the original skb. */
10788c2ecf20Sopenharmony_ci		dma_unmap_single(&np->pci_dev->dev, np->tx_addr[entry],
10798c2ecf20Sopenharmony_ci				 np->tx_skbuff[entry]->len, DMA_TO_DEVICE);
10808c2ecf20Sopenharmony_ci		np->tx_q_bytes -= np->tx_skbuff[entry]->len;
10818c2ecf20Sopenharmony_ci		dev_kfree_skb_irq(np->tx_skbuff[entry]);
10828c2ecf20Sopenharmony_ci		np->tx_skbuff[entry] = NULL;
10838c2ecf20Sopenharmony_ci	}
10848c2ecf20Sopenharmony_ci	if (np->tx_full &&
10858c2ecf20Sopenharmony_ci		np->cur_tx - np->dirty_tx < TX_QUEUE_LEN_RESTART &&
10868c2ecf20Sopenharmony_ci		np->tx_q_bytes < TX_BUG_FIFO_LIMIT) {
10878c2ecf20Sopenharmony_ci		/* The ring is no longer full, clear tbusy. */
10888c2ecf20Sopenharmony_ci		np->tx_full = 0;
10898c2ecf20Sopenharmony_ci		wmb();
10908c2ecf20Sopenharmony_ci		netif_wake_queue(dev);
10918c2ecf20Sopenharmony_ci	}
10928c2ecf20Sopenharmony_ci}
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci/* The interrupt handler does all of the Rx thread work and cleans up
10958c2ecf20Sopenharmony_ci   after the Tx thread. */
10968c2ecf20Sopenharmony_cistatic irqreturn_t intr_handler(int irq, void *dev_instance)
10978c2ecf20Sopenharmony_ci{
10988c2ecf20Sopenharmony_ci	struct net_device *dev = (struct net_device *)dev_instance;
10998c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
11008c2ecf20Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
11018c2ecf20Sopenharmony_ci	int work_limit = max_interrupt_work;
11028c2ecf20Sopenharmony_ci	int handled = 0;
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	if (!netif_device_present(dev))
11058c2ecf20Sopenharmony_ci		return IRQ_NONE;
11068c2ecf20Sopenharmony_ci	do {
11078c2ecf20Sopenharmony_ci		u32 intr_status = ioread32(ioaddr + IntrStatus);
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci		/* Acknowledge all of the current interrupt sources ASAP. */
11108c2ecf20Sopenharmony_ci		iowrite32(intr_status & 0x001ffff, ioaddr + IntrStatus);
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci		if (debug > 4)
11138c2ecf20Sopenharmony_ci			netdev_dbg(dev, "Interrupt, status %04x\n", intr_status);
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci		if ((intr_status & (NormalIntr|AbnormalIntr)) == 0)
11168c2ecf20Sopenharmony_ci			break;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci		handled = 1;
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci		if (intr_status & (RxIntr | RxNoBuf))
11218c2ecf20Sopenharmony_ci			netdev_rx(dev);
11228c2ecf20Sopenharmony_ci		if (intr_status & RxNoBuf)
11238c2ecf20Sopenharmony_ci			iowrite32(0, ioaddr + RxStartDemand);
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci		if (intr_status & (TxNoBuf | TxIntr) &&
11268c2ecf20Sopenharmony_ci			np->cur_tx != np->dirty_tx) {
11278c2ecf20Sopenharmony_ci			spin_lock(&np->lock);
11288c2ecf20Sopenharmony_ci			netdev_tx_done(dev);
11298c2ecf20Sopenharmony_ci			spin_unlock(&np->lock);
11308c2ecf20Sopenharmony_ci		}
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci		/* Abnormal error summary/uncommon events handlers. */
11338c2ecf20Sopenharmony_ci		if (intr_status & (AbnormalIntr | TxFIFOUnderflow | SystemError |
11348c2ecf20Sopenharmony_ci						   TimerInt | TxDied))
11358c2ecf20Sopenharmony_ci			netdev_error(dev, intr_status);
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci		if (--work_limit < 0) {
11388c2ecf20Sopenharmony_ci			dev_warn(&dev->dev,
11398c2ecf20Sopenharmony_ci				 "Too much work at interrupt, status=0x%04x\n",
11408c2ecf20Sopenharmony_ci				 intr_status);
11418c2ecf20Sopenharmony_ci			/* Set the timer to re-enable the other interrupts after
11428c2ecf20Sopenharmony_ci			   10*82usec ticks. */
11438c2ecf20Sopenharmony_ci			spin_lock(&np->lock);
11448c2ecf20Sopenharmony_ci			if (netif_device_present(dev)) {
11458c2ecf20Sopenharmony_ci				iowrite32(AbnormalIntr | TimerInt, ioaddr + IntrEnable);
11468c2ecf20Sopenharmony_ci				iowrite32(10, ioaddr + GPTimer);
11478c2ecf20Sopenharmony_ci			}
11488c2ecf20Sopenharmony_ci			spin_unlock(&np->lock);
11498c2ecf20Sopenharmony_ci			break;
11508c2ecf20Sopenharmony_ci		}
11518c2ecf20Sopenharmony_ci	} while (1);
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	if (debug > 3)
11548c2ecf20Sopenharmony_ci		netdev_dbg(dev, "exiting interrupt, status=%#4.4x\n",
11558c2ecf20Sopenharmony_ci			   ioread32(ioaddr + IntrStatus));
11568c2ecf20Sopenharmony_ci	return IRQ_RETVAL(handled);
11578c2ecf20Sopenharmony_ci}
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci/* This routine is logically part of the interrupt handler, but separated
11608c2ecf20Sopenharmony_ci   for clarity and better register allocation. */
11618c2ecf20Sopenharmony_cistatic int netdev_rx(struct net_device *dev)
11628c2ecf20Sopenharmony_ci{
11638c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
11648c2ecf20Sopenharmony_ci	int entry = np->cur_rx % RX_RING_SIZE;
11658c2ecf20Sopenharmony_ci	int work_limit = np->dirty_rx + RX_RING_SIZE - np->cur_rx;
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	if (debug > 4) {
11688c2ecf20Sopenharmony_ci		netdev_dbg(dev, " In netdev_rx(), entry %d status %04x\n",
11698c2ecf20Sopenharmony_ci			   entry, np->rx_ring[entry].status);
11708c2ecf20Sopenharmony_ci	}
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci	/* If EOP is set on the next entry, it's a new packet. Send it up. */
11738c2ecf20Sopenharmony_ci	while (--work_limit >= 0) {
11748c2ecf20Sopenharmony_ci		struct w840_rx_desc *desc = np->rx_head_desc;
11758c2ecf20Sopenharmony_ci		s32 status = desc->status;
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci		if (debug > 4)
11788c2ecf20Sopenharmony_ci			netdev_dbg(dev, "  netdev_rx() status was %08x\n",
11798c2ecf20Sopenharmony_ci				   status);
11808c2ecf20Sopenharmony_ci		if (status < 0)
11818c2ecf20Sopenharmony_ci			break;
11828c2ecf20Sopenharmony_ci		if ((status & 0x38008300) != 0x0300) {
11838c2ecf20Sopenharmony_ci			if ((status & 0x38000300) != 0x0300) {
11848c2ecf20Sopenharmony_ci				/* Ingore earlier buffers. */
11858c2ecf20Sopenharmony_ci				if ((status & 0xffff) != 0x7fff) {
11868c2ecf20Sopenharmony_ci					dev_warn(&dev->dev,
11878c2ecf20Sopenharmony_ci						 "Oversized Ethernet frame spanned multiple buffers, entry %#x status %04x!\n",
11888c2ecf20Sopenharmony_ci						 np->cur_rx, status);
11898c2ecf20Sopenharmony_ci					np->stats.rx_length_errors++;
11908c2ecf20Sopenharmony_ci				}
11918c2ecf20Sopenharmony_ci			} else if (status & 0x8000) {
11928c2ecf20Sopenharmony_ci				/* There was a fatal error. */
11938c2ecf20Sopenharmony_ci				if (debug > 2)
11948c2ecf20Sopenharmony_ci					netdev_dbg(dev, "Receive error, Rx status %08x\n",
11958c2ecf20Sopenharmony_ci						   status);
11968c2ecf20Sopenharmony_ci				np->stats.rx_errors++; /* end of a packet.*/
11978c2ecf20Sopenharmony_ci				if (status & 0x0890) np->stats.rx_length_errors++;
11988c2ecf20Sopenharmony_ci				if (status & 0x004C) np->stats.rx_frame_errors++;
11998c2ecf20Sopenharmony_ci				if (status & 0x0002) np->stats.rx_crc_errors++;
12008c2ecf20Sopenharmony_ci			}
12018c2ecf20Sopenharmony_ci		} else {
12028c2ecf20Sopenharmony_ci			struct sk_buff *skb;
12038c2ecf20Sopenharmony_ci			/* Omit the four octet CRC from the length. */
12048c2ecf20Sopenharmony_ci			int pkt_len = ((status >> 16) & 0x7ff) - 4;
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci#ifndef final_version
12078c2ecf20Sopenharmony_ci			if (debug > 4)
12088c2ecf20Sopenharmony_ci				netdev_dbg(dev, "  netdev_rx() normal Rx pkt length %d status %x\n",
12098c2ecf20Sopenharmony_ci					   pkt_len, status);
12108c2ecf20Sopenharmony_ci#endif
12118c2ecf20Sopenharmony_ci			/* Check if the packet is long enough to accept without copying
12128c2ecf20Sopenharmony_ci			   to a minimally-sized skbuff. */
12138c2ecf20Sopenharmony_ci			if (pkt_len < rx_copybreak &&
12148c2ecf20Sopenharmony_ci			    (skb = netdev_alloc_skb(dev, pkt_len + 2)) != NULL) {
12158c2ecf20Sopenharmony_ci				skb_reserve(skb, 2);	/* 16 byte align the IP header */
12168c2ecf20Sopenharmony_ci				dma_sync_single_for_cpu(&np->pci_dev->dev,
12178c2ecf20Sopenharmony_ci							np->rx_addr[entry],
12188c2ecf20Sopenharmony_ci							np->rx_skbuff[entry]->len,
12198c2ecf20Sopenharmony_ci							DMA_FROM_DEVICE);
12208c2ecf20Sopenharmony_ci				skb_copy_to_linear_data(skb, np->rx_skbuff[entry]->data, pkt_len);
12218c2ecf20Sopenharmony_ci				skb_put(skb, pkt_len);
12228c2ecf20Sopenharmony_ci				dma_sync_single_for_device(&np->pci_dev->dev,
12238c2ecf20Sopenharmony_ci							   np->rx_addr[entry],
12248c2ecf20Sopenharmony_ci							   np->rx_skbuff[entry]->len,
12258c2ecf20Sopenharmony_ci							   DMA_FROM_DEVICE);
12268c2ecf20Sopenharmony_ci			} else {
12278c2ecf20Sopenharmony_ci				dma_unmap_single(&np->pci_dev->dev,
12288c2ecf20Sopenharmony_ci						 np->rx_addr[entry],
12298c2ecf20Sopenharmony_ci						 np->rx_skbuff[entry]->len,
12308c2ecf20Sopenharmony_ci						 DMA_FROM_DEVICE);
12318c2ecf20Sopenharmony_ci				skb_put(skb = np->rx_skbuff[entry], pkt_len);
12328c2ecf20Sopenharmony_ci				np->rx_skbuff[entry] = NULL;
12338c2ecf20Sopenharmony_ci			}
12348c2ecf20Sopenharmony_ci#ifndef final_version				/* Remove after testing. */
12358c2ecf20Sopenharmony_ci			/* You will want this info for the initial debug. */
12368c2ecf20Sopenharmony_ci			if (debug > 5)
12378c2ecf20Sopenharmony_ci				netdev_dbg(dev, "  Rx data %pM %pM %02x%02x %pI4\n",
12388c2ecf20Sopenharmony_ci					   &skb->data[0], &skb->data[6],
12398c2ecf20Sopenharmony_ci					   skb->data[12], skb->data[13],
12408c2ecf20Sopenharmony_ci					   &skb->data[14]);
12418c2ecf20Sopenharmony_ci#endif
12428c2ecf20Sopenharmony_ci			skb->protocol = eth_type_trans(skb, dev);
12438c2ecf20Sopenharmony_ci			netif_rx(skb);
12448c2ecf20Sopenharmony_ci			np->stats.rx_packets++;
12458c2ecf20Sopenharmony_ci			np->stats.rx_bytes += pkt_len;
12468c2ecf20Sopenharmony_ci		}
12478c2ecf20Sopenharmony_ci		entry = (++np->cur_rx) % RX_RING_SIZE;
12488c2ecf20Sopenharmony_ci		np->rx_head_desc = &np->rx_ring[entry];
12498c2ecf20Sopenharmony_ci	}
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	/* Refill the Rx ring buffers. */
12528c2ecf20Sopenharmony_ci	for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) {
12538c2ecf20Sopenharmony_ci		struct sk_buff *skb;
12548c2ecf20Sopenharmony_ci		entry = np->dirty_rx % RX_RING_SIZE;
12558c2ecf20Sopenharmony_ci		if (np->rx_skbuff[entry] == NULL) {
12568c2ecf20Sopenharmony_ci			skb = netdev_alloc_skb(dev, np->rx_buf_sz);
12578c2ecf20Sopenharmony_ci			np->rx_skbuff[entry] = skb;
12588c2ecf20Sopenharmony_ci			if (skb == NULL)
12598c2ecf20Sopenharmony_ci				break;			/* Better luck next round. */
12608c2ecf20Sopenharmony_ci			np->rx_addr[entry] = dma_map_single(&np->pci_dev->dev,
12618c2ecf20Sopenharmony_ci							    skb->data,
12628c2ecf20Sopenharmony_ci							    np->rx_buf_sz,
12638c2ecf20Sopenharmony_ci							    DMA_FROM_DEVICE);
12648c2ecf20Sopenharmony_ci			np->rx_ring[entry].buffer1 = np->rx_addr[entry];
12658c2ecf20Sopenharmony_ci		}
12668c2ecf20Sopenharmony_ci		wmb();
12678c2ecf20Sopenharmony_ci		np->rx_ring[entry].status = DescOwned;
12688c2ecf20Sopenharmony_ci	}
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	return 0;
12718c2ecf20Sopenharmony_ci}
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_cistatic void netdev_error(struct net_device *dev, int intr_status)
12748c2ecf20Sopenharmony_ci{
12758c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
12768c2ecf20Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	if (debug > 2)
12798c2ecf20Sopenharmony_ci		netdev_dbg(dev, "Abnormal event, %08x\n", intr_status);
12808c2ecf20Sopenharmony_ci	if (intr_status == 0xffffffff)
12818c2ecf20Sopenharmony_ci		return;
12828c2ecf20Sopenharmony_ci	spin_lock(&np->lock);
12838c2ecf20Sopenharmony_ci	if (intr_status & TxFIFOUnderflow) {
12848c2ecf20Sopenharmony_ci		int new;
12858c2ecf20Sopenharmony_ci		/* Bump up the Tx threshold */
12868c2ecf20Sopenharmony_ci#if 0
12878c2ecf20Sopenharmony_ci		/* This causes lots of dropped packets,
12888c2ecf20Sopenharmony_ci		 * and under high load even tx_timeouts
12898c2ecf20Sopenharmony_ci		 */
12908c2ecf20Sopenharmony_ci		new = np->csr6 + 0x4000;
12918c2ecf20Sopenharmony_ci#else
12928c2ecf20Sopenharmony_ci		new = (np->csr6 >> 14)&0x7f;
12938c2ecf20Sopenharmony_ci		if (new < 64)
12948c2ecf20Sopenharmony_ci			new *= 2;
12958c2ecf20Sopenharmony_ci		 else
12968c2ecf20Sopenharmony_ci		 	new = 127; /* load full packet before starting */
12978c2ecf20Sopenharmony_ci		new = (np->csr6 & ~(0x7F << 14)) | (new<<14);
12988c2ecf20Sopenharmony_ci#endif
12998c2ecf20Sopenharmony_ci		netdev_dbg(dev, "Tx underflow, new csr6 %08x\n", new);
13008c2ecf20Sopenharmony_ci		update_csr6(dev, new);
13018c2ecf20Sopenharmony_ci	}
13028c2ecf20Sopenharmony_ci	if (intr_status & RxDied) {		/* Missed a Rx frame. */
13038c2ecf20Sopenharmony_ci		np->stats.rx_errors++;
13048c2ecf20Sopenharmony_ci	}
13058c2ecf20Sopenharmony_ci	if (intr_status & TimerInt) {
13068c2ecf20Sopenharmony_ci		/* Re-enable other interrupts. */
13078c2ecf20Sopenharmony_ci		if (netif_device_present(dev))
13088c2ecf20Sopenharmony_ci			iowrite32(0x1A0F5, ioaddr + IntrEnable);
13098c2ecf20Sopenharmony_ci	}
13108c2ecf20Sopenharmony_ci	np->stats.rx_missed_errors += ioread32(ioaddr + RxMissed) & 0xffff;
13118c2ecf20Sopenharmony_ci	iowrite32(0, ioaddr + RxStartDemand);
13128c2ecf20Sopenharmony_ci	spin_unlock(&np->lock);
13138c2ecf20Sopenharmony_ci}
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_cistatic struct net_device_stats *get_stats(struct net_device *dev)
13168c2ecf20Sopenharmony_ci{
13178c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
13188c2ecf20Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	/* The chip only need report frame silently dropped. */
13218c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
13228c2ecf20Sopenharmony_ci	if (netif_running(dev) && netif_device_present(dev))
13238c2ecf20Sopenharmony_ci		np->stats.rx_missed_errors += ioread32(ioaddr + RxMissed) & 0xffff;
13248c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	return &np->stats;
13278c2ecf20Sopenharmony_ci}
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_cistatic u32 __set_rx_mode(struct net_device *dev)
13318c2ecf20Sopenharmony_ci{
13328c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
13338c2ecf20Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
13348c2ecf20Sopenharmony_ci	u32 mc_filter[2];			/* Multicast hash filter */
13358c2ecf20Sopenharmony_ci	u32 rx_mode;
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
13388c2ecf20Sopenharmony_ci		memset(mc_filter, 0xff, sizeof(mc_filter));
13398c2ecf20Sopenharmony_ci		rx_mode = RxAcceptBroadcast | AcceptMulticast | RxAcceptAllPhys
13408c2ecf20Sopenharmony_ci			| AcceptMyPhys;
13418c2ecf20Sopenharmony_ci	} else if ((netdev_mc_count(dev) > multicast_filter_limit) ||
13428c2ecf20Sopenharmony_ci		   (dev->flags & IFF_ALLMULTI)) {
13438c2ecf20Sopenharmony_ci		/* Too many to match, or accept all multicasts. */
13448c2ecf20Sopenharmony_ci		memset(mc_filter, 0xff, sizeof(mc_filter));
13458c2ecf20Sopenharmony_ci		rx_mode = RxAcceptBroadcast | AcceptMulticast | AcceptMyPhys;
13468c2ecf20Sopenharmony_ci	} else {
13478c2ecf20Sopenharmony_ci		struct netdev_hw_addr *ha;
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci		memset(mc_filter, 0, sizeof(mc_filter));
13508c2ecf20Sopenharmony_ci		netdev_for_each_mc_addr(ha, dev) {
13518c2ecf20Sopenharmony_ci			int filbit;
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci			filbit = (ether_crc(ETH_ALEN, ha->addr) >> 26) ^ 0x3F;
13548c2ecf20Sopenharmony_ci			filbit &= 0x3f;
13558c2ecf20Sopenharmony_ci			mc_filter[filbit >> 5] |= 1 << (filbit & 31);
13568c2ecf20Sopenharmony_ci		}
13578c2ecf20Sopenharmony_ci		rx_mode = RxAcceptBroadcast | AcceptMulticast | AcceptMyPhys;
13588c2ecf20Sopenharmony_ci	}
13598c2ecf20Sopenharmony_ci	iowrite32(mc_filter[0], ioaddr + MulticastFilter0);
13608c2ecf20Sopenharmony_ci	iowrite32(mc_filter[1], ioaddr + MulticastFilter1);
13618c2ecf20Sopenharmony_ci	return rx_mode;
13628c2ecf20Sopenharmony_ci}
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_cistatic void set_rx_mode(struct net_device *dev)
13658c2ecf20Sopenharmony_ci{
13668c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
13678c2ecf20Sopenharmony_ci	u32 rx_mode = __set_rx_mode(dev);
13688c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
13698c2ecf20Sopenharmony_ci	update_csr6(dev, (np->csr6 & ~0x00F8) | rx_mode);
13708c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
13718c2ecf20Sopenharmony_ci}
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_cistatic void netdev_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info)
13748c2ecf20Sopenharmony_ci{
13758c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci	strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
13788c2ecf20Sopenharmony_ci	strlcpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info));
13798c2ecf20Sopenharmony_ci}
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_cistatic int netdev_get_link_ksettings(struct net_device *dev,
13828c2ecf20Sopenharmony_ci				     struct ethtool_link_ksettings *cmd)
13838c2ecf20Sopenharmony_ci{
13848c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
13878c2ecf20Sopenharmony_ci	mii_ethtool_get_link_ksettings(&np->mii_if, cmd);
13888c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci	return 0;
13918c2ecf20Sopenharmony_ci}
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_cistatic int netdev_set_link_ksettings(struct net_device *dev,
13948c2ecf20Sopenharmony_ci				     const struct ethtool_link_ksettings *cmd)
13958c2ecf20Sopenharmony_ci{
13968c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
13978c2ecf20Sopenharmony_ci	int rc;
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
14008c2ecf20Sopenharmony_ci	rc = mii_ethtool_set_link_ksettings(&np->mii_if, cmd);
14018c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	return rc;
14048c2ecf20Sopenharmony_ci}
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_cistatic int netdev_nway_reset(struct net_device *dev)
14078c2ecf20Sopenharmony_ci{
14088c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
14098c2ecf20Sopenharmony_ci	return mii_nway_restart(&np->mii_if);
14108c2ecf20Sopenharmony_ci}
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_cistatic u32 netdev_get_link(struct net_device *dev)
14138c2ecf20Sopenharmony_ci{
14148c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
14158c2ecf20Sopenharmony_ci	return mii_link_ok(&np->mii_if);
14168c2ecf20Sopenharmony_ci}
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_cistatic u32 netdev_get_msglevel(struct net_device *dev)
14198c2ecf20Sopenharmony_ci{
14208c2ecf20Sopenharmony_ci	return debug;
14218c2ecf20Sopenharmony_ci}
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_cistatic void netdev_set_msglevel(struct net_device *dev, u32 value)
14248c2ecf20Sopenharmony_ci{
14258c2ecf20Sopenharmony_ci	debug = value;
14268c2ecf20Sopenharmony_ci}
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_cistatic const struct ethtool_ops netdev_ethtool_ops = {
14298c2ecf20Sopenharmony_ci	.get_drvinfo		= netdev_get_drvinfo,
14308c2ecf20Sopenharmony_ci	.nway_reset		= netdev_nway_reset,
14318c2ecf20Sopenharmony_ci	.get_link		= netdev_get_link,
14328c2ecf20Sopenharmony_ci	.get_msglevel		= netdev_get_msglevel,
14338c2ecf20Sopenharmony_ci	.set_msglevel		= netdev_set_msglevel,
14348c2ecf20Sopenharmony_ci	.get_link_ksettings	= netdev_get_link_ksettings,
14358c2ecf20Sopenharmony_ci	.set_link_ksettings	= netdev_set_link_ksettings,
14368c2ecf20Sopenharmony_ci};
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_cistatic int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
14398c2ecf20Sopenharmony_ci{
14408c2ecf20Sopenharmony_ci	struct mii_ioctl_data *data = if_mii(rq);
14418c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	switch(cmd) {
14448c2ecf20Sopenharmony_ci	case SIOCGMIIPHY:		/* Get address of MII PHY in use. */
14458c2ecf20Sopenharmony_ci		data->phy_id = ((struct netdev_private *)netdev_priv(dev))->phys[0] & 0x1f;
14468c2ecf20Sopenharmony_ci		fallthrough;
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	case SIOCGMIIREG:		/* Read MII PHY register. */
14498c2ecf20Sopenharmony_ci		spin_lock_irq(&np->lock);
14508c2ecf20Sopenharmony_ci		data->val_out = mdio_read(dev, data->phy_id & 0x1f, data->reg_num & 0x1f);
14518c2ecf20Sopenharmony_ci		spin_unlock_irq(&np->lock);
14528c2ecf20Sopenharmony_ci		return 0;
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_ci	case SIOCSMIIREG:		/* Write MII PHY register. */
14558c2ecf20Sopenharmony_ci		spin_lock_irq(&np->lock);
14568c2ecf20Sopenharmony_ci		mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in);
14578c2ecf20Sopenharmony_ci		spin_unlock_irq(&np->lock);
14588c2ecf20Sopenharmony_ci		return 0;
14598c2ecf20Sopenharmony_ci	default:
14608c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
14618c2ecf20Sopenharmony_ci	}
14628c2ecf20Sopenharmony_ci}
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_cistatic int netdev_close(struct net_device *dev)
14658c2ecf20Sopenharmony_ci{
14668c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
14678c2ecf20Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	netif_stop_queue(dev);
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	if (debug > 1) {
14728c2ecf20Sopenharmony_ci		netdev_dbg(dev, "Shutting down ethercard, status was %08x Config %08x\n",
14738c2ecf20Sopenharmony_ci			   ioread32(ioaddr + IntrStatus),
14748c2ecf20Sopenharmony_ci			   ioread32(ioaddr + NetworkConfig));
14758c2ecf20Sopenharmony_ci		netdev_dbg(dev, "Queue pointers were Tx %d / %d,  Rx %d / %d\n",
14768c2ecf20Sopenharmony_ci			   np->cur_tx, np->dirty_tx,
14778c2ecf20Sopenharmony_ci			   np->cur_rx, np->dirty_rx);
14788c2ecf20Sopenharmony_ci	}
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci 	/* Stop the chip's Tx and Rx processes. */
14818c2ecf20Sopenharmony_ci	spin_lock_irq(&np->lock);
14828c2ecf20Sopenharmony_ci	netif_device_detach(dev);
14838c2ecf20Sopenharmony_ci	update_csr6(dev, 0);
14848c2ecf20Sopenharmony_ci	iowrite32(0x0000, ioaddr + IntrEnable);
14858c2ecf20Sopenharmony_ci	spin_unlock_irq(&np->lock);
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ci	free_irq(np->pci_dev->irq, dev);
14888c2ecf20Sopenharmony_ci	wmb();
14898c2ecf20Sopenharmony_ci	netif_device_attach(dev);
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci	if (ioread32(ioaddr + NetworkConfig) != 0xffffffff)
14928c2ecf20Sopenharmony_ci		np->stats.rx_missed_errors += ioread32(ioaddr + RxMissed) & 0xffff;
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci#ifdef __i386__
14958c2ecf20Sopenharmony_ci	if (debug > 2) {
14968c2ecf20Sopenharmony_ci		int i;
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_ci		printk(KERN_DEBUG"  Tx ring at %p:\n", np->tx_ring);
14998c2ecf20Sopenharmony_ci		for (i = 0; i < TX_RING_SIZE; i++)
15008c2ecf20Sopenharmony_ci			printk(KERN_DEBUG " #%d desc. %04x %04x %08x\n",
15018c2ecf20Sopenharmony_ci			       i, np->tx_ring[i].length,
15028c2ecf20Sopenharmony_ci			       np->tx_ring[i].status, np->tx_ring[i].buffer1);
15038c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "  Rx ring %p:\n", np->rx_ring);
15048c2ecf20Sopenharmony_ci		for (i = 0; i < RX_RING_SIZE; i++) {
15058c2ecf20Sopenharmony_ci			printk(KERN_DEBUG " #%d desc. %04x %04x %08x\n",
15068c2ecf20Sopenharmony_ci			       i, np->rx_ring[i].length,
15078c2ecf20Sopenharmony_ci			       np->rx_ring[i].status, np->rx_ring[i].buffer1);
15088c2ecf20Sopenharmony_ci		}
15098c2ecf20Sopenharmony_ci	}
15108c2ecf20Sopenharmony_ci#endif /* __i386__ debugging only */
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	del_timer_sync(&np->timer);
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci	free_rxtx_rings(np);
15158c2ecf20Sopenharmony_ci	free_ringdesc(np);
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci	return 0;
15188c2ecf20Sopenharmony_ci}
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_cistatic void w840_remove1(struct pci_dev *pdev)
15218c2ecf20Sopenharmony_ci{
15228c2ecf20Sopenharmony_ci	struct net_device *dev = pci_get_drvdata(pdev);
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_ci	if (dev) {
15258c2ecf20Sopenharmony_ci		struct netdev_private *np = netdev_priv(dev);
15268c2ecf20Sopenharmony_ci		unregister_netdev(dev);
15278c2ecf20Sopenharmony_ci		pci_iounmap(pdev, np->base_addr);
15288c2ecf20Sopenharmony_ci		free_netdev(dev);
15298c2ecf20Sopenharmony_ci	}
15308c2ecf20Sopenharmony_ci}
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci/*
15338c2ecf20Sopenharmony_ci * suspend/resume synchronization:
15348c2ecf20Sopenharmony_ci * - open, close, do_ioctl:
15358c2ecf20Sopenharmony_ci * 	rtnl_lock, & netif_device_detach after the rtnl_unlock.
15368c2ecf20Sopenharmony_ci * - get_stats:
15378c2ecf20Sopenharmony_ci * 	spin_lock_irq(np->lock), doesn't touch hw if not present
15388c2ecf20Sopenharmony_ci * - start_xmit:
15398c2ecf20Sopenharmony_ci * 	synchronize_irq + netif_tx_disable;
15408c2ecf20Sopenharmony_ci * - tx_timeout:
15418c2ecf20Sopenharmony_ci * 	netif_device_detach + netif_tx_disable;
15428c2ecf20Sopenharmony_ci * - set_multicast_list
15438c2ecf20Sopenharmony_ci * 	netif_device_detach + netif_tx_disable;
15448c2ecf20Sopenharmony_ci * - interrupt handler
15458c2ecf20Sopenharmony_ci * 	doesn't touch hw if not present, synchronize_irq waits for
15468c2ecf20Sopenharmony_ci * 	running instances of the interrupt handler.
15478c2ecf20Sopenharmony_ci *
15488c2ecf20Sopenharmony_ci * Disabling hw requires clearing csr6 & IntrEnable.
15498c2ecf20Sopenharmony_ci * update_csr6 & all function that write IntrEnable check netif_device_present
15508c2ecf20Sopenharmony_ci * before settings any bits.
15518c2ecf20Sopenharmony_ci *
15528c2ecf20Sopenharmony_ci * Detach must occur under spin_unlock_irq(), interrupts from a detached
15538c2ecf20Sopenharmony_ci * device would cause an irq storm.
15548c2ecf20Sopenharmony_ci */
15558c2ecf20Sopenharmony_cistatic int __maybe_unused w840_suspend(struct device *dev_d)
15568c2ecf20Sopenharmony_ci{
15578c2ecf20Sopenharmony_ci	struct net_device *dev = dev_get_drvdata(dev_d);
15588c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
15598c2ecf20Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
15608c2ecf20Sopenharmony_ci
15618c2ecf20Sopenharmony_ci	rtnl_lock();
15628c2ecf20Sopenharmony_ci	if (netif_running (dev)) {
15638c2ecf20Sopenharmony_ci		del_timer_sync(&np->timer);
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci		spin_lock_irq(&np->lock);
15668c2ecf20Sopenharmony_ci		netif_device_detach(dev);
15678c2ecf20Sopenharmony_ci		update_csr6(dev, 0);
15688c2ecf20Sopenharmony_ci		iowrite32(0, ioaddr + IntrEnable);
15698c2ecf20Sopenharmony_ci		spin_unlock_irq(&np->lock);
15708c2ecf20Sopenharmony_ci
15718c2ecf20Sopenharmony_ci		synchronize_irq(np->pci_dev->irq);
15728c2ecf20Sopenharmony_ci		netif_tx_disable(dev);
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci		np->stats.rx_missed_errors += ioread32(ioaddr + RxMissed) & 0xffff;
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci		/* no more hardware accesses behind this line. */
15778c2ecf20Sopenharmony_ci
15788c2ecf20Sopenharmony_ci		BUG_ON(np->csr6 || ioread32(ioaddr + IntrEnable));
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci		/* pci_power_off(pdev, -1); */
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci		free_rxtx_rings(np);
15838c2ecf20Sopenharmony_ci	} else {
15848c2ecf20Sopenharmony_ci		netif_device_detach(dev);
15858c2ecf20Sopenharmony_ci	}
15868c2ecf20Sopenharmony_ci	rtnl_unlock();
15878c2ecf20Sopenharmony_ci	return 0;
15888c2ecf20Sopenharmony_ci}
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_cistatic int __maybe_unused w840_resume(struct device *dev_d)
15918c2ecf20Sopenharmony_ci{
15928c2ecf20Sopenharmony_ci	struct net_device *dev = dev_get_drvdata(dev_d);
15938c2ecf20Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci	rtnl_lock();
15968c2ecf20Sopenharmony_ci	if (netif_device_present(dev))
15978c2ecf20Sopenharmony_ci		goto out; /* device not suspended */
15988c2ecf20Sopenharmony_ci	if (netif_running(dev)) {
15998c2ecf20Sopenharmony_ci		spin_lock_irq(&np->lock);
16008c2ecf20Sopenharmony_ci		iowrite32(1, np->base_addr+PCIBusCfg);
16018c2ecf20Sopenharmony_ci		ioread32(np->base_addr+PCIBusCfg);
16028c2ecf20Sopenharmony_ci		udelay(1);
16038c2ecf20Sopenharmony_ci		netif_device_attach(dev);
16048c2ecf20Sopenharmony_ci		init_rxtx_rings(dev);
16058c2ecf20Sopenharmony_ci		init_registers(dev);
16068c2ecf20Sopenharmony_ci		spin_unlock_irq(&np->lock);
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_ci		netif_wake_queue(dev);
16098c2ecf20Sopenharmony_ci
16108c2ecf20Sopenharmony_ci		mod_timer(&np->timer, jiffies + 1*HZ);
16118c2ecf20Sopenharmony_ci	} else {
16128c2ecf20Sopenharmony_ci		netif_device_attach(dev);
16138c2ecf20Sopenharmony_ci	}
16148c2ecf20Sopenharmony_ciout:
16158c2ecf20Sopenharmony_ci	rtnl_unlock();
16168c2ecf20Sopenharmony_ci	return 0;
16178c2ecf20Sopenharmony_ci}
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(w840_pm_ops, w840_suspend, w840_resume);
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_cistatic struct pci_driver w840_driver = {
16228c2ecf20Sopenharmony_ci	.name		= DRV_NAME,
16238c2ecf20Sopenharmony_ci	.id_table	= w840_pci_tbl,
16248c2ecf20Sopenharmony_ci	.probe		= w840_probe1,
16258c2ecf20Sopenharmony_ci	.remove		= w840_remove1,
16268c2ecf20Sopenharmony_ci	.driver.pm	= &w840_pm_ops,
16278c2ecf20Sopenharmony_ci};
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_cistatic int __init w840_init(void)
16308c2ecf20Sopenharmony_ci{
16318c2ecf20Sopenharmony_ci	return pci_register_driver(&w840_driver);
16328c2ecf20Sopenharmony_ci}
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_cistatic void __exit w840_exit(void)
16358c2ecf20Sopenharmony_ci{
16368c2ecf20Sopenharmony_ci	pci_unregister_driver(&w840_driver);
16378c2ecf20Sopenharmony_ci}
16388c2ecf20Sopenharmony_ci
16398c2ecf20Sopenharmony_cimodule_init(w840_init);
16408c2ecf20Sopenharmony_cimodule_exit(w840_exit);
1641