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