18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci Written 1998-2000 by Donald Becker. 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci This software may be used and distributed according to the terms of 58c2ecf20Sopenharmony_ci the GNU General Public License (GPL), incorporated herein by reference. 68c2ecf20Sopenharmony_ci Drivers based on or derived from this code fall under the GPL and must 78c2ecf20Sopenharmony_ci retain the authorship, copyright and license notice. This file is not 88c2ecf20Sopenharmony_ci a complete program and may only be used when the entire operating 98c2ecf20Sopenharmony_ci system is licensed under the GPL. 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci The author may be reached as becker@scyld.com, or C/O 128c2ecf20Sopenharmony_ci Scyld Computing Corporation 138c2ecf20Sopenharmony_ci 410 Severn Ave., Suite 210 148c2ecf20Sopenharmony_ci Annapolis MD 21403 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci Support information and updates available at 178c2ecf20Sopenharmony_ci http://www.scyld.com/network/pci-skeleton.html 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci Linux kernel updates: 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci Version 2.51, Nov 17, 2001 (jgarzik): 228c2ecf20Sopenharmony_ci - Add ethtool support 238c2ecf20Sopenharmony_ci - Replace some MII-related magic numbers with constants 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci*/ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define DRV_NAME "fealnx" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int debug; /* 1-> print debug message */ 308c2ecf20Sopenharmony_cistatic int max_interrupt_work = 20; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). */ 338c2ecf20Sopenharmony_cistatic int multicast_filter_limit = 32; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* Set the copy breakpoint for the copy-only-tiny-frames scheme. */ 368c2ecf20Sopenharmony_ci/* Setting to > 1518 effectively disables this feature. */ 378c2ecf20Sopenharmony_cistatic int rx_copybreak; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* Used to pass the media type, etc. */ 408c2ecf20Sopenharmony_ci/* Both 'options[]' and 'full_duplex[]' should exist for driver */ 418c2ecf20Sopenharmony_ci/* interoperability. */ 428c2ecf20Sopenharmony_ci/* The media type is usually passed in 'options[]'. */ 438c2ecf20Sopenharmony_ci#define MAX_UNITS 8 /* More are supported, limit only on options */ 448c2ecf20Sopenharmony_cistatic int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 }; 458c2ecf20Sopenharmony_cistatic int full_duplex[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 }; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* Operational parameters that are set at compile time. */ 488c2ecf20Sopenharmony_ci/* Keep the ring sizes a power of two for compile efficiency. */ 498c2ecf20Sopenharmony_ci/* The compiler will convert <unsigned>'%'<2^N> into a bit mask. */ 508c2ecf20Sopenharmony_ci/* Making the Tx ring too large decreases the effectiveness of channel */ 518c2ecf20Sopenharmony_ci/* bonding and packet priority. */ 528c2ecf20Sopenharmony_ci/* There are no ill effects from too-large receive rings. */ 538c2ecf20Sopenharmony_ci// 88-12-9 modify, 548c2ecf20Sopenharmony_ci// #define TX_RING_SIZE 16 558c2ecf20Sopenharmony_ci// #define RX_RING_SIZE 32 568c2ecf20Sopenharmony_ci#define TX_RING_SIZE 6 578c2ecf20Sopenharmony_ci#define RX_RING_SIZE 12 588c2ecf20Sopenharmony_ci#define TX_TOTAL_SIZE TX_RING_SIZE*sizeof(struct fealnx_desc) 598c2ecf20Sopenharmony_ci#define RX_TOTAL_SIZE RX_RING_SIZE*sizeof(struct fealnx_desc) 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* Operational parameters that usually are not changed. */ 628c2ecf20Sopenharmony_ci/* Time in jiffies before concluding the transmitter is hung. */ 638c2ecf20Sopenharmony_ci#define TX_TIMEOUT (2*HZ) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */ 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* Include files, designed to support most kernel versions 2.0.0 and later. */ 698c2ecf20Sopenharmony_ci#include <linux/module.h> 708c2ecf20Sopenharmony_ci#include <linux/kernel.h> 718c2ecf20Sopenharmony_ci#include <linux/string.h> 728c2ecf20Sopenharmony_ci#include <linux/timer.h> 738c2ecf20Sopenharmony_ci#include <linux/errno.h> 748c2ecf20Sopenharmony_ci#include <linux/ioport.h> 758c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 768c2ecf20Sopenharmony_ci#include <linux/pci.h> 778c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 788c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 798c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 808c2ecf20Sopenharmony_ci#include <linux/init.h> 818c2ecf20Sopenharmony_ci#include <linux/mii.h> 828c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 838c2ecf20Sopenharmony_ci#include <linux/crc32.h> 848c2ecf20Sopenharmony_ci#include <linux/delay.h> 858c2ecf20Sopenharmony_ci#include <linux/bitops.h> 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#include <asm/processor.h> /* Processor type for cache alignment. */ 888c2ecf20Sopenharmony_ci#include <asm/io.h> 898c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 908c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* This driver was written to use PCI memory space, however some x86 systems 938c2ecf20Sopenharmony_ci work only with I/O space accesses. */ 948c2ecf20Sopenharmony_ci#ifndef __alpha__ 958c2ecf20Sopenharmony_ci#define USE_IO_OPS 968c2ecf20Sopenharmony_ci#endif 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* Kernel compatibility defines, some common to David Hinds' PCMCIA package. */ 998c2ecf20Sopenharmony_ci/* This is only in the support-all-kernels source code. */ 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci#define RUN_AT(x) (jiffies + (x)) 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Myson or whoever"); 1048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Myson MTD-8xx 100/10M Ethernet PCI Adapter Driver"); 1058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1068c2ecf20Sopenharmony_cimodule_param(max_interrupt_work, int, 0); 1078c2ecf20Sopenharmony_cimodule_param(debug, int, 0); 1088c2ecf20Sopenharmony_cimodule_param(rx_copybreak, int, 0); 1098c2ecf20Sopenharmony_cimodule_param(multicast_filter_limit, int, 0); 1108c2ecf20Sopenharmony_cimodule_param_array(options, int, NULL, 0); 1118c2ecf20Sopenharmony_cimodule_param_array(full_duplex, int, NULL, 0); 1128c2ecf20Sopenharmony_ciMODULE_PARM_DESC(max_interrupt_work, "fealnx maximum events handled per interrupt"); 1138c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "fealnx enable debugging (0-1)"); 1148c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rx_copybreak, "fealnx copy breakpoint for copy-only-tiny-frames"); 1158c2ecf20Sopenharmony_ciMODULE_PARM_DESC(multicast_filter_limit, "fealnx maximum number of filtered multicast addresses"); 1168c2ecf20Sopenharmony_ciMODULE_PARM_DESC(options, "fealnx: Bits 0-3: media type, bit 17: full duplex"); 1178c2ecf20Sopenharmony_ciMODULE_PARM_DESC(full_duplex, "fealnx full duplex setting(s) (1)"); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cienum { 1208c2ecf20Sopenharmony_ci MIN_REGION_SIZE = 136, 1218c2ecf20Sopenharmony_ci}; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* A chip capabilities table, matching the entries in pci_tbl[] above. */ 1248c2ecf20Sopenharmony_cienum chip_capability_flags { 1258c2ecf20Sopenharmony_ci HAS_MII_XCVR, 1268c2ecf20Sopenharmony_ci HAS_CHIP_XCVR, 1278c2ecf20Sopenharmony_ci}; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* 89/6/13 add, */ 1308c2ecf20Sopenharmony_ci/* for different PHY */ 1318c2ecf20Sopenharmony_cienum phy_type_flags { 1328c2ecf20Sopenharmony_ci MysonPHY = 1, 1338c2ecf20Sopenharmony_ci AhdocPHY = 2, 1348c2ecf20Sopenharmony_ci SeeqPHY = 3, 1358c2ecf20Sopenharmony_ci MarvellPHY = 4, 1368c2ecf20Sopenharmony_ci Myson981 = 5, 1378c2ecf20Sopenharmony_ci LevelOnePHY = 6, 1388c2ecf20Sopenharmony_ci OtherPHY = 10, 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistruct chip_info { 1428c2ecf20Sopenharmony_ci char *chip_name; 1438c2ecf20Sopenharmony_ci int flags; 1448c2ecf20Sopenharmony_ci}; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic const struct chip_info skel_netdrv_tbl[] = { 1478c2ecf20Sopenharmony_ci { "100/10M Ethernet PCI Adapter", HAS_MII_XCVR }, 1488c2ecf20Sopenharmony_ci { "100/10M Ethernet PCI Adapter", HAS_CHIP_XCVR }, 1498c2ecf20Sopenharmony_ci { "1000/100/10M Ethernet PCI Adapter", HAS_MII_XCVR }, 1508c2ecf20Sopenharmony_ci}; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/* Offsets to the Command and Status Registers. */ 1538c2ecf20Sopenharmony_cienum fealnx_offsets { 1548c2ecf20Sopenharmony_ci PAR0 = 0x0, /* physical address 0-3 */ 1558c2ecf20Sopenharmony_ci PAR1 = 0x04, /* physical address 4-5 */ 1568c2ecf20Sopenharmony_ci MAR0 = 0x08, /* multicast address 0-3 */ 1578c2ecf20Sopenharmony_ci MAR1 = 0x0C, /* multicast address 4-7 */ 1588c2ecf20Sopenharmony_ci FAR0 = 0x10, /* flow-control address 0-3 */ 1598c2ecf20Sopenharmony_ci FAR1 = 0x14, /* flow-control address 4-5 */ 1608c2ecf20Sopenharmony_ci TCRRCR = 0x18, /* receive & transmit configuration */ 1618c2ecf20Sopenharmony_ci BCR = 0x1C, /* bus command */ 1628c2ecf20Sopenharmony_ci TXPDR = 0x20, /* transmit polling demand */ 1638c2ecf20Sopenharmony_ci RXPDR = 0x24, /* receive polling demand */ 1648c2ecf20Sopenharmony_ci RXCWP = 0x28, /* receive current word pointer */ 1658c2ecf20Sopenharmony_ci TXLBA = 0x2C, /* transmit list base address */ 1668c2ecf20Sopenharmony_ci RXLBA = 0x30, /* receive list base address */ 1678c2ecf20Sopenharmony_ci ISR = 0x34, /* interrupt status */ 1688c2ecf20Sopenharmony_ci IMR = 0x38, /* interrupt mask */ 1698c2ecf20Sopenharmony_ci FTH = 0x3C, /* flow control high/low threshold */ 1708c2ecf20Sopenharmony_ci MANAGEMENT = 0x40, /* bootrom/eeprom and mii management */ 1718c2ecf20Sopenharmony_ci TALLY = 0x44, /* tally counters for crc and mpa */ 1728c2ecf20Sopenharmony_ci TSR = 0x48, /* tally counter for transmit status */ 1738c2ecf20Sopenharmony_ci BMCRSR = 0x4c, /* basic mode control and status */ 1748c2ecf20Sopenharmony_ci PHYIDENTIFIER = 0x50, /* phy identifier */ 1758c2ecf20Sopenharmony_ci ANARANLPAR = 0x54, /* auto-negotiation advertisement and link 1768c2ecf20Sopenharmony_ci partner ability */ 1778c2ecf20Sopenharmony_ci ANEROCR = 0x58, /* auto-negotiation expansion and pci conf. */ 1788c2ecf20Sopenharmony_ci BPREMRPSR = 0x5c, /* bypass & receive error mask and phy status */ 1798c2ecf20Sopenharmony_ci}; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* Bits in the interrupt status/enable registers. */ 1828c2ecf20Sopenharmony_ci/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */ 1838c2ecf20Sopenharmony_cienum intr_status_bits { 1848c2ecf20Sopenharmony_ci RFCON = 0x00020000, /* receive flow control xon packet */ 1858c2ecf20Sopenharmony_ci RFCOFF = 0x00010000, /* receive flow control xoff packet */ 1868c2ecf20Sopenharmony_ci LSCStatus = 0x00008000, /* link status change */ 1878c2ecf20Sopenharmony_ci ANCStatus = 0x00004000, /* autonegotiation completed */ 1888c2ecf20Sopenharmony_ci FBE = 0x00002000, /* fatal bus error */ 1898c2ecf20Sopenharmony_ci FBEMask = 0x00001800, /* mask bit12-11 */ 1908c2ecf20Sopenharmony_ci ParityErr = 0x00000000, /* parity error */ 1918c2ecf20Sopenharmony_ci TargetErr = 0x00001000, /* target abort */ 1928c2ecf20Sopenharmony_ci MasterErr = 0x00000800, /* master error */ 1938c2ecf20Sopenharmony_ci TUNF = 0x00000400, /* transmit underflow */ 1948c2ecf20Sopenharmony_ci ROVF = 0x00000200, /* receive overflow */ 1958c2ecf20Sopenharmony_ci ETI = 0x00000100, /* transmit early int */ 1968c2ecf20Sopenharmony_ci ERI = 0x00000080, /* receive early int */ 1978c2ecf20Sopenharmony_ci CNTOVF = 0x00000040, /* counter overflow */ 1988c2ecf20Sopenharmony_ci RBU = 0x00000020, /* receive buffer unavailable */ 1998c2ecf20Sopenharmony_ci TBU = 0x00000010, /* transmit buffer unavilable */ 2008c2ecf20Sopenharmony_ci TI = 0x00000008, /* transmit interrupt */ 2018c2ecf20Sopenharmony_ci RI = 0x00000004, /* receive interrupt */ 2028c2ecf20Sopenharmony_ci RxErr = 0x00000002, /* receive error */ 2038c2ecf20Sopenharmony_ci}; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci/* Bits in the NetworkConfig register, W for writing, R for reading */ 2068c2ecf20Sopenharmony_ci/* FIXME: some names are invented by me. Marked with (name?) */ 2078c2ecf20Sopenharmony_ci/* If you have docs and know bit names, please fix 'em */ 2088c2ecf20Sopenharmony_cienum rx_mode_bits { 2098c2ecf20Sopenharmony_ci CR_W_ENH = 0x02000000, /* enhanced mode (name?) */ 2108c2ecf20Sopenharmony_ci CR_W_FD = 0x00100000, /* full duplex */ 2118c2ecf20Sopenharmony_ci CR_W_PS10 = 0x00080000, /* 10 mbit */ 2128c2ecf20Sopenharmony_ci CR_W_TXEN = 0x00040000, /* tx enable (name?) */ 2138c2ecf20Sopenharmony_ci CR_W_PS1000 = 0x00010000, /* 1000 mbit */ 2148c2ecf20Sopenharmony_ci /* CR_W_RXBURSTMASK= 0x00000e00, Im unsure about this */ 2158c2ecf20Sopenharmony_ci CR_W_RXMODEMASK = 0x000000e0, 2168c2ecf20Sopenharmony_ci CR_W_PROM = 0x00000080, /* promiscuous mode */ 2178c2ecf20Sopenharmony_ci CR_W_AB = 0x00000040, /* accept broadcast */ 2188c2ecf20Sopenharmony_ci CR_W_AM = 0x00000020, /* accept mutlicast */ 2198c2ecf20Sopenharmony_ci CR_W_ARP = 0x00000008, /* receive runt pkt */ 2208c2ecf20Sopenharmony_ci CR_W_ALP = 0x00000004, /* receive long pkt */ 2218c2ecf20Sopenharmony_ci CR_W_SEP = 0x00000002, /* receive error pkt */ 2228c2ecf20Sopenharmony_ci CR_W_RXEN = 0x00000001, /* rx enable (unicast?) (name?) */ 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci CR_R_TXSTOP = 0x04000000, /* tx stopped (name?) */ 2258c2ecf20Sopenharmony_ci CR_R_FD = 0x00100000, /* full duplex detected */ 2268c2ecf20Sopenharmony_ci CR_R_PS10 = 0x00080000, /* 10 mbit detected */ 2278c2ecf20Sopenharmony_ci CR_R_RXSTOP = 0x00008000, /* rx stopped (name?) */ 2288c2ecf20Sopenharmony_ci}; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci/* The Tulip Rx and Tx buffer descriptors. */ 2318c2ecf20Sopenharmony_cistruct fealnx_desc { 2328c2ecf20Sopenharmony_ci s32 status; 2338c2ecf20Sopenharmony_ci s32 control; 2348c2ecf20Sopenharmony_ci u32 buffer; 2358c2ecf20Sopenharmony_ci u32 next_desc; 2368c2ecf20Sopenharmony_ci struct fealnx_desc *next_desc_logical; 2378c2ecf20Sopenharmony_ci struct sk_buff *skbuff; 2388c2ecf20Sopenharmony_ci u32 reserved1; 2398c2ecf20Sopenharmony_ci u32 reserved2; 2408c2ecf20Sopenharmony_ci}; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* Bits in network_desc.status */ 2438c2ecf20Sopenharmony_cienum rx_desc_status_bits { 2448c2ecf20Sopenharmony_ci RXOWN = 0x80000000, /* own bit */ 2458c2ecf20Sopenharmony_ci FLNGMASK = 0x0fff0000, /* frame length */ 2468c2ecf20Sopenharmony_ci FLNGShift = 16, 2478c2ecf20Sopenharmony_ci MARSTATUS = 0x00004000, /* multicast address received */ 2488c2ecf20Sopenharmony_ci BARSTATUS = 0x00002000, /* broadcast address received */ 2498c2ecf20Sopenharmony_ci PHYSTATUS = 0x00001000, /* physical address received */ 2508c2ecf20Sopenharmony_ci RXFSD = 0x00000800, /* first descriptor */ 2518c2ecf20Sopenharmony_ci RXLSD = 0x00000400, /* last descriptor */ 2528c2ecf20Sopenharmony_ci ErrorSummary = 0x80, /* error summary */ 2538c2ecf20Sopenharmony_ci RUNTPKT = 0x40, /* runt packet received */ 2548c2ecf20Sopenharmony_ci LONGPKT = 0x20, /* long packet received */ 2558c2ecf20Sopenharmony_ci FAE = 0x10, /* frame align error */ 2568c2ecf20Sopenharmony_ci CRC = 0x08, /* crc error */ 2578c2ecf20Sopenharmony_ci RXER = 0x04, /* receive error */ 2588c2ecf20Sopenharmony_ci}; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cienum rx_desc_control_bits { 2618c2ecf20Sopenharmony_ci RXIC = 0x00800000, /* interrupt control */ 2628c2ecf20Sopenharmony_ci RBSShift = 0, 2638c2ecf20Sopenharmony_ci}; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cienum tx_desc_status_bits { 2668c2ecf20Sopenharmony_ci TXOWN = 0x80000000, /* own bit */ 2678c2ecf20Sopenharmony_ci JABTO = 0x00004000, /* jabber timeout */ 2688c2ecf20Sopenharmony_ci CSL = 0x00002000, /* carrier sense lost */ 2698c2ecf20Sopenharmony_ci LC = 0x00001000, /* late collision */ 2708c2ecf20Sopenharmony_ci EC = 0x00000800, /* excessive collision */ 2718c2ecf20Sopenharmony_ci UDF = 0x00000400, /* fifo underflow */ 2728c2ecf20Sopenharmony_ci DFR = 0x00000200, /* deferred */ 2738c2ecf20Sopenharmony_ci HF = 0x00000100, /* heartbeat fail */ 2748c2ecf20Sopenharmony_ci NCRMask = 0x000000ff, /* collision retry count */ 2758c2ecf20Sopenharmony_ci NCRShift = 0, 2768c2ecf20Sopenharmony_ci}; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cienum tx_desc_control_bits { 2798c2ecf20Sopenharmony_ci TXIC = 0x80000000, /* interrupt control */ 2808c2ecf20Sopenharmony_ci ETIControl = 0x40000000, /* early transmit interrupt */ 2818c2ecf20Sopenharmony_ci TXLD = 0x20000000, /* last descriptor */ 2828c2ecf20Sopenharmony_ci TXFD = 0x10000000, /* first descriptor */ 2838c2ecf20Sopenharmony_ci CRCEnable = 0x08000000, /* crc control */ 2848c2ecf20Sopenharmony_ci PADEnable = 0x04000000, /* padding control */ 2858c2ecf20Sopenharmony_ci RetryTxLC = 0x02000000, /* retry late collision */ 2868c2ecf20Sopenharmony_ci PKTSMask = 0x3ff800, /* packet size bit21-11 */ 2878c2ecf20Sopenharmony_ci PKTSShift = 11, 2888c2ecf20Sopenharmony_ci TBSMask = 0x000007ff, /* transmit buffer bit 10-0 */ 2898c2ecf20Sopenharmony_ci TBSShift = 0, 2908c2ecf20Sopenharmony_ci}; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci/* BootROM/EEPROM/MII Management Register */ 2938c2ecf20Sopenharmony_ci#define MASK_MIIR_MII_READ 0x00000000 2948c2ecf20Sopenharmony_ci#define MASK_MIIR_MII_WRITE 0x00000008 2958c2ecf20Sopenharmony_ci#define MASK_MIIR_MII_MDO 0x00000004 2968c2ecf20Sopenharmony_ci#define MASK_MIIR_MII_MDI 0x00000002 2978c2ecf20Sopenharmony_ci#define MASK_MIIR_MII_MDC 0x00000001 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci/* ST+OP+PHYAD+REGAD+TA */ 3008c2ecf20Sopenharmony_ci#define OP_READ 0x6000 /* ST:01+OP:10+PHYAD+REGAD+TA:Z0 */ 3018c2ecf20Sopenharmony_ci#define OP_WRITE 0x5002 /* ST:01+OP:01+PHYAD+REGAD+TA:10 */ 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 3048c2ecf20Sopenharmony_ci/* Constants for Myson PHY */ 3058c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 3068c2ecf20Sopenharmony_ci#define MysonPHYID 0xd0000302 3078c2ecf20Sopenharmony_ci/* 89-7-27 add, (begin) */ 3088c2ecf20Sopenharmony_ci#define MysonPHYID0 0x0302 3098c2ecf20Sopenharmony_ci#define StatusRegister 18 3108c2ecf20Sopenharmony_ci#define SPEED100 0x0400 // bit10 3118c2ecf20Sopenharmony_ci#define FULLMODE 0x0800 // bit11 3128c2ecf20Sopenharmony_ci/* 89-7-27 add, (end) */ 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 3158c2ecf20Sopenharmony_ci/* Constants for Seeq 80225 PHY */ 3168c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 3178c2ecf20Sopenharmony_ci#define SeeqPHYID0 0x0016 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci#define MIIRegister18 18 3208c2ecf20Sopenharmony_ci#define SPD_DET_100 0x80 3218c2ecf20Sopenharmony_ci#define DPLX_DET_FULL 0x40 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 3248c2ecf20Sopenharmony_ci/* Constants for Ahdoc 101 PHY */ 3258c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */ 3268c2ecf20Sopenharmony_ci#define AhdocPHYID0 0x0022 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci#define DiagnosticReg 18 3298c2ecf20Sopenharmony_ci#define DPLX_FULL 0x0800 3308c2ecf20Sopenharmony_ci#define Speed_100 0x0400 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci/* 89/6/13 add, */ 3338c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */ 3348c2ecf20Sopenharmony_ci/* Constants */ 3358c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- */ 3368c2ecf20Sopenharmony_ci#define MarvellPHYID0 0x0141 3378c2ecf20Sopenharmony_ci#define LevelOnePHYID0 0x0013 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci#define MII1000BaseTControlReg 9 3408c2ecf20Sopenharmony_ci#define MII1000BaseTStatusReg 10 3418c2ecf20Sopenharmony_ci#define SpecificReg 17 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci/* for 1000BaseT Control Register */ 3448c2ecf20Sopenharmony_ci#define PHYAbletoPerform1000FullDuplex 0x0200 3458c2ecf20Sopenharmony_ci#define PHYAbletoPerform1000HalfDuplex 0x0100 3468c2ecf20Sopenharmony_ci#define PHY1000AbilityMask 0x300 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci// for phy specific status register, marvell phy. 3498c2ecf20Sopenharmony_ci#define SpeedMask 0x0c000 3508c2ecf20Sopenharmony_ci#define Speed_1000M 0x08000 3518c2ecf20Sopenharmony_ci#define Speed_100M 0x4000 3528c2ecf20Sopenharmony_ci#define Speed_10M 0 3538c2ecf20Sopenharmony_ci#define Full_Duplex 0x2000 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci// 89/12/29 add, for phy specific status register, levelone phy, (begin) 3568c2ecf20Sopenharmony_ci#define LXT1000_100M 0x08000 3578c2ecf20Sopenharmony_ci#define LXT1000_1000M 0x0c000 3588c2ecf20Sopenharmony_ci#define LXT1000_Full 0x200 3598c2ecf20Sopenharmony_ci// 89/12/29 add, for phy specific status register, levelone phy, (end) 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci/* for 3-in-1 case, BMCRSR register */ 3628c2ecf20Sopenharmony_ci#define LinkIsUp2 0x00040000 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci/* for PHY */ 3658c2ecf20Sopenharmony_ci#define LinkIsUp 0x0004 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistruct netdev_private { 3698c2ecf20Sopenharmony_ci /* Descriptor rings first for alignment. */ 3708c2ecf20Sopenharmony_ci struct fealnx_desc *rx_ring; 3718c2ecf20Sopenharmony_ci struct fealnx_desc *tx_ring; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci dma_addr_t rx_ring_dma; 3748c2ecf20Sopenharmony_ci dma_addr_t tx_ring_dma; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci spinlock_t lock; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* Media monitoring timer. */ 3798c2ecf20Sopenharmony_ci struct timer_list timer; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci /* Reset timer */ 3828c2ecf20Sopenharmony_ci struct timer_list reset_timer; 3838c2ecf20Sopenharmony_ci int reset_timer_armed; 3848c2ecf20Sopenharmony_ci unsigned long crvalue_sv; 3858c2ecf20Sopenharmony_ci unsigned long imrvalue_sv; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* Frequently used values: keep some adjacent for cache effect. */ 3888c2ecf20Sopenharmony_ci int flags; 3898c2ecf20Sopenharmony_ci struct pci_dev *pci_dev; 3908c2ecf20Sopenharmony_ci unsigned long crvalue; 3918c2ecf20Sopenharmony_ci unsigned long bcrvalue; 3928c2ecf20Sopenharmony_ci unsigned long imrvalue; 3938c2ecf20Sopenharmony_ci struct fealnx_desc *cur_rx; 3948c2ecf20Sopenharmony_ci struct fealnx_desc *lack_rxbuf; 3958c2ecf20Sopenharmony_ci int really_rx_count; 3968c2ecf20Sopenharmony_ci struct fealnx_desc *cur_tx; 3978c2ecf20Sopenharmony_ci struct fealnx_desc *cur_tx_copy; 3988c2ecf20Sopenharmony_ci int really_tx_count; 3998c2ecf20Sopenharmony_ci int free_tx_count; 4008c2ecf20Sopenharmony_ci unsigned int rx_buf_sz; /* Based on MTU+slack. */ 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* These values are keep track of the transceiver/media in use. */ 4038c2ecf20Sopenharmony_ci unsigned int linkok; 4048c2ecf20Sopenharmony_ci unsigned int line_speed; 4058c2ecf20Sopenharmony_ci unsigned int duplexmode; 4068c2ecf20Sopenharmony_ci unsigned int default_port:4; /* Last dev->if_port value. */ 4078c2ecf20Sopenharmony_ci unsigned int PHYType; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* MII transceiver section. */ 4108c2ecf20Sopenharmony_ci int mii_cnt; /* MII device addresses. */ 4118c2ecf20Sopenharmony_ci unsigned char phys[2]; /* MII device addresses. */ 4128c2ecf20Sopenharmony_ci struct mii_if_info mii; 4138c2ecf20Sopenharmony_ci void __iomem *mem; 4148c2ecf20Sopenharmony_ci}; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic int mdio_read(struct net_device *dev, int phy_id, int location); 4188c2ecf20Sopenharmony_cistatic void mdio_write(struct net_device *dev, int phy_id, int location, int value); 4198c2ecf20Sopenharmony_cistatic int netdev_open(struct net_device *dev); 4208c2ecf20Sopenharmony_cistatic void getlinktype(struct net_device *dev); 4218c2ecf20Sopenharmony_cistatic void getlinkstatus(struct net_device *dev); 4228c2ecf20Sopenharmony_cistatic void netdev_timer(struct timer_list *t); 4238c2ecf20Sopenharmony_cistatic void reset_timer(struct timer_list *t); 4248c2ecf20Sopenharmony_cistatic void fealnx_tx_timeout(struct net_device *dev, unsigned int txqueue); 4258c2ecf20Sopenharmony_cistatic void init_ring(struct net_device *dev); 4268c2ecf20Sopenharmony_cistatic netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev); 4278c2ecf20Sopenharmony_cistatic irqreturn_t intr_handler(int irq, void *dev_instance); 4288c2ecf20Sopenharmony_cistatic int netdev_rx(struct net_device *dev); 4298c2ecf20Sopenharmony_cistatic void set_rx_mode(struct net_device *dev); 4308c2ecf20Sopenharmony_cistatic void __set_rx_mode(struct net_device *dev); 4318c2ecf20Sopenharmony_cistatic struct net_device_stats *get_stats(struct net_device *dev); 4328c2ecf20Sopenharmony_cistatic int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); 4338c2ecf20Sopenharmony_cistatic const struct ethtool_ops netdev_ethtool_ops; 4348c2ecf20Sopenharmony_cistatic int netdev_close(struct net_device *dev); 4358c2ecf20Sopenharmony_cistatic void reset_rx_descriptors(struct net_device *dev); 4368c2ecf20Sopenharmony_cistatic void reset_tx_descriptors(struct net_device *dev); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic void stop_nic_rx(void __iomem *ioaddr, long crvalue) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci int delay = 0x1000; 4418c2ecf20Sopenharmony_ci iowrite32(crvalue & ~(CR_W_RXEN), ioaddr + TCRRCR); 4428c2ecf20Sopenharmony_ci while (--delay) { 4438c2ecf20Sopenharmony_ci if ( (ioread32(ioaddr + TCRRCR) & CR_R_RXSTOP) == CR_R_RXSTOP) 4448c2ecf20Sopenharmony_ci break; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic void stop_nic_rxtx(void __iomem *ioaddr, long crvalue) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci int delay = 0x1000; 4528c2ecf20Sopenharmony_ci iowrite32(crvalue & ~(CR_W_RXEN+CR_W_TXEN), ioaddr + TCRRCR); 4538c2ecf20Sopenharmony_ci while (--delay) { 4548c2ecf20Sopenharmony_ci if ( (ioread32(ioaddr + TCRRCR) & (CR_R_RXSTOP+CR_R_TXSTOP)) 4558c2ecf20Sopenharmony_ci == (CR_R_RXSTOP+CR_R_TXSTOP) ) 4568c2ecf20Sopenharmony_ci break; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic const struct net_device_ops netdev_ops = { 4618c2ecf20Sopenharmony_ci .ndo_open = netdev_open, 4628c2ecf20Sopenharmony_ci .ndo_stop = netdev_close, 4638c2ecf20Sopenharmony_ci .ndo_start_xmit = start_tx, 4648c2ecf20Sopenharmony_ci .ndo_get_stats = get_stats, 4658c2ecf20Sopenharmony_ci .ndo_set_rx_mode = set_rx_mode, 4668c2ecf20Sopenharmony_ci .ndo_do_ioctl = mii_ioctl, 4678c2ecf20Sopenharmony_ci .ndo_tx_timeout = fealnx_tx_timeout, 4688c2ecf20Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 4698c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 4708c2ecf20Sopenharmony_ci}; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic int fealnx_init_one(struct pci_dev *pdev, 4738c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci struct netdev_private *np; 4768c2ecf20Sopenharmony_ci int i, option, err, irq; 4778c2ecf20Sopenharmony_ci static int card_idx = -1; 4788c2ecf20Sopenharmony_ci char boardname[12]; 4798c2ecf20Sopenharmony_ci void __iomem *ioaddr; 4808c2ecf20Sopenharmony_ci unsigned long len; 4818c2ecf20Sopenharmony_ci unsigned int chip_id = ent->driver_data; 4828c2ecf20Sopenharmony_ci struct net_device *dev; 4838c2ecf20Sopenharmony_ci void *ring_space; 4848c2ecf20Sopenharmony_ci dma_addr_t ring_dma; 4858c2ecf20Sopenharmony_ci#ifdef USE_IO_OPS 4868c2ecf20Sopenharmony_ci int bar = 0; 4878c2ecf20Sopenharmony_ci#else 4888c2ecf20Sopenharmony_ci int bar = 1; 4898c2ecf20Sopenharmony_ci#endif 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci card_idx++; 4928c2ecf20Sopenharmony_ci sprintf(boardname, "fealnx%d", card_idx); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci option = card_idx < MAX_UNITS ? options[card_idx] : 0; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci i = pci_enable_device(pdev); 4978c2ecf20Sopenharmony_ci if (i) return i; 4988c2ecf20Sopenharmony_ci pci_set_master(pdev); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci len = pci_resource_len(pdev, bar); 5018c2ecf20Sopenharmony_ci if (len < MIN_REGION_SIZE) { 5028c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 5038c2ecf20Sopenharmony_ci "region size %ld too small, aborting\n", len); 5048c2ecf20Sopenharmony_ci return -ENODEV; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci i = pci_request_regions(pdev, boardname); 5088c2ecf20Sopenharmony_ci if (i) 5098c2ecf20Sopenharmony_ci return i; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci irq = pdev->irq; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci ioaddr = pci_iomap(pdev, bar, len); 5148c2ecf20Sopenharmony_ci if (!ioaddr) { 5158c2ecf20Sopenharmony_ci err = -ENOMEM; 5168c2ecf20Sopenharmony_ci goto err_out_res; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(struct netdev_private)); 5208c2ecf20Sopenharmony_ci if (!dev) { 5218c2ecf20Sopenharmony_ci err = -ENOMEM; 5228c2ecf20Sopenharmony_ci goto err_out_unmap; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* read ethernet id */ 5278c2ecf20Sopenharmony_ci for (i = 0; i < 6; ++i) 5288c2ecf20Sopenharmony_ci dev->dev_addr[i] = ioread8(ioaddr + PAR0 + i); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* Reset the chip to erase previous misconfiguration. */ 5318c2ecf20Sopenharmony_ci iowrite32(0x00000001, ioaddr + BCR); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci /* Make certain the descriptor lists are aligned. */ 5348c2ecf20Sopenharmony_ci np = netdev_priv(dev); 5358c2ecf20Sopenharmony_ci np->mem = ioaddr; 5368c2ecf20Sopenharmony_ci spin_lock_init(&np->lock); 5378c2ecf20Sopenharmony_ci np->pci_dev = pdev; 5388c2ecf20Sopenharmony_ci np->flags = skel_netdrv_tbl[chip_id].flags; 5398c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, dev); 5408c2ecf20Sopenharmony_ci np->mii.dev = dev; 5418c2ecf20Sopenharmony_ci np->mii.mdio_read = mdio_read; 5428c2ecf20Sopenharmony_ci np->mii.mdio_write = mdio_write; 5438c2ecf20Sopenharmony_ci np->mii.phy_id_mask = 0x1f; 5448c2ecf20Sopenharmony_ci np->mii.reg_num_mask = 0x1f; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci ring_space = dma_alloc_coherent(&pdev->dev, RX_TOTAL_SIZE, &ring_dma, 5478c2ecf20Sopenharmony_ci GFP_KERNEL); 5488c2ecf20Sopenharmony_ci if (!ring_space) { 5498c2ecf20Sopenharmony_ci err = -ENOMEM; 5508c2ecf20Sopenharmony_ci goto err_out_free_dev; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci np->rx_ring = ring_space; 5538c2ecf20Sopenharmony_ci np->rx_ring_dma = ring_dma; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci ring_space = dma_alloc_coherent(&pdev->dev, TX_TOTAL_SIZE, &ring_dma, 5568c2ecf20Sopenharmony_ci GFP_KERNEL); 5578c2ecf20Sopenharmony_ci if (!ring_space) { 5588c2ecf20Sopenharmony_ci err = -ENOMEM; 5598c2ecf20Sopenharmony_ci goto err_out_free_rx; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci np->tx_ring = ring_space; 5628c2ecf20Sopenharmony_ci np->tx_ring_dma = ring_dma; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci /* find the connected MII xcvrs */ 5658c2ecf20Sopenharmony_ci if (np->flags == HAS_MII_XCVR) { 5668c2ecf20Sopenharmony_ci int phy, phy_idx = 0; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci for (phy = 1; phy < 32 && phy_idx < ARRAY_SIZE(np->phys); 5698c2ecf20Sopenharmony_ci phy++) { 5708c2ecf20Sopenharmony_ci int mii_status = mdio_read(dev, phy, 1); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci if (mii_status != 0xffff && mii_status != 0x0000) { 5738c2ecf20Sopenharmony_ci np->phys[phy_idx++] = phy; 5748c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 5758c2ecf20Sopenharmony_ci "MII PHY found at address %d, status " 5768c2ecf20Sopenharmony_ci "0x%4.4x.\n", phy, mii_status); 5778c2ecf20Sopenharmony_ci /* get phy type */ 5788c2ecf20Sopenharmony_ci { 5798c2ecf20Sopenharmony_ci unsigned int data; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci data = mdio_read(dev, np->phys[0], 2); 5828c2ecf20Sopenharmony_ci if (data == SeeqPHYID0) 5838c2ecf20Sopenharmony_ci np->PHYType = SeeqPHY; 5848c2ecf20Sopenharmony_ci else if (data == AhdocPHYID0) 5858c2ecf20Sopenharmony_ci np->PHYType = AhdocPHY; 5868c2ecf20Sopenharmony_ci else if (data == MarvellPHYID0) 5878c2ecf20Sopenharmony_ci np->PHYType = MarvellPHY; 5888c2ecf20Sopenharmony_ci else if (data == MysonPHYID0) 5898c2ecf20Sopenharmony_ci np->PHYType = Myson981; 5908c2ecf20Sopenharmony_ci else if (data == LevelOnePHYID0) 5918c2ecf20Sopenharmony_ci np->PHYType = LevelOnePHY; 5928c2ecf20Sopenharmony_ci else 5938c2ecf20Sopenharmony_ci np->PHYType = OtherPHY; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci np->mii_cnt = phy_idx; 5998c2ecf20Sopenharmony_ci if (phy_idx == 0) 6008c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, 6018c2ecf20Sopenharmony_ci "MII PHY not found -- this device may " 6028c2ecf20Sopenharmony_ci "not operate correctly.\n"); 6038c2ecf20Sopenharmony_ci } else { 6048c2ecf20Sopenharmony_ci np->phys[0] = 32; 6058c2ecf20Sopenharmony_ci/* 89/6/23 add, (begin) */ 6068c2ecf20Sopenharmony_ci /* get phy type */ 6078c2ecf20Sopenharmony_ci if (ioread32(ioaddr + PHYIDENTIFIER) == MysonPHYID) 6088c2ecf20Sopenharmony_ci np->PHYType = MysonPHY; 6098c2ecf20Sopenharmony_ci else 6108c2ecf20Sopenharmony_ci np->PHYType = OtherPHY; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci np->mii.phy_id = np->phys[0]; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci if (dev->mem_start) 6158c2ecf20Sopenharmony_ci option = dev->mem_start; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci /* The lower four bits are the media type. */ 6188c2ecf20Sopenharmony_ci if (option > 0) { 6198c2ecf20Sopenharmony_ci if (option & 0x200) 6208c2ecf20Sopenharmony_ci np->mii.full_duplex = 1; 6218c2ecf20Sopenharmony_ci np->default_port = option & 15; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci if (card_idx < MAX_UNITS && full_duplex[card_idx] > 0) 6258c2ecf20Sopenharmony_ci np->mii.full_duplex = full_duplex[card_idx]; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci if (np->mii.full_duplex) { 6288c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Media type forced to Full Duplex.\n"); 6298c2ecf20Sopenharmony_ci/* 89/6/13 add, (begin) */ 6308c2ecf20Sopenharmony_ci// if (np->PHYType==MarvellPHY) 6318c2ecf20Sopenharmony_ci if ((np->PHYType == MarvellPHY) || (np->PHYType == LevelOnePHY)) { 6328c2ecf20Sopenharmony_ci unsigned int data; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci data = mdio_read(dev, np->phys[0], 9); 6358c2ecf20Sopenharmony_ci data = (data & 0xfcff) | 0x0200; 6368c2ecf20Sopenharmony_ci mdio_write(dev, np->phys[0], 9, data); 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci/* 89/6/13 add, (end) */ 6398c2ecf20Sopenharmony_ci if (np->flags == HAS_MII_XCVR) 6408c2ecf20Sopenharmony_ci mdio_write(dev, np->phys[0], MII_ADVERTISE, ADVERTISE_FULL); 6418c2ecf20Sopenharmony_ci else 6428c2ecf20Sopenharmony_ci iowrite32(ADVERTISE_FULL, ioaddr + ANARANLPAR); 6438c2ecf20Sopenharmony_ci np->mii.force_media = 1; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci dev->netdev_ops = &netdev_ops; 6478c2ecf20Sopenharmony_ci dev->ethtool_ops = &netdev_ethtool_ops; 6488c2ecf20Sopenharmony_ci dev->watchdog_timeo = TX_TIMEOUT; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci err = register_netdev(dev); 6518c2ecf20Sopenharmony_ci if (err) 6528c2ecf20Sopenharmony_ci goto err_out_free_tx; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: %s at %p, %pM, IRQ %d.\n", 6558c2ecf20Sopenharmony_ci dev->name, skel_netdrv_tbl[chip_id].chip_name, ioaddr, 6568c2ecf20Sopenharmony_ci dev->dev_addr, irq); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci return 0; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_cierr_out_free_tx: 6618c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, np->tx_ring, 6628c2ecf20Sopenharmony_ci np->tx_ring_dma); 6638c2ecf20Sopenharmony_cierr_out_free_rx: 6648c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, np->rx_ring, 6658c2ecf20Sopenharmony_ci np->rx_ring_dma); 6668c2ecf20Sopenharmony_cierr_out_free_dev: 6678c2ecf20Sopenharmony_ci free_netdev(dev); 6688c2ecf20Sopenharmony_cierr_out_unmap: 6698c2ecf20Sopenharmony_ci pci_iounmap(pdev, ioaddr); 6708c2ecf20Sopenharmony_cierr_out_res: 6718c2ecf20Sopenharmony_ci pci_release_regions(pdev); 6728c2ecf20Sopenharmony_ci return err; 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic void fealnx_remove_one(struct pci_dev *pdev) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci struct net_device *dev = pci_get_drvdata(pdev); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (dev) { 6818c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, np->tx_ring, 6848c2ecf20Sopenharmony_ci np->tx_ring_dma); 6858c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, np->rx_ring, 6868c2ecf20Sopenharmony_ci np->rx_ring_dma); 6878c2ecf20Sopenharmony_ci unregister_netdev(dev); 6888c2ecf20Sopenharmony_ci pci_iounmap(pdev, np->mem); 6898c2ecf20Sopenharmony_ci free_netdev(dev); 6908c2ecf20Sopenharmony_ci pci_release_regions(pdev); 6918c2ecf20Sopenharmony_ci } else 6928c2ecf20Sopenharmony_ci printk(KERN_ERR "fealnx: remove for unknown device\n"); 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_cistatic ulong m80x_send_cmd_to_phy(void __iomem *miiport, int opcode, int phyad, int regad) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci ulong miir; 6998c2ecf20Sopenharmony_ci int i; 7008c2ecf20Sopenharmony_ci unsigned int mask, data; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* enable MII output */ 7038c2ecf20Sopenharmony_ci miir = (ulong) ioread32(miiport); 7048c2ecf20Sopenharmony_ci miir &= 0xfffffff0; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci miir |= MASK_MIIR_MII_WRITE + MASK_MIIR_MII_MDO; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci /* send 32 1's preamble */ 7098c2ecf20Sopenharmony_ci for (i = 0; i < 32; i++) { 7108c2ecf20Sopenharmony_ci /* low MDC; MDO is already high (miir) */ 7118c2ecf20Sopenharmony_ci miir &= ~MASK_MIIR_MII_MDC; 7128c2ecf20Sopenharmony_ci iowrite32(miir, miiport); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci /* high MDC */ 7158c2ecf20Sopenharmony_ci miir |= MASK_MIIR_MII_MDC; 7168c2ecf20Sopenharmony_ci iowrite32(miir, miiport); 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci /* calculate ST+OP+PHYAD+REGAD+TA */ 7208c2ecf20Sopenharmony_ci data = opcode | (phyad << 7) | (regad << 2); 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci /* sent out */ 7238c2ecf20Sopenharmony_ci mask = 0x8000; 7248c2ecf20Sopenharmony_ci while (mask) { 7258c2ecf20Sopenharmony_ci /* low MDC, prepare MDO */ 7268c2ecf20Sopenharmony_ci miir &= ~(MASK_MIIR_MII_MDC + MASK_MIIR_MII_MDO); 7278c2ecf20Sopenharmony_ci if (mask & data) 7288c2ecf20Sopenharmony_ci miir |= MASK_MIIR_MII_MDO; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci iowrite32(miir, miiport); 7318c2ecf20Sopenharmony_ci /* high MDC */ 7328c2ecf20Sopenharmony_ci miir |= MASK_MIIR_MII_MDC; 7338c2ecf20Sopenharmony_ci iowrite32(miir, miiport); 7348c2ecf20Sopenharmony_ci udelay(30); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci /* next */ 7378c2ecf20Sopenharmony_ci mask >>= 1; 7388c2ecf20Sopenharmony_ci if (mask == 0x2 && opcode == OP_READ) 7398c2ecf20Sopenharmony_ci miir &= ~MASK_MIIR_MII_WRITE; 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci return miir; 7428c2ecf20Sopenharmony_ci} 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_cistatic int mdio_read(struct net_device *dev, int phyad, int regad) 7468c2ecf20Sopenharmony_ci{ 7478c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 7488c2ecf20Sopenharmony_ci void __iomem *miiport = np->mem + MANAGEMENT; 7498c2ecf20Sopenharmony_ci ulong miir; 7508c2ecf20Sopenharmony_ci unsigned int mask, data; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci miir = m80x_send_cmd_to_phy(miiport, OP_READ, phyad, regad); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci /* read data */ 7558c2ecf20Sopenharmony_ci mask = 0x8000; 7568c2ecf20Sopenharmony_ci data = 0; 7578c2ecf20Sopenharmony_ci while (mask) { 7588c2ecf20Sopenharmony_ci /* low MDC */ 7598c2ecf20Sopenharmony_ci miir &= ~MASK_MIIR_MII_MDC; 7608c2ecf20Sopenharmony_ci iowrite32(miir, miiport); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci /* read MDI */ 7638c2ecf20Sopenharmony_ci miir = ioread32(miiport); 7648c2ecf20Sopenharmony_ci if (miir & MASK_MIIR_MII_MDI) 7658c2ecf20Sopenharmony_ci data |= mask; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci /* high MDC, and wait */ 7688c2ecf20Sopenharmony_ci miir |= MASK_MIIR_MII_MDC; 7698c2ecf20Sopenharmony_ci iowrite32(miir, miiport); 7708c2ecf20Sopenharmony_ci udelay(30); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci /* next */ 7738c2ecf20Sopenharmony_ci mask >>= 1; 7748c2ecf20Sopenharmony_ci } 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci /* low MDC */ 7778c2ecf20Sopenharmony_ci miir &= ~MASK_MIIR_MII_MDC; 7788c2ecf20Sopenharmony_ci iowrite32(miir, miiport); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci return data & 0xffff; 7818c2ecf20Sopenharmony_ci} 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_cistatic void mdio_write(struct net_device *dev, int phyad, int regad, int data) 7858c2ecf20Sopenharmony_ci{ 7868c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 7878c2ecf20Sopenharmony_ci void __iomem *miiport = np->mem + MANAGEMENT; 7888c2ecf20Sopenharmony_ci ulong miir; 7898c2ecf20Sopenharmony_ci unsigned int mask; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci miir = m80x_send_cmd_to_phy(miiport, OP_WRITE, phyad, regad); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci /* write data */ 7948c2ecf20Sopenharmony_ci mask = 0x8000; 7958c2ecf20Sopenharmony_ci while (mask) { 7968c2ecf20Sopenharmony_ci /* low MDC, prepare MDO */ 7978c2ecf20Sopenharmony_ci miir &= ~(MASK_MIIR_MII_MDC + MASK_MIIR_MII_MDO); 7988c2ecf20Sopenharmony_ci if (mask & data) 7998c2ecf20Sopenharmony_ci miir |= MASK_MIIR_MII_MDO; 8008c2ecf20Sopenharmony_ci iowrite32(miir, miiport); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci /* high MDC */ 8038c2ecf20Sopenharmony_ci miir |= MASK_MIIR_MII_MDC; 8048c2ecf20Sopenharmony_ci iowrite32(miir, miiport); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci /* next */ 8078c2ecf20Sopenharmony_ci mask >>= 1; 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci /* low MDC */ 8118c2ecf20Sopenharmony_ci miir &= ~MASK_MIIR_MII_MDC; 8128c2ecf20Sopenharmony_ci iowrite32(miir, miiport); 8138c2ecf20Sopenharmony_ci} 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_cistatic int netdev_open(struct net_device *dev) 8178c2ecf20Sopenharmony_ci{ 8188c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 8198c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->mem; 8208c2ecf20Sopenharmony_ci const int irq = np->pci_dev->irq; 8218c2ecf20Sopenharmony_ci int rc, i; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci iowrite32(0x00000001, ioaddr + BCR); /* Reset */ 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci rc = request_irq(irq, intr_handler, IRQF_SHARED, dev->name, dev); 8268c2ecf20Sopenharmony_ci if (rc) 8278c2ecf20Sopenharmony_ci return -EAGAIN; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) 8308c2ecf20Sopenharmony_ci iowrite16(((unsigned short*)dev->dev_addr)[i], 8318c2ecf20Sopenharmony_ci ioaddr + PAR0 + i*2); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci init_ring(dev); 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci iowrite32(np->rx_ring_dma, ioaddr + RXLBA); 8368c2ecf20Sopenharmony_ci iowrite32(np->tx_ring_dma, ioaddr + TXLBA); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci /* Initialize other registers. */ 8398c2ecf20Sopenharmony_ci /* Configure the PCI bus bursts and FIFO thresholds. 8408c2ecf20Sopenharmony_ci 486: Set 8 longword burst. 8418c2ecf20Sopenharmony_ci 586: no burst limit. 8428c2ecf20Sopenharmony_ci Burst length 5:3 8438c2ecf20Sopenharmony_ci 0 0 0 1 8448c2ecf20Sopenharmony_ci 0 0 1 4 8458c2ecf20Sopenharmony_ci 0 1 0 8 8468c2ecf20Sopenharmony_ci 0 1 1 16 8478c2ecf20Sopenharmony_ci 1 0 0 32 8488c2ecf20Sopenharmony_ci 1 0 1 64 8498c2ecf20Sopenharmony_ci 1 1 0 128 8508c2ecf20Sopenharmony_ci 1 1 1 256 8518c2ecf20Sopenharmony_ci Wait the specified 50 PCI cycles after a reset by initializing 8528c2ecf20Sopenharmony_ci Tx and Rx queues and the address filter list. 8538c2ecf20Sopenharmony_ci FIXME (Ueimor): optimistic for alpha + posted writes ? */ 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci np->bcrvalue = 0x10; /* little-endian, 8 burst length */ 8568c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN 8578c2ecf20Sopenharmony_ci np->bcrvalue |= 0x04; /* big-endian */ 8588c2ecf20Sopenharmony_ci#endif 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci#if defined(__i386__) && !defined(MODULE) 8618c2ecf20Sopenharmony_ci if (boot_cpu_data.x86 <= 4) 8628c2ecf20Sopenharmony_ci np->crvalue = 0xa00; 8638c2ecf20Sopenharmony_ci else 8648c2ecf20Sopenharmony_ci#endif 8658c2ecf20Sopenharmony_ci np->crvalue = 0xe00; /* rx 128 burst length */ 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci// 89/12/29 add, 8698c2ecf20Sopenharmony_ci// 90/1/16 modify, 8708c2ecf20Sopenharmony_ci// np->imrvalue=FBE|TUNF|CNTOVF|RBU|TI|RI; 8718c2ecf20Sopenharmony_ci np->imrvalue = TUNF | CNTOVF | RBU | TI | RI; 8728c2ecf20Sopenharmony_ci if (np->pci_dev->device == 0x891) { 8738c2ecf20Sopenharmony_ci np->bcrvalue |= 0x200; /* set PROG bit */ 8748c2ecf20Sopenharmony_ci np->crvalue |= CR_W_ENH; /* set enhanced bit */ 8758c2ecf20Sopenharmony_ci np->imrvalue |= ETI; 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci iowrite32(np->bcrvalue, ioaddr + BCR); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci if (dev->if_port == 0) 8808c2ecf20Sopenharmony_ci dev->if_port = np->default_port; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci iowrite32(0, ioaddr + RXPDR); 8838c2ecf20Sopenharmony_ci// 89/9/1 modify, 8848c2ecf20Sopenharmony_ci// np->crvalue = 0x00e40001; /* tx store and forward, tx/rx enable */ 8858c2ecf20Sopenharmony_ci np->crvalue |= 0x00e40001; /* tx store and forward, tx/rx enable */ 8868c2ecf20Sopenharmony_ci np->mii.full_duplex = np->mii.force_media; 8878c2ecf20Sopenharmony_ci getlinkstatus(dev); 8888c2ecf20Sopenharmony_ci if (np->linkok) 8898c2ecf20Sopenharmony_ci getlinktype(dev); 8908c2ecf20Sopenharmony_ci __set_rx_mode(dev); 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci netif_start_queue(dev); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci /* Clear and Enable interrupts by setting the interrupt mask. */ 8958c2ecf20Sopenharmony_ci iowrite32(FBE | TUNF | CNTOVF | RBU | TI | RI, ioaddr + ISR); 8968c2ecf20Sopenharmony_ci iowrite32(np->imrvalue, ioaddr + IMR); 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci if (debug) 8998c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Done netdev_open().\n", dev->name); 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci /* Set the timer to check for link beat. */ 9028c2ecf20Sopenharmony_ci timer_setup(&np->timer, netdev_timer, 0); 9038c2ecf20Sopenharmony_ci np->timer.expires = RUN_AT(3 * HZ); 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci /* timer handler */ 9068c2ecf20Sopenharmony_ci add_timer(&np->timer); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci timer_setup(&np->reset_timer, reset_timer, 0); 9098c2ecf20Sopenharmony_ci np->reset_timer_armed = 0; 9108c2ecf20Sopenharmony_ci return rc; 9118c2ecf20Sopenharmony_ci} 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_cistatic void getlinkstatus(struct net_device *dev) 9158c2ecf20Sopenharmony_ci/* function: Routine will read MII Status Register to get link status. */ 9168c2ecf20Sopenharmony_ci/* input : dev... pointer to the adapter block. */ 9178c2ecf20Sopenharmony_ci/* output : none. */ 9188c2ecf20Sopenharmony_ci{ 9198c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 9208c2ecf20Sopenharmony_ci unsigned int i, DelayTime = 0x1000; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci np->linkok = 0; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci if (np->PHYType == MysonPHY) { 9258c2ecf20Sopenharmony_ci for (i = 0; i < DelayTime; ++i) { 9268c2ecf20Sopenharmony_ci if (ioread32(np->mem + BMCRSR) & LinkIsUp2) { 9278c2ecf20Sopenharmony_ci np->linkok = 1; 9288c2ecf20Sopenharmony_ci return; 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci udelay(100); 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci } else { 9338c2ecf20Sopenharmony_ci for (i = 0; i < DelayTime; ++i) { 9348c2ecf20Sopenharmony_ci if (mdio_read(dev, np->phys[0], MII_BMSR) & BMSR_LSTATUS) { 9358c2ecf20Sopenharmony_ci np->linkok = 1; 9368c2ecf20Sopenharmony_ci return; 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci udelay(100); 9398c2ecf20Sopenharmony_ci } 9408c2ecf20Sopenharmony_ci } 9418c2ecf20Sopenharmony_ci} 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_cistatic void getlinktype(struct net_device *dev) 9458c2ecf20Sopenharmony_ci{ 9468c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci if (np->PHYType == MysonPHY) { /* 3-in-1 case */ 9498c2ecf20Sopenharmony_ci if (ioread32(np->mem + TCRRCR) & CR_R_FD) 9508c2ecf20Sopenharmony_ci np->duplexmode = 2; /* full duplex */ 9518c2ecf20Sopenharmony_ci else 9528c2ecf20Sopenharmony_ci np->duplexmode = 1; /* half duplex */ 9538c2ecf20Sopenharmony_ci if (ioread32(np->mem + TCRRCR) & CR_R_PS10) 9548c2ecf20Sopenharmony_ci np->line_speed = 1; /* 10M */ 9558c2ecf20Sopenharmony_ci else 9568c2ecf20Sopenharmony_ci np->line_speed = 2; /* 100M */ 9578c2ecf20Sopenharmony_ci } else { 9588c2ecf20Sopenharmony_ci if (np->PHYType == SeeqPHY) { /* this PHY is SEEQ 80225 */ 9598c2ecf20Sopenharmony_ci unsigned int data; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci data = mdio_read(dev, np->phys[0], MIIRegister18); 9628c2ecf20Sopenharmony_ci if (data & SPD_DET_100) 9638c2ecf20Sopenharmony_ci np->line_speed = 2; /* 100M */ 9648c2ecf20Sopenharmony_ci else 9658c2ecf20Sopenharmony_ci np->line_speed = 1; /* 10M */ 9668c2ecf20Sopenharmony_ci if (data & DPLX_DET_FULL) 9678c2ecf20Sopenharmony_ci np->duplexmode = 2; /* full duplex mode */ 9688c2ecf20Sopenharmony_ci else 9698c2ecf20Sopenharmony_ci np->duplexmode = 1; /* half duplex mode */ 9708c2ecf20Sopenharmony_ci } else if (np->PHYType == AhdocPHY) { 9718c2ecf20Sopenharmony_ci unsigned int data; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci data = mdio_read(dev, np->phys[0], DiagnosticReg); 9748c2ecf20Sopenharmony_ci if (data & Speed_100) 9758c2ecf20Sopenharmony_ci np->line_speed = 2; /* 100M */ 9768c2ecf20Sopenharmony_ci else 9778c2ecf20Sopenharmony_ci np->line_speed = 1; /* 10M */ 9788c2ecf20Sopenharmony_ci if (data & DPLX_FULL) 9798c2ecf20Sopenharmony_ci np->duplexmode = 2; /* full duplex mode */ 9808c2ecf20Sopenharmony_ci else 9818c2ecf20Sopenharmony_ci np->duplexmode = 1; /* half duplex mode */ 9828c2ecf20Sopenharmony_ci } 9838c2ecf20Sopenharmony_ci/* 89/6/13 add, (begin) */ 9848c2ecf20Sopenharmony_ci else if (np->PHYType == MarvellPHY) { 9858c2ecf20Sopenharmony_ci unsigned int data; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci data = mdio_read(dev, np->phys[0], SpecificReg); 9888c2ecf20Sopenharmony_ci if (data & Full_Duplex) 9898c2ecf20Sopenharmony_ci np->duplexmode = 2; /* full duplex mode */ 9908c2ecf20Sopenharmony_ci else 9918c2ecf20Sopenharmony_ci np->duplexmode = 1; /* half duplex mode */ 9928c2ecf20Sopenharmony_ci data &= SpeedMask; 9938c2ecf20Sopenharmony_ci if (data == Speed_1000M) 9948c2ecf20Sopenharmony_ci np->line_speed = 3; /* 1000M */ 9958c2ecf20Sopenharmony_ci else if (data == Speed_100M) 9968c2ecf20Sopenharmony_ci np->line_speed = 2; /* 100M */ 9978c2ecf20Sopenharmony_ci else 9988c2ecf20Sopenharmony_ci np->line_speed = 1; /* 10M */ 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci/* 89/6/13 add, (end) */ 10018c2ecf20Sopenharmony_ci/* 89/7/27 add, (begin) */ 10028c2ecf20Sopenharmony_ci else if (np->PHYType == Myson981) { 10038c2ecf20Sopenharmony_ci unsigned int data; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci data = mdio_read(dev, np->phys[0], StatusRegister); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci if (data & SPEED100) 10088c2ecf20Sopenharmony_ci np->line_speed = 2; 10098c2ecf20Sopenharmony_ci else 10108c2ecf20Sopenharmony_ci np->line_speed = 1; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci if (data & FULLMODE) 10138c2ecf20Sopenharmony_ci np->duplexmode = 2; 10148c2ecf20Sopenharmony_ci else 10158c2ecf20Sopenharmony_ci np->duplexmode = 1; 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci/* 89/7/27 add, (end) */ 10188c2ecf20Sopenharmony_ci/* 89/12/29 add */ 10198c2ecf20Sopenharmony_ci else if (np->PHYType == LevelOnePHY) { 10208c2ecf20Sopenharmony_ci unsigned int data; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci data = mdio_read(dev, np->phys[0], SpecificReg); 10238c2ecf20Sopenharmony_ci if (data & LXT1000_Full) 10248c2ecf20Sopenharmony_ci np->duplexmode = 2; /* full duplex mode */ 10258c2ecf20Sopenharmony_ci else 10268c2ecf20Sopenharmony_ci np->duplexmode = 1; /* half duplex mode */ 10278c2ecf20Sopenharmony_ci data &= SpeedMask; 10288c2ecf20Sopenharmony_ci if (data == LXT1000_1000M) 10298c2ecf20Sopenharmony_ci np->line_speed = 3; /* 1000M */ 10308c2ecf20Sopenharmony_ci else if (data == LXT1000_100M) 10318c2ecf20Sopenharmony_ci np->line_speed = 2; /* 100M */ 10328c2ecf20Sopenharmony_ci else 10338c2ecf20Sopenharmony_ci np->line_speed = 1; /* 10M */ 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci np->crvalue &= (~CR_W_PS10) & (~CR_W_FD) & (~CR_W_PS1000); 10368c2ecf20Sopenharmony_ci if (np->line_speed == 1) 10378c2ecf20Sopenharmony_ci np->crvalue |= CR_W_PS10; 10388c2ecf20Sopenharmony_ci else if (np->line_speed == 3) 10398c2ecf20Sopenharmony_ci np->crvalue |= CR_W_PS1000; 10408c2ecf20Sopenharmony_ci if (np->duplexmode == 2) 10418c2ecf20Sopenharmony_ci np->crvalue |= CR_W_FD; 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci} 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci/* Take lock before calling this */ 10478c2ecf20Sopenharmony_cistatic void allocate_rx_buffers(struct net_device *dev) 10488c2ecf20Sopenharmony_ci{ 10498c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci /* allocate skb for rx buffers */ 10528c2ecf20Sopenharmony_ci while (np->really_rx_count != RX_RING_SIZE) { 10538c2ecf20Sopenharmony_ci struct sk_buff *skb; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(dev, np->rx_buf_sz); 10568c2ecf20Sopenharmony_ci if (skb == NULL) 10578c2ecf20Sopenharmony_ci break; /* Better luck next round. */ 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci while (np->lack_rxbuf->skbuff) 10608c2ecf20Sopenharmony_ci np->lack_rxbuf = np->lack_rxbuf->next_desc_logical; 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci np->lack_rxbuf->skbuff = skb; 10638c2ecf20Sopenharmony_ci np->lack_rxbuf->buffer = dma_map_single(&np->pci_dev->dev, 10648c2ecf20Sopenharmony_ci skb->data, 10658c2ecf20Sopenharmony_ci np->rx_buf_sz, 10668c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 10678c2ecf20Sopenharmony_ci np->lack_rxbuf->status = RXOWN; 10688c2ecf20Sopenharmony_ci ++np->really_rx_count; 10698c2ecf20Sopenharmony_ci } 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_cistatic void netdev_timer(struct timer_list *t) 10748c2ecf20Sopenharmony_ci{ 10758c2ecf20Sopenharmony_ci struct netdev_private *np = from_timer(np, t, timer); 10768c2ecf20Sopenharmony_ci struct net_device *dev = np->mii.dev; 10778c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->mem; 10788c2ecf20Sopenharmony_ci int old_crvalue = np->crvalue; 10798c2ecf20Sopenharmony_ci unsigned int old_linkok = np->linkok; 10808c2ecf20Sopenharmony_ci unsigned long flags; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci if (debug) 10838c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Media selection timer tick, status %8.8x " 10848c2ecf20Sopenharmony_ci "config %8.8x.\n", dev->name, ioread32(ioaddr + ISR), 10858c2ecf20Sopenharmony_ci ioread32(ioaddr + TCRRCR)); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci if (np->flags == HAS_MII_XCVR) { 10908c2ecf20Sopenharmony_ci getlinkstatus(dev); 10918c2ecf20Sopenharmony_ci if ((old_linkok == 0) && (np->linkok == 1)) { /* we need to detect the media type again */ 10928c2ecf20Sopenharmony_ci getlinktype(dev); 10938c2ecf20Sopenharmony_ci if (np->crvalue != old_crvalue) { 10948c2ecf20Sopenharmony_ci stop_nic_rxtx(ioaddr, np->crvalue); 10958c2ecf20Sopenharmony_ci iowrite32(np->crvalue, ioaddr + TCRRCR); 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci } 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci allocate_rx_buffers(dev); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci np->timer.expires = RUN_AT(10 * HZ); 11058c2ecf20Sopenharmony_ci add_timer(&np->timer); 11068c2ecf20Sopenharmony_ci} 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci/* Take lock before calling */ 11108c2ecf20Sopenharmony_ci/* Reset chip and disable rx, tx and interrupts */ 11118c2ecf20Sopenharmony_cistatic void reset_and_disable_rxtx(struct net_device *dev) 11128c2ecf20Sopenharmony_ci{ 11138c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 11148c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->mem; 11158c2ecf20Sopenharmony_ci int delay=51; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci /* Reset the chip's Tx and Rx processes. */ 11188c2ecf20Sopenharmony_ci stop_nic_rxtx(ioaddr, 0); 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci /* Disable interrupts by clearing the interrupt mask. */ 11218c2ecf20Sopenharmony_ci iowrite32(0, ioaddr + IMR); 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci /* Reset the chip to erase previous misconfiguration. */ 11248c2ecf20Sopenharmony_ci iowrite32(0x00000001, ioaddr + BCR); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci /* Ueimor: wait for 50 PCI cycles (and flush posted writes btw). 11278c2ecf20Sopenharmony_ci We surely wait too long (address+data phase). Who cares? */ 11288c2ecf20Sopenharmony_ci while (--delay) { 11298c2ecf20Sopenharmony_ci ioread32(ioaddr + BCR); 11308c2ecf20Sopenharmony_ci rmb(); 11318c2ecf20Sopenharmony_ci } 11328c2ecf20Sopenharmony_ci} 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci/* Take lock before calling */ 11368c2ecf20Sopenharmony_ci/* Restore chip after reset */ 11378c2ecf20Sopenharmony_cistatic void enable_rxtx(struct net_device *dev) 11388c2ecf20Sopenharmony_ci{ 11398c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 11408c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->mem; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci reset_rx_descriptors(dev); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci iowrite32(np->tx_ring_dma + ((char*)np->cur_tx - (char*)np->tx_ring), 11458c2ecf20Sopenharmony_ci ioaddr + TXLBA); 11468c2ecf20Sopenharmony_ci iowrite32(np->rx_ring_dma + ((char*)np->cur_rx - (char*)np->rx_ring), 11478c2ecf20Sopenharmony_ci ioaddr + RXLBA); 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci iowrite32(np->bcrvalue, ioaddr + BCR); 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci iowrite32(0, ioaddr + RXPDR); 11528c2ecf20Sopenharmony_ci __set_rx_mode(dev); /* changes np->crvalue, writes it into TCRRCR */ 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci /* Clear and Enable interrupts by setting the interrupt mask. */ 11558c2ecf20Sopenharmony_ci iowrite32(FBE | TUNF | CNTOVF | RBU | TI | RI, ioaddr + ISR); 11568c2ecf20Sopenharmony_ci iowrite32(np->imrvalue, ioaddr + IMR); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci iowrite32(0, ioaddr + TXPDR); 11598c2ecf20Sopenharmony_ci} 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_cistatic void reset_timer(struct timer_list *t) 11638c2ecf20Sopenharmony_ci{ 11648c2ecf20Sopenharmony_ci struct netdev_private *np = from_timer(np, t, reset_timer); 11658c2ecf20Sopenharmony_ci struct net_device *dev = np->mii.dev; 11668c2ecf20Sopenharmony_ci unsigned long flags; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: resetting tx and rx machinery\n", dev->name); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 11718c2ecf20Sopenharmony_ci np->crvalue = np->crvalue_sv; 11728c2ecf20Sopenharmony_ci np->imrvalue = np->imrvalue_sv; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci reset_and_disable_rxtx(dev); 11758c2ecf20Sopenharmony_ci /* works for me without this: 11768c2ecf20Sopenharmony_ci reset_tx_descriptors(dev); */ 11778c2ecf20Sopenharmony_ci enable_rxtx(dev); 11788c2ecf20Sopenharmony_ci netif_start_queue(dev); /* FIXME: or netif_wake_queue(dev); ? */ 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci np->reset_timer_armed = 0; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 11838c2ecf20Sopenharmony_ci} 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_cistatic void fealnx_tx_timeout(struct net_device *dev, unsigned int txqueue) 11878c2ecf20Sopenharmony_ci{ 11888c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 11898c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->mem; 11908c2ecf20Sopenharmony_ci unsigned long flags; 11918c2ecf20Sopenharmony_ci int i; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci printk(KERN_WARNING 11948c2ecf20Sopenharmony_ci "%s: Transmit timed out, status %8.8x, resetting...\n", 11958c2ecf20Sopenharmony_ci dev->name, ioread32(ioaddr + ISR)); 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci { 11988c2ecf20Sopenharmony_ci printk(KERN_DEBUG " Rx ring %p: ", np->rx_ring); 11998c2ecf20Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) 12008c2ecf20Sopenharmony_ci printk(KERN_CONT " %8.8x", 12018c2ecf20Sopenharmony_ci (unsigned int) np->rx_ring[i].status); 12028c2ecf20Sopenharmony_ci printk(KERN_CONT "\n"); 12038c2ecf20Sopenharmony_ci printk(KERN_DEBUG " Tx ring %p: ", np->tx_ring); 12048c2ecf20Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) 12058c2ecf20Sopenharmony_ci printk(KERN_CONT " %4.4x", np->tx_ring[i].status); 12068c2ecf20Sopenharmony_ci printk(KERN_CONT "\n"); 12078c2ecf20Sopenharmony_ci } 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci reset_and_disable_rxtx(dev); 12128c2ecf20Sopenharmony_ci reset_tx_descriptors(dev); 12138c2ecf20Sopenharmony_ci enable_rxtx(dev); 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci netif_trans_update(dev); /* prevent tx timeout */ 12188c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 12198c2ecf20Sopenharmony_ci netif_wake_queue(dev); /* or .._start_.. ?? */ 12208c2ecf20Sopenharmony_ci} 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ 12248c2ecf20Sopenharmony_cistatic void init_ring(struct net_device *dev) 12258c2ecf20Sopenharmony_ci{ 12268c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 12278c2ecf20Sopenharmony_ci int i; 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci /* initialize rx variables */ 12308c2ecf20Sopenharmony_ci np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); 12318c2ecf20Sopenharmony_ci np->cur_rx = &np->rx_ring[0]; 12328c2ecf20Sopenharmony_ci np->lack_rxbuf = np->rx_ring; 12338c2ecf20Sopenharmony_ci np->really_rx_count = 0; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci /* initial rx descriptors. */ 12368c2ecf20Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 12378c2ecf20Sopenharmony_ci np->rx_ring[i].status = 0; 12388c2ecf20Sopenharmony_ci np->rx_ring[i].control = np->rx_buf_sz << RBSShift; 12398c2ecf20Sopenharmony_ci np->rx_ring[i].next_desc = np->rx_ring_dma + 12408c2ecf20Sopenharmony_ci (i + 1)*sizeof(struct fealnx_desc); 12418c2ecf20Sopenharmony_ci np->rx_ring[i].next_desc_logical = &np->rx_ring[i + 1]; 12428c2ecf20Sopenharmony_ci np->rx_ring[i].skbuff = NULL; 12438c2ecf20Sopenharmony_ci } 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci /* for the last rx descriptor */ 12468c2ecf20Sopenharmony_ci np->rx_ring[i - 1].next_desc = np->rx_ring_dma; 12478c2ecf20Sopenharmony_ci np->rx_ring[i - 1].next_desc_logical = np->rx_ring; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci /* allocate skb for rx buffers */ 12508c2ecf20Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 12518c2ecf20Sopenharmony_ci struct sk_buff *skb = netdev_alloc_skb(dev, np->rx_buf_sz); 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci if (skb == NULL) { 12548c2ecf20Sopenharmony_ci np->lack_rxbuf = &np->rx_ring[i]; 12558c2ecf20Sopenharmony_ci break; 12568c2ecf20Sopenharmony_ci } 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci ++np->really_rx_count; 12598c2ecf20Sopenharmony_ci np->rx_ring[i].skbuff = skb; 12608c2ecf20Sopenharmony_ci np->rx_ring[i].buffer = dma_map_single(&np->pci_dev->dev, 12618c2ecf20Sopenharmony_ci skb->data, 12628c2ecf20Sopenharmony_ci np->rx_buf_sz, 12638c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 12648c2ecf20Sopenharmony_ci np->rx_ring[i].status = RXOWN; 12658c2ecf20Sopenharmony_ci np->rx_ring[i].control |= RXIC; 12668c2ecf20Sopenharmony_ci } 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci /* initialize tx variables */ 12698c2ecf20Sopenharmony_ci np->cur_tx = &np->tx_ring[0]; 12708c2ecf20Sopenharmony_ci np->cur_tx_copy = &np->tx_ring[0]; 12718c2ecf20Sopenharmony_ci np->really_tx_count = 0; 12728c2ecf20Sopenharmony_ci np->free_tx_count = TX_RING_SIZE; 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) { 12758c2ecf20Sopenharmony_ci np->tx_ring[i].status = 0; 12768c2ecf20Sopenharmony_ci /* do we need np->tx_ring[i].control = XXX; ?? */ 12778c2ecf20Sopenharmony_ci np->tx_ring[i].next_desc = np->tx_ring_dma + 12788c2ecf20Sopenharmony_ci (i + 1)*sizeof(struct fealnx_desc); 12798c2ecf20Sopenharmony_ci np->tx_ring[i].next_desc_logical = &np->tx_ring[i + 1]; 12808c2ecf20Sopenharmony_ci np->tx_ring[i].skbuff = NULL; 12818c2ecf20Sopenharmony_ci } 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci /* for the last tx descriptor */ 12848c2ecf20Sopenharmony_ci np->tx_ring[i - 1].next_desc = np->tx_ring_dma; 12858c2ecf20Sopenharmony_ci np->tx_ring[i - 1].next_desc_logical = &np->tx_ring[0]; 12868c2ecf20Sopenharmony_ci} 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_cistatic netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev) 12908c2ecf20Sopenharmony_ci{ 12918c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 12928c2ecf20Sopenharmony_ci unsigned long flags; 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci spin_lock_irqsave(&np->lock, flags); 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci np->cur_tx_copy->skbuff = skb; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci#define one_buffer 12998c2ecf20Sopenharmony_ci#define BPT 1022 13008c2ecf20Sopenharmony_ci#if defined(one_buffer) 13018c2ecf20Sopenharmony_ci np->cur_tx_copy->buffer = dma_map_single(&np->pci_dev->dev, skb->data, 13028c2ecf20Sopenharmony_ci skb->len, DMA_TO_DEVICE); 13038c2ecf20Sopenharmony_ci np->cur_tx_copy->control = TXIC | TXLD | TXFD | CRCEnable | PADEnable; 13048c2ecf20Sopenharmony_ci np->cur_tx_copy->control |= (skb->len << PKTSShift); /* pkt size */ 13058c2ecf20Sopenharmony_ci np->cur_tx_copy->control |= (skb->len << TBSShift); /* buffer size */ 13068c2ecf20Sopenharmony_ci// 89/12/29 add, 13078c2ecf20Sopenharmony_ci if (np->pci_dev->device == 0x891) 13088c2ecf20Sopenharmony_ci np->cur_tx_copy->control |= ETIControl | RetryTxLC; 13098c2ecf20Sopenharmony_ci np->cur_tx_copy->status = TXOWN; 13108c2ecf20Sopenharmony_ci np->cur_tx_copy = np->cur_tx_copy->next_desc_logical; 13118c2ecf20Sopenharmony_ci --np->free_tx_count; 13128c2ecf20Sopenharmony_ci#elif defined(two_buffer) 13138c2ecf20Sopenharmony_ci if (skb->len > BPT) { 13148c2ecf20Sopenharmony_ci struct fealnx_desc *next; 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci /* for the first descriptor */ 13178c2ecf20Sopenharmony_ci np->cur_tx_copy->buffer = dma_map_single(&np->pci_dev->dev, 13188c2ecf20Sopenharmony_ci skb->data, BPT, 13198c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 13208c2ecf20Sopenharmony_ci np->cur_tx_copy->control = TXIC | TXFD | CRCEnable | PADEnable; 13218c2ecf20Sopenharmony_ci np->cur_tx_copy->control |= (skb->len << PKTSShift); /* pkt size */ 13228c2ecf20Sopenharmony_ci np->cur_tx_copy->control |= (BPT << TBSShift); /* buffer size */ 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci /* for the last descriptor */ 13258c2ecf20Sopenharmony_ci next = np->cur_tx_copy->next_desc_logical; 13268c2ecf20Sopenharmony_ci next->skbuff = skb; 13278c2ecf20Sopenharmony_ci next->control = TXIC | TXLD | CRCEnable | PADEnable; 13288c2ecf20Sopenharmony_ci next->control |= (skb->len << PKTSShift); /* pkt size */ 13298c2ecf20Sopenharmony_ci next->control |= ((skb->len - BPT) << TBSShift); /* buf size */ 13308c2ecf20Sopenharmony_ci// 89/12/29 add, 13318c2ecf20Sopenharmony_ci if (np->pci_dev->device == 0x891) 13328c2ecf20Sopenharmony_ci np->cur_tx_copy->control |= ETIControl | RetryTxLC; 13338c2ecf20Sopenharmony_ci next->buffer = dma_map_single(&ep->pci_dev->dev, 13348c2ecf20Sopenharmony_ci skb->data + BPT, skb->len - BPT, 13358c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci next->status = TXOWN; 13388c2ecf20Sopenharmony_ci np->cur_tx_copy->status = TXOWN; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci np->cur_tx_copy = next->next_desc_logical; 13418c2ecf20Sopenharmony_ci np->free_tx_count -= 2; 13428c2ecf20Sopenharmony_ci } else { 13438c2ecf20Sopenharmony_ci np->cur_tx_copy->buffer = dma_map_single(&np->pci_dev->dev, 13448c2ecf20Sopenharmony_ci skb->data, skb->len, 13458c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 13468c2ecf20Sopenharmony_ci np->cur_tx_copy->control = TXIC | TXLD | TXFD | CRCEnable | PADEnable; 13478c2ecf20Sopenharmony_ci np->cur_tx_copy->control |= (skb->len << PKTSShift); /* pkt size */ 13488c2ecf20Sopenharmony_ci np->cur_tx_copy->control |= (skb->len << TBSShift); /* buffer size */ 13498c2ecf20Sopenharmony_ci// 89/12/29 add, 13508c2ecf20Sopenharmony_ci if (np->pci_dev->device == 0x891) 13518c2ecf20Sopenharmony_ci np->cur_tx_copy->control |= ETIControl | RetryTxLC; 13528c2ecf20Sopenharmony_ci np->cur_tx_copy->status = TXOWN; 13538c2ecf20Sopenharmony_ci np->cur_tx_copy = np->cur_tx_copy->next_desc_logical; 13548c2ecf20Sopenharmony_ci --np->free_tx_count; 13558c2ecf20Sopenharmony_ci } 13568c2ecf20Sopenharmony_ci#endif 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci if (np->free_tx_count < 2) 13598c2ecf20Sopenharmony_ci netif_stop_queue(dev); 13608c2ecf20Sopenharmony_ci ++np->really_tx_count; 13618c2ecf20Sopenharmony_ci iowrite32(0, np->mem + TXPDR); 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&np->lock, flags); 13648c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 13658c2ecf20Sopenharmony_ci} 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci/* Take lock before calling */ 13698c2ecf20Sopenharmony_ci/* Chip probably hosed tx ring. Clean up. */ 13708c2ecf20Sopenharmony_cistatic void reset_tx_descriptors(struct net_device *dev) 13718c2ecf20Sopenharmony_ci{ 13728c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 13738c2ecf20Sopenharmony_ci struct fealnx_desc *cur; 13748c2ecf20Sopenharmony_ci int i; 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci /* initialize tx variables */ 13778c2ecf20Sopenharmony_ci np->cur_tx = &np->tx_ring[0]; 13788c2ecf20Sopenharmony_ci np->cur_tx_copy = &np->tx_ring[0]; 13798c2ecf20Sopenharmony_ci np->really_tx_count = 0; 13808c2ecf20Sopenharmony_ci np->free_tx_count = TX_RING_SIZE; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) { 13838c2ecf20Sopenharmony_ci cur = &np->tx_ring[i]; 13848c2ecf20Sopenharmony_ci if (cur->skbuff) { 13858c2ecf20Sopenharmony_ci dma_unmap_single(&np->pci_dev->dev, cur->buffer, 13868c2ecf20Sopenharmony_ci cur->skbuff->len, DMA_TO_DEVICE); 13878c2ecf20Sopenharmony_ci dev_kfree_skb_any(cur->skbuff); 13888c2ecf20Sopenharmony_ci cur->skbuff = NULL; 13898c2ecf20Sopenharmony_ci } 13908c2ecf20Sopenharmony_ci cur->status = 0; 13918c2ecf20Sopenharmony_ci cur->control = 0; /* needed? */ 13928c2ecf20Sopenharmony_ci /* probably not needed. We do it for purely paranoid reasons */ 13938c2ecf20Sopenharmony_ci cur->next_desc = np->tx_ring_dma + 13948c2ecf20Sopenharmony_ci (i + 1)*sizeof(struct fealnx_desc); 13958c2ecf20Sopenharmony_ci cur->next_desc_logical = &np->tx_ring[i + 1]; 13968c2ecf20Sopenharmony_ci } 13978c2ecf20Sopenharmony_ci /* for the last tx descriptor */ 13988c2ecf20Sopenharmony_ci np->tx_ring[TX_RING_SIZE - 1].next_desc = np->tx_ring_dma; 13998c2ecf20Sopenharmony_ci np->tx_ring[TX_RING_SIZE - 1].next_desc_logical = &np->tx_ring[0]; 14008c2ecf20Sopenharmony_ci} 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci/* Take lock and stop rx before calling this */ 14048c2ecf20Sopenharmony_cistatic void reset_rx_descriptors(struct net_device *dev) 14058c2ecf20Sopenharmony_ci{ 14068c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 14078c2ecf20Sopenharmony_ci struct fealnx_desc *cur = np->cur_rx; 14088c2ecf20Sopenharmony_ci int i; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci allocate_rx_buffers(dev); 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 14138c2ecf20Sopenharmony_ci if (cur->skbuff) 14148c2ecf20Sopenharmony_ci cur->status = RXOWN; 14158c2ecf20Sopenharmony_ci cur = cur->next_desc_logical; 14168c2ecf20Sopenharmony_ci } 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci iowrite32(np->rx_ring_dma + ((char*)np->cur_rx - (char*)np->rx_ring), 14198c2ecf20Sopenharmony_ci np->mem + RXLBA); 14208c2ecf20Sopenharmony_ci} 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci/* The interrupt handler does all of the Rx thread work and cleans up 14248c2ecf20Sopenharmony_ci after the Tx thread. */ 14258c2ecf20Sopenharmony_cistatic irqreturn_t intr_handler(int irq, void *dev_instance) 14268c2ecf20Sopenharmony_ci{ 14278c2ecf20Sopenharmony_ci struct net_device *dev = (struct net_device *) dev_instance; 14288c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 14298c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->mem; 14308c2ecf20Sopenharmony_ci long boguscnt = max_interrupt_work; 14318c2ecf20Sopenharmony_ci unsigned int num_tx = 0; 14328c2ecf20Sopenharmony_ci int handled = 0; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci spin_lock(&np->lock); 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci iowrite32(0, ioaddr + IMR); 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci do { 14398c2ecf20Sopenharmony_ci u32 intr_status = ioread32(ioaddr + ISR); 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci /* Acknowledge all of the current interrupt sources ASAP. */ 14428c2ecf20Sopenharmony_ci iowrite32(intr_status, ioaddr + ISR); 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci if (debug) 14458c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n", dev->name, 14468c2ecf20Sopenharmony_ci intr_status); 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci if (!(intr_status & np->imrvalue)) 14498c2ecf20Sopenharmony_ci break; 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci handled = 1; 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci// 90/1/16 delete, 14548c2ecf20Sopenharmony_ci// 14558c2ecf20Sopenharmony_ci// if (intr_status & FBE) 14568c2ecf20Sopenharmony_ci// { /* fatal error */ 14578c2ecf20Sopenharmony_ci// stop_nic_tx(ioaddr, 0); 14588c2ecf20Sopenharmony_ci// stop_nic_rx(ioaddr, 0); 14598c2ecf20Sopenharmony_ci// break; 14608c2ecf20Sopenharmony_ci// }; 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci if (intr_status & TUNF) 14638c2ecf20Sopenharmony_ci iowrite32(0, ioaddr + TXPDR); 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci if (intr_status & CNTOVF) { 14668c2ecf20Sopenharmony_ci /* missed pkts */ 14678c2ecf20Sopenharmony_ci dev->stats.rx_missed_errors += 14688c2ecf20Sopenharmony_ci ioread32(ioaddr + TALLY) & 0x7fff; 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci /* crc error */ 14718c2ecf20Sopenharmony_ci dev->stats.rx_crc_errors += 14728c2ecf20Sopenharmony_ci (ioread32(ioaddr + TALLY) & 0x7fff0000) >> 16; 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci if (intr_status & (RI | RBU)) { 14768c2ecf20Sopenharmony_ci if (intr_status & RI) 14778c2ecf20Sopenharmony_ci netdev_rx(dev); 14788c2ecf20Sopenharmony_ci else { 14798c2ecf20Sopenharmony_ci stop_nic_rx(ioaddr, np->crvalue); 14808c2ecf20Sopenharmony_ci reset_rx_descriptors(dev); 14818c2ecf20Sopenharmony_ci iowrite32(np->crvalue, ioaddr + TCRRCR); 14828c2ecf20Sopenharmony_ci } 14838c2ecf20Sopenharmony_ci } 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci while (np->really_tx_count) { 14868c2ecf20Sopenharmony_ci long tx_status = np->cur_tx->status; 14878c2ecf20Sopenharmony_ci long tx_control = np->cur_tx->control; 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci if (!(tx_control & TXLD)) { /* this pkt is combined by two tx descriptors */ 14908c2ecf20Sopenharmony_ci struct fealnx_desc *next; 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci next = np->cur_tx->next_desc_logical; 14938c2ecf20Sopenharmony_ci tx_status = next->status; 14948c2ecf20Sopenharmony_ci tx_control = next->control; 14958c2ecf20Sopenharmony_ci } 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci if (tx_status & TXOWN) 14988c2ecf20Sopenharmony_ci break; 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci if (!(np->crvalue & CR_W_ENH)) { 15018c2ecf20Sopenharmony_ci if (tx_status & (CSL | LC | EC | UDF | HF)) { 15028c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 15038c2ecf20Sopenharmony_ci if (tx_status & EC) 15048c2ecf20Sopenharmony_ci dev->stats.tx_aborted_errors++; 15058c2ecf20Sopenharmony_ci if (tx_status & CSL) 15068c2ecf20Sopenharmony_ci dev->stats.tx_carrier_errors++; 15078c2ecf20Sopenharmony_ci if (tx_status & LC) 15088c2ecf20Sopenharmony_ci dev->stats.tx_window_errors++; 15098c2ecf20Sopenharmony_ci if (tx_status & UDF) 15108c2ecf20Sopenharmony_ci dev->stats.tx_fifo_errors++; 15118c2ecf20Sopenharmony_ci if ((tx_status & HF) && np->mii.full_duplex == 0) 15128c2ecf20Sopenharmony_ci dev->stats.tx_heartbeat_errors++; 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci } else { 15158c2ecf20Sopenharmony_ci dev->stats.tx_bytes += 15168c2ecf20Sopenharmony_ci ((tx_control & PKTSMask) >> PKTSShift); 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci dev->stats.collisions += 15198c2ecf20Sopenharmony_ci ((tx_status & NCRMask) >> NCRShift); 15208c2ecf20Sopenharmony_ci dev->stats.tx_packets++; 15218c2ecf20Sopenharmony_ci } 15228c2ecf20Sopenharmony_ci } else { 15238c2ecf20Sopenharmony_ci dev->stats.tx_bytes += 15248c2ecf20Sopenharmony_ci ((tx_control & PKTSMask) >> PKTSShift); 15258c2ecf20Sopenharmony_ci dev->stats.tx_packets++; 15268c2ecf20Sopenharmony_ci } 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci /* Free the original skb. */ 15298c2ecf20Sopenharmony_ci dma_unmap_single(&np->pci_dev->dev, 15308c2ecf20Sopenharmony_ci np->cur_tx->buffer, 15318c2ecf20Sopenharmony_ci np->cur_tx->skbuff->len, 15328c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 15338c2ecf20Sopenharmony_ci dev_consume_skb_irq(np->cur_tx->skbuff); 15348c2ecf20Sopenharmony_ci np->cur_tx->skbuff = NULL; 15358c2ecf20Sopenharmony_ci --np->really_tx_count; 15368c2ecf20Sopenharmony_ci if (np->cur_tx->control & TXLD) { 15378c2ecf20Sopenharmony_ci np->cur_tx = np->cur_tx->next_desc_logical; 15388c2ecf20Sopenharmony_ci ++np->free_tx_count; 15398c2ecf20Sopenharmony_ci } else { 15408c2ecf20Sopenharmony_ci np->cur_tx = np->cur_tx->next_desc_logical; 15418c2ecf20Sopenharmony_ci np->cur_tx = np->cur_tx->next_desc_logical; 15428c2ecf20Sopenharmony_ci np->free_tx_count += 2; 15438c2ecf20Sopenharmony_ci } 15448c2ecf20Sopenharmony_ci num_tx++; 15458c2ecf20Sopenharmony_ci } /* end of for loop */ 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci if (num_tx && np->free_tx_count >= 2) 15488c2ecf20Sopenharmony_ci netif_wake_queue(dev); 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci /* read transmit status for enhanced mode only */ 15518c2ecf20Sopenharmony_ci if (np->crvalue & CR_W_ENH) { 15528c2ecf20Sopenharmony_ci long data; 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci data = ioread32(ioaddr + TSR); 15558c2ecf20Sopenharmony_ci dev->stats.tx_errors += (data & 0xff000000) >> 24; 15568c2ecf20Sopenharmony_ci dev->stats.tx_aborted_errors += 15578c2ecf20Sopenharmony_ci (data & 0xff000000) >> 24; 15588c2ecf20Sopenharmony_ci dev->stats.tx_window_errors += 15598c2ecf20Sopenharmony_ci (data & 0x00ff0000) >> 16; 15608c2ecf20Sopenharmony_ci dev->stats.collisions += (data & 0x0000ffff); 15618c2ecf20Sopenharmony_ci } 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci if (--boguscnt < 0) { 15648c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Too much work at interrupt, " 15658c2ecf20Sopenharmony_ci "status=0x%4.4x.\n", dev->name, intr_status); 15668c2ecf20Sopenharmony_ci if (!np->reset_timer_armed) { 15678c2ecf20Sopenharmony_ci np->reset_timer_armed = 1; 15688c2ecf20Sopenharmony_ci np->reset_timer.expires = RUN_AT(HZ/2); 15698c2ecf20Sopenharmony_ci add_timer(&np->reset_timer); 15708c2ecf20Sopenharmony_ci stop_nic_rxtx(ioaddr, 0); 15718c2ecf20Sopenharmony_ci netif_stop_queue(dev); 15728c2ecf20Sopenharmony_ci /* or netif_tx_disable(dev); ?? */ 15738c2ecf20Sopenharmony_ci /* Prevent other paths from enabling tx,rx,intrs */ 15748c2ecf20Sopenharmony_ci np->crvalue_sv = np->crvalue; 15758c2ecf20Sopenharmony_ci np->imrvalue_sv = np->imrvalue; 15768c2ecf20Sopenharmony_ci np->crvalue &= ~(CR_W_TXEN | CR_W_RXEN); /* or simply = 0? */ 15778c2ecf20Sopenharmony_ci np->imrvalue = 0; 15788c2ecf20Sopenharmony_ci } 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci break; 15818c2ecf20Sopenharmony_ci } 15828c2ecf20Sopenharmony_ci } while (1); 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci /* read the tally counters */ 15858c2ecf20Sopenharmony_ci /* missed pkts */ 15868c2ecf20Sopenharmony_ci dev->stats.rx_missed_errors += ioread32(ioaddr + TALLY) & 0x7fff; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci /* crc error */ 15898c2ecf20Sopenharmony_ci dev->stats.rx_crc_errors += 15908c2ecf20Sopenharmony_ci (ioread32(ioaddr + TALLY) & 0x7fff0000) >> 16; 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci if (debug) 15938c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n", 15948c2ecf20Sopenharmony_ci dev->name, ioread32(ioaddr + ISR)); 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci iowrite32(np->imrvalue, ioaddr + IMR); 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci spin_unlock(&np->lock); 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci return IRQ_RETVAL(handled); 16018c2ecf20Sopenharmony_ci} 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci/* This routine is logically part of the interrupt handler, but separated 16058c2ecf20Sopenharmony_ci for clarity and better register allocation. */ 16068c2ecf20Sopenharmony_cistatic int netdev_rx(struct net_device *dev) 16078c2ecf20Sopenharmony_ci{ 16088c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 16098c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->mem; 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci /* If EOP is set on the next entry, it's a new packet. Send it up. */ 16128c2ecf20Sopenharmony_ci while (!(np->cur_rx->status & RXOWN) && np->cur_rx->skbuff) { 16138c2ecf20Sopenharmony_ci s32 rx_status = np->cur_rx->status; 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci if (np->really_rx_count == 0) 16168c2ecf20Sopenharmony_ci break; 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci if (debug) 16198c2ecf20Sopenharmony_ci printk(KERN_DEBUG " netdev_rx() status was %8.8x.\n", rx_status); 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci if ((!((rx_status & RXFSD) && (rx_status & RXLSD))) || 16228c2ecf20Sopenharmony_ci (rx_status & ErrorSummary)) { 16238c2ecf20Sopenharmony_ci if (rx_status & ErrorSummary) { /* there was a fatal error */ 16248c2ecf20Sopenharmony_ci if (debug) 16258c2ecf20Sopenharmony_ci printk(KERN_DEBUG 16268c2ecf20Sopenharmony_ci "%s: Receive error, Rx status %8.8x.\n", 16278c2ecf20Sopenharmony_ci dev->name, rx_status); 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci dev->stats.rx_errors++; /* end of a packet. */ 16308c2ecf20Sopenharmony_ci if (rx_status & (LONGPKT | RUNTPKT)) 16318c2ecf20Sopenharmony_ci dev->stats.rx_length_errors++; 16328c2ecf20Sopenharmony_ci if (rx_status & RXER) 16338c2ecf20Sopenharmony_ci dev->stats.rx_frame_errors++; 16348c2ecf20Sopenharmony_ci if (rx_status & CRC) 16358c2ecf20Sopenharmony_ci dev->stats.rx_crc_errors++; 16368c2ecf20Sopenharmony_ci } else { 16378c2ecf20Sopenharmony_ci int need_to_reset = 0; 16388c2ecf20Sopenharmony_ci int desno = 0; 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci if (rx_status & RXFSD) { /* this pkt is too long, over one rx buffer */ 16418c2ecf20Sopenharmony_ci struct fealnx_desc *cur; 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci /* check this packet is received completely? */ 16448c2ecf20Sopenharmony_ci cur = np->cur_rx; 16458c2ecf20Sopenharmony_ci while (desno <= np->really_rx_count) { 16468c2ecf20Sopenharmony_ci ++desno; 16478c2ecf20Sopenharmony_ci if ((!(cur->status & RXOWN)) && 16488c2ecf20Sopenharmony_ci (cur->status & RXLSD)) 16498c2ecf20Sopenharmony_ci break; 16508c2ecf20Sopenharmony_ci /* goto next rx descriptor */ 16518c2ecf20Sopenharmony_ci cur = cur->next_desc_logical; 16528c2ecf20Sopenharmony_ci } 16538c2ecf20Sopenharmony_ci if (desno > np->really_rx_count) 16548c2ecf20Sopenharmony_ci need_to_reset = 1; 16558c2ecf20Sopenharmony_ci } else /* RXLSD did not find, something error */ 16568c2ecf20Sopenharmony_ci need_to_reset = 1; 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci if (need_to_reset == 0) { 16598c2ecf20Sopenharmony_ci int i; 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci dev->stats.rx_length_errors++; 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci /* free all rx descriptors related this long pkt */ 16648c2ecf20Sopenharmony_ci for (i = 0; i < desno; ++i) { 16658c2ecf20Sopenharmony_ci if (!np->cur_rx->skbuff) { 16668c2ecf20Sopenharmony_ci printk(KERN_DEBUG 16678c2ecf20Sopenharmony_ci "%s: I'm scared\n", dev->name); 16688c2ecf20Sopenharmony_ci break; 16698c2ecf20Sopenharmony_ci } 16708c2ecf20Sopenharmony_ci np->cur_rx->status = RXOWN; 16718c2ecf20Sopenharmony_ci np->cur_rx = np->cur_rx->next_desc_logical; 16728c2ecf20Sopenharmony_ci } 16738c2ecf20Sopenharmony_ci continue; 16748c2ecf20Sopenharmony_ci } else { /* rx error, need to reset this chip */ 16758c2ecf20Sopenharmony_ci stop_nic_rx(ioaddr, np->crvalue); 16768c2ecf20Sopenharmony_ci reset_rx_descriptors(dev); 16778c2ecf20Sopenharmony_ci iowrite32(np->crvalue, ioaddr + TCRRCR); 16788c2ecf20Sopenharmony_ci } 16798c2ecf20Sopenharmony_ci break; /* exit the while loop */ 16808c2ecf20Sopenharmony_ci } 16818c2ecf20Sopenharmony_ci } else { /* this received pkt is ok */ 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci struct sk_buff *skb; 16848c2ecf20Sopenharmony_ci /* Omit the four octet CRC from the length. */ 16858c2ecf20Sopenharmony_ci short pkt_len = ((rx_status & FLNGMASK) >> FLNGShift) - 4; 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci#ifndef final_version 16888c2ecf20Sopenharmony_ci if (debug) 16898c2ecf20Sopenharmony_ci printk(KERN_DEBUG " netdev_rx() normal Rx pkt length %d" 16908c2ecf20Sopenharmony_ci " status %x.\n", pkt_len, rx_status); 16918c2ecf20Sopenharmony_ci#endif 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci /* Check if the packet is long enough to accept without copying 16948c2ecf20Sopenharmony_ci to a minimally-sized skbuff. */ 16958c2ecf20Sopenharmony_ci if (pkt_len < rx_copybreak && 16968c2ecf20Sopenharmony_ci (skb = netdev_alloc_skb(dev, pkt_len + 2)) != NULL) { 16978c2ecf20Sopenharmony_ci skb_reserve(skb, 2); /* 16 byte align the IP header */ 16988c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(&np->pci_dev->dev, 16998c2ecf20Sopenharmony_ci np->cur_rx->buffer, 17008c2ecf20Sopenharmony_ci np->rx_buf_sz, 17018c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 17028c2ecf20Sopenharmony_ci /* Call copy + cksum if available. */ 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci#if ! defined(__alpha__) 17058c2ecf20Sopenharmony_ci skb_copy_to_linear_data(skb, 17068c2ecf20Sopenharmony_ci np->cur_rx->skbuff->data, pkt_len); 17078c2ecf20Sopenharmony_ci skb_put(skb, pkt_len); 17088c2ecf20Sopenharmony_ci#else 17098c2ecf20Sopenharmony_ci skb_put_data(skb, np->cur_rx->skbuff->data, 17108c2ecf20Sopenharmony_ci pkt_len); 17118c2ecf20Sopenharmony_ci#endif 17128c2ecf20Sopenharmony_ci dma_sync_single_for_device(&np->pci_dev->dev, 17138c2ecf20Sopenharmony_ci np->cur_rx->buffer, 17148c2ecf20Sopenharmony_ci np->rx_buf_sz, 17158c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 17168c2ecf20Sopenharmony_ci } else { 17178c2ecf20Sopenharmony_ci dma_unmap_single(&np->pci_dev->dev, 17188c2ecf20Sopenharmony_ci np->cur_rx->buffer, 17198c2ecf20Sopenharmony_ci np->rx_buf_sz, 17208c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 17218c2ecf20Sopenharmony_ci skb_put(skb = np->cur_rx->skbuff, pkt_len); 17228c2ecf20Sopenharmony_ci np->cur_rx->skbuff = NULL; 17238c2ecf20Sopenharmony_ci --np->really_rx_count; 17248c2ecf20Sopenharmony_ci } 17258c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 17268c2ecf20Sopenharmony_ci netif_rx(skb); 17278c2ecf20Sopenharmony_ci dev->stats.rx_packets++; 17288c2ecf20Sopenharmony_ci dev->stats.rx_bytes += pkt_len; 17298c2ecf20Sopenharmony_ci } 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci np->cur_rx = np->cur_rx->next_desc_logical; 17328c2ecf20Sopenharmony_ci } /* end of while loop */ 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci /* allocate skb for rx buffers */ 17358c2ecf20Sopenharmony_ci allocate_rx_buffers(dev); 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci return 0; 17388c2ecf20Sopenharmony_ci} 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_cistatic struct net_device_stats *get_stats(struct net_device *dev) 17428c2ecf20Sopenharmony_ci{ 17438c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 17448c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->mem; 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_ci /* The chip only need report frame silently dropped. */ 17478c2ecf20Sopenharmony_ci if (netif_running(dev)) { 17488c2ecf20Sopenharmony_ci dev->stats.rx_missed_errors += 17498c2ecf20Sopenharmony_ci ioread32(ioaddr + TALLY) & 0x7fff; 17508c2ecf20Sopenharmony_ci dev->stats.rx_crc_errors += 17518c2ecf20Sopenharmony_ci (ioread32(ioaddr + TALLY) & 0x7fff0000) >> 16; 17528c2ecf20Sopenharmony_ci } 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci return &dev->stats; 17558c2ecf20Sopenharmony_ci} 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci/* for dev->set_multicast_list */ 17598c2ecf20Sopenharmony_cistatic void set_rx_mode(struct net_device *dev) 17608c2ecf20Sopenharmony_ci{ 17618c2ecf20Sopenharmony_ci spinlock_t *lp = &((struct netdev_private *)netdev_priv(dev))->lock; 17628c2ecf20Sopenharmony_ci unsigned long flags; 17638c2ecf20Sopenharmony_ci spin_lock_irqsave(lp, flags); 17648c2ecf20Sopenharmony_ci __set_rx_mode(dev); 17658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(lp, flags); 17668c2ecf20Sopenharmony_ci} 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci/* Take lock before calling */ 17708c2ecf20Sopenharmony_cistatic void __set_rx_mode(struct net_device *dev) 17718c2ecf20Sopenharmony_ci{ 17728c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 17738c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->mem; 17748c2ecf20Sopenharmony_ci u32 mc_filter[2]; /* Multicast hash filter */ 17758c2ecf20Sopenharmony_ci u32 rx_mode; 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ 17788c2ecf20Sopenharmony_ci memset(mc_filter, 0xff, sizeof(mc_filter)); 17798c2ecf20Sopenharmony_ci rx_mode = CR_W_PROM | CR_W_AB | CR_W_AM; 17808c2ecf20Sopenharmony_ci } else if ((netdev_mc_count(dev) > multicast_filter_limit) || 17818c2ecf20Sopenharmony_ci (dev->flags & IFF_ALLMULTI)) { 17828c2ecf20Sopenharmony_ci /* Too many to match, or accept all multicasts. */ 17838c2ecf20Sopenharmony_ci memset(mc_filter, 0xff, sizeof(mc_filter)); 17848c2ecf20Sopenharmony_ci rx_mode = CR_W_AB | CR_W_AM; 17858c2ecf20Sopenharmony_ci } else { 17868c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci memset(mc_filter, 0, sizeof(mc_filter)); 17898c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 17908c2ecf20Sopenharmony_ci unsigned int bit; 17918c2ecf20Sopenharmony_ci bit = (ether_crc(ETH_ALEN, ha->addr) >> 26) ^ 0x3F; 17928c2ecf20Sopenharmony_ci mc_filter[bit >> 5] |= (1 << bit); 17938c2ecf20Sopenharmony_ci } 17948c2ecf20Sopenharmony_ci rx_mode = CR_W_AB | CR_W_AM; 17958c2ecf20Sopenharmony_ci } 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci stop_nic_rxtx(ioaddr, np->crvalue); 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci iowrite32(mc_filter[0], ioaddr + MAR0); 18008c2ecf20Sopenharmony_ci iowrite32(mc_filter[1], ioaddr + MAR1); 18018c2ecf20Sopenharmony_ci np->crvalue &= ~CR_W_RXMODEMASK; 18028c2ecf20Sopenharmony_ci np->crvalue |= rx_mode; 18038c2ecf20Sopenharmony_ci iowrite32(np->crvalue, ioaddr + TCRRCR); 18048c2ecf20Sopenharmony_ci} 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_cistatic void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) 18078c2ecf20Sopenharmony_ci{ 18088c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); 18118c2ecf20Sopenharmony_ci strlcpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info)); 18128c2ecf20Sopenharmony_ci} 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_cistatic int netdev_get_link_ksettings(struct net_device *dev, 18158c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 18168c2ecf20Sopenharmony_ci{ 18178c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci spin_lock_irq(&np->lock); 18208c2ecf20Sopenharmony_ci mii_ethtool_get_link_ksettings(&np->mii, cmd); 18218c2ecf20Sopenharmony_ci spin_unlock_irq(&np->lock); 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci return 0; 18248c2ecf20Sopenharmony_ci} 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_cistatic int netdev_set_link_ksettings(struct net_device *dev, 18278c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 18288c2ecf20Sopenharmony_ci{ 18298c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 18308c2ecf20Sopenharmony_ci int rc; 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci spin_lock_irq(&np->lock); 18338c2ecf20Sopenharmony_ci rc = mii_ethtool_set_link_ksettings(&np->mii, cmd); 18348c2ecf20Sopenharmony_ci spin_unlock_irq(&np->lock); 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci return rc; 18378c2ecf20Sopenharmony_ci} 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_cistatic int netdev_nway_reset(struct net_device *dev) 18408c2ecf20Sopenharmony_ci{ 18418c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 18428c2ecf20Sopenharmony_ci return mii_nway_restart(&np->mii); 18438c2ecf20Sopenharmony_ci} 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_cistatic u32 netdev_get_link(struct net_device *dev) 18468c2ecf20Sopenharmony_ci{ 18478c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 18488c2ecf20Sopenharmony_ci return mii_link_ok(&np->mii); 18498c2ecf20Sopenharmony_ci} 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_cistatic u32 netdev_get_msglevel(struct net_device *dev) 18528c2ecf20Sopenharmony_ci{ 18538c2ecf20Sopenharmony_ci return debug; 18548c2ecf20Sopenharmony_ci} 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_cistatic void netdev_set_msglevel(struct net_device *dev, u32 value) 18578c2ecf20Sopenharmony_ci{ 18588c2ecf20Sopenharmony_ci debug = value; 18598c2ecf20Sopenharmony_ci} 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_cistatic const struct ethtool_ops netdev_ethtool_ops = { 18628c2ecf20Sopenharmony_ci .get_drvinfo = netdev_get_drvinfo, 18638c2ecf20Sopenharmony_ci .nway_reset = netdev_nway_reset, 18648c2ecf20Sopenharmony_ci .get_link = netdev_get_link, 18658c2ecf20Sopenharmony_ci .get_msglevel = netdev_get_msglevel, 18668c2ecf20Sopenharmony_ci .set_msglevel = netdev_set_msglevel, 18678c2ecf20Sopenharmony_ci .get_link_ksettings = netdev_get_link_ksettings, 18688c2ecf20Sopenharmony_ci .set_link_ksettings = netdev_set_link_ksettings, 18698c2ecf20Sopenharmony_ci}; 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_cistatic int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 18728c2ecf20Sopenharmony_ci{ 18738c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 18748c2ecf20Sopenharmony_ci int rc; 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_ci if (!netif_running(dev)) 18778c2ecf20Sopenharmony_ci return -EINVAL; 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci spin_lock_irq(&np->lock); 18808c2ecf20Sopenharmony_ci rc = generic_mii_ioctl(&np->mii, if_mii(rq), cmd, NULL); 18818c2ecf20Sopenharmony_ci spin_unlock_irq(&np->lock); 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci return rc; 18848c2ecf20Sopenharmony_ci} 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_cistatic int netdev_close(struct net_device *dev) 18888c2ecf20Sopenharmony_ci{ 18898c2ecf20Sopenharmony_ci struct netdev_private *np = netdev_priv(dev); 18908c2ecf20Sopenharmony_ci void __iomem *ioaddr = np->mem; 18918c2ecf20Sopenharmony_ci int i; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci netif_stop_queue(dev); 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci /* Disable interrupts by clearing the interrupt mask. */ 18968c2ecf20Sopenharmony_ci iowrite32(0x0000, ioaddr + IMR); 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci /* Stop the chip's Tx and Rx processes. */ 18998c2ecf20Sopenharmony_ci stop_nic_rxtx(ioaddr, 0); 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci del_timer_sync(&np->timer); 19028c2ecf20Sopenharmony_ci del_timer_sync(&np->reset_timer); 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci free_irq(np->pci_dev->irq, dev); 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci /* Free all the skbuffs in the Rx queue. */ 19078c2ecf20Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 19088c2ecf20Sopenharmony_ci struct sk_buff *skb = np->rx_ring[i].skbuff; 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci np->rx_ring[i].status = 0; 19118c2ecf20Sopenharmony_ci if (skb) { 19128c2ecf20Sopenharmony_ci dma_unmap_single(&np->pci_dev->dev, 19138c2ecf20Sopenharmony_ci np->rx_ring[i].buffer, np->rx_buf_sz, 19148c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 19158c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 19168c2ecf20Sopenharmony_ci np->rx_ring[i].skbuff = NULL; 19178c2ecf20Sopenharmony_ci } 19188c2ecf20Sopenharmony_ci } 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) { 19218c2ecf20Sopenharmony_ci struct sk_buff *skb = np->tx_ring[i].skbuff; 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci if (skb) { 19248c2ecf20Sopenharmony_ci dma_unmap_single(&np->pci_dev->dev, 19258c2ecf20Sopenharmony_ci np->tx_ring[i].buffer, skb->len, 19268c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 19278c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 19288c2ecf20Sopenharmony_ci np->tx_ring[i].skbuff = NULL; 19298c2ecf20Sopenharmony_ci } 19308c2ecf20Sopenharmony_ci } 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci return 0; 19338c2ecf20Sopenharmony_ci} 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_cistatic const struct pci_device_id fealnx_pci_tbl[] = { 19368c2ecf20Sopenharmony_ci {0x1516, 0x0800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 19378c2ecf20Sopenharmony_ci {0x1516, 0x0803, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, 19388c2ecf20Sopenharmony_ci {0x1516, 0x0891, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, 19398c2ecf20Sopenharmony_ci {} /* terminate list */ 19408c2ecf20Sopenharmony_ci}; 19418c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, fealnx_pci_tbl); 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_cistatic struct pci_driver fealnx_driver = { 19458c2ecf20Sopenharmony_ci .name = "fealnx", 19468c2ecf20Sopenharmony_ci .id_table = fealnx_pci_tbl, 19478c2ecf20Sopenharmony_ci .probe = fealnx_init_one, 19488c2ecf20Sopenharmony_ci .remove = fealnx_remove_one, 19498c2ecf20Sopenharmony_ci}; 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_cistatic int __init fealnx_init(void) 19528c2ecf20Sopenharmony_ci{ 19538c2ecf20Sopenharmony_ci return pci_register_driver(&fealnx_driver); 19548c2ecf20Sopenharmony_ci} 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_cistatic void __exit fealnx_exit(void) 19578c2ecf20Sopenharmony_ci{ 19588c2ecf20Sopenharmony_ci pci_unregister_driver(&fealnx_driver); 19598c2ecf20Sopenharmony_ci} 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_cimodule_init(fealnx_init); 19628c2ecf20Sopenharmony_cimodule_exit(fealnx_exit); 1963