162306a36Sopenharmony_ci/* typhoon.c: A Linux Ethernet device driver for 3Com 3CR990 family of NICs */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci	Written 2002-2004 by David Dillow <dave@thedillows.org>
462306a36Sopenharmony_ci	Based on code written 1998-2000 by Donald Becker <becker@scyld.com> and
562306a36Sopenharmony_ci	Linux 2.2.x driver by David P. McLean <davidpmclean@yahoo.com>.
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci	This software may be used and distributed according to the terms of
862306a36Sopenharmony_ci	the GNU General Public License (GPL), incorporated herein by reference.
962306a36Sopenharmony_ci	Drivers based on or derived from this code fall under the GPL and must
1062306a36Sopenharmony_ci	retain the authorship, copyright and license notice.  This file is not
1162306a36Sopenharmony_ci	a complete program and may only be used when the entire operating
1262306a36Sopenharmony_ci	system is licensed under the GPL.
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci	This software is available on a public web site. It may enable
1562306a36Sopenharmony_ci	cryptographic capabilities of the 3Com hardware, and may be
1662306a36Sopenharmony_ci	exported from the United States under License Exception "TSU"
1762306a36Sopenharmony_ci	pursuant to 15 C.F.R. Section 740.13(e).
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci	This work was funded by the National Library of Medicine under
2062306a36Sopenharmony_ci	the Department of Energy project number 0274DD06D1 and NLM project
2162306a36Sopenharmony_ci	number Y1-LM-2015-01.
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci	This driver is designed for the 3Com 3CR990 Family of cards with the
2462306a36Sopenharmony_ci	3XP Processor. It has been tested on x86 and sparc64.
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	KNOWN ISSUES:
2762306a36Sopenharmony_ci	*) Cannot DMA Rx packets to a 2 byte aligned address. Also firmware
2862306a36Sopenharmony_ci		issue. Hopefully 3Com will fix it.
2962306a36Sopenharmony_ci	*) Waiting for a command response takes 8ms due to non-preemptable
3062306a36Sopenharmony_ci		polling. Only significant for getting stats and creating
3162306a36Sopenharmony_ci		SAs, but an ugly wart never the less.
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	TODO:
3462306a36Sopenharmony_ci	*) Doesn't do IPSEC offloading. Yet. Keep yer pants on, it's coming.
3562306a36Sopenharmony_ci	*) Add more support for ethtool (especially for NIC stats)
3662306a36Sopenharmony_ci	*) Allow disabling of RX checksum offloading
3762306a36Sopenharmony_ci	*) Fix MAC changing to work while the interface is up
3862306a36Sopenharmony_ci		(Need to put commands on the TX ring, which changes
3962306a36Sopenharmony_ci		the locking)
4062306a36Sopenharmony_ci	*) Add in FCS to {rx,tx}_bytes, since the hardware doesn't. See
4162306a36Sopenharmony_ci		http://oss.sgi.com/cgi-bin/mesg.cgi?a=netdev&i=20031215152211.7003fe8e.rddunlap%40osdl.org
4262306a36Sopenharmony_ci*/
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
4562306a36Sopenharmony_ci * Setting to > 1518 effectively disables this feature.
4662306a36Sopenharmony_ci */
4762306a36Sopenharmony_cistatic int rx_copybreak = 200;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* Should we use MMIO or Port IO?
5062306a36Sopenharmony_ci * 0: Port IO
5162306a36Sopenharmony_ci * 1: MMIO
5262306a36Sopenharmony_ci * 2: Try MMIO, fallback to Port IO
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_cistatic unsigned int use_mmio = 2;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/* end user-configurable values */
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_cistatic const int multicast_filter_limit = 32;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* Operational parameters that are set at compile time. */
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* Keep the ring sizes a power of two for compile efficiency.
6562306a36Sopenharmony_ci * The compiler will convert <unsigned>'%'<2^N> into a bit mask.
6662306a36Sopenharmony_ci * Making the Tx ring too large decreases the effectiveness of channel
6762306a36Sopenharmony_ci * bonding and packet priority.
6862306a36Sopenharmony_ci * There are no ill effects from too-large receive rings.
6962306a36Sopenharmony_ci *
7062306a36Sopenharmony_ci * We don't currently use the Hi Tx ring so, don't make it very big.
7162306a36Sopenharmony_ci *
7262306a36Sopenharmony_ci * Beware that if we start using the Hi Tx ring, we will need to change
7362306a36Sopenharmony_ci * typhoon_num_free_tx() and typhoon_tx_complete() to account for that.
7462306a36Sopenharmony_ci */
7562306a36Sopenharmony_ci#define TXHI_ENTRIES		2
7662306a36Sopenharmony_ci#define TXLO_ENTRIES		128
7762306a36Sopenharmony_ci#define RX_ENTRIES		32
7862306a36Sopenharmony_ci#define COMMAND_ENTRIES		16
7962306a36Sopenharmony_ci#define RESPONSE_ENTRIES	32
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#define COMMAND_RING_SIZE	(COMMAND_ENTRIES * sizeof(struct cmd_desc))
8262306a36Sopenharmony_ci#define RESPONSE_RING_SIZE	(RESPONSE_ENTRIES * sizeof(struct resp_desc))
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/* The 3XP will preload and remove 64 entries from the free buffer
8562306a36Sopenharmony_ci * list, and we need one entry to keep the ring from wrapping, so
8662306a36Sopenharmony_ci * to keep this a power of two, we use 128 entries.
8762306a36Sopenharmony_ci */
8862306a36Sopenharmony_ci#define RXFREE_ENTRIES		128
8962306a36Sopenharmony_ci#define RXENT_ENTRIES		(RXFREE_ENTRIES - 1)
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/* Operational parameters that usually are not changed. */
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/* Time in jiffies before concluding the transmitter is hung. */
9462306a36Sopenharmony_ci#define TX_TIMEOUT  (2*HZ)
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci#define PKT_BUF_SZ		1536
9762306a36Sopenharmony_ci#define FIRMWARE_NAME		"3com/typhoon.bin"
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci#define pr_fmt(fmt)		KBUILD_MODNAME " " fmt
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci#include <linux/module.h>
10262306a36Sopenharmony_ci#include <linux/kernel.h>
10362306a36Sopenharmony_ci#include <linux/sched.h>
10462306a36Sopenharmony_ci#include <linux/string.h>
10562306a36Sopenharmony_ci#include <linux/timer.h>
10662306a36Sopenharmony_ci#include <linux/errno.h>
10762306a36Sopenharmony_ci#include <linux/ioport.h>
10862306a36Sopenharmony_ci#include <linux/interrupt.h>
10962306a36Sopenharmony_ci#include <linux/pci.h>
11062306a36Sopenharmony_ci#include <linux/netdevice.h>
11162306a36Sopenharmony_ci#include <linux/etherdevice.h>
11262306a36Sopenharmony_ci#include <linux/skbuff.h>
11362306a36Sopenharmony_ci#include <linux/mm.h>
11462306a36Sopenharmony_ci#include <linux/init.h>
11562306a36Sopenharmony_ci#include <linux/delay.h>
11662306a36Sopenharmony_ci#include <linux/ethtool.h>
11762306a36Sopenharmony_ci#include <linux/if_vlan.h>
11862306a36Sopenharmony_ci#include <linux/crc32.h>
11962306a36Sopenharmony_ci#include <linux/bitops.h>
12062306a36Sopenharmony_ci#include <asm/processor.h>
12162306a36Sopenharmony_ci#include <asm/io.h>
12262306a36Sopenharmony_ci#include <linux/uaccess.h>
12362306a36Sopenharmony_ci#include <linux/in6.h>
12462306a36Sopenharmony_ci#include <linux/dma-mapping.h>
12562306a36Sopenharmony_ci#include <linux/firmware.h>
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci#include "typhoon.h"
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ciMODULE_AUTHOR("David Dillow <dave@thedillows.org>");
13062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
13162306a36Sopenharmony_ciMODULE_FIRMWARE(FIRMWARE_NAME);
13262306a36Sopenharmony_ciMODULE_DESCRIPTION("3Com Typhoon Family (3C990, 3CR990, and variants)");
13362306a36Sopenharmony_ciMODULE_PARM_DESC(rx_copybreak, "Packets smaller than this are copied and "
13462306a36Sopenharmony_ci			       "the buffer given back to the NIC. Default "
13562306a36Sopenharmony_ci			       "is 200.");
13662306a36Sopenharmony_ciMODULE_PARM_DESC(use_mmio, "Use MMIO (1) or PIO(0) to access the NIC. "
13762306a36Sopenharmony_ci			   "Default is to try MMIO and fallback to PIO.");
13862306a36Sopenharmony_cimodule_param(rx_copybreak, int, 0);
13962306a36Sopenharmony_cimodule_param(use_mmio, int, 0);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci#if TXLO_ENTRIES <= (2 * MAX_SKB_FRAGS)
14262306a36Sopenharmony_ci#error TX ring too small!
14362306a36Sopenharmony_ci#endif
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistruct typhoon_card_info {
14662306a36Sopenharmony_ci	const char *name;
14762306a36Sopenharmony_ci	const int capabilities;
14862306a36Sopenharmony_ci};
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci#define TYPHOON_CRYPTO_NONE		0x00
15162306a36Sopenharmony_ci#define TYPHOON_CRYPTO_DES		0x01
15262306a36Sopenharmony_ci#define TYPHOON_CRYPTO_3DES		0x02
15362306a36Sopenharmony_ci#define	TYPHOON_CRYPTO_VARIABLE		0x04
15462306a36Sopenharmony_ci#define TYPHOON_FIBER			0x08
15562306a36Sopenharmony_ci#define TYPHOON_WAKEUP_NEEDS_RESET	0x10
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cienum typhoon_cards {
15862306a36Sopenharmony_ci	TYPHOON_TX = 0, TYPHOON_TX95, TYPHOON_TX97, TYPHOON_SVR,
15962306a36Sopenharmony_ci	TYPHOON_SVR95, TYPHOON_SVR97, TYPHOON_TXM, TYPHOON_BSVR,
16062306a36Sopenharmony_ci	TYPHOON_FX95, TYPHOON_FX97, TYPHOON_FX95SVR, TYPHOON_FX97SVR,
16162306a36Sopenharmony_ci	TYPHOON_FXM,
16262306a36Sopenharmony_ci};
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci/* directly indexed by enum typhoon_cards, above */
16562306a36Sopenharmony_cistatic struct typhoon_card_info typhoon_card_info[] = {
16662306a36Sopenharmony_ci	{ "3Com Typhoon (3C990-TX)",
16762306a36Sopenharmony_ci		TYPHOON_CRYPTO_NONE},
16862306a36Sopenharmony_ci	{ "3Com Typhoon (3CR990-TX-95)",
16962306a36Sopenharmony_ci		TYPHOON_CRYPTO_DES},
17062306a36Sopenharmony_ci	{ "3Com Typhoon (3CR990-TX-97)",
17162306a36Sopenharmony_ci	 	TYPHOON_CRYPTO_DES | TYPHOON_CRYPTO_3DES},
17262306a36Sopenharmony_ci	{ "3Com Typhoon (3C990SVR)",
17362306a36Sopenharmony_ci		TYPHOON_CRYPTO_NONE},
17462306a36Sopenharmony_ci	{ "3Com Typhoon (3CR990SVR95)",
17562306a36Sopenharmony_ci		TYPHOON_CRYPTO_DES},
17662306a36Sopenharmony_ci	{ "3Com Typhoon (3CR990SVR97)",
17762306a36Sopenharmony_ci	 	TYPHOON_CRYPTO_DES | TYPHOON_CRYPTO_3DES},
17862306a36Sopenharmony_ci	{ "3Com Typhoon2 (3C990B-TX-M)",
17962306a36Sopenharmony_ci		TYPHOON_CRYPTO_VARIABLE},
18062306a36Sopenharmony_ci	{ "3Com Typhoon2 (3C990BSVR)",
18162306a36Sopenharmony_ci		TYPHOON_CRYPTO_VARIABLE},
18262306a36Sopenharmony_ci	{ "3Com Typhoon (3CR990-FX-95)",
18362306a36Sopenharmony_ci		TYPHOON_CRYPTO_DES | TYPHOON_FIBER},
18462306a36Sopenharmony_ci	{ "3Com Typhoon (3CR990-FX-97)",
18562306a36Sopenharmony_ci	 	TYPHOON_CRYPTO_DES | TYPHOON_CRYPTO_3DES | TYPHOON_FIBER},
18662306a36Sopenharmony_ci	{ "3Com Typhoon (3CR990-FX-95 Server)",
18762306a36Sopenharmony_ci	 	TYPHOON_CRYPTO_DES | TYPHOON_FIBER},
18862306a36Sopenharmony_ci	{ "3Com Typhoon (3CR990-FX-97 Server)",
18962306a36Sopenharmony_ci	 	TYPHOON_CRYPTO_DES | TYPHOON_CRYPTO_3DES | TYPHOON_FIBER},
19062306a36Sopenharmony_ci	{ "3Com Typhoon2 (3C990B-FX-97)",
19162306a36Sopenharmony_ci		TYPHOON_CRYPTO_VARIABLE | TYPHOON_FIBER},
19262306a36Sopenharmony_ci};
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci/* Notes on the new subsystem numbering scheme:
19562306a36Sopenharmony_ci * bits 0-1 indicate crypto capabilities: (0) variable, (1) DES, or (2) 3DES
19662306a36Sopenharmony_ci * bit 4 indicates if this card has secured firmware (we don't support it)
19762306a36Sopenharmony_ci * bit 8 indicates if this is a (0) copper or (1) fiber card
19862306a36Sopenharmony_ci * bits 12-16 indicate card type: (0) client and (1) server
19962306a36Sopenharmony_ci */
20062306a36Sopenharmony_cistatic const struct pci_device_id typhoon_pci_tbl[] = {
20162306a36Sopenharmony_ci	{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990,
20262306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0,TYPHOON_TX },
20362306a36Sopenharmony_ci	{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_TX_95,
20462306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_TX95 },
20562306a36Sopenharmony_ci	{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_TX_97,
20662306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_TX97 },
20762306a36Sopenharmony_ci	{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990B,
20862306a36Sopenharmony_ci	  PCI_ANY_ID, 0x1000, 0, 0, TYPHOON_TXM },
20962306a36Sopenharmony_ci	{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990B,
21062306a36Sopenharmony_ci	  PCI_ANY_ID, 0x1102, 0, 0, TYPHOON_FXM },
21162306a36Sopenharmony_ci	{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990B,
21262306a36Sopenharmony_ci	  PCI_ANY_ID, 0x2000, 0, 0, TYPHOON_BSVR },
21362306a36Sopenharmony_ci	{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_FX,
21462306a36Sopenharmony_ci	  PCI_ANY_ID, 0x1101, 0, 0, TYPHOON_FX95 },
21562306a36Sopenharmony_ci	{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_FX,
21662306a36Sopenharmony_ci	  PCI_ANY_ID, 0x1102, 0, 0, TYPHOON_FX97 },
21762306a36Sopenharmony_ci	{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_FX,
21862306a36Sopenharmony_ci	  PCI_ANY_ID, 0x2101, 0, 0, TYPHOON_FX95SVR },
21962306a36Sopenharmony_ci	{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990_FX,
22062306a36Sopenharmony_ci	  PCI_ANY_ID, 0x2102, 0, 0, TYPHOON_FX97SVR },
22162306a36Sopenharmony_ci	{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990SVR95,
22262306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_SVR95 },
22362306a36Sopenharmony_ci	{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990SVR97,
22462306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_SVR97 },
22562306a36Sopenharmony_ci	{ PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3CR990SVR,
22662306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPHOON_SVR },
22762306a36Sopenharmony_ci	{ 0, }
22862306a36Sopenharmony_ci};
22962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, typhoon_pci_tbl);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci/* Define the shared memory area
23262306a36Sopenharmony_ci * Align everything the 3XP will normally be using.
23362306a36Sopenharmony_ci * We'll need to move/align txHi if we start using that ring.
23462306a36Sopenharmony_ci */
23562306a36Sopenharmony_ci#define __3xp_aligned	____cacheline_aligned
23662306a36Sopenharmony_cistruct typhoon_shared {
23762306a36Sopenharmony_ci	struct typhoon_interface	iface;
23862306a36Sopenharmony_ci	struct typhoon_indexes		indexes			__3xp_aligned;
23962306a36Sopenharmony_ci	struct tx_desc			txLo[TXLO_ENTRIES] 	__3xp_aligned;
24062306a36Sopenharmony_ci	struct rx_desc			rxLo[RX_ENTRIES]	__3xp_aligned;
24162306a36Sopenharmony_ci	struct rx_desc			rxHi[RX_ENTRIES]	__3xp_aligned;
24262306a36Sopenharmony_ci	struct cmd_desc			cmd[COMMAND_ENTRIES]	__3xp_aligned;
24362306a36Sopenharmony_ci	struct resp_desc		resp[RESPONSE_ENTRIES]	__3xp_aligned;
24462306a36Sopenharmony_ci	struct rx_free			rxBuff[RXFREE_ENTRIES]	__3xp_aligned;
24562306a36Sopenharmony_ci	u32				zeroWord;
24662306a36Sopenharmony_ci	struct tx_desc			txHi[TXHI_ENTRIES];
24762306a36Sopenharmony_ci} __packed;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistruct rxbuff_ent {
25062306a36Sopenharmony_ci	struct sk_buff *skb;
25162306a36Sopenharmony_ci	dma_addr_t	dma_addr;
25262306a36Sopenharmony_ci};
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistruct typhoon {
25562306a36Sopenharmony_ci	/* Tx cache line section */
25662306a36Sopenharmony_ci	struct transmit_ring 	txLoRing	____cacheline_aligned;
25762306a36Sopenharmony_ci	struct pci_dev *	tx_pdev;
25862306a36Sopenharmony_ci	void __iomem		*tx_ioaddr;
25962306a36Sopenharmony_ci	u32			txlo_dma_addr;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	/* Irq/Rx cache line section */
26262306a36Sopenharmony_ci	void __iomem		*ioaddr		____cacheline_aligned;
26362306a36Sopenharmony_ci	struct typhoon_indexes *indexes;
26462306a36Sopenharmony_ci	u8			awaiting_resp;
26562306a36Sopenharmony_ci	u8			duplex;
26662306a36Sopenharmony_ci	u8			speed;
26762306a36Sopenharmony_ci	u8			card_state;
26862306a36Sopenharmony_ci	struct basic_ring	rxLoRing;
26962306a36Sopenharmony_ci	struct pci_dev *	pdev;
27062306a36Sopenharmony_ci	struct net_device *	dev;
27162306a36Sopenharmony_ci	struct napi_struct	napi;
27262306a36Sopenharmony_ci	struct basic_ring	rxHiRing;
27362306a36Sopenharmony_ci	struct basic_ring	rxBuffRing;
27462306a36Sopenharmony_ci	struct rxbuff_ent	rxbuffers[RXENT_ENTRIES];
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	/* general section */
27762306a36Sopenharmony_ci	spinlock_t		command_lock	____cacheline_aligned;
27862306a36Sopenharmony_ci	struct basic_ring	cmdRing;
27962306a36Sopenharmony_ci	struct basic_ring	respRing;
28062306a36Sopenharmony_ci	struct net_device_stats	stats_saved;
28162306a36Sopenharmony_ci	struct typhoon_shared *	shared;
28262306a36Sopenharmony_ci	dma_addr_t		shared_dma;
28362306a36Sopenharmony_ci	__le16			xcvr_select;
28462306a36Sopenharmony_ci	__le16			wol_events;
28562306a36Sopenharmony_ci	__le32			offload;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	/* unused stuff (future use) */
28862306a36Sopenharmony_ci	int			capabilities;
28962306a36Sopenharmony_ci	struct transmit_ring 	txHiRing;
29062306a36Sopenharmony_ci};
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cienum completion_wait_values {
29362306a36Sopenharmony_ci	NoWait = 0, WaitNoSleep, WaitSleep,
29462306a36Sopenharmony_ci};
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci/* These are the values for the typhoon.card_state variable.
29762306a36Sopenharmony_ci * These determine where the statistics will come from in get_stats().
29862306a36Sopenharmony_ci * The sleep image does not support the statistics we need.
29962306a36Sopenharmony_ci */
30062306a36Sopenharmony_cienum state_values {
30162306a36Sopenharmony_ci	Sleeping = 0, Running,
30262306a36Sopenharmony_ci};
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci/* PCI writes are not guaranteed to be posted in order, but outstanding writes
30562306a36Sopenharmony_ci * cannot pass a read, so this forces current writes to post.
30662306a36Sopenharmony_ci */
30762306a36Sopenharmony_ci#define typhoon_post_pci_writes(x) \
30862306a36Sopenharmony_ci	do { if (likely(use_mmio)) ioread32(x+TYPHOON_REG_HEARTBEAT); } while (0)
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci/* We'll wait up to six seconds for a reset, and half a second normally.
31162306a36Sopenharmony_ci */
31262306a36Sopenharmony_ci#define TYPHOON_UDELAY			50
31362306a36Sopenharmony_ci#define TYPHOON_RESET_TIMEOUT_SLEEP	(6 * HZ)
31462306a36Sopenharmony_ci#define TYPHOON_RESET_TIMEOUT_NOSLEEP	((6 * 1000000) / TYPHOON_UDELAY)
31562306a36Sopenharmony_ci#define TYPHOON_WAIT_TIMEOUT		((1000000 / 2) / TYPHOON_UDELAY)
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci#if defined(NETIF_F_TSO)
31862306a36Sopenharmony_ci#define skb_tso_size(x)		(skb_shinfo(x)->gso_size)
31962306a36Sopenharmony_ci#define TSO_NUM_DESCRIPTORS	2
32062306a36Sopenharmony_ci#define TSO_OFFLOAD_ON		TYPHOON_OFFLOAD_TCP_SEGMENT
32162306a36Sopenharmony_ci#else
32262306a36Sopenharmony_ci#define NETIF_F_TSO 		0
32362306a36Sopenharmony_ci#define skb_tso_size(x) 	0
32462306a36Sopenharmony_ci#define TSO_NUM_DESCRIPTORS	0
32562306a36Sopenharmony_ci#define TSO_OFFLOAD_ON		0
32662306a36Sopenharmony_ci#endif
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic inline void
32962306a36Sopenharmony_cityphoon_inc_index(u32 *index, const int count, const int num_entries)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	/* Increment a ring index -- we can use this for all rings execept
33262306a36Sopenharmony_ci	 * the Rx rings, as they use different size descriptors
33362306a36Sopenharmony_ci	 * otherwise, everything is the same size as a cmd_desc
33462306a36Sopenharmony_ci	 */
33562306a36Sopenharmony_ci	*index += count * sizeof(struct cmd_desc);
33662306a36Sopenharmony_ci	*index %= num_entries * sizeof(struct cmd_desc);
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic inline void
34062306a36Sopenharmony_cityphoon_inc_cmd_index(u32 *index, const int count)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	typhoon_inc_index(index, count, COMMAND_ENTRIES);
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic inline void
34662306a36Sopenharmony_cityphoon_inc_resp_index(u32 *index, const int count)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	typhoon_inc_index(index, count, RESPONSE_ENTRIES);
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic inline void
35262306a36Sopenharmony_cityphoon_inc_rxfree_index(u32 *index, const int count)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	typhoon_inc_index(index, count, RXFREE_ENTRIES);
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic inline void
35862306a36Sopenharmony_cityphoon_inc_tx_index(u32 *index, const int count)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	/* if we start using the Hi Tx ring, this needs updating */
36162306a36Sopenharmony_ci	typhoon_inc_index(index, count, TXLO_ENTRIES);
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic inline void
36562306a36Sopenharmony_cityphoon_inc_rx_index(u32 *index, const int count)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	/* sizeof(struct rx_desc) != sizeof(struct cmd_desc) */
36862306a36Sopenharmony_ci	*index += count * sizeof(struct rx_desc);
36962306a36Sopenharmony_ci	*index %= RX_ENTRIES * sizeof(struct rx_desc);
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic int
37362306a36Sopenharmony_cityphoon_reset(void __iomem *ioaddr, int wait_type)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	int i, err = 0;
37662306a36Sopenharmony_ci	int timeout;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	if (wait_type == WaitNoSleep)
37962306a36Sopenharmony_ci		timeout = TYPHOON_RESET_TIMEOUT_NOSLEEP;
38062306a36Sopenharmony_ci	else
38162306a36Sopenharmony_ci		timeout = TYPHOON_RESET_TIMEOUT_SLEEP;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
38462306a36Sopenharmony_ci	iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_STATUS);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	iowrite32(TYPHOON_RESET_ALL, ioaddr + TYPHOON_REG_SOFT_RESET);
38762306a36Sopenharmony_ci	typhoon_post_pci_writes(ioaddr);
38862306a36Sopenharmony_ci	udelay(1);
38962306a36Sopenharmony_ci	iowrite32(TYPHOON_RESET_NONE, ioaddr + TYPHOON_REG_SOFT_RESET);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	if (wait_type != NoWait) {
39262306a36Sopenharmony_ci		for (i = 0; i < timeout; i++) {
39362306a36Sopenharmony_ci			if (ioread32(ioaddr + TYPHOON_REG_STATUS) ==
39462306a36Sopenharmony_ci			   TYPHOON_STATUS_WAITING_FOR_HOST)
39562306a36Sopenharmony_ci				goto out;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci			if (wait_type == WaitSleep)
39862306a36Sopenharmony_ci				schedule_timeout_uninterruptible(1);
39962306a36Sopenharmony_ci			else
40062306a36Sopenharmony_ci				udelay(TYPHOON_UDELAY);
40162306a36Sopenharmony_ci		}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci		err = -ETIMEDOUT;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ciout:
40762306a36Sopenharmony_ci	iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
40862306a36Sopenharmony_ci	iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_STATUS);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	/* The 3XP seems to need a little extra time to complete the load
41162306a36Sopenharmony_ci	 * of the sleep image before we can reliably boot it. Failure to
41262306a36Sopenharmony_ci	 * do this occasionally results in a hung adapter after boot in
41362306a36Sopenharmony_ci	 * typhoon_init_one() while trying to read the MAC address or
41462306a36Sopenharmony_ci	 * putting the card to sleep. 3Com's driver waits 5ms, but
41562306a36Sopenharmony_ci	 * that seems to be overkill. However, if we can sleep, we might
41662306a36Sopenharmony_ci	 * as well give it that much time. Otherwise, we'll give it 500us,
41762306a36Sopenharmony_ci	 * which should be enough (I've see it work well at 100us, but still
41862306a36Sopenharmony_ci	 * saw occasional problems.)
41962306a36Sopenharmony_ci	 */
42062306a36Sopenharmony_ci	if (wait_type == WaitSleep)
42162306a36Sopenharmony_ci		msleep(5);
42262306a36Sopenharmony_ci	else
42362306a36Sopenharmony_ci		udelay(500);
42462306a36Sopenharmony_ci	return err;
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic int
42862306a36Sopenharmony_cityphoon_wait_status(void __iomem *ioaddr, u32 wait_value)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	int i, err = 0;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	for (i = 0; i < TYPHOON_WAIT_TIMEOUT; i++) {
43362306a36Sopenharmony_ci		if (ioread32(ioaddr + TYPHOON_REG_STATUS) == wait_value)
43462306a36Sopenharmony_ci			goto out;
43562306a36Sopenharmony_ci		udelay(TYPHOON_UDELAY);
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	err = -ETIMEDOUT;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ciout:
44162306a36Sopenharmony_ci	return err;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic inline void
44562306a36Sopenharmony_cityphoon_media_status(struct net_device *dev, struct resp_desc *resp)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	if (resp->parm1 & TYPHOON_MEDIA_STAT_NO_LINK)
44862306a36Sopenharmony_ci		netif_carrier_off(dev);
44962306a36Sopenharmony_ci	else
45062306a36Sopenharmony_ci		netif_carrier_on(dev);
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_cistatic inline void
45462306a36Sopenharmony_cityphoon_hello(struct typhoon *tp)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	struct basic_ring *ring = &tp->cmdRing;
45762306a36Sopenharmony_ci	struct cmd_desc *cmd;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	/* We only get a hello request if we've not sent anything to the
46062306a36Sopenharmony_ci	 * card in a long while. If the lock is held, then we're in the
46162306a36Sopenharmony_ci	 * process of issuing a command, so we don't need to respond.
46262306a36Sopenharmony_ci	 */
46362306a36Sopenharmony_ci	if (spin_trylock(&tp->command_lock)) {
46462306a36Sopenharmony_ci		cmd = (struct cmd_desc *)(ring->ringBase + ring->lastWrite);
46562306a36Sopenharmony_ci		typhoon_inc_cmd_index(&ring->lastWrite, 1);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci		INIT_COMMAND_NO_RESPONSE(cmd, TYPHOON_CMD_HELLO_RESP);
46862306a36Sopenharmony_ci		wmb();
46962306a36Sopenharmony_ci		iowrite32(ring->lastWrite, tp->ioaddr + TYPHOON_REG_CMD_READY);
47062306a36Sopenharmony_ci		spin_unlock(&tp->command_lock);
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistatic int
47562306a36Sopenharmony_cityphoon_process_response(struct typhoon *tp, int resp_size,
47662306a36Sopenharmony_ci				struct resp_desc *resp_save)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	struct typhoon_indexes *indexes = tp->indexes;
47962306a36Sopenharmony_ci	struct resp_desc *resp;
48062306a36Sopenharmony_ci	u8 *base = tp->respRing.ringBase;
48162306a36Sopenharmony_ci	int count, len, wrap_len;
48262306a36Sopenharmony_ci	u32 cleared;
48362306a36Sopenharmony_ci	u32 ready;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	cleared = le32_to_cpu(indexes->respCleared);
48662306a36Sopenharmony_ci	ready = le32_to_cpu(indexes->respReady);
48762306a36Sopenharmony_ci	while (cleared != ready) {
48862306a36Sopenharmony_ci		resp = (struct resp_desc *)(base + cleared);
48962306a36Sopenharmony_ci		count = resp->numDesc + 1;
49062306a36Sopenharmony_ci		if (resp_save && resp->seqNo) {
49162306a36Sopenharmony_ci			if (count > resp_size) {
49262306a36Sopenharmony_ci				resp_save->flags = TYPHOON_RESP_ERROR;
49362306a36Sopenharmony_ci				goto cleanup;
49462306a36Sopenharmony_ci			}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci			wrap_len = 0;
49762306a36Sopenharmony_ci			len = count * sizeof(*resp);
49862306a36Sopenharmony_ci			if (unlikely(cleared + len > RESPONSE_RING_SIZE)) {
49962306a36Sopenharmony_ci				wrap_len = cleared + len - RESPONSE_RING_SIZE;
50062306a36Sopenharmony_ci				len = RESPONSE_RING_SIZE - cleared;
50162306a36Sopenharmony_ci			}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci			memcpy(resp_save, resp, len);
50462306a36Sopenharmony_ci			if (unlikely(wrap_len)) {
50562306a36Sopenharmony_ci				resp_save += len / sizeof(*resp);
50662306a36Sopenharmony_ci				memcpy(resp_save, base, wrap_len);
50762306a36Sopenharmony_ci			}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci			resp_save = NULL;
51062306a36Sopenharmony_ci		} else if (resp->cmd == TYPHOON_CMD_READ_MEDIA_STATUS) {
51162306a36Sopenharmony_ci			typhoon_media_status(tp->dev, resp);
51262306a36Sopenharmony_ci		} else if (resp->cmd == TYPHOON_CMD_HELLO_RESP) {
51362306a36Sopenharmony_ci			typhoon_hello(tp);
51462306a36Sopenharmony_ci		} else {
51562306a36Sopenharmony_ci			netdev_err(tp->dev,
51662306a36Sopenharmony_ci				   "dumping unexpected response 0x%04x:%d:0x%02x:0x%04x:%08x:%08x\n",
51762306a36Sopenharmony_ci				   le16_to_cpu(resp->cmd),
51862306a36Sopenharmony_ci				   resp->numDesc, resp->flags,
51962306a36Sopenharmony_ci				   le16_to_cpu(resp->parm1),
52062306a36Sopenharmony_ci				   le32_to_cpu(resp->parm2),
52162306a36Sopenharmony_ci				   le32_to_cpu(resp->parm3));
52262306a36Sopenharmony_ci		}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cicleanup:
52562306a36Sopenharmony_ci		typhoon_inc_resp_index(&cleared, count);
52662306a36Sopenharmony_ci	}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	indexes->respCleared = cpu_to_le32(cleared);
52962306a36Sopenharmony_ci	wmb();
53062306a36Sopenharmony_ci	return resp_save == NULL;
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic inline int
53462306a36Sopenharmony_cityphoon_num_free(int lastWrite, int lastRead, int ringSize)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	/* this works for all descriptors but rx_desc, as they are a
53762306a36Sopenharmony_ci	 * different size than the cmd_desc -- everyone else is the same
53862306a36Sopenharmony_ci	 */
53962306a36Sopenharmony_ci	lastWrite /= sizeof(struct cmd_desc);
54062306a36Sopenharmony_ci	lastRead /= sizeof(struct cmd_desc);
54162306a36Sopenharmony_ci	return (ringSize + lastRead - lastWrite - 1) % ringSize;
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cistatic inline int
54562306a36Sopenharmony_cityphoon_num_free_cmd(struct typhoon *tp)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	int lastWrite = tp->cmdRing.lastWrite;
54862306a36Sopenharmony_ci	int cmdCleared = le32_to_cpu(tp->indexes->cmdCleared);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	return typhoon_num_free(lastWrite, cmdCleared, COMMAND_ENTRIES);
55162306a36Sopenharmony_ci}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic inline int
55462306a36Sopenharmony_cityphoon_num_free_resp(struct typhoon *tp)
55562306a36Sopenharmony_ci{
55662306a36Sopenharmony_ci	int respReady = le32_to_cpu(tp->indexes->respReady);
55762306a36Sopenharmony_ci	int respCleared = le32_to_cpu(tp->indexes->respCleared);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	return typhoon_num_free(respReady, respCleared, RESPONSE_ENTRIES);
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic inline int
56362306a36Sopenharmony_cityphoon_num_free_tx(struct transmit_ring *ring)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	/* if we start using the Hi Tx ring, this needs updating */
56662306a36Sopenharmony_ci	return typhoon_num_free(ring->lastWrite, ring->lastRead, TXLO_ENTRIES);
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_cistatic int
57062306a36Sopenharmony_cityphoon_issue_command(struct typhoon *tp, int num_cmd, struct cmd_desc *cmd,
57162306a36Sopenharmony_ci		      int num_resp, struct resp_desc *resp)
57262306a36Sopenharmony_ci{
57362306a36Sopenharmony_ci	struct typhoon_indexes *indexes = tp->indexes;
57462306a36Sopenharmony_ci	struct basic_ring *ring = &tp->cmdRing;
57562306a36Sopenharmony_ci	struct resp_desc local_resp;
57662306a36Sopenharmony_ci	int i, err = 0;
57762306a36Sopenharmony_ci	int got_resp;
57862306a36Sopenharmony_ci	int freeCmd, freeResp;
57962306a36Sopenharmony_ci	int len, wrap_len;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	spin_lock(&tp->command_lock);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	freeCmd = typhoon_num_free_cmd(tp);
58462306a36Sopenharmony_ci	freeResp = typhoon_num_free_resp(tp);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	if (freeCmd < num_cmd || freeResp < num_resp) {
58762306a36Sopenharmony_ci		netdev_err(tp->dev, "no descs for cmd, had (needed) %d (%d) cmd, %d (%d) resp\n",
58862306a36Sopenharmony_ci			   freeCmd, num_cmd, freeResp, num_resp);
58962306a36Sopenharmony_ci		err = -ENOMEM;
59062306a36Sopenharmony_ci		goto out;
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	if (cmd->flags & TYPHOON_CMD_RESPOND) {
59462306a36Sopenharmony_ci		/* If we're expecting a response, but the caller hasn't given
59562306a36Sopenharmony_ci		 * us a place to put it, we'll provide one.
59662306a36Sopenharmony_ci		 */
59762306a36Sopenharmony_ci		tp->awaiting_resp = 1;
59862306a36Sopenharmony_ci		if (resp == NULL) {
59962306a36Sopenharmony_ci			resp = &local_resp;
60062306a36Sopenharmony_ci			num_resp = 1;
60162306a36Sopenharmony_ci		}
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	wrap_len = 0;
60562306a36Sopenharmony_ci	len = num_cmd * sizeof(*cmd);
60662306a36Sopenharmony_ci	if (unlikely(ring->lastWrite + len > COMMAND_RING_SIZE)) {
60762306a36Sopenharmony_ci		wrap_len = ring->lastWrite + len - COMMAND_RING_SIZE;
60862306a36Sopenharmony_ci		len = COMMAND_RING_SIZE - ring->lastWrite;
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	memcpy(ring->ringBase + ring->lastWrite, cmd, len);
61262306a36Sopenharmony_ci	if (unlikely(wrap_len)) {
61362306a36Sopenharmony_ci		struct cmd_desc *wrap_ptr = cmd;
61462306a36Sopenharmony_ci		wrap_ptr += len / sizeof(*cmd);
61562306a36Sopenharmony_ci		memcpy(ring->ringBase, wrap_ptr, wrap_len);
61662306a36Sopenharmony_ci	}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	typhoon_inc_cmd_index(&ring->lastWrite, num_cmd);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	/* "I feel a presence... another warrior is on the mesa."
62162306a36Sopenharmony_ci	 */
62262306a36Sopenharmony_ci	wmb();
62362306a36Sopenharmony_ci	iowrite32(ring->lastWrite, tp->ioaddr + TYPHOON_REG_CMD_READY);
62462306a36Sopenharmony_ci	typhoon_post_pci_writes(tp->ioaddr);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	if ((cmd->flags & TYPHOON_CMD_RESPOND) == 0)
62762306a36Sopenharmony_ci		goto out;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	/* Ugh. We'll be here about 8ms, spinning our thumbs, unable to
63062306a36Sopenharmony_ci	 * preempt or do anything other than take interrupts. So, don't
63162306a36Sopenharmony_ci	 * wait for a response unless you have to.
63262306a36Sopenharmony_ci	 *
63362306a36Sopenharmony_ci	 * I've thought about trying to sleep here, but we're called
63462306a36Sopenharmony_ci	 * from many contexts that don't allow that. Also, given the way
63562306a36Sopenharmony_ci	 * 3Com has implemented irq coalescing, we would likely timeout --
63662306a36Sopenharmony_ci	 * this has been observed in real life!
63762306a36Sopenharmony_ci	 *
63862306a36Sopenharmony_ci	 * The big killer is we have to wait to get stats from the card,
63962306a36Sopenharmony_ci	 * though we could go to a periodic refresh of those if we don't
64062306a36Sopenharmony_ci	 * mind them getting somewhat stale. The rest of the waiting
64162306a36Sopenharmony_ci	 * commands occur during open/close/suspend/resume, so they aren't
64262306a36Sopenharmony_ci	 * time critical. Creating SAs in the future will also have to
64362306a36Sopenharmony_ci	 * wait here.
64462306a36Sopenharmony_ci	 */
64562306a36Sopenharmony_ci	got_resp = 0;
64662306a36Sopenharmony_ci	for (i = 0; i < TYPHOON_WAIT_TIMEOUT && !got_resp; i++) {
64762306a36Sopenharmony_ci		if (indexes->respCleared != indexes->respReady)
64862306a36Sopenharmony_ci			got_resp = typhoon_process_response(tp, num_resp,
64962306a36Sopenharmony_ci								resp);
65062306a36Sopenharmony_ci		udelay(TYPHOON_UDELAY);
65162306a36Sopenharmony_ci	}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (!got_resp) {
65462306a36Sopenharmony_ci		err = -ETIMEDOUT;
65562306a36Sopenharmony_ci		goto out;
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	/* Collect the error response even if we don't care about the
65962306a36Sopenharmony_ci	 * rest of the response
66062306a36Sopenharmony_ci	 */
66162306a36Sopenharmony_ci	if (resp->flags & TYPHOON_RESP_ERROR)
66262306a36Sopenharmony_ci		err = -EIO;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ciout:
66562306a36Sopenharmony_ci	if (tp->awaiting_resp) {
66662306a36Sopenharmony_ci		tp->awaiting_resp = 0;
66762306a36Sopenharmony_ci		smp_wmb();
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci		/* Ugh. If a response was added to the ring between
67062306a36Sopenharmony_ci		 * the call to typhoon_process_response() and the clearing
67162306a36Sopenharmony_ci		 * of tp->awaiting_resp, we could have missed the interrupt
67262306a36Sopenharmony_ci		 * and it could hang in the ring an indeterminate amount of
67362306a36Sopenharmony_ci		 * time. So, check for it, and interrupt ourselves if this
67462306a36Sopenharmony_ci		 * is the case.
67562306a36Sopenharmony_ci		 */
67662306a36Sopenharmony_ci		if (indexes->respCleared != indexes->respReady)
67762306a36Sopenharmony_ci			iowrite32(1, tp->ioaddr + TYPHOON_REG_SELF_INTERRUPT);
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	spin_unlock(&tp->command_lock);
68162306a36Sopenharmony_ci	return err;
68262306a36Sopenharmony_ci}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_cistatic inline void
68562306a36Sopenharmony_cityphoon_tso_fill(struct sk_buff *skb, struct transmit_ring *txRing,
68662306a36Sopenharmony_ci			u32 ring_dma)
68762306a36Sopenharmony_ci{
68862306a36Sopenharmony_ci	struct tcpopt_desc *tcpd;
68962306a36Sopenharmony_ci	u32 tcpd_offset = ring_dma;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	tcpd = (struct tcpopt_desc *) (txRing->ringBase + txRing->lastWrite);
69262306a36Sopenharmony_ci	tcpd_offset += txRing->lastWrite;
69362306a36Sopenharmony_ci	tcpd_offset += offsetof(struct tcpopt_desc, bytesTx);
69462306a36Sopenharmony_ci	typhoon_inc_tx_index(&txRing->lastWrite, 1);
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	tcpd->flags = TYPHOON_OPT_DESC | TYPHOON_OPT_TCP_SEG;
69762306a36Sopenharmony_ci	tcpd->numDesc = 1;
69862306a36Sopenharmony_ci	tcpd->mss_flags = cpu_to_le16(skb_tso_size(skb));
69962306a36Sopenharmony_ci	tcpd->mss_flags |= TYPHOON_TSO_FIRST | TYPHOON_TSO_LAST;
70062306a36Sopenharmony_ci	tcpd->respAddrLo = cpu_to_le32(tcpd_offset);
70162306a36Sopenharmony_ci	tcpd->bytesTx = cpu_to_le32(skb->len);
70262306a36Sopenharmony_ci	tcpd->status = 0;
70362306a36Sopenharmony_ci}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_cistatic netdev_tx_t
70662306a36Sopenharmony_cityphoon_start_tx(struct sk_buff *skb, struct net_device *dev)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	struct typhoon *tp = netdev_priv(dev);
70962306a36Sopenharmony_ci	struct transmit_ring *txRing;
71062306a36Sopenharmony_ci	struct tx_desc *txd, *first_txd;
71162306a36Sopenharmony_ci	dma_addr_t skb_dma;
71262306a36Sopenharmony_ci	int numDesc;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	/* we have two rings to choose from, but we only use txLo for now
71562306a36Sopenharmony_ci	 * If we start using the Hi ring as well, we'll need to update
71662306a36Sopenharmony_ci	 * typhoon_stop_runtime(), typhoon_interrupt(), typhoon_num_free_tx(),
71762306a36Sopenharmony_ci	 * and TXHI_ENTRIES to match, as well as update the TSO code below
71862306a36Sopenharmony_ci	 * to get the right DMA address
71962306a36Sopenharmony_ci	 */
72062306a36Sopenharmony_ci	txRing = &tp->txLoRing;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	/* We need one descriptor for each fragment of the sk_buff, plus the
72362306a36Sopenharmony_ci	 * one for the ->data area of it.
72462306a36Sopenharmony_ci	 *
72562306a36Sopenharmony_ci	 * The docs say a maximum of 16 fragment descriptors per TCP option
72662306a36Sopenharmony_ci	 * descriptor, then make a new packet descriptor and option descriptor
72762306a36Sopenharmony_ci	 * for the next 16 fragments. The engineers say just an option
72862306a36Sopenharmony_ci	 * descriptor is needed. I've tested up to 26 fragments with a single
72962306a36Sopenharmony_ci	 * packet descriptor/option descriptor combo, so I use that for now.
73062306a36Sopenharmony_ci	 *
73162306a36Sopenharmony_ci	 * If problems develop with TSO, check this first.
73262306a36Sopenharmony_ci	 */
73362306a36Sopenharmony_ci	numDesc = skb_shinfo(skb)->nr_frags + 1;
73462306a36Sopenharmony_ci	if (skb_is_gso(skb))
73562306a36Sopenharmony_ci		numDesc++;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	/* When checking for free space in the ring, we need to also
73862306a36Sopenharmony_ci	 * account for the initial Tx descriptor, and we always must leave
73962306a36Sopenharmony_ci	 * at least one descriptor unused in the ring so that it doesn't
74062306a36Sopenharmony_ci	 * wrap and look empty.
74162306a36Sopenharmony_ci	 *
74262306a36Sopenharmony_ci	 * The only time we should loop here is when we hit the race
74362306a36Sopenharmony_ci	 * between marking the queue awake and updating the cleared index.
74462306a36Sopenharmony_ci	 * Just loop and it will appear. This comes from the acenic driver.
74562306a36Sopenharmony_ci	 */
74662306a36Sopenharmony_ci	while (unlikely(typhoon_num_free_tx(txRing) < (numDesc + 2)))
74762306a36Sopenharmony_ci		smp_rmb();
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	first_txd = (struct tx_desc *) (txRing->ringBase + txRing->lastWrite);
75062306a36Sopenharmony_ci	typhoon_inc_tx_index(&txRing->lastWrite, 1);
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	first_txd->flags = TYPHOON_TX_DESC | TYPHOON_DESC_VALID;
75362306a36Sopenharmony_ci	first_txd->numDesc = 0;
75462306a36Sopenharmony_ci	first_txd->len = 0;
75562306a36Sopenharmony_ci	first_txd->tx_addr = (u64)((unsigned long) skb);
75662306a36Sopenharmony_ci	first_txd->processFlags = 0;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	if (skb->ip_summed == CHECKSUM_PARTIAL) {
75962306a36Sopenharmony_ci		/* The 3XP will figure out if this is UDP/TCP */
76062306a36Sopenharmony_ci		first_txd->processFlags |= TYPHOON_TX_PF_TCP_CHKSUM;
76162306a36Sopenharmony_ci		first_txd->processFlags |= TYPHOON_TX_PF_UDP_CHKSUM;
76262306a36Sopenharmony_ci		first_txd->processFlags |= TYPHOON_TX_PF_IP_CHKSUM;
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	if (skb_vlan_tag_present(skb)) {
76662306a36Sopenharmony_ci		first_txd->processFlags |=
76762306a36Sopenharmony_ci		    TYPHOON_TX_PF_INSERT_VLAN | TYPHOON_TX_PF_VLAN_PRIORITY;
76862306a36Sopenharmony_ci		first_txd->processFlags |=
76962306a36Sopenharmony_ci		    cpu_to_le32(htons(skb_vlan_tag_get(skb)) <<
77062306a36Sopenharmony_ci				TYPHOON_TX_PF_VLAN_TAG_SHIFT);
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	if (skb_is_gso(skb)) {
77462306a36Sopenharmony_ci		first_txd->processFlags |= TYPHOON_TX_PF_TCP_SEGMENT;
77562306a36Sopenharmony_ci		first_txd->numDesc++;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci		typhoon_tso_fill(skb, txRing, tp->txlo_dma_addr);
77862306a36Sopenharmony_ci	}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	txd = (struct tx_desc *) (txRing->ringBase + txRing->lastWrite);
78162306a36Sopenharmony_ci	typhoon_inc_tx_index(&txRing->lastWrite, 1);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	/* No need to worry about padding packet -- the firmware pads
78462306a36Sopenharmony_ci	 * it with zeros to ETH_ZLEN for us.
78562306a36Sopenharmony_ci	 */
78662306a36Sopenharmony_ci	if (skb_shinfo(skb)->nr_frags == 0) {
78762306a36Sopenharmony_ci		skb_dma = dma_map_single(&tp->tx_pdev->dev, skb->data,
78862306a36Sopenharmony_ci					 skb->len, DMA_TO_DEVICE);
78962306a36Sopenharmony_ci		txd->flags = TYPHOON_FRAG_DESC | TYPHOON_DESC_VALID;
79062306a36Sopenharmony_ci		txd->len = cpu_to_le16(skb->len);
79162306a36Sopenharmony_ci		txd->frag.addr = cpu_to_le32(skb_dma);
79262306a36Sopenharmony_ci		txd->frag.addrHi = 0;
79362306a36Sopenharmony_ci		first_txd->numDesc++;
79462306a36Sopenharmony_ci	} else {
79562306a36Sopenharmony_ci		int i, len;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci		len = skb_headlen(skb);
79862306a36Sopenharmony_ci		skb_dma = dma_map_single(&tp->tx_pdev->dev, skb->data, len,
79962306a36Sopenharmony_ci					 DMA_TO_DEVICE);
80062306a36Sopenharmony_ci		txd->flags = TYPHOON_FRAG_DESC | TYPHOON_DESC_VALID;
80162306a36Sopenharmony_ci		txd->len = cpu_to_le16(len);
80262306a36Sopenharmony_ci		txd->frag.addr = cpu_to_le32(skb_dma);
80362306a36Sopenharmony_ci		txd->frag.addrHi = 0;
80462306a36Sopenharmony_ci		first_txd->numDesc++;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
80762306a36Sopenharmony_ci			const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
80862306a36Sopenharmony_ci			void *frag_addr;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci			txd = (struct tx_desc *) (txRing->ringBase +
81162306a36Sopenharmony_ci						txRing->lastWrite);
81262306a36Sopenharmony_ci			typhoon_inc_tx_index(&txRing->lastWrite, 1);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci			len = skb_frag_size(frag);
81562306a36Sopenharmony_ci			frag_addr = skb_frag_address(frag);
81662306a36Sopenharmony_ci			skb_dma = dma_map_single(&tp->tx_pdev->dev, frag_addr,
81762306a36Sopenharmony_ci						 len, DMA_TO_DEVICE);
81862306a36Sopenharmony_ci			txd->flags = TYPHOON_FRAG_DESC | TYPHOON_DESC_VALID;
81962306a36Sopenharmony_ci			txd->len = cpu_to_le16(len);
82062306a36Sopenharmony_ci			txd->frag.addr = cpu_to_le32(skb_dma);
82162306a36Sopenharmony_ci			txd->frag.addrHi = 0;
82262306a36Sopenharmony_ci			first_txd->numDesc++;
82362306a36Sopenharmony_ci		}
82462306a36Sopenharmony_ci	}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	/* Kick the 3XP
82762306a36Sopenharmony_ci	 */
82862306a36Sopenharmony_ci	wmb();
82962306a36Sopenharmony_ci	iowrite32(txRing->lastWrite, tp->tx_ioaddr + txRing->writeRegister);
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	/* If we don't have room to put the worst case packet on the
83262306a36Sopenharmony_ci	 * queue, then we must stop the queue. We need 2 extra
83362306a36Sopenharmony_ci	 * descriptors -- one to prevent ring wrap, and one for the
83462306a36Sopenharmony_ci	 * Tx header.
83562306a36Sopenharmony_ci	 */
83662306a36Sopenharmony_ci	numDesc = MAX_SKB_FRAGS + TSO_NUM_DESCRIPTORS + 1;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	if (typhoon_num_free_tx(txRing) < (numDesc + 2)) {
83962306a36Sopenharmony_ci		netif_stop_queue(dev);
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci		/* A Tx complete IRQ could have gotten between, making
84262306a36Sopenharmony_ci		 * the ring free again. Only need to recheck here, since
84362306a36Sopenharmony_ci		 * Tx is serialized.
84462306a36Sopenharmony_ci		 */
84562306a36Sopenharmony_ci		if (typhoon_num_free_tx(txRing) >= (numDesc + 2))
84662306a36Sopenharmony_ci			netif_wake_queue(dev);
84762306a36Sopenharmony_ci	}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	return NETDEV_TX_OK;
85062306a36Sopenharmony_ci}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_cistatic void
85362306a36Sopenharmony_cityphoon_set_rx_mode(struct net_device *dev)
85462306a36Sopenharmony_ci{
85562306a36Sopenharmony_ci	struct typhoon *tp = netdev_priv(dev);
85662306a36Sopenharmony_ci	struct cmd_desc xp_cmd;
85762306a36Sopenharmony_ci	u32 mc_filter[2];
85862306a36Sopenharmony_ci	__le16 filter;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	filter = TYPHOON_RX_FILTER_DIRECTED | TYPHOON_RX_FILTER_BROADCAST;
86162306a36Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {
86262306a36Sopenharmony_ci		filter |= TYPHOON_RX_FILTER_PROMISCOUS;
86362306a36Sopenharmony_ci	} else if ((netdev_mc_count(dev) > multicast_filter_limit) ||
86462306a36Sopenharmony_ci		  (dev->flags & IFF_ALLMULTI)) {
86562306a36Sopenharmony_ci		/* Too many to match, or accept all multicasts. */
86662306a36Sopenharmony_ci		filter |= TYPHOON_RX_FILTER_ALL_MCAST;
86762306a36Sopenharmony_ci	} else if (!netdev_mc_empty(dev)) {
86862306a36Sopenharmony_ci		struct netdev_hw_addr *ha;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci		memset(mc_filter, 0, sizeof(mc_filter));
87162306a36Sopenharmony_ci		netdev_for_each_mc_addr(ha, dev) {
87262306a36Sopenharmony_ci			int bit = ether_crc(ETH_ALEN, ha->addr) & 0x3f;
87362306a36Sopenharmony_ci			mc_filter[bit >> 5] |= 1 << (bit & 0x1f);
87462306a36Sopenharmony_ci		}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci		INIT_COMMAND_NO_RESPONSE(&xp_cmd,
87762306a36Sopenharmony_ci					 TYPHOON_CMD_SET_MULTICAST_HASH);
87862306a36Sopenharmony_ci		xp_cmd.parm1 = TYPHOON_MCAST_HASH_SET;
87962306a36Sopenharmony_ci		xp_cmd.parm2 = cpu_to_le32(mc_filter[0]);
88062306a36Sopenharmony_ci		xp_cmd.parm3 = cpu_to_le32(mc_filter[1]);
88162306a36Sopenharmony_ci		typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci		filter |= TYPHOON_RX_FILTER_MCAST_HASH;
88462306a36Sopenharmony_ci	}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_SET_RX_FILTER);
88762306a36Sopenharmony_ci	xp_cmd.parm1 = filter;
88862306a36Sopenharmony_ci	typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
88962306a36Sopenharmony_ci}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_cistatic int
89262306a36Sopenharmony_cityphoon_do_get_stats(struct typhoon *tp)
89362306a36Sopenharmony_ci{
89462306a36Sopenharmony_ci	struct net_device_stats *stats = &tp->dev->stats;
89562306a36Sopenharmony_ci	struct net_device_stats *saved = &tp->stats_saved;
89662306a36Sopenharmony_ci	struct cmd_desc xp_cmd;
89762306a36Sopenharmony_ci	struct resp_desc xp_resp[7];
89862306a36Sopenharmony_ci	struct stats_resp *s = (struct stats_resp *) xp_resp;
89962306a36Sopenharmony_ci	int err;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_READ_STATS);
90262306a36Sopenharmony_ci	err = typhoon_issue_command(tp, 1, &xp_cmd, 7, xp_resp);
90362306a36Sopenharmony_ci	if (err < 0)
90462306a36Sopenharmony_ci		return err;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	/* 3Com's Linux driver uses txMultipleCollisions as it's
90762306a36Sopenharmony_ci	 * collisions value, but there is some other collision info as well...
90862306a36Sopenharmony_ci	 *
90962306a36Sopenharmony_ci	 * The extra status reported would be a good candidate for
91062306a36Sopenharmony_ci	 * ethtool_ops->get_{strings,stats}()
91162306a36Sopenharmony_ci	 */
91262306a36Sopenharmony_ci	stats->tx_packets = le32_to_cpu(s->txPackets) +
91362306a36Sopenharmony_ci			saved->tx_packets;
91462306a36Sopenharmony_ci	stats->tx_bytes = le64_to_cpu(s->txBytes) +
91562306a36Sopenharmony_ci			saved->tx_bytes;
91662306a36Sopenharmony_ci	stats->tx_errors = le32_to_cpu(s->txCarrierLost) +
91762306a36Sopenharmony_ci			saved->tx_errors;
91862306a36Sopenharmony_ci	stats->tx_carrier_errors = le32_to_cpu(s->txCarrierLost) +
91962306a36Sopenharmony_ci			saved->tx_carrier_errors;
92062306a36Sopenharmony_ci	stats->collisions = le32_to_cpu(s->txMultipleCollisions) +
92162306a36Sopenharmony_ci			saved->collisions;
92262306a36Sopenharmony_ci	stats->rx_packets = le32_to_cpu(s->rxPacketsGood) +
92362306a36Sopenharmony_ci			saved->rx_packets;
92462306a36Sopenharmony_ci	stats->rx_bytes = le64_to_cpu(s->rxBytesGood) +
92562306a36Sopenharmony_ci			saved->rx_bytes;
92662306a36Sopenharmony_ci	stats->rx_fifo_errors = le32_to_cpu(s->rxFifoOverruns) +
92762306a36Sopenharmony_ci			saved->rx_fifo_errors;
92862306a36Sopenharmony_ci	stats->rx_errors = le32_to_cpu(s->rxFifoOverruns) +
92962306a36Sopenharmony_ci			le32_to_cpu(s->BadSSD) + le32_to_cpu(s->rxCrcErrors) +
93062306a36Sopenharmony_ci			saved->rx_errors;
93162306a36Sopenharmony_ci	stats->rx_crc_errors = le32_to_cpu(s->rxCrcErrors) +
93262306a36Sopenharmony_ci			saved->rx_crc_errors;
93362306a36Sopenharmony_ci	stats->rx_length_errors = le32_to_cpu(s->rxOversized) +
93462306a36Sopenharmony_ci			saved->rx_length_errors;
93562306a36Sopenharmony_ci	tp->speed = (s->linkStatus & TYPHOON_LINK_100MBPS) ?
93662306a36Sopenharmony_ci			SPEED_100 : SPEED_10;
93762306a36Sopenharmony_ci	tp->duplex = (s->linkStatus & TYPHOON_LINK_FULL_DUPLEX) ?
93862306a36Sopenharmony_ci			DUPLEX_FULL : DUPLEX_HALF;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	return 0;
94162306a36Sopenharmony_ci}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_cistatic struct net_device_stats *
94462306a36Sopenharmony_cityphoon_get_stats(struct net_device *dev)
94562306a36Sopenharmony_ci{
94662306a36Sopenharmony_ci	struct typhoon *tp = netdev_priv(dev);
94762306a36Sopenharmony_ci	struct net_device_stats *stats = &tp->dev->stats;
94862306a36Sopenharmony_ci	struct net_device_stats *saved = &tp->stats_saved;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	smp_rmb();
95162306a36Sopenharmony_ci	if (tp->card_state == Sleeping)
95262306a36Sopenharmony_ci		return saved;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	if (typhoon_do_get_stats(tp) < 0) {
95562306a36Sopenharmony_ci		netdev_err(dev, "error getting stats\n");
95662306a36Sopenharmony_ci		return saved;
95762306a36Sopenharmony_ci	}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	return stats;
96062306a36Sopenharmony_ci}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_cistatic void
96362306a36Sopenharmony_cityphoon_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
96462306a36Sopenharmony_ci{
96562306a36Sopenharmony_ci	struct typhoon *tp = netdev_priv(dev);
96662306a36Sopenharmony_ci	struct pci_dev *pci_dev = tp->pdev;
96762306a36Sopenharmony_ci	struct cmd_desc xp_cmd;
96862306a36Sopenharmony_ci	struct resp_desc xp_resp[3];
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	smp_rmb();
97162306a36Sopenharmony_ci	if (tp->card_state == Sleeping) {
97262306a36Sopenharmony_ci		strscpy(info->fw_version, "Sleep image",
97362306a36Sopenharmony_ci			sizeof(info->fw_version));
97462306a36Sopenharmony_ci	} else {
97562306a36Sopenharmony_ci		INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_READ_VERSIONS);
97662306a36Sopenharmony_ci		if (typhoon_issue_command(tp, 1, &xp_cmd, 3, xp_resp) < 0) {
97762306a36Sopenharmony_ci			strscpy(info->fw_version, "Unknown runtime",
97862306a36Sopenharmony_ci				sizeof(info->fw_version));
97962306a36Sopenharmony_ci		} else {
98062306a36Sopenharmony_ci			u32 sleep_ver = le32_to_cpu(xp_resp[0].parm2);
98162306a36Sopenharmony_ci			snprintf(info->fw_version, sizeof(info->fw_version),
98262306a36Sopenharmony_ci				"%02x.%03x.%03x", sleep_ver >> 24,
98362306a36Sopenharmony_ci				(sleep_ver >> 12) & 0xfff, sleep_ver & 0xfff);
98462306a36Sopenharmony_ci		}
98562306a36Sopenharmony_ci	}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	strscpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
98862306a36Sopenharmony_ci	strscpy(info->bus_info, pci_name(pci_dev), sizeof(info->bus_info));
98962306a36Sopenharmony_ci}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_cistatic int
99262306a36Sopenharmony_cityphoon_get_link_ksettings(struct net_device *dev,
99362306a36Sopenharmony_ci			   struct ethtool_link_ksettings *cmd)
99462306a36Sopenharmony_ci{
99562306a36Sopenharmony_ci	struct typhoon *tp = netdev_priv(dev);
99662306a36Sopenharmony_ci	u32 supported, advertising = 0;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	supported = SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
99962306a36Sopenharmony_ci				SUPPORTED_Autoneg;
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	switch (tp->xcvr_select) {
100262306a36Sopenharmony_ci	case TYPHOON_XCVR_10HALF:
100362306a36Sopenharmony_ci		advertising = ADVERTISED_10baseT_Half;
100462306a36Sopenharmony_ci		break;
100562306a36Sopenharmony_ci	case TYPHOON_XCVR_10FULL:
100662306a36Sopenharmony_ci		advertising = ADVERTISED_10baseT_Full;
100762306a36Sopenharmony_ci		break;
100862306a36Sopenharmony_ci	case TYPHOON_XCVR_100HALF:
100962306a36Sopenharmony_ci		advertising = ADVERTISED_100baseT_Half;
101062306a36Sopenharmony_ci		break;
101162306a36Sopenharmony_ci	case TYPHOON_XCVR_100FULL:
101262306a36Sopenharmony_ci		advertising = ADVERTISED_100baseT_Full;
101362306a36Sopenharmony_ci		break;
101462306a36Sopenharmony_ci	case TYPHOON_XCVR_AUTONEG:
101562306a36Sopenharmony_ci		advertising = ADVERTISED_10baseT_Half |
101662306a36Sopenharmony_ci					    ADVERTISED_10baseT_Full |
101762306a36Sopenharmony_ci					    ADVERTISED_100baseT_Half |
101862306a36Sopenharmony_ci					    ADVERTISED_100baseT_Full |
101962306a36Sopenharmony_ci					    ADVERTISED_Autoneg;
102062306a36Sopenharmony_ci		break;
102162306a36Sopenharmony_ci	}
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	if (tp->capabilities & TYPHOON_FIBER) {
102462306a36Sopenharmony_ci		supported |= SUPPORTED_FIBRE;
102562306a36Sopenharmony_ci		advertising |= ADVERTISED_FIBRE;
102662306a36Sopenharmony_ci		cmd->base.port = PORT_FIBRE;
102762306a36Sopenharmony_ci	} else {
102862306a36Sopenharmony_ci		supported |= SUPPORTED_10baseT_Half |
102962306a36Sopenharmony_ci		    			SUPPORTED_10baseT_Full |
103062306a36Sopenharmony_ci					SUPPORTED_TP;
103162306a36Sopenharmony_ci		advertising |= ADVERTISED_TP;
103262306a36Sopenharmony_ci		cmd->base.port = PORT_TP;
103362306a36Sopenharmony_ci	}
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	/* need to get stats to make these link speed/duplex valid */
103662306a36Sopenharmony_ci	typhoon_do_get_stats(tp);
103762306a36Sopenharmony_ci	cmd->base.speed = tp->speed;
103862306a36Sopenharmony_ci	cmd->base.duplex = tp->duplex;
103962306a36Sopenharmony_ci	cmd->base.phy_address = 0;
104062306a36Sopenharmony_ci	if (tp->xcvr_select == TYPHOON_XCVR_AUTONEG)
104162306a36Sopenharmony_ci		cmd->base.autoneg = AUTONEG_ENABLE;
104262306a36Sopenharmony_ci	else
104362306a36Sopenharmony_ci		cmd->base.autoneg = AUTONEG_DISABLE;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
104662306a36Sopenharmony_ci						supported);
104762306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
104862306a36Sopenharmony_ci						advertising);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	return 0;
105162306a36Sopenharmony_ci}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_cistatic int
105462306a36Sopenharmony_cityphoon_set_link_ksettings(struct net_device *dev,
105562306a36Sopenharmony_ci			   const struct ethtool_link_ksettings *cmd)
105662306a36Sopenharmony_ci{
105762306a36Sopenharmony_ci	struct typhoon *tp = netdev_priv(dev);
105862306a36Sopenharmony_ci	u32 speed = cmd->base.speed;
105962306a36Sopenharmony_ci	struct cmd_desc xp_cmd;
106062306a36Sopenharmony_ci	__le16 xcvr;
106162306a36Sopenharmony_ci	int err;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	err = -EINVAL;
106462306a36Sopenharmony_ci	if (cmd->base.autoneg == AUTONEG_ENABLE) {
106562306a36Sopenharmony_ci		xcvr = TYPHOON_XCVR_AUTONEG;
106662306a36Sopenharmony_ci	} else {
106762306a36Sopenharmony_ci		if (cmd->base.duplex == DUPLEX_HALF) {
106862306a36Sopenharmony_ci			if (speed == SPEED_10)
106962306a36Sopenharmony_ci				xcvr = TYPHOON_XCVR_10HALF;
107062306a36Sopenharmony_ci			else if (speed == SPEED_100)
107162306a36Sopenharmony_ci				xcvr = TYPHOON_XCVR_100HALF;
107262306a36Sopenharmony_ci			else
107362306a36Sopenharmony_ci				goto out;
107462306a36Sopenharmony_ci		} else if (cmd->base.duplex == DUPLEX_FULL) {
107562306a36Sopenharmony_ci			if (speed == SPEED_10)
107662306a36Sopenharmony_ci				xcvr = TYPHOON_XCVR_10FULL;
107762306a36Sopenharmony_ci			else if (speed == SPEED_100)
107862306a36Sopenharmony_ci				xcvr = TYPHOON_XCVR_100FULL;
107962306a36Sopenharmony_ci			else
108062306a36Sopenharmony_ci				goto out;
108162306a36Sopenharmony_ci		} else
108262306a36Sopenharmony_ci			goto out;
108362306a36Sopenharmony_ci	}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_XCVR_SELECT);
108662306a36Sopenharmony_ci	xp_cmd.parm1 = xcvr;
108762306a36Sopenharmony_ci	err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
108862306a36Sopenharmony_ci	if (err < 0)
108962306a36Sopenharmony_ci		goto out;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	tp->xcvr_select = xcvr;
109262306a36Sopenharmony_ci	if (cmd->base.autoneg == AUTONEG_ENABLE) {
109362306a36Sopenharmony_ci		tp->speed = 0xff;	/* invalid */
109462306a36Sopenharmony_ci		tp->duplex = 0xff;	/* invalid */
109562306a36Sopenharmony_ci	} else {
109662306a36Sopenharmony_ci		tp->speed = speed;
109762306a36Sopenharmony_ci		tp->duplex = cmd->base.duplex;
109862306a36Sopenharmony_ci	}
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ciout:
110162306a36Sopenharmony_ci	return err;
110262306a36Sopenharmony_ci}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_cistatic void
110562306a36Sopenharmony_cityphoon_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
110662306a36Sopenharmony_ci{
110762306a36Sopenharmony_ci	struct typhoon *tp = netdev_priv(dev);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	wol->supported = WAKE_PHY | WAKE_MAGIC;
111062306a36Sopenharmony_ci	wol->wolopts = 0;
111162306a36Sopenharmony_ci	if (tp->wol_events & TYPHOON_WAKE_LINK_EVENT)
111262306a36Sopenharmony_ci		wol->wolopts |= WAKE_PHY;
111362306a36Sopenharmony_ci	if (tp->wol_events & TYPHOON_WAKE_MAGIC_PKT)
111462306a36Sopenharmony_ci		wol->wolopts |= WAKE_MAGIC;
111562306a36Sopenharmony_ci	memset(&wol->sopass, 0, sizeof(wol->sopass));
111662306a36Sopenharmony_ci}
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_cistatic int
111962306a36Sopenharmony_cityphoon_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
112062306a36Sopenharmony_ci{
112162306a36Sopenharmony_ci	struct typhoon *tp = netdev_priv(dev);
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	if (wol->wolopts & ~(WAKE_PHY | WAKE_MAGIC))
112462306a36Sopenharmony_ci		return -EINVAL;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	tp->wol_events = 0;
112762306a36Sopenharmony_ci	if (wol->wolopts & WAKE_PHY)
112862306a36Sopenharmony_ci		tp->wol_events |= TYPHOON_WAKE_LINK_EVENT;
112962306a36Sopenharmony_ci	if (wol->wolopts & WAKE_MAGIC)
113062306a36Sopenharmony_ci		tp->wol_events |= TYPHOON_WAKE_MAGIC_PKT;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	return 0;
113362306a36Sopenharmony_ci}
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_cistatic void
113662306a36Sopenharmony_cityphoon_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ering,
113762306a36Sopenharmony_ci		      struct kernel_ethtool_ringparam *kernel_ering,
113862306a36Sopenharmony_ci		      struct netlink_ext_ack *extack)
113962306a36Sopenharmony_ci{
114062306a36Sopenharmony_ci	ering->rx_max_pending = RXENT_ENTRIES;
114162306a36Sopenharmony_ci	ering->tx_max_pending = TXLO_ENTRIES - 1;
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	ering->rx_pending = RXENT_ENTRIES;
114462306a36Sopenharmony_ci	ering->tx_pending = TXLO_ENTRIES - 1;
114562306a36Sopenharmony_ci}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_cistatic const struct ethtool_ops typhoon_ethtool_ops = {
114862306a36Sopenharmony_ci	.get_drvinfo		= typhoon_get_drvinfo,
114962306a36Sopenharmony_ci	.get_wol		= typhoon_get_wol,
115062306a36Sopenharmony_ci	.set_wol		= typhoon_set_wol,
115162306a36Sopenharmony_ci	.get_link		= ethtool_op_get_link,
115262306a36Sopenharmony_ci	.get_ringparam		= typhoon_get_ringparam,
115362306a36Sopenharmony_ci	.get_link_ksettings	= typhoon_get_link_ksettings,
115462306a36Sopenharmony_ci	.set_link_ksettings	= typhoon_set_link_ksettings,
115562306a36Sopenharmony_ci};
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_cistatic int
115862306a36Sopenharmony_cityphoon_wait_interrupt(void __iomem *ioaddr)
115962306a36Sopenharmony_ci{
116062306a36Sopenharmony_ci	int i, err = 0;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	for (i = 0; i < TYPHOON_WAIT_TIMEOUT; i++) {
116362306a36Sopenharmony_ci		if (ioread32(ioaddr + TYPHOON_REG_INTR_STATUS) &
116462306a36Sopenharmony_ci		   TYPHOON_INTR_BOOTCMD)
116562306a36Sopenharmony_ci			goto out;
116662306a36Sopenharmony_ci		udelay(TYPHOON_UDELAY);
116762306a36Sopenharmony_ci	}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	err = -ETIMEDOUT;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ciout:
117262306a36Sopenharmony_ci	iowrite32(TYPHOON_INTR_BOOTCMD, ioaddr + TYPHOON_REG_INTR_STATUS);
117362306a36Sopenharmony_ci	return err;
117462306a36Sopenharmony_ci}
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci#define shared_offset(x)	offsetof(struct typhoon_shared, x)
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_cistatic void
117962306a36Sopenharmony_cityphoon_init_interface(struct typhoon *tp)
118062306a36Sopenharmony_ci{
118162306a36Sopenharmony_ci	struct typhoon_interface *iface = &tp->shared->iface;
118262306a36Sopenharmony_ci	dma_addr_t shared_dma;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	memset(tp->shared, 0, sizeof(struct typhoon_shared));
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	/* The *Hi members of iface are all init'd to zero by the memset().
118762306a36Sopenharmony_ci	 */
118862306a36Sopenharmony_ci	shared_dma = tp->shared_dma + shared_offset(indexes);
118962306a36Sopenharmony_ci	iface->ringIndex = cpu_to_le32(shared_dma);
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	shared_dma = tp->shared_dma + shared_offset(txLo);
119262306a36Sopenharmony_ci	iface->txLoAddr = cpu_to_le32(shared_dma);
119362306a36Sopenharmony_ci	iface->txLoSize = cpu_to_le32(TXLO_ENTRIES * sizeof(struct tx_desc));
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	shared_dma = tp->shared_dma + shared_offset(txHi);
119662306a36Sopenharmony_ci	iface->txHiAddr = cpu_to_le32(shared_dma);
119762306a36Sopenharmony_ci	iface->txHiSize = cpu_to_le32(TXHI_ENTRIES * sizeof(struct tx_desc));
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	shared_dma = tp->shared_dma + shared_offset(rxBuff);
120062306a36Sopenharmony_ci	iface->rxBuffAddr = cpu_to_le32(shared_dma);
120162306a36Sopenharmony_ci	iface->rxBuffSize = cpu_to_le32(RXFREE_ENTRIES *
120262306a36Sopenharmony_ci					sizeof(struct rx_free));
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	shared_dma = tp->shared_dma + shared_offset(rxLo);
120562306a36Sopenharmony_ci	iface->rxLoAddr = cpu_to_le32(shared_dma);
120662306a36Sopenharmony_ci	iface->rxLoSize = cpu_to_le32(RX_ENTRIES * sizeof(struct rx_desc));
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	shared_dma = tp->shared_dma + shared_offset(rxHi);
120962306a36Sopenharmony_ci	iface->rxHiAddr = cpu_to_le32(shared_dma);
121062306a36Sopenharmony_ci	iface->rxHiSize = cpu_to_le32(RX_ENTRIES * sizeof(struct rx_desc));
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	shared_dma = tp->shared_dma + shared_offset(cmd);
121362306a36Sopenharmony_ci	iface->cmdAddr = cpu_to_le32(shared_dma);
121462306a36Sopenharmony_ci	iface->cmdSize = cpu_to_le32(COMMAND_RING_SIZE);
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	shared_dma = tp->shared_dma + shared_offset(resp);
121762306a36Sopenharmony_ci	iface->respAddr = cpu_to_le32(shared_dma);
121862306a36Sopenharmony_ci	iface->respSize = cpu_to_le32(RESPONSE_RING_SIZE);
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	shared_dma = tp->shared_dma + shared_offset(zeroWord);
122162306a36Sopenharmony_ci	iface->zeroAddr = cpu_to_le32(shared_dma);
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	tp->indexes = &tp->shared->indexes;
122462306a36Sopenharmony_ci	tp->txLoRing.ringBase = (u8 *) tp->shared->txLo;
122562306a36Sopenharmony_ci	tp->txHiRing.ringBase = (u8 *) tp->shared->txHi;
122662306a36Sopenharmony_ci	tp->rxLoRing.ringBase = (u8 *) tp->shared->rxLo;
122762306a36Sopenharmony_ci	tp->rxHiRing.ringBase = (u8 *) tp->shared->rxHi;
122862306a36Sopenharmony_ci	tp->rxBuffRing.ringBase = (u8 *) tp->shared->rxBuff;
122962306a36Sopenharmony_ci	tp->cmdRing.ringBase = (u8 *) tp->shared->cmd;
123062306a36Sopenharmony_ci	tp->respRing.ringBase = (u8 *) tp->shared->resp;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	tp->txLoRing.writeRegister = TYPHOON_REG_TX_LO_READY;
123362306a36Sopenharmony_ci	tp->txHiRing.writeRegister = TYPHOON_REG_TX_HI_READY;
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	tp->txlo_dma_addr = le32_to_cpu(iface->txLoAddr);
123662306a36Sopenharmony_ci	tp->card_state = Sleeping;
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	tp->offload = TYPHOON_OFFLOAD_IP_CHKSUM | TYPHOON_OFFLOAD_TCP_CHKSUM;
123962306a36Sopenharmony_ci	tp->offload |= TYPHOON_OFFLOAD_UDP_CHKSUM | TSO_OFFLOAD_ON;
124062306a36Sopenharmony_ci	tp->offload |= TYPHOON_OFFLOAD_VLAN;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	spin_lock_init(&tp->command_lock);
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	/* Force the writes to the shared memory area out before continuing. */
124562306a36Sopenharmony_ci	wmb();
124662306a36Sopenharmony_ci}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_cistatic void
124962306a36Sopenharmony_cityphoon_init_rings(struct typhoon *tp)
125062306a36Sopenharmony_ci{
125162306a36Sopenharmony_ci	memset(tp->indexes, 0, sizeof(struct typhoon_indexes));
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	tp->txLoRing.lastWrite = 0;
125462306a36Sopenharmony_ci	tp->txHiRing.lastWrite = 0;
125562306a36Sopenharmony_ci	tp->rxLoRing.lastWrite = 0;
125662306a36Sopenharmony_ci	tp->rxHiRing.lastWrite = 0;
125762306a36Sopenharmony_ci	tp->rxBuffRing.lastWrite = 0;
125862306a36Sopenharmony_ci	tp->cmdRing.lastWrite = 0;
125962306a36Sopenharmony_ci	tp->respRing.lastWrite = 0;
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	tp->txLoRing.lastRead = 0;
126262306a36Sopenharmony_ci	tp->txHiRing.lastRead = 0;
126362306a36Sopenharmony_ci}
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_cistatic const struct firmware *typhoon_fw;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_cistatic int
126862306a36Sopenharmony_cityphoon_request_firmware(struct typhoon *tp)
126962306a36Sopenharmony_ci{
127062306a36Sopenharmony_ci	const struct typhoon_file_header *fHdr;
127162306a36Sopenharmony_ci	const struct typhoon_section_header *sHdr;
127262306a36Sopenharmony_ci	const u8 *image_data;
127362306a36Sopenharmony_ci	u32 numSections;
127462306a36Sopenharmony_ci	u32 section_len;
127562306a36Sopenharmony_ci	u32 remaining;
127662306a36Sopenharmony_ci	int err;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	if (typhoon_fw)
127962306a36Sopenharmony_ci		return 0;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	err = request_firmware(&typhoon_fw, FIRMWARE_NAME, &tp->pdev->dev);
128262306a36Sopenharmony_ci	if (err) {
128362306a36Sopenharmony_ci		netdev_err(tp->dev, "Failed to load firmware \"%s\"\n",
128462306a36Sopenharmony_ci			   FIRMWARE_NAME);
128562306a36Sopenharmony_ci		return err;
128662306a36Sopenharmony_ci	}
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	image_data = typhoon_fw->data;
128962306a36Sopenharmony_ci	remaining = typhoon_fw->size;
129062306a36Sopenharmony_ci	if (remaining < sizeof(struct typhoon_file_header))
129162306a36Sopenharmony_ci		goto invalid_fw;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	fHdr = (struct typhoon_file_header *) image_data;
129462306a36Sopenharmony_ci	if (memcmp(fHdr->tag, "TYPHOON", 8))
129562306a36Sopenharmony_ci		goto invalid_fw;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	numSections = le32_to_cpu(fHdr->numSections);
129862306a36Sopenharmony_ci	image_data += sizeof(struct typhoon_file_header);
129962306a36Sopenharmony_ci	remaining -= sizeof(struct typhoon_file_header);
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	while (numSections--) {
130262306a36Sopenharmony_ci		if (remaining < sizeof(struct typhoon_section_header))
130362306a36Sopenharmony_ci			goto invalid_fw;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci		sHdr = (struct typhoon_section_header *) image_data;
130662306a36Sopenharmony_ci		image_data += sizeof(struct typhoon_section_header);
130762306a36Sopenharmony_ci		section_len = le32_to_cpu(sHdr->len);
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci		if (remaining < section_len)
131062306a36Sopenharmony_ci			goto invalid_fw;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci		image_data += section_len;
131362306a36Sopenharmony_ci		remaining -= section_len;
131462306a36Sopenharmony_ci	}
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	return 0;
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ciinvalid_fw:
131962306a36Sopenharmony_ci	netdev_err(tp->dev, "Invalid firmware image\n");
132062306a36Sopenharmony_ci	release_firmware(typhoon_fw);
132162306a36Sopenharmony_ci	typhoon_fw = NULL;
132262306a36Sopenharmony_ci	return -EINVAL;
132362306a36Sopenharmony_ci}
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_cistatic int
132662306a36Sopenharmony_cityphoon_download_firmware(struct typhoon *tp)
132762306a36Sopenharmony_ci{
132862306a36Sopenharmony_ci	void __iomem *ioaddr = tp->ioaddr;
132962306a36Sopenharmony_ci	struct pci_dev *pdev = tp->pdev;
133062306a36Sopenharmony_ci	const struct typhoon_file_header *fHdr;
133162306a36Sopenharmony_ci	const struct typhoon_section_header *sHdr;
133262306a36Sopenharmony_ci	const u8 *image_data;
133362306a36Sopenharmony_ci	void *dpage;
133462306a36Sopenharmony_ci	dma_addr_t dpage_dma;
133562306a36Sopenharmony_ci	__sum16 csum;
133662306a36Sopenharmony_ci	u32 irqEnabled;
133762306a36Sopenharmony_ci	u32 irqMasked;
133862306a36Sopenharmony_ci	u32 numSections;
133962306a36Sopenharmony_ci	u32 section_len;
134062306a36Sopenharmony_ci	u32 len;
134162306a36Sopenharmony_ci	u32 load_addr;
134262306a36Sopenharmony_ci	u32 hmac;
134362306a36Sopenharmony_ci	int i;
134462306a36Sopenharmony_ci	int err;
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	image_data = typhoon_fw->data;
134762306a36Sopenharmony_ci	fHdr = (struct typhoon_file_header *) image_data;
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	/* Cannot just map the firmware image using dma_map_single() as
135062306a36Sopenharmony_ci	 * the firmware is vmalloc()'d and may not be physically contiguous,
135162306a36Sopenharmony_ci	 * so we allocate some coherent memory to copy the sections into.
135262306a36Sopenharmony_ci	 */
135362306a36Sopenharmony_ci	err = -ENOMEM;
135462306a36Sopenharmony_ci	dpage = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &dpage_dma, GFP_ATOMIC);
135562306a36Sopenharmony_ci	if (!dpage) {
135662306a36Sopenharmony_ci		netdev_err(tp->dev, "no DMA mem for firmware\n");
135762306a36Sopenharmony_ci		goto err_out;
135862306a36Sopenharmony_ci	}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	irqEnabled = ioread32(ioaddr + TYPHOON_REG_INTR_ENABLE);
136162306a36Sopenharmony_ci	iowrite32(irqEnabled | TYPHOON_INTR_BOOTCMD,
136262306a36Sopenharmony_ci	       ioaddr + TYPHOON_REG_INTR_ENABLE);
136362306a36Sopenharmony_ci	irqMasked = ioread32(ioaddr + TYPHOON_REG_INTR_MASK);
136462306a36Sopenharmony_ci	iowrite32(irqMasked | TYPHOON_INTR_BOOTCMD,
136562306a36Sopenharmony_ci	       ioaddr + TYPHOON_REG_INTR_MASK);
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	err = -ETIMEDOUT;
136862306a36Sopenharmony_ci	if (typhoon_wait_status(ioaddr, TYPHOON_STATUS_WAITING_FOR_HOST) < 0) {
136962306a36Sopenharmony_ci		netdev_err(tp->dev, "card ready timeout\n");
137062306a36Sopenharmony_ci		goto err_out_irq;
137162306a36Sopenharmony_ci	}
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	numSections = le32_to_cpu(fHdr->numSections);
137462306a36Sopenharmony_ci	load_addr = le32_to_cpu(fHdr->startAddr);
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	iowrite32(TYPHOON_INTR_BOOTCMD, ioaddr + TYPHOON_REG_INTR_STATUS);
137762306a36Sopenharmony_ci	iowrite32(load_addr, ioaddr + TYPHOON_REG_DOWNLOAD_BOOT_ADDR);
137862306a36Sopenharmony_ci	hmac = le32_to_cpu(fHdr->hmacDigest[0]);
137962306a36Sopenharmony_ci	iowrite32(hmac, ioaddr + TYPHOON_REG_DOWNLOAD_HMAC_0);
138062306a36Sopenharmony_ci	hmac = le32_to_cpu(fHdr->hmacDigest[1]);
138162306a36Sopenharmony_ci	iowrite32(hmac, ioaddr + TYPHOON_REG_DOWNLOAD_HMAC_1);
138262306a36Sopenharmony_ci	hmac = le32_to_cpu(fHdr->hmacDigest[2]);
138362306a36Sopenharmony_ci	iowrite32(hmac, ioaddr + TYPHOON_REG_DOWNLOAD_HMAC_2);
138462306a36Sopenharmony_ci	hmac = le32_to_cpu(fHdr->hmacDigest[3]);
138562306a36Sopenharmony_ci	iowrite32(hmac, ioaddr + TYPHOON_REG_DOWNLOAD_HMAC_3);
138662306a36Sopenharmony_ci	hmac = le32_to_cpu(fHdr->hmacDigest[4]);
138762306a36Sopenharmony_ci	iowrite32(hmac, ioaddr + TYPHOON_REG_DOWNLOAD_HMAC_4);
138862306a36Sopenharmony_ci	typhoon_post_pci_writes(ioaddr);
138962306a36Sopenharmony_ci	iowrite32(TYPHOON_BOOTCMD_RUNTIME_IMAGE, ioaddr + TYPHOON_REG_COMMAND);
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	image_data += sizeof(struct typhoon_file_header);
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	/* The ioread32() in typhoon_wait_interrupt() will force the
139462306a36Sopenharmony_ci	 * last write to the command register to post, so
139562306a36Sopenharmony_ci	 * we don't need a typhoon_post_pci_writes() after it.
139662306a36Sopenharmony_ci	 */
139762306a36Sopenharmony_ci	for (i = 0; i < numSections; i++) {
139862306a36Sopenharmony_ci		sHdr = (struct typhoon_section_header *) image_data;
139962306a36Sopenharmony_ci		image_data += sizeof(struct typhoon_section_header);
140062306a36Sopenharmony_ci		load_addr = le32_to_cpu(sHdr->startAddr);
140162306a36Sopenharmony_ci		section_len = le32_to_cpu(sHdr->len);
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci		while (section_len) {
140462306a36Sopenharmony_ci			len = min_t(u32, section_len, PAGE_SIZE);
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci			if (typhoon_wait_interrupt(ioaddr) < 0 ||
140762306a36Sopenharmony_ci			   ioread32(ioaddr + TYPHOON_REG_STATUS) !=
140862306a36Sopenharmony_ci			   TYPHOON_STATUS_WAITING_FOR_SEGMENT) {
140962306a36Sopenharmony_ci				netdev_err(tp->dev, "segment ready timeout\n");
141062306a36Sopenharmony_ci				goto err_out_irq;
141162306a36Sopenharmony_ci			}
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci			/* Do an pseudo IPv4 checksum on the data -- first
141462306a36Sopenharmony_ci			 * need to convert each u16 to cpu order before
141562306a36Sopenharmony_ci			 * summing. Fortunately, due to the properties of
141662306a36Sopenharmony_ci			 * the checksum, we can do this once, at the end.
141762306a36Sopenharmony_ci			 */
141862306a36Sopenharmony_ci			csum = csum_fold(csum_partial_copy_nocheck(image_data,
141962306a36Sopenharmony_ci								   dpage, len));
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci			iowrite32(len, ioaddr + TYPHOON_REG_BOOT_LENGTH);
142262306a36Sopenharmony_ci			iowrite32(le16_to_cpu((__force __le16)csum),
142362306a36Sopenharmony_ci					ioaddr + TYPHOON_REG_BOOT_CHECKSUM);
142462306a36Sopenharmony_ci			iowrite32(load_addr,
142562306a36Sopenharmony_ci					ioaddr + TYPHOON_REG_BOOT_DEST_ADDR);
142662306a36Sopenharmony_ci			iowrite32(0, ioaddr + TYPHOON_REG_BOOT_DATA_HI);
142762306a36Sopenharmony_ci			iowrite32(dpage_dma, ioaddr + TYPHOON_REG_BOOT_DATA_LO);
142862306a36Sopenharmony_ci			typhoon_post_pci_writes(ioaddr);
142962306a36Sopenharmony_ci			iowrite32(TYPHOON_BOOTCMD_SEG_AVAILABLE,
143062306a36Sopenharmony_ci					ioaddr + TYPHOON_REG_COMMAND);
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci			image_data += len;
143362306a36Sopenharmony_ci			load_addr += len;
143462306a36Sopenharmony_ci			section_len -= len;
143562306a36Sopenharmony_ci		}
143662306a36Sopenharmony_ci	}
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	if (typhoon_wait_interrupt(ioaddr) < 0 ||
143962306a36Sopenharmony_ci	   ioread32(ioaddr + TYPHOON_REG_STATUS) !=
144062306a36Sopenharmony_ci	   TYPHOON_STATUS_WAITING_FOR_SEGMENT) {
144162306a36Sopenharmony_ci		netdev_err(tp->dev, "final segment ready timeout\n");
144262306a36Sopenharmony_ci		goto err_out_irq;
144362306a36Sopenharmony_ci	}
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	iowrite32(TYPHOON_BOOTCMD_DNLD_COMPLETE, ioaddr + TYPHOON_REG_COMMAND);
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	if (typhoon_wait_status(ioaddr, TYPHOON_STATUS_WAITING_FOR_BOOT) < 0) {
144862306a36Sopenharmony_ci		netdev_err(tp->dev, "boot ready timeout, status 0x%0x\n",
144962306a36Sopenharmony_ci			   ioread32(ioaddr + TYPHOON_REG_STATUS));
145062306a36Sopenharmony_ci		goto err_out_irq;
145162306a36Sopenharmony_ci	}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	err = 0;
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_cierr_out_irq:
145662306a36Sopenharmony_ci	iowrite32(irqMasked, ioaddr + TYPHOON_REG_INTR_MASK);
145762306a36Sopenharmony_ci	iowrite32(irqEnabled, ioaddr + TYPHOON_REG_INTR_ENABLE);
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	dma_free_coherent(&pdev->dev, PAGE_SIZE, dpage, dpage_dma);
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_cierr_out:
146262306a36Sopenharmony_ci	return err;
146362306a36Sopenharmony_ci}
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_cistatic int
146662306a36Sopenharmony_cityphoon_boot_3XP(struct typhoon *tp, u32 initial_status)
146762306a36Sopenharmony_ci{
146862306a36Sopenharmony_ci	void __iomem *ioaddr = tp->ioaddr;
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	if (typhoon_wait_status(ioaddr, initial_status) < 0) {
147162306a36Sopenharmony_ci		netdev_err(tp->dev, "boot ready timeout\n");
147262306a36Sopenharmony_ci		goto out_timeout;
147362306a36Sopenharmony_ci	}
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	iowrite32(0, ioaddr + TYPHOON_REG_BOOT_RECORD_ADDR_HI);
147662306a36Sopenharmony_ci	iowrite32(tp->shared_dma, ioaddr + TYPHOON_REG_BOOT_RECORD_ADDR_LO);
147762306a36Sopenharmony_ci	typhoon_post_pci_writes(ioaddr);
147862306a36Sopenharmony_ci	iowrite32(TYPHOON_BOOTCMD_REG_BOOT_RECORD,
147962306a36Sopenharmony_ci				ioaddr + TYPHOON_REG_COMMAND);
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	if (typhoon_wait_status(ioaddr, TYPHOON_STATUS_RUNNING) < 0) {
148262306a36Sopenharmony_ci		netdev_err(tp->dev, "boot finish timeout (status 0x%x)\n",
148362306a36Sopenharmony_ci			   ioread32(ioaddr + TYPHOON_REG_STATUS));
148462306a36Sopenharmony_ci		goto out_timeout;
148562306a36Sopenharmony_ci	}
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	/* Clear the Transmit and Command ready registers
148862306a36Sopenharmony_ci	 */
148962306a36Sopenharmony_ci	iowrite32(0, ioaddr + TYPHOON_REG_TX_HI_READY);
149062306a36Sopenharmony_ci	iowrite32(0, ioaddr + TYPHOON_REG_CMD_READY);
149162306a36Sopenharmony_ci	iowrite32(0, ioaddr + TYPHOON_REG_TX_LO_READY);
149262306a36Sopenharmony_ci	typhoon_post_pci_writes(ioaddr);
149362306a36Sopenharmony_ci	iowrite32(TYPHOON_BOOTCMD_BOOT, ioaddr + TYPHOON_REG_COMMAND);
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	return 0;
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ciout_timeout:
149862306a36Sopenharmony_ci	return -ETIMEDOUT;
149962306a36Sopenharmony_ci}
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_cistatic u32
150262306a36Sopenharmony_cityphoon_clean_tx(struct typhoon *tp, struct transmit_ring *txRing,
150362306a36Sopenharmony_ci			volatile __le32 * index)
150462306a36Sopenharmony_ci{
150562306a36Sopenharmony_ci	u32 lastRead = txRing->lastRead;
150662306a36Sopenharmony_ci	struct tx_desc *tx;
150762306a36Sopenharmony_ci	dma_addr_t skb_dma;
150862306a36Sopenharmony_ci	int dma_len;
150962306a36Sopenharmony_ci	int type;
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	while (lastRead != le32_to_cpu(*index)) {
151262306a36Sopenharmony_ci		tx = (struct tx_desc *) (txRing->ringBase + lastRead);
151362306a36Sopenharmony_ci		type = tx->flags & TYPHOON_TYPE_MASK;
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci		if (type == TYPHOON_TX_DESC) {
151662306a36Sopenharmony_ci			/* This tx_desc describes a packet.
151762306a36Sopenharmony_ci			 */
151862306a36Sopenharmony_ci			unsigned long ptr = tx->tx_addr;
151962306a36Sopenharmony_ci			struct sk_buff *skb = (struct sk_buff *) ptr;
152062306a36Sopenharmony_ci			dev_kfree_skb_irq(skb);
152162306a36Sopenharmony_ci		} else if (type == TYPHOON_FRAG_DESC) {
152262306a36Sopenharmony_ci			/* This tx_desc describes a memory mapping. Free it.
152362306a36Sopenharmony_ci			 */
152462306a36Sopenharmony_ci			skb_dma = (dma_addr_t) le32_to_cpu(tx->frag.addr);
152562306a36Sopenharmony_ci			dma_len = le16_to_cpu(tx->len);
152662306a36Sopenharmony_ci			dma_unmap_single(&tp->pdev->dev, skb_dma, dma_len,
152762306a36Sopenharmony_ci					 DMA_TO_DEVICE);
152862306a36Sopenharmony_ci		}
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci		tx->flags = 0;
153162306a36Sopenharmony_ci		typhoon_inc_tx_index(&lastRead, 1);
153262306a36Sopenharmony_ci	}
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	return lastRead;
153562306a36Sopenharmony_ci}
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_cistatic void
153862306a36Sopenharmony_cityphoon_tx_complete(struct typhoon *tp, struct transmit_ring *txRing,
153962306a36Sopenharmony_ci			volatile __le32 * index)
154062306a36Sopenharmony_ci{
154162306a36Sopenharmony_ci	u32 lastRead;
154262306a36Sopenharmony_ci	int numDesc = MAX_SKB_FRAGS + 1;
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	/* This will need changing if we start to use the Hi Tx ring. */
154562306a36Sopenharmony_ci	lastRead = typhoon_clean_tx(tp, txRing, index);
154662306a36Sopenharmony_ci	if (netif_queue_stopped(tp->dev) && typhoon_num_free(txRing->lastWrite,
154762306a36Sopenharmony_ci				lastRead, TXLO_ENTRIES) > (numDesc + 2))
154862306a36Sopenharmony_ci		netif_wake_queue(tp->dev);
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	txRing->lastRead = lastRead;
155162306a36Sopenharmony_ci	smp_wmb();
155262306a36Sopenharmony_ci}
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_cistatic void
155562306a36Sopenharmony_cityphoon_recycle_rx_skb(struct typhoon *tp, u32 idx)
155662306a36Sopenharmony_ci{
155762306a36Sopenharmony_ci	struct typhoon_indexes *indexes = tp->indexes;
155862306a36Sopenharmony_ci	struct rxbuff_ent *rxb = &tp->rxbuffers[idx];
155962306a36Sopenharmony_ci	struct basic_ring *ring = &tp->rxBuffRing;
156062306a36Sopenharmony_ci	struct rx_free *r;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	if ((ring->lastWrite + sizeof(*r)) % (RXFREE_ENTRIES * sizeof(*r)) ==
156362306a36Sopenharmony_ci				le32_to_cpu(indexes->rxBuffCleared)) {
156462306a36Sopenharmony_ci		/* no room in ring, just drop the skb
156562306a36Sopenharmony_ci		 */
156662306a36Sopenharmony_ci		dev_kfree_skb_any(rxb->skb);
156762306a36Sopenharmony_ci		rxb->skb = NULL;
156862306a36Sopenharmony_ci		return;
156962306a36Sopenharmony_ci	}
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	r = (struct rx_free *) (ring->ringBase + ring->lastWrite);
157262306a36Sopenharmony_ci	typhoon_inc_rxfree_index(&ring->lastWrite, 1);
157362306a36Sopenharmony_ci	r->virtAddr = idx;
157462306a36Sopenharmony_ci	r->physAddr = cpu_to_le32(rxb->dma_addr);
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	/* Tell the card about it */
157762306a36Sopenharmony_ci	wmb();
157862306a36Sopenharmony_ci	indexes->rxBuffReady = cpu_to_le32(ring->lastWrite);
157962306a36Sopenharmony_ci}
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_cistatic int
158262306a36Sopenharmony_cityphoon_alloc_rx_skb(struct typhoon *tp, u32 idx)
158362306a36Sopenharmony_ci{
158462306a36Sopenharmony_ci	struct typhoon_indexes *indexes = tp->indexes;
158562306a36Sopenharmony_ci	struct rxbuff_ent *rxb = &tp->rxbuffers[idx];
158662306a36Sopenharmony_ci	struct basic_ring *ring = &tp->rxBuffRing;
158762306a36Sopenharmony_ci	struct rx_free *r;
158862306a36Sopenharmony_ci	struct sk_buff *skb;
158962306a36Sopenharmony_ci	dma_addr_t dma_addr;
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	rxb->skb = NULL;
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	if ((ring->lastWrite + sizeof(*r)) % (RXFREE_ENTRIES * sizeof(*r)) ==
159462306a36Sopenharmony_ci				le32_to_cpu(indexes->rxBuffCleared))
159562306a36Sopenharmony_ci		return -ENOMEM;
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	skb = netdev_alloc_skb(tp->dev, PKT_BUF_SZ);
159862306a36Sopenharmony_ci	if (!skb)
159962306a36Sopenharmony_ci		return -ENOMEM;
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci#if 0
160262306a36Sopenharmony_ci	/* Please, 3com, fix the firmware to allow DMA to a unaligned
160362306a36Sopenharmony_ci	 * address! Pretty please?
160462306a36Sopenharmony_ci	 */
160562306a36Sopenharmony_ci	skb_reserve(skb, 2);
160662306a36Sopenharmony_ci#endif
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	dma_addr = dma_map_single(&tp->pdev->dev, skb->data, PKT_BUF_SZ,
160962306a36Sopenharmony_ci				  DMA_FROM_DEVICE);
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	/* Since no card does 64 bit DAC, the high bits will never
161262306a36Sopenharmony_ci	 * change from zero.
161362306a36Sopenharmony_ci	 */
161462306a36Sopenharmony_ci	r = (struct rx_free *) (ring->ringBase + ring->lastWrite);
161562306a36Sopenharmony_ci	typhoon_inc_rxfree_index(&ring->lastWrite, 1);
161662306a36Sopenharmony_ci	r->virtAddr = idx;
161762306a36Sopenharmony_ci	r->physAddr = cpu_to_le32(dma_addr);
161862306a36Sopenharmony_ci	rxb->skb = skb;
161962306a36Sopenharmony_ci	rxb->dma_addr = dma_addr;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	/* Tell the card about it */
162262306a36Sopenharmony_ci	wmb();
162362306a36Sopenharmony_ci	indexes->rxBuffReady = cpu_to_le32(ring->lastWrite);
162462306a36Sopenharmony_ci	return 0;
162562306a36Sopenharmony_ci}
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_cistatic int
162862306a36Sopenharmony_cityphoon_rx(struct typhoon *tp, struct basic_ring *rxRing, volatile __le32 * ready,
162962306a36Sopenharmony_ci	   volatile __le32 * cleared, int budget)
163062306a36Sopenharmony_ci{
163162306a36Sopenharmony_ci	struct rx_desc *rx;
163262306a36Sopenharmony_ci	struct sk_buff *skb, *new_skb;
163362306a36Sopenharmony_ci	struct rxbuff_ent *rxb;
163462306a36Sopenharmony_ci	dma_addr_t dma_addr;
163562306a36Sopenharmony_ci	u32 local_ready;
163662306a36Sopenharmony_ci	u32 rxaddr;
163762306a36Sopenharmony_ci	int pkt_len;
163862306a36Sopenharmony_ci	u32 idx;
163962306a36Sopenharmony_ci	__le32 csum_bits;
164062306a36Sopenharmony_ci	int received;
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	received = 0;
164362306a36Sopenharmony_ci	local_ready = le32_to_cpu(*ready);
164462306a36Sopenharmony_ci	rxaddr = le32_to_cpu(*cleared);
164562306a36Sopenharmony_ci	while (rxaddr != local_ready && budget > 0) {
164662306a36Sopenharmony_ci		rx = (struct rx_desc *) (rxRing->ringBase + rxaddr);
164762306a36Sopenharmony_ci		idx = rx->addr;
164862306a36Sopenharmony_ci		rxb = &tp->rxbuffers[idx];
164962306a36Sopenharmony_ci		skb = rxb->skb;
165062306a36Sopenharmony_ci		dma_addr = rxb->dma_addr;
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci		typhoon_inc_rx_index(&rxaddr, 1);
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci		if (rx->flags & TYPHOON_RX_ERROR) {
165562306a36Sopenharmony_ci			typhoon_recycle_rx_skb(tp, idx);
165662306a36Sopenharmony_ci			continue;
165762306a36Sopenharmony_ci		}
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci		pkt_len = le16_to_cpu(rx->frameLen);
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci		if (pkt_len < rx_copybreak &&
166262306a36Sopenharmony_ci		   (new_skb = netdev_alloc_skb(tp->dev, pkt_len + 2)) != NULL) {
166362306a36Sopenharmony_ci			skb_reserve(new_skb, 2);
166462306a36Sopenharmony_ci			dma_sync_single_for_cpu(&tp->pdev->dev, dma_addr,
166562306a36Sopenharmony_ci						PKT_BUF_SZ, DMA_FROM_DEVICE);
166662306a36Sopenharmony_ci			skb_copy_to_linear_data(new_skb, skb->data, pkt_len);
166762306a36Sopenharmony_ci			dma_sync_single_for_device(&tp->pdev->dev, dma_addr,
166862306a36Sopenharmony_ci						   PKT_BUF_SZ,
166962306a36Sopenharmony_ci						   DMA_FROM_DEVICE);
167062306a36Sopenharmony_ci			skb_put(new_skb, pkt_len);
167162306a36Sopenharmony_ci			typhoon_recycle_rx_skb(tp, idx);
167262306a36Sopenharmony_ci		} else {
167362306a36Sopenharmony_ci			new_skb = skb;
167462306a36Sopenharmony_ci			skb_put(new_skb, pkt_len);
167562306a36Sopenharmony_ci			dma_unmap_single(&tp->pdev->dev, dma_addr, PKT_BUF_SZ,
167662306a36Sopenharmony_ci					 DMA_FROM_DEVICE);
167762306a36Sopenharmony_ci			typhoon_alloc_rx_skb(tp, idx);
167862306a36Sopenharmony_ci		}
167962306a36Sopenharmony_ci		new_skb->protocol = eth_type_trans(new_skb, tp->dev);
168062306a36Sopenharmony_ci		csum_bits = rx->rxStatus & (TYPHOON_RX_IP_CHK_GOOD |
168162306a36Sopenharmony_ci			TYPHOON_RX_UDP_CHK_GOOD | TYPHOON_RX_TCP_CHK_GOOD);
168262306a36Sopenharmony_ci		if (csum_bits ==
168362306a36Sopenharmony_ci		   (TYPHOON_RX_IP_CHK_GOOD | TYPHOON_RX_TCP_CHK_GOOD) ||
168462306a36Sopenharmony_ci		   csum_bits ==
168562306a36Sopenharmony_ci		   (TYPHOON_RX_IP_CHK_GOOD | TYPHOON_RX_UDP_CHK_GOOD)) {
168662306a36Sopenharmony_ci			new_skb->ip_summed = CHECKSUM_UNNECESSARY;
168762306a36Sopenharmony_ci		} else
168862306a36Sopenharmony_ci			skb_checksum_none_assert(new_skb);
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci		if (rx->rxStatus & TYPHOON_RX_VLAN)
169162306a36Sopenharmony_ci			__vlan_hwaccel_put_tag(new_skb, htons(ETH_P_8021Q),
169262306a36Sopenharmony_ci					       ntohl(rx->vlanTag) & 0xffff);
169362306a36Sopenharmony_ci		netif_receive_skb(new_skb);
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci		received++;
169662306a36Sopenharmony_ci		budget--;
169762306a36Sopenharmony_ci	}
169862306a36Sopenharmony_ci	*cleared = cpu_to_le32(rxaddr);
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	return received;
170162306a36Sopenharmony_ci}
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_cistatic void
170462306a36Sopenharmony_cityphoon_fill_free_ring(struct typhoon *tp)
170562306a36Sopenharmony_ci{
170662306a36Sopenharmony_ci	u32 i;
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	for (i = 0; i < RXENT_ENTRIES; i++) {
170962306a36Sopenharmony_ci		struct rxbuff_ent *rxb = &tp->rxbuffers[i];
171062306a36Sopenharmony_ci		if (rxb->skb)
171162306a36Sopenharmony_ci			continue;
171262306a36Sopenharmony_ci		if (typhoon_alloc_rx_skb(tp, i) < 0)
171362306a36Sopenharmony_ci			break;
171462306a36Sopenharmony_ci	}
171562306a36Sopenharmony_ci}
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_cistatic int
171862306a36Sopenharmony_cityphoon_poll(struct napi_struct *napi, int budget)
171962306a36Sopenharmony_ci{
172062306a36Sopenharmony_ci	struct typhoon *tp = container_of(napi, struct typhoon, napi);
172162306a36Sopenharmony_ci	struct typhoon_indexes *indexes = tp->indexes;
172262306a36Sopenharmony_ci	int work_done;
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	rmb();
172562306a36Sopenharmony_ci	if (!tp->awaiting_resp && indexes->respReady != indexes->respCleared)
172662306a36Sopenharmony_ci			typhoon_process_response(tp, 0, NULL);
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	if (le32_to_cpu(indexes->txLoCleared) != tp->txLoRing.lastRead)
172962306a36Sopenharmony_ci		typhoon_tx_complete(tp, &tp->txLoRing, &indexes->txLoCleared);
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci	work_done = 0;
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci	if (indexes->rxHiCleared != indexes->rxHiReady) {
173462306a36Sopenharmony_ci		work_done += typhoon_rx(tp, &tp->rxHiRing, &indexes->rxHiReady,
173562306a36Sopenharmony_ci			   		&indexes->rxHiCleared, budget);
173662306a36Sopenharmony_ci	}
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci	if (indexes->rxLoCleared != indexes->rxLoReady) {
173962306a36Sopenharmony_ci		work_done += typhoon_rx(tp, &tp->rxLoRing, &indexes->rxLoReady,
174062306a36Sopenharmony_ci					&indexes->rxLoCleared, budget - work_done);
174162306a36Sopenharmony_ci	}
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	if (le32_to_cpu(indexes->rxBuffCleared) == tp->rxBuffRing.lastWrite) {
174462306a36Sopenharmony_ci		/* rxBuff ring is empty, try to fill it. */
174562306a36Sopenharmony_ci		typhoon_fill_free_ring(tp);
174662306a36Sopenharmony_ci	}
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ci	if (work_done < budget) {
174962306a36Sopenharmony_ci		napi_complete_done(napi, work_done);
175062306a36Sopenharmony_ci		iowrite32(TYPHOON_INTR_NONE,
175162306a36Sopenharmony_ci				tp->ioaddr + TYPHOON_REG_INTR_MASK);
175262306a36Sopenharmony_ci		typhoon_post_pci_writes(tp->ioaddr);
175362306a36Sopenharmony_ci	}
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	return work_done;
175662306a36Sopenharmony_ci}
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_cistatic irqreturn_t
175962306a36Sopenharmony_cityphoon_interrupt(int irq, void *dev_instance)
176062306a36Sopenharmony_ci{
176162306a36Sopenharmony_ci	struct net_device *dev = dev_instance;
176262306a36Sopenharmony_ci	struct typhoon *tp = netdev_priv(dev);
176362306a36Sopenharmony_ci	void __iomem *ioaddr = tp->ioaddr;
176462306a36Sopenharmony_ci	u32 intr_status;
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	intr_status = ioread32(ioaddr + TYPHOON_REG_INTR_STATUS);
176762306a36Sopenharmony_ci	if (!(intr_status & TYPHOON_INTR_HOST_INT))
176862306a36Sopenharmony_ci		return IRQ_NONE;
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	iowrite32(intr_status, ioaddr + TYPHOON_REG_INTR_STATUS);
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	if (napi_schedule_prep(&tp->napi)) {
177362306a36Sopenharmony_ci		iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
177462306a36Sopenharmony_ci		typhoon_post_pci_writes(ioaddr);
177562306a36Sopenharmony_ci		__napi_schedule(&tp->napi);
177662306a36Sopenharmony_ci	} else {
177762306a36Sopenharmony_ci		netdev_err(dev, "Error, poll already scheduled\n");
177862306a36Sopenharmony_ci	}
177962306a36Sopenharmony_ci	return IRQ_HANDLED;
178062306a36Sopenharmony_ci}
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_cistatic void
178362306a36Sopenharmony_cityphoon_free_rx_rings(struct typhoon *tp)
178462306a36Sopenharmony_ci{
178562306a36Sopenharmony_ci	u32 i;
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci	for (i = 0; i < RXENT_ENTRIES; i++) {
178862306a36Sopenharmony_ci		struct rxbuff_ent *rxb = &tp->rxbuffers[i];
178962306a36Sopenharmony_ci		if (rxb->skb) {
179062306a36Sopenharmony_ci			dma_unmap_single(&tp->pdev->dev, rxb->dma_addr,
179162306a36Sopenharmony_ci					 PKT_BUF_SZ, DMA_FROM_DEVICE);
179262306a36Sopenharmony_ci			dev_kfree_skb(rxb->skb);
179362306a36Sopenharmony_ci			rxb->skb = NULL;
179462306a36Sopenharmony_ci		}
179562306a36Sopenharmony_ci	}
179662306a36Sopenharmony_ci}
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_cistatic int
179962306a36Sopenharmony_cityphoon_sleep_early(struct typhoon *tp, __le16 events)
180062306a36Sopenharmony_ci{
180162306a36Sopenharmony_ci	void __iomem *ioaddr = tp->ioaddr;
180262306a36Sopenharmony_ci	struct cmd_desc xp_cmd;
180362306a36Sopenharmony_ci	int err;
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_ENABLE_WAKE_EVENTS);
180662306a36Sopenharmony_ci	xp_cmd.parm1 = events;
180762306a36Sopenharmony_ci	err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
180862306a36Sopenharmony_ci	if (err < 0) {
180962306a36Sopenharmony_ci		netdev_err(tp->dev, "typhoon_sleep(): wake events cmd err %d\n",
181062306a36Sopenharmony_ci			   err);
181162306a36Sopenharmony_ci		return err;
181262306a36Sopenharmony_ci	}
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_GOTO_SLEEP);
181562306a36Sopenharmony_ci	err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
181662306a36Sopenharmony_ci	if (err < 0) {
181762306a36Sopenharmony_ci		netdev_err(tp->dev, "typhoon_sleep(): sleep cmd err %d\n", err);
181862306a36Sopenharmony_ci		return err;
181962306a36Sopenharmony_ci	}
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci	if (typhoon_wait_status(ioaddr, TYPHOON_STATUS_SLEEPING) < 0)
182262306a36Sopenharmony_ci		return -ETIMEDOUT;
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	/* Since we cannot monitor the status of the link while sleeping,
182562306a36Sopenharmony_ci	 * tell the world it went away.
182662306a36Sopenharmony_ci	 */
182762306a36Sopenharmony_ci	netif_carrier_off(tp->dev);
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci	return 0;
183062306a36Sopenharmony_ci}
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_cistatic int
183362306a36Sopenharmony_cityphoon_sleep(struct typhoon *tp, pci_power_t state, __le16 events)
183462306a36Sopenharmony_ci{
183562306a36Sopenharmony_ci	int err;
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	err = typhoon_sleep_early(tp, events);
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci	if (err)
184062306a36Sopenharmony_ci		return err;
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_ci	pci_enable_wake(tp->pdev, state, 1);
184362306a36Sopenharmony_ci	pci_disable_device(tp->pdev);
184462306a36Sopenharmony_ci	return pci_set_power_state(tp->pdev, state);
184562306a36Sopenharmony_ci}
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_cistatic int
184862306a36Sopenharmony_cityphoon_wakeup(struct typhoon *tp, int wait_type)
184962306a36Sopenharmony_ci{
185062306a36Sopenharmony_ci	void __iomem *ioaddr = tp->ioaddr;
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	/* Post 2.x.x versions of the Sleep Image require a reset before
185362306a36Sopenharmony_ci	 * we can download the Runtime Image. But let's not make users of
185462306a36Sopenharmony_ci	 * the old firmware pay for the reset.
185562306a36Sopenharmony_ci	 */
185662306a36Sopenharmony_ci	iowrite32(TYPHOON_BOOTCMD_WAKEUP, ioaddr + TYPHOON_REG_COMMAND);
185762306a36Sopenharmony_ci	if (typhoon_wait_status(ioaddr, TYPHOON_STATUS_WAITING_FOR_HOST) < 0 ||
185862306a36Sopenharmony_ci			(tp->capabilities & TYPHOON_WAKEUP_NEEDS_RESET))
185962306a36Sopenharmony_ci		return typhoon_reset(ioaddr, wait_type);
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci	return 0;
186262306a36Sopenharmony_ci}
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_cistatic int
186562306a36Sopenharmony_cityphoon_start_runtime(struct typhoon *tp)
186662306a36Sopenharmony_ci{
186762306a36Sopenharmony_ci	struct net_device *dev = tp->dev;
186862306a36Sopenharmony_ci	void __iomem *ioaddr = tp->ioaddr;
186962306a36Sopenharmony_ci	struct cmd_desc xp_cmd;
187062306a36Sopenharmony_ci	int err;
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	typhoon_init_rings(tp);
187362306a36Sopenharmony_ci	typhoon_fill_free_ring(tp);
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci	err = typhoon_download_firmware(tp);
187662306a36Sopenharmony_ci	if (err < 0) {
187762306a36Sopenharmony_ci		netdev_err(tp->dev, "cannot load runtime on 3XP\n");
187862306a36Sopenharmony_ci		goto error_out;
187962306a36Sopenharmony_ci	}
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	if (typhoon_boot_3XP(tp, TYPHOON_STATUS_WAITING_FOR_BOOT) < 0) {
188262306a36Sopenharmony_ci		netdev_err(tp->dev, "cannot boot 3XP\n");
188362306a36Sopenharmony_ci		err = -EIO;
188462306a36Sopenharmony_ci		goto error_out;
188562306a36Sopenharmony_ci	}
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_SET_MAX_PKT_SIZE);
188862306a36Sopenharmony_ci	xp_cmd.parm1 = cpu_to_le16(PKT_BUF_SZ);
188962306a36Sopenharmony_ci	err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
189062306a36Sopenharmony_ci	if (err < 0)
189162306a36Sopenharmony_ci		goto error_out;
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci	INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_SET_MAC_ADDRESS);
189462306a36Sopenharmony_ci	xp_cmd.parm1 = cpu_to_le16(ntohs(*(__be16 *)&dev->dev_addr[0]));
189562306a36Sopenharmony_ci	xp_cmd.parm2 = cpu_to_le32(ntohl(*(__be32 *)&dev->dev_addr[2]));
189662306a36Sopenharmony_ci	err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
189762306a36Sopenharmony_ci	if (err < 0)
189862306a36Sopenharmony_ci		goto error_out;
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci	/* Disable IRQ coalescing -- we can reenable it when 3Com gives
190162306a36Sopenharmony_ci	 * us some more information on how to control it.
190262306a36Sopenharmony_ci	 */
190362306a36Sopenharmony_ci	INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_IRQ_COALESCE_CTRL);
190462306a36Sopenharmony_ci	xp_cmd.parm1 = 0;
190562306a36Sopenharmony_ci	err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
190662306a36Sopenharmony_ci	if (err < 0)
190762306a36Sopenharmony_ci		goto error_out;
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci	INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_XCVR_SELECT);
191062306a36Sopenharmony_ci	xp_cmd.parm1 = tp->xcvr_select;
191162306a36Sopenharmony_ci	err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
191262306a36Sopenharmony_ci	if (err < 0)
191362306a36Sopenharmony_ci		goto error_out;
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_VLAN_TYPE_WRITE);
191662306a36Sopenharmony_ci	xp_cmd.parm1 = cpu_to_le16(ETH_P_8021Q);
191762306a36Sopenharmony_ci	err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
191862306a36Sopenharmony_ci	if (err < 0)
191962306a36Sopenharmony_ci		goto error_out;
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci	INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_SET_OFFLOAD_TASKS);
192262306a36Sopenharmony_ci	xp_cmd.parm2 = tp->offload;
192362306a36Sopenharmony_ci	xp_cmd.parm3 = tp->offload;
192462306a36Sopenharmony_ci	err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
192562306a36Sopenharmony_ci	if (err < 0)
192662306a36Sopenharmony_ci		goto error_out;
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci	typhoon_set_rx_mode(dev);
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_TX_ENABLE);
193162306a36Sopenharmony_ci	err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
193262306a36Sopenharmony_ci	if (err < 0)
193362306a36Sopenharmony_ci		goto error_out;
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_RX_ENABLE);
193662306a36Sopenharmony_ci	err = typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
193762306a36Sopenharmony_ci	if (err < 0)
193862306a36Sopenharmony_ci		goto error_out;
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci	tp->card_state = Running;
194162306a36Sopenharmony_ci	smp_wmb();
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci	iowrite32(TYPHOON_INTR_ENABLE_ALL, ioaddr + TYPHOON_REG_INTR_ENABLE);
194462306a36Sopenharmony_ci	iowrite32(TYPHOON_INTR_NONE, ioaddr + TYPHOON_REG_INTR_MASK);
194562306a36Sopenharmony_ci	typhoon_post_pci_writes(ioaddr);
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	return 0;
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_cierror_out:
195062306a36Sopenharmony_ci	typhoon_reset(ioaddr, WaitNoSleep);
195162306a36Sopenharmony_ci	typhoon_free_rx_rings(tp);
195262306a36Sopenharmony_ci	typhoon_init_rings(tp);
195362306a36Sopenharmony_ci	return err;
195462306a36Sopenharmony_ci}
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_cistatic int
195762306a36Sopenharmony_cityphoon_stop_runtime(struct typhoon *tp, int wait_type)
195862306a36Sopenharmony_ci{
195962306a36Sopenharmony_ci	struct typhoon_indexes *indexes = tp->indexes;
196062306a36Sopenharmony_ci	struct transmit_ring *txLo = &tp->txLoRing;
196162306a36Sopenharmony_ci	void __iomem *ioaddr = tp->ioaddr;
196262306a36Sopenharmony_ci	struct cmd_desc xp_cmd;
196362306a36Sopenharmony_ci	int i;
196462306a36Sopenharmony_ci
196562306a36Sopenharmony_ci	/* Disable interrupts early, since we can't schedule a poll
196662306a36Sopenharmony_ci	 * when called with !netif_running(). This will be posted
196762306a36Sopenharmony_ci	 * when we force the posting of the command.
196862306a36Sopenharmony_ci	 */
196962306a36Sopenharmony_ci	iowrite32(TYPHOON_INTR_NONE, ioaddr + TYPHOON_REG_INTR_ENABLE);
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_RX_DISABLE);
197262306a36Sopenharmony_ci	typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci	/* Wait 1/2 sec for any outstanding transmits to occur
197562306a36Sopenharmony_ci	 * We'll cleanup after the reset if this times out.
197662306a36Sopenharmony_ci	 */
197762306a36Sopenharmony_ci	for (i = 0; i < TYPHOON_WAIT_TIMEOUT; i++) {
197862306a36Sopenharmony_ci		if (indexes->txLoCleared == cpu_to_le32(txLo->lastWrite))
197962306a36Sopenharmony_ci			break;
198062306a36Sopenharmony_ci		udelay(TYPHOON_UDELAY);
198162306a36Sopenharmony_ci	}
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci	if (i == TYPHOON_WAIT_TIMEOUT)
198462306a36Sopenharmony_ci		netdev_err(tp->dev, "halt timed out waiting for Tx to complete\n");
198562306a36Sopenharmony_ci
198662306a36Sopenharmony_ci	INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_TX_DISABLE);
198762306a36Sopenharmony_ci	typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ci	/* save the statistics so when we bring the interface up again,
199062306a36Sopenharmony_ci	 * the values reported to userspace are correct.
199162306a36Sopenharmony_ci	 */
199262306a36Sopenharmony_ci	tp->card_state = Sleeping;
199362306a36Sopenharmony_ci	smp_wmb();
199462306a36Sopenharmony_ci	typhoon_do_get_stats(tp);
199562306a36Sopenharmony_ci	memcpy(&tp->stats_saved, &tp->dev->stats, sizeof(struct net_device_stats));
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci	INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_HALT);
199862306a36Sopenharmony_ci	typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL);
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_ci	if (typhoon_wait_status(ioaddr, TYPHOON_STATUS_HALTED) < 0)
200162306a36Sopenharmony_ci		netdev_err(tp->dev, "timed out waiting for 3XP to halt\n");
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_ci	if (typhoon_reset(ioaddr, wait_type) < 0) {
200462306a36Sopenharmony_ci		netdev_err(tp->dev, "unable to reset 3XP\n");
200562306a36Sopenharmony_ci		return -ETIMEDOUT;
200662306a36Sopenharmony_ci	}
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci	/* cleanup any outstanding Tx packets */
200962306a36Sopenharmony_ci	if (indexes->txLoCleared != cpu_to_le32(txLo->lastWrite)) {
201062306a36Sopenharmony_ci		indexes->txLoCleared = cpu_to_le32(txLo->lastWrite);
201162306a36Sopenharmony_ci		typhoon_clean_tx(tp, &tp->txLoRing, &indexes->txLoCleared);
201262306a36Sopenharmony_ci	}
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci	return 0;
201562306a36Sopenharmony_ci}
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_cistatic void
201862306a36Sopenharmony_cityphoon_tx_timeout(struct net_device *dev, unsigned int txqueue)
201962306a36Sopenharmony_ci{
202062306a36Sopenharmony_ci	struct typhoon *tp = netdev_priv(dev);
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci	if (typhoon_reset(tp->ioaddr, WaitNoSleep) < 0) {
202362306a36Sopenharmony_ci		netdev_warn(dev, "could not reset in tx timeout\n");
202462306a36Sopenharmony_ci		goto truly_dead;
202562306a36Sopenharmony_ci	}
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci	/* If we ever start using the Hi ring, it will need cleaning too */
202862306a36Sopenharmony_ci	typhoon_clean_tx(tp, &tp->txLoRing, &tp->indexes->txLoCleared);
202962306a36Sopenharmony_ci	typhoon_free_rx_rings(tp);
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	if (typhoon_start_runtime(tp) < 0) {
203262306a36Sopenharmony_ci		netdev_err(dev, "could not start runtime in tx timeout\n");
203362306a36Sopenharmony_ci		goto truly_dead;
203462306a36Sopenharmony_ci        }
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci	netif_wake_queue(dev);
203762306a36Sopenharmony_ci	return;
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_citruly_dead:
204062306a36Sopenharmony_ci	/* Reset the hardware, and turn off carrier to avoid more timeouts */
204162306a36Sopenharmony_ci	typhoon_reset(tp->ioaddr, NoWait);
204262306a36Sopenharmony_ci	netif_carrier_off(dev);
204362306a36Sopenharmony_ci}
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_cistatic int
204662306a36Sopenharmony_cityphoon_open(struct net_device *dev)
204762306a36Sopenharmony_ci{
204862306a36Sopenharmony_ci	struct typhoon *tp = netdev_priv(dev);
204962306a36Sopenharmony_ci	int err;
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci	err = typhoon_request_firmware(tp);
205262306a36Sopenharmony_ci	if (err)
205362306a36Sopenharmony_ci		goto out;
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	pci_set_power_state(tp->pdev, PCI_D0);
205662306a36Sopenharmony_ci	pci_restore_state(tp->pdev);
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ci	err = typhoon_wakeup(tp, WaitSleep);
205962306a36Sopenharmony_ci	if (err < 0) {
206062306a36Sopenharmony_ci		netdev_err(dev, "unable to wakeup device\n");
206162306a36Sopenharmony_ci		goto out_sleep;
206262306a36Sopenharmony_ci	}
206362306a36Sopenharmony_ci
206462306a36Sopenharmony_ci	err = request_irq(dev->irq, typhoon_interrupt, IRQF_SHARED,
206562306a36Sopenharmony_ci				dev->name, dev);
206662306a36Sopenharmony_ci	if (err < 0)
206762306a36Sopenharmony_ci		goto out_sleep;
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	napi_enable(&tp->napi);
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci	err = typhoon_start_runtime(tp);
207262306a36Sopenharmony_ci	if (err < 0) {
207362306a36Sopenharmony_ci		napi_disable(&tp->napi);
207462306a36Sopenharmony_ci		goto out_irq;
207562306a36Sopenharmony_ci	}
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci	netif_start_queue(dev);
207862306a36Sopenharmony_ci	return 0;
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ciout_irq:
208162306a36Sopenharmony_ci	free_irq(dev->irq, dev);
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_ciout_sleep:
208462306a36Sopenharmony_ci	if (typhoon_boot_3XP(tp, TYPHOON_STATUS_WAITING_FOR_HOST) < 0) {
208562306a36Sopenharmony_ci		netdev_err(dev, "unable to reboot into sleep img\n");
208662306a36Sopenharmony_ci		typhoon_reset(tp->ioaddr, NoWait);
208762306a36Sopenharmony_ci		goto out;
208862306a36Sopenharmony_ci	}
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci	if (typhoon_sleep(tp, PCI_D3hot, 0) < 0)
209162306a36Sopenharmony_ci		netdev_err(dev, "unable to go back to sleep\n");
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_ciout:
209462306a36Sopenharmony_ci	return err;
209562306a36Sopenharmony_ci}
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_cistatic int
209862306a36Sopenharmony_cityphoon_close(struct net_device *dev)
209962306a36Sopenharmony_ci{
210062306a36Sopenharmony_ci	struct typhoon *tp = netdev_priv(dev);
210162306a36Sopenharmony_ci
210262306a36Sopenharmony_ci	netif_stop_queue(dev);
210362306a36Sopenharmony_ci	napi_disable(&tp->napi);
210462306a36Sopenharmony_ci
210562306a36Sopenharmony_ci	if (typhoon_stop_runtime(tp, WaitSleep) < 0)
210662306a36Sopenharmony_ci		netdev_err(dev, "unable to stop runtime\n");
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_ci	/* Make sure there is no irq handler running on a different CPU. */
210962306a36Sopenharmony_ci	free_irq(dev->irq, dev);
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_ci	typhoon_free_rx_rings(tp);
211262306a36Sopenharmony_ci	typhoon_init_rings(tp);
211362306a36Sopenharmony_ci
211462306a36Sopenharmony_ci	if (typhoon_boot_3XP(tp, TYPHOON_STATUS_WAITING_FOR_HOST) < 0)
211562306a36Sopenharmony_ci		netdev_err(dev, "unable to boot sleep image\n");
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci	if (typhoon_sleep(tp, PCI_D3hot, 0) < 0)
211862306a36Sopenharmony_ci		netdev_err(dev, "unable to put card to sleep\n");
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ci	return 0;
212162306a36Sopenharmony_ci}
212262306a36Sopenharmony_ci
212362306a36Sopenharmony_cistatic int __maybe_unused
212462306a36Sopenharmony_cityphoon_resume(struct device *dev_d)
212562306a36Sopenharmony_ci{
212662306a36Sopenharmony_ci	struct net_device *dev = dev_get_drvdata(dev_d);
212762306a36Sopenharmony_ci	struct typhoon *tp = netdev_priv(dev);
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_ci	/* If we're down, resume when we are upped.
213062306a36Sopenharmony_ci	 */
213162306a36Sopenharmony_ci	if (!netif_running(dev))
213262306a36Sopenharmony_ci		return 0;
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_ci	if (typhoon_wakeup(tp, WaitNoSleep) < 0) {
213562306a36Sopenharmony_ci		netdev_err(dev, "critical: could not wake up in resume\n");
213662306a36Sopenharmony_ci		goto reset;
213762306a36Sopenharmony_ci	}
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	if (typhoon_start_runtime(tp) < 0) {
214062306a36Sopenharmony_ci		netdev_err(dev, "critical: could not start runtime in resume\n");
214162306a36Sopenharmony_ci		goto reset;
214262306a36Sopenharmony_ci	}
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_ci	netif_device_attach(dev);
214562306a36Sopenharmony_ci	return 0;
214662306a36Sopenharmony_ci
214762306a36Sopenharmony_cireset:
214862306a36Sopenharmony_ci	typhoon_reset(tp->ioaddr, NoWait);
214962306a36Sopenharmony_ci	return -EBUSY;
215062306a36Sopenharmony_ci}
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_cistatic int __maybe_unused
215362306a36Sopenharmony_cityphoon_suspend(struct device *dev_d)
215462306a36Sopenharmony_ci{
215562306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(dev_d);
215662306a36Sopenharmony_ci	struct net_device *dev = pci_get_drvdata(pdev);
215762306a36Sopenharmony_ci	struct typhoon *tp = netdev_priv(dev);
215862306a36Sopenharmony_ci	struct cmd_desc xp_cmd;
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci	/* If we're down, we're already suspended.
216162306a36Sopenharmony_ci	 */
216262306a36Sopenharmony_ci	if (!netif_running(dev))
216362306a36Sopenharmony_ci		return 0;
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_ci	/* TYPHOON_OFFLOAD_VLAN is always on now, so this doesn't work */
216662306a36Sopenharmony_ci	if (tp->wol_events & TYPHOON_WAKE_MAGIC_PKT)
216762306a36Sopenharmony_ci		netdev_warn(dev, "cannot do WAKE_MAGIC with VLAN offloading\n");
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci	netif_device_detach(dev);
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci	if (typhoon_stop_runtime(tp, WaitNoSleep) < 0) {
217262306a36Sopenharmony_ci		netdev_err(dev, "unable to stop runtime\n");
217362306a36Sopenharmony_ci		goto need_resume;
217462306a36Sopenharmony_ci	}
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	typhoon_free_rx_rings(tp);
217762306a36Sopenharmony_ci	typhoon_init_rings(tp);
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_ci	if (typhoon_boot_3XP(tp, TYPHOON_STATUS_WAITING_FOR_HOST) < 0) {
218062306a36Sopenharmony_ci		netdev_err(dev, "unable to boot sleep image\n");
218162306a36Sopenharmony_ci		goto need_resume;
218262306a36Sopenharmony_ci	}
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_ci	INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_SET_MAC_ADDRESS);
218562306a36Sopenharmony_ci	xp_cmd.parm1 = cpu_to_le16(ntohs(*(__be16 *)&dev->dev_addr[0]));
218662306a36Sopenharmony_ci	xp_cmd.parm2 = cpu_to_le32(ntohl(*(__be32 *)&dev->dev_addr[2]));
218762306a36Sopenharmony_ci	if (typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL) < 0) {
218862306a36Sopenharmony_ci		netdev_err(dev, "unable to set mac address in suspend\n");
218962306a36Sopenharmony_ci		goto need_resume;
219062306a36Sopenharmony_ci	}
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_ci	INIT_COMMAND_NO_RESPONSE(&xp_cmd, TYPHOON_CMD_SET_RX_FILTER);
219362306a36Sopenharmony_ci	xp_cmd.parm1 = TYPHOON_RX_FILTER_DIRECTED | TYPHOON_RX_FILTER_BROADCAST;
219462306a36Sopenharmony_ci	if (typhoon_issue_command(tp, 1, &xp_cmd, 0, NULL) < 0) {
219562306a36Sopenharmony_ci		netdev_err(dev, "unable to set rx filter in suspend\n");
219662306a36Sopenharmony_ci		goto need_resume;
219762306a36Sopenharmony_ci	}
219862306a36Sopenharmony_ci
219962306a36Sopenharmony_ci	if (typhoon_sleep_early(tp, tp->wol_events) < 0) {
220062306a36Sopenharmony_ci		netdev_err(dev, "unable to put card to sleep\n");
220162306a36Sopenharmony_ci		goto need_resume;
220262306a36Sopenharmony_ci	}
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci	device_wakeup_enable(dev_d);
220562306a36Sopenharmony_ci
220662306a36Sopenharmony_ci	return 0;
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_cineed_resume:
220962306a36Sopenharmony_ci	typhoon_resume(dev_d);
221062306a36Sopenharmony_ci	return -EBUSY;
221162306a36Sopenharmony_ci}
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_cistatic int
221462306a36Sopenharmony_cityphoon_test_mmio(struct pci_dev *pdev)
221562306a36Sopenharmony_ci{
221662306a36Sopenharmony_ci	void __iomem *ioaddr = pci_iomap(pdev, 1, 128);
221762306a36Sopenharmony_ci	int mode = 0;
221862306a36Sopenharmony_ci	u32 val;
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_ci	if (!ioaddr)
222162306a36Sopenharmony_ci		goto out;
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_ci	if (ioread32(ioaddr + TYPHOON_REG_STATUS) !=
222462306a36Sopenharmony_ci				TYPHOON_STATUS_WAITING_FOR_HOST)
222562306a36Sopenharmony_ci		goto out_unmap;
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci	iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
222862306a36Sopenharmony_ci	iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_STATUS);
222962306a36Sopenharmony_ci	iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_ENABLE);
223062306a36Sopenharmony_ci
223162306a36Sopenharmony_ci	/* Ok, see if we can change our interrupt status register by
223262306a36Sopenharmony_ci	 * sending ourselves an interrupt. If so, then MMIO works.
223362306a36Sopenharmony_ci	 * The 50usec delay is arbitrary -- it could probably be smaller.
223462306a36Sopenharmony_ci	 */
223562306a36Sopenharmony_ci	val = ioread32(ioaddr + TYPHOON_REG_INTR_STATUS);
223662306a36Sopenharmony_ci	if ((val & TYPHOON_INTR_SELF) == 0) {
223762306a36Sopenharmony_ci		iowrite32(1, ioaddr + TYPHOON_REG_SELF_INTERRUPT);
223862306a36Sopenharmony_ci		ioread32(ioaddr + TYPHOON_REG_INTR_STATUS);
223962306a36Sopenharmony_ci		udelay(50);
224062306a36Sopenharmony_ci		val = ioread32(ioaddr + TYPHOON_REG_INTR_STATUS);
224162306a36Sopenharmony_ci		if (val & TYPHOON_INTR_SELF)
224262306a36Sopenharmony_ci			mode = 1;
224362306a36Sopenharmony_ci	}
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_ci	iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_MASK);
224662306a36Sopenharmony_ci	iowrite32(TYPHOON_INTR_ALL, ioaddr + TYPHOON_REG_INTR_STATUS);
224762306a36Sopenharmony_ci	iowrite32(TYPHOON_INTR_NONE, ioaddr + TYPHOON_REG_INTR_ENABLE);
224862306a36Sopenharmony_ci	ioread32(ioaddr + TYPHOON_REG_INTR_STATUS);
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_ciout_unmap:
225162306a36Sopenharmony_ci	pci_iounmap(pdev, ioaddr);
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_ciout:
225462306a36Sopenharmony_ci	if (!mode)
225562306a36Sopenharmony_ci		pr_info("%s: falling back to port IO\n", pci_name(pdev));
225662306a36Sopenharmony_ci	return mode;
225762306a36Sopenharmony_ci}
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci#if MAX_SKB_FRAGS > 32
226062306a36Sopenharmony_ci
226162306a36Sopenharmony_ci#include <net/vxlan.h>
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_cistatic netdev_features_t typhoon_features_check(struct sk_buff *skb,
226462306a36Sopenharmony_ci						struct net_device *dev,
226562306a36Sopenharmony_ci						netdev_features_t features)
226662306a36Sopenharmony_ci{
226762306a36Sopenharmony_ci	if (skb_shinfo(skb)->nr_frags > 32 && skb_is_gso(skb))
226862306a36Sopenharmony_ci		features &= ~NETIF_F_GSO_MASK;
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_ci	features = vlan_features_check(skb, features);
227162306a36Sopenharmony_ci	return vxlan_features_check(skb, features);
227262306a36Sopenharmony_ci}
227362306a36Sopenharmony_ci#endif
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_cistatic const struct net_device_ops typhoon_netdev_ops = {
227662306a36Sopenharmony_ci	.ndo_open		= typhoon_open,
227762306a36Sopenharmony_ci	.ndo_stop		= typhoon_close,
227862306a36Sopenharmony_ci#if MAX_SKB_FRAGS > 32
227962306a36Sopenharmony_ci	.ndo_features_check	= typhoon_features_check,
228062306a36Sopenharmony_ci#endif
228162306a36Sopenharmony_ci	.ndo_start_xmit		= typhoon_start_tx,
228262306a36Sopenharmony_ci	.ndo_set_rx_mode	= typhoon_set_rx_mode,
228362306a36Sopenharmony_ci	.ndo_tx_timeout		= typhoon_tx_timeout,
228462306a36Sopenharmony_ci	.ndo_get_stats		= typhoon_get_stats,
228562306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
228662306a36Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
228762306a36Sopenharmony_ci};
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_cistatic int
229062306a36Sopenharmony_cityphoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
229162306a36Sopenharmony_ci{
229262306a36Sopenharmony_ci	struct net_device *dev;
229362306a36Sopenharmony_ci	struct typhoon *tp;
229462306a36Sopenharmony_ci	int card_id = (int) ent->driver_data;
229562306a36Sopenharmony_ci	u8 addr[ETH_ALEN] __aligned(4);
229662306a36Sopenharmony_ci	void __iomem *ioaddr;
229762306a36Sopenharmony_ci	void *shared;
229862306a36Sopenharmony_ci	dma_addr_t shared_dma;
229962306a36Sopenharmony_ci	struct cmd_desc xp_cmd;
230062306a36Sopenharmony_ci	struct resp_desc xp_resp[3];
230162306a36Sopenharmony_ci	int err = 0;
230262306a36Sopenharmony_ci	const char *err_msg;
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof(*tp));
230562306a36Sopenharmony_ci	if (dev == NULL) {
230662306a36Sopenharmony_ci		err_msg = "unable to alloc new net device";
230762306a36Sopenharmony_ci		err = -ENOMEM;
230862306a36Sopenharmony_ci		goto error_out;
230962306a36Sopenharmony_ci	}
231062306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, &pdev->dev);
231162306a36Sopenharmony_ci
231262306a36Sopenharmony_ci	err = pci_enable_device(pdev);
231362306a36Sopenharmony_ci	if (err < 0) {
231462306a36Sopenharmony_ci		err_msg = "unable to enable device";
231562306a36Sopenharmony_ci		goto error_out_dev;
231662306a36Sopenharmony_ci	}
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci	err = pci_set_mwi(pdev);
231962306a36Sopenharmony_ci	if (err < 0) {
232062306a36Sopenharmony_ci		err_msg = "unable to set MWI";
232162306a36Sopenharmony_ci		goto error_out_disable;
232262306a36Sopenharmony_ci	}
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_ci	err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
232562306a36Sopenharmony_ci	if (err < 0) {
232662306a36Sopenharmony_ci		err_msg = "No usable DMA configuration";
232762306a36Sopenharmony_ci		goto error_out_mwi;
232862306a36Sopenharmony_ci	}
232962306a36Sopenharmony_ci
233062306a36Sopenharmony_ci	/* sanity checks on IO and MMIO BARs
233162306a36Sopenharmony_ci	 */
233262306a36Sopenharmony_ci	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_IO)) {
233362306a36Sopenharmony_ci		err_msg = "region #1 not a PCI IO resource, aborting";
233462306a36Sopenharmony_ci		err = -ENODEV;
233562306a36Sopenharmony_ci		goto error_out_mwi;
233662306a36Sopenharmony_ci	}
233762306a36Sopenharmony_ci	if (pci_resource_len(pdev, 0) < 128) {
233862306a36Sopenharmony_ci		err_msg = "Invalid PCI IO region size, aborting";
233962306a36Sopenharmony_ci		err = -ENODEV;
234062306a36Sopenharmony_ci		goto error_out_mwi;
234162306a36Sopenharmony_ci	}
234262306a36Sopenharmony_ci	if (!(pci_resource_flags(pdev, 1) & IORESOURCE_MEM)) {
234362306a36Sopenharmony_ci		err_msg = "region #1 not a PCI MMIO resource, aborting";
234462306a36Sopenharmony_ci		err = -ENODEV;
234562306a36Sopenharmony_ci		goto error_out_mwi;
234662306a36Sopenharmony_ci	}
234762306a36Sopenharmony_ci	if (pci_resource_len(pdev, 1) < 128) {
234862306a36Sopenharmony_ci		err_msg = "Invalid PCI MMIO region size, aborting";
234962306a36Sopenharmony_ci		err = -ENODEV;
235062306a36Sopenharmony_ci		goto error_out_mwi;
235162306a36Sopenharmony_ci	}
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci	err = pci_request_regions(pdev, KBUILD_MODNAME);
235462306a36Sopenharmony_ci	if (err < 0) {
235562306a36Sopenharmony_ci		err_msg = "could not request regions";
235662306a36Sopenharmony_ci		goto error_out_mwi;
235762306a36Sopenharmony_ci	}
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci	/* map our registers
236062306a36Sopenharmony_ci	 */
236162306a36Sopenharmony_ci	if (use_mmio != 0 && use_mmio != 1)
236262306a36Sopenharmony_ci		use_mmio = typhoon_test_mmio(pdev);
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci	ioaddr = pci_iomap(pdev, use_mmio, 128);
236562306a36Sopenharmony_ci	if (!ioaddr) {
236662306a36Sopenharmony_ci		err_msg = "cannot remap registers, aborting";
236762306a36Sopenharmony_ci		err = -EIO;
236862306a36Sopenharmony_ci		goto error_out_regions;
236962306a36Sopenharmony_ci	}
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_ci	/* allocate pci dma space for rx and tx descriptor rings
237262306a36Sopenharmony_ci	 */
237362306a36Sopenharmony_ci	shared = dma_alloc_coherent(&pdev->dev, sizeof(struct typhoon_shared),
237462306a36Sopenharmony_ci				    &shared_dma, GFP_KERNEL);
237562306a36Sopenharmony_ci	if (!shared) {
237662306a36Sopenharmony_ci		err_msg = "could not allocate DMA memory";
237762306a36Sopenharmony_ci		err = -ENOMEM;
237862306a36Sopenharmony_ci		goto error_out_remap;
237962306a36Sopenharmony_ci	}
238062306a36Sopenharmony_ci
238162306a36Sopenharmony_ci	dev->irq = pdev->irq;
238262306a36Sopenharmony_ci	tp = netdev_priv(dev);
238362306a36Sopenharmony_ci	tp->shared = shared;
238462306a36Sopenharmony_ci	tp->shared_dma = shared_dma;
238562306a36Sopenharmony_ci	tp->pdev = pdev;
238662306a36Sopenharmony_ci	tp->tx_pdev = pdev;
238762306a36Sopenharmony_ci	tp->ioaddr = ioaddr;
238862306a36Sopenharmony_ci	tp->tx_ioaddr = ioaddr;
238962306a36Sopenharmony_ci	tp->dev = dev;
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_ci	/* Init sequence:
239262306a36Sopenharmony_ci	 * 1) Reset the adapter to clear any bad juju
239362306a36Sopenharmony_ci	 * 2) Reload the sleep image
239462306a36Sopenharmony_ci	 * 3) Boot the sleep image
239562306a36Sopenharmony_ci	 * 4) Get the hardware address.
239662306a36Sopenharmony_ci	 * 5) Put the card to sleep.
239762306a36Sopenharmony_ci	 */
239862306a36Sopenharmony_ci	err = typhoon_reset(ioaddr, WaitSleep);
239962306a36Sopenharmony_ci	if (err < 0) {
240062306a36Sopenharmony_ci		err_msg = "could not reset 3XP";
240162306a36Sopenharmony_ci		goto error_out_dma;
240262306a36Sopenharmony_ci	}
240362306a36Sopenharmony_ci
240462306a36Sopenharmony_ci	/* Now that we've reset the 3XP and are sure it's not going to
240562306a36Sopenharmony_ci	 * write all over memory, enable bus mastering, and save our
240662306a36Sopenharmony_ci	 * state for resuming after a suspend.
240762306a36Sopenharmony_ci	 */
240862306a36Sopenharmony_ci	pci_set_master(pdev);
240962306a36Sopenharmony_ci	pci_save_state(pdev);
241062306a36Sopenharmony_ci
241162306a36Sopenharmony_ci	typhoon_init_interface(tp);
241262306a36Sopenharmony_ci	typhoon_init_rings(tp);
241362306a36Sopenharmony_ci
241462306a36Sopenharmony_ci	err = typhoon_boot_3XP(tp, TYPHOON_STATUS_WAITING_FOR_HOST);
241562306a36Sopenharmony_ci	if (err < 0) {
241662306a36Sopenharmony_ci		err_msg = "cannot boot 3XP sleep image";
241762306a36Sopenharmony_ci		goto error_out_reset;
241862306a36Sopenharmony_ci	}
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci	INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_READ_MAC_ADDRESS);
242162306a36Sopenharmony_ci	err = typhoon_issue_command(tp, 1, &xp_cmd, 1, xp_resp);
242262306a36Sopenharmony_ci	if (err < 0) {
242362306a36Sopenharmony_ci		err_msg = "cannot read MAC address";
242462306a36Sopenharmony_ci		goto error_out_reset;
242562306a36Sopenharmony_ci	}
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_ci	*(__be16 *)&addr[0] = htons(le16_to_cpu(xp_resp[0].parm1));
242862306a36Sopenharmony_ci	*(__be32 *)&addr[2] = htonl(le32_to_cpu(xp_resp[0].parm2));
242962306a36Sopenharmony_ci	eth_hw_addr_set(dev, addr);
243062306a36Sopenharmony_ci
243162306a36Sopenharmony_ci	if (!is_valid_ether_addr(dev->dev_addr)) {
243262306a36Sopenharmony_ci		err_msg = "Could not obtain valid ethernet address, aborting";
243362306a36Sopenharmony_ci		err = -EIO;
243462306a36Sopenharmony_ci		goto error_out_reset;
243562306a36Sopenharmony_ci	}
243662306a36Sopenharmony_ci
243762306a36Sopenharmony_ci	/* Read the Sleep Image version last, so the response is valid
243862306a36Sopenharmony_ci	 * later when we print out the version reported.
243962306a36Sopenharmony_ci	 */
244062306a36Sopenharmony_ci	INIT_COMMAND_WITH_RESPONSE(&xp_cmd, TYPHOON_CMD_READ_VERSIONS);
244162306a36Sopenharmony_ci	err = typhoon_issue_command(tp, 1, &xp_cmd, 3, xp_resp);
244262306a36Sopenharmony_ci	if (err < 0) {
244362306a36Sopenharmony_ci		err_msg = "Could not get Sleep Image version";
244462306a36Sopenharmony_ci		goto error_out_reset;
244562306a36Sopenharmony_ci	}
244662306a36Sopenharmony_ci
244762306a36Sopenharmony_ci	tp->capabilities = typhoon_card_info[card_id].capabilities;
244862306a36Sopenharmony_ci	tp->xcvr_select = TYPHOON_XCVR_AUTONEG;
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_ci	/* Typhoon 1.0 Sleep Images return one response descriptor to the
245162306a36Sopenharmony_ci	 * READ_VERSIONS command. Those versions are OK after waking up
245262306a36Sopenharmony_ci	 * from sleep without needing a reset. Typhoon 1.1+ Sleep Images
245362306a36Sopenharmony_ci	 * seem to need a little extra help to get started. Since we don't
245462306a36Sopenharmony_ci	 * know how to nudge it along, just kick it.
245562306a36Sopenharmony_ci	 */
245662306a36Sopenharmony_ci	if (xp_resp[0].numDesc != 0)
245762306a36Sopenharmony_ci		tp->capabilities |= TYPHOON_WAKEUP_NEEDS_RESET;
245862306a36Sopenharmony_ci
245962306a36Sopenharmony_ci	err = typhoon_sleep(tp, PCI_D3hot, 0);
246062306a36Sopenharmony_ci	if (err < 0) {
246162306a36Sopenharmony_ci		err_msg = "cannot put adapter to sleep";
246262306a36Sopenharmony_ci		goto error_out_reset;
246362306a36Sopenharmony_ci	}
246462306a36Sopenharmony_ci
246562306a36Sopenharmony_ci	/* The chip-specific entries in the device structure. */
246662306a36Sopenharmony_ci	dev->netdev_ops		= &typhoon_netdev_ops;
246762306a36Sopenharmony_ci	netif_napi_add_weight(dev, &tp->napi, typhoon_poll, 16);
246862306a36Sopenharmony_ci	dev->watchdog_timeo	= TX_TIMEOUT;
246962306a36Sopenharmony_ci
247062306a36Sopenharmony_ci	dev->ethtool_ops = &typhoon_ethtool_ops;
247162306a36Sopenharmony_ci
247262306a36Sopenharmony_ci	/* We can handle scatter gather, up to 16 entries, and
247362306a36Sopenharmony_ci	 * we can do IP checksumming (only version 4, doh...)
247462306a36Sopenharmony_ci	 *
247562306a36Sopenharmony_ci	 * There's no way to turn off the RX VLAN offloading and stripping
247662306a36Sopenharmony_ci	 * on the current 3XP firmware -- it does not respect the offload
247762306a36Sopenharmony_ci	 * settings -- so we only allow the user to toggle the TX processing.
247862306a36Sopenharmony_ci	 */
247962306a36Sopenharmony_ci	dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
248062306a36Sopenharmony_ci		NETIF_F_HW_VLAN_CTAG_TX;
248162306a36Sopenharmony_ci	dev->features = dev->hw_features |
248262306a36Sopenharmony_ci		NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_RXCSUM;
248362306a36Sopenharmony_ci
248462306a36Sopenharmony_ci	err = register_netdev(dev);
248562306a36Sopenharmony_ci	if (err < 0) {
248662306a36Sopenharmony_ci		err_msg = "unable to register netdev";
248762306a36Sopenharmony_ci		goto error_out_reset;
248862306a36Sopenharmony_ci	}
248962306a36Sopenharmony_ci
249062306a36Sopenharmony_ci	pci_set_drvdata(pdev, dev);
249162306a36Sopenharmony_ci
249262306a36Sopenharmony_ci	netdev_info(dev, "%s at %s 0x%llx, %pM\n",
249362306a36Sopenharmony_ci		    typhoon_card_info[card_id].name,
249462306a36Sopenharmony_ci		    use_mmio ? "MMIO" : "IO",
249562306a36Sopenharmony_ci		    (unsigned long long)pci_resource_start(pdev, use_mmio),
249662306a36Sopenharmony_ci		    dev->dev_addr);
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ci	/* xp_resp still contains the response to the READ_VERSIONS command.
249962306a36Sopenharmony_ci	 * For debugging, let the user know what version he has.
250062306a36Sopenharmony_ci	 */
250162306a36Sopenharmony_ci	if (xp_resp[0].numDesc == 0) {
250262306a36Sopenharmony_ci		/* This is the Typhoon 1.0 type Sleep Image, last 16 bits
250362306a36Sopenharmony_ci		 * of version is Month/Day of build.
250462306a36Sopenharmony_ci		 */
250562306a36Sopenharmony_ci		u16 monthday = le32_to_cpu(xp_resp[0].parm2) & 0xffff;
250662306a36Sopenharmony_ci		netdev_info(dev, "Typhoon 1.0 Sleep Image built %02u/%02u/2000\n",
250762306a36Sopenharmony_ci			    monthday >> 8, monthday & 0xff);
250862306a36Sopenharmony_ci	} else if (xp_resp[0].numDesc == 2) {
250962306a36Sopenharmony_ci		/* This is the Typhoon 1.1+ type Sleep Image
251062306a36Sopenharmony_ci		 */
251162306a36Sopenharmony_ci		u32 sleep_ver = le32_to_cpu(xp_resp[0].parm2);
251262306a36Sopenharmony_ci		u8 *ver_string = (u8 *) &xp_resp[1];
251362306a36Sopenharmony_ci		ver_string[25] = 0;
251462306a36Sopenharmony_ci		netdev_info(dev, "Typhoon 1.1+ Sleep Image version %02x.%03x.%03x %s\n",
251562306a36Sopenharmony_ci			    sleep_ver >> 24, (sleep_ver >> 12) & 0xfff,
251662306a36Sopenharmony_ci			    sleep_ver & 0xfff, ver_string);
251762306a36Sopenharmony_ci	} else {
251862306a36Sopenharmony_ci		netdev_warn(dev, "Unknown Sleep Image version (%u:%04x)\n",
251962306a36Sopenharmony_ci			    xp_resp[0].numDesc, le32_to_cpu(xp_resp[0].parm2));
252062306a36Sopenharmony_ci	}
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_ci	return 0;
252362306a36Sopenharmony_ci
252462306a36Sopenharmony_cierror_out_reset:
252562306a36Sopenharmony_ci	typhoon_reset(ioaddr, NoWait);
252662306a36Sopenharmony_ci
252762306a36Sopenharmony_cierror_out_dma:
252862306a36Sopenharmony_ci	dma_free_coherent(&pdev->dev, sizeof(struct typhoon_shared), shared,
252962306a36Sopenharmony_ci			  shared_dma);
253062306a36Sopenharmony_cierror_out_remap:
253162306a36Sopenharmony_ci	pci_iounmap(pdev, ioaddr);
253262306a36Sopenharmony_cierror_out_regions:
253362306a36Sopenharmony_ci	pci_release_regions(pdev);
253462306a36Sopenharmony_cierror_out_mwi:
253562306a36Sopenharmony_ci	pci_clear_mwi(pdev);
253662306a36Sopenharmony_cierror_out_disable:
253762306a36Sopenharmony_ci	pci_disable_device(pdev);
253862306a36Sopenharmony_cierror_out_dev:
253962306a36Sopenharmony_ci	free_netdev(dev);
254062306a36Sopenharmony_cierror_out:
254162306a36Sopenharmony_ci	pr_err("%s: %s\n", pci_name(pdev), err_msg);
254262306a36Sopenharmony_ci	return err;
254362306a36Sopenharmony_ci}
254462306a36Sopenharmony_ci
254562306a36Sopenharmony_cistatic void
254662306a36Sopenharmony_cityphoon_remove_one(struct pci_dev *pdev)
254762306a36Sopenharmony_ci{
254862306a36Sopenharmony_ci	struct net_device *dev = pci_get_drvdata(pdev);
254962306a36Sopenharmony_ci	struct typhoon *tp = netdev_priv(dev);
255062306a36Sopenharmony_ci
255162306a36Sopenharmony_ci	unregister_netdev(dev);
255262306a36Sopenharmony_ci	pci_set_power_state(pdev, PCI_D0);
255362306a36Sopenharmony_ci	pci_restore_state(pdev);
255462306a36Sopenharmony_ci	typhoon_reset(tp->ioaddr, NoWait);
255562306a36Sopenharmony_ci	pci_iounmap(pdev, tp->ioaddr);
255662306a36Sopenharmony_ci	dma_free_coherent(&pdev->dev, sizeof(struct typhoon_shared),
255762306a36Sopenharmony_ci			  tp->shared, tp->shared_dma);
255862306a36Sopenharmony_ci	pci_release_regions(pdev);
255962306a36Sopenharmony_ci	pci_clear_mwi(pdev);
256062306a36Sopenharmony_ci	pci_disable_device(pdev);
256162306a36Sopenharmony_ci	free_netdev(dev);
256262306a36Sopenharmony_ci}
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(typhoon_pm_ops, typhoon_suspend, typhoon_resume);
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_cistatic struct pci_driver typhoon_driver = {
256762306a36Sopenharmony_ci	.name		= KBUILD_MODNAME,
256862306a36Sopenharmony_ci	.id_table	= typhoon_pci_tbl,
256962306a36Sopenharmony_ci	.probe		= typhoon_init_one,
257062306a36Sopenharmony_ci	.remove		= typhoon_remove_one,
257162306a36Sopenharmony_ci	.driver.pm	= &typhoon_pm_ops,
257262306a36Sopenharmony_ci};
257362306a36Sopenharmony_ci
257462306a36Sopenharmony_cistatic int __init
257562306a36Sopenharmony_cityphoon_init(void)
257662306a36Sopenharmony_ci{
257762306a36Sopenharmony_ci	return pci_register_driver(&typhoon_driver);
257862306a36Sopenharmony_ci}
257962306a36Sopenharmony_ci
258062306a36Sopenharmony_cistatic void __exit
258162306a36Sopenharmony_cityphoon_cleanup(void)
258262306a36Sopenharmony_ci{
258362306a36Sopenharmony_ci	release_firmware(typhoon_fw);
258462306a36Sopenharmony_ci	pci_unregister_driver(&typhoon_driver);
258562306a36Sopenharmony_ci}
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_cimodule_init(typhoon_init);
258862306a36Sopenharmony_cimodule_exit(typhoon_cleanup);
2589