162306a36Sopenharmony_ci/* 8139cp.c: A Linux PCI Ethernet driver for the RealTek 8139C+ chips. */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci Copyright 2001-2004 Jeff Garzik <jgarzik@pobox.com> 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com) [tg3.c] 662306a36Sopenharmony_ci Copyright (C) 2000, 2001 David S. Miller (davem@redhat.com) [sungem.c] 762306a36Sopenharmony_ci Copyright 2001 Manfred Spraul [natsemi.c] 862306a36Sopenharmony_ci Copyright 1999-2001 by Donald Becker. [natsemi.c] 962306a36Sopenharmony_ci Written 1997-2001 by Donald Becker. [8139too.c] 1062306a36Sopenharmony_ci Copyright 1998-2001 by Jes Sorensen, <jes@trained-monkey.org>. [acenic.c] 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci This software may be used and distributed according to the terms of 1362306a36Sopenharmony_ci the GNU General Public License (GPL), incorporated herein by reference. 1462306a36Sopenharmony_ci Drivers based on or derived from this code fall under the GPL and must 1562306a36Sopenharmony_ci retain the authorship, copyright and license notice. This file is not 1662306a36Sopenharmony_ci a complete program and may only be used when the entire operating 1762306a36Sopenharmony_ci system is licensed under the GPL. 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci See the file COPYING in this distribution for more information. 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci Contributors: 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci Wake-on-LAN support - Felipe Damasio <felipewd@terra.com.br> 2462306a36Sopenharmony_ci PCI suspend/resume - Felipe Damasio <felipewd@terra.com.br> 2562306a36Sopenharmony_ci LinkChg interrupt - Felipe Damasio <felipewd@terra.com.br> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci TODO: 2862306a36Sopenharmony_ci * Test Tx checksumming thoroughly 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci Low priority TODO: 3162306a36Sopenharmony_ci * Complete reset on PciErr 3262306a36Sopenharmony_ci * Consider Rx interrupt mitigation using TimerIntr 3362306a36Sopenharmony_ci * Investigate using skb->priority with h/w VLAN priority 3462306a36Sopenharmony_ci * Investigate using High Priority Tx Queue with skb->priority 3562306a36Sopenharmony_ci * Adjust Rx FIFO threshold and Max Rx DMA burst on Rx FIFO error 3662306a36Sopenharmony_ci * Adjust Tx FIFO threshold and Max Tx DMA burst on Tx FIFO error 3762306a36Sopenharmony_ci * Implement Tx software interrupt mitigation via 3862306a36Sopenharmony_ci Tx descriptor bit 3962306a36Sopenharmony_ci * The real minimum of CP_MIN_MTU is 4 bytes. However, 4062306a36Sopenharmony_ci for this to be supported, one must(?) turn on packet padding. 4162306a36Sopenharmony_ci * Support external MII transceivers (patch available) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci NOTES: 4462306a36Sopenharmony_ci * TX checksumming is considered experimental. It is off by 4562306a36Sopenharmony_ci default, use ethtool to turn it on. 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define DRV_NAME "8139cp" 5262306a36Sopenharmony_ci#define DRV_VERSION "1.3" 5362306a36Sopenharmony_ci#define DRV_RELDATE "Mar 22, 2004" 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#include <linux/module.h> 5762306a36Sopenharmony_ci#include <linux/moduleparam.h> 5862306a36Sopenharmony_ci#include <linux/kernel.h> 5962306a36Sopenharmony_ci#include <linux/compiler.h> 6062306a36Sopenharmony_ci#include <linux/netdevice.h> 6162306a36Sopenharmony_ci#include <linux/etherdevice.h> 6262306a36Sopenharmony_ci#include <linux/init.h> 6362306a36Sopenharmony_ci#include <linux/interrupt.h> 6462306a36Sopenharmony_ci#include <linux/pci.h> 6562306a36Sopenharmony_ci#include <linux/dma-mapping.h> 6662306a36Sopenharmony_ci#include <linux/delay.h> 6762306a36Sopenharmony_ci#include <linux/ethtool.h> 6862306a36Sopenharmony_ci#include <linux/gfp.h> 6962306a36Sopenharmony_ci#include <linux/mii.h> 7062306a36Sopenharmony_ci#include <linux/if_vlan.h> 7162306a36Sopenharmony_ci#include <linux/crc32.h> 7262306a36Sopenharmony_ci#include <linux/in.h> 7362306a36Sopenharmony_ci#include <linux/ip.h> 7462306a36Sopenharmony_ci#include <linux/tcp.h> 7562306a36Sopenharmony_ci#include <linux/udp.h> 7662306a36Sopenharmony_ci#include <linux/cache.h> 7762306a36Sopenharmony_ci#include <asm/io.h> 7862306a36Sopenharmony_ci#include <asm/irq.h> 7962306a36Sopenharmony_ci#include <linux/uaccess.h> 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* These identify the driver base version and may not be removed. */ 8262306a36Sopenharmony_cistatic char version[] = 8362306a36Sopenharmony_ciDRV_NAME ": 10/100 PCI Ethernet driver v" DRV_VERSION " (" DRV_RELDATE ")\n"; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ciMODULE_AUTHOR("Jeff Garzik <jgarzik@pobox.com>"); 8662306a36Sopenharmony_ciMODULE_DESCRIPTION("RealTek RTL-8139C+ series 10/100 PCI Ethernet driver"); 8762306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 8862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic int debug = -1; 9162306a36Sopenharmony_cimodule_param(debug, int, 0); 9262306a36Sopenharmony_ciMODULE_PARM_DESC (debug, "8139cp: bitmapped message enable number"); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). 9562306a36Sopenharmony_ci The RTL chips use a 64 element hash table based on the Ethernet CRC. */ 9662306a36Sopenharmony_cistatic int multicast_filter_limit = 32; 9762306a36Sopenharmony_cimodule_param(multicast_filter_limit, int, 0); 9862306a36Sopenharmony_ciMODULE_PARM_DESC (multicast_filter_limit, "8139cp: maximum number of filtered multicast addresses"); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define CP_DEF_MSG_ENABLE (NETIF_MSG_DRV | \ 10162306a36Sopenharmony_ci NETIF_MSG_PROBE | \ 10262306a36Sopenharmony_ci NETIF_MSG_LINK) 10362306a36Sopenharmony_ci#define CP_NUM_STATS 14 /* struct cp_dma_stats, plus one */ 10462306a36Sopenharmony_ci#define CP_STATS_SIZE 64 /* size in bytes of DMA stats block */ 10562306a36Sopenharmony_ci#define CP_REGS_SIZE (0xff + 1) 10662306a36Sopenharmony_ci#define CP_REGS_VER 1 /* version 1 */ 10762306a36Sopenharmony_ci#define CP_RX_RING_SIZE 64 10862306a36Sopenharmony_ci#define CP_TX_RING_SIZE 64 10962306a36Sopenharmony_ci#define CP_RING_BYTES \ 11062306a36Sopenharmony_ci ((sizeof(struct cp_desc) * CP_RX_RING_SIZE) + \ 11162306a36Sopenharmony_ci (sizeof(struct cp_desc) * CP_TX_RING_SIZE) + \ 11262306a36Sopenharmony_ci CP_STATS_SIZE) 11362306a36Sopenharmony_ci#define NEXT_TX(N) (((N) + 1) & (CP_TX_RING_SIZE - 1)) 11462306a36Sopenharmony_ci#define NEXT_RX(N) (((N) + 1) & (CP_RX_RING_SIZE - 1)) 11562306a36Sopenharmony_ci#define TX_BUFFS_AVAIL(CP) \ 11662306a36Sopenharmony_ci (((CP)->tx_tail <= (CP)->tx_head) ? \ 11762306a36Sopenharmony_ci (CP)->tx_tail + (CP_TX_RING_SIZE - 1) - (CP)->tx_head : \ 11862306a36Sopenharmony_ci (CP)->tx_tail - (CP)->tx_head - 1) 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ 12162306a36Sopenharmony_ci#define CP_INTERNAL_PHY 32 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. */ 12462306a36Sopenharmony_ci#define RX_FIFO_THRESH 5 /* Rx buffer level before first PCI xfer. */ 12562306a36Sopenharmony_ci#define RX_DMA_BURST 4 /* Maximum PCI burst, '4' is 256 */ 12662306a36Sopenharmony_ci#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ 12762306a36Sopenharmony_ci#define TX_EARLY_THRESH 256 /* Early Tx threshold, in bytes */ 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* Time in jiffies before concluding the transmitter is hung. */ 13062306a36Sopenharmony_ci#define TX_TIMEOUT (6*HZ) 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* hardware minimum and maximum for a single frame's data payload */ 13362306a36Sopenharmony_ci#define CP_MIN_MTU 60 /* TODO: allow lower, but pad */ 13462306a36Sopenharmony_ci#define CP_MAX_MTU 4096 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cienum { 13762306a36Sopenharmony_ci /* NIC register offsets */ 13862306a36Sopenharmony_ci MAC0 = 0x00, /* Ethernet hardware address. */ 13962306a36Sopenharmony_ci MAR0 = 0x08, /* Multicast filter. */ 14062306a36Sopenharmony_ci StatsAddr = 0x10, /* 64-bit start addr of 64-byte DMA stats blk */ 14162306a36Sopenharmony_ci TxRingAddr = 0x20, /* 64-bit start addr of Tx ring */ 14262306a36Sopenharmony_ci HiTxRingAddr = 0x28, /* 64-bit start addr of high priority Tx ring */ 14362306a36Sopenharmony_ci Cmd = 0x37, /* Command register */ 14462306a36Sopenharmony_ci IntrMask = 0x3C, /* Interrupt mask */ 14562306a36Sopenharmony_ci IntrStatus = 0x3E, /* Interrupt status */ 14662306a36Sopenharmony_ci TxConfig = 0x40, /* Tx configuration */ 14762306a36Sopenharmony_ci ChipVersion = 0x43, /* 8-bit chip version, inside TxConfig */ 14862306a36Sopenharmony_ci RxConfig = 0x44, /* Rx configuration */ 14962306a36Sopenharmony_ci RxMissed = 0x4C, /* 24 bits valid, write clears */ 15062306a36Sopenharmony_ci Cfg9346 = 0x50, /* EEPROM select/control; Cfg reg [un]lock */ 15162306a36Sopenharmony_ci Config1 = 0x52, /* Config1 */ 15262306a36Sopenharmony_ci Config3 = 0x59, /* Config3 */ 15362306a36Sopenharmony_ci Config4 = 0x5A, /* Config4 */ 15462306a36Sopenharmony_ci MultiIntr = 0x5C, /* Multiple interrupt select */ 15562306a36Sopenharmony_ci BasicModeCtrl = 0x62, /* MII BMCR */ 15662306a36Sopenharmony_ci BasicModeStatus = 0x64, /* MII BMSR */ 15762306a36Sopenharmony_ci NWayAdvert = 0x66, /* MII ADVERTISE */ 15862306a36Sopenharmony_ci NWayLPAR = 0x68, /* MII LPA */ 15962306a36Sopenharmony_ci NWayExpansion = 0x6A, /* MII Expansion */ 16062306a36Sopenharmony_ci TxDmaOkLowDesc = 0x82, /* Low 16 bit address of a Tx descriptor. */ 16162306a36Sopenharmony_ci Config5 = 0xD8, /* Config5 */ 16262306a36Sopenharmony_ci TxPoll = 0xD9, /* Tell chip to check Tx descriptors for work */ 16362306a36Sopenharmony_ci RxMaxSize = 0xDA, /* Max size of an Rx packet (8169 only) */ 16462306a36Sopenharmony_ci CpCmd = 0xE0, /* C+ Command register (C+ mode only) */ 16562306a36Sopenharmony_ci IntrMitigate = 0xE2, /* rx/tx interrupt mitigation control */ 16662306a36Sopenharmony_ci RxRingAddr = 0xE4, /* 64-bit start addr of Rx ring */ 16762306a36Sopenharmony_ci TxThresh = 0xEC, /* Early Tx threshold */ 16862306a36Sopenharmony_ci OldRxBufAddr = 0x30, /* DMA address of Rx ring buffer (C mode) */ 16962306a36Sopenharmony_ci OldTSD0 = 0x10, /* DMA address of first Tx desc (C mode) */ 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* Tx and Rx status descriptors */ 17262306a36Sopenharmony_ci DescOwn = (1 << 31), /* Descriptor is owned by NIC */ 17362306a36Sopenharmony_ci RingEnd = (1 << 30), /* End of descriptor ring */ 17462306a36Sopenharmony_ci FirstFrag = (1 << 29), /* First segment of a packet */ 17562306a36Sopenharmony_ci LastFrag = (1 << 28), /* Final segment of a packet */ 17662306a36Sopenharmony_ci LargeSend = (1 << 27), /* TCP Large Send Offload (TSO) */ 17762306a36Sopenharmony_ci MSSShift = 16, /* MSS value position */ 17862306a36Sopenharmony_ci MSSMask = 0x7ff, /* MSS value: 11 bits */ 17962306a36Sopenharmony_ci TxError = (1 << 23), /* Tx error summary */ 18062306a36Sopenharmony_ci RxError = (1 << 20), /* Rx error summary */ 18162306a36Sopenharmony_ci IPCS = (1 << 18), /* Calculate IP checksum */ 18262306a36Sopenharmony_ci UDPCS = (1 << 17), /* Calculate UDP/IP checksum */ 18362306a36Sopenharmony_ci TCPCS = (1 << 16), /* Calculate TCP/IP checksum */ 18462306a36Sopenharmony_ci TxVlanTag = (1 << 17), /* Add VLAN tag */ 18562306a36Sopenharmony_ci RxVlanTagged = (1 << 16), /* Rx VLAN tag available */ 18662306a36Sopenharmony_ci IPFail = (1 << 15), /* IP checksum failed */ 18762306a36Sopenharmony_ci UDPFail = (1 << 14), /* UDP/IP checksum failed */ 18862306a36Sopenharmony_ci TCPFail = (1 << 13), /* TCP/IP checksum failed */ 18962306a36Sopenharmony_ci NormalTxPoll = (1 << 6), /* One or more normal Tx packets to send */ 19062306a36Sopenharmony_ci PID1 = (1 << 17), /* 2 protocol id bits: 0==non-IP, */ 19162306a36Sopenharmony_ci PID0 = (1 << 16), /* 1==UDP/IP, 2==TCP/IP, 3==IP */ 19262306a36Sopenharmony_ci RxProtoTCP = 1, 19362306a36Sopenharmony_ci RxProtoUDP = 2, 19462306a36Sopenharmony_ci RxProtoIP = 3, 19562306a36Sopenharmony_ci TxFIFOUnder = (1 << 25), /* Tx FIFO underrun */ 19662306a36Sopenharmony_ci TxOWC = (1 << 22), /* Tx Out-of-window collision */ 19762306a36Sopenharmony_ci TxLinkFail = (1 << 21), /* Link failed during Tx of packet */ 19862306a36Sopenharmony_ci TxMaxCol = (1 << 20), /* Tx aborted due to excessive collisions */ 19962306a36Sopenharmony_ci TxColCntShift = 16, /* Shift, to get 4-bit Tx collision cnt */ 20062306a36Sopenharmony_ci TxColCntMask = 0x01 | 0x02 | 0x04 | 0x08, /* 4-bit collision count */ 20162306a36Sopenharmony_ci RxErrFrame = (1 << 27), /* Rx frame alignment error */ 20262306a36Sopenharmony_ci RxMcast = (1 << 26), /* Rx multicast packet rcv'd */ 20362306a36Sopenharmony_ci RxErrCRC = (1 << 18), /* Rx CRC error */ 20462306a36Sopenharmony_ci RxErrRunt = (1 << 19), /* Rx error, packet < 64 bytes */ 20562306a36Sopenharmony_ci RxErrLong = (1 << 21), /* Rx error, packet > 4096 bytes */ 20662306a36Sopenharmony_ci RxErrFIFO = (1 << 22), /* Rx error, FIFO overflowed, pkt bad */ 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* StatsAddr register */ 20962306a36Sopenharmony_ci DumpStats = (1 << 3), /* Begin stats dump */ 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* RxConfig register */ 21262306a36Sopenharmony_ci RxCfgFIFOShift = 13, /* Shift, to get Rx FIFO thresh value */ 21362306a36Sopenharmony_ci RxCfgDMAShift = 8, /* Shift, to get Rx Max DMA value */ 21462306a36Sopenharmony_ci AcceptErr = 0x20, /* Accept packets with CRC errors */ 21562306a36Sopenharmony_ci AcceptRunt = 0x10, /* Accept runt (<64 bytes) packets */ 21662306a36Sopenharmony_ci AcceptBroadcast = 0x08, /* Accept broadcast packets */ 21762306a36Sopenharmony_ci AcceptMulticast = 0x04, /* Accept multicast packets */ 21862306a36Sopenharmony_ci AcceptMyPhys = 0x02, /* Accept pkts with our MAC as dest */ 21962306a36Sopenharmony_ci AcceptAllPhys = 0x01, /* Accept all pkts w/ physical dest */ 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* IntrMask / IntrStatus registers */ 22262306a36Sopenharmony_ci PciErr = (1 << 15), /* System error on the PCI bus */ 22362306a36Sopenharmony_ci TimerIntr = (1 << 14), /* Asserted when TCTR reaches TimerInt value */ 22462306a36Sopenharmony_ci LenChg = (1 << 13), /* Cable length change */ 22562306a36Sopenharmony_ci SWInt = (1 << 8), /* Software-requested interrupt */ 22662306a36Sopenharmony_ci TxEmpty = (1 << 7), /* No Tx descriptors available */ 22762306a36Sopenharmony_ci RxFIFOOvr = (1 << 6), /* Rx FIFO Overflow */ 22862306a36Sopenharmony_ci LinkChg = (1 << 5), /* Packet underrun, or link change */ 22962306a36Sopenharmony_ci RxEmpty = (1 << 4), /* No Rx descriptors available */ 23062306a36Sopenharmony_ci TxErr = (1 << 3), /* Tx error */ 23162306a36Sopenharmony_ci TxOK = (1 << 2), /* Tx packet sent */ 23262306a36Sopenharmony_ci RxErr = (1 << 1), /* Rx error */ 23362306a36Sopenharmony_ci RxOK = (1 << 0), /* Rx packet received */ 23462306a36Sopenharmony_ci IntrResvd = (1 << 10), /* reserved, according to RealTek engineers, 23562306a36Sopenharmony_ci but hardware likes to raise it */ 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci IntrAll = PciErr | TimerIntr | LenChg | SWInt | TxEmpty | 23862306a36Sopenharmony_ci RxFIFOOvr | LinkChg | RxEmpty | TxErr | TxOK | 23962306a36Sopenharmony_ci RxErr | RxOK | IntrResvd, 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* C mode command register */ 24262306a36Sopenharmony_ci CmdReset = (1 << 4), /* Enable to reset; self-clearing */ 24362306a36Sopenharmony_ci RxOn = (1 << 3), /* Rx mode enable */ 24462306a36Sopenharmony_ci TxOn = (1 << 2), /* Tx mode enable */ 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* C+ mode command register */ 24762306a36Sopenharmony_ci RxVlanOn = (1 << 6), /* Rx VLAN de-tagging enable */ 24862306a36Sopenharmony_ci RxChkSum = (1 << 5), /* Rx checksum offload enable */ 24962306a36Sopenharmony_ci PCIDAC = (1 << 4), /* PCI Dual Address Cycle (64-bit PCI) */ 25062306a36Sopenharmony_ci PCIMulRW = (1 << 3), /* Enable PCI read/write multiple */ 25162306a36Sopenharmony_ci CpRxOn = (1 << 1), /* Rx mode enable */ 25262306a36Sopenharmony_ci CpTxOn = (1 << 0), /* Tx mode enable */ 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* Cfg9436 EEPROM control register */ 25562306a36Sopenharmony_ci Cfg9346_Lock = 0x00, /* Lock ConfigX/MII register access */ 25662306a36Sopenharmony_ci Cfg9346_Unlock = 0xC0, /* Unlock ConfigX/MII register access */ 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* TxConfig register */ 25962306a36Sopenharmony_ci IFG = (1 << 25) | (1 << 24), /* standard IEEE interframe gap */ 26062306a36Sopenharmony_ci TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */ 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* Early Tx Threshold register */ 26362306a36Sopenharmony_ci TxThreshMask = 0x3f, /* Mask bits 5-0 */ 26462306a36Sopenharmony_ci TxThreshMax = 2048, /* Max early Tx threshold */ 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* Config1 register */ 26762306a36Sopenharmony_ci DriverLoaded = (1 << 5), /* Software marker, driver is loaded */ 26862306a36Sopenharmony_ci LWACT = (1 << 4), /* LWAKE active mode */ 26962306a36Sopenharmony_ci PMEnable = (1 << 0), /* Enable various PM features of chip */ 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* Config3 register */ 27262306a36Sopenharmony_ci PARMEnable = (1 << 6), /* Enable auto-loading of PHY parms */ 27362306a36Sopenharmony_ci MagicPacket = (1 << 5), /* Wake up when receives a Magic Packet */ 27462306a36Sopenharmony_ci LinkUp = (1 << 4), /* Wake up when the cable connection is re-established */ 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* Config4 register */ 27762306a36Sopenharmony_ci LWPTN = (1 << 1), /* LWAKE Pattern */ 27862306a36Sopenharmony_ci LWPME = (1 << 4), /* LANWAKE vs PMEB */ 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* Config5 register */ 28162306a36Sopenharmony_ci BWF = (1 << 6), /* Accept Broadcast wakeup frame */ 28262306a36Sopenharmony_ci MWF = (1 << 5), /* Accept Multicast wakeup frame */ 28362306a36Sopenharmony_ci UWF = (1 << 4), /* Accept Unicast wakeup frame */ 28462306a36Sopenharmony_ci LANWake = (1 << 1), /* Enable LANWake signal */ 28562306a36Sopenharmony_ci PMEStatus = (1 << 0), /* PME status can be reset by PCI RST# */ 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci cp_norx_intr_mask = PciErr | LinkChg | TxOK | TxErr | TxEmpty, 28862306a36Sopenharmony_ci cp_rx_intr_mask = RxOK | RxErr | RxEmpty | RxFIFOOvr, 28962306a36Sopenharmony_ci cp_intr_mask = cp_rx_intr_mask | cp_norx_intr_mask, 29062306a36Sopenharmony_ci}; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic const unsigned int cp_rx_config = 29362306a36Sopenharmony_ci (RX_FIFO_THRESH << RxCfgFIFOShift) | 29462306a36Sopenharmony_ci (RX_DMA_BURST << RxCfgDMAShift); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistruct cp_desc { 29762306a36Sopenharmony_ci __le32 opts1; 29862306a36Sopenharmony_ci __le32 opts2; 29962306a36Sopenharmony_ci __le64 addr; 30062306a36Sopenharmony_ci}; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistruct cp_dma_stats { 30362306a36Sopenharmony_ci __le64 tx_ok; 30462306a36Sopenharmony_ci __le64 rx_ok; 30562306a36Sopenharmony_ci __le64 tx_err; 30662306a36Sopenharmony_ci __le32 rx_err; 30762306a36Sopenharmony_ci __le16 rx_fifo; 30862306a36Sopenharmony_ci __le16 frame_align; 30962306a36Sopenharmony_ci __le32 tx_ok_1col; 31062306a36Sopenharmony_ci __le32 tx_ok_mcol; 31162306a36Sopenharmony_ci __le64 rx_ok_phys; 31262306a36Sopenharmony_ci __le64 rx_ok_bcast; 31362306a36Sopenharmony_ci __le32 rx_ok_mcast; 31462306a36Sopenharmony_ci __le16 tx_abort; 31562306a36Sopenharmony_ci __le16 tx_underrun; 31662306a36Sopenharmony_ci} __packed; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistruct cp_extra_stats { 31962306a36Sopenharmony_ci unsigned long rx_frags; 32062306a36Sopenharmony_ci}; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistruct cp_private { 32362306a36Sopenharmony_ci void __iomem *regs; 32462306a36Sopenharmony_ci struct net_device *dev; 32562306a36Sopenharmony_ci spinlock_t lock; 32662306a36Sopenharmony_ci u32 msg_enable; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci struct napi_struct napi; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci struct pci_dev *pdev; 33162306a36Sopenharmony_ci u32 rx_config; 33262306a36Sopenharmony_ci u16 cpcmd; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci struct cp_extra_stats cp_stats; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci unsigned rx_head ____cacheline_aligned; 33762306a36Sopenharmony_ci unsigned rx_tail; 33862306a36Sopenharmony_ci struct cp_desc *rx_ring; 33962306a36Sopenharmony_ci struct sk_buff *rx_skb[CP_RX_RING_SIZE]; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci unsigned tx_head ____cacheline_aligned; 34262306a36Sopenharmony_ci unsigned tx_tail; 34362306a36Sopenharmony_ci struct cp_desc *tx_ring; 34462306a36Sopenharmony_ci struct sk_buff *tx_skb[CP_TX_RING_SIZE]; 34562306a36Sopenharmony_ci u32 tx_opts[CP_TX_RING_SIZE]; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci unsigned rx_buf_sz; 34862306a36Sopenharmony_ci unsigned wol_enabled : 1; /* Is Wake-on-LAN enabled? */ 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci dma_addr_t ring_dma; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci struct mii_if_info mii_if; 35362306a36Sopenharmony_ci}; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci#define cpr8(reg) readb(cp->regs + (reg)) 35662306a36Sopenharmony_ci#define cpr16(reg) readw(cp->regs + (reg)) 35762306a36Sopenharmony_ci#define cpr32(reg) readl(cp->regs + (reg)) 35862306a36Sopenharmony_ci#define cpw8(reg,val) writeb((val), cp->regs + (reg)) 35962306a36Sopenharmony_ci#define cpw16(reg,val) writew((val), cp->regs + (reg)) 36062306a36Sopenharmony_ci#define cpw32(reg,val) writel((val), cp->regs + (reg)) 36162306a36Sopenharmony_ci#define cpw8_f(reg,val) do { \ 36262306a36Sopenharmony_ci writeb((val), cp->regs + (reg)); \ 36362306a36Sopenharmony_ci readb(cp->regs + (reg)); \ 36462306a36Sopenharmony_ci } while (0) 36562306a36Sopenharmony_ci#define cpw16_f(reg,val) do { \ 36662306a36Sopenharmony_ci writew((val), cp->regs + (reg)); \ 36762306a36Sopenharmony_ci readw(cp->regs + (reg)); \ 36862306a36Sopenharmony_ci } while (0) 36962306a36Sopenharmony_ci#define cpw32_f(reg,val) do { \ 37062306a36Sopenharmony_ci writel((val), cp->regs + (reg)); \ 37162306a36Sopenharmony_ci readl(cp->regs + (reg)); \ 37262306a36Sopenharmony_ci } while (0) 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic void __cp_set_rx_mode (struct net_device *dev); 37662306a36Sopenharmony_cistatic void cp_tx (struct cp_private *cp); 37762306a36Sopenharmony_cistatic void cp_clean_rings (struct cp_private *cp); 37862306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 37962306a36Sopenharmony_cistatic void cp_poll_controller(struct net_device *dev); 38062306a36Sopenharmony_ci#endif 38162306a36Sopenharmony_cistatic int cp_get_eeprom_len(struct net_device *dev); 38262306a36Sopenharmony_cistatic int cp_get_eeprom(struct net_device *dev, 38362306a36Sopenharmony_ci struct ethtool_eeprom *eeprom, u8 *data); 38462306a36Sopenharmony_cistatic int cp_set_eeprom(struct net_device *dev, 38562306a36Sopenharmony_ci struct ethtool_eeprom *eeprom, u8 *data); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic struct { 38862306a36Sopenharmony_ci const char str[ETH_GSTRING_LEN]; 38962306a36Sopenharmony_ci} ethtool_stats_keys[] = { 39062306a36Sopenharmony_ci { "tx_ok" }, 39162306a36Sopenharmony_ci { "rx_ok" }, 39262306a36Sopenharmony_ci { "tx_err" }, 39362306a36Sopenharmony_ci { "rx_err" }, 39462306a36Sopenharmony_ci { "rx_fifo" }, 39562306a36Sopenharmony_ci { "frame_align" }, 39662306a36Sopenharmony_ci { "tx_ok_1col" }, 39762306a36Sopenharmony_ci { "tx_ok_mcol" }, 39862306a36Sopenharmony_ci { "rx_ok_phys" }, 39962306a36Sopenharmony_ci { "rx_ok_bcast" }, 40062306a36Sopenharmony_ci { "rx_ok_mcast" }, 40162306a36Sopenharmony_ci { "tx_abort" }, 40262306a36Sopenharmony_ci { "tx_underrun" }, 40362306a36Sopenharmony_ci { "rx_frags" }, 40462306a36Sopenharmony_ci}; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic inline void cp_set_rxbufsize (struct cp_private *cp) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci unsigned int mtu = cp->dev->mtu; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (mtu > ETH_DATA_LEN) 41262306a36Sopenharmony_ci /* MTU + ethernet header + FCS + optional VLAN tag */ 41362306a36Sopenharmony_ci cp->rx_buf_sz = mtu + ETH_HLEN + 8; 41462306a36Sopenharmony_ci else 41562306a36Sopenharmony_ci cp->rx_buf_sz = PKT_BUF_SZ; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic inline void cp_rx_skb (struct cp_private *cp, struct sk_buff *skb, 41962306a36Sopenharmony_ci struct cp_desc *desc) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci u32 opts2 = le32_to_cpu(desc->opts2); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci skb->protocol = eth_type_trans (skb, cp->dev); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci cp->dev->stats.rx_packets++; 42662306a36Sopenharmony_ci cp->dev->stats.rx_bytes += skb->len; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (opts2 & RxVlanTagged) 42962306a36Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), swab16(opts2 & 0xffff)); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci napi_gro_receive(&cp->napi, skb); 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic void cp_rx_err_acct (struct cp_private *cp, unsigned rx_tail, 43562306a36Sopenharmony_ci u32 status, u32 len) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci netif_dbg(cp, rx_err, cp->dev, "rx err, slot %d status 0x%x len %d\n", 43862306a36Sopenharmony_ci rx_tail, status, len); 43962306a36Sopenharmony_ci cp->dev->stats.rx_errors++; 44062306a36Sopenharmony_ci if (status & RxErrFrame) 44162306a36Sopenharmony_ci cp->dev->stats.rx_frame_errors++; 44262306a36Sopenharmony_ci if (status & RxErrCRC) 44362306a36Sopenharmony_ci cp->dev->stats.rx_crc_errors++; 44462306a36Sopenharmony_ci if ((status & RxErrRunt) || (status & RxErrLong)) 44562306a36Sopenharmony_ci cp->dev->stats.rx_length_errors++; 44662306a36Sopenharmony_ci if ((status & (FirstFrag | LastFrag)) != (FirstFrag | LastFrag)) 44762306a36Sopenharmony_ci cp->dev->stats.rx_length_errors++; 44862306a36Sopenharmony_ci if (status & RxErrFIFO) 44962306a36Sopenharmony_ci cp->dev->stats.rx_fifo_errors++; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic inline unsigned int cp_rx_csum_ok (u32 status) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci unsigned int protocol = (status >> 16) & 0x3; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (((protocol == RxProtoTCP) && !(status & TCPFail)) || 45762306a36Sopenharmony_ci ((protocol == RxProtoUDP) && !(status & UDPFail))) 45862306a36Sopenharmony_ci return 1; 45962306a36Sopenharmony_ci else 46062306a36Sopenharmony_ci return 0; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic int cp_rx_poll(struct napi_struct *napi, int budget) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci struct cp_private *cp = container_of(napi, struct cp_private, napi); 46662306a36Sopenharmony_ci struct net_device *dev = cp->dev; 46762306a36Sopenharmony_ci unsigned int rx_tail = cp->rx_tail; 46862306a36Sopenharmony_ci int rx = 0; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci cpw16(IntrStatus, cp_rx_intr_mask); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci while (rx < budget) { 47362306a36Sopenharmony_ci u32 status, len; 47462306a36Sopenharmony_ci dma_addr_t mapping, new_mapping; 47562306a36Sopenharmony_ci struct sk_buff *skb, *new_skb; 47662306a36Sopenharmony_ci struct cp_desc *desc; 47762306a36Sopenharmony_ci const unsigned buflen = cp->rx_buf_sz; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci skb = cp->rx_skb[rx_tail]; 48062306a36Sopenharmony_ci BUG_ON(!skb); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci desc = &cp->rx_ring[rx_tail]; 48362306a36Sopenharmony_ci status = le32_to_cpu(desc->opts1); 48462306a36Sopenharmony_ci if (status & DescOwn) 48562306a36Sopenharmony_ci break; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci len = (status & 0x1fff) - 4; 48862306a36Sopenharmony_ci mapping = le64_to_cpu(desc->addr); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if ((status & (FirstFrag | LastFrag)) != (FirstFrag | LastFrag)) { 49162306a36Sopenharmony_ci /* we don't support incoming fragmented frames. 49262306a36Sopenharmony_ci * instead, we attempt to ensure that the 49362306a36Sopenharmony_ci * pre-allocated RX skbs are properly sized such 49462306a36Sopenharmony_ci * that RX fragments are never encountered 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_ci cp_rx_err_acct(cp, rx_tail, status, len); 49762306a36Sopenharmony_ci dev->stats.rx_dropped++; 49862306a36Sopenharmony_ci cp->cp_stats.rx_frags++; 49962306a36Sopenharmony_ci goto rx_next; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (status & (RxError | RxErrFIFO)) { 50362306a36Sopenharmony_ci cp_rx_err_acct(cp, rx_tail, status, len); 50462306a36Sopenharmony_ci goto rx_next; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci netif_dbg(cp, rx_status, dev, "rx slot %d status 0x%x len %d\n", 50862306a36Sopenharmony_ci rx_tail, status, len); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci new_skb = napi_alloc_skb(napi, buflen); 51162306a36Sopenharmony_ci if (!new_skb) { 51262306a36Sopenharmony_ci dev->stats.rx_dropped++; 51362306a36Sopenharmony_ci goto rx_next; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci new_mapping = dma_map_single(&cp->pdev->dev, new_skb->data, buflen, 51762306a36Sopenharmony_ci DMA_FROM_DEVICE); 51862306a36Sopenharmony_ci if (dma_mapping_error(&cp->pdev->dev, new_mapping)) { 51962306a36Sopenharmony_ci dev->stats.rx_dropped++; 52062306a36Sopenharmony_ci kfree_skb(new_skb); 52162306a36Sopenharmony_ci goto rx_next; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci dma_unmap_single(&cp->pdev->dev, mapping, 52562306a36Sopenharmony_ci buflen, DMA_FROM_DEVICE); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* Handle checksum offloading for incoming packets. */ 52862306a36Sopenharmony_ci if (cp_rx_csum_ok(status)) 52962306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 53062306a36Sopenharmony_ci else 53162306a36Sopenharmony_ci skb_checksum_none_assert(skb); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci skb_put(skb, len); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci cp->rx_skb[rx_tail] = new_skb; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci cp_rx_skb(cp, skb, desc); 53862306a36Sopenharmony_ci rx++; 53962306a36Sopenharmony_ci mapping = new_mapping; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cirx_next: 54262306a36Sopenharmony_ci cp->rx_ring[rx_tail].opts2 = 0; 54362306a36Sopenharmony_ci cp->rx_ring[rx_tail].addr = cpu_to_le64(mapping); 54462306a36Sopenharmony_ci if (rx_tail == (CP_RX_RING_SIZE - 1)) 54562306a36Sopenharmony_ci desc->opts1 = cpu_to_le32(DescOwn | RingEnd | 54662306a36Sopenharmony_ci cp->rx_buf_sz); 54762306a36Sopenharmony_ci else 54862306a36Sopenharmony_ci desc->opts1 = cpu_to_le32(DescOwn | cp->rx_buf_sz); 54962306a36Sopenharmony_ci rx_tail = NEXT_RX(rx_tail); 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci cp->rx_tail = rx_tail; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci /* if we did not reach work limit, then we're done with 55562306a36Sopenharmony_ci * this round of polling 55662306a36Sopenharmony_ci */ 55762306a36Sopenharmony_ci if (rx < budget && napi_complete_done(napi, rx)) { 55862306a36Sopenharmony_ci unsigned long flags; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 56162306a36Sopenharmony_ci cpw16_f(IntrMask, cp_intr_mask); 56262306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci return rx; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic irqreturn_t cp_interrupt (int irq, void *dev_instance) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci struct net_device *dev = dev_instance; 57162306a36Sopenharmony_ci struct cp_private *cp; 57262306a36Sopenharmony_ci int handled = 0; 57362306a36Sopenharmony_ci u16 status; 57462306a36Sopenharmony_ci u16 mask; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (unlikely(dev == NULL)) 57762306a36Sopenharmony_ci return IRQ_NONE; 57862306a36Sopenharmony_ci cp = netdev_priv(dev); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci spin_lock(&cp->lock); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci mask = cpr16(IntrMask); 58362306a36Sopenharmony_ci if (!mask) 58462306a36Sopenharmony_ci goto out_unlock; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci status = cpr16(IntrStatus); 58762306a36Sopenharmony_ci if (!status || (status == 0xFFFF)) 58862306a36Sopenharmony_ci goto out_unlock; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci handled = 1; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci netif_dbg(cp, intr, dev, "intr, status %04x cmd %02x cpcmd %04x\n", 59362306a36Sopenharmony_ci status, cpr8(Cmd), cpr16(CpCmd)); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci cpw16(IntrStatus, status & ~cp_rx_intr_mask); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* close possible race's with dev_close */ 59862306a36Sopenharmony_ci if (unlikely(!netif_running(dev))) { 59962306a36Sopenharmony_ci cpw16(IntrMask, 0); 60062306a36Sopenharmony_ci goto out_unlock; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci if (status & (RxOK | RxErr | RxEmpty | RxFIFOOvr)) 60462306a36Sopenharmony_ci if (napi_schedule_prep(&cp->napi)) { 60562306a36Sopenharmony_ci cpw16_f(IntrMask, cp_norx_intr_mask); 60662306a36Sopenharmony_ci __napi_schedule(&cp->napi); 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (status & (TxOK | TxErr | TxEmpty | SWInt)) 61062306a36Sopenharmony_ci cp_tx(cp); 61162306a36Sopenharmony_ci if (status & LinkChg) 61262306a36Sopenharmony_ci mii_check_media(&cp->mii_if, netif_msg_link(cp), false); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (status & PciErr) { 61662306a36Sopenharmony_ci u16 pci_status; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci pci_read_config_word(cp->pdev, PCI_STATUS, &pci_status); 61962306a36Sopenharmony_ci pci_write_config_word(cp->pdev, PCI_STATUS, pci_status); 62062306a36Sopenharmony_ci netdev_err(dev, "PCI bus error, status=%04x, PCI status=%04x\n", 62162306a36Sopenharmony_ci status, pci_status); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci /* TODO: reset hardware */ 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ciout_unlock: 62762306a36Sopenharmony_ci spin_unlock(&cp->lock); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci return IRQ_RETVAL(handled); 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 63362306a36Sopenharmony_ci/* 63462306a36Sopenharmony_ci * Polling receive - used by netconsole and other diagnostic tools 63562306a36Sopenharmony_ci * to allow network i/o with interrupts disabled. 63662306a36Sopenharmony_ci */ 63762306a36Sopenharmony_cistatic void cp_poll_controller(struct net_device *dev) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 64062306a36Sopenharmony_ci const int irq = cp->pdev->irq; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci disable_irq(irq); 64362306a36Sopenharmony_ci cp_interrupt(irq, dev); 64462306a36Sopenharmony_ci enable_irq(irq); 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci#endif 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic void cp_tx (struct cp_private *cp) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci unsigned tx_head = cp->tx_head; 65162306a36Sopenharmony_ci unsigned tx_tail = cp->tx_tail; 65262306a36Sopenharmony_ci unsigned bytes_compl = 0, pkts_compl = 0; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci while (tx_tail != tx_head) { 65562306a36Sopenharmony_ci struct cp_desc *txd = cp->tx_ring + tx_tail; 65662306a36Sopenharmony_ci struct sk_buff *skb; 65762306a36Sopenharmony_ci u32 status; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci rmb(); 66062306a36Sopenharmony_ci status = le32_to_cpu(txd->opts1); 66162306a36Sopenharmony_ci if (status & DescOwn) 66262306a36Sopenharmony_ci break; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci skb = cp->tx_skb[tx_tail]; 66562306a36Sopenharmony_ci BUG_ON(!skb); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci dma_unmap_single(&cp->pdev->dev, le64_to_cpu(txd->addr), 66862306a36Sopenharmony_ci cp->tx_opts[tx_tail] & 0xffff, 66962306a36Sopenharmony_ci DMA_TO_DEVICE); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (status & LastFrag) { 67262306a36Sopenharmony_ci if (status & (TxError | TxFIFOUnder)) { 67362306a36Sopenharmony_ci netif_dbg(cp, tx_err, cp->dev, 67462306a36Sopenharmony_ci "tx err, status 0x%x\n", status); 67562306a36Sopenharmony_ci cp->dev->stats.tx_errors++; 67662306a36Sopenharmony_ci if (status & TxOWC) 67762306a36Sopenharmony_ci cp->dev->stats.tx_window_errors++; 67862306a36Sopenharmony_ci if (status & TxMaxCol) 67962306a36Sopenharmony_ci cp->dev->stats.tx_aborted_errors++; 68062306a36Sopenharmony_ci if (status & TxLinkFail) 68162306a36Sopenharmony_ci cp->dev->stats.tx_carrier_errors++; 68262306a36Sopenharmony_ci if (status & TxFIFOUnder) 68362306a36Sopenharmony_ci cp->dev->stats.tx_fifo_errors++; 68462306a36Sopenharmony_ci } else { 68562306a36Sopenharmony_ci cp->dev->stats.collisions += 68662306a36Sopenharmony_ci ((status >> TxColCntShift) & TxColCntMask); 68762306a36Sopenharmony_ci cp->dev->stats.tx_packets++; 68862306a36Sopenharmony_ci cp->dev->stats.tx_bytes += skb->len; 68962306a36Sopenharmony_ci netif_dbg(cp, tx_done, cp->dev, 69062306a36Sopenharmony_ci "tx done, slot %d\n", tx_tail); 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci bytes_compl += skb->len; 69362306a36Sopenharmony_ci pkts_compl++; 69462306a36Sopenharmony_ci dev_consume_skb_irq(skb); 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci cp->tx_skb[tx_tail] = NULL; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci tx_tail = NEXT_TX(tx_tail); 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci cp->tx_tail = tx_tail; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci netdev_completed_queue(cp->dev, pkts_compl, bytes_compl); 70562306a36Sopenharmony_ci if (TX_BUFFS_AVAIL(cp) > (MAX_SKB_FRAGS + 1)) 70662306a36Sopenharmony_ci netif_wake_queue(cp->dev); 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic inline u32 cp_tx_vlan_tag(struct sk_buff *skb) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci return skb_vlan_tag_present(skb) ? 71262306a36Sopenharmony_ci TxVlanTag | swab16(skb_vlan_tag_get(skb)) : 0x00; 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic void unwind_tx_frag_mapping(struct cp_private *cp, struct sk_buff *skb, 71662306a36Sopenharmony_ci int first, int entry_last) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci int frag, index; 71962306a36Sopenharmony_ci struct cp_desc *txd; 72062306a36Sopenharmony_ci skb_frag_t *this_frag; 72162306a36Sopenharmony_ci for (frag = 0; frag+first < entry_last; frag++) { 72262306a36Sopenharmony_ci index = first+frag; 72362306a36Sopenharmony_ci cp->tx_skb[index] = NULL; 72462306a36Sopenharmony_ci txd = &cp->tx_ring[index]; 72562306a36Sopenharmony_ci this_frag = &skb_shinfo(skb)->frags[frag]; 72662306a36Sopenharmony_ci dma_unmap_single(&cp->pdev->dev, le64_to_cpu(txd->addr), 72762306a36Sopenharmony_ci skb_frag_size(this_frag), DMA_TO_DEVICE); 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic netdev_tx_t cp_start_xmit (struct sk_buff *skb, 73262306a36Sopenharmony_ci struct net_device *dev) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 73562306a36Sopenharmony_ci unsigned entry; 73662306a36Sopenharmony_ci u32 eor, opts1; 73762306a36Sopenharmony_ci unsigned long intr_flags; 73862306a36Sopenharmony_ci __le32 opts2; 73962306a36Sopenharmony_ci int mss = 0; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, intr_flags); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci /* This is a hard error, log it. */ 74462306a36Sopenharmony_ci if (TX_BUFFS_AVAIL(cp) <= (skb_shinfo(skb)->nr_frags + 1)) { 74562306a36Sopenharmony_ci netif_stop_queue(dev); 74662306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, intr_flags); 74762306a36Sopenharmony_ci netdev_err(dev, "BUG! Tx Ring full when queue awake!\n"); 74862306a36Sopenharmony_ci return NETDEV_TX_BUSY; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci entry = cp->tx_head; 75262306a36Sopenharmony_ci eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0; 75362306a36Sopenharmony_ci mss = skb_shinfo(skb)->gso_size; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci if (mss > MSSMask) { 75662306a36Sopenharmony_ci netdev_WARN_ONCE(dev, "Net bug: GSO size %d too large for 8139CP\n", 75762306a36Sopenharmony_ci mss); 75862306a36Sopenharmony_ci goto out_dma_error; 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci opts2 = cpu_to_le32(cp_tx_vlan_tag(skb)); 76262306a36Sopenharmony_ci opts1 = DescOwn; 76362306a36Sopenharmony_ci if (mss) 76462306a36Sopenharmony_ci opts1 |= LargeSend | (mss << MSSShift); 76562306a36Sopenharmony_ci else if (skb->ip_summed == CHECKSUM_PARTIAL) { 76662306a36Sopenharmony_ci const struct iphdr *ip = ip_hdr(skb); 76762306a36Sopenharmony_ci if (ip->protocol == IPPROTO_TCP) 76862306a36Sopenharmony_ci opts1 |= IPCS | TCPCS; 76962306a36Sopenharmony_ci else if (ip->protocol == IPPROTO_UDP) 77062306a36Sopenharmony_ci opts1 |= IPCS | UDPCS; 77162306a36Sopenharmony_ci else { 77262306a36Sopenharmony_ci WARN_ONCE(1, 77362306a36Sopenharmony_ci "Net bug: asked to checksum invalid Legacy IP packet\n"); 77462306a36Sopenharmony_ci goto out_dma_error; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci } 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci if (skb_shinfo(skb)->nr_frags == 0) { 77962306a36Sopenharmony_ci struct cp_desc *txd = &cp->tx_ring[entry]; 78062306a36Sopenharmony_ci u32 len; 78162306a36Sopenharmony_ci dma_addr_t mapping; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci len = skb->len; 78462306a36Sopenharmony_ci mapping = dma_map_single(&cp->pdev->dev, skb->data, len, DMA_TO_DEVICE); 78562306a36Sopenharmony_ci if (dma_mapping_error(&cp->pdev->dev, mapping)) 78662306a36Sopenharmony_ci goto out_dma_error; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci txd->opts2 = opts2; 78962306a36Sopenharmony_ci txd->addr = cpu_to_le64(mapping); 79062306a36Sopenharmony_ci wmb(); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci opts1 |= eor | len | FirstFrag | LastFrag; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci txd->opts1 = cpu_to_le32(opts1); 79562306a36Sopenharmony_ci wmb(); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci cp->tx_skb[entry] = skb; 79862306a36Sopenharmony_ci cp->tx_opts[entry] = opts1; 79962306a36Sopenharmony_ci netif_dbg(cp, tx_queued, cp->dev, "tx queued, slot %d, skblen %d\n", 80062306a36Sopenharmony_ci entry, skb->len); 80162306a36Sopenharmony_ci } else { 80262306a36Sopenharmony_ci struct cp_desc *txd; 80362306a36Sopenharmony_ci u32 first_len, first_eor, ctrl; 80462306a36Sopenharmony_ci dma_addr_t first_mapping; 80562306a36Sopenharmony_ci int frag, first_entry = entry; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci /* We must give this initial chunk to the device last. 80862306a36Sopenharmony_ci * Otherwise we could race with the device. 80962306a36Sopenharmony_ci */ 81062306a36Sopenharmony_ci first_eor = eor; 81162306a36Sopenharmony_ci first_len = skb_headlen(skb); 81262306a36Sopenharmony_ci first_mapping = dma_map_single(&cp->pdev->dev, skb->data, 81362306a36Sopenharmony_ci first_len, DMA_TO_DEVICE); 81462306a36Sopenharmony_ci if (dma_mapping_error(&cp->pdev->dev, first_mapping)) 81562306a36Sopenharmony_ci goto out_dma_error; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci cp->tx_skb[entry] = skb; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) { 82062306a36Sopenharmony_ci const skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag]; 82162306a36Sopenharmony_ci u32 len; 82262306a36Sopenharmony_ci dma_addr_t mapping; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci entry = NEXT_TX(entry); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci len = skb_frag_size(this_frag); 82762306a36Sopenharmony_ci mapping = dma_map_single(&cp->pdev->dev, 82862306a36Sopenharmony_ci skb_frag_address(this_frag), 82962306a36Sopenharmony_ci len, DMA_TO_DEVICE); 83062306a36Sopenharmony_ci if (dma_mapping_error(&cp->pdev->dev, mapping)) { 83162306a36Sopenharmony_ci unwind_tx_frag_mapping(cp, skb, first_entry, entry); 83262306a36Sopenharmony_ci goto out_dma_error; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci ctrl = opts1 | eor | len; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (frag == skb_shinfo(skb)->nr_frags - 1) 84062306a36Sopenharmony_ci ctrl |= LastFrag; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci txd = &cp->tx_ring[entry]; 84362306a36Sopenharmony_ci txd->opts2 = opts2; 84462306a36Sopenharmony_ci txd->addr = cpu_to_le64(mapping); 84562306a36Sopenharmony_ci wmb(); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci txd->opts1 = cpu_to_le32(ctrl); 84862306a36Sopenharmony_ci wmb(); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci cp->tx_opts[entry] = ctrl; 85162306a36Sopenharmony_ci cp->tx_skb[entry] = skb; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci txd = &cp->tx_ring[first_entry]; 85562306a36Sopenharmony_ci txd->opts2 = opts2; 85662306a36Sopenharmony_ci txd->addr = cpu_to_le64(first_mapping); 85762306a36Sopenharmony_ci wmb(); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci ctrl = opts1 | first_eor | first_len | FirstFrag; 86062306a36Sopenharmony_ci txd->opts1 = cpu_to_le32(ctrl); 86162306a36Sopenharmony_ci wmb(); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci cp->tx_opts[first_entry] = ctrl; 86462306a36Sopenharmony_ci netif_dbg(cp, tx_queued, cp->dev, "tx queued, slots %d-%d, skblen %d\n", 86562306a36Sopenharmony_ci first_entry, entry, skb->len); 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci cp->tx_head = NEXT_TX(entry); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci netdev_sent_queue(dev, skb->len); 87062306a36Sopenharmony_ci if (TX_BUFFS_AVAIL(cp) <= (MAX_SKB_FRAGS + 1)) 87162306a36Sopenharmony_ci netif_stop_queue(dev); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ciout_unlock: 87462306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, intr_flags); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci cpw8(TxPoll, NormalTxPoll); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci return NETDEV_TX_OK; 87962306a36Sopenharmony_ciout_dma_error: 88062306a36Sopenharmony_ci dev_kfree_skb_any(skb); 88162306a36Sopenharmony_ci cp->dev->stats.tx_dropped++; 88262306a36Sopenharmony_ci goto out_unlock; 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci/* Set or clear the multicast filter for this adaptor. 88662306a36Sopenharmony_ci This routine is not state sensitive and need not be SMP locked. */ 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_cistatic void __cp_set_rx_mode (struct net_device *dev) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 89162306a36Sopenharmony_ci u32 mc_filter[2]; /* Multicast hash filter */ 89262306a36Sopenharmony_ci int rx_mode; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci /* Note: do not reorder, GCC is clever about common statements. */ 89562306a36Sopenharmony_ci if (dev->flags & IFF_PROMISC) { 89662306a36Sopenharmony_ci /* Unconditionally log net taps. */ 89762306a36Sopenharmony_ci rx_mode = 89862306a36Sopenharmony_ci AcceptBroadcast | AcceptMulticast | AcceptMyPhys | 89962306a36Sopenharmony_ci AcceptAllPhys; 90062306a36Sopenharmony_ci mc_filter[1] = mc_filter[0] = 0xffffffff; 90162306a36Sopenharmony_ci } else if ((netdev_mc_count(dev) > multicast_filter_limit) || 90262306a36Sopenharmony_ci (dev->flags & IFF_ALLMULTI)) { 90362306a36Sopenharmony_ci /* Too many to filter perfectly -- accept all multicasts. */ 90462306a36Sopenharmony_ci rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; 90562306a36Sopenharmony_ci mc_filter[1] = mc_filter[0] = 0xffffffff; 90662306a36Sopenharmony_ci } else { 90762306a36Sopenharmony_ci struct netdev_hw_addr *ha; 90862306a36Sopenharmony_ci rx_mode = AcceptBroadcast | AcceptMyPhys; 90962306a36Sopenharmony_ci mc_filter[1] = mc_filter[0] = 0; 91062306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 91162306a36Sopenharmony_ci int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); 91462306a36Sopenharmony_ci rx_mode |= AcceptMulticast; 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci /* We can safely update without stopping the chip. */ 91962306a36Sopenharmony_ci cp->rx_config = cp_rx_config | rx_mode; 92062306a36Sopenharmony_ci cpw32_f(RxConfig, cp->rx_config); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci cpw32_f (MAR0 + 0, mc_filter[0]); 92362306a36Sopenharmony_ci cpw32_f (MAR0 + 4, mc_filter[1]); 92462306a36Sopenharmony_ci} 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_cistatic void cp_set_rx_mode (struct net_device *dev) 92762306a36Sopenharmony_ci{ 92862306a36Sopenharmony_ci unsigned long flags; 92962306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci spin_lock_irqsave (&cp->lock, flags); 93262306a36Sopenharmony_ci __cp_set_rx_mode(dev); 93362306a36Sopenharmony_ci spin_unlock_irqrestore (&cp->lock, flags); 93462306a36Sopenharmony_ci} 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_cistatic void __cp_get_stats(struct cp_private *cp) 93762306a36Sopenharmony_ci{ 93862306a36Sopenharmony_ci /* only lower 24 bits valid; write any value to clear */ 93962306a36Sopenharmony_ci cp->dev->stats.rx_missed_errors += (cpr32 (RxMissed) & 0xffffff); 94062306a36Sopenharmony_ci cpw32 (RxMissed, 0); 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic struct net_device_stats *cp_get_stats(struct net_device *dev) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 94662306a36Sopenharmony_ci unsigned long flags; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci /* The chip only need report frame silently dropped. */ 94962306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 95062306a36Sopenharmony_ci if (netif_running(dev) && netif_device_present(dev)) 95162306a36Sopenharmony_ci __cp_get_stats(cp); 95262306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci return &dev->stats; 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_cistatic void cp_stop_hw (struct cp_private *cp) 95862306a36Sopenharmony_ci{ 95962306a36Sopenharmony_ci cpw16(IntrStatus, ~(cpr16(IntrStatus))); 96062306a36Sopenharmony_ci cpw16_f(IntrMask, 0); 96162306a36Sopenharmony_ci cpw8(Cmd, 0); 96262306a36Sopenharmony_ci cpw16_f(CpCmd, 0); 96362306a36Sopenharmony_ci cpw16_f(IntrStatus, ~(cpr16(IntrStatus))); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci cp->rx_tail = 0; 96662306a36Sopenharmony_ci cp->tx_head = cp->tx_tail = 0; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci netdev_reset_queue(cp->dev); 96962306a36Sopenharmony_ci} 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_cistatic void cp_reset_hw (struct cp_private *cp) 97262306a36Sopenharmony_ci{ 97362306a36Sopenharmony_ci unsigned work = 1000; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci cpw8(Cmd, CmdReset); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci while (work--) { 97862306a36Sopenharmony_ci if (!(cpr8(Cmd) & CmdReset)) 97962306a36Sopenharmony_ci return; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci schedule_timeout_uninterruptible(10); 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci netdev_err(cp->dev, "hardware reset timeout\n"); 98562306a36Sopenharmony_ci} 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_cistatic inline void cp_start_hw (struct cp_private *cp) 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci dma_addr_t ring_dma; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci cpw16(CpCmd, cp->cpcmd); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci /* 99462306a36Sopenharmony_ci * These (at least TxRingAddr) need to be configured after the 99562306a36Sopenharmony_ci * corresponding bits in CpCmd are enabled. Datasheet v1.6 §6.33 99662306a36Sopenharmony_ci * (C+ Command Register) recommends that these and more be configured 99762306a36Sopenharmony_ci * *after* the [RT]xEnable bits in CpCmd are set. And on some hardware 99862306a36Sopenharmony_ci * it's been observed that the TxRingAddr is actually reset to garbage 99962306a36Sopenharmony_ci * when C+ mode Tx is enabled in CpCmd. 100062306a36Sopenharmony_ci */ 100162306a36Sopenharmony_ci cpw32_f(HiTxRingAddr, 0); 100262306a36Sopenharmony_ci cpw32_f(HiTxRingAddr + 4, 0); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci ring_dma = cp->ring_dma; 100562306a36Sopenharmony_ci cpw32_f(RxRingAddr, ring_dma & 0xffffffff); 100662306a36Sopenharmony_ci cpw32_f(RxRingAddr + 4, (ring_dma >> 16) >> 16); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci ring_dma += sizeof(struct cp_desc) * CP_RX_RING_SIZE; 100962306a36Sopenharmony_ci cpw32_f(TxRingAddr, ring_dma & 0xffffffff); 101062306a36Sopenharmony_ci cpw32_f(TxRingAddr + 4, (ring_dma >> 16) >> 16); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci /* 101362306a36Sopenharmony_ci * Strictly speaking, the datasheet says this should be enabled 101462306a36Sopenharmony_ci * *before* setting the descriptor addresses. But what, then, would 101562306a36Sopenharmony_ci * prevent it from doing DMA to random unconfigured addresses? 101662306a36Sopenharmony_ci * This variant appears to work fine. 101762306a36Sopenharmony_ci */ 101862306a36Sopenharmony_ci cpw8(Cmd, RxOn | TxOn); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci netdev_reset_queue(cp->dev); 102162306a36Sopenharmony_ci} 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_cistatic void cp_enable_irq(struct cp_private *cp) 102462306a36Sopenharmony_ci{ 102562306a36Sopenharmony_ci cpw16_f(IntrMask, cp_intr_mask); 102662306a36Sopenharmony_ci} 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_cistatic void cp_init_hw (struct cp_private *cp) 102962306a36Sopenharmony_ci{ 103062306a36Sopenharmony_ci struct net_device *dev = cp->dev; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci cp_reset_hw(cp); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci cpw8_f (Cfg9346, Cfg9346_Unlock); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci /* Restore our idea of the MAC address. */ 103762306a36Sopenharmony_ci cpw32_f (MAC0 + 0, le32_to_cpu (*(__le32 *) (dev->dev_addr + 0))); 103862306a36Sopenharmony_ci cpw32_f (MAC0 + 4, le32_to_cpu (*(__le32 *) (dev->dev_addr + 4))); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci cp_start_hw(cp); 104162306a36Sopenharmony_ci cpw8(TxThresh, 0x06); /* XXX convert magic num to a constant */ 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci __cp_set_rx_mode(dev); 104462306a36Sopenharmony_ci cpw32_f (TxConfig, IFG | (TX_DMA_BURST << TxDMAShift)); 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci cpw8(Config1, cpr8(Config1) | DriverLoaded | PMEnable); 104762306a36Sopenharmony_ci /* Disable Wake-on-LAN. Can be turned on with ETHTOOL_SWOL */ 104862306a36Sopenharmony_ci cpw8(Config3, PARMEnable); 104962306a36Sopenharmony_ci cp->wol_enabled = 0; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci cpw8(Config5, cpr8(Config5) & PMEStatus); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci cpw16(MultiIntr, 0); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci cpw8_f(Cfg9346, Cfg9346_Lock); 105662306a36Sopenharmony_ci} 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_cistatic int cp_refill_rx(struct cp_private *cp) 105962306a36Sopenharmony_ci{ 106062306a36Sopenharmony_ci struct net_device *dev = cp->dev; 106162306a36Sopenharmony_ci unsigned i; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci for (i = 0; i < CP_RX_RING_SIZE; i++) { 106462306a36Sopenharmony_ci struct sk_buff *skb; 106562306a36Sopenharmony_ci dma_addr_t mapping; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci skb = netdev_alloc_skb_ip_align(dev, cp->rx_buf_sz); 106862306a36Sopenharmony_ci if (!skb) 106962306a36Sopenharmony_ci goto err_out; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci mapping = dma_map_single(&cp->pdev->dev, skb->data, 107262306a36Sopenharmony_ci cp->rx_buf_sz, DMA_FROM_DEVICE); 107362306a36Sopenharmony_ci if (dma_mapping_error(&cp->pdev->dev, mapping)) { 107462306a36Sopenharmony_ci kfree_skb(skb); 107562306a36Sopenharmony_ci goto err_out; 107662306a36Sopenharmony_ci } 107762306a36Sopenharmony_ci cp->rx_skb[i] = skb; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci cp->rx_ring[i].opts2 = 0; 108062306a36Sopenharmony_ci cp->rx_ring[i].addr = cpu_to_le64(mapping); 108162306a36Sopenharmony_ci if (i == (CP_RX_RING_SIZE - 1)) 108262306a36Sopenharmony_ci cp->rx_ring[i].opts1 = 108362306a36Sopenharmony_ci cpu_to_le32(DescOwn | RingEnd | cp->rx_buf_sz); 108462306a36Sopenharmony_ci else 108562306a36Sopenharmony_ci cp->rx_ring[i].opts1 = 108662306a36Sopenharmony_ci cpu_to_le32(DescOwn | cp->rx_buf_sz); 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci return 0; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_cierr_out: 109262306a36Sopenharmony_ci cp_clean_rings(cp); 109362306a36Sopenharmony_ci return -ENOMEM; 109462306a36Sopenharmony_ci} 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_cistatic void cp_init_rings_index (struct cp_private *cp) 109762306a36Sopenharmony_ci{ 109862306a36Sopenharmony_ci cp->rx_tail = 0; 109962306a36Sopenharmony_ci cp->tx_head = cp->tx_tail = 0; 110062306a36Sopenharmony_ci} 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_cistatic int cp_init_rings (struct cp_private *cp) 110362306a36Sopenharmony_ci{ 110462306a36Sopenharmony_ci memset(cp->tx_ring, 0, sizeof(struct cp_desc) * CP_TX_RING_SIZE); 110562306a36Sopenharmony_ci cp->tx_ring[CP_TX_RING_SIZE - 1].opts1 = cpu_to_le32(RingEnd); 110662306a36Sopenharmony_ci memset(cp->tx_opts, 0, sizeof(cp->tx_opts)); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci cp_init_rings_index(cp); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci return cp_refill_rx (cp); 111162306a36Sopenharmony_ci} 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_cistatic int cp_alloc_rings (struct cp_private *cp) 111462306a36Sopenharmony_ci{ 111562306a36Sopenharmony_ci struct device *d = &cp->pdev->dev; 111662306a36Sopenharmony_ci void *mem; 111762306a36Sopenharmony_ci int rc; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci mem = dma_alloc_coherent(d, CP_RING_BYTES, &cp->ring_dma, GFP_KERNEL); 112062306a36Sopenharmony_ci if (!mem) 112162306a36Sopenharmony_ci return -ENOMEM; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci cp->rx_ring = mem; 112462306a36Sopenharmony_ci cp->tx_ring = &cp->rx_ring[CP_RX_RING_SIZE]; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci rc = cp_init_rings(cp); 112762306a36Sopenharmony_ci if (rc < 0) 112862306a36Sopenharmony_ci dma_free_coherent(d, CP_RING_BYTES, cp->rx_ring, cp->ring_dma); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci return rc; 113162306a36Sopenharmony_ci} 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_cistatic void cp_clean_rings (struct cp_private *cp) 113462306a36Sopenharmony_ci{ 113562306a36Sopenharmony_ci struct cp_desc *desc; 113662306a36Sopenharmony_ci unsigned i; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci for (i = 0; i < CP_RX_RING_SIZE; i++) { 113962306a36Sopenharmony_ci if (cp->rx_skb[i]) { 114062306a36Sopenharmony_ci desc = cp->rx_ring + i; 114162306a36Sopenharmony_ci dma_unmap_single(&cp->pdev->dev,le64_to_cpu(desc->addr), 114262306a36Sopenharmony_ci cp->rx_buf_sz, DMA_FROM_DEVICE); 114362306a36Sopenharmony_ci dev_kfree_skb_any(cp->rx_skb[i]); 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci for (i = 0; i < CP_TX_RING_SIZE; i++) { 114862306a36Sopenharmony_ci if (cp->tx_skb[i]) { 114962306a36Sopenharmony_ci struct sk_buff *skb = cp->tx_skb[i]; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci desc = cp->tx_ring + i; 115262306a36Sopenharmony_ci dma_unmap_single(&cp->pdev->dev,le64_to_cpu(desc->addr), 115362306a36Sopenharmony_ci le32_to_cpu(desc->opts1) & 0xffff, 115462306a36Sopenharmony_ci DMA_TO_DEVICE); 115562306a36Sopenharmony_ci if (le32_to_cpu(desc->opts1) & LastFrag) 115662306a36Sopenharmony_ci dev_kfree_skb_any(skb); 115762306a36Sopenharmony_ci cp->dev->stats.tx_dropped++; 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci netdev_reset_queue(cp->dev); 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci memset(cp->rx_ring, 0, sizeof(struct cp_desc) * CP_RX_RING_SIZE); 116362306a36Sopenharmony_ci memset(cp->tx_ring, 0, sizeof(struct cp_desc) * CP_TX_RING_SIZE); 116462306a36Sopenharmony_ci memset(cp->tx_opts, 0, sizeof(cp->tx_opts)); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci memset(cp->rx_skb, 0, sizeof(struct sk_buff *) * CP_RX_RING_SIZE); 116762306a36Sopenharmony_ci memset(cp->tx_skb, 0, sizeof(struct sk_buff *) * CP_TX_RING_SIZE); 116862306a36Sopenharmony_ci} 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_cistatic void cp_free_rings (struct cp_private *cp) 117162306a36Sopenharmony_ci{ 117262306a36Sopenharmony_ci cp_clean_rings(cp); 117362306a36Sopenharmony_ci dma_free_coherent(&cp->pdev->dev, CP_RING_BYTES, cp->rx_ring, 117462306a36Sopenharmony_ci cp->ring_dma); 117562306a36Sopenharmony_ci cp->rx_ring = NULL; 117662306a36Sopenharmony_ci cp->tx_ring = NULL; 117762306a36Sopenharmony_ci} 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_cistatic int cp_open (struct net_device *dev) 118062306a36Sopenharmony_ci{ 118162306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 118262306a36Sopenharmony_ci const int irq = cp->pdev->irq; 118362306a36Sopenharmony_ci int rc; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci netif_dbg(cp, ifup, dev, "enabling interface\n"); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci rc = cp_alloc_rings(cp); 118862306a36Sopenharmony_ci if (rc) 118962306a36Sopenharmony_ci return rc; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci napi_enable(&cp->napi); 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci cp_init_hw(cp); 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci rc = request_irq(irq, cp_interrupt, IRQF_SHARED, dev->name, dev); 119662306a36Sopenharmony_ci if (rc) 119762306a36Sopenharmony_ci goto err_out_hw; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci cp_enable_irq(cp); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci netif_carrier_off(dev); 120262306a36Sopenharmony_ci mii_check_media(&cp->mii_if, netif_msg_link(cp), true); 120362306a36Sopenharmony_ci netif_start_queue(dev); 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci return 0; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_cierr_out_hw: 120862306a36Sopenharmony_ci napi_disable(&cp->napi); 120962306a36Sopenharmony_ci cp_stop_hw(cp); 121062306a36Sopenharmony_ci cp_free_rings(cp); 121162306a36Sopenharmony_ci return rc; 121262306a36Sopenharmony_ci} 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_cistatic int cp_close (struct net_device *dev) 121562306a36Sopenharmony_ci{ 121662306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 121762306a36Sopenharmony_ci unsigned long flags; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci napi_disable(&cp->napi); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci netif_dbg(cp, ifdown, dev, "disabling interface\n"); 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci netif_stop_queue(dev); 122662306a36Sopenharmony_ci netif_carrier_off(dev); 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci cp_stop_hw(cp); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci free_irq(cp->pdev->irq, dev); 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci cp_free_rings(cp); 123562306a36Sopenharmony_ci return 0; 123662306a36Sopenharmony_ci} 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_cistatic void cp_tx_timeout(struct net_device *dev, unsigned int txqueue) 123962306a36Sopenharmony_ci{ 124062306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 124162306a36Sopenharmony_ci unsigned long flags; 124262306a36Sopenharmony_ci int i; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci netdev_warn(dev, "Transmit timeout, status %2x %4x %4x %4x\n", 124562306a36Sopenharmony_ci cpr8(Cmd), cpr16(CpCmd), 124662306a36Sopenharmony_ci cpr16(IntrStatus), cpr16(IntrMask)); 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci netif_dbg(cp, tx_err, cp->dev, "TX ring head %d tail %d desc %x\n", 125162306a36Sopenharmony_ci cp->tx_head, cp->tx_tail, cpr16(TxDmaOkLowDesc)); 125262306a36Sopenharmony_ci for (i = 0; i < CP_TX_RING_SIZE; i++) { 125362306a36Sopenharmony_ci netif_dbg(cp, tx_err, cp->dev, 125462306a36Sopenharmony_ci "TX slot %d @%p: %08x (%08x) %08x %llx %p\n", 125562306a36Sopenharmony_ci i, &cp->tx_ring[i], le32_to_cpu(cp->tx_ring[i].opts1), 125662306a36Sopenharmony_ci cp->tx_opts[i], le32_to_cpu(cp->tx_ring[i].opts2), 125762306a36Sopenharmony_ci le64_to_cpu(cp->tx_ring[i].addr), 125862306a36Sopenharmony_ci cp->tx_skb[i]); 125962306a36Sopenharmony_ci } 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci cp_stop_hw(cp); 126262306a36Sopenharmony_ci cp_clean_rings(cp); 126362306a36Sopenharmony_ci cp_init_rings(cp); 126462306a36Sopenharmony_ci cp_start_hw(cp); 126562306a36Sopenharmony_ci __cp_set_rx_mode(dev); 126662306a36Sopenharmony_ci cpw16_f(IntrMask, cp_norx_intr_mask); 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci netif_wake_queue(dev); 126962306a36Sopenharmony_ci napi_schedule_irqoff(&cp->napi); 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 127262306a36Sopenharmony_ci} 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_cistatic int cp_change_mtu(struct net_device *dev, int new_mtu) 127562306a36Sopenharmony_ci{ 127662306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci /* if network interface not up, no need for complexity */ 127962306a36Sopenharmony_ci if (!netif_running(dev)) { 128062306a36Sopenharmony_ci dev->mtu = new_mtu; 128162306a36Sopenharmony_ci cp_set_rxbufsize(cp); /* set new rx buf size */ 128262306a36Sopenharmony_ci return 0; 128362306a36Sopenharmony_ci } 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci /* network IS up, close it, reset MTU, and come up again. */ 128662306a36Sopenharmony_ci cp_close(dev); 128762306a36Sopenharmony_ci dev->mtu = new_mtu; 128862306a36Sopenharmony_ci cp_set_rxbufsize(cp); 128962306a36Sopenharmony_ci return cp_open(dev); 129062306a36Sopenharmony_ci} 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_cistatic const char mii_2_8139_map[8] = { 129362306a36Sopenharmony_ci BasicModeCtrl, 129462306a36Sopenharmony_ci BasicModeStatus, 129562306a36Sopenharmony_ci 0, 129662306a36Sopenharmony_ci 0, 129762306a36Sopenharmony_ci NWayAdvert, 129862306a36Sopenharmony_ci NWayLPAR, 129962306a36Sopenharmony_ci NWayExpansion, 130062306a36Sopenharmony_ci 0 130162306a36Sopenharmony_ci}; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_cistatic int mdio_read(struct net_device *dev, int phy_id, int location) 130462306a36Sopenharmony_ci{ 130562306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci return location < 8 && mii_2_8139_map[location] ? 130862306a36Sopenharmony_ci readw(cp->regs + mii_2_8139_map[location]) : 0; 130962306a36Sopenharmony_ci} 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_cistatic void mdio_write(struct net_device *dev, int phy_id, int location, 131362306a36Sopenharmony_ci int value) 131462306a36Sopenharmony_ci{ 131562306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci if (location == 0) { 131862306a36Sopenharmony_ci cpw8(Cfg9346, Cfg9346_Unlock); 131962306a36Sopenharmony_ci cpw16(BasicModeCtrl, value); 132062306a36Sopenharmony_ci cpw8(Cfg9346, Cfg9346_Lock); 132162306a36Sopenharmony_ci } else if (location < 8 && mii_2_8139_map[location]) 132262306a36Sopenharmony_ci cpw16(mii_2_8139_map[location], value); 132362306a36Sopenharmony_ci} 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci/* Set the ethtool Wake-on-LAN settings */ 132662306a36Sopenharmony_cistatic int netdev_set_wol (struct cp_private *cp, 132762306a36Sopenharmony_ci const struct ethtool_wolinfo *wol) 132862306a36Sopenharmony_ci{ 132962306a36Sopenharmony_ci u8 options; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci options = cpr8 (Config3) & ~(LinkUp | MagicPacket); 133262306a36Sopenharmony_ci /* If WOL is being disabled, no need for complexity */ 133362306a36Sopenharmony_ci if (wol->wolopts) { 133462306a36Sopenharmony_ci if (wol->wolopts & WAKE_PHY) options |= LinkUp; 133562306a36Sopenharmony_ci if (wol->wolopts & WAKE_MAGIC) options |= MagicPacket; 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci cpw8 (Cfg9346, Cfg9346_Unlock); 133962306a36Sopenharmony_ci cpw8 (Config3, options); 134062306a36Sopenharmony_ci cpw8 (Cfg9346, Cfg9346_Lock); 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci options = 0; /* Paranoia setting */ 134362306a36Sopenharmony_ci options = cpr8 (Config5) & ~(UWF | MWF | BWF); 134462306a36Sopenharmony_ci /* If WOL is being disabled, no need for complexity */ 134562306a36Sopenharmony_ci if (wol->wolopts) { 134662306a36Sopenharmony_ci if (wol->wolopts & WAKE_UCAST) options |= UWF; 134762306a36Sopenharmony_ci if (wol->wolopts & WAKE_BCAST) options |= BWF; 134862306a36Sopenharmony_ci if (wol->wolopts & WAKE_MCAST) options |= MWF; 134962306a36Sopenharmony_ci } 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci cpw8 (Config5, options); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci cp->wol_enabled = (wol->wolopts) ? 1 : 0; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci return 0; 135662306a36Sopenharmony_ci} 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci/* Get the ethtool Wake-on-LAN settings */ 135962306a36Sopenharmony_cistatic void netdev_get_wol (struct cp_private *cp, 136062306a36Sopenharmony_ci struct ethtool_wolinfo *wol) 136162306a36Sopenharmony_ci{ 136262306a36Sopenharmony_ci u8 options; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci wol->wolopts = 0; /* Start from scratch */ 136562306a36Sopenharmony_ci wol->supported = WAKE_PHY | WAKE_BCAST | WAKE_MAGIC | 136662306a36Sopenharmony_ci WAKE_MCAST | WAKE_UCAST; 136762306a36Sopenharmony_ci /* We don't need to go on if WOL is disabled */ 136862306a36Sopenharmony_ci if (!cp->wol_enabled) return; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci options = cpr8 (Config3); 137162306a36Sopenharmony_ci if (options & LinkUp) wol->wolopts |= WAKE_PHY; 137262306a36Sopenharmony_ci if (options & MagicPacket) wol->wolopts |= WAKE_MAGIC; 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci options = 0; /* Paranoia setting */ 137562306a36Sopenharmony_ci options = cpr8 (Config5); 137662306a36Sopenharmony_ci if (options & UWF) wol->wolopts |= WAKE_UCAST; 137762306a36Sopenharmony_ci if (options & BWF) wol->wolopts |= WAKE_BCAST; 137862306a36Sopenharmony_ci if (options & MWF) wol->wolopts |= WAKE_MCAST; 137962306a36Sopenharmony_ci} 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_cistatic void cp_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info) 138262306a36Sopenharmony_ci{ 138362306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci strscpy(info->driver, DRV_NAME, sizeof(info->driver)); 138662306a36Sopenharmony_ci strscpy(info->version, DRV_VERSION, sizeof(info->version)); 138762306a36Sopenharmony_ci strscpy(info->bus_info, pci_name(cp->pdev), sizeof(info->bus_info)); 138862306a36Sopenharmony_ci} 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_cistatic void cp_get_ringparam(struct net_device *dev, 139162306a36Sopenharmony_ci struct ethtool_ringparam *ring, 139262306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ring, 139362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 139462306a36Sopenharmony_ci{ 139562306a36Sopenharmony_ci ring->rx_max_pending = CP_RX_RING_SIZE; 139662306a36Sopenharmony_ci ring->tx_max_pending = CP_TX_RING_SIZE; 139762306a36Sopenharmony_ci ring->rx_pending = CP_RX_RING_SIZE; 139862306a36Sopenharmony_ci ring->tx_pending = CP_TX_RING_SIZE; 139962306a36Sopenharmony_ci} 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_cistatic int cp_get_regs_len(struct net_device *dev) 140262306a36Sopenharmony_ci{ 140362306a36Sopenharmony_ci return CP_REGS_SIZE; 140462306a36Sopenharmony_ci} 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_cistatic int cp_get_sset_count (struct net_device *dev, int sset) 140762306a36Sopenharmony_ci{ 140862306a36Sopenharmony_ci switch (sset) { 140962306a36Sopenharmony_ci case ETH_SS_STATS: 141062306a36Sopenharmony_ci return CP_NUM_STATS; 141162306a36Sopenharmony_ci default: 141262306a36Sopenharmony_ci return -EOPNOTSUPP; 141362306a36Sopenharmony_ci } 141462306a36Sopenharmony_ci} 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_cistatic int cp_get_link_ksettings(struct net_device *dev, 141762306a36Sopenharmony_ci struct ethtool_link_ksettings *cmd) 141862306a36Sopenharmony_ci{ 141962306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 142062306a36Sopenharmony_ci unsigned long flags; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 142362306a36Sopenharmony_ci mii_ethtool_get_link_ksettings(&cp->mii_if, cmd); 142462306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci return 0; 142762306a36Sopenharmony_ci} 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_cistatic int cp_set_link_ksettings(struct net_device *dev, 143062306a36Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 143162306a36Sopenharmony_ci{ 143262306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 143362306a36Sopenharmony_ci int rc; 143462306a36Sopenharmony_ci unsigned long flags; 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 143762306a36Sopenharmony_ci rc = mii_ethtool_set_link_ksettings(&cp->mii_if, cmd); 143862306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci return rc; 144162306a36Sopenharmony_ci} 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_cistatic int cp_nway_reset(struct net_device *dev) 144462306a36Sopenharmony_ci{ 144562306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 144662306a36Sopenharmony_ci return mii_nway_restart(&cp->mii_if); 144762306a36Sopenharmony_ci} 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_cistatic u32 cp_get_msglevel(struct net_device *dev) 145062306a36Sopenharmony_ci{ 145162306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 145262306a36Sopenharmony_ci return cp->msg_enable; 145362306a36Sopenharmony_ci} 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_cistatic void cp_set_msglevel(struct net_device *dev, u32 value) 145662306a36Sopenharmony_ci{ 145762306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 145862306a36Sopenharmony_ci cp->msg_enable = value; 145962306a36Sopenharmony_ci} 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_cistatic int cp_set_features(struct net_device *dev, netdev_features_t features) 146262306a36Sopenharmony_ci{ 146362306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 146462306a36Sopenharmony_ci unsigned long flags; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci if (!((dev->features ^ features) & NETIF_F_RXCSUM)) 146762306a36Sopenharmony_ci return 0; 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci if (features & NETIF_F_RXCSUM) 147262306a36Sopenharmony_ci cp->cpcmd |= RxChkSum; 147362306a36Sopenharmony_ci else 147462306a36Sopenharmony_ci cp->cpcmd &= ~RxChkSum; 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci if (features & NETIF_F_HW_VLAN_CTAG_RX) 147762306a36Sopenharmony_ci cp->cpcmd |= RxVlanOn; 147862306a36Sopenharmony_ci else 147962306a36Sopenharmony_ci cp->cpcmd &= ~RxVlanOn; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci cpw16_f(CpCmd, cp->cpcmd); 148262306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci return 0; 148562306a36Sopenharmony_ci} 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_cistatic void cp_get_regs(struct net_device *dev, struct ethtool_regs *regs, 148862306a36Sopenharmony_ci void *p) 148962306a36Sopenharmony_ci{ 149062306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 149162306a36Sopenharmony_ci unsigned long flags; 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci if (regs->len < CP_REGS_SIZE) 149462306a36Sopenharmony_ci return /* -EINVAL */; 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci regs->version = CP_REGS_VER; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 149962306a36Sopenharmony_ci memcpy_fromio(p, cp->regs, CP_REGS_SIZE); 150062306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 150162306a36Sopenharmony_ci} 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_cistatic void cp_get_wol (struct net_device *dev, struct ethtool_wolinfo *wol) 150462306a36Sopenharmony_ci{ 150562306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 150662306a36Sopenharmony_ci unsigned long flags; 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci spin_lock_irqsave (&cp->lock, flags); 150962306a36Sopenharmony_ci netdev_get_wol (cp, wol); 151062306a36Sopenharmony_ci spin_unlock_irqrestore (&cp->lock, flags); 151162306a36Sopenharmony_ci} 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_cistatic int cp_set_wol (struct net_device *dev, struct ethtool_wolinfo *wol) 151462306a36Sopenharmony_ci{ 151562306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 151662306a36Sopenharmony_ci unsigned long flags; 151762306a36Sopenharmony_ci int rc; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci spin_lock_irqsave (&cp->lock, flags); 152062306a36Sopenharmony_ci rc = netdev_set_wol (cp, wol); 152162306a36Sopenharmony_ci spin_unlock_irqrestore (&cp->lock, flags); 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci return rc; 152462306a36Sopenharmony_ci} 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_cistatic void cp_get_strings (struct net_device *dev, u32 stringset, u8 *buf) 152762306a36Sopenharmony_ci{ 152862306a36Sopenharmony_ci switch (stringset) { 152962306a36Sopenharmony_ci case ETH_SS_STATS: 153062306a36Sopenharmony_ci memcpy(buf, ðtool_stats_keys, sizeof(ethtool_stats_keys)); 153162306a36Sopenharmony_ci break; 153262306a36Sopenharmony_ci default: 153362306a36Sopenharmony_ci BUG(); 153462306a36Sopenharmony_ci break; 153562306a36Sopenharmony_ci } 153662306a36Sopenharmony_ci} 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_cistatic void cp_get_ethtool_stats (struct net_device *dev, 153962306a36Sopenharmony_ci struct ethtool_stats *estats, u64 *tmp_stats) 154062306a36Sopenharmony_ci{ 154162306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 154262306a36Sopenharmony_ci struct cp_dma_stats *nic_stats; 154362306a36Sopenharmony_ci dma_addr_t dma; 154462306a36Sopenharmony_ci int i; 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci nic_stats = dma_alloc_coherent(&cp->pdev->dev, sizeof(*nic_stats), 154762306a36Sopenharmony_ci &dma, GFP_KERNEL); 154862306a36Sopenharmony_ci if (!nic_stats) 154962306a36Sopenharmony_ci return; 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci /* begin NIC statistics dump */ 155262306a36Sopenharmony_ci cpw32(StatsAddr + 4, (u64)dma >> 32); 155362306a36Sopenharmony_ci cpw32(StatsAddr, ((u64)dma & DMA_BIT_MASK(32)) | DumpStats); 155462306a36Sopenharmony_ci cpr32(StatsAddr); 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci for (i = 0; i < 1000; i++) { 155762306a36Sopenharmony_ci if ((cpr32(StatsAddr) & DumpStats) == 0) 155862306a36Sopenharmony_ci break; 155962306a36Sopenharmony_ci udelay(10); 156062306a36Sopenharmony_ci } 156162306a36Sopenharmony_ci cpw32(StatsAddr, 0); 156262306a36Sopenharmony_ci cpw32(StatsAddr + 4, 0); 156362306a36Sopenharmony_ci cpr32(StatsAddr); 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci i = 0; 156662306a36Sopenharmony_ci tmp_stats[i++] = le64_to_cpu(nic_stats->tx_ok); 156762306a36Sopenharmony_ci tmp_stats[i++] = le64_to_cpu(nic_stats->rx_ok); 156862306a36Sopenharmony_ci tmp_stats[i++] = le64_to_cpu(nic_stats->tx_err); 156962306a36Sopenharmony_ci tmp_stats[i++] = le32_to_cpu(nic_stats->rx_err); 157062306a36Sopenharmony_ci tmp_stats[i++] = le16_to_cpu(nic_stats->rx_fifo); 157162306a36Sopenharmony_ci tmp_stats[i++] = le16_to_cpu(nic_stats->frame_align); 157262306a36Sopenharmony_ci tmp_stats[i++] = le32_to_cpu(nic_stats->tx_ok_1col); 157362306a36Sopenharmony_ci tmp_stats[i++] = le32_to_cpu(nic_stats->tx_ok_mcol); 157462306a36Sopenharmony_ci tmp_stats[i++] = le64_to_cpu(nic_stats->rx_ok_phys); 157562306a36Sopenharmony_ci tmp_stats[i++] = le64_to_cpu(nic_stats->rx_ok_bcast); 157662306a36Sopenharmony_ci tmp_stats[i++] = le32_to_cpu(nic_stats->rx_ok_mcast); 157762306a36Sopenharmony_ci tmp_stats[i++] = le16_to_cpu(nic_stats->tx_abort); 157862306a36Sopenharmony_ci tmp_stats[i++] = le16_to_cpu(nic_stats->tx_underrun); 157962306a36Sopenharmony_ci tmp_stats[i++] = cp->cp_stats.rx_frags; 158062306a36Sopenharmony_ci BUG_ON(i != CP_NUM_STATS); 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci dma_free_coherent(&cp->pdev->dev, sizeof(*nic_stats), nic_stats, dma); 158362306a36Sopenharmony_ci} 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_cistatic const struct ethtool_ops cp_ethtool_ops = { 158662306a36Sopenharmony_ci .get_drvinfo = cp_get_drvinfo, 158762306a36Sopenharmony_ci .get_regs_len = cp_get_regs_len, 158862306a36Sopenharmony_ci .get_sset_count = cp_get_sset_count, 158962306a36Sopenharmony_ci .nway_reset = cp_nway_reset, 159062306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 159162306a36Sopenharmony_ci .get_msglevel = cp_get_msglevel, 159262306a36Sopenharmony_ci .set_msglevel = cp_set_msglevel, 159362306a36Sopenharmony_ci .get_regs = cp_get_regs, 159462306a36Sopenharmony_ci .get_wol = cp_get_wol, 159562306a36Sopenharmony_ci .set_wol = cp_set_wol, 159662306a36Sopenharmony_ci .get_strings = cp_get_strings, 159762306a36Sopenharmony_ci .get_ethtool_stats = cp_get_ethtool_stats, 159862306a36Sopenharmony_ci .get_eeprom_len = cp_get_eeprom_len, 159962306a36Sopenharmony_ci .get_eeprom = cp_get_eeprom, 160062306a36Sopenharmony_ci .set_eeprom = cp_set_eeprom, 160162306a36Sopenharmony_ci .get_ringparam = cp_get_ringparam, 160262306a36Sopenharmony_ci .get_link_ksettings = cp_get_link_ksettings, 160362306a36Sopenharmony_ci .set_link_ksettings = cp_set_link_ksettings, 160462306a36Sopenharmony_ci}; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_cistatic int cp_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) 160762306a36Sopenharmony_ci{ 160862306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 160962306a36Sopenharmony_ci int rc; 161062306a36Sopenharmony_ci unsigned long flags; 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci if (!netif_running(dev)) 161362306a36Sopenharmony_ci return -EINVAL; 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 161662306a36Sopenharmony_ci rc = generic_mii_ioctl(&cp->mii_if, if_mii(rq), cmd, NULL); 161762306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 161862306a36Sopenharmony_ci return rc; 161962306a36Sopenharmony_ci} 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_cistatic int cp_set_mac_address(struct net_device *dev, void *p) 162262306a36Sopenharmony_ci{ 162362306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 162462306a36Sopenharmony_ci struct sockaddr *addr = p; 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 162762306a36Sopenharmony_ci return -EADDRNOTAVAIL; 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci eth_hw_addr_set(dev, addr->sa_data); 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci spin_lock_irq(&cp->lock); 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci cpw8_f(Cfg9346, Cfg9346_Unlock); 163462306a36Sopenharmony_ci cpw32_f(MAC0 + 0, le32_to_cpu (*(__le32 *) (dev->dev_addr + 0))); 163562306a36Sopenharmony_ci cpw32_f(MAC0 + 4, le32_to_cpu (*(__le32 *) (dev->dev_addr + 4))); 163662306a36Sopenharmony_ci cpw8_f(Cfg9346, Cfg9346_Lock); 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci spin_unlock_irq(&cp->lock); 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci return 0; 164162306a36Sopenharmony_ci} 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci/* Serial EEPROM section. */ 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci/* EEPROM_Ctrl bits. */ 164662306a36Sopenharmony_ci#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */ 164762306a36Sopenharmony_ci#define EE_CS 0x08 /* EEPROM chip select. */ 164862306a36Sopenharmony_ci#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */ 164962306a36Sopenharmony_ci#define EE_WRITE_0 0x00 165062306a36Sopenharmony_ci#define EE_WRITE_1 0x02 165162306a36Sopenharmony_ci#define EE_DATA_READ 0x01 /* EEPROM chip data out. */ 165262306a36Sopenharmony_ci#define EE_ENB (0x80 | EE_CS) 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci/* Delay between EEPROM clock transitions. 165562306a36Sopenharmony_ci No extra delay is needed with 33Mhz PCI, but 66Mhz may change this. 165662306a36Sopenharmony_ci */ 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci#define eeprom_delay() readb(ee_addr) 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci/* The EEPROM commands include the alway-set leading bit. */ 166162306a36Sopenharmony_ci#define EE_EXTEND_CMD (4) 166262306a36Sopenharmony_ci#define EE_WRITE_CMD (5) 166362306a36Sopenharmony_ci#define EE_READ_CMD (6) 166462306a36Sopenharmony_ci#define EE_ERASE_CMD (7) 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci#define EE_EWDS_ADDR (0) 166762306a36Sopenharmony_ci#define EE_WRAL_ADDR (1) 166862306a36Sopenharmony_ci#define EE_ERAL_ADDR (2) 166962306a36Sopenharmony_ci#define EE_EWEN_ADDR (3) 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci#define CP_EEPROM_MAGIC PCI_DEVICE_ID_REALTEK_8139 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_cistatic void eeprom_cmd_start(void __iomem *ee_addr) 167462306a36Sopenharmony_ci{ 167562306a36Sopenharmony_ci writeb (EE_ENB & ~EE_CS, ee_addr); 167662306a36Sopenharmony_ci writeb (EE_ENB, ee_addr); 167762306a36Sopenharmony_ci eeprom_delay (); 167862306a36Sopenharmony_ci} 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_cistatic void eeprom_cmd(void __iomem *ee_addr, int cmd, int cmd_len) 168162306a36Sopenharmony_ci{ 168262306a36Sopenharmony_ci int i; 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci /* Shift the command bits out. */ 168562306a36Sopenharmony_ci for (i = cmd_len - 1; i >= 0; i--) { 168662306a36Sopenharmony_ci int dataval = (cmd & (1 << i)) ? EE_DATA_WRITE : 0; 168762306a36Sopenharmony_ci writeb (EE_ENB | dataval, ee_addr); 168862306a36Sopenharmony_ci eeprom_delay (); 168962306a36Sopenharmony_ci writeb (EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); 169062306a36Sopenharmony_ci eeprom_delay (); 169162306a36Sopenharmony_ci } 169262306a36Sopenharmony_ci writeb (EE_ENB, ee_addr); 169362306a36Sopenharmony_ci eeprom_delay (); 169462306a36Sopenharmony_ci} 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_cistatic void eeprom_cmd_end(void __iomem *ee_addr) 169762306a36Sopenharmony_ci{ 169862306a36Sopenharmony_ci writeb(0, ee_addr); 169962306a36Sopenharmony_ci eeprom_delay (); 170062306a36Sopenharmony_ci} 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_cistatic void eeprom_extend_cmd(void __iomem *ee_addr, int extend_cmd, 170362306a36Sopenharmony_ci int addr_len) 170462306a36Sopenharmony_ci{ 170562306a36Sopenharmony_ci int cmd = (EE_EXTEND_CMD << addr_len) | (extend_cmd << (addr_len - 2)); 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci eeprom_cmd_start(ee_addr); 170862306a36Sopenharmony_ci eeprom_cmd(ee_addr, cmd, 3 + addr_len); 170962306a36Sopenharmony_ci eeprom_cmd_end(ee_addr); 171062306a36Sopenharmony_ci} 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_cistatic u16 read_eeprom (void __iomem *ioaddr, int location, int addr_len) 171362306a36Sopenharmony_ci{ 171462306a36Sopenharmony_ci int i; 171562306a36Sopenharmony_ci u16 retval = 0; 171662306a36Sopenharmony_ci void __iomem *ee_addr = ioaddr + Cfg9346; 171762306a36Sopenharmony_ci int read_cmd = location | (EE_READ_CMD << addr_len); 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci eeprom_cmd_start(ee_addr); 172062306a36Sopenharmony_ci eeprom_cmd(ee_addr, read_cmd, 3 + addr_len); 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci for (i = 16; i > 0; i--) { 172362306a36Sopenharmony_ci writeb (EE_ENB | EE_SHIFT_CLK, ee_addr); 172462306a36Sopenharmony_ci eeprom_delay (); 172562306a36Sopenharmony_ci retval = 172662306a36Sopenharmony_ci (retval << 1) | ((readb (ee_addr) & EE_DATA_READ) ? 1 : 172762306a36Sopenharmony_ci 0); 172862306a36Sopenharmony_ci writeb (EE_ENB, ee_addr); 172962306a36Sopenharmony_ci eeprom_delay (); 173062306a36Sopenharmony_ci } 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci eeprom_cmd_end(ee_addr); 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci return retval; 173562306a36Sopenharmony_ci} 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_cistatic void write_eeprom(void __iomem *ioaddr, int location, u16 val, 173862306a36Sopenharmony_ci int addr_len) 173962306a36Sopenharmony_ci{ 174062306a36Sopenharmony_ci int i; 174162306a36Sopenharmony_ci void __iomem *ee_addr = ioaddr + Cfg9346; 174262306a36Sopenharmony_ci int write_cmd = location | (EE_WRITE_CMD << addr_len); 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci eeprom_extend_cmd(ee_addr, EE_EWEN_ADDR, addr_len); 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci eeprom_cmd_start(ee_addr); 174762306a36Sopenharmony_ci eeprom_cmd(ee_addr, write_cmd, 3 + addr_len); 174862306a36Sopenharmony_ci eeprom_cmd(ee_addr, val, 16); 174962306a36Sopenharmony_ci eeprom_cmd_end(ee_addr); 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci eeprom_cmd_start(ee_addr); 175262306a36Sopenharmony_ci for (i = 0; i < 20000; i++) 175362306a36Sopenharmony_ci if (readb(ee_addr) & EE_DATA_READ) 175462306a36Sopenharmony_ci break; 175562306a36Sopenharmony_ci eeprom_cmd_end(ee_addr); 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci eeprom_extend_cmd(ee_addr, EE_EWDS_ADDR, addr_len); 175862306a36Sopenharmony_ci} 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_cistatic int cp_get_eeprom_len(struct net_device *dev) 176162306a36Sopenharmony_ci{ 176262306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 176362306a36Sopenharmony_ci int size; 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci spin_lock_irq(&cp->lock); 176662306a36Sopenharmony_ci size = read_eeprom(cp->regs, 0, 8) == 0x8129 ? 256 : 128; 176762306a36Sopenharmony_ci spin_unlock_irq(&cp->lock); 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci return size; 177062306a36Sopenharmony_ci} 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_cistatic int cp_get_eeprom(struct net_device *dev, 177362306a36Sopenharmony_ci struct ethtool_eeprom *eeprom, u8 *data) 177462306a36Sopenharmony_ci{ 177562306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 177662306a36Sopenharmony_ci unsigned int addr_len; 177762306a36Sopenharmony_ci u16 val; 177862306a36Sopenharmony_ci u32 offset = eeprom->offset >> 1; 177962306a36Sopenharmony_ci u32 len = eeprom->len; 178062306a36Sopenharmony_ci u32 i = 0; 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci eeprom->magic = CP_EEPROM_MAGIC; 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci spin_lock_irq(&cp->lock); 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci addr_len = read_eeprom(cp->regs, 0, 8) == 0x8129 ? 8 : 6; 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci if (eeprom->offset & 1) { 178962306a36Sopenharmony_ci val = read_eeprom(cp->regs, offset, addr_len); 179062306a36Sopenharmony_ci data[i++] = (u8)(val >> 8); 179162306a36Sopenharmony_ci offset++; 179262306a36Sopenharmony_ci } 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci while (i < len - 1) { 179562306a36Sopenharmony_ci val = read_eeprom(cp->regs, offset, addr_len); 179662306a36Sopenharmony_ci data[i++] = (u8)val; 179762306a36Sopenharmony_ci data[i++] = (u8)(val >> 8); 179862306a36Sopenharmony_ci offset++; 179962306a36Sopenharmony_ci } 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci if (i < len) { 180262306a36Sopenharmony_ci val = read_eeprom(cp->regs, offset, addr_len); 180362306a36Sopenharmony_ci data[i] = (u8)val; 180462306a36Sopenharmony_ci } 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci spin_unlock_irq(&cp->lock); 180762306a36Sopenharmony_ci return 0; 180862306a36Sopenharmony_ci} 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_cistatic int cp_set_eeprom(struct net_device *dev, 181162306a36Sopenharmony_ci struct ethtool_eeprom *eeprom, u8 *data) 181262306a36Sopenharmony_ci{ 181362306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 181462306a36Sopenharmony_ci unsigned int addr_len; 181562306a36Sopenharmony_ci u16 val; 181662306a36Sopenharmony_ci u32 offset = eeprom->offset >> 1; 181762306a36Sopenharmony_ci u32 len = eeprom->len; 181862306a36Sopenharmony_ci u32 i = 0; 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci if (eeprom->magic != CP_EEPROM_MAGIC) 182162306a36Sopenharmony_ci return -EINVAL; 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci spin_lock_irq(&cp->lock); 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci addr_len = read_eeprom(cp->regs, 0, 8) == 0x8129 ? 8 : 6; 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ci if (eeprom->offset & 1) { 182862306a36Sopenharmony_ci val = read_eeprom(cp->regs, offset, addr_len) & 0xff; 182962306a36Sopenharmony_ci val |= (u16)data[i++] << 8; 183062306a36Sopenharmony_ci write_eeprom(cp->regs, offset, val, addr_len); 183162306a36Sopenharmony_ci offset++; 183262306a36Sopenharmony_ci } 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci while (i < len - 1) { 183562306a36Sopenharmony_ci val = (u16)data[i++]; 183662306a36Sopenharmony_ci val |= (u16)data[i++] << 8; 183762306a36Sopenharmony_ci write_eeprom(cp->regs, offset, val, addr_len); 183862306a36Sopenharmony_ci offset++; 183962306a36Sopenharmony_ci } 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci if (i < len) { 184262306a36Sopenharmony_ci val = read_eeprom(cp->regs, offset, addr_len) & 0xff00; 184362306a36Sopenharmony_ci val |= (u16)data[i]; 184462306a36Sopenharmony_ci write_eeprom(cp->regs, offset, val, addr_len); 184562306a36Sopenharmony_ci } 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci spin_unlock_irq(&cp->lock); 184862306a36Sopenharmony_ci return 0; 184962306a36Sopenharmony_ci} 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci/* Put the board into D3cold state and wait for WakeUp signal */ 185262306a36Sopenharmony_cistatic void cp_set_d3_state (struct cp_private *cp) 185362306a36Sopenharmony_ci{ 185462306a36Sopenharmony_ci pci_enable_wake(cp->pdev, PCI_D0, 1); /* Enable PME# generation */ 185562306a36Sopenharmony_ci pci_set_power_state (cp->pdev, PCI_D3hot); 185662306a36Sopenharmony_ci} 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_cistatic netdev_features_t cp_features_check(struct sk_buff *skb, 185962306a36Sopenharmony_ci struct net_device *dev, 186062306a36Sopenharmony_ci netdev_features_t features) 186162306a36Sopenharmony_ci{ 186262306a36Sopenharmony_ci if (skb_shinfo(skb)->gso_size > MSSMask) 186362306a36Sopenharmony_ci features &= ~NETIF_F_TSO; 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci return vlan_features_check(skb, features); 186662306a36Sopenharmony_ci} 186762306a36Sopenharmony_cistatic const struct net_device_ops cp_netdev_ops = { 186862306a36Sopenharmony_ci .ndo_open = cp_open, 186962306a36Sopenharmony_ci .ndo_stop = cp_close, 187062306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 187162306a36Sopenharmony_ci .ndo_set_mac_address = cp_set_mac_address, 187262306a36Sopenharmony_ci .ndo_set_rx_mode = cp_set_rx_mode, 187362306a36Sopenharmony_ci .ndo_get_stats = cp_get_stats, 187462306a36Sopenharmony_ci .ndo_eth_ioctl = cp_ioctl, 187562306a36Sopenharmony_ci .ndo_start_xmit = cp_start_xmit, 187662306a36Sopenharmony_ci .ndo_tx_timeout = cp_tx_timeout, 187762306a36Sopenharmony_ci .ndo_set_features = cp_set_features, 187862306a36Sopenharmony_ci .ndo_change_mtu = cp_change_mtu, 187962306a36Sopenharmony_ci .ndo_features_check = cp_features_check, 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 188262306a36Sopenharmony_ci .ndo_poll_controller = cp_poll_controller, 188362306a36Sopenharmony_ci#endif 188462306a36Sopenharmony_ci}; 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_cistatic int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) 188762306a36Sopenharmony_ci{ 188862306a36Sopenharmony_ci struct net_device *dev; 188962306a36Sopenharmony_ci struct cp_private *cp; 189062306a36Sopenharmony_ci int rc; 189162306a36Sopenharmony_ci void __iomem *regs; 189262306a36Sopenharmony_ci resource_size_t pciaddr; 189362306a36Sopenharmony_ci unsigned int addr_len, i, pci_using_dac; 189462306a36Sopenharmony_ci __le16 addr[ETH_ALEN / 2]; 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci pr_info_once("%s", version); 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci if (pdev->vendor == PCI_VENDOR_ID_REALTEK && 189962306a36Sopenharmony_ci pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pdev->revision < 0x20) { 190062306a36Sopenharmony_ci dev_info(&pdev->dev, 190162306a36Sopenharmony_ci "This (id %04x:%04x rev %02x) is not an 8139C+ compatible chip, use 8139too\n", 190262306a36Sopenharmony_ci pdev->vendor, pdev->device, pdev->revision); 190362306a36Sopenharmony_ci return -ENODEV; 190462306a36Sopenharmony_ci } 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci dev = alloc_etherdev(sizeof(struct cp_private)); 190762306a36Sopenharmony_ci if (!dev) 190862306a36Sopenharmony_ci return -ENOMEM; 190962306a36Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci cp = netdev_priv(dev); 191262306a36Sopenharmony_ci cp->pdev = pdev; 191362306a36Sopenharmony_ci cp->dev = dev; 191462306a36Sopenharmony_ci cp->msg_enable = (debug < 0 ? CP_DEF_MSG_ENABLE : debug); 191562306a36Sopenharmony_ci spin_lock_init (&cp->lock); 191662306a36Sopenharmony_ci cp->mii_if.dev = dev; 191762306a36Sopenharmony_ci cp->mii_if.mdio_read = mdio_read; 191862306a36Sopenharmony_ci cp->mii_if.mdio_write = mdio_write; 191962306a36Sopenharmony_ci cp->mii_if.phy_id = CP_INTERNAL_PHY; 192062306a36Sopenharmony_ci cp->mii_if.phy_id_mask = 0x1f; 192162306a36Sopenharmony_ci cp->mii_if.reg_num_mask = 0x1f; 192262306a36Sopenharmony_ci cp_set_rxbufsize(cp); 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci rc = pci_enable_device(pdev); 192562306a36Sopenharmony_ci if (rc) 192662306a36Sopenharmony_ci goto err_out_free; 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_ci rc = pci_set_mwi(pdev); 192962306a36Sopenharmony_ci if (rc) 193062306a36Sopenharmony_ci goto err_out_disable; 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci rc = pci_request_regions(pdev, DRV_NAME); 193362306a36Sopenharmony_ci if (rc) 193462306a36Sopenharmony_ci goto err_out_mwi; 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci pciaddr = pci_resource_start(pdev, 1); 193762306a36Sopenharmony_ci if (!pciaddr) { 193862306a36Sopenharmony_ci rc = -EIO; 193962306a36Sopenharmony_ci dev_err(&pdev->dev, "no MMIO resource\n"); 194062306a36Sopenharmony_ci goto err_out_res; 194162306a36Sopenharmony_ci } 194262306a36Sopenharmony_ci if (pci_resource_len(pdev, 1) < CP_REGS_SIZE) { 194362306a36Sopenharmony_ci rc = -EIO; 194462306a36Sopenharmony_ci dev_err(&pdev->dev, "MMIO resource (%llx) too small\n", 194562306a36Sopenharmony_ci (unsigned long long)pci_resource_len(pdev, 1)); 194662306a36Sopenharmony_ci goto err_out_res; 194762306a36Sopenharmony_ci } 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_ci /* Configure DMA attributes. */ 195062306a36Sopenharmony_ci if ((sizeof(dma_addr_t) > 4) && 195162306a36Sopenharmony_ci !dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64))) { 195262306a36Sopenharmony_ci pci_using_dac = 1; 195362306a36Sopenharmony_ci } else { 195462306a36Sopenharmony_ci pci_using_dac = 0; 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 195762306a36Sopenharmony_ci if (rc) { 195862306a36Sopenharmony_ci dev_err(&pdev->dev, 195962306a36Sopenharmony_ci "No usable DMA configuration, aborting\n"); 196062306a36Sopenharmony_ci goto err_out_res; 196162306a36Sopenharmony_ci } 196262306a36Sopenharmony_ci } 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci cp->cpcmd = (pci_using_dac ? PCIDAC : 0) | 196562306a36Sopenharmony_ci PCIMulRW | RxChkSum | CpRxOn | CpTxOn; 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci dev->features |= NETIF_F_RXCSUM; 196862306a36Sopenharmony_ci dev->hw_features |= NETIF_F_RXCSUM; 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci regs = ioremap(pciaddr, CP_REGS_SIZE); 197162306a36Sopenharmony_ci if (!regs) { 197262306a36Sopenharmony_ci rc = -EIO; 197362306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot map PCI MMIO (%Lx@%Lx)\n", 197462306a36Sopenharmony_ci (unsigned long long)pci_resource_len(pdev, 1), 197562306a36Sopenharmony_ci (unsigned long long)pciaddr); 197662306a36Sopenharmony_ci goto err_out_res; 197762306a36Sopenharmony_ci } 197862306a36Sopenharmony_ci cp->regs = regs; 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci cp_stop_hw(cp); 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci /* read MAC address from EEPROM */ 198362306a36Sopenharmony_ci addr_len = read_eeprom (regs, 0, 8) == 0x8129 ? 8 : 6; 198462306a36Sopenharmony_ci for (i = 0; i < 3; i++) 198562306a36Sopenharmony_ci addr[i] = cpu_to_le16(read_eeprom (regs, i + 7, addr_len)); 198662306a36Sopenharmony_ci eth_hw_addr_set(dev, (u8 *)addr); 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci dev->netdev_ops = &cp_netdev_ops; 198962306a36Sopenharmony_ci netif_napi_add_weight(dev, &cp->napi, cp_rx_poll, 16); 199062306a36Sopenharmony_ci dev->ethtool_ops = &cp_ethtool_ops; 199162306a36Sopenharmony_ci dev->watchdog_timeo = TX_TIMEOUT; 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | 199462306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci if (pci_using_dac) 199762306a36Sopenharmony_ci dev->features |= NETIF_F_HIGHDMA; 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci dev->hw_features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | 200062306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; 200162306a36Sopenharmony_ci dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | 200262306a36Sopenharmony_ci NETIF_F_HIGHDMA; 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci /* MTU range: 60 - 4096 */ 200562306a36Sopenharmony_ci dev->min_mtu = CP_MIN_MTU; 200662306a36Sopenharmony_ci dev->max_mtu = CP_MAX_MTU; 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci rc = register_netdev(dev); 200962306a36Sopenharmony_ci if (rc) 201062306a36Sopenharmony_ci goto err_out_iomap; 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci netdev_info(dev, "RTL-8139C+ at 0x%p, %pM, IRQ %d\n", 201362306a36Sopenharmony_ci regs, dev->dev_addr, pdev->irq); 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci pci_set_drvdata(pdev, dev); 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci /* enable busmastering and memory-write-invalidate */ 201862306a36Sopenharmony_ci pci_set_master(pdev); 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci if (cp->wol_enabled) 202162306a36Sopenharmony_ci cp_set_d3_state (cp); 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_ci return 0; 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_cierr_out_iomap: 202662306a36Sopenharmony_ci iounmap(regs); 202762306a36Sopenharmony_cierr_out_res: 202862306a36Sopenharmony_ci pci_release_regions(pdev); 202962306a36Sopenharmony_cierr_out_mwi: 203062306a36Sopenharmony_ci pci_clear_mwi(pdev); 203162306a36Sopenharmony_cierr_out_disable: 203262306a36Sopenharmony_ci pci_disable_device(pdev); 203362306a36Sopenharmony_cierr_out_free: 203462306a36Sopenharmony_ci free_netdev(dev); 203562306a36Sopenharmony_ci return rc; 203662306a36Sopenharmony_ci} 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_cistatic void cp_remove_one (struct pci_dev *pdev) 203962306a36Sopenharmony_ci{ 204062306a36Sopenharmony_ci struct net_device *dev = pci_get_drvdata(pdev); 204162306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci unregister_netdev(dev); 204462306a36Sopenharmony_ci iounmap(cp->regs); 204562306a36Sopenharmony_ci if (cp->wol_enabled) 204662306a36Sopenharmony_ci pci_set_power_state (pdev, PCI_D0); 204762306a36Sopenharmony_ci pci_release_regions(pdev); 204862306a36Sopenharmony_ci pci_clear_mwi(pdev); 204962306a36Sopenharmony_ci pci_disable_device(pdev); 205062306a36Sopenharmony_ci free_netdev(dev); 205162306a36Sopenharmony_ci} 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_cistatic int __maybe_unused cp_suspend(struct device *device) 205462306a36Sopenharmony_ci{ 205562306a36Sopenharmony_ci struct net_device *dev = dev_get_drvdata(device); 205662306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 205762306a36Sopenharmony_ci unsigned long flags; 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci if (!netif_running(dev)) 206062306a36Sopenharmony_ci return 0; 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci netif_device_detach (dev); 206362306a36Sopenharmony_ci netif_stop_queue (dev); 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci spin_lock_irqsave (&cp->lock, flags); 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci /* Disable Rx and Tx */ 206862306a36Sopenharmony_ci cpw16 (IntrMask, 0); 206962306a36Sopenharmony_ci cpw8 (Cmd, cpr8 (Cmd) & (~RxOn | ~TxOn)); 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci spin_unlock_irqrestore (&cp->lock, flags); 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_ci device_set_wakeup_enable(device, cp->wol_enabled); 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_ci return 0; 207662306a36Sopenharmony_ci} 207762306a36Sopenharmony_ci 207862306a36Sopenharmony_cistatic int __maybe_unused cp_resume(struct device *device) 207962306a36Sopenharmony_ci{ 208062306a36Sopenharmony_ci struct net_device *dev = dev_get_drvdata(device); 208162306a36Sopenharmony_ci struct cp_private *cp = netdev_priv(dev); 208262306a36Sopenharmony_ci unsigned long flags; 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci if (!netif_running(dev)) 208562306a36Sopenharmony_ci return 0; 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci netif_device_attach (dev); 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci /* FIXME: sh*t may happen if the Rx ring buffer is depleted */ 209062306a36Sopenharmony_ci cp_init_rings_index (cp); 209162306a36Sopenharmony_ci cp_init_hw (cp); 209262306a36Sopenharmony_ci cp_enable_irq(cp); 209362306a36Sopenharmony_ci netif_start_queue (dev); 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci spin_lock_irqsave (&cp->lock, flags); 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci mii_check_media(&cp->mii_if, netif_msg_link(cp), false); 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_ci spin_unlock_irqrestore (&cp->lock, flags); 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci return 0; 210262306a36Sopenharmony_ci} 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_cistatic const struct pci_device_id cp_pci_tbl[] = { 210562306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, PCI_DEVICE_ID_REALTEK_8139), }, 210662306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_TTTECH, PCI_DEVICE_ID_TTTECH_MC322), }, 210762306a36Sopenharmony_ci { }, 210862306a36Sopenharmony_ci}; 210962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cp_pci_tbl); 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(cp_pm_ops, cp_suspend, cp_resume); 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_cistatic struct pci_driver cp_driver = { 211462306a36Sopenharmony_ci .name = DRV_NAME, 211562306a36Sopenharmony_ci .id_table = cp_pci_tbl, 211662306a36Sopenharmony_ci .probe = cp_init_one, 211762306a36Sopenharmony_ci .remove = cp_remove_one, 211862306a36Sopenharmony_ci .driver.pm = &cp_pm_ops, 211962306a36Sopenharmony_ci}; 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_cimodule_pci_driver(cp_driver); 2122