18c2ecf20Sopenharmony_ci/* sundance.c: A Linux device driver for the Sundance ST201 "Alta". */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci Written 1999-2000 by Donald Becker. 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci This software may be used and distributed according to the terms of 68c2ecf20Sopenharmony_ci the GNU General Public License (GPL), incorporated herein by reference. 78c2ecf20Sopenharmony_ci Drivers based on or derived from this code fall under the GPL and must 88c2ecf20Sopenharmony_ci retain the authorship, copyright and license notice. This file is not 98c2ecf20Sopenharmony_ci a complete program and may only be used when the entire operating 108c2ecf20Sopenharmony_ci system is licensed under the GPL. 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci The author may be reached as becker@scyld.com, or C/O 138c2ecf20Sopenharmony_ci Scyld Computing Corporation 148c2ecf20Sopenharmony_ci 410 Severn Ave., Suite 210 158c2ecf20Sopenharmony_ci Annapolis MD 21403 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci Support and updates available at 188c2ecf20Sopenharmony_ci http://www.scyld.com/network/sundance.html 198c2ecf20Sopenharmony_ci [link no longer provides useful info -jgarzik] 208c2ecf20Sopenharmony_ci Archives of the mailing list are still available at 218c2ecf20Sopenharmony_ci https://www.beowulf.org/pipermail/netdrivers/ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci*/ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define DRV_NAME "sundance" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* The user-configurable values. 288c2ecf20Sopenharmony_ci These may be modified when a driver module is loaded.*/ 298c2ecf20Sopenharmony_cistatic int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */ 308c2ecf20Sopenharmony_ci/* Maximum number of multicast addresses to filter (vs. rx-all-multicast). 318c2ecf20Sopenharmony_ci Typical is a 64 element hash table based on the Ethernet CRC. */ 328c2ecf20Sopenharmony_cistatic const int multicast_filter_limit = 32; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* Set the copy breakpoint for the copy-only-tiny-frames scheme. 358c2ecf20Sopenharmony_ci Setting to > 1518 effectively disables this feature. 368c2ecf20Sopenharmony_ci This chip can receive into offset buffers, so the Alpha does not 378c2ecf20Sopenharmony_ci need a copy-align. */ 388c2ecf20Sopenharmony_cistatic int rx_copybreak; 398c2ecf20Sopenharmony_cistatic int flowctrl=1; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* media[] specifies the media type the NIC operates at. 428c2ecf20Sopenharmony_ci autosense Autosensing active media. 438c2ecf20Sopenharmony_ci 10mbps_hd 10Mbps half duplex. 448c2ecf20Sopenharmony_ci 10mbps_fd 10Mbps full duplex. 458c2ecf20Sopenharmony_ci 100mbps_hd 100Mbps half duplex. 468c2ecf20Sopenharmony_ci 100mbps_fd 100Mbps full duplex. 478c2ecf20Sopenharmony_ci 0 Autosensing active media. 488c2ecf20Sopenharmony_ci 1 10Mbps half duplex. 498c2ecf20Sopenharmony_ci 2 10Mbps full duplex. 508c2ecf20Sopenharmony_ci 3 100Mbps half duplex. 518c2ecf20Sopenharmony_ci 4 100Mbps full duplex. 528c2ecf20Sopenharmony_ci*/ 538c2ecf20Sopenharmony_ci#define MAX_UNITS 8 548c2ecf20Sopenharmony_cistatic char *media[MAX_UNITS]; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* Operational parameters that are set at compile time. */ 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* Keep the ring sizes a power of two for compile efficiency. 608c2ecf20Sopenharmony_ci The compiler will convert <unsigned>'%'<2^N> into a bit mask. 618c2ecf20Sopenharmony_ci Making the Tx ring too large decreases the effectiveness of channel 628c2ecf20Sopenharmony_ci bonding and packet priority, and more than 128 requires modifying the 638c2ecf20Sopenharmony_ci Tx error recovery. 648c2ecf20Sopenharmony_ci Large receive rings merely waste memory. */ 658c2ecf20Sopenharmony_ci#define TX_RING_SIZE 32 668c2ecf20Sopenharmony_ci#define TX_QUEUE_LEN (TX_RING_SIZE - 1) /* Limit ring entries actually used. */ 678c2ecf20Sopenharmony_ci#define RX_RING_SIZE 64 688c2ecf20Sopenharmony_ci#define RX_BUDGET 32 698c2ecf20Sopenharmony_ci#define TX_TOTAL_SIZE TX_RING_SIZE*sizeof(struct netdev_desc) 708c2ecf20Sopenharmony_ci#define RX_TOTAL_SIZE RX_RING_SIZE*sizeof(struct netdev_desc) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* Operational parameters that usually are not changed. */ 738c2ecf20Sopenharmony_ci/* Time in jiffies before concluding the transmitter is hung. */ 748c2ecf20Sopenharmony_ci#define TX_TIMEOUT (4*HZ) 758c2ecf20Sopenharmony_ci#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* Include files, designed to support most kernel versions 2.0.0 and later. */ 788c2ecf20Sopenharmony_ci#include <linux/module.h> 798c2ecf20Sopenharmony_ci#include <linux/kernel.h> 808c2ecf20Sopenharmony_ci#include <linux/string.h> 818c2ecf20Sopenharmony_ci#include <linux/timer.h> 828c2ecf20Sopenharmony_ci#include <linux/errno.h> 838c2ecf20Sopenharmony_ci#include <linux/ioport.h> 848c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 858c2ecf20Sopenharmony_ci#include <linux/pci.h> 868c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 878c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 888c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 898c2ecf20Sopenharmony_ci#include <linux/init.h> 908c2ecf20Sopenharmony_ci#include <linux/bitops.h> 918c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 928c2ecf20Sopenharmony_ci#include <asm/processor.h> /* Processor type for cache alignment. */ 938c2ecf20Sopenharmony_ci#include <asm/io.h> 948c2ecf20Sopenharmony_ci#include <linux/delay.h> 958c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 968c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 978c2ecf20Sopenharmony_ci#include <linux/crc32.h> 988c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 998c2ecf20Sopenharmony_ci#include <linux/mii.h> 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ciMODULE_AUTHOR("Donald Becker <becker@scyld.com>"); 1028c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Sundance Alta Ethernet driver"); 1038c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cimodule_param(debug, int, 0); 1068c2ecf20Sopenharmony_cimodule_param(rx_copybreak, int, 0); 1078c2ecf20Sopenharmony_cimodule_param_array(media, charp, NULL, 0); 1088c2ecf20Sopenharmony_cimodule_param(flowctrl, int, 0); 1098c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Sundance Alta debug level (0-5)"); 1108c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rx_copybreak, "Sundance Alta copy breakpoint for copy-only-tiny-frames"); 1118c2ecf20Sopenharmony_ciMODULE_PARM_DESC(flowctrl, "Sundance Alta flow control [0|1]"); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* 1148c2ecf20Sopenharmony_ci Theory of Operation 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ciI. Board Compatibility 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ciThis driver is designed for the Sundance Technologies "Alta" ST201 chip. 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ciII. Board-specific settings 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ciIII. Driver operation 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ciIIIa. Ring buffers 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ciThis driver uses two statically allocated fixed-size descriptor lists 1278c2ecf20Sopenharmony_ciformed into rings by a branch from the final descriptor to the beginning of 1288c2ecf20Sopenharmony_cithe list. The ring sizes are set at compile time by RX/TX_RING_SIZE. 1298c2ecf20Sopenharmony_ciSome chips explicitly use only 2^N sized rings, while others use a 1308c2ecf20Sopenharmony_ci'next descriptor' pointer that the driver forms into rings. 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ciIIIb/c. Transmit/Receive Structure 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ciThis driver uses a zero-copy receive and transmit scheme. 1358c2ecf20Sopenharmony_ciThe driver allocates full frame size skbuffs for the Rx ring buffers at 1368c2ecf20Sopenharmony_ciopen() time and passes the skb->data field to the chip as receive data 1378c2ecf20Sopenharmony_cibuffers. When an incoming frame is less than RX_COPYBREAK bytes long, 1388c2ecf20Sopenharmony_cia fresh skbuff is allocated and the frame is copied to the new skbuff. 1398c2ecf20Sopenharmony_ciWhen the incoming frame is larger, the skbuff is passed directly up the 1408c2ecf20Sopenharmony_ciprotocol stack. Buffers consumed this way are replaced by newly allocated 1418c2ecf20Sopenharmony_ciskbuffs in a later phase of receives. 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ciThe RX_COPYBREAK value is chosen to trade-off the memory wasted by 1448c2ecf20Sopenharmony_ciusing a full-sized skbuff for small frames vs. the copying costs of larger 1458c2ecf20Sopenharmony_ciframes. New boards are typically used in generously configured machines 1468c2ecf20Sopenharmony_ciand the underfilled buffers have negligible impact compared to the benefit of 1478c2ecf20Sopenharmony_cia single allocation size, so the default value of zero results in never 1488c2ecf20Sopenharmony_cicopying packets. When copying is done, the cost is usually mitigated by using 1498c2ecf20Sopenharmony_cia combined copy/checksum routine. Copying also preloads the cache, which is 1508c2ecf20Sopenharmony_cimost useful with small frames. 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ciA subtle aspect of the operation is that the IP header at offset 14 in an 1538c2ecf20Sopenharmony_ciethernet frame isn't longword aligned for further processing. 1548c2ecf20Sopenharmony_ciUnaligned buffers are permitted by the Sundance hardware, so 1558c2ecf20Sopenharmony_ciframes are received into the skbuff at an offset of "+2", 16-byte aligning 1568c2ecf20Sopenharmony_cithe IP header. 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ciIIId. Synchronization 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ciThe driver runs as two independent, single-threaded flows of control. One 1618c2ecf20Sopenharmony_ciis the send-packet routine, which enforces single-threaded use by the 1628c2ecf20Sopenharmony_cidev->tbusy flag. The other thread is the interrupt handler, which is single 1638c2ecf20Sopenharmony_cithreaded by the hardware and interrupt handling software. 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ciThe send packet thread has partial control over the Tx ring and 'dev->tbusy' 1668c2ecf20Sopenharmony_ciflag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next 1678c2ecf20Sopenharmony_ciqueue slot is empty, it clears the tbusy flag when finished otherwise it sets 1688c2ecf20Sopenharmony_cithe 'lp->tx_full' flag. 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ciThe interrupt handler has exclusive control over the Rx ring and records stats 1718c2ecf20Sopenharmony_cifrom the Tx ring. After reaping the stats, it marks the Tx queue entry as 1728c2ecf20Sopenharmony_ciempty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it 1738c2ecf20Sopenharmony_ciclears both the tx_full and tbusy flags. 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ciIV. Notes 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ciIVb. References 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ciThe Sundance ST201 datasheet, preliminary version. 1808c2ecf20Sopenharmony_ciThe Kendin KS8723 datasheet, preliminary version. 1818c2ecf20Sopenharmony_ciThe ICplus IP100 datasheet, preliminary version. 1828c2ecf20Sopenharmony_cihttp://www.scyld.com/expert/100mbps.html 1838c2ecf20Sopenharmony_cihttp://www.scyld.com/expert/NWay.html 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ciIVc. Errata 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci*/ 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/* Work-around for Kendin chip bugs. */ 1908c2ecf20Sopenharmony_ci#ifndef CONFIG_SUNDANCE_MMIO 1918c2ecf20Sopenharmony_ci#define USE_IO_OPS 1 1928c2ecf20Sopenharmony_ci#endif 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic const struct pci_device_id sundance_pci_tbl[] = { 1958c2ecf20Sopenharmony_ci { 0x1186, 0x1002, 0x1186, 0x1002, 0, 0, 0 }, 1968c2ecf20Sopenharmony_ci { 0x1186, 0x1002, 0x1186, 0x1003, 0, 0, 1 }, 1978c2ecf20Sopenharmony_ci { 0x1186, 0x1002, 0x1186, 0x1012, 0, 0, 2 }, 1988c2ecf20Sopenharmony_ci { 0x1186, 0x1002, 0x1186, 0x1040, 0, 0, 3 }, 1998c2ecf20Sopenharmony_ci { 0x1186, 0x1002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, 2008c2ecf20Sopenharmony_ci { 0x13F0, 0x0201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5 }, 2018c2ecf20Sopenharmony_ci { 0x13F0, 0x0200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6 }, 2028c2ecf20Sopenharmony_ci { } 2038c2ecf20Sopenharmony_ci}; 2048c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, sundance_pci_tbl); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cienum { 2078c2ecf20Sopenharmony_ci netdev_io_size = 128 2088c2ecf20Sopenharmony_ci}; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistruct pci_id_info { 2118c2ecf20Sopenharmony_ci const char *name; 2128c2ecf20Sopenharmony_ci}; 2138c2ecf20Sopenharmony_cistatic const struct pci_id_info pci_id_tbl[] = { 2148c2ecf20Sopenharmony_ci {"D-Link DFE-550TX FAST Ethernet Adapter"}, 2158c2ecf20Sopenharmony_ci {"D-Link DFE-550FX 100Mbps Fiber-optics Adapter"}, 2168c2ecf20Sopenharmony_ci {"D-Link DFE-580TX 4 port Server Adapter"}, 2178c2ecf20Sopenharmony_ci {"D-Link DFE-530TXS FAST Ethernet Adapter"}, 2188c2ecf20Sopenharmony_ci {"D-Link DL10050-based FAST Ethernet Adapter"}, 2198c2ecf20Sopenharmony_ci {"Sundance Technology Alta"}, 2208c2ecf20Sopenharmony_ci {"IC Plus Corporation IP100A FAST Ethernet Adapter"}, 2218c2ecf20Sopenharmony_ci { } /* terminate list. */ 2228c2ecf20Sopenharmony_ci}; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/* This driver was written to use PCI memory space, however x86-oriented 2258c2ecf20Sopenharmony_ci hardware often uses I/O space accesses. */ 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci/* Offsets to the device registers. 2288c2ecf20Sopenharmony_ci Unlike software-only systems, device drivers interact with complex hardware. 2298c2ecf20Sopenharmony_ci It's not useful to define symbolic names for every register bit in the 2308c2ecf20Sopenharmony_ci device. The name can only partially document the semantics and make 2318c2ecf20Sopenharmony_ci the driver longer and more difficult to read. 2328c2ecf20Sopenharmony_ci In general, only the important configuration values or bits changed 2338c2ecf20Sopenharmony_ci multiple times should be defined symbolically. 2348c2ecf20Sopenharmony_ci*/ 2358c2ecf20Sopenharmony_cienum alta_offsets { 2368c2ecf20Sopenharmony_ci DMACtrl = 0x00, 2378c2ecf20Sopenharmony_ci TxListPtr = 0x04, 2388c2ecf20Sopenharmony_ci TxDMABurstThresh = 0x08, 2398c2ecf20Sopenharmony_ci TxDMAUrgentThresh = 0x09, 2408c2ecf20Sopenharmony_ci TxDMAPollPeriod = 0x0a, 2418c2ecf20Sopenharmony_ci RxDMAStatus = 0x0c, 2428c2ecf20Sopenharmony_ci RxListPtr = 0x10, 2438c2ecf20Sopenharmony_ci DebugCtrl0 = 0x1a, 2448c2ecf20Sopenharmony_ci DebugCtrl1 = 0x1c, 2458c2ecf20Sopenharmony_ci RxDMABurstThresh = 0x14, 2468c2ecf20Sopenharmony_ci RxDMAUrgentThresh = 0x15, 2478c2ecf20Sopenharmony_ci RxDMAPollPeriod = 0x16, 2488c2ecf20Sopenharmony_ci LEDCtrl = 0x1a, 2498c2ecf20Sopenharmony_ci ASICCtrl = 0x30, 2508c2ecf20Sopenharmony_ci EEData = 0x34, 2518c2ecf20Sopenharmony_ci EECtrl = 0x36, 2528c2ecf20Sopenharmony_ci FlashAddr = 0x40, 2538c2ecf20Sopenharmony_ci FlashData = 0x44, 2548c2ecf20Sopenharmony_ci WakeEvent = 0x45, 2558c2ecf20Sopenharmony_ci TxStatus = 0x46, 2568c2ecf20Sopenharmony_ci TxFrameId = 0x47, 2578c2ecf20Sopenharmony_ci DownCounter = 0x18, 2588c2ecf20Sopenharmony_ci IntrClear = 0x4a, 2598c2ecf20Sopenharmony_ci IntrEnable = 0x4c, 2608c2ecf20Sopenharmony_ci IntrStatus = 0x4e, 2618c2ecf20Sopenharmony_ci MACCtrl0 = 0x50, 2628c2ecf20Sopenharmony_ci MACCtrl1 = 0x52, 2638c2ecf20Sopenharmony_ci StationAddr = 0x54, 2648c2ecf20Sopenharmony_ci MaxFrameSize = 0x5A, 2658c2ecf20Sopenharmony_ci RxMode = 0x5c, 2668c2ecf20Sopenharmony_ci MIICtrl = 0x5e, 2678c2ecf20Sopenharmony_ci MulticastFilter0 = 0x60, 2688c2ecf20Sopenharmony_ci MulticastFilter1 = 0x64, 2698c2ecf20Sopenharmony_ci RxOctetsLow = 0x68, 2708c2ecf20Sopenharmony_ci RxOctetsHigh = 0x6a, 2718c2ecf20Sopenharmony_ci TxOctetsLow = 0x6c, 2728c2ecf20Sopenharmony_ci TxOctetsHigh = 0x6e, 2738c2ecf20Sopenharmony_ci TxFramesOK = 0x70, 2748c2ecf20Sopenharmony_ci RxFramesOK = 0x72, 2758c2ecf20Sopenharmony_ci StatsCarrierError = 0x74, 2768c2ecf20Sopenharmony_ci StatsLateColl = 0x75, 2778c2ecf20Sopenharmony_ci StatsMultiColl = 0x76, 2788c2ecf20Sopenharmony_ci StatsOneColl = 0x77, 2798c2ecf20Sopenharmony_ci StatsTxDefer = 0x78, 2808c2ecf20Sopenharmony_ci RxMissed = 0x79, 2818c2ecf20Sopenharmony_ci StatsTxXSDefer = 0x7a, 2828c2ecf20Sopenharmony_ci StatsTxAbort = 0x7b, 2838c2ecf20Sopenharmony_ci StatsBcastTx = 0x7c, 2848c2ecf20Sopenharmony_ci StatsBcastRx = 0x7d, 2858c2ecf20Sopenharmony_ci StatsMcastTx = 0x7e, 2868c2ecf20Sopenharmony_ci StatsMcastRx = 0x7f, 2878c2ecf20Sopenharmony_ci /* Aliased and bogus values! */ 2888c2ecf20Sopenharmony_ci RxStatus = 0x0c, 2898c2ecf20Sopenharmony_ci}; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci#define ASIC_HI_WORD(x) ((x) + 2) 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cienum ASICCtrl_HiWord_bit { 2948c2ecf20Sopenharmony_ci GlobalReset = 0x0001, 2958c2ecf20Sopenharmony_ci RxReset = 0x0002, 2968c2ecf20Sopenharmony_ci TxReset = 0x0004, 2978c2ecf20Sopenharmony_ci DMAReset = 0x0008, 2988c2ecf20Sopenharmony_ci FIFOReset = 0x0010, 2998c2ecf20Sopenharmony_ci NetworkReset = 0x0020, 3008c2ecf20Sopenharmony_ci HostReset = 0x0040, 3018c2ecf20Sopenharmony_ci ResetBusy = 0x0400, 3028c2ecf20Sopenharmony_ci}; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci/* Bits in the interrupt status/mask registers. */ 3058c2ecf20Sopenharmony_cienum intr_status_bits { 3068c2ecf20Sopenharmony_ci IntrSummary=0x0001, IntrPCIErr=0x0002, IntrMACCtrl=0x0008, 3078c2ecf20Sopenharmony_ci IntrTxDone=0x0004, IntrRxDone=0x0010, IntrRxStart=0x0020, 3088c2ecf20Sopenharmony_ci IntrDrvRqst=0x0040, 3098c2ecf20Sopenharmony_ci StatsMax=0x0080, LinkChange=0x0100, 3108c2ecf20Sopenharmony_ci IntrTxDMADone=0x0200, IntrRxDMADone=0x0400, 3118c2ecf20Sopenharmony_ci}; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci/* Bits in the RxMode register. */ 3148c2ecf20Sopenharmony_cienum rx_mode_bits { 3158c2ecf20Sopenharmony_ci AcceptAllIPMulti=0x20, AcceptMultiHash=0x10, AcceptAll=0x08, 3168c2ecf20Sopenharmony_ci AcceptBroadcast=0x04, AcceptMulticast=0x02, AcceptMyPhys=0x01, 3178c2ecf20Sopenharmony_ci}; 3188c2ecf20Sopenharmony_ci/* Bits in MACCtrl. */ 3198c2ecf20Sopenharmony_cienum mac_ctrl0_bits { 3208c2ecf20Sopenharmony_ci EnbFullDuplex=0x20, EnbRcvLargeFrame=0x40, 3218c2ecf20Sopenharmony_ci EnbFlowCtrl=0x100, EnbPassRxCRC=0x200, 3228c2ecf20Sopenharmony_ci}; 3238c2ecf20Sopenharmony_cienum mac_ctrl1_bits { 3248c2ecf20Sopenharmony_ci StatsEnable=0x0020, StatsDisable=0x0040, StatsEnabled=0x0080, 3258c2ecf20Sopenharmony_ci TxEnable=0x0100, TxDisable=0x0200, TxEnabled=0x0400, 3268c2ecf20Sopenharmony_ci RxEnable=0x0800, RxDisable=0x1000, RxEnabled=0x2000, 3278c2ecf20Sopenharmony_ci}; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci/* Bits in WakeEvent register. */ 3308c2ecf20Sopenharmony_cienum wake_event_bits { 3318c2ecf20Sopenharmony_ci WakePktEnable = 0x01, 3328c2ecf20Sopenharmony_ci MagicPktEnable = 0x02, 3338c2ecf20Sopenharmony_ci LinkEventEnable = 0x04, 3348c2ecf20Sopenharmony_ci WolEnable = 0x80, 3358c2ecf20Sopenharmony_ci}; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci/* The Rx and Tx buffer descriptors. */ 3388c2ecf20Sopenharmony_ci/* Note that using only 32 bit fields simplifies conversion to big-endian 3398c2ecf20Sopenharmony_ci architectures. */ 3408c2ecf20Sopenharmony_cistruct netdev_desc { 3418c2ecf20Sopenharmony_ci __le32 next_desc; 3428c2ecf20Sopenharmony_ci __le32 status; 3438c2ecf20Sopenharmony_ci struct desc_frag { __le32 addr, length; } frag[1]; 3448c2ecf20Sopenharmony_ci}; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci/* Bits in netdev_desc.status */ 3478c2ecf20Sopenharmony_cienum desc_status_bits { 3488c2ecf20Sopenharmony_ci DescOwn=0x8000, 3498c2ecf20Sopenharmony_ci DescEndPacket=0x4000, 3508c2ecf20Sopenharmony_ci DescEndRing=0x2000, 3518c2ecf20Sopenharmony_ci LastFrag=0x80000000, 3528c2ecf20Sopenharmony_ci DescIntrOnTx=0x8000, 3538c2ecf20Sopenharmony_ci DescIntrOnDMADone=0x80000000, 3548c2ecf20Sopenharmony_ci DisableAlign = 0x00000001, 3558c2ecf20Sopenharmony_ci}; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci#define PRIV_ALIGN 15 /* Required alignment mask */ 3588c2ecf20Sopenharmony_ci/* Use __attribute__((aligned (L1_CACHE_BYTES))) to maintain alignment 3598c2ecf20Sopenharmony_ci within the structure. */ 3608c2ecf20Sopenharmony_ci#define MII_CNT 4 3618c2ecf20Sopenharmony_cistruct netdev_private { 3628c2ecf20Sopenharmony_ci /* Descriptor rings first for alignment. */ 3638c2ecf20Sopenharmony_ci struct netdev_desc *rx_ring; 3648c2ecf20Sopenharmony_ci struct netdev_desc *tx_ring; 3658c2ecf20Sopenharmony_ci struct sk_buff* rx_skbuff[RX_RING_SIZE]; 3668c2ecf20Sopenharmony_ci struct sk_buff* tx_skbuff[TX_RING_SIZE]; 3678c2ecf20Sopenharmony_ci dma_addr_t tx_ring_dma; 3688c2ecf20Sopenharmony_ci dma_addr_t rx_ring_dma; 3698c2ecf20Sopenharmony_ci struct timer_list timer; /* Media monitoring timer. */ 3708c2ecf20Sopenharmony_ci struct net_device *ndev; /* backpointer */ 3718c2ecf20Sopenharmony_ci /* ethtool extra stats */ 3728c2ecf20Sopenharmony_ci struct { 3738c2ecf20Sopenharmony_ci u64 tx_multiple_collisions; 3748c2ecf20Sopenharmony_ci u64 tx_single_collisions; 3758c2ecf20Sopenharmony_ci u64 tx_late_collisions; 3768c2ecf20Sopenharmony_ci u64 tx_deferred; 3778c2ecf20Sopenharmony_ci u64 tx_deferred_excessive; 3788c2ecf20Sopenharmony_ci u64 tx_aborted; 3798c2ecf20Sopenharmony_ci u64 tx_bcasts; 3808c2ecf20Sopenharmony_ci u64 rx_bcasts; 3818c2ecf20Sopenharmony_ci u64 tx_mcasts; 3828c2ecf20Sopenharmony_ci u64 rx_mcasts; 3838c2ecf20Sopenharmony_ci } xstats; 3848c2ecf20Sopenharmony_ci /* Frequently used values: keep some adjacent for cache effect. */ 3858c2ecf20Sopenharmony_ci spinlock_t lock; 3868c2ecf20Sopenharmony_ci int msg_enable; 3878c2ecf20Sopenharmony_ci int chip_id; 3888c2ecf20Sopenharmony_ci unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */ 3898c2ecf20Sopenharmony_ci unsigned int rx_buf_sz; /* Based on MTU+slack. */ 3908c2ecf20Sopenharmony_ci struct netdev_desc *last_tx; /* Last Tx descriptor used. */ 3918c2ecf20Sopenharmony_ci unsigned int cur_tx, dirty_tx; 3928c2ecf20Sopenharmony_ci /* These values are keep track of the transceiver/media in use. */ 3938c2ecf20Sopenharmony_ci unsigned int flowctrl:1; 3948c2ecf20Sopenharmony_ci unsigned int default_port:4; /* Last dev->if_port value. */ 3958c2ecf20Sopenharmony_ci unsigned int an_enable:1; 3968c2ecf20Sopenharmony_ci unsigned int speed; 3978c2ecf20Sopenharmony_ci unsigned int wol_enabled:1; /* Wake on LAN enabled */ 3988c2ecf20Sopenharmony_ci struct tasklet_struct rx_tasklet; 3998c2ecf20Sopenharmony_ci struct tasklet_struct tx_tasklet; 4008c2ecf20Sopenharmony_ci int budget; 4018c2ecf20Sopenharmony_ci int cur_task; 4028c2ecf20Sopenharmony_ci /* Multicast and receive mode. */ 4038c2ecf20Sopenharmony_ci spinlock_t mcastlock; /* SMP lock multicast updates. */ 4048c2ecf20Sopenharmony_ci u16 mcast_filter[4]; 4058c2ecf20Sopenharmony_ci /* MII transceiver section. */ 4068c2ecf20Sopenharmony_ci struct mii_if_info mii_if; 4078c2ecf20Sopenharmony_ci int mii_preamble_required; 4088c2ecf20Sopenharmony_ci unsigned char phys[MII_CNT]; /* MII device addresses, only first one used. */ 4098c2ecf20Sopenharmony_ci struct pci_dev *pci_dev; 4108c2ecf20Sopenharmony_ci void __iomem *base; 4118c2ecf20Sopenharmony_ci spinlock_t statlock; 4128c2ecf20Sopenharmony_ci}; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci/* The station address location in the EEPROM. */ 4158c2ecf20Sopenharmony_ci#define EEPROM_SA_OFFSET 0x10 4168c2ecf20Sopenharmony_ci#define DEFAULT_INTR (IntrRxDMADone | IntrPCIErr | \ 4178c2ecf20Sopenharmony_ci IntrDrvRqst | IntrTxDone | StatsMax | \ 4188c2ecf20Sopenharmony_ci LinkChange) 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic int change_mtu(struct net_device *dev, int new_mtu); 4218c2ecf20Sopenharmony_cistatic int eeprom_read(void __iomem *ioaddr, int location); 4228c2ecf20Sopenharmony_cistatic int mdio_read(struct net_device *dev, int phy_id, int location); 4238c2ecf20Sopenharmony_cistatic void mdio_write(struct net_device *dev, int phy_id, int location, int value); 4248c2ecf20Sopenharmony_cistatic int mdio_wait_link(struct net_device *dev, int wait); 4258c2ecf20Sopenharmony_cistatic int netdev_open(struct net_device *dev); 4268c2ecf20Sopenharmony_cistatic void check_duplex(struct net_device *dev); 4278c2ecf20Sopenharmony_cistatic void netdev_timer(struct timer_list *t); 4288c2ecf20Sopenharmony_cistatic void tx_timeout(struct net_device *dev, unsigned int txqueue); 4298c2ecf20Sopenharmony_cistatic void init_ring(struct net_device *dev); 4308c2ecf20Sopenharmony_cistatic netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev); 4318c2ecf20Sopenharmony_cistatic int reset_tx (struct net_device *dev); 4328c2ecf20Sopenharmony_cistatic irqreturn_t intr_handler(int irq, void *dev_instance); 4338c2ecf20Sopenharmony_cistatic void rx_poll(struct tasklet_struct *t); 4348c2ecf20Sopenharmony_cistatic void tx_poll(struct tasklet_struct *t); 4358c2ecf20Sopenharmony_cistatic void refill_rx (struct net_device *dev); 4368c2ecf20Sopenharmony_cistatic void netdev_error(struct net_device *dev, int intr_status); 4378c2ecf20Sopenharmony_cistatic void netdev_error(struct net_device *dev, int intr_status); 4388c2ecf20Sopenharmony_cistatic void set_rx_mode(struct net_device *dev); 4398c2ecf20Sopenharmony_cistatic int __set_mac_addr(struct net_device *dev); 4408c2ecf20Sopenharmony_cistatic int sundance_set_mac_addr(struct net_device *dev, void *data); 4418c2ecf20Sopenharmony_cistatic struct net_device_stats *get_stats(struct net_device *dev); 4428c2ecf20Sopenharmony_cistatic int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); 4438c2ecf20Sopenharmony_cistatic int netdev_close(struct net_device *dev); 4448c2ecf20Sopenharmony_cistatic const struct ethtool_ops ethtool_ops; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic void sundance_reset(struct net_device *dev, unsigned long reset_cmd) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 4498c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->base + ASICCtrl; 4508c2ecf20Sopenharmony_ci int countdown; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* ST201 documentation states ASICCtrl is a 32bit register */ 4538c2ecf20Sopenharmony_ci iowrite32 (reset_cmd | ioread32 (ioaddr), ioaddr); 4548c2ecf20Sopenharmony_ci /* ST201 documentation states reset can take up to 1 ms */ 4558c2ecf20Sopenharmony_ci countdown = 10 + 1; 4568c2ecf20Sopenharmony_ci while (ioread32 (ioaddr) & (ResetBusy << 16)) { 4578c2ecf20Sopenharmony_ci if (--countdown == 0) { 4588c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s : reset not completed !!\n", dev->name); 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci udelay(100); 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 4668c2ecf20Sopenharmony_cistatic void sundance_poll_controller(struct net_device *dev) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci disable_irq(np->pci_dev->irq); 4718c2ecf20Sopenharmony_ci intr_handler(np->pci_dev->irq, dev); 4728c2ecf20Sopenharmony_ci enable_irq(np->pci_dev->irq); 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci#endif 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic const struct net_device_ops netdev_ops = { 4778c2ecf20Sopenharmony_ci .ndo_open = netdev_open, 4788c2ecf20Sopenharmony_ci .ndo_stop = netdev_close, 4798c2ecf20Sopenharmony_ci .ndo_start_xmit = start_tx, 4808c2ecf20Sopenharmony_ci .ndo_get_stats = get_stats, 4818c2ecf20Sopenharmony_ci .ndo_set_rx_mode = set_rx_mode, 4828c2ecf20Sopenharmony_ci .ndo_do_ioctl = netdev_ioctl, 4838c2ecf20Sopenharmony_ci .ndo_tx_timeout = tx_timeout, 4848c2ecf20Sopenharmony_ci .ndo_change_mtu = change_mtu, 4858c2ecf20Sopenharmony_ci .ndo_set_mac_address = sundance_set_mac_addr, 4868c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 4878c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 4888c2ecf20Sopenharmony_ci .ndo_poll_controller = sundance_poll_controller, 4898c2ecf20Sopenharmony_ci#endif 4908c2ecf20Sopenharmony_ci}; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic int sundance_probe1(struct pci_dev *pdev, 4938c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci struct net_device *dev; 4968c2ecf20Sopenharmony_ci struct netdev_private *np; 4978c2ecf20Sopenharmony_ci static int card_idx; 4988c2ecf20Sopenharmony_ci int chip_idx = ent->driver_data; 4998c2ecf20Sopenharmony_ci int irq; 5008c2ecf20Sopenharmony_ci int i; 5018c2ecf20Sopenharmony_ci void __iomem *ioaddr; 5028c2ecf20Sopenharmony_ci u16 mii_ctl; 5038c2ecf20Sopenharmony_ci void *ring_space; 5048c2ecf20Sopenharmony_ci dma_addr_t ring_dma; 5058c2ecf20Sopenharmony_ci#ifdef USE_IO_OPS 5068c2ecf20Sopenharmony_ci int bar = 0; 5078c2ecf20Sopenharmony_ci#else 5088c2ecf20Sopenharmony_ci int bar = 1; 5098c2ecf20Sopenharmony_ci#endif 5108c2ecf20Sopenharmony_ci int phy, phy_end, phy_idx = 0; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (pci_enable_device(pdev)) 5138c2ecf20Sopenharmony_ci return -EIO; 5148c2ecf20Sopenharmony_ci pci_set_master(pdev); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci irq = pdev->irq; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(*np)); 5198c2ecf20Sopenharmony_ci if (!dev) 5208c2ecf20Sopenharmony_ci return -ENOMEM; 5218c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (pci_request_regions(pdev, DRV_NAME)) 5248c2ecf20Sopenharmony_ci goto err_out_netdev; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci ioaddr = pci_iomap(pdev, bar, netdev_io_size); 5278c2ecf20Sopenharmony_ci if (!ioaddr) 5288c2ecf20Sopenharmony_ci goto err_out_res; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) 5318c2ecf20Sopenharmony_ci ((__le16 *)dev->dev_addr)[i] = 5328c2ecf20Sopenharmony_ci cpu_to_le16(eeprom_read(ioaddr, i + EEPROM_SA_OFFSET)); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci np = netdev_priv(dev); 5358c2ecf20Sopenharmony_ci np->ndev = dev; 5368c2ecf20Sopenharmony_ci np->base = ioaddr; 5378c2ecf20Sopenharmony_ci np->pci_dev = pdev; 5388c2ecf20Sopenharmony_ci np->chip_id = chip_idx; 5398c2ecf20Sopenharmony_ci np->msg_enable = (1 << debug) - 1; 5408c2ecf20Sopenharmony_ci spin_lock_init(&np->lock); 5418c2ecf20Sopenharmony_ci spin_lock_init(&np->statlock); 5428c2ecf20Sopenharmony_ci tasklet_setup(&np->rx_tasklet, rx_poll); 5438c2ecf20Sopenharmony_ci tasklet_setup(&np->tx_tasklet, tx_poll); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci ring_space = dma_alloc_coherent(&pdev->dev, TX_TOTAL_SIZE, 5468c2ecf20Sopenharmony_ci &ring_dma, GFP_KERNEL); 5478c2ecf20Sopenharmony_ci if (!ring_space) 5488c2ecf20Sopenharmony_ci goto err_out_cleardev; 5498c2ecf20Sopenharmony_ci np->tx_ring = (struct netdev_desc *)ring_space; 5508c2ecf20Sopenharmony_ci np->tx_ring_dma = ring_dma; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci ring_space = dma_alloc_coherent(&pdev->dev, RX_TOTAL_SIZE, 5538c2ecf20Sopenharmony_ci &ring_dma, GFP_KERNEL); 5548c2ecf20Sopenharmony_ci if (!ring_space) 5558c2ecf20Sopenharmony_ci goto err_out_unmap_tx; 5568c2ecf20Sopenharmony_ci np->rx_ring = (struct netdev_desc *)ring_space; 5578c2ecf20Sopenharmony_ci np->rx_ring_dma = ring_dma; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci np->mii_if.dev = dev; 5608c2ecf20Sopenharmony_ci np->mii_if.mdio_read = mdio_read; 5618c2ecf20Sopenharmony_ci np->mii_if.mdio_write = mdio_write; 5628c2ecf20Sopenharmony_ci np->mii_if.phy_id_mask = 0x1f; 5638c2ecf20Sopenharmony_ci np->mii_if.reg_num_mask = 0x1f; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* The chip-specific entries in the device structure. */ 5668c2ecf20Sopenharmony_ci dev->netdev_ops = &netdev_ops; 5678c2ecf20Sopenharmony_ci dev->ethtool_ops = ðtool_ops; 5688c2ecf20Sopenharmony_ci dev->watchdog_timeo = TX_TIMEOUT; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci /* MTU range: 68 - 8191 */ 5718c2ecf20Sopenharmony_ci dev->min_mtu = ETH_MIN_MTU; 5728c2ecf20Sopenharmony_ci dev->max_mtu = 8191; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, dev); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci i = register_netdev(dev); 5778c2ecf20Sopenharmony_ci if (i) 5788c2ecf20Sopenharmony_ci goto err_out_unmap_rx; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: %s at %p, %pM, IRQ %d.\n", 5818c2ecf20Sopenharmony_ci dev->name, pci_id_tbl[chip_idx].name, ioaddr, 5828c2ecf20Sopenharmony_ci dev->dev_addr, irq); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci np->phys[0] = 1; /* Default setting */ 5858c2ecf20Sopenharmony_ci np->mii_preamble_required++; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* 5888c2ecf20Sopenharmony_ci * It seems some phys doesn't deal well with address 0 being accessed 5898c2ecf20Sopenharmony_ci * first 5908c2ecf20Sopenharmony_ci */ 5918c2ecf20Sopenharmony_ci if (sundance_pci_tbl[np->chip_id].device == 0x0200) { 5928c2ecf20Sopenharmony_ci phy = 0; 5938c2ecf20Sopenharmony_ci phy_end = 31; 5948c2ecf20Sopenharmony_ci } else { 5958c2ecf20Sopenharmony_ci phy = 1; 5968c2ecf20Sopenharmony_ci phy_end = 32; /* wraps to zero, due to 'phy & 0x1f' */ 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci for (; phy <= phy_end && phy_idx < MII_CNT; phy++) { 5998c2ecf20Sopenharmony_ci int phyx = phy & 0x1f; 6008c2ecf20Sopenharmony_ci int mii_status = mdio_read(dev, phyx, MII_BMSR); 6018c2ecf20Sopenharmony_ci if (mii_status != 0xffff && mii_status != 0x0000) { 6028c2ecf20Sopenharmony_ci np->phys[phy_idx++] = phyx; 6038c2ecf20Sopenharmony_ci np->mii_if.advertising = mdio_read(dev, phyx, MII_ADVERTISE); 6048c2ecf20Sopenharmony_ci if ((mii_status & 0x0040) == 0) 6058c2ecf20Sopenharmony_ci np->mii_preamble_required++; 6068c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: MII PHY found at address %d, status " 6078c2ecf20Sopenharmony_ci "0x%4.4x advertising %4.4x.\n", 6088c2ecf20Sopenharmony_ci dev->name, phyx, mii_status, np->mii_if.advertising); 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci np->mii_preamble_required--; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (phy_idx == 0) { 6148c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: No MII transceiver found, aborting. ASIC status %x\n", 6158c2ecf20Sopenharmony_ci dev->name, ioread32(ioaddr + ASICCtrl)); 6168c2ecf20Sopenharmony_ci goto err_out_unregister; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci np->mii_if.phy_id = np->phys[0]; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci /* Parse override configuration */ 6228c2ecf20Sopenharmony_ci np->an_enable = 1; 6238c2ecf20Sopenharmony_ci if (card_idx < MAX_UNITS) { 6248c2ecf20Sopenharmony_ci if (media[card_idx] != NULL) { 6258c2ecf20Sopenharmony_ci np->an_enable = 0; 6268c2ecf20Sopenharmony_ci if (strcmp (media[card_idx], "100mbps_fd") == 0 || 6278c2ecf20Sopenharmony_ci strcmp (media[card_idx], "4") == 0) { 6288c2ecf20Sopenharmony_ci np->speed = 100; 6298c2ecf20Sopenharmony_ci np->mii_if.full_duplex = 1; 6308c2ecf20Sopenharmony_ci } else if (strcmp (media[card_idx], "100mbps_hd") == 0 || 6318c2ecf20Sopenharmony_ci strcmp (media[card_idx], "3") == 0) { 6328c2ecf20Sopenharmony_ci np->speed = 100; 6338c2ecf20Sopenharmony_ci np->mii_if.full_duplex = 0; 6348c2ecf20Sopenharmony_ci } else if (strcmp (media[card_idx], "10mbps_fd") == 0 || 6358c2ecf20Sopenharmony_ci strcmp (media[card_idx], "2") == 0) { 6368c2ecf20Sopenharmony_ci np->speed = 10; 6378c2ecf20Sopenharmony_ci np->mii_if.full_duplex = 1; 6388c2ecf20Sopenharmony_ci } else if (strcmp (media[card_idx], "10mbps_hd") == 0 || 6398c2ecf20Sopenharmony_ci strcmp (media[card_idx], "1") == 0) { 6408c2ecf20Sopenharmony_ci np->speed = 10; 6418c2ecf20Sopenharmony_ci np->mii_if.full_duplex = 0; 6428c2ecf20Sopenharmony_ci } else { 6438c2ecf20Sopenharmony_ci np->an_enable = 1; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci if (flowctrl == 1) 6478c2ecf20Sopenharmony_ci np->flowctrl = 1; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci /* Fibre PHY? */ 6518c2ecf20Sopenharmony_ci if (ioread32 (ioaddr + ASICCtrl) & 0x80) { 6528c2ecf20Sopenharmony_ci /* Default 100Mbps Full */ 6538c2ecf20Sopenharmony_ci if (np->an_enable) { 6548c2ecf20Sopenharmony_ci np->speed = 100; 6558c2ecf20Sopenharmony_ci np->mii_if.full_duplex = 1; 6568c2ecf20Sopenharmony_ci np->an_enable = 0; 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci /* Reset PHY */ 6608c2ecf20Sopenharmony_ci mdio_write (dev, np->phys[0], MII_BMCR, BMCR_RESET); 6618c2ecf20Sopenharmony_ci mdelay (300); 6628c2ecf20Sopenharmony_ci /* If flow control enabled, we need to advertise it.*/ 6638c2ecf20Sopenharmony_ci if (np->flowctrl) 6648c2ecf20Sopenharmony_ci mdio_write (dev, np->phys[0], MII_ADVERTISE, np->mii_if.advertising | 0x0400); 6658c2ecf20Sopenharmony_ci mdio_write (dev, np->phys[0], MII_BMCR, BMCR_ANENABLE|BMCR_ANRESTART); 6668c2ecf20Sopenharmony_ci /* Force media type */ 6678c2ecf20Sopenharmony_ci if (!np->an_enable) { 6688c2ecf20Sopenharmony_ci mii_ctl = 0; 6698c2ecf20Sopenharmony_ci mii_ctl |= (np->speed == 100) ? BMCR_SPEED100 : 0; 6708c2ecf20Sopenharmony_ci mii_ctl |= (np->mii_if.full_duplex) ? BMCR_FULLDPLX : 0; 6718c2ecf20Sopenharmony_ci mdio_write (dev, np->phys[0], MII_BMCR, mii_ctl); 6728c2ecf20Sopenharmony_ci printk (KERN_INFO "Override speed=%d, %s duplex\n", 6738c2ecf20Sopenharmony_ci np->speed, np->mii_if.full_duplex ? "Full" : "Half"); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci /* Perhaps move the reset here? */ 6788c2ecf20Sopenharmony_ci /* Reset the chip to erase previous misconfiguration. */ 6798c2ecf20Sopenharmony_ci if (netif_msg_hw(np)) 6808c2ecf20Sopenharmony_ci printk("ASIC Control is %x.\n", ioread32(ioaddr + ASICCtrl)); 6818c2ecf20Sopenharmony_ci sundance_reset(dev, 0x00ff << 16); 6828c2ecf20Sopenharmony_ci if (netif_msg_hw(np)) 6838c2ecf20Sopenharmony_ci printk("ASIC Control is now %x.\n", ioread32(ioaddr + ASICCtrl)); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci card_idx++; 6868c2ecf20Sopenharmony_ci return 0; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_cierr_out_unregister: 6898c2ecf20Sopenharmony_ci unregister_netdev(dev); 6908c2ecf20Sopenharmony_cierr_out_unmap_rx: 6918c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, 6928c2ecf20Sopenharmony_ci np->rx_ring, np->rx_ring_dma); 6938c2ecf20Sopenharmony_cierr_out_unmap_tx: 6948c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, 6958c2ecf20Sopenharmony_ci np->tx_ring, np->tx_ring_dma); 6968c2ecf20Sopenharmony_cierr_out_cleardev: 6978c2ecf20Sopenharmony_ci pci_iounmap(pdev, ioaddr); 6988c2ecf20Sopenharmony_cierr_out_res: 6998c2ecf20Sopenharmony_ci pci_release_regions(pdev); 7008c2ecf20Sopenharmony_cierr_out_netdev: 7018c2ecf20Sopenharmony_ci free_netdev (dev); 7028c2ecf20Sopenharmony_ci return -ENODEV; 7038c2ecf20Sopenharmony_ci} 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_cistatic int change_mtu(struct net_device *dev, int new_mtu) 7068c2ecf20Sopenharmony_ci{ 7078c2ecf20Sopenharmony_ci if (netif_running(dev)) 7088c2ecf20Sopenharmony_ci return -EBUSY; 7098c2ecf20Sopenharmony_ci dev->mtu = new_mtu; 7108c2ecf20Sopenharmony_ci return 0; 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci#define eeprom_delay(ee_addr) ioread32(ee_addr) 7148c2ecf20Sopenharmony_ci/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. */ 7158c2ecf20Sopenharmony_cistatic int eeprom_read(void __iomem *ioaddr, int location) 7168c2ecf20Sopenharmony_ci{ 7178c2ecf20Sopenharmony_ci int boguscnt = 10000; /* Typical 1900 ticks. */ 7188c2ecf20Sopenharmony_ci iowrite16(0x0200 | (location & 0xff), ioaddr + EECtrl); 7198c2ecf20Sopenharmony_ci do { 7208c2ecf20Sopenharmony_ci eeprom_delay(ioaddr + EECtrl); 7218c2ecf20Sopenharmony_ci if (! (ioread16(ioaddr + EECtrl) & 0x8000)) { 7228c2ecf20Sopenharmony_ci return ioread16(ioaddr + EEData); 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci } while (--boguscnt > 0); 7258c2ecf20Sopenharmony_ci return 0; 7268c2ecf20Sopenharmony_ci} 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci/* MII transceiver control section. 7298c2ecf20Sopenharmony_ci Read and write the MII registers using software-generated serial 7308c2ecf20Sopenharmony_ci MDIO protocol. See the MII specifications or DP83840A data sheet 7318c2ecf20Sopenharmony_ci for details. 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci The maximum data clock rate is 2.5 Mhz. The minimum timing is usually 7348c2ecf20Sopenharmony_ci met by back-to-back 33Mhz PCI cycles. */ 7358c2ecf20Sopenharmony_ci#define mdio_delay() ioread8(mdio_addr) 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_cienum mii_reg_bits { 7388c2ecf20Sopenharmony_ci MDIO_ShiftClk=0x0001, MDIO_Data=0x0002, MDIO_EnbOutput=0x0004, 7398c2ecf20Sopenharmony_ci}; 7408c2ecf20Sopenharmony_ci#define MDIO_EnbIn (0) 7418c2ecf20Sopenharmony_ci#define MDIO_WRITE0 (MDIO_EnbOutput) 7428c2ecf20Sopenharmony_ci#define MDIO_WRITE1 (MDIO_Data | MDIO_EnbOutput) 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci/* Generate the preamble required for initial synchronization and 7458c2ecf20Sopenharmony_ci a few older transceivers. */ 7468c2ecf20Sopenharmony_cistatic void mdio_sync(void __iomem *mdio_addr) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci int bits = 32; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci /* Establish sync by sending at least 32 logic ones. */ 7518c2ecf20Sopenharmony_ci while (--bits >= 0) { 7528c2ecf20Sopenharmony_ci iowrite8(MDIO_WRITE1, mdio_addr); 7538c2ecf20Sopenharmony_ci mdio_delay(); 7548c2ecf20Sopenharmony_ci iowrite8(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr); 7558c2ecf20Sopenharmony_ci mdio_delay(); 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_cistatic int mdio_read(struct net_device *dev, int phy_id, int location) 7608c2ecf20Sopenharmony_ci{ 7618c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 7628c2ecf20Sopenharmony_ci void __iomem *mdio_addr = np->base + MIICtrl; 7638c2ecf20Sopenharmony_ci int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; 7648c2ecf20Sopenharmony_ci int i, retval = 0; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci if (np->mii_preamble_required) 7678c2ecf20Sopenharmony_ci mdio_sync(mdio_addr); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci /* Shift the read command bits out. */ 7708c2ecf20Sopenharmony_ci for (i = 15; i >= 0; i--) { 7718c2ecf20Sopenharmony_ci int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci iowrite8(dataval, mdio_addr); 7748c2ecf20Sopenharmony_ci mdio_delay(); 7758c2ecf20Sopenharmony_ci iowrite8(dataval | MDIO_ShiftClk, mdio_addr); 7768c2ecf20Sopenharmony_ci mdio_delay(); 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci /* Read the two transition, 16 data, and wire-idle bits. */ 7798c2ecf20Sopenharmony_ci for (i = 19; i > 0; i--) { 7808c2ecf20Sopenharmony_ci iowrite8(MDIO_EnbIn, mdio_addr); 7818c2ecf20Sopenharmony_ci mdio_delay(); 7828c2ecf20Sopenharmony_ci retval = (retval << 1) | ((ioread8(mdio_addr) & MDIO_Data) ? 1 : 0); 7838c2ecf20Sopenharmony_ci iowrite8(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); 7848c2ecf20Sopenharmony_ci mdio_delay(); 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci return (retval>>1) & 0xffff; 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_cistatic void mdio_write(struct net_device *dev, int phy_id, int location, int value) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 7928c2ecf20Sopenharmony_ci void __iomem *mdio_addr = np->base + MIICtrl; 7938c2ecf20Sopenharmony_ci int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; 7948c2ecf20Sopenharmony_ci int i; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci if (np->mii_preamble_required) 7978c2ecf20Sopenharmony_ci mdio_sync(mdio_addr); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci /* Shift the command bits out. */ 8008c2ecf20Sopenharmony_ci for (i = 31; i >= 0; i--) { 8018c2ecf20Sopenharmony_ci int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci iowrite8(dataval, mdio_addr); 8048c2ecf20Sopenharmony_ci mdio_delay(); 8058c2ecf20Sopenharmony_ci iowrite8(dataval | MDIO_ShiftClk, mdio_addr); 8068c2ecf20Sopenharmony_ci mdio_delay(); 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci /* Clear out extra bits. */ 8098c2ecf20Sopenharmony_ci for (i = 2; i > 0; i--) { 8108c2ecf20Sopenharmony_ci iowrite8(MDIO_EnbIn, mdio_addr); 8118c2ecf20Sopenharmony_ci mdio_delay(); 8128c2ecf20Sopenharmony_ci iowrite8(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); 8138c2ecf20Sopenharmony_ci mdio_delay(); 8148c2ecf20Sopenharmony_ci } 8158c2ecf20Sopenharmony_ci} 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_cistatic int mdio_wait_link(struct net_device *dev, int wait) 8188c2ecf20Sopenharmony_ci{ 8198c2ecf20Sopenharmony_ci int bmsr; 8208c2ecf20Sopenharmony_ci int phy_id; 8218c2ecf20Sopenharmony_ci struct netdev_private *np; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci np = netdev_priv(dev); 8248c2ecf20Sopenharmony_ci phy_id = np->phys[0]; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci do { 8278c2ecf20Sopenharmony_ci bmsr = mdio_read(dev, phy_id, MII_BMSR); 8288c2ecf20Sopenharmony_ci if (bmsr & 0x0004) 8298c2ecf20Sopenharmony_ci return 0; 8308c2ecf20Sopenharmony_ci mdelay(1); 8318c2ecf20Sopenharmony_ci } while (--wait > 0); 8328c2ecf20Sopenharmony_ci return -1; 8338c2ecf20Sopenharmony_ci} 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_cistatic int netdev_open(struct net_device *dev) 8368c2ecf20Sopenharmony_ci{ 8378c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 8388c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->base; 8398c2ecf20Sopenharmony_ci const int irq = np->pci_dev->irq; 8408c2ecf20Sopenharmony_ci unsigned long flags; 8418c2ecf20Sopenharmony_ci int i; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci sundance_reset(dev, 0x00ff << 16); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci i = request_irq(irq, intr_handler, IRQF_SHARED, dev->name, dev); 8468c2ecf20Sopenharmony_ci if (i) 8478c2ecf20Sopenharmony_ci return i; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci if (netif_msg_ifup(np)) 8508c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: netdev_open() irq %d\n", dev->name, irq); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci init_ring(dev); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci iowrite32(np->rx_ring_dma, ioaddr + RxListPtr); 8558c2ecf20Sopenharmony_ci /* The Tx list pointer is written as packets are queued. */ 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci /* Initialize other registers. */ 8588c2ecf20Sopenharmony_ci __set_mac_addr(dev); 8598c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_VLAN_8021Q) 8608c2ecf20Sopenharmony_ci iowrite16(dev->mtu + 18, ioaddr + MaxFrameSize); 8618c2ecf20Sopenharmony_ci#else 8628c2ecf20Sopenharmony_ci iowrite16(dev->mtu + 14, ioaddr + MaxFrameSize); 8638c2ecf20Sopenharmony_ci#endif 8648c2ecf20Sopenharmony_ci if (dev->mtu > 2047) 8658c2ecf20Sopenharmony_ci iowrite32(ioread32(ioaddr + ASICCtrl) | 0x0C, ioaddr + ASICCtrl); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci /* Configure the PCI bus bursts and FIFO thresholds. */ 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (dev->if_port == 0) 8708c2ecf20Sopenharmony_ci dev->if_port = np->default_port; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci spin_lock_init(&np->mcastlock); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci set_rx_mode(dev); 8758c2ecf20Sopenharmony_ci iowrite16(0, ioaddr + IntrEnable); 8768c2ecf20Sopenharmony_ci iowrite16(0, ioaddr + DownCounter); 8778c2ecf20Sopenharmony_ci /* Set the chip to poll every N*320nsec. */ 8788c2ecf20Sopenharmony_ci iowrite8(100, ioaddr + RxDMAPollPeriod); 8798c2ecf20Sopenharmony_ci iowrite8(127, ioaddr + TxDMAPollPeriod); 8808c2ecf20Sopenharmony_ci /* Fix DFE-580TX packet drop issue */ 8818c2ecf20Sopenharmony_ci if (np->pci_dev->revision >= 0x14) 8828c2ecf20Sopenharmony_ci iowrite8(0x01, ioaddr + DebugCtrl1); 8838c2ecf20Sopenharmony_ci netif_start_queue(dev); 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 8868c2ecf20Sopenharmony_ci reset_tx(dev); 8878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci iowrite16 (StatsEnable | RxEnable | TxEnable, ioaddr + MACCtrl1); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci /* Disable Wol */ 8928c2ecf20Sopenharmony_ci iowrite8(ioread8(ioaddr + WakeEvent) | 0x00, ioaddr + WakeEvent); 8938c2ecf20Sopenharmony_ci np->wol_enabled = 0; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci if (netif_msg_ifup(np)) 8968c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Done netdev_open(), status: Rx %x Tx %x " 8978c2ecf20Sopenharmony_ci "MAC Control %x, %4.4x %4.4x.\n", 8988c2ecf20Sopenharmony_ci dev->name, ioread32(ioaddr + RxStatus), ioread8(ioaddr + TxStatus), 8998c2ecf20Sopenharmony_ci ioread32(ioaddr + MACCtrl0), 9008c2ecf20Sopenharmony_ci ioread16(ioaddr + MACCtrl1), ioread16(ioaddr + MACCtrl0)); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci /* Set the timer to check for link beat. */ 9038c2ecf20Sopenharmony_ci timer_setup(&np->timer, netdev_timer, 0); 9048c2ecf20Sopenharmony_ci np->timer.expires = jiffies + 3*HZ; 9058c2ecf20Sopenharmony_ci add_timer(&np->timer); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci /* Enable interrupts by setting the interrupt mask. */ 9088c2ecf20Sopenharmony_ci iowrite16(DEFAULT_INTR, ioaddr + IntrEnable); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci return 0; 9118c2ecf20Sopenharmony_ci} 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_cistatic void check_duplex(struct net_device *dev) 9148c2ecf20Sopenharmony_ci{ 9158c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 9168c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->base; 9178c2ecf20Sopenharmony_ci int mii_lpa = mdio_read(dev, np->phys[0], MII_LPA); 9188c2ecf20Sopenharmony_ci int negotiated = mii_lpa & np->mii_if.advertising; 9198c2ecf20Sopenharmony_ci int duplex; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci /* Force media */ 9228c2ecf20Sopenharmony_ci if (!np->an_enable || mii_lpa == 0xffff) { 9238c2ecf20Sopenharmony_ci if (np->mii_if.full_duplex) 9248c2ecf20Sopenharmony_ci iowrite16 (ioread16 (ioaddr + MACCtrl0) | EnbFullDuplex, 9258c2ecf20Sopenharmony_ci ioaddr + MACCtrl0); 9268c2ecf20Sopenharmony_ci return; 9278c2ecf20Sopenharmony_ci } 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci /* Autonegotiation */ 9308c2ecf20Sopenharmony_ci duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040; 9318c2ecf20Sopenharmony_ci if (np->mii_if.full_duplex != duplex) { 9328c2ecf20Sopenharmony_ci np->mii_if.full_duplex = duplex; 9338c2ecf20Sopenharmony_ci if (netif_msg_link(np)) 9348c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d " 9358c2ecf20Sopenharmony_ci "negotiated capability %4.4x.\n", dev->name, 9368c2ecf20Sopenharmony_ci duplex ? "full" : "half", np->phys[0], negotiated); 9378c2ecf20Sopenharmony_ci iowrite16(ioread16(ioaddr + MACCtrl0) | (duplex ? 0x20 : 0), ioaddr + MACCtrl0); 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci} 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_cistatic void netdev_timer(struct timer_list *t) 9428c2ecf20Sopenharmony_ci{ 9438c2ecf20Sopenharmony_ci struct netdev_private *np = from_timer(np, t, timer); 9448c2ecf20Sopenharmony_ci struct net_device *dev = np->mii_if.dev; 9458c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->base; 9468c2ecf20Sopenharmony_ci int next_tick = 10*HZ; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci if (netif_msg_timer(np)) { 9498c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Media selection timer tick, intr status %4.4x, " 9508c2ecf20Sopenharmony_ci "Tx %x Rx %x.\n", 9518c2ecf20Sopenharmony_ci dev->name, ioread16(ioaddr + IntrEnable), 9528c2ecf20Sopenharmony_ci ioread8(ioaddr + TxStatus), ioread32(ioaddr + RxStatus)); 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci check_duplex(dev); 9558c2ecf20Sopenharmony_ci np->timer.expires = jiffies + next_tick; 9568c2ecf20Sopenharmony_ci add_timer(&np->timer); 9578c2ecf20Sopenharmony_ci} 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_cistatic void tx_timeout(struct net_device *dev, unsigned int txqueue) 9608c2ecf20Sopenharmony_ci{ 9618c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 9628c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->base; 9638c2ecf20Sopenharmony_ci unsigned long flag; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci netif_stop_queue(dev); 9668c2ecf20Sopenharmony_ci tasklet_disable(&np->tx_tasklet); 9678c2ecf20Sopenharmony_ci iowrite16(0, ioaddr + IntrEnable); 9688c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Transmit timed out, TxStatus %2.2x " 9698c2ecf20Sopenharmony_ci "TxFrameId %2.2x," 9708c2ecf20Sopenharmony_ci " resetting...\n", dev->name, ioread8(ioaddr + TxStatus), 9718c2ecf20Sopenharmony_ci ioread8(ioaddr + TxFrameId)); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci { 9748c2ecf20Sopenharmony_ci int i; 9758c2ecf20Sopenharmony_ci for (i=0; i<TX_RING_SIZE; i++) { 9768c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%02x %08llx %08x %08x(%02x) %08x %08x\n", i, 9778c2ecf20Sopenharmony_ci (unsigned long long)(np->tx_ring_dma + i*sizeof(*np->tx_ring)), 9788c2ecf20Sopenharmony_ci le32_to_cpu(np->tx_ring[i].next_desc), 9798c2ecf20Sopenharmony_ci le32_to_cpu(np->tx_ring[i].status), 9808c2ecf20Sopenharmony_ci (le32_to_cpu(np->tx_ring[i].status) >> 2) & 0xff, 9818c2ecf20Sopenharmony_ci le32_to_cpu(np->tx_ring[i].frag[0].addr), 9828c2ecf20Sopenharmony_ci le32_to_cpu(np->tx_ring[i].frag[0].length)); 9838c2ecf20Sopenharmony_ci } 9848c2ecf20Sopenharmony_ci printk(KERN_DEBUG "TxListPtr=%08x netif_queue_stopped=%d\n", 9858c2ecf20Sopenharmony_ci ioread32(np->base + TxListPtr), 9868c2ecf20Sopenharmony_ci netif_queue_stopped(dev)); 9878c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cur_tx=%d(%02x) dirty_tx=%d(%02x)\n", 9888c2ecf20Sopenharmony_ci np->cur_tx, np->cur_tx % TX_RING_SIZE, 9898c2ecf20Sopenharmony_ci np->dirty_tx, np->dirty_tx % TX_RING_SIZE); 9908c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cur_rx=%d dirty_rx=%d\n", np->cur_rx, np->dirty_rx); 9918c2ecf20Sopenharmony_ci printk(KERN_DEBUG "cur_task=%d\n", np->cur_task); 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci spin_lock_irqsave(&np->lock, flag); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci /* Stop and restart the chip's Tx processes . */ 9968c2ecf20Sopenharmony_ci reset_tx(dev); 9978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flag); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci dev->if_port = 0; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci netif_trans_update(dev); /* prevent tx timeout */ 10028c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 10038c2ecf20Sopenharmony_ci if (np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) { 10048c2ecf20Sopenharmony_ci netif_wake_queue(dev); 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci iowrite16(DEFAULT_INTR, ioaddr + IntrEnable); 10078c2ecf20Sopenharmony_ci tasklet_enable(&np->tx_tasklet); 10088c2ecf20Sopenharmony_ci} 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ 10128c2ecf20Sopenharmony_cistatic void init_ring(struct net_device *dev) 10138c2ecf20Sopenharmony_ci{ 10148c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 10158c2ecf20Sopenharmony_ci int i; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci np->cur_rx = np->cur_tx = 0; 10188c2ecf20Sopenharmony_ci np->dirty_rx = np->dirty_tx = 0; 10198c2ecf20Sopenharmony_ci np->cur_task = 0; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci np->rx_buf_sz = (dev->mtu <= 1520 ? PKT_BUF_SZ : dev->mtu + 16); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci /* Initialize all Rx descriptors. */ 10248c2ecf20Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 10258c2ecf20Sopenharmony_ci np->rx_ring[i].next_desc = cpu_to_le32(np->rx_ring_dma + 10268c2ecf20Sopenharmony_ci ((i+1)%RX_RING_SIZE)*sizeof(*np->rx_ring)); 10278c2ecf20Sopenharmony_ci np->rx_ring[i].status = 0; 10288c2ecf20Sopenharmony_ci np->rx_ring[i].frag[0].length = 0; 10298c2ecf20Sopenharmony_ci np->rx_skbuff[i] = NULL; 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci /* Fill in the Rx buffers. Handle allocation failure gracefully. */ 10338c2ecf20Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 10348c2ecf20Sopenharmony_ci struct sk_buff *skb = 10358c2ecf20Sopenharmony_ci netdev_alloc_skb(dev, np->rx_buf_sz + 2); 10368c2ecf20Sopenharmony_ci np->rx_skbuff[i] = skb; 10378c2ecf20Sopenharmony_ci if (skb == NULL) 10388c2ecf20Sopenharmony_ci break; 10398c2ecf20Sopenharmony_ci skb_reserve(skb, 2); /* 16 byte align the IP header. */ 10408c2ecf20Sopenharmony_ci np->rx_ring[i].frag[0].addr = cpu_to_le32( 10418c2ecf20Sopenharmony_ci dma_map_single(&np->pci_dev->dev, skb->data, 10428c2ecf20Sopenharmony_ci np->rx_buf_sz, DMA_FROM_DEVICE)); 10438c2ecf20Sopenharmony_ci if (dma_mapping_error(&np->pci_dev->dev, 10448c2ecf20Sopenharmony_ci np->rx_ring[i].frag[0].addr)) { 10458c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 10468c2ecf20Sopenharmony_ci np->rx_skbuff[i] = NULL; 10478c2ecf20Sopenharmony_ci break; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci np->rx_ring[i].frag[0].length = cpu_to_le32(np->rx_buf_sz | LastFrag); 10508c2ecf20Sopenharmony_ci } 10518c2ecf20Sopenharmony_ci np->dirty_rx = (unsigned int)(i - RX_RING_SIZE); 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) { 10548c2ecf20Sopenharmony_ci np->tx_skbuff[i] = NULL; 10558c2ecf20Sopenharmony_ci np->tx_ring[i].status = 0; 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci} 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_cistatic void tx_poll(struct tasklet_struct *t) 10608c2ecf20Sopenharmony_ci{ 10618c2ecf20Sopenharmony_ci struct netdev_private *np = from_tasklet(np, t, tx_tasklet); 10628c2ecf20Sopenharmony_ci unsigned head = np->cur_task % TX_RING_SIZE; 10638c2ecf20Sopenharmony_ci struct netdev_desc *txdesc = 10648c2ecf20Sopenharmony_ci &np->tx_ring[(np->cur_tx - 1) % TX_RING_SIZE]; 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci /* Chain the next pointer */ 10678c2ecf20Sopenharmony_ci for (; np->cur_tx - np->cur_task > 0; np->cur_task++) { 10688c2ecf20Sopenharmony_ci int entry = np->cur_task % TX_RING_SIZE; 10698c2ecf20Sopenharmony_ci txdesc = &np->tx_ring[entry]; 10708c2ecf20Sopenharmony_ci if (np->last_tx) { 10718c2ecf20Sopenharmony_ci np->last_tx->next_desc = cpu_to_le32(np->tx_ring_dma + 10728c2ecf20Sopenharmony_ci entry*sizeof(struct netdev_desc)); 10738c2ecf20Sopenharmony_ci } 10748c2ecf20Sopenharmony_ci np->last_tx = txdesc; 10758c2ecf20Sopenharmony_ci } 10768c2ecf20Sopenharmony_ci /* Indicate the latest descriptor of tx ring */ 10778c2ecf20Sopenharmony_ci txdesc->status |= cpu_to_le32(DescIntrOnTx); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci if (ioread32 (np->base + TxListPtr) == 0) 10808c2ecf20Sopenharmony_ci iowrite32 (np->tx_ring_dma + head * sizeof(struct netdev_desc), 10818c2ecf20Sopenharmony_ci np->base + TxListPtr); 10828c2ecf20Sopenharmony_ci} 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_cistatic netdev_tx_t 10858c2ecf20Sopenharmony_cistart_tx (struct sk_buff *skb, struct net_device *dev) 10868c2ecf20Sopenharmony_ci{ 10878c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 10888c2ecf20Sopenharmony_ci struct netdev_desc *txdesc; 10898c2ecf20Sopenharmony_ci unsigned entry; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci /* Calculate the next Tx descriptor entry. */ 10928c2ecf20Sopenharmony_ci entry = np->cur_tx % TX_RING_SIZE; 10938c2ecf20Sopenharmony_ci np->tx_skbuff[entry] = skb; 10948c2ecf20Sopenharmony_ci txdesc = &np->tx_ring[entry]; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci txdesc->next_desc = 0; 10978c2ecf20Sopenharmony_ci txdesc->status = cpu_to_le32 ((entry << 2) | DisableAlign); 10988c2ecf20Sopenharmony_ci txdesc->frag[0].addr = cpu_to_le32(dma_map_single(&np->pci_dev->dev, 10998c2ecf20Sopenharmony_ci skb->data, skb->len, DMA_TO_DEVICE)); 11008c2ecf20Sopenharmony_ci if (dma_mapping_error(&np->pci_dev->dev, 11018c2ecf20Sopenharmony_ci txdesc->frag[0].addr)) 11028c2ecf20Sopenharmony_ci goto drop_frame; 11038c2ecf20Sopenharmony_ci txdesc->frag[0].length = cpu_to_le32 (skb->len | LastFrag); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci /* Increment cur_tx before tasklet_schedule() */ 11068c2ecf20Sopenharmony_ci np->cur_tx++; 11078c2ecf20Sopenharmony_ci mb(); 11088c2ecf20Sopenharmony_ci /* Schedule a tx_poll() task */ 11098c2ecf20Sopenharmony_ci tasklet_schedule(&np->tx_tasklet); 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci /* On some architectures: explicitly flush cache lines here. */ 11128c2ecf20Sopenharmony_ci if (np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 1 && 11138c2ecf20Sopenharmony_ci !netif_queue_stopped(dev)) { 11148c2ecf20Sopenharmony_ci /* do nothing */ 11158c2ecf20Sopenharmony_ci } else { 11168c2ecf20Sopenharmony_ci netif_stop_queue (dev); 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci if (netif_msg_tx_queued(np)) { 11198c2ecf20Sopenharmony_ci printk (KERN_DEBUG 11208c2ecf20Sopenharmony_ci "%s: Transmit frame #%d queued in slot %d.\n", 11218c2ecf20Sopenharmony_ci dev->name, np->cur_tx, entry); 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_cidrop_frame: 11268c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 11278c2ecf20Sopenharmony_ci np->tx_skbuff[entry] = NULL; 11288c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 11298c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 11308c2ecf20Sopenharmony_ci} 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci/* Reset hardware tx and free all of tx buffers */ 11338c2ecf20Sopenharmony_cistatic int 11348c2ecf20Sopenharmony_cireset_tx (struct net_device *dev) 11358c2ecf20Sopenharmony_ci{ 11368c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 11378c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->base; 11388c2ecf20Sopenharmony_ci struct sk_buff *skb; 11398c2ecf20Sopenharmony_ci int i; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci /* Reset tx logic, TxListPtr will be cleaned */ 11428c2ecf20Sopenharmony_ci iowrite16 (TxDisable, ioaddr + MACCtrl1); 11438c2ecf20Sopenharmony_ci sundance_reset(dev, (NetworkReset|FIFOReset|DMAReset|TxReset) << 16); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci /* free all tx skbuff */ 11468c2ecf20Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) { 11478c2ecf20Sopenharmony_ci np->tx_ring[i].next_desc = 0; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci skb = np->tx_skbuff[i]; 11508c2ecf20Sopenharmony_ci if (skb) { 11518c2ecf20Sopenharmony_ci dma_unmap_single(&np->pci_dev->dev, 11528c2ecf20Sopenharmony_ci le32_to_cpu(np->tx_ring[i].frag[0].addr), 11538c2ecf20Sopenharmony_ci skb->len, DMA_TO_DEVICE); 11548c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 11558c2ecf20Sopenharmony_ci np->tx_skbuff[i] = NULL; 11568c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 11578c2ecf20Sopenharmony_ci } 11588c2ecf20Sopenharmony_ci } 11598c2ecf20Sopenharmony_ci np->cur_tx = np->dirty_tx = 0; 11608c2ecf20Sopenharmony_ci np->cur_task = 0; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci np->last_tx = NULL; 11638c2ecf20Sopenharmony_ci iowrite8(127, ioaddr + TxDMAPollPeriod); 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci iowrite16 (StatsEnable | RxEnable | TxEnable, ioaddr + MACCtrl1); 11668c2ecf20Sopenharmony_ci return 0; 11678c2ecf20Sopenharmony_ci} 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci/* The interrupt handler cleans up after the Tx thread, 11708c2ecf20Sopenharmony_ci and schedule a Rx thread work */ 11718c2ecf20Sopenharmony_cistatic irqreturn_t intr_handler(int irq, void *dev_instance) 11728c2ecf20Sopenharmony_ci{ 11738c2ecf20Sopenharmony_ci struct net_device *dev = (struct net_device *)dev_instance; 11748c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 11758c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->base; 11768c2ecf20Sopenharmony_ci int hw_frame_id; 11778c2ecf20Sopenharmony_ci int tx_cnt; 11788c2ecf20Sopenharmony_ci int tx_status; 11798c2ecf20Sopenharmony_ci int handled = 0; 11808c2ecf20Sopenharmony_ci int i; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci do { 11838c2ecf20Sopenharmony_ci int intr_status = ioread16(ioaddr + IntrStatus); 11848c2ecf20Sopenharmony_ci iowrite16(intr_status, ioaddr + IntrStatus); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci if (netif_msg_intr(np)) 11878c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n", 11888c2ecf20Sopenharmony_ci dev->name, intr_status); 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci if (!(intr_status & DEFAULT_INTR)) 11918c2ecf20Sopenharmony_ci break; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci handled = 1; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci if (intr_status & (IntrRxDMADone)) { 11968c2ecf20Sopenharmony_ci iowrite16(DEFAULT_INTR & ~(IntrRxDone|IntrRxDMADone), 11978c2ecf20Sopenharmony_ci ioaddr + IntrEnable); 11988c2ecf20Sopenharmony_ci if (np->budget < 0) 11998c2ecf20Sopenharmony_ci np->budget = RX_BUDGET; 12008c2ecf20Sopenharmony_ci tasklet_schedule(&np->rx_tasklet); 12018c2ecf20Sopenharmony_ci } 12028c2ecf20Sopenharmony_ci if (intr_status & (IntrTxDone | IntrDrvRqst)) { 12038c2ecf20Sopenharmony_ci tx_status = ioread16 (ioaddr + TxStatus); 12048c2ecf20Sopenharmony_ci for (tx_cnt=32; tx_status & 0x80; --tx_cnt) { 12058c2ecf20Sopenharmony_ci if (netif_msg_tx_done(np)) 12068c2ecf20Sopenharmony_ci printk 12078c2ecf20Sopenharmony_ci ("%s: Transmit status is %2.2x.\n", 12088c2ecf20Sopenharmony_ci dev->name, tx_status); 12098c2ecf20Sopenharmony_ci if (tx_status & 0x1e) { 12108c2ecf20Sopenharmony_ci if (netif_msg_tx_err(np)) 12118c2ecf20Sopenharmony_ci printk("%s: Transmit error status %4.4x.\n", 12128c2ecf20Sopenharmony_ci dev->name, tx_status); 12138c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 12148c2ecf20Sopenharmony_ci if (tx_status & 0x10) 12158c2ecf20Sopenharmony_ci dev->stats.tx_fifo_errors++; 12168c2ecf20Sopenharmony_ci if (tx_status & 0x08) 12178c2ecf20Sopenharmony_ci dev->stats.collisions++; 12188c2ecf20Sopenharmony_ci if (tx_status & 0x04) 12198c2ecf20Sopenharmony_ci dev->stats.tx_fifo_errors++; 12208c2ecf20Sopenharmony_ci if (tx_status & 0x02) 12218c2ecf20Sopenharmony_ci dev->stats.tx_window_errors++; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci /* 12248c2ecf20Sopenharmony_ci ** This reset has been verified on 12258c2ecf20Sopenharmony_ci ** DFE-580TX boards ! phdm@macqel.be. 12268c2ecf20Sopenharmony_ci */ 12278c2ecf20Sopenharmony_ci if (tx_status & 0x10) { /* TxUnderrun */ 12288c2ecf20Sopenharmony_ci /* Restart Tx FIFO and transmitter */ 12298c2ecf20Sopenharmony_ci sundance_reset(dev, (NetworkReset|FIFOReset|TxReset) << 16); 12308c2ecf20Sopenharmony_ci /* No need to reset the Tx pointer here */ 12318c2ecf20Sopenharmony_ci } 12328c2ecf20Sopenharmony_ci /* Restart the Tx. Need to make sure tx enabled */ 12338c2ecf20Sopenharmony_ci i = 10; 12348c2ecf20Sopenharmony_ci do { 12358c2ecf20Sopenharmony_ci iowrite16(ioread16(ioaddr + MACCtrl1) | TxEnable, ioaddr + MACCtrl1); 12368c2ecf20Sopenharmony_ci if (ioread16(ioaddr + MACCtrl1) & TxEnabled) 12378c2ecf20Sopenharmony_ci break; 12388c2ecf20Sopenharmony_ci mdelay(1); 12398c2ecf20Sopenharmony_ci } while (--i); 12408c2ecf20Sopenharmony_ci } 12418c2ecf20Sopenharmony_ci /* Yup, this is a documentation bug. It cost me *hours*. */ 12428c2ecf20Sopenharmony_ci iowrite16 (0, ioaddr + TxStatus); 12438c2ecf20Sopenharmony_ci if (tx_cnt < 0) { 12448c2ecf20Sopenharmony_ci iowrite32(5000, ioaddr + DownCounter); 12458c2ecf20Sopenharmony_ci break; 12468c2ecf20Sopenharmony_ci } 12478c2ecf20Sopenharmony_ci tx_status = ioread16 (ioaddr + TxStatus); 12488c2ecf20Sopenharmony_ci } 12498c2ecf20Sopenharmony_ci hw_frame_id = (tx_status >> 8) & 0xff; 12508c2ecf20Sopenharmony_ci } else { 12518c2ecf20Sopenharmony_ci hw_frame_id = ioread8(ioaddr + TxFrameId); 12528c2ecf20Sopenharmony_ci } 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci if (np->pci_dev->revision >= 0x14) { 12558c2ecf20Sopenharmony_ci spin_lock(&np->lock); 12568c2ecf20Sopenharmony_ci for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) { 12578c2ecf20Sopenharmony_ci int entry = np->dirty_tx % TX_RING_SIZE; 12588c2ecf20Sopenharmony_ci struct sk_buff *skb; 12598c2ecf20Sopenharmony_ci int sw_frame_id; 12608c2ecf20Sopenharmony_ci sw_frame_id = (le32_to_cpu( 12618c2ecf20Sopenharmony_ci np->tx_ring[entry].status) >> 2) & 0xff; 12628c2ecf20Sopenharmony_ci if (sw_frame_id == hw_frame_id && 12638c2ecf20Sopenharmony_ci !(le32_to_cpu(np->tx_ring[entry].status) 12648c2ecf20Sopenharmony_ci & 0x00010000)) 12658c2ecf20Sopenharmony_ci break; 12668c2ecf20Sopenharmony_ci if (sw_frame_id == (hw_frame_id + 1) % 12678c2ecf20Sopenharmony_ci TX_RING_SIZE) 12688c2ecf20Sopenharmony_ci break; 12698c2ecf20Sopenharmony_ci skb = np->tx_skbuff[entry]; 12708c2ecf20Sopenharmony_ci /* Free the original skb. */ 12718c2ecf20Sopenharmony_ci dma_unmap_single(&np->pci_dev->dev, 12728c2ecf20Sopenharmony_ci le32_to_cpu(np->tx_ring[entry].frag[0].addr), 12738c2ecf20Sopenharmony_ci skb->len, DMA_TO_DEVICE); 12748c2ecf20Sopenharmony_ci dev_consume_skb_irq(np->tx_skbuff[entry]); 12758c2ecf20Sopenharmony_ci np->tx_skbuff[entry] = NULL; 12768c2ecf20Sopenharmony_ci np->tx_ring[entry].frag[0].addr = 0; 12778c2ecf20Sopenharmony_ci np->tx_ring[entry].frag[0].length = 0; 12788c2ecf20Sopenharmony_ci } 12798c2ecf20Sopenharmony_ci spin_unlock(&np->lock); 12808c2ecf20Sopenharmony_ci } else { 12818c2ecf20Sopenharmony_ci spin_lock(&np->lock); 12828c2ecf20Sopenharmony_ci for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) { 12838c2ecf20Sopenharmony_ci int entry = np->dirty_tx % TX_RING_SIZE; 12848c2ecf20Sopenharmony_ci struct sk_buff *skb; 12858c2ecf20Sopenharmony_ci if (!(le32_to_cpu(np->tx_ring[entry].status) 12868c2ecf20Sopenharmony_ci & 0x00010000)) 12878c2ecf20Sopenharmony_ci break; 12888c2ecf20Sopenharmony_ci skb = np->tx_skbuff[entry]; 12898c2ecf20Sopenharmony_ci /* Free the original skb. */ 12908c2ecf20Sopenharmony_ci dma_unmap_single(&np->pci_dev->dev, 12918c2ecf20Sopenharmony_ci le32_to_cpu(np->tx_ring[entry].frag[0].addr), 12928c2ecf20Sopenharmony_ci skb->len, DMA_TO_DEVICE); 12938c2ecf20Sopenharmony_ci dev_consume_skb_irq(np->tx_skbuff[entry]); 12948c2ecf20Sopenharmony_ci np->tx_skbuff[entry] = NULL; 12958c2ecf20Sopenharmony_ci np->tx_ring[entry].frag[0].addr = 0; 12968c2ecf20Sopenharmony_ci np->tx_ring[entry].frag[0].length = 0; 12978c2ecf20Sopenharmony_ci } 12988c2ecf20Sopenharmony_ci spin_unlock(&np->lock); 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci if (netif_queue_stopped(dev) && 13028c2ecf20Sopenharmony_ci np->cur_tx - np->dirty_tx < TX_QUEUE_LEN - 4) { 13038c2ecf20Sopenharmony_ci /* The ring is no longer full, clear busy flag. */ 13048c2ecf20Sopenharmony_ci netif_wake_queue (dev); 13058c2ecf20Sopenharmony_ci } 13068c2ecf20Sopenharmony_ci /* Abnormal error summary/uncommon events handlers. */ 13078c2ecf20Sopenharmony_ci if (intr_status & (IntrPCIErr | LinkChange | StatsMax)) 13088c2ecf20Sopenharmony_ci netdev_error(dev, intr_status); 13098c2ecf20Sopenharmony_ci } while (0); 13108c2ecf20Sopenharmony_ci if (netif_msg_intr(np)) 13118c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", 13128c2ecf20Sopenharmony_ci dev->name, ioread16(ioaddr + IntrStatus)); 13138c2ecf20Sopenharmony_ci return IRQ_RETVAL(handled); 13148c2ecf20Sopenharmony_ci} 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_cistatic void rx_poll(struct tasklet_struct *t) 13178c2ecf20Sopenharmony_ci{ 13188c2ecf20Sopenharmony_ci struct netdev_private *np = from_tasklet(np, t, rx_tasklet); 13198c2ecf20Sopenharmony_ci struct net_device *dev = np->ndev; 13208c2ecf20Sopenharmony_ci int entry = np->cur_rx % RX_RING_SIZE; 13218c2ecf20Sopenharmony_ci int boguscnt = np->budget; 13228c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->base; 13238c2ecf20Sopenharmony_ci int received = 0; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci /* If EOP is set on the next entry, it's a new packet. Send it up. */ 13268c2ecf20Sopenharmony_ci while (1) { 13278c2ecf20Sopenharmony_ci struct netdev_desc *desc = &(np->rx_ring[entry]); 13288c2ecf20Sopenharmony_ci u32 frame_status = le32_to_cpu(desc->status); 13298c2ecf20Sopenharmony_ci int pkt_len; 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci if (--boguscnt < 0) { 13328c2ecf20Sopenharmony_ci goto not_done; 13338c2ecf20Sopenharmony_ci } 13348c2ecf20Sopenharmony_ci if (!(frame_status & DescOwn)) 13358c2ecf20Sopenharmony_ci break; 13368c2ecf20Sopenharmony_ci pkt_len = frame_status & 0x1fff; /* Chip omits the CRC. */ 13378c2ecf20Sopenharmony_ci if (netif_msg_rx_status(np)) 13388c2ecf20Sopenharmony_ci printk(KERN_DEBUG " netdev_rx() status was %8.8x.\n", 13398c2ecf20Sopenharmony_ci frame_status); 13408c2ecf20Sopenharmony_ci if (frame_status & 0x001f4000) { 13418c2ecf20Sopenharmony_ci /* There was a error. */ 13428c2ecf20Sopenharmony_ci if (netif_msg_rx_err(np)) 13438c2ecf20Sopenharmony_ci printk(KERN_DEBUG " netdev_rx() Rx error was %8.8x.\n", 13448c2ecf20Sopenharmony_ci frame_status); 13458c2ecf20Sopenharmony_ci dev->stats.rx_errors++; 13468c2ecf20Sopenharmony_ci if (frame_status & 0x00100000) 13478c2ecf20Sopenharmony_ci dev->stats.rx_length_errors++; 13488c2ecf20Sopenharmony_ci if (frame_status & 0x00010000) 13498c2ecf20Sopenharmony_ci dev->stats.rx_fifo_errors++; 13508c2ecf20Sopenharmony_ci if (frame_status & 0x00060000) 13518c2ecf20Sopenharmony_ci dev->stats.rx_frame_errors++; 13528c2ecf20Sopenharmony_ci if (frame_status & 0x00080000) 13538c2ecf20Sopenharmony_ci dev->stats.rx_crc_errors++; 13548c2ecf20Sopenharmony_ci if (frame_status & 0x00100000) { 13558c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Oversized Ethernet frame," 13568c2ecf20Sopenharmony_ci " status %8.8x.\n", 13578c2ecf20Sopenharmony_ci dev->name, frame_status); 13588c2ecf20Sopenharmony_ci } 13598c2ecf20Sopenharmony_ci } else { 13608c2ecf20Sopenharmony_ci struct sk_buff *skb; 13618c2ecf20Sopenharmony_ci#ifndef final_version 13628c2ecf20Sopenharmony_ci if (netif_msg_rx_status(np)) 13638c2ecf20Sopenharmony_ci printk(KERN_DEBUG " netdev_rx() normal Rx pkt length %d" 13648c2ecf20Sopenharmony_ci ", bogus_cnt %d.\n", 13658c2ecf20Sopenharmony_ci pkt_len, boguscnt); 13668c2ecf20Sopenharmony_ci#endif 13678c2ecf20Sopenharmony_ci /* Check if the packet is long enough to accept without copying 13688c2ecf20Sopenharmony_ci to a minimally-sized skbuff. */ 13698c2ecf20Sopenharmony_ci if (pkt_len < rx_copybreak && 13708c2ecf20Sopenharmony_ci (skb = netdev_alloc_skb(dev, pkt_len + 2)) != NULL) { 13718c2ecf20Sopenharmony_ci skb_reserve(skb, 2); /* 16 byte align the IP header */ 13728c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(&np->pci_dev->dev, 13738c2ecf20Sopenharmony_ci le32_to_cpu(desc->frag[0].addr), 13748c2ecf20Sopenharmony_ci np->rx_buf_sz, DMA_FROM_DEVICE); 13758c2ecf20Sopenharmony_ci skb_copy_to_linear_data(skb, np->rx_skbuff[entry]->data, pkt_len); 13768c2ecf20Sopenharmony_ci dma_sync_single_for_device(&np->pci_dev->dev, 13778c2ecf20Sopenharmony_ci le32_to_cpu(desc->frag[0].addr), 13788c2ecf20Sopenharmony_ci np->rx_buf_sz, DMA_FROM_DEVICE); 13798c2ecf20Sopenharmony_ci skb_put(skb, pkt_len); 13808c2ecf20Sopenharmony_ci } else { 13818c2ecf20Sopenharmony_ci dma_unmap_single(&np->pci_dev->dev, 13828c2ecf20Sopenharmony_ci le32_to_cpu(desc->frag[0].addr), 13838c2ecf20Sopenharmony_ci np->rx_buf_sz, DMA_FROM_DEVICE); 13848c2ecf20Sopenharmony_ci skb_put(skb = np->rx_skbuff[entry], pkt_len); 13858c2ecf20Sopenharmony_ci np->rx_skbuff[entry] = NULL; 13868c2ecf20Sopenharmony_ci } 13878c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 13888c2ecf20Sopenharmony_ci /* Note: checksum -> skb->ip_summed = CHECKSUM_UNNECESSARY; */ 13898c2ecf20Sopenharmony_ci netif_rx(skb); 13908c2ecf20Sopenharmony_ci } 13918c2ecf20Sopenharmony_ci entry = (entry + 1) % RX_RING_SIZE; 13928c2ecf20Sopenharmony_ci received++; 13938c2ecf20Sopenharmony_ci } 13948c2ecf20Sopenharmony_ci np->cur_rx = entry; 13958c2ecf20Sopenharmony_ci refill_rx (dev); 13968c2ecf20Sopenharmony_ci np->budget -= received; 13978c2ecf20Sopenharmony_ci iowrite16(DEFAULT_INTR, ioaddr + IntrEnable); 13988c2ecf20Sopenharmony_ci return; 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_cinot_done: 14018c2ecf20Sopenharmony_ci np->cur_rx = entry; 14028c2ecf20Sopenharmony_ci refill_rx (dev); 14038c2ecf20Sopenharmony_ci if (!received) 14048c2ecf20Sopenharmony_ci received = 1; 14058c2ecf20Sopenharmony_ci np->budget -= received; 14068c2ecf20Sopenharmony_ci if (np->budget <= 0) 14078c2ecf20Sopenharmony_ci np->budget = RX_BUDGET; 14088c2ecf20Sopenharmony_ci tasklet_schedule(&np->rx_tasklet); 14098c2ecf20Sopenharmony_ci} 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_cistatic void refill_rx (struct net_device *dev) 14128c2ecf20Sopenharmony_ci{ 14138c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 14148c2ecf20Sopenharmony_ci int entry; 14158c2ecf20Sopenharmony_ci int cnt = 0; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci /* Refill the Rx ring buffers. */ 14188c2ecf20Sopenharmony_ci for (;(np->cur_rx - np->dirty_rx + RX_RING_SIZE) % RX_RING_SIZE > 0; 14198c2ecf20Sopenharmony_ci np->dirty_rx = (np->dirty_rx + 1) % RX_RING_SIZE) { 14208c2ecf20Sopenharmony_ci struct sk_buff *skb; 14218c2ecf20Sopenharmony_ci entry = np->dirty_rx % RX_RING_SIZE; 14228c2ecf20Sopenharmony_ci if (np->rx_skbuff[entry] == NULL) { 14238c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(dev, np->rx_buf_sz + 2); 14248c2ecf20Sopenharmony_ci np->rx_skbuff[entry] = skb; 14258c2ecf20Sopenharmony_ci if (skb == NULL) 14268c2ecf20Sopenharmony_ci break; /* Better luck next round. */ 14278c2ecf20Sopenharmony_ci skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ 14288c2ecf20Sopenharmony_ci np->rx_ring[entry].frag[0].addr = cpu_to_le32( 14298c2ecf20Sopenharmony_ci dma_map_single(&np->pci_dev->dev, skb->data, 14308c2ecf20Sopenharmony_ci np->rx_buf_sz, DMA_FROM_DEVICE)); 14318c2ecf20Sopenharmony_ci if (dma_mapping_error(&np->pci_dev->dev, 14328c2ecf20Sopenharmony_ci np->rx_ring[entry].frag[0].addr)) { 14338c2ecf20Sopenharmony_ci dev_kfree_skb_irq(skb); 14348c2ecf20Sopenharmony_ci np->rx_skbuff[entry] = NULL; 14358c2ecf20Sopenharmony_ci break; 14368c2ecf20Sopenharmony_ci } 14378c2ecf20Sopenharmony_ci } 14388c2ecf20Sopenharmony_ci /* Perhaps we need not reset this field. */ 14398c2ecf20Sopenharmony_ci np->rx_ring[entry].frag[0].length = 14408c2ecf20Sopenharmony_ci cpu_to_le32(np->rx_buf_sz | LastFrag); 14418c2ecf20Sopenharmony_ci np->rx_ring[entry].status = 0; 14428c2ecf20Sopenharmony_ci cnt++; 14438c2ecf20Sopenharmony_ci } 14448c2ecf20Sopenharmony_ci} 14458c2ecf20Sopenharmony_cistatic void netdev_error(struct net_device *dev, int intr_status) 14468c2ecf20Sopenharmony_ci{ 14478c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 14488c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->base; 14498c2ecf20Sopenharmony_ci u16 mii_ctl, mii_advertise, mii_lpa; 14508c2ecf20Sopenharmony_ci int speed; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci if (intr_status & LinkChange) { 14538c2ecf20Sopenharmony_ci if (mdio_wait_link(dev, 10) == 0) { 14548c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Link up\n", dev->name); 14558c2ecf20Sopenharmony_ci if (np->an_enable) { 14568c2ecf20Sopenharmony_ci mii_advertise = mdio_read(dev, np->phys[0], 14578c2ecf20Sopenharmony_ci MII_ADVERTISE); 14588c2ecf20Sopenharmony_ci mii_lpa = mdio_read(dev, np->phys[0], MII_LPA); 14598c2ecf20Sopenharmony_ci mii_advertise &= mii_lpa; 14608c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Link changed: ", 14618c2ecf20Sopenharmony_ci dev->name); 14628c2ecf20Sopenharmony_ci if (mii_advertise & ADVERTISE_100FULL) { 14638c2ecf20Sopenharmony_ci np->speed = 100; 14648c2ecf20Sopenharmony_ci printk("100Mbps, full duplex\n"); 14658c2ecf20Sopenharmony_ci } else if (mii_advertise & ADVERTISE_100HALF) { 14668c2ecf20Sopenharmony_ci np->speed = 100; 14678c2ecf20Sopenharmony_ci printk("100Mbps, half duplex\n"); 14688c2ecf20Sopenharmony_ci } else if (mii_advertise & ADVERTISE_10FULL) { 14698c2ecf20Sopenharmony_ci np->speed = 10; 14708c2ecf20Sopenharmony_ci printk("10Mbps, full duplex\n"); 14718c2ecf20Sopenharmony_ci } else if (mii_advertise & ADVERTISE_10HALF) { 14728c2ecf20Sopenharmony_ci np->speed = 10; 14738c2ecf20Sopenharmony_ci printk("10Mbps, half duplex\n"); 14748c2ecf20Sopenharmony_ci } else 14758c2ecf20Sopenharmony_ci printk("\n"); 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci } else { 14788c2ecf20Sopenharmony_ci mii_ctl = mdio_read(dev, np->phys[0], MII_BMCR); 14798c2ecf20Sopenharmony_ci speed = (mii_ctl & BMCR_SPEED100) ? 100 : 10; 14808c2ecf20Sopenharmony_ci np->speed = speed; 14818c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Link changed: %dMbps ,", 14828c2ecf20Sopenharmony_ci dev->name, speed); 14838c2ecf20Sopenharmony_ci printk("%s duplex.\n", 14848c2ecf20Sopenharmony_ci (mii_ctl & BMCR_FULLDPLX) ? 14858c2ecf20Sopenharmony_ci "full" : "half"); 14868c2ecf20Sopenharmony_ci } 14878c2ecf20Sopenharmony_ci check_duplex(dev); 14888c2ecf20Sopenharmony_ci if (np->flowctrl && np->mii_if.full_duplex) { 14898c2ecf20Sopenharmony_ci iowrite16(ioread16(ioaddr + MulticastFilter1+2) | 0x0200, 14908c2ecf20Sopenharmony_ci ioaddr + MulticastFilter1+2); 14918c2ecf20Sopenharmony_ci iowrite16(ioread16(ioaddr + MACCtrl0) | EnbFlowCtrl, 14928c2ecf20Sopenharmony_ci ioaddr + MACCtrl0); 14938c2ecf20Sopenharmony_ci } 14948c2ecf20Sopenharmony_ci netif_carrier_on(dev); 14958c2ecf20Sopenharmony_ci } else { 14968c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Link down\n", dev->name); 14978c2ecf20Sopenharmony_ci netif_carrier_off(dev); 14988c2ecf20Sopenharmony_ci } 14998c2ecf20Sopenharmony_ci } 15008c2ecf20Sopenharmony_ci if (intr_status & StatsMax) { 15018c2ecf20Sopenharmony_ci get_stats(dev); 15028c2ecf20Sopenharmony_ci } 15038c2ecf20Sopenharmony_ci if (intr_status & IntrPCIErr) { 15048c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Something Wicked happened! %4.4x.\n", 15058c2ecf20Sopenharmony_ci dev->name, intr_status); 15068c2ecf20Sopenharmony_ci /* We must do a global reset of DMA to continue. */ 15078c2ecf20Sopenharmony_ci } 15088c2ecf20Sopenharmony_ci} 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_cistatic struct net_device_stats *get_stats(struct net_device *dev) 15118c2ecf20Sopenharmony_ci{ 15128c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 15138c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->base; 15148c2ecf20Sopenharmony_ci unsigned long flags; 15158c2ecf20Sopenharmony_ci u8 late_coll, single_coll, mult_coll; 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci spin_lock_irqsave(&np->statlock, flags); 15188c2ecf20Sopenharmony_ci /* The chip only need report frame silently dropped. */ 15198c2ecf20Sopenharmony_ci dev->stats.rx_missed_errors += ioread8(ioaddr + RxMissed); 15208c2ecf20Sopenharmony_ci dev->stats.tx_packets += ioread16(ioaddr + TxFramesOK); 15218c2ecf20Sopenharmony_ci dev->stats.rx_packets += ioread16(ioaddr + RxFramesOK); 15228c2ecf20Sopenharmony_ci dev->stats.tx_carrier_errors += ioread8(ioaddr + StatsCarrierError); 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci mult_coll = ioread8(ioaddr + StatsMultiColl); 15258c2ecf20Sopenharmony_ci np->xstats.tx_multiple_collisions += mult_coll; 15268c2ecf20Sopenharmony_ci single_coll = ioread8(ioaddr + StatsOneColl); 15278c2ecf20Sopenharmony_ci np->xstats.tx_single_collisions += single_coll; 15288c2ecf20Sopenharmony_ci late_coll = ioread8(ioaddr + StatsLateColl); 15298c2ecf20Sopenharmony_ci np->xstats.tx_late_collisions += late_coll; 15308c2ecf20Sopenharmony_ci dev->stats.collisions += mult_coll 15318c2ecf20Sopenharmony_ci + single_coll 15328c2ecf20Sopenharmony_ci + late_coll; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci np->xstats.tx_deferred += ioread8(ioaddr + StatsTxDefer); 15358c2ecf20Sopenharmony_ci np->xstats.tx_deferred_excessive += ioread8(ioaddr + StatsTxXSDefer); 15368c2ecf20Sopenharmony_ci np->xstats.tx_aborted += ioread8(ioaddr + StatsTxAbort); 15378c2ecf20Sopenharmony_ci np->xstats.tx_bcasts += ioread8(ioaddr + StatsBcastTx); 15388c2ecf20Sopenharmony_ci np->xstats.rx_bcasts += ioread8(ioaddr + StatsBcastRx); 15398c2ecf20Sopenharmony_ci np->xstats.tx_mcasts += ioread8(ioaddr + StatsMcastTx); 15408c2ecf20Sopenharmony_ci np->xstats.rx_mcasts += ioread8(ioaddr + StatsMcastRx); 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci dev->stats.tx_bytes += ioread16(ioaddr + TxOctetsLow); 15438c2ecf20Sopenharmony_ci dev->stats.tx_bytes += ioread16(ioaddr + TxOctetsHigh) << 16; 15448c2ecf20Sopenharmony_ci dev->stats.rx_bytes += ioread16(ioaddr + RxOctetsLow); 15458c2ecf20Sopenharmony_ci dev->stats.rx_bytes += ioread16(ioaddr + RxOctetsHigh) << 16; 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&np->statlock, flags); 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci return &dev->stats; 15508c2ecf20Sopenharmony_ci} 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_cistatic void set_rx_mode(struct net_device *dev) 15538c2ecf20Sopenharmony_ci{ 15548c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 15558c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->base; 15568c2ecf20Sopenharmony_ci u16 mc_filter[4]; /* Multicast hash filter */ 15578c2ecf20Sopenharmony_ci u32 rx_mode; 15588c2ecf20Sopenharmony_ci int i; 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ 15618c2ecf20Sopenharmony_ci memset(mc_filter, 0xff, sizeof(mc_filter)); 15628c2ecf20Sopenharmony_ci rx_mode = AcceptBroadcast | AcceptMulticast | AcceptAll | AcceptMyPhys; 15638c2ecf20Sopenharmony_ci } else if ((netdev_mc_count(dev) > multicast_filter_limit) || 15648c2ecf20Sopenharmony_ci (dev->flags & IFF_ALLMULTI)) { 15658c2ecf20Sopenharmony_ci /* Too many to match, or accept all multicasts. */ 15668c2ecf20Sopenharmony_ci memset(mc_filter, 0xff, sizeof(mc_filter)); 15678c2ecf20Sopenharmony_ci rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; 15688c2ecf20Sopenharmony_ci } else if (!netdev_mc_empty(dev)) { 15698c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 15708c2ecf20Sopenharmony_ci int bit; 15718c2ecf20Sopenharmony_ci int index; 15728c2ecf20Sopenharmony_ci int crc; 15738c2ecf20Sopenharmony_ci memset (mc_filter, 0, sizeof (mc_filter)); 15748c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 15758c2ecf20Sopenharmony_ci crc = ether_crc_le(ETH_ALEN, ha->addr); 15768c2ecf20Sopenharmony_ci for (index=0, bit=0; bit < 6; bit++, crc <<= 1) 15778c2ecf20Sopenharmony_ci if (crc & 0x80000000) index |= 1 << bit; 15788c2ecf20Sopenharmony_ci mc_filter[index/16] |= (1 << (index % 16)); 15798c2ecf20Sopenharmony_ci } 15808c2ecf20Sopenharmony_ci rx_mode = AcceptBroadcast | AcceptMultiHash | AcceptMyPhys; 15818c2ecf20Sopenharmony_ci } else { 15828c2ecf20Sopenharmony_ci iowrite8(AcceptBroadcast | AcceptMyPhys, ioaddr + RxMode); 15838c2ecf20Sopenharmony_ci return; 15848c2ecf20Sopenharmony_ci } 15858c2ecf20Sopenharmony_ci if (np->mii_if.full_duplex && np->flowctrl) 15868c2ecf20Sopenharmony_ci mc_filter[3] |= 0x0200; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 15898c2ecf20Sopenharmony_ci iowrite16(mc_filter[i], ioaddr + MulticastFilter0 + i*2); 15908c2ecf20Sopenharmony_ci iowrite8(rx_mode, ioaddr + RxMode); 15918c2ecf20Sopenharmony_ci} 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_cistatic int __set_mac_addr(struct net_device *dev) 15948c2ecf20Sopenharmony_ci{ 15958c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 15968c2ecf20Sopenharmony_ci u16 addr16; 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci addr16 = (dev->dev_addr[0] | (dev->dev_addr[1] << 8)); 15998c2ecf20Sopenharmony_ci iowrite16(addr16, np->base + StationAddr); 16008c2ecf20Sopenharmony_ci addr16 = (dev->dev_addr[2] | (dev->dev_addr[3] << 8)); 16018c2ecf20Sopenharmony_ci iowrite16(addr16, np->base + StationAddr+2); 16028c2ecf20Sopenharmony_ci addr16 = (dev->dev_addr[4] | (dev->dev_addr[5] << 8)); 16038c2ecf20Sopenharmony_ci iowrite16(addr16, np->base + StationAddr+4); 16048c2ecf20Sopenharmony_ci return 0; 16058c2ecf20Sopenharmony_ci} 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci/* Invoked with rtnl_lock held */ 16088c2ecf20Sopenharmony_cistatic int sundance_set_mac_addr(struct net_device *dev, void *data) 16098c2ecf20Sopenharmony_ci{ 16108c2ecf20Sopenharmony_ci const struct sockaddr *addr = data; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 16138c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 16148c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); 16158c2ecf20Sopenharmony_ci __set_mac_addr(dev); 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci return 0; 16188c2ecf20Sopenharmony_ci} 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_cistatic const struct { 16218c2ecf20Sopenharmony_ci const char name[ETH_GSTRING_LEN]; 16228c2ecf20Sopenharmony_ci} sundance_stats[] = { 16238c2ecf20Sopenharmony_ci { "tx_multiple_collisions" }, 16248c2ecf20Sopenharmony_ci { "tx_single_collisions" }, 16258c2ecf20Sopenharmony_ci { "tx_late_collisions" }, 16268c2ecf20Sopenharmony_ci { "tx_deferred" }, 16278c2ecf20Sopenharmony_ci { "tx_deferred_excessive" }, 16288c2ecf20Sopenharmony_ci { "tx_aborted" }, 16298c2ecf20Sopenharmony_ci { "tx_bcasts" }, 16308c2ecf20Sopenharmony_ci { "rx_bcasts" }, 16318c2ecf20Sopenharmony_ci { "tx_mcasts" }, 16328c2ecf20Sopenharmony_ci { "rx_mcasts" }, 16338c2ecf20Sopenharmony_ci}; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_cistatic int check_if_running(struct net_device *dev) 16368c2ecf20Sopenharmony_ci{ 16378c2ecf20Sopenharmony_ci if (!netif_running(dev)) 16388c2ecf20Sopenharmony_ci return -EINVAL; 16398c2ecf20Sopenharmony_ci return 0; 16408c2ecf20Sopenharmony_ci} 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_cistatic void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) 16438c2ecf20Sopenharmony_ci{ 16448c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 16458c2ecf20Sopenharmony_ci strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); 16468c2ecf20Sopenharmony_ci strlcpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); 16478c2ecf20Sopenharmony_ci} 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_cistatic int get_link_ksettings(struct net_device *dev, 16508c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 16518c2ecf20Sopenharmony_ci{ 16528c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 16538c2ecf20Sopenharmony_ci spin_lock_irq(&np->lock); 16548c2ecf20Sopenharmony_ci mii_ethtool_get_link_ksettings(&np->mii_if, cmd); 16558c2ecf20Sopenharmony_ci spin_unlock_irq(&np->lock); 16568c2ecf20Sopenharmony_ci return 0; 16578c2ecf20Sopenharmony_ci} 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_cistatic int set_link_ksettings(struct net_device *dev, 16608c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 16618c2ecf20Sopenharmony_ci{ 16628c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 16638c2ecf20Sopenharmony_ci int res; 16648c2ecf20Sopenharmony_ci spin_lock_irq(&np->lock); 16658c2ecf20Sopenharmony_ci res = mii_ethtool_set_link_ksettings(&np->mii_if, cmd); 16668c2ecf20Sopenharmony_ci spin_unlock_irq(&np->lock); 16678c2ecf20Sopenharmony_ci return res; 16688c2ecf20Sopenharmony_ci} 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_cistatic int nway_reset(struct net_device *dev) 16718c2ecf20Sopenharmony_ci{ 16728c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 16738c2ecf20Sopenharmony_ci return mii_nway_restart(&np->mii_if); 16748c2ecf20Sopenharmony_ci} 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_cistatic u32 get_link(struct net_device *dev) 16778c2ecf20Sopenharmony_ci{ 16788c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 16798c2ecf20Sopenharmony_ci return mii_link_ok(&np->mii_if); 16808c2ecf20Sopenharmony_ci} 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_cistatic u32 get_msglevel(struct net_device *dev) 16838c2ecf20Sopenharmony_ci{ 16848c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 16858c2ecf20Sopenharmony_ci return np->msg_enable; 16868c2ecf20Sopenharmony_ci} 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_cistatic void set_msglevel(struct net_device *dev, u32 val) 16898c2ecf20Sopenharmony_ci{ 16908c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 16918c2ecf20Sopenharmony_ci np->msg_enable = val; 16928c2ecf20Sopenharmony_ci} 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_cistatic void get_strings(struct net_device *dev, u32 stringset, 16958c2ecf20Sopenharmony_ci u8 *data) 16968c2ecf20Sopenharmony_ci{ 16978c2ecf20Sopenharmony_ci if (stringset == ETH_SS_STATS) 16988c2ecf20Sopenharmony_ci memcpy(data, sundance_stats, sizeof(sundance_stats)); 16998c2ecf20Sopenharmony_ci} 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_cistatic int get_sset_count(struct net_device *dev, int sset) 17028c2ecf20Sopenharmony_ci{ 17038c2ecf20Sopenharmony_ci switch (sset) { 17048c2ecf20Sopenharmony_ci case ETH_SS_STATS: 17058c2ecf20Sopenharmony_ci return ARRAY_SIZE(sundance_stats); 17068c2ecf20Sopenharmony_ci default: 17078c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 17088c2ecf20Sopenharmony_ci } 17098c2ecf20Sopenharmony_ci} 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_cistatic void get_ethtool_stats(struct net_device *dev, 17128c2ecf20Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 17138c2ecf20Sopenharmony_ci{ 17148c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 17158c2ecf20Sopenharmony_ci int i = 0; 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci get_stats(dev); 17188c2ecf20Sopenharmony_ci data[i++] = np->xstats.tx_multiple_collisions; 17198c2ecf20Sopenharmony_ci data[i++] = np->xstats.tx_single_collisions; 17208c2ecf20Sopenharmony_ci data[i++] = np->xstats.tx_late_collisions; 17218c2ecf20Sopenharmony_ci data[i++] = np->xstats.tx_deferred; 17228c2ecf20Sopenharmony_ci data[i++] = np->xstats.tx_deferred_excessive; 17238c2ecf20Sopenharmony_ci data[i++] = np->xstats.tx_aborted; 17248c2ecf20Sopenharmony_ci data[i++] = np->xstats.tx_bcasts; 17258c2ecf20Sopenharmony_ci data[i++] = np->xstats.rx_bcasts; 17268c2ecf20Sopenharmony_ci data[i++] = np->xstats.tx_mcasts; 17278c2ecf20Sopenharmony_ci data[i++] = np->xstats.rx_mcasts; 17288c2ecf20Sopenharmony_ci} 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_cistatic void sundance_get_wol(struct net_device *dev, 17338c2ecf20Sopenharmony_ci struct ethtool_wolinfo *wol) 17348c2ecf20Sopenharmony_ci{ 17358c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 17368c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->base; 17378c2ecf20Sopenharmony_ci u8 wol_bits; 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci wol->wolopts = 0; 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci wol->supported = (WAKE_PHY | WAKE_MAGIC); 17428c2ecf20Sopenharmony_ci if (!np->wol_enabled) 17438c2ecf20Sopenharmony_ci return; 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci wol_bits = ioread8(ioaddr + WakeEvent); 17468c2ecf20Sopenharmony_ci if (wol_bits & MagicPktEnable) 17478c2ecf20Sopenharmony_ci wol->wolopts |= WAKE_MAGIC; 17488c2ecf20Sopenharmony_ci if (wol_bits & LinkEventEnable) 17498c2ecf20Sopenharmony_ci wol->wolopts |= WAKE_PHY; 17508c2ecf20Sopenharmony_ci} 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_cistatic int sundance_set_wol(struct net_device *dev, 17538c2ecf20Sopenharmony_ci struct ethtool_wolinfo *wol) 17548c2ecf20Sopenharmony_ci{ 17558c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 17568c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->base; 17578c2ecf20Sopenharmony_ci u8 wol_bits; 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci if (!device_can_wakeup(&np->pci_dev->dev)) 17608c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci np->wol_enabled = !!(wol->wolopts); 17638c2ecf20Sopenharmony_ci wol_bits = ioread8(ioaddr + WakeEvent); 17648c2ecf20Sopenharmony_ci wol_bits &= ~(WakePktEnable | MagicPktEnable | 17658c2ecf20Sopenharmony_ci LinkEventEnable | WolEnable); 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci if (np->wol_enabled) { 17688c2ecf20Sopenharmony_ci if (wol->wolopts & WAKE_MAGIC) 17698c2ecf20Sopenharmony_ci wol_bits |= (MagicPktEnable | WolEnable); 17708c2ecf20Sopenharmony_ci if (wol->wolopts & WAKE_PHY) 17718c2ecf20Sopenharmony_ci wol_bits |= (LinkEventEnable | WolEnable); 17728c2ecf20Sopenharmony_ci } 17738c2ecf20Sopenharmony_ci iowrite8(wol_bits, ioaddr + WakeEvent); 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ci device_set_wakeup_enable(&np->pci_dev->dev, np->wol_enabled); 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci return 0; 17788c2ecf20Sopenharmony_ci} 17798c2ecf20Sopenharmony_ci#else 17808c2ecf20Sopenharmony_ci#define sundance_get_wol NULL 17818c2ecf20Sopenharmony_ci#define sundance_set_wol NULL 17828c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_cistatic const struct ethtool_ops ethtool_ops = { 17858c2ecf20Sopenharmony_ci .begin = check_if_running, 17868c2ecf20Sopenharmony_ci .get_drvinfo = get_drvinfo, 17878c2ecf20Sopenharmony_ci .nway_reset = nway_reset, 17888c2ecf20Sopenharmony_ci .get_link = get_link, 17898c2ecf20Sopenharmony_ci .get_wol = sundance_get_wol, 17908c2ecf20Sopenharmony_ci .set_wol = sundance_set_wol, 17918c2ecf20Sopenharmony_ci .get_msglevel = get_msglevel, 17928c2ecf20Sopenharmony_ci .set_msglevel = set_msglevel, 17938c2ecf20Sopenharmony_ci .get_strings = get_strings, 17948c2ecf20Sopenharmony_ci .get_sset_count = get_sset_count, 17958c2ecf20Sopenharmony_ci .get_ethtool_stats = get_ethtool_stats, 17968c2ecf20Sopenharmony_ci .get_link_ksettings = get_link_ksettings, 17978c2ecf20Sopenharmony_ci .set_link_ksettings = set_link_ksettings, 17988c2ecf20Sopenharmony_ci}; 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_cistatic int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 18018c2ecf20Sopenharmony_ci{ 18028c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 18038c2ecf20Sopenharmony_ci int rc; 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci if (!netif_running(dev)) 18068c2ecf20Sopenharmony_ci return -EINVAL; 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci spin_lock_irq(&np->lock); 18098c2ecf20Sopenharmony_ci rc = generic_mii_ioctl(&np->mii_if, if_mii(rq), cmd, NULL); 18108c2ecf20Sopenharmony_ci spin_unlock_irq(&np->lock); 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci return rc; 18138c2ecf20Sopenharmony_ci} 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_cistatic int netdev_close(struct net_device *dev) 18168c2ecf20Sopenharmony_ci{ 18178c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 18188c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->base; 18198c2ecf20Sopenharmony_ci struct sk_buff *skb; 18208c2ecf20Sopenharmony_ci int i; 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci /* Wait and kill tasklet */ 18238c2ecf20Sopenharmony_ci tasklet_kill(&np->rx_tasklet); 18248c2ecf20Sopenharmony_ci tasklet_kill(&np->tx_tasklet); 18258c2ecf20Sopenharmony_ci np->cur_tx = 0; 18268c2ecf20Sopenharmony_ci np->dirty_tx = 0; 18278c2ecf20Sopenharmony_ci np->cur_task = 0; 18288c2ecf20Sopenharmony_ci np->last_tx = NULL; 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci netif_stop_queue(dev); 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci if (netif_msg_ifdown(np)) { 18338c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Shutting down ethercard, status was Tx %2.2x " 18348c2ecf20Sopenharmony_ci "Rx %4.4x Int %2.2x.\n", 18358c2ecf20Sopenharmony_ci dev->name, ioread8(ioaddr + TxStatus), 18368c2ecf20Sopenharmony_ci ioread32(ioaddr + RxStatus), ioread16(ioaddr + IntrStatus)); 18378c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d, Rx %d / %d.\n", 18388c2ecf20Sopenharmony_ci dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx); 18398c2ecf20Sopenharmony_ci } 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci /* Disable interrupts by clearing the interrupt mask. */ 18428c2ecf20Sopenharmony_ci iowrite16(0x0000, ioaddr + IntrEnable); 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci /* Disable Rx and Tx DMA for safely release resource */ 18458c2ecf20Sopenharmony_ci iowrite32(0x500, ioaddr + DMACtrl); 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci /* Stop the chip's Tx and Rx processes. */ 18488c2ecf20Sopenharmony_ci iowrite16(TxDisable | RxDisable | StatsDisable, ioaddr + MACCtrl1); 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci for (i = 2000; i > 0; i--) { 18518c2ecf20Sopenharmony_ci if ((ioread32(ioaddr + DMACtrl) & 0xc000) == 0) 18528c2ecf20Sopenharmony_ci break; 18538c2ecf20Sopenharmony_ci mdelay(1); 18548c2ecf20Sopenharmony_ci } 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci iowrite16(GlobalReset | DMAReset | FIFOReset | NetworkReset, 18578c2ecf20Sopenharmony_ci ioaddr + ASIC_HI_WORD(ASICCtrl)); 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci for (i = 2000; i > 0; i--) { 18608c2ecf20Sopenharmony_ci if ((ioread16(ioaddr + ASIC_HI_WORD(ASICCtrl)) & ResetBusy) == 0) 18618c2ecf20Sopenharmony_ci break; 18628c2ecf20Sopenharmony_ci mdelay(1); 18638c2ecf20Sopenharmony_ci } 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci#ifdef __i386__ 18668c2ecf20Sopenharmony_ci if (netif_msg_hw(np)) { 18678c2ecf20Sopenharmony_ci printk(KERN_DEBUG " Tx ring at %8.8x:\n", 18688c2ecf20Sopenharmony_ci (int)(np->tx_ring_dma)); 18698c2ecf20Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) 18708c2ecf20Sopenharmony_ci printk(KERN_DEBUG " #%d desc. %4.4x %8.8x %8.8x.\n", 18718c2ecf20Sopenharmony_ci i, np->tx_ring[i].status, np->tx_ring[i].frag[0].addr, 18728c2ecf20Sopenharmony_ci np->tx_ring[i].frag[0].length); 18738c2ecf20Sopenharmony_ci printk(KERN_DEBUG " Rx ring %8.8x:\n", 18748c2ecf20Sopenharmony_ci (int)(np->rx_ring_dma)); 18758c2ecf20Sopenharmony_ci for (i = 0; i < /*RX_RING_SIZE*/4 ; i++) { 18768c2ecf20Sopenharmony_ci printk(KERN_DEBUG " #%d desc. %4.4x %4.4x %8.8x\n", 18778c2ecf20Sopenharmony_ci i, np->rx_ring[i].status, np->rx_ring[i].frag[0].addr, 18788c2ecf20Sopenharmony_ci np->rx_ring[i].frag[0].length); 18798c2ecf20Sopenharmony_ci } 18808c2ecf20Sopenharmony_ci } 18818c2ecf20Sopenharmony_ci#endif /* __i386__ debugging only */ 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci free_irq(np->pci_dev->irq, dev); 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci del_timer_sync(&np->timer); 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci /* Free all the skbuffs in the Rx queue. */ 18888c2ecf20Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 18898c2ecf20Sopenharmony_ci np->rx_ring[i].status = 0; 18908c2ecf20Sopenharmony_ci skb = np->rx_skbuff[i]; 18918c2ecf20Sopenharmony_ci if (skb) { 18928c2ecf20Sopenharmony_ci dma_unmap_single(&np->pci_dev->dev, 18938c2ecf20Sopenharmony_ci le32_to_cpu(np->rx_ring[i].frag[0].addr), 18948c2ecf20Sopenharmony_ci np->rx_buf_sz, DMA_FROM_DEVICE); 18958c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 18968c2ecf20Sopenharmony_ci np->rx_skbuff[i] = NULL; 18978c2ecf20Sopenharmony_ci } 18988c2ecf20Sopenharmony_ci np->rx_ring[i].frag[0].addr = cpu_to_le32(0xBADF00D0); /* poison */ 18998c2ecf20Sopenharmony_ci } 19008c2ecf20Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) { 19018c2ecf20Sopenharmony_ci np->tx_ring[i].next_desc = 0; 19028c2ecf20Sopenharmony_ci skb = np->tx_skbuff[i]; 19038c2ecf20Sopenharmony_ci if (skb) { 19048c2ecf20Sopenharmony_ci dma_unmap_single(&np->pci_dev->dev, 19058c2ecf20Sopenharmony_ci le32_to_cpu(np->tx_ring[i].frag[0].addr), 19068c2ecf20Sopenharmony_ci skb->len, DMA_TO_DEVICE); 19078c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 19088c2ecf20Sopenharmony_ci np->tx_skbuff[i] = NULL; 19098c2ecf20Sopenharmony_ci } 19108c2ecf20Sopenharmony_ci } 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci return 0; 19138c2ecf20Sopenharmony_ci} 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_cistatic void sundance_remove1(struct pci_dev *pdev) 19168c2ecf20Sopenharmony_ci{ 19178c2ecf20Sopenharmony_ci struct net_device *dev = pci_get_drvdata(pdev); 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci if (dev) { 19208c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 19218c2ecf20Sopenharmony_ci unregister_netdev(dev); 19228c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, 19238c2ecf20Sopenharmony_ci np->rx_ring, np->rx_ring_dma); 19248c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, 19258c2ecf20Sopenharmony_ci np->tx_ring, np->tx_ring_dma); 19268c2ecf20Sopenharmony_ci pci_iounmap(pdev, np->base); 19278c2ecf20Sopenharmony_ci pci_release_regions(pdev); 19288c2ecf20Sopenharmony_ci free_netdev(dev); 19298c2ecf20Sopenharmony_ci } 19308c2ecf20Sopenharmony_ci} 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_cistatic int __maybe_unused sundance_suspend(struct device *dev_d) 19338c2ecf20Sopenharmony_ci{ 19348c2ecf20Sopenharmony_ci struct net_device *dev = dev_get_drvdata(dev_d); 19358c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 19368c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->base; 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci if (!netif_running(dev)) 19398c2ecf20Sopenharmony_ci return 0; 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci netdev_close(dev); 19428c2ecf20Sopenharmony_ci netif_device_detach(dev); 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci if (np->wol_enabled) { 19458c2ecf20Sopenharmony_ci iowrite8(AcceptBroadcast | AcceptMyPhys, ioaddr + RxMode); 19468c2ecf20Sopenharmony_ci iowrite16(RxEnable, ioaddr + MACCtrl1); 19478c2ecf20Sopenharmony_ci } 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci device_set_wakeup_enable(dev_d, np->wol_enabled); 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci return 0; 19528c2ecf20Sopenharmony_ci} 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_cistatic int __maybe_unused sundance_resume(struct device *dev_d) 19558c2ecf20Sopenharmony_ci{ 19568c2ecf20Sopenharmony_ci struct net_device *dev = dev_get_drvdata(dev_d); 19578c2ecf20Sopenharmony_ci int err = 0; 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci if (!netif_running(dev)) 19608c2ecf20Sopenharmony_ci return 0; 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ci err = netdev_open(dev); 19638c2ecf20Sopenharmony_ci if (err) { 19648c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Can't resume interface!\n", 19658c2ecf20Sopenharmony_ci dev->name); 19668c2ecf20Sopenharmony_ci goto out; 19678c2ecf20Sopenharmony_ci } 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci netif_device_attach(dev); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ciout: 19728c2ecf20Sopenharmony_ci return err; 19738c2ecf20Sopenharmony_ci} 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(sundance_pm_ops, sundance_suspend, sundance_resume); 19768c2ecf20Sopenharmony_ci 19778c2ecf20Sopenharmony_cistatic struct pci_driver sundance_driver = { 19788c2ecf20Sopenharmony_ci .name = DRV_NAME, 19798c2ecf20Sopenharmony_ci .id_table = sundance_pci_tbl, 19808c2ecf20Sopenharmony_ci .probe = sundance_probe1, 19818c2ecf20Sopenharmony_ci .remove = sundance_remove1, 19828c2ecf20Sopenharmony_ci .driver.pm = &sundance_pm_ops, 19838c2ecf20Sopenharmony_ci}; 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_cistatic int __init sundance_init(void) 19868c2ecf20Sopenharmony_ci{ 19878c2ecf20Sopenharmony_ci return pci_register_driver(&sundance_driver); 19888c2ecf20Sopenharmony_ci} 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_cistatic void __exit sundance_exit(void) 19918c2ecf20Sopenharmony_ci{ 19928c2ecf20Sopenharmony_ci pci_unregister_driver(&sundance_driver); 19938c2ecf20Sopenharmony_ci} 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_cimodule_init(sundance_init); 19968c2ecf20Sopenharmony_cimodule_exit(sundance_exit); 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci 1999