diff --git a/Makefile b/Makefile
index 76a74a4..9bf8c3c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,119 +1,20 @@
# SPDX-License-Identifier: GPL-2.0-only
-################################################################################
#
-# r8169 is the Linux device driver released for RealTek RTL8169S/8110S,
-# RTL8169SB/8110SB, and RTL8110SC Gigabit Ethernet controllers with
-# PCI interface.
+# Makefile for the Realtek network device drivers.
#
-# Copyright(c) 2022 Realtek Semiconductor Corp. All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation; either version 2 of the License, or (at your option)
-# any later version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-# more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, see .
-#
-# Author:
-# Realtek NIC software team
-# No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan
-#
-################################################################################
-################################################################################
-# This product is covered by one or more of the following patents:
-# US5,307,459, US5,434,872, US5,732,094, US6,570,884, US6,115,776, and US6,327,625.
-################################################################################
+CONFIG_R8169 = m
-ENABLE_S5_KEEP_CURR_MAC = n
-
-ifneq ($(KERNELRELEASE),)
- obj-m := r8169.o
- r8169-objs := r8169_n.o
- EXTRA_CFLAGS += -DCONFIG_R8169_NAPI
- EXTRA_CFLAGS += -DCONFIG_R8169_VLAN
- ifeq ($(ENABLE_S5_KEEP_CURR_MAC), y)
- EXTRA_CFLAGS += -DENABLE_S5_KEEP_CURR_MAC
- endif
-else
- BASEDIR := /lib/modules/$(shell uname -r)
- KERNELDIR ?= $(BASEDIR)/build
- PWD :=$(shell pwd)
- DRIVERDIR := $(shell find $(BASEDIR)/kernel/drivers/net/ethernet -name realtek -type d)
- ifeq ($(DRIVERDIR),)
- DRIVERDIR := $(shell find $(BASEDIR)/kernel/drivers/net -name realtek -type d)
- endif
- ifeq ($(DRIVERDIR),)
- DRIVERDIR := $(BASEDIR)/kernel/drivers/net
- endif
- RTKDIR := $(subst $(BASEDIR)/,,$(DRIVERDIR))
-
- KERNEL_GCC_VERSION := $(shell cat /proc/version | sed -n 's/.*gcc version \([[:digit:]]\.[[:digit:]]\.[[:digit:]]\).*/\1/p')
- CCVERSION = $(shell $(CC) -dumpversion)
-
- KVER = $(shell uname -r)
- KMAJ = $(shell echo $(KVER) | \
- sed -e 's/^\([0-9][0-9]*\)\.[0-9][0-9]*\.[0-9][0-9]*.*/\1/')
- KMIN = $(shell echo $(KVER) | \
- sed -e 's/^[0-9][0-9]*\.\([0-9][0-9]*\)\.[0-9][0-9]*.*/\1/')
- KREV = $(shell echo $(KVER) | \
- sed -e 's/^[0-9][0-9]*\.[0-9][0-9]*\.\([0-9][0-9]*\).*/\1/')
-
- kver_ge = $(shell \
- echo test | awk '{if($(KMAJ) < $(1)) {print 0} else { \
- if($(KMAJ) > $(1)) {print 1} else { \
- if($(KMIN) < $(2)) {print 0} else { \
- if($(KMIN) > $(2)) {print 1} else { \
- if($(KREV) < $(3)) {print 0} else { print 1 } \
- }}}}}' \
- )
-
-.PHONY: all
-all: print_vars clean modules install
-
-print_vars:
- @echo
- @echo "CC: " $(CC)
- @echo "CCVERSION: " $(CCVERSION)
- @echo "KERNEL_GCC_VERSION: " $(KERNEL_GCC_VERSION)
- @echo "KVER: " $(KVER)
- @echo "KMAJ: " $(KMAJ)
- @echo "KMIN: " $(KMIN)
- @echo "KREV: " $(KREV)
- @echo "BASEDIR: " $(BASEDIR)
- @echo "DRIVERDIR: " $(DRIVERDIR)
- @echo "PWD: " $(PWD)
- @echo "RTKDIR: " $(RTKDIR)
- @echo
-
-.PHONY:modules
-modules:
-#ifeq ($(call kver_ge,5,0,0),1)
- $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
-#else
-# $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules
-#endif
-
-.PHONY:clean
-clean:
-#ifeq ($(call kver_ge,5,0,0),1)
- $(MAKE) -C $(KERNELDIR) M=$(PWD) clean
-#else
-# $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) clean
-#endif
-
-.PHONY:install
-install:
-#ifeq ($(call kver_ge,5,0,0),1)
- $(MAKE) -C $(KERNELDIR) M=$(PWD) INSTALL_MOD_DIR=$(RTKDIR) modules_install
-#else
-# $(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) INSTALL_MOD_DIR=$(RTKDIR) modules_install
-#endif
+ENABLE_INCLUDE_R8168 = n
+ENABLE_INCLUDE_R8125 = n
+ifeq ($(ENABLE_INCLUDE_R8168), y)
+ EXTRA_CFLAGS += -DINCLUDE_R8168
endif
+
+ifeq ($(ENABLE_INCLUDE_R8125), y)
+ EXTRA_CFLAGS += -DINCLUDE_R8125
+endif
+
+r8169-objs += r8169_main.o r8169_firmware.o r8169_phy_config.o
+obj-$(CONFIG_R8169) += r8169.o
diff --git a/Makefile_linux24x b/Makefile_linux24x
deleted file mode 100644
index 0e42c36..0000000
--- a/Makefile_linux24x
+++ /dev/null
@@ -1,80 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-################################################################################
-#
-# r8169 is the Linux device driver released for RealTek RTL8169S/8110S,
-# RTL8169SB/8110SB, and RTL8110SC Gigabit Ethernet controllers with
-# PCI interface.
-#
-# Copyright(c) 2022 Realtek Semiconductor Corp. All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation; either version 2 of the License, or (at your option)
-# any later version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-# more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, see .
-#
-# Author:
-# Realtek NIC software team
-# No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan
-#
-################################################################################
-
-################################################################################
-# This product is covered by one or more of the following patents:
-# US5,307,459, US5,434,872, US5,732,094, US6,570,884, US6,115,776, and US6,327,625.
-################################################################################
-
-CC := gcc
-LD := ld
-ARCH := $(shell uname -m | sed 's/i.86/i386/')
-KSRC := /lib/modules/$(shell uname -r)/build
-CONFIG_FILE := $(KSRC)/include/linux/autoconf.h
-KMISC := /lib/modules/$(shell uname -r)/kernel/drivers/net/
-
-
-ifeq ($(ARCH),x86_64)
- MODCFLAGS += -mcmodel=kernel -mno-red-zone
-endif
-
-#standard flags for module builds
-MODCFLAGS += -DLINUX -D__KERNEL__ -DMODULE -O2 -pipe -Wall
-MODCFLAGS += -I$(KSRC)/include -I.
-MODCFLAGS += -DMODVERSIONS -DEXPORT_SYMTAB -include $(KSRC)/include/linux/modversions.h
-ifeq ($(RTL_IOCTL),y)
- SOURCE := r8169_n.c rtl_ioctl.c
-else
- SOURCE := r8169_n.c
-endif
-OBJS := $(SOURCE:.c=.o)
-
-
-SMP := $(shell $(CC) $(MODCFLAGS) -E -dM $(CONFIG_FILE) | \
- grep CONFIG_SMP | awk '{print $$3}')
-
-ifneq ($(SMP),1)
- SMP := 0
-endif
-
-ifeq ($(SMP),1)
- MODCFLAGS += -D__SMP__
-endif
-
-modules: $(OBJS)
- $(LD) -r $^ -o r8169.o
- strip --strip-debug r8169.o
-
-%.o: %.c
- $(CC) $(MODCFLAGS) -c $< -o $@
-
-clean:
- rm *.o -f
-
-install:
- install -m 744 -c r8169.o $(KMISC)
diff --git a/r8169.h b/r8169.h
index 282ad4e..55ef825 100644
--- a/r8169.h
+++ b/r8169.h
@@ -1,1391 +1,79 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-/*
-################################################################################
-#
-# r8169 is the Linux device driver released for Realtek Gigabit Ethernet
-# Controllers with PCI interface.
-#
-# Copyright(c) 2022 Realtek Semiconductor Corp. All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation; either version 2 of the License, or (at your option)
-# any later version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-# more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, see .
-#
-# Author:
-# Realtek NIC software team
-# No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan
-#
-################################################################################
-*/
-
-/************************************************************************************
- * This product is covered by one or more of the following patents:
- * US6,570,884, US6,115,776, and US6,327,625.
- ***********************************************************************************/
-
-#include
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
-typedef int netdev_tx_t;
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
-#define skb_transport_offset(skb) (skb->h.raw - skb->data)
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
-#define device_set_wakeup_enable(dev, val) do {} while (0)
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)
-static inline void ether_addr_copy(u8 *dst, const u8 *src)
-{
- u16 *a = (u16 *)dst;
- const u16 *b = (const u16 *)src;
-
- a[0] = b[0];
- a[1] = b[1];
- a[2] = b[2];
-}
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,15,0)
-#define IS_ERR_OR_NULL(ptr) (!ptr)
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0)
-#define reinit_completion(x) ((x)->done = 0)
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,39)
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
-#define pm_runtime_mark_last_busy(x)
-#define pm_runtime_put_autosuspend(x) pm_runtime_put(x)
-#define pm_runtime_put_sync_autosuspend(x) pm_runtime_put_sync(x)
-
-static inline bool pm_runtime_suspended(struct device *dev)
-{
- return dev->power.runtime_status == RPM_SUSPENDED
- && !dev->power.disable_depth;
-}
-
-static inline bool pm_runtime_active(struct device *dev)
-{
- return dev->power.runtime_status == RPM_ACTIVE
- || dev->power.disable_depth;
-}
-#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
-#define queue_delayed_work(long_wq, work, delay) schedule_delayed_work(work, delay)
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34)
-#define netif_printk(priv, type, level, netdev, fmt, args...) \
- do { \
- if (netif_msg_##type(priv)) \
- printk(level "%s: " fmt,(netdev)->name , ##args); \
- } while (0)
-
-#define netif_emerg(priv, type, netdev, fmt, args...) \
- netif_printk(priv, type, KERN_EMERG, netdev, fmt, ##args)
-#define netif_alert(priv, type, netdev, fmt, args...) \
- netif_printk(priv, type, KERN_ALERT, netdev, fmt, ##args)
-#define netif_crit(priv, type, netdev, fmt, args...) \
- netif_printk(priv, type, KERN_CRIT, netdev, fmt, ##args)
-#define netif_err(priv, type, netdev, fmt, args...) \
- netif_printk(priv, type, KERN_ERR, netdev, fmt, ##args)
-#define netif_warn(priv, type, netdev, fmt, args...) \
- netif_printk(priv, type, KERN_WARNING, netdev, fmt, ##args)
-#define netif_notice(priv, type, netdev, fmt, args...) \
- netif_printk(priv, type, KERN_NOTICE, netdev, fmt, ##args)
-#define netif_info(priv, type, netdev, fmt, args...) \
- netif_printk(priv, type, KERN_INFO, (netdev), fmt, ##args)
-#endif
-#endif
-#endif
-#endif
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
-#define setup_timer(_timer, _function, _data) \
-do { \
- (_timer)->function = _function; \
- (_timer)->data = _data; \
- init_timer(_timer); \
-} while (0)
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0)
-#if defined(skb_vlan_tag_present) && !defined(vlan_tx_tag_present)
-#define vlan_tx_tag_present skb_vlan_tag_present
-#endif
-#if defined(skb_vlan_tag_get) && !defined(vlan_tx_tag_get)
-#define vlan_tx_tag_get skb_vlan_tag_get
-#endif
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0)
-
-#define RTL_ALLOC_SKB_INTR(tp, length) dev_alloc_skb(length)
-#ifdef CONFIG_R8169_NAPI
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,0)
-#undef RTL_ALLOC_SKB_INTR
-#define RTL_ALLOC_SKB_INTR(tp, length) napi_alloc_skb(&tp->napi, length)
-#endif
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)
-#define eth_random_addr(addr) random_ether_addr(addr)
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
-#define netdev_features_t u32
-#endif
-#endif
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
-#define NETIF_F_HW_VLAN_RX NETIF_F_HW_VLAN_CTAG_RX
-#define NETIF_F_HW_VLAN_TX NETIF_F_HW_VLAN_CTAG_TX
-#endif
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)
-#define __devinit
-#define __devexit
-#define __devexit_p(func) func
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
-#define CHECKSUM_PARTIAL CHECKSUM_HW
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
-#define irqreturn_t void
-#define IRQ_HANDLED 1
-#define IRQ_NONE 0
-#define IRQ_RETVAL(x)
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
-typedef unsigned long uintptr_t;
-#endif
-
-#ifndef NETIF_F_RXALL
-#define NETIF_F_RXALL 0
-#endif
-
-#ifndef NETIF_F_RXFCS
-#define NETIF_F_RXFCS 0
-#endif
-
-#if !defined(HAVE_FREE_NETDEV) && (LINUX_VERSION_CODE < KERNEL_VERSION(3,1,0))
-#define free_netdev(x) kfree(x)
-#endif
-
-#ifndef SET_NETDEV_DEV
-#define SET_NETDEV_DEV(net, pdev)
-#endif
-
-#ifndef SET_MODULE_OWNER
-#define SET_MODULE_OWNER(dev)
-#endif
-
-#ifndef SA_SHIRQ
-#define SA_SHIRQ IRQF_SHARED
-#endif
-
-#ifndef NETIF_F_GSO
-#define gso_size tso_size
-#define gso_segs tso_segs
-#endif
-
-#ifndef PCI_VENDOR_ID_DLINK
-#define PCI_VENDOR_ID_DLINK 0x1186
-#endif
-
-#ifndef dma_mapping_error
-#define dma_mapping_error(a,b) 0
-#endif
-
-#ifndef netif_err
-#define netif_err(a,b,c,d)
-#endif
-
-#ifndef AUTONEG_DISABLE
-#define AUTONEG_DISABLE 0x00
-#endif
-
-#ifndef AUTONEG_ENABLE
-#define AUTONEG_ENABLE 0x01
-#endif
-
-#ifndef BMCR_SPEED1000
-#define BMCR_SPEED1000 0x0040
-#endif
-
-#ifndef BMCR_SPEED100
-#define BMCR_SPEED100 0x2000
-#endif
-
-#ifndef BMCR_SPEED10
-#define BMCR_SPEED10 0x0000
-#endif
-
-#ifndef SPEED_UNKNOWN
-#define SPEED_UNKNOWN -1
-#endif
-
-#ifndef DUPLEX_UNKNOWN
-#define DUPLEX_UNKNOWN 0xff
-#endif
-
-#ifndef SUPPORTED_Pause
-#define SUPPORTED_Pause (1 << 13)
-#endif
-
-#ifndef SUPPORTED_Asym_Pause
-#define SUPPORTED_Asym_Pause (1 << 14)
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
-#ifdef CONFIG_NET_POLL_CONTROLLER
-#define RTL_NET_POLL_CONTROLLER dev->poll_controller=rtl8169_netpoll
-#else
-#define RTL_NET_POLL_CONTROLLER
-#endif
-
-#ifdef CONFIG_R8169_VLAN
-#define RTL_SET_VLAN dev->vlan_rx_register=rtl8169_vlan_rx_register
-#else
-#define RTL_SET_VLAN
-#endif
-
-#define RTL_NET_DEVICE_OPS(ops) dev->open=rtl8169_open; \
- dev->hard_start_xmit=rtl8169_start_xmit; \
- dev->get_stats=rtl8169_get_stats; \
- dev->stop=rtl8169_close; \
- dev->tx_timeout=rtl8169_tx_timeout; \
- dev->set_multicast_list=rtl8169_set_rx_mode; \
- dev->change_mtu=rtl8169_change_mtu; \
- dev->set_mac_address=rtl8169_set_mac_address; \
- dev->do_ioctl=rtl8169_ioctl; \
- RTL_NET_POLL_CONTROLLER; \
- RTL_SET_VLAN;
-#else
-#define RTL_NET_DEVICE_OPS(ops) dev->netdev_ops=&ops
-#endif
-
-#ifndef FALSE
-#define FALSE 0
-#endif
-
-#ifndef TRUE
-#define TRUE 1
-#endif
-
-#ifndef false
-#define false 0
-#endif
-
-#ifndef true
-#define true 1
-#endif
-
-#ifndef NET_IP_ALIGN
-#define NET_IP_ALIGN 2
-#endif
-
-#define NODE_ADDRESS_SIZE 6
-
-#define SHORT_PACKET_PADDING_BUF_SIZE 256
-
-#define RTK_MAGIC_DEBUG_VALUE 0x0badbeef
-
-#ifdef CONFIG_R8169_NAPI
-#define NAPI_SUFFIX "-NAPI"
-#else
-#define NAPI_SUFFIX ""
-#endif
-
-#define RTL8169_VERSION "6.031.00" NAPI_SUFFIX
-#define MODULENAME "r8169"
-#define PFX MODULENAME ": "
-
-#define GPL_CLAIM "\
-r8169 Copyright (C) 2022 Realtek NIC software team \n \
-This program comes with ABSOLUTELY NO WARRANTY; for details, please see . \n \
-This is free software, and you are welcome to redistribute it under certain conditions; see . \n"
-
-#ifdef RTL8169_DEBUG
-#define assert(expr) \
- if (!(expr)) { \
- printk( "Assertion failed! %s,%s,%s,line=%d\n", \
- #expr,__FILE__,__FUNCTION__,__LINE__); \
- }
-#define dprintk(fmt, args...) do { printk(PFX fmt, ## args); } while (0)
-#else
-#define assert(expr) do {} while (0)
-#define dprintk(fmt, args...) do {} while (0)
-#endif /* RTL8169_DEBUG */
-
-#define R8169_MSG_DEFAULT \
- (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN)
-
-#ifdef CONFIG_R8169_NAPI
-#define rtl8169_rx_skb netif_receive_skb
-#define rtl8169_rx_hwaccel_skb vlan_hwaccel_receive_skb
-#define rtl8169_rx_quota(count, quota) min(count, quota)
-#else
-#define rtl8169_rx_skb netif_rx
-#define rtl8169_rx_hwaccel_skb vlan_hwaccel_rx
-#define rtl8169_rx_quota(count, quota) count
-#endif
-
-/* MAC address length */
-#ifndef MAC_ADDR_LEN
-#define MAC_ADDR_LEN 6
-#endif
-
-#ifndef MAC_PROTOCOL_LEN
-#define MAC_PROTOCOL_LEN 2
-#endif
-
-#ifndef ETH_FCS_LEN
-#define ETH_FCS_LEN 4
-#endif
-
-#ifndef NETIF_F_TSO6
-#define NETIF_F_TSO6 0
-#endif
-
-#define Reserved2_data 7
-#define RX_DMA_BURST 7 /* Maximum PCI burst, '7' is unlimited */
-#define TX_DMA_BURST 7 /* Maximum PCI burst, '7' is unlimited */
-#define Reserved1_data 0x3F
-#define RxPacketMaxSize 0x3FE8 /* 16K - 1 - ETH_HLEN - VLAN - CRC... */
-#define Jumbo_Frame_7k (7*1024 - ETH_HLEN - VLAN_HLEN - ETH_FCS_LEN)
-
-#define IFG0 (1 << 24)
-#define IFG1 (1 << 25)
-#define IFG2 (1 << 19)
-
-#define R8169_REGS_SIZE (256)
-#define R8169_MAC_REGS_SIZE (256)
-#define R8169_PHY_REGS_SIZE (16*2)
-#define R8169_REGS_DUMP_SIZE (0x200)
-#define R8169_NAPI_WEIGHT 64
-#define NUM_TX_DESC 256 /* Number of Tx descriptor registers */
-#define NUM_RX_DESC 256 /* Number of Rx descriptor registers */
-#define RX_BUF_SIZE 1536 /* Rx Buffer size */
-#define R8169_TX_RING_BYTES ((NUM_TX_DESC + 1) * sizeof(struct TxDesc))
-#define R8169_RX_RING_BYTES ((NUM_RX_DESC + 1) * sizeof(struct RxDesc))
-
-//Channel Wait Count
-#define R8169_CHANNEL_WAIT_COUNT (20000)
-#define R8169_CHANNEL_WAIT_TIME (1) // 1us
-#define R8169_CHANNEL_EXIT_DELAY_TIME (20) //20us
-
-#define RTL8169_TX_TIMEOUT (6*HZ)
-#define RTL8169_PHY_TIMEOUT (10*HZ)
-#define RTL8169_ESD_TIMEOUT (2*HZ)
-
-/* write/read MMIO register */
-#define RTL_W8(reg, val8) writeb ((val8), ioaddr + (reg))
-#define RTL_W16(reg, val16) writew ((val16), ioaddr + (reg))
-#define RTL_W32(reg, val32) writel ((val32), ioaddr + (reg))
-#define RTL_R8(reg) readb (ioaddr + (reg))
-#define RTL_R16(reg) readw (ioaddr + (reg))
-#define RTL_R32(reg) ((unsigned long) readl (ioaddr + (reg)))
-
-#ifndef DMA_64BIT_MASK
-#define DMA_64BIT_MASK 0xffffffffffffffffULL
-#endif
-
-#ifndef DMA_32BIT_MASK
-#define DMA_32BIT_MASK 0x00000000ffffffffULL
-#endif
-
-#ifndef NETDEV_TX_OK
-#define NETDEV_TX_OK 0 /* driver took care of packet */
-#endif
-
-#ifndef NETDEV_TX_BUSY
-#define NETDEV_TX_BUSY 1 /* driver tx path was busy*/
-#endif
-
-#ifndef NETDEV_TX_LOCKED
-#define NETDEV_TX_LOCKED -1 /* driver tx lock was already taken */
-#endif
-
-#ifndef ADVERTISED_Pause
-#define ADVERTISED_Pause (1 << 13)
-#endif
-
-#ifndef ADVERTISED_Asym_Pause
-#define ADVERTISED_Asym_Pause (1 << 14)
-#endif
-
-#ifndef ADVERTISE_PAUSE_CAP
-#define ADVERTISE_PAUSE_CAP 0x400
-#endif
-
-#ifndef ADVERTISE_PAUSE_ASYM
-#define ADVERTISE_PAUSE_ASYM 0x800
-#endif
-
-#ifndef MII_CTRL1000
-#define MII_CTRL1000 0x09
-#endif
-
-#ifndef ADVERTISE_1000FULL
-#define ADVERTISE_1000FULL 0x200
-#endif
-
-#ifndef ADVERTISE_1000HALF
-#define ADVERTISE_1000HALF 0x100
-#endif
-
-#ifndef ETH_MIN_MTU
-#define ETH_MIN_MTU 68
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9)
-#ifdef __CHECKER__
-#define __iomem __attribute__((noderef, address_space(2)))
-extern void __chk_io_ptr(void __iomem *);
-#define __bitwise __attribute__((bitwise))
-#else
-#define __iomem
-#define __chk_io_ptr(x) (void)0
-#define __bitwise
-#endif
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9)
-
-#ifndef module_param
-#define module_param(v,t,p) MODULE_PARM(v, "i");
-#endif
-
-#ifndef PCI_DEVICE
-#define PCI_DEVICE(vend,dev) \
- .vendor = (vend), .device = (dev), \
- .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID
-#endif
-
-/*****************************************************************************/
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
-#define RTLDEV tp
-#else
-#define RTLDEV dev
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
-/*****************************************************************************/
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
-typedef struct net_device *napi_ptr;
-typedef int *napi_budget;
-
-#define napi dev
-#define RTL_NAPI_CONFIG(ndev, priv, function, weig) ndev->poll=function; \
- ndev->weight=weig;
-#define RTL_NAPI_QUOTA(budget, ndev) min(*budget, ndev->quota)
-#define RTL_GET_PRIV(stuct_ptr, priv_struct) netdev_priv(stuct_ptr)
-#define RTL_GET_NETDEV(priv_ptr)
-#define RTL_RX_QUOTA(budget) *budget
-#define RTL_NAPI_QUOTA_UPDATE(ndev, work_done, budget) *budget -= work_done; \
- ndev->quota -= work_done;
-#define RTL_NETIF_RX_COMPLETE(dev, napi, work_done) netif_rx_complete(dev)
-#define RTL_NETIF_RX_SCHEDULE_PREP(dev, napi) netif_rx_schedule_prep(dev)
-#define __RTL_NETIF_RX_SCHEDULE(dev, napi) __netif_rx_schedule(dev)
-#define RTL_NAPI_RETURN_VALUE work_done >= work_to_do
-#define RTL_NAPI_ENABLE(dev, napi) netif_poll_enable(dev)
-#define RTL_NAPI_DISABLE(dev, napi) netif_poll_disable(dev)
-#define DMA_BIT_MASK(n) (((n) == 64) ? ~0ULL : ((1ULL<<(n))-1))
-#else
-typedef struct napi_struct *napi_ptr;
-typedef int napi_budget;
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,1,0)
-#define RTL_NAPI_CONFIG(ndev, priv, function, weight) netif_napi_add_weight(ndev, &priv->napi, function, weight)
-#else
-#define RTL_NAPI_CONFIG(ndev, priv, function, weight) netif_napi_add(ndev, &priv->napi, function, weight)
-#endif //LINUX_VERSION_CODE >= KERNEL_VERSION(6,1,0)
-#define RTL_NAPI_QUOTA(budget, ndev) min(budget, budget)
-#define RTL_GET_PRIV(stuct_ptr, priv_struct) container_of(stuct_ptr, priv_struct, stuct_ptr)
-#define RTL_GET_NETDEV(priv_ptr) struct net_device *dev = priv_ptr->dev;
-#define RTL_RX_QUOTA(budget) budget
-#define RTL_NAPI_QUOTA_UPDATE(ndev, work_done, budget)
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
-#define RTL_NETIF_RX_COMPLETE(dev, napi, work_done) netif_rx_complete(dev, napi)
-#define RTL_NETIF_RX_SCHEDULE_PREP(dev, napi) netif_rx_schedule_prep(dev, napi)
-#define __RTL_NETIF_RX_SCHEDULE(dev, napi) __netif_rx_schedule(dev, napi)
-#endif
-#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,29)
-#define RTL_NETIF_RX_COMPLETE(dev, napi, work_done) netif_rx_complete(napi)
-#define RTL_NETIF_RX_SCHEDULE_PREP(dev, napi) netif_rx_schedule_prep(napi)
-#define __RTL_NETIF_RX_SCHEDULE(dev, napi) __netif_rx_schedule(napi)
-#endif
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29)
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,0)
-#define RTL_NETIF_RX_COMPLETE(dev, napi, work_done) napi_complete_done(napi, work_done)
-#else
-#define RTL_NETIF_RX_COMPLETE(dev, napi, work_done) napi_complete(napi)
-#endif
-#define RTL_NETIF_RX_SCHEDULE_PREP(dev, napi) napi_schedule_prep(napi)
-#define __RTL_NETIF_RX_SCHEDULE(dev, napi) __napi_schedule(napi)
-#endif
-#define RTL_NAPI_RETURN_VALUE work_done
-#define RTL_NAPI_ENABLE(dev, napi) napi_enable(napi)
-#define RTL_NAPI_DISABLE(dev, napi) napi_disable(napi)
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
-#define RTL_NAPI_DEL(priv)
-#else
-#define RTL_NAPI_DEL(priv) netif_napi_del(&priv->napi)
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
-
-/*****************************************************************************/
-
-/* 2.4.22 => 2.4.17 */
-#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,22) )
-#define pci_name(x) ((x)->slot_name)
-#endif /* 2.4.22 => 2.4.17 */
-
-/*****************************************************************************/
-/* 2.6.5 => 2.6.0 */
-#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5) )
-#define pci_dma_sync_single_for_cpu pci_dma_sync_single
-#define pci_dma_sync_single_for_device pci_dma_sync_single_for_cpu
-#endif /* 2.6.5 => 2.6.0 */
-
-/*****************************************************************************/
-#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) )
-#ifdef module_param_array_named
-#undef module_param_array_named
-#define module_param_array_named(name, array, type, nump, perm) \
- static struct kparam_array __param_arr_##name \
- = { ARRAY_SIZE(array), nump, param_set_##type, param_get_##type, \
- sizeof(array[0]), array }; \
- module_param_call(name, param_array_set, param_array_get, \
- &__param_arr_##name, perm)
-#endif /* module_param_array_named */
-#endif /* < 2.6.10 */
-
-/*****************************************************************************/
-/* 2.6.0 => 2.5.28 */
-#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) )
-#define MODULE_INFO(version, _version)
-#ifndef CONFIG_E1000_DISABLE_PACKET_SPLIT
-#define CONFIG_E1000_DISABLE_PACKET_SPLIT 1
-#endif
-
-#define pci_set_consistent_dma_mask(dev,mask) 1
-
-#undef dev_put
-#define dev_put(dev) __dev_put(dev)
-
-#ifndef skb_fill_page_desc
-#define skb_fill_page_desc _kc_skb_fill_page_desc
-extern void _kc_skb_fill_page_desc(struct sk_buff *skb, int i, struct page *page, int off, int size);
-#endif
-
-#ifndef pci_dma_mapping_error
-#define pci_dma_mapping_error _kc_pci_dma_mapping_error
-static inline int _kc_pci_dma_mapping_error(dma_addr_t dma_addr)
-{
- return dma_addr == 0;
-}
-#endif
-
-#undef ALIGN
-#define ALIGN(x,a) (((x)+(a)-1)&~((a)-1))
-
-#endif /* 2.6.0 => 2.5.28 */
-
-/*****************************************************************************/
-
-/* 2.6.4 => 2.6.0 */
-#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,4) )
-#define MODULE_VERSION(_version) MODULE_INFO(version, _version)
-#endif /* 2.6.4 => 2.6.0 */
-
-/*****************************************************************************/
-/* 2.5.28 => 2.4.23 */
-#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,5,28) )
-
-static inline void _kc_synchronize_irq(void)
-{
- synchronize_irq();
-}
-#undef synchronize_irq
-#define synchronize_irq(X) _kc_synchronize_irq()
-
-#include
-#define work_struct tq_struct
-#undef INIT_WORK
-#define INIT_WORK(a,b,c) INIT_TQUEUE(a,(void (*)(void *))b,c)
-#undef container_of
-#define container_of list_entry
-#define schedule_work schedule_task
-#define flush_scheduled_work flush_scheduled_tasks
-#endif /* 2.5.28 => 2.4.17 */
-
-/*****************************************************************************/
-/* 2.6.4 => 2.6.0 */
-#if ((LINUX_VERSION_CODE < KERNEL_VERSION(2,4,25) && \
- LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22)) || \
- (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) && \
- LINUX_VERSION_CODE < KERNEL_VERSION(2,6,4)))
-#define ETHTOOL_OPS_COMPAT
-#endif /* 2.6.4 => 2.6.0 */
-
-/*****************************************************************************/
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
-/*
- * initialize a work-struct's func and data pointers:
+/* r8169.h: RealTek 8169/8168/8101 ethernet driver.
+ *
+ * Copyright (c) 2002 ShuChen
+ * Copyright (c) 2003 - 2007 Francois Romieu
+ * Copyright (c) a lot of people too. Please respect their work.
+ *
+ * See MAINTAINERS file for support contact information.
*/
-#define PREPARE_WORK(_work, _func, _data) \
- do { \
- (_work)->func = _func; \
- (_work)->data = _data; \
- } while (0)
-#endif
-/*****************************************************************************/
+#include
+#include
-#if 0//LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
-#undef ethtool_ops
-#define ethtool_ops _kc_ethtool_ops
-
-struct _kc_ethtool_ops {
- int (*get_settings)(struct net_device *, struct ethtool_cmd *);
- int (*set_settings)(struct net_device *, struct ethtool_cmd *);
- void (*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *);
- int (*get_regs_len)(struct net_device *);
- void (*get_regs)(struct net_device *, struct ethtool_regs *, void *);
- void (*get_wol)(struct net_device *, struct ethtool_wolinfo *);
- int (*set_wol)(struct net_device *, struct ethtool_wolinfo *);
- u32 (*get_msglevel)(struct net_device *);
- void (*set_msglevel)(struct net_device *, u32);
- int (*nway_reset)(struct net_device *);
- u32 (*get_link)(struct net_device *);
- int (*get_eeprom_len)(struct net_device *);
- int (*get_eeprom)(struct net_device *, struct ethtool_eeprom *, u8 *);
- int (*set_eeprom)(struct net_device *, struct ethtool_eeprom *, u8 *);
- int (*get_coalesce)(struct net_device *, struct ethtool_coalesce *);
- int (*set_coalesce)(struct net_device *, struct ethtool_coalesce *);
- void (*get_ringparam)(struct net_device *, struct ethtool_ringparam *);
- int (*set_ringparam)(struct net_device *, struct ethtool_ringparam *);
- void (*get_pauseparam)(struct net_device *,
- struct ethtool_pauseparam*);
- int (*set_pauseparam)(struct net_device *,
- struct ethtool_pauseparam*);
- u32 (*get_rx_csum)(struct net_device *);
- int (*set_rx_csum)(struct net_device *, u32);
- u32 (*get_tx_csum)(struct net_device *);
- int (*set_tx_csum)(struct net_device *, u32);
- u32 (*get_sg)(struct net_device *);
- int (*set_sg)(struct net_device *, u32);
- u32 (*get_tso)(struct net_device *);
- int (*set_tso)(struct net_device *, u32);
- int (*self_test_count)(struct net_device *);
- void (*self_test)(struct net_device *, struct ethtool_test *, u64 *);
- void (*get_strings)(struct net_device *, u32 stringset, u8 *);
- int (*phys_id)(struct net_device *, u32);
- int (*get_stats_count)(struct net_device *);
- void (*get_ethtool_stats)(struct net_device *, struct ethtool_stats *,
- u64 *);
-} *ethtool_ops = NULL;
-
-#undef SET_ETHTOOL_OPS
-#define SET_ETHTOOL_OPS(netdev, ops) (ethtool_ops = (ops))
-
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0)
-#ifndef SET_ETHTOOL_OPS
-#define SET_ETHTOOL_OPS(netdev,ops) \
- ( (netdev)->ethtool_ops = (ops) )
-#endif //SET_ETHTOOL_OPS
-#endif //LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0)
-
-/*****************************************************************************/
-/* Installations with ethtool version without eeprom, adapter id, or statistics
- * support */
-
-#ifndef ETH_GSTRING_LEN
-#define ETH_GSTRING_LEN 32
-#endif
-
-#ifndef ETHTOOL_GSTATS
-#define ETHTOOL_GSTATS 0x1d
-#undef ethtool_drvinfo
-#define ethtool_drvinfo k_ethtool_drvinfo
-struct k_ethtool_drvinfo {
- u32 cmd;
- char driver[32];
- char version[32];
- char fw_version[32];
- char bus_info[32];
- char reserved1[32];
- char reserved2[16];
- u32 n_stats;
- u32 testinfo_len;
- u32 eedump_len;
- u32 regdump_len;
+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,
+ RTL_GIGA_MAC_VER_11,
+ /* 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,
+ RTL_GIGA_MAC_VER_53,
+ /* support for RTL_GIGA_MAC_VER_60 has been removed */
+ RTL_GIGA_MAC_VER_61,
+ RTL_GIGA_MAC_VER_63,
+ RTL_GIGA_MAC_NONE
};
-struct ethtool_stats {
- u32 cmd;
- u32 n_stats;
- u64 data[0];
-};
-#endif /* ETHTOOL_GSTATS */
+struct rtl8169_private;
-#ifndef ETHTOOL_PHYS_ID
-#define ETHTOOL_PHYS_ID 0x1c
-#endif /* ETHTOOL_PHYS_ID */
-
-#ifndef ETHTOOL_GSTRINGS
-#define ETHTOOL_GSTRINGS 0x1b
-enum ethtool_stringset {
- ETH_SS_TEST = 0,
- ETH_SS_STATS,
-};
-struct ethtool_gstrings {
- u32 cmd; /* ETHTOOL_GSTRINGS */
- u32 string_set; /* string set id e.c. ETH_SS_TEST, etc*/
- u32 len; /* number of strings in the string set */
- u8 data[0];
-};
-#endif /* ETHTOOL_GSTRINGS */
-
-#ifndef ETHTOOL_TEST
-#define ETHTOOL_TEST 0x1a
-enum ethtool_test_flags {
- ETH_TEST_FL_OFFLINE = (1 << 0),
- ETH_TEST_FL_FAILED = (1 << 1),
-};
-struct ethtool_test {
- u32 cmd;
- u32 flags;
- u32 reserved;
- u32 len;
- u64 data[0];
-};
-#endif /* ETHTOOL_TEST */
-
-#ifndef ETHTOOL_GEEPROM
-#define ETHTOOL_GEEPROM 0xb
-#undef ETHTOOL_GREGS
-struct ethtool_eeprom {
- u32 cmd;
- u32 magic;
- u32 offset;
- u32 len;
- u8 data[0];
-};
-
-struct ethtool_value {
- u32 cmd;
- u32 data;
-};
-#endif /* ETHTOOL_GEEPROM */
-
-#ifndef ETHTOOL_GLINK
-#define ETHTOOL_GLINK 0xa
-#endif /* ETHTOOL_GLINK */
-
-#ifndef ETHTOOL_GREGS
-#define ETHTOOL_GREGS 0x00000004 /* Get NIC registers */
-#define ethtool_regs _kc_ethtool_regs
-/* for passing big chunks of data */
-struct _kc_ethtool_regs {
- u32 cmd;
- u32 version; /* driver-specific, indicates different chips/revs */
- u32 len; /* bytes */
- u8 data[0];
-};
-#endif /* ETHTOOL_GREGS */
-
-#ifndef ETHTOOL_GMSGLVL
-#define ETHTOOL_GMSGLVL 0x00000007 /* Get driver message level */
-#endif
-#ifndef ETHTOOL_SMSGLVL
-#define ETHTOOL_SMSGLVL 0x00000008 /* Set driver msg level, priv. */
-#endif
-#ifndef ETHTOOL_NWAY_RST
-#define ETHTOOL_NWAY_RST 0x00000009 /* Restart autonegotiation, priv */
-#endif
-#ifndef ETHTOOL_GLINK
-#define ETHTOOL_GLINK 0x0000000a /* Get link status */
-#endif
-#ifndef ETHTOOL_GEEPROM
-#define ETHTOOL_GEEPROM 0x0000000b /* Get EEPROM data */
-#endif
-#ifndef ETHTOOL_SEEPROM
-#define ETHTOOL_SEEPROM 0x0000000c /* Set EEPROM data */
-#endif
-#ifndef ETHTOOL_GCOALESCE
-#define ETHTOOL_GCOALESCE 0x0000000e /* Get coalesce config */
-/* for configuring coalescing parameters of chip */
-#define ethtool_coalesce _kc_ethtool_coalesce
-struct _kc_ethtool_coalesce {
- u32 cmd; /* ETHTOOL_{G,S}COALESCE */
-
- /* How many usecs to delay an RX interrupt after
- * a packet arrives. If 0, only rx_max_coalesced_frames
- * is used.
- */
- u32 rx_coalesce_usecs;
-
- /* How many packets to delay an RX interrupt after
- * a packet arrives. If 0, only rx_coalesce_usecs is
- * used. It is illegal to set both usecs and max frames
- * to zero as this would cause RX interrupts to never be
- * generated.
- */
- u32 rx_max_coalesced_frames;
-
- /* Same as above two parameters, except that these values
- * apply while an IRQ is being serviced by the host. Not
- * all cards support this feature and the values are ignored
- * in that case.
- */
- u32 rx_coalesce_usecs_irq;
- u32 rx_max_coalesced_frames_irq;
-
- /* How many usecs to delay a TX interrupt after
- * a packet is sent. If 0, only tx_max_coalesced_frames
- * is used.
- */
- u32 tx_coalesce_usecs;
-
- /* How many packets to delay a TX interrupt after
- * a packet is sent. If 0, only tx_coalesce_usecs is
- * used. It is illegal to set both usecs and max frames
- * to zero as this would cause TX interrupts to never be
- * generated.
- */
- u32 tx_max_coalesced_frames;
-
- /* Same as above two parameters, except that these values
- * apply while an IRQ is being serviced by the host. Not
- * all cards support this feature and the values are ignored
- * in that case.
- */
- u32 tx_coalesce_usecs_irq;
- u32 tx_max_coalesced_frames_irq;
-
- /* How many usecs to delay in-memory statistics
- * block updates. Some drivers do not have an in-memory
- * statistic block, and in such cases this value is ignored.
- * This value must not be zero.
- */
- u32 stats_block_coalesce_usecs;
-
- /* Adaptive RX/TX coalescing is an algorithm implemented by
- * some drivers to improve latency under low packet rates and
- * improve throughput under high packet rates. Some drivers
- * only implement one of RX or TX adaptive coalescing. Anything
- * not implemented by the driver causes these values to be
- * silently ignored.
- */
- u32 use_adaptive_rx_coalesce;
- u32 use_adaptive_tx_coalesce;
-
- /* When the packet rate (measured in packets per second)
- * is below pkt_rate_low, the {rx,tx}_*_low parameters are
- * used.
- */
- u32 pkt_rate_low;
- u32 rx_coalesce_usecs_low;
- u32 rx_max_coalesced_frames_low;
- u32 tx_coalesce_usecs_low;
- u32 tx_max_coalesced_frames_low;
-
- /* When the packet rate is below pkt_rate_high but above
- * pkt_rate_low (both measured in packets per second) the
- * normal {rx,tx}_* coalescing parameters are used.
- */
-
- /* When the packet rate is (measured in packets per second)
- * is above pkt_rate_high, the {rx,tx}_*_high parameters are
- * used.
- */
- u32 pkt_rate_high;
- u32 rx_coalesce_usecs_high;
- u32 rx_max_coalesced_frames_high;
- u32 tx_coalesce_usecs_high;
- u32 tx_max_coalesced_frames_high;
-
- /* How often to do adaptive coalescing packet rate sampling,
- * measured in seconds. Must not be zero.
- */
- u32 rate_sample_interval;
-};
-#endif /* ETHTOOL_GCOALESCE */
-
-#ifndef ETHTOOL_SCOALESCE
-#define ETHTOOL_SCOALESCE 0x0000000f /* Set coalesce config. */
-#endif
-#ifndef ETHTOOL_GRINGPARAM
-#define ETHTOOL_GRINGPARAM 0x00000010 /* Get ring parameters */
-/* for configuring RX/TX ring parameters */
-#define ethtool_ringparam _kc_ethtool_ringparam
-struct _kc_ethtool_ringparam {
- u32 cmd; /* ETHTOOL_{G,S}RINGPARAM */
-
- /* Read only attributes. These indicate the maximum number
- * of pending RX/TX ring entries the driver will allow the
- * user to set.
- */
- u32 rx_max_pending;
- u32 rx_mini_max_pending;
- u32 rx_jumbo_max_pending;
- u32 tx_max_pending;
-
- /* Values changeable by the user. The valid values are
- * in the range 1 to the "*_max_pending" counterpart above.
- */
- u32 rx_pending;
- u32 rx_mini_pending;
- u32 rx_jumbo_pending;
- u32 tx_pending;
-};
-#endif /* ETHTOOL_GRINGPARAM */
-
-#ifndef ETHTOOL_SRINGPARAM
-#define ETHTOOL_SRINGPARAM 0x00000011 /* Set ring parameters, priv. */
-#endif
-#ifndef ETHTOOL_GPAUSEPARAM
-#define ETHTOOL_GPAUSEPARAM 0x00000012 /* Get pause parameters */
-/* for configuring link flow control parameters */
-#define ethtool_pauseparam _kc_ethtool_pauseparam
-struct _kc_ethtool_pauseparam {
- u32 cmd; /* ETHTOOL_{G,S}PAUSEPARAM */
-
- /* If the link is being auto-negotiated (via ethtool_cmd.autoneg
- * being true) the user may set 'autonet' here non-zero to have the
- * pause parameters be auto-negotiated too. In such a case, the
- * {rx,tx}_pause values below determine what capabilities are
- * advertised.
- *
- * If 'autoneg' is zero or the link is not being auto-negotiated,
- * then {rx,tx}_pause force the driver to use/not-use pause
- * flow control.
- */
- u32 autoneg;
- u32 rx_pause;
- u32 tx_pause;
-};
-#endif /* ETHTOOL_GPAUSEPARAM */
-
-#ifndef ETHTOOL_SPAUSEPARAM
-#define ETHTOOL_SPAUSEPARAM 0x00000013 /* Set pause parameters. */
-#endif
-#ifndef ETHTOOL_GRXCSUM
-#define ETHTOOL_GRXCSUM 0x00000014 /* Get RX hw csum enable (ethtool_value) */
-#endif
-#ifndef ETHTOOL_SRXCSUM
-#define ETHTOOL_SRXCSUM 0x00000015 /* Set RX hw csum enable (ethtool_value) */
-#endif
-#ifndef ETHTOOL_GTXCSUM
-#define ETHTOOL_GTXCSUM 0x00000016 /* Get TX hw csum enable (ethtool_value) */
-#endif
-#ifndef ETHTOOL_STXCSUM
-#define ETHTOOL_STXCSUM 0x00000017 /* Set TX hw csum enable (ethtool_value) */
-#endif
-#ifndef ETHTOOL_GSG
-#define ETHTOOL_GSG 0x00000018 /* Get scatter-gather enable
-* (ethtool_value) */
-#endif
-#ifndef ETHTOOL_SSG
-#define ETHTOOL_SSG 0x00000019 /* Set scatter-gather enable
-* (ethtool_value). */
-#endif
-#ifndef ETHTOOL_TEST
-#define ETHTOOL_TEST 0x0000001a /* execute NIC self-test, priv. */
-#endif
-#ifndef ETHTOOL_GSTRINGS
-#define ETHTOOL_GSTRINGS 0x0000001b /* get specified string set */
-#endif
-#ifndef ETHTOOL_PHYS_ID
-#define ETHTOOL_PHYS_ID 0x0000001c /* identify the NIC */
-#endif
-#ifndef ETHTOOL_GSTATS
-#define ETHTOOL_GSTATS 0x0000001d /* get NIC-specific statistics */
-#endif
-#ifndef ETHTOOL_GTSO
-#define ETHTOOL_GTSO 0x0000001e /* Get TSO enable (ethtool_value) */
-#endif
-#ifndef ETHTOOL_STSO
-#define ETHTOOL_STSO 0x0000001f /* Set TSO enable (ethtool_value) */
-#endif
-
-#ifndef ETHTOOL_BUSINFO_LEN
-#define ETHTOOL_BUSINFO_LEN 32
-#endif
-
-/*****************************************************************************/
-
-enum mcfg {
- CFG_METHOD_1 = 0x00,
- CFG_METHOD_2 = 0x01,
- CFG_METHOD_3 = 0x02,
- CFG_METHOD_4 = 0x04,
- CFG_METHOD_5 = 0x05,
- CFG_METHOD_6 = 0x06,
-};
-
-enum pcfg {
- PCFG_METHOD_1 = 0x03, /* PHY Reg 0x03 bit0-3 == 0x0000 */
- PCFG_METHOD_2 = 0x04, /* PHY Reg 0x03 bit0-3 == 0x0000 */
- PCFG_METHOD_3 = 0x05, /* PHY Reg 0x03 bit0-3 == 0x0000 */
- PCFG_METHOD_4 = 0x06, /* PHY Reg 0x03 bit0-3 == 0x0001 */
- PCFG_METHOD_5 = 0x07, /* PHY Reg 0x03 bit0-3 == 0x0002 */
- PCFG_METHOD_6 = 0x08, /* PHY Reg 0x03 bit0-3 == 0x0003 */
-};
-
-enum RTL8169_registers {
- MAC0 = 0x00, /* Ethernet hardware address. */
- MAC4 = 0x04,
- MAR0 = 0x08, /* 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,
- RxConfig = 0x44,
- RxMissed = 0x4C,
- Cfg9346 = 0x50,
- Config0 = 0x51,
- Config1 = 0x52,
- Config2 = 0x53,
- Config3 = 0x54,
- Config4 = 0x55,
- Config5 = 0x56,
- TimeInt0 = 0x58,
- MultiIntr = 0x5C,
- PHYAR = 0x60,
- PHYstatus = 0x6C,
- Offset_7Ch = 0x7C,
- RxMaxSize = 0xDA,
- CPlusCmd = 0xE0,
- IntrMitigate = 0xE2,
- RxDescAddrLow = 0xE4,
- RxDescAddrHigh = 0xE8,
- Reserved1 = 0xEC,
- FuncEvent = 0xF0,
- FuncEventMask = 0xF4,
- FuncPresetState = 0xF8,
- FuncForceEvent = 0xFC,
-};
-
-enum RTL8169_register_content {
- /* InterruptStatusBits */
- SYSErr = 0x8000,
- PCSTimeout = 0x4000,
- SWInt = 0x0100,
- TxDescUnavail = 0x80,
- RxFIFOOver = 0x40,
- LinkChg = 0x20,
- RxOverflow = 0x10,
- TxErr = 0x08,
- TxOK = 0x04,
- RxErr = 0x02,
- RxOK = 0x01,
-
- /* RxStatusDesc */
- RxRWT = (1 << 22),
- RxRES = (1 << 21),
- RxRUNT = (1 << 20),
- RxCRC = (1 << 19),
-
- /* ChipCmdBits */
- CmdReset = 0x10,
- CmdRxEnb = 0x08,
- CmdTxEnb = 0x04,
- RxBufEmpty = 0x01,
-
- /* Cfg9346Bits */
- Cfg9346_Lock = 0x00,
- Cfg9346_Unlock = 0xC0,
-
- /* rx_mode_bits */
- AcceptErr = 0x20,
- AcceptRunt = 0x10,
- AcceptBroadcast = 0x08,
- AcceptMulticast = 0x04,
- AcceptMyPhys = 0x02,
- AcceptAllPhys = 0x01,
-
- /* Transmit Priority Polling*/
- HPQ = 0x80,
- NPQ = 0x40,
- FSWInt = 0x01,
-
- /* RxConfigBits */
- Reserved2_shift = 13,
- RxCfgDMAShift = 8,
-
- /* TxConfigBits */
- TxInterFrameGapShift = 24,
- TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */
- TxMACLoopBack = (1 << 17), /* MAC loopback */
-
- /* Config1 register p.24 */
- PMEnable = (1 << 0), /* Power Management Enable */
- VPDEnable = (1 << 1), /* RTL8169 VPD eanble */
-
- /* Config2 register p.26 */
- 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 */
-
- /* Config4 register */
- iMode = (1 << 0), /* Improve IP/TCP checksum compatibility with some NIC cards for RTL8169SB */
-
- /* 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 */
- LanWake = (1 << 1), /* LanWake enable/disable */
- PMEStatus = (1 << 0), /* PME status can be reset by PCI RST# */
-
- /* CPlusCmd p.31 */
- EnAnaPLL = (1 << 14),
- RxVlan = (1 << 6),
- RxChkSum = (1 << 5),
- PCIDAC = (1 << 4),
- PCIMulRW = (1 << 3),
-
- /* rtl8169_PHYstatus */
- TxFlowCtrl = 0x40,
- RxFlowCtrl = 0x20,
- _1000bpsF = 0x10,
- _100bps = 0x08,
- _10bps = 0x04,
- LinkStatus = 0x02,
- FullDup = 0x01,
-
- /* DumpCounterCommand */
- CounterDump = 0x8,
-
- /* PHY access */
- PHYAR_Flag = 0x80000000,
- PHYAR_Write = 0x80000000,
- PHYAR_Read = 0x00000000,
- PHYAR_Reg_Mask = 0x1f,
- PHYAR_Reg_shift = 16,
- PHYAR_Data_Mask = 0xffff,
-};
-
-enum _DescStatusBit {
- 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 */
-
- /* Tx private */
- LargeSend = (1 << 27), /* TCP Large Send Offload (TSO) */
- MSSShift = 16, /* MSS value position */
- MSSMask = 0x7FFU, /* MSS value 11 bits */
- 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 */
-
- /* Rx private */
- PID1 = (1 << 18), /* Protocol ID bit 1/2 */
- PID0 = (1 << 17), /* Protocol ID bit 2/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 */
- RxVlanTag = (1 << 16), /* VLAN tag available */
-};
-
-enum bits {
- BIT_0 = (1 << 0),
- BIT_1 = (1 << 1),
- BIT_2 = (1 << 2),
- BIT_3 = (1 << 3),
- BIT_4 = (1 << 4),
- BIT_5 = (1 << 5),
- BIT_6 = (1 << 6),
- BIT_7 = (1 << 7),
- BIT_8 = (1 << 8),
- BIT_9 = (1 << 9),
- BIT_10 = (1 << 10),
- BIT_11 = (1 << 11),
- BIT_12 = (1 << 12),
- BIT_13 = (1 << 13),
- BIT_14 = (1 << 14),
- BIT_15 = (1 << 15),
- BIT_16 = (1 << 16),
- BIT_17 = (1 << 17),
- BIT_18 = (1 << 18),
- BIT_19 = (1 << 19),
- BIT_20 = (1 << 20),
- BIT_21 = (1 << 21),
- BIT_22 = (1 << 22),
- BIT_23 = (1 << 23),
- BIT_24 = (1 << 24),
- BIT_25 = (1 << 25),
- BIT_26 = (1 << 26),
- BIT_27 = (1 << 27),
- BIT_28 = (1 << 28),
- BIT_29 = (1 << 29),
- BIT_30 = (1 << 30),
- BIT_31 = (1 << 31)
-};
-
-#define RsvdMask 0x3fffc000
-
-struct TxDesc {
- u32 opts1;
- u32 opts2;
- u64 addr;
-};
-
-struct RxDesc {
- u32 opts1;
- u32 opts2;
- u64 addr;
-};
-
-struct ring_info {
- struct sk_buff *skb;
- u32 len;
- u8 __pad[sizeof(void *) - sizeof(u32)];
-};
-
-enum wol_capability {
- WOL_DISABLED = 0,
- WOL_ENABLED = 1
-};
-
-struct pci_resource {
- u8 cmd;
- u8 cls;
- u16 io_base_h;
- u16 io_base_l;
- u16 mem_base_h;
- u16 mem_base_l;
- u8 ilr;
- u16 resv_0x20_h;
- u16 resv_0x20_l;
- u16 resv_0x24_h;
- u16 resv_0x24_l;
-};
-
-/* Flow Control Settings */
-enum rtl8169_fc_mode {
- rtl8169_fc_none = 0,
- rtl8169_fc_rx_pause,
- rtl8169_fc_tx_pause,
- rtl8169_fc_full,
- rtl8169_fc_default
-};
-
-struct rtl8169_private {
- void __iomem *mmio_addr; /* memory map physical address */
- struct pci_dev *pci_dev; /* Index of PCI device */
- struct net_device *dev;
-#ifdef CONFIG_R8169_NAPI
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
- struct napi_struct napi;
-#endif
-#endif
- struct net_device_stats stats; /* statistics of net device */
- spinlock_t lock; /* spin lock flag */
- u32 msg_enable;
- int max_jumbo_frame_size;
- int chipset;
- int mcfg;
- int pcfg;
- 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_rx;
- 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 sk_buff *Rx_skbuff[NUM_RX_DESC]; /* Rx data buffers */
- struct ring_info tx_skb[NUM_TX_DESC]; /* Tx data buffers */
- unsigned align;
- unsigned rx_buf_sz;
- struct timer_list link_timer;
- struct timer_list esd_timer;
- struct pci_resource pci_cfg_space;
- unsigned int esd_flag;
- unsigned int pci_cfg_is_read;
- u16 cp_cmd;
- u16 intr_mask;
- int phy_auto_nego_reg;
- int phy_1000_ctrl_reg;
- u8 org_mac_addr[NODE_ADDRESS_SIZE];
-
-#ifdef CONFIG_R8169_VLAN
- struct vlan_group *vlgrp;
-#endif
- u8 autoneg;
- u8 duplex;
- u32 speed;
- u32 advertising;
- enum rtl8169_fc_mode fcpause;
-
- int (*set_speed)(struct net_device *, u8 autoneg, u32 speed, u8 duplex, u32 adv);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,6,0)
- void (*get_settings)(struct net_device *, struct ethtool_cmd *);
-#else
- void (*get_settings)(struct net_device *, struct ethtool_link_ksettings *);
-#endif
- void (*phy_reset_enable)(struct net_device *);
- unsigned int (*phy_reset_pending)(struct net_device *);
- unsigned int (*link_ok)(struct net_device *);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
- struct work_struct task;
-#else
- struct delayed_work task;
-#endif
- unsigned wol_enabled;
- unsigned features;
-
- u8 UseSwPaddingShortPkt;
-
- u8 random_mac;
-};
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34)
-#define netdev_mc_count(dev) ((dev)->mc_count)
-#define netdev_mc_empty(dev) (netdev_mc_count(dev) == 0)
-#endif
-
-#define LSO_32K 32000
-#define LSO_64K 64000
-
-#define NIC_MIN_PHYS_BUF_COUNT (2)
-#define NIC_MAX_PHYS_BUF_COUNT_LSO_64K (24)
-#define NIC_MAX_PHYS_BUF_COUNT_LSO2 (16*4)
-
-#define MSS_MAX 0x07ffu /* MSS value */
+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);
diff --git a/r8169_firmware.c b/r8169_firmware.c
new file mode 100644
index 0000000..cbc6b84
--- /dev/null
+++ b/r8169_firmware.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* r8169_firmware.c: RealTek 8169/8168/8101 ethernet driver.
+ *
+ * Copyright (c) 2002 ShuChen
+ * Copyright (c) 2003 - 2007 Francois Romieu
+ * Copyright (c) a lot of people too. Please respect their work.
+ *
+ * See MAINTAINERS file for support contact information.
+ */
+
+#include
+#include
+
+#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, RTL_VER_SIZE);
+
+ 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;
+
+ if (!action)
+ break;
+
+ 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 = request_firmware(&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_err(rtl_fw->dev, "Unable to load firmware %s (%d)\n",
+ rtl_fw->fw_name, rc);
+ return rc;
+}
diff --git a/r8169_firmware.h b/r8169_firmware.h
new file mode 100644
index 0000000..7dc348e
--- /dev/null
+++ b/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
+ * Copyright (c) 2003 - 2007 Francois Romieu
+ * Copyright (c) a lot of people too. Please respect their work.
+ *
+ * See MAINTAINERS file for support contact information.
+ */
+
+#include
+#include
+
+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/r8169_main.c b/r8169_main.c
new file mode 100644
index 0000000..b502824
--- /dev/null
+++ b/r8169_main.c
@@ -0,0 +1,5348 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * r8169.c: RealTek 8169/8168/8101 ethernet driver.
+ *
+ * Copyright (c) 2002 ShuChen
+ * Copyright (c) 2003 - 2007 Francois Romieu
+ * Copyright (c) a lot of people too. Please respect their work.
+ *
+ * See MAINTAINERS file for support contact information.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#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"
+
+/* 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. */
+#define MC_FILTER_LIMIT 32
+
+#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 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)
+
+static const struct {
+ const char *name;
+ const char *fw_name;
+} rtl_chip_infos[] = {
+ /* PCI devices. */
+ [RTL_GIGA_MAC_VER_02] = {"RTL8169s" },
+ [RTL_GIGA_MAC_VER_03] = {"RTL8110s" },
+ [RTL_GIGA_MAC_VER_04] = {"RTL8169sb/8110sb" },
+ [RTL_GIGA_MAC_VER_05] = {"RTL8169sc/8110sc" },
+ [RTL_GIGA_MAC_VER_06] = {"RTL8169sc/8110sc" },
+ /* PCI-E devices. */
+ [RTL_GIGA_MAC_VER_07] = {"RTL8102e" },
+ [RTL_GIGA_MAC_VER_08] = {"RTL8102e" },
+ [RTL_GIGA_MAC_VER_09] = {"RTL8102e/RTL8103e" },
+ [RTL_GIGA_MAC_VER_10] = {"RTL8101e/RTL8100e" },
+ [RTL_GIGA_MAC_VER_11] = {"RTL8168b/8111b" },
+ [RTL_GIGA_MAC_VER_14] = {"RTL8401" },
+ [RTL_GIGA_MAC_VER_17] = {"RTL8168b/8111b" },
+ [RTL_GIGA_MAC_VER_18] = {"RTL8168cp/8111cp" },
+ [RTL_GIGA_MAC_VER_19] = {"RTL8168c/8111c" },
+ [RTL_GIGA_MAC_VER_20] = {"RTL8168c/8111c" },
+ [RTL_GIGA_MAC_VER_21] = {"RTL8168c/8111c" },
+ [RTL_GIGA_MAC_VER_22] = {"RTL8168c/8111c" },
+ [RTL_GIGA_MAC_VER_23] = {"RTL8168cp/8111cp" },
+ [RTL_GIGA_MAC_VER_24] = {"RTL8168cp/8111cp" },
+ [RTL_GIGA_MAC_VER_25] = {"RTL8168d/8111d", FIRMWARE_8168D_1},
+ [RTL_GIGA_MAC_VER_26] = {"RTL8168d/8111d", FIRMWARE_8168D_2},
+ [RTL_GIGA_MAC_VER_28] = {"RTL8168dp/8111dp" },
+ [RTL_GIGA_MAC_VER_29] = {"RTL8105e", FIRMWARE_8105E_1},
+ [RTL_GIGA_MAC_VER_30] = {"RTL8105e", FIRMWARE_8105E_1},
+ [RTL_GIGA_MAC_VER_31] = {"RTL8168dp/8111dp" },
+ [RTL_GIGA_MAC_VER_32] = {"RTL8168e/8111e", FIRMWARE_8168E_1},
+ [RTL_GIGA_MAC_VER_33] = {"RTL8168e/8111e", FIRMWARE_8168E_2},
+ [RTL_GIGA_MAC_VER_34] = {"RTL8168evl/8111evl", FIRMWARE_8168E_3},
+ [RTL_GIGA_MAC_VER_35] = {"RTL8168f/8111f", FIRMWARE_8168F_1},
+ [RTL_GIGA_MAC_VER_36] = {"RTL8168f/8111f", FIRMWARE_8168F_2},
+ [RTL_GIGA_MAC_VER_37] = {"RTL8402", FIRMWARE_8402_1 },
+ [RTL_GIGA_MAC_VER_38] = {"RTL8411", FIRMWARE_8411_1 },
+ [RTL_GIGA_MAC_VER_39] = {"RTL8106e", FIRMWARE_8106E_1},
+ [RTL_GIGA_MAC_VER_40] = {"RTL8168g/8111g", FIRMWARE_8168G_2},
+ [RTL_GIGA_MAC_VER_42] = {"RTL8168gu/8111gu", FIRMWARE_8168G_3},
+ [RTL_GIGA_MAC_VER_43] = {"RTL8106eus", FIRMWARE_8106E_2},
+ [RTL_GIGA_MAC_VER_44] = {"RTL8411b", FIRMWARE_8411_2 },
+ [RTL_GIGA_MAC_VER_46] = {"RTL8168h/8111h", FIRMWARE_8168H_2},
+ [RTL_GIGA_MAC_VER_48] = {"RTL8107e", FIRMWARE_8107E_2},
+ [RTL_GIGA_MAC_VER_51] = {"RTL8168ep/8111ep" },
+ [RTL_GIGA_MAC_VER_52] = {"RTL8168fp/RTL8117", FIRMWARE_8168FP_3},
+ [RTL_GIGA_MAC_VER_53] = {"RTL8168fp/RTL8117", },
+ [RTL_GIGA_MAC_VER_61] = {"RTL8125A", FIRMWARE_8125A_3},
+ /* reserve 62 for CFG_METHOD_4 in the vendor driver */
+ [RTL_GIGA_MAC_VER_63] = {"RTL8125B", FIRMWARE_8125B_2},
+};
+
+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), RTL_CFG_NO_GBIT },
+ { PCI_VDEVICE(REALTEK, 0x8161) },
+ { PCI_VDEVICE(REALTEK, 0x8162) },
+ { PCI_VDEVICE(REALTEK, 0x8167) },
+#ifdef INCLUDE_R8168
+ { PCI_VDEVICE(REALTEK, 0x8168) },
+ { PCI_VDEVICE(NCUBE, 0x8168) },
+#endif
+ { PCI_VDEVICE(REALTEK, 0x8169) },
+ { PCI_VENDOR_ID_DLINK, 0x4300,
+ PCI_VENDOR_ID_DLINK, 0x4b10, 0, 0 },
+ { PCI_VDEVICE(DLINK, 0x4300) },
+ { PCI_VDEVICE(DLINK, 0x4302) },
+ { PCI_VDEVICE(AT, 0xc107) },
+ { PCI_VDEVICE(USR, 0x0116) },
+ { PCI_VENDOR_ID_LINKSYS, 0x1032, PCI_ANY_ID, 0x0024 },
+ { 0x0001, 0x8168, PCI_ANY_ID, 0x2410 },
+#ifdef INCLUDE_R8125
+ { PCI_VDEVICE(REALTEK, 0x8125) },
+#endif
+ { PCI_VDEVICE(REALTEK, 0x3000) },
+ {}
+};
+
+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,
+#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 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,
+};
+
+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_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)
+};
+
+enum rtl8125_registers {
+ IntrMask_8125 = 0x38,
+ IntrStatus_8125 = 0x3c,
+ TxPoll_8125 = 0x90,
+ MAC0_BKP = 0x19e0,
+ EEE_TXIDLE_TIMER_8125 = 0x6048,
+};
+
+#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_underun;
+};
+
+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_ENABLED = 0,
+ RTL_FLAG_TASK_RESET_PENDING,
+ RTL_FLAG_MAX
+};
+
+enum rtl_dash_type {
+ RTL_DASH_NONE,
+ RTL_DASH_DP,
+ RTL_DASH_EP,
+};
+
+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;
+ u32 irq_mask;
+ int irq;
+ struct clk *clk;
+
+ struct {
+ DECLARE_BITMAP(flags, RTL_FLAG_MAX);
+ struct work_struct work;
+ } wk;
+
+ unsigned supports_gmii:1;
+ unsigned aspm_manageable:1;
+ dma_addr_t counters_phys_addr;
+ struct rtl8169_counters *counters;
+ struct rtl8169_tc_offsets tc_offset;
+ u32 saved_wolopts;
+ int eee_adv;
+
+ const char *fw_name;
+ struct rtl_fw *rtl_fw;
+
+ u32 ocp_base;
+};
+
+typedef void (*rtl_generic_fct)(struct rtl8169_private *tp);
+
+MODULE_AUTHOR("Realtek and the Linux r8169 crew ");
+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);
+
+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 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_53;
+}
+
+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)
+
+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 ||
+ tp->mac_version == RTL_GIGA_MAC_VER_53))
+ *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;
+
+ 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 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 void r8168_mac_ocp_modify(struct rtl8169_private *tp, u32 reg, u16 mask,
+ u16 set)
+{
+ u16 data = r8168_mac_ocp_read(tp, reg);
+
+ r8168_mac_ocp_write(tp, reg, (data & ~mask) | set);
+}
+
+/* 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_63:
+ 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_63:
+ 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);
+ 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);
+ rtl_loop_wait_high(tp, &rtl_ep_ocp_read_cond, 10000, 10);
+}
+
+static void rtl8168_driver_start(struct rtl8169_private *tp)
+{
+ if (tp->dash_type == RTL_DASH_DP)
+ rtl8168dp_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);
+ 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);
+ rtl_loop_wait_low(tp, &rtl_ep_ocp_read_cond, 10000, 10);
+}
+
+static void rtl8168_driver_stop(struct rtl8169_private *tp)
+{
+ if (tp->dash_type == RTL_DASH_DP)
+ rtl8168dp_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 enum rtl_dash_type rtl_check_dash(struct rtl8169_private *tp)
+{
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_28:
+ case RTL_GIGA_MAC_VER_31:
+ return r8168dp_check_dash(tp) ? RTL_DASH_DP : RTL_DASH_NONE;
+ case RTL_GIGA_MAC_VER_51 ... RTL_GIGA_MAC_VER_53:
+ return r8168ep_check_dash(tp) ? RTL_DASH_EP : 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_25 ... RTL_GIGA_MAC_VER_26:
+ case RTL_GIGA_MAC_VER_29 ... RTL_GIGA_MAC_VER_30:
+ case RTL_GIGA_MAC_VER_32 ... RTL_GIGA_MAC_VER_37:
+ case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_63:
+ if (enable)
+ RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) & ~D3_NO_PLL_DOWN);
+ else
+ RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) | D3_NO_PLL_DOWN);
+ break;
+ default:
+ 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)
+{
+ static const struct {
+ u32 opt;
+ u16 reg;
+ u8 mask;
+ } cfg[] = {
+ { WAKE_PHY, Config3, LinkUp },
+ { WAKE_UCAST, Config5, UWF },
+ { WAKE_BCAST, Config5, BWF },
+ { WAKE_MCAST, Config5, MWF },
+ { WAKE_ANY, Config5, LanWake },
+ { WAKE_MAGIC, Config3, MagicPacket }
+ };
+ unsigned int i, tmp = ARRAY_SIZE(cfg);
+ u8 options;
+
+ rtl_unlock_config_regs(tp);
+
+ if (rtl_is_8168evl_up(tp)) {
+ tmp--;
+ 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)) {
+ tmp--;
+ if (wolopts & WAKE_MAGIC)
+ r8168_mac_ocp_modify(tp, 0xc0b6, 0, BIT(0));
+ else
+ r8168_mac_ocp_modify(tp, 0xc0b6, BIT(0), 0);
+ }
+
+ for (i = 0; i < tmp; i++) {
+ options = RTL_R8(tp, cfg[i].reg) & ~cfg[i].mask;
+ if (wolopts & cfg[i].opt)
+ options |= cfg[i].mask;
+ RTL_W8(tp, cfg[i].reg, options);
+ }
+
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_06:
+ options = RTL_R8(tp, Config1) & ~PMEnable;
+ if (wolopts)
+ options |= PMEnable;
+ RTL_W8(tp, Config1, options);
+ break;
+ case RTL_GIGA_MAC_VER_34:
+ case RTL_GIGA_MAC_VER_37:
+ case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_63:
+ options = RTL_R8(tp, Config2) & ~PME_SIGNAL;
+ if (wolopts)
+ options |= PME_SIGNAL;
+ RTL_W8(tp, Config2, options);
+ break;
+ default:
+ break;
+ }
+
+ rtl_lock_config_regs(tp);
+
+ device_set_wakeup_enable(tp_to_dev(tp), wolopts);
+
+ if (tp->dash_type == RTL_DASH_NONE) {
+ rtl_set_d3_pll_down(tp, !wolopts);
+ tp->dev->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, sizeof(info->driver));
+ strscpy(info->bus_info, pci_name(tp->pci_dev), sizeof(info->bus_info));
+ BUILD_BUG_ON(sizeof(info->fw_version) < sizeof(rtl_fw->version));
+ if (rtl_fw)
+ strscpy(info->fw_version, rtl_fw->version,
+ sizeof(info->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_underun);
+}
+
+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 int rtl8169_get_eee(struct net_device *dev, struct ethtool_eee *data)
+{
+ struct rtl8169_private *tp = netdev_priv(dev);
+
+ if (!rtl_supports_eee(tp))
+ return -EOPNOTSUPP;
+
+ return phy_ethtool_get_eee(tp->phydev, data);
+}
+
+static int rtl8169_set_eee(struct net_device *dev, struct ethtool_eee *data)
+{
+ struct rtl8169_private *tp = netdev_priv(dev);
+ int ret;
+
+ if (!rtl_supports_eee(tp))
+ return -EOPNOTSUPP;
+
+ ret = phy_ethtool_set_eee(tp->phydev, data);
+
+ if (!ret)
+ tp->eee_adv = phy_read_mmd(dev->phydev, MDIO_MMD_AN,
+ MDIO_AN_EEE_ADV);
+ return ret;
+}
+
+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_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 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 = phy_ethtool_set_link_ksettings,
+ .get_ringparam = rtl8169_get_ringparam,
+ .get_pauseparam = rtl8169_get_pauseparam,
+ .set_pauseparam = rtl8169_set_pauseparam,
+};
+
+static void rtl_enable_eee(struct rtl8169_private *tp)
+{
+ struct phy_device *phydev = tp->phydev;
+ int adv;
+
+ /* respect EEE advertisement the user may have set */
+ if (tp->eee_adv >= 0)
+ adv = tp->eee_adv;
+ else
+ adv = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE);
+
+ if (adv >= 0)
+ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv);
+}
+
+static enum mac_version rtl8169_get_mac_version(u16 xid, bool gmii)
+{
+ /*
+ * The driver currently handles the 8168Bf and the 8168Be identically
+ * but they can be identified more specifically through the test below
+ * if needed:
+ *
+ * (RTL_R32(tp, TxConfig) & 0x700000) == 0x500000 ? 8168Bf : 8168Be
+ *
+ * Same thing for the 8101Eb and the 8101Ec:
+ *
+ * (RTL_R32(tp, TxConfig) & 0x700000) == 0x200000 ? 8101Eb : 8101Ec
+ */
+ static const struct rtl_mac_info {
+ u16 mask;
+ u16 val;
+ enum mac_version ver;
+ } mac_info[] = {
+ /* 8125B family. */
+ { 0x7cf, 0x641, RTL_GIGA_MAC_VER_63 },
+
+ /* 8125A family. */
+ { 0x7cf, 0x609, RTL_GIGA_MAC_VER_61 },
+ /* It seems only XID 609 made it to the mass market.
+ * { 0x7cf, 0x608, RTL_GIGA_MAC_VER_60 },
+ * { 0x7c8, 0x608, RTL_GIGA_MAC_VER_61 },
+ */
+
+ /* RTL8117 */
+ { 0x7cf, 0x54b, RTL_GIGA_MAC_VER_53 },
+ { 0x7cf, 0x54a, RTL_GIGA_MAC_VER_52 },
+
+ /* 8168EP family. */
+ { 0x7cf, 0x502, RTL_GIGA_MAC_VER_51 },
+ /* It seems this chip version never made it to
+ * the wild. Let's disable detection.
+ * { 0x7cf, 0x501, RTL_GIGA_MAC_VER_50 },
+ * { 0x7cf, 0x500, RTL_GIGA_MAC_VER_49 },
+ */
+
+ /* 8168H family. */
+ { 0x7cf, 0x541, RTL_GIGA_MAC_VER_46 },
+ /* It seems this chip version never made it to
+ * the wild. Let's disable detection.
+ * { 0x7cf, 0x540, RTL_GIGA_MAC_VER_45 },
+ */
+
+ /* 8168G family. */
+ { 0x7cf, 0x5c8, RTL_GIGA_MAC_VER_44 },
+ { 0x7cf, 0x509, RTL_GIGA_MAC_VER_42 },
+ /* It seems this chip version never made it to
+ * the wild. Let's disable detection.
+ * { 0x7cf, 0x4c1, RTL_GIGA_MAC_VER_41 },
+ */
+ { 0x7cf, 0x4c0, RTL_GIGA_MAC_VER_40 },
+
+ /* 8168F family. */
+ { 0x7c8, 0x488, RTL_GIGA_MAC_VER_38 },
+ { 0x7cf, 0x481, RTL_GIGA_MAC_VER_36 },
+ { 0x7cf, 0x480, RTL_GIGA_MAC_VER_35 },
+
+ /* 8168E family. */
+ { 0x7c8, 0x2c8, RTL_GIGA_MAC_VER_34 },
+ { 0x7cf, 0x2c1, RTL_GIGA_MAC_VER_32 },
+ { 0x7c8, 0x2c0, RTL_GIGA_MAC_VER_33 },
+
+ /* 8168D family. */
+ { 0x7cf, 0x281, RTL_GIGA_MAC_VER_25 },
+ { 0x7c8, 0x280, RTL_GIGA_MAC_VER_26 },
+
+ /* 8168DP family. */
+ /* It seems this early RTL8168dp version never made it to
+ * the wild. Support has been removed.
+ * { 0x7cf, 0x288, RTL_GIGA_MAC_VER_27 },
+ */
+ { 0x7cf, 0x28a, RTL_GIGA_MAC_VER_28 },
+ { 0x7cf, 0x28b, RTL_GIGA_MAC_VER_31 },
+
+ /* 8168C family. */
+ { 0x7cf, 0x3c9, RTL_GIGA_MAC_VER_23 },
+ { 0x7cf, 0x3c8, RTL_GIGA_MAC_VER_18 },
+ { 0x7c8, 0x3c8, RTL_GIGA_MAC_VER_24 },
+ { 0x7cf, 0x3c0, RTL_GIGA_MAC_VER_19 },
+ { 0x7cf, 0x3c2, RTL_GIGA_MAC_VER_20 },
+ { 0x7cf, 0x3c3, RTL_GIGA_MAC_VER_21 },
+ { 0x7c8, 0x3c0, RTL_GIGA_MAC_VER_22 },
+
+ /* 8168B family. */
+ { 0x7c8, 0x380, RTL_GIGA_MAC_VER_17 },
+ { 0x7c8, 0x300, RTL_GIGA_MAC_VER_11 },
+
+ /* 8101 family. */
+ { 0x7c8, 0x448, RTL_GIGA_MAC_VER_39 },
+ { 0x7c8, 0x440, RTL_GIGA_MAC_VER_37 },
+ { 0x7cf, 0x409, RTL_GIGA_MAC_VER_29 },
+ { 0x7c8, 0x408, RTL_GIGA_MAC_VER_30 },
+ { 0x7cf, 0x349, RTL_GIGA_MAC_VER_08 },
+ { 0x7cf, 0x249, RTL_GIGA_MAC_VER_08 },
+ { 0x7cf, 0x348, RTL_GIGA_MAC_VER_07 },
+ { 0x7cf, 0x248, RTL_GIGA_MAC_VER_07 },
+ { 0x7cf, 0x240, RTL_GIGA_MAC_VER_14 },
+ { 0x7c8, 0x348, RTL_GIGA_MAC_VER_09 },
+ { 0x7c8, 0x248, RTL_GIGA_MAC_VER_09 },
+ { 0x7c8, 0x340, RTL_GIGA_MAC_VER_10 },
+
+ /* 8110 family. */
+ { 0xfc8, 0x980, RTL_GIGA_MAC_VER_06 },
+ { 0xfc8, 0x180, RTL_GIGA_MAC_VER_05 },
+ { 0xfc8, 0x100, RTL_GIGA_MAC_VER_04 },
+ { 0xfc8, 0x040, RTL_GIGA_MAC_VER_03 },
+ { 0xfc8, 0x008, RTL_GIGA_MAC_VER_02 },
+
+ /* Catch-all */
+ { 0x000, 0x000, RTL_GIGA_MAC_NONE }
+ };
+ const struct rtl_mac_info *p = mac_info;
+ enum mac_version ver;
+
+ while ((xid & p->mask) != p->val)
+ p++;
+ ver = p->ver;
+
+ if (ver != RTL_GIGA_MAC_NONE && !gmii) {
+ if (ver == RTL_GIGA_MAC_VER_42)
+ ver = RTL_GIGA_MAC_VER_43;
+ else if (ver == RTL_GIGA_MAC_VER_46)
+ ver = RTL_GIGA_MAC_VER_48;
+ }
+
+ return ver;
+}
+
+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 rtl8168_config_eee_mac(struct rtl8169_private *tp)
+{
+ /* Adjust EEE LED frequency */
+ if (tp->mac_version != RTL_GIGA_MAC_VER_38)
+ RTL_W8(tp, EEE_LED, RTL_R8(tp, EEE_LED) & ~0x07);
+
+ rtl_eri_set_bits(tp, 0x1b0, 0x0003);
+}
+
+static void rtl8125a_config_eee_mac(struct rtl8169_private *tp)
+{
+ r8168_mac_ocp_modify(tp, 0xe040, 0, BIT(1) | BIT(0));
+ r8168_mac_ocp_modify(tp, 0xeb62, 0, BIT(2) | BIT(1));
+}
+
+static void rtl8125_set_eee_txidle_timer(struct rtl8169_private *tp)
+{
+ RTL_W16(tp, EEE_TXIDLE_TIMER_8125, tp->dev->mtu + ETH_HLEN + 0x20);
+}
+
+static void rtl8125b_config_eee_mac(struct rtl8169_private *tp)
+{
+ rtl8125_set_eee_txidle_timer(tp);
+ r8168_mac_ocp_modify(tp, 0xe040, 0, BIT(1) | BIT(0));
+}
+
+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);
+ schedule_work(&tp->wk.work);
+}
+
+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);
+
+ /* We may have called phy_speed_down before */
+ phy_speed_up(tp->phydev);
+
+ if (rtl_supports_eee(tp))
+ rtl_enable_eee(tp);
+
+ 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_53:
+ RTL_W32(tp, RxConfig, RX128_INT_EN | RX_MULTI_EN | RX_DMA_BURST | RX_EARLY_OFF);
+ break;
+ case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63:
+ RTL_W32(tp, RxConfig, RX_FETCH_DFLT_8125 | RX_DMA_BURST);
+ 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 r8168c_hw_jumbo_enable(struct rtl8169_private *tp)
+{
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) | Jumbo_En0);
+ RTL_W8(tp, Config4, RTL_R8(tp, Config4) | Jumbo_En1);
+}
+
+static void r8168c_hw_jumbo_disable(struct rtl8169_private *tp)
+{
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Jumbo_En0);
+ RTL_W8(tp, Config4, RTL_R8(tp, Config4) & ~Jumbo_En1);
+}
+
+static void r8168dp_hw_jumbo_enable(struct rtl8169_private *tp)
+{
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) | Jumbo_En0);
+}
+
+static void r8168dp_hw_jumbo_disable(struct rtl8169_private *tp)
+{
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Jumbo_En0);
+}
+
+static void r8168e_hw_jumbo_enable(struct rtl8169_private *tp)
+{
+ RTL_W8(tp, MaxTxPacketSize, 0x24);
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) | Jumbo_En0);
+ RTL_W8(tp, Config4, RTL_R8(tp, Config4) | 0x01);
+}
+
+static void r8168e_hw_jumbo_disable(struct rtl8169_private *tp)
+{
+ RTL_W8(tp, MaxTxPacketSize, 0x3f);
+ RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Jumbo_En0);
+ RTL_W8(tp, Config4, RTL_R8(tp, Config4) & ~0x01);
+}
+
+static void r8168b_1_hw_jumbo_enable(struct rtl8169_private *tp)
+{
+ RTL_W8(tp, Config4, RTL_R8(tp, Config4) | (1 << 0));
+}
+
+static void r8168b_1_hw_jumbo_disable(struct rtl8169_private *tp)
+{
+ RTL_W8(tp, Config4, RTL_R8(tp, Config4) & ~(1 << 0));
+}
+
+static void rtl_jumbo_config(struct rtl8169_private *tp)
+{
+ bool jumbo = tp->dev->mtu > ETH_DATA_LEN;
+ int readrq = 4096;
+
+ rtl_unlock_config_regs(tp);
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_17:
+ if (jumbo) {
+ readrq = 512;
+ r8168b_1_hw_jumbo_enable(tp);
+ } else {
+ r8168b_1_hw_jumbo_disable(tp);
+ }
+ break;
+ case RTL_GIGA_MAC_VER_18 ... RTL_GIGA_MAC_VER_26:
+ if (jumbo) {
+ readrq = 512;
+ r8168c_hw_jumbo_enable(tp);
+ } else {
+ r8168c_hw_jumbo_disable(tp);
+ }
+ break;
+ case RTL_GIGA_MAC_VER_28:
+ if (jumbo)
+ r8168dp_hw_jumbo_enable(tp);
+ else
+ r8168dp_hw_jumbo_disable(tp);
+ break;
+ case RTL_GIGA_MAC_VER_31 ... RTL_GIGA_MAC_VER_33:
+ if (jumbo)
+ r8168e_hw_jumbo_enable(tp);
+ else
+ r8168e_hw_jumbo_disable(tp);
+ 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(sizeof(*rtl_fw), GFP_KERNEL);
+ 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_53:
+ 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_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->dash_type != RTL_DASH_NONE)
+ return;
+
+ 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 (netdev_mc_count(dev) > MC_FILTER_LIMIT ||
+ 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_set_aspm_entry_latency(struct rtl8169_private *tp, u8 val)
+{
+ struct pci_dev *pdev = tp->pci_dev;
+ u32 csi;
+
+ /* 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;
+
+ netdev_notice_once(tp->dev,
+ "No native access to PCI extended config space, falling back to CSI\n");
+ csi = rtl_csi_read(tp, 0x070c) & 0x00ffffff;
+ rtl_csi_write(tp, 0x070c, csi | 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_63:
+ 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_63:
+ r8168_mac_ocp_modify(tp, 0xc0ac, 0x1f80, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable)
+{
+ /* Don't enable ASPM in the chip if OS can't control ASPM */
+ if (enable && tp->aspm_manageable) {
+ RTL_W8(tp, Config5, RTL_R8(tp, Config5) | ASPM_en);
+ RTL_W8(tp, Config2, RTL_R8(tp, Config2) | ClkReqEn);
+
+ 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_63:
+ /* 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_63:
+ r8168_mac_ocp_modify(tp, 0xe092, 0x00ff, 0);
+ break;
+ default:
+ break;
+ }
+
+ RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~ClkReqEn);
+ RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~ASPM_en);
+ }
+
+ udelay(10);
+}
+
+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_W8(tp, Config5, RTL_R8(tp, Config5) & ~Spi_en);
+}
+
+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);
+
+ rtl8168_config_eee_mac(tp);
+
+ RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) | PFM_EN);
+ RTL_W32(tp, MISC, RTL_R32(tp, MISC) | PWM_EN);
+ RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~Spi_en);
+
+ rtl_hw_aspm_clkreq_enable(tp, true);
+}
+
+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_W8(tp, Config5, RTL_R8(tp, Config5) & ~Spi_en);
+
+ rtl8168_config_eee_mac(tp);
+}
+
+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);
+
+ rtl8168_config_eee_mac(tp);
+
+ 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);
+
+ /* disable aspm and clock request before access ephy */
+ rtl_hw_aspm_clkreq_enable(tp, false);
+ rtl_ephy_init(tp, e_info_8168g_1);
+ rtl_hw_aspm_clkreq_enable(tp, true);
+}
+
+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);
+
+ /* disable aspm and clock request before access ephy */
+ rtl_hw_aspm_clkreq_enable(tp, false);
+ rtl_ephy_init(tp, e_info_8168g_2);
+}
+
+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);
+
+ /* disable aspm and clock request before access ephy */
+ rtl_hw_aspm_clkreq_enable(tp, false);
+ 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);
+
+ r8168_mac_ocp_write(tp, 0xF800, 0xE008);
+ r8168_mac_ocp_write(tp, 0xF802, 0xE00A);
+ r8168_mac_ocp_write(tp, 0xF804, 0xE00C);
+ r8168_mac_ocp_write(tp, 0xF806, 0xE00E);
+ r8168_mac_ocp_write(tp, 0xF808, 0xE027);
+ r8168_mac_ocp_write(tp, 0xF80A, 0xE04F);
+ r8168_mac_ocp_write(tp, 0xF80C, 0xE05E);
+ r8168_mac_ocp_write(tp, 0xF80E, 0xE065);
+ r8168_mac_ocp_write(tp, 0xF810, 0xC602);
+ r8168_mac_ocp_write(tp, 0xF812, 0xBE00);
+ r8168_mac_ocp_write(tp, 0xF814, 0x0000);
+ r8168_mac_ocp_write(tp, 0xF816, 0xC502);
+ r8168_mac_ocp_write(tp, 0xF818, 0xBD00);
+ r8168_mac_ocp_write(tp, 0xF81A, 0x074C);
+ r8168_mac_ocp_write(tp, 0xF81C, 0xC302);
+ r8168_mac_ocp_write(tp, 0xF81E, 0xBB00);
+ r8168_mac_ocp_write(tp, 0xF820, 0x080A);
+ r8168_mac_ocp_write(tp, 0xF822, 0x6420);
+ r8168_mac_ocp_write(tp, 0xF824, 0x48C2);
+ r8168_mac_ocp_write(tp, 0xF826, 0x8C20);
+ r8168_mac_ocp_write(tp, 0xF828, 0xC516);
+ r8168_mac_ocp_write(tp, 0xF82A, 0x64A4);
+ r8168_mac_ocp_write(tp, 0xF82C, 0x49C0);
+ r8168_mac_ocp_write(tp, 0xF82E, 0xF009);
+ r8168_mac_ocp_write(tp, 0xF830, 0x74A2);
+ r8168_mac_ocp_write(tp, 0xF832, 0x8CA5);
+ r8168_mac_ocp_write(tp, 0xF834, 0x74A0);
+ r8168_mac_ocp_write(tp, 0xF836, 0xC50E);
+ r8168_mac_ocp_write(tp, 0xF838, 0x9CA2);
+ r8168_mac_ocp_write(tp, 0xF83A, 0x1C11);
+ r8168_mac_ocp_write(tp, 0xF83C, 0x9CA0);
+ r8168_mac_ocp_write(tp, 0xF83E, 0xE006);
+ r8168_mac_ocp_write(tp, 0xF840, 0x74F8);
+ r8168_mac_ocp_write(tp, 0xF842, 0x48C4);
+ r8168_mac_ocp_write(tp, 0xF844, 0x8CF8);
+ r8168_mac_ocp_write(tp, 0xF846, 0xC404);
+ r8168_mac_ocp_write(tp, 0xF848, 0xBC00);
+ r8168_mac_ocp_write(tp, 0xF84A, 0xC403);
+ r8168_mac_ocp_write(tp, 0xF84C, 0xBC00);
+ r8168_mac_ocp_write(tp, 0xF84E, 0x0BF2);
+ r8168_mac_ocp_write(tp, 0xF850, 0x0C0A);
+ r8168_mac_ocp_write(tp, 0xF852, 0xE434);
+ r8168_mac_ocp_write(tp, 0xF854, 0xD3C0);
+ r8168_mac_ocp_write(tp, 0xF856, 0x49D9);
+ r8168_mac_ocp_write(tp, 0xF858, 0xF01F);
+ r8168_mac_ocp_write(tp, 0xF85A, 0xC526);
+ r8168_mac_ocp_write(tp, 0xF85C, 0x64A5);
+ r8168_mac_ocp_write(tp, 0xF85E, 0x1400);
+ r8168_mac_ocp_write(tp, 0xF860, 0xF007);
+ r8168_mac_ocp_write(tp, 0xF862, 0x0C01);
+ r8168_mac_ocp_write(tp, 0xF864, 0x8CA5);
+ r8168_mac_ocp_write(tp, 0xF866, 0x1C15);
+ r8168_mac_ocp_write(tp, 0xF868, 0xC51B);
+ r8168_mac_ocp_write(tp, 0xF86A, 0x9CA0);
+ r8168_mac_ocp_write(tp, 0xF86C, 0xE013);
+ r8168_mac_ocp_write(tp, 0xF86E, 0xC519);
+ r8168_mac_ocp_write(tp, 0xF870, 0x74A0);
+ r8168_mac_ocp_write(tp, 0xF872, 0x48C4);
+ r8168_mac_ocp_write(tp, 0xF874, 0x8CA0);
+ r8168_mac_ocp_write(tp, 0xF876, 0xC516);
+ r8168_mac_ocp_write(tp, 0xF878, 0x74A4);
+ r8168_mac_ocp_write(tp, 0xF87A, 0x48C8);
+ r8168_mac_ocp_write(tp, 0xF87C, 0x48CA);
+ r8168_mac_ocp_write(tp, 0xF87E, 0x9CA4);
+ r8168_mac_ocp_write(tp, 0xF880, 0xC512);
+ r8168_mac_ocp_write(tp, 0xF882, 0x1B00);
+ r8168_mac_ocp_write(tp, 0xF884, 0x9BA0);
+ r8168_mac_ocp_write(tp, 0xF886, 0x1B1C);
+ r8168_mac_ocp_write(tp, 0xF888, 0x483F);
+ r8168_mac_ocp_write(tp, 0xF88A, 0x9BA2);
+ r8168_mac_ocp_write(tp, 0xF88C, 0x1B04);
+ r8168_mac_ocp_write(tp, 0xF88E, 0xC508);
+ r8168_mac_ocp_write(tp, 0xF890, 0x9BA0);
+ r8168_mac_ocp_write(tp, 0xF892, 0xC505);
+ r8168_mac_ocp_write(tp, 0xF894, 0xBD00);
+ r8168_mac_ocp_write(tp, 0xF896, 0xC502);
+ r8168_mac_ocp_write(tp, 0xF898, 0xBD00);
+ r8168_mac_ocp_write(tp, 0xF89A, 0x0300);
+ r8168_mac_ocp_write(tp, 0xF89C, 0x051E);
+ r8168_mac_ocp_write(tp, 0xF89E, 0xE434);
+ r8168_mac_ocp_write(tp, 0xF8A0, 0xE018);
+ r8168_mac_ocp_write(tp, 0xF8A2, 0xE092);
+ r8168_mac_ocp_write(tp, 0xF8A4, 0xDE20);
+ r8168_mac_ocp_write(tp, 0xF8A6, 0xD3C0);
+ r8168_mac_ocp_write(tp, 0xF8A8, 0xC50F);
+ r8168_mac_ocp_write(tp, 0xF8AA, 0x76A4);
+ r8168_mac_ocp_write(tp, 0xF8AC, 0x49E3);
+ r8168_mac_ocp_write(tp, 0xF8AE, 0xF007);
+ r8168_mac_ocp_write(tp, 0xF8B0, 0x49C0);
+ r8168_mac_ocp_write(tp, 0xF8B2, 0xF103);
+ r8168_mac_ocp_write(tp, 0xF8B4, 0xC607);
+ r8168_mac_ocp_write(tp, 0xF8B6, 0xBE00);
+ r8168_mac_ocp_write(tp, 0xF8B8, 0xC606);
+ r8168_mac_ocp_write(tp, 0xF8BA, 0xBE00);
+ r8168_mac_ocp_write(tp, 0xF8BC, 0xC602);
+ r8168_mac_ocp_write(tp, 0xF8BE, 0xBE00);
+ r8168_mac_ocp_write(tp, 0xF8C0, 0x0C4C);
+ r8168_mac_ocp_write(tp, 0xF8C2, 0x0C28);
+ r8168_mac_ocp_write(tp, 0xF8C4, 0x0C2C);
+ r8168_mac_ocp_write(tp, 0xF8C6, 0xDC00);
+ r8168_mac_ocp_write(tp, 0xF8C8, 0xC707);
+ r8168_mac_ocp_write(tp, 0xF8CA, 0x1D00);
+ r8168_mac_ocp_write(tp, 0xF8CC, 0x8DE2);
+ r8168_mac_ocp_write(tp, 0xF8CE, 0x48C1);
+ r8168_mac_ocp_write(tp, 0xF8D0, 0xC502);
+ r8168_mac_ocp_write(tp, 0xF8D2, 0xBD00);
+ r8168_mac_ocp_write(tp, 0xF8D4, 0x00AA);
+ r8168_mac_ocp_write(tp, 0xF8D6, 0xE0C0);
+ r8168_mac_ocp_write(tp, 0xF8D8, 0xC502);
+ r8168_mac_ocp_write(tp, 0xF8DA, 0xBD00);
+ r8168_mac_ocp_write(tp, 0xF8DC, 0x0132);
+
+ 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);
+
+ rtl_hw_aspm_clkreq_enable(tp, true);
+}
+
+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;
+
+ /* disable aspm and clock request before access ephy */
+ rtl_hw_aspm_clkreq_enable(tp, false);
+ 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);
+
+ rtl8168_config_eee_mac(tp);
+
+ 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, 0x0070);
+ 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);
+
+ rtl_hw_aspm_clkreq_enable(tp, true);
+}
+
+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);
+
+ rtl8168_config_eee_mac(tp);
+
+ 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 },
+ };
+
+ /* disable aspm and clock request before access ephy */
+ rtl_hw_aspm_clkreq_enable(tp, false);
+ 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);
+
+ rtl_hw_aspm_clkreq_enable(tp, true);
+}
+
+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);
+
+ /* disable aspm and clock request before access ephy */
+ rtl_hw_aspm_clkreq_enable(tp, false);
+ 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);
+
+ rtl8168_config_eee_mac(tp);
+
+ 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, 0x0070);
+ 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);
+
+ rtl_hw_aspm_clkreq_enable(tp, true);
+}
+
+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)
+{
+ rtl_hw_aspm_clkreq_enable(tp, false);
+
+ /* 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);
+ rtl_hw_aspm_clkreq_enable(tp, true);
+}
+
+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_W8(tp, 0x4500, 0);
+ RTL_W16(tp, 0x4800, 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_63)
+ r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0200);
+ else
+ r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0400);
+
+ 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, 0x0030);
+ r8168_mac_ocp_modify(tp, 0xe040, 0x1000, 0x0000);
+ r8168_mac_ocp_modify(tp, 0xea1c, 0x0003, 0x0001);
+ 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);
+
+ if (tp->mac_version == RTL_GIGA_MAC_VER_63)
+ rtl8125b_config_eee_mac(tp);
+ else
+ rtl8125a_config_eee_mac(tp);
+
+ 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);
+
+ /* disable aspm and clock request before access ephy */
+ rtl_hw_aspm_clkreq_enable(tp, false);
+ rtl_ephy_init(tp, e_info_8125a_2);
+
+ rtl_hw_start_8125_common(tp);
+ rtl_hw_aspm_clkreq_enable(tp, true);
+}
+
+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_hw_aspm_clkreq_enable(tp, false);
+
+ rtl_ephy_init(tp, e_info_8125b);
+ rtl_hw_start_8125_common(tp);
+
+ rtl_hw_aspm_clkreq_enable(tp, true);
+}
+
+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_11] = rtl_hw_start_8168b,
+ [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_53] = rtl_hw_start_8117,
+ [RTL_GIGA_MAC_VER_61] = rtl_hw_start_8125a_2,
+ [RTL_GIGA_MAC_VER_63] = rtl_hw_start_8125b,
+ };
+
+ if (hw_configs[tp->mac_version])
+ hw_configs[tp->mac_version](tp);
+}
+
+static void rtl_hw_start_8125(struct rtl8169_private *tp)
+{
+ int i;
+
+ /* disable interrupt coalescing */
+ for (i = 0xa00; i < 0xb00; i += 4)
+ RTL_W32(tp, i, 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);
+
+ RTL_W16(tp, CPlusCmd, tp->cp_cmd);
+
+ 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_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);
+
+ dev->mtu = new_mtu;
+ netdev_update_features(dev);
+ rtl_jumbo_config(tp);
+
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_61:
+ case RTL_GIGA_MAC_VER_63:
+ rtl8125_set_eee_txidle_timer(tp);
+ break;
+ default:
+ break;
+ }
+
+ 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_63:
+ 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_RESET_PENDING);
+}
+
+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 (rtl_is_8125(tp) && 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;
+
+ padto = rtl8125_quirk_udp_padto(tp, skb);
+
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_34:
+ case RTL_GIGA_MAC_VER_61:
+ case RTL_GIGA_MAC_VER_63:
+ 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 bool rtl_tx_slots_avail(struct rtl8169_private *tp)
+{
+ unsigned int slots_avail = READ_ONCE(tp->dirty_tx) + NUM_TX_DESC
+ - READ_ONCE(tp->cur_tx);
+
+ /* A skbuff with nr_frags needs nr_frags+1 entries in the tx queue */
+ return slots_avail > MAX_SKB_FRAGS;
+}
+
+/* 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)
+{
+ unsigned int frags = skb_shinfo(skb)->nr_frags;
+ 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;
+ u32 opts[2];
+
+ if (unlikely(!rtl_tx_slots_avail(tp))) {
+ if (net_ratelimit())
+ netdev_err(dev, "BUG! Tx Ring full when queue awake!\n");
+ goto err_stop_0;
+ }
+
+ 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;
+
+ 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 = !rtl_tx_slots_avail(tp);
+ if (unlikely(stop_queue)) {
+ /* Avoid wrongly optimistic queue wake-up: rtl_tx thread must
+ * not miss a ring update when it notices a stopped queue.
+ */
+ smp_wmb();
+ netif_stop_queue(dev);
+ /* Sync with rtl_tx:
+ * - publish queue status and cur_tx ring index (write barrier)
+ * - refresh dirty_tx ring index (read barrier).
+ * May the current thread have a pessimistic view of the ring
+ * status and forget to wake up queue, a racing rtl_tx thread
+ * can't.
+ */
+ smp_mb__after_atomic();
+ if (rtl_tx_slots_avail(tp))
+ netif_start_queue(dev);
+ door_bell = true;
+ }
+
+ if (door_bell)
+ 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;
+
+err_stop_0:
+ netif_stop_queue(dev);
+ dev->stats.tx_dropped++;
+ return NETDEV_TX_BUSY;
+}
+
+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(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) {
+ netdev_completed_queue(dev, pkts_compl, bytes_compl);
+ dev_sw_netstats_tx_add(dev, pkts_compl, bytes_compl);
+
+ /* Sync with rtl8169_start_xmit:
+ * - publish dirty_tx ring index (write barrier)
+ * - refresh cur_tx ring index and queue status (read barrier)
+ * May the current thread miss the stopped queue condition,
+ * a racing xmit thread can only have a right view of the
+ * ring status.
+ */
+ smp_store_mb(tp->dirty_tx, dirty_tx);
+ if (netif_queue_stopped(dev) && rtl_tx_slots_avail(tp))
+ netif_wake_queue(dev);
+ /*
+ * 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 (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(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;
+
+ if (unlikely(status & SYSErr)) {
+ rtl8169_pcierr_interrupt(tp->dev);
+ goto out;
+ }
+
+ if (status & LinkChg)
+ phy_mac_interrupt(tp->phydev);
+
+ if (unlikely(status & RxFIFOOver &&
+ tp->mac_version == RTL_GIGA_MAC_VER_11)) {
+ netif_stop_queue(tp->dev);
+ rtl_schedule_task(tp, RTL_FLAG_TASK_RESET_PENDING);
+ }
+
+ if (napi_schedule_prep(&tp->napi)) {
+ 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);
+
+ rtnl_lock();
+
+ if (!netif_running(tp->dev) ||
+ !test_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags))
+ goto out_unlock;
+
+ if (test_and_clear_bit(RTL_FLAG_TASK_RESET_PENDING, tp->wk.flags)) {
+ rtl_reset_work(tp);
+ netif_wake_queue(tp->dev);
+ }
+out_unlock:
+ rtnl_unlock();
+}
+
+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 r8169_phylink_handler(struct net_device *ndev)
+{
+ struct rtl8169_private *tp = netdev_priv(ndev);
+
+ if (netif_carrier_ok(ndev)) {
+ rtl_link_chg_patch(tp);
+ pm_request_resume(&tp->pci_dev->dev);
+ } else {
+ pm_runtime_idle(&tp->pci_dev->dev);
+ }
+
+ 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)
+{
+ /* Clear all task flags */
+ bitmap_zero(tp->wk.flags, RTL_FLAG_MAX);
+
+ phy_stop(tp->phydev);
+
+ 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);
+}
+
+static void rtl8169_up(struct rtl8169_private *tp)
+{
+ pci_set_master(tp->pci_dev);
+ phy_init_hw(tp->phydev);
+ phy_resume(tp->phydev);
+ rtl8169_init_phy(tp);
+ napi_enable(&tp->napi);
+ set_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags);
+ 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);
+
+ cancel_work_sync(&tp->wk.work);
+
+ 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);
+
+ /* Reportedly at least Asus X453MA truncates packets otherwise */
+ if (tp->mac_version == RTL_GIGA_MAC_VER_37)
+ 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_type != RTL_DASH_NONE)
+ 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_type == RTL_DASH_NONE) {
+ pci_wake_from_d3(pdev, tp->saved_wolopts);
+ pci_set_power_state(pdev, PCI_D3hot);
+ }
+}
+
+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);
+
+ 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 | RxOverflow | RxFIFOOver;
+ else if (tp->mac_version == RTL_GIGA_MAC_VER_11)
+ /* special workaround needed */
+ tp->irq_mask |= RxFIFOOver;
+ else
+ tp->irq_mask |= RxOverflow;
+}
+
+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_LEGACY;
+ 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_register(struct rtl8169_private *tp)
+{
+ struct pci_dev *pdev = tp->pci_dev;
+ struct mii_bus *new_bus;
+ int ret;
+
+ 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;
+ 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;
+
+ 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 = 1;
+
+ phy_support_asym_pause(tp->phydev);
+
+ /* 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_53:
+ 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_63:
+ 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_11:
+ case RTL_GIGA_MAC_VER_17:
+ return JUMBO_4K;
+ /* RTL8168c */
+ case RTL_GIGA_MAC_VER_18 ... RTL_GIGA_MAC_VER_24:
+ return JUMBO_6K;
+ 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_61 &&
+ 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)
+{
+ struct rtl8169_private *tp;
+ int jumbo_max, region, rc;
+ enum mac_version chipset;
+ struct net_device *dev;
+ u16 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->eee_adv = -1;
+ tp->ocp_base = OCP_STD_PHY_BASE;
+
+ dev->tstats = devm_netdev_alloc_pcpu_stats(&pdev->dev,
+ struct pcpu_sw_netstats);
+ if (!dev->tstats)
+ return -ENOMEM;
+
+ /* 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) {
+ dev_err(&pdev->dev, "enable failure\n");
+ return rc;
+ }
+
+ 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) {
+ dev_err(&pdev->dev, "no MMIO resource found\n");
+ return -ENODEV;
+ }
+
+ rc = pcim_iomap_regions(pdev, BIT(region), KBUILD_MODNAME);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "cannot remap MMIO, aborting\n");
+ return rc;
+ }
+
+ tp->mmio_addr = pcim_iomap_table(pdev)[region];
+
+ xid = (RTL_R32(tp, TxConfig) >> 20) & 0xfcf;
+
+ /* Identify chip attached to board */
+ chipset = rtl8169_get_mac_version(xid, tp->supports_gmii);
+ if (chipset == RTL_GIGA_MAC_NONE) {
+ dev_err(&pdev->dev, "unknown chip XID %03x, contact r8169 maintainers (see MAINTAINERS file)\n", xid);
+ return -ENODEV;
+ }
+
+ tp->mac_version = chipset;
+
+ /* Disable ASPM L1 as that cause random device stop working
+ * problems as well as full system hangs for some PCIe devices users.
+ * Chips from RTL8168h partially have issues with L1.2, but seem
+ * to work fine with L1 and L1.1.
+ */
+ if (rtl_aspm_is_safe(tp))
+ rc = 0;
+ else if (tp->mac_version >= RTL_GIGA_MAC_VER_46)
+ rc = pci_disable_link_state(pdev, PCIE_LINK_STATE_L1_2);
+ else
+ rc = pci_disable_link_state(pdev, PCIE_LINK_STATE_L1);
+ tp->aspm_manageable = !rc;
+
+ tp->dash_type = rtl_check_dash(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) {
+ dev_err(&pdev->dev, "Can't allocate interrupt\n");
+ return rc;
+ }
+ tp->irq = pci_irq_vector(pdev, 0);
+
+ INIT_WORK(&tp->wk.work, rtl_task);
+
+ 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;
+
+ /* 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.
+ * Therefore disable both features by default, but allow users to
+ * enable them. Use at own risk!
+ */
+ 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);
+ }
+
+ dev->hw_features |= NETIF_F_RXALL;
+ dev->hw_features |= NETIF_F_RXFCS;
+
+ /* configure chip for default features */
+ rtl8169_set_features(dev, dev->features);
+
+ if (tp->dash_type == RTL_DASH_NONE) {
+ rtl_set_d3_pll_down(tp, true);
+ } else {
+ rtl_set_d3_pll_down(tp, false);
+ dev->wol_enabled = 1;
+ }
+
+ jumbo_max = rtl_jumbo_max(tp);
+ if (jumbo_max)
+ dev->max_mtu = jumbo_max;
+
+ rtl_set_irq_mask(tp);
+
+ tp->fw_name = rtl_chip_infos[chipset].fw_name;
+
+ 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;
+
+ netdev_info(dev, "%s, %pM, XID %03x, IRQ %d\n",
+ rtl_chip_infos[chipset].name, dev->dev_addr, 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 enabled\n");
+ 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/r8169_n.c b/r8169_n.c
deleted file mode 100644
index 5a9e5b0..0000000
--- a/r8169_n.c
+++ /dev/null
@@ -1,4981 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
-################################################################################
-#
-# r8169 is the Linux device driver released for Realtek Gigabit Ethernet
-# Controllers with PCI interface.
-#
-# Copyright(c) 2022 Realtek Semiconductor Corp. All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation; either version 2 of the License, or (at your option)
-# any later version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-# more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, see .
-#
-# Author:
-# Realtek NIC software team
-# No. 2, Innovation Road II, Hsinchu Science Park, Hsinchu 300, Taiwan
-#
-################################################################################
-*/
-
-/************************************************************************************
- * This product is covered by one or more of the following patents:
- * US6,570,884, US6,115,776, and US6,327,625.
- ***********************************************************************************/
-
-/*
- * This driver is modified from r8169.c in Linux kernel 2.6.18
- */
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,37)
-#include
-#endif
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
-#include
-#include
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
-
-#include
-#include
-#include
-
-#include "r8169.h"
-
-/* 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 const int multicast_filter_limit = 32;
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8)
-#ifdef __CHECKER__
-#define __force __attribute__((force))
-#else
-#define __force
-#endif
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8)
-
-#define _R(NAME,MAC,MASK) \
- { .name = NAME, .mcfg = MAC, .RxConfigMask = MASK }
-
-static const struct {
- const char *name;
- u8 mcfg;
- u32 RxConfigMask; /* Clears the bits supported by this chip */
-} rtl_chip_info[] = {
- _R("RTL8169", CFG_METHOD_1, 0xff7e1880),
- _R("RTL8169S/8110S", CFG_METHOD_2, 0xff7e1880),
- _R("RTL8169S/8110S", CFG_METHOD_3, 0xff7e1880),
- _R("RTL8169SB/8110SB", CFG_METHOD_4, 0xff7e1880),
- _R("RTL8169SC/8110SC", CFG_METHOD_5, 0xff7e1880),
- _R("RTL8169SC/8110SC", CFG_METHOD_6, 0xff7e1880),
-};
-#undef _R
-
-enum cfg_version {
- RTL_CFG_0 = 0x00,
- RTL_CFG_1,
- RTL_CFG_2
-};
-
-static const struct {
- unsigned int region;
- unsigned int align;
-} rtl_cfg_info[] = {
- [RTL_CFG_0] = { 1, 8 },
- [RTL_CFG_1] = { 2, 8 },
- [RTL_CFG_2] = { 2, 8 }
-};
-
-static struct pci_device_id rtl8169_pci_tbl[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8167), 0, 0, RTL_CFG_0 },
- { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8169), 0, 0, RTL_CFG_0 },
- { PCI_VENDOR_ID_DLINK, 0x4300, PCI_VENDOR_ID_DLINK, 0x4300, 0, 0, RTL_CFG_0 },
- { PCI_VENDOR_ID_DLINK, 0x4302, PCI_VENDOR_ID_DLINK, 0x4302, 0, 0, RTL_CFG_0 },
- { PCI_VENDOR_ID_DLINK, 0x4300, PCI_VENDOR_ID_DLINK, 0x4c00, 0, 0, RTL_CFG_0 },
- {0,},
-};
-
-MODULE_DEVICE_TABLE(pci, rtl8169_pci_tbl);
-
-static int rx_copybreak = 0;
-static int use_dac;
-#ifdef ENABLE_S5_KEEP_CURR_MAC
-static int s5_keep_curr_mac = 1;
-#else
-static int s5_keep_curr_mac = 0;
-#endif
-static struct {
- u32 msg_enable;
-} debug = { -1 };
-
-static unsigned int speed_mode = SPEED_1000;
-static unsigned int duplex_mode = DUPLEX_FULL;
-static unsigned int autoneg_mode = AUTONEG_ENABLE;
-static unsigned int advertising_mode = ADVERTISED_10baseT_Half |
- ADVERTISED_10baseT_Full |
- ADVERTISED_100baseT_Half |
- ADVERTISED_100baseT_Full |
- ADVERTISED_1000baseT_Half |
- ADVERTISED_1000baseT_Full;
-
-module_param(speed_mode, uint, 0);
-MODULE_PARM_DESC(speed_mode, "force phy operation. Deprecated by ethtool (8).");
-
-module_param(duplex_mode, uint, 0);
-MODULE_PARM_DESC(duplex_mode, "force phy operation. Deprecated by ethtool (8).");
-
-module_param(autoneg_mode, uint, 0);
-MODULE_PARM_DESC(autoneg_mode, "force phy operation. Deprecated by ethtool (8).");
-
-module_param(advertising_mode, uint, 0);
-MODULE_PARM_DESC(advertising_mode, "force phy operation. Deprecated by ethtool (8).");
-
-module_param(rx_copybreak, int, 0);
-MODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy-only-tiny-frames");
-
-module_param(use_dac, int, 0);
-MODULE_PARM_DESC(use_dac, "Enable PCI DAC. Unsafe on 32 bit PCI slot.");
-
-module_param(s5_keep_curr_mac, int, 0);
-MODULE_PARM_DESC(s5_keep_curr_mac, "Enable Shutdown Keep Current MAC Address.");
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
-module_param_named(debug, debug.msg_enable, int, 0);
-MODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., 16=all)");
-#endif//LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
-
-MODULE_LICENSE("GPL");
-
-#ifndef MODULE_VERSION
-#define MODULE_VERSION(_version) MODULE_INFO(version, _version)
-#endif
-MODULE_VERSION(RTL8169_VERSION);
-
-static void rtl8169_set_tx_config(struct net_device *dev);
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
-static void rtl8169_esd_timer(unsigned long __opaque);
-#else
-static void rtl8169_esd_timer(struct timer_list *t);
-#endif
-static void rtl8169_tx_clear(struct rtl8169_private *tp);
-static void rtl8169_rx_clear(struct rtl8169_private *tp);
-static void rtl8169_init_ring_indexes(struct rtl8169_private *tp);
-
-static int rtl8169_open(struct net_device *dev);
-static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, struct net_device *dev);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
-static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
-#else
-static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance);
-#endif
-static int rtl8169_init_ring(struct net_device *dev);
-static void rtl8169_hw_start(struct net_device *dev);
-static int rtl8169_close(struct net_device *dev);
-static void rtl8169_set_rx_mode(struct net_device *dev);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
-static void rtl8169_tx_timeout(struct net_device *dev, unsigned int txqueue);
-#else
-static void rtl8169_tx_timeout(struct net_device *dev);
-#endif
-static struct net_device_stats *rtl8169_get_stats(struct net_device *dev);
-static int rtl8169_rx_interrupt(struct net_device *, struct rtl8169_private *, void __iomem *, napi_budget);
-static int rtl8169_change_mtu(struct net_device *dev, int new_mtu);
-static void rtl8169_down(struct net_device *dev);
-static int rtl8169_set_mac_address(struct net_device *dev, void *p);
-void rtl8169_rar_set(struct rtl8169_private *tp, const u8 *addr);
-
-static void rtl8169_phy_power_up(struct net_device *dev);
-static void rtl8169_phy_power_down(struct net_device *dev);
-
-static void rtl8169_hw_reset(struct net_device *dev);
-
-#ifdef CONFIG_R8169_NAPI
-static int rtl8169_poll(napi_ptr napi, napi_budget budget);
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
-static void rtl8169_reset_task(void *_data);
-#else
-static void rtl8169_reset_task(struct work_struct *work);
-#endif
-
-static const u16 rtl8169_intr_mask =
- SYSErr | LinkChg | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK;
-static const u16 rtl8169_napi_event =
- RxOK | RxOverflow | RxFIFOOver | TxOK | TxErr;
-static const unsigned int rtl8169_rx_config =
- (Reserved2_data << Reserved2_shift) | (RX_DMA_BURST << RxCfgDMAShift);
-
-#if (( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,27) ) || \
- (( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ) && \
- ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,3) )))
-/* copied from linux kernel 2.6.20 include/linux/netdev.h */
-#define NETDEV_ALIGN 32
-#define NETDEV_ALIGN_CONST (NETDEV_ALIGN - 1)
-
-static inline void *netdev_priv(struct net_device *dev)
-{
- return (char *)dev + ((sizeof(struct net_device)
- + NETDEV_ALIGN_CONST)
- & ~NETDEV_ALIGN_CONST);
-}
-#endif
-
-#if ((LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) && \
- LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,00)))
-void ethtool_convert_legacy_u32_to_link_mode(unsigned long *dst,
- u32 legacy_u32)
-{
- bitmap_zero(dst, __ETHTOOL_LINK_MODE_MASK_NBITS);
- dst[0] = legacy_u32;
-}
-
-bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32,
- const unsigned long *src)
-{
- bool retval = true;
-
- /* TODO: following test will soon always be true */
- if (__ETHTOOL_LINK_MODE_MASK_NBITS > 32) {
- __ETHTOOL_DECLARE_LINK_MODE_MASK(ext);
-
- bitmap_zero(ext, __ETHTOOL_LINK_MODE_MASK_NBITS);
- bitmap_fill(ext, 32);
- bitmap_complement(ext, ext, __ETHTOOL_LINK_MODE_MASK_NBITS);
- if (bitmap_intersects(ext, src,
- __ETHTOOL_LINK_MODE_MASK_NBITS)) {
- /* src mask goes beyond bit 31 */
- retval = false;
- }
- }
- *legacy_u32 = src[0];
- return retval;
-}
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
-
-#ifndef LPA_1000FULL
-#define LPA_1000FULL 0x0800
-#endif
-
-#ifndef LPA_1000HALF
-#define LPA_1000HALF 0x0400
-#endif
-
-static inline u32 mii_adv_to_ethtool_adv_t(u32 adv)
-{
- u32 result = 0;
-
- if (adv & ADVERTISE_10HALF)
- result |= ADVERTISED_10baseT_Half;
- if (adv & ADVERTISE_10FULL)
- result |= ADVERTISED_10baseT_Full;
- if (adv & ADVERTISE_100HALF)
- result |= ADVERTISED_100baseT_Half;
- if (adv & ADVERTISE_100FULL)
- result |= ADVERTISED_100baseT_Full;
- if (adv & ADVERTISE_PAUSE_CAP)
- result |= ADVERTISED_Pause;
- if (adv & ADVERTISE_PAUSE_ASYM)
- result |= ADVERTISED_Asym_Pause;
-
- return result;
-}
-
-static inline u32 mii_lpa_to_ethtool_lpa_t(u32 lpa)
-{
- u32 result = 0;
-
- if (lpa & LPA_LPACK)
- result |= ADVERTISED_Autoneg;
-
- return result | mii_adv_to_ethtool_adv_t(lpa);
-}
-
-static inline u32 mii_stat1000_to_ethtool_lpa_t(u32 lpa)
-{
- u32 result = 0;
-
- if (lpa & LPA_1000HALF)
- result |= ADVERTISED_1000baseT_Half;
- if (lpa & LPA_1000FULL)
- result |= ADVERTISED_1000baseT_Full;
-
- return result;
-}
-
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0)
-static inline void eth_hw_addr_random(struct net_device *dev)
-{
- random_ether_addr(dev->dev_addr);
-}
-#endif
-
-//#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5)
-#ifndef netif_msg_init
-#define netif_msg_init _kc_netif_msg_init
-/* copied from linux kernel 2.6.20 include/linux/netdevice.h */
-static inline u32 _kc_netif_msg_init(int debug_value, int default_msg_enable_bits)
-{
- /* use default */
- if (debug_value < 0 || debug_value >= (sizeof(u32) * 8))
- return default_msg_enable_bits;
- if (debug_value == 0) /* no output */
- return 0;
- /* set low N bits */
- return (1 << debug_value) - 1;
-}
-
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5)
-
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,7)
-/* copied from linux kernel 2.6.20 /include/linux/time.h */
-/* Parameters used to convert the timespec values: */
-#define MSEC_PER_SEC 1000L
-
-/* copied from linux kernel 2.6.20 /include/linux/jiffies.h */
-/*
- * Change timeval to jiffies, trying to avoid the
- * most obvious overflows..
- *
- * And some not so obvious.
- *
- * Note that we don't want to return MAX_LONG, because
- * for various timeout reasons we often end up having
- * to wait "jiffies+1" in order to guarantee that we wait
- * at _least_ "jiffies" - so "jiffies+1" had better still
- * be positive.
- */
-#define MAX_JIFFY_OFFSET ((~0UL >> 1)-1)
-
-/*
- * Convert jiffies to milliseconds and back.
- *
- * Avoid unnecessary multiplications/divisions in the
- * two most common HZ cases:
- */
-static inline unsigned int _kc_jiffies_to_msecs(const unsigned long j)
-{
-#if HZ <= MSEC_PER_SEC && !(MSEC_PER_SEC % HZ)
- return (MSEC_PER_SEC / HZ) * j;
-#elif HZ > MSEC_PER_SEC && !(HZ % MSEC_PER_SEC)
- return (j + (HZ / MSEC_PER_SEC) - 1)/(HZ / MSEC_PER_SEC);
-#else
- return (j * MSEC_PER_SEC) / HZ;
-#endif
-}
-
-static inline unsigned long _kc_msecs_to_jiffies(const unsigned int m)
-{
- if (m > _kc_jiffies_to_msecs(MAX_JIFFY_OFFSET))
- return MAX_JIFFY_OFFSET;
-#if HZ <= MSEC_PER_SEC && !(MSEC_PER_SEC % HZ)
- return (m + (MSEC_PER_SEC / HZ) - 1) / (MSEC_PER_SEC / HZ);
-#elif HZ > MSEC_PER_SEC && !(HZ % MSEC_PER_SEC)
- return m * (HZ / MSEC_PER_SEC);
-#else
- return (m * HZ + MSEC_PER_SEC - 1) / MSEC_PER_SEC;
-#endif
-}
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,7)
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
-//for linux kernel 2.6.10 and earlier.
-
-/* copied from linux kernel 2.6.12.6 /include/linux/pm.h */
-typedef int __bitwise pci_power_t;
-
-/* copied from linux kernel 2.6.12.6 /include/linux/pci.h */
-typedef u32 __bitwise pm_message_t;
-
-#define PCI_D0 ((pci_power_t __force) 0)
-#define PCI_D1 ((pci_power_t __force) 1)
-#define PCI_D2 ((pci_power_t __force) 2)
-#define PCI_D3hot ((pci_power_t __force) 3)
-#define PCI_D3cold ((pci_power_t __force) 4)
-#define PCI_POWER_ERROR ((pci_power_t __force) -1)
-
-/* copied from linux kernel 2.6.12.6 /drivers/pci/pci.c */
-/**
- * pci_choose_state - Choose the power state of a PCI device
- * @dev: PCI device to be suspended
- * @state: target sleep state for the whole system. This is the value
- * that is passed to suspend() function.
- *
- * Returns PCI power state suitable for given device and given system
- * message.
- */
-
-pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
-{
- if (!pci_find_capability(dev, PCI_CAP_ID_PM))
- return PCI_D0;
-
- switch (state) {
- case 0:
- return PCI_D0;
- case 3:
- return PCI_D3hot;
- default:
- printk("They asked me for state %d\n", state);
-// BUG();
- }
- return PCI_D0;
-}
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9)
-//porting on 2.6.8.1 and earlier
-/**
- * msleep_interruptible - sleep waiting for waitqueue interruptions
- * @msecs: Time in milliseconds to sleep for
- */
-#define msleep_interruptible _kc_msleep_interruptible
-unsigned long _kc_msleep_interruptible(unsigned int msecs)
-{
- unsigned long timeout = _kc_msecs_to_jiffies(msecs);
-
- while (timeout && !signal_pending(current)) {
- set_current_state(TASK_INTERRUPTIBLE);
- timeout = schedule_timeout(timeout);
- }
- return _kc_jiffies_to_msecs(timeout);
-}
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9)
-
-/*****************************************************************************/
-#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8) )
-#define msleep(x) do { set_current_state(TASK_UNINTERRUPTIBLE); \
- schedule_timeout((x * HZ)/1000 + 2); \
- } while (0)
-#endif
-/*****************************************************************************/
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,7)
-/* copied from linux kernel 2.6.20 include/linux/sched.h */
-#ifndef __sched
-#define __sched __attribute__((__section__(".sched.text")))
-#endif
-
-/* copied from linux kernel 2.6.20 kernel/timer.c */
-signed long __sched schedule_timeout_uninterruptible(signed long timeout)
-{
- __set_current_state(TASK_UNINTERRUPTIBLE);
- return schedule_timeout(timeout);
-}
-
-/* copied from linux kernel 2.6.20 include/linux/mii.h */
-#undef if_mii
-#define if_mii _kc_if_mii
-static inline struct mii_ioctl_data *if_mii(struct ifreq *rq)
-{
- return (struct mii_ioctl_data *) &rq->ifr_ifru;
-}
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,7)
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,22)
-static inline void eth_copy_and_sum (struct sk_buff *dest,
- const unsigned char *src,
- int len, int base)
-{
- memcpy (dest->data, src, len);
-}
-#endif //LINUX_VERSION_CODE > KERNEL_VERSION(2,6,22)
-
-static void mdio_write(struct rtl8169_private *tp, int RegAddr, int value)
-{
- void __iomem *ioaddr = tp->mmio_addr;
- int i;
-
- RTL_W32(PHYAR,
- PHYAR_Write |
- (RegAddr & PHYAR_Reg_Mask) << PHYAR_Reg_shift |
- (value & PHYAR_Data_Mask));
-
- for (i = 0; i < R8169_CHANNEL_WAIT_COUNT; i++) {
- /* Check if the RTL8169 has completed writing to the specified MII register */
- if (!(RTL_R32(PHYAR) & PHYAR_Flag))
- break;
- udelay(R8169_CHANNEL_WAIT_TIME);
- }
-
- udelay(R8169_CHANNEL_EXIT_DELAY_TIME);
-}
-
-static int mdio_read(struct rtl8169_private *tp, int RegAddr)
-{
- void __iomem *ioaddr = tp->mmio_addr;
- int i, value = -1;
-
- RTL_W32(PHYAR, PHYAR_Read | (RegAddr & PHYAR_Reg_Mask) << PHYAR_Reg_shift);
-
- for (i = 0; i < R8169_CHANNEL_WAIT_COUNT; i++) {
- /* Check if the RTL8169 has completed retrieving data from the specified MII register */
- if (RTL_R32(PHYAR) & PHYAR_Flag) {
- udelay(1);
- value = (int) (RTL_R32(PHYAR) & PHYAR_Data_Mask);
- break;
- }
- udelay(R8169_CHANNEL_WAIT_TIME);
-
- }
-
- udelay(R8169_CHANNEL_EXIT_DELAY_TIME);
-
- return value;
-}
-
-static void rtl8169_irq_mask_and_ack(void __iomem *ioaddr)
-{
- RTL_W16(IntrMask, 0x0000);
-
- RTL_W16(IntrStatus, 0xffff);
-}
-
-static unsigned int rtl8169_xmii_reset_pending(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- unsigned int retval;
-
- mdio_write(tp, 0x1f, 0x0000);
- retval = mdio_read(tp, MII_BMCR) & BMCR_RESET;
-
- return retval;
-}
-
-static unsigned int rtl8169_xmii_link_ok(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- unsigned int retval;
-
- mdio_write(tp, 0x1f, 0x0000);
- retval = mdio_read(tp, MII_BMSR) & BMSR_LSTATUS;
-
- return retval;
-}
-
-static void rtl8169_xmii_reset_enable(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- int i, val = 0;
-
- mdio_write(tp, 0x1f, 0x0000);
- mdio_write(tp, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
-
- for (i = 0; i < 2500; i++) {
- val = mdio_read(tp, MII_BMCR) & BMCR_RESET;
-
- if (!val) {
- return;
- }
-
- mdelay(1);
- }
-
- if (netif_msg_link(tp))
- printk(KERN_ERR "%s: PHY reset failed.\n", dev->name);
-}
-
-static void rtl8169_check_link_status(struct net_device *dev, struct rtl8169_private *tp, void __iomem *ioaddr)
-{
- u8 status;
-
- if (tp->link_ok(dev)) {
- if (tp->mcfg == CFG_METHOD_4) {
- status = RTL_R8(PHYstatus);
-
- if ((status & _10bps) && (RTL_R8(Config2) & PCI_Clock_66MHz)) {
- RTL_W32(TxConfig, RTL_R32(TxConfig) & ~(TX_DMA_BURST << TxDMAShift));
- }
- } else {
- RTL_W32(TxConfig, RTL_R32(TxConfig) | (TX_DMA_BURST << TxDMAShift));
- }
-
- if (netif_msg_ifup(tp))
- printk(KERN_INFO PFX "%s: link up\n", dev->name);
-
- rtl8169_set_tx_config(dev);
-
- netif_carrier_on(dev);
-
- netif_wake_queue(dev);
- } else {
- if (netif_msg_ifdown(tp))
- printk(KERN_INFO PFX "%s: link down\n", dev->name);
-
- netif_stop_queue(dev);
-
- netif_carrier_off(dev);
- }
-}
-
-static void
-rtl8169_link_option(u8 *aut,
- u32 *spd,
- u8 *dup,
- u32 *adv)
-{
- if ((*spd != SPEED_1000) && (*spd != SPEED_100) && (*spd != SPEED_10))
- *spd = SPEED_1000;
-
- if ((*dup != DUPLEX_FULL) && (*dup != DUPLEX_HALF))
- *dup = DUPLEX_FULL;
-
- if ((*aut != AUTONEG_ENABLE) && (*aut != AUTONEG_DISABLE))
- *aut = AUTONEG_ENABLE;
-
- *adv &= (ADVERTISED_10baseT_Half |
- ADVERTISED_10baseT_Full |
- ADVERTISED_100baseT_Half |
- ADVERTISED_100baseT_Full |
- ADVERTISED_1000baseT_Half |
- ADVERTISED_1000baseT_Full);
- if (*adv == 0)
- *adv = (ADVERTISED_10baseT_Half |
- ADVERTISED_10baseT_Full |
- ADVERTISED_100baseT_Half |
- ADVERTISED_100baseT_Full |
- ADVERTISED_1000baseT_Half |
- ADVERTISED_1000baseT_Full);
-}
-
-static void
-rtl8169_phy_setup_force_mode(struct net_device *dev, u32 speed, u8 duplex)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- u16 bmcr_true_force = 0;
-
- if ((speed == SPEED_10) && (duplex == DUPLEX_HALF)) {
- bmcr_true_force = BMCR_SPEED10;
- } else if ((speed == SPEED_10) && (duplex == DUPLEX_FULL)) {
- bmcr_true_force = BMCR_SPEED10 | BMCR_FULLDPLX;
- } else if ((speed == SPEED_100) && (duplex == DUPLEX_HALF)) {
- bmcr_true_force = BMCR_SPEED100;
- } else if ((speed == SPEED_100) && (duplex == DUPLEX_FULL)) {
- bmcr_true_force = BMCR_SPEED100 | BMCR_FULLDPLX;
- } else {
- netif_err(tp, drv, dev, "Failed to set phy force mode!\n");
- return;
- }
-
- mdio_write(tp, 0x1F, 0x0000);
- mdio_write(tp, MII_BMCR, bmcr_true_force);
-}
-
-static void
-rtl8169_powerdown_pll(struct rtl8169_private *tp)
-{
- struct net_device *dev = pci_get_drvdata(tp->pci_dev);
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W16(RxMaxSize, RX_BUF_SIZE);
-
- if (tp->wol_enabled == WOL_ENABLED) {
- int auto_nego;
- int giga_ctrl;
- u16 val;
-
- mdio_write(tp, 0x1F, 0x0000);
- auto_nego = mdio_read(tp, MII_ADVERTISE);
- auto_nego &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL
- | ADVERTISE_100HALF | ADVERTISE_100FULL);
-
- val = mdio_read(tp, MII_LPA);
-
- if (val & (LPA_10HALF | LPA_10FULL))
- auto_nego |= (ADVERTISE_10HALF | ADVERTISE_10FULL);
- else
- auto_nego |= (ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10HALF | ADVERTISE_10FULL);
-
- giga_ctrl = mdio_read(tp, MII_CTRL1000) & ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
- mdio_write(tp, MII_ADVERTISE, auto_nego);
- mdio_write(tp, MII_CTRL1000, giga_ctrl);
- mdio_write(tp, MII_BMCR, BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART);
-
- return;
- }
-
- rtl8169_phy_power_down(dev);
-}
-
-static void rtl8169_powerup_pll(struct net_device *dev)
-{
- rtl8169_phy_power_up(dev);
-}
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22)
-static void rtl8169_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- u8 options;
-
- wol->wolopts = 0;
-
-#define WAKE_ANY (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST)
- wol->supported = WAKE_ANY;
-
- spin_lock_irq(&tp->lock);
-
- options = RTL_R8(Config1);
- if (!(options & PMEnable))
- goto out_unlock;
-
- options = RTL_R8(Config3);
- if (options & LinkUp)
- wol->wolopts |= WAKE_PHY;
- if (options & MagicPacket)
- wol->wolopts |= WAKE_MAGIC;
-
- options = RTL_R8(Config5);
- if (options & UWF)
- wol->wolopts |= WAKE_UCAST;
- if (options & BWF)
- wol->wolopts |= WAKE_BCAST;
- if (options & MWF)
- wol->wolopts |= WAKE_MCAST;
-
-out_unlock:
- spin_unlock_irq(&tp->lock);
-}
-
-static int rtl8169_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- int i;
- static struct {
- u32 opt;
- u16 reg;
- u8 mask;
- } cfg[] = {
- { WAKE_ANY, Config1, PMEnable },
- { WAKE_PHY, Config3, LinkUp },
- { WAKE_MAGIC, Config3, MagicPacket },
- { WAKE_UCAST, Config5, UWF },
- { WAKE_BCAST, Config5, BWF },
- { WAKE_MCAST, Config5, MWF },
- { WAKE_ANY, Config5, LanWake }
- };
-
- spin_lock_irq(&tp->lock);
-
- RTL_W8(Cfg9346, Cfg9346_Unlock);
-
- for (i = 0; i < ARRAY_SIZE(cfg); i++) {
- u8 options = RTL_R8(cfg[i].reg) & ~cfg[i].mask;
- if (wol->wolopts & cfg[i].opt)
- options |= cfg[i].mask;
- RTL_W8(cfg[i].reg, options);
- }
-
- RTL_W8(Cfg9346, Cfg9346_Lock);
-
- tp->wol_enabled = (wol->wolopts) ? WOL_ENABLED : WOL_DISABLED;
-
- spin_unlock_irq(&tp->lock);
-
- device_set_wakeup_enable(&tp->pci_dev->dev, tp->wol_enabled);
-
- return 0;
-}
-
-static void rtl8169_get_drvinfo(struct net_device *dev,
- struct ethtool_drvinfo *info)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
-
- strcpy(info->driver, MODULENAME);
- strcpy(info->version, RTL8169_VERSION);
- strcpy(info->bus_info, pci_name(tp->pci_dev));
- info->regdump_len = R8169_REGS_DUMP_SIZE;
-}
-
-static int rtl8169_get_regs_len(struct net_device *dev)
-{
- return R8169_REGS_DUMP_SIZE;
-}
-
-#endif //LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22)
-
-static void rtl8169_set_tx_config(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- u32 tx_config;
- u8 duplex;
-
- duplex = (RTL_R8(PHYstatus) & FullDup) ? DUPLEX_FULL : DUPLEX_HALF;
-
- tx_config = RTL_R32(TxConfig) | IFG0 | IFG1;
-
- if (((tp->mcfg == CFG_METHOD_5) | (tp->mcfg == CFG_METHOD_6)) && duplex == DUPLEX_HALF)
- tx_config &= ~IFG0;
-
- RTL_W32(TxConfig, tx_config);
-}
-
-static int rtl8169_set_speed_xmii(struct net_device *dev,
- u8 autoneg,
- u32 speed,
- u8 duplex,
- u32 adv)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- int auto_nego = 0;
- int giga_ctrl = 0;
- int rc = -EINVAL;
-
- if ((speed != SPEED_1000) &&
- (speed != SPEED_100) &&
- (speed != SPEED_10)) {
- speed = SPEED_1000;
- duplex = DUPLEX_FULL;
- }
-
- if (autoneg == AUTONEG_ENABLE) {
- /*n-way force*/
- auto_nego = mdio_read(tp, MII_ADVERTISE);
- auto_nego &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL |
- ADVERTISE_100HALF | ADVERTISE_100FULL |
- ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
-
- if (adv & ADVERTISED_10baseT_Half)
- auto_nego |= ADVERTISE_10HALF;
- if (adv & ADVERTISED_10baseT_Full)
- auto_nego |= ADVERTISE_10FULL;
- if (adv & ADVERTISED_100baseT_Half)
- auto_nego |= ADVERTISE_100HALF;
- if (adv & ADVERTISED_100baseT_Full)
- auto_nego |= ADVERTISE_100FULL;
- if (adv & ADVERTISED_1000baseT_Half)
- giga_ctrl |= ADVERTISE_1000HALF;
- if (adv & ADVERTISED_1000baseT_Full)
- giga_ctrl |= ADVERTISE_1000FULL;
-
- //flow control
- if (tp->fcpause == rtl8169_fc_full)
- auto_nego |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
-
- tp->phy_auto_nego_reg = auto_nego;
- tp->phy_1000_ctrl_reg = giga_ctrl;
-
- tp->autoneg = autoneg;
- tp->speed = speed;
- tp->duplex = duplex;
-
- mdio_write(tp, 0x1f, 0x0000);
- mdio_write(tp, MII_ADVERTISE, auto_nego);
- mdio_write(tp, MII_CTRL1000, giga_ctrl);
- mdio_write(tp, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
- } else {
- /*true force*/
- if (speed == SPEED_10 || speed == SPEED_100) {
- rtl8169_phy_setup_force_mode(dev, speed, duplex);
- } else
- goto out;
- }
-
- if ((tp->mcfg == CFG_METHOD_2) || (tp->mcfg == CFG_METHOD_3)) {
- if ((speed == SPEED_100) && (autoneg != AUTONEG_ENABLE)) {
- mdio_write(tp, 0x17, 0x2138);
- mdio_write(tp, 0x0e, 0x0260);
- } else {
- mdio_write(tp, 0x17, 0x2108);
- mdio_write(tp, 0x0e, 0x0000);
- }
- }
-
- tp->autoneg = autoneg;
- tp->speed = speed;
- tp->duplex = duplex;
- tp->advertising = adv;
-
- rc = 0;
-out:
- return rc;
-}
-
-static int rtl8169_set_speed(struct net_device *dev,
- u8 autoneg,
- u32 speed,
- u8 duplex,
- u32 adv)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- int ret;
-
- ret = tp->set_speed(dev, autoneg, speed, duplex, adv);
-
- return ret;
-}
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22)
-static int rtl8169_set_settings(struct net_device *dev,
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,6,0)
- struct ethtool_cmd *cmd
-#else
- const struct ethtool_link_ksettings *cmd
-#endif
- )
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- int ret;
- unsigned long flags;
- u8 autoneg;
- u32 speed;
- u8 duplex;
- u32 supported, advertising;
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,6,0)
- autoneg = cmd->autoneg;
- speed = cmd->speed;
- duplex = cmd->duplex;
- supported = cmd->supported;
- advertising = cmd->advertising;
-#else
- const struct ethtool_link_settings *base = &cmd->base;
- autoneg = base->autoneg;
- speed = base->speed;
- duplex = base->duplex;
- ethtool_convert_link_mode_to_legacy_u32(&supported,
- cmd->link_modes.supported);
- ethtool_convert_link_mode_to_legacy_u32(&advertising,
- cmd->link_modes.advertising);
-#endif
- if (advertising & ~supported)
- return -EINVAL;
-
- spin_lock_irqsave(&tp->lock, flags);
- ret = rtl8169_set_speed(dev, autoneg, speed, duplex, advertising);
- spin_unlock_irqrestore(&tp->lock, flags);
-
- return ret;
-}
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
-static u32
-rtl8169_get_tx_csum(struct net_device *dev)
-{
- return (dev->features & NETIF_F_IP_CSUM) != 0;
-}
-
-static u32 rtl8169_get_rx_csum(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
-
- return tp->cp_cmd & RxChkSum;
-}
-
-static int
-rtl8169_set_tx_csum(struct net_device *dev,
- u32 data)
-{
- if (data)
- dev->features |= NETIF_F_IP_CSUM;
- else
- dev->features &= ~NETIF_F_IP_CSUM;
-
- return 0;
-}
-
-static int rtl8169_set_rx_csum(struct net_device *dev, u32 data)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- unsigned long flags;
-
- spin_lock_irqsave(&tp->lock, flags);
-
- if (data)
- tp->cp_cmd |= RxChkSum;
- else
- tp->cp_cmd &= ~RxChkSum;
-
- RTL_W16(CPlusCmd, tp->cp_cmd);
- RTL_R16(CPlusCmd);
-
- spin_unlock_irqrestore(&tp->lock, flags);
-
- return 0;
-}
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
-#endif //LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22)
-
-#ifdef CONFIG_R8169_VLAN
-
-static inline u32 rtl8169_tx_vlan_tag(struct rtl8169_private *tp,
- struct sk_buff *skb)
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
- return (tp->vlgrp && vlan_tx_tag_present(skb)) ?
- TxVlanTag | swab16(vlan_tx_tag_get(skb)) : 0x00;
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0)
- return (vlan_tx_tag_present(skb)) ?
- TxVlanTag | swab16(vlan_tx_tag_get(skb)) : 0x00;
-#else
- return (skb_vlan_tag_present(skb)) ?
- TxVlanTag | swab16(skb_vlan_tag_get(skb)) : 0x00;
-#endif
-}
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
-
-static void rtl8169_vlan_rx_register(struct net_device *dev,
- struct vlan_group *grp)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- unsigned long flags;
-
- spin_lock_irqsave(&tp->lock, flags);
- tp->vlgrp = grp;
- if (tp->vlgrp)
- tp->cp_cmd |= RxVlan;
- else
- tp->cp_cmd &= ~RxVlan;
- RTL_W16(CPlusCmd, tp->cp_cmd);
- RTL_R16(CPlusCmd);
- spin_unlock_irqrestore(&tp->lock, flags);
-}
-
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
-static void rtl8169_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- unsigned long flags;
-
- spin_lock_irqsave(&tp->lock, flags);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
- if (tp->vlgrp)
- tp->vlgrp->vlan_devices[vid] = NULL;
-#else
- vlan_group_set_device(tp->vlgrp, vid, NULL);
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)
- spin_unlock_irqrestore(&tp->lock, flags);
-}
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
-
-static int rtl8169_rx_vlan_skb(struct rtl8169_private *tp,
- struct RxDesc *desc,
- struct sk_buff *skb)
-{
- u32 opts2 = le32_to_cpu(desc->opts2);
- int ret = -1;
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
- if (tp->vlgrp && (opts2 & RxVlanTag)) {
- rtl8169_rx_hwaccel_skb(skb, tp->vlgrp,
- swab16(opts2 & 0xffff));
- ret = 0;
- }
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
- if (opts2 & RxVlanTag)
- __vlan_hwaccel_put_tag(skb, swab16(opts2 & 0xffff));
-#else
- if (opts2 & RxVlanTag)
- __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), swab16(opts2 & 0xffff));
-#endif
-
- desc->opts2 = 0;
- return ret;
-}
-
-#else /* !CONFIG_R8169_VLAN */
-
-static inline u32 rtl8169_tx_vlan_tag(struct rtl8169_private *tp,
- struct sk_buff *skb)
-{
- return 0;
-}
-
-static int rtl8169_rx_vlan_skb(struct rtl8169_private *tp,
- struct RxDesc *desc,
- struct sk_buff *skb)
-{
- return -1;
-}
-
-#endif
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
-
-static netdev_features_t rtl8169_fix_features(struct net_device *dev,
- netdev_features_t features)
-{
- if (dev->mtu > MSSMask)
- features &= ~NETIF_F_ALL_TSO;
-
- return features;
-}
-
-static int rtl8169_hw_set_features(struct net_device *dev,
- netdev_features_t features)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
-
- if (features & NETIF_F_RXCSUM)
- tp->cp_cmd |= RxChkSum;
- else
- tp->cp_cmd &= ~RxChkSum;
-
- if (dev->features & NETIF_F_HW_VLAN_RX)
- tp->cp_cmd |= RxVlan;
- else
- tp->cp_cmd &= ~RxVlan;
-
- RTL_W16(CPlusCmd, tp->cp_cmd);
- RTL_R16(CPlusCmd);
-
- return 0;
-}
-
-static int rtl8169_set_features(struct net_device *dev,
- netdev_features_t features)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- unsigned long flags;
-
- features &= NETIF_F_RXALL | NETIF_F_RXCSUM | NETIF_F_HW_VLAN_RX;
-
- spin_lock_irqsave(&tp->lock, flags);
- if (features ^ dev->features)
- rtl8169_hw_set_features(dev, features);
- spin_unlock_irqrestore(&tp->lock, flags);
-
- return 0;
-}
-
-#endif //LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
-
-static void rtl8169_gset_xmii(struct net_device *dev,
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,6,0)
- struct ethtool_cmd *cmd
-#else
- struct ethtool_link_ksettings *cmd
-#endif
- )
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- u8 status;
- u8 autoneg, duplex;
- u32 speed = 0;
- u16 bmcr, bmsr, anlpar, ctrl1000 = 0, stat1000 = 0;
- u32 supported, advertising, lp_advertising;
- unsigned long flags;
-
- supported = SUPPORTED_10baseT_Half |
- SUPPORTED_10baseT_Full |
- SUPPORTED_100baseT_Half |
- SUPPORTED_100baseT_Full |
- SUPPORTED_1000baseT_Full |
- SUPPORTED_Autoneg |
- SUPPORTED_TP |
- SUPPORTED_Pause |
- SUPPORTED_Asym_Pause;
-
- advertising = ADVERTISED_TP;
-
- spin_lock_irqsave(&tp->lock, flags);
- mdio_write(tp, 0x1F, 0x0000);
- bmcr = mdio_read(tp, MII_BMCR);
- bmsr = mdio_read(tp, MII_BMSR);
- anlpar = mdio_read(tp, MII_LPA);
- ctrl1000 = mdio_read(tp, MII_CTRL1000);
- stat1000 = mdio_read(tp, MII_STAT1000);
- spin_unlock_irqrestore(&tp->lock, flags);
-
- if (bmcr & BMCR_ANENABLE) {
- advertising |= ADVERTISED_Autoneg;
- autoneg = AUTONEG_ENABLE;
-
- if (bmsr & BMSR_ANEGCOMPLETE) {
- lp_advertising = mii_lpa_to_ethtool_lpa_t(anlpar);
- lp_advertising |=
- mii_stat1000_to_ethtool_lpa_t(stat1000);
- } else {
- lp_advertising = 0;
- }
-
- if (tp->phy_auto_nego_reg & ADVERTISE_10HALF)
- advertising |= ADVERTISED_10baseT_Half;
- if (tp->phy_auto_nego_reg & ADVERTISE_10FULL)
- advertising |= ADVERTISED_10baseT_Full;
- if (tp->phy_auto_nego_reg & ADVERTISE_100HALF)
- advertising |= ADVERTISED_100baseT_Half;
- if (tp->phy_auto_nego_reg & ADVERTISE_100FULL)
- advertising |= ADVERTISED_100baseT_Full;
- if (tp->phy_1000_ctrl_reg & ADVERTISE_1000FULL)
- advertising |= ADVERTISED_1000baseT_Full;
- } else {
- autoneg = AUTONEG_DISABLE;
- lp_advertising = 0;
- }
-
- status = RTL_R8(PHYstatus);
-
- if (status & LinkStatus) {
- /*link on*/
- if (status & _1000bpsF)
- speed = SPEED_1000;
- else if (status & _100bps)
- speed = SPEED_100;
- else if (status & _10bps)
- speed = SPEED_10;
-
- if (status & TxFlowCtrl)
- advertising |= ADVERTISED_Asym_Pause;
-
- if (status & RxFlowCtrl)
- advertising |= ADVERTISED_Pause;
-
- duplex = ((status & _1000bpsF) || (status & FullDup)) ?
- DUPLEX_FULL : DUPLEX_HALF;
- } else {
- /*link down*/
- speed = SPEED_UNKNOWN;
- duplex = DUPLEX_UNKNOWN;
- }
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,6,0)
- cmd->supported = supported;
- cmd->advertising = advertising;
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30)
- cmd->lp_advertising = lp_advertising;
-#endif
- cmd->autoneg = autoneg;
- cmd->speed = speed;
- cmd->duplex = duplex;
- cmd->port = PORT_TP;
-#else
- ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
- supported);
- ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
- advertising);
- ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising,
- lp_advertising);
- cmd->base.autoneg = autoneg;
- cmd->base.speed = speed;
- cmd->base.duplex = duplex;
- cmd->base.port = PORT_TP;
-#endif
-}
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22)
-static int rtl8169_get_settings(struct net_device *dev,
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,6,0)
- struct ethtool_cmd *cmd
-#else
- struct ethtool_link_ksettings *cmd
-#endif
- )
-{
- struct rtl8169_private *tp = netdev_priv(dev);
-
- tp->get_settings(dev, cmd);
-
- return 0;
-}
-
-static void rtl8169_get_regs(struct net_device *dev, struct ethtool_regs *regs,
- void *p)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- unsigned int i;
- u8 *data = p;
- unsigned long flags;
-
- if (regs->len < R8169_REGS_DUMP_SIZE)
- return /* -EINVAL */;
-
- memset(p, 0, regs->len);
-
- spin_lock_irqsave(&tp->lock, flags);
- for (i = 0; i < R8169_MAC_REGS_SIZE; i++)
- *data++ = readb(ioaddr + i);
- data = (u8*)p + 256;
-
- mdio_write(tp, 0x1F, 0x0000);
- for (i = 0; i < R8169_PHY_REGS_SIZE/2; i++) {
- *(u16*)data = mdio_read(tp, i);
- data += 2;
- }
- spin_unlock_irqrestore(&tp->lock, flags);
-}
-
-static void rtl8169_get_pauseparam(struct net_device *dev,
- struct ethtool_pauseparam *pause)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
-
- pause->autoneg = (tp->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE);
- if (tp->fcpause == rtl8169_fc_rx_pause)
- pause->rx_pause = 1;
- else if (tp->fcpause == rtl8169_fc_tx_pause)
- pause->tx_pause = 1;
- else if (tp->fcpause == rtl8169_fc_full) {
- pause->rx_pause = 1;
- pause->tx_pause = 1;
- }
-}
-
-static int rtl8169_set_pauseparam(struct net_device *dev,
- struct ethtool_pauseparam *pause)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- enum rtl8169_fc_mode newfc;
-
- if (pause->tx_pause || pause->rx_pause)
- newfc = rtl8169_fc_full;
- else
- newfc = rtl8169_fc_none;
-
- if (tp->fcpause != newfc) {
- tp->fcpause = newfc;
-
- rtl8169_set_speed(dev, tp->autoneg, tp->speed, tp->duplex, tp->advertising);
- }
-
- return 0;
-
-}
-
-static u32 rtl8169_get_msglevel(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
-
- return tp->msg_enable;
-}
-
-static void rtl8169_set_msglevel(struct net_device *dev, u32 value)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
-
- tp->msg_enable = value;
-}
-
-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",
-};
-#endif //#LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22)
-
-struct rtl8169_counters {
- u64 tx_packets;
- u64 rx_packets;
- u64 tx_errors;
- u32 rx_errors;
- u16 rx_missed;
- u16 align_errors;
- u32 tx_one_collision;
- u32 tx_multi_collision;
- u64 rx_unicast;
- u64 rx_broadcast;
- u32 rx_multicast;
- u16 tx_aborted;
- u16 tx_underun;
-};
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22)
-static int rtl8169_get_stats_count(struct net_device *dev)
-{
- return ARRAY_SIZE(rtl8169_gstrings);
-}
-#endif //#LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22)
-#else
-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;
- }
-}
-#endif
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22)
-static void rtl8169_get_ethtool_stats(struct net_device *dev,
- struct ethtool_stats *stats, u64 *data)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- struct rtl8169_counters *counters;
- dma_addr_t paddr;
- u32 cmd;
-
- ASSERT_RTNL();
-
- counters = dma_alloc_coherent(&tp->pci_dev->dev, sizeof(*counters), &paddr, GFP_KERNEL);
- if (!counters)
- return;
-
- RTL_W32(CounterAddrHigh, (u64)paddr >> 32);
- cmd = (u64)paddr & DMA_BIT_MASK(32);
- RTL_W32(CounterAddrLow, cmd);
- RTL_W32(CounterAddrLow, cmd | CounterDump);
-
- while (RTL_R32(CounterAddrLow) & CounterDump) {
- if (msleep_interruptible(1))
- break;
- }
-
- RTL_W32(CounterAddrLow, 0);
- RTL_W32(CounterAddrHigh, 0);
-
- 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_underun);
-
- dma_free_coherent(&tp->pci_dev->dev, sizeof(*counters), counters, paddr);
-}
-
-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;
- }
-}
-#endif //LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22)
-
-#undef ethtool_op_get_link
-#define ethtool_op_get_link _kc_ethtool_op_get_link
-u32 _kc_ethtool_op_get_link(struct net_device *dev)
-{
- return netif_carrier_ok(dev) ? 1 : 0;
-}
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
-#undef ethtool_op_get_sg
-#define ethtool_op_get_sg _kc_ethtool_op_get_sg
-u32 _kc_ethtool_op_get_sg(struct net_device *dev)
-{
-#ifdef NETIF_F_SG
- return (dev->features & NETIF_F_SG) != 0;
-#else
- return 0;
-#endif
-}
-
-#undef ethtool_op_set_sg
-#define ethtool_op_set_sg _kc_ethtool_op_set_sg
-int _kc_ethtool_op_set_sg(struct net_device *dev, u32 data)
-{
-#ifdef NETIF_F_SG
- if (data)
- dev->features |= NETIF_F_SG;
- else
- dev->features &= ~NETIF_F_SG;
-#endif
-
- return 0;
-}
-#endif
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22)
-static const struct ethtool_ops rtl8169_ethtool_ops = {
- .get_drvinfo = rtl8169_get_drvinfo,
- .get_regs_len = rtl8169_get_regs_len,
- .get_link = ethtool_op_get_link,
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,6,0)
- .get_settings = rtl8169_get_settings,
- .set_settings = rtl8169_set_settings,
-#else
- .get_link_ksettings = rtl8169_get_settings,
- .set_link_ksettings = rtl8169_set_settings,
-#endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
- .get_pauseparam = rtl8169_get_pauseparam,
- .set_pauseparam = rtl8169_set_pauseparam,
-#endif
- .get_msglevel = rtl8169_get_msglevel,
- .set_msglevel = rtl8169_set_msglevel,
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0)
- .get_rx_csum = rtl8169_get_rx_csum,
- .set_rx_csum = rtl8169_set_rx_csum,
- .get_tx_csum = rtl8169_get_tx_csum,
- .set_tx_csum = rtl8169_set_tx_csum,
- .get_sg = ethtool_op_get_sg,
- .set_sg = ethtool_op_set_sg,
-#ifdef NETIF_F_TSO
- .get_tso = ethtool_op_get_tso,
- .set_tso = ethtool_op_set_tso,
-#endif
-#endif
- .get_regs = rtl8169_get_regs,
- .get_wol = rtl8169_get_wol,
- .set_wol = rtl8169_set_wol,
- .get_strings = rtl8169_get_strings,
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
- .get_stats_count = rtl8169_get_stats_count,
-#else
- .get_sset_count = rtl8169_get_sset_count,
-#endif
- .get_ethtool_stats = rtl8169_get_ethtool_stats,
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
-#ifdef ETHTOOL_GPERMADDR
- .get_perm_addr = ethtool_op_get_perm_addr,
-#endif
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23)
-};
-#endif //LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22)
-
-static void rtl8169_get_mac_version(struct rtl8169_private *tp, void __iomem *ioaddr)
-{
- u32 reg,val32;
-
- val32 = RTL_R32(TxConfig) ;
- reg = val32 & 0xFC800000;
-
- switch(reg) {
- case 0x00000000:
- tp->mcfg = CFG_METHOD_1;
- break;
- case 0x00800000:
- tp->mcfg = CFG_METHOD_2;
- break;
- case 0x04000000:
- tp->mcfg = CFG_METHOD_3;
- break;
- case 0x10000000:
- tp->mcfg = CFG_METHOD_4;
- break;
- case 0x18000000:
- tp->mcfg = CFG_METHOD_5;
- break;
- case 0x98000000:
- tp->mcfg = CFG_METHOD_6;
- break;
- default:
- tp->mcfg = 0xFFFFFFFF;
- printk("unknown chip version (%x)\n",reg);
- break;
- }
-}
-
-static void rtl8169_print_mac_version(struct rtl8169_private *tp)
-{
- dprintk("mac_version = 0x%02x\n", tp->mcfg);
-}
-
-static void rtl8169_get_phy_version(struct rtl8169_private *tp)
-{
- const struct {
- u16 mask;
- u16 set;
- int pcfg;
- } phy_info[] = {
- { 0x000f, 0x0002, PCFG_METHOD_5 },
- { 0x000f, 0x0001, PCFG_METHOD_4 },
- { 0x000f, 0x0000, PCFG_METHOD_3 },
- { 0x0000, 0x0000, PCFG_METHOD_2 } /* Catch-all */
- }, *p = phy_info;
- u16 reg;
-
- mdio_write(tp, 0x1f, 0x0000);
- reg = mdio_read(tp, MII_PHYSID2) & 0xffff;
-
- while ((reg & p->mask) != p->set)
- p++;
- tp->pcfg = p->pcfg;
-}
-
-static void rtl8169_print_phy_version(struct rtl8169_private *tp)
-{
- struct {
- int version;
- char *msg;
- u32 reg;
- } phy_print[] = {
- { PCFG_METHOD_5, "PCFG_METHOD_5", 0x0002 },
- { PCFG_METHOD_4, "PCFG_METHOD_4", 0x0001 },
- { PCFG_METHOD_3, "PCFG_METHOD_3", 0x0000 },
- { PCFG_METHOD_2, "PCFG_METHOD_2", 0x0000 },
- { 0, NULL, 0x0000 }
- }, *p;
-
- for (p = phy_print; p->msg; p++) {
- if (tp->pcfg == p->version) {
- dprintk("phy_version == %s (%04x)\n", p->msg, p->reg);
- return;
- }
- }
- dprintk("phy_version == Unknown\n");
-}
-
-static void rtl8169_hw_phy_config(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- struct pci_dev *pdev = tp->pci_dev;
- u16 vendor_id;
- u16 device_id;
-
- tp->phy_reset_enable(dev);
-
- if (tp->mcfg == CFG_METHOD_6) {
- mdio_write(tp, 0x1f, 0x0001);
- mdio_write(tp, 0x04, 0x0000);
- mdio_write(tp, 0x03, 0x00a1);
- mdio_write(tp, 0x02, 0x0008);
- mdio_write(tp, 0x01, 0x0120);
- mdio_write(tp, 0x00, 0x1000);
- mdio_write(tp, 0x04, 0x0800);
- mdio_write(tp, 0x04, 0x9000);
- mdio_write(tp, 0x03, 0x802f);
- mdio_write(tp, 0x02, 0x4f02);
- mdio_write(tp, 0x01, 0x0409);
- mdio_write(tp, 0x00, 0xf099);
- mdio_write(tp, 0x04, 0x9800);
- mdio_write(tp, 0x04, 0xa000);
- mdio_write(tp, 0x03, 0xdf01);
- mdio_write(tp, 0x02, 0xdf20);
- mdio_write(tp, 0x01, 0xff95);
- mdio_write(tp, 0x00, 0xba00);
- mdio_write(tp, 0x04, 0xa800);
- mdio_write(tp, 0x04, 0xf000);
- mdio_write(tp, 0x03, 0xdf01);
- mdio_write(tp, 0x02, 0xdf20);
- mdio_write(tp, 0x01, 0x101a);
- mdio_write(tp, 0x00, 0xa0ff);
- mdio_write(tp, 0x04, 0xf800);
- mdio_write(tp, 0x04, 0x0000);
- mdio_write(tp, 0x1f, 0x0000);
-
- mdio_write(tp, 0x1f, 0x0001);
- mdio_write(tp, 0x0b, 0x8480);
- mdio_write(tp, 0x1f, 0x0000);
-
- mdio_write(tp, 0x1f, 0x0001);
- mdio_write(tp, 0x18, 0x67c7);
- mdio_write(tp, 0x04, 0x2000);
- mdio_write(tp, 0x03, 0x002f);
- mdio_write(tp, 0x02, 0x4360);
- mdio_write(tp, 0x01, 0x0109);
- mdio_write(tp, 0x00, 0x3022);
- mdio_write(tp, 0x04, 0x2800);
- mdio_write(tp, 0x1f, 0x0000);
-
- mdio_write(tp, 0x1f, 0x0001);
- mdio_write(tp, 0x17, 0x0cc0);
- mdio_write(tp, 0x1f, 0x0000);
- } else if (tp->mcfg == CFG_METHOD_5) {
- mdio_write(tp, 0x1f, 0x0001);
- mdio_write(tp, 0x04, 0x0000);
- mdio_write(tp, 0x03, 0x00a1);
- mdio_write(tp, 0x02, 0x0008);
- mdio_write(tp, 0x01, 0x0120);
- mdio_write(tp, 0x00, 0x1000);
- mdio_write(tp, 0x04, 0x0800);
- mdio_write(tp, 0x04, 0x9000);
- mdio_write(tp, 0x03, 0x802f);
- mdio_write(tp, 0x02, 0x4f02);
- mdio_write(tp, 0x01, 0x0409);
- mdio_write(tp, 0x00, 0xf099);
- mdio_write(tp, 0x04, 0x9800);
- mdio_write(tp, 0x04, 0xa000);
- mdio_write(tp, 0x03, 0xdf01);
- mdio_write(tp, 0x02, 0xdf20);
- mdio_write(tp, 0x01, 0xff95);
- mdio_write(tp, 0x00, 0xba00);
- mdio_write(tp, 0x04, 0xa800);
- mdio_write(tp, 0x04, 0xf000);
- mdio_write(tp, 0x03, 0xdf01);
- mdio_write(tp, 0x02, 0xdf20);
- mdio_write(tp, 0x01, 0x101a);
- mdio_write(tp, 0x00, 0xa0ff);
- mdio_write(tp, 0x04, 0xf800);
- mdio_write(tp, 0x04, 0x0000);
- mdio_write(tp, 0x1f, 0x0000);
-
- mdio_write(tp, 0x1f, 0x0001);
- mdio_write(tp, 0x10, 0xf41b);
- mdio_write(tp, 0x14, 0xfb54);
- mdio_write(tp, 0x18, 0xf5c7);
- mdio_write(tp, 0x1f, 0x0000);
-
- mdio_write(tp, 0x1f, 0x0001);
- mdio_write(tp, 0x17, 0x0cc0);
- mdio_write(tp, 0x1f, 0x0000);
-
- pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &vendor_id);
- pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &device_id);
- if ((vendor_id == 0x1458) && (device_id == 0xe000)) {
- mdio_write(tp, 0x1f, 0x0001);
- mdio_write(tp, 0x10, 0xf01b);
- mdio_write(tp, 0x1f, 0x0000);
- }
- } else if (tp->mcfg == CFG_METHOD_4) {
- mdio_write(tp, 0x1f, 0x0002);
- mdio_write(tp, 0x01, 0x90d0);
- mdio_write(tp, 0x1f, 0x0000);
- //mdio_write(tp, 0x1e, 0x8c00); /* PHY link down with some Giga switch */
- } else if ((tp->mcfg == CFG_METHOD_2) || (tp->mcfg == CFG_METHOD_3)) {
- mdio_write(tp, 0x1f, 0x0001);
- mdio_write(tp, 0x06, 0x006e);
- mdio_write(tp, 0x08, 0x0708);
- mdio_write(tp, 0x15, 0x4000);
- mdio_write(tp, 0x18, 0x65c7);
-
- mdio_write(tp, 0x1f, 0x0001);
- mdio_write(tp, 0x03, 0x00a1);
- mdio_write(tp, 0x02, 0x0008);
- mdio_write(tp, 0x01, 0x0120);
- mdio_write(tp, 0x00, 0x1000);
- mdio_write(tp, 0x04, 0x0800);
- mdio_write(tp, 0x04, 0x0000);
-
- mdio_write(tp, 0x03, 0xff41);
- mdio_write(tp, 0x02, 0xdf60);
- mdio_write(tp, 0x01, 0x0140);
- mdio_write(tp, 0x00, 0x0077);
- mdio_write(tp, 0x04, 0x7800);
- mdio_write(tp, 0x04, 0x7000);
-
- mdio_write(tp, 0x03, 0x802f);
- mdio_write(tp, 0x02, 0x4f02);
- mdio_write(tp, 0x01, 0x0409);
- mdio_write(tp, 0x00, 0xf0f9);
- mdio_write(tp, 0x04, 0x9800);
- mdio_write(tp, 0x04, 0x9000);
-
- mdio_write(tp, 0x03, 0xdf01);
- mdio_write(tp, 0x02, 0xdf20);
- mdio_write(tp, 0x01, 0xff95);
- mdio_write(tp, 0x00, 0xba00);
- mdio_write(tp, 0x04, 0xa800);
- mdio_write(tp, 0x04, 0xa000);
-
- mdio_write(tp, 0x03, 0xff41);
- mdio_write(tp, 0x02, 0xdf20);
- mdio_write(tp, 0x01, 0x0140);
- mdio_write(tp, 0x00, 0x00bb);
- mdio_write(tp, 0x04, 0xb800);
- mdio_write(tp, 0x04, 0xb000);
-
- mdio_write(tp, 0x03, 0xdf41);
- mdio_write(tp, 0x02, 0xdc60);
- mdio_write(tp, 0x01, 0x6340);
- mdio_write(tp, 0x00, 0x007d);
- mdio_write(tp, 0x04, 0xd800);
- mdio_write(tp, 0x04, 0xd000);
-
- mdio_write(tp, 0x03, 0xdf01);
- mdio_write(tp, 0x02, 0xdf20);
- mdio_write(tp, 0x01, 0x100a);
- mdio_write(tp, 0x00, 0xa0ff);
- mdio_write(tp, 0x04, 0xf800);
- mdio_write(tp, 0x04, 0xf000);
-
- mdio_write(tp, 0x1f, 0x0000);
- mdio_write(tp, 0x0b, 0x0000);
- mdio_write(tp, 0x00, 0x9200);
- }
-
- mdio_write(tp, 0x1F, 0x0000);
-}
-
-static void
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
-rtl8169_phy_timer(unsigned long __opaque)
-#else
-rtl8169_phy_timer(struct timer_list *t)
-#endif
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
- struct net_device *dev = (struct net_device *)__opaque;
- struct rtl8169_private *tp = netdev_priv(dev);
- struct timer_list *timer = &tp->link_timer;
-#else
- struct rtl8169_private *tp = from_timer(tp, t, link_timer);
- struct net_device *dev = tp->dev;
- struct timer_list *timer = t;
-#endif
- unsigned long timeout = RTL8169_PHY_TIMEOUT;
-
- assert(tp->mcfg > CFG_METHOD_1);
- assert(tp->pcfg < PCFG_METHOD_6);
-
- if (!(tp->phy_1000_ctrl_reg & ADVERTISE_1000FULL))
- return;
-
- spin_lock_irq(&tp->lock);
-
- if (tp->phy_reset_pending(dev)) {
- /*
- * A busy loop could burn quite a few cycles on nowadays CPU.
- * Let's delay the execution of the timer for a few ticks.
- */
- timeout = HZ/10;
- goto out_mod_timer;
- }
-
- if (tp->link_ok(dev))
- goto out_unlock;
-
- if (netif_msg_link(tp))
- printk(KERN_WARNING "%s: PHY reset until link up\n", dev->name);
-
- tp->phy_reset_enable(dev);
-
-out_mod_timer:
- mod_timer(timer, jiffies + timeout);
-out_unlock:
- spin_unlock_irq(&tp->lock);
-}
-
-static inline void rtl8169_delete_timer(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- struct timer_list *timer = &tp->link_timer;
-
- if ((tp->mcfg <= CFG_METHOD_1) ||
- (tp->pcfg >= PCFG_METHOD_6))
- return;
-
- del_timer_sync(timer);
-}
-
-static inline void rtl8169_request_timer(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- struct timer_list *timer = &tp->link_timer;
-
- if ((tp->mcfg <= CFG_METHOD_1) ||
- (tp->pcfg >= PCFG_METHOD_6))
- return;
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
- setup_timer(timer, rtl8169_phy_timer, (unsigned long)dev);
-#else
- timer_setup(timer, rtl8169_phy_timer, 0);
-#endif
- mod_timer(timer, jiffies + RTL8169_PHY_TIMEOUT);
-}
-
-static inline void rtl8169_delete_esd_timer(struct net_device *dev, struct timer_list *timer)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
-
- spin_lock_irq(&tp->lock);
- del_timer_sync(timer);
- spin_unlock_irq(&tp->lock);
-}
-
-static inline void rtl8169_request_esd_timer(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- struct timer_list *timer = &tp->esd_timer;
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
- setup_timer(timer, rtl8169_esd_timer, (unsigned long)dev);
-#else
- timer_setup(timer, rtl8169_esd_timer, 0);
-#endif
- mod_timer(timer, jiffies + RTL8169_ESD_TIMEOUT);
-}
-
-static void
-rtl8169_hw_init(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W32(RxConfig, RTL_R32(RxConfig) & ~(AcceptErr | AcceptRunt | AcceptBroadcast | AcceptMulticast | AcceptMyPhys | AcceptAllPhys));
-
- if (RTL_R8(Config2) & PCI_Clock_66MHz) {
- if (tp->mcfg == CFG_METHOD_5)
- RTL_W32(Offset_7Ch, 0x000FFFFF);
- else if (tp->mcfg == CFG_METHOD_6)
- RTL_W32(Offset_7Ch, 0x003FFFFF);
- } else {
- if (tp->mcfg == CFG_METHOD_5)
- RTL_W32(Offset_7Ch, 0x000FFF00);
- else if (tp->mcfg == CFG_METHOD_6)
- RTL_W32(Offset_7Ch, 0x003FFF00);
- }
-
- if (tp->mcfg == CFG_METHOD_4) {
- RTL_W8(Cfg9346, Cfg9346_Unlock);
- RTL_W8(Config4, RTL_R8(Config4) | iMode);
- RTL_W8(Cfg9346, Cfg9346_Lock);
- }
-}
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-/*
- * Polling 'interrupt' - used by things like netconsole to send skbs
- * without having to re-enable interrupts. It's not called while
- * the interrupt routine is executing.
- */
-static void rtl8169_netpoll(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- struct pci_dev *pdev = tp->pci_dev;
-
- disable_irq(pdev->irq);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
- rtl8169_interrupt(pdev->irq, dev, NULL);
-#else
- rtl8169_interrupt(pdev->irq, dev);
-#endif
- enable_irq(pdev->irq);
-}
-#endif
-
-static void
-rtl8169_init_software_variable(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
-
- tp->UseSwPaddingShortPkt = TRUE;
-}
-
-static void rtl8169_release_board(struct pci_dev *pdev, struct net_device *dev,
- void __iomem *ioaddr)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
-
- rtl8169_phy_power_down(dev);
-
- /* restore the original MAC address */
- rtl8169_rar_set(tp, tp->org_mac_addr);
-
- iounmap(ioaddr);
- pci_release_regions(pdev);
- pci_clear_mwi(pdev);
- pci_disable_device(pdev);
- free_netdev(dev);
-}
-
-static void
-rtl8169_hw_address_set(struct net_device *dev, u8 mac_addr[MAC_ADDR_LEN])
-{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,17,0)
- eth_hw_addr_set(dev, mac_addr);
-#else
- memcpy(dev->dev_addr, mac_addr, MAC_ADDR_LEN);
-#endif //LINUX_VERSION_CODE >= KERNEL_VERSION(5,17,0)
-}
-
-static int
-rtl8169_get_mac_address(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- int i;
- u8 mac_addr[MAC_ADDR_LEN];
-
- /* Get MAC address. FIXME: read EEPROM */
- for (i = 0; i < MAC_ADDR_LEN; i++)
- mac_addr[i] = RTL_R8(MAC0 + i);
-
- if (!is_valid_ether_addr(mac_addr)) {
- netif_err(tp, probe, dev, "Invalid ether addr %pM\n",
- mac_addr);
- eth_random_addr(mac_addr);
- dev->addr_assign_type = NET_ADDR_RANDOM;
- netif_info(tp, probe, dev, "Random ether addr %pM\n",
- mac_addr);
- tp->random_mac = 1;
- }
-
- rtl8169_hw_address_set(dev, mac_addr);
- rtl8169_rar_set(tp, mac_addr);
-
- /* keep the original MAC address */
- memcpy(tp->org_mac_addr, dev->dev_addr, MAC_ADDR_LEN);
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13)
- memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len);
-#endif
- return 0;
-}
-
-/**
- * rtl8169_set_mac_address - Change the Ethernet Address of the NIC
- * @dev: network interface device structure
- * @p: pointer to an address structure
- *
- * Return 0 on success, negative on failure
- **/
-static int
-rtl8169_set_mac_address(struct net_device *dev,
- void *p)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- struct sockaddr *addr = p;
- unsigned long flags;
-
- if (!is_valid_ether_addr(addr->sa_data))
- return -EADDRNOTAVAIL;
-
- spin_lock_irqsave(&tp->lock, flags);
-
- rtl8169_hw_address_set(dev, addr->sa_data);
-
- rtl8169_rar_set(tp, dev->dev_addr);
-
- spin_unlock_irqrestore(&tp->lock, flags);
-
- return 0;
-}
-
-/******************************************************************************
- * rtl8169_rar_set - Puts an ethernet address into a receive address register.
- *
- * tp - The private data structure for driver
- * addr - Address to put into receive address register
- *****************************************************************************/
-void
-rtl8169_rar_set(struct rtl8169_private *tp,
- const u8 *addr)
-{
- void __iomem *ioaddr = tp->mmio_addr;
- uint32_t rar_low = 0;
- uint32_t rar_high = 0;
-
- rar_low = ((uint32_t) addr[0] |
- ((uint32_t) addr[1] << 8) |
- ((uint32_t) addr[2] << 16) |
- ((uint32_t) addr[3] << 24));
-
- rar_high = ((uint32_t) addr[4] |
- ((uint32_t) addr[5] << 8));
-
- RTL_W8(Cfg9346, Cfg9346_Unlock);
- RTL_W32(MAC0, rar_low);
- RTL_W32(MAC4, rar_high);
- RTL_W8(Cfg9346, Cfg9346_Lock);
-}
-
-#ifdef ETHTOOL_OPS_COMPAT
-static int ethtool_get_settings(struct net_device *dev, void *useraddr)
-{
- struct ethtool_cmd cmd = { ETHTOOL_GSET };
- int err;
-
- if (!ethtool_ops->get_settings)
- return -EOPNOTSUPP;
-
- err = ethtool_ops->get_settings(dev, &cmd);
- if (err < 0)
- return err;
-
- if (copy_to_user(useraddr, &cmd, sizeof(cmd)))
- return -EFAULT;
- return 0;
-}
-
-static int ethtool_set_settings(struct net_device *dev, void *useraddr)
-{
- struct ethtool_cmd cmd;
-
- if (!ethtool_ops->set_settings)
- return -EOPNOTSUPP;
-
- if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
- return -EFAULT;
-
- return ethtool_ops->set_settings(dev, &cmd);
-}
-
-static int ethtool_get_drvinfo(struct net_device *dev, void *useraddr)
-{
- struct ethtool_drvinfo info;
- struct ethtool_ops *ops = ethtool_ops;
-
- if (!ops->get_drvinfo)
- return -EOPNOTSUPP;
-
- memset(&info, 0, sizeof(info));
- info.cmd = ETHTOOL_GDRVINFO;
- ops->get_drvinfo(dev, &info);
-
- if (ops->self_test_count)
- info.testinfo_len = ops->self_test_count(dev);
- if (ops->get_stats_count)
- info.n_stats = ops->get_stats_count(dev);
- if (ops->get_regs_len)
- info.regdump_len = ops->get_regs_len(dev);
- if (ops->get_eeprom_len)
- info.eedump_len = ops->get_eeprom_len(dev);
-
- if (copy_to_user(useraddr, &info, sizeof(info)))
- return -EFAULT;
- return 0;
-}
-
-static int ethtool_get_regs(struct net_device *dev, char *useraddr)
-{
- struct ethtool_regs regs;
- struct ethtool_ops *ops = ethtool_ops;
- void *regbuf;
- int reglen, ret;
-
- if (!ops->get_regs || !ops->get_regs_len)
- return -EOPNOTSUPP;
-
- if (copy_from_user(®s, useraddr, sizeof(regs)))
- return -EFAULT;
-
- reglen = ops->get_regs_len(dev);
- if (regs.len > reglen)
- regs.len = reglen;
-
- regbuf = kmalloc(reglen, GFP_USER);
- if (!regbuf)
- return -ENOMEM;
-
- ops->get_regs(dev, ®s, regbuf);
-
- ret = -EFAULT;
- if (copy_to_user(useraddr, ®s, sizeof(regs)))
- goto out;
- useraddr += offsetof(struct ethtool_regs, data);
- if (copy_to_user(useraddr, regbuf, reglen))
- goto out;
- ret = 0;
-
-out:
- kfree(regbuf);
- return ret;
-}
-
-static int ethtool_get_wol(struct net_device *dev, char *useraddr)
-{
- struct ethtool_wolinfo wol = { ETHTOOL_GWOL };
-
- if (!ethtool_ops->get_wol)
- return -EOPNOTSUPP;
-
- ethtool_ops->get_wol(dev, &wol);
-
- if (copy_to_user(useraddr, &wol, sizeof(wol)))
- return -EFAULT;
- return 0;
-}
-
-static int ethtool_set_wol(struct net_device *dev, char *useraddr)
-{
- struct ethtool_wolinfo wol;
-
- if (!ethtool_ops->set_wol)
- return -EOPNOTSUPP;
-
- if (copy_from_user(&wol, useraddr, sizeof(wol)))
- return -EFAULT;
-
- return ethtool_ops->set_wol(dev, &wol);
-}
-
-static int ethtool_get_msglevel(struct net_device *dev, char *useraddr)
-{
- struct ethtool_value edata = { ETHTOOL_GMSGLVL };
-
- if (!ethtool_ops->get_msglevel)
- return -EOPNOTSUPP;
-
- edata.data = ethtool_ops->get_msglevel(dev);
-
- if (copy_to_user(useraddr, &edata, sizeof(edata)))
- return -EFAULT;
- return 0;
-}
-
-static int ethtool_set_msglevel(struct net_device *dev, char *useraddr)
-{
- struct ethtool_value edata;
-
- if (!ethtool_ops->set_msglevel)
- return -EOPNOTSUPP;
-
- if (copy_from_user(&edata, useraddr, sizeof(edata)))
- return -EFAULT;
-
- ethtool_ops->set_msglevel(dev, edata.data);
- return 0;
-}
-
-static int ethtool_nway_reset(struct net_device *dev)
-{
- if (!ethtool_ops->nway_reset)
- return -EOPNOTSUPP;
-
- return ethtool_ops->nway_reset(dev);
-}
-
-static int ethtool_get_link(struct net_device *dev, void *useraddr)
-{
- struct ethtool_value edata = { ETHTOOL_GLINK };
-
- if (!ethtool_ops->get_link)
- return -EOPNOTSUPP;
-
- edata.data = ethtool_ops->get_link(dev);
-
- if (copy_to_user(useraddr, &edata, sizeof(edata)))
- return -EFAULT;
- return 0;
-}
-
-static int ethtool_get_eeprom(struct net_device *dev, void *useraddr)
-{
- struct ethtool_eeprom eeprom;
- struct ethtool_ops *ops = ethtool_ops;
- u8 *data;
- int ret;
-
- if (!ops->get_eeprom || !ops->get_eeprom_len)
- return -EOPNOTSUPP;
-
- if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
- return -EFAULT;
-
- /* Check for wrap and zero */
- if (eeprom.offset + eeprom.len <= eeprom.offset)
- return -EINVAL;
-
- /* Check for exceeding total eeprom len */
- if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev))
- return -EINVAL;
-
- data = kmalloc(eeprom.len, GFP_USER);
- if (!data)
- return -ENOMEM;
-
- ret = -EFAULT;
- if (copy_from_user(data, useraddr + sizeof(eeprom), eeprom.len))
- goto out;
-
- ret = ops->get_eeprom(dev, &eeprom, data);
- if (ret)
- goto out;
-
- ret = -EFAULT;
- if (copy_to_user(useraddr, &eeprom, sizeof(eeprom)))
- goto out;
- if (copy_to_user(useraddr + sizeof(eeprom), data, eeprom.len))
- goto out;
- ret = 0;
-
-out:
- kfree(data);
- return ret;
-}
-
-static int ethtool_set_eeprom(struct net_device *dev, void *useraddr)
-{
- struct ethtool_eeprom eeprom;
- struct ethtool_ops *ops = ethtool_ops;
- u8 *data;
- int ret;
-
- if (!ops->set_eeprom || !ops->get_eeprom_len)
- return -EOPNOTSUPP;
-
- if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
- return -EFAULT;
-
- /* Check for wrap and zero */
- if (eeprom.offset + eeprom.len <= eeprom.offset)
- return -EINVAL;
-
- /* Check for exceeding total eeprom len */
- if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev))
- return -EINVAL;
-
- data = kmalloc(eeprom.len, GFP_USER);
- if (!data)
- return -ENOMEM;
-
- ret = -EFAULT;
- if (copy_from_user(data, useraddr + sizeof(eeprom), eeprom.len))
- goto out;
-
- ret = ops->set_eeprom(dev, &eeprom, data);
- if (ret)
- goto out;
-
- if (copy_to_user(useraddr + sizeof(eeprom), data, eeprom.len))
- ret = -EFAULT;
-
-out:
- kfree(data);
- return ret;
-}
-
-static int ethtool_get_coalesce(struct net_device *dev, void *useraddr)
-{
- struct ethtool_coalesce coalesce = { ETHTOOL_GCOALESCE };
-
- if (!ethtool_ops->get_coalesce)
- return -EOPNOTSUPP;
-
- ethtool_ops->get_coalesce(dev, &coalesce);
-
- if (copy_to_user(useraddr, &coalesce, sizeof(coalesce)))
- return -EFAULT;
- return 0;
-}
-
-static int ethtool_set_coalesce(struct net_device *dev, void *useraddr)
-{
- struct ethtool_coalesce coalesce;
-
- if (!ethtool_ops->get_coalesce)
- return -EOPNOTSUPP;
-
- if (copy_from_user(&coalesce, useraddr, sizeof(coalesce)))
- return -EFAULT;
-
- return ethtool_ops->set_coalesce(dev, &coalesce);
-}
-
-static int ethtool_get_ringparam(struct net_device *dev, void *useraddr)
-{
- struct ethtool_ringparam ringparam = { ETHTOOL_GRINGPARAM };
-
- if (!ethtool_ops->get_ringparam)
- return -EOPNOTSUPP;
-
- ethtool_ops->get_ringparam(dev, &ringparam);
-
- if (copy_to_user(useraddr, &ringparam, sizeof(ringparam)))
- return -EFAULT;
- return 0;
-}
-
-static int ethtool_set_ringparam(struct net_device *dev, void *useraddr)
-{
- struct ethtool_ringparam ringparam;
-
- if (!ethtool_ops->get_ringparam)
- return -EOPNOTSUPP;
-
- if (copy_from_user(&ringparam, useraddr, sizeof(ringparam)))
- return -EFAULT;
-
- return ethtool_ops->set_ringparam(dev, &ringparam);
-}
-
-static int ethtool_get_pauseparam(struct net_device *dev, void *useraddr)
-{
- struct ethtool_pauseparam pauseparam = { ETHTOOL_GPAUSEPARAM };
-
- if (!ethtool_ops->get_pauseparam)
- return -EOPNOTSUPP;
-
- ethtool_ops->get_pauseparam(dev, &pauseparam);
-
- if (copy_to_user(useraddr, &pauseparam, sizeof(pauseparam)))
- return -EFAULT;
- return 0;
-}
-
-static int ethtool_set_pauseparam(struct net_device *dev, void *useraddr)
-{
- struct ethtool_pauseparam pauseparam;
-
- if (!ethtool_ops->get_pauseparam)
- return -EOPNOTSUPP;
-
- if (copy_from_user(&pauseparam, useraddr, sizeof(pauseparam)))
- return -EFAULT;
-
- return ethtool_ops->set_pauseparam(dev, &pauseparam);
-}
-
-static int ethtool_get_rx_csum(struct net_device *dev, char *useraddr)
-{
- struct ethtool_value edata = { ETHTOOL_GRXCSUM };
-
- if (!ethtool_ops->get_rx_csum)
- return -EOPNOTSUPP;
-
- edata.data = ethtool_ops->get_rx_csum(dev);
-
- if (copy_to_user(useraddr, &edata, sizeof(edata)))
- return -EFAULT;
- return 0;
-}
-
-static int ethtool_set_rx_csum(struct net_device *dev, char *useraddr)
-{
- struct ethtool_value edata;
-
- if (!ethtool_ops->set_rx_csum)
- return -EOPNOTSUPP;
-
- if (copy_from_user(&edata, useraddr, sizeof(edata)))
- return -EFAULT;
-
- ethtool_ops->set_rx_csum(dev, edata.data);
- return 0;
-}
-
-static int ethtool_get_tx_csum(struct net_device *dev, char *useraddr)
-{
- struct ethtool_value edata = { ETHTOOL_GTXCSUM };
-
- if (!ethtool_ops->get_tx_csum)
- return -EOPNOTSUPP;
-
- edata.data = ethtool_ops->get_tx_csum(dev);
-
- if (copy_to_user(useraddr, &edata, sizeof(edata)))
- return -EFAULT;
- return 0;
-}
-
-static int ethtool_set_tx_csum(struct net_device *dev, char *useraddr)
-{
- struct ethtool_value edata;
-
- if (!ethtool_ops->set_tx_csum)
- return -EOPNOTSUPP;
-
- if (copy_from_user(&edata, useraddr, sizeof(edata)))
- return -EFAULT;
-
- return ethtool_ops->set_tx_csum(dev, edata.data);
-}
-
-static int ethtool_get_sg(struct net_device *dev, char *useraddr)
-{
- struct ethtool_value edata = { ETHTOOL_GSG };
-
- if (!ethtool_ops->get_sg)
- return -EOPNOTSUPP;
-
- edata.data = ethtool_ops->get_sg(dev);
-
- if (copy_to_user(useraddr, &edata, sizeof(edata)))
- return -EFAULT;
- return 0;
-}
-
-static int ethtool_set_sg(struct net_device *dev, char *useraddr)
-{
- struct ethtool_value edata;
-
- if (!ethtool_ops->set_sg)
- return -EOPNOTSUPP;
-
- if (copy_from_user(&edata, useraddr, sizeof(edata)))
- return -EFAULT;
-
- return ethtool_ops->set_sg(dev, edata.data);
-}
-
-static int ethtool_get_tso(struct net_device *dev, char *useraddr)
-{
- struct ethtool_value edata = { ETHTOOL_GTSO };
-
- if (!ethtool_ops->get_tso)
- return -EOPNOTSUPP;
-
- edata.data = ethtool_ops->get_tso(dev);
-
- if (copy_to_user(useraddr, &edata, sizeof(edata)))
- return -EFAULT;
- return 0;
-}
-
-static int ethtool_set_tso(struct net_device *dev, char *useraddr)
-{
- struct ethtool_value edata;
-
- if (!ethtool_ops->set_tso)
- return -EOPNOTSUPP;
-
- if (copy_from_user(&edata, useraddr, sizeof(edata)))
- return -EFAULT;
-
- return ethtool_ops->set_tso(dev, edata.data);
-}
-
-static int ethtool_self_test(struct net_device *dev, char *useraddr)
-{
- struct ethtool_test test;
- struct ethtool_ops *ops = ethtool_ops;
- u64 *data;
- int ret;
-
- if (!ops->self_test || !ops->self_test_count)
- return -EOPNOTSUPP;
-
- if (copy_from_user(&test, useraddr, sizeof(test)))
- return -EFAULT;
-
- test.len = ops->self_test_count(dev);
- data = kmalloc(test.len * sizeof(u64), GFP_USER);
- if (!data)
- return -ENOMEM;
-
- ops->self_test(dev, &test, data);
-
- ret = -EFAULT;
- if (copy_to_user(useraddr, &test, sizeof(test)))
- goto out;
- useraddr += sizeof(test);
- if (copy_to_user(useraddr, data, test.len * sizeof(u64)))
- goto out;
- ret = 0;
-
-out:
- kfree(data);
- return ret;
-}
-
-static int ethtool_get_strings(struct net_device *dev, void *useraddr)
-{
- struct ethtool_gstrings gstrings;
- struct ethtool_ops *ops = ethtool_ops;
- u8 *data;
- int ret;
-
- if (!ops->get_strings)
- return -EOPNOTSUPP;
-
- if (copy_from_user(&gstrings, useraddr, sizeof(gstrings)))
- return -EFAULT;
-
- switch (gstrings.string_set) {
- case ETH_SS_TEST:
- if (!ops->self_test_count)
- return -EOPNOTSUPP;
- gstrings.len = ops->self_test_count(dev);
- break;
- case ETH_SS_STATS:
- if (!ops->get_stats_count)
- return -EOPNOTSUPP;
- gstrings.len = ops->get_stats_count(dev);
- break;
- default:
- return -EINVAL;
- }
-
- data = kmalloc(gstrings.len * ETH_GSTRING_LEN, GFP_USER);
- if (!data)
- return -ENOMEM;
-
- ops->get_strings(dev, gstrings.string_set, data);
-
- ret = -EFAULT;
- if (copy_to_user(useraddr, &gstrings, sizeof(gstrings)))
- goto out;
- useraddr += sizeof(gstrings);
- if (copy_to_user(useraddr, data, gstrings.len * ETH_GSTRING_LEN))
- goto out;
- ret = 0;
-
-out:
- kfree(data);
- return ret;
-}
-
-static int ethtool_phys_id(struct net_device *dev, void *useraddr)
-{
- struct ethtool_value id;
-
- if (!ethtool_ops->phys_id)
- return -EOPNOTSUPP;
-
- if (copy_from_user(&id, useraddr, sizeof(id)))
- return -EFAULT;
-
- return ethtool_ops->phys_id(dev, id.data);
-}
-
-static int ethtool_get_stats(struct net_device *dev, void *useraddr)
-{
- struct ethtool_stats stats;
- struct ethtool_ops *ops = ethtool_ops;
- u64 *data;
- int ret;
-
- if (!ops->get_ethtool_stats || !ops->get_stats_count)
- return -EOPNOTSUPP;
-
- if (copy_from_user(&stats, useraddr, sizeof(stats)))
- return -EFAULT;
-
- stats.n_stats = ops->get_stats_count(dev);
- data = kmalloc(stats.n_stats * sizeof(u64), GFP_USER);
- if (!data)
- return -ENOMEM;
-
- ops->get_ethtool_stats(dev, &stats, data);
-
- ret = -EFAULT;
- if (copy_to_user(useraddr, &stats, sizeof(stats)))
- goto out;
- useraddr += sizeof(stats);
- if (copy_to_user(useraddr, data, stats.n_stats * sizeof(u64)))
- goto out;
- ret = 0;
-
-out:
- kfree(data);
- return ret;
-}
-
-static int ethtool_ioctl(struct ifreq *ifr)
-{
- struct net_device *dev = __dev_get_by_name(ifr->ifr_name);
- void *useraddr = (void *) ifr->ifr_data;
- u32 ethcmd;
-
- /*
- * XXX: This can be pushed down into the ethtool_* handlers that
- * need it. Keep existing behaviour for the moment.
- */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
-
- if (!dev || !netif_device_present(dev))
- return -ENODEV;
-
- if (copy_from_user(ðcmd, useraddr, sizeof (ethcmd)))
- return -EFAULT;
-
- switch (ethcmd) {
- case ETHTOOL_GSET:
- return ethtool_get_settings(dev, useraddr);
- case ETHTOOL_SSET:
- return ethtool_set_settings(dev, useraddr);
- case ETHTOOL_GDRVINFO:
- return ethtool_get_drvinfo(dev, useraddr);
- case ETHTOOL_GREGS:
- return ethtool_get_regs(dev, useraddr);
- case ETHTOOL_GWOL:
- return ethtool_get_wol(dev, useraddr);
- case ETHTOOL_SWOL:
- return ethtool_set_wol(dev, useraddr);
- case ETHTOOL_GMSGLVL:
- return ethtool_get_msglevel(dev, useraddr);
- case ETHTOOL_SMSGLVL:
- return ethtool_set_msglevel(dev, useraddr);
- case ETHTOOL_NWAY_RST:
- return ethtool_nway_reset(dev);
- case ETHTOOL_GLINK:
- return ethtool_get_link(dev, useraddr);
- case ETHTOOL_GEEPROM:
- return ethtool_get_eeprom(dev, useraddr);
- case ETHTOOL_SEEPROM:
- return ethtool_set_eeprom(dev, useraddr);
- case ETHTOOL_GCOALESCE:
- return ethtool_get_coalesce(dev, useraddr);
- case ETHTOOL_SCOALESCE:
- return ethtool_set_coalesce(dev, useraddr);
- case ETHTOOL_GRINGPARAM:
- return ethtool_get_ringparam(dev, useraddr);
- case ETHTOOL_SRINGPARAM:
- return ethtool_set_ringparam(dev, useraddr);
- case ETHTOOL_GPAUSEPARAM:
- return ethtool_get_pauseparam(dev, useraddr);
- case ETHTOOL_SPAUSEPARAM:
- return ethtool_set_pauseparam(dev, useraddr);
- case ETHTOOL_GRXCSUM:
- return ethtool_get_rx_csum(dev, useraddr);
- case ETHTOOL_SRXCSUM:
- return ethtool_set_rx_csum(dev, useraddr);
- case ETHTOOL_GTXCSUM:
- return ethtool_get_tx_csum(dev, useraddr);
- case ETHTOOL_STXCSUM:
- return ethtool_set_tx_csum(dev, useraddr);
- case ETHTOOL_GSG:
- return ethtool_get_sg(dev, useraddr);
- case ETHTOOL_SSG:
- return ethtool_set_sg(dev, useraddr);
- case ETHTOOL_GTSO:
- return ethtool_get_tso(dev, useraddr);
- case ETHTOOL_STSO:
- return ethtool_set_tso(dev, useraddr);
- case ETHTOOL_TEST:
- return ethtool_self_test(dev, useraddr);
- case ETHTOOL_GSTRINGS:
- return ethtool_get_strings(dev, useraddr);
- case ETHTOOL_PHYS_ID:
- return ethtool_phys_id(dev, useraddr);
- case ETHTOOL_GSTATS:
- return ethtool_get_stats(dev, useraddr);
- default:
- return -EOPNOTSUPP;
- }
-
- return -EOPNOTSUPP;
-}
-#endif //ETHTOOL_OPS_COMPAT
-
-static int rtl8169_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- struct mii_ioctl_data *data = if_mii(ifr);
- unsigned long flags;
-
- if (!netif_running(dev))
- return -ENODEV;
-
- switch (cmd) {
- case SIOCGMIIPHY:
- data->phy_id = 32; /* Internal PHY */
- return 0;
-
- case SIOCGMIIREG:
- spin_lock_irqsave(&tp->lock, flags);
- mdio_write(tp, 0x1f, 0x0000);
- data->val_out = mdio_read(tp, data->reg_num & 0x1f);
- spin_unlock_irqrestore(&tp->lock, flags);
- return 0;
-
- case SIOCSMIIREG:
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
- spin_lock_irqsave(&tp->lock, flags);
- mdio_write(tp, 0x1f, 0x0000);
- mdio_write(tp, data->reg_num & 0x1f, data->val_in);
- spin_unlock_irqrestore(&tp->lock, flags);
- return 0;
-#ifdef ETHTOOL_OPS_COMPAT
- case SIOCETHTOOL:
- return ethtool_ioctl(ifr);
-#endif
- default:
- return -EOPNOTSUPP;
- }
-}
-
-static void rtl8169_phy_power_up(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
-
- mdio_write(tp, 0x1F, 0x0000);
- mdio_write(tp, 0x0E, 0x0000);
- mdio_write(tp, MII_BMCR, BMCR_ANENABLE);
-}
-
-static void rtl8169_phy_power_down(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
-
- mdio_write(tp, 0x1F, 0x0000);
- mdio_write(tp, MII_BMCR, BMCR_PDOWN | BMCR_ANENABLE);
-}
-
-static void
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
-rtl8169_esd_timer(unsigned long __opaque)
-#else
-rtl8169_esd_timer(struct timer_list *t)
-#endif
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
- struct net_device *dev = (struct net_device *)__opaque;
- struct rtl8169_private *tp = netdev_priv(dev);
- struct timer_list *timer = &tp->esd_timer;
-#else
- struct rtl8169_private *tp = from_timer(tp, t, esd_timer);
- struct net_device *dev = tp->dev;
- struct timer_list *timer = t;
-#endif
- struct pci_dev *pdev = tp->pci_dev;
- unsigned long timeout = RTL8169_ESD_TIMEOUT;
- u8 cmd;
- u8 cls;
- u16 io_base_l;
- u16 mem_base_l;
- u16 mem_base_h;
- u8 ilr;
- u16 resv_0x20_l;
- u16 resv_0x20_h;
- u16 resv_0x24_l;
- u16 resv_0x24_h;
- unsigned long flags;
-
- spin_lock_irqsave(&tp->lock, flags);
-
- tp->esd_flag = 0;
-
- pci_read_config_byte(pdev, PCI_COMMAND, &cmd);
- if (cmd != tp->pci_cfg_space.cmd) {
- printk(KERN_ERR "%s: cmd = 0x%02x, should be 0x%02x \n.", dev->name, cmd, tp->pci_cfg_space.cmd);
- pci_write_config_byte(pdev, PCI_COMMAND, tp->pci_cfg_space.cmd);
- tp->esd_flag |= BIT_0;
-
- pci_read_config_byte(pdev, PCI_COMMAND, &cmd);
- if (cmd == 0xff) {
- netif_err(tp, drv, dev, "pci link is down \n");
- goto out_unlock;
- }
- }
-
- pci_read_config_word(pdev, PCI_BASE_ADDRESS_0, &io_base_l);
- if (io_base_l != tp->pci_cfg_space.io_base_l) {
- printk(KERN_ERR "%s: io_base_l = 0x%04x, should be 0x%04x \n.", dev->name, io_base_l, tp->pci_cfg_space.io_base_l);
- pci_write_config_word(pdev, PCI_BASE_ADDRESS_0, tp->pci_cfg_space.io_base_l);
- tp->esd_flag |= BIT_1;
- }
-
- pci_read_config_word(pdev, PCI_BASE_ADDRESS_2, &mem_base_l);
- if (mem_base_l != tp->pci_cfg_space.mem_base_l) {
- printk(KERN_ERR "%s: mem_base_l = 0x%04x, should be 0x%04x \n.", dev->name, mem_base_l, tp->pci_cfg_space.mem_base_l);
- pci_write_config_word(pdev, PCI_BASE_ADDRESS_2, tp->pci_cfg_space.mem_base_l);
- tp->esd_flag |= BIT_2;
- }
-
- pci_read_config_word(pdev, PCI_BASE_ADDRESS_2 + 2, &mem_base_h);
- if (mem_base_h!= tp->pci_cfg_space.mem_base_h) {
- printk(KERN_ERR "%s: mem_base_h = 0x%04x, should be 0x%04x \n.", dev->name, mem_base_h, tp->pci_cfg_space.mem_base_h);
- pci_write_config_word(pdev, PCI_BASE_ADDRESS_2 + 2, tp->pci_cfg_space.mem_base_h);
- tp->esd_flag |= BIT_3;
- }
-
- pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cls);
- if (cls != tp->pci_cfg_space.cls) {
- printk(KERN_ERR "%s: cls = 0x%02x, should be 0x%02x \n.", dev->name, cls, tp->pci_cfg_space.cls);
- pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, tp->pci_cfg_space.cls);
- tp->esd_flag |= BIT_4;
- }
-
- pci_read_config_word(pdev, PCI_BASE_ADDRESS_4, &resv_0x20_l);
- if (resv_0x20_l != tp->pci_cfg_space.resv_0x20_l) {
- printk(KERN_ERR "%s: resv_0x20_l = 0x%04x, should be 0x%04x \n.", dev->name, resv_0x20_l, tp->pci_cfg_space.resv_0x20_l);
- pci_write_config_word(pdev, PCI_BASE_ADDRESS_4, tp->pci_cfg_space.resv_0x20_l);
- tp->esd_flag |= BIT_6;
- }
-
- pci_read_config_word(pdev, PCI_BASE_ADDRESS_4 + 2, &resv_0x20_h);
- if (resv_0x20_h != tp->pci_cfg_space.resv_0x20_h) {
- printk(KERN_ERR "%s: resv_0x20_h = 0x%04x, should be 0x%04x \n.", dev->name, resv_0x20_h, tp->pci_cfg_space.resv_0x20_h);
- pci_write_config_word(pdev, PCI_BASE_ADDRESS_4 + 2, tp->pci_cfg_space.resv_0x20_h);
- tp->esd_flag |= BIT_7;
- }
-
- pci_read_config_word(pdev, PCI_BASE_ADDRESS_5, &resv_0x24_l);
- if (resv_0x24_l != tp->pci_cfg_space.resv_0x24_l) {
- printk(KERN_ERR "%s: resv_0x24_l = 0x%04x, should be 0x%04x \n.", dev->name, resv_0x24_l, tp->pci_cfg_space.resv_0x24_l);
- pci_write_config_word(pdev, PCI_BASE_ADDRESS_5, tp->pci_cfg_space.resv_0x24_l);
- tp->esd_flag |= BIT_8;
- }
-
- pci_read_config_word(pdev, PCI_BASE_ADDRESS_5 + 2, &resv_0x24_h);
- if (resv_0x24_h != tp->pci_cfg_space.resv_0x24_h) {
- printk(KERN_ERR "%s: resv_0x24_h = 0x%04x, should be 0x%04x \n.", dev->name, resv_0x24_h, tp->pci_cfg_space.resv_0x24_h);
- pci_write_config_word(pdev, PCI_BASE_ADDRESS_5 + 2, tp->pci_cfg_space.resv_0x24_h);
- tp->esd_flag |= BIT_9;
- }
-
- pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &ilr);
- if (ilr != tp->pci_cfg_space.ilr) {
- printk(KERN_ERR "%s: ilr = 0x%02x, should be 0x%02x \n.", dev->name, ilr, tp->pci_cfg_space.ilr);
- pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, tp->pci_cfg_space.ilr);
- tp->esd_flag |= BIT_10;
- }
-
- if (tp->esd_flag != 0) {
- printk(KERN_ERR "%s: esd_flag = 0x%04x\n.\n", dev->name, tp->esd_flag);
- netif_stop_queue(dev);
- netif_carrier_off(dev);
- rtl8169_hw_reset(dev);
- rtl8169_tx_clear(tp);
- rtl8169_rx_clear(tp);
- rtl8169_init_ring(dev);
- rtl8169_powerup_pll(dev);
- rtl8169_hw_phy_config(dev);
- rtl8169_hw_start(dev);
- rtl8169_set_speed(dev, tp->autoneg, tp->speed, tp->duplex, tp->advertising);
- tp->esd_flag = 0;
- }
-
-out_unlock:
- spin_unlock_irqrestore(&tp->lock, flags);
-
- mod_timer(timer, jiffies + timeout);
-}
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
-static const struct net_device_ops rtl8169_netdev_ops = {
- .ndo_open = rtl8169_open,
- .ndo_stop = rtl8169_close,
- .ndo_get_stats = rtl8169_get_stats,
- .ndo_start_xmit = rtl8169_start_xmit,
- .ndo_tx_timeout = rtl8169_tx_timeout,
- .ndo_change_mtu = rtl8169_change_mtu,
- .ndo_set_mac_address = rtl8169_set_mac_address,
-#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0)
- .ndo_do_ioctl = rtl8169_ioctl,
-#else
- .ndo_eth_ioctl = rtl8169_ioctl,
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0)
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,1,0)
- .ndo_set_multicast_list = rtl8169_set_rx_mode,
-#else
- .ndo_set_rx_mode = rtl8169_set_rx_mode,
-#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
-#ifdef CONFIG_R8169_VLAN
- .ndo_vlan_rx_register = rtl8169_vlan_rx_register,
-#endif
-#else
- .ndo_fix_features = rtl8169_fix_features,
- .ndo_set_features = rtl8169_set_features,
-#endif
-#ifdef CONFIG_NET_POLL_CONTROLLER
- .ndo_poll_controller = rtl8169_netpoll,
-#endif
-};
-#endif //HAVE_NET_DEVICE_OPS
-
-static int __devinit
-rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
-{
- const unsigned int region = rtl_cfg_info[ent->driver_data].region;
- struct rtl8169_private *tp;
- struct net_device *dev;
- void __iomem *ioaddr;
- unsigned int pm_cap;
- int i, rc;
- static int board_idx = -1;
-
- board_idx++;
-
- if (netif_msg_drv(&debug)) {
- printk(KERN_INFO "%s Gigabit Ethernet driver %s loaded\n",
- MODULENAME, RTL8169_VERSION);
- }
-
- dev = alloc_etherdev(sizeof (*tp));
- if (!dev) {
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
- if (netif_msg_drv(&debug))
- dev_err(&pdev->dev, "unable to alloc new ethernet\n");
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
- rc = -ENOMEM;
- goto out;
- }
-
- SET_MODULE_OWNER(dev);
- SET_NETDEV_DEV(dev, &pdev->dev);
- tp = netdev_priv(dev);
- tp->dev = dev;
- tp->msg_enable = netif_msg_init(debug.msg_enable, R8169_MSG_DEFAULT);
-
- /* enable device (incl. PCI PM wakeup and hotplug setup) */
- rc = pci_enable_device(pdev);
- if (rc < 0) {
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
- if (netif_msg_probe(tp))
- dev_err(&pdev->dev, "enable failure\n");
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
- goto err_out_free_dev_1;
- }
-
- if (pci_set_mwi(pdev) < 0) {
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
- if (netif_msg_drv(&debug))
- dev_info(&pdev->dev, "Mem-Wr-Inval unavailable.\n");
-#endif //LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
- }
-
- /* save power state before pci_enable_device overwrites it */
- pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM);
- if (pm_cap) {
- u16 pwr_command, acpi_idle_state;
-
- pci_read_config_word(pdev, pm_cap + PCI_PM_CTRL, &pwr_command);
- acpi_idle_state = pwr_command & PCI_PM_CTRL_STATE_MASK;
- } else {
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
- if (netif_msg_probe(tp)) {
- dev_err(&pdev->dev, "PowerManagement capability not found.\n");
- }
-#else
- printk("PowerManagement capability not found.\n");
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
- }
-
- /* make sure PCI base addr 1 is MMIO */
- if (!(pci_resource_flags(pdev, region) & IORESOURCE_MEM)) {
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
- if (netif_msg_probe(tp)) {
- dev_err(&pdev->dev, "region #%d not an MMIO resource, aborting\n", region);
- }
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
- rc = -ENODEV;
- goto err_out_mwi_3;
- }
-
- /* check for weird/broken PCI region reporting */
- if (pci_resource_len(pdev, region) < R8169_REGS_SIZE) {
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
- if (netif_msg_probe(tp)) {
- dev_err(&pdev->dev, "Invalid PCI region size(s), aborting\n");
- }
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
- rc = -ENODEV;
- goto err_out_mwi_3;
- }
-
- rc = pci_request_regions(pdev, MODULENAME);
- if (rc < 0) {
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
- if (netif_msg_probe(tp))
- dev_err(&pdev->dev, "could not request regions.\n");
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
- goto err_out_mwi_3;
- }
-
- tp->cp_cmd = PCIMulRW | RxChkSum;
-
- if ((sizeof(dma_addr_t) > 4) &&
- use_dac &&
- !dma_set_mask(&pdev->dev,DMA_BIT_MASK(64)) &&
- !dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64))) {
- tp->cp_cmd |= PCIDAC;
- dev->features |= NETIF_F_HIGHDMA;
- } else {
- rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
- if (rc < 0) {
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
- if (netif_msg_probe(tp)) {
- dev_err(&pdev->dev, "DMA configuration failed.\n");
- }
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
- goto err_out_free_res_4;
- }
- }
-
- /* ioremap MMIO region */
- ioaddr = ioremap(pci_resource_start(pdev, region), R8169_REGS_SIZE);
- if (!ioaddr) {
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
- if (netif_msg_probe(tp))
- dev_err(&pdev->dev, "cannot remap MMIO, aborting\n");
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
- rc = -EIO;
- goto err_out_free_res_4;
- }
-
- /* Identify chip attached to board */
- rtl8169_get_mac_version(tp, ioaddr);
-
- rtl8169_print_mac_version(tp);
-
- for (i = ARRAY_SIZE(rtl_chip_info) - 1; i >= 0; i--) {
- if (tp->mcfg == rtl_chip_info[i].mcfg)
- break;
- }
- if (i < 0) {
- /* Unknown chip: assume array element #0, original RTL-8169 */
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
- if (netif_msg_probe(tp)) {
- dev_printk(KERN_DEBUG, &pdev->dev, "unknown chip version, assuming %s\n", rtl_chip_info[0].name);
- }
-#else
- printk("Realtek unknown chip version, assuming %s\n", rtl_chip_info[0].name);
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
- i++;
- }
- tp->chipset = i;
-
- RTL_W8(Cfg9346, Cfg9346_Unlock);
- RTL_W8(Config1, RTL_R8(Config1) | PMEnable);
- RTL_W8(Config5, RTL_R8(Config5) & PMEStatus);
- RTL_W8(Cfg9346, Cfg9346_Lock);
-
- tp->set_speed = rtl8169_set_speed_xmii;
- tp->get_settings = rtl8169_gset_xmii;
- tp->phy_reset_enable = rtl8169_xmii_reset_enable;
- tp->phy_reset_pending = rtl8169_xmii_reset_pending;
- tp->link_ok = rtl8169_xmii_link_ok;
-
- RTL_W8(Cfg9346, Cfg9346_Unlock);
- RTL_W8(Cfg9346, Cfg9346_Lock);
-
- RTL_NET_DEVICE_OPS(rtl8169_netdev_ops);
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22)
- SET_ETHTOOL_OPS(dev, &rtl8169_ethtool_ops);
-#endif
-
- dev->watchdog_timeo = RTL8169_TX_TIMEOUT;
- dev->irq = pdev->irq;
- dev->base_addr = (unsigned long) ioaddr;
-
-#ifdef CONFIG_R8169_NAPI
- RTL_NAPI_CONFIG(dev, tp, rtl8169_poll, R8169_NAPI_WEIGHT);
-#endif
-
-#ifdef CONFIG_R8169_VLAN
- dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
- dev->vlan_rx_kill_vid = rtl8169_vlan_rx_kill_vid;
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
-#endif //CONFIG_R8169_VLAN
-
- /* 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.
- * Therefore disable both features by default, but allow users to
- * enable them. Use at own risk!
- */
- dev->features |= NETIF_F_IP_CSUM;
- tp->cp_cmd |= RTL_R16(CPlusCmd);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
- tp->cp_cmd |= RxChkSum;
-#else
- dev->features |= NETIF_F_RXCSUM;
- dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
- NETIF_F_RXCSUM | NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
- dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
- NETIF_F_HIGHDMA;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0)
- dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
-#endif //LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0)
- dev->hw_features |= NETIF_F_RXALL;
- dev->hw_features |= NETIF_F_RXFCS;
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,19,0)
- netif_set_tso_max_size(dev, LSO_64K);
- netif_set_tso_max_segs(dev, NIC_MAX_PHYS_BUF_COUNT_LSO2);
-#else //LINUX_VERSION_CODE >= KERNEL_VERSION(5,19,0)
- netif_set_gso_max_size(dev, LSO_32K);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0)
- dev->gso_max_segs = NIC_MAX_PHYS_BUF_COUNT_LSO_64K;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
- dev->gso_min_segs = NIC_MIN_PHYS_BUF_COUNT;
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
-#endif //LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0)
-#endif //LINUX_VERSION_CODE >= KERNEL_VERSION(5,19,0)
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
-
- tp->max_jumbo_frame_size = Jumbo_Frame_7k;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)
- /* MTU range: 60 - hw-specific max */
- dev->min_mtu = ETH_MIN_MTU;
- dev->max_mtu = tp->max_jumbo_frame_size;
-#endif //LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)
-
- tp->intr_mask = rtl8169_intr_mask;
- tp->pci_dev = pdev;
- tp->mmio_addr = ioaddr;
- tp->align = rtl_cfg_info[ent->driver_data].align;
-
- spin_lock_init(&tp->lock);
-
- rtl8169_init_software_variable(dev);
-
- rtl8169_hw_init(dev);
-
- rtl8169_hw_reset(dev);
-
- rtl8169_get_mac_address(dev);
-
- rtl8169_get_phy_version(tp);
-
- rtl8169_print_phy_version(tp);
-
- pci_set_drvdata(pdev, dev);
-
- rc = register_netdev(dev);
- if (rc < 0)
- goto err_out_unmap_5;
-
- printk(KERN_INFO "%s: This product is covered by one or more of the following patents: US6,570,884, US6,115,776, and US6,327,625.\n", MODULENAME);
-
- if (netif_msg_probe(tp)) {
- printk(KERN_INFO "%s: %s at 0x%lx, "
- "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
- "IRQ %d\n",
- dev->name,
- rtl_chip_info[tp->chipset].name,
- dev->base_addr,
- dev->dev_addr[0], dev->dev_addr[1],
- dev->dev_addr[2], dev->dev_addr[3],
- dev->dev_addr[4], dev->dev_addr[5], dev->irq);
- }
-
- rtl8169_link_option((u8*)&autoneg_mode, (u32*)&speed_mode, (u8*)&duplex_mode, (u32*)&advertising_mode);
-
- tp->autoneg = autoneg_mode;
- tp->speed = speed_mode;
- tp->duplex = duplex_mode;
- tp->advertising = advertising_mode;
- tp->fcpause = rtl8169_fc_full;
-
- printk("%s", GPL_CLAIM);
-
-out:
- return rc;
-
-err_out_unmap_5:
-#ifdef CONFIG_R8169_NAPI
- RTL_NAPI_DEL(tp);
-#endif
- iounmap(ioaddr);
-err_out_free_res_4:
- pci_release_regions(pdev);
-err_out_mwi_3:
- pci_clear_mwi(pdev);
- pci_disable_device(pdev);
-err_out_free_dev_1:
- free_netdev(dev);
- goto out;
-}
-
-static void __devexit
-rtl8169_remove_one(struct pci_dev *pdev)
-{
- struct net_device *dev = pci_get_drvdata(pdev);
- struct rtl8169_private *tp = netdev_priv(dev);
-
- assert(dev != NULL);
- assert(tp != NULL);
-
-#ifdef CONFIG_R8169_NAPI
- RTL_NAPI_DEL(tp);
-#endif
-
- unregister_netdev(dev);
- rtl8169_release_board(pdev, dev, tp->mmio_addr);
- pci_set_drvdata(pdev, NULL);
-}
-
-static void rtl8169_set_rxbufsize(struct rtl8169_private *tp,
- struct net_device *dev)
-{
- unsigned int mtu = dev->mtu;
-
- tp->rx_buf_sz = (mtu > ETH_DATA_LEN) ? mtu + ETH_HLEN + 8 : RX_BUF_SIZE;
-}
-
-static int rtl8169_open(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- struct pci_dev *pdev = tp->pci_dev;
- unsigned long flags;
- int retval;
-
- rtl8169_set_rxbufsize(tp, dev);
-
- retval = -ENOMEM;
-
- /*
- * 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 err_free_all_allocated_mem;
-
- tp->RxDescArray = dma_alloc_coherent(&pdev->dev, R8169_RX_RING_BYTES,
- &tp->RxPhyAddr, GFP_KERNEL);
- if (!tp->RxDescArray)
- goto err_free_all_allocated_mem;
-
- retval = rtl8169_init_ring(dev);
- if (retval < 0)
- goto err_free_all_allocated_mem;
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
- INIT_WORK(&tp->task, rtl8169_reset_task, dev);
-#else
- INIT_DELAYED_WORK(&tp->task, rtl8169_reset_task);
-#endif
-
- pci_set_master(pdev);
-
-#ifdef CONFIG_R8169_NAPI
- RTL_NAPI_ENABLE(dev, &tp->napi);
-#endif
- spin_lock_irqsave(&tp->lock, flags);
-
- rtl8169_hw_init(dev);
-
- rtl8169_hw_reset(dev);
-
- rtl8169_phy_power_up(dev);
-
- rtl8169_hw_phy_config(dev);
-
- rtl8169_hw_start(dev);
-
- if (tp->esd_flag == 0) {
- rtl8169_request_timer(dev);
- rtl8169_request_esd_timer(dev);
- }
-
- rtl8169_check_link_status(dev, tp, tp->mmio_addr);
-
- rtl8169_set_speed(dev, tp->autoneg, tp->speed, tp->duplex, tp->advertising);
-
- spin_unlock_irqrestore(&tp->lock, flags);
-
- retval = request_irq(dev->irq, rtl8169_interrupt, SA_SHIRQ, dev->name, dev);
-
- if (retval < 0)
- goto err_free_all_allocated_mem;
-
-out:
- return retval;
-
-err_free_all_allocated_mem:
- if (tp->RxDescArray != NULL) {
- dma_free_coherent(&pdev->dev, R8169_RX_RING_BYTES, tp->RxDescArray,
- tp->RxPhyAddr);
- tp->RxDescArray = NULL;
- }
-
- if (tp->TxDescArray != NULL) {
- dma_free_coherent(&pdev->dev, R8169_TX_RING_BYTES, tp->TxDescArray,
- tp->TxPhyAddr);
- tp->TxDescArray = NULL;
- }
-
- goto out;
-}
-
-static void rtl8169_nic_reset(void __iomem *ioaddr)
-{
- int i;
-
- RTL_W32(RxConfig, (RX_DMA_BURST << RxCfgDMAShift));
-
- mdelay(1);
-
- /* Soft reset the chip. */
- RTL_W8(ChipCmd, CmdReset);
-
- /* Check that the chip has finished the reset. */
- for (i = 1000; i > 0; i--) {
- if ((RTL_R8(ChipCmd) & CmdReset) == 0)
- break;
-
- udelay(100);
- }
-}
-
-static void
-rtl8169_hw_clear_timer_int(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
-
- RTL_W32(TimeInt0, 0x0000);
-}
-
-static void rtl8169_hw_reset(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
-
- /* Disable interrupts */
- rtl8169_irq_mask_and_ack(ioaddr);
-
- rtl8169_hw_clear_timer_int(dev);
-
- /* Reset the chipset */
- rtl8169_nic_reset(ioaddr);
-
- /* Disable interrupts */
- /* RTL8169 may enable interrupt after reset */
- rtl8169_irq_mask_and_ack(ioaddr);
-}
-
-static void
-rtl8169_hw_set_rx_packet_filter(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- u32 mc_filter[2]; /* Multicast hash filter */
- int rx_mode;
- u32 tmp = 0;
-
- if (dev->flags & IFF_PROMISC) {
- /* Unconditionally log net taps. */
-
- if (netif_msg_link(tp)) {
- printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n",
- dev->name);
- }
- 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 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
- struct dev_mc_list *mclist;
- int i;
-
- rx_mode = AcceptBroadcast | AcceptMyPhys;
- mc_filter[1] = mc_filter[0] = 0;
- for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
- i++, mclist = mclist->next) {
- int bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26;
- mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31);
- rx_mode |= AcceptMulticast;
- }
-#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;
- }
-#endif
- }
-
- if (dev->features & NETIF_F_RXALL)
- rx_mode |= (AcceptErr | AcceptRunt);
-
- tmp = rtl8169_rx_config | rx_mode |
- (RTL_R32(RxConfig) & rtl_chip_info[tp->chipset].RxConfigMask);
-
- RTL_W32(RxConfig, tmp);
- RTL_W32(MAR0 + 0, mc_filter[0]);
- RTL_W32(MAR0 + 4, mc_filter[1]);
-}
-
-static void
-rtl8169_set_rx_mode(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- unsigned long flags;
-
- spin_lock_irqsave(&tp->lock, flags);
-
- rtl8169_hw_set_rx_packet_filter(dev);
-
- spin_unlock_irqrestore(&tp->lock, flags);
-}
-
-/**
- * rtl8169_get_stats - Get rtl8169 read/write statistics
- * @dev: The Ethernet Device to get statistics for
- *
- * Get TX/RX statistics for rtl8169
- */
-static struct net_device_stats *rtl8169_get_stats(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- unsigned long flags;
-
- if (netif_running(dev)) {
- spin_lock_irqsave(&tp->lock, flags);
- tp->stats.rx_missed_errors += RTL_R32(RxMissed);
- RTL_W32(RxMissed, 0);
- spin_unlock_irqrestore(&tp->lock, flags);
- }
-
- return &RTLDEV->stats;
-}
-
-static void rtl8169_hw_start(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- struct pci_dev *pdev = tp->pci_dev;
- u8 options1, options2;
-
- RTL_W32(RxConfig, (RX_DMA_BURST << RxCfgDMAShift));
-
- rtl8169_hw_reset(dev);
-
- dprintk("Set PCI Latency=0x40\n");
- pci_write_config_byte(tp->pci_dev, PCI_LATENCY_TIMER, 0x40);
-
- RTL_W8(Cfg9346, Cfg9346_Unlock);
-
- RTL_W8(Reserved1, Reserved1_data);
-
- tp->cp_cmd |= PCIMulRW;
- RTL_W16(CPlusCmd, tp->cp_cmd);
- pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x08);
-
- if (tp->mcfg == CFG_METHOD_2)
- tp->cp_cmd |= EnAnaPLL;
- else
- tp->cp_cmd &= ~EnAnaPLL;
-
- pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x40);
-
- /*
- * Undocumented corner. Supposedly:
- */
- RTL_W16(IntrMitigate, 0x0000);
-
- /*
- * 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(TxDescStartAddrLow, ((u64) tp->TxPhyAddr & DMA_BIT_MASK(32)));
- RTL_W32(RxDescAddrLow, ((u64) tp->RxPhyAddr & DMA_BIT_MASK(32)));
- RTL_W32(TxDescStartAddrHigh, ((u64) tp->TxPhyAddr >> 32));
- RTL_W32(RxDescAddrHigh, ((u64) tp->RxPhyAddr >> 32));
-
- RTL_W32(RxMissed, 0);
-
- /* no early-rx interrupts */
- RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xF000);
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
- RTL_W16(CPlusCmd, tp->cp_cmd);
-#else
- rtl8169_hw_set_features(dev, dev->features);
-#endif
-
- RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb);
-
- RTL_W16(RxMaxSize, tp->rx_buf_sz);
-
- /* Set Rx packet filter */
- rtl8169_hw_set_rx_packet_filter(dev);
-
- /* Set DMA burst size and Interframe Gap Time */
- rtl8169_set_tx_config(dev);
-
- RTL_W8(Cfg9346, Cfg9346_Lock);
-
- rtl8169_hw_clear_timer_int(dev);
-
- /* Clear the interrupt status. */
- RTL_W16(IntrStatus, 0xFFFF);
-
- /* Enable all known interrupts by setting the interrupt mask. */
- RTL_W16(IntrMask, rtl8169_intr_mask);
-
- if (tp->link_ok(dev))
- netif_wake_queue(dev);
- else
- netif_stop_queue(dev);
-
- if (!tp->pci_cfg_is_read) {
- pci_read_config_byte(pdev, PCI_COMMAND, &tp->pci_cfg_space.cmd);
- pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &tp->pci_cfg_space.cls);
- pci_read_config_word(pdev, PCI_BASE_ADDRESS_0, &tp->pci_cfg_space.io_base_l);
- pci_read_config_word(pdev, PCI_BASE_ADDRESS_0 + 2, &tp->pci_cfg_space.io_base_h);
- pci_read_config_word(pdev, PCI_BASE_ADDRESS_2, &tp->pci_cfg_space.mem_base_l);
- pci_read_config_word(pdev, PCI_BASE_ADDRESS_2 + 2, &tp->pci_cfg_space.mem_base_h);
- pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &tp->pci_cfg_space.ilr);
- pci_read_config_word(pdev, PCI_BASE_ADDRESS_4, &tp->pci_cfg_space.resv_0x20_l);
- pci_read_config_word(pdev, PCI_BASE_ADDRESS_4 + 2, &tp->pci_cfg_space.resv_0x20_h);
- pci_read_config_word(pdev, PCI_BASE_ADDRESS_5, &tp->pci_cfg_space.resv_0x24_l);
- pci_read_config_word(pdev, PCI_BASE_ADDRESS_5 + 2, &tp->pci_cfg_space.resv_0x24_h);
-
- tp->pci_cfg_is_read = 1;
- }
-
- options1 = RTL_R8(Config3);
- options2 = RTL_R8(Config5);
-
- if ((options1 & LinkUp) || (options1 & MagicPacket) || (options2 & UWF) || (options2 & BWF) || (options2 & MWF))
- tp->wol_enabled = WOL_ENABLED;
- else
- tp->wol_enabled = WOL_DISABLED;
-
- device_set_wakeup_enable(&tp->pci_dev->dev, tp->wol_enabled);
-
- udelay(10);
-}
-
-static int rtl8169_change_mtu(struct net_device *dev, int new_mtu)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- int ret = 0;
- unsigned long flags;
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0)
- if (new_mtu < ETH_MIN_MTU)
- return -EINVAL;
- else if (new_mtu > tp->max_jumbo_frame_size)
- new_mtu = tp->max_jumbo_frame_size;
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0)
-
- spin_lock_irqsave(&tp->lock, flags);
- dev->mtu = new_mtu;
- spin_unlock_irqrestore(&tp->lock, flags);
-
- if (!netif_running(dev))
- goto out;
-
- rtl8169_down(dev);
-
- spin_lock_irqsave(&tp->lock, flags);
-
- rtl8169_set_rxbufsize(tp, dev);
-
- ret = rtl8169_init_ring(dev);
-
- if (ret < 0) {
- spin_unlock_irqrestore(&tp->lock, flags);
- goto err_out;
- }
-
-#ifdef CONFIG_R8169_NAPI
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
- RTL_NAPI_ENABLE(dev, &tp->napi);
-#endif
-#endif//CONFIG_R8169_NAPI
-
- rtl8169_hw_start(dev);
- spin_unlock_irqrestore(&tp->lock, flags);
- rtl8169_set_speed(dev, tp->autoneg, tp->speed, tp->duplex, tp->advertising);
-
- mod_timer(&tp->link_timer, jiffies + RTL8169_PHY_TIMEOUT);
- mod_timer(&tp->esd_timer, jiffies + RTL8169_ESD_TIMEOUT);
-out:
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
- netdev_update_features(dev);
-#endif
-
-err_out:
- return ret;
-}
-
-static inline void rtl8169_make_unusable_by_asic(struct RxDesc *desc)
-{
- desc->addr = 0x0badbadbadbadbadull;
- desc->opts1 &= ~cpu_to_le32(DescOwn | RsvdMask);
-}
-
-static void rtl8169_free_rx_skb(struct rtl8169_private *tp,
- struct sk_buff **sk_buff, struct RxDesc *desc)
-{
- struct pci_dev *pdev = tp->pci_dev;
-
- dma_unmap_single(&pdev->dev, le64_to_cpu(desc->addr), tp->rx_buf_sz,
- DMA_FROM_DEVICE);
- dev_kfree_skb(*sk_buff);
- *sk_buff = NULL;
- rtl8169_make_unusable_by_asic(desc);
-}
-
-static inline void rtl8169_mark_to_asic(struct RxDesc *desc, u32 rx_buf_sz)
-{
- u32 eor = le32_to_cpu(desc->opts1) & RingEnd;
-
- desc->opts1 = cpu_to_le32(DescOwn | eor | rx_buf_sz);
-}
-
-static inline void rtl8169_map_to_asic(struct RxDesc *desc, dma_addr_t mapping,
- u32 rx_buf_sz)
-{
- desc->addr = cpu_to_le64(mapping);
- wmb();
- rtl8169_mark_to_asic(desc, rx_buf_sz);
-}
-
-static int rtl8169_alloc_rx_skb(struct rtl8169_private *tp,
- struct sk_buff **sk_buff,
- struct RxDesc *desc,
- int rx_buf_sz,
- unsigned int align,
- u8 in_intr)
-{
- struct sk_buff *skb;
- dma_addr_t mapping;
- int ret = 0;
-
- if (in_intr)
- skb = RTL_ALLOC_SKB_INTR(tp, rx_buf_sz + align);
- else
- skb = dev_alloc_skb(rx_buf_sz + align);
-
- if (unlikely(!skb))
- goto err_out;
-
- skb_reserve(skb, align - ((align - 1) & (uintptr_t)skb->data));
-
- mapping = dma_map_single(&tp->pci_dev->dev, skb->data, rx_buf_sz,
- DMA_FROM_DEVICE);
- if (unlikely(dma_mapping_error(&tp->pci_dev->dev, mapping))) {
- if (unlikely(net_ratelimit()))
- netif_err(tp, drv, tp->dev, "Failed to map RX DMA!\n");
- goto err_out;
- }
-
- *sk_buff = skb;
- rtl8169_map_to_asic(desc, mapping, rx_buf_sz);
-
-out:
- return ret;
-
-err_out:
- if (skb)
- dev_kfree_skb(skb);
- ret = -ENOMEM;
- rtl8169_make_unusable_by_asic(desc);
- goto out;
-}
-
-static void rtl8169_rx_clear(struct rtl8169_private *tp)
-{
- int i;
-
- for (i = 0; i < NUM_RX_DESC; i++) {
- if (tp->Rx_skbuff[i]) {
- rtl8169_free_rx_skb(tp, tp->Rx_skbuff + i,
- tp->RxDescArray + i);
- }
- }
-}
-
-static u32 rtl8169_rx_fill(struct rtl8169_private *tp, struct net_device *dev,
- u32 start, u32 end, u8 in_intr)
-{
- u32 cur;
-
- for (cur = start; end - cur > 0; cur++) {
- int ret, i = cur % NUM_RX_DESC;
-
- if (tp->Rx_skbuff[i])
- continue;
-
- ret = rtl8169_alloc_rx_skb(tp, tp->Rx_skbuff + i,
- tp->RxDescArray + i,
- tp->rx_buf_sz, tp->align,
- in_intr);
- if (ret < 0)
- break;
- }
- return cur - start;
-}
-
-static inline void rtl8169_mark_as_last_descriptor(struct RxDesc *desc)
-{
- desc->opts1 |= cpu_to_le32(RingEnd);
-}
-
-static void rtl8169_init_ring_indexes(struct rtl8169_private *tp)
-{
- tp->dirty_tx = tp->dirty_rx = tp->cur_tx = tp->cur_rx = 0;
-}
-
-static void
-rtl8169_tx_desc_init(struct rtl8169_private *tp)
-{
- int i = 0;
-
- memset(tp->TxDescArray, 0x0, R8169_TX_RING_BYTES);
-
- for (i = 0; i < NUM_TX_DESC; i++) {
- if (i == (NUM_TX_DESC - 1))
- tp->TxDescArray[i].opts1 = cpu_to_le32(RingEnd);
- }
-}
-
-static void
-rtl8169_rx_desc_init(struct rtl8169_private *tp)
-{
- memset(tp->RxDescArray, 0x0, R8169_RX_RING_BYTES);
-}
-
-static int rtl8169_init_ring(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
-
- rtl8169_init_ring_indexes(tp);
-
- memset(tp->tx_skb, 0x0, NUM_TX_DESC * sizeof(struct ring_info));
- memset(tp->Rx_skbuff, 0x0, NUM_RX_DESC * sizeof(struct sk_buff *));
-
- rtl8169_tx_desc_init(tp);
- rtl8169_rx_desc_init(tp);
-
- if (rtl8169_rx_fill(tp, dev, 0, NUM_RX_DESC, 0) != NUM_RX_DESC)
- goto err_out;
-
- rtl8169_mark_as_last_descriptor(tp->RxDescArray + NUM_RX_DESC - 1);
-
- return 0;
-
-err_out:
- rtl8169_rx_clear(tp);
- return -ENOMEM;
-}
-
-static void rtl8169_unmap_tx_skb(struct pci_dev *pdev, struct ring_info *tx_skb,
- struct TxDesc *desc)
-{
- unsigned int len = tx_skb->len;
-
- dma_unmap_single(&pdev->dev, le64_to_cpu(desc->addr), len, DMA_TO_DEVICE);
-
- desc->opts1 = cpu_to_le32(RTK_MAGIC_DEBUG_VALUE);
- desc->opts2 = 0x00;
- desc->addr = 0x00;
- tx_skb->len = 0;
-}
-
-static void rtl8169_tx_clear_range(struct rtl8169_private *tp, u32 start,
- unsigned int n)
-{
- unsigned int i;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
- struct net_device *dev = tp->dev;
-#endif
-
- 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->pci_dev, tx_skb,
- tp->TxDescArray + entry);
- if (skb) {
- RTLDEV->stats.tx_dropped++;
- dev_kfree_skb_any(skb);
- tx_skb->skb = NULL;
- }
- }
- }
-}
-
-static void
-rtl8169_tx_clear(struct rtl8169_private *tp)
-{
- rtl8169_tx_clear_range(tp, tp->dirty_tx, NUM_TX_DESC);
- tp->cur_tx = tp->dirty_tx = 0;
-}
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
-static void rtl8169_schedule_work(struct net_device *dev, void (*task)(void *))
-{
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
- struct rtl8169_private *tp = netdev_priv(dev);
-
- INIT_WORK(&tp->task, task, dev);
- schedule_delayed_work(&tp->task, 4);
-#endif //LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
-}
-
-#define rtl8169_cancel_schedule_work(a)
-
-#else
-static void rtl8169_schedule_work(struct net_device *dev, work_func_t task)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
-
- INIT_DELAYED_WORK(&tp->task, task);
- schedule_delayed_work(&tp->task, 4);
-}
-
-static void rtl8169_cancel_schedule_work(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- struct work_struct *work = &tp->task.work;
-
- if (!work->func) return;
-
- cancel_delayed_work_sync(&tp->task);
-}
-#endif
-
-static void rtl8169_wait_for_quiescence(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
-
- synchronize_irq(dev->irq);
-
- /* Wait for any pending NAPI task to complete */
-#ifdef CONFIG_R8169_NAPI
- RTL_NAPI_DISABLE(dev, &tp->napi);
-#endif
-
- rtl8169_irq_mask_and_ack(ioaddr);
-
-#ifdef CONFIG_R8169_NAPI
- RTL_NAPI_ENABLE(dev, &tp->napi);
-#endif
-}
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
-static void rtl8169_reinit_task(void *_data)
-#else
-static void rtl8169_reinit_task(struct work_struct *work)
-#endif
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
- struct net_device *dev = _data;
-#else
- struct rtl8169_private *tp =
- container_of(work, struct rtl8169_private, task.work);
- struct net_device *dev = tp->dev;
-#endif
- int ret;
-
- if (netif_running(dev)) {
- rtl8169_wait_for_quiescence(dev);
- rtl8169_close(dev);
- }
-
- ret = rtl8169_open(dev);
- if (unlikely(ret < 0)) {
- if (unlikely(net_ratelimit())) {
- struct rtl8169_private *tp = netdev_priv(dev);
-
- if (netif_msg_drv(tp)) {
- printk(PFX KERN_ERR
- "%s: reinit failure (status = %d)."
- " Rescheduling.\n", dev->name, ret);
- }
- }
- rtl8169_schedule_work(dev, rtl8169_reinit_task);
- }
-}
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
-static void rtl8169_reset_task(void *_data)
-{
- struct net_device *dev = _data;
- struct rtl8169_private *tp = netdev_priv(dev);
-#else
-static void rtl8169_reset_task(struct work_struct *work)
-{
- struct rtl8169_private *tp =
- container_of(work, struct rtl8169_private, task.work);
- struct net_device *dev = tp->dev;
-#endif
- u32 budget = ~(u32)0;
- unsigned long flags;
-
- if (!netif_running(dev))
- return;
-
- rtl8169_wait_for_quiescence(dev);
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
- rtl8169_rx_interrupt(dev, tp, tp->mmio_addr, &budget);
-#else
- rtl8169_rx_interrupt(dev, tp, tp->mmio_addr, budget);
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
-
- spin_lock_irqsave(&tp->lock, flags);
-
- rtl8169_tx_clear(tp);
-
- if (tp->dirty_rx == tp->cur_rx) {
- rtl8169_rx_clear(tp);
- rtl8169_init_ring(dev);
- rtl8169_hw_start(dev);
- netif_wake_queue(dev);
- rtl8169_set_speed(dev, tp->autoneg, tp->speed, tp->duplex, tp->advertising);
- spin_unlock_irqrestore(&tp->lock, flags);
- } else {
- spin_unlock_irqrestore(&tp->lock, flags);
- if (unlikely(net_ratelimit())) {
- struct rtl8169_private *tp = netdev_priv(dev);
-
- if (netif_msg_intr(tp)) {
- printk(PFX KERN_EMERG
- "%s: Rx buffers shortage\n", dev->name);
- }
- }
- rtl8169_schedule_work(dev, rtl8169_reset_task);
- }
-}
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0)
-static void rtl8169_tx_timeout(struct net_device *dev, unsigned int txqueue)
-#else
-static void rtl8169_tx_timeout(struct net_device *dev)
-#endif
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- unsigned long flags;
-
- spin_lock_irqsave(&tp->lock, flags);
- netif_stop_queue(dev);
- rtl8169_hw_reset(dev);
- spin_unlock_irqrestore(&tp->lock, flags);
-
- /* Let's wait a bit while any (async) irq lands on */
- rtl8169_schedule_work(dev, rtl8169_reset_task);
-}
-
-static u32 rtl8169_get_txd_opts1(u32 opts1, u32 len, unsigned int entry)
-{
- u32 status = opts1 | len;
-
- if (entry == NUM_TX_DESC - 1)
- status |= RingEnd;
-
- return status;
-}
-
-static int rtl8169_xmit_frags(struct rtl8169_private *tp,
- struct sk_buff *skb,
- const u32 *opts)
-{
- struct skb_shared_info *info = skb_shinfo(skb);
- unsigned int cur_frag, entry;
- struct TxDesc *txd = NULL;
- const unsigned char nr_frags = info->nr_frags;
-
- entry = tp->cur_tx;
- for (cur_frag = 0; cur_frag < nr_frags; cur_frag++) {
- skb_frag_t *frag = info->frags + cur_frag;
- dma_addr_t mapping;
- u32 status, len;
- void *addr;
-
- entry = (entry + 1) % NUM_TX_DESC;
-
- txd = tp->TxDescArray + entry;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)
- len = frag->size;
- addr = ((void *) page_address(frag->page)) + frag->page_offset;
-#else
- len = skb_frag_size(frag);
- addr = skb_frag_address(frag);
-#endif
- mapping = dma_map_single(&tp->pci_dev->dev, addr, len, DMA_TO_DEVICE);
-
- if (unlikely(dma_mapping_error(&tp->pci_dev->dev, mapping))) {
- if (unlikely(net_ratelimit()))
- netif_err(tp, drv, tp->dev,
- "Failed to map TX fragments DMA!\n");
- goto err_out;
- }
-
- /* anti gcc 2.95.3 bugware (sic) */
- status = rtl8169_get_txd_opts1(opts[1], len, entry);;
- if (cur_frag == (nr_frags - 1)) {
- tp->tx_skb[entry].skb = skb;
- status |= LastFrag;
- }
-
- txd->addr = cpu_to_le64(mapping);
-
- tp->tx_skb[entry].len = len;
-
- txd->opts2 = cpu_to_le32(opts[1]);
- wmb();
- txd->opts1 = cpu_to_le32(status);
- }
-
- return cur_frag;
-
-err_out:
- rtl8169_tx_clear_range(tp, tp->cur_tx + 1, cur_frag);
- return -EIO;
-}
-
-static bool rtl8169_skb_pad(struct sk_buff *skb)
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
- if (skb_padto(skb, ETH_ZLEN))
- return false;
- skb_put(skb, ETH_ZLEN - skb->len);
- return true;
-#else
- return !eth_skb_pad(skb);
-#endif
-}
-
-static inline bool
-rtl8169_tx_csum(struct sk_buff *skb,
- struct net_device *dev,
- u32 *opts)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- u32 csum_cmd = 0;
- u8 sw_calc_csum = FALSE;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
- const struct iphdr *ip = skb->nh.iph;
-#else
- const struct iphdr *ip = ip_hdr(skb);
-#endif
-
- if (skb->ip_summed == CHECKSUM_PARTIAL) {
- if (ip->protocol == IPPROTO_TCP)
- csum_cmd = IPCS | TCPCS;
- else if (ip->protocol == IPPROTO_UDP)
- csum_cmd = IPCS | UDPCS;
-
- if (csum_cmd == 0) {
- sw_calc_csum = TRUE;
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
- WARN_ON(1); /* we need a WARN() */
-#endif
- }
- }
-
- opts[0] |= csum_cmd;
-
- if (tp->UseSwPaddingShortPkt && skb->len < ETH_ZLEN)
- if (!rtl8169_skb_pad(skb))
- return false;
-
- if (sw_calc_csum) {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,7)
- skb_checksum_help(&skb, 0);
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) && LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
- skb_checksum_help(skb, 0);
-#else
- skb_checksum_help(skb);
-#endif
- }
-
- return true;
-}
-
-static bool rtl8169_tx_slots_avail(struct rtl8169_private *tp,
- unsigned int nr_frags)
-{
- unsigned int slots_avail = tp->dirty_tx + NUM_TX_DESC - tp->cur_tx;
-
- /* A skbuff with nr_frags needs nr_frags+1 entries in the tx queue */
- return slots_avail > nr_frags;
-}
-
-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;
- struct TxDesc *txd;
- void __iomem *ioaddr = tp->mmio_addr;
- dma_addr_t mapping;
- u32 status, len;
- u32 opts[2];
- netdev_tx_t ret = NETDEV_TX_OK;
- unsigned long flags, large_send;
- int frags;
-
- spin_lock_irqsave(&tp->lock, flags);
-
- if (unlikely(!rtl8169_tx_slots_avail(tp, skb_shinfo(skb)->nr_frags))) {
- if (netif_msg_drv(tp)) {
- printk(KERN_ERR
- "%s: BUG! Tx Ring full when queue awake!\n",
- dev->name);
- }
- goto err_stop;
- }
-
- entry = tp->cur_tx % NUM_TX_DESC;
- txd = tp->TxDescArray + entry;
-
- if (unlikely(le32_to_cpu(txd->opts1) & DescOwn)) {
- if (netif_msg_drv(tp)) {
- printk(KERN_ERR
- "%s: BUG! Tx Desc is own by hardware!\n",
- dev->name);
- }
- goto err_stop;
- }
-
- opts[0] = DescOwn;
- opts[1] = rtl8169_tx_vlan_tag(tp, skb);
-
- large_send = 0;
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
- if (dev->features & NETIF_F_TSO) {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
- u32 mss = skb_shinfo(skb)->tso_size;
-#else
- u32 mss = skb_shinfo(skb)->gso_size;
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
-
- if (mss) {
- opts[0] |= LargeSend | (min(mss, MSS_MAX) << MSSShift);
- large_send = 1;
- }
- }
-#endif //LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
-
- if (large_send == 0) {
- if (unlikely(!rtl8169_tx_csum(skb, dev, opts)))
- goto err_dma_0;
- }
-
- frags = rtl8169_xmit_frags(tp, skb, opts);
- if (unlikely(frags < 0))
- goto err_dma_0;
- if (frags) {
- len = skb_headlen(skb);
- opts[0] |= FirstFrag;
- } else {
- len = skb->len;
- tp->tx_skb[entry].skb = skb;
-
- opts[0] |= FirstFrag | LastFrag;
- }
-
- mapping = dma_map_single(&tp->pci_dev->dev, skb->data, len, DMA_TO_DEVICE);
- if (unlikely(dma_mapping_error(&tp->pci_dev->dev, mapping))) {
- if (unlikely(net_ratelimit()))
- netif_err(tp, drv, dev, "Failed to map TX DMA!\n");
- goto err_dma_1;
- }
-
- /* anti gcc 2.95.3 bugware (sic) */
- status = rtl8169_get_txd_opts1(opts[0], len, entry);
-
- txd->addr = cpu_to_le64(mapping);
-
- tp->tx_skb[entry].len = len;
-
- txd->opts2 = cpu_to_le32(opts[1]);
- wmb();
- txd->opts1 = cpu_to_le32(status);
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0)
- dev->trans_start = jiffies;
-#else
- skb_tx_timestamp(skb);
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0)
-
- tp->cur_tx += frags + 1;
-
- smp_wmb();
-
- RTL_W8(TxPoll, NPQ); /* set polling bit */
-
- if (!rtl8169_tx_slots_avail(tp, MAX_SKB_FRAGS)) {
- netif_stop_queue(dev);
- smp_rmb();
- if (rtl8169_tx_slots_avail(tp, MAX_SKB_FRAGS))
- netif_wake_queue(dev);
- }
-
- spin_unlock_irqrestore(&tp->lock, flags);
-
-out:
- return ret;
-
-err_dma_1:
- tp->tx_skb[entry].skb = NULL;
- rtl8169_tx_clear_range(tp, tp->cur_tx + 1, frags);
-err_dma_0:
- RTLDEV->stats.tx_dropped++;
- spin_unlock_irqrestore(&tp->lock, flags);
- dev_kfree_skb_any(skb);
- ret = NETDEV_TX_OK;
- goto out;
-err_stop:
- netif_stop_queue(dev);
- ret = NETDEV_TX_BUSY;
- RTLDEV->stats.tx_dropped++;
-
- spin_unlock_irqrestore(&tp->lock, flags);
- goto out;
-}
-
-static void rtl8169_pcierr_interrupt(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- struct pci_dev *pdev = tp->pci_dev;
- void __iomem *ioaddr = tp->mmio_addr;
- u16 pci_status, pci_cmd;
-
- pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd);
- pci_read_config_word(pdev, PCI_STATUS, &pci_status);
-
- if (netif_msg_intr(tp)) {
- printk(KERN_ERR
- "%s: PCI error (cmd = 0x%04x, status = 0x%04x).\n",
- dev->name, pci_cmd, pci_status);
- }
-
- /*
- * The recovery sequence below admits a very elaborated explanation:
- * - it seems to work;
- * - I did not see what else could be done;
- * - it makes iop3xx happy.
- *
- * Feel free to adjust to your needs.
- */
-
- pci_write_config_word(pdev, PCI_COMMAND, pci_cmd | PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
-
- pci_write_config_word(pdev, PCI_STATUS,
- pci_status & (PCI_STATUS_DETECTED_PARITY |
- PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_REC_MASTER_ABORT |
- PCI_STATUS_REC_TARGET_ABORT | PCI_STATUS_SIG_TARGET_ABORT));
-
- /* The infamous DAC f*ckup only happens at boot time */
- if ((tp->cp_cmd & PCIDAC) && !tp->dirty_rx && !tp->cur_rx) {
- if (netif_msg_intr(tp))
- printk(KERN_INFO "%s: disabling PCI DAC.\n", dev->name);
- tp->cp_cmd &= ~PCIDAC;
- RTL_W16(CPlusCmd, tp->cp_cmd);
- dev->features &= ~NETIF_F_HIGHDMA;
- }
-
- rtl8169_hw_reset(dev);
-
- rtl8169_schedule_work(dev, rtl8169_reinit_task);
-}
-
-static void
-rtl8169_tx_interrupt(struct net_device *dev, struct rtl8169_private *tp,
- void __iomem *ioaddr)
-{
- unsigned int dirty_tx, tx_left;
-
- assert(dev != NULL);
- assert(tp != NULL);
- assert(ioaddr != NULL);
-
- dirty_tx = tp->dirty_tx;
- smp_rmb();
- tx_left = tp->cur_tx - dirty_tx;
-
- while (tx_left > 0) {
- unsigned int entry = dirty_tx % NUM_TX_DESC;
- struct ring_info *tx_skb = tp->tx_skb + entry;
- u32 len = tx_skb->len;
- u32 status;
-
- rmb();
- status = le32_to_cpu(tp->TxDescArray[entry].opts1);
- if (status & DescOwn)
- break;
-
- RTLDEV->stats.tx_bytes += len;
- RTLDEV->stats.tx_packets++;
-
- rtl8169_unmap_tx_skb(tp->pci_dev, tx_skb, tp->TxDescArray + entry);
-
- if (tx_skb->skb!=NULL) {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)
- dev_consume_skb_any(tx_skb->skb);
-#else
- dev_kfree_skb_any(tx_skb->skb);
-#endif
- tx_skb->skb = NULL;
- }
- dirty_tx++;
- tx_left--;
- }
-
- if (tp->dirty_tx != dirty_tx) {
- tp->dirty_tx = dirty_tx;
- smp_wmb();
- if (netif_queue_stopped(dev) &&
- (rtl8169_tx_slots_avail(tp, MAX_SKB_FRAGS))) {
- netif_wake_queue(dev);
- }
- smp_rmb();
- }
-}
-
-static inline int rtl8169_fragmented_frame(u32 status)
-{
- return (status & (FirstFrag | LastFrag)) != (FirstFrag | LastFrag);
-}
-
-static inline void rtl8169_rx_csum(struct sk_buff *skb, struct RxDesc *desc)
-{
- u32 opts1 = le32_to_cpu(desc->opts1);
- u32 status = opts1 & RxProtoMask;
-
- if (((status == RxProtoTCP) && !(opts1 & (TCPFail | IPFail))) ||
- ((status == RxProtoUDP) && !(opts1 & (UDPFail | IPFail))))
- skb->ip_summed = CHECKSUM_UNNECESSARY;
- else
- skb->ip_summed = CHECKSUM_NONE;
-}
-
-static inline int rtl8169_try_rx_copy(struct rtl8169_private *tp,
- struct sk_buff **sk_buff,
- int pkt_size,
- struct RxDesc *desc, int rx_buf_sz,
- unsigned int align)
-{
- int ret = -1;
-
- if (pkt_size < rx_copybreak) {
- struct sk_buff *skb;
-
- skb = RTL_ALLOC_SKB_INTR(tp, pkt_size + NET_IP_ALIGN);
- if (skb) {
- u8 *data;
-
- data = sk_buff[0]->data;
- skb_reserve(skb, NET_IP_ALIGN);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,37)
- prefetch(data - NET_IP_ALIGN);
-#endif
- eth_copy_and_sum(skb, data, pkt_size, 0);
- *sk_buff = skb;
- rtl8169_mark_to_asic(desc, rx_buf_sz);
- ret = 0;
- }
- }
- return ret;
-}
-
-static int
-rtl8169_rx_interrupt(struct net_device *dev,
- struct rtl8169_private *tp,
- void __iomem *ioaddr, napi_budget budget)
-{
- unsigned int cur_rx, rx_left;
- unsigned int delta, count = 0;
- unsigned int entry;
- struct RxDesc *desc;
- u32 status;
- u32 rx_quota;
-
- assert(dev != NULL);
- assert(tp != NULL);
- assert(ioaddr != NULL);
-
- if (tp->RxDescArray == NULL)
- goto rx_out;
-
- rx_quota = RTL_RX_QUOTA(budget);
- cur_rx = tp->cur_rx;
- entry = cur_rx % NUM_RX_DESC;
- desc = tp->RxDescArray + entry;
- rx_left = NUM_RX_DESC + tp->dirty_rx - cur_rx;
- rx_left = rtl8169_rx_quota(rx_left, (u32) rx_quota);
-
- for (; rx_left > 0; rx_left--) {
- status = le32_to_cpu(desc->opts1);
- if (status & DescOwn)
- break;
-
- rmb();
-
- if (unlikely(status & RxRES)) {
- if (netif_msg_rx_err(tp)) {
- printk(KERN_INFO
- "%s: Rx ERROR. status = %08x\n",
- dev->name, status);
- }
- RTLDEV->stats.rx_errors++;
-
- if (status & (RxRWT | RxRUNT))
- RTLDEV->stats.rx_length_errors++;
- if (status & RxCRC)
- RTLDEV->stats.rx_crc_errors++;
- if (dev->features & NETIF_F_RXALL)
- goto process_pkt;
- rtl8169_mark_to_asic(desc, tp->rx_buf_sz);
- } else {
- struct sk_buff *skb;
- int pkt_size ;
-
-process_pkt:
- pkt_size = status & 0x00003fff;
- 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)) ||
- unlikely(pkt_size > tp->rx_buf_sz)) {
- RTLDEV->stats.rx_dropped++;
- RTLDEV->stats.rx_length_errors++;
- rtl8169_mark_to_asic(desc, tp->rx_buf_sz);
- continue;
- }
-
- skb = tp->Rx_skbuff[entry];
-
- dma_sync_single_for_cpu(&tp->pci_dev->dev,
- le64_to_cpu(desc->addr), tp->rx_buf_sz,
- DMA_FROM_DEVICE);
-
- if (rtl8169_try_rx_copy(tp, &skb, pkt_size,
- desc, tp->rx_buf_sz, tp->align)) {
- tp->Rx_skbuff[entry] = NULL;
- dma_unmap_single(&tp->pci_dev->dev, le64_to_cpu(desc->addr),
- tp->rx_buf_sz, DMA_FROM_DEVICE);
- } else {
- dma_sync_single_for_device(&tp->pci_dev->dev, le64_to_cpu(desc->addr),
- tp->rx_buf_sz, DMA_FROM_DEVICE);
- }
-
- if (tp->cp_cmd & RxChkSum)
- rtl8169_rx_csum(skb, desc);
-
- skb->dev = dev;
- skb_put(skb, pkt_size);
- skb->protocol = eth_type_trans(skb, dev);
-
- if (skb->pkt_type == PACKET_MULTICAST)
- RTLDEV->stats.multicast++;
-
- if (rtl8169_rx_vlan_skb(tp, desc, skb) < 0)
- rtl8169_rx_skb(skb);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,11,0)
- dev->last_rx = jiffies;
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(4,11,0)
- RTLDEV->stats.rx_bytes += pkt_size;
- RTLDEV->stats.rx_packets++;
- }
-
- cur_rx++;
- entry = cur_rx % NUM_RX_DESC;
- desc = tp->RxDescArray + entry;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,37)
- prefetch(desc);
-#endif
- }
-
- count = cur_rx - tp->cur_rx;
- tp->cur_rx = cur_rx;
-
- delta = rtl8169_rx_fill(tp, dev, tp->dirty_rx, tp->cur_rx, 1);
- if (!delta && count && netif_msg_intr(tp))
- printk(KERN_INFO "%s: no Rx buffer allocated\n", dev->name);
- tp->dirty_rx += delta;
-
- /*
- * FIXME: until there is periodic timer to try and refill the ring,
- * a temporary shortage may definitely kill the Rx process.
- * - disable the asic to try and avoid an overflow and kick it again
- * after refill ?
- * - how do others driver handle this condition (Uh oh...).
- */
- if ((tp->dirty_rx + NUM_RX_DESC == tp->cur_rx) && netif_msg_intr(tp))
- printk(KERN_EMERG "%s: Rx buffers exhausted\n", dev->name);
-
-rx_out:
- return count;
-}
-
-/* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
-static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
-#else
-static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
-#endif
-{
- struct net_device *dev = (struct net_device *) dev_instance;
- struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- int status;
- int handled = IRQ_NONE;
-
- do {
- status = RTL_R16(IntrStatus);
-
- /* hotplug/major error/no more work/shared irq */
- if ((status == 0xFFFF) || !status)
- break;
-
- status &= tp->intr_mask;
-
- if (!(status & rtl8169_intr_mask))
- break;
-
- handled = 1;
-
- RTL_W16(IntrStatus,
- (status & RxFIFOOver) ? (status | RxOverflow) : status);
-
- if (unlikely(status & SYSErr)) {
- rtl8169_pcierr_interrupt(dev);
- break;
- }
-
- if (status & LinkChg)
- rtl8169_check_link_status(dev, tp, ioaddr);
-
-#ifdef CONFIG_R8169_NAPI
- if (status & rtl8169_napi_event) {
- RTL_W16(IntrMask, rtl8169_intr_mask & ~rtl8169_napi_event);
- tp->intr_mask = rtl8169_intr_mask & ~rtl8169_napi_event;
-
- if (likely(RTL_NETIF_RX_SCHEDULE_PREP(dev, &tp->napi))) {
- __RTL_NETIF_RX_SCHEDULE(dev, &tp->napi);
- } else if (netif_msg_intr(tp)) {
- printk(KERN_INFO "%s: interrupt %04x in poll\n",
- dev->name, status);
- }
- }
- break;
-#else
- u32 budget = ~(u32)0;
-
- /* Tx interrupt */
- rtl8169_tx_interrupt(dev, tp, ioaddr);
-
- /* Rx interrupt */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
- rtl8169_rx_interrupt(dev, tp, tp->mmio_addr, &budget);
-#else
- rtl8169_rx_interrupt(dev, tp, tp->mmio_addr, budget);
-#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
-
- RTL_W16(IntrMask, rtl8169_intr_mask);
-#endif
- } while (false);
-
- return IRQ_RETVAL(handled);
-}
-
-#ifdef CONFIG_R8169_NAPI
-static int rtl8169_poll(napi_ptr napi, napi_budget budget)
-{
- struct rtl8169_private *tp = RTL_GET_PRIV(napi, struct rtl8169_private);
- void __iomem *ioaddr = tp->mmio_addr;
- RTL_GET_NETDEV(tp)
- unsigned int work_to_do = RTL_NAPI_QUOTA(budget, dev);
- unsigned int work_done;
- unsigned long flags;
-
- spin_lock_irqsave(&tp->lock, flags);
- rtl8169_tx_interrupt(dev, tp, ioaddr);
- spin_unlock_irqrestore(&tp->lock, flags);
-
- work_done = rtl8169_rx_interrupt(dev, tp, ioaddr, budget);
-
- RTL_NAPI_QUOTA_UPDATE(dev, work_done, budget);
-
- if (work_done < work_to_do) {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)
- if (RTL_NETIF_RX_COMPLETE(dev, napi, work_done) == FALSE) return RTL_NAPI_RETURN_VALUE;
-#else
- RTL_NETIF_RX_COMPLETE(dev, napi, work_done);
-#endif
- tp->intr_mask = rtl8169_intr_mask;
- /*
- * 20040426: the barrier is not strictly required but the
- * behavior of the irq handler could be less predictable
- * without it. Btw, the lack of flush for the posted pci
- * write is safe - FR
- */
- smp_wmb();
- RTL_W16(IntrMask, rtl8169_intr_mask);
- }
-
- return RTL_NAPI_RETURN_VALUE;
-}
-#endif//CONFIG_R8169_NAPI
-
-static void rtl8169_down(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
- unsigned long flags;
-#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)) && (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0))
- unsigned int poll_locked = 0;
-#endif
-
-
- rtl8169_delete_timer(dev);
- rtl8169_delete_esd_timer(dev, &tp->esd_timer);
-
- netif_stop_queue(dev);
-
-#ifdef CONFIG_R8169_NAPI
- RTL_NAPI_DISABLE(dev, &tp->napi);
-#endif
- spin_lock_irqsave(&tp->lock, flags);
-
- rtl8169_hw_reset(dev);
-
- /* Update the error counts. */
- tp->stats.rx_missed_errors += RTL_R32(RxMissed);
- RTL_W32(RxMissed, 0);
-
- spin_unlock_irqrestore(&tp->lock, flags);
-
- synchronize_irq(dev->irq);
-
-#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)) && (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0))
- if (!poll_locked) {
-#ifdef CONFIG_R8169_NAPI
- netif_poll_disable(dev);
-#endif
- poll_locked++;
- }
-#endif
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,11)
- /* Give a racing hard_start_xmit a few cycles to complete. */
- synchronize_rcu(); /* FIXME: should this be synchronize_irq()? */
-#endif
-
- /*
- * And now for the 50k$ question: are IRQ disabled or not ?
- *
- * Two paths lead here:
- * 1) dev->close
- * -> netif_running() is available to sync the current code and the
- * IRQ handler. See rtl8169_interrupt for details.
- * 2) dev->change_mtu
- * -> rtl8169_poll can not be issued again and re-enable the
- * interruptions. Let's simply issue the IRQ down sequence again.
- *
- * No loop if hotpluged or major error (0xffff).
- */
-
- spin_lock_irqsave(&tp->lock, flags);
-
- rtl8169_tx_clear(tp);
-
- rtl8169_rx_clear(tp);
-
- rtl8169_powerdown_pll(tp);
-
- spin_unlock_irqrestore(&tp->lock, flags);
-}
-
-static int rtl8169_close(struct net_device *dev)
-{
- struct rtl8169_private *tp = netdev_priv(dev);
- struct pci_dev *pdev = tp->pci_dev;
-
- if(tp->TxDescArray!=NULL && tp->RxDescArray!=NULL) {
- rtl8169_cancel_schedule_work(dev);
-
- rtl8169_down(dev);
-
- pci_clear_master(tp->pci_dev);
-
- free_irq(dev->irq, dev);
-
- 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;
- }
-
- return 0;
-}
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,11)
-static void rtl8169_shutdown(struct pci_dev *pdev)
-{
- struct net_device *dev = pci_get_drvdata(pdev);
- struct rtl8169_private *tp = netdev_priv(dev);
-
- /* restore the original MAC address */
- if (s5_keep_curr_mac == 0 && tp->random_mac == 0)
- rtl8169_rar_set(tp, tp->org_mac_addr);
-
- rtl8169_close(dev);
-
- if (system_state == SYSTEM_POWER_OFF) {
- pci_clear_master(tp->pci_dev);
- pci_wake_from_d3(pdev, tp->wol_enabled);
- pci_set_power_state(pdev, PCI_D3hot);
- }
-}
-#endif
-
-#ifdef CONFIG_PM
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
-static int
-rtl8169_suspend(struct pci_dev *pdev,
- u32 state)
-#else
-static int
-rtl8169_suspend(struct pci_dev *pdev,
- pm_message_t state)
-#endif
-{
- struct net_device *dev = pci_get_drvdata(pdev);
- struct rtl8169_private *tp = netdev_priv(dev);
- void __iomem *ioaddr = tp->mmio_addr;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
- u32 pci_pm_state = pci_choose_state(pdev, state);
-#endif
- unsigned long flags;
-
- if (!netif_running(dev))
- goto out;
-
- rtl8169_cancel_schedule_work(dev);
-
- netif_device_detach(dev);
- netif_stop_queue(dev);
-
- rtl8169_delete_timer(dev);
- rtl8169_delete_esd_timer(dev, &tp->esd_timer);
-
- spin_lock_irqsave(&tp->lock, flags);
-
- rtl8169_hw_reset(dev);
-
- pci_clear_master(pdev);
-
- tp->stats.rx_missed_errors += RTL_R32(RxMissed);
- RTL_W32(RxMissed, 0);
-
- rtl8169_powerdown_pll(tp);
-
- spin_unlock_irqrestore(&tp->lock, flags);
-
-out:
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
- pci_save_state(pdev, &pci_pm_state);
-#else
- pci_save_state(pdev);
-#endif
- pci_enable_wake(pdev, pci_choose_state(pdev, state), tp->wol_enabled);
-// pci_set_power_state(pdev, pci_choose_state(pdev, state));
-
- return 0;
-}
-
-static int rtl8169_resume(struct pci_dev *pdev)
-{
- struct net_device *dev = pci_get_drvdata(pdev);
- struct rtl8169_private *tp = netdev_priv(dev);
- unsigned long flags;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
- u32 pci_pm_state = PCI_D0;
-#endif
-
- pci_set_power_state(pdev, PCI_D0);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)
- pci_restore_state(pdev, &pci_pm_state);
-#else
- pci_restore_state(pdev);
-#endif
- pci_enable_wake(pdev, PCI_D0, 0);
-
- spin_lock_irqsave(&tp->lock, flags);
-
- /* restore last modified mac address */
- rtl8169_rar_set(tp, dev->dev_addr);
-
- spin_unlock_irqrestore(&tp->lock, flags);
-
- if (!netif_running(dev))
- goto out;
-
- pci_set_master(pdev);
-
- spin_lock_irqsave(&tp->lock, flags);
-
- rtl8169_hw_init(dev);
-
- rtl8169_phy_power_up(dev);
-
- rtl8169_hw_phy_config(dev);
-
- spin_unlock_irqrestore(&tp->lock, flags);
-
- rtl8169_schedule_work(dev, rtl8169_reset_task);
-
- netif_device_attach(dev);
-
- mod_timer(&tp->link_timer, jiffies + RTL8169_PHY_TIMEOUT);
- mod_timer(&tp->esd_timer, jiffies + RTL8169_ESD_TIMEOUT);
-out:
- return 0;
-}
-
-#endif /* CONFIG_PM */
-
-static struct pci_driver rtl8169_pci_driver = {
- .name = MODULENAME,
- .id_table = rtl8169_pci_tbl,
- .probe = rtl8169_init_one,
- .remove = __devexit_p(rtl8169_remove_one),
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,11)
- .shutdown = rtl8169_shutdown,
-#endif
-#ifdef CONFIG_PM
- .suspend = rtl8169_suspend,
- .resume = rtl8169_resume,
-#endif
-};
-
-static int __init
-rtl8169_init_module(void)
-{
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
- return pci_register_driver(&rtl8169_pci_driver);
-#else
- return pci_module_init(&rtl8169_pci_driver);
-#endif
-}
-
-static void __exit
-rtl8169_cleanup_module(void)
-{
- pci_unregister_driver(&rtl8169_pci_driver);
-}
-
-module_init(rtl8169_init_module);
-module_exit(rtl8169_cleanup_module);
diff --git a/r8169_phy_config.c b/r8169_phy_config.c
new file mode 100644
index 0000000..b50f167
--- /dev/null
+++ b/r8169_phy_config.c
@@ -0,0 +1,1159 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * r8169_phy_config.c: RealTek 8169/8168/8101 ethernet driver.
+ *
+ * Copyright (c) 2002 ShuChen
+ * Copyright (c) 2003 - 2007 Francois Romieu
+ * Copyright (c) a lot of people too. Please respect their work.
+ *
+ * See MAINTAINERS file for support contact information.
+ */
+
+#include
+#include
+
+#include "r8169.h"
+
+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);
+}
+
+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 rtl8125a_config_eee_phy(struct phy_device *phydev)
+{
+ rtl8168h_config_eee_phy(phydev);
+
+ phy_modify_paged(phydev, 0xa6d, 0x12, 0x0001, 0x0000);
+ phy_modify_paged(phydev, 0xa6d, 0x14, 0x0010, 0x0000);
+}
+
+static void rtl8125b_config_eee_phy(struct phy_device *phydev)
+{
+ phy_modify_paged(phydev, 0xa6d, 0x12, 0x0001, 0x0000);
+ 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 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 rtl8168bb_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ phy_write(phydev, 0x1f, 0x0001);
+ phy_set_bits(phydev, 0x16, BIT(0));
+ phy_write(phydev, 0x10, 0xf41b);
+ phy_write(phydev, 0x1f, 0x0000);
+}
+
+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);
+
+ phy_write(phydev, 0x1f, 0x0b87);
+ phy_write(phydev, 0x16, 0x80a2);
+ phy_write(phydev, 0x17, 0x0153);
+ phy_write(phydev, 0x16, 0x809c);
+ phy_write(phydev, 0x17, 0x0153);
+ phy_write(phydev, 0x1f, 0x0000);
+
+ 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);
+
+ rtl8125a_config_eee_phy(phydev);
+}
+
+static void rtl8125b_hw_phy_config(struct rtl8169_private *tp,
+ struct phy_device *phydev)
+{
+ r8169_apply_firmware(tp);
+
+ phy_modify_paged(phydev, 0xa44, 0x11, 0x0000, 0x0800);
+ phy_modify_paged(phydev, 0xac4, 0x13, 0x00f0, 0x0090);
+ phy_modify_paged(phydev, 0xad3, 0x10, 0x0003, 0x0001);
+
+ phy_write(phydev, 0x1f, 0x0b87);
+ phy_write(phydev, 0x16, 0x80f5);
+ phy_write(phydev, 0x17, 0x760e);
+ phy_write(phydev, 0x16, 0x8107);
+ phy_write(phydev, 0x17, 0x360e);
+ phy_write(phydev, 0x16, 0x8551);
+ phy_modify(phydev, 0x17, 0xff00, 0x0800);
+ phy_write(phydev, 0x1f, 0x0000);
+
+ 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);
+ rtl8125b_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_11] = rtl8168bb_hw_phy_config,
+ [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_53] = rtl8117_hw_phy_config,
+ [RTL_GIGA_MAC_VER_61] = rtl8125a_2_hw_phy_config,
+ [RTL_GIGA_MAC_VER_63] = rtl8125b_hw_phy_config,
+ };
+
+ if (phy_configs[ver])
+ phy_configs[ver](tp, phydev);
+}