162306a36Sopenharmony_ci/* sundance.c: A Linux device driver for the Sundance ST201 "Alta". */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci Written 1999-2000 by Donald Becker. 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci This software may be used and distributed according to the terms of 662306a36Sopenharmony_ci the GNU General Public License (GPL), incorporated herein by reference. 762306a36Sopenharmony_ci Drivers based on or derived from this code fall under the GPL and must 862306a36Sopenharmony_ci retain the authorship, copyright and license notice. This file is not 962306a36Sopenharmony_ci a complete program and may only be used when the entire operating 1062306a36Sopenharmony_ci system is licensed under the GPL. 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci The author may be reached as becker@scyld.com, or C/O 1362306a36Sopenharmony_ci Scyld Computing Corporation 1462306a36Sopenharmony_ci 410 Severn Ave., Suite 210 1562306a36Sopenharmony_ci Annapolis MD 21403 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci Support and updates available at 1862306a36Sopenharmony_ci http://www.scyld.com/network/sundance.html 1962306a36Sopenharmony_ci [link no longer provides useful info -jgarzik] 2062306a36Sopenharmony_ci Archives of the mailing list are still available at 2162306a36Sopenharmony_ci https://www.beowulf.org/pipermail/netdrivers/ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci*/ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define DRV_NAME "sundance" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* The user-configurable values. 2862306a36Sopenharmony_ci These may be modified when a driver module is loaded.*/ 2962306a36Sopenharmony_cistatic int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ 3062306a36Sopenharmony_ci/* Maximum number of multicast addresses to filter (vs. rx-all-multicast). 3162306a36Sopenharmony_ci Typical is a 64 element hash table based on the Ethernet CRC. */ 3262306a36Sopenharmony_cistatic const int multicast_filter_limit = 32; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* Set the copy breakpoint for the copy-only-tiny-frames scheme. 3562306a36Sopenharmony_ci Setting to > 1518 effectively disables this feature. 3662306a36Sopenharmony_ci This chip can receive into offset buffers, so the Alpha does not 3762306a36Sopenharmony_ci need a copy-align. */ 3862306a36Sopenharmony_cistatic int rx_copybreak; 3962306a36Sopenharmony_cistatic int flowctrl=1; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* media[] specifies the media type the NIC operates at. 4262306a36Sopenharmony_ci autosense Autosensing active media. 4362306a36Sopenharmony_ci 10mbps_hd 10Mbps half duplex. 4462306a36Sopenharmony_ci 10mbps_fd 10Mbps full duplex. 4562306a36Sopenharmony_ci 100mbps_hd 100Mbps half duplex. 4662306a36Sopenharmony_ci 100mbps_fd 100Mbps full duplex. 4762306a36Sopenharmony_ci 0 Autosensing active media. 4862306a36Sopenharmony_ci 1 10Mbps half duplex. 4962306a36Sopenharmony_ci 2 10Mbps full duplex. 5062306a36Sopenharmony_ci 3 100Mbps half duplex. 5162306a36Sopenharmony_ci 4 100Mbps full duplex. 5262306a36Sopenharmony_ci*/ 5362306a36Sopenharmony_ci#define MAX_UNITS 8 5462306a36Sopenharmony_cistatic char *media[MAX_UNITS]; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* Operational parameters that are set at compile time. */ 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* Keep the ring sizes a power of two for compile efficiency. 6062306a36Sopenharmony_ci The compiler will convert <unsigned>'%'<2^N> into a bit mask. 6162306a36Sopenharmony_ci Making the Tx ring too large decreases the effectiveness of channel 6262306a36Sopenharmony_ci bonding and packet priority, and more than 128 requires modifying the 6362306a36Sopenharmony_ci Tx error recovery. 6462306a36Sopenharmony_ci Large receive rings merely waste memory. */ 6562306a36Sopenharmony_ci#define TX_RING_SIZE 32 6662306a36Sopenharmony_ci#define TX_QUEUE_LEN (TX_RING_SIZE - 1) /* Limit ring entries actually used. */ 6762306a36Sopenharmony_ci#define RX_RING_SIZE 64 6862306a36Sopenharmony_ci#define RX_BUDGET 32 6962306a36Sopenharmony_ci#define TX_TOTAL_SIZE TX_RING_SIZE*sizeof(struct netdev_desc) 7062306a36Sopenharmony_ci#define RX_TOTAL_SIZE RX_RING_SIZE*sizeof(struct netdev_desc) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* Operational parameters that usually are not changed. */ 7362306a36Sopenharmony_ci/* Time in jiffies before concluding the transmitter is hung. */ 7462306a36Sopenharmony_ci#define TX_TIMEOUT (4*HZ) 7562306a36Sopenharmony_ci#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* Include files, designed to support most kernel versions 2.0.0 and later. */ 7862306a36Sopenharmony_ci#include <linux/module.h> 7962306a36Sopenharmony_ci#include <linux/kernel.h> 8062306a36Sopenharmony_ci#include <linux/string.h> 8162306a36Sopenharmony_ci#include <linux/timer.h> 8262306a36Sopenharmony_ci#include <linux/errno.h> 8362306a36Sopenharmony_ci#include <linux/ioport.h> 8462306a36Sopenharmony_ci#include <linux/interrupt.h> 8562306a36Sopenharmony_ci#include <linux/pci.h> 8662306a36Sopenharmony_ci#include <linux/netdevice.h> 8762306a36Sopenharmony_ci#include <linux/etherdevice.h> 8862306a36Sopenharmony_ci#include <linux/skbuff.h> 8962306a36Sopenharmony_ci#include <linux/init.h> 9062306a36Sopenharmony_ci#include <linux/bitops.h> 9162306a36Sopenharmony_ci#include <linux/uaccess.h> 9262306a36Sopenharmony_ci#include <asm/processor.h> /* Processor type for cache alignment. */ 9362306a36Sopenharmony_ci#include <asm/io.h> 9462306a36Sopenharmony_ci#include <linux/delay.h> 9562306a36Sopenharmony_ci#include <linux/spinlock.h> 9662306a36Sopenharmony_ci#include <linux/dma-mapping.h> 9762306a36Sopenharmony_ci#include <linux/crc32.h> 9862306a36Sopenharmony_ci#include <linux/ethtool.h> 9962306a36Sopenharmony_ci#include <linux/mii.h> 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ciMODULE_AUTHOR("Donald Becker <becker@scyld.com>"); 10262306a36Sopenharmony_ciMODULE_DESCRIPTION("Sundance Alta Ethernet driver"); 10362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cimodule_param(debug, int, 0); 10662306a36Sopenharmony_cimodule_param(rx_copybreak, int, 0); 10762306a36Sopenharmony_cimodule_param_array(media, charp, NULL, 0); 10862306a36Sopenharmony_cimodule_param(flowctrl, int, 0); 10962306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "Sundance Alta debug level (0-5)"); 11062306a36Sopenharmony_ciMODULE_PARM_DESC(rx_copybreak, "Sundance Alta copy breakpoint for copy-only-tiny-frames"); 11162306a36Sopenharmony_ciMODULE_PARM_DESC(flowctrl, "Sundance Alta flow control [0|1]"); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* 11462306a36Sopenharmony_ci Theory of Operation 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ciI. Board Compatibility 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ciThis driver is designed for the Sundance Technologies "Alta" ST201 chip. 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ciII. Board-specific settings 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ciIII. Driver operation 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ciIIIa. Ring buffers 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ciThis driver uses two statically allocated fixed-size descriptor lists 12762306a36Sopenharmony_ciformed into rings by a branch from the final descriptor to the beginning of 12862306a36Sopenharmony_cithe list. The ring sizes are set at compile time by RX/TX_RING_SIZE. 12962306a36Sopenharmony_ciSome chips explicitly use only 2^N sized rings, while others use a 13062306a36Sopenharmony_ci'next descriptor' pointer that the driver forms into rings. 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ciIIIb/c. Transmit/Receive Structure 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ciThis driver uses a zero-copy receive and transmit scheme. 13562306a36Sopenharmony_ciThe driver allocates full frame size skbuffs for the Rx ring buffers at 13662306a36Sopenharmony_ciopen() time and passes the skb->data field to the chip as receive data 13762306a36Sopenharmony_cibuffers. When an incoming frame is less than RX_COPYBREAK bytes long, 13862306a36Sopenharmony_cia fresh skbuff is allocated and the frame is copied to the new skbuff. 13962306a36Sopenharmony_ciWhen the incoming frame is larger, the skbuff is passed directly up the 14062306a36Sopenharmony_ciprotocol stack. Buffers consumed this way are replaced by newly allocated 14162306a36Sopenharmony_ciskbuffs in a later phase of receives. 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ciThe RX_COPYBREAK value is chosen to trade-off the memory wasted by 14462306a36Sopenharmony_ciusing a full-sized skbuff for small frames vs. the copying costs of larger 14562306a36Sopenharmony_ciframes. New boards are typically used in generously configured machines 14662306a36Sopenharmony_ciand the underfilled buffers have negligible impact compared to the benefit of 14762306a36Sopenharmony_cia single allocation size, so the default value of zero results in never 14862306a36Sopenharmony_cicopying packets. When copying is done, the cost is usually mitigated by using 14962306a36Sopenharmony_cia combined copy/checksum routine. Copying also preloads the cache, which is 15062306a36Sopenharmony_cimost useful with small frames. 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ciA subtle aspect of the operation is that the IP header at offset 14 in an 15362306a36Sopenharmony_ciethernet frame isn't longword aligned for further processing. 15462306a36Sopenharmony_ciUnaligned buffers are permitted by the Sundance hardware, so 15562306a36Sopenharmony_ciframes are received into the skbuff at an offset of "+2", 16-byte aligning 15662306a36Sopenharmony_cithe IP header. 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ciIIId. Synchronization 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ciThe driver runs as two independent, single-threaded flows of control. One 16162306a36Sopenharmony_ciis the send-packet routine, which enforces single-threaded use by the 16262306a36Sopenharmony_cidev->tbusy flag. The other thread is the interrupt handler, which is single 16362306a36Sopenharmony_cithreaded by the hardware and interrupt handling software. 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ciThe send packet thread has partial control over the Tx ring and 'dev->tbusy' 16662306a36Sopenharmony_ciflag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next 16762306a36Sopenharmony_ciqueue slot is empty, it clears the tbusy flag when finished otherwise it sets 16862306a36Sopenharmony_cithe 'lp->tx_full' flag. 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ciThe interrupt handler has exclusive control over the Rx ring and records stats 17162306a36Sopenharmony_cifrom the Tx ring. After reaping the stats, it marks the Tx queue entry as 17262306a36Sopenharmony_ciempty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it 17362306a36Sopenharmony_ciclears both the tx_full and tbusy flags. 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ciIV. Notes 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ciIVb. References 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ciThe Sundance ST201 datasheet, preliminary version. 18062306a36Sopenharmony_ciThe Kendin KS8723 datasheet, preliminary version. 18162306a36Sopenharmony_ciThe ICplus IP100 datasheet, preliminary version. 18262306a36Sopenharmony_cihttp://www.scyld.com/expert/100mbps.html 18362306a36Sopenharmony_cihttp://www.scyld.com/expert/NWay.html 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ciIVc. Errata 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci*/ 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/* Work-around for Kendin chip bugs. */ 19062306a36Sopenharmony_ci#ifndef CONFIG_SUNDANCE_MMIO 19162306a36Sopenharmony_ci#define USE_IO_OPS 1 19262306a36Sopenharmony_ci#endif 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic const struct pci_device_id sundance_pci_tbl[] = { 19562306a36Sopenharmony_ci { 0x1186, 0x1002, 0x1186, 0x1002, 0, 0, 0 }, 19662306a36Sopenharmony_ci { 0x1186, 0x1002, 0x1186, 0x1003, 0, 0, 1 }, 19762306a36Sopenharmony_ci { 0x1186, 0x1002, 0x1186, 0x1012, 0, 0, 2 }, 19862306a36Sopenharmony_ci { 0x1186, 0x1002, 0x1186, 0x1040, 0, 0, 3 }, 19962306a36Sopenharmony_ci { 0x1186, 0x1002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, 20062306a36Sopenharmony_ci { 0x13F0, 0x0201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5 }, 20162306a36Sopenharmony_ci { 0x13F0, 0x0200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6 }, 20262306a36Sopenharmony_ci { } 20362306a36Sopenharmony_ci}; 20462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, sundance_pci_tbl); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cienum { 20762306a36Sopenharmony_ci netdev_io_size = 128 20862306a36Sopenharmony_ci}; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistruct pci_id_info { 21162306a36Sopenharmony_ci const char *name; 21262306a36Sopenharmony_ci}; 21362306a36Sopenharmony_cistatic const struct pci_id_info pci_id_tbl[] = { 21462306a36Sopenharmony_ci {"D-Link DFE-550TX FAST Ethernet Adapter"}, 21562306a36Sopenharmony_ci {"D-Link DFE-550FX 100Mbps Fiber-optics Adapter"}, 21662306a36Sopenharmony_ci {"D-Link DFE-580TX 4 port Server Adapter"}, 21762306a36Sopenharmony_ci {"D-Link DFE-530TXS FAST Ethernet Adapter"}, 21862306a36Sopenharmony_ci {"D-Link DL10050-based FAST Ethernet Adapter"}, 21962306a36Sopenharmony_ci {"Sundance Technology Alta"}, 22062306a36Sopenharmony_ci {"IC Plus Corporation IP100A FAST Ethernet Adapter"}, 22162306a36Sopenharmony_ci { } /* terminate list. */ 22262306a36Sopenharmony_ci}; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci/* This driver was written to use PCI memory space, however x86-oriented 22562306a36Sopenharmony_ci hardware often uses I/O space accesses. */ 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci/* Offsets to the device registers. 22862306a36Sopenharmony_ci Unlike software-only systems, device drivers interact with complex hardware. 22962306a36Sopenharmony_ci It's not useful to define symbolic names for every register bit in the 23062306a36Sopenharmony_ci device. The name can only partially document the semantics and make 23162306a36Sopenharmony_ci the driver longer and more difficult to read. 23262306a36Sopenharmony_ci In general, only the important configuration values or bits changed 23362306a36Sopenharmony_ci multiple times should be defined symbolically. 23462306a36Sopenharmony_ci*/ 23562306a36Sopenharmony_cienum alta_offsets { 23662306a36Sopenharmony_ci DMACtrl = 0x00, 23762306a36Sopenharmony_ci TxListPtr = 0x04, 23862306a36Sopenharmony_ci TxDMABurstThresh = 0x08, 23962306a36Sopenharmony_ci TxDMAUrgentThresh = 0x09, 24062306a36Sopenharmony_ci TxDMAPollPeriod = 0x0a, 24162306a36Sopenharmony_ci RxDMAStatus = 0x0c, 24262306a36Sopenharmony_ci RxListPtr = 0x10, 24362306a36Sopenharmony_ci DebugCtrl0 = 0x1a, 24462306a36Sopenharmony_ci DebugCtrl1 = 0x1c, 24562306a36Sopenharmony_ci RxDMABurstThresh = 0x14, 24662306a36Sopenharmony_ci RxDMAUrgentThresh = 0x15, 24762306a36Sopenharmony_ci RxDMAPollPeriod = 0x16, 24862306a36Sopenharmony_ci LEDCtrl = 0x1a, 24962306a36Sopenharmony_ci ASICCtrl = 0x30, 25062306a36Sopenharmony_ci EEData = 0x34, 25162306a36Sopenharmony_ci EECtrl = 0x36, 25262306a36Sopenharmony_ci FlashAddr = 0x40, 25362306a36Sopenharmony_ci FlashData = 0x44, 25462306a36Sopenharmony_ci WakeEvent = 0x45, 25562306a36Sopenharmony_ci TxStatus = 0x46, 25662306a36Sopenharmony_ci TxFrameId = 0x47, 25762306a36Sopenharmony_ci DownCounter = 0x18, 25862306a36Sopenharmony_ci IntrClear = 0x4a, 25962306a36Sopenharmony_ci IntrEnable = 0x4c, 26062306a36Sopenharmony_ci IntrStatus = 0x4e, 26162306a36Sopenharmony_ci MACCtrl0 = 0x50, 26262306a36Sopenharmony_ci MACCtrl1 = 0x52, 26362306a36Sopenharmony_ci StationAddr = 0x54, 26462306a36Sopenharmony_ci MaxFrameSize = 0x5A, 26562306a36Sopenharmony_ci RxMode = 0x5c, 26662306a36Sopenharmony_ci MIICtrl = 0x5e, 26762306a36Sopenharmony_ci MulticastFilter0 = 0x60, 26862306a36Sopenharmony_ci MulticastFilter1 = 0x64, 26962306a36Sopenharmony_ci RxOctetsLow = 0x68, 27062306a36Sopenharmony_ci RxOctetsHigh = 0x6a, 27162306a36Sopenharmony_ci TxOctetsLow = 0x6c, 27262306a36Sopenharmony_ci TxOctetsHigh = 0x6e, 27362306a36Sopenharmony_ci TxFramesOK = 0x70, 27462306a36Sopenharmony_ci RxFramesOK = 0x72, 27562306a36Sopenharmony_ci StatsCarrierError = 0x74, 27662306a36Sopenharmony_ci StatsLateColl = 0x75, 27762306a36Sopenharmony_ci StatsMultiColl = 0x76, 27862306a36Sopenharmony_ci StatsOneColl = 0x77, 27962306a36Sopenharmony_ci StatsTxDefer = 0x78, 28062306a36Sopenharmony_ci RxMissed = 0x79, 28162306a36Sopenharmony_ci StatsTxXSDefer = 0x7a, 28262306a36Sopenharmony_ci StatsTxAbort = 0x7b, 28362306a36Sopenharmony_ci StatsBcastTx = 0x7c, 28462306a36Sopenharmony_ci StatsBcastRx = 0x7d, 28562306a36Sopenharmony_ci StatsMcastTx = 0x7e, 28662306a36Sopenharmony_ci StatsMcastRx = 0x7f, 28762306a36Sopenharmony_ci /* Aliased and bogus values! */ 28862306a36Sopenharmony_ci RxStatus = 0x0c, 28962306a36Sopenharmony_ci}; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci#define ASIC_HI_WORD(x) ((x) + 2) 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cienum ASICCtrl_HiWord_bit { 29462306a36Sopenharmony_ci GlobalReset = 0x0001, 29562306a36Sopenharmony_ci RxReset = 0x0002, 29662306a36Sopenharmony_ci TxReset = 0x0004, 29762306a36Sopenharmony_ci DMAReset = 0x0008, 29862306a36Sopenharmony_ci FIFOReset = 0x0010, 29962306a36Sopenharmony_ci NetworkReset = 0x0020, 30062306a36Sopenharmony_ci HostReset = 0x0040, 30162306a36Sopenharmony_ci ResetBusy = 0x0400, 30262306a36Sopenharmony_ci}; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci/* Bits in the interrupt status/mask registers. */ 30562306a36Sopenharmony_cienum intr_status_bits { 30662306a36Sopenharmony_ci IntrSummary=0x0001, IntrPCIErr=0x0002, IntrMACCtrl=0x0008, 30762306a36Sopenharmony_ci IntrTxDone=0x0004, IntrRxDone=0x0010, IntrRxStart=0x0020, 30862306a36Sopenharmony_ci IntrDrvRqst=0x0040, 30962306a36Sopenharmony_ci StatsMax=0x0080, LinkChange=0x0100, 31062306a36Sopenharmony_ci IntrTxDMADone=0x0200, IntrRxDMADone=0x0400, 31162306a36Sopenharmony_ci}; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci/* Bits in the RxMode register. */ 31462306a36Sopenharmony_cienum rx_mode_bits { 31562306a36Sopenharmony_ci AcceptAllIPMulti=0x20, AcceptMultiHash=0x10, AcceptAll=0x08, 31662306a36Sopenharmony_ci AcceptBroadcast=0x04, AcceptMulticast=0x02, AcceptMyPhys=0x01, 31762306a36Sopenharmony_ci}; 31862306a36Sopenharmony_ci/* Bits in MACCtrl. */ 31962306a36Sopenharmony_cienum mac_ctrl0_bits { 32062306a36Sopenharmony_ci EnbFullDuplex=0x20, EnbRcvLargeFrame=0x40, 32162306a36Sopenharmony_ci EnbFlowCtrl=0x100, EnbPassRxCRC=0x200, 32262306a36Sopenharmony_ci}; 32362306a36Sopenharmony_cienum mac_ctrl1_bits { 32462306a36Sopenharmony_ci StatsEnable=0x0020, StatsDisable=0x0040, StatsEnabled=0x0080, 32562306a36Sopenharmony_ci TxEnable=0x0100, TxDisable=0x0200, TxEnabled=0x0400, 32662306a36Sopenharmony_ci RxEnable=0x0800, RxDisable=0x1000, RxEnabled=0x2000, 32762306a36Sopenharmony_ci}; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci/* Bits in WakeEvent register. */ 33062306a36Sopenharmony_cienum wake_event_bits { 33162306a36Sopenharmony_ci WakePktEnable = 0x01, 33262306a36Sopenharmony_ci MagicPktEnable = 0x02, 33362306a36Sopenharmony_ci LinkEventEnable = 0x04, 33462306a36Sopenharmony_ci WolEnable = 0x80, 33562306a36Sopenharmony_ci}; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci/* The Rx and Tx buffer descriptors. */ 33862306a36Sopenharmony_ci/* Note that using only 32 bit fields simplifies conversion to big-endian 33962306a36Sopenharmony_ci architectures. */ 34062306a36Sopenharmony_cistruct netdev_desc { 34162306a36Sopenharmony_ci __le32 next_desc; 34262306a36Sopenharmony_ci __le32 status; 34362306a36Sopenharmony_ci struct desc_frag { __le32 addr, length; } frag; 34462306a36Sopenharmony_ci}; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci/* Bits in netdev_desc.status */ 34762306a36Sopenharmony_cienum desc_status_bits { 34862306a36Sopenharmony_ci DescOwn=0x8000, 34962306a36Sopenharmony_ci DescEndPacket=0x4000, 35062306a36Sopenharmony_ci DescEndRing=0x2000, 35162306a36Sopenharmony_ci LastFrag=0x80000000, 35262306a36Sopenharmony_ci DescIntrOnTx=0x8000, 35362306a36Sopenharmony_ci DescIntrOnDMADone=0x80000000, 35462306a36Sopenharmony_ci DisableAlign = 0x00000001, 35562306a36Sopenharmony_ci}; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci#define PRIV_ALIGN 15 /* Required alignment mask */ 35862306a36Sopenharmony_ci/* Use __attribute__((aligned (L1_CACHE_BYTES))) to maintain alignment 35962306a36Sopenharmony_ci within the structure. */ 36062306a36Sopenharmony_ci#define MII_CNT 4 36162306a36Sopenharmony_cistruct netdev_private { 36262306a36Sopenharmony_ci /* Descriptor rings first for alignment. */ 36362306a36Sopenharmony_ci struct netdev_desc *rx_ring; 36462306a36Sopenharmony_ci struct netdev_desc *tx_ring; 36562306a36Sopenharmony_ci struct sk_buff* rx_skbuff[RX_RING_SIZE]; 36662306a36Sopenharmony_ci struct sk_buff* tx_skbuff[TX_RING_SIZE]; 36762306a36Sopenharmony_ci dma_addr_t tx_ring_dma; 36862306a36Sopenharmony_ci dma_addr_t rx_ring_dma; 36962306a36Sopenharmony_ci struct timer_list timer; /* Media monitoring timer. */ 37062306a36Sopenharmony_ci struct net_device *ndev; /* backpointer */ 37162306a36Sopenharmony_ci /* ethtool extra stats */ 37262306a36Sopenharmony_ci struct { 37362306a36Sopenharmony_ci u64 tx_multiple_collisions; 37462306a36Sopenharmony_ci u64 tx_single_collisions; 37562306a36Sopenharmony_ci u64 tx_late_collisions; 37662306a36Sopenharmony_ci u64 tx_deferred; 37762306a36Sopenharmony_ci u64 tx_deferred_excessive; 37862306a36Sopenharmony_ci u64 tx_aborted; 37962306a36Sopenharmony_ci u64 tx_bcasts; 38062306a36Sopenharmony_ci u64 rx_bcasts; 38162306a36Sopenharmony_ci u64 tx_mcasts; 38262306a36Sopenharmony_ci u64 rx_mcasts; 38362306a36Sopenharmony_ci } xstats; 38462306a36Sopenharmony_ci /* Frequently used values: keep some adjacent for cache effect. */ 38562306a36Sopenharmony_ci spinlock_t lock; 38662306a36Sopenharmony_ci int msg_enable; 38762306a36Sopenharmony_ci int chip_id; 38862306a36Sopenharmony_ci unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */ 38962306a36Sopenharmony_ci unsigned int rx_buf_sz; /* Based on MTU+slack. */ 39062306a36Sopenharmony_ci struct netdev_desc *last_tx; /* Last Tx descriptor used. */ 39162306a36Sopenharmony_ci unsigned int cur_tx, dirty_tx; 39262306a36Sopenharmony_ci /* These values are keep track of the transceiver/media in use. */ 39362306a36Sopenharmony_ci unsigned int flowctrl:1; 39462306a36Sopenharmony_ci unsigned int default_port:4; /* Last dev->if_port value. */ 39562306a36Sopenharmony_ci unsigned int an_enable:1; 39662306a36Sopenharmony_ci unsigned int speed; 39762306a36Sopenharmony_ci unsigned int wol_enabled:1; /* Wake on LAN enabled */ 39862306a36Sopenharmony_ci struct tasklet_struct rx_tasklet; 39962306a36Sopenharmony_ci struct tasklet_struct tx_tasklet; 40062306a36Sopenharmony_ci int budget; 40162306a36Sopenharmony_ci int cur_task; 40262306a36Sopenharmony_ci /* Multicast and receive mode. */ 40362306a36Sopenharmony_ci spinlock_t mcastlock; /* SMP lock multicast updates. */ 40462306a36Sopenharmony_ci u16 mcast_filter[4]; 40562306a36Sopenharmony_ci /* MII transceiver section. */ 40662306a36Sopenharmony_ci struct mii_if_info mii_if; 40762306a36Sopenharmony_ci int mii_preamble_required; 40862306a36Sopenharmony_ci unsigned char phys[MII_CNT]; /* MII device addresses, only first one used. */ 40962306a36Sopenharmony_ci struct pci_dev *pci_dev; 41062306a36Sopenharmony_ci void __iomem *base; 41162306a36Sopenharmony_ci spinlock_t statlock; 41262306a36Sopenharmony_ci}; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci/* The station address location in the EEPROM. */ 41562306a36Sopenharmony_ci#define EEPROM_SA_OFFSET 0x10 41662306a36Sopenharmony_ci#define DEFAULT_INTR (IntrRxDMADone | IntrPCIErr | \ 41762306a36Sopenharmony_ci IntrDrvRqst | IntrTxDone | StatsMax | \ 41862306a36Sopenharmony_ci LinkChange) 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic int change_mtu(struct net_device *dev, int new_mtu); 42162306a36Sopenharmony_cistatic int eeprom_read(void __iomem *ioaddr, int location); 42262306a36Sopenharmony_cistatic int mdio_read(struct net_device *dev, int phy_id, int location); 42362306a36Sopenharmony_cistatic void mdio_write(struct net_device *dev, int phy_id, int location, int value); 42462306a36Sopenharmony_cistatic int mdio_wait_link(struct net_device *dev, int wait); 42562306a36Sopenharmony_cistatic int netdev_open(struct net_device *dev); 42662306a36Sopenharmony_cistatic void check_duplex(struct net_device *dev); 42762306a36Sopenharmony_cistatic void netdev_timer(struct timer_list *t); 42862306a36Sopenharmony_cistatic void tx_timeout(struct net_device *dev, unsigned int txqueue); 42962306a36Sopenharmony_cistatic void init_ring(struct net_device *dev); 43062306a36Sopenharmony_cistatic netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev); 43162306a36Sopenharmony_cistatic int reset_tx (struct net_device *dev); 43262306a36Sopenharmony_cistatic irqreturn_t intr_handler(int irq, void *dev_instance); 43362306a36Sopenharmony_cistatic void rx_poll(struct tasklet_struct *t); 43462306a36Sopenharmony_cistatic void tx_poll(struct tasklet_struct *t); 43562306a36Sopenharmony_cistatic void refill_rx (struct net_device *dev); 43662306a36Sopenharmony_cistatic void netdev_error(struct net_device *dev, int intr_status); 43762306a36Sopenharmony_cistatic void netdev_error(struct net_device *dev, int intr_status); 43862306a36Sopenharmony_cistatic void set_rx_mode(struct net_device *dev); 43962306a36Sopenharmony_cistatic int __set_mac_addr(struct net_device *dev); 44062306a36Sopenharmony_cistatic int sundance_set_mac_addr(struct net_device *dev, void *data); 44162306a36Sopenharmony_cistatic struct net_device_stats *get_stats(struct net_device *dev); 44262306a36Sopenharmony_cistatic int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); 44362306a36Sopenharmony_cistatic int netdev_close(struct net_device *dev); 44462306a36Sopenharmony_cistatic const struct ethtool_ops ethtool_ops; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic void sundance_reset(struct net_device *dev, unsigned long reset_cmd) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 44962306a36Sopenharmony_ci void __iomem *ioaddr = np->base + ASICCtrl; 45062306a36Sopenharmony_ci int countdown; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* ST201 documentation states ASICCtrl is a 32bit register */ 45362306a36Sopenharmony_ci iowrite32 (reset_cmd | ioread32 (ioaddr), ioaddr); 45462306a36Sopenharmony_ci /* ST201 documentation states reset can take up to 1 ms */ 45562306a36Sopenharmony_ci countdown = 10 + 1; 45662306a36Sopenharmony_ci while (ioread32 (ioaddr) & (ResetBusy << 16)) { 45762306a36Sopenharmony_ci if (--countdown == 0) { 45862306a36Sopenharmony_ci printk(KERN_WARNING "%s : reset not completed !!\n", dev->name); 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci udelay(100); 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 46662306a36Sopenharmony_cistatic void sundance_poll_controller(struct net_device *dev) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci disable_irq(np->pci_dev->irq); 47162306a36Sopenharmony_ci intr_handler(np->pci_dev->irq, dev); 47262306a36Sopenharmony_ci enable_irq(np->pci_dev->irq); 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci#endif 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic const struct net_device_ops netdev_ops = { 47762306a36Sopenharmony_ci .ndo_open = netdev_open, 47862306a36Sopenharmony_ci .ndo_stop = netdev_close, 47962306a36Sopenharmony_ci .ndo_start_xmit = start_tx, 48062306a36Sopenharmony_ci .ndo_get_stats = get_stats, 48162306a36Sopenharmony_ci .ndo_set_rx_mode = set_rx_mode, 48262306a36Sopenharmony_ci .ndo_eth_ioctl = netdev_ioctl, 48362306a36Sopenharmony_ci .ndo_tx_timeout = tx_timeout, 48462306a36Sopenharmony_ci .ndo_change_mtu = change_mtu, 48562306a36Sopenharmony_ci .ndo_set_mac_address = sundance_set_mac_addr, 48662306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 48762306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 48862306a36Sopenharmony_ci .ndo_poll_controller = sundance_poll_controller, 48962306a36Sopenharmony_ci#endif 49062306a36Sopenharmony_ci}; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic int sundance_probe1(struct pci_dev *pdev, 49362306a36Sopenharmony_ci const struct pci_device_id *ent) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci struct net_device *dev; 49662306a36Sopenharmony_ci struct netdev_private *np; 49762306a36Sopenharmony_ci static int card_idx; 49862306a36Sopenharmony_ci int chip_idx = ent->driver_data; 49962306a36Sopenharmony_ci int irq; 50062306a36Sopenharmony_ci int i; 50162306a36Sopenharmony_ci void __iomem *ioaddr; 50262306a36Sopenharmony_ci u16 mii_ctl; 50362306a36Sopenharmony_ci void *ring_space; 50462306a36Sopenharmony_ci dma_addr_t ring_dma; 50562306a36Sopenharmony_ci#ifdef USE_IO_OPS 50662306a36Sopenharmony_ci int bar = 0; 50762306a36Sopenharmony_ci#else 50862306a36Sopenharmony_ci int bar = 1; 50962306a36Sopenharmony_ci#endif 51062306a36Sopenharmony_ci int phy, phy_end, phy_idx = 0; 51162306a36Sopenharmony_ci __le16 addr[ETH_ALEN / 2]; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (pci_enable_device(pdev)) 51462306a36Sopenharmony_ci return -EIO; 51562306a36Sopenharmony_ci pci_set_master(pdev); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci irq = pdev->irq; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci dev = alloc_etherdev(sizeof(*np)); 52062306a36Sopenharmony_ci if (!dev) 52162306a36Sopenharmony_ci return -ENOMEM; 52262306a36Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (pci_request_regions(pdev, DRV_NAME)) 52562306a36Sopenharmony_ci goto err_out_netdev; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci ioaddr = pci_iomap(pdev, bar, netdev_io_size); 52862306a36Sopenharmony_ci if (!ioaddr) 52962306a36Sopenharmony_ci goto err_out_res; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci for (i = 0; i < 3; i++) 53262306a36Sopenharmony_ci addr[i] = 53362306a36Sopenharmony_ci cpu_to_le16(eeprom_read(ioaddr, i + EEPROM_SA_OFFSET)); 53462306a36Sopenharmony_ci eth_hw_addr_set(dev, (u8 *)addr); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci np = netdev_priv(dev); 53762306a36Sopenharmony_ci np->ndev = dev; 53862306a36Sopenharmony_ci np->base = ioaddr; 53962306a36Sopenharmony_ci np->pci_dev = pdev; 54062306a36Sopenharmony_ci np->chip_id = chip_idx; 54162306a36Sopenharmony_ci np->msg_enable = (1 << debug) - 1; 54262306a36Sopenharmony_ci spin_lock_init(&np->lock); 54362306a36Sopenharmony_ci spin_lock_init(&np->statlock); 54462306a36Sopenharmony_ci tasklet_setup(&np->rx_tasklet, rx_poll); 54562306a36Sopenharmony_ci tasklet_setup(&np->tx_tasklet, tx_poll); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci ring_space = dma_alloc_coherent(&pdev->dev, TX_TOTAL_SIZE, 54862306a36Sopenharmony_ci &ring_dma, GFP_KERNEL); 54962306a36Sopenharmony_ci if (!ring_space) 55062306a36Sopenharmony_ci goto err_out_cleardev; 55162306a36Sopenharmony_ci np->tx_ring = (struct netdev_desc *)ring_space; 55262306a36Sopenharmony_ci np->tx_ring_dma = ring_dma; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci ring_space = dma_alloc_coherent(&pdev->dev, RX_TOTAL_SIZE, 55562306a36Sopenharmony_ci &ring_dma, GFP_KERNEL); 55662306a36Sopenharmony_ci if (!ring_space) 55762306a36Sopenharmony_ci goto err_out_unmap_tx; 55862306a36Sopenharmony_ci np->rx_ring = (struct netdev_desc *)ring_space; 55962306a36Sopenharmony_ci np->rx_ring_dma = ring_dma; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci np->mii_if.dev = dev; 56262306a36Sopenharmony_ci np->mii_if.mdio_read = mdio_read; 56362306a36Sopenharmony_ci np->mii_if.mdio_write = mdio_write; 56462306a36Sopenharmony_ci np->mii_if.phy_id_mask = 0x1f; 56562306a36Sopenharmony_ci np->mii_if.reg_num_mask = 0x1f; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* The chip-specific entries in the device structure. */ 56862306a36Sopenharmony_ci dev->netdev_ops = &netdev_ops; 56962306a36Sopenharmony_ci dev->ethtool_ops = ðtool_ops; 57062306a36Sopenharmony_ci dev->watchdog_timeo = TX_TIMEOUT; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* MTU range: 68 - 8191 */ 57362306a36Sopenharmony_ci dev->min_mtu = ETH_MIN_MTU; 57462306a36Sopenharmony_ci dev->max_mtu = 8191; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci pci_set_drvdata(pdev, dev); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci i = register_netdev(dev); 57962306a36Sopenharmony_ci if (i) 58062306a36Sopenharmony_ci goto err_out_unmap_rx; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci printk(KERN_INFO "%s: %s at %p, %pM, IRQ %d.\n", 58362306a36Sopenharmony_ci dev->name, pci_id_tbl[chip_idx].name, ioaddr, 58462306a36Sopenharmony_ci dev->dev_addr, irq); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci np->phys[0] = 1; /* Default setting */ 58762306a36Sopenharmony_ci np->mii_preamble_required++; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci /* 59062306a36Sopenharmony_ci * It seems some phys doesn't deal well with address 0 being accessed 59162306a36Sopenharmony_ci * first 59262306a36Sopenharmony_ci */ 59362306a36Sopenharmony_ci if (sundance_pci_tbl[np->chip_id].device == 0x0200) { 59462306a36Sopenharmony_ci phy = 0; 59562306a36Sopenharmony_ci phy_end = 31; 59662306a36Sopenharmony_ci } else { 59762306a36Sopenharmony_ci phy = 1; 59862306a36Sopenharmony_ci phy_end = 32; /* wraps to zero, due to 'phy & 0x1f' */ 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci for (; phy <= phy_end && phy_idx < MII_CNT; phy++) { 60162306a36Sopenharmony_ci int phyx = phy & 0x1f; 60262306a36Sopenharmony_ci int mii_status = mdio_read(dev, phyx, MII_BMSR); 60362306a36Sopenharmony_ci if (mii_status != 0xffff && mii_status != 0x0000) { 60462306a36Sopenharmony_ci np->phys[phy_idx++] = phyx; 60562306a36Sopenharmony_ci np->mii_if.advertising = mdio_read(dev, phyx, MII_ADVERTISE); 60662306a36Sopenharmony_ci if ((mii_status & 0x0040) == 0) 60762306a36Sopenharmony_ci np->mii_preamble_required++; 60862306a36Sopenharmony_ci printk(KERN_INFO "%s: MII PHY found at address %d, status " 60962306a36Sopenharmony_ci "0x%4.4x advertising %4.4x.\n", 61062306a36Sopenharmony_ci dev->name, phyx, mii_status, np->mii_if.advertising); 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci np->mii_preamble_required--; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (phy_idx == 0) { 61662306a36Sopenharmony_ci printk(KERN_INFO "%s: No MII transceiver found, aborting. ASIC status %x\n", 61762306a36Sopenharmony_ci dev->name, ioread32(ioaddr + ASICCtrl)); 61862306a36Sopenharmony_ci goto err_out_unregister; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci np->mii_if.phy_id = np->phys[0]; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci /* Parse override configuration */ 62462306a36Sopenharmony_ci np->an_enable = 1; 62562306a36Sopenharmony_ci if (card_idx < MAX_UNITS) { 62662306a36Sopenharmony_ci if (media[card_idx] != NULL) { 62762306a36Sopenharmony_ci np->an_enable = 0; 62862306a36Sopenharmony_ci if (strcmp (media[card_idx], "100mbps_fd") == 0 || 62962306a36Sopenharmony_ci strcmp (media[card_idx], "4") == 0) { 63062306a36Sopenharmony_ci np->speed = 100; 63162306a36Sopenharmony_ci np->mii_if.full_duplex = 1; 63262306a36Sopenharmony_ci } else if (strcmp (media[card_idx], "100mbps_hd") == 0 || 63362306a36Sopenharmony_ci strcmp (media[card_idx], "3") == 0) { 63462306a36Sopenharmony_ci np->speed = 100; 63562306a36Sopenharmony_ci np->mii_if.full_duplex = 0; 63662306a36Sopenharmony_ci } else if (strcmp (media[card_idx], "10mbps_fd") == 0 || 63762306a36Sopenharmony_ci strcmp (media[card_idx], "2") == 0) { 63862306a36Sopenharmony_ci np->speed = 10; 63962306a36Sopenharmony_ci np->mii_if.full_duplex = 1; 64062306a36Sopenharmony_ci } else if (strcmp (media[card_idx], "10mbps_hd") == 0 || 64162306a36Sopenharmony_ci strcmp (media[card_idx], "1") == 0) { 64262306a36Sopenharmony_ci np->speed = 10; 64362306a36Sopenharmony_ci np->mii_if.full_duplex = 0; 64462306a36Sopenharmony_ci } else { 64562306a36Sopenharmony_ci np->an_enable = 1; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci if (flowctrl == 1) 64962306a36Sopenharmony_ci np->flowctrl = 1; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci /* Fibre PHY? */ 65362306a36Sopenharmony_ci if (ioread32 (ioaddr + ASICCtrl) & 0x80) { 65462306a36Sopenharmony_ci /* Default 100Mbps Full */ 65562306a36Sopenharmony_ci if (np->an_enable) { 65662306a36Sopenharmony_ci np->speed = 100; 65762306a36Sopenharmony_ci np->mii_if.full_duplex = 1; 65862306a36Sopenharmony_ci np->an_enable = 0; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci /* Reset PHY */ 66262306a36Sopenharmony_ci mdio_write (dev, np->phys[0], MII_BMCR, BMCR_RESET); 66362306a36Sopenharmony_ci mdelay (300); 66462306a36Sopenharmony_ci /* If flow control enabled, we need to advertise it.*/ 66562306a36Sopenharmony_ci if (np->flowctrl) 66662306a36Sopenharmony_ci mdio_write (dev, np->phys[0], MII_ADVERTISE, np->mii_if.advertising | 0x0400); 66762306a36Sopenharmony_ci mdio_write (dev, np->phys[0], MII_BMCR, BMCR_ANENABLE|BMCR_ANRESTART); 66862306a36Sopenharmony_ci /* Force media type */ 66962306a36Sopenharmony_ci if (!np->an_enable) { 67062306a36Sopenharmony_ci mii_ctl = 0; 67162306a36Sopenharmony_ci mii_ctl |= (np->speed == 100) ? BMCR_SPEED100 : 0; 67262306a36Sopenharmony_ci mii_ctl |= (np->mii_if.full_duplex) ? BMCR_FULLDPLX : 0; 67362306a36Sopenharmony_ci mdio_write (dev, np->phys[0], MII_BMCR, mii_ctl); 67462306a36Sopenharmony_ci printk (KERN_INFO "Override speed=%d, %s duplex\n", 67562306a36Sopenharmony_ci np->speed, np->mii_if.full_duplex ? "Full" : "Half"); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci /* Perhaps move the reset here? */ 68062306a36Sopenharmony_ci /* Reset the chip to erase previous misconfiguration. */ 68162306a36Sopenharmony_ci if (netif_msg_hw(np)) 68262306a36Sopenharmony_ci printk("ASIC Control is %x.\n", ioread32(ioaddr + ASICCtrl)); 68362306a36Sopenharmony_ci sundance_reset(dev, 0x00ff << 16); 68462306a36Sopenharmony_ci if (netif_msg_hw(np)) 68562306a36Sopenharmony_ci printk("ASIC Control is now %x.\n", ioread32(ioaddr + ASICCtrl)); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci card_idx++; 68862306a36Sopenharmony_ci return 0; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cierr_out_unregister: 69162306a36Sopenharmony_ci unregister_netdev(dev); 69262306a36Sopenharmony_cierr_out_unmap_rx: 69362306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, 69462306a36Sopenharmony_ci np->rx_ring, np->rx_ring_dma); 69562306a36Sopenharmony_cierr_out_unmap_tx: 69662306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, 69762306a36Sopenharmony_ci np->tx_ring, np->tx_ring_dma); 69862306a36Sopenharmony_cierr_out_cleardev: 69962306a36Sopenharmony_ci pci_iounmap(pdev, ioaddr); 70062306a36Sopenharmony_cierr_out_res: 70162306a36Sopenharmony_ci pci_release_regions(pdev); 70262306a36Sopenharmony_cierr_out_netdev: 70362306a36Sopenharmony_ci free_netdev (dev); 70462306a36Sopenharmony_ci return -ENODEV; 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistatic int change_mtu(struct net_device *dev, int new_mtu) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci if (netif_running(dev)) 71062306a36Sopenharmony_ci return -EBUSY; 71162306a36Sopenharmony_ci dev->mtu = new_mtu; 71262306a36Sopenharmony_ci return 0; 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci#define eeprom_delay(ee_addr) ioread32(ee_addr) 71662306a36Sopenharmony_ci/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. */ 71762306a36Sopenharmony_cistatic int eeprom_read(void __iomem *ioaddr, int location) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci int boguscnt = 10000; /* Typical 1900 ticks. */ 72062306a36Sopenharmony_ci iowrite16(0x0200 | (location & 0xff), ioaddr + EECtrl); 72162306a36Sopenharmony_ci do { 72262306a36Sopenharmony_ci eeprom_delay(ioaddr + EECtrl); 72362306a36Sopenharmony_ci if (! (ioread16(ioaddr + EECtrl) & 0x8000)) { 72462306a36Sopenharmony_ci return ioread16(ioaddr + EEData); 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci } while (--boguscnt > 0); 72762306a36Sopenharmony_ci return 0; 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci/* MII transceiver control section. 73162306a36Sopenharmony_ci Read and write the MII registers using software-generated serial 73262306a36Sopenharmony_ci MDIO protocol. See the MII specifications or DP83840A data sheet 73362306a36Sopenharmony_ci for details. 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci The maximum data clock rate is 2.5 Mhz. The minimum timing is usually 73662306a36Sopenharmony_ci met by back-to-back 33Mhz PCI cycles. */ 73762306a36Sopenharmony_ci#define mdio_delay() ioread8(mdio_addr) 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cienum mii_reg_bits { 74062306a36Sopenharmony_ci MDIO_ShiftClk=0x0001, MDIO_Data=0x0002, MDIO_EnbOutput=0x0004, 74162306a36Sopenharmony_ci}; 74262306a36Sopenharmony_ci#define MDIO_EnbIn (0) 74362306a36Sopenharmony_ci#define MDIO_WRITE0 (MDIO_EnbOutput) 74462306a36Sopenharmony_ci#define MDIO_WRITE1 (MDIO_Data | MDIO_EnbOutput) 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci/* Generate the preamble required for initial synchronization and 74762306a36Sopenharmony_ci a few older transceivers. */ 74862306a36Sopenharmony_cistatic void mdio_sync(void __iomem *mdio_addr) 74962306a36Sopenharmony_ci{ 75062306a36Sopenharmony_ci int bits = 32; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci /* Establish sync by sending at least 32 logic ones. */ 75362306a36Sopenharmony_ci while (--bits >= 0) { 75462306a36Sopenharmony_ci iowrite8(MDIO_WRITE1, mdio_addr); 75562306a36Sopenharmony_ci mdio_delay(); 75662306a36Sopenharmony_ci iowrite8(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr); 75762306a36Sopenharmony_ci mdio_delay(); 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cistatic int mdio_read(struct net_device *dev, int phy_id, int location) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 76462306a36Sopenharmony_ci void __iomem *mdio_addr = np->base + MIICtrl; 76562306a36Sopenharmony_ci int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; 76662306a36Sopenharmony_ci int i, retval = 0; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci if (np->mii_preamble_required) 76962306a36Sopenharmony_ci mdio_sync(mdio_addr); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci /* Shift the read command bits out. */ 77262306a36Sopenharmony_ci for (i = 15; i >= 0; i--) { 77362306a36Sopenharmony_ci int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci iowrite8(dataval, mdio_addr); 77662306a36Sopenharmony_ci mdio_delay(); 77762306a36Sopenharmony_ci iowrite8(dataval | MDIO_ShiftClk, mdio_addr); 77862306a36Sopenharmony_ci mdio_delay(); 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci /* Read the two transition, 16 data, and wire-idle bits. */ 78162306a36Sopenharmony_ci for (i = 19; i > 0; i--) { 78262306a36Sopenharmony_ci iowrite8(MDIO_EnbIn, mdio_addr); 78362306a36Sopenharmony_ci mdio_delay(); 78462306a36Sopenharmony_ci retval = (retval << 1) | ((ioread8(mdio_addr) & MDIO_Data) ? 1 : 0); 78562306a36Sopenharmony_ci iowrite8(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); 78662306a36Sopenharmony_ci mdio_delay(); 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci return (retval>>1) & 0xffff; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistatic void mdio_write(struct net_device *dev, int phy_id, int location, int value) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 79462306a36Sopenharmony_ci void __iomem *mdio_addr = np->base + MIICtrl; 79562306a36Sopenharmony_ci int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; 79662306a36Sopenharmony_ci int i; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci if (np->mii_preamble_required) 79962306a36Sopenharmony_ci mdio_sync(mdio_addr); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* Shift the command bits out. */ 80262306a36Sopenharmony_ci for (i = 31; i >= 0; i--) { 80362306a36Sopenharmony_ci int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci iowrite8(dataval, mdio_addr); 80662306a36Sopenharmony_ci mdio_delay(); 80762306a36Sopenharmony_ci iowrite8(dataval | MDIO_ShiftClk, mdio_addr); 80862306a36Sopenharmony_ci mdio_delay(); 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci /* Clear out extra bits. */ 81162306a36Sopenharmony_ci for (i = 2; i > 0; i--) { 81262306a36Sopenharmony_ci iowrite8(MDIO_EnbIn, mdio_addr); 81362306a36Sopenharmony_ci mdio_delay(); 81462306a36Sopenharmony_ci iowrite8(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); 81562306a36Sopenharmony_ci mdio_delay(); 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_cistatic int mdio_wait_link(struct net_device *dev, int wait) 82062306a36Sopenharmony_ci{ 82162306a36Sopenharmony_ci int bmsr; 82262306a36Sopenharmony_ci int phy_id; 82362306a36Sopenharmony_ci struct netdev_private *np; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci np = netdev_priv(dev); 82662306a36Sopenharmony_ci phy_id = np->phys[0]; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci do { 82962306a36Sopenharmony_ci bmsr = mdio_read(dev, phy_id, MII_BMSR); 83062306a36Sopenharmony_ci if (bmsr & 0x0004) 83162306a36Sopenharmony_ci return 0; 83262306a36Sopenharmony_ci mdelay(1); 83362306a36Sopenharmony_ci } while (--wait > 0); 83462306a36Sopenharmony_ci return -1; 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_cistatic int netdev_open(struct net_device *dev) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 84062306a36Sopenharmony_ci void __iomem *ioaddr = np->base; 84162306a36Sopenharmony_ci const int irq = np->pci_dev->irq; 84262306a36Sopenharmony_ci unsigned long flags; 84362306a36Sopenharmony_ci int i; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci sundance_reset(dev, 0x00ff << 16); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci i = request_irq(irq, intr_handler, IRQF_SHARED, dev->name, dev); 84862306a36Sopenharmony_ci if (i) 84962306a36Sopenharmony_ci return i; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci if (netif_msg_ifup(np)) 85262306a36Sopenharmony_ci printk(KERN_DEBUG "%s: netdev_open() irq %d\n", dev->name, irq); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci init_ring(dev); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci iowrite32(np->rx_ring_dma, ioaddr + RxListPtr); 85762306a36Sopenharmony_ci /* The Tx list pointer is written as packets are queued. */ 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci /* Initialize other registers. */ 86062306a36Sopenharmony_ci __set_mac_addr(dev); 86162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_VLAN_8021Q) 86262306a36Sopenharmony_ci iowrite16(dev->mtu + 18, ioaddr + MaxFrameSize); 86362306a36Sopenharmony_ci#else 86462306a36Sopenharmony_ci iowrite16(dev->mtu + 14, ioaddr + MaxFrameSize); 86562306a36Sopenharmony_ci#endif 86662306a36Sopenharmony_ci if (dev->mtu > 2047) 86762306a36Sopenharmony_ci iowrite32(ioread32(ioaddr + ASICCtrl) | 0x0C, ioaddr + ASICCtrl); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci /* Configure the PCI bus bursts and FIFO thresholds. */ 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (dev->if_port == 0) 87262306a36Sopenharmony_ci dev->if_port = np->default_port; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci spin_lock_init(&np->mcastlock); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci set_rx_mode(dev); 87762306a36Sopenharmony_ci iowrite16(0, ioaddr + IntrEnable); 87862306a36Sopenharmony_ci iowrite16(0, ioaddr + DownCounter); 87962306a36Sopenharmony_ci /* Set the chip to poll every N*320nsec. */ 88062306a36Sopenharmony_ci iowrite8(100, ioaddr + RxDMAPollPeriod); 88162306a36Sopenharmony_ci iowrite8(127, ioaddr + TxDMAPollPeriod); 88262306a36Sopenharmony_ci /* Fix DFE-580TX packet drop issue */ 88362306a36Sopenharmony_ci if (np->pci_dev->revision >= 0x14) 88462306a36Sopenharmony_ci iowrite8(0x01, ioaddr + DebugCtrl1); 88562306a36Sopenharmony_ci netif_start_queue(dev); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 88862306a36Sopenharmony_ci reset_tx(dev); 88962306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci iowrite16 (StatsEnable | RxEnable | TxEnable, ioaddr + MACCtrl1); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci /* Disable Wol */ 89462306a36Sopenharmony_ci iowrite8(ioread8(ioaddr + WakeEvent) | 0x00, ioaddr + WakeEvent); 89562306a36Sopenharmony_ci np->wol_enabled = 0; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci if (netif_msg_ifup(np)) 89862306a36Sopenharmony_ci printk(KERN_DEBUG "%s: Done netdev_open(), status: Rx %x Tx %x " 89962306a36Sopenharmony_ci "MAC Control %x, %4.4x %4.4x.\n", 90062306a36Sopenharmony_ci dev->name, ioread32(ioaddr + RxStatus), ioread8(ioaddr + TxStatus), 90162306a36Sopenharmony_ci ioread32(ioaddr + MACCtrl0), 90262306a36Sopenharmony_ci ioread16(ioaddr + MACCtrl1), ioread16(ioaddr + MACCtrl0)); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci /* Set the timer to check for link beat. */ 90562306a36Sopenharmony_ci timer_setup(&np->timer, netdev_timer, 0); 90662306a36Sopenharmony_ci np->timer.expires = jiffies + 3*HZ; 90762306a36Sopenharmony_ci add_timer(&np->timer); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci /* Enable interrupts by setting the interrupt mask. */ 91062306a36Sopenharmony_ci iowrite16(DEFAULT_INTR, ioaddr + IntrEnable); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci return 0; 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_cistatic void check_duplex(struct net_device *dev) 91662306a36Sopenharmony_ci{ 91762306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 91862306a36Sopenharmony_ci void __iomem *ioaddr = np->base; 91962306a36Sopenharmony_ci int mii_lpa = mdio_read(dev, np->phys[0], MII_LPA); 92062306a36Sopenharmony_ci int negotiated = mii_lpa & np->mii_if.advertising; 92162306a36Sopenharmony_ci int duplex; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* Force media */ 92462306a36Sopenharmony_ci if (!np->an_enable || mii_lpa == 0xffff) { 92562306a36Sopenharmony_ci if (np->mii_if.full_duplex) 92662306a36Sopenharmony_ci iowrite16 (ioread16 (ioaddr + MACCtrl0) | EnbFullDuplex, 92762306a36Sopenharmony_ci ioaddr + MACCtrl0); 92862306a36Sopenharmony_ci return; 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci /* Autonegotiation */ 93262306a36Sopenharmony_ci duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040; 93362306a36Sopenharmony_ci if (np->mii_if.full_duplex != duplex) { 93462306a36Sopenharmony_ci np->mii_if.full_duplex = duplex; 93562306a36Sopenharmony_ci if (netif_msg_link(np)) 93662306a36Sopenharmony_ci printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d " 93762306a36Sopenharmony_ci "negotiated capability %4.4x.\n", dev->name, 93862306a36Sopenharmony_ci duplex ? "full" : "half", np->phys[0], negotiated); 93962306a36Sopenharmony_ci iowrite16(ioread16(ioaddr + MACCtrl0) | (duplex ? 0x20 : 0), ioaddr + MACCtrl0); 94062306a36Sopenharmony_ci } 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic void netdev_timer(struct timer_list *t) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci struct netdev_private *np = from_timer(np, t, timer); 94662306a36Sopenharmony_ci struct net_device *dev = np->mii_if.dev; 94762306a36Sopenharmony_ci void __iomem *ioaddr = np->base; 94862306a36Sopenharmony_ci int next_tick = 10*HZ; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci if (netif_msg_timer(np)) { 95162306a36Sopenharmony_ci printk(KERN_DEBUG "%s: Media selection timer tick, intr status %4.4x, " 95262306a36Sopenharmony_ci "Tx %x Rx %x.\n", 95362306a36Sopenharmony_ci dev->name, ioread16(ioaddr + IntrEnable), 95462306a36Sopenharmony_ci ioread8(ioaddr + TxStatus), ioread32(ioaddr + RxStatus)); 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci check_duplex(dev); 95762306a36Sopenharmony_ci np->timer.expires = jiffies + next_tick; 95862306a36Sopenharmony_ci add_timer(&np->timer); 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_cistatic void tx_timeout(struct net_device *dev, unsigned int txqueue) 96262306a36Sopenharmony_ci{ 96362306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 96462306a36Sopenharmony_ci void __iomem *ioaddr = np->base; 96562306a36Sopenharmony_ci unsigned long flag; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci netif_stop_queue(dev); 96862306a36Sopenharmony_ci tasklet_disable_in_atomic(&np->tx_tasklet); 96962306a36Sopenharmony_ci iowrite16(0, ioaddr + IntrEnable); 97062306a36Sopenharmony_ci printk(KERN_WARNING "%s: Transmit timed out, TxStatus %2.2x " 97162306a36Sopenharmony_ci "TxFrameId %2.2x," 97262306a36Sopenharmony_ci " resetting...\n", dev->name, ioread8(ioaddr + TxStatus), 97362306a36Sopenharmony_ci ioread8(ioaddr + TxFrameId)); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci { 97662306a36Sopenharmony_ci int i; 97762306a36Sopenharmony_ci for (i=0; i<TX_RING_SIZE; i++) { 97862306a36Sopenharmony_ci printk(KERN_DEBUG "%02x %08llx %08x %08x(%02x) %08x %08x\n", i, 97962306a36Sopenharmony_ci (unsigned long long)(np->tx_ring_dma + i*sizeof(*np->tx_ring)), 98062306a36Sopenharmony_ci le32_to_cpu(np->tx_ring[i].next_desc), 98162306a36Sopenharmony_ci le32_to_cpu(np->tx_ring[i].status), 98262306a36Sopenharmony_ci (le32_to_cpu(np->tx_ring[i].status) >> 2) & 0xff, 98362306a36Sopenharmony_ci le32_to_cpu(np->tx_ring[i].frag.addr), 98462306a36Sopenharmony_ci le32_to_cpu(np->tx_ring[i].frag.length)); 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci printk(KERN_DEBUG "TxListPtr=%08x netif_queue_stopped=%d\n", 98762306a36Sopenharmony_ci ioread32(np->base + TxListPtr), 98862306a36Sopenharmony_ci netif_queue_stopped(dev)); 98962306a36Sopenharmony_ci printk(KERN_DEBUG "cur_tx=%d(%02x) dirty_tx=%d(%02x)\n", 99062306a36Sopenharmony_ci np->cur_tx, np->cur_tx % TX_RING_SIZE, 99162306a36Sopenharmony_ci np->dirty_tx, np->dirty_tx % TX_RING_SIZE); 99262306a36Sopenharmony_ci printk(KERN_DEBUG "cur_rx=%d dirty_rx=%d\n", np->cur_rx, np->dirty_rx); 99362306a36Sopenharmony_ci printk(KERN_DEBUG "cur_task=%d\n", np->cur_task); 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci spin_lock_irqsave(&np->lock, flag); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci /* Stop and restart the chip's Tx processes . */ 99862306a36Sopenharmony_ci reset_tx(dev); 99962306a36Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flag); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci dev->if_port = 0; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci netif_trans_update(dev); /* prevent tx timeout */ 100462306a36Sopenharmony_ci dev->stats.tx_errors++; 100562306a36Sopenharmony_ci if (np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) { 100662306a36Sopenharmony_ci netif_wake_queue(dev); 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci iowrite16(DEFAULT_INTR, ioaddr + IntrEnable); 100962306a36Sopenharmony_ci tasklet_enable(&np->tx_tasklet); 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ 101462306a36Sopenharmony_cistatic void init_ring(struct net_device *dev) 101562306a36Sopenharmony_ci{ 101662306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 101762306a36Sopenharmony_ci int i; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci np->cur_rx = np->cur_tx = 0; 102062306a36Sopenharmony_ci np->dirty_rx = np->dirty_tx = 0; 102162306a36Sopenharmony_ci np->cur_task = 0; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci np->rx_buf_sz = (dev->mtu <= 1520 ? PKT_BUF_SZ : dev->mtu + 16); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci /* Initialize all Rx descriptors. */ 102662306a36Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 102762306a36Sopenharmony_ci np->rx_ring[i].next_desc = cpu_to_le32(np->rx_ring_dma + 102862306a36Sopenharmony_ci ((i+1)%RX_RING_SIZE)*sizeof(*np->rx_ring)); 102962306a36Sopenharmony_ci np->rx_ring[i].status = 0; 103062306a36Sopenharmony_ci np->rx_ring[i].frag.length = 0; 103162306a36Sopenharmony_ci np->rx_skbuff[i] = NULL; 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci /* Fill in the Rx buffers. Handle allocation failure gracefully. */ 103562306a36Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 103662306a36Sopenharmony_ci struct sk_buff *skb = 103762306a36Sopenharmony_ci netdev_alloc_skb(dev, np->rx_buf_sz + 2); 103862306a36Sopenharmony_ci np->rx_skbuff[i] = skb; 103962306a36Sopenharmony_ci if (skb == NULL) 104062306a36Sopenharmony_ci break; 104162306a36Sopenharmony_ci skb_reserve(skb, 2); /* 16 byte align the IP header. */ 104262306a36Sopenharmony_ci np->rx_ring[i].frag.addr = cpu_to_le32( 104362306a36Sopenharmony_ci dma_map_single(&np->pci_dev->dev, skb->data, 104462306a36Sopenharmony_ci np->rx_buf_sz, DMA_FROM_DEVICE)); 104562306a36Sopenharmony_ci if (dma_mapping_error(&np->pci_dev->dev, 104662306a36Sopenharmony_ci np->rx_ring[i].frag.addr)) { 104762306a36Sopenharmony_ci dev_kfree_skb(skb); 104862306a36Sopenharmony_ci np->rx_skbuff[i] = NULL; 104962306a36Sopenharmony_ci break; 105062306a36Sopenharmony_ci } 105162306a36Sopenharmony_ci np->rx_ring[i].frag.length = cpu_to_le32(np->rx_buf_sz | LastFrag); 105262306a36Sopenharmony_ci } 105362306a36Sopenharmony_ci np->dirty_rx = (unsigned int)(i - RX_RING_SIZE); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) { 105662306a36Sopenharmony_ci np->tx_skbuff[i] = NULL; 105762306a36Sopenharmony_ci np->tx_ring[i].status = 0; 105862306a36Sopenharmony_ci } 105962306a36Sopenharmony_ci} 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_cistatic void tx_poll(struct tasklet_struct *t) 106262306a36Sopenharmony_ci{ 106362306a36Sopenharmony_ci struct netdev_private *np = from_tasklet(np, t, tx_tasklet); 106462306a36Sopenharmony_ci unsigned head = np->cur_task % TX_RING_SIZE; 106562306a36Sopenharmony_ci struct netdev_desc *txdesc = 106662306a36Sopenharmony_ci &np->tx_ring[(np->cur_tx - 1) % TX_RING_SIZE]; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci /* Chain the next pointer */ 106962306a36Sopenharmony_ci for (; np->cur_tx - np->cur_task > 0; np->cur_task++) { 107062306a36Sopenharmony_ci int entry = np->cur_task % TX_RING_SIZE; 107162306a36Sopenharmony_ci txdesc = &np->tx_ring[entry]; 107262306a36Sopenharmony_ci if (np->last_tx) { 107362306a36Sopenharmony_ci np->last_tx->next_desc = cpu_to_le32(np->tx_ring_dma + 107462306a36Sopenharmony_ci entry*sizeof(struct netdev_desc)); 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci np->last_tx = txdesc; 107762306a36Sopenharmony_ci } 107862306a36Sopenharmony_ci /* Indicate the latest descriptor of tx ring */ 107962306a36Sopenharmony_ci txdesc->status |= cpu_to_le32(DescIntrOnTx); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci if (ioread32 (np->base + TxListPtr) == 0) 108262306a36Sopenharmony_ci iowrite32 (np->tx_ring_dma + head * sizeof(struct netdev_desc), 108362306a36Sopenharmony_ci np->base + TxListPtr); 108462306a36Sopenharmony_ci} 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_cistatic netdev_tx_t 108762306a36Sopenharmony_cistart_tx (struct sk_buff *skb, struct net_device *dev) 108862306a36Sopenharmony_ci{ 108962306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 109062306a36Sopenharmony_ci struct netdev_desc *txdesc; 109162306a36Sopenharmony_ci unsigned entry; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci /* Calculate the next Tx descriptor entry. */ 109462306a36Sopenharmony_ci entry = np->cur_tx % TX_RING_SIZE; 109562306a36Sopenharmony_ci np->tx_skbuff[entry] = skb; 109662306a36Sopenharmony_ci txdesc = &np->tx_ring[entry]; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci txdesc->next_desc = 0; 109962306a36Sopenharmony_ci txdesc->status = cpu_to_le32 ((entry << 2) | DisableAlign); 110062306a36Sopenharmony_ci txdesc->frag.addr = cpu_to_le32(dma_map_single(&np->pci_dev->dev, 110162306a36Sopenharmony_ci skb->data, skb->len, DMA_TO_DEVICE)); 110262306a36Sopenharmony_ci if (dma_mapping_error(&np->pci_dev->dev, 110362306a36Sopenharmony_ci txdesc->frag.addr)) 110462306a36Sopenharmony_ci goto drop_frame; 110562306a36Sopenharmony_ci txdesc->frag.length = cpu_to_le32 (skb->len | LastFrag); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci /* Increment cur_tx before tasklet_schedule() */ 110862306a36Sopenharmony_ci np->cur_tx++; 110962306a36Sopenharmony_ci mb(); 111062306a36Sopenharmony_ci /* Schedule a tx_poll() task */ 111162306a36Sopenharmony_ci tasklet_schedule(&np->tx_tasklet); 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci /* On some architectures: explicitly flush cache lines here. */ 111462306a36Sopenharmony_ci if (np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 1 && 111562306a36Sopenharmony_ci !netif_queue_stopped(dev)) { 111662306a36Sopenharmony_ci /* do nothing */ 111762306a36Sopenharmony_ci } else { 111862306a36Sopenharmony_ci netif_stop_queue (dev); 111962306a36Sopenharmony_ci } 112062306a36Sopenharmony_ci if (netif_msg_tx_queued(np)) { 112162306a36Sopenharmony_ci printk (KERN_DEBUG 112262306a36Sopenharmony_ci "%s: Transmit frame #%d queued in slot %d.\n", 112362306a36Sopenharmony_ci dev->name, np->cur_tx, entry); 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci return NETDEV_TX_OK; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_cidrop_frame: 112862306a36Sopenharmony_ci dev_kfree_skb_any(skb); 112962306a36Sopenharmony_ci np->tx_skbuff[entry] = NULL; 113062306a36Sopenharmony_ci dev->stats.tx_dropped++; 113162306a36Sopenharmony_ci return NETDEV_TX_OK; 113262306a36Sopenharmony_ci} 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci/* Reset hardware tx and free all of tx buffers */ 113562306a36Sopenharmony_cistatic int 113662306a36Sopenharmony_cireset_tx (struct net_device *dev) 113762306a36Sopenharmony_ci{ 113862306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 113962306a36Sopenharmony_ci void __iomem *ioaddr = np->base; 114062306a36Sopenharmony_ci struct sk_buff *skb; 114162306a36Sopenharmony_ci int i; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci /* Reset tx logic, TxListPtr will be cleaned */ 114462306a36Sopenharmony_ci iowrite16 (TxDisable, ioaddr + MACCtrl1); 114562306a36Sopenharmony_ci sundance_reset(dev, (NetworkReset|FIFOReset|DMAReset|TxReset) << 16); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci /* free all tx skbuff */ 114862306a36Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) { 114962306a36Sopenharmony_ci np->tx_ring[i].next_desc = 0; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci skb = np->tx_skbuff[i]; 115262306a36Sopenharmony_ci if (skb) { 115362306a36Sopenharmony_ci dma_unmap_single(&np->pci_dev->dev, 115462306a36Sopenharmony_ci le32_to_cpu(np->tx_ring[i].frag.addr), 115562306a36Sopenharmony_ci skb->len, DMA_TO_DEVICE); 115662306a36Sopenharmony_ci dev_kfree_skb_any(skb); 115762306a36Sopenharmony_ci np->tx_skbuff[i] = NULL; 115862306a36Sopenharmony_ci dev->stats.tx_dropped++; 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci } 116162306a36Sopenharmony_ci np->cur_tx = np->dirty_tx = 0; 116262306a36Sopenharmony_ci np->cur_task = 0; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci np->last_tx = NULL; 116562306a36Sopenharmony_ci iowrite8(127, ioaddr + TxDMAPollPeriod); 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci iowrite16 (StatsEnable | RxEnable | TxEnable, ioaddr + MACCtrl1); 116862306a36Sopenharmony_ci return 0; 116962306a36Sopenharmony_ci} 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci/* The interrupt handler cleans up after the Tx thread, 117262306a36Sopenharmony_ci and schedule a Rx thread work */ 117362306a36Sopenharmony_cistatic irqreturn_t intr_handler(int irq, void *dev_instance) 117462306a36Sopenharmony_ci{ 117562306a36Sopenharmony_ci struct net_device *dev = (struct net_device *)dev_instance; 117662306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 117762306a36Sopenharmony_ci void __iomem *ioaddr = np->base; 117862306a36Sopenharmony_ci int hw_frame_id; 117962306a36Sopenharmony_ci int tx_cnt; 118062306a36Sopenharmony_ci int tx_status; 118162306a36Sopenharmony_ci int handled = 0; 118262306a36Sopenharmony_ci int i; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci do { 118562306a36Sopenharmony_ci int intr_status = ioread16(ioaddr + IntrStatus); 118662306a36Sopenharmony_ci iowrite16(intr_status, ioaddr + IntrStatus); 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci if (netif_msg_intr(np)) 118962306a36Sopenharmony_ci printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n", 119062306a36Sopenharmony_ci dev->name, intr_status); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci if (!(intr_status & DEFAULT_INTR)) 119362306a36Sopenharmony_ci break; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci handled = 1; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci if (intr_status & (IntrRxDMADone)) { 119862306a36Sopenharmony_ci iowrite16(DEFAULT_INTR & ~(IntrRxDone|IntrRxDMADone), 119962306a36Sopenharmony_ci ioaddr + IntrEnable); 120062306a36Sopenharmony_ci if (np->budget < 0) 120162306a36Sopenharmony_ci np->budget = RX_BUDGET; 120262306a36Sopenharmony_ci tasklet_schedule(&np->rx_tasklet); 120362306a36Sopenharmony_ci } 120462306a36Sopenharmony_ci if (intr_status & (IntrTxDone | IntrDrvRqst)) { 120562306a36Sopenharmony_ci tx_status = ioread16 (ioaddr + TxStatus); 120662306a36Sopenharmony_ci for (tx_cnt=32; tx_status & 0x80; --tx_cnt) { 120762306a36Sopenharmony_ci if (netif_msg_tx_done(np)) 120862306a36Sopenharmony_ci printk 120962306a36Sopenharmony_ci ("%s: Transmit status is %2.2x.\n", 121062306a36Sopenharmony_ci dev->name, tx_status); 121162306a36Sopenharmony_ci if (tx_status & 0x1e) { 121262306a36Sopenharmony_ci if (netif_msg_tx_err(np)) 121362306a36Sopenharmony_ci printk("%s: Transmit error status %4.4x.\n", 121462306a36Sopenharmony_ci dev->name, tx_status); 121562306a36Sopenharmony_ci dev->stats.tx_errors++; 121662306a36Sopenharmony_ci if (tx_status & 0x10) 121762306a36Sopenharmony_ci dev->stats.tx_fifo_errors++; 121862306a36Sopenharmony_ci if (tx_status & 0x08) 121962306a36Sopenharmony_ci dev->stats.collisions++; 122062306a36Sopenharmony_ci if (tx_status & 0x04) 122162306a36Sopenharmony_ci dev->stats.tx_fifo_errors++; 122262306a36Sopenharmony_ci if (tx_status & 0x02) 122362306a36Sopenharmony_ci dev->stats.tx_window_errors++; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci /* 122662306a36Sopenharmony_ci ** This reset has been verified on 122762306a36Sopenharmony_ci ** DFE-580TX boards ! phdm@macqel.be. 122862306a36Sopenharmony_ci */ 122962306a36Sopenharmony_ci if (tx_status & 0x10) { /* TxUnderrun */ 123062306a36Sopenharmony_ci /* Restart Tx FIFO and transmitter */ 123162306a36Sopenharmony_ci sundance_reset(dev, (NetworkReset|FIFOReset|TxReset) << 16); 123262306a36Sopenharmony_ci /* No need to reset the Tx pointer here */ 123362306a36Sopenharmony_ci } 123462306a36Sopenharmony_ci /* Restart the Tx. Need to make sure tx enabled */ 123562306a36Sopenharmony_ci i = 10; 123662306a36Sopenharmony_ci do { 123762306a36Sopenharmony_ci iowrite16(ioread16(ioaddr + MACCtrl1) | TxEnable, ioaddr + MACCtrl1); 123862306a36Sopenharmony_ci if (ioread16(ioaddr + MACCtrl1) & TxEnabled) 123962306a36Sopenharmony_ci break; 124062306a36Sopenharmony_ci mdelay(1); 124162306a36Sopenharmony_ci } while (--i); 124262306a36Sopenharmony_ci } 124362306a36Sopenharmony_ci /* Yup, this is a documentation bug. It cost me *hours*. */ 124462306a36Sopenharmony_ci iowrite16 (0, ioaddr + TxStatus); 124562306a36Sopenharmony_ci if (tx_cnt < 0) { 124662306a36Sopenharmony_ci iowrite32(5000, ioaddr + DownCounter); 124762306a36Sopenharmony_ci break; 124862306a36Sopenharmony_ci } 124962306a36Sopenharmony_ci tx_status = ioread16 (ioaddr + TxStatus); 125062306a36Sopenharmony_ci } 125162306a36Sopenharmony_ci hw_frame_id = (tx_status >> 8) & 0xff; 125262306a36Sopenharmony_ci } else { 125362306a36Sopenharmony_ci hw_frame_id = ioread8(ioaddr + TxFrameId); 125462306a36Sopenharmony_ci } 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci if (np->pci_dev->revision >= 0x14) { 125762306a36Sopenharmony_ci spin_lock(&np->lock); 125862306a36Sopenharmony_ci for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) { 125962306a36Sopenharmony_ci int entry = np->dirty_tx % TX_RING_SIZE; 126062306a36Sopenharmony_ci struct sk_buff *skb; 126162306a36Sopenharmony_ci int sw_frame_id; 126262306a36Sopenharmony_ci sw_frame_id = (le32_to_cpu( 126362306a36Sopenharmony_ci np->tx_ring[entry].status) >> 2) & 0xff; 126462306a36Sopenharmony_ci if (sw_frame_id == hw_frame_id && 126562306a36Sopenharmony_ci !(le32_to_cpu(np->tx_ring[entry].status) 126662306a36Sopenharmony_ci & 0x00010000)) 126762306a36Sopenharmony_ci break; 126862306a36Sopenharmony_ci if (sw_frame_id == (hw_frame_id + 1) % 126962306a36Sopenharmony_ci TX_RING_SIZE) 127062306a36Sopenharmony_ci break; 127162306a36Sopenharmony_ci skb = np->tx_skbuff[entry]; 127262306a36Sopenharmony_ci /* Free the original skb. */ 127362306a36Sopenharmony_ci dma_unmap_single(&np->pci_dev->dev, 127462306a36Sopenharmony_ci le32_to_cpu(np->tx_ring[entry].frag.addr), 127562306a36Sopenharmony_ci skb->len, DMA_TO_DEVICE); 127662306a36Sopenharmony_ci dev_consume_skb_irq(np->tx_skbuff[entry]); 127762306a36Sopenharmony_ci np->tx_skbuff[entry] = NULL; 127862306a36Sopenharmony_ci np->tx_ring[entry].frag.addr = 0; 127962306a36Sopenharmony_ci np->tx_ring[entry].frag.length = 0; 128062306a36Sopenharmony_ci } 128162306a36Sopenharmony_ci spin_unlock(&np->lock); 128262306a36Sopenharmony_ci } else { 128362306a36Sopenharmony_ci spin_lock(&np->lock); 128462306a36Sopenharmony_ci for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) { 128562306a36Sopenharmony_ci int entry = np->dirty_tx % TX_RING_SIZE; 128662306a36Sopenharmony_ci struct sk_buff *skb; 128762306a36Sopenharmony_ci if (!(le32_to_cpu(np->tx_ring[entry].status) 128862306a36Sopenharmony_ci & 0x00010000)) 128962306a36Sopenharmony_ci break; 129062306a36Sopenharmony_ci skb = np->tx_skbuff[entry]; 129162306a36Sopenharmony_ci /* Free the original skb. */ 129262306a36Sopenharmony_ci dma_unmap_single(&np->pci_dev->dev, 129362306a36Sopenharmony_ci le32_to_cpu(np->tx_ring[entry].frag.addr), 129462306a36Sopenharmony_ci skb->len, DMA_TO_DEVICE); 129562306a36Sopenharmony_ci dev_consume_skb_irq(np->tx_skbuff[entry]); 129662306a36Sopenharmony_ci np->tx_skbuff[entry] = NULL; 129762306a36Sopenharmony_ci np->tx_ring[entry].frag.addr = 0; 129862306a36Sopenharmony_ci np->tx_ring[entry].frag.length = 0; 129962306a36Sopenharmony_ci } 130062306a36Sopenharmony_ci spin_unlock(&np->lock); 130162306a36Sopenharmony_ci } 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci if (netif_queue_stopped(dev) && 130462306a36Sopenharmony_ci np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) { 130562306a36Sopenharmony_ci /* The ring is no longer full, clear busy flag. */ 130662306a36Sopenharmony_ci netif_wake_queue (dev); 130762306a36Sopenharmony_ci } 130862306a36Sopenharmony_ci /* Abnormal error summary/uncommon events handlers. */ 130962306a36Sopenharmony_ci if (intr_status & (IntrPCIErr | LinkChange | StatsMax)) 131062306a36Sopenharmony_ci netdev_error(dev, intr_status); 131162306a36Sopenharmony_ci } while (0); 131262306a36Sopenharmony_ci if (netif_msg_intr(np)) 131362306a36Sopenharmony_ci printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", 131462306a36Sopenharmony_ci dev->name, ioread16(ioaddr + IntrStatus)); 131562306a36Sopenharmony_ci return IRQ_RETVAL(handled); 131662306a36Sopenharmony_ci} 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_cistatic void rx_poll(struct tasklet_struct *t) 131962306a36Sopenharmony_ci{ 132062306a36Sopenharmony_ci struct netdev_private *np = from_tasklet(np, t, rx_tasklet); 132162306a36Sopenharmony_ci struct net_device *dev = np->ndev; 132262306a36Sopenharmony_ci int entry = np->cur_rx % RX_RING_SIZE; 132362306a36Sopenharmony_ci int boguscnt = np->budget; 132462306a36Sopenharmony_ci void __iomem *ioaddr = np->base; 132562306a36Sopenharmony_ci int received = 0; 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci /* If EOP is set on the next entry, it's a new packet. Send it up. */ 132862306a36Sopenharmony_ci while (1) { 132962306a36Sopenharmony_ci struct netdev_desc *desc = &(np->rx_ring[entry]); 133062306a36Sopenharmony_ci u32 frame_status = le32_to_cpu(desc->status); 133162306a36Sopenharmony_ci int pkt_len; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci if (--boguscnt < 0) { 133462306a36Sopenharmony_ci goto not_done; 133562306a36Sopenharmony_ci } 133662306a36Sopenharmony_ci if (!(frame_status & DescOwn)) 133762306a36Sopenharmony_ci break; 133862306a36Sopenharmony_ci pkt_len = frame_status & 0x1fff; /* Chip omits the CRC. */ 133962306a36Sopenharmony_ci if (netif_msg_rx_status(np)) 134062306a36Sopenharmony_ci printk(KERN_DEBUG " netdev_rx() status was %8.8x.\n", 134162306a36Sopenharmony_ci frame_status); 134262306a36Sopenharmony_ci if (frame_status & 0x001f4000) { 134362306a36Sopenharmony_ci /* There was a error. */ 134462306a36Sopenharmony_ci if (netif_msg_rx_err(np)) 134562306a36Sopenharmony_ci printk(KERN_DEBUG " netdev_rx() Rx error was %8.8x.\n", 134662306a36Sopenharmony_ci frame_status); 134762306a36Sopenharmony_ci dev->stats.rx_errors++; 134862306a36Sopenharmony_ci if (frame_status & 0x00100000) 134962306a36Sopenharmony_ci dev->stats.rx_length_errors++; 135062306a36Sopenharmony_ci if (frame_status & 0x00010000) 135162306a36Sopenharmony_ci dev->stats.rx_fifo_errors++; 135262306a36Sopenharmony_ci if (frame_status & 0x00060000) 135362306a36Sopenharmony_ci dev->stats.rx_frame_errors++; 135462306a36Sopenharmony_ci if (frame_status & 0x00080000) 135562306a36Sopenharmony_ci dev->stats.rx_crc_errors++; 135662306a36Sopenharmony_ci if (frame_status & 0x00100000) { 135762306a36Sopenharmony_ci printk(KERN_WARNING "%s: Oversized Ethernet frame," 135862306a36Sopenharmony_ci " status %8.8x.\n", 135962306a36Sopenharmony_ci dev->name, frame_status); 136062306a36Sopenharmony_ci } 136162306a36Sopenharmony_ci } else { 136262306a36Sopenharmony_ci struct sk_buff *skb; 136362306a36Sopenharmony_ci#ifndef final_version 136462306a36Sopenharmony_ci if (netif_msg_rx_status(np)) 136562306a36Sopenharmony_ci printk(KERN_DEBUG " netdev_rx() normal Rx pkt length %d" 136662306a36Sopenharmony_ci ", bogus_cnt %d.\n", 136762306a36Sopenharmony_ci pkt_len, boguscnt); 136862306a36Sopenharmony_ci#endif 136962306a36Sopenharmony_ci /* Check if the packet is long enough to accept without copying 137062306a36Sopenharmony_ci to a minimally-sized skbuff. */ 137162306a36Sopenharmony_ci if (pkt_len < rx_copybreak && 137262306a36Sopenharmony_ci (skb = netdev_alloc_skb(dev, pkt_len + 2)) != NULL) { 137362306a36Sopenharmony_ci skb_reserve(skb, 2); /* 16 byte align the IP header */ 137462306a36Sopenharmony_ci dma_sync_single_for_cpu(&np->pci_dev->dev, 137562306a36Sopenharmony_ci le32_to_cpu(desc->frag.addr), 137662306a36Sopenharmony_ci np->rx_buf_sz, DMA_FROM_DEVICE); 137762306a36Sopenharmony_ci skb_copy_to_linear_data(skb, np->rx_skbuff[entry]->data, pkt_len); 137862306a36Sopenharmony_ci dma_sync_single_for_device(&np->pci_dev->dev, 137962306a36Sopenharmony_ci le32_to_cpu(desc->frag.addr), 138062306a36Sopenharmony_ci np->rx_buf_sz, DMA_FROM_DEVICE); 138162306a36Sopenharmony_ci skb_put(skb, pkt_len); 138262306a36Sopenharmony_ci } else { 138362306a36Sopenharmony_ci dma_unmap_single(&np->pci_dev->dev, 138462306a36Sopenharmony_ci le32_to_cpu(desc->frag.addr), 138562306a36Sopenharmony_ci np->rx_buf_sz, DMA_FROM_DEVICE); 138662306a36Sopenharmony_ci skb_put(skb = np->rx_skbuff[entry], pkt_len); 138762306a36Sopenharmony_ci np->rx_skbuff[entry] = NULL; 138862306a36Sopenharmony_ci } 138962306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 139062306a36Sopenharmony_ci /* Note: checksum -> skb->ip_summed = CHECKSUM_UNNECESSARY; */ 139162306a36Sopenharmony_ci netif_rx(skb); 139262306a36Sopenharmony_ci } 139362306a36Sopenharmony_ci entry = (entry + 1) % RX_RING_SIZE; 139462306a36Sopenharmony_ci received++; 139562306a36Sopenharmony_ci } 139662306a36Sopenharmony_ci np->cur_rx = entry; 139762306a36Sopenharmony_ci refill_rx (dev); 139862306a36Sopenharmony_ci np->budget -= received; 139962306a36Sopenharmony_ci iowrite16(DEFAULT_INTR, ioaddr + IntrEnable); 140062306a36Sopenharmony_ci return; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_cinot_done: 140362306a36Sopenharmony_ci np->cur_rx = entry; 140462306a36Sopenharmony_ci refill_rx (dev); 140562306a36Sopenharmony_ci if (!received) 140662306a36Sopenharmony_ci received = 1; 140762306a36Sopenharmony_ci np->budget -= received; 140862306a36Sopenharmony_ci if (np->budget <= 0) 140962306a36Sopenharmony_ci np->budget = RX_BUDGET; 141062306a36Sopenharmony_ci tasklet_schedule(&np->rx_tasklet); 141162306a36Sopenharmony_ci} 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_cistatic void refill_rx (struct net_device *dev) 141462306a36Sopenharmony_ci{ 141562306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 141662306a36Sopenharmony_ci int entry; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci /* Refill the Rx ring buffers. */ 141962306a36Sopenharmony_ci for (;(np->cur_rx - np->dirty_rx + RX_RING_SIZE) % RX_RING_SIZE > 0; 142062306a36Sopenharmony_ci np->dirty_rx = (np->dirty_rx + 1) % RX_RING_SIZE) { 142162306a36Sopenharmony_ci struct sk_buff *skb; 142262306a36Sopenharmony_ci entry = np->dirty_rx % RX_RING_SIZE; 142362306a36Sopenharmony_ci if (np->rx_skbuff[entry] == NULL) { 142462306a36Sopenharmony_ci skb = netdev_alloc_skb(dev, np->rx_buf_sz + 2); 142562306a36Sopenharmony_ci np->rx_skbuff[entry] = skb; 142662306a36Sopenharmony_ci if (skb == NULL) 142762306a36Sopenharmony_ci break; /* Better luck next round. */ 142862306a36Sopenharmony_ci skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ 142962306a36Sopenharmony_ci np->rx_ring[entry].frag.addr = cpu_to_le32( 143062306a36Sopenharmony_ci dma_map_single(&np->pci_dev->dev, skb->data, 143162306a36Sopenharmony_ci np->rx_buf_sz, DMA_FROM_DEVICE)); 143262306a36Sopenharmony_ci if (dma_mapping_error(&np->pci_dev->dev, 143362306a36Sopenharmony_ci np->rx_ring[entry].frag.addr)) { 143462306a36Sopenharmony_ci dev_kfree_skb_irq(skb); 143562306a36Sopenharmony_ci np->rx_skbuff[entry] = NULL; 143662306a36Sopenharmony_ci break; 143762306a36Sopenharmony_ci } 143862306a36Sopenharmony_ci } 143962306a36Sopenharmony_ci /* Perhaps we need not reset this field. */ 144062306a36Sopenharmony_ci np->rx_ring[entry].frag.length = 144162306a36Sopenharmony_ci cpu_to_le32(np->rx_buf_sz | LastFrag); 144262306a36Sopenharmony_ci np->rx_ring[entry].status = 0; 144362306a36Sopenharmony_ci } 144462306a36Sopenharmony_ci} 144562306a36Sopenharmony_cistatic void netdev_error(struct net_device *dev, int intr_status) 144662306a36Sopenharmony_ci{ 144762306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 144862306a36Sopenharmony_ci void __iomem *ioaddr = np->base; 144962306a36Sopenharmony_ci u16 mii_ctl, mii_advertise, mii_lpa; 145062306a36Sopenharmony_ci int speed; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci if (intr_status & LinkChange) { 145362306a36Sopenharmony_ci if (mdio_wait_link(dev, 10) == 0) { 145462306a36Sopenharmony_ci printk(KERN_INFO "%s: Link up\n", dev->name); 145562306a36Sopenharmony_ci if (np->an_enable) { 145662306a36Sopenharmony_ci mii_advertise = mdio_read(dev, np->phys[0], 145762306a36Sopenharmony_ci MII_ADVERTISE); 145862306a36Sopenharmony_ci mii_lpa = mdio_read(dev, np->phys[0], MII_LPA); 145962306a36Sopenharmony_ci mii_advertise &= mii_lpa; 146062306a36Sopenharmony_ci printk(KERN_INFO "%s: Link changed: ", 146162306a36Sopenharmony_ci dev->name); 146262306a36Sopenharmony_ci if (mii_advertise & ADVERTISE_100FULL) { 146362306a36Sopenharmony_ci np->speed = 100; 146462306a36Sopenharmony_ci printk("100Mbps, full duplex\n"); 146562306a36Sopenharmony_ci } else if (mii_advertise & ADVERTISE_100HALF) { 146662306a36Sopenharmony_ci np->speed = 100; 146762306a36Sopenharmony_ci printk("100Mbps, half duplex\n"); 146862306a36Sopenharmony_ci } else if (mii_advertise & ADVERTISE_10FULL) { 146962306a36Sopenharmony_ci np->speed = 10; 147062306a36Sopenharmony_ci printk("10Mbps, full duplex\n"); 147162306a36Sopenharmony_ci } else if (mii_advertise & ADVERTISE_10HALF) { 147262306a36Sopenharmony_ci np->speed = 10; 147362306a36Sopenharmony_ci printk("10Mbps, half duplex\n"); 147462306a36Sopenharmony_ci } else 147562306a36Sopenharmony_ci printk("\n"); 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci } else { 147862306a36Sopenharmony_ci mii_ctl = mdio_read(dev, np->phys[0], MII_BMCR); 147962306a36Sopenharmony_ci speed = (mii_ctl & BMCR_SPEED100) ? 100 : 10; 148062306a36Sopenharmony_ci np->speed = speed; 148162306a36Sopenharmony_ci printk(KERN_INFO "%s: Link changed: %dMbps ,", 148262306a36Sopenharmony_ci dev->name, speed); 148362306a36Sopenharmony_ci printk("%s duplex.\n", 148462306a36Sopenharmony_ci (mii_ctl & BMCR_FULLDPLX) ? 148562306a36Sopenharmony_ci "full" : "half"); 148662306a36Sopenharmony_ci } 148762306a36Sopenharmony_ci check_duplex(dev); 148862306a36Sopenharmony_ci if (np->flowctrl && np->mii_if.full_duplex) { 148962306a36Sopenharmony_ci iowrite16(ioread16(ioaddr + MulticastFilter1+2) | 0x0200, 149062306a36Sopenharmony_ci ioaddr + MulticastFilter1+2); 149162306a36Sopenharmony_ci iowrite16(ioread16(ioaddr + MACCtrl0) | EnbFlowCtrl, 149262306a36Sopenharmony_ci ioaddr + MACCtrl0); 149362306a36Sopenharmony_ci } 149462306a36Sopenharmony_ci netif_carrier_on(dev); 149562306a36Sopenharmony_ci } else { 149662306a36Sopenharmony_ci printk(KERN_INFO "%s: Link down\n", dev->name); 149762306a36Sopenharmony_ci netif_carrier_off(dev); 149862306a36Sopenharmony_ci } 149962306a36Sopenharmony_ci } 150062306a36Sopenharmony_ci if (intr_status & StatsMax) { 150162306a36Sopenharmony_ci get_stats(dev); 150262306a36Sopenharmony_ci } 150362306a36Sopenharmony_ci if (intr_status & IntrPCIErr) { 150462306a36Sopenharmony_ci printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", 150562306a36Sopenharmony_ci dev->name, intr_status); 150662306a36Sopenharmony_ci /* We must do a global reset of DMA to continue. */ 150762306a36Sopenharmony_ci } 150862306a36Sopenharmony_ci} 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_cistatic struct net_device_stats *get_stats(struct net_device *dev) 151162306a36Sopenharmony_ci{ 151262306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 151362306a36Sopenharmony_ci void __iomem *ioaddr = np->base; 151462306a36Sopenharmony_ci unsigned long flags; 151562306a36Sopenharmony_ci u8 late_coll, single_coll, mult_coll; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci spin_lock_irqsave(&np->statlock, flags); 151862306a36Sopenharmony_ci /* The chip only need report frame silently dropped. */ 151962306a36Sopenharmony_ci dev->stats.rx_missed_errors += ioread8(ioaddr + RxMissed); 152062306a36Sopenharmony_ci dev->stats.tx_packets += ioread16(ioaddr + TxFramesOK); 152162306a36Sopenharmony_ci dev->stats.rx_packets += ioread16(ioaddr + RxFramesOK); 152262306a36Sopenharmony_ci dev->stats.tx_carrier_errors += ioread8(ioaddr + StatsCarrierError); 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci mult_coll = ioread8(ioaddr + StatsMultiColl); 152562306a36Sopenharmony_ci np->xstats.tx_multiple_collisions += mult_coll; 152662306a36Sopenharmony_ci single_coll = ioread8(ioaddr + StatsOneColl); 152762306a36Sopenharmony_ci np->xstats.tx_single_collisions += single_coll; 152862306a36Sopenharmony_ci late_coll = ioread8(ioaddr + StatsLateColl); 152962306a36Sopenharmony_ci np->xstats.tx_late_collisions += late_coll; 153062306a36Sopenharmony_ci dev->stats.collisions += mult_coll 153162306a36Sopenharmony_ci + single_coll 153262306a36Sopenharmony_ci + late_coll; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci np->xstats.tx_deferred += ioread8(ioaddr + StatsTxDefer); 153562306a36Sopenharmony_ci np->xstats.tx_deferred_excessive += ioread8(ioaddr + StatsTxXSDefer); 153662306a36Sopenharmony_ci np->xstats.tx_aborted += ioread8(ioaddr + StatsTxAbort); 153762306a36Sopenharmony_ci np->xstats.tx_bcasts += ioread8(ioaddr + StatsBcastTx); 153862306a36Sopenharmony_ci np->xstats.rx_bcasts += ioread8(ioaddr + StatsBcastRx); 153962306a36Sopenharmony_ci np->xstats.tx_mcasts += ioread8(ioaddr + StatsMcastTx); 154062306a36Sopenharmony_ci np->xstats.rx_mcasts += ioread8(ioaddr + StatsMcastRx); 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci dev->stats.tx_bytes += ioread16(ioaddr + TxOctetsLow); 154362306a36Sopenharmony_ci dev->stats.tx_bytes += ioread16(ioaddr + TxOctetsHigh) << 16; 154462306a36Sopenharmony_ci dev->stats.rx_bytes += ioread16(ioaddr + RxOctetsLow); 154562306a36Sopenharmony_ci dev->stats.rx_bytes += ioread16(ioaddr + RxOctetsHigh) << 16; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci spin_unlock_irqrestore(&np->statlock, flags); 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci return &dev->stats; 155062306a36Sopenharmony_ci} 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_cistatic void set_rx_mode(struct net_device *dev) 155362306a36Sopenharmony_ci{ 155462306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 155562306a36Sopenharmony_ci void __iomem *ioaddr = np->base; 155662306a36Sopenharmony_ci u16 mc_filter[4]; /* Multicast hash filter */ 155762306a36Sopenharmony_ci u32 rx_mode; 155862306a36Sopenharmony_ci int i; 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ 156162306a36Sopenharmony_ci memset(mc_filter, 0xff, sizeof(mc_filter)); 156262306a36Sopenharmony_ci rx_mode = AcceptBroadcast | AcceptMulticast | AcceptAll | AcceptMyPhys; 156362306a36Sopenharmony_ci } else if ((netdev_mc_count(dev) > multicast_filter_limit) || 156462306a36Sopenharmony_ci (dev->flags & IFF_ALLMULTI)) { 156562306a36Sopenharmony_ci /* Too many to match, or accept all multicasts. */ 156662306a36Sopenharmony_ci memset(mc_filter, 0xff, sizeof(mc_filter)); 156762306a36Sopenharmony_ci rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; 156862306a36Sopenharmony_ci } else if (!netdev_mc_empty(dev)) { 156962306a36Sopenharmony_ci struct netdev_hw_addr *ha; 157062306a36Sopenharmony_ci int bit; 157162306a36Sopenharmony_ci int index; 157262306a36Sopenharmony_ci int crc; 157362306a36Sopenharmony_ci memset (mc_filter, 0, sizeof (mc_filter)); 157462306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 157562306a36Sopenharmony_ci crc = ether_crc_le(ETH_ALEN, ha->addr); 157662306a36Sopenharmony_ci for (index=0, bit=0; bit < 6; bit++, crc <<= 1) 157762306a36Sopenharmony_ci if (crc & 0x80000000) index |= 1 << bit; 157862306a36Sopenharmony_ci mc_filter[index/16] |= (1 << (index % 16)); 157962306a36Sopenharmony_ci } 158062306a36Sopenharmony_ci rx_mode = AcceptBroadcast | AcceptMultiHash | AcceptMyPhys; 158162306a36Sopenharmony_ci } else { 158262306a36Sopenharmony_ci iowrite8(AcceptBroadcast | AcceptMyPhys, ioaddr + RxMode); 158362306a36Sopenharmony_ci return; 158462306a36Sopenharmony_ci } 158562306a36Sopenharmony_ci if (np->mii_if.full_duplex && np->flowctrl) 158662306a36Sopenharmony_ci mc_filter[3] |= 0x0200; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci for (i = 0; i < 4; i++) 158962306a36Sopenharmony_ci iowrite16(mc_filter[i], ioaddr + MulticastFilter0 + i*2); 159062306a36Sopenharmony_ci iowrite8(rx_mode, ioaddr + RxMode); 159162306a36Sopenharmony_ci} 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_cistatic int __set_mac_addr(struct net_device *dev) 159462306a36Sopenharmony_ci{ 159562306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 159662306a36Sopenharmony_ci u16 addr16; 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci addr16 = (dev->dev_addr[0] | (dev->dev_addr[1] << 8)); 159962306a36Sopenharmony_ci iowrite16(addr16, np->base + StationAddr); 160062306a36Sopenharmony_ci addr16 = (dev->dev_addr[2] | (dev->dev_addr[3] << 8)); 160162306a36Sopenharmony_ci iowrite16(addr16, np->base + StationAddr+2); 160262306a36Sopenharmony_ci addr16 = (dev->dev_addr[4] | (dev->dev_addr[5] << 8)); 160362306a36Sopenharmony_ci iowrite16(addr16, np->base + StationAddr+4); 160462306a36Sopenharmony_ci return 0; 160562306a36Sopenharmony_ci} 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci/* Invoked with rtnl_lock held */ 160862306a36Sopenharmony_cistatic int sundance_set_mac_addr(struct net_device *dev, void *data) 160962306a36Sopenharmony_ci{ 161062306a36Sopenharmony_ci const struct sockaddr *addr = data; 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 161362306a36Sopenharmony_ci return -EADDRNOTAVAIL; 161462306a36Sopenharmony_ci eth_hw_addr_set(dev, addr->sa_data); 161562306a36Sopenharmony_ci __set_mac_addr(dev); 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci return 0; 161862306a36Sopenharmony_ci} 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_cistatic const struct { 162162306a36Sopenharmony_ci const char name[ETH_GSTRING_LEN]; 162262306a36Sopenharmony_ci} sundance_stats[] = { 162362306a36Sopenharmony_ci { "tx_multiple_collisions" }, 162462306a36Sopenharmony_ci { "tx_single_collisions" }, 162562306a36Sopenharmony_ci { "tx_late_collisions" }, 162662306a36Sopenharmony_ci { "tx_deferred" }, 162762306a36Sopenharmony_ci { "tx_deferred_excessive" }, 162862306a36Sopenharmony_ci { "tx_aborted" }, 162962306a36Sopenharmony_ci { "tx_bcasts" }, 163062306a36Sopenharmony_ci { "rx_bcasts" }, 163162306a36Sopenharmony_ci { "tx_mcasts" }, 163262306a36Sopenharmony_ci { "rx_mcasts" }, 163362306a36Sopenharmony_ci}; 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_cistatic int check_if_running(struct net_device *dev) 163662306a36Sopenharmony_ci{ 163762306a36Sopenharmony_ci if (!netif_running(dev)) 163862306a36Sopenharmony_ci return -EINVAL; 163962306a36Sopenharmony_ci return 0; 164062306a36Sopenharmony_ci} 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_cistatic void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) 164362306a36Sopenharmony_ci{ 164462306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 164562306a36Sopenharmony_ci strscpy(info->driver, DRV_NAME, sizeof(info->driver)); 164662306a36Sopenharmony_ci strscpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); 164762306a36Sopenharmony_ci} 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_cistatic int get_link_ksettings(struct net_device *dev, 165062306a36Sopenharmony_ci struct ethtool_link_ksettings *cmd) 165162306a36Sopenharmony_ci{ 165262306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 165362306a36Sopenharmony_ci spin_lock_irq(&np->lock); 165462306a36Sopenharmony_ci mii_ethtool_get_link_ksettings(&np->mii_if, cmd); 165562306a36Sopenharmony_ci spin_unlock_irq(&np->lock); 165662306a36Sopenharmony_ci return 0; 165762306a36Sopenharmony_ci} 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_cistatic int set_link_ksettings(struct net_device *dev, 166062306a36Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 166162306a36Sopenharmony_ci{ 166262306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 166362306a36Sopenharmony_ci int res; 166462306a36Sopenharmony_ci spin_lock_irq(&np->lock); 166562306a36Sopenharmony_ci res = mii_ethtool_set_link_ksettings(&np->mii_if, cmd); 166662306a36Sopenharmony_ci spin_unlock_irq(&np->lock); 166762306a36Sopenharmony_ci return res; 166862306a36Sopenharmony_ci} 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_cistatic int nway_reset(struct net_device *dev) 167162306a36Sopenharmony_ci{ 167262306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 167362306a36Sopenharmony_ci return mii_nway_restart(&np->mii_if); 167462306a36Sopenharmony_ci} 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_cistatic u32 get_link(struct net_device *dev) 167762306a36Sopenharmony_ci{ 167862306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 167962306a36Sopenharmony_ci return mii_link_ok(&np->mii_if); 168062306a36Sopenharmony_ci} 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_cistatic u32 get_msglevel(struct net_device *dev) 168362306a36Sopenharmony_ci{ 168462306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 168562306a36Sopenharmony_ci return np->msg_enable; 168662306a36Sopenharmony_ci} 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_cistatic void set_msglevel(struct net_device *dev, u32 val) 168962306a36Sopenharmony_ci{ 169062306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 169162306a36Sopenharmony_ci np->msg_enable = val; 169262306a36Sopenharmony_ci} 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_cistatic void get_strings(struct net_device *dev, u32 stringset, 169562306a36Sopenharmony_ci u8 *data) 169662306a36Sopenharmony_ci{ 169762306a36Sopenharmony_ci if (stringset == ETH_SS_STATS) 169862306a36Sopenharmony_ci memcpy(data, sundance_stats, sizeof(sundance_stats)); 169962306a36Sopenharmony_ci} 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_cistatic int get_sset_count(struct net_device *dev, int sset) 170262306a36Sopenharmony_ci{ 170362306a36Sopenharmony_ci switch (sset) { 170462306a36Sopenharmony_ci case ETH_SS_STATS: 170562306a36Sopenharmony_ci return ARRAY_SIZE(sundance_stats); 170662306a36Sopenharmony_ci default: 170762306a36Sopenharmony_ci return -EOPNOTSUPP; 170862306a36Sopenharmony_ci } 170962306a36Sopenharmony_ci} 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_cistatic void get_ethtool_stats(struct net_device *dev, 171262306a36Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 171362306a36Sopenharmony_ci{ 171462306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 171562306a36Sopenharmony_ci int i = 0; 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci get_stats(dev); 171862306a36Sopenharmony_ci data[i++] = np->xstats.tx_multiple_collisions; 171962306a36Sopenharmony_ci data[i++] = np->xstats.tx_single_collisions; 172062306a36Sopenharmony_ci data[i++] = np->xstats.tx_late_collisions; 172162306a36Sopenharmony_ci data[i++] = np->xstats.tx_deferred; 172262306a36Sopenharmony_ci data[i++] = np->xstats.tx_deferred_excessive; 172362306a36Sopenharmony_ci data[i++] = np->xstats.tx_aborted; 172462306a36Sopenharmony_ci data[i++] = np->xstats.tx_bcasts; 172562306a36Sopenharmony_ci data[i++] = np->xstats.rx_bcasts; 172662306a36Sopenharmony_ci data[i++] = np->xstats.tx_mcasts; 172762306a36Sopenharmony_ci data[i++] = np->xstats.rx_mcasts; 172862306a36Sopenharmony_ci} 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci#ifdef CONFIG_PM 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_cistatic void sundance_get_wol(struct net_device *dev, 173362306a36Sopenharmony_ci struct ethtool_wolinfo *wol) 173462306a36Sopenharmony_ci{ 173562306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 173662306a36Sopenharmony_ci void __iomem *ioaddr = np->base; 173762306a36Sopenharmony_ci u8 wol_bits; 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci wol->wolopts = 0; 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci wol->supported = (WAKE_PHY | WAKE_MAGIC); 174262306a36Sopenharmony_ci if (!np->wol_enabled) 174362306a36Sopenharmony_ci return; 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci wol_bits = ioread8(ioaddr + WakeEvent); 174662306a36Sopenharmony_ci if (wol_bits & MagicPktEnable) 174762306a36Sopenharmony_ci wol->wolopts |= WAKE_MAGIC; 174862306a36Sopenharmony_ci if (wol_bits & LinkEventEnable) 174962306a36Sopenharmony_ci wol->wolopts |= WAKE_PHY; 175062306a36Sopenharmony_ci} 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_cistatic int sundance_set_wol(struct net_device *dev, 175362306a36Sopenharmony_ci struct ethtool_wolinfo *wol) 175462306a36Sopenharmony_ci{ 175562306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 175662306a36Sopenharmony_ci void __iomem *ioaddr = np->base; 175762306a36Sopenharmony_ci u8 wol_bits; 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci if (!device_can_wakeup(&np->pci_dev->dev)) 176062306a36Sopenharmony_ci return -EOPNOTSUPP; 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci np->wol_enabled = !!(wol->wolopts); 176362306a36Sopenharmony_ci wol_bits = ioread8(ioaddr + WakeEvent); 176462306a36Sopenharmony_ci wol_bits &= ~(WakePktEnable | MagicPktEnable | 176562306a36Sopenharmony_ci LinkEventEnable | WolEnable); 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci if (np->wol_enabled) { 176862306a36Sopenharmony_ci if (wol->wolopts & WAKE_MAGIC) 176962306a36Sopenharmony_ci wol_bits |= (MagicPktEnable | WolEnable); 177062306a36Sopenharmony_ci if (wol->wolopts & WAKE_PHY) 177162306a36Sopenharmony_ci wol_bits |= (LinkEventEnable | WolEnable); 177262306a36Sopenharmony_ci } 177362306a36Sopenharmony_ci iowrite8(wol_bits, ioaddr + WakeEvent); 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci device_set_wakeup_enable(&np->pci_dev->dev, np->wol_enabled); 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci return 0; 177862306a36Sopenharmony_ci} 177962306a36Sopenharmony_ci#else 178062306a36Sopenharmony_ci#define sundance_get_wol NULL 178162306a36Sopenharmony_ci#define sundance_set_wol NULL 178262306a36Sopenharmony_ci#endif /* CONFIG_PM */ 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_cistatic const struct ethtool_ops ethtool_ops = { 178562306a36Sopenharmony_ci .begin = check_if_running, 178662306a36Sopenharmony_ci .get_drvinfo = get_drvinfo, 178762306a36Sopenharmony_ci .nway_reset = nway_reset, 178862306a36Sopenharmony_ci .get_link = get_link, 178962306a36Sopenharmony_ci .get_wol = sundance_get_wol, 179062306a36Sopenharmony_ci .set_wol = sundance_set_wol, 179162306a36Sopenharmony_ci .get_msglevel = get_msglevel, 179262306a36Sopenharmony_ci .set_msglevel = set_msglevel, 179362306a36Sopenharmony_ci .get_strings = get_strings, 179462306a36Sopenharmony_ci .get_sset_count = get_sset_count, 179562306a36Sopenharmony_ci .get_ethtool_stats = get_ethtool_stats, 179662306a36Sopenharmony_ci .get_link_ksettings = get_link_ksettings, 179762306a36Sopenharmony_ci .set_link_ksettings = set_link_ksettings, 179862306a36Sopenharmony_ci}; 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_cistatic int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 180162306a36Sopenharmony_ci{ 180262306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 180362306a36Sopenharmony_ci int rc; 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci if (!netif_running(dev)) 180662306a36Sopenharmony_ci return -EINVAL; 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci spin_lock_irq(&np->lock); 180962306a36Sopenharmony_ci rc = generic_mii_ioctl(&np->mii_if, if_mii(rq), cmd, NULL); 181062306a36Sopenharmony_ci spin_unlock_irq(&np->lock); 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci return rc; 181362306a36Sopenharmony_ci} 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_cistatic int netdev_close(struct net_device *dev) 181662306a36Sopenharmony_ci{ 181762306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 181862306a36Sopenharmony_ci void __iomem *ioaddr = np->base; 181962306a36Sopenharmony_ci struct sk_buff *skb; 182062306a36Sopenharmony_ci int i; 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci /* Wait and kill tasklet */ 182362306a36Sopenharmony_ci tasklet_kill(&np->rx_tasklet); 182462306a36Sopenharmony_ci tasklet_kill(&np->tx_tasklet); 182562306a36Sopenharmony_ci np->cur_tx = 0; 182662306a36Sopenharmony_ci np->dirty_tx = 0; 182762306a36Sopenharmony_ci np->cur_task = 0; 182862306a36Sopenharmony_ci np->last_tx = NULL; 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci netif_stop_queue(dev); 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci if (netif_msg_ifdown(np)) { 183362306a36Sopenharmony_ci printk(KERN_DEBUG "%s: Shutting down ethercard, status was Tx %2.2x " 183462306a36Sopenharmony_ci "Rx %4.4x Int %2.2x.\n", 183562306a36Sopenharmony_ci dev->name, ioread8(ioaddr + TxStatus), 183662306a36Sopenharmony_ci ioread32(ioaddr + RxStatus), ioread16(ioaddr + IntrStatus)); 183762306a36Sopenharmony_ci printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d, Rx %d / %d.\n", 183862306a36Sopenharmony_ci dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx); 183962306a36Sopenharmony_ci } 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci /* Disable interrupts by clearing the interrupt mask. */ 184262306a36Sopenharmony_ci iowrite16(0x0000, ioaddr + IntrEnable); 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci /* Disable Rx and Tx DMA for safely release resource */ 184562306a36Sopenharmony_ci iowrite32(0x500, ioaddr + DMACtrl); 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci /* Stop the chip's Tx and Rx processes. */ 184862306a36Sopenharmony_ci iowrite16(TxDisable | RxDisable | StatsDisable, ioaddr + MACCtrl1); 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci for (i = 2000; i > 0; i--) { 185162306a36Sopenharmony_ci if ((ioread32(ioaddr + DMACtrl) & 0xc000) == 0) 185262306a36Sopenharmony_ci break; 185362306a36Sopenharmony_ci mdelay(1); 185462306a36Sopenharmony_ci } 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci iowrite16(GlobalReset | DMAReset | FIFOReset | NetworkReset, 185762306a36Sopenharmony_ci ioaddr + ASIC_HI_WORD(ASICCtrl)); 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci for (i = 2000; i > 0; i--) { 186062306a36Sopenharmony_ci if ((ioread16(ioaddr + ASIC_HI_WORD(ASICCtrl)) & ResetBusy) == 0) 186162306a36Sopenharmony_ci break; 186262306a36Sopenharmony_ci mdelay(1); 186362306a36Sopenharmony_ci } 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci#ifdef __i386__ 186662306a36Sopenharmony_ci if (netif_msg_hw(np)) { 186762306a36Sopenharmony_ci printk(KERN_DEBUG " Tx ring at %8.8x:\n", 186862306a36Sopenharmony_ci (int)(np->tx_ring_dma)); 186962306a36Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) 187062306a36Sopenharmony_ci printk(KERN_DEBUG " #%d desc. %4.4x %8.8x %8.8x.\n", 187162306a36Sopenharmony_ci i, np->tx_ring[i].status, np->tx_ring[i].frag.addr, 187262306a36Sopenharmony_ci np->tx_ring[i].frag.length); 187362306a36Sopenharmony_ci printk(KERN_DEBUG " Rx ring %8.8x:\n", 187462306a36Sopenharmony_ci (int)(np->rx_ring_dma)); 187562306a36Sopenharmony_ci for (i = 0; i < /*RX_RING_SIZE*/4 ; i++) { 187662306a36Sopenharmony_ci printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x\n", 187762306a36Sopenharmony_ci i, np->rx_ring[i].status, np->rx_ring[i].frag.addr, 187862306a36Sopenharmony_ci np->rx_ring[i].frag.length); 187962306a36Sopenharmony_ci } 188062306a36Sopenharmony_ci } 188162306a36Sopenharmony_ci#endif /* __i386__ debugging only */ 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci free_irq(np->pci_dev->irq, dev); 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci del_timer_sync(&np->timer); 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci /* Free all the skbuffs in the Rx queue. */ 188862306a36Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 188962306a36Sopenharmony_ci np->rx_ring[i].status = 0; 189062306a36Sopenharmony_ci skb = np->rx_skbuff[i]; 189162306a36Sopenharmony_ci if (skb) { 189262306a36Sopenharmony_ci dma_unmap_single(&np->pci_dev->dev, 189362306a36Sopenharmony_ci le32_to_cpu(np->rx_ring[i].frag.addr), 189462306a36Sopenharmony_ci np->rx_buf_sz, DMA_FROM_DEVICE); 189562306a36Sopenharmony_ci dev_kfree_skb(skb); 189662306a36Sopenharmony_ci np->rx_skbuff[i] = NULL; 189762306a36Sopenharmony_ci } 189862306a36Sopenharmony_ci np->rx_ring[i].frag.addr = cpu_to_le32(0xBADF00D0); /* poison */ 189962306a36Sopenharmony_ci } 190062306a36Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) { 190162306a36Sopenharmony_ci np->tx_ring[i].next_desc = 0; 190262306a36Sopenharmony_ci skb = np->tx_skbuff[i]; 190362306a36Sopenharmony_ci if (skb) { 190462306a36Sopenharmony_ci dma_unmap_single(&np->pci_dev->dev, 190562306a36Sopenharmony_ci le32_to_cpu(np->tx_ring[i].frag.addr), 190662306a36Sopenharmony_ci skb->len, DMA_TO_DEVICE); 190762306a36Sopenharmony_ci dev_kfree_skb(skb); 190862306a36Sopenharmony_ci np->tx_skbuff[i] = NULL; 190962306a36Sopenharmony_ci } 191062306a36Sopenharmony_ci } 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci return 0; 191362306a36Sopenharmony_ci} 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_cistatic void sundance_remove1(struct pci_dev *pdev) 191662306a36Sopenharmony_ci{ 191762306a36Sopenharmony_ci struct net_device *dev = pci_get_drvdata(pdev); 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci if (dev) { 192062306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 192162306a36Sopenharmony_ci unregister_netdev(dev); 192262306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, 192362306a36Sopenharmony_ci np->rx_ring, np->rx_ring_dma); 192462306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, 192562306a36Sopenharmony_ci np->tx_ring, np->tx_ring_dma); 192662306a36Sopenharmony_ci pci_iounmap(pdev, np->base); 192762306a36Sopenharmony_ci pci_release_regions(pdev); 192862306a36Sopenharmony_ci free_netdev(dev); 192962306a36Sopenharmony_ci } 193062306a36Sopenharmony_ci} 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_cistatic int __maybe_unused sundance_suspend(struct device *dev_d) 193362306a36Sopenharmony_ci{ 193462306a36Sopenharmony_ci struct net_device *dev = dev_get_drvdata(dev_d); 193562306a36Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 193662306a36Sopenharmony_ci void __iomem *ioaddr = np->base; 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci if (!netif_running(dev)) 193962306a36Sopenharmony_ci return 0; 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci netdev_close(dev); 194262306a36Sopenharmony_ci netif_device_detach(dev); 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci if (np->wol_enabled) { 194562306a36Sopenharmony_ci iowrite8(AcceptBroadcast | AcceptMyPhys, ioaddr + RxMode); 194662306a36Sopenharmony_ci iowrite16(RxEnable, ioaddr + MACCtrl1); 194762306a36Sopenharmony_ci } 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_ci device_set_wakeup_enable(dev_d, np->wol_enabled); 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci return 0; 195262306a36Sopenharmony_ci} 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_cistatic int __maybe_unused sundance_resume(struct device *dev_d) 195562306a36Sopenharmony_ci{ 195662306a36Sopenharmony_ci struct net_device *dev = dev_get_drvdata(dev_d); 195762306a36Sopenharmony_ci int err = 0; 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci if (!netif_running(dev)) 196062306a36Sopenharmony_ci return 0; 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci err = netdev_open(dev); 196362306a36Sopenharmony_ci if (err) { 196462306a36Sopenharmony_ci printk(KERN_ERR "%s: Can't resume interface!\n", 196562306a36Sopenharmony_ci dev->name); 196662306a36Sopenharmony_ci goto out; 196762306a36Sopenharmony_ci } 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci netif_device_attach(dev); 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ciout: 197262306a36Sopenharmony_ci return err; 197362306a36Sopenharmony_ci} 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(sundance_pm_ops, sundance_suspend, sundance_resume); 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_cistatic struct pci_driver sundance_driver = { 197862306a36Sopenharmony_ci .name = DRV_NAME, 197962306a36Sopenharmony_ci .id_table = sundance_pci_tbl, 198062306a36Sopenharmony_ci .probe = sundance_probe1, 198162306a36Sopenharmony_ci .remove = sundance_remove1, 198262306a36Sopenharmony_ci .driver.pm = &sundance_pm_ops, 198362306a36Sopenharmony_ci}; 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_cimodule_pci_driver(sundance_driver); 1986