diff options
| author | Breno Leitao <leitao@debian.org> | 2026-08-05 07:52:36 -0700 |
|---|---|---|
| committer | Tejun Heo <tj@kernel.org> | 2026-08-10 11:47:20 -1000 |
| commit | a5bde5d8fde8a8cb28e59a672d5ddc5b9c1e7656 (patch) | |
| tree | 39f5b258386e1127d253d293194e9a2991bf3c34 /drivers/net/ethernet/realtek | |
| download | linux-stable-a5bde5d8fde8a8cb28e59a672d5ddc5b9c1e7656.tar.gz linux-stable-a5bde5d8fde8a8cb28e59a672d5ddc5b9c1e7656.zip | |
workqueue: skip the node_nr_active update for non-unbound workqueuesgrafted
apply_wqattrs_commit() updates node_nr_active->max unconditionally.
wq->node_nr_active[] is only allocated for unbound workqueues, so guard
the call before per-cpu workqueues start using this path.
No functional change: only unbound workqueues reach apply_wqattrs_*()
today.
Signed-off-by: Breno Leitao <leitao@debian.org>
Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'drivers/net/ethernet/realtek')
| -rw-r--r-- | drivers/net/ethernet/realtek/8139cp.c | 2121 | ||||
| -rw-r--r-- | drivers/net/ethernet/realtek/8139too.c | 2642 | ||||
| -rw-r--r-- | drivers/net/ethernet/realtek/Kconfig | 129 | ||||
| -rw-r--r-- | drivers/net/ethernet/realtek/Makefile | 11 | ||||
| -rw-r--r-- | drivers/net/ethernet/realtek/r8169.h | 97 | ||||
| -rw-r--r-- | drivers/net/ethernet/realtek/r8169_firmware.c | 233 | ||||
| -rw-r--r-- | drivers/net/ethernet/realtek/r8169_firmware.h | 39 | ||||
| -rw-r--r-- | drivers/net/ethernet/realtek/r8169_leds.c | 275 | ||||
| -rw-r--r-- | drivers/net/ethernet/realtek/r8169_main.c | 5835 | ||||
| -rw-r--r-- | drivers/net/ethernet/realtek/r8169_phy_config.c | 1380 | ||||
| -rw-r--r-- | drivers/net/ethernet/realtek/rtase/Makefile | 10 | ||||
| -rw-r--r-- | drivers/net/ethernet/realtek/rtase/rtase.h | 366 | ||||
| -rw-r--r-- | drivers/net/ethernet/realtek/rtase/rtase_main.c | 2406 |
13 files changed, 15544 insertions, 0 deletions
diff --git a/drivers/net/ethernet/realtek/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c new file mode 100644 index 000000000..5652da8a1 --- /dev/null +++ b/drivers/net/ethernet/realtek/8139cp.c @@ -0,0 +1,2121 @@ +/* 8139cp.c: A Linux PCI Ethernet driver for the RealTek 8139C+ chips. */ +/* + Copyright 2001-2004 Jeff Garzik <jgarzik@pobox.com> + + Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com) [tg3.c] + Copyright (C) 2000, 2001 David S. Miller (davem@redhat.com) [sungem.c] + Copyright 2001 Manfred Spraul [natsemi.c] + Copyright 1999-2001 by Donald Becker. [natsemi.c] + Written 1997-2001 by Donald Becker. [8139too.c] + Copyright 1998-2001 by Jes Sorensen, <jes@trained-monkey.org>. [acenic.c] + + This software may be used and distributed according to the terms of + the GNU General Public License (GPL), incorporated herein by reference. + Drivers based on or derived from this code fall under the GPL and must + retain the authorship, copyright and license notice. This file is not + a complete program and may only be used when the entire operating + system is licensed under the GPL. + + See the file COPYING in this distribution for more information. + + Contributors: + + Wake-on-LAN support - Felipe Damasio <felipewd@terra.com.br> + PCI suspend/resume - Felipe Damasio <felipewd@terra.com.br> + LinkChg interrupt - Felipe Damasio <felipewd@terra.com.br> + + TODO: + * Test Tx checksumming thoroughly + + Low priority TODO: + * Complete reset on PciErr + * Consider Rx interrupt mitigation using TimerIntr + * Investigate using skb->priority with h/w VLAN priority + * Investigate using High Priority Tx Queue with skb->priority + * Adjust Rx FIFO threshold and Max Rx DMA burst on Rx FIFO error + * Adjust Tx FIFO threshold and Max Tx DMA burst on Tx FIFO error + * Implement Tx software interrupt mitigation via + Tx descriptor bit + * The real minimum of CP_MIN_MTU is 4 bytes. However, + for this to be supported, one must(?) turn on packet padding. + * Support external MII transceivers (patch available) + + NOTES: + * TX checksumming is considered experimental. It is off by + default, use ethtool to turn it on. + + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define DRV_NAME "8139cp" +#define DRV_VERSION "1.3" +#define DRV_RELDATE "Mar 22, 2004" + + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/compiler.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/ethtool.h> +#include <linux/gfp.h> +#include <linux/mii.h> +#include <linux/if_vlan.h> +#include <linux/crc32.h> +#include <linux/in.h> +#include <linux/ip.h> +#include <linux/tcp.h> +#include <linux/udp.h> +#include <linux/cache.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <linux/uaccess.h> + +/* These identify the driver base version and may not be removed. */ +static char version[] = +DRV_NAME ": 10/100 PCI Ethernet driver v" DRV_VERSION " (" DRV_RELDATE ")\n"; + +MODULE_AUTHOR("Jeff Garzik <jgarzik@pobox.com>"); +MODULE_DESCRIPTION("RealTek RTL-8139C+ series 10/100 PCI Ethernet driver"); +MODULE_VERSION(DRV_VERSION); +MODULE_LICENSE("GPL"); + +static int debug = -1; +module_param(debug, int, 0); +MODULE_PARM_DESC (debug, "8139cp: bitmapped message enable number"); + +/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). + The RTL chips use a 64 element hash table based on the Ethernet CRC. */ +static int multicast_filter_limit = 32; +module_param(multicast_filter_limit, int, 0); +MODULE_PARM_DESC (multicast_filter_limit, "8139cp: maximum number of filtered multicast addresses"); + +#define CP_DEF_MSG_ENABLE (NETIF_MSG_DRV | \ + NETIF_MSG_PROBE | \ + NETIF_MSG_LINK) +#define CP_NUM_STATS 14 /* struct cp_dma_stats, plus one */ +#define CP_STATS_SIZE 64 /* size in bytes of DMA stats block */ +#define CP_REGS_SIZE (0xff + 1) +#define CP_REGS_VER 1 /* version 1 */ +#define CP_RX_RING_SIZE 64 +#define CP_TX_RING_SIZE 64 +#define CP_RING_BYTES \ + ((sizeof(struct cp_desc) * CP_RX_RING_SIZE) + \ + (sizeof(struct cp_desc) * CP_TX_RING_SIZE) + \ + CP_STATS_SIZE) +#define NEXT_TX(N) (((N) + 1) & (CP_TX_RING_SIZE - 1)) +#define NEXT_RX(N) (((N) + 1) & (CP_RX_RING_SIZE - 1)) +#define TX_BUFFS_AVAIL(CP) \ + (((CP)->tx_tail <= (CP)->tx_head) ? \ + (CP)->tx_tail + (CP_TX_RING_SIZE - 1) - (CP)->tx_head : \ + (CP)->tx_tail - (CP)->tx_head - 1) + +#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ +#define CP_INTERNAL_PHY 32 + +/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. */ +#define RX_FIFO_THRESH 5 /* Rx buffer level before first PCI xfer. */ +#define RX_DMA_BURST 4 /* Maximum PCI burst, '4' is 256 */ +#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ +#define TX_EARLY_THRESH 256 /* Early Tx threshold, in bytes */ + +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (6*HZ) + +/* hardware minimum and maximum for a single frame's data payload */ +#define CP_MIN_MTU 60 /* TODO: allow lower, but pad */ +#define CP_MAX_MTU 4096 + +enum { + /* NIC register offsets */ + MAC0 = 0x00, /* Ethernet hardware address. */ + MAR0 = 0x08, /* Multicast filter. */ + StatsAddr = 0x10, /* 64-bit start addr of 64-byte DMA stats blk */ + TxRingAddr = 0x20, /* 64-bit start addr of Tx ring */ + HiTxRingAddr = 0x28, /* 64-bit start addr of high priority Tx ring */ + Cmd = 0x37, /* Command register */ + IntrMask = 0x3C, /* Interrupt mask */ + IntrStatus = 0x3E, /* Interrupt status */ + TxConfig = 0x40, /* Tx configuration */ + ChipVersion = 0x43, /* 8-bit chip version, inside TxConfig */ + RxConfig = 0x44, /* Rx configuration */ + RxMissed = 0x4C, /* 24 bits valid, write clears */ + Cfg9346 = 0x50, /* EEPROM select/control; Cfg reg [un]lock */ + Config1 = 0x52, /* Config1 */ + Config3 = 0x59, /* Config3 */ + Config4 = 0x5A, /* Config4 */ + MultiIntr = 0x5C, /* Multiple interrupt select */ + BasicModeCtrl = 0x62, /* MII BMCR */ + BasicModeStatus = 0x64, /* MII BMSR */ + NWayAdvert = 0x66, /* MII ADVERTISE */ + NWayLPAR = 0x68, /* MII LPA */ + NWayExpansion = 0x6A, /* MII Expansion */ + TxDmaOkLowDesc = 0x82, /* Low 16 bit address of a Tx descriptor. */ + Config5 = 0xD8, /* Config5 */ + TxPoll = 0xD9, /* Tell chip to check Tx descriptors for work */ + RxMaxSize = 0xDA, /* Max size of an Rx packet (8169 only) */ + CpCmd = 0xE0, /* C+ Command register (C+ mode only) */ + IntrMitigate = 0xE2, /* rx/tx interrupt mitigation control */ + RxRingAddr = 0xE4, /* 64-bit start addr of Rx ring */ + TxThresh = 0xEC, /* Early Tx threshold */ + OldRxBufAddr = 0x30, /* DMA address of Rx ring buffer (C mode) */ + OldTSD0 = 0x10, /* DMA address of first Tx desc (C mode) */ + + /* Tx and Rx status descriptors */ + DescOwn = (1 << 31), /* Descriptor is owned by NIC */ + RingEnd = (1 << 30), /* End of descriptor ring */ + FirstFrag = (1 << 29), /* First segment of a packet */ + LastFrag = (1 << 28), /* Final segment of a packet */ + LargeSend = (1 << 27), /* TCP Large Send Offload (TSO) */ + MSSShift = 16, /* MSS value position */ + MSSMask = 0x7ff, /* MSS value: 11 bits */ + TxError = (1 << 23), /* Tx error summary */ + RxError = (1 << 20), /* Rx error summary */ + IPCS = (1 << 18), /* Calculate IP checksum */ + UDPCS = (1 << 17), /* Calculate UDP/IP checksum */ + TCPCS = (1 << 16), /* Calculate TCP/IP checksum */ + TxVlanTag = (1 << 17), /* Add VLAN tag */ + RxVlanTagged = (1 << 16), /* Rx VLAN tag available */ + IPFail = (1 << 15), /* IP checksum failed */ + UDPFail = (1 << 14), /* UDP/IP checksum failed */ + TCPFail = (1 << 13), /* TCP/IP checksum failed */ + NormalTxPoll = (1 << 6), /* One or more normal Tx packets to send */ + PID1 = (1 << 17), /* 2 protocol id bits: 0==non-IP, */ + PID0 = (1 << 16), /* 1==UDP/IP, 2==TCP/IP, 3==IP */ + RxProtoTCP = 1, + RxProtoUDP = 2, + RxProtoIP = 3, + TxFIFOUnder = (1 << 25), /* Tx FIFO underrun */ + TxOWC = (1 << 22), /* Tx Out-of-window collision */ + TxLinkFail = (1 << 21), /* Link failed during Tx of packet */ + TxMaxCol = (1 << 20), /* Tx aborted due to excessive collisions */ + TxColCntShift = 16, /* Shift, to get 4-bit Tx collision cnt */ + TxColCntMask = 0x01 | 0x02 | 0x04 | 0x08, /* 4-bit collision count */ + RxErrFrame = (1 << 27), /* Rx frame alignment error */ + RxMcast = (1 << 26), /* Rx multicast packet rcv'd */ + RxErrCRC = (1 << 18), /* Rx CRC error */ + RxErrRunt = (1 << 19), /* Rx error, packet < 64 bytes */ + RxErrLong = (1 << 21), /* Rx error, packet > 4096 bytes */ + RxErrFIFO = (1 << 22), /* Rx error, FIFO overflowed, pkt bad */ + + /* StatsAddr register */ + DumpStats = (1 << 3), /* Begin stats dump */ + + /* RxConfig register */ + RxCfgFIFOShift = 13, /* Shift, to get Rx FIFO thresh value */ + RxCfgDMAShift = 8, /* Shift, to get Rx Max DMA value */ + AcceptErr = 0x20, /* Accept packets with CRC errors */ + AcceptRunt = 0x10, /* Accept runt (<64 bytes) packets */ + AcceptBroadcast = 0x08, /* Accept broadcast packets */ + AcceptMulticast = 0x04, /* Accept multicast packets */ + AcceptMyPhys = 0x02, /* Accept pkts with our MAC as dest */ + AcceptAllPhys = 0x01, /* Accept all pkts w/ physical dest */ + + /* IntrMask / IntrStatus registers */ + PciErr = (1 << 15), /* System error on the PCI bus */ + TimerIntr = (1 << 14), /* Asserted when TCTR reaches TimerInt value */ + LenChg = (1 << 13), /* Cable length change */ + SWInt = (1 << 8), /* Software-requested interrupt */ + TxEmpty = (1 << 7), /* No Tx descriptors available */ + RxFIFOOvr = (1 << 6), /* Rx FIFO Overflow */ + LinkChg = (1 << 5), /* Packet underrun, or link change */ + RxEmpty = (1 << 4), /* No Rx descriptors available */ + TxErr = (1 << 3), /* Tx error */ + TxOK = (1 << 2), /* Tx packet sent */ + RxErr = (1 << 1), /* Rx error */ + RxOK = (1 << 0), /* Rx packet received */ + IntrResvd = (1 << 10), /* reserved, according to RealTek engineers, + but hardware likes to raise it */ + + IntrAll = PciErr | TimerIntr | LenChg | SWInt | TxEmpty | + RxFIFOOvr | LinkChg | RxEmpty | TxErr | TxOK | + RxErr | RxOK | IntrResvd, + + /* C mode command register */ + CmdReset = (1 << 4), /* Enable to reset; self-clearing */ + RxOn = (1 << 3), /* Rx mode enable */ + TxOn = (1 << 2), /* Tx mode enable */ + + /* C+ mode command register */ + RxVlanOn = (1 << 6), /* Rx VLAN de-tagging enable */ + RxChkSum = (1 << 5), /* Rx checksum offload enable */ + PCIDAC = (1 << 4), /* PCI Dual Address Cycle (64-bit PCI) */ + PCIMulRW = (1 << 3), /* Enable PCI read/write multiple */ + CpRxOn = (1 << 1), /* Rx mode enable */ + CpTxOn = (1 << 0), /* Tx mode enable */ + + /* Cfg9436 EEPROM control register */ + Cfg9346_Lock = 0x00, /* Lock ConfigX/MII register access */ + Cfg9346_Unlock = 0xC0, /* Unlock ConfigX/MII register access */ + + /* TxConfig register */ + IFG = (1 << 25) | (1 << 24), /* standard IEEE interframe gap */ + TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */ + + /* Early Tx Threshold register */ + TxThreshMask = 0x3f, /* Mask bits 5-0 */ + TxThreshMax = 2048, /* Max early Tx threshold */ + + /* Config1 register */ + DriverLoaded = (1 << 5), /* Software marker, driver is loaded */ + LWACT = (1 << 4), /* LWAKE active mode */ + PMEnable = (1 << 0), /* Enable various PM features of chip */ + + /* Config3 register */ + PARMEnable = (1 << 6), /* Enable auto-loading of PHY parms */ + MagicPacket = (1 << 5), /* Wake up when receives a Magic Packet */ + LinkUp = (1 << 4), /* Wake up when the cable connection is re-established */ + + /* Config4 register */ + LWPTN = (1 << 1), /* LWAKE Pattern */ + LWPME = (1 << 4), /* LANWAKE vs PMEB */ + + /* Config5 register */ + BWF = (1 << 6), /* Accept Broadcast wakeup frame */ + MWF = (1 << 5), /* Accept Multicast wakeup frame */ + UWF = (1 << 4), /* Accept Unicast wakeup frame */ + LANWake = (1 << 1), /* Enable LANWake signal */ + PMEStatus = (1 << 0), /* PME status can be reset by PCI RST# */ + + cp_norx_intr_mask = PciErr | LinkChg | TxOK | TxErr | TxEmpty, + cp_rx_intr_mask = RxOK | RxErr | RxEmpty | RxFIFOOvr, + cp_intr_mask = cp_rx_intr_mask | cp_norx_intr_mask, +}; + +static const unsigned int cp_rx_config = + (RX_FIFO_THRESH << RxCfgFIFOShift) | + (RX_DMA_BURST << RxCfgDMAShift); + +struct cp_desc { + __le32 opts1; + __le32 opts2; + __le64 addr; +}; + +struct cp_dma_stats { + __le64 tx_ok; + __le64 rx_ok; + __le64 tx_err; + __le32 rx_err; + __le16 rx_fifo; + __le16 frame_align; + __le32 tx_ok_1col; + __le32 tx_ok_mcol; + __le64 rx_ok_phys; + __le64 rx_ok_bcast; + __le32 rx_ok_mcast; + __le16 tx_abort; + __le16 tx_underrun; +} __packed; + +struct cp_extra_stats { + unsigned long rx_frags; +}; + +struct cp_private { + void __iomem *regs; + struct net_device *dev; + spinlock_t lock; + u32 msg_enable; + + struct napi_struct napi; + + struct pci_dev *pdev; + u32 rx_config; + u16 cpcmd; + + struct cp_extra_stats cp_stats; + + unsigned rx_head ____cacheline_aligned; + unsigned rx_tail; + struct cp_desc *rx_ring; + struct sk_buff *rx_skb[CP_RX_RING_SIZE]; + + unsigned tx_head ____cacheline_aligned; + unsigned tx_tail; + struct cp_desc *tx_ring; + struct sk_buff *tx_skb[CP_TX_RING_SIZE]; + u32 tx_opts[CP_TX_RING_SIZE]; + + unsigned rx_buf_sz; + unsigned wol_enabled : 1; /* Is Wake-on-LAN enabled? */ + + dma_addr_t ring_dma; + + struct mii_if_info mii_if; +}; + +#define cpr8(reg) readb(cp->regs + (reg)) +#define cpr16(reg) readw(cp->regs + (reg)) +#define cpr32(reg) readl(cp->regs + (reg)) +#define cpw8(reg,val) writeb((val), cp->regs + (reg)) +#define cpw16(reg,val) writew((val), cp->regs + (reg)) +#define cpw32(reg,val) writel((val), cp->regs + (reg)) +#define cpw8_f(reg,val) do { \ + writeb((val), cp->regs + (reg)); \ + readb(cp->regs + (reg)); \ + } while (0) +#define cpw16_f(reg,val) do { \ + writew((val), cp->regs + (reg)); \ + readw(cp->regs + (reg)); \ + } while (0) +#define cpw32_f(reg,val) do { \ + writel((val), cp->regs + (reg)); \ + readl(cp->regs + (reg)); \ + } while (0) + + +static void __cp_set_rx_mode (struct net_device *dev); +static void cp_tx (struct cp_private *cp); +static void cp_clean_rings (struct cp_private *cp); +#ifdef CONFIG_NET_POLL_CONTROLLER +static void cp_poll_controller(struct net_device *dev); +#endif +static int cp_get_eeprom_len(struct net_device *dev); +static int cp_get_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 *data); +static int cp_set_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 *data); + +static struct { + const char str[ETH_GSTRING_LEN]; +} ethtool_stats_keys[] = { + { "tx_ok" }, + { "rx_ok" }, + { "tx_err" }, + { "rx_err" }, + { "rx_fifo" }, + { "frame_align" }, + { "tx_ok_1col" }, + { "tx_ok_mcol" }, + { "rx_ok_phys" }, + { "rx_ok_bcast" }, + { "rx_ok_mcast" }, + { "tx_abort" }, + { "tx_underrun" }, + { "rx_frags" }, +}; + + +static inline void cp_set_rxbufsize (struct cp_private *cp) +{ + unsigned int mtu = cp->dev->mtu; + + if (mtu > ETH_DATA_LEN) + /* MTU + ethernet header + FCS + optional VLAN tag */ + cp->rx_buf_sz = mtu + ETH_HLEN + 8; + else + cp->rx_buf_sz = PKT_BUF_SZ; +} + +static inline void cp_rx_skb (struct cp_private *cp, struct sk_buff *skb, + struct cp_desc *desc) +{ + u32 opts2 = le32_to_cpu(desc->opts2); + + skb->protocol = eth_type_trans (skb, cp->dev); + + cp->dev->stats.rx_packets++; + cp->dev->stats.rx_bytes += skb->len; + + if (opts2 & RxVlanTagged) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), swab16(opts2 & 0xffff)); + + napi_gro_receive(&cp->napi, skb); +} + +static void cp_rx_err_acct (struct cp_private *cp, unsigned rx_tail, + u32 status, u32 len) +{ + netif_dbg(cp, rx_err, cp->dev, "rx err, slot %d status 0x%x len %d\n", + rx_tail, status, len); + cp->dev->stats.rx_errors++; + if (status & RxErrFrame) + cp->dev->stats.rx_frame_errors++; + if (status & RxErrCRC) + cp->dev->stats.rx_crc_errors++; + if ((status & RxErrRunt) || (status & RxErrLong)) + cp->dev->stats.rx_length_errors++; + if ((status & (FirstFrag | LastFrag)) != (FirstFrag | LastFrag)) + cp->dev->stats.rx_length_errors++; + if (status & RxErrFIFO) + cp->dev->stats.rx_fifo_errors++; +} + +static inline unsigned int cp_rx_csum_ok (u32 status) +{ + unsigned int protocol = (status >> 16) & 0x3; + + if (((protocol == RxProtoTCP) && !(status & TCPFail)) || + ((protocol == RxProtoUDP) && !(status & UDPFail))) + return 1; + else + return 0; +} + +static int cp_rx_poll(struct napi_struct *napi, int budget) +{ + struct cp_private *cp = container_of(napi, struct cp_private, napi); + struct net_device *dev = cp->dev; + unsigned int rx_tail = cp->rx_tail; + int rx = 0; + + cpw16(IntrStatus, cp_rx_intr_mask); + + while (rx < budget) { + u32 status, len; + dma_addr_t mapping, new_mapping; + struct sk_buff *skb, *new_skb; + struct cp_desc *desc; + const unsigned buflen = cp->rx_buf_sz; + + skb = cp->rx_skb[rx_tail]; + BUG_ON(!skb); + + desc = &cp->rx_ring[rx_tail]; + status = le32_to_cpu(desc->opts1); + if (status & DescOwn) + break; + + len = (status & 0x1fff) - 4; + mapping = le64_to_cpu(desc->addr); + + if ((status & (FirstFrag | LastFrag)) != (FirstFrag | LastFrag)) { + /* we don't support incoming fragmented frames. + * instead, we attempt to ensure that the + * pre-allocated RX skbs are properly sized such + * that RX fragments are never encountered + */ + cp_rx_err_acct(cp, rx_tail, status, len); + dev->stats.rx_dropped++; + cp->cp_stats.rx_frags++; + goto rx_next; + } + + if (status & (RxError | RxErrFIFO)) { + cp_rx_err_acct(cp, rx_tail, status, len); + goto rx_next; + } + + netif_dbg(cp, rx_status, dev, "rx slot %d status 0x%x len %d\n", + rx_tail, status, len); + + new_skb = napi_alloc_skb(napi, buflen); + if (!new_skb) { + dev->stats.rx_dropped++; + goto rx_next; + } + + new_mapping = dma_map_single(&cp->pdev->dev, new_skb->data, buflen, + DMA_FROM_DEVICE); + if (dma_mapping_error(&cp->pdev->dev, new_mapping)) { + dev->stats.rx_dropped++; + kfree_skb(new_skb); + goto rx_next; + } + + dma_unmap_single(&cp->pdev->dev, mapping, + buflen, DMA_FROM_DEVICE); + + /* Handle checksum offloading for incoming packets. */ + if (cp_rx_csum_ok(status)) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb_checksum_none_assert(skb); + + skb_put(skb, len); + + cp->rx_skb[rx_tail] = new_skb; + + cp_rx_skb(cp, skb, desc); + rx++; + mapping = new_mapping; + +rx_next: + cp->rx_ring[rx_tail].opts2 = 0; + cp->rx_ring[rx_tail].addr = cpu_to_le64(mapping); + if (rx_tail == (CP_RX_RING_SIZE - 1)) + desc->opts1 = cpu_to_le32(DescOwn | RingEnd | + cp->rx_buf_sz); + else + desc->opts1 = cpu_to_le32(DescOwn | cp->rx_buf_sz); + rx_tail = NEXT_RX(rx_tail); + } + + cp->rx_tail = rx_tail; + + /* if we did not reach work limit, then we're done with + * this round of polling + */ + if (rx < budget && napi_complete_done(napi, rx)) { + unsigned long flags; + + spin_lock_irqsave(&cp->lock, flags); + cpw16_f(IntrMask, cp_intr_mask); + spin_unlock_irqrestore(&cp->lock, flags); + } + + return rx; +} + +static irqreturn_t cp_interrupt (int irq, void *dev_instance) +{ + struct net_device *dev = dev_instance; + struct cp_private *cp; + int handled = 0; + u16 status; + u16 mask; + + if (unlikely(dev == NULL)) + return IRQ_NONE; + cp = netdev_priv(dev); + + spin_lock(&cp->lock); + + mask = cpr16(IntrMask); + if (!mask) + goto out_unlock; + + status = cpr16(IntrStatus); + if (!status || (status == 0xFFFF)) + goto out_unlock; + + handled = 1; + + netif_dbg(cp, intr, dev, "intr, status %04x cmd %02x cpcmd %04x\n", + status, cpr8(Cmd), cpr16(CpCmd)); + + cpw16(IntrStatus, status & ~cp_rx_intr_mask); + + /* close possible race's with dev_close */ + if (unlikely(!netif_running(dev))) { + cpw16(IntrMask, 0); + goto out_unlock; + } + + if (status & (RxOK | RxErr | RxEmpty | RxFIFOOvr)) + if (napi_schedule_prep(&cp->napi)) { + cpw16_f(IntrMask, cp_norx_intr_mask); + __napi_schedule(&cp->napi); + } + + if (status & (TxOK | TxErr | TxEmpty | SWInt)) + cp_tx(cp); + if (status & LinkChg) + mii_check_media(&cp->mii_if, netif_msg_link(cp), false); + + + if (status & PciErr) { + u16 pci_status; + + pci_read_config_word(cp->pdev, PCI_STATUS, &pci_status); + pci_write_config_word(cp->pdev, PCI_STATUS, pci_status); + netdev_err(dev, "PCI bus error, status=%04x, PCI status=%04x\n", + status, pci_status); + + /* TODO: reset hardware */ + } + +out_unlock: + spin_unlock(&cp->lock); + + return IRQ_RETVAL(handled); +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +/* + * Polling receive - used by netconsole and other diagnostic tools + * to allow network i/o with interrupts disabled. + */ +static void cp_poll_controller(struct net_device *dev) +{ + struct cp_private *cp = netdev_priv(dev); + const int irq = cp->pdev->irq; + + disable_irq(irq); + cp_interrupt(irq, dev); + enable_irq(irq); +} +#endif + +static void cp_tx (struct cp_private *cp) +{ + unsigned tx_head = cp->tx_head; + unsigned tx_tail = cp->tx_tail; + unsigned bytes_compl = 0, pkts_compl = 0; + + while (tx_tail != tx_head) { + struct cp_desc *txd = cp->tx_ring + tx_tail; + struct sk_buff *skb; + u32 status; + + rmb(); + status = le32_to_cpu(txd->opts1); + if (status & DescOwn) + break; + + skb = cp->tx_skb[tx_tail]; + BUG_ON(!skb); + + dma_unmap_single(&cp->pdev->dev, le64_to_cpu(txd->addr), + cp->tx_opts[tx_tail] & 0xffff, + DMA_TO_DEVICE); + + if (status & LastFrag) { + if (status & (TxError | TxFIFOUnder)) { + netif_dbg(cp, tx_err, cp->dev, + "tx err, status 0x%x\n", status); + cp->dev->stats.tx_errors++; + if (status & TxOWC) + cp->dev->stats.tx_window_errors++; + if (status & TxMaxCol) + cp->dev->stats.tx_aborted_errors++; + if (status & TxLinkFail) + cp->dev->stats.tx_carrier_errors++; + if (status & TxFIFOUnder) + cp->dev->stats.tx_fifo_errors++; + } else { + cp->dev->stats.collisions += + ((status >> TxColCntShift) & TxColCntMask); + cp->dev->stats.tx_packets++; + cp->dev->stats.tx_bytes += skb->len; + netif_dbg(cp, tx_done, cp->dev, + "tx done, slot %d\n", tx_tail); + } + bytes_compl += skb->len; + pkts_compl++; + dev_consume_skb_irq(skb); + } + + cp->tx_skb[tx_tail] = NULL; + + tx_tail = NEXT_TX(tx_tail); + } + + cp->tx_tail = tx_tail; + + netdev_completed_queue(cp->dev, pkts_compl, bytes_compl); + if (TX_BUFFS_AVAIL(cp) > (MAX_SKB_FRAGS + 1)) + netif_wake_queue(cp->dev); +} + +static inline u32 cp_tx_vlan_tag(struct sk_buff *skb) +{ + return skb_vlan_tag_present(skb) ? + TxVlanTag | swab16(skb_vlan_tag_get(skb)) : 0x00; +} + +static void unwind_tx_frag_mapping(struct cp_private *cp, struct sk_buff *skb, + int first, int entry_last) +{ + int frag, index; + struct cp_desc *txd; + skb_frag_t *this_frag; + for (frag = 0; frag+first < entry_last; frag++) { + index = first+frag; + cp->tx_skb[index] = NULL; + txd = &cp->tx_ring[index]; + this_frag = &skb_shinfo(skb)->frags[frag]; + dma_unmap_single(&cp->pdev->dev, le64_to_cpu(txd->addr), + skb_frag_size(this_frag), DMA_TO_DEVICE); + } +} + +static netdev_tx_t cp_start_xmit (struct sk_buff *skb, + struct net_device *dev) +{ + struct cp_private *cp = netdev_priv(dev); + unsigned entry; + u32 eor, opts1; + unsigned long intr_flags; + __le32 opts2; + int mss = 0; + + spin_lock_irqsave(&cp->lock, intr_flags); + + /* This is a hard error, log it. */ + if (TX_BUFFS_AVAIL(cp) <= (skb_shinfo(skb)->nr_frags + 1)) { + netif_stop_queue(dev); + spin_unlock_irqrestore(&cp->lock, intr_flags); + netdev_err(dev, "BUG! Tx Ring full when queue awake!\n"); + return NETDEV_TX_BUSY; + } + + entry = cp->tx_head; + eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0; + mss = skb_shinfo(skb)->gso_size; + + if (mss > MSSMask) { + netdev_WARN_ONCE(dev, "Net bug: GSO size %d too large for 8139CP\n", + mss); + goto out_dma_error; + } + + opts2 = cpu_to_le32(cp_tx_vlan_tag(skb)); + opts1 = DescOwn; + if (mss) + opts1 |= LargeSend | (mss << MSSShift); + else if (skb->ip_summed == CHECKSUM_PARTIAL) { + const struct iphdr *ip = ip_hdr(skb); + if (ip->protocol == IPPROTO_TCP) + opts1 |= IPCS | TCPCS; + else if (ip->protocol == IPPROTO_UDP) + opts1 |= IPCS | UDPCS; + else { + WARN_ONCE(1, + "Net bug: asked to checksum invalid Legacy IP packet\n"); + goto out_dma_error; + } + } + + if (skb_shinfo(skb)->nr_frags == 0) { + struct cp_desc *txd = &cp->tx_ring[entry]; + u32 len; + dma_addr_t mapping; + + len = skb->len; + mapping = dma_map_single(&cp->pdev->dev, skb->data, len, DMA_TO_DEVICE); + if (dma_mapping_error(&cp->pdev->dev, mapping)) + goto out_dma_error; + + txd->opts2 = opts2; + txd->addr = cpu_to_le64(mapping); + wmb(); + + opts1 |= eor | len | FirstFrag | LastFrag; + + txd->opts1 = cpu_to_le32(opts1); + wmb(); + + cp->tx_skb[entry] = skb; + cp->tx_opts[entry] = opts1; + netif_dbg(cp, tx_queued, cp->dev, "tx queued, slot %d, skblen %d\n", + entry, skb->len); + } else { + struct cp_desc *txd; + u32 first_len, first_eor, ctrl; + dma_addr_t first_mapping; + int frag, first_entry = entry; + + /* We must give this initial chunk to the device last. + * Otherwise we could race with the device. + */ + first_eor = eor; + first_len = skb_headlen(skb); + first_mapping = dma_map_single(&cp->pdev->dev, skb->data, + first_len, DMA_TO_DEVICE); + if (dma_mapping_error(&cp->pdev->dev, first_mapping)) + goto out_dma_error; + + cp->tx_skb[entry] = skb; + + for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) { + const skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag]; + u32 len; + dma_addr_t mapping; + + entry = NEXT_TX(entry); + + len = skb_frag_size(this_frag); + mapping = dma_map_single(&cp->pdev->dev, + skb_frag_address(this_frag), + len, DMA_TO_DEVICE); + if (dma_mapping_error(&cp->pdev->dev, mapping)) { + unwind_tx_frag_mapping(cp, skb, first_entry, entry); + goto out_dma_error; + } + + eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0; + + ctrl = opts1 | eor | len; + + if (frag == skb_shinfo(skb)->nr_frags - 1) + ctrl |= LastFrag; + + txd = &cp->tx_ring[entry]; + txd->opts2 = opts2; + txd->addr = cpu_to_le64(mapping); + wmb(); + + txd->opts1 = cpu_to_le32(ctrl); + wmb(); + + cp->tx_opts[entry] = ctrl; + cp->tx_skb[entry] = skb; + } + + txd = &cp->tx_ring[first_entry]; + txd->opts2 = opts2; + txd->addr = cpu_to_le64(first_mapping); + wmb(); + + ctrl = opts1 | first_eor | first_len | FirstFrag; + txd->opts1 = cpu_to_le32(ctrl); + wmb(); + + cp->tx_opts[first_entry] = ctrl; + netif_dbg(cp, tx_queued, cp->dev, "tx queued, slots %d-%d, skblen %d\n", + first_entry, entry, skb->len); + } + cp->tx_head = NEXT_TX(entry); + + netdev_sent_queue(dev, skb->len); + if (TX_BUFFS_AVAIL(cp) <= (MAX_SKB_FRAGS + 1)) + netif_stop_queue(dev); + +out_unlock: + spin_unlock_irqrestore(&cp->lock, intr_flags); + + cpw8(TxPoll, NormalTxPoll); + + return NETDEV_TX_OK; +out_dma_error: + dev_kfree_skb_any(skb); + cp->dev->stats.tx_dropped++; + goto out_unlock; +} + +/* Set or clear the multicast filter for this adaptor. + This routine is not state sensitive and need not be SMP locked. */ + +static void __cp_set_rx_mode (struct net_device *dev) +{ + struct cp_private *cp = netdev_priv(dev); + u32 mc_filter[2]; /* Multicast hash filter */ + int rx_mode; + + /* Note: do not reorder, GCC is clever about common statements. */ + if (dev->flags & IFF_PROMISC) { + /* Unconditionally log net taps. */ + rx_mode = + AcceptBroadcast | AcceptMulticast | AcceptMyPhys | + AcceptAllPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; + } else if ((netdev_mc_count(dev) > multicast_filter_limit) || + (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter perfectly -- accept all multicasts. */ + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; + } else { + struct netdev_hw_addr *ha; + rx_mode = AcceptBroadcast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0; + netdev_for_each_mc_addr(ha, dev) { + int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; + + mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); + rx_mode |= AcceptMulticast; + } + } + + /* We can safely update without stopping the chip. */ + cp->rx_config = cp_rx_config | rx_mode; + cpw32_f(RxConfig, cp->rx_config); + + cpw32_f (MAR0 + 0, mc_filter[0]); + cpw32_f (MAR0 + 4, mc_filter[1]); +} + +static void cp_set_rx_mode (struct net_device *dev) +{ + unsigned long flags; + struct cp_private *cp = netdev_priv(dev); + + spin_lock_irqsave (&cp->lock, flags); + __cp_set_rx_mode(dev); + spin_unlock_irqrestore (&cp->lock, flags); +} + +static void __cp_get_stats(struct cp_private *cp) +{ + /* only lower 24 bits valid; write any value to clear */ + cp->dev->stats.rx_missed_errors += (cpr32 (RxMissed) & 0xffffff); + cpw32 (RxMissed, 0); +} + +static struct net_device_stats *cp_get_stats(struct net_device *dev) +{ + struct cp_private *cp = netdev_priv(dev); + unsigned long flags; + + /* The chip only need report frame silently dropped. */ + spin_lock_irqsave(&cp->lock, flags); + if (netif_running(dev) && netif_device_present(dev)) + __cp_get_stats(cp); + spin_unlock_irqrestore(&cp->lock, flags); + + return &dev->stats; +} + +static void cp_stop_hw (struct cp_private *cp) +{ + cpw16(IntrStatus, ~(cpr16(IntrStatus))); + cpw16_f(IntrMask, 0); + cpw8(Cmd, 0); + cpw16_f(CpCmd, 0); + cpw16_f(IntrStatus, ~(cpr16(IntrStatus))); + + cp->rx_tail = 0; + cp->tx_head = cp->tx_tail = 0; + + netdev_reset_queue(cp->dev); +} + +static void cp_reset_hw (struct cp_private *cp) +{ + unsigned work = 1000; + + cpw8(Cmd, CmdReset); + + while (work--) { + if (!(cpr8(Cmd) & CmdReset)) + return; + + schedule_timeout_uninterruptible(10); + } + + netdev_err(cp->dev, "hardware reset timeout\n"); +} + +static inline void cp_start_hw (struct cp_private *cp) +{ + dma_addr_t ring_dma; + + cpw16(CpCmd, cp->cpcmd); + + /* + * These (at least TxRingAddr) need to be configured after the + * corresponding bits in CpCmd are enabled. Datasheet v1.6 §6.33 + * (C+ Command Register) recommends that these and more be configured + * *after* the [RT]xEnable bits in CpCmd are set. And on some hardware + * it's been observed that the TxRingAddr is actually reset to garbage + * when C+ mode Tx is enabled in CpCmd. + */ + cpw32_f(HiTxRingAddr, 0); + cpw32_f(HiTxRingAddr + 4, 0); + + ring_dma = cp->ring_dma; + cpw32_f(RxRingAddr, ring_dma & 0xffffffff); + cpw32_f(RxRingAddr + 4, (ring_dma >> 16) >> 16); + + ring_dma += sizeof(struct cp_desc) * CP_RX_RING_SIZE; + cpw32_f(TxRingAddr, ring_dma & 0xffffffff); + cpw32_f(TxRingAddr + 4, (ring_dma >> 16) >> 16); + + /* + * Strictly speaking, the datasheet says this should be enabled + * *before* setting the descriptor addresses. But what, then, would + * prevent it from doing DMA to random unconfigured addresses? + * This variant appears to work fine. + */ + cpw8(Cmd, RxOn | TxOn); + + netdev_reset_queue(cp->dev); +} + +static void cp_enable_irq(struct cp_private *cp) +{ + cpw16_f(IntrMask, cp_intr_mask); +} + +static void cp_init_hw (struct cp_private *cp) +{ + struct net_device *dev = cp->dev; + + cp_reset_hw(cp); + + cpw8_f (Cfg9346, Cfg9346_Unlock); + + /* Restore our idea of the MAC address. */ + cpw32_f (MAC0 + 0, le32_to_cpu (*(__le32 *) (dev->dev_addr + 0))); + cpw32_f (MAC0 + 4, le32_to_cpu (*(__le32 *) (dev->dev_addr + 4))); + + cp_start_hw(cp); + cpw8(TxThresh, 0x06); /* XXX convert magic num to a constant */ + + __cp_set_rx_mode(dev); + cpw32_f (TxConfig, IFG | (TX_DMA_BURST << TxDMAShift)); + + cpw8(Config1, cpr8(Config1) | DriverLoaded | PMEnable); + /* Disable Wake-on-LAN. Can be turned on with ETHTOOL_SWOL */ + cpw8(Config3, PARMEnable); + cp->wol_enabled = 0; + + cpw8(Config5, cpr8(Config5) & PMEStatus); + + cpw16(MultiIntr, 0); + + cpw8_f(Cfg9346, Cfg9346_Lock); +} + +static int cp_refill_rx(struct cp_private *cp) +{ + struct net_device *dev = cp->dev; + unsigned i; + + for (i = 0; i < CP_RX_RING_SIZE; i++) { + struct sk_buff *skb; + dma_addr_t mapping; + + skb = netdev_alloc_skb_ip_align(dev, cp->rx_buf_sz); + if (!skb) + goto err_out; + + mapping = dma_map_single(&cp->pdev->dev, skb->data, + cp->rx_buf_sz, DMA_FROM_DEVICE); + if (dma_mapping_error(&cp->pdev->dev, mapping)) { + kfree_skb(skb); + goto err_out; + } + cp->rx_skb[i] = skb; + + cp->rx_ring[i].opts2 = 0; + cp->rx_ring[i].addr = cpu_to_le64(mapping); + if (i == (CP_RX_RING_SIZE - 1)) + cp->rx_ring[i].opts1 = + cpu_to_le32(DescOwn | RingEnd | cp->rx_buf_sz); + else + cp->rx_ring[i].opts1 = + cpu_to_le32(DescOwn | cp->rx_buf_sz); + } + + return 0; + +err_out: + cp_clean_rings(cp); + return -ENOMEM; +} + +static void cp_init_rings_index (struct cp_private *cp) +{ + cp->rx_tail = 0; + cp->tx_head = cp->tx_tail = 0; +} + +static int cp_init_rings (struct cp_private *cp) +{ + memset(cp->tx_ring, 0, sizeof(struct cp_desc) * CP_TX_RING_SIZE); + cp->tx_ring[CP_TX_RING_SIZE - 1].opts1 = cpu_to_le32(RingEnd); + memset(cp->tx_opts, 0, sizeof(cp->tx_opts)); + + cp_init_rings_index(cp); + + return cp_refill_rx (cp); +} + +static int cp_alloc_rings (struct cp_private *cp) +{ + struct device *d = &cp->pdev->dev; + void *mem; + int rc; + + mem = dma_alloc_coherent(d, CP_RING_BYTES, &cp->ring_dma, GFP_KERNEL); + if (!mem) + return -ENOMEM; + + cp->rx_ring = mem; + cp->tx_ring = &cp->rx_ring[CP_RX_RING_SIZE]; + + rc = cp_init_rings(cp); + if (rc < 0) + dma_free_coherent(d, CP_RING_BYTES, cp->rx_ring, cp->ring_dma); + + return rc; +} + +static void cp_clean_rings (struct cp_private *cp) +{ + struct cp_desc *desc; + unsigned i; + + for (i = 0; i < CP_RX_RING_SIZE; i++) { + if (cp->rx_skb[i]) { + desc = cp->rx_ring + i; + dma_unmap_single(&cp->pdev->dev,le64_to_cpu(desc->addr), + cp->rx_buf_sz, DMA_FROM_DEVICE); + dev_kfree_skb_any(cp->rx_skb[i]); + } + } + + for (i = 0; i < CP_TX_RING_SIZE; i++) { + if (cp->tx_skb[i]) { + struct sk_buff *skb = cp->tx_skb[i]; + + desc = cp->tx_ring + i; + dma_unmap_single(&cp->pdev->dev,le64_to_cpu(desc->addr), + le32_to_cpu(desc->opts1) & 0xffff, + DMA_TO_DEVICE); + if (le32_to_cpu(desc->opts1) & LastFrag) + dev_kfree_skb_any(skb); + cp->dev->stats.tx_dropped++; + } + } + netdev_reset_queue(cp->dev); + + memset(cp->rx_ring, 0, sizeof(struct cp_desc) * CP_RX_RING_SIZE); + memset(cp->tx_ring, 0, sizeof(struct cp_desc) * CP_TX_RING_SIZE); + memset(cp->tx_opts, 0, sizeof(cp->tx_opts)); + + memset(cp->rx_skb, 0, sizeof(struct sk_buff *) * CP_RX_RING_SIZE); + memset(cp->tx_skb, 0, sizeof(struct sk_buff *) * CP_TX_RING_SIZE); +} + +static void cp_free_rings (struct cp_private *cp) +{ + cp_clean_rings(cp); + dma_free_coherent(&cp->pdev->dev, CP_RING_BYTES, cp->rx_ring, + cp->ring_dma); + cp->rx_ring = NULL; + cp->tx_ring = NULL; +} + +static int cp_open (struct net_device *dev) +{ + struct cp_private *cp = netdev_priv(dev); + const int irq = cp->pdev->irq; + int rc; + + netif_dbg(cp, ifup, dev, "enabling interface\n"); + + rc = cp_alloc_rings(cp); + if (rc) + return rc; + + napi_enable(&cp->napi); + + cp_init_hw(cp); + + rc = request_irq(irq, cp_interrupt, IRQF_SHARED, dev->name, dev); + if (rc) + goto err_out_hw; + + cp_enable_irq(cp); + + netif_carrier_off(dev); + mii_check_media(&cp->mii_if, netif_msg_link(cp), true); + netif_start_queue(dev); + + return 0; + +err_out_hw: + napi_disable(&cp->napi); + cp_stop_hw(cp); + cp_free_rings(cp); + return rc; +} + +static int cp_close (struct net_device *dev) +{ + struct cp_private *cp = netdev_priv(dev); + unsigned long flags; + + napi_disable(&cp->napi); + + netif_dbg(cp, ifdown, dev, "disabling interface\n"); + + spin_lock_irqsave(&cp->lock, flags); + + netif_stop_queue(dev); + netif_carrier_off(dev); + + cp_stop_hw(cp); + + spin_unlock_irqrestore(&cp->lock, flags); + + free_irq(cp->pdev->irq, dev); + + cp_free_rings(cp); + return 0; +} + +static void cp_tx_timeout(struct net_device *dev, unsigned int txqueue) +{ + struct cp_private *cp = netdev_priv(dev); + unsigned long flags; + int i; + + netdev_warn(dev, "Transmit timeout, status %2x %4x %4x %4x\n", + cpr8(Cmd), cpr16(CpCmd), + cpr16(IntrStatus), cpr16(IntrMask)); + + spin_lock_irqsave(&cp->lock, flags); + + netif_dbg(cp, tx_err, cp->dev, "TX ring head %d tail %d desc %x\n", + cp->tx_head, cp->tx_tail, cpr16(TxDmaOkLowDesc)); + for (i = 0; i < CP_TX_RING_SIZE; i++) { + netif_dbg(cp, tx_err, cp->dev, + "TX slot %d @%p: %08x (%08x) %08x %llx %p\n", + i, &cp->tx_ring[i], le32_to_cpu(cp->tx_ring[i].opts1), + cp->tx_opts[i], le32_to_cpu(cp->tx_ring[i].opts2), + le64_to_cpu(cp->tx_ring[i].addr), + cp->tx_skb[i]); + } + + cp_stop_hw(cp); + cp_clean_rings(cp); + cp_init_rings(cp); + cp_start_hw(cp); + __cp_set_rx_mode(dev); + cpw16_f(IntrMask, cp_norx_intr_mask); + + netif_wake_queue(dev); + napi_schedule_irqoff(&cp->napi); + + spin_unlock_irqrestore(&cp->lock, flags); +} + +static int cp_change_mtu(struct net_device *dev, int new_mtu) +{ + struct cp_private *cp = netdev_priv(dev); + + /* if network interface not up, no need for complexity */ + if (!netif_running(dev)) { + WRITE_ONCE(dev->mtu, new_mtu); + cp_set_rxbufsize(cp); /* set new rx buf size */ + return 0; + } + + /* network IS up, close it, reset MTU, and come up again. */ + cp_close(dev); + WRITE_ONCE(dev->mtu, new_mtu); + cp_set_rxbufsize(cp); + return cp_open(dev); +} + +static const char mii_2_8139_map[8] = { + BasicModeCtrl, + BasicModeStatus, + 0, + 0, + NWayAdvert, + NWayLPAR, + NWayExpansion, + 0 +}; + +static int mdio_read(struct net_device *dev, int phy_id, int location) +{ + struct cp_private *cp = netdev_priv(dev); + + return location < 8 && mii_2_8139_map[location] ? + readw(cp->regs + mii_2_8139_map[location]) : 0; +} + + +static void mdio_write(struct net_device *dev, int phy_id, int location, + int value) +{ + struct cp_private *cp = netdev_priv(dev); + + if (location == 0) { + cpw8(Cfg9346, Cfg9346_Unlock); + cpw16(BasicModeCtrl, value); + cpw8(Cfg9346, Cfg9346_Lock); + } else if (location < 8 && mii_2_8139_map[location]) + cpw16(mii_2_8139_map[location], value); +} + +/* Set the ethtool Wake-on-LAN settings */ +static int netdev_set_wol (struct cp_private *cp, + const struct ethtool_wolinfo *wol) +{ + u8 options; + + options = cpr8 (Config3) & ~(LinkUp | MagicPacket); + /* If WOL is being disabled, no need for complexity */ + if (wol->wolopts) { + if (wol->wolopts & WAKE_PHY) options |= LinkUp; + if (wol->wolopts & WAKE_MAGIC) options |= MagicPacket; + } + + cpw8 (Cfg9346, Cfg9346_Unlock); + cpw8 (Config3, options); + cpw8 (Cfg9346, Cfg9346_Lock); + + options = 0; /* Paranoia setting */ + options = cpr8 (Config5) & ~(UWF | MWF | BWF); + /* If WOL is being disabled, no need for complexity */ + if (wol->wolopts) { + if (wol->wolopts & WAKE_UCAST) options |= UWF; + if (wol->wolopts & WAKE_BCAST) options |= BWF; + if (wol->wolopts & WAKE_MCAST) options |= MWF; + } + + cpw8 (Config5, options); + + cp->wol_enabled = (wol->wolopts) ? 1 : 0; + + return 0; +} + +/* Get the ethtool Wake-on-LAN settings */ +static void netdev_get_wol (struct cp_private *cp, + struct ethtool_wolinfo *wol) +{ + u8 options; + + wol->wolopts = 0; /* Start from scratch */ + wol->supported = WAKE_PHY | WAKE_BCAST | WAKE_MAGIC | + WAKE_MCAST | WAKE_UCAST; + /* We don't need to go on if WOL is disabled */ + if (!cp->wol_enabled) return; + + options = cpr8 (Config3); + if (options & LinkUp) wol->wolopts |= WAKE_PHY; + if (options & MagicPacket) wol->wolopts |= WAKE_MAGIC; + + options = 0; /* Paranoia setting */ + options = cpr8 (Config5); + if (options & UWF) wol->wolopts |= WAKE_UCAST; + if (options & BWF) wol->wolopts |= WAKE_BCAST; + if (options & MWF) wol->wolopts |= WAKE_MCAST; +} + +static void cp_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info) +{ + struct cp_private *cp = netdev_priv(dev); + + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(cp->pdev), sizeof(info->bus_info)); +} + +static void cp_get_ringparam(struct net_device *dev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kernel_ring, + struct netlink_ext_ack *extack) +{ + ring->rx_max_pending = CP_RX_RING_SIZE; + ring->tx_max_pending = CP_TX_RING_SIZE; + ring->rx_pending = CP_RX_RING_SIZE; + ring->tx_pending = CP_TX_RING_SIZE; +} + +static int cp_get_regs_len(struct net_device *dev) +{ + return CP_REGS_SIZE; +} + +static int cp_get_sset_count (struct net_device *dev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return CP_NUM_STATS; + default: + return -EOPNOTSUPP; + } +} + +static int cp_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) +{ + struct cp_private *cp = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&cp->lock, flags); + mii_ethtool_get_link_ksettings(&cp->mii_if, cmd); + spin_unlock_irqrestore(&cp->lock, flags); + + return 0; +} + +static int cp_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) +{ + struct cp_private *cp = netdev_priv(dev); + int rc; + unsigned long flags; + + spin_lock_irqsave(&cp->lock, flags); + rc = mii_ethtool_set_link_ksettings(&cp->mii_if, cmd); + spin_unlock_irqrestore(&cp->lock, flags); + + return rc; +} + +static int cp_nway_reset(struct net_device *dev) +{ + struct cp_private *cp = netdev_priv(dev); + return mii_nway_restart(&cp->mii_if); +} + +static u32 cp_get_msglevel(struct net_device *dev) +{ + struct cp_private *cp = netdev_priv(dev); + return cp->msg_enable; +} + +static void cp_set_msglevel(struct net_device *dev, u32 value) +{ + struct cp_private *cp = netdev_priv(dev); + cp->msg_enable = value; +} + +static int cp_set_features(struct net_device *dev, netdev_features_t features) +{ + struct cp_private *cp = netdev_priv(dev); + unsigned long flags; + + if (!((dev->features ^ features) & NETIF_F_RXCSUM)) + return 0; + + spin_lock_irqsave(&cp->lock, flags); + + if (features & NETIF_F_RXCSUM) + cp->cpcmd |= RxChkSum; + else + cp->cpcmd &= ~RxChkSum; + + if (features & NETIF_F_HW_VLAN_CTAG_RX) + cp->cpcmd |= RxVlanOn; + else + cp->cpcmd &= ~RxVlanOn; + + cpw16_f(CpCmd, cp->cpcmd); + spin_unlock_irqrestore(&cp->lock, flags); + + return 0; +} + +static void cp_get_regs(struct net_device *dev, struct ethtool_regs *regs, + void *p) +{ + struct cp_private *cp = netdev_priv(dev); + unsigned long flags; + + if (regs->len < CP_REGS_SIZE) + return /* -EINVAL */; + + regs->version = CP_REGS_VER; + + spin_lock_irqsave(&cp->lock, flags); + memcpy_fromio(p, cp->regs, CP_REGS_SIZE); + spin_unlock_irqrestore(&cp->lock, flags); +} + +static void cp_get_wol (struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct cp_private *cp = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave (&cp->lock, flags); + netdev_get_wol (cp, wol); + spin_unlock_irqrestore (&cp->lock, flags); +} + +static int cp_set_wol (struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct cp_private *cp = netdev_priv(dev); + unsigned long flags; + int rc; + + spin_lock_irqsave (&cp->lock, flags); + rc = netdev_set_wol (cp, wol); + spin_unlock_irqrestore (&cp->lock, flags); + + return rc; +} + +static void cp_get_strings (struct net_device *dev, u32 stringset, u8 *buf) +{ + switch (stringset) { + case ETH_SS_STATS: + memcpy(buf, ðtool_stats_keys, sizeof(ethtool_stats_keys)); + break; + default: + BUG(); + break; + } +} + +static void cp_get_ethtool_stats (struct net_device *dev, + struct ethtool_stats *estats, u64 *tmp_stats) +{ + struct cp_private *cp = netdev_priv(dev); + struct cp_dma_stats *nic_stats; + dma_addr_t dma; + int i; + + nic_stats = dma_alloc_coherent(&cp->pdev->dev, sizeof(*nic_stats), + &dma, GFP_KERNEL); + if (!nic_stats) + return; + + /* begin NIC statistics dump */ + cpw32(StatsAddr + 4, (u64)dma >> 32); + cpw32(StatsAddr, ((u64)dma & DMA_BIT_MASK(32)) | DumpStats); + cpr32(StatsAddr); + + for (i = 0; i < 1000; i++) { + if ((cpr32(StatsAddr) & DumpStats) == 0) + break; + udelay(10); + } + cpw32(StatsAddr, 0); + cpw32(StatsAddr + 4, 0); + cpr32(StatsAddr); + + i = 0; + tmp_stats[i++] = le64_to_cpu(nic_stats->tx_ok); + tmp_stats[i++] = le64_to_cpu(nic_stats->rx_ok); + tmp_stats[i++] = le64_to_cpu(nic_stats->tx_err); + tmp_stats[i++] = le32_to_cpu(nic_stats->rx_err); + tmp_stats[i++] = le16_to_cpu(nic_stats->rx_fifo); + tmp_stats[i++] = le16_to_cpu(nic_stats->frame_align); + tmp_stats[i++] = le32_to_cpu(nic_stats->tx_ok_1col); + tmp_stats[i++] = le32_to_cpu(nic_stats->tx_ok_mcol); + tmp_stats[i++] = le64_to_cpu(nic_stats->rx_ok_phys); + tmp_stats[i++] = le64_to_cpu(nic_stats->rx_ok_bcast); + tmp_stats[i++] = le32_to_cpu(nic_stats->rx_ok_mcast); + tmp_stats[i++] = le16_to_cpu(nic_stats->tx_abort); + tmp_stats[i++] = le16_to_cpu(nic_stats->tx_underrun); + tmp_stats[i++] = cp->cp_stats.rx_frags; + BUG_ON(i != CP_NUM_STATS); + + dma_free_coherent(&cp->pdev->dev, sizeof(*nic_stats), nic_stats, dma); +} + +static const struct ethtool_ops cp_ethtool_ops = { + .get_drvinfo = cp_get_drvinfo, + .get_regs_len = cp_get_regs_len, + .get_sset_count = cp_get_sset_count, + .nway_reset = cp_nway_reset, + .get_link = ethtool_op_get_link, + .get_msglevel = cp_get_msglevel, + .set_msglevel = cp_set_msglevel, + .get_regs = cp_get_regs, + .get_wol = cp_get_wol, + .set_wol = cp_set_wol, + .get_strings = cp_get_strings, + .get_ethtool_stats = cp_get_ethtool_stats, + .get_eeprom_len = cp_get_eeprom_len, + .get_eeprom = cp_get_eeprom, + .set_eeprom = cp_set_eeprom, + .get_ringparam = cp_get_ringparam, + .get_link_ksettings = cp_get_link_ksettings, + .set_link_ksettings = cp_set_link_ksettings, +}; + +static int cp_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct cp_private *cp = netdev_priv(dev); + int rc; + unsigned long flags; + + if (!netif_running(dev)) + return -EINVAL; + + spin_lock_irqsave(&cp->lock, flags); + rc = generic_mii_ioctl(&cp->mii_if, if_mii(rq), cmd, NULL); + spin_unlock_irqrestore(&cp->lock, flags); + return rc; +} + +static int cp_set_mac_address(struct net_device *dev, void *p) +{ + struct cp_private *cp = netdev_priv(dev); + struct sockaddr *addr = p; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + eth_hw_addr_set(dev, addr->sa_data); + + spin_lock_irq(&cp->lock); + + cpw8_f(Cfg9346, Cfg9346_Unlock); + cpw32_f(MAC0 + 0, le32_to_cpu (*(__le32 *) (dev->dev_addr + 0))); + cpw32_f(MAC0 + 4, le32_to_cpu (*(__le32 *) (dev->dev_addr + 4))); + cpw8_f(Cfg9346, Cfg9346_Lock); + + spin_unlock_irq(&cp->lock); + + return 0; +} + +/* Serial EEPROM section. */ + +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */ +#define EE_CS 0x08 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x00 +#define EE_WRITE_1 0x02 +#define EE_DATA_READ 0x01 /* EEPROM chip data out. */ +#define EE_ENB (0x80 | EE_CS) + +/* Delay between EEPROM clock transitions. + No extra delay is needed with 33Mhz PCI, but 66Mhz may change this. + */ + +#define eeprom_delay() readb(ee_addr) + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_EXTEND_CMD (4) +#define EE_WRITE_CMD (5) +#define EE_READ_CMD (6) +#define EE_ERASE_CMD (7) + +#define EE_EWDS_ADDR (0) +#define EE_WRAL_ADDR (1) +#define EE_ERAL_ADDR (2) +#define EE_EWEN_ADDR (3) + +#define CP_EEPROM_MAGIC PCI_DEVICE_ID_REALTEK_8139 + +static void eeprom_cmd_start(void __iomem *ee_addr) +{ + writeb (EE_ENB & ~EE_CS, ee_addr); + writeb (EE_ENB, ee_addr); + eeprom_delay (); +} + +static void eeprom_cmd(void __iomem *ee_addr, int cmd, int cmd_len) +{ + int i; + + /* Shift the command bits out. */ + for (i = cmd_len - 1; i >= 0; i--) { + int dataval = (cmd & (1 << i)) ? EE_DATA_WRITE : 0; + writeb (EE_ENB | dataval, ee_addr); + eeprom_delay (); + writeb (EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay (); + } + writeb (EE_ENB, ee_addr); + eeprom_delay (); +} + +static void eeprom_cmd_end(void __iomem *ee_addr) +{ + writeb(0, ee_addr); + eeprom_delay (); +} + +static void eeprom_extend_cmd(void __iomem *ee_addr, int extend_cmd, + int addr_len) +{ + int cmd = (EE_EXTEND_CMD << addr_len) | (extend_cmd << (addr_len - 2)); + + eeprom_cmd_start(ee_addr); + eeprom_cmd(ee_addr, cmd, 3 + addr_len); + eeprom_cmd_end(ee_addr); +} + +static u16 read_eeprom (void __iomem *ioaddr, int location, int addr_len) +{ + int i; + u16 retval = 0; + void __iomem *ee_addr = ioaddr + Cfg9346; + int read_cmd = location | (EE_READ_CMD << addr_len); + + eeprom_cmd_start(ee_addr); + eeprom_cmd(ee_addr, read_cmd, 3 + addr_len); + + for (i = 16; i > 0; i--) { + writeb (EE_ENB | EE_SHIFT_CLK, ee_addr); + eeprom_delay (); + retval = + (retval << 1) | ((readb (ee_addr) & EE_DATA_READ) ? 1 : + 0); + writeb (EE_ENB, ee_addr); + eeprom_delay (); + } + + eeprom_cmd_end(ee_addr); + + return retval; +} + +static void write_eeprom(void __iomem *ioaddr, int location, u16 val, + int addr_len) +{ + int i; + void __iomem *ee_addr = ioaddr + Cfg9346; + int write_cmd = location | (EE_WRITE_CMD << addr_len); + + eeprom_extend_cmd(ee_addr, EE_EWEN_ADDR, addr_len); + + eeprom_cmd_start(ee_addr); + eeprom_cmd(ee_addr, write_cmd, 3 + addr_len); + eeprom_cmd(ee_addr, val, 16); + eeprom_cmd_end(ee_addr); + + eeprom_cmd_start(ee_addr); + for (i = 0; i < 20000; i++) + if (readb(ee_addr) & EE_DATA_READ) + break; + eeprom_cmd_end(ee_addr); + + eeprom_extend_cmd(ee_addr, EE_EWDS_ADDR, addr_len); +} + +static int cp_get_eeprom_len(struct net_device *dev) +{ + struct cp_private *cp = netdev_priv(dev); + int size; + + spin_lock_irq(&cp->lock); + size = read_eeprom(cp->regs, 0, 8) == 0x8129 ? 256 : 128; + spin_unlock_irq(&cp->lock); + + return size; +} + +static int cp_get_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 *data) +{ + struct cp_private *cp = netdev_priv(dev); + unsigned int addr_len; + u16 val; + u32 offset = eeprom->offset >> 1; + u32 len = eeprom->len; + u32 i = 0; + + eeprom->magic = CP_EEPROM_MAGIC; + + spin_lock_irq(&cp->lock); + + addr_len = read_eeprom(cp->regs, 0, 8) == 0x8129 ? 8 : 6; + + if (eeprom->offset & 1) { + val = read_eeprom(cp->regs, offset, addr_len); + data[i++] = (u8)(val >> 8); + offset++; + } + + while (i < len - 1) { + val = read_eeprom(cp->regs, offset, addr_len); + data[i++] = (u8)val; + data[i++] = (u8)(val >> 8); + offset++; + } + + if (i < len) { + val = read_eeprom(cp->regs, offset, addr_len); + data[i] = (u8)val; + } + + spin_unlock_irq(&cp->lock); + return 0; +} + +static int cp_set_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 *data) +{ + struct cp_private *cp = netdev_priv(dev); + unsigned int addr_len; + u16 val; + u32 offset = eeprom->offset >> 1; + u32 len = eeprom->len; + u32 i = 0; + + if (eeprom->magic != CP_EEPROM_MAGIC) + return -EINVAL; + + spin_lock_irq(&cp->lock); + + addr_len = read_eeprom(cp->regs, 0, 8) == 0x8129 ? 8 : 6; + + if (eeprom->offset & 1) { + val = read_eeprom(cp->regs, offset, addr_len) & 0xff; + val |= (u16)data[i++] << 8; + write_eeprom(cp->regs, offset, val, addr_len); + offset++; + } + + while (i < len - 1) { + val = (u16)data[i++]; + val |= (u16)data[i++] << 8; + write_eeprom(cp->regs, offset, val, addr_len); + offset++; + } + + if (i < len) { + val = read_eeprom(cp->regs, offset, addr_len) & 0xff00; + val |= (u16)data[i]; + write_eeprom(cp->regs, offset, val, addr_len); + } + + spin_unlock_irq(&cp->lock); + return 0; +} + +/* Put the board into D3cold state and wait for WakeUp signal */ +static void cp_set_d3_state (struct cp_private *cp) +{ + pci_enable_wake(cp->pdev, PCI_D0, 1); /* Enable PME# generation */ + pci_set_power_state (cp->pdev, PCI_D3hot); +} + +static netdev_features_t cp_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ + if (skb_shinfo(skb)->gso_size > MSSMask) + features &= ~NETIF_F_TSO; + + return vlan_features_check(skb, features); +} +static const struct net_device_ops cp_netdev_ops = { + .ndo_open = cp_open, + .ndo_stop = cp_close, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = cp_set_mac_address, + .ndo_set_rx_mode = cp_set_rx_mode, + .ndo_get_stats = cp_get_stats, + .ndo_eth_ioctl = cp_ioctl, + .ndo_start_xmit = cp_start_xmit, + .ndo_tx_timeout = cp_tx_timeout, + .ndo_set_features = cp_set_features, + .ndo_change_mtu = cp_change_mtu, + .ndo_features_check = cp_features_check, + +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = cp_poll_controller, +#endif +}; + +static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct net_device *dev; + struct cp_private *cp; + int rc; + void __iomem *regs; + resource_size_t pciaddr; + unsigned int addr_len, i, pci_using_dac; + __le16 addr[ETH_ALEN / 2]; + + pr_info_once("%s", version); + + if (pdev->vendor == PCI_VENDOR_ID_REALTEK && + pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pdev->revision < 0x20) { + dev_info(&pdev->dev, + "This (id %04x:%04x rev %02x) is not an 8139C+ compatible chip, use 8139too\n", + pdev->vendor, pdev->device, pdev->revision); + return -ENODEV; + } + + dev = alloc_etherdev(sizeof(struct cp_private)); + if (!dev) + return -ENOMEM; + SET_NETDEV_DEV(dev, &pdev->dev); + + cp = netdev_priv(dev); + cp->pdev = pdev; + cp->dev = dev; + cp->msg_enable = (debug < 0 ? CP_DEF_MSG_ENABLE : debug); + spin_lock_init (&cp->lock); + cp->mii_if.dev = dev; + cp->mii_if.mdio_read = mdio_read; + cp->mii_if.mdio_write = mdio_write; + cp->mii_if.phy_id = CP_INTERNAL_PHY; + cp->mii_if.phy_id_mask = 0x1f; + cp->mii_if.reg_num_mask = 0x1f; + cp_set_rxbufsize(cp); + + rc = pci_enable_device(pdev); + if (rc) + goto err_out_free; + + rc = pci_set_mwi(pdev); + if (rc) + goto err_out_disable; + + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out_mwi; + + pciaddr = pci_resource_start(pdev, 1); + if (!pciaddr) { + rc = -EIO; + dev_err(&pdev->dev, "no MMIO resource\n"); + goto err_out_res; + } + if (pci_resource_len(pdev, 1) < CP_REGS_SIZE) { + rc = -EIO; + dev_err(&pdev->dev, "MMIO resource (%llx) too small\n", + (unsigned long long)pci_resource_len(pdev, 1)); + goto err_out_res; + } + + /* Configure DMA attributes. */ + if ((sizeof(dma_addr_t) > 4) && + !dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64))) { + pci_using_dac = 1; + } else { + pci_using_dac = 0; + + rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (rc) { + dev_err(&pdev->dev, + "No usable DMA configuration, aborting\n"); + goto err_out_res; + } + } + + cp->cpcmd = (pci_using_dac ? PCIDAC : 0) | + PCIMulRW | RxChkSum | CpRxOn | CpTxOn; + + dev->features |= NETIF_F_RXCSUM; + dev->hw_features |= NETIF_F_RXCSUM; + + regs = ioremap(pciaddr, CP_REGS_SIZE); + if (!regs) { + rc = -EIO; + dev_err(&pdev->dev, "Cannot map PCI MMIO (%Lx@%Lx)\n", + (unsigned long long)pci_resource_len(pdev, 1), + (unsigned long long)pciaddr); + goto err_out_res; + } + cp->regs = regs; + + cp_stop_hw(cp); + + /* read MAC address from EEPROM */ + addr_len = read_eeprom (regs, 0, 8) == 0x8129 ? 8 : 6; + for (i = 0; i < 3; i++) + addr[i] = cpu_to_le16(read_eeprom (regs, i + 7, addr_len)); + eth_hw_addr_set(dev, (u8 *)addr); + + dev->netdev_ops = &cp_netdev_ops; + netif_napi_add_weight(dev, &cp->napi, cp_rx_poll, 16); + dev->ethtool_ops = &cp_ethtool_ops; + dev->watchdog_timeo = TX_TIMEOUT; + + dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | + NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; + + if (pci_using_dac) + dev->features |= NETIF_F_HIGHDMA; + + dev->hw_features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | + NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; + dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | + NETIF_F_HIGHDMA; + + /* MTU range: 60 - 4096 */ + dev->min_mtu = CP_MIN_MTU; + dev->max_mtu = CP_MAX_MTU; + + rc = register_netdev(dev); + if (rc) + goto err_out_iomap; + + netdev_info(dev, "RTL-8139C+ at 0x%p, %pM, IRQ %d\n", + regs, dev->dev_addr, pdev->irq); + + pci_set_drvdata(pdev, dev); + + /* enable busmastering and memory-write-invalidate */ + pci_set_master(pdev); + + if (cp->wol_enabled) + cp_set_d3_state (cp); + + return 0; + +err_out_iomap: + iounmap(regs); +err_out_res: + pci_release_regions(pdev); +err_out_mwi: + pci_clear_mwi(pdev); +err_out_disable: + pci_disable_device(pdev); +err_out_free: + free_netdev(dev); + return rc; +} + +static void cp_remove_one (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct cp_private *cp = netdev_priv(dev); + + unregister_netdev(dev); + iounmap(cp->regs); + if (cp->wol_enabled) + pci_set_power_state (pdev, PCI_D0); + pci_release_regions(pdev); + pci_clear_mwi(pdev); + pci_disable_device(pdev); + free_netdev(dev); +} + +static int __maybe_unused cp_suspend(struct device *device) +{ + struct net_device *dev = dev_get_drvdata(device); + struct cp_private *cp = netdev_priv(dev); + unsigned long flags; + + if (!netif_running(dev)) + return 0; + + netif_device_detach (dev); + netif_stop_queue (dev); + + spin_lock_irqsave (&cp->lock, flags); + + /* Disable Rx and Tx */ + cpw16 (IntrMask, 0); + cpw8 (Cmd, cpr8 (Cmd) & (~RxOn | ~TxOn)); + + spin_unlock_irqrestore (&cp->lock, flags); + + device_set_wakeup_enable(device, cp->wol_enabled); + + return 0; +} + +static int __maybe_unused cp_resume(struct device *device) +{ + struct net_device *dev = dev_get_drvdata(device); + struct cp_private *cp = netdev_priv(dev); + unsigned long flags; + + if (!netif_running(dev)) + return 0; + + netif_device_attach (dev); + + /* FIXME: sh*t may happen if the Rx ring buffer is depleted */ + cp_init_rings_index (cp); + cp_init_hw (cp); + cp_enable_irq(cp); + netif_start_queue (dev); + + spin_lock_irqsave (&cp->lock, flags); + + mii_check_media(&cp->mii_if, netif_msg_link(cp), false); + + spin_unlock_irqrestore (&cp->lock, flags); + + return 0; +} + +static const struct pci_device_id cp_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, PCI_DEVICE_ID_REALTEK_8139), }, + { PCI_DEVICE(PCI_VENDOR_ID_TTTECH, PCI_DEVICE_ID_TTTECH_MC322), }, + { }, +}; +MODULE_DEVICE_TABLE(pci, cp_pci_tbl); + +static SIMPLE_DEV_PM_OPS(cp_pm_ops, cp_suspend, cp_resume); + +static struct pci_driver cp_driver = { + .name = DRV_NAME, + .id_table = cp_pci_tbl, + .probe = cp_init_one, + .remove = cp_remove_one, + .driver.pm = &cp_pm_ops, +}; + +module_pci_driver(cp_driver); diff --git a/drivers/net/ethernet/realtek/8139too.c b/drivers/net/ethernet/realtek/8139too.c new file mode 100644 index 000000000..8241bcf76 --- /dev/null +++ b/drivers/net/ethernet/realtek/8139too.c @@ -0,0 +1,2642 @@ +/* + + 8139too.c: A RealTek RTL-8139 Fast Ethernet driver for Linux. + + Maintained by Jeff Garzik <jgarzik@pobox.com> + Copyright 2000-2002 Jeff Garzik + + Much code comes from Donald Becker's rtl8139.c driver, + versions 1.13 and older. This driver was originally based + on rtl8139.c version 1.07. Header of rtl8139.c version 1.13: + + -----<snip>----- + + Written 1997-2001 by Donald Becker. + This software may be used and distributed according to the + terms of the GNU General Public License (GPL), incorporated + herein by reference. Drivers based on or derived from this + code fall under the GPL and must retain the authorship, + copyright and license notice. This file is not a complete + program and may only be used when the entire operating + system is licensed under the GPL. + + This driver is for boards based on the RTL8129 and RTL8139 + PCI ethernet chips. + + The author may be reached as becker@scyld.com, or C/O Scyld + Computing Corporation 410 Severn Ave., Suite 210 Annapolis + MD 21403 + + Support and updates available at + http://www.scyld.com/network/rtl8139.html + + Twister-tuning table provided by Kinston + <shangh@realtek.com.tw>. + + -----<snip>----- + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + Contributors: + + Donald Becker - he wrote the original driver, kudos to him! + (but please don't e-mail him for support, this isn't his driver) + + Tigran Aivazian - bug fixes, skbuff free cleanup + + Martin Mares - suggestions for PCI cleanup + + David S. Miller - PCI DMA and softnet updates + + Ernst Gill - fixes ported from BSD driver + + Daniel Kobras - identified specific locations of + posted MMIO write bugginess + + Gerard Sharp - bug fix, testing and feedback + + David Ford - Rx ring wrap fix + + Dan DeMaggio - swapped RTL8139 cards with me, and allowed me + to find and fix a crucial bug on older chipsets. + + Donald Becker/Chris Butterworth/Marcus Westergren - + Noticed various Rx packet size-related buglets. + + Santiago Garcia Mantinan - testing and feedback + + Jens David - 2.2.x kernel backports + + Martin Dennett - incredibly helpful insight on undocumented + features of the 8139 chips + + Jean-Jacques Michel - bug fix + + Tobias Ringström - Rx interrupt status checking suggestion + + Andrew Morton - Clear blocked signals, avoid + buffer overrun setting current->comm. + + Kalle Olavi Niemitalo - Wake-on-LAN ioctls + + Robert Kuebel - Save kernel thread from dying on any signal. + + Submitting bug reports: + + "rtl8139-diag -mmmaaavvveefN" output + enable RTL8139_DEBUG below, and look at 'dmesg' or kernel log + +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define DRV_NAME "8139too" + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/compiler.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/rtnetlink.h> +#include <linux/delay.h> +#include <linux/ethtool.h> +#include <linux/mii.h> +#include <linux/completion.h> +#include <linux/crc32.h> +#include <linux/io.h> +#include <linux/uaccess.h> +#include <linux/gfp.h> +#include <linux/if_vlan.h> +#include <asm/irq.h> + +/* Default Message level */ +#define RTL8139_DEF_MSG_ENABLE (NETIF_MSG_DRV | \ + NETIF_MSG_PROBE | \ + NETIF_MSG_LINK) + + +/* define to 1, 2 or 3 to enable copious debugging info */ +#define RTL8139_DEBUG 0 + +/* define to 1 to disable lightweight runtime debugging checks */ +#undef RTL8139_NDEBUG + + +#ifdef RTL8139_NDEBUG +# define assert(expr) do {} while (0) +#else +# define assert(expr) \ + if (unlikely(!(expr))) { \ + pr_err("Assertion failed! %s,%s,%s,line=%d\n", \ + #expr, __FILE__, __func__, __LINE__); \ + } +#endif + + +/* A few user-configurable values. */ +/* media options */ +#define MAX_UNITS 8 +static int media[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +/* Whether to use MMIO or PIO. Default to MMIO. */ +#ifdef CONFIG_8139TOO_PIO +static bool use_io = true; +#else +static bool use_io = false; +#endif + +/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). + The RTL chips use a 64 element hash table based on the Ethernet CRC. */ +static int multicast_filter_limit = 32; + +/* bitmapped message enable number */ +static int debug = -1; + +/* + * Receive ring size + * Warning: 64K ring has hardware issues and may lock up. + */ +#if defined(CONFIG_SH_DREAMCAST) +#define RX_BUF_IDX 0 /* 8K ring */ +#else +#define RX_BUF_IDX 2 /* 32K ring */ +#endif +#define RX_BUF_LEN (8192 << RX_BUF_IDX) +#define RX_BUF_PAD 16 +#define RX_BUF_WRAP_PAD 2048 /* spare padding to handle lack of packet wrap */ + +#if RX_BUF_LEN == 65536 +#define RX_BUF_TOT_LEN RX_BUF_LEN +#else +#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD + RX_BUF_WRAP_PAD) +#endif + +/* Number of Tx descriptor registers. */ +#define NUM_TX_DESC 4 + +/* max supported ethernet frame size -- must be at least (dev->mtu+18+4).*/ +#define MAX_ETH_FRAME_SIZE 1792 + +/* max supported payload size */ +#define MAX_ETH_DATA_SIZE (MAX_ETH_FRAME_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN) + +/* Size of the Tx bounce buffers -- must be at least (dev->mtu+18+4). */ +#define TX_BUF_SIZE MAX_ETH_FRAME_SIZE +#define TX_BUF_TOT_LEN (TX_BUF_SIZE * NUM_TX_DESC) + +/* PCI Tuning Parameters + Threshold is bytes transferred to chip before transmission starts. */ +#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */ + +/* The following settings are log_2(bytes)-4: 0 == 16 bytes .. 6==1024, 7==end of packet. */ +#define RX_FIFO_THRESH 7 /* Rx buffer level before first PCI xfer. */ +#define RX_DMA_BURST 7 /* Maximum PCI burst, '6' is 1024 */ +#define TX_DMA_BURST 6 /* Maximum PCI burst, '6' is 1024 */ +#define TX_RETRY 8 /* 0-15. retries = 16 + (TX_RETRY * 16) */ + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (6*HZ) + + +enum { + HAS_MII_XCVR = 0x010000, + HAS_CHIP_XCVR = 0x020000, + HAS_LNK_CHNG = 0x040000, +}; + +#define RTL_NUM_STATS 4 /* number of ETHTOOL_GSTATS u64's */ +#define RTL_REGS_VER 1 /* version of reg. data in ETHTOOL_GREGS */ +#define RTL_MIN_IO_SIZE 0x80 +#define RTL8139B_IO_SIZE 256 + +#define RTL8129_CAPS HAS_MII_XCVR +#define RTL8139_CAPS (HAS_CHIP_XCVR|HAS_LNK_CHNG) + +typedef enum { + RTL8139 = 0, + RTL8129, +} board_t; + + +/* indexed by board_t, above */ +static const struct { + const char *name; + u32 hw_flags; +} board_info[] = { + { "RealTek RTL8139", RTL8139_CAPS }, + { "RealTek RTL8129", RTL8129_CAPS }, +}; + + +static const struct pci_device_id rtl8139_pci_tbl[] = { + { PCI_DEVICE(0x10ec, 0x8139), .driver_data = RTL8139 }, + { PCI_DEVICE(0x10ec, 0x8138), .driver_data = RTL8139 }, + { PCI_DEVICE(0x1113, 0x1211), .driver_data = RTL8139 }, + { PCI_DEVICE(0x1500, 0x1360), .driver_data = RTL8139 }, + { PCI_DEVICE(0x4033, 0x1360), .driver_data = RTL8139 }, + { PCI_DEVICE(0x1186, 0x1300), .driver_data = RTL8139 }, + { PCI_DEVICE(0x1186, 0x1340), .driver_data = RTL8139 }, + { PCI_DEVICE(0x13d1, 0xab06), .driver_data = RTL8139 }, + { PCI_DEVICE(0x1259, 0xa117), .driver_data = RTL8139 }, + { PCI_DEVICE(0x1259, 0xa11e), .driver_data = RTL8139 }, + { PCI_DEVICE(0x14ea, 0xab06), .driver_data = RTL8139 }, + { PCI_DEVICE(0x14ea, 0xab07), .driver_data = RTL8139 }, + { PCI_DEVICE(0x11db, 0x1234), .driver_data = RTL8139 }, + { PCI_DEVICE(0x1432, 0x9130), .driver_data = RTL8139 }, + { PCI_DEVICE(0x02ac, 0x1012), .driver_data = RTL8139 }, + { PCI_DEVICE(0x018a, 0x0106), .driver_data = RTL8139 }, + { PCI_DEVICE(0x126c, 0x1211), .driver_data = RTL8139 }, + { PCI_DEVICE(0x1743, 0x8139), .driver_data = RTL8139 }, + { PCI_DEVICE(0x021b, 0x8139), .driver_data = RTL8139 }, + { PCI_DEVICE(0x16ec, 0xab06), .driver_data = RTL8139 }, + +#ifdef CONFIG_SH_SECUREEDGE5410 + /* Bogus 8139 silicon reports 8129 without external PROM :-( */ + { PCI_DEVICE(0x10ec, 0x8129), .driver_data = RTL8139 }, +#endif +#ifdef CONFIG_8139TOO_8129 + { PCI_DEVICE(0x10ec, 0x8129), .driver_data = RTL8129 }, +#endif + + /* some crazy cards report invalid vendor ids like + * 0x0001 here. The other ids are valid and constant, + * so we simply don't match on the main vendor id. + */ + { PCI_DEVICE_SUB(PCI_ANY_ID, 0x8139, 0x10ec, 0x8139), .driver_data = RTL8139 }, + { PCI_DEVICE_SUB(PCI_ANY_ID, 0x8139, 0x1186, 0x1300), .driver_data = RTL8139 }, + { PCI_DEVICE_SUB(PCI_ANY_ID, 0x8139, 0x13d1, 0xab06), .driver_data = RTL8139 }, + + { } +}; +MODULE_DEVICE_TABLE (pci, rtl8139_pci_tbl); + +static struct { + const char str[ETH_GSTRING_LEN]; +} ethtool_stats_keys[] = { + { "early_rx" }, + { "tx_buf_mapped" }, + { "tx_timeouts" }, + { "rx_lost_in_ring" }, +}; + +/* The rest of these values should never change. */ + +/* Symbolic offsets to registers. */ +enum RTL8139_registers { + MAC0 = 0, /* Ethernet hardware address. */ + MAR0 = 8, /* Multicast filter. */ + TxStatus0 = 0x10, /* Transmit status (Four 32bit registers). */ + TxAddr0 = 0x20, /* Tx descriptors (also four 32bit). */ + RxBuf = 0x30, + ChipCmd = 0x37, + RxBufPtr = 0x38, + RxBufAddr = 0x3A, + IntrMask = 0x3C, + IntrStatus = 0x3E, + TxConfig = 0x40, + RxConfig = 0x44, + Timer = 0x48, /* A general-purpose counter. */ + RxMissed = 0x4C, /* 24 bits valid, write clears. */ + Cfg9346 = 0x50, + Config0 = 0x51, + Config1 = 0x52, + TimerInt = 0x54, + MediaStatus = 0x58, + Config3 = 0x59, + Config4 = 0x5A, /* absent on RTL-8139A */ + HltClk = 0x5B, + MultiIntr = 0x5C, + TxSummary = 0x60, + BasicModeCtrl = 0x62, + BasicModeStatus = 0x64, + NWayAdvert = 0x66, + NWayLPAR = 0x68, + NWayExpansion = 0x6A, + /* Undocumented registers, but required for proper operation. */ + FIFOTMS = 0x70, /* FIFO Control and test. */ + CSCR = 0x74, /* Chip Status and Configuration Register. */ + PARA78 = 0x78, + FlashReg = 0xD4, /* Communication with Flash ROM, four bytes. */ + PARA7c = 0x7c, /* Magic transceiver parameter register. */ + Config5 = 0xD8, /* absent on RTL-8139A */ +}; + +enum ClearBitMasks { + MultiIntrClear = 0xF000, + ChipCmdClear = 0xE2, + Config1Clear = (1<<7)|(1<<6)|(1<<3)|(1<<2)|(1<<1), +}; + +enum ChipCmdBits { + CmdReset = 0x10, + CmdRxEnb = 0x08, + CmdTxEnb = 0x04, + RxBufEmpty = 0x01, +}; + +/* Interrupt register bits, using my own meaningful names. */ +enum IntrStatusBits { + PCIErr = 0x8000, + PCSTimeout = 0x4000, + RxFIFOOver = 0x40, + RxUnderrun = 0x20, + RxOverflow = 0x10, + TxErr = 0x08, + TxOK = 0x04, + RxErr = 0x02, + RxOK = 0x01, + + RxAckBits = RxFIFOOver | RxOverflow | RxOK, +}; + +enum TxStatusBits { + TxHostOwns = 0x2000, + TxUnderrun = 0x4000, + TxStatOK = 0x8000, + TxOutOfWindow = 0x20000000, + TxAborted = 0x40000000, + TxCarrierLost = 0x80000000, +}; +enum RxStatusBits { + RxMulticast = 0x8000, + RxPhysical = 0x4000, + RxBroadcast = 0x2000, + RxBadSymbol = 0x0020, + RxRunt = 0x0010, + RxTooLong = 0x0008, + RxCRCErr = 0x0004, + RxBadAlign = 0x0002, + RxStatusOK = 0x0001, +}; + +/* Bits in RxConfig. */ +enum rx_mode_bits { + AcceptErr = 0x20, + AcceptRunt = 0x10, + AcceptBroadcast = 0x08, + AcceptMulticast = 0x04, + AcceptMyPhys = 0x02, + AcceptAllPhys = 0x01, +}; + +/* Bits in TxConfig. */ +enum tx_config_bits { + /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */ + TxIFGShift = 24, + TxIFG84 = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */ + TxIFG88 = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */ + TxIFG92 = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */ + TxIFG96 = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */ + + TxLoopBack = (1 << 18) | (1 << 17), /* enable loopback test mode */ + TxCRC = (1 << 16), /* DISABLE Tx pkt CRC append */ + TxClearAbt = (1 << 0), /* Clear abort (WO) */ + TxDMAShift = 8, /* DMA burst value (0-7) is shifted X many bits */ + TxRetryShift = 4, /* TXRR value (0-15) is shifted X many bits */ + + TxVersionMask = 0x7C800000, /* mask out version bits 30-26, 23 */ +}; + +/* Bits in Config1 */ +enum Config1Bits { + Cfg1_PM_Enable = 0x01, + Cfg1_VPD_Enable = 0x02, + Cfg1_PIO = 0x04, + Cfg1_MMIO = 0x08, + LWAKE = 0x10, /* not on 8139, 8139A */ + Cfg1_Driver_Load = 0x20, + Cfg1_LED0 = 0x40, + Cfg1_LED1 = 0x80, + SLEEP = (1 << 1), /* only on 8139, 8139A */ + PWRDN = (1 << 0), /* only on 8139, 8139A */ +}; + +/* Bits in Config3 */ +enum Config3Bits { + Cfg3_FBtBEn = (1 << 0), /* 1 = Fast Back to Back */ + Cfg3_FuncRegEn = (1 << 1), /* 1 = enable CardBus Function registers */ + Cfg3_CLKRUN_En = (1 << 2), /* 1 = enable CLKRUN */ + Cfg3_CardB_En = (1 << 3), /* 1 = enable CardBus registers */ + Cfg3_LinkUp = (1 << 4), /* 1 = wake up on link up */ + Cfg3_Magic = (1 << 5), /* 1 = wake up on Magic Packet (tm) */ + Cfg3_PARM_En = (1 << 6), /* 0 = software can set twister parameters */ + Cfg3_GNTSel = (1 << 7), /* 1 = delay 1 clock from PCI GNT signal */ +}; + +/* Bits in Config4 */ +enum Config4Bits { + LWPTN = (1 << 2), /* not on 8139, 8139A */ +}; + +/* Bits in Config5 */ +enum Config5Bits { + Cfg5_PME_STS = (1 << 0), /* 1 = PCI reset resets PME_Status */ + Cfg5_LANWake = (1 << 1), /* 1 = enable LANWake signal */ + Cfg5_LDPS = (1 << 2), /* 0 = save power when link is down */ + Cfg5_FIFOAddrPtr= (1 << 3), /* Realtek internal SRAM testing */ + Cfg5_UWF = (1 << 4), /* 1 = accept unicast wakeup frame */ + Cfg5_MWF = (1 << 5), /* 1 = accept multicast wakeup frame */ + Cfg5_BWF = (1 << 6), /* 1 = accept broadcast wakeup frame */ +}; + +enum RxConfigBits { + /* rx fifo threshold */ + RxCfgFIFOShift = 13, + RxCfgFIFONone = (7 << RxCfgFIFOShift), + + /* Max DMA burst */ + RxCfgDMAShift = 8, + RxCfgDMAUnlimited = (7 << RxCfgDMAShift), + + /* rx ring buffer length */ + RxCfgRcv8K = 0, + RxCfgRcv16K = (1 << 11), + RxCfgRcv32K = (1 << 12), + RxCfgRcv64K = (1 << 11) | (1 << 12), + + /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */ + RxNoWrap = (1 << 7), +}; + +/* Twister tuning parameters from RealTek. + Completely undocumented, but required to tune bad links on some boards. */ +enum CSCRBits { + CSCR_LinkOKBit = 0x0400, + CSCR_LinkChangeBit = 0x0800, + CSCR_LinkStatusBits = 0x0f000, + CSCR_LinkDownOffCmd = 0x003c0, + CSCR_LinkDownCmd = 0x0f3c0, +}; + +enum Cfg9346Bits { + Cfg9346_Lock = 0x00, + Cfg9346_Unlock = 0xC0, +}; + +typedef enum { + CH_8139 = 0, + CH_8139_K, + CH_8139A, + CH_8139A_G, + CH_8139B, + CH_8130, + CH_8139C, + CH_8100, + CH_8100B_8139D, + CH_8101, +} chip_t; + +enum chip_flags { + HasHltClk = (1 << 0), + HasLWake = (1 << 1), +}; + +#define HW_REVID(b30, b29, b28, b27, b26, b23, b22) \ + (b30<<30 | b29<<29 | b28<<28 | b27<<27 | b26<<26 | b23<<23 | b22<<22) +#define HW_REVID_MASK HW_REVID(1, 1, 1, 1, 1, 1, 1) + +/* directly indexed by chip_t, above */ +static const struct { + const char *name; + u32 version; /* from RTL8139C/RTL8139D docs */ + u32 flags; +} rtl_chip_info[] = { + { "RTL-8139", + HW_REVID(1, 0, 0, 0, 0, 0, 0), + HasHltClk, + }, + + { "RTL-8139 rev K", + HW_REVID(1, 1, 0, 0, 0, 0, 0), + HasHltClk, + }, + + { "RTL-8139A", + HW_REVID(1, 1, 1, 0, 0, 0, 0), + HasHltClk, /* XXX undocumented? */ + }, + + { "RTL-8139A rev G", + HW_REVID(1, 1, 1, 0, 0, 1, 0), + HasHltClk, /* XXX undocumented? */ + }, + + { "RTL-8139B", + HW_REVID(1, 1, 1, 1, 0, 0, 0), + HasLWake, + }, + + { "RTL-8130", + HW_REVID(1, 1, 1, 1, 1, 0, 0), + HasLWake, + }, + + { "RTL-8139C", + HW_REVID(1, 1, 1, 0, 1, 0, 0), + HasLWake, + }, + + { "RTL-8100", + HW_REVID(1, 1, 1, 1, 0, 1, 0), + HasLWake, + }, + + { "RTL-8100B/8139D", + HW_REVID(1, 1, 1, 0, 1, 0, 1), + HasHltClk /* XXX undocumented? */ + | HasLWake, + }, + + { "RTL-8101", + HW_REVID(1, 1, 1, 0, 1, 1, 1), + HasLWake, + }, +}; + +struct rtl_extra_stats { + unsigned long early_rx; + unsigned long tx_buf_mapped; + unsigned long tx_timeouts; + unsigned long rx_lost_in_ring; +}; + +struct rtl8139_stats { + u64 packets; + u64 bytes; + struct u64_stats_sync syncp; +}; + +struct rtl8139_private { + void __iomem *mmio_addr; + int drv_flags; + struct pci_dev *pci_dev; + u32 msg_enable; + struct napi_struct napi; + struct net_device *dev; + + unsigned char *rx_ring; + unsigned int cur_rx; /* RX buf index of next pkt */ + struct rtl8139_stats rx_stats; + dma_addr_t rx_ring_dma; + + unsigned int tx_flag; + unsigned long cur_tx; + unsigned long dirty_tx; + struct rtl8139_stats tx_stats; + unsigned char *tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */ + unsigned char *tx_bufs; /* Tx bounce buffer region. */ + dma_addr_t tx_bufs_dma; + + signed char phys[4]; /* MII device addresses. */ + + /* Twister tune state. */ + char twistie, twist_row, twist_col; + + unsigned int watchdog_fired : 1; + unsigned int default_port : 4; /* Last dev->if_port value. */ + unsigned int have_thread : 1; + + spinlock_t lock; + spinlock_t rx_lock; + + chip_t chipset; + u32 rx_config; + struct rtl_extra_stats xstats; + + struct delayed_work thread; + + struct mii_if_info mii; + unsigned int regs_len; + unsigned long fifo_copy_timeout; +}; + +MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>"); +MODULE_DESCRIPTION ("RealTek RTL-8139 Fast Ethernet driver"); +MODULE_LICENSE("GPL"); + +module_param(use_io, bool, 0); +MODULE_PARM_DESC(use_io, "Force use of I/O access mode. 0=MMIO 1=PIO"); +module_param(multicast_filter_limit, int, 0); +module_param_array(media, int, NULL, 0); +module_param_array(full_duplex, int, NULL, 0); +module_param(debug, int, 0); +MODULE_PARM_DESC (debug, "8139too bitmapped message enable number"); +MODULE_PARM_DESC (multicast_filter_limit, "8139too maximum number of filtered multicast addresses"); +MODULE_PARM_DESC (media, "8139too: Bits 4+9: force full duplex, bit 5: 100Mbps"); +MODULE_PARM_DESC (full_duplex, "8139too: Force full duplex for board(s) (1)"); + +static int read_eeprom (void __iomem *ioaddr, int location, int addr_len); +static int rtl8139_open (struct net_device *dev); +static int mdio_read (struct net_device *dev, int phy_id, int location); +static void mdio_write (struct net_device *dev, int phy_id, int location, + int val); +static void rtl8139_start_thread(struct rtl8139_private *tp); +static void rtl8139_tx_timeout (struct net_device *dev, unsigned int txqueue); +static void rtl8139_init_ring (struct net_device *dev); +static netdev_tx_t rtl8139_start_xmit (struct sk_buff *skb, + struct net_device *dev); +#ifdef CONFIG_NET_POLL_CONTROLLER +static void rtl8139_poll_controller(struct net_device *dev); +#endif +static int rtl8139_set_mac_address(struct net_device *dev, void *p); +static int rtl8139_poll(struct napi_struct *napi, int budget); +static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance); +static int rtl8139_close (struct net_device *dev); +static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); +static void rtl8139_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats); +static void rtl8139_set_rx_mode (struct net_device *dev); +static void __set_rx_mode (struct net_device *dev); +static void rtl8139_hw_start (struct net_device *dev); +static void rtl8139_thread (struct work_struct *work); +static void rtl8139_tx_timeout_task(struct work_struct *work); +static const struct ethtool_ops rtl8139_ethtool_ops; + +/* write MMIO register, with flush */ +/* Flush avoids rtl8139 bug w/ posted MMIO writes */ +#define RTL_W8_F(reg, val8) do { iowrite8 ((val8), ioaddr + (reg)); ioread8 (ioaddr + (reg)); } while (0) +#define RTL_W16_F(reg, val16) do { iowrite16 ((val16), ioaddr + (reg)); ioread16 (ioaddr + (reg)); } while (0) +#define RTL_W32_F(reg, val32) do { iowrite32 ((val32), ioaddr + (reg)); ioread32 (ioaddr + (reg)); } while (0) + +/* write MMIO register */ +#define RTL_W8(reg, val8) iowrite8 ((val8), ioaddr + (reg)) +#define RTL_W16(reg, val16) iowrite16 ((val16), ioaddr + (reg)) +#define RTL_W32(reg, val32) iowrite32 ((val32), ioaddr + (reg)) + +/* read MMIO register */ +#define RTL_R8(reg) ioread8 (ioaddr + (reg)) +#define RTL_R16(reg) ioread16 (ioaddr + (reg)) +#define RTL_R32(reg) ioread32 (ioaddr + (reg)) + + +static const u16 rtl8139_intr_mask = + PCIErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | + TxErr | TxOK | RxErr | RxOK; + +static const u16 rtl8139_norx_intr_mask = + PCIErr | PCSTimeout | RxUnderrun | + TxErr | TxOK | RxErr ; + +#if RX_BUF_IDX == 0 +static const unsigned int rtl8139_rx_config = + RxCfgRcv8K | RxNoWrap | + (RX_FIFO_THRESH << RxCfgFIFOShift) | + (RX_DMA_BURST << RxCfgDMAShift); +#elif RX_BUF_IDX == 1 +static const unsigned int rtl8139_rx_config = + RxCfgRcv16K | RxNoWrap | + (RX_FIFO_THRESH << RxCfgFIFOShift) | + (RX_DMA_BURST << RxCfgDMAShift); +#elif RX_BUF_IDX == 2 +static const unsigned int rtl8139_rx_config = + RxCfgRcv32K | RxNoWrap | + (RX_FIFO_THRESH << RxCfgFIFOShift) | + (RX_DMA_BURST << RxCfgDMAShift); +#elif RX_BUF_IDX == 3 +static const unsigned int rtl8139_rx_config = + RxCfgRcv64K | + (RX_FIFO_THRESH << RxCfgFIFOShift) | + (RX_DMA_BURST << RxCfgDMAShift); +#else +#error "Invalid configuration for 8139_RXBUF_IDX" +#endif + +static const unsigned int rtl8139_tx_config = + TxIFG96 | (TX_DMA_BURST << TxDMAShift) | (TX_RETRY << TxRetryShift); + +static void __rtl8139_cleanup_dev (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + struct pci_dev *pdev; + + assert (dev != NULL); + assert (tp->pci_dev != NULL); + pdev = tp->pci_dev; + + if (tp->mmio_addr) + pci_iounmap (pdev, tp->mmio_addr); + + /* it's ok to call this even if we have no regions to free */ + pci_release_regions (pdev); + + free_netdev(dev); +} + + +static void rtl8139_chip_reset (void __iomem *ioaddr) +{ + int i; + + /* Soft reset the chip. */ + RTL_W8 (ChipCmd, CmdReset); + + /* Check that the chip has finished the reset. */ + for (i = 1000; i > 0; i--) { + barrier(); + if ((RTL_R8 (ChipCmd) & CmdReset) == 0) + break; + udelay (10); + } +} + + +static struct net_device *rtl8139_init_board(struct pci_dev *pdev) +{ + struct device *d = &pdev->dev; + void __iomem *ioaddr; + struct net_device *dev; + struct rtl8139_private *tp; + u8 tmp8; + int rc, disable_dev_on_err = 0; + unsigned int i, bar; + unsigned long io_len; + u32 version; + static const struct { + unsigned long mask; + char *type; + } res[] = { + { IORESOURCE_IO, "PIO" }, + { IORESOURCE_MEM, "MMIO" } + }; + + assert (pdev != NULL); + + /* dev and priv zeroed in alloc_etherdev */ + dev = alloc_etherdev (sizeof (*tp)); + if (dev == NULL) + return ERR_PTR(-ENOMEM); + + SET_NETDEV_DEV(dev, &pdev->dev); + + tp = netdev_priv(dev); + tp->pci_dev = pdev; + + /* enable device (incl. PCI PM wakeup and hotplug setup) */ + rc = pci_enable_device (pdev); + if (rc) + goto err_out; + + disable_dev_on_err = 1; + rc = pci_request_regions (pdev, DRV_NAME); + if (rc) + goto err_out; + + pci_set_master (pdev); + + u64_stats_init(&tp->rx_stats.syncp); + u64_stats_init(&tp->tx_stats.syncp); + +retry: + /* PIO bar register comes first. */ + bar = !use_io; + + io_len = pci_resource_len(pdev, bar); + + dev_dbg(d, "%s region size = 0x%02lX\n", res[bar].type, io_len); + + if (!(pci_resource_flags(pdev, bar) & res[bar].mask)) { + dev_err(d, "region #%d not a %s resource, aborting\n", bar, + res[bar].type); + rc = -ENODEV; + goto err_out; + } + if (io_len < RTL_MIN_IO_SIZE) { + dev_err(d, "Invalid PCI %s region size(s), aborting\n", + res[bar].type); + rc = -ENODEV; + goto err_out; + } + + ioaddr = pci_iomap(pdev, bar, 0); + if (!ioaddr) { + dev_err(d, "cannot map %s\n", res[bar].type); + if (!use_io) { + use_io = true; + goto retry; + } + rc = -ENODEV; + goto err_out; + } + tp->regs_len = io_len; + tp->mmio_addr = ioaddr; + + /* Bring old chips out of low-power mode. */ + RTL_W8 (HltClk, 'R'); + + /* check for missing/broken hardware */ + if (RTL_R32 (TxConfig) == 0xFFFFFFFF) { + dev_err(&pdev->dev, "Chip not responding, ignoring board\n"); + rc = -EIO; + goto err_out; + } + + /* identify chip attached to board */ + version = RTL_R32 (TxConfig) & HW_REVID_MASK; + for (i = 0; i < ARRAY_SIZE (rtl_chip_info); i++) + if (version == rtl_chip_info[i].version) { + tp->chipset = i; + goto match; + } + + /* if unknown chip, assume array element #0, original RTL-8139 in this case */ + i = 0; + dev_dbg(&pdev->dev, "unknown chip version, assuming RTL-8139\n"); + dev_dbg(&pdev->dev, "TxConfig = 0x%x\n", RTL_R32 (TxConfig)); + tp->chipset = 0; + +match: + pr_debug("chipset id (%d) == index %d, '%s'\n", + version, i, rtl_chip_info[i].name); + + if (tp->chipset >= CH_8139B) { + u8 new_tmp8 = tmp8 = RTL_R8 (Config1); + pr_debug("PCI PM wakeup\n"); + if ((rtl_chip_info[tp->chipset].flags & HasLWake) && + (tmp8 & LWAKE)) + new_tmp8 &= ~LWAKE; + new_tmp8 |= Cfg1_PM_Enable; + if (new_tmp8 != tmp8) { + RTL_W8 (Cfg9346, Cfg9346_Unlock); + RTL_W8 (Config1, tmp8); + RTL_W8 (Cfg9346, Cfg9346_Lock); + } + if (rtl_chip_info[tp->chipset].flags & HasLWake) { + tmp8 = RTL_R8 (Config4); + if (tmp8 & LWPTN) { + RTL_W8 (Cfg9346, Cfg9346_Unlock); + RTL_W8 (Config4, tmp8 & ~LWPTN); + RTL_W8 (Cfg9346, Cfg9346_Lock); + } + } + } else { + pr_debug("Old chip wakeup\n"); + tmp8 = RTL_R8 (Config1); + tmp8 &= ~(SLEEP | PWRDN); + RTL_W8 (Config1, tmp8); + } + + rtl8139_chip_reset (ioaddr); + + return dev; + +err_out: + __rtl8139_cleanup_dev (dev); + if (disable_dev_on_err) + pci_disable_device (pdev); + return ERR_PTR(rc); +} + +static int rtl8139_set_features(struct net_device *dev, netdev_features_t features) +{ + struct rtl8139_private *tp = netdev_priv(dev); + unsigned long flags; + netdev_features_t changed = features ^ dev->features; + void __iomem *ioaddr = tp->mmio_addr; + + if (!(changed & (NETIF_F_RXALL))) + return 0; + + spin_lock_irqsave(&tp->lock, flags); + + if (changed & NETIF_F_RXALL) { + int rx_mode = tp->rx_config; + if (features & NETIF_F_RXALL) + rx_mode |= (AcceptErr | AcceptRunt); + else + rx_mode &= ~(AcceptErr | AcceptRunt); + tp->rx_config = rtl8139_rx_config | rx_mode; + RTL_W32_F(RxConfig, tp->rx_config); + } + + spin_unlock_irqrestore(&tp->lock, flags); + + return 0; +} + +static const struct net_device_ops rtl8139_netdev_ops = { + .ndo_open = rtl8139_open, + .ndo_stop = rtl8139_close, + .ndo_get_stats64 = rtl8139_get_stats64, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = rtl8139_set_mac_address, + .ndo_start_xmit = rtl8139_start_xmit, + .ndo_set_rx_mode = rtl8139_set_rx_mode, + .ndo_eth_ioctl = netdev_ioctl, + .ndo_tx_timeout = rtl8139_tx_timeout, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = rtl8139_poll_controller, +#endif + .ndo_set_features = rtl8139_set_features, +}; + +static int rtl8139_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *dev = NULL; + struct rtl8139_private *tp; + __le16 addr[ETH_ALEN / 2]; + int i, addr_len, option; + void __iomem *ioaddr; + static int board_idx = -1; + + assert (pdev != NULL); + assert (ent != NULL); + + board_idx++; + + if (pdev->vendor == PCI_VENDOR_ID_REALTEK && + pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pdev->revision >= 0x20) { + dev_info(&pdev->dev, + "This (id %04x:%04x rev %02x) is an enhanced 8139C+ chip, use 8139cp\n", + pdev->vendor, pdev->device, pdev->revision); + return -ENODEV; + } + + if (pdev->vendor == PCI_VENDOR_ID_REALTEK && + pdev->device == PCI_DEVICE_ID_REALTEK_8139 && + pdev->subsystem_vendor == PCI_VENDOR_ID_ATHEROS && + pdev->subsystem_device == PCI_DEVICE_ID_REALTEK_8139) { + pr_info("OQO Model 2 detected. Forcing PIO\n"); + use_io = true; + } + + dev = rtl8139_init_board (pdev); + if (IS_ERR(dev)) + return PTR_ERR(dev); + + assert (dev != NULL); + tp = netdev_priv(dev); + tp->dev = dev; + + ioaddr = tp->mmio_addr; + assert (ioaddr != NULL); + + addr_len = read_eeprom (ioaddr, 0, 8) == 0x8129 ? 8 : 6; + for (i = 0; i < 3; i++) + addr[i] = cpu_to_le16(read_eeprom (ioaddr, i + 7, addr_len)); + eth_hw_addr_set(dev, (u8 *)addr); + + /* The Rtl8139-specific entries in the device structure. */ + dev->netdev_ops = &rtl8139_netdev_ops; + dev->ethtool_ops = &rtl8139_ethtool_ops; + dev->watchdog_timeo = TX_TIMEOUT; + netif_napi_add(dev, &tp->napi, rtl8139_poll); + + /* note: the hardware is not capable of sg/csum/highdma, however + * through the use of skb_copy_and_csum_dev we enable these + * features + */ + dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA; + dev->vlan_features = dev->features; + + dev->hw_features |= NETIF_F_RXALL; + dev->hw_features |= NETIF_F_RXFCS; + + /* MTU range: 68 - 1770 */ + dev->min_mtu = ETH_MIN_MTU; + dev->max_mtu = MAX_ETH_DATA_SIZE; + + /* tp zeroed and aligned in alloc_etherdev */ + tp = netdev_priv(dev); + + /* note: tp->chipset set in rtl8139_init_board */ + tp->drv_flags = board_info[ent->driver_data].hw_flags; + tp->mmio_addr = ioaddr; + tp->msg_enable = + (debug < 0 ? RTL8139_DEF_MSG_ENABLE : ((1 << debug) - 1)); + spin_lock_init (&tp->lock); + spin_lock_init (&tp->rx_lock); + INIT_DELAYED_WORK(&tp->thread, rtl8139_thread); + tp->mii.dev = dev; + tp->mii.mdio_read = mdio_read; + tp->mii.mdio_write = mdio_write; + tp->mii.phy_id_mask = 0x3f; + tp->mii.reg_num_mask = 0x1f; + + /* dev is fully set up and ready to use now */ + pr_debug("about to register device named %s (%p)...\n", + dev->name, dev); + i = register_netdev (dev); + if (i) goto err_out; + + pci_set_drvdata (pdev, dev); + + netdev_info(dev, "%s at 0x%p, %pM, IRQ %d\n", + board_info[ent->driver_data].name, + ioaddr, dev->dev_addr, pdev->irq); + + netdev_dbg(dev, "Identified 8139 chip type '%s'\n", + rtl_chip_info[tp->chipset].name); + + /* Find the connected MII xcvrs. + Doing this in open() would allow detecting external xcvrs later, but + takes too much time. */ +#ifdef CONFIG_8139TOO_8129 + if (tp->drv_flags & HAS_MII_XCVR) { + int phy, phy_idx = 0; + for (phy = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) { + int mii_status = mdio_read(dev, phy, 1); + if (mii_status != 0xffff && mii_status != 0x0000) { + u16 advertising = mdio_read(dev, phy, 4); + tp->phys[phy_idx++] = phy; + netdev_info(dev, "MII transceiver %d status 0x%04x advertising %04x\n", + phy, mii_status, advertising); + } + } + if (phy_idx == 0) { + netdev_info(dev, "No MII transceivers found! Assuming SYM transceiver\n"); + tp->phys[0] = 32; + } + } else +#endif + tp->phys[0] = 32; + tp->mii.phy_id = tp->phys[0]; + + /* The lower four bits are the media type. */ + option = (board_idx >= MAX_UNITS) ? 0 : media[board_idx]; + if (option > 0) { + tp->mii.full_duplex = (option & 0x210) ? 1 : 0; + tp->default_port = option & 0xFF; + if (tp->default_port) + tp->mii.force_media = 1; + } + if (board_idx < MAX_UNITS && full_duplex[board_idx] > 0) + tp->mii.full_duplex = full_duplex[board_idx]; + if (tp->mii.full_duplex) { + netdev_info(dev, "Media type forced to Full Duplex\n"); + /* Changing the MII-advertised media because might prevent + re-connection. */ + tp->mii.force_media = 1; + } + if (tp->default_port) { + netdev_info(dev, " Forcing %dMbps %s-duplex operation\n", + (option & 0x20 ? 100 : 10), + (option & 0x10 ? "full" : "half")); + mdio_write(dev, tp->phys[0], 0, + ((option & 0x20) ? 0x2000 : 0) | /* 100Mbps? */ + ((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */ + } + + /* Put the chip into low-power mode. */ + if (rtl_chip_info[tp->chipset].flags & HasHltClk) + RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */ + + return 0; + +err_out: + __rtl8139_cleanup_dev (dev); + pci_disable_device (pdev); + return i; +} + + +static void rtl8139_remove_one(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata (pdev); + struct rtl8139_private *tp = netdev_priv(dev); + + assert (dev != NULL); + + cancel_delayed_work_sync(&tp->thread); + + unregister_netdev (dev); + + __rtl8139_cleanup_dev (dev); + pci_disable_device (pdev); +} + + +/* Serial EEPROM section. */ + +/* EEPROM_Ctrl bits. */ +#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */ +#define EE_CS 0x08 /* EEPROM chip select. */ +#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */ +#define EE_WRITE_0 0x00 +#define EE_WRITE_1 0x02 +#define EE_DATA_READ 0x01 /* EEPROM chip data out. */ +#define EE_ENB (0x80 | EE_CS) + +/* Delay between EEPROM clock transitions. + No extra delay is needed with 33Mhz PCI, but 66Mhz may change this. + */ + +#define eeprom_delay() (void)RTL_R8(Cfg9346) + +/* The EEPROM commands include the alway-set leading bit. */ +#define EE_WRITE_CMD (5) +#define EE_READ_CMD (6) +#define EE_ERASE_CMD (7) + +static int read_eeprom(void __iomem *ioaddr, int location, int addr_len) +{ + int i; + unsigned retval = 0; + int read_cmd = location | (EE_READ_CMD << addr_len); + + RTL_W8 (Cfg9346, EE_ENB & ~EE_CS); + RTL_W8 (Cfg9346, EE_ENB); + eeprom_delay (); + + /* Shift the read command bits out. */ + for (i = 4 + addr_len; i >= 0; i--) { + int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + RTL_W8 (Cfg9346, EE_ENB | dataval); + eeprom_delay (); + RTL_W8 (Cfg9346, EE_ENB | dataval | EE_SHIFT_CLK); + eeprom_delay (); + } + RTL_W8 (Cfg9346, EE_ENB); + eeprom_delay (); + + for (i = 16; i > 0; i--) { + RTL_W8 (Cfg9346, EE_ENB | EE_SHIFT_CLK); + eeprom_delay (); + retval = + (retval << 1) | ((RTL_R8 (Cfg9346) & EE_DATA_READ) ? 1 : + 0); + RTL_W8 (Cfg9346, EE_ENB); + eeprom_delay (); + } + + /* Terminate the EEPROM access. */ + RTL_W8(Cfg9346, 0); + eeprom_delay (); + + return retval; +} + +/* MII serial management: mostly bogus for now. */ +/* Read and write the MII management registers using software-generated + serial MDIO protocol. + The maximum data clock rate is 2.5 Mhz. The minimum timing is usually + met by back-to-back PCI I/O cycles, but we insert a delay to avoid + "overclocking" issues. */ +#define MDIO_DIR 0x80 +#define MDIO_DATA_OUT 0x04 +#define MDIO_DATA_IN 0x02 +#define MDIO_CLK 0x01 +#define MDIO_WRITE0 (MDIO_DIR) +#define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT) + +#define mdio_delay() RTL_R8(Config4) + + +static const char mii_2_8139_map[8] = { + BasicModeCtrl, + BasicModeStatus, + 0, + 0, + NWayAdvert, + NWayLPAR, + NWayExpansion, + 0 +}; + + +#ifdef CONFIG_8139TOO_8129 +/* Syncronize the MII management interface by shifting 32 one bits out. */ +static void mdio_sync (void __iomem *ioaddr) +{ + int i; + + for (i = 32; i >= 0; i--) { + RTL_W8 (Config4, MDIO_WRITE1); + mdio_delay (); + RTL_W8 (Config4, MDIO_WRITE1 | MDIO_CLK); + mdio_delay (); + } +} +#endif + +static int mdio_read (struct net_device *dev, int phy_id, int location) +{ + struct rtl8139_private *tp = netdev_priv(dev); + int retval = 0; +#ifdef CONFIG_8139TOO_8129 + void __iomem *ioaddr = tp->mmio_addr; + int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; + int i; +#endif + + if (phy_id > 31) { /* Really a 8139. Use internal registers. */ + void __iomem *ioaddr = tp->mmio_addr; + return location < 8 && mii_2_8139_map[location] ? + RTL_R16 (mii_2_8139_map[location]) : 0; + } + +#ifdef CONFIG_8139TOO_8129 + mdio_sync (ioaddr); + /* Shift the read command bits out. */ + for (i = 15; i >= 0; i--) { + int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0; + + RTL_W8 (Config4, MDIO_DIR | dataval); + mdio_delay (); + RTL_W8 (Config4, MDIO_DIR | dataval | MDIO_CLK); + mdio_delay (); + } + + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 19; i > 0; i--) { + RTL_W8 (Config4, 0); + mdio_delay (); + retval = (retval << 1) | ((RTL_R8 (Config4) & MDIO_DATA_IN) ? 1 : 0); + RTL_W8 (Config4, MDIO_CLK); + mdio_delay (); + } +#endif + + return (retval >> 1) & 0xffff; +} + + +static void mdio_write (struct net_device *dev, int phy_id, int location, + int value) +{ + struct rtl8139_private *tp = netdev_priv(dev); +#ifdef CONFIG_8139TOO_8129 + void __iomem *ioaddr = tp->mmio_addr; + int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value; + int i; +#endif + + if (phy_id > 31) { /* Really a 8139. Use internal registers. */ + void __iomem *ioaddr = tp->mmio_addr; + if (location == 0) { + RTL_W8 (Cfg9346, Cfg9346_Unlock); + RTL_W16 (BasicModeCtrl, value); + RTL_W8 (Cfg9346, Cfg9346_Lock); + } else if (location < 8 && mii_2_8139_map[location]) + RTL_W16 (mii_2_8139_map[location], value); + return; + } + +#ifdef CONFIG_8139TOO_8129 + mdio_sync (ioaddr); + + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = + (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; + RTL_W8 (Config4, dataval); + mdio_delay (); + RTL_W8 (Config4, dataval | MDIO_CLK); + mdio_delay (); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + RTL_W8 (Config4, 0); + mdio_delay (); + RTL_W8 (Config4, MDIO_CLK); + mdio_delay (); + } +#endif +} + + +static int rtl8139_open (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + const int irq = tp->pci_dev->irq; + int retval; + + retval = request_irq(irq, rtl8139_interrupt, IRQF_SHARED, dev->name, dev); + if (retval) + return retval; + + tp->tx_bufs = dma_alloc_coherent(&tp->pci_dev->dev, TX_BUF_TOT_LEN, + &tp->tx_bufs_dma, GFP_KERNEL); + tp->rx_ring = dma_alloc_coherent(&tp->pci_dev->dev, RX_BUF_TOT_LEN, + &tp->rx_ring_dma, GFP_KERNEL); + if (tp->tx_bufs == NULL || tp->rx_ring == NULL) { + free_irq(irq, dev); + + if (tp->tx_bufs) + dma_free_coherent(&tp->pci_dev->dev, TX_BUF_TOT_LEN, + tp->tx_bufs, tp->tx_bufs_dma); + if (tp->rx_ring) + dma_free_coherent(&tp->pci_dev->dev, RX_BUF_TOT_LEN, + tp->rx_ring, tp->rx_ring_dma); + + return -ENOMEM; + + } + + napi_enable(&tp->napi); + + tp->mii.full_duplex = tp->mii.force_media; + tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000; + + rtl8139_init_ring (dev); + rtl8139_hw_start (dev); + netif_start_queue (dev); + + netif_dbg(tp, ifup, dev, + "%s() ioaddr %#llx IRQ %d GP Pins %02x %s-duplex\n", + __func__, + (unsigned long long)pci_resource_start (tp->pci_dev, 1), + irq, RTL_R8 (MediaStatus), + tp->mii.full_duplex ? "full" : "half"); + + rtl8139_start_thread(tp); + + return 0; +} + + +static void rtl_check_media (struct net_device *dev, unsigned int init_media) +{ + struct rtl8139_private *tp = netdev_priv(dev); + + if (tp->phys[0] >= 0) { + mii_check_media(&tp->mii, netif_msg_link(tp), init_media); + } +} + +/* Start the hardware at open or resume. */ +static void rtl8139_hw_start (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + u32 i; + u8 tmp; + + /* Bring old chips out of low-power mode. */ + if (rtl_chip_info[tp->chipset].flags & HasHltClk) + RTL_W8 (HltClk, 'R'); + + rtl8139_chip_reset (ioaddr); + + /* unlock Config[01234] and BMCR register writes */ + RTL_W8_F (Cfg9346, Cfg9346_Unlock); + /* Restore our idea of the MAC address. */ + RTL_W32_F (MAC0 + 0, le32_to_cpu (*(__le32 *) (dev->dev_addr + 0))); + RTL_W32_F (MAC0 + 4, le16_to_cpu (*(__le16 *) (dev->dev_addr + 4))); + + tp->cur_rx = 0; + + /* init Rx ring buffer DMA address */ + RTL_W32_F (RxBuf, tp->rx_ring_dma); + + /* Must enable Tx/Rx before setting transfer thresholds! */ + RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); + + tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys; + RTL_W32 (RxConfig, tp->rx_config); + RTL_W32 (TxConfig, rtl8139_tx_config); + + rtl_check_media (dev, 1); + + if (tp->chipset >= CH_8139B) { + /* Disable magic packet scanning, which is enabled + * when PM is enabled in Config1. It can be reenabled + * via ETHTOOL_SWOL if desired. */ + RTL_W8 (Config3, RTL_R8 (Config3) & ~Cfg3_Magic); + } + + netdev_dbg(dev, "init buffer addresses\n"); + + /* Lock Config[01234] and BMCR register writes */ + RTL_W8 (Cfg9346, Cfg9346_Lock); + + /* init Tx buffer DMA addresses */ + for (i = 0; i < NUM_TX_DESC; i++) + RTL_W32_F (TxAddr0 + (i * 4), tp->tx_bufs_dma + (tp->tx_buf[i] - tp->tx_bufs)); + + RTL_W32 (RxMissed, 0); + + rtl8139_set_rx_mode (dev); + + /* no early-rx interrupts */ + RTL_W16 (MultiIntr, RTL_R16 (MultiIntr) & MultiIntrClear); + + /* make sure RxTx has started */ + tmp = RTL_R8 (ChipCmd); + if ((!(tmp & CmdRxEnb)) || (!(tmp & CmdTxEnb))) + RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); + + /* Enable all known interrupts by setting the interrupt mask. */ + RTL_W16 (IntrMask, rtl8139_intr_mask); +} + + +/* Initialize the Rx and Tx rings, along with various 'dev' bits. */ +static void rtl8139_init_ring (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + int i; + + tp->cur_rx = 0; + tp->cur_tx = 0; + tp->dirty_tx = 0; + + for (i = 0; i < NUM_TX_DESC; i++) + tp->tx_buf[i] = &tp->tx_bufs[i * TX_BUF_SIZE]; +} + + +/* This must be global for CONFIG_8139TOO_TUNE_TWISTER case */ +static int next_tick = 3 * HZ; + +#ifndef CONFIG_8139TOO_TUNE_TWISTER +static inline void rtl8139_tune_twister (struct net_device *dev, + struct rtl8139_private *tp) {} +#else +enum TwisterParamVals { + PARA78_default = 0x78fa8388, + PARA7c_default = 0xcb38de43, /* param[0][3] */ + PARA7c_xxx = 0xcb38de43, +}; + +static const unsigned long param[4][4] = { + {0xcb39de43, 0xcb39ce43, 0xfb38de03, 0xcb38de43}, + {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, + {0xcb39de43, 0xcb39ce43, 0xcb39ce83, 0xcb39ce83}, + {0xbb39de43, 0xbb39ce43, 0xbb39ce83, 0xbb39ce83} +}; + +static void rtl8139_tune_twister (struct net_device *dev, + struct rtl8139_private *tp) +{ + int linkcase; + void __iomem *ioaddr = tp->mmio_addr; + + /* This is a complicated state machine to configure the "twister" for + impedance/echos based on the cable length. + All of this is magic and undocumented. + */ + switch (tp->twistie) { + case 1: + if (RTL_R16 (CSCR) & CSCR_LinkOKBit) { + /* We have link beat, let us tune the twister. */ + RTL_W16 (CSCR, CSCR_LinkDownOffCmd); + tp->twistie = 2; /* Change to state 2. */ + next_tick = HZ / 10; + } else { + /* Just put in some reasonable defaults for when beat returns. */ + RTL_W16 (CSCR, CSCR_LinkDownCmd); + RTL_W32 (FIFOTMS, 0x20); /* Turn on cable test mode. */ + RTL_W32 (PARA78, PARA78_default); + RTL_W32 (PARA7c, PARA7c_default); + tp->twistie = 0; /* Bail from future actions. */ + } + break; + case 2: + /* Read how long it took to hear the echo. */ + linkcase = RTL_R16 (CSCR) & CSCR_LinkStatusBits; + if (linkcase == 0x7000) + tp->twist_row = 3; + else if (linkcase == 0x3000) + tp->twist_row = 2; + else if (linkcase == 0x1000) + tp->twist_row = 1; + else + tp->twist_row = 0; + tp->twist_col = 0; + tp->twistie = 3; /* Change to state 2. */ + next_tick = HZ / 10; + break; + case 3: + /* Put out four tuning parameters, one per 100msec. */ + if (tp->twist_col == 0) + RTL_W16 (FIFOTMS, 0); + RTL_W32 (PARA7c, param[(int) tp->twist_row] + [(int) tp->twist_col]); + next_tick = HZ / 10; + if (++tp->twist_col >= 4) { + /* For short cables we are done. + For long cables (row == 3) check for mistune. */ + tp->twistie = + (tp->twist_row == 3) ? 4 : 0; + } + break; + case 4: + /* Special case for long cables: check for mistune. */ + if ((RTL_R16 (CSCR) & + CSCR_LinkStatusBits) == 0x7000) { + tp->twistie = 0; + break; + } else { + RTL_W32 (PARA7c, 0xfb38de03); + tp->twistie = 5; + next_tick = HZ / 10; + } + break; + case 5: + /* Retune for shorter cable (column 2). */ + RTL_W32 (FIFOTMS, 0x20); + RTL_W32 (PARA78, PARA78_default); + RTL_W32 (PARA7c, PARA7c_default); + RTL_W32 (FIFOTMS, 0x00); + tp->twist_row = 2; + tp->twist_col = 0; + tp->twistie = 3; + next_tick = HZ / 10; + break; + + default: + /* do nothing */ + break; + } +} +#endif /* CONFIG_8139TOO_TUNE_TWISTER */ + +static inline void rtl8139_thread_iter (struct net_device *dev, + struct rtl8139_private *tp, + void __iomem *ioaddr) +{ + int mii_lpa; + + mii_lpa = mdio_read (dev, tp->phys[0], MII_LPA); + + if (!tp->mii.force_media && mii_lpa != 0xffff) { + int duplex = ((mii_lpa & LPA_100FULL) || + (mii_lpa & 0x01C0) == 0x0040); + if (tp->mii.full_duplex != duplex) { + tp->mii.full_duplex = duplex; + + if (mii_lpa) { + netdev_info(dev, "Setting %s-duplex based on MII #%d link partner ability of %04x\n", + tp->mii.full_duplex ? "full" : "half", + tp->phys[0], mii_lpa); + } else { + netdev_info(dev, "media is unconnected, link down, or incompatible connection\n"); + } +#if 0 + RTL_W8 (Cfg9346, Cfg9346_Unlock); + RTL_W8 (Config1, tp->mii.full_duplex ? 0x60 : 0x20); + RTL_W8 (Cfg9346, Cfg9346_Lock); +#endif + } + } + + next_tick = HZ * 60; + + rtl8139_tune_twister (dev, tp); + + netdev_dbg(dev, "Media selection tick, Link partner %04x\n", + RTL_R16(NWayLPAR)); + netdev_dbg(dev, "Other registers are IntMask %04x IntStatus %04x\n", + RTL_R16(IntrMask), RTL_R16(IntrStatus)); + netdev_dbg(dev, "Chip config %02x %02x\n", + RTL_R8(Config0), RTL_R8(Config1)); +} + +static void rtl8139_thread (struct work_struct *work) +{ + struct rtl8139_private *tp = + container_of(work, struct rtl8139_private, thread.work); + struct net_device *dev = tp->mii.dev; + unsigned long thr_delay = next_tick; + + rtnl_lock(); + + if (!netif_running(dev)) + goto out_unlock; + + if (tp->watchdog_fired) { + tp->watchdog_fired = 0; + rtl8139_tx_timeout_task(work); + } else + rtl8139_thread_iter(dev, tp, tp->mmio_addr); + + if (tp->have_thread) + schedule_delayed_work(&tp->thread, thr_delay); +out_unlock: + rtnl_unlock (); +} + +static void rtl8139_start_thread(struct rtl8139_private *tp) +{ + tp->twistie = 0; + if (tp->chipset == CH_8139_K) + tp->twistie = 1; + else if (tp->drv_flags & HAS_LNK_CHNG) + return; + + tp->have_thread = 1; + tp->watchdog_fired = 0; + + schedule_delayed_work(&tp->thread, next_tick); +} + +static inline void rtl8139_tx_clear (struct rtl8139_private *tp) +{ + tp->cur_tx = 0; + tp->dirty_tx = 0; + + /* XXX account for unsent Tx packets in tp->stats.tx_dropped */ +} + +static void rtl8139_tx_timeout_task (struct work_struct *work) +{ + struct rtl8139_private *tp = + container_of(work, struct rtl8139_private, thread.work); + struct net_device *dev = tp->mii.dev; + void __iomem *ioaddr = tp->mmio_addr; + int i; + u8 tmp8; + + napi_disable(&tp->napi); + netif_stop_queue(dev); + synchronize_rcu(); + + netdev_dbg(dev, "Transmit timeout, status %02x %04x %04x media %02x\n", + RTL_R8(ChipCmd), RTL_R16(IntrStatus), + RTL_R16(IntrMask), RTL_R8(MediaStatus)); + /* Emit info to figure out what went wrong. */ + netdev_dbg(dev, "Tx queue start entry %ld dirty entry %ld\n", + tp->cur_tx, tp->dirty_tx); + for (i = 0; i < NUM_TX_DESC; i++) + netdev_dbg(dev, "Tx descriptor %d is %08x%s\n", + i, RTL_R32(TxStatus0 + (i * 4)), + i == tp->dirty_tx % NUM_TX_DESC ? + " (queue head)" : ""); + + tp->xstats.tx_timeouts++; + + /* disable Tx ASAP, if not already */ + tmp8 = RTL_R8 (ChipCmd); + if (tmp8 & CmdTxEnb) + RTL_W8 (ChipCmd, CmdRxEnb); + + netdev_lock(dev); + spin_lock_bh(&tp->rx_lock); + /* Disable interrupts by clearing the interrupt mask. */ + RTL_W16 (IntrMask, 0x0000); + + /* Stop a shared interrupt from scavenging while we are. */ + spin_lock_irq(&tp->lock); + rtl8139_tx_clear (tp); + spin_unlock_irq(&tp->lock); + + /* ...and finally, reset everything */ + napi_enable_locked(&tp->napi); + rtl8139_hw_start(dev); + netif_wake_queue(dev); + + spin_unlock_bh(&tp->rx_lock); + netdev_unlock(dev); +} + +static void rtl8139_tx_timeout(struct net_device *dev, unsigned int txqueue) +{ + struct rtl8139_private *tp = netdev_priv(dev); + + tp->watchdog_fired = 1; + if (!tp->have_thread) { + INIT_DELAYED_WORK(&tp->thread, rtl8139_thread); + schedule_delayed_work(&tp->thread, next_tick); + } +} + +static netdev_tx_t rtl8139_start_xmit (struct sk_buff *skb, + struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned int entry; + unsigned int len = skb->len; + unsigned long flags; + + /* Calculate the next Tx descriptor entry. */ + entry = tp->cur_tx % NUM_TX_DESC; + + /* Note: the chip doesn't have auto-pad! */ + if (likely(len < TX_BUF_SIZE)) { + if (len < ETH_ZLEN) + memset(tp->tx_buf[entry], 0, ETH_ZLEN); + skb_copy_and_csum_dev(skb, tp->tx_buf[entry]); + dev_kfree_skb_any(skb); + } else { + dev_kfree_skb_any(skb); + dev->stats.tx_dropped++; + return NETDEV_TX_OK; + } + + spin_lock_irqsave(&tp->lock, flags); + /* + * Writing to TxStatus triggers a DMA transfer of the data + * copied to tp->tx_buf[entry] above. Use a memory barrier + * to make sure that the device sees the updated data. + */ + wmb(); + RTL_W32_F (TxStatus0 + (entry * sizeof (u32)), + tp->tx_flag | max(len, (unsigned int)ETH_ZLEN)); + + tp->cur_tx++; + + if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx) + netif_stop_queue (dev); + spin_unlock_irqrestore(&tp->lock, flags); + + netif_dbg(tp, tx_queued, dev, "Queued Tx packet size %u to slot %d\n", + len, entry); + + return NETDEV_TX_OK; +} + + +static void rtl8139_tx_interrupt (struct net_device *dev, + struct rtl8139_private *tp, + void __iomem *ioaddr) +{ + unsigned long dirty_tx, tx_left; + + assert (dev != NULL); + assert (ioaddr != NULL); + + dirty_tx = tp->dirty_tx; + tx_left = tp->cur_tx - dirty_tx; + while (tx_left > 0) { + int entry = dirty_tx % NUM_TX_DESC; + int txstatus; + + txstatus = RTL_R32 (TxStatus0 + (entry * sizeof (u32))); + + if (!(txstatus & (TxStatOK | TxUnderrun | TxAborted))) + break; /* It still hasn't been Txed */ + + /* Note: TxCarrierLost is always asserted at 100mbps. */ + if (txstatus & (TxOutOfWindow | TxAborted)) { + /* There was an major error, log it. */ + netif_dbg(tp, tx_err, dev, "Transmit error, Tx status %08x\n", + txstatus); + dev->stats.tx_errors++; + if (txstatus & TxAborted) { + dev->stats.tx_aborted_errors++; + RTL_W32 (TxConfig, TxClearAbt); + RTL_W16 (IntrStatus, TxErr); + wmb(); + } + if (txstatus & TxCarrierLost) + dev->stats.tx_carrier_errors++; + if (txstatus & TxOutOfWindow) + dev->stats.tx_window_errors++; + } else { + if (txstatus & TxUnderrun) { + /* Add 64 to the Tx FIFO threshold. */ + if (tp->tx_flag < 0x00300000) + tp->tx_flag += 0x00020000; + dev->stats.tx_fifo_errors++; + } + dev->stats.collisions += (txstatus >> 24) & 15; + u64_stats_update_begin(&tp->tx_stats.syncp); + tp->tx_stats.packets++; + tp->tx_stats.bytes += txstatus & 0x7ff; + u64_stats_update_end(&tp->tx_stats.syncp); + } + + dirty_tx++; + tx_left--; + } + +#ifndef RTL8139_NDEBUG + if (tp->cur_tx - dirty_tx > NUM_TX_DESC) { + netdev_err(dev, "Out-of-sync dirty pointer, %ld vs. %ld\n", + dirty_tx, tp->cur_tx); + dirty_tx += NUM_TX_DESC; + } +#endif /* RTL8139_NDEBUG */ + + /* only wake the queue if we did work, and the queue is stopped */ + if (tp->dirty_tx != dirty_tx) { + tp->dirty_tx = dirty_tx; + mb(); + netif_wake_queue (dev); + } +} + + +/* TODO: clean this up! Rx reset need not be this intensive */ +static void rtl8139_rx_err (u32 rx_status, struct net_device *dev, + struct rtl8139_private *tp, void __iomem *ioaddr) +{ + u8 tmp8; +#ifdef CONFIG_8139_OLD_RX_RESET + int tmp_work; +#endif + + netif_dbg(tp, rx_err, dev, "Ethernet frame had errors, status %08x\n", + rx_status); + dev->stats.rx_errors++; + if (!(rx_status & RxStatusOK)) { + if (rx_status & RxTooLong) { + netdev_dbg(dev, "Oversized Ethernet frame, status %04x!\n", + rx_status); + /* A.C.: The chip hangs here. */ + } + if (rx_status & (RxBadSymbol | RxBadAlign)) + dev->stats.rx_frame_errors++; + if (rx_status & (RxRunt | RxTooLong)) + dev->stats.rx_length_errors++; + if (rx_status & RxCRCErr) + dev->stats.rx_crc_errors++; + } else { + tp->xstats.rx_lost_in_ring++; + } + +#ifndef CONFIG_8139_OLD_RX_RESET + tmp8 = RTL_R8 (ChipCmd); + RTL_W8 (ChipCmd, tmp8 & ~CmdRxEnb); + RTL_W8 (ChipCmd, tmp8); + RTL_W32 (RxConfig, tp->rx_config); + tp->cur_rx = 0; +#else + /* Reset the receiver, based on RealTek recommendation. (Bug?) */ + + /* disable receive */ + RTL_W8_F (ChipCmd, CmdTxEnb); + tmp_work = 200; + while (--tmp_work > 0) { + udelay(1); + tmp8 = RTL_R8 (ChipCmd); + if (!(tmp8 & CmdRxEnb)) + break; + } + if (tmp_work <= 0) + netdev_warn(dev, "rx stop wait too long\n"); + /* restart receive */ + tmp_work = 200; + while (--tmp_work > 0) { + RTL_W8_F (ChipCmd, CmdRxEnb | CmdTxEnb); + udelay(1); + tmp8 = RTL_R8 (ChipCmd); + if ((tmp8 & CmdRxEnb) && (tmp8 & CmdTxEnb)) + break; + } + if (tmp_work <= 0) + netdev_warn(dev, "tx/rx enable wait too long\n"); + + /* and reinitialize all rx related registers */ + RTL_W8_F (Cfg9346, Cfg9346_Unlock); + /* Must enable Tx/Rx before setting transfer thresholds! */ + RTL_W8 (ChipCmd, CmdRxEnb | CmdTxEnb); + + tp->rx_config = rtl8139_rx_config | AcceptBroadcast | AcceptMyPhys; + RTL_W32 (RxConfig, tp->rx_config); + tp->cur_rx = 0; + + netdev_dbg(dev, "init buffer addresses\n"); + + /* Lock Config[01234] and BMCR register writes */ + RTL_W8 (Cfg9346, Cfg9346_Lock); + + /* init Rx ring buffer DMA address */ + RTL_W32_F (RxBuf, tp->rx_ring_dma); + + /* A.C.: Reset the multicast list. */ + __set_rx_mode (dev); +#endif +} + +#if RX_BUF_IDX == 3 +static inline void wrap_copy(struct sk_buff *skb, const unsigned char *ring, + u32 offset, unsigned int size) +{ + u32 left = RX_BUF_LEN - offset; + + if (size > left) { + skb_copy_to_linear_data(skb, ring + offset, left); + skb_copy_to_linear_data_offset(skb, left, ring, size - left); + } else + skb_copy_to_linear_data(skb, ring + offset, size); +} +#endif + +static void rtl8139_isr_ack(struct rtl8139_private *tp) +{ + void __iomem *ioaddr = tp->mmio_addr; + u16 status; + + status = RTL_R16 (IntrStatus) & RxAckBits; + + /* Clear out errors and receive interrupts */ + if (likely(status != 0)) { + if (unlikely(status & (RxFIFOOver | RxOverflow))) { + tp->dev->stats.rx_errors++; + if (status & RxFIFOOver) + tp->dev->stats.rx_fifo_errors++; + } + RTL_W16_F (IntrStatus, RxAckBits); + } +} + +static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp, + int budget) +{ + void __iomem *ioaddr = tp->mmio_addr; + int received = 0; + unsigned char *rx_ring = tp->rx_ring; + unsigned int cur_rx = tp->cur_rx; + unsigned int rx_size = 0; + + netdev_dbg(dev, "In %s(), current %04x BufAddr %04x, free to %04x, Cmd %02x\n", + __func__, (u16)cur_rx, + RTL_R16(RxBufAddr), RTL_R16(RxBufPtr), RTL_R8(ChipCmd)); + + while (netif_running(dev) && received < budget && + (RTL_R8 (ChipCmd) & RxBufEmpty) == 0) { + u32 ring_offset = cur_rx % RX_BUF_LEN; + u32 rx_status; + unsigned int pkt_size; + struct sk_buff *skb; + + rmb(); + + /* read size+status of next frame from DMA ring buffer */ + rx_status = le32_to_cpu (*(__le32 *) (rx_ring + ring_offset)); + rx_size = rx_status >> 16; + if (likely(!(dev->features & NETIF_F_RXFCS))) + pkt_size = rx_size - 4; + else + pkt_size = rx_size; + + netif_dbg(tp, rx_status, dev, "%s() status %04x, size %04x, cur %04x\n", + __func__, rx_status, rx_size, cur_rx); +#if RTL8139_DEBUG > 2 + print_hex_dump(KERN_DEBUG, "Frame contents: ", + DUMP_PREFIX_OFFSET, 16, 1, + &rx_ring[ring_offset], 70, true); +#endif + + /* Packet copy from FIFO still in progress. + * Theoretically, this should never happen + * since EarlyRx is disabled. + */ + if (unlikely(rx_size == 0xfff0)) { + if (!tp->fifo_copy_timeout) + tp->fifo_copy_timeout = jiffies + 2; + else if (time_after(jiffies, tp->fifo_copy_timeout)) { + netdev_dbg(dev, "hung FIFO. Reset\n"); + rx_size = 0; + goto no_early_rx; + } + netif_dbg(tp, intr, dev, "fifo copy in progress\n"); + tp->xstats.early_rx++; + break; + } + +no_early_rx: + tp->fifo_copy_timeout = 0; + + /* If Rx err or invalid rx_size/rx_status received + * (which happens if we get lost in the ring), + * Rx process gets reset, so we abort any further + * Rx processing. + */ + if (unlikely((rx_size > (MAX_ETH_FRAME_SIZE+4)) || + (rx_size < 8) || + (!(rx_status & RxStatusOK)))) { + if ((dev->features & NETIF_F_RXALL) && + (rx_size <= (MAX_ETH_FRAME_SIZE + 4)) && + (rx_size >= 8) && + (!(rx_status & RxStatusOK))) { + /* Length is at least mostly OK, but pkt has + * error. I'm hoping we can handle some of these + * errors without resetting the chip. --Ben + */ + dev->stats.rx_errors++; + if (rx_status & RxCRCErr) { + dev->stats.rx_crc_errors++; + goto keep_pkt; + } + if (rx_status & RxRunt) { + dev->stats.rx_length_errors++; + goto keep_pkt; + } + } + rtl8139_rx_err (rx_status, dev, tp, ioaddr); + received = -1; + goto out; + } + +keep_pkt: + /* Malloc up new buffer, compatible with net-2e. */ + /* Omit the four octet CRC from the length. */ + + skb = napi_alloc_skb(&tp->napi, pkt_size); + if (likely(skb)) { +#if RX_BUF_IDX == 3 + wrap_copy(skb, rx_ring, ring_offset+4, pkt_size); +#else + skb_copy_to_linear_data (skb, &rx_ring[ring_offset + 4], pkt_size); +#endif + skb_put (skb, pkt_size); + + skb->protocol = eth_type_trans (skb, dev); + + u64_stats_update_begin(&tp->rx_stats.syncp); + tp->rx_stats.packets++; + tp->rx_stats.bytes += pkt_size; + u64_stats_update_end(&tp->rx_stats.syncp); + + netif_receive_skb (skb); + } else { + dev->stats.rx_dropped++; + } + received++; + + cur_rx = (cur_rx + rx_size + 4 + 3) & ~3; + RTL_W16 (RxBufPtr, (u16) (cur_rx - 16)); + + rtl8139_isr_ack(tp); + } + + if (unlikely(!received || rx_size == 0xfff0)) + rtl8139_isr_ack(tp); + + netdev_dbg(dev, "Done %s(), current %04x BufAddr %04x, free to %04x, Cmd %02x\n", + __func__, cur_rx, + RTL_R16(RxBufAddr), RTL_R16(RxBufPtr), RTL_R8(ChipCmd)); + + tp->cur_rx = cur_rx; + + /* + * The receive buffer should be mostly empty. + * Tell NAPI to reenable the Rx irq. + */ + if (tp->fifo_copy_timeout) + received = budget; + +out: + return received; +} + + +static void rtl8139_weird_interrupt (struct net_device *dev, + struct rtl8139_private *tp, + void __iomem *ioaddr, + int status, int link_changed) +{ + netdev_dbg(dev, "Abnormal interrupt, status %08x\n", status); + + assert (dev != NULL); + assert (tp != NULL); + assert (ioaddr != NULL); + + /* Update the error count. */ + dev->stats.rx_missed_errors += RTL_R32 (RxMissed); + RTL_W32 (RxMissed, 0); + + if ((status & RxUnderrun) && link_changed && + (tp->drv_flags & HAS_LNK_CHNG)) { + rtl_check_media(dev, 0); + status &= ~RxUnderrun; + } + + if (status & (RxUnderrun | RxErr)) + dev->stats.rx_errors++; + + if (status & PCSTimeout) + dev->stats.rx_length_errors++; + if (status & RxUnderrun) + dev->stats.rx_fifo_errors++; + if (status & PCIErr) { + u16 pci_cmd_status; + pci_read_config_word (tp->pci_dev, PCI_STATUS, &pci_cmd_status); + pci_write_config_word (tp->pci_dev, PCI_STATUS, pci_cmd_status); + + netdev_err(dev, "PCI Bus error %04x\n", pci_cmd_status); + } +} + +static int rtl8139_poll(struct napi_struct *napi, int budget) +{ + struct rtl8139_private *tp = container_of(napi, struct rtl8139_private, napi); + struct net_device *dev = tp->dev; + void __iomem *ioaddr = tp->mmio_addr; + int work_done; + + spin_lock(&tp->rx_lock); + work_done = 0; + if (likely(RTL_R16(IntrStatus) & RxAckBits)) + work_done += rtl8139_rx(dev, tp, budget); + + if (work_done < budget) { + unsigned long flags; + + spin_lock_irqsave(&tp->lock, flags); + if (napi_complete_done(napi, work_done)) + RTL_W16_F(IntrMask, rtl8139_intr_mask); + spin_unlock_irqrestore(&tp->lock, flags); + } + spin_unlock(&tp->rx_lock); + + return work_done; +} + +/* The interrupt handler does all of the Rx thread work and cleans up + after the Tx thread. */ +static irqreturn_t rtl8139_interrupt (int irq, void *dev_instance) +{ + struct net_device *dev = (struct net_device *) dev_instance; + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + u16 status, ackstat; + int link_changed = 0; /* avoid bogus "uninit" warning */ + int handled = 0; + + spin_lock (&tp->lock); + status = RTL_R16 (IntrStatus); + + /* shared irq? */ + if (unlikely((status & rtl8139_intr_mask) == 0)) + goto out; + + handled = 1; + + /* h/w no longer present (hotplug?) or major error, bail */ + if (unlikely(status == 0xFFFF)) + goto out; + + /* close possible race's with dev_close */ + if (unlikely(!netif_running(dev))) { + RTL_W16 (IntrMask, 0); + goto out; + } + + /* Acknowledge all of the current interrupt sources ASAP, but + an first get an additional status bit from CSCR. */ + if (unlikely(status & RxUnderrun)) + link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit; + + ackstat = status & ~(RxAckBits | TxErr); + if (ackstat) + RTL_W16 (IntrStatus, ackstat); + + /* Receive packets are processed by poll routine. + If not running start it now. */ + if (status & RxAckBits){ + if (napi_schedule_prep(&tp->napi)) { + RTL_W16_F (IntrMask, rtl8139_norx_intr_mask); + __napi_schedule(&tp->napi); + } + } + + /* Check uncommon events with one test. */ + if (unlikely(status & (PCIErr | PCSTimeout | RxUnderrun | RxErr))) + rtl8139_weird_interrupt (dev, tp, ioaddr, + status, link_changed); + + if (status & (TxOK | TxErr)) { + rtl8139_tx_interrupt (dev, tp, ioaddr); + if (status & TxErr) + RTL_W16 (IntrStatus, TxErr); + } + out: + spin_unlock (&tp->lock); + + netdev_dbg(dev, "exiting interrupt, intr_status=%#4.4x\n", + RTL_R16(IntrStatus)); + return IRQ_RETVAL(handled); +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +/* + * Polling receive - used by netconsole and other diagnostic tools + * to allow network i/o with interrupts disabled. + */ +static void rtl8139_poll_controller(struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + const int irq = tp->pci_dev->irq; + + disable_irq_nosync(irq); + rtl8139_interrupt(irq, dev); + enable_irq(irq); +} +#endif + +static int rtl8139_set_mac_address(struct net_device *dev, void *p) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + struct sockaddr *addr = p; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + eth_hw_addr_set(dev, addr->sa_data); + + spin_lock_irq(&tp->lock); + + RTL_W8_F(Cfg9346, Cfg9346_Unlock); + RTL_W32_F(MAC0 + 0, cpu_to_le32 (*(u32 *) (dev->dev_addr + 0))); + RTL_W32_F(MAC0 + 4, cpu_to_le32 (*(u32 *) (dev->dev_addr + 4))); + RTL_W8_F(Cfg9346, Cfg9346_Lock); + + spin_unlock_irq(&tp->lock); + + return 0; +} + +static int rtl8139_close (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned long flags; + + netif_stop_queue(dev); + napi_disable(&tp->napi); + + netif_dbg(tp, ifdown, dev, "Shutting down ethercard, status was 0x%04x\n", + RTL_R16(IntrStatus)); + + spin_lock_irqsave (&tp->lock, flags); + + /* Stop the chip's Tx and Rx DMA processes. */ + RTL_W8 (ChipCmd, 0); + + /* Disable interrupts by clearing the interrupt mask. */ + RTL_W16 (IntrMask, 0); + + /* Update the error counts. */ + dev->stats.rx_missed_errors += RTL_R32 (RxMissed); + RTL_W32 (RxMissed, 0); + + spin_unlock_irqrestore (&tp->lock, flags); + + free_irq(tp->pci_dev->irq, dev); + + rtl8139_tx_clear (tp); + + dma_free_coherent(&tp->pci_dev->dev, RX_BUF_TOT_LEN, + tp->rx_ring, tp->rx_ring_dma); + dma_free_coherent(&tp->pci_dev->dev, TX_BUF_TOT_LEN, + tp->tx_bufs, tp->tx_bufs_dma); + tp->rx_ring = NULL; + tp->tx_bufs = NULL; + + /* Green! Put the chip in low-power mode. */ + RTL_W8 (Cfg9346, Cfg9346_Unlock); + + if (rtl_chip_info[tp->chipset].flags & HasHltClk) + RTL_W8 (HltClk, 'H'); /* 'R' would leave the clock running. */ + + return 0; +} + + +/* Get the ethtool Wake-on-LAN settings. Assumes that wol points to + kernel memory, *wol has been initialized as {ETHTOOL_GWOL}, and + other threads or interrupts aren't messing with the 8139. */ +static void rtl8139_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + + spin_lock_irq(&tp->lock); + if (rtl_chip_info[tp->chipset].flags & HasLWake) { + u8 cfg3 = RTL_R8 (Config3); + u8 cfg5 = RTL_R8 (Config5); + + wol->supported = WAKE_PHY | WAKE_MAGIC + | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST; + + wol->wolopts = 0; + if (cfg3 & Cfg3_LinkUp) + wol->wolopts |= WAKE_PHY; + if (cfg3 & Cfg3_Magic) + wol->wolopts |= WAKE_MAGIC; + /* (KON)FIXME: See how netdev_set_wol() handles the + following constants. */ + if (cfg5 & Cfg5_UWF) + wol->wolopts |= WAKE_UCAST; + if (cfg5 & Cfg5_MWF) + wol->wolopts |= WAKE_MCAST; + if (cfg5 & Cfg5_BWF) + wol->wolopts |= WAKE_BCAST; + } + spin_unlock_irq(&tp->lock); +} + + +/* Set the ethtool Wake-on-LAN settings. Return 0 or -errno. Assumes + that wol points to kernel memory and other threads or interrupts + aren't messing with the 8139. */ +static int rtl8139_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + u32 support; + u8 cfg3, cfg5; + + support = ((rtl_chip_info[tp->chipset].flags & HasLWake) + ? (WAKE_PHY | WAKE_MAGIC + | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST) + : 0); + if (wol->wolopts & ~support) + return -EINVAL; + + spin_lock_irq(&tp->lock); + cfg3 = RTL_R8 (Config3) & ~(Cfg3_LinkUp | Cfg3_Magic); + if (wol->wolopts & WAKE_PHY) + cfg3 |= Cfg3_LinkUp; + if (wol->wolopts & WAKE_MAGIC) + cfg3 |= Cfg3_Magic; + RTL_W8 (Cfg9346, Cfg9346_Unlock); + RTL_W8 (Config3, cfg3); + RTL_W8 (Cfg9346, Cfg9346_Lock); + + cfg5 = RTL_R8 (Config5) & ~(Cfg5_UWF | Cfg5_MWF | Cfg5_BWF); + /* (KON)FIXME: These are untested. We may have to set the + CRC0, Wakeup0 and LSBCRC0 registers too, but I have no + documentation. */ + if (wol->wolopts & WAKE_UCAST) + cfg5 |= Cfg5_UWF; + if (wol->wolopts & WAKE_MCAST) + cfg5 |= Cfg5_MWF; + if (wol->wolopts & WAKE_BCAST) + cfg5 |= Cfg5_BWF; + RTL_W8 (Config5, cfg5); /* need not unlock via Cfg9346 */ + spin_unlock_irq(&tp->lock); + + return 0; +} + +static void rtl8139_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + struct rtl8139_private *tp = netdev_priv(dev); + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->bus_info, pci_name(tp->pci_dev), sizeof(info->bus_info)); +} + +static int rtl8139_get_link_ksettings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) +{ + struct rtl8139_private *tp = netdev_priv(dev); + spin_lock_irq(&tp->lock); + mii_ethtool_get_link_ksettings(&tp->mii, cmd); + spin_unlock_irq(&tp->lock); + return 0; +} + +static int rtl8139_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd) +{ + struct rtl8139_private *tp = netdev_priv(dev); + int rc; + spin_lock_irq(&tp->lock); + rc = mii_ethtool_set_link_ksettings(&tp->mii, cmd); + spin_unlock_irq(&tp->lock); + return rc; +} + +static int rtl8139_nway_reset(struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + return mii_nway_restart(&tp->mii); +} + +static u32 rtl8139_get_link(struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + return mii_link_ok(&tp->mii); +} + +static u32 rtl8139_get_msglevel(struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + return tp->msg_enable; +} + +static void rtl8139_set_msglevel(struct net_device *dev, u32 datum) +{ + struct rtl8139_private *tp = netdev_priv(dev); + tp->msg_enable = datum; +} + +static int rtl8139_get_regs_len(struct net_device *dev) +{ + struct rtl8139_private *tp; + /* TODO: we are too slack to do reg dumping for pio, for now */ + if (use_io) + return 0; + tp = netdev_priv(dev); + return tp->regs_len; +} + +static void rtl8139_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf) +{ + struct rtl8139_private *tp; + + /* TODO: we are too slack to do reg dumping for pio, for now */ + if (use_io) + return; + tp = netdev_priv(dev); + + regs->version = RTL_REGS_VER; + + spin_lock_irq(&tp->lock); + memcpy_fromio(regbuf, tp->mmio_addr, regs->len); + spin_unlock_irq(&tp->lock); +} + +static int rtl8139_get_sset_count(struct net_device *dev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return RTL_NUM_STATS; + default: + return -EOPNOTSUPP; + } +} + +static void rtl8139_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) +{ + struct rtl8139_private *tp = netdev_priv(dev); + + data[0] = tp->xstats.early_rx; + data[1] = tp->xstats.tx_buf_mapped; + data[2] = tp->xstats.tx_timeouts; + data[3] = tp->xstats.rx_lost_in_ring; +} + +static void rtl8139_get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ + memcpy(data, ethtool_stats_keys, sizeof(ethtool_stats_keys)); +} + +static const struct ethtool_ops rtl8139_ethtool_ops = { + .get_drvinfo = rtl8139_get_drvinfo, + .get_regs_len = rtl8139_get_regs_len, + .get_regs = rtl8139_get_regs, + .nway_reset = rtl8139_nway_reset, + .get_link = rtl8139_get_link, + .get_msglevel = rtl8139_get_msglevel, + .set_msglevel = rtl8139_set_msglevel, + .get_wol = rtl8139_get_wol, + .set_wol = rtl8139_set_wol, + .get_strings = rtl8139_get_strings, + .get_sset_count = rtl8139_get_sset_count, + .get_ethtool_stats = rtl8139_get_ethtool_stats, + .get_link_ksettings = rtl8139_get_link_ksettings, + .set_link_ksettings = rtl8139_set_link_ksettings, +}; + +static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct rtl8139_private *tp = netdev_priv(dev); + int rc; + + if (!netif_running(dev)) + return -EINVAL; + + spin_lock_irq(&tp->lock); + rc = generic_mii_ioctl(&tp->mii, if_mii(rq), cmd, NULL); + spin_unlock_irq(&tp->lock); + + return rc; +} + + +static void +rtl8139_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned long flags; + unsigned int start; + + if (netif_running(dev)) { + spin_lock_irqsave (&tp->lock, flags); + dev->stats.rx_missed_errors += RTL_R32 (RxMissed); + RTL_W32 (RxMissed, 0); + spin_unlock_irqrestore (&tp->lock, flags); + } + + netdev_stats_to_stats64(stats, &dev->stats); + + do { + start = u64_stats_fetch_begin(&tp->rx_stats.syncp); + stats->rx_packets = tp->rx_stats.packets; + stats->rx_bytes = tp->rx_stats.bytes; + } while (u64_stats_fetch_retry(&tp->rx_stats.syncp, start)); + + do { + start = u64_stats_fetch_begin(&tp->tx_stats.syncp); + stats->tx_packets = tp->tx_stats.packets; + stats->tx_bytes = tp->tx_stats.bytes; + } while (u64_stats_fetch_retry(&tp->tx_stats.syncp, start)); +} + +/* Set or clear the multicast filter for this adaptor. + This routine is not state sensitive and need not be SMP locked. */ + +static void __set_rx_mode (struct net_device *dev) +{ + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + u32 mc_filter[2]; /* Multicast hash filter */ + int rx_mode; + u32 tmp; + + netdev_dbg(dev, "rtl8139_set_rx_mode(%04x) done -- Rx config %08x\n", + dev->flags, RTL_R32(RxConfig)); + + /* Note: do not reorder, GCC is clever about common statements. */ + if (dev->flags & IFF_PROMISC) { + rx_mode = + AcceptBroadcast | AcceptMulticast | AcceptMyPhys | + AcceptAllPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; + } else if ((netdev_mc_count(dev) > multicast_filter_limit) || + (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter perfectly -- accept all multicasts. */ + rx_mode = AcceptBroadcast | AcceptMulticast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0xffffffff; + } else { + struct netdev_hw_addr *ha; + rx_mode = AcceptBroadcast | AcceptMyPhys; + mc_filter[1] = mc_filter[0] = 0; + netdev_for_each_mc_addr(ha, dev) { + int bit_nr = ether_crc(ETH_ALEN, ha->addr) >> 26; + + mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); + rx_mode |= AcceptMulticast; + } + } + + if (dev->features & NETIF_F_RXALL) + rx_mode |= (AcceptErr | AcceptRunt); + + /* We can safely update without stopping the chip. */ + tmp = rtl8139_rx_config | rx_mode; + if (tp->rx_config != tmp) { + RTL_W32_F (RxConfig, tmp); + tp->rx_config = tmp; + } + RTL_W32_F (MAR0 + 0, mc_filter[0]); + RTL_W32_F (MAR0 + 4, mc_filter[1]); +} + +static void rtl8139_set_rx_mode (struct net_device *dev) +{ + unsigned long flags; + struct rtl8139_private *tp = netdev_priv(dev); + + spin_lock_irqsave (&tp->lock, flags); + __set_rx_mode(dev); + spin_unlock_irqrestore (&tp->lock, flags); +} + +static int __maybe_unused rtl8139_suspend(struct device *device) +{ + struct net_device *dev = dev_get_drvdata(device); + struct rtl8139_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned long flags; + + if (!netif_running (dev)) + return 0; + + netif_device_detach (dev); + + spin_lock_irqsave (&tp->lock, flags); + + /* Disable interrupts, stop Tx and Rx. */ + RTL_W16 (IntrMask, 0); + RTL_W8 (ChipCmd, 0); + + /* Update the error counts. */ + dev->stats.rx_missed_errors += RTL_R32 (RxMissed); + RTL_W32 (RxMissed, 0); + + spin_unlock_irqrestore (&tp->lock, flags); + + return 0; +} + +static int __maybe_unused rtl8139_resume(struct device *device) +{ + struct net_device *dev = dev_get_drvdata(device); + + if (!netif_running (dev)) + return 0; + + rtl8139_init_ring (dev); + rtl8139_hw_start (dev); + netif_device_attach (dev); + return 0; +} + +static SIMPLE_DEV_PM_OPS(rtl8139_pm_ops, rtl8139_suspend, rtl8139_resume); + +static struct pci_driver rtl8139_pci_driver = { + .name = DRV_NAME, + .id_table = rtl8139_pci_tbl, + .probe = rtl8139_init_one, + .remove = rtl8139_remove_one, + .driver.pm = &rtl8139_pm_ops, +}; + +module_pci_driver(rtl8139_pci_driver); diff --git a/drivers/net/ethernet/realtek/Kconfig b/drivers/net/ethernet/realtek/Kconfig new file mode 100644 index 000000000..9b0f4f963 --- /dev/null +++ b/drivers/net/ethernet/realtek/Kconfig @@ -0,0 +1,129 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Realtek device configuration +# + +config NET_VENDOR_REALTEK + bool "Realtek devices" + default y + depends on PCI + help + If you have a network (Ethernet) card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about Realtek devices. If you say Y, you will be asked for + your specific card in the following questions. + +if NET_VENDOR_REALTEK + +config 8139CP + tristate "RealTek RTL-8139 C+ PCI Fast Ethernet Adapter support" + depends on PCI + select CRC32 + select MII + help + This is a driver for the Fast Ethernet PCI network cards based on + the RTL8139C+ chips. If you have one of those, say Y here. + + To compile this driver as a module, choose M here: the module + will be called 8139cp. This is recommended. + +config 8139TOO + tristate "RealTek RTL-8129/8130/8139 PCI Fast Ethernet Adapter support" + depends on PCI + select CRC32 + select MII + help + This is a driver for the Fast Ethernet PCI network cards based on + the RTL 8129/8130/8139 chips. If you have one of those, say Y here. + + To compile this driver as a module, choose M here: the module + will be called 8139too. This is recommended. + +config 8139TOO_PIO + bool "Use PIO instead of MMIO" + default y + depends on 8139TOO && !NO_IOPORT_MAP + help + This instructs the driver to use programmed I/O ports (PIO) instead + of PCI shared memory (MMIO). This can possibly solve some problems + in case your mainboard has memory consistency issues. If unsure, + say N. + +config 8139TOO_TUNE_TWISTER + bool "Support for uncommon RTL-8139 rev. K (automatic channel equalization)" + depends on 8139TOO + help + This implements a function which might come in handy in case you + are using low quality on long cabling. It is required for RealTek + RTL-8139 revision K boards, and totally unused otherwise. It tries + to match the transceiver to the cable characteristics. This is + experimental since hardly documented by the manufacturer. + If unsure, say Y. + +config 8139TOO_8129 + bool "Support for older RTL-8129/8130 boards" + depends on 8139TOO + help + This enables support for the older and uncommon RTL-8129 and + RTL-8130 chips, which support MII via an external transceiver, + instead of an internal one. Disabling this option will save some + memory by making the code size smaller. If unsure, say Y. + +config 8139_OLD_RX_RESET + bool "Use older RX-reset method" + depends on 8139TOO + help + The 8139too driver was recently updated to contain a more rapid + reset sequence, in the face of severe receive errors. This "new" + RX-reset method should be adequate for all boards. But if you + experience problems, you can enable this option to restore the + old RX-reset behavior. If unsure, say N. + +config R8169 + tristate "Realtek 8169/8168/8101/8125 ethernet support" + depends on PCI + select FW_LOADER + select CRC32 + select PHYLIB + select REALTEK_PHY + help + Say Y here if you have a Realtek Ethernet adapter belonging to + the following families: + RTL8169 Gigabit Ethernet + RTL8168 Gigabit Ethernet + RTL8101 Fast Ethernet + RTL8125 2.5GBit Ethernet + + To compile this driver as a module, choose M here: the module + will be called r8169. This is recommended. + +config R8169_LEDS + bool "Support for controlling the NIC LEDs" + depends on R8169 && LEDS_TRIGGER_NETDEV + depends on !(R8169=y && LEDS_CLASS=m) + help + Optional support for controlling the NIC LED's with the netdev + LED trigger. + +config RTASE + tristate "Realtek Automotive Switch 9054/9068/9072/9075/9068/9071 PCIe Interface support" + depends on PCI + select CRC32 + select PAGE_POOL + help + Say Y here and it will be compiled and linked with the kernel + if you have a Realtek Ethernet adapter belonging to the + following families: + RTL9054 5GBit Ethernet + RTL9068 5GBit Ethernet + RTL9072 5GBit Ethernet + RTL9075 5GBit Ethernet + RTL9068 5GBit Ethernet + RTL9071 5GBit Ethernet + + To compile this driver as a module, choose M here: the module + will be called rtase. This is recommended. + +endif # NET_VENDOR_REALTEK diff --git a/drivers/net/ethernet/realtek/Makefile b/drivers/net/ethernet/realtek/Makefile new file mode 100644 index 000000000..12a9c399f --- /dev/null +++ b/drivers/net/ethernet/realtek/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the Realtek network device drivers. +# + +obj-$(CONFIG_8139CP) += 8139cp.o +obj-$(CONFIG_8139TOO) += 8139too.o +r8169-y += r8169_main.o r8169_firmware.o r8169_phy_config.o +r8169-$(CONFIG_R8169_LEDS) += r8169_leds.o +obj-$(CONFIG_R8169) += r8169.o +obj-$(CONFIG_RTASE) += rtase/ diff --git a/drivers/net/ethernet/realtek/r8169.h b/drivers/net/ethernet/realtek/r8169.h new file mode 100644 index 000000000..0b9c1d4eb --- /dev/null +++ b/drivers/net/ethernet/realtek/r8169.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* r8169.h: RealTek 8169/8168/8101 ethernet driver. + * + * Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw> + * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com> + * Copyright (c) a lot of people too. Please respect their work. + * + * See MAINTAINERS file for support contact information. + */ + +#include <linux/netdevice.h> +#include <linux/types.h> +#include <linux/phy.h> + +enum mac_version { + /* support for ancient RTL_GIGA_MAC_VER_01 has been removed */ + RTL_GIGA_MAC_VER_02, + RTL_GIGA_MAC_VER_03, + RTL_GIGA_MAC_VER_04, + RTL_GIGA_MAC_VER_05, + RTL_GIGA_MAC_VER_06, + RTL_GIGA_MAC_VER_07, + RTL_GIGA_MAC_VER_08, + RTL_GIGA_MAC_VER_09, + RTL_GIGA_MAC_VER_10, + /* support for RTL_GIGA_MAC_VER_11 has been removed */ + /* RTL_GIGA_MAC_VER_12 was handled the same as VER_17 */ + /* RTL_GIGA_MAC_VER_13 was merged with VER_10 */ + RTL_GIGA_MAC_VER_14, + /* RTL_GIGA_MAC_VER_16 was merged with VER_10 */ + RTL_GIGA_MAC_VER_17, + RTL_GIGA_MAC_VER_18, + RTL_GIGA_MAC_VER_19, + RTL_GIGA_MAC_VER_20, + RTL_GIGA_MAC_VER_21, + RTL_GIGA_MAC_VER_22, + RTL_GIGA_MAC_VER_23, + RTL_GIGA_MAC_VER_24, + RTL_GIGA_MAC_VER_25, + RTL_GIGA_MAC_VER_26, + /* support for RTL_GIGA_MAC_VER_27 has been removed */ + RTL_GIGA_MAC_VER_28, + RTL_GIGA_MAC_VER_29, + RTL_GIGA_MAC_VER_30, + RTL_GIGA_MAC_VER_31, + RTL_GIGA_MAC_VER_32, + RTL_GIGA_MAC_VER_33, + RTL_GIGA_MAC_VER_34, + RTL_GIGA_MAC_VER_35, + RTL_GIGA_MAC_VER_36, + RTL_GIGA_MAC_VER_37, + RTL_GIGA_MAC_VER_38, + RTL_GIGA_MAC_VER_39, + RTL_GIGA_MAC_VER_40, + /* support for RTL_GIGA_MAC_VER_41 has been removed */ + RTL_GIGA_MAC_VER_42, + RTL_GIGA_MAC_VER_43, + RTL_GIGA_MAC_VER_44, + /* support for RTL_GIGA_MAC_VER_45 has been removed */ + RTL_GIGA_MAC_VER_46, + /* support for RTL_GIGA_MAC_VER_47 has been removed */ + RTL_GIGA_MAC_VER_48, + /* support for RTL_GIGA_MAC_VER_49 has been removed */ + /* support for RTL_GIGA_MAC_VER_50 has been removed */ + RTL_GIGA_MAC_VER_51, + RTL_GIGA_MAC_VER_52, + /* support for RTL_GIGA_MAC_VER_60 has been removed */ + RTL_GIGA_MAC_VER_61, + RTL_GIGA_MAC_VER_63, + RTL_GIGA_MAC_VER_64, + RTL_GIGA_MAC_VER_65, + RTL_GIGA_MAC_VER_66, + RTL_GIGA_MAC_VER_70, + RTL_GIGA_MAC_VER_80, + RTL_GIGA_MAC_NONE, + RTL_GIGA_MAC_VER_LAST = RTL_GIGA_MAC_NONE - 1, + RTL_GIGA_MAC_VER_EXTENDED +}; + +struct rtl8169_private; +struct r8169_led_classdev; + +void r8169_apply_firmware(struct rtl8169_private *tp); +u16 rtl8168h_2_get_adc_bias_ioffset(struct rtl8169_private *tp); +u8 rtl8168d_efuse_read(struct rtl8169_private *tp, int reg_addr); +void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev, + enum mac_version ver); + +void r8169_get_led_name(struct rtl8169_private *tp, int idx, + char *buf, int buf_len); +int rtl8168_get_led_mode(struct rtl8169_private *tp); +int rtl8168_led_mod_ctrl(struct rtl8169_private *tp, u16 mask, u16 val); +struct r8169_led_classdev *rtl8168_init_leds(struct net_device *ndev); +int rtl8125_get_led_mode(struct rtl8169_private *tp, int index); +int rtl8125_set_led_mode(struct rtl8169_private *tp, int index, u16 mode); +struct r8169_led_classdev *rtl8125_init_leds(struct net_device *ndev); +void r8169_remove_leds(struct r8169_led_classdev *leds); diff --git a/drivers/net/ethernet/realtek/r8169_firmware.c b/drivers/net/ethernet/realtek/r8169_firmware.c new file mode 100644 index 000000000..6dff3d947 --- /dev/null +++ b/drivers/net/ethernet/realtek/r8169_firmware.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* r8169_firmware.c: RealTek 8169/8168/8101 ethernet driver. + * + * Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw> + * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com> + * Copyright (c) a lot of people too. Please respect their work. + * + * See MAINTAINERS file for support contact information. + */ + +#include <linux/delay.h> +#include <linux/firmware.h> + +#include "r8169_firmware.h" + +enum rtl_fw_opcode { + PHY_READ = 0x0, + PHY_DATA_OR = 0x1, + PHY_DATA_AND = 0x2, + PHY_BJMPN = 0x3, + PHY_MDIO_CHG = 0x4, + PHY_CLEAR_READCOUNT = 0x7, + PHY_WRITE = 0x8, + PHY_READCOUNT_EQ_SKIP = 0x9, + PHY_COMP_EQ_SKIPN = 0xa, + PHY_COMP_NEQ_SKIPN = 0xb, + PHY_WRITE_PREVIOUS = 0xc, + PHY_SKIPN = 0xd, + PHY_DELAY_MS = 0xe, +}; + +struct fw_info { + u32 magic; + char version[RTL_VER_SIZE]; + __le32 fw_start; + __le32 fw_len; + u8 chksum; +} __packed; + +#define FW_OPCODE_SIZE sizeof_field(struct rtl_fw_phy_action, code[0]) + +static bool rtl_fw_format_ok(struct rtl_fw *rtl_fw) +{ + const struct firmware *fw = rtl_fw->fw; + struct fw_info *fw_info = (struct fw_info *)fw->data; + struct rtl_fw_phy_action *pa = &rtl_fw->phy_action; + + if (fw->size < FW_OPCODE_SIZE) + return false; + + if (!fw_info->magic) { + size_t i, size, start; + u8 checksum = 0; + + if (fw->size < sizeof(*fw_info)) + return false; + + for (i = 0; i < fw->size; i++) + checksum += fw->data[i]; + if (checksum != 0) + return false; + + start = le32_to_cpu(fw_info->fw_start); + if (start > fw->size) + return false; + + size = le32_to_cpu(fw_info->fw_len); + if (size > (fw->size - start) / FW_OPCODE_SIZE) + return false; + + strscpy(rtl_fw->version, fw_info->version); + + pa->code = (__le32 *)(fw->data + start); + pa->size = size; + } else { + if (fw->size % FW_OPCODE_SIZE) + return false; + + strscpy(rtl_fw->version, rtl_fw->fw_name, RTL_VER_SIZE); + + pa->code = (__le32 *)fw->data; + pa->size = fw->size / FW_OPCODE_SIZE; + } + + return true; +} + +static bool rtl_fw_data_ok(struct rtl_fw *rtl_fw) +{ + struct rtl_fw_phy_action *pa = &rtl_fw->phy_action; + size_t index; + + for (index = 0; index < pa->size; index++) { + u32 action = le32_to_cpu(pa->code[index]); + u32 val = action & 0x0000ffff; + u32 regno = (action & 0x0fff0000) >> 16; + + switch (action >> 28) { + case PHY_READ: + case PHY_DATA_OR: + case PHY_DATA_AND: + case PHY_CLEAR_READCOUNT: + case PHY_WRITE: + case PHY_WRITE_PREVIOUS: + case PHY_DELAY_MS: + break; + + case PHY_MDIO_CHG: + if (val > 1) + goto out; + break; + + case PHY_BJMPN: + if (regno > index) + goto out; + break; + case PHY_READCOUNT_EQ_SKIP: + if (index + 2 >= pa->size) + goto out; + break; + case PHY_COMP_EQ_SKIPN: + case PHY_COMP_NEQ_SKIPN: + case PHY_SKIPN: + if (index + 1 + regno >= pa->size) + goto out; + break; + + default: + dev_err(rtl_fw->dev, "Invalid action 0x%08x\n", action); + return false; + } + } + + return true; +out: + dev_err(rtl_fw->dev, "Out of range of firmware\n"); + return false; +} + +void rtl_fw_write_firmware(struct rtl8169_private *tp, struct rtl_fw *rtl_fw) +{ + struct rtl_fw_phy_action *pa = &rtl_fw->phy_action; + rtl_fw_write_t fw_write = rtl_fw->phy_write; + rtl_fw_read_t fw_read = rtl_fw->phy_read; + int predata = 0, count = 0; + size_t index; + + for (index = 0; index < pa->size; index++) { + u32 action = le32_to_cpu(pa->code[index]); + u32 data = action & 0x0000ffff; + u32 regno = (action & 0x0fff0000) >> 16; + enum rtl_fw_opcode opcode = action >> 28; + + switch (opcode) { + case PHY_READ: + predata = fw_read(tp, regno); + count++; + break; + case PHY_DATA_OR: + predata |= data; + break; + case PHY_DATA_AND: + predata &= data; + break; + case PHY_BJMPN: + index -= (regno + 1); + break; + case PHY_MDIO_CHG: + if (data) { + fw_write = rtl_fw->mac_mcu_write; + fw_read = rtl_fw->mac_mcu_read; + } else { + fw_write = rtl_fw->phy_write; + fw_read = rtl_fw->phy_read; + } + + break; + case PHY_CLEAR_READCOUNT: + count = 0; + break; + case PHY_WRITE: + fw_write(tp, regno, data); + break; + case PHY_READCOUNT_EQ_SKIP: + if (count == data) + index++; + break; + case PHY_COMP_EQ_SKIPN: + if (predata == data) + index += regno; + break; + case PHY_COMP_NEQ_SKIPN: + if (predata != data) + index += regno; + break; + case PHY_WRITE_PREVIOUS: + fw_write(tp, regno, predata); + break; + case PHY_SKIPN: + index += regno; + break; + case PHY_DELAY_MS: + msleep(data); + break; + } + } +} + +void rtl_fw_release_firmware(struct rtl_fw *rtl_fw) +{ + release_firmware(rtl_fw->fw); +} + +int rtl_fw_request_firmware(struct rtl_fw *rtl_fw) +{ + int rc; + + rc = firmware_request_nowarn(&rtl_fw->fw, rtl_fw->fw_name, rtl_fw->dev); + if (rc < 0) + goto out; + + if (!rtl_fw_format_ok(rtl_fw) || !rtl_fw_data_ok(rtl_fw)) { + release_firmware(rtl_fw->fw); + rc = -EINVAL; + goto out; + } + + return 0; +out: + dev_warn(rtl_fw->dev, "Unable to load firmware %s (%d)\n", + rtl_fw->fw_name, rc); + return rc; +} diff --git a/drivers/net/ethernet/realtek/r8169_firmware.h b/drivers/net/ethernet/realtek/r8169_firmware.h new file mode 100644 index 000000000..7dc348ed8 --- /dev/null +++ b/drivers/net/ethernet/realtek/r8169_firmware.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* r8169_firmware.h: RealTek 8169/8168/8101 ethernet driver. + * + * Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw> + * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com> + * Copyright (c) a lot of people too. Please respect their work. + * + * See MAINTAINERS file for support contact information. + */ + +#include <linux/device.h> +#include <linux/firmware.h> + +struct rtl8169_private; +typedef void (*rtl_fw_write_t)(struct rtl8169_private *tp, int reg, int val); +typedef int (*rtl_fw_read_t)(struct rtl8169_private *tp, int reg); + +#define RTL_VER_SIZE 32 + +struct rtl_fw { + rtl_fw_write_t phy_write; + rtl_fw_read_t phy_read; + rtl_fw_write_t mac_mcu_write; + rtl_fw_read_t mac_mcu_read; + const struct firmware *fw; + const char *fw_name; + struct device *dev; + + char version[RTL_VER_SIZE]; + + struct rtl_fw_phy_action { + __le32 *code; + size_t size; + } phy_action; +}; + +int rtl_fw_request_firmware(struct rtl_fw *rtl_fw); +void rtl_fw_release_firmware(struct rtl_fw *rtl_fw); +void rtl_fw_write_firmware(struct rtl8169_private *tp, struct rtl_fw *rtl_fw); diff --git a/drivers/net/ethernet/realtek/r8169_leds.c b/drivers/net/ethernet/realtek/r8169_leds.c new file mode 100644 index 000000000..1999e81f0 --- /dev/null +++ b/drivers/net/ethernet/realtek/r8169_leds.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* r8169_leds.c: Realtek 8169/8168/8101/8125 ethernet driver. + * + * Copyright (c) 2023 Heiner Kallweit <hkallweit1@gmail.com> + * + * See MAINTAINERS file for support contact information. + */ + +#include <linux/leds.h> +#include <linux/netdevice.h> +#include <uapi/linux/uleds.h> + +#include "r8169.h" + +#define RTL8168_LED_CTRL_OPTION2 BIT(15) +#define RTL8168_LED_CTRL_ACT BIT(3) +#define RTL8168_LED_CTRL_LINK_1000 BIT(2) +#define RTL8168_LED_CTRL_LINK_100 BIT(1) +#define RTL8168_LED_CTRL_LINK_10 BIT(0) + +#define RTL8125_LED_CTRL_ACT BIT(9) +#define RTL8125_LED_CTRL_LINK_2500 BIT(5) +#define RTL8125_LED_CTRL_LINK_1000 BIT(3) +#define RTL8125_LED_CTRL_LINK_100 BIT(1) +#define RTL8125_LED_CTRL_LINK_10 BIT(0) + +#define RTL8168_NUM_LEDS 3 +#define RTL8125_NUM_LEDS 4 + +struct r8169_led_classdev { + struct led_classdev led; + struct net_device *ndev; + int index; +}; + +#define lcdev_to_r8169_ldev(lcdev) container_of(lcdev, struct r8169_led_classdev, led) + +static bool r8169_trigger_mode_is_valid(unsigned long flags) +{ + bool rx, tx; + + if (flags & BIT(TRIGGER_NETDEV_HALF_DUPLEX)) + return false; + if (flags & BIT(TRIGGER_NETDEV_FULL_DUPLEX)) + return false; + + rx = flags & BIT(TRIGGER_NETDEV_RX); + tx = flags & BIT(TRIGGER_NETDEV_TX); + + return rx == tx; +} + +static int rtl8168_led_hw_control_is_supported(struct led_classdev *led_cdev, + unsigned long flags) +{ + struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev); + struct rtl8169_private *tp = netdev_priv(ldev->ndev); + int shift = ldev->index * 4; + + if (!r8169_trigger_mode_is_valid(flags)) { + /* Switch LED off to indicate that mode isn't supported */ + rtl8168_led_mod_ctrl(tp, 0x000f << shift, 0); + return -EOPNOTSUPP; + } + + return 0; +} + +static int rtl8168_led_hw_control_set(struct led_classdev *led_cdev, + unsigned long flags) +{ + struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev); + struct rtl8169_private *tp = netdev_priv(ldev->ndev); + int shift = ldev->index * 4; + u16 mode = 0; + + if (flags & BIT(TRIGGER_NETDEV_LINK_10)) + mode |= RTL8168_LED_CTRL_LINK_10; + if (flags & BIT(TRIGGER_NETDEV_LINK_100)) + mode |= RTL8168_LED_CTRL_LINK_100; + if (flags & BIT(TRIGGER_NETDEV_LINK_1000)) + mode |= RTL8168_LED_CTRL_LINK_1000; + if (flags & BIT(TRIGGER_NETDEV_TX)) + mode |= RTL8168_LED_CTRL_ACT; + + return rtl8168_led_mod_ctrl(tp, 0x000f << shift, mode << shift); +} + +static int rtl8168_led_hw_control_get(struct led_classdev *led_cdev, + unsigned long *flags) +{ + struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev); + struct rtl8169_private *tp = netdev_priv(ldev->ndev); + int shift = ldev->index * 4; + int mode; + + mode = rtl8168_get_led_mode(tp); + if (mode < 0) + return mode; + + if (mode & RTL8168_LED_CTRL_OPTION2) { + rtl8168_led_mod_ctrl(tp, RTL8168_LED_CTRL_OPTION2, 0); + netdev_notice(ldev->ndev, "Deactivating unsupported Option2 LED mode\n"); + } + + mode = (mode >> shift) & 0x000f; + + if (mode & RTL8168_LED_CTRL_ACT) + *flags |= BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX); + + if (mode & RTL8168_LED_CTRL_LINK_10) + *flags |= BIT(TRIGGER_NETDEV_LINK_10); + if (mode & RTL8168_LED_CTRL_LINK_100) + *flags |= BIT(TRIGGER_NETDEV_LINK_100); + if (mode & RTL8168_LED_CTRL_LINK_1000) + *flags |= BIT(TRIGGER_NETDEV_LINK_1000); + + return 0; +} + +static struct device * + r8169_led_hw_control_get_device(struct led_classdev *led_cdev) +{ + struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev); + + return &ldev->ndev->dev; +} + +static void rtl8168_setup_ldev(struct r8169_led_classdev *ldev, + struct net_device *ndev, int index) +{ + struct rtl8169_private *tp = netdev_priv(ndev); + struct led_classdev *led_cdev = &ldev->led; + char led_name[LED_MAX_NAME_SIZE]; + + ldev->ndev = ndev; + ldev->index = index; + + r8169_get_led_name(tp, index, led_name, LED_MAX_NAME_SIZE); + led_cdev->name = led_name; + led_cdev->hw_control_trigger = "netdev"; + led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN; + led_cdev->hw_control_is_supported = rtl8168_led_hw_control_is_supported; + led_cdev->hw_control_set = rtl8168_led_hw_control_set; + led_cdev->hw_control_get = rtl8168_led_hw_control_get; + led_cdev->hw_control_get_device = r8169_led_hw_control_get_device; + + /* ignore errors */ + led_classdev_register(&ndev->dev, led_cdev); +} + +struct r8169_led_classdev *rtl8168_init_leds(struct net_device *ndev) +{ + struct r8169_led_classdev *leds; + int i; + + leds = kzalloc_objs(*leds, RTL8168_NUM_LEDS + 1); + if (!leds) + return NULL; + + for (i = 0; i < RTL8168_NUM_LEDS; i++) + rtl8168_setup_ldev(leds + i, ndev, i); + + return leds; +} + +static int rtl8125_led_hw_control_is_supported(struct led_classdev *led_cdev, + unsigned long flags) +{ + struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev); + struct rtl8169_private *tp = netdev_priv(ldev->ndev); + + if (!r8169_trigger_mode_is_valid(flags)) { + /* Switch LED off to indicate that mode isn't supported */ + rtl8125_set_led_mode(tp, ldev->index, 0); + return -EOPNOTSUPP; + } + + return 0; +} + +static int rtl8125_led_hw_control_set(struct led_classdev *led_cdev, + unsigned long flags) +{ + struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev); + struct rtl8169_private *tp = netdev_priv(ldev->ndev); + u16 mode = 0; + + if (flags & BIT(TRIGGER_NETDEV_LINK_10)) + mode |= RTL8125_LED_CTRL_LINK_10; + if (flags & BIT(TRIGGER_NETDEV_LINK_100)) + mode |= RTL8125_LED_CTRL_LINK_100; + if (flags & BIT(TRIGGER_NETDEV_LINK_1000)) + mode |= RTL8125_LED_CTRL_LINK_1000; + if (flags & BIT(TRIGGER_NETDEV_LINK_2500)) + mode |= RTL8125_LED_CTRL_LINK_2500; + if (flags & (BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX))) + mode |= RTL8125_LED_CTRL_ACT; + + return rtl8125_set_led_mode(tp, ldev->index, mode); +} + +static int rtl8125_led_hw_control_get(struct led_classdev *led_cdev, + unsigned long *flags) +{ + struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev); + struct rtl8169_private *tp = netdev_priv(ldev->ndev); + int mode; + + mode = rtl8125_get_led_mode(tp, ldev->index); + if (mode < 0) + return mode; + + if (mode & RTL8125_LED_CTRL_LINK_10) + *flags |= BIT(TRIGGER_NETDEV_LINK_10); + if (mode & RTL8125_LED_CTRL_LINK_100) + *flags |= BIT(TRIGGER_NETDEV_LINK_100); + if (mode & RTL8125_LED_CTRL_LINK_1000) + *flags |= BIT(TRIGGER_NETDEV_LINK_1000); + if (mode & RTL8125_LED_CTRL_LINK_2500) + *flags |= BIT(TRIGGER_NETDEV_LINK_2500); + if (mode & RTL8125_LED_CTRL_ACT) + *flags |= BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX); + + return 0; +} + +static void rtl8125_setup_led_ldev(struct r8169_led_classdev *ldev, + struct net_device *ndev, int index) +{ + struct rtl8169_private *tp = netdev_priv(ndev); + struct led_classdev *led_cdev = &ldev->led; + char led_name[LED_MAX_NAME_SIZE]; + + ldev->ndev = ndev; + ldev->index = index; + + r8169_get_led_name(tp, index, led_name, LED_MAX_NAME_SIZE); + led_cdev->name = led_name; + led_cdev->hw_control_trigger = "netdev"; + led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN; + led_cdev->hw_control_is_supported = rtl8125_led_hw_control_is_supported; + led_cdev->hw_control_set = rtl8125_led_hw_control_set; + led_cdev->hw_control_get = rtl8125_led_hw_control_get; + led_cdev->hw_control_get_device = r8169_led_hw_control_get_device; + + /* ignore errors */ + led_classdev_register(&ndev->dev, led_cdev); +} + +struct r8169_led_classdev *rtl8125_init_leds(struct net_device *ndev) +{ + struct r8169_led_classdev *leds; + int i; + + leds = kzalloc_objs(*leds, RTL8125_NUM_LEDS + 1); + if (!leds) + return NULL; + + for (i = 0; i < RTL8125_NUM_LEDS; i++) + rtl8125_setup_led_ldev(leds + i, ndev, i); + + return leds; +} + +void r8169_remove_leds(struct r8169_led_classdev *leds) +{ + if (!leds) + return; + + for (struct r8169_led_classdev *l = leds; l->ndev; l++) + led_classdev_unregister(&l->led); + + kfree(leds); +} diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c new file mode 100644 index 000000000..ec4fc21fa --- /dev/null +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -0,0 +1,5835 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * r8169.c: RealTek 8169/8168/8101 ethernet driver. + * + * Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw> + * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com> + * Copyright (c) a lot of people too. Please respect their work. + * + * See MAINTAINERS file for support contact information. + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/ethtool.h> +#include <linux/phy.h> +#include <linux/if_vlan.h> +#include <linux/in.h> +#include <linux/io.h> +#include <linux/ip.h> +#include <linux/tcp.h> +#include <linux/interrupt.h> +#include <linux/dma-mapping.h> +#include <linux/pm_runtime.h> +#include <linux/bitfield.h> +#include <linux/prefetch.h> +#include <linux/ipv6.h> +#include <linux/unaligned.h> +#include <net/ip6_checksum.h> +#include <net/netdev_queues.h> +#include <net/phy/realtek_phy.h> + +#include "r8169.h" +#include "r8169_firmware.h" + +#define FIRMWARE_8168D_1 "rtl_nic/rtl8168d-1.fw" +#define FIRMWARE_8168D_2 "rtl_nic/rtl8168d-2.fw" +#define FIRMWARE_8168E_1 "rtl_nic/rtl8168e-1.fw" +#define FIRMWARE_8168E_2 "rtl_nic/rtl8168e-2.fw" +#define FIRMWARE_8168E_3 "rtl_nic/rtl8168e-3.fw" +#define FIRMWARE_8168F_1 "rtl_nic/rtl8168f-1.fw" +#define FIRMWARE_8168F_2 "rtl_nic/rtl8168f-2.fw" +#define FIRMWARE_8105E_1 "rtl_nic/rtl8105e-1.fw" +#define FIRMWARE_8402_1 "rtl_nic/rtl8402-1.fw" +#define FIRMWARE_8411_1 "rtl_nic/rtl8411-1.fw" +#define FIRMWARE_8411_2 "rtl_nic/rtl8411-2.fw" +#define FIRMWARE_8106E_1 "rtl_nic/rtl8106e-1.fw" +#define FIRMWARE_8106E_2 "rtl_nic/rtl8106e-2.fw" +#define FIRMWARE_8168G_2 "rtl_nic/rtl8168g-2.fw" +#define FIRMWARE_8168G_3 "rtl_nic/rtl8168g-3.fw" +#define FIRMWARE_8168H_2 "rtl_nic/rtl8168h-2.fw" +#define FIRMWARE_8168FP_3 "rtl_nic/rtl8168fp-3.fw" +#define FIRMWARE_8107E_2 "rtl_nic/rtl8107e-2.fw" +#define FIRMWARE_8125A_3 "rtl_nic/rtl8125a-3.fw" +#define FIRMWARE_8125B_2 "rtl_nic/rtl8125b-2.fw" +#define FIRMWARE_8125D_1 "rtl_nic/rtl8125d-1.fw" +#define FIRMWARE_8125D_2 "rtl_nic/rtl8125d-2.fw" +#define FIRMWARE_8125K_1 "rtl_nic/rtl8125k-1.fw" +#define FIRMWARE_8125BP_2 "rtl_nic/rtl8125bp-2.fw" +#define FIRMWARE_8125CP_1 "rtl_nic/rtl8125cp-1.fw" +#define FIRMWARE_9151A_1 "rtl_nic/rtl9151a-1.fw" +#define FIRMWARE_8126A_2 "rtl_nic/rtl8126a-2.fw" +#define FIRMWARE_8126A_3 "rtl_nic/rtl8126a-3.fw" +#define FIRMWARE_8127A_1 "rtl_nic/rtl8127a-1.fw" + +#define TX_DMA_BURST 7 /* Maximum PCI burst, '7' is unlimited */ +#define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */ + +#define R8169_REGS_SIZE 256 +#define R8169_RX_BUF_SIZE (SZ_16K - 1) +#define NUM_TX_DESC 256 /* Number of Tx descriptor registers */ +#define NUM_RX_DESC 256 /* Number of Rx descriptor registers */ +#define R8169_TX_RING_BYTES (NUM_TX_DESC * sizeof(struct TxDesc)) +#define R8169_RX_RING_BYTES (NUM_RX_DESC * sizeof(struct RxDesc)) +#define R8169_TX_STOP_THRS (MAX_SKB_FRAGS + 1) +#define R8169_TX_START_THRS (2 * R8169_TX_STOP_THRS) + +#define OCP_STD_PHY_BASE 0xa400 + +#define RTL_CFG_NO_GBIT 1 + +/* write/read MMIO register */ +#define RTL_W8(tp, reg, val8) writeb((val8), tp->mmio_addr + (reg)) +#define RTL_W16(tp, reg, val16) writew((val16), tp->mmio_addr + (reg)) +#define RTL_W32(tp, reg, val32) writel((val32), tp->mmio_addr + (reg)) +#define RTL_R8(tp, reg) readb(tp->mmio_addr + (reg)) +#define RTL_R16(tp, reg) readw(tp->mmio_addr + (reg)) +#define RTL_R32(tp, reg) readl(tp->mmio_addr + (reg)) + +#define JUMBO_4K (4 * SZ_1K - VLAN_ETH_HLEN - ETH_FCS_LEN) +#define JUMBO_6K (6 * SZ_1K - VLAN_ETH_HLEN - ETH_FCS_LEN) +#define JUMBO_7K (7 * SZ_1K - VLAN_ETH_HLEN - ETH_FCS_LEN) +#define JUMBO_9K (9 * SZ_1K - VLAN_ETH_HLEN - ETH_FCS_LEN) +#define JUMBO_16K (SZ_16K - VLAN_ETH_HLEN - ETH_FCS_LEN) + +static const struct rtl_chip_info { + u32 mask; + u32 val; + enum mac_version mac_version; + const char *name; + const char *fw_name; +} rtl_chip_infos[] = { + /* 8127A family. */ + { 0x7cf, 0x6c9, RTL_GIGA_MAC_VER_80, "RTL8127A", FIRMWARE_8127A_1 }, + + /* 8126A family. */ + { 0x7cf, 0x64a, RTL_GIGA_MAC_VER_70, "RTL8126A", FIRMWARE_8126A_3 }, + { 0x7cf, 0x649, RTL_GIGA_MAC_VER_70, "RTL8126A", FIRMWARE_8126A_2 }, + + /* 8125BP family. */ + { 0x7cf, 0x681, RTL_GIGA_MAC_VER_66, "RTL8125BP", FIRMWARE_8125BP_2 }, + + /* 8125CP family*/ + { 0x7cf, 0x708, RTL_GIGA_MAC_VER_65, "RTL8125CP", FIRMWARE_8125CP_1 }, + + /* 8125D family. */ + { 0x7cf, 0x68b, RTL_GIGA_MAC_VER_64, "RTL9151A", FIRMWARE_9151A_1 }, + { 0x7cf, 0x68a, RTL_GIGA_MAC_VER_64, "RTL8125K", FIRMWARE_8125K_1 }, + { 0x7cf, 0x689, RTL_GIGA_MAC_VER_64, "RTL8125D", FIRMWARE_8125D_2 }, + { 0x7cf, 0x688, RTL_GIGA_MAC_VER_64, "RTL8125D", FIRMWARE_8125D_1 }, + + /* 8125B family. */ + { 0x7cf, 0x641, RTL_GIGA_MAC_VER_63, "RTL8125B", FIRMWARE_8125B_2 }, + + /* 8125A family. */ + { 0x7cf, 0x609, RTL_GIGA_MAC_VER_61, "RTL8125A", FIRMWARE_8125A_3 }, + + /* RTL8117 */ + { 0x7cf, 0x54b, RTL_GIGA_MAC_VER_52, "RTL8168fp/RTL8117" }, + { 0x7cf, 0x54a, RTL_GIGA_MAC_VER_52, "RTL8168fp/RTL8117", + FIRMWARE_8168FP_3 }, + + /* 8168EP family. */ + { 0x7cf, 0x502, RTL_GIGA_MAC_VER_51, "RTL8168ep/8111ep" }, + + /* 8168H family. */ + { 0x7cf, 0x541, RTL_GIGA_MAC_VER_46, "RTL8168h/8111h", + FIRMWARE_8168H_2 }, + /* Realtek calls it RTL8168M, but it's handled like RTL8168H */ + { 0x7cf, 0x6c0, RTL_GIGA_MAC_VER_46, "RTL8168M", FIRMWARE_8168H_2 }, + + /* 8168G family. */ + { 0x7cf, 0x5c8, RTL_GIGA_MAC_VER_44, "RTL8411b", FIRMWARE_8411_2 }, + { 0x7cf, 0x509, RTL_GIGA_MAC_VER_42, "RTL8168gu/8111gu", + FIRMWARE_8168G_3 }, + { 0x7cf, 0x4c0, RTL_GIGA_MAC_VER_40, "RTL8168g/8111g", + FIRMWARE_8168G_2 }, + + /* 8168F family. */ + { 0x7c8, 0x488, RTL_GIGA_MAC_VER_38, "RTL8411", FIRMWARE_8411_1 }, + { 0x7cf, 0x481, RTL_GIGA_MAC_VER_36, "RTL8168f/8111f", + FIRMWARE_8168F_2 }, + { 0x7cf, 0x480, RTL_GIGA_MAC_VER_35, "RTL8168f/8111f", + FIRMWARE_8168F_1 }, + + /* 8168E family. */ + { 0x7c8, 0x2c8, RTL_GIGA_MAC_VER_34, "RTL8168evl/8111evl", + FIRMWARE_8168E_3 }, + { 0x7cf, 0x2c1, RTL_GIGA_MAC_VER_32, "RTL8168e/8111e", + FIRMWARE_8168E_1 }, + { 0x7c8, 0x2c0, RTL_GIGA_MAC_VER_33, "RTL8168e/8111e", + FIRMWARE_8168E_2 }, + + /* 8168D family. */ + { 0x7cf, 0x281, RTL_GIGA_MAC_VER_25, "RTL8168d/8111d", + FIRMWARE_8168D_1 }, + { 0x7c8, 0x280, RTL_GIGA_MAC_VER_26, "RTL8168d/8111d", + FIRMWARE_8168D_2 }, + + /* 8168DP family. */ + { 0x7cf, 0x28a, RTL_GIGA_MAC_VER_28, "RTL8168dp/8111dp" }, + { 0x7cf, 0x28b, RTL_GIGA_MAC_VER_31, "RTL8168dp/8111dp" }, + + /* 8168C family. */ + { 0x7cf, 0x3c9, RTL_GIGA_MAC_VER_23, "RTL8168cp/8111cp" }, + { 0x7cf, 0x3c8, RTL_GIGA_MAC_VER_18, "RTL8168cp/8111cp" }, + { 0x7c8, 0x3c8, RTL_GIGA_MAC_VER_24, "RTL8168cp/8111cp" }, + { 0x7cf, 0x3c0, RTL_GIGA_MAC_VER_19, "RTL8168c/8111c" }, + { 0x7cf, 0x3c2, RTL_GIGA_MAC_VER_20, "RTL8168c/8111c" }, + { 0x7cf, 0x3c3, RTL_GIGA_MAC_VER_21, "RTL8168c/8111c" }, + { 0x7c8, 0x3c0, RTL_GIGA_MAC_VER_22, "RTL8168c/8111c" }, + + /* 8168B family. */ + { 0x7c8, 0x380, RTL_GIGA_MAC_VER_17, "RTL8168b/8111b" }, + /* This one is very old and rare, support has been removed. + * { 0x7c8, 0x300, RTL_GIGA_MAC_VER_11, "RTL8168b/8111b" }, + */ + + /* 8101 family. */ + { 0x7c8, 0x448, RTL_GIGA_MAC_VER_39, "RTL8106e", FIRMWARE_8106E_1 }, + { 0x7c8, 0x440, RTL_GIGA_MAC_VER_37, "RTL8402", FIRMWARE_8402_1 }, + { 0x7cf, 0x409, RTL_GIGA_MAC_VER_29, "RTL8105e", FIRMWARE_8105E_1 }, + { 0x7c8, 0x408, RTL_GIGA_MAC_VER_30, "RTL8105e", FIRMWARE_8105E_1 }, + { 0x7cf, 0x349, RTL_GIGA_MAC_VER_08, "RTL8102e" }, + { 0x7cf, 0x249, RTL_GIGA_MAC_VER_08, "RTL8102e" }, + { 0x7cf, 0x348, RTL_GIGA_MAC_VER_07, "RTL8102e" }, + { 0x7cf, 0x248, RTL_GIGA_MAC_VER_07, "RTL8102e" }, + { 0x7cf, 0x240, RTL_GIGA_MAC_VER_14, "RTL8401" }, + { 0x7c8, 0x348, RTL_GIGA_MAC_VER_09, "RTL8102e/RTL8103e" }, + { 0x7c8, 0x248, RTL_GIGA_MAC_VER_09, "RTL8102e/RTL8103e" }, + { 0x7c8, 0x340, RTL_GIGA_MAC_VER_10, "RTL8101e/RTL8100e" }, + + /* 8110 family. */ + { 0xfc8, 0x980, RTL_GIGA_MAC_VER_06, "RTL8169sc/8110sc" }, + { 0xfc8, 0x180, RTL_GIGA_MAC_VER_05, "RTL8169sc/8110sc" }, + { 0xfc8, 0x100, RTL_GIGA_MAC_VER_04, "RTL8169sb/8110sb" }, + { 0xfc8, 0x040, RTL_GIGA_MAC_VER_03, "RTL8110s" }, + { 0xfc8, 0x008, RTL_GIGA_MAC_VER_02, "RTL8169s" }, + + /* extended chip version*/ + { 0x7cf, 0x7c8, RTL_GIGA_MAC_VER_EXTENDED }, + + /* Catch-all */ + { 0x000, 0x000, RTL_GIGA_MAC_NONE } +}; + +static const struct rtl_chip_info rtl_chip_infos_extended[] = { + { 0x7fffffff, 0x00000000, RTL_GIGA_MAC_VER_64, "RTL9151AS", + FIRMWARE_9151A_1}, + + /* Catch-all */ + { 0x00000000, 0x00000000, RTL_GIGA_MAC_NONE } +}; + +static const struct pci_device_id rtl8169_pci_tbl[] = { + { PCI_VDEVICE(REALTEK, 0x2502) }, + { PCI_VDEVICE(REALTEK, 0x2600) }, + { PCI_VDEVICE(REALTEK, 0x8129) }, + { PCI_VDEVICE(REALTEK, 0x8136), .driver_data = RTL_CFG_NO_GBIT }, + { PCI_VDEVICE(REALTEK, 0x8161) }, + { PCI_VDEVICE(REALTEK, 0x8162) }, + { PCI_VDEVICE(REALTEK, 0x8167) }, + { PCI_VDEVICE(REALTEK, 0x8168) }, + { PCI_VDEVICE(NCUBE, 0x8168) }, + { PCI_VDEVICE(REALTEK, 0x8169) }, + { PCI_VDEVICE(DLINK, 0x4300) }, + { PCI_VDEVICE(DLINK, 0x4302) }, + { PCI_VDEVICE(AT, 0xc107) }, + { PCI_VDEVICE(USR, 0x0116) }, + { PCI_VDEVICE_SUB(LINKSYS, 0x1032, PCI_ANY_ID, 0x0024) }, + { PCI_DEVICE_SUB(0x0001, 0x8168, PCI_ANY_ID, 0x2410) }, + { PCI_VDEVICE(REALTEK, 0x8125) }, + { PCI_VDEVICE(REALTEK, 0x8126) }, + { PCI_VDEVICE(REALTEK, 0x8127) }, + { PCI_VDEVICE(REALTEK, 0x3000) }, + { PCI_VDEVICE(REALTEK, 0x5000) }, + { PCI_VDEVICE(REALTEK, 0x0e10) }, + { } +}; + +MODULE_DEVICE_TABLE(pci, rtl8169_pci_tbl); + +enum rtl_registers { + MAC0 = 0, /* Ethernet hardware address. */ + MAC4 = 4, + MAR0 = 8, /* Multicast filter. */ + CounterAddrLow = 0x10, + CounterAddrHigh = 0x14, + TxDescStartAddrLow = 0x20, + TxDescStartAddrHigh = 0x24, + TxHDescStartAddrLow = 0x28, + TxHDescStartAddrHigh = 0x2c, + FLASH = 0x30, + ERSR = 0x36, + ChipCmd = 0x37, + TxPoll = 0x38, + IntrMask = 0x3c, + IntrStatus = 0x3e, + + TxConfig = 0x40, + /* Extended chip version id */ + TX_CONFIG_V2 = 0x60b0, +#define TXCFG_AUTO_FIFO (1 << 7) /* 8111e-vl */ +#define TXCFG_EMPTY (1 << 11) /* 8111e-vl */ + + RxConfig = 0x44, +#define RX128_INT_EN (1 << 15) /* 8111c and later */ +#define RX_MULTI_EN (1 << 14) /* 8111c only */ +#define RXCFG_FIFO_SHIFT 13 + /* No threshold before first PCI xfer */ +#define RX_FIFO_THRESH (7 << RXCFG_FIFO_SHIFT) +#define RX_EARLY_OFF (1 << 11) +#define RX_PAUSE_SLOT_ON (1 << 11) /* 8125b and later */ +#define RXCFG_DMA_SHIFT 8 + /* Unlimited maximum PCI burst. */ +#define RX_DMA_BURST (7 << RXCFG_DMA_SHIFT) + + Cfg9346 = 0x50, + Config0 = 0x51, + Config1 = 0x52, + Config2 = 0x53, +#define PME_SIGNAL (1 << 5) /* 8168c and later */ + + Config3 = 0x54, + Config4 = 0x55, + Config5 = 0x56, + PHYAR = 0x60, + PHYstatus = 0x6c, + RxMaxSize = 0xda, + CPlusCmd = 0xe0, + IntrMitigate = 0xe2, + +#define RTL_COALESCE_TX_USECS GENMASK(15, 12) +#define RTL_COALESCE_TX_FRAMES GENMASK(11, 8) +#define RTL_COALESCE_RX_USECS GENMASK(7, 4) +#define RTL_COALESCE_RX_FRAMES GENMASK(3, 0) + +#define RTL_COALESCE_T_MAX 0x0fU +#define RTL_COALESCE_FRAME_MAX (RTL_COALESCE_T_MAX * 4) + + RxDescAddrLow = 0xe4, + RxDescAddrHigh = 0xe8, + EarlyTxThres = 0xec, /* 8169. Unit of 32 bytes. */ + +#define NoEarlyTx 0x3f /* Max value : no early transmit. */ + + MaxTxPacketSize = 0xec, /* 8101/8168. Unit of 128 bytes. */ + +#define TxPacketMax (8064 >> 7) +#define EarlySize 0x27 + + FuncEvent = 0xf0, + FuncEventMask = 0xf4, + FuncPresetState = 0xf8, + IBCR0 = 0xf8, + IBCR2 = 0xf9, + IBIMR0 = 0xfa, + IBISR0 = 0xfb, + FuncForceEvent = 0xfc, + + ALDPS_LTR = 0xe0a2, + LTR_OBFF_LOCK = 0xe032, + LTR_SNOOP = 0xe034, + +#define ALDPS_LTR_EN BIT(0) +#define LTR_OBFF_LOCK_EN BIT(0) +#define LINK_SPEED_CHANGE_EN BIT(14) +#define LTR_SNOOP_EN GENMASK(15, 14) +}; + +enum rtl8168_8101_registers { + CSIDR = 0x64, + CSIAR = 0x68, +#define CSIAR_FLAG 0x80000000 +#define CSIAR_WRITE_CMD 0x80000000 +#define CSIAR_BYTE_ENABLE 0x0000f000 +#define CSIAR_ADDR_MASK 0x00000fff + PMCH = 0x6f, +#define D3COLD_NO_PLL_DOWN BIT(7) +#define D3HOT_NO_PLL_DOWN BIT(6) +#define D3_NO_PLL_DOWN (BIT(7) | BIT(6)) + EPHYAR = 0x80, +#define EPHYAR_FLAG 0x80000000 +#define EPHYAR_WRITE_CMD 0x80000000 +#define EPHYAR_REG_MASK 0x1f +#define EPHYAR_REG_SHIFT 16 +#define EPHYAR_DATA_MASK 0xffff + DLLPR = 0xd0, +#define PFM_EN (1 << 6) +#define TX_10M_PS_EN (1 << 7) + DBG_REG = 0xd1, +#define FIX_NAK_1 (1 << 4) +#define FIX_NAK_2 (1 << 3) + TWSI = 0xd2, + MCU = 0xd3, +#define NOW_IS_OOB (1 << 7) +#define TX_EMPTY (1 << 5) +#define RX_EMPTY (1 << 4) +#define RXTX_EMPTY (TX_EMPTY | RX_EMPTY) +#define EN_NDP (1 << 3) +#define EN_OOB_RESET (1 << 2) +#define LINK_LIST_RDY (1 << 1) + EFUSEAR = 0xdc, +#define EFUSEAR_FLAG 0x80000000 +#define EFUSEAR_WRITE_CMD 0x80000000 +#define EFUSEAR_READ_CMD 0x00000000 +#define EFUSEAR_REG_MASK 0x03ff +#define EFUSEAR_REG_SHIFT 8 +#define EFUSEAR_DATA_MASK 0xff + MISC_1 = 0xf2, +#define PFM_D3COLD_EN (1 << 6) +}; + +enum rtl8168_registers { + LED_CTRL = 0x18, + LED_FREQ = 0x1a, + EEE_LED = 0x1b, + ERIDR = 0x70, + ERIAR = 0x74, +#define ERIAR_FLAG 0x80000000 +#define ERIAR_WRITE_CMD 0x80000000 +#define ERIAR_READ_CMD 0x00000000 +#define ERIAR_ADDR_BYTE_ALIGN 4 +#define ERIAR_TYPE_SHIFT 16 +#define ERIAR_EXGMAC (0x00 << ERIAR_TYPE_SHIFT) +#define ERIAR_MSIX (0x01 << ERIAR_TYPE_SHIFT) +#define ERIAR_ASF (0x02 << ERIAR_TYPE_SHIFT) +#define ERIAR_OOB (0x02 << ERIAR_TYPE_SHIFT) +#define ERIAR_MASK_SHIFT 12 +#define ERIAR_MASK_0001 (0x1 << ERIAR_MASK_SHIFT) +#define ERIAR_MASK_0011 (0x3 << ERIAR_MASK_SHIFT) +#define ERIAR_MASK_0100 (0x4 << ERIAR_MASK_SHIFT) +#define ERIAR_MASK_0101 (0x5 << ERIAR_MASK_SHIFT) +#define ERIAR_MASK_1111 (0xf << ERIAR_MASK_SHIFT) + EPHY_RXER_NUM = 0x7c, + OCPDR = 0xb0, /* OCP GPHY access */ +#define OCPDR_WRITE_CMD 0x80000000 +#define OCPDR_READ_CMD 0x00000000 +#define OCPDR_REG_MASK 0x7f +#define OCPDR_GPHY_REG_SHIFT 16 +#define OCPDR_DATA_MASK 0xffff + OCPAR = 0xb4, +#define OCPAR_FLAG 0x80000000 +#define OCPAR_GPHY_WRITE_CMD 0x8000f060 +#define OCPAR_GPHY_READ_CMD 0x0000f060 + GPHY_OCP = 0xb8, + RDSAR1 = 0xd0, /* 8168c only. Undocumented on 8168dp */ + MISC = 0xf0, /* 8168e only. */ +#define TXPLA_RST (1 << 29) +#define DISABLE_LAN_EN (1 << 23) /* Enable GPIO pin */ +#define PWM_EN (1 << 22) +#define RXDV_GATED_EN (1 << 19) +#define EARLY_TALLY_EN (1 << 16) + COMBO_LTR_EXTEND = 0xb6, +#define COMBO_LTR_EXTEND_EN BIT(0) +}; + +enum rtl8125_registers { + LEDSEL0 = 0x18, + INT_CFG0_8125 = 0x34, +#define INT_CFG0_ENABLE_8125 BIT(0) +#define INT_CFG0_CLKREQEN BIT(3) + IntrMask_8125 = 0x38, + IntrStatus_8125 = 0x3c, + INT_CFG1_8125 = 0x7a, + LEDSEL2 = 0x84, + LEDSEL1 = 0x86, + TxPoll_8125 = 0x90, + LEDSEL3 = 0x96, + MAC0_BKP = 0x19e0, + RSS_CTRL_8125 = 0x4500, + Q_NUM_CTRL_8125 = 0x4800, + EEE_TXIDLE_TIMER_8125 = 0x6048, +}; + +#define LEDSEL_MASK_8125 0x23f + +#define RX_VLAN_INNER_8125 BIT(22) +#define RX_VLAN_OUTER_8125 BIT(23) +#define RX_VLAN_8125 (RX_VLAN_INNER_8125 | RX_VLAN_OUTER_8125) + +#define RX_FETCH_DFLT_8125 (8 << 27) + +enum rtl_register_content { + /* InterruptStatusBits */ + SYSErr = 0x8000, + PCSTimeout = 0x4000, + SWInt = 0x0100, + TxDescUnavail = 0x0080, + RxFIFOOver = 0x0040, + LinkChg = 0x0020, + RxOverflow = 0x0010, + TxErr = 0x0008, + TxOK = 0x0004, + RxErr = 0x0002, + RxOK = 0x0001, + + /* RxStatusDesc */ + RxRWT = (1 << 22), + RxRES = (1 << 21), + RxRUNT = (1 << 20), + RxCRC = (1 << 19), + + /* ChipCmdBits */ + StopReq = 0x80, + CmdReset = 0x10, + CmdRxEnb = 0x08, + CmdTxEnb = 0x04, + RxBufEmpty = 0x01, + + /* TXPoll register p.5 */ + HPQ = 0x80, /* Poll cmd on the high prio queue */ + NPQ = 0x40, /* Poll cmd on the low prio queue */ + FSWInt = 0x01, /* Forced software interrupt */ + + /* Cfg9346Bits */ + Cfg9346_Lock = 0x00, + Cfg9346_Unlock = 0xc0, + + /* rx_mode_bits */ + AcceptErr = 0x20, + AcceptRunt = 0x10, +#define RX_CONFIG_ACCEPT_ERR_MASK 0x30 + AcceptBroadcast = 0x08, + AcceptMulticast = 0x04, + AcceptMyPhys = 0x02, + AcceptAllPhys = 0x01, +#define RX_CONFIG_ACCEPT_OK_MASK 0x0f +#define RX_CONFIG_ACCEPT_MASK 0x3f + + /* TxConfigBits */ + TxInterFrameGapShift = 24, + TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */ + + /* Config1 register p.24 */ + LEDS1 = (1 << 7), + LEDS0 = (1 << 6), + Speed_down = (1 << 4), + MEMMAP = (1 << 3), + IOMAP = (1 << 2), + VPD = (1 << 1), + PMEnable = (1 << 0), /* Power Management Enable */ + + /* Config2 register p. 25 */ + ClkReqEn = (1 << 7), /* Clock Request Enable */ + MSIEnable = (1 << 5), /* 8169 only. Reserved in the 8168. */ + PCI_Clock_66MHz = 0x01, + PCI_Clock_33MHz = 0x00, + + /* Config3 register p.25 */ + MagicPacket = (1 << 5), /* Wake up when receives a Magic Packet */ + LinkUp = (1 << 4), /* Wake up when the cable connection is re-established */ + Jumbo_En0 = (1 << 2), /* 8168 only. Reserved in the 8168b */ + Rdy_to_L23 = (1 << 1), /* L23 Enable */ + Beacon_en = (1 << 0), /* 8168 only. Reserved in the 8168b */ + + /* Config4 register */ + Jumbo_En1 = (1 << 1), /* 8168 only. Reserved in the 8168b */ + + /* Config5 register p.27 */ + BWF = (1 << 6), /* Accept Broadcast wakeup frame */ + MWF = (1 << 5), /* Accept Multicast wakeup frame */ + UWF = (1 << 4), /* Accept Unicast wakeup frame */ + Spi_en = (1 << 3), + LanWake = (1 << 1), /* LanWake enable/disable */ + PMEStatus = (1 << 0), /* PME status can be reset by PCI RST# */ + ASPM_en = (1 << 0), /* ASPM enable */ + + /* CPlusCmd p.31 */ + EnableBist = (1 << 15), // 8168 8101 + Mac_dbgo_oe = (1 << 14), // 8168 8101 + EnAnaPLL = (1 << 14), // 8169 + Normal_mode = (1 << 13), // unused + Force_half_dup = (1 << 12), // 8168 8101 + Force_rxflow_en = (1 << 11), // 8168 8101 + Force_txflow_en = (1 << 10), // 8168 8101 + Cxpl_dbg_sel = (1 << 9), // 8168 8101 + ASF = (1 << 8), // 8168 8101 + PktCntrDisable = (1 << 7), // 8168 8101 + Mac_dbgo_sel = 0x001c, // 8168 + RxVlan = (1 << 6), + RxChkSum = (1 << 5), + PCIDAC = (1 << 4), + PCIMulRW = (1 << 3), +#define INTT_MASK GENMASK(1, 0) +#define CPCMD_MASK (Normal_mode | RxVlan | RxChkSum | INTT_MASK) + + /* rtl8169_PHYstatus */ + TBI_Enable = 0x80, + TxFlowCtrl = 0x40, + RxFlowCtrl = 0x20, + _1000bpsF = 0x10, + _100bps = 0x08, + _10bps = 0x04, + LinkStatus = 0x02, + FullDup = 0x01, + + /* ResetCounterCommand */ + CounterReset = 0x1, + + /* DumpCounterCommand */ + CounterDump = 0x8, + + /* magic enable v2 */ + MagicPacket_v2 = (1 << 16), /* Wake up when receives a Magic Packet */ +}; + +enum rtl_desc_bit { + /* First doubleword. */ + DescOwn = (1 << 31), /* Descriptor is owned by NIC */ + RingEnd = (1 << 30), /* End of descriptor ring */ + FirstFrag = (1 << 29), /* First segment of a packet */ + LastFrag = (1 << 28), /* Final segment of a packet */ +}; + +/* Generic case. */ +enum rtl_tx_desc_bit { + /* First doubleword. */ + TD_LSO = (1 << 27), /* Large Send Offload */ +#define TD_MSS_MAX 0x07ffu /* MSS value */ + + /* Second doubleword. */ + TxVlanTag = (1 << 17), /* Add VLAN tag */ +}; + +/* 8169, 8168b and 810x except 8102e. */ +enum rtl_tx_desc_bit_0 { + /* First doubleword. */ +#define TD0_MSS_SHIFT 16 /* MSS position (11 bits) */ + TD0_TCP_CS = (1 << 16), /* Calculate TCP/IP checksum */ + TD0_UDP_CS = (1 << 17), /* Calculate UDP/IP checksum */ + TD0_IP_CS = (1 << 18), /* Calculate IP checksum */ +}; + +/* 8102e, 8168c and beyond. */ +enum rtl_tx_desc_bit_1 { + /* First doubleword. */ + TD1_GTSENV4 = (1 << 26), /* Giant Send for IPv4 */ + TD1_GTSENV6 = (1 << 25), /* Giant Send for IPv6 */ +#define GTTCPHO_SHIFT 18 +#define GTTCPHO_MAX 0x7f + + /* Second doubleword. */ +#define TCPHO_SHIFT 18 +#define TCPHO_MAX 0x3ff +#define TD1_MSS_SHIFT 18 /* MSS position (11 bits) */ + TD1_IPv6_CS = (1 << 28), /* Calculate IPv6 checksum */ + TD1_IPv4_CS = (1 << 29), /* Calculate IPv4 checksum */ + TD1_TCP_CS = (1 << 30), /* Calculate TCP/IP checksum */ + TD1_UDP_CS = (1 << 31), /* Calculate UDP/IP checksum */ +}; + +enum rtl_rx_desc_bit { + /* Rx private */ + PID1 = (1 << 18), /* Protocol ID bit 1/2 */ + PID0 = (1 << 17), /* Protocol ID bit 0/2 */ + +#define RxProtoUDP (PID1) +#define RxProtoTCP (PID0) +#define RxProtoIP (PID1 | PID0) +#define RxProtoMask RxProtoIP + + IPFail = (1 << 16), /* IP checksum failed */ + UDPFail = (1 << 15), /* UDP/IP checksum failed */ + TCPFail = (1 << 14), /* TCP/IP checksum failed */ + +#define RxCSFailMask (IPFail | UDPFail | TCPFail) + + RxVlanTag = (1 << 16), /* VLAN tag available */ +}; + +#define RTL_GSO_MAX_SIZE_V1 32000 +#define RTL_GSO_MAX_SEGS_V1 24 +#define RTL_GSO_MAX_SIZE_V2 64000 +#define RTL_GSO_MAX_SEGS_V2 64 + +struct TxDesc { + __le32 opts1; + __le32 opts2; + __le64 addr; +}; + +struct RxDesc { + __le32 opts1; + __le32 opts2; + __le64 addr; +}; + +struct ring_info { + struct sk_buff *skb; + u32 len; +}; + +struct rtl8169_counters { + __le64 tx_packets; + __le64 rx_packets; + __le64 tx_errors; + __le32 rx_errors; + __le16 rx_missed; + __le16 align_errors; + __le32 tx_one_collision; + __le32 tx_multi_collision; + __le64 rx_unicast; + __le64 rx_broadcast; + __le32 rx_multicast; + __le16 tx_aborted; + __le16 tx_underrun; + /* new since RTL8125 */ + __le64 tx_octets; + __le64 rx_octets; + __le64 rx_multicast64; + __le64 tx_unicast64; + __le64 tx_broadcast64; + __le64 tx_multicast64; + __le32 tx_pause_on; + __le32 tx_pause_off; + __le32 tx_pause_all; + __le32 tx_deferred; + __le32 tx_late_collision; + __le32 tx_all_collision; + __le32 tx_aborted32; + __le32 align_errors32; + __le32 rx_frame_too_long; + __le32 rx_runt; + __le32 rx_pause_on; + __le32 rx_pause_off; + __le32 rx_pause_all; + __le32 rx_unknown_opcode; + __le32 rx_mac_error; + __le32 tx_underrun32; + __le32 rx_mac_missed; + __le32 rx_tcam_dropped; + __le32 tdu; + __le32 rdu; +}; + +struct rtl8169_tc_offsets { + bool inited; + __le64 tx_errors; + __le32 tx_multi_collision; + __le16 tx_aborted; + __le16 rx_missed; +}; + +enum rtl_flag { + RTL_FLAG_TASK_RESET_PENDING, + RTL_FLAG_TASK_TX_TIMEOUT, + RTL_FLAG_MAX +}; + +enum rtl_dash_type { + RTL_DASH_NONE, + RTL_DASH_DP, + RTL_DASH_EP, + RTL_DASH_25_BP, +}; + +struct rtl8169_private { + void __iomem *mmio_addr; /* memory map physical address */ + struct pci_dev *pci_dev; + struct net_device *dev; + struct phy_device *phydev; + struct napi_struct napi; + enum mac_version mac_version; + enum rtl_dash_type dash_type; + u32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */ + u32 cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */ + u32 dirty_tx; + struct TxDesc *TxDescArray; /* 256-aligned Tx descriptor ring */ + struct RxDesc *RxDescArray; /* 256-aligned Rx descriptor ring */ + dma_addr_t TxPhyAddr; + dma_addr_t RxPhyAddr; + struct page *Rx_databuff[NUM_RX_DESC]; /* Rx data buffers */ + struct ring_info tx_skb[NUM_TX_DESC]; /* Tx data buffers */ + u16 cp_cmd; + u16 tx_lpi_timer; + u32 irq_mask; + int irq; + struct clk *clk; + + struct { + DECLARE_BITMAP(flags, RTL_FLAG_MAX); + struct work_struct work; + } wk; + + raw_spinlock_t mac_ocp_lock; + struct mutex led_lock; /* serialize LED ctrl RMW access */ + + unsigned supports_gmii:1; + unsigned aspm_manageable:1; + unsigned dash_enabled:1; + bool sfp_mode:1; + dma_addr_t counters_phys_addr; + struct rtl8169_counters *counters; + struct rtl8169_tc_offsets tc_offset; + u32 saved_wolopts; + + const char *fw_name; + struct rtl_fw *rtl_fw; + + struct r8169_led_classdev *leds; + + u32 ocp_base; +}; + +typedef void (*rtl_generic_fct)(struct rtl8169_private *tp); + +MODULE_AUTHOR("Realtek and the Linux r8169 crew <netdev@vger.kernel.org>"); +MODULE_DESCRIPTION("RealTek RTL-8169 Gigabit Ethernet driver"); +MODULE_SOFTDEP("pre: realtek"); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(FIRMWARE_8168D_1); +MODULE_FIRMWARE(FIRMWARE_8168D_2); +MODULE_FIRMWARE(FIRMWARE_8168E_1); +MODULE_FIRMWARE(FIRMWARE_8168E_2); +MODULE_FIRMWARE(FIRMWARE_8168E_3); +MODULE_FIRMWARE(FIRMWARE_8105E_1); +MODULE_FIRMWARE(FIRMWARE_8168F_1); +MODULE_FIRMWARE(FIRMWARE_8168F_2); +MODULE_FIRMWARE(FIRMWARE_8402_1); +MODULE_FIRMWARE(FIRMWARE_8411_1); +MODULE_FIRMWARE(FIRMWARE_8411_2); +MODULE_FIRMWARE(FIRMWARE_8106E_1); +MODULE_FIRMWARE(FIRMWARE_8106E_2); +MODULE_FIRMWARE(FIRMWARE_8168G_2); +MODULE_FIRMWARE(FIRMWARE_8168G_3); +MODULE_FIRMWARE(FIRMWARE_8168H_2); +MODULE_FIRMWARE(FIRMWARE_8168FP_3); +MODULE_FIRMWARE(FIRMWARE_8107E_2); +MODULE_FIRMWARE(FIRMWARE_8125A_3); +MODULE_FIRMWARE(FIRMWARE_8125B_2); +MODULE_FIRMWARE(FIRMWARE_8125D_1); +MODULE_FIRMWARE(FIRMWARE_8125D_2); +MODULE_FIRMWARE(FIRMWARE_8125K_1); +MODULE_FIRMWARE(FIRMWARE_8125BP_2); +MODULE_FIRMWARE(FIRMWARE_8125CP_1); +MODULE_FIRMWARE(FIRMWARE_9151A_1); +MODULE_FIRMWARE(FIRMWARE_8126A_2); +MODULE_FIRMWARE(FIRMWARE_8126A_3); +MODULE_FIRMWARE(FIRMWARE_8127A_1); + +static inline struct device *tp_to_dev(struct rtl8169_private *tp) +{ + return &tp->pci_dev->dev; +} + +static void rtl_lock_config_regs(struct rtl8169_private *tp) +{ + RTL_W8(tp, Cfg9346, Cfg9346_Lock); +} + +static void rtl_unlock_config_regs(struct rtl8169_private *tp) +{ + RTL_W8(tp, Cfg9346, Cfg9346_Unlock); +} + +static void rtl_pci_commit(struct rtl8169_private *tp) +{ + /* Read an arbitrary register to commit a preceding PCI write */ + RTL_R8(tp, ChipCmd); +} + +static void rtl_mod_config2(struct rtl8169_private *tp, u8 clear, u8 set) +{ + u8 val; + + val = RTL_R8(tp, Config2); + RTL_W8(tp, Config2, (val & ~clear) | set); +} + +static void rtl_mod_config5(struct rtl8169_private *tp, u8 clear, u8 set) +{ + u8 val; + + val = RTL_R8(tp, Config5); + RTL_W8(tp, Config5, (val & ~clear) | set); +} + +static void r8169_mod_reg8_cond(struct rtl8169_private *tp, int reg, + u8 bits, bool cond) +{ + u8 val, old_val; + + old_val = RTL_R8(tp, reg); + if (cond) + val = old_val | bits; + else + val = old_val & ~bits; + if (val != old_val) + RTL_W8(tp, reg, val); +} + +static bool rtl_is_8125(struct rtl8169_private *tp) +{ + return tp->mac_version >= RTL_GIGA_MAC_VER_61; +} + +static bool rtl_is_8168evl_up(struct rtl8169_private *tp) +{ + return tp->mac_version >= RTL_GIGA_MAC_VER_34 && + tp->mac_version != RTL_GIGA_MAC_VER_39 && + tp->mac_version <= RTL_GIGA_MAC_VER_52; +} + +static bool rtl_supports_eee(struct rtl8169_private *tp) +{ + return tp->mac_version >= RTL_GIGA_MAC_VER_34 && + tp->mac_version != RTL_GIGA_MAC_VER_37 && + tp->mac_version != RTL_GIGA_MAC_VER_39; +} + +static void rtl_read_mac_from_reg(struct rtl8169_private *tp, u8 *mac, int reg) +{ + int i; + + for (i = 0; i < ETH_ALEN; i++) + mac[i] = RTL_R8(tp, reg + i); +} + +struct rtl_cond { + bool (*check)(struct rtl8169_private *); + const char *msg; +}; + +static bool rtl_loop_wait(struct rtl8169_private *tp, const struct rtl_cond *c, + unsigned long usecs, int n, bool high) +{ + int i; + + for (i = 0; i < n; i++) { + if (c->check(tp) == high) + return true; + fsleep(usecs); + } + + if (net_ratelimit()) + netdev_err(tp->dev, "%s == %d (loop: %d, delay: %lu).\n", + c->msg, !high, n, usecs); + return false; +} + +static bool rtl_loop_wait_high(struct rtl8169_private *tp, + const struct rtl_cond *c, + unsigned long d, int n) +{ + return rtl_loop_wait(tp, c, d, n, true); +} + +static bool rtl_loop_wait_low(struct rtl8169_private *tp, + const struct rtl_cond *c, + unsigned long d, int n) +{ + return rtl_loop_wait(tp, c, d, n, false); +} + +#define DECLARE_RTL_COND(name) \ +static bool name ## _check(struct rtl8169_private *); \ + \ +static const struct rtl_cond name = { \ + .check = name ## _check, \ + .msg = #name \ +}; \ + \ +static bool name ## _check(struct rtl8169_private *tp) + +int rtl8168_led_mod_ctrl(struct rtl8169_private *tp, u16 mask, u16 val) +{ + struct device *dev = tp_to_dev(tp); + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + mutex_lock(&tp->led_lock); + RTL_W16(tp, LED_CTRL, (RTL_R16(tp, LED_CTRL) & ~mask) | val); + mutex_unlock(&tp->led_lock); + + pm_runtime_put_sync(dev); + + return 0; +} + +int rtl8168_get_led_mode(struct rtl8169_private *tp) +{ + struct device *dev = tp_to_dev(tp); + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + ret = RTL_R16(tp, LED_CTRL); + + pm_runtime_put_sync(dev); + + return ret; +} + +static int rtl8125_get_led_reg(int index) +{ + static const int led_regs[] = { LEDSEL0, LEDSEL1, LEDSEL2, LEDSEL3 }; + + return led_regs[index]; +} + +int rtl8125_set_led_mode(struct rtl8169_private *tp, int index, u16 mode) +{ + int reg = rtl8125_get_led_reg(index); + struct device *dev = tp_to_dev(tp); + int ret; + u16 val; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + mutex_lock(&tp->led_lock); + val = RTL_R16(tp, reg) & ~LEDSEL_MASK_8125; + RTL_W16(tp, reg, val | mode); + mutex_unlock(&tp->led_lock); + + pm_runtime_put_sync(dev); + + return 0; +} + +int rtl8125_get_led_mode(struct rtl8169_private *tp, int index) +{ + int reg = rtl8125_get_led_reg(index); + struct device *dev = tp_to_dev(tp); + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + ret = RTL_R16(tp, reg); + + pm_runtime_put_sync(dev); + + return ret; +} + +void r8169_get_led_name(struct rtl8169_private *tp, int idx, + char *buf, int buf_len) +{ + struct pci_dev *pdev = tp->pci_dev; + char pdom[8], pfun[8]; + int domain; + + domain = pci_domain_nr(pdev->bus); + if (domain) + snprintf(pdom, sizeof(pdom), "P%d", domain); + else + pdom[0] = '\0'; + + if (pdev->multifunction) + snprintf(pfun, sizeof(pfun), "f%d", PCI_FUNC(pdev->devfn)); + else + pfun[0] = '\0'; + + snprintf(buf, buf_len, "en%sp%ds%d%s-%d::lan", pdom, pdev->bus->number, + PCI_SLOT(pdev->devfn), pfun, idx); +} + +static void r8168fp_adjust_ocp_cmd(struct rtl8169_private *tp, u32 *cmd, int type) +{ + /* based on RTL8168FP_OOBMAC_BASE in vendor driver */ + if (type == ERIAR_OOB && tp->mac_version == RTL_GIGA_MAC_VER_52) + *cmd |= 0xf70 << 18; +} + +DECLARE_RTL_COND(rtl_eriar_cond) +{ + return RTL_R32(tp, ERIAR) & ERIAR_FLAG; +} + +static void _rtl_eri_write(struct rtl8169_private *tp, int addr, u32 mask, + u32 val, int type) +{ + u32 cmd = ERIAR_WRITE_CMD | type | mask | addr; + + if (WARN(addr & 3 || !mask, "addr: 0x%x, mask: 0x%08x\n", addr, mask)) + return; + + RTL_W32(tp, ERIDR, val); + r8168fp_adjust_ocp_cmd(tp, &cmd, type); + RTL_W32(tp, ERIAR, cmd); + + rtl_loop_wait_low(tp, &rtl_eriar_cond, 100, 100); +} + +static void rtl_eri_write(struct rtl8169_private *tp, int addr, u32 mask, + u32 val) +{ + _rtl_eri_write(tp, addr, mask, val, ERIAR_EXGMAC); +} + +static u32 _rtl_eri_read(struct rtl8169_private *tp, int addr, int type) +{ + u32 cmd = ERIAR_READ_CMD | type | ERIAR_MASK_1111 | addr; + + r8168fp_adjust_ocp_cmd(tp, &cmd, type); + RTL_W32(tp, ERIAR, cmd); + + return rtl_loop_wait_high(tp, &rtl_eriar_cond, 100, 100) ? + RTL_R32(tp, ERIDR) : ~0; +} + +static u32 rtl_eri_read(struct rtl8169_private *tp, int addr) +{ + return _rtl_eri_read(tp, addr, ERIAR_EXGMAC); +} + +static void rtl_w0w1_eri(struct rtl8169_private *tp, int addr, u32 p, u32 m) +{ + u32 val = rtl_eri_read(tp, addr); + + rtl_eri_write(tp, addr, ERIAR_MASK_1111, (val & ~m) | p); +} + +static void rtl_eri_set_bits(struct rtl8169_private *tp, int addr, u32 p) +{ + rtl_w0w1_eri(tp, addr, p, 0); +} + +static void rtl_eri_clear_bits(struct rtl8169_private *tp, int addr, u32 m) +{ + rtl_w0w1_eri(tp, addr, 0, m); +} + +static bool rtl_ocp_reg_failure(u32 reg) +{ + return WARN_ONCE(reg & 0xffff0001, "Invalid ocp reg %x!\n", reg); +} + +DECLARE_RTL_COND(rtl_ocp_gphy_cond) +{ + return RTL_R32(tp, GPHY_OCP) & OCPAR_FLAG; +} + +static void r8168_phy_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data) +{ + if (rtl_ocp_reg_failure(reg)) + return; + + RTL_W32(tp, GPHY_OCP, OCPAR_FLAG | (reg << 15) | data); + + rtl_loop_wait_low(tp, &rtl_ocp_gphy_cond, 25, 10); +} + +static int r8168_phy_ocp_read(struct rtl8169_private *tp, u32 reg) +{ + if (rtl_ocp_reg_failure(reg)) + return 0; + + /* Return dummy MII_PHYSID2 in SFP mode to match SFP PHY driver */ + if (tp->sfp_mode && reg == (OCP_STD_PHY_BASE + 2 * MII_PHYSID2)) + return PHY_ID_RTL_DUMMY_SFP & 0xffff; + + RTL_W32(tp, GPHY_OCP, reg << 15); + + return rtl_loop_wait_high(tp, &rtl_ocp_gphy_cond, 25, 10) ? + (RTL_R32(tp, GPHY_OCP) & 0xffff) : -ETIMEDOUT; +} + +static void __r8168_mac_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data) +{ + if (rtl_ocp_reg_failure(reg)) + return; + + RTL_W32(tp, OCPDR, OCPAR_FLAG | (reg << 15) | data); +} + +static void r8168_mac_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&tp->mac_ocp_lock, flags); + __r8168_mac_ocp_write(tp, reg, data); + raw_spin_unlock_irqrestore(&tp->mac_ocp_lock, flags); +} + +static u16 __r8168_mac_ocp_read(struct rtl8169_private *tp, u32 reg) +{ + if (rtl_ocp_reg_failure(reg)) + return 0; + + RTL_W32(tp, OCPDR, reg << 15); + + return RTL_R32(tp, OCPDR); +} + +static u16 r8168_mac_ocp_read(struct rtl8169_private *tp, u32 reg) +{ + unsigned long flags; + u16 val; + + raw_spin_lock_irqsave(&tp->mac_ocp_lock, flags); + val = __r8168_mac_ocp_read(tp, reg); + raw_spin_unlock_irqrestore(&tp->mac_ocp_lock, flags); + + return val; +} + +static void r8168_mac_ocp_modify(struct rtl8169_private *tp, u32 reg, u16 mask, + u16 set) +{ + unsigned long flags; + u16 data; + + raw_spin_lock_irqsave(&tp->mac_ocp_lock, flags); + data = __r8168_mac_ocp_read(tp, reg); + __r8168_mac_ocp_write(tp, reg, (data & ~mask) | set); + raw_spin_unlock_irqrestore(&tp->mac_ocp_lock, flags); +} + +static void r8127_sfp_sds_phy_reset(struct rtl8169_private *tp) +{ + RTL_W8(tp, 0x2350, RTL_R8(tp, 0x2350) & ~BIT(0)); + udelay(1); + + RTL_W16(tp, 0x233a, 0x801f); + RTL_W8(tp, 0x2350, RTL_R8(tp, 0x2350) | BIT(0)); + usleep_range(10, 20); +} + +static void r8127_sfp_init_10g(struct rtl8169_private *tp) +{ + int val; + + r8127_sfp_sds_phy_reset(tp); + + RTL_W16(tp, 0x233a, 0x801a); + RTL_W16(tp, 0x233e, (RTL_R16(tp, 0x233e) & ~0x3003) | 0x1000); + + r8168_phy_ocp_write(tp, 0xc40a, 0x0000); + r8168_phy_ocp_write(tp, 0xc466, 0x0003); + r8168_phy_ocp_write(tp, 0xc808, 0x0000); + r8168_phy_ocp_write(tp, 0xc80a, 0x0000); + + val = r8168_phy_ocp_read(tp, 0xc804); + r8168_phy_ocp_write(tp, 0xc804, (val & ~0x000f) | 0x000c); +} + +static void rtl_sfp_init(struct rtl8169_private *tp) +{ + if (tp->mac_version == RTL_GIGA_MAC_VER_80) + r8127_sfp_init_10g(tp); +} + +static void rtl_sfp_reset(struct rtl8169_private *tp) +{ + if (tp->mac_version == RTL_GIGA_MAC_VER_80) + r8127_sfp_sds_phy_reset(tp); +} + +/* Work around a hw issue with RTL8168g PHY, the quirk disables + * PHY MCU interrupts before PHY power-down. + */ +static void rtl8168g_phy_suspend_quirk(struct rtl8169_private *tp, int value) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_40: + if (value & BMCR_RESET || !(value & BMCR_PDOWN)) + rtl_eri_set_bits(tp, 0x1a8, 0xfc000000); + else + rtl_eri_clear_bits(tp, 0x1a8, 0xfc000000); + break; + default: + break; + } +}; + +static void r8168g_mdio_write(struct rtl8169_private *tp, int reg, int value) +{ + if (reg == 0x1f) { + tp->ocp_base = value ? value << 4 : OCP_STD_PHY_BASE; + return; + } + + if (tp->ocp_base != OCP_STD_PHY_BASE) + reg -= 0x10; + + if (tp->ocp_base == OCP_STD_PHY_BASE && reg == MII_BMCR) + rtl8168g_phy_suspend_quirk(tp, value); + + r8168_phy_ocp_write(tp, tp->ocp_base + reg * 2, value); +} + +static int r8168g_mdio_read(struct rtl8169_private *tp, int reg) +{ + if (reg == 0x1f) + return tp->ocp_base == OCP_STD_PHY_BASE ? 0 : tp->ocp_base >> 4; + + if (tp->ocp_base != OCP_STD_PHY_BASE) + reg -= 0x10; + + return r8168_phy_ocp_read(tp, tp->ocp_base + reg * 2); +} + +static void mac_mcu_write(struct rtl8169_private *tp, int reg, int value) +{ + if (reg == 0x1f) { + tp->ocp_base = value << 4; + return; + } + + r8168_mac_ocp_write(tp, tp->ocp_base + reg, value); +} + +static int mac_mcu_read(struct rtl8169_private *tp, int reg) +{ + return r8168_mac_ocp_read(tp, tp->ocp_base + reg); +} + +DECLARE_RTL_COND(rtl_phyar_cond) +{ + return RTL_R32(tp, PHYAR) & 0x80000000; +} + +static void r8169_mdio_write(struct rtl8169_private *tp, int reg, int value) +{ + RTL_W32(tp, PHYAR, 0x80000000 | (reg & 0x1f) << 16 | (value & 0xffff)); + + rtl_loop_wait_low(tp, &rtl_phyar_cond, 25, 20); + /* + * According to hardware specs a 20us delay is required after write + * complete indication, but before sending next command. + */ + udelay(20); +} + +static int r8169_mdio_read(struct rtl8169_private *tp, int reg) +{ + int value; + + RTL_W32(tp, PHYAR, 0x0 | (reg & 0x1f) << 16); + + value = rtl_loop_wait_high(tp, &rtl_phyar_cond, 25, 20) ? + RTL_R32(tp, PHYAR) & 0xffff : -ETIMEDOUT; + + /* + * According to hardware specs a 20us delay is required after read + * complete indication, but before sending next command. + */ + udelay(20); + + return value; +} + +DECLARE_RTL_COND(rtl_ocpar_cond) +{ + return RTL_R32(tp, OCPAR) & OCPAR_FLAG; +} + +#define R8168DP_1_MDIO_ACCESS_BIT 0x00020000 + +static void r8168dp_2_mdio_start(struct rtl8169_private *tp) +{ + RTL_W32(tp, 0xd0, RTL_R32(tp, 0xd0) & ~R8168DP_1_MDIO_ACCESS_BIT); +} + +static void r8168dp_2_mdio_stop(struct rtl8169_private *tp) +{ + RTL_W32(tp, 0xd0, RTL_R32(tp, 0xd0) | R8168DP_1_MDIO_ACCESS_BIT); +} + +static void r8168dp_2_mdio_write(struct rtl8169_private *tp, int reg, int value) +{ + r8168dp_2_mdio_start(tp); + + r8169_mdio_write(tp, reg, value); + + r8168dp_2_mdio_stop(tp); +} + +static int r8168dp_2_mdio_read(struct rtl8169_private *tp, int reg) +{ + int value; + + /* Work around issue with chip reporting wrong PHY ID */ + if (reg == MII_PHYSID2) + return 0xc912; + + r8168dp_2_mdio_start(tp); + + value = r8169_mdio_read(tp, reg); + + r8168dp_2_mdio_stop(tp); + + return value; +} + +static void rtl_writephy(struct rtl8169_private *tp, int location, int val) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_28: + case RTL_GIGA_MAC_VER_31: + r8168dp_2_mdio_write(tp, location, val); + break; + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_LAST: + r8168g_mdio_write(tp, location, val); + break; + default: + r8169_mdio_write(tp, location, val); + break; + } +} + +static int rtl_readphy(struct rtl8169_private *tp, int location) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_28: + case RTL_GIGA_MAC_VER_31: + return r8168dp_2_mdio_read(tp, location); + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_LAST: + return r8168g_mdio_read(tp, location); + default: + return r8169_mdio_read(tp, location); + } +} + +DECLARE_RTL_COND(rtl_ephyar_cond) +{ + return RTL_R32(tp, EPHYAR) & EPHYAR_FLAG; +} + +static void rtl_ephy_write(struct rtl8169_private *tp, int reg_addr, int value) +{ + RTL_W32(tp, EPHYAR, EPHYAR_WRITE_CMD | (value & EPHYAR_DATA_MASK) | + (reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT); + + rtl_loop_wait_low(tp, &rtl_ephyar_cond, 10, 100); + + udelay(10); +} + +static u16 rtl_ephy_read(struct rtl8169_private *tp, int reg_addr) +{ + RTL_W32(tp, EPHYAR, (reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT); + + return rtl_loop_wait_high(tp, &rtl_ephyar_cond, 10, 100) ? + RTL_R32(tp, EPHYAR) & EPHYAR_DATA_MASK : ~0; +} + +static u32 r8168dp_ocp_read(struct rtl8169_private *tp, u16 reg) +{ + RTL_W32(tp, OCPAR, 0x0fu << 12 | (reg & 0x0fff)); + return rtl_loop_wait_high(tp, &rtl_ocpar_cond, 100, 20) ? + RTL_R32(tp, OCPDR) : ~0; +} + +static u32 r8168ep_ocp_read(struct rtl8169_private *tp, u16 reg) +{ + return _rtl_eri_read(tp, reg, ERIAR_OOB); +} + +static void r8168dp_ocp_write(struct rtl8169_private *tp, u8 mask, u16 reg, + u32 data) +{ + RTL_W32(tp, OCPDR, data); + RTL_W32(tp, OCPAR, OCPAR_FLAG | ((u32)mask & 0x0f) << 12 | (reg & 0x0fff)); + rtl_loop_wait_low(tp, &rtl_ocpar_cond, 100, 20); +} + +static void r8168ep_ocp_write(struct rtl8169_private *tp, u8 mask, u16 reg, + u32 data) +{ + _rtl_eri_write(tp, reg, ((u32)mask & 0x0f) << ERIAR_MASK_SHIFT, + data, ERIAR_OOB); +} + +static void r8168dp_oob_notify(struct rtl8169_private *tp, u8 cmd) +{ + rtl_eri_write(tp, 0xe8, ERIAR_MASK_0001, cmd); + + r8168dp_ocp_write(tp, 0x1, 0x30, 0x00000001); +} + +#define OOB_CMD_RESET 0x00 +#define OOB_CMD_DRIVER_START 0x05 +#define OOB_CMD_DRIVER_STOP 0x06 + +static u16 rtl8168_get_ocp_reg(struct rtl8169_private *tp) +{ + return (tp->mac_version == RTL_GIGA_MAC_VER_31) ? 0xb8 : 0x10; +} + +DECLARE_RTL_COND(rtl_dp_ocp_read_cond) +{ + u16 reg; + + reg = rtl8168_get_ocp_reg(tp); + + return r8168dp_ocp_read(tp, reg) & 0x00000800; +} + +DECLARE_RTL_COND(rtl_ep_ocp_read_cond) +{ + return r8168ep_ocp_read(tp, 0x124) & 0x00000001; +} + +DECLARE_RTL_COND(rtl_ocp_tx_cond) +{ + return RTL_R8(tp, IBISR0) & 0x20; +} + +static void rtl8168ep_stop_cmac(struct rtl8169_private *tp) +{ + RTL_W8(tp, IBCR2, RTL_R8(tp, IBCR2) & ~0x01); + rtl_loop_wait_high(tp, &rtl_ocp_tx_cond, 50000, 2000); + RTL_W8(tp, IBISR0, RTL_R8(tp, IBISR0) | 0x20); + RTL_W8(tp, IBCR0, RTL_R8(tp, IBCR0) & ~0x01); +} + +static void rtl8168dp_driver_start(struct rtl8169_private *tp) +{ + r8168dp_oob_notify(tp, OOB_CMD_DRIVER_START); + if (tp->dash_enabled) + rtl_loop_wait_high(tp, &rtl_dp_ocp_read_cond, 10000, 10); +} + +static void rtl8168ep_driver_start(struct rtl8169_private *tp) +{ + r8168ep_ocp_write(tp, 0x01, 0x180, OOB_CMD_DRIVER_START); + r8168ep_ocp_write(tp, 0x01, 0x30, r8168ep_ocp_read(tp, 0x30) | 0x01); + if (tp->dash_enabled) + rtl_loop_wait_high(tp, &rtl_ep_ocp_read_cond, 10000, 30); +} + +static void rtl8125bp_driver_start(struct rtl8169_private *tp) +{ + r8168ep_ocp_write(tp, 0x01, 0x14, OOB_CMD_DRIVER_START); + r8168ep_ocp_write(tp, 0x01, 0x18, 0x00); + r8168ep_ocp_write(tp, 0x01, 0x10, 0x01); +} + +static void rtl8168_driver_start(struct rtl8169_private *tp) +{ + if (tp->dash_type == RTL_DASH_DP) + rtl8168dp_driver_start(tp); + else if (tp->dash_type == RTL_DASH_25_BP) + rtl8125bp_driver_start(tp); + else + rtl8168ep_driver_start(tp); +} + +static void rtl8168dp_driver_stop(struct rtl8169_private *tp) +{ + r8168dp_oob_notify(tp, OOB_CMD_DRIVER_STOP); + if (tp->dash_enabled) + rtl_loop_wait_low(tp, &rtl_dp_ocp_read_cond, 10000, 10); +} + +static void rtl8168ep_driver_stop(struct rtl8169_private *tp) +{ + rtl8168ep_stop_cmac(tp); + r8168ep_ocp_write(tp, 0x01, 0x180, OOB_CMD_DRIVER_STOP); + r8168ep_ocp_write(tp, 0x01, 0x30, r8168ep_ocp_read(tp, 0x30) | 0x01); + if (tp->dash_enabled) + rtl_loop_wait_low(tp, &rtl_ep_ocp_read_cond, 10000, 10); +} + +static void rtl8125bp_driver_stop(struct rtl8169_private *tp) +{ + r8168ep_ocp_write(tp, 0x01, 0x14, OOB_CMD_DRIVER_STOP); + r8168ep_ocp_write(tp, 0x01, 0x18, 0x00); + r8168ep_ocp_write(tp, 0x01, 0x10, 0x01); +} + +static void rtl8168_driver_stop(struct rtl8169_private *tp) +{ + if (tp->dash_type == RTL_DASH_DP) + rtl8168dp_driver_stop(tp); + else if (tp->dash_type == RTL_DASH_25_BP) + rtl8125bp_driver_stop(tp); + else + rtl8168ep_driver_stop(tp); +} + +static bool r8168dp_check_dash(struct rtl8169_private *tp) +{ + u16 reg = rtl8168_get_ocp_reg(tp); + + return r8168dp_ocp_read(tp, reg) & BIT(15); +} + +static bool r8168ep_check_dash(struct rtl8169_private *tp) +{ + return r8168ep_ocp_read(tp, 0x128) & BIT(0); +} + +static bool rtl_dash_is_enabled(struct rtl8169_private *tp) +{ + switch (tp->dash_type) { + case RTL_DASH_DP: + return r8168dp_check_dash(tp); + case RTL_DASH_EP: + case RTL_DASH_25_BP: + return r8168ep_check_dash(tp); + default: + return false; + } +} + +static enum rtl_dash_type rtl_get_dash_type(struct rtl8169_private *tp) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_28: + case RTL_GIGA_MAC_VER_31: + return RTL_DASH_DP; + case RTL_GIGA_MAC_VER_51 ... RTL_GIGA_MAC_VER_52: + return RTL_DASH_EP; + case RTL_GIGA_MAC_VER_66: + return RTL_DASH_25_BP; + case RTL_GIGA_MAC_VER_80: + return (tp->pci_dev->revision == 0x04) + ? RTL_DASH_25_BP + : RTL_DASH_NONE; + default: + return RTL_DASH_NONE; + } +} + +static void rtl_set_d3_pll_down(struct rtl8169_private *tp, bool enable) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_24: + case RTL_GIGA_MAC_VER_28: + case RTL_GIGA_MAC_VER_31: + case RTL_GIGA_MAC_VER_38: + break; + case RTL_GIGA_MAC_VER_80: + r8169_mod_reg8_cond(tp, PMCH, D3_NO_PLL_DOWN, true); + break; + default: + r8169_mod_reg8_cond(tp, PMCH, D3HOT_NO_PLL_DOWN, true); + r8169_mod_reg8_cond(tp, PMCH, D3COLD_NO_PLL_DOWN, !enable); + break; + } +} + +static void rtl_reset_packet_filter(struct rtl8169_private *tp) +{ + rtl_eri_clear_bits(tp, 0xdc, BIT(0)); + rtl_eri_set_bits(tp, 0xdc, BIT(0)); +} + +DECLARE_RTL_COND(rtl_efusear_cond) +{ + return RTL_R32(tp, EFUSEAR) & EFUSEAR_FLAG; +} + +u8 rtl8168d_efuse_read(struct rtl8169_private *tp, int reg_addr) +{ + RTL_W32(tp, EFUSEAR, (reg_addr & EFUSEAR_REG_MASK) << EFUSEAR_REG_SHIFT); + + return rtl_loop_wait_high(tp, &rtl_efusear_cond, 100, 300) ? + RTL_R32(tp, EFUSEAR) & EFUSEAR_DATA_MASK : ~0; +} + +static u32 rtl_get_events(struct rtl8169_private *tp) +{ + if (rtl_is_8125(tp)) + return RTL_R32(tp, IntrStatus_8125); + else + return RTL_R16(tp, IntrStatus); +} + +static void rtl_ack_events(struct rtl8169_private *tp, u32 bits) +{ + if (rtl_is_8125(tp)) + RTL_W32(tp, IntrStatus_8125, bits); + else + RTL_W16(tp, IntrStatus, bits); +} + +static void rtl_irq_disable(struct rtl8169_private *tp) +{ + if (rtl_is_8125(tp)) + RTL_W32(tp, IntrMask_8125, 0); + else + RTL_W16(tp, IntrMask, 0); +} + +static void rtl_irq_enable(struct rtl8169_private *tp) +{ + if (rtl_is_8125(tp)) + RTL_W32(tp, IntrMask_8125, tp->irq_mask); + else + RTL_W16(tp, IntrMask, tp->irq_mask); +} + +static void rtl8169_irq_mask_and_ack(struct rtl8169_private *tp) +{ + rtl_irq_disable(tp); + rtl_ack_events(tp, 0xffffffff); + rtl_pci_commit(tp); +} + +static void rtl_link_chg_patch(struct rtl8169_private *tp) +{ + struct phy_device *phydev = tp->phydev; + + if (tp->mac_version == RTL_GIGA_MAC_VER_34 || + tp->mac_version == RTL_GIGA_MAC_VER_38) { + if (phydev->speed == SPEED_1000) { + rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x00000011); + rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x00000005); + } else if (phydev->speed == SPEED_100) { + rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x0000001f); + rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x00000005); + } else { + rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x0000001f); + rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x0000003f); + } + rtl_reset_packet_filter(tp); + } else if (tp->mac_version == RTL_GIGA_MAC_VER_35 || + tp->mac_version == RTL_GIGA_MAC_VER_36) { + if (phydev->speed == SPEED_1000) { + rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x00000011); + rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x00000005); + } else { + rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x0000001f); + rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x0000003f); + } + } else if (tp->mac_version == RTL_GIGA_MAC_VER_37) { + if (phydev->speed == SPEED_10) { + rtl_eri_write(tp, 0x1d0, ERIAR_MASK_0011, 0x4d02); + rtl_eri_write(tp, 0x1dc, ERIAR_MASK_0011, 0x0060a); + } else { + rtl_eri_write(tp, 0x1d0, ERIAR_MASK_0011, 0x0000); + } + } +} + +#define WAKE_ANY (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST) + +static void rtl8169_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + wol->supported = WAKE_ANY; + wol->wolopts = tp->saved_wolopts; +} + +static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts) +{ + rtl_unlock_config_regs(tp); + + if (rtl_is_8168evl_up(tp)) { + if (wolopts & WAKE_MAGIC) + rtl_eri_set_bits(tp, 0x0dc, MagicPacket_v2); + else + rtl_eri_clear_bits(tp, 0x0dc, MagicPacket_v2); + } else if (rtl_is_8125(tp)) { + if (wolopts & WAKE_MAGIC) + r8168_mac_ocp_modify(tp, 0xc0b6, 0, BIT(0)); + else + r8168_mac_ocp_modify(tp, 0xc0b6, BIT(0), 0); + } else { + r8169_mod_reg8_cond(tp, Config3, MagicPacket, + wolopts & WAKE_MAGIC); + } + + r8169_mod_reg8_cond(tp, Config3, LinkUp, wolopts & WAKE_PHY); + if (rtl_is_8125(tp)) + r8168_mac_ocp_modify(tp, 0xe0c6, 0x3f, + wolopts & WAKE_PHY ? 0x13 : 0); + r8169_mod_reg8_cond(tp, Config5, UWF, wolopts & WAKE_UCAST); + r8169_mod_reg8_cond(tp, Config5, BWF, wolopts & WAKE_BCAST); + r8169_mod_reg8_cond(tp, Config5, MWF, wolopts & WAKE_MCAST); + r8169_mod_reg8_cond(tp, Config5, LanWake, wolopts); + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_06: + r8169_mod_reg8_cond(tp, Config1, PMEnable, wolopts); + break; + case RTL_GIGA_MAC_VER_34: + case RTL_GIGA_MAC_VER_37: + case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_LAST: + r8169_mod_reg8_cond(tp, Config2, PME_SIGNAL, wolopts); + break; + default: + break; + } + + rtl_lock_config_regs(tp); + + device_set_wakeup_enable(tp_to_dev(tp), wolopts); + + if (!tp->dash_enabled) { + rtl_set_d3_pll_down(tp, !wolopts); + tp->dev->ethtool->wol_enabled = wolopts ? 1 : 0; + } +} + +static int rtl8169_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + if (wol->wolopts & ~WAKE_ANY) + return -EINVAL; + + tp->saved_wolopts = wol->wolopts; + __rtl8169_set_wol(tp, tp->saved_wolopts); + + return 0; +} + +static void rtl8169_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct rtl_fw *rtl_fw = tp->rtl_fw; + + strscpy(info->driver, KBUILD_MODNAME); + strscpy(info->bus_info, pci_name(tp->pci_dev)); + BUILD_BUG_ON(sizeof(info->fw_version) < sizeof(rtl_fw->version)); + if (rtl_fw) + strscpy(info->fw_version, rtl_fw->version); +} + +static int rtl8169_get_regs_len(struct net_device *dev) +{ + return R8169_REGS_SIZE; +} + +static netdev_features_t rtl8169_fix_features(struct net_device *dev, + netdev_features_t features) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + if (dev->mtu > TD_MSS_MAX) + features &= ~NETIF_F_ALL_TSO; + + if (dev->mtu > ETH_DATA_LEN && + tp->mac_version > RTL_GIGA_MAC_VER_06) + features &= ~(NETIF_F_CSUM_MASK | NETIF_F_ALL_TSO); + + return features; +} + +static void rtl_set_rx_config_features(struct rtl8169_private *tp, + netdev_features_t features) +{ + u32 rx_config = RTL_R32(tp, RxConfig); + + if (features & NETIF_F_RXALL) + rx_config |= RX_CONFIG_ACCEPT_ERR_MASK; + else + rx_config &= ~RX_CONFIG_ACCEPT_ERR_MASK; + + if (rtl_is_8125(tp)) { + if (features & NETIF_F_HW_VLAN_CTAG_RX) + rx_config |= RX_VLAN_8125; + else + rx_config &= ~RX_VLAN_8125; + } + + RTL_W32(tp, RxConfig, rx_config); +} + +static int rtl8169_set_features(struct net_device *dev, + netdev_features_t features) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + rtl_set_rx_config_features(tp, features); + + if (features & NETIF_F_RXCSUM) + tp->cp_cmd |= RxChkSum; + else + tp->cp_cmd &= ~RxChkSum; + + if (!rtl_is_8125(tp)) { + if (features & NETIF_F_HW_VLAN_CTAG_RX) + tp->cp_cmd |= RxVlan; + else + tp->cp_cmd &= ~RxVlan; + } + + RTL_W16(tp, CPlusCmd, tp->cp_cmd); + rtl_pci_commit(tp); + + return 0; +} + +static inline u32 rtl8169_tx_vlan_tag(struct sk_buff *skb) +{ + return (skb_vlan_tag_present(skb)) ? + TxVlanTag | swab16(skb_vlan_tag_get(skb)) : 0x00; +} + +static void rtl8169_rx_vlan_tag(struct RxDesc *desc, struct sk_buff *skb) +{ + u32 opts2 = le32_to_cpu(desc->opts2); + + if (opts2 & RxVlanTag) + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), swab16(opts2 & 0xffff)); +} + +static void rtl8169_get_regs(struct net_device *dev, struct ethtool_regs *regs, + void *p) +{ + struct rtl8169_private *tp = netdev_priv(dev); + u32 __iomem *data = tp->mmio_addr; + u32 *dw = p; + int i; + + for (i = 0; i < R8169_REGS_SIZE; i += 4) + memcpy_fromio(dw++, data++, 4); +} + +static const char rtl8169_gstrings[][ETH_GSTRING_LEN] = { + "tx_packets", + "rx_packets", + "tx_errors", + "rx_errors", + "rx_missed", + "align_errors", + "tx_single_collisions", + "tx_multi_collisions", + "unicast", + "broadcast", + "multicast", + "tx_aborted", + "tx_underrun", +}; + +static int rtl8169_get_sset_count(struct net_device *dev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return ARRAY_SIZE(rtl8169_gstrings); + default: + return -EOPNOTSUPP; + } +} + +DECLARE_RTL_COND(rtl_counters_cond) +{ + return RTL_R32(tp, CounterAddrLow) & (CounterReset | CounterDump); +} + +static void rtl8169_do_counters(struct rtl8169_private *tp, u32 counter_cmd) +{ + u32 cmd = lower_32_bits(tp->counters_phys_addr); + + RTL_W32(tp, CounterAddrHigh, upper_32_bits(tp->counters_phys_addr)); + rtl_pci_commit(tp); + RTL_W32(tp, CounterAddrLow, cmd); + RTL_W32(tp, CounterAddrLow, cmd | counter_cmd); + + rtl_loop_wait_low(tp, &rtl_counters_cond, 10, 1000); +} + +static void rtl8169_update_counters(struct rtl8169_private *tp) +{ + u8 val = RTL_R8(tp, ChipCmd); + + /* + * Some chips are unable to dump tally counters when the receiver + * is disabled. If 0xff chip may be in a PCI power-save state. + */ + if (val & CmdRxEnb && val != 0xff) + rtl8169_do_counters(tp, CounterDump); +} + +static void rtl8169_init_counter_offsets(struct rtl8169_private *tp) +{ + struct rtl8169_counters *counters = tp->counters; + + /* + * rtl8169_init_counter_offsets is called from rtl_open. On chip + * versions prior to RTL_GIGA_MAC_VER_19 the tally counters are only + * reset by a power cycle, while the counter values collected by the + * driver are reset at every driver unload/load cycle. + * + * To make sure the HW values returned by @get_stats64 match the SW + * values, we collect the initial values at first open(*) and use them + * as offsets to normalize the values returned by @get_stats64. + * + * (*) We can't call rtl8169_init_counter_offsets from rtl_init_one + * for the reason stated in rtl8169_update_counters; CmdRxEnb is only + * set at open time by rtl_hw_start. + */ + + if (tp->tc_offset.inited) + return; + + if (tp->mac_version >= RTL_GIGA_MAC_VER_19) { + rtl8169_do_counters(tp, CounterReset); + } else { + rtl8169_update_counters(tp); + tp->tc_offset.tx_errors = counters->tx_errors; + tp->tc_offset.tx_multi_collision = counters->tx_multi_collision; + tp->tc_offset.tx_aborted = counters->tx_aborted; + tp->tc_offset.rx_missed = counters->rx_missed; + } + + tp->tc_offset.inited = true; +} + +static void rtl8169_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct rtl8169_counters *counters; + + counters = tp->counters; + rtl8169_update_counters(tp); + + data[0] = le64_to_cpu(counters->tx_packets); + data[1] = le64_to_cpu(counters->rx_packets); + data[2] = le64_to_cpu(counters->tx_errors); + data[3] = le32_to_cpu(counters->rx_errors); + data[4] = le16_to_cpu(counters->rx_missed); + data[5] = le16_to_cpu(counters->align_errors); + data[6] = le32_to_cpu(counters->tx_one_collision); + data[7] = le32_to_cpu(counters->tx_multi_collision); + data[8] = le64_to_cpu(counters->rx_unicast); + data[9] = le64_to_cpu(counters->rx_broadcast); + data[10] = le32_to_cpu(counters->rx_multicast); + data[11] = le16_to_cpu(counters->tx_aborted); + data[12] = le16_to_cpu(counters->tx_underrun); +} + +static void rtl8169_get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ + switch(stringset) { + case ETH_SS_STATS: + memcpy(data, rtl8169_gstrings, sizeof(rtl8169_gstrings)); + break; + } +} + +/* + * Interrupt coalescing + * + * > 1 - the availability of the IntrMitigate (0xe2) register through the + * > 8169, 8168 and 810x line of chipsets + * + * 8169, 8168, and 8136(810x) serial chipsets support it. + * + * > 2 - the Tx timer unit at gigabit speed + * + * The unit of the timer depends on both the speed and the setting of CPlusCmd + * (0xe0) bit 1 and bit 0. + * + * For 8169 + * bit[1:0] \ speed 1000M 100M 10M + * 0 0 320ns 2.56us 40.96us + * 0 1 2.56us 20.48us 327.7us + * 1 0 5.12us 40.96us 655.4us + * 1 1 10.24us 81.92us 1.31ms + * + * For the other + * bit[1:0] \ speed 1000M 100M 10M + * 0 0 5us 2.56us 40.96us + * 0 1 40us 20.48us 327.7us + * 1 0 80us 40.96us 655.4us + * 1 1 160us 81.92us 1.31ms + */ + +/* rx/tx scale factors for all CPlusCmd[0:1] cases */ +struct rtl_coalesce_info { + u32 speed; + u32 scale_nsecs[4]; +}; + +/* produce array with base delay *1, *8, *8*2, *8*2*2 */ +#define COALESCE_DELAY(d) { (d), 8 * (d), 16 * (d), 32 * (d) } + +static const struct rtl_coalesce_info rtl_coalesce_info_8169[] = { + { SPEED_1000, COALESCE_DELAY(320) }, + { SPEED_100, COALESCE_DELAY(2560) }, + { SPEED_10, COALESCE_DELAY(40960) }, + { 0 }, +}; + +static const struct rtl_coalesce_info rtl_coalesce_info_8168_8136[] = { + { SPEED_1000, COALESCE_DELAY(5000) }, + { SPEED_100, COALESCE_DELAY(2560) }, + { SPEED_10, COALESCE_DELAY(40960) }, + { 0 }, +}; +#undef COALESCE_DELAY + +/* get rx/tx scale vector corresponding to current speed */ +static const struct rtl_coalesce_info * +rtl_coalesce_info(struct rtl8169_private *tp) +{ + const struct rtl_coalesce_info *ci; + + if (tp->mac_version <= RTL_GIGA_MAC_VER_06) + ci = rtl_coalesce_info_8169; + else + ci = rtl_coalesce_info_8168_8136; + + /* if speed is unknown assume highest one */ + if (tp->phydev->speed == SPEED_UNKNOWN) + return ci; + + for (; ci->speed; ci++) { + if (tp->phydev->speed == ci->speed) + return ci; + } + + return ERR_PTR(-ELNRNG); +} + +static int rtl_get_coalesce(struct net_device *dev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) +{ + struct rtl8169_private *tp = netdev_priv(dev); + const struct rtl_coalesce_info *ci; + u32 scale, c_us, c_fr; + u16 intrmit; + + if (rtl_is_8125(tp)) + return -EOPNOTSUPP; + + memset(ec, 0, sizeof(*ec)); + + /* get rx/tx scale corresponding to current speed and CPlusCmd[0:1] */ + ci = rtl_coalesce_info(tp); + if (IS_ERR(ci)) + return PTR_ERR(ci); + + scale = ci->scale_nsecs[tp->cp_cmd & INTT_MASK]; + + intrmit = RTL_R16(tp, IntrMitigate); + + c_us = FIELD_GET(RTL_COALESCE_TX_USECS, intrmit); + ec->tx_coalesce_usecs = DIV_ROUND_UP(c_us * scale, 1000); + + c_fr = FIELD_GET(RTL_COALESCE_TX_FRAMES, intrmit); + /* ethtool_coalesce states usecs and max_frames must not both be 0 */ + ec->tx_max_coalesced_frames = (c_us || c_fr) ? c_fr * 4 : 1; + + c_us = FIELD_GET(RTL_COALESCE_RX_USECS, intrmit); + ec->rx_coalesce_usecs = DIV_ROUND_UP(c_us * scale, 1000); + + c_fr = FIELD_GET(RTL_COALESCE_RX_FRAMES, intrmit); + ec->rx_max_coalesced_frames = (c_us || c_fr) ? c_fr * 4 : 1; + + return 0; +} + +/* choose appropriate scale factor and CPlusCmd[0:1] for (speed, usec) */ +static int rtl_coalesce_choose_scale(struct rtl8169_private *tp, u32 usec, + u16 *cp01) +{ + const struct rtl_coalesce_info *ci; + u16 i; + + ci = rtl_coalesce_info(tp); + if (IS_ERR(ci)) + return PTR_ERR(ci); + + for (i = 0; i < 4; i++) { + if (usec <= ci->scale_nsecs[i] * RTL_COALESCE_T_MAX / 1000U) { + *cp01 = i; + return ci->scale_nsecs[i]; + } + } + + return -ERANGE; +} + +static int rtl_set_coalesce(struct net_device *dev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) +{ + struct rtl8169_private *tp = netdev_priv(dev); + u32 tx_fr = ec->tx_max_coalesced_frames; + u32 rx_fr = ec->rx_max_coalesced_frames; + u32 coal_usec_max, units; + u16 w = 0, cp01 = 0; + int scale; + + if (rtl_is_8125(tp)) + return -EOPNOTSUPP; + + if (rx_fr > RTL_COALESCE_FRAME_MAX || tx_fr > RTL_COALESCE_FRAME_MAX) + return -ERANGE; + + coal_usec_max = max(ec->rx_coalesce_usecs, ec->tx_coalesce_usecs); + scale = rtl_coalesce_choose_scale(tp, coal_usec_max, &cp01); + if (scale < 0) + return scale; + + /* Accept max_frames=1 we returned in rtl_get_coalesce. Accept it + * not only when usecs=0 because of e.g. the following scenario: + * + * - both rx_usecs=0 & rx_frames=0 in hardware (no delay on RX) + * - rtl_get_coalesce returns rx_usecs=0, rx_frames=1 + * - then user does `ethtool -C eth0 rx-usecs 100` + * + * Since ethtool sends to kernel whole ethtool_coalesce settings, + * if we want to ignore rx_frames then it has to be set to 0. + */ + if (rx_fr == 1) + rx_fr = 0; + if (tx_fr == 1) + tx_fr = 0; + + /* HW requires time limit to be set if frame limit is set */ + if ((tx_fr && !ec->tx_coalesce_usecs) || + (rx_fr && !ec->rx_coalesce_usecs)) + return -EINVAL; + + w |= FIELD_PREP(RTL_COALESCE_TX_FRAMES, DIV_ROUND_UP(tx_fr, 4)); + w |= FIELD_PREP(RTL_COALESCE_RX_FRAMES, DIV_ROUND_UP(rx_fr, 4)); + + units = DIV_ROUND_UP(ec->tx_coalesce_usecs * 1000U, scale); + w |= FIELD_PREP(RTL_COALESCE_TX_USECS, units); + units = DIV_ROUND_UP(ec->rx_coalesce_usecs * 1000U, scale); + w |= FIELD_PREP(RTL_COALESCE_RX_USECS, units); + + RTL_W16(tp, IntrMitigate, w); + + /* Meaning of PktCntrDisable bit changed from RTL8168e-vl */ + if (rtl_is_8168evl_up(tp)) { + if (!rx_fr && !tx_fr) + /* disable packet counter */ + tp->cp_cmd |= PktCntrDisable; + else + tp->cp_cmd &= ~PktCntrDisable; + } + + tp->cp_cmd = (tp->cp_cmd & ~INTT_MASK) | cp01; + RTL_W16(tp, CPlusCmd, tp->cp_cmd); + rtl_pci_commit(tp); + + return 0; +} + +static void rtl_set_eee_txidle_timer(struct rtl8169_private *tp) +{ + unsigned int timer_val = READ_ONCE(tp->dev->mtu) + ETH_HLEN + 0x20; + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_46: + case RTL_GIGA_MAC_VER_48: + tp->tx_lpi_timer = timer_val; + r8168_mac_ocp_write(tp, 0xe048, timer_val); + break; + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_LAST: + tp->tx_lpi_timer = timer_val; + RTL_W16(tp, EEE_TXIDLE_TIMER_8125, timer_val); + break; + default: + break; + } +} + +static unsigned int r8169_get_tx_lpi_timer_us(struct rtl8169_private *tp) +{ + unsigned int speed = tp->phydev->speed; + unsigned int timer = tp->tx_lpi_timer; + + if (!timer || speed == SPEED_UNKNOWN) + return 0; + + /* tx_lpi_timer value is in bytes */ + return DIV_ROUND_CLOSEST(timer * BITS_PER_BYTE, speed); +} + +static int rtl8169_get_eee(struct net_device *dev, struct ethtool_keee *data) +{ + struct rtl8169_private *tp = netdev_priv(dev); + int ret; + + if (!rtl_supports_eee(tp)) + return -EOPNOTSUPP; + + ret = phy_ethtool_get_eee(tp->phydev, data); + if (ret) + return ret; + + data->tx_lpi_timer = r8169_get_tx_lpi_timer_us(tp); + + return 0; +} + +static int rtl8169_set_eee(struct net_device *dev, struct ethtool_keee *data) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + if (!rtl_supports_eee(tp)) + return -EOPNOTSUPP; + + return phy_ethtool_set_eee(tp->phydev, data); +} + +static void rtl8169_get_ringparam(struct net_device *dev, + struct ethtool_ringparam *data, + struct kernel_ethtool_ringparam *kernel_data, + struct netlink_ext_ack *extack) +{ + data->rx_max_pending = NUM_RX_DESC; + data->rx_pending = NUM_RX_DESC; + data->tx_max_pending = NUM_TX_DESC; + data->tx_pending = NUM_TX_DESC; +} + +static void rtl8169_get_pause_stats(struct net_device *dev, + struct ethtool_pause_stats *pause_stats) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + if (!rtl_is_8125(tp)) + return; + + rtl8169_update_counters(tp); + pause_stats->tx_pause_frames = le32_to_cpu(tp->counters->tx_pause_on); + pause_stats->rx_pause_frames = le32_to_cpu(tp->counters->rx_pause_on); +} + +static void rtl8169_get_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *data) +{ + struct rtl8169_private *tp = netdev_priv(dev); + bool tx_pause, rx_pause; + + phy_get_pause(tp->phydev, &tx_pause, &rx_pause); + + data->autoneg = tp->phydev->autoneg; + data->tx_pause = tx_pause ? 1 : 0; + data->rx_pause = rx_pause ? 1 : 0; +} + +static int rtl8169_set_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *data) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + if (dev->mtu > ETH_DATA_LEN) + return -EOPNOTSUPP; + + phy_set_asym_pause(tp->phydev, data->rx_pause, data->tx_pause); + + return 0; +} + +static void rtl8169_get_eth_mac_stats(struct net_device *dev, + struct ethtool_eth_mac_stats *mac_stats) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + rtl8169_update_counters(tp); + + mac_stats->FramesTransmittedOK = + le64_to_cpu(tp->counters->tx_packets); + mac_stats->SingleCollisionFrames = + le32_to_cpu(tp->counters->tx_one_collision); + mac_stats->MultipleCollisionFrames = + le32_to_cpu(tp->counters->tx_multi_collision); + mac_stats->FramesReceivedOK = + le64_to_cpu(tp->counters->rx_packets); + mac_stats->AlignmentErrors = + le16_to_cpu(tp->counters->align_errors); + mac_stats->FramesLostDueToIntMACXmitError = + le64_to_cpu(tp->counters->tx_errors); + mac_stats->BroadcastFramesReceivedOK = + le64_to_cpu(tp->counters->rx_broadcast); + mac_stats->MulticastFramesReceivedOK = + le32_to_cpu(tp->counters->rx_multicast); + + if (!rtl_is_8125(tp)) + return; + + mac_stats->AlignmentErrors = + le32_to_cpu(tp->counters->align_errors32); + mac_stats->OctetsTransmittedOK = + le64_to_cpu(tp->counters->tx_octets); + mac_stats->LateCollisions = + le32_to_cpu(tp->counters->tx_late_collision); + mac_stats->FramesAbortedDueToXSColls = + le32_to_cpu(tp->counters->tx_aborted32); + mac_stats->OctetsReceivedOK = + le64_to_cpu(tp->counters->rx_octets); + mac_stats->FramesLostDueToIntMACRcvError = + le32_to_cpu(tp->counters->rx_mac_error); + mac_stats->MulticastFramesXmittedOK = + le64_to_cpu(tp->counters->tx_multicast64); + mac_stats->BroadcastFramesXmittedOK = + le64_to_cpu(tp->counters->tx_broadcast64); + mac_stats->MulticastFramesReceivedOK = + le64_to_cpu(tp->counters->rx_multicast64); + mac_stats->FrameTooLongErrors = + le32_to_cpu(tp->counters->rx_frame_too_long); +} + +static void rtl8169_get_eth_ctrl_stats(struct net_device *dev, + struct ethtool_eth_ctrl_stats *ctrl_stats) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + if (!rtl_is_8125(tp)) + return; + + rtl8169_update_counters(tp); + + ctrl_stats->UnsupportedOpcodesReceived = + le32_to_cpu(tp->counters->rx_unknown_opcode); +} + +static int rtl8169_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *cmd) +{ + struct rtl8169_private *tp = netdev_priv(ndev); + struct phy_device *phydev = tp->phydev; + int duplex = cmd->base.duplex; + int speed = cmd->base.speed; + + if (!tp->sfp_mode) + return phy_ethtool_ksettings_set(phydev, cmd); + + if (cmd->base.autoneg != AUTONEG_DISABLE) + return -EINVAL; + + if (!phy_check_valid(speed, duplex, phydev->supported)) + return -EINVAL; + + mutex_lock(&phydev->lock); + + phydev->autoneg = AUTONEG_DISABLE; + phydev->speed = speed; + phydev->duplex = duplex; + + rtl_sfp_init(tp); + + mutex_unlock(&phydev->lock); + + return 0; +} + +static const struct ethtool_ops rtl8169_ethtool_ops = { + .supported_coalesce_params = ETHTOOL_COALESCE_USECS | + ETHTOOL_COALESCE_MAX_FRAMES, + .get_drvinfo = rtl8169_get_drvinfo, + .get_regs_len = rtl8169_get_regs_len, + .get_link = ethtool_op_get_link, + .get_coalesce = rtl_get_coalesce, + .set_coalesce = rtl_set_coalesce, + .get_regs = rtl8169_get_regs, + .get_wol = rtl8169_get_wol, + .set_wol = rtl8169_set_wol, + .get_strings = rtl8169_get_strings, + .get_sset_count = rtl8169_get_sset_count, + .get_ethtool_stats = rtl8169_get_ethtool_stats, + .get_ts_info = ethtool_op_get_ts_info, + .nway_reset = phy_ethtool_nway_reset, + .get_eee = rtl8169_get_eee, + .set_eee = rtl8169_set_eee, + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = rtl8169_set_link_ksettings, + .get_ringparam = rtl8169_get_ringparam, + .get_pause_stats = rtl8169_get_pause_stats, + .get_pauseparam = rtl8169_get_pauseparam, + .set_pauseparam = rtl8169_set_pauseparam, + .get_eth_mac_stats = rtl8169_get_eth_mac_stats, + .get_eth_ctrl_stats = rtl8169_get_eth_ctrl_stats, +}; + +static const struct rtl_chip_info *rtl8169_get_chip_version(u32 xid, bool gmii) +{ + /* Chips combining a 1Gbps MAC with a 100Mbps PHY */ + static const struct rtl_chip_info rtl8106eus_info = { + .mac_version = RTL_GIGA_MAC_VER_43, + .name = "RTL8106eus", + .fw_name = FIRMWARE_8106E_2, + }; + static const struct rtl_chip_info rtl8107e_info = { + .mac_version = RTL_GIGA_MAC_VER_48, + .name = "RTL8107e", + .fw_name = FIRMWARE_8107E_2, + }; + const struct rtl_chip_info *p = rtl_chip_infos; + + while ((xid & p->mask) != p->val) + p++; + + if (p->mac_version == RTL_GIGA_MAC_VER_42 && !gmii) + return &rtl8106eus_info; + if (p->mac_version == RTL_GIGA_MAC_VER_46 && !gmii) + return &rtl8107e_info; + + return p; +} + +static const struct rtl_chip_info *rtl8169_get_extended_chip_version(u32 xid2) +{ + const struct rtl_chip_info *p = rtl_chip_infos_extended; + + while ((xid2 & p->mask) != p->val) + p++; + return p; +} + +static void rtl_release_firmware(struct rtl8169_private *tp) +{ + if (tp->rtl_fw) { + rtl_fw_release_firmware(tp->rtl_fw); + kfree(tp->rtl_fw); + tp->rtl_fw = NULL; + } +} + +void r8169_apply_firmware(struct rtl8169_private *tp) +{ + int val; + + /* TODO: release firmware if rtl_fw_write_firmware signals failure. */ + if (tp->rtl_fw) { + rtl_fw_write_firmware(tp, tp->rtl_fw); + /* At least one firmware doesn't reset tp->ocp_base. */ + tp->ocp_base = OCP_STD_PHY_BASE; + + /* PHY soft reset may still be in progress */ + phy_read_poll_timeout(tp->phydev, MII_BMCR, val, + !(val & BMCR_RESET), + 50000, 600000, true); + } +} + +static void rtl_rar_exgmac_set(struct rtl8169_private *tp, const u8 *addr) +{ + rtl_eri_write(tp, 0xe0, ERIAR_MASK_1111, get_unaligned_le32(addr)); + rtl_eri_write(tp, 0xe4, ERIAR_MASK_1111, get_unaligned_le16(addr + 4)); + rtl_eri_write(tp, 0xf0, ERIAR_MASK_1111, get_unaligned_le16(addr) << 16); + rtl_eri_write(tp, 0xf4, ERIAR_MASK_1111, get_unaligned_le32(addr + 2)); +} + +u16 rtl8168h_2_get_adc_bias_ioffset(struct rtl8169_private *tp) +{ + u16 data1, data2, ioffset; + + r8168_mac_ocp_write(tp, 0xdd02, 0x807d); + data1 = r8168_mac_ocp_read(tp, 0xdd02); + data2 = r8168_mac_ocp_read(tp, 0xdd00); + + ioffset = (data2 >> 1) & 0x7ff8; + ioffset |= data2 & 0x0007; + if (data1 & BIT(7)) + ioffset |= BIT(15); + + return ioffset; +} + +static void rtl_schedule_task(struct rtl8169_private *tp, enum rtl_flag flag) +{ + set_bit(flag, tp->wk.flags); + if (!schedule_work(&tp->wk.work)) + clear_bit(flag, tp->wk.flags); +} + +static void rtl8169_init_phy(struct rtl8169_private *tp) +{ + r8169_hw_phy_config(tp, tp->phydev, tp->mac_version); + + if (tp->mac_version <= RTL_GIGA_MAC_VER_06) { + pci_write_config_byte(tp->pci_dev, PCI_LATENCY_TIMER, 0x40); + pci_write_config_byte(tp->pci_dev, PCI_CACHE_LINE_SIZE, 0x08); + /* set undocumented MAC Reg C+CR Offset 0x82h */ + RTL_W8(tp, 0x82, 0x01); + } + + if (tp->mac_version == RTL_GIGA_MAC_VER_05 && + tp->pci_dev->subsystem_vendor == PCI_VENDOR_ID_GIGABYTE && + tp->pci_dev->subsystem_device == 0xe000) + phy_write_paged(tp->phydev, 0x0001, 0x10, 0xf01b); + + if (tp->sfp_mode) + rtl_sfp_init(tp); + + /* We may have called phy_speed_down before */ + phy_speed_up(tp->phydev); + + genphy_soft_reset(tp->phydev); +} + +static void rtl_rar_set(struct rtl8169_private *tp, const u8 *addr) +{ + rtl_unlock_config_regs(tp); + + RTL_W32(tp, MAC4, get_unaligned_le16(addr + 4)); + rtl_pci_commit(tp); + + RTL_W32(tp, MAC0, get_unaligned_le32(addr)); + rtl_pci_commit(tp); + + if (tp->mac_version == RTL_GIGA_MAC_VER_34) + rtl_rar_exgmac_set(tp, addr); + + rtl_lock_config_regs(tp); +} + +static int rtl_set_mac_address(struct net_device *dev, void *p) +{ + struct rtl8169_private *tp = netdev_priv(dev); + int ret; + + ret = eth_mac_addr(dev, p); + if (ret) + return ret; + + rtl_rar_set(tp, dev->dev_addr); + + return 0; +} + +static void rtl_init_rxcfg(struct rtl8169_private *tp) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_06: + case RTL_GIGA_MAC_VER_10 ... RTL_GIGA_MAC_VER_17: + RTL_W32(tp, RxConfig, RX_FIFO_THRESH | RX_DMA_BURST); + break; + case RTL_GIGA_MAC_VER_18 ... RTL_GIGA_MAC_VER_24: + case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_36: + case RTL_GIGA_MAC_VER_38: + RTL_W32(tp, RxConfig, RX128_INT_EN | RX_MULTI_EN | RX_DMA_BURST); + break; + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_52: + RTL_W32(tp, RxConfig, RX128_INT_EN | RX_MULTI_EN | RX_DMA_BURST | RX_EARLY_OFF); + break; + case RTL_GIGA_MAC_VER_61: + RTL_W32(tp, RxConfig, RX_FETCH_DFLT_8125 | RX_DMA_BURST); + break; + case RTL_GIGA_MAC_VER_63 ... RTL_GIGA_MAC_VER_LAST: + RTL_W32(tp, RxConfig, RX_FETCH_DFLT_8125 | RX_DMA_BURST | + RX_PAUSE_SLOT_ON); + break; + default: + RTL_W32(tp, RxConfig, RX128_INT_EN | RX_DMA_BURST); + break; + } +} + +static void rtl8169_init_ring_indexes(struct rtl8169_private *tp) +{ + tp->dirty_tx = tp->cur_tx = tp->cur_rx = 0; +} + +static void rtl_jumbo_config(struct rtl8169_private *tp) +{ + bool jumbo = tp->dev->mtu > ETH_DATA_LEN; + int readrq = 4096; + + if (jumbo && tp->mac_version >= RTL_GIGA_MAC_VER_17 && + tp->mac_version <= RTL_GIGA_MAC_VER_26) + readrq = 512; + + rtl_unlock_config_regs(tp); + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_17: + r8169_mod_reg8_cond(tp, Config4, BIT(0), jumbo); + break; + case RTL_GIGA_MAC_VER_18 ... RTL_GIGA_MAC_VER_26: + r8169_mod_reg8_cond(tp, Config3, Jumbo_En0, jumbo); + r8169_mod_reg8_cond(tp, Config4, Jumbo_En1, jumbo); + break; + case RTL_GIGA_MAC_VER_28: + r8169_mod_reg8_cond(tp, Config3, Jumbo_En0, jumbo); + break; + case RTL_GIGA_MAC_VER_31 ... RTL_GIGA_MAC_VER_33: + RTL_W8(tp, MaxTxPacketSize, jumbo ? 0x24 : 0x3f); + r8169_mod_reg8_cond(tp, Config3, Jumbo_En0, jumbo); + r8169_mod_reg8_cond(tp, Config4, BIT(0), jumbo); + break; + default: + break; + } + rtl_lock_config_regs(tp); + + if (pci_is_pcie(tp->pci_dev) && tp->supports_gmii) + pcie_set_readrq(tp->pci_dev, readrq); + + /* Chip doesn't support pause in jumbo mode */ + if (jumbo) { + linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT, + tp->phydev->advertising); + linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, + tp->phydev->advertising); + phy_start_aneg(tp->phydev); + } +} + +DECLARE_RTL_COND(rtl_chipcmd_cond) +{ + return RTL_R8(tp, ChipCmd) & CmdReset; +} + +static void rtl_hw_reset(struct rtl8169_private *tp) +{ + RTL_W8(tp, ChipCmd, CmdReset); + + rtl_loop_wait_low(tp, &rtl_chipcmd_cond, 100, 100); +} + +static void rtl_request_firmware(struct rtl8169_private *tp) +{ + struct rtl_fw *rtl_fw; + + /* firmware loaded already or no firmware available */ + if (tp->rtl_fw || !tp->fw_name) + return; + + rtl_fw = kzalloc_obj(*rtl_fw); + if (!rtl_fw) + return; + + rtl_fw->phy_write = rtl_writephy; + rtl_fw->phy_read = rtl_readphy; + rtl_fw->mac_mcu_write = mac_mcu_write; + rtl_fw->mac_mcu_read = mac_mcu_read; + rtl_fw->fw_name = tp->fw_name; + rtl_fw->dev = tp_to_dev(tp); + + if (rtl_fw_request_firmware(rtl_fw)) + kfree(rtl_fw); + else + tp->rtl_fw = rtl_fw; +} + +static void rtl_rx_close(struct rtl8169_private *tp) +{ + RTL_W32(tp, RxConfig, RTL_R32(tp, RxConfig) & ~RX_CONFIG_ACCEPT_MASK); +} + +DECLARE_RTL_COND(rtl_npq_cond) +{ + return RTL_R8(tp, TxPoll) & NPQ; +} + +DECLARE_RTL_COND(rtl_txcfg_empty_cond) +{ + return RTL_R32(tp, TxConfig) & TXCFG_EMPTY; +} + +DECLARE_RTL_COND(rtl_rxtx_empty_cond) +{ + return (RTL_R8(tp, MCU) & RXTX_EMPTY) == RXTX_EMPTY; +} + +DECLARE_RTL_COND(rtl_rxtx_empty_cond_2) +{ + /* IntrMitigate has new functionality on RTL8125 */ + return (RTL_R16(tp, IntrMitigate) & 0x0103) == 0x0103; +} + +static void rtl_wait_txrx_fifo_empty(struct rtl8169_private *tp) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_52: + rtl_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 42); + rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42); + break; + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_61: + rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42); + break; + case RTL_GIGA_MAC_VER_63 ... RTL_GIGA_MAC_VER_LAST: + RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq); + rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42); + rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond_2, 100, 42); + break; + default: + break; + } +} + +static void rtl_disable_rxdvgate(struct rtl8169_private *tp) +{ + RTL_W32(tp, MISC, RTL_R32(tp, MISC) & ~RXDV_GATED_EN); +} + +static void rtl_enable_rxdvgate(struct rtl8169_private *tp) +{ + RTL_W32(tp, MISC, RTL_R32(tp, MISC) | RXDV_GATED_EN); + fsleep(2000); + rtl_wait_txrx_fifo_empty(tp); +} + +static void rtl_wol_enable_rx(struct rtl8169_private *tp) +{ + if (tp->mac_version >= RTL_GIGA_MAC_VER_25) + RTL_W32(tp, RxConfig, RTL_R32(tp, RxConfig) | + AcceptBroadcast | AcceptMulticast | AcceptMyPhys); + + if (tp->mac_version >= RTL_GIGA_MAC_VER_40) + rtl_disable_rxdvgate(tp); +} + +static void rtl_prepare_power_down(struct rtl8169_private *tp) +{ + if (tp->mac_version == RTL_GIGA_MAC_VER_32 || + tp->mac_version == RTL_GIGA_MAC_VER_33) + rtl_ephy_write(tp, 0x19, 0xff64); + + if (device_may_wakeup(tp_to_dev(tp))) { + phy_speed_down(tp->phydev, false); + rtl_wol_enable_rx(tp); + } +} + +static void rtl_set_tx_config_registers(struct rtl8169_private *tp) +{ + u32 val = TX_DMA_BURST << TxDMAShift | + InterFrameGap << TxInterFrameGapShift; + + if (rtl_is_8168evl_up(tp)) + val |= TXCFG_AUTO_FIFO; + + RTL_W32(tp, TxConfig, val); +} + +static void rtl_set_rx_max_size(struct rtl8169_private *tp) +{ + /* Low hurts. Let's disable the filtering. */ + RTL_W16(tp, RxMaxSize, R8169_RX_BUF_SIZE + 1); +} + +static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp) +{ + /* + * Magic spell: some iop3xx ARM board needs the TxDescAddrHigh + * register to be written before TxDescAddrLow to work. + * Switching from MMIO to I/O access fixes the issue as well. + */ + RTL_W32(tp, TxDescStartAddrHigh, ((u64) tp->TxPhyAddr) >> 32); + RTL_W32(tp, TxDescStartAddrLow, ((u64) tp->TxPhyAddr) & DMA_BIT_MASK(32)); + RTL_W32(tp, RxDescAddrHigh, ((u64) tp->RxPhyAddr) >> 32); + RTL_W32(tp, RxDescAddrLow, ((u64) tp->RxPhyAddr) & DMA_BIT_MASK(32)); +} + +static void rtl8169_set_magic_reg(struct rtl8169_private *tp) +{ + u32 val; + + if (tp->mac_version == RTL_GIGA_MAC_VER_05) + val = 0x000fff00; + else if (tp->mac_version == RTL_GIGA_MAC_VER_06) + val = 0x00ffff00; + else + return; + + if (RTL_R8(tp, Config2) & PCI_Clock_66MHz) + val |= 0xff; + + RTL_W32(tp, 0x7c, val); +} + +static void rtl_set_rx_mode(struct net_device *dev) +{ + u32 rx_mode = AcceptBroadcast | AcceptMyPhys | AcceptMulticast; + /* Multicast hash filter */ + u32 mc_filter[2] = { 0xffffffff, 0xffffffff }; + struct rtl8169_private *tp = netdev_priv(dev); + u32 tmp; + + if (dev->flags & IFF_PROMISC) { + rx_mode |= AcceptAllPhys; + } else if (!(dev->flags & IFF_MULTICAST)) { + rx_mode &= ~AcceptMulticast; + } else if (dev->flags & IFF_ALLMULTI || + tp->mac_version == RTL_GIGA_MAC_VER_35) { + /* accept all multicasts */ + } else if (netdev_mc_empty(dev)) { + rx_mode &= ~AcceptMulticast; + } else { + struct netdev_hw_addr *ha; + + mc_filter[1] = mc_filter[0] = 0; + netdev_for_each_mc_addr(ha, dev) { + u32 bit_nr = eth_hw_addr_crc(ha) >> 26; + mc_filter[bit_nr >> 5] |= BIT(bit_nr & 31); + } + + if (tp->mac_version > RTL_GIGA_MAC_VER_06) { + tmp = mc_filter[0]; + mc_filter[0] = swab32(mc_filter[1]); + mc_filter[1] = swab32(tmp); + } + } + + RTL_W32(tp, MAR0 + 4, mc_filter[1]); + RTL_W32(tp, MAR0 + 0, mc_filter[0]); + + tmp = RTL_R32(tp, RxConfig); + RTL_W32(tp, RxConfig, (tmp & ~RX_CONFIG_ACCEPT_OK_MASK) | rx_mode); +} + +DECLARE_RTL_COND(rtl_csiar_cond) +{ + return RTL_R32(tp, CSIAR) & CSIAR_FLAG; +} + +static void rtl_csi_write(struct rtl8169_private *tp, int addr, int value) +{ + u32 func = PCI_FUNC(tp->pci_dev->devfn); + + RTL_W32(tp, CSIDR, value); + RTL_W32(tp, CSIAR, CSIAR_WRITE_CMD | (addr & CSIAR_ADDR_MASK) | + CSIAR_BYTE_ENABLE | func << 16); + + rtl_loop_wait_low(tp, &rtl_csiar_cond, 10, 100); +} + +static u32 rtl_csi_read(struct rtl8169_private *tp, int addr) +{ + u32 func = PCI_FUNC(tp->pci_dev->devfn); + + RTL_W32(tp, CSIAR, (addr & CSIAR_ADDR_MASK) | func << 16 | + CSIAR_BYTE_ENABLE); + + return rtl_loop_wait_high(tp, &rtl_csiar_cond, 10, 100) ? + RTL_R32(tp, CSIDR) : ~0; +} + +static void rtl_csi_mod(struct rtl8169_private *tp, int addr, + u32 mask, u32 set) +{ + u32 val; + + WARN(addr % 4, "Invalid CSI address %#x\n", addr); + + netdev_notice_once(tp->dev, + "No native access to PCI extended config space, falling back to CSI\n"); + + val = rtl_csi_read(tp, addr); + rtl_csi_write(tp, addr, (val & ~mask) | set); +} + +static void rtl_disable_zrxdc_timeout(struct rtl8169_private *tp) +{ + struct pci_dev *pdev = tp->pci_dev; + int rc; + u8 val; + +#define RTL_GEN3_RELATED_OFF 0x0890 +#define RTL_GEN3_ZRXDC_NONCOMPL 0x1 + if (pdev->cfg_size > RTL_GEN3_RELATED_OFF) { + rc = pci_read_config_byte(pdev, RTL_GEN3_RELATED_OFF, &val); + if (rc == PCIBIOS_SUCCESSFUL) { + val &= ~RTL_GEN3_ZRXDC_NONCOMPL; + rc = pci_write_config_byte(pdev, RTL_GEN3_RELATED_OFF, + val); + if (rc == PCIBIOS_SUCCESSFUL) + return; + } + } + + rtl_csi_mod(tp, RTL_GEN3_RELATED_OFF, RTL_GEN3_ZRXDC_NONCOMPL, 0); +} + +static void rtl_set_aspm_entry_latency(struct rtl8169_private *tp, u8 val) +{ + struct pci_dev *pdev = tp->pci_dev; + + /* According to Realtek the value at config space address 0x070f + * controls the L0s/L1 entrance latency. We try standard ECAM access + * first and if it fails fall back to CSI. + * bit 0..2: L0: 0 = 1us, 1 = 2us .. 6 = 7us, 7 = 7us (no typo) + * bit 3..5: L1: 0 = 1us, 1 = 2us .. 6 = 64us, 7 = 64us + */ + if (pdev->cfg_size > 0x070f && + pci_write_config_byte(pdev, 0x070f, val) == PCIBIOS_SUCCESSFUL) + return; + + rtl_csi_mod(tp, 0x070c, 0xff000000, val << 24); +} + +static void rtl_set_def_aspm_entry_latency(struct rtl8169_private *tp) +{ + /* L0 7us, L1 16us */ + rtl_set_aspm_entry_latency(tp, 0x27); +} + +struct ephy_info { + unsigned int offset; + u16 mask; + u16 bits; +}; + +static void __rtl_ephy_init(struct rtl8169_private *tp, + const struct ephy_info *e, int len) +{ + u16 w; + + while (len-- > 0) { + w = (rtl_ephy_read(tp, e->offset) & ~e->mask) | e->bits; + rtl_ephy_write(tp, e->offset, w); + e++; + } +} + +#define rtl_ephy_init(tp, a) __rtl_ephy_init(tp, a, ARRAY_SIZE(a)) + +static void rtl_disable_clock_request(struct rtl8169_private *tp) +{ + pcie_capability_clear_word(tp->pci_dev, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_CLKREQ_EN); +} + +static void rtl_enable_clock_request(struct rtl8169_private *tp) +{ + pcie_capability_set_word(tp->pci_dev, PCI_EXP_LNKCTL, + PCI_EXP_LNKCTL_CLKREQ_EN); +} + +static void rtl_pcie_state_l2l3_disable(struct rtl8169_private *tp) +{ + /* work around an issue when PCI reset occurs during L2/L3 state */ + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Rdy_to_L23); +} + +static void rtl_enable_exit_l1(struct rtl8169_private *tp) +{ + /* Bits control which events trigger ASPM L1 exit: + * Bit 12: rxdv + * Bit 11: ltr_msg + * Bit 10: txdma_poll + * Bit 9: xadm + * Bit 8: pktavi + * Bit 7: txpla + */ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_36: + rtl_eri_set_bits(tp, 0xd4, 0x1f00); + break; + case RTL_GIGA_MAC_VER_37 ... RTL_GIGA_MAC_VER_38: + rtl_eri_set_bits(tp, 0xd4, 0x0c00); + break; + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_LAST: + r8168_mac_ocp_modify(tp, 0xc0ac, 0, 0x1f80); + break; + default: + break; + } +} + +static void rtl_disable_exit_l1(struct rtl8169_private *tp) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_38: + rtl_eri_clear_bits(tp, 0xd4, 0x1f00); + break; + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_LAST: + r8168_mac_ocp_modify(tp, 0xc0ac, 0x1f80, 0); + break; + default: + break; + } +} + +static void rtl_enable_ltr(struct rtl8169_private *tp) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_80: + r8168_mac_ocp_write(tp, 0xcdd0, 0x9003); + r8168_mac_ocp_modify(tp, LTR_SNOOP, 0x0000, LTR_SNOOP_EN); + r8168_mac_ocp_modify(tp, ALDPS_LTR, 0x0000, ALDPS_LTR_EN); + r8168_mac_ocp_write(tp, 0xcdd2, 0x8c09); + r8168_mac_ocp_write(tp, 0xcdd8, 0x9003); + r8168_mac_ocp_write(tp, 0xcdd4, 0x9003); + r8168_mac_ocp_write(tp, 0xcdda, 0x9003); + r8168_mac_ocp_write(tp, 0xcdd6, 0x9003); + r8168_mac_ocp_write(tp, 0xcddc, 0x9003); + r8168_mac_ocp_write(tp, 0xcde8, 0x887a); + r8168_mac_ocp_write(tp, 0xcdea, 0x9003); + r8168_mac_ocp_write(tp, 0xcdec, 0x8c09); + r8168_mac_ocp_write(tp, 0xcdee, 0x9003); + r8168_mac_ocp_write(tp, 0xcdf0, 0x8a62); + r8168_mac_ocp_write(tp, 0xcdf2, 0x9003); + r8168_mac_ocp_write(tp, 0xcdf4, 0x883e); + r8168_mac_ocp_write(tp, 0xcdf6, 0x9003); + r8168_mac_ocp_write(tp, 0xcdf8, 0x8849); + r8168_mac_ocp_write(tp, 0xcdfa, 0x9003); + r8168_mac_ocp_modify(tp, LTR_OBFF_LOCK, 0x0000, LINK_SPEED_CHANGE_EN); + break; + case RTL_GIGA_MAC_VER_70: + r8168_mac_ocp_write(tp, 0xcdd0, 0x9003); + r8168_mac_ocp_modify(tp, LTR_SNOOP, 0x0000, LTR_SNOOP_EN); + r8168_mac_ocp_modify(tp, ALDPS_LTR, 0x0000, ALDPS_LTR_EN); + r8168_mac_ocp_write(tp, 0xcdd2, 0x8c09); + r8168_mac_ocp_write(tp, 0xcdd8, 0x9003); + r8168_mac_ocp_write(tp, 0xcdd4, 0x9003); + r8168_mac_ocp_write(tp, 0xcdda, 0x9003); + r8168_mac_ocp_write(tp, 0xcdd6, 0x9003); + r8168_mac_ocp_write(tp, 0xcddc, 0x9003); + r8168_mac_ocp_write(tp, 0xcde8, 0x887a); + r8168_mac_ocp_write(tp, 0xcdea, 0x9003); + r8168_mac_ocp_write(tp, 0xcdec, 0x8c09); + r8168_mac_ocp_write(tp, 0xcdee, 0x9003); + r8168_mac_ocp_write(tp, 0xcdf0, 0x8a62); + r8168_mac_ocp_write(tp, 0xcdf2, 0x9003); + r8168_mac_ocp_write(tp, 0xcdf4, 0x883e); + r8168_mac_ocp_write(tp, 0xcdf6, 0x9003); + r8168_mac_ocp_modify(tp, LTR_OBFF_LOCK, 0x0000, LINK_SPEED_CHANGE_EN); + break; + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_66: + r8168_mac_ocp_write(tp, 0xcdd0, 0x9003); + r8168_mac_ocp_modify(tp, LTR_SNOOP, 0x0000, LTR_SNOOP_EN); + r8168_mac_ocp_modify(tp, ALDPS_LTR, 0x0000, ALDPS_LTR_EN); + r8168_mac_ocp_write(tp, 0xcdd2, 0x889c); + r8168_mac_ocp_write(tp, 0xcdd8, 0x9003); + r8168_mac_ocp_write(tp, 0xcdd4, 0x8c30); + r8168_mac_ocp_write(tp, 0xcdda, 0x9003); + r8168_mac_ocp_write(tp, 0xcdd6, 0x9003); + r8168_mac_ocp_write(tp, 0xcddc, 0x9003); + r8168_mac_ocp_write(tp, 0xcde8, 0x883e); + r8168_mac_ocp_write(tp, 0xcdea, 0x9003); + r8168_mac_ocp_write(tp, 0xcdec, 0x889c); + r8168_mac_ocp_write(tp, 0xcdee, 0x9003); + r8168_mac_ocp_write(tp, 0xcdf0, 0x8C09); + r8168_mac_ocp_write(tp, 0xcdf2, 0x9003); + r8168_mac_ocp_modify(tp, LTR_OBFF_LOCK, 0x0000, LINK_SPEED_CHANGE_EN); + break; + case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48: + case RTL_GIGA_MAC_VER_52: + r8168_mac_ocp_modify(tp, ALDPS_LTR, 0x0000, ALDPS_LTR_EN); + RTL_W8(tp, COMBO_LTR_EXTEND, RTL_R8(tp, COMBO_LTR_EXTEND) | COMBO_LTR_EXTEND_EN); + fallthrough; + case RTL_GIGA_MAC_VER_51: + r8168_mac_ocp_modify(tp, LTR_SNOOP, 0x0000, LTR_SNOOP_EN); + r8168_mac_ocp_write(tp, 0xe02c, 0x1880); + r8168_mac_ocp_write(tp, 0xe02e, 0x4880); + r8168_mac_ocp_write(tp, 0xcdd8, 0x9003); + r8168_mac_ocp_write(tp, 0xcdda, 0x9003); + r8168_mac_ocp_write(tp, 0xcddc, 0x9003); + r8168_mac_ocp_write(tp, 0xcdd2, 0x883c); + r8168_mac_ocp_write(tp, 0xcdd4, 0x8c12); + r8168_mac_ocp_write(tp, 0xcdd6, 0x9003); + break; + default: + return; + } + /* chip can trigger LTR */ + r8168_mac_ocp_modify(tp, LTR_OBFF_LOCK, 0x0003, LTR_OBFF_LOCK_EN); +} + +static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable) +{ + u8 val8; + + if (tp->mac_version < RTL_GIGA_MAC_VER_32) + return; + + /* Don't enable ASPM in the chip if OS can't control ASPM */ + if (enable && tp->aspm_manageable) { + /* On these chip versions ASPM can even harm + * bus communication of other PCI devices. + */ + if (tp->mac_version == RTL_GIGA_MAC_VER_42 || + tp->mac_version == RTL_GIGA_MAC_VER_43) + return; + + rtl_mod_config5(tp, 0, ASPM_en); + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_70: + case RTL_GIGA_MAC_VER_80: + val8 = RTL_R8(tp, INT_CFG0_8125) | INT_CFG0_CLKREQEN; + RTL_W8(tp, INT_CFG0_8125, val8); + break; + default: + rtl_mod_config2(tp, 0, ClkReqEn); + break; + } + + rtl_enable_ltr(tp); + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48: + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_LAST: + /* reset ephy tx/rx disable timer */ + r8168_mac_ocp_modify(tp, 0xe094, 0xff00, 0); + /* chip can trigger L1.2 */ + r8168_mac_ocp_modify(tp, 0xe092, 0x00ff, BIT(2)); + break; + default: + break; + } + } else { + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48: + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_LAST: + r8168_mac_ocp_modify(tp, 0xe092, 0x00ff, 0); + break; + default: + break; + } + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_70: + case RTL_GIGA_MAC_VER_80: + val8 = RTL_R8(tp, INT_CFG0_8125) & ~INT_CFG0_CLKREQEN; + RTL_W8(tp, INT_CFG0_8125, val8); + break; + default: + rtl_mod_config2(tp, ClkReqEn, 0); + break; + } + rtl_mod_config5(tp, ASPM_en, 0); + } +} + +static void rtl_set_fifo_size(struct rtl8169_private *tp, u16 rx_stat, + u16 tx_stat, u16 rx_dyn, u16 tx_dyn) +{ + /* Usage of dynamic vs. static FIFO is controlled by bit + * TXCFG_AUTO_FIFO. Exact meaning of FIFO values isn't known. + */ + rtl_eri_write(tp, 0xc8, ERIAR_MASK_1111, (rx_stat << 16) | rx_dyn); + rtl_eri_write(tp, 0xe8, ERIAR_MASK_1111, (tx_stat << 16) | tx_dyn); +} + +static void rtl8168g_set_pause_thresholds(struct rtl8169_private *tp, + u8 low, u8 high) +{ + /* FIFO thresholds for pause flow control */ + rtl_eri_write(tp, 0xcc, ERIAR_MASK_0001, low); + rtl_eri_write(tp, 0xd0, ERIAR_MASK_0001, high); +} + +static void rtl_hw_start_8168b(struct rtl8169_private *tp) +{ + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); +} + +static void __rtl_hw_start_8168cp(struct rtl8169_private *tp) +{ + RTL_W8(tp, Config1, RTL_R8(tp, Config1) | Speed_down); + + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); + + rtl_disable_clock_request(tp); +} + +static void rtl_hw_start_8168cp_1(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168cp[] = { + { 0x01, 0, 0x0001 }, + { 0x02, 0x0800, 0x1000 }, + { 0x03, 0, 0x0042 }, + { 0x06, 0x0080, 0x0000 }, + { 0x07, 0, 0x2000 } + }; + + rtl_set_def_aspm_entry_latency(tp); + + rtl_ephy_init(tp, e_info_8168cp); + + __rtl_hw_start_8168cp(tp); +} + +static void rtl_hw_start_8168cp_2(struct rtl8169_private *tp) +{ + rtl_set_def_aspm_entry_latency(tp); + + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); +} + +static void rtl_hw_start_8168cp_3(struct rtl8169_private *tp) +{ + rtl_set_def_aspm_entry_latency(tp); + + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); + + /* Magic. */ + RTL_W8(tp, DBG_REG, 0x20); +} + +static void rtl_hw_start_8168c_1(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168c_1[] = { + { 0x02, 0x0800, 0x1000 }, + { 0x03, 0, 0x0002 }, + { 0x06, 0x0080, 0x0000 } + }; + + rtl_set_def_aspm_entry_latency(tp); + + RTL_W8(tp, DBG_REG, 0x06 | FIX_NAK_1 | FIX_NAK_2); + + rtl_ephy_init(tp, e_info_8168c_1); + + __rtl_hw_start_8168cp(tp); +} + +static void rtl_hw_start_8168c_2(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168c_2[] = { + { 0x01, 0, 0x0001 }, + { 0x03, 0x0400, 0x0020 } + }; + + rtl_set_def_aspm_entry_latency(tp); + + rtl_ephy_init(tp, e_info_8168c_2); + + __rtl_hw_start_8168cp(tp); +} + +static void rtl_hw_start_8168c_4(struct rtl8169_private *tp) +{ + rtl_set_def_aspm_entry_latency(tp); + + __rtl_hw_start_8168cp(tp); +} + +static void rtl_hw_start_8168d(struct rtl8169_private *tp) +{ + rtl_set_def_aspm_entry_latency(tp); + + rtl_disable_clock_request(tp); +} + +static void rtl_hw_start_8168d_4(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168d_4[] = { + { 0x0b, 0x0000, 0x0048 }, + { 0x19, 0x0020, 0x0050 }, + { 0x0c, 0x0100, 0x0020 }, + { 0x10, 0x0004, 0x0000 }, + }; + + rtl_set_def_aspm_entry_latency(tp); + + rtl_ephy_init(tp, e_info_8168d_4); + + rtl_enable_clock_request(tp); +} + +static void rtl_hw_start_8168e_1(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168e_1[] = { + { 0x00, 0x0200, 0x0100 }, + { 0x00, 0x0000, 0x0004 }, + { 0x06, 0x0002, 0x0001 }, + { 0x06, 0x0000, 0x0030 }, + { 0x07, 0x0000, 0x2000 }, + { 0x00, 0x0000, 0x0020 }, + { 0x03, 0x5800, 0x2000 }, + { 0x03, 0x0000, 0x0001 }, + { 0x01, 0x0800, 0x1000 }, + { 0x07, 0x0000, 0x4000 }, + { 0x1e, 0x0000, 0x2000 }, + { 0x19, 0xffff, 0xfe6c }, + { 0x0a, 0x0000, 0x0040 } + }; + + rtl_set_def_aspm_entry_latency(tp); + + rtl_ephy_init(tp, e_info_8168e_1); + + rtl_disable_clock_request(tp); + + /* Reset tx FIFO pointer */ + RTL_W32(tp, MISC, RTL_R32(tp, MISC) | TXPLA_RST); + RTL_W32(tp, MISC, RTL_R32(tp, MISC) & ~TXPLA_RST); + + rtl_mod_config5(tp, Spi_en, 0); +} + +static void rtl_hw_start_8168e_2(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168e_2[] = { + { 0x09, 0x0000, 0x0080 }, + { 0x19, 0x0000, 0x0224 }, + { 0x00, 0x0000, 0x0004 }, + { 0x0c, 0x3df0, 0x0200 }, + }; + + rtl_set_def_aspm_entry_latency(tp); + + rtl_ephy_init(tp, e_info_8168e_2); + + rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000); + rtl_eri_write(tp, 0xb8, ERIAR_MASK_1111, 0x0000); + rtl_set_fifo_size(tp, 0x10, 0x10, 0x02, 0x06); + rtl_eri_set_bits(tp, 0x1d0, BIT(1)); + rtl_reset_packet_filter(tp); + rtl_eri_set_bits(tp, 0x1b0, BIT(4)); + rtl_eri_write(tp, 0xcc, ERIAR_MASK_1111, 0x00000050); + rtl_eri_write(tp, 0xd0, ERIAR_MASK_1111, 0x07ff0060); + + rtl_disable_clock_request(tp); + + RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB); + + RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) | PFM_EN); + RTL_W32(tp, MISC, RTL_R32(tp, MISC) | PWM_EN); + rtl_mod_config5(tp, Spi_en, 0); +} + +static void rtl_hw_start_8168f(struct rtl8169_private *tp) +{ + rtl_set_def_aspm_entry_latency(tp); + + rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000); + rtl_eri_write(tp, 0xb8, ERIAR_MASK_1111, 0x0000); + rtl_set_fifo_size(tp, 0x10, 0x10, 0x02, 0x06); + rtl_reset_packet_filter(tp); + rtl_eri_set_bits(tp, 0x1b0, BIT(4)); + rtl_eri_set_bits(tp, 0x1d0, BIT(4) | BIT(1)); + rtl_eri_write(tp, 0xcc, ERIAR_MASK_1111, 0x00000050); + rtl_eri_write(tp, 0xd0, ERIAR_MASK_1111, 0x00000060); + + rtl_disable_clock_request(tp); + + RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB); + RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) | PFM_EN); + RTL_W32(tp, MISC, RTL_R32(tp, MISC) | PWM_EN); + rtl_mod_config5(tp, Spi_en, 0); +} + +static void rtl_hw_start_8168f_1(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168f_1[] = { + { 0x06, 0x00c0, 0x0020 }, + { 0x08, 0x0001, 0x0002 }, + { 0x09, 0x0000, 0x0080 }, + { 0x19, 0x0000, 0x0224 }, + { 0x00, 0x0000, 0x0008 }, + { 0x0c, 0x3df0, 0x0200 }, + }; + + rtl_hw_start_8168f(tp); + + rtl_ephy_init(tp, e_info_8168f_1); +} + +static void rtl_hw_start_8411(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168f_1[] = { + { 0x06, 0x00c0, 0x0020 }, + { 0x0f, 0xffff, 0x5200 }, + { 0x19, 0x0000, 0x0224 }, + { 0x00, 0x0000, 0x0008 }, + { 0x0c, 0x3df0, 0x0200 }, + }; + + rtl_hw_start_8168f(tp); + rtl_pcie_state_l2l3_disable(tp); + + rtl_ephy_init(tp, e_info_8168f_1); +} + +static void rtl_hw_start_8168g(struct rtl8169_private *tp) +{ + rtl_set_fifo_size(tp, 0x08, 0x10, 0x02, 0x06); + rtl8168g_set_pause_thresholds(tp, 0x38, 0x48); + + rtl_set_def_aspm_entry_latency(tp); + + rtl_reset_packet_filter(tp); + rtl_eri_write(tp, 0x2f8, ERIAR_MASK_0011, 0x1d8f); + + rtl_disable_rxdvgate(tp); + + rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000); + rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000); + + rtl_w0w1_eri(tp, 0x2fc, 0x01, 0x06); + rtl_eri_clear_bits(tp, 0x1b0, BIT(12)); + + rtl_pcie_state_l2l3_disable(tp); +} + +static void rtl_hw_start_8168g_1(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168g_1[] = { + { 0x00, 0x0008, 0x0000 }, + { 0x0c, 0x3ff0, 0x0820 }, + { 0x1e, 0x0000, 0x0001 }, + { 0x19, 0x8000, 0x0000 } + }; + + rtl_hw_start_8168g(tp); + rtl_ephy_init(tp, e_info_8168g_1); +} + +static void rtl_hw_start_8168g_2(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168g_2[] = { + { 0x00, 0x0008, 0x0000 }, + { 0x0c, 0x3ff0, 0x0820 }, + { 0x19, 0xffff, 0x7c00 }, + { 0x1e, 0xffff, 0x20eb }, + { 0x0d, 0xffff, 0x1666 }, + { 0x00, 0xffff, 0x10a3 }, + { 0x06, 0xffff, 0xf050 }, + { 0x04, 0x0000, 0x0010 }, + { 0x1d, 0x4000, 0x0000 }, + }; + + rtl_hw_start_8168g(tp); + rtl_ephy_init(tp, e_info_8168g_2); +} + +static void rtl8411b_fix_phy_down(struct rtl8169_private *tp) +{ + static const u16 fix_data[] = { +/* 0xf800 */ 0xe008, 0xe00a, 0xe00c, 0xe00e, 0xe027, 0xe04f, 0xe05e, 0xe065, +/* 0xf810 */ 0xc602, 0xbe00, 0x0000, 0xc502, 0xbd00, 0x074c, 0xc302, 0xbb00, +/* 0xf820 */ 0x080a, 0x6420, 0x48c2, 0x8c20, 0xc516, 0x64a4, 0x49c0, 0xf009, +/* 0xf830 */ 0x74a2, 0x8ca5, 0x74a0, 0xc50e, 0x9ca2, 0x1c11, 0x9ca0, 0xe006, +/* 0xf840 */ 0x74f8, 0x48c4, 0x8cf8, 0xc404, 0xbc00, 0xc403, 0xbc00, 0x0bf2, +/* 0xf850 */ 0x0c0a, 0xe434, 0xd3c0, 0x49d9, 0xf01f, 0xc526, 0x64a5, 0x1400, +/* 0xf860 */ 0xf007, 0x0c01, 0x8ca5, 0x1c15, 0xc51b, 0x9ca0, 0xe013, 0xc519, +/* 0xf870 */ 0x74a0, 0x48c4, 0x8ca0, 0xc516, 0x74a4, 0x48c8, 0x48ca, 0x9ca4, +/* 0xf880 */ 0xc512, 0x1b00, 0x9ba0, 0x1b1c, 0x483f, 0x9ba2, 0x1b04, 0xc508, +/* 0xf890 */ 0x9ba0, 0xc505, 0xbd00, 0xc502, 0xbd00, 0x0300, 0x051e, 0xe434, +/* 0xf8a0 */ 0xe018, 0xe092, 0xde20, 0xd3c0, 0xc50f, 0x76a4, 0x49e3, 0xf007, +/* 0xf8b0 */ 0x49c0, 0xf103, 0xc607, 0xbe00, 0xc606, 0xbe00, 0xc602, 0xbe00, +/* 0xf8c0 */ 0x0c4c, 0x0c28, 0x0c2c, 0xdc00, 0xc707, 0x1d00, 0x8de2, 0x48c1, +/* 0xf8d0 */ 0xc502, 0xbd00, 0x00aa, 0xe0c0, 0xc502, 0xbd00, 0x0132 + }; + unsigned long flags; + int i; + + raw_spin_lock_irqsave(&tp->mac_ocp_lock, flags); + for (i = 0; i < ARRAY_SIZE(fix_data); i++) + __r8168_mac_ocp_write(tp, 0xf800 + 2 * i, fix_data[i]); + raw_spin_unlock_irqrestore(&tp->mac_ocp_lock, flags); +} + +static void rtl_hw_start_8411_2(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8411_2[] = { + { 0x00, 0x0008, 0x0000 }, + { 0x0c, 0x37d0, 0x0820 }, + { 0x1e, 0x0000, 0x0001 }, + { 0x19, 0x8021, 0x0000 }, + { 0x1e, 0x0000, 0x2000 }, + { 0x0d, 0x0100, 0x0200 }, + { 0x00, 0x0000, 0x0080 }, + { 0x06, 0x0000, 0x0010 }, + { 0x04, 0x0000, 0x0010 }, + { 0x1d, 0x0000, 0x4000 }, + }; + + rtl_hw_start_8168g(tp); + + rtl_ephy_init(tp, e_info_8411_2); + + /* The following Realtek-provided magic fixes an issue with the RX unit + * getting confused after the PHY having been powered-down. + */ + r8168_mac_ocp_write(tp, 0xFC28, 0x0000); + r8168_mac_ocp_write(tp, 0xFC2A, 0x0000); + r8168_mac_ocp_write(tp, 0xFC2C, 0x0000); + r8168_mac_ocp_write(tp, 0xFC2E, 0x0000); + r8168_mac_ocp_write(tp, 0xFC30, 0x0000); + r8168_mac_ocp_write(tp, 0xFC32, 0x0000); + r8168_mac_ocp_write(tp, 0xFC34, 0x0000); + r8168_mac_ocp_write(tp, 0xFC36, 0x0000); + mdelay(3); + r8168_mac_ocp_write(tp, 0xFC26, 0x0000); + + rtl8411b_fix_phy_down(tp); + + r8168_mac_ocp_write(tp, 0xFC26, 0x8000); + + r8168_mac_ocp_write(tp, 0xFC2A, 0x0743); + r8168_mac_ocp_write(tp, 0xFC2C, 0x0801); + r8168_mac_ocp_write(tp, 0xFC2E, 0x0BE9); + r8168_mac_ocp_write(tp, 0xFC30, 0x02FD); + r8168_mac_ocp_write(tp, 0xFC32, 0x0C25); + r8168_mac_ocp_write(tp, 0xFC34, 0x00A9); + r8168_mac_ocp_write(tp, 0xFC36, 0x012D); +} + +static void rtl_hw_start_8168h_1(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168h_1[] = { + { 0x1e, 0x0800, 0x0001 }, + { 0x1d, 0x0000, 0x0800 }, + { 0x05, 0xffff, 0x2089 }, + { 0x06, 0xffff, 0x5881 }, + { 0x04, 0xffff, 0x854a }, + { 0x01, 0xffff, 0x068b } + }; + int rg_saw_cnt; + + rtl_ephy_init(tp, e_info_8168h_1); + + rtl_set_fifo_size(tp, 0x08, 0x10, 0x02, 0x06); + rtl8168g_set_pause_thresholds(tp, 0x38, 0x48); + + rtl_set_def_aspm_entry_latency(tp); + + rtl_reset_packet_filter(tp); + + rtl_eri_set_bits(tp, 0xdc, 0x001c); + + rtl_eri_write(tp, 0x5f0, ERIAR_MASK_0011, 0x4f87); + + rtl_disable_rxdvgate(tp); + + rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000); + rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000); + + RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~PFM_EN); + RTL_W8(tp, MISC_1, RTL_R8(tp, MISC_1) & ~PFM_D3COLD_EN); + + RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~TX_10M_PS_EN); + + rtl_eri_clear_bits(tp, 0x1b0, BIT(12)); + + rtl_pcie_state_l2l3_disable(tp); + + rg_saw_cnt = phy_read_paged(tp->phydev, 0x0c42, 0x13) & 0x3fff; + if (rg_saw_cnt > 0) { + u16 sw_cnt_1ms_ini; + + sw_cnt_1ms_ini = 16000000/rg_saw_cnt; + sw_cnt_1ms_ini &= 0x0fff; + r8168_mac_ocp_modify(tp, 0xd412, 0x0fff, sw_cnt_1ms_ini); + } + + r8168_mac_ocp_modify(tp, 0xe056, 0x00f0, 0x0000); + r8168_mac_ocp_modify(tp, 0xe052, 0x6000, 0x8008); + r8168_mac_ocp_modify(tp, 0xe0d6, 0x01ff, 0x017f); + r8168_mac_ocp_modify(tp, 0xd420, 0x0fff, 0x047f); + + r8168_mac_ocp_write(tp, 0xe63e, 0x0001); + r8168_mac_ocp_write(tp, 0xe63e, 0x0000); + r8168_mac_ocp_write(tp, 0xc094, 0x0000); + r8168_mac_ocp_write(tp, 0xc09e, 0x0000); +} + +static void rtl_hw_start_8168ep(struct rtl8169_private *tp) +{ + rtl8168ep_stop_cmac(tp); + + rtl_set_fifo_size(tp, 0x08, 0x10, 0x02, 0x06); + rtl8168g_set_pause_thresholds(tp, 0x2f, 0x5f); + + rtl_set_def_aspm_entry_latency(tp); + + rtl_reset_packet_filter(tp); + + rtl_eri_write(tp, 0x5f0, ERIAR_MASK_0011, 0x4f87); + + rtl_disable_rxdvgate(tp); + + rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000); + rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000); + + rtl_w0w1_eri(tp, 0x2fc, 0x01, 0x06); + + RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~TX_10M_PS_EN); + + rtl_pcie_state_l2l3_disable(tp); +} + +static void rtl_hw_start_8168ep_3(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168ep_3[] = { + { 0x00, 0x0000, 0x0080 }, + { 0x0d, 0x0100, 0x0200 }, + { 0x19, 0x8021, 0x0000 }, + { 0x1e, 0x0000, 0x2000 }, + }; + + rtl_ephy_init(tp, e_info_8168ep_3); + + rtl_hw_start_8168ep(tp); + + RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~PFM_EN); + RTL_W8(tp, MISC_1, RTL_R8(tp, MISC_1) & ~PFM_D3COLD_EN); + + r8168_mac_ocp_modify(tp, 0xd3e2, 0x0fff, 0x0271); + r8168_mac_ocp_modify(tp, 0xd3e4, 0x00ff, 0x0000); + r8168_mac_ocp_modify(tp, 0xe860, 0x0000, 0x0080); +} + +static void rtl_hw_start_8117(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8117[] = { + { 0x19, 0x0040, 0x1100 }, + { 0x59, 0x0040, 0x1100 }, + }; + int rg_saw_cnt; + + rtl8168ep_stop_cmac(tp); + rtl_ephy_init(tp, e_info_8117); + + rtl_set_fifo_size(tp, 0x08, 0x10, 0x02, 0x06); + rtl8168g_set_pause_thresholds(tp, 0x2f, 0x5f); + + rtl_set_def_aspm_entry_latency(tp); + + rtl_reset_packet_filter(tp); + + rtl_eri_set_bits(tp, 0xd4, 0x0010); + + rtl_eri_write(tp, 0x5f0, ERIAR_MASK_0011, 0x4f87); + + rtl_disable_rxdvgate(tp); + + rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000); + rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000); + + RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~PFM_EN); + RTL_W8(tp, MISC_1, RTL_R8(tp, MISC_1) & ~PFM_D3COLD_EN); + + RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~TX_10M_PS_EN); + + rtl_eri_clear_bits(tp, 0x1b0, BIT(12)); + + rtl_pcie_state_l2l3_disable(tp); + + rg_saw_cnt = phy_read_paged(tp->phydev, 0x0c42, 0x13) & 0x3fff; + if (rg_saw_cnt > 0) { + u16 sw_cnt_1ms_ini; + + sw_cnt_1ms_ini = (16000000 / rg_saw_cnt) & 0x0fff; + r8168_mac_ocp_modify(tp, 0xd412, 0x0fff, sw_cnt_1ms_ini); + } + + r8168_mac_ocp_modify(tp, 0xe056, 0x00f0, 0x0000); + r8168_mac_ocp_write(tp, 0xea80, 0x0003); + r8168_mac_ocp_modify(tp, 0xe052, 0x0000, 0x0009); + r8168_mac_ocp_modify(tp, 0xd420, 0x0fff, 0x047f); + + r8168_mac_ocp_write(tp, 0xe63e, 0x0001); + r8168_mac_ocp_write(tp, 0xe63e, 0x0000); + r8168_mac_ocp_write(tp, 0xc094, 0x0000); + r8168_mac_ocp_write(tp, 0xc09e, 0x0000); + + /* firmware is for MAC only */ + r8169_apply_firmware(tp); +} + +static void rtl_hw_start_8102e_1(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8102e_1[] = { + { 0x01, 0, 0x6e65 }, + { 0x02, 0, 0x091f }, + { 0x03, 0, 0xc2f9 }, + { 0x06, 0, 0xafb5 }, + { 0x07, 0, 0x0e00 }, + { 0x19, 0, 0xec80 }, + { 0x01, 0, 0x2e65 }, + { 0x01, 0, 0x6e65 } + }; + u8 cfg1; + + rtl_set_def_aspm_entry_latency(tp); + + RTL_W8(tp, DBG_REG, FIX_NAK_1); + + RTL_W8(tp, Config1, + LEDS1 | LEDS0 | Speed_down | MEMMAP | IOMAP | VPD | PMEnable); + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); + + cfg1 = RTL_R8(tp, Config1); + if ((cfg1 & LEDS0) && (cfg1 & LEDS1)) + RTL_W8(tp, Config1, cfg1 & ~LEDS0); + + rtl_ephy_init(tp, e_info_8102e_1); +} + +static void rtl_hw_start_8102e_2(struct rtl8169_private *tp) +{ + rtl_set_def_aspm_entry_latency(tp); + + RTL_W8(tp, Config1, MEMMAP | IOMAP | VPD | PMEnable); + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); +} + +static void rtl_hw_start_8102e_3(struct rtl8169_private *tp) +{ + rtl_hw_start_8102e_2(tp); + + rtl_ephy_write(tp, 0x03, 0xc2f9); +} + +static void rtl_hw_start_8401(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8401[] = { + { 0x01, 0xffff, 0x6fe5 }, + { 0x03, 0xffff, 0x0599 }, + { 0x06, 0xffff, 0xaf25 }, + { 0x07, 0xffff, 0x8e68 }, + }; + + rtl_ephy_init(tp, e_info_8401); + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); +} + +static void rtl_hw_start_8105e_1(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8105e_1[] = { + { 0x07, 0, 0x4000 }, + { 0x19, 0, 0x0200 }, + { 0x19, 0, 0x0020 }, + { 0x1e, 0, 0x2000 }, + { 0x03, 0, 0x0001 }, + { 0x19, 0, 0x0100 }, + { 0x19, 0, 0x0004 }, + { 0x0a, 0, 0x0020 } + }; + + /* Force LAN exit from ASPM if Rx/Tx are not idle */ + RTL_W32(tp, FuncEvent, RTL_R32(tp, FuncEvent) | 0x002800); + + /* Disable Early Tally Counter */ + RTL_W32(tp, FuncEvent, RTL_R32(tp, FuncEvent) & ~0x010000); + + RTL_W8(tp, MCU, RTL_R8(tp, MCU) | EN_NDP | EN_OOB_RESET); + RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) | PFM_EN); + + rtl_ephy_init(tp, e_info_8105e_1); + + rtl_pcie_state_l2l3_disable(tp); +} + +static void rtl_hw_start_8105e_2(struct rtl8169_private *tp) +{ + rtl_hw_start_8105e_1(tp); + rtl_ephy_write(tp, 0x1e, rtl_ephy_read(tp, 0x1e) | 0x8000); +} + +static void rtl_hw_start_8402(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8402[] = { + { 0x19, 0xffff, 0xff64 }, + { 0x1e, 0, 0x4000 } + }; + + rtl_set_def_aspm_entry_latency(tp); + + /* Force LAN exit from ASPM if Rx/Tx are not idle */ + RTL_W32(tp, FuncEvent, RTL_R32(tp, FuncEvent) | 0x002800); + + RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB); + + rtl_ephy_init(tp, e_info_8402); + + rtl_set_fifo_size(tp, 0x00, 0x00, 0x02, 0x06); + rtl_reset_packet_filter(tp); + rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000); + rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000); + rtl_w0w1_eri(tp, 0x0d4, 0x0e00, 0xff00); + + /* disable EEE */ + rtl_eri_write(tp, 0x1b0, ERIAR_MASK_0011, 0x0000); + + rtl_pcie_state_l2l3_disable(tp); +} + +static void rtl_hw_start_8106(struct rtl8169_private *tp) +{ + /* Force LAN exit from ASPM if Rx/Tx are not idle */ + RTL_W32(tp, FuncEvent, RTL_R32(tp, FuncEvent) | 0x002800); + + RTL_W32(tp, MISC, (RTL_R32(tp, MISC) | DISABLE_LAN_EN) & ~EARLY_TALLY_EN); + RTL_W8(tp, MCU, RTL_R8(tp, MCU) | EN_NDP | EN_OOB_RESET); + RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~PFM_EN); + + /* L0 7us, L1 32us - needed to avoid issues with link-up detection */ + rtl_set_aspm_entry_latency(tp, 0x2f); + + rtl_eri_write(tp, 0x1d0, ERIAR_MASK_0011, 0x0000); + + /* disable EEE */ + rtl_eri_write(tp, 0x1b0, ERIAR_MASK_0011, 0x0000); + + rtl_pcie_state_l2l3_disable(tp); +} + +DECLARE_RTL_COND(rtl_mac_ocp_e00e_cond) +{ + return r8168_mac_ocp_read(tp, 0xe00e) & BIT(13); +} + +static void rtl_hw_start_8125_common(struct rtl8169_private *tp) +{ + rtl_pcie_state_l2l3_disable(tp); + + RTL_W16(tp, 0x382, 0x221b); + RTL_W32(tp, RSS_CTRL_8125, 0); + RTL_W16(tp, Q_NUM_CTRL_8125, 0); + + /* disable UPS */ + r8168_mac_ocp_modify(tp, 0xd40a, 0x0010, 0x0000); + + RTL_W8(tp, Config1, RTL_R8(tp, Config1) & ~0x10); + + r8168_mac_ocp_write(tp, 0xc140, 0xffff); + r8168_mac_ocp_write(tp, 0xc142, 0xffff); + + r8168_mac_ocp_modify(tp, 0xd3e2, 0x0fff, 0x03a9); + r8168_mac_ocp_modify(tp, 0xd3e4, 0x00ff, 0x0000); + r8168_mac_ocp_modify(tp, 0xe860, 0x0000, 0x0080); + + /* disable new tx descriptor format */ + r8168_mac_ocp_modify(tp, 0xeb58, 0x0001, 0x0000); + + if (tp->mac_version == RTL_GIGA_MAC_VER_70 || + tp->mac_version == RTL_GIGA_MAC_VER_80) + RTL_W8(tp, 0xD8, RTL_R8(tp, 0xD8) & ~0x02); + + if (tp->mac_version == RTL_GIGA_MAC_VER_80) + r8168_mac_ocp_modify(tp, 0xe614, 0x0f00, 0x0f00); + else if (tp->mac_version == RTL_GIGA_MAC_VER_70) + r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0400); + else if (tp->mac_version == RTL_GIGA_MAC_VER_63) + r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0200); + else + r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0300); + + if (tp->mac_version == RTL_GIGA_MAC_VER_63) + r8168_mac_ocp_modify(tp, 0xe63e, 0x0c30, 0x0000); + else + r8168_mac_ocp_modify(tp, 0xe63e, 0x0c30, 0x0020); + + r8168_mac_ocp_modify(tp, 0xc0b4, 0x0000, 0x000c); + r8168_mac_ocp_modify(tp, 0xeb6a, 0x00ff, 0x0033); + r8168_mac_ocp_modify(tp, 0xeb50, 0x03e0, 0x0040); + r8168_mac_ocp_modify(tp, 0xe056, 0x00f0, 0x0000); + r8168_mac_ocp_modify(tp, 0xe040, 0x1000, 0x0000); + r8168_mac_ocp_modify(tp, 0xea1c, 0x0003, 0x0001); + if (tp->mac_version == RTL_GIGA_MAC_VER_70 || + tp->mac_version == RTL_GIGA_MAC_VER_80) + r8168_mac_ocp_modify(tp, 0xea1c, 0x0300, 0x0000); + else + r8168_mac_ocp_modify(tp, 0xea1c, 0x0004, 0x0000); + r8168_mac_ocp_modify(tp, 0xe0c0, 0x4f0f, 0x4403); + r8168_mac_ocp_modify(tp, 0xe052, 0x0080, 0x0068); + r8168_mac_ocp_modify(tp, 0xd430, 0x0fff, 0x047f); + + r8168_mac_ocp_modify(tp, 0xea1c, 0x0004, 0x0000); + r8168_mac_ocp_modify(tp, 0xeb54, 0x0000, 0x0001); + udelay(1); + r8168_mac_ocp_modify(tp, 0xeb54, 0x0001, 0x0000); + RTL_W16(tp, 0x1880, RTL_R16(tp, 0x1880) & ~0x0030); + + r8168_mac_ocp_write(tp, 0xe098, 0xc302); + + rtl_loop_wait_low(tp, &rtl_mac_ocp_e00e_cond, 1000, 10); + + rtl_disable_rxdvgate(tp); +} + +static void rtl_hw_start_8125a_2(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8125a_2[] = { + { 0x04, 0xffff, 0xd000 }, + { 0x0a, 0xffff, 0x8653 }, + { 0x23, 0xffff, 0xab66 }, + { 0x20, 0xffff, 0x9455 }, + { 0x21, 0xffff, 0x99ff }, + { 0x29, 0xffff, 0xfe04 }, + + { 0x44, 0xffff, 0xd000 }, + { 0x4a, 0xffff, 0x8653 }, + { 0x63, 0xffff, 0xab66 }, + { 0x60, 0xffff, 0x9455 }, + { 0x61, 0xffff, 0x99ff }, + { 0x69, 0xffff, 0xfe04 }, + }; + + rtl_set_def_aspm_entry_latency(tp); + rtl_ephy_init(tp, e_info_8125a_2); + rtl_hw_start_8125_common(tp); +} + +static void rtl_hw_start_8125b(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8125b[] = { + { 0x0b, 0xffff, 0xa908 }, + { 0x1e, 0xffff, 0x20eb }, + { 0x4b, 0xffff, 0xa908 }, + { 0x5e, 0xffff, 0x20eb }, + { 0x22, 0x0030, 0x0020 }, + { 0x62, 0x0030, 0x0020 }, + }; + + rtl_set_def_aspm_entry_latency(tp); + rtl_ephy_init(tp, e_info_8125b); + rtl_hw_start_8125_common(tp); +} + +static void rtl_hw_start_8125d(struct rtl8169_private *tp) +{ + rtl_set_def_aspm_entry_latency(tp); + rtl_hw_start_8125_common(tp); +} + +static void rtl_hw_start_8126a(struct rtl8169_private *tp) +{ + rtl_disable_zrxdc_timeout(tp); + rtl_set_def_aspm_entry_latency(tp); + rtl_hw_start_8125_common(tp); +} + +static void rtl_hw_start_8127a(struct rtl8169_private *tp) +{ + rtl_set_def_aspm_entry_latency(tp); + rtl_hw_start_8125_common(tp); +} + +static void rtl_hw_config(struct rtl8169_private *tp) +{ + static const rtl_generic_fct hw_configs[] = { + [RTL_GIGA_MAC_VER_07] = rtl_hw_start_8102e_1, + [RTL_GIGA_MAC_VER_08] = rtl_hw_start_8102e_3, + [RTL_GIGA_MAC_VER_09] = rtl_hw_start_8102e_2, + [RTL_GIGA_MAC_VER_10] = NULL, + [RTL_GIGA_MAC_VER_14] = rtl_hw_start_8401, + [RTL_GIGA_MAC_VER_17] = rtl_hw_start_8168b, + [RTL_GIGA_MAC_VER_18] = rtl_hw_start_8168cp_1, + [RTL_GIGA_MAC_VER_19] = rtl_hw_start_8168c_1, + [RTL_GIGA_MAC_VER_20] = rtl_hw_start_8168c_2, + [RTL_GIGA_MAC_VER_21] = rtl_hw_start_8168c_2, + [RTL_GIGA_MAC_VER_22] = rtl_hw_start_8168c_4, + [RTL_GIGA_MAC_VER_23] = rtl_hw_start_8168cp_2, + [RTL_GIGA_MAC_VER_24] = rtl_hw_start_8168cp_3, + [RTL_GIGA_MAC_VER_25] = rtl_hw_start_8168d, + [RTL_GIGA_MAC_VER_26] = rtl_hw_start_8168d, + [RTL_GIGA_MAC_VER_28] = rtl_hw_start_8168d_4, + [RTL_GIGA_MAC_VER_29] = rtl_hw_start_8105e_1, + [RTL_GIGA_MAC_VER_30] = rtl_hw_start_8105e_2, + [RTL_GIGA_MAC_VER_31] = rtl_hw_start_8168d, + [RTL_GIGA_MAC_VER_32] = rtl_hw_start_8168e_1, + [RTL_GIGA_MAC_VER_33] = rtl_hw_start_8168e_1, + [RTL_GIGA_MAC_VER_34] = rtl_hw_start_8168e_2, + [RTL_GIGA_MAC_VER_35] = rtl_hw_start_8168f_1, + [RTL_GIGA_MAC_VER_36] = rtl_hw_start_8168f_1, + [RTL_GIGA_MAC_VER_37] = rtl_hw_start_8402, + [RTL_GIGA_MAC_VER_38] = rtl_hw_start_8411, + [RTL_GIGA_MAC_VER_39] = rtl_hw_start_8106, + [RTL_GIGA_MAC_VER_40] = rtl_hw_start_8168g_1, + [RTL_GIGA_MAC_VER_42] = rtl_hw_start_8168g_2, + [RTL_GIGA_MAC_VER_43] = rtl_hw_start_8168g_2, + [RTL_GIGA_MAC_VER_44] = rtl_hw_start_8411_2, + [RTL_GIGA_MAC_VER_46] = rtl_hw_start_8168h_1, + [RTL_GIGA_MAC_VER_48] = rtl_hw_start_8168h_1, + [RTL_GIGA_MAC_VER_51] = rtl_hw_start_8168ep_3, + [RTL_GIGA_MAC_VER_52] = rtl_hw_start_8117, + [RTL_GIGA_MAC_VER_61] = rtl_hw_start_8125a_2, + [RTL_GIGA_MAC_VER_63] = rtl_hw_start_8125b, + [RTL_GIGA_MAC_VER_64] = rtl_hw_start_8125d, + [RTL_GIGA_MAC_VER_65] = rtl_hw_start_8125d, + [RTL_GIGA_MAC_VER_66] = rtl_hw_start_8125d, + [RTL_GIGA_MAC_VER_70] = rtl_hw_start_8126a, + [RTL_GIGA_MAC_VER_80] = rtl_hw_start_8127a, + }; + + if (hw_configs[tp->mac_version]) + hw_configs[tp->mac_version](tp); +} + +static void rtl_hw_start_8125(struct rtl8169_private *tp) +{ + int i; + + RTL_W8(tp, INT_CFG0_8125, 0x00); + + /* disable interrupt coalescing */ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_61: + case RTL_GIGA_MAC_VER_64: + case RTL_GIGA_MAC_VER_65: + case RTL_GIGA_MAC_VER_66: + case RTL_GIGA_MAC_VER_80: + for (i = 0xa00; i < 0xb00; i += 4) + RTL_W32(tp, i, 0); + if (tp->mac_version == RTL_GIGA_MAC_VER_80) + RTL_W16(tp, INT_CFG1_8125, 0x0000); + break; + case RTL_GIGA_MAC_VER_63: + case RTL_GIGA_MAC_VER_70: + for (i = 0xa00; i < 0xa80; i += 4) + RTL_W32(tp, i, 0); + RTL_W16(tp, INT_CFG1_8125, 0x0000); + break; + default: + break; + } + + /* enable extended tally counter */ + r8168_mac_ocp_modify(tp, 0xea84, 0, BIT(1) | BIT(0)); + + rtl_hw_config(tp); +} + +static void rtl_hw_start_8168(struct rtl8169_private *tp) +{ + if (rtl_is_8168evl_up(tp)) + RTL_W8(tp, MaxTxPacketSize, EarlySize); + else + RTL_W8(tp, MaxTxPacketSize, TxPacketMax); + + rtl_hw_config(tp); + + /* disable interrupt coalescing */ + RTL_W16(tp, IntrMitigate, 0x0000); +} + +static void rtl_hw_start_8169(struct rtl8169_private *tp) +{ + RTL_W8(tp, EarlyTxThres, NoEarlyTx); + + tp->cp_cmd |= PCIMulRW; + + if (tp->mac_version == RTL_GIGA_MAC_VER_02 || + tp->mac_version == RTL_GIGA_MAC_VER_03) + tp->cp_cmd |= EnAnaPLL; + + RTL_W16(tp, CPlusCmd, tp->cp_cmd); + + rtl8169_set_magic_reg(tp); + + /* disable interrupt coalescing */ + RTL_W16(tp, IntrMitigate, 0x0000); +} + +static void rtl_hw_start(struct rtl8169_private *tp) +{ + rtl_unlock_config_regs(tp); + /* disable aspm and clock request before ephy access */ + rtl_hw_aspm_clkreq_enable(tp, false); + RTL_W16(tp, CPlusCmd, tp->cp_cmd); + + rtl_set_eee_txidle_timer(tp); + + if (tp->mac_version <= RTL_GIGA_MAC_VER_06) + rtl_hw_start_8169(tp); + else if (rtl_is_8125(tp)) + rtl_hw_start_8125(tp); + else + rtl_hw_start_8168(tp); + + rtl_enable_exit_l1(tp); + rtl_hw_aspm_clkreq_enable(tp, true); + rtl_set_rx_max_size(tp); + rtl_set_rx_tx_desc_registers(tp); + rtl_lock_config_regs(tp); + + rtl_jumbo_config(tp); + + /* Initially a 10 us delay. Turned it into a PCI commit. - FR */ + rtl_pci_commit(tp); + + RTL_W8(tp, ChipCmd, CmdTxEnb | CmdRxEnb); + rtl_init_rxcfg(tp); + rtl_set_tx_config_registers(tp); + rtl_set_rx_config_features(tp, tp->dev->features); + rtl_set_rx_mode(tp->dev); + rtl_irq_enable(tp); +} + +static int rtl8169_change_mtu(struct net_device *dev, int new_mtu) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + WRITE_ONCE(dev->mtu, new_mtu); + netdev_update_features(dev); + rtl_jumbo_config(tp); + rtl_set_eee_txidle_timer(tp); + + return 0; +} + +static void rtl8169_mark_to_asic(struct RxDesc *desc) +{ + u32 eor = le32_to_cpu(desc->opts1) & RingEnd; + + desc->opts2 = 0; + /* Force memory writes to complete before releasing descriptor */ + dma_wmb(); + WRITE_ONCE(desc->opts1, cpu_to_le32(DescOwn | eor | R8169_RX_BUF_SIZE)); +} + +static struct page *rtl8169_alloc_rx_data(struct rtl8169_private *tp, + struct RxDesc *desc) +{ + struct device *d = tp_to_dev(tp); + int node = dev_to_node(d); + dma_addr_t mapping; + struct page *data; + + data = alloc_pages_node(node, GFP_KERNEL, get_order(R8169_RX_BUF_SIZE)); + if (!data) + return NULL; + + mapping = dma_map_page(d, data, 0, R8169_RX_BUF_SIZE, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(d, mapping))) { + netdev_err(tp->dev, "Failed to map RX DMA!\n"); + __free_pages(data, get_order(R8169_RX_BUF_SIZE)); + return NULL; + } + + desc->addr = cpu_to_le64(mapping); + rtl8169_mark_to_asic(desc); + + return data; +} + +static void rtl8169_rx_clear(struct rtl8169_private *tp) +{ + int i; + + for (i = 0; i < NUM_RX_DESC && tp->Rx_databuff[i]; i++) { + dma_unmap_page(tp_to_dev(tp), + le64_to_cpu(tp->RxDescArray[i].addr), + R8169_RX_BUF_SIZE, DMA_FROM_DEVICE); + __free_pages(tp->Rx_databuff[i], get_order(R8169_RX_BUF_SIZE)); + tp->Rx_databuff[i] = NULL; + tp->RxDescArray[i].addr = 0; + tp->RxDescArray[i].opts1 = 0; + } +} + +static int rtl8169_rx_fill(struct rtl8169_private *tp) +{ + int i; + + for (i = 0; i < NUM_RX_DESC; i++) { + struct page *data; + + data = rtl8169_alloc_rx_data(tp, tp->RxDescArray + i); + if (!data) { + rtl8169_rx_clear(tp); + return -ENOMEM; + } + tp->Rx_databuff[i] = data; + } + + /* mark as last descriptor in the ring */ + tp->RxDescArray[NUM_RX_DESC - 1].opts1 |= cpu_to_le32(RingEnd); + + return 0; +} + +static int rtl8169_init_ring(struct rtl8169_private *tp) +{ + rtl8169_init_ring_indexes(tp); + + memset(tp->tx_skb, 0, sizeof(tp->tx_skb)); + memset(tp->Rx_databuff, 0, sizeof(tp->Rx_databuff)); + + return rtl8169_rx_fill(tp); +} + +static void rtl8169_unmap_tx_skb(struct rtl8169_private *tp, unsigned int entry) +{ + struct ring_info *tx_skb = tp->tx_skb + entry; + struct TxDesc *desc = tp->TxDescArray + entry; + + dma_unmap_single(tp_to_dev(tp), le64_to_cpu(desc->addr), tx_skb->len, + DMA_TO_DEVICE); + memset(desc, 0, sizeof(*desc)); + memset(tx_skb, 0, sizeof(*tx_skb)); +} + +static void rtl8169_tx_clear_range(struct rtl8169_private *tp, u32 start, + unsigned int n) +{ + unsigned int i; + + for (i = 0; i < n; i++) { + unsigned int entry = (start + i) % NUM_TX_DESC; + struct ring_info *tx_skb = tp->tx_skb + entry; + unsigned int len = tx_skb->len; + + if (len) { + struct sk_buff *skb = tx_skb->skb; + + rtl8169_unmap_tx_skb(tp, entry); + if (skb) + dev_consume_skb_any(skb); + } + } +} + +static void rtl8169_tx_clear(struct rtl8169_private *tp) +{ + rtl8169_tx_clear_range(tp, tp->dirty_tx, NUM_TX_DESC); + netdev_reset_queue(tp->dev); +} + +static void rtl8169_cleanup(struct rtl8169_private *tp) +{ + napi_disable(&tp->napi); + + /* Give a racing hard_start_xmit a few cycles to complete. */ + synchronize_net(); + + /* Disable interrupts */ + rtl8169_irq_mask_and_ack(tp); + + rtl_rx_close(tp); + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_28: + case RTL_GIGA_MAC_VER_31: + rtl_loop_wait_low(tp, &rtl_npq_cond, 20, 2000); + break; + case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_38: + RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq); + rtl_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 666); + break; + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_LAST: + rtl_enable_rxdvgate(tp); + fsleep(2000); + break; + default: + RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq); + fsleep(100); + break; + } + + rtl_hw_reset(tp); + + rtl8169_tx_clear(tp); + rtl8169_init_ring_indexes(tp); +} + +static void rtl_reset_work(struct rtl8169_private *tp) +{ + int i; + + netif_stop_queue(tp->dev); + + rtl8169_cleanup(tp); + + for (i = 0; i < NUM_RX_DESC; i++) + rtl8169_mark_to_asic(tp->RxDescArray + i); + + napi_enable(&tp->napi); + rtl_hw_start(tp); +} + +static void rtl8169_tx_timeout(struct net_device *dev, unsigned int txqueue) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + rtl_schedule_task(tp, RTL_FLAG_TASK_TX_TIMEOUT); +} + +static int rtl8169_tx_map(struct rtl8169_private *tp, const u32 *opts, u32 len, + void *addr, unsigned int entry, bool desc_own) +{ + struct TxDesc *txd = tp->TxDescArray + entry; + struct device *d = tp_to_dev(tp); + dma_addr_t mapping; + u32 opts1; + int ret; + + mapping = dma_map_single(d, addr, len, DMA_TO_DEVICE); + ret = dma_mapping_error(d, mapping); + if (unlikely(ret)) { + if (net_ratelimit()) + netdev_err(tp->dev, "Failed to map TX data!\n"); + return ret; + } + + txd->addr = cpu_to_le64(mapping); + txd->opts2 = cpu_to_le32(opts[1]); + + opts1 = opts[0] | len; + if (entry == NUM_TX_DESC - 1) + opts1 |= RingEnd; + if (desc_own) + opts1 |= DescOwn; + txd->opts1 = cpu_to_le32(opts1); + + tp->tx_skb[entry].len = len; + + return 0; +} + +static int rtl8169_xmit_frags(struct rtl8169_private *tp, struct sk_buff *skb, + const u32 *opts, unsigned int entry) +{ + struct skb_shared_info *info = skb_shinfo(skb); + unsigned int cur_frag; + + for (cur_frag = 0; cur_frag < info->nr_frags; cur_frag++) { + const skb_frag_t *frag = info->frags + cur_frag; + void *addr = skb_frag_address(frag); + u32 len = skb_frag_size(frag); + + entry = (entry + 1) % NUM_TX_DESC; + + if (unlikely(rtl8169_tx_map(tp, opts, len, addr, entry, true))) + goto err_out; + } + + return 0; + +err_out: + rtl8169_tx_clear_range(tp, tp->cur_tx + 1, cur_frag); + return -EIO; +} + +static bool rtl_skb_is_udp(struct sk_buff *skb) +{ + int no = skb_network_offset(skb); + struct ipv6hdr *i6h, _i6h; + struct iphdr *ih, _ih; + + switch (vlan_get_protocol(skb)) { + case htons(ETH_P_IP): + ih = skb_header_pointer(skb, no, sizeof(_ih), &_ih); + return ih && ih->protocol == IPPROTO_UDP; + case htons(ETH_P_IPV6): + i6h = skb_header_pointer(skb, no, sizeof(_i6h), &_i6h); + return i6h && i6h->nexthdr == IPPROTO_UDP; + default: + return false; + } +} + +#define RTL_MIN_PATCH_LEN 47 + +/* see rtl8125_get_patch_pad_len() in r8125 vendor driver */ +static unsigned int rtl8125_quirk_udp_padto(struct rtl8169_private *tp, + struct sk_buff *skb) +{ + unsigned int padto = 0, len = skb->len; + + if (len < 128 + RTL_MIN_PATCH_LEN && rtl_skb_is_udp(skb) && + skb_transport_header_was_set(skb)) { + unsigned int trans_data_len = skb_tail_pointer(skb) - + skb_transport_header(skb); + + if (trans_data_len >= offsetof(struct udphdr, len) && + trans_data_len < RTL_MIN_PATCH_LEN) { + u16 dest = ntohs(udp_hdr(skb)->dest); + + /* dest is a standard PTP port */ + if (dest == 319 || dest == 320) + padto = len + RTL_MIN_PATCH_LEN - trans_data_len; + } + + if (trans_data_len < sizeof(struct udphdr)) + padto = max_t(unsigned int, padto, + len + sizeof(struct udphdr) - trans_data_len); + } + + return padto; +} + +static unsigned int rtl_quirk_packet_padto(struct rtl8169_private *tp, + struct sk_buff *skb) +{ + unsigned int padto = 0; + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63: + padto = rtl8125_quirk_udp_padto(tp, skb); + break; + default: + break; + } + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_34: + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_LAST: + padto = max_t(unsigned int, padto, ETH_ZLEN); + break; + default: + break; + } + + return padto; +} + +static void rtl8169_tso_csum_v1(struct sk_buff *skb, u32 *opts) +{ + u32 mss = skb_shinfo(skb)->gso_size; + + if (mss) { + opts[0] |= TD_LSO; + opts[0] |= mss << TD0_MSS_SHIFT; + } else if (skb->ip_summed == CHECKSUM_PARTIAL) { + const struct iphdr *ip = ip_hdr(skb); + + if (ip->protocol == IPPROTO_TCP) + opts[0] |= TD0_IP_CS | TD0_TCP_CS; + else if (ip->protocol == IPPROTO_UDP) + opts[0] |= TD0_IP_CS | TD0_UDP_CS; + else + WARN_ON_ONCE(1); + } +} + +static bool rtl8169_tso_csum_v2(struct rtl8169_private *tp, + struct sk_buff *skb, u32 *opts) +{ + struct skb_shared_info *shinfo = skb_shinfo(skb); + u32 mss = shinfo->gso_size; + + if (mss) { + if (shinfo->gso_type & SKB_GSO_TCPV4) { + opts[0] |= TD1_GTSENV4; + } else if (shinfo->gso_type & SKB_GSO_TCPV6) { + if (skb_cow_head(skb, 0)) + return false; + + tcp_v6_gso_csum_prep(skb); + opts[0] |= TD1_GTSENV6; + } else { + WARN_ON_ONCE(1); + } + + opts[0] |= skb_transport_offset(skb) << GTTCPHO_SHIFT; + opts[1] |= mss << TD1_MSS_SHIFT; + } else if (skb->ip_summed == CHECKSUM_PARTIAL) { + u8 ip_protocol; + + switch (vlan_get_protocol(skb)) { + case htons(ETH_P_IP): + opts[1] |= TD1_IPv4_CS; + ip_protocol = ip_hdr(skb)->protocol; + break; + + case htons(ETH_P_IPV6): + opts[1] |= TD1_IPv6_CS; + ip_protocol = ipv6_hdr(skb)->nexthdr; + break; + + default: + ip_protocol = IPPROTO_RAW; + break; + } + + if (ip_protocol == IPPROTO_TCP) + opts[1] |= TD1_TCP_CS; + else if (ip_protocol == IPPROTO_UDP) + opts[1] |= TD1_UDP_CS; + else + WARN_ON_ONCE(1); + + opts[1] |= skb_transport_offset(skb) << TCPHO_SHIFT; + } else { + unsigned int padto = rtl_quirk_packet_padto(tp, skb); + + /* skb_padto would free the skb on error */ + return !__skb_put_padto(skb, padto, false); + } + + return true; +} + +static unsigned int rtl_tx_slots_avail(struct rtl8169_private *tp) +{ + return READ_ONCE(tp->dirty_tx) + NUM_TX_DESC - READ_ONCE(tp->cur_tx); +} + +/* Versions RTL8102e and from RTL8168c onwards support csum_v2 */ +static bool rtl_chip_supports_csum_v2(struct rtl8169_private *tp) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_06: + case RTL_GIGA_MAC_VER_10 ... RTL_GIGA_MAC_VER_17: + return false; + default: + return true; + } +} + +static void rtl8169_doorbell(struct rtl8169_private *tp) +{ + if (rtl_is_8125(tp)) + RTL_W16(tp, TxPoll_8125, BIT(0)); + else + RTL_W8(tp, TxPoll, NPQ); +} + +static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + unsigned int entry = tp->cur_tx % NUM_TX_DESC; + struct TxDesc *txd_first, *txd_last; + bool stop_queue, door_bell; + unsigned int frags; + u32 opts[2]; + + if (unlikely(!rtl_tx_slots_avail(tp))) { + if (net_ratelimit()) + netdev_err(dev, "BUG! Tx Ring full when queue awake!\n"); + netif_stop_queue(dev); + return NETDEV_TX_BUSY; + } + + opts[1] = rtl8169_tx_vlan_tag(skb); + opts[0] = 0; + + if (!rtl_chip_supports_csum_v2(tp)) + rtl8169_tso_csum_v1(skb, opts); + else if (!rtl8169_tso_csum_v2(tp, skb, opts)) + goto err_dma_0; + + if (unlikely(rtl8169_tx_map(tp, opts, skb_headlen(skb), skb->data, + entry, false))) + goto err_dma_0; + + txd_first = tp->TxDescArray + entry; + + frags = skb_shinfo(skb)->nr_frags; + if (frags) { + if (rtl8169_xmit_frags(tp, skb, opts, entry)) + goto err_dma_1; + entry = (entry + frags) % NUM_TX_DESC; + } + + txd_last = tp->TxDescArray + entry; + txd_last->opts1 |= cpu_to_le32(LastFrag); + tp->tx_skb[entry].skb = skb; + + skb_tx_timestamp(skb); + + /* Force memory writes to complete before releasing descriptor */ + dma_wmb(); + + door_bell = __netdev_sent_queue(dev, skb->len, netdev_xmit_more()); + + txd_first->opts1 |= cpu_to_le32(DescOwn | FirstFrag); + + /* rtl_tx needs to see descriptor changes before updated tp->cur_tx */ + smp_wmb(); + + WRITE_ONCE(tp->cur_tx, tp->cur_tx + frags + 1); + + stop_queue = !netif_subqueue_maybe_stop(dev, 0, rtl_tx_slots_avail(tp), + R8169_TX_STOP_THRS, + R8169_TX_START_THRS); + if (door_bell || stop_queue) + rtl8169_doorbell(tp); + + return NETDEV_TX_OK; + +err_dma_1: + rtl8169_unmap_tx_skb(tp, entry); +err_dma_0: + dev_kfree_skb_any(skb); + dev->stats.tx_dropped++; + return NETDEV_TX_OK; +} + +static unsigned int rtl_last_frag_len(struct sk_buff *skb) +{ + struct skb_shared_info *info = skb_shinfo(skb); + unsigned int nr_frags = info->nr_frags; + + if (!nr_frags) + return UINT_MAX; + + return skb_frag_size(info->frags + nr_frags - 1); +} + +/* Workaround for hw issues with TSO on RTL8168evl */ +static netdev_features_t rtl8168evl_fix_tso(struct sk_buff *skb, + netdev_features_t features) +{ + /* IPv4 header has options field */ + if (vlan_get_protocol(skb) == htons(ETH_P_IP) && + ip_hdrlen(skb) > sizeof(struct iphdr)) + features &= ~NETIF_F_ALL_TSO; + + /* IPv4 TCP header has options field */ + else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4 && + tcp_hdrlen(skb) > sizeof(struct tcphdr)) + features &= ~NETIF_F_ALL_TSO; + + else if (rtl_last_frag_len(skb) <= 6) + features &= ~NETIF_F_ALL_TSO; + + return features; +} + +static netdev_features_t rtl8169_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + if (skb_is_gso(skb)) { + if (tp->mac_version == RTL_GIGA_MAC_VER_34) + features = rtl8168evl_fix_tso(skb, features); + + if (skb_transport_offset(skb) > GTTCPHO_MAX && + rtl_chip_supports_csum_v2(tp)) + features &= ~NETIF_F_ALL_TSO; + } else if (skb->ip_summed == CHECKSUM_PARTIAL) { + /* work around hw bug on some chip versions */ + if (skb->len < ETH_ZLEN) + features &= ~NETIF_F_CSUM_MASK; + + if (rtl_quirk_packet_padto(tp, skb)) + features &= ~NETIF_F_CSUM_MASK; + + if (skb_transport_offset(skb) > TCPHO_MAX && + rtl_chip_supports_csum_v2(tp)) + features &= ~NETIF_F_CSUM_MASK; + } + + return vlan_features_check(skb, features); +} + +static void rtl8169_pcierr_interrupt(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct pci_dev *pdev = tp->pci_dev; + int pci_status_errs; + u16 pci_cmd; + + pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd); + + pci_status_errs = pci_status_get_and_clear_errors(pdev); + + if (net_ratelimit()) + netdev_err(dev, "PCI error (cmd = 0x%04x, status_errs = 0x%04x)\n", + pci_cmd, pci_status_errs); + + rtl_schedule_task(tp, RTL_FLAG_TASK_RESET_PENDING); +} + +static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp, + int budget) +{ + unsigned int dirty_tx, bytes_compl = 0, pkts_compl = 0; + struct sk_buff *skb; + + dirty_tx = tp->dirty_tx; + + while (READ_ONCE(tp->cur_tx) != dirty_tx) { + unsigned int entry = dirty_tx % NUM_TX_DESC; + u32 status; + + status = le32_to_cpu(READ_ONCE(tp->TxDescArray[entry].opts1)); + if (status & DescOwn) + break; + + skb = tp->tx_skb[entry].skb; + rtl8169_unmap_tx_skb(tp, entry); + + if (skb) { + pkts_compl++; + bytes_compl += skb->len; + napi_consume_skb(skb, budget); + } + dirty_tx++; + } + + if (tp->dirty_tx != dirty_tx) { + dev_sw_netstats_tx_add(dev, pkts_compl, bytes_compl); + WRITE_ONCE(tp->dirty_tx, dirty_tx); + + netif_subqueue_completed_wake(dev, 0, pkts_compl, bytes_compl, + rtl_tx_slots_avail(tp), + R8169_TX_START_THRS); + /* + * 8168 hack: TxPoll requests are lost when the Tx packets are + * too close. Let's kick an extra TxPoll request when a burst + * of start_xmit activity is detected (if it is not detected, + * it is slow enough). -- FR + * If skb is NULL then we come here again once a tx irq is + * triggered after the last fragment is marked transmitted. + */ + if (READ_ONCE(tp->cur_tx) != dirty_tx && skb) + rtl8169_doorbell(tp); + } +} + +static inline int rtl8169_fragmented_frame(u32 status) +{ + return (status & (FirstFrag | LastFrag)) != (FirstFrag | LastFrag); +} + +static inline void rtl8169_rx_csum(struct sk_buff *skb, u32 opts1) +{ + u32 status = opts1 & (RxProtoMask | RxCSFailMask); + + if (status == RxProtoTCP || status == RxProtoUDP) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb_checksum_none_assert(skb); +} + +static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp, int budget) +{ + struct device *d = tp_to_dev(tp); + int count; + + for (count = 0; count < budget; count++, tp->cur_rx++) { + unsigned int pkt_size, entry = tp->cur_rx % NUM_RX_DESC; + struct RxDesc *desc = tp->RxDescArray + entry; + struct sk_buff *skb; + const void *rx_buf; + dma_addr_t addr; + u32 status; + + status = le32_to_cpu(READ_ONCE(desc->opts1)); + if (status & DescOwn) + break; + + /* This barrier is needed to keep us from reading + * any other fields out of the Rx descriptor until + * we know the status of DescOwn + */ + dma_rmb(); + + if (unlikely(status & RxRES)) { + if (net_ratelimit()) + netdev_warn(dev, "Rx ERROR. status = %08x\n", + status); + dev->stats.rx_errors++; + if (status & (RxRWT | RxRUNT)) + dev->stats.rx_length_errors++; + if (status & RxCRC) + dev->stats.rx_crc_errors++; + + if (!(dev->features & NETIF_F_RXALL)) + goto release_descriptor; + else if (status & RxRWT || !(status & (RxRUNT | RxCRC))) + goto release_descriptor; + } + + pkt_size = status & GENMASK(13, 0); + if (likely(!(dev->features & NETIF_F_RXFCS))) + pkt_size -= ETH_FCS_LEN; + + /* The driver does not support incoming fragmented frames. + * They are seen as a symptom of over-mtu sized frames. + */ + if (unlikely(rtl8169_fragmented_frame(status))) { + dev->stats.rx_dropped++; + dev->stats.rx_length_errors++; + goto release_descriptor; + } + + skb = napi_alloc_skb(&tp->napi, pkt_size); + if (unlikely(!skb)) { + dev->stats.rx_dropped++; + goto release_descriptor; + } + + addr = le64_to_cpu(desc->addr); + rx_buf = page_address(tp->Rx_databuff[entry]); + + dma_sync_single_for_cpu(d, addr, pkt_size, DMA_FROM_DEVICE); + prefetch(rx_buf); + skb_copy_to_linear_data(skb, rx_buf, pkt_size); + skb->tail += pkt_size; + skb->len = pkt_size; + dma_sync_single_for_device(d, addr, pkt_size, DMA_FROM_DEVICE); + + rtl8169_rx_csum(skb, status); + skb->protocol = eth_type_trans(skb, dev); + + rtl8169_rx_vlan_tag(desc, skb); + + if (skb->pkt_type == PACKET_MULTICAST) + dev->stats.multicast++; + + napi_gro_receive(&tp->napi, skb); + + dev_sw_netstats_rx_add(dev, pkt_size); +release_descriptor: + rtl8169_mark_to_asic(desc); + } + + return count; +} + +static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance) +{ + struct rtl8169_private *tp = dev_instance; + u32 status = rtl_get_events(tp); + + if ((status & 0xffff) == 0xffff || !(status & tp->irq_mask)) + return IRQ_NONE; + + /* At least RTL8168fp may unexpectedly set the SYSErr bit */ + if (unlikely(status & SYSErr && + tp->mac_version <= RTL_GIGA_MAC_VER_06)) { + rtl8169_pcierr_interrupt(tp->dev); + goto out; + } + + if (status & LinkChg) + phy_mac_interrupt(tp->phydev); + + rtl_irq_disable(tp); + napi_schedule(&tp->napi); +out: + rtl_ack_events(tp, status); + + return IRQ_HANDLED; +} + +static void rtl_task(struct work_struct *work) +{ + struct rtl8169_private *tp = + container_of(work, struct rtl8169_private, wk.work); + int ret; + + if (test_and_clear_bit(RTL_FLAG_TASK_TX_TIMEOUT, tp->wk.flags)) { + /* if chip isn't accessible, reset bus to revive it */ + if (RTL_R32(tp, TxConfig) == ~0) { + ret = pci_reset_bus(tp->pci_dev); + if (ret < 0) { + netdev_err(tp->dev, "Can't reset secondary PCI bus, detach NIC\n"); + netif_device_detach(tp->dev); + return; + } + } + + /* ASPM compatibility issues are a typical reason for tx timeouts */ + ret = pci_disable_link_state(tp->pci_dev, PCIE_LINK_STATE_L1 | + PCIE_LINK_STATE_L0S); + if (!ret) + netdev_warn_once(tp->dev, "ASPM disabled on Tx timeout\n"); + goto reset; + } + + if (test_and_clear_bit(RTL_FLAG_TASK_RESET_PENDING, tp->wk.flags)) { +reset: + rtl_reset_work(tp); + netif_wake_queue(tp->dev); + } +} + +static int rtl8169_poll(struct napi_struct *napi, int budget) +{ + struct rtl8169_private *tp = container_of(napi, struct rtl8169_private, napi); + struct net_device *dev = tp->dev; + int work_done; + + rtl_tx(dev, tp, budget); + + work_done = rtl_rx(dev, tp, budget); + + if (work_done < budget && napi_complete_done(napi, work_done)) + rtl_irq_enable(tp); + + return work_done; +} + +static void rtl_enable_tx_lpi(struct rtl8169_private *tp, bool enable) +{ + if (!rtl_supports_eee(tp)) + return; + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_52: + /* Adjust EEE LED frequency */ + if (tp->mac_version != RTL_GIGA_MAC_VER_38) + RTL_W8(tp, EEE_LED, RTL_R8(tp, EEE_LED) & ~0x07); + if (enable) + rtl_eri_set_bits(tp, 0x1b0, 0x0003); + else + rtl_eri_clear_bits(tp, 0x1b0, 0x0003); + break; + case RTL_GIGA_MAC_VER_61: + if (enable) { + r8168_mac_ocp_modify(tp, 0xe040, 0, 0x0003); + r8168_mac_ocp_modify(tp, 0xeb62, 0, 0x0006); + } else { + r8168_mac_ocp_modify(tp, 0xe040, 0x0003, 0); + r8168_mac_ocp_modify(tp, 0xeb62, 0x0006, 0); + } + break; + case RTL_GIGA_MAC_VER_63 ... RTL_GIGA_MAC_VER_LAST: + if (enable) + r8168_mac_ocp_modify(tp, 0xe040, 0, 0x0003); + else + r8168_mac_ocp_modify(tp, 0xe040, 0x0003, 0); + break; + default: + break; + } +} + +static void r8169_phylink_handler(struct net_device *ndev) +{ + struct rtl8169_private *tp = netdev_priv(ndev); + struct device *d = tp_to_dev(tp); + + if (netif_carrier_ok(ndev)) { + rtl_link_chg_patch(tp); + rtl_enable_tx_lpi(tp, tp->phydev->enable_tx_lpi); + pm_request_resume(d); + } else { + pm_runtime_idle(d); + } + + phy_print_status(tp->phydev); +} + +static int r8169_phy_connect(struct rtl8169_private *tp) +{ + struct phy_device *phydev = tp->phydev; + phy_interface_t phy_mode; + int ret; + + phy_mode = tp->supports_gmii ? PHY_INTERFACE_MODE_GMII : + PHY_INTERFACE_MODE_MII; + + ret = phy_connect_direct(tp->dev, phydev, r8169_phylink_handler, + phy_mode); + if (ret) + return ret; + + if (!tp->supports_gmii) + phy_set_max_speed(phydev, SPEED_100); + + phy_attached_info(phydev); + + return 0; +} + +static void rtl8169_down(struct rtl8169_private *tp) +{ + disable_work_sync(&tp->wk.work); + /* Clear all task flags */ + bitmap_zero(tp->wk.flags, RTL_FLAG_MAX); + + phy_stop(tp->phydev); + + /* Reset SerDes PHY to bring down fiber link */ + if (tp->sfp_mode) + rtl_sfp_reset(tp); + + rtl8169_update_counters(tp); + + pci_clear_master(tp->pci_dev); + rtl_pci_commit(tp); + + rtl8169_cleanup(tp); + rtl_disable_exit_l1(tp); + rtl_prepare_power_down(tp); + + if (tp->dash_type != RTL_DASH_NONE && !tp->saved_wolopts) + rtl8168_driver_stop(tp); +} + +static void rtl8169_up(struct rtl8169_private *tp) +{ + if (tp->dash_type != RTL_DASH_NONE) + rtl8168_driver_start(tp); + + pci_set_master(tp->pci_dev); + phy_init_hw(tp->phydev); + phy_resume(tp->phydev); + rtl8169_init_phy(tp); + napi_enable(&tp->napi); + enable_work(&tp->wk.work); + rtl_reset_work(tp); + + phy_start(tp->phydev); +} + +static int rtl8169_close(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct pci_dev *pdev = tp->pci_dev; + + pm_runtime_get_sync(&pdev->dev); + + netif_stop_queue(dev); + rtl8169_down(tp); + rtl8169_rx_clear(tp); + + free_irq(tp->irq, tp); + + phy_disconnect(tp->phydev); + + dma_free_coherent(&pdev->dev, R8169_RX_RING_BYTES, tp->RxDescArray, + tp->RxPhyAddr); + dma_free_coherent(&pdev->dev, R8169_TX_RING_BYTES, tp->TxDescArray, + tp->TxPhyAddr); + tp->TxDescArray = NULL; + tp->RxDescArray = NULL; + + pm_runtime_put_sync(&pdev->dev); + + return 0; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void rtl8169_netpoll(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + + rtl8169_interrupt(tp->irq, tp); +} +#endif + +static int rtl_open(struct net_device *dev) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct pci_dev *pdev = tp->pci_dev; + unsigned long irqflags; + int retval = -ENOMEM; + + pm_runtime_get_sync(&pdev->dev); + + /* + * Rx and Tx descriptors needs 256 bytes alignment. + * dma_alloc_coherent provides more. + */ + tp->TxDescArray = dma_alloc_coherent(&pdev->dev, R8169_TX_RING_BYTES, + &tp->TxPhyAddr, GFP_KERNEL); + if (!tp->TxDescArray) + goto out; + + tp->RxDescArray = dma_alloc_coherent(&pdev->dev, R8169_RX_RING_BYTES, + &tp->RxPhyAddr, GFP_KERNEL); + if (!tp->RxDescArray) + goto err_free_tx_0; + + retval = rtl8169_init_ring(tp); + if (retval < 0) + goto err_free_rx_1; + + rtl_request_firmware(tp); + + irqflags = pci_dev_msi_enabled(pdev) ? IRQF_NO_THREAD : IRQF_SHARED; + retval = request_irq(tp->irq, rtl8169_interrupt, irqflags, dev->name, tp); + if (retval < 0) + goto err_release_fw_2; + + retval = r8169_phy_connect(tp); + if (retval) + goto err_free_irq; + + rtl8169_up(tp); + rtl8169_init_counter_offsets(tp); + netif_start_queue(dev); +out: + pm_runtime_put_sync(&pdev->dev); + + return retval; + +err_free_irq: + free_irq(tp->irq, tp); +err_release_fw_2: + rtl_release_firmware(tp); + rtl8169_rx_clear(tp); +err_free_rx_1: + dma_free_coherent(&pdev->dev, R8169_RX_RING_BYTES, tp->RxDescArray, + tp->RxPhyAddr); + tp->RxDescArray = NULL; +err_free_tx_0: + dma_free_coherent(&pdev->dev, R8169_TX_RING_BYTES, tp->TxDescArray, + tp->TxPhyAddr); + tp->TxDescArray = NULL; + goto out; +} + +static void +rtl8169_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) +{ + struct rtl8169_private *tp = netdev_priv(dev); + struct pci_dev *pdev = tp->pci_dev; + struct rtl8169_counters *counters = tp->counters; + + pm_runtime_get_noresume(&pdev->dev); + + netdev_stats_to_stats64(stats, &dev->stats); + dev_fetch_sw_netstats(stats, dev->tstats); + + /* + * Fetch additional counter values missing in stats collected by driver + * from tally counters. + */ + if (pm_runtime_active(&pdev->dev)) + rtl8169_update_counters(tp); + + /* + * Subtract values fetched during initalization. + * See rtl8169_init_counter_offsets for a description why we do that. + */ + stats->tx_errors = le64_to_cpu(counters->tx_errors) - + le64_to_cpu(tp->tc_offset.tx_errors); + stats->collisions = le32_to_cpu(counters->tx_multi_collision) - + le32_to_cpu(tp->tc_offset.tx_multi_collision); + stats->tx_aborted_errors = le16_to_cpu(counters->tx_aborted) - + le16_to_cpu(tp->tc_offset.tx_aborted); + stats->rx_missed_errors = le16_to_cpu(counters->rx_missed) - + le16_to_cpu(tp->tc_offset.rx_missed); + + pm_runtime_put_noidle(&pdev->dev); +} + +static void rtl8169_net_suspend(struct rtl8169_private *tp) +{ + netif_device_detach(tp->dev); + + if (netif_running(tp->dev)) + rtl8169_down(tp); +} + +static int rtl8169_runtime_resume(struct device *dev) +{ + struct rtl8169_private *tp = dev_get_drvdata(dev); + + rtl_rar_set(tp, tp->dev->dev_addr); + __rtl8169_set_wol(tp, tp->saved_wolopts); + + if (tp->TxDescArray) + rtl8169_up(tp); + + netif_device_attach(tp->dev); + + return 0; +} + +static int rtl8169_suspend(struct device *device) +{ + struct rtl8169_private *tp = dev_get_drvdata(device); + + rtnl_lock(); + rtl8169_net_suspend(tp); + if (!device_may_wakeup(tp_to_dev(tp))) + clk_disable_unprepare(tp->clk); + rtnl_unlock(); + + return 0; +} + +static int rtl8169_resume(struct device *device) +{ + struct rtl8169_private *tp = dev_get_drvdata(device); + + if (!device_may_wakeup(tp_to_dev(tp))) + clk_prepare_enable(tp->clk); + + /* Some chip versions may truncate packets without this initialization */ + rtl_init_rxcfg(tp); + + return rtl8169_runtime_resume(device); +} + +static int rtl8169_runtime_suspend(struct device *device) +{ + struct rtl8169_private *tp = dev_get_drvdata(device); + + if (!tp->TxDescArray) { + netif_device_detach(tp->dev); + return 0; + } + + rtnl_lock(); + __rtl8169_set_wol(tp, WAKE_PHY); + rtl8169_net_suspend(tp); + rtnl_unlock(); + + return 0; +} + +static int rtl8169_runtime_idle(struct device *device) +{ + struct rtl8169_private *tp = dev_get_drvdata(device); + + if (tp->dash_enabled) + return -EBUSY; + + if (!netif_running(tp->dev) || !netif_carrier_ok(tp->dev)) + pm_schedule_suspend(device, 10000); + + return -EBUSY; +} + +static const struct dev_pm_ops rtl8169_pm_ops = { + SYSTEM_SLEEP_PM_OPS(rtl8169_suspend, rtl8169_resume) + RUNTIME_PM_OPS(rtl8169_runtime_suspend, rtl8169_runtime_resume, + rtl8169_runtime_idle) +}; + +static void rtl_shutdown(struct pci_dev *pdev) +{ + struct rtl8169_private *tp = pci_get_drvdata(pdev); + + rtnl_lock(); + rtl8169_net_suspend(tp); + rtnl_unlock(); + + /* Restore original MAC address */ + rtl_rar_set(tp, tp->dev->perm_addr); + + if (system_state == SYSTEM_POWER_OFF && !tp->dash_enabled) + pci_prepare_to_sleep(pdev); +} + +static void rtl_remove_one(struct pci_dev *pdev) +{ + struct rtl8169_private *tp = pci_get_drvdata(pdev); + + if (pci_dev_run_wake(pdev)) + pm_runtime_get_noresume(&pdev->dev); + + disable_work_sync(&tp->wk.work); + + if (IS_ENABLED(CONFIG_R8169_LEDS)) + r8169_remove_leds(tp->leds); + + unregister_netdev(tp->dev); + + if (tp->dash_type != RTL_DASH_NONE) + rtl8168_driver_stop(tp); + + rtl_release_firmware(tp); + + /* restore original MAC address */ + rtl_rar_set(tp, tp->dev->perm_addr); +} + +static const struct net_device_ops rtl_netdev_ops = { + .ndo_open = rtl_open, + .ndo_stop = rtl8169_close, + .ndo_get_stats64 = rtl8169_get_stats64, + .ndo_start_xmit = rtl8169_start_xmit, + .ndo_features_check = rtl8169_features_check, + .ndo_tx_timeout = rtl8169_tx_timeout, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = rtl8169_change_mtu, + .ndo_fix_features = rtl8169_fix_features, + .ndo_set_features = rtl8169_set_features, + .ndo_set_mac_address = rtl_set_mac_address, + .ndo_eth_ioctl = phy_do_ioctl_running, + .ndo_set_rx_mode = rtl_set_rx_mode, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = rtl8169_netpoll, +#endif + +}; + +static void rtl_set_irq_mask(struct rtl8169_private *tp) +{ + tp->irq_mask = RxOK | RxErr | TxOK | TxErr | LinkChg; + + if (tp->mac_version <= RTL_GIGA_MAC_VER_06) + tp->irq_mask |= SYSErr | RxFIFOOver; +} + +static int rtl_alloc_irq(struct rtl8169_private *tp) +{ + unsigned int flags; + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_06: + rtl_unlock_config_regs(tp); + RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~MSIEnable); + rtl_lock_config_regs(tp); + fallthrough; + case RTL_GIGA_MAC_VER_07 ... RTL_GIGA_MAC_VER_17: + flags = PCI_IRQ_INTX; + break; + default: + flags = PCI_IRQ_ALL_TYPES; + break; + } + + return pci_alloc_irq_vectors(tp->pci_dev, 1, 1, flags); +} + +static void rtl_read_mac_address(struct rtl8169_private *tp, + u8 mac_addr[ETH_ALEN]) +{ + /* Get MAC address */ + if (rtl_is_8168evl_up(tp) && tp->mac_version != RTL_GIGA_MAC_VER_34) { + u32 value; + + value = rtl_eri_read(tp, 0xe0); + put_unaligned_le32(value, mac_addr); + value = rtl_eri_read(tp, 0xe4); + put_unaligned_le16(value, mac_addr + 4); + } else if (rtl_is_8125(tp)) { + rtl_read_mac_from_reg(tp, mac_addr, MAC0_BKP); + } +} + +DECLARE_RTL_COND(rtl_link_list_ready_cond) +{ + return RTL_R8(tp, MCU) & LINK_LIST_RDY; +} + +static void r8168g_wait_ll_share_fifo_ready(struct rtl8169_private *tp) +{ + rtl_loop_wait_high(tp, &rtl_link_list_ready_cond, 100, 42); +} + +static int r8169_mdio_read_reg(struct mii_bus *mii_bus, int phyaddr, int phyreg) +{ + struct rtl8169_private *tp = mii_bus->priv; + + if (phyaddr > 0) + return -ENODEV; + + return rtl_readphy(tp, phyreg); +} + +static int r8169_mdio_write_reg(struct mii_bus *mii_bus, int phyaddr, + int phyreg, u16 val) +{ + struct rtl8169_private *tp = mii_bus->priv; + + if (phyaddr > 0) + return -ENODEV; + + rtl_writephy(tp, phyreg, val); + + return 0; +} + +static int r8169_mdio_read_reg_c45(struct mii_bus *mii_bus, int addr, + int devnum, int regnum) +{ + struct rtl8169_private *tp = mii_bus->priv; + + if (addr > 0) + return -ENODEV; + + if (devnum == MDIO_MMD_VEND2 && regnum > MDIO_STAT2) + return r8168_phy_ocp_read(tp, regnum); + + return 0; +} + +static int r8169_mdio_write_reg_c45(struct mii_bus *mii_bus, int addr, + int devnum, int regnum, u16 val) +{ + struct rtl8169_private *tp = mii_bus->priv; + + if (addr > 0 || devnum != MDIO_MMD_VEND2 || regnum <= MDIO_STAT2) + return -ENODEV; + + r8168_phy_ocp_write(tp, regnum, val); + + return 0; +} + +static int r8169_mdio_register(struct rtl8169_private *tp) +{ + struct pci_dev *pdev = tp->pci_dev; + struct mii_bus *new_bus; + int ret; + + /* On some boards with this chip version the BIOS is buggy and misses + * to reset the PHY page selector. This results in the PHY ID read + * accessing registers on a different page, returning a more or + * less random value. Fix this by resetting the page selector first. + */ + if (tp->mac_version == RTL_GIGA_MAC_VER_25 || + tp->mac_version == RTL_GIGA_MAC_VER_26) + r8169_mdio_write(tp, 0x1f, 0); + + new_bus = devm_mdiobus_alloc(&pdev->dev); + if (!new_bus) + return -ENOMEM; + + new_bus->name = "r8169"; + new_bus->priv = tp; + new_bus->parent = &pdev->dev; + new_bus->irq[0] = PHY_MAC_INTERRUPT; + new_bus->phy_mask = GENMASK(31, 1); + snprintf(new_bus->id, MII_BUS_ID_SIZE, "r8169-%x-%x", + pci_domain_nr(pdev->bus), pci_dev_id(pdev)); + + new_bus->read = r8169_mdio_read_reg; + new_bus->write = r8169_mdio_write_reg; + + if (tp->mac_version >= RTL_GIGA_MAC_VER_40) { + new_bus->read_c45 = r8169_mdio_read_reg_c45; + new_bus->write_c45 = r8169_mdio_write_reg_c45; + } + + ret = devm_mdiobus_register(&pdev->dev, new_bus); + if (ret) + return ret; + + tp->phydev = mdiobus_get_phy(new_bus, 0); + if (!tp->phydev) { + return -ENODEV; + } else if (!tp->phydev->drv) { + /* Most chip versions fail with the genphy driver. + * Therefore ensure that the dedicated PHY driver is loaded. + */ + dev_err(&pdev->dev, "no dedicated PHY driver found for PHY ID 0x%08x, maybe realtek.ko needs to be added to initramfs?\n", + tp->phydev->phy_id); + return -EUNATCH; + } + + tp->phydev->mac_managed_pm = true; + if (rtl_supports_eee(tp)) + phy_support_eee(tp->phydev); + phy_support_asym_pause(tp->phydev); + + /* mimic behavior of r8125/r8126 vendor drivers */ + if (tp->mac_version == RTL_GIGA_MAC_VER_61) + phy_disable_eee_mode(tp->phydev, + ETHTOOL_LINK_MODE_2500baseT_Full_BIT); + + /* PHY will be woken up in rtl_open() */ + phy_suspend(tp->phydev); + + return 0; +} + +static void rtl_hw_init_8168g(struct rtl8169_private *tp) +{ + rtl_enable_rxdvgate(tp); + + RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) & ~(CmdTxEnb | CmdRxEnb)); + msleep(1); + RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB); + + r8168_mac_ocp_modify(tp, 0xe8de, BIT(14), 0); + r8168g_wait_ll_share_fifo_ready(tp); + + r8168_mac_ocp_modify(tp, 0xe8de, 0, BIT(15)); + r8168g_wait_ll_share_fifo_ready(tp); +} + +static void rtl_hw_init_8125(struct rtl8169_private *tp) +{ + rtl_enable_rxdvgate(tp); + + RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) & ~(CmdTxEnb | CmdRxEnb)); + msleep(1); + RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB); + + r8168_mac_ocp_modify(tp, 0xe8de, BIT(14), 0); + r8168g_wait_ll_share_fifo_ready(tp); + + r8168_mac_ocp_write(tp, 0xc0aa, 0x07d0); + r8168_mac_ocp_write(tp, 0xc0a6, 0x0150); + r8168_mac_ocp_write(tp, 0xc01e, 0x5555); + r8168g_wait_ll_share_fifo_ready(tp); +} + +static void rtl_hw_initialize(struct rtl8169_private *tp) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_51 ... RTL_GIGA_MAC_VER_52: + rtl8168ep_stop_cmac(tp); + fallthrough; + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_48: + rtl_hw_init_8168g(tp); + break; + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_LAST: + rtl_hw_init_8125(tp); + break; + default: + break; + } +} + +static int rtl_jumbo_max(struct rtl8169_private *tp) +{ + /* Non-GBit versions don't support jumbo frames */ + if (!tp->supports_gmii) + return 0; + + switch (tp->mac_version) { + /* RTL8169 */ + case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_06: + return JUMBO_7K; + /* RTL8168b */ + case RTL_GIGA_MAC_VER_17: + return JUMBO_4K; + /* RTL8168c */ + case RTL_GIGA_MAC_VER_18 ... RTL_GIGA_MAC_VER_24: + return JUMBO_6K; + /* RTL8125/8126 */ + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_LAST: + return JUMBO_16K; + default: + return JUMBO_9K; + } +} + +static void rtl_init_mac_address(struct rtl8169_private *tp) +{ + u8 mac_addr[ETH_ALEN] __aligned(2) = {}; + struct net_device *dev = tp->dev; + int rc; + + rc = eth_platform_get_mac_address(tp_to_dev(tp), mac_addr); + if (!rc) + goto done; + + rtl_read_mac_address(tp, mac_addr); + if (is_valid_ether_addr(mac_addr)) + goto done; + + rtl_read_mac_from_reg(tp, mac_addr, MAC0); + if (is_valid_ether_addr(mac_addr)) + goto done; + + eth_random_addr(mac_addr); + dev->addr_assign_type = NET_ADDR_RANDOM; + dev_warn(tp_to_dev(tp), "can't read MAC address, setting random one\n"); +done: + eth_hw_addr_set(dev, mac_addr); + rtl_rar_set(tp, mac_addr); +} + +/* register is set if system vendor successfully tested ASPM 1.2 */ +static bool rtl_aspm_is_safe(struct rtl8169_private *tp) +{ + if (tp->mac_version >= RTL_GIGA_MAC_VER_46 && + r8168_mac_ocp_read(tp, 0xc0b2) & 0xf) + return true; + + return false; +} + +static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + const struct rtl_chip_info *chip; + const char *ext_xid_str = ""; + struct rtl8169_private *tp; + int jumbo_max, region, rc; + struct net_device *dev; + u32 txconfig; + u32 xid; + + dev = devm_alloc_etherdev(&pdev->dev, sizeof (*tp)); + if (!dev) + return -ENOMEM; + + SET_NETDEV_DEV(dev, &pdev->dev); + dev->netdev_ops = &rtl_netdev_ops; + tp = netdev_priv(dev); + tp->dev = dev; + tp->pci_dev = pdev; + tp->supports_gmii = ent->driver_data == RTL_CFG_NO_GBIT ? 0 : 1; + tp->ocp_base = OCP_STD_PHY_BASE; + + raw_spin_lock_init(&tp->mac_ocp_lock); + mutex_init(&tp->led_lock); + + /* Get the *optional* external "ether_clk" used on some boards */ + tp->clk = devm_clk_get_optional_enabled(&pdev->dev, "ether_clk"); + if (IS_ERR(tp->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(tp->clk), "failed to get ether_clk\n"); + + /* enable device (incl. PCI PM wakeup and hotplug setup) */ + rc = pcim_enable_device(pdev); + if (rc < 0) + return dev_err_probe(&pdev->dev, rc, "enable failure\n"); + + if (pcim_set_mwi(pdev) < 0) + dev_info(&pdev->dev, "Mem-Wr-Inval unavailable\n"); + + /* use first MMIO region */ + region = ffs(pci_select_bars(pdev, IORESOURCE_MEM)) - 1; + if (region < 0) + return dev_err_probe(&pdev->dev, -ENODEV, "no MMIO resource found\n"); + + tp->mmio_addr = pcim_iomap_region(pdev, region, KBUILD_MODNAME); + if (IS_ERR(tp->mmio_addr)) + return dev_err_probe(&pdev->dev, PTR_ERR(tp->mmio_addr), + "cannot remap MMIO, aborting\n"); + + txconfig = RTL_R32(tp, TxConfig); + if (txconfig == ~0U) + return dev_err_probe(&pdev->dev, -EIO, "PCI read failed\n"); + + xid = (txconfig >> 20) & 0xfcf; + + /* Identify chip attached to board */ + chip = rtl8169_get_chip_version(xid, tp->supports_gmii); + + if (chip->mac_version == RTL_GIGA_MAC_VER_EXTENDED) { + ext_xid_str = "ext"; + xid = RTL_R32(tp, TX_CONFIG_V2); + chip = rtl8169_get_extended_chip_version(xid); + } + if (chip->mac_version == RTL_GIGA_MAC_NONE) + return dev_err_probe(&pdev->dev, -ENODEV, + "unknown chip %sXID %x, contact r8169 maintainers (see MAINTAINERS file)\n", + ext_xid_str, xid); + tp->mac_version = chip->mac_version; + tp->fw_name = chip->fw_name; + + /* Disable ASPM L1 as that cause random device stop working + * problems as well as full system hangs for some PCIe devices users. + */ + if (rtl_aspm_is_safe(tp)) { + dev_info(&pdev->dev, "System vendor flags ASPM as safe\n"); + rc = 0; + } else { + rc = pci_disable_link_state(pdev, PCIE_LINK_STATE_L1); + } + tp->aspm_manageable = !rc; + + if (rtl_is_8125(tp)) { + u16 data = r8168_mac_ocp_read(tp, 0xd006); + + if ((data & 0xff) == 0x07) + tp->sfp_mode = true; + } + + tp->dash_type = rtl_get_dash_type(tp); + tp->dash_enabled = rtl_dash_is_enabled(tp); + + tp->cp_cmd = RTL_R16(tp, CPlusCmd) & CPCMD_MASK; + + if (sizeof(dma_addr_t) > 4 && tp->mac_version >= RTL_GIGA_MAC_VER_18 && + !dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64))) + dev->features |= NETIF_F_HIGHDMA; + + rtl_init_rxcfg(tp); + + rtl8169_irq_mask_and_ack(tp); + + rtl_hw_initialize(tp); + + rtl_hw_reset(tp); + + rc = rtl_alloc_irq(tp); + if (rc < 0) + return dev_err_probe(&pdev->dev, rc, "Can't allocate interrupt\n"); + + tp->irq = pci_irq_vector(pdev, 0); + + INIT_WORK(&tp->wk.work, rtl_task); + disable_work(&tp->wk.work); + + rtl_init_mac_address(tp); + + dev->ethtool_ops = &rtl8169_ethtool_ops; + + netif_napi_add(dev, &tp->napi, rtl8169_poll); + + dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM | + NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; + dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO; + dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; + + /* + * Pretend we are using VLANs; This bypasses a nasty bug where + * Interrupts stop flowing on high load on 8110SCd controllers. + */ + if (tp->mac_version == RTL_GIGA_MAC_VER_05) + /* Disallow toggling */ + dev->hw_features &= ~NETIF_F_HW_VLAN_CTAG_RX; + + if (rtl_chip_supports_csum_v2(tp)) + dev->hw_features |= NETIF_F_IPV6_CSUM; + + dev->features |= dev->hw_features; + + if (rtl_chip_supports_csum_v2(tp)) { + dev->hw_features |= NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6; + netif_set_tso_max_size(dev, RTL_GSO_MAX_SIZE_V2); + netif_set_tso_max_segs(dev, RTL_GSO_MAX_SEGS_V2); + } else { + dev->hw_features |= NETIF_F_SG | NETIF_F_TSO; + netif_set_tso_max_size(dev, RTL_GSO_MAX_SIZE_V1); + netif_set_tso_max_segs(dev, RTL_GSO_MAX_SEGS_V1); + } + + /* There has been a number of reports that using SG/TSO results in + * tx timeouts. However for a lot of people SG/TSO works fine. + * It's not fully clear which chip versions are affected. Vendor + * drivers enable SG/TSO for certain chip versions per default, + * let's mimic this here. On other chip versions users can + * use ethtool to enable SG/TSO, use at own risk! + */ + if (tp->mac_version >= RTL_GIGA_MAC_VER_46 && + tp->mac_version != RTL_GIGA_MAC_VER_61) + dev->features |= dev->hw_features; + + dev->hw_features |= NETIF_F_RXALL; + dev->hw_features |= NETIF_F_RXFCS; + + dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; + + netdev_sw_irq_coalesce_default_on(dev); + + /* configure chip for default features */ + rtl8169_set_features(dev, dev->features); + + if (!tp->dash_enabled) { + rtl_set_d3_pll_down(tp, true); + } else { + rtl_set_d3_pll_down(tp, false); + dev->ethtool->wol_enabled = 1; + } + + jumbo_max = rtl_jumbo_max(tp); + if (jumbo_max) + dev->max_mtu = jumbo_max; + + rtl_set_irq_mask(tp); + + tp->counters = dmam_alloc_coherent (&pdev->dev, sizeof(*tp->counters), + &tp->counters_phys_addr, + GFP_KERNEL); + if (!tp->counters) + return -ENOMEM; + + pci_set_drvdata(pdev, tp); + + rc = r8169_mdio_register(tp); + if (rc) + return rc; + + rc = register_netdev(dev); + if (rc) + return rc; + + if (IS_ENABLED(CONFIG_R8169_LEDS)) { + if (rtl_is_8125(tp)) + tp->leds = rtl8125_init_leds(dev); + else if (tp->mac_version > RTL_GIGA_MAC_VER_06) + tp->leds = rtl8168_init_leds(dev); + } + + netdev_info(dev, "%s, %pM, %sXID %x, IRQ %d\n", + chip->name, dev->dev_addr, ext_xid_str, xid, tp->irq); + + if (jumbo_max) + netdev_info(dev, "jumbo features [frames: %d bytes, tx checksumming: %s]\n", + jumbo_max, tp->mac_version <= RTL_GIGA_MAC_VER_06 ? + "ok" : "ko"); + + if (tp->dash_type != RTL_DASH_NONE) { + netdev_info(dev, "DASH %s\n", + tp->dash_enabled ? "enabled" : "disabled"); + rtl8168_driver_start(tp); + } + + if (pci_dev_run_wake(pdev)) + pm_runtime_put_sync(&pdev->dev); + + return 0; +} + +static struct pci_driver rtl8169_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = rtl8169_pci_tbl, + .probe = rtl_init_one, + .remove = rtl_remove_one, + .shutdown = rtl_shutdown, + .driver.pm = pm_ptr(&rtl8169_pm_ops), +}; + +module_pci_driver(rtl8169_pci_driver); diff --git a/drivers/net/ethernet/realtek/r8169_phy_config.c b/drivers/net/ethernet/realtek/r8169_phy_config.c new file mode 100644 index 000000000..4f1d44713 --- /dev/null +++ b/drivers/net/ethernet/realtek/r8169_phy_config.c @@ -0,0 +1,1380 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * r8169_phy_config.c: RealTek 8169/8168/8101 ethernet driver. + * + * Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw> + * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com> + * Copyright (c) a lot of people too. Please respect their work. + * + * See MAINTAINERS file for support contact information. + */ + +#include <linux/delay.h> +#include <linux/phy.h> + +#include "r8169.h" + +#define LINK_SPEED_10M_PLL_OFF BIT(0) +#define ALDPS_PLL_OFF BIT(1) + +typedef void (*rtl_phy_cfg_fct)(struct rtl8169_private *tp, + struct phy_device *phydev); + +static void r8168d_modify_extpage(struct phy_device *phydev, int extpage, + int reg, u16 mask, u16 val) +{ + int oldpage = phy_select_page(phydev, 0x0007); + + __phy_write(phydev, 0x1e, extpage); + __phy_modify(phydev, reg, mask, val); + + phy_restore_page(phydev, oldpage, 0); +} + +static void r8168d_phy_param(struct phy_device *phydev, u16 parm, + u16 mask, u16 val) +{ + int oldpage = phy_select_page(phydev, 0x0005); + + __phy_write(phydev, 0x05, parm); + __phy_modify(phydev, 0x06, mask, val); + + phy_restore_page(phydev, oldpage, 0); +} + +static void r8168g_phy_param(struct phy_device *phydev, u16 parm, + u16 mask, u16 val) +{ + int oldpage = phy_select_page(phydev, 0x0a43); + + __phy_write(phydev, 0x13, parm); + __phy_modify(phydev, 0x14, mask, val); + + phy_restore_page(phydev, oldpage, 0); +} + +static void rtl8125_phy_param(struct phy_device *phydev, u16 parm, + u16 mask, u16 val) +{ + phy_lock_mdio_bus(phydev); + __phy_write_mmd(phydev, MDIO_MMD_VEND2, 0xb87c, parm); + __phy_modify_mmd(phydev, MDIO_MMD_VEND2, 0xb87e, mask, val); + phy_unlock_mdio_bus(phydev); +} + +struct phy_reg { + u16 reg; + u16 val; +}; + +static void __rtl_writephy_batch(struct phy_device *phydev, + const struct phy_reg *regs, int len) +{ + phy_lock_mdio_bus(phydev); + + while (len-- > 0) { + __phy_write(phydev, regs->reg, regs->val); + regs++; + } + + phy_unlock_mdio_bus(phydev); +} + +#define rtl_writephy_batch(p, a) __rtl_writephy_batch(p, a, ARRAY_SIZE(a)) + +static void rtl8168f_config_eee_phy(struct phy_device *phydev) +{ + r8168d_modify_extpage(phydev, 0x0020, 0x15, 0, BIT(8)); + r8168d_phy_param(phydev, 0x8b85, 0, BIT(13)); +} + +static void rtl8168g_config_eee_phy(struct phy_device *phydev) +{ + phy_modify_paged(phydev, 0x0a43, 0x11, 0, BIT(4)); +} + +static void rtl8168h_config_eee_phy(struct phy_device *phydev) +{ + rtl8168g_config_eee_phy(phydev); + + phy_modify_paged(phydev, 0xa4a, 0x11, 0x0000, 0x0200); + phy_modify_paged(phydev, 0xa42, 0x14, 0x0000, 0x0080); +} + +static void rtl8125_common_config_eee_phy(struct phy_device *phydev) +{ + phy_modify_paged(phydev, 0xa6d, 0x14, 0x0010, 0x0000); + phy_modify_paged(phydev, 0xa42, 0x14, 0x0080, 0x0000); + phy_modify_paged(phydev, 0xa4a, 0x11, 0x0200, 0x0000); +} + +static void rtl8125_config_eee_phy(struct phy_device *phydev) +{ + rtl8168g_config_eee_phy(phydev); + rtl8125_common_config_eee_phy(phydev); +} + +static void rtl8169s_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + static const struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x06, 0x006e }, + { 0x08, 0x0708 }, + { 0x15, 0x4000 }, + { 0x18, 0x65c7 }, + + { 0x1f, 0x0001 }, + { 0x03, 0x00a1 }, + { 0x02, 0x0008 }, + { 0x01, 0x0120 }, + { 0x00, 0x1000 }, + { 0x04, 0x0800 }, + { 0x04, 0x0000 }, + + { 0x03, 0xff41 }, + { 0x02, 0xdf60 }, + { 0x01, 0x0140 }, + { 0x00, 0x0077 }, + { 0x04, 0x7800 }, + { 0x04, 0x7000 }, + + { 0x03, 0x802f }, + { 0x02, 0x4f02 }, + { 0x01, 0x0409 }, + { 0x00, 0xf0f9 }, + { 0x04, 0x9800 }, + { 0x04, 0x9000 }, + + { 0x03, 0xdf01 }, + { 0x02, 0xdf20 }, + { 0x01, 0xff95 }, + { 0x00, 0xba00 }, + { 0x04, 0xa800 }, + { 0x04, 0xa000 }, + + { 0x03, 0xff41 }, + { 0x02, 0xdf20 }, + { 0x01, 0x0140 }, + { 0x00, 0x00bb }, + { 0x04, 0xb800 }, + { 0x04, 0xb000 }, + + { 0x03, 0xdf41 }, + { 0x02, 0xdc60 }, + { 0x01, 0x6340 }, + { 0x00, 0x007d }, + { 0x04, 0xd800 }, + { 0x04, 0xd000 }, + + { 0x03, 0xdf01 }, + { 0x02, 0xdf20 }, + { 0x01, 0x100a }, + { 0x00, 0xa0ff }, + { 0x04, 0xf800 }, + { 0x04, 0xf000 }, + + { 0x1f, 0x0000 }, + { 0x0b, 0x0000 }, + { 0x00, 0x9200 } + }; + + rtl_writephy_batch(phydev, phy_reg_init); +} + +static void rtl8169sb_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + phy_write_paged(phydev, 0x0002, 0x01, 0x90d0); +} + +static void rtl8169scd_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + static const struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x04, 0x0000 }, + { 0x03, 0x00a1 }, + { 0x02, 0x0008 }, + { 0x01, 0x0120 }, + { 0x00, 0x1000 }, + { 0x04, 0x0800 }, + { 0x04, 0x9000 }, + { 0x03, 0x802f }, + { 0x02, 0x4f02 }, + { 0x01, 0x0409 }, + { 0x00, 0xf099 }, + { 0x04, 0x9800 }, + { 0x04, 0xa000 }, + { 0x03, 0xdf01 }, + { 0x02, 0xdf20 }, + { 0x01, 0xff95 }, + { 0x00, 0xba00 }, + { 0x04, 0xa800 }, + { 0x04, 0xf000 }, + { 0x03, 0xdf01 }, + { 0x02, 0xdf20 }, + { 0x01, 0x101a }, + { 0x00, 0xa0ff }, + { 0x04, 0xf800 }, + { 0x04, 0x0000 }, + { 0x1f, 0x0000 }, + + { 0x1f, 0x0001 }, + { 0x10, 0xf41b }, + { 0x14, 0xfb54 }, + { 0x18, 0xf5c7 }, + { 0x1f, 0x0000 }, + + { 0x1f, 0x0001 }, + { 0x17, 0x0cc0 }, + { 0x1f, 0x0000 } + }; + + rtl_writephy_batch(phydev, phy_reg_init); +} + +static void rtl8169sce_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + static const struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x04, 0x0000 }, + { 0x03, 0x00a1 }, + { 0x02, 0x0008 }, + { 0x01, 0x0120 }, + { 0x00, 0x1000 }, + { 0x04, 0x0800 }, + { 0x04, 0x9000 }, + { 0x03, 0x802f }, + { 0x02, 0x4f02 }, + { 0x01, 0x0409 }, + { 0x00, 0xf099 }, + { 0x04, 0x9800 }, + { 0x04, 0xa000 }, + { 0x03, 0xdf01 }, + { 0x02, 0xdf20 }, + { 0x01, 0xff95 }, + { 0x00, 0xba00 }, + { 0x04, 0xa800 }, + { 0x04, 0xf000 }, + { 0x03, 0xdf01 }, + { 0x02, 0xdf20 }, + { 0x01, 0x101a }, + { 0x00, 0xa0ff }, + { 0x04, 0xf800 }, + { 0x04, 0x0000 }, + { 0x1f, 0x0000 }, + + { 0x1f, 0x0001 }, + { 0x0b, 0x8480 }, + { 0x1f, 0x0000 }, + + { 0x1f, 0x0001 }, + { 0x18, 0x67c7 }, + { 0x04, 0x2000 }, + { 0x03, 0x002f }, + { 0x02, 0x4360 }, + { 0x01, 0x0109 }, + { 0x00, 0x3022 }, + { 0x04, 0x2800 }, + { 0x1f, 0x0000 }, + + { 0x1f, 0x0001 }, + { 0x17, 0x0cc0 }, + { 0x1f, 0x0000 } + }; + + rtl_writephy_batch(phydev, phy_reg_init); +} + +static void rtl8168bef_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + phy_write_paged(phydev, 0x0001, 0x10, 0xf41b); +} + +static void rtl8168cp_1_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + phy_write(phydev, 0x1d, 0x0f00); + phy_write_paged(phydev, 0x0002, 0x0c, 0x1ec8); +} + +static void rtl8168cp_2_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + phy_set_bits(phydev, 0x14, BIT(5)); + phy_set_bits(phydev, 0x0d, BIT(5)); + phy_write_paged(phydev, 0x0001, 0x1d, 0x3d98); +} + +static void rtl8168c_1_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + static const struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x12, 0x2300 }, + { 0x1f, 0x0002 }, + { 0x00, 0x88d4 }, + { 0x01, 0x82b1 }, + { 0x03, 0x7002 }, + { 0x08, 0x9e30 }, + { 0x09, 0x01f0 }, + { 0x0a, 0x5500 }, + { 0x0c, 0x00c8 }, + { 0x1f, 0x0003 }, + { 0x12, 0xc096 }, + { 0x16, 0x000a }, + { 0x1f, 0x0000 }, + { 0x1f, 0x0000 }, + { 0x09, 0x2000 }, + { 0x09, 0x0000 } + }; + + rtl_writephy_batch(phydev, phy_reg_init); + + phy_set_bits(phydev, 0x14, BIT(5)); + phy_set_bits(phydev, 0x0d, BIT(5)); +} + +static void rtl8168c_2_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + static const struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x12, 0x2300 }, + { 0x03, 0x802f }, + { 0x02, 0x4f02 }, + { 0x01, 0x0409 }, + { 0x00, 0xf099 }, + { 0x04, 0x9800 }, + { 0x04, 0x9000 }, + { 0x1d, 0x3d98 }, + { 0x1f, 0x0002 }, + { 0x0c, 0x7eb8 }, + { 0x06, 0x0761 }, + { 0x1f, 0x0003 }, + { 0x16, 0x0f0a }, + { 0x1f, 0x0000 } + }; + + rtl_writephy_batch(phydev, phy_reg_init); + + phy_set_bits(phydev, 0x16, BIT(0)); + phy_set_bits(phydev, 0x14, BIT(5)); + phy_set_bits(phydev, 0x0d, BIT(5)); +} + +static void rtl8168c_3_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + static const struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x12, 0x2300 }, + { 0x1d, 0x3d98 }, + { 0x1f, 0x0002 }, + { 0x0c, 0x7eb8 }, + { 0x06, 0x5461 }, + { 0x1f, 0x0003 }, + { 0x16, 0x0f0a }, + { 0x1f, 0x0000 } + }; + + rtl_writephy_batch(phydev, phy_reg_init); + + phy_set_bits(phydev, 0x16, BIT(0)); + phy_set_bits(phydev, 0x14, BIT(5)); + phy_set_bits(phydev, 0x0d, BIT(5)); +} + +static const struct phy_reg rtl8168d_1_phy_reg_init_0[] = { + /* Channel Estimation */ + { 0x1f, 0x0001 }, + { 0x06, 0x4064 }, + { 0x07, 0x2863 }, + { 0x08, 0x059c }, + { 0x09, 0x26b4 }, + { 0x0a, 0x6a19 }, + { 0x0b, 0xdcc8 }, + { 0x10, 0xf06d }, + { 0x14, 0x7f68 }, + { 0x18, 0x7fd9 }, + { 0x1c, 0xf0ff }, + { 0x1d, 0x3d9c }, + { 0x1f, 0x0003 }, + { 0x12, 0xf49f }, + { 0x13, 0x070b }, + { 0x1a, 0x05ad }, + { 0x14, 0x94c0 }, + + /* + * Tx Error Issue + * Enhance line driver power + */ + { 0x1f, 0x0002 }, + { 0x06, 0x5561 }, + { 0x1f, 0x0005 }, + { 0x05, 0x8332 }, + { 0x06, 0x5561 }, + + /* + * Can not link to 1Gbps with bad cable + * Decrease SNR threshold form 21.07dB to 19.04dB + */ + { 0x1f, 0x0001 }, + { 0x17, 0x0cc0 }, + + { 0x1f, 0x0000 }, + { 0x0d, 0xf880 } +}; + +static void rtl8168d_apply_firmware_cond(struct rtl8169_private *tp, + struct phy_device *phydev, + u16 val) +{ + u16 reg_val; + + phy_write(phydev, 0x1f, 0x0005); + phy_write(phydev, 0x05, 0x001b); + reg_val = phy_read(phydev, 0x06); + phy_write(phydev, 0x1f, 0x0000); + + if (reg_val != val) + phydev_warn(phydev, "chipset not ready for firmware\n"); + else + r8169_apply_firmware(tp); +} + +static void rtl8168d_1_common(struct phy_device *phydev) +{ + u16 val; + + phy_write_paged(phydev, 0x0002, 0x05, 0x669a); + r8168d_phy_param(phydev, 0x8330, 0xffff, 0x669a); + phy_write(phydev, 0x1f, 0x0002); + + val = phy_read(phydev, 0x0d); + + if ((val & 0x00ff) != 0x006c) { + static const u16 set[] = { + 0x0065, 0x0066, 0x0067, 0x0068, + 0x0069, 0x006a, 0x006b, 0x006c + }; + int i; + + val &= 0xff00; + for (i = 0; i < ARRAY_SIZE(set); i++) + phy_write(phydev, 0x0d, val | set[i]); + } +} + +static void rtl8168d_1_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + rtl_writephy_batch(phydev, rtl8168d_1_phy_reg_init_0); + + /* + * Rx Error Issue + * Fine Tune Switching regulator parameter + */ + phy_write(phydev, 0x1f, 0x0002); + phy_modify(phydev, 0x0b, 0x00ef, 0x0010); + phy_modify(phydev, 0x0c, 0x5d00, 0xa200); + + if (rtl8168d_efuse_read(tp, 0x01) == 0xb1) { + rtl8168d_1_common(phydev); + } else { + phy_write_paged(phydev, 0x0002, 0x05, 0x6662); + r8168d_phy_param(phydev, 0x8330, 0xffff, 0x6662); + } + + /* RSET couple improve */ + phy_write(phydev, 0x1f, 0x0002); + phy_set_bits(phydev, 0x0d, 0x0300); + phy_set_bits(phydev, 0x0f, 0x0010); + + /* Fine tune PLL performance */ + phy_write(phydev, 0x1f, 0x0002); + phy_modify(phydev, 0x02, 0x0600, 0x0100); + phy_clear_bits(phydev, 0x03, 0xe000); + phy_write(phydev, 0x1f, 0x0000); + + rtl8168d_apply_firmware_cond(tp, phydev, 0xbf00); +} + +static void rtl8168d_2_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + rtl_writephy_batch(phydev, rtl8168d_1_phy_reg_init_0); + + if (rtl8168d_efuse_read(tp, 0x01) == 0xb1) { + rtl8168d_1_common(phydev); + } else { + phy_write_paged(phydev, 0x0002, 0x05, 0x2642); + r8168d_phy_param(phydev, 0x8330, 0xffff, 0x2642); + } + + /* Fine tune PLL performance */ + phy_write(phydev, 0x1f, 0x0002); + phy_modify(phydev, 0x02, 0x0600, 0x0100); + phy_clear_bits(phydev, 0x03, 0xe000); + phy_write(phydev, 0x1f, 0x0000); + + /* Switching regulator Slew rate */ + phy_modify_paged(phydev, 0x0002, 0x0f, 0x0000, 0x0017); + + rtl8168d_apply_firmware_cond(tp, phydev, 0xb300); +} + +static void rtl8168d_4_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + phy_write_paged(phydev, 0x0001, 0x17, 0x0cc0); + r8168d_modify_extpage(phydev, 0x002d, 0x18, 0xffff, 0x0040); + phy_set_bits(phydev, 0x0d, BIT(5)); +} + +static void rtl8168e_1_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + static const struct phy_reg phy_reg_init[] = { + /* Channel estimation fine tune */ + { 0x1f, 0x0001 }, + { 0x0b, 0x6c20 }, + { 0x07, 0x2872 }, + { 0x1c, 0xefff }, + { 0x1f, 0x0003 }, + { 0x14, 0x6420 }, + { 0x1f, 0x0000 }, + }; + + r8169_apply_firmware(tp); + + /* Enable Delay cap */ + r8168d_phy_param(phydev, 0x8b80, 0xffff, 0xc896); + + rtl_writephy_batch(phydev, phy_reg_init); + + /* Update PFM & 10M TX idle timer */ + r8168d_modify_extpage(phydev, 0x002f, 0x15, 0xffff, 0x1919); + + r8168d_modify_extpage(phydev, 0x00ac, 0x18, 0xffff, 0x0006); + + /* DCO enable for 10M IDLE Power */ + r8168d_modify_extpage(phydev, 0x0023, 0x17, 0x0000, 0x0006); + + /* For impedance matching */ + phy_modify_paged(phydev, 0x0002, 0x08, 0x7f00, 0x8000); + + /* PHY auto speed down */ + r8168d_modify_extpage(phydev, 0x002d, 0x18, 0x0000, 0x0050); + phy_set_bits(phydev, 0x14, BIT(15)); + + r8168d_phy_param(phydev, 0x8b86, 0x0000, 0x0001); + r8168d_phy_param(phydev, 0x8b85, 0x2000, 0x0000); + + r8168d_modify_extpage(phydev, 0x0020, 0x15, 0x1100, 0x0000); + phy_write_paged(phydev, 0x0006, 0x00, 0x5a00); + + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0000); +} + +static void rtl8168e_2_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + r8169_apply_firmware(tp); + + /* Enable Delay cap */ + r8168d_modify_extpage(phydev, 0x00ac, 0x18, 0xffff, 0x0006); + + /* Channel estimation fine tune */ + phy_write_paged(phydev, 0x0003, 0x09, 0xa20f); + + /* Green Setting */ + r8168d_phy_param(phydev, 0x8b5b, 0xffff, 0x9222); + r8168d_phy_param(phydev, 0x8b6d, 0xffff, 0x8000); + r8168d_phy_param(phydev, 0x8b76, 0xffff, 0x8000); + + /* For 4-corner performance improve */ + phy_write(phydev, 0x1f, 0x0005); + phy_write(phydev, 0x05, 0x8b80); + phy_set_bits(phydev, 0x17, 0x0006); + phy_write(phydev, 0x1f, 0x0000); + + /* PHY auto speed down */ + r8168d_modify_extpage(phydev, 0x002d, 0x18, 0x0000, 0x0010); + phy_set_bits(phydev, 0x14, BIT(15)); + + /* improve 10M EEE waveform */ + r8168d_phy_param(phydev, 0x8b86, 0x0000, 0x0001); + + /* Improve 2-pair detection performance */ + r8168d_phy_param(phydev, 0x8b85, 0x0000, 0x4000); + + rtl8168f_config_eee_phy(phydev); + + /* Green feature */ + phy_write(phydev, 0x1f, 0x0003); + phy_set_bits(phydev, 0x19, BIT(0)); + phy_set_bits(phydev, 0x10, BIT(10)); + phy_write(phydev, 0x1f, 0x0000); + phy_modify_paged(phydev, 0x0005, 0x01, 0, BIT(8)); +} + +static void rtl8168f_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + /* For 4-corner performance improve */ + r8168d_phy_param(phydev, 0x8b80, 0x0000, 0x0006); + + /* PHY auto speed down */ + r8168d_modify_extpage(phydev, 0x002d, 0x18, 0x0000, 0x0010); + phy_set_bits(phydev, 0x14, BIT(15)); + + /* Improve 10M EEE waveform */ + r8168d_phy_param(phydev, 0x8b86, 0x0000, 0x0001); + + rtl8168f_config_eee_phy(phydev); +} + +static void rtl8168f_1_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + r8169_apply_firmware(tp); + + /* Channel estimation fine tune */ + phy_write_paged(phydev, 0x0003, 0x09, 0xa20f); + + /* Modify green table for giga & fnet */ + r8168d_phy_param(phydev, 0x8b55, 0xffff, 0x0000); + r8168d_phy_param(phydev, 0x8b5e, 0xffff, 0x0000); + r8168d_phy_param(phydev, 0x8b67, 0xffff, 0x0000); + r8168d_phy_param(phydev, 0x8b70, 0xffff, 0x0000); + r8168d_modify_extpage(phydev, 0x0078, 0x17, 0xffff, 0x0000); + r8168d_modify_extpage(phydev, 0x0078, 0x19, 0xffff, 0x00fb); + + /* Modify green table for 10M */ + r8168d_phy_param(phydev, 0x8b79, 0xffff, 0xaa00); + + /* Disable hiimpedance detection (RTCT) */ + phy_write_paged(phydev, 0x0003, 0x01, 0x328a); + + rtl8168f_hw_phy_config(tp, phydev); + + /* Improve 2-pair detection performance */ + r8168d_phy_param(phydev, 0x8b85, 0x0000, 0x4000); +} + +static void rtl8168f_2_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + r8169_apply_firmware(tp); + + rtl8168f_hw_phy_config(tp, phydev); +} + +static void rtl8411_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + r8169_apply_firmware(tp); + + rtl8168f_hw_phy_config(tp, phydev); + + /* Improve 2-pair detection performance */ + r8168d_phy_param(phydev, 0x8b85, 0x0000, 0x4000); + + /* Channel estimation fine tune */ + phy_write_paged(phydev, 0x0003, 0x09, 0xa20f); + + /* Modify green table for giga & fnet */ + r8168d_phy_param(phydev, 0x8b55, 0xffff, 0x0000); + r8168d_phy_param(phydev, 0x8b5e, 0xffff, 0x0000); + r8168d_phy_param(phydev, 0x8b67, 0xffff, 0x0000); + r8168d_phy_param(phydev, 0x8b70, 0xffff, 0x0000); + r8168d_modify_extpage(phydev, 0x0078, 0x17, 0xffff, 0x0000); + r8168d_modify_extpage(phydev, 0x0078, 0x19, 0xffff, 0x00aa); + + /* Modify green table for 10M */ + r8168d_phy_param(phydev, 0x8b79, 0xffff, 0xaa00); + + /* Disable hiimpedance detection (RTCT) */ + phy_write_paged(phydev, 0x0003, 0x01, 0x328a); + + /* Modify green table for giga */ + r8168d_phy_param(phydev, 0x8b54, 0x0800, 0x0000); + r8168d_phy_param(phydev, 0x8b5d, 0x0800, 0x0000); + r8168d_phy_param(phydev, 0x8a7c, 0x0100, 0x0000); + r8168d_phy_param(phydev, 0x8a7f, 0x0000, 0x0100); + r8168d_phy_param(phydev, 0x8a82, 0x0100, 0x0000); + r8168d_phy_param(phydev, 0x8a85, 0x0100, 0x0000); + r8168d_phy_param(phydev, 0x8a88, 0x0100, 0x0000); + + /* uc same-seed solution */ + r8168d_phy_param(phydev, 0x8b85, 0x0000, 0x8000); + + /* Green feature */ + phy_write(phydev, 0x1f, 0x0003); + phy_clear_bits(phydev, 0x19, BIT(0)); + phy_clear_bits(phydev, 0x10, BIT(10)); + phy_write(phydev, 0x1f, 0x0000); +} + +static void rtl8168g_disable_aldps(struct phy_device *phydev) +{ + phy_modify_paged(phydev, 0x0a43, 0x10, BIT(2), 0); +} + +static void rtl8168g_enable_gphy_10m(struct phy_device *phydev) +{ + phy_modify_paged(phydev, 0x0a44, 0x11, 0, BIT(11)); +} + +static void rtl8168g_phy_adjust_10m_aldps(struct phy_device *phydev) +{ + phy_modify_paged(phydev, 0x0bcc, 0x14, BIT(8), 0); + phy_modify_paged(phydev, 0x0a44, 0x11, 0, BIT(7) | BIT(6)); + r8168g_phy_param(phydev, 0x8084, 0x6000, 0x0000); + phy_modify_paged(phydev, 0x0a43, 0x10, 0x0000, 0x1003); +} + +static void rtl8168g_1_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + int ret; + + r8169_apply_firmware(tp); + + ret = phy_read_paged(phydev, 0x0a46, 0x10); + if (ret & BIT(8)) + phy_modify_paged(phydev, 0x0bcc, 0x12, BIT(15), 0); + else + phy_modify_paged(phydev, 0x0bcc, 0x12, 0, BIT(15)); + + ret = phy_read_paged(phydev, 0x0a46, 0x13); + if (ret & BIT(8)) + phy_modify_paged(phydev, 0x0c41, 0x15, 0, BIT(1)); + else + phy_modify_paged(phydev, 0x0c41, 0x15, BIT(1), 0); + + /* Enable PHY auto speed down */ + phy_modify_paged(phydev, 0x0a44, 0x11, 0, BIT(3) | BIT(2)); + + rtl8168g_phy_adjust_10m_aldps(phydev); + + /* EEE auto-fallback function */ + phy_modify_paged(phydev, 0x0a4b, 0x11, 0, BIT(2)); + + /* Enable UC LPF tune function */ + r8168g_phy_param(phydev, 0x8012, 0x0000, 0x8000); + + phy_modify_paged(phydev, 0x0c42, 0x11, BIT(13), BIT(14)); + + /* Improve SWR Efficiency */ + phy_write(phydev, 0x1f, 0x0bcd); + phy_write(phydev, 0x14, 0x5065); + phy_write(phydev, 0x14, 0xd065); + phy_write(phydev, 0x1f, 0x0bc8); + phy_write(phydev, 0x11, 0x5655); + phy_write(phydev, 0x1f, 0x0bcd); + phy_write(phydev, 0x14, 0x1065); + phy_write(phydev, 0x14, 0x9065); + phy_write(phydev, 0x14, 0x1065); + phy_write(phydev, 0x1f, 0x0000); + + rtl8168g_disable_aldps(phydev); + rtl8168g_config_eee_phy(phydev); +} + +static void rtl8168g_2_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + r8169_apply_firmware(tp); + rtl8168g_config_eee_phy(phydev); +} + +static void rtl8168h_2_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + u16 ioffset, rlen; + u32 data; + + r8169_apply_firmware(tp); + + /* CHIN EST parameter update */ + r8168g_phy_param(phydev, 0x808a, 0x003f, 0x000a); + + /* enable R-tune & PGA-retune function */ + r8168g_phy_param(phydev, 0x0811, 0x0000, 0x0800); + phy_modify_paged(phydev, 0x0a42, 0x16, 0x0000, 0x0002); + + rtl8168g_enable_gphy_10m(phydev); + + ioffset = rtl8168h_2_get_adc_bias_ioffset(tp); + if (ioffset != 0xffff) + phy_write_paged(phydev, 0x0bcf, 0x16, ioffset); + + /* Modify rlen (TX LPF corner frequency) level */ + data = phy_read_paged(phydev, 0x0bcd, 0x16); + data &= 0x000f; + rlen = 0; + if (data > 3) + rlen = data - 3; + data = rlen | (rlen << 4) | (rlen << 8) | (rlen << 12); + phy_write_paged(phydev, 0x0bcd, 0x17, data); + + /* disable phy pfm mode */ + phy_modify_paged(phydev, 0x0a44, 0x11, BIT(7), 0); + + /* disable 10m pll off */ + phy_modify_paged(phydev, 0x0a43, 0x10, BIT(0), 0); + + rtl8168g_disable_aldps(phydev); + rtl8168g_config_eee_phy(phydev); +} + +static void rtl8168ep_2_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + rtl8168g_phy_adjust_10m_aldps(phydev); + + /* Enable UC LPF tune function */ + r8168g_phy_param(phydev, 0x8012, 0x0000, 0x8000); + + /* Set rg_sel_sdm_rate */ + phy_modify_paged(phydev, 0x0c42, 0x11, BIT(13), BIT(14)); + + /* Channel estimation parameters */ + r8168g_phy_param(phydev, 0x80f3, 0xff00, 0x8b00); + r8168g_phy_param(phydev, 0x80f0, 0xff00, 0x3a00); + r8168g_phy_param(phydev, 0x80ef, 0xff00, 0x0500); + r8168g_phy_param(phydev, 0x80f6, 0xff00, 0x6e00); + r8168g_phy_param(phydev, 0x80ec, 0xff00, 0x6800); + r8168g_phy_param(phydev, 0x80ed, 0xff00, 0x7c00); + r8168g_phy_param(phydev, 0x80f2, 0xff00, 0xf400); + r8168g_phy_param(phydev, 0x80f4, 0xff00, 0x8500); + r8168g_phy_param(phydev, 0x8110, 0xff00, 0xa800); + r8168g_phy_param(phydev, 0x810f, 0xff00, 0x1d00); + r8168g_phy_param(phydev, 0x8111, 0xff00, 0xf500); + r8168g_phy_param(phydev, 0x8113, 0xff00, 0x6100); + r8168g_phy_param(phydev, 0x8115, 0xff00, 0x9200); + r8168g_phy_param(phydev, 0x810e, 0xff00, 0x0400); + r8168g_phy_param(phydev, 0x810c, 0xff00, 0x7c00); + r8168g_phy_param(phydev, 0x810b, 0xff00, 0x5a00); + r8168g_phy_param(phydev, 0x80d1, 0xff00, 0xff00); + r8168g_phy_param(phydev, 0x80cd, 0xff00, 0x9e00); + r8168g_phy_param(phydev, 0x80d3, 0xff00, 0x0e00); + r8168g_phy_param(phydev, 0x80d5, 0xff00, 0xca00); + r8168g_phy_param(phydev, 0x80d7, 0xff00, 0x8400); + + /* Force PWM-mode */ + phy_write(phydev, 0x1f, 0x0bcd); + phy_write(phydev, 0x14, 0x5065); + phy_write(phydev, 0x14, 0xd065); + phy_write(phydev, 0x1f, 0x0bc8); + phy_write(phydev, 0x12, 0x00ed); + phy_write(phydev, 0x1f, 0x0bcd); + phy_write(phydev, 0x14, 0x1065); + phy_write(phydev, 0x14, 0x9065); + phy_write(phydev, 0x14, 0x1065); + phy_write(phydev, 0x1f, 0x0000); + + rtl8168g_disable_aldps(phydev); + rtl8168g_config_eee_phy(phydev); +} + +static void rtl8117_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + /* CHN EST parameters adjust - fnet */ + r8168g_phy_param(phydev, 0x808e, 0xff00, 0x4800); + r8168g_phy_param(phydev, 0x8090, 0xff00, 0xcc00); + r8168g_phy_param(phydev, 0x8092, 0xff00, 0xb000); + + r8168g_phy_param(phydev, 0x8088, 0xff00, 0x6000); + r8168g_phy_param(phydev, 0x808b, 0x3f00, 0x0b00); + r8168g_phy_param(phydev, 0x808d, 0x1f00, 0x0600); + r8168g_phy_param(phydev, 0x808c, 0xff00, 0xb000); + r8168g_phy_param(phydev, 0x80a0, 0xff00, 0x2800); + r8168g_phy_param(phydev, 0x80a2, 0xff00, 0x5000); + r8168g_phy_param(phydev, 0x809b, 0xf800, 0xb000); + r8168g_phy_param(phydev, 0x809a, 0xff00, 0x4b00); + r8168g_phy_param(phydev, 0x809d, 0x3f00, 0x0800); + r8168g_phy_param(phydev, 0x80a1, 0xff00, 0x7000); + r8168g_phy_param(phydev, 0x809f, 0x1f00, 0x0300); + r8168g_phy_param(phydev, 0x809e, 0xff00, 0x8800); + r8168g_phy_param(phydev, 0x80b2, 0xff00, 0x2200); + r8168g_phy_param(phydev, 0x80ad, 0xf800, 0x9800); + r8168g_phy_param(phydev, 0x80af, 0x3f00, 0x0800); + r8168g_phy_param(phydev, 0x80b3, 0xff00, 0x6f00); + r8168g_phy_param(phydev, 0x80b1, 0x1f00, 0x0300); + r8168g_phy_param(phydev, 0x80b0, 0xff00, 0x9300); + + r8168g_phy_param(phydev, 0x8011, 0x0000, 0x0800); + + rtl8168g_enable_gphy_10m(phydev); + + r8168g_phy_param(phydev, 0x8016, 0x0000, 0x0400); + + rtl8168g_disable_aldps(phydev); + rtl8168h_config_eee_phy(phydev); +} + +static void rtl8102e_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + static const struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0003 }, + { 0x08, 0x441d }, + { 0x01, 0x9100 }, + { 0x1f, 0x0000 } + }; + + phy_set_bits(phydev, 0x11, BIT(12)); + phy_set_bits(phydev, 0x19, BIT(13)); + phy_set_bits(phydev, 0x10, BIT(15)); + + rtl_writephy_batch(phydev, phy_reg_init); +} + +static void rtl8401_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + phy_set_bits(phydev, 0x11, BIT(12)); + phy_modify_paged(phydev, 0x0002, 0x0f, 0x0000, 0x0003); +} + +static void rtl8105e_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + /* Disable ALDPS before ram code */ + phy_write(phydev, 0x18, 0x0310); + msleep(100); + + r8169_apply_firmware(tp); + + phy_write_paged(phydev, 0x0005, 0x1a, 0x0000); + phy_write_paged(phydev, 0x0004, 0x1c, 0x0000); + phy_write_paged(phydev, 0x0001, 0x15, 0x7701); +} + +static void rtl8402_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + /* Disable ALDPS before setting firmware */ + phy_write(phydev, 0x18, 0x0310); + msleep(20); + + r8169_apply_firmware(tp); + + /* EEE setting */ + phy_write(phydev, 0x1f, 0x0004); + phy_write(phydev, 0x10, 0x401f); + phy_write(phydev, 0x19, 0x7030); + phy_write(phydev, 0x1f, 0x0000); +} + +static void rtl8106e_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + static const struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0004 }, + { 0x10, 0xc07f }, + { 0x19, 0x7030 }, + { 0x1f, 0x0000 } + }; + + /* Disable ALDPS before ram code */ + phy_write(phydev, 0x18, 0x0310); + msleep(100); + + r8169_apply_firmware(tp); + + rtl_writephy_batch(phydev, phy_reg_init); +} + +static void rtl8125_legacy_force_mode(struct phy_device *phydev) +{ + phy_modify_paged(phydev, 0xa5b, 0x12, BIT(15), 0); +} + +static void rtl8125a_2_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + int i; + + phy_modify_paged(phydev, 0xad4, 0x17, 0x0000, 0x0010); + phy_modify_paged(phydev, 0xad1, 0x13, 0x03ff, 0x03ff); + phy_modify_paged(phydev, 0xad3, 0x11, 0x003f, 0x0006); + phy_modify_paged(phydev, 0xac0, 0x14, 0x1100, 0x0000); + phy_modify_paged(phydev, 0xacc, 0x10, 0x0003, 0x0002); + phy_modify_paged(phydev, 0xad4, 0x10, 0x00e7, 0x0044); + phy_modify_paged(phydev, 0xac1, 0x12, 0x0080, 0x0000); + phy_modify_paged(phydev, 0xac8, 0x10, 0x0300, 0x0000); + phy_modify_paged(phydev, 0xac5, 0x17, 0x0007, 0x0002); + phy_write_paged(phydev, 0xad4, 0x16, 0x00a8); + phy_write_paged(phydev, 0xac5, 0x16, 0x01ff); + phy_modify_paged(phydev, 0xac8, 0x15, 0x00f0, 0x0030); + + rtl8125_phy_param(phydev, 0x80a2, 0xffff, 0x0153); + rtl8125_phy_param(phydev, 0x809c, 0xffff, 0x0153); + + phy_write(phydev, 0x1f, 0x0a43); + phy_write(phydev, 0x13, 0x81B3); + phy_write(phydev, 0x14, 0x0043); + phy_write(phydev, 0x14, 0x00A7); + phy_write(phydev, 0x14, 0x00D6); + phy_write(phydev, 0x14, 0x00EC); + phy_write(phydev, 0x14, 0x00F6); + phy_write(phydev, 0x14, 0x00FB); + phy_write(phydev, 0x14, 0x00FD); + phy_write(phydev, 0x14, 0x00FF); + phy_write(phydev, 0x14, 0x00BB); + phy_write(phydev, 0x14, 0x0058); + phy_write(phydev, 0x14, 0x0029); + phy_write(phydev, 0x14, 0x0013); + phy_write(phydev, 0x14, 0x0009); + phy_write(phydev, 0x14, 0x0004); + phy_write(phydev, 0x14, 0x0002); + for (i = 0; i < 25; i++) + phy_write(phydev, 0x14, 0x0000); + phy_write(phydev, 0x1f, 0x0000); + + r8168g_phy_param(phydev, 0x8257, 0xffff, 0x020F); + r8168g_phy_param(phydev, 0x80ea, 0xffff, 0x7843); + + r8169_apply_firmware(tp); + + phy_modify_paged(phydev, 0xd06, 0x14, 0x0000, 0x2000); + + r8168g_phy_param(phydev, 0x81a2, 0x0000, 0x0100); + + phy_modify_paged(phydev, 0xb54, 0x16, 0xff00, 0xdb00); + phy_modify_paged(phydev, 0xa45, 0x12, 0x0001, 0x0000); + phy_modify_paged(phydev, 0xa5d, 0x12, 0x0000, 0x0020); + phy_modify_paged(phydev, 0xad4, 0x17, 0x0010, 0x0000); + phy_modify_paged(phydev, 0xa86, 0x15, 0x0001, 0x0000); + rtl8168g_enable_gphy_10m(phydev); + + rtl8168g_disable_aldps(phydev); + rtl8125_config_eee_phy(phydev); +} + +static void rtl8125b_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + r8169_apply_firmware(tp); + rtl8168g_enable_gphy_10m(phydev); + + phy_modify_paged(phydev, 0xac4, 0x13, 0x00f0, 0x0090); + phy_modify_paged(phydev, 0xad3, 0x10, 0x0003, 0x0001); + + rtl8125_phy_param(phydev, 0x80f5, 0xffff, 0x760e); + rtl8125_phy_param(phydev, 0x8107, 0xffff, 0x360e); + rtl8125_phy_param(phydev, 0x8551, 0xff00, 0x0800); + + phy_modify_paged(phydev, 0xbf0, 0x10, 0xe000, 0xa000); + phy_modify_paged(phydev, 0xbf4, 0x13, 0x0f00, 0x0300); + + r8168g_phy_param(phydev, 0x8044, 0xffff, 0x2417); + r8168g_phy_param(phydev, 0x804a, 0xffff, 0x2417); + r8168g_phy_param(phydev, 0x8050, 0xffff, 0x2417); + r8168g_phy_param(phydev, 0x8056, 0xffff, 0x2417); + r8168g_phy_param(phydev, 0x805c, 0xffff, 0x2417); + r8168g_phy_param(phydev, 0x8062, 0xffff, 0x2417); + r8168g_phy_param(phydev, 0x8068, 0xffff, 0x2417); + r8168g_phy_param(phydev, 0x806e, 0xffff, 0x2417); + r8168g_phy_param(phydev, 0x8074, 0xffff, 0x2417); + r8168g_phy_param(phydev, 0x807a, 0xffff, 0x2417); + + phy_modify_paged(phydev, 0xa4c, 0x15, 0x0000, 0x0040); + phy_modify_paged(phydev, 0xbf8, 0x12, 0xe000, 0xa000); + + rtl8125_legacy_force_mode(phydev); + rtl8168g_disable_aldps(phydev); + rtl8125_config_eee_phy(phydev); +} + +static void rtl8125d_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + r8169_apply_firmware(tp); + rtl8168g_enable_gphy_10m(phydev); + rtl8125_legacy_force_mode(phydev); + rtl8168g_disable_aldps(phydev); + rtl8125_config_eee_phy(phydev); +} + +static void rtl8125cp_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + r8169_apply_firmware(tp); + rtl8168g_enable_gphy_10m(phydev); + + phy_modify_paged(phydev, 0xad0, 0x17, 0x007f, 0x000b); + phy_modify_paged(phydev, 0xad7, 0x14, 0x0000, BIT(4)); + rtl8125_phy_param(phydev, 0x807f, 0xff00, 0x5300); + r8168g_phy_param(phydev, 0x81b8, 0xffff, 0x00b4); + r8168g_phy_param(phydev, 0x81ba, 0xffff, 0x00e4); + r8168g_phy_param(phydev, 0x81c5, 0xffff, 0x0104); + r8168g_phy_param(phydev, 0x81d0, 0xffff, 0x054d); + phy_modify_paged(phydev, 0x0a43, 0x10, 0x0000, + LINK_SPEED_10M_PLL_OFF | ALDPS_PLL_OFF); + phy_modify_paged(phydev, 0x0a44, 0x11, 0x0000, BIT(7)); + + rtl8125_legacy_force_mode(phydev); + rtl8168g_disable_aldps(phydev); + rtl8125_config_eee_phy(phydev); +} + +static void rtl8125bp_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + r8169_apply_firmware(tp); + rtl8168g_enable_gphy_10m(phydev); + + r8168g_phy_param(phydev, 0x8010, 0x0800, 0x0000); + + rtl8125_phy_param(phydev, 0x8088, 0xff00, 0x9000); + rtl8125_phy_param(phydev, 0x808f, 0xff00, 0x9000); + + r8168g_phy_param(phydev, 0x8174, 0x2000, 0x1800); + + rtl8125_legacy_force_mode(phydev); + rtl8168g_disable_aldps(phydev); + rtl8125_config_eee_phy(phydev); +} + +static void rtl8126a_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + r8169_apply_firmware(tp); + rtl8168g_enable_gphy_10m(phydev); + rtl8125_legacy_force_mode(phydev); + rtl8168g_disable_aldps(phydev); + rtl8125_common_config_eee_phy(phydev); +} + +static void rtl8127a_1_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + r8169_apply_firmware(tp); + rtl8168g_enable_gphy_10m(phydev); + + r8168g_phy_param(phydev, 0x8415, 0xff00, 0x9300); + r8168g_phy_param(phydev, 0x81a3, 0xff00, 0x0f00); + r8168g_phy_param(phydev, 0x81ae, 0xff00, 0x0f00); + r8168g_phy_param(phydev, 0x81b9, 0xff00, 0xb900); + rtl8125_phy_param(phydev, 0x83b0, 0x0e00, 0x0000); + rtl8125_phy_param(phydev, 0x83C5, 0x0e00, 0x0000); + rtl8125_phy_param(phydev, 0x83da, 0x0e00, 0x0000); + rtl8125_phy_param(phydev, 0x83ef, 0x0e00, 0x0000); + phy_modify_paged(phydev, 0x0bf3, 0x14, 0x01f0, 0x0160); + phy_modify_paged(phydev, 0x0bf3, 0x15, 0x001f, 0x0014); + phy_modify_paged(phydev, 0x0bf2, 0x14, 0x6000, 0x0000); + phy_modify_paged(phydev, 0x0bf2, 0x16, 0xc000, 0x0000); + phy_modify_paged(phydev, 0x0bf2, 0x14, 0x1fff, 0x0187); + phy_modify_paged(phydev, 0x0bf2, 0x15, 0x003f, 0x0003); + + r8168g_phy_param(phydev, 0x8173, 0xffff, 0x8620); + r8168g_phy_param(phydev, 0x8175, 0xffff, 0x8671); + r8168g_phy_param(phydev, 0x817c, 0x0000, 0x2000); + r8168g_phy_param(phydev, 0x8187, 0x0000, 0x2000); + r8168g_phy_param(phydev, 0x8192, 0x0000, 0x2000); + r8168g_phy_param(phydev, 0x819d, 0x0000, 0x2000); + r8168g_phy_param(phydev, 0x81a8, 0x2000, 0x0000); + r8168g_phy_param(phydev, 0x81b3, 0x2000, 0x0000); + r8168g_phy_param(phydev, 0x81be, 0x0000, 0x2000); + r8168g_phy_param(phydev, 0x817d, 0xff00, 0xa600); + r8168g_phy_param(phydev, 0x8188, 0xff00, 0xa600); + r8168g_phy_param(phydev, 0x8193, 0xff00, 0xa600); + r8168g_phy_param(phydev, 0x819e, 0xff00, 0xa600); + r8168g_phy_param(phydev, 0x81a9, 0xff00, 0x1400); + r8168g_phy_param(phydev, 0x81b4, 0xff00, 0x1400); + r8168g_phy_param(phydev, 0x81bf, 0xff00, 0xa600); + + phy_modify_paged(phydev, 0x0aea, 0x15, 0x0028, 0x0000); + + rtl8125_phy_param(phydev, 0x84f0, 0xffff, 0x201c); + rtl8125_phy_param(phydev, 0x84f2, 0xffff, 0x3117); + + phy_write_paged(phydev, 0x0aec, 0x13, 0x0000); + phy_write_paged(phydev, 0x0ae2, 0x10, 0xffff); + phy_write_paged(phydev, 0x0aec, 0x17, 0xffff); + phy_write_paged(phydev, 0x0aed, 0x11, 0xffff); + phy_write_paged(phydev, 0x0aec, 0x14, 0x0000); + phy_modify_paged(phydev, 0x0aed, 0x10, 0x0001, 0x0000); + phy_write_paged(phydev, 0x0adb, 0x14, 0x0150); + rtl8125_phy_param(phydev, 0x8197, 0xff00, 0x5000); + rtl8125_phy_param(phydev, 0x8231, 0xff00, 0x5000); + rtl8125_phy_param(phydev, 0x82cb, 0xff00, 0x5000); + rtl8125_phy_param(phydev, 0x82cd, 0xff00, 0x5700); + rtl8125_phy_param(phydev, 0x8233, 0xff00, 0x5700); + rtl8125_phy_param(phydev, 0x8199, 0xff00, 0x5700); + + rtl8125_phy_param(phydev, 0x815a, 0xffff, 0x0150); + rtl8125_phy_param(phydev, 0x81f4, 0xffff, 0x0150); + rtl8125_phy_param(phydev, 0x828e, 0xffff, 0x0150); + rtl8125_phy_param(phydev, 0x81b1, 0xffff, 0x0000); + rtl8125_phy_param(phydev, 0x824b, 0xffff, 0x0000); + rtl8125_phy_param(phydev, 0x82e5, 0xffff, 0x0000); + + rtl8125_phy_param(phydev, 0x84f7, 0xff00, 0x2800); + phy_modify_paged(phydev, 0x0aec, 0x11, 0x0000, 0x1000); + rtl8125_phy_param(phydev, 0x81b3, 0xff00, 0xad00); + rtl8125_phy_param(phydev, 0x824d, 0xff00, 0xad00); + rtl8125_phy_param(phydev, 0x82e7, 0xff00, 0xad00); + phy_modify_paged(phydev, 0x0ae4, 0x17, 0x000f, 0x0001); + rtl8125_phy_param(phydev, 0x82ce, 0xf000, 0x4000); + + rtl8125_phy_param(phydev, 0x84ac, 0xffff, 0x0000); + rtl8125_phy_param(phydev, 0x84ae, 0xffff, 0x0000); + rtl8125_phy_param(phydev, 0x84b0, 0xffff, 0xf818); + rtl8125_phy_param(phydev, 0x84b2, 0xff00, 0x6000); + + rtl8125_phy_param(phydev, 0x8ffc, 0xffff, 0x6008); + rtl8125_phy_param(phydev, 0x8ffe, 0xffff, 0xf450); + + rtl8125_phy_param(phydev, 0x8015, 0x0000, 0x0200); + rtl8125_phy_param(phydev, 0x8016, 0x0800, 0x0000); + rtl8125_phy_param(phydev, 0x8fe6, 0xff00, 0x0800); + rtl8125_phy_param(phydev, 0x8fe4, 0xffff, 0x2114); + + rtl8125_phy_param(phydev, 0x8647, 0xffff, 0xa7b1); + rtl8125_phy_param(phydev, 0x8649, 0xffff, 0xbbca); + rtl8125_phy_param(phydev, 0x864b, 0xff00, 0xdc00); + + rtl8125_phy_param(phydev, 0x8154, 0xc000, 0x4000); + rtl8125_phy_param(phydev, 0x8158, 0xc000, 0x0000); + + rtl8125_phy_param(phydev, 0x826c, 0xffff, 0xffff); + rtl8125_phy_param(phydev, 0x826e, 0xffff, 0xffff); + + rtl8125_phy_param(phydev, 0x8872, 0xff00, 0x0e00); + r8168g_phy_param(phydev, 0x8012, 0x0000, 0x0800); + r8168g_phy_param(phydev, 0x8012, 0x0000, 0x4000); + phy_modify_paged(phydev, 0x0b57, 0x13, 0x0000, 0x0001); + r8168g_phy_param(phydev, 0x834a, 0xff00, 0x0700); + rtl8125_phy_param(phydev, 0x8217, 0x3f00, 0x2a00); + r8168g_phy_param(phydev, 0x81b1, 0xff00, 0x0b00); + rtl8125_phy_param(phydev, 0x8fed, 0xff00, 0x4e00); + + rtl8125_phy_param(phydev, 0x88ac, 0xff00, 0x2300); + phy_modify_paged(phydev, 0x0bf0, 0x16, 0x0000, 0x3800); + rtl8125_phy_param(phydev, 0x88de, 0xff00, 0x0000); + rtl8125_phy_param(phydev, 0x80b4, 0xffff, 0x5195); + + r8168g_phy_param(phydev, 0x8370, 0xffff, 0x8671); + r8168g_phy_param(phydev, 0x8372, 0xffff, 0x86c8); + + r8168g_phy_param(phydev, 0x8401, 0xffff, 0x86c8); + r8168g_phy_param(phydev, 0x8403, 0xffff, 0x86da); + r8168g_phy_param(phydev, 0x8406, 0x1800, 0x1000); + r8168g_phy_param(phydev, 0x8408, 0x1800, 0x1000); + r8168g_phy_param(phydev, 0x840a, 0x1800, 0x1000); + r8168g_phy_param(phydev, 0x840c, 0x1800, 0x1000); + r8168g_phy_param(phydev, 0x840e, 0x1800, 0x1000); + r8168g_phy_param(phydev, 0x8410, 0x1800, 0x1000); + r8168g_phy_param(phydev, 0x8412, 0x1800, 0x1000); + r8168g_phy_param(phydev, 0x8414, 0x1800, 0x1000); + r8168g_phy_param(phydev, 0x8416, 0x1800, 0x1000); + + r8168g_phy_param(phydev, 0x82bd, 0xffff, 0x1f40); + + phy_modify_paged(phydev, 0x0bfb, 0x12, 0x07ff, 0x0328); + phy_write_paged(phydev, 0x0bfb, 0x13, 0x3e14); + + r8168g_phy_param(phydev, 0x81c4, 0xffff, 0x003b); + r8168g_phy_param(phydev, 0x81c6, 0xffff, 0x0086); + r8168g_phy_param(phydev, 0x81c8, 0xffff, 0x00b7); + r8168g_phy_param(phydev, 0x81ca, 0xffff, 0x00db); + r8168g_phy_param(phydev, 0x81cc, 0xffff, 0x00fe); + r8168g_phy_param(phydev, 0x81ce, 0xffff, 0x00fe); + r8168g_phy_param(phydev, 0x81d0, 0xffff, 0x00fe); + r8168g_phy_param(phydev, 0x81d2, 0xffff, 0x00fe); + r8168g_phy_param(phydev, 0x81d4, 0xffff, 0x00c3); + r8168g_phy_param(phydev, 0x81d6, 0xffff, 0x0078); + r8168g_phy_param(phydev, 0x81d8, 0xffff, 0x0047); + r8168g_phy_param(phydev, 0x81da, 0xffff, 0x0023); + + rtl8125_phy_param(phydev, 0x88d7, 0xffff, 0x01a0); + rtl8125_phy_param(phydev, 0x88d9, 0xffff, 0x01a0); + rtl8125_phy_param(phydev, 0x8ffa, 0xffff, 0x002a); + + rtl8125_phy_param(phydev, 0x8fee, 0xffff, 0xffdf); + rtl8125_phy_param(phydev, 0x8ff0, 0xffff, 0xffff); + rtl8125_phy_param(phydev, 0x8ff2, 0xffff, 0x0a4a); + rtl8125_phy_param(phydev, 0x8ff4, 0xffff, 0xaa5a); + rtl8125_phy_param(phydev, 0x8ff6, 0xffff, 0x0a4a); + + rtl8125_phy_param(phydev, 0x8ff8, 0xffff, 0xaa5a); + rtl8125_phy_param(phydev, 0x88d5, 0xff00, 0x0200); + + r8168g_phy_param(phydev, 0x84bb, 0xff00, 0x0a00); + r8168g_phy_param(phydev, 0x84c0, 0xff00, 0x1600); + + phy_modify_paged(phydev, 0x0a43, 0x10, 0x0000, 0x0003); + + rtl8125_legacy_force_mode(phydev); + rtl8168g_disable_aldps(phydev); + rtl8125_common_config_eee_phy(phydev); +} + +void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev, + enum mac_version ver) +{ + static const rtl_phy_cfg_fct phy_configs[] = { + /* PCI devices. */ + [RTL_GIGA_MAC_VER_02] = rtl8169s_hw_phy_config, + [RTL_GIGA_MAC_VER_03] = rtl8169s_hw_phy_config, + [RTL_GIGA_MAC_VER_04] = rtl8169sb_hw_phy_config, + [RTL_GIGA_MAC_VER_05] = rtl8169scd_hw_phy_config, + [RTL_GIGA_MAC_VER_06] = rtl8169sce_hw_phy_config, + /* PCI-E devices. */ + [RTL_GIGA_MAC_VER_07] = rtl8102e_hw_phy_config, + [RTL_GIGA_MAC_VER_08] = rtl8102e_hw_phy_config, + [RTL_GIGA_MAC_VER_09] = rtl8102e_hw_phy_config, + [RTL_GIGA_MAC_VER_10] = NULL, + [RTL_GIGA_MAC_VER_14] = rtl8401_hw_phy_config, + [RTL_GIGA_MAC_VER_17] = rtl8168bef_hw_phy_config, + [RTL_GIGA_MAC_VER_18] = rtl8168cp_1_hw_phy_config, + [RTL_GIGA_MAC_VER_19] = rtl8168c_1_hw_phy_config, + [RTL_GIGA_MAC_VER_20] = rtl8168c_2_hw_phy_config, + [RTL_GIGA_MAC_VER_21] = rtl8168c_3_hw_phy_config, + [RTL_GIGA_MAC_VER_22] = rtl8168c_3_hw_phy_config, + [RTL_GIGA_MAC_VER_23] = rtl8168cp_2_hw_phy_config, + [RTL_GIGA_MAC_VER_24] = rtl8168cp_2_hw_phy_config, + [RTL_GIGA_MAC_VER_25] = rtl8168d_1_hw_phy_config, + [RTL_GIGA_MAC_VER_26] = rtl8168d_2_hw_phy_config, + [RTL_GIGA_MAC_VER_28] = rtl8168d_4_hw_phy_config, + [RTL_GIGA_MAC_VER_29] = rtl8105e_hw_phy_config, + [RTL_GIGA_MAC_VER_30] = rtl8105e_hw_phy_config, + [RTL_GIGA_MAC_VER_31] = NULL, + [RTL_GIGA_MAC_VER_32] = rtl8168e_1_hw_phy_config, + [RTL_GIGA_MAC_VER_33] = rtl8168e_1_hw_phy_config, + [RTL_GIGA_MAC_VER_34] = rtl8168e_2_hw_phy_config, + [RTL_GIGA_MAC_VER_35] = rtl8168f_1_hw_phy_config, + [RTL_GIGA_MAC_VER_36] = rtl8168f_2_hw_phy_config, + [RTL_GIGA_MAC_VER_37] = rtl8402_hw_phy_config, + [RTL_GIGA_MAC_VER_38] = rtl8411_hw_phy_config, + [RTL_GIGA_MAC_VER_39] = rtl8106e_hw_phy_config, + [RTL_GIGA_MAC_VER_40] = rtl8168g_1_hw_phy_config, + [RTL_GIGA_MAC_VER_42] = rtl8168g_2_hw_phy_config, + [RTL_GIGA_MAC_VER_43] = rtl8168g_2_hw_phy_config, + [RTL_GIGA_MAC_VER_44] = rtl8168g_2_hw_phy_config, + [RTL_GIGA_MAC_VER_46] = rtl8168h_2_hw_phy_config, + [RTL_GIGA_MAC_VER_48] = rtl8168h_2_hw_phy_config, + [RTL_GIGA_MAC_VER_51] = rtl8168ep_2_hw_phy_config, + [RTL_GIGA_MAC_VER_52] = rtl8117_hw_phy_config, + [RTL_GIGA_MAC_VER_61] = rtl8125a_2_hw_phy_config, + [RTL_GIGA_MAC_VER_63] = rtl8125b_hw_phy_config, + [RTL_GIGA_MAC_VER_64] = rtl8125d_hw_phy_config, + [RTL_GIGA_MAC_VER_65] = rtl8125cp_hw_phy_config, + [RTL_GIGA_MAC_VER_66] = rtl8125bp_hw_phy_config, + [RTL_GIGA_MAC_VER_70] = rtl8126a_hw_phy_config, + [RTL_GIGA_MAC_VER_80] = rtl8127a_1_hw_phy_config, + }; + + if (phy_configs[ver]) + phy_configs[ver](tp, phydev); +} diff --git a/drivers/net/ethernet/realtek/rtase/Makefile b/drivers/net/ethernet/realtek/rtase/Makefile new file mode 100644 index 000000000..ba3d8550f --- /dev/null +++ b/drivers/net/ethernet/realtek/rtase/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +# Copyright(c) 2024 Realtek Semiconductor Corp. All rights reserved. + +# +# Makefile for the Realtek PCIe driver +# + +obj-$(CONFIG_RTASE) += rtase.o + +rtase-objs := rtase_main.o diff --git a/drivers/net/ethernet/realtek/rtase/rtase.h b/drivers/net/ethernet/realtek/rtase/rtase.h new file mode 100644 index 000000000..9bd687247 --- /dev/null +++ b/drivers/net/ethernet/realtek/rtase/rtase.h @@ -0,0 +1,366 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * rtase is the Linux device driver released for Realtek Automotive Switch + * controllers with PCI-Express interface. + * + * Copyright(c) 2024 Realtek Semiconductor Corp. + */ + +#ifndef RTASE_H +#define RTASE_H + +#define RTASE_HW_VER_MASK 0x7C800000 +#define RTASE_HW_VER_906X_7XA 0x00800000 +#define RTASE_HW_VER_906X_7XC 0x04000000 +#define RTASE_HW_VER_907XD_V1 0x04800000 +#define RTASE_HW_VER_907XD_VA 0x08000000 + +#define RTASE_RX_DMA_BURST_256 4 +#define RTASE_TX_DMA_BURST_UNLIMITED 7 + +#define RTASE_RX_BUF_SIZE (PAGE_SIZE - \ + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) +#define RTASE_MAX_JUMBO_SIZE (RTASE_RX_BUF_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN) + +/* 3 means InterFrameGap = the shortest one */ +#define RTASE_INTERFRAMEGAP 0x03 + +#define RTASE_REGS_SIZE 256 +#define RTASE_PCI_REGS_SIZE 0x100 + +#define RTASE_MULTICAST_FILTER_MASK GENMASK(30, 26) + +#define RTASE_VLAN_FILTER_ENTRY_NUM 32 +#define RTASE_NUM_TX_QUEUE 8 +#define RTASE_NUM_RX_QUEUE 4 + +#define RTASE_TXQ_CTRL 1 +#define RTASE_FUNC_TXQ_NUM 1 +#define RTASE_FUNC_RXQ_NUM 1 +#define RTASE_INTERRUPT_NUM 1 + +#define RTASE_MITI_TIME_COUNT_MASK GENMASK(3, 0) +#define RTASE_MITI_TIME_UNIT_MASK GENMASK(7, 4) +#define RTASE_MITI_DEFAULT_TIME 128 +#define RTASE_MITI_MAX_TIME 491520 +#define RTASE_MITI_PKT_NUM_COUNT_MASK GENMASK(11, 8) +#define RTASE_MITI_PKT_NUM_UNIT_MASK GENMASK(13, 12) +#define RTASE_MITI_DEFAULT_PKT_NUM 64 +#define RTASE_MITI_MAX_PKT_NUM_IDX 3 +#define RTASE_MITI_MAX_PKT_NUM_UNIT 16 +#define RTASE_MITI_MAX_PKT_NUM 240 +#define RTASE_MITI_COUNT_BIT_NUM 4 + +#define RTASE_NUM_MSIX 4 + +#define RTASE_DWORD_MOD 16 + +/*****************************************************************************/ +enum rtase_registers { + RTASE_MAC0 = 0x0000, + RTASE_MAC4 = 0x0004, + RTASE_MAR0 = 0x0008, + RTASE_MAR1 = 0x000C, + RTASE_DTCCR0 = 0x0010, + RTASE_DTCCR4 = 0x0014, +#define RTASE_COUNTER_RESET BIT(0) +#define RTASE_COUNTER_DUMP BIT(3) + + RTASE_FCR = 0x0018, +#define RTASE_FCR_RXQ_MASK GENMASK(5, 4) + + RTASE_LBK_CTRL = 0x001A, +#define RTASE_LBK_ATLD BIT(1) +#define RTASE_LBK_CLR BIT(0) + + RTASE_TX_DESC_ADDR0 = 0x0020, + RTASE_TX_DESC_ADDR4 = 0x0024, + RTASE_TX_DESC_COMMAND = 0x0028, +#define RTASE_TX_DESC_CMD_CS BIT(15) +#define RTASE_TX_DESC_CMD_WE BIT(14) + + RTASE_BOOT_CTL = 0x6004, + RTASE_CLKSW_SET = 0x6018, + + RTASE_CHIP_CMD = 0x0037, +#define RTASE_STOP_REQ BIT(7) +#define RTASE_STOP_REQ_DONE BIT(6) +#define RTASE_RE BIT(3) +#define RTASE_TE BIT(2) + + RTASE_IMR0 = 0x0038, + RTASE_ISR0 = 0x003C, +#define RTASE_TOK7 BIT(30) +#define RTASE_TOK6 BIT(28) +#define RTASE_TOK5 BIT(26) +#define RTASE_TOK4 BIT(24) +#define RTASE_FOVW BIT(6) +#define RTASE_RDU BIT(4) +#define RTASE_TOK BIT(2) +#define RTASE_ROK BIT(0) + + RTASE_IMR1 = 0x0800, + RTASE_ISR1 = 0x0802, +#define RTASE_Q_TOK BIT(4) +#define RTASE_Q_RDU BIT(1) +#define RTASE_Q_ROK BIT(0) + + RTASE_EPHY_ISR = 0x6014, + RTASE_EPHY_IMR = 0x6016, + + RTASE_TX_CONFIG_0 = 0x0040, +#define RTASE_TX_INTER_FRAME_GAP_MASK GENMASK(25, 24) + /* DMA burst value (0-7) is shift this many bits */ +#define RTASE_TX_DMA_MASK GENMASK(10, 8) + + RTASE_RX_CONFIG_0 = 0x0044, +#define RTASE_RX_SINGLE_FETCH BIT(14) +#define RTASE_RX_SINGLE_TAG BIT(13) +#define RTASE_RX_MX_DMA_MASK GENMASK(10, 8) +#define RTASE_ACPT_FLOW BIT(7) +#define RTASE_ACCEPT_ERR BIT(5) +#define RTASE_ACCEPT_RUNT BIT(4) +#define RTASE_ACCEPT_BROADCAST BIT(3) +#define RTASE_ACCEPT_MULTICAST BIT(2) +#define RTASE_ACCEPT_MYPHYS BIT(1) +#define RTASE_ACCEPT_ALLPHYS BIT(0) +#define RTASE_ACCEPT_MASK (RTASE_ACPT_FLOW | RTASE_ACCEPT_ERR | \ + RTASE_ACCEPT_RUNT | RTASE_ACCEPT_BROADCAST | \ + RTASE_ACCEPT_MULTICAST | RTASE_ACCEPT_MYPHYS | \ + RTASE_ACCEPT_ALLPHYS) + + RTASE_RX_CONFIG_1 = 0x0046, +#define RTASE_RX_MAX_FETCH_DESC_MASK GENMASK(15, 11) +#define RTASE_RX_NEW_DESC_FORMAT_EN BIT(8) +#define RTASE_OUTER_VLAN_DETAG_EN BIT(7) +#define RTASE_INNER_VLAN_DETAG_EN BIT(6) +#define RTASE_PCIE_NEW_FLOW BIT(2) +#define RTASE_PCIE_RELOAD_EN BIT(0) + + RTASE_EEM = 0x0050, +#define RTASE_EEM_UNLOCK 0xC0 + + RTASE_TDFNR = 0x0057, + RTASE_TPPOLL = 0x0090, + RTASE_PDR = 0x00B0, + RTASE_FIFOR = 0x00D3, +#define RTASE_TX_FIFO_EMPTY BIT(5) +#define RTASE_RX_FIFO_EMPTY BIT(4) + + RTASE_RMS = 0x00DA, + RTASE_CPLUS_CMD = 0x00E0, +#define RTASE_FORCE_RXFLOW_EN BIT(11) +#define RTASE_FORCE_TXFLOW_EN BIT(10) +#define RTASE_RX_CHKSUM BIT(5) + + RTASE_GPHY_STD_00 = 0x6024, +#define RTASE_RXFLOW_EN BIT(7) +#define RTASE_TXFLOW_EN BIT(6) + + RTASE_Q0_RX_DESC_ADDR0 = 0x00E4, + RTASE_Q0_RX_DESC_ADDR4 = 0x00E8, + RTASE_Q1_RX_DESC_ADDR0 = 0x4000, + RTASE_Q1_RX_DESC_ADDR4 = 0x4004, + RTASE_MTPS = 0x00EC, +#define RTASE_TAG_NUM_SEL_MASK GENMASK(10, 8) + + RTASE_MISC = 0x00F2, +#define RTASE_RX_DV_GATE_EN BIT(3) + + RTASE_TFUN_CTRL = 0x0400, +#define RTASE_TX_NEW_DESC_FORMAT_EN BIT(0) + + RTASE_TX_CONFIG_1 = 0x203E, +#define RTASE_TC_MODE_MASK GENMASK(11, 10) + + RTASE_TOKSEL = 0x2046, + RTASE_TXQCRDT_0 = 0x2500, + RTASE_RFIFONFULL = 0x4406, + RTASE_INT_MITI_TX = 0x0A00, + RTASE_INT_MITI_RX = 0x0A80, + + RTASE_VLAN_ENTRY_0 = 0xAC80, +}; + +enum rtase_desc_status_bit { + RTASE_DESC_OWN = BIT(31), /* Descriptor is owned by NIC */ + RTASE_RING_END = BIT(30), /* End of descriptor ring */ +}; + +enum rtase_sw_flag_content { + RTASE_SWF_MSI_ENABLED = BIT(1), + RTASE_SWF_MSIX_ENABLED = BIT(2), +}; + +#define RSVD_MASK 0x3FFFC000 + +struct rtase_tx_desc { + __le32 opts1; + __le32 opts2; + __le64 addr; + __le32 opts3; + __le32 reserved1; + __le32 reserved2; + __le32 reserved3; +} __packed; + +/*------ offset 0 of tx descriptor ------*/ +#define RTASE_TX_FIRST_FRAG BIT(29) /* Tx First segment of a packet */ +#define RTASE_TX_LAST_FRAG BIT(28) /* Tx Final segment of a packet */ +#define RTASE_GIANT_SEND_V4 BIT(26) /* TCP Giant Send Offload V4 (GSOv4) */ +#define RTASE_GIANT_SEND_V6 BIT(25) /* TCP Giant Send Offload V6 (GSOv6) */ +#define RTASE_TX_VLAN_TAG BIT(17) /* Add VLAN tag */ + +/*------ offset 4 of tx descriptor ------*/ +#define RTASE_TX_UDPCS_C BIT(31) /* Calculate UDP/IP checksum */ +#define RTASE_TX_TCPCS_C BIT(30) /* Calculate TCP/IP checksum */ +#define RTASE_TX_IPCS_C BIT(29) /* Calculate IP checksum */ +#define RTASE_TX_IPV6F_C BIT(28) /* Indicate it is an IPv6 packet */ + +union rtase_rx_desc { + struct { + __le64 header_buf_addr; + __le32 reserved1; + __le32 opts_header_len; + __le64 addr; + __le32 reserved2; + __le32 opts1; + } __packed desc_cmd; + + struct { + __le32 reserved1; + __le32 reserved2; + __le32 rss; + __le32 opts4; + __le32 reserved3; + __le32 opts3; + __le32 opts2; + __le32 opts1; + } __packed desc_status; +} __packed; + +/*------ offset 28 of rx descriptor ------*/ +#define RTASE_RX_FIRST_FRAG BIT(25) /* Rx First segment of a packet */ +#define RTASE_RX_LAST_FRAG BIT(24) /* Rx Final segment of a packet */ +#define RTASE_RX_RES BIT(20) +#define RTASE_RX_RUNT BIT(19) +#define RTASE_RX_RWT BIT(18) +#define RTASE_RX_CRC BIT(17) +#define RTASE_RX_V6F BIT(31) +#define RTASE_RX_V4F BIT(30) +#define RTASE_RX_UDPT BIT(29) +#define RTASE_RX_TCPT BIT(28) +#define RTASE_RX_IPF BIT(26) /* IP checksum failed */ +#define RTASE_RX_UDPF BIT(25) /* UDP/IP checksum failed */ +#define RTASE_RX_TCPF BIT(24) /* TCP/IP checksum failed */ +#define RTASE_RX_VLAN_TAG BIT(16) /* VLAN tag available */ + +#define RTASE_NUM_DESC 1024 +#define RTASE_TX_BUDGET_DEFAULT 256 +#define RTASE_TX_RING_DESC_SIZE (RTASE_NUM_DESC * sizeof(struct rtase_tx_desc)) +#define RTASE_RX_RING_DESC_SIZE (RTASE_NUM_DESC * sizeof(union rtase_rx_desc)) +#define RTASE_TX_STOP_THRS (MAX_SKB_FRAGS + 1) +#define RTASE_TX_START_THRS (2 * RTASE_TX_STOP_THRS) +#define RTASE_VLAN_TAG_MASK GENMASK(15, 0) +#define RTASE_RX_PKT_SIZE_MASK GENMASK(13, 0) + +/* txqos hardware definitions */ +#define RTASE_1T_CLOCK 64 +#define RTASE_1T_POWER 10000000 +#define RTASE_IDLESLOPE_INT_SHIFT 25 +#define RTASE_IDLESLOPE_INT_MASK GENMASK(31, 25) + +#define RTASE_IVEC_NAME_SIZE (IFNAMSIZ + 10) + +struct rtase_int_vector { + struct rtase_private *tp; + unsigned int irq; + char name[RTASE_IVEC_NAME_SIZE]; + u16 index; + u16 imr_addr; + u16 isr_addr; + u32 imr; + struct list_head ring_list; + struct napi_struct napi; + int (*poll)(struct napi_struct *napi, int budget); +}; + +struct rtase_ring { + struct rtase_int_vector *ivec; + void *desc; + dma_addr_t phy_addr; + u32 cur_idx; + u32 dirty_idx; + u16 index; + u8 type; + + struct sk_buff *skbuff[RTASE_NUM_DESC]; + void *data_buf[RTASE_NUM_DESC]; + union { + u32 len[RTASE_NUM_DESC]; + dma_addr_t data_phy_addr[RTASE_NUM_DESC]; + } mis; + + struct list_head ring_entry; + int (*ring_handler)(struct rtase_ring *ring, int budget); + u64 alloc_fail; +}; + +struct rtase_txqos { + int hicredit; + int locredit; + int idleslope; + int sendslope; +}; + +struct rtase_stats { + u64 tx_dropped; + u64 rx_dropped; + u64 multicast; + u64 rx_errors; + u64 rx_length_errors; + u64 rx_crc_errors; +}; + +struct rtase_private { + void __iomem *mmio_addr; + u32 sw_flag; + + struct pci_dev *pdev; + struct net_device *dev; + u32 rx_buf_sz; + + struct page_pool *page_pool; + struct rtase_ring tx_ring[RTASE_NUM_TX_QUEUE]; + struct rtase_txqos tx_qos[RTASE_NUM_TX_QUEUE]; + struct rtase_ring rx_ring[RTASE_NUM_RX_QUEUE]; + struct rtase_counters *tally_vaddr; + dma_addr_t tally_paddr; + + u32 vlan_filter_ctrl; + u16 vlan_filter_vid[RTASE_VLAN_FILTER_ENTRY_NUM]; + + struct msix_entry msix_entry[RTASE_NUM_MSIX]; + struct rtase_int_vector int_vector[RTASE_NUM_MSIX]; + + struct rtase_stats stats; + + u16 tx_queue_ctrl; + u16 func_tx_queue_num; + u16 func_rx_queue_num; + u16 int_nums; + u16 tx_int_mit; + u16 rx_int_mit; + + u32 hw_ver; +}; + +#define RTASE_LSO_64K 64000 + +#define RTASE_NIC_MAX_PHYS_BUF_COUNT_LSO2 (16 * 4) + +#define RTASE_TCPHO_MASK GENMASK(24, 18) + +#define RTASE_MSS_MASK GENMASK(28, 18) + +#endif /* RTASE_H */ diff --git a/drivers/net/ethernet/realtek/rtase/rtase_main.c b/drivers/net/ethernet/realtek/rtase/rtase_main.c new file mode 100644 index 000000000..255667775 --- /dev/null +++ b/drivers/net/ethernet/realtek/rtase/rtase_main.c @@ -0,0 +1,2406 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * rtase is the Linux device driver released for Realtek Automotive Switch + * controllers with PCI-Express interface. + * + * Copyright(c) 2024 Realtek Semiconductor Corp. + * + * Below is a simplified block diagram of the chip and its relevant interfaces. + * + * ************************* + * * * + * * CPU network device * + * * * + * * +-------------+ * + * * | PCIE Host | * + * ***********++************ + * || + * PCIE + * || + * ********************++********************** + * * | PCIE Endpoint | * + * * +---------------+ * + * * | GMAC | * + * * +--++--+ Realtek * + * * || RTL90xx Series * + * * || * + * * +-------------++----------------+ * + * * | | MAC | | * + * * | +-----+ | * + * * | | * + * * | Ethernet Switch Core | * + * * | | * + * * | +-----+ +-----+ | * + * * | | MAC |...........| MAC | | * + * * +---+-----+-----------+-----+---+ * + * * | PHY |...........| PHY | * + * * +--++-+ +--++-+ * + * *************||****************||*********** + * + * The block of the Realtek RTL90xx series is our entire chip architecture, + * the GMAC is connected to the switch core, and there is no PHY in between. + * In addition, this driver is mainly used to control GMAC, but does not + * control the switch core, so it is not the same as DSA. Linux only plays + * the role of a normal leaf node in this model. + */ + +#include <linux/crc32.h> +#include <linux/dma-mapping.h> +#include <linux/etherdevice.h> +#include <linux/if_vlan.h> +#include <linux/in.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <linux/mdio.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/pci.h> +#include <linux/pm_runtime.h> +#include <linux/prefetch.h> +#include <linux/rtnetlink.h> +#include <linux/tcp.h> +#include <asm/irq.h> +#include <net/ip6_checksum.h> +#include <net/netdev_queues.h> +#include <net/page_pool/helpers.h> +#include <net/pkt_cls.h> + +#include "rtase.h" + +#define RTK_OPTS1_DEBUG_VALUE 0x0BADBEEF +#define RTK_MAGIC_NUMBER 0x0BADBADBADBADBAD + +static const struct pci_device_id rtase_pci_tbl[] = { + {PCI_VDEVICE(REALTEK, 0x906A)}, + {} +}; + +MODULE_DEVICE_TABLE(pci, rtase_pci_tbl); + +MODULE_AUTHOR("Realtek ARD Software Team"); +MODULE_DESCRIPTION("Network Driver for the PCIe interface of Realtek Automotive Ethernet Switch"); +MODULE_LICENSE("Dual BSD/GPL"); + +struct rtase_counters { + __le64 tx_packets; + __le64 rx_packets; + __le64 tx_errors; + __le32 rx_errors; + __le16 rx_missed; + __le16 align_errors; + __le32 tx_one_collision; + __le32 tx_multi_collision; + __le64 rx_unicast; + __le64 rx_broadcast; + __le32 rx_multicast; + __le16 tx_aborted; + __le16 tx_underrun; +} __packed; + +static void rtase_w8(const struct rtase_private *tp, u16 reg, u8 val8) +{ + writeb(val8, tp->mmio_addr + reg); +} + +static void rtase_w16(const struct rtase_private *tp, u16 reg, u16 val16) +{ + writew(val16, tp->mmio_addr + reg); +} + +static void rtase_w32(const struct rtase_private *tp, u16 reg, u32 val32) +{ + writel(val32, tp->mmio_addr + reg); +} + +static u8 rtase_r8(const struct rtase_private *tp, u16 reg) +{ + return readb(tp->mmio_addr + reg); +} + +static u16 rtase_r16(const struct rtase_private *tp, u16 reg) +{ + return readw(tp->mmio_addr + reg); +} + +static u32 rtase_r32(const struct rtase_private *tp, u16 reg) +{ + return readl(tp->mmio_addr + reg); +} + +static void rtase_free_desc(struct rtase_private *tp) +{ + struct pci_dev *pdev = tp->pdev; + u32 i; + + for (i = 0; i < tp->func_tx_queue_num; i++) { + if (!tp->tx_ring[i].desc) + continue; + + dma_free_coherent(&pdev->dev, RTASE_TX_RING_DESC_SIZE, + tp->tx_ring[i].desc, + tp->tx_ring[i].phy_addr); + tp->tx_ring[i].desc = NULL; + } + + for (i = 0; i < tp->func_rx_queue_num; i++) { + if (!tp->rx_ring[i].desc) + continue; + + dma_free_coherent(&pdev->dev, RTASE_RX_RING_DESC_SIZE, + tp->rx_ring[i].desc, + tp->rx_ring[i].phy_addr); + tp->rx_ring[i].desc = NULL; + } +} + +static int rtase_alloc_desc(struct rtase_private *tp) +{ + struct pci_dev *pdev = tp->pdev; + u32 i; + + /* rx and tx descriptors needs 256 bytes alignment. + * dma_alloc_coherent provides more. + */ + for (i = 0; i < tp->func_tx_queue_num; i++) { + tp->tx_ring[i].desc = + dma_alloc_coherent(&pdev->dev, + RTASE_TX_RING_DESC_SIZE, + &tp->tx_ring[i].phy_addr, + GFP_KERNEL); + if (!tp->tx_ring[i].desc) + goto err_out; + } + + for (i = 0; i < tp->func_rx_queue_num; i++) { + tp->rx_ring[i].desc = + dma_alloc_coherent(&pdev->dev, + RTASE_RX_RING_DESC_SIZE, + &tp->rx_ring[i].phy_addr, + GFP_KERNEL); + if (!tp->rx_ring[i].desc) + goto err_out; + } + + return 0; + +err_out: + rtase_free_desc(tp); + return -ENOMEM; +} + +static void rtase_unmap_tx_skb(struct pci_dev *pdev, u32 len, + struct rtase_tx_desc *desc) +{ + dma_unmap_single(&pdev->dev, le64_to_cpu(desc->addr), len, + DMA_TO_DEVICE); + desc->opts1 = cpu_to_le32(RTK_OPTS1_DEBUG_VALUE); + desc->opts2 = 0x00; + desc->addr = cpu_to_le64(RTK_MAGIC_NUMBER); +} + +static void rtase_tx_clear_range(struct rtase_ring *ring, u32 start, u32 n) +{ + struct rtase_tx_desc *desc_base = ring->desc; + struct rtase_private *tp = ring->ivec->tp; + u32 i; + + for (i = 0; i < n; i++) { + u32 entry = (start + i) % RTASE_NUM_DESC; + struct rtase_tx_desc *desc = desc_base + entry; + u32 len = ring->mis.len[entry]; + struct sk_buff *skb; + + if (len == 0) + continue; + + rtase_unmap_tx_skb(tp->pdev, len, desc); + ring->mis.len[entry] = 0; + skb = ring->skbuff[entry]; + if (!skb) + continue; + + tp->stats.tx_dropped++; + dev_kfree_skb_any(skb); + ring->skbuff[entry] = NULL; + } +} + +static void rtase_tx_clear(struct rtase_private *tp) +{ + struct rtase_ring *ring; + u16 i; + + for (i = 0; i < tp->func_tx_queue_num; i++) { + ring = &tp->tx_ring[i]; + rtase_tx_clear_range(ring, ring->dirty_idx, RTASE_NUM_DESC); + ring->cur_idx = 0; + ring->dirty_idx = 0; + + netdev_tx_reset_subqueue(tp->dev, i); + } +} + +static void rtase_mark_to_asic(union rtase_rx_desc *desc, u32 rx_buf_sz) +{ + u32 eor = le32_to_cpu(desc->desc_cmd.opts1) & RTASE_RING_END; + + desc->desc_status.opts2 = 0; + /* force memory writes to complete before releasing descriptor */ + dma_wmb(); + WRITE_ONCE(desc->desc_cmd.opts1, + cpu_to_le32(RTASE_DESC_OWN | eor | rx_buf_sz)); +} + +static u32 rtase_tx_avail(struct rtase_ring *ring) +{ + return READ_ONCE(ring->dirty_idx) + RTASE_NUM_DESC - + READ_ONCE(ring->cur_idx); +} + +static int tx_handler(struct rtase_ring *ring, int budget) +{ + const struct rtase_private *tp = ring->ivec->tp; + struct net_device *dev = tp->dev; + u32 dirty_tx, tx_left; + u32 bytes_compl = 0; + u32 pkts_compl = 0; + int workdone = 0; + + dirty_tx = ring->dirty_idx; + tx_left = READ_ONCE(ring->cur_idx) - dirty_tx; + + while (tx_left > 0) { + u32 entry = dirty_tx % RTASE_NUM_DESC; + struct rtase_tx_desc *desc = ring->desc + + sizeof(struct rtase_tx_desc) * entry; + u32 status; + + status = le32_to_cpu(desc->opts1); + + if (status & RTASE_DESC_OWN) + break; + + rtase_unmap_tx_skb(tp->pdev, ring->mis.len[entry], desc); + ring->mis.len[entry] = 0; + if (ring->skbuff[entry]) { + pkts_compl++; + bytes_compl += ring->skbuff[entry]->len; + napi_consume_skb(ring->skbuff[entry], budget); + ring->skbuff[entry] = NULL; + } + + dirty_tx++; + tx_left--; + workdone++; + + if (workdone == RTASE_TX_BUDGET_DEFAULT) + break; + } + + if (ring->dirty_idx != dirty_tx) { + dev_sw_netstats_tx_add(dev, pkts_compl, bytes_compl); + WRITE_ONCE(ring->dirty_idx, dirty_tx); + + netif_subqueue_completed_wake(dev, ring->index, pkts_compl, + bytes_compl, + rtase_tx_avail(ring), + RTASE_TX_START_THRS); + + if (ring->cur_idx != dirty_tx) + rtase_w8(tp, RTASE_TPPOLL, BIT(ring->index)); + } + + return 0; +} + +static void rtase_tx_desc_init(struct rtase_private *tp, u16 idx) +{ + struct rtase_ring *ring = &tp->tx_ring[idx]; + struct rtase_tx_desc *desc; + u32 i; + + memset(ring->desc, 0x0, RTASE_TX_RING_DESC_SIZE); + memset(ring->skbuff, 0x0, sizeof(ring->skbuff)); + ring->cur_idx = 0; + ring->dirty_idx = 0; + ring->index = idx; + ring->type = NETDEV_QUEUE_TYPE_TX; + ring->alloc_fail = 0; + + for (i = 0; i < RTASE_NUM_DESC; i++) { + ring->mis.len[i] = 0; + if ((RTASE_NUM_DESC - 1) == i) { + desc = ring->desc + sizeof(struct rtase_tx_desc) * i; + desc->opts1 = cpu_to_le32(RTASE_RING_END); + } + } + + ring->ring_handler = tx_handler; + if (idx < 4) { + ring->ivec = &tp->int_vector[idx]; + list_add_tail(&ring->ring_entry, + &tp->int_vector[idx].ring_list); + } else { + ring->ivec = &tp->int_vector[0]; + list_add_tail(&ring->ring_entry, &tp->int_vector[0].ring_list); + } + + netif_queue_set_napi(tp->dev, ring->index, + ring->type, &ring->ivec->napi); +} + +static void rtase_map_to_asic(union rtase_rx_desc *desc, dma_addr_t mapping, + u32 rx_buf_sz) +{ + desc->desc_cmd.addr = cpu_to_le64(mapping); + + rtase_mark_to_asic(desc, rx_buf_sz); +} + +static void rtase_make_unusable_by_asic(union rtase_rx_desc *desc) +{ + desc->desc_cmd.addr = cpu_to_le64(RTK_MAGIC_NUMBER); + desc->desc_cmd.opts1 &= ~cpu_to_le32(RTASE_DESC_OWN | RSVD_MASK); +} + +static int rtase_alloc_rx_data_buf(struct rtase_ring *ring, + void **p_data_buf, + union rtase_rx_desc *desc, + dma_addr_t *rx_phy_addr) +{ + struct rtase_int_vector *ivec = ring->ivec; + const struct rtase_private *tp = ivec->tp; + dma_addr_t mapping; + struct page *page; + + page = page_pool_dev_alloc_pages(tp->page_pool); + if (!page) { + ring->alloc_fail++; + goto err_out; + } + + *p_data_buf = page_address(page); + mapping = page_pool_get_dma_addr(page); + *rx_phy_addr = mapping; + rtase_map_to_asic(desc, mapping, tp->rx_buf_sz); + + return 0; + +err_out: + rtase_make_unusable_by_asic(desc); + + return -ENOMEM; +} + +static u32 rtase_rx_ring_fill(struct rtase_ring *ring, u32 ring_start, + u32 ring_end) +{ + union rtase_rx_desc *desc_base = ring->desc; + u32 cur; + + for (cur = ring_start; ring_end - cur > 0; cur++) { + u32 i = cur % RTASE_NUM_DESC; + union rtase_rx_desc *desc = desc_base + i; + int ret; + + if (ring->data_buf[i]) + continue; + + ret = rtase_alloc_rx_data_buf(ring, &ring->data_buf[i], desc, + &ring->mis.data_phy_addr[i]); + if (ret) + break; + } + + return cur - ring_start; +} + +static void rtase_mark_as_last_descriptor(union rtase_rx_desc *desc) +{ + desc->desc_cmd.opts1 |= cpu_to_le32(RTASE_RING_END); +} + +static void rtase_rx_ring_clear(struct page_pool *page_pool, + struct rtase_ring *ring) +{ + union rtase_rx_desc *desc; + struct page *page; + u32 i; + + for (i = 0; i < RTASE_NUM_DESC; i++) { + desc = ring->desc + sizeof(union rtase_rx_desc) * i; + page = virt_to_head_page(ring->data_buf[i]); + + if (ring->data_buf[i]) + page_pool_put_full_page(page_pool, page, true); + + rtase_make_unusable_by_asic(desc); + } +} + +static int rtase_fragmented_frame(u32 status) +{ + return (status & (RTASE_RX_FIRST_FRAG | RTASE_RX_LAST_FRAG)) != + (RTASE_RX_FIRST_FRAG | RTASE_RX_LAST_FRAG); +} + +static void rtase_rx_csum(const struct rtase_private *tp, struct sk_buff *skb, + const union rtase_rx_desc *desc) +{ + u32 opts2 = le32_to_cpu(desc->desc_status.opts2); + + /* rx csum offload */ + if (((opts2 & RTASE_RX_V4F) && !(opts2 & RTASE_RX_IPF)) || + (opts2 & RTASE_RX_V6F)) { + if (((opts2 & RTASE_RX_TCPT) && !(opts2 & RTASE_RX_TCPF)) || + ((opts2 & RTASE_RX_UDPT) && !(opts2 & RTASE_RX_UDPF))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb->ip_summed = CHECKSUM_NONE; + } else { + skb->ip_summed = CHECKSUM_NONE; + } +} + +static void rtase_rx_vlan_skb(union rtase_rx_desc *desc, struct sk_buff *skb) +{ + u32 opts2 = le32_to_cpu(desc->desc_status.opts2); + + if (!(opts2 & RTASE_RX_VLAN_TAG)) + return; + + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), + swab16(opts2 & RTASE_VLAN_TAG_MASK)); +} + +static void rtase_rx_skb(const struct rtase_ring *ring, struct sk_buff *skb) +{ + struct rtase_int_vector *ivec = ring->ivec; + + napi_gro_receive(&ivec->napi, skb); +} + +static int rx_handler(struct rtase_ring *ring, int budget) +{ + union rtase_rx_desc *desc_base = ring->desc; + u32 pkt_size, cur_rx, delta, entry, status; + struct rtase_private *tp = ring->ivec->tp; + struct net_device *dev = tp->dev; + union rtase_rx_desc *desc; + struct sk_buff *skb; + int workdone = 0; + + cur_rx = ring->cur_idx; + entry = cur_rx % RTASE_NUM_DESC; + desc = &desc_base[entry]; + + while (workdone < budget) { + status = le32_to_cpu(desc->desc_status.opts1); + + if (status & RTASE_DESC_OWN) + break; + + /* This barrier is needed to keep us from reading + * any other fields out of the rx descriptor until + * we know the status of RTASE_DESC_OWN + */ + dma_rmb(); + + if (unlikely(status & RTASE_RX_RES)) { + if (net_ratelimit()) + netdev_warn(dev, "Rx ERROR. status = %08x\n", + status); + + tp->stats.rx_errors++; + + if (status & (RTASE_RX_RWT | RTASE_RX_RUNT)) + tp->stats.rx_length_errors++; + + if (status & RTASE_RX_CRC) + tp->stats.rx_crc_errors++; + + if (dev->features & NETIF_F_RXALL) + goto process_pkt; + + rtase_mark_to_asic(desc, tp->rx_buf_sz); + goto skip_process_pkt; + } + +process_pkt: + pkt_size = status & RTASE_RX_PKT_SIZE_MASK; + if (likely(!(dev->features & NETIF_F_RXFCS))) + pkt_size -= ETH_FCS_LEN; + + /* The driver does not support incoming fragmented frames. + * They are seen as a symptom of over-mtu sized frames. + */ + if (unlikely(rtase_fragmented_frame(status))) { + tp->stats.rx_dropped++; + tp->stats.rx_length_errors++; + rtase_mark_to_asic(desc, tp->rx_buf_sz); + goto skip_process_pkt; + } + + dma_sync_single_for_cpu(&tp->pdev->dev, + ring->mis.data_phy_addr[entry], + tp->rx_buf_sz, DMA_FROM_DEVICE); + + skb = build_skb(ring->data_buf[entry], PAGE_SIZE); + if (!skb) { + tp->stats.rx_dropped++; + rtase_mark_to_asic(desc, tp->rx_buf_sz); + goto skip_process_pkt; + } + ring->data_buf[entry] = NULL; + + if (dev->features & NETIF_F_RXCSUM) + rtase_rx_csum(tp, skb, desc); + + skb_put(skb, pkt_size); + skb_mark_for_recycle(skb); + skb->protocol = eth_type_trans(skb, dev); + + if (skb->pkt_type == PACKET_MULTICAST) + tp->stats.multicast++; + + rtase_rx_vlan_skb(desc, skb); + rtase_rx_skb(ring, skb); + + dev_sw_netstats_rx_add(dev, pkt_size); + +skip_process_pkt: + workdone++; + cur_rx++; + entry = cur_rx % RTASE_NUM_DESC; + desc = ring->desc + sizeof(union rtase_rx_desc) * entry; + } + + ring->cur_idx = cur_rx; + delta = rtase_rx_ring_fill(ring, ring->dirty_idx, ring->cur_idx); + ring->dirty_idx += delta; + + return workdone; +} + +static void rtase_rx_desc_init(struct rtase_private *tp, u16 idx) +{ + struct rtase_ring *ring = &tp->rx_ring[idx]; + u16 i; + + memset(ring->desc, 0x0, RTASE_RX_RING_DESC_SIZE); + memset(ring->data_buf, 0x0, sizeof(ring->data_buf)); + ring->cur_idx = 0; + ring->dirty_idx = 0; + ring->index = idx; + ring->type = NETDEV_QUEUE_TYPE_RX; + ring->alloc_fail = 0; + + for (i = 0; i < RTASE_NUM_DESC; i++) + ring->mis.data_phy_addr[i] = 0; + + ring->ring_handler = rx_handler; + ring->ivec = &tp->int_vector[idx]; + netif_queue_set_napi(tp->dev, ring->index, + ring->type, &ring->ivec->napi); + list_add_tail(&ring->ring_entry, &tp->int_vector[idx].ring_list); +} + +static void rtase_rx_clear(struct rtase_private *tp) +{ + u32 i; + + for (i = 0; i < tp->func_rx_queue_num; i++) + rtase_rx_ring_clear(tp->page_pool, &tp->rx_ring[i]); + + page_pool_destroy(tp->page_pool); + tp->page_pool = NULL; +} + +static int rtase_init_ring(const struct net_device *dev) +{ + struct rtase_private *tp = netdev_priv(dev); + struct page_pool_params pp_params = { 0 }; + struct page_pool *page_pool; + u32 num; + u16 i; + + pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV; + pp_params.order = 0; + pp_params.pool_size = RTASE_NUM_DESC * tp->func_rx_queue_num; + pp_params.nid = dev_to_node(&tp->pdev->dev); + pp_params.dev = &tp->pdev->dev; + pp_params.dma_dir = DMA_FROM_DEVICE; + pp_params.max_len = PAGE_SIZE; + pp_params.offset = 0; + + page_pool = page_pool_create(&pp_params); + if (IS_ERR(page_pool)) { + netdev_err(tp->dev, "failed to create page pool\n"); + return -ENOMEM; + } + + tp->page_pool = page_pool; + + for (i = 0; i < tp->func_tx_queue_num; i++) + rtase_tx_desc_init(tp, i); + + for (i = 0; i < tp->func_rx_queue_num; i++) { + rtase_rx_desc_init(tp, i); + + num = rtase_rx_ring_fill(&tp->rx_ring[i], 0, RTASE_NUM_DESC); + if (num != RTASE_NUM_DESC) + goto err_out; + + rtase_mark_as_last_descriptor(tp->rx_ring[i].desc + + sizeof(union rtase_rx_desc) * + (RTASE_NUM_DESC - 1)); + } + + return 0; + +err_out: + rtase_rx_clear(tp); + return -ENOMEM; +} + +static void rtase_interrupt_mitigation(const struct rtase_private *tp) +{ + u32 i; + + for (i = 0; i < tp->func_tx_queue_num; i++) + rtase_w16(tp, RTASE_INT_MITI_TX + i * 2, tp->tx_int_mit); + + for (i = 0; i < tp->func_rx_queue_num; i++) + rtase_w16(tp, RTASE_INT_MITI_RX + i * 2, tp->rx_int_mit); +} + +static void rtase_tally_counter_addr_fill(const struct rtase_private *tp) +{ + rtase_w32(tp, RTASE_DTCCR4, upper_32_bits(tp->tally_paddr)); + rtase_w32(tp, RTASE_DTCCR0, lower_32_bits(tp->tally_paddr)); +} + +static void rtase_tally_counter_clear(const struct rtase_private *tp) +{ + u32 cmd = lower_32_bits(tp->tally_paddr); + + rtase_w32(tp, RTASE_DTCCR4, upper_32_bits(tp->tally_paddr)); + rtase_w32(tp, RTASE_DTCCR0, cmd | RTASE_COUNTER_RESET); +} + +static void rtase_desc_addr_fill(const struct rtase_private *tp) +{ + const struct rtase_ring *ring; + u16 i, cmd, val; + int err; + + for (i = 0; i < tp->func_tx_queue_num; i++) { + ring = &tp->tx_ring[i]; + + rtase_w32(tp, RTASE_TX_DESC_ADDR0, + lower_32_bits(ring->phy_addr)); + rtase_w32(tp, RTASE_TX_DESC_ADDR4, + upper_32_bits(ring->phy_addr)); + + cmd = i | RTASE_TX_DESC_CMD_WE | RTASE_TX_DESC_CMD_CS; + rtase_w16(tp, RTASE_TX_DESC_COMMAND, cmd); + + err = read_poll_timeout(rtase_r16, val, + !(val & RTASE_TX_DESC_CMD_CS), 10, + 1000, false, tp, + RTASE_TX_DESC_COMMAND); + + if (err == -ETIMEDOUT) + netdev_err(tp->dev, + "error occurred in fill tx descriptor\n"); + } + + for (i = 0; i < tp->func_rx_queue_num; i++) { + ring = &tp->rx_ring[i]; + + if (i == 0) { + rtase_w32(tp, RTASE_Q0_RX_DESC_ADDR0, + lower_32_bits(ring->phy_addr)); + rtase_w32(tp, RTASE_Q0_RX_DESC_ADDR4, + upper_32_bits(ring->phy_addr)); + } else { + rtase_w32(tp, (RTASE_Q1_RX_DESC_ADDR0 + ((i - 1) * 8)), + lower_32_bits(ring->phy_addr)); + rtase_w32(tp, (RTASE_Q1_RX_DESC_ADDR4 + ((i - 1) * 8)), + upper_32_bits(ring->phy_addr)); + } + } +} + +static void rtase_hw_set_features(const struct net_device *dev, + netdev_features_t features) +{ + const struct rtase_private *tp = netdev_priv(dev); + u16 rx_config, val; + + rx_config = rtase_r16(tp, RTASE_RX_CONFIG_0); + if (features & NETIF_F_RXALL) + rx_config |= (RTASE_ACCEPT_ERR | RTASE_ACCEPT_RUNT); + else + rx_config &= ~(RTASE_ACCEPT_ERR | RTASE_ACCEPT_RUNT); + + rtase_w16(tp, RTASE_RX_CONFIG_0, rx_config); + + val = rtase_r16(tp, RTASE_CPLUS_CMD); + if (features & NETIF_F_RXCSUM) + rtase_w16(tp, RTASE_CPLUS_CMD, val | RTASE_RX_CHKSUM); + else + rtase_w16(tp, RTASE_CPLUS_CMD, val & ~RTASE_RX_CHKSUM); + + rx_config = rtase_r16(tp, RTASE_RX_CONFIG_1); + if (dev->features & NETIF_F_HW_VLAN_CTAG_RX) + rx_config |= (RTASE_INNER_VLAN_DETAG_EN | + RTASE_OUTER_VLAN_DETAG_EN); + else + rx_config &= ~(RTASE_INNER_VLAN_DETAG_EN | + RTASE_OUTER_VLAN_DETAG_EN); + + rtase_w16(tp, RTASE_RX_CONFIG_1, rx_config); +} + +static void rtase_hw_set_rx_packet_filter(struct net_device *dev) +{ + u32 mc_filter[2] = { 0xFFFFFFFF, 0xFFFFFFFF }; + struct rtase_private *tp = netdev_priv(dev); + u16 rx_mode; + + rx_mode = rtase_r16(tp, RTASE_RX_CONFIG_0) & ~RTASE_ACCEPT_MASK; + rx_mode |= RTASE_ACCEPT_BROADCAST | RTASE_ACCEPT_MYPHYS; + + if (dev->flags & IFF_PROMISC) { + rx_mode |= RTASE_ACCEPT_MULTICAST | RTASE_ACCEPT_ALLPHYS; + } else if (dev->flags & IFF_ALLMULTI) { + rx_mode |= RTASE_ACCEPT_MULTICAST; + } else { + struct netdev_hw_addr *hw_addr; + + mc_filter[0] = 0; + mc_filter[1] = 0; + + netdev_for_each_mc_addr(hw_addr, dev) { + u32 bit_nr = eth_hw_addr_crc(hw_addr); + u32 idx = u32_get_bits(bit_nr, BIT(31)); + u32 bit = u32_get_bits(bit_nr, + RTASE_MULTICAST_FILTER_MASK); + + mc_filter[idx] |= BIT(bit); + rx_mode |= RTASE_ACCEPT_MULTICAST; + } + } + + if (dev->features & NETIF_F_RXALL) + rx_mode |= RTASE_ACCEPT_ERR | RTASE_ACCEPT_RUNT; + + rtase_w32(tp, RTASE_MAR0, swab32(mc_filter[1])); + rtase_w32(tp, RTASE_MAR1, swab32(mc_filter[0])); + rtase_w16(tp, RTASE_RX_CONFIG_0, rx_mode); +} + +static void rtase_irq_dis_and_clear(const struct rtase_private *tp) +{ + const struct rtase_int_vector *ivec = &tp->int_vector[0]; + u32 val1; + u16 val2; + u8 i; + + rtase_w32(tp, ivec->imr_addr, 0); + val1 = rtase_r32(tp, ivec->isr_addr); + rtase_w32(tp, ivec->isr_addr, val1); + + for (i = 1; i < tp->int_nums; i++) { + ivec = &tp->int_vector[i]; + rtase_w16(tp, ivec->imr_addr, 0); + val2 = rtase_r16(tp, ivec->isr_addr); + rtase_w16(tp, ivec->isr_addr, val2); + } +} + +static void rtase_poll_timeout(const struct rtase_private *tp, u32 cond, + u32 sleep_us, u64 timeout_us, u16 reg) +{ + int err; + u8 val; + + err = read_poll_timeout(rtase_r8, val, val & cond, sleep_us, + timeout_us, false, tp, reg); + + if (err == -ETIMEDOUT) + netdev_err(tp->dev, "poll reg 0x00%x timeout\n", reg); +} + +static void rtase_nic_reset(const struct net_device *dev) +{ + const struct rtase_private *tp = netdev_priv(dev); + u16 rx_config; + u8 val; + + rx_config = rtase_r16(tp, RTASE_RX_CONFIG_0); + rtase_w16(tp, RTASE_RX_CONFIG_0, rx_config & ~RTASE_ACCEPT_MASK); + + val = rtase_r8(tp, RTASE_MISC); + rtase_w8(tp, RTASE_MISC, val | RTASE_RX_DV_GATE_EN); + + val = rtase_r8(tp, RTASE_CHIP_CMD); + rtase_w8(tp, RTASE_CHIP_CMD, val | RTASE_STOP_REQ); + mdelay(2); + + rtase_poll_timeout(tp, RTASE_STOP_REQ_DONE, 100, 150000, + RTASE_CHIP_CMD); + + rtase_poll_timeout(tp, RTASE_TX_FIFO_EMPTY, 100, 100000, + RTASE_FIFOR); + + rtase_poll_timeout(tp, RTASE_RX_FIFO_EMPTY, 100, 100000, + RTASE_FIFOR); + + val = rtase_r8(tp, RTASE_CHIP_CMD); + rtase_w8(tp, RTASE_CHIP_CMD, val & ~(RTASE_TE | RTASE_RE)); + val = rtase_r8(tp, RTASE_CHIP_CMD); + rtase_w8(tp, RTASE_CHIP_CMD, val & ~RTASE_STOP_REQ); + + rtase_w16(tp, RTASE_RX_CONFIG_0, rx_config); +} + +static void rtase_hw_reset(const struct net_device *dev) +{ + const struct rtase_private *tp = netdev_priv(dev); + + rtase_irq_dis_and_clear(tp); + + rtase_nic_reset(dev); +} + +static void rtase_set_rx_queue(const struct rtase_private *tp) +{ + u16 reg_data; + + reg_data = rtase_r16(tp, RTASE_FCR); + switch (tp->func_rx_queue_num) { + case 1: + u16p_replace_bits(®_data, 0x1, RTASE_FCR_RXQ_MASK); + break; + case 2: + u16p_replace_bits(®_data, 0x2, RTASE_FCR_RXQ_MASK); + break; + case 4: + u16p_replace_bits(®_data, 0x3, RTASE_FCR_RXQ_MASK); + break; + } + rtase_w16(tp, RTASE_FCR, reg_data); +} + +static void rtase_set_tx_queue(const struct rtase_private *tp) +{ + u16 reg_data; + + reg_data = rtase_r16(tp, RTASE_TX_CONFIG_1); + switch (tp->tx_queue_ctrl) { + case 1: + u16p_replace_bits(®_data, 0x0, RTASE_TC_MODE_MASK); + break; + case 2: + u16p_replace_bits(®_data, 0x1, RTASE_TC_MODE_MASK); + break; + case 3: + case 4: + u16p_replace_bits(®_data, 0x2, RTASE_TC_MODE_MASK); + break; + default: + u16p_replace_bits(®_data, 0x3, RTASE_TC_MODE_MASK); + break; + } + rtase_w16(tp, RTASE_TX_CONFIG_1, reg_data); +} + +static void rtase_hw_config(struct net_device *dev) +{ + const struct rtase_private *tp = netdev_priv(dev); + u32 reg_data32; + u16 reg_data16; + + rtase_hw_reset(dev); + + /* set rx dma burst */ + reg_data16 = rtase_r16(tp, RTASE_RX_CONFIG_0); + reg_data16 &= ~(RTASE_RX_SINGLE_TAG | RTASE_RX_SINGLE_FETCH); + u16p_replace_bits(®_data16, RTASE_RX_DMA_BURST_256, + RTASE_RX_MX_DMA_MASK); + rtase_w16(tp, RTASE_RX_CONFIG_0, reg_data16); + + /* new rx descritpor */ + reg_data16 = rtase_r16(tp, RTASE_RX_CONFIG_1); + reg_data16 |= RTASE_RX_NEW_DESC_FORMAT_EN | RTASE_PCIE_NEW_FLOW; + u16p_replace_bits(®_data16, 0xF, RTASE_RX_MAX_FETCH_DESC_MASK); + rtase_w16(tp, RTASE_RX_CONFIG_1, reg_data16); + + rtase_set_rx_queue(tp); + + rtase_interrupt_mitigation(tp); + + /* set tx dma burst size and interframe gap time */ + reg_data32 = rtase_r32(tp, RTASE_TX_CONFIG_0); + u32p_replace_bits(®_data32, RTASE_TX_DMA_BURST_UNLIMITED, + RTASE_TX_DMA_MASK); + u32p_replace_bits(®_data32, RTASE_INTERFRAMEGAP, + RTASE_TX_INTER_FRAME_GAP_MASK); + rtase_w32(tp, RTASE_TX_CONFIG_0, reg_data32); + + /* new tx descriptor */ + reg_data16 = rtase_r16(tp, RTASE_TFUN_CTRL); + rtase_w16(tp, RTASE_TFUN_CTRL, reg_data16 | + RTASE_TX_NEW_DESC_FORMAT_EN); + + /* tx fetch desc number */ + rtase_w8(tp, RTASE_TDFNR, 0x10); + + /* tag num select */ + reg_data16 = rtase_r16(tp, RTASE_MTPS); + u16p_replace_bits(®_data16, 0x4, RTASE_TAG_NUM_SEL_MASK); + rtase_w16(tp, RTASE_MTPS, reg_data16); + + rtase_set_tx_queue(tp); + + rtase_w16(tp, RTASE_TOKSEL, 0x5555); + + rtase_tally_counter_addr_fill(tp); + rtase_desc_addr_fill(tp); + rtase_hw_set_features(dev, dev->features); + + /* enable flow control */ + reg_data16 = rtase_r16(tp, RTASE_GPHY_STD_00); + reg_data16 &= ~(RTASE_TXFLOW_EN | RTASE_RXFLOW_EN); + rtase_w16(tp, RTASE_GPHY_STD_00, reg_data16); + reg_data16 = rtase_r16(tp, RTASE_CPLUS_CMD); + reg_data16 |= (RTASE_FORCE_TXFLOW_EN | RTASE_FORCE_RXFLOW_EN); + rtase_w16(tp, RTASE_CPLUS_CMD, reg_data16); + /* set near fifo threshold - rx missed issue. */ + rtase_w16(tp, RTASE_RFIFONFULL, 0x190); + + rtase_w16(tp, RTASE_RMS, tp->rx_buf_sz); + + rtase_hw_set_rx_packet_filter(dev); +} + +static void rtase_nic_enable(const struct net_device *dev) +{ + const struct rtase_private *tp = netdev_priv(dev); + u16 rcr = rtase_r16(tp, RTASE_RX_CONFIG_1); + u8 val; + + rtase_w16(tp, RTASE_RX_CONFIG_1, rcr & ~RTASE_PCIE_RELOAD_EN); + rtase_w16(tp, RTASE_RX_CONFIG_1, rcr | RTASE_PCIE_RELOAD_EN); + + val = rtase_r8(tp, RTASE_CHIP_CMD); + rtase_w8(tp, RTASE_CHIP_CMD, val | RTASE_TE | RTASE_RE); + + val = rtase_r8(tp, RTASE_MISC); + rtase_w8(tp, RTASE_MISC, val & ~RTASE_RX_DV_GATE_EN); +} + +static void rtase_enable_hw_interrupt(const struct rtase_private *tp) +{ + const struct rtase_int_vector *ivec = &tp->int_vector[0]; + u32 i; + + rtase_w32(tp, ivec->imr_addr, ivec->imr); + + for (i = 1; i < tp->int_nums; i++) { + ivec = &tp->int_vector[i]; + rtase_w16(tp, ivec->imr_addr, ivec->imr); + } +} + +static void rtase_hw_start(const struct net_device *dev) +{ + const struct rtase_private *tp = netdev_priv(dev); + + rtase_nic_enable(dev); + rtase_enable_hw_interrupt(tp); +} + +/* the interrupt handler does RXQ0 and TXQ0, TXQ4~7 interrutp status + */ +static irqreturn_t rtase_interrupt(int irq, void *dev_instance) +{ + const struct rtase_private *tp; + struct rtase_int_vector *ivec; + u32 status; + + ivec = dev_instance; + tp = ivec->tp; + status = rtase_r32(tp, ivec->isr_addr); + + rtase_w32(tp, ivec->imr_addr, 0x0); + rtase_w32(tp, ivec->isr_addr, status & ~RTASE_FOVW); + + if (napi_schedule_prep(&ivec->napi)) + __napi_schedule(&ivec->napi); + + return IRQ_HANDLED; +} + +/* the interrupt handler does RXQ1&TXQ1 or RXQ2&TXQ2 or RXQ3&TXQ3 interrupt + * status according to interrupt vector + */ +static irqreturn_t rtase_q_interrupt(int irq, void *dev_instance) +{ + const struct rtase_private *tp; + struct rtase_int_vector *ivec; + u16 status; + + ivec = dev_instance; + tp = ivec->tp; + status = rtase_r16(tp, ivec->isr_addr); + + rtase_w16(tp, ivec->imr_addr, 0x0); + rtase_w16(tp, ivec->isr_addr, status); + + if (napi_schedule_prep(&ivec->napi)) + __napi_schedule(&ivec->napi); + + return IRQ_HANDLED; +} + +static int rtase_poll(struct napi_struct *napi, int budget) +{ + const struct rtase_int_vector *ivec; + const struct rtase_private *tp; + struct rtase_ring *ring; + int total_workdone = 0; + + ivec = container_of(napi, struct rtase_int_vector, napi); + tp = ivec->tp; + + list_for_each_entry(ring, &ivec->ring_list, ring_entry) + total_workdone += ring->ring_handler(ring, budget); + + if (total_workdone >= budget) + return budget; + + if (napi_complete_done(napi, total_workdone)) { + if (!ivec->index) + rtase_w32(tp, ivec->imr_addr, ivec->imr); + else + rtase_w16(tp, ivec->imr_addr, ivec->imr); + } + + return total_workdone; +} + +static int rtase_open(struct net_device *dev) +{ + struct rtase_private *tp = netdev_priv(dev); + const struct pci_dev *pdev = tp->pdev; + struct rtase_int_vector *ivec; + u16 i = 0, j; + int ret; + + ivec = &tp->int_vector[0]; + tp->rx_buf_sz = RTASE_RX_BUF_SIZE; + + ret = rtase_alloc_desc(tp); + if (ret) + return ret; + + ret = rtase_init_ring(dev); + if (ret) + goto err_free_all_allocated_mem; + + rtase_hw_config(dev); + + if (tp->sw_flag & RTASE_SWF_MSIX_ENABLED) { + ret = request_irq(ivec->irq, rtase_interrupt, 0, + dev->name, ivec); + if (ret) + goto err_free_all_allocated_irq; + + /* request other interrupts to handle multiqueue */ + for (i = 1; i < tp->int_nums; i++) { + ivec = &tp->int_vector[i]; + snprintf(ivec->name, sizeof(ivec->name), "%s_int%u", + tp->dev->name, i); + ret = request_irq(ivec->irq, rtase_q_interrupt, 0, + ivec->name, ivec); + if (ret) + goto err_free_all_allocated_irq; + } + } else { + ret = request_irq(pdev->irq, rtase_interrupt, 0, dev->name, + ivec); + if (ret) + goto err_free_all_allocated_mem; + } + + rtase_hw_start(dev); + + for (i = 0; i < tp->int_nums; i++) { + ivec = &tp->int_vector[i]; + napi_enable(&ivec->napi); + } + + netif_carrier_on(dev); + netif_wake_queue(dev); + + return 0; + +err_free_all_allocated_irq: + for (j = 0; j < i; j++) + free_irq(tp->int_vector[j].irq, &tp->int_vector[j]); + +err_free_all_allocated_mem: + rtase_free_desc(tp); + + return ret; +} + +static void rtase_down(struct net_device *dev) +{ + struct rtase_private *tp = netdev_priv(dev); + struct rtase_int_vector *ivec; + struct rtase_ring *ring, *tmp; + u32 i; + + for (i = 0; i < tp->int_nums; i++) { + ivec = &tp->int_vector[i]; + napi_disable(&ivec->napi); + list_for_each_entry_safe(ring, tmp, &ivec->ring_list, + ring_entry) { + netif_queue_set_napi(tp->dev, ring->index, + ring->type, NULL); + + list_del(&ring->ring_entry); + } + } + + netif_tx_disable(dev); + + netif_carrier_off(dev); + + rtase_hw_reset(dev); + + rtase_tx_clear(tp); + + rtase_rx_clear(tp); +} + +static int rtase_close(struct net_device *dev) +{ + struct rtase_private *tp = netdev_priv(dev); + const struct pci_dev *pdev = tp->pdev; + u32 i; + + rtase_down(dev); + + if (tp->sw_flag & RTASE_SWF_MSIX_ENABLED) { + for (i = 0; i < tp->int_nums; i++) + free_irq(tp->int_vector[i].irq, &tp->int_vector[i]); + + } else { + free_irq(pdev->irq, &tp->int_vector[0]); + } + + rtase_free_desc(tp); + + return 0; +} + +static u32 rtase_tx_vlan_tag(const struct rtase_private *tp, + const struct sk_buff *skb) +{ + return (skb_vlan_tag_present(skb)) ? + (RTASE_TX_VLAN_TAG | swab16(skb_vlan_tag_get(skb))) : 0x00; +} + +static u32 rtase_tx_csum(struct sk_buff *skb, const struct net_device *dev) +{ + u32 csum_cmd = 0; + u8 ip_protocol; + + switch (vlan_get_protocol(skb)) { + case htons(ETH_P_IP): + csum_cmd = RTASE_TX_IPCS_C; + ip_protocol = ip_hdr(skb)->protocol; + break; + + case htons(ETH_P_IPV6): + csum_cmd = RTASE_TX_IPV6F_C; + ip_protocol = ipv6_hdr(skb)->nexthdr; + break; + + default: + ip_protocol = IPPROTO_RAW; + break; + } + + if (ip_protocol == IPPROTO_TCP) + csum_cmd |= RTASE_TX_TCPCS_C; + else if (ip_protocol == IPPROTO_UDP) + csum_cmd |= RTASE_TX_UDPCS_C; + + csum_cmd |= u32_encode_bits(skb_transport_offset(skb), + RTASE_TCPHO_MASK); + + return csum_cmd; +} + +static int rtase_xmit_frags(struct rtase_ring *ring, struct sk_buff *skb, + u32 opts1, u32 opts2) +{ + const struct skb_shared_info *info = skb_shinfo(skb); + const struct rtase_private *tp = ring->ivec->tp; + const u8 nr_frags = info->nr_frags; + struct rtase_tx_desc *txd = NULL; + u32 cur_frag, entry; + + entry = ring->cur_idx; + for (cur_frag = 0; cur_frag < nr_frags; cur_frag++) { + const skb_frag_t *frag = &info->frags[cur_frag]; + dma_addr_t mapping; + u32 status, len; + void *addr; + + entry = (entry + 1) % RTASE_NUM_DESC; + + txd = ring->desc + sizeof(struct rtase_tx_desc) * entry; + len = skb_frag_size(frag); + addr = skb_frag_address(frag); + mapping = dma_map_single(&tp->pdev->dev, addr, len, + DMA_TO_DEVICE); + + if (unlikely(dma_mapping_error(&tp->pdev->dev, mapping))) { + if (unlikely(net_ratelimit())) + netdev_err(tp->dev, + "Failed to map TX fragments DMA!\n"); + + goto err_out; + } + + if (((entry + 1) % RTASE_NUM_DESC) == 0) + status = (opts1 | len | RTASE_RING_END); + else + status = opts1 | len; + + if (cur_frag == (nr_frags - 1)) { + ring->skbuff[entry] = skb; + status |= RTASE_TX_LAST_FRAG; + } + + ring->mis.len[entry] = len; + txd->addr = cpu_to_le64(mapping); + txd->opts2 = cpu_to_le32(opts2); + + /* make sure the operating fields have been updated */ + dma_wmb(); + txd->opts1 = cpu_to_le32(status); + } + + return cur_frag; + +err_out: + rtase_tx_clear_range(ring, ring->cur_idx + 1, cur_frag); + return -EIO; +} + +static netdev_tx_t rtase_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct skb_shared_info *shinfo = skb_shinfo(skb); + struct rtase_private *tp = netdev_priv(dev); + u32 q_idx, entry, len, opts1, opts2; + struct netdev_queue *tx_queue; + bool stop_queue, door_bell; + u32 mss = shinfo->gso_size; + struct rtase_tx_desc *txd; + struct rtase_ring *ring; + dma_addr_t mapping; + int frags; + + /* multiqueues */ + q_idx = skb_get_queue_mapping(skb); + ring = &tp->tx_ring[q_idx]; + tx_queue = netdev_get_tx_queue(dev, q_idx); + + if (unlikely(!rtase_tx_avail(ring))) { + if (net_ratelimit()) + netdev_err(dev, + "BUG! Tx Ring full when queue awake!\n"); + + netif_stop_queue(dev); + return NETDEV_TX_BUSY; + } + + entry = ring->cur_idx % RTASE_NUM_DESC; + txd = ring->desc + sizeof(struct rtase_tx_desc) * entry; + + opts1 = RTASE_DESC_OWN; + opts2 = rtase_tx_vlan_tag(tp, skb); + + /* tcp segmentation offload (or tcp large send) */ + if (mss) { + if (shinfo->gso_type & SKB_GSO_TCPV4) { + opts1 |= RTASE_GIANT_SEND_V4; + } else if (shinfo->gso_type & SKB_GSO_TCPV6) { + if (skb_cow_head(skb, 0)) + goto err_dma_0; + + tcp_v6_gso_csum_prep(skb); + opts1 |= RTASE_GIANT_SEND_V6; + } else { + WARN_ON_ONCE(1); + } + + opts1 |= u32_encode_bits(skb_transport_offset(skb), + RTASE_TCPHO_MASK); + opts2 |= u32_encode_bits(mss, RTASE_MSS_MASK); + } else if (skb->ip_summed == CHECKSUM_PARTIAL) { + opts2 |= rtase_tx_csum(skb, dev); + } + + frags = rtase_xmit_frags(ring, skb, opts1, opts2); + if (unlikely(frags < 0)) + goto err_dma_0; + + if (frags) { + len = skb_headlen(skb); + opts1 |= RTASE_TX_FIRST_FRAG; + } else { + len = skb->len; + ring->skbuff[entry] = skb; + opts1 |= RTASE_TX_FIRST_FRAG | RTASE_TX_LAST_FRAG; + } + + if (((entry + 1) % RTASE_NUM_DESC) == 0) + opts1 |= (len | RTASE_RING_END); + else + opts1 |= len; + + mapping = dma_map_single(&tp->pdev->dev, skb->data, len, + DMA_TO_DEVICE); + + if (unlikely(dma_mapping_error(&tp->pdev->dev, mapping))) { + if (unlikely(net_ratelimit())) + netdev_err(dev, "Failed to map TX DMA!\n"); + + goto err_dma_1; + } + + ring->mis.len[entry] = len; + txd->addr = cpu_to_le64(mapping); + txd->opts2 = cpu_to_le32(opts2); + txd->opts1 = cpu_to_le32(opts1 & ~RTASE_DESC_OWN); + + /* make sure the operating fields have been updated */ + dma_wmb(); + + door_bell = __netdev_tx_sent_queue(tx_queue, skb->len, + netdev_xmit_more()); + + txd->opts1 = cpu_to_le32(opts1); + + skb_tx_timestamp(skb); + + /* tx needs to see descriptor changes before updated cur_idx */ + smp_wmb(); + + WRITE_ONCE(ring->cur_idx, ring->cur_idx + frags + 1); + + stop_queue = !netif_subqueue_maybe_stop(dev, ring->index, + rtase_tx_avail(ring), + RTASE_TX_STOP_THRS, + RTASE_TX_START_THRS); + + if (door_bell || stop_queue) + rtase_w8(tp, RTASE_TPPOLL, BIT(ring->index)); + + return NETDEV_TX_OK; + +err_dma_1: + ring->skbuff[entry] = NULL; + rtase_tx_clear_range(ring, ring->cur_idx + 1, frags); + +err_dma_0: + tp->stats.tx_dropped++; + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +static void rtase_set_rx_mode(struct net_device *dev) +{ + rtase_hw_set_rx_packet_filter(dev); +} + +static void rtase_enable_eem_write(const struct rtase_private *tp) +{ + u8 val; + + val = rtase_r8(tp, RTASE_EEM); + rtase_w8(tp, RTASE_EEM, val | RTASE_EEM_UNLOCK); +} + +static void rtase_disable_eem_write(const struct rtase_private *tp) +{ + u8 val; + + val = rtase_r8(tp, RTASE_EEM); + rtase_w8(tp, RTASE_EEM, val & ~RTASE_EEM_UNLOCK); +} + +static void rtase_rar_set(const struct rtase_private *tp, const u8 *addr) +{ + u32 rar_low, rar_high; + + rar_low = (u32)addr[0] | ((u32)addr[1] << 8) | + ((u32)addr[2] << 16) | ((u32)addr[3] << 24); + + rar_high = (u32)addr[4] | ((u32)addr[5] << 8); + + rtase_enable_eem_write(tp); + rtase_w32(tp, RTASE_MAC0, rar_low); + rtase_w32(tp, RTASE_MAC4, rar_high); + rtase_disable_eem_write(tp); + rtase_w16(tp, RTASE_LBK_CTRL, RTASE_LBK_ATLD | RTASE_LBK_CLR); +} + +static int rtase_set_mac_address(struct net_device *dev, void *p) +{ + struct rtase_private *tp = netdev_priv(dev); + int ret; + + ret = eth_mac_addr(dev, p); + if (ret) + return ret; + + rtase_rar_set(tp, dev->dev_addr); + + return 0; +} + +static int rtase_change_mtu(struct net_device *dev, int new_mtu) +{ + dev->mtu = new_mtu; + + netdev_update_features(dev); + + return 0; +} + +static void rtase_wait_for_quiescence(const struct net_device *dev) +{ + struct rtase_private *tp = netdev_priv(dev); + struct rtase_int_vector *ivec; + u32 i; + + for (i = 0; i < tp->int_nums; i++) { + ivec = &tp->int_vector[i]; + synchronize_irq(ivec->irq); + /* wait for any pending NAPI task to complete */ + napi_disable(&ivec->napi); + } + + rtase_irq_dis_and_clear(tp); + + for (i = 0; i < tp->int_nums; i++) { + ivec = &tp->int_vector[i]; + napi_enable(&ivec->napi); + } +} + +static void rtase_sw_reset(struct net_device *dev) +{ + struct rtase_private *tp = netdev_priv(dev); + struct rtase_ring *ring, *tmp; + struct rtase_int_vector *ivec; + int ret; + u32 i; + + netif_stop_queue(dev); + netif_carrier_off(dev); + rtase_hw_reset(dev); + + /* let's wait a bit while any (async) irq lands on */ + rtase_wait_for_quiescence(dev); + rtase_tx_clear(tp); + rtase_rx_clear(tp); + + for (i = 0; i < tp->int_nums; i++) { + ivec = &tp->int_vector[i]; + list_for_each_entry_safe(ring, tmp, &ivec->ring_list, + ring_entry) { + netif_queue_set_napi(tp->dev, ring->index, + ring->type, NULL); + + list_del(&ring->ring_entry); + } + } + + ret = rtase_init_ring(dev); + if (ret) { + netdev_err(dev, "unable to init ring\n"); + rtase_free_desc(tp); + return; + } + + rtase_hw_config(dev); + /* always link, so start to transmit & receive */ + rtase_hw_start(dev); + + netif_carrier_on(dev); + netif_wake_queue(dev); +} + +static void rtase_dump_tally_counter(const struct rtase_private *tp) +{ + dma_addr_t paddr = tp->tally_paddr; + u32 cmd = lower_32_bits(paddr); + u32 val; + int err; + + rtase_w32(tp, RTASE_DTCCR4, upper_32_bits(paddr)); + rtase_w32(tp, RTASE_DTCCR0, cmd); + rtase_w32(tp, RTASE_DTCCR0, cmd | RTASE_COUNTER_DUMP); + + err = read_poll_timeout_atomic(rtase_r32, val, + !(val & RTASE_COUNTER_DUMP), + 10, 250, false, tp, RTASE_DTCCR0); + + if (err == -ETIMEDOUT) + netdev_err(tp->dev, "error occurred in dump tally counter\n"); +} + +static void rtase_dump_state(const struct net_device *dev) +{ + const struct rtase_private *tp = netdev_priv(dev); + int max_reg_size = RTASE_PCI_REGS_SIZE; + const struct rtase_counters *counters; + const struct rtase_ring *ring; + u32 dword_rd; + int n = 0; + + ring = &tp->tx_ring[0]; + netdev_err(dev, "Tx descriptor info:\n"); + netdev_err(dev, "Tx curIdx = 0x%x\n", ring->cur_idx); + netdev_err(dev, "Tx dirtyIdx = 0x%x\n", ring->dirty_idx); + netdev_err(dev, "Tx phyAddr = %pad\n", &ring->phy_addr); + + ring = &tp->rx_ring[0]; + netdev_err(dev, "Rx descriptor info:\n"); + netdev_err(dev, "Rx curIdx = 0x%x\n", ring->cur_idx); + netdev_err(dev, "Rx dirtyIdx = 0x%x\n", ring->dirty_idx); + netdev_err(dev, "Rx phyAddr = %pad\n", &ring->phy_addr); + + netdev_err(dev, "Device Registers:\n"); + netdev_err(dev, "Chip Command = 0x%02x\n", + rtase_r8(tp, RTASE_CHIP_CMD)); + netdev_err(dev, "IMR = %08x\n", rtase_r32(tp, RTASE_IMR0)); + netdev_err(dev, "ISR = %08x\n", rtase_r32(tp, RTASE_ISR0)); + netdev_err(dev, "Boot Ctrl Reg(0xE004) = %04x\n", + rtase_r16(tp, RTASE_BOOT_CTL)); + netdev_err(dev, "EPHY ISR(0xE014) = %04x\n", + rtase_r16(tp, RTASE_EPHY_ISR)); + netdev_err(dev, "EPHY IMR(0xE016) = %04x\n", + rtase_r16(tp, RTASE_EPHY_IMR)); + netdev_err(dev, "CLKSW SET REG(0xE018) = %04x\n", + rtase_r16(tp, RTASE_CLKSW_SET)); + + netdev_err(dev, "Dump PCI Registers:\n"); + + while (n < max_reg_size) { + if ((n % RTASE_DWORD_MOD) == 0) + netdev_err(tp->dev, "0x%03x:\n", n); + + pci_read_config_dword(tp->pdev, n, &dword_rd); + netdev_err(tp->dev, "%08x\n", dword_rd); + n += 4; + } + + netdev_err(dev, "Dump tally counter:\n"); + counters = tp->tally_vaddr; + rtase_dump_tally_counter(tp); + + netdev_err(dev, "tx_packets %lld\n", + le64_to_cpu(counters->tx_packets)); + netdev_err(dev, "rx_packets %lld\n", + le64_to_cpu(counters->rx_packets)); + netdev_err(dev, "tx_errors %lld\n", + le64_to_cpu(counters->tx_errors)); + netdev_err(dev, "rx_errors %d\n", + le32_to_cpu(counters->rx_errors)); + netdev_err(dev, "rx_missed %d\n", + le16_to_cpu(counters->rx_missed)); + netdev_err(dev, "align_errors %d\n", + le16_to_cpu(counters->align_errors)); + netdev_err(dev, "tx_one_collision %d\n", + le32_to_cpu(counters->tx_one_collision)); + netdev_err(dev, "tx_multi_collision %d\n", + le32_to_cpu(counters->tx_multi_collision)); + netdev_err(dev, "rx_unicast %lld\n", + le64_to_cpu(counters->rx_unicast)); + netdev_err(dev, "rx_broadcast %lld\n", + le64_to_cpu(counters->rx_broadcast)); + netdev_err(dev, "rx_multicast %d\n", + le32_to_cpu(counters->rx_multicast)); + netdev_err(dev, "tx_aborted %d\n", + le16_to_cpu(counters->tx_aborted)); + netdev_err(dev, "tx_underrun %d\n", + le16_to_cpu(counters->tx_underrun)); +} + +static void rtase_tx_timeout(struct net_device *dev, unsigned int txqueue) +{ + rtase_dump_state(dev); + rtase_sw_reset(dev); +} + +static void rtase_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) +{ + const struct rtase_private *tp = netdev_priv(dev); + const struct rtase_counters *counters; + + counters = tp->tally_vaddr; + + dev_fetch_sw_netstats(stats, dev->tstats); + + /* fetch additional counter values missing in stats collected by driver + * from tally counter + */ + rtase_dump_tally_counter(tp); + stats->rx_errors = tp->stats.rx_errors; + stats->tx_errors = le64_to_cpu(counters->tx_errors); + stats->rx_dropped = tp->stats.rx_dropped; + stats->tx_dropped = tp->stats.tx_dropped; + stats->multicast = tp->stats.multicast; + stats->rx_length_errors = tp->stats.rx_length_errors; +} + +static void rtase_set_hw_cbs(const struct rtase_private *tp, u32 queue) +{ + u32 idle = tp->tx_qos[queue].idleslope * RTASE_1T_CLOCK; + u32 val, i; + + val = u32_encode_bits(idle / RTASE_1T_POWER, RTASE_IDLESLOPE_INT_MASK); + idle %= RTASE_1T_POWER; + + for (i = 1; i <= RTASE_IDLESLOPE_INT_SHIFT; i++) { + idle *= 2; + if ((idle / RTASE_1T_POWER) == 1) + val |= BIT(RTASE_IDLESLOPE_INT_SHIFT - i); + + idle %= RTASE_1T_POWER; + } + + rtase_w32(tp, RTASE_TXQCRDT_0 + queue * 4, val); +} + +static int rtase_setup_tc_cbs(struct rtase_private *tp, + const struct tc_cbs_qopt_offload *qopt) +{ + int queue = qopt->queue; + + if (queue < 0 || queue >= tp->func_tx_queue_num) + return -EINVAL; + + if (!qopt->enable) { + tp->tx_qos[queue].hicredit = 0; + tp->tx_qos[queue].locredit = 0; + tp->tx_qos[queue].idleslope = 0; + tp->tx_qos[queue].sendslope = 0; + + rtase_w32(tp, RTASE_TXQCRDT_0 + queue * 4, 0); + } else { + tp->tx_qos[queue].hicredit = qopt->hicredit; + tp->tx_qos[queue].locredit = qopt->locredit; + tp->tx_qos[queue].idleslope = qopt->idleslope; + tp->tx_qos[queue].sendslope = qopt->sendslope; + + rtase_set_hw_cbs(tp, queue); + } + + return 0; +} + +static int rtase_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data) +{ + struct rtase_private *tp = netdev_priv(dev); + + switch (type) { + case TC_SETUP_QDISC_CBS: + return rtase_setup_tc_cbs(tp, type_data); + default: + return -EOPNOTSUPP; + } +} + +static netdev_features_t rtase_fix_features(struct net_device *dev, + netdev_features_t features) +{ + netdev_features_t features_fix = features; + + /* not support TSO for jumbo frames */ + if (dev->mtu > ETH_DATA_LEN) + features_fix &= ~NETIF_F_ALL_TSO; + + return features_fix; +} + +static int rtase_set_features(struct net_device *dev, + netdev_features_t features) +{ + netdev_features_t features_set = features; + + features_set &= NETIF_F_RXALL | NETIF_F_RXCSUM | + NETIF_F_HW_VLAN_CTAG_RX; + + if (features_set ^ dev->features) + rtase_hw_set_features(dev, features_set); + + return 0; +} + +static const struct net_device_ops rtase_netdev_ops = { + .ndo_open = rtase_open, + .ndo_stop = rtase_close, + .ndo_start_xmit = rtase_start_xmit, + .ndo_set_rx_mode = rtase_set_rx_mode, + .ndo_set_mac_address = rtase_set_mac_address, + .ndo_change_mtu = rtase_change_mtu, + .ndo_tx_timeout = rtase_tx_timeout, + .ndo_get_stats64 = rtase_get_stats64, + .ndo_setup_tc = rtase_setup_tc, + .ndo_fix_features = rtase_fix_features, + .ndo_set_features = rtase_set_features, +}; + +static void rtase_get_mac_address(struct net_device *dev) +{ + struct rtase_private *tp = netdev_priv(dev); + u8 mac_addr[ETH_ALEN] __aligned(2) = {}; + u32 i; + + for (i = 0; i < ETH_ALEN; i++) + mac_addr[i] = rtase_r8(tp, RTASE_MAC0 + i); + + if (!is_valid_ether_addr(mac_addr)) { + eth_hw_addr_random(dev); + netdev_warn(dev, "Random ether addr %pM\n", dev->dev_addr); + } else { + eth_hw_addr_set(dev, mac_addr); + ether_addr_copy(dev->perm_addr, dev->dev_addr); + } + + rtase_rar_set(tp, dev->dev_addr); +} + +static int rtase_get_settings(struct net_device *dev, + struct ethtool_link_ksettings *cmd) +{ + u32 supported = SUPPORTED_MII | SUPPORTED_Pause | SUPPORTED_Asym_Pause; + const struct rtase_private *tp = netdev_priv(dev); + + ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, + supported); + + switch (tp->hw_ver) { + case RTASE_HW_VER_906X_7XA: + case RTASE_HW_VER_906X_7XC: + cmd->base.speed = SPEED_5000; + break; + case RTASE_HW_VER_907XD_V1: + case RTASE_HW_VER_907XD_VA: + cmd->base.speed = SPEED_10000; + break; + } + + cmd->base.duplex = DUPLEX_FULL; + cmd->base.port = PORT_MII; + cmd->base.autoneg = AUTONEG_DISABLE; + + return 0; +} + +static void rtase_get_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *pause) +{ + const struct rtase_private *tp = netdev_priv(dev); + u16 value = rtase_r16(tp, RTASE_CPLUS_CMD); + + pause->autoneg = AUTONEG_DISABLE; + pause->tx_pause = !!(value & RTASE_FORCE_TXFLOW_EN); + pause->rx_pause = !!(value & RTASE_FORCE_RXFLOW_EN); +} + +static int rtase_set_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *pause) +{ + const struct rtase_private *tp = netdev_priv(dev); + u16 value = rtase_r16(tp, RTASE_CPLUS_CMD); + + if (pause->autoneg) + return -EOPNOTSUPP; + + value &= ~(RTASE_FORCE_TXFLOW_EN | RTASE_FORCE_RXFLOW_EN); + + if (pause->tx_pause) + value |= RTASE_FORCE_TXFLOW_EN; + + if (pause->rx_pause) + value |= RTASE_FORCE_RXFLOW_EN; + + rtase_w16(tp, RTASE_CPLUS_CMD, value); + return 0; +} + +static void rtase_get_eth_mac_stats(struct net_device *dev, + struct ethtool_eth_mac_stats *stats) +{ + struct rtase_private *tp = netdev_priv(dev); + const struct rtase_counters *counters; + + counters = tp->tally_vaddr; + + rtase_dump_tally_counter(tp); + + stats->FramesTransmittedOK = le64_to_cpu(counters->tx_packets); + stats->FramesReceivedOK = le64_to_cpu(counters->rx_packets); + stats->FramesLostDueToIntMACXmitError = + le64_to_cpu(counters->tx_errors); + stats->BroadcastFramesReceivedOK = le64_to_cpu(counters->rx_broadcast); +} + +static const struct ethtool_ops rtase_ethtool_ops = { + .get_link = ethtool_op_get_link, + .get_link_ksettings = rtase_get_settings, + .get_pauseparam = rtase_get_pauseparam, + .set_pauseparam = rtase_set_pauseparam, + .get_eth_mac_stats = rtase_get_eth_mac_stats, + .get_ts_info = ethtool_op_get_ts_info, +}; + +static void rtase_init_netdev_ops(struct net_device *dev) +{ + dev->netdev_ops = &rtase_netdev_ops; + dev->ethtool_ops = &rtase_ethtool_ops; +} + +static void rtase_init_napi(struct rtase_private *tp) +{ + u16 i; + + for (i = 0; i < tp->int_nums; i++) { + netif_napi_add_config(tp->dev, &tp->int_vector[i].napi, + tp->int_vector[i].poll, i); + netif_napi_set_irq(&tp->int_vector[i].napi, + tp->int_vector[i].irq); + } +} + +static void rtase_reset_interrupt(struct pci_dev *pdev, + const struct rtase_private *tp) +{ + if (tp->sw_flag & RTASE_SWF_MSIX_ENABLED) + pci_disable_msix(pdev); + else + pci_disable_msi(pdev); +} + +static int rtase_alloc_msix(struct pci_dev *pdev, struct rtase_private *tp) +{ + int ret, irq; + u16 i; + + memset(tp->msix_entry, 0x0, RTASE_NUM_MSIX * + sizeof(struct msix_entry)); + + for (i = 0; i < RTASE_NUM_MSIX; i++) + tp->msix_entry[i].entry = i; + + ret = pci_enable_msix_exact(pdev, tp->msix_entry, tp->int_nums); + + if (ret) + return ret; + + for (i = 0; i < tp->int_nums; i++) { + irq = pci_irq_vector(pdev, i); + if (irq < 0) { + pci_disable_msix(pdev); + return irq; + } + + tp->int_vector[i].irq = irq; + } + + return 0; +} + +static int rtase_alloc_interrupt(struct pci_dev *pdev, + struct rtase_private *tp) +{ + int ret; + + ret = rtase_alloc_msix(pdev, tp); + if (ret) { + ret = pci_enable_msi(pdev); + if (ret) { + dev_err(&pdev->dev, + "unable to alloc interrupt.(MSI)\n"); + return ret; + } + + tp->sw_flag |= RTASE_SWF_MSI_ENABLED; + } else { + tp->sw_flag |= RTASE_SWF_MSIX_ENABLED; + } + + return 0; +} + +static void rtase_init_hardware(const struct rtase_private *tp) +{ + u16 i; + + for (i = 0; i < RTASE_VLAN_FILTER_ENTRY_NUM; i++) + rtase_w32(tp, RTASE_VLAN_ENTRY_0 + i * 4, 0); +} + +static void rtase_init_int_vector(struct rtase_private *tp) +{ + u16 i; + + /* interrupt vector 0 */ + tp->int_vector[0].tp = tp; + tp->int_vector[0].index = 0; + tp->int_vector[0].imr_addr = RTASE_IMR0; + tp->int_vector[0].isr_addr = RTASE_ISR0; + tp->int_vector[0].imr = RTASE_ROK | RTASE_RDU | RTASE_TOK | + RTASE_TOK4 | RTASE_TOK5 | RTASE_TOK6 | + RTASE_TOK7; + tp->int_vector[0].poll = rtase_poll; + + memset(tp->int_vector[0].name, 0x0, sizeof(tp->int_vector[0].name)); + INIT_LIST_HEAD(&tp->int_vector[0].ring_list); + + /* interrupt vector 1 ~ 3 */ + for (i = 1; i < tp->int_nums; i++) { + tp->int_vector[i].tp = tp; + tp->int_vector[i].index = i; + tp->int_vector[i].imr_addr = RTASE_IMR1 + (i - 1) * 4; + tp->int_vector[i].isr_addr = RTASE_ISR1 + (i - 1) * 4; + tp->int_vector[i].imr = RTASE_Q_ROK | RTASE_Q_RDU | + RTASE_Q_TOK; + tp->int_vector[i].poll = rtase_poll; + + memset(tp->int_vector[i].name, 0x0, + sizeof(tp->int_vector[0].name)); + INIT_LIST_HEAD(&tp->int_vector[i].ring_list); + } +} + +static u16 rtase_calc_time_mitigation(u32 time_us) +{ + u8 msb, time_count, time_unit; + u16 int_miti; + + time_us = min(time_us, RTASE_MITI_MAX_TIME); + + if (time_us > RTASE_MITI_TIME_COUNT_MASK) { + msb = fls(time_us); + time_unit = msb - RTASE_MITI_COUNT_BIT_NUM; + time_count = time_us >> (msb - RTASE_MITI_COUNT_BIT_NUM); + } else { + time_unit = 0; + time_count = time_us; + } + + int_miti = u16_encode_bits(time_count, RTASE_MITI_TIME_COUNT_MASK) | + u16_encode_bits(time_unit, RTASE_MITI_TIME_UNIT_MASK); + + return int_miti; +} + +static u16 rtase_calc_packet_num_mitigation(u16 pkt_num) +{ + u8 msb, pkt_num_count, pkt_num_unit; + u16 int_miti; + + pkt_num = min(pkt_num, RTASE_MITI_MAX_PKT_NUM); + + if (pkt_num > 60) { + pkt_num_unit = RTASE_MITI_MAX_PKT_NUM_IDX; + pkt_num_count = pkt_num / RTASE_MITI_MAX_PKT_NUM_UNIT; + } else { + msb = fls(pkt_num); + if (msb >= RTASE_MITI_COUNT_BIT_NUM) { + pkt_num_unit = msb - RTASE_MITI_COUNT_BIT_NUM; + pkt_num_count = pkt_num >> (msb - + RTASE_MITI_COUNT_BIT_NUM); + } else { + pkt_num_unit = 0; + pkt_num_count = pkt_num; + } + } + + int_miti = u16_encode_bits(pkt_num_count, + RTASE_MITI_PKT_NUM_COUNT_MASK) | + u16_encode_bits(pkt_num_unit, + RTASE_MITI_PKT_NUM_UNIT_MASK); + + return int_miti; +} + +static void rtase_init_software_variable(struct pci_dev *pdev, + struct rtase_private *tp) +{ + u16 int_miti; + + tp->tx_queue_ctrl = RTASE_TXQ_CTRL; + tp->func_tx_queue_num = RTASE_FUNC_TXQ_NUM; + tp->func_rx_queue_num = RTASE_FUNC_RXQ_NUM; + tp->int_nums = RTASE_INTERRUPT_NUM; + + int_miti = rtase_calc_time_mitigation(RTASE_MITI_DEFAULT_TIME) | + rtase_calc_packet_num_mitigation(RTASE_MITI_DEFAULT_PKT_NUM); + tp->tx_int_mit = int_miti; + tp->rx_int_mit = int_miti; + + tp->sw_flag = 0; + + rtase_init_int_vector(tp); + + /* MTU range: 60 - hw-specific max */ + tp->dev->min_mtu = ETH_ZLEN; + tp->dev->max_mtu = RTASE_MAX_JUMBO_SIZE; +} + +static int rtase_check_mac_version_valid(struct rtase_private *tp) +{ + int ret = -ENODEV; + + tp->hw_ver = rtase_r32(tp, RTASE_TX_CONFIG_0) & RTASE_HW_VER_MASK; + + switch (tp->hw_ver) { + case RTASE_HW_VER_906X_7XA: + case RTASE_HW_VER_906X_7XC: + case RTASE_HW_VER_907XD_V1: + case RTASE_HW_VER_907XD_VA: + ret = 0; + break; + } + + return ret; +} + +static int rtase_init_board(struct pci_dev *pdev, struct net_device **dev_out, + void __iomem **ioaddr_out) +{ + struct net_device *dev; + void __iomem *ioaddr; + int ret = -ENOMEM; + + /* dev zeroed in alloc_etherdev */ + dev = alloc_etherdev_mq(sizeof(struct rtase_private), + RTASE_FUNC_TXQ_NUM); + if (!dev) + goto err_out; + + SET_NETDEV_DEV(dev, &pdev->dev); + + ret = pci_enable_device(pdev); + if (ret) + goto err_out_free_dev; + + /* make sure PCI base addr 1 is MMIO */ + if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) { + ret = -ENODEV; + goto err_out_disable; + } + + /* check for weird/broken PCI region reporting */ + if (pci_resource_len(pdev, 2) < RTASE_REGS_SIZE) { + ret = -ENODEV; + goto err_out_disable; + } + + ret = pci_request_regions(pdev, KBUILD_MODNAME); + if (ret) + goto err_out_disable; + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) { + dev_err(&pdev->dev, "no usable dma addressing method\n"); + goto err_out_free_res; + } + + pci_set_master(pdev); + + /* ioremap MMIO region */ + ioaddr = ioremap(pci_resource_start(pdev, 2), + pci_resource_len(pdev, 2)); + if (!ioaddr) { + ret = -EIO; + goto err_out_free_res; + } + + *ioaddr_out = ioaddr; + *dev_out = dev; + + return ret; + +err_out_free_res: + pci_release_regions(pdev); + +err_out_disable: + pci_disable_device(pdev); + +err_out_free_dev: + free_netdev(dev); + +err_out: + *ioaddr_out = NULL; + *dev_out = NULL; + + return ret; +} + +static void rtase_release_board(struct pci_dev *pdev, struct net_device *dev, + void __iomem *ioaddr) +{ + const struct rtase_private *tp = netdev_priv(dev); + + rtase_rar_set(tp, tp->dev->perm_addr); + iounmap(ioaddr); + + if (tp->sw_flag & RTASE_SWF_MSIX_ENABLED) + pci_disable_msix(pdev); + else + pci_disable_msi(pdev); + + pci_release_regions(pdev); + pci_disable_device(pdev); + free_netdev(dev); +} + +static int rtase_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *dev = NULL; + struct rtase_int_vector *ivec; + void __iomem *ioaddr = NULL; + struct rtase_private *tp; + int ret, i; + + if (!pdev->is_physfn && pdev->is_virtfn) { + dev_err(&pdev->dev, + "This module does not support a virtual function."); + return -EINVAL; + } + + dev_dbg(&pdev->dev, "Automotive Switch Ethernet driver loaded\n"); + + ret = rtase_init_board(pdev, &dev, &ioaddr); + if (ret) + return ret; + + tp = netdev_priv(dev); + tp->mmio_addr = ioaddr; + tp->dev = dev; + tp->pdev = pdev; + + /* identify chip attached to board */ + ret = rtase_check_mac_version_valid(tp); + if (ret) { + dev_err(&pdev->dev, + "unknown chip version: 0x%08x, contact rtase maintainers (see MAINTAINERS file)\n", + tp->hw_ver); + goto err_out_release_board; + } + + rtase_init_software_variable(pdev, tp); + rtase_init_hardware(tp); + + ret = rtase_alloc_interrupt(pdev, tp); + if (ret) { + dev_err(&pdev->dev, "unable to alloc MSIX/MSI\n"); + goto err_out_del_napi; + } + + rtase_init_napi(tp); + + rtase_init_netdev_ops(dev); + + dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; + + dev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | + NETIF_F_IP_CSUM | NETIF_F_HIGHDMA | + NETIF_F_RXCSUM | NETIF_F_SG | + NETIF_F_TSO | NETIF_F_IPV6_CSUM | + NETIF_F_TSO6; + + dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | + NETIF_F_TSO | NETIF_F_RXCSUM | + NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | + NETIF_F_RXALL | NETIF_F_RXFCS | + NETIF_F_IPV6_CSUM | NETIF_F_TSO6; + + dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | + NETIF_F_HIGHDMA; + dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; + netif_set_tso_max_size(dev, RTASE_LSO_64K); + netif_set_tso_max_segs(dev, RTASE_NIC_MAX_PHYS_BUF_COUNT_LSO2); + + rtase_get_mac_address(dev); + + tp->tally_vaddr = dma_alloc_coherent(&pdev->dev, + sizeof(*tp->tally_vaddr), + &tp->tally_paddr, + GFP_KERNEL); + if (!tp->tally_vaddr) { + ret = -ENOMEM; + goto err_out_free_dma; + } + + rtase_tally_counter_clear(tp); + + pci_set_drvdata(pdev, dev); + + netif_carrier_off(dev); + + ret = register_netdev(dev); + if (ret) + goto err_out_free_dma; + + netdev_dbg(dev, "%pM, IRQ %d\n", dev->dev_addr, dev->irq); + + return 0; + +err_out_free_dma: + if (tp->tally_vaddr) { + dma_free_coherent(&pdev->dev, + sizeof(*tp->tally_vaddr), + tp->tally_vaddr, + tp->tally_paddr); + + tp->tally_vaddr = NULL; + } + +err_out_del_napi: + for (i = 0; i < tp->int_nums; i++) { + ivec = &tp->int_vector[i]; + netif_napi_del(&ivec->napi); + } + +err_out_release_board: + rtase_release_board(pdev, dev, ioaddr); + + return ret; +} + +static void rtase_remove_one(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rtase_private *tp = netdev_priv(dev); + struct rtase_int_vector *ivec; + u32 i; + + unregister_netdev(dev); + + for (i = 0; i < tp->int_nums; i++) { + ivec = &tp->int_vector[i]; + netif_napi_del(&ivec->napi); + } + + rtase_reset_interrupt(pdev, tp); + if (tp->tally_vaddr) { + dma_free_coherent(&pdev->dev, + sizeof(*tp->tally_vaddr), + tp->tally_vaddr, + tp->tally_paddr); + tp->tally_vaddr = NULL; + } + + rtase_release_board(pdev, dev, tp->mmio_addr); + pci_set_drvdata(pdev, NULL); +} + +static void rtase_shutdown(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + const struct rtase_private *tp; + + tp = netdev_priv(dev); + + if (netif_running(dev)) + rtase_close(dev); + + rtase_reset_interrupt(pdev, tp); +} + +static int rtase_suspend(struct device *device) +{ + struct net_device *dev = dev_get_drvdata(device); + + if (netif_running(dev)) { + netif_device_detach(dev); + rtase_hw_reset(dev); + } + + return 0; +} + +static int rtase_resume(struct device *device) +{ + struct net_device *dev = dev_get_drvdata(device); + struct rtase_private *tp = netdev_priv(dev); + int ret; + + /* restore last modified mac address */ + rtase_rar_set(tp, dev->dev_addr); + + if (!netif_running(dev)) + goto out; + + rtase_wait_for_quiescence(dev); + + rtase_tx_clear(tp); + rtase_rx_clear(tp); + + ret = rtase_init_ring(dev); + if (ret) { + netdev_err(dev, "unable to init ring\n"); + rtase_free_desc(tp); + return -ENOMEM; + } + + rtase_hw_config(dev); + /* always link, so start to transmit & receive */ + rtase_hw_start(dev); + + netif_device_attach(dev); +out: + + return 0; +} + +static const struct dev_pm_ops rtase_pm_ops = { + SYSTEM_SLEEP_PM_OPS(rtase_suspend, rtase_resume) +}; + +static struct pci_driver rtase_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = rtase_pci_tbl, + .probe = rtase_init_one, + .remove = rtase_remove_one, + .shutdown = rtase_shutdown, + .driver.pm = pm_ptr(&rtase_pm_ops), +}; + +module_pci_driver(rtase_pci_driver); |
