162306a36Sopenharmony_ci/* winbond-840.c: A Linux PCI network adapter device driver. */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci	Written 1998-2001 by Donald Becker.
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci	This software may be used and distributed according to the terms of
662306a36Sopenharmony_ci	the GNU General Public License (GPL), incorporated herein by reference.
762306a36Sopenharmony_ci	Drivers based on or derived from this code fall under the GPL and must
862306a36Sopenharmony_ci	retain the authorship, copyright and license notice.  This file is not
962306a36Sopenharmony_ci	a complete program and may only be used when the entire operating
1062306a36Sopenharmony_ci	system is licensed under the GPL.
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci	The author may be reached as becker@scyld.com, or C/O
1362306a36Sopenharmony_ci	Scyld Computing Corporation
1462306a36Sopenharmony_ci	410 Severn Ave., Suite 210
1562306a36Sopenharmony_ci	Annapolis MD 21403
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci	Support and updates available at
1862306a36Sopenharmony_ci	http://www.scyld.com/network/drivers.html
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	Do not remove the copyright information.
2162306a36Sopenharmony_ci	Do not change the version information unless an improvement has been made.
2262306a36Sopenharmony_ci	Merely removing my name, as Compex has done in the past, does not count
2362306a36Sopenharmony_ci	as an improvement.
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	Changelog:
2662306a36Sopenharmony_ci	* ported to 2.4
2762306a36Sopenharmony_ci		???
2862306a36Sopenharmony_ci	* spin lock update, memory barriers, new style dma mappings
2962306a36Sopenharmony_ci		limit each tx buffer to < 1024 bytes
3062306a36Sopenharmony_ci		remove DescIntr from Rx descriptors (that's an Tx flag)
3162306a36Sopenharmony_ci		remove next pointer from Tx descriptors
3262306a36Sopenharmony_ci		synchronize tx_q_bytes
3362306a36Sopenharmony_ci		software reset in tx_timeout
3462306a36Sopenharmony_ci			Copyright (C) 2000 Manfred Spraul
3562306a36Sopenharmony_ci	* further cleanups
3662306a36Sopenharmony_ci		power management.
3762306a36Sopenharmony_ci		support for big endian descriptors
3862306a36Sopenharmony_ci			Copyright (C) 2001 Manfred Spraul
3962306a36Sopenharmony_ci	* ethtool support (jgarzik)
4062306a36Sopenharmony_ci	* Replace some MII-related magic numbers with constants (jgarzik)
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	TODO:
4362306a36Sopenharmony_ci	* enable pci_power_off
4462306a36Sopenharmony_ci	* Wake-On-LAN
4562306a36Sopenharmony_ci*/
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define DRV_NAME	"winbond-840"
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/* Automatically extracted configuration info:
5262306a36Sopenharmony_ciprobe-func: winbond840_probe
5362306a36Sopenharmony_ciconfig-in: tristate 'Winbond W89c840 Ethernet support' CONFIG_WINBOND_840
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cic-help-name: Winbond W89c840 PCI Ethernet support
5662306a36Sopenharmony_cic-help-symbol: CONFIG_WINBOND_840
5762306a36Sopenharmony_cic-help: This driver is for the Winbond W89c840 chip.  It also works with
5862306a36Sopenharmony_cic-help: the TX9882 chip on the Compex RL100-ATX board.
5962306a36Sopenharmony_cic-help: More specific information and updates are available from
6062306a36Sopenharmony_cic-help: http://www.scyld.com/network/drivers.html
6162306a36Sopenharmony_ci*/
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/* The user-configurable values.
6462306a36Sopenharmony_ci   These may be modified when a driver module is loaded.*/
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic int debug = 1;			/* 1 normal messages, 0 quiet .. 7 verbose. */
6762306a36Sopenharmony_cistatic int max_interrupt_work = 20;
6862306a36Sopenharmony_ci/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
6962306a36Sopenharmony_ci   The '840 uses a 64 element hash table based on the Ethernet CRC.  */
7062306a36Sopenharmony_cistatic int multicast_filter_limit = 32;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
7362306a36Sopenharmony_ci   Setting to > 1518 effectively disables this feature. */
7462306a36Sopenharmony_cistatic int rx_copybreak;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/* Used to pass the media type, etc.
7762306a36Sopenharmony_ci   Both 'options[]' and 'full_duplex[]' should exist for driver
7862306a36Sopenharmony_ci   interoperability.
7962306a36Sopenharmony_ci   The media type is usually passed in 'options[]'.
8062306a36Sopenharmony_ci*/
8162306a36Sopenharmony_ci#define MAX_UNITS 8		/* More are supported, limit only on options */
8262306a36Sopenharmony_cistatic int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
8362306a36Sopenharmony_cistatic int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/* Operational parameters that are set at compile time. */
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/* Keep the ring sizes a power of two for compile efficiency.
8862306a36Sopenharmony_ci   The compiler will convert <unsigned>'%'<2^N> into a bit mask.
8962306a36Sopenharmony_ci   Making the Tx ring too large decreases the effectiveness of channel
9062306a36Sopenharmony_ci   bonding and packet priority.
9162306a36Sopenharmony_ci   There are no ill effects from too-large receive rings. */
9262306a36Sopenharmony_ci#define TX_QUEUE_LEN	10		/* Limit ring entries actually used.  */
9362306a36Sopenharmony_ci#define TX_QUEUE_LEN_RESTART	5
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci#define TX_BUFLIMIT	(1024-128)
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/* The presumed FIFO size for working around the Tx-FIFO-overflow bug.
9862306a36Sopenharmony_ci   To avoid overflowing we don't queue again until we have room for a
9962306a36Sopenharmony_ci   full-size packet.
10062306a36Sopenharmony_ci */
10162306a36Sopenharmony_ci#define TX_FIFO_SIZE (2048)
10262306a36Sopenharmony_ci#define TX_BUG_FIFO_LIMIT (TX_FIFO_SIZE-1514-16)
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci/* Operational parameters that usually are not changed. */
10662306a36Sopenharmony_ci/* Time in jiffies before concluding the transmitter is hung. */
10762306a36Sopenharmony_ci#define TX_TIMEOUT  (2*HZ)
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/* Include files, designed to support most kernel versions 2.0.0 and later. */
11062306a36Sopenharmony_ci#include <linux/module.h>
11162306a36Sopenharmony_ci#include <linux/kernel.h>
11262306a36Sopenharmony_ci#include <linux/string.h>
11362306a36Sopenharmony_ci#include <linux/timer.h>
11462306a36Sopenharmony_ci#include <linux/errno.h>
11562306a36Sopenharmony_ci#include <linux/ioport.h>
11662306a36Sopenharmony_ci#include <linux/interrupt.h>
11762306a36Sopenharmony_ci#include <linux/pci.h>
11862306a36Sopenharmony_ci#include <linux/dma-mapping.h>
11962306a36Sopenharmony_ci#include <linux/netdevice.h>
12062306a36Sopenharmony_ci#include <linux/etherdevice.h>
12162306a36Sopenharmony_ci#include <linux/skbuff.h>
12262306a36Sopenharmony_ci#include <linux/init.h>
12362306a36Sopenharmony_ci#include <linux/delay.h>
12462306a36Sopenharmony_ci#include <linux/ethtool.h>
12562306a36Sopenharmony_ci#include <linux/mii.h>
12662306a36Sopenharmony_ci#include <linux/rtnetlink.h>
12762306a36Sopenharmony_ci#include <linux/crc32.h>
12862306a36Sopenharmony_ci#include <linux/bitops.h>
12962306a36Sopenharmony_ci#include <linux/uaccess.h>
13062306a36Sopenharmony_ci#include <asm/processor.h>		/* Processor type for cache alignment. */
13162306a36Sopenharmony_ci#include <asm/io.h>
13262306a36Sopenharmony_ci#include <asm/irq.h>
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci#include "tulip.h"
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci#undef PKT_BUF_SZ			/* tulip.h also defines this */
13762306a36Sopenharmony_ci#define PKT_BUF_SZ		1536	/* Size of each temporary Rx buffer.*/
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ciMODULE_AUTHOR("Donald Becker <becker@scyld.com>");
14062306a36Sopenharmony_ciMODULE_DESCRIPTION("Winbond W89c840 Ethernet driver");
14162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cimodule_param(max_interrupt_work, int, 0);
14462306a36Sopenharmony_cimodule_param(debug, int, 0);
14562306a36Sopenharmony_cimodule_param(rx_copybreak, int, 0);
14662306a36Sopenharmony_cimodule_param(multicast_filter_limit, int, 0);
14762306a36Sopenharmony_cimodule_param_array(options, int, NULL, 0);
14862306a36Sopenharmony_cimodule_param_array(full_duplex, int, NULL, 0);
14962306a36Sopenharmony_ciMODULE_PARM_DESC(max_interrupt_work, "winbond-840 maximum events handled per interrupt");
15062306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "winbond-840 debug level (0-6)");
15162306a36Sopenharmony_ciMODULE_PARM_DESC(rx_copybreak, "winbond-840 copy breakpoint for copy-only-tiny-frames");
15262306a36Sopenharmony_ciMODULE_PARM_DESC(multicast_filter_limit, "winbond-840 maximum number of filtered multicast addresses");
15362306a36Sopenharmony_ciMODULE_PARM_DESC(options, "winbond-840: Bits 0-3: media type, bit 17: full duplex");
15462306a36Sopenharmony_ciMODULE_PARM_DESC(full_duplex, "winbond-840 full duplex setting(s) (1)");
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/*
15762306a36Sopenharmony_ci				Theory of Operation
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ciI. Board Compatibility
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ciThis driver is for the Winbond w89c840 chip.
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ciII. Board-specific settings
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ciNone.
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ciIII. Driver operation
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ciThis chip is very similar to the Digital 21*4* "Tulip" family.  The first
17062306a36Sopenharmony_citwelve registers and the descriptor format are nearly identical.  Read a
17162306a36Sopenharmony_ciTulip manual for operational details.
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ciA significant difference is that the multicast filter and station address are
17462306a36Sopenharmony_cistored in registers rather than loaded through a pseudo-transmit packet.
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ciUnlike the Tulip, transmit buffers are limited to 1KB.  To transmit a
17762306a36Sopenharmony_cifull-sized packet we must use both data buffers in a descriptor.  Thus the
17862306a36Sopenharmony_cidriver uses ring mode where descriptors are implicitly sequential in memory,
17962306a36Sopenharmony_cirather than using the second descriptor address as a chain pointer to
18062306a36Sopenharmony_cisubsequent descriptors.
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ciIV. Notes
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ciIf you are going to almost clone a Tulip, why not go all the way and avoid
18562306a36Sopenharmony_cithe need for a new driver?
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ciIVb. References
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cihttp://www.scyld.com/expert/100mbps.html
19062306a36Sopenharmony_cihttp://www.scyld.com/expert/NWay.html
19162306a36Sopenharmony_cihttp://www.winbond.com.tw/
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ciIVc. Errata
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ciA horrible bug exists in the transmit FIFO.  Apparently the chip doesn't
19662306a36Sopenharmony_cicorrectly detect a full FIFO, and queuing more than 2048 bytes may result in
19762306a36Sopenharmony_cisilent data corruption.
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ciTest with 'ping -s 10000' on a fast computer.
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci*/
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci/*
20662306a36Sopenharmony_ci  PCI probe table.
20762306a36Sopenharmony_ci*/
20862306a36Sopenharmony_cienum chip_capability_flags {
20962306a36Sopenharmony_ci	CanHaveMII=1, HasBrokenTx=2, AlwaysFDX=4, FDXOnNoMII=8,
21062306a36Sopenharmony_ci};
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic const struct pci_device_id w840_pci_tbl[] = {
21362306a36Sopenharmony_ci	{ 0x1050, 0x0840, PCI_ANY_ID, 0x8153,     0, 0, 0 },
21462306a36Sopenharmony_ci	{ 0x1050, 0x0840, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },
21562306a36Sopenharmony_ci	{ 0x11f6, 0x2011, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 },
21662306a36Sopenharmony_ci	{ }
21762306a36Sopenharmony_ci};
21862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, w840_pci_tbl);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cienum {
22162306a36Sopenharmony_ci	netdev_res_size		= 128,	/* size of PCI BAR resource */
22262306a36Sopenharmony_ci};
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistruct pci_id_info {
22562306a36Sopenharmony_ci        const char *name;
22662306a36Sopenharmony_ci        int drv_flags;		/* Driver use, intended as capability flags. */
22762306a36Sopenharmony_ci};
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic const struct pci_id_info pci_id_tbl[] = {
23062306a36Sopenharmony_ci	{ 				/* Sometime a Level-One switch card. */
23162306a36Sopenharmony_ci	  "Winbond W89c840",	CanHaveMII | HasBrokenTx | FDXOnNoMII},
23262306a36Sopenharmony_ci	{ "Winbond W89c840",	CanHaveMII | HasBrokenTx},
23362306a36Sopenharmony_ci	{ "Compex RL100-ATX",	CanHaveMII | HasBrokenTx},
23462306a36Sopenharmony_ci	{ }	/* terminate list. */
23562306a36Sopenharmony_ci};
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci/* This driver was written to use PCI memory space, however some x86 systems
23862306a36Sopenharmony_ci   work only with I/O space accesses. See CONFIG_TULIP_MMIO in .config
23962306a36Sopenharmony_ci*/
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci/* Offsets to the Command and Status Registers, "CSRs".
24262306a36Sopenharmony_ci   While similar to the Tulip, these registers are longword aligned.
24362306a36Sopenharmony_ci   Note: It's not useful to define symbolic names for every register bit in
24462306a36Sopenharmony_ci   the device.  The name can only partially document the semantics and make
24562306a36Sopenharmony_ci   the driver longer and more difficult to read.
24662306a36Sopenharmony_ci*/
24762306a36Sopenharmony_cienum w840_offsets {
24862306a36Sopenharmony_ci	PCIBusCfg=0x00, TxStartDemand=0x04, RxStartDemand=0x08,
24962306a36Sopenharmony_ci	RxRingPtr=0x0C, TxRingPtr=0x10,
25062306a36Sopenharmony_ci	IntrStatus=0x14, NetworkConfig=0x18, IntrEnable=0x1C,
25162306a36Sopenharmony_ci	RxMissed=0x20, EECtrl=0x24, MIICtrl=0x24, BootRom=0x28, GPTimer=0x2C,
25262306a36Sopenharmony_ci	CurRxDescAddr=0x30, CurRxBufAddr=0x34,			/* Debug use */
25362306a36Sopenharmony_ci	MulticastFilter0=0x38, MulticastFilter1=0x3C, StationAddr=0x40,
25462306a36Sopenharmony_ci	CurTxDescAddr=0x4C, CurTxBufAddr=0x50,
25562306a36Sopenharmony_ci};
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci/* Bits in the NetworkConfig register. */
25862306a36Sopenharmony_cienum rx_mode_bits {
25962306a36Sopenharmony_ci	AcceptErr=0x80,
26062306a36Sopenharmony_ci	RxAcceptBroadcast=0x20, AcceptMulticast=0x10,
26162306a36Sopenharmony_ci	RxAcceptAllPhys=0x08, AcceptMyPhys=0x02,
26262306a36Sopenharmony_ci};
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cienum mii_reg_bits {
26562306a36Sopenharmony_ci	MDIO_ShiftClk=0x10000, MDIO_DataIn=0x80000, MDIO_DataOut=0x20000,
26662306a36Sopenharmony_ci	MDIO_EnbOutput=0x40000, MDIO_EnbIn = 0x00000,
26762306a36Sopenharmony_ci};
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci/* The Tulip Rx and Tx buffer descriptors. */
27062306a36Sopenharmony_cistruct w840_rx_desc {
27162306a36Sopenharmony_ci	s32 status;
27262306a36Sopenharmony_ci	s32 length;
27362306a36Sopenharmony_ci	u32 buffer1;
27462306a36Sopenharmony_ci	u32 buffer2;
27562306a36Sopenharmony_ci};
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistruct w840_tx_desc {
27862306a36Sopenharmony_ci	s32 status;
27962306a36Sopenharmony_ci	s32 length;
28062306a36Sopenharmony_ci	u32 buffer1, buffer2;
28162306a36Sopenharmony_ci};
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci#define MII_CNT		1 /* winbond only supports one MII */
28462306a36Sopenharmony_cistruct netdev_private {
28562306a36Sopenharmony_ci	struct w840_rx_desc *rx_ring;
28662306a36Sopenharmony_ci	dma_addr_t	rx_addr[RX_RING_SIZE];
28762306a36Sopenharmony_ci	struct w840_tx_desc *tx_ring;
28862306a36Sopenharmony_ci	dma_addr_t	tx_addr[TX_RING_SIZE];
28962306a36Sopenharmony_ci	dma_addr_t ring_dma_addr;
29062306a36Sopenharmony_ci	/* The addresses of receive-in-place skbuffs. */
29162306a36Sopenharmony_ci	struct sk_buff* rx_skbuff[RX_RING_SIZE];
29262306a36Sopenharmony_ci	/* The saved address of a sent-in-place packet/buffer, for later free(). */
29362306a36Sopenharmony_ci	struct sk_buff* tx_skbuff[TX_RING_SIZE];
29462306a36Sopenharmony_ci	struct net_device_stats stats;
29562306a36Sopenharmony_ci	struct timer_list timer;	/* Media monitoring timer. */
29662306a36Sopenharmony_ci	/* Frequently used values: keep some adjacent for cache effect. */
29762306a36Sopenharmony_ci	spinlock_t lock;
29862306a36Sopenharmony_ci	int chip_id, drv_flags;
29962306a36Sopenharmony_ci	struct pci_dev *pci_dev;
30062306a36Sopenharmony_ci	int csr6;
30162306a36Sopenharmony_ci	struct w840_rx_desc *rx_head_desc;
30262306a36Sopenharmony_ci	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */
30362306a36Sopenharmony_ci	unsigned int rx_buf_sz;				/* Based on MTU+slack. */
30462306a36Sopenharmony_ci	unsigned int cur_tx, dirty_tx;
30562306a36Sopenharmony_ci	unsigned int tx_q_bytes;
30662306a36Sopenharmony_ci	unsigned int tx_full;				/* The Tx queue is full. */
30762306a36Sopenharmony_ci	/* MII transceiver section. */
30862306a36Sopenharmony_ci	int mii_cnt;						/* MII device addresses. */
30962306a36Sopenharmony_ci	unsigned char phys[MII_CNT];		/* MII device addresses, but only the first is used */
31062306a36Sopenharmony_ci	u32 mii;
31162306a36Sopenharmony_ci	struct mii_if_info mii_if;
31262306a36Sopenharmony_ci	void __iomem *base_addr;
31362306a36Sopenharmony_ci};
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic int  eeprom_read(void __iomem *ioaddr, int location);
31662306a36Sopenharmony_cistatic int  mdio_read(struct net_device *dev, int phy_id, int location);
31762306a36Sopenharmony_cistatic void mdio_write(struct net_device *dev, int phy_id, int location, int value);
31862306a36Sopenharmony_cistatic int  netdev_open(struct net_device *dev);
31962306a36Sopenharmony_cistatic int  update_link(struct net_device *dev);
32062306a36Sopenharmony_cistatic void netdev_timer(struct timer_list *t);
32162306a36Sopenharmony_cistatic void init_rxtx_rings(struct net_device *dev);
32262306a36Sopenharmony_cistatic void free_rxtx_rings(struct netdev_private *np);
32362306a36Sopenharmony_cistatic void init_registers(struct net_device *dev);
32462306a36Sopenharmony_cistatic void tx_timeout(struct net_device *dev, unsigned int txqueue);
32562306a36Sopenharmony_cistatic int alloc_ringdesc(struct net_device *dev);
32662306a36Sopenharmony_cistatic void free_ringdesc(struct netdev_private *np);
32762306a36Sopenharmony_cistatic netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev);
32862306a36Sopenharmony_cistatic irqreturn_t intr_handler(int irq, void *dev_instance);
32962306a36Sopenharmony_cistatic void netdev_error(struct net_device *dev, int intr_status);
33062306a36Sopenharmony_cistatic int  netdev_rx(struct net_device *dev);
33162306a36Sopenharmony_cistatic u32 __set_rx_mode(struct net_device *dev);
33262306a36Sopenharmony_cistatic void set_rx_mode(struct net_device *dev);
33362306a36Sopenharmony_cistatic struct net_device_stats *get_stats(struct net_device *dev);
33462306a36Sopenharmony_cistatic int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
33562306a36Sopenharmony_cistatic const struct ethtool_ops netdev_ethtool_ops;
33662306a36Sopenharmony_cistatic int  netdev_close(struct net_device *dev);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic const struct net_device_ops netdev_ops = {
33962306a36Sopenharmony_ci	.ndo_open		= netdev_open,
34062306a36Sopenharmony_ci	.ndo_stop		= netdev_close,
34162306a36Sopenharmony_ci	.ndo_start_xmit		= start_tx,
34262306a36Sopenharmony_ci	.ndo_get_stats		= get_stats,
34362306a36Sopenharmony_ci	.ndo_set_rx_mode	= set_rx_mode,
34462306a36Sopenharmony_ci	.ndo_eth_ioctl		= netdev_ioctl,
34562306a36Sopenharmony_ci	.ndo_tx_timeout		= tx_timeout,
34662306a36Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
34762306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
34862306a36Sopenharmony_ci};
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic int w840_probe1(struct pci_dev *pdev, const struct pci_device_id *ent)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	struct net_device *dev;
35362306a36Sopenharmony_ci	struct netdev_private *np;
35462306a36Sopenharmony_ci	static int find_cnt;
35562306a36Sopenharmony_ci	int chip_idx = ent->driver_data;
35662306a36Sopenharmony_ci	int irq;
35762306a36Sopenharmony_ci	int i, option = find_cnt < MAX_UNITS ? options[find_cnt] : 0;
35862306a36Sopenharmony_ci	__le16 addr[ETH_ALEN / 2];
35962306a36Sopenharmony_ci	void __iomem *ioaddr;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	i = pcim_enable_device(pdev);
36262306a36Sopenharmony_ci	if (i) return i;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	pci_set_master(pdev);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	irq = pdev->irq;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) {
36962306a36Sopenharmony_ci		pr_warn("Device %s disabled due to DMA limitations\n",
37062306a36Sopenharmony_ci			pci_name(pdev));
37162306a36Sopenharmony_ci		return -EIO;
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof(*np));
37462306a36Sopenharmony_ci	if (!dev)
37562306a36Sopenharmony_ci		return -ENOMEM;
37662306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, &pdev->dev);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	if (pci_request_regions(pdev, DRV_NAME))
37962306a36Sopenharmony_ci		goto err_out_netdev;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	ioaddr = pci_iomap(pdev, TULIP_BAR, netdev_res_size);
38262306a36Sopenharmony_ci	if (!ioaddr)
38362306a36Sopenharmony_ci		goto err_out_netdev;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	for (i = 0; i < 3; i++)
38662306a36Sopenharmony_ci		addr[i] = cpu_to_le16(eeprom_read(ioaddr, i));
38762306a36Sopenharmony_ci	eth_hw_addr_set(dev, (u8 *)addr);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/* Reset the chip to erase previous misconfiguration.
39062306a36Sopenharmony_ci	   No hold time required! */
39162306a36Sopenharmony_ci	iowrite32(0x00000001, ioaddr + PCIBusCfg);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	np = netdev_priv(dev);
39462306a36Sopenharmony_ci	np->pci_dev = pdev;
39562306a36Sopenharmony_ci	np->chip_id = chip_idx;
39662306a36Sopenharmony_ci	np->drv_flags = pci_id_tbl[chip_idx].drv_flags;
39762306a36Sopenharmony_ci	spin_lock_init(&np->lock);
39862306a36Sopenharmony_ci	np->mii_if.dev = dev;
39962306a36Sopenharmony_ci	np->mii_if.mdio_read = mdio_read;
40062306a36Sopenharmony_ci	np->mii_if.mdio_write = mdio_write;
40162306a36Sopenharmony_ci	np->base_addr = ioaddr;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	pci_set_drvdata(pdev, dev);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (dev->mem_start)
40662306a36Sopenharmony_ci		option = dev->mem_start;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	/* The lower four bits are the media type. */
40962306a36Sopenharmony_ci	if (option > 0) {
41062306a36Sopenharmony_ci		if (option & 0x200)
41162306a36Sopenharmony_ci			np->mii_if.full_duplex = 1;
41262306a36Sopenharmony_ci		if (option & 15)
41362306a36Sopenharmony_ci			dev_info(&dev->dev,
41462306a36Sopenharmony_ci				 "ignoring user supplied media type %d",
41562306a36Sopenharmony_ci				 option & 15);
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci	if (find_cnt < MAX_UNITS  &&  full_duplex[find_cnt] > 0)
41862306a36Sopenharmony_ci		np->mii_if.full_duplex = 1;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	if (np->mii_if.full_duplex)
42162306a36Sopenharmony_ci		np->mii_if.force_media = 1;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	/* The chip-specific entries in the device structure. */
42462306a36Sopenharmony_ci	dev->netdev_ops = &netdev_ops;
42562306a36Sopenharmony_ci	dev->ethtool_ops = &netdev_ethtool_ops;
42662306a36Sopenharmony_ci	dev->watchdog_timeo = TX_TIMEOUT;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	i = register_netdev(dev);
42962306a36Sopenharmony_ci	if (i)
43062306a36Sopenharmony_ci		goto err_out_cleardev;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	dev_info(&dev->dev, "%s at %p, %pM, IRQ %d\n",
43362306a36Sopenharmony_ci		 pci_id_tbl[chip_idx].name, ioaddr, dev->dev_addr, irq);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (np->drv_flags & CanHaveMII) {
43662306a36Sopenharmony_ci		int phy, phy_idx = 0;
43762306a36Sopenharmony_ci		for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) {
43862306a36Sopenharmony_ci			int mii_status = mdio_read(dev, phy, MII_BMSR);
43962306a36Sopenharmony_ci			if (mii_status != 0xffff  &&  mii_status != 0x0000) {
44062306a36Sopenharmony_ci				np->phys[phy_idx++] = phy;
44162306a36Sopenharmony_ci				np->mii_if.advertising = mdio_read(dev, phy, MII_ADVERTISE);
44262306a36Sopenharmony_ci				np->mii = (mdio_read(dev, phy, MII_PHYSID1) << 16)+
44362306a36Sopenharmony_ci						mdio_read(dev, phy, MII_PHYSID2);
44462306a36Sopenharmony_ci				dev_info(&dev->dev,
44562306a36Sopenharmony_ci					 "MII PHY %08xh found at address %d, status 0x%04x advertising %04x\n",
44662306a36Sopenharmony_ci					 np->mii, phy, mii_status,
44762306a36Sopenharmony_ci					 np->mii_if.advertising);
44862306a36Sopenharmony_ci			}
44962306a36Sopenharmony_ci		}
45062306a36Sopenharmony_ci		np->mii_cnt = phy_idx;
45162306a36Sopenharmony_ci		np->mii_if.phy_id = np->phys[0];
45262306a36Sopenharmony_ci		if (phy_idx == 0) {
45362306a36Sopenharmony_ci			dev_warn(&dev->dev,
45462306a36Sopenharmony_ci				 "MII PHY not found -- this device may not operate correctly\n");
45562306a36Sopenharmony_ci		}
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	find_cnt++;
45962306a36Sopenharmony_ci	return 0;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cierr_out_cleardev:
46262306a36Sopenharmony_ci	pci_iounmap(pdev, ioaddr);
46362306a36Sopenharmony_cierr_out_netdev:
46462306a36Sopenharmony_ci	free_netdev (dev);
46562306a36Sopenharmony_ci	return -ENODEV;
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces.  These are
47062306a36Sopenharmony_ci   often serial bit streams generated by the host processor.
47162306a36Sopenharmony_ci   The example below is for the common 93c46 EEPROM, 64 16 bit words. */
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci/* Delay between EEPROM clock transitions.
47462306a36Sopenharmony_ci   No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need
47562306a36Sopenharmony_ci   a delay.  Note that pre-2.0.34 kernels had a cache-alignment bug that
47662306a36Sopenharmony_ci   made udelay() unreliable.
47762306a36Sopenharmony_ci*/
47862306a36Sopenharmony_ci#define eeprom_delay(ee_addr)	ioread32(ee_addr)
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cienum EEPROM_Ctrl_Bits {
48162306a36Sopenharmony_ci	EE_ShiftClk=0x02, EE_Write0=0x801, EE_Write1=0x805,
48262306a36Sopenharmony_ci	EE_ChipSelect=0x801, EE_DataIn=0x08,
48362306a36Sopenharmony_ci};
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci/* The EEPROM commands include the alway-set leading bit. */
48662306a36Sopenharmony_cienum EEPROM_Cmds {
48762306a36Sopenharmony_ci	EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6),
48862306a36Sopenharmony_ci};
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic int eeprom_read(void __iomem *addr, int location)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	int i;
49362306a36Sopenharmony_ci	int retval = 0;
49462306a36Sopenharmony_ci	void __iomem *ee_addr = addr + EECtrl;
49562306a36Sopenharmony_ci	int read_cmd = location | EE_ReadCmd;
49662306a36Sopenharmony_ci	iowrite32(EE_ChipSelect, ee_addr);
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	/* Shift the read command bits out. */
49962306a36Sopenharmony_ci	for (i = 10; i >= 0; i--) {
50062306a36Sopenharmony_ci		short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0;
50162306a36Sopenharmony_ci		iowrite32(dataval, ee_addr);
50262306a36Sopenharmony_ci		eeprom_delay(ee_addr);
50362306a36Sopenharmony_ci		iowrite32(dataval | EE_ShiftClk, ee_addr);
50462306a36Sopenharmony_ci		eeprom_delay(ee_addr);
50562306a36Sopenharmony_ci	}
50662306a36Sopenharmony_ci	iowrite32(EE_ChipSelect, ee_addr);
50762306a36Sopenharmony_ci	eeprom_delay(ee_addr);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	for (i = 16; i > 0; i--) {
51062306a36Sopenharmony_ci		iowrite32(EE_ChipSelect | EE_ShiftClk, ee_addr);
51162306a36Sopenharmony_ci		eeprom_delay(ee_addr);
51262306a36Sopenharmony_ci		retval = (retval << 1) | ((ioread32(ee_addr) & EE_DataIn) ? 1 : 0);
51362306a36Sopenharmony_ci		iowrite32(EE_ChipSelect, ee_addr);
51462306a36Sopenharmony_ci		eeprom_delay(ee_addr);
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	/* Terminate the EEPROM access. */
51862306a36Sopenharmony_ci	iowrite32(0, ee_addr);
51962306a36Sopenharmony_ci	return retval;
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci/*  MII transceiver control section.
52362306a36Sopenharmony_ci	Read and write the MII registers using software-generated serial
52462306a36Sopenharmony_ci	MDIO protocol.  See the MII specifications or DP83840A data sheet
52562306a36Sopenharmony_ci	for details.
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	The maximum data clock rate is 2.5 Mhz.  The minimum timing is usually
52862306a36Sopenharmony_ci	met by back-to-back 33Mhz PCI cycles. */
52962306a36Sopenharmony_ci#define mdio_delay(mdio_addr) ioread32(mdio_addr)
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci/* Set iff a MII transceiver on any interface requires mdio preamble.
53262306a36Sopenharmony_ci   This only set with older transceivers, so the extra
53362306a36Sopenharmony_ci   code size of a per-interface flag is not worthwhile. */
53462306a36Sopenharmony_cistatic char mii_preamble_required = 1;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci#define MDIO_WRITE0 (MDIO_EnbOutput)
53762306a36Sopenharmony_ci#define MDIO_WRITE1 (MDIO_DataOut | MDIO_EnbOutput)
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci/* Generate the preamble required for initial synchronization and
54062306a36Sopenharmony_ci   a few older transceivers. */
54162306a36Sopenharmony_cistatic void mdio_sync(void __iomem *mdio_addr)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	int bits = 32;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	/* Establish sync by sending at least 32 logic ones. */
54662306a36Sopenharmony_ci	while (--bits >= 0) {
54762306a36Sopenharmony_ci		iowrite32(MDIO_WRITE1, mdio_addr);
54862306a36Sopenharmony_ci		mdio_delay(mdio_addr);
54962306a36Sopenharmony_ci		iowrite32(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr);
55062306a36Sopenharmony_ci		mdio_delay(mdio_addr);
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_cistatic int mdio_read(struct net_device *dev, int phy_id, int location)
55562306a36Sopenharmony_ci{
55662306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
55762306a36Sopenharmony_ci	void __iomem *mdio_addr = np->base_addr + MIICtrl;
55862306a36Sopenharmony_ci	int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location;
55962306a36Sopenharmony_ci	int i, retval = 0;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	if (mii_preamble_required)
56262306a36Sopenharmony_ci		mdio_sync(mdio_addr);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	/* Shift the read command bits out. */
56562306a36Sopenharmony_ci	for (i = 15; i >= 0; i--) {
56662306a36Sopenharmony_ci		int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci		iowrite32(dataval, mdio_addr);
56962306a36Sopenharmony_ci		mdio_delay(mdio_addr);
57062306a36Sopenharmony_ci		iowrite32(dataval | MDIO_ShiftClk, mdio_addr);
57162306a36Sopenharmony_ci		mdio_delay(mdio_addr);
57262306a36Sopenharmony_ci	}
57362306a36Sopenharmony_ci	/* Read the two transition, 16 data, and wire-idle bits. */
57462306a36Sopenharmony_ci	for (i = 20; i > 0; i--) {
57562306a36Sopenharmony_ci		iowrite32(MDIO_EnbIn, mdio_addr);
57662306a36Sopenharmony_ci		mdio_delay(mdio_addr);
57762306a36Sopenharmony_ci		retval = (retval << 1) | ((ioread32(mdio_addr) & MDIO_DataIn) ? 1 : 0);
57862306a36Sopenharmony_ci		iowrite32(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
57962306a36Sopenharmony_ci		mdio_delay(mdio_addr);
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci	return (retval>>1) & 0xffff;
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic void mdio_write(struct net_device *dev, int phy_id, int location, int value)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
58762306a36Sopenharmony_ci	void __iomem *mdio_addr = np->base_addr + MIICtrl;
58862306a36Sopenharmony_ci	int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
58962306a36Sopenharmony_ci	int i;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	if (location == 4  &&  phy_id == np->phys[0])
59262306a36Sopenharmony_ci		np->mii_if.advertising = value;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	if (mii_preamble_required)
59562306a36Sopenharmony_ci		mdio_sync(mdio_addr);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	/* Shift the command bits out. */
59862306a36Sopenharmony_ci	for (i = 31; i >= 0; i--) {
59962306a36Sopenharmony_ci		int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci		iowrite32(dataval, mdio_addr);
60262306a36Sopenharmony_ci		mdio_delay(mdio_addr);
60362306a36Sopenharmony_ci		iowrite32(dataval | MDIO_ShiftClk, mdio_addr);
60462306a36Sopenharmony_ci		mdio_delay(mdio_addr);
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci	/* Clear out extra bits. */
60762306a36Sopenharmony_ci	for (i = 2; i > 0; i--) {
60862306a36Sopenharmony_ci		iowrite32(MDIO_EnbIn, mdio_addr);
60962306a36Sopenharmony_ci		mdio_delay(mdio_addr);
61062306a36Sopenharmony_ci		iowrite32(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr);
61162306a36Sopenharmony_ci		mdio_delay(mdio_addr);
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_cistatic int netdev_open(struct net_device *dev)
61762306a36Sopenharmony_ci{
61862306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
61962306a36Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
62062306a36Sopenharmony_ci	const int irq = np->pci_dev->irq;
62162306a36Sopenharmony_ci	int i;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	iowrite32(0x00000001, ioaddr + PCIBusCfg);		/* Reset */
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	netif_device_detach(dev);
62662306a36Sopenharmony_ci	i = request_irq(irq, intr_handler, IRQF_SHARED, dev->name, dev);
62762306a36Sopenharmony_ci	if (i)
62862306a36Sopenharmony_ci		goto out_err;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	if (debug > 1)
63162306a36Sopenharmony_ci		netdev_dbg(dev, "%s() irq %d\n", __func__, irq);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	i = alloc_ringdesc(dev);
63462306a36Sopenharmony_ci	if (i)
63562306a36Sopenharmony_ci		goto out_err;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	spin_lock_irq(&np->lock);
63862306a36Sopenharmony_ci	netif_device_attach(dev);
63962306a36Sopenharmony_ci	init_registers(dev);
64062306a36Sopenharmony_ci	spin_unlock_irq(&np->lock);
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	netif_start_queue(dev);
64362306a36Sopenharmony_ci	if (debug > 2)
64462306a36Sopenharmony_ci		netdev_dbg(dev, "Done %s()\n", __func__);
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	/* Set the timer to check for link beat. */
64762306a36Sopenharmony_ci	timer_setup(&np->timer, netdev_timer, 0);
64862306a36Sopenharmony_ci	np->timer.expires = jiffies + 1*HZ;
64962306a36Sopenharmony_ci	add_timer(&np->timer);
65062306a36Sopenharmony_ci	return 0;
65162306a36Sopenharmony_ciout_err:
65262306a36Sopenharmony_ci	netif_device_attach(dev);
65362306a36Sopenharmony_ci	return i;
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci#define MII_DAVICOM_DM9101	0x0181b800
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_cistatic int update_link(struct net_device *dev)
65962306a36Sopenharmony_ci{
66062306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
66162306a36Sopenharmony_ci	int duplex, fasteth, result, mii_reg;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	/* BSMR */
66462306a36Sopenharmony_ci	mii_reg = mdio_read(dev, np->phys[0], MII_BMSR);
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	if (mii_reg == 0xffff)
66762306a36Sopenharmony_ci		return np->csr6;
66862306a36Sopenharmony_ci	/* reread: the link status bit is sticky */
66962306a36Sopenharmony_ci	mii_reg = mdio_read(dev, np->phys[0], MII_BMSR);
67062306a36Sopenharmony_ci	if (!(mii_reg & 0x4)) {
67162306a36Sopenharmony_ci		if (netif_carrier_ok(dev)) {
67262306a36Sopenharmony_ci			if (debug)
67362306a36Sopenharmony_ci				dev_info(&dev->dev,
67462306a36Sopenharmony_ci					 "MII #%d reports no link. Disabling watchdog\n",
67562306a36Sopenharmony_ci					 np->phys[0]);
67662306a36Sopenharmony_ci			netif_carrier_off(dev);
67762306a36Sopenharmony_ci		}
67862306a36Sopenharmony_ci		return np->csr6;
67962306a36Sopenharmony_ci	}
68062306a36Sopenharmony_ci	if (!netif_carrier_ok(dev)) {
68162306a36Sopenharmony_ci		if (debug)
68262306a36Sopenharmony_ci			dev_info(&dev->dev,
68362306a36Sopenharmony_ci				 "MII #%d link is back. Enabling watchdog\n",
68462306a36Sopenharmony_ci				 np->phys[0]);
68562306a36Sopenharmony_ci		netif_carrier_on(dev);
68662306a36Sopenharmony_ci	}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	if ((np->mii & ~0xf) == MII_DAVICOM_DM9101) {
68962306a36Sopenharmony_ci		/* If the link partner doesn't support autonegotiation
69062306a36Sopenharmony_ci		 * the MII detects it's abilities with the "parallel detection".
69162306a36Sopenharmony_ci		 * Some MIIs update the LPA register to the result of the parallel
69262306a36Sopenharmony_ci		 * detection, some don't.
69362306a36Sopenharmony_ci		 * The Davicom PHY [at least 0181b800] doesn't.
69462306a36Sopenharmony_ci		 * Instead bit 9 and 13 of the BMCR are updated to the result
69562306a36Sopenharmony_ci		 * of the negotiation..
69662306a36Sopenharmony_ci		 */
69762306a36Sopenharmony_ci		mii_reg = mdio_read(dev, np->phys[0], MII_BMCR);
69862306a36Sopenharmony_ci		duplex = mii_reg & BMCR_FULLDPLX;
69962306a36Sopenharmony_ci		fasteth = mii_reg & BMCR_SPEED100;
70062306a36Sopenharmony_ci	} else {
70162306a36Sopenharmony_ci		int negotiated;
70262306a36Sopenharmony_ci		mii_reg	= mdio_read(dev, np->phys[0], MII_LPA);
70362306a36Sopenharmony_ci		negotiated = mii_reg & np->mii_if.advertising;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci		duplex = (negotiated & LPA_100FULL) || ((negotiated & 0x02C0) == LPA_10FULL);
70662306a36Sopenharmony_ci		fasteth = negotiated & 0x380;
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci	duplex |= np->mii_if.force_media;
70962306a36Sopenharmony_ci	/* remove fastether and fullduplex */
71062306a36Sopenharmony_ci	result = np->csr6 & ~0x20000200;
71162306a36Sopenharmony_ci	if (duplex)
71262306a36Sopenharmony_ci		result |= 0x200;
71362306a36Sopenharmony_ci	if (fasteth)
71462306a36Sopenharmony_ci		result |= 0x20000000;
71562306a36Sopenharmony_ci	if (result != np->csr6 && debug)
71662306a36Sopenharmony_ci		dev_info(&dev->dev,
71762306a36Sopenharmony_ci			 "Setting %dMBit-%s-duplex based on MII#%d\n",
71862306a36Sopenharmony_ci			 fasteth ? 100 : 10, duplex ? "full" : "half",
71962306a36Sopenharmony_ci			 np->phys[0]);
72062306a36Sopenharmony_ci	return result;
72162306a36Sopenharmony_ci}
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci#define RXTX_TIMEOUT	2000
72462306a36Sopenharmony_cistatic inline void update_csr6(struct net_device *dev, int new)
72562306a36Sopenharmony_ci{
72662306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
72762306a36Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
72862306a36Sopenharmony_ci	int limit = RXTX_TIMEOUT;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	if (!netif_device_present(dev))
73162306a36Sopenharmony_ci		new = 0;
73262306a36Sopenharmony_ci	if (new==np->csr6)
73362306a36Sopenharmony_ci		return;
73462306a36Sopenharmony_ci	/* stop both Tx and Rx processes */
73562306a36Sopenharmony_ci	iowrite32(np->csr6 & ~0x2002, ioaddr + NetworkConfig);
73662306a36Sopenharmony_ci	/* wait until they have really stopped */
73762306a36Sopenharmony_ci	for (;;) {
73862306a36Sopenharmony_ci		int csr5 = ioread32(ioaddr + IntrStatus);
73962306a36Sopenharmony_ci		int t;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci		t = (csr5 >> 17) & 0x07;
74262306a36Sopenharmony_ci		if (t==0||t==1) {
74362306a36Sopenharmony_ci			/* rx stopped */
74462306a36Sopenharmony_ci			t = (csr5 >> 20) & 0x07;
74562306a36Sopenharmony_ci			if (t==0||t==1)
74662306a36Sopenharmony_ci				break;
74762306a36Sopenharmony_ci		}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci		limit--;
75062306a36Sopenharmony_ci		if(!limit) {
75162306a36Sopenharmony_ci			dev_info(&dev->dev,
75262306a36Sopenharmony_ci				 "couldn't stop rxtx, IntrStatus %xh\n", csr5);
75362306a36Sopenharmony_ci			break;
75462306a36Sopenharmony_ci		}
75562306a36Sopenharmony_ci		udelay(1);
75662306a36Sopenharmony_ci	}
75762306a36Sopenharmony_ci	np->csr6 = new;
75862306a36Sopenharmony_ci	/* and restart them with the new configuration */
75962306a36Sopenharmony_ci	iowrite32(np->csr6, ioaddr + NetworkConfig);
76062306a36Sopenharmony_ci	if (new & 0x200)
76162306a36Sopenharmony_ci		np->mii_if.full_duplex = 1;
76262306a36Sopenharmony_ci}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_cistatic void netdev_timer(struct timer_list *t)
76562306a36Sopenharmony_ci{
76662306a36Sopenharmony_ci	struct netdev_private *np = from_timer(np, t, timer);
76762306a36Sopenharmony_ci	struct net_device *dev = pci_get_drvdata(np->pci_dev);
76862306a36Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	if (debug > 2)
77162306a36Sopenharmony_ci		netdev_dbg(dev, "Media selection timer tick, status %08x config %08x\n",
77262306a36Sopenharmony_ci			   ioread32(ioaddr + IntrStatus),
77362306a36Sopenharmony_ci			   ioread32(ioaddr + NetworkConfig));
77462306a36Sopenharmony_ci	spin_lock_irq(&np->lock);
77562306a36Sopenharmony_ci	update_csr6(dev, update_link(dev));
77662306a36Sopenharmony_ci	spin_unlock_irq(&np->lock);
77762306a36Sopenharmony_ci	np->timer.expires = jiffies + 10*HZ;
77862306a36Sopenharmony_ci	add_timer(&np->timer);
77962306a36Sopenharmony_ci}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_cistatic void init_rxtx_rings(struct net_device *dev)
78262306a36Sopenharmony_ci{
78362306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
78462306a36Sopenharmony_ci	int i;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	np->rx_head_desc = &np->rx_ring[0];
78762306a36Sopenharmony_ci	np->tx_ring = (struct w840_tx_desc*)&np->rx_ring[RX_RING_SIZE];
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	/* Initial all Rx descriptors. */
79062306a36Sopenharmony_ci	for (i = 0; i < RX_RING_SIZE; i++) {
79162306a36Sopenharmony_ci		np->rx_ring[i].length = np->rx_buf_sz;
79262306a36Sopenharmony_ci		np->rx_ring[i].status = 0;
79362306a36Sopenharmony_ci		np->rx_skbuff[i] = NULL;
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci	/* Mark the last entry as wrapping the ring. */
79662306a36Sopenharmony_ci	np->rx_ring[i-1].length |= DescEndRing;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	/* Fill in the Rx buffers.  Handle allocation failure gracefully. */
79962306a36Sopenharmony_ci	for (i = 0; i < RX_RING_SIZE; i++) {
80062306a36Sopenharmony_ci		struct sk_buff *skb = netdev_alloc_skb(dev, np->rx_buf_sz);
80162306a36Sopenharmony_ci		np->rx_skbuff[i] = skb;
80262306a36Sopenharmony_ci		if (skb == NULL)
80362306a36Sopenharmony_ci			break;
80462306a36Sopenharmony_ci		np->rx_addr[i] = dma_map_single(&np->pci_dev->dev, skb->data,
80562306a36Sopenharmony_ci						np->rx_buf_sz,
80662306a36Sopenharmony_ci						DMA_FROM_DEVICE);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci		np->rx_ring[i].buffer1 = np->rx_addr[i];
80962306a36Sopenharmony_ci		np->rx_ring[i].status = DescOwned;
81062306a36Sopenharmony_ci	}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	np->cur_rx = 0;
81362306a36Sopenharmony_ci	np->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	/* Initialize the Tx descriptors */
81662306a36Sopenharmony_ci	for (i = 0; i < TX_RING_SIZE; i++) {
81762306a36Sopenharmony_ci		np->tx_skbuff[i] = NULL;
81862306a36Sopenharmony_ci		np->tx_ring[i].status = 0;
81962306a36Sopenharmony_ci	}
82062306a36Sopenharmony_ci	np->tx_full = 0;
82162306a36Sopenharmony_ci	np->tx_q_bytes = np->dirty_tx = np->cur_tx = 0;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	iowrite32(np->ring_dma_addr, np->base_addr + RxRingPtr);
82462306a36Sopenharmony_ci	iowrite32(np->ring_dma_addr+sizeof(struct w840_rx_desc)*RX_RING_SIZE,
82562306a36Sopenharmony_ci		np->base_addr + TxRingPtr);
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_cistatic void free_rxtx_rings(struct netdev_private* np)
83062306a36Sopenharmony_ci{
83162306a36Sopenharmony_ci	int i;
83262306a36Sopenharmony_ci	/* Free all the skbuffs in the Rx queue. */
83362306a36Sopenharmony_ci	for (i = 0; i < RX_RING_SIZE; i++) {
83462306a36Sopenharmony_ci		np->rx_ring[i].status = 0;
83562306a36Sopenharmony_ci		if (np->rx_skbuff[i]) {
83662306a36Sopenharmony_ci			dma_unmap_single(&np->pci_dev->dev, np->rx_addr[i],
83762306a36Sopenharmony_ci					 np->rx_skbuff[i]->len,
83862306a36Sopenharmony_ci					 DMA_FROM_DEVICE);
83962306a36Sopenharmony_ci			dev_kfree_skb(np->rx_skbuff[i]);
84062306a36Sopenharmony_ci		}
84162306a36Sopenharmony_ci		np->rx_skbuff[i] = NULL;
84262306a36Sopenharmony_ci	}
84362306a36Sopenharmony_ci	for (i = 0; i < TX_RING_SIZE; i++) {
84462306a36Sopenharmony_ci		if (np->tx_skbuff[i]) {
84562306a36Sopenharmony_ci			dma_unmap_single(&np->pci_dev->dev, np->tx_addr[i],
84662306a36Sopenharmony_ci					 np->tx_skbuff[i]->len, DMA_TO_DEVICE);
84762306a36Sopenharmony_ci			dev_kfree_skb(np->tx_skbuff[i]);
84862306a36Sopenharmony_ci		}
84962306a36Sopenharmony_ci		np->tx_skbuff[i] = NULL;
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_cistatic void init_registers(struct net_device *dev)
85462306a36Sopenharmony_ci{
85562306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
85662306a36Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
85762306a36Sopenharmony_ci	int i;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	for (i = 0; i < 6; i++)
86062306a36Sopenharmony_ci		iowrite8(dev->dev_addr[i], ioaddr + StationAddr + i);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	/* Initialize other registers. */
86362306a36Sopenharmony_ci#ifdef __BIG_ENDIAN
86462306a36Sopenharmony_ci	i = (1<<20);	/* Big-endian descriptors */
86562306a36Sopenharmony_ci#else
86662306a36Sopenharmony_ci	i = 0;
86762306a36Sopenharmony_ci#endif
86862306a36Sopenharmony_ci	i |= (0x04<<2);		/* skip length 4 u32 */
86962306a36Sopenharmony_ci	i |= 0x02;		/* give Rx priority */
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	/* Configure the PCI bus bursts and FIFO thresholds.
87262306a36Sopenharmony_ci	   486: Set 8 longword cache alignment, 8 longword burst.
87362306a36Sopenharmony_ci	   586: Set 16 longword cache alignment, no burst limit.
87462306a36Sopenharmony_ci	   Cache alignment bits 15:14	     Burst length 13:8
87562306a36Sopenharmony_ci		0000	<not allowed> 		0000 align to cache	0800 8 longwords
87662306a36Sopenharmony_ci		4000	8  longwords		0100 1 longword		1000 16 longwords
87762306a36Sopenharmony_ci		8000	16 longwords		0200 2 longwords	2000 32 longwords
87862306a36Sopenharmony_ci		C000	32  longwords		0400 4 longwords */
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci#if defined (__i386__) && !defined(MODULE) && !defined(CONFIG_UML)
88162306a36Sopenharmony_ci	/* When not a module we can work around broken '486 PCI boards. */
88262306a36Sopenharmony_ci	if (boot_cpu_data.x86 <= 4) {
88362306a36Sopenharmony_ci		i |= 0x4800;
88462306a36Sopenharmony_ci		dev_info(&dev->dev,
88562306a36Sopenharmony_ci			 "This is a 386/486 PCI system, setting cache alignment to 8 longwords\n");
88662306a36Sopenharmony_ci	} else {
88762306a36Sopenharmony_ci		i |= 0xE000;
88862306a36Sopenharmony_ci	}
88962306a36Sopenharmony_ci#elif defined(__powerpc__) || defined(__i386__) || defined(__alpha__) || defined(__ia64__) || defined(__x86_64__)
89062306a36Sopenharmony_ci	i |= 0xE000;
89162306a36Sopenharmony_ci#elif defined(CONFIG_SPARC) || defined (CONFIG_PARISC) || defined(CONFIG_ARM)
89262306a36Sopenharmony_ci	i |= 0x4800;
89362306a36Sopenharmony_ci#else
89462306a36Sopenharmony_ci	dev_warn(&dev->dev, "unknown CPU architecture, using default csr0 setting\n");
89562306a36Sopenharmony_ci	i |= 0x4800;
89662306a36Sopenharmony_ci#endif
89762306a36Sopenharmony_ci	iowrite32(i, ioaddr + PCIBusCfg);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	np->csr6 = 0;
90062306a36Sopenharmony_ci	/* 128 byte Tx threshold;
90162306a36Sopenharmony_ci		Transmit on; Receive on; */
90262306a36Sopenharmony_ci	update_csr6(dev, 0x00022002 | update_link(dev) | __set_rx_mode(dev));
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	/* Clear and Enable interrupts by setting the interrupt mask. */
90562306a36Sopenharmony_ci	iowrite32(0x1A0F5, ioaddr + IntrStatus);
90662306a36Sopenharmony_ci	iowrite32(0x1A0F5, ioaddr + IntrEnable);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	iowrite32(0, ioaddr + RxStartDemand);
90962306a36Sopenharmony_ci}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_cistatic void tx_timeout(struct net_device *dev, unsigned int txqueue)
91262306a36Sopenharmony_ci{
91362306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
91462306a36Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
91562306a36Sopenharmony_ci	const int irq = np->pci_dev->irq;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	dev_warn(&dev->dev, "Transmit timed out, status %08x, resetting...\n",
91862306a36Sopenharmony_ci		 ioread32(ioaddr + IntrStatus));
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	{
92162306a36Sopenharmony_ci		int i;
92262306a36Sopenharmony_ci		printk(KERN_DEBUG "  Rx ring %p: ", np->rx_ring);
92362306a36Sopenharmony_ci		for (i = 0; i < RX_RING_SIZE; i++)
92462306a36Sopenharmony_ci			printk(KERN_CONT " %08x", (unsigned int)np->rx_ring[i].status);
92562306a36Sopenharmony_ci		printk(KERN_CONT "\n");
92662306a36Sopenharmony_ci		printk(KERN_DEBUG "  Tx ring %p: ", np->tx_ring);
92762306a36Sopenharmony_ci		for (i = 0; i < TX_RING_SIZE; i++)
92862306a36Sopenharmony_ci			printk(KERN_CONT " %08x", np->tx_ring[i].status);
92962306a36Sopenharmony_ci		printk(KERN_CONT "\n");
93062306a36Sopenharmony_ci	}
93162306a36Sopenharmony_ci	printk(KERN_DEBUG "Tx cur %d Tx dirty %d Tx Full %d, q bytes %d\n",
93262306a36Sopenharmony_ci	       np->cur_tx, np->dirty_tx, np->tx_full, np->tx_q_bytes);
93362306a36Sopenharmony_ci	printk(KERN_DEBUG "Tx Descriptor addr %xh\n", ioread32(ioaddr+0x4C));
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	disable_irq(irq);
93662306a36Sopenharmony_ci	spin_lock_irq(&np->lock);
93762306a36Sopenharmony_ci	/*
93862306a36Sopenharmony_ci	 * Under high load dirty_tx and the internal tx descriptor pointer
93962306a36Sopenharmony_ci	 * come out of sync, thus perform a software reset and reinitialize
94062306a36Sopenharmony_ci	 * everything.
94162306a36Sopenharmony_ci	 */
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	iowrite32(1, np->base_addr+PCIBusCfg);
94462306a36Sopenharmony_ci	udelay(1);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	free_rxtx_rings(np);
94762306a36Sopenharmony_ci	init_rxtx_rings(dev);
94862306a36Sopenharmony_ci	init_registers(dev);
94962306a36Sopenharmony_ci	spin_unlock_irq(&np->lock);
95062306a36Sopenharmony_ci	enable_irq(irq);
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	netif_wake_queue(dev);
95362306a36Sopenharmony_ci	netif_trans_update(dev); /* prevent tx timeout */
95462306a36Sopenharmony_ci	np->stats.tx_errors++;
95562306a36Sopenharmony_ci}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
95862306a36Sopenharmony_cistatic int alloc_ringdesc(struct net_device *dev)
95962306a36Sopenharmony_ci{
96062306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	np->rx_ring = dma_alloc_coherent(&np->pci_dev->dev,
96562306a36Sopenharmony_ci					 sizeof(struct w840_rx_desc) * RX_RING_SIZE +
96662306a36Sopenharmony_ci					 sizeof(struct w840_tx_desc) * TX_RING_SIZE,
96762306a36Sopenharmony_ci					 &np->ring_dma_addr, GFP_KERNEL);
96862306a36Sopenharmony_ci	if(!np->rx_ring)
96962306a36Sopenharmony_ci		return -ENOMEM;
97062306a36Sopenharmony_ci	init_rxtx_rings(dev);
97162306a36Sopenharmony_ci	return 0;
97262306a36Sopenharmony_ci}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_cistatic void free_ringdesc(struct netdev_private *np)
97562306a36Sopenharmony_ci{
97662306a36Sopenharmony_ci	dma_free_coherent(&np->pci_dev->dev,
97762306a36Sopenharmony_ci			  sizeof(struct w840_rx_desc) * RX_RING_SIZE +
97862306a36Sopenharmony_ci			  sizeof(struct w840_tx_desc) * TX_RING_SIZE,
97962306a36Sopenharmony_ci			  np->rx_ring, np->ring_dma_addr);
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_cistatic netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev)
98462306a36Sopenharmony_ci{
98562306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
98662306a36Sopenharmony_ci	unsigned entry;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	/* Caution: the write order is important here, set the field
98962306a36Sopenharmony_ci	   with the "ownership" bits last. */
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	/* Calculate the next Tx descriptor entry. */
99262306a36Sopenharmony_ci	entry = np->cur_tx % TX_RING_SIZE;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	np->tx_addr[entry] = dma_map_single(&np->pci_dev->dev, skb->data,
99562306a36Sopenharmony_ci					    skb->len, DMA_TO_DEVICE);
99662306a36Sopenharmony_ci	np->tx_skbuff[entry] = skb;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	np->tx_ring[entry].buffer1 = np->tx_addr[entry];
99962306a36Sopenharmony_ci	if (skb->len < TX_BUFLIMIT) {
100062306a36Sopenharmony_ci		np->tx_ring[entry].length = DescWholePkt | skb->len;
100162306a36Sopenharmony_ci	} else {
100262306a36Sopenharmony_ci		int len = skb->len - TX_BUFLIMIT;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci		np->tx_ring[entry].buffer2 = np->tx_addr[entry]+TX_BUFLIMIT;
100562306a36Sopenharmony_ci		np->tx_ring[entry].length = DescWholePkt | (len << 11) | TX_BUFLIMIT;
100662306a36Sopenharmony_ci	}
100762306a36Sopenharmony_ci	if(entry == TX_RING_SIZE-1)
100862306a36Sopenharmony_ci		np->tx_ring[entry].length |= DescEndRing;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	/* Now acquire the irq spinlock.
101162306a36Sopenharmony_ci	 * The difficult race is the ordering between
101262306a36Sopenharmony_ci	 * increasing np->cur_tx and setting DescOwned:
101362306a36Sopenharmony_ci	 * - if np->cur_tx is increased first the interrupt
101462306a36Sopenharmony_ci	 *   handler could consider the packet as transmitted
101562306a36Sopenharmony_ci	 *   since DescOwned is cleared.
101662306a36Sopenharmony_ci	 * - If DescOwned is set first the NIC could report the
101762306a36Sopenharmony_ci	 *   packet as sent, but the interrupt handler would ignore it
101862306a36Sopenharmony_ci	 *   since the np->cur_tx was not yet increased.
101962306a36Sopenharmony_ci	 */
102062306a36Sopenharmony_ci	spin_lock_irq(&np->lock);
102162306a36Sopenharmony_ci	np->cur_tx++;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	wmb(); /* flush length, buffer1, buffer2 */
102462306a36Sopenharmony_ci	np->tx_ring[entry].status = DescOwned;
102562306a36Sopenharmony_ci	wmb(); /* flush status and kick the hardware */
102662306a36Sopenharmony_ci	iowrite32(0, np->base_addr + TxStartDemand);
102762306a36Sopenharmony_ci	np->tx_q_bytes += skb->len;
102862306a36Sopenharmony_ci	/* Work around horrible bug in the chip by marking the queue as full
102962306a36Sopenharmony_ci	   when we do not have FIFO room for a maximum sized packet. */
103062306a36Sopenharmony_ci	if (np->cur_tx - np->dirty_tx > TX_QUEUE_LEN ||
103162306a36Sopenharmony_ci		((np->drv_flags & HasBrokenTx) && np->tx_q_bytes > TX_BUG_FIFO_LIMIT)) {
103262306a36Sopenharmony_ci		netif_stop_queue(dev);
103362306a36Sopenharmony_ci		wmb();
103462306a36Sopenharmony_ci		np->tx_full = 1;
103562306a36Sopenharmony_ci	}
103662306a36Sopenharmony_ci	spin_unlock_irq(&np->lock);
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	if (debug > 4) {
103962306a36Sopenharmony_ci		netdev_dbg(dev, "Transmit frame #%d queued in slot %d\n",
104062306a36Sopenharmony_ci			   np->cur_tx, entry);
104162306a36Sopenharmony_ci	}
104262306a36Sopenharmony_ci	return NETDEV_TX_OK;
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_cistatic void netdev_tx_done(struct net_device *dev)
104662306a36Sopenharmony_ci{
104762306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
104862306a36Sopenharmony_ci	for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
104962306a36Sopenharmony_ci		int entry = np->dirty_tx % TX_RING_SIZE;
105062306a36Sopenharmony_ci		int tx_status = np->tx_ring[entry].status;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci		if (tx_status < 0)
105362306a36Sopenharmony_ci			break;
105462306a36Sopenharmony_ci		if (tx_status & 0x8000) { 	/* There was an error, log it. */
105562306a36Sopenharmony_ci#ifndef final_version
105662306a36Sopenharmony_ci			if (debug > 1)
105762306a36Sopenharmony_ci				netdev_dbg(dev, "Transmit error, Tx status %08x\n",
105862306a36Sopenharmony_ci					   tx_status);
105962306a36Sopenharmony_ci#endif
106062306a36Sopenharmony_ci			np->stats.tx_errors++;
106162306a36Sopenharmony_ci			if (tx_status & 0x0104) np->stats.tx_aborted_errors++;
106262306a36Sopenharmony_ci			if (tx_status & 0x0C80) np->stats.tx_carrier_errors++;
106362306a36Sopenharmony_ci			if (tx_status & 0x0200) np->stats.tx_window_errors++;
106462306a36Sopenharmony_ci			if (tx_status & 0x0002) np->stats.tx_fifo_errors++;
106562306a36Sopenharmony_ci			if ((tx_status & 0x0080) && np->mii_if.full_duplex == 0)
106662306a36Sopenharmony_ci				np->stats.tx_heartbeat_errors++;
106762306a36Sopenharmony_ci		} else {
106862306a36Sopenharmony_ci#ifndef final_version
106962306a36Sopenharmony_ci			if (debug > 3)
107062306a36Sopenharmony_ci				netdev_dbg(dev, "Transmit slot %d ok, Tx status %08x\n",
107162306a36Sopenharmony_ci					   entry, tx_status);
107262306a36Sopenharmony_ci#endif
107362306a36Sopenharmony_ci			np->stats.tx_bytes += np->tx_skbuff[entry]->len;
107462306a36Sopenharmony_ci			np->stats.collisions += (tx_status >> 3) & 15;
107562306a36Sopenharmony_ci			np->stats.tx_packets++;
107662306a36Sopenharmony_ci		}
107762306a36Sopenharmony_ci		/* Free the original skb. */
107862306a36Sopenharmony_ci		dma_unmap_single(&np->pci_dev->dev, np->tx_addr[entry],
107962306a36Sopenharmony_ci				 np->tx_skbuff[entry]->len, DMA_TO_DEVICE);
108062306a36Sopenharmony_ci		np->tx_q_bytes -= np->tx_skbuff[entry]->len;
108162306a36Sopenharmony_ci		dev_kfree_skb_irq(np->tx_skbuff[entry]);
108262306a36Sopenharmony_ci		np->tx_skbuff[entry] = NULL;
108362306a36Sopenharmony_ci	}
108462306a36Sopenharmony_ci	if (np->tx_full &&
108562306a36Sopenharmony_ci		np->cur_tx - np->dirty_tx < TX_QUEUE_LEN_RESTART &&
108662306a36Sopenharmony_ci		np->tx_q_bytes < TX_BUG_FIFO_LIMIT) {
108762306a36Sopenharmony_ci		/* The ring is no longer full, clear tbusy. */
108862306a36Sopenharmony_ci		np->tx_full = 0;
108962306a36Sopenharmony_ci		wmb();
109062306a36Sopenharmony_ci		netif_wake_queue(dev);
109162306a36Sopenharmony_ci	}
109262306a36Sopenharmony_ci}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci/* The interrupt handler does all of the Rx thread work and cleans up
109562306a36Sopenharmony_ci   after the Tx thread. */
109662306a36Sopenharmony_cistatic irqreturn_t intr_handler(int irq, void *dev_instance)
109762306a36Sopenharmony_ci{
109862306a36Sopenharmony_ci	struct net_device *dev = (struct net_device *)dev_instance;
109962306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
110062306a36Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
110162306a36Sopenharmony_ci	int work_limit = max_interrupt_work;
110262306a36Sopenharmony_ci	int handled = 0;
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	if (!netif_device_present(dev))
110562306a36Sopenharmony_ci		return IRQ_NONE;
110662306a36Sopenharmony_ci	do {
110762306a36Sopenharmony_ci		u32 intr_status = ioread32(ioaddr + IntrStatus);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci		/* Acknowledge all of the current interrupt sources ASAP. */
111062306a36Sopenharmony_ci		iowrite32(intr_status & 0x001ffff, ioaddr + IntrStatus);
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci		if (debug > 4)
111362306a36Sopenharmony_ci			netdev_dbg(dev, "Interrupt, status %04x\n", intr_status);
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci		if ((intr_status & (NormalIntr|AbnormalIntr)) == 0)
111662306a36Sopenharmony_ci			break;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci		handled = 1;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci		if (intr_status & (RxIntr | RxNoBuf))
112162306a36Sopenharmony_ci			netdev_rx(dev);
112262306a36Sopenharmony_ci		if (intr_status & RxNoBuf)
112362306a36Sopenharmony_ci			iowrite32(0, ioaddr + RxStartDemand);
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci		if (intr_status & (TxNoBuf | TxIntr) &&
112662306a36Sopenharmony_ci			np->cur_tx != np->dirty_tx) {
112762306a36Sopenharmony_ci			spin_lock(&np->lock);
112862306a36Sopenharmony_ci			netdev_tx_done(dev);
112962306a36Sopenharmony_ci			spin_unlock(&np->lock);
113062306a36Sopenharmony_ci		}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci		/* Abnormal error summary/uncommon events handlers. */
113362306a36Sopenharmony_ci		if (intr_status & (AbnormalIntr | TxFIFOUnderflow | SystemError |
113462306a36Sopenharmony_ci						   TimerInt | TxDied))
113562306a36Sopenharmony_ci			netdev_error(dev, intr_status);
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci		if (--work_limit < 0) {
113862306a36Sopenharmony_ci			dev_warn(&dev->dev,
113962306a36Sopenharmony_ci				 "Too much work at interrupt, status=0x%04x\n",
114062306a36Sopenharmony_ci				 intr_status);
114162306a36Sopenharmony_ci			/* Set the timer to re-enable the other interrupts after
114262306a36Sopenharmony_ci			   10*82usec ticks. */
114362306a36Sopenharmony_ci			spin_lock(&np->lock);
114462306a36Sopenharmony_ci			if (netif_device_present(dev)) {
114562306a36Sopenharmony_ci				iowrite32(AbnormalIntr | TimerInt, ioaddr + IntrEnable);
114662306a36Sopenharmony_ci				iowrite32(10, ioaddr + GPTimer);
114762306a36Sopenharmony_ci			}
114862306a36Sopenharmony_ci			spin_unlock(&np->lock);
114962306a36Sopenharmony_ci			break;
115062306a36Sopenharmony_ci		}
115162306a36Sopenharmony_ci	} while (1);
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	if (debug > 3)
115462306a36Sopenharmony_ci		netdev_dbg(dev, "exiting interrupt, status=%#4.4x\n",
115562306a36Sopenharmony_ci			   ioread32(ioaddr + IntrStatus));
115662306a36Sopenharmony_ci	return IRQ_RETVAL(handled);
115762306a36Sopenharmony_ci}
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci/* This routine is logically part of the interrupt handler, but separated
116062306a36Sopenharmony_ci   for clarity and better register allocation. */
116162306a36Sopenharmony_cistatic int netdev_rx(struct net_device *dev)
116262306a36Sopenharmony_ci{
116362306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
116462306a36Sopenharmony_ci	int entry = np->cur_rx % RX_RING_SIZE;
116562306a36Sopenharmony_ci	int work_limit = np->dirty_rx + RX_RING_SIZE - np->cur_rx;
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	if (debug > 4) {
116862306a36Sopenharmony_ci		netdev_dbg(dev, " In netdev_rx(), entry %d status %04x\n",
116962306a36Sopenharmony_ci			   entry, np->rx_ring[entry].status);
117062306a36Sopenharmony_ci	}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	/* If EOP is set on the next entry, it's a new packet. Send it up. */
117362306a36Sopenharmony_ci	while (--work_limit >= 0) {
117462306a36Sopenharmony_ci		struct w840_rx_desc *desc = np->rx_head_desc;
117562306a36Sopenharmony_ci		s32 status = desc->status;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci		if (debug > 4)
117862306a36Sopenharmony_ci			netdev_dbg(dev, "  netdev_rx() status was %08x\n",
117962306a36Sopenharmony_ci				   status);
118062306a36Sopenharmony_ci		if (status < 0)
118162306a36Sopenharmony_ci			break;
118262306a36Sopenharmony_ci		if ((status & 0x38008300) != 0x0300) {
118362306a36Sopenharmony_ci			if ((status & 0x38000300) != 0x0300) {
118462306a36Sopenharmony_ci				/* Ingore earlier buffers. */
118562306a36Sopenharmony_ci				if ((status & 0xffff) != 0x7fff) {
118662306a36Sopenharmony_ci					dev_warn(&dev->dev,
118762306a36Sopenharmony_ci						 "Oversized Ethernet frame spanned multiple buffers, entry %#x status %04x!\n",
118862306a36Sopenharmony_ci						 np->cur_rx, status);
118962306a36Sopenharmony_ci					np->stats.rx_length_errors++;
119062306a36Sopenharmony_ci				}
119162306a36Sopenharmony_ci			} else if (status & 0x8000) {
119262306a36Sopenharmony_ci				/* There was a fatal error. */
119362306a36Sopenharmony_ci				if (debug > 2)
119462306a36Sopenharmony_ci					netdev_dbg(dev, "Receive error, Rx status %08x\n",
119562306a36Sopenharmony_ci						   status);
119662306a36Sopenharmony_ci				np->stats.rx_errors++; /* end of a packet.*/
119762306a36Sopenharmony_ci				if (status & 0x0890) np->stats.rx_length_errors++;
119862306a36Sopenharmony_ci				if (status & 0x004C) np->stats.rx_frame_errors++;
119962306a36Sopenharmony_ci				if (status & 0x0002) np->stats.rx_crc_errors++;
120062306a36Sopenharmony_ci			}
120162306a36Sopenharmony_ci		} else {
120262306a36Sopenharmony_ci			struct sk_buff *skb;
120362306a36Sopenharmony_ci			/* Omit the four octet CRC from the length. */
120462306a36Sopenharmony_ci			int pkt_len = ((status >> 16) & 0x7ff) - 4;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci#ifndef final_version
120762306a36Sopenharmony_ci			if (debug > 4)
120862306a36Sopenharmony_ci				netdev_dbg(dev, "  netdev_rx() normal Rx pkt length %d status %x\n",
120962306a36Sopenharmony_ci					   pkt_len, status);
121062306a36Sopenharmony_ci#endif
121162306a36Sopenharmony_ci			/* Check if the packet is long enough to accept without copying
121262306a36Sopenharmony_ci			   to a minimally-sized skbuff. */
121362306a36Sopenharmony_ci			if (pkt_len < rx_copybreak &&
121462306a36Sopenharmony_ci			    (skb = netdev_alloc_skb(dev, pkt_len + 2)) != NULL) {
121562306a36Sopenharmony_ci				skb_reserve(skb, 2);	/* 16 byte align the IP header */
121662306a36Sopenharmony_ci				dma_sync_single_for_cpu(&np->pci_dev->dev,
121762306a36Sopenharmony_ci							np->rx_addr[entry],
121862306a36Sopenharmony_ci							np->rx_skbuff[entry]->len,
121962306a36Sopenharmony_ci							DMA_FROM_DEVICE);
122062306a36Sopenharmony_ci				skb_copy_to_linear_data(skb, np->rx_skbuff[entry]->data, pkt_len);
122162306a36Sopenharmony_ci				skb_put(skb, pkt_len);
122262306a36Sopenharmony_ci				dma_sync_single_for_device(&np->pci_dev->dev,
122362306a36Sopenharmony_ci							   np->rx_addr[entry],
122462306a36Sopenharmony_ci							   np->rx_skbuff[entry]->len,
122562306a36Sopenharmony_ci							   DMA_FROM_DEVICE);
122662306a36Sopenharmony_ci			} else {
122762306a36Sopenharmony_ci				dma_unmap_single(&np->pci_dev->dev,
122862306a36Sopenharmony_ci						 np->rx_addr[entry],
122962306a36Sopenharmony_ci						 np->rx_skbuff[entry]->len,
123062306a36Sopenharmony_ci						 DMA_FROM_DEVICE);
123162306a36Sopenharmony_ci				skb_put(skb = np->rx_skbuff[entry], pkt_len);
123262306a36Sopenharmony_ci				np->rx_skbuff[entry] = NULL;
123362306a36Sopenharmony_ci			}
123462306a36Sopenharmony_ci#ifndef final_version				/* Remove after testing. */
123562306a36Sopenharmony_ci			/* You will want this info for the initial debug. */
123662306a36Sopenharmony_ci			if (debug > 5)
123762306a36Sopenharmony_ci				netdev_dbg(dev, "  Rx data %pM %pM %02x%02x %pI4\n",
123862306a36Sopenharmony_ci					   &skb->data[0], &skb->data[6],
123962306a36Sopenharmony_ci					   skb->data[12], skb->data[13],
124062306a36Sopenharmony_ci					   &skb->data[14]);
124162306a36Sopenharmony_ci#endif
124262306a36Sopenharmony_ci			skb->protocol = eth_type_trans(skb, dev);
124362306a36Sopenharmony_ci			netif_rx(skb);
124462306a36Sopenharmony_ci			np->stats.rx_packets++;
124562306a36Sopenharmony_ci			np->stats.rx_bytes += pkt_len;
124662306a36Sopenharmony_ci		}
124762306a36Sopenharmony_ci		entry = (++np->cur_rx) % RX_RING_SIZE;
124862306a36Sopenharmony_ci		np->rx_head_desc = &np->rx_ring[entry];
124962306a36Sopenharmony_ci	}
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	/* Refill the Rx ring buffers. */
125262306a36Sopenharmony_ci	for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) {
125362306a36Sopenharmony_ci		struct sk_buff *skb;
125462306a36Sopenharmony_ci		entry = np->dirty_rx % RX_RING_SIZE;
125562306a36Sopenharmony_ci		if (np->rx_skbuff[entry] == NULL) {
125662306a36Sopenharmony_ci			skb = netdev_alloc_skb(dev, np->rx_buf_sz);
125762306a36Sopenharmony_ci			np->rx_skbuff[entry] = skb;
125862306a36Sopenharmony_ci			if (skb == NULL)
125962306a36Sopenharmony_ci				break;			/* Better luck next round. */
126062306a36Sopenharmony_ci			np->rx_addr[entry] = dma_map_single(&np->pci_dev->dev,
126162306a36Sopenharmony_ci							    skb->data,
126262306a36Sopenharmony_ci							    np->rx_buf_sz,
126362306a36Sopenharmony_ci							    DMA_FROM_DEVICE);
126462306a36Sopenharmony_ci			np->rx_ring[entry].buffer1 = np->rx_addr[entry];
126562306a36Sopenharmony_ci		}
126662306a36Sopenharmony_ci		wmb();
126762306a36Sopenharmony_ci		np->rx_ring[entry].status = DescOwned;
126862306a36Sopenharmony_ci	}
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	return 0;
127162306a36Sopenharmony_ci}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_cistatic void netdev_error(struct net_device *dev, int intr_status)
127462306a36Sopenharmony_ci{
127562306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
127662306a36Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	if (debug > 2)
127962306a36Sopenharmony_ci		netdev_dbg(dev, "Abnormal event, %08x\n", intr_status);
128062306a36Sopenharmony_ci	if (intr_status == 0xffffffff)
128162306a36Sopenharmony_ci		return;
128262306a36Sopenharmony_ci	spin_lock(&np->lock);
128362306a36Sopenharmony_ci	if (intr_status & TxFIFOUnderflow) {
128462306a36Sopenharmony_ci		int new;
128562306a36Sopenharmony_ci		/* Bump up the Tx threshold */
128662306a36Sopenharmony_ci#if 0
128762306a36Sopenharmony_ci		/* This causes lots of dropped packets,
128862306a36Sopenharmony_ci		 * and under high load even tx_timeouts
128962306a36Sopenharmony_ci		 */
129062306a36Sopenharmony_ci		new = np->csr6 + 0x4000;
129162306a36Sopenharmony_ci#else
129262306a36Sopenharmony_ci		new = (np->csr6 >> 14)&0x7f;
129362306a36Sopenharmony_ci		if (new < 64)
129462306a36Sopenharmony_ci			new *= 2;
129562306a36Sopenharmony_ci		 else
129662306a36Sopenharmony_ci		 	new = 127; /* load full packet before starting */
129762306a36Sopenharmony_ci		new = (np->csr6 & ~(0x7F << 14)) | (new<<14);
129862306a36Sopenharmony_ci#endif
129962306a36Sopenharmony_ci		netdev_dbg(dev, "Tx underflow, new csr6 %08x\n", new);
130062306a36Sopenharmony_ci		update_csr6(dev, new);
130162306a36Sopenharmony_ci	}
130262306a36Sopenharmony_ci	if (intr_status & RxDied) {		/* Missed a Rx frame. */
130362306a36Sopenharmony_ci		np->stats.rx_errors++;
130462306a36Sopenharmony_ci	}
130562306a36Sopenharmony_ci	if (intr_status & TimerInt) {
130662306a36Sopenharmony_ci		/* Re-enable other interrupts. */
130762306a36Sopenharmony_ci		if (netif_device_present(dev))
130862306a36Sopenharmony_ci			iowrite32(0x1A0F5, ioaddr + IntrEnable);
130962306a36Sopenharmony_ci	}
131062306a36Sopenharmony_ci	np->stats.rx_missed_errors += ioread32(ioaddr + RxMissed) & 0xffff;
131162306a36Sopenharmony_ci	iowrite32(0, ioaddr + RxStartDemand);
131262306a36Sopenharmony_ci	spin_unlock(&np->lock);
131362306a36Sopenharmony_ci}
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_cistatic struct net_device_stats *get_stats(struct net_device *dev)
131662306a36Sopenharmony_ci{
131762306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
131862306a36Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	/* The chip only need report frame silently dropped. */
132162306a36Sopenharmony_ci	spin_lock_irq(&np->lock);
132262306a36Sopenharmony_ci	if (netif_running(dev) && netif_device_present(dev))
132362306a36Sopenharmony_ci		np->stats.rx_missed_errors += ioread32(ioaddr + RxMissed) & 0xffff;
132462306a36Sopenharmony_ci	spin_unlock_irq(&np->lock);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	return &np->stats;
132762306a36Sopenharmony_ci}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_cistatic u32 __set_rx_mode(struct net_device *dev)
133162306a36Sopenharmony_ci{
133262306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
133362306a36Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
133462306a36Sopenharmony_ci	u32 mc_filter[2];			/* Multicast hash filter */
133562306a36Sopenharmony_ci	u32 rx_mode;
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
133862306a36Sopenharmony_ci		memset(mc_filter, 0xff, sizeof(mc_filter));
133962306a36Sopenharmony_ci		rx_mode = RxAcceptBroadcast | AcceptMulticast | RxAcceptAllPhys
134062306a36Sopenharmony_ci			| AcceptMyPhys;
134162306a36Sopenharmony_ci	} else if ((netdev_mc_count(dev) > multicast_filter_limit) ||
134262306a36Sopenharmony_ci		   (dev->flags & IFF_ALLMULTI)) {
134362306a36Sopenharmony_ci		/* Too many to match, or accept all multicasts. */
134462306a36Sopenharmony_ci		memset(mc_filter, 0xff, sizeof(mc_filter));
134562306a36Sopenharmony_ci		rx_mode = RxAcceptBroadcast | AcceptMulticast | AcceptMyPhys;
134662306a36Sopenharmony_ci	} else {
134762306a36Sopenharmony_ci		struct netdev_hw_addr *ha;
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci		memset(mc_filter, 0, sizeof(mc_filter));
135062306a36Sopenharmony_ci		netdev_for_each_mc_addr(ha, dev) {
135162306a36Sopenharmony_ci			int filbit;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci			filbit = (ether_crc(ETH_ALEN, ha->addr) >> 26) ^ 0x3F;
135462306a36Sopenharmony_ci			filbit &= 0x3f;
135562306a36Sopenharmony_ci			mc_filter[filbit >> 5] |= 1 << (filbit & 31);
135662306a36Sopenharmony_ci		}
135762306a36Sopenharmony_ci		rx_mode = RxAcceptBroadcast | AcceptMulticast | AcceptMyPhys;
135862306a36Sopenharmony_ci	}
135962306a36Sopenharmony_ci	iowrite32(mc_filter[0], ioaddr + MulticastFilter0);
136062306a36Sopenharmony_ci	iowrite32(mc_filter[1], ioaddr + MulticastFilter1);
136162306a36Sopenharmony_ci	return rx_mode;
136262306a36Sopenharmony_ci}
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_cistatic void set_rx_mode(struct net_device *dev)
136562306a36Sopenharmony_ci{
136662306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
136762306a36Sopenharmony_ci	u32 rx_mode = __set_rx_mode(dev);
136862306a36Sopenharmony_ci	spin_lock_irq(&np->lock);
136962306a36Sopenharmony_ci	update_csr6(dev, (np->csr6 & ~0x00F8) | rx_mode);
137062306a36Sopenharmony_ci	spin_unlock_irq(&np->lock);
137162306a36Sopenharmony_ci}
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_cistatic void netdev_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info)
137462306a36Sopenharmony_ci{
137562306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
137862306a36Sopenharmony_ci	strscpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info));
137962306a36Sopenharmony_ci}
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_cistatic int netdev_get_link_ksettings(struct net_device *dev,
138262306a36Sopenharmony_ci				     struct ethtool_link_ksettings *cmd)
138362306a36Sopenharmony_ci{
138462306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	spin_lock_irq(&np->lock);
138762306a36Sopenharmony_ci	mii_ethtool_get_link_ksettings(&np->mii_if, cmd);
138862306a36Sopenharmony_ci	spin_unlock_irq(&np->lock);
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	return 0;
139162306a36Sopenharmony_ci}
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_cistatic int netdev_set_link_ksettings(struct net_device *dev,
139462306a36Sopenharmony_ci				     const struct ethtool_link_ksettings *cmd)
139562306a36Sopenharmony_ci{
139662306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
139762306a36Sopenharmony_ci	int rc;
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	spin_lock_irq(&np->lock);
140062306a36Sopenharmony_ci	rc = mii_ethtool_set_link_ksettings(&np->mii_if, cmd);
140162306a36Sopenharmony_ci	spin_unlock_irq(&np->lock);
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	return rc;
140462306a36Sopenharmony_ci}
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_cistatic int netdev_nway_reset(struct net_device *dev)
140762306a36Sopenharmony_ci{
140862306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
140962306a36Sopenharmony_ci	return mii_nway_restart(&np->mii_if);
141062306a36Sopenharmony_ci}
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_cistatic u32 netdev_get_link(struct net_device *dev)
141362306a36Sopenharmony_ci{
141462306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
141562306a36Sopenharmony_ci	return mii_link_ok(&np->mii_if);
141662306a36Sopenharmony_ci}
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_cistatic u32 netdev_get_msglevel(struct net_device *dev)
141962306a36Sopenharmony_ci{
142062306a36Sopenharmony_ci	return debug;
142162306a36Sopenharmony_ci}
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_cistatic void netdev_set_msglevel(struct net_device *dev, u32 value)
142462306a36Sopenharmony_ci{
142562306a36Sopenharmony_ci	debug = value;
142662306a36Sopenharmony_ci}
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_cistatic const struct ethtool_ops netdev_ethtool_ops = {
142962306a36Sopenharmony_ci	.get_drvinfo		= netdev_get_drvinfo,
143062306a36Sopenharmony_ci	.nway_reset		= netdev_nway_reset,
143162306a36Sopenharmony_ci	.get_link		= netdev_get_link,
143262306a36Sopenharmony_ci	.get_msglevel		= netdev_get_msglevel,
143362306a36Sopenharmony_ci	.set_msglevel		= netdev_set_msglevel,
143462306a36Sopenharmony_ci	.get_link_ksettings	= netdev_get_link_ksettings,
143562306a36Sopenharmony_ci	.set_link_ksettings	= netdev_set_link_ksettings,
143662306a36Sopenharmony_ci};
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_cistatic int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
143962306a36Sopenharmony_ci{
144062306a36Sopenharmony_ci	struct mii_ioctl_data *data = if_mii(rq);
144162306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	switch(cmd) {
144462306a36Sopenharmony_ci	case SIOCGMIIPHY:		/* Get address of MII PHY in use. */
144562306a36Sopenharmony_ci		data->phy_id = ((struct netdev_private *)netdev_priv(dev))->phys[0] & 0x1f;
144662306a36Sopenharmony_ci		fallthrough;
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	case SIOCGMIIREG:		/* Read MII PHY register. */
144962306a36Sopenharmony_ci		spin_lock_irq(&np->lock);
145062306a36Sopenharmony_ci		data->val_out = mdio_read(dev, data->phy_id & 0x1f, data->reg_num & 0x1f);
145162306a36Sopenharmony_ci		spin_unlock_irq(&np->lock);
145262306a36Sopenharmony_ci		return 0;
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	case SIOCSMIIREG:		/* Write MII PHY register. */
145562306a36Sopenharmony_ci		spin_lock_irq(&np->lock);
145662306a36Sopenharmony_ci		mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in);
145762306a36Sopenharmony_ci		spin_unlock_irq(&np->lock);
145862306a36Sopenharmony_ci		return 0;
145962306a36Sopenharmony_ci	default:
146062306a36Sopenharmony_ci		return -EOPNOTSUPP;
146162306a36Sopenharmony_ci	}
146262306a36Sopenharmony_ci}
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_cistatic int netdev_close(struct net_device *dev)
146562306a36Sopenharmony_ci{
146662306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
146762306a36Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	netif_stop_queue(dev);
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	if (debug > 1) {
147262306a36Sopenharmony_ci		netdev_dbg(dev, "Shutting down ethercard, status was %08x Config %08x\n",
147362306a36Sopenharmony_ci			   ioread32(ioaddr + IntrStatus),
147462306a36Sopenharmony_ci			   ioread32(ioaddr + NetworkConfig));
147562306a36Sopenharmony_ci		netdev_dbg(dev, "Queue pointers were Tx %d / %d,  Rx %d / %d\n",
147662306a36Sopenharmony_ci			   np->cur_tx, np->dirty_tx,
147762306a36Sopenharmony_ci			   np->cur_rx, np->dirty_rx);
147862306a36Sopenharmony_ci	}
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	/* Stop the chip's Tx and Rx processes. */
148162306a36Sopenharmony_ci	spin_lock_irq(&np->lock);
148262306a36Sopenharmony_ci	netif_device_detach(dev);
148362306a36Sopenharmony_ci	update_csr6(dev, 0);
148462306a36Sopenharmony_ci	iowrite32(0x0000, ioaddr + IntrEnable);
148562306a36Sopenharmony_ci	spin_unlock_irq(&np->lock);
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	free_irq(np->pci_dev->irq, dev);
148862306a36Sopenharmony_ci	wmb();
148962306a36Sopenharmony_ci	netif_device_attach(dev);
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	if (ioread32(ioaddr + NetworkConfig) != 0xffffffff)
149262306a36Sopenharmony_ci		np->stats.rx_missed_errors += ioread32(ioaddr + RxMissed) & 0xffff;
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci#ifdef __i386__
149562306a36Sopenharmony_ci	if (debug > 2) {
149662306a36Sopenharmony_ci		int i;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci		printk(KERN_DEBUG"  Tx ring at %p:\n", np->tx_ring);
149962306a36Sopenharmony_ci		for (i = 0; i < TX_RING_SIZE; i++)
150062306a36Sopenharmony_ci			printk(KERN_DEBUG " #%d desc. %04x %04x %08x\n",
150162306a36Sopenharmony_ci			       i, np->tx_ring[i].length,
150262306a36Sopenharmony_ci			       np->tx_ring[i].status, np->tx_ring[i].buffer1);
150362306a36Sopenharmony_ci		printk(KERN_DEBUG "  Rx ring %p:\n", np->rx_ring);
150462306a36Sopenharmony_ci		for (i = 0; i < RX_RING_SIZE; i++) {
150562306a36Sopenharmony_ci			printk(KERN_DEBUG " #%d desc. %04x %04x %08x\n",
150662306a36Sopenharmony_ci			       i, np->rx_ring[i].length,
150762306a36Sopenharmony_ci			       np->rx_ring[i].status, np->rx_ring[i].buffer1);
150862306a36Sopenharmony_ci		}
150962306a36Sopenharmony_ci	}
151062306a36Sopenharmony_ci#endif /* __i386__ debugging only */
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	del_timer_sync(&np->timer);
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	free_rxtx_rings(np);
151562306a36Sopenharmony_ci	free_ringdesc(np);
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	return 0;
151862306a36Sopenharmony_ci}
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_cistatic void w840_remove1(struct pci_dev *pdev)
152162306a36Sopenharmony_ci{
152262306a36Sopenharmony_ci	struct net_device *dev = pci_get_drvdata(pdev);
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	if (dev) {
152562306a36Sopenharmony_ci		struct netdev_private *np = netdev_priv(dev);
152662306a36Sopenharmony_ci		unregister_netdev(dev);
152762306a36Sopenharmony_ci		pci_iounmap(pdev, np->base_addr);
152862306a36Sopenharmony_ci		free_netdev(dev);
152962306a36Sopenharmony_ci	}
153062306a36Sopenharmony_ci}
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci/*
153362306a36Sopenharmony_ci * suspend/resume synchronization:
153462306a36Sopenharmony_ci * - open, close, do_ioctl:
153562306a36Sopenharmony_ci * 	rtnl_lock, & netif_device_detach after the rtnl_unlock.
153662306a36Sopenharmony_ci * - get_stats:
153762306a36Sopenharmony_ci * 	spin_lock_irq(np->lock), doesn't touch hw if not present
153862306a36Sopenharmony_ci * - start_xmit:
153962306a36Sopenharmony_ci * 	synchronize_irq + netif_tx_disable;
154062306a36Sopenharmony_ci * - tx_timeout:
154162306a36Sopenharmony_ci * 	netif_device_detach + netif_tx_disable;
154262306a36Sopenharmony_ci * - set_multicast_list
154362306a36Sopenharmony_ci * 	netif_device_detach + netif_tx_disable;
154462306a36Sopenharmony_ci * - interrupt handler
154562306a36Sopenharmony_ci * 	doesn't touch hw if not present, synchronize_irq waits for
154662306a36Sopenharmony_ci * 	running instances of the interrupt handler.
154762306a36Sopenharmony_ci *
154862306a36Sopenharmony_ci * Disabling hw requires clearing csr6 & IntrEnable.
154962306a36Sopenharmony_ci * update_csr6 & all function that write IntrEnable check netif_device_present
155062306a36Sopenharmony_ci * before settings any bits.
155162306a36Sopenharmony_ci *
155262306a36Sopenharmony_ci * Detach must occur under spin_unlock_irq(), interrupts from a detached
155362306a36Sopenharmony_ci * device would cause an irq storm.
155462306a36Sopenharmony_ci */
155562306a36Sopenharmony_cistatic int __maybe_unused w840_suspend(struct device *dev_d)
155662306a36Sopenharmony_ci{
155762306a36Sopenharmony_ci	struct net_device *dev = dev_get_drvdata(dev_d);
155862306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
155962306a36Sopenharmony_ci	void __iomem *ioaddr = np->base_addr;
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	rtnl_lock();
156262306a36Sopenharmony_ci	if (netif_running (dev)) {
156362306a36Sopenharmony_ci		del_timer_sync(&np->timer);
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci		spin_lock_irq(&np->lock);
156662306a36Sopenharmony_ci		netif_device_detach(dev);
156762306a36Sopenharmony_ci		update_csr6(dev, 0);
156862306a36Sopenharmony_ci		iowrite32(0, ioaddr + IntrEnable);
156962306a36Sopenharmony_ci		spin_unlock_irq(&np->lock);
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci		synchronize_irq(np->pci_dev->irq);
157262306a36Sopenharmony_ci		netif_tx_disable(dev);
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci		np->stats.rx_missed_errors += ioread32(ioaddr + RxMissed) & 0xffff;
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci		/* no more hardware accesses behind this line. */
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci		BUG_ON(np->csr6 || ioread32(ioaddr + IntrEnable));
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci		/* pci_power_off(pdev, -1); */
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci		free_rxtx_rings(np);
158362306a36Sopenharmony_ci	} else {
158462306a36Sopenharmony_ci		netif_device_detach(dev);
158562306a36Sopenharmony_ci	}
158662306a36Sopenharmony_ci	rtnl_unlock();
158762306a36Sopenharmony_ci	return 0;
158862306a36Sopenharmony_ci}
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_cistatic int __maybe_unused w840_resume(struct device *dev_d)
159162306a36Sopenharmony_ci{
159262306a36Sopenharmony_ci	struct net_device *dev = dev_get_drvdata(dev_d);
159362306a36Sopenharmony_ci	struct netdev_private *np = netdev_priv(dev);
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	rtnl_lock();
159662306a36Sopenharmony_ci	if (netif_device_present(dev))
159762306a36Sopenharmony_ci		goto out; /* device not suspended */
159862306a36Sopenharmony_ci	if (netif_running(dev)) {
159962306a36Sopenharmony_ci		spin_lock_irq(&np->lock);
160062306a36Sopenharmony_ci		iowrite32(1, np->base_addr+PCIBusCfg);
160162306a36Sopenharmony_ci		ioread32(np->base_addr+PCIBusCfg);
160262306a36Sopenharmony_ci		udelay(1);
160362306a36Sopenharmony_ci		netif_device_attach(dev);
160462306a36Sopenharmony_ci		init_rxtx_rings(dev);
160562306a36Sopenharmony_ci		init_registers(dev);
160662306a36Sopenharmony_ci		spin_unlock_irq(&np->lock);
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci		netif_wake_queue(dev);
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci		mod_timer(&np->timer, jiffies + 1*HZ);
161162306a36Sopenharmony_ci	} else {
161262306a36Sopenharmony_ci		netif_device_attach(dev);
161362306a36Sopenharmony_ci	}
161462306a36Sopenharmony_ciout:
161562306a36Sopenharmony_ci	rtnl_unlock();
161662306a36Sopenharmony_ci	return 0;
161762306a36Sopenharmony_ci}
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(w840_pm_ops, w840_suspend, w840_resume);
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_cistatic struct pci_driver w840_driver = {
162262306a36Sopenharmony_ci	.name		= DRV_NAME,
162362306a36Sopenharmony_ci	.id_table	= w840_pci_tbl,
162462306a36Sopenharmony_ci	.probe		= w840_probe1,
162562306a36Sopenharmony_ci	.remove		= w840_remove1,
162662306a36Sopenharmony_ci	.driver.pm	= &w840_pm_ops,
162762306a36Sopenharmony_ci};
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_cimodule_pci_driver(w840_driver);
1630