18c2ecf20Sopenharmony_ci/* 8139cp.c: A Linux PCI Ethernet driver for the RealTek 8139C+ chips. */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci Copyright 2001-2004 Jeff Garzik <jgarzik@pobox.com> 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com) [tg3.c] 68c2ecf20Sopenharmony_ci Copyright (C) 2000, 2001 David S. Miller (davem@redhat.com) [sungem.c] 78c2ecf20Sopenharmony_ci Copyright 2001 Manfred Spraul [natsemi.c] 88c2ecf20Sopenharmony_ci Copyright 1999-2001 by Donald Becker. [natsemi.c] 98c2ecf20Sopenharmony_ci Written 1997-2001 by Donald Becker. [8139too.c] 108c2ecf20Sopenharmony_ci Copyright 1998-2001 by Jes Sorensen, <jes@trained-monkey.org>. [acenic.c] 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci This software may be used and distributed according to the terms of 138c2ecf20Sopenharmony_ci the GNU General Public License (GPL), incorporated herein by reference. 148c2ecf20Sopenharmony_ci Drivers based on or derived from this code fall under the GPL and must 158c2ecf20Sopenharmony_ci retain the authorship, copyright and license notice. This file is not 168c2ecf20Sopenharmony_ci a complete program and may only be used when the entire operating 178c2ecf20Sopenharmony_ci system is licensed under the GPL. 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci See the file COPYING in this distribution for more information. 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci Contributors: 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci Wake-on-LAN support - Felipe Damasio <felipewd@terra.com.br> 248c2ecf20Sopenharmony_ci PCI suspend/resume - Felipe Damasio <felipewd@terra.com.br> 258c2ecf20Sopenharmony_ci LinkChg interrupt - Felipe Damasio <felipewd@terra.com.br> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci TODO: 288c2ecf20Sopenharmony_ci * Test Tx checksumming thoroughly 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci Low priority TODO: 318c2ecf20Sopenharmony_ci * Complete reset on PciErr 328c2ecf20Sopenharmony_ci * Consider Rx interrupt mitigation using TimerIntr 338c2ecf20Sopenharmony_ci * Investigate using skb->priority with h/w VLAN priority 348c2ecf20Sopenharmony_ci * Investigate using High Priority Tx Queue with skb->priority 358c2ecf20Sopenharmony_ci * Adjust Rx FIFO threshold and Max Rx DMA burst on Rx FIFO error 368c2ecf20Sopenharmony_ci * Adjust Tx FIFO threshold and Max Tx DMA burst on Tx FIFO error 378c2ecf20Sopenharmony_ci * Implement Tx software interrupt mitigation via 388c2ecf20Sopenharmony_ci Tx descriptor bit 398c2ecf20Sopenharmony_ci * The real minimum of CP_MIN_MTU is 4 bytes. However, 408c2ecf20Sopenharmony_ci for this to be supported, one must(?) turn on packet padding. 418c2ecf20Sopenharmony_ci * Support external MII transceivers (patch available) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci NOTES: 448c2ecf20Sopenharmony_ci * TX checksumming is considered experimental. It is off by 458c2ecf20Sopenharmony_ci default, use ethtool to turn it on. 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define DRV_NAME "8139cp" 528c2ecf20Sopenharmony_ci#define DRV_VERSION "1.3" 538c2ecf20Sopenharmony_ci#define DRV_RELDATE "Mar 22, 2004" 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#include <linux/module.h> 578c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 588c2ecf20Sopenharmony_ci#include <linux/kernel.h> 598c2ecf20Sopenharmony_ci#include <linux/compiler.h> 608c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 618c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 628c2ecf20Sopenharmony_ci#include <linux/init.h> 638c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 648c2ecf20Sopenharmony_ci#include <linux/pci.h> 658c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 668c2ecf20Sopenharmony_ci#include <linux/delay.h> 678c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 688c2ecf20Sopenharmony_ci#include <linux/gfp.h> 698c2ecf20Sopenharmony_ci#include <linux/mii.h> 708c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 718c2ecf20Sopenharmony_ci#include <linux/crc32.h> 728c2ecf20Sopenharmony_ci#include <linux/in.h> 738c2ecf20Sopenharmony_ci#include <linux/ip.h> 748c2ecf20Sopenharmony_ci#include <linux/tcp.h> 758c2ecf20Sopenharmony_ci#include <linux/udp.h> 768c2ecf20Sopenharmony_ci#include <linux/cache.h> 778c2ecf20Sopenharmony_ci#include <asm/io.h> 788c2ecf20Sopenharmony_ci#include <asm/irq.h> 798c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* These identify the driver base version and may not be removed. */ 828c2ecf20Sopenharmony_cistatic char version[] = 838c2ecf20Sopenharmony_ciDRV_NAME ": 10/100 PCI Ethernet driver v" DRV_VERSION " (" DRV_RELDATE ")\n"; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jeff Garzik <jgarzik@pobox.com>"); 868c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("RealTek RTL-8139C+ series 10/100 PCI Ethernet driver"); 878c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 888c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int debug = -1; 918c2ecf20Sopenharmony_cimodule_param(debug, int, 0); 928c2ecf20Sopenharmony_ciMODULE_PARM_DESC (debug, "8139cp: bitmapped message enable number"); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). 958c2ecf20Sopenharmony_ci The RTL chips use a 64 element hash table based on the Ethernet CRC. */ 968c2ecf20Sopenharmony_cistatic int multicast_filter_limit = 32; 978c2ecf20Sopenharmony_cimodule_param(multicast_filter_limit, int, 0); 988c2ecf20Sopenharmony_ciMODULE_PARM_DESC (multicast_filter_limit, "8139cp: maximum number of filtered multicast addresses"); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define CP_DEF_MSG_ENABLE (NETIF_MSG_DRV | \ 1018c2ecf20Sopenharmony_ci NETIF_MSG_PROBE | \ 1028c2ecf20Sopenharmony_ci NETIF_MSG_LINK) 1038c2ecf20Sopenharmony_ci#define CP_NUM_STATS 14 /* struct cp_dma_stats, plus one */ 1048c2ecf20Sopenharmony_ci#define CP_STATS_SIZE 64 /* size in bytes of DMA stats block */ 1058c2ecf20Sopenharmony_ci#define CP_REGS_SIZE (0xff + 1) 1068c2ecf20Sopenharmony_ci#define CP_REGS_VER 1 /* version 1 */ 1078c2ecf20Sopenharmony_ci#define CP_RX_RING_SIZE 64 1088c2ecf20Sopenharmony_ci#define CP_TX_RING_SIZE 64 1098c2ecf20Sopenharmony_ci#define CP_RING_BYTES \ 1108c2ecf20Sopenharmony_ci ((sizeof(struct cp_desc) * CP_RX_RING_SIZE) + \ 1118c2ecf20Sopenharmony_ci (sizeof(struct cp_desc) * CP_TX_RING_SIZE) + \ 1128c2ecf20Sopenharmony_ci CP_STATS_SIZE) 1138c2ecf20Sopenharmony_ci#define NEXT_TX(N) (((N) + 1) & (CP_TX_RING_SIZE - 1)) 1148c2ecf20Sopenharmony_ci#define NEXT_RX(N) (((N) + 1) & (CP_RX_RING_SIZE - 1)) 1158c2ecf20Sopenharmony_ci#define TX_BUFFS_AVAIL(CP) \ 1168c2ecf20Sopenharmony_ci (((CP)->tx_tail <= (CP)->tx_head) ? \ 1178c2ecf20Sopenharmony_ci (CP)->tx_tail + (CP_TX_RING_SIZE - 1) - (CP)->tx_head : \ 1188c2ecf20Sopenharmony_ci (CP)->tx_tail - (CP)->tx_head - 1) 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ 1218c2ecf20Sopenharmony_ci#define CP_INTERNAL_PHY 32 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. */ 1248c2ecf20Sopenharmony_ci#define RX_FIFO_THRESH 5 /* Rx buffer level before first PCI xfer. */ 1258c2ecf20Sopenharmony_ci#define RX_DMA_BURST 4 /* Maximum PCI burst, '4' is 256 */ 1268c2ecf20Sopenharmony_ci#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ 1278c2ecf20Sopenharmony_ci#define TX_EARLY_THRESH 256 /* Early Tx threshold, in bytes */ 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* Time in jiffies before concluding the transmitter is hung. */ 1308c2ecf20Sopenharmony_ci#define TX_TIMEOUT (6*HZ) 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci/* hardware minimum and maximum for a single frame's data payload */ 1338c2ecf20Sopenharmony_ci#define CP_MIN_MTU 60 /* TODO: allow lower, but pad */ 1348c2ecf20Sopenharmony_ci#define CP_MAX_MTU 4096 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cienum { 1378c2ecf20Sopenharmony_ci /* NIC register offsets */ 1388c2ecf20Sopenharmony_ci MAC0 = 0x00, /* Ethernet hardware address. */ 1398c2ecf20Sopenharmony_ci MAR0 = 0x08, /* Multicast filter. */ 1408c2ecf20Sopenharmony_ci StatsAddr = 0x10, /* 64-bit start addr of 64-byte DMA stats blk */ 1418c2ecf20Sopenharmony_ci TxRingAddr = 0x20, /* 64-bit start addr of Tx ring */ 1428c2ecf20Sopenharmony_ci HiTxRingAddr = 0x28, /* 64-bit start addr of high priority Tx ring */ 1438c2ecf20Sopenharmony_ci Cmd = 0x37, /* Command register */ 1448c2ecf20Sopenharmony_ci IntrMask = 0x3C, /* Interrupt mask */ 1458c2ecf20Sopenharmony_ci IntrStatus = 0x3E, /* Interrupt status */ 1468c2ecf20Sopenharmony_ci TxConfig = 0x40, /* Tx configuration */ 1478c2ecf20Sopenharmony_ci ChipVersion = 0x43, /* 8-bit chip version, inside TxConfig */ 1488c2ecf20Sopenharmony_ci RxConfig = 0x44, /* Rx configuration */ 1498c2ecf20Sopenharmony_ci RxMissed = 0x4C, /* 24 bits valid, write clears */ 1508c2ecf20Sopenharmony_ci Cfg9346 = 0x50, /* EEPROM select/control; Cfg reg [un]lock */ 1518c2ecf20Sopenharmony_ci Config1 = 0x52, /* Config1 */ 1528c2ecf20Sopenharmony_ci Config3 = 0x59, /* Config3 */ 1538c2ecf20Sopenharmony_ci Config4 = 0x5A, /* Config4 */ 1548c2ecf20Sopenharmony_ci MultiIntr = 0x5C, /* Multiple interrupt select */ 1558c2ecf20Sopenharmony_ci BasicModeCtrl = 0x62, /* MII BMCR */ 1568c2ecf20Sopenharmony_ci BasicModeStatus = 0x64, /* MII BMSR */ 1578c2ecf20Sopenharmony_ci NWayAdvert = 0x66, /* MII ADVERTISE */ 1588c2ecf20Sopenharmony_ci NWayLPAR = 0x68, /* MII LPA */ 1598c2ecf20Sopenharmony_ci NWayExpansion = 0x6A, /* MII Expansion */ 1608c2ecf20Sopenharmony_ci TxDmaOkLowDesc = 0x82, /* Low 16 bit address of a Tx descriptor. */ 1618c2ecf20Sopenharmony_ci Config5 = 0xD8, /* Config5 */ 1628c2ecf20Sopenharmony_ci TxPoll = 0xD9, /* Tell chip to check Tx descriptors for work */ 1638c2ecf20Sopenharmony_ci RxMaxSize = 0xDA, /* Max size of an Rx packet (8169 only) */ 1648c2ecf20Sopenharmony_ci CpCmd = 0xE0, /* C+ Command register (C+ mode only) */ 1658c2ecf20Sopenharmony_ci IntrMitigate = 0xE2, /* rx/tx interrupt mitigation control */ 1668c2ecf20Sopenharmony_ci RxRingAddr = 0xE4, /* 64-bit start addr of Rx ring */ 1678c2ecf20Sopenharmony_ci TxThresh = 0xEC, /* Early Tx threshold */ 1688c2ecf20Sopenharmony_ci OldRxBufAddr = 0x30, /* DMA address of Rx ring buffer (C mode) */ 1698c2ecf20Sopenharmony_ci OldTSD0 = 0x10, /* DMA address of first Tx desc (C mode) */ 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* Tx and Rx status descriptors */ 1728c2ecf20Sopenharmony_ci DescOwn = (1 << 31), /* Descriptor is owned by NIC */ 1738c2ecf20Sopenharmony_ci RingEnd = (1 << 30), /* End of descriptor ring */ 1748c2ecf20Sopenharmony_ci FirstFrag = (1 << 29), /* First segment of a packet */ 1758c2ecf20Sopenharmony_ci LastFrag = (1 << 28), /* Final segment of a packet */ 1768c2ecf20Sopenharmony_ci LargeSend = (1 << 27), /* TCP Large Send Offload (TSO) */ 1778c2ecf20Sopenharmony_ci MSSShift = 16, /* MSS value position */ 1788c2ecf20Sopenharmony_ci MSSMask = 0x7ff, /* MSS value: 11 bits */ 1798c2ecf20Sopenharmony_ci TxError = (1 << 23), /* Tx error summary */ 1808c2ecf20Sopenharmony_ci RxError = (1 << 20), /* Rx error summary */ 1818c2ecf20Sopenharmony_ci IPCS = (1 << 18), /* Calculate IP checksum */ 1828c2ecf20Sopenharmony_ci UDPCS = (1 << 17), /* Calculate UDP/IP checksum */ 1838c2ecf20Sopenharmony_ci TCPCS = (1 << 16), /* Calculate TCP/IP checksum */ 1848c2ecf20Sopenharmony_ci TxVlanTag = (1 << 17), /* Add VLAN tag */ 1858c2ecf20Sopenharmony_ci RxVlanTagged = (1 << 16), /* Rx VLAN tag available */ 1868c2ecf20Sopenharmony_ci IPFail = (1 << 15), /* IP checksum failed */ 1878c2ecf20Sopenharmony_ci UDPFail = (1 << 14), /* UDP/IP checksum failed */ 1888c2ecf20Sopenharmony_ci TCPFail = (1 << 13), /* TCP/IP checksum failed */ 1898c2ecf20Sopenharmony_ci NormalTxPoll = (1 << 6), /* One or more normal Tx packets to send */ 1908c2ecf20Sopenharmony_ci PID1 = (1 << 17), /* 2 protocol id bits: 0==non-IP, */ 1918c2ecf20Sopenharmony_ci PID0 = (1 << 16), /* 1==UDP/IP, 2==TCP/IP, 3==IP */ 1928c2ecf20Sopenharmony_ci RxProtoTCP = 1, 1938c2ecf20Sopenharmony_ci RxProtoUDP = 2, 1948c2ecf20Sopenharmony_ci RxProtoIP = 3, 1958c2ecf20Sopenharmony_ci TxFIFOUnder = (1 << 25), /* Tx FIFO underrun */ 1968c2ecf20Sopenharmony_ci TxOWC = (1 << 22), /* Tx Out-of-window collision */ 1978c2ecf20Sopenharmony_ci TxLinkFail = (1 << 21), /* Link failed during Tx of packet */ 1988c2ecf20Sopenharmony_ci TxMaxCol = (1 << 20), /* Tx aborted due to excessive collisions */ 1998c2ecf20Sopenharmony_ci TxColCntShift = 16, /* Shift, to get 4-bit Tx collision cnt */ 2008c2ecf20Sopenharmony_ci TxColCntMask = 0x01 | 0x02 | 0x04 | 0x08, /* 4-bit collision count */ 2018c2ecf20Sopenharmony_ci RxErrFrame = (1 << 27), /* Rx frame alignment error */ 2028c2ecf20Sopenharmony_ci RxMcast = (1 << 26), /* Rx multicast packet rcv'd */ 2038c2ecf20Sopenharmony_ci RxErrCRC = (1 << 18), /* Rx CRC error */ 2048c2ecf20Sopenharmony_ci RxErrRunt = (1 << 19), /* Rx error, packet < 64 bytes */ 2058c2ecf20Sopenharmony_ci RxErrLong = (1 << 21), /* Rx error, packet > 4096 bytes */ 2068c2ecf20Sopenharmony_ci RxErrFIFO = (1 << 22), /* Rx error, FIFO overflowed, pkt bad */ 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* StatsAddr register */ 2098c2ecf20Sopenharmony_ci DumpStats = (1 << 3), /* Begin stats dump */ 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* RxConfig register */ 2128c2ecf20Sopenharmony_ci RxCfgFIFOShift = 13, /* Shift, to get Rx FIFO thresh value */ 2138c2ecf20Sopenharmony_ci RxCfgDMAShift = 8, /* Shift, to get Rx Max DMA value */ 2148c2ecf20Sopenharmony_ci AcceptErr = 0x20, /* Accept packets with CRC errors */ 2158c2ecf20Sopenharmony_ci AcceptRunt = 0x10, /* Accept runt (<64 bytes) packets */ 2168c2ecf20Sopenharmony_ci AcceptBroadcast = 0x08, /* Accept broadcast packets */ 2178c2ecf20Sopenharmony_ci AcceptMulticast = 0x04, /* Accept multicast packets */ 2188c2ecf20Sopenharmony_ci AcceptMyPhys = 0x02, /* Accept pkts with our MAC as dest */ 2198c2ecf20Sopenharmony_ci AcceptAllPhys = 0x01, /* Accept all pkts w/ physical dest */ 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* IntrMask / IntrStatus registers */ 2228c2ecf20Sopenharmony_ci PciErr = (1 << 15), /* System error on the PCI bus */ 2238c2ecf20Sopenharmony_ci TimerIntr = (1 << 14), /* Asserted when TCTR reaches TimerInt value */ 2248c2ecf20Sopenharmony_ci LenChg = (1 << 13), /* Cable length change */ 2258c2ecf20Sopenharmony_ci SWInt = (1 << 8), /* Software-requested interrupt */ 2268c2ecf20Sopenharmony_ci TxEmpty = (1 << 7), /* No Tx descriptors available */ 2278c2ecf20Sopenharmony_ci RxFIFOOvr = (1 << 6), /* Rx FIFO Overflow */ 2288c2ecf20Sopenharmony_ci LinkChg = (1 << 5), /* Packet underrun, or link change */ 2298c2ecf20Sopenharmony_ci RxEmpty = (1 << 4), /* No Rx descriptors available */ 2308c2ecf20Sopenharmony_ci TxErr = (1 << 3), /* Tx error */ 2318c2ecf20Sopenharmony_ci TxOK = (1 << 2), /* Tx packet sent */ 2328c2ecf20Sopenharmony_ci RxErr = (1 << 1), /* Rx error */ 2338c2ecf20Sopenharmony_ci RxOK = (1 << 0), /* Rx packet received */ 2348c2ecf20Sopenharmony_ci IntrResvd = (1 << 10), /* reserved, according to RealTek engineers, 2358c2ecf20Sopenharmony_ci but hardware likes to raise it */ 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci IntrAll = PciErr | TimerIntr | LenChg | SWInt | TxEmpty | 2388c2ecf20Sopenharmony_ci RxFIFOOvr | LinkChg | RxEmpty | TxErr | TxOK | 2398c2ecf20Sopenharmony_ci RxErr | RxOK | IntrResvd, 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* C mode command register */ 2428c2ecf20Sopenharmony_ci CmdReset = (1 << 4), /* Enable to reset; self-clearing */ 2438c2ecf20Sopenharmony_ci RxOn = (1 << 3), /* Rx mode enable */ 2448c2ecf20Sopenharmony_ci TxOn = (1 << 2), /* Tx mode enable */ 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* C+ mode command register */ 2478c2ecf20Sopenharmony_ci RxVlanOn = (1 << 6), /* Rx VLAN de-tagging enable */ 2488c2ecf20Sopenharmony_ci RxChkSum = (1 << 5), /* Rx checksum offload enable */ 2498c2ecf20Sopenharmony_ci PCIDAC = (1 << 4), /* PCI Dual Address Cycle (64-bit PCI) */ 2508c2ecf20Sopenharmony_ci PCIMulRW = (1 << 3), /* Enable PCI read/write multiple */ 2518c2ecf20Sopenharmony_ci CpRxOn = (1 << 1), /* Rx mode enable */ 2528c2ecf20Sopenharmony_ci CpTxOn = (1 << 0), /* Tx mode enable */ 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* Cfg9436 EEPROM control register */ 2558c2ecf20Sopenharmony_ci Cfg9346_Lock = 0x00, /* Lock ConfigX/MII register access */ 2568c2ecf20Sopenharmony_ci Cfg9346_Unlock = 0xC0, /* Unlock ConfigX/MII register access */ 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* TxConfig register */ 2598c2ecf20Sopenharmony_ci IFG = (1 << 25) | (1 << 24), /* standard IEEE interframe gap */ 2608c2ecf20Sopenharmony_ci TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */ 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* Early Tx Threshold register */ 2638c2ecf20Sopenharmony_ci TxThreshMask = 0x3f, /* Mask bits 5-0 */ 2648c2ecf20Sopenharmony_ci TxThreshMax = 2048, /* Max early Tx threshold */ 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* Config1 register */ 2678c2ecf20Sopenharmony_ci DriverLoaded = (1 << 5), /* Software marker, driver is loaded */ 2688c2ecf20Sopenharmony_ci LWACT = (1 << 4), /* LWAKE active mode */ 2698c2ecf20Sopenharmony_ci PMEnable = (1 << 0), /* Enable various PM features of chip */ 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* Config3 register */ 2728c2ecf20Sopenharmony_ci PARMEnable = (1 << 6), /* Enable auto-loading of PHY parms */ 2738c2ecf20Sopenharmony_ci MagicPacket = (1 << 5), /* Wake up when receives a Magic Packet */ 2748c2ecf20Sopenharmony_ci LinkUp = (1 << 4), /* Wake up when the cable connection is re-established */ 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* Config4 register */ 2778c2ecf20Sopenharmony_ci LWPTN = (1 << 1), /* LWAKE Pattern */ 2788c2ecf20Sopenharmony_ci LWPME = (1 << 4), /* LANWAKE vs PMEB */ 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* Config5 register */ 2818c2ecf20Sopenharmony_ci BWF = (1 << 6), /* Accept Broadcast wakeup frame */ 2828c2ecf20Sopenharmony_ci MWF = (1 << 5), /* Accept Multicast wakeup frame */ 2838c2ecf20Sopenharmony_ci UWF = (1 << 4), /* Accept Unicast wakeup frame */ 2848c2ecf20Sopenharmony_ci LANWake = (1 << 1), /* Enable LANWake signal */ 2858c2ecf20Sopenharmony_ci PMEStatus = (1 << 0), /* PME status can be reset by PCI RST# */ 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci cp_norx_intr_mask = PciErr | LinkChg | TxOK | TxErr | TxEmpty, 2888c2ecf20Sopenharmony_ci cp_rx_intr_mask = RxOK | RxErr | RxEmpty | RxFIFOOvr, 2898c2ecf20Sopenharmony_ci cp_intr_mask = cp_rx_intr_mask | cp_norx_intr_mask, 2908c2ecf20Sopenharmony_ci}; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic const unsigned int cp_rx_config = 2938c2ecf20Sopenharmony_ci (RX_FIFO_THRESH << RxCfgFIFOShift) | 2948c2ecf20Sopenharmony_ci (RX_DMA_BURST << RxCfgDMAShift); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistruct cp_desc { 2978c2ecf20Sopenharmony_ci __le32 opts1; 2988c2ecf20Sopenharmony_ci __le32 opts2; 2998c2ecf20Sopenharmony_ci __le64 addr; 3008c2ecf20Sopenharmony_ci}; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistruct cp_dma_stats { 3038c2ecf20Sopenharmony_ci __le64 tx_ok; 3048c2ecf20Sopenharmony_ci __le64 rx_ok; 3058c2ecf20Sopenharmony_ci __le64 tx_err; 3068c2ecf20Sopenharmony_ci __le32 rx_err; 3078c2ecf20Sopenharmony_ci __le16 rx_fifo; 3088c2ecf20Sopenharmony_ci __le16 frame_align; 3098c2ecf20Sopenharmony_ci __le32 tx_ok_1col; 3108c2ecf20Sopenharmony_ci __le32 tx_ok_mcol; 3118c2ecf20Sopenharmony_ci __le64 rx_ok_phys; 3128c2ecf20Sopenharmony_ci __le64 rx_ok_bcast; 3138c2ecf20Sopenharmony_ci __le32 rx_ok_mcast; 3148c2ecf20Sopenharmony_ci __le16 tx_abort; 3158c2ecf20Sopenharmony_ci __le16 tx_underrun; 3168c2ecf20Sopenharmony_ci} __packed; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistruct cp_extra_stats { 3198c2ecf20Sopenharmony_ci unsigned long rx_frags; 3208c2ecf20Sopenharmony_ci}; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistruct cp_private { 3238c2ecf20Sopenharmony_ci void __iomem *regs; 3248c2ecf20Sopenharmony_ci struct net_device *dev; 3258c2ecf20Sopenharmony_ci spinlock_t lock; 3268c2ecf20Sopenharmony_ci u32 msg_enable; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci struct napi_struct napi; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci struct pci_dev *pdev; 3318c2ecf20Sopenharmony_ci u32 rx_config; 3328c2ecf20Sopenharmony_ci u16 cpcmd; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci struct cp_extra_stats cp_stats; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci unsigned rx_head ____cacheline_aligned; 3378c2ecf20Sopenharmony_ci unsigned rx_tail; 3388c2ecf20Sopenharmony_ci struct cp_desc *rx_ring; 3398c2ecf20Sopenharmony_ci struct sk_buff *rx_skb[CP_RX_RING_SIZE]; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci unsigned tx_head ____cacheline_aligned; 3428c2ecf20Sopenharmony_ci unsigned tx_tail; 3438c2ecf20Sopenharmony_ci struct cp_desc *tx_ring; 3448c2ecf20Sopenharmony_ci struct sk_buff *tx_skb[CP_TX_RING_SIZE]; 3458c2ecf20Sopenharmony_ci u32 tx_opts[CP_TX_RING_SIZE]; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci unsigned rx_buf_sz; 3488c2ecf20Sopenharmony_ci unsigned wol_enabled : 1; /* Is Wake-on-LAN enabled? */ 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci dma_addr_t ring_dma; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci struct mii_if_info mii_if; 3538c2ecf20Sopenharmony_ci}; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci#define cpr8(reg) readb(cp->regs + (reg)) 3568c2ecf20Sopenharmony_ci#define cpr16(reg) readw(cp->regs + (reg)) 3578c2ecf20Sopenharmony_ci#define cpr32(reg) readl(cp->regs + (reg)) 3588c2ecf20Sopenharmony_ci#define cpw8(reg,val) writeb((val), cp->regs + (reg)) 3598c2ecf20Sopenharmony_ci#define cpw16(reg,val) writew((val), cp->regs + (reg)) 3608c2ecf20Sopenharmony_ci#define cpw32(reg,val) writel((val), cp->regs + (reg)) 3618c2ecf20Sopenharmony_ci#define cpw8_f(reg,val) do { \ 3628c2ecf20Sopenharmony_ci writeb((val), cp->regs + (reg)); \ 3638c2ecf20Sopenharmony_ci readb(cp->regs + (reg)); \ 3648c2ecf20Sopenharmony_ci } while (0) 3658c2ecf20Sopenharmony_ci#define cpw16_f(reg,val) do { \ 3668c2ecf20Sopenharmony_ci writew((val), cp->regs + (reg)); \ 3678c2ecf20Sopenharmony_ci readw(cp->regs + (reg)); \ 3688c2ecf20Sopenharmony_ci } while (0) 3698c2ecf20Sopenharmony_ci#define cpw32_f(reg,val) do { \ 3708c2ecf20Sopenharmony_ci writel((val), cp->regs + (reg)); \ 3718c2ecf20Sopenharmony_ci readl(cp->regs + (reg)); \ 3728c2ecf20Sopenharmony_ci } while (0) 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic void __cp_set_rx_mode (struct net_device *dev); 3768c2ecf20Sopenharmony_cistatic void cp_tx (struct cp_private *cp); 3778c2ecf20Sopenharmony_cistatic void cp_clean_rings (struct cp_private *cp); 3788c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 3798c2ecf20Sopenharmony_cistatic void cp_poll_controller(struct net_device *dev); 3808c2ecf20Sopenharmony_ci#endif 3818c2ecf20Sopenharmony_cistatic int cp_get_eeprom_len(struct net_device *dev); 3828c2ecf20Sopenharmony_cistatic int cp_get_eeprom(struct net_device *dev, 3838c2ecf20Sopenharmony_ci struct ethtool_eeprom *eeprom, u8 *data); 3848c2ecf20Sopenharmony_cistatic int cp_set_eeprom(struct net_device *dev, 3858c2ecf20Sopenharmony_ci struct ethtool_eeprom *eeprom, u8 *data); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic struct { 3888c2ecf20Sopenharmony_ci const char str[ETH_GSTRING_LEN]; 3898c2ecf20Sopenharmony_ci} ethtool_stats_keys[] = { 3908c2ecf20Sopenharmony_ci { "tx_ok" }, 3918c2ecf20Sopenharmony_ci { "rx_ok" }, 3928c2ecf20Sopenharmony_ci { "tx_err" }, 3938c2ecf20Sopenharmony_ci { "rx_err" }, 3948c2ecf20Sopenharmony_ci { "rx_fifo" }, 3958c2ecf20Sopenharmony_ci { "frame_align" }, 3968c2ecf20Sopenharmony_ci { "tx_ok_1col" }, 3978c2ecf20Sopenharmony_ci { "tx_ok_mcol" }, 3988c2ecf20Sopenharmony_ci { "rx_ok_phys" }, 3998c2ecf20Sopenharmony_ci { "rx_ok_bcast" }, 4008c2ecf20Sopenharmony_ci { "rx_ok_mcast" }, 4018c2ecf20Sopenharmony_ci { "tx_abort" }, 4028c2ecf20Sopenharmony_ci { "tx_underrun" }, 4038c2ecf20Sopenharmony_ci { "rx_frags" }, 4048c2ecf20Sopenharmony_ci}; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic inline void cp_set_rxbufsize (struct cp_private *cp) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci unsigned int mtu = cp->dev->mtu; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (mtu > ETH_DATA_LEN) 4128c2ecf20Sopenharmony_ci /* MTU + ethernet header + FCS + optional VLAN tag */ 4138c2ecf20Sopenharmony_ci cp->rx_buf_sz = mtu + ETH_HLEN + 8; 4148c2ecf20Sopenharmony_ci else 4158c2ecf20Sopenharmony_ci cp->rx_buf_sz = PKT_BUF_SZ; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic inline void cp_rx_skb (struct cp_private *cp, struct sk_buff *skb, 4198c2ecf20Sopenharmony_ci struct cp_desc *desc) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci u32 opts2 = le32_to_cpu(desc->opts2); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans (skb, cp->dev); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci cp->dev->stats.rx_packets++; 4268c2ecf20Sopenharmony_ci cp->dev->stats.rx_bytes += skb->len; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (opts2 & RxVlanTagged) 4298c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), swab16(opts2 & 0xffff)); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci napi_gro_receive(&cp->napi, skb); 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic void cp_rx_err_acct (struct cp_private *cp, unsigned rx_tail, 4358c2ecf20Sopenharmony_ci u32 status, u32 len) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci netif_dbg(cp, rx_err, cp->dev, "rx err, slot %d status 0x%x len %d\n", 4388c2ecf20Sopenharmony_ci rx_tail, status, len); 4398c2ecf20Sopenharmony_ci cp->dev->stats.rx_errors++; 4408c2ecf20Sopenharmony_ci if (status & RxErrFrame) 4418c2ecf20Sopenharmony_ci cp->dev->stats.rx_frame_errors++; 4428c2ecf20Sopenharmony_ci if (status & RxErrCRC) 4438c2ecf20Sopenharmony_ci cp->dev->stats.rx_crc_errors++; 4448c2ecf20Sopenharmony_ci if ((status & RxErrRunt) || (status & RxErrLong)) 4458c2ecf20Sopenharmony_ci cp->dev->stats.rx_length_errors++; 4468c2ecf20Sopenharmony_ci if ((status & (FirstFrag | LastFrag)) != (FirstFrag | LastFrag)) 4478c2ecf20Sopenharmony_ci cp->dev->stats.rx_length_errors++; 4488c2ecf20Sopenharmony_ci if (status & RxErrFIFO) 4498c2ecf20Sopenharmony_ci cp->dev->stats.rx_fifo_errors++; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic inline unsigned int cp_rx_csum_ok (u32 status) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci unsigned int protocol = (status >> 16) & 0x3; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (((protocol == RxProtoTCP) && !(status & TCPFail)) || 4578c2ecf20Sopenharmony_ci ((protocol == RxProtoUDP) && !(status & UDPFail))) 4588c2ecf20Sopenharmony_ci return 1; 4598c2ecf20Sopenharmony_ci else 4608c2ecf20Sopenharmony_ci return 0; 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic int cp_rx_poll(struct napi_struct *napi, int budget) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci struct cp_private *cp = container_of(napi, struct cp_private, napi); 4668c2ecf20Sopenharmony_ci struct net_device *dev = cp->dev; 4678c2ecf20Sopenharmony_ci unsigned int rx_tail = cp->rx_tail; 4688c2ecf20Sopenharmony_ci int rx = 0; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci cpw16(IntrStatus, cp_rx_intr_mask); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci while (rx < budget) { 4738c2ecf20Sopenharmony_ci u32 status, len; 4748c2ecf20Sopenharmony_ci dma_addr_t mapping, new_mapping; 4758c2ecf20Sopenharmony_ci struct sk_buff *skb, *new_skb; 4768c2ecf20Sopenharmony_ci struct cp_desc *desc; 4778c2ecf20Sopenharmony_ci const unsigned buflen = cp->rx_buf_sz; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci skb = cp->rx_skb[rx_tail]; 4808c2ecf20Sopenharmony_ci BUG_ON(!skb); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci desc = &cp->rx_ring[rx_tail]; 4838c2ecf20Sopenharmony_ci status = le32_to_cpu(desc->opts1); 4848c2ecf20Sopenharmony_ci if (status & DescOwn) 4858c2ecf20Sopenharmony_ci break; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci len = (status & 0x1fff) - 4; 4888c2ecf20Sopenharmony_ci mapping = le64_to_cpu(desc->addr); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if ((status & (FirstFrag | LastFrag)) != (FirstFrag | LastFrag)) { 4918c2ecf20Sopenharmony_ci /* we don't support incoming fragmented frames. 4928c2ecf20Sopenharmony_ci * instead, we attempt to ensure that the 4938c2ecf20Sopenharmony_ci * pre-allocated RX skbs are properly sized such 4948c2ecf20Sopenharmony_ci * that RX fragments are never encountered 4958c2ecf20Sopenharmony_ci */ 4968c2ecf20Sopenharmony_ci cp_rx_err_acct(cp, rx_tail, status, len); 4978c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 4988c2ecf20Sopenharmony_ci cp->cp_stats.rx_frags++; 4998c2ecf20Sopenharmony_ci goto rx_next; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci if (status & (RxError | RxErrFIFO)) { 5038c2ecf20Sopenharmony_ci cp_rx_err_acct(cp, rx_tail, status, len); 5048c2ecf20Sopenharmony_ci goto rx_next; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci netif_dbg(cp, rx_status, dev, "rx slot %d status 0x%x len %d\n", 5088c2ecf20Sopenharmony_ci rx_tail, status, len); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci new_skb = napi_alloc_skb(napi, buflen); 5118c2ecf20Sopenharmony_ci if (!new_skb) { 5128c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 5138c2ecf20Sopenharmony_ci goto rx_next; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci new_mapping = dma_map_single(&cp->pdev->dev, new_skb->data, buflen, 5178c2ecf20Sopenharmony_ci PCI_DMA_FROMDEVICE); 5188c2ecf20Sopenharmony_ci if (dma_mapping_error(&cp->pdev->dev, new_mapping)) { 5198c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 5208c2ecf20Sopenharmony_ci kfree_skb(new_skb); 5218c2ecf20Sopenharmony_ci goto rx_next; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci dma_unmap_single(&cp->pdev->dev, mapping, 5258c2ecf20Sopenharmony_ci buflen, PCI_DMA_FROMDEVICE); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci /* Handle checksum offloading for incoming packets. */ 5288c2ecf20Sopenharmony_ci if (cp_rx_csum_ok(status)) 5298c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 5308c2ecf20Sopenharmony_ci else 5318c2ecf20Sopenharmony_ci skb_checksum_none_assert(skb); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci skb_put(skb, len); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci cp->rx_skb[rx_tail] = new_skb; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci cp_rx_skb(cp, skb, desc); 5388c2ecf20Sopenharmony_ci rx++; 5398c2ecf20Sopenharmony_ci mapping = new_mapping; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cirx_next: 5428c2ecf20Sopenharmony_ci cp->rx_ring[rx_tail].opts2 = 0; 5438c2ecf20Sopenharmony_ci cp->rx_ring[rx_tail].addr = cpu_to_le64(mapping); 5448c2ecf20Sopenharmony_ci if (rx_tail == (CP_RX_RING_SIZE - 1)) 5458c2ecf20Sopenharmony_ci desc->opts1 = cpu_to_le32(DescOwn | RingEnd | 5468c2ecf20Sopenharmony_ci cp->rx_buf_sz); 5478c2ecf20Sopenharmony_ci else 5488c2ecf20Sopenharmony_ci desc->opts1 = cpu_to_le32(DescOwn | cp->rx_buf_sz); 5498c2ecf20Sopenharmony_ci rx_tail = NEXT_RX(rx_tail); 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci cp->rx_tail = rx_tail; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* if we did not reach work limit, then we're done with 5558c2ecf20Sopenharmony_ci * this round of polling 5568c2ecf20Sopenharmony_ci */ 5578c2ecf20Sopenharmony_ci if (rx < budget && napi_complete_done(napi, rx)) { 5588c2ecf20Sopenharmony_ci unsigned long flags; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 5618c2ecf20Sopenharmony_ci cpw16_f(IntrMask, cp_intr_mask); 5628c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci return rx; 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cistatic irqreturn_t cp_interrupt (int irq, void *dev_instance) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci struct net_device *dev = dev_instance; 5718c2ecf20Sopenharmony_ci struct cp_private *cp; 5728c2ecf20Sopenharmony_ci int handled = 0; 5738c2ecf20Sopenharmony_ci u16 status; 5748c2ecf20Sopenharmony_ci u16 mask; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci if (unlikely(dev == NULL)) 5778c2ecf20Sopenharmony_ci return IRQ_NONE; 5788c2ecf20Sopenharmony_ci cp = netdev_priv(dev); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci spin_lock(&cp->lock); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci mask = cpr16(IntrMask); 5838c2ecf20Sopenharmony_ci if (!mask) 5848c2ecf20Sopenharmony_ci goto out_unlock; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci status = cpr16(IntrStatus); 5878c2ecf20Sopenharmony_ci if (!status || (status == 0xFFFF)) 5888c2ecf20Sopenharmony_ci goto out_unlock; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci handled = 1; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci netif_dbg(cp, intr, dev, "intr, status %04x cmd %02x cpcmd %04x\n", 5938c2ecf20Sopenharmony_ci status, cpr8(Cmd), cpr16(CpCmd)); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci cpw16(IntrStatus, status & ~cp_rx_intr_mask); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* close possible race's with dev_close */ 5988c2ecf20Sopenharmony_ci if (unlikely(!netif_running(dev))) { 5998c2ecf20Sopenharmony_ci cpw16(IntrMask, 0); 6008c2ecf20Sopenharmony_ci goto out_unlock; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (status & (RxOK | RxErr | RxEmpty | RxFIFOOvr)) 6048c2ecf20Sopenharmony_ci if (napi_schedule_prep(&cp->napi)) { 6058c2ecf20Sopenharmony_ci cpw16_f(IntrMask, cp_norx_intr_mask); 6068c2ecf20Sopenharmony_ci __napi_schedule(&cp->napi); 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci if (status & (TxOK | TxErr | TxEmpty | SWInt)) 6108c2ecf20Sopenharmony_ci cp_tx(cp); 6118c2ecf20Sopenharmony_ci if (status & LinkChg) 6128c2ecf20Sopenharmony_ci mii_check_media(&cp->mii_if, netif_msg_link(cp), false); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (status & PciErr) { 6168c2ecf20Sopenharmony_ci u16 pci_status; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci pci_read_config_word(cp->pdev, PCI_STATUS, &pci_status); 6198c2ecf20Sopenharmony_ci pci_write_config_word(cp->pdev, PCI_STATUS, pci_status); 6208c2ecf20Sopenharmony_ci netdev_err(dev, "PCI bus error, status=%04x, PCI status=%04x\n", 6218c2ecf20Sopenharmony_ci status, pci_status); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci /* TODO: reset hardware */ 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ciout_unlock: 6278c2ecf20Sopenharmony_ci spin_unlock(&cp->lock); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return IRQ_RETVAL(handled); 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 6338c2ecf20Sopenharmony_ci/* 6348c2ecf20Sopenharmony_ci * Polling receive - used by netconsole and other diagnostic tools 6358c2ecf20Sopenharmony_ci * to allow network i/o with interrupts disabled. 6368c2ecf20Sopenharmony_ci */ 6378c2ecf20Sopenharmony_cistatic void cp_poll_controller(struct net_device *dev) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 6408c2ecf20Sopenharmony_ci const int irq = cp->pdev->irq; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci disable_irq(irq); 6438c2ecf20Sopenharmony_ci cp_interrupt(irq, dev); 6448c2ecf20Sopenharmony_ci enable_irq(irq); 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci#endif 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cistatic void cp_tx (struct cp_private *cp) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci unsigned tx_head = cp->tx_head; 6518c2ecf20Sopenharmony_ci unsigned tx_tail = cp->tx_tail; 6528c2ecf20Sopenharmony_ci unsigned bytes_compl = 0, pkts_compl = 0; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci while (tx_tail != tx_head) { 6558c2ecf20Sopenharmony_ci struct cp_desc *txd = cp->tx_ring + tx_tail; 6568c2ecf20Sopenharmony_ci struct sk_buff *skb; 6578c2ecf20Sopenharmony_ci u32 status; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci rmb(); 6608c2ecf20Sopenharmony_ci status = le32_to_cpu(txd->opts1); 6618c2ecf20Sopenharmony_ci if (status & DescOwn) 6628c2ecf20Sopenharmony_ci break; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci skb = cp->tx_skb[tx_tail]; 6658c2ecf20Sopenharmony_ci BUG_ON(!skb); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci dma_unmap_single(&cp->pdev->dev, le64_to_cpu(txd->addr), 6688c2ecf20Sopenharmony_ci cp->tx_opts[tx_tail] & 0xffff, 6698c2ecf20Sopenharmony_ci PCI_DMA_TODEVICE); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci if (status & LastFrag) { 6728c2ecf20Sopenharmony_ci if (status & (TxError | TxFIFOUnder)) { 6738c2ecf20Sopenharmony_ci netif_dbg(cp, tx_err, cp->dev, 6748c2ecf20Sopenharmony_ci "tx err, status 0x%x\n", status); 6758c2ecf20Sopenharmony_ci cp->dev->stats.tx_errors++; 6768c2ecf20Sopenharmony_ci if (status & TxOWC) 6778c2ecf20Sopenharmony_ci cp->dev->stats.tx_window_errors++; 6788c2ecf20Sopenharmony_ci if (status & TxMaxCol) 6798c2ecf20Sopenharmony_ci cp->dev->stats.tx_aborted_errors++; 6808c2ecf20Sopenharmony_ci if (status & TxLinkFail) 6818c2ecf20Sopenharmony_ci cp->dev->stats.tx_carrier_errors++; 6828c2ecf20Sopenharmony_ci if (status & TxFIFOUnder) 6838c2ecf20Sopenharmony_ci cp->dev->stats.tx_fifo_errors++; 6848c2ecf20Sopenharmony_ci } else { 6858c2ecf20Sopenharmony_ci cp->dev->stats.collisions += 6868c2ecf20Sopenharmony_ci ((status >> TxColCntShift) & TxColCntMask); 6878c2ecf20Sopenharmony_ci cp->dev->stats.tx_packets++; 6888c2ecf20Sopenharmony_ci cp->dev->stats.tx_bytes += skb->len; 6898c2ecf20Sopenharmony_ci netif_dbg(cp, tx_done, cp->dev, 6908c2ecf20Sopenharmony_ci "tx done, slot %d\n", tx_tail); 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci bytes_compl += skb->len; 6938c2ecf20Sopenharmony_ci pkts_compl++; 6948c2ecf20Sopenharmony_ci dev_consume_skb_irq(skb); 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci cp->tx_skb[tx_tail] = NULL; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci tx_tail = NEXT_TX(tx_tail); 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci cp->tx_tail = tx_tail; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci netdev_completed_queue(cp->dev, pkts_compl, bytes_compl); 7058c2ecf20Sopenharmony_ci if (TX_BUFFS_AVAIL(cp) > (MAX_SKB_FRAGS + 1)) 7068c2ecf20Sopenharmony_ci netif_wake_queue(cp->dev); 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_cistatic inline u32 cp_tx_vlan_tag(struct sk_buff *skb) 7108c2ecf20Sopenharmony_ci{ 7118c2ecf20Sopenharmony_ci return skb_vlan_tag_present(skb) ? 7128c2ecf20Sopenharmony_ci TxVlanTag | swab16(skb_vlan_tag_get(skb)) : 0x00; 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_cistatic void unwind_tx_frag_mapping(struct cp_private *cp, struct sk_buff *skb, 7168c2ecf20Sopenharmony_ci int first, int entry_last) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci int frag, index; 7198c2ecf20Sopenharmony_ci struct cp_desc *txd; 7208c2ecf20Sopenharmony_ci skb_frag_t *this_frag; 7218c2ecf20Sopenharmony_ci for (frag = 0; frag+first < entry_last; frag++) { 7228c2ecf20Sopenharmony_ci index = first+frag; 7238c2ecf20Sopenharmony_ci cp->tx_skb[index] = NULL; 7248c2ecf20Sopenharmony_ci txd = &cp->tx_ring[index]; 7258c2ecf20Sopenharmony_ci this_frag = &skb_shinfo(skb)->frags[frag]; 7268c2ecf20Sopenharmony_ci dma_unmap_single(&cp->pdev->dev, le64_to_cpu(txd->addr), 7278c2ecf20Sopenharmony_ci skb_frag_size(this_frag), PCI_DMA_TODEVICE); 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cistatic netdev_tx_t cp_start_xmit (struct sk_buff *skb, 7328c2ecf20Sopenharmony_ci struct net_device *dev) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 7358c2ecf20Sopenharmony_ci unsigned entry; 7368c2ecf20Sopenharmony_ci u32 eor, opts1; 7378c2ecf20Sopenharmony_ci unsigned long intr_flags; 7388c2ecf20Sopenharmony_ci __le32 opts2; 7398c2ecf20Sopenharmony_ci int mss = 0; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci spin_lock_irqsave(&cp->lock, intr_flags); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci /* This is a hard error, log it. */ 7448c2ecf20Sopenharmony_ci if (TX_BUFFS_AVAIL(cp) <= (skb_shinfo(skb)->nr_frags + 1)) { 7458c2ecf20Sopenharmony_ci netif_stop_queue(dev); 7468c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, intr_flags); 7478c2ecf20Sopenharmony_ci netdev_err(dev, "BUG! Tx Ring full when queue awake!\n"); 7488c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci entry = cp->tx_head; 7528c2ecf20Sopenharmony_ci eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0; 7538c2ecf20Sopenharmony_ci mss = skb_shinfo(skb)->gso_size; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci if (mss > MSSMask) { 7568c2ecf20Sopenharmony_ci netdev_WARN_ONCE(dev, "Net bug: GSO size %d too large for 8139CP\n", 7578c2ecf20Sopenharmony_ci mss); 7588c2ecf20Sopenharmony_ci goto out_dma_error; 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci opts2 = cpu_to_le32(cp_tx_vlan_tag(skb)); 7628c2ecf20Sopenharmony_ci opts1 = DescOwn; 7638c2ecf20Sopenharmony_ci if (mss) 7648c2ecf20Sopenharmony_ci opts1 |= LargeSend | (mss << MSSShift); 7658c2ecf20Sopenharmony_ci else if (skb->ip_summed == CHECKSUM_PARTIAL) { 7668c2ecf20Sopenharmony_ci const struct iphdr *ip = ip_hdr(skb); 7678c2ecf20Sopenharmony_ci if (ip->protocol == IPPROTO_TCP) 7688c2ecf20Sopenharmony_ci opts1 |= IPCS | TCPCS; 7698c2ecf20Sopenharmony_ci else if (ip->protocol == IPPROTO_UDP) 7708c2ecf20Sopenharmony_ci opts1 |= IPCS | UDPCS; 7718c2ecf20Sopenharmony_ci else { 7728c2ecf20Sopenharmony_ci WARN_ONCE(1, 7738c2ecf20Sopenharmony_ci "Net bug: asked to checksum invalid Legacy IP packet\n"); 7748c2ecf20Sopenharmony_ci goto out_dma_error; 7758c2ecf20Sopenharmony_ci } 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci if (skb_shinfo(skb)->nr_frags == 0) { 7798c2ecf20Sopenharmony_ci struct cp_desc *txd = &cp->tx_ring[entry]; 7808c2ecf20Sopenharmony_ci u32 len; 7818c2ecf20Sopenharmony_ci dma_addr_t mapping; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci len = skb->len; 7848c2ecf20Sopenharmony_ci mapping = dma_map_single(&cp->pdev->dev, skb->data, len, PCI_DMA_TODEVICE); 7858c2ecf20Sopenharmony_ci if (dma_mapping_error(&cp->pdev->dev, mapping)) 7868c2ecf20Sopenharmony_ci goto out_dma_error; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci txd->opts2 = opts2; 7898c2ecf20Sopenharmony_ci txd->addr = cpu_to_le64(mapping); 7908c2ecf20Sopenharmony_ci wmb(); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci opts1 |= eor | len | FirstFrag | LastFrag; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci txd->opts1 = cpu_to_le32(opts1); 7958c2ecf20Sopenharmony_ci wmb(); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci cp->tx_skb[entry] = skb; 7988c2ecf20Sopenharmony_ci cp->tx_opts[entry] = opts1; 7998c2ecf20Sopenharmony_ci netif_dbg(cp, tx_queued, cp->dev, "tx queued, slot %d, skblen %d\n", 8008c2ecf20Sopenharmony_ci entry, skb->len); 8018c2ecf20Sopenharmony_ci } else { 8028c2ecf20Sopenharmony_ci struct cp_desc *txd; 8038c2ecf20Sopenharmony_ci u32 first_len, first_eor, ctrl; 8048c2ecf20Sopenharmony_ci dma_addr_t first_mapping; 8058c2ecf20Sopenharmony_ci int frag, first_entry = entry; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci /* We must give this initial chunk to the device last. 8088c2ecf20Sopenharmony_ci * Otherwise we could race with the device. 8098c2ecf20Sopenharmony_ci */ 8108c2ecf20Sopenharmony_ci first_eor = eor; 8118c2ecf20Sopenharmony_ci first_len = skb_headlen(skb); 8128c2ecf20Sopenharmony_ci first_mapping = dma_map_single(&cp->pdev->dev, skb->data, 8138c2ecf20Sopenharmony_ci first_len, PCI_DMA_TODEVICE); 8148c2ecf20Sopenharmony_ci if (dma_mapping_error(&cp->pdev->dev, first_mapping)) 8158c2ecf20Sopenharmony_ci goto out_dma_error; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci cp->tx_skb[entry] = skb; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) { 8208c2ecf20Sopenharmony_ci const skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag]; 8218c2ecf20Sopenharmony_ci u32 len; 8228c2ecf20Sopenharmony_ci dma_addr_t mapping; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci entry = NEXT_TX(entry); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci len = skb_frag_size(this_frag); 8278c2ecf20Sopenharmony_ci mapping = dma_map_single(&cp->pdev->dev, 8288c2ecf20Sopenharmony_ci skb_frag_address(this_frag), 8298c2ecf20Sopenharmony_ci len, PCI_DMA_TODEVICE); 8308c2ecf20Sopenharmony_ci if (dma_mapping_error(&cp->pdev->dev, mapping)) { 8318c2ecf20Sopenharmony_ci unwind_tx_frag_mapping(cp, skb, first_entry, entry); 8328c2ecf20Sopenharmony_ci goto out_dma_error; 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci ctrl = opts1 | eor | len; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci if (frag == skb_shinfo(skb)->nr_frags - 1) 8408c2ecf20Sopenharmony_ci ctrl |= LastFrag; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci txd = &cp->tx_ring[entry]; 8438c2ecf20Sopenharmony_ci txd->opts2 = opts2; 8448c2ecf20Sopenharmony_ci txd->addr = cpu_to_le64(mapping); 8458c2ecf20Sopenharmony_ci wmb(); 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci txd->opts1 = cpu_to_le32(ctrl); 8488c2ecf20Sopenharmony_ci wmb(); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci cp->tx_opts[entry] = ctrl; 8518c2ecf20Sopenharmony_ci cp->tx_skb[entry] = skb; 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci txd = &cp->tx_ring[first_entry]; 8558c2ecf20Sopenharmony_ci txd->opts2 = opts2; 8568c2ecf20Sopenharmony_ci txd->addr = cpu_to_le64(first_mapping); 8578c2ecf20Sopenharmony_ci wmb(); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci ctrl = opts1 | first_eor | first_len | FirstFrag; 8608c2ecf20Sopenharmony_ci txd->opts1 = cpu_to_le32(ctrl); 8618c2ecf20Sopenharmony_ci wmb(); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci cp->tx_opts[first_entry] = ctrl; 8648c2ecf20Sopenharmony_ci netif_dbg(cp, tx_queued, cp->dev, "tx queued, slots %d-%d, skblen %d\n", 8658c2ecf20Sopenharmony_ci first_entry, entry, skb->len); 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci cp->tx_head = NEXT_TX(entry); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci netdev_sent_queue(dev, skb->len); 8708c2ecf20Sopenharmony_ci if (TX_BUFFS_AVAIL(cp) <= (MAX_SKB_FRAGS + 1)) 8718c2ecf20Sopenharmony_ci netif_stop_queue(dev); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ciout_unlock: 8748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, intr_flags); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci cpw8(TxPoll, NormalTxPoll); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 8798c2ecf20Sopenharmony_ciout_dma_error: 8808c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 8818c2ecf20Sopenharmony_ci cp->dev->stats.tx_dropped++; 8828c2ecf20Sopenharmony_ci goto out_unlock; 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci/* Set or clear the multicast filter for this adaptor. 8868c2ecf20Sopenharmony_ci This routine is not state sensitive and need not be SMP locked. */ 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_cistatic void __cp_set_rx_mode (struct net_device *dev) 8898c2ecf20Sopenharmony_ci{ 8908c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 8918c2ecf20Sopenharmony_ci u32 mc_filter[2]; /* Multicast hash filter */ 8928c2ecf20Sopenharmony_ci int rx_mode; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci /* Note: do not reorder, GCC is clever about common statements. */ 8958c2ecf20Sopenharmony_ci if (dev->flags & IFF_PROMISC) { 8968c2ecf20Sopenharmony_ci /* Unconditionally log net taps. */ 8978c2ecf20Sopenharmony_ci rx_mode = 8988c2ecf20Sopenharmony_ci AcceptBroadcast | AcceptMulticast | AcceptMyPhys | 8998c2ecf20Sopenharmony_ci AcceptAllPhys; 9008c2ecf20Sopenharmony_ci mc_filter[1] = mc_filter[0] = 0xffffffff; 9018c2ecf20Sopenharmony_ci } else if ((netdev_mc_count(dev) > multicast_filter_limit) || 9028c2ecf20Sopenharmony_ci (dev->flags & IFF_ALLMULTI)) { 9038c2ecf20Sopenharmony_ci /* Too many to filter perfectly -- accept all multicasts. */ 9048c2ecf20Sopenharmony_ci rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; 9058c2ecf20Sopenharmony_ci mc_filter[1] = mc_filter[0] = 0xffffffff; 9068c2ecf20Sopenharmony_ci } else { 9078c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 9088c2ecf20Sopenharmony_ci rx_mode = AcceptBroadcast | AcceptMyPhys; 9098c2ecf20Sopenharmony_ci mc_filter[1] = mc_filter[0] = 0; 9108c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 9118c2ecf20Sopenharmony_ci int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); 9148c2ecf20Sopenharmony_ci rx_mode |= AcceptMulticast; 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci /* We can safely update without stopping the chip. */ 9198c2ecf20Sopenharmony_ci cp->rx_config = cp_rx_config | rx_mode; 9208c2ecf20Sopenharmony_ci cpw32_f(RxConfig, cp->rx_config); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci cpw32_f (MAR0 + 0, mc_filter[0]); 9238c2ecf20Sopenharmony_ci cpw32_f (MAR0 + 4, mc_filter[1]); 9248c2ecf20Sopenharmony_ci} 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_cistatic void cp_set_rx_mode (struct net_device *dev) 9278c2ecf20Sopenharmony_ci{ 9288c2ecf20Sopenharmony_ci unsigned long flags; 9298c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci spin_lock_irqsave (&cp->lock, flags); 9328c2ecf20Sopenharmony_ci __cp_set_rx_mode(dev); 9338c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&cp->lock, flags); 9348c2ecf20Sopenharmony_ci} 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_cistatic void __cp_get_stats(struct cp_private *cp) 9378c2ecf20Sopenharmony_ci{ 9388c2ecf20Sopenharmony_ci /* only lower 24 bits valid; write any value to clear */ 9398c2ecf20Sopenharmony_ci cp->dev->stats.rx_missed_errors += (cpr32 (RxMissed) & 0xffffff); 9408c2ecf20Sopenharmony_ci cpw32 (RxMissed, 0); 9418c2ecf20Sopenharmony_ci} 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_cistatic struct net_device_stats *cp_get_stats(struct net_device *dev) 9448c2ecf20Sopenharmony_ci{ 9458c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 9468c2ecf20Sopenharmony_ci unsigned long flags; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci /* The chip only need report frame silently dropped. */ 9498c2ecf20Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 9508c2ecf20Sopenharmony_ci if (netif_running(dev) && netif_device_present(dev)) 9518c2ecf20Sopenharmony_ci __cp_get_stats(cp); 9528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci return &dev->stats; 9558c2ecf20Sopenharmony_ci} 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_cistatic void cp_stop_hw (struct cp_private *cp) 9588c2ecf20Sopenharmony_ci{ 9598c2ecf20Sopenharmony_ci cpw16(IntrStatus, ~(cpr16(IntrStatus))); 9608c2ecf20Sopenharmony_ci cpw16_f(IntrMask, 0); 9618c2ecf20Sopenharmony_ci cpw8(Cmd, 0); 9628c2ecf20Sopenharmony_ci cpw16_f(CpCmd, 0); 9638c2ecf20Sopenharmony_ci cpw16_f(IntrStatus, ~(cpr16(IntrStatus))); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci cp->rx_tail = 0; 9668c2ecf20Sopenharmony_ci cp->tx_head = cp->tx_tail = 0; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci netdev_reset_queue(cp->dev); 9698c2ecf20Sopenharmony_ci} 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_cistatic void cp_reset_hw (struct cp_private *cp) 9728c2ecf20Sopenharmony_ci{ 9738c2ecf20Sopenharmony_ci unsigned work = 1000; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci cpw8(Cmd, CmdReset); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci while (work--) { 9788c2ecf20Sopenharmony_ci if (!(cpr8(Cmd) & CmdReset)) 9798c2ecf20Sopenharmony_ci return; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci schedule_timeout_uninterruptible(10); 9828c2ecf20Sopenharmony_ci } 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci netdev_err(cp->dev, "hardware reset timeout\n"); 9858c2ecf20Sopenharmony_ci} 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_cistatic inline void cp_start_hw (struct cp_private *cp) 9888c2ecf20Sopenharmony_ci{ 9898c2ecf20Sopenharmony_ci dma_addr_t ring_dma; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci cpw16(CpCmd, cp->cpcmd); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci /* 9948c2ecf20Sopenharmony_ci * These (at least TxRingAddr) need to be configured after the 9958c2ecf20Sopenharmony_ci * corresponding bits in CpCmd are enabled. Datasheet v1.6 §6.33 9968c2ecf20Sopenharmony_ci * (C+ Command Register) recommends that these and more be configured 9978c2ecf20Sopenharmony_ci * *after* the [RT]xEnable bits in CpCmd are set. And on some hardware 9988c2ecf20Sopenharmony_ci * it's been observed that the TxRingAddr is actually reset to garbage 9998c2ecf20Sopenharmony_ci * when C+ mode Tx is enabled in CpCmd. 10008c2ecf20Sopenharmony_ci */ 10018c2ecf20Sopenharmony_ci cpw32_f(HiTxRingAddr, 0); 10028c2ecf20Sopenharmony_ci cpw32_f(HiTxRingAddr + 4, 0); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci ring_dma = cp->ring_dma; 10058c2ecf20Sopenharmony_ci cpw32_f(RxRingAddr, ring_dma & 0xffffffff); 10068c2ecf20Sopenharmony_ci cpw32_f(RxRingAddr + 4, (ring_dma >> 16) >> 16); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci ring_dma += sizeof(struct cp_desc) * CP_RX_RING_SIZE; 10098c2ecf20Sopenharmony_ci cpw32_f(TxRingAddr, ring_dma & 0xffffffff); 10108c2ecf20Sopenharmony_ci cpw32_f(TxRingAddr + 4, (ring_dma >> 16) >> 16); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci /* 10138c2ecf20Sopenharmony_ci * Strictly speaking, the datasheet says this should be enabled 10148c2ecf20Sopenharmony_ci * *before* setting the descriptor addresses. But what, then, would 10158c2ecf20Sopenharmony_ci * prevent it from doing DMA to random unconfigured addresses? 10168c2ecf20Sopenharmony_ci * This variant appears to work fine. 10178c2ecf20Sopenharmony_ci */ 10188c2ecf20Sopenharmony_ci cpw8(Cmd, RxOn | TxOn); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci netdev_reset_queue(cp->dev); 10218c2ecf20Sopenharmony_ci} 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_cistatic void cp_enable_irq(struct cp_private *cp) 10248c2ecf20Sopenharmony_ci{ 10258c2ecf20Sopenharmony_ci cpw16_f(IntrMask, cp_intr_mask); 10268c2ecf20Sopenharmony_ci} 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_cistatic void cp_init_hw (struct cp_private *cp) 10298c2ecf20Sopenharmony_ci{ 10308c2ecf20Sopenharmony_ci struct net_device *dev = cp->dev; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci cp_reset_hw(cp); 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci cpw8_f (Cfg9346, Cfg9346_Unlock); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci /* Restore our idea of the MAC address. */ 10378c2ecf20Sopenharmony_ci cpw32_f (MAC0 + 0, le32_to_cpu (*(__le32 *) (dev->dev_addr + 0))); 10388c2ecf20Sopenharmony_ci cpw32_f (MAC0 + 4, le32_to_cpu (*(__le32 *) (dev->dev_addr + 4))); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci cp_start_hw(cp); 10418c2ecf20Sopenharmony_ci cpw8(TxThresh, 0x06); /* XXX convert magic num to a constant */ 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci __cp_set_rx_mode(dev); 10448c2ecf20Sopenharmony_ci cpw32_f (TxConfig, IFG | (TX_DMA_BURST << TxDMAShift)); 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci cpw8(Config1, cpr8(Config1) | DriverLoaded | PMEnable); 10478c2ecf20Sopenharmony_ci /* Disable Wake-on-LAN. Can be turned on with ETHTOOL_SWOL */ 10488c2ecf20Sopenharmony_ci cpw8(Config3, PARMEnable); 10498c2ecf20Sopenharmony_ci cp->wol_enabled = 0; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci cpw8(Config5, cpr8(Config5) & PMEStatus); 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci cpw16(MultiIntr, 0); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci cpw8_f(Cfg9346, Cfg9346_Lock); 10568c2ecf20Sopenharmony_ci} 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_cistatic int cp_refill_rx(struct cp_private *cp) 10598c2ecf20Sopenharmony_ci{ 10608c2ecf20Sopenharmony_ci struct net_device *dev = cp->dev; 10618c2ecf20Sopenharmony_ci unsigned i; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci for (i = 0; i < CP_RX_RING_SIZE; i++) { 10648c2ecf20Sopenharmony_ci struct sk_buff *skb; 10658c2ecf20Sopenharmony_ci dma_addr_t mapping; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci skb = netdev_alloc_skb_ip_align(dev, cp->rx_buf_sz); 10688c2ecf20Sopenharmony_ci if (!skb) 10698c2ecf20Sopenharmony_ci goto err_out; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci mapping = dma_map_single(&cp->pdev->dev, skb->data, 10728c2ecf20Sopenharmony_ci cp->rx_buf_sz, PCI_DMA_FROMDEVICE); 10738c2ecf20Sopenharmony_ci if (dma_mapping_error(&cp->pdev->dev, mapping)) { 10748c2ecf20Sopenharmony_ci kfree_skb(skb); 10758c2ecf20Sopenharmony_ci goto err_out; 10768c2ecf20Sopenharmony_ci } 10778c2ecf20Sopenharmony_ci cp->rx_skb[i] = skb; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci cp->rx_ring[i].opts2 = 0; 10808c2ecf20Sopenharmony_ci cp->rx_ring[i].addr = cpu_to_le64(mapping); 10818c2ecf20Sopenharmony_ci if (i == (CP_RX_RING_SIZE - 1)) 10828c2ecf20Sopenharmony_ci cp->rx_ring[i].opts1 = 10838c2ecf20Sopenharmony_ci cpu_to_le32(DescOwn | RingEnd | cp->rx_buf_sz); 10848c2ecf20Sopenharmony_ci else 10858c2ecf20Sopenharmony_ci cp->rx_ring[i].opts1 = 10868c2ecf20Sopenharmony_ci cpu_to_le32(DescOwn | cp->rx_buf_sz); 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci return 0; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_cierr_out: 10928c2ecf20Sopenharmony_ci cp_clean_rings(cp); 10938c2ecf20Sopenharmony_ci return -ENOMEM; 10948c2ecf20Sopenharmony_ci} 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_cistatic void cp_init_rings_index (struct cp_private *cp) 10978c2ecf20Sopenharmony_ci{ 10988c2ecf20Sopenharmony_ci cp->rx_tail = 0; 10998c2ecf20Sopenharmony_ci cp->tx_head = cp->tx_tail = 0; 11008c2ecf20Sopenharmony_ci} 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_cistatic int cp_init_rings (struct cp_private *cp) 11038c2ecf20Sopenharmony_ci{ 11048c2ecf20Sopenharmony_ci memset(cp->tx_ring, 0, sizeof(struct cp_desc) * CP_TX_RING_SIZE); 11058c2ecf20Sopenharmony_ci cp->tx_ring[CP_TX_RING_SIZE - 1].opts1 = cpu_to_le32(RingEnd); 11068c2ecf20Sopenharmony_ci memset(cp->tx_opts, 0, sizeof(cp->tx_opts)); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci cp_init_rings_index(cp); 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci return cp_refill_rx (cp); 11118c2ecf20Sopenharmony_ci} 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_cistatic int cp_alloc_rings (struct cp_private *cp) 11148c2ecf20Sopenharmony_ci{ 11158c2ecf20Sopenharmony_ci struct device *d = &cp->pdev->dev; 11168c2ecf20Sopenharmony_ci void *mem; 11178c2ecf20Sopenharmony_ci int rc; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci mem = dma_alloc_coherent(d, CP_RING_BYTES, &cp->ring_dma, GFP_KERNEL); 11208c2ecf20Sopenharmony_ci if (!mem) 11218c2ecf20Sopenharmony_ci return -ENOMEM; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci cp->rx_ring = mem; 11248c2ecf20Sopenharmony_ci cp->tx_ring = &cp->rx_ring[CP_RX_RING_SIZE]; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci rc = cp_init_rings(cp); 11278c2ecf20Sopenharmony_ci if (rc < 0) 11288c2ecf20Sopenharmony_ci dma_free_coherent(d, CP_RING_BYTES, cp->rx_ring, cp->ring_dma); 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci return rc; 11318c2ecf20Sopenharmony_ci} 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_cistatic void cp_clean_rings (struct cp_private *cp) 11348c2ecf20Sopenharmony_ci{ 11358c2ecf20Sopenharmony_ci struct cp_desc *desc; 11368c2ecf20Sopenharmony_ci unsigned i; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci for (i = 0; i < CP_RX_RING_SIZE; i++) { 11398c2ecf20Sopenharmony_ci if (cp->rx_skb[i]) { 11408c2ecf20Sopenharmony_ci desc = cp->rx_ring + i; 11418c2ecf20Sopenharmony_ci dma_unmap_single(&cp->pdev->dev,le64_to_cpu(desc->addr), 11428c2ecf20Sopenharmony_ci cp->rx_buf_sz, PCI_DMA_FROMDEVICE); 11438c2ecf20Sopenharmony_ci dev_kfree_skb_any(cp->rx_skb[i]); 11448c2ecf20Sopenharmony_ci } 11458c2ecf20Sopenharmony_ci } 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci for (i = 0; i < CP_TX_RING_SIZE; i++) { 11488c2ecf20Sopenharmony_ci if (cp->tx_skb[i]) { 11498c2ecf20Sopenharmony_ci struct sk_buff *skb = cp->tx_skb[i]; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci desc = cp->tx_ring + i; 11528c2ecf20Sopenharmony_ci dma_unmap_single(&cp->pdev->dev,le64_to_cpu(desc->addr), 11538c2ecf20Sopenharmony_ci le32_to_cpu(desc->opts1) & 0xffff, 11548c2ecf20Sopenharmony_ci PCI_DMA_TODEVICE); 11558c2ecf20Sopenharmony_ci if (le32_to_cpu(desc->opts1) & LastFrag) 11568c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 11578c2ecf20Sopenharmony_ci cp->dev->stats.tx_dropped++; 11588c2ecf20Sopenharmony_ci } 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci netdev_reset_queue(cp->dev); 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci memset(cp->rx_ring, 0, sizeof(struct cp_desc) * CP_RX_RING_SIZE); 11638c2ecf20Sopenharmony_ci memset(cp->tx_ring, 0, sizeof(struct cp_desc) * CP_TX_RING_SIZE); 11648c2ecf20Sopenharmony_ci memset(cp->tx_opts, 0, sizeof(cp->tx_opts)); 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci memset(cp->rx_skb, 0, sizeof(struct sk_buff *) * CP_RX_RING_SIZE); 11678c2ecf20Sopenharmony_ci memset(cp->tx_skb, 0, sizeof(struct sk_buff *) * CP_TX_RING_SIZE); 11688c2ecf20Sopenharmony_ci} 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_cistatic void cp_free_rings (struct cp_private *cp) 11718c2ecf20Sopenharmony_ci{ 11728c2ecf20Sopenharmony_ci cp_clean_rings(cp); 11738c2ecf20Sopenharmony_ci dma_free_coherent(&cp->pdev->dev, CP_RING_BYTES, cp->rx_ring, 11748c2ecf20Sopenharmony_ci cp->ring_dma); 11758c2ecf20Sopenharmony_ci cp->rx_ring = NULL; 11768c2ecf20Sopenharmony_ci cp->tx_ring = NULL; 11778c2ecf20Sopenharmony_ci} 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_cistatic int cp_open (struct net_device *dev) 11808c2ecf20Sopenharmony_ci{ 11818c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 11828c2ecf20Sopenharmony_ci const int irq = cp->pdev->irq; 11838c2ecf20Sopenharmony_ci int rc; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci netif_dbg(cp, ifup, dev, "enabling interface\n"); 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci rc = cp_alloc_rings(cp); 11888c2ecf20Sopenharmony_ci if (rc) 11898c2ecf20Sopenharmony_ci return rc; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci napi_enable(&cp->napi); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci cp_init_hw(cp); 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci rc = request_irq(irq, cp_interrupt, IRQF_SHARED, dev->name, dev); 11968c2ecf20Sopenharmony_ci if (rc) 11978c2ecf20Sopenharmony_ci goto err_out_hw; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci cp_enable_irq(cp); 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci netif_carrier_off(dev); 12028c2ecf20Sopenharmony_ci mii_check_media(&cp->mii_if, netif_msg_link(cp), true); 12038c2ecf20Sopenharmony_ci netif_start_queue(dev); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci return 0; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_cierr_out_hw: 12088c2ecf20Sopenharmony_ci napi_disable(&cp->napi); 12098c2ecf20Sopenharmony_ci cp_stop_hw(cp); 12108c2ecf20Sopenharmony_ci cp_free_rings(cp); 12118c2ecf20Sopenharmony_ci return rc; 12128c2ecf20Sopenharmony_ci} 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_cistatic int cp_close (struct net_device *dev) 12158c2ecf20Sopenharmony_ci{ 12168c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 12178c2ecf20Sopenharmony_ci unsigned long flags; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci napi_disable(&cp->napi); 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci netif_dbg(cp, ifdown, dev, "disabling interface\n"); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci netif_stop_queue(dev); 12268c2ecf20Sopenharmony_ci netif_carrier_off(dev); 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci cp_stop_hw(cp); 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci free_irq(cp->pdev->irq, dev); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci cp_free_rings(cp); 12358c2ecf20Sopenharmony_ci return 0; 12368c2ecf20Sopenharmony_ci} 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_cistatic void cp_tx_timeout(struct net_device *dev, unsigned int txqueue) 12398c2ecf20Sopenharmony_ci{ 12408c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 12418c2ecf20Sopenharmony_ci unsigned long flags; 12428c2ecf20Sopenharmony_ci int i; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci netdev_warn(dev, "Transmit timeout, status %2x %4x %4x %4x\n", 12458c2ecf20Sopenharmony_ci cpr8(Cmd), cpr16(CpCmd), 12468c2ecf20Sopenharmony_ci cpr16(IntrStatus), cpr16(IntrMask)); 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci netif_dbg(cp, tx_err, cp->dev, "TX ring head %d tail %d desc %x\n", 12518c2ecf20Sopenharmony_ci cp->tx_head, cp->tx_tail, cpr16(TxDmaOkLowDesc)); 12528c2ecf20Sopenharmony_ci for (i = 0; i < CP_TX_RING_SIZE; i++) { 12538c2ecf20Sopenharmony_ci netif_dbg(cp, tx_err, cp->dev, 12548c2ecf20Sopenharmony_ci "TX slot %d @%p: %08x (%08x) %08x %llx %p\n", 12558c2ecf20Sopenharmony_ci i, &cp->tx_ring[i], le32_to_cpu(cp->tx_ring[i].opts1), 12568c2ecf20Sopenharmony_ci cp->tx_opts[i], le32_to_cpu(cp->tx_ring[i].opts2), 12578c2ecf20Sopenharmony_ci le64_to_cpu(cp->tx_ring[i].addr), 12588c2ecf20Sopenharmony_ci cp->tx_skb[i]); 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci cp_stop_hw(cp); 12628c2ecf20Sopenharmony_ci cp_clean_rings(cp); 12638c2ecf20Sopenharmony_ci cp_init_rings(cp); 12648c2ecf20Sopenharmony_ci cp_start_hw(cp); 12658c2ecf20Sopenharmony_ci __cp_set_rx_mode(dev); 12668c2ecf20Sopenharmony_ci cpw16_f(IntrMask, cp_norx_intr_mask); 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci netif_wake_queue(dev); 12698c2ecf20Sopenharmony_ci napi_schedule_irqoff(&cp->napi); 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 12728c2ecf20Sopenharmony_ci} 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_cistatic int cp_change_mtu(struct net_device *dev, int new_mtu) 12758c2ecf20Sopenharmony_ci{ 12768c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci /* if network interface not up, no need for complexity */ 12798c2ecf20Sopenharmony_ci if (!netif_running(dev)) { 12808c2ecf20Sopenharmony_ci dev->mtu = new_mtu; 12818c2ecf20Sopenharmony_ci cp_set_rxbufsize(cp); /* set new rx buf size */ 12828c2ecf20Sopenharmony_ci return 0; 12838c2ecf20Sopenharmony_ci } 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci /* network IS up, close it, reset MTU, and come up again. */ 12868c2ecf20Sopenharmony_ci cp_close(dev); 12878c2ecf20Sopenharmony_ci dev->mtu = new_mtu; 12888c2ecf20Sopenharmony_ci cp_set_rxbufsize(cp); 12898c2ecf20Sopenharmony_ci return cp_open(dev); 12908c2ecf20Sopenharmony_ci} 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_cistatic const char mii_2_8139_map[8] = { 12938c2ecf20Sopenharmony_ci BasicModeCtrl, 12948c2ecf20Sopenharmony_ci BasicModeStatus, 12958c2ecf20Sopenharmony_ci 0, 12968c2ecf20Sopenharmony_ci 0, 12978c2ecf20Sopenharmony_ci NWayAdvert, 12988c2ecf20Sopenharmony_ci NWayLPAR, 12998c2ecf20Sopenharmony_ci NWayExpansion, 13008c2ecf20Sopenharmony_ci 0 13018c2ecf20Sopenharmony_ci}; 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_cistatic int mdio_read(struct net_device *dev, int phy_id, int location) 13048c2ecf20Sopenharmony_ci{ 13058c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci return location < 8 && mii_2_8139_map[location] ? 13088c2ecf20Sopenharmony_ci readw(cp->regs + mii_2_8139_map[location]) : 0; 13098c2ecf20Sopenharmony_ci} 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_cistatic void mdio_write(struct net_device *dev, int phy_id, int location, 13138c2ecf20Sopenharmony_ci int value) 13148c2ecf20Sopenharmony_ci{ 13158c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci if (location == 0) { 13188c2ecf20Sopenharmony_ci cpw8(Cfg9346, Cfg9346_Unlock); 13198c2ecf20Sopenharmony_ci cpw16(BasicModeCtrl, value); 13208c2ecf20Sopenharmony_ci cpw8(Cfg9346, Cfg9346_Lock); 13218c2ecf20Sopenharmony_ci } else if (location < 8 && mii_2_8139_map[location]) 13228c2ecf20Sopenharmony_ci cpw16(mii_2_8139_map[location], value); 13238c2ecf20Sopenharmony_ci} 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci/* Set the ethtool Wake-on-LAN settings */ 13268c2ecf20Sopenharmony_cistatic int netdev_set_wol (struct cp_private *cp, 13278c2ecf20Sopenharmony_ci const struct ethtool_wolinfo *wol) 13288c2ecf20Sopenharmony_ci{ 13298c2ecf20Sopenharmony_ci u8 options; 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci options = cpr8 (Config3) & ~(LinkUp | MagicPacket); 13328c2ecf20Sopenharmony_ci /* If WOL is being disabled, no need for complexity */ 13338c2ecf20Sopenharmony_ci if (wol->wolopts) { 13348c2ecf20Sopenharmony_ci if (wol->wolopts & WAKE_PHY) options |= LinkUp; 13358c2ecf20Sopenharmony_ci if (wol->wolopts & WAKE_MAGIC) options |= MagicPacket; 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci cpw8 (Cfg9346, Cfg9346_Unlock); 13398c2ecf20Sopenharmony_ci cpw8 (Config3, options); 13408c2ecf20Sopenharmony_ci cpw8 (Cfg9346, Cfg9346_Lock); 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci options = 0; /* Paranoia setting */ 13438c2ecf20Sopenharmony_ci options = cpr8 (Config5) & ~(UWF | MWF | BWF); 13448c2ecf20Sopenharmony_ci /* If WOL is being disabled, no need for complexity */ 13458c2ecf20Sopenharmony_ci if (wol->wolopts) { 13468c2ecf20Sopenharmony_ci if (wol->wolopts & WAKE_UCAST) options |= UWF; 13478c2ecf20Sopenharmony_ci if (wol->wolopts & WAKE_BCAST) options |= BWF; 13488c2ecf20Sopenharmony_ci if (wol->wolopts & WAKE_MCAST) options |= MWF; 13498c2ecf20Sopenharmony_ci } 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci cpw8 (Config5, options); 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci cp->wol_enabled = (wol->wolopts) ? 1 : 0; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci return 0; 13568c2ecf20Sopenharmony_ci} 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci/* Get the ethtool Wake-on-LAN settings */ 13598c2ecf20Sopenharmony_cistatic void netdev_get_wol (struct cp_private *cp, 13608c2ecf20Sopenharmony_ci struct ethtool_wolinfo *wol) 13618c2ecf20Sopenharmony_ci{ 13628c2ecf20Sopenharmony_ci u8 options; 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci wol->wolopts = 0; /* Start from scratch */ 13658c2ecf20Sopenharmony_ci wol->supported = WAKE_PHY | WAKE_BCAST | WAKE_MAGIC | 13668c2ecf20Sopenharmony_ci WAKE_MCAST | WAKE_UCAST; 13678c2ecf20Sopenharmony_ci /* We don't need to go on if WOL is disabled */ 13688c2ecf20Sopenharmony_ci if (!cp->wol_enabled) return; 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci options = cpr8 (Config3); 13718c2ecf20Sopenharmony_ci if (options & LinkUp) wol->wolopts |= WAKE_PHY; 13728c2ecf20Sopenharmony_ci if (options & MagicPacket) wol->wolopts |= WAKE_MAGIC; 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci options = 0; /* Paranoia setting */ 13758c2ecf20Sopenharmony_ci options = cpr8 (Config5); 13768c2ecf20Sopenharmony_ci if (options & UWF) wol->wolopts |= WAKE_UCAST; 13778c2ecf20Sopenharmony_ci if (options & BWF) wol->wolopts |= WAKE_BCAST; 13788c2ecf20Sopenharmony_ci if (options & MWF) wol->wolopts |= WAKE_MCAST; 13798c2ecf20Sopenharmony_ci} 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_cistatic void cp_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info) 13828c2ecf20Sopenharmony_ci{ 13838c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); 13868c2ecf20Sopenharmony_ci strlcpy(info->version, DRV_VERSION, sizeof(info->version)); 13878c2ecf20Sopenharmony_ci strlcpy(info->bus_info, pci_name(cp->pdev), sizeof(info->bus_info)); 13888c2ecf20Sopenharmony_ci} 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_cistatic void cp_get_ringparam(struct net_device *dev, 13918c2ecf20Sopenharmony_ci struct ethtool_ringparam *ring) 13928c2ecf20Sopenharmony_ci{ 13938c2ecf20Sopenharmony_ci ring->rx_max_pending = CP_RX_RING_SIZE; 13948c2ecf20Sopenharmony_ci ring->tx_max_pending = CP_TX_RING_SIZE; 13958c2ecf20Sopenharmony_ci ring->rx_pending = CP_RX_RING_SIZE; 13968c2ecf20Sopenharmony_ci ring->tx_pending = CP_TX_RING_SIZE; 13978c2ecf20Sopenharmony_ci} 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_cistatic int cp_get_regs_len(struct net_device *dev) 14008c2ecf20Sopenharmony_ci{ 14018c2ecf20Sopenharmony_ci return CP_REGS_SIZE; 14028c2ecf20Sopenharmony_ci} 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_cistatic int cp_get_sset_count (struct net_device *dev, int sset) 14058c2ecf20Sopenharmony_ci{ 14068c2ecf20Sopenharmony_ci switch (sset) { 14078c2ecf20Sopenharmony_ci case ETH_SS_STATS: 14088c2ecf20Sopenharmony_ci return CP_NUM_STATS; 14098c2ecf20Sopenharmony_ci default: 14108c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 14118c2ecf20Sopenharmony_ci } 14128c2ecf20Sopenharmony_ci} 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_cistatic int cp_get_link_ksettings(struct net_device *dev, 14158c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 14168c2ecf20Sopenharmony_ci{ 14178c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 14188c2ecf20Sopenharmony_ci unsigned long flags; 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 14218c2ecf20Sopenharmony_ci mii_ethtool_get_link_ksettings(&cp->mii_if, cmd); 14228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci return 0; 14258c2ecf20Sopenharmony_ci} 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_cistatic int cp_set_link_ksettings(struct net_device *dev, 14288c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 14298c2ecf20Sopenharmony_ci{ 14308c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 14318c2ecf20Sopenharmony_ci int rc; 14328c2ecf20Sopenharmony_ci unsigned long flags; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 14358c2ecf20Sopenharmony_ci rc = mii_ethtool_set_link_ksettings(&cp->mii_if, cmd); 14368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci return rc; 14398c2ecf20Sopenharmony_ci} 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_cistatic int cp_nway_reset(struct net_device *dev) 14428c2ecf20Sopenharmony_ci{ 14438c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 14448c2ecf20Sopenharmony_ci return mii_nway_restart(&cp->mii_if); 14458c2ecf20Sopenharmony_ci} 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_cistatic u32 cp_get_msglevel(struct net_device *dev) 14488c2ecf20Sopenharmony_ci{ 14498c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 14508c2ecf20Sopenharmony_ci return cp->msg_enable; 14518c2ecf20Sopenharmony_ci} 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_cistatic void cp_set_msglevel(struct net_device *dev, u32 value) 14548c2ecf20Sopenharmony_ci{ 14558c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 14568c2ecf20Sopenharmony_ci cp->msg_enable = value; 14578c2ecf20Sopenharmony_ci} 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_cistatic int cp_set_features(struct net_device *dev, netdev_features_t features) 14608c2ecf20Sopenharmony_ci{ 14618c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 14628c2ecf20Sopenharmony_ci unsigned long flags; 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci if (!((dev->features ^ features) & NETIF_F_RXCSUM)) 14658c2ecf20Sopenharmony_ci return 0; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci if (features & NETIF_F_RXCSUM) 14708c2ecf20Sopenharmony_ci cp->cpcmd |= RxChkSum; 14718c2ecf20Sopenharmony_ci else 14728c2ecf20Sopenharmony_ci cp->cpcmd &= ~RxChkSum; 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci if (features & NETIF_F_HW_VLAN_CTAG_RX) 14758c2ecf20Sopenharmony_ci cp->cpcmd |= RxVlanOn; 14768c2ecf20Sopenharmony_ci else 14778c2ecf20Sopenharmony_ci cp->cpcmd &= ~RxVlanOn; 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci cpw16_f(CpCmd, cp->cpcmd); 14808c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci return 0; 14838c2ecf20Sopenharmony_ci} 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_cistatic void cp_get_regs(struct net_device *dev, struct ethtool_regs *regs, 14868c2ecf20Sopenharmony_ci void *p) 14878c2ecf20Sopenharmony_ci{ 14888c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 14898c2ecf20Sopenharmony_ci unsigned long flags; 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci if (regs->len < CP_REGS_SIZE) 14928c2ecf20Sopenharmony_ci return /* -EINVAL */; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci regs->version = CP_REGS_VER; 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 14978c2ecf20Sopenharmony_ci memcpy_fromio(p, cp->regs, CP_REGS_SIZE); 14988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 14998c2ecf20Sopenharmony_ci} 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_cistatic void cp_get_wol (struct net_device *dev, struct ethtool_wolinfo *wol) 15028c2ecf20Sopenharmony_ci{ 15038c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 15048c2ecf20Sopenharmony_ci unsigned long flags; 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci spin_lock_irqsave (&cp->lock, flags); 15078c2ecf20Sopenharmony_ci netdev_get_wol (cp, wol); 15088c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&cp->lock, flags); 15098c2ecf20Sopenharmony_ci} 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_cistatic int cp_set_wol (struct net_device *dev, struct ethtool_wolinfo *wol) 15128c2ecf20Sopenharmony_ci{ 15138c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 15148c2ecf20Sopenharmony_ci unsigned long flags; 15158c2ecf20Sopenharmony_ci int rc; 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci spin_lock_irqsave (&cp->lock, flags); 15188c2ecf20Sopenharmony_ci rc = netdev_set_wol (cp, wol); 15198c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&cp->lock, flags); 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci return rc; 15228c2ecf20Sopenharmony_ci} 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_cistatic void cp_get_strings (struct net_device *dev, u32 stringset, u8 *buf) 15258c2ecf20Sopenharmony_ci{ 15268c2ecf20Sopenharmony_ci switch (stringset) { 15278c2ecf20Sopenharmony_ci case ETH_SS_STATS: 15288c2ecf20Sopenharmony_ci memcpy(buf, ðtool_stats_keys, sizeof(ethtool_stats_keys)); 15298c2ecf20Sopenharmony_ci break; 15308c2ecf20Sopenharmony_ci default: 15318c2ecf20Sopenharmony_ci BUG(); 15328c2ecf20Sopenharmony_ci break; 15338c2ecf20Sopenharmony_ci } 15348c2ecf20Sopenharmony_ci} 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_cistatic void cp_get_ethtool_stats (struct net_device *dev, 15378c2ecf20Sopenharmony_ci struct ethtool_stats *estats, u64 *tmp_stats) 15388c2ecf20Sopenharmony_ci{ 15398c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 15408c2ecf20Sopenharmony_ci struct cp_dma_stats *nic_stats; 15418c2ecf20Sopenharmony_ci dma_addr_t dma; 15428c2ecf20Sopenharmony_ci int i; 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci nic_stats = dma_alloc_coherent(&cp->pdev->dev, sizeof(*nic_stats), 15458c2ecf20Sopenharmony_ci &dma, GFP_KERNEL); 15468c2ecf20Sopenharmony_ci if (!nic_stats) 15478c2ecf20Sopenharmony_ci return; 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci /* begin NIC statistics dump */ 15508c2ecf20Sopenharmony_ci cpw32(StatsAddr + 4, (u64)dma >> 32); 15518c2ecf20Sopenharmony_ci cpw32(StatsAddr, ((u64)dma & DMA_BIT_MASK(32)) | DumpStats); 15528c2ecf20Sopenharmony_ci cpr32(StatsAddr); 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci for (i = 0; i < 1000; i++) { 15558c2ecf20Sopenharmony_ci if ((cpr32(StatsAddr) & DumpStats) == 0) 15568c2ecf20Sopenharmony_ci break; 15578c2ecf20Sopenharmony_ci udelay(10); 15588c2ecf20Sopenharmony_ci } 15598c2ecf20Sopenharmony_ci cpw32(StatsAddr, 0); 15608c2ecf20Sopenharmony_ci cpw32(StatsAddr + 4, 0); 15618c2ecf20Sopenharmony_ci cpr32(StatsAddr); 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci i = 0; 15648c2ecf20Sopenharmony_ci tmp_stats[i++] = le64_to_cpu(nic_stats->tx_ok); 15658c2ecf20Sopenharmony_ci tmp_stats[i++] = le64_to_cpu(nic_stats->rx_ok); 15668c2ecf20Sopenharmony_ci tmp_stats[i++] = le64_to_cpu(nic_stats->tx_err); 15678c2ecf20Sopenharmony_ci tmp_stats[i++] = le32_to_cpu(nic_stats->rx_err); 15688c2ecf20Sopenharmony_ci tmp_stats[i++] = le16_to_cpu(nic_stats->rx_fifo); 15698c2ecf20Sopenharmony_ci tmp_stats[i++] = le16_to_cpu(nic_stats->frame_align); 15708c2ecf20Sopenharmony_ci tmp_stats[i++] = le32_to_cpu(nic_stats->tx_ok_1col); 15718c2ecf20Sopenharmony_ci tmp_stats[i++] = le32_to_cpu(nic_stats->tx_ok_mcol); 15728c2ecf20Sopenharmony_ci tmp_stats[i++] = le64_to_cpu(nic_stats->rx_ok_phys); 15738c2ecf20Sopenharmony_ci tmp_stats[i++] = le64_to_cpu(nic_stats->rx_ok_bcast); 15748c2ecf20Sopenharmony_ci tmp_stats[i++] = le32_to_cpu(nic_stats->rx_ok_mcast); 15758c2ecf20Sopenharmony_ci tmp_stats[i++] = le16_to_cpu(nic_stats->tx_abort); 15768c2ecf20Sopenharmony_ci tmp_stats[i++] = le16_to_cpu(nic_stats->tx_underrun); 15778c2ecf20Sopenharmony_ci tmp_stats[i++] = cp->cp_stats.rx_frags; 15788c2ecf20Sopenharmony_ci BUG_ON(i != CP_NUM_STATS); 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci dma_free_coherent(&cp->pdev->dev, sizeof(*nic_stats), nic_stats, dma); 15818c2ecf20Sopenharmony_ci} 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_cistatic const struct ethtool_ops cp_ethtool_ops = { 15848c2ecf20Sopenharmony_ci .get_drvinfo = cp_get_drvinfo, 15858c2ecf20Sopenharmony_ci .get_regs_len = cp_get_regs_len, 15868c2ecf20Sopenharmony_ci .get_sset_count = cp_get_sset_count, 15878c2ecf20Sopenharmony_ci .nway_reset = cp_nway_reset, 15888c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 15898c2ecf20Sopenharmony_ci .get_msglevel = cp_get_msglevel, 15908c2ecf20Sopenharmony_ci .set_msglevel = cp_set_msglevel, 15918c2ecf20Sopenharmony_ci .get_regs = cp_get_regs, 15928c2ecf20Sopenharmony_ci .get_wol = cp_get_wol, 15938c2ecf20Sopenharmony_ci .set_wol = cp_set_wol, 15948c2ecf20Sopenharmony_ci .get_strings = cp_get_strings, 15958c2ecf20Sopenharmony_ci .get_ethtool_stats = cp_get_ethtool_stats, 15968c2ecf20Sopenharmony_ci .get_eeprom_len = cp_get_eeprom_len, 15978c2ecf20Sopenharmony_ci .get_eeprom = cp_get_eeprom, 15988c2ecf20Sopenharmony_ci .set_eeprom = cp_set_eeprom, 15998c2ecf20Sopenharmony_ci .get_ringparam = cp_get_ringparam, 16008c2ecf20Sopenharmony_ci .get_link_ksettings = cp_get_link_ksettings, 16018c2ecf20Sopenharmony_ci .set_link_ksettings = cp_set_link_ksettings, 16028c2ecf20Sopenharmony_ci}; 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_cistatic int cp_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) 16058c2ecf20Sopenharmony_ci{ 16068c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 16078c2ecf20Sopenharmony_ci int rc; 16088c2ecf20Sopenharmony_ci unsigned long flags; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci if (!netif_running(dev)) 16118c2ecf20Sopenharmony_ci return -EINVAL; 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 16148c2ecf20Sopenharmony_ci rc = generic_mii_ioctl(&cp->mii_if, if_mii(rq), cmd, NULL); 16158c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 16168c2ecf20Sopenharmony_ci return rc; 16178c2ecf20Sopenharmony_ci} 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_cistatic int cp_set_mac_address(struct net_device *dev, void *p) 16208c2ecf20Sopenharmony_ci{ 16218c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 16228c2ecf20Sopenharmony_ci struct sockaddr *addr = p; 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 16258c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci spin_lock_irq(&cp->lock); 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci cpw8_f(Cfg9346, Cfg9346_Unlock); 16328c2ecf20Sopenharmony_ci cpw32_f(MAC0 + 0, le32_to_cpu (*(__le32 *) (dev->dev_addr + 0))); 16338c2ecf20Sopenharmony_ci cpw32_f(MAC0 + 4, le32_to_cpu (*(__le32 *) (dev->dev_addr + 4))); 16348c2ecf20Sopenharmony_ci cpw8_f(Cfg9346, Cfg9346_Lock); 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci spin_unlock_irq(&cp->lock); 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci return 0; 16398c2ecf20Sopenharmony_ci} 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci/* Serial EEPROM section. */ 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci/* EEPROM_Ctrl bits. */ 16448c2ecf20Sopenharmony_ci#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */ 16458c2ecf20Sopenharmony_ci#define EE_CS 0x08 /* EEPROM chip select. */ 16468c2ecf20Sopenharmony_ci#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */ 16478c2ecf20Sopenharmony_ci#define EE_WRITE_0 0x00 16488c2ecf20Sopenharmony_ci#define EE_WRITE_1 0x02 16498c2ecf20Sopenharmony_ci#define EE_DATA_READ 0x01 /* EEPROM chip data out. */ 16508c2ecf20Sopenharmony_ci#define EE_ENB (0x80 | EE_CS) 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci/* Delay between EEPROM clock transitions. 16538c2ecf20Sopenharmony_ci No extra delay is needed with 33Mhz PCI, but 66Mhz may change this. 16548c2ecf20Sopenharmony_ci */ 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci#define eeprom_delay() readb(ee_addr) 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci/* The EEPROM commands include the alway-set leading bit. */ 16598c2ecf20Sopenharmony_ci#define EE_EXTEND_CMD (4) 16608c2ecf20Sopenharmony_ci#define EE_WRITE_CMD (5) 16618c2ecf20Sopenharmony_ci#define EE_READ_CMD (6) 16628c2ecf20Sopenharmony_ci#define EE_ERASE_CMD (7) 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci#define EE_EWDS_ADDR (0) 16658c2ecf20Sopenharmony_ci#define EE_WRAL_ADDR (1) 16668c2ecf20Sopenharmony_ci#define EE_ERAL_ADDR (2) 16678c2ecf20Sopenharmony_ci#define EE_EWEN_ADDR (3) 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci#define CP_EEPROM_MAGIC PCI_DEVICE_ID_REALTEK_8139 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_cistatic void eeprom_cmd_start(void __iomem *ee_addr) 16728c2ecf20Sopenharmony_ci{ 16738c2ecf20Sopenharmony_ci writeb (EE_ENB & ~EE_CS, ee_addr); 16748c2ecf20Sopenharmony_ci writeb (EE_ENB, ee_addr); 16758c2ecf20Sopenharmony_ci eeprom_delay (); 16768c2ecf20Sopenharmony_ci} 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_cistatic void eeprom_cmd(void __iomem *ee_addr, int cmd, int cmd_len) 16798c2ecf20Sopenharmony_ci{ 16808c2ecf20Sopenharmony_ci int i; 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci /* Shift the command bits out. */ 16838c2ecf20Sopenharmony_ci for (i = cmd_len - 1; i >= 0; i--) { 16848c2ecf20Sopenharmony_ci int dataval = (cmd & (1 << i)) ? EE_DATA_WRITE : 0; 16858c2ecf20Sopenharmony_ci writeb (EE_ENB | dataval, ee_addr); 16868c2ecf20Sopenharmony_ci eeprom_delay (); 16878c2ecf20Sopenharmony_ci writeb (EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); 16888c2ecf20Sopenharmony_ci eeprom_delay (); 16898c2ecf20Sopenharmony_ci } 16908c2ecf20Sopenharmony_ci writeb (EE_ENB, ee_addr); 16918c2ecf20Sopenharmony_ci eeprom_delay (); 16928c2ecf20Sopenharmony_ci} 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_cistatic void eeprom_cmd_end(void __iomem *ee_addr) 16958c2ecf20Sopenharmony_ci{ 16968c2ecf20Sopenharmony_ci writeb(0, ee_addr); 16978c2ecf20Sopenharmony_ci eeprom_delay (); 16988c2ecf20Sopenharmony_ci} 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_cistatic void eeprom_extend_cmd(void __iomem *ee_addr, int extend_cmd, 17018c2ecf20Sopenharmony_ci int addr_len) 17028c2ecf20Sopenharmony_ci{ 17038c2ecf20Sopenharmony_ci int cmd = (EE_EXTEND_CMD << addr_len) | (extend_cmd << (addr_len - 2)); 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci eeprom_cmd_start(ee_addr); 17068c2ecf20Sopenharmony_ci eeprom_cmd(ee_addr, cmd, 3 + addr_len); 17078c2ecf20Sopenharmony_ci eeprom_cmd_end(ee_addr); 17088c2ecf20Sopenharmony_ci} 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_cistatic u16 read_eeprom (void __iomem *ioaddr, int location, int addr_len) 17118c2ecf20Sopenharmony_ci{ 17128c2ecf20Sopenharmony_ci int i; 17138c2ecf20Sopenharmony_ci u16 retval = 0; 17148c2ecf20Sopenharmony_ci void __iomem *ee_addr = ioaddr + Cfg9346; 17158c2ecf20Sopenharmony_ci int read_cmd = location | (EE_READ_CMD << addr_len); 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci eeprom_cmd_start(ee_addr); 17188c2ecf20Sopenharmony_ci eeprom_cmd(ee_addr, read_cmd, 3 + addr_len); 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci for (i = 16; i > 0; i--) { 17218c2ecf20Sopenharmony_ci writeb (EE_ENB | EE_SHIFT_CLK, ee_addr); 17228c2ecf20Sopenharmony_ci eeprom_delay (); 17238c2ecf20Sopenharmony_ci retval = 17248c2ecf20Sopenharmony_ci (retval << 1) | ((readb (ee_addr) & EE_DATA_READ) ? 1 : 17258c2ecf20Sopenharmony_ci 0); 17268c2ecf20Sopenharmony_ci writeb (EE_ENB, ee_addr); 17278c2ecf20Sopenharmony_ci eeprom_delay (); 17288c2ecf20Sopenharmony_ci } 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci eeprom_cmd_end(ee_addr); 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci return retval; 17338c2ecf20Sopenharmony_ci} 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_cistatic void write_eeprom(void __iomem *ioaddr, int location, u16 val, 17368c2ecf20Sopenharmony_ci int addr_len) 17378c2ecf20Sopenharmony_ci{ 17388c2ecf20Sopenharmony_ci int i; 17398c2ecf20Sopenharmony_ci void __iomem *ee_addr = ioaddr + Cfg9346; 17408c2ecf20Sopenharmony_ci int write_cmd = location | (EE_WRITE_CMD << addr_len); 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci eeprom_extend_cmd(ee_addr, EE_EWEN_ADDR, addr_len); 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci eeprom_cmd_start(ee_addr); 17458c2ecf20Sopenharmony_ci eeprom_cmd(ee_addr, write_cmd, 3 + addr_len); 17468c2ecf20Sopenharmony_ci eeprom_cmd(ee_addr, val, 16); 17478c2ecf20Sopenharmony_ci eeprom_cmd_end(ee_addr); 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci eeprom_cmd_start(ee_addr); 17508c2ecf20Sopenharmony_ci for (i = 0; i < 20000; i++) 17518c2ecf20Sopenharmony_ci if (readb(ee_addr) & EE_DATA_READ) 17528c2ecf20Sopenharmony_ci break; 17538c2ecf20Sopenharmony_ci eeprom_cmd_end(ee_addr); 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci eeprom_extend_cmd(ee_addr, EE_EWDS_ADDR, addr_len); 17568c2ecf20Sopenharmony_ci} 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_cistatic int cp_get_eeprom_len(struct net_device *dev) 17598c2ecf20Sopenharmony_ci{ 17608c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 17618c2ecf20Sopenharmony_ci int size; 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci spin_lock_irq(&cp->lock); 17648c2ecf20Sopenharmony_ci size = read_eeprom(cp->regs, 0, 8) == 0x8129 ? 256 : 128; 17658c2ecf20Sopenharmony_ci spin_unlock_irq(&cp->lock); 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci return size; 17688c2ecf20Sopenharmony_ci} 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_cistatic int cp_get_eeprom(struct net_device *dev, 17718c2ecf20Sopenharmony_ci struct ethtool_eeprom *eeprom, u8 *data) 17728c2ecf20Sopenharmony_ci{ 17738c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 17748c2ecf20Sopenharmony_ci unsigned int addr_len; 17758c2ecf20Sopenharmony_ci u16 val; 17768c2ecf20Sopenharmony_ci u32 offset = eeprom->offset >> 1; 17778c2ecf20Sopenharmony_ci u32 len = eeprom->len; 17788c2ecf20Sopenharmony_ci u32 i = 0; 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci eeprom->magic = CP_EEPROM_MAGIC; 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ci spin_lock_irq(&cp->lock); 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci addr_len = read_eeprom(cp->regs, 0, 8) == 0x8129 ? 8 : 6; 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci if (eeprom->offset & 1) { 17878c2ecf20Sopenharmony_ci val = read_eeprom(cp->regs, offset, addr_len); 17888c2ecf20Sopenharmony_ci data[i++] = (u8)(val >> 8); 17898c2ecf20Sopenharmony_ci offset++; 17908c2ecf20Sopenharmony_ci } 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci while (i < len - 1) { 17938c2ecf20Sopenharmony_ci val = read_eeprom(cp->regs, offset, addr_len); 17948c2ecf20Sopenharmony_ci data[i++] = (u8)val; 17958c2ecf20Sopenharmony_ci data[i++] = (u8)(val >> 8); 17968c2ecf20Sopenharmony_ci offset++; 17978c2ecf20Sopenharmony_ci } 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci if (i < len) { 18008c2ecf20Sopenharmony_ci val = read_eeprom(cp->regs, offset, addr_len); 18018c2ecf20Sopenharmony_ci data[i] = (u8)val; 18028c2ecf20Sopenharmony_ci } 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci spin_unlock_irq(&cp->lock); 18058c2ecf20Sopenharmony_ci return 0; 18068c2ecf20Sopenharmony_ci} 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_cistatic int cp_set_eeprom(struct net_device *dev, 18098c2ecf20Sopenharmony_ci struct ethtool_eeprom *eeprom, u8 *data) 18108c2ecf20Sopenharmony_ci{ 18118c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 18128c2ecf20Sopenharmony_ci unsigned int addr_len; 18138c2ecf20Sopenharmony_ci u16 val; 18148c2ecf20Sopenharmony_ci u32 offset = eeprom->offset >> 1; 18158c2ecf20Sopenharmony_ci u32 len = eeprom->len; 18168c2ecf20Sopenharmony_ci u32 i = 0; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci if (eeprom->magic != CP_EEPROM_MAGIC) 18198c2ecf20Sopenharmony_ci return -EINVAL; 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci spin_lock_irq(&cp->lock); 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci addr_len = read_eeprom(cp->regs, 0, 8) == 0x8129 ? 8 : 6; 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci if (eeprom->offset & 1) { 18268c2ecf20Sopenharmony_ci val = read_eeprom(cp->regs, offset, addr_len) & 0xff; 18278c2ecf20Sopenharmony_ci val |= (u16)data[i++] << 8; 18288c2ecf20Sopenharmony_ci write_eeprom(cp->regs, offset, val, addr_len); 18298c2ecf20Sopenharmony_ci offset++; 18308c2ecf20Sopenharmony_ci } 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci while (i < len - 1) { 18338c2ecf20Sopenharmony_ci val = (u16)data[i++]; 18348c2ecf20Sopenharmony_ci val |= (u16)data[i++] << 8; 18358c2ecf20Sopenharmony_ci write_eeprom(cp->regs, offset, val, addr_len); 18368c2ecf20Sopenharmony_ci offset++; 18378c2ecf20Sopenharmony_ci } 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci if (i < len) { 18408c2ecf20Sopenharmony_ci val = read_eeprom(cp->regs, offset, addr_len) & 0xff00; 18418c2ecf20Sopenharmony_ci val |= (u16)data[i]; 18428c2ecf20Sopenharmony_ci write_eeprom(cp->regs, offset, val, addr_len); 18438c2ecf20Sopenharmony_ci } 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci spin_unlock_irq(&cp->lock); 18468c2ecf20Sopenharmony_ci return 0; 18478c2ecf20Sopenharmony_ci} 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci/* Put the board into D3cold state and wait for WakeUp signal */ 18508c2ecf20Sopenharmony_cistatic void cp_set_d3_state (struct cp_private *cp) 18518c2ecf20Sopenharmony_ci{ 18528c2ecf20Sopenharmony_ci pci_enable_wake(cp->pdev, PCI_D0, 1); /* Enable PME# generation */ 18538c2ecf20Sopenharmony_ci pci_set_power_state (cp->pdev, PCI_D3hot); 18548c2ecf20Sopenharmony_ci} 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_cistatic netdev_features_t cp_features_check(struct sk_buff *skb, 18578c2ecf20Sopenharmony_ci struct net_device *dev, 18588c2ecf20Sopenharmony_ci netdev_features_t features) 18598c2ecf20Sopenharmony_ci{ 18608c2ecf20Sopenharmony_ci if (skb_shinfo(skb)->gso_size > MSSMask) 18618c2ecf20Sopenharmony_ci features &= ~NETIF_F_TSO; 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci return vlan_features_check(skb, features); 18648c2ecf20Sopenharmony_ci} 18658c2ecf20Sopenharmony_cistatic const struct net_device_ops cp_netdev_ops = { 18668c2ecf20Sopenharmony_ci .ndo_open = cp_open, 18678c2ecf20Sopenharmony_ci .ndo_stop = cp_close, 18688c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 18698c2ecf20Sopenharmony_ci .ndo_set_mac_address = cp_set_mac_address, 18708c2ecf20Sopenharmony_ci .ndo_set_rx_mode = cp_set_rx_mode, 18718c2ecf20Sopenharmony_ci .ndo_get_stats = cp_get_stats, 18728c2ecf20Sopenharmony_ci .ndo_do_ioctl = cp_ioctl, 18738c2ecf20Sopenharmony_ci .ndo_start_xmit = cp_start_xmit, 18748c2ecf20Sopenharmony_ci .ndo_tx_timeout = cp_tx_timeout, 18758c2ecf20Sopenharmony_ci .ndo_set_features = cp_set_features, 18768c2ecf20Sopenharmony_ci .ndo_change_mtu = cp_change_mtu, 18778c2ecf20Sopenharmony_ci .ndo_features_check = cp_features_check, 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 18808c2ecf20Sopenharmony_ci .ndo_poll_controller = cp_poll_controller, 18818c2ecf20Sopenharmony_ci#endif 18828c2ecf20Sopenharmony_ci}; 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_cistatic int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) 18858c2ecf20Sopenharmony_ci{ 18868c2ecf20Sopenharmony_ci struct net_device *dev; 18878c2ecf20Sopenharmony_ci struct cp_private *cp; 18888c2ecf20Sopenharmony_ci int rc; 18898c2ecf20Sopenharmony_ci void __iomem *regs; 18908c2ecf20Sopenharmony_ci resource_size_t pciaddr; 18918c2ecf20Sopenharmony_ci unsigned int addr_len, i, pci_using_dac; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci pr_info_once("%s", version); 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci if (pdev->vendor == PCI_VENDOR_ID_REALTEK && 18968c2ecf20Sopenharmony_ci pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pdev->revision < 0x20) { 18978c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 18988c2ecf20Sopenharmony_ci "This (id %04x:%04x rev %02x) is not an 8139C+ compatible chip, use 8139too\n", 18998c2ecf20Sopenharmony_ci pdev->vendor, pdev->device, pdev->revision); 19008c2ecf20Sopenharmony_ci return -ENODEV; 19018c2ecf20Sopenharmony_ci } 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(struct cp_private)); 19048c2ecf20Sopenharmony_ci if (!dev) 19058c2ecf20Sopenharmony_ci return -ENOMEM; 19068c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci cp = netdev_priv(dev); 19098c2ecf20Sopenharmony_ci cp->pdev = pdev; 19108c2ecf20Sopenharmony_ci cp->dev = dev; 19118c2ecf20Sopenharmony_ci cp->msg_enable = (debug < 0 ? CP_DEF_MSG_ENABLE : debug); 19128c2ecf20Sopenharmony_ci spin_lock_init (&cp->lock); 19138c2ecf20Sopenharmony_ci cp->mii_if.dev = dev; 19148c2ecf20Sopenharmony_ci cp->mii_if.mdio_read = mdio_read; 19158c2ecf20Sopenharmony_ci cp->mii_if.mdio_write = mdio_write; 19168c2ecf20Sopenharmony_ci cp->mii_if.phy_id = CP_INTERNAL_PHY; 19178c2ecf20Sopenharmony_ci cp->mii_if.phy_id_mask = 0x1f; 19188c2ecf20Sopenharmony_ci cp->mii_if.reg_num_mask = 0x1f; 19198c2ecf20Sopenharmony_ci cp_set_rxbufsize(cp); 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci rc = pci_enable_device(pdev); 19228c2ecf20Sopenharmony_ci if (rc) 19238c2ecf20Sopenharmony_ci goto err_out_free; 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_ci rc = pci_set_mwi(pdev); 19268c2ecf20Sopenharmony_ci if (rc) 19278c2ecf20Sopenharmony_ci goto err_out_disable; 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci rc = pci_request_regions(pdev, DRV_NAME); 19308c2ecf20Sopenharmony_ci if (rc) 19318c2ecf20Sopenharmony_ci goto err_out_mwi; 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci pciaddr = pci_resource_start(pdev, 1); 19348c2ecf20Sopenharmony_ci if (!pciaddr) { 19358c2ecf20Sopenharmony_ci rc = -EIO; 19368c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no MMIO resource\n"); 19378c2ecf20Sopenharmony_ci goto err_out_res; 19388c2ecf20Sopenharmony_ci } 19398c2ecf20Sopenharmony_ci if (pci_resource_len(pdev, 1) < CP_REGS_SIZE) { 19408c2ecf20Sopenharmony_ci rc = -EIO; 19418c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "MMIO resource (%llx) too small\n", 19428c2ecf20Sopenharmony_ci (unsigned long long)pci_resource_len(pdev, 1)); 19438c2ecf20Sopenharmony_ci goto err_out_res; 19448c2ecf20Sopenharmony_ci } 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci /* Configure DMA attributes. */ 19478c2ecf20Sopenharmony_ci if ((sizeof(dma_addr_t) > 4) && 19488c2ecf20Sopenharmony_ci !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) && 19498c2ecf20Sopenharmony_ci !pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) { 19508c2ecf20Sopenharmony_ci pci_using_dac = 1; 19518c2ecf20Sopenharmony_ci } else { 19528c2ecf20Sopenharmony_ci pci_using_dac = 0; 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); 19558c2ecf20Sopenharmony_ci if (rc) { 19568c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 19578c2ecf20Sopenharmony_ci "No usable DMA configuration, aborting\n"); 19588c2ecf20Sopenharmony_ci goto err_out_res; 19598c2ecf20Sopenharmony_ci } 19608c2ecf20Sopenharmony_ci rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); 19618c2ecf20Sopenharmony_ci if (rc) { 19628c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 19638c2ecf20Sopenharmony_ci "No usable consistent DMA configuration, aborting\n"); 19648c2ecf20Sopenharmony_ci goto err_out_res; 19658c2ecf20Sopenharmony_ci } 19668c2ecf20Sopenharmony_ci } 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci cp->cpcmd = (pci_using_dac ? PCIDAC : 0) | 19698c2ecf20Sopenharmony_ci PCIMulRW | RxChkSum | CpRxOn | CpTxOn; 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci dev->features |= NETIF_F_RXCSUM; 19728c2ecf20Sopenharmony_ci dev->hw_features |= NETIF_F_RXCSUM; 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci regs = ioremap(pciaddr, CP_REGS_SIZE); 19758c2ecf20Sopenharmony_ci if (!regs) { 19768c2ecf20Sopenharmony_ci rc = -EIO; 19778c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot map PCI MMIO (%Lx@%Lx)\n", 19788c2ecf20Sopenharmony_ci (unsigned long long)pci_resource_len(pdev, 1), 19798c2ecf20Sopenharmony_ci (unsigned long long)pciaddr); 19808c2ecf20Sopenharmony_ci goto err_out_res; 19818c2ecf20Sopenharmony_ci } 19828c2ecf20Sopenharmony_ci cp->regs = regs; 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_ci cp_stop_hw(cp); 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci /* read MAC address from EEPROM */ 19878c2ecf20Sopenharmony_ci addr_len = read_eeprom (regs, 0, 8) == 0x8129 ? 8 : 6; 19888c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) 19898c2ecf20Sopenharmony_ci ((__le16 *) (dev->dev_addr))[i] = 19908c2ecf20Sopenharmony_ci cpu_to_le16(read_eeprom (regs, i + 7, addr_len)); 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci dev->netdev_ops = &cp_netdev_ops; 19938c2ecf20Sopenharmony_ci netif_napi_add(dev, &cp->napi, cp_rx_poll, 16); 19948c2ecf20Sopenharmony_ci dev->ethtool_ops = &cp_ethtool_ops; 19958c2ecf20Sopenharmony_ci dev->watchdog_timeo = TX_TIMEOUT; 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | 19988c2ecf20Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci if (pci_using_dac) 20018c2ecf20Sopenharmony_ci dev->features |= NETIF_F_HIGHDMA; 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci dev->hw_features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | 20048c2ecf20Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; 20058c2ecf20Sopenharmony_ci dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | 20068c2ecf20Sopenharmony_ci NETIF_F_HIGHDMA; 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci /* MTU range: 60 - 4096 */ 20098c2ecf20Sopenharmony_ci dev->min_mtu = CP_MIN_MTU; 20108c2ecf20Sopenharmony_ci dev->max_mtu = CP_MAX_MTU; 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci rc = register_netdev(dev); 20138c2ecf20Sopenharmony_ci if (rc) 20148c2ecf20Sopenharmony_ci goto err_out_iomap; 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci netdev_info(dev, "RTL-8139C+ at 0x%p, %pM, IRQ %d\n", 20178c2ecf20Sopenharmony_ci regs, dev->dev_addr, pdev->irq); 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, dev); 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci /* enable busmastering and memory-write-invalidate */ 20228c2ecf20Sopenharmony_ci pci_set_master(pdev); 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci if (cp->wol_enabled) 20258c2ecf20Sopenharmony_ci cp_set_d3_state (cp); 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci return 0; 20288c2ecf20Sopenharmony_ci 20298c2ecf20Sopenharmony_cierr_out_iomap: 20308c2ecf20Sopenharmony_ci iounmap(regs); 20318c2ecf20Sopenharmony_cierr_out_res: 20328c2ecf20Sopenharmony_ci pci_release_regions(pdev); 20338c2ecf20Sopenharmony_cierr_out_mwi: 20348c2ecf20Sopenharmony_ci pci_clear_mwi(pdev); 20358c2ecf20Sopenharmony_cierr_out_disable: 20368c2ecf20Sopenharmony_ci pci_disable_device(pdev); 20378c2ecf20Sopenharmony_cierr_out_free: 20388c2ecf20Sopenharmony_ci free_netdev(dev); 20398c2ecf20Sopenharmony_ci return rc; 20408c2ecf20Sopenharmony_ci} 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_cistatic void cp_remove_one (struct pci_dev *pdev) 20438c2ecf20Sopenharmony_ci{ 20448c2ecf20Sopenharmony_ci struct net_device *dev = pci_get_drvdata(pdev); 20458c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci unregister_netdev(dev); 20488c2ecf20Sopenharmony_ci iounmap(cp->regs); 20498c2ecf20Sopenharmony_ci if (cp->wol_enabled) 20508c2ecf20Sopenharmony_ci pci_set_power_state (pdev, PCI_D0); 20518c2ecf20Sopenharmony_ci pci_release_regions(pdev); 20528c2ecf20Sopenharmony_ci pci_clear_mwi(pdev); 20538c2ecf20Sopenharmony_ci pci_disable_device(pdev); 20548c2ecf20Sopenharmony_ci free_netdev(dev); 20558c2ecf20Sopenharmony_ci} 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_cistatic int __maybe_unused cp_suspend(struct device *device) 20588c2ecf20Sopenharmony_ci{ 20598c2ecf20Sopenharmony_ci struct net_device *dev = dev_get_drvdata(device); 20608c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 20618c2ecf20Sopenharmony_ci unsigned long flags; 20628c2ecf20Sopenharmony_ci 20638c2ecf20Sopenharmony_ci if (!netif_running(dev)) 20648c2ecf20Sopenharmony_ci return 0; 20658c2ecf20Sopenharmony_ci 20668c2ecf20Sopenharmony_ci netif_device_detach (dev); 20678c2ecf20Sopenharmony_ci netif_stop_queue (dev); 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_ci spin_lock_irqsave (&cp->lock, flags); 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci /* Disable Rx and Tx */ 20728c2ecf20Sopenharmony_ci cpw16 (IntrMask, 0); 20738c2ecf20Sopenharmony_ci cpw8 (Cmd, cpr8 (Cmd) & (~RxOn | ~TxOn)); 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&cp->lock, flags); 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci device_set_wakeup_enable(device, cp->wol_enabled); 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci return 0; 20808c2ecf20Sopenharmony_ci} 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_cistatic int __maybe_unused cp_resume(struct device *device) 20838c2ecf20Sopenharmony_ci{ 20848c2ecf20Sopenharmony_ci struct net_device *dev = dev_get_drvdata(device); 20858c2ecf20Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 20868c2ecf20Sopenharmony_ci unsigned long flags; 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_ci if (!netif_running(dev)) 20898c2ecf20Sopenharmony_ci return 0; 20908c2ecf20Sopenharmony_ci 20918c2ecf20Sopenharmony_ci netif_device_attach (dev); 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci /* FIXME: sh*t may happen if the Rx ring buffer is depleted */ 20948c2ecf20Sopenharmony_ci cp_init_rings_index (cp); 20958c2ecf20Sopenharmony_ci cp_init_hw (cp); 20968c2ecf20Sopenharmony_ci cp_enable_irq(cp); 20978c2ecf20Sopenharmony_ci netif_start_queue (dev); 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci spin_lock_irqsave (&cp->lock, flags); 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci mii_check_media(&cp->mii_if, netif_msg_link(cp), false); 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci spin_unlock_irqrestore (&cp->lock, flags); 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_ci return 0; 21068c2ecf20Sopenharmony_ci} 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_cistatic const struct pci_device_id cp_pci_tbl[] = { 21098c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, PCI_DEVICE_ID_REALTEK_8139), }, 21108c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_TTTECH, PCI_DEVICE_ID_TTTECH_MC322), }, 21118c2ecf20Sopenharmony_ci { }, 21128c2ecf20Sopenharmony_ci}; 21138c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cp_pci_tbl); 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(cp_pm_ops, cp_suspend, cp_resume); 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_cistatic struct pci_driver cp_driver = { 21188c2ecf20Sopenharmony_ci .name = DRV_NAME, 21198c2ecf20Sopenharmony_ci .id_table = cp_pci_tbl, 21208c2ecf20Sopenharmony_ci .probe = cp_init_one, 21218c2ecf20Sopenharmony_ci .remove = cp_remove_one, 21228c2ecf20Sopenharmony_ci .driver.pm = &cp_pm_ops, 21238c2ecf20Sopenharmony_ci}; 21248c2ecf20Sopenharmony_ci 21258c2ecf20Sopenharmony_cimodule_pci_driver(cp_driver); 2126