18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci Written 1997-1998 by Donald Becker. 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci This software may be used and distributed according to the terms 58c2ecf20Sopenharmony_ci of the GNU General Public License, incorporated herein by reference. 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci This driver is for the 3Com ISA EtherLink XL "Corkscrew" 3c515 ethercard. 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci The author may be reached as becker@scyld.com, or C/O 108c2ecf20Sopenharmony_ci Scyld Computing Corporation 118c2ecf20Sopenharmony_ci 410 Severn Ave., Suite 210 128c2ecf20Sopenharmony_ci Annapolis MD 21403 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci 2000/2/2- Added support for kernel-level ISAPnP 168c2ecf20Sopenharmony_ci by Stephen Frost <sfrost@snowman.net> and Alessandro Zummo 178c2ecf20Sopenharmony_ci Cleaned up for 2.3.x/softnet by Jeff Garzik and Alan Cox. 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci 2001/11/17 - Added ethtool support (jgarzik) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci 2002/10/28 - Locking updates for 2.5 (alan@lxorguk.ukuu.org.uk) 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci*/ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define DRV_NAME "3c515" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define CORKSCREW 1 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* "Knobs" that adjust features and parameters. */ 308c2ecf20Sopenharmony_ci/* Set the copy breakpoint for the copy-only-tiny-frames scheme. 318c2ecf20Sopenharmony_ci Setting to > 1512 effectively disables this feature. */ 328c2ecf20Sopenharmony_cistatic int rx_copybreak = 200; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */ 358c2ecf20Sopenharmony_cistatic const int mtu = 1500; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ 388c2ecf20Sopenharmony_cistatic int max_interrupt_work = 20; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* Enable the automatic media selection code -- usually set. */ 418c2ecf20Sopenharmony_ci#define AUTOMEDIA 1 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* Allow the use of fragment bus master transfers instead of only 448c2ecf20Sopenharmony_ci programmed-I/O for Vortex cards. Full-bus-master transfers are always 458c2ecf20Sopenharmony_ci enabled by default on Boomerang cards. If VORTEX_BUS_MASTER is defined, 468c2ecf20Sopenharmony_ci the feature may be turned on using 'options'. */ 478c2ecf20Sopenharmony_ci#define VORTEX_BUS_MASTER 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* A few values that may be tweaked. */ 508c2ecf20Sopenharmony_ci/* Keep the ring sizes a power of two for efficiency. */ 518c2ecf20Sopenharmony_ci#define TX_RING_SIZE 16 528c2ecf20Sopenharmony_ci#define RX_RING_SIZE 16 538c2ecf20Sopenharmony_ci#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */ 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#include <linux/module.h> 568c2ecf20Sopenharmony_ci#include <linux/isapnp.h> 578c2ecf20Sopenharmony_ci#include <linux/kernel.h> 588c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 598c2ecf20Sopenharmony_ci#include <linux/string.h> 608c2ecf20Sopenharmony_ci#include <linux/errno.h> 618c2ecf20Sopenharmony_ci#include <linux/in.h> 628c2ecf20Sopenharmony_ci#include <linux/ioport.h> 638c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 648c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 658c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 668c2ecf20Sopenharmony_ci#include <linux/timer.h> 678c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 688c2ecf20Sopenharmony_ci#include <linux/bitops.h> 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 718c2ecf20Sopenharmony_ci#include <asm/io.h> 728c2ecf20Sopenharmony_ci#include <asm/dma.h> 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define NEW_MULTICAST 758c2ecf20Sopenharmony_ci#include <linux/delay.h> 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define MAX_UNITS 8 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ciMODULE_AUTHOR("Donald Becker <becker@scyld.com>"); 808c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("3Com 3c515 Corkscrew driver"); 818c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* "Knobs" for adjusting internal parameters. */ 848c2ecf20Sopenharmony_ci/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */ 858c2ecf20Sopenharmony_ci#define DRIVER_DEBUG 1 868c2ecf20Sopenharmony_ci/* Some values here only for performance evaluation and path-coverage 878c2ecf20Sopenharmony_ci debugging. */ 888c2ecf20Sopenharmony_cistatic int rx_nocopy, rx_copy, queued_packet; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* Number of times to check to see if the Tx FIFO has space, used in some 918c2ecf20Sopenharmony_ci limited cases. */ 928c2ecf20Sopenharmony_ci#define WAIT_TX_AVAIL 200 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* Operational parameter that usually are not changed. */ 958c2ecf20Sopenharmony_ci#define TX_TIMEOUT ((4*HZ)/10) /* Time in jiffies before concluding Tx hung */ 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* The size here is somewhat misleading: the Corkscrew also uses the ISA 988c2ecf20Sopenharmony_ci aliased registers at <base>+0x400. 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_ci#define CORKSCREW_TOTAL_SIZE 0x20 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci#ifdef DRIVER_DEBUG 1038c2ecf20Sopenharmony_cistatic int corkscrew_debug = DRIVER_DEBUG; 1048c2ecf20Sopenharmony_ci#else 1058c2ecf20Sopenharmony_cistatic int corkscrew_debug = 1; 1068c2ecf20Sopenharmony_ci#endif 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci#define CORKSCREW_ID 10 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* 1118c2ecf20Sopenharmony_ci Theory of Operation 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ciI. Board Compatibility 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ciThis device driver is designed for the 3Com 3c515 ISA Fast EtherLink XL, 1168c2ecf20Sopenharmony_ci3Com's ISA bus adapter for Fast Ethernet. Due to the unique I/O port layout, 1178c2ecf20Sopenharmony_ciit's not practical to integrate this driver with the other EtherLink drivers. 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ciII. Board-specific settings 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ciThe Corkscrew has an EEPROM for configuration, but no special settings are 1228c2ecf20Sopenharmony_cineeded for Linux. 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ciIII. Driver operation 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ciThe 3c515 series use an interface that's very similar to the 3c900 "Boomerang" 1278c2ecf20Sopenharmony_ciPCI cards, with the bus master interface extensively modified to work with 1288c2ecf20Sopenharmony_cithe ISA bus. 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ciThe card is capable of full-bus-master transfers with separate 1318c2ecf20Sopenharmony_cilists of transmit and receive descriptors, similar to the AMD LANCE/PCnet, 1328c2ecf20Sopenharmony_ciDEC Tulip and Intel Speedo3. 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ciThis driver uses a "RX_COPYBREAK" scheme rather than a fixed intermediate 1358c2ecf20Sopenharmony_cireceive buffer. This scheme allocates full-sized skbuffs as receive 1368c2ecf20Sopenharmony_cibuffers. The value RX_COPYBREAK is used as the copying breakpoint: it is 1378c2ecf20Sopenharmony_cichosen to trade-off the memory wasted by passing the full-sized skbuff to 1388c2ecf20Sopenharmony_cithe queue layer for all frames vs. the copying cost of copying a frame to a 1398c2ecf20Sopenharmony_cicorrectly-sized skbuff. 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ciIIIC. Synchronization 1438c2ecf20Sopenharmony_ciThe driver runs as two independent, single-threaded flows of control. One 1448c2ecf20Sopenharmony_ciis the send-packet routine, which enforces single-threaded use by the netif 1458c2ecf20Sopenharmony_cilayer. The other thread is the interrupt handler, which is single 1468c2ecf20Sopenharmony_cithreaded by the hardware and other software. 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ciIV. Notes 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ciThanks to Terry Murphy of 3Com for providing documentation and a development 1518c2ecf20Sopenharmony_ciboard. 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ciThe names "Vortex", "Boomerang" and "Corkscrew" are the internal 3Com 1548c2ecf20Sopenharmony_ciproject names. I use these names to eliminate confusion -- 3Com product 1558c2ecf20Sopenharmony_cinumbers and names are very similar and often confused. 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ciThe new chips support both ethernet (1.5K) and FDDI (4.5K) frame sizes! 1588c2ecf20Sopenharmony_ciThis driver only supports ethernet frames because of the recent MTU limit 1598c2ecf20Sopenharmony_ciof 1.5K, but the changes to support 4.5K are minimal. 1608c2ecf20Sopenharmony_ci*/ 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* Operational definitions. 1638c2ecf20Sopenharmony_ci These are not used by other compilation units and thus are not 1648c2ecf20Sopenharmony_ci exported in a ".h" file. 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci First the windows. There are eight register windows, with the command 1678c2ecf20Sopenharmony_ci and status registers available in each. 1688c2ecf20Sopenharmony_ci */ 1698c2ecf20Sopenharmony_ci#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD) 1708c2ecf20Sopenharmony_ci#define EL3_CMD 0x0e 1718c2ecf20Sopenharmony_ci#define EL3_STATUS 0x0e 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/* The top five bits written to EL3_CMD are a command, the lower 1748c2ecf20Sopenharmony_ci 11 bits are the parameter, if applicable. 1758c2ecf20Sopenharmony_ci Note that 11 parameters bits was fine for ethernet, but the new chips 1768c2ecf20Sopenharmony_ci can handle FDDI length frames (~4500 octets) and now parameters count 1778c2ecf20Sopenharmony_ci 32-bit 'Dwords' rather than octets. */ 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cienum corkscrew_cmd { 1808c2ecf20Sopenharmony_ci TotalReset = 0 << 11, SelectWindow = 1 << 11, StartCoax = 2 << 11, 1818c2ecf20Sopenharmony_ci RxDisable = 3 << 11, RxEnable = 4 << 11, RxReset = 5 << 11, 1828c2ecf20Sopenharmony_ci UpStall = 6 << 11, UpUnstall = (6 << 11) + 1, DownStall = (6 << 11) + 2, 1838c2ecf20Sopenharmony_ci DownUnstall = (6 << 11) + 3, RxDiscard = 8 << 11, TxEnable = 9 << 11, 1848c2ecf20Sopenharmony_ci TxDisable = 10 << 11, TxReset = 11 << 11, FakeIntr = 12 << 11, 1858c2ecf20Sopenharmony_ci AckIntr = 13 << 11, SetIntrEnb = 14 << 11, SetStatusEnb = 15 << 11, 1868c2ecf20Sopenharmony_ci SetRxFilter = 16 << 11, SetRxThreshold = 17 << 11, 1878c2ecf20Sopenharmony_ci SetTxThreshold = 18 << 11, SetTxStart = 19 << 11, StartDMAUp = 20 << 11, 1888c2ecf20Sopenharmony_ci StartDMADown = (20 << 11) + 1, StatsEnable = 21 << 11, 1898c2ecf20Sopenharmony_ci StatsDisable = 22 << 11, StopCoax = 23 << 11, 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci/* The SetRxFilter command accepts the following classes: */ 1938c2ecf20Sopenharmony_cienum RxFilter { 1948c2ecf20Sopenharmony_ci RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 1958c2ecf20Sopenharmony_ci}; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/* Bits in the general status register. */ 1988c2ecf20Sopenharmony_cienum corkscrew_status { 1998c2ecf20Sopenharmony_ci IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004, 2008c2ecf20Sopenharmony_ci TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020, 2018c2ecf20Sopenharmony_ci IntReq = 0x0040, StatsFull = 0x0080, 2028c2ecf20Sopenharmony_ci DMADone = 1 << 8, DownComplete = 1 << 9, UpComplete = 1 << 10, 2038c2ecf20Sopenharmony_ci DMAInProgress = 1 << 11, /* DMA controller is still busy. */ 2048c2ecf20Sopenharmony_ci CmdInProgress = 1 << 12, /* EL3_CMD is still busy. */ 2058c2ecf20Sopenharmony_ci}; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/* Register window 1 offsets, the window used in normal operation. 2088c2ecf20Sopenharmony_ci On the Corkscrew this window is always mapped at offsets 0x10-0x1f. */ 2098c2ecf20Sopenharmony_cienum Window1 { 2108c2ecf20Sopenharmony_ci TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14, 2118c2ecf20Sopenharmony_ci RxStatus = 0x18, Timer = 0x1A, TxStatus = 0x1B, 2128c2ecf20Sopenharmony_ci TxFree = 0x1C, /* Remaining free bytes in Tx buffer. */ 2138c2ecf20Sopenharmony_ci}; 2148c2ecf20Sopenharmony_cienum Window0 { 2158c2ecf20Sopenharmony_ci Wn0IRQ = 0x08, 2168c2ecf20Sopenharmony_ci#if defined(CORKSCREW) 2178c2ecf20Sopenharmony_ci Wn0EepromCmd = 0x200A, /* Corkscrew EEPROM command register. */ 2188c2ecf20Sopenharmony_ci Wn0EepromData = 0x200C, /* Corkscrew EEPROM results register. */ 2198c2ecf20Sopenharmony_ci#else 2208c2ecf20Sopenharmony_ci Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */ 2218c2ecf20Sopenharmony_ci Wn0EepromData = 12, /* Window 0: EEPROM results register. */ 2228c2ecf20Sopenharmony_ci#endif 2238c2ecf20Sopenharmony_ci}; 2248c2ecf20Sopenharmony_cienum Win0_EEPROM_bits { 2258c2ecf20Sopenharmony_ci EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0, 2268c2ecf20Sopenharmony_ci EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */ 2278c2ecf20Sopenharmony_ci EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */ 2288c2ecf20Sopenharmony_ci}; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci/* EEPROM locations. */ 2318c2ecf20Sopenharmony_cienum eeprom_offset { 2328c2ecf20Sopenharmony_ci PhysAddr01 = 0, PhysAddr23 = 1, PhysAddr45 = 2, ModelID = 3, 2338c2ecf20Sopenharmony_ci EtherLink3ID = 7, 2348c2ecf20Sopenharmony_ci}; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cienum Window3 { /* Window 3: MAC/config bits. */ 2378c2ecf20Sopenharmony_ci Wn3_Config = 0, Wn3_MAC_Ctrl = 6, Wn3_Options = 8, 2388c2ecf20Sopenharmony_ci}; 2398c2ecf20Sopenharmony_cienum wn3_config { 2408c2ecf20Sopenharmony_ci Ram_size = 7, 2418c2ecf20Sopenharmony_ci Ram_width = 8, 2428c2ecf20Sopenharmony_ci Ram_speed = 0x30, 2438c2ecf20Sopenharmony_ci Rom_size = 0xc0, 2448c2ecf20Sopenharmony_ci Ram_split_shift = 16, 2458c2ecf20Sopenharmony_ci Ram_split = 3 << Ram_split_shift, 2468c2ecf20Sopenharmony_ci Xcvr_shift = 20, 2478c2ecf20Sopenharmony_ci Xcvr = 7 << Xcvr_shift, 2488c2ecf20Sopenharmony_ci Autoselect = 0x1000000, 2498c2ecf20Sopenharmony_ci}; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cienum Window4 { 2528c2ecf20Sopenharmony_ci Wn4_NetDiag = 6, Wn4_Media = 10, /* Window 4: Xcvr/media bits. */ 2538c2ecf20Sopenharmony_ci}; 2548c2ecf20Sopenharmony_cienum Win4_Media_bits { 2558c2ecf20Sopenharmony_ci Media_SQE = 0x0008, /* Enable SQE error counting for AUI. */ 2568c2ecf20Sopenharmony_ci Media_10TP = 0x00C0, /* Enable link beat and jabber for 10baseT. */ 2578c2ecf20Sopenharmony_ci Media_Lnk = 0x0080, /* Enable just link beat for 100TX/100FX. */ 2588c2ecf20Sopenharmony_ci Media_LnkBeat = 0x0800, 2598c2ecf20Sopenharmony_ci}; 2608c2ecf20Sopenharmony_cienum Window7 { /* Window 7: Bus Master control. */ 2618c2ecf20Sopenharmony_ci Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12, 2628c2ecf20Sopenharmony_ci}; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci/* Boomerang-style bus master control registers. Note ISA aliases! */ 2658c2ecf20Sopenharmony_cienum MasterCtrl { 2668c2ecf20Sopenharmony_ci PktStatus = 0x400, DownListPtr = 0x404, FragAddr = 0x408, FragLen = 2678c2ecf20Sopenharmony_ci 0x40c, 2688c2ecf20Sopenharmony_ci TxFreeThreshold = 0x40f, UpPktStatus = 0x410, UpListPtr = 0x418, 2698c2ecf20Sopenharmony_ci}; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci/* The Rx and Tx descriptor lists. 2728c2ecf20Sopenharmony_ci Caution Alpha hackers: these types are 32 bits! Note also the 8 byte 2738c2ecf20Sopenharmony_ci alignment contraint on tx_ring[] and rx_ring[]. */ 2748c2ecf20Sopenharmony_cistruct boom_rx_desc { 2758c2ecf20Sopenharmony_ci u32 next; 2768c2ecf20Sopenharmony_ci s32 status; 2778c2ecf20Sopenharmony_ci u32 addr; 2788c2ecf20Sopenharmony_ci s32 length; 2798c2ecf20Sopenharmony_ci}; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci/* Values for the Rx status entry. */ 2828c2ecf20Sopenharmony_cienum rx_desc_status { 2838c2ecf20Sopenharmony_ci RxDComplete = 0x00008000, RxDError = 0x4000, 2848c2ecf20Sopenharmony_ci /* See boomerang_rx() for actual error bits */ 2858c2ecf20Sopenharmony_ci}; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistruct boom_tx_desc { 2888c2ecf20Sopenharmony_ci u32 next; 2898c2ecf20Sopenharmony_ci s32 status; 2908c2ecf20Sopenharmony_ci u32 addr; 2918c2ecf20Sopenharmony_ci s32 length; 2928c2ecf20Sopenharmony_ci}; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistruct corkscrew_private { 2958c2ecf20Sopenharmony_ci const char *product_name; 2968c2ecf20Sopenharmony_ci struct list_head list; 2978c2ecf20Sopenharmony_ci struct net_device *our_dev; 2988c2ecf20Sopenharmony_ci /* The Rx and Tx rings are here to keep them quad-word-aligned. */ 2998c2ecf20Sopenharmony_ci struct boom_rx_desc rx_ring[RX_RING_SIZE]; 3008c2ecf20Sopenharmony_ci struct boom_tx_desc tx_ring[TX_RING_SIZE]; 3018c2ecf20Sopenharmony_ci /* The addresses of transmit- and receive-in-place skbuffs. */ 3028c2ecf20Sopenharmony_ci struct sk_buff *rx_skbuff[RX_RING_SIZE]; 3038c2ecf20Sopenharmony_ci struct sk_buff *tx_skbuff[TX_RING_SIZE]; 3048c2ecf20Sopenharmony_ci unsigned int cur_rx, cur_tx; /* The next free ring entry */ 3058c2ecf20Sopenharmony_ci unsigned int dirty_rx, dirty_tx;/* The ring entries to be free()ed. */ 3068c2ecf20Sopenharmony_ci struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */ 3078c2ecf20Sopenharmony_ci struct timer_list timer; /* Media selection timer. */ 3088c2ecf20Sopenharmony_ci int capabilities ; /* Adapter capabilities word. */ 3098c2ecf20Sopenharmony_ci int options; /* User-settable misc. driver options. */ 3108c2ecf20Sopenharmony_ci int last_rx_packets; /* For media autoselection. */ 3118c2ecf20Sopenharmony_ci unsigned int available_media:8, /* From Wn3_Options */ 3128c2ecf20Sopenharmony_ci media_override:3, /* Passed-in media type. */ 3138c2ecf20Sopenharmony_ci default_media:3, /* Read from the EEPROM. */ 3148c2ecf20Sopenharmony_ci full_duplex:1, autoselect:1, bus_master:1, /* Vortex can only do a fragment bus-m. */ 3158c2ecf20Sopenharmony_ci full_bus_master_tx:1, full_bus_master_rx:1, /* Boomerang */ 3168c2ecf20Sopenharmony_ci tx_full:1; 3178c2ecf20Sopenharmony_ci spinlock_t lock; 3188c2ecf20Sopenharmony_ci struct device *dev; 3198c2ecf20Sopenharmony_ci}; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci/* The action to take with a media selection timer tick. 3228c2ecf20Sopenharmony_ci Note that we deviate from the 3Com order by checking 10base2 before AUI. 3238c2ecf20Sopenharmony_ci */ 3248c2ecf20Sopenharmony_cienum xcvr_types { 3258c2ecf20Sopenharmony_ci XCVR_10baseT = 0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx, 3268c2ecf20Sopenharmony_ci XCVR_100baseFx, XCVR_MII = 6, XCVR_Default = 8, 3278c2ecf20Sopenharmony_ci}; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic struct media_table { 3308c2ecf20Sopenharmony_ci char *name; 3318c2ecf20Sopenharmony_ci unsigned int media_bits:16, /* Bits to set in Wn4_Media register. */ 3328c2ecf20Sopenharmony_ci mask:8, /* The transceiver-present bit in Wn3_Config. */ 3338c2ecf20Sopenharmony_ci next:8; /* The media type to try next. */ 3348c2ecf20Sopenharmony_ci short wait; /* Time before we check media status. */ 3358c2ecf20Sopenharmony_ci} media_tbl[] = { 3368c2ecf20Sopenharmony_ci { "10baseT", Media_10TP, 0x08, XCVR_10base2, (14 * HZ) / 10 }, 3378c2ecf20Sopenharmony_ci { "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1 * HZ) / 10}, 3388c2ecf20Sopenharmony_ci { "undefined", 0, 0x80, XCVR_10baseT, 10000}, 3398c2ecf20Sopenharmony_ci { "10base2", 0, 0x10, XCVR_AUI, (1 * HZ) / 10}, 3408c2ecf20Sopenharmony_ci { "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, (14 * HZ) / 10}, 3418c2ecf20Sopenharmony_ci { "100baseFX", Media_Lnk, 0x04, XCVR_MII, (14 * HZ) / 10}, 3428c2ecf20Sopenharmony_ci { "MII", 0, 0x40, XCVR_10baseT, 3 * HZ}, 3438c2ecf20Sopenharmony_ci { "undefined", 0, 0x01, XCVR_10baseT, 10000}, 3448c2ecf20Sopenharmony_ci { "Default", 0, 0xFF, XCVR_10baseT, 10000}, 3458c2ecf20Sopenharmony_ci}; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci#ifdef __ISAPNP__ 3488c2ecf20Sopenharmony_cistatic struct isapnp_device_id corkscrew_isapnp_adapters[] = { 3498c2ecf20Sopenharmony_ci { ISAPNP_ANY_ID, ISAPNP_ANY_ID, 3508c2ecf20Sopenharmony_ci ISAPNP_VENDOR('T', 'C', 'M'), ISAPNP_FUNCTION(0x5051), 3518c2ecf20Sopenharmony_ci (long) "3Com Fast EtherLink ISA" }, 3528c2ecf20Sopenharmony_ci { } /* terminate list */ 3538c2ecf20Sopenharmony_ci}; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(isapnp, corkscrew_isapnp_adapters); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic int nopnp; 3588c2ecf20Sopenharmony_ci#endif /* __ISAPNP__ */ 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic struct net_device *corkscrew_scan(int unit); 3618c2ecf20Sopenharmony_cistatic int corkscrew_setup(struct net_device *dev, int ioaddr, 3628c2ecf20Sopenharmony_ci struct pnp_dev *idev, int card_number); 3638c2ecf20Sopenharmony_cistatic int corkscrew_open(struct net_device *dev); 3648c2ecf20Sopenharmony_cistatic void corkscrew_timer(struct timer_list *t); 3658c2ecf20Sopenharmony_cistatic netdev_tx_t corkscrew_start_xmit(struct sk_buff *skb, 3668c2ecf20Sopenharmony_ci struct net_device *dev); 3678c2ecf20Sopenharmony_cistatic int corkscrew_rx(struct net_device *dev); 3688c2ecf20Sopenharmony_cistatic void corkscrew_timeout(struct net_device *dev, unsigned int txqueue); 3698c2ecf20Sopenharmony_cistatic int boomerang_rx(struct net_device *dev); 3708c2ecf20Sopenharmony_cistatic irqreturn_t corkscrew_interrupt(int irq, void *dev_id); 3718c2ecf20Sopenharmony_cistatic int corkscrew_close(struct net_device *dev); 3728c2ecf20Sopenharmony_cistatic void update_stats(int addr, struct net_device *dev); 3738c2ecf20Sopenharmony_cistatic struct net_device_stats *corkscrew_get_stats(struct net_device *dev); 3748c2ecf20Sopenharmony_cistatic void set_rx_mode(struct net_device *dev); 3758c2ecf20Sopenharmony_cistatic const struct ethtool_ops netdev_ethtool_ops; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci/* 3798c2ecf20Sopenharmony_ci Unfortunately maximizing the shared code between the integrated and 3808c2ecf20Sopenharmony_ci module version of the driver results in a complicated set of initialization 3818c2ecf20Sopenharmony_ci procedures. 3828c2ecf20Sopenharmony_ci init_module() -- modules / tc59x_init() -- built-in 3838c2ecf20Sopenharmony_ci The wrappers for corkscrew_scan() 3848c2ecf20Sopenharmony_ci corkscrew_scan() The common routine that scans for PCI and EISA cards 3858c2ecf20Sopenharmony_ci corkscrew_found_device() Allocate a device structure when we find a card. 3868c2ecf20Sopenharmony_ci Different versions exist for modules and built-in. 3878c2ecf20Sopenharmony_ci corkscrew_probe1() Fill in the device structure -- this is separated 3888c2ecf20Sopenharmony_ci so that the modules code can put it in dev->init. 3898c2ecf20Sopenharmony_ci*/ 3908c2ecf20Sopenharmony_ci/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */ 3918c2ecf20Sopenharmony_ci/* Note: this is the only limit on the number of cards supported!! */ 3928c2ecf20Sopenharmony_cistatic int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1, }; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci#ifdef MODULE 3958c2ecf20Sopenharmony_cistatic int debug = -1; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cimodule_param(debug, int, 0); 3988c2ecf20Sopenharmony_cimodule_param_array(options, int, NULL, 0); 3998c2ecf20Sopenharmony_cimodule_param(rx_copybreak, int, 0); 4008c2ecf20Sopenharmony_cimodule_param(max_interrupt_work, int, 0); 4018c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "3c515 debug level (0-6)"); 4028c2ecf20Sopenharmony_ciMODULE_PARM_DESC(options, "3c515: Bits 0-2: media type, bit 3: full duplex, bit 4: bus mastering"); 4038c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rx_copybreak, "3c515 copy breakpoint for copy-only-tiny-frames"); 4048c2ecf20Sopenharmony_ciMODULE_PARM_DESC(max_interrupt_work, "3c515 maximum events handled per interrupt"); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci/* A list of all installed Vortex devices, for removing the driver module. */ 4078c2ecf20Sopenharmony_ci/* we will need locking (and refcounting) if we ever use it for more */ 4088c2ecf20Sopenharmony_cistatic LIST_HEAD(root_corkscrew_dev); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ciint init_module(void) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci int found = 0; 4138c2ecf20Sopenharmony_ci if (debug >= 0) 4148c2ecf20Sopenharmony_ci corkscrew_debug = debug; 4158c2ecf20Sopenharmony_ci while (corkscrew_scan(-1)) 4168c2ecf20Sopenharmony_ci found++; 4178c2ecf20Sopenharmony_ci return found ? 0 : -ENODEV; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci#else 4218c2ecf20Sopenharmony_cistruct net_device *tc515_probe(int unit) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct net_device *dev = corkscrew_scan(unit); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (!dev) 4268c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci return dev; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci#endif /* not MODULE */ 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic int check_device(unsigned ioaddr) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci int timer; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (!request_region(ioaddr, CORKSCREW_TOTAL_SIZE, "3c515")) 4378c2ecf20Sopenharmony_ci return 0; 4388c2ecf20Sopenharmony_ci /* Check the resource configuration for a matching ioaddr. */ 4398c2ecf20Sopenharmony_ci if ((inw(ioaddr + 0x2002) & 0x1f0) != (ioaddr & 0x1f0)) { 4408c2ecf20Sopenharmony_ci release_region(ioaddr, CORKSCREW_TOTAL_SIZE); 4418c2ecf20Sopenharmony_ci return 0; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci /* Verify by reading the device ID from the EEPROM. */ 4448c2ecf20Sopenharmony_ci outw(EEPROM_Read + 7, ioaddr + Wn0EepromCmd); 4458c2ecf20Sopenharmony_ci /* Pause for at least 162 us. for the read to take place. */ 4468c2ecf20Sopenharmony_ci for (timer = 4; timer >= 0; timer--) { 4478c2ecf20Sopenharmony_ci udelay(162); 4488c2ecf20Sopenharmony_ci if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0) 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci if (inw(ioaddr + Wn0EepromData) != 0x6d50) { 4528c2ecf20Sopenharmony_ci release_region(ioaddr, CORKSCREW_TOTAL_SIZE); 4538c2ecf20Sopenharmony_ci return 0; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci return 1; 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic void cleanup_card(struct net_device *dev) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci struct corkscrew_private *vp = netdev_priv(dev); 4618c2ecf20Sopenharmony_ci list_del_init(&vp->list); 4628c2ecf20Sopenharmony_ci if (dev->dma) 4638c2ecf20Sopenharmony_ci free_dma(dev->dma); 4648c2ecf20Sopenharmony_ci outw(TotalReset, dev->base_addr + EL3_CMD); 4658c2ecf20Sopenharmony_ci release_region(dev->base_addr, CORKSCREW_TOTAL_SIZE); 4668c2ecf20Sopenharmony_ci if (vp->dev) 4678c2ecf20Sopenharmony_ci pnp_device_detach(to_pnp_dev(vp->dev)); 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic struct net_device *corkscrew_scan(int unit) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci struct net_device *dev; 4738c2ecf20Sopenharmony_ci static int cards_found = 0; 4748c2ecf20Sopenharmony_ci static int ioaddr; 4758c2ecf20Sopenharmony_ci int err; 4768c2ecf20Sopenharmony_ci#ifdef __ISAPNP__ 4778c2ecf20Sopenharmony_ci short i; 4788c2ecf20Sopenharmony_ci static int pnp_cards; 4798c2ecf20Sopenharmony_ci#endif 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(struct corkscrew_private)); 4828c2ecf20Sopenharmony_ci if (!dev) 4838c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (unit >= 0) { 4868c2ecf20Sopenharmony_ci sprintf(dev->name, "eth%d", unit); 4878c2ecf20Sopenharmony_ci netdev_boot_setup_check(dev); 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci#ifdef __ISAPNP__ 4918c2ecf20Sopenharmony_ci if(nopnp == 1) 4928c2ecf20Sopenharmony_ci goto no_pnp; 4938c2ecf20Sopenharmony_ci for(i=0; corkscrew_isapnp_adapters[i].vendor != 0; i++) { 4948c2ecf20Sopenharmony_ci struct pnp_dev *idev = NULL; 4958c2ecf20Sopenharmony_ci int irq; 4968c2ecf20Sopenharmony_ci while((idev = pnp_find_dev(NULL, 4978c2ecf20Sopenharmony_ci corkscrew_isapnp_adapters[i].vendor, 4988c2ecf20Sopenharmony_ci corkscrew_isapnp_adapters[i].function, 4998c2ecf20Sopenharmony_ci idev))) { 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (pnp_device_attach(idev) < 0) 5028c2ecf20Sopenharmony_ci continue; 5038c2ecf20Sopenharmony_ci if (pnp_activate_dev(idev) < 0) { 5048c2ecf20Sopenharmony_ci pr_warn("pnp activate failed (out of resources?)\n"); 5058c2ecf20Sopenharmony_ci pnp_device_detach(idev); 5068c2ecf20Sopenharmony_ci continue; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) { 5098c2ecf20Sopenharmony_ci pnp_device_detach(idev); 5108c2ecf20Sopenharmony_ci continue; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci ioaddr = pnp_port_start(idev, 0); 5138c2ecf20Sopenharmony_ci irq = pnp_irq(idev, 0); 5148c2ecf20Sopenharmony_ci if (!check_device(ioaddr)) { 5158c2ecf20Sopenharmony_ci pnp_device_detach(idev); 5168c2ecf20Sopenharmony_ci continue; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci if(corkscrew_debug) 5198c2ecf20Sopenharmony_ci pr_debug("ISAPNP reports %s at i/o 0x%x, irq %d\n", 5208c2ecf20Sopenharmony_ci (char*) corkscrew_isapnp_adapters[i].driver_data, ioaddr, irq); 5218c2ecf20Sopenharmony_ci pr_info("3c515 Resource configuration register %#4.4x, DCR %4.4x.\n", 5228c2ecf20Sopenharmony_ci inl(ioaddr + 0x2002), inw(ioaddr + 0x2000)); 5238c2ecf20Sopenharmony_ci /* irq = inw(ioaddr + 0x2002) & 15; */ /* Use the irq from isapnp */ 5248c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &idev->dev); 5258c2ecf20Sopenharmony_ci pnp_cards++; 5268c2ecf20Sopenharmony_ci err = corkscrew_setup(dev, ioaddr, idev, cards_found++); 5278c2ecf20Sopenharmony_ci if (!err) 5288c2ecf20Sopenharmony_ci return dev; 5298c2ecf20Sopenharmony_ci cleanup_card(dev); 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_cino_pnp: 5338c2ecf20Sopenharmony_ci#endif /* __ISAPNP__ */ 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* Check all locations on the ISA bus -- evil! */ 5368c2ecf20Sopenharmony_ci for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20) { 5378c2ecf20Sopenharmony_ci if (!check_device(ioaddr)) 5388c2ecf20Sopenharmony_ci continue; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci pr_info("3c515 Resource configuration register %#4.4x, DCR %4.4x.\n", 5418c2ecf20Sopenharmony_ci inl(ioaddr + 0x2002), inw(ioaddr + 0x2000)); 5428c2ecf20Sopenharmony_ci err = corkscrew_setup(dev, ioaddr, NULL, cards_found++); 5438c2ecf20Sopenharmony_ci if (!err) 5448c2ecf20Sopenharmony_ci return dev; 5458c2ecf20Sopenharmony_ci cleanup_card(dev); 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci free_netdev(dev); 5488c2ecf20Sopenharmony_ci return NULL; 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic const struct net_device_ops netdev_ops = { 5538c2ecf20Sopenharmony_ci .ndo_open = corkscrew_open, 5548c2ecf20Sopenharmony_ci .ndo_stop = corkscrew_close, 5558c2ecf20Sopenharmony_ci .ndo_start_xmit = corkscrew_start_xmit, 5568c2ecf20Sopenharmony_ci .ndo_tx_timeout = corkscrew_timeout, 5578c2ecf20Sopenharmony_ci .ndo_get_stats = corkscrew_get_stats, 5588c2ecf20Sopenharmony_ci .ndo_set_rx_mode = set_rx_mode, 5598c2ecf20Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 5608c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 5618c2ecf20Sopenharmony_ci}; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_cistatic int corkscrew_setup(struct net_device *dev, int ioaddr, 5658c2ecf20Sopenharmony_ci struct pnp_dev *idev, int card_number) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci struct corkscrew_private *vp = netdev_priv(dev); 5688c2ecf20Sopenharmony_ci unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */ 5698c2ecf20Sopenharmony_ci int i; 5708c2ecf20Sopenharmony_ci int irq; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci#ifdef __ISAPNP__ 5738c2ecf20Sopenharmony_ci if (idev) { 5748c2ecf20Sopenharmony_ci irq = pnp_irq(idev, 0); 5758c2ecf20Sopenharmony_ci vp->dev = &idev->dev; 5768c2ecf20Sopenharmony_ci } else { 5778c2ecf20Sopenharmony_ci irq = inw(ioaddr + 0x2002) & 15; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci#else 5808c2ecf20Sopenharmony_ci irq = inw(ioaddr + 0x2002) & 15; 5818c2ecf20Sopenharmony_ci#endif 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci dev->base_addr = ioaddr; 5848c2ecf20Sopenharmony_ci dev->irq = irq; 5858c2ecf20Sopenharmony_ci dev->dma = inw(ioaddr + 0x2000) & 7; 5868c2ecf20Sopenharmony_ci vp->product_name = "3c515"; 5878c2ecf20Sopenharmony_ci vp->options = dev->mem_start; 5888c2ecf20Sopenharmony_ci vp->our_dev = dev; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if (!vp->options) { 5918c2ecf20Sopenharmony_ci if (card_number >= MAX_UNITS) 5928c2ecf20Sopenharmony_ci vp->options = -1; 5938c2ecf20Sopenharmony_ci else 5948c2ecf20Sopenharmony_ci vp->options = options[card_number]; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (vp->options >= 0) { 5988c2ecf20Sopenharmony_ci vp->media_override = vp->options & 7; 5998c2ecf20Sopenharmony_ci if (vp->media_override == 2) 6008c2ecf20Sopenharmony_ci vp->media_override = 0; 6018c2ecf20Sopenharmony_ci vp->full_duplex = (vp->options & 8) ? 1 : 0; 6028c2ecf20Sopenharmony_ci vp->bus_master = (vp->options & 16) ? 1 : 0; 6038c2ecf20Sopenharmony_ci } else { 6048c2ecf20Sopenharmony_ci vp->media_override = 7; 6058c2ecf20Sopenharmony_ci vp->full_duplex = 0; 6068c2ecf20Sopenharmony_ci vp->bus_master = 0; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci#ifdef MODULE 6098c2ecf20Sopenharmony_ci list_add(&vp->list, &root_corkscrew_dev); 6108c2ecf20Sopenharmony_ci#endif 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci pr_info("%s: 3Com %s at %#3x,", dev->name, vp->product_name, ioaddr); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci spin_lock_init(&vp->lock); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci timer_setup(&vp->timer, corkscrew_timer, 0); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci /* Read the station address from the EEPROM. */ 6198c2ecf20Sopenharmony_ci EL3WINDOW(0); 6208c2ecf20Sopenharmony_ci for (i = 0; i < 0x18; i++) { 6218c2ecf20Sopenharmony_ci __be16 *phys_addr = (__be16 *) dev->dev_addr; 6228c2ecf20Sopenharmony_ci int timer; 6238c2ecf20Sopenharmony_ci outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd); 6248c2ecf20Sopenharmony_ci /* Pause for at least 162 us. for the read to take place. */ 6258c2ecf20Sopenharmony_ci for (timer = 4; timer >= 0; timer--) { 6268c2ecf20Sopenharmony_ci udelay(162); 6278c2ecf20Sopenharmony_ci if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0) 6288c2ecf20Sopenharmony_ci break; 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci eeprom[i] = inw(ioaddr + Wn0EepromData); 6318c2ecf20Sopenharmony_ci checksum ^= eeprom[i]; 6328c2ecf20Sopenharmony_ci if (i < 3) 6338c2ecf20Sopenharmony_ci phys_addr[i] = htons(eeprom[i]); 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci checksum = (checksum ^ (checksum >> 8)) & 0xff; 6368c2ecf20Sopenharmony_ci if (checksum != 0x00) 6378c2ecf20Sopenharmony_ci pr_cont(" ***INVALID CHECKSUM %4.4x*** ", checksum); 6388c2ecf20Sopenharmony_ci pr_cont(" %pM", dev->dev_addr); 6398c2ecf20Sopenharmony_ci if (eeprom[16] == 0x11c7) { /* Corkscrew */ 6408c2ecf20Sopenharmony_ci if (request_dma(dev->dma, "3c515")) { 6418c2ecf20Sopenharmony_ci pr_cont(", DMA %d allocation failed", dev->dma); 6428c2ecf20Sopenharmony_ci dev->dma = 0; 6438c2ecf20Sopenharmony_ci } else 6448c2ecf20Sopenharmony_ci pr_cont(", DMA %d", dev->dma); 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci pr_cont(", IRQ %d\n", dev->irq); 6478c2ecf20Sopenharmony_ci /* Tell them about an invalid IRQ. */ 6488c2ecf20Sopenharmony_ci if (corkscrew_debug && (dev->irq <= 0 || dev->irq > 15)) 6498c2ecf20Sopenharmony_ci pr_warn(" *** Warning: this IRQ is unlikely to work! ***\n"); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci { 6528c2ecf20Sopenharmony_ci static const char * const ram_split[] = { 6538c2ecf20Sopenharmony_ci "5:3", "3:1", "1:1", "3:5" 6548c2ecf20Sopenharmony_ci }; 6558c2ecf20Sopenharmony_ci __u32 config; 6568c2ecf20Sopenharmony_ci EL3WINDOW(3); 6578c2ecf20Sopenharmony_ci vp->available_media = inw(ioaddr + Wn3_Options); 6588c2ecf20Sopenharmony_ci config = inl(ioaddr + Wn3_Config); 6598c2ecf20Sopenharmony_ci if (corkscrew_debug > 1) 6608c2ecf20Sopenharmony_ci pr_info(" Internal config register is %4.4x, transceivers %#x.\n", 6618c2ecf20Sopenharmony_ci config, inw(ioaddr + Wn3_Options)); 6628c2ecf20Sopenharmony_ci pr_info(" %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n", 6638c2ecf20Sopenharmony_ci 8 << config & Ram_size, 6648c2ecf20Sopenharmony_ci config & Ram_width ? "word" : "byte", 6658c2ecf20Sopenharmony_ci ram_split[(config & Ram_split) >> Ram_split_shift], 6668c2ecf20Sopenharmony_ci config & Autoselect ? "autoselect/" : "", 6678c2ecf20Sopenharmony_ci media_tbl[(config & Xcvr) >> Xcvr_shift].name); 6688c2ecf20Sopenharmony_ci vp->default_media = (config & Xcvr) >> Xcvr_shift; 6698c2ecf20Sopenharmony_ci vp->autoselect = config & Autoselect ? 1 : 0; 6708c2ecf20Sopenharmony_ci dev->if_port = vp->default_media; 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci if (vp->media_override != 7) { 6738c2ecf20Sopenharmony_ci pr_info(" Media override to transceiver type %d (%s).\n", 6748c2ecf20Sopenharmony_ci vp->media_override, 6758c2ecf20Sopenharmony_ci media_tbl[vp->media_override].name); 6768c2ecf20Sopenharmony_ci dev->if_port = vp->media_override; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci vp->capabilities = eeprom[16]; 6808c2ecf20Sopenharmony_ci vp->full_bus_master_tx = (vp->capabilities & 0x20) ? 1 : 0; 6818c2ecf20Sopenharmony_ci /* Rx is broken at 10mbps, so we always disable it. */ 6828c2ecf20Sopenharmony_ci /* vp->full_bus_master_rx = 0; */ 6838c2ecf20Sopenharmony_ci vp->full_bus_master_rx = (vp->capabilities & 0x20) ? 1 : 0; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci /* The 3c51x-specific entries in the device structure. */ 6868c2ecf20Sopenharmony_ci dev->netdev_ops = &netdev_ops; 6878c2ecf20Sopenharmony_ci dev->watchdog_timeo = (400 * HZ) / 1000; 6888c2ecf20Sopenharmony_ci dev->ethtool_ops = &netdev_ethtool_ops; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci return register_netdev(dev); 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic int corkscrew_open(struct net_device *dev) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci int ioaddr = dev->base_addr; 6978c2ecf20Sopenharmony_ci struct corkscrew_private *vp = netdev_priv(dev); 6988c2ecf20Sopenharmony_ci bool armtimer = false; 6998c2ecf20Sopenharmony_ci __u32 config; 7008c2ecf20Sopenharmony_ci int i; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* Before initializing select the active media port. */ 7038c2ecf20Sopenharmony_ci EL3WINDOW(3); 7048c2ecf20Sopenharmony_ci if (vp->full_duplex) 7058c2ecf20Sopenharmony_ci outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */ 7068c2ecf20Sopenharmony_ci config = inl(ioaddr + Wn3_Config); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci if (vp->media_override != 7) { 7098c2ecf20Sopenharmony_ci if (corkscrew_debug > 1) 7108c2ecf20Sopenharmony_ci pr_info("%s: Media override to transceiver %d (%s).\n", 7118c2ecf20Sopenharmony_ci dev->name, vp->media_override, 7128c2ecf20Sopenharmony_ci media_tbl[vp->media_override].name); 7138c2ecf20Sopenharmony_ci dev->if_port = vp->media_override; 7148c2ecf20Sopenharmony_ci } else if (vp->autoselect) { 7158c2ecf20Sopenharmony_ci /* Find first available media type, starting with 100baseTx. */ 7168c2ecf20Sopenharmony_ci dev->if_port = 4; 7178c2ecf20Sopenharmony_ci while (!(vp->available_media & media_tbl[dev->if_port].mask)) 7188c2ecf20Sopenharmony_ci dev->if_port = media_tbl[dev->if_port].next; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (corkscrew_debug > 1) 7218c2ecf20Sopenharmony_ci pr_debug("%s: Initial media type %s.\n", 7228c2ecf20Sopenharmony_ci dev->name, media_tbl[dev->if_port].name); 7238c2ecf20Sopenharmony_ci armtimer = true; 7248c2ecf20Sopenharmony_ci } else 7258c2ecf20Sopenharmony_ci dev->if_port = vp->default_media; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift); 7288c2ecf20Sopenharmony_ci outl(config, ioaddr + Wn3_Config); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (corkscrew_debug > 1) { 7318c2ecf20Sopenharmony_ci pr_debug("%s: corkscrew_open() InternalConfig %8.8x.\n", 7328c2ecf20Sopenharmony_ci dev->name, config); 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci outw(TxReset, ioaddr + EL3_CMD); 7368c2ecf20Sopenharmony_ci for (i = 20; i >= 0; i--) 7378c2ecf20Sopenharmony_ci if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) 7388c2ecf20Sopenharmony_ci break; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci outw(RxReset, ioaddr + EL3_CMD); 7418c2ecf20Sopenharmony_ci /* Wait a few ticks for the RxReset command to complete. */ 7428c2ecf20Sopenharmony_ci for (i = 20; i >= 0; i--) 7438c2ecf20Sopenharmony_ci if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) 7448c2ecf20Sopenharmony_ci break; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci /* Use the now-standard shared IRQ implementation. */ 7498c2ecf20Sopenharmony_ci if (vp->capabilities == 0x11c7) { 7508c2ecf20Sopenharmony_ci /* Corkscrew: Cannot share ISA resources. */ 7518c2ecf20Sopenharmony_ci if (dev->irq == 0 || 7528c2ecf20Sopenharmony_ci dev->dma == 0 || 7538c2ecf20Sopenharmony_ci request_irq(dev->irq, corkscrew_interrupt, 0, 7548c2ecf20Sopenharmony_ci vp->product_name, dev)) 7558c2ecf20Sopenharmony_ci return -EAGAIN; 7568c2ecf20Sopenharmony_ci enable_dma(dev->dma); 7578c2ecf20Sopenharmony_ci set_dma_mode(dev->dma, DMA_MODE_CASCADE); 7588c2ecf20Sopenharmony_ci } else if (request_irq(dev->irq, corkscrew_interrupt, IRQF_SHARED, 7598c2ecf20Sopenharmony_ci vp->product_name, dev)) { 7608c2ecf20Sopenharmony_ci return -EAGAIN; 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci if (armtimer) 7648c2ecf20Sopenharmony_ci mod_timer(&vp->timer, jiffies + media_tbl[dev->if_port].wait); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci if (corkscrew_debug > 1) { 7678c2ecf20Sopenharmony_ci EL3WINDOW(4); 7688c2ecf20Sopenharmony_ci pr_debug("%s: corkscrew_open() irq %d media status %4.4x.\n", 7698c2ecf20Sopenharmony_ci dev->name, dev->irq, inw(ioaddr + Wn4_Media)); 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci /* Set the station address and mask in window 2 each time opened. */ 7738c2ecf20Sopenharmony_ci EL3WINDOW(2); 7748c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) 7758c2ecf20Sopenharmony_ci outb(dev->dev_addr[i], ioaddr + i); 7768c2ecf20Sopenharmony_ci for (; i < 12; i += 2) 7778c2ecf20Sopenharmony_ci outw(0, ioaddr + i); 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci if (dev->if_port == 3) 7808c2ecf20Sopenharmony_ci /* Start the thinnet transceiver. We should really wait 50ms... */ 7818c2ecf20Sopenharmony_ci outw(StartCoax, ioaddr + EL3_CMD); 7828c2ecf20Sopenharmony_ci EL3WINDOW(4); 7838c2ecf20Sopenharmony_ci outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP | Media_SQE)) | 7848c2ecf20Sopenharmony_ci media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci /* Switch to the stats window, and clear all stats by reading. */ 7878c2ecf20Sopenharmony_ci outw(StatsDisable, ioaddr + EL3_CMD); 7888c2ecf20Sopenharmony_ci EL3WINDOW(6); 7898c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) 7908c2ecf20Sopenharmony_ci inb(ioaddr + i); 7918c2ecf20Sopenharmony_ci inw(ioaddr + 10); 7928c2ecf20Sopenharmony_ci inw(ioaddr + 12); 7938c2ecf20Sopenharmony_ci /* New: On the Vortex we must also clear the BadSSD counter. */ 7948c2ecf20Sopenharmony_ci EL3WINDOW(4); 7958c2ecf20Sopenharmony_ci inb(ioaddr + 12); 7968c2ecf20Sopenharmony_ci /* ..and on the Boomerang we enable the extra statistics bits. */ 7978c2ecf20Sopenharmony_ci outw(0x0040, ioaddr + Wn4_NetDiag); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci /* Switch to register set 7 for normal use. */ 8008c2ecf20Sopenharmony_ci EL3WINDOW(7); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci if (vp->full_bus_master_rx) { /* Boomerang bus master. */ 8038c2ecf20Sopenharmony_ci vp->cur_rx = vp->dirty_rx = 0; 8048c2ecf20Sopenharmony_ci if (corkscrew_debug > 2) 8058c2ecf20Sopenharmony_ci pr_debug("%s: Filling in the Rx ring.\n", dev->name); 8068c2ecf20Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 8078c2ecf20Sopenharmony_ci struct sk_buff *skb; 8088c2ecf20Sopenharmony_ci if (i < (RX_RING_SIZE - 1)) 8098c2ecf20Sopenharmony_ci vp->rx_ring[i].next = 8108c2ecf20Sopenharmony_ci isa_virt_to_bus(&vp->rx_ring[i + 1]); 8118c2ecf20Sopenharmony_ci else 8128c2ecf20Sopenharmony_ci vp->rx_ring[i].next = 0; 8138c2ecf20Sopenharmony_ci vp->rx_ring[i].status = 0; /* Clear complete bit. */ 8148c2ecf20Sopenharmony_ci vp->rx_ring[i].length = PKT_BUF_SZ | 0x80000000; 8158c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(dev, PKT_BUF_SZ); 8168c2ecf20Sopenharmony_ci vp->rx_skbuff[i] = skb; 8178c2ecf20Sopenharmony_ci if (skb == NULL) 8188c2ecf20Sopenharmony_ci break; /* Bad news! */ 8198c2ecf20Sopenharmony_ci skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ 8208c2ecf20Sopenharmony_ci vp->rx_ring[i].addr = isa_virt_to_bus(skb->data); 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci if (i != 0) 8238c2ecf20Sopenharmony_ci vp->rx_ring[i - 1].next = 8248c2ecf20Sopenharmony_ci isa_virt_to_bus(&vp->rx_ring[0]); /* Wrap the ring. */ 8258c2ecf20Sopenharmony_ci outl(isa_virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr); 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci if (vp->full_bus_master_tx) { /* Boomerang bus master Tx. */ 8288c2ecf20Sopenharmony_ci vp->cur_tx = vp->dirty_tx = 0; 8298c2ecf20Sopenharmony_ci outb(PKT_BUF_SZ >> 8, ioaddr + TxFreeThreshold); /* Room for a packet. */ 8308c2ecf20Sopenharmony_ci /* Clear the Tx ring. */ 8318c2ecf20Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) 8328c2ecf20Sopenharmony_ci vp->tx_skbuff[i] = NULL; 8338c2ecf20Sopenharmony_ci outl(0, ioaddr + DownListPtr); 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci /* Set receiver mode: presumably accept b-case and phys addr only. */ 8368c2ecf20Sopenharmony_ci set_rx_mode(dev); 8378c2ecf20Sopenharmony_ci outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */ 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci netif_start_queue(dev); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */ 8428c2ecf20Sopenharmony_ci outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */ 8438c2ecf20Sopenharmony_ci /* Allow status bits to be seen. */ 8448c2ecf20Sopenharmony_ci outw(SetStatusEnb | AdapterFailure | IntReq | StatsFull | 8458c2ecf20Sopenharmony_ci (vp->full_bus_master_tx ? DownComplete : TxAvailable) | 8468c2ecf20Sopenharmony_ci (vp->full_bus_master_rx ? UpComplete : RxComplete) | 8478c2ecf20Sopenharmony_ci (vp->bus_master ? DMADone : 0), ioaddr + EL3_CMD); 8488c2ecf20Sopenharmony_ci /* Ack all pending events, and set active indicator mask. */ 8498c2ecf20Sopenharmony_ci outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq, 8508c2ecf20Sopenharmony_ci ioaddr + EL3_CMD); 8518c2ecf20Sopenharmony_ci outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull 8528c2ecf20Sopenharmony_ci | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete, 8538c2ecf20Sopenharmony_ci ioaddr + EL3_CMD); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci return 0; 8568c2ecf20Sopenharmony_ci} 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_cistatic void corkscrew_timer(struct timer_list *t) 8598c2ecf20Sopenharmony_ci{ 8608c2ecf20Sopenharmony_ci#ifdef AUTOMEDIA 8618c2ecf20Sopenharmony_ci struct corkscrew_private *vp = from_timer(vp, t, timer); 8628c2ecf20Sopenharmony_ci struct net_device *dev = vp->our_dev; 8638c2ecf20Sopenharmony_ci int ioaddr = dev->base_addr; 8648c2ecf20Sopenharmony_ci unsigned long flags; 8658c2ecf20Sopenharmony_ci int ok = 0; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci if (corkscrew_debug > 1) 8688c2ecf20Sopenharmony_ci pr_debug("%s: Media selection timer tick happened, %s.\n", 8698c2ecf20Sopenharmony_ci dev->name, media_tbl[dev->if_port].name); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci spin_lock_irqsave(&vp->lock, flags); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci { 8748c2ecf20Sopenharmony_ci int old_window = inw(ioaddr + EL3_CMD) >> 13; 8758c2ecf20Sopenharmony_ci int media_status; 8768c2ecf20Sopenharmony_ci EL3WINDOW(4); 8778c2ecf20Sopenharmony_ci media_status = inw(ioaddr + Wn4_Media); 8788c2ecf20Sopenharmony_ci switch (dev->if_port) { 8798c2ecf20Sopenharmony_ci case 0: 8808c2ecf20Sopenharmony_ci case 4: 8818c2ecf20Sopenharmony_ci case 5: /* 10baseT, 100baseTX, 100baseFX */ 8828c2ecf20Sopenharmony_ci if (media_status & Media_LnkBeat) { 8838c2ecf20Sopenharmony_ci ok = 1; 8848c2ecf20Sopenharmony_ci if (corkscrew_debug > 1) 8858c2ecf20Sopenharmony_ci pr_debug("%s: Media %s has link beat, %x.\n", 8868c2ecf20Sopenharmony_ci dev->name, 8878c2ecf20Sopenharmony_ci media_tbl[dev->if_port].name, 8888c2ecf20Sopenharmony_ci media_status); 8898c2ecf20Sopenharmony_ci } else if (corkscrew_debug > 1) 8908c2ecf20Sopenharmony_ci pr_debug("%s: Media %s is has no link beat, %x.\n", 8918c2ecf20Sopenharmony_ci dev->name, 8928c2ecf20Sopenharmony_ci media_tbl[dev->if_port].name, 8938c2ecf20Sopenharmony_ci media_status); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci break; 8968c2ecf20Sopenharmony_ci default: /* Other media types handled by Tx timeouts. */ 8978c2ecf20Sopenharmony_ci if (corkscrew_debug > 1) 8988c2ecf20Sopenharmony_ci pr_debug("%s: Media %s is has no indication, %x.\n", 8998c2ecf20Sopenharmony_ci dev->name, 9008c2ecf20Sopenharmony_ci media_tbl[dev->if_port].name, 9018c2ecf20Sopenharmony_ci media_status); 9028c2ecf20Sopenharmony_ci ok = 1; 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci if (!ok) { 9058c2ecf20Sopenharmony_ci __u32 config; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci do { 9088c2ecf20Sopenharmony_ci dev->if_port = 9098c2ecf20Sopenharmony_ci media_tbl[dev->if_port].next; 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci while (!(vp->available_media & media_tbl[dev->if_port].mask)); 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci if (dev->if_port == 8) { /* Go back to default. */ 9148c2ecf20Sopenharmony_ci dev->if_port = vp->default_media; 9158c2ecf20Sopenharmony_ci if (corkscrew_debug > 1) 9168c2ecf20Sopenharmony_ci pr_debug("%s: Media selection failing, using default %s port.\n", 9178c2ecf20Sopenharmony_ci dev->name, 9188c2ecf20Sopenharmony_ci media_tbl[dev->if_port].name); 9198c2ecf20Sopenharmony_ci } else { 9208c2ecf20Sopenharmony_ci if (corkscrew_debug > 1) 9218c2ecf20Sopenharmony_ci pr_debug("%s: Media selection failed, now trying %s port.\n", 9228c2ecf20Sopenharmony_ci dev->name, 9238c2ecf20Sopenharmony_ci media_tbl[dev->if_port].name); 9248c2ecf20Sopenharmony_ci vp->timer.expires = jiffies + media_tbl[dev->if_port].wait; 9258c2ecf20Sopenharmony_ci add_timer(&vp->timer); 9268c2ecf20Sopenharmony_ci } 9278c2ecf20Sopenharmony_ci outw((media_status & ~(Media_10TP | Media_SQE)) | 9288c2ecf20Sopenharmony_ci media_tbl[dev->if_port].media_bits, 9298c2ecf20Sopenharmony_ci ioaddr + Wn4_Media); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci EL3WINDOW(3); 9328c2ecf20Sopenharmony_ci config = inl(ioaddr + Wn3_Config); 9338c2ecf20Sopenharmony_ci config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift); 9348c2ecf20Sopenharmony_ci outl(config, ioaddr + Wn3_Config); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci outw(dev->if_port == 3 ? StartCoax : StopCoax, 9378c2ecf20Sopenharmony_ci ioaddr + EL3_CMD); 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci EL3WINDOW(old_window); 9408c2ecf20Sopenharmony_ci } 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vp->lock, flags); 9438c2ecf20Sopenharmony_ci if (corkscrew_debug > 1) 9448c2ecf20Sopenharmony_ci pr_debug("%s: Media selection timer finished, %s.\n", 9458c2ecf20Sopenharmony_ci dev->name, media_tbl[dev->if_port].name); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci#endif /* AUTOMEDIA */ 9488c2ecf20Sopenharmony_ci} 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_cistatic void corkscrew_timeout(struct net_device *dev, unsigned int txqueue) 9518c2ecf20Sopenharmony_ci{ 9528c2ecf20Sopenharmony_ci int i; 9538c2ecf20Sopenharmony_ci struct corkscrew_private *vp = netdev_priv(dev); 9548c2ecf20Sopenharmony_ci int ioaddr = dev->base_addr; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci pr_warn("%s: transmit timed out, tx_status %2.2x status %4.4x\n", 9578c2ecf20Sopenharmony_ci dev->name, inb(ioaddr + TxStatus), 9588c2ecf20Sopenharmony_ci inw(ioaddr + EL3_STATUS)); 9598c2ecf20Sopenharmony_ci /* Slight code bloat to be user friendly. */ 9608c2ecf20Sopenharmony_ci if ((inb(ioaddr + TxStatus) & 0x88) == 0x88) 9618c2ecf20Sopenharmony_ci pr_warn("%s: Transmitter encountered 16 collisions -- network cable problem?\n", 9628c2ecf20Sopenharmony_ci dev->name); 9638c2ecf20Sopenharmony_ci#ifndef final_version 9648c2ecf20Sopenharmony_ci pr_debug(" Flags; bus-master %d, full %d; dirty %d current %d.\n", 9658c2ecf20Sopenharmony_ci vp->full_bus_master_tx, vp->tx_full, vp->dirty_tx, 9668c2ecf20Sopenharmony_ci vp->cur_tx); 9678c2ecf20Sopenharmony_ci pr_debug(" Down list %8.8x vs. %p.\n", inl(ioaddr + DownListPtr), 9688c2ecf20Sopenharmony_ci &vp->tx_ring[0]); 9698c2ecf20Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) { 9708c2ecf20Sopenharmony_ci pr_debug(" %d: %p length %8.8x status %8.8x\n", i, 9718c2ecf20Sopenharmony_ci &vp->tx_ring[i], 9728c2ecf20Sopenharmony_ci vp->tx_ring[i].length, vp->tx_ring[i].status); 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci#endif 9758c2ecf20Sopenharmony_ci /* Issue TX_RESET and TX_START commands. */ 9768c2ecf20Sopenharmony_ci outw(TxReset, ioaddr + EL3_CMD); 9778c2ecf20Sopenharmony_ci for (i = 20; i >= 0; i--) 9788c2ecf20Sopenharmony_ci if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) 9798c2ecf20Sopenharmony_ci break; 9808c2ecf20Sopenharmony_ci outw(TxEnable, ioaddr + EL3_CMD); 9818c2ecf20Sopenharmony_ci netif_trans_update(dev); /* prevent tx timeout */ 9828c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 9838c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 9848c2ecf20Sopenharmony_ci netif_wake_queue(dev); 9858c2ecf20Sopenharmony_ci} 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_cistatic netdev_tx_t corkscrew_start_xmit(struct sk_buff *skb, 9888c2ecf20Sopenharmony_ci struct net_device *dev) 9898c2ecf20Sopenharmony_ci{ 9908c2ecf20Sopenharmony_ci struct corkscrew_private *vp = netdev_priv(dev); 9918c2ecf20Sopenharmony_ci int ioaddr = dev->base_addr; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci /* Block a timer-based transmit from overlapping. */ 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci netif_stop_queue(dev); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci if (vp->full_bus_master_tx) { /* BOOMERANG bus-master */ 9988c2ecf20Sopenharmony_ci /* Calculate the next Tx descriptor entry. */ 9998c2ecf20Sopenharmony_ci int entry = vp->cur_tx % TX_RING_SIZE; 10008c2ecf20Sopenharmony_ci struct boom_tx_desc *prev_entry; 10018c2ecf20Sopenharmony_ci unsigned long flags; 10028c2ecf20Sopenharmony_ci int i; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci if (vp->tx_full) /* No room to transmit with */ 10058c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 10068c2ecf20Sopenharmony_ci if (vp->cur_tx != 0) 10078c2ecf20Sopenharmony_ci prev_entry = &vp->tx_ring[(vp->cur_tx - 1) % TX_RING_SIZE]; 10088c2ecf20Sopenharmony_ci else 10098c2ecf20Sopenharmony_ci prev_entry = NULL; 10108c2ecf20Sopenharmony_ci if (corkscrew_debug > 3) 10118c2ecf20Sopenharmony_ci pr_debug("%s: Trying to send a packet, Tx index %d.\n", 10128c2ecf20Sopenharmony_ci dev->name, vp->cur_tx); 10138c2ecf20Sopenharmony_ci /* vp->tx_full = 1; */ 10148c2ecf20Sopenharmony_ci vp->tx_skbuff[entry] = skb; 10158c2ecf20Sopenharmony_ci vp->tx_ring[entry].next = 0; 10168c2ecf20Sopenharmony_ci vp->tx_ring[entry].addr = isa_virt_to_bus(skb->data); 10178c2ecf20Sopenharmony_ci vp->tx_ring[entry].length = skb->len | 0x80000000; 10188c2ecf20Sopenharmony_ci vp->tx_ring[entry].status = skb->len | 0x80000000; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci spin_lock_irqsave(&vp->lock, flags); 10218c2ecf20Sopenharmony_ci outw(DownStall, ioaddr + EL3_CMD); 10228c2ecf20Sopenharmony_ci /* Wait for the stall to complete. */ 10238c2ecf20Sopenharmony_ci for (i = 20; i >= 0; i--) 10248c2ecf20Sopenharmony_ci if ((inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0) 10258c2ecf20Sopenharmony_ci break; 10268c2ecf20Sopenharmony_ci if (prev_entry) 10278c2ecf20Sopenharmony_ci prev_entry->next = isa_virt_to_bus(&vp->tx_ring[entry]); 10288c2ecf20Sopenharmony_ci if (inl(ioaddr + DownListPtr) == 0) { 10298c2ecf20Sopenharmony_ci outl(isa_virt_to_bus(&vp->tx_ring[entry]), 10308c2ecf20Sopenharmony_ci ioaddr + DownListPtr); 10318c2ecf20Sopenharmony_ci queued_packet++; 10328c2ecf20Sopenharmony_ci } 10338c2ecf20Sopenharmony_ci outw(DownUnstall, ioaddr + EL3_CMD); 10348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vp->lock, flags); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci vp->cur_tx++; 10378c2ecf20Sopenharmony_ci if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1) 10388c2ecf20Sopenharmony_ci vp->tx_full = 1; 10398c2ecf20Sopenharmony_ci else { /* Clear previous interrupt enable. */ 10408c2ecf20Sopenharmony_ci if (prev_entry) 10418c2ecf20Sopenharmony_ci prev_entry->status &= ~0x80000000; 10428c2ecf20Sopenharmony_ci netif_wake_queue(dev); 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci /* Put out the doubleword header... */ 10478c2ecf20Sopenharmony_ci outl(skb->len, ioaddr + TX_FIFO); 10488c2ecf20Sopenharmony_ci dev->stats.tx_bytes += skb->len; 10498c2ecf20Sopenharmony_ci#ifdef VORTEX_BUS_MASTER 10508c2ecf20Sopenharmony_ci if (vp->bus_master) { 10518c2ecf20Sopenharmony_ci /* Set the bus-master controller to transfer the packet. */ 10528c2ecf20Sopenharmony_ci outl((int) (skb->data), ioaddr + Wn7_MasterAddr); 10538c2ecf20Sopenharmony_ci outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen); 10548c2ecf20Sopenharmony_ci vp->tx_skb = skb; 10558c2ecf20Sopenharmony_ci outw(StartDMADown, ioaddr + EL3_CMD); 10568c2ecf20Sopenharmony_ci /* queue will be woken at the DMADone interrupt. */ 10578c2ecf20Sopenharmony_ci } else { 10588c2ecf20Sopenharmony_ci /* ... and the packet rounded to a doubleword. */ 10598c2ecf20Sopenharmony_ci outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); 10608c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 10618c2ecf20Sopenharmony_ci if (inw(ioaddr + TxFree) > 1536) { 10628c2ecf20Sopenharmony_ci netif_wake_queue(dev); 10638c2ecf20Sopenharmony_ci } else 10648c2ecf20Sopenharmony_ci /* Interrupt us when the FIFO has room for max-sized packet. */ 10658c2ecf20Sopenharmony_ci outw(SetTxThreshold + (1536 >> 2), 10668c2ecf20Sopenharmony_ci ioaddr + EL3_CMD); 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci#else 10698c2ecf20Sopenharmony_ci /* ... and the packet rounded to a doubleword. */ 10708c2ecf20Sopenharmony_ci outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); 10718c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 10728c2ecf20Sopenharmony_ci if (inw(ioaddr + TxFree) > 1536) { 10738c2ecf20Sopenharmony_ci netif_wake_queue(dev); 10748c2ecf20Sopenharmony_ci } else 10758c2ecf20Sopenharmony_ci /* Interrupt us when the FIFO has room for max-sized packet. */ 10768c2ecf20Sopenharmony_ci outw(SetTxThreshold + (1536 >> 2), ioaddr + EL3_CMD); 10778c2ecf20Sopenharmony_ci#endif /* bus master */ 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci /* Clear the Tx status stack. */ 10818c2ecf20Sopenharmony_ci { 10828c2ecf20Sopenharmony_ci short tx_status; 10838c2ecf20Sopenharmony_ci int i = 4; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci while (--i > 0 && (tx_status = inb(ioaddr + TxStatus)) > 0) { 10868c2ecf20Sopenharmony_ci if (tx_status & 0x3C) { /* A Tx-disabling error occurred. */ 10878c2ecf20Sopenharmony_ci if (corkscrew_debug > 2) 10888c2ecf20Sopenharmony_ci pr_debug("%s: Tx error, status %2.2x.\n", 10898c2ecf20Sopenharmony_ci dev->name, tx_status); 10908c2ecf20Sopenharmony_ci if (tx_status & 0x04) 10918c2ecf20Sopenharmony_ci dev->stats.tx_fifo_errors++; 10928c2ecf20Sopenharmony_ci if (tx_status & 0x38) 10938c2ecf20Sopenharmony_ci dev->stats.tx_aborted_errors++; 10948c2ecf20Sopenharmony_ci if (tx_status & 0x30) { 10958c2ecf20Sopenharmony_ci int j; 10968c2ecf20Sopenharmony_ci outw(TxReset, ioaddr + EL3_CMD); 10978c2ecf20Sopenharmony_ci for (j = 20; j >= 0; j--) 10988c2ecf20Sopenharmony_ci if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) 10998c2ecf20Sopenharmony_ci break; 11008c2ecf20Sopenharmony_ci } 11018c2ecf20Sopenharmony_ci outw(TxEnable, ioaddr + EL3_CMD); 11028c2ecf20Sopenharmony_ci } 11038c2ecf20Sopenharmony_ci outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */ 11048c2ecf20Sopenharmony_ci } 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 11078c2ecf20Sopenharmony_ci} 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci/* The interrupt handler does all of the Rx thread work and cleans up 11108c2ecf20Sopenharmony_ci after the Tx thread. */ 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_cistatic irqreturn_t corkscrew_interrupt(int irq, void *dev_id) 11138c2ecf20Sopenharmony_ci{ 11148c2ecf20Sopenharmony_ci /* Use the now-standard shared IRQ implementation. */ 11158c2ecf20Sopenharmony_ci struct net_device *dev = dev_id; 11168c2ecf20Sopenharmony_ci struct corkscrew_private *lp = netdev_priv(dev); 11178c2ecf20Sopenharmony_ci int ioaddr, status; 11188c2ecf20Sopenharmony_ci int latency; 11198c2ecf20Sopenharmony_ci int i = max_interrupt_work; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci ioaddr = dev->base_addr; 11228c2ecf20Sopenharmony_ci latency = inb(ioaddr + Timer); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci spin_lock(&lp->lock); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci status = inw(ioaddr + EL3_STATUS); 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci if (corkscrew_debug > 4) 11298c2ecf20Sopenharmony_ci pr_debug("%s: interrupt, status %4.4x, timer %d.\n", 11308c2ecf20Sopenharmony_ci dev->name, status, latency); 11318c2ecf20Sopenharmony_ci if ((status & 0xE000) != 0xE000) { 11328c2ecf20Sopenharmony_ci static int donedidthis; 11338c2ecf20Sopenharmony_ci /* Some interrupt controllers store a bogus interrupt from boot-time. 11348c2ecf20Sopenharmony_ci Ignore a single early interrupt, but don't hang the machine for 11358c2ecf20Sopenharmony_ci other interrupt problems. */ 11368c2ecf20Sopenharmony_ci if (donedidthis++ > 100) { 11378c2ecf20Sopenharmony_ci pr_err("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n", 11388c2ecf20Sopenharmony_ci dev->name, status, netif_running(dev)); 11398c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 11408c2ecf20Sopenharmony_ci dev->irq = -1; 11418c2ecf20Sopenharmony_ci } 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci do { 11458c2ecf20Sopenharmony_ci if (corkscrew_debug > 5) 11468c2ecf20Sopenharmony_ci pr_debug("%s: In interrupt loop, status %4.4x.\n", 11478c2ecf20Sopenharmony_ci dev->name, status); 11488c2ecf20Sopenharmony_ci if (status & RxComplete) 11498c2ecf20Sopenharmony_ci corkscrew_rx(dev); 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci if (status & TxAvailable) { 11528c2ecf20Sopenharmony_ci if (corkscrew_debug > 5) 11538c2ecf20Sopenharmony_ci pr_debug(" TX room bit was handled.\n"); 11548c2ecf20Sopenharmony_ci /* There's room in the FIFO for a full-sized packet. */ 11558c2ecf20Sopenharmony_ci outw(AckIntr | TxAvailable, ioaddr + EL3_CMD); 11568c2ecf20Sopenharmony_ci netif_wake_queue(dev); 11578c2ecf20Sopenharmony_ci } 11588c2ecf20Sopenharmony_ci if (status & DownComplete) { 11598c2ecf20Sopenharmony_ci unsigned int dirty_tx = lp->dirty_tx; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci while (lp->cur_tx - dirty_tx > 0) { 11628c2ecf20Sopenharmony_ci int entry = dirty_tx % TX_RING_SIZE; 11638c2ecf20Sopenharmony_ci if (inl(ioaddr + DownListPtr) == isa_virt_to_bus(&lp->tx_ring[entry])) 11648c2ecf20Sopenharmony_ci break; /* It still hasn't been processed. */ 11658c2ecf20Sopenharmony_ci if (lp->tx_skbuff[entry]) { 11668c2ecf20Sopenharmony_ci dev_consume_skb_irq(lp->tx_skbuff[entry]); 11678c2ecf20Sopenharmony_ci lp->tx_skbuff[entry] = NULL; 11688c2ecf20Sopenharmony_ci } 11698c2ecf20Sopenharmony_ci dirty_tx++; 11708c2ecf20Sopenharmony_ci } 11718c2ecf20Sopenharmony_ci lp->dirty_tx = dirty_tx; 11728c2ecf20Sopenharmony_ci outw(AckIntr | DownComplete, ioaddr + EL3_CMD); 11738c2ecf20Sopenharmony_ci if (lp->tx_full && (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) { 11748c2ecf20Sopenharmony_ci lp->tx_full = 0; 11758c2ecf20Sopenharmony_ci netif_wake_queue(dev); 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci } 11788c2ecf20Sopenharmony_ci#ifdef VORTEX_BUS_MASTER 11798c2ecf20Sopenharmony_ci if (status & DMADone) { 11808c2ecf20Sopenharmony_ci outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */ 11818c2ecf20Sopenharmony_ci dev_consume_skb_irq(lp->tx_skb); /* Release the transferred buffer */ 11828c2ecf20Sopenharmony_ci netif_wake_queue(dev); 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci#endif 11858c2ecf20Sopenharmony_ci if (status & UpComplete) { 11868c2ecf20Sopenharmony_ci boomerang_rx(dev); 11878c2ecf20Sopenharmony_ci outw(AckIntr | UpComplete, ioaddr + EL3_CMD); 11888c2ecf20Sopenharmony_ci } 11898c2ecf20Sopenharmony_ci if (status & (AdapterFailure | RxEarly | StatsFull)) { 11908c2ecf20Sopenharmony_ci /* Handle all uncommon interrupts at once. */ 11918c2ecf20Sopenharmony_ci if (status & RxEarly) { /* Rx early is unused. */ 11928c2ecf20Sopenharmony_ci corkscrew_rx(dev); 11938c2ecf20Sopenharmony_ci outw(AckIntr | RxEarly, ioaddr + EL3_CMD); 11948c2ecf20Sopenharmony_ci } 11958c2ecf20Sopenharmony_ci if (status & StatsFull) { /* Empty statistics. */ 11968c2ecf20Sopenharmony_ci static int DoneDidThat; 11978c2ecf20Sopenharmony_ci if (corkscrew_debug > 4) 11988c2ecf20Sopenharmony_ci pr_debug("%s: Updating stats.\n", dev->name); 11998c2ecf20Sopenharmony_ci update_stats(ioaddr, dev); 12008c2ecf20Sopenharmony_ci /* DEBUG HACK: Disable statistics as an interrupt source. */ 12018c2ecf20Sopenharmony_ci /* This occurs when we have the wrong media type! */ 12028c2ecf20Sopenharmony_ci if (DoneDidThat == 0 && inw(ioaddr + EL3_STATUS) & StatsFull) { 12038c2ecf20Sopenharmony_ci int win, reg; 12048c2ecf20Sopenharmony_ci pr_notice("%s: Updating stats failed, disabling stats as an interrupt source.\n", 12058c2ecf20Sopenharmony_ci dev->name); 12068c2ecf20Sopenharmony_ci for (win = 0; win < 8; win++) { 12078c2ecf20Sopenharmony_ci EL3WINDOW(win); 12088c2ecf20Sopenharmony_ci pr_notice("Vortex window %d:", win); 12098c2ecf20Sopenharmony_ci for (reg = 0; reg < 16; reg++) 12108c2ecf20Sopenharmony_ci pr_cont(" %2.2x", inb(ioaddr + reg)); 12118c2ecf20Sopenharmony_ci pr_cont("\n"); 12128c2ecf20Sopenharmony_ci } 12138c2ecf20Sopenharmony_ci EL3WINDOW(7); 12148c2ecf20Sopenharmony_ci outw(SetIntrEnb | TxAvailable | 12158c2ecf20Sopenharmony_ci RxComplete | AdapterFailure | 12168c2ecf20Sopenharmony_ci UpComplete | DownComplete | 12178c2ecf20Sopenharmony_ci TxComplete, ioaddr + EL3_CMD); 12188c2ecf20Sopenharmony_ci DoneDidThat++; 12198c2ecf20Sopenharmony_ci } 12208c2ecf20Sopenharmony_ci } 12218c2ecf20Sopenharmony_ci if (status & AdapterFailure) { 12228c2ecf20Sopenharmony_ci /* Adapter failure requires Rx reset and reinit. */ 12238c2ecf20Sopenharmony_ci outw(RxReset, ioaddr + EL3_CMD); 12248c2ecf20Sopenharmony_ci /* Set the Rx filter to the current state. */ 12258c2ecf20Sopenharmony_ci set_rx_mode(dev); 12268c2ecf20Sopenharmony_ci outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */ 12278c2ecf20Sopenharmony_ci outw(AckIntr | AdapterFailure, 12288c2ecf20Sopenharmony_ci ioaddr + EL3_CMD); 12298c2ecf20Sopenharmony_ci } 12308c2ecf20Sopenharmony_ci } 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci if (--i < 0) { 12338c2ecf20Sopenharmony_ci pr_err("%s: Too much work in interrupt, status %4.4x. Disabling functions (%4.4x).\n", 12348c2ecf20Sopenharmony_ci dev->name, status, SetStatusEnb | ((~status) & 0x7FE)); 12358c2ecf20Sopenharmony_ci /* Disable all pending interrupts. */ 12368c2ecf20Sopenharmony_ci outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD); 12378c2ecf20Sopenharmony_ci outw(AckIntr | 0x7FF, ioaddr + EL3_CMD); 12388c2ecf20Sopenharmony_ci break; 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci /* Acknowledge the IRQ. */ 12418c2ecf20Sopenharmony_ci outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete)); 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci spin_unlock(&lp->lock); 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci if (corkscrew_debug > 4) 12488c2ecf20Sopenharmony_ci pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name, status); 12498c2ecf20Sopenharmony_ci return IRQ_HANDLED; 12508c2ecf20Sopenharmony_ci} 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_cistatic int corkscrew_rx(struct net_device *dev) 12538c2ecf20Sopenharmony_ci{ 12548c2ecf20Sopenharmony_ci int ioaddr = dev->base_addr; 12558c2ecf20Sopenharmony_ci int i; 12568c2ecf20Sopenharmony_ci short rx_status; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci if (corkscrew_debug > 5) 12598c2ecf20Sopenharmony_ci pr_debug(" In rx_packet(), status %4.4x, rx_status %4.4x.\n", 12608c2ecf20Sopenharmony_ci inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus)); 12618c2ecf20Sopenharmony_ci while ((rx_status = inw(ioaddr + RxStatus)) > 0) { 12628c2ecf20Sopenharmony_ci if (rx_status & 0x4000) { /* Error, update stats. */ 12638c2ecf20Sopenharmony_ci unsigned char rx_error = inb(ioaddr + RxErrors); 12648c2ecf20Sopenharmony_ci if (corkscrew_debug > 2) 12658c2ecf20Sopenharmony_ci pr_debug(" Rx error: status %2.2x.\n", 12668c2ecf20Sopenharmony_ci rx_error); 12678c2ecf20Sopenharmony_ci dev->stats.rx_errors++; 12688c2ecf20Sopenharmony_ci if (rx_error & 0x01) 12698c2ecf20Sopenharmony_ci dev->stats.rx_over_errors++; 12708c2ecf20Sopenharmony_ci if (rx_error & 0x02) 12718c2ecf20Sopenharmony_ci dev->stats.rx_length_errors++; 12728c2ecf20Sopenharmony_ci if (rx_error & 0x04) 12738c2ecf20Sopenharmony_ci dev->stats.rx_frame_errors++; 12748c2ecf20Sopenharmony_ci if (rx_error & 0x08) 12758c2ecf20Sopenharmony_ci dev->stats.rx_crc_errors++; 12768c2ecf20Sopenharmony_ci if (rx_error & 0x10) 12778c2ecf20Sopenharmony_ci dev->stats.rx_length_errors++; 12788c2ecf20Sopenharmony_ci } else { 12798c2ecf20Sopenharmony_ci /* The packet length: up to 4.5K!. */ 12808c2ecf20Sopenharmony_ci short pkt_len = rx_status & 0x1fff; 12818c2ecf20Sopenharmony_ci struct sk_buff *skb; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(dev, pkt_len + 5 + 2); 12848c2ecf20Sopenharmony_ci if (corkscrew_debug > 4) 12858c2ecf20Sopenharmony_ci pr_debug("Receiving packet size %d status %4.4x.\n", 12868c2ecf20Sopenharmony_ci pkt_len, rx_status); 12878c2ecf20Sopenharmony_ci if (skb != NULL) { 12888c2ecf20Sopenharmony_ci skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ 12898c2ecf20Sopenharmony_ci /* 'skb_put()' points to the start of sk_buff data area. */ 12908c2ecf20Sopenharmony_ci insl(ioaddr + RX_FIFO, 12918c2ecf20Sopenharmony_ci skb_put(skb, pkt_len), 12928c2ecf20Sopenharmony_ci (pkt_len + 3) >> 2); 12938c2ecf20Sopenharmony_ci outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ 12948c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 12958c2ecf20Sopenharmony_ci netif_rx(skb); 12968c2ecf20Sopenharmony_ci dev->stats.rx_packets++; 12978c2ecf20Sopenharmony_ci dev->stats.rx_bytes += pkt_len; 12988c2ecf20Sopenharmony_ci /* Wait a limited time to go to next packet. */ 12998c2ecf20Sopenharmony_ci for (i = 200; i >= 0; i--) 13008c2ecf20Sopenharmony_ci if (! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) 13018c2ecf20Sopenharmony_ci break; 13028c2ecf20Sopenharmony_ci continue; 13038c2ecf20Sopenharmony_ci } else if (corkscrew_debug) 13048c2ecf20Sopenharmony_ci pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len); 13058c2ecf20Sopenharmony_ci } 13068c2ecf20Sopenharmony_ci outw(RxDiscard, ioaddr + EL3_CMD); 13078c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 13088c2ecf20Sopenharmony_ci /* Wait a limited time to skip this packet. */ 13098c2ecf20Sopenharmony_ci for (i = 200; i >= 0; i--) 13108c2ecf20Sopenharmony_ci if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) 13118c2ecf20Sopenharmony_ci break; 13128c2ecf20Sopenharmony_ci } 13138c2ecf20Sopenharmony_ci return 0; 13148c2ecf20Sopenharmony_ci} 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_cistatic int boomerang_rx(struct net_device *dev) 13178c2ecf20Sopenharmony_ci{ 13188c2ecf20Sopenharmony_ci struct corkscrew_private *vp = netdev_priv(dev); 13198c2ecf20Sopenharmony_ci int entry = vp->cur_rx % RX_RING_SIZE; 13208c2ecf20Sopenharmony_ci int ioaddr = dev->base_addr; 13218c2ecf20Sopenharmony_ci int rx_status; 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci if (corkscrew_debug > 5) 13248c2ecf20Sopenharmony_ci pr_debug(" In boomerang_rx(), status %4.4x, rx_status %4.4x.\n", 13258c2ecf20Sopenharmony_ci inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus)); 13268c2ecf20Sopenharmony_ci while ((rx_status = vp->rx_ring[entry].status) & RxDComplete) { 13278c2ecf20Sopenharmony_ci if (rx_status & RxDError) { /* Error, update stats. */ 13288c2ecf20Sopenharmony_ci unsigned char rx_error = rx_status >> 16; 13298c2ecf20Sopenharmony_ci if (corkscrew_debug > 2) 13308c2ecf20Sopenharmony_ci pr_debug(" Rx error: status %2.2x.\n", 13318c2ecf20Sopenharmony_ci rx_error); 13328c2ecf20Sopenharmony_ci dev->stats.rx_errors++; 13338c2ecf20Sopenharmony_ci if (rx_error & 0x01) 13348c2ecf20Sopenharmony_ci dev->stats.rx_over_errors++; 13358c2ecf20Sopenharmony_ci if (rx_error & 0x02) 13368c2ecf20Sopenharmony_ci dev->stats.rx_length_errors++; 13378c2ecf20Sopenharmony_ci if (rx_error & 0x04) 13388c2ecf20Sopenharmony_ci dev->stats.rx_frame_errors++; 13398c2ecf20Sopenharmony_ci if (rx_error & 0x08) 13408c2ecf20Sopenharmony_ci dev->stats.rx_crc_errors++; 13418c2ecf20Sopenharmony_ci if (rx_error & 0x10) 13428c2ecf20Sopenharmony_ci dev->stats.rx_length_errors++; 13438c2ecf20Sopenharmony_ci } else { 13448c2ecf20Sopenharmony_ci /* The packet length: up to 4.5K!. */ 13458c2ecf20Sopenharmony_ci short pkt_len = rx_status & 0x1fff; 13468c2ecf20Sopenharmony_ci struct sk_buff *skb; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci dev->stats.rx_bytes += pkt_len; 13498c2ecf20Sopenharmony_ci if (corkscrew_debug > 4) 13508c2ecf20Sopenharmony_ci pr_debug("Receiving packet size %d status %4.4x.\n", 13518c2ecf20Sopenharmony_ci pkt_len, rx_status); 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci /* Check if the packet is long enough to just accept without 13548c2ecf20Sopenharmony_ci copying to a properly sized skbuff. */ 13558c2ecf20Sopenharmony_ci if (pkt_len < rx_copybreak && 13568c2ecf20Sopenharmony_ci (skb = netdev_alloc_skb(dev, pkt_len + 4)) != NULL) { 13578c2ecf20Sopenharmony_ci skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ 13588c2ecf20Sopenharmony_ci /* 'skb_put()' points to the start of sk_buff data area. */ 13598c2ecf20Sopenharmony_ci skb_put_data(skb, 13608c2ecf20Sopenharmony_ci isa_bus_to_virt(vp->rx_ring[entry].addr), 13618c2ecf20Sopenharmony_ci pkt_len); 13628c2ecf20Sopenharmony_ci rx_copy++; 13638c2ecf20Sopenharmony_ci } else { 13648c2ecf20Sopenharmony_ci void *temp; 13658c2ecf20Sopenharmony_ci /* Pass up the skbuff already on the Rx ring. */ 13668c2ecf20Sopenharmony_ci skb = vp->rx_skbuff[entry]; 13678c2ecf20Sopenharmony_ci vp->rx_skbuff[entry] = NULL; 13688c2ecf20Sopenharmony_ci temp = skb_put(skb, pkt_len); 13698c2ecf20Sopenharmony_ci /* Remove this checking code for final release. */ 13708c2ecf20Sopenharmony_ci if (isa_bus_to_virt(vp->rx_ring[entry].addr) != temp) 13718c2ecf20Sopenharmony_ci pr_warn("%s: Warning -- the skbuff addresses do not match in boomerang_rx: %p vs. %p / %p\n", 13728c2ecf20Sopenharmony_ci dev->name, 13738c2ecf20Sopenharmony_ci isa_bus_to_virt(vp->rx_ring[entry].addr), 13748c2ecf20Sopenharmony_ci skb->head, temp); 13758c2ecf20Sopenharmony_ci rx_nocopy++; 13768c2ecf20Sopenharmony_ci } 13778c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 13788c2ecf20Sopenharmony_ci netif_rx(skb); 13798c2ecf20Sopenharmony_ci dev->stats.rx_packets++; 13808c2ecf20Sopenharmony_ci } 13818c2ecf20Sopenharmony_ci entry = (++vp->cur_rx) % RX_RING_SIZE; 13828c2ecf20Sopenharmony_ci } 13838c2ecf20Sopenharmony_ci /* Refill the Rx ring buffers. */ 13848c2ecf20Sopenharmony_ci for (; vp->cur_rx - vp->dirty_rx > 0; vp->dirty_rx++) { 13858c2ecf20Sopenharmony_ci struct sk_buff *skb; 13868c2ecf20Sopenharmony_ci entry = vp->dirty_rx % RX_RING_SIZE; 13878c2ecf20Sopenharmony_ci if (vp->rx_skbuff[entry] == NULL) { 13888c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(dev, PKT_BUF_SZ); 13898c2ecf20Sopenharmony_ci if (skb == NULL) 13908c2ecf20Sopenharmony_ci break; /* Bad news! */ 13918c2ecf20Sopenharmony_ci skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ 13928c2ecf20Sopenharmony_ci vp->rx_ring[entry].addr = isa_virt_to_bus(skb->data); 13938c2ecf20Sopenharmony_ci vp->rx_skbuff[entry] = skb; 13948c2ecf20Sopenharmony_ci } 13958c2ecf20Sopenharmony_ci vp->rx_ring[entry].status = 0; /* Clear complete bit. */ 13968c2ecf20Sopenharmony_ci } 13978c2ecf20Sopenharmony_ci return 0; 13988c2ecf20Sopenharmony_ci} 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_cistatic int corkscrew_close(struct net_device *dev) 14018c2ecf20Sopenharmony_ci{ 14028c2ecf20Sopenharmony_ci struct corkscrew_private *vp = netdev_priv(dev); 14038c2ecf20Sopenharmony_ci int ioaddr = dev->base_addr; 14048c2ecf20Sopenharmony_ci int i; 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci netif_stop_queue(dev); 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci if (corkscrew_debug > 1) { 14098c2ecf20Sopenharmony_ci pr_debug("%s: corkscrew_close() status %4.4x, Tx status %2.2x.\n", 14108c2ecf20Sopenharmony_ci dev->name, inw(ioaddr + EL3_STATUS), 14118c2ecf20Sopenharmony_ci inb(ioaddr + TxStatus)); 14128c2ecf20Sopenharmony_ci pr_debug("%s: corkscrew close stats: rx_nocopy %d rx_copy %d tx_queued %d.\n", 14138c2ecf20Sopenharmony_ci dev->name, rx_nocopy, rx_copy, queued_packet); 14148c2ecf20Sopenharmony_ci } 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci del_timer_sync(&vp->timer); 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci /* Turn off statistics ASAP. We update lp->stats below. */ 14198c2ecf20Sopenharmony_ci outw(StatsDisable, ioaddr + EL3_CMD); 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci /* Disable the receiver and transmitter. */ 14228c2ecf20Sopenharmony_ci outw(RxDisable, ioaddr + EL3_CMD); 14238c2ecf20Sopenharmony_ci outw(TxDisable, ioaddr + EL3_CMD); 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci if (dev->if_port == XCVR_10base2) 14268c2ecf20Sopenharmony_ci /* Turn off thinnet power. Green! */ 14278c2ecf20Sopenharmony_ci outw(StopCoax, ioaddr + EL3_CMD); 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD); 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci update_stats(ioaddr, dev); 14348c2ecf20Sopenharmony_ci if (vp->full_bus_master_rx) { /* Free Boomerang bus master Rx buffers. */ 14358c2ecf20Sopenharmony_ci outl(0, ioaddr + UpListPtr); 14368c2ecf20Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) 14378c2ecf20Sopenharmony_ci if (vp->rx_skbuff[i]) { 14388c2ecf20Sopenharmony_ci dev_kfree_skb(vp->rx_skbuff[i]); 14398c2ecf20Sopenharmony_ci vp->rx_skbuff[i] = NULL; 14408c2ecf20Sopenharmony_ci } 14418c2ecf20Sopenharmony_ci } 14428c2ecf20Sopenharmony_ci if (vp->full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. */ 14438c2ecf20Sopenharmony_ci outl(0, ioaddr + DownListPtr); 14448c2ecf20Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) 14458c2ecf20Sopenharmony_ci if (vp->tx_skbuff[i]) { 14468c2ecf20Sopenharmony_ci dev_kfree_skb(vp->tx_skbuff[i]); 14478c2ecf20Sopenharmony_ci vp->tx_skbuff[i] = NULL; 14488c2ecf20Sopenharmony_ci } 14498c2ecf20Sopenharmony_ci } 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci return 0; 14528c2ecf20Sopenharmony_ci} 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_cistatic struct net_device_stats *corkscrew_get_stats(struct net_device *dev) 14558c2ecf20Sopenharmony_ci{ 14568c2ecf20Sopenharmony_ci struct corkscrew_private *vp = netdev_priv(dev); 14578c2ecf20Sopenharmony_ci unsigned long flags; 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci if (netif_running(dev)) { 14608c2ecf20Sopenharmony_ci spin_lock_irqsave(&vp->lock, flags); 14618c2ecf20Sopenharmony_ci update_stats(dev->base_addr, dev); 14628c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vp->lock, flags); 14638c2ecf20Sopenharmony_ci } 14648c2ecf20Sopenharmony_ci return &dev->stats; 14658c2ecf20Sopenharmony_ci} 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci/* Update statistics. 14688c2ecf20Sopenharmony_ci Unlike with the EL3 we need not worry about interrupts changing 14698c2ecf20Sopenharmony_ci the window setting from underneath us, but we must still guard 14708c2ecf20Sopenharmony_ci against a race condition with a StatsUpdate interrupt updating the 14718c2ecf20Sopenharmony_ci table. This is done by checking that the ASM (!) code generated uses 14728c2ecf20Sopenharmony_ci atomic updates with '+='. 14738c2ecf20Sopenharmony_ci */ 14748c2ecf20Sopenharmony_cistatic void update_stats(int ioaddr, struct net_device *dev) 14758c2ecf20Sopenharmony_ci{ 14768c2ecf20Sopenharmony_ci /* Unlike the 3c5x9 we need not turn off stats updates while reading. */ 14778c2ecf20Sopenharmony_ci /* Switch to the stats window, and read everything. */ 14788c2ecf20Sopenharmony_ci EL3WINDOW(6); 14798c2ecf20Sopenharmony_ci dev->stats.tx_carrier_errors += inb(ioaddr + 0); 14808c2ecf20Sopenharmony_ci dev->stats.tx_heartbeat_errors += inb(ioaddr + 1); 14818c2ecf20Sopenharmony_ci /* Multiple collisions. */ inb(ioaddr + 2); 14828c2ecf20Sopenharmony_ci dev->stats.collisions += inb(ioaddr + 3); 14838c2ecf20Sopenharmony_ci dev->stats.tx_window_errors += inb(ioaddr + 4); 14848c2ecf20Sopenharmony_ci dev->stats.rx_fifo_errors += inb(ioaddr + 5); 14858c2ecf20Sopenharmony_ci dev->stats.tx_packets += inb(ioaddr + 6); 14868c2ecf20Sopenharmony_ci dev->stats.tx_packets += (inb(ioaddr + 9) & 0x30) << 4; 14878c2ecf20Sopenharmony_ci /* Rx packets */ inb(ioaddr + 7); 14888c2ecf20Sopenharmony_ci /* Must read to clear */ 14898c2ecf20Sopenharmony_ci /* Tx deferrals */ inb(ioaddr + 8); 14908c2ecf20Sopenharmony_ci /* Don't bother with register 9, an extension of registers 6&7. 14918c2ecf20Sopenharmony_ci If we do use the 6&7 values the atomic update assumption above 14928c2ecf20Sopenharmony_ci is invalid. */ 14938c2ecf20Sopenharmony_ci inw(ioaddr + 10); /* Total Rx and Tx octets. */ 14948c2ecf20Sopenharmony_ci inw(ioaddr + 12); 14958c2ecf20Sopenharmony_ci /* New: On the Vortex we must also clear the BadSSD counter. */ 14968c2ecf20Sopenharmony_ci EL3WINDOW(4); 14978c2ecf20Sopenharmony_ci inb(ioaddr + 12); 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci /* We change back to window 7 (not 1) with the Vortex. */ 15008c2ecf20Sopenharmony_ci EL3WINDOW(7); 15018c2ecf20Sopenharmony_ci} 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci/* This new version of set_rx_mode() supports v1.4 kernels. 15048c2ecf20Sopenharmony_ci The Vortex chip has no documented multicast filter, so the only 15058c2ecf20Sopenharmony_ci multicast setting is to receive all multicast frames. At least 15068c2ecf20Sopenharmony_ci the chip has a very clean way to set the mode, unlike many others. */ 15078c2ecf20Sopenharmony_cistatic void set_rx_mode(struct net_device *dev) 15088c2ecf20Sopenharmony_ci{ 15098c2ecf20Sopenharmony_ci int ioaddr = dev->base_addr; 15108c2ecf20Sopenharmony_ci unsigned short new_mode; 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci if (dev->flags & IFF_PROMISC) { 15138c2ecf20Sopenharmony_ci if (corkscrew_debug > 3) 15148c2ecf20Sopenharmony_ci pr_debug("%s: Setting promiscuous mode.\n", 15158c2ecf20Sopenharmony_ci dev->name); 15168c2ecf20Sopenharmony_ci new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm; 15178c2ecf20Sopenharmony_ci } else if (!netdev_mc_empty(dev) || dev->flags & IFF_ALLMULTI) { 15188c2ecf20Sopenharmony_ci new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast; 15198c2ecf20Sopenharmony_ci } else 15208c2ecf20Sopenharmony_ci new_mode = SetRxFilter | RxStation | RxBroadcast; 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci outw(new_mode, ioaddr + EL3_CMD); 15238c2ecf20Sopenharmony_ci} 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_cistatic void netdev_get_drvinfo(struct net_device *dev, 15268c2ecf20Sopenharmony_ci struct ethtool_drvinfo *info) 15278c2ecf20Sopenharmony_ci{ 15288c2ecf20Sopenharmony_ci strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); 15298c2ecf20Sopenharmony_ci snprintf(info->bus_info, sizeof(info->bus_info), "ISA 0x%lx", 15308c2ecf20Sopenharmony_ci dev->base_addr); 15318c2ecf20Sopenharmony_ci} 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_cistatic u32 netdev_get_msglevel(struct net_device *dev) 15348c2ecf20Sopenharmony_ci{ 15358c2ecf20Sopenharmony_ci return corkscrew_debug; 15368c2ecf20Sopenharmony_ci} 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_cistatic void netdev_set_msglevel(struct net_device *dev, u32 level) 15398c2ecf20Sopenharmony_ci{ 15408c2ecf20Sopenharmony_ci corkscrew_debug = level; 15418c2ecf20Sopenharmony_ci} 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_cistatic const struct ethtool_ops netdev_ethtool_ops = { 15448c2ecf20Sopenharmony_ci .get_drvinfo = netdev_get_drvinfo, 15458c2ecf20Sopenharmony_ci .get_msglevel = netdev_get_msglevel, 15468c2ecf20Sopenharmony_ci .set_msglevel = netdev_set_msglevel, 15478c2ecf20Sopenharmony_ci}; 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci#ifdef MODULE 15518c2ecf20Sopenharmony_civoid cleanup_module(void) 15528c2ecf20Sopenharmony_ci{ 15538c2ecf20Sopenharmony_ci while (!list_empty(&root_corkscrew_dev)) { 15548c2ecf20Sopenharmony_ci struct net_device *dev; 15558c2ecf20Sopenharmony_ci struct corkscrew_private *vp; 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci vp = list_entry(root_corkscrew_dev.next, 15588c2ecf20Sopenharmony_ci struct corkscrew_private, list); 15598c2ecf20Sopenharmony_ci dev = vp->our_dev; 15608c2ecf20Sopenharmony_ci unregister_netdev(dev); 15618c2ecf20Sopenharmony_ci cleanup_card(dev); 15628c2ecf20Sopenharmony_ci free_netdev(dev); 15638c2ecf20Sopenharmony_ci } 15648c2ecf20Sopenharmony_ci} 15658c2ecf20Sopenharmony_ci#endif /* MODULE */ 1566