From 28736a68b219231aad838bd7578b604de2b4257f Mon Sep 17 00:00:00 2001 From: Tianli Xiong Date: Fri, 9 Sep 2022 17:46:10 +0800 Subject: [PATCH 01/56] PCI: Check if entry->offset already exist for mem resource Fix patch "LoongArch: Add PCI controller support" Signed-off-by: Tianli Xiong Change-Id: I796b1224d8990fed952643adac491228d6541696 --- arch/loongarch/pci/acpi.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/loongarch/pci/acpi.c b/arch/loongarch/pci/acpi.c index 7706acd4c290..f7bfc3986ec6 100644 --- a/arch/loongarch/pci/acpi.c +++ b/arch/loongarch/pci/acpi.c @@ -210,9 +210,11 @@ static int acpi_prepare_root_resources(struct acpi_pci_root_info *ci) if (status > 0) { resource_list_for_each_entry_safe(entry, tmp, &ci->resources) { if (entry->res->flags & IORESOURCE_MEM) { - entry->offset = ci->root->mcfg_addr & GENMASK_ULL(63, 40); - entry->res->start |= entry->offset; - entry->res->end |= entry->offset; + if(!entry_>offset) { + entry->offset = ci->root->mcfg_addr & GENMASK_ULL(63, 40); + entry->res->start |= entry->offset; + entry->res->end |= entry->offset; + } } } return status; -- Gitee From cc2a7f5d9cf99d57bdce2f394aa1b645df1f0dad Mon Sep 17 00:00:00 2001 From: Tianli Xiong Date: Fri, 9 Sep 2022 18:03:19 +0800 Subject: [PATCH 02/56] PCI: Check if the pci controller can use both CFG0 and CFG1 mode to access configuration space Fix patch "PCI: loongson: Use generic 8/16/32-bit config ops on LS2K/LS7A" Signed-off-by: Tianli Xiong Change-Id: I59f2de29370d5d9085254d8c4337f4bbcae99de0 --- drivers/pci/controller/pci-loongson.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/pci-loongson.c b/drivers/pci/controller/pci-loongson.c index 679ff66f9c29..f0ace5748f81 100644 --- a/drivers/pci/controller/pci-loongson.c +++ b/drivers/pci/controller/pci-loongson.c @@ -282,6 +282,7 @@ static int loongson_pci_probe(struct platform_device *pdev) struct device_node *node = dev->of_node; struct pci_host_bridge *bridge; struct resource *regs; + unsigned int num = 0; if (!node) return -ENODEV; @@ -306,7 +307,9 @@ static int loongson_pci_probe(struct platform_device *pdev) } if (priv->data->flags & FLAG_CFG1) { - regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (priv->cfg0_base) + num = 1; + regs = platform_get_resource(pdev, IORESOURCE_MEM, num); if (!regs) dev_info(dev, "missing mem resource for cfg1\n"); else { -- Gitee From 7631b45d4a0cb78cb0f17804b4a6f94ef50b0601 Mon Sep 17 00:00:00 2001 From: Jianmin Lv Date: Tue, 6 Sep 2022 16:41:32 +0800 Subject: [PATCH 03/56] PCI: PM: Fix pcie mrrs restoring Don't limit mmrs during resume, so that saved value can be restored. Fix patch "PCI: loongson: Improve the MRRS quirk for LS7A" Signed-off-by: Jianmin Lv Change-Id: I63c49e5c1d7e2a0a6eb3de9faa13cef1f94a4462 --- drivers/pci/pci.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8ace56c8141b..99eeafead49e 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "pci.h" DEFINE_MUTEX(pci_slot_mutex); @@ -147,6 +148,11 @@ static bool pci_bridge_d3_disable; /* Force bridge_d3 for all PCIe ports */ static bool pci_bridge_d3_force; +#ifndef CONFIG_PM_SLEEP +suspend_state_t pm_suspend_target_state; +#define pm_suspend_target_state (PM_SUSPEND_ON) +#endif + static int __init pcie_port_pm_setup(char *str) { if (!strcmp(str, "off")) @@ -5769,7 +5775,8 @@ int pcie_set_readrq(struct pci_dev *dev, int rq) v = (ffs(rq) - 8) << 12; - if (bridge->no_inc_mrrs) { + if (pm_suspend_target_state == PM_SUSPEND_ON && + bridge->no_inc_mrrs) { if (rq > pcie_get_readrq(dev)) return -EINVAL; } -- Gitee From 0ca417e5a13b446979324d9a2e5601e648d31120 Mon Sep 17 00:00:00 2001 From: Tianli Xiong Date: Wed, 7 Sep 2022 16:47:51 +0800 Subject: [PATCH 04/56] LoongArch: Fixed some pcie card not scanning properly Fix some pcie card not scanning properly when bus number is inconsistent during firmware and kernel scan phases. Signed-off-by: liuyun Signed-off-by: Tianli Xiong Change-Id: Iac9c07463569ca08da93ab0fa279b1880206e816 --- drivers/pci/controller/pci-loongson.c | 34 +++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/pci-loongson.c b/drivers/pci/controller/pci-loongson.c index f0ace5748f81..2777b9c0ed1d 100644 --- a/drivers/pci/controller/pci-loongson.c +++ b/drivers/pci/controller/pci-loongson.c @@ -216,6 +216,36 @@ static void __iomem *pci_loongson_map_bus(struct pci_bus *bus, return NULL; } +static int pci_loongson_config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + void __iomem *addr; + + addr = bus->ops->map_bus(bus, devfn, where); + if (!addr) { + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + if (size == 1) + *val = readb(addr); + else if (size == 2) + *val = readw(addr); + else + *val = readl(addr); + /* + * fix some pcie card not scanning properly when bus number is + * inconsistent during firmware and kernel scan phases. + */ + if (*val == 0x0 && where == PCI_VENDOR_ID) { + writel(*val, addr); + *val = readl(addr); + } + + + return PCIBIOS_SUCCESSFUL; +} + #ifdef CONFIG_OF static int loongson_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) @@ -239,7 +269,7 @@ static int loongson_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) /* LS2K/LS7A accept 8/16/32-bit PCI config operations */ static struct pci_ops loongson_pci_ops = { .map_bus = pci_loongson_map_bus, - .read = pci_generic_config_read, + .read = pci_loongson_config_read, .write = pci_generic_config_write, }; @@ -366,7 +396,7 @@ const struct pci_ecam_ops loongson_pci_ecam_ops = { .init = loongson_pci_ecam_init, .pci_ops = { .map_bus = pci_loongson_map_bus, - .read = pci_generic_config_read, + .read = pci_loongson_config_read, .write = pci_generic_config_write, } }; -- Gitee From acb26554dab43462aadc77a2ef5d8ea737a19c57 Mon Sep 17 00:00:00 2001 From: Jianmin Lv Date: Mon, 5 Sep 2022 17:26:21 +0800 Subject: [PATCH 05/56] pci/quirks: ls7a2000: fix pm transition of devices under pcie port Signed-off-by: Jianmin Lv Change-Id: Ie2c78cc46aa7bde00395906f7e6ab6a09e09a190 --- drivers/pci/controller/pci-loongson.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/pci/controller/pci-loongson.c b/drivers/pci/controller/pci-loongson.c index 2777b9c0ed1d..24441c1cf02c 100644 --- a/drivers/pci/controller/pci-loongson.c +++ b/drivers/pci/controller/pci-loongson.c @@ -19,6 +19,12 @@ #define DEV_PCIE_PORT_1 0x7a19 #define DEV_PCIE_PORT_2 0x7a29 +#define DEV_PCIE_PORT_4 0x7a39 +#define DEV_PCIE_PORT_5 0x7a49 +#define DEV_PCIE_PORT_6 0x7a59 +#define DEV_PCIE_PORT_7 0x7a69 + + #define DEV_LS2K_APB 0x7a02 #define DEV_LS7A_GMAC 0x7a03 #define DEV_LS7A_DC1 0x7a06 @@ -76,6 +82,20 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_LPC, system_bus_quirk); +static void loongson_d3_quirk(struct pci_dev *pdev) +{ + pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3; + pdev->no_d1d2 = 1; +} +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_LOONGSON, + DEV_PCIE_PORT_4, loongson_d3_quirk); +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_LOONGSON, + DEV_PCIE_PORT_5, loongson_d3_quirk); +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_LOONGSON, + DEV_PCIE_PORT_6, loongson_d3_quirk); +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_LOONGSON, + DEV_PCIE_PORT_7, loongson_d3_quirk); + static void loongson_mrrs_quirk(struct pci_dev *pdev) { /* -- Gitee From cb9d2fe8b994903035d8c1177769ea9fe9a3b1d1 Mon Sep 17 00:00:00 2001 From: Baoqi Zhang Date: Mon, 5 Sep 2022 18:07:34 +0800 Subject: [PATCH 06/56] LS7A2000: PCIE: Fixup GPU card error Add window to solve GPU access error Signed-off-by: Baoqi Zhang Change-Id: Idb607fa22e68e06fc4448af57247bd4e51616a7d --- drivers/pci/controller/pci-loongson.c | 38 +++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drivers/pci/controller/pci-loongson.c b/drivers/pci/controller/pci-loongson.c index 24441c1cf02c..0421169dca6a 100644 --- a/drivers/pci/controller/pci-loongson.c +++ b/drivers/pci/controller/pci-loongson.c @@ -157,6 +157,44 @@ static void loongson_ohci_quirk(struct pci_dev *dev) } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, DEV_LS7A_OHCI, loongson_ohci_quirk); +static void loongson_display_quirk(struct pci_dev *dev) +{ + u32 val; + u64 mask, size; + u64 max_size = 0; + int i, num; + struct pci_bus *bus = dev->bus; + + if (!dev->bus->number) { + if (!(dev->vendor == PCI_VENDOR_ID_LOONGSON && dev->device == 0x7a25)) + return; + } else { + while (!pci_is_root_bus(bus->parent)) + bus = bus->parent; + + /* ensure slot is 7a2000 */ + if (bus->self->vendor != PCI_VENDOR_ID_LOONGSON || bus->self->device < 0x7a39) + return; + } + max_size = 0; + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { + if (dev->resource[i].flags & IORESOURCE_MEM) { + size = dev->resource[i].end - dev->resource[i].start; + if (size > max_size) { + max_size = size; + num = i; + } + } + } + mask = ~(dev->resource[num].end - dev->resource[num].start); + val = (dev->resource[num].start >> (24 - 16)) | ((mask >> 24) & 0xffff); + writel(val, 0x80000efdfb000174); + writel(0x80000000, 0x80000efdfb000170); +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, 0x7a25, loongson_display_quirk); +DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID, + PCI_BASE_CLASS_DISPLAY, 16, loongson_display_quirk); + static struct loongson_pci *pci_bus_to_loongson_pci(struct pci_bus *bus) { struct pci_config_window *cfg; -- Gitee From a37457c68ca7c6f4a840f1c34c9a9891c69e6b09 Mon Sep 17 00:00:00 2001 From: suijingfeng Date: Mon, 5 Sep 2022 18:18:06 +0800 Subject: [PATCH 07/56] pci: fix X server auto probe fail when both ast and etnaviv drm present According to PCI-to-PCI bridge spec, bit 3 of Bridge Control Register is VGA Enable bit which modifies the response by the bridge to VGA compatible addresses. The Bridge Control register provides extensions to the Command register that are specific to a bridge. The Bridge Control register provides many of the same controls for the secondary interface that are provided by the Command register for the primary interface. There are some bits that affect the operation of both interfaces of the bridge. If the VGA Enable bit is set, the bridge will positively decode and forward the following accesses on the primary interface to the secondary interface (and, conversely, block the forwarding of these addresses from the secondary to primary interface) Forwarding of these accesses is qualified by the I/O Enable and Memory Enable bits in the Command register.) The default state of this bit after reset must be 0. Bit 3 of Bridge Control Register is VGA Enable bit which modifies the response by the bridge to VGA compatible addresses. when 0: do not forward VGA compatible memory and I/O addresses from the primary to secondary interface (addresses defined below) unless they are enabled for forwarding by the defined I/O when 1: forward VGA compatible memory and I/O addresses (addresses defined below) from the primary interface to the secondary interface (if the I/O Enable and Memory Enable bits are set) independent of the I/O and memory address ranges and independent of the ISA Enable bit * memory accesses in the range 000A 0000h to 000B FFFFh * I/O addresses in the first 64 KB of the I/O address space (AD[31:16] are 0000h) where AD[9:: 0] are in the ranges 3B0h to 3BBh and 3C0h to 3DFh (inclusive of ISA address aliases - AD[15::10] are not decoded) If the VGA Enable bit is set, forwarding of these accesses is independent of the I/O address range and memory address ranges defined by the I/O Base and Limit registers, the Memory Base and Limit registers, and the Prefetchable Memory Base and Limit registers of the bridge. Forwarding of these accesses is also independent of the settings of the ISA Enable bit (in the Bridge Control register) or VGA Palette Snoop bits (in the Command register). The AST2500 hardward we are using do not set the VGA Enable bit on its bridge control reg, this cause vgaarb subsystem don't think the VGA card behind this pridge as a valid boot vga device which made X server choose wrong video card to use when multiple video card present in the system. Its seems more vgaarb's fault than the ast2500 bmc itself. even through bit 3 of Bridge Control Register is 0, it should still allow to forward the accesses when the addresses is in the range of IO/MEM Base and Limit registers. Nevertheless, in order to support loongson CPU product line, we provide a workaround to this bug for the Sugon L620-G30 and Sugon L820-G30 server. see similar bug: https://patchwork.kernel.org/project/linux-pci/patch/20170619023528.11532-1-dja@axtens.net/ Signed-off-by: suijingfeng Change-Id: I53b6dee11c17b06866bdd927ac82cd6db88e16aa --- drivers/pci/controller/pci-loongson.c | 48 +++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/drivers/pci/controller/pci-loongson.c b/drivers/pci/controller/pci-loongson.c index 0421169dca6a..ec8908b8f3b7 100644 --- a/drivers/pci/controller/pci-loongson.c +++ b/drivers/pci/controller/pci-loongson.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "../pci.h" @@ -195,6 +196,53 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, 0x7a25, loongson_display_quirk); DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY, 16, loongson_display_quirk); +static void pci_fixup_aspeed(struct pci_dev *pdev) +{ + struct pci_dev *bridge; + struct pci_bus *bus; + struct pci_dev *vdevp = NULL; + u16 config; + + bus = pdev->bus; + bridge = bus->self; + + /* Is VGA routed to us? */ + if (bridge && (pci_is_bridge(bridge))) { + pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &config); + + /* Yes, this bridge is PCI bridge-to-bridge spec compliant, + * just return! + */ + if (config & PCI_BRIDGE_CTL_VGA) + return; + + dev_warn(&pdev->dev, "VGA bridge control is not enabled\n"); + } + + /* Just return if the system already have a default device */ + if (vga_default_device()) + return; + + /* No default vga device */ + while ((vdevp = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, vdevp))) { + if (vdevp->vendor != 0x1a03) { + /* Have other vga devcie in the system, do nothing */ + dev_info(&pdev->dev, + "Another boot vga device: 0x%x:0x%x\n", + vdevp->vendor, vdevp->device); + return; + } + } + + vga_set_default_device(pdev); + + dev_info(&pdev->dev, + "Boot vga device set as 0x%x:0x%x\n", + pdev->vendor, pdev->device); +} +DECLARE_PCI_FIXUP_CLASS_FINAL(0x1a03, 0x2000, + PCI_CLASS_DISPLAY_VGA, 8, pci_fixup_aspeed); + static struct loongson_pci *pci_bus_to_loongson_pci(struct pci_bus *bus) { struct pci_config_window *cfg; -- Gitee From 0abbc86976a88ed19b9e4951732c23452490219b Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Wed, 7 Sep 2022 14:02:38 +0800 Subject: [PATCH 08/56] LoongArch: Add vector extensions support Add LoongArch's vector extensions support, which including 128bit LSX (i.e., Loongson SIMD eXtension) and 256bit LASX (i.e., Loongson Advanced SIMD eXtension). Signed-off-by: Huacai Chen Change-Id: Icbdcc8c0657d85198accb9050f90dd41c9fa456c --- arch/loongarch/Kconfig | 32 ++ arch/loongarch/configs/loongson3_defconfig | 2 + arch/loongarch/include/asm/asmmacro.h | 624 +++++++++++++++++++++ arch/loongarch/include/asm/fpu.h | 198 ++++++- arch/loongarch/kernel/cpu-probe.c | 12 + arch/loongarch/kernel/fpu.S | 288 ++++++++++ arch/loongarch/kernel/process.c | 10 +- arch/loongarch/kernel/ptrace.c | 110 ++++ arch/loongarch/kernel/signal.c | 330 ++++++++++- arch/loongarch/kernel/traps.c | 82 ++- 10 files changed, 1678 insertions(+), 10 deletions(-) diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 068515a599e7..76f0be990526 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -75,6 +75,8 @@ config LOONGARCH select GENERIC_SCHED_CLOCK select GENERIC_SMP_IDLE_THREAD select GENERIC_TIME_VSYSCALL + select CPU_SUPPORTS_LSX + select CPU_SUPPORTS_LASX select GPIOLIB select HAVE_ARCH_AUDITSYSCALL select HAVE_ARCH_MMAP_RND_BITS if MMU @@ -145,6 +147,36 @@ config CPU_HAS_PREFETCH bool default y +config CPU_HAS_LSX + bool "Support for the Loongson SIMD Extension" + depends on CPU_SUPPORTS_LSX + depends on 64BIT + help + Loongson SIMD Extension (LSX) introduces 128 bit wide vector registers + and a set of SIMD instructions to operate on them. When this option + is enabled the kernel will support allocating & switching LSX + vector register contexts. If you know that your kernel will only be + running on CPUs which do not support LSX or that your userland will + not be making use of it then you may wish to say N here to reduce + the size & complexity of your kernel. + + If unsure, say Y. + +config CPU_HAS_LASX + bool "Support for the Loongson Advanced SIMD Extension" + depends on CPU_SUPPORTS_LASX + depends on 64BIT && CPU_HAS_LSX + help + Loongson Advanced SIMD Extension is 256 bit wide SIMD extension. + + If unsure, say Y. + +config CPU_SUPPORTS_LSX + bool + +config CPU_SUPPORTS_LASX + bool + config GENERIC_CALIBRATE_DELAY def_bool y diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index 7bdb125ff4d1..ace3efc63b08 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -35,6 +35,8 @@ CONFIG_BPF_SYSCALL=y CONFIG_USERFAULTFD=y CONFIG_PERF_EVENTS=y # CONFIG_COMPAT_BRK is not set +CONFIG_CPU_HAS_LSX=y +CONFIG_CPU_HAS_LASX=y CONFIG_HOTPLUG_CPU=y CONFIG_NUMA=y CONFIG_ACPI_SPCR_TABLE=y diff --git a/arch/loongarch/include/asm/asmmacro.h b/arch/loongarch/include/asm/asmmacro.h index be037a40580d..3a629b545174 100644 --- a/arch/loongarch/include/asm/asmmacro.h +++ b/arch/loongarch/include/asm/asmmacro.h @@ -117,6 +117,212 @@ .endif .endm + .macro parse_vr var vr + \var = -1 + .ifc \vr, $vr0 + \var = 0 + .endif + .ifc \vr, $vr1 + \var = 1 + .endif + .ifc \vr, $vr2 + \var = 2 + .endif + .ifc \vr, $vr3 + \var = 3 + .endif + .ifc \vr, $vr4 + \var = 4 + .endif + .ifc \vr, $vr5 + \var = 5 + .endif + .ifc \vr, $vr6 + \var = 6 + .endif + .ifc \vr, $vr7 + \var = 7 + .endif + .ifc \vr, $vr8 + \var = 8 + .endif + .ifc \vr, $vr9 + \var = 9 + .endif + .ifc \vr, $vr10 + \var = 10 + .endif + .ifc \vr, $vr11 + \var = 11 + .endif + .ifc \vr, $vr12 + \var = 12 + .endif + .ifc \vr, $vr13 + \var = 13 + .endif + .ifc \vr, $vr14 + \var = 14 + .endif + .ifc \vr, $vr15 + \var = 15 + .endif + .ifc \vr, $vr16 + \var = 16 + .endif + .ifc \vr, $vr17 + \var = 17 + .endif + .ifc \vr, $vr18 + \var = 18 + .endif + .ifc \vr, $vr19 + \var = 19 + .endif + .ifc \vr, $vr20 + \var = 20 + .endif + .ifc \vr, $vr21 + \var = 21 + .endif + .ifc \vr, $vr22 + \var = 22 + .endif + .ifc \vr, $vr23 + \var = 23 + .endif + .ifc \vr, $vr24 + \var = 24 + .endif + .ifc \vr, $vr25 + \var = 25 + .endif + .ifc \vr, $vr26 + \var = 26 + .endif + .ifc \vr, $vr27 + \var = 27 + .endif + .ifc \vr, $vr28 + \var = 28 + .endif + .ifc \vr, $vr29 + \var = 29 + .endif + .ifc \vr, $vr30 + \var = 30 + .endif + .ifc \vr, $vr31 + \var = 31 + .endif + .iflt \var + .error "Unable to parse register name \r" + .endif + .endm + + .macro parse_xr var xr + \var = -1 + .ifc \xr, $xr0 + \var = 0 + .endif + .ifc \xr, $xr1 + \var = 1 + .endif + .ifc \xr, $xr2 + \var = 2 + .endif + .ifc \xr, $xr3 + \var = 3 + .endif + .ifc \xr, $xr4 + \var = 4 + .endif + .ifc \xr, $xr5 + \var = 5 + .endif + .ifc \xr, $xr6 + \var = 6 + .endif + .ifc \xr, $xr7 + \var = 7 + .endif + .ifc \xr, $xr8 + \var = 8 + .endif + .ifc \xr, $xr9 + \var = 9 + .endif + .ifc \xr, $xr10 + \var = 10 + .endif + .ifc \xr, $xr11 + \var = 11 + .endif + .ifc \xr, $xr12 + \var = 12 + .endif + .ifc \xr, $xr13 + \var = 13 + .endif + .ifc \xr, $xr14 + \var = 14 + .endif + .ifc \xr, $xr15 + \var = 15 + .endif + .ifc \xr, $xr16 + \var = 16 + .endif + .ifc \xr, $xr17 + \var = 17 + .endif + .ifc \xr, $xr18 + \var = 18 + .endif + .ifc \xr, $xr19 + \var = 19 + .endif + .ifc \xr, $xr20 + \var = 20 + .endif + .ifc \xr, $xr21 + \var = 21 + .endif + .ifc \xr, $xr22 + \var = 22 + .endif + .ifc \xr, $xr23 + \var = 23 + .endif + .ifc \xr, $xr24 + \var = 24 + .endif + .ifc \xr, $xr25 + \var = 25 + .endif + .ifc \xr, $xr26 + \var = 26 + .endif + .ifc \xr, $xr27 + \var = 27 + .endif + .ifc \xr, $xr28 + \var = 28 + .endif + .ifc \xr, $xr29 + \var = 29 + .endif + .ifc \xr, $xr30 + \var = 30 + .endif + .ifc \xr, $xr31 + \var = 31 + .endif + .iflt \var + .error "Unable to parse register name \r" + .endif + .endm + .macro cpu_save_nonscratch thread stptr.d s0, \thread, THREAD_REG23 stptr.d s1, \thread, THREAD_REG24 @@ -270,6 +476,424 @@ fld.d $f31, \tmp, THREAD_FPR31 - THREAD_FPR0 .endm + .macro lsx_save_data thread tmp + parse_r __tmp, \tmp + li.w \tmp, THREAD_FPR0 + PTR_ADD \tmp, \thread, \tmp + /* vst opcode is 0xb1 */ + .word (0xb1 << 22 | ((THREAD_FPR0-THREAD_FPR0) << 10) | __tmp << 5 | 0) + .word (0xb1 << 22 | ((THREAD_FPR1-THREAD_FPR0) << 10) | __tmp << 5 | 1) + .word (0xb1 << 22 | ((THREAD_FPR2-THREAD_FPR0) << 10) | __tmp << 5 | 2) + .word (0xb1 << 22 | ((THREAD_FPR3-THREAD_FPR0) << 10) | __tmp << 5 | 3) + .word (0xb1 << 22 | ((THREAD_FPR4-THREAD_FPR0) << 10) | __tmp << 5 | 4) + .word (0xb1 << 22 | ((THREAD_FPR5-THREAD_FPR0) << 10) | __tmp << 5 | 5) + .word (0xb1 << 22 | ((THREAD_FPR6-THREAD_FPR0) << 10) | __tmp << 5 | 6) + .word (0xb1 << 22 | ((THREAD_FPR7-THREAD_FPR0) << 10) | __tmp << 5 | 7) + .word (0xb1 << 22 | ((THREAD_FPR8-THREAD_FPR0) << 10) | __tmp << 5 | 8) + .word (0xb1 << 22 | ((THREAD_FPR9-THREAD_FPR0) << 10) | __tmp << 5 | 9) + .word (0xb1 << 22 | ((THREAD_FPR10-THREAD_FPR0) << 10) | __tmp << 5 | 10) + .word (0xb1 << 22 | ((THREAD_FPR11-THREAD_FPR0) << 10) | __tmp << 5 | 11) + .word (0xb1 << 22 | ((THREAD_FPR12-THREAD_FPR0) << 10) | __tmp << 5 | 12) + .word (0xb1 << 22 | ((THREAD_FPR13-THREAD_FPR0) << 10) | __tmp << 5 | 13) + .word (0xb1 << 22 | ((THREAD_FPR14-THREAD_FPR0) << 10) | __tmp << 5 | 14) + .word (0xb1 << 22 | ((THREAD_FPR15-THREAD_FPR0) << 10) | __tmp << 5 | 15) + .word (0xb1 << 22 | ((THREAD_FPR16-THREAD_FPR0) << 10) | __tmp << 5 | 16) + .word (0xb1 << 22 | ((THREAD_FPR17-THREAD_FPR0) << 10) | __tmp << 5 | 17) + .word (0xb1 << 22 | ((THREAD_FPR18-THREAD_FPR0) << 10) | __tmp << 5 | 18) + .word (0xb1 << 22 | ((THREAD_FPR19-THREAD_FPR0) << 10) | __tmp << 5 | 19) + .word (0xb1 << 22 | ((THREAD_FPR20-THREAD_FPR0) << 10) | __tmp << 5 | 20) + .word (0xb1 << 22 | ((THREAD_FPR21-THREAD_FPR0) << 10) | __tmp << 5 | 21) + .word (0xb1 << 22 | ((THREAD_FPR22-THREAD_FPR0) << 10) | __tmp << 5 | 22) + .word (0xb1 << 22 | ((THREAD_FPR23-THREAD_FPR0) << 10) | __tmp << 5 | 23) + .word (0xb1 << 22 | ((THREAD_FPR24-THREAD_FPR0) << 10) | __tmp << 5 | 24) + .word (0xb1 << 22 | ((THREAD_FPR25-THREAD_FPR0) << 10) | __tmp << 5 | 25) + .word (0xb1 << 22 | ((THREAD_FPR26-THREAD_FPR0) << 10) | __tmp << 5 | 26) + .word (0xb1 << 22 | ((THREAD_FPR27-THREAD_FPR0) << 10) | __tmp << 5 | 27) + .word (0xb1 << 22 | ((THREAD_FPR28-THREAD_FPR0) << 10) | __tmp << 5 | 28) + .word (0xb1 << 22 | ((THREAD_FPR29-THREAD_FPR0) << 10) | __tmp << 5 | 29) + .word (0xb1 << 22 | ((THREAD_FPR30-THREAD_FPR0) << 10) | __tmp << 5 | 30) + .word (0xb1 << 22 | ((THREAD_FPR31-THREAD_FPR0) << 10) | __tmp << 5 | 31) + .endm + + .macro lsx_restore_data thread tmp + parse_r __tmp, \tmp + li.w \tmp, THREAD_FPR0 + PTR_ADD \tmp, \thread, \tmp + /* vld opcode is 0xb0 */ + .word (0xb0 << 22 | ((THREAD_FPR0-THREAD_FPR0) << 10) | __tmp << 5 | 0) + .word (0xb0 << 22 | ((THREAD_FPR1-THREAD_FPR0) << 10) | __tmp << 5 | 1) + .word (0xb0 << 22 | ((THREAD_FPR2-THREAD_FPR0) << 10) | __tmp << 5 | 2) + .word (0xb0 << 22 | ((THREAD_FPR3-THREAD_FPR0) << 10) | __tmp << 5 | 3) + .word (0xb0 << 22 | ((THREAD_FPR4-THREAD_FPR0) << 10) | __tmp << 5 | 4) + .word (0xb0 << 22 | ((THREAD_FPR5-THREAD_FPR0) << 10) | __tmp << 5 | 5) + .word (0xb0 << 22 | ((THREAD_FPR6-THREAD_FPR0) << 10) | __tmp << 5 | 6) + .word (0xb0 << 22 | ((THREAD_FPR7-THREAD_FPR0) << 10) | __tmp << 5 | 7) + .word (0xb0 << 22 | ((THREAD_FPR8-THREAD_FPR0) << 10) | __tmp << 5 | 8) + .word (0xb0 << 22 | ((THREAD_FPR9-THREAD_FPR0) << 10) | __tmp << 5 | 9) + .word (0xb0 << 22 | ((THREAD_FPR10-THREAD_FPR0) << 10) | __tmp << 5 | 10) + .word (0xb0 << 22 | ((THREAD_FPR11-THREAD_FPR0) << 10) | __tmp << 5 | 11) + .word (0xb0 << 22 | ((THREAD_FPR12-THREAD_FPR0) << 10) | __tmp << 5 | 12) + .word (0xb0 << 22 | ((THREAD_FPR13-THREAD_FPR0) << 10) | __tmp << 5 | 13) + .word (0xb0 << 22 | ((THREAD_FPR14-THREAD_FPR0) << 10) | __tmp << 5 | 14) + .word (0xb0 << 22 | ((THREAD_FPR15-THREAD_FPR0) << 10) | __tmp << 5 | 15) + .word (0xb0 << 22 | ((THREAD_FPR16-THREAD_FPR0) << 10) | __tmp << 5 | 16) + .word (0xb0 << 22 | ((THREAD_FPR17-THREAD_FPR0) << 10) | __tmp << 5 | 17) + .word (0xb0 << 22 | ((THREAD_FPR18-THREAD_FPR0) << 10) | __tmp << 5 | 18) + .word (0xb0 << 22 | ((THREAD_FPR19-THREAD_FPR0) << 10) | __tmp << 5 | 19) + .word (0xb0 << 22 | ((THREAD_FPR20-THREAD_FPR0) << 10) | __tmp << 5 | 20) + .word (0xb0 << 22 | ((THREAD_FPR21-THREAD_FPR0) << 10) | __tmp << 5 | 21) + .word (0xb0 << 22 | ((THREAD_FPR22-THREAD_FPR0) << 10) | __tmp << 5 | 22) + .word (0xb0 << 22 | ((THREAD_FPR23-THREAD_FPR0) << 10) | __tmp << 5 | 23) + .word (0xb0 << 22 | ((THREAD_FPR24-THREAD_FPR0) << 10) | __tmp << 5 | 24) + .word (0xb0 << 22 | ((THREAD_FPR25-THREAD_FPR0) << 10) | __tmp << 5 | 25) + .word (0xb0 << 22 | ((THREAD_FPR26-THREAD_FPR0) << 10) | __tmp << 5 | 26) + .word (0xb0 << 22 | ((THREAD_FPR27-THREAD_FPR0) << 10) | __tmp << 5 | 27) + .word (0xb0 << 22 | ((THREAD_FPR28-THREAD_FPR0) << 10) | __tmp << 5 | 28) + .word (0xb0 << 22 | ((THREAD_FPR29-THREAD_FPR0) << 10) | __tmp << 5 | 29) + .word (0xb0 << 22 | ((THREAD_FPR30-THREAD_FPR0) << 10) | __tmp << 5 | 30) + .word (0xb0 << 22 | ((THREAD_FPR31-THREAD_FPR0) << 10) | __tmp << 5 | 31) + .endm + + .macro lsx_save_all thread tmp0 tmp1 + fpu_save_cc \thread, \tmp0, \tmp1 + fpu_save_csr \thread, \tmp0 + lsx_save_data \thread, \tmp0 + .endm + + .macro lsx_restore_all thread tmp0 tmp1 + lsx_restore_data \thread, \tmp0 + fpu_restore_cc \thread, \tmp0, \tmp1 + fpu_restore_csr \thread, \tmp0 + .endm + + .macro lsx_save_upper vd base tmp off + parse_vr __vd, \vd + parse_r __tmp, \tmp + /* vpickve2gr opcode is 0xe5dfe */ + .word (0xe5dfe << 11 | 1 << 10 | __vd << 5 | __tmp) + st.d \tmp, \base, (\off+8) + .endm + + .macro lsx_save_all_upper thread base tmp + li.w \tmp, THREAD_FPR0 + PTR_ADD \base, \thread, \tmp + lsx_save_upper $vr0, \base, \tmp, (THREAD_FPR0-THREAD_FPR0) + lsx_save_upper $vr1, \base, \tmp, (THREAD_FPR1-THREAD_FPR0) + lsx_save_upper $vr2, \base, \tmp, (THREAD_FPR2-THREAD_FPR0) + lsx_save_upper $vr3, \base, \tmp, (THREAD_FPR3-THREAD_FPR0) + lsx_save_upper $vr4, \base, \tmp, (THREAD_FPR4-THREAD_FPR0) + lsx_save_upper $vr5, \base, \tmp, (THREAD_FPR5-THREAD_FPR0) + lsx_save_upper $vr6, \base, \tmp, (THREAD_FPR6-THREAD_FPR0) + lsx_save_upper $vr7, \base, \tmp, (THREAD_FPR7-THREAD_FPR0) + lsx_save_upper $vr8, \base, \tmp, (THREAD_FPR8-THREAD_FPR0) + lsx_save_upper $vr9, \base, \tmp, (THREAD_FPR9-THREAD_FPR0) + lsx_save_upper $vr10, \base, \tmp, (THREAD_FPR10-THREAD_FPR0) + lsx_save_upper $vr11, \base, \tmp, (THREAD_FPR11-THREAD_FPR0) + lsx_save_upper $vr12, \base, \tmp, (THREAD_FPR12-THREAD_FPR0) + lsx_save_upper $vr13, \base, \tmp, (THREAD_FPR13-THREAD_FPR0) + lsx_save_upper $vr14, \base, \tmp, (THREAD_FPR14-THREAD_FPR0) + lsx_save_upper $vr15, \base, \tmp, (THREAD_FPR15-THREAD_FPR0) + lsx_save_upper $vr16, \base, \tmp, (THREAD_FPR16-THREAD_FPR0) + lsx_save_upper $vr17, \base, \tmp, (THREAD_FPR17-THREAD_FPR0) + lsx_save_upper $vr18, \base, \tmp, (THREAD_FPR18-THREAD_FPR0) + lsx_save_upper $vr19, \base, \tmp, (THREAD_FPR19-THREAD_FPR0) + lsx_save_upper $vr20, \base, \tmp, (THREAD_FPR20-THREAD_FPR0) + lsx_save_upper $vr21, \base, \tmp, (THREAD_FPR21-THREAD_FPR0) + lsx_save_upper $vr22, \base, \tmp, (THREAD_FPR22-THREAD_FPR0) + lsx_save_upper $vr23, \base, \tmp, (THREAD_FPR23-THREAD_FPR0) + lsx_save_upper $vr24, \base, \tmp, (THREAD_FPR24-THREAD_FPR0) + lsx_save_upper $vr25, \base, \tmp, (THREAD_FPR25-THREAD_FPR0) + lsx_save_upper $vr26, \base, \tmp, (THREAD_FPR26-THREAD_FPR0) + lsx_save_upper $vr27, \base, \tmp, (THREAD_FPR27-THREAD_FPR0) + lsx_save_upper $vr28, \base, \tmp, (THREAD_FPR28-THREAD_FPR0) + lsx_save_upper $vr29, \base, \tmp, (THREAD_FPR29-THREAD_FPR0) + lsx_save_upper $vr30, \base, \tmp, (THREAD_FPR30-THREAD_FPR0) + lsx_save_upper $vr31, \base, \tmp, (THREAD_FPR31-THREAD_FPR0) + .endm + + .macro lsx_restore_upper vd base tmp off + parse_vr __vd, \vd + parse_r __tmp, \tmp + ld.d \tmp, \base, (\off+8) + /* vinsgr2vr opcode is 0xe5d7e */ + .word (0xe5d7e << 11 | 1 << 10 | __tmp << 5 | __vd) + .endm + + .macro lsx_restore_all_upper thread base tmp + li.w \tmp, THREAD_FPR0 + PTR_ADD \base, \thread, \tmp + lsx_restore_upper $vr0, \base, \tmp, (THREAD_FPR0-THREAD_FPR0) + lsx_restore_upper $vr1, \base, \tmp, (THREAD_FPR1-THREAD_FPR0) + lsx_restore_upper $vr2, \base, \tmp, (THREAD_FPR2-THREAD_FPR0) + lsx_restore_upper $vr3, \base, \tmp, (THREAD_FPR3-THREAD_FPR0) + lsx_restore_upper $vr4, \base, \tmp, (THREAD_FPR4-THREAD_FPR0) + lsx_restore_upper $vr5, \base, \tmp, (THREAD_FPR5-THREAD_FPR0) + lsx_restore_upper $vr6, \base, \tmp, (THREAD_FPR6-THREAD_FPR0) + lsx_restore_upper $vr7, \base, \tmp, (THREAD_FPR7-THREAD_FPR0) + lsx_restore_upper $vr8, \base, \tmp, (THREAD_FPR8-THREAD_FPR0) + lsx_restore_upper $vr9, \base, \tmp, (THREAD_FPR9-THREAD_FPR0) + lsx_restore_upper $vr10, \base, \tmp, (THREAD_FPR10-THREAD_FPR0) + lsx_restore_upper $vr11, \base, \tmp, (THREAD_FPR11-THREAD_FPR0) + lsx_restore_upper $vr12, \base, \tmp, (THREAD_FPR12-THREAD_FPR0) + lsx_restore_upper $vr13, \base, \tmp, (THREAD_FPR13-THREAD_FPR0) + lsx_restore_upper $vr14, \base, \tmp, (THREAD_FPR14-THREAD_FPR0) + lsx_restore_upper $vr15, \base, \tmp, (THREAD_FPR15-THREAD_FPR0) + lsx_restore_upper $vr16, \base, \tmp, (THREAD_FPR16-THREAD_FPR0) + lsx_restore_upper $vr17, \base, \tmp, (THREAD_FPR17-THREAD_FPR0) + lsx_restore_upper $vr18, \base, \tmp, (THREAD_FPR18-THREAD_FPR0) + lsx_restore_upper $vr19, \base, \tmp, (THREAD_FPR19-THREAD_FPR0) + lsx_restore_upper $vr20, \base, \tmp, (THREAD_FPR20-THREAD_FPR0) + lsx_restore_upper $vr21, \base, \tmp, (THREAD_FPR21-THREAD_FPR0) + lsx_restore_upper $vr22, \base, \tmp, (THREAD_FPR22-THREAD_FPR0) + lsx_restore_upper $vr23, \base, \tmp, (THREAD_FPR23-THREAD_FPR0) + lsx_restore_upper $vr24, \base, \tmp, (THREAD_FPR24-THREAD_FPR0) + lsx_restore_upper $vr25, \base, \tmp, (THREAD_FPR25-THREAD_FPR0) + lsx_restore_upper $vr26, \base, \tmp, (THREAD_FPR26-THREAD_FPR0) + lsx_restore_upper $vr27, \base, \tmp, (THREAD_FPR27-THREAD_FPR0) + lsx_restore_upper $vr28, \base, \tmp, (THREAD_FPR28-THREAD_FPR0) + lsx_restore_upper $vr29, \base, \tmp, (THREAD_FPR29-THREAD_FPR0) + lsx_restore_upper $vr30, \base, \tmp, (THREAD_FPR30-THREAD_FPR0) + lsx_restore_upper $vr31, \base, \tmp, (THREAD_FPR31-THREAD_FPR0) + .endm + + .macro lsx_init_upper vd tmp + parse_vr __vd, \vd + parse_r __tmp, \tmp + /* vinsgr2vr opcode is 0xe5d7e */ + .word (0xe5d7e << 11 | 1 << 10 | __tmp << 5 | __vd) + .endm + + .macro lsx_init_all_upper tmp + not \tmp, zero + lsx_init_upper $vr0 \tmp + lsx_init_upper $vr1 \tmp + lsx_init_upper $vr2 \tmp + lsx_init_upper $vr3 \tmp + lsx_init_upper $vr4 \tmp + lsx_init_upper $vr5 \tmp + lsx_init_upper $vr6 \tmp + lsx_init_upper $vr7 \tmp + lsx_init_upper $vr8 \tmp + lsx_init_upper $vr9 \tmp + lsx_init_upper $vr10 \tmp + lsx_init_upper $vr11 \tmp + lsx_init_upper $vr12 \tmp + lsx_init_upper $vr13 \tmp + lsx_init_upper $vr14 \tmp + lsx_init_upper $vr15 \tmp + lsx_init_upper $vr16 \tmp + lsx_init_upper $vr17 \tmp + lsx_init_upper $vr18 \tmp + lsx_init_upper $vr19 \tmp + lsx_init_upper $vr20 \tmp + lsx_init_upper $vr21 \tmp + lsx_init_upper $vr22 \tmp + lsx_init_upper $vr23 \tmp + lsx_init_upper $vr24 \tmp + lsx_init_upper $vr25 \tmp + lsx_init_upper $vr26 \tmp + lsx_init_upper $vr27 \tmp + lsx_init_upper $vr28 \tmp + lsx_init_upper $vr29 \tmp + lsx_init_upper $vr30 \tmp + lsx_init_upper $vr31 \tmp + .endm + + .macro lasx_save_data thread tmp + parse_r __tmp, \tmp + li.w \tmp, THREAD_FPR0 + PTR_ADD \tmp, \thread, \tmp + /* xvst opcode is 0xb3 */ + .word (0xb3 << 22 | ((THREAD_FPR0-THREAD_FPR0) << 10) | __tmp << 5 | 0) + .word (0xb3 << 22 | ((THREAD_FPR1-THREAD_FPR0) << 10) | __tmp << 5 | 1) + .word (0xb3 << 22 | ((THREAD_FPR2-THREAD_FPR0) << 10) | __tmp << 5 | 2) + .word (0xb3 << 22 | ((THREAD_FPR3-THREAD_FPR0) << 10) | __tmp << 5 | 3) + .word (0xb3 << 22 | ((THREAD_FPR4-THREAD_FPR0) << 10) | __tmp << 5 | 4) + .word (0xb3 << 22 | ((THREAD_FPR5-THREAD_FPR0) << 10) | __tmp << 5 | 5) + .word (0xb3 << 22 | ((THREAD_FPR6-THREAD_FPR0) << 10) | __tmp << 5 | 6) + .word (0xb3 << 22 | ((THREAD_FPR7-THREAD_FPR0) << 10) | __tmp << 5 | 7) + .word (0xb3 << 22 | ((THREAD_FPR8-THREAD_FPR0) << 10) | __tmp << 5 | 8) + .word (0xb3 << 22 | ((THREAD_FPR9-THREAD_FPR0) << 10) | __tmp << 5 | 9) + .word (0xb3 << 22 | ((THREAD_FPR10-THREAD_FPR0) << 10) | __tmp << 5 | 10) + .word (0xb3 << 22 | ((THREAD_FPR11-THREAD_FPR0) << 10) | __tmp << 5 | 11) + .word (0xb3 << 22 | ((THREAD_FPR12-THREAD_FPR0) << 10) | __tmp << 5 | 12) + .word (0xb3 << 22 | ((THREAD_FPR13-THREAD_FPR0) << 10) | __tmp << 5 | 13) + .word (0xb3 << 22 | ((THREAD_FPR14-THREAD_FPR0) << 10) | __tmp << 5 | 14) + .word (0xb3 << 22 | ((THREAD_FPR15-THREAD_FPR0) << 10) | __tmp << 5 | 15) + .word (0xb3 << 22 | ((THREAD_FPR16-THREAD_FPR0) << 10) | __tmp << 5 | 16) + .word (0xb3 << 22 | ((THREAD_FPR17-THREAD_FPR0) << 10) | __tmp << 5 | 17) + .word (0xb3 << 22 | ((THREAD_FPR18-THREAD_FPR0) << 10) | __tmp << 5 | 18) + .word (0xb3 << 22 | ((THREAD_FPR19-THREAD_FPR0) << 10) | __tmp << 5 | 19) + .word (0xb3 << 22 | ((THREAD_FPR20-THREAD_FPR0) << 10) | __tmp << 5 | 20) + .word (0xb3 << 22 | ((THREAD_FPR21-THREAD_FPR0) << 10) | __tmp << 5 | 21) + .word (0xb3 << 22 | ((THREAD_FPR22-THREAD_FPR0) << 10) | __tmp << 5 | 22) + .word (0xb3 << 22 | ((THREAD_FPR23-THREAD_FPR0) << 10) | __tmp << 5 | 23) + .word (0xb3 << 22 | ((THREAD_FPR24-THREAD_FPR0) << 10) | __tmp << 5 | 24) + .word (0xb3 << 22 | ((THREAD_FPR25-THREAD_FPR0) << 10) | __tmp << 5 | 25) + .word (0xb3 << 22 | ((THREAD_FPR26-THREAD_FPR0) << 10) | __tmp << 5 | 26) + .word (0xb3 << 22 | ((THREAD_FPR27-THREAD_FPR0) << 10) | __tmp << 5 | 27) + .word (0xb3 << 22 | ((THREAD_FPR28-THREAD_FPR0) << 10) | __tmp << 5 | 28) + .word (0xb3 << 22 | ((THREAD_FPR29-THREAD_FPR0) << 10) | __tmp << 5 | 29) + .word (0xb3 << 22 | ((THREAD_FPR30-THREAD_FPR0) << 10) | __tmp << 5 | 30) + .word (0xb3 << 22 | ((THREAD_FPR31-THREAD_FPR0) << 10) | __tmp << 5 | 31) + .endm + + .macro lasx_restore_data thread tmp + parse_r __tmp, \tmp + li.w \tmp, THREAD_FPR0 + PTR_ADD \tmp, \thread, \tmp + /* xvld opcode is 0xb2 */ + .word (0xb2 << 22 | ((THREAD_FPR0-THREAD_FPR0) << 10) | __tmp << 5 | 0) + .word (0xb2 << 22 | ((THREAD_FPR1-THREAD_FPR0) << 10) | __tmp << 5 | 1) + .word (0xb2 << 22 | ((THREAD_FPR2-THREAD_FPR0) << 10) | __tmp << 5 | 2) + .word (0xb2 << 22 | ((THREAD_FPR3-THREAD_FPR0) << 10) | __tmp << 5 | 3) + .word (0xb2 << 22 | ((THREAD_FPR4-THREAD_FPR0) << 10) | __tmp << 5 | 4) + .word (0xb2 << 22 | ((THREAD_FPR5-THREAD_FPR0) << 10) | __tmp << 5 | 5) + .word (0xb2 << 22 | ((THREAD_FPR6-THREAD_FPR0) << 10) | __tmp << 5 | 6) + .word (0xb2 << 22 | ((THREAD_FPR7-THREAD_FPR0) << 10) | __tmp << 5 | 7) + .word (0xb2 << 22 | ((THREAD_FPR8-THREAD_FPR0) << 10) | __tmp << 5 | 8) + .word (0xb2 << 22 | ((THREAD_FPR9-THREAD_FPR0) << 10) | __tmp << 5 | 9) + .word (0xb2 << 22 | ((THREAD_FPR10-THREAD_FPR0) << 10) | __tmp << 5 | 10) + .word (0xb2 << 22 | ((THREAD_FPR11-THREAD_FPR0) << 10) | __tmp << 5 | 11) + .word (0xb2 << 22 | ((THREAD_FPR12-THREAD_FPR0) << 10) | __tmp << 5 | 12) + .word (0xb2 << 22 | ((THREAD_FPR13-THREAD_FPR0) << 10) | __tmp << 5 | 13) + .word (0xb2 << 22 | ((THREAD_FPR14-THREAD_FPR0) << 10) | __tmp << 5 | 14) + .word (0xb2 << 22 | ((THREAD_FPR15-THREAD_FPR0) << 10) | __tmp << 5 | 15) + .word (0xb2 << 22 | ((THREAD_FPR16-THREAD_FPR0) << 10) | __tmp << 5 | 16) + .word (0xb2 << 22 | ((THREAD_FPR17-THREAD_FPR0) << 10) | __tmp << 5 | 17) + .word (0xb2 << 22 | ((THREAD_FPR18-THREAD_FPR0) << 10) | __tmp << 5 | 18) + .word (0xb2 << 22 | ((THREAD_FPR19-THREAD_FPR0) << 10) | __tmp << 5 | 19) + .word (0xb2 << 22 | ((THREAD_FPR20-THREAD_FPR0) << 10) | __tmp << 5 | 20) + .word (0xb2 << 22 | ((THREAD_FPR21-THREAD_FPR0) << 10) | __tmp << 5 | 21) + .word (0xb2 << 22 | ((THREAD_FPR22-THREAD_FPR0) << 10) | __tmp << 5 | 22) + .word (0xb2 << 22 | ((THREAD_FPR23-THREAD_FPR0) << 10) | __tmp << 5 | 23) + .word (0xb2 << 22 | ((THREAD_FPR24-THREAD_FPR0) << 10) | __tmp << 5 | 24) + .word (0xb2 << 22 | ((THREAD_FPR25-THREAD_FPR0) << 10) | __tmp << 5 | 25) + .word (0xb2 << 22 | ((THREAD_FPR26-THREAD_FPR0) << 10) | __tmp << 5 | 26) + .word (0xb2 << 22 | ((THREAD_FPR27-THREAD_FPR0) << 10) | __tmp << 5 | 27) + .word (0xb2 << 22 | ((THREAD_FPR28-THREAD_FPR0) << 10) | __tmp << 5 | 28) + .word (0xb2 << 22 | ((THREAD_FPR29-THREAD_FPR0) << 10) | __tmp << 5 | 29) + .word (0xb2 << 22 | ((THREAD_FPR30-THREAD_FPR0) << 10) | __tmp << 5 | 30) + .word (0xb2 << 22 | ((THREAD_FPR31-THREAD_FPR0) << 10) | __tmp << 5 | 31) + .endm + + .macro lasx_save_all thread tmp0 tmp1 + fpu_save_cc \thread, \tmp0, \tmp1 + fpu_save_csr \thread, \tmp0 + lasx_save_data \thread, \tmp0 + .endm + + .macro lasx_restore_all thread tmp0 tmp1 + lasx_restore_data \thread, \tmp0 + fpu_restore_cc \thread, \tmp0, \tmp1 + fpu_restore_csr \thread, \tmp0 + .endm + + .macro lasx_save_upper xd base tmp off + /* Nothing */ + .endm + + .macro lasx_save_all_upper thread base tmp + /* Nothing */ + .endm + + .macro lasx_restore_upper xd base tmp off + parse_xr __xd, \xd + parse_xr __xt, \tmp + parse_r __base, \base + /* vld opcode is 0xb0 */ + .word (0xb0 << 22 | (\off+16) << 10 | __base << 5 | __xt) + /* xvpermi.q opcode is 0x1dfb */ + .word (0x1dfb << 18 | 0x2 << 10 | __xt << 5 | __xd) + .endm + + .macro lasx_restore_all_upper thread base tmp + li.w \tmp, THREAD_FPR0 + PTR_ADD \base, \thread, \tmp + /* Save $vr31, xvpickve2gr opcode is 0x76efe */ + .word (0x76efe << 12 | 0 << 10 | 31 << 5 | 0x11) + .word (0x76efe << 12 | 1 << 10 | 31 << 5 | 0x12) + lasx_restore_upper $xr0, \base, $xr31, (THREAD_FPR0-THREAD_FPR0) + lasx_restore_upper $xr1, \base, $xr31, (THREAD_FPR1-THREAD_FPR0) + lasx_restore_upper $xr2, \base, $xr31, (THREAD_FPR2-THREAD_FPR0) + lasx_restore_upper $xr3, \base, $xr31, (THREAD_FPR3-THREAD_FPR0) + lasx_restore_upper $xr4, \base, $xr31, (THREAD_FPR4-THREAD_FPR0) + lasx_restore_upper $xr5, \base, $xr31, (THREAD_FPR5-THREAD_FPR0) + lasx_restore_upper $xr6, \base, $xr31, (THREAD_FPR6-THREAD_FPR0) + lasx_restore_upper $xr7, \base, $xr31, (THREAD_FPR7-THREAD_FPR0) + lasx_restore_upper $xr8, \base, $xr31, (THREAD_FPR8-THREAD_FPR0) + lasx_restore_upper $xr9, \base, $xr31, (THREAD_FPR9-THREAD_FPR0) + lasx_restore_upper $xr10, \base, $xr31, (THREAD_FPR10-THREAD_FPR0) + lasx_restore_upper $xr11, \base, $xr31, (THREAD_FPR11-THREAD_FPR0) + lasx_restore_upper $xr12, \base, $xr31, (THREAD_FPR12-THREAD_FPR0) + lasx_restore_upper $xr13, \base, $xr31, (THREAD_FPR13-THREAD_FPR0) + lasx_restore_upper $xr14, \base, $xr31, (THREAD_FPR14-THREAD_FPR0) + lasx_restore_upper $xr15, \base, $xr31, (THREAD_FPR15-THREAD_FPR0) + lasx_restore_upper $xr16, \base, $xr31, (THREAD_FPR16-THREAD_FPR0) + lasx_restore_upper $xr17, \base, $xr31, (THREAD_FPR17-THREAD_FPR0) + lasx_restore_upper $xr18, \base, $xr31, (THREAD_FPR18-THREAD_FPR0) + lasx_restore_upper $xr19, \base, $xr31, (THREAD_FPR19-THREAD_FPR0) + lasx_restore_upper $xr20, \base, $xr31, (THREAD_FPR20-THREAD_FPR0) + lasx_restore_upper $xr21, \base, $xr31, (THREAD_FPR21-THREAD_FPR0) + lasx_restore_upper $xr22, \base, $xr31, (THREAD_FPR22-THREAD_FPR0) + lasx_restore_upper $xr23, \base, $xr31, (THREAD_FPR23-THREAD_FPR0) + lasx_restore_upper $xr24, \base, $xr31, (THREAD_FPR24-THREAD_FPR0) + lasx_restore_upper $xr25, \base, $xr31, (THREAD_FPR25-THREAD_FPR0) + lasx_restore_upper $xr26, \base, $xr31, (THREAD_FPR26-THREAD_FPR0) + lasx_restore_upper $xr27, \base, $xr31, (THREAD_FPR27-THREAD_FPR0) + lasx_restore_upper $xr28, \base, $xr31, (THREAD_FPR28-THREAD_FPR0) + lasx_restore_upper $xr29, \base, $xr31, (THREAD_FPR29-THREAD_FPR0) + lasx_restore_upper $xr30, \base, $xr31, (THREAD_FPR30-THREAD_FPR0) + lasx_restore_upper $xr31, \base, $xr31, (THREAD_FPR31-THREAD_FPR0) + /* Restore $vr31, xvinsgr2vr opcode is 0x76ebe */ + .word (0x76ebe << 12 | 0 << 10 | 0x11 << 5 | 31) + .word (0x76ebe << 12 | 1 << 10 | 0x12 << 5 | 31) + .endm + + .macro lasx_init_upper xd tmp + parse_xr __xd, \xd + parse_r __tmp, \tmp + /* xvinsgr2vr opcode is 0x76ebe */ + .word (0x76ebe << 12 | 2 << 10 | __tmp << 5 | __xd) + .word (0x76ebe << 12 | 3 << 10 | __tmp << 5 | __xd) + .endm + + .macro lasx_init_all_upper tmp + not \tmp, zero + lasx_init_upper $xr0 \tmp + lasx_init_upper $xr1 \tmp + lasx_init_upper $xr2 \tmp + lasx_init_upper $xr3 \tmp + lasx_init_upper $xr4 \tmp + lasx_init_upper $xr5 \tmp + lasx_init_upper $xr6 \tmp + lasx_init_upper $xr7 \tmp + lasx_init_upper $xr8 \tmp + lasx_init_upper $xr9 \tmp + lasx_init_upper $xr10 \tmp + lasx_init_upper $xr11 \tmp + lasx_init_upper $xr12 \tmp + lasx_init_upper $xr13 \tmp + lasx_init_upper $xr14 \tmp + lasx_init_upper $xr15 \tmp + lasx_init_upper $xr16 \tmp + lasx_init_upper $xr17 \tmp + lasx_init_upper $xr18 \tmp + lasx_init_upper $xr19 \tmp + lasx_init_upper $xr20 \tmp + lasx_init_upper $xr21 \tmp + lasx_init_upper $xr22 \tmp + lasx_init_upper $xr23 \tmp + lasx_init_upper $xr24 \tmp + lasx_init_upper $xr25 \tmp + lasx_init_upper $xr26 \tmp + lasx_init_upper $xr27 \tmp + lasx_init_upper $xr28 \tmp + lasx_init_upper $xr29 \tmp + lasx_init_upper $xr30 \tmp + lasx_init_upper $xr31 \tmp + .endm + .macro not dst src nor \dst, \src, zero .endm diff --git a/arch/loongarch/include/asm/fpu.h b/arch/loongarch/include/asm/fpu.h index 358b254d9c1d..eaa8030f2d9c 100644 --- a/arch/loongarch/include/asm/fpu.h +++ b/arch/loongarch/include/asm/fpu.h @@ -25,6 +25,26 @@ extern void _init_fpu(unsigned int); extern void _save_fp(struct loongarch_fpu *); extern void _restore_fp(struct loongarch_fpu *); +extern void _save_lsx(struct loongarch_fpu *fpu); +extern void _restore_lsx(struct loongarch_fpu *fpu); +extern void _init_lsx_upper(void); +extern void _restore_lsx_upper(struct loongarch_fpu *fpu); + +extern void _save_lasx(struct loongarch_fpu *fpu); +extern void _restore_lasx(struct loongarch_fpu *fpu); +extern void _init_lasx_upper(void); +extern void _restore_lasx_upper(struct loongarch_fpu *fpu); + +static inline void enable_lsx(void); +static inline void disable_lsx(void); +static inline void save_lsx(struct task_struct *t); +static inline void restore_lsx(struct task_struct *t); + +static inline void enable_lasx(void); +static inline void disable_lasx(void); +static inline void save_lasx(struct task_struct *t); +static inline void restore_lasx(struct task_struct *t); + /* * Mask the FCSR Cause bits according to the Enable bits, observing * that Unimplemented is always enabled. @@ -41,6 +61,29 @@ static inline int is_fp_enabled(void) 1 : 0; } +static inline int is_lsx_enabled(void) +{ + if (!cpu_has_lsx) + return 0; + + return (csr_read32(LOONGARCH_CSR_EUEN) & CSR_EUEN_LSXEN) ? + 1 : 0; +} + +static inline int is_lasx_enabled(void) +{ + if (!cpu_has_lasx) + return 0; + + return (csr_read32(LOONGARCH_CSR_EUEN) & CSR_EUEN_LASXEN) ? + 1 : 0; +} + +static inline int is_simd_enabled(void) +{ + return is_lsx_enabled() | is_lasx_enabled(); +} + #define enable_fpu() set_csr_euen(CSR_EUEN_FPEN) #define disable_fpu() clear_csr_euen(CSR_EUEN_FPEN) @@ -78,9 +121,22 @@ static inline void own_fpu(int restore) static inline void lose_fpu_inatomic(int save, struct task_struct *tsk) { if (is_fpu_owner()) { - if (save) - _save_fp(&tsk->thread.fpu); - disable_fpu(); + if (is_simd_enabled()) { + if (save) { + if (is_lasx_enabled()) + save_lasx(tsk); + else + save_lsx(tsk); + } + disable_fpu(); + disable_lsx(); + disable_lasx(); + clear_tsk_thread_flag(tsk, TIF_USEDSIMD); + } else { + if (save) + _save_fp(&tsk->thread.fpu); + disable_fpu(); + } clear_tsk_thread_flag(tsk, TIF_USEDFPU); } KSTK_EUEN(tsk) &= ~(CSR_EUEN_FPEN | CSR_EUEN_LSXEN | CSR_EUEN_LASXEN); @@ -126,4 +182,140 @@ static inline union fpureg *get_fpu_regs(struct task_struct *tsk) return tsk->thread.fpu.fpr; } +enum { + CTX_LSX = 1, + CTX_LASX = 2, +}; + +static inline int is_simd_owner(void) +{ + return test_thread_flag(TIF_USEDSIMD); +} + +#ifdef CONFIG_CPU_HAS_LSX + +static inline void enable_lsx(void) +{ + if (cpu_has_lsx) + csr_xchg32(CSR_EUEN_LSXEN, CSR_EUEN_LSXEN, LOONGARCH_CSR_EUEN); +} + +static inline void disable_lsx(void) +{ + if (cpu_has_lsx) + csr_xchg32(0, CSR_EUEN_LSXEN, LOONGARCH_CSR_EUEN); +} + +static inline void save_lsx(struct task_struct *t) +{ + if (cpu_has_lsx) + _save_lsx(&t->thread.fpu); +} + +static inline void restore_lsx(struct task_struct *t) +{ + if (cpu_has_lsx) + _restore_lsx(&t->thread.fpu); +} + +static inline void init_lsx_upper(void) +{ + /* + * Check cpu_has_lsx only if it's a constant. This will allow the + * compiler to optimise out code for CPUs without LSX without adding + * an extra redundant check for CPUs with LSX. + */ + if (__builtin_constant_p(cpu_has_lsx) && !cpu_has_lsx) + return; + + _init_lsx_upper(); +} + +static inline void restore_lsx_upper(struct task_struct *t) +{ + if (cpu_has_lsx) + _restore_lsx_upper(&t->thread.fpu); +} + +#else +static inline void enable_lsx(void) {} +static inline void disable_lsx(void) {} +static inline void save_lsx(struct task_struct *t) {} +static inline void restore_lsx(struct task_struct *t) {} +static inline void init_lsx_upper(void) {} +static inline void restore_lsx_upper(struct task_struct *t) {} +#endif + +#ifdef CONFIG_CPU_HAS_LASX + +static inline void enable_lasx(void) +{ + + if (cpu_has_lasx) + csr_xchg32(CSR_EUEN_LASXEN, CSR_EUEN_LASXEN, LOONGARCH_CSR_EUEN); +} + +static inline void disable_lasx(void) +{ + if (cpu_has_lasx) + csr_xchg32(0, CSR_EUEN_LASXEN, LOONGARCH_CSR_EUEN); +} + +static inline void save_lasx(struct task_struct *t) +{ + if (cpu_has_lasx) + _save_lasx(&t->thread.fpu); +} + +static inline void restore_lasx(struct task_struct *t) +{ + if (cpu_has_lasx) + _restore_lasx(&t->thread.fpu); +} + +static inline void init_lasx_upper(void) +{ + if (cpu_has_lasx) + _init_lasx_upper(); +} + +static inline void restore_lasx_upper(struct task_struct *t) +{ + if (cpu_has_lasx) + _restore_lasx_upper(&t->thread.fpu); +} + +#else +static inline void enable_lasx(void) {} +static inline void disable_lasx(void) {} +static inline void save_lasx(struct task_struct *t) {} +static inline void restore_lasx(struct task_struct *t) {} +static inline void init_lasx_upper(void) {} +static inline void restore_lasx_upper(struct task_struct *t) {} +#endif + +static inline int thread_lsx_context_live(void) +{ + int ret = 0; + + if (__builtin_constant_p(cpu_has_lsx) && !cpu_has_lsx) + goto out; + + ret = test_thread_flag(TIF_LSX_CTX_LIVE) ? CTX_LSX : 0; +out: + return ret; +} + +static inline int thread_lasx_context_live(void) +{ + int ret = 0; + + if (__builtin_constant_p(cpu_has_lasx) && !cpu_has_lasx) + goto out; + + ret = test_thread_flag(TIF_LASX_CTX_LIVE) ? CTX_LASX : 0; +out: + return ret; +} + #endif /* _ASM_FPU_H */ diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c index 529ab8f44ec6..3dd31afb2ae6 100644 --- a/arch/loongarch/kernel/cpu-probe.c +++ b/arch/loongarch/kernel/cpu-probe.c @@ -111,6 +111,18 @@ static void cpu_probe_common(struct cpuinfo_loongarch *c) c->options |= LOONGARCH_CPU_FPU; elf_hwcap |= HWCAP_LOONGARCH_FPU; } +#ifdef CONFIG_CPU_HAS_LSX + if (config & CPUCFG2_LSX) { + c->options |= LOONGARCH_CPU_LSX; + elf_hwcap |= HWCAP_LOONGARCH_LSX; + } +#endif +#ifdef CONFIG_CPU_HAS_LASX + if (config & CPUCFG2_LASX) { + c->options |= LOONGARCH_CPU_LASX; + elf_hwcap |= HWCAP_LOONGARCH_LASX; + } +#endif if (config & CPUCFG2_COMPLEX) { c->options |= LOONGARCH_CPU_COMPLEX; elf_hwcap |= HWCAP_LOONGARCH_COMPLEX; diff --git a/arch/loongarch/kernel/fpu.S b/arch/loongarch/kernel/fpu.S index 576b3370a296..65d245a0f409 100644 --- a/arch/loongarch/kernel/fpu.S +++ b/arch/loongarch/kernel/fpu.S @@ -26,6 +26,32 @@ .previous .endm + .macro EX_V insn, reg, src, offs + parse_v __insn, \insn + parse_v __offs, \offs + parse_r __src, \src + parse_vr __reg, \reg + +.ex\@: + .word __insn << 22 | __offs << 10 | __src << 5 | __reg + .section __ex_table,"a" + PTR .ex\@, fault + .previous + .endm + + .macro EX_XV insn, reg, src, offs + parse_v __insn, \insn + parse_v __offs, \offs + parse_r __src, \src + parse_xr __reg, \reg + +.ex\@: + .word __insn << 22 | __offs << 10 | __src << 5 | __reg + .section __ex_table,"a" + PTR .ex\@, fault + .previous + .endm + .macro sc_save_fp base EX fst.d $f0, \base, (0 * FPU_REG_WIDTH) EX fst.d $f1, \base, (1 * FPU_REG_WIDTH) @@ -146,6 +172,146 @@ movgr2fcsr fcsr0, \tmp0 .endm + .macro sc_save_lsx base + EX_V 0xb1 $vr0, \base, (0 * LSX_REG_WIDTH) + EX_V 0xb1 $vr1, \base, (1 * LSX_REG_WIDTH) + EX_V 0xb1 $vr2, \base, (2 * LSX_REG_WIDTH) + EX_V 0xb1 $vr3, \base, (3 * LSX_REG_WIDTH) + EX_V 0xb1 $vr4, \base, (4 * LSX_REG_WIDTH) + EX_V 0xb1 $vr5, \base, (5 * LSX_REG_WIDTH) + EX_V 0xb1 $vr6, \base, (6 * LSX_REG_WIDTH) + EX_V 0xb1 $vr7, \base, (7 * LSX_REG_WIDTH) + EX_V 0xb1 $vr8, \base, (8 * LSX_REG_WIDTH) + EX_V 0xb1 $vr9, \base, (9 * LSX_REG_WIDTH) + EX_V 0xb1 $vr10, \base, (10 * LSX_REG_WIDTH) + EX_V 0xb1 $vr11, \base, (11 * LSX_REG_WIDTH) + EX_V 0xb1 $vr12, \base, (12 * LSX_REG_WIDTH) + EX_V 0xb1 $vr13, \base, (13 * LSX_REG_WIDTH) + EX_V 0xb1 $vr14, \base, (14 * LSX_REG_WIDTH) + EX_V 0xb1 $vr15, \base, (15 * LSX_REG_WIDTH) + EX_V 0xb1 $vr16, \base, (16 * LSX_REG_WIDTH) + EX_V 0xb1 $vr17, \base, (17 * LSX_REG_WIDTH) + EX_V 0xb1 $vr18, \base, (18 * LSX_REG_WIDTH) + EX_V 0xb1 $vr19, \base, (19 * LSX_REG_WIDTH) + EX_V 0xb1 $vr20, \base, (20 * LSX_REG_WIDTH) + EX_V 0xb1 $vr21, \base, (21 * LSX_REG_WIDTH) + EX_V 0xb1 $vr22, \base, (22 * LSX_REG_WIDTH) + EX_V 0xb1 $vr23, \base, (23 * LSX_REG_WIDTH) + EX_V 0xb1 $vr24, \base, (24 * LSX_REG_WIDTH) + EX_V 0xb1 $vr25, \base, (25 * LSX_REG_WIDTH) + EX_V 0xb1 $vr26, \base, (26 * LSX_REG_WIDTH) + EX_V 0xb1 $vr27, \base, (27 * LSX_REG_WIDTH) + EX_V 0xb1 $vr28, \base, (28 * LSX_REG_WIDTH) + EX_V 0xb1 $vr29, \base, (29 * LSX_REG_WIDTH) + EX_V 0xb1 $vr30, \base, (30 * LSX_REG_WIDTH) + EX_V 0xb1 $vr31, \base, (31 * LSX_REG_WIDTH) + .endm + + .macro sc_restore_lsx base + EX_V 0xb0 $vr0, \base, (0 * LSX_REG_WIDTH) + EX_V 0xb0 $vr1, \base, (1 * LSX_REG_WIDTH) + EX_V 0xb0 $vr2, \base, (2 * LSX_REG_WIDTH) + EX_V 0xb0 $vr3, \base, (3 * LSX_REG_WIDTH) + EX_V 0xb0 $vr4, \base, (4 * LSX_REG_WIDTH) + EX_V 0xb0 $vr5, \base, (5 * LSX_REG_WIDTH) + EX_V 0xb0 $vr6, \base, (6 * LSX_REG_WIDTH) + EX_V 0xb0 $vr7, \base, (7 * LSX_REG_WIDTH) + EX_V 0xb0 $vr8, \base, (8 * LSX_REG_WIDTH) + EX_V 0xb0 $vr9, \base, (9 * LSX_REG_WIDTH) + EX_V 0xb0 $vr10, \base, (10 * LSX_REG_WIDTH) + EX_V 0xb0 $vr11, \base, (11 * LSX_REG_WIDTH) + EX_V 0xb0 $vr12, \base, (12 * LSX_REG_WIDTH) + EX_V 0xb0 $vr13, \base, (13 * LSX_REG_WIDTH) + EX_V 0xb0 $vr14, \base, (14 * LSX_REG_WIDTH) + EX_V 0xb0 $vr15, \base, (15 * LSX_REG_WIDTH) + EX_V 0xb0 $vr16, \base, (16 * LSX_REG_WIDTH) + EX_V 0xb0 $vr17, \base, (17 * LSX_REG_WIDTH) + EX_V 0xb0 $vr18, \base, (18 * LSX_REG_WIDTH) + EX_V 0xb0 $vr19, \base, (19 * LSX_REG_WIDTH) + EX_V 0xb0 $vr20, \base, (20 * LSX_REG_WIDTH) + EX_V 0xb0 $vr21, \base, (21 * LSX_REG_WIDTH) + EX_V 0xb0 $vr22, \base, (22 * LSX_REG_WIDTH) + EX_V 0xb0 $vr23, \base, (23 * LSX_REG_WIDTH) + EX_V 0xb0 $vr24, \base, (24 * LSX_REG_WIDTH) + EX_V 0xb0 $vr25, \base, (25 * LSX_REG_WIDTH) + EX_V 0xb0 $vr26, \base, (26 * LSX_REG_WIDTH) + EX_V 0xb0 $vr27, \base, (27 * LSX_REG_WIDTH) + EX_V 0xb0 $vr28, \base, (28 * LSX_REG_WIDTH) + EX_V 0xb0 $vr29, \base, (29 * LSX_REG_WIDTH) + EX_V 0xb0 $vr30, \base, (30 * LSX_REG_WIDTH) + EX_V 0xb0 $vr31, \base, (31 * LSX_REG_WIDTH) + .endm + + .macro sc_save_lasx base + EX_XV 0xb3 $xr0, \base, (0 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr1, \base, (1 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr2, \base, (2 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr3, \base, (3 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr4, \base, (4 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr5, \base, (5 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr6, \base, (6 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr7, \base, (7 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr8, \base, (8 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr9, \base, (9 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr10, \base, (10 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr11, \base, (11 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr12, \base, (12 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr13, \base, (13 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr14, \base, (14 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr15, \base, (15 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr16, \base, (16 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr17, \base, (17 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr18, \base, (18 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr19, \base, (19 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr20, \base, (20 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr21, \base, (21 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr22, \base, (22 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr23, \base, (23 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr24, \base, (24 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr25, \base, (25 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr26, \base, (26 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr27, \base, (27 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr28, \base, (28 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr29, \base, (29 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr30, \base, (30 * LASX_REG_WIDTH) + EX_XV 0xb3 $xr31, \base, (31 * LASX_REG_WIDTH) + .endm + + .macro sc_restore_lasx base + EX_XV 0xb2 $xr0, \base, (0 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr1, \base, (1 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr2, \base, (2 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr3, \base, (3 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr4, \base, (4 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr5, \base, (5 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr6, \base, (6 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr7, \base, (7 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr8, \base, (8 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr9, \base, (9 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr10, \base, (10 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr11, \base, (11 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr12, \base, (12 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr13, \base, (13 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr14, \base, (14 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr15, \base, (15 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr16, \base, (16 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr17, \base, (17 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr18, \base, (18 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr19, \base, (19 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr20, \base, (20 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr21, \base, (21 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr22, \base, (22 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr23, \base, (23 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr24, \base, (24 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr25, \base, (25 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr26, \base, (26 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr27, \base, (27 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr28, \base, (28 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr29, \base, (29 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr30, \base, (30 * LASX_REG_WIDTH) + EX_XV 0xb2 $xr31, \base, (31 * LASX_REG_WIDTH) + .endm + /* * Save a thread's fp context. */ @@ -167,6 +333,76 @@ SYM_FUNC_START(_restore_fp) jr ra SYM_FUNC_END(_restore_fp) +#ifdef CONFIG_CPU_HAS_LSX + +/* + * Save a thread's LSX vector context. + */ +SYM_FUNC_START(_save_lsx) + lsx_save_all a0 t1 t2 + jirl zero, ra, 0 +SYM_FUNC_END(_save_lsx) +EXPORT_SYMBOL(_save_lsx) + +/* + * Restore a thread's LSX vector context. + */ +SYM_FUNC_START(_restore_lsx) + lsx_restore_all a0 t1 t2 + jirl zero, ra, 0 +SYM_FUNC_END(_restore_lsx) + +SYM_FUNC_START(_save_lsx_upper) + lsx_save_all_upper a0 t0 t1 + jirl zero, ra, 0 +SYM_FUNC_END(_save_lsx_upper) + +SYM_FUNC_START(_restore_lsx_upper) + lsx_restore_all_upper a0 t0 t1 + jirl zero, ra, 0 +SYM_FUNC_END(_restore_lsx_upper) + +SYM_FUNC_START(_init_lsx_upper) + lsx_init_all_upper t1 + jirl zero, ra, 0 +SYM_FUNC_END(_init_lsx_upper) +#endif + +#ifdef CONFIG_CPU_HAS_LASX + +/* + * Save a thread's LASX vector context. + */ +SYM_FUNC_START(_save_lasx) + lasx_save_all a0 t1 t2 + jirl zero, ra, 0 +SYM_FUNC_END(_save_lasx) +EXPORT_SYMBOL(_save_lasx) + +/* + * Restore a thread's LASX vector context. + */ +SYM_FUNC_START(_restore_lasx) + lasx_restore_all a0 t1 t2 + jirl zero, ra, 0 +SYM_FUNC_END(_restore_lasx) + +SYM_FUNC_START(_save_lasx_upper) + lasx_save_all_upper a0 t0 t1 + jirl zero, ra, 0 +SYM_FUNC_END(_save_lasx_upper) + +SYM_FUNC_START(_restore_lasx_upper) + lasx_restore_all_upper a0 t0 t1 + jirl zero, ra, 0 +SYM_FUNC_END(_restore_lasx_upper) + +SYM_FUNC_START(_init_lasx_upper) + lasx_init_all_upper t1 + jirl zero, ra, 0 +SYM_FUNC_END(_init_lasx_upper) +#endif + /* * Load the FPU with signalling NANS. This bit pattern we're using has * the property that no matter whether considered as single or as double @@ -245,6 +481,58 @@ SYM_FUNC_START(_restore_fp_context) jr ra SYM_FUNC_END(_restore_fp_context) +/* + * a0: fpregs + * a1: fcc + * a2: fcsr + */ +SYM_FUNC_START(_save_lsx_context) + sc_save_fcc a1, t0, t1 + sc_save_fcsr a2, t0 + sc_save_lsx a0 + li.w a0, 0 # success + jirl zero, ra, 0 +SYM_FUNC_END(_save_lsx_context) + +/* + * a0: fpregs + * a1: fcc + * a2: fcsr + */ +SYM_FUNC_START(_restore_lsx_context) + sc_restore_lsx a0 + sc_restore_fcc a1, t1, t2 + sc_restore_fcsr a2, t1 + li.w a0, 0 # success + jirl zero, ra, 0 +SYM_FUNC_END(_restore_lsx_context) + +/* + * a0: fpregs + * a1: fcc + * a2: fcsr + */ +SYM_FUNC_START(_save_lasx_context) + sc_save_fcc a1, t0, t1 + sc_save_fcsr a2, t0 + sc_save_lasx a0 + li.w a0, 0 # success + jirl zero, ra, 0 +SYM_FUNC_END(_save_lasx_context) + +/* + * a0: fpregs + * a1: fcc + * a2: fcsr + */ +SYM_FUNC_START(_restore_lasx_context) + sc_restore_lasx a0 + sc_restore_fcc a1, t1, t2 + sc_restore_fcsr a2, t1 + li.w a0, 0 # success + jirl zero, ra, 0 +SYM_FUNC_END(_restore_lasx_context) + SYM_FUNC_START(fault) li.w a0, -EFAULT # failure jr ra diff --git a/arch/loongarch/kernel/process.c b/arch/loongarch/kernel/process.c index 95a124058e32..86600e39799e 100644 --- a/arch/loongarch/kernel/process.c +++ b/arch/loongarch/kernel/process.c @@ -105,8 +105,14 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) */ preempt_disable(); - if (is_fpu_owner()) - save_fp(current); + if (is_fpu_owner()) { + if (is_lasx_enabled()) + save_lasx(current); + else if (is_lsx_enabled()) + save_lsx(current); + else + save_fp(current); + } preempt_enable(); diff --git a/arch/loongarch/kernel/ptrace.c b/arch/loongarch/kernel/ptrace.c index dc2b82ea894c..bee4194177fd 100644 --- a/arch/loongarch/kernel/ptrace.c +++ b/arch/loongarch/kernel/ptrace.c @@ -246,6 +246,90 @@ static int cfg_set(struct task_struct *target, return 0; } +#ifdef CONFIG_CPU_HAS_LSX + +static void copy_pad_fprs(struct task_struct *target, + const struct user_regset *regset, + struct membuf *to, unsigned int live_sz) +{ + int i, j; + unsigned long long fill = ~0ull; + unsigned int cp_sz, pad_sz; + + cp_sz = min(regset->size, live_sz); + pad_sz = regset->size - cp_sz; + WARN_ON(pad_sz % sizeof(fill)); + + for (i = 0; i < NUM_FPU_REGS; i++) { + membuf_write(to, &target->thread.fpu.fpr[i], cp_sz); + for (j = 0; j < (pad_sz / sizeof(fill)); j++) { + membuf_store(to, fill); + } + } +} + +static int simd_get(struct task_struct *target, + const struct user_regset *regset, + struct membuf to) +{ + const unsigned int wr_size = NUM_FPU_REGS * regset->size; + + if (!tsk_used_math(target)) { + /* The task hasn't used FP or LSX, fill with 0xff */ + copy_pad_fprs(target, regset, &to, 0); + } else if (!test_tsk_thread_flag(target, TIF_LSX_CTX_LIVE)) { + /* Copy scalar FP context, fill the rest with 0xff */ + copy_pad_fprs(target, regset, &to, 8); +#ifdef CONFIG_CPU_HAS_LASX + } else if (!test_tsk_thread_flag(target, TIF_LASX_CTX_LIVE)) { + /* Copy LSX 128 Bit context, fill the rest with 0xff */ + copy_pad_fprs(target, regset, &to, 16); +#endif + } else if (sizeof(target->thread.fpu.fpr[0]) == regset->size) { + /* Trivially copy the vector registers */ + membuf_write(&to, &target->thread.fpu.fpr, wr_size); + } else { + /* Copy as much context as possible, fill the rest with 0xff */ + copy_pad_fprs(target, regset, &to, sizeof(target->thread.fpu.fpr[0])); + } + + return 0; +} + +static int simd_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + const unsigned int wr_size = NUM_FPU_REGS * regset->size; + unsigned int cp_sz; + int i, err, start; + + init_fp_ctx(target); + + if (sizeof(target->thread.fpu.fpr[0]) == regset->size) { + /* Trivially copy the vector registers */ + err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.fpu.fpr, + 0, wr_size); + } else { + /* Copy as much context as possible */ + cp_sz = min_t(unsigned int, regset->size, + sizeof(target->thread.fpu.fpr[0])); + + i = start = err = 0; + for (; i < NUM_FPU_REGS; i++, start += regset->size) { + err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.fpu.fpr[i], + start, start + cp_sz); + } + } + + return err; +} + +#endif /* CONFIG_CPU_HAS_LSX */ + struct pt_regs_offset { const char *name; int offset; @@ -319,6 +403,12 @@ enum loongarch_regset { REGSET_GPR, REGSET_FPR, REGSET_CPUCFG, +#ifdef CONFIG_CPU_HAS_LSX + REGSET_LSX, +#endif +#ifdef CONFIG_CPU_HAS_LASX + REGSET_LASX, +#endif }; static const struct user_regset loongarch64_regsets[] = { @@ -346,6 +436,26 @@ static const struct user_regset loongarch64_regsets[] = { .regset_get = cfg_get, .set = cfg_set, }, +#ifdef CONFIG_CPU_HAS_LSX + [REGSET_LSX] = { + .core_note_type = NT_LOONGARCH_LSX, + .n = NUM_FPU_REGS, + .size = 16, + .align = 16, + .regset_get = simd_get, + .set = simd_set, + }, +#endif +#ifdef CONFIG_CPU_HAS_LASX + [REGSET_LASX] = { + .core_note_type = NT_LOONGARCH_LASX, + .n = NUM_FPU_REGS, + .size = 32, + .align = 32, + .regset_get = simd_get, + .set = simd_set, + }, +#endif }; static const struct user_regset_view user_loongarch64_view = { diff --git a/arch/loongarch/kernel/signal.c b/arch/loongarch/kernel/signal.c index 2d6eb75b11f1..8c2ef6598328 100644 --- a/arch/loongarch/kernel/signal.c +++ b/arch/loongarch/kernel/signal.c @@ -50,6 +50,16 @@ extern asmlinkage int _save_fp_context(void __user *fpregs, void __user *fcc, void __user *csr); extern asmlinkage int _restore_fp_context(void __user *fpregs, void __user *fcc, void __user *csr); +extern asmlinkage int +_save_lsx_context(void __user *fpregs, void __user *fcc, void __user *fcsr); +extern asmlinkage int +_restore_lsx_context(void __user *fpregs, void __user *fcc, void __user *fcsr); +extern asmlinkage int +_save_lasx_context(void __user *fpregs, void __user *fcc, void __user *fcsr); +extern asmlinkage int +_restore_lasx_context(void __user *fpregs, void __user *fcc, void __user *fcsr); +extern asmlinkage int _save_lsx_all_upper(void __user *buf); +extern asmlinkage int _restore_lsx_all_upper(void __user *buf); struct rt_sigframe { struct siginfo rs_info; @@ -65,9 +75,29 @@ struct extctx_layout { unsigned long size; unsigned int flags; struct _ctx_layout fpu; + struct _ctx_layout lsx; + struct _ctx_layout lasx; struct _ctx_layout end; }; +/* LSX context */ +#define LSX_CTX_MAGIC 0x53580001 +#define LSX_CTX_ALIGN 16 +struct lsx_context { + __u64 regs[2*32]; + __u64 fcc; + __u32 fcsr; +}; + +/* LASX context */ +#define LASX_CTX_MAGIC 0x41535801 +#define LASX_CTX_ALIGN 32 +struct lasx_context { + __u64 regs[4*32]; + __u64 fcc; + __u32 fcsr; +}; + static void __user *get_ctx_through_ctxinfo(struct sctx_info *info) { return (void __user *)((char *)info + sizeof(struct sctx_info)); @@ -115,6 +145,96 @@ static int copy_fpu_from_sigcontext(struct fpu_context __user *ctx) return err; } +static int copy_lsx_to_sigcontext(struct lsx_context __user *ctx) +{ + int i; + int err = 0; + uint64_t __user *regs = (uint64_t *)&ctx->regs; + uint64_t __user *fcc = &ctx->fcc; + uint32_t __user *fcsr = &ctx->fcsr; + + for (i = 0; i < NUM_FPU_REGS; i++) { + err |= __put_user(get_fpr64(¤t->thread.fpu.fpr[i], 0), + ®s[2*i]); + err |= __put_user(get_fpr64(¤t->thread.fpu.fpr[i], 1), + ®s[2*i+1]); + } + err |= __put_user(current->thread.fpu.fcc, fcc); + err |= __put_user(current->thread.fpu.fcsr, fcsr); + + return err; +} + +static int copy_lsx_from_sigcontext(struct lsx_context __user *ctx) +{ + int i; + int err = 0; + u64 fpr_val; + uint64_t __user *regs = (uint64_t *)&ctx->regs; + uint64_t __user *fcc = &ctx->fcc; + uint32_t __user *fcsr = &ctx->fcsr; + + for (i = 0; i < NUM_FPU_REGS; i++) { + err |= __get_user(fpr_val, ®s[2*i]); + set_fpr64(¤t->thread.fpu.fpr[i], 0, fpr_val); + err |= __get_user(fpr_val, ®s[2*i+1]); + set_fpr64(¤t->thread.fpu.fpr[i], 1, fpr_val); + } + err |= __get_user(current->thread.fpu.fcc, fcc); + err |= __get_user(current->thread.fpu.fcsr, fcsr); + + return err; +} + +static int copy_lasx_to_sigcontext(struct lasx_context __user *ctx) +{ + int i; + int err = 0; + uint64_t __user *regs = (uint64_t *)&ctx->regs; + uint64_t __user *fcc = &ctx->fcc; + uint32_t __user *fcsr = &ctx->fcsr; + + for (i = 0; i < NUM_FPU_REGS; i++) { + err |= __put_user(get_fpr64(¤t->thread.fpu.fpr[i], 0), + ®s[4*i]); + err |= __put_user(get_fpr64(¤t->thread.fpu.fpr[i], 1), + ®s[4*i+1]); + err |= __put_user(get_fpr64(¤t->thread.fpu.fpr[i], 2), + ®s[4*i+2]); + err |= __put_user(get_fpr64(¤t->thread.fpu.fpr[i], 3), + ®s[4*i+3]); + } + err |= __put_user(current->thread.fpu.fcc, fcc); + err |= __put_user(current->thread.fpu.fcsr, fcsr); + + return err; +} + +static int copy_lasx_from_sigcontext(struct lasx_context __user *ctx) +{ + int i; + int err = 0; + u64 fpr_val; + uint64_t __user *regs = (uint64_t *)&ctx->regs; + uint64_t __user *fcc = &ctx->fcc; + uint32_t __user *fcsr = &ctx->fcsr; + + for (i = 0; i < NUM_FPU_REGS; i++) { + err |= __get_user(fpr_val, ®s[4*i]); + set_fpr64(¤t->thread.fpu.fpr[i], 0, fpr_val); + err |= __get_user(fpr_val, ®s[4*i+1]); + set_fpr64(¤t->thread.fpu.fpr[i], 1, fpr_val); + err |= __get_user(fpr_val, ®s[4*i+2]); + set_fpr64(¤t->thread.fpu.fpr[i], 2, fpr_val); + err |= __get_user(fpr_val, ®s[4*i+3]); + set_fpr64(¤t->thread.fpu.fpr[i], 3, fpr_val); + } + err |= __get_user(current->thread.fpu.fcc, fcc); + err |= __get_user(current->thread.fpu.fcsr, fcsr); + + return err; +} + /* * Wrappers for the assembly _{save,restore}_fp_context functions. */ @@ -136,6 +256,42 @@ static int restore_hw_fpu_context(struct fpu_context __user *ctx) return _restore_fp_context(regs, fcc, fcsr); } +static int save_hw_lsx_context(struct lsx_context __user *ctx) +{ + uint64_t __user *regs = (uint64_t *)&ctx->regs; + uint64_t __user *fcc = &ctx->fcc; + uint32_t __user *fcsr = &ctx->fcsr; + + return _save_lsx_context(regs, fcc, fcsr); +} + +static int restore_hw_lsx_context(struct lsx_context __user *ctx) +{ + uint64_t __user *regs = (uint64_t *)&ctx->regs; + uint64_t __user *fcc = &ctx->fcc; + uint32_t __user *fcsr = &ctx->fcsr; + + return _restore_lsx_context(regs, fcc, fcsr); +} + +static int save_hw_lasx_context(struct lasx_context __user *ctx) +{ + uint64_t __user *regs = (uint64_t *)&ctx->regs; + uint64_t __user *fcc = &ctx->fcc; + uint32_t __user *fcsr = &ctx->fcsr; + + return _save_lasx_context(regs, fcc, fcsr); +} + +static int restore_hw_lasx_context(struct lasx_context __user *ctx) +{ + uint64_t __user *regs = (uint64_t *)&ctx->regs; + uint64_t __user *fcc = &ctx->fcc; + uint32_t __user *fcsr = &ctx->fcsr; + + return _restore_lasx_context(regs, fcc, fcsr); +} + static int fcsr_pending(unsigned int __user *fcsr) { int err, sig = 0; @@ -227,6 +383,146 @@ static int protected_restore_fpu_context(struct extctx_layout *extctx) return err ?: sig; } +static int protected_save_lsx_context(struct extctx_layout *extctx) +{ + int err = 0; + struct sctx_info __user *info = extctx->lsx.addr; + struct lsx_context __user *lsx_ctx = (struct lsx_context *)get_ctx_through_ctxinfo(info); + uint64_t __user *regs = (uint64_t *)&lsx_ctx->regs; + uint64_t __user *fcc = &lsx_ctx->fcc; + uint32_t __user *fcsr = &lsx_ctx->fcsr; + + while (1) { + lock_fpu_owner(); + if (is_lsx_enabled()) + err = save_hw_lsx_context(lsx_ctx); + else + err = copy_lsx_to_sigcontext(lsx_ctx); + unlock_fpu_owner(); + + err |= __put_user(LSX_CTX_MAGIC, &info->magic); + err |= __put_user(extctx->lsx.size, &info->size); + + if (likely(!err)) + break; + /* Touch the LSX context and try again */ + err = __put_user(0, ®s[0]) | + __put_user(0, ®s[32*2-1]) | + __put_user(0, fcc) | + __put_user(0, fcsr); + if (err) + return err; /* really bad sigcontext */ + } + + return err; +} + +static int protected_restore_lsx_context(struct extctx_layout *extctx) +{ + int err = 0, sig = 0, tmp __maybe_unused; + struct sctx_info __user *info = extctx->lsx.addr; + struct lsx_context __user *lsx_ctx = (struct lsx_context *)get_ctx_through_ctxinfo(info); + uint64_t __user *regs = (uint64_t *)&lsx_ctx->regs; + uint64_t __user *fcc = &lsx_ctx->fcc; + uint32_t __user *fcsr = &lsx_ctx->fcsr; + + err = sig = fcsr_pending(fcsr); + if (err < 0) + return err; + + while (1) { + lock_fpu_owner(); + if (is_lsx_enabled()) + err = restore_hw_lsx_context(lsx_ctx); + else + err = copy_lsx_from_sigcontext(lsx_ctx); + unlock_fpu_owner(); + + if (likely(!err)) + break; + /* Touch the LSX context and try again */ + err = __get_user(tmp, ®s[0]) | + __get_user(tmp, ®s[32*2-1]) | + __get_user(tmp, fcc) | + __get_user(tmp, fcsr); + if (err) + break; /* really bad sigcontext */ + } + + return err ?: sig; +} + +static int protected_save_lasx_context(struct extctx_layout *extctx) +{ + int err = 0; + struct sctx_info __user *info = extctx->lasx.addr; + struct lasx_context __user *lasx_ctx = + (struct lasx_context *)get_ctx_through_ctxinfo(info); + uint64_t __user *regs = (uint64_t *)&lasx_ctx->regs; + uint64_t __user *fcc = &lasx_ctx->fcc; + uint32_t __user *fcsr = &lasx_ctx->fcsr; + + while (1) { + lock_fpu_owner(); + if (is_lasx_enabled()) + err = save_hw_lasx_context(lasx_ctx); + else + err = copy_lasx_to_sigcontext(lasx_ctx); + unlock_fpu_owner(); + + err |= __put_user(LASX_CTX_MAGIC, &info->magic); + err |= __put_user(extctx->lasx.size, &info->size); + + if (likely(!err)) + break; + /* Touch the LASX context and try again */ + err = __put_user(0, ®s[0]) | + __put_user(0, ®s[32*4-1]) | + __put_user(0, fcc) | + __put_user(0, fcsr); + if (err) + return err; /* really bad sigcontext */ + } + + return err; +} + +static int protected_restore_lasx_context(struct extctx_layout *extctx) +{ + int err = 0, sig = 0, tmp __maybe_unused; + struct sctx_info __user *info = extctx->lasx.addr; + struct lasx_context __user *lasx_ctx = + (struct lasx_context *)get_ctx_through_ctxinfo(info); + uint64_t __user *regs = (uint64_t *)&lasx_ctx->regs; + uint64_t __user *fcc = &lasx_ctx->fcc; + uint32_t __user *fcsr = &lasx_ctx->fcsr; + + err = sig = fcsr_pending(fcsr); + if (err < 0) + return err; + + while (1) { + lock_fpu_owner(); + if (is_lasx_enabled()) + err = restore_hw_lasx_context(lasx_ctx); + else + err = copy_lasx_from_sigcontext(lasx_ctx); + unlock_fpu_owner(); + + if (likely(!err)) + break; + /* Touch the LASX context and try again */ + err = __get_user(tmp, ®s[0]) | + __get_user(tmp, ®s[32*4-1]) | + __get_user(tmp, fcc) | + __get_user(tmp, fcsr); + if (err) + break; /* really bad sigcontext */ + } + + return err ?: sig; +} + static int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, struct extctx_layout *extctx) { @@ -240,7 +536,11 @@ static int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, for (i = 1; i < 32; i++) err |= __put_user(regs->regs[i], &sc->sc_regs[i]); - if (extctx->fpu.addr) + if (extctx->lasx.addr) + err |= protected_save_lasx_context(extctx); + else if (extctx->lsx.addr) + err |= protected_save_lsx_context(extctx); + else if (extctx->fpu.addr) err |= protected_save_fpu_context(extctx); /* Set the "end" magic */ @@ -274,6 +574,20 @@ static int parse_extcontext(struct sigcontext __user *sc, struct extctx_layout * extctx->fpu.addr = info; break; + case LSX_CTX_MAGIC: + if (size < (sizeof(struct sctx_info) + + sizeof(struct lsx_context))) + goto invalid; + extctx->lsx.addr = info; + break; + + case LASX_CTX_MAGIC: + if (size < (sizeof(struct sctx_info) + + sizeof(struct lasx_context))) + goto invalid; + extctx->lasx.addr = info; + break; + default: goto invalid; } @@ -319,7 +633,11 @@ static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc for (i = 1; i < 32; i++) err |= __get_user(regs->regs[i], &sc->sc_regs[i]); - if (extctx.fpu.addr) + if (extctx.lasx.addr) + err |= protected_restore_lasx_context(&extctx); + else if (extctx.lsx.addr) + err |= protected_restore_lsx_context(&extctx); + else if (extctx.fpu.addr) err |= protected_restore_fpu_context(&extctx); bad: @@ -375,7 +693,13 @@ static unsigned long setup_extcontext(struct extctx_layout *extctx, unsigned lon extctx->size += extctx->end.size; if (extctx->flags & SC_USED_FP) { - if (cpu_has_fpu) + if (cpu_has_lasx && thread_lasx_context_live()) + new_sp = extframe_alloc(extctx, &extctx->lasx, + sizeof(struct lasx_context), LASX_CTX_ALIGN, new_sp); + else if (cpu_has_lsx && thread_lsx_context_live()) + new_sp = extframe_alloc(extctx, &extctx->lsx, + sizeof(struct lsx_context), LSX_CTX_ALIGN, new_sp); + else if (cpu_has_fpu) new_sp = extframe_alloc(extctx, &extctx->fpu, sizeof(struct fpu_context), FPU_CTX_ALIGN, new_sp); } diff --git a/arch/loongarch/kernel/traps.c b/arch/loongarch/kernel/traps.c index 94616e677e23..4840358c5341 100644 --- a/arch/loongarch/kernel/traps.c +++ b/arch/loongarch/kernel/traps.c @@ -514,12 +514,67 @@ static void init_restore_fp(void) BUG_ON(!is_fp_enabled()); } +static void init_restore_lsx(void) +{ + enable_lsx(); + + if (!thread_lsx_context_live()) { + /* First time LSX context user */ + init_restore_fp(); + init_lsx_upper(); + set_thread_flag(TIF_LSX_CTX_LIVE); + } else { + if (!is_simd_owner()) { + if (is_fpu_owner()) { + restore_lsx_upper(current); + } else { + __own_fpu(); + restore_lsx(current); + } + } + } + + set_thread_flag(TIF_USEDSIMD); + + BUG_ON(!is_fp_enabled()); + BUG_ON(!is_lsx_enabled()); +} + +static void init_restore_lasx(void) +{ + enable_lasx(); + + if (!thread_lasx_context_live()) { + /* First time LASX context user */ + init_restore_lsx(); + init_lasx_upper(); + set_thread_flag(TIF_LASX_CTX_LIVE); + } else { + if (is_fpu_owner() || is_simd_owner()) { + init_restore_lsx(); + restore_lasx_upper(current); + } else { + __own_fpu(); + enable_lsx(); + restore_lasx(current); + } + } + + set_thread_flag(TIF_USEDSIMD); + + BUG_ON(!is_fp_enabled()); + BUG_ON(!is_lsx_enabled()); + BUG_ON(!is_lasx_enabled()); +} + asmlinkage void noinstr do_fpu(struct pt_regs *regs) { irqentry_state_t state = irqentry_enter(regs); local_irq_enable(); die_if_kernel("do_fpu invoked from kernel context!", regs); + BUG_ON(is_lsx_enabled()); + BUG_ON(is_lasx_enabled()); preempt_disable(); init_restore_fp(); @@ -534,7 +589,19 @@ asmlinkage void noinstr do_lsx(struct pt_regs *regs) irqentry_state_t state = irqentry_enter(regs); local_irq_enable(); - force_sig(SIGILL); + if (!cpu_has_lsx) { + force_sig(SIGILL); + goto out; + } + + die_if_kernel("do_lsx invoked from kernel context!", regs); + BUG_ON(is_lasx_enabled()); + + preempt_disable(); + init_restore_lsx(); + preempt_enable(); + +out: local_irq_disable(); irqentry_exit(regs, state); @@ -545,7 +612,18 @@ asmlinkage void noinstr do_lasx(struct pt_regs *regs) irqentry_state_t state = irqentry_enter(regs); local_irq_enable(); - force_sig(SIGILL); + if (!cpu_has_lasx) { + force_sig(SIGILL); + goto out; + } + + die_if_kernel("do_lasx invoked from kernel context!", regs); + + preempt_disable(); + init_restore_lasx(); + preempt_enable(); + +out: local_irq_disable(); irqentry_exit(regs, state); -- Gitee From 4d4d897a7c8a9d83dae37ca09133c3469a8e79b8 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Sat, 3 Sep 2022 09:42:07 +0800 Subject: [PATCH 09/56] LoongArch: Fix section mismatch due to acpi_os_ioremap() Now acpi_os_ioremap() is marked with __init because it calls memblock_ is_memory() which is also marked with __init in the !ARCH_KEEP_MEMBLOCK case. However, acpi_os_ioremap() is called by ordinary functions such as acpi_os_{read, write}_memory() and causes section mismatch warnings: WARNING: modpost: vmlinux.o: section mismatch in reference: acpi_os_read_memory (section: .text) -> acpi_os_ioremap (section: .init.text) WARNING: modpost: vmlinux.o: section mismatch in reference: acpi_os_write_memory (section: .text) -> acpi_os_ioremap (section: .init.text) Fix these warnings by selecting ARCH_KEEP_MEMBLOCK unconditionally and removing the __init modifier of acpi_os_ioremap(). This can also give a chance to track "memory" and "reserved" memblocks after early boot. Signed-off-by: Huacai Chen Change-Id: If0cd1baeb01c8d627c70c2f1b4569d6ac68bf696 --- arch/loongarch/Kconfig | 1 + arch/loongarch/include/asm/acpi.h | 2 +- arch/loongarch/kernel/acpi.c | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 76f0be990526..94a722515400 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -39,6 +39,7 @@ config LOONGARCH select ARCH_INLINE_SPIN_UNLOCK_BH if !PREEMPTION select ARCH_INLINE_SPIN_UNLOCK_IRQ if !PREEMPTION select ARCH_INLINE_SPIN_UNLOCK_IRQRESTORE if !PREEMPTION + select ARCH_KEEP_MEMBLOCK select ARCH_MIGHT_HAVE_PC_PARPORT select ARCH_MIGHT_HAVE_PC_SERIO select ARCH_SPARSEMEM_ENABLE diff --git a/arch/loongarch/include/asm/acpi.h b/arch/loongarch/include/asm/acpi.h index 62044cd5b7bc..825c2519b9d1 100644 --- a/arch/loongarch/include/asm/acpi.h +++ b/arch/loongarch/include/asm/acpi.h @@ -15,7 +15,7 @@ extern int acpi_pci_disabled; extern int acpi_noirq; #define acpi_os_ioremap acpi_os_ioremap -void __init __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size); +void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size); static inline void disable_acpi(void) { diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c index 61221e6b2cac..0d6a4de10f6c 100644 --- a/arch/loongarch/kernel/acpi.c +++ b/arch/loongarch/kernel/acpi.c @@ -49,7 +49,7 @@ void __init __acpi_unmap_table(void __iomem *map, unsigned long size) early_memunmap(map, size); } -void __init __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size) +void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size) { if (!memblock_is_memory(phys)) return ioremap(phys, size); -- Gitee From c3b2212836880489047d81e39ab01ee21bce39b4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 18 Jan 2022 09:02:43 -0300 Subject: [PATCH 10/56] perf machine: Use path__join() to compose a path instead of snprintf(dir, '/', filename) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Its more intention revealing, and if we're interested in the odd cases where this may end up truncating we can do debug checks at one centralized place. Motivation, of all the container builds, fedora rawhide started complaining of: util/machine.c: In function ‘machine__create_modules’: util/machine.c:1419:50: error: ‘%s’ directive output may be truncated writing up to 255 bytes into a region of size between 0 and 4095 [-Werror=format-truncation=] 1419 | snprintf(path, sizeof(path), "%s/%s", dir_name, dent->d_name); | ^~ In file included from /usr/include/stdio.h:894, from util/branch.h:9, from util/callchain.h:8, from util/machine.c:7: In function ‘snprintf’, inlined from ‘maps__set_modules_path_dir’ at util/machine.c:1419:3, inlined from ‘machine__set_modules_path’ at util/machine.c:1473:9, inlined from ‘machine__create_modules’ at util/machine.c:1519:7: /usr/include/bits/stdio2.h:71:10: note: ‘__builtin___snprintf_chk’ output between 2 and 4352 bytes into a destination of size 4096 There are other places where we should use path__join(), but lets get rid of this one first. Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Acked-by: Ian Rogers Link: Link: https://lore.kernel.org/r/YebZKjwgfdOz0lAs@kernel.org Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Ming Wang Change-Id: Ic64ba0c9d194a9704016f1f5c22fc2269d5f2eda --- tools/perf/util/machine.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index df515cd8d018..94affad83101 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -16,6 +16,7 @@ #include "map_symbol.h" #include "branch.h" #include "mem-events.h" +#include "path.h" #include "srcline.h" #include "symbol.h" #include "sort.h" @@ -1379,7 +1380,7 @@ static int maps__set_modules_path_dir(struct maps *maps, const char *dir_name, i struct stat st; /*sshfs might return bad dent->d_type, so we have to stat*/ - snprintf(path, sizeof(path), "%s/%s", dir_name, dent->d_name); + path__join(path, sizeof(path), dir_name, dent->d_name); if (stat(path, &st)) continue; -- Gitee From b66ffc3b452aa9d79e41a718888c023ed6142c86 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Wed, 17 Nov 2021 15:16:39 +0800 Subject: [PATCH 11/56] tools/perf: Add basic support for LoongArch Signed-off-by: Huacai Chen Signed-off-by: Ming Wang Change-Id: Id8769bae9287344689add6b523c4c49f7391354f --- scripts/Makefile | 9 +- .../loongarch/include/uapi/asm/perf_regs.h | 40 +++++++++ .../arch/loongarch/include/uapi/asm/unistd.h | 22 +++++ tools/perf/Makefile.config | 7 ++ tools/perf/arch/loongarch/Build | 1 + tools/perf/arch/loongarch/Makefile | 27 ++++++ .../arch/loongarch/annotate/instructions.c | 45 ++++++++++ .../loongarch/entry/syscalls/mksyscalltbl | 60 +++++++++++++ .../arch/loongarch/include/dwarf-regs-table.h | 27 ++++++ tools/perf/arch/loongarch/include/perf_regs.h | 88 +++++++++++++++++++ tools/perf/arch/loongarch/util/Build | 4 + tools/perf/arch/loongarch/util/dwarf-regs.c | 33 +++++++ tools/perf/arch/loongarch/util/perf_regs.c | 6 ++ .../arch/loongarch/util/unwind-libunwind.c | 82 +++++++++++++++++ tools/perf/util/annotate.c | 8 ++ tools/perf/util/dwarf-regs.c | 7 ++ tools/perf/util/env.c | 2 + tools/perf/util/genelf.h | 3 + tools/perf/util/syscalltbl.c | 4 + tools/scripts/Makefile.arch | 12 ++- 20 files changed, 485 insertions(+), 2 deletions(-) create mode 100644 tools/arch/loongarch/include/uapi/asm/perf_regs.h create mode 100644 tools/arch/loongarch/include/uapi/asm/unistd.h create mode 100644 tools/perf/arch/loongarch/Build create mode 100644 tools/perf/arch/loongarch/Makefile create mode 100644 tools/perf/arch/loongarch/annotate/instructions.c create mode 100755 tools/perf/arch/loongarch/entry/syscalls/mksyscalltbl create mode 100644 tools/perf/arch/loongarch/include/dwarf-regs-table.h create mode 100644 tools/perf/arch/loongarch/include/perf_regs.h create mode 100644 tools/perf/arch/loongarch/util/Build create mode 100644 tools/perf/arch/loongarch/util/dwarf-regs.c create mode 100644 tools/perf/arch/loongarch/util/perf_regs.c create mode 100644 tools/perf/arch/loongarch/util/unwind-libunwind.c diff --git a/scripts/Makefile b/scripts/Makefile index 9adb6d247818..5d89c2ee2748 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -24,10 +24,17 @@ HOSTCFLAGS_extract-cert.o = $(CRYPTO_CFLAGS) HOSTLDLIBS_extract-cert = $(CRYPTO_LIBS) ifdef CONFIG_UNWINDER_ORC +# Additional ARCH settings for x86 ifeq ($(ARCH),x86_64) ARCH := x86 endif -HOSTCFLAGS_sorttable.o += -I$(srctree)/tools/arch/x86/include + +# Additional ARCH settings for loongarch +ifeq ($(ARCH),loongarch64) +ARCH := loongarch +endif + +HOSTCFLAGS_sorttable.o += -I$(srctree)/tools/arch/$(ARCH)/include HOSTCFLAGS_sorttable.o += -DUNWINDER_ORC_ENABLED HOSTLDLIBS_sorttable = -lpthread endif diff --git a/tools/arch/loongarch/include/uapi/asm/perf_regs.h b/tools/arch/loongarch/include/uapi/asm/perf_regs.h new file mode 100644 index 000000000000..9943d418e01d --- /dev/null +++ b/tools/arch/loongarch/include/uapi/asm/perf_regs.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _ASM_LOONGARCH_PERF_REGS_H +#define _ASM_LOONGARCH_PERF_REGS_H + +enum perf_event_loongarch_regs { + PERF_REG_LOONGARCH_PC, + PERF_REG_LOONGARCH_R1, + PERF_REG_LOONGARCH_R2, + PERF_REG_LOONGARCH_R3, + PERF_REG_LOONGARCH_R4, + PERF_REG_LOONGARCH_R5, + PERF_REG_LOONGARCH_R6, + PERF_REG_LOONGARCH_R7, + PERF_REG_LOONGARCH_R8, + PERF_REG_LOONGARCH_R9, + PERF_REG_LOONGARCH_R10, + PERF_REG_LOONGARCH_R11, + PERF_REG_LOONGARCH_R12, + PERF_REG_LOONGARCH_R13, + PERF_REG_LOONGARCH_R14, + PERF_REG_LOONGARCH_R15, + PERF_REG_LOONGARCH_R16, + PERF_REG_LOONGARCH_R17, + PERF_REG_LOONGARCH_R18, + PERF_REG_LOONGARCH_R19, + PERF_REG_LOONGARCH_R20, + PERF_REG_LOONGARCH_R21, + PERF_REG_LOONGARCH_R22, + PERF_REG_LOONGARCH_R23, + PERF_REG_LOONGARCH_R24, + PERF_REG_LOONGARCH_R25, + PERF_REG_LOONGARCH_R26, + PERF_REG_LOONGARCH_R27, + PERF_REG_LOONGARCH_R28, + PERF_REG_LOONGARCH_R29, + PERF_REG_LOONGARCH_R30, + PERF_REG_LOONGARCH_R31, + PERF_REG_LOONGARCH_MAX = PERF_REG_LOONGARCH_R31 + 1, +}; +#endif /* _ASM_LOONGARCH_PERF_REGS_H */ diff --git a/tools/arch/loongarch/include/uapi/asm/unistd.h b/tools/arch/loongarch/include/uapi/asm/unistd.h new file mode 100644 index 000000000000..d3666a55f7a6 --- /dev/null +++ b/tools/arch/loongarch/include/uapi/asm/unistd.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 . + */ + +#define __ARCH_WANT_NEW_STAT +#define __ARCH_WANT_SYS_CLONE +#define __ARCH_WANT_SYS_CLONE3 + +#include diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index f195e079660f..a93b4b55fd2f 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -74,6 +74,13 @@ ifeq ($(SRCARCH),arm64) LIBUNWIND_LIBS = -lunwind -lunwind-aarch64 endif +ifeq ($(SRCARCH),loongarch) + NO_PERF_REGS := 0 + CFLAGS += -I$(OUTPUT)arch/loongarch/include/generated + CFLAGS += -I$(OUTPUT)../arch/loongarch/include/uapi + LIBUNWIND_LIBS = -lunwind -lunwind-loongarch +endif + ifeq ($(SRCARCH),riscv) NO_PERF_REGS := 0 endif diff --git a/tools/perf/arch/loongarch/Build b/tools/perf/arch/loongarch/Build new file mode 100644 index 000000000000..e4e5f33c84d8 --- /dev/null +++ b/tools/perf/arch/loongarch/Build @@ -0,0 +1 @@ +perf-y += util/ diff --git a/tools/perf/arch/loongarch/Makefile b/tools/perf/arch/loongarch/Makefile new file mode 100644 index 000000000000..1229157b09e1 --- /dev/null +++ b/tools/perf/arch/loongarch/Makefile @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0 +ifndef NO_DWARF +PERF_HAVE_DWARF_REGS := 1 +endif +PERF_HAVE_JITDUMP := 1 + +# +# Syscall table generation for perf +# + +out := $(OUTPUT)arch/loongarch/include/generated/asm +header := $(out)/syscalls_64.c +incpath := $(srctree)/tools +sysdef := $(srctree)/tools/arch/loongarch/include/uapi/asm/unistd.h +sysprf := $(srctree)/tools/perf/arch/loongarch/entry/syscalls/ +systbl := $(sysprf)/mksyscalltbl + +# Create output directory if not already present +_dummy := $(shell [ -d '$(out)' ] || mkdir -p '$(out)') + +$(header): $(sysdef) $(systbl) + $(Q)$(SHELL) '$(systbl)' '$(CC)' '$(HOSTCC)' $(incpath) $(sysdef) > $@ + +clean:: + $(call QUIET_CLEAN, loongarch) $(RM) $(header) + +archheaders: $(header) diff --git a/tools/perf/arch/loongarch/annotate/instructions.c b/tools/perf/arch/loongarch/annotate/instructions.c new file mode 100644 index 000000000000..206e1fca38a4 --- /dev/null +++ b/tools/perf/arch/loongarch/annotate/instructions.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Perf annotate functions. + * + * Copyright (C) 2020 Loongson Technology Corporation Limited + */ + +static +struct ins_ops *loongarch__associate_ins_ops(struct arch *arch, const char *name) +{ + struct ins_ops *ops = NULL; + + if (!strncmp(name, "beqz", 4) || + !strncmp(name, "bnez", 4) || + !strncmp(name, "beq", 3) || + !strncmp(name, "bne", 3) || + !strncmp(name, "blt", 3) || + !strncmp(name, "bge", 3) || + !strncmp(name, "bltu", 4) || + !strncmp(name, "bgeu", 4) || + !strncmp(name, "bl", 2)) + ops = &call_ops; + else if (!strncmp(name, "jirl", 4)) + ops = &ret_ops; + else if (name[0] == 'b') + ops = &jump_ops; + else + return NULL; + + arch__associate_ins_ops(arch, name, ops); + + return ops; +} + +static +int loongarch__annotate_init(struct arch *arch, char *cpuid __maybe_unused) +{ + if (!arch->initialized) { + arch->associate_instruction_ops = loongarch__associate_ins_ops; + arch->initialized = true; + arch->objdump.comment_char = '#'; + } + + return 0; +} diff --git a/tools/perf/arch/loongarch/entry/syscalls/mksyscalltbl b/tools/perf/arch/loongarch/entry/syscalls/mksyscalltbl new file mode 100755 index 000000000000..86dad5a018b7 --- /dev/null +++ b/tools/perf/arch/loongarch/entry/syscalls/mksyscalltbl @@ -0,0 +1,60 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# +# Generate system call table for perf. Derived from +# powerpc script. +# +# Copyright (C) 2020 Loongson Technology Co., Ltd. +# Author(s): Ming Wang + +gcc=$1 +hostcc=$2 +incpath=$3 +input=$4 + +if ! test -r $input; then + echo "Could not read input file" >&2 + exit 1 +fi + +create_table_from_c() +{ + local sc nr last_sc + + create_table_exe=`mktemp ${TMPDIR:-/tmp}/create-table-XXXXXX` + + { + + cat <<-_EoHEADER + #include + #include "$input" + int main(int argc, char *argv[]) + { + _EoHEADER + + while read sc nr; do + printf "%s\n" " printf(\"\\t[%d] = \\\"$sc\\\",\\n\", $nr);" + last_sc=$nr + done + + printf "%s\n" " printf(\"#define SYSCALLTBL_LOONGARCH_MAX_ID %d\\n\", $last_sc);" + printf "}\n" + + } | $hostcc -I $incpath/include/uapi -o $create_table_exe -x c - + + $create_table_exe + + rm -f $create_table_exe +} + +create_table() +{ + echo "static const char *syscalltbl_loongarch[] = {" + create_table_from_c + echo "};" +} + +$gcc -E -dM -x c -I $incpath/include/uapi $input \ + |sed -ne 's/^#define __NR_//p' \ + |sort -t' ' -k2 -n \ + |create_table diff --git a/tools/perf/arch/loongarch/include/dwarf-regs-table.h b/tools/perf/arch/loongarch/include/dwarf-regs-table.h new file mode 100644 index 000000000000..676c54a226a5 --- /dev/null +++ b/tools/perf/arch/loongarch/include/dwarf-regs-table.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * dwarf-regs-table.h : Mapping of DWARF debug register numbers into + * register names. + * + * Copyright (C) 2020 Loongson Technology Corporation Limited + * + * 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. + * + */ + +#ifdef DEFINE_DWARF_REGSTR_TABLE +static const char * const loongarch_regstr_tbl[] = { + "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", + "$10", "$11", "$12", "$13", "$14", "$15", "$16", "$17", "$18", "$19", + "$20", "$21", "$22", "$23", "$24", "$25", "$26", "$27", "$28", "%29", + "$30", "$31", +}; +#endif diff --git a/tools/perf/arch/loongarch/include/perf_regs.h b/tools/perf/arch/loongarch/include/perf_regs.h new file mode 100644 index 000000000000..82d531dcd90f --- /dev/null +++ b/tools/perf/arch/loongarch/include/perf_regs.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef ARCH_PERF_REGS_H +#define ARCH_PERF_REGS_H + +#include +#include +#include + +#define PERF_REGS_MAX PERF_REG_LOONGARCH_MAX +#define PERF_REG_IP PERF_REG_LOONGARCH_PC +#define PERF_REG_SP PERF_REG_LOONGARCH_R3 + +#define PERF_REGS_MASK ((1ULL << PERF_REG_LOONGARCH_MAX) - 1) + +static inline const char *__perf_reg_name(int id) +{ + switch (id) { + case PERF_REG_LOONGARCH_PC: + return "PC"; + case PERF_REG_LOONGARCH_R1: + return "$1"; + case PERF_REG_LOONGARCH_R2: + return "$2"; + case PERF_REG_LOONGARCH_R3: + return "$3"; + case PERF_REG_LOONGARCH_R4: + return "$4"; + case PERF_REG_LOONGARCH_R5: + return "$5"; + case PERF_REG_LOONGARCH_R6: + return "$6"; + case PERF_REG_LOONGARCH_R7: + return "$7"; + case PERF_REG_LOONGARCH_R8: + return "$8"; + case PERF_REG_LOONGARCH_R9: + return "$9"; + case PERF_REG_LOONGARCH_R10: + return "$10"; + case PERF_REG_LOONGARCH_R11: + return "$11"; + case PERF_REG_LOONGARCH_R12: + return "$12"; + case PERF_REG_LOONGARCH_R13: + return "$13"; + case PERF_REG_LOONGARCH_R14: + return "$14"; + case PERF_REG_LOONGARCH_R15: + return "$15"; + case PERF_REG_LOONGARCH_R16: + return "$16"; + case PERF_REG_LOONGARCH_R17: + return "$17"; + case PERF_REG_LOONGARCH_R18: + return "$18"; + case PERF_REG_LOONGARCH_R19: + return "$19"; + case PERF_REG_LOONGARCH_R20: + return "$20"; + case PERF_REG_LOONGARCH_R21: + return "$21"; + case PERF_REG_LOONGARCH_R22: + return "$22"; + case PERF_REG_LOONGARCH_R23: + return "$23"; + case PERF_REG_LOONGARCH_R24: + return "$24"; + case PERF_REG_LOONGARCH_R25: + return "$25"; + case PERF_REG_LOONGARCH_R26: + return "$26"; + case PERF_REG_LOONGARCH_R27: + return "$27"; + case PERF_REG_LOONGARCH_R28: + return "$28"; + case PERF_REG_LOONGARCH_R29: + return "$29"; + case PERF_REG_LOONGARCH_R30: + return "$30"; + case PERF_REG_LOONGARCH_R31: + return "$31"; + default: + break; + } + return NULL; +} + +#endif /* ARCH_PERF_REGS_H */ diff --git a/tools/perf/arch/loongarch/util/Build b/tools/perf/arch/loongarch/util/Build new file mode 100644 index 000000000000..fab48acf21c5 --- /dev/null +++ b/tools/perf/arch/loongarch/util/Build @@ -0,0 +1,4 @@ +perf-y += perf_regs.o + +perf-$(CONFIG_DWARF) += dwarf-regs.o +perf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o diff --git a/tools/perf/arch/loongarch/util/dwarf-regs.c b/tools/perf/arch/loongarch/util/dwarf-regs.c new file mode 100644 index 000000000000..bbd343e3191f --- /dev/null +++ b/tools/perf/arch/loongarch/util/dwarf-regs.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dwarf-regs.c : Mapping of DWARF debug register numbers into register names. + * + * Copyright (C) 2013 Cavium, Inc. + * + * 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. + * + */ + +#include +#include + +static const char *loongarch_gpr_names[32] = { + "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", + "$10", "$11", "$12", "$13", "$14", "$15", "$16", "$17", "$18", "$19", + "$20", "$21", "$22", "$23", "$24", "$25", "$26", "$27", "$28", "$29", + "$30", "$31" +}; + +const char *get_arch_regstr(unsigned int n) +{ + n %= 32; + return loongarch_gpr_names[n]; +} diff --git a/tools/perf/arch/loongarch/util/perf_regs.c b/tools/perf/arch/loongarch/util/perf_regs.c new file mode 100644 index 000000000000..2833e101a7c6 --- /dev/null +++ b/tools/perf/arch/loongarch/util/perf_regs.c @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "../../../util/perf_regs.h" + +const struct sample_reg sample_reg_masks[] = { + SMPL_REG_END +}; diff --git a/tools/perf/arch/loongarch/util/unwind-libunwind.c b/tools/perf/arch/loongarch/util/unwind-libunwind.c new file mode 100644 index 000000000000..abcd9e2c6624 --- /dev/null +++ b/tools/perf/arch/loongarch/util/unwind-libunwind.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include "perf_regs.h" +#include "../../util/unwind.h" +#include "util/debug.h" + +int libunwind__arch_reg_id(int regnum) +{ + switch (regnum) { + case UNW_LOONGARCH_R1: + return PERF_REG_LOONGARCH_R1; + case UNW_LOONGARCH_R2: + return PERF_REG_LOONGARCH_R2; + case UNW_LOONGARCH_R3: + return PERF_REG_LOONGARCH_R3; + case UNW_LOONGARCH_R4: + return PERF_REG_LOONGARCH_R4; + case UNW_LOONGARCH_R5: + return PERF_REG_LOONGARCH_R5; + case UNW_LOONGARCH_R6: + return PERF_REG_LOONGARCH_R6; + case UNW_LOONGARCH_R7: + return PERF_REG_LOONGARCH_R7; + case UNW_LOONGARCH_R8: + return PERF_REG_LOONGARCH_R8; + case UNW_LOONGARCH_R9: + return PERF_REG_LOONGARCH_R9; + case UNW_LOONGARCH_R10: + return PERF_REG_LOONGARCH_R10; + case UNW_LOONGARCH_R11: + return PERF_REG_LOONGARCH_R11; + case UNW_LOONGARCH_R12: + return PERF_REG_LOONGARCH_R12; + case UNW_LOONGARCH_R13: + return PERF_REG_LOONGARCH_R13; + case UNW_LOONGARCH_R14: + return PERF_REG_LOONGARCH_R14; + case UNW_LOONGARCH_R15: + return PERF_REG_LOONGARCH_R15; + case UNW_LOONGARCH_R16: + return PERF_REG_LOONGARCH_R16; + case UNW_LOONGARCH_R17: + return PERF_REG_LOONGARCH_R17; + case UNW_LOONGARCH_R18: + return PERF_REG_LOONGARCH_R18; + case UNW_LOONGARCH_R19: + return PERF_REG_LOONGARCH_R19; + case UNW_LOONGARCH_R20: + return PERF_REG_LOONGARCH_R20; + case UNW_LOONGARCH_R21: + return PERF_REG_LOONGARCH_R21; + case UNW_LOONGARCH_R22: + return PERF_REG_LOONGARCH_R22; + case UNW_LOONGARCH_R23: + return PERF_REG_LOONGARCH_R23; + case UNW_LOONGARCH_R24: + return PERF_REG_LOONGARCH_R24; + case UNW_LOONGARCH_R25: + return PERF_REG_LOONGARCH_R25; + case UNW_LOONGARCH_R26: + return PERF_REG_LOONGARCH_R26; + case UNW_LOONGARCH_R27: + return PERF_REG_LOONGARCH_R27; + case UNW_LOONGARCH_R28: + return PERF_REG_LOONGARCH_R28; + case UNW_LOONGARCH_R29: + return PERF_REG_LOONGARCH_R29; + case UNW_LOONGARCH_R30: + return PERF_REG_LOONGARCH_R30; + case UNW_LOONGARCH_R31: + return PERF_REG_LOONGARCH_R31; + case UNW_LOONGARCH_PC: + return PERF_REG_LOONGARCH_PC; + default: + pr_err("unwind: invalid reg id %d\n", regnum); + return -EINVAL; + } + + return -EINVAL; +} diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 0f225b68d748..d2ab62d2c8f3 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -156,6 +156,7 @@ static int arch__associate_ins_ops(struct arch* arch, const char *name, struct i #include "arch/powerpc/annotate/instructions.c" #include "arch/s390/annotate/instructions.c" #include "arch/sparc/annotate/instructions.c" +#include "arch/loongarch/annotate/instructions.c" static struct arch architectures[] = { { @@ -205,6 +206,13 @@ static struct arch architectures[] = { .comment_char = '#', }, }, + { + .name = "loongarch", + .init = loongarch__annotate_init, + .objdump = { + .comment_char = '#', + }, + }, }; static void ins__delete(struct ins_operands *ops) diff --git a/tools/perf/util/dwarf-regs.c b/tools/perf/util/dwarf-regs.c index 1b49ecee5aff..43a77aeeb310 100644 --- a/tools/perf/util/dwarf-regs.c +++ b/tools/perf/util/dwarf-regs.c @@ -14,6 +14,10 @@ #define EM_AARCH64 183 /* ARM 64 bit */ #endif +#ifndef EM_LOONGARCH +#define EM_LOONGARCH 258 /* LoongArch */ +#endif + /* Define const char * {arch}_register_tbl[] */ #define DEFINE_DWARF_REGSTR_TABLE #include "../arch/x86/include/dwarf-regs-table.h" @@ -24,6 +28,7 @@ #include "../arch/s390/include/dwarf-regs-table.h" #include "../arch/sparc/include/dwarf-regs-table.h" #include "../arch/xtensa/include/dwarf-regs-table.h" +#include "../arch/loongarch/include/dwarf-regs-table.h" #define __get_dwarf_regstr(tbl, n) (((n) < ARRAY_SIZE(tbl)) ? (tbl)[(n)] : NULL) @@ -53,6 +58,8 @@ const char *get_dwarf_regstr(unsigned int n, unsigned int machine) return __get_dwarf_regstr(sparc_regstr_tbl, n); case EM_XTENSA: return __get_dwarf_regstr(xtensa_regstr_tbl, n); + case EM_LOONGARCH: + return __get_dwarf_regstr(loongarch_regstr_tbl, n); default: pr_err("ELF MACHINE %x is not supported.\n", machine); } diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index f243d9acf943..abbab063616f 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c @@ -347,6 +347,8 @@ static const char *normalize_arch(char *arch) return "mips"; if (!strncmp(arch, "sh", 2) && isdigit(arch[2])) return "sh"; + if (!strncmp(arch, "loongarch", 9)) + return "loongarch"; return arch; } diff --git a/tools/perf/util/genelf.h b/tools/perf/util/genelf.h index d4137559be05..fdeef73cf85f 100644 --- a/tools/perf/util/genelf.h +++ b/tools/perf/util/genelf.h @@ -38,6 +38,9 @@ int jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_ent #elif defined(__s390x__) #define GEN_ELF_ARCH EM_S390 #define GEN_ELF_CLASS ELFCLASS64 +#elif defined(__loongarch__) +#define GEN_ELF_ARCH EM_LOONGARCH +#define GEN_ELF_CLASS ELFCLASS64 #else #error "unsupported architecture" #endif diff --git a/tools/perf/util/syscalltbl.c b/tools/perf/util/syscalltbl.c index 03bd99d3be16..e441f12c7272 100644 --- a/tools/perf/util/syscalltbl.c +++ b/tools/perf/util/syscalltbl.c @@ -34,6 +34,10 @@ static const char **syscalltbl_native = syscalltbl_powerpc_32; #include const int syscalltbl_native_max_id = SYSCALLTBL_ARM64_MAX_ID; static const char **syscalltbl_native = syscalltbl_arm64; +#elif defined(__loongarch__) +#include +const int syscalltbl_native_max_id = SYSCALLTBL_LOONGARCH_MAX_ID; +static const char **syscalltbl_native = syscalltbl_loongarch; #endif struct syscall { diff --git a/tools/scripts/Makefile.arch b/tools/scripts/Makefile.arch index b10b7a27c33f..0b2af44c167f 100644 --- a/tools/scripts/Makefile.arch +++ b/tools/scripts/Makefile.arch @@ -4,7 +4,8 @@ HOSTARCH := $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ \ -e /arm64/!s/arm.*/arm/ -e s/sa110/arm/ \ -e s/s390x/s390/ -e s/parisc64/parisc/ \ -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \ - -e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ ) + -e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ \ + -e s/loongarch.*/loongarch/) ifndef ARCH ARCH := $(HOSTARCH) @@ -33,6 +34,15 @@ ifeq ($(ARCH),sh64) SRCARCH := sh endif +# Additional ARCH settings for loongarch +ifeq ($(ARCH),loongarch32) + SRCARCH := loongarch +endif + +ifeq ($(ARCH),loongarch64) + SRCARCH := loongarch +endif + LP64 := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1) ifeq ($(LP64), 1) IS_64_BIT := 1 -- Gitee From c727b50ddd4368bd7d449a64635980f9d38216a7 Mon Sep 17 00:00:00 2001 From: Ming Wang Date: Tue, 30 Aug 2022 14:53:55 +0800 Subject: [PATCH 12/56] net: realtek: Add R8168 drivers from realtek (r8168-8.049.02) Add network card driver to adapt to realtek R8168 network card. This patch porting from 4.19 kernel. Signed-off-by: Ming Wang Change-Id: I85988a835a95a0800a3702e3a11b0933e0885576 --- drivers/net/ethernet/realtek/Kconfig | 14 + drivers/net/ethernet/realtek/Makefile | 1 + drivers/net/ethernet/realtek/r8168/Makefile | 171 + .../ethernet/realtek/r8168/Makefile_linux24x | 75 + drivers/net/ethernet/realtek/r8168/r8168.h | 1842 + .../net/ethernet/realtek/r8168/r8168_asf.c | 422 + .../net/ethernet/realtek/r8168/r8168_asf.h | 295 + .../net/ethernet/realtek/r8168/r8168_dash.h | 256 + .../net/ethernet/realtek/r8168/r8168_fiber.h | 67 + .../ethernet/realtek/r8168/r8168_firmware.c | 264 + .../ethernet/realtek/r8168/r8168_firmware.h | 68 + drivers/net/ethernet/realtek/r8168/r8168_n.c | 28725 ++++++++++++++++ .../ethernet/realtek/r8168/r8168_realwow.h | 118 + .../net/ethernet/realtek/r8168/rtl_eeprom.c | 289 + .../net/ethernet/realtek/r8168/rtl_eeprom.h | 56 + drivers/net/ethernet/realtek/r8168/rtltool.c | 359 + drivers/net/ethernet/realtek/r8168/rtltool.h | 86 + 17 files changed, 33108 insertions(+) create mode 100644 drivers/net/ethernet/realtek/r8168/Makefile create mode 100644 drivers/net/ethernet/realtek/r8168/Makefile_linux24x create mode 100644 drivers/net/ethernet/realtek/r8168/r8168.h create mode 100644 drivers/net/ethernet/realtek/r8168/r8168_asf.c create mode 100644 drivers/net/ethernet/realtek/r8168/r8168_asf.h create mode 100644 drivers/net/ethernet/realtek/r8168/r8168_dash.h create mode 100644 drivers/net/ethernet/realtek/r8168/r8168_fiber.h create mode 100644 drivers/net/ethernet/realtek/r8168/r8168_firmware.c create mode 100644 drivers/net/ethernet/realtek/r8168/r8168_firmware.h create mode 100644 drivers/net/ethernet/realtek/r8168/r8168_n.c create mode 100644 drivers/net/ethernet/realtek/r8168/r8168_realwow.h create mode 100644 drivers/net/ethernet/realtek/r8168/rtl_eeprom.c create mode 100644 drivers/net/ethernet/realtek/r8168/rtl_eeprom.h create mode 100644 drivers/net/ethernet/realtek/r8168/rtltool.c create mode 100644 drivers/net/ethernet/realtek/r8168/rtltool.h diff --git a/drivers/net/ethernet/realtek/Kconfig b/drivers/net/ethernet/realtek/Kconfig index 93d9df55b361..53821061d1b7 100644 --- a/drivers/net/ethernet/realtek/Kconfig +++ b/drivers/net/ethernet/realtek/Kconfig @@ -113,4 +113,18 @@ config R8169 To compile this driver as a module, choose M here: the module will be called r8169. This is recommended. +config R8168 + tristate "Realtek 8168 PCI-Express Gigabit Ethernet support" + depends on PCI + select FW_LOADER + select CRC32 + select NET_CORE + select MII + help + Say Y here if you have a Realtek 8168 PCI-Express Gigabit Ethernet + adapter. + + To compile this driver as a module, choose M here: the module + will be called r8169. This is recommended. + endif # NET_VENDOR_REALTEK diff --git a/drivers/net/ethernet/realtek/Makefile b/drivers/net/ethernet/realtek/Makefile index 2e1d78b106b0..58d35205142c 100644 --- a/drivers/net/ethernet/realtek/Makefile +++ b/drivers/net/ethernet/realtek/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_8139TOO) += 8139too.o obj-$(CONFIG_ATP) += atp.o r8169-objs += r8169_main.o r8169_firmware.o r8169_phy_config.o obj-$(CONFIG_R8169) += r8169.o +obj-$(CONFIG_R8168) += r8168/ diff --git a/drivers/net/ethernet/realtek/r8168/Makefile b/drivers/net/ethernet/realtek/r8168/Makefile new file mode 100644 index 000000000000..518e9d2204ad --- /dev/null +++ b/drivers/net/ethernet/realtek/r8168/Makefile @@ -0,0 +1,171 @@ +# SPDX-License-Identifier: GPL-2.0-only +################################################################################ +# +# r8168 is the Linux device driver released for Realtek Gigabit Ethernet +# controllers with PCI-Express interface. +# +# Copyright(c) 2021 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. +################################################################################ + +CONFIG_SOC_LAN = n +ENABLE_FIBER_SUPPORT = n +ENABLE_REALWOW_SUPPORT = n +ENABLE_DASH_SUPPORT = n +ENABLE_DASH_PRINTER_SUPPORT = n +CONFIG_DOWN_SPEED_100 = n +CONFIG_ASPM = y +ENABLE_S5WOL = y +ENABLE_S5_KEEP_CURR_MAC = n +ENABLE_EEE = y +ENABLE_S0_MAGIC_PACKET = n +CONFIG_DYNAMIC_ASPM = y +ENABLE_USE_FIRMWARE_FILE = n + +ifneq ($(KERNELRELEASE),) + obj-m := r8168.o + r8168-objs := r8168_n.o r8168_asf.o rtl_eeprom.o rtltool.o + ifeq ($(CONFIG_SOC_LAN), y) + EXTRA_CFLAGS += -DCONFIG_SOC_LAN + endif + ifeq ($(ENABLE_FIBER_SUPPORT), y) + r8168-objs += r8168_fiber.o + EXTRA_CFLAGS += -DENABLE_FIBER_SUPPORT + endif + ifeq ($(ENABLE_REALWOW_SUPPORT), y) + r8168-objs += r8168_realwow.o + EXTRA_CFLAGS += -DENABLE_REALWOW_SUPPORT + endif + ifeq ($(ENABLE_DASH_SUPPORT), y) + r8168-objs += r8168_dash.o + EXTRA_CFLAGS += -DENABLE_DASH_SUPPORT + endif + ifeq ($(ENABLE_DASH_PRINTER_SUPPORT), y) + r8168-objs += r8168_dash.o + EXTRA_CFLAGS += -DENABLE_DASH_SUPPORT -DENABLE_DASH_PRINTER_SUPPORT + endif + EXTRA_CFLAGS += -DCONFIG_R8168_NAPI + EXTRA_CFLAGS += -DCONFIG_R8168_VLAN + ifeq ($(CONFIG_DOWN_SPEED_100), y) + EXTRA_CFLAGS += -DCONFIG_DOWN_SPEED_100 + endif + ifeq ($(CONFIG_ASPM), y) + EXTRA_CFLAGS += -DCONFIG_ASPM + endif + ifeq ($(ENABLE_S5WOL), y) + EXTRA_CFLAGS += -DENABLE_S5WOL + endif + ifeq ($(ENABLE_S5_KEEP_CURR_MAC), y) + EXTRA_CFLAGS += -DENABLE_S5_KEEP_CURR_MAC + endif + ifeq ($(ENABLE_EEE), y) + EXTRA_CFLAGS += -DENABLE_EEE + endif + ifeq ($(ENABLE_S0_MAGIC_PACKET), y) + EXTRA_CFLAGS += -DENABLE_S0_MAGIC_PACKET + endif + ifeq ($(CONFIG_DYNAMIC_ASPM), y) + EXTRA_CFLAGS += -DCONFIG_DYNAMIC_ASPM + endif + ifeq ($(ENABLE_USE_FIRMWARE_FILE), y) + r8168-objs += r8168_firmware.o + EXTRA_CFLAGS += -DENABLE_USE_FIRMWARE_FILE + 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 + +endif diff --git a/drivers/net/ethernet/realtek/r8168/Makefile_linux24x b/drivers/net/ethernet/realtek/r8168/Makefile_linux24x new file mode 100644 index 000000000000..238fa905a800 --- /dev/null +++ b/drivers/net/ethernet/realtek/r8168/Makefile_linux24x @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: GPL-2.0-only +################################################################################ +# +# r8168 is the Linux device driver released for Realtek Gigabit Ethernet +# controllers with PCI-Express interface. +# +# Copyright(c) 2021 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. +################################################################################ + +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 +SOURCE := r8168_n.c r8168_asf.c rtl_eeprom.c rtltool.c +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 r8168.o + strip --strip-debug r8168.o + +%.o: %.c + $(CC) $(MODCFLAGS) -c $< -o $@ + +clean: + rm *.o -f + +install: + install -m 744 -c r8168.o $(KMISC) diff --git a/drivers/net/ethernet/realtek/r8168/r8168.h b/drivers/net/ethernet/realtek/r8168/r8168.h new file mode 100644 index 000000000000..ac50cce0859e --- /dev/null +++ b/drivers/net/ethernet/realtek/r8168/r8168.h @@ -0,0 +1,1842 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* +################################################################################ +# +# r8168 is the Linux device driver released for Realtek Gigabit Ethernet +# controllers with PCI-Express interface. +# +# Copyright(c) 2021 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 +#include "r8168_dash.h" +#include "r8168_realwow.h" +#include "r8168_fiber.h" + +#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_R8168_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,3,0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) +#define netdev_features_t u32 +#endif +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,5,0) +#define NETIF_F_ALL_CSUM NETIF_F_CSUM_MASK +#else +#ifndef NETIF_F_ALL_CSUM +#define NETIF_F_ALL_CSUM NETIF_F_CSUM_MASK +#endif +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,37) +#define ENABLE_R8168_PROCFS +#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 + +#ifndef NETIF_F_RXALL +#define NETIF_F_RXALL 0 +#endif + +#ifndef NETIF_F_RXFCS +#define NETIF_F_RXFCS 0 +#endif + +#ifndef HAVE_FREE_NETDEV +#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 + +#ifndef MDIO_EEE_100TX +#define MDIO_EEE_100TX 0x0002 +#endif + +#ifndef MDIO_EEE_1000T +#define MDIO_EEE_1000T 0x0004 +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) +#ifdef CONFIG_NET_POLL_CONTROLLER +#define RTL_NET_POLL_CONTROLLER dev->poll_controller=rtl8168_netpoll +#else +#define RTL_NET_POLL_CONTROLLER +#endif + +#ifdef CONFIG_R8168_VLAN +#define RTL_SET_VLAN dev->vlan_rx_register=rtl8168_vlan_rx_register +#else +#define RTL_SET_VLAN +#endif + +#define RTL_NET_DEVICE_OPS(ops) dev->open=rtl8168_open; \ + dev->hard_start_xmit=rtl8168_start_xmit; \ + dev->get_stats=rtl8168_get_stats; \ + dev->stop=rtl8168_close; \ + dev->tx_timeout=rtl8168_tx_timeout; \ + dev->set_multicast_list=rtl8168_set_rx_mode; \ + dev->change_mtu=rtl8168_change_mtu; \ + dev->set_mac_address=rtl8168_set_mac_address; \ + dev->do_ioctl=rtl8168_do_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 + +//Hardware will continue interrupt 10 times after interrupt finished. +#define RTK_KEEP_INTERRUPT_COUNT (10) + +//Due to the hardware design of RTL8111B, the low 32 bit address of receive +//buffer must be 8-byte alignment. +#ifndef NET_IP_ALIGN +#define NET_IP_ALIGN 2 +#endif +#define RTK_RX_ALIGN 8 + +#ifdef CONFIG_R8168_NAPI +#define NAPI_SUFFIX "-NAPI" +#else +#define NAPI_SUFFIX "" +#endif +#ifdef ENABLE_FIBER_SUPPORT +#define FIBER_SUFFIX "-FIBER" +#else +#define FIBER_SUFFIX "" +#endif +#ifdef ENABLE_REALWOW_SUPPORT +#define REALWOW_SUFFIX "-REALWOW" +#else +#define REALWOW_SUFFIX "" +#endif +#if defined(ENABLE_DASH_PRINTER_SUPPORT) +#define DASH_SUFFIX "-PRINTER" +#elif defined(ENABLE_DASH_SUPPORT) +#define DASH_SUFFIX "-DASH" +#else +#define DASH_SUFFIX "" +#endif + +#define RTL8168_VERSION "8.049.02" NAPI_SUFFIX FIBER_SUFFIX REALWOW_SUFFIX DASH_SUFFIX +#define MODULENAME "r8168" +#define PFX MODULENAME ": " + +#define GPL_CLAIM "\ +r8168 Copyright (C) 2021 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 RTL8168_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 /* RTL8168_DEBUG */ + +#define R8168_MSG_DEFAULT \ + (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN) + +#ifdef CONFIG_R8168_NAPI +#define rtl8168_rx_hwaccel_skb vlan_hwaccel_receive_skb +#define rtl8168_rx_quota(count, quota) min(count, quota) +#else +#define rtl8168_rx_hwaccel_skb vlan_hwaccel_rx +#define rtl8168_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, '6' is 1024 */ +#define TX_DMA_BURST_unlimited 7 +#define TX_DMA_BURST_1024 6 +#define TX_DMA_BURST_512 5 +#define TX_DMA_BURST_256 4 +#define TX_DMA_BURST_128 3 +#define TX_DMA_BURST_64 2 +#define TX_DMA_BURST_32 1 +#define TX_DMA_BURST_16 0 +#define Reserved1_data 0x3F +#define RxPacketMaxSize 0x3FE8 /* 16K - 1 - ETH_HLEN - VLAN - CRC... */ +#define Jumbo_Frame_1k ETH_DATA_LEN +#define Jumbo_Frame_2k (2*1024 - ETH_HLEN - VLAN_HLEN - ETH_FCS_LEN) +#define Jumbo_Frame_3k (3*1024 - ETH_HLEN - VLAN_HLEN - ETH_FCS_LEN) +#define Jumbo_Frame_4k (4*1024 - ETH_HLEN - VLAN_HLEN - ETH_FCS_LEN) +#define Jumbo_Frame_5k (5*1024 - ETH_HLEN - VLAN_HLEN - ETH_FCS_LEN) +#define Jumbo_Frame_6k (6*1024 - ETH_HLEN - VLAN_HLEN - ETH_FCS_LEN) +#define Jumbo_Frame_7k (7*1024 - ETH_HLEN - VLAN_HLEN - ETH_FCS_LEN) +#define Jumbo_Frame_8k (8*1024 - ETH_HLEN - VLAN_HLEN - ETH_FCS_LEN) +#define Jumbo_Frame_9k (9*1024 - ETH_HLEN - VLAN_HLEN - ETH_FCS_LEN) +#define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */ +#define RxEarly_off_V1 (0x07 << 11) +#define RxEarly_off_V2 (1 << 11) +#define Rx_Single_fetch_V2 (1 << 14) + +#define R8168_REGS_SIZE (256) +#define R8168_MAC_REGS_SIZE (256) +#define R8168_PHY_REGS_SIZE (16*2) +#define R8168_EPHY_REGS_SIZE (31*2) +#define R8168_ERI_REGS_SIZE (0x100) +#define R8168_REGS_DUMP_SIZE (0x400) +#define R8168_PCI_REGS_SIZE (0x100) +#define R8168_NAPI_WEIGHT 64 + +#define RTL8168_TX_TIMEOUT (6 * HZ) +#define RTL8168_LINK_TIMEOUT (1 * HZ) +#define RTL8168_ESD_TIMEOUT (2 * HZ) + +#define NUM_TX_DESC 1024 /* Number of Tx descriptor registers */ +#define NUM_RX_DESC 1024 /* Number of Rx descriptor registers */ + +#define RX_BUF_SIZE 0x05F3 /* 0x05F3 = 1522bye + 1 */ +#define R8168_TX_RING_BYTES (NUM_TX_DESC * sizeof(struct TxDesc)) +#define R8168_RX_RING_BYTES (NUM_RX_DESC * sizeof(struct RxDesc)) + +#define OCP_STD_PHY_BASE 0xa400 + +#define NODE_ADDRESS_SIZE 6 + +#define SHORT_PACKET_PADDING_BUF_SIZE 256 + +#define RTK_MAGIC_DEBUG_VALUE 0x0badbeef + +/* 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) ((unsigned long) readl(tp->mmio_addr + (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,3) +#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 //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,3) + +/*****************************************************************************/ +#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; + +#define RTL_NAPI_CONFIG(ndev, priv, function, weight) netif_napi_add(ndev, &priv->napi, function, weight) +#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) + +/*****************************************************************************/ +#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) + +/*****************************************************************************/ +#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) + +#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 + +/*****************************************************************************/ +/* 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,6,4) ) +#define MODULE_VERSION(_version) MODULE_INFO(version, _version) +#endif /* 2.6.4 => 2.6.0 */ +/*****************************************************************************/ +/* 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.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,0) +/* + * initialize a work-struct's func and data pointers: + */ +#define PREPARE_WORK(_work, _func, _data) \ + do { \ + (_work)->func = _func; \ + (_work)->data = _data; \ + } while (0) + +#endif +/*****************************************************************************/ +/* 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 */ + +/*****************************************************************************/ +/* 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; +}; + +struct ethtool_stats { + u32 cmd; + u32 n_stats; + u64 data[0]; +}; +#endif /* ETHTOOL_GSTATS */ + +#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 RTL8168_DSM_STATE { + DSM_MAC_INIT = 1, + DSM_NIC_GOTO_D3 = 2, + DSM_IF_DOWN = 3, + DSM_NIC_RESUME_D3 = 4, + DSM_IF_UP = 5, +}; + +enum RTL8168_registers { + MAC0 = 0x00, /* Ethernet hardware address. */ + MAC4 = 0x04, + MAR0 = 0x08, /* Multicast filter. */ + CounterAddrLow = 0x10, + CounterAddrHigh = 0x14, + CustomLED = 0x18, + TxDescStartAddrLow = 0x20, + TxDescStartAddrHigh = 0x24, + TxHDescStartAddrLow = 0x28, + TxHDescStartAddrHigh = 0x2c, + FLASH = 0x30, + ERSR = 0x36, + ChipCmd = 0x37, + TxPoll = 0x38, + IntrMask = 0x3C, + IntrStatus = 0x3E, + TxConfig = 0x40, + RxConfig = 0x44, + TCTR = 0x48, + Cfg9346 = 0x50, + Config0 = 0x51, + Config1 = 0x52, + Config2 = 0x53, + Config3 = 0x54, + Config4 = 0x55, + Config5 = 0x56, + TDFNR = 0x57, + TimeInt0 = 0x58, + TimeInt1 = 0x5C, + PHYAR = 0x60, + CSIDR = 0x64, + CSIAR = 0x68, + PHYstatus = 0x6C, + MACDBG = 0x6D, + GPIO = 0x6E, + PMCH = 0x6F, + ERIDR = 0x70, + ERIAR = 0x74, + EPHY_RXER_NUM = 0x7C, + EPHYAR = 0x80, + TimeInt2 = 0x8C, + OCPDR = 0xB0, + MACOCP = 0xB0, + OCPAR = 0xB4, + SecMAC0 = 0xB4, + SecMAC4 = 0xB8, + PHYOCP = 0xB8, + DBG_reg = 0xD1, + TwiCmdReg = 0xD2, + MCUCmd_reg = 0xD3, + RxMaxSize = 0xDA, + EFUSEAR = 0xDC, + CPlusCmd = 0xE0, + IntrMitigate = 0xE2, + RxDescAddrLow = 0xE4, + RxDescAddrHigh = 0xE8, + MTPS = 0xEC, + FuncEvent = 0xF0, + PPSW = 0xF2, + FuncEventMask = 0xF4, + TimeInt3 = 0xF4, + FuncPresetState = 0xF8, + CMAC_IBCR0 = 0xF8, + CMAC_IBCR2 = 0xF9, + CMAC_IBIMR0 = 0xFA, + CMAC_IBISR0 = 0xFB, + FuncForceEvent = 0xFC, +}; + +enum RTL8168_register_content { + /* InterruptStatusBits */ + SYSErr = 0x8000, + PCSTimeout = 0x4000, + SWInt = 0x0100, + TxDescUnavail = 0x0080, + RxFIFOOver = 0x0040, + LinkChg = 0x0020, + RxDescUnavail = 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, + + /* Cfg9346Bits */ + Cfg9346_Lock = 0x00, + Cfg9346_Unlock = 0xC0, + Cfg9346_EEDO = (1 << 0), + Cfg9346_EEDI = (1 << 1), + Cfg9346_EESK = (1 << 2), + Cfg9346_EECS = (1 << 3), + Cfg9346_EEM0 = (1 << 6), + Cfg9346_EEM1 = (1 << 7), + + /* 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, + RxCfg_128_int_en = (1 << 15), + RxCfg_fet_multi_en = (1 << 14), + RxCfg_half_refetch = (1 << 13), + RxCfg_9356SEL = (1 << 6), + + /* TxConfigBits */ + TxInterFrameGapShift = 24, + TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */ + TxMACLoopBack = (1 << 17), /* MAC loopback */ + + /* Config1 register */ + 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 */ + ClkReqEn = (1 << 7), /* Clock Request Enable */ + PMSTS_En = (1 << 5), + + /* Config3 register */ + Isolate_en = (1 << 12), /* Isolate enable */ + MagicPacket = (1 << 5), /* Wake up when receives a Magic Packet */ + LinkUp = (1 << 4), /* This bit is reserved in RTL8168B.*/ + /* Wake up when the cable connection is re-established */ + ECRCEN = (1 << 3), /* This bit is reserved in RTL8168B*/ + Jumbo_En0 = (1 << 2), /* This bit is reserved in RTL8168B*/ + RDY_TO_L23 = (1 << 1), /* This bit is reserved in RTL8168B*/ + Beacon_en = (1 << 0), /* This bit is reserved in RTL8168B*/ + + /* Config4 register */ + Jumbo_En1 = (1 << 1), /* This bit is reserved in RTL8168B*/ + + /* Config5 register */ + BWF = (1 << 6), /* Accept Broadcast wakeup frame */ + MWF = (1 << 5), /* Accept Multicast wakeup frame */ + UWF = (1 << 4), /* Accept Unicast wakeup frame */ + LanWake = (1 << 1), /* LanWake enable/disable */ + PMEStatus = (1 << 0), /* PME status can be reset by PCI RST# */ + ASPM_en = (1 << 0), /* ASPM enable */ + + /* CPlusCmd */ + EnableBist = (1 << 15), + Macdbgo_oe = (1 << 14), + Normal_mode = (1 << 13), + Force_halfdup = (1 << 12), + Force_rxflow_en = (1 << 11), + Force_txflow_en = (1 << 10), + Cxpl_dbg_sel = (1 << 9),//This bit is reserved in RTL8168B + ASF = (1 << 8),//This bit is reserved in RTL8168C + PktCntrDisable = (1 << 7), + RxVlan = (1 << 6), + RxChkSum = (1 << 5), + Macdbgo_sel = 0x001C, + INTT_0 = 0x0000, + INTT_1 = 0x0001, + INTT_2 = 0x0002, + INTT_3 = 0x0003, + + /* rtl8168_PHYstatus */ + PowerSaveStatus = 0x80, + TxFlowCtrl = 0x40, + RxFlowCtrl = 0x20, + _1000bpsF = 0x10, + _100bps = 0x08, + _10bps = 0x04, + LinkStatus = 0x02, + FullDup = 0x01, + + /* DBG_reg */ + Fix_Nak_1 = (1 << 4), + Fix_Nak_2 = (1 << 3), + DBGPIN_E2 = (1 << 0), + + /* ResetCounterCommand */ + CounterReset = 0x1, + /* 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, + + /* EPHY access */ + EPHYAR_Flag = 0x80000000, + EPHYAR_Write = 0x80000000, + EPHYAR_Read = 0x00000000, + EPHYAR_Reg_Mask = 0x3f, + EPHYAR_Reg_shift = 16, + EPHYAR_Data_Mask = 0xffff, + + /* CSI access */ + CSIAR_Flag = 0x80000000, + CSIAR_Write = 0x80000000, + CSIAR_Read = 0x00000000, + CSIAR_ByteEn = 0x0f, + CSIAR_ByteEn_shift = 12, + CSIAR_Addr_Mask = 0x0fff, + + /* ERI access */ + ERIAR_Flag = 0x80000000, + ERIAR_Write = 0x80000000, + ERIAR_Read = 0x00000000, + ERIAR_Addr_Align = 4, /* ERI access register address must be 4 byte alignment */ + ERIAR_ExGMAC = 0, + ERIAR_MSIX = 1, + ERIAR_ASF = 2, + ERIAR_OOB = 2, + ERIAR_Type_shift = 16, + ERIAR_ByteEn = 0x0f, + ERIAR_ByteEn_shift = 12, + + /* OCP GPHY access */ + OCPDR_Write = 0x80000000, + OCPDR_Read = 0x00000000, + OCPDR_Reg_Mask = 0xFF, + OCPDR_Data_Mask = 0xFFFF, + OCPDR_GPHY_Reg_shift = 16, + OCPAR_Flag = 0x80000000, + OCPAR_GPHY_Write = 0x8000F060, + OCPAR_GPHY_Read = 0x0000F060, + OCPR_Write = 0x80000000, + OCPR_Read = 0x00000000, + OCPR_Addr_Reg_shift = 16, + OCPR_Flag = 0x80000000, + OCP_STD_PHY_BASE_PAGE = 0x0A40, + + /* MCU Command */ + Now_is_oob = (1 << 7), + Txfifo_empty = (1 << 5), + Rxfifo_empty = (1 << 4), + + /* E-FUSE access */ + EFUSE_WRITE = 0x80000000, + EFUSE_WRITE_OK = 0x00000000, + EFUSE_READ = 0x00000000, + EFUSE_READ_OK = 0x80000000, + EFUSE_WRITE_V3 = 0x40000000, + EFUSE_WRITE_OK_V3 = 0x00000000, + EFUSE_READ_V3 = 0x80000000, + EFUSE_READ_OK_V3 = 0x00000000, + EFUSE_Reg_Mask = 0x03FF, + EFUSE_Reg_Shift = 8, + EFUSE_Check_Cnt = 300, + EFUSE_READ_FAIL = 0xFF, + EFUSE_Data_Mask = 0x000000FF, + + /* GPIO */ + GPIO_en = (1 << 0), + +}; + +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 */ + /*------ offset 0 of tx descriptor ------*/ + LargeSend = (1 << 27), /* TCP Large Send Offload (TSO) */ + GiantSendv4 = (1 << 26), /* TCP Giant Send Offload V4 (GSOv4) */ + GiantSendv6 = (1 << 25), /* TCP Giant Send Offload V6 (GSOv6) */ + LargeSend_DP = (1 << 16), /* TCP Large Send Offload (TSO) */ + MSSShift = 16, /* MSS value position */ + MSSMask = 0x7FFU, /* MSS value 11 bits */ + TxIPCS = (1 << 18), /* Calculate IP checksum */ + TxUDPCS = (1 << 17), /* Calculate UDP/IP checksum */ + TxTCPCS = (1 << 16), /* Calculate TCP/IP checksum */ + TxVlanTag = (1 << 17), /* Add VLAN tag */ + + /*@@@@@@ offset 4 of tx descriptor => bits for RTL8168C/CP only begin @@@@@@*/ + TxUDPCS_C = (1 << 31), /* Calculate UDP/IP checksum */ + TxTCPCS_C = (1 << 30), /* Calculate TCP/IP checksum */ + TxIPCS_C = (1 << 29), /* Calculate IP checksum */ + TxIPV6F_C = (1 << 28), /* Indicate it is an IPv6 packet */ + /*@@@@@@ offset 4 of tx descriptor => bits for RTL8168C/CP only end @@@@@@*/ + + + /* Rx private */ + /*------ offset 0 of rx descriptor ------*/ + 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 + + RxIPF = (1 << 16), /* IP checksum failed */ + RxUDPF = (1 << 15), /* UDP/IP checksum failed */ + RxTCPF = (1 << 14), /* TCP/IP checksum failed */ + RxVlanTag = (1 << 16), /* VLAN tag available */ + + /*@@@@@@ offset 0 of rx descriptor => bits for RTL8168C/CP only begin @@@@@@*/ + RxUDPT = (1 << 18), + RxTCPT = (1 << 17), + /*@@@@@@ offset 0 of rx descriptor => bits for RTL8168C/CP only end @@@@@@*/ + + /*@@@@@@ offset 4 of rx descriptor => bits for RTL8168C/CP only begin @@@@@@*/ + RxV6F = (1 << 31), + RxV4F = (1 << 30), + /*@@@@@@ offset 4 of rx descriptor => bits for RTL8168C/CP only end @@@@@@*/ +}; + +enum features { +// RTL_FEATURE_WOL = (1 << 0), + RTL_FEATURE_MSI = (1 << 1), +}; + +enum wol_capability { + WOL_DISABLED = 0, + WOL_ENABLED = 1 +}; + +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) +}; + +enum effuse { + EFUSE_NOT_SUPPORT = 0, + EFUSE_SUPPORT_V1, + EFUSE_SUPPORT_V2, + EFUSE_SUPPORT_V3, +}; +#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)]; +}; + +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_0x1c_h; + u16 resv_0x1c_l; + u16 resv_0x20_h; + u16 resv_0x20_l; + u16 resv_0x24_h; + u16 resv_0x24_l; + u16 resv_0x2c_h; + u16 resv_0x2c_l; + u32 pci_sn_l; + u32 pci_sn_h; +}; + +struct rtl8168_private { + void __iomem *mmio_addr; /* memory map physical address */ + struct pci_dev *pci_dev; /* Index of PCI device */ + struct net_device *dev; +#ifdef CONFIG_R8168_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; + u32 tx_tcp_csum_cmd; + u32 tx_udp_csum_cmd; + u32 tx_ip_csum_cmd; + u32 tx_ipv6_csum_cmd; + int max_jumbo_frame_size; + int chipset; + u32 mcfg; + 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 rx_buf_sz; + struct timer_list esd_timer; + struct timer_list link_timer; + struct pci_resource pci_cfg_space; + unsigned int esd_flag; + unsigned int pci_cfg_is_read; + unsigned int rtl8168_rx_config; + u16 cp_cmd; + u16 intr_mask; + u16 timer_intr_mask; + int phy_auto_nego_reg; + int phy_1000_ctrl_reg; + u8 org_mac_addr[NODE_ADDRESS_SIZE]; + struct rtl8168_counters *tally_vaddr; + dma_addr_t tally_paddr; + +#ifdef CONFIG_R8168_VLAN + struct vlan_group *vlgrp; +#endif + u8 wol_enabled; + u32 wol_opts; + u8 efuse_ver; + u8 eeprom_type; + u8 autoneg; + u8 duplex; + u32 speed; + u32 advertising; + u16 eeprom_len; + u16 cur_page; + u32 bios_setting; + + 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 features; + + u8 org_pci_offset_99; + u8 org_pci_offset_180; + u8 issue_offset_99_event; + + u8 org_pci_offset_80; + u8 org_pci_offset_81; + u8 use_timer_interrrupt; + + u32 keep_intr_cnt; + + u8 HwIcVerUnknown; + u8 NotWrRamCodeToMicroP; + u8 NotWrMcuPatchCode; + u8 HwHasWrRamCodeToMicroP; + + u16 sw_ram_code_ver; + u16 hw_ram_code_ver; + + u8 rtk_enable_diag; + + u8 ShortPacketSwChecksum; + + u8 UseSwPaddingShortPkt; + + u8 RequireAdcBiasPatch; + u16 AdcBiasPatchIoffset; + + u8 RequireAdjustUpsTxLinkPulseTiming; + u16 SwrCnt1msIni; + + u8 HwSuppNowIsOobVer; + + u8 RequiredSecLanDonglePatch; + + u32 HwFiberModeVer; + u32 HwFiberStat; + u8 HwSwitchMdiToFiber; + + u8 HwSuppSerDesPhyVer; + + u8 HwSuppPhyOcpVer; + + u8 HwSuppAspmClkIntrLock; + + u16 NicCustLedValue; + + u8 HwSuppUpsVer; + + u8 HwSuppMagicPktVer; + + u8 HwSuppCheckPhyDisableModeVer; + + u8 random_mac; + + u16 phy_reg_aner; + u16 phy_reg_anlpar; + u16 phy_reg_gbsr; + + u32 HwPcieSNOffset; + + u8 HwSuppEsdVer; + u8 TestPhyOcpReg; + u16 BackupPhyFuseDout_15_0; + u16 BackupPhyFuseDout_47_32; + u16 BackupPhyFuseDout_63_48; + + const char *fw_name; + struct rtl8168_fw *rtl_fw; + u32 ocp_base; + + //Dash+++++++++++++++++ + u8 HwSuppDashVer; + u8 DASH; + u8 dash_printer_enabled; + u8 HwPkgDet; + void __iomem *mapped_cmac_ioaddr; /* mapped cmac memory map physical address */ + void __iomem *cmac_ioaddr; /* cmac memory map physical address */ + +#ifdef ENABLE_DASH_SUPPORT + u16 AfterRecvFromFwBufLen; + u8 AfterRecvFromFwBuf[RECV_FROM_FW_BUF_SIZE]; + u16 AfterSendToFwBufLen; + u8 AfterSendToFwBuf[SEND_TO_FW_BUF_SIZE]; + u16 SendToFwBufferLen; + u32 SizeOfSendToFwBuffer ; + u32 SizeOfSendToFwBufferMemAlloc ; + u32 NumOfSendToFwBuffer ; + + u8 OobReq; + u8 OobAck; + u32 OobReqComplete; + u32 OobAckComplete; + + u8 RcvFwReqSysOkEvt; + u8 RcvFwDashOkEvt; + u8 SendFwHostOkEvt; + + u8 DashFwDisableRx; + + void *SendToFwBuffer ; + dma_addr_t SendToFwBufferPhy ; + u8 SendingToFw; + PTX_DASH_SEND_FW_DESC TxDashSendFwDesc; + dma_addr_t TxDashSendFwDescPhy; + u32 SizeOfTxDashSendFwDescMemAlloc; + u32 SizeOfTxDashSendFwDesc ; + u32 NumTxDashSendFwDesc ; + u32 CurrNumTxDashSendFwDesc ; + u32 LastSendNumTxDashSendFwDesc ; + + u32 NumRecvFromFwBuffer ; + u32 SizeOfRecvFromFwBuffer ; + u32 SizeOfRecvFromFwBufferMemAlloc ; + void *RecvFromFwBuffer ; + dma_addr_t RecvFromFwBufferPhy ; + + PRX_DASH_FROM_FW_DESC RxDashRecvFwDesc; + dma_addr_t RxDashRecvFwDescPhy; + u32 SizeOfRxDashRecvFwDescMemAlloc; + u32 SizeOfRxDashRecvFwDesc ; + u32 NumRxDashRecvFwDesc ; + u32 CurrNumRxDashRecvFwDesc ; + u8 DashReqRegValue; + u16 HostReqValue; + + u32 CmacResetIsrCounter; + u8 CmacResetIntr ; + u8 CmacResetting ; + u8 CmacOobIssueCmacReset ; + u32 CmacResetbyFwCnt; + +#if defined(ENABLE_DASH_PRINTER_SUPPORT) + struct completion fw_ack; + struct completion fw_req; + struct completion fw_host_ok; +#endif + //Dash----------------- +#endif //ENABLE_DASH_SUPPORT + + //Realwow++++++++++++++ + u8 HwSuppKCPOffloadVer; + + u8 EnableDhcpTimeoutWake; + u8 EnableTeredoOffload; + u8 EnableKCPOffload; +#ifdef ENABLE_REALWOW_SUPPORT + u32 DhcpTimeout; + MP_KCP_INFO MpKCPInfo; + //Realwow-------------- +#endif //ENABLE_REALWOW_SUPPORT + + u32 eee_adv_t; + u8 eee_enabled; + + u32 dynamic_aspm_packet_count; + +#ifdef ENABLE_R8168_PROCFS + //Procfs support + struct proc_dir_entry *proc_dir; +#endif +}; + +enum eetype { + EEPROM_TYPE_NONE=0, + EEPROM_TYPE_93C46, + EEPROM_TYPE_93C56, + EEPROM_TWSI +}; + +enum mcfg { + CFG_METHOD_1=0, + CFG_METHOD_2, + CFG_METHOD_3, + CFG_METHOD_4, + CFG_METHOD_5, + CFG_METHOD_6, + CFG_METHOD_7, + CFG_METHOD_8, + CFG_METHOD_9 , + CFG_METHOD_10, + CFG_METHOD_11, + CFG_METHOD_12, + CFG_METHOD_13, + CFG_METHOD_14, + CFG_METHOD_15, + CFG_METHOD_16, + CFG_METHOD_17, + CFG_METHOD_18, + CFG_METHOD_19, + CFG_METHOD_20, + CFG_METHOD_21, + CFG_METHOD_22, + CFG_METHOD_23, + CFG_METHOD_24, + CFG_METHOD_25, + CFG_METHOD_26, + CFG_METHOD_27, + CFG_METHOD_28, + CFG_METHOD_29, + CFG_METHOD_30, + CFG_METHOD_31, + CFG_METHOD_32, + CFG_METHOD_33, + CFG_METHOD_MAX, + CFG_METHOD_DEFAULT = 0xFF +}; + +#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 GTTCPHO_SHIFT 18 +#define GTTCPHO_MAX 0x7fU +#define GTPKTSIZE_MAX 0x3ffffU +#define TCPHO_SHIFT 18 +#define TCPHO_MAX 0x3ffU +#define LSOPKTSIZE_MAX 0xffffU +#define MSS_MAX 0x07ffu /* MSS value */ + +#define OOB_CMD_RESET 0x00 +#define OOB_CMD_DRIVER_START 0x05 +#define OOB_CMD_DRIVER_STOP 0x06 +#define OOB_CMD_SET_IPMAC 0x41 + +#define WAKEUP_MAGIC_PACKET_NOT_SUPPORT (0) +#define WAKEUP_MAGIC_PACKET_V1 (1) +#define WAKEUP_MAGIC_PACKET_V2 (2) + +//Ram Code Version +#define NIC_RAMCODE_VERSION_CFG_METHOD_14 (0x0057) +#define NIC_RAMCODE_VERSION_CFG_METHOD_16 (0x0055) +#define NIC_RAMCODE_VERSION_CFG_METHOD_18 (0x0052) +#define NIC_RAMCODE_VERSION_CFG_METHOD_20 (0x0044) +#define NIC_RAMCODE_VERSION_CFG_METHOD_21 (0x0042) +#define NIC_RAMCODE_VERSION_CFG_METHOD_24 (0x0001) +#define NIC_RAMCODE_VERSION_CFG_METHOD_23 (0x0015) +#define NIC_RAMCODE_VERSION_CFG_METHOD_26 (0x0012) +#define NIC_RAMCODE_VERSION_CFG_METHOD_28 (0x0019) +#define NIC_RAMCODE_VERSION_CFG_METHOD_29 (0x0055) +#define NIC_RAMCODE_VERSION_CFG_METHOD_31 (0x0003) + +//hwoptimize +#define HW_PATCH_SOC_LAN (BIT_0) +#define HW_PATCH_SAMSUNG_LAN_DONGLE (BIT_2) + +#define HW_PHY_STATUS_INI 1 +#define HW_PHY_STATUS_EXT_INI 2 +#define HW_PHY_STATUS_LAN_ON 3 + +void rtl8168_mdio_write(struct rtl8168_private *tp, u16 RegAddr, u16 value); +void rtl8168_mdio_prot_write(struct rtl8168_private *tp, u32 RegAddr, u32 value); +void rtl8168_mdio_prot_direct_write_phy_ocp(struct rtl8168_private *tp, u32 RegAddr, u32 value); +u32 rtl8168_mdio_read(struct rtl8168_private *tp, u16 RegAddr); +u32 rtl8168_mdio_prot_read(struct rtl8168_private *tp, u32 RegAddr); +u32 rtl8168_mdio_prot_direct_read_phy_ocp(struct rtl8168_private *tp, u32 RegAddr); +void rtl8168_ephy_write(struct rtl8168_private *tp, int RegAddr, int value); +void rtl8168_mac_ocp_write(struct rtl8168_private *tp, u16 reg_addr, u16 value); +u16 rtl8168_mac_ocp_read(struct rtl8168_private *tp, u16 reg_addr); +void rtl8168_clear_eth_phy_bit(struct rtl8168_private *tp, u8 addr, u16 mask); +void rtl8168_set_eth_phy_bit(struct rtl8168_private *tp, u8 addr, u16 mask); +void rtl8168_ocp_write(struct rtl8168_private *tp, u16 addr, u8 len, u32 data); +void rtl8168_oob_notify(struct rtl8168_private *tp, u8 cmd); +void rtl8168_init_ring_indexes(struct rtl8168_private *tp); +int rtl8168_eri_write(struct rtl8168_private *tp, int addr, int len, u32 value, int type); +void rtl8168_oob_mutex_lock(struct rtl8168_private *tp); +u32 rtl8168_ocp_read(struct rtl8168_private *tp, u16 addr, u8 len); +u32 rtl8168_ocp_read_with_oob_base_address(struct rtl8168_private *tp, u16 addr, u8 len, u32 base_address); +u32 rtl8168_ocp_write_with_oob_base_address(struct rtl8168_private *tp, u16 addr, u8 len, u32 value, u32 base_address); +u32 rtl8168_eri_read(struct rtl8168_private *tp, int addr, int len, int type); +u32 rtl8168_eri_read_with_oob_base_address(struct rtl8168_private *tp, int addr, int len, int type, u32 base_address); +int rtl8168_eri_write_with_oob_base_address(struct rtl8168_private *tp, int addr, int len, u32 value, int type, u32 base_address); +u16 rtl8168_ephy_read(struct rtl8168_private *tp, int RegAddr); +void rtl8168_wait_txrx_fifo_empty(struct net_device *dev); +void rtl8168_wait_ll_share_fifo_ready(struct net_device *dev); +void rtl8168_enable_now_is_oob(struct rtl8168_private *tp); +void rtl8168_disable_now_is_oob(struct rtl8168_private *tp); +void rtl8168_oob_mutex_unlock(struct rtl8168_private *tp); +void rtl8168_dash2_disable_tx(struct rtl8168_private *tp); +void rtl8168_dash2_enable_tx(struct rtl8168_private *tp); +void rtl8168_dash2_disable_rx(struct rtl8168_private *tp); +void rtl8168_dash2_enable_rx(struct rtl8168_private *tp); +void rtl8168_hw_disable_mac_mcu_bps(struct net_device *dev); + +#define HW_SUPPORT_CHECK_PHY_DISABLE_MODE(_M) ((_M)->HwSuppCheckPhyDisableModeVer > 0 ) +#define HW_SUPP_SERDES_PHY(_M) ((_M)->HwSuppSerDesPhyVer > 0) +#define HW_HAS_WRITE_PHY_MCU_RAM_CODE(_M) (((_M)->HwHasWrRamCodeToMicroP == TRUE) ? 1 : 0) +#define HW_SUPPORT_UPS_MODE(_M) ((_M)->HwSuppUpsVer > 0) + +#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) +#define netdev_for_each_mc_addr(mclist, dev) \ + for (mclist = dev->mc_list; mclist; mclist = mclist->next) +#endif diff --git a/drivers/net/ethernet/realtek/r8168/r8168_asf.c b/drivers/net/ethernet/realtek/r8168/r8168_asf.c new file mode 100644 index 000000000000..77524f0450ec --- /dev/null +++ b/drivers/net/ethernet/realtek/r8168/r8168_asf.c @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* +################################################################################ +# +# r8168 is the Linux device driver released for Realtek Gigabit Ethernet +# controllers with PCI-Express interface. +# +# Copyright(c) 2021 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "r8168.h" +#include "r8168_asf.h" +#include "rtl_eeprom.h" + +int rtl8168_asf_ioctl(struct net_device *dev, + struct ifreq *ifr) +{ + struct rtl8168_private *tp = netdev_priv(dev); + void *user_data = ifr->ifr_data; + struct asf_ioctl_struct asf_usrdata; + unsigned long flags; + + if (tp->mcfg != CFG_METHOD_7 && tp->mcfg != CFG_METHOD_8) + return -EOPNOTSUPP; + + if (copy_from_user(&asf_usrdata, user_data, sizeof(struct asf_ioctl_struct))) + return -EFAULT; + + spin_lock_irqsave(&tp->lock, flags); + + switch (asf_usrdata.offset) { + case HBPeriod: + rtl8168_asf_hbperiod(tp, asf_usrdata.arg, asf_usrdata.u.data); + break; + case WD8Timer: + break; + case WD16Rst: + rtl8168_asf_wd16rst(tp, asf_usrdata.arg, asf_usrdata.u.data); + break; + case WD8Rst: + rtl8168_asf_time_period(tp, asf_usrdata.arg, WD8Rst, asf_usrdata.u.data); + break; + case LSnsrPollCycle: + rtl8168_asf_time_period(tp, asf_usrdata.arg, LSnsrPollCycle, asf_usrdata.u.data); + break; + case ASFSnsrPollPrd: + rtl8168_asf_time_period(tp, asf_usrdata.arg, ASFSnsrPollPrd, asf_usrdata.u.data); + break; + case AlertReSendItvl: + rtl8168_asf_time_period(tp, asf_usrdata.arg, AlertReSendItvl, asf_usrdata.u.data); + break; + case SMBAddr: + rtl8168_asf_rw_hexadecimal(tp, asf_usrdata.arg, SMBAddr, RW_ONE_BYTE, asf_usrdata.u.data); + break; + case ASFConfigR0: + rtl8168_asf_config_regs(tp, asf_usrdata.arg, ASFConfigR0, asf_usrdata.u.data); + break; + case ASFConfigR1: + rtl8168_asf_config_regs(tp, asf_usrdata.arg, ASFConfigR1, asf_usrdata.u.data); + break; + case ConsoleMA: + rtl8168_asf_console_mac(tp, asf_usrdata.arg, asf_usrdata.u.data); + break; + case ConsoleIP: + rtl8168_asf_ip_address(tp, asf_usrdata.arg, ConsoleIP, asf_usrdata.u.data); + break; + case IPAddr: + rtl8168_asf_ip_address(tp, asf_usrdata.arg, IPAddr, asf_usrdata.u.data); + break; + case UUID: + rtl8168_asf_rw_uuid(tp, asf_usrdata.arg, asf_usrdata.u.data); + break; + case IANA: + rtl8168_asf_rw_iana(tp, asf_usrdata.arg, asf_usrdata.u.data); + break; + case SysID: + rtl8168_asf_rw_systemid(tp, asf_usrdata.arg, asf_usrdata.u.data); + break; + case Community: + rtl8168_asf_community_string(tp, asf_usrdata.arg, asf_usrdata.u.string); + break; + case StringLength: + rtl8168_asf_community_string_len(tp, asf_usrdata.arg, asf_usrdata.u.data); + break; + case FmCapMsk: + rtl8168_asf_capability_masks(tp, asf_usrdata.arg, FmCapMsk, asf_usrdata.u.data); + break; + case SpCMDMsk: + rtl8168_asf_capability_masks(tp, asf_usrdata.arg, SpCMDMsk, asf_usrdata.u.data); + break; + case SysCapMsk: + rtl8168_asf_capability_masks(tp, asf_usrdata.arg, SysCapMsk, asf_usrdata.u.data); + break; + case RmtRstAddr: + rtl8168_asf_rw_hexadecimal(tp, asf_usrdata.arg, RmtRstAddr, RW_ONE_BYTE, asf_usrdata.u.data); + break; + case RmtRstCmd: + rtl8168_asf_rw_hexadecimal(tp, asf_usrdata.arg, RmtRstCmd, RW_ONE_BYTE, asf_usrdata.u.data); + break; + case RmtRstData: + rtl8168_asf_rw_hexadecimal(tp, asf_usrdata.arg, RmtRstData, RW_ONE_BYTE, asf_usrdata.u.data); + break; + case RmtPwrOffAddr: + rtl8168_asf_rw_hexadecimal(tp, asf_usrdata.arg, RmtPwrOffAddr, RW_ONE_BYTE, asf_usrdata.u.data); + break; + case RmtPwrOffCmd: + rtl8168_asf_rw_hexadecimal(tp, asf_usrdata.arg, RmtPwrOffCmd, RW_ONE_BYTE, asf_usrdata.u.data); + break; + case RmtPwrOffData: + rtl8168_asf_rw_hexadecimal(tp, asf_usrdata.arg, RmtPwrOffData, RW_ONE_BYTE, asf_usrdata.u.data); + break; + case RmtPwrOnAddr: + rtl8168_asf_rw_hexadecimal(tp, asf_usrdata.arg, RmtPwrOnAddr, RW_ONE_BYTE, asf_usrdata.u.data); + break; + case RmtPwrOnCmd: + rtl8168_asf_rw_hexadecimal(tp, asf_usrdata.arg, RmtPwrOnCmd, RW_ONE_BYTE, asf_usrdata.u.data); + break; + case RmtPwrOnData: + rtl8168_asf_rw_hexadecimal(tp, asf_usrdata.arg, RmtPwrOnData, RW_ONE_BYTE, asf_usrdata.u.data); + break; + case RmtPCRAddr: + rtl8168_asf_rw_hexadecimal(tp, asf_usrdata.arg, RmtPCRAddr, RW_ONE_BYTE, asf_usrdata.u.data); + break; + case RmtPCRCmd: + rtl8168_asf_rw_hexadecimal(tp, asf_usrdata.arg, RmtPCRCmd, RW_ONE_BYTE, asf_usrdata.u.data); + break; + case RmtPCRData: + rtl8168_asf_rw_hexadecimal(tp, asf_usrdata.arg, RmtPCRData, RW_ONE_BYTE, asf_usrdata.u.data); + break; + case ASFSnsr0Addr: + rtl8168_asf_rw_hexadecimal(tp, asf_usrdata.arg, ASFSnsr0Addr, RW_ONE_BYTE, asf_usrdata.u.data); + break; + case LSnsrAddr0: + rtl8168_asf_rw_hexadecimal(tp, asf_usrdata.arg, LSnsrAddr0, RW_ONE_BYTE, asf_usrdata.u.data); + break; + case KO: + /* Get/Set Key Operation */ + rtl8168_asf_key_access(tp, asf_usrdata.arg, KO, asf_usrdata.u.data); + break; + case KA: + /* Get/Set Key Administrator */ + rtl8168_asf_key_access(tp, asf_usrdata.arg, KA, asf_usrdata.u.data); + break; + case KG: + /* Get/Set Key Generation */ + rtl8168_asf_key_access(tp, asf_usrdata.arg, KG, asf_usrdata.u.data); + break; + case KR: + /* Get/Set Key Random */ + rtl8168_asf_key_access(tp, asf_usrdata.arg, KR, asf_usrdata.u.data); + break; + default: + spin_unlock_irqrestore(&tp->lock, flags); + return -EOPNOTSUPP; + } + + spin_unlock_irqrestore(&tp->lock, flags); + + if (copy_to_user(user_data, &asf_usrdata, sizeof(struct asf_ioctl_struct))) + return -EFAULT; + + return 0; +} + +void rtl8168_asf_hbperiod(struct rtl8168_private *tp, int arg, unsigned int *data) +{ + if (arg == ASF_GET) + data[ASFHBPERIOD] = rtl8168_eri_read(tp, HBPeriod, RW_TWO_BYTES, ERIAR_ASF); + else if (arg == ASF_SET) { + rtl8168_eri_write(tp, HBPeriod, RW_TWO_BYTES, data[ASFHBPERIOD], ERIAR_ASF); + rtl8168_eri_write(tp, 0x1EC, RW_ONE_BYTE, 0x07, ERIAR_ASF); + } +} + +void rtl8168_asf_wd16rst(struct rtl8168_private *tp, int arg, unsigned int *data) +{ + data[ASFWD16RST] = rtl8168_eri_read(tp, WD16Rst, RW_TWO_BYTES, ERIAR_ASF); +} + +void rtl8168_asf_console_mac(struct rtl8168_private *tp, int arg, unsigned int *data) +{ + int i; + + if (arg == ASF_GET) { + for (i = 0; i < 6; i++) + data[i] = rtl8168_eri_read(tp, ConsoleMA + i, RW_ONE_BYTE, ERIAR_ASF); + } else if (arg == ASF_SET) { + for (i = 0; i < 6; i++) + rtl8168_eri_write(tp, ConsoleMA + i, RW_ONE_BYTE, data[i], ERIAR_ASF); + + /* write the new console MAC address to EEPROM */ + rtl8168_eeprom_write_sc(tp, 70, (data[1] << 8) | data[0]); + rtl8168_eeprom_write_sc(tp, 71, (data[3] << 8) | data[2]); + rtl8168_eeprom_write_sc(tp, 72, (data[5] << 8) | data[4]); + } +} + +void rtl8168_asf_ip_address(struct rtl8168_private *tp, int arg, int offset, unsigned int *data) +{ + int i; + int eeprom_off = 0; + + if (arg == ASF_GET) { + for (i = 0; i < 4; i++) + data[i] = rtl8168_eri_read(tp, offset + i, RW_ONE_BYTE, ERIAR_ASF); + } else if (arg == ASF_SET) { + for (i = 0; i < 4; i++) + rtl8168_eri_write(tp, offset + i, RW_ONE_BYTE, data[i], ERIAR_ASF); + + if (offset == ConsoleIP) + eeprom_off = 73; + else if (offset == IPAddr) + eeprom_off = 75; + + /* write the new IP address to EEPROM */ + rtl8168_eeprom_write_sc(tp, eeprom_off, (data[1] << 8) | data[0]); + rtl8168_eeprom_write_sc(tp, eeprom_off + 1, (data[3] << 8) | data[2]); + + } +} + +void rtl8168_asf_config_regs(struct rtl8168_private *tp, int arg, int offset, unsigned int *data) +{ + unsigned int value; + + if (arg == ASF_GET) { + data[ASFCAPABILITY] = (rtl8168_eri_read(tp, offset, RW_ONE_BYTE, ERIAR_ASF) & data[ASFCONFIG]) ? FUNCTION_ENABLE : FUNCTION_DISABLE; + } else if (arg == ASF_SET) { + value = rtl8168_eri_read(tp, offset, RW_ONE_BYTE, ERIAR_ASF); + + if (data[ASFCAPABILITY] == FUNCTION_ENABLE) + value |= data[ASFCONFIG]; + else if (data[ASFCAPABILITY] == FUNCTION_DISABLE) + value &= ~data[ASFCONFIG]; + + rtl8168_eri_write(tp, offset, RW_ONE_BYTE, value, ERIAR_ASF); + } +} + +void rtl8168_asf_capability_masks(struct rtl8168_private *tp, int arg, int offset, unsigned int *data) +{ + unsigned int len, bit_mask; + + bit_mask = DISABLE_MASK; + + if (offset == FmCapMsk) { + /* System firmware capabilities */ + len = RW_FOUR_BYTES; + if (data[ASFCAPMASK] == FUNCTION_ENABLE) + bit_mask = FMW_CAP_MASK; + } else if (offset == SpCMDMsk) { + /* Special commands */ + len = RW_TWO_BYTES; + if (data[ASFCAPMASK] == FUNCTION_ENABLE) + bit_mask = SPC_CMD_MASK; + } else { + /* System capability (offset == SysCapMsk)*/ + len = RW_ONE_BYTE; + if (data[ASFCAPMASK] == FUNCTION_ENABLE) + bit_mask = SYS_CAP_MASK; + } + + if (arg == ASF_GET) + data[ASFCAPMASK] = rtl8168_eri_read(tp, offset, len, ERIAR_ASF) ? FUNCTION_ENABLE : FUNCTION_DISABLE; + else /* arg == ASF_SET */ + rtl8168_eri_write(tp, offset, len, bit_mask, ERIAR_ASF); +} + +void rtl8168_asf_community_string(struct rtl8168_private *tp, int arg, char *string) +{ + int i; + + if (arg == ASF_GET) { + for (i = 0; i < COMMU_STR_MAX_LEN; i++) + string[i] = rtl8168_eri_read(tp, Community + i, RW_ONE_BYTE, ERIAR_ASF); + } else { /* arg == ASF_SET */ + for (i = 0; i < COMMU_STR_MAX_LEN; i++) + rtl8168_eri_write(tp, Community + i, RW_ONE_BYTE, string[i], ERIAR_ASF); + } +} + +void rtl8168_asf_community_string_len(struct rtl8168_private *tp, int arg, unsigned int *data) +{ + if (arg == ASF_GET) + data[ASFCOMMULEN] = rtl8168_eri_read(tp, StringLength, RW_ONE_BYTE, ERIAR_ASF); + else /* arg == ASF_SET */ + rtl8168_eri_write(tp, StringLength, RW_ONE_BYTE, data[ASFCOMMULEN], ERIAR_ASF); +} + +void rtl8168_asf_time_period(struct rtl8168_private *tp, int arg, int offset, unsigned int *data) +{ + int pos = 0; + + if (offset == WD8Rst) + pos = ASFWD8RESET; + else if (offset == LSnsrPollCycle) + pos = ASFLSNRPOLLCYC; + else if (offset == ASFSnsrPollPrd) + pos = ASFSNRPOLLCYC; + else if (offset == AlertReSendItvl) + pos = ASFALERTRESND; + + if (arg == ASF_GET) + data[pos] = rtl8168_eri_read(tp, offset, RW_ONE_BYTE, ERIAR_ASF); + else /* arg == ASF_SET */ + rtl8168_eri_write(tp, offset, RW_ONE_BYTE, data[pos], ERIAR_ASF); + +} + +void rtl8168_asf_key_access(struct rtl8168_private *tp, int arg, int offset, unsigned int *data) +{ + int i, j; + int key_off = 0; + + if (arg == ASF_GET) { + for (i = 0; i < KEY_LEN; i++) + data[i] = rtl8168_eri_read(tp, offset + KEY_LEN - (i + 1), RW_ONE_BYTE, ERIAR_ASF); + } else { + if (offset == KO) + key_off = 162; + else if (offset == KA) + key_off = 172; + else if (offset == KG) + key_off = 182; + else if (offset == KR) + key_off = 192; + + /* arg == ASF_SET */ + for (i = 0; i < KEY_LEN; i++) + rtl8168_eri_write(tp, offset + KEY_LEN - (i + 1), RW_ONE_BYTE, data[i], ERIAR_ASF); + + /* write the new key to EEPROM */ + for (i = 0, j = 19; i < 10; i++, j = j - 2) + rtl8168_eeprom_write_sc(tp, key_off + i, (data[j - 1] << 8) | data[j]); + } +} + +void rtl8168_asf_rw_hexadecimal(struct rtl8168_private *tp, int arg, int offset, int len, unsigned int *data) +{ + if (arg == ASF_GET) + data[ASFRWHEXNUM] = rtl8168_eri_read(tp, offset, len, ERIAR_ASF); + else /* arg == ASF_SET */ + rtl8168_eri_write(tp, offset, len, data[ASFRWHEXNUM], ERIAR_ASF); +} + +void rtl8168_asf_rw_systemid(struct rtl8168_private *tp, int arg, unsigned int *data) +{ + int i; + + if (arg == ASF_GET) + for (i = 0; i < SYSID_LEN ; i++) + data[i] = rtl8168_eri_read(tp, SysID + i, RW_ONE_BYTE, ERIAR_ASF); + else /* arg == ASF_SET */ + for (i = 0; i < SYSID_LEN ; i++) + rtl8168_eri_write(tp, SysID + i, RW_ONE_BYTE, data[i], ERIAR_ASF); +} + +void rtl8168_asf_rw_iana(struct rtl8168_private *tp, int arg, unsigned int *data) +{ + int i; + + if (arg == ASF_GET) + for (i = 0; i < RW_FOUR_BYTES; i++) + data[i] = rtl8168_eri_read(tp, IANA + i, RW_ONE_BYTE, ERIAR_ASF); + else /* arg == ASF_SET */ + for (i = 0; i < RW_FOUR_BYTES; i++) + rtl8168_eri_write(tp, IANA + i, RW_ONE_BYTE, data[i], ERIAR_ASF); +} + +void rtl8168_asf_rw_uuid(struct rtl8168_private *tp, int arg, unsigned int *data) +{ + int i, j; + + if (arg == ASF_GET) + for (i = UUID_LEN - 1, j = 0; i >= 0 ; i--, j++) + data[j] = rtl8168_eri_read(tp, UUID + i, RW_ONE_BYTE, ERIAR_ASF); + else /* arg == ASF_SET */ + for (i = UUID_LEN - 1, j = 0; i >= 0 ; i--, j++) + rtl8168_eri_write(tp, UUID + i, RW_ONE_BYTE, data[j], ERIAR_ASF); +} diff --git a/drivers/net/ethernet/realtek/r8168/r8168_asf.h b/drivers/net/ethernet/realtek/r8168/r8168_asf.h new file mode 100644 index 000000000000..c4b412a6314b --- /dev/null +++ b/drivers/net/ethernet/realtek/r8168/r8168_asf.h @@ -0,0 +1,295 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* +################################################################################ +# +# r8168 is the Linux device driver released for Realtek Gigabit Ethernet +# controllers with PCI-Express interface. +# +# Copyright(c) 2021 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. + ***********************************************************************************/ + +#define SIOCDEVPRIVATE_RTLASF SIOCDEVPRIVATE + +#define FUNCTION_ENABLE 1 +#define FUNCTION_DISABLE 0 + +#define ASFCONFIG 0 +#define ASFCAPABILITY 1 +#define ASFCOMMULEN 0 +#define ASFHBPERIOD 0 +#define ASFWD16RST 0 +#define ASFCAPMASK 0 +#define ASFALERTRESND 0 +#define ASFLSNRPOLLCYC 0 +#define ASFSNRPOLLCYC 0 +#define ASFWD8RESET 0 +#define ASFRWHEXNUM 0 + +#define FMW_CAP_MASK 0x0000F867 +#define SPC_CMD_MASK 0x1F00 +#define SYS_CAP_MASK 0xFF +#define DISABLE_MASK 0x00 + +#define MAX_DATA_LEN 200 +#define MAX_STR_LEN 200 + +#define COMMU_STR_MAX_LEN 23 + +#define KEY_LEN 20 +#define UUID_LEN 16 +#define SYSID_LEN 2 + +#define RW_ONE_BYTE 1 +#define RW_TWO_BYTES 2 +#define RW_FOUR_BYTES 4 + +enum asf_registers { + HBPeriod = 0x0000, + WD8Rst = 0x0002, + WD8Timer = 0x0003, + WD16Rst = 0x0004, + LSnsrPollCycle = 0x0006, + ASFSnsrPollPrd = 0x0007, + AlertReSendCnt = 0x0008, + AlertReSendItvl = 0x0009, + SMBAddr = 0x000A, + SMBCap = 0x000B, + ASFConfigR0 = 0x000C, + ASFConfigR1 = 0x000D, + WD16Timer = 0x000E, + ConsoleMA = 0x0010, + ConsoleIP = 0x0016, + IPAddr = 0x001A, + + UUID = 0x0020, + IANA = 0x0030, + SysID = 0x0034, + Community = 0x0036, + StringLength = 0x004D, + LC = 0x004E, + EntityInst = 0x004F, + FmCapMsk = 0x0050, + SpCMDMsk = 0x0054, + SysCapMsk = 0x0056, + WDSysSt = 0x0057, + RxMsgType = 0x0058, + RxSpCMD = 0x0059, + RxSpCMDPa = 0x005A, + RxBtOpMsk = 0x005C, + RmtRstAddr = 0x005E, + RmtRstCmd = 0x005F, + RmtRstData = 0x0060, + RmtPwrOffAddr = 0x0061, + RmtPwrOffCmd = 0x0062, + RmtPwrOffData = 0x0063, + RmtPwrOnAddr = 0x0064, + RmtPwrOnCmd = 0x0065, + RmtPwrOnData = 0x0066, + RmtPCRAddr = 0x0067, + RmtPCRCmd = 0x0068, + RmtPCRData = 0x0069, + RMCP_IANA = 0x006A, + RMCP_OEM = 0x006E, + ASFSnsr0Addr = 0x0070, + + ASFSnsrEvSt = 0x0073, + ASFSnsrEvAlert = 0x0081, + + LSnsrNo = 0x00AD, + AssrtEvntMsk = 0x00AE, + DeAssrtEvntMsk = 0x00AF, + + LSnsrAddr0 = 0x00B0, + LAlertCMD0 = 0x00B1, + LAlertDataMsk0 = 0x00B2, + LAlertCmp0 = 0x00B3, + LAlertESnsrT0 = 0x00B4, + LAlertET0 = 0x00B5, + LAlertEOffset0 = 0x00B6, + LAlertES0 = 0x00B7, + LAlertSN0 = 0x00B8, + LAlertEntity0 = 0x00B9, + LAlertEI0 = 0x00BA, + LSnsrState0 = 0x00BB, + + LSnsrAddr1 = 0x00BD, + LAlertCMD1 = 0x00BE, + LAlertDataMsk1 = 0x00BF, + LAlertCmp1 = 0x00C0, + LAlertESnsrT1 = 0x00C1, + LAlertET1 = 0x00C2, + LAlertEOffset1 = 0x00C3, + LAlertES1 = 0x00C4, + LAlertSN1 = 0x00C5, + LAlertEntity1 = 0x00C6, + LAlertEI1 = 0x00C7, + LSnsrState1 = 0x00C8, + + LSnsrAddr2 = 0x00CA, + LAlertCMD2 = 0x00CB, + LAlertDataMsk2 = 0x00CC, + LAlertCmp2 = 0x00CD, + LAlertESnsrT2 = 0x00CE, + LAlertET2 = 0x00CF, + LAlertEOffset2 = 0x00D0, + LAlertES2 = 0x00D1, + LAlertSN2 = 0x00D2, + LAlertEntity2 = 0x00D3, + LAlertEI2 = 0x00D4, + LSnsrState2 = 0x00D5, + + LSnsrAddr3 = 0x00D7, + LAlertCMD3 = 0x00D8, + LAlertDataMsk3 = 0x00D9, + LAlertCmp3 = 0x00DA, + LAlertESnsrT3 = 0x00DB, + LAlertET3 = 0x00DC, + LAlertEOffset3 = 0x00DD, + LAlertES3 = 0x00DE, + LAlertSN3 = 0x00DF, + LAlertEntity3 = 0x00E0, + LAlertEI3 = 0x00E1, + LSnsrState3 = 0x00E2, + + LSnsrAddr4 = 0x00E4, + LAlertCMD4 = 0x00E5, + LAlertDataMsk4 = 0x00E6, + LAlertCmp4 = 0x00E7, + LAlertESnsrT4 = 0x00E8, + LAlertET4 = 0x00E9, + LAlertEOffset4 = 0x00EA, + LAlertES4 = 0x00EB, + LAlertSN4 = 0x00EC, + LAlertEntity4 = 0x00ED, + LAlertEI4 = 0x00EE, + LSnsrState4 = 0x00EF, + + LSnsrAddr5 = 0x00F1, + LAlertCMD5 = 0x00F2, + LAlertDataMsk5 = 0x00F3, + LAlertCmp5 = 0x00F4, + LAlertESnsrT5 = 0x00F5, + LAlertET5 = 0x00F6, + LAlertEOffset5 = 0x00F7, + LAlertES5 = 0x00F8, + LAlertSN5 = 0x00F9, + LAlertEntity5 = 0x00FA, + LAlertEI5 = 0x00FB, + LSnsrState5 = 0x00FC, + + LSnsrAddr6 = 0x00FE, + LAlertCMD6 = 0x00FF, + LAlertDataMsk6 = 0x0100, + LAlertCmp6 = 0x0101, + LAlertESnsrT6 = 0x0102, + LAlertET6 = 0x0103, + LAlertEOffset6 = 0x0104, + LAlertES6 = 0x0105, + LAlertSN6 = 0x0106, + LAlertEntity6 = 0x0107, + LAlertEI6 = 0x0108, + LSnsrState6 = 0x0109, + + LSnsrAddr7 = 0x010B, + LAlertCMD7 = 0x010C, + LAlertDataMsk7 = 0x010D, + LAlertCmp7 = 0x010E, + LAlertESnsrT7 = 0x010F, + LAlertET7 = 0x0110, + LAlertEOffset7 = 0x0111, + LAlertES7 = 0x0112, + LAlertSN7 = 0x0113, + LAlertEntity7 = 0x0114, + LAlertEI7 = 0x0115, + LSnsrState7 = 0x0116, + LAssert = 0x0117, + LDAssert = 0x0118, + IPServiceType = 0x0119, + IPIdfr = 0x011A, + FlagFOffset = 0x011C, + TTL = 0x011E, + HbtEI = 0x011F, + MgtConSID1 = 0x0120, + MgtConSID2 = 0x0124, + MgdCltSID = 0x0128, + StCd = 0x012C, + MgtConUR = 0x012D, + MgtConUNL = 0x012E, + + AuthPd = 0x0130, + IntyPd = 0x0138, + MgtConRN = 0x0140, + MgdCtlRN = 0x0150, + MgtConUN = 0x0160, + Rakp2IntCk = 0x0170, + KO = 0x017C, + KA = 0x0190, + KG = 0x01A4, + KR = 0x01B8, + CP = 0x01CC, + CQ = 0x01D0, + KC = 0x01D4, + ConsoleSid = 0x01E8, + + SIK1 = 0x01FC, + SIK2 = 0x0210, + Udpsrc_port = 0x0224, + Udpdes_port = 0x0226, + Asf_debug_mux = 0x0228 +}; + +enum asf_cmdln_opt { + ASF_GET, + ASF_SET, + ASF_HELP +}; + +struct asf_ioctl_struct { + unsigned int arg; + unsigned int offset; + union { + unsigned int data[MAX_DATA_LEN]; + char string[MAX_STR_LEN]; + } u; +}; + +int rtl8168_asf_ioctl(struct net_device *dev, struct ifreq *ifr); +void rtl8168_asf_hbperiod(struct rtl8168_private *tp, int arg, unsigned int *data); +void rtl8168_asf_wd16rst(struct rtl8168_private *tp, int arg, unsigned int *data); +void rtl8168_asf_console_mac(struct rtl8168_private *, int arg, unsigned int *data); +void rtl8168_asf_ip_address(struct rtl8168_private *, int arg, int offset, unsigned int *data); +void rtl8168_asf_config_regs(struct rtl8168_private *tp, int arg, int offset, unsigned int *data); +void rtl8168_asf_capability_masks(struct rtl8168_private *tp, int arg, int offset, unsigned int *data); +void rtl8168_asf_community_string(struct rtl8168_private *tp, int arg, char *string); +void rtl8168_asf_community_string_len(struct rtl8168_private *tp, int arg, unsigned int *data); +void rtl8168_asf_alert_resend_interval(struct rtl8168_private *tp, int arg, unsigned int *data); +void rtl8168_asf_time_period(struct rtl8168_private *tp, int arg, int offset, unsigned int *data); +void rtl8168_asf_key_access(struct rtl8168_private *, int arg, int offset, unsigned int *data); +void rtl8168_asf_rw_hexadecimal(struct rtl8168_private *tp, int arg, int offset, int len, unsigned int *data); +void rtl8168_asf_rw_iana(struct rtl8168_private *tp, int arg, unsigned int *data); +void rtl8168_asf_rw_uuid(struct rtl8168_private *tp, int arg, unsigned int *data); +void rtl8168_asf_rw_systemid(struct rtl8168_private *tp, int arg, unsigned int *data); diff --git a/drivers/net/ethernet/realtek/r8168/r8168_dash.h b/drivers/net/ethernet/realtek/r8168/r8168_dash.h new file mode 100644 index 000000000000..b4d358f1e13f --- /dev/null +++ b/drivers/net/ethernet/realtek/r8168/r8168_dash.h @@ -0,0 +1,256 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* +################################################################################ +# +# r8168 is the Linux device driver released for Realtek Gigabit Ethernet +# controllers with PCI-Express interface. +# +# Copyright(c) 2021 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. + ***********************************************************************************/ + +#ifndef _LINUX_R8168_DASH_H +#define _LINUX_R8168_DASH_H + +#define SIOCDEVPRIVATE_RTLDASH SIOCDEVPRIVATE+2 + +enum rtl_dash_cmd { + RTL_DASH_ARP_NS_OFFLOAD = 0, + RTL_DASH_SET_OOB_IPMAC, + RTL_DASH_NOTIFY_OOB, + + RTL_DASH_SEND_BUFFER_DATA_TO_DASH_FW, + RTL_DASH_CHECK_SEND_BUFFER_TO_DASH_FW_COMPLETE, + RTL_DASH_GET_RCV_FROM_FW_BUFFER_DATA, + RTL_DASH_OOB_REQ, + RTL_DASH_OOB_ACK, + RTL_DASH_DETACH_OOB_REQ, + RTL_DASH_DETACH_OOB_ACK, + + RTL_FW_SET_IPV4 = 0x10, + RTL_FW_GET_IPV4, + RTL_FW_SET_IPV6, + RTL_FW_GET_IPV6, + RTL_FW_SET_EXT_SNMP, + RTL_FW_GET_EXT_SNMP, + RTL_FW_SET_WAKEUP_PATTERN, + RTL_FW_GET_WAKEUP_PATTERN, + RTL_FW_DEL_WAKEUP_PATTERN, + + RTLT_DASH_COMMAND_INVALID, +}; + +struct rtl_dash_ip_mac { + struct sockaddr ifru_addr; + struct sockaddr ifru_netmask; + struct sockaddr ifru_hwaddr; +}; + +struct rtl_dash_ioctl_struct { + __u32 cmd; + __u32 offset; + __u32 len; + union { + __u32 data; + void *data_buffer; + }; +}; + +struct settings_ipv4 { + __u32 IPv4addr; + __u32 IPv4mask; + __u32 IPv4Gateway; +}; + +struct settings_ipv6 { + __u32 reserved; + __u32 prefixLen; + __u16 IPv6addr[8]; + __u16 IPv6Gateway[8]; +}; + +struct settings_ext_snmp { + __u16 index; + __u16 oid_get_len; + __u8 oid_for_get[24]; + __u8 reserved0[26]; + __u16 value_len; + __u8 value[256]; + __u8 supported; + __u8 reserved1[27]; +}; + +struct wakeup_pattern { + __u8 index; + __u8 valid; + __u8 start; + __u8 length; + __u8 name[36]; + __u8 mask[16]; + __u8 pattern[128]; + __u32 reserved[2]; +}; + +typedef struct _RX_DASH_FROM_FW_DESC { + __le16 length; + __le16 status; + __le32 resv; + __le64 BufferAddress; +} +RX_DASH_FROM_FW_DESC, *PRX_DASH_FROM_FW_DESC; + +typedef struct _TX_DASH_SEND_FW_DESC { + __le16 length; + __le16 status; + __le32 resv; + __le64 BufferAddress; +} +TX_DASH_SEND_FW_DESC, *PTX_DASH_SEND_FW_DESC; + +typedef struct _OSOOBHdr { + __le32 len; + u8 type; + u8 flag; + u8 hostReqV; + u8 res; +} +OSOOBHdr, *POSOOBHdr; + +typedef struct _RX_DASH_BUFFER_TYPE_2 { + OSOOBHdr oobhdr; + u8 RxDataBuffer[0]; +} +RX_DASH_BUFFER_TYPE_2, *PRX_DASH_BUFFER_TYPE_2; + +#define ALIGN_8 (0x7) +#define ALIGN_16 (0xf) +#define ALIGN_32 (0x1f) +#define ALIGN_64 (0x3f) +#define ALIGN_256 (0xff) +#define ALIGN_4096 (0xfff) + +#define OCP_REG_CONFIG0 (0x10) +#define OCP_REG_CONFIG0_REV_F (0xB8) +#define OCP_REG_DASH_POLL (0x30) +#define OCP_REG_HOST_REQ (0x34) +#define OCP_REG_DASH_REQ (0x35) +#define OCP_REG_CR (0x36) +#define OCP_REG_DMEMSTA (0x38) +#define OCP_REG_GPHYAR (0x60) + + +#define OCP_REG_CONFIG0_DASHEN BIT_15 +#define OCP_REG_CONFIG0_OOBRESET BIT_14 +#define OCP_REG_CONFIG0_APRDY BIT_13 +#define OCP_REG_CONFIG0_FIRMWARERDY BIT_12 +#define OCP_REG_CONFIG0_DRIVERRDY BIT_11 +#define OCP_REG_CONFIG0_OOB_WDT BIT_9 +#define OCP_REG_CONFIG0_DRV_WAIT_OOB BIT_8 +#define OCP_REG_CONFIG0_TLSEN BIT_7 + +#define HW_DASH_SUPPORT_DASH(_M) ((_M)->HwSuppDashVer > 0) +#define HW_DASH_SUPPORT_TYPE_1(_M) ((_M)->HwSuppDashVer == 1) +#define HW_DASH_SUPPORT_TYPE_2(_M) ((_M)->HwSuppDashVer == 2) +#define HW_DASH_SUPPORT_TYPE_3(_M) ((_M)->HwSuppDashVer == 3) + +#define RECV_FROM_FW_BUF_SIZE (2048) +#define SEND_TO_FW_BUF_SIZE (2048) + +#define RX_DASH_FROM_FW_OWN BIT_15 +#define TX_DASH_SEND_FW_OWN BIT_15 + +#define TXS_CC3_0 (BIT_0|BIT_1|BIT_2|BIT_3) +#define TXS_EXC BIT_4 +#define TXS_LNKF BIT_5 +#define TXS_OWC BIT_6 +#define TXS_TES BIT_7 +#define TXS_UNF BIT_9 +#define TXS_LGSEN BIT_11 +#define TXS_LS BIT_12 +#define TXS_FS BIT_13 +#define TXS_EOR BIT_14 +#define TXS_OWN BIT_15 + +#define TPPool_HRDY 0x20 + +#define HostReqReg (0xC0) +#define SystemMasterDescStartAddrLow (0xF0) +#define SystemMasterDescStartAddrHigh (0xF4) +#define SystemSlaveDescStartAddrLow (0xF8) +#define SystemSlaveDescStartAddrHigh (0xFC) + +//DASH Request Type +#define WSMANREG 0x01 +#define OSPUSHDATA 0x02 + +#define RXS_OWN BIT_15 +#define RXS_EOR BIT_14 +#define RXS_FS BIT_13 +#define RXS_LS BIT_12 + +#define ISRIMR_DP_DASH_OK BIT_15 +#define ISRIMR_DP_HOST_OK BIT_13 +#define ISRIMR_DP_REQSYS_OK BIT_11 + +#define ISRIMR_DASH_INTR_EN BIT_12 +#define ISRIMR_DASH_INTR_CMAC_RESET BIT_15 + +#define ISRIMR_DASH_TYPE2_ROK BIT_0 +#define ISRIMR_DASH_TYPE2_RDU BIT_1 +#define ISRIMR_DASH_TYPE2_TOK BIT_2 +#define ISRIMR_DASH_TYPE2_TDU BIT_3 +#define ISRIMR_DASH_TYPE2_TX_FIFO_FULL BIT_4 +#define ISRIMR_DASH_TYPE2_TX_DISABLE_IDLE BIT_5 +#define ISRIMR_DASH_TYPE2_RX_DISABLE_IDLE BIT_6 + +#define CMAC_OOB_STOP 0x25 +#define CMAC_OOB_INIT 0x26 +#define CMAC_OOB_RESET 0x2a + +#define NO_BASE_ADDRESS 0x00000000 +#define RTL8168FP_OOBMAC_BASE 0xBAF70000 +#define RTL8168FP_CMAC_IOBASE 0xBAF20000 +#define RTL8168FP_KVM_BASE 0xBAF80400 +#define CMAC_SYNC_REG 0x20 +#define CMAC_RXDESC_OFFSET 0x90 //RX: 0x90 - 0x98 +#define CMAC_TXDESC_OFFSET 0x98 //TX: 0x98 - 0x9F + +/* cmac write/read MMIO register */ +#define RTL_CMAC_W8(tp, reg, val8) writeb ((val8), tp->cmac_ioaddr + (reg)) +#define RTL_CMAC_W16(tp, reg, val16) writew ((val16), tp->cmac_ioaddr + (reg)) +#define RTL_CMAC_W32(tp, reg, val32) writel ((val32), tp->cmac_ioaddr + (reg)) +#define RTL_CMAC_R8(tp, reg) readb (tp->cmac_ioaddr + (reg)) +#define RTL_CMAC_R16(tp, reg) readw (tp->cmac_ioaddr + (reg)) +#define RTL_CMAC_R32(tp, reg) ((unsigned long) readl (tp->cmac_ioaddr + (reg))) + +int rtl8168_dash_ioctl(struct net_device *dev, struct ifreq *ifr); +void HandleDashInterrupt(struct net_device *dev); +int AllocateDashShareMemory(struct net_device *dev); +void FreeAllocatedDashShareMemory(struct net_device *dev); +void DashHwInit(struct net_device *dev); + + +#endif /* _LINUX_R8168_DASH_H */ diff --git a/drivers/net/ethernet/realtek/r8168/r8168_fiber.h b/drivers/net/ethernet/realtek/r8168/r8168_fiber.h new file mode 100644 index 000000000000..2e303fe2e6aa --- /dev/null +++ b/drivers/net/ethernet/realtek/r8168/r8168_fiber.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* +################################################################################ +# +# r8168 is the Linux device driver released for Realtek Gigabit Ethernet +# controllers with PCI-Express interface. +# +# Copyright(c) 2021 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. + ***********************************************************************************/ + +#ifndef _LINUX_R8168_FIBER_H +#define _LINUX_R8168_FIBER_H + +enum { + FIBER_MODE_NIC_ONLY = 0, + FIBER_MODE_RTL8168H_RTL8211FS, + FIBER_MODE_RTL8168H_MDI_SWITCH_RTL8211FS, + FIBER_MODE_MAX +}; + +enum { + FIBER_STAT_NOT_CHECKED = 0, + FIBER_STAT_CONNECT, + FIBER_STAT_DISCONNECT, + FIBER_STAT_MAX +}; + +#define HW_FIBER_MODE_ENABLED(_M) ((_M)->HwFiberModeVer > 0) + + + +void rtl8168_hw_init_fiber_nic(struct net_device *dev); +void rtl8168_hw_fiber_nic_d3_para(struct net_device *dev); +void rtl8168_hw_fiber_phy_config(struct net_device *dev); +void rtl8168_hw_switch_mdi_to_fiber(struct net_device *dev); +void rtl8168_hw_switch_mdi_to_nic(struct net_device *dev); +unsigned int rtl8168_hw_fiber_link_ok(struct net_device *dev); +void rtl8168_check_fiber_link_status(struct net_device *dev); +void rtl8168_check_hw_fiber_mode_support(struct net_device *dev); +void rtl8168_set_fiber_mode_software_variable(struct net_device *dev); + + +#endif /* _LINUX_R8168_FIBER_H */ diff --git a/drivers/net/ethernet/realtek/r8168/r8168_firmware.c b/drivers/net/ethernet/realtek/r8168/r8168_firmware.c new file mode 100644 index 000000000000..3fe95db9ab18 --- /dev/null +++ b/drivers/net/ethernet/realtek/r8168/r8168_firmware.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* +################################################################################ +# +# r8168 is the Linux device driver released for Realtek Gigabit Ethernet +# controllers with PCI-Express interface. +# +# Copyright(c) 2021 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 +#include +#include + +#include "r8168_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[RTL8168_VER_SIZE]; + __le32 fw_start; + __le32 fw_len; + u8 chksum; +} __packed; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,16,0) +#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER)) +#endif +#define FW_OPCODE_SIZE sizeof_field(struct rtl8168_fw_phy_action, code[0]) + +static bool rtl8168_fw_format_ok(struct rtl8168_fw *rtl_fw) +{ + const struct firmware *fw = rtl_fw->fw; + struct fw_info *fw_info = (struct fw_info *)fw->data; + struct rtl8168_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, RTL8168_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, RTL8168_VER_SIZE); + + pa->code = (__le32 *)fw->data; + pa->size = fw->size / FW_OPCODE_SIZE; + } + + return true; +} + +static bool rtl8168_fw_data_ok(struct rtl8168_fw *rtl_fw) +{ + struct rtl8168_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 rtl8168_fw_write_firmware(struct rtl8168_private *tp, struct rtl8168_fw *rtl_fw) +{ + struct rtl8168_fw_phy_action *pa = &rtl_fw->phy_action; + rtl8168_fw_write_t fw_write = rtl_fw->phy_write; + rtl8168_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: + mdelay(data); + break; + } + } +} + +void rtl8168_fw_release_firmware(struct rtl8168_fw *rtl_fw) +{ + release_firmware(rtl_fw->fw); +} + +int rtl8168_fw_request_firmware(struct rtl8168_fw *rtl_fw) +{ + int rc; + + rc = request_firmware(&rtl_fw->fw, rtl_fw->fw_name, rtl_fw->dev); + if (rc < 0) + goto out; + + if (!rtl8168_fw_format_ok(rtl_fw) || !rtl8168_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/drivers/net/ethernet/realtek/r8168/r8168_firmware.h b/drivers/net/ethernet/realtek/r8168/r8168_firmware.h new file mode 100644 index 000000000000..563280ff38c0 --- /dev/null +++ b/drivers/net/ethernet/realtek/r8168/r8168_firmware.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* +################################################################################ +# +# r8168 is the Linux device driver released for Realtek 2.5Gigabit Ethernet +# controllers with PCI-Express interface. +# +# Copyright(c) 2021 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. + ***********************************************************************************/ + +#ifndef _LINUX_RTL8168_FIRMWARE_H +#define _LINUX_RTL8168_FIRMWARE_H + +#include +#include + +struct rtl8168_private; +typedef void (*rtl8168_fw_write_t)(struct rtl8168_private *tp, u16 reg, u16 val); +typedef u32 (*rtl8168_fw_read_t)(struct rtl8168_private *tp, u16 reg); + +#define RTL8168_VER_SIZE 32 + +struct rtl8168_fw { + rtl8168_fw_write_t phy_write; + rtl8168_fw_read_t phy_read; + rtl8168_fw_write_t mac_mcu_write; + rtl8168_fw_read_t mac_mcu_read; + const struct firmware *fw; + const char *fw_name; + struct device *dev; + + char version[RTL8168_VER_SIZE]; + + struct rtl8168_fw_phy_action { + __le32 *code; + size_t size; + } phy_action; +}; + +int rtl8168_fw_request_firmware(struct rtl8168_fw *rtl_fw); +void rtl8168_fw_release_firmware(struct rtl8168_fw *rtl_fw); +void rtl8168_fw_write_firmware(struct rtl8168_private *tp, struct rtl8168_fw *rtl_fw); + +#endif /* _LINUX_RTL8168_FIRMWARE_H */ diff --git a/drivers/net/ethernet/realtek/r8168/r8168_n.c b/drivers/net/ethernet/realtek/r8168/r8168_n.c new file mode 100644 index 000000000000..0bc5ba9ac2fa --- /dev/null +++ b/drivers/net/ethernet/realtek/r8168/r8168_n.c @@ -0,0 +1,28725 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* +################################################################################ +# +# r8168 is the Linux device driver released for Realtek Gigabit Ethernet +# controllers with PCI-Express interface. +# +# Copyright(c) 2021 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 + */ + +/* In Linux 5.4 asm_inline was introduced, but it's not supported by clang. + * Redefine it to just asm to enable successful compilation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) +#include +#include +#endif +#include +#include +#include +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) +#include +#endif +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,37) +#include +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +#define dev_printk(A,B,fmt,args...) printk(A fmt,##args) +#else +#include +#include +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) +#include +#endif + +#include +#include + +#include "r8168.h" +#include "r8168_asf.h" +#include "rtl_eeprom.h" +#include "rtltool.h" +#include "r8168_firmware.h" + +#ifdef ENABLE_R8168_PROCFS +#include +#include +#endif + +#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_8168E_4 "rtl_nic/rtl8168e-4.fw" +#define FIRMWARE_8168F_1 "rtl_nic/rtl8168f-1.fw" +#define FIRMWARE_8168F_2 "rtl_nic/rtl8168f-2.fw" +#define FIRMWARE_8411_1 "rtl_nic/rtl8411-1.fw" +#define FIRMWARE_8411_2 "rtl_nic/rtl8411-2.fw" +#define FIRMWARE_8168G_2 "rtl_nic/rtl8168g-2.fw" +#define FIRMWARE_8168G_3 "rtl_nic/rtl8168g-3.fw" +#define FIRMWARE_8168EP_1 "rtl_nic/rtl8168ep-1.fw" +#define FIRMWARE_8168EP_2 "rtl_nic/rtl8168ep-2.fw" +#define FIRMWARE_8168EP_3 "rtl_nic/rtl8168ep-3.fw" +#define FIRMWARE_8168H_1 "rtl_nic/rtl8168h-1.fw" +#define FIRMWARE_8168H_2 "rtl_nic/rtl8168h-2.fw" +#define FIRMWARE_8168FP_3 "rtl_nic/rtl8168fp-3.fw" +#define FIRMWARE_8168FP_4 "rtl_nic/rtl8168fp-4.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. */ +static const int multicast_filter_limit = 32; + +static const struct { + const char *name; + const char *fw_name; +} rtl_chip_fw_infos[] = { + /* PCI-E devices. */ + [CFG_METHOD_1] = {"RTL8168B/8111", }, + [CFG_METHOD_2] = {"RTL8168B/8111", }, + [CFG_METHOD_3] = {"RTL8168B/8111", }, + [CFG_METHOD_4] = {"RTL8168C/8111C", }, + [CFG_METHOD_5] = {"RTL8168C/8111C", }, + [CFG_METHOD_6] = {"RTL8168C/8111C", }, + [CFG_METHOD_7] = {"RTL8168CP/8111CP", }, + [CFG_METHOD_8] = {"RTL8168CP/8111CP", }, + [CFG_METHOD_9] = {"RTL8168D/8111D", FIRMWARE_8168D_1}, + [CFG_METHOD_10] = {"RTL8168D/8111D", FIRMWARE_8168D_2}, + [CFG_METHOD_11] = {"RTL8168DP/8111DP", }, + [CFG_METHOD_12] = {"RTL8168DP/8111DP", }, + [CFG_METHOD_13] = {"RTL8168DP/8111DP", }, + [CFG_METHOD_14] = {"RTL8168E/8111E", FIRMWARE_8168E_1}, + [CFG_METHOD_15] = {"RTL8168E/8111E", FIRMWARE_8168E_2}, + [CFG_METHOD_16] = {"RTL8168E-VL/8111E-VL", FIRMWARE_8168E_3}, + [CFG_METHOD_17] = {"RTL8168E-VL/8111E-VL", FIRMWARE_8168E_4}, + [CFG_METHOD_18] = {"RTL8168F/8111F", FIRMWARE_8168F_1}, + [CFG_METHOD_19] = {"RTL8168F/8111F", FIRMWARE_8168F_2}, + [CFG_METHOD_20] = {"RTL8411", FIRMWARE_8411_1}, + [CFG_METHOD_21] = {"RTL8168G/8111G", FIRMWARE_8168G_2}, + [CFG_METHOD_22] = {"RTL8168G/8111G", }, + [CFG_METHOD_23] = {"RTL8168EP/8111EP", FIRMWARE_8168EP_1}, + [CFG_METHOD_24] = {"RTL8168GU/8111GU", }, + [CFG_METHOD_25] = {"RTL8168GU/8111GU", FIRMWARE_8168G_3}, + [CFG_METHOD_26] = {"8411B", FIRMWARE_8411_2}, + [CFG_METHOD_27] = {"RTL8168EP/8111EP", FIRMWARE_8168EP_2}, + [CFG_METHOD_28] = {"RTL8168EP/8111EP", FIRMWARE_8168EP_3}, + [CFG_METHOD_29] = {"RTL8168H/8111H", FIRMWARE_8168H_1}, + [CFG_METHOD_30] = {"RTL8168H/8111H", FIRMWARE_8168H_2}, + [CFG_METHOD_31] = {"RTL8168FP/8111FP", }, + [CFG_METHOD_32] = {"RTL8168FP/8111FP", FIRMWARE_8168FP_3}, + [CFG_METHOD_33] = {"RTL8168FP/8111FP", FIRMWARE_8168FP_4}, + [CFG_METHOD_DEFAULT] = {"Unknown", }, +}; + +#define _R(NAME,MAC,RCR,MASK, JumFrameSz) \ + { .name = NAME, .mcfg = MAC, .RCR_Cfg = RCR, .RxConfigMask = MASK, .jumbo_frame_sz = JumFrameSz } + +static const struct { + const char *name; + u8 mcfg; + u32 RCR_Cfg; + u32 RxConfigMask; /* Clears the bits supported by this chip */ + u32 jumbo_frame_sz; +} rtl_chip_info[] = { + _R("RTL8168B/8111B", + CFG_METHOD_1, + (Reserved2_data << Reserved2_shift) | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e1880, + Jumbo_Frame_4k), + + _R("RTL8168B/8111B", + CFG_METHOD_2, + (Reserved2_data << Reserved2_shift) | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e1880, + Jumbo_Frame_4k), + + _R("RTL8168B/8111B", + CFG_METHOD_3, + (Reserved2_data << Reserved2_shift) | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e1880, + Jumbo_Frame_4k), + + _R("RTL8168C/8111C", + CFG_METHOD_4, + RxCfg_128_int_en | RxCfg_fet_multi_en | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e1880, + Jumbo_Frame_6k), + + _R("RTL8168C/8111C", + CFG_METHOD_5, + RxCfg_128_int_en | RxCfg_fet_multi_en | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e1880, + Jumbo_Frame_6k), + + _R("RTL8168C/8111C", + CFG_METHOD_6, + RxCfg_128_int_en | RxCfg_fet_multi_en | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e1880, + Jumbo_Frame_6k), + + _R("RTL8168CP/8111CP", + CFG_METHOD_7, + RxCfg_128_int_en | RxCfg_fet_multi_en | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e1880, + Jumbo_Frame_6k), + + _R("RTL8168CP/8111CP", + CFG_METHOD_8, + RxCfg_128_int_en | RxCfg_fet_multi_en | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e1880, + Jumbo_Frame_6k), + + _R("RTL8168D/8111D", + CFG_METHOD_9, + RxCfg_128_int_en | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e1880, + Jumbo_Frame_9k), + + _R("RTL8168D/8111D", + CFG_METHOD_10, + RxCfg_128_int_en | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e1880, + Jumbo_Frame_9k), + + _R("RTL8168DP/8111DP", + CFG_METHOD_11, + RxCfg_128_int_en | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e1880, + Jumbo_Frame_9k), + + _R("RTL8168DP/8111DP", + CFG_METHOD_12, + RxCfg_128_int_en | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e1880, + Jumbo_Frame_9k), + + _R("RTL8168DP/8111DP", + CFG_METHOD_13, + RxCfg_128_int_en | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e1880, + Jumbo_Frame_9k), + + _R("RTL8168E/8111E", + CFG_METHOD_14, + RxCfg_128_int_en | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e1880, + Jumbo_Frame_9k), + + _R("RTL8168E/8111E", + CFG_METHOD_15, + RxCfg_128_int_en | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e1880, + Jumbo_Frame_9k), + + _R("RTL8168E-VL/8111E-VL", + CFG_METHOD_16, + RxCfg_128_int_en | RxEarly_off_V1 | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e0080, + Jumbo_Frame_9k), + + _R("RTL8168E-VL/8111E-VL", + CFG_METHOD_17, + RxCfg_128_int_en | RxEarly_off_V1 | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e1880, + Jumbo_Frame_9k), + + _R("RTL8168F/8111F", + CFG_METHOD_18, + RxCfg_128_int_en | RxEarly_off_V1 | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e1880, + Jumbo_Frame_9k), + + _R("RTL8168F/8111F", + CFG_METHOD_19, + RxCfg_128_int_en | RxEarly_off_V1 | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e1880, + Jumbo_Frame_9k), + + _R("RTL8411", + CFG_METHOD_20, + RxCfg_128_int_en | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e1880, + Jumbo_Frame_9k), + + _R("RTL8168G/8111G", + CFG_METHOD_21, + RxCfg_128_int_en | RxEarly_off_V2 | Rx_Single_fetch_V2 | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e5880, + Jumbo_Frame_9k), + + _R("RTL8168G/8111G", + CFG_METHOD_22, + RxCfg_128_int_en | RxEarly_off_V2 | Rx_Single_fetch_V2 | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e5880, + Jumbo_Frame_9k), + + _R("RTL8168EP/8111EP", + CFG_METHOD_23, + RxCfg_128_int_en | RxEarly_off_V2 | Rx_Single_fetch_V2 | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e5880, + Jumbo_Frame_9k), + + _R("RTL8168GU/8111GU", + CFG_METHOD_24, + RxCfg_128_int_en | RxEarly_off_V2 | Rx_Single_fetch_V2 | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e5880, + Jumbo_Frame_9k), + + _R("RTL8168GU/8111GU", + CFG_METHOD_25, + RxCfg_128_int_en | RxEarly_off_V2 | Rx_Single_fetch_V2 | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e5880, + Jumbo_Frame_9k), + + _R("8411B", + CFG_METHOD_26, + RxCfg_128_int_en | RxEarly_off_V2 | Rx_Single_fetch_V2 | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e5880, + Jumbo_Frame_9k), + + _R("RTL8168EP/8111EP", + CFG_METHOD_27, + RxCfg_128_int_en | RxEarly_off_V2 | Rx_Single_fetch_V2 | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e5880, + Jumbo_Frame_9k), + + _R("RTL8168EP/8111EP", + CFG_METHOD_28, + RxCfg_128_int_en | RxEarly_off_V2 | Rx_Single_fetch_V2 | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e5880, + Jumbo_Frame_9k), + + _R("RTL8168H/8111H", + CFG_METHOD_29, + RxCfg_128_int_en | RxEarly_off_V2 | Rx_Single_fetch_V2 | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e5880, + Jumbo_Frame_9k), + + _R("RTL8168H/8111H", + CFG_METHOD_30, + RxCfg_128_int_en | RxEarly_off_V2 | Rx_Single_fetch_V2 | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e5880, + Jumbo_Frame_9k), + + _R("RTL8168FP/8111FP", + CFG_METHOD_31, + RxCfg_128_int_en | RxEarly_off_V2 | Rx_Single_fetch_V2 | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e5880, + Jumbo_Frame_9k), + + _R("RTL8168FP/8111FP", + CFG_METHOD_32, + RxCfg_128_int_en | RxEarly_off_V2 | Rx_Single_fetch_V2 | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e5880, + Jumbo_Frame_9k), + + _R("RTL8168FP/8111FP", + CFG_METHOD_33, + RxCfg_128_int_en | RxEarly_off_V2 | Rx_Single_fetch_V2 | (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e5880, + Jumbo_Frame_9k), + + _R("Unknown", + CFG_METHOD_DEFAULT, + (RX_DMA_BURST << RxCfgDMAShift), + 0xff7e5880, + Jumbo_Frame_1k) +}; +#undef _R + +#ifndef PCI_VENDOR_ID_DLINK +#define PCI_VENDOR_ID_DLINK 0x1186 +#endif + +static struct pci_device_id rtl8168_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8168), }, + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8161), }, + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x2502), }, + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x2600), }, + { PCI_VENDOR_ID_DLINK, 0x4300, 0x1186, 0x4b10,}, + {0,}, +}; + +MODULE_DEVICE_TABLE(pci, rtl8168_pci_tbl); + +static int rx_copybreak = 0; +static int use_dac = 1; +static int timer_count = 0x2600; +static int dynamic_aspm_packet_threshold = 10; + +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; +#ifdef CONFIG_ASPM +static int aspm = 1; +#else +static int aspm = 0; +#endif +#ifdef CONFIG_DYNAMIC_ASPM +static int dynamic_aspm = 1; +#else +static int dynamic_aspm = 0; +#endif +#ifdef ENABLE_S5WOL +static int s5wol = 1; +#else +static int s5wol = 0; +#endif +#ifdef ENABLE_S5_KEEP_CURR_MAC +static int s5_keep_curr_mac = 1; +#else +static int s5_keep_curr_mac = 0; +#endif +#ifdef ENABLE_EEE +static int eee_enable = 1; +#else +static int eee_enable = 0; +#endif +#ifdef CONFIG_SOC_LAN +static ulong hwoptimize = HW_PATCH_SOC_LAN; +#else +static ulong hwoptimize = 0; +#endif +#ifdef ENABLE_S0_MAGIC_PACKET +static int s0_magic_packet = 1; +#else +static int s0_magic_packet = 0; +#endif + +MODULE_AUTHOR("Realtek and the Linux r8168 crew "); +MODULE_DESCRIPTION("RealTek RTL-8168 Gigabit Ethernet driver"); + +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(aspm, int, 0); +MODULE_PARM_DESC(aspm, "Enable ASPM."); + +module_param(dynamic_aspm, int, 0); +MODULE_PARM_DESC(aspm, "Enable Software Dynamic ASPM."); + +module_param(s5wol, int, 0); +MODULE_PARM_DESC(s5wol, "Enable Shutdown Wake On Lan."); + +module_param(s5_keep_curr_mac, int, 0); +MODULE_PARM_DESC(s5_keep_curr_mac, "Enable Shutdown Keep Current MAC Address."); + +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(timer_count, int, 0); +MODULE_PARM_DESC(timer_count, "Timer Interrupt Interval."); + +module_param(eee_enable, int, 0); +MODULE_PARM_DESC(eee_enable, "Enable Energy Efficient Ethernet."); + +module_param(hwoptimize, ulong, 0); +MODULE_PARM_DESC(hwoptimize, "Enable HW optimization function."); + +module_param(s0_magic_packet, int, 0); +MODULE_PARM_DESC(s0_magic_packet, "Enable S0 Magic Packet."); + +module_param(dynamic_aspm_packet_threshold, int, 0); +MODULE_PARM_DESC(dynamic_aspm_packet_threshold, "Dynamic ASPM packet threshold."); + +#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"); +#ifdef ENABLE_USE_FIRMWARE_FILE +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_8168E_4); +MODULE_FIRMWARE(FIRMWARE_8168F_1); +MODULE_FIRMWARE(FIRMWARE_8168F_2); +MODULE_FIRMWARE(FIRMWARE_8411_1); +MODULE_FIRMWARE(FIRMWARE_8411_2); +MODULE_FIRMWARE(FIRMWARE_8168G_2); +MODULE_FIRMWARE(FIRMWARE_8168G_3); +MODULE_FIRMWARE(FIRMWARE_8168EP_1); +MODULE_FIRMWARE(FIRMWARE_8168EP_2); +MODULE_FIRMWARE(FIRMWARE_8168EP_3); +MODULE_FIRMWARE(FIRMWARE_8168H_1); +MODULE_FIRMWARE(FIRMWARE_8168H_2); +MODULE_FIRMWARE(FIRMWARE_8168FP_3); +MODULE_FIRMWARE(FIRMWARE_8168FP_4); +#endif + +MODULE_VERSION(RTL8168_VERSION); + +static void rtl8168_sleep_rx_enable(struct net_device *dev); +static void rtl8168_dsm(struct net_device *dev, int dev_state); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) +static void rtl8168_esd_timer(unsigned long __opaque); +#else +static void rtl8168_esd_timer(struct timer_list *t); +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) +static void rtl8168_link_timer(unsigned long __opaque); +#else +static void rtl8168_link_timer(struct timer_list *t); +#endif +static void rtl8168_tx_clear(struct rtl8168_private *tp); +static void rtl8168_rx_clear(struct rtl8168_private *tp); + +static int rtl8168_open(struct net_device *dev); +static netdev_tx_t rtl8168_start_xmit(struct sk_buff *skb, struct net_device *dev); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +static irqreturn_t rtl8168_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +#else +static irqreturn_t rtl8168_interrupt(int irq, void *dev_instance); +#endif +static void rtl8168_rx_desc_offset0_init(struct rtl8168_private *, int); +static int rtl8168_init_ring(struct net_device *dev); +static void rtl8168_hw_config(struct net_device *dev); +static void rtl8168_hw_start(struct net_device *dev); +static int rtl8168_close(struct net_device *dev); +static void rtl8168_set_rx_mode(struct net_device *dev); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0) +static void rtl8168_tx_timeout(struct net_device *dev, unsigned int txqueue); +#else +static void rtl8168_tx_timeout(struct net_device *dev); +#endif +static struct net_device_stats *rtl8168_get_stats(struct net_device *dev); +static int rtl8168_rx_interrupt(struct net_device *, struct rtl8168_private *, napi_budget); +static int rtl8168_change_mtu(struct net_device *dev, int new_mtu); +static void rtl8168_down(struct net_device *dev); + +static int rtl8168_set_mac_address(struct net_device *dev, void *p); +void rtl8168_rar_set(struct rtl8168_private *tp, uint8_t *addr); +static void rtl8168_desc_addr_fill(struct rtl8168_private *); +static void rtl8168_tx_desc_init(struct rtl8168_private *tp); +static void rtl8168_rx_desc_init(struct rtl8168_private *tp); + +static u16 rtl8168_get_hw_phy_mcu_code_ver(struct rtl8168_private *tp); + +static void rtl8168_hw_reset(struct net_device *dev); + +static void rtl8168_phy_power_up(struct net_device *dev); +static void rtl8168_phy_power_down(struct net_device *dev); +static int rtl8168_set_speed(struct net_device *dev, u8 autoneg, u32 speed, u8 duplex, u32 adv); + +static int rtl8168_set_phy_mcu_patch_request(struct rtl8168_private *tp); +static int rtl8168_clear_phy_mcu_patch_request(struct rtl8168_private *tp); + +#ifdef CONFIG_R8168_NAPI +static int rtl8168_poll(napi_ptr napi, napi_budget budget); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +static void rtl8168_reset_task(void *_data); +#else +static void rtl8168_reset_task(struct work_struct *work); +#endif + +static inline struct device *tp_to_dev(struct rtl8168_private *tp) +{ + return &tp->pci_dev->dev; +} + +#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,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) + +//#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 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,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) + +#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) + +/* 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) +/** + * 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,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) + +struct rtl8168_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_underrun; +}; + +#ifdef ENABLE_R8168_PROCFS +/**************************************************************************** +* -----------------------------PROCFS STUFF------------------------- +***************************************************************************** +*/ + +static struct proc_dir_entry *rtl8168_proc; +static int proc_init_num = 0; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) +static int proc_get_driver_variable(struct seq_file *m, void *v) +{ + struct net_device *dev = m->private; + struct rtl8168_private *tp = netdev_priv(dev); + unsigned long flags; + + seq_puts(m, "\nDump Driver Variable\n"); + + spin_lock_irqsave(&tp->lock, flags); + seq_puts(m, "Variable\tValue\n----------\t-----\n"); + seq_printf(m, "MODULENAME\t%s\n", MODULENAME); + seq_printf(m, "driver version\t%s\n", RTL8168_VERSION); + seq_printf(m, "chipset\t%d\n", tp->chipset); + seq_printf(m, "chipset_name\t%s\n", rtl_chip_info[tp->chipset].name); + seq_printf(m, "mtu\t%d\n", dev->mtu); + seq_printf(m, "NUM_RX_DESC\t0x%x\n", NUM_RX_DESC); + seq_printf(m, "cur_rx\t0x%x\n", tp->cur_rx); + seq_printf(m, "dirty_rx\t0x%x\n", tp->dirty_rx); + seq_printf(m, "NUM_TX_DESC\t0x%x\n", NUM_TX_DESC); + seq_printf(m, "cur_tx\t0x%x\n", tp->cur_tx); + seq_printf(m, "dirty_tx\t0x%x\n", tp->dirty_tx); + seq_printf(m, "rx_buf_sz\t0x%x\n", tp->rx_buf_sz); + seq_printf(m, "esd_flag\t0x%x\n", tp->esd_flag); + seq_printf(m, "pci_cfg_is_read\t0x%x\n", tp->pci_cfg_is_read); + seq_printf(m, "rtl8168_rx_config\t0x%x\n", tp->rtl8168_rx_config); + seq_printf(m, "cp_cmd\t0x%x\n", tp->cp_cmd); + seq_printf(m, "intr_mask\t0x%x\n", tp->intr_mask); + seq_printf(m, "timer_intr_mask\t0x%x\n", tp->timer_intr_mask); + seq_printf(m, "wol_enabled\t0x%x\n", tp->wol_enabled); + seq_printf(m, "wol_opts\t0x%x\n", tp->wol_opts); + seq_printf(m, "efuse_ver\t0x%x\n", tp->efuse_ver); + seq_printf(m, "eeprom_type\t0x%x\n", tp->eeprom_type); + seq_printf(m, "autoneg\t0x%x\n", tp->autoneg); + seq_printf(m, "duplex\t0x%x\n", tp->duplex); + seq_printf(m, "speed\t%d\n", tp->speed); + seq_printf(m, "advertising\t0x%x\n", tp->advertising); + seq_printf(m, "eeprom_len\t0x%x\n", tp->eeprom_len); + seq_printf(m, "cur_page\t0x%x\n", tp->cur_page); + seq_printf(m, "bios_setting\t0x%x\n", tp->bios_setting); + seq_printf(m, "features\t0x%x\n", tp->features); + seq_printf(m, "org_pci_offset_99\t0x%x\n", tp->org_pci_offset_99); + seq_printf(m, "org_pci_offset_180\t0x%x\n", tp->org_pci_offset_180); + seq_printf(m, "issue_offset_99_event\t0x%x\n", tp->issue_offset_99_event); + seq_printf(m, "org_pci_offset_80\t0x%x\n", tp->org_pci_offset_80); + seq_printf(m, "org_pci_offset_81\t0x%x\n", tp->org_pci_offset_81); + seq_printf(m, "use_timer_interrrupt\t0x%x\n", tp->use_timer_interrrupt); + seq_printf(m, "HwIcVerUnknown\t0x%x\n", tp->HwIcVerUnknown); + seq_printf(m, "NotWrRamCodeToMicroP\t0x%x\n", tp->NotWrRamCodeToMicroP); + seq_printf(m, "NotWrMcuPatchCode\t0x%x\n", tp->NotWrMcuPatchCode); + seq_printf(m, "HwHasWrRamCodeToMicroP\t0x%x\n", tp->HwHasWrRamCodeToMicroP); + seq_printf(m, "sw_ram_code_ver\t0x%x\n", tp->sw_ram_code_ver); + seq_printf(m, "hw_ram_code_ver\t0x%x\n", tp->hw_ram_code_ver); + seq_printf(m, "rtk_enable_diag\t0x%x\n", tp->rtk_enable_diag); + seq_printf(m, "ShortPacketSwChecksum\t0x%x\n", tp->ShortPacketSwChecksum); + seq_printf(m, "UseSwPaddingShortPkt\t0x%x\n", tp->UseSwPaddingShortPkt); + seq_printf(m, "RequireAdcBiasPatch\t0x%x\n", tp->RequireAdcBiasPatch); + seq_printf(m, "AdcBiasPatchIoffset\t0x%x\n", tp->AdcBiasPatchIoffset); + seq_printf(m, "RequireAdjustUpsTxLinkPulseTiming\t0x%x\n", tp->RequireAdjustUpsTxLinkPulseTiming); + seq_printf(m, "SwrCnt1msIni\t0x%x\n", tp->SwrCnt1msIni); + seq_printf(m, "HwSuppNowIsOobVer\t0x%x\n", tp->HwSuppNowIsOobVer); + seq_printf(m, "HwFiberModeVer\t0x%x\n", tp->HwFiberModeVer); + seq_printf(m, "HwFiberStat\t0x%x\n", tp->HwFiberStat); + seq_printf(m, "HwSwitchMdiToFiber\t0x%x\n", tp->HwSwitchMdiToFiber); + seq_printf(m, "HwSuppSerDesPhyVer\t0x%x\n", tp->HwSuppSerDesPhyVer); + seq_printf(m, "NicCustLedValue\t0x%x\n", tp->NicCustLedValue); + seq_printf(m, "RequiredSecLanDonglePatch\t0x%x\n", tp->RequiredSecLanDonglePatch); + seq_printf(m, "HwSuppDashVer\t0x%x\n", tp->HwSuppDashVer); + seq_printf(m, "DASH\t0x%x\n", tp->DASH); + seq_printf(m, "dash_printer_enabled\t0x%x\n", tp->dash_printer_enabled); + seq_printf(m, "HwSuppKCPOffloadVer\t0x%x\n", tp->HwSuppKCPOffloadVer); + seq_printf(m, "speed_mode\t0x%x\n", speed_mode); + seq_printf(m, "duplex_mode\t0x%x\n", duplex_mode); + seq_printf(m, "autoneg_mode\t0x%x\n", autoneg_mode); + seq_printf(m, "advertising_mode\t0x%x\n", advertising_mode); + seq_printf(m, "aspm\t0x%x\n", aspm); + seq_printf(m, "s5wol\t0x%x\n", s5wol); + seq_printf(m, "s5_keep_curr_mac\t0x%x\n", s5_keep_curr_mac); + seq_printf(m, "eee_enable\t0x%x\n", tp->eee_enabled); + seq_printf(m, "hwoptimize\t0x%lx\n", hwoptimize); + seq_printf(m, "proc_init_num\t0x%x\n", proc_init_num); + seq_printf(m, "s0_magic_packet\t0x%x\n", s0_magic_packet); + seq_printf(m, "HwSuppMagicPktVer\t0x%x\n", tp->HwSuppMagicPktVer); + seq_printf(m, "HwSuppUpsVer\t0x%x\n", tp->HwSuppUpsVer); + seq_printf(m, "HwSuppEsdVer\t0x%x\n", tp->HwSuppEsdVer); + seq_printf(m, "HwSuppCheckPhyDisableModeVer\t0x%x\n", tp->HwSuppCheckPhyDisableModeVer); + seq_printf(m, "HwPkgDet\t0x%x\n", tp->HwPkgDet); + seq_printf(m, "random_mac\t0x%x\n", tp->random_mac); + seq_printf(m, "org_mac_addr\t%pM\n", tp->org_mac_addr); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13) + seq_printf(m, "perm_addr\t%pM\n", dev->perm_addr); +#endif + seq_printf(m, "dev_addr\t%pM\n", dev->dev_addr); + spin_unlock_irqrestore(&tp->lock, flags); + + seq_putc(m, '\n'); + return 0; +} + +static int proc_get_tally_counter(struct seq_file *m, void *v) +{ + struct net_device *dev = m->private; + struct rtl8168_private *tp = netdev_priv(dev); + struct rtl8168_counters *counters; + dma_addr_t paddr; + u32 cmd; + u32 WaitCnt; + unsigned long flags; + + seq_puts(m, "\nDump Tally Counter\n"); + + //ASSERT_RTNL(); + + counters = tp->tally_vaddr; + paddr = tp->tally_paddr; + if (!counters) { + seq_puts(m, "\nDump Tally Counter Fail\n"); + return 0; + } + + spin_lock_irqsave(&tp->lock, flags); + RTL_W32(tp, CounterAddrHigh, (u64)paddr >> 32); + cmd = (u64)paddr & DMA_BIT_MASK(32); + RTL_W32(tp, CounterAddrLow, cmd); + RTL_W32(tp, CounterAddrLow, cmd | CounterDump); + + WaitCnt = 0; + while (RTL_R32(tp, CounterAddrLow) & CounterDump) { + udelay(10); + + WaitCnt++; + if (WaitCnt > 20) + break; + } + spin_unlock_irqrestore(&tp->lock, flags); + + seq_puts(m, "Statistics\tValue\n----------\t-----\n"); + seq_printf(m, "tx_packets\t%lld\n", le64_to_cpu(counters->tx_packets)); + seq_printf(m, "rx_packets\t%lld\n", le64_to_cpu(counters->rx_packets)); + seq_printf(m, "tx_errors\t%lld\n", le64_to_cpu(counters->tx_errors)); + seq_printf(m, "rx_errors\t%d\n", le32_to_cpu(counters->rx_errors)); + seq_printf(m, "rx_missed\t%d\n", le16_to_cpu(counters->rx_missed)); + seq_printf(m, "align_errors\t%d\n", le16_to_cpu(counters->align_errors)); + seq_printf(m, "tx_one_collision\t%d\n", le32_to_cpu(counters->tx_one_collision)); + seq_printf(m, "tx_multi_collision\t%d\n", le32_to_cpu(counters->tx_multi_collision)); + seq_printf(m, "rx_unicast\t%lld\n", le64_to_cpu(counters->rx_unicast)); + seq_printf(m, "rx_broadcast\t%lld\n", le64_to_cpu(counters->rx_broadcast)); + seq_printf(m, "rx_multicast\t%d\n", le32_to_cpu(counters->rx_multicast)); + seq_printf(m, "tx_aborted\t%d\n", le16_to_cpu(counters->tx_aborted)); + seq_printf(m, "tx_underrun\t%d\n", le16_to_cpu(counters->tx_underrun)); + + seq_putc(m, '\n'); + return 0; +} + +static int proc_get_registers(struct seq_file *m, void *v) +{ + struct net_device *dev = m->private; + int i, n, max = R8168_MAC_REGS_SIZE; + u8 byte_rd; + struct rtl8168_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned long flags; + + seq_puts(m, "\nDump MAC Registers\n"); + seq_puts(m, "Offset\tValue\n------\t-----\n"); + + spin_lock_irqsave(&tp->lock, flags); + for (n = 0; n < max;) { + seq_printf(m, "\n0x%02x:\t", n); + + for (i = 0; i < 16 && n < max; i++, n++) { + byte_rd = readb(ioaddr + n); + seq_printf(m, "%02x ", byte_rd); + } + } + spin_unlock_irqrestore(&tp->lock, flags); + + seq_putc(m, '\n'); + return 0; +} + +static int proc_get_pcie_phy(struct seq_file *m, void *v) +{ + struct net_device *dev = m->private; + int i, n, max = R8168_EPHY_REGS_SIZE/2; + u16 word_rd; + struct rtl8168_private *tp = netdev_priv(dev); + unsigned long flags; + + seq_puts(m, "\nDump PCIE PHY\n"); + seq_puts(m, "\nOffset\tValue\n------\t-----\n "); + + spin_lock_irqsave(&tp->lock, flags); + for (n = 0; n < max;) { + seq_printf(m, "\n0x%02x:\t", n); + + for (i = 0; i < 8 && n < max; i++, n++) { + word_rd = rtl8168_ephy_read(tp, n); + seq_printf(m, "%04x ", word_rd); + } + } + spin_unlock_irqrestore(&tp->lock, flags); + + seq_putc(m, '\n'); + return 0; +} + +static int proc_get_eth_phy(struct seq_file *m, void *v) +{ + struct net_device *dev = m->private; + int i, n, max = R8168_PHY_REGS_SIZE/2; + u16 word_rd; + struct rtl8168_private *tp = netdev_priv(dev); + unsigned long flags; + + seq_puts(m, "\nDump Ethernet PHY\n"); + seq_puts(m, "\nOffset\tValue\n------\t-----\n "); + + spin_lock_irqsave(&tp->lock, flags); + seq_puts(m, "\n####################page 0##################\n "); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + for (n = 0; n < max;) { + seq_printf(m, "\n0x%02x:\t", n); + + for (i = 0; i < 8 && n < max; i++, n++) { + word_rd = rtl8168_mdio_read(tp, n); + seq_printf(m, "%04x ", word_rd); + } + } + spin_unlock_irqrestore(&tp->lock, flags); + + seq_putc(m, '\n'); + return 0; +} + +static int proc_get_extended_registers(struct seq_file *m, void *v) +{ + struct net_device *dev = m->private; + int i, n, max = R8168_ERI_REGS_SIZE; + u32 dword_rd; + struct rtl8168_private *tp = netdev_priv(dev); + unsigned long flags; + + switch (tp->mcfg) { + case CFG_METHOD_1: + case CFG_METHOD_2: + case CFG_METHOD_3: + /* RTL8168B does not support Extend GMAC */ + seq_puts(m, "\nNot Support Dump Extended Registers\n"); + return 0; + } + + seq_puts(m, "\nDump Extended Registers\n"); + seq_puts(m, "\nOffset\tValue\n------\t-----\n "); + + spin_lock_irqsave(&tp->lock, flags); + for (n = 0; n < max;) { + seq_printf(m, "\n0x%02x:\t", n); + + for (i = 0; i < 4 && n < max; i++, n+=4) { + dword_rd = rtl8168_eri_read(tp, n, 4, ERIAR_ExGMAC); + seq_printf(m, "%08x ", dword_rd); + } + } + spin_unlock_irqrestore(&tp->lock, flags); + + seq_putc(m, '\n'); + return 0; +} + +static int proc_get_pci_registers(struct seq_file *m, void *v) +{ + struct net_device *dev = m->private; + int i, n, max = R8168_PCI_REGS_SIZE; + u32 dword_rd; + struct rtl8168_private *tp = netdev_priv(dev); + unsigned long flags; + + seq_puts(m, "\nDump PCI Registers\n"); + seq_puts(m, "\nOffset\tValue\n------\t-----\n "); + + spin_lock_irqsave(&tp->lock, flags); + for (n = 0; n < max;) { + seq_printf(m, "\n0x%03x:\t", n); + + for (i = 0; i < 4 && n < max; i++, n+=4) { + pci_read_config_dword(tp->pci_dev, n, &dword_rd); + seq_printf(m, "%08x ", dword_rd); + } + } + + n = 0x110; + pci_read_config_dword(tp->pci_dev, n, &dword_rd); + seq_printf(m, "\n0x%03x:\t%08x ", n, dword_rd); + n = 0x70c; + pci_read_config_dword(tp->pci_dev, n, &dword_rd); + seq_printf(m, "\n0x%03x:\t%08x ", n, dword_rd); + + spin_unlock_irqrestore(&tp->lock, flags); + + seq_putc(m, '\n'); + return 0; +} +#else + +static int proc_get_driver_variable(char *page, char **start, + off_t offset, int count, + int *eof, void *data) +{ + struct net_device *dev = data; + struct rtl8168_private *tp = netdev_priv(dev); + unsigned long flags; + int len = 0; + + len += snprintf(page + len, count - len, + "\nDump Driver Driver\n"); + + spin_lock_irqsave(&tp->lock, flags); + len += snprintf(page + len, count - len, + "Variable\tValue\n----------\t-----\n"); + + len += snprintf(page + len, count - len, + "MODULENAME\t%s\n" + "driver version\t%s\n" + "chipset\t%d\n" + "chipset_name\t%s\n" + "mtu\t%d\n" + "NUM_RX_DESC\t0x%x\n" + "cur_rx\t0x%x\n" + "dirty_rx\t0x%x\n" + "NUM_TX_DESC\t0x%x\n" + "cur_tx\t0x%x\n" + "dirty_tx\t0x%x\n" + "rx_buf_sz\t0x%x\n" + "esd_flag\t0x%x\n" + "pci_cfg_is_read\t0x%x\n" + "rtl8168_rx_config\t0x%x\n" + "cp_cmd\t0x%x\n" + "intr_mask\t0x%x\n" + "timer_intr_mask\t0x%x\n" + "wol_enabled\t0x%x\n" + "wol_opts\t0x%x\n" + "efuse_ver\t0x%x\n" + "eeprom_type\t0x%x\n" + "autoneg\t0x%x\n" + "duplex\t0x%x\n" + "speed\t%d\n" + "advertising\t0x%x\n" + "eeprom_len\t0x%x\n" + "cur_page\t0x%x\n" + "bios_setting\t0x%x\n" + "features\t0x%x\n" + "org_pci_offset_99\t0x%x\n" + "org_pci_offset_180\t0x%x\n" + "issue_offset_99_event\t0x%x\n" + "org_pci_offset_80\t0x%x\n" + "org_pci_offset_81\t0x%x\n" + "use_timer_interrrupt\t0x%x\n" + "HwIcVerUnknown\t0x%x\n" + "NotWrRamCodeToMicroP\t0x%x\n" + "NotWrMcuPatchCode\t0x%x\n" + "HwHasWrRamCodeToMicroP\t0x%x\n" + "sw_ram_code_ver\t0x%x\n" + "hw_ram_code_ver\t0x%x\n" + "rtk_enable_diag\t0x%x\n" + "ShortPacketSwChecksum\t0x%x\n" + "UseSwPaddingShortPkt\t0x%x\n" + "RequireAdcBiasPatch\t0x%x\n" + "AdcBiasPatchIoffset\t0x%x\n" + "RequireAdjustUpsTxLinkPulseTiming\t0x%x\n" + "SwrCnt1msIni\t0x%x\n" + "HwSuppNowIsOobVer\t0x%x\n" + "HwFiberModeVer\t0x%x\n" + "HwFiberStat\t0x%x\n" + "HwSwitchMdiToFiber\t0x%x\n" + "HwSuppSerDesPhyVer\t0x%x\n" + "NicCustLedValue\t0x%x\n" + "RequiredSecLanDonglePatch\t0x%x\n" + "HwSuppDashVer\t0x%x\n" + "DASH\t0x%x\n" + "dash_printer_enabled\t0x%x\n" + "HwSuppKCPOffloadVer\t0x%x\n" + "speed_mode\t0x%x\n" + "duplex_mode\t0x%x\n" + "autoneg_mode\t0x%x\n" + "advertising_mode\t0x%x\n" + "aspm\t0x%x\n" + "s5wol\t0x%x\n" + "s5_keep_curr_mac\t0x%x\n" + "eee_enable\t0x%x\n" + "hwoptimize\t0x%lx\n" + "proc_init_num\t0x%x\n" + "s0_magic_packet\t0x%x\n" + "HwSuppMagicPktVer\t0x%x\n" + "HwSuppUpsVer\t0x%x\n" + "HwSuppEsdVer\t0x%x\n" + "HwSuppCheckPhyDisableModeVer\t0x%x\n" + "HwPkgDet\t0x%x\n" + "random_mac\t0x%x\n" + "org_mac_addr\t%pM\n" +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13) + "perm_addr\t%pM\n" +#endif + "dev_addr\t%pM\n", + MODULENAME, + RTL8168_VERSION, + tp->chipset, + rtl_chip_info[tp->chipset].name, + dev->mtu, + NUM_RX_DESC, + tp->cur_rx, + tp->dirty_rx, + NUM_TX_DESC, + tp->cur_tx, + tp->dirty_tx, + tp->rx_buf_sz, + tp->esd_flag, + tp->pci_cfg_is_read, + tp->rtl8168_rx_config, + tp->cp_cmd, + tp->intr_mask, + tp->timer_intr_mask, + tp->wol_enabled, + tp->wol_opts, + tp->efuse_ver, + tp->eeprom_type, + tp->autoneg, + tp->duplex, + tp->speed, + tp->advertising, + tp->eeprom_len, + tp->cur_page, + tp->bios_setting, + tp->features, + tp->org_pci_offset_99, + tp->org_pci_offset_180, + tp->issue_offset_99_event, + tp->org_pci_offset_80, + tp->org_pci_offset_81, + tp->use_timer_interrrupt, + tp->HwIcVerUnknown, + tp->NotWrRamCodeToMicroP, + tp->NotWrMcuPatchCode, + tp->HwHasWrRamCodeToMicroP, + tp->sw_ram_code_ver, + tp->hw_ram_code_ver, + tp->rtk_enable_diag, + tp->ShortPacketSwChecksum, + tp->UseSwPaddingShortPkt, + tp->RequireAdcBiasPatch, + tp->AdcBiasPatchIoffset, + tp->RequireAdjustUpsTxLinkPulseTiming, + tp->SwrCnt1msIni, + tp->HwSuppNowIsOobVer, + tp->HwFiberModeVer, + tp->HwFiberStat, + tp->HwSwitchMdiToFiber, + tp->HwSuppSerDesPhyVer, + tp->NicCustLedValue, + tp->RequiredSecLanDonglePatch, + tp->HwSuppDashVer, + tp->DASH, + tp->dash_printer_enabled, + tp->HwSuppKCPOffloadVer, + speed_mode, + duplex_mode, + autoneg_mode, + advertising_mode, + aspm, + s5wol, + s5_keep_curr_mac, + tp->eee_enabled, + hwoptimize, + proc_init_num, + s0_magic_packet, + tp->HwSuppMagicPktVer, + tp->HwSuppUpsVer, + tp->HwSuppEsdVer, + tp->HwSuppCheckPhyDisableModeVer, + tp->HwPkgDet, + tp->random_mac, + tp->org_mac_addr, +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13) + dev->perm_addr, +#endif + dev->dev_addr + ); + spin_unlock_irqrestore(&tp->lock, flags); + + len += snprintf(page + len, count - len, "\n"); + + *eof = 1; + return len; +} + +static int proc_get_tally_counter(char *page, char **start, + off_t offset, int count, + int *eof, void *data) +{ + struct net_device *dev = data; + struct rtl8168_private *tp = netdev_priv(dev); + struct rtl8168_counters *counters; + dma_addr_t paddr; + u32 cmd; + u32 WaitCnt; + unsigned long flags; + int len = 0; + + len += snprintf(page + len, count - len, + "\nDump Tally Counter\n"); + + //ASSERT_RTNL(); + + counters = tp->tally_vaddr; + paddr = tp->tally_paddr; + if (!counters) { + len += snprintf(page + len, count - len, + "\nDump Tally Counter Fail\n"); + goto out; + } + + spin_lock_irqsave(&tp->lock, flags); + RTL_W32(tp, CounterAddrHigh, (u64)paddr >> 32); + cmd = (u64)paddr & DMA_BIT_MASK(32); + RTL_W32(tp, CounterAddrLow, cmd); + RTL_W32(tp, CounterAddrLow, cmd | CounterDump); + + WaitCnt = 0; + while (RTL_R32(tp, CounterAddrLow) & CounterDump) { + udelay(10); + + WaitCnt++; + if (WaitCnt > 20) + break; + } + spin_unlock_irqrestore(&tp->lock, flags); + + len += snprintf(page + len, count - len, + "Statistics\tValue\n----------\t-----\n"); + + len += snprintf(page + len, count - len, + "tx_packets\t%lld\n" + "rx_packets\t%lld\n" + "tx_errors\t%lld\n" + "rx_errors\t%d\n" + "rx_missed\t%d\n" + "align_errors\t%d\n" + "tx_one_collision\t%d\n" + "tx_multi_collision\t%d\n" + "rx_unicast\t%lld\n" + "rx_broadcast\t%lld\n" + "rx_multicast\t%d\n" + "tx_aborted\t%d\n" + "tx_underrun\t%d\n", + le64_to_cpu(counters->tx_packets), + le64_to_cpu(counters->rx_packets), + le64_to_cpu(counters->tx_errors), + le32_to_cpu(counters->rx_errors), + le16_to_cpu(counters->rx_missed), + le16_to_cpu(counters->align_errors), + le32_to_cpu(counters->tx_one_collision), + le32_to_cpu(counters->tx_multi_collision), + le64_to_cpu(counters->rx_unicast), + le64_to_cpu(counters->rx_broadcast), + le32_to_cpu(counters->rx_multicast), + le16_to_cpu(counters->tx_aborted), + le16_to_cpu(counters->tx_underrun) + ); + + len += snprintf(page + len, count - len, "\n"); +out: + *eof = 1; + return len; +} + +static int proc_get_registers(char *page, char **start, + off_t offset, int count, + int *eof, void *data) +{ + struct net_device *dev = data; + int i, n, max = R8168_MAC_REGS_SIZE; + u8 byte_rd; + struct rtl8168_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned long flags; + int len = 0; + + len += snprintf(page + len, count - len, + "\nDump MAC Registers\n" + "Offset\tValue\n------\t-----\n"); + + spin_lock_irqsave(&tp->lock, flags); + for (n = 0; n < max;) { + len += snprintf(page + len, count - len, + "\n0x%02x:\t", + n); + + for (i = 0; i < 16 && n < max; i++, n++) { + byte_rd = readb(ioaddr + n); + len += snprintf(page + len, count - len, + "%02x ", + byte_rd); + } + } + spin_unlock_irqrestore(&tp->lock, flags); + + len += snprintf(page + len, count - len, "\n"); + + *eof = 1; + return len; +} + +static int proc_get_pcie_phy(char *page, char **start, + off_t offset, int count, + int *eof, void *data) +{ + struct net_device *dev = data; + int i, n, max = R8168_EPHY_REGS_SIZE/2; + u16 word_rd; + struct rtl8168_private *tp = netdev_priv(dev); + unsigned long flags; + int len = 0; + + len += snprintf(page + len, count - len, + "\nDump PCIE PHY\n" + "Offset\tValue\n------\t-----\n"); + + spin_lock_irqsave(&tp->lock, flags); + for (n = 0; n < max;) { + len += snprintf(page + len, count - len, + "\n0x%02x:\t", + n); + + for (i = 0; i < 8 && n < max; i++, n++) { + word_rd = rtl8168_ephy_read(tp, n); + len += snprintf(page + len, count - len, + "%04x ", + word_rd); + } + } + spin_unlock_irqrestore(&tp->lock, flags); + + len += snprintf(page + len, count - len, "\n"); + + *eof = 1; + return len; +} + +static int proc_get_eth_phy(char *page, char **start, + off_t offset, int count, + int *eof, void *data) +{ + struct net_device *dev = data; + int i, n, max = R8168_PHY_REGS_SIZE/2; + u16 word_rd; + struct rtl8168_private *tp = netdev_priv(dev); + unsigned long flags; + int len = 0; + + len += snprintf(page + len, count - len, + "\nDump Ethernet PHY\n" + "Offset\tValue\n------\t-----\n"); + + spin_lock_irqsave(&tp->lock, flags); + len += snprintf(page + len, count - len, + "\n####################page 0##################\n"); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + for (n = 0; n < max;) { + len += snprintf(page + len, count - len, + "\n0x%02x:\t", + n); + + for (i = 0; i < 8 && n < max; i++, n++) { + word_rd = rtl8168_mdio_read(tp, n); + len += snprintf(page + len, count - len, + "%04x ", + word_rd); + } + } + spin_unlock_irqrestore(&tp->lock, flags); + + len += snprintf(page + len, count - len, "\n"); + + *eof = 1; + return len; +} + +static int proc_get_extended_registers(char *page, char **start, + off_t offset, int count, + int *eof, void *data) +{ + struct net_device *dev = data; + int i, n, max = R8168_ERI_REGS_SIZE; + u32 dword_rd; + struct rtl8168_private *tp = netdev_priv(dev); + unsigned long flags; + int len = 0; + + switch (tp->mcfg) { + case CFG_METHOD_1: + case CFG_METHOD_2: + case CFG_METHOD_3: + /* RTL8168B does not support Extend GMAC */ + len += snprintf(page + len, count - len, + "\nNot Support Dump Extended Registers\n"); + + goto out; + } + + len += snprintf(page + len, count - len, + "\nDump Extended Registers\n" + "Offset\tValue\n------\t-----\n"); + + spin_lock_irqsave(&tp->lock, flags); + for (n = 0; n < max;) { + len += snprintf(page + len, count - len, + "\n0x%02x:\t", + n); + + for (i = 0; i < 4 && n < max; i++, n+=4) { + dword_rd = rtl8168_eri_read(tp, n, 4, ERIAR_ExGMAC); + len += snprintf(page + len, count - len, + "%08x ", + dword_rd); + } + } + spin_unlock_irqrestore(&tp->lock, flags); + + len += snprintf(page + len, count - len, "\n"); +out: + *eof = 1; + return len; +} + +static int proc_get_pci_registers(char *page, char **start, + off_t offset, int count, + int *eof, void *data) +{ + struct net_device *dev = data; + int i, n, max = R8168_PCI_REGS_SIZE; + u32 dword_rd; + struct rtl8168_private *tp = netdev_priv(dev); + unsigned long flags; + int len = 0; + + len += snprintf(page + len, count - len, + "\nDump PCI Registers\n" + "Offset\tValue\n------\t-----\n"); + + spin_lock_irqsave(&tp->lock, flags); + for (n = 0; n < max;) { + len += snprintf(page + len, count - len, + "\n0x%03x:\t", + n); + + for (i = 0; i < 4 && n < max; i++, n+=4) { + pci_read_config_dword(tp->pci_dev, n, &dword_rd); + len += snprintf(page + len, count - len, + "%08x ", + dword_rd); + } + } + + n = 0x110; + pci_read_config_dword(tp->pci_dev, n, &dword_rd); + len += snprintf(page + len, count - len, + "\n0x%03x:\t%08x ", + n, + dword_rd); + n = 0x70c; + pci_read_config_dword(tp->pci_dev, n, &dword_rd); + len += snprintf(page + len, count - len, + "\n0x%03x:\t%08x ", + n, + dword_rd); + spin_unlock_irqrestore(&tp->lock, flags); + + len += snprintf(page + len, count - len, "\n"); + + *eof = 1; + return len; +} +#endif +static void rtl8168_proc_module_init(void) +{ + //create /proc/net/r8168 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32) + rtl8168_proc = proc_mkdir(MODULENAME, init_net.proc_net); +#else + rtl8168_proc = proc_mkdir(MODULENAME, proc_net); +#endif + if (!rtl8168_proc) + dprintk("cannot create %s proc entry \n", MODULENAME); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) +/* + * seq_file wrappers for procfile show routines. + */ +static int rtl8168_proc_open(struct inode *inode, struct file *file) +{ + struct net_device *dev = proc_get_parent_data(inode); + int (*show)(struct seq_file *, void *) = PDE_DATA(inode); + + return single_open(file, show, dev); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0) +static const struct proc_ops rtl8168_proc_fops = { + .proc_open = rtl8168_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +#else +static const struct file_operations rtl8168_proc_fops = { + .open = rtl8168_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +#endif + +/* + * Table of proc files we need to create. + */ +struct rtl8168_proc_file { + char name[12]; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) + int (*show)(struct seq_file *, void *); +#else + int (*show)(char *, char **, off_t, int, int *, void *); +#endif +}; + +static const struct rtl8168_proc_file rtl8168_proc_files[] = { + { "driver_var", &proc_get_driver_variable }, + { "tally", &proc_get_tally_counter }, + { "registers", &proc_get_registers }, + { "pcie_phy", &proc_get_pcie_phy }, + { "eth_phy", &proc_get_eth_phy }, + { "ext_regs", &proc_get_extended_registers }, + { "pci_regs", &proc_get_pci_registers }, + { "" } +}; + +static void rtl8168_proc_init(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + const struct rtl8168_proc_file *f; + struct proc_dir_entry *dir; + + if (rtl8168_proc && !tp->proc_dir) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) + dir = proc_mkdir_data(dev->name, 0, rtl8168_proc, dev); + if (!dir) { + printk("Unable to initialize /proc/net/%s/%s\n", + MODULENAME, dev->name); + return; + } + + tp->proc_dir = dir; + proc_init_num++; + + for (f = rtl8168_proc_files; f->name[0]; f++) { + if (!proc_create_data(f->name, S_IFREG | S_IRUGO, dir, + &rtl8168_proc_fops, f->show)) { + printk("Unable to initialize " + "/proc/net/%s/%s/%s\n", + MODULENAME, dev->name, f->name); + return; + } + } +#else + dir = proc_mkdir(dev->name, rtl8168_proc); + if (!dir) { + printk("Unable to initialize /proc/net/%s/%s\n", + MODULENAME, dev->name); + return; + } + + tp->proc_dir = dir; + proc_init_num++; + + for (f = rtl8168_proc_files; f->name[0]; f++) { + if (!create_proc_read_entry(f->name, S_IFREG | S_IRUGO, + dir, f->show, dev)) { + printk("Unable to initialize " + "/proc/net/%s/%s/%s\n", + MODULENAME, dev->name, f->name); + return; + } + } +#endif + } +} + +static void rtl8168_proc_remove(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + if (tp->proc_dir) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) + remove_proc_subtree(dev->name, rtl8168_proc); + proc_init_num--; + +#else + const struct rtl8168_proc_file *f; + struct rtl8168_private *tp = netdev_priv(dev); + + for (f = rtl8168_proc_files; f->name[0]; f++) + remove_proc_entry(f->name, tp->proc_dir); + + remove_proc_entry(dev->name, rtl8168_proc); + proc_init_num--; +#endif + tp->proc_dir = NULL; + } +} + +#endif //ENABLE_R8168_PROCFS + +static inline u16 map_phy_ocp_addr(u16 PageNum, u8 RegNum) +{ + u16 OcpPageNum = 0; + u8 OcpRegNum = 0; + u16 OcpPhyAddress = 0; + + if ( PageNum == 0 ) { + OcpPageNum = OCP_STD_PHY_BASE_PAGE + ( RegNum / 8 ); + OcpRegNum = 0x10 + ( RegNum % 8 ); + } else { + OcpPageNum = PageNum; + OcpRegNum = RegNum; + } + + OcpPageNum <<= 4; + + if ( OcpRegNum < 16 ) { + OcpPhyAddress = 0; + } else { + OcpRegNum -= 16; + OcpRegNum <<= 1; + + OcpPhyAddress = OcpPageNum + OcpRegNum; + } + + + return OcpPhyAddress; +} + +static void mdio_real_direct_write_phy_ocp(struct rtl8168_private *tp, + u32 RegAddr, + u32 value) +{ + u32 data32; + int i; + + if (tp->HwSuppPhyOcpVer == 0) goto out; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) + WARN_ON_ONCE(RegAddr % 2); +#endif + data32 = RegAddr/2; + data32 <<= OCPR_Addr_Reg_shift; + data32 |= OCPR_Write | value; + + RTL_W32(tp, PHYOCP, data32); + for (i = 0; i < 100; i++) { + udelay(1); + + if (!(RTL_R32(tp, PHYOCP) & OCPR_Flag)) + break; + } +out: + return; +} + +static void mdio_direct_write_phy_ocp(struct rtl8168_private *tp, + u16 RegAddr, + u16 value) +{ + if (tp->rtk_enable_diag) return; + + mdio_real_direct_write_phy_ocp(tp, RegAddr, value); +} + +static void rtl8168_mdio_write_phy_ocp(struct rtl8168_private *tp, + u16 PageNum, + u32 RegAddr, + u32 value) +{ + u16 ocp_addr; + + if (tp->rtk_enable_diag) return; + + ocp_addr = map_phy_ocp_addr(PageNum, RegAddr); + + mdio_direct_write_phy_ocp(tp, ocp_addr, value); +} + +static void rtl8168_mdio_real_write_phy_ocp(struct rtl8168_private *tp, + u16 PageNum, + u32 RegAddr, + u32 value) +{ + u16 ocp_addr; + + ocp_addr = map_phy_ocp_addr(PageNum, RegAddr); + + mdio_real_direct_write_phy_ocp(tp, ocp_addr, value); +} + +static void mdio_real_write(struct rtl8168_private *tp, + u32 RegAddr, + u32 value) +{ + int i; + + if (RegAddr == 0x1F) { + tp->cur_page = value; + } + + if (tp->mcfg == CFG_METHOD_11) { + RTL_W32(tp, OCPDR, OCPDR_Write | + (RegAddr & OCPDR_Reg_Mask) << OCPDR_GPHY_Reg_shift | + (value & OCPDR_Data_Mask)); + RTL_W32(tp, OCPAR, OCPAR_GPHY_Write); + RTL_W32(tp, EPHY_RXER_NUM, 0); + + for (i = 0; i < 100; i++) { + mdelay(1); + if (!(RTL_R32(tp, OCPAR) & OCPAR_Flag)) + break; + } + } else { + if (tp->HwSuppPhyOcpVer > 0) { + if (RegAddr == 0x1F) { + return; + } + rtl8168_mdio_real_write_phy_ocp(tp, tp->cur_page, RegAddr, value); + } else { + if (tp->mcfg == CFG_METHOD_12 || tp->mcfg == CFG_METHOD_13) + RTL_W32(tp, 0xD0, RTL_R32(tp, 0xD0) & ~0x00020000); + + RTL_W32(tp, PHYAR, PHYAR_Write | + (RegAddr & PHYAR_Reg_Mask) << PHYAR_Reg_shift | + (value & PHYAR_Data_Mask)); + + for (i = 0; i < 10; i++) { + udelay(100); + + /* Check if the RTL8168 has completed writing to the specified MII register */ + if (!(RTL_R32(tp, PHYAR) & PHYAR_Flag)) { + udelay(20); + break; + } + } + + if (tp->mcfg == CFG_METHOD_12 || tp->mcfg == CFG_METHOD_13) + RTL_W32(tp, 0xD0, RTL_R32(tp, 0xD0) | 0x00020000); + } + } +} + +void rtl8168_mdio_write(struct rtl8168_private *tp, + u16 RegAddr, + u16 value) +{ + if (tp->rtk_enable_diag) return; + + mdio_real_write(tp, RegAddr, value); +} + +void rtl8168_mdio_prot_write(struct rtl8168_private *tp, + u32 RegAddr, + u32 value) +{ + mdio_real_write(tp, RegAddr, value); +} + +void rtl8168_mdio_prot_direct_write_phy_ocp(struct rtl8168_private *tp, + u32 RegAddr, + u32 value) +{ + mdio_real_direct_write_phy_ocp(tp, RegAddr, value); +} + +static u32 mdio_real_direct_read_phy_ocp(struct rtl8168_private *tp, + u32 RegAddr) +{ + u32 data32; + int i, value = 0; + + if (tp->HwSuppPhyOcpVer == 0) goto out; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) + WARN_ON_ONCE(RegAddr % 2); +#endif + data32 = RegAddr/2; + data32 <<= OCPR_Addr_Reg_shift; + + RTL_W32(tp, PHYOCP, data32); + for (i = 0; i < 100; i++) { + udelay(1); + + if (RTL_R32(tp, PHYOCP) & OCPR_Flag) + break; + } + value = RTL_R32(tp, PHYOCP) & OCPDR_Data_Mask; + +out: + return value; +} + +static u32 mdio_direct_read_phy_ocp(struct rtl8168_private *tp, + u16 RegAddr) +{ + if (tp->rtk_enable_diag) return 0xffffffff; + + return mdio_real_direct_read_phy_ocp(tp, RegAddr); +} + +static u32 rtl8168_mdio_read_phy_ocp(struct rtl8168_private *tp, + u16 PageNum, + u32 RegAddr) +{ + u16 ocp_addr; + + if (tp->rtk_enable_diag) return 0xffffffff; + + ocp_addr = map_phy_ocp_addr(PageNum, RegAddr); + + return mdio_direct_read_phy_ocp(tp, ocp_addr); +} + +static u32 rtl8168_mdio_real_read_phy_ocp(struct rtl8168_private *tp, + u16 PageNum, + u32 RegAddr) +{ + u16 ocp_addr; + + ocp_addr = map_phy_ocp_addr(PageNum, RegAddr); + + return mdio_real_direct_read_phy_ocp(tp, ocp_addr); +} + +u32 mdio_real_read(struct rtl8168_private *tp, + u32 RegAddr) +{ + int i, value = 0; + + if (tp->mcfg==CFG_METHOD_11) { + RTL_W32(tp, OCPDR, OCPDR_Read | + (RegAddr & OCPDR_Reg_Mask) << OCPDR_GPHY_Reg_shift); + RTL_W32(tp, OCPAR, OCPAR_GPHY_Write); + RTL_W32(tp, EPHY_RXER_NUM, 0); + + for (i = 0; i < 100; i++) { + mdelay(1); + if (!(RTL_R32(tp, OCPAR) & OCPAR_Flag)) + break; + } + + mdelay(1); + RTL_W32(tp, OCPAR, OCPAR_GPHY_Read); + RTL_W32(tp, EPHY_RXER_NUM, 0); + + for (i = 0; i < 100; i++) { + mdelay(1); + if (RTL_R32(tp, OCPAR) & OCPAR_Flag) + break; + } + + value = RTL_R32(tp, OCPDR) & OCPDR_Data_Mask; + } else { + if (tp->HwSuppPhyOcpVer > 0) { + value = rtl8168_mdio_real_read_phy_ocp(tp, tp->cur_page, RegAddr); + } else { + if (tp->mcfg == CFG_METHOD_12 || tp->mcfg == CFG_METHOD_13) + RTL_W32(tp, 0xD0, RTL_R32(tp, 0xD0) & ~0x00020000); + + RTL_W32(tp, PHYAR, + PHYAR_Read | (RegAddr & PHYAR_Reg_Mask) << PHYAR_Reg_shift); + + for (i = 0; i < 10; i++) { + udelay(100); + + /* Check if the RTL8168 has completed retrieving data from the specified MII register */ + if (RTL_R32(tp, PHYAR) & PHYAR_Flag) { + value = RTL_R32(tp, PHYAR) & PHYAR_Data_Mask; + udelay(20); + break; + } + } + + if (tp->mcfg == CFG_METHOD_12 || tp->mcfg == CFG_METHOD_13) + RTL_W32(tp, 0xD0, RTL_R32(tp, 0xD0) | 0x00020000); + } + } + + return value; +} + +u32 rtl8168_mdio_read(struct rtl8168_private *tp, + u16 RegAddr) +{ + if (tp->rtk_enable_diag) return 0xffffffff; + + return mdio_real_read(tp, RegAddr); +} + +u32 rtl8168_mdio_prot_read(struct rtl8168_private *tp, + u32 RegAddr) +{ + return mdio_real_read(tp, RegAddr); +} + +u32 rtl8168_mdio_prot_direct_read_phy_ocp(struct rtl8168_private *tp, + u32 RegAddr) +{ + return mdio_real_direct_read_phy_ocp(tp, RegAddr); +} + +static void ClearAndSetEthPhyBit(struct rtl8168_private *tp, u8 addr, u16 clearmask, u16 setmask) +{ + u16 PhyRegValue; + + + PhyRegValue = rtl8168_mdio_read(tp, addr); + PhyRegValue &= ~clearmask; + PhyRegValue |= setmask; + rtl8168_mdio_write(tp, addr, PhyRegValue); +} + +void rtl8168_clear_eth_phy_bit(struct rtl8168_private *tp, u8 addr, u16 mask) +{ + ClearAndSetEthPhyBit(tp, + addr, + mask, + 0 + ); +} + +void rtl8168_set_eth_phy_bit(struct rtl8168_private *tp, u8 addr, u16 mask) +{ + ClearAndSetEthPhyBit(tp, + addr, + 0, + mask + ); +} + +void rtl8168_mac_ocp_write(struct rtl8168_private *tp, u16 reg_addr, u16 value) +{ + u32 data32; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) + WARN_ON_ONCE(reg_addr % 2); +#endif + + data32 = reg_addr/2; + data32 <<= OCPR_Addr_Reg_shift; + data32 += value; + data32 |= OCPR_Write; + + RTL_W32(tp, MACOCP, data32); +} + +u16 rtl8168_mac_ocp_read(struct rtl8168_private *tp, u16 reg_addr) +{ + u32 data32; + u16 data16 = 0; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) + WARN_ON_ONCE(reg_addr % 2); +#endif + + data32 = reg_addr/2; + data32 <<= OCPR_Addr_Reg_shift; + + RTL_W32(tp, MACOCP, data32); + data16 = (u16)RTL_R32(tp, MACOCP); + + return data16; +} + +#ifdef ENABLE_USE_FIRMWARE_FILE +static void mac_mcu_write(struct rtl8168_private *tp, u16 reg, u16 value) +{ + if (reg == 0x1f) { + tp->ocp_base = value << 4; + return; + } + + rtl8168_mac_ocp_write(tp, tp->ocp_base + reg, value); +} + +static u32 mac_mcu_read(struct rtl8168_private *tp, u16 reg) +{ + return rtl8168_mac_ocp_read(tp, tp->ocp_base + reg); +} +#endif + +static void +rtl8168_clear_and_set_mcu_ocp_bit( + struct rtl8168_private *tp, + u16 addr, + u16 clearmask, + u16 setmask +) +{ + u16 RegValue; + + RegValue = rtl8168_mac_ocp_read(tp, addr); + RegValue &= ~clearmask; + RegValue |= setmask; + rtl8168_mac_ocp_write(tp, addr, RegValue); +} + +/* +static void +rtl8168_clear_mcu_ocp_bit( + struct rtl8168_private *tp, + u16 addr, + u16 mask +) +{ + rtl8168_clear_and_set_mcu_ocp_bit(tp, + addr, + mask, + 0 + ); +} +*/ + +static void +rtl8168_set_mcu_ocp_bit( + struct rtl8168_private *tp, + u16 addr, + u16 mask +) +{ + rtl8168_clear_and_set_mcu_ocp_bit(tp, + addr, + 0, + mask + ); +} + +static u32 real_ocp_read(struct rtl8168_private *tp, u16 addr, u8 len) +{ + int i, val_shift, shift = 0; + u32 value1 = 0, value2 = 0, mask; + + if (len > 4 || len <= 0) + return -1; + + while (len > 0) { + val_shift = addr % 4; + addr = addr & ~0x3; + + RTL_W32(tp, OCPAR, (0x0F<<12) | (addr&0xFFF)); + + for (i = 0; i < 20; i++) { + udelay(100); + if (RTL_R32(tp, OCPAR) & OCPAR_Flag) + break; + } + + if (len == 1) mask = (0xFF << (val_shift * 8)) & 0xFFFFFFFF; + else if (len == 2) mask = (0xFFFF << (val_shift * 8)) & 0xFFFFFFFF; + else if (len == 3) mask = (0xFFFFFF << (val_shift * 8)) & 0xFFFFFFFF; + else mask = (0xFFFFFFFF << (val_shift * 8)) & 0xFFFFFFFF; + + value1 = RTL_R32(tp, OCPDR) & mask; + value2 |= (value1 >> val_shift * 8) << shift * 8; + + if (len <= 4 - val_shift) { + len = 0; + } else { + len -= (4 - val_shift); + shift = 4 - val_shift; + addr += 4; + } + } + + udelay(20); + + return value2; +} + +u32 rtl8168_ocp_read_with_oob_base_address(struct rtl8168_private *tp, u16 addr, u8 len, const u32 base_address) +{ + return rtl8168_eri_read_with_oob_base_address(tp, addr, len, ERIAR_OOB, base_address); +} + +u32 rtl8168_ocp_read(struct rtl8168_private *tp, u16 addr, u8 len) +{ + u32 value = 0; + + if (HW_DASH_SUPPORT_TYPE_2(tp)) + value = rtl8168_ocp_read_with_oob_base_address(tp, addr, len, NO_BASE_ADDRESS); + else if (HW_DASH_SUPPORT_TYPE_3(tp)) + value = rtl8168_ocp_read_with_oob_base_address(tp, addr, len, RTL8168FP_OOBMAC_BASE); + else + value = real_ocp_read(tp, addr, len); + + return value; +} + +static int real_ocp_write(struct rtl8168_private *tp, u16 addr, u8 len, u32 value) +{ + int i, val_shift, shift = 0; + u32 value1 = 0, mask; + + if (len > 4 || len <= 0) + return -1; + + while (len > 0) { + val_shift = addr % 4; + addr = addr & ~0x3; + + if (len == 1) mask = (0xFF << (val_shift * 8)) & 0xFFFFFFFF; + else if (len == 2) mask = (0xFFFF << (val_shift * 8)) & 0xFFFFFFFF; + else if (len == 3) mask = (0xFFFFFF << (val_shift * 8)) & 0xFFFFFFFF; + else mask = (0xFFFFFFFF << (val_shift * 8)) & 0xFFFFFFFF; + + value1 = rtl8168_ocp_read(tp, addr, 4) & ~mask; + value1 |= ((value << val_shift * 8) >> shift * 8); + + RTL_W32(tp, OCPDR, value1); + RTL_W32(tp, OCPAR, OCPAR_Flag | (0x0F<<12) | (addr&0xFFF)); + + for (i = 0; i < 10; i++) { + udelay(100); + + /* Check if the RTL8168 has completed ERI write */ + if (!(RTL_R32(tp, OCPAR) & OCPAR_Flag)) + break; + } + + if (len <= 4 - val_shift) { + len = 0; + } else { + len -= (4 - val_shift); + shift = 4 - val_shift; + addr += 4; + } + } + + udelay(20); + + return 0; +} + +u32 rtl8168_ocp_write_with_oob_base_address(struct rtl8168_private *tp, u16 addr, u8 len, u32 value, const u32 base_address) +{ + return rtl8168_eri_write_with_oob_base_address(tp, addr, len, value, ERIAR_OOB, base_address); +} + +void rtl8168_ocp_write(struct rtl8168_private *tp, u16 addr, u8 len, u32 value) +{ + if (HW_DASH_SUPPORT_TYPE_2(tp)) + rtl8168_ocp_write_with_oob_base_address(tp, addr, len, value, NO_BASE_ADDRESS); + else if (HW_DASH_SUPPORT_TYPE_3(tp)) + rtl8168_ocp_write_with_oob_base_address(tp, addr, len, value, RTL8168FP_OOBMAC_BASE); + else + real_ocp_write(tp, addr, len, value); +} + +void rtl8168_oob_mutex_lock(struct rtl8168_private *tp) +{ + u8 reg_16, reg_a0; + u32 wait_cnt_0, wait_Cnt_1; + u16 ocp_reg_mutex_ib; + u16 ocp_reg_mutex_oob; + u16 ocp_reg_mutex_prio; + + if (!tp->DASH) return; + + switch (tp->mcfg) { + case CFG_METHOD_11: + case CFG_METHOD_12: + ocp_reg_mutex_oob = 0x16; + ocp_reg_mutex_ib = 0x17; + ocp_reg_mutex_prio = 0x9C; + break; + case CFG_METHOD_13: + ocp_reg_mutex_oob = 0x06; + ocp_reg_mutex_ib = 0x07; + ocp_reg_mutex_prio = 0x9C; + break; + case CFG_METHOD_23: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + default: + ocp_reg_mutex_oob = 0x110; + ocp_reg_mutex_ib = 0x114; + ocp_reg_mutex_prio = 0x11C; + break; + } + + rtl8168_ocp_write(tp, ocp_reg_mutex_ib, 1, BIT_0); + reg_16 = rtl8168_ocp_read(tp, ocp_reg_mutex_oob, 1); + wait_cnt_0 = 0; + while(reg_16) { + reg_a0 = rtl8168_ocp_read(tp, ocp_reg_mutex_prio, 1); + if (reg_a0) { + rtl8168_ocp_write(tp, ocp_reg_mutex_ib, 1, 0x00); + reg_a0 = rtl8168_ocp_read(tp, ocp_reg_mutex_prio, 1); + wait_Cnt_1 = 0; + while(reg_a0) { + reg_a0 = rtl8168_ocp_read(tp, ocp_reg_mutex_prio, 1); + + wait_Cnt_1++; + + if (wait_Cnt_1 > 2000) + break; + }; + rtl8168_ocp_write(tp, ocp_reg_mutex_ib, 1, BIT_0); + + } + reg_16 = rtl8168_ocp_read(tp, ocp_reg_mutex_oob, 1); + + wait_cnt_0++; + + if (wait_cnt_0 > 2000) + break; + }; +} + +void rtl8168_oob_mutex_unlock(struct rtl8168_private *tp) +{ + u16 ocp_reg_mutex_ib; + u16 ocp_reg_mutex_oob; + u16 ocp_reg_mutex_prio; + + if (!tp->DASH) return; + + switch (tp->mcfg) { + case CFG_METHOD_11: + case CFG_METHOD_12: + ocp_reg_mutex_oob = 0x16; + ocp_reg_mutex_ib = 0x17; + ocp_reg_mutex_prio = 0x9C; + break; + case CFG_METHOD_13: + ocp_reg_mutex_oob = 0x06; + ocp_reg_mutex_ib = 0x07; + ocp_reg_mutex_prio = 0x9C; + break; + case CFG_METHOD_23: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + default: + ocp_reg_mutex_oob = 0x110; + ocp_reg_mutex_ib = 0x114; + ocp_reg_mutex_prio = 0x11C; + break; + } + + rtl8168_ocp_write(tp, ocp_reg_mutex_prio, 1, BIT_0); + rtl8168_ocp_write(tp, ocp_reg_mutex_ib, 1, 0x00); +} + +void rtl8168_oob_notify(struct rtl8168_private *tp, u8 cmd) +{ + rtl8168_eri_write(tp, 0xE8, 1, cmd, ERIAR_ExGMAC); + + rtl8168_ocp_write(tp, 0x30, 1, 0x01); +} + +static int rtl8168_check_dash(struct rtl8168_private *tp) +{ + if (HW_DASH_SUPPORT_TYPE_2(tp) || HW_DASH_SUPPORT_TYPE_3(tp)) { + if (rtl8168_ocp_read(tp, 0x128, 1) & BIT_0) + return 1; + else + return 0; + } else { + u32 reg; + + if (tp->mcfg == CFG_METHOD_13) + reg = 0xb8; + else + reg = 0x10; + + if (rtl8168_ocp_read(tp, reg, 2) & 0x00008000) + return 1; + else + return 0; + } +} + +void rtl8168_dash2_disable_tx(struct rtl8168_private *tp) +{ + if (!tp->DASH) return; + + if (HW_DASH_SUPPORT_TYPE_2(tp) || HW_DASH_SUPPORT_TYPE_3(tp)) { + u16 WaitCnt; + u8 TmpUchar; + + //Disable oob Tx + RTL_CMAC_W8(tp, CMAC_IBCR2, RTL_CMAC_R8(tp, CMAC_IBCR2) & ~( BIT_0 )); + WaitCnt = 0; + + //wait oob tx disable + do { + TmpUchar = RTL_CMAC_R8(tp, CMAC_IBISR0); + + if ( TmpUchar & ISRIMR_DASH_TYPE2_TX_DISABLE_IDLE ) { + break; + } + + udelay( 50 ); + WaitCnt++; + } while(WaitCnt < 2000); + + //Clear ISRIMR_DASH_TYPE2_TX_DISABLE_IDLE + RTL_CMAC_W8(tp, CMAC_IBISR0, RTL_CMAC_R8(tp, CMAC_IBISR0) | ISRIMR_DASH_TYPE2_TX_DISABLE_IDLE); + } +} + +void rtl8168_dash2_enable_tx(struct rtl8168_private *tp) +{ + if (!tp->DASH) return; + + if (HW_DASH_SUPPORT_TYPE_2(tp) || HW_DASH_SUPPORT_TYPE_3(tp)) { + RTL_CMAC_W8(tp, CMAC_IBCR2, RTL_CMAC_R8(tp, CMAC_IBCR2) | BIT_0); + } +} + +void rtl8168_dash2_disable_rx(struct rtl8168_private *tp) +{ + if (!tp->DASH) return; + + if (HW_DASH_SUPPORT_TYPE_2(tp) || HW_DASH_SUPPORT_TYPE_3(tp)) { + RTL_CMAC_W8(tp, CMAC_IBCR0, RTL_CMAC_R8(tp, CMAC_IBCR0) & ~( BIT_0 )); + } +} + +void rtl8168_dash2_enable_rx(struct rtl8168_private *tp) +{ + if (!tp->DASH) return; + + if (HW_DASH_SUPPORT_TYPE_2(tp) || HW_DASH_SUPPORT_TYPE_3(tp)) { + RTL_CMAC_W8(tp, CMAC_IBCR0, RTL_CMAC_R8(tp, CMAC_IBCR0) | BIT_0); + } +} + +static void rtl8168_dash2_disable_txrx(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + if (HW_DASH_SUPPORT_TYPE_2(tp) || HW_DASH_SUPPORT_TYPE_3(tp)) { + rtl8168_dash2_disable_tx( tp ); + rtl8168_dash2_disable_rx( tp ); + } +} + +void rtl8168_ephy_write(struct rtl8168_private *tp, int RegAddr, int value) +{ + int i; + + RTL_W32(tp, EPHYAR, + EPHYAR_Write | + (RegAddr & EPHYAR_Reg_Mask) << EPHYAR_Reg_shift | + (value & EPHYAR_Data_Mask)); + + for (i = 0; i < 10; i++) { + udelay(100); + + /* Check if the RTL8168 has completed EPHY write */ + if (!(RTL_R32(tp, EPHYAR) & EPHYAR_Flag)) + break; + } + + udelay(20); +} + +u16 rtl8168_ephy_read(struct rtl8168_private *tp, int RegAddr) +{ + int i; + u16 value = 0xffff; + + RTL_W32(tp, EPHYAR, + EPHYAR_Read | (RegAddr & EPHYAR_Reg_Mask) << EPHYAR_Reg_shift); + + for (i = 0; i < 10; i++) { + udelay(100); + + /* Check if the RTL8168 has completed EPHY read */ + if (RTL_R32(tp, EPHYAR) & EPHYAR_Flag) { + value = (u16) (RTL_R32(tp, EPHYAR) & EPHYAR_Data_Mask); + break; + } + } + + udelay(20); + + return value; +} + +static void ClearAndSetPCIePhyBit(struct rtl8168_private *tp, u8 addr, u16 clearmask, u16 setmask) +{ + u16 EphyValue; + + EphyValue = rtl8168_ephy_read(tp, addr); + EphyValue &= ~clearmask; + EphyValue |= setmask; + rtl8168_ephy_write(tp, addr, EphyValue); +} + +static void ClearPCIePhyBit(struct rtl8168_private *tp, u8 addr, u16 mask) +{ + ClearAndSetPCIePhyBit( tp, + addr, + mask, + 0 + ); +} + +static void SetPCIePhyBit( struct rtl8168_private *tp, u8 addr, u16 mask) +{ + ClearAndSetPCIePhyBit( tp, + addr, + 0, + mask + ); +} + +static u32 +rtl8168_csi_other_fun_read(struct rtl8168_private *tp, + u8 multi_fun_sel_bit, + u32 addr) +{ + u32 cmd; + int i; + u32 value = 0; + + cmd = CSIAR_Read | CSIAR_ByteEn << CSIAR_ByteEn_shift | (addr & CSIAR_Addr_Mask); + + if (tp->mcfg != CFG_METHOD_20 && tp->mcfg != CFG_METHOD_23 && + tp->mcfg != CFG_METHOD_26 && tp->mcfg != CFG_METHOD_27 && + tp->mcfg != CFG_METHOD_28 && tp->mcfg != CFG_METHOD_31 && + tp->mcfg != CFG_METHOD_32 && tp->mcfg != CFG_METHOD_33) { + multi_fun_sel_bit = 0; + } + + if ( multi_fun_sel_bit > 7 ) { + return 0xffffffff; + } + + cmd |= multi_fun_sel_bit << 16; + + RTL_W32(tp, CSIAR, cmd); + + for (i = 0; i < 10; i++) { + udelay(100); + + /* Check if the RTL8168 has completed CSI read */ + if (RTL_R32(tp, CSIAR) & CSIAR_Flag) { + value = (u32)RTL_R32(tp, CSIDR); + break; + } + } + + udelay(20); + + return value; +} + +static void +rtl8168_csi_other_fun_write(struct rtl8168_private *tp, + u8 multi_fun_sel_bit, + u32 addr, + u32 value) +{ + u32 cmd; + int i; + + RTL_W32(tp, CSIDR, value); + cmd = CSIAR_Write | CSIAR_ByteEn << CSIAR_ByteEn_shift | (addr & CSIAR_Addr_Mask); + if (tp->mcfg != CFG_METHOD_20 && tp->mcfg != CFG_METHOD_23 && + tp->mcfg != CFG_METHOD_26 && tp->mcfg != CFG_METHOD_27 && + tp->mcfg != CFG_METHOD_28 && tp->mcfg != CFG_METHOD_31 && + tp->mcfg != CFG_METHOD_32 && tp->mcfg != CFG_METHOD_33) { + multi_fun_sel_bit = 0; + } + + if ( multi_fun_sel_bit > 7 ) { + return; + } + + cmd |= multi_fun_sel_bit << 16; + + RTL_W32(tp, CSIAR, cmd); + + for (i = 0; i < 10; i++) { + udelay(100); + + /* Check if the RTL8168 has completed CSI write */ + if (!(RTL_R32(tp, CSIAR) & CSIAR_Flag)) + break; + } + + udelay(20); +} + +static u32 +rtl8168_csi_read(struct rtl8168_private *tp, + u32 addr) +{ + u8 multi_fun_sel_bit; + + if (tp->mcfg == CFG_METHOD_20) + multi_fun_sel_bit = 2; + else if (tp->mcfg == CFG_METHOD_26 || tp->mcfg == CFG_METHOD_31 || + tp->mcfg == CFG_METHOD_32 || tp->mcfg == CFG_METHOD_33) + multi_fun_sel_bit = 1; + else + multi_fun_sel_bit = 0; + + return rtl8168_csi_other_fun_read(tp, multi_fun_sel_bit, addr); +} + +static void +rtl8168_csi_write(struct rtl8168_private *tp, + u32 addr, + u32 value) +{ + u8 multi_fun_sel_bit; + + if (tp->mcfg == CFG_METHOD_20) + multi_fun_sel_bit = 2; + else if (tp->mcfg == CFG_METHOD_26 || tp->mcfg == CFG_METHOD_31 || + tp->mcfg == CFG_METHOD_32 || tp->mcfg == CFG_METHOD_33) + multi_fun_sel_bit = 1; + else + multi_fun_sel_bit = 0; + + rtl8168_csi_other_fun_write(tp, multi_fun_sel_bit, addr, value); +} + +static u8 +rtl8168_csi_fun0_read_byte(struct rtl8168_private *tp, + u32 addr) +{ + u8 RetVal = 0; + + if (tp->mcfg == CFG_METHOD_20 || tp->mcfg == CFG_METHOD_26 || + tp->mcfg == CFG_METHOD_31 || tp->mcfg == CFG_METHOD_32 || + tp->mcfg == CFG_METHOD_33) { + u32 TmpUlong; + u16 RegAlignAddr; + u8 ShiftByte; + + RegAlignAddr = addr & ~(0x3); + ShiftByte = addr & (0x3); + TmpUlong = rtl8168_csi_other_fun_read(tp, 0, addr); + TmpUlong >>= (8*ShiftByte); + RetVal = (u8)TmpUlong; + } else { + struct pci_dev *pdev = tp->pci_dev; + + pci_read_config_byte(pdev, addr, &RetVal); + } + + udelay(20); + + return RetVal; +} + +static void +rtl8168_csi_fun0_write_byte(struct rtl8168_private *tp, + u32 addr, + u8 value) +{ + if (tp->mcfg == CFG_METHOD_20 || tp->mcfg == CFG_METHOD_26 || + tp->mcfg == CFG_METHOD_31 || tp->mcfg == CFG_METHOD_32 || + tp->mcfg == CFG_METHOD_33) { + u32 TmpUlong; + u16 RegAlignAddr; + u8 ShiftByte; + + RegAlignAddr = addr & ~(0x3); + ShiftByte = addr & (0x3); + TmpUlong = rtl8168_csi_other_fun_read(tp, 0, RegAlignAddr); + TmpUlong &= ~(0xFF << (8*ShiftByte)); + TmpUlong |= (value << (8*ShiftByte)); + rtl8168_csi_other_fun_write( tp, 0, RegAlignAddr, TmpUlong ); + } else { + struct pci_dev *pdev = tp->pci_dev; + + pci_write_config_byte(pdev, addr, value); + } + + udelay(20); +} + +static void +rtl8168_clear_and_set_other_fun_pci_bit(struct rtl8168_private *tp, + u8 multi_fun_sel_bit, + u32 addr, + u32 clearmask, + u32 setmask) +{ + u32 TmpUlong; + + TmpUlong = rtl8168_csi_other_fun_read(tp, multi_fun_sel_bit, addr); + TmpUlong &= ~clearmask; + TmpUlong |= setmask; + rtl8168_csi_other_fun_write(tp, multi_fun_sel_bit, addr, TmpUlong); +} + +static void +rtl8168_other_fun_dev_pci_setting(struct rtl8168_private *tp, + u32 addr, + u32 clearmask, + u32 setmask, + u8 multi_fun_sel_bit) +{ + u32 TmpUlong; + u8 i; + u8 FunBit; + + for (i = 0; i < 8; i++) { + FunBit = (1 << i); + if (FunBit & multi_fun_sel_bit) { + u8 set_other_fun = TRUE; + + switch(tp->mcfg) { + case CFG_METHOD_23: + case CFG_METHOD_27: + case CFG_METHOD_28: + //0: UMAC, 1: TCR1, 2: TCR2, 3: KCS, 4: EHCI(Control by EHCI Driver) + if (i < 5) { + TmpUlong = rtl8168_csi_other_fun_read(tp, i, 0x00); + if (TmpUlong == 0xFFFFFFFF) + set_other_fun = TRUE; + else + set_other_fun = FALSE; + } + break; + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + //0: BMC, 1: NIC, 2: TCR, 3: VGA/PCIE_TO_USB, 4: EHCI, 5: WIFI, 6: WIFI, 7: KCS + if (i == 5 || i == 6) { + if (tp->DASH) { + TmpUlong = rtl8168_ocp_read(tp, 0x184, 4); + if (TmpUlong & BIT_26) + set_other_fun = FALSE; + else + set_other_fun = TRUE; + } + } else { //function 0/1/2/3/4/7 + TmpUlong = rtl8168_csi_other_fun_read(tp, i, 0x00); + if (TmpUlong == 0xFFFFFFFF) + set_other_fun = TRUE; + else + set_other_fun = FALSE; + } + break; + default: + return; + } + + if (set_other_fun) + rtl8168_clear_and_set_other_fun_pci_bit(tp, i, addr, clearmask, setmask); + } + } +} + +static void +rtl8168_set_dash_other_fun_dev_state_change(struct rtl8168_private *tp, + u8 dev_state, + u8 multi_fun_sel_bit) +{ + u32 clearmask; + u32 setmask; + + if (dev_state == 0) { + // + // goto D0 + // + clearmask = (BIT_0 | BIT_1); + setmask = 0; + + rtl8168_other_fun_dev_pci_setting(tp, 0x44, clearmask, setmask, multi_fun_sel_bit); + } else { + // + // goto D3 + // + clearmask = 0; + setmask = (BIT_0 | BIT_1); + + rtl8168_other_fun_dev_pci_setting(tp, 0x44, clearmask, setmask, multi_fun_sel_bit); + } +} + +static void +rtl8168_set_dash_other_fun_dev_aspm_clkreq(struct rtl8168_private *tp, + u8 aspm_val, + u8 clkreq_en, + u8 multi_fun_sel_bit) +{ + u32 clearmask; + u32 setmask; + + aspm_val &= (BIT_0 | BIT_1); + clearmask = (BIT_0 | BIT_1 | BIT_8); + setmask = aspm_val; + if (clkreq_en) + setmask |= BIT_8; + + rtl8168_other_fun_dev_pci_setting(tp, 0x80, clearmask, setmask, multi_fun_sel_bit); +} + +/* +static void +rtl8168_set_dash_other_fun_dev_pci_cmd_register(struct rtl8168_private *tp, + u8 pci_cmd_reg, + u8 multi_fun_sel_bit) +{ + u32 clearmask; + u32 setmask; + + pci_cmd_reg &= (BIT_0 | BIT_1 | BIT_2); + + clearmask = (BIT_0 | BIT_1 | BIT_2); + setmask = pci_cmd_reg; + + rtl8168_other_fun_dev_pci_setting(tp, 0x04, clearmask, setmask, multi_fun_sel_bit); +} +*/ + +u32 rtl8168_eri_read_with_oob_base_address(struct rtl8168_private *tp, int addr, int len, int type, const u32 base_address) +{ + int i, val_shift, shift = 0; + u32 value1 = 0, value2 = 0, mask; + u32 eri_cmd; + const u32 transformed_base_address = ((base_address & 0x00FFF000) << 6) | (base_address & 0x000FFF); + + if (len > 4 || len <= 0) + return -1; + + while (len > 0) { + val_shift = addr % ERIAR_Addr_Align; + addr = addr & ~0x3; + + eri_cmd = ERIAR_Read | + transformed_base_address | + type << ERIAR_Type_shift | + ERIAR_ByteEn << ERIAR_ByteEn_shift | + (addr & 0x0FFF); + if (addr & 0xF000) { + u32 tmp; + + tmp = addr & 0xF000; + tmp >>= 12; + eri_cmd |= (tmp << 20) & 0x00F00000; + } + + RTL_W32(tp, ERIAR, eri_cmd); + + for (i = 0; i < 10; i++) { + udelay(100); + + /* Check if the RTL8168 has completed ERI read */ + if (RTL_R32(tp, ERIAR) & ERIAR_Flag) + break; + } + + if (len == 1) mask = (0xFF << (val_shift * 8)) & 0xFFFFFFFF; + else if (len == 2) mask = (0xFFFF << (val_shift * 8)) & 0xFFFFFFFF; + else if (len == 3) mask = (0xFFFFFF << (val_shift * 8)) & 0xFFFFFFFF; + else mask = (0xFFFFFFFF << (val_shift * 8)) & 0xFFFFFFFF; + + value1 = RTL_R32(tp, ERIDR) & mask; + value2 |= (value1 >> val_shift * 8) << shift * 8; + + if (len <= 4 - val_shift) { + len = 0; + } else { + len -= (4 - val_shift); + shift = 4 - val_shift; + addr += 4; + } + } + + udelay(20); + + return value2; +} + +u32 rtl8168_eri_read(struct rtl8168_private *tp, int addr, int len, int type) +{ + return rtl8168_eri_read_with_oob_base_address(tp, addr, len, type, 0); +} + +int rtl8168_eri_write_with_oob_base_address(struct rtl8168_private *tp, int addr, int len, u32 value, int type, const u32 base_address) +{ + int i, val_shift, shift = 0; + u32 value1 = 0, mask; + u32 eri_cmd; + const u32 transformed_base_address = ((base_address & 0x00FFF000) << 6) | (base_address & 0x000FFF); + + if (len > 4 || len <= 0) + return -1; + + while (len > 0) { + val_shift = addr % ERIAR_Addr_Align; + addr = addr & ~0x3; + + if (len == 1) mask = (0xFF << (val_shift * 8)) & 0xFFFFFFFF; + else if (len == 2) mask = (0xFFFF << (val_shift * 8)) & 0xFFFFFFFF; + else if (len == 3) mask = (0xFFFFFF << (val_shift * 8)) & 0xFFFFFFFF; + else mask = (0xFFFFFFFF << (val_shift * 8)) & 0xFFFFFFFF; + + value1 = rtl8168_eri_read_with_oob_base_address(tp, addr, 4, type, base_address) & ~mask; + value1 |= ((value << val_shift * 8) >> shift * 8); + + RTL_W32(tp, ERIDR, value1); + + eri_cmd = ERIAR_Write | + transformed_base_address | + type << ERIAR_Type_shift | + ERIAR_ByteEn << ERIAR_ByteEn_shift | + (addr & 0x0FFF); + if (addr & 0xF000) { + u32 tmp; + + tmp = addr & 0xF000; + tmp >>= 12; + eri_cmd |= (tmp << 20) & 0x00F00000; + } + + RTL_W32(tp, ERIAR, eri_cmd); + + for (i = 0; i < 10; i++) { + udelay(100); + + /* Check if the RTL8168 has completed ERI write */ + if (!(RTL_R32(tp, ERIAR) & ERIAR_Flag)) + break; + } + + if (len <= 4 - val_shift) { + len = 0; + } else { + len -= (4 - val_shift); + shift = 4 - val_shift; + addr += 4; + } + } + + udelay(20); + + return 0; +} + +int rtl8168_eri_write(struct rtl8168_private *tp, int addr, int len, u32 value, int type) +{ + return rtl8168_eri_write_with_oob_base_address(tp, addr, len, value, type, NO_BASE_ADDRESS); +} + +static void +rtl8168_enable_rxdvgate(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + RTL_W8(tp, 0xF2, RTL_R8(tp, 0xF2) | BIT_3); + mdelay(2); + break; + } +} + +static void +rtl8168_disable_rxdvgate(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + RTL_W8(tp, 0xF2, RTL_R8(tp, 0xF2) & ~BIT_3); + mdelay(2); + break; + } +} + +static u8 +rtl8168_is_gpio_low(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u8 gpio_low = FALSE; + + switch (tp->HwSuppCheckPhyDisableModeVer) { + case 1: + case 2: + if (!(rtl8168_mac_ocp_read(tp, 0xDC04) & BIT_9)) + gpio_low = TRUE; + break; + case 3: + if (!(rtl8168_mac_ocp_read(tp, 0xDC04) & BIT_13)) + gpio_low = TRUE; + break; + } + + if (gpio_low) + dprintk("gpio is low.\n"); + + return gpio_low; +} + +static u8 +rtl8168_is_phy_disable_mode_enabled(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u8 phy_disable_mode_enabled = FALSE; + + switch (tp->HwSuppCheckPhyDisableModeVer) { + case 1: + if (rtl8168_mac_ocp_read(tp, 0xDC20) & BIT_1) + phy_disable_mode_enabled = TRUE; + break; + case 2: + case 3: + if (RTL_R8(tp, 0xF2) & BIT_5) + phy_disable_mode_enabled = TRUE; + break; + } + + if (phy_disable_mode_enabled) + dprintk("phy disable mode enabled.\n"); + + return phy_disable_mode_enabled; +} + +static u8 +rtl8168_is_in_phy_disable_mode(struct net_device *dev) +{ + u8 in_phy_disable_mode = FALSE; + + if (rtl8168_is_phy_disable_mode_enabled(dev) && rtl8168_is_gpio_low(dev)) + in_phy_disable_mode = TRUE; + + if (in_phy_disable_mode) + dprintk("Hardware is in phy disable mode.\n"); + + return in_phy_disable_mode; +} + +void +rtl8168_wait_txrx_fifo_empty(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + int i; + + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + for (i = 0; i < 10; i++) { + udelay(100); + if (RTL_R32(tp, TxConfig) & BIT_11) + break; + } + + for (i = 0; i < 10; i++) { + udelay(100); + if ((RTL_R8(tp, MCUCmd_reg) & (Txfifo_empty | Rxfifo_empty)) == (Txfifo_empty | Rxfifo_empty)) + break; + } + + mdelay(1); + break; + } +} + +static void rtl8168_driver_start(struct rtl8168_private *tp) +{ + //change other device state to D0. + switch (tp->mcfg) { + case CFG_METHOD_23: + case CFG_METHOD_27: + case CFG_METHOD_28: + rtl8168_set_dash_other_fun_dev_aspm_clkreq(tp, 3, 1, 0x1E); + rtl8168_set_dash_other_fun_dev_state_change(tp, 3, 0x1E); + break; + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_set_dash_other_fun_dev_aspm_clkreq(tp, 3, 1, 0xFC); + rtl8168_set_dash_other_fun_dev_state_change(tp, 3, 0xFC); + break; + } + + if (!tp->DASH) + return; + + if (HW_DASH_SUPPORT_TYPE_2(tp) || HW_DASH_SUPPORT_TYPE_3(tp)) { + int timeout; + u32 tmp_value; + + rtl8168_ocp_write(tp, 0x180, 1, OOB_CMD_DRIVER_START); + tmp_value = rtl8168_ocp_read(tp, 0x30, 1); + tmp_value |= BIT_0; + rtl8168_ocp_write(tp, 0x30, 1, tmp_value); + + for (timeout = 0; timeout < 10; timeout++) { + mdelay(10); + if (rtl8168_ocp_read(tp, 0x124, 1) & BIT_0) + break; + } + } else { + int timeout; + u32 reg; + + if (tp->mcfg == CFG_METHOD_13) { + RTL_W8(tp, TwiCmdReg, RTL_R8(tp, TwiCmdReg) | ( BIT_7 )); + } + + rtl8168_oob_notify(tp, OOB_CMD_DRIVER_START); + + if (tp->mcfg == CFG_METHOD_13) + reg = 0xB8; + else + reg = 0x10; + + for (timeout = 0; timeout < 10; timeout++) { + mdelay(10); + if (rtl8168_ocp_read(tp, reg, 2) & BIT_11) + break; + } + } +} + +static void rtl8168_driver_stop(struct rtl8168_private *tp) +{ + if (!tp->DASH) + goto update_device_state; + + if (HW_DASH_SUPPORT_TYPE_2(tp) || HW_DASH_SUPPORT_TYPE_3(tp)) { + struct net_device *dev = tp->dev; + int timeout; + u32 tmp_value; + + rtl8168_dash2_disable_txrx(dev); + + rtl8168_ocp_write(tp, 0x180, 1, OOB_CMD_DRIVER_STOP); + tmp_value = rtl8168_ocp_read(tp, 0x30, 1); + tmp_value |= BIT_0; + rtl8168_ocp_write(tp, 0x30, 1, tmp_value); + + for (timeout = 0; timeout < 10; timeout++) { + mdelay(10); + if (!(rtl8168_ocp_read(tp, 0x124, 1) & BIT_0)) + break; + } + } else { + int timeout; + u32 reg; + + rtl8168_oob_notify(tp, OOB_CMD_DRIVER_STOP); + + if (tp->mcfg == CFG_METHOD_13) + reg = 0xB8; + else + reg = 0x10; + + for (timeout = 0; timeout < 10; timeout++) { + mdelay(10); + if ((rtl8168_ocp_read(tp, reg, 2) & BIT_11) == 0) + break; + } + + if (tp->mcfg == CFG_METHOD_13) { + RTL_W8(tp, TwiCmdReg, RTL_R8(tp, TwiCmdReg) & ~( BIT_7 )); + } + } + +update_device_state: + //change other device state to D3. + switch (tp->mcfg) { + case CFG_METHOD_23: + case CFG_METHOD_27: + case CFG_METHOD_28: + rtl8168_set_dash_other_fun_dev_state_change(tp, 3, 0x0E); + break; + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_set_dash_other_fun_dev_state_change(tp, 3, 0xFD); + break; + } +} + +#ifdef ENABLE_DASH_SUPPORT + +inline void +rtl8168_enable_dash2_interrupt(struct rtl8168_private *tp) +{ + if (!tp->DASH) return; + + if (HW_DASH_SUPPORT_TYPE_2(tp) || HW_DASH_SUPPORT_TYPE_3(tp)) { + RTL_CMAC_W8(tp, CMAC_IBIMR0, ( ISRIMR_DASH_TYPE2_ROK | ISRIMR_DASH_TYPE2_TOK | ISRIMR_DASH_TYPE2_TDU | ISRIMR_DASH_TYPE2_RDU | ISRIMR_DASH_TYPE2_RX_DISABLE_IDLE )); + } +} + +static inline void +rtl8168_disable_dash2_interrupt(struct rtl8168_private *tp) +{ + if (!tp->DASH) return; + + if (HW_DASH_SUPPORT_TYPE_2(tp) || HW_DASH_SUPPORT_TYPE_3(tp)) { + RTL_CMAC_W8(tp, CMAC_IBIMR0, 0); + } +} +#endif + +static inline void +rtl8168_enable_hw_interrupt(struct rtl8168_private *tp) +{ + RTL_W16(tp, IntrMask, tp->intr_mask); + +#ifdef ENABLE_DASH_SUPPORT + if (tp->DASH) + rtl8168_enable_dash2_interrupt(tp); +#endif +} + +static inline void +rtl8168_disable_hw_interrupt(struct rtl8168_private *tp) +{ + RTL_W16(tp, IntrMask, 0x0000); + +#ifdef ENABLE_DASH_SUPPORT + if (tp->DASH) + rtl8168_disable_dash2_interrupt(tp); +#endif +} + + +static inline void +rtl8168_switch_to_hw_interrupt(struct rtl8168_private *tp) +{ + RTL_W32(tp, TimeInt0, 0x0000); + + rtl8168_enable_hw_interrupt(tp); +} + +static inline void +rtl8168_switch_to_timer_interrupt(struct rtl8168_private *tp) +{ + if (tp->use_timer_interrrupt) { + RTL_W32(tp, TimeInt0, timer_count); + RTL_W32(tp, TCTR, timer_count); + RTL_W16(tp, IntrMask, tp->timer_intr_mask); + +#ifdef ENABLE_DASH_SUPPORT + if (tp->DASH) + rtl8168_enable_dash2_interrupt(tp); +#endif + } else { + rtl8168_switch_to_hw_interrupt(tp); + } +} + +static void +rtl8168_irq_mask_and_ack(struct rtl8168_private *tp) +{ + rtl8168_disable_hw_interrupt(tp); +#ifdef ENABLE_DASH_SUPPORT + if (tp->DASH) { + if (tp->dash_printer_enabled) { + RTL_W16(tp, IntrStatus, RTL_R16(tp, IntrStatus) & + ~(ISRIMR_DASH_INTR_EN | ISRIMR_DASH_INTR_CMAC_RESET)); + } else { + if (HW_DASH_SUPPORT_TYPE_2(tp) || HW_DASH_SUPPORT_TYPE_3(tp)) { + RTL_CMAC_W8(tp, CMAC_IBISR0, RTL_CMAC_R8(tp, CMAC_IBISR0)); + } + } + } else { + RTL_W16(tp, IntrStatus, RTL_R16(tp, IntrStatus)); + } +#else + RTL_W16(tp, IntrStatus, RTL_R16(tp, IntrStatus)); +#endif +} + +static void +rtl8168_nic_reset(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + int i; + + RTL_W32(tp, RxConfig, (RX_DMA_BURST << RxCfgDMAShift)); + + rtl8168_enable_rxdvgate(dev); + + switch (tp->mcfg) { + case CFG_METHOD_1: + case CFG_METHOD_2: + case CFG_METHOD_3: + mdelay(10); + break; + case CFG_METHOD_4: + case CFG_METHOD_5: + case CFG_METHOD_6: + case CFG_METHOD_7: + case CFG_METHOD_8: + case CFG_METHOD_9: + case CFG_METHOD_10: + case CFG_METHOD_14: + case CFG_METHOD_15: + RTL_W8(tp, ChipCmd, StopReq | CmdRxEnb | CmdTxEnb); + udelay(100); + break; + case CFG_METHOD_11: + case CFG_METHOD_12: + case CFG_METHOD_13: + for (i = 0; i < 2000; i++) { + if (!(RTL_R8(tp, TxPoll) & NPQ)) break; + udelay(100); + } + break; + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + mdelay(2); + break; + default: + mdelay(10); + break; + } + + rtl8168_wait_txrx_fifo_empty(dev); + + /* Soft reset the chip. */ + RTL_W8(tp, ChipCmd, CmdReset); + + /* Check that the chip has finished the reset. */ + for (i = 100; i > 0; i--) { + udelay(100); + if ((RTL_R8(tp, ChipCmd) & CmdReset) == 0) + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_11: + rtl8168_oob_mutex_lock(tp); + rtl8168_ocp_write(tp, 0x10, 2, rtl8168_ocp_read(tp, 0x010, 2)&~0x00004000); + rtl8168_oob_mutex_unlock(tp); + + rtl8168_oob_notify(tp, OOB_CMD_RESET); + + for (i = 0; i < 10; i++) { + mdelay(10); + if (rtl8168_ocp_read(tp, 0x010, 2)&0x00004000) + break; + } + + for (i = 0; i < 5; i++) { + if ( rtl8168_ocp_read(tp, 0x034, 1) == 0) + break; + } + break; + } +} + +static void +rtl8168_hw_clear_timer_int(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + RTL_W32(tp, TimeInt0, 0x0000); + + switch (tp->mcfg) { + case CFG_METHOD_4: + case CFG_METHOD_5: + case CFG_METHOD_6: + case CFG_METHOD_7: + case CFG_METHOD_8: + RTL_W32(tp, TimeInt1, 0x0000); + break; + case CFG_METHOD_9: + case CFG_METHOD_10: + case CFG_METHOD_11: + case CFG_METHOD_12: + case CFG_METHOD_13: + case CFG_METHOD_14: + case CFG_METHOD_15: + case CFG_METHOD_16: + case CFG_METHOD_17: + case CFG_METHOD_18: + case CFG_METHOD_19: + case CFG_METHOD_20: + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + RTL_W32(tp, TimeInt1, 0x0000); + RTL_W32(tp, TimeInt2, 0x0000); + RTL_W32(tp, TimeInt3, 0x0000); + break; + } +} + +static void +rtl8168_hw_reset(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + /* Disable interrupts */ + rtl8168_irq_mask_and_ack(tp); + + rtl8168_hw_clear_timer_int(dev); + + rtl8168_nic_reset(dev); +} + +static void rtl8168_mac_loopback_test(struct rtl8168_private *tp) +{ + struct pci_dev *pdev = tp->pci_dev; + struct net_device *dev = tp->dev; + struct sk_buff *skb, *rx_skb; + dma_addr_t mapping; + struct TxDesc *txd; + struct RxDesc *rxd; + void *tmpAddr; + u32 len, rx_len, rx_cmd = 0; + u16 type; + u8 pattern; + int i; + + if (tp->DASH) + return; + + pattern = 0x5A; + len = 60; + type = htons(ETH_P_IP); + txd = tp->TxDescArray; + rxd = tp->RxDescArray; + rx_skb = tp->Rx_skbuff[0]; + RTL_W32(tp, TxConfig, (RTL_R32(tp, TxConfig) & ~0x00060000) | 0x00020000); + + do { + skb = dev_alloc_skb(len + RTK_RX_ALIGN); + if (unlikely(!skb)) + dev_printk(KERN_NOTICE, tp_to_dev(tp), "-ENOMEM;\n"); + } while (unlikely(skb == NULL)); + skb_reserve(skb, RTK_RX_ALIGN); + + memcpy(skb_put(skb, dev->addr_len), dev->dev_addr, dev->addr_len); + memcpy(skb_put(skb, dev->addr_len), dev->dev_addr, dev->addr_len); + memcpy(skb_put(skb, sizeof(type)), &type, sizeof(type)); + tmpAddr = skb_put(skb, len - 14); + + mapping = dma_map_single(tp_to_dev(tp), skb->data, len, DMA_TO_DEVICE); + dma_sync_single_for_cpu(tp_to_dev(tp), le64_to_cpu(mapping), + len, DMA_TO_DEVICE); + txd->addr = cpu_to_le64(mapping); + txd->opts2 = 0; + while (1) { + memset(tmpAddr, pattern++, len - 14); + pci_dma_sync_single_for_device(tp->pci_dev, + le64_to_cpu(mapping), + len, DMA_TO_DEVICE); + txd->opts1 = cpu_to_le32(DescOwn | FirstFrag | LastFrag | len); + + RTL_W32(tp, RxConfig, RTL_R32(tp, RxConfig) | AcceptMyPhys); + + smp_wmb(); + RTL_W8(tp, TxPoll, NPQ); /* set polling bit */ + + for (i = 0; i < 50; i++) { + udelay(200); + rx_cmd = le32_to_cpu(rxd->opts1); + if ((rx_cmd & DescOwn) == 0) + break; + } + + RTL_W32(tp, RxConfig, RTL_R32(tp, RxConfig) & ~(AcceptErr | AcceptRunt | AcceptBroadcast | AcceptMulticast | AcceptMyPhys | AcceptAllPhys)); + + rx_len = rx_cmd & 0x3FFF; + rx_len -= 4; + rxd->opts1 = cpu_to_le32(DescOwn | tp->rx_buf_sz); + + dma_sync_single_for_cpu(tp_to_dev(tp), le64_to_cpu(mapping), len, DMA_TO_DEVICE); + + if (rx_len == len) { + dma_sync_single_for_cpu(tp_to_dev(tp), le64_to_cpu(rxd->addr), tp->rx_buf_sz, DMA_FROM_DEVICE); + i = memcmp(skb->data, rx_skb->data, rx_len); + pci_dma_sync_single_for_device(tp->pci_dev, le64_to_cpu(rxd->addr), tp->rx_buf_sz, DMA_FROM_DEVICE); + if (i == 0) { +// dev_printk(KERN_INFO, tp_to_dev(tp), "loopback test finished\n",rx_len,len); + break; + } + } + + rtl8168_hw_reset(dev); + rtl8168_disable_rxdvgate(dev); + RTL_W8(tp, ChipCmd, CmdTxEnb | CmdRxEnb); + } + tp->dirty_tx++; + tp->dirty_rx++; + tp->cur_tx++; + tp->cur_rx++; + dma_unmap_single(&pdev->dev, le64_to_cpu(mapping), + len, DMA_TO_DEVICE); + RTL_W32(tp, TxConfig, RTL_R32(tp, TxConfig) & ~0x00060000); + dev_kfree_skb_any(skb); + RTL_W16(tp, IntrStatus, 0xFFBF); +} + +static unsigned int +rtl8168_xmii_reset_pending(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned int retval; + + rtl8168_mdio_write(tp, 0x1f, 0x0000); + retval = rtl8168_mdio_read(tp, MII_BMCR) & BMCR_RESET; + + return retval; +} + +static unsigned int +rtl8168_xmii_link_ok(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned int retval; + + retval = (RTL_R8(tp, PHYstatus) & LinkStatus) ? 1 : 0; + + return retval; +} + +static int +rtl8168_wait_phy_reset_complete(struct rtl8168_private *tp) +{ + int i, val; + + for (i = 0; i < 2500; i++) { + val = rtl8168_mdio_read(tp, MII_BMCR) & BMCR_RESET; + if (!val) + return 0; + + mdelay(1); + } + + return -1; +} + +static void +rtl8168_xmii_reset_enable(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + if (rtl8168_is_in_phy_disable_mode(dev)) + return; + + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, MII_ADVERTISE, rtl8168_mdio_read(tp, MII_ADVERTISE) & + ~(ADVERTISE_10HALF | ADVERTISE_10FULL | + ADVERTISE_100HALF | ADVERTISE_100FULL)); + rtl8168_mdio_write(tp, MII_CTRL1000, rtl8168_mdio_read(tp, MII_CTRL1000) & + ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL)); + rtl8168_mdio_write(tp, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); + + if (rtl8168_wait_phy_reset_complete(tp) == 0) return; + + if (netif_msg_link(tp)) + printk(KERN_ERR "%s: PHY reset failed.\n", dev->name); +} + +static void +rtl8168dp_10mbps_gphy_para(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u8 status = RTL_R8(tp, PHYstatus); + + if ((status & LinkStatus) && (status & _10bps)) { + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x10, 0x04EE); + } else { + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x10, 0x01EE); + } +} + +void rtl8168_init_ring_indexes(struct rtl8168_private *tp) +{ + tp->dirty_tx = 0; + tp->dirty_rx = 0; + tp->cur_tx = 0; + tp->cur_rx = 0; +} + +static void +rtl8168_issue_offset_99_event(struct rtl8168_private *tp) +{ + u32 csi_tmp; + + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_27: + case CFG_METHOD_28: + if (tp->mcfg == CFG_METHOD_24 || tp->mcfg == CFG_METHOD_25 || + tp->mcfg == CFG_METHOD_27 || tp->mcfg == CFG_METHOD_28) { + rtl8168_eri_write(tp, 0x3FC, 4, 0x00000000, ERIAR_ExGMAC); + } else { + rtl8168_eri_write(tp, 0x3FC, 4, 0x083C083C, ERIAR_ExGMAC); + } + csi_tmp = rtl8168_eri_read(tp, 0x3F8, 1, ERIAR_ExGMAC); + csi_tmp |= BIT_0; + rtl8168_eri_write(tp, 0x3F8, 1, csi_tmp, ERIAR_ExGMAC); + break; + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + csi_tmp = rtl8168_eri_read(tp, 0x1EA, 1, ERIAR_ExGMAC); + csi_tmp |= BIT_0; + rtl8168_eri_write(tp, 0x1EA, 1, csi_tmp, ERIAR_ExGMAC); + break; + } +} + +static void +rtl8168_enable_cfg9346_write(struct rtl8168_private *tp) +{ + RTL_W8(tp, Cfg9346, RTL_R8(tp, Cfg9346) | Cfg9346_Unlock); +} + +static void +rtl8168_disable_cfg9346_write(struct rtl8168_private *tp) +{ + RTL_W8(tp, Cfg9346, RTL_R8(tp, Cfg9346) & ~Cfg9346_Unlock); +} + +static void +rtl8168_enable_exit_l1_mask(struct rtl8168_private *tp) +{ + u32 csi_tmp; + + switch (tp->mcfg) { + case CFG_METHOD_16: + case CFG_METHOD_17: + case CFG_METHOD_18: + case CFG_METHOD_19: + csi_tmp = rtl8168_eri_read(tp, 0xD4, 4, ERIAR_ExGMAC); + csi_tmp |= (BIT_8 | BIT_9 | BIT_10 | BIT_11 | BIT_12); + rtl8168_eri_write(tp, 0xD4, 4, csi_tmp, ERIAR_ExGMAC); + break; + case CFG_METHOD_20: + csi_tmp = rtl8168_eri_read(tp, 0xD4, 4, ERIAR_ExGMAC); + csi_tmp |= (BIT_10 | BIT_11); + rtl8168_eri_write(tp, 0xD4, 4, csi_tmp, ERIAR_ExGMAC); + break; + case CFG_METHOD_21 ... CFG_METHOD_33: + csi_tmp = rtl8168_eri_read(tp, 0xD4, 4, ERIAR_ExGMAC); + csi_tmp |= (BIT_7 | BIT_8 | BIT_9 | BIT_10 | BIT_11 | BIT_12); + rtl8168_eri_write(tp, 0xD4, 4, csi_tmp, ERIAR_ExGMAC); + break; + } +} + +static void +rtl8168_disable_exit_l1_mask(struct rtl8168_private *tp) +{ + u32 csi_tmp; + + switch (tp->mcfg) { + case CFG_METHOD_16: + case CFG_METHOD_17: + case CFG_METHOD_18: + case CFG_METHOD_19: + csi_tmp = rtl8168_eri_read(tp, 0xD4, 4, ERIAR_ExGMAC); + csi_tmp &= ~(BIT_8 | BIT_9 | BIT_10 | BIT_11 | BIT_12); + rtl8168_eri_write(tp, 0xD4, 4, csi_tmp, ERIAR_ExGMAC); + break; + case CFG_METHOD_20: + csi_tmp = rtl8168_eri_read(tp, 0xD4, 4, ERIAR_ExGMAC); + csi_tmp &= ~(BIT_10 | BIT_11); + rtl8168_eri_write(tp, 0xD4, 4, csi_tmp, ERIAR_ExGMAC); + break; + case CFG_METHOD_21 ... CFG_METHOD_33: + csi_tmp = rtl8168_eri_read(tp, 0xD4, 4, ERIAR_ExGMAC); + csi_tmp &= ~(BIT_7 | BIT_8 | BIT_9 | BIT_10 | BIT_11 | BIT_12); + rtl8168_eri_write(tp, 0xD4, 4, csi_tmp, ERIAR_ExGMAC); + break; + } +} + +static void +rtl8168_hw_aspm_clkreq_enable(struct rtl8168_private *tp, bool enable) +{ + if (!tp->HwSuppAspmClkIntrLock) return; + + if (enable && aspm) { + RTL_W8(tp, Config5, RTL_R8(tp, Config5) | ASPM_en); + RTL_W8(tp, Config2, RTL_R8(tp, Config2) | ClkReqEn); + } else { + RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~ClkReqEn); + RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~ASPM_en); + } + + udelay(10); +} + +#ifdef ENABLE_DASH_SUPPORT +static void +NICChkTypeEnableDashInterrupt(struct rtl8168_private *tp) +{ + if (tp->DASH) { + // + // even disconnected, enable 3 dash interrupt mask bits for in-band/out-band communication + // + if (HW_DASH_SUPPORT_TYPE_2(tp) || HW_DASH_SUPPORT_TYPE_3(tp)) { + rtl8168_enable_dash2_interrupt(tp); + RTL_W16(tp, IntrMask, (ISRIMR_DASH_INTR_EN | ISRIMR_DASH_INTR_CMAC_RESET)); + } else { + RTL_W16(tp, IntrMask, (ISRIMR_DP_DASH_OK | ISRIMR_DP_HOST_OK | ISRIMR_DP_REQSYS_OK)); + } + } +} +#endif + +static void +rtl8168_check_link_status(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + int link_status_on; + +#ifdef ENABLE_FIBER_SUPPORT + rtl8168_check_fiber_link_status(dev); +#endif //ENABLE_FIBER_SUPPORT + + link_status_on = tp->link_ok(dev); + + if (tp->mcfg == CFG_METHOD_11) + rtl8168dp_10mbps_gphy_para(dev); + + if (netif_carrier_ok(dev) != link_status_on) { + if (link_status_on) { + rtl8168_hw_config(dev); + + if (tp->mcfg == CFG_METHOD_18 || tp->mcfg == CFG_METHOD_19 || tp->mcfg == CFG_METHOD_20) { + if (RTL_R8(tp, PHYstatus) & _1000bpsF) { + rtl8168_eri_write(tp, 0x1bc, 4, 0x00000011, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0x1dc, 4, 0x0000001f, ERIAR_ExGMAC); + } else if (RTL_R8(tp, PHYstatus) & _100bps) { + rtl8168_eri_write(tp, 0x1bc, 4, 0x0000001f, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0x1dc, 4, 0x0000001f, ERIAR_ExGMAC); + } else { + rtl8168_eri_write(tp, 0x1bc, 4, 0x0000001f, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0x1dc, 4, 0x0000002d, ERIAR_ExGMAC); + } + } else if ((tp->mcfg == CFG_METHOD_16 || tp->mcfg == CFG_METHOD_17) && netif_running(dev)) { + if (tp->mcfg == CFG_METHOD_16 && (RTL_R8(tp, PHYstatus) & _10bps)) { + RTL_W32(tp, RxConfig, RTL_R32(tp, RxConfig) | AcceptAllPhys); + } else if (tp->mcfg == CFG_METHOD_17) { + if (RTL_R8(tp, PHYstatus) & _1000bpsF) { + rtl8168_eri_write(tp, 0x1bc, 4, 0x00000011, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0x1dc, 4, 0x00000005, ERIAR_ExGMAC); + } else if (RTL_R8(tp, PHYstatus) & _100bps) { + rtl8168_eri_write(tp, 0x1bc, 4, 0x0000001f, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0x1dc, 4, 0x00000005, ERIAR_ExGMAC); + } else { + rtl8168_eri_write(tp, 0x1bc, 4, 0x0000001f, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0x1dc, 4, 0x0000003f, ERIAR_ExGMAC); + } + } + } else if ((tp->mcfg == CFG_METHOD_14 || tp->mcfg == CFG_METHOD_15) && tp->eee_enabled == 1) { + /*Full -Duplex mode*/ + if (RTL_R8(tp, PHYstatus)&FullDup) { + rtl8168_mdio_write(tp, 0x1F, 0x0006); + rtl8168_mdio_write(tp, 0x00, 0x5a30); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + if (RTL_R8(tp, PHYstatus) & (_10bps | _100bps)) + RTL_W32(tp, TxConfig, (RTL_R32(tp, TxConfig) & ~BIT_19) | BIT_25); + + } else { + rtl8168_mdio_write(tp, 0x1F, 0x0006); + rtl8168_mdio_write(tp, 0x00, 0x5a00); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + if (RTL_R8(tp, PHYstatus) & (_10bps | _100bps)) + RTL_W32(tp, TxConfig, (RTL_R32(tp, TxConfig) & ~BIT_19) | (InterFrameGap << TxInterFrameGapShift)); + } + } else if ((tp->mcfg == CFG_METHOD_21 || tp->mcfg == CFG_METHOD_22 || + tp->mcfg == CFG_METHOD_23 || tp->mcfg == CFG_METHOD_24 || + tp->mcfg == CFG_METHOD_25 || tp->mcfg == CFG_METHOD_26 || + tp->mcfg == CFG_METHOD_27 || tp->mcfg == CFG_METHOD_28 || + tp->mcfg == CFG_METHOD_29 || tp->mcfg == CFG_METHOD_30 || + tp->mcfg == CFG_METHOD_31 || tp->mcfg == CFG_METHOD_32 || + tp->mcfg == CFG_METHOD_33) && netif_running(dev)) { + if (RTL_R8(tp, PHYstatus)&FullDup) + RTL_W32(tp, TxConfig, (RTL_R32(tp, TxConfig) | (BIT_24 | BIT_25)) & ~BIT_19); + else + RTL_W32(tp, TxConfig, (RTL_R32(tp, TxConfig) | BIT_25) & ~(BIT_19 | BIT_24)); + } + + if (tp->mcfg == CFG_METHOD_21 || tp->mcfg == CFG_METHOD_22 || + tp->mcfg == CFG_METHOD_27 || tp->mcfg == CFG_METHOD_28 || + tp->mcfg == CFG_METHOD_31 || tp->mcfg == CFG_METHOD_32 || + tp->mcfg == CFG_METHOD_33) { + /*half mode*/ + if (!(RTL_R8(tp, PHYstatus)&FullDup)) { + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, MII_ADVERTISE, rtl8168_mdio_read(tp, MII_ADVERTISE)&~(ADVERTISE_PAUSE_CAP|ADVERTISE_PAUSE_ASYM)); + } + } + + if ((tp->mcfg == CFG_METHOD_31 || tp->mcfg == CFG_METHOD_32 || + tp->mcfg == CFG_METHOD_33) && (RTL_R8(tp, PHYstatus) & _10bps)) { + u32 csi_tmp; + + csi_tmp = rtl8168_eri_read(tp, 0x1D0, 1, ERIAR_ExGMAC); + csi_tmp |= BIT_1; + rtl8168_eri_write(tp, 0x1D0, 1, csi_tmp, ERIAR_ExGMAC); + } + + rtl8168_hw_start(dev); + + netif_carrier_on(dev); + + netif_wake_queue(dev); + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + tp->phy_reg_aner = rtl8168_mdio_read(tp, MII_EXPANSION); + tp->phy_reg_anlpar = rtl8168_mdio_read(tp, MII_LPA); + tp->phy_reg_gbsr = rtl8168_mdio_read(tp, MII_STAT1000); + + if (netif_msg_ifup(tp)) + printk(KERN_INFO PFX "%s: link up\n", dev->name); + } else { + if (netif_msg_ifdown(tp)) + printk(KERN_INFO PFX "%s: link down\n", dev->name); + + tp->phy_reg_aner = 0; + tp->phy_reg_anlpar = 0; + tp->phy_reg_gbsr = 0; + + netif_stop_queue(dev); + + netif_carrier_off(dev); + + rtl8168_hw_reset(dev); + + rtl8168_tx_clear(tp); + + rtl8168_rx_clear(tp); + + rtl8168_init_ring(dev); + + if (dynamic_aspm) { + rtl8168_enable_cfg9346_write(tp); + rtl8168_hw_aspm_clkreq_enable(tp, true); + rtl8168_disable_cfg9346_write(tp); + } + + rtl8168_set_speed(dev, tp->autoneg, tp->speed, tp->duplex, tp->advertising); + + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_27: + case CFG_METHOD_28: + if (tp->org_pci_offset_99 & BIT_2) + tp->issue_offset_99_event = TRUE; + break; + } + +#ifdef ENABLE_DASH_SUPPORT + if (tp->DASH) { + NICChkTypeEnableDashInterrupt(tp); + } +#endif + } + } + + if (!link_status_on) { + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_27: + case CFG_METHOD_28: + if (tp->issue_offset_99_event) { + if (!(RTL_R8(tp, PHYstatus) & PowerSaveStatus)) { + tp->issue_offset_99_event = FALSE; + rtl8168_issue_offset_99_event(tp); + } + } + break; + } + } else { + if (dynamic_aspm) { + bool enable_hw_aspm_clkreq = true; + if (tp->dynamic_aspm_packet_count > dynamic_aspm_packet_threshold) + enable_hw_aspm_clkreq = false; + + rtl8168_enable_cfg9346_write(tp); + rtl8168_hw_aspm_clkreq_enable(tp, enable_hw_aspm_clkreq); + rtl8168_disable_cfg9346_write(tp); + } + tp->dynamic_aspm_packet_count = 0; + } +} + +static void +rtl8168_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 +rtl8168_enable_ocp_phy_power_saving(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u16 val; + + if (tp->mcfg == CFG_METHOD_25 || tp->mcfg == CFG_METHOD_26 || + tp->mcfg == CFG_METHOD_27 || tp->mcfg == CFG_METHOD_28 || + tp->mcfg == CFG_METHOD_29 || tp->mcfg == CFG_METHOD_30 || + tp->mcfg == CFG_METHOD_31 || tp->mcfg == CFG_METHOD_32 || + tp->mcfg == CFG_METHOD_33) { + val = rtl8168_mdio_read_phy_ocp(tp, 0x0C41, 0x13); + if (val != 0x0050) { + rtl8168_set_phy_mcu_patch_request(tp); + rtl8168_mdio_write_phy_ocp(tp, 0x0C41, 0x13, 0x0000); + rtl8168_mdio_write_phy_ocp(tp, 0x0C41, 0x13, 0x0050); + rtl8168_clear_phy_mcu_patch_request(tp); + } + } +} + +static void +rtl8168_disable_ocp_phy_power_saving(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u16 val; + + if (tp->mcfg == CFG_METHOD_25 || tp->mcfg == CFG_METHOD_26 || + tp->mcfg == CFG_METHOD_27 || tp->mcfg == CFG_METHOD_28 || + tp->mcfg == CFG_METHOD_29 || tp->mcfg == CFG_METHOD_30 || + tp->mcfg == CFG_METHOD_31 || tp->mcfg == CFG_METHOD_32 || + tp->mcfg == CFG_METHOD_33) { + val = rtl8168_mdio_read_phy_ocp(tp, 0x0C41, 0x13); + if (val != 0x0500) { + rtl8168_set_phy_mcu_patch_request(tp); + rtl8168_mdio_write_phy_ocp(tp, 0x0C41, 0x13, 0x0000); + rtl8168_mdio_write_phy_ocp(tp, 0x0C41, 0x13, 0x0500); + rtl8168_clear_phy_mcu_patch_request(tp); + } + } +} + +void +rtl8168_wait_ll_share_fifo_ready(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + int i; + + for (i = 0; i < 10; i++) { + udelay(100); + if (RTL_R16(tp, 0xD2) & BIT_9) + break; + } +} + +static void +rtl8168_disable_pci_offset_99(struct rtl8168_private *tp) +{ + u32 csi_tmp; + + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + csi_tmp = rtl8168_eri_read(tp, 0x3F2, 2, ERIAR_ExGMAC); + csi_tmp &= ~(BIT_0 | BIT_1); + rtl8168_eri_write(tp, 0x3F2, 2, csi_tmp, ERIAR_ExGMAC); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_26: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_csi_fun0_write_byte(tp, 0x99, 0x00); + break; + } +} + +static void +rtl8168_enable_pci_offset_99(struct rtl8168_private *tp) +{ + u32 csi_tmp; + + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_26: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_csi_fun0_write_byte(tp, 0x99, tp->org_pci_offset_99); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + csi_tmp = rtl8168_eri_read(tp, 0x3F2, 2, ERIAR_ExGMAC); + csi_tmp &= ~(BIT_0 | BIT_1); + if (tp->org_pci_offset_99 & (BIT_5 | BIT_6)) + csi_tmp |= BIT_1; + if (tp->org_pci_offset_99 & BIT_2) + csi_tmp |= BIT_0; + rtl8168_eri_write(tp, 0x3F2, 2, csi_tmp, ERIAR_ExGMAC); + break; + } +} + +static void +rtl8168_init_pci_offset_99(struct rtl8168_private *tp) +{ + u32 csi_tmp; + + switch (tp->mcfg) { + case CFG_METHOD_26: + if (tp->org_pci_offset_99 & BIT_2) { + csi_tmp = rtl8168_eri_read(tp, 0x5C2, 1, ERIAR_ExGMAC); + csi_tmp &= ~BIT_1; + rtl8168_eri_write(tp, 0x5C2, 1, csi_tmp, ERIAR_ExGMAC); + } + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + csi_tmp = rtl8168_eri_read(tp, 0x3F2, 2, ERIAR_ExGMAC); + csi_tmp &= ~( BIT_8 | BIT_9 | BIT_10 | BIT_11 | BIT_12 | BIT_13 | BIT_14 | BIT_15 ); + csi_tmp |= ( BIT_9 | BIT_10 | BIT_13 | BIT_14 | BIT_15 ); + rtl8168_eri_write(tp, 0x3F2, 2, csi_tmp, ERIAR_ExGMAC); + csi_tmp = rtl8168_eri_read(tp, 0x3F5, 1, ERIAR_ExGMAC); + csi_tmp |= BIT_6 | BIT_7; + rtl8168_eri_write(tp, 0x3F5, 1, csi_tmp, ERIAR_ExGMAC); + rtl8168_mac_ocp_write(tp, 0xE02C, 0x1880); + rtl8168_mac_ocp_write(tp, 0xE02E, 0x4880); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_26: + rtl8168_eri_write(tp, 0x5C0, 1, 0xFA, ERIAR_ExGMAC); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_26: + if (tp->org_pci_offset_99 & BIT_2) { + csi_tmp = rtl8168_eri_read(tp, 0x5C8, 1, ERIAR_ExGMAC); + csi_tmp |= BIT_0; + rtl8168_eri_write(tp, 0x5C8, 1, csi_tmp, ERIAR_ExGMAC); + } + break; + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + if (tp->org_pci_offset_99 & BIT_2) + rtl8168_mac_ocp_write(tp, 0xE0A2, rtl8168_mac_ocp_read(tp, 0xE0A2) | BIT_0); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_23: + rtl8168_eri_write(tp, 0x2E8, 2, 0x883C, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0x2EA, 2, 0x8C12, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0x2EC, 2, 0x9003, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0x2E2, 2, 0x883C, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0x2E4, 2, 0x8C12, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0x2E6, 2, 0x9003, ERIAR_ExGMAC); + break; + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_eri_write(tp, 0x2E8, 2, 0x9003, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0x2EA, 2, 0x9003, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0x2EC, 2, 0x9003, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0x2E2, 2, 0x883C, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0x2E4, 2, 0x8C12, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0x2E6, 2, 0x9003, ERIAR_ExGMAC); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + csi_tmp = rtl8168_eri_read(tp, 0x3FA, 2, ERIAR_ExGMAC); + csi_tmp |= BIT_14; + rtl8168_eri_write(tp, 0x3FA, 2, csi_tmp, ERIAR_ExGMAC); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_26: + case CFG_METHOD_29: + case CFG_METHOD_30: + if (tp->org_pci_offset_99 & BIT_2) + RTL_W8(tp, 0xB6, RTL_R8(tp, 0xB6) | BIT_0); + break; + } + + rtl8168_enable_pci_offset_99(tp); +} + +static void +rtl8168_disable_pci_offset_180(struct rtl8168_private *tp) +{ + u32 csi_tmp; + + switch (tp->mcfg) { + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + csi_tmp = rtl8168_eri_read(tp, 0x1E2, 1, ERIAR_ExGMAC); + csi_tmp &= ~BIT_2; + rtl8168_eri_write(tp, 0x1E2, 1, csi_tmp, ERIAR_ExGMAC); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_26: + rtl8168_eri_write(tp, 0x1E9, 1, 0x0A, ERIAR_ExGMAC); + break; + } +} + +static void +rtl8168_enable_pci_offset_180(struct rtl8168_private *tp) +{ + u32 csi_tmp; + + switch (tp->mcfg) { + case CFG_METHOD_25: + case CFG_METHOD_28: + csi_tmp = rtl8168_eri_read(tp, 0x1E8, 4, ERIAR_ExGMAC); + csi_tmp &= ~(0x0000FF00); + csi_tmp |= (0x00006400); + rtl8168_eri_write(tp, 0x1E8, 4, csi_tmp, ERIAR_ExGMAC); + + csi_tmp = rtl8168_eri_read(tp, 0x1E4, 4, ERIAR_ExGMAC); + csi_tmp &= ~(0x0000FF00); + rtl8168_eri_write(tp, 0x1E4, 4, csi_tmp, ERIAR_ExGMAC); + break; + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + csi_tmp = rtl8168_eri_read(tp, 0x1E8, 4, ERIAR_ExGMAC); + csi_tmp &= ~(0x0000FFF0); + csi_tmp |= (0x00000640); + rtl8168_eri_write(tp, 0x1E8, 4, csi_tmp, ERIAR_ExGMAC); + + csi_tmp = rtl8168_eri_read(tp, 0x1E4, 4, ERIAR_ExGMAC); + csi_tmp &= ~(0x0000FF00); + rtl8168_eri_write(tp, 0x1E4, 4, csi_tmp, ERIAR_ExGMAC); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + csi_tmp = rtl8168_eri_read(tp, 0x1E2, 1, ERIAR_ExGMAC); + csi_tmp |= BIT_2; + rtl8168_eri_write(tp, 0x1E2, 1, csi_tmp, ERIAR_ExGMAC); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_26: + rtl8168_eri_write(tp, 0x1E9, 1, 0x64, ERIAR_ExGMAC); + break; + } + + rtl8168_mac_ocp_write(tp, 0xE094, 0x0000); +} + +static void +rtl8168_init_pci_offset_180(struct rtl8168_private *tp) +{ + if (tp->org_pci_offset_180 & (BIT_0|BIT_1)) + rtl8168_enable_pci_offset_180(tp); + else + rtl8168_disable_pci_offset_180(tp); +} + +static void +rtl8168_set_pci_99_180_exit_driver_para(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_issue_offset_99_event(tp); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_disable_pci_offset_99(tp); + break; + } + switch (tp->mcfg) { + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_disable_pci_offset_180(tp); + break; + } +} + +static void +rtl8168_hw_d3_para(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + RTL_W16(tp, RxMaxSize, RX_BUF_SIZE); + + if (tp->HwSuppAspmClkIntrLock) { + RTL_W8(tp, 0xF1, RTL_R8(tp, 0xF1) & ~BIT_7); + rtl8168_enable_cfg9346_write(tp); + rtl8168_hw_aspm_clkreq_enable(tp, false); + rtl8168_disable_cfg9346_write(tp); + } + + rtl8168_disable_exit_l1_mask(tp); + +#ifdef ENABLE_REALWOW_SUPPORT + rtl8168_set_realwow_d3_para(dev); +#endif + + if (tp->mcfg == CFG_METHOD_18 || tp->mcfg == CFG_METHOD_19 || tp->mcfg == CFG_METHOD_20) { + rtl8168_eri_write(tp, 0x1bc, 4, 0x0000001f, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0x1dc, 4, 0x0000002d, ERIAR_ExGMAC); + } else if (tp->mcfg == CFG_METHOD_16) { + rtl8168_eri_write(tp, 0x1bc, 4, 0x0000001f, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0x1dc, 4, 0x0000003f, ERIAR_ExGMAC); + } + + if (tp->mcfg == CFG_METHOD_21 || tp->mcfg == CFG_METHOD_22 || + tp->mcfg == CFG_METHOD_23 || tp->mcfg == CFG_METHOD_24 || + tp->mcfg == CFG_METHOD_25 || tp->mcfg == CFG_METHOD_26 || + tp->mcfg == CFG_METHOD_27 || tp->mcfg == CFG_METHOD_28 || + tp->mcfg == CFG_METHOD_31 || tp->mcfg == CFG_METHOD_32 || + tp->mcfg == CFG_METHOD_33) { + rtl8168_eri_write(tp, 0x2F8, 2, 0x0064, ERIAR_ExGMAC); + } + + if (tp->bios_setting & BIT_28) { + if (tp->mcfg == CFG_METHOD_18 || tp->mcfg == CFG_METHOD_19 || + tp->mcfg == CFG_METHOD_20) { + u32 gphy_val; + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x04, 0x0061); + rtl8168_mdio_write(tp, 0x09, 0x0000); + rtl8168_mdio_write(tp, 0x00, 0x9200); + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B80); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val &= ~BIT_7; + rtl8168_mdio_write(tp, 0x06, gphy_val); + mdelay(1); + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x002C); + gphy_val = rtl8168_mdio_read(tp, 0x16); + gphy_val &= ~BIT_10; + rtl8168_mdio_write(tp, 0x16, gphy_val); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } + } + + rtl8168_set_pci_99_180_exit_driver_para(dev); + + /*disable ocp phy power saving*/ + if (tp->mcfg == CFG_METHOD_25 || tp->mcfg == CFG_METHOD_26 || + tp->mcfg == CFG_METHOD_27 || tp->mcfg == CFG_METHOD_28 || + tp->mcfg == CFG_METHOD_29 || tp->mcfg == CFG_METHOD_30 || + tp->mcfg == CFG_METHOD_31 || tp->mcfg == CFG_METHOD_32 || + tp->mcfg == CFG_METHOD_33) + if (!tp->dash_printer_enabled) + rtl8168_disable_ocp_phy_power_saving(dev); + + rtl8168_disable_rxdvgate(dev); +} + +static void +rtl8168_enable_magic_packet(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u32 csi_tmp; + + switch (tp->HwSuppMagicPktVer) { + case WAKEUP_MAGIC_PACKET_V1: + rtl8168_enable_cfg9346_write(tp); + RTL_W8(tp, Config3, RTL_R8(tp, Config3) | MagicPacket); + rtl8168_disable_cfg9346_write(tp); + break; + case WAKEUP_MAGIC_PACKET_V2: + csi_tmp = rtl8168_eri_read(tp, 0xDE, 1, ERIAR_ExGMAC); + csi_tmp |= BIT_0; + rtl8168_eri_write(tp, 0xDE, 1, csi_tmp, ERIAR_ExGMAC); + break; + } +} +static void +rtl8168_disable_magic_packet(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u32 csi_tmp; + + switch (tp->HwSuppMagicPktVer) { + case WAKEUP_MAGIC_PACKET_V1: + rtl8168_enable_cfg9346_write(tp); + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~MagicPacket); + rtl8168_disable_cfg9346_write(tp); + break; + case WAKEUP_MAGIC_PACKET_V2: + csi_tmp = rtl8168_eri_read(tp, 0xDE, 1, ERIAR_ExGMAC); + csi_tmp &= ~BIT_0; + rtl8168_eri_write(tp, 0xDE, 1, csi_tmp, ERIAR_ExGMAC); + break; + } +} + +#define WAKE_ANY (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST) + +static void +rtl8168_get_hw_wol(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u8 options; + u32 csi_tmp; + unsigned long flags; + + + spin_lock_irqsave(&tp->lock, flags); + + tp->wol_opts = 0; + options = RTL_R8(tp, Config1); + if (!(options & PMEnable)) + goto out_unlock; + + options = RTL_R8(tp, Config3); + if (options & LinkUp) + tp->wol_opts |= WAKE_PHY; + + switch (tp->HwSuppMagicPktVer) { + case WAKEUP_MAGIC_PACKET_V2: + csi_tmp = rtl8168_eri_read(tp, 0xDE, 1, ERIAR_ExGMAC); + if (csi_tmp & BIT_0) + tp->wol_opts |= WAKE_MAGIC; + break; + default: + if (options & MagicPacket) + tp->wol_opts |= WAKE_MAGIC; + break; + } + + options = RTL_R8(tp, Config5); + if (options & UWF) + tp->wol_opts |= WAKE_UCAST; + if (options & BWF) + tp->wol_opts |= WAKE_BCAST; + if (options & MWF) + tp->wol_opts |= WAKE_MCAST; + +out_unlock: + tp->wol_enabled = (tp->wol_opts || tp->dash_printer_enabled) ? WOL_ENABLED : WOL_DISABLED; + + spin_unlock_irqrestore(&tp->lock, flags); +} + +static void +rtl8168_set_hw_wol(struct net_device *dev, u32 wolopts) +{ + struct rtl8168_private *tp = netdev_priv(dev); + int i,tmp; + static 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 }, + }; + + switch (tp->HwSuppMagicPktVer) { + case WAKEUP_MAGIC_PACKET_V2: + tmp = ARRAY_SIZE(cfg) - 1; + + if (wolopts & WAKE_MAGIC) + rtl8168_enable_magic_packet(dev); + else + rtl8168_disable_magic_packet(dev); + break; + default: + tmp = ARRAY_SIZE(cfg); + break; + } + + rtl8168_enable_cfg9346_write(tp); + + for (i = 0; i < tmp; i++) { + u8 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); + } + + if (tp->dash_printer_enabled) + RTL_W8(tp, Config5, RTL_R8(tp, Config5) | LanWake); + + rtl8168_disable_cfg9346_write(tp); +} + +static void +rtl8168_phy_restart_nway(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + if (rtl8168_is_in_phy_disable_mode(dev)) return; + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, MII_BMCR, BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART); +} + +static void +rtl8168_phy_setup_force_mode(struct net_device *dev, u32 speed, u8 duplex) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u16 bmcr_true_force = 0; + + if (rtl8168_is_in_phy_disable_mode(dev)) return; + + 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; + } + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, MII_BMCR, bmcr_true_force); +} + +static void +rtl8168_set_pci_pme(struct rtl8168_private *tp, int set) +{ + struct pci_dev *pdev = tp->pci_dev; + u16 pmc; + + if (!pdev->pm_cap) + return; + + pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &pmc); + pmc |= PCI_PM_CTRL_PME_STATUS; + if (set) + pmc |= PCI_PM_CTRL_PME_ENABLE; + else + pmc &= ~PCI_PM_CTRL_PME_ENABLE; + pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, pmc); +} + +static void +rtl8168_set_wol_link_speed(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + int auto_nego; + int giga_ctrl; + u32 adv; + u16 anlpar; + u16 gbsr; + u16 aner; + + if (tp->autoneg != AUTONEG_ENABLE) + goto exit; + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + auto_nego = rtl8168_mdio_read(tp, MII_ADVERTISE); + auto_nego &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL + | ADVERTISE_100HALF | ADVERTISE_100FULL); + + giga_ctrl = rtl8168_mdio_read(tp, MII_CTRL1000); + giga_ctrl &= ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL); + + aner = anlpar = gbsr = 0; + if (tp->link_ok(dev)) { + aner = rtl8168_mdio_read(tp, MII_EXPANSION); + anlpar = rtl8168_mdio_read(tp, MII_LPA); + gbsr = rtl8168_mdio_read(tp, MII_STAT1000); + } else { + if (netif_running(dev)) { + aner = tp->phy_reg_aner; + anlpar = tp->phy_reg_anlpar; + gbsr = tp->phy_reg_gbsr; + } + } + + if ((aner | anlpar | gbsr) == 0) { + int auto_nego_tmp = 0; + adv = tp->advertising; + if ((adv & ADVERTISED_10baseT_Half) && (anlpar & LPA_10HALF)) + auto_nego_tmp |= ADVERTISE_10HALF; + if ((adv & ADVERTISED_10baseT_Full) && (anlpar & LPA_10FULL)) + auto_nego_tmp |= ADVERTISE_10FULL; + if ((adv & ADVERTISED_100baseT_Half) && (anlpar & LPA_100HALF)) + auto_nego_tmp |= ADVERTISE_100HALF; + if ((adv & ADVERTISED_100baseT_Full) && (anlpar & LPA_100FULL)) + auto_nego_tmp |= ADVERTISE_100FULL; + + if (auto_nego_tmp == 0) goto exit; + + auto_nego |= auto_nego_tmp; + goto skip_check_lpa; + } + if (!(aner & EXPANSION_NWAY)) goto exit; + + adv = tp->advertising; + if ((adv & ADVERTISED_10baseT_Half) && (anlpar & LPA_10HALF)) + auto_nego |= ADVERTISE_10HALF; + else if ((adv & ADVERTISED_10baseT_Full) && (anlpar & LPA_10FULL)) + auto_nego |= ADVERTISE_10FULL; + else if ((adv & ADVERTISED_100baseT_Half) && (anlpar & LPA_100HALF)) + auto_nego |= ADVERTISE_100HALF; + else if ((adv & ADVERTISED_100baseT_Full) && (anlpar & LPA_100FULL)) + auto_nego |= ADVERTISE_100FULL; + else if (adv & ADVERTISED_1000baseT_Half && (gbsr & LPA_1000HALF)) + giga_ctrl |= ADVERTISE_1000HALF; + else if (adv & ADVERTISED_1000baseT_Full && (gbsr & LPA_1000FULL)) + giga_ctrl |= ADVERTISE_1000FULL; + else + goto exit; + +skip_check_lpa: + if (tp->DASH) + auto_nego |= (ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10HALF | ADVERTISE_10FULL); + + if (((tp->mcfg == CFG_METHOD_7) || (tp->mcfg == CFG_METHOD_8)) && (RTL_R16(tp, CPlusCmd) & ASF)) + auto_nego |= (ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10HALF | ADVERTISE_10FULL); + +#ifdef CONFIG_DOWN_SPEED_100 + auto_nego |= (ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10HALF | ADVERTISE_10FULL); +#endif + + rtl8168_mdio_write(tp, MII_ADVERTISE, auto_nego); + rtl8168_mdio_write(tp, MII_CTRL1000, giga_ctrl); + + rtl8168_phy_restart_nway(dev); + +exit: + return; +} + +static void +rtl8168_powerdown_pll(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + +#ifdef ENABLE_FIBER_SUPPORT + if (HW_FIBER_MODE_ENABLED(tp)) + return; +#endif //ENABLE_FIBER_SUPPORT + + if (tp->wol_enabled == WOL_ENABLED || tp->DASH || tp->EnableKCPOffload) { + rtl8168_set_hw_wol(dev, tp->wol_opts); + + if (tp->mcfg == CFG_METHOD_16 || tp->mcfg == CFG_METHOD_17 || + tp->mcfg == CFG_METHOD_21 || tp->mcfg == CFG_METHOD_22 || + tp->mcfg == CFG_METHOD_24 || tp->mcfg == CFG_METHOD_25 || + tp->mcfg == CFG_METHOD_26 || tp->mcfg == CFG_METHOD_23 || + tp->mcfg == CFG_METHOD_27 || tp->mcfg == CFG_METHOD_28 || + tp->mcfg == CFG_METHOD_29 || tp->mcfg == CFG_METHOD_30 || + tp->mcfg == CFG_METHOD_31 || tp->mcfg == CFG_METHOD_32 || + tp->mcfg == CFG_METHOD_33) { + rtl8168_enable_cfg9346_write(tp); + RTL_W8(tp, Config2, RTL_R8(tp, Config2) | PMSTS_En); + rtl8168_disable_cfg9346_write(tp); + } + + /* Enable the PME and clear the status */ + rtl8168_set_pci_pme(tp, 1); + + if (HW_SUPP_SERDES_PHY(tp)) + return; + + rtl8168_set_wol_link_speed(dev); + + RTL_W32(tp, RxConfig, RTL_R32(tp, RxConfig) | AcceptBroadcast | AcceptMulticast | AcceptMyPhys); + + return; + } + + if (tp->DASH) + return; + + if (((tp->mcfg == CFG_METHOD_7) || (tp->mcfg == CFG_METHOD_8)) && (RTL_R16(tp, CPlusCmd) & ASF)) + return; + + rtl8168_phy_power_down(dev); + + if (!tp->HwIcVerUnknown) { + switch (tp->mcfg) { + case CFG_METHOD_9: + case CFG_METHOD_10: + //case CFG_METHOD_11: + case CFG_METHOD_12: + case CFG_METHOD_13: + case CFG_METHOD_14: + case CFG_METHOD_15: + case CFG_METHOD_17: + case CFG_METHOD_18: + case CFG_METHOD_19: + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) & ~BIT_7); + break; + } + } + + switch (tp->mcfg) { + case CFG_METHOD_14 ... CFG_METHOD_15: + RTL_W8(tp, 0xD0, RTL_R8(tp, 0xD0) & ~BIT_6); + break; + case CFG_METHOD_16 ... CFG_METHOD_33: + RTL_W8(tp, 0xD0, RTL_R8(tp, 0xD0) & ~BIT_6); + RTL_W8(tp, 0xF2, RTL_R8(tp, 0xF2) & ~BIT_6); + break; + } +} + +static void rtl8168_powerup_pll(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + switch (tp->mcfg) { + case CFG_METHOD_9: + case CFG_METHOD_10: + case CFG_METHOD_11: + case CFG_METHOD_12: + case CFG_METHOD_13: + case CFG_METHOD_14: + case CFG_METHOD_15: + case CFG_METHOD_17: + case CFG_METHOD_18: + case CFG_METHOD_19: + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) | BIT_7 | BIT_6); + break; + } + + rtl8168_phy_power_up(dev); +} + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22) +static void +rtl8168_get_wol(struct net_device *dev, + struct ethtool_wolinfo *wol) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u8 options; + unsigned long flags; + + wol->wolopts = 0; + + if (tp->mcfg == CFG_METHOD_DEFAULT) { + wol->supported = 0; + return; + } else { + wol->supported = WAKE_ANY; + } + + spin_lock_irqsave(&tp->lock, flags); + + options = RTL_R8(tp, Config1); + if (!(options & PMEnable)) + goto out_unlock; + + wol->wolopts = tp->wol_opts; + +out_unlock: + spin_unlock_irqrestore(&tp->lock, flags); +} + +static int +rtl8168_set_wol(struct net_device *dev, + struct ethtool_wolinfo *wol) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned long flags; + + if (tp->mcfg == CFG_METHOD_DEFAULT) + return -EOPNOTSUPP; + + spin_lock_irqsave(&tp->lock, flags); + + tp->wol_opts = wol->wolopts; + + tp->wol_enabled = (tp->wol_opts || tp->dash_printer_enabled) ? WOL_ENABLED : WOL_DISABLED; + + spin_unlock_irqrestore(&tp->lock, flags); + + device_set_wakeup_enable(tp_to_dev(tp), tp->wol_enabled); + + return 0; +} + +static void +rtl8168_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct rtl8168_private *tp = netdev_priv(dev); + struct rtl8168_fw *rtl_fw = tp->rtl_fw; + + strcpy(info->driver, MODULENAME); + strcpy(info->version, RTL8168_VERSION); + strcpy(info->bus_info, pci_name(tp->pci_dev)); + info->regdump_len = R8168_REGS_DUMP_SIZE; + info->eedump_len = tp->eeprom_len; + BUILD_BUG_ON(sizeof(info->fw_version) < sizeof(rtl_fw->version)); + if (rtl_fw) + strlcpy(info->fw_version, rtl_fw->version, + sizeof(info->fw_version)); +} + +static int +rtl8168_get_regs_len(struct net_device *dev) +{ + return R8168_REGS_DUMP_SIZE; +} +#endif //LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22) + +static int +rtl8168_set_speed_xmii(struct net_device *dev, + u8 autoneg, + u32 speed, + u8 duplex, + u32 adv) +{ + struct rtl8168_private *tp = netdev_priv(dev); + int auto_nego = 0; + int giga_ctrl = 0; + int rc = -EINVAL; + + if (tp->mcfg == CFG_METHOD_29 || tp->mcfg == CFG_METHOD_30 || + tp->mcfg == CFG_METHOD_31 || tp->mcfg == CFG_METHOD_32 || + tp->mcfg == CFG_METHOD_33) { + //Disable Giga Lite + rtl8168_mdio_write(tp, 0x1F, 0x0A42); + rtl8168_clear_eth_phy_bit(tp, 0x14, BIT_9); + if (tp->mcfg == CFG_METHOD_31 || tp->mcfg == CFG_METHOD_32 || + tp->mcfg == CFG_METHOD_33) + rtl8168_clear_eth_phy_bit(tp, 0x14, BIT_7); + rtl8168_mdio_write(tp, 0x1F, 0x0A40); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } + + if ((speed != SPEED_1000) && + (speed != SPEED_100) && + (speed != SPEED_10)) { + speed = SPEED_1000; + duplex = DUPLEX_FULL; + } + + giga_ctrl = rtl8168_mdio_read(tp, MII_CTRL1000); + giga_ctrl &= ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL); + + if (autoneg == AUTONEG_ENABLE) { + /*n-way force*/ + auto_nego = rtl8168_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 (dev->mtu <= ETH_DATA_LEN) + auto_nego |= ADVERTISE_PAUSE_CAP|ADVERTISE_PAUSE_ASYM; + + tp->phy_auto_nego_reg = auto_nego; + tp->phy_1000_ctrl_reg = giga_ctrl; + + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, MII_ADVERTISE, auto_nego); + rtl8168_mdio_write(tp, MII_CTRL1000, giga_ctrl); + rtl8168_phy_restart_nway(dev); + mdelay(20); + } else { + /*true force*/ + if (speed == SPEED_10 || speed == SPEED_100) + rtl8168_phy_setup_force_mode(dev, speed, duplex); + else + goto out; + } + + tp->autoneg = autoneg; + tp->speed = speed; + tp->duplex = duplex; + tp->advertising = adv; + + if (tp->mcfg == CFG_METHOD_11) + rtl8168dp_10mbps_gphy_para(dev); + + rc = 0; +out: + return rc; +} + +static int +rtl8168_set_speed(struct net_device *dev, + u8 autoneg, + u32 speed, + u8 duplex, + u32 adv) +{ + struct rtl8168_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 +rtl8168_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 rtl8168_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 = rtl8168_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 +rtl8168_get_tx_csum(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u32 ret; + unsigned long flags; + + spin_lock_irqsave(&tp->lock, flags); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) + ret = ((dev->features & NETIF_F_IP_CSUM) != 0); +#else + ret = ((dev->features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) != 0); +#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) + spin_unlock_irqrestore(&tp->lock, flags); + + return ret; +} + +static u32 +rtl8168_get_rx_csum(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u32 ret; + unsigned long flags; + + spin_lock_irqsave(&tp->lock, flags); + ret = tp->cp_cmd & RxChkSum; + spin_unlock_irqrestore(&tp->lock, flags); + + return ret; +} + +static int +rtl8168_set_tx_csum(struct net_device *dev, + u32 data) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned long flags; + + if (tp->mcfg == CFG_METHOD_DEFAULT) + return -EOPNOTSUPP; + + spin_lock_irqsave(&tp->lock, flags); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) + if (data) + dev->features |= NETIF_F_IP_CSUM; + else + dev->features &= ~NETIF_F_IP_CSUM; +#else + if (data) + if ((tp->mcfg == CFG_METHOD_1) || (tp->mcfg == CFG_METHOD_2) || (tp->mcfg == CFG_METHOD_3)) + dev->features |= NETIF_F_IP_CSUM; + else + dev->features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM); + else + dev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM); +#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) + + spin_unlock_irqrestore(&tp->lock, flags); + + return 0; +} + +static int +rtl8168_set_rx_csum(struct net_device *dev, + u32 data) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned long flags; + + if (tp->mcfg == CFG_METHOD_DEFAULT) + return -EOPNOTSUPP; + + spin_lock_irqsave(&tp->lock, flags); + + if (data) + tp->cp_cmd |= RxChkSum; + else + tp->cp_cmd &= ~RxChkSum; + + RTL_W16(tp, CPlusCmd, tp->cp_cmd); + + 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_R8168_VLAN + +static inline u32 +rtl8168_tx_vlan_tag(struct rtl8168_private *tp, + struct sk_buff *skb) +{ + u32 tag; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) + tag = (tp->vlgrp && vlan_tx_tag_present(skb)) ? + TxVlanTag | swab16(vlan_tx_tag_get(skb)) : 0x00; +#elif LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) + tag = (vlan_tx_tag_present(skb)) ? + TxVlanTag | swab16(vlan_tx_tag_get(skb)) : 0x00; +#else + tag = (skb_vlan_tag_present(skb)) ? + TxVlanTag | swab16(skb_vlan_tag_get(skb)) : 0x00; +#endif + + return tag; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) + +static void +rtl8168_vlan_rx_register(struct net_device *dev, + struct vlan_group *grp) +{ + struct rtl8168_private *tp = netdev_priv(dev); + 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(tp, CPlusCmd, tp->cp_cmd); + RTL_R16(tp, CPlusCmd); + spin_unlock_irqrestore(&tp->lock, flags); +} + +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) +static void +rtl8168_vlan_rx_kill_vid(struct net_device *dev, + unsigned short vid) +{ + struct rtl8168_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 +rtl8168_rx_vlan_skb(struct rtl8168_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)) { + rtl8168_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_R8168_VLAN */ + +static inline u32 +rtl8168_tx_vlan_tag(struct rtl8168_private *tp, + struct sk_buff *skb) +{ + return 0; +} + +static int +rtl8168_rx_vlan_skb(struct rtl8168_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 rtl8168_fix_features(struct net_device *dev, + netdev_features_t features) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&tp->lock, flags); + if (dev->mtu > MSS_MAX) + features &= ~NETIF_F_ALL_TSO; + if (dev->mtu > ETH_DATA_LEN) { + features &= ~NETIF_F_ALL_TSO; + features &= ~NETIF_F_ALL_CSUM; + } + spin_unlock_irqrestore(&tp->lock, flags); + + return features; +} + +static int rtl8168_hw_set_features(struct net_device *dev, + netdev_features_t features) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u32 rx_config; + + rx_config = RTL_R32(tp, RxConfig); + if (features & NETIF_F_RXALL) + rx_config |= (AcceptErr | AcceptRunt); + else + rx_config &= ~(AcceptErr | AcceptRunt); + + RTL_W32(tp, RxConfig, rx_config); + + 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(tp, CPlusCmd, tp->cp_cmd); + RTL_R16(tp, CPlusCmd); + + return 0; +} + +static int rtl8168_set_features(struct net_device *dev, + netdev_features_t features) +{ + struct rtl8168_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) + rtl8168_hw_set_features(dev, features); + spin_unlock_irqrestore(&tp->lock, flags); + + return 0; +} + +#endif + +static void rtl8168_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 rtl8168_private *tp = netdev_priv(dev); + 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); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + bmcr = rtl8168_mdio_read(tp, MII_BMCR); + bmsr = rtl8168_mdio_read(tp, MII_BMSR); + anlpar = rtl8168_mdio_read(tp, MII_LPA); + ctrl1000 = rtl8168_mdio_read(tp, MII_CTRL1000); + stat1000 = rtl8168_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(tp, 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 +rtl8168_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 rtl8168_private *tp = netdev_priv(dev); + + tp->get_settings(dev, cmd); + + return 0; +} + +static void rtl8168_get_regs(struct net_device *dev, struct ethtool_regs *regs, + void *p) +{ + struct rtl8168_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + unsigned int i; + u8 *data = p; + unsigned long flags; + + if (regs->len < R8168_REGS_DUMP_SIZE) + return /* -EINVAL */; + + memset(p, 0, regs->len); + + spin_lock_irqsave(&tp->lock, flags); + for (i = 0; i < R8168_MAC_REGS_SIZE; i++) + *data++ = readb(ioaddr + i); + data = (u8*)p + 256; + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + for (i = 0; i < R8168_PHY_REGS_SIZE/2; i++) { + *(u16*)data = rtl8168_mdio_read(tp, i); + data += 2; + } + data = (u8*)p + 256 * 2; + + for (i = 0; i < R8168_EPHY_REGS_SIZE/2; i++) { + *(u16*)data = rtl8168_ephy_read(tp, i); + data += 2; + } + data = (u8*)p + 256 * 3; + + switch (tp->mcfg) { + case CFG_METHOD_1: + case CFG_METHOD_2: + case CFG_METHOD_3: + /* RTL8168B does not support Extend GMAC */ + break; + default: + for (i = 0; i < R8168_ERI_REGS_SIZE; i+=4) { + *(u32*)data = rtl8168_eri_read(tp, i , 4, ERIAR_ExGMAC); + data += 4; + } + break; + } + spin_unlock_irqrestore(&tp->lock, flags); +} + +static u32 +rtl8168_get_msglevel(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + return tp->msg_enable; +} + +static void +rtl8168_set_msglevel(struct net_device *dev, + u32 value) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + tp->msg_enable = value; +} + +static const char rtl8168_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) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22) +static int rtl8168_get_stats_count(struct net_device *dev) +{ + return ARRAY_SIZE(rtl8168_gstrings); +} +#endif //#LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22) +#else +static int rtl8168_get_sset_count(struct net_device *dev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return ARRAY_SIZE(rtl8168_gstrings); + default: + return -EOPNOTSUPP; + } +} +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22) +static void +rtl8168_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, + u64 *data) +{ + struct rtl8168_private *tp = netdev_priv(dev); + struct rtl8168_counters *counters; + dma_addr_t paddr; + u32 cmd; + u32 WaitCnt; + unsigned long flags; + + ASSERT_RTNL(); + + counters = tp->tally_vaddr; + paddr = tp->tally_paddr; + if (!counters) + return; + + spin_lock_irqsave(&tp->lock, flags); + RTL_W32(tp, CounterAddrHigh, (u64)paddr >> 32); + cmd = (u64)paddr & DMA_BIT_MASK(32); + RTL_W32(tp, CounterAddrLow, cmd); + RTL_W32(tp, CounterAddrLow, cmd | CounterDump); + + WaitCnt = 0; + while (RTL_R32(tp, CounterAddrLow) & CounterDump) { + udelay(10); + + WaitCnt++; + if (WaitCnt > 20) + break; + } + spin_unlock_irqrestore(&tp->lock, flags); + + data[0] = le64_to_cpu(counters->tx_packets); + data[1] = le64_to_cpu(counters->rx_packets); + data[2] = le64_to_cpu(counters->tx_errors); + data[3] = le32_to_cpu(counters->rx_errors); + data[4] = le16_to_cpu(counters->rx_missed); + data[5] = le16_to_cpu(counters->align_errors); + data[6] = le32_to_cpu(counters->tx_one_collision); + data[7] = le32_to_cpu(counters->tx_multi_collision); + data[8] = le64_to_cpu(counters->rx_unicast); + data[9] = le64_to_cpu(counters->rx_broadcast); + data[10] = le32_to_cpu(counters->rx_multicast); + data[11] = le16_to_cpu(counters->tx_aborted); + data[12] = le16_to_cpu(counters->tx_underrun); +} + +static void +rtl8168_get_strings(struct net_device *dev, + u32 stringset, + u8 *data) +{ + switch (stringset) { + case ETH_SS_STATS: + memcpy(data, *rtl8168_gstrings, sizeof(rtl8168_gstrings)); + break; + } +} +#endif //#LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22) + +static int rtl_get_eeprom_len(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + return tp->eeprom_len; +} + +static int rtl_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *buf) +{ + struct rtl8168_private *tp = netdev_priv(dev); + int i,j,ret; + int start_w, end_w; + int VPD_addr, VPD_data; + u32 *eeprom_buff; + u16 tmp; + + if (tp->eeprom_type == EEPROM_TYPE_NONE) { + dev_printk(KERN_DEBUG, tp_to_dev(tp), "Detect none EEPROM\n"); + return -EOPNOTSUPP; + } else if (eeprom->len == 0 || (eeprom->offset+eeprom->len) > tp->eeprom_len) { + dev_printk(KERN_DEBUG, tp_to_dev(tp), "Invalid parameter\n"); + return -EINVAL; + } + + switch (tp->mcfg) { + case CFG_METHOD_9: + case CFG_METHOD_10: + VPD_addr = 0xCE; + VPD_data = 0xD0; + break; + + case CFG_METHOD_1: + case CFG_METHOD_2: + case CFG_METHOD_3: + case CFG_METHOD_11: + case CFG_METHOD_12: + case CFG_METHOD_13: + return -EOPNOTSUPP; + default: + VPD_addr = 0xD2; + VPD_data = 0xD4; + break; + } + + start_w = eeprom->offset >> 2; + end_w = (eeprom->offset + eeprom->len - 1) >> 2; + + eeprom_buff = kmalloc(sizeof(u32)*(end_w - start_w + 1), GFP_KERNEL); + if (!eeprom_buff) + return -ENOMEM; + + rtl8168_enable_cfg9346_write(tp); + ret = -EFAULT; + for (i=start_w; i<=end_w; i++) { + pci_write_config_word(tp->pci_dev, VPD_addr, (u16)i*4); + ret = -EFAULT; + for (j = 0; j < 10; j++) { + udelay(400); + pci_read_config_word(tp->pci_dev, VPD_addr, &tmp); + if (tmp&0x8000) { + ret = 0; + break; + } + } + + if (ret) + break; + + pci_read_config_dword(tp->pci_dev, VPD_data, &eeprom_buff[i-start_w]); + } + rtl8168_disable_cfg9346_write(tp); + + if (!ret) + memcpy(buf, (u8 *)eeprom_buff + (eeprom->offset & 3), eeprom->len); + + kfree(eeprom_buff); + + return ret; +} + +#undef ethtool_op_get_link +#define ethtool_op_get_link _kc_ethtool_op_get_link +static 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 +static 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 +static int _kc_ethtool_op_set_sg(struct net_device *dev, u32 data) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + if (tp->mcfg == CFG_METHOD_DEFAULT) + return -EOPNOTSUPP; + +#ifdef NETIF_F_SG + if (data) + dev->features |= NETIF_F_SG; + else + dev->features &= ~NETIF_F_SG; +#endif + + return 0; +} +#endif + +static int rtl8168_enable_EEE(struct rtl8168_private *tp) +{ + int ret; + u16 data; + u32 csi_tmp; + + ret = 0; + switch (tp->mcfg) { + case CFG_METHOD_14: + case CFG_METHOD_15: + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x0020); + data = rtl8168_mdio_read(tp, 0x15) | 0x0100; + rtl8168_mdio_write(tp, 0x15, data); + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B85); + data = rtl8168_mdio_read(tp, 0x06) | 0x2000; + rtl8168_mdio_write(tp, 0x06, data); + rtl8168_mdio_write(tp, 0x1F, 0x0006); + rtl8168_mdio_write(tp, 0x00, 0x5A30); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x0D, 0x0007); + rtl8168_mdio_write(tp, 0x0E, 0x003C); + rtl8168_mdio_write(tp, 0x0D, 0x4007); + rtl8168_mdio_write(tp, 0x0E, 0x0006); + rtl8168_mdio_write(tp, 0x0D, 0x0000); + if ((RTL_R8(tp, Config4)&0x40) && (RTL_R8(tp, 0x6D) & BIT_7)) { + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8AC8); + rtl8168_mdio_write(tp, 0x06, RTL_R16(tp, tp->NicCustLedValue)); + rtl8168_mdio_write(tp, 0x05, 0x8B82); + data = rtl8168_mdio_read(tp, 0x06) | 0x0010; + rtl8168_mdio_write(tp, 0x05, 0x8B82); + rtl8168_mdio_write(tp, 0x06, data); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } + break; + + case CFG_METHOD_16: + case CFG_METHOD_17: + csi_tmp = rtl8168_eri_read(tp, 0x1B0, 4, ERIAR_ExGMAC) | 0x0003; + rtl8168_eri_write(tp, 0x1B0, 4, csi_tmp, ERIAR_ExGMAC); + rtl8168_mdio_write(tp,0x1F , 0x0004); + rtl8168_mdio_write(tp,0x1F , 0x0007); + rtl8168_mdio_write(tp,0x1E , 0x0020); + data = rtl8168_mdio_read(tp, 0x15)|0x0100; + rtl8168_mdio_write(tp,0x15 , data); + rtl8168_mdio_write(tp,0x1F , 0x0002); + rtl8168_mdio_write(tp,0x1F , 0x0005); + rtl8168_mdio_write(tp,0x05 , 0x8B85); + data = rtl8168_mdio_read(tp, 0x06)|0x2000; + rtl8168_mdio_write(tp,0x06 , data); + rtl8168_mdio_write(tp,0x1F , 0x0000); + rtl8168_mdio_write(tp,0x0D , 0x0007); + rtl8168_mdio_write(tp,0x0E , 0x003C); + rtl8168_mdio_write(tp,0x0D , 0x4007); + rtl8168_mdio_write(tp,0x0E , 0x0006); + rtl8168_mdio_write(tp,0x0D , 0x0000); + break; + + case CFG_METHOD_18: + case CFG_METHOD_19: + case CFG_METHOD_20: + csi_tmp = rtl8168_eri_read(tp, 0x1B0, 4, ERIAR_ExGMAC); + csi_tmp |= BIT_1 | BIT_0; + rtl8168_eri_write(tp, 0x1B0, 4, csi_tmp, ERIAR_ExGMAC); + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0020); + data = rtl8168_mdio_read(tp, 0x15); + data |= BIT_8; + rtl8168_mdio_write(tp, 0x15, data); + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B85); + data = rtl8168_mdio_read(tp, 0x06); + data |= BIT_13; + rtl8168_mdio_write(tp, 0x06, data); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x0D, 0x0007); + rtl8168_mdio_write(tp, 0x0E, 0x003C); + rtl8168_mdio_write(tp, 0x0D, 0x4007); + rtl8168_mdio_write(tp, 0x0E, 0x0006); + rtl8168_mdio_write(tp, 0x0D, 0x0000); + break; + + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + csi_tmp = rtl8168_eri_read(tp, 0x1B0, 4, ERIAR_ExGMAC); + csi_tmp |= BIT_1 | BIT_0; + rtl8168_eri_write(tp, 0x1B0, 4, csi_tmp, ERIAR_ExGMAC); + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + data = rtl8168_mdio_read(tp, 0x11); + rtl8168_mdio_write(tp, 0x11, data | BIT_4); + rtl8168_mdio_write(tp, 0x1F, 0x0A5D); + rtl8168_mdio_write(tp, 0x10, tp->eee_adv_t); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + break; + + default: +// dev_printk(KERN_DEBUG, tp_to_dev(tp), "Not Support EEE\n"); + ret = -EOPNOTSUPP; + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_mdio_write(tp, 0x1F, 0x0A4A); + rtl8168_set_eth_phy_bit(tp, 0x11, BIT_9); + rtl8168_mdio_write(tp, 0x1F, 0x0A42); + rtl8168_set_eth_phy_bit(tp, 0x14, BIT_7); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + break; + } + + /*Advanced EEE*/ + switch (tp->mcfg) { + case CFG_METHOD_23: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_oob_mutex_lock(tp); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_set_phy_mcu_patch_request(tp); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_25: + rtl8168_eri_write(tp, 0x1EA, 1, 0xFA, ERIAR_ExGMAC); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + data = rtl8168_mdio_read(tp, 0x10); + if (data & BIT_10) { + rtl8168_mdio_write(tp, 0x1F, 0x0A42); + data = rtl8168_mdio_read(tp, 0x16); + data &= ~(BIT_1); + rtl8168_mdio_write(tp, 0x16, data); + } else { + rtl8168_mdio_write(tp, 0x1F, 0x0A42); + data = rtl8168_mdio_read(tp, 0x16); + data |= BIT_1; + rtl8168_mdio_write(tp, 0x16, data); + } + rtl8168_mdio_write(tp, 0x1F, 0x0000); + break; + case CFG_METHOD_26: + data = rtl8168_mac_ocp_read(tp, 0xE052); + data |= BIT_0; + rtl8168_mac_ocp_write(tp, 0xE052, data); + data = rtl8168_mac_ocp_read(tp, 0xE056); + data &= 0xFF0F; + data |= (BIT_4 | BIT_5 | BIT_6); + rtl8168_mac_ocp_write(tp, 0xE056, data); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + data = rtl8168_mdio_read(tp, 0x10); + if (data & BIT_10) { + rtl8168_mdio_write(tp, 0x1F, 0x0A42); + data = rtl8168_mdio_read(tp, 0x16); + data &= ~(BIT_1); + rtl8168_mdio_write(tp, 0x16, data); + } else { + rtl8168_mdio_write(tp, 0x1F, 0x0A42); + data = rtl8168_mdio_read(tp, 0x16); + data |= BIT_1; + rtl8168_mdio_write(tp, 0x16, data); + } + rtl8168_mdio_write(tp, 0x1F, 0x0000); + break; + case CFG_METHOD_27: + case CFG_METHOD_28: + data = rtl8168_mac_ocp_read(tp, 0xE052); + data &= ~BIT_0; + rtl8168_mac_ocp_write(tp, 0xE052, data); + data = rtl8168_mac_ocp_read(tp, 0xE056); + data &= 0xFF0F; + data |= (BIT_4 | BIT_5 | BIT_6); + rtl8168_mac_ocp_write(tp, 0xE056, data); + break; + case CFG_METHOD_29: + case CFG_METHOD_30: + data = rtl8168_mac_ocp_read(tp, 0xE052); + data &= ~(BIT_0); + rtl8168_mac_ocp_write(tp, 0xE052, data); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + data = rtl8168_mdio_read(tp, 0x10) | BIT_15; + rtl8168_mdio_write(tp, 0x10, data); + + rtl8168_mdio_write(tp, 0x1F, 0x0A44); + data = rtl8168_mdio_read(tp, 0x11) | BIT_13 | BIT_14; + data &= ~(BIT_12); + rtl8168_mdio_write(tp, 0x11, data); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + break; + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + /* + data = rtl8168_mac_ocp_read(tp, 0xE052); + data |= BIT_0; + rtl8168_mac_ocp_write(tp, 0xE052, data); + */ + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + data = rtl8168_mdio_read(tp, 0x10) | BIT_15; + rtl8168_mdio_write(tp, 0x10, data); + + rtl8168_mdio_write(tp, 0x1F, 0x0A44); + data = rtl8168_mdio_read(tp, 0x11) | BIT_13 | BIT_14; + data &= ~(BIT_12); + rtl8168_mdio_write(tp, 0x11, data); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_clear_phy_mcu_patch_request(tp); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_23: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_oob_mutex_unlock(tp); + break; + } + + return ret; +} + +static int rtl8168_disable_EEE(struct rtl8168_private *tp) +{ + int ret; + u16 data; + u32 csi_tmp; + + ret = 0; + switch (tp->mcfg) { + case CFG_METHOD_14: + case CFG_METHOD_15: + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B85); + data = rtl8168_mdio_read(tp, 0x06) & ~0x2000; + rtl8168_mdio_write(tp, 0x06, data); + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x0020); + data = rtl8168_mdio_read(tp, 0x15) & ~0x0100; + rtl8168_mdio_write(tp, 0x15, data); + rtl8168_mdio_write(tp, 0x1F, 0x0006); + rtl8168_mdio_write(tp, 0x00, 0x5A00); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x0D, 0x0007); + rtl8168_mdio_write(tp, 0x0E, 0x003C); + rtl8168_mdio_write(tp, 0x0D, 0x4007); + rtl8168_mdio_write(tp, 0x0E, 0x0000); + rtl8168_mdio_write(tp, 0x0D, 0x0000); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + if (RTL_R8(tp, Config4) & 0x40) { + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B82); + data = rtl8168_mdio_read(tp, 0x06) & ~0x0010; + rtl8168_mdio_write(tp, 0x05, 0x8B82); + rtl8168_mdio_write(tp, 0x06, data); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } + break; + + case CFG_METHOD_16: + case CFG_METHOD_17: + csi_tmp = rtl8168_eri_read(tp, 0x1B0,4, ERIAR_ExGMAC)& ~0x0003; + rtl8168_eri_write(tp, 0x1B0, 4, csi_tmp, ERIAR_ExGMAC); + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B85); + data = rtl8168_mdio_read(tp, 0x06) & ~0x2000; + rtl8168_mdio_write(tp, 0x06, data); + rtl8168_mdio_write(tp, 0x1F, 0x0004); + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x0020); + data = rtl8168_mdio_read(tp, 0x15) & ~0x0100; + rtl8168_mdio_write(tp,0x15 , data); + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x0D, 0x0007); + rtl8168_mdio_write(tp, 0x0E, 0x003C); + rtl8168_mdio_write(tp, 0x0D, 0x4007); + rtl8168_mdio_write(tp, 0x0E, 0x0000); + rtl8168_mdio_write(tp, 0x0D, 0x0000); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + break; + + case CFG_METHOD_18: + case CFG_METHOD_19: + case CFG_METHOD_20: + csi_tmp = rtl8168_eri_read(tp, 0x1B0, 4, ERIAR_ExGMAC); + csi_tmp &= ~(BIT_1 | BIT_0); + rtl8168_eri_write(tp, 0x1B0, 4, csi_tmp, ERIAR_ExGMAC); + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B85); + data = rtl8168_mdio_read(tp, 0x06); + data &= ~BIT_13; + rtl8168_mdio_write(tp, 0x06, data); + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0020); + data = rtl8168_mdio_read(tp, 0x15); + data &= ~BIT_8; + rtl8168_mdio_write(tp, 0x15, data); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x0D, 0x0007); + rtl8168_mdio_write(tp, 0x0E, 0x003C); + rtl8168_mdio_write(tp, 0x0D, 0x4007); + rtl8168_mdio_write(tp, 0x0E, 0x0000); + rtl8168_mdio_write(tp, 0x0D, 0x0000); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + break; + + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + csi_tmp = rtl8168_eri_read(tp, 0x1B0, 4, ERIAR_ExGMAC); + csi_tmp &= ~(BIT_1 | BIT_0); + rtl8168_eri_write(tp, 0x1B0, 4, csi_tmp, ERIAR_ExGMAC); + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + data = rtl8168_mdio_read(tp, 0x11); + rtl8168_mdio_write(tp, 0x11, data & ~BIT_4); + rtl8168_mdio_write(tp, 0x1F, 0x0A5D); + rtl8168_mdio_write(tp, 0x10, 0x0000); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + break; + + default: +// dev_printk(KERN_DEBUG, tp_to_dev(tp), "Not Support EEE\n"); + ret = -EOPNOTSUPP; + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_29: + case CFG_METHOD_30: + rtl8168_mdio_write(tp, 0x1F, 0x0A42); + rtl8168_clear_eth_phy_bit(tp, 0x14, BIT_7); + rtl8168_mdio_write(tp, 0x1F, 0x0A4A); + rtl8168_clear_eth_phy_bit(tp, 0x11, BIT_9); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + break; + } + + /*Advanced EEE*/ + switch (tp->mcfg) { + case CFG_METHOD_23: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_oob_mutex_lock(tp); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_set_phy_mcu_patch_request(tp); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_25: + rtl8168_eri_write(tp, 0x1EA, 1, 0x00, ERIAR_ExGMAC); + + rtl8168_mdio_write(tp, 0x1F, 0x0A42); + data = rtl8168_mdio_read(tp, 0x16); + data &= ~(BIT_1); + rtl8168_mdio_write(tp, 0x16, data); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + break; + case CFG_METHOD_26: + data = rtl8168_mac_ocp_read(tp, 0xE052); + data &= ~(BIT_0); + rtl8168_mac_ocp_write(tp, 0xE052, data); + + rtl8168_mdio_write(tp, 0x1F, 0x0A42); + data = rtl8168_mdio_read(tp, 0x16); + data &= ~(BIT_1); + rtl8168_mdio_write(tp, 0x16, data); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + break; + case CFG_METHOD_27: + case CFG_METHOD_28: + data = rtl8168_mac_ocp_read(tp, 0xE052); + data &= ~(BIT_0); + rtl8168_mac_ocp_write(tp, 0xE052, data); + break; + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + data = rtl8168_mac_ocp_read(tp, 0xE052); + data &= ~(BIT_0); + rtl8168_mac_ocp_write(tp, 0xE052, data); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + data = rtl8168_mdio_read(tp, 0x10) & ~(BIT_15); + rtl8168_mdio_write(tp, 0x10, data); + + rtl8168_mdio_write(tp, 0x1F, 0x0A44); + data = rtl8168_mdio_read(tp, 0x11) & ~(BIT_12 | BIT_13 | BIT_14); + rtl8168_mdio_write(tp, 0x11, data); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_clear_phy_mcu_patch_request(tp); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_23: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_oob_mutex_unlock(tp); + break; + } + + return ret; +} + +static int rtl_nway_reset(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned long flags; + int ret, bmcr; + + spin_lock_irqsave(&tp->lock, flags); + + if (unlikely(tp->rtk_enable_diag)) { + spin_unlock_irqrestore(&tp->lock, flags); + return -EBUSY; + } + + /* if autoneg is off, it's an error */ + rtl8168_mdio_write(tp, 0x1F, 0x0000); + bmcr = rtl8168_mdio_read(tp, MII_BMCR); + + if (bmcr & BMCR_ANENABLE) { + bmcr |= BMCR_ANRESTART; + rtl8168_mdio_write(tp, MII_BMCR, bmcr); + ret = 0; + } else { + ret = -EINVAL; + } + + spin_unlock_irqrestore(&tp->lock, flags); + + return ret; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0) +static int +rtl_ethtool_get_eee(struct net_device *net, struct ethtool_eee *eee) +{ + struct rtl8168_private *tp = netdev_priv(net); + u32 lp, adv, supported = 0; + unsigned long flags; + u16 val; + + switch (tp->mcfg) { + case CFG_METHOD_21 ... CFG_METHOD_33: + break; + default: + return -EOPNOTSUPP; + } + + spin_lock_irqsave(&tp->lock, flags); + + if (unlikely(tp->rtk_enable_diag)) { + spin_unlock_irqrestore(&tp->lock, flags); + return -EBUSY; + } + + rtl8168_mdio_write(tp, 0x1F, 0x0A5C); + val = rtl8168_mdio_read(tp, 0x12); + supported = mmd_eee_cap_to_ethtool_sup_t(val); + + rtl8168_mdio_write(tp, 0x1F, 0x0A5D); + val = rtl8168_mdio_read(tp, 0x10); + adv = mmd_eee_adv_to_ethtool_adv_t(val); + + val = rtl8168_mdio_read(tp, 0x11); + lp = mmd_eee_adv_to_ethtool_adv_t(val); + + val = rtl8168_eri_read(tp, 0x1B0, 2, ERIAR_ExGMAC); + val &= BIT_1 | BIT_0; + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + spin_unlock_irqrestore(&tp->lock, flags); + + eee->eee_enabled = !!val; + eee->eee_active = !!(supported & adv & lp); + eee->supported = supported; + eee->advertised = adv; + eee->lp_advertised = lp; + + return 0; +} + +static int +rtl_ethtool_set_eee(struct net_device *net, struct ethtool_eee *eee) +{ + struct rtl8168_private *tp = netdev_priv(net); + unsigned long flags; + + switch (tp->mcfg) { + case CFG_METHOD_21 ... CFG_METHOD_33: + break; + default: + return -EOPNOTSUPP; + } + + if (HW_SUPP_SERDES_PHY(tp) || + !HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp) || + tp->DASH) + return -EOPNOTSUPP; + + spin_lock_irqsave(&tp->lock, flags); + + if (unlikely(tp->rtk_enable_diag)) { + spin_unlock_irqrestore(&tp->lock, flags); + return -EBUSY; + } + + tp->eee_enabled = eee->eee_enabled; + tp->eee_adv_t = ethtool_adv_to_mmd_eee_adv_t(eee->advertised); + + if (tp->eee_enabled) + rtl8168_enable_EEE(tp); + else + rtl8168_disable_EEE(tp); + + spin_unlock_irqrestore(&tp->lock, flags); + + rtl_nway_reset(net); + + return 0; +} +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0) */ + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22) +static const struct ethtool_ops rtl8168_ethtool_ops = { + .get_drvinfo = rtl8168_get_drvinfo, + .get_regs_len = rtl8168_get_regs_len, + .get_link = ethtool_op_get_link, +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,6,0) + .get_settings = rtl8168_get_settings, + .set_settings = rtl8168_set_settings, +#else + .get_link_ksettings = rtl8168_get_settings, + .set_link_ksettings = rtl8168_set_settings, +#endif + .get_msglevel = rtl8168_get_msglevel, + .set_msglevel = rtl8168_set_msglevel, +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0) + .get_rx_csum = rtl8168_get_rx_csum, + .set_rx_csum = rtl8168_set_rx_csum, + .get_tx_csum = rtl8168_get_tx_csum, + .set_tx_csum = rtl8168_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 = rtl8168_get_regs, + .get_wol = rtl8168_get_wol, + .set_wol = rtl8168_set_wol, + .get_strings = rtl8168_get_strings, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) + .get_stats_count = rtl8168_get_stats_count, +#else + .get_sset_count = rtl8168_get_sset_count, +#endif + .get_ethtool_stats = rtl8168_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) + .get_eeprom = rtl_get_eeprom, + .get_eeprom_len = rtl_get_eeprom_len, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) + .get_ts_info = ethtool_op_get_ts_info, +#endif //LINUX_VERSION_CODE >= KERNEL_VERSION(3,5,0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0) + .get_eee = rtl_ethtool_get_eee, + .set_eee = rtl_ethtool_set_eee, +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0) */ + .nway_reset = rtl_nway_reset, +}; +#endif //LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22) + +#if 0 + +static int rtl8168_enable_green_feature(struct rtl8168_private *tp) +{ + u16 gphy_val; + unsigned long flags; + + switch (tp->mcfg) { + case CFG_METHOD_14: + case CFG_METHOD_15: + rtl8168_mdio_write(tp, 0x1F, 0x0003); + gphy_val = rtl8168_mdio_read(tp, 0x10) | 0x0400; + rtl8168_mdio_write(tp, 0x10, gphy_val); + gphy_val = rtl8168_mdio_read(tp, 0x19) | 0x0001; + rtl8168_mdio_write(tp, 0x19, gphy_val); + rtl8168_mdio_write(tp, 0x1F, 0x0005); + gphy_val = rtl8168_mdio_read(tp, 0x01) & ~0x0100; + rtl8168_mdio_write(tp, 0x01, gphy_val); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x00, 0x9200); + mdelay(20); + break; + + case CFG_METHOD_17: + case CFG_METHOD_18: + case CFG_METHOD_19: + case CFG_METHOD_20: + rtl8168_mdio_write(tp, 0x1f, 0x0003); + gphy_val = rtl8168_mdio_read(tp, 0x10); + gphy_val |= BIT_10; + rtl8168_mdio_write(tp, 0x10, gphy_val); + gphy_val = rtl8168_mdio_read(tp, 0x19); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp, 0x19, gphy_val); + rtl8168_mdio_write(tp, 0x1F, 0x0005); + gphy_val = rtl8168_mdio_read(tp, 0x01); + gphy_val |= BIT_8; + rtl8168_mdio_write(tp, 0x01, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x00, 0x9200); + break; + case CFG_METHOD_21: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8011); + rtl8168_set_eth_phy_bit( tp, 0x14, BIT_14 ); + rtl8168_mdio_write(tp, 0x1F, 0x0A40); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x00, 0x9200); + break; + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8045); + rtl8168_mdio_write(tp, 0x14, 0x0000); + rtl8168_mdio_write(tp, 0x13, 0x804d); + rtl8168_mdio_write(tp, 0x14, 0x1222); + rtl8168_mdio_write(tp, 0x13, 0x805d); + rtl8168_mdio_write(tp, 0x14, 0x0022); + rtl8168_mdio_write(tp, 0x13, 0x8011); + rtl8168_set_eth_phy_bit( tp, 0x14, BIT_15 ); + rtl8168_mdio_write(tp, 0x1F, 0x0A40); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x00, 0x9200); + break; + default: + dev_printk(KERN_DEBUG, tp_to_dev(tp), "Not Support Green Feature\n"); + break; + } + + return 0; +} + +static int rtl8168_disable_green_feature(struct rtl8168_private *tp) +{ + u16 gphy_val; + unsigned long flags; + + switch (tp->mcfg) { + case CFG_METHOD_14: + case CFG_METHOD_15: + rtl8168_mdio_write(tp, 0x1F, 0x0005); + gphy_val = rtl8168_mdio_read(tp, 0x01) | 0x0100; + rtl8168_mdio_write(tp, 0x01, gphy_val); + rtl8168_mdio_write(tp, 0x1F, 0x0003); + gphy_val = rtl8168_mdio_read(tp, 0x10) & ~0x0400; + rtl8168_mdio_write(tp, 0x10, gphy_val); + gphy_val = rtl8168_mdio_read(tp, 0x19) & ~0x0001; + rtl8168_mdio_write(tp, 0x19, gphy_val); + rtl8168_mdio_write(tp, 0x1F, 0x0002); + gphy_val = rtl8168_mdio_read(tp, 0x06) & ~0x7000; + gphy_val |= 0x3000; + rtl8168_mdio_write(tp, 0x06, gphy_val); + gphy_val = rtl8168_mdio_read(tp, 0x0D) & 0x0700; + gphy_val |= 0x0500; + rtl8168_mdio_write(tp, 0x0D, gphy_val); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + break; + + case CFG_METHOD_17: + case CFG_METHOD_18: + case CFG_METHOD_19: + case CFG_METHOD_20: + rtl8168_mdio_write(tp, 0x1f, 0x0003); + gphy_val = rtl8168_mdio_read(tp, 0x19); + gphy_val &= ~BIT_0; + rtl8168_mdio_write(tp, 0x19, gphy_val); + gphy_val = rtl8168_mdio_read(tp, 0x10); + gphy_val &= ~BIT_10; + rtl8168_mdio_write(tp, 0x10, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + break; + case CFG_METHOD_21: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8011); + rtl8168_clear_eth_phy_bit( tp, 0x14, BIT_14 ); + rtl8168_mdio_write(tp, 0x1F, 0x0A40); + rtl8168_mdio_write(tp, 0x00, 0x9200); + break; + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8045); + rtl8168_mdio_write(tp, 0x14, 0x2444); + rtl8168_mdio_write(tp, 0x13, 0x804d); + rtl8168_mdio_write(tp, 0x14, 0x2444); + rtl8168_mdio_write(tp, 0x13, 0x805d); + rtl8168_mdio_write(tp, 0x14, 0x2444); + rtl8168_mdio_write(tp, 0x13, 0x8011); + rtl8168_set_eth_phy_bit( tp, 0x14, BIT_15 ); + rtl8168_mdio_write(tp, 0x1F, 0x0A40); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x00, 0x9200); + break; + default: + dev_printk(KERN_DEBUG, tp_to_dev(tp), "Not Support Green Feature\n"); + break; + } + + return 0; +} + +#endif + +static void rtl8168_get_mac_version(struct rtl8168_private *tp) +{ + u32 reg,val32; + u32 ICVerID; + + val32 = RTL_R32(tp, TxConfig); + reg = val32 & 0x7c800000; + ICVerID = val32 & 0x00700000; + + switch (reg) { + case 0x30000000: + tp->mcfg = CFG_METHOD_1; + tp->efuse_ver = EFUSE_NOT_SUPPORT; + break; + case 0x38000000: + if (ICVerID == 0x00000000) { + tp->mcfg = CFG_METHOD_2; + } else if (ICVerID == 0x00500000) { + tp->mcfg = CFG_METHOD_3; + } else { + tp->mcfg = CFG_METHOD_3; + tp->HwIcVerUnknown = TRUE; + } + tp->efuse_ver = EFUSE_NOT_SUPPORT; + break; + case 0x3C000000: + if (ICVerID == 0x00000000) { + tp->mcfg = CFG_METHOD_4; + } else if (ICVerID == 0x00200000) { + tp->mcfg = CFG_METHOD_5; + } else if (ICVerID == 0x00400000) { + tp->mcfg = CFG_METHOD_6; + } else { + tp->mcfg = CFG_METHOD_6; + tp->HwIcVerUnknown = TRUE; + } + tp->efuse_ver = EFUSE_NOT_SUPPORT; + break; + case 0x3C800000: + if (ICVerID == 0x00100000) { + tp->mcfg = CFG_METHOD_7; + } else if (ICVerID == 0x00300000) { + tp->mcfg = CFG_METHOD_8; + } else { + tp->mcfg = CFG_METHOD_8; + tp->HwIcVerUnknown = TRUE; + } + tp->efuse_ver = EFUSE_NOT_SUPPORT; + break; + case 0x28000000: + if (ICVerID == 0x00100000) { + tp->mcfg = CFG_METHOD_9; + } else if (ICVerID == 0x00300000) { + tp->mcfg = CFG_METHOD_10; + } else { + tp->mcfg = CFG_METHOD_10; + tp->HwIcVerUnknown = TRUE; + } + tp->efuse_ver = EFUSE_SUPPORT_V1; + break; + case 0x28800000: + if (ICVerID == 0x00000000) { + tp->mcfg = CFG_METHOD_11; + } else if (ICVerID == 0x00200000) { + tp->mcfg = CFG_METHOD_12; + RTL_W32(tp, 0xD0, RTL_R32(tp, 0xD0) | 0x00020000); + } else if (ICVerID == 0x00300000) { + tp->mcfg = CFG_METHOD_13; + } else { + tp->mcfg = CFG_METHOD_13; + tp->HwIcVerUnknown = TRUE; + } + tp->efuse_ver = EFUSE_SUPPORT_V1; + break; + case 0x2C000000: + if (ICVerID == 0x00100000) { + tp->mcfg = CFG_METHOD_14; + } else if (ICVerID == 0x00200000) { + tp->mcfg = CFG_METHOD_15; + } else { + tp->mcfg = CFG_METHOD_15; + tp->HwIcVerUnknown = TRUE; + } + tp->efuse_ver = EFUSE_SUPPORT_V2; + break; + case 0x2C800000: + if (ICVerID == 0x00000000) { + tp->mcfg = CFG_METHOD_16; + } else if (ICVerID == 0x00100000) { + tp->mcfg = CFG_METHOD_17; + } else { + tp->mcfg = CFG_METHOD_17; + tp->HwIcVerUnknown = TRUE; + } + tp->efuse_ver = EFUSE_SUPPORT_V3; + break; + case 0x48000000: + if (ICVerID == 0x00000000) { + tp->mcfg = CFG_METHOD_18; + } else if (ICVerID == 0x00100000) { + tp->mcfg = CFG_METHOD_19; + } else { + tp->mcfg = CFG_METHOD_19; + tp->HwIcVerUnknown = TRUE; + } + tp->efuse_ver = EFUSE_SUPPORT_V3; + break; + case 0x48800000: + if (ICVerID == 0x00000000) { + tp->mcfg = CFG_METHOD_20; + } else { + tp->mcfg = CFG_METHOD_20; + tp->HwIcVerUnknown = TRUE; + } + + tp->efuse_ver = EFUSE_SUPPORT_V3; + break; + case 0x4C000000: + if (ICVerID == 0x00000000) { + tp->mcfg = CFG_METHOD_21; + } else if (ICVerID == 0x00100000) { + tp->mcfg = CFG_METHOD_22; + } else { + tp->mcfg = CFG_METHOD_22; + tp->HwIcVerUnknown = TRUE; + } + tp->efuse_ver = EFUSE_SUPPORT_V3; + break; + case 0x50000000: + if (ICVerID == 0x00000000) { + tp->mcfg = CFG_METHOD_23; + } else if (ICVerID == 0x00100000) { + tp->mcfg = CFG_METHOD_27; + } else if (ICVerID == 0x00200000) { + tp->mcfg = CFG_METHOD_28; + } else { + tp->mcfg = CFG_METHOD_28; + tp->HwIcVerUnknown = TRUE; + } + tp->efuse_ver = EFUSE_SUPPORT_V3; + break; + case 0x50800000: + if (ICVerID == 0x00000000) { + tp->mcfg = CFG_METHOD_24; + } else if (ICVerID == 0x00100000) { + tp->mcfg = CFG_METHOD_25; + } else { + tp->mcfg = CFG_METHOD_25; + tp->HwIcVerUnknown = TRUE; + } + tp->efuse_ver = EFUSE_SUPPORT_V3; + break; + case 0x5C800000: + if (ICVerID == 0x00000000) { + tp->mcfg = CFG_METHOD_26; + } else { + tp->mcfg = CFG_METHOD_26; + tp->HwIcVerUnknown = TRUE; + } + + tp->efuse_ver = EFUSE_SUPPORT_V3; + break; + case 0x54000000: + if (ICVerID == 0x00000000) { + tp->mcfg = CFG_METHOD_29; + } else if (ICVerID == 0x00100000) { + tp->mcfg = CFG_METHOD_30; + } else { + tp->mcfg = CFG_METHOD_30; + tp->HwIcVerUnknown = TRUE; + } + + tp->efuse_ver = EFUSE_SUPPORT_V3; + break; + case 0x54800000: + if (ICVerID == 0x00100000) { + tp->mcfg = CFG_METHOD_31; + } else if (ICVerID == 0x00200000) { + tp->mcfg = CFG_METHOD_32; + } else if (ICVerID == 0x00300000) { + tp->mcfg = CFG_METHOD_33; + } else { + tp->mcfg = CFG_METHOD_33; + tp->HwIcVerUnknown = TRUE; + } + + tp->efuse_ver = EFUSE_SUPPORT_V3; + break; + default: + printk("unknown chip version (%x)\n",reg); + tp->mcfg = CFG_METHOD_DEFAULT; + tp->HwIcVerUnknown = TRUE; + tp->efuse_ver = EFUSE_NOT_SUPPORT; + break; + } +} + +static void +rtl8168_print_mac_version(struct rtl8168_private *tp) +{ + int i; + for (i = ARRAY_SIZE(rtl_chip_info) - 1; i >= 0; i--) { + if (tp->mcfg == rtl_chip_info[i].mcfg) { + dprintk("Realtek PCIe GbE Family Controller mcfg = %04d\n", + rtl_chip_info[i].mcfg); + return; + } + } + + dprintk("mac_version == Unknown\n"); +} + +static u8 rtl8168_calc_efuse_dummy_bit(u16 reg) +{ + int s,a,b; + u8 dummyBitPos = 0; + + + s=reg% 32; + a=s % 16; + b=s/16; + + if (s/16) { + dummyBitPos = (u8)(16-a); + } else { + dummyBitPos = (u8)a; + } + + return dummyBitPos; +} + +static u32 rtl8168_decode_efuse_cmd(struct rtl8168_private *tp, u32 DwCmd) +{ + u16 reg = (u16)((DwCmd & 0x00FE0000) >> 17); + u32 DummyPos = rtl8168_calc_efuse_dummy_bit(reg); + u32 DeCodeDwCmd = DwCmd; + u32 Dw17BitData; + + + if (tp->efuse_ver < 3) { + DeCodeDwCmd = (DwCmd>>(DummyPos+1))< 0) { + DeCodeDwCmd |= ((DwCmd<<(32-DummyPos))>>(32-DummyPos)); + } + } else { + reg = (u16)((DwCmd & 0x007F0000) >> 16); + DummyPos = rtl8168_calc_efuse_dummy_bit(reg); + Dw17BitData = ((DwCmd & BIT_23) >> 23); + Dw17BitData <<= 16; + Dw17BitData |= (DwCmd & 0x0000FFFF); + DeCodeDwCmd = (Dw17BitData>>(DummyPos+1))< 0) { + DeCodeDwCmd |= ((Dw17BitData<<(32-DummyPos))>>(32-DummyPos)); + } + } + + return DeCodeDwCmd; +} + +static u8 rtl8168_efuse_read(struct rtl8168_private *tp, u16 reg) +{ + u8 efuse_data = 0; + u32 temp; + int cnt; + + if (tp->efuse_ver == EFUSE_NOT_SUPPORT) + return EFUSE_READ_FAIL; + + if (tp->efuse_ver == EFUSE_SUPPORT_V1) { + temp = EFUSE_READ | ((reg & EFUSE_Reg_Mask) << EFUSE_Reg_Shift); + RTL_W32(tp, EFUSEAR, temp); + + cnt = 0; + do { + udelay(100); + temp = RTL_R32(tp, EFUSEAR); + cnt++; + } while (!(temp & EFUSE_READ_OK) && (cnt < EFUSE_Check_Cnt)); + + if (cnt == EFUSE_Check_Cnt) + efuse_data = EFUSE_READ_FAIL; + else + efuse_data = (u8)(RTL_R32(tp, EFUSEAR) & EFUSE_Data_Mask); + } else if (tp->efuse_ver == EFUSE_SUPPORT_V2) { + temp = (reg/2) & 0x03ff; + temp <<= 17; + temp |= EFUSE_READ; + RTL_W32(tp, EFUSEAR, temp); + + cnt = 0; + do { + udelay(100); + temp = RTL_R32(tp, EFUSEAR); + cnt++; + } while (!(temp & EFUSE_READ_OK) && (cnt < EFUSE_Check_Cnt)); + + if (cnt == EFUSE_Check_Cnt) { + efuse_data = EFUSE_READ_FAIL; + } else { + temp = RTL_R32(tp, EFUSEAR); + temp = rtl8168_decode_efuse_cmd(tp, temp); + + if (reg%2) { + temp >>= 8; + efuse_data = (u8)temp; + } else { + efuse_data = (u8)temp; + } + } + } else if (tp->efuse_ver == EFUSE_SUPPORT_V3) { + temp = (reg/2) & 0x03ff; + temp <<= 16; + temp |= EFUSE_READ_V3; + RTL_W32(tp, EFUSEAR, temp); + + cnt = 0; + do { + udelay(100); + temp = RTL_R32(tp, EFUSEAR); + cnt++; + } while ((temp & BIT_31) && (cnt < EFUSE_Check_Cnt)); + + if (cnt == EFUSE_Check_Cnt) { + efuse_data = EFUSE_READ_FAIL; + } else { + temp = RTL_R32(tp, EFUSEAR); + temp = rtl8168_decode_efuse_cmd(tp, temp); + + if (reg%2) { + temp >>= 8; + efuse_data = (u8)temp; + } else { + efuse_data = (u8)temp; + } + } + } + + udelay(20); + + return efuse_data; +} + +static void +rtl8168_tally_counter_addr_fill(struct rtl8168_private *tp) +{ + if (!tp->tally_paddr) + return; + + RTL_W32(tp, CounterAddrHigh, (u64)tp->tally_paddr >> 32); + RTL_W32(tp, CounterAddrLow, (u64)tp->tally_paddr & (DMA_BIT_MASK(32))); +} + +static void +rtl8168_tally_counter_clear(struct rtl8168_private *tp) +{ + if (tp->mcfg == CFG_METHOD_1 || tp->mcfg == CFG_METHOD_2 || + tp->mcfg == CFG_METHOD_3 ) + return; + + if (!tp->tally_paddr) + return; + + RTL_W32(tp, CounterAddrHigh, (u64)tp->tally_paddr >> 32); + RTL_W32(tp, CounterAddrLow, ((u64)tp->tally_paddr & (DMA_BIT_MASK(32))) | CounterReset); +} + +static +u16 +rtl8168_get_phy_state(struct rtl8168_private *tp) +{ + u16 PhyState = 0xFF; + + if (HW_SUPPORT_UPS_MODE(tp) == FALSE) goto exit; + + switch (tp->HwSuppUpsVer) { + case 1: + PhyState = rtl8168_mdio_read_phy_ocp(tp, 0x0A42, 0x10); + PhyState &= 0x7; //bit[2:0] + break; + } + +exit: + return PhyState; +} + +static +bool +rtl8168_wait_phy_state_ready(struct rtl8168_private *tp, + u16 PhyState, + u32 MicroSecondTimeout + ) +{ + u16 TmpPhyState; + u32 WaitCount; + u32 i = 0; + bool PhyStateReady = TRUE; + + if (HW_SUPPORT_UPS_MODE(tp) == FALSE) goto exit; + + WaitCount = MicroSecondTimeout / 1000; + if (WaitCount == 0) WaitCount = 100; + + do { + TmpPhyState = rtl8168_get_phy_state(tp); + mdelay(1); + i++; + } while ((i < WaitCount) && (TmpPhyState != PhyState)); + + PhyStateReady = (i == WaitCount && TmpPhyState != PhyState) ? FALSE : TRUE; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) + WARN_ON_ONCE(i == WaitCount); +#endif + +exit: + return PhyStateReady; +} + +static +bool +rtl8168_test_phy_ocp(struct rtl8168_private *tp) +{ + bool RestorePhyOcpReg = FALSE; + + if (tp->TestPhyOcpReg == FALSE) goto exit; + + if (tp->HwSuppEsdVer == 2) { + u16 PhyRegValue; + u8 ResetPhyType = 0; + + if (HW_PHY_STATUS_INI == rtl8168_get_phy_state(tp)) { + ResetPhyType = 1; + } else { + rtl8168_mdio_write(tp, 0x1F, 0x0C40); + PhyRegValue = rtl8168_mdio_read(tp, 0x12); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + if ((PhyRegValue & 0x03) != 0x00) { + ResetPhyType = 2; + } + } + + if (ResetPhyType > 0) { + u32 WaitCnt; + struct net_device *dev = tp->dev; + + printk(KERN_ERR "%s: test_phy_ocp ResetPhyType = 0x%02x\n.\n", dev->name, ResetPhyType); + + rtl8168_mdio_write(tp, 0x1F, 0x0C41); + rtl8168_set_eth_phy_bit(tp, 0x14, BIT_0); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + mdelay(24); //24ms + + rtl8168_mdio_write(tp, 0x1F, 0x0C40); + PhyRegValue = rtl8168_mdio_read(tp, 0x12); + if ((PhyRegValue & 0x03) != 0x00) { + WaitCnt = 0; + while ((PhyRegValue & 0x03) != 0x00 && WaitCnt < 5) { + rtl8168_mdio_write(tp, 0x1F, 0x0C40); + rtl8168_set_eth_phy_bit(tp, 0x11, (BIT_15 | BIT_14)); + rtl8168_clear_eth_phy_bit(tp, 0x11, (BIT_15 | BIT_14)); + mdelay(100); + rtl8168_mdio_write(tp, 0x1F, 0x0C40); + PhyRegValue = rtl8168_mdio_read(tp, 0x12); + WaitCnt++; + } + } + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0A46); + rtl8168_mdio_write(tp, 0x10, tp->BackupPhyFuseDout_15_0); + rtl8168_mdio_write(tp, 0x12, tp->BackupPhyFuseDout_47_32); + rtl8168_mdio_write(tp, 0x13, tp->BackupPhyFuseDout_63_48); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_wait_phy_state_ready(tp, HW_PHY_STATUS_INI, 5000000); + rtl8168_mdio_write(tp, 0x1F, 0x0A46); + rtl8168_set_eth_phy_bit(tp, 0x14, BIT_0); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_wait_phy_state_ready(tp, HW_PHY_STATUS_LAN_ON, 500000); + + tp->HwHasWrRamCodeToMicroP = FALSE; + + RestorePhyOcpReg = TRUE; + } + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } + +exit: + return RestorePhyOcpReg; +} + +static int +rtl8168_is_ups_resume(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + return (rtl8168_mac_ocp_read(tp, 0xD408) & BIT_0); +} + +static void +rtl8168_clear_ups_resume_bit(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + rtl8168_mac_ocp_write(tp, 0xD408, rtl8168_mac_ocp_read(tp, 0xD408) & ~(BIT_0)); +} + +static void +rtl8168_wait_phy_ups_resume(struct net_device *dev, u16 PhyState) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u16 TmpPhyState; + int i = 0; + + do { + TmpPhyState = rtl8168_mdio_read_phy_ocp(tp, 0x0A42, 0x10); + TmpPhyState &= 0x7; + mdelay(1); + i++; + } while ((i < 100) && (TmpPhyState != PhyState)); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) + WARN_ON_ONCE(i == 100); +#endif +} + +void +rtl8168_enable_now_is_oob(struct rtl8168_private *tp) +{ + if ( tp->HwSuppNowIsOobVer == 1 ) { + RTL_W8(tp, MCUCmd_reg, RTL_R8(tp, MCUCmd_reg) | Now_is_oob); + } +} + +void +rtl8168_disable_now_is_oob(struct rtl8168_private *tp) +{ + if ( tp->HwSuppNowIsOobVer == 1 ) { + RTL_W8(tp, MCUCmd_reg, RTL_R8(tp, MCUCmd_reg) & ~Now_is_oob); + } +} + +static void +rtl8168_switch_to_sgmii_mode( + struct rtl8168_private *tp +) +{ + if (FALSE == HW_SUPP_SERDES_PHY(tp)) return; + + switch (tp->HwSuppSerDesPhyVer) { + case 1: + rtl8168_mac_ocp_write(tp, 0xEB00, 0x2); + rtl8168_set_mcu_ocp_bit(tp, 0xEB16, BIT_1); + break; + } +} + +static void +rtl8168_exit_oob(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u16 data16; + + RTL_W32(tp, RxConfig, RTL_R32(tp, RxConfig) & ~(AcceptErr | AcceptRunt | AcceptBroadcast | AcceptMulticast | AcceptMyPhys | AcceptAllPhys)); + + if (HW_SUPP_SERDES_PHY(tp)) { + if (tp->HwSuppSerDesPhyVer == 1) { + rtl8168_switch_to_sgmii_mode(tp); + } + } + + if (HW_DASH_SUPPORT_DASH(tp)) { + rtl8168_driver_start(tp); + rtl8168_dash2_disable_txrx(dev); +#ifdef ENABLE_DASH_SUPPORT + DashHwInit(dev); +#endif + } + + //Disable realwow function + switch (tp->mcfg) { + case CFG_METHOD_18: + case CFG_METHOD_19: + RTL_W32(tp, MACOCP, 0xE5A90000); + RTL_W32(tp, MACOCP, 0xF2100010); + break; + case CFG_METHOD_20: + RTL_W32(tp, MACOCP, 0xE5A90000); + RTL_W32(tp, MACOCP, 0xE4640000); + RTL_W32(tp, MACOCP, 0xF2100010); + break; + case CFG_METHOD_21: + case CFG_METHOD_22: + RTL_W32(tp, MACOCP, 0x605E0000); + RTL_W32(tp, MACOCP, (0xE05E << 16) | (RTL_R32(tp, MACOCP) & 0xFFFE)); + RTL_W32(tp, MACOCP, 0xE9720000); + RTL_W32(tp, MACOCP, 0xF2140010); + break; + case CFG_METHOD_26: + RTL_W32(tp, MACOCP, 0xE05E00FF); + RTL_W32(tp, MACOCP, 0xE9720000); + rtl8168_mac_ocp_write(tp, 0xE428, 0x0010); + break; + } + +#ifdef ENABLE_REALWOW_SUPPORT + rtl8168_realwow_hw_init(dev); +#else + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + rtl8168_eri_write(tp, 0x174, 2, 0x0000, ERIAR_ExGMAC); + rtl8168_mac_ocp_write(tp, 0xE428, 0x0010); + break; + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_28: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_eri_write(tp, 0x174, 2, 0x00FF, ERIAR_ExGMAC); + rtl8168_mac_ocp_write(tp, 0xE428, 0x0010); + break; + case CFG_METHOD_29: + case CFG_METHOD_30: { + u32 csi_tmp; + csi_tmp = rtl8168_eri_read(tp, 0x174, 2, ERIAR_ExGMAC); + csi_tmp &= ~(BIT_8); + csi_tmp |= (BIT_15); + rtl8168_eri_write(tp, 0x174, 2, csi_tmp, ERIAR_ExGMAC); + rtl8168_mac_ocp_write(tp, 0xE428, 0x0010); + } + break; + } +#endif //ENABLE_REALWOW_SUPPORT + + rtl8168_nic_reset(dev); + + switch (tp->mcfg) { + case CFG_METHOD_20: + rtl8168_wait_ll_share_fifo_ready(dev); + + data16 = rtl8168_mac_ocp_read(tp, 0xD4DE) | BIT_15; + rtl8168_mac_ocp_write(tp, 0xD4DE, data16); + + rtl8168_wait_ll_share_fifo_ready(dev); + break; + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_disable_now_is_oob(tp); + + data16 = rtl8168_mac_ocp_read(tp, 0xE8DE) & ~BIT_14; + rtl8168_mac_ocp_write(tp, 0xE8DE, data16); + rtl8168_wait_ll_share_fifo_ready(dev); + + data16 = rtl8168_mac_ocp_read(tp, 0xE8DE) | BIT_15; + rtl8168_mac_ocp_write(tp, 0xE8DE, data16); + + rtl8168_wait_ll_share_fifo_ready(dev); + break; + } + + //wait ups resume (phy state 2) + if (HW_SUPPORT_UPS_MODE(tp)) + if (rtl8168_is_ups_resume(dev)) { + rtl8168_wait_phy_ups_resume(dev, HW_PHY_STATUS_EXT_INI); + rtl8168_clear_ups_resume_bit(dev); + } + +#ifdef ENABLE_FIBER_SUPPORT + if (HW_FIBER_MODE_ENABLED(tp)) + rtl8168_hw_init_fiber_nic(dev); +#endif //ENABLE_FIBER_SUPPORT +} + +void +rtl8168_hw_disable_mac_mcu_bps(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + if (tp->HwSuppAspmClkIntrLock) { + rtl8168_enable_cfg9346_write(tp); + rtl8168_hw_aspm_clkreq_enable(tp, false); + rtl8168_disable_cfg9346_write(tp); + } + + switch (tp->mcfg) { + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_mac_ocp_write(tp, 0xFC38, 0x0000); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_mac_ocp_write(tp, 0xFC28, 0x0000); + rtl8168_mac_ocp_write(tp, 0xFC2A, 0x0000); + rtl8168_mac_ocp_write(tp, 0xFC2C, 0x0000); + rtl8168_mac_ocp_write(tp, 0xFC2E, 0x0000); + rtl8168_mac_ocp_write(tp, 0xFC30, 0x0000); + rtl8168_mac_ocp_write(tp, 0xFC32, 0x0000); + rtl8168_mac_ocp_write(tp, 0xFC34, 0x0000); + rtl8168_mac_ocp_write(tp, 0xFC36, 0x0000); + mdelay(3); + rtl8168_mac_ocp_write(tp, 0xFC26, 0x0000); + break; + } +} + +#ifndef ENABLE_USE_FIRMWARE_FILE +static void +rtl8168_set_mac_mcu_8168g_1(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + rtl8168_mac_ocp_write(tp, 0xE43C, 0x0000); + rtl8168_mac_ocp_write(tp, 0xE43E, 0x0000); + + rtl8168_mac_ocp_write(tp, 0xE434, 0x0004); + rtl8168_mac_ocp_write(tp, 0xE43C, 0x0004); + + rtl8168_hw_disable_mac_mcu_bps(dev); + + rtl8168_mac_ocp_write( tp, 0xF800, 0xE008 ); + rtl8168_mac_ocp_write( tp, 0xF802, 0xE01B ); + rtl8168_mac_ocp_write( tp, 0xF804, 0xE022 ); + rtl8168_mac_ocp_write( tp, 0xF806, 0xE094 ); + rtl8168_mac_ocp_write( tp, 0xF808, 0xE097 ); + rtl8168_mac_ocp_write( tp, 0xF80A, 0xE09A ); + rtl8168_mac_ocp_write( tp, 0xF80C, 0xE0B3 ); + rtl8168_mac_ocp_write( tp, 0xF80E, 0xE0BA ); + rtl8168_mac_ocp_write( tp, 0xF810, 0x49D2 ); + rtl8168_mac_ocp_write( tp, 0xF812, 0xF10D ); + rtl8168_mac_ocp_write( tp, 0xF814, 0x766C ); + rtl8168_mac_ocp_write( tp, 0xF816, 0x49E2 ); + rtl8168_mac_ocp_write( tp, 0xF818, 0xF00A ); + rtl8168_mac_ocp_write( tp, 0xF81A, 0x1EC0 ); + rtl8168_mac_ocp_write( tp, 0xF81C, 0x8EE1 ); + rtl8168_mac_ocp_write( tp, 0xF81E, 0xC60A ); + rtl8168_mac_ocp_write( tp, 0xF820, 0x77C0 ); + rtl8168_mac_ocp_write( tp, 0xF822, 0x4870 ); + rtl8168_mac_ocp_write( tp, 0xF824, 0x9FC0 ); + rtl8168_mac_ocp_write( tp, 0xF826, 0x1EA0 ); + rtl8168_mac_ocp_write( tp, 0xF828, 0xC707 ); + rtl8168_mac_ocp_write( tp, 0xF82A, 0x8EE1 ); + rtl8168_mac_ocp_write( tp, 0xF82C, 0x9D6C ); + rtl8168_mac_ocp_write( tp, 0xF82E, 0xC603 ); + rtl8168_mac_ocp_write( tp, 0xF830, 0xBE00 ); + rtl8168_mac_ocp_write( tp, 0xF832, 0xB416 ); + rtl8168_mac_ocp_write( tp, 0xF834, 0x0076 ); + rtl8168_mac_ocp_write( tp, 0xF836, 0xE86C ); + rtl8168_mac_ocp_write( tp, 0xF838, 0xC406 ); + rtl8168_mac_ocp_write( tp, 0xF83A, 0x7580 ); + rtl8168_mac_ocp_write( tp, 0xF83C, 0x4852 ); + rtl8168_mac_ocp_write( tp, 0xF83E, 0x8D80 ); + rtl8168_mac_ocp_write( tp, 0xF840, 0xC403 ); + rtl8168_mac_ocp_write( tp, 0xF842, 0xBC00 ); + rtl8168_mac_ocp_write( tp, 0xF844, 0xD3E0 ); + rtl8168_mac_ocp_write( tp, 0xF846, 0x02C8 ); + rtl8168_mac_ocp_write( tp, 0xF848, 0x8918 ); + rtl8168_mac_ocp_write( tp, 0xF84A, 0xE815 ); + rtl8168_mac_ocp_write( tp, 0xF84C, 0x1100 ); + rtl8168_mac_ocp_write( tp, 0xF84E, 0xF011 ); + rtl8168_mac_ocp_write( tp, 0xF850, 0xE812 ); + rtl8168_mac_ocp_write( tp, 0xF852, 0x4990 ); + rtl8168_mac_ocp_write( tp, 0xF854, 0xF002 ); + rtl8168_mac_ocp_write( tp, 0xF856, 0xE817 ); + rtl8168_mac_ocp_write( tp, 0xF858, 0xE80E ); + rtl8168_mac_ocp_write( tp, 0xF85A, 0x4992 ); + rtl8168_mac_ocp_write( tp, 0xF85C, 0xF002 ); + rtl8168_mac_ocp_write( tp, 0xF85E, 0xE80E ); + rtl8168_mac_ocp_write( tp, 0xF860, 0xE80A ); + rtl8168_mac_ocp_write( tp, 0xF862, 0x4993 ); + rtl8168_mac_ocp_write( tp, 0xF864, 0xF002 ); + rtl8168_mac_ocp_write( tp, 0xF866, 0xE818 ); + rtl8168_mac_ocp_write( tp, 0xF868, 0xE806 ); + rtl8168_mac_ocp_write( tp, 0xF86A, 0x4991 ); + rtl8168_mac_ocp_write( tp, 0xF86C, 0xF002 ); + rtl8168_mac_ocp_write( tp, 0xF86E, 0xE838 ); + rtl8168_mac_ocp_write( tp, 0xF870, 0xC25E ); + rtl8168_mac_ocp_write( tp, 0xF872, 0xBA00 ); + rtl8168_mac_ocp_write( tp, 0xF874, 0xC056 ); + rtl8168_mac_ocp_write( tp, 0xF876, 0x7100 ); + rtl8168_mac_ocp_write( tp, 0xF878, 0xFF80 ); + rtl8168_mac_ocp_write( tp, 0xF87A, 0x7100 ); + rtl8168_mac_ocp_write( tp, 0xF87C, 0x4892 ); + rtl8168_mac_ocp_write( tp, 0xF87E, 0x4813 ); + rtl8168_mac_ocp_write( tp, 0xF880, 0x8900 ); + rtl8168_mac_ocp_write( tp, 0xF882, 0xE00A ); + rtl8168_mac_ocp_write( tp, 0xF884, 0x7100 ); + rtl8168_mac_ocp_write( tp, 0xF886, 0x4890 ); + rtl8168_mac_ocp_write( tp, 0xF888, 0x4813 ); + rtl8168_mac_ocp_write( tp, 0xF88A, 0x8900 ); + rtl8168_mac_ocp_write( tp, 0xF88C, 0xC74B ); + rtl8168_mac_ocp_write( tp, 0xF88E, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xF890, 0x48C2 ); + rtl8168_mac_ocp_write( tp, 0xF892, 0x4841 ); + rtl8168_mac_ocp_write( tp, 0xF894, 0x8CF8 ); + rtl8168_mac_ocp_write( tp, 0xF896, 0xC746 ); + rtl8168_mac_ocp_write( tp, 0xF898, 0x74FC ); + rtl8168_mac_ocp_write( tp, 0xF89A, 0x49C0 ); + rtl8168_mac_ocp_write( tp, 0xF89C, 0xF120 ); + rtl8168_mac_ocp_write( tp, 0xF89E, 0x49C1 ); + rtl8168_mac_ocp_write( tp, 0xF8A0, 0xF11E ); + rtl8168_mac_ocp_write( tp, 0xF8A2, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xF8A4, 0x49C0 ); + rtl8168_mac_ocp_write( tp, 0xF8A6, 0xF01B ); + rtl8168_mac_ocp_write( tp, 0xF8A8, 0x49C6 ); + rtl8168_mac_ocp_write( tp, 0xF8AA, 0xF119 ); + rtl8168_mac_ocp_write( tp, 0xF8AC, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xF8AE, 0x49C4 ); + rtl8168_mac_ocp_write( tp, 0xF8B0, 0xF013 ); + rtl8168_mac_ocp_write( tp, 0xF8B2, 0xC536 ); + rtl8168_mac_ocp_write( tp, 0xF8B4, 0x74B0 ); + rtl8168_mac_ocp_write( tp, 0xF8B6, 0x49C1 ); + rtl8168_mac_ocp_write( tp, 0xF8B8, 0xF1FD ); + rtl8168_mac_ocp_write( tp, 0xF8BA, 0xC537 ); + rtl8168_mac_ocp_write( tp, 0xF8BC, 0xC434 ); + rtl8168_mac_ocp_write( tp, 0xF8BE, 0x9CA0 ); + rtl8168_mac_ocp_write( tp, 0xF8C0, 0xC435 ); + rtl8168_mac_ocp_write( tp, 0xF8C2, 0x1C13 ); + rtl8168_mac_ocp_write( tp, 0xF8C4, 0x484F ); + rtl8168_mac_ocp_write( tp, 0xF8C6, 0x9CA2 ); + rtl8168_mac_ocp_write( tp, 0xF8C8, 0xC52B ); + rtl8168_mac_ocp_write( tp, 0xF8CA, 0x74B0 ); + rtl8168_mac_ocp_write( tp, 0xF8CC, 0x49C1 ); + rtl8168_mac_ocp_write( tp, 0xF8CE, 0xF1FD ); + rtl8168_mac_ocp_write( tp, 0xF8D0, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xF8D2, 0x48C4 ); + rtl8168_mac_ocp_write( tp, 0xF8D4, 0x8CF8 ); + rtl8168_mac_ocp_write( tp, 0xF8D6, 0x7100 ); + rtl8168_mac_ocp_write( tp, 0xF8D8, 0x4893 ); + rtl8168_mac_ocp_write( tp, 0xF8DA, 0x8900 ); + rtl8168_mac_ocp_write( tp, 0xF8DC, 0xFF80 ); + rtl8168_mac_ocp_write( tp, 0xF8DE, 0xC520 ); + rtl8168_mac_ocp_write( tp, 0xF8E0, 0x74B0 ); + rtl8168_mac_ocp_write( tp, 0xF8E2, 0x49C1 ); + rtl8168_mac_ocp_write( tp, 0xF8E4, 0xF11C ); + rtl8168_mac_ocp_write( tp, 0xF8E6, 0xC71E ); + rtl8168_mac_ocp_write( tp, 0xF8E8, 0x74FC ); + rtl8168_mac_ocp_write( tp, 0xF8EA, 0x49C1 ); + rtl8168_mac_ocp_write( tp, 0xF8EC, 0xF118 ); + rtl8168_mac_ocp_write( tp, 0xF8EE, 0x49C0 ); + rtl8168_mac_ocp_write( tp, 0xF8F0, 0xF116 ); + rtl8168_mac_ocp_write( tp, 0xF8F2, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xF8F4, 0x49C0 ); + rtl8168_mac_ocp_write( tp, 0xF8F6, 0xF013 ); + rtl8168_mac_ocp_write( tp, 0xF8F8, 0x48C3 ); + rtl8168_mac_ocp_write( tp, 0xF8FA, 0x8CF8 ); + rtl8168_mac_ocp_write( tp, 0xF8FC, 0xC516 ); + rtl8168_mac_ocp_write( tp, 0xF8FE, 0x74A2 ); + rtl8168_mac_ocp_write( tp, 0xF900, 0x49CE ); + rtl8168_mac_ocp_write( tp, 0xF902, 0xF1FE ); + rtl8168_mac_ocp_write( tp, 0xF904, 0xC411 ); + rtl8168_mac_ocp_write( tp, 0xF906, 0x9CA0 ); + rtl8168_mac_ocp_write( tp, 0xF908, 0xC411 ); + rtl8168_mac_ocp_write( tp, 0xF90A, 0x1C13 ); + rtl8168_mac_ocp_write( tp, 0xF90C, 0x484F ); + rtl8168_mac_ocp_write( tp, 0xF90E, 0x9CA2 ); + rtl8168_mac_ocp_write( tp, 0xF910, 0x74A2 ); + rtl8168_mac_ocp_write( tp, 0xF912, 0x49CF ); + rtl8168_mac_ocp_write( tp, 0xF914, 0xF1FE ); + rtl8168_mac_ocp_write( tp, 0xF916, 0x7100 ); + rtl8168_mac_ocp_write( tp, 0xF918, 0x4891 ); + rtl8168_mac_ocp_write( tp, 0xF91A, 0x8900 ); + rtl8168_mac_ocp_write( tp, 0xF91C, 0xFF80 ); + rtl8168_mac_ocp_write( tp, 0xF91E, 0xE400 ); + rtl8168_mac_ocp_write( tp, 0xF920, 0xD3E0 ); + rtl8168_mac_ocp_write( tp, 0xF922, 0xE000 ); + rtl8168_mac_ocp_write( tp, 0xF924, 0x0481 ); + rtl8168_mac_ocp_write( tp, 0xF926, 0x0C81 ); + rtl8168_mac_ocp_write( tp, 0xF928, 0xDE20 ); + rtl8168_mac_ocp_write( tp, 0xF92A, 0x0000 ); + rtl8168_mac_ocp_write( tp, 0xF92C, 0x0992 ); + rtl8168_mac_ocp_write( tp, 0xF92E, 0x1B76 ); + rtl8168_mac_ocp_write( tp, 0xF930, 0xC602 ); + rtl8168_mac_ocp_write( tp, 0xF932, 0xBE00 ); + rtl8168_mac_ocp_write( tp, 0xF934, 0x059C ); + rtl8168_mac_ocp_write( tp, 0xF936, 0x1B76 ); + rtl8168_mac_ocp_write( tp, 0xF938, 0xC602 ); + rtl8168_mac_ocp_write( tp, 0xF93A, 0xBE00 ); + rtl8168_mac_ocp_write( tp, 0xF93C, 0x065A ); + rtl8168_mac_ocp_write( tp, 0xF93E, 0xB400 ); + rtl8168_mac_ocp_write( tp, 0xF940, 0x18DE ); + rtl8168_mac_ocp_write( tp, 0xF942, 0x2008 ); + rtl8168_mac_ocp_write( tp, 0xF944, 0x4001 ); + rtl8168_mac_ocp_write( tp, 0xF946, 0xF10F ); + rtl8168_mac_ocp_write( tp, 0xF948, 0x7342 ); + rtl8168_mac_ocp_write( tp, 0xF94A, 0x1880 ); + rtl8168_mac_ocp_write( tp, 0xF94C, 0x2008 ); + rtl8168_mac_ocp_write( tp, 0xF94E, 0x0009 ); + rtl8168_mac_ocp_write( tp, 0xF950, 0x4018 ); + rtl8168_mac_ocp_write( tp, 0xF952, 0xF109 ); + rtl8168_mac_ocp_write( tp, 0xF954, 0x7340 ); + rtl8168_mac_ocp_write( tp, 0xF956, 0x25BC ); + rtl8168_mac_ocp_write( tp, 0xF958, 0x130F ); + rtl8168_mac_ocp_write( tp, 0xF95A, 0xF105 ); + rtl8168_mac_ocp_write( tp, 0xF95C, 0xC00A ); + rtl8168_mac_ocp_write( tp, 0xF95E, 0x7300 ); + rtl8168_mac_ocp_write( tp, 0xF960, 0x4831 ); + rtl8168_mac_ocp_write( tp, 0xF962, 0x9B00 ); + rtl8168_mac_ocp_write( tp, 0xF964, 0xB000 ); + rtl8168_mac_ocp_write( tp, 0xF966, 0x7340 ); + rtl8168_mac_ocp_write( tp, 0xF968, 0x8320 ); + rtl8168_mac_ocp_write( tp, 0xF96A, 0xC302 ); + rtl8168_mac_ocp_write( tp, 0xF96C, 0xBB00 ); + rtl8168_mac_ocp_write( tp, 0xF96E, 0x0C12 ); + rtl8168_mac_ocp_write( tp, 0xF970, 0xE860 ); + rtl8168_mac_ocp_write( tp, 0xF972, 0xC406 ); + rtl8168_mac_ocp_write( tp, 0xF974, 0x7580 ); + rtl8168_mac_ocp_write( tp, 0xF976, 0x4851 ); + rtl8168_mac_ocp_write( tp, 0xF978, 0x8D80 ); + rtl8168_mac_ocp_write( tp, 0xF97A, 0xC403 ); + rtl8168_mac_ocp_write( tp, 0xF97C, 0xBC00 ); + rtl8168_mac_ocp_write( tp, 0xF97E, 0xD3E0 ); + rtl8168_mac_ocp_write( tp, 0xF980, 0x02C8 ); + rtl8168_mac_ocp_write( tp, 0xF982, 0xC406 ); + rtl8168_mac_ocp_write( tp, 0xF984, 0x7580 ); + rtl8168_mac_ocp_write( tp, 0xF986, 0x4850 ); + rtl8168_mac_ocp_write( tp, 0xF988, 0x8D80 ); + rtl8168_mac_ocp_write( tp, 0xF98A, 0xC403 ); + rtl8168_mac_ocp_write( tp, 0xF98C, 0xBC00 ); + rtl8168_mac_ocp_write( tp, 0xF98E, 0xD3E0 ); + rtl8168_mac_ocp_write( tp, 0xF990, 0x0298 ); + + rtl8168_mac_ocp_write( tp, 0xDE30, 0x0080 ); + + rtl8168_mac_ocp_write( tp, 0xFC26, 0x8000 ); + + rtl8168_mac_ocp_write( tp, 0xFC28, 0x0075 ); + rtl8168_mac_ocp_write( tp, 0xFC2A, 0x02B1 ); + rtl8168_mac_ocp_write( tp, 0xFC2C, 0x0991 ); + rtl8168_mac_ocp_write( tp, 0xFC2E, 0x059B ); + rtl8168_mac_ocp_write( tp, 0xFC30, 0x0659 ); + rtl8168_mac_ocp_write( tp, 0xFC32, 0x0000 ); + rtl8168_mac_ocp_write( tp, 0xFC34, 0x02C7 ); + rtl8168_mac_ocp_write( tp, 0xFC36, 0x0279 ); +} + +static void +rtl8168_set_mac_mcu_8168gu_1(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + rtl8168_hw_disable_mac_mcu_bps(dev); + + rtl8168_mac_ocp_write( tp, 0xF800, 0xE008 ); + rtl8168_mac_ocp_write( tp, 0xF802, 0xE011 ); + rtl8168_mac_ocp_write( tp, 0xF804, 0xE015 ); + rtl8168_mac_ocp_write( tp, 0xF806, 0xE018 ); + rtl8168_mac_ocp_write( tp, 0xF808, 0xE01B ); + rtl8168_mac_ocp_write( tp, 0xF80A, 0xE027 ); + rtl8168_mac_ocp_write( tp, 0xF80C, 0xE043 ); + rtl8168_mac_ocp_write( tp, 0xF80E, 0xE065 ); + rtl8168_mac_ocp_write( tp, 0xF810, 0x49E2 ); + rtl8168_mac_ocp_write( tp, 0xF812, 0xF005 ); + rtl8168_mac_ocp_write( tp, 0xF814, 0x49EA ); + rtl8168_mac_ocp_write( tp, 0xF816, 0xF003 ); + rtl8168_mac_ocp_write( tp, 0xF818, 0xC404 ); + rtl8168_mac_ocp_write( tp, 0xF81A, 0xBC00 ); + rtl8168_mac_ocp_write( tp, 0xF81C, 0xC403 ); + rtl8168_mac_ocp_write( tp, 0xF81E, 0xBC00 ); + rtl8168_mac_ocp_write( tp, 0xF820, 0x0496 ); + rtl8168_mac_ocp_write( tp, 0xF822, 0x051A ); + rtl8168_mac_ocp_write( tp, 0xF824, 0x1D01 ); + rtl8168_mac_ocp_write( tp, 0xF826, 0x8DE8 ); + rtl8168_mac_ocp_write( tp, 0xF828, 0xC602 ); + rtl8168_mac_ocp_write( tp, 0xF82A, 0xBE00 ); + rtl8168_mac_ocp_write( tp, 0xF82C, 0x0206 ); + rtl8168_mac_ocp_write( tp, 0xF82E, 0x1B76 ); + rtl8168_mac_ocp_write( tp, 0xF830, 0xC202 ); + rtl8168_mac_ocp_write( tp, 0xF832, 0xBA00 ); + rtl8168_mac_ocp_write( tp, 0xF834, 0x058A ); + rtl8168_mac_ocp_write( tp, 0xF836, 0x1B76 ); + rtl8168_mac_ocp_write( tp, 0xF838, 0xC602 ); + rtl8168_mac_ocp_write( tp, 0xF83A, 0xBE00 ); + rtl8168_mac_ocp_write( tp, 0xF83C, 0x0648 ); + rtl8168_mac_ocp_write( tp, 0xF83E, 0x74E6 ); + rtl8168_mac_ocp_write( tp, 0xF840, 0x1B78 ); + rtl8168_mac_ocp_write( tp, 0xF842, 0x46DC ); + rtl8168_mac_ocp_write( tp, 0xF844, 0x1300 ); + rtl8168_mac_ocp_write( tp, 0xF846, 0xF005 ); + rtl8168_mac_ocp_write( tp, 0xF848, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xF84A, 0x48C3 ); + rtl8168_mac_ocp_write( tp, 0xF84C, 0x48C4 ); + rtl8168_mac_ocp_write( tp, 0xF84E, 0x8CF8 ); + rtl8168_mac_ocp_write( tp, 0xF850, 0x64E7 ); + rtl8168_mac_ocp_write( tp, 0xF852, 0xC302 ); + rtl8168_mac_ocp_write( tp, 0xF854, 0xBB00 ); + rtl8168_mac_ocp_write( tp, 0xF856, 0x068E ); + rtl8168_mac_ocp_write( tp, 0xF858, 0x74E4 ); + rtl8168_mac_ocp_write( tp, 0xF85A, 0x49C5 ); + rtl8168_mac_ocp_write( tp, 0xF85C, 0xF106 ); + rtl8168_mac_ocp_write( tp, 0xF85E, 0x49C6 ); + rtl8168_mac_ocp_write( tp, 0xF860, 0xF107 ); + rtl8168_mac_ocp_write( tp, 0xF862, 0x48C8 ); + rtl8168_mac_ocp_write( tp, 0xF864, 0x48C9 ); + rtl8168_mac_ocp_write( tp, 0xF866, 0xE011 ); + rtl8168_mac_ocp_write( tp, 0xF868, 0x48C9 ); + rtl8168_mac_ocp_write( tp, 0xF86A, 0x4848 ); + rtl8168_mac_ocp_write( tp, 0xF86C, 0xE00E ); + rtl8168_mac_ocp_write( tp, 0xF86E, 0x4848 ); + rtl8168_mac_ocp_write( tp, 0xF870, 0x49C7 ); + rtl8168_mac_ocp_write( tp, 0xF872, 0xF00A ); + rtl8168_mac_ocp_write( tp, 0xF874, 0x48C9 ); + rtl8168_mac_ocp_write( tp, 0xF876, 0xC60D ); + rtl8168_mac_ocp_write( tp, 0xF878, 0x1D1F ); + rtl8168_mac_ocp_write( tp, 0xF87A, 0x8DC2 ); + rtl8168_mac_ocp_write( tp, 0xF87C, 0x1D00 ); + rtl8168_mac_ocp_write( tp, 0xF87E, 0x8DC3 ); + rtl8168_mac_ocp_write( tp, 0xF880, 0x1D11 ); + rtl8168_mac_ocp_write( tp, 0xF882, 0x8DC0 ); + rtl8168_mac_ocp_write( tp, 0xF884, 0xE002 ); + rtl8168_mac_ocp_write( tp, 0xF886, 0x4849 ); + rtl8168_mac_ocp_write( tp, 0xF888, 0x94E5 ); + rtl8168_mac_ocp_write( tp, 0xF88A, 0xC602 ); + rtl8168_mac_ocp_write( tp, 0xF88C, 0xBE00 ); + rtl8168_mac_ocp_write( tp, 0xF88E, 0x0238 ); + rtl8168_mac_ocp_write( tp, 0xF890, 0xE434 ); + rtl8168_mac_ocp_write( tp, 0xF892, 0x49D9 ); + rtl8168_mac_ocp_write( tp, 0xF894, 0xF01B ); + rtl8168_mac_ocp_write( tp, 0xF896, 0xC31E ); + rtl8168_mac_ocp_write( tp, 0xF898, 0x7464 ); + rtl8168_mac_ocp_write( tp, 0xF89A, 0x49C4 ); + rtl8168_mac_ocp_write( tp, 0xF89C, 0xF114 ); + rtl8168_mac_ocp_write( tp, 0xF89E, 0xC31B ); + rtl8168_mac_ocp_write( tp, 0xF8A0, 0x6460 ); + rtl8168_mac_ocp_write( tp, 0xF8A2, 0x14FA ); + rtl8168_mac_ocp_write( tp, 0xF8A4, 0xFA02 ); + rtl8168_mac_ocp_write( tp, 0xF8A6, 0xE00F ); + rtl8168_mac_ocp_write( tp, 0xF8A8, 0xC317 ); + rtl8168_mac_ocp_write( tp, 0xF8AA, 0x7460 ); + rtl8168_mac_ocp_write( tp, 0xF8AC, 0x49C0 ); + rtl8168_mac_ocp_write( tp, 0xF8AE, 0xF10B ); + rtl8168_mac_ocp_write( tp, 0xF8B0, 0xC311 ); + rtl8168_mac_ocp_write( tp, 0xF8B2, 0x7462 ); + rtl8168_mac_ocp_write( tp, 0xF8B4, 0x48C1 ); + rtl8168_mac_ocp_write( tp, 0xF8B6, 0x9C62 ); + rtl8168_mac_ocp_write( tp, 0xF8B8, 0x4841 ); + rtl8168_mac_ocp_write( tp, 0xF8BA, 0x9C62 ); + rtl8168_mac_ocp_write( tp, 0xF8BC, 0xC30A ); + rtl8168_mac_ocp_write( tp, 0xF8BE, 0x1C04 ); + rtl8168_mac_ocp_write( tp, 0xF8C0, 0x8C60 ); + rtl8168_mac_ocp_write( tp, 0xF8C2, 0xE004 ); + rtl8168_mac_ocp_write( tp, 0xF8C4, 0x1C15 ); + rtl8168_mac_ocp_write( tp, 0xF8C6, 0xC305 ); + rtl8168_mac_ocp_write( tp, 0xF8C8, 0x8C60 ); + rtl8168_mac_ocp_write( tp, 0xF8CA, 0xC602 ); + rtl8168_mac_ocp_write( tp, 0xF8CC, 0xBE00 ); + rtl8168_mac_ocp_write( tp, 0xF8CE, 0x0374 ); + rtl8168_mac_ocp_write( tp, 0xF8D0, 0xE434 ); + rtl8168_mac_ocp_write( tp, 0xF8D2, 0xE030 ); + rtl8168_mac_ocp_write( tp, 0xF8D4, 0xE61C ); + rtl8168_mac_ocp_write( tp, 0xF8D6, 0xE906 ); + rtl8168_mac_ocp_write( tp, 0xF8D8, 0xC602 ); + rtl8168_mac_ocp_write( tp, 0xF8DA, 0xBE00 ); + rtl8168_mac_ocp_write( tp, 0xF8DC, 0x0000 ); + + rtl8168_mac_ocp_write( tp, 0xFC26, 0x8000 ); + + rtl8168_mac_ocp_write( tp, 0xFC28, 0x0493 ); + rtl8168_mac_ocp_write( tp, 0xFC2A, 0x0205 ); + rtl8168_mac_ocp_write( tp, 0xFC2C, 0x0589 ); + rtl8168_mac_ocp_write( tp, 0xFC2E, 0x0647 ); + rtl8168_mac_ocp_write( tp, 0xFC30, 0x0000 ); + rtl8168_mac_ocp_write( tp, 0xFC32, 0x0215 ); + rtl8168_mac_ocp_write( tp, 0xFC34, 0x0285 ); +} + +static void +rtl8168_set_mac_mcu_8168gu_2(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + rtl8168_hw_disable_mac_mcu_bps(dev); + + rtl8168_mac_ocp_write( tp, 0xF800, 0xE008 ); + rtl8168_mac_ocp_write( tp, 0xF802, 0xE00A ); + rtl8168_mac_ocp_write( tp, 0xF804, 0xE00D ); + rtl8168_mac_ocp_write( tp, 0xF806, 0xE02F ); + rtl8168_mac_ocp_write( tp, 0xF808, 0xE031 ); + rtl8168_mac_ocp_write( tp, 0xF80A, 0xE038 ); + rtl8168_mac_ocp_write( tp, 0xF80C, 0xE03A ); + rtl8168_mac_ocp_write( tp, 0xF80E, 0xE051 ); + rtl8168_mac_ocp_write( tp, 0xF810, 0xC202 ); + rtl8168_mac_ocp_write( tp, 0xF812, 0xBA00 ); + rtl8168_mac_ocp_write( tp, 0xF814, 0x0DFC ); + rtl8168_mac_ocp_write( tp, 0xF816, 0x7444 ); + rtl8168_mac_ocp_write( tp, 0xF818, 0xC502 ); + rtl8168_mac_ocp_write( tp, 0xF81A, 0xBD00 ); + rtl8168_mac_ocp_write( tp, 0xF81C, 0x0A30 ); + rtl8168_mac_ocp_write( tp, 0xF81E, 0x49D9 ); + rtl8168_mac_ocp_write( tp, 0xF820, 0xF019 ); + rtl8168_mac_ocp_write( tp, 0xF822, 0xC520 ); + rtl8168_mac_ocp_write( tp, 0xF824, 0x64A5 ); + rtl8168_mac_ocp_write( tp, 0xF826, 0x1400 ); + rtl8168_mac_ocp_write( tp, 0xF828, 0xF007 ); + rtl8168_mac_ocp_write( tp, 0xF82A, 0x0C01 ); + rtl8168_mac_ocp_write( tp, 0xF82C, 0x8CA5 ); + rtl8168_mac_ocp_write( tp, 0xF82E, 0x1C15 ); + rtl8168_mac_ocp_write( tp, 0xF830, 0xC515 ); + rtl8168_mac_ocp_write( tp, 0xF832, 0x9CA0 ); + rtl8168_mac_ocp_write( tp, 0xF834, 0xE00F ); + rtl8168_mac_ocp_write( tp, 0xF836, 0xC513 ); + rtl8168_mac_ocp_write( tp, 0xF838, 0x74A0 ); + rtl8168_mac_ocp_write( tp, 0xF83A, 0x48C8 ); + rtl8168_mac_ocp_write( tp, 0xF83C, 0x48CA ); + rtl8168_mac_ocp_write( tp, 0xF83E, 0x9CA0 ); + rtl8168_mac_ocp_write( tp, 0xF840, 0xC510 ); + rtl8168_mac_ocp_write( tp, 0xF842, 0x1B00 ); + rtl8168_mac_ocp_write( tp, 0xF844, 0x9BA0 ); + rtl8168_mac_ocp_write( tp, 0xF846, 0x1B1C ); + rtl8168_mac_ocp_write( tp, 0xF848, 0x483F ); + rtl8168_mac_ocp_write( tp, 0xF84A, 0x9BA2 ); + rtl8168_mac_ocp_write( tp, 0xF84C, 0x1B04 ); + rtl8168_mac_ocp_write( tp, 0xF84E, 0xC506 ); + rtl8168_mac_ocp_write( tp, 0xF850, 0x9BA0 ); + rtl8168_mac_ocp_write( tp, 0xF852, 0xC603 ); + rtl8168_mac_ocp_write( tp, 0xF854, 0xBE00 ); + rtl8168_mac_ocp_write( tp, 0xF856, 0x0298 ); + rtl8168_mac_ocp_write( tp, 0xF858, 0x03DE ); + rtl8168_mac_ocp_write( tp, 0xF85A, 0xE434 ); + rtl8168_mac_ocp_write( tp, 0xF85C, 0xE096 ); + rtl8168_mac_ocp_write( tp, 0xF85E, 0xE860 ); + rtl8168_mac_ocp_write( tp, 0xF860, 0xDE20 ); + rtl8168_mac_ocp_write( tp, 0xF862, 0xD3C0 ); + rtl8168_mac_ocp_write( tp, 0xF864, 0xC602 ); + rtl8168_mac_ocp_write( tp, 0xF866, 0xBE00 ); + rtl8168_mac_ocp_write( tp, 0xF868, 0x0A64 ); + rtl8168_mac_ocp_write( tp, 0xF86A, 0xC707 ); + rtl8168_mac_ocp_write( tp, 0xF86C, 0x1D00 ); + rtl8168_mac_ocp_write( tp, 0xF86E, 0x8DE2 ); + rtl8168_mac_ocp_write( tp, 0xF870, 0x48C1 ); + rtl8168_mac_ocp_write( tp, 0xF872, 0xC502 ); + rtl8168_mac_ocp_write( tp, 0xF874, 0xBD00 ); + rtl8168_mac_ocp_write( tp, 0xF876, 0x00AA ); + rtl8168_mac_ocp_write( tp, 0xF878, 0xE0C0 ); + rtl8168_mac_ocp_write( tp, 0xF87A, 0xC502 ); + rtl8168_mac_ocp_write( tp, 0xF87C, 0xBD00 ); + rtl8168_mac_ocp_write( tp, 0xF87E, 0x0132 ); + rtl8168_mac_ocp_write( tp, 0xF880, 0xC50C ); + rtl8168_mac_ocp_write( tp, 0xF882, 0x74A2 ); + rtl8168_mac_ocp_write( tp, 0xF884, 0x49CE ); + rtl8168_mac_ocp_write( tp, 0xF886, 0xF1FE ); + rtl8168_mac_ocp_write( tp, 0xF888, 0x1C00 ); + rtl8168_mac_ocp_write( tp, 0xF88A, 0x9EA0 ); + rtl8168_mac_ocp_write( tp, 0xF88C, 0x1C1C ); + rtl8168_mac_ocp_write( tp, 0xF88E, 0x484F ); + rtl8168_mac_ocp_write( tp, 0xF890, 0x9CA2 ); + rtl8168_mac_ocp_write( tp, 0xF892, 0xC402 ); + rtl8168_mac_ocp_write( tp, 0xF894, 0xBC00 ); + rtl8168_mac_ocp_write( tp, 0xF896, 0x0AFA ); + rtl8168_mac_ocp_write( tp, 0xF898, 0xDE20 ); + rtl8168_mac_ocp_write( tp, 0xF89A, 0xE000 ); + rtl8168_mac_ocp_write( tp, 0xF89C, 0xE092 ); + rtl8168_mac_ocp_write( tp, 0xF89E, 0xE430 ); + rtl8168_mac_ocp_write( tp, 0xF8A0, 0xDE20 ); + rtl8168_mac_ocp_write( tp, 0xF8A2, 0xE0C0 ); + rtl8168_mac_ocp_write( tp, 0xF8A4, 0xE860 ); + rtl8168_mac_ocp_write( tp, 0xF8A6, 0xE84C ); + rtl8168_mac_ocp_write( tp, 0xF8A8, 0xB400 ); + rtl8168_mac_ocp_write( tp, 0xF8AA, 0xB430 ); + rtl8168_mac_ocp_write( tp, 0xF8AC, 0xE410 ); + rtl8168_mac_ocp_write( tp, 0xF8AE, 0xC0AE ); + rtl8168_mac_ocp_write( tp, 0xF8B0, 0xB407 ); + rtl8168_mac_ocp_write( tp, 0xF8B2, 0xB406 ); + rtl8168_mac_ocp_write( tp, 0xF8B4, 0xB405 ); + rtl8168_mac_ocp_write( tp, 0xF8B6, 0xB404 ); + rtl8168_mac_ocp_write( tp, 0xF8B8, 0xB403 ); + rtl8168_mac_ocp_write( tp, 0xF8BA, 0xB402 ); + rtl8168_mac_ocp_write( tp, 0xF8BC, 0xB401 ); + rtl8168_mac_ocp_write( tp, 0xF8BE, 0xC7EE ); + rtl8168_mac_ocp_write( tp, 0xF8C0, 0x76F4 ); + rtl8168_mac_ocp_write( tp, 0xF8C2, 0xC2ED ); + rtl8168_mac_ocp_write( tp, 0xF8C4, 0xC3ED ); + rtl8168_mac_ocp_write( tp, 0xF8C6, 0xC1EF ); + rtl8168_mac_ocp_write( tp, 0xF8C8, 0xC5F3 ); + rtl8168_mac_ocp_write( tp, 0xF8CA, 0x74A0 ); + rtl8168_mac_ocp_write( tp, 0xF8CC, 0x49CD ); + rtl8168_mac_ocp_write( tp, 0xF8CE, 0xF001 ); + rtl8168_mac_ocp_write( tp, 0xF8D0, 0xC5EE ); + rtl8168_mac_ocp_write( tp, 0xF8D2, 0x74A0 ); + rtl8168_mac_ocp_write( tp, 0xF8D4, 0x49C1 ); + rtl8168_mac_ocp_write( tp, 0xF8D6, 0xF105 ); + rtl8168_mac_ocp_write( tp, 0xF8D8, 0xC5E4 ); + rtl8168_mac_ocp_write( tp, 0xF8DA, 0x74A2 ); + rtl8168_mac_ocp_write( tp, 0xF8DC, 0x49CE ); + rtl8168_mac_ocp_write( tp, 0xF8DE, 0xF00B ); + rtl8168_mac_ocp_write( tp, 0xF8E0, 0x7444 ); + rtl8168_mac_ocp_write( tp, 0xF8E2, 0x484B ); + rtl8168_mac_ocp_write( tp, 0xF8E4, 0x9C44 ); + rtl8168_mac_ocp_write( tp, 0xF8E6, 0x1C10 ); + rtl8168_mac_ocp_write( tp, 0xF8E8, 0x9C62 ); + rtl8168_mac_ocp_write( tp, 0xF8EA, 0x1C11 ); + rtl8168_mac_ocp_write( tp, 0xF8EC, 0x8C60 ); + rtl8168_mac_ocp_write( tp, 0xF8EE, 0x1C00 ); + rtl8168_mac_ocp_write( tp, 0xF8F0, 0x9CF6 ); + rtl8168_mac_ocp_write( tp, 0xF8F2, 0xE0EC ); + rtl8168_mac_ocp_write( tp, 0xF8F4, 0x49E7 ); + rtl8168_mac_ocp_write( tp, 0xF8F6, 0xF016 ); + rtl8168_mac_ocp_write( tp, 0xF8F8, 0x1D80 ); + rtl8168_mac_ocp_write( tp, 0xF8FA, 0x8DF4 ); + rtl8168_mac_ocp_write( tp, 0xF8FC, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xF8FE, 0x4843 ); + rtl8168_mac_ocp_write( tp, 0xF900, 0x8CF8 ); + rtl8168_mac_ocp_write( tp, 0xF902, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xF904, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xF906, 0x7444 ); + rtl8168_mac_ocp_write( tp, 0xF908, 0x48C8 ); + rtl8168_mac_ocp_write( tp, 0xF90A, 0x48C9 ); + rtl8168_mac_ocp_write( tp, 0xF90C, 0x48CA ); + rtl8168_mac_ocp_write( tp, 0xF90E, 0x9C44 ); + rtl8168_mac_ocp_write( tp, 0xF910, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xF912, 0x4844 ); + rtl8168_mac_ocp_write( tp, 0xF914, 0x8CF8 ); + rtl8168_mac_ocp_write( tp, 0xF916, 0x1E01 ); + rtl8168_mac_ocp_write( tp, 0xF918, 0xE8DB ); + rtl8168_mac_ocp_write( tp, 0xF91A, 0x7420 ); + rtl8168_mac_ocp_write( tp, 0xF91C, 0x48C1 ); + rtl8168_mac_ocp_write( tp, 0xF91E, 0x9C20 ); + rtl8168_mac_ocp_write( tp, 0xF920, 0xE0D5 ); + rtl8168_mac_ocp_write( tp, 0xF922, 0x49E6 ); + rtl8168_mac_ocp_write( tp, 0xF924, 0xF02A ); + rtl8168_mac_ocp_write( tp, 0xF926, 0x1D40 ); + rtl8168_mac_ocp_write( tp, 0xF928, 0x8DF4 ); + rtl8168_mac_ocp_write( tp, 0xF92A, 0x74FC ); + rtl8168_mac_ocp_write( tp, 0xF92C, 0x49C0 ); + rtl8168_mac_ocp_write( tp, 0xF92E, 0xF124 ); + rtl8168_mac_ocp_write( tp, 0xF930, 0x49C1 ); + rtl8168_mac_ocp_write( tp, 0xF932, 0xF122 ); + rtl8168_mac_ocp_write( tp, 0xF934, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xF936, 0x49C0 ); + rtl8168_mac_ocp_write( tp, 0xF938, 0xF01F ); + rtl8168_mac_ocp_write( tp, 0xF93A, 0xE8D3 ); + rtl8168_mac_ocp_write( tp, 0xF93C, 0x48C4 ); + rtl8168_mac_ocp_write( tp, 0xF93E, 0x8CF8 ); + rtl8168_mac_ocp_write( tp, 0xF940, 0x1E00 ); + rtl8168_mac_ocp_write( tp, 0xF942, 0xE8C6 ); + rtl8168_mac_ocp_write( tp, 0xF944, 0xC5B1 ); + rtl8168_mac_ocp_write( tp, 0xF946, 0x74A0 ); + rtl8168_mac_ocp_write( tp, 0xF948, 0x49C3 ); + rtl8168_mac_ocp_write( tp, 0xF94A, 0xF016 ); + rtl8168_mac_ocp_write( tp, 0xF94C, 0xC5AF ); + rtl8168_mac_ocp_write( tp, 0xF94E, 0x74A4 ); + rtl8168_mac_ocp_write( tp, 0xF950, 0x49C2 ); + rtl8168_mac_ocp_write( tp, 0xF952, 0xF005 ); + rtl8168_mac_ocp_write( tp, 0xF954, 0xC5AA ); + rtl8168_mac_ocp_write( tp, 0xF956, 0x74B2 ); + rtl8168_mac_ocp_write( tp, 0xF958, 0x49C9 ); + rtl8168_mac_ocp_write( tp, 0xF95A, 0xF10E ); + rtl8168_mac_ocp_write( tp, 0xF95C, 0xC5A6 ); + rtl8168_mac_ocp_write( tp, 0xF95E, 0x74A8 ); + rtl8168_mac_ocp_write( tp, 0xF960, 0x4845 ); + rtl8168_mac_ocp_write( tp, 0xF962, 0x4846 ); + rtl8168_mac_ocp_write( tp, 0xF964, 0x4847 ); + rtl8168_mac_ocp_write( tp, 0xF966, 0x4848 ); + rtl8168_mac_ocp_write( tp, 0xF968, 0x9CA8 ); + rtl8168_mac_ocp_write( tp, 0xF96A, 0x74B2 ); + rtl8168_mac_ocp_write( tp, 0xF96C, 0x4849 ); + rtl8168_mac_ocp_write( tp, 0xF96E, 0x9CB2 ); + rtl8168_mac_ocp_write( tp, 0xF970, 0x74A0 ); + rtl8168_mac_ocp_write( tp, 0xF972, 0x484F ); + rtl8168_mac_ocp_write( tp, 0xF974, 0x9CA0 ); + rtl8168_mac_ocp_write( tp, 0xF976, 0xE0AA ); + rtl8168_mac_ocp_write( tp, 0xF978, 0x49E4 ); + rtl8168_mac_ocp_write( tp, 0xF97A, 0xF018 ); + rtl8168_mac_ocp_write( tp, 0xF97C, 0x1D10 ); + rtl8168_mac_ocp_write( tp, 0xF97E, 0x8DF4 ); + rtl8168_mac_ocp_write( tp, 0xF980, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xF982, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xF984, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xF986, 0x4843 ); + rtl8168_mac_ocp_write( tp, 0xF988, 0x8CF8 ); + rtl8168_mac_ocp_write( tp, 0xF98A, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xF98C, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xF98E, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xF990, 0x4844 ); + rtl8168_mac_ocp_write( tp, 0xF992, 0x4842 ); + rtl8168_mac_ocp_write( tp, 0xF994, 0x4841 ); + rtl8168_mac_ocp_write( tp, 0xF996, 0x8CF8 ); + rtl8168_mac_ocp_write( tp, 0xF998, 0x1E01 ); + rtl8168_mac_ocp_write( tp, 0xF99A, 0xE89A ); + rtl8168_mac_ocp_write( tp, 0xF99C, 0x7420 ); + rtl8168_mac_ocp_write( tp, 0xF99E, 0x4841 ); + rtl8168_mac_ocp_write( tp, 0xF9A0, 0x9C20 ); + rtl8168_mac_ocp_write( tp, 0xF9A2, 0x7444 ); + rtl8168_mac_ocp_write( tp, 0xF9A4, 0x4848 ); + rtl8168_mac_ocp_write( tp, 0xF9A6, 0x9C44 ); + rtl8168_mac_ocp_write( tp, 0xF9A8, 0xE091 ); + rtl8168_mac_ocp_write( tp, 0xF9AA, 0x49E5 ); + rtl8168_mac_ocp_write( tp, 0xF9AC, 0xF03E ); + rtl8168_mac_ocp_write( tp, 0xF9AE, 0x1D20 ); + rtl8168_mac_ocp_write( tp, 0xF9B0, 0x8DF4 ); + rtl8168_mac_ocp_write( tp, 0xF9B2, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xF9B4, 0x48C2 ); + rtl8168_mac_ocp_write( tp, 0xF9B6, 0x4841 ); + rtl8168_mac_ocp_write( tp, 0xF9B8, 0x8CF8 ); + rtl8168_mac_ocp_write( tp, 0xF9BA, 0x1E01 ); + rtl8168_mac_ocp_write( tp, 0xF9BC, 0x7444 ); + rtl8168_mac_ocp_write( tp, 0xF9BE, 0x49CA ); + rtl8168_mac_ocp_write( tp, 0xF9C0, 0xF103 ); + rtl8168_mac_ocp_write( tp, 0xF9C2, 0x49C2 ); + rtl8168_mac_ocp_write( tp, 0xF9C4, 0xF00C ); + rtl8168_mac_ocp_write( tp, 0xF9C6, 0x49C1 ); + rtl8168_mac_ocp_write( tp, 0xF9C8, 0xF004 ); + rtl8168_mac_ocp_write( tp, 0xF9CA, 0x6447 ); + rtl8168_mac_ocp_write( tp, 0xF9CC, 0x2244 ); + rtl8168_mac_ocp_write( tp, 0xF9CE, 0xE002 ); + rtl8168_mac_ocp_write( tp, 0xF9D0, 0x1C01 ); + rtl8168_mac_ocp_write( tp, 0xF9D2, 0x9C62 ); + rtl8168_mac_ocp_write( tp, 0xF9D4, 0x1C11 ); + rtl8168_mac_ocp_write( tp, 0xF9D6, 0x8C60 ); + rtl8168_mac_ocp_write( tp, 0xF9D8, 0x1C00 ); + rtl8168_mac_ocp_write( tp, 0xF9DA, 0x9CF6 ); + rtl8168_mac_ocp_write( tp, 0xF9DC, 0x7444 ); + rtl8168_mac_ocp_write( tp, 0xF9DE, 0x49C8 ); + rtl8168_mac_ocp_write( tp, 0xF9E0, 0xF01D ); + rtl8168_mac_ocp_write( tp, 0xF9E2, 0x74FC ); + rtl8168_mac_ocp_write( tp, 0xF9E4, 0x49C0 ); + rtl8168_mac_ocp_write( tp, 0xF9E6, 0xF11A ); + rtl8168_mac_ocp_write( tp, 0xF9E8, 0x49C1 ); + rtl8168_mac_ocp_write( tp, 0xF9EA, 0xF118 ); + rtl8168_mac_ocp_write( tp, 0xF9EC, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xF9EE, 0x49C0 ); + rtl8168_mac_ocp_write( tp, 0xF9F0, 0xF015 ); + rtl8168_mac_ocp_write( tp, 0xF9F2, 0x49C6 ); + rtl8168_mac_ocp_write( tp, 0xF9F4, 0xF113 ); + rtl8168_mac_ocp_write( tp, 0xF9F6, 0xE875 ); + rtl8168_mac_ocp_write( tp, 0xF9F8, 0x48C4 ); + rtl8168_mac_ocp_write( tp, 0xF9FA, 0x8CF8 ); + rtl8168_mac_ocp_write( tp, 0xF9FC, 0x7420 ); + rtl8168_mac_ocp_write( tp, 0xF9FE, 0x48C1 ); + rtl8168_mac_ocp_write( tp, 0xFA00, 0x9C20 ); + rtl8168_mac_ocp_write( tp, 0xFA02, 0xC50A ); + rtl8168_mac_ocp_write( tp, 0xFA04, 0x74A2 ); + rtl8168_mac_ocp_write( tp, 0xFA06, 0x8CA5 ); + rtl8168_mac_ocp_write( tp, 0xFA08, 0x74A0 ); + rtl8168_mac_ocp_write( tp, 0xFA0A, 0xC505 ); + rtl8168_mac_ocp_write( tp, 0xFA0C, 0x9CA2 ); + rtl8168_mac_ocp_write( tp, 0xFA0E, 0x1C11 ); + rtl8168_mac_ocp_write( tp, 0xFA10, 0x9CA0 ); + rtl8168_mac_ocp_write( tp, 0xFA12, 0xE00A ); + rtl8168_mac_ocp_write( tp, 0xFA14, 0xE434 ); + rtl8168_mac_ocp_write( tp, 0xFA16, 0xD3C0 ); + rtl8168_mac_ocp_write( tp, 0xFA18, 0xDC00 ); + rtl8168_mac_ocp_write( tp, 0xFA1A, 0x7444 ); + rtl8168_mac_ocp_write( tp, 0xFA1C, 0x49CA ); + rtl8168_mac_ocp_write( tp, 0xFA1E, 0xF004 ); + rtl8168_mac_ocp_write( tp, 0xFA20, 0x48CA ); + rtl8168_mac_ocp_write( tp, 0xFA22, 0x9C44 ); + rtl8168_mac_ocp_write( tp, 0xFA24, 0xE855 ); + rtl8168_mac_ocp_write( tp, 0xFA26, 0xE052 ); + rtl8168_mac_ocp_write( tp, 0xFA28, 0x49E8 ); + rtl8168_mac_ocp_write( tp, 0xFA2A, 0xF024 ); + rtl8168_mac_ocp_write( tp, 0xFA2C, 0x1D01 ); + rtl8168_mac_ocp_write( tp, 0xFA2E, 0x8DF5 ); + rtl8168_mac_ocp_write( tp, 0xFA30, 0x7440 ); + rtl8168_mac_ocp_write( tp, 0xFA32, 0x49C0 ); + rtl8168_mac_ocp_write( tp, 0xFA34, 0xF11E ); + rtl8168_mac_ocp_write( tp, 0xFA36, 0x7444 ); + rtl8168_mac_ocp_write( tp, 0xFA38, 0x49C8 ); + rtl8168_mac_ocp_write( tp, 0xFA3A, 0xF01B ); + rtl8168_mac_ocp_write( tp, 0xFA3C, 0x49CA ); + rtl8168_mac_ocp_write( tp, 0xFA3E, 0xF119 ); + rtl8168_mac_ocp_write( tp, 0xFA40, 0xC5EC ); + rtl8168_mac_ocp_write( tp, 0xFA42, 0x76A4 ); + rtl8168_mac_ocp_write( tp, 0xFA44, 0x49E3 ); + rtl8168_mac_ocp_write( tp, 0xFA46, 0xF015 ); + rtl8168_mac_ocp_write( tp, 0xFA48, 0x49C0 ); + rtl8168_mac_ocp_write( tp, 0xFA4A, 0xF103 ); + rtl8168_mac_ocp_write( tp, 0xFA4C, 0x49C1 ); + rtl8168_mac_ocp_write( tp, 0xFA4E, 0xF011 ); + rtl8168_mac_ocp_write( tp, 0xFA50, 0x4849 ); + rtl8168_mac_ocp_write( tp, 0xFA52, 0x9C44 ); + rtl8168_mac_ocp_write( tp, 0xFA54, 0x1C00 ); + rtl8168_mac_ocp_write( tp, 0xFA56, 0x9CF6 ); + rtl8168_mac_ocp_write( tp, 0xFA58, 0x7444 ); + rtl8168_mac_ocp_write( tp, 0xFA5A, 0x49C1 ); + rtl8168_mac_ocp_write( tp, 0xFA5C, 0xF004 ); + rtl8168_mac_ocp_write( tp, 0xFA5E, 0x6446 ); + rtl8168_mac_ocp_write( tp, 0xFA60, 0x1E07 ); + rtl8168_mac_ocp_write( tp, 0xFA62, 0xE003 ); + rtl8168_mac_ocp_write( tp, 0xFA64, 0x1C01 ); + rtl8168_mac_ocp_write( tp, 0xFA66, 0x1E03 ); + rtl8168_mac_ocp_write( tp, 0xFA68, 0x9C62 ); + rtl8168_mac_ocp_write( tp, 0xFA6A, 0x1C11 ); + rtl8168_mac_ocp_write( tp, 0xFA6C, 0x8C60 ); + rtl8168_mac_ocp_write( tp, 0xFA6E, 0xE830 ); + rtl8168_mac_ocp_write( tp, 0xFA70, 0xE02D ); + rtl8168_mac_ocp_write( tp, 0xFA72, 0x49E9 ); + rtl8168_mac_ocp_write( tp, 0xFA74, 0xF004 ); + rtl8168_mac_ocp_write( tp, 0xFA76, 0x1D02 ); + rtl8168_mac_ocp_write( tp, 0xFA78, 0x8DF5 ); + rtl8168_mac_ocp_write( tp, 0xFA7A, 0xE79C ); + rtl8168_mac_ocp_write( tp, 0xFA7C, 0x49E3 ); + rtl8168_mac_ocp_write( tp, 0xFA7E, 0xF006 ); + rtl8168_mac_ocp_write( tp, 0xFA80, 0x1D08 ); + rtl8168_mac_ocp_write( tp, 0xFA82, 0x8DF4 ); + rtl8168_mac_ocp_write( tp, 0xFA84, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xFA86, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xFA88, 0xE73A ); + rtl8168_mac_ocp_write( tp, 0xFA8A, 0x49E1 ); + rtl8168_mac_ocp_write( tp, 0xFA8C, 0xF007 ); + rtl8168_mac_ocp_write( tp, 0xFA8E, 0x1D02 ); + rtl8168_mac_ocp_write( tp, 0xFA90, 0x8DF4 ); + rtl8168_mac_ocp_write( tp, 0xFA92, 0x1E01 ); + rtl8168_mac_ocp_write( tp, 0xFA94, 0xE7A7 ); + rtl8168_mac_ocp_write( tp, 0xFA96, 0xDE20 ); + rtl8168_mac_ocp_write( tp, 0xFA98, 0xE410 ); + rtl8168_mac_ocp_write( tp, 0xFA9A, 0x49E0 ); + rtl8168_mac_ocp_write( tp, 0xFA9C, 0xF017 ); + rtl8168_mac_ocp_write( tp, 0xFA9E, 0x1D01 ); + rtl8168_mac_ocp_write( tp, 0xFAA0, 0x8DF4 ); + rtl8168_mac_ocp_write( tp, 0xFAA2, 0xC5FA ); + rtl8168_mac_ocp_write( tp, 0xFAA4, 0x1C00 ); + rtl8168_mac_ocp_write( tp, 0xFAA6, 0x8CA0 ); + rtl8168_mac_ocp_write( tp, 0xFAA8, 0x1C1B ); + rtl8168_mac_ocp_write( tp, 0xFAAA, 0x9CA2 ); + rtl8168_mac_ocp_write( tp, 0xFAAC, 0x74A2 ); + rtl8168_mac_ocp_write( tp, 0xFAAE, 0x49CF ); + rtl8168_mac_ocp_write( tp, 0xFAB0, 0xF0FE ); + rtl8168_mac_ocp_write( tp, 0xFAB2, 0xC5F3 ); + rtl8168_mac_ocp_write( tp, 0xFAB4, 0x74A0 ); + rtl8168_mac_ocp_write( tp, 0xFAB6, 0x4849 ); + rtl8168_mac_ocp_write( tp, 0xFAB8, 0x9CA0 ); + rtl8168_mac_ocp_write( tp, 0xFABA, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xFABC, 0x49C0 ); + rtl8168_mac_ocp_write( tp, 0xFABE, 0xF006 ); + rtl8168_mac_ocp_write( tp, 0xFAC0, 0x48C3 ); + rtl8168_mac_ocp_write( tp, 0xFAC2, 0x8CF8 ); + rtl8168_mac_ocp_write( tp, 0xFAC4, 0xE820 ); + rtl8168_mac_ocp_write( tp, 0xFAC6, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xFAC8, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xFACA, 0xC432 ); + rtl8168_mac_ocp_write( tp, 0xFACC, 0xBC00 ); + rtl8168_mac_ocp_write( tp, 0xFACE, 0xC5E4 ); + rtl8168_mac_ocp_write( tp, 0xFAD0, 0x74A2 ); + rtl8168_mac_ocp_write( tp, 0xFAD2, 0x49CE ); + rtl8168_mac_ocp_write( tp, 0xFAD4, 0xF1FE ); + rtl8168_mac_ocp_write( tp, 0xFAD6, 0x9EA0 ); + rtl8168_mac_ocp_write( tp, 0xFAD8, 0x1C1C ); + rtl8168_mac_ocp_write( tp, 0xFADA, 0x484F ); + rtl8168_mac_ocp_write( tp, 0xFADC, 0x9CA2 ); + rtl8168_mac_ocp_write( tp, 0xFADE, 0xFF80 ); + rtl8168_mac_ocp_write( tp, 0xFAE0, 0xB404 ); + rtl8168_mac_ocp_write( tp, 0xFAE2, 0xB405 ); + rtl8168_mac_ocp_write( tp, 0xFAE4, 0xC5D9 ); + rtl8168_mac_ocp_write( tp, 0xFAE6, 0x74A2 ); + rtl8168_mac_ocp_write( tp, 0xFAE8, 0x49CE ); + rtl8168_mac_ocp_write( tp, 0xFAEA, 0xF1FE ); + rtl8168_mac_ocp_write( tp, 0xFAEC, 0xC41F ); + rtl8168_mac_ocp_write( tp, 0xFAEE, 0x9CA0 ); + rtl8168_mac_ocp_write( tp, 0xFAF0, 0xC41C ); + rtl8168_mac_ocp_write( tp, 0xFAF2, 0x1C13 ); + rtl8168_mac_ocp_write( tp, 0xFAF4, 0x484F ); + rtl8168_mac_ocp_write( tp, 0xFAF6, 0x9CA2 ); + rtl8168_mac_ocp_write( tp, 0xFAF8, 0x74A2 ); + rtl8168_mac_ocp_write( tp, 0xFAFA, 0x49CF ); + rtl8168_mac_ocp_write( tp, 0xFAFC, 0xF1FE ); + rtl8168_mac_ocp_write( tp, 0xFAFE, 0xB005 ); + rtl8168_mac_ocp_write( tp, 0xFB00, 0xB004 ); + rtl8168_mac_ocp_write( tp, 0xFB02, 0xFF80 ); + rtl8168_mac_ocp_write( tp, 0xFB04, 0xB404 ); + rtl8168_mac_ocp_write( tp, 0xFB06, 0xB405 ); + rtl8168_mac_ocp_write( tp, 0xFB08, 0xC5C7 ); + rtl8168_mac_ocp_write( tp, 0xFB0A, 0x74A2 ); + rtl8168_mac_ocp_write( tp, 0xFB0C, 0x49CE ); + rtl8168_mac_ocp_write( tp, 0xFB0E, 0xF1FE ); + rtl8168_mac_ocp_write( tp, 0xFB10, 0xC40E ); + rtl8168_mac_ocp_write( tp, 0xFB12, 0x9CA0 ); + rtl8168_mac_ocp_write( tp, 0xFB14, 0xC40A ); + rtl8168_mac_ocp_write( tp, 0xFB16, 0x1C13 ); + rtl8168_mac_ocp_write( tp, 0xFB18, 0x484F ); + rtl8168_mac_ocp_write( tp, 0xFB1A, 0x9CA2 ); + rtl8168_mac_ocp_write( tp, 0xFB1C, 0x74A2 ); + rtl8168_mac_ocp_write( tp, 0xFB1E, 0x49CF ); + rtl8168_mac_ocp_write( tp, 0xFB20, 0xF1FE ); + rtl8168_mac_ocp_write( tp, 0xFB22, 0xB005 ); + rtl8168_mac_ocp_write( tp, 0xFB24, 0xB004 ); + rtl8168_mac_ocp_write( tp, 0xFB26, 0xFF80 ); + rtl8168_mac_ocp_write( tp, 0xFB28, 0x0000 ); + rtl8168_mac_ocp_write( tp, 0xFB2A, 0x0481 ); + rtl8168_mac_ocp_write( tp, 0xFB2C, 0x0C81 ); + rtl8168_mac_ocp_write( tp, 0xFB2E, 0x0AE0 ); + + + rtl8168_mac_ocp_write( tp, 0xFC26, 0x8000 ); + + rtl8168_mac_ocp_write( tp, 0xFC28, 0x0000 ); + rtl8168_mac_ocp_write( tp, 0xFC2A, 0x0000 ); + rtl8168_mac_ocp_write( tp, 0xFC2C, 0x0297 ); + rtl8168_mac_ocp_write( tp, 0xFC2E, 0x0000 ); + rtl8168_mac_ocp_write( tp, 0xFC30, 0x00A9 ); + rtl8168_mac_ocp_write( tp, 0xFC32, 0x012D ); + rtl8168_mac_ocp_write( tp, 0xFC34, 0x0000 ); + rtl8168_mac_ocp_write( tp, 0xFC36, 0x08DF ); +} + +static void +rtl8168_set_mac_mcu_8411b_1(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + rtl8168_hw_disable_mac_mcu_bps(dev); + + rtl8168_mac_ocp_write( tp, 0xF800, 0xE008 ); + rtl8168_mac_ocp_write( tp, 0xF802, 0xE00A ); + rtl8168_mac_ocp_write( tp, 0xF804, 0xE00C ); + rtl8168_mac_ocp_write( tp, 0xF806, 0xE00E ); + rtl8168_mac_ocp_write( tp, 0xF808, 0xE027 ); + rtl8168_mac_ocp_write( tp, 0xF80A, 0xE04F ); + rtl8168_mac_ocp_write( tp, 0xF80C, 0xE05E ); + rtl8168_mac_ocp_write( tp, 0xF80E, 0xE065 ); + rtl8168_mac_ocp_write( tp, 0xF810, 0xC602 ); + rtl8168_mac_ocp_write( tp, 0xF812, 0xBE00 ); + rtl8168_mac_ocp_write( tp, 0xF814, 0x0000 ); + rtl8168_mac_ocp_write( tp, 0xF816, 0xC502 ); + rtl8168_mac_ocp_write( tp, 0xF818, 0xBD00 ); + rtl8168_mac_ocp_write( tp, 0xF81A, 0x074C ); + rtl8168_mac_ocp_write( tp, 0xF81C, 0xC302 ); + rtl8168_mac_ocp_write( tp, 0xF81E, 0xBB00 ); + rtl8168_mac_ocp_write( tp, 0xF820, 0x080A ); + rtl8168_mac_ocp_write( tp, 0xF822, 0x6420 ); + rtl8168_mac_ocp_write( tp, 0xF824, 0x48C2 ); + rtl8168_mac_ocp_write( tp, 0xF826, 0x8C20 ); + rtl8168_mac_ocp_write( tp, 0xF828, 0xC516 ); + rtl8168_mac_ocp_write( tp, 0xF82A, 0x64A4 ); + rtl8168_mac_ocp_write( tp, 0xF82C, 0x49C0 ); + rtl8168_mac_ocp_write( tp, 0xF82E, 0xF009 ); + rtl8168_mac_ocp_write( tp, 0xF830, 0x74A2 ); + rtl8168_mac_ocp_write( tp, 0xF832, 0x8CA5 ); + rtl8168_mac_ocp_write( tp, 0xF834, 0x74A0 ); + rtl8168_mac_ocp_write( tp, 0xF836, 0xC50E ); + rtl8168_mac_ocp_write( tp, 0xF838, 0x9CA2 ); + rtl8168_mac_ocp_write( tp, 0xF83A, 0x1C11 ); + rtl8168_mac_ocp_write( tp, 0xF83C, 0x9CA0 ); + rtl8168_mac_ocp_write( tp, 0xF83E, 0xE006 ); + rtl8168_mac_ocp_write( tp, 0xF840, 0x74F8 ); + rtl8168_mac_ocp_write( tp, 0xF842, 0x48C4 ); + rtl8168_mac_ocp_write( tp, 0xF844, 0x8CF8 ); + rtl8168_mac_ocp_write( tp, 0xF846, 0xC404 ); + rtl8168_mac_ocp_write( tp, 0xF848, 0xBC00 ); + rtl8168_mac_ocp_write( tp, 0xF84A, 0xC403 ); + rtl8168_mac_ocp_write( tp, 0xF84C, 0xBC00 ); + rtl8168_mac_ocp_write( tp, 0xF84E, 0x0BF2 ); + rtl8168_mac_ocp_write( tp, 0xF850, 0x0C0A ); + rtl8168_mac_ocp_write( tp, 0xF852, 0xE434 ); + rtl8168_mac_ocp_write( tp, 0xF854, 0xD3C0 ); + rtl8168_mac_ocp_write( tp, 0xF856, 0x49D9 ); + rtl8168_mac_ocp_write( tp, 0xF858, 0xF01F ); + rtl8168_mac_ocp_write( tp, 0xF85A, 0xC526 ); + rtl8168_mac_ocp_write( tp, 0xF85C, 0x64A5 ); + rtl8168_mac_ocp_write( tp, 0xF85E, 0x1400 ); + rtl8168_mac_ocp_write( tp, 0xF860, 0xF007 ); + rtl8168_mac_ocp_write( tp, 0xF862, 0x0C01 ); + rtl8168_mac_ocp_write( tp, 0xF864, 0x8CA5 ); + rtl8168_mac_ocp_write( tp, 0xF866, 0x1C15 ); + rtl8168_mac_ocp_write( tp, 0xF868, 0xC51B ); + rtl8168_mac_ocp_write( tp, 0xF86A, 0x9CA0 ); + rtl8168_mac_ocp_write( tp, 0xF86C, 0xE013 ); + rtl8168_mac_ocp_write( tp, 0xF86E, 0xC519 ); + rtl8168_mac_ocp_write( tp, 0xF870, 0x74A0 ); + rtl8168_mac_ocp_write( tp, 0xF872, 0x48C4 ); + rtl8168_mac_ocp_write( tp, 0xF874, 0x8CA0 ); + rtl8168_mac_ocp_write( tp, 0xF876, 0xC516 ); + rtl8168_mac_ocp_write( tp, 0xF878, 0x74A4 ); + rtl8168_mac_ocp_write( tp, 0xF87A, 0x48C8 ); + rtl8168_mac_ocp_write( tp, 0xF87C, 0x48CA ); + rtl8168_mac_ocp_write( tp, 0xF87E, 0x9CA4 ); + rtl8168_mac_ocp_write( tp, 0xF880, 0xC512 ); + rtl8168_mac_ocp_write( tp, 0xF882, 0x1B00 ); + rtl8168_mac_ocp_write( tp, 0xF884, 0x9BA0 ); + rtl8168_mac_ocp_write( tp, 0xF886, 0x1B1C ); + rtl8168_mac_ocp_write( tp, 0xF888, 0x483F ); + rtl8168_mac_ocp_write( tp, 0xF88A, 0x9BA2 ); + rtl8168_mac_ocp_write( tp, 0xF88C, 0x1B04 ); + rtl8168_mac_ocp_write( tp, 0xF88E, 0xC508 ); + rtl8168_mac_ocp_write( tp, 0xF890, 0x9BA0 ); + rtl8168_mac_ocp_write( tp, 0xF892, 0xC505 ); + rtl8168_mac_ocp_write( tp, 0xF894, 0xBD00 ); + rtl8168_mac_ocp_write( tp, 0xF896, 0xC502 ); + rtl8168_mac_ocp_write( tp, 0xF898, 0xBD00 ); + rtl8168_mac_ocp_write( tp, 0xF89A, 0x0300 ); + rtl8168_mac_ocp_write( tp, 0xF89C, 0x051E ); + rtl8168_mac_ocp_write( tp, 0xF89E, 0xE434 ); + rtl8168_mac_ocp_write( tp, 0xF8A0, 0xE018 ); + rtl8168_mac_ocp_write( tp, 0xF8A2, 0xE092 ); + rtl8168_mac_ocp_write( tp, 0xF8A4, 0xDE20 ); + rtl8168_mac_ocp_write( tp, 0xF8A6, 0xD3C0 ); + rtl8168_mac_ocp_write( tp, 0xF8A8, 0xC50F ); + rtl8168_mac_ocp_write( tp, 0xF8AA, 0x76A4 ); + rtl8168_mac_ocp_write( tp, 0xF8AC, 0x49E3 ); + rtl8168_mac_ocp_write( tp, 0xF8AE, 0xF007 ); + rtl8168_mac_ocp_write( tp, 0xF8B0, 0x49C0 ); + rtl8168_mac_ocp_write( tp, 0xF8B2, 0xF103 ); + rtl8168_mac_ocp_write( tp, 0xF8B4, 0xC607 ); + rtl8168_mac_ocp_write( tp, 0xF8B6, 0xBE00 ); + rtl8168_mac_ocp_write( tp, 0xF8B8, 0xC606 ); + rtl8168_mac_ocp_write( tp, 0xF8BA, 0xBE00 ); + rtl8168_mac_ocp_write( tp, 0xF8BC, 0xC602 ); + rtl8168_mac_ocp_write( tp, 0xF8BE, 0xBE00 ); + rtl8168_mac_ocp_write( tp, 0xF8C0, 0x0C4C ); + rtl8168_mac_ocp_write( tp, 0xF8C2, 0x0C28 ); + rtl8168_mac_ocp_write( tp, 0xF8C4, 0x0C2C ); + rtl8168_mac_ocp_write( tp, 0xF8C6, 0xDC00 ); + rtl8168_mac_ocp_write( tp, 0xF8C8, 0xC707 ); + rtl8168_mac_ocp_write( tp, 0xF8CA, 0x1D00 ); + rtl8168_mac_ocp_write( tp, 0xF8CC, 0x8DE2 ); + rtl8168_mac_ocp_write( tp, 0xF8CE, 0x48C1 ); + rtl8168_mac_ocp_write( tp, 0xF8D0, 0xC502 ); + rtl8168_mac_ocp_write( tp, 0xF8D2, 0xBD00 ); + rtl8168_mac_ocp_write( tp, 0xF8D4, 0x00AA ); + rtl8168_mac_ocp_write( tp, 0xF8D6, 0xE0C0 ); + rtl8168_mac_ocp_write( tp, 0xF8D8, 0xC502 ); + rtl8168_mac_ocp_write( tp, 0xF8DA, 0xBD00 ); + rtl8168_mac_ocp_write( tp, 0xF8DC, 0x0132 ); + + rtl8168_mac_ocp_write( tp, 0xFC26, 0x8000 ); + + rtl8168_mac_ocp_write( tp, 0xFC2A, 0x0743 ); + rtl8168_mac_ocp_write( tp, 0xFC2C, 0x0801 ); + rtl8168_mac_ocp_write( tp, 0xFC2E, 0x0BE9 ); + rtl8168_mac_ocp_write( tp, 0xFC30, 0x02FD ); + rtl8168_mac_ocp_write( tp, 0xFC32, 0x0C25 ); + rtl8168_mac_ocp_write( tp, 0xFC34, 0x00A9 ); + rtl8168_mac_ocp_write( tp, 0xFC36, 0x012D ); +} + +static void +rtl8168_set_mac_mcu_8168ep_1(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u16 i; + static const u16 mcu_patch_code_8168ep_1[] = { + 0xE008, 0xE0D3, 0xE0D6, 0xE0D9, 0xE0DB, 0xE0DD, 0xE0DF, 0xE0E1, 0xC251, + 0x7340, 0x49B1, 0xF010, 0x1D02, 0x8D40, 0xC202, 0xBA00, 0x2C3A, 0xC0F0, + 0xE8DE, 0x2000, 0x8000, 0xC0B6, 0x268C, 0x752C, 0x49D4, 0xF112, 0xE025, + 0xC2F6, 0x7146, 0xC2F5, 0x7340, 0x49BE, 0xF103, 0xC7F2, 0xE002, 0xC7F1, + 0x304F, 0x6226, 0x49A1, 0xF1F0, 0x7222, 0x49A0, 0xF1ED, 0x2525, 0x1F28, + 0x3097, 0x3091, 0x9A36, 0x752C, 0x21DC, 0x25BC, 0xC6E2, 0x77C0, 0x1304, + 0xF014, 0x1303, 0xF014, 0x1302, 0xF014, 0x1301, 0xF014, 0x49D4, 0xF103, + 0xC3D7, 0xBB00, 0xC618, 0x67C6, 0x752E, 0x22D7, 0x26DD, 0x1505, 0xF013, + 0xC60A, 0xBE00, 0xC309, 0xBB00, 0xC308, 0xBB00, 0xC307, 0xBB00, 0xC306, + 0xBB00, 0x25C8, 0x25A6, 0x25AC, 0x25B2, 0x25B8, 0xCD08, 0x0000, 0xC0BC, + 0xC2FF, 0x7340, 0x49B0, 0xF04E, 0x1F46, 0x308F, 0xC3F7, 0x1C04, 0xE84D, + 0x1401, 0xF147, 0x7226, 0x49A7, 0xF044, 0x7222, 0x2525, 0x1F30, 0x3097, + 0x3091, 0x7340, 0xC4EA, 0x401C, 0xF006, 0xC6E8, 0x75C0, 0x49D7, 0xF105, + 0xE036, 0x1D08, 0x8DC1, 0x0208, 0x6640, 0x2764, 0x1606, 0xF12F, 0x6346, + 0x133B, 0xF12C, 0x9B34, 0x1B18, 0x3093, 0xC32A, 0x1C10, 0xE82A, 0x1401, + 0xF124, 0x1A36, 0x308A, 0x7322, 0x25B5, 0x0B0E, 0x1C00, 0xE82C, 0xC71F, + 0x4027, 0xF11A, 0xE838, 0x1F42, 0x308F, 0x1B08, 0xE824, 0x7236, 0x7746, + 0x1700, 0xF00D, 0xC313, 0x401F, 0xF103, 0x1F00, 0x9F46, 0x7744, 0x449F, + 0x445F, 0xE817, 0xC70A, 0x4027, 0xF105, 0xC302, 0xBB00, 0x2E08, 0x2DC2, + 0xC7FF, 0xBF00, 0xCDB8, 0xFFFF, 0x0C02, 0xA554, 0xA5DC, 0x402F, 0xF105, + 0x1400, 0xF1FA, 0x1C01, 0xE002, 0x1C00, 0xFF80, 0x49B0, 0xF004, 0x0B01, + 0xA1D3, 0xE003, 0x0B02, 0xA5D3, 0x3127, 0x3720, 0x0B02, 0xA5D3, 0x3127, + 0x3720, 0x1300, 0xF1FB, 0xFF80, 0x7322, 0x25B5, 0x1E28, 0x30DE, 0x30D9, + 0x7264, 0x1E11, 0x2368, 0x3116, 0xFF80, 0x1B7E, 0xC602, 0xBE00, 0x06A6, + 0x1B7E, 0xC602, 0xBE00, 0x0764, 0xC602, 0xBE00, 0x0000, 0xC602, 0xBE00, + 0x0000, 0xC602, 0xBE00, 0x0000, 0xC602, 0xBE00, 0x0000, 0xC602, 0xBE00, + 0x0000 + }; + + rtl8168_hw_disable_mac_mcu_bps(dev); + + for (i = 0; i < ARRAY_SIZE(mcu_patch_code_8168ep_1); i++) { + rtl8168_mac_ocp_write(tp, 0xF800 + i * 2, mcu_patch_code_8168ep_1[i]); + } + + rtl8168_mac_ocp_write(tp, 0xFC26, 0x8000); + + rtl8168_mac_ocp_write(tp, 0xFC28, 0x2549); + rtl8168_mac_ocp_write(tp, 0xFC2A, 0x06A5); + rtl8168_mac_ocp_write(tp, 0xFC2C, 0x0763); +} + +static void +rtl8168_set_mac_mcu_8168ep_2(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u16 i; + static const u16 mcu_patch_code_8168ep_2[] = { + 0xE008, 0xE017, 0xE052, 0xE056, 0xE058, 0xE05A, 0xE05C, 0xE05E, 0xC50F, + 0x76A4, 0x49E3, 0xF007, 0x49C0, 0xF103, 0xC607, 0xBE00, 0xC606, 0xBE00, + 0xC602, 0xBE00, 0x0BDA, 0x0BB6, 0x0BBA, 0xDC00, 0xB400, 0xB401, 0xB402, + 0xB403, 0xB404, 0xC02E, 0x7206, 0x49AE, 0xF1FE, 0xC12B, 0x9904, 0xC12A, + 0x9906, 0x7206, 0x49AE, 0xF1FE, 0x7200, 0x49A0, 0xF117, 0xC123, 0xC223, + 0xC323, 0xE808, 0xC322, 0xE806, 0xC321, 0xE804, 0xC320, 0xE802, 0xE00C, + 0x740E, 0x49CE, 0xF1FE, 0x9908, 0x990A, 0x9A0C, 0x9B0E, 0x740E, 0x49CE, + 0xF1FE, 0xFF80, 0xB004, 0xB003, 0xB002, 0xB001, 0xB000, 0xC604, 0xC002, + 0xB800, 0x1FC8, 0xE000, 0xE8E0, 0xF128, 0x0002, 0xFFFF, 0xF000, 0x8001, + 0x8002, 0x8003, 0x8004, 0x48C1, 0x48C2, 0xC502, 0xBD00, 0x0490, 0xC602, + 0xBE00, 0x0000, 0xC602, 0xBE00, 0x0000, 0xC602, 0xBE00, 0x0000, 0xC602, + 0xBE00, 0x0000, 0xC602, 0xBE00, 0x0000, + }; + + rtl8168_hw_disable_mac_mcu_bps(dev); + + for (i = 0; i < ARRAY_SIZE(mcu_patch_code_8168ep_2); i++) { + rtl8168_mac_ocp_write(tp, 0xF800 + i * 2, mcu_patch_code_8168ep_2[i]); + } + + rtl8168_mac_ocp_write(tp, 0xFC26, 0x8000); + + rtl8168_mac_ocp_write(tp, 0xFC28, 0x0BB3); + rtl8168_mac_ocp_write(tp, 0xFC2A, 0x1FC7); + rtl8168_mac_ocp_write(tp, 0xFC2C, 0x0485); +} + +static void +rtl8168_set_mac_mcu_8168h_1(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u16 i; + static const u16 mcu_patch_code_8168h_1[] = { + 0xE008, 0xE00F, 0xE011, 0xE047, 0xE049, 0xE073, 0xE075, 0xE079, 0xC707, + 0x1D00, 0x8DE2, 0x48C1, 0xC502, 0xBD00, 0x00E4, 0xE0C0, 0xC502, 0xBD00, + 0x0216, 0xC634, 0x75C0, 0x49D3, 0xF027, 0xC631, 0x75C0, 0x49D3, 0xF123, + 0xC627, 0x75C0, 0xB405, 0xC525, 0x9DC0, 0xC621, 0x75C8, 0x49D5, 0xF00A, + 0x49D6, 0xF008, 0x49D7, 0xF006, 0x49D8, 0xF004, 0x75D2, 0x49D9, 0xF111, + 0xC517, 0x9DC8, 0xC516, 0x9DD2, 0xC618, 0x75C0, 0x49D4, 0xF003, 0x49D0, + 0xF104, 0xC60A, 0xC50E, 0x9DC0, 0xB005, 0xC607, 0x9DC0, 0xB007, 0xC602, + 0xBE00, 0x1A06, 0xB400, 0xE86C, 0xA000, 0x01E1, 0x0200, 0x9200, 0xE84C, + 0xE004, 0xE908, 0xC502, 0xBD00, 0x0B58, 0xB407, 0xB404, 0x2195, 0x25BD, + 0x9BE0, 0x1C1C, 0x484F, 0x9CE2, 0x72E2, 0x49AE, 0xF1FE, 0x0B00, 0xF116, + 0xC71C, 0xC419, 0x9CE0, 0x1C13, 0x484F, 0x9CE2, 0x74E2, 0x49CE, 0xF1FE, + 0xC412, 0x9CE0, 0x1C13, 0x484F, 0x9CE2, 0x74E2, 0x49CE, 0xF1FE, 0xC70C, + 0x74F8, 0x48C3, 0x8CF8, 0xB004, 0xB007, 0xC502, 0xBD00, 0x0F24, 0x0481, + 0x0C81, 0xDE24, 0xE000, 0xC602, 0xBE00, 0x0CA4, 0x48C1, 0x48C2, 0xC502, + 0xBD00, 0x0578, 0xC602, 0xBE00, 0x0000 + }; + + rtl8168_hw_disable_mac_mcu_bps(dev); + + for (i = 0; i < ARRAY_SIZE(mcu_patch_code_8168h_1); i++) { + rtl8168_mac_ocp_write(tp, 0xF800 + i * 2, mcu_patch_code_8168h_1[i]); + } + + rtl8168_mac_ocp_write(tp, 0xFC26, 0x8000); + + rtl8168_mac_ocp_write(tp, 0xFC28, 0x00E2); + rtl8168_mac_ocp_write(tp, 0xFC2A, 0x0210); + rtl8168_mac_ocp_write(tp, 0xFC2C, 0x1A04); + rtl8168_mac_ocp_write(tp, 0xFC2E, 0x0B26); + rtl8168_mac_ocp_write(tp, 0xFC30, 0x0F02); + rtl8168_mac_ocp_write(tp, 0xFC32, 0x0CA0); + rtl8168_mac_ocp_write(tp, 0xFC34, 0x056C); + + rtl8168_mac_ocp_write(tp, 0xFC38, 0x007F); +} + +static void +rtl8168_set_mac_mcu_8168fp_1(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u16 i; + u16 breakPointEnabled = 0; + + rtl8168_hw_disable_mac_mcu_bps(dev); + + if(tp->HwPkgDet == 0x00 || tp->HwPkgDet == 0x0F) { + static const u16 mcu_patch_code_8168fp_1_1[] = { + 0xE00A, 0xE0C1, 0xE104, 0xE108, 0xE10D, 0xE112, 0xE11C, 0xE121, 0xE000, + 0xE0C8, 0xB400, 0xC1FE, 0x49E2, 0xF04C, 0x49EA, 0xF04A, 0x74E6, 0xC246, + 0x7542, 0x73EC, 0x1800, 0x49C0, 0xF10D, 0x49C1, 0xF10B, 0x49C2, 0xF109, + 0x49B0, 0xF107, 0x49B1, 0xF105, 0x7220, 0x49A2, 0xF102, 0xE002, 0x4800, + 0x49D0, 0xF10A, 0x49D1, 0xF108, 0x49D2, 0xF106, 0x49D3, 0xF104, 0x49DF, + 0xF102, 0xE00C, 0x4801, 0x72E4, 0x49AD, 0xF108, 0xC225, 0x6741, 0x48F0, + 0x8F41, 0x4870, 0x8F41, 0xC7CF, 0x49B5, 0xF01F, 0x49B2, 0xF00B, 0x4980, + 0xF003, 0x484E, 0x94E7, 0x4981, 0xF004, 0x485E, 0xC212, 0x9543, 0xE071, + 0x49B6, 0xF003, 0x49B3, 0xF10F, 0x4980, 0xF003, 0x484E, 0x94E7, 0x4981, + 0xF004, 0x485E, 0xC204, 0x9543, 0xE005, 0xE000, 0xE0FC, 0xE0FA, 0xE065, + 0x49B7, 0xF007, 0x4980, 0xF005, 0x1A38, 0x46D4, 0x1200, 0xF109, 0x4981, + 0xF055, 0x49C3, 0xF105, 0x1A30, 0x46D5, 0x1200, 0xF04F, 0x7220, 0x49A2, + 0xF130, 0x49C1, 0xF12E, 0x49B0, 0xF12C, 0xC2E6, 0x7240, 0x49A8, 0xF003, + 0x49D0, 0xF126, 0x49A9, 0xF003, 0x49D1, 0xF122, 0x49AA, 0xF003, 0x49D2, + 0xF11E, 0x49AB, 0xF003, 0x49DF, 0xF11A, 0x49AC, 0xF003, 0x49D3, 0xF116, + 0x4980, 0xF003, 0x49C7, 0xF105, 0x4981, 0xF02C, 0x49D7, 0xF02A, 0x49C0, + 0xF00C, 0xC721, 0x62F4, 0x49A0, 0xF008, 0x49A4, 0xF106, 0x4824, 0x8AF4, + 0xC71A, 0x1A40, 0x9AE0, 0x49B6, 0xF017, 0x200E, 0xC7B8, 0x72E0, 0x4710, + 0x92E1, 0xC70E, 0x77E0, 0x49F0, 0xF112, 0xC70B, 0x77E0, 0x27FE, 0x1AFA, + 0x4317, 0xC705, 0x9AE2, 0x1A11, 0x8AE0, 0xE008, 0xE41C, 0xC0AE, 0xD23A, + 0xC7A2, 0x74E6, 0x484F, 0x94E7, 0xC79E, 0x8CE6, 0x8BEC, 0xC29C, 0x8D42, + 0x7220, 0xB000, 0xC502, 0xBD00, 0x0932, 0xB400, 0xC240, 0xC340, 0x7060, + 0x498F, 0xF014, 0x488F, 0x9061, 0x744C, 0x49C3, 0xF004, 0x7562, 0x485E, + 0x9563, 0x7446, 0x49C3, 0xF106, 0x7562, 0x1C30, 0x46E5, 0x1200, 0xF004, + 0x7446, 0x484F, 0x9447, 0xC32A, 0x7466, 0x49C0, 0xF00F, 0x48C0, 0x9C66, + 0x7446, 0x4840, 0x4841, 0x4842, 0x9C46, 0x744C, 0x4840, 0x9C4C, 0x744A, + 0x484A, 0x9C4A, 0xE013, 0x498E, 0xF011, 0x488E, 0x9061, 0x744C, 0x49C3, + 0xF004, 0x7446, 0x484E, 0x9447, 0x7446, 0x1D38, 0x46EC, 0x1500, 0xF004, + 0x7446, 0x484F, 0x9447, 0xB000, 0xC502, 0xBD00, 0x074C, 0xE000, 0xE0FC, + 0xE0C0, 0x4830, 0x4837, 0xC502, 0xBD00, 0x0978, 0x63E2, 0x4830, 0x4837, + 0xC502, 0xBD00, 0x09FE, 0x73E2, 0x4830, 0x8BE2, 0xC302, 0xBB00, 0x0A12, + 0x73E2, 0x48B0, 0x48B3, 0x48B4, 0x48B5, 0x48B6, 0x48B7, 0x8BE2, 0xC302, + 0xBB00, 0x0A5A, 0x73E2, 0x4830, 0x8BE2, 0xC302, 0xBB00, 0x0A6C, 0x73E2, + 0x4830, 0x4837, 0xC502, 0xBD00, 0x0A86 + }; + + for (i = 0; i < ARRAY_SIZE(mcu_patch_code_8168fp_1_1); i++) { + rtl8168_mac_ocp_write(tp, 0xF800 + i * 2, mcu_patch_code_8168fp_1_1[i]); + } + + rtl8168_mac_ocp_write(tp, 0xFC26, 0x8000); + + rtl8168_mac_ocp_write(tp, 0xFC28, 0x0890); + rtl8168_mac_ocp_write(tp, 0xFC2A, 0x0712); + rtl8168_mac_ocp_write(tp, 0xFC2C, 0x0974); + rtl8168_mac_ocp_write(tp, 0xFC2E, 0x09FC); + rtl8168_mac_ocp_write(tp, 0xFC30, 0x0A0E); + rtl8168_mac_ocp_write(tp, 0xFC32, 0x0A56); + rtl8168_mac_ocp_write(tp, 0xFC34, 0x0A68); + rtl8168_mac_ocp_write(tp, 0xFC36, 0x0A84); + + } else if (tp->HwPkgDet == 0x6) { + static const u16 mcu_patch_code_8168fp_1_2[] = { + 0xE008, 0xE00A, 0xE031, 0xE033, 0xE035, 0xE144, 0xE166, 0xE168, 0xC502, + 0xBD00, 0x0000, 0xC725, 0x75E0, 0x48D0, 0x9DE0, 0xC722, 0x75E0, 0x1C78, + 0x416C, 0x1530, 0xF111, 0xC71D, 0x75F6, 0x49D1, 0xF00D, 0x75E0, 0x1C1F, + 0x416C, 0x1502, 0xF108, 0x75FA, 0x49D3, 0xF005, 0x75EC, 0x9DE4, 0x4853, + 0x9DFA, 0xC70B, 0x75E0, 0x4852, 0x4850, 0x9DE0, 0xC602, 0xBE00, 0x04B8, + 0xE420, 0xE000, 0xE0FC, 0xE43C, 0xDC00, 0xEB00, 0xC202, 0xBA00, 0x0000, + 0xC002, 0xB800, 0x0000, 0xB401, 0xB402, 0xB403, 0xB404, 0xB405, 0xB406, + 0xC44D, 0xC54D, 0x1867, 0xE8A2, 0x2318, 0x276E, 0x1601, 0xF106, 0x1A07, + 0xE861, 0xE86B, 0xE873, 0xE037, 0x231E, 0x276E, 0x1602, 0xF10B, 0x1A07, + 0xE858, 0xE862, 0xC247, 0xC344, 0xE8E3, 0xC73B, 0x66E0, 0xE8B5, 0xE029, + 0x231A, 0x276C, 0xC733, 0x9EE0, 0x1866, 0xE885, 0x251C, 0x120F, 0xF011, + 0x1209, 0xF011, 0x2014, 0x240E, 0x1000, 0xF007, 0x120C, 0xF00D, 0x1203, + 0xF00D, 0x1200, 0xF00D, 0x120C, 0xF00D, 0x1203, 0xF00D, 0x1A03, 0xE00C, + 0x1A07, 0xE00A, 0x1A00, 0xE008, 0x1A01, 0xE006, 0x1A02, 0xE004, 0x1A04, + 0xE002, 0x1A05, 0xE829, 0xE833, 0xB006, 0xB005, 0xB004, 0xB003, 0xB002, + 0xB001, 0x60C4, 0xC702, 0xBF00, 0x2786, 0xDD00, 0xD030, 0xE0C4, 0xE0F8, + 0xDC42, 0xD3F0, 0x0000, 0x0004, 0x0007, 0x0014, 0x0090, 0x1000, 0x0F00, + 0x1004, 0x1008, 0x3000, 0x3004, 0x3008, 0x4000, 0x7777, 0x8000, 0x8001, + 0x8008, 0x8003, 0x8004, 0xC000, 0xC004, 0xF004, 0xFFFF, 0xB406, 0xB407, + 0xC6E5, 0x77C0, 0x27F3, 0x23F3, 0x47FA, 0x9FC0, 0xB007, 0xB006, 0xFF80, + 0xB405, 0xB407, 0xC7D8, 0x75E0, 0x48D0, 0x9DE0, 0xB007, 0xB005, 0xFF80, + 0xB401, 0xC0EA, 0xC2DC, 0xC3D8, 0xE865, 0xC0D3, 0xC1E0, 0xC2E3, 0xE861, + 0xE817, 0xC0CD, 0xC2CF, 0xE85D, 0xC0C9, 0xC1D6, 0xC2DB, 0xE859, 0xE80F, + 0xC1C7, 0xC2CE, 0xE855, 0xC0C0, 0xC1D1, 0xC2D3, 0xE851, 0xE807, 0xC0BE, + 0xC2C2, 0xE84D, 0xE803, 0xB001, 0xFF80, 0xB402, 0xC2C6, 0xE859, 0x499F, + 0xF1FE, 0xB002, 0xFF80, 0xB402, 0xB403, 0xB407, 0xE821, 0x8882, 0x1980, + 0x8983, 0xE81D, 0x7180, 0x218B, 0x25BB, 0x1310, 0xF014, 0x1310, 0xFB03, + 0x1F20, 0x38FB, 0x3288, 0x434B, 0x2491, 0x430B, 0x1F0F, 0x38FB, 0x4313, + 0x2121, 0x4353, 0x2521, 0x418A, 0x6282, 0x2527, 0x212F, 0x418A, 0xB007, + 0xB003, 0xB002, 0xFF80, 0x6183, 0x2496, 0x1100, 0xF1FD, 0xFF80, 0x4800, + 0x4801, 0xC213, 0xC313, 0xE815, 0x4860, 0x8EE0, 0xC210, 0xC310, 0xE822, + 0x481E, 0xC20C, 0xC30C, 0xE80C, 0xC206, 0x7358, 0x483A, 0x9B58, 0xFF80, + 0xE8E0, 0xE000, 0x1008, 0x0F00, 0x800C, 0x0F00, 0xB407, 0xB406, 0xB403, + 0xC7F7, 0x98E0, 0x99E2, 0x9AE4, 0x21B2, 0x4831, 0x483F, 0x9BE6, 0x66E7, + 0x49E6, 0xF1FE, 0xB003, 0xB006, 0xB007, 0xFF80, 0xB407, 0xB406, 0xB403, + 0xC7E5, 0x9AE4, 0x21B2, 0x4831, 0x9BE6, 0x66E7, 0x49E6, 0xF1FE, 0x70E0, + 0x71E2, 0xB003, 0xB006, 0xB007, 0xFF80, 0x4882, 0xB406, 0xB405, 0xC71E, + 0x76E0, 0x1D78, 0x4175, 0x1630, 0xF10C, 0xC715, 0x76E0, 0x4861, 0x9EE0, + 0xC713, 0x1EFF, 0x9EE2, 0x75E0, 0x4850, 0x9DE0, 0xE005, 0xC70B, 0x76E0, + 0x4865, 0x9EE0, 0xB005, 0xB006, 0xC708, 0xC102, 0xB900, 0x279E, 0xEB16, + 0xEB00, 0xE43C, 0xDC00, 0xD3EC, 0xC602, 0xBE00, 0x0000, 0xC602, 0xBE00, + 0x0000 + }; + + for (i = 0; i < ARRAY_SIZE(mcu_patch_code_8168fp_1_2); i++) { + rtl8168_mac_ocp_write(tp, 0xF800 + i * 2, mcu_patch_code_8168fp_1_2[i]); + } + + rtl8168_mac_ocp_write(tp, 0xFC26, 0x8000); + + rtl8168_mac_ocp_write(tp, 0xFC28, 0x0000); + rtl8168_mac_ocp_write(tp, 0xFC2A, 0x04b4); + rtl8168_mac_ocp_write(tp, 0xFC2C, 0x0000); + rtl8168_mac_ocp_write(tp, 0xFC2E, 0x0000); + rtl8168_mac_ocp_write(tp, 0xFC30, 0x0000); + rtl8168_mac_ocp_write(tp, 0xFC32, 0x279C); + rtl8168_mac_ocp_write(tp, 0xFC34, 0x0000); + rtl8168_mac_ocp_write(tp, 0xFC36, 0x0000); + } + + if (tp->HwPkgDet == 0x00) + breakPointEnabled = 0x00FC; + else if (tp->HwPkgDet == 0x0F) + breakPointEnabled = 0x00FF; + else if (tp->HwPkgDet == 0x06) + breakPointEnabled = 0x0022; + + rtl8168_mac_ocp_write(tp, 0xFC38, breakPointEnabled); +} + +static void +rtl8168_set_mac_mcu_8168fp_2(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u16 i; + static const u16 mcu_patch_code_8168fp_2[] = { + 0xE008, 0xE00A, 0xE00F, 0xE014, 0xE05F, 0xE063, 0xE065, 0xE067, 0xC602, + 0xBE00, 0x2AB2, 0x1BC0, 0x46EB, 0x1BFE, 0xC102, 0xB900, 0x0B1A, 0x1BC0, + 0x46EB, 0x1B7E, 0xC102, 0xB900, 0x0BEA, 0xB400, 0xB401, 0xB402, 0xB403, + 0xB404, 0xB405, 0xC03A, 0x7206, 0x49AE, 0xF1FE, 0xC137, 0x9904, 0xC136, + 0x9906, 0x7206, 0x49AE, 0xF1FE, 0x7200, 0x49A0, 0xF10B, 0xC52F, 0xC12E, + 0xC232, 0xC332, 0xE812, 0xC331, 0xE810, 0xC330, 0xE80E, 0xE018, 0xC126, + 0xC229, 0xC525, 0xC328, 0xE808, 0xC523, 0xC326, 0xE805, 0xC521, 0xC324, + 0xE802, 0xE00C, 0x740E, 0x49CE, 0xF1FE, 0x9908, 0x9D0A, 0x9A0C, 0x9B0E, + 0x740E, 0x49CE, 0xF1FE, 0xFF80, 0xB005, 0xB004, 0xB003, 0xB002, 0xB001, + 0xB000, 0xC604, 0xC002, 0xB800, 0x2A5E, 0xE000, 0xE8E0, 0xF128, 0x3DC2, + 0xFFFF, 0x10EC, 0x816A, 0x816D, 0x816C, 0xF000, 0x8002, 0x8004, 0x8007, + 0x48C1, 0x48C2, 0xC502, 0xBD00, 0x07BC, 0xC602, 0xBE00, 0x0000, 0xC602, + 0xBE00, 0x0000, 0xC602, 0xBE00, 0x0000 + }; + + rtl8168_hw_disable_mac_mcu_bps(dev); + + for (i = 0; i < ARRAY_SIZE(mcu_patch_code_8168fp_2); i++) { + rtl8168_mac_ocp_write(tp, 0xF800 + i * 2, mcu_patch_code_8168fp_2[i]); + } + + rtl8168_mac_ocp_write(tp, 0xFC26, 0x8000); + + rtl8168_mac_ocp_write(tp, 0xFC28, 0x2AAC); + rtl8168_mac_ocp_write(tp, 0xFC2A, 0x0B14); + rtl8168_mac_ocp_write(tp, 0xFC2C, 0x0BE4); + rtl8168_mac_ocp_write(tp, 0xFC2E, 0x2A5C); + rtl8168_mac_ocp_write(tp, 0xFC30, 0x07B0); + + if (tp->HwSuppSerDesPhyVer == 1) + rtl8168_mac_ocp_write(tp, 0xFC38, 0x001F); + else + rtl8168_mac_ocp_write(tp, 0xFC38, 0x001E); + +} + +static void +rtl8168_set_mac_mcu_8168fp_3(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u16 i; + static const u16 mcu_patch_code_8168fp_3[] = { + 0xE008, 0xE053, 0xE057, 0xE059, 0xE05B, 0xE05D, 0xE05F, 0xE061, 0xB400, + 0xB401, 0xB402, 0xB403, 0xB404, 0xB405, 0xC03A, 0x7206, 0x49AE, 0xF1FE, + 0xC137, 0x9904, 0xC136, 0x9906, 0x7206, 0x49AE, 0xF1FE, 0x7200, 0x49A0, + 0xF10B, 0xC52F, 0xC12E, 0xC232, 0xC332, 0xE812, 0xC331, 0xE810, 0xC330, + 0xE80E, 0xE018, 0xC126, 0xC229, 0xC525, 0xC328, 0xE808, 0xC523, 0xC326, + 0xE805, 0xC521, 0xC324, 0xE802, 0xE00C, 0x740E, 0x49CE, 0xF1FE, 0x9908, + 0x9D0A, 0x9A0C, 0x9B0E, 0x740E, 0x49CE, 0xF1FE, 0xFF80, 0xB005, 0xB004, + 0xB003, 0xB002, 0xB001, 0xB000, 0xC604, 0xC002, 0xB800, 0x2B16, 0xE000, + 0xE8E0, 0xF128, 0x3DC2, 0xFFFF, 0x10EC, 0x816A, 0x816D, 0x816C, 0xF000, + 0x8002, 0x8004, 0x8007, 0x48C1, 0x48C2, 0xC502, 0xBD00, 0x07BC, 0xC602, + 0xBE00, 0x0000, 0xC602, 0xBE00, 0x0000, 0xC602, 0xBE00, 0x0000, 0xC602, + 0xBE00, 0x0000, 0xC602, 0xBE00, 0x0000, 0xC602, 0xBE00, 0x0000 + }; + + rtl8168_hw_disable_mac_mcu_bps(dev); + + for (i = 0; i < ARRAY_SIZE(mcu_patch_code_8168fp_3); i++) { + rtl8168_mac_ocp_write(tp, 0xF800 + i * 2, mcu_patch_code_8168fp_3[i]); + } + + rtl8168_mac_ocp_write(tp, 0xFC26, 0x8000); + + rtl8168_mac_ocp_write(tp, 0xFC28, 0x2B14); + rtl8168_mac_ocp_write(tp, 0xFC2A, 0x07B0); + + rtl8168_mac_ocp_write(tp, 0xFC38, 0x0003); +} + +static void +rtl8168_hw_mac_mcu_config(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + if (tp->NotWrMcuPatchCode == TRUE) return; + + switch (tp->mcfg) { + case CFG_METHOD_21: + rtl8168_set_mac_mcu_8168g_1(dev); + break; + case CFG_METHOD_24: + rtl8168_set_mac_mcu_8168gu_1(dev); + break; + case CFG_METHOD_25: + rtl8168_set_mac_mcu_8168gu_2(dev); + break; + case CFG_METHOD_26: + rtl8168_set_mac_mcu_8411b_1(dev); + break; + case CFG_METHOD_27: + rtl8168_set_mac_mcu_8168ep_1(dev); + break; + case CFG_METHOD_28: + rtl8168_set_mac_mcu_8168ep_2(dev); + break; + case CFG_METHOD_29: + case CFG_METHOD_30: + rtl8168_set_mac_mcu_8168h_1(dev); + break; + case CFG_METHOD_31: + rtl8168_set_mac_mcu_8168fp_1(dev); + break; + case CFG_METHOD_32: + rtl8168_set_mac_mcu_8168fp_2(dev); + break; + case CFG_METHOD_33: + rtl8168_set_mac_mcu_8168fp_3(dev); + break; + } +} +#endif + +#ifdef ENABLE_USE_FIRMWARE_FILE +static void rtl8168_release_firmware(struct rtl8168_private *tp) +{ + if (tp->rtl_fw) { + rtl8168_fw_release_firmware(tp->rtl_fw); + kfree(tp->rtl_fw); + tp->rtl_fw = NULL; + } +} + +void rtl8168_apply_firmware(struct rtl8168_private *tp) +{ + /* TODO: release firmware if rtl_fw_write_firmware signals failure. */ + if (tp->rtl_fw) { + rtl8168_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); + rtl8168_wait_phy_reset_complete(tp); + + tp->hw_ram_code_ver = rtl8168_get_hw_phy_mcu_code_ver(tp); + tp->sw_ram_code_ver = tp->hw_ram_code_ver; + tp->HwHasWrRamCodeToMicroP = TRUE; + } +} +#endif + +static void +rtl8168_hw_init(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u32 csi_tmp; + + if (tp->HwSuppAspmClkIntrLock) { + RTL_W8(tp, 0xF1, RTL_R8(tp, 0xF1) & ~BIT_7); + rtl8168_enable_cfg9346_write(tp); + rtl8168_hw_aspm_clkreq_enable(tp, false); + rtl8168_disable_cfg9346_write(tp); + } + + //Disable UPS + if (HW_SUPPORT_UPS_MODE(tp)) + rtl8168_mac_ocp_write(tp, 0xD400, rtl8168_mac_ocp_read( tp, 0xD400) & ~(BIT_0)); + + //Disable DMA Aggregation + switch (tp->mcfg) { + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_mac_ocp_write(tp, 0xE63E, rtl8168_mac_ocp_read( tp, 0xE63E) & ~(BIT_3 | BIT_2 | BIT_1)); + rtl8168_mac_ocp_write(tp, 0xE63E, rtl8168_mac_ocp_read( tp, 0xE63E) | (BIT_0)); + rtl8168_mac_ocp_write(tp, 0xE63E, rtl8168_mac_ocp_read( tp, 0xE63E) & ~(BIT_0)); + rtl8168_mac_ocp_write(tp, 0xC094, 0x0); + rtl8168_mac_ocp_write(tp, 0xC09E, 0x0); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_9: + case CFG_METHOD_10: + RTL_W8(tp, DBG_reg, RTL_R8(tp, DBG_reg) | BIT_1 | BIT_7); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_14: + case CFG_METHOD_15: + case CFG_METHOD_16: + case CFG_METHOD_17: + case CFG_METHOD_18: + case CFG_METHOD_19: + RTL_W8(tp, 0xF2, (RTL_R8(tp, 0xF2) & ~(BIT_2 | BIT_1 | BIT_0))); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_16: + case CFG_METHOD_17: + case CFG_METHOD_18: + case CFG_METHOD_19: + case CFG_METHOD_20: + if (aspm) { + RTL_W8(tp, 0x6E, RTL_R8(tp, 0x6E) | BIT_6); + rtl8168_eri_write(tp, 0x1AE, 2, 0x0403, ERIAR_ExGMAC); + } + break; + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_29: + case CFG_METHOD_30: + if (aspm) { + if ((rtl8168_mac_ocp_read(tp, 0xDC00) & BIT_3) || (RTL_R8(tp, Config0) & 0x07)) { + RTL_W8(tp, 0x6E, RTL_R8(tp, 0x6E) | BIT_6); + rtl8168_eri_write(tp, 0x1AE, 2, 0x0403, ERIAR_ExGMAC); + } + } + break; + } + + if (tp->mcfg == CFG_METHOD_10 || tp->mcfg == CFG_METHOD_14 || tp->mcfg == CFG_METHOD_15) + RTL_W8(tp, 0xF3, RTL_R8(tp, 0xF3) | BIT_2); + +#ifndef ENABLE_USE_FIRMWARE_FILE + if (!tp->rtl_fw) + rtl8168_hw_mac_mcu_config(dev); +#endif + + /*disable ocp phy power saving*/ + if (tp->mcfg == CFG_METHOD_25 || tp->mcfg == CFG_METHOD_26 || + tp->mcfg == CFG_METHOD_27 || tp->mcfg == CFG_METHOD_28 || + tp->mcfg == CFG_METHOD_29 || tp->mcfg == CFG_METHOD_30 || + tp->mcfg == CFG_METHOD_31 || tp->mcfg == CFG_METHOD_32 || + tp->mcfg == CFG_METHOD_33) + if (!tp->dash_printer_enabled) + rtl8168_disable_ocp_phy_power_saving(dev); + + //Set PCIE uncorrectable error status mask pcie 0x108 + csi_tmp = rtl8168_csi_read(tp, 0x108); + csi_tmp |= BIT_20; + rtl8168_csi_write(tp, 0x108, csi_tmp); + + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + csi_tmp = rtl8168_eri_read(tp, 0x1AB, 1, ERIAR_ExGMAC); + csi_tmp |= ( BIT_2 | BIT_3 | BIT_4 | BIT_5 | BIT_6 | BIT_7 ); + rtl8168_eri_write(tp, 0x1AB, 1, csi_tmp, ERIAR_ExGMAC); + break; + } + + rtl8168_set_pci_pme(tp, 0); + + if (s0_magic_packet == 1) + rtl8168_enable_magic_packet(dev); + +#ifdef ENABLE_USE_FIRMWARE_FILE + if (tp->rtl_fw && + !(HW_DASH_SUPPORT_TYPE_3(tp) && + tp->HwPkgDet == 0x06)) + rtl8168_apply_firmware(tp); +#endif +} + +static void +rtl8168_hw_ephy_config(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u16 ephy_data; + + switch (tp->mcfg) { + case CFG_METHOD_4: + /*Set EPHY registers begin*/ + /*Set EPHY register offset 0x02 bit 11 to 0 and bit 12 to 1*/ + ephy_data = rtl8168_ephy_read(tp, 0x02); + ephy_data &= ~BIT_11; + ephy_data |= BIT_12; + rtl8168_ephy_write(tp, 0x02, ephy_data); + + /*Set EPHY register offset 0x03 bit 1 to 1*/ + ephy_data = rtl8168_ephy_read(tp, 0x03); + ephy_data |= (1 << 1); + rtl8168_ephy_write(tp, 0x03, ephy_data); + + /*Set EPHY register offset 0x06 bit 7 to 0*/ + ephy_data = rtl8168_ephy_read(tp, 0x06); + ephy_data &= ~(1 << 7); + rtl8168_ephy_write(tp, 0x06, ephy_data); + /*Set EPHY registers end*/ + + break; + case CFG_METHOD_5: + /* set EPHY registers */ + SetPCIePhyBit(tp, 0x01, BIT_0); + + ClearAndSetPCIePhyBit(tp, + 0x03, + BIT_10, + BIT_5 + ); + + break; + case CFG_METHOD_9: + /* set EPHY registers */ + rtl8168_ephy_write(tp, 0x01, 0x7C7F); + rtl8168_ephy_write(tp, 0x02, 0x011F); + if (tp->eeprom_type != EEPROM_TYPE_NONE) { + ClearAndSetPCIePhyBit(tp, + 0x03, + 0xFFB0, + 0x05B0 + ); + } else { + ClearAndSetPCIePhyBit(tp, + 0x03, + 0xFFF0, + 0x05F0 + ); + } + rtl8168_ephy_write(tp, 0x06, 0xB271); + rtl8168_ephy_write(tp, 0x07, 0xCE00); + + break; + case CFG_METHOD_10: + /* set EPHY registers */ + rtl8168_ephy_write(tp, 0x01, 0x6C7F); + rtl8168_ephy_write(tp, 0x02, 0x011F); + ClearAndSetPCIePhyBit(tp, + 0x03, + 0xFFF0, + 0x01B0 + ); + rtl8168_ephy_write(tp, 0x1A, 0x0546); + rtl8168_ephy_write(tp, 0x1C, 0x80C4); + rtl8168_ephy_write(tp, 0x1D, 0x78E5); + rtl8168_ephy_write(tp, 0x0A, 0x8100); + + break; + case CFG_METHOD_12: + case CFG_METHOD_13: + ephy_data = rtl8168_ephy_read(tp, 0x0B); + rtl8168_ephy_write(tp, 0x0B, ephy_data|0x48); + ephy_data = rtl8168_ephy_read(tp, 0x19); + ephy_data &= ~0x20; + rtl8168_ephy_write(tp, 0x19, ephy_data|0x50); + ephy_data = rtl8168_ephy_read(tp, 0x0C); + ephy_data &= ~0x100; + rtl8168_ephy_write(tp, 0x0C, ephy_data|0x20); + ephy_data = rtl8168_ephy_read(tp, 0x10); + ephy_data &= ~0x04; + rtl8168_ephy_write(tp, 0x10, ephy_data); + + break; + case CFG_METHOD_14: + case CFG_METHOD_15: + /* set EPHY registers */ + ephy_data = rtl8168_ephy_read(tp, 0x00) & ~0x0200; + ephy_data |= 0x0100; + rtl8168_ephy_write(tp, 0x00, ephy_data); + + ephy_data = rtl8168_ephy_read(tp, 0x00); + ephy_data |= 0x0004; + rtl8168_ephy_write(tp, 0x00, ephy_data); + + ephy_data = rtl8168_ephy_read(tp, 0x06) & ~0x0002; + ephy_data |= 0x0001; + rtl8168_ephy_write(tp, 0x06, ephy_data); + + ephy_data = rtl8168_ephy_read(tp, 0x06); + ephy_data |= 0x0030; + rtl8168_ephy_write(tp, 0x06, ephy_data); + + ephy_data = rtl8168_ephy_read(tp, 0x07); + ephy_data |= 0x2000; + rtl8168_ephy_write(tp, 0x07, ephy_data); + + ephy_data = rtl8168_ephy_read(tp, 0x00); + ephy_data |= 0x0020; + rtl8168_ephy_write(tp, 0x00, ephy_data); + + ephy_data = rtl8168_ephy_read(tp, 0x03) & ~0x5800; + ephy_data |= 0x2000; + rtl8168_ephy_write(tp, 0x03, ephy_data); + + ephy_data = rtl8168_ephy_read(tp, 0x03); + ephy_data |= 0x0001; + rtl8168_ephy_write(tp, 0x03, ephy_data); + + ephy_data = rtl8168_ephy_read(tp, 0x01) & ~0x0800; + ephy_data |= 0x1000; + rtl8168_ephy_write(tp, 0x01, ephy_data); + + ephy_data = rtl8168_ephy_read(tp, 0x07); + ephy_data |= 0x4000; + rtl8168_ephy_write(tp, 0x07, ephy_data); + + ephy_data = rtl8168_ephy_read(tp, 0x1E); + ephy_data |= 0x2000; + rtl8168_ephy_write(tp, 0x1E, ephy_data); + + rtl8168_ephy_write(tp, 0x19, 0xFE6C); + + ephy_data = rtl8168_ephy_read(tp, 0x0A); + ephy_data |= 0x0040; + rtl8168_ephy_write(tp, 0x0A, ephy_data); + + break; + case CFG_METHOD_16: + case CFG_METHOD_17: + if (tp->mcfg == CFG_METHOD_16) { + rtl8168_ephy_write(tp, 0x06, 0xF020); + rtl8168_ephy_write(tp, 0x07, 0x01FF); + rtl8168_ephy_write(tp, 0x00, 0x5027); + rtl8168_ephy_write(tp, 0x01, 0x0003); + rtl8168_ephy_write(tp, 0x02, 0x2D16); + rtl8168_ephy_write(tp, 0x03, 0x6D49); + rtl8168_ephy_write(tp, 0x08, 0x0006); + rtl8168_ephy_write(tp, 0x0A, 0x00C8); + } + + ephy_data = rtl8168_ephy_read(tp, 0x09); + ephy_data |= BIT_7; + rtl8168_ephy_write(tp, 0x09, ephy_data); + + ephy_data = rtl8168_ephy_read(tp, 0x19); + ephy_data |= (BIT_2 | BIT_5 | BIT_9); + rtl8168_ephy_write(tp, 0x19, ephy_data); + + ephy_data = rtl8168_ephy_read(tp, 0x00); + ephy_data |= BIT_3; + rtl8168_ephy_write(tp, 0x00, ephy_data); + ephy_data = rtl8168_ephy_read(tp, 0x0C); + ephy_data &= ~(BIT_13 | BIT_12 | BIT_11 | BIT_10 | BIT_8 | BIT_7 | BIT_6 | BIT_5 | BIT_4); + ephy_data |= BIT_9; + rtl8168_ephy_write(tp, 0x0C, ephy_data); + + break; + case CFG_METHOD_18: + case CFG_METHOD_19: + if (tp->mcfg == CFG_METHOD_18) { + ephy_data = rtl8168_ephy_read(tp, 0x06); + ephy_data |= BIT_5; + ephy_data &= ~(BIT_7 | BIT_6); + rtl8168_ephy_write(tp, 0x06, ephy_data); + + ephy_data = rtl8168_ephy_read(tp, 0x08); + ephy_data |= BIT_1; + ephy_data &= ~BIT_0; + rtl8168_ephy_write(tp, 0x08, ephy_data); + } + + ephy_data = rtl8168_ephy_read(tp, 0x09); + ephy_data |= BIT_7; + rtl8168_ephy_write(tp, 0x09, ephy_data); + + ephy_data = rtl8168_ephy_read(tp, 0x19); + ephy_data |= (BIT_2 | BIT_5 | BIT_9); + rtl8168_ephy_write(tp, 0x19, ephy_data); + + ephy_data = rtl8168_ephy_read(tp, 0x00); + ephy_data |= BIT_3; + rtl8168_ephy_write(tp, 0x00, ephy_data); + ephy_data = rtl8168_ephy_read(tp, 0x0C); + ephy_data &= ~(BIT_13 | BIT_12 | BIT_11 | BIT_10 | BIT_8 | BIT_7 | BIT_6 | BIT_5 | BIT_4); + ephy_data |= BIT_9; + rtl8168_ephy_write(tp, 0x0C, ephy_data); + + break; + case CFG_METHOD_20: + ephy_data = rtl8168_ephy_read(tp, 0x06); + ephy_data |= BIT_5; + ephy_data &= ~(BIT_7 | BIT_6); + rtl8168_ephy_write(tp, 0x06, ephy_data); + + rtl8168_ephy_write(tp, 0x0f, 0x5200); + + ephy_data = rtl8168_ephy_read(tp, 0x19); + ephy_data |= (BIT_2 | BIT_5 | BIT_9); + rtl8168_ephy_write(tp, 0x19, ephy_data); + + ephy_data = rtl8168_ephy_read(tp, 0x00); + ephy_data |= BIT_3; + rtl8168_ephy_write(tp, 0x00, ephy_data); + ephy_data = rtl8168_ephy_read(tp, 0x0C); + ephy_data &= ~(BIT_13 | BIT_12 | BIT_11 | BIT_10 | BIT_8 | BIT_7 | BIT_6 | BIT_5 | BIT_4); + ephy_data |= BIT_9; + rtl8168_ephy_write(tp, 0x0C, ephy_data); + + break; + case CFG_METHOD_21: + case CFG_METHOD_22: + + ephy_data = rtl8168_ephy_read(tp, 0x00); + ephy_data &= ~(BIT_3); + rtl8168_ephy_write(tp, 0x00, ephy_data); + ephy_data = rtl8168_ephy_read(tp, 0x0C); + ephy_data &= ~(BIT_13 | BIT_12 | BIT_11 | BIT_10 | BIT_9 | BIT_8 | BIT_7 | BIT_6 | BIT_5 | BIT_4); + ephy_data |= (BIT_5 | BIT_11); + rtl8168_ephy_write(tp, 0x0C, ephy_data); + + ephy_data = rtl8168_ephy_read(tp, 0x1E); + ephy_data |= (BIT_0); + rtl8168_ephy_write(tp, 0x1E, ephy_data); + + ephy_data = rtl8168_ephy_read(tp, 0x19); + ephy_data &= ~(BIT_15); + rtl8168_ephy_write(tp, 0x19, ephy_data); + + break; + case CFG_METHOD_25: + ephy_data = rtl8168_ephy_read(tp, 0x00); + ephy_data &= ~BIT_3; + rtl8168_ephy_write(tp, 0x00, ephy_data); + ephy_data = rtl8168_ephy_read(tp, 0x0C); + ephy_data &= ~(BIT_13 | BIT_12 | BIT_11 | BIT_10| BIT_9 | BIT_8 | BIT_7 | BIT_6 | BIT_5 | BIT_4); + ephy_data |= (BIT_5 | BIT_11); + rtl8168_ephy_write(tp, 0x0C, ephy_data); + + rtl8168_ephy_write(tp, 0x19, 0x7C00); + rtl8168_ephy_write(tp, 0x1E, 0x20EB); + rtl8168_ephy_write(tp, 0x0D, 0x1666); + rtl8168_ephy_write(tp, 0x00, 0x10A3); + rtl8168_ephy_write(tp, 0x06, 0xF050); + + SetPCIePhyBit(tp, 0x04, BIT_4); + ClearPCIePhyBit(tp, 0x1D, BIT_14); + + break; + case CFG_METHOD_26: + ClearPCIePhyBit(tp, 0x00, BIT_3); + ClearAndSetPCIePhyBit( tp, + 0x0C, + (BIT_13 | BIT_12 | BIT_10 | BIT_9 | BIT_8 | BIT_7 | BIT_6 | BIT_4), + (BIT_5 | BIT_11) + ); + SetPCIePhyBit(tp, 0x1E, BIT_0); + ClearPCIePhyBit(tp, 0x19, BIT_15); + + ClearPCIePhyBit(tp, 0x19, (BIT_5 | BIT_0)); + + SetPCIePhyBit(tp, 0x1E, BIT_13); + ClearPCIePhyBit(tp, 0x0D, BIT_8); + SetPCIePhyBit(tp, 0x0D, BIT_9); + SetPCIePhyBit(tp, 0x00, BIT_7); + + SetPCIePhyBit(tp, 0x06, BIT_4); + + SetPCIePhyBit(tp, 0x04, BIT_4); + SetPCIePhyBit(tp, 0x1D, BIT_14); + + break; + case CFG_METHOD_23: + rtl8168_ephy_write(tp, 0x00, 0x10AB); + rtl8168_ephy_write(tp, 0x06, 0xf030); + rtl8168_ephy_write(tp, 0x08, 0x2006); + rtl8168_ephy_write(tp, 0x0D, 0x1666); + + ephy_data = rtl8168_ephy_read(tp, 0x0C); + ephy_data &= ~(BIT_13 | BIT_12 | BIT_11 | BIT_10 | BIT_9 | BIT_8 | BIT_7 | BIT_6 | BIT_5 | BIT_4); + rtl8168_ephy_write(tp, 0x0C, ephy_data); + + break; + case CFG_METHOD_27: + rtl8168_ephy_write(tp, 0x00, 0x10A3); + rtl8168_ephy_write(tp, 0x19, 0xFC00); + rtl8168_ephy_write(tp, 0x1E, 0x20EA); + + break; + case CFG_METHOD_28: + rtl8168_ephy_write(tp, 0x00, 0x10AB); + rtl8168_ephy_write(tp, 0x19, 0xFC00); + rtl8168_ephy_write(tp, 0x1E, 0x20EB); + rtl8168_ephy_write(tp, 0x0D, 0x1666); + ClearPCIePhyBit(tp, 0x0B, BIT_0); + SetPCIePhyBit(tp, 0x1D, BIT_14); + ClearAndSetPCIePhyBit(tp, + 0x0C, + BIT_13 | BIT_12 | BIT_11 | BIT_10 | BIT_8 | BIT_7 | BIT_6 | BIT_5, + BIT_9 | BIT_4 + ); + + break; + case CFG_METHOD_29: + case CFG_METHOD_30: + ClearPCIePhyBit(tp, 0x1E, BIT_11); + + SetPCIePhyBit(tp, 0x1E, BIT_0); + SetPCIePhyBit(tp, 0x1D, BIT_11); + + rtl8168_ephy_write(tp, 0x05, 0x2089); + rtl8168_ephy_write(tp, 0x06, 0x5881); + + rtl8168_ephy_write(tp, 0x04, 0x854A); + rtl8168_ephy_write(tp, 0x01, 0x068B); + + break; + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + ClearAndSetPCIePhyBit(tp, + 0x19, + BIT_6, + (BIT_12| BIT_8) + ); + ClearAndSetPCIePhyBit(tp, + 0x59, + BIT_6, + (BIT_12| BIT_8) + ); + + ClearPCIePhyBit(tp, 0x0C, BIT_4); + ClearPCIePhyBit(tp, 0x4C, BIT_4); + ClearPCIePhyBit(tp, 0x0B, BIT_0); + + break; + } +} + +static int +rtl8168_set_phy_mcu_patch_request(struct rtl8168_private *tp) +{ + u16 PhyRegValue; + u32 WaitCnt; + int retval = TRUE; + + switch (tp->mcfg) { + case CFG_METHOD_21 ... CFG_METHOD_33: + rtl8168_mdio_write(tp,0x1f, 0x0B82); + rtl8168_set_eth_phy_bit(tp, 0x10, BIT_4); + + rtl8168_mdio_write(tp,0x1f, 0x0B80); + WaitCnt = 0; + do { + PhyRegValue = rtl8168_mdio_read(tp, 0x10); + udelay(100); + WaitCnt++; + } while (!(PhyRegValue & BIT_6) && (WaitCnt < 1000)); + + if (!(PhyRegValue & BIT_6) && (WaitCnt == 1000)) retval = FALSE; + + rtl8168_mdio_write(tp,0x1f, 0x0000); + break; + } + + return retval; +} + +static int +rtl8168_clear_phy_mcu_patch_request(struct rtl8168_private *tp) +{ + u16 PhyRegValue; + u32 WaitCnt; + int retval = TRUE; + + switch (tp->mcfg) { + case CFG_METHOD_21 ... CFG_METHOD_33: + rtl8168_mdio_write(tp, 0x1f, 0x0B82); + rtl8168_clear_eth_phy_bit(tp, 0x10, BIT_4); + + rtl8168_mdio_write(tp,0x1f, 0x0B80); + WaitCnt = 0; + do { + PhyRegValue = rtl8168_mdio_read(tp, 0x10); + udelay(100); + WaitCnt++; + } while ((PhyRegValue & BIT_6) && (WaitCnt < 1000)); + + if ((PhyRegValue & BIT_6) && (WaitCnt == 1000)) retval = FALSE; + + rtl8168_mdio_write(tp,0x1f, 0x0000); + break; + } + + return retval; +} + +static u16 +rtl8168_get_hw_phy_mcu_code_ver(struct rtl8168_private *tp) +{ + u16 hw_ram_code_ver = ~0; + + switch (tp->mcfg) { + case CFG_METHOD_14: + case CFG_METHOD_15: + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B60); + hw_ram_code_ver = rtl8168_mdio_read(tp, 0x06); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + break; + case CFG_METHOD_16: + case CFG_METHOD_17: + case CFG_METHOD_18: + case CFG_METHOD_19: + case CFG_METHOD_20: + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B30); + hw_ram_code_ver = rtl8168_mdio_read(tp, 0x06); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + break; + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x801E); + hw_ram_code_ver = rtl8168_mdio_read(tp, 0x14); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + break; + default: + tp->hw_ram_code_ver = ~0; + break; + } + + return hw_ram_code_ver; +} + +#ifndef ENABLE_USE_FIRMWARE_FILE +static void +rtl8168_enable_phy_disable_mode(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + switch (tp->HwSuppCheckPhyDisableModeVer) { + case 1: + rtl8168_mac_ocp_write(tp, 0xDC20, rtl8168_mac_ocp_read(tp, 0xDC20) | BIT_1); + break; + case 2: + case 3: + RTL_W8(tp, 0xF2, RTL_R8(tp, 0xF2) | BIT_5); + break; + } + + dprintk("enable phy disable mode.\n"); +} + +static void +rtl8168_disable_phy_disable_mode(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + switch (tp->HwSuppCheckPhyDisableModeVer) { + case 1: + rtl8168_mac_ocp_write(tp, 0xDC20, rtl8168_mac_ocp_read(tp, 0xDC20) & ~BIT_1); + break; + case 2: + case 3: + RTL_W8(tp, 0xF2, RTL_R8(tp, 0xF2) & ~BIT_5); + break; + } + + mdelay(1); + + dprintk("disable phy disable mode.\n"); +} + +static int +rtl8168_check_hw_phy_mcu_code_ver(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + int ram_code_ver_match = 0; + + tp->hw_ram_code_ver = rtl8168_get_hw_phy_mcu_code_ver(tp); + + if ( tp->hw_ram_code_ver == tp->sw_ram_code_ver) { + ram_code_ver_match = 1; + tp->HwHasWrRamCodeToMicroP = TRUE; + } + + return ram_code_ver_match; +} + +static void +rtl8168_write_hw_phy_mcu_code_ver(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + switch (tp->mcfg) { + case CFG_METHOD_14: + case CFG_METHOD_15: + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B60); + rtl8168_mdio_write(tp, 0x06, tp->sw_ram_code_ver); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + tp->hw_ram_code_ver = tp->sw_ram_code_ver; + break; + case CFG_METHOD_16: + case CFG_METHOD_17: + case CFG_METHOD_18: + case CFG_METHOD_19: + case CFG_METHOD_20: + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B30); + rtl8168_mdio_write(tp, 0x06, tp->sw_ram_code_ver); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + tp->hw_ram_code_ver = tp->sw_ram_code_ver; + break; + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x801E); + rtl8168_mdio_write(tp, 0x14, tp->sw_ram_code_ver); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + tp->hw_ram_code_ver = tp->sw_ram_code_ver; + break; + } +} +static int +rtl8168_phy_ram_code_check(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u16 PhyRegValue; + int retval = TRUE; + + switch(tp->mcfg) { + case CFG_METHOD_21: + rtl8168_mdio_write(tp, 0x1f, 0x0A40); + PhyRegValue = rtl8168_mdio_read(tp, 0x10); + PhyRegValue &= ~(BIT_11); + rtl8168_mdio_write(tp, 0x10, PhyRegValue); + + + rtl8168_mdio_write(tp, 0x1f, 0x0A00); + PhyRegValue = rtl8168_mdio_read(tp, 0x10); + PhyRegValue &= ~(BIT_12 | BIT_13 | BIT_14 | BIT_15); + rtl8168_mdio_write(tp, 0x10, PhyRegValue); + + rtl8168_mdio_write(tp, 0x1f, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8010); + PhyRegValue = rtl8168_mdio_read(tp, 0x14); + PhyRegValue &= ~(BIT_11); + rtl8168_mdio_write(tp, 0x14, PhyRegValue); + + retval = rtl8168_set_phy_mcu_patch_request(tp); + + rtl8168_mdio_write(tp, 0x1f, 0x0A40); + rtl8168_mdio_write(tp, 0x10, 0x0140); + + rtl8168_mdio_write(tp, 0x1f, 0x0A4A); + PhyRegValue = rtl8168_mdio_read(tp, 0x13); + PhyRegValue &= ~(BIT_6); + PhyRegValue |= (BIT_7); + rtl8168_mdio_write(tp, 0x13, PhyRegValue); + + rtl8168_mdio_write(tp, 0x1f, 0x0A44); + PhyRegValue = rtl8168_mdio_read(tp, 0x14); + PhyRegValue |= (BIT_2); + rtl8168_mdio_write(tp, 0x14, PhyRegValue); + + rtl8168_mdio_write(tp, 0x1f, 0x0A50); + PhyRegValue = rtl8168_mdio_read(tp, 0x11); + PhyRegValue |= (BIT_11|BIT_12); + rtl8168_mdio_write(tp, 0x11, PhyRegValue); + + retval = rtl8168_clear_phy_mcu_patch_request(tp); + + rtl8168_mdio_write(tp, 0x1f, 0x0A40); + rtl8168_mdio_write(tp, 0x10, 0x1040); + + rtl8168_mdio_write(tp, 0x1f, 0x0A4A); + PhyRegValue = rtl8168_mdio_read(tp, 0x13); + PhyRegValue &= ~(BIT_6|BIT_7); + rtl8168_mdio_write(tp, 0x13, PhyRegValue); + + rtl8168_mdio_write(tp, 0x1f, 0x0A44); + PhyRegValue = rtl8168_mdio_read(tp, 0x14); + PhyRegValue &= ~(BIT_2); + rtl8168_mdio_write(tp, 0x14, PhyRegValue); + + rtl8168_mdio_write(tp, 0x1f, 0x0A50); + PhyRegValue = rtl8168_mdio_read(tp, 0x11); + PhyRegValue &= ~(BIT_11|BIT_12); + rtl8168_mdio_write(tp, 0x11, PhyRegValue); + + rtl8168_mdio_write(tp, 0x1f, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8010); + PhyRegValue = rtl8168_mdio_read(tp, 0x14); + PhyRegValue |= (BIT_11); + rtl8168_mdio_write(tp, 0x14, PhyRegValue); + + retval = rtl8168_set_phy_mcu_patch_request(tp); + + rtl8168_mdio_write(tp, 0x1f, 0x0A20); + PhyRegValue = rtl8168_mdio_read(tp, 0x13); + if (PhyRegValue & BIT_11) { + if (PhyRegValue & BIT_10) { + retval = FALSE; + } + } + + retval = rtl8168_clear_phy_mcu_patch_request(tp); + + mdelay(2); + break; + default: + break; + } + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + return retval; +} + +static void +rtl8168_set_phy_ram_code_check_fail_flag(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u16 TmpUshort; + + switch(tp->mcfg) { + case CFG_METHOD_21: + TmpUshort = rtl8168_mac_ocp_read(tp, 0xD3C0); + TmpUshort |= BIT_0; + rtl8168_mac_ocp_write(tp, 0xD3C0, TmpUshort); + break; + } +} + +static void +rtl8168_set_phy_mcu_8168e_1(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned int gphy_val,i; + + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x00, 0x1800); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0023); + rtl8168_mdio_write(tp, 0x17, 0x0117); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x002C); + rtl8168_mdio_write(tp, 0x1B, 0x5000); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x16, 0x4104); + for (i = 0; i < 200; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x1E); + gphy_val &= 0x03FF; + if (gphy_val == 0x000C) + break; + } + rtl8168_mdio_write(tp, 0x1f, 0x0005); + for (i = 0; i < 200; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x07); + if ((gphy_val & BIT_5) == 0) + break; + } + gphy_val = rtl8168_mdio_read(tp, 0x07); + if (gphy_val & BIT_5) { + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x00a1); + rtl8168_mdio_write(tp, 0x17, 0x1000); + rtl8168_mdio_write(tp, 0x17, 0x0000); + rtl8168_mdio_write(tp, 0x17, 0x2000); + rtl8168_mdio_write(tp, 0x1e, 0x002f); + rtl8168_mdio_write(tp, 0x18, 0x9bfb); + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x07, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + } + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x0080); + gphy_val = rtl8168_mdio_read(tp, 0x00); + gphy_val &= ~(BIT_7); + rtl8168_mdio_write(tp, 0x00, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0002); + gphy_val = rtl8168_mdio_read(tp, 0x08); + gphy_val &= ~(BIT_7); + rtl8168_mdio_write(tp, 0x08, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0023); + rtl8168_mdio_write(tp, 0x16, 0x0306); + rtl8168_mdio_write(tp, 0x16, 0x0307); + rtl8168_mdio_write(tp, 0x15, 0x000e); + rtl8168_mdio_write(tp, 0x19, 0x000a); + rtl8168_mdio_write(tp, 0x15, 0x0010); + rtl8168_mdio_write(tp, 0x19, 0x0008); + rtl8168_mdio_write(tp, 0x15, 0x0018); + rtl8168_mdio_write(tp, 0x19, 0x4801); + rtl8168_mdio_write(tp, 0x15, 0x0019); + rtl8168_mdio_write(tp, 0x19, 0x6801); + rtl8168_mdio_write(tp, 0x15, 0x001a); + rtl8168_mdio_write(tp, 0x19, 0x66a1); + rtl8168_mdio_write(tp, 0x15, 0x001f); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0020); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0021); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0022); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0023); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0024); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0025); + rtl8168_mdio_write(tp, 0x19, 0x64a1); + rtl8168_mdio_write(tp, 0x15, 0x0026); + rtl8168_mdio_write(tp, 0x19, 0x40ea); + rtl8168_mdio_write(tp, 0x15, 0x0027); + rtl8168_mdio_write(tp, 0x19, 0x4503); + rtl8168_mdio_write(tp, 0x15, 0x0028); + rtl8168_mdio_write(tp, 0x19, 0x9f00); + rtl8168_mdio_write(tp, 0x15, 0x0029); + rtl8168_mdio_write(tp, 0x19, 0xa631); + rtl8168_mdio_write(tp, 0x15, 0x002a); + rtl8168_mdio_write(tp, 0x19, 0x9717); + rtl8168_mdio_write(tp, 0x15, 0x002b); + rtl8168_mdio_write(tp, 0x19, 0x302c); + rtl8168_mdio_write(tp, 0x15, 0x002c); + rtl8168_mdio_write(tp, 0x19, 0x4802); + rtl8168_mdio_write(tp, 0x15, 0x002d); + rtl8168_mdio_write(tp, 0x19, 0x58da); + rtl8168_mdio_write(tp, 0x15, 0x002e); + rtl8168_mdio_write(tp, 0x19, 0x400d); + rtl8168_mdio_write(tp, 0x15, 0x002f); + rtl8168_mdio_write(tp, 0x19, 0x4488); + rtl8168_mdio_write(tp, 0x15, 0x0030); + rtl8168_mdio_write(tp, 0x19, 0x9e00); + rtl8168_mdio_write(tp, 0x15, 0x0031); + rtl8168_mdio_write(tp, 0x19, 0x63c8); + rtl8168_mdio_write(tp, 0x15, 0x0032); + rtl8168_mdio_write(tp, 0x19, 0x6481); + rtl8168_mdio_write(tp, 0x15, 0x0033); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0034); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0035); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0036); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0037); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0038); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0039); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x003a); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x003b); + rtl8168_mdio_write(tp, 0x19, 0x63e8); + rtl8168_mdio_write(tp, 0x15, 0x003c); + rtl8168_mdio_write(tp, 0x19, 0x7d00); + rtl8168_mdio_write(tp, 0x15, 0x003d); + rtl8168_mdio_write(tp, 0x19, 0x59d4); + rtl8168_mdio_write(tp, 0x15, 0x003e); + rtl8168_mdio_write(tp, 0x19, 0x63f8); + rtl8168_mdio_write(tp, 0x15, 0x0040); + rtl8168_mdio_write(tp, 0x19, 0x64a1); + rtl8168_mdio_write(tp, 0x15, 0x0041); + rtl8168_mdio_write(tp, 0x19, 0x30de); + rtl8168_mdio_write(tp, 0x15, 0x0044); + rtl8168_mdio_write(tp, 0x19, 0x480f); + rtl8168_mdio_write(tp, 0x15, 0x0045); + rtl8168_mdio_write(tp, 0x19, 0x6800); + rtl8168_mdio_write(tp, 0x15, 0x0046); + rtl8168_mdio_write(tp, 0x19, 0x6680); + rtl8168_mdio_write(tp, 0x15, 0x0047); + rtl8168_mdio_write(tp, 0x19, 0x7c10); + rtl8168_mdio_write(tp, 0x15, 0x0048); + rtl8168_mdio_write(tp, 0x19, 0x63c8); + rtl8168_mdio_write(tp, 0x15, 0x0049); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x004a); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x004b); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x004c); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x004d); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x004e); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x004f); + rtl8168_mdio_write(tp, 0x19, 0x40ea); + rtl8168_mdio_write(tp, 0x15, 0x0050); + rtl8168_mdio_write(tp, 0x19, 0x4503); + rtl8168_mdio_write(tp, 0x15, 0x0051); + rtl8168_mdio_write(tp, 0x19, 0x58ca); + rtl8168_mdio_write(tp, 0x15, 0x0052); + rtl8168_mdio_write(tp, 0x19, 0x63c8); + rtl8168_mdio_write(tp, 0x15, 0x0053); + rtl8168_mdio_write(tp, 0x19, 0x63d8); + rtl8168_mdio_write(tp, 0x15, 0x0054); + rtl8168_mdio_write(tp, 0x19, 0x66a0); + rtl8168_mdio_write(tp, 0x15, 0x0055); + rtl8168_mdio_write(tp, 0x19, 0x9f00); + rtl8168_mdio_write(tp, 0x15, 0x0056); + rtl8168_mdio_write(tp, 0x19, 0x3000); + rtl8168_mdio_write(tp, 0x15, 0x006E); + rtl8168_mdio_write(tp, 0x19, 0x9afa); + rtl8168_mdio_write(tp, 0x15, 0x00a1); + rtl8168_mdio_write(tp, 0x19, 0x3044); + rtl8168_mdio_write(tp, 0x15, 0x00ab); + rtl8168_mdio_write(tp, 0x19, 0x5820); + rtl8168_mdio_write(tp, 0x15, 0x00ac); + rtl8168_mdio_write(tp, 0x19, 0x5e04); + rtl8168_mdio_write(tp, 0x15, 0x00ad); + rtl8168_mdio_write(tp, 0x19, 0xb60c); + rtl8168_mdio_write(tp, 0x15, 0x00af); + rtl8168_mdio_write(tp, 0x19, 0x000a); + rtl8168_mdio_write(tp, 0x15, 0x00b2); + rtl8168_mdio_write(tp, 0x19, 0x30b9); + rtl8168_mdio_write(tp, 0x15, 0x00b9); + rtl8168_mdio_write(tp, 0x19, 0x4408); + rtl8168_mdio_write(tp, 0x15, 0x00ba); + rtl8168_mdio_write(tp, 0x19, 0x480b); + rtl8168_mdio_write(tp, 0x15, 0x00bb); + rtl8168_mdio_write(tp, 0x19, 0x5e00); + rtl8168_mdio_write(tp, 0x15, 0x00bc); + rtl8168_mdio_write(tp, 0x19, 0x405f); + rtl8168_mdio_write(tp, 0x15, 0x00bd); + rtl8168_mdio_write(tp, 0x19, 0x4448); + rtl8168_mdio_write(tp, 0x15, 0x00be); + rtl8168_mdio_write(tp, 0x19, 0x4020); + rtl8168_mdio_write(tp, 0x15, 0x00bf); + rtl8168_mdio_write(tp, 0x19, 0x4468); + rtl8168_mdio_write(tp, 0x15, 0x00c0); + rtl8168_mdio_write(tp, 0x19, 0x9c02); + rtl8168_mdio_write(tp, 0x15, 0x00c1); + rtl8168_mdio_write(tp, 0x19, 0x58a0); + rtl8168_mdio_write(tp, 0x15, 0x00c2); + rtl8168_mdio_write(tp, 0x19, 0xb605); + rtl8168_mdio_write(tp, 0x15, 0x00c3); + rtl8168_mdio_write(tp, 0x19, 0xc0d3); + rtl8168_mdio_write(tp, 0x15, 0x00c4); + rtl8168_mdio_write(tp, 0x19, 0x00e6); + rtl8168_mdio_write(tp, 0x15, 0x00c5); + rtl8168_mdio_write(tp, 0x19, 0xdaec); + rtl8168_mdio_write(tp, 0x15, 0x00c6); + rtl8168_mdio_write(tp, 0x19, 0x00fa); + rtl8168_mdio_write(tp, 0x15, 0x00c7); + rtl8168_mdio_write(tp, 0x19, 0x9df9); + rtl8168_mdio_write(tp, 0x15, 0x00c8); + rtl8168_mdio_write(tp, 0x19, 0x307a); + rtl8168_mdio_write(tp, 0x15, 0x0112); + rtl8168_mdio_write(tp, 0x19, 0x6421); + rtl8168_mdio_write(tp, 0x15, 0x0113); + rtl8168_mdio_write(tp, 0x19, 0x7c08); + rtl8168_mdio_write(tp, 0x15, 0x0114); + rtl8168_mdio_write(tp, 0x19, 0x63f0); + rtl8168_mdio_write(tp, 0x15, 0x0115); + rtl8168_mdio_write(tp, 0x19, 0x4003); + rtl8168_mdio_write(tp, 0x15, 0x0116); + rtl8168_mdio_write(tp, 0x19, 0x4418); + rtl8168_mdio_write(tp, 0x15, 0x0117); + rtl8168_mdio_write(tp, 0x19, 0x9b00); + rtl8168_mdio_write(tp, 0x15, 0x0118); + rtl8168_mdio_write(tp, 0x19, 0x6461); + rtl8168_mdio_write(tp, 0x15, 0x0119); + rtl8168_mdio_write(tp, 0x19, 0x64e1); + rtl8168_mdio_write(tp, 0x15, 0x011a); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0150); + rtl8168_mdio_write(tp, 0x19, 0x7c80); + rtl8168_mdio_write(tp, 0x15, 0x0151); + rtl8168_mdio_write(tp, 0x19, 0x6461); + rtl8168_mdio_write(tp, 0x15, 0x0152); + rtl8168_mdio_write(tp, 0x19, 0x4003); + rtl8168_mdio_write(tp, 0x15, 0x0153); + rtl8168_mdio_write(tp, 0x19, 0x4540); + rtl8168_mdio_write(tp, 0x15, 0x0154); + rtl8168_mdio_write(tp, 0x19, 0x9f00); + rtl8168_mdio_write(tp, 0x15, 0x0155); + rtl8168_mdio_write(tp, 0x19, 0x9d00); + rtl8168_mdio_write(tp, 0x15, 0x0156); + rtl8168_mdio_write(tp, 0x19, 0x7c40); + rtl8168_mdio_write(tp, 0x15, 0x0157); + rtl8168_mdio_write(tp, 0x19, 0x6421); + rtl8168_mdio_write(tp, 0x15, 0x0158); + rtl8168_mdio_write(tp, 0x19, 0x7c80); + rtl8168_mdio_write(tp, 0x15, 0x0159); + rtl8168_mdio_write(tp, 0x19, 0x64a1); + rtl8168_mdio_write(tp, 0x15, 0x015a); + rtl8168_mdio_write(tp, 0x19, 0x30fe); + rtl8168_mdio_write(tp, 0x15, 0x021e); + rtl8168_mdio_write(tp, 0x19, 0x5410); + rtl8168_mdio_write(tp, 0x15, 0x0225); + rtl8168_mdio_write(tp, 0x19, 0x5400); + rtl8168_mdio_write(tp, 0x15, 0x023D); + rtl8168_mdio_write(tp, 0x19, 0x4050); + rtl8168_mdio_write(tp, 0x15, 0x0295); + rtl8168_mdio_write(tp, 0x19, 0x6c08); + rtl8168_mdio_write(tp, 0x15, 0x02bd); + rtl8168_mdio_write(tp, 0x19, 0xa523); + rtl8168_mdio_write(tp, 0x15, 0x02be); + rtl8168_mdio_write(tp, 0x19, 0x32ca); + rtl8168_mdio_write(tp, 0x15, 0x02ca); + rtl8168_mdio_write(tp, 0x19, 0x48b3); + rtl8168_mdio_write(tp, 0x15, 0x02cb); + rtl8168_mdio_write(tp, 0x19, 0x4020); + rtl8168_mdio_write(tp, 0x15, 0x02cc); + rtl8168_mdio_write(tp, 0x19, 0x4823); + rtl8168_mdio_write(tp, 0x15, 0x02cd); + rtl8168_mdio_write(tp, 0x19, 0x4510); + rtl8168_mdio_write(tp, 0x15, 0x02ce); + rtl8168_mdio_write(tp, 0x19, 0xb63a); + rtl8168_mdio_write(tp, 0x15, 0x02cf); + rtl8168_mdio_write(tp, 0x19, 0x7dc8); + rtl8168_mdio_write(tp, 0x15, 0x02d6); + rtl8168_mdio_write(tp, 0x19, 0x9bf8); + rtl8168_mdio_write(tp, 0x15, 0x02d8); + rtl8168_mdio_write(tp, 0x19, 0x85f6); + rtl8168_mdio_write(tp, 0x15, 0x02d9); + rtl8168_mdio_write(tp, 0x19, 0x32e0); + rtl8168_mdio_write(tp, 0x15, 0x02e0); + rtl8168_mdio_write(tp, 0x19, 0x4834); + rtl8168_mdio_write(tp, 0x15, 0x02e1); + rtl8168_mdio_write(tp, 0x19, 0x6c08); + rtl8168_mdio_write(tp, 0x15, 0x02e2); + rtl8168_mdio_write(tp, 0x19, 0x4020); + rtl8168_mdio_write(tp, 0x15, 0x02e3); + rtl8168_mdio_write(tp, 0x19, 0x4824); + rtl8168_mdio_write(tp, 0x15, 0x02e4); + rtl8168_mdio_write(tp, 0x19, 0x4520); + rtl8168_mdio_write(tp, 0x15, 0x02e5); + rtl8168_mdio_write(tp, 0x19, 0x4008); + rtl8168_mdio_write(tp, 0x15, 0x02e6); + rtl8168_mdio_write(tp, 0x19, 0x4560); + rtl8168_mdio_write(tp, 0x15, 0x02e7); + rtl8168_mdio_write(tp, 0x19, 0x9d04); + rtl8168_mdio_write(tp, 0x15, 0x02e8); + rtl8168_mdio_write(tp, 0x19, 0x48c4); + rtl8168_mdio_write(tp, 0x15, 0x02e9); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02ea); + rtl8168_mdio_write(tp, 0x19, 0x4844); + rtl8168_mdio_write(tp, 0x15, 0x02eb); + rtl8168_mdio_write(tp, 0x19, 0x7dc8); + rtl8168_mdio_write(tp, 0x15, 0x02f0); + rtl8168_mdio_write(tp, 0x19, 0x9cf7); + rtl8168_mdio_write(tp, 0x15, 0x02f1); + rtl8168_mdio_write(tp, 0x19, 0xdf94); + rtl8168_mdio_write(tp, 0x15, 0x02f2); + rtl8168_mdio_write(tp, 0x19, 0x0002); + rtl8168_mdio_write(tp, 0x15, 0x02f3); + rtl8168_mdio_write(tp, 0x19, 0x6810); + rtl8168_mdio_write(tp, 0x15, 0x02f4); + rtl8168_mdio_write(tp, 0x19, 0xb614); + rtl8168_mdio_write(tp, 0x15, 0x02f5); + rtl8168_mdio_write(tp, 0x19, 0xc42b); + rtl8168_mdio_write(tp, 0x15, 0x02f6); + rtl8168_mdio_write(tp, 0x19, 0x00d4); + rtl8168_mdio_write(tp, 0x15, 0x02f7); + rtl8168_mdio_write(tp, 0x19, 0xc455); + rtl8168_mdio_write(tp, 0x15, 0x02f8); + rtl8168_mdio_write(tp, 0x19, 0x0093); + rtl8168_mdio_write(tp, 0x15, 0x02f9); + rtl8168_mdio_write(tp, 0x19, 0x92ee); + rtl8168_mdio_write(tp, 0x15, 0x02fa); + rtl8168_mdio_write(tp, 0x19, 0xefed); + rtl8168_mdio_write(tp, 0x15, 0x02fb); + rtl8168_mdio_write(tp, 0x19, 0x3312); + rtl8168_mdio_write(tp, 0x15, 0x0312); + rtl8168_mdio_write(tp, 0x19, 0x49b5); + rtl8168_mdio_write(tp, 0x15, 0x0313); + rtl8168_mdio_write(tp, 0x19, 0x7d00); + rtl8168_mdio_write(tp, 0x15, 0x0314); + rtl8168_mdio_write(tp, 0x19, 0x4d00); + rtl8168_mdio_write(tp, 0x15, 0x0315); + rtl8168_mdio_write(tp, 0x19, 0x6810); + rtl8168_mdio_write(tp, 0x15, 0x031e); + rtl8168_mdio_write(tp, 0x19, 0x404f); + rtl8168_mdio_write(tp, 0x15, 0x031f); + rtl8168_mdio_write(tp, 0x19, 0x44c8); + rtl8168_mdio_write(tp, 0x15, 0x0320); + rtl8168_mdio_write(tp, 0x19, 0xd64f); + rtl8168_mdio_write(tp, 0x15, 0x0321); + rtl8168_mdio_write(tp, 0x19, 0x00e7); + rtl8168_mdio_write(tp, 0x15, 0x0322); + rtl8168_mdio_write(tp, 0x19, 0x7c08); + rtl8168_mdio_write(tp, 0x15, 0x0323); + rtl8168_mdio_write(tp, 0x19, 0x8203); + rtl8168_mdio_write(tp, 0x15, 0x0324); + rtl8168_mdio_write(tp, 0x19, 0x4d48); + rtl8168_mdio_write(tp, 0x15, 0x0325); + rtl8168_mdio_write(tp, 0x19, 0x3327); + rtl8168_mdio_write(tp, 0x15, 0x0326); + rtl8168_mdio_write(tp, 0x19, 0x4d40); + rtl8168_mdio_write(tp, 0x15, 0x0327); + rtl8168_mdio_write(tp, 0x19, 0xc8d7); + rtl8168_mdio_write(tp, 0x15, 0x0328); + rtl8168_mdio_write(tp, 0x19, 0x0003); + rtl8168_mdio_write(tp, 0x15, 0x0329); + rtl8168_mdio_write(tp, 0x19, 0x7c20); + rtl8168_mdio_write(tp, 0x15, 0x032a); + rtl8168_mdio_write(tp, 0x19, 0x4c20); + rtl8168_mdio_write(tp, 0x15, 0x032b); + rtl8168_mdio_write(tp, 0x19, 0xc8ed); + rtl8168_mdio_write(tp, 0x15, 0x032c); + rtl8168_mdio_write(tp, 0x19, 0x00f4); + rtl8168_mdio_write(tp, 0x15, 0x032d); + rtl8168_mdio_write(tp, 0x19, 0x82b3); + rtl8168_mdio_write(tp, 0x15, 0x032e); + rtl8168_mdio_write(tp, 0x19, 0xd11d); + rtl8168_mdio_write(tp, 0x15, 0x032f); + rtl8168_mdio_write(tp, 0x19, 0x00b1); + rtl8168_mdio_write(tp, 0x15, 0x0330); + rtl8168_mdio_write(tp, 0x19, 0xde18); + rtl8168_mdio_write(tp, 0x15, 0x0331); + rtl8168_mdio_write(tp, 0x19, 0x0008); + rtl8168_mdio_write(tp, 0x15, 0x0332); + rtl8168_mdio_write(tp, 0x19, 0x91ee); + rtl8168_mdio_write(tp, 0x15, 0x0333); + rtl8168_mdio_write(tp, 0x19, 0x3339); + rtl8168_mdio_write(tp, 0x15, 0x033a); + rtl8168_mdio_write(tp, 0x19, 0x4064); + rtl8168_mdio_write(tp, 0x15, 0x0340); + rtl8168_mdio_write(tp, 0x19, 0x9e06); + rtl8168_mdio_write(tp, 0x15, 0x0341); + rtl8168_mdio_write(tp, 0x19, 0x7c08); + rtl8168_mdio_write(tp, 0x15, 0x0342); + rtl8168_mdio_write(tp, 0x19, 0x8203); + rtl8168_mdio_write(tp, 0x15, 0x0343); + rtl8168_mdio_write(tp, 0x19, 0x4d48); + rtl8168_mdio_write(tp, 0x15, 0x0344); + rtl8168_mdio_write(tp, 0x19, 0x3346); + rtl8168_mdio_write(tp, 0x15, 0x0345); + rtl8168_mdio_write(tp, 0x19, 0x4d40); + rtl8168_mdio_write(tp, 0x15, 0x0346); + rtl8168_mdio_write(tp, 0x19, 0xd11d); + rtl8168_mdio_write(tp, 0x15, 0x0347); + rtl8168_mdio_write(tp, 0x19, 0x0099); + rtl8168_mdio_write(tp, 0x15, 0x0348); + rtl8168_mdio_write(tp, 0x19, 0xbb17); + rtl8168_mdio_write(tp, 0x15, 0x0349); + rtl8168_mdio_write(tp, 0x19, 0x8102); + rtl8168_mdio_write(tp, 0x15, 0x034a); + rtl8168_mdio_write(tp, 0x19, 0x334d); + rtl8168_mdio_write(tp, 0x15, 0x034b); + rtl8168_mdio_write(tp, 0x19, 0xa22c); + rtl8168_mdio_write(tp, 0x15, 0x034c); + rtl8168_mdio_write(tp, 0x19, 0x3397); + rtl8168_mdio_write(tp, 0x15, 0x034d); + rtl8168_mdio_write(tp, 0x19, 0x91f2); + rtl8168_mdio_write(tp, 0x15, 0x034e); + rtl8168_mdio_write(tp, 0x19, 0xc218); + rtl8168_mdio_write(tp, 0x15, 0x034f); + rtl8168_mdio_write(tp, 0x19, 0x00f0); + rtl8168_mdio_write(tp, 0x15, 0x0350); + rtl8168_mdio_write(tp, 0x19, 0x3397); + rtl8168_mdio_write(tp, 0x15, 0x0351); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0364); + rtl8168_mdio_write(tp, 0x19, 0xbc05); + rtl8168_mdio_write(tp, 0x15, 0x0367); + rtl8168_mdio_write(tp, 0x19, 0xa1fc); + rtl8168_mdio_write(tp, 0x15, 0x0368); + rtl8168_mdio_write(tp, 0x19, 0x3377); + rtl8168_mdio_write(tp, 0x15, 0x0369); + rtl8168_mdio_write(tp, 0x19, 0x328b); + rtl8168_mdio_write(tp, 0x15, 0x036a); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0377); + rtl8168_mdio_write(tp, 0x19, 0x4b97); + rtl8168_mdio_write(tp, 0x15, 0x0378); + rtl8168_mdio_write(tp, 0x19, 0x6818); + rtl8168_mdio_write(tp, 0x15, 0x0379); + rtl8168_mdio_write(tp, 0x19, 0x4b07); + rtl8168_mdio_write(tp, 0x15, 0x037a); + rtl8168_mdio_write(tp, 0x19, 0x40ac); + rtl8168_mdio_write(tp, 0x15, 0x037b); + rtl8168_mdio_write(tp, 0x19, 0x4445); + rtl8168_mdio_write(tp, 0x15, 0x037c); + rtl8168_mdio_write(tp, 0x19, 0x404e); + rtl8168_mdio_write(tp, 0x15, 0x037d); + rtl8168_mdio_write(tp, 0x19, 0x4461); + rtl8168_mdio_write(tp, 0x15, 0x037e); + rtl8168_mdio_write(tp, 0x19, 0x9c09); + rtl8168_mdio_write(tp, 0x15, 0x037f); + rtl8168_mdio_write(tp, 0x19, 0x63da); + rtl8168_mdio_write(tp, 0x15, 0x0380); + rtl8168_mdio_write(tp, 0x19, 0x5440); + rtl8168_mdio_write(tp, 0x15, 0x0381); + rtl8168_mdio_write(tp, 0x19, 0x4b98); + rtl8168_mdio_write(tp, 0x15, 0x0382); + rtl8168_mdio_write(tp, 0x19, 0x7c60); + rtl8168_mdio_write(tp, 0x15, 0x0383); + rtl8168_mdio_write(tp, 0x19, 0x4c00); + rtl8168_mdio_write(tp, 0x15, 0x0384); + rtl8168_mdio_write(tp, 0x19, 0x4b08); + rtl8168_mdio_write(tp, 0x15, 0x0385); + rtl8168_mdio_write(tp, 0x19, 0x63d8); + rtl8168_mdio_write(tp, 0x15, 0x0386); + rtl8168_mdio_write(tp, 0x19, 0x338d); + rtl8168_mdio_write(tp, 0x15, 0x0387); + rtl8168_mdio_write(tp, 0x19, 0xd64f); + rtl8168_mdio_write(tp, 0x15, 0x0388); + rtl8168_mdio_write(tp, 0x19, 0x0080); + rtl8168_mdio_write(tp, 0x15, 0x0389); + rtl8168_mdio_write(tp, 0x19, 0x820c); + rtl8168_mdio_write(tp, 0x15, 0x038a); + rtl8168_mdio_write(tp, 0x19, 0xa10b); + rtl8168_mdio_write(tp, 0x15, 0x038b); + rtl8168_mdio_write(tp, 0x19, 0x9df3); + rtl8168_mdio_write(tp, 0x15, 0x038c); + rtl8168_mdio_write(tp, 0x19, 0x3395); + rtl8168_mdio_write(tp, 0x15, 0x038d); + rtl8168_mdio_write(tp, 0x19, 0xd64f); + rtl8168_mdio_write(tp, 0x15, 0x038e); + rtl8168_mdio_write(tp, 0x19, 0x00f9); + rtl8168_mdio_write(tp, 0x15, 0x038f); + rtl8168_mdio_write(tp, 0x19, 0xc017); + rtl8168_mdio_write(tp, 0x15, 0x0390); + rtl8168_mdio_write(tp, 0x19, 0x0005); + rtl8168_mdio_write(tp, 0x15, 0x0391); + rtl8168_mdio_write(tp, 0x19, 0x6c0b); + rtl8168_mdio_write(tp, 0x15, 0x0392); + rtl8168_mdio_write(tp, 0x19, 0xa103); + rtl8168_mdio_write(tp, 0x15, 0x0393); + rtl8168_mdio_write(tp, 0x19, 0x6c08); + rtl8168_mdio_write(tp, 0x15, 0x0394); + rtl8168_mdio_write(tp, 0x19, 0x9df9); + rtl8168_mdio_write(tp, 0x15, 0x0395); + rtl8168_mdio_write(tp, 0x19, 0x6c08); + rtl8168_mdio_write(tp, 0x15, 0x0396); + rtl8168_mdio_write(tp, 0x19, 0x3397); + rtl8168_mdio_write(tp, 0x15, 0x0399); + rtl8168_mdio_write(tp, 0x19, 0x6810); + rtl8168_mdio_write(tp, 0x15, 0x03a4); + rtl8168_mdio_write(tp, 0x19, 0x7c08); + rtl8168_mdio_write(tp, 0x15, 0x03a5); + rtl8168_mdio_write(tp, 0x19, 0x8203); + rtl8168_mdio_write(tp, 0x15, 0x03a6); + rtl8168_mdio_write(tp, 0x19, 0x4d08); + rtl8168_mdio_write(tp, 0x15, 0x03a7); + rtl8168_mdio_write(tp, 0x19, 0x33a9); + rtl8168_mdio_write(tp, 0x15, 0x03a8); + rtl8168_mdio_write(tp, 0x19, 0x4d00); + rtl8168_mdio_write(tp, 0x15, 0x03a9); + rtl8168_mdio_write(tp, 0x19, 0x9bfa); + rtl8168_mdio_write(tp, 0x15, 0x03aa); + rtl8168_mdio_write(tp, 0x19, 0x33b6); + rtl8168_mdio_write(tp, 0x15, 0x03bb); + rtl8168_mdio_write(tp, 0x19, 0x4056); + rtl8168_mdio_write(tp, 0x15, 0x03bc); + rtl8168_mdio_write(tp, 0x19, 0x44e9); + rtl8168_mdio_write(tp, 0x15, 0x03bd); + rtl8168_mdio_write(tp, 0x19, 0x405e); + rtl8168_mdio_write(tp, 0x15, 0x03be); + rtl8168_mdio_write(tp, 0x19, 0x44f8); + rtl8168_mdio_write(tp, 0x15, 0x03bf); + rtl8168_mdio_write(tp, 0x19, 0xd64f); + rtl8168_mdio_write(tp, 0x15, 0x03c0); + rtl8168_mdio_write(tp, 0x19, 0x0037); + rtl8168_mdio_write(tp, 0x15, 0x03c1); + rtl8168_mdio_write(tp, 0x19, 0xbd37); + rtl8168_mdio_write(tp, 0x15, 0x03c2); + rtl8168_mdio_write(tp, 0x19, 0x9cfd); + rtl8168_mdio_write(tp, 0x15, 0x03c3); + rtl8168_mdio_write(tp, 0x19, 0xc639); + rtl8168_mdio_write(tp, 0x15, 0x03c4); + rtl8168_mdio_write(tp, 0x19, 0x0011); + rtl8168_mdio_write(tp, 0x15, 0x03c5); + rtl8168_mdio_write(tp, 0x19, 0x9b03); + rtl8168_mdio_write(tp, 0x15, 0x03c6); + rtl8168_mdio_write(tp, 0x19, 0x7c01); + rtl8168_mdio_write(tp, 0x15, 0x03c7); + rtl8168_mdio_write(tp, 0x19, 0x4c01); + rtl8168_mdio_write(tp, 0x15, 0x03c8); + rtl8168_mdio_write(tp, 0x19, 0x9e03); + rtl8168_mdio_write(tp, 0x15, 0x03c9); + rtl8168_mdio_write(tp, 0x19, 0x7c20); + rtl8168_mdio_write(tp, 0x15, 0x03ca); + rtl8168_mdio_write(tp, 0x19, 0x4c20); + rtl8168_mdio_write(tp, 0x15, 0x03cb); + rtl8168_mdio_write(tp, 0x19, 0x9af4); + rtl8168_mdio_write(tp, 0x15, 0x03cc); + rtl8168_mdio_write(tp, 0x19, 0x7c12); + rtl8168_mdio_write(tp, 0x15, 0x03cd); + rtl8168_mdio_write(tp, 0x19, 0x4c52); + rtl8168_mdio_write(tp, 0x15, 0x03ce); + rtl8168_mdio_write(tp, 0x19, 0x4470); + rtl8168_mdio_write(tp, 0x15, 0x03cf); + rtl8168_mdio_write(tp, 0x19, 0x7c12); + rtl8168_mdio_write(tp, 0x15, 0x03d0); + rtl8168_mdio_write(tp, 0x19, 0x4c40); + rtl8168_mdio_write(tp, 0x15, 0x03d1); + rtl8168_mdio_write(tp, 0x19, 0x33bf); + rtl8168_mdio_write(tp, 0x15, 0x03d6); + rtl8168_mdio_write(tp, 0x19, 0x4047); + rtl8168_mdio_write(tp, 0x15, 0x03d7); + rtl8168_mdio_write(tp, 0x19, 0x4469); + rtl8168_mdio_write(tp, 0x15, 0x03d8); + rtl8168_mdio_write(tp, 0x19, 0x492b); + rtl8168_mdio_write(tp, 0x15, 0x03d9); + rtl8168_mdio_write(tp, 0x19, 0x4479); + rtl8168_mdio_write(tp, 0x15, 0x03da); + rtl8168_mdio_write(tp, 0x19, 0x7c09); + rtl8168_mdio_write(tp, 0x15, 0x03db); + rtl8168_mdio_write(tp, 0x19, 0x8203); + rtl8168_mdio_write(tp, 0x15, 0x03dc); + rtl8168_mdio_write(tp, 0x19, 0x4d48); + rtl8168_mdio_write(tp, 0x15, 0x03dd); + rtl8168_mdio_write(tp, 0x19, 0x33df); + rtl8168_mdio_write(tp, 0x15, 0x03de); + rtl8168_mdio_write(tp, 0x19, 0x4d40); + rtl8168_mdio_write(tp, 0x15, 0x03df); + rtl8168_mdio_write(tp, 0x19, 0xd64f); + rtl8168_mdio_write(tp, 0x15, 0x03e0); + rtl8168_mdio_write(tp, 0x19, 0x0017); + rtl8168_mdio_write(tp, 0x15, 0x03e1); + rtl8168_mdio_write(tp, 0x19, 0xbd17); + rtl8168_mdio_write(tp, 0x15, 0x03e2); + rtl8168_mdio_write(tp, 0x19, 0x9b03); + rtl8168_mdio_write(tp, 0x15, 0x03e3); + rtl8168_mdio_write(tp, 0x19, 0x7c20); + rtl8168_mdio_write(tp, 0x15, 0x03e4); + rtl8168_mdio_write(tp, 0x19, 0x4c20); + rtl8168_mdio_write(tp, 0x15, 0x03e5); + rtl8168_mdio_write(tp, 0x19, 0x88f5); + rtl8168_mdio_write(tp, 0x15, 0x03e6); + rtl8168_mdio_write(tp, 0x19, 0xc428); + rtl8168_mdio_write(tp, 0x15, 0x03e7); + rtl8168_mdio_write(tp, 0x19, 0x0008); + rtl8168_mdio_write(tp, 0x15, 0x03e8); + rtl8168_mdio_write(tp, 0x19, 0x9af2); + rtl8168_mdio_write(tp, 0x15, 0x03e9); + rtl8168_mdio_write(tp, 0x19, 0x7c12); + rtl8168_mdio_write(tp, 0x15, 0x03ea); + rtl8168_mdio_write(tp, 0x19, 0x4c52); + rtl8168_mdio_write(tp, 0x15, 0x03eb); + rtl8168_mdio_write(tp, 0x19, 0x4470); + rtl8168_mdio_write(tp, 0x15, 0x03ec); + rtl8168_mdio_write(tp, 0x19, 0x7c12); + rtl8168_mdio_write(tp, 0x15, 0x03ed); + rtl8168_mdio_write(tp, 0x19, 0x4c40); + rtl8168_mdio_write(tp, 0x15, 0x03ee); + rtl8168_mdio_write(tp, 0x19, 0x33da); + rtl8168_mdio_write(tp, 0x15, 0x03ef); + rtl8168_mdio_write(tp, 0x19, 0x3312); + rtl8168_mdio_write(tp, 0x16, 0x0306); + rtl8168_mdio_write(tp, 0x16, 0x0300); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x17, 0x2179); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0040); + rtl8168_mdio_write(tp, 0x18, 0x0645); + rtl8168_mdio_write(tp, 0x19, 0xe200); + rtl8168_mdio_write(tp, 0x18, 0x0655); + rtl8168_mdio_write(tp, 0x19, 0x9000); + rtl8168_mdio_write(tp, 0x18, 0x0d05); + rtl8168_mdio_write(tp, 0x19, 0xbe00); + rtl8168_mdio_write(tp, 0x18, 0x0d15); + rtl8168_mdio_write(tp, 0x19, 0xd300); + rtl8168_mdio_write(tp, 0x18, 0x0d25); + rtl8168_mdio_write(tp, 0x19, 0xfe00); + rtl8168_mdio_write(tp, 0x18, 0x0d35); + rtl8168_mdio_write(tp, 0x19, 0x4000); + rtl8168_mdio_write(tp, 0x18, 0x0d45); + rtl8168_mdio_write(tp, 0x19, 0x7f00); + rtl8168_mdio_write(tp, 0x18, 0x0d55); + rtl8168_mdio_write(tp, 0x19, 0x1000); + rtl8168_mdio_write(tp, 0x18, 0x0d65); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x18, 0x0d75); + rtl8168_mdio_write(tp, 0x19, 0x8200); + rtl8168_mdio_write(tp, 0x18, 0x0d85); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x18, 0x0d95); + rtl8168_mdio_write(tp, 0x19, 0x7000); + rtl8168_mdio_write(tp, 0x18, 0x0da5); + rtl8168_mdio_write(tp, 0x19, 0x0f00); + rtl8168_mdio_write(tp, 0x18, 0x0db5); + rtl8168_mdio_write(tp, 0x19, 0x0100); + rtl8168_mdio_write(tp, 0x18, 0x0dc5); + rtl8168_mdio_write(tp, 0x19, 0x9b00); + rtl8168_mdio_write(tp, 0x18, 0x0dd5); + rtl8168_mdio_write(tp, 0x19, 0x7f00); + rtl8168_mdio_write(tp, 0x18, 0x0de5); + rtl8168_mdio_write(tp, 0x19, 0xe000); + rtl8168_mdio_write(tp, 0x18, 0x0df5); + rtl8168_mdio_write(tp, 0x19, 0xef00); + rtl8168_mdio_write(tp, 0x18, 0x16d5); + rtl8168_mdio_write(tp, 0x19, 0xe200); + rtl8168_mdio_write(tp, 0x18, 0x16e5); + rtl8168_mdio_write(tp, 0x19, 0xab00); + rtl8168_mdio_write(tp, 0x18, 0x2904); + rtl8168_mdio_write(tp, 0x19, 0x4000); + rtl8168_mdio_write(tp, 0x18, 0x2914); + rtl8168_mdio_write(tp, 0x19, 0x7f00); + rtl8168_mdio_write(tp, 0x18, 0x2924); + rtl8168_mdio_write(tp, 0x19, 0x0100); + rtl8168_mdio_write(tp, 0x18, 0x2934); + rtl8168_mdio_write(tp, 0x19, 0x2000); + rtl8168_mdio_write(tp, 0x18, 0x2944); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x18, 0x2954); + rtl8168_mdio_write(tp, 0x19, 0x4600); + rtl8168_mdio_write(tp, 0x18, 0x2964); + rtl8168_mdio_write(tp, 0x19, 0xfc00); + rtl8168_mdio_write(tp, 0x18, 0x2974); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x18, 0x2984); + rtl8168_mdio_write(tp, 0x19, 0x5000); + rtl8168_mdio_write(tp, 0x18, 0x2994); + rtl8168_mdio_write(tp, 0x19, 0x9d00); + rtl8168_mdio_write(tp, 0x18, 0x29a4); + rtl8168_mdio_write(tp, 0x19, 0xff00); + rtl8168_mdio_write(tp, 0x18, 0x29b4); + rtl8168_mdio_write(tp, 0x19, 0x4000); + rtl8168_mdio_write(tp, 0x18, 0x29c4); + rtl8168_mdio_write(tp, 0x19, 0x7f00); + rtl8168_mdio_write(tp, 0x18, 0x29d4); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x18, 0x29e4); + rtl8168_mdio_write(tp, 0x19, 0x2000); + rtl8168_mdio_write(tp, 0x18, 0x29f4); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x18, 0x2a04); + rtl8168_mdio_write(tp, 0x19, 0xe600); + rtl8168_mdio_write(tp, 0x18, 0x2a14); + rtl8168_mdio_write(tp, 0x19, 0xff00); + rtl8168_mdio_write(tp, 0x18, 0x2a24); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x18, 0x2a34); + rtl8168_mdio_write(tp, 0x19, 0x5000); + rtl8168_mdio_write(tp, 0x18, 0x2a44); + rtl8168_mdio_write(tp, 0x19, 0x8500); + rtl8168_mdio_write(tp, 0x18, 0x2a54); + rtl8168_mdio_write(tp, 0x19, 0x7f00); + rtl8168_mdio_write(tp, 0x18, 0x2a64); + rtl8168_mdio_write(tp, 0x19, 0xac00); + rtl8168_mdio_write(tp, 0x18, 0x2a74); + rtl8168_mdio_write(tp, 0x19, 0x0800); + rtl8168_mdio_write(tp, 0x18, 0x2a84); + rtl8168_mdio_write(tp, 0x19, 0xfc00); + rtl8168_mdio_write(tp, 0x18, 0x2a94); + rtl8168_mdio_write(tp, 0x19, 0xe000); + rtl8168_mdio_write(tp, 0x18, 0x2aa4); + rtl8168_mdio_write(tp, 0x19, 0x7400); + rtl8168_mdio_write(tp, 0x18, 0x2ab4); + rtl8168_mdio_write(tp, 0x19, 0x4000); + rtl8168_mdio_write(tp, 0x18, 0x2ac4); + rtl8168_mdio_write(tp, 0x19, 0x7f00); + rtl8168_mdio_write(tp, 0x18, 0x2ad4); + rtl8168_mdio_write(tp, 0x19, 0x0100); + rtl8168_mdio_write(tp, 0x18, 0x2ae4); + rtl8168_mdio_write(tp, 0x19, 0xff00); + rtl8168_mdio_write(tp, 0x18, 0x2af4); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x18, 0x2b04); + rtl8168_mdio_write(tp, 0x19, 0x4400); + rtl8168_mdio_write(tp, 0x18, 0x2b14); + rtl8168_mdio_write(tp, 0x19, 0xfc00); + rtl8168_mdio_write(tp, 0x18, 0x2b24); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x18, 0x2b34); + rtl8168_mdio_write(tp, 0x19, 0x4000); + rtl8168_mdio_write(tp, 0x18, 0x2b44); + rtl8168_mdio_write(tp, 0x19, 0x9d00); + rtl8168_mdio_write(tp, 0x18, 0x2b54); + rtl8168_mdio_write(tp, 0x19, 0xff00); + rtl8168_mdio_write(tp, 0x18, 0x2b64); + rtl8168_mdio_write(tp, 0x19, 0x4000); + rtl8168_mdio_write(tp, 0x18, 0x2b74); + rtl8168_mdio_write(tp, 0x19, 0x7f00); + rtl8168_mdio_write(tp, 0x18, 0x2b84); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x18, 0x2b94); + rtl8168_mdio_write(tp, 0x19, 0xff00); + rtl8168_mdio_write(tp, 0x18, 0x2ba4); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x18, 0x2bb4); + rtl8168_mdio_write(tp, 0x19, 0xfc00); + rtl8168_mdio_write(tp, 0x18, 0x2bc4); + rtl8168_mdio_write(tp, 0x19, 0xff00); + rtl8168_mdio_write(tp, 0x18, 0x2bd4); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x18, 0x2be4); + rtl8168_mdio_write(tp, 0x19, 0x4000); + rtl8168_mdio_write(tp, 0x18, 0x2bf4); + rtl8168_mdio_write(tp, 0x19, 0x8900); + rtl8168_mdio_write(tp, 0x18, 0x2c04); + rtl8168_mdio_write(tp, 0x19, 0x8300); + rtl8168_mdio_write(tp, 0x18, 0x2c14); + rtl8168_mdio_write(tp, 0x19, 0xe000); + rtl8168_mdio_write(tp, 0x18, 0x2c24); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x18, 0x2c34); + rtl8168_mdio_write(tp, 0x19, 0xac00); + rtl8168_mdio_write(tp, 0x18, 0x2c44); + rtl8168_mdio_write(tp, 0x19, 0x0800); + rtl8168_mdio_write(tp, 0x18, 0x2c54); + rtl8168_mdio_write(tp, 0x19, 0xfa00); + rtl8168_mdio_write(tp, 0x18, 0x2c64); + rtl8168_mdio_write(tp, 0x19, 0xe100); + rtl8168_mdio_write(tp, 0x18, 0x2c74); + rtl8168_mdio_write(tp, 0x19, 0x7f00); + rtl8168_mdio_write(tp, 0x18, 0x0001); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x17, 0x2100); + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x0080); + rtl8168_mdio_write(tp, 0x05, 0x8b88); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x05, 0x8000); + rtl8168_mdio_write(tp, 0x06, 0xd480); + rtl8168_mdio_write(tp, 0x06, 0xc1e4); + rtl8168_mdio_write(tp, 0x06, 0x8b9a); + rtl8168_mdio_write(tp, 0x06, 0xe58b); + rtl8168_mdio_write(tp, 0x06, 0x9bee); + rtl8168_mdio_write(tp, 0x06, 0x8b83); + rtl8168_mdio_write(tp, 0x06, 0x41bf); + rtl8168_mdio_write(tp, 0x06, 0x8b88); + rtl8168_mdio_write(tp, 0x06, 0xec00); + rtl8168_mdio_write(tp, 0x06, 0x19a9); + rtl8168_mdio_write(tp, 0x06, 0x8b90); + rtl8168_mdio_write(tp, 0x06, 0xf9ee); + rtl8168_mdio_write(tp, 0x06, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0xfff7); + rtl8168_mdio_write(tp, 0x06, 0xffe0); + rtl8168_mdio_write(tp, 0x06, 0xe140); + rtl8168_mdio_write(tp, 0x06, 0xe1e1); + rtl8168_mdio_write(tp, 0x06, 0x41f7); + rtl8168_mdio_write(tp, 0x06, 0x2ff6); + rtl8168_mdio_write(tp, 0x06, 0x28e4); + rtl8168_mdio_write(tp, 0x06, 0xe140); + rtl8168_mdio_write(tp, 0x06, 0xe5e1); + rtl8168_mdio_write(tp, 0x06, 0x41f7); + rtl8168_mdio_write(tp, 0x06, 0x0002); + rtl8168_mdio_write(tp, 0x06, 0x020c); + rtl8168_mdio_write(tp, 0x06, 0x0202); + rtl8168_mdio_write(tp, 0x06, 0x1d02); + rtl8168_mdio_write(tp, 0x06, 0x0230); + rtl8168_mdio_write(tp, 0x06, 0x0202); + rtl8168_mdio_write(tp, 0x06, 0x4002); + rtl8168_mdio_write(tp, 0x06, 0x028b); + rtl8168_mdio_write(tp, 0x06, 0x0280); + rtl8168_mdio_write(tp, 0x06, 0x6c02); + rtl8168_mdio_write(tp, 0x06, 0x8085); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x88e1); + rtl8168_mdio_write(tp, 0x06, 0x8b89); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8a1e); + rtl8168_mdio_write(tp, 0x06, 0x01e1); + rtl8168_mdio_write(tp, 0x06, 0x8b8b); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8c1e); + rtl8168_mdio_write(tp, 0x06, 0x01e1); + rtl8168_mdio_write(tp, 0x06, 0x8b8d); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8e1e); + rtl8168_mdio_write(tp, 0x06, 0x01a0); + rtl8168_mdio_write(tp, 0x06, 0x00c7); + rtl8168_mdio_write(tp, 0x06, 0xaec3); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b8d); + rtl8168_mdio_write(tp, 0x06, 0xad20); + rtl8168_mdio_write(tp, 0x06, 0x10ee); + rtl8168_mdio_write(tp, 0x06, 0x8b8d); + rtl8168_mdio_write(tp, 0x06, 0x0002); + rtl8168_mdio_write(tp, 0x06, 0x1310); + rtl8168_mdio_write(tp, 0x06, 0x021f); + rtl8168_mdio_write(tp, 0x06, 0x9d02); + rtl8168_mdio_write(tp, 0x06, 0x1f0c); + rtl8168_mdio_write(tp, 0x06, 0x0227); + rtl8168_mdio_write(tp, 0x06, 0x49fc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x200b); + rtl8168_mdio_write(tp, 0x06, 0xf620); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8e02); + rtl8168_mdio_write(tp, 0x06, 0x830e); + rtl8168_mdio_write(tp, 0x06, 0x021b); + rtl8168_mdio_write(tp, 0x06, 0x67ad); + rtl8168_mdio_write(tp, 0x06, 0x2211); + rtl8168_mdio_write(tp, 0x06, 0xf622); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8e02); + rtl8168_mdio_write(tp, 0x06, 0x2ba5); + rtl8168_mdio_write(tp, 0x06, 0x022a); + rtl8168_mdio_write(tp, 0x06, 0x2402); + rtl8168_mdio_write(tp, 0x06, 0x80c6); + rtl8168_mdio_write(tp, 0x06, 0x022a); + rtl8168_mdio_write(tp, 0x06, 0xf0ad); + rtl8168_mdio_write(tp, 0x06, 0x2511); + rtl8168_mdio_write(tp, 0x06, 0xf625); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8e02); + rtl8168_mdio_write(tp, 0x06, 0x8226); + rtl8168_mdio_write(tp, 0x06, 0x0204); + rtl8168_mdio_write(tp, 0x06, 0x0302); + rtl8168_mdio_write(tp, 0x06, 0x19cc); + rtl8168_mdio_write(tp, 0x06, 0x022b); + rtl8168_mdio_write(tp, 0x06, 0x5bfc); + rtl8168_mdio_write(tp, 0x06, 0x04ee); + rtl8168_mdio_write(tp, 0x06, 0x8b8d); + rtl8168_mdio_write(tp, 0x06, 0x0105); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b83); + rtl8168_mdio_write(tp, 0x06, 0xad24); + rtl8168_mdio_write(tp, 0x06, 0x44e0); + rtl8168_mdio_write(tp, 0x06, 0xe022); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x23ad); + rtl8168_mdio_write(tp, 0x06, 0x223b); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0xbea0); + rtl8168_mdio_write(tp, 0x06, 0x0005); + rtl8168_mdio_write(tp, 0x06, 0x0228); + rtl8168_mdio_write(tp, 0x06, 0xdeae); + rtl8168_mdio_write(tp, 0x06, 0x42a0); + rtl8168_mdio_write(tp, 0x06, 0x0105); + rtl8168_mdio_write(tp, 0x06, 0x0228); + rtl8168_mdio_write(tp, 0x06, 0xf1ae); + rtl8168_mdio_write(tp, 0x06, 0x3aa0); + rtl8168_mdio_write(tp, 0x06, 0x0205); + rtl8168_mdio_write(tp, 0x06, 0x0281); + rtl8168_mdio_write(tp, 0x06, 0x25ae); + rtl8168_mdio_write(tp, 0x06, 0x32a0); + rtl8168_mdio_write(tp, 0x06, 0x0305); + rtl8168_mdio_write(tp, 0x06, 0x0229); + rtl8168_mdio_write(tp, 0x06, 0x9aae); + rtl8168_mdio_write(tp, 0x06, 0x2aa0); + rtl8168_mdio_write(tp, 0x06, 0x0405); + rtl8168_mdio_write(tp, 0x06, 0x0229); + rtl8168_mdio_write(tp, 0x06, 0xaeae); + rtl8168_mdio_write(tp, 0x06, 0x22a0); + rtl8168_mdio_write(tp, 0x06, 0x0505); + rtl8168_mdio_write(tp, 0x06, 0x0229); + rtl8168_mdio_write(tp, 0x06, 0xd7ae); + rtl8168_mdio_write(tp, 0x06, 0x1aa0); + rtl8168_mdio_write(tp, 0x06, 0x0605); + rtl8168_mdio_write(tp, 0x06, 0x0229); + rtl8168_mdio_write(tp, 0x06, 0xfeae); + rtl8168_mdio_write(tp, 0x06, 0x12ee); + rtl8168_mdio_write(tp, 0x06, 0x8ac0); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0x8ac1); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0x8ac6); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0x8abe); + rtl8168_mdio_write(tp, 0x06, 0x00ae); + rtl8168_mdio_write(tp, 0x06, 0x00fc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0x022a); + rtl8168_mdio_write(tp, 0x06, 0x67e0); + rtl8168_mdio_write(tp, 0x06, 0xe022); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x230d); + rtl8168_mdio_write(tp, 0x06, 0x0658); + rtl8168_mdio_write(tp, 0x06, 0x03a0); + rtl8168_mdio_write(tp, 0x06, 0x0202); + rtl8168_mdio_write(tp, 0x06, 0xae2d); + rtl8168_mdio_write(tp, 0x06, 0xa001); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0x2da0); + rtl8168_mdio_write(tp, 0x06, 0x004d); + rtl8168_mdio_write(tp, 0x06, 0xe0e2); + rtl8168_mdio_write(tp, 0x06, 0x00e1); + rtl8168_mdio_write(tp, 0x06, 0xe201); + rtl8168_mdio_write(tp, 0x06, 0xad24); + rtl8168_mdio_write(tp, 0x06, 0x44e0); + rtl8168_mdio_write(tp, 0x06, 0x8ac2); + rtl8168_mdio_write(tp, 0x06, 0xe48a); + rtl8168_mdio_write(tp, 0x06, 0xc4e0); + rtl8168_mdio_write(tp, 0x06, 0x8ac3); + rtl8168_mdio_write(tp, 0x06, 0xe48a); + rtl8168_mdio_write(tp, 0x06, 0xc5ee); + rtl8168_mdio_write(tp, 0x06, 0x8abe); + rtl8168_mdio_write(tp, 0x06, 0x03e0); + rtl8168_mdio_write(tp, 0x06, 0x8b83); + rtl8168_mdio_write(tp, 0x06, 0xad25); + rtl8168_mdio_write(tp, 0x06, 0x3aee); + rtl8168_mdio_write(tp, 0x06, 0x8abe); + rtl8168_mdio_write(tp, 0x06, 0x05ae); + rtl8168_mdio_write(tp, 0x06, 0x34e0); + rtl8168_mdio_write(tp, 0x06, 0x8ace); + rtl8168_mdio_write(tp, 0x06, 0xae03); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0xcfe1); + rtl8168_mdio_write(tp, 0x06, 0x8ac2); + rtl8168_mdio_write(tp, 0x06, 0x4905); + rtl8168_mdio_write(tp, 0x06, 0xe58a); + rtl8168_mdio_write(tp, 0x06, 0xc4e1); + rtl8168_mdio_write(tp, 0x06, 0x8ac3); + rtl8168_mdio_write(tp, 0x06, 0x4905); + rtl8168_mdio_write(tp, 0x06, 0xe58a); + rtl8168_mdio_write(tp, 0x06, 0xc5ee); + rtl8168_mdio_write(tp, 0x06, 0x8abe); + rtl8168_mdio_write(tp, 0x06, 0x0502); + rtl8168_mdio_write(tp, 0x06, 0x2ab6); + rtl8168_mdio_write(tp, 0x06, 0xac20); + rtl8168_mdio_write(tp, 0x06, 0x1202); + rtl8168_mdio_write(tp, 0x06, 0x819b); + rtl8168_mdio_write(tp, 0x06, 0xac20); + rtl8168_mdio_write(tp, 0x06, 0x0cee); + rtl8168_mdio_write(tp, 0x06, 0x8ac1); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0x8ac6); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0x8abe); + rtl8168_mdio_write(tp, 0x06, 0x02fc); + rtl8168_mdio_write(tp, 0x06, 0x04d0); + rtl8168_mdio_write(tp, 0x06, 0x0002); + rtl8168_mdio_write(tp, 0x06, 0x81ad); + rtl8168_mdio_write(tp, 0x06, 0x590f); + rtl8168_mdio_write(tp, 0x06, 0x3902); + rtl8168_mdio_write(tp, 0x06, 0xaa04); + rtl8168_mdio_write(tp, 0x06, 0xd001); + rtl8168_mdio_write(tp, 0x06, 0xae02); + rtl8168_mdio_write(tp, 0x06, 0xd000); + rtl8168_mdio_write(tp, 0x06, 0x04f9); + rtl8168_mdio_write(tp, 0x06, 0xfae2); + rtl8168_mdio_write(tp, 0x06, 0xe2d2); + rtl8168_mdio_write(tp, 0x06, 0xe3e2); + rtl8168_mdio_write(tp, 0x06, 0xd3f9); + rtl8168_mdio_write(tp, 0x06, 0x5af7); + rtl8168_mdio_write(tp, 0x06, 0xe6e2); + rtl8168_mdio_write(tp, 0x06, 0xd2e7); + rtl8168_mdio_write(tp, 0x06, 0xe2d3); + rtl8168_mdio_write(tp, 0x06, 0xe2e0); + rtl8168_mdio_write(tp, 0x06, 0x2ce3); + rtl8168_mdio_write(tp, 0x06, 0xe02d); + rtl8168_mdio_write(tp, 0x06, 0xf95b); + rtl8168_mdio_write(tp, 0x06, 0xe01e); + rtl8168_mdio_write(tp, 0x06, 0x30e6); + rtl8168_mdio_write(tp, 0x06, 0xe02c); + rtl8168_mdio_write(tp, 0x06, 0xe7e0); + rtl8168_mdio_write(tp, 0x06, 0x2de2); + rtl8168_mdio_write(tp, 0x06, 0xe2cc); + rtl8168_mdio_write(tp, 0x06, 0xe3e2); + rtl8168_mdio_write(tp, 0x06, 0xcdf9); + rtl8168_mdio_write(tp, 0x06, 0x5a0f); + rtl8168_mdio_write(tp, 0x06, 0x6a50); + rtl8168_mdio_write(tp, 0x06, 0xe6e2); + rtl8168_mdio_write(tp, 0x06, 0xcce7); + rtl8168_mdio_write(tp, 0x06, 0xe2cd); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x3ce1); + rtl8168_mdio_write(tp, 0x06, 0xe03d); + rtl8168_mdio_write(tp, 0x06, 0xef64); + rtl8168_mdio_write(tp, 0x06, 0xfde0); + rtl8168_mdio_write(tp, 0x06, 0xe2cc); + rtl8168_mdio_write(tp, 0x06, 0xe1e2); + rtl8168_mdio_write(tp, 0x06, 0xcd58); + rtl8168_mdio_write(tp, 0x06, 0x0f5a); + rtl8168_mdio_write(tp, 0x06, 0xf01e); + rtl8168_mdio_write(tp, 0x06, 0x02e4); + rtl8168_mdio_write(tp, 0x06, 0xe2cc); + rtl8168_mdio_write(tp, 0x06, 0xe5e2); + rtl8168_mdio_write(tp, 0x06, 0xcdfd); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x2ce1); + rtl8168_mdio_write(tp, 0x06, 0xe02d); + rtl8168_mdio_write(tp, 0x06, 0x59e0); + rtl8168_mdio_write(tp, 0x06, 0x5b1f); + rtl8168_mdio_write(tp, 0x06, 0x1e13); + rtl8168_mdio_write(tp, 0x06, 0xe4e0); + rtl8168_mdio_write(tp, 0x06, 0x2ce5); + rtl8168_mdio_write(tp, 0x06, 0xe02d); + rtl8168_mdio_write(tp, 0x06, 0xfde0); + rtl8168_mdio_write(tp, 0x06, 0xe2d2); + rtl8168_mdio_write(tp, 0x06, 0xe1e2); + rtl8168_mdio_write(tp, 0x06, 0xd358); + rtl8168_mdio_write(tp, 0x06, 0xf75a); + rtl8168_mdio_write(tp, 0x06, 0x081e); + rtl8168_mdio_write(tp, 0x06, 0x02e4); + rtl8168_mdio_write(tp, 0x06, 0xe2d2); + rtl8168_mdio_write(tp, 0x06, 0xe5e2); + rtl8168_mdio_write(tp, 0x06, 0xd3ef); + rtl8168_mdio_write(tp, 0x06, 0x46fe); + rtl8168_mdio_write(tp, 0x06, 0xfd04); + rtl8168_mdio_write(tp, 0x06, 0xf8f9); + rtl8168_mdio_write(tp, 0x06, 0xfaef); + rtl8168_mdio_write(tp, 0x06, 0x69e0); + rtl8168_mdio_write(tp, 0x06, 0xe022); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x2358); + rtl8168_mdio_write(tp, 0x06, 0xc4e1); + rtl8168_mdio_write(tp, 0x06, 0x8b6e); + rtl8168_mdio_write(tp, 0x06, 0x1f10); + rtl8168_mdio_write(tp, 0x06, 0x9e58); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x6ead); + rtl8168_mdio_write(tp, 0x06, 0x2222); + rtl8168_mdio_write(tp, 0x06, 0xac27); + rtl8168_mdio_write(tp, 0x06, 0x55ac); + rtl8168_mdio_write(tp, 0x06, 0x2602); + rtl8168_mdio_write(tp, 0x06, 0xae1a); + rtl8168_mdio_write(tp, 0x06, 0xd106); + rtl8168_mdio_write(tp, 0x06, 0xbf3b); + rtl8168_mdio_write(tp, 0x06, 0xba02); + rtl8168_mdio_write(tp, 0x06, 0x2dc1); + rtl8168_mdio_write(tp, 0x06, 0xd107); + rtl8168_mdio_write(tp, 0x06, 0xbf3b); + rtl8168_mdio_write(tp, 0x06, 0xbd02); + rtl8168_mdio_write(tp, 0x06, 0x2dc1); + rtl8168_mdio_write(tp, 0x06, 0xd107); + rtl8168_mdio_write(tp, 0x06, 0xbf3b); + rtl8168_mdio_write(tp, 0x06, 0xc002); + rtl8168_mdio_write(tp, 0x06, 0x2dc1); + rtl8168_mdio_write(tp, 0x06, 0xae30); + rtl8168_mdio_write(tp, 0x06, 0xd103); + rtl8168_mdio_write(tp, 0x06, 0xbf3b); + rtl8168_mdio_write(tp, 0x06, 0xc302); + rtl8168_mdio_write(tp, 0x06, 0x2dc1); + rtl8168_mdio_write(tp, 0x06, 0xd100); + rtl8168_mdio_write(tp, 0x06, 0xbf3b); + rtl8168_mdio_write(tp, 0x06, 0xc602); + rtl8168_mdio_write(tp, 0x06, 0x2dc1); + rtl8168_mdio_write(tp, 0x06, 0xd100); + rtl8168_mdio_write(tp, 0x06, 0xbf82); + rtl8168_mdio_write(tp, 0x06, 0xca02); + rtl8168_mdio_write(tp, 0x06, 0x2dc1); + rtl8168_mdio_write(tp, 0x06, 0xd10f); + rtl8168_mdio_write(tp, 0x06, 0xbf3b); + rtl8168_mdio_write(tp, 0x06, 0xba02); + rtl8168_mdio_write(tp, 0x06, 0x2dc1); + rtl8168_mdio_write(tp, 0x06, 0xd101); + rtl8168_mdio_write(tp, 0x06, 0xbf3b); + rtl8168_mdio_write(tp, 0x06, 0xbd02); + rtl8168_mdio_write(tp, 0x06, 0x2dc1); + rtl8168_mdio_write(tp, 0x06, 0xd101); + rtl8168_mdio_write(tp, 0x06, 0xbf3b); + rtl8168_mdio_write(tp, 0x06, 0xc002); + rtl8168_mdio_write(tp, 0x06, 0x2dc1); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfefd); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xd100); + rtl8168_mdio_write(tp, 0x06, 0xbf3b); + rtl8168_mdio_write(tp, 0x06, 0xc302); + rtl8168_mdio_write(tp, 0x06, 0x2dc1); + rtl8168_mdio_write(tp, 0x06, 0xd011); + rtl8168_mdio_write(tp, 0x06, 0x022b); + rtl8168_mdio_write(tp, 0x06, 0xfb59); + rtl8168_mdio_write(tp, 0x06, 0x03ef); + rtl8168_mdio_write(tp, 0x06, 0x01d1); + rtl8168_mdio_write(tp, 0x06, 0x00a0); + rtl8168_mdio_write(tp, 0x06, 0x0002); + rtl8168_mdio_write(tp, 0x06, 0xd101); + rtl8168_mdio_write(tp, 0x06, 0xbf3b); + rtl8168_mdio_write(tp, 0x06, 0xc602); + rtl8168_mdio_write(tp, 0x06, 0x2dc1); + rtl8168_mdio_write(tp, 0x06, 0xd111); + rtl8168_mdio_write(tp, 0x06, 0xad20); + rtl8168_mdio_write(tp, 0x06, 0x020c); + rtl8168_mdio_write(tp, 0x06, 0x11ad); + rtl8168_mdio_write(tp, 0x06, 0x2102); + rtl8168_mdio_write(tp, 0x06, 0x0c12); + rtl8168_mdio_write(tp, 0x06, 0xbf82); + rtl8168_mdio_write(tp, 0x06, 0xca02); + rtl8168_mdio_write(tp, 0x06, 0x2dc1); + rtl8168_mdio_write(tp, 0x06, 0xaec8); + rtl8168_mdio_write(tp, 0x06, 0x70e4); + rtl8168_mdio_write(tp, 0x06, 0x2602); + rtl8168_mdio_write(tp, 0x06, 0x82d1); + rtl8168_mdio_write(tp, 0x06, 0x05f8); + rtl8168_mdio_write(tp, 0x06, 0xfaef); + rtl8168_mdio_write(tp, 0x06, 0x69e0); + rtl8168_mdio_write(tp, 0x06, 0xe2fe); + rtl8168_mdio_write(tp, 0x06, 0xe1e2); + rtl8168_mdio_write(tp, 0x06, 0xffad); + rtl8168_mdio_write(tp, 0x06, 0x2d1a); + rtl8168_mdio_write(tp, 0x06, 0xe0e1); + rtl8168_mdio_write(tp, 0x06, 0x4ee1); + rtl8168_mdio_write(tp, 0x06, 0xe14f); + rtl8168_mdio_write(tp, 0x06, 0xac2d); + rtl8168_mdio_write(tp, 0x06, 0x22f6); + rtl8168_mdio_write(tp, 0x06, 0x0302); + rtl8168_mdio_write(tp, 0x06, 0x033b); + rtl8168_mdio_write(tp, 0x06, 0xf703); + rtl8168_mdio_write(tp, 0x06, 0xf706); + rtl8168_mdio_write(tp, 0x06, 0xbf84); + rtl8168_mdio_write(tp, 0x06, 0x4402); + rtl8168_mdio_write(tp, 0x06, 0x2d21); + rtl8168_mdio_write(tp, 0x06, 0xae11); + rtl8168_mdio_write(tp, 0x06, 0xe0e1); + rtl8168_mdio_write(tp, 0x06, 0x4ee1); + rtl8168_mdio_write(tp, 0x06, 0xe14f); + rtl8168_mdio_write(tp, 0x06, 0xad2d); + rtl8168_mdio_write(tp, 0x06, 0x08bf); + rtl8168_mdio_write(tp, 0x06, 0x844f); + rtl8168_mdio_write(tp, 0x06, 0x022d); + rtl8168_mdio_write(tp, 0x06, 0x21f6); + rtl8168_mdio_write(tp, 0x06, 0x06ef); + rtl8168_mdio_write(tp, 0x06, 0x96fe); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0x0283); + rtl8168_mdio_write(tp, 0x06, 0x4502); + rtl8168_mdio_write(tp, 0x06, 0x83a2); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x00e1); + rtl8168_mdio_write(tp, 0x06, 0xe001); + rtl8168_mdio_write(tp, 0x06, 0xad27); + rtl8168_mdio_write(tp, 0x06, 0x1fd1); + rtl8168_mdio_write(tp, 0x06, 0x01bf); + rtl8168_mdio_write(tp, 0x06, 0x843b); + rtl8168_mdio_write(tp, 0x06, 0x022d); + rtl8168_mdio_write(tp, 0x06, 0xc1e0); + rtl8168_mdio_write(tp, 0x06, 0xe020); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x21ad); + rtl8168_mdio_write(tp, 0x06, 0x200e); + rtl8168_mdio_write(tp, 0x06, 0xd100); + rtl8168_mdio_write(tp, 0x06, 0xbf84); + rtl8168_mdio_write(tp, 0x06, 0x3b02); + rtl8168_mdio_write(tp, 0x06, 0x2dc1); + rtl8168_mdio_write(tp, 0x06, 0xbf3b); + rtl8168_mdio_write(tp, 0x06, 0x9602); + rtl8168_mdio_write(tp, 0x06, 0x2d21); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfefc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xf9fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x87ad); + rtl8168_mdio_write(tp, 0x06, 0x204c); + rtl8168_mdio_write(tp, 0x06, 0xd200); + rtl8168_mdio_write(tp, 0x06, 0xe0e2); + rtl8168_mdio_write(tp, 0x06, 0x0058); + rtl8168_mdio_write(tp, 0x06, 0x010c); + rtl8168_mdio_write(tp, 0x06, 0x021e); + rtl8168_mdio_write(tp, 0x06, 0x20e0); + rtl8168_mdio_write(tp, 0x06, 0xe000); + rtl8168_mdio_write(tp, 0x06, 0x5810); + rtl8168_mdio_write(tp, 0x06, 0x1e20); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x3658); + rtl8168_mdio_write(tp, 0x06, 0x031e); + rtl8168_mdio_write(tp, 0x06, 0x20e0); + rtl8168_mdio_write(tp, 0x06, 0xe022); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x2358); + rtl8168_mdio_write(tp, 0x06, 0xe01e); + rtl8168_mdio_write(tp, 0x06, 0x20e0); + rtl8168_mdio_write(tp, 0x06, 0x8b64); + rtl8168_mdio_write(tp, 0x06, 0x1f02); + rtl8168_mdio_write(tp, 0x06, 0x9e22); + rtl8168_mdio_write(tp, 0x06, 0xe68b); + rtl8168_mdio_write(tp, 0x06, 0x64ad); + rtl8168_mdio_write(tp, 0x06, 0x3214); + rtl8168_mdio_write(tp, 0x06, 0xad34); + rtl8168_mdio_write(tp, 0x06, 0x11ef); + rtl8168_mdio_write(tp, 0x06, 0x0258); + rtl8168_mdio_write(tp, 0x06, 0x039e); + rtl8168_mdio_write(tp, 0x06, 0x07ad); + rtl8168_mdio_write(tp, 0x06, 0x3508); + rtl8168_mdio_write(tp, 0x06, 0x5ac0); + rtl8168_mdio_write(tp, 0x06, 0x9f04); + rtl8168_mdio_write(tp, 0x06, 0xd101); + rtl8168_mdio_write(tp, 0x06, 0xae02); + rtl8168_mdio_write(tp, 0x06, 0xd100); + rtl8168_mdio_write(tp, 0x06, 0xbf84); + rtl8168_mdio_write(tp, 0x06, 0x3e02); + rtl8168_mdio_write(tp, 0x06, 0x2dc1); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfefd); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8f9); + rtl8168_mdio_write(tp, 0x06, 0xfbe0); + rtl8168_mdio_write(tp, 0x06, 0x8b85); + rtl8168_mdio_write(tp, 0x06, 0xad25); + rtl8168_mdio_write(tp, 0x06, 0x22e0); + rtl8168_mdio_write(tp, 0x06, 0xe022); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x23e2); + rtl8168_mdio_write(tp, 0x06, 0xe036); + rtl8168_mdio_write(tp, 0x06, 0xe3e0); + rtl8168_mdio_write(tp, 0x06, 0x375a); + rtl8168_mdio_write(tp, 0x06, 0xc40d); + rtl8168_mdio_write(tp, 0x06, 0x0158); + rtl8168_mdio_write(tp, 0x06, 0x021e); + rtl8168_mdio_write(tp, 0x06, 0x20e3); + rtl8168_mdio_write(tp, 0x06, 0x8ae7); + rtl8168_mdio_write(tp, 0x06, 0xac31); + rtl8168_mdio_write(tp, 0x06, 0x60ac); + rtl8168_mdio_write(tp, 0x06, 0x3a08); + rtl8168_mdio_write(tp, 0x06, 0xac3e); + rtl8168_mdio_write(tp, 0x06, 0x26ae); + rtl8168_mdio_write(tp, 0x06, 0x67af); + rtl8168_mdio_write(tp, 0x06, 0x8437); + rtl8168_mdio_write(tp, 0x06, 0xad37); + rtl8168_mdio_write(tp, 0x06, 0x61e0); + rtl8168_mdio_write(tp, 0x06, 0x8ae8); + rtl8168_mdio_write(tp, 0x06, 0x10e4); + rtl8168_mdio_write(tp, 0x06, 0x8ae8); + rtl8168_mdio_write(tp, 0x06, 0xe18a); + rtl8168_mdio_write(tp, 0x06, 0xe91b); + rtl8168_mdio_write(tp, 0x06, 0x109e); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0x51d1); + rtl8168_mdio_write(tp, 0x06, 0x00bf); + rtl8168_mdio_write(tp, 0x06, 0x8441); + rtl8168_mdio_write(tp, 0x06, 0x022d); + rtl8168_mdio_write(tp, 0x06, 0xc1ee); + rtl8168_mdio_write(tp, 0x06, 0x8ae8); + rtl8168_mdio_write(tp, 0x06, 0x00ae); + rtl8168_mdio_write(tp, 0x06, 0x43ad); + rtl8168_mdio_write(tp, 0x06, 0x3627); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0xeee1); + rtl8168_mdio_write(tp, 0x06, 0x8aef); + rtl8168_mdio_write(tp, 0x06, 0xef74); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0xeae1); + rtl8168_mdio_write(tp, 0x06, 0x8aeb); + rtl8168_mdio_write(tp, 0x06, 0x1b74); + rtl8168_mdio_write(tp, 0x06, 0x9e2e); + rtl8168_mdio_write(tp, 0x06, 0x14e4); + rtl8168_mdio_write(tp, 0x06, 0x8aea); + rtl8168_mdio_write(tp, 0x06, 0xe58a); + rtl8168_mdio_write(tp, 0x06, 0xebef); + rtl8168_mdio_write(tp, 0x06, 0x74e0); + rtl8168_mdio_write(tp, 0x06, 0x8aee); + rtl8168_mdio_write(tp, 0x06, 0xe18a); + rtl8168_mdio_write(tp, 0x06, 0xef1b); + rtl8168_mdio_write(tp, 0x06, 0x479e); + rtl8168_mdio_write(tp, 0x06, 0x0fae); + rtl8168_mdio_write(tp, 0x06, 0x19ee); + rtl8168_mdio_write(tp, 0x06, 0x8aea); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0x8aeb); + rtl8168_mdio_write(tp, 0x06, 0x00ae); + rtl8168_mdio_write(tp, 0x06, 0x0fac); + rtl8168_mdio_write(tp, 0x06, 0x390c); + rtl8168_mdio_write(tp, 0x06, 0xd101); + rtl8168_mdio_write(tp, 0x06, 0xbf84); + rtl8168_mdio_write(tp, 0x06, 0x4102); + rtl8168_mdio_write(tp, 0x06, 0x2dc1); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0xe800); + rtl8168_mdio_write(tp, 0x06, 0xe68a); + rtl8168_mdio_write(tp, 0x06, 0xe7ff); + rtl8168_mdio_write(tp, 0x06, 0xfdfc); + rtl8168_mdio_write(tp, 0x06, 0x0400); + rtl8168_mdio_write(tp, 0x06, 0xe234); + rtl8168_mdio_write(tp, 0x06, 0xcce2); + rtl8168_mdio_write(tp, 0x06, 0x0088); + rtl8168_mdio_write(tp, 0x06, 0xe200); + rtl8168_mdio_write(tp, 0x06, 0xa725); + rtl8168_mdio_write(tp, 0x06, 0xe50a); + rtl8168_mdio_write(tp, 0x06, 0x1de5); + rtl8168_mdio_write(tp, 0x06, 0x0a2c); + rtl8168_mdio_write(tp, 0x06, 0xe50a); + rtl8168_mdio_write(tp, 0x06, 0x6de5); + rtl8168_mdio_write(tp, 0x06, 0x0a1d); + rtl8168_mdio_write(tp, 0x06, 0xe50a); + rtl8168_mdio_write(tp, 0x06, 0x1ce5); + rtl8168_mdio_write(tp, 0x06, 0x0a2d); + rtl8168_mdio_write(tp, 0x06, 0xa755); + rtl8168_mdio_write(tp, 0x05, 0x8b64); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x05, 0x8b94); + rtl8168_mdio_write(tp, 0x06, 0x82cd); + rtl8168_mdio_write(tp, 0x05, 0x8b85); + rtl8168_mdio_write(tp, 0x06, 0x2000); + rtl8168_mdio_write(tp, 0x05, 0x8aee); + rtl8168_mdio_write(tp, 0x06, 0x03b8); + rtl8168_mdio_write(tp, 0x05, 0x8ae8); + rtl8168_mdio_write(tp, 0x06, 0x0002); + gphy_val = rtl8168_mdio_read(tp, 0x01); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp, 0x01, gphy_val); + gphy_val = rtl8168_mdio_read(tp, 0x00); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp, 0x00, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0005); + for (i = 0; i < 200; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x00); + if (gphy_val & BIT_7) + break; + } + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0023); + gphy_val = rtl8168_mdio_read(tp, 0x17); + gphy_val &= ~(BIT_0); + if (tp->RequiredSecLanDonglePatch) + gphy_val &= ~(BIT_2); + rtl8168_mdio_write(tp, 0x17, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0028); + rtl8168_mdio_write(tp, 0x15, 0x0010); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0041); + rtl8168_mdio_write(tp, 0x15, 0x0802); + rtl8168_mdio_write(tp, 0x16, 0x2185); + rtl8168_mdio_write(tp, 0x1f, 0x0000); +} + +static void +rtl8168_set_phy_mcu_8168e_2(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned int gphy_val,i; + + if (rtl8168_efuse_read(tp, 0x22) == 0x0c) { + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x00, 0x1800); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0023); + rtl8168_mdio_write(tp, 0x17, 0x0117); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x002C); + rtl8168_mdio_write(tp, 0x1B, 0x5000); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x16, 0x4104); + for (i = 0; i < 200; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x1E); + gphy_val &= 0x03FF; + if (gphy_val==0x000C) + break; + } + rtl8168_mdio_write(tp, 0x1f, 0x0005); + for (i = 0; i < 200; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x07); + if ((gphy_val & BIT_5) == 0) + break; + } + gphy_val = rtl8168_mdio_read(tp, 0x07); + if (gphy_val & BIT_5) { + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x00a1); + rtl8168_mdio_write(tp, 0x17, 0x1000); + rtl8168_mdio_write(tp, 0x17, 0x0000); + rtl8168_mdio_write(tp, 0x17, 0x2000); + rtl8168_mdio_write(tp, 0x1e, 0x002f); + rtl8168_mdio_write(tp, 0x18, 0x9bfb); + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x07, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + } + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x0080); + gphy_val = rtl8168_mdio_read(tp, 0x00); + gphy_val &= ~(BIT_7); + rtl8168_mdio_write(tp, 0x00, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0002); + gphy_val = rtl8168_mdio_read(tp, 0x08); + gphy_val &= ~(BIT_7); + rtl8168_mdio_write(tp, 0x08, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0023); + rtl8168_mdio_write(tp, 0x16, 0x0306); + rtl8168_mdio_write(tp, 0x16, 0x0307); + rtl8168_mdio_write(tp, 0x15, 0x000e); + rtl8168_mdio_write(tp, 0x19, 0x000a); + rtl8168_mdio_write(tp, 0x15, 0x0010); + rtl8168_mdio_write(tp, 0x19, 0x0008); + rtl8168_mdio_write(tp, 0x15, 0x0018); + rtl8168_mdio_write(tp, 0x19, 0x4801); + rtl8168_mdio_write(tp, 0x15, 0x0019); + rtl8168_mdio_write(tp, 0x19, 0x6801); + rtl8168_mdio_write(tp, 0x15, 0x001a); + rtl8168_mdio_write(tp, 0x19, 0x66a1); + rtl8168_mdio_write(tp, 0x15, 0x001f); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0020); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0021); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0022); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0023); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0024); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0025); + rtl8168_mdio_write(tp, 0x19, 0x64a1); + rtl8168_mdio_write(tp, 0x15, 0x0026); + rtl8168_mdio_write(tp, 0x19, 0x40ea); + rtl8168_mdio_write(tp, 0x15, 0x0027); + rtl8168_mdio_write(tp, 0x19, 0x4503); + rtl8168_mdio_write(tp, 0x15, 0x0028); + rtl8168_mdio_write(tp, 0x19, 0x9f00); + rtl8168_mdio_write(tp, 0x15, 0x0029); + rtl8168_mdio_write(tp, 0x19, 0xa631); + rtl8168_mdio_write(tp, 0x15, 0x002a); + rtl8168_mdio_write(tp, 0x19, 0x9717); + rtl8168_mdio_write(tp, 0x15, 0x002b); + rtl8168_mdio_write(tp, 0x19, 0x302c); + rtl8168_mdio_write(tp, 0x15, 0x002c); + rtl8168_mdio_write(tp, 0x19, 0x4802); + rtl8168_mdio_write(tp, 0x15, 0x002d); + rtl8168_mdio_write(tp, 0x19, 0x58da); + rtl8168_mdio_write(tp, 0x15, 0x002e); + rtl8168_mdio_write(tp, 0x19, 0x400d); + rtl8168_mdio_write(tp, 0x15, 0x002f); + rtl8168_mdio_write(tp, 0x19, 0x4488); + rtl8168_mdio_write(tp, 0x15, 0x0030); + rtl8168_mdio_write(tp, 0x19, 0x9e00); + rtl8168_mdio_write(tp, 0x15, 0x0031); + rtl8168_mdio_write(tp, 0x19, 0x63c8); + rtl8168_mdio_write(tp, 0x15, 0x0032); + rtl8168_mdio_write(tp, 0x19, 0x6481); + rtl8168_mdio_write(tp, 0x15, 0x0033); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0034); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0035); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0036); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0037); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0038); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0039); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x003a); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x003b); + rtl8168_mdio_write(tp, 0x19, 0x63e8); + rtl8168_mdio_write(tp, 0x15, 0x003c); + rtl8168_mdio_write(tp, 0x19, 0x7d00); + rtl8168_mdio_write(tp, 0x15, 0x003d); + rtl8168_mdio_write(tp, 0x19, 0x59d4); + rtl8168_mdio_write(tp, 0x15, 0x003e); + rtl8168_mdio_write(tp, 0x19, 0x63f8); + rtl8168_mdio_write(tp, 0x15, 0x0040); + rtl8168_mdio_write(tp, 0x19, 0x64a1); + rtl8168_mdio_write(tp, 0x15, 0x0041); + rtl8168_mdio_write(tp, 0x19, 0x30de); + rtl8168_mdio_write(tp, 0x15, 0x0044); + rtl8168_mdio_write(tp, 0x19, 0x480f); + rtl8168_mdio_write(tp, 0x15, 0x0045); + rtl8168_mdio_write(tp, 0x19, 0x6800); + rtl8168_mdio_write(tp, 0x15, 0x0046); + rtl8168_mdio_write(tp, 0x19, 0x6680); + rtl8168_mdio_write(tp, 0x15, 0x0047); + rtl8168_mdio_write(tp, 0x19, 0x7c10); + rtl8168_mdio_write(tp, 0x15, 0x0048); + rtl8168_mdio_write(tp, 0x19, 0x63c8); + rtl8168_mdio_write(tp, 0x15, 0x0049); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x004a); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x004b); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x004c); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x004d); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x004e); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x004f); + rtl8168_mdio_write(tp, 0x19, 0x40ea); + rtl8168_mdio_write(tp, 0x15, 0x0050); + rtl8168_mdio_write(tp, 0x19, 0x4503); + rtl8168_mdio_write(tp, 0x15, 0x0051); + rtl8168_mdio_write(tp, 0x19, 0x58ca); + rtl8168_mdio_write(tp, 0x15, 0x0052); + rtl8168_mdio_write(tp, 0x19, 0x63c8); + rtl8168_mdio_write(tp, 0x15, 0x0053); + rtl8168_mdio_write(tp, 0x19, 0x63d8); + rtl8168_mdio_write(tp, 0x15, 0x0054); + rtl8168_mdio_write(tp, 0x19, 0x66a0); + rtl8168_mdio_write(tp, 0x15, 0x0055); + rtl8168_mdio_write(tp, 0x19, 0x9f00); + rtl8168_mdio_write(tp, 0x15, 0x0056); + rtl8168_mdio_write(tp, 0x19, 0x3000); + rtl8168_mdio_write(tp, 0x15, 0x00a1); + rtl8168_mdio_write(tp, 0x19, 0x3044); + rtl8168_mdio_write(tp, 0x15, 0x00ab); + rtl8168_mdio_write(tp, 0x19, 0x5820); + rtl8168_mdio_write(tp, 0x15, 0x00ac); + rtl8168_mdio_write(tp, 0x19, 0x5e04); + rtl8168_mdio_write(tp, 0x15, 0x00ad); + rtl8168_mdio_write(tp, 0x19, 0xb60c); + rtl8168_mdio_write(tp, 0x15, 0x00af); + rtl8168_mdio_write(tp, 0x19, 0x000a); + rtl8168_mdio_write(tp, 0x15, 0x00b2); + rtl8168_mdio_write(tp, 0x19, 0x30b9); + rtl8168_mdio_write(tp, 0x15, 0x00b9); + rtl8168_mdio_write(tp, 0x19, 0x4408); + rtl8168_mdio_write(tp, 0x15, 0x00ba); + rtl8168_mdio_write(tp, 0x19, 0x480b); + rtl8168_mdio_write(tp, 0x15, 0x00bb); + rtl8168_mdio_write(tp, 0x19, 0x5e00); + rtl8168_mdio_write(tp, 0x15, 0x00bc); + rtl8168_mdio_write(tp, 0x19, 0x405f); + rtl8168_mdio_write(tp, 0x15, 0x00bd); + rtl8168_mdio_write(tp, 0x19, 0x4448); + rtl8168_mdio_write(tp, 0x15, 0x00be); + rtl8168_mdio_write(tp, 0x19, 0x4020); + rtl8168_mdio_write(tp, 0x15, 0x00bf); + rtl8168_mdio_write(tp, 0x19, 0x4468); + rtl8168_mdio_write(tp, 0x15, 0x00c0); + rtl8168_mdio_write(tp, 0x19, 0x9c02); + rtl8168_mdio_write(tp, 0x15, 0x00c1); + rtl8168_mdio_write(tp, 0x19, 0x58a0); + rtl8168_mdio_write(tp, 0x15, 0x00c2); + rtl8168_mdio_write(tp, 0x19, 0xb605); + rtl8168_mdio_write(tp, 0x15, 0x00c3); + rtl8168_mdio_write(tp, 0x19, 0xc0d3); + rtl8168_mdio_write(tp, 0x15, 0x00c4); + rtl8168_mdio_write(tp, 0x19, 0x00e6); + rtl8168_mdio_write(tp, 0x15, 0x00c5); + rtl8168_mdio_write(tp, 0x19, 0xdaec); + rtl8168_mdio_write(tp, 0x15, 0x00c6); + rtl8168_mdio_write(tp, 0x19, 0x00fa); + rtl8168_mdio_write(tp, 0x15, 0x00c7); + rtl8168_mdio_write(tp, 0x19, 0x9df9); + rtl8168_mdio_write(tp, 0x15, 0x0112); + rtl8168_mdio_write(tp, 0x19, 0x6421); + rtl8168_mdio_write(tp, 0x15, 0x0113); + rtl8168_mdio_write(tp, 0x19, 0x7c08); + rtl8168_mdio_write(tp, 0x15, 0x0114); + rtl8168_mdio_write(tp, 0x19, 0x63f0); + rtl8168_mdio_write(tp, 0x15, 0x0115); + rtl8168_mdio_write(tp, 0x19, 0x4003); + rtl8168_mdio_write(tp, 0x15, 0x0116); + rtl8168_mdio_write(tp, 0x19, 0x4418); + rtl8168_mdio_write(tp, 0x15, 0x0117); + rtl8168_mdio_write(tp, 0x19, 0x9b00); + rtl8168_mdio_write(tp, 0x15, 0x0118); + rtl8168_mdio_write(tp, 0x19, 0x6461); + rtl8168_mdio_write(tp, 0x15, 0x0119); + rtl8168_mdio_write(tp, 0x19, 0x64e1); + rtl8168_mdio_write(tp, 0x15, 0x011a); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0150); + rtl8168_mdio_write(tp, 0x19, 0x7c80); + rtl8168_mdio_write(tp, 0x15, 0x0151); + rtl8168_mdio_write(tp, 0x19, 0x6461); + rtl8168_mdio_write(tp, 0x15, 0x0152); + rtl8168_mdio_write(tp, 0x19, 0x4003); + rtl8168_mdio_write(tp, 0x15, 0x0153); + rtl8168_mdio_write(tp, 0x19, 0x4540); + rtl8168_mdio_write(tp, 0x15, 0x0154); + rtl8168_mdio_write(tp, 0x19, 0x9f00); + rtl8168_mdio_write(tp, 0x15, 0x0155); + rtl8168_mdio_write(tp, 0x19, 0x9d00); + rtl8168_mdio_write(tp, 0x15, 0x0156); + rtl8168_mdio_write(tp, 0x19, 0x7c40); + rtl8168_mdio_write(tp, 0x15, 0x0157); + rtl8168_mdio_write(tp, 0x19, 0x6421); + rtl8168_mdio_write(tp, 0x15, 0x0158); + rtl8168_mdio_write(tp, 0x19, 0x7c80); + rtl8168_mdio_write(tp, 0x15, 0x0159); + rtl8168_mdio_write(tp, 0x19, 0x64a1); + rtl8168_mdio_write(tp, 0x15, 0x015a); + rtl8168_mdio_write(tp, 0x19, 0x30fe); + rtl8168_mdio_write(tp, 0x15, 0x029c); + rtl8168_mdio_write(tp, 0x19, 0x0070); + rtl8168_mdio_write(tp, 0x15, 0x02b2); + rtl8168_mdio_write(tp, 0x19, 0x005a); + rtl8168_mdio_write(tp, 0x15, 0x02bd); + rtl8168_mdio_write(tp, 0x19, 0xa522); + rtl8168_mdio_write(tp, 0x15, 0x02ce); + rtl8168_mdio_write(tp, 0x19, 0xb63e); + rtl8168_mdio_write(tp, 0x15, 0x02d9); + rtl8168_mdio_write(tp, 0x19, 0x32df); + rtl8168_mdio_write(tp, 0x15, 0x02df); + rtl8168_mdio_write(tp, 0x19, 0x4500); + rtl8168_mdio_write(tp, 0x15, 0x02e7); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02f4); + rtl8168_mdio_write(tp, 0x19, 0xb618); + rtl8168_mdio_write(tp, 0x15, 0x02fb); + rtl8168_mdio_write(tp, 0x19, 0xb900); + rtl8168_mdio_write(tp, 0x15, 0x02fc); + rtl8168_mdio_write(tp, 0x19, 0x49b5); + rtl8168_mdio_write(tp, 0x15, 0x02fd); + rtl8168_mdio_write(tp, 0x19, 0x6812); + rtl8168_mdio_write(tp, 0x15, 0x02fe); + rtl8168_mdio_write(tp, 0x19, 0x66a0); + rtl8168_mdio_write(tp, 0x15, 0x02ff); + rtl8168_mdio_write(tp, 0x19, 0x9900); + rtl8168_mdio_write(tp, 0x15, 0x0300); + rtl8168_mdio_write(tp, 0x19, 0x64a0); + rtl8168_mdio_write(tp, 0x15, 0x0301); + rtl8168_mdio_write(tp, 0x19, 0x3316); + rtl8168_mdio_write(tp, 0x15, 0x0308); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x030c); + rtl8168_mdio_write(tp, 0x19, 0x3000); + rtl8168_mdio_write(tp, 0x15, 0x0312); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0313); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0314); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0315); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0316); + rtl8168_mdio_write(tp, 0x19, 0x49b5); + rtl8168_mdio_write(tp, 0x15, 0x0317); + rtl8168_mdio_write(tp, 0x19, 0x7d00); + rtl8168_mdio_write(tp, 0x15, 0x0318); + rtl8168_mdio_write(tp, 0x19, 0x4d00); + rtl8168_mdio_write(tp, 0x15, 0x0319); + rtl8168_mdio_write(tp, 0x19, 0x6810); + rtl8168_mdio_write(tp, 0x15, 0x031a); + rtl8168_mdio_write(tp, 0x19, 0x6c08); + rtl8168_mdio_write(tp, 0x15, 0x031b); + rtl8168_mdio_write(tp, 0x19, 0x4925); + rtl8168_mdio_write(tp, 0x15, 0x031c); + rtl8168_mdio_write(tp, 0x19, 0x403b); + rtl8168_mdio_write(tp, 0x15, 0x031d); + rtl8168_mdio_write(tp, 0x19, 0xa602); + rtl8168_mdio_write(tp, 0x15, 0x031e); + rtl8168_mdio_write(tp, 0x19, 0x402f); + rtl8168_mdio_write(tp, 0x15, 0x031f); + rtl8168_mdio_write(tp, 0x19, 0x4484); + rtl8168_mdio_write(tp, 0x15, 0x0320); + rtl8168_mdio_write(tp, 0x19, 0x40c8); + rtl8168_mdio_write(tp, 0x15, 0x0321); + rtl8168_mdio_write(tp, 0x19, 0x44c4); + rtl8168_mdio_write(tp, 0x15, 0x0322); + rtl8168_mdio_write(tp, 0x19, 0x404f); + rtl8168_mdio_write(tp, 0x15, 0x0323); + rtl8168_mdio_write(tp, 0x19, 0x44c8); + rtl8168_mdio_write(tp, 0x15, 0x0324); + rtl8168_mdio_write(tp, 0x19, 0xd64f); + rtl8168_mdio_write(tp, 0x15, 0x0325); + rtl8168_mdio_write(tp, 0x19, 0x00e7); + rtl8168_mdio_write(tp, 0x15, 0x0326); + rtl8168_mdio_write(tp, 0x19, 0x7c08); + rtl8168_mdio_write(tp, 0x15, 0x0327); + rtl8168_mdio_write(tp, 0x19, 0x8203); + rtl8168_mdio_write(tp, 0x15, 0x0328); + rtl8168_mdio_write(tp, 0x19, 0x4d48); + rtl8168_mdio_write(tp, 0x15, 0x0329); + rtl8168_mdio_write(tp, 0x19, 0x332b); + rtl8168_mdio_write(tp, 0x15, 0x032a); + rtl8168_mdio_write(tp, 0x19, 0x4d40); + rtl8168_mdio_write(tp, 0x15, 0x032c); + rtl8168_mdio_write(tp, 0x19, 0x00f8); + rtl8168_mdio_write(tp, 0x15, 0x032d); + rtl8168_mdio_write(tp, 0x19, 0x82b2); + rtl8168_mdio_write(tp, 0x15, 0x032f); + rtl8168_mdio_write(tp, 0x19, 0x00b0); + rtl8168_mdio_write(tp, 0x15, 0x0332); + rtl8168_mdio_write(tp, 0x19, 0x91f2); + rtl8168_mdio_write(tp, 0x15, 0x033f); + rtl8168_mdio_write(tp, 0x19, 0xb6cd); + rtl8168_mdio_write(tp, 0x15, 0x0340); + rtl8168_mdio_write(tp, 0x19, 0x9e01); + rtl8168_mdio_write(tp, 0x15, 0x0341); + rtl8168_mdio_write(tp, 0x19, 0xd11d); + rtl8168_mdio_write(tp, 0x15, 0x0342); + rtl8168_mdio_write(tp, 0x19, 0x009d); + rtl8168_mdio_write(tp, 0x15, 0x0343); + rtl8168_mdio_write(tp, 0x19, 0xbb1c); + rtl8168_mdio_write(tp, 0x15, 0x0344); + rtl8168_mdio_write(tp, 0x19, 0x8102); + rtl8168_mdio_write(tp, 0x15, 0x0345); + rtl8168_mdio_write(tp, 0x19, 0x3348); + rtl8168_mdio_write(tp, 0x15, 0x0346); + rtl8168_mdio_write(tp, 0x19, 0xa231); + rtl8168_mdio_write(tp, 0x15, 0x0347); + rtl8168_mdio_write(tp, 0x19, 0x335b); + rtl8168_mdio_write(tp, 0x15, 0x0348); + rtl8168_mdio_write(tp, 0x19, 0x91f7); + rtl8168_mdio_write(tp, 0x15, 0x0349); + rtl8168_mdio_write(tp, 0x19, 0xc218); + rtl8168_mdio_write(tp, 0x15, 0x034a); + rtl8168_mdio_write(tp, 0x19, 0x00f5); + rtl8168_mdio_write(tp, 0x15, 0x034b); + rtl8168_mdio_write(tp, 0x19, 0x335b); + rtl8168_mdio_write(tp, 0x15, 0x034c); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x034d); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x034e); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x034f); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0350); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x035b); + rtl8168_mdio_write(tp, 0x19, 0xa23c); + rtl8168_mdio_write(tp, 0x15, 0x035c); + rtl8168_mdio_write(tp, 0x19, 0x7c08); + rtl8168_mdio_write(tp, 0x15, 0x035d); + rtl8168_mdio_write(tp, 0x19, 0x4c00); + rtl8168_mdio_write(tp, 0x15, 0x035e); + rtl8168_mdio_write(tp, 0x19, 0x3397); + rtl8168_mdio_write(tp, 0x15, 0x0363); + rtl8168_mdio_write(tp, 0x19, 0xb6a9); + rtl8168_mdio_write(tp, 0x15, 0x0366); + rtl8168_mdio_write(tp, 0x19, 0x00f5); + rtl8168_mdio_write(tp, 0x15, 0x0382); + rtl8168_mdio_write(tp, 0x19, 0x7c40); + rtl8168_mdio_write(tp, 0x15, 0x0388); + rtl8168_mdio_write(tp, 0x19, 0x0084); + rtl8168_mdio_write(tp, 0x15, 0x0389); + rtl8168_mdio_write(tp, 0x19, 0xdd17); + rtl8168_mdio_write(tp, 0x15, 0x038a); + rtl8168_mdio_write(tp, 0x19, 0x000b); + rtl8168_mdio_write(tp, 0x15, 0x038b); + rtl8168_mdio_write(tp, 0x19, 0xa10a); + rtl8168_mdio_write(tp, 0x15, 0x038c); + rtl8168_mdio_write(tp, 0x19, 0x337e); + rtl8168_mdio_write(tp, 0x15, 0x038d); + rtl8168_mdio_write(tp, 0x19, 0x6c0b); + rtl8168_mdio_write(tp, 0x15, 0x038e); + rtl8168_mdio_write(tp, 0x19, 0xa107); + rtl8168_mdio_write(tp, 0x15, 0x038f); + rtl8168_mdio_write(tp, 0x19, 0x6c08); + rtl8168_mdio_write(tp, 0x15, 0x0390); + rtl8168_mdio_write(tp, 0x19, 0xc017); + rtl8168_mdio_write(tp, 0x15, 0x0391); + rtl8168_mdio_write(tp, 0x19, 0x0004); + rtl8168_mdio_write(tp, 0x15, 0x0392); + rtl8168_mdio_write(tp, 0x19, 0xd64f); + rtl8168_mdio_write(tp, 0x15, 0x0393); + rtl8168_mdio_write(tp, 0x19, 0x00f4); + rtl8168_mdio_write(tp, 0x15, 0x0397); + rtl8168_mdio_write(tp, 0x19, 0x4098); + rtl8168_mdio_write(tp, 0x15, 0x0398); + rtl8168_mdio_write(tp, 0x19, 0x4408); + rtl8168_mdio_write(tp, 0x15, 0x0399); + rtl8168_mdio_write(tp, 0x19, 0x55bf); + rtl8168_mdio_write(tp, 0x15, 0x039a); + rtl8168_mdio_write(tp, 0x19, 0x4bb9); + rtl8168_mdio_write(tp, 0x15, 0x039b); + rtl8168_mdio_write(tp, 0x19, 0x6810); + rtl8168_mdio_write(tp, 0x15, 0x039c); + rtl8168_mdio_write(tp, 0x19, 0x4b29); + rtl8168_mdio_write(tp, 0x15, 0x039d); + rtl8168_mdio_write(tp, 0x19, 0x4041); + rtl8168_mdio_write(tp, 0x15, 0x039e); + rtl8168_mdio_write(tp, 0x19, 0x442a); + rtl8168_mdio_write(tp, 0x15, 0x039f); + rtl8168_mdio_write(tp, 0x19, 0x4029); + rtl8168_mdio_write(tp, 0x15, 0x03aa); + rtl8168_mdio_write(tp, 0x19, 0x33b8); + rtl8168_mdio_write(tp, 0x15, 0x03b6); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03b7); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03b8); + rtl8168_mdio_write(tp, 0x19, 0x543f); + rtl8168_mdio_write(tp, 0x15, 0x03b9); + rtl8168_mdio_write(tp, 0x19, 0x499a); + rtl8168_mdio_write(tp, 0x15, 0x03ba); + rtl8168_mdio_write(tp, 0x19, 0x7c40); + rtl8168_mdio_write(tp, 0x15, 0x03bb); + rtl8168_mdio_write(tp, 0x19, 0x4c40); + rtl8168_mdio_write(tp, 0x15, 0x03bc); + rtl8168_mdio_write(tp, 0x19, 0x490a); + rtl8168_mdio_write(tp, 0x15, 0x03bd); + rtl8168_mdio_write(tp, 0x19, 0x405e); + rtl8168_mdio_write(tp, 0x15, 0x03c2); + rtl8168_mdio_write(tp, 0x19, 0x9a03); + rtl8168_mdio_write(tp, 0x15, 0x03c4); + rtl8168_mdio_write(tp, 0x19, 0x0015); + rtl8168_mdio_write(tp, 0x15, 0x03c5); + rtl8168_mdio_write(tp, 0x19, 0x9e03); + rtl8168_mdio_write(tp, 0x15, 0x03c8); + rtl8168_mdio_write(tp, 0x19, 0x9cf7); + rtl8168_mdio_write(tp, 0x15, 0x03c9); + rtl8168_mdio_write(tp, 0x19, 0x7c12); + rtl8168_mdio_write(tp, 0x15, 0x03ca); + rtl8168_mdio_write(tp, 0x19, 0x4c52); + rtl8168_mdio_write(tp, 0x15, 0x03cb); + rtl8168_mdio_write(tp, 0x19, 0x4458); + rtl8168_mdio_write(tp, 0x15, 0x03cd); + rtl8168_mdio_write(tp, 0x19, 0x4c40); + rtl8168_mdio_write(tp, 0x15, 0x03ce); + rtl8168_mdio_write(tp, 0x19, 0x33bf); + rtl8168_mdio_write(tp, 0x15, 0x03cf); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03d0); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03d1); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03d5); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03d6); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03d7); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03d8); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03d9); + rtl8168_mdio_write(tp, 0x19, 0x49bb); + rtl8168_mdio_write(tp, 0x15, 0x03da); + rtl8168_mdio_write(tp, 0x19, 0x4478); + rtl8168_mdio_write(tp, 0x15, 0x03db); + rtl8168_mdio_write(tp, 0x19, 0x492b); + rtl8168_mdio_write(tp, 0x15, 0x03dc); + rtl8168_mdio_write(tp, 0x19, 0x7c01); + rtl8168_mdio_write(tp, 0x15, 0x03dd); + rtl8168_mdio_write(tp, 0x19, 0x4c00); + rtl8168_mdio_write(tp, 0x15, 0x03de); + rtl8168_mdio_write(tp, 0x19, 0xbd1a); + rtl8168_mdio_write(tp, 0x15, 0x03df); + rtl8168_mdio_write(tp, 0x19, 0xc428); + rtl8168_mdio_write(tp, 0x15, 0x03e0); + rtl8168_mdio_write(tp, 0x19, 0x0008); + rtl8168_mdio_write(tp, 0x15, 0x03e1); + rtl8168_mdio_write(tp, 0x19, 0x9cfd); + rtl8168_mdio_write(tp, 0x15, 0x03e2); + rtl8168_mdio_write(tp, 0x19, 0x7c12); + rtl8168_mdio_write(tp, 0x15, 0x03e3); + rtl8168_mdio_write(tp, 0x19, 0x4c52); + rtl8168_mdio_write(tp, 0x15, 0x03e4); + rtl8168_mdio_write(tp, 0x19, 0x4458); + rtl8168_mdio_write(tp, 0x15, 0x03e5); + rtl8168_mdio_write(tp, 0x19, 0x7c12); + rtl8168_mdio_write(tp, 0x15, 0x03e6); + rtl8168_mdio_write(tp, 0x19, 0x4c40); + rtl8168_mdio_write(tp, 0x15, 0x03e7); + rtl8168_mdio_write(tp, 0x19, 0x33de); + rtl8168_mdio_write(tp, 0x15, 0x03e8); + rtl8168_mdio_write(tp, 0x19, 0xc218); + rtl8168_mdio_write(tp, 0x15, 0x03e9); + rtl8168_mdio_write(tp, 0x19, 0x0002); + rtl8168_mdio_write(tp, 0x15, 0x03ea); + rtl8168_mdio_write(tp, 0x19, 0x32df); + rtl8168_mdio_write(tp, 0x15, 0x03eb); + rtl8168_mdio_write(tp, 0x19, 0x3316); + rtl8168_mdio_write(tp, 0x15, 0x03ec); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03ed); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03ee); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03ef); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03f7); + rtl8168_mdio_write(tp, 0x19, 0x330c); + rtl8168_mdio_write(tp, 0x16, 0x0306); + rtl8168_mdio_write(tp, 0x16, 0x0300); + + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x0080); + rtl8168_mdio_write(tp, 0x05, 0x8000); + rtl8168_mdio_write(tp, 0x06, 0x0280); + rtl8168_mdio_write(tp, 0x06, 0x48f7); + rtl8168_mdio_write(tp, 0x06, 0x00e0); + rtl8168_mdio_write(tp, 0x06, 0xfff7); + rtl8168_mdio_write(tp, 0x06, 0xa080); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0xf602); + rtl8168_mdio_write(tp, 0x06, 0x0200); + rtl8168_mdio_write(tp, 0x06, 0x0280); + rtl8168_mdio_write(tp, 0x06, 0x9002); + rtl8168_mdio_write(tp, 0x06, 0x0224); + rtl8168_mdio_write(tp, 0x06, 0x0202); + rtl8168_mdio_write(tp, 0x06, 0x3402); + rtl8168_mdio_write(tp, 0x06, 0x027f); + rtl8168_mdio_write(tp, 0x06, 0x0280); + rtl8168_mdio_write(tp, 0x06, 0xa602); + rtl8168_mdio_write(tp, 0x06, 0x80bf); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x88e1); + rtl8168_mdio_write(tp, 0x06, 0x8b89); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8a1e); + rtl8168_mdio_write(tp, 0x06, 0x01e1); + rtl8168_mdio_write(tp, 0x06, 0x8b8b); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8c1e); + rtl8168_mdio_write(tp, 0x06, 0x01e1); + rtl8168_mdio_write(tp, 0x06, 0x8b8d); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8e1e); + rtl8168_mdio_write(tp, 0x06, 0x01a0); + rtl8168_mdio_write(tp, 0x06, 0x00c7); + rtl8168_mdio_write(tp, 0x06, 0xaebb); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0xe600); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0xee03); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0xefb8); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0xe902); + rtl8168_mdio_write(tp, 0x06, 0xee8b); + rtl8168_mdio_write(tp, 0x06, 0x8285); + rtl8168_mdio_write(tp, 0x06, 0xee8b); + rtl8168_mdio_write(tp, 0x06, 0x8520); + rtl8168_mdio_write(tp, 0x06, 0xee8b); + rtl8168_mdio_write(tp, 0x06, 0x8701); + rtl8168_mdio_write(tp, 0x06, 0xd481); + rtl8168_mdio_write(tp, 0x06, 0x35e4); + rtl8168_mdio_write(tp, 0x06, 0x8b94); + rtl8168_mdio_write(tp, 0x06, 0xe58b); + rtl8168_mdio_write(tp, 0x06, 0x95bf); + rtl8168_mdio_write(tp, 0x06, 0x8b88); + rtl8168_mdio_write(tp, 0x06, 0xec00); + rtl8168_mdio_write(tp, 0x06, 0x19a9); + rtl8168_mdio_write(tp, 0x06, 0x8b90); + rtl8168_mdio_write(tp, 0x06, 0xf9ee); + rtl8168_mdio_write(tp, 0x06, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0xfff7); + rtl8168_mdio_write(tp, 0x06, 0xffe0); + rtl8168_mdio_write(tp, 0x06, 0xe140); + rtl8168_mdio_write(tp, 0x06, 0xe1e1); + rtl8168_mdio_write(tp, 0x06, 0x41f7); + rtl8168_mdio_write(tp, 0x06, 0x2ff6); + rtl8168_mdio_write(tp, 0x06, 0x28e4); + rtl8168_mdio_write(tp, 0x06, 0xe140); + rtl8168_mdio_write(tp, 0x06, 0xe5e1); + rtl8168_mdio_write(tp, 0x06, 0x4104); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b89); + rtl8168_mdio_write(tp, 0x06, 0xad20); + rtl8168_mdio_write(tp, 0x06, 0x0dee); + rtl8168_mdio_write(tp, 0x06, 0x8b89); + rtl8168_mdio_write(tp, 0x06, 0x0002); + rtl8168_mdio_write(tp, 0x06, 0x82f4); + rtl8168_mdio_write(tp, 0x06, 0x021f); + rtl8168_mdio_write(tp, 0x06, 0x4102); + rtl8168_mdio_write(tp, 0x06, 0x2812); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b8d); + rtl8168_mdio_write(tp, 0x06, 0xad20); + rtl8168_mdio_write(tp, 0x06, 0x10ee); + rtl8168_mdio_write(tp, 0x06, 0x8b8d); + rtl8168_mdio_write(tp, 0x06, 0x0002); + rtl8168_mdio_write(tp, 0x06, 0x139d); + rtl8168_mdio_write(tp, 0x06, 0x0281); + rtl8168_mdio_write(tp, 0x06, 0xd602); + rtl8168_mdio_write(tp, 0x06, 0x1f99); + rtl8168_mdio_write(tp, 0x06, 0x0227); + rtl8168_mdio_write(tp, 0x06, 0xeafc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x2014); + rtl8168_mdio_write(tp, 0x06, 0xf620); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8e02); + rtl8168_mdio_write(tp, 0x06, 0x8104); + rtl8168_mdio_write(tp, 0x06, 0x021b); + rtl8168_mdio_write(tp, 0x06, 0xf402); + rtl8168_mdio_write(tp, 0x06, 0x2c9c); + rtl8168_mdio_write(tp, 0x06, 0x0281); + rtl8168_mdio_write(tp, 0x06, 0x7902); + rtl8168_mdio_write(tp, 0x06, 0x8443); + rtl8168_mdio_write(tp, 0x06, 0xad22); + rtl8168_mdio_write(tp, 0x06, 0x11f6); + rtl8168_mdio_write(tp, 0x06, 0x22e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0x022c); + rtl8168_mdio_write(tp, 0x06, 0x4602); + rtl8168_mdio_write(tp, 0x06, 0x2ac5); + rtl8168_mdio_write(tp, 0x06, 0x0229); + rtl8168_mdio_write(tp, 0x06, 0x2002); + rtl8168_mdio_write(tp, 0x06, 0x2b91); + rtl8168_mdio_write(tp, 0x06, 0xad25); + rtl8168_mdio_write(tp, 0x06, 0x11f6); + rtl8168_mdio_write(tp, 0x06, 0x25e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0x0284); + rtl8168_mdio_write(tp, 0x06, 0xe202); + rtl8168_mdio_write(tp, 0x06, 0x043a); + rtl8168_mdio_write(tp, 0x06, 0x021a); + rtl8168_mdio_write(tp, 0x06, 0x5902); + rtl8168_mdio_write(tp, 0x06, 0x2bfc); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x00e1); + rtl8168_mdio_write(tp, 0x06, 0xe001); + rtl8168_mdio_write(tp, 0x06, 0xad27); + rtl8168_mdio_write(tp, 0x06, 0x1fd1); + rtl8168_mdio_write(tp, 0x06, 0x01bf); + rtl8168_mdio_write(tp, 0x06, 0x8638); + rtl8168_mdio_write(tp, 0x06, 0x022f); + rtl8168_mdio_write(tp, 0x06, 0x50e0); + rtl8168_mdio_write(tp, 0x06, 0xe020); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x21ad); + rtl8168_mdio_write(tp, 0x06, 0x200e); + rtl8168_mdio_write(tp, 0x06, 0xd100); + rtl8168_mdio_write(tp, 0x06, 0xbf86); + rtl8168_mdio_write(tp, 0x06, 0x3802); + rtl8168_mdio_write(tp, 0x06, 0x2f50); + rtl8168_mdio_write(tp, 0x06, 0xbf3d); + rtl8168_mdio_write(tp, 0x06, 0x3902); + rtl8168_mdio_write(tp, 0x06, 0x2eb0); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfefc); + rtl8168_mdio_write(tp, 0x06, 0x0402); + rtl8168_mdio_write(tp, 0x06, 0x8591); + rtl8168_mdio_write(tp, 0x06, 0x0281); + rtl8168_mdio_write(tp, 0x06, 0x3c05); + rtl8168_mdio_write(tp, 0x06, 0xf8fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xe0e2); + rtl8168_mdio_write(tp, 0x06, 0xfee1); + rtl8168_mdio_write(tp, 0x06, 0xe2ff); + rtl8168_mdio_write(tp, 0x06, 0xad2d); + rtl8168_mdio_write(tp, 0x06, 0x1ae0); + rtl8168_mdio_write(tp, 0x06, 0xe14e); + rtl8168_mdio_write(tp, 0x06, 0xe1e1); + rtl8168_mdio_write(tp, 0x06, 0x4fac); + rtl8168_mdio_write(tp, 0x06, 0x2d22); + rtl8168_mdio_write(tp, 0x06, 0xf603); + rtl8168_mdio_write(tp, 0x06, 0x0203); + rtl8168_mdio_write(tp, 0x06, 0x36f7); + rtl8168_mdio_write(tp, 0x06, 0x03f7); + rtl8168_mdio_write(tp, 0x06, 0x06bf); + rtl8168_mdio_write(tp, 0x06, 0x8622); + rtl8168_mdio_write(tp, 0x06, 0x022e); + rtl8168_mdio_write(tp, 0x06, 0xb0ae); + rtl8168_mdio_write(tp, 0x06, 0x11e0); + rtl8168_mdio_write(tp, 0x06, 0xe14e); + rtl8168_mdio_write(tp, 0x06, 0xe1e1); + rtl8168_mdio_write(tp, 0x06, 0x4fad); + rtl8168_mdio_write(tp, 0x06, 0x2d08); + rtl8168_mdio_write(tp, 0x06, 0xbf86); + rtl8168_mdio_write(tp, 0x06, 0x2d02); + rtl8168_mdio_write(tp, 0x06, 0x2eb0); + rtl8168_mdio_write(tp, 0x06, 0xf606); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfefc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xf9fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x87ad); + rtl8168_mdio_write(tp, 0x06, 0x204c); + rtl8168_mdio_write(tp, 0x06, 0xd200); + rtl8168_mdio_write(tp, 0x06, 0xe0e2); + rtl8168_mdio_write(tp, 0x06, 0x0058); + rtl8168_mdio_write(tp, 0x06, 0x010c); + rtl8168_mdio_write(tp, 0x06, 0x021e); + rtl8168_mdio_write(tp, 0x06, 0x20e0); + rtl8168_mdio_write(tp, 0x06, 0xe000); + rtl8168_mdio_write(tp, 0x06, 0x5810); + rtl8168_mdio_write(tp, 0x06, 0x1e20); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x3658); + rtl8168_mdio_write(tp, 0x06, 0x031e); + rtl8168_mdio_write(tp, 0x06, 0x20e0); + rtl8168_mdio_write(tp, 0x06, 0xe022); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x2358); + rtl8168_mdio_write(tp, 0x06, 0xe01e); + rtl8168_mdio_write(tp, 0x06, 0x20e0); + rtl8168_mdio_write(tp, 0x06, 0x8ae6); + rtl8168_mdio_write(tp, 0x06, 0x1f02); + rtl8168_mdio_write(tp, 0x06, 0x9e22); + rtl8168_mdio_write(tp, 0x06, 0xe68a); + rtl8168_mdio_write(tp, 0x06, 0xe6ad); + rtl8168_mdio_write(tp, 0x06, 0x3214); + rtl8168_mdio_write(tp, 0x06, 0xad34); + rtl8168_mdio_write(tp, 0x06, 0x11ef); + rtl8168_mdio_write(tp, 0x06, 0x0258); + rtl8168_mdio_write(tp, 0x06, 0x039e); + rtl8168_mdio_write(tp, 0x06, 0x07ad); + rtl8168_mdio_write(tp, 0x06, 0x3508); + rtl8168_mdio_write(tp, 0x06, 0x5ac0); + rtl8168_mdio_write(tp, 0x06, 0x9f04); + rtl8168_mdio_write(tp, 0x06, 0xd101); + rtl8168_mdio_write(tp, 0x06, 0xae02); + rtl8168_mdio_write(tp, 0x06, 0xd100); + rtl8168_mdio_write(tp, 0x06, 0xbf86); + rtl8168_mdio_write(tp, 0x06, 0x3e02); + rtl8168_mdio_write(tp, 0x06, 0x2f50); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfefd); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8f9); + rtl8168_mdio_write(tp, 0x06, 0xfae0); + rtl8168_mdio_write(tp, 0x06, 0x8b81); + rtl8168_mdio_write(tp, 0x06, 0xac26); + rtl8168_mdio_write(tp, 0x06, 0x0ee0); + rtl8168_mdio_write(tp, 0x06, 0x8b81); + rtl8168_mdio_write(tp, 0x06, 0xac21); + rtl8168_mdio_write(tp, 0x06, 0x08e0); + rtl8168_mdio_write(tp, 0x06, 0x8b87); + rtl8168_mdio_write(tp, 0x06, 0xac24); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0x6bee); + rtl8168_mdio_write(tp, 0x06, 0xe0ea); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0xe0eb); + rtl8168_mdio_write(tp, 0x06, 0x00e2); + rtl8168_mdio_write(tp, 0x06, 0xe07c); + rtl8168_mdio_write(tp, 0x06, 0xe3e0); + rtl8168_mdio_write(tp, 0x06, 0x7da5); + rtl8168_mdio_write(tp, 0x06, 0x1111); + rtl8168_mdio_write(tp, 0x06, 0x15d2); + rtl8168_mdio_write(tp, 0x06, 0x60d6); + rtl8168_mdio_write(tp, 0x06, 0x6666); + rtl8168_mdio_write(tp, 0x06, 0x0207); + rtl8168_mdio_write(tp, 0x06, 0xf9d2); + rtl8168_mdio_write(tp, 0x06, 0xa0d6); + rtl8168_mdio_write(tp, 0x06, 0xaaaa); + rtl8168_mdio_write(tp, 0x06, 0x0207); + rtl8168_mdio_write(tp, 0x06, 0xf902); + rtl8168_mdio_write(tp, 0x06, 0x825c); + rtl8168_mdio_write(tp, 0x06, 0xae44); + rtl8168_mdio_write(tp, 0x06, 0xa566); + rtl8168_mdio_write(tp, 0x06, 0x6602); + rtl8168_mdio_write(tp, 0x06, 0xae38); + rtl8168_mdio_write(tp, 0x06, 0xa5aa); + rtl8168_mdio_write(tp, 0x06, 0xaa02); + rtl8168_mdio_write(tp, 0x06, 0xae32); + rtl8168_mdio_write(tp, 0x06, 0xeee0); + rtl8168_mdio_write(tp, 0x06, 0xea04); + rtl8168_mdio_write(tp, 0x06, 0xeee0); + rtl8168_mdio_write(tp, 0x06, 0xeb06); + rtl8168_mdio_write(tp, 0x06, 0xe2e0); + rtl8168_mdio_write(tp, 0x06, 0x7ce3); + rtl8168_mdio_write(tp, 0x06, 0xe07d); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x38e1); + rtl8168_mdio_write(tp, 0x06, 0xe039); + rtl8168_mdio_write(tp, 0x06, 0xad2e); + rtl8168_mdio_write(tp, 0x06, 0x21ad); + rtl8168_mdio_write(tp, 0x06, 0x3f13); + rtl8168_mdio_write(tp, 0x06, 0xe0e4); + rtl8168_mdio_write(tp, 0x06, 0x14e1); + rtl8168_mdio_write(tp, 0x06, 0xe415); + rtl8168_mdio_write(tp, 0x06, 0x6880); + rtl8168_mdio_write(tp, 0x06, 0xe4e4); + rtl8168_mdio_write(tp, 0x06, 0x14e5); + rtl8168_mdio_write(tp, 0x06, 0xe415); + rtl8168_mdio_write(tp, 0x06, 0x0282); + rtl8168_mdio_write(tp, 0x06, 0x5cae); + rtl8168_mdio_write(tp, 0x06, 0x0bac); + rtl8168_mdio_write(tp, 0x06, 0x3e02); + rtl8168_mdio_write(tp, 0x06, 0xae06); + rtl8168_mdio_write(tp, 0x06, 0x0282); + rtl8168_mdio_write(tp, 0x06, 0x8602); + rtl8168_mdio_write(tp, 0x06, 0x82b0); + rtl8168_mdio_write(tp, 0x06, 0xfefd); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e1); + rtl8168_mdio_write(tp, 0x06, 0x8b2e); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x81ad); + rtl8168_mdio_write(tp, 0x06, 0x2605); + rtl8168_mdio_write(tp, 0x06, 0x0221); + rtl8168_mdio_write(tp, 0x06, 0xf3f7); + rtl8168_mdio_write(tp, 0x06, 0x28e0); + rtl8168_mdio_write(tp, 0x06, 0x8b81); + rtl8168_mdio_write(tp, 0x06, 0xad21); + rtl8168_mdio_write(tp, 0x06, 0x0502); + rtl8168_mdio_write(tp, 0x06, 0x22f8); + rtl8168_mdio_write(tp, 0x06, 0xf729); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x87ad); + rtl8168_mdio_write(tp, 0x06, 0x2405); + rtl8168_mdio_write(tp, 0x06, 0x0282); + rtl8168_mdio_write(tp, 0x06, 0xebf7); + rtl8168_mdio_write(tp, 0x06, 0x2ae5); + rtl8168_mdio_write(tp, 0x06, 0x8b2e); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b81); + rtl8168_mdio_write(tp, 0x06, 0xad26); + rtl8168_mdio_write(tp, 0x06, 0x0302); + rtl8168_mdio_write(tp, 0x06, 0x2134); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x81ad); + rtl8168_mdio_write(tp, 0x06, 0x2109); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x2eac); + rtl8168_mdio_write(tp, 0x06, 0x2003); + rtl8168_mdio_write(tp, 0x06, 0x0283); + rtl8168_mdio_write(tp, 0x06, 0x52e0); + rtl8168_mdio_write(tp, 0x06, 0x8b87); + rtl8168_mdio_write(tp, 0x06, 0xad24); + rtl8168_mdio_write(tp, 0x06, 0x09e0); + rtl8168_mdio_write(tp, 0x06, 0x8b2e); + rtl8168_mdio_write(tp, 0x06, 0xac21); + rtl8168_mdio_write(tp, 0x06, 0x0302); + rtl8168_mdio_write(tp, 0x06, 0x8337); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e1); + rtl8168_mdio_write(tp, 0x06, 0x8b2e); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x81ad); + rtl8168_mdio_write(tp, 0x06, 0x2608); + rtl8168_mdio_write(tp, 0x06, 0xe085); + rtl8168_mdio_write(tp, 0x06, 0xd2ad); + rtl8168_mdio_write(tp, 0x06, 0x2502); + rtl8168_mdio_write(tp, 0x06, 0xf628); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x81ad); + rtl8168_mdio_write(tp, 0x06, 0x210a); + rtl8168_mdio_write(tp, 0x06, 0xe086); + rtl8168_mdio_write(tp, 0x06, 0x0af6); + rtl8168_mdio_write(tp, 0x06, 0x27a0); + rtl8168_mdio_write(tp, 0x06, 0x0502); + rtl8168_mdio_write(tp, 0x06, 0xf629); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x87ad); + rtl8168_mdio_write(tp, 0x06, 0x2408); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0xedad); + rtl8168_mdio_write(tp, 0x06, 0x2002); + rtl8168_mdio_write(tp, 0x06, 0xf62a); + rtl8168_mdio_write(tp, 0x06, 0xe58b); + rtl8168_mdio_write(tp, 0x06, 0x2ea1); + rtl8168_mdio_write(tp, 0x06, 0x0003); + rtl8168_mdio_write(tp, 0x06, 0x0221); + rtl8168_mdio_write(tp, 0x06, 0x11fc); + rtl8168_mdio_write(tp, 0x06, 0x04ee); + rtl8168_mdio_write(tp, 0x06, 0x8aed); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0x8aec); + rtl8168_mdio_write(tp, 0x06, 0x0004); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b87); + rtl8168_mdio_write(tp, 0x06, 0xad24); + rtl8168_mdio_write(tp, 0x06, 0x3ae0); + rtl8168_mdio_write(tp, 0x06, 0xe0ea); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0xeb58); + rtl8168_mdio_write(tp, 0x06, 0xf8d1); + rtl8168_mdio_write(tp, 0x06, 0x01e4); + rtl8168_mdio_write(tp, 0x06, 0xe0ea); + rtl8168_mdio_write(tp, 0x06, 0xe5e0); + rtl8168_mdio_write(tp, 0x06, 0xebe0); + rtl8168_mdio_write(tp, 0x06, 0xe07c); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x7d5c); + rtl8168_mdio_write(tp, 0x06, 0x00ff); + rtl8168_mdio_write(tp, 0x06, 0x3c00); + rtl8168_mdio_write(tp, 0x06, 0x1eab); + rtl8168_mdio_write(tp, 0x06, 0x1ce0); + rtl8168_mdio_write(tp, 0x06, 0xe04c); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x4d58); + rtl8168_mdio_write(tp, 0x06, 0xc1e4); + rtl8168_mdio_write(tp, 0x06, 0xe04c); + rtl8168_mdio_write(tp, 0x06, 0xe5e0); + rtl8168_mdio_write(tp, 0x06, 0x4de0); + rtl8168_mdio_write(tp, 0x06, 0xe0ee); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0x3ce4); + rtl8168_mdio_write(tp, 0x06, 0xe0ee); + rtl8168_mdio_write(tp, 0x06, 0xe5e0); + rtl8168_mdio_write(tp, 0x06, 0xeffc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x87ad); + rtl8168_mdio_write(tp, 0x06, 0x2412); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0xeee1); + rtl8168_mdio_write(tp, 0x06, 0xe0ef); + rtl8168_mdio_write(tp, 0x06, 0x59c3); + rtl8168_mdio_write(tp, 0x06, 0xe4e0); + rtl8168_mdio_write(tp, 0x06, 0xeee5); + rtl8168_mdio_write(tp, 0x06, 0xe0ef); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0xed01); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b81); + rtl8168_mdio_write(tp, 0x06, 0xac25); + rtl8168_mdio_write(tp, 0x06, 0x0502); + rtl8168_mdio_write(tp, 0x06, 0x8363); + rtl8168_mdio_write(tp, 0x06, 0xae03); + rtl8168_mdio_write(tp, 0x06, 0x0225); + rtl8168_mdio_write(tp, 0x06, 0x16fc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xf9fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xfae0); + rtl8168_mdio_write(tp, 0x06, 0x860a); + rtl8168_mdio_write(tp, 0x06, 0xa000); + rtl8168_mdio_write(tp, 0x06, 0x19e0); + rtl8168_mdio_write(tp, 0x06, 0x860b); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x331b); + rtl8168_mdio_write(tp, 0x06, 0x109e); + rtl8168_mdio_write(tp, 0x06, 0x04aa); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0x06ee); + rtl8168_mdio_write(tp, 0x06, 0x860a); + rtl8168_mdio_write(tp, 0x06, 0x01ae); + rtl8168_mdio_write(tp, 0x06, 0xe602); + rtl8168_mdio_write(tp, 0x06, 0x241e); + rtl8168_mdio_write(tp, 0x06, 0xae14); + rtl8168_mdio_write(tp, 0x06, 0xa001); + rtl8168_mdio_write(tp, 0x06, 0x1402); + rtl8168_mdio_write(tp, 0x06, 0x2426); + rtl8168_mdio_write(tp, 0x06, 0xbf26); + rtl8168_mdio_write(tp, 0x06, 0x6d02); + rtl8168_mdio_write(tp, 0x06, 0x2eb0); + rtl8168_mdio_write(tp, 0x06, 0xee86); + rtl8168_mdio_write(tp, 0x06, 0x0b00); + rtl8168_mdio_write(tp, 0x06, 0xee86); + rtl8168_mdio_write(tp, 0x06, 0x0a02); + rtl8168_mdio_write(tp, 0x06, 0xaf84); + rtl8168_mdio_write(tp, 0x06, 0x3ca0); + rtl8168_mdio_write(tp, 0x06, 0x0252); + rtl8168_mdio_write(tp, 0x06, 0xee86); + rtl8168_mdio_write(tp, 0x06, 0x0400); + rtl8168_mdio_write(tp, 0x06, 0xee86); + rtl8168_mdio_write(tp, 0x06, 0x0500); + rtl8168_mdio_write(tp, 0x06, 0xe086); + rtl8168_mdio_write(tp, 0x06, 0x0be1); + rtl8168_mdio_write(tp, 0x06, 0x8b32); + rtl8168_mdio_write(tp, 0x06, 0x1b10); + rtl8168_mdio_write(tp, 0x06, 0x9e04); + rtl8168_mdio_write(tp, 0x06, 0xaa02); + rtl8168_mdio_write(tp, 0x06, 0xaecb); + rtl8168_mdio_write(tp, 0x06, 0xee86); + rtl8168_mdio_write(tp, 0x06, 0x0b00); + rtl8168_mdio_write(tp, 0x06, 0x0224); + rtl8168_mdio_write(tp, 0x06, 0x3ae2); + rtl8168_mdio_write(tp, 0x06, 0x8604); + rtl8168_mdio_write(tp, 0x06, 0xe386); + rtl8168_mdio_write(tp, 0x06, 0x05ef); + rtl8168_mdio_write(tp, 0x06, 0x65e2); + rtl8168_mdio_write(tp, 0x06, 0x8606); + rtl8168_mdio_write(tp, 0x06, 0xe386); + rtl8168_mdio_write(tp, 0x06, 0x071b); + rtl8168_mdio_write(tp, 0x06, 0x56aa); + rtl8168_mdio_write(tp, 0x06, 0x0eef); + rtl8168_mdio_write(tp, 0x06, 0x56e6); + rtl8168_mdio_write(tp, 0x06, 0x8606); + rtl8168_mdio_write(tp, 0x06, 0xe786); + rtl8168_mdio_write(tp, 0x06, 0x07e2); + rtl8168_mdio_write(tp, 0x06, 0x8609); + rtl8168_mdio_write(tp, 0x06, 0xe686); + rtl8168_mdio_write(tp, 0x06, 0x08e0); + rtl8168_mdio_write(tp, 0x06, 0x8609); + rtl8168_mdio_write(tp, 0x06, 0xa000); + rtl8168_mdio_write(tp, 0x06, 0x07ee); + rtl8168_mdio_write(tp, 0x06, 0x860a); + rtl8168_mdio_write(tp, 0x06, 0x03af); + rtl8168_mdio_write(tp, 0x06, 0x8369); + rtl8168_mdio_write(tp, 0x06, 0x0224); + rtl8168_mdio_write(tp, 0x06, 0x8e02); + rtl8168_mdio_write(tp, 0x06, 0x2426); + rtl8168_mdio_write(tp, 0x06, 0xae48); + rtl8168_mdio_write(tp, 0x06, 0xa003); + rtl8168_mdio_write(tp, 0x06, 0x21e0); + rtl8168_mdio_write(tp, 0x06, 0x8608); + rtl8168_mdio_write(tp, 0x06, 0xe186); + rtl8168_mdio_write(tp, 0x06, 0x091b); + rtl8168_mdio_write(tp, 0x06, 0x019e); + rtl8168_mdio_write(tp, 0x06, 0x0caa); + rtl8168_mdio_write(tp, 0x06, 0x0502); + rtl8168_mdio_write(tp, 0x06, 0x249d); + rtl8168_mdio_write(tp, 0x06, 0xaee7); + rtl8168_mdio_write(tp, 0x06, 0x0224); + rtl8168_mdio_write(tp, 0x06, 0x8eae); + rtl8168_mdio_write(tp, 0x06, 0xe2ee); + rtl8168_mdio_write(tp, 0x06, 0x860a); + rtl8168_mdio_write(tp, 0x06, 0x04ee); + rtl8168_mdio_write(tp, 0x06, 0x860b); + rtl8168_mdio_write(tp, 0x06, 0x00af); + rtl8168_mdio_write(tp, 0x06, 0x8369); + rtl8168_mdio_write(tp, 0x06, 0xa004); + rtl8168_mdio_write(tp, 0x06, 0x15e0); + rtl8168_mdio_write(tp, 0x06, 0x860b); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x341b); + rtl8168_mdio_write(tp, 0x06, 0x109e); + rtl8168_mdio_write(tp, 0x06, 0x05aa); + rtl8168_mdio_write(tp, 0x06, 0x03af); + rtl8168_mdio_write(tp, 0x06, 0x8383); + rtl8168_mdio_write(tp, 0x06, 0xee86); + rtl8168_mdio_write(tp, 0x06, 0x0a05); + rtl8168_mdio_write(tp, 0x06, 0xae0c); + rtl8168_mdio_write(tp, 0x06, 0xa005); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0x0702); + rtl8168_mdio_write(tp, 0x06, 0x2309); + rtl8168_mdio_write(tp, 0x06, 0xee86); + rtl8168_mdio_write(tp, 0x06, 0x0a00); + rtl8168_mdio_write(tp, 0x06, 0xfeef); + rtl8168_mdio_write(tp, 0x06, 0x96fe); + rtl8168_mdio_write(tp, 0x06, 0xfdfc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xf9fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xfbe0); + rtl8168_mdio_write(tp, 0x06, 0x8b85); + rtl8168_mdio_write(tp, 0x06, 0xad25); + rtl8168_mdio_write(tp, 0x06, 0x22e0); + rtl8168_mdio_write(tp, 0x06, 0xe022); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x23e2); + rtl8168_mdio_write(tp, 0x06, 0xe036); + rtl8168_mdio_write(tp, 0x06, 0xe3e0); + rtl8168_mdio_write(tp, 0x06, 0x375a); + rtl8168_mdio_write(tp, 0x06, 0xc40d); + rtl8168_mdio_write(tp, 0x06, 0x0158); + rtl8168_mdio_write(tp, 0x06, 0x021e); + rtl8168_mdio_write(tp, 0x06, 0x20e3); + rtl8168_mdio_write(tp, 0x06, 0x8ae7); + rtl8168_mdio_write(tp, 0x06, 0xac31); + rtl8168_mdio_write(tp, 0x06, 0x60ac); + rtl8168_mdio_write(tp, 0x06, 0x3a08); + rtl8168_mdio_write(tp, 0x06, 0xac3e); + rtl8168_mdio_write(tp, 0x06, 0x26ae); + rtl8168_mdio_write(tp, 0x06, 0x67af); + rtl8168_mdio_write(tp, 0x06, 0x84db); + rtl8168_mdio_write(tp, 0x06, 0xad37); + rtl8168_mdio_write(tp, 0x06, 0x61e0); + rtl8168_mdio_write(tp, 0x06, 0x8ae8); + rtl8168_mdio_write(tp, 0x06, 0x10e4); + rtl8168_mdio_write(tp, 0x06, 0x8ae8); + rtl8168_mdio_write(tp, 0x06, 0xe18a); + rtl8168_mdio_write(tp, 0x06, 0xe91b); + rtl8168_mdio_write(tp, 0x06, 0x109e); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0x51d1); + rtl8168_mdio_write(tp, 0x06, 0x00bf); + rtl8168_mdio_write(tp, 0x06, 0x863b); + rtl8168_mdio_write(tp, 0x06, 0x022f); + rtl8168_mdio_write(tp, 0x06, 0x50ee); + rtl8168_mdio_write(tp, 0x06, 0x8ae8); + rtl8168_mdio_write(tp, 0x06, 0x00ae); + rtl8168_mdio_write(tp, 0x06, 0x43ad); + rtl8168_mdio_write(tp, 0x06, 0x3627); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0xeee1); + rtl8168_mdio_write(tp, 0x06, 0x8aef); + rtl8168_mdio_write(tp, 0x06, 0xef74); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0xeae1); + rtl8168_mdio_write(tp, 0x06, 0x8aeb); + rtl8168_mdio_write(tp, 0x06, 0x1b74); + rtl8168_mdio_write(tp, 0x06, 0x9e2e); + rtl8168_mdio_write(tp, 0x06, 0x14e4); + rtl8168_mdio_write(tp, 0x06, 0x8aea); + rtl8168_mdio_write(tp, 0x06, 0xe58a); + rtl8168_mdio_write(tp, 0x06, 0xebef); + rtl8168_mdio_write(tp, 0x06, 0x74e0); + rtl8168_mdio_write(tp, 0x06, 0x8aee); + rtl8168_mdio_write(tp, 0x06, 0xe18a); + rtl8168_mdio_write(tp, 0x06, 0xef1b); + rtl8168_mdio_write(tp, 0x06, 0x479e); + rtl8168_mdio_write(tp, 0x06, 0x0fae); + rtl8168_mdio_write(tp, 0x06, 0x19ee); + rtl8168_mdio_write(tp, 0x06, 0x8aea); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0x8aeb); + rtl8168_mdio_write(tp, 0x06, 0x00ae); + rtl8168_mdio_write(tp, 0x06, 0x0fac); + rtl8168_mdio_write(tp, 0x06, 0x390c); + rtl8168_mdio_write(tp, 0x06, 0xd101); + rtl8168_mdio_write(tp, 0x06, 0xbf86); + rtl8168_mdio_write(tp, 0x06, 0x3b02); + rtl8168_mdio_write(tp, 0x06, 0x2f50); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0xe800); + rtl8168_mdio_write(tp, 0x06, 0xe68a); + rtl8168_mdio_write(tp, 0x06, 0xe7ff); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfefd); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8f9); + rtl8168_mdio_write(tp, 0x06, 0xfaef); + rtl8168_mdio_write(tp, 0x06, 0x69e0); + rtl8168_mdio_write(tp, 0x06, 0xe022); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x2358); + rtl8168_mdio_write(tp, 0x06, 0xc4e1); + rtl8168_mdio_write(tp, 0x06, 0x8b6e); + rtl8168_mdio_write(tp, 0x06, 0x1f10); + rtl8168_mdio_write(tp, 0x06, 0x9e24); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x6ead); + rtl8168_mdio_write(tp, 0x06, 0x2218); + rtl8168_mdio_write(tp, 0x06, 0xac27); + rtl8168_mdio_write(tp, 0x06, 0x0dac); + rtl8168_mdio_write(tp, 0x06, 0x2605); + rtl8168_mdio_write(tp, 0x06, 0x0203); + rtl8168_mdio_write(tp, 0x06, 0x8fae); + rtl8168_mdio_write(tp, 0x06, 0x1302); + rtl8168_mdio_write(tp, 0x06, 0x03c8); + rtl8168_mdio_write(tp, 0x06, 0xae0e); + rtl8168_mdio_write(tp, 0x06, 0x0203); + rtl8168_mdio_write(tp, 0x06, 0xe102); + rtl8168_mdio_write(tp, 0x06, 0x8520); + rtl8168_mdio_write(tp, 0x06, 0xae06); + rtl8168_mdio_write(tp, 0x06, 0x0203); + rtl8168_mdio_write(tp, 0x06, 0x8f02); + rtl8168_mdio_write(tp, 0x06, 0x8566); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfefd); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x82ad); + rtl8168_mdio_write(tp, 0x06, 0x2737); + rtl8168_mdio_write(tp, 0x06, 0xbf86); + rtl8168_mdio_write(tp, 0x06, 0x4402); + rtl8168_mdio_write(tp, 0x06, 0x2f23); + rtl8168_mdio_write(tp, 0x06, 0xac28); + rtl8168_mdio_write(tp, 0x06, 0x2ed1); + rtl8168_mdio_write(tp, 0x06, 0x01bf); + rtl8168_mdio_write(tp, 0x06, 0x8647); + rtl8168_mdio_write(tp, 0x06, 0x022f); + rtl8168_mdio_write(tp, 0x06, 0x50bf); + rtl8168_mdio_write(tp, 0x06, 0x8641); + rtl8168_mdio_write(tp, 0x06, 0x022f); + rtl8168_mdio_write(tp, 0x06, 0x23e5); + rtl8168_mdio_write(tp, 0x06, 0x8af0); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x22e1); + rtl8168_mdio_write(tp, 0x06, 0xe023); + rtl8168_mdio_write(tp, 0x06, 0xac2e); + rtl8168_mdio_write(tp, 0x06, 0x04d1); + rtl8168_mdio_write(tp, 0x06, 0x01ae); + rtl8168_mdio_write(tp, 0x06, 0x02d1); + rtl8168_mdio_write(tp, 0x06, 0x00bf); + rtl8168_mdio_write(tp, 0x06, 0x8641); + rtl8168_mdio_write(tp, 0x06, 0x022f); + rtl8168_mdio_write(tp, 0x06, 0x50d1); + rtl8168_mdio_write(tp, 0x06, 0x01bf); + rtl8168_mdio_write(tp, 0x06, 0x8644); + rtl8168_mdio_write(tp, 0x06, 0x022f); + rtl8168_mdio_write(tp, 0x06, 0x50ef); + rtl8168_mdio_write(tp, 0x06, 0x96fe); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xbf86); + rtl8168_mdio_write(tp, 0x06, 0x4702); + rtl8168_mdio_write(tp, 0x06, 0x2f23); + rtl8168_mdio_write(tp, 0x06, 0xad28); + rtl8168_mdio_write(tp, 0x06, 0x19d1); + rtl8168_mdio_write(tp, 0x06, 0x00bf); + rtl8168_mdio_write(tp, 0x06, 0x8644); + rtl8168_mdio_write(tp, 0x06, 0x022f); + rtl8168_mdio_write(tp, 0x06, 0x50e1); + rtl8168_mdio_write(tp, 0x06, 0x8af0); + rtl8168_mdio_write(tp, 0x06, 0xbf86); + rtl8168_mdio_write(tp, 0x06, 0x4102); + rtl8168_mdio_write(tp, 0x06, 0x2f50); + rtl8168_mdio_write(tp, 0x06, 0xd100); + rtl8168_mdio_write(tp, 0x06, 0xbf86); + rtl8168_mdio_write(tp, 0x06, 0x4702); + rtl8168_mdio_write(tp, 0x06, 0x2f50); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfefc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xe0e2); + rtl8168_mdio_write(tp, 0x06, 0xfee1); + rtl8168_mdio_write(tp, 0x06, 0xe2ff); + rtl8168_mdio_write(tp, 0x06, 0xad2e); + rtl8168_mdio_write(tp, 0x06, 0x63e0); + rtl8168_mdio_write(tp, 0x06, 0xe038); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x39ad); + rtl8168_mdio_write(tp, 0x06, 0x2f10); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x34e1); + rtl8168_mdio_write(tp, 0x06, 0xe035); + rtl8168_mdio_write(tp, 0x06, 0xf726); + rtl8168_mdio_write(tp, 0x06, 0xe4e0); + rtl8168_mdio_write(tp, 0x06, 0x34e5); + rtl8168_mdio_write(tp, 0x06, 0xe035); + rtl8168_mdio_write(tp, 0x06, 0xae0e); + rtl8168_mdio_write(tp, 0x06, 0xe0e2); + rtl8168_mdio_write(tp, 0x06, 0xd6e1); + rtl8168_mdio_write(tp, 0x06, 0xe2d7); + rtl8168_mdio_write(tp, 0x06, 0xf728); + rtl8168_mdio_write(tp, 0x06, 0xe4e2); + rtl8168_mdio_write(tp, 0x06, 0xd6e5); + rtl8168_mdio_write(tp, 0x06, 0xe2d7); + rtl8168_mdio_write(tp, 0x06, 0xe0e2); + rtl8168_mdio_write(tp, 0x06, 0x34e1); + rtl8168_mdio_write(tp, 0x06, 0xe235); + rtl8168_mdio_write(tp, 0x06, 0xf72b); + rtl8168_mdio_write(tp, 0x06, 0xe4e2); + rtl8168_mdio_write(tp, 0x06, 0x34e5); + rtl8168_mdio_write(tp, 0x06, 0xe235); + rtl8168_mdio_write(tp, 0x06, 0xd07d); + rtl8168_mdio_write(tp, 0x06, 0xb0fe); + rtl8168_mdio_write(tp, 0x06, 0xe0e2); + rtl8168_mdio_write(tp, 0x06, 0x34e1); + rtl8168_mdio_write(tp, 0x06, 0xe235); + rtl8168_mdio_write(tp, 0x06, 0xf62b); + rtl8168_mdio_write(tp, 0x06, 0xe4e2); + rtl8168_mdio_write(tp, 0x06, 0x34e5); + rtl8168_mdio_write(tp, 0x06, 0xe235); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x34e1); + rtl8168_mdio_write(tp, 0x06, 0xe035); + rtl8168_mdio_write(tp, 0x06, 0xf626); + rtl8168_mdio_write(tp, 0x06, 0xe4e0); + rtl8168_mdio_write(tp, 0x06, 0x34e5); + rtl8168_mdio_write(tp, 0x06, 0xe035); + rtl8168_mdio_write(tp, 0x06, 0xe0e2); + rtl8168_mdio_write(tp, 0x06, 0xd6e1); + rtl8168_mdio_write(tp, 0x06, 0xe2d7); + rtl8168_mdio_write(tp, 0x06, 0xf628); + rtl8168_mdio_write(tp, 0x06, 0xe4e2); + rtl8168_mdio_write(tp, 0x06, 0xd6e5); + rtl8168_mdio_write(tp, 0x06, 0xe2d7); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xae20); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0xa725); + rtl8168_mdio_write(tp, 0x06, 0xe50a); + rtl8168_mdio_write(tp, 0x06, 0x1de5); + rtl8168_mdio_write(tp, 0x06, 0x0a2c); + rtl8168_mdio_write(tp, 0x06, 0xe50a); + rtl8168_mdio_write(tp, 0x06, 0x6de5); + rtl8168_mdio_write(tp, 0x06, 0x0a1d); + rtl8168_mdio_write(tp, 0x06, 0xe50a); + rtl8168_mdio_write(tp, 0x06, 0x1ce5); + rtl8168_mdio_write(tp, 0x06, 0x0a2d); + rtl8168_mdio_write(tp, 0x06, 0xa755); + rtl8168_mdio_write(tp, 0x06, 0x00e2); + rtl8168_mdio_write(tp, 0x06, 0x3488); + rtl8168_mdio_write(tp, 0x06, 0xe200); + rtl8168_mdio_write(tp, 0x06, 0xcce2); + rtl8168_mdio_write(tp, 0x06, 0x0055); + rtl8168_mdio_write(tp, 0x06, 0xe020); + rtl8168_mdio_write(tp, 0x06, 0x55e2); + rtl8168_mdio_write(tp, 0x06, 0xd600); + rtl8168_mdio_write(tp, 0x06, 0xe24a); + gphy_val = rtl8168_mdio_read(tp, 0x01); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp, 0x01, gphy_val); + gphy_val = rtl8168_mdio_read(tp, 0x00); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp, 0x00, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x17, 0x2179); + rtl8168_mdio_write(tp, 0x1f, 0x0001); + rtl8168_mdio_write(tp, 0x10, 0xf274); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0042); + rtl8168_mdio_write(tp, 0x15, 0x0f00); + rtl8168_mdio_write(tp, 0x15, 0x0f00); + rtl8168_mdio_write(tp, 0x16, 0x7408); + rtl8168_mdio_write(tp, 0x15, 0x0e00); + rtl8168_mdio_write(tp, 0x15, 0x0f00); + rtl8168_mdio_write(tp, 0x15, 0x0f01); + rtl8168_mdio_write(tp, 0x16, 0x4000); + rtl8168_mdio_write(tp, 0x15, 0x0e01); + rtl8168_mdio_write(tp, 0x15, 0x0f01); + rtl8168_mdio_write(tp, 0x15, 0x0f02); + rtl8168_mdio_write(tp, 0x16, 0x9400); + rtl8168_mdio_write(tp, 0x15, 0x0e02); + rtl8168_mdio_write(tp, 0x15, 0x0f02); + rtl8168_mdio_write(tp, 0x15, 0x0f03); + rtl8168_mdio_write(tp, 0x16, 0x7408); + rtl8168_mdio_write(tp, 0x15, 0x0e03); + rtl8168_mdio_write(tp, 0x15, 0x0f03); + rtl8168_mdio_write(tp, 0x15, 0x0f04); + rtl8168_mdio_write(tp, 0x16, 0x4008); + rtl8168_mdio_write(tp, 0x15, 0x0e04); + rtl8168_mdio_write(tp, 0x15, 0x0f04); + rtl8168_mdio_write(tp, 0x15, 0x0f05); + rtl8168_mdio_write(tp, 0x16, 0x9400); + rtl8168_mdio_write(tp, 0x15, 0x0e05); + rtl8168_mdio_write(tp, 0x15, 0x0f05); + rtl8168_mdio_write(tp, 0x15, 0x0f06); + rtl8168_mdio_write(tp, 0x16, 0x0803); + rtl8168_mdio_write(tp, 0x15, 0x0e06); + rtl8168_mdio_write(tp, 0x15, 0x0f06); + rtl8168_mdio_write(tp, 0x15, 0x0d00); + rtl8168_mdio_write(tp, 0x15, 0x0100); + rtl8168_mdio_write(tp, 0x1f, 0x0001); + rtl8168_mdio_write(tp, 0x10, 0xf074); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x17, 0x2149); + + rtl8168_mdio_write(tp, 0x1f, 0x0005); + for (i = 0; i < 200; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x00); + if (gphy_val & BIT_7) + break; + } + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0023); + gphy_val = rtl8168_mdio_read(tp, 0x17); + gphy_val &= ~(BIT_0); + if (tp->RequiredSecLanDonglePatch) + gphy_val &= ~(BIT_2); + rtl8168_mdio_write(tp, 0x17, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0023); + gphy_val = rtl8168_mdio_read(tp, 0x17); + gphy_val |= BIT_14; + rtl8168_mdio_write(tp, 0x17, gphy_val); + rtl8168_mdio_write(tp, 0x1e, 0x0020); + gphy_val = rtl8168_mdio_read(tp, 0x1b); + gphy_val |= BIT_7; + rtl8168_mdio_write(tp, 0x1b, gphy_val); + rtl8168_mdio_write(tp, 0x1e, 0x0041); + rtl8168_mdio_write(tp, 0x15, 0x0e02); + rtl8168_mdio_write(tp, 0x1e, 0x0028); + gphy_val = rtl8168_mdio_read(tp, 0x19); + gphy_val |= BIT_15; + rtl8168_mdio_write(tp, 0x19, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + } else { + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x00, 0x1800); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0023); + rtl8168_mdio_write(tp, 0x17, 0x0117); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x002C); + rtl8168_mdio_write(tp, 0x1B, 0x5000); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x16, 0x4104); + for (i = 0; i < 200; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x1E); + gphy_val &= 0x03FF; + if (gphy_val==0x000C) + break; + } + rtl8168_mdio_write(tp, 0x1f, 0x0005); + for (i = 0; i < 200; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x07); + if ((gphy_val & BIT_5) == 0) + break; + } + gphy_val = rtl8168_mdio_read(tp, 0x07); + if (gphy_val & BIT_5) { + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x00a1); + rtl8168_mdio_write(tp, 0x17, 0x1000); + rtl8168_mdio_write(tp, 0x17, 0x0000); + rtl8168_mdio_write(tp, 0x17, 0x2000); + rtl8168_mdio_write(tp, 0x1e, 0x002f); + rtl8168_mdio_write(tp, 0x18, 0x9bfb); + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x07, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + } + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x0080); + gphy_val = rtl8168_mdio_read(tp, 0x00); + gphy_val &= ~(BIT_7); + rtl8168_mdio_write(tp, 0x00, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0002); + gphy_val = rtl8168_mdio_read(tp, 0x08); + gphy_val &= ~(BIT_7); + rtl8168_mdio_write(tp, 0x08, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0023); + rtl8168_mdio_write(tp, 0x16, 0x0306); + rtl8168_mdio_write(tp, 0x16, 0x0307); + rtl8168_mdio_write(tp, 0x15, 0x000e); + rtl8168_mdio_write(tp, 0x19, 0x000a); + rtl8168_mdio_write(tp, 0x15, 0x0010); + rtl8168_mdio_write(tp, 0x19, 0x0008); + rtl8168_mdio_write(tp, 0x15, 0x0018); + rtl8168_mdio_write(tp, 0x19, 0x4801); + rtl8168_mdio_write(tp, 0x15, 0x0019); + rtl8168_mdio_write(tp, 0x19, 0x6801); + rtl8168_mdio_write(tp, 0x15, 0x001a); + rtl8168_mdio_write(tp, 0x19, 0x66a1); + rtl8168_mdio_write(tp, 0x15, 0x001f); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0020); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0021); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0022); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0023); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0024); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0025); + rtl8168_mdio_write(tp, 0x19, 0x64a1); + rtl8168_mdio_write(tp, 0x15, 0x0026); + rtl8168_mdio_write(tp, 0x19, 0x40ea); + rtl8168_mdio_write(tp, 0x15, 0x0027); + rtl8168_mdio_write(tp, 0x19, 0x4503); + rtl8168_mdio_write(tp, 0x15, 0x0028); + rtl8168_mdio_write(tp, 0x19, 0x9f00); + rtl8168_mdio_write(tp, 0x15, 0x0029); + rtl8168_mdio_write(tp, 0x19, 0xa631); + rtl8168_mdio_write(tp, 0x15, 0x002a); + rtl8168_mdio_write(tp, 0x19, 0x9717); + rtl8168_mdio_write(tp, 0x15, 0x002b); + rtl8168_mdio_write(tp, 0x19, 0x302c); + rtl8168_mdio_write(tp, 0x15, 0x002c); + rtl8168_mdio_write(tp, 0x19, 0x4802); + rtl8168_mdio_write(tp, 0x15, 0x002d); + rtl8168_mdio_write(tp, 0x19, 0x58da); + rtl8168_mdio_write(tp, 0x15, 0x002e); + rtl8168_mdio_write(tp, 0x19, 0x400d); + rtl8168_mdio_write(tp, 0x15, 0x002f); + rtl8168_mdio_write(tp, 0x19, 0x4488); + rtl8168_mdio_write(tp, 0x15, 0x0030); + rtl8168_mdio_write(tp, 0x19, 0x9e00); + rtl8168_mdio_write(tp, 0x15, 0x0031); + rtl8168_mdio_write(tp, 0x19, 0x63c8); + rtl8168_mdio_write(tp, 0x15, 0x0032); + rtl8168_mdio_write(tp, 0x19, 0x6481); + rtl8168_mdio_write(tp, 0x15, 0x0033); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0034); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0035); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0036); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0037); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0038); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0039); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x003a); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x003b); + rtl8168_mdio_write(tp, 0x19, 0x63e8); + rtl8168_mdio_write(tp, 0x15, 0x003c); + rtl8168_mdio_write(tp, 0x19, 0x7d00); + rtl8168_mdio_write(tp, 0x15, 0x003d); + rtl8168_mdio_write(tp, 0x19, 0x59d4); + rtl8168_mdio_write(tp, 0x15, 0x003e); + rtl8168_mdio_write(tp, 0x19, 0x63f8); + rtl8168_mdio_write(tp, 0x15, 0x0040); + rtl8168_mdio_write(tp, 0x19, 0x64a1); + rtl8168_mdio_write(tp, 0x15, 0x0041); + rtl8168_mdio_write(tp, 0x19, 0x30de); + rtl8168_mdio_write(tp, 0x15, 0x0044); + rtl8168_mdio_write(tp, 0x19, 0x480f); + rtl8168_mdio_write(tp, 0x15, 0x0045); + rtl8168_mdio_write(tp, 0x19, 0x6800); + rtl8168_mdio_write(tp, 0x15, 0x0046); + rtl8168_mdio_write(tp, 0x19, 0x6680); + rtl8168_mdio_write(tp, 0x15, 0x0047); + rtl8168_mdio_write(tp, 0x19, 0x7c10); + rtl8168_mdio_write(tp, 0x15, 0x0048); + rtl8168_mdio_write(tp, 0x19, 0x63c8); + rtl8168_mdio_write(tp, 0x15, 0x0049); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x004a); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x004b); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x004c); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x004d); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x004e); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x004f); + rtl8168_mdio_write(tp, 0x19, 0x40ea); + rtl8168_mdio_write(tp, 0x15, 0x0050); + rtl8168_mdio_write(tp, 0x19, 0x4503); + rtl8168_mdio_write(tp, 0x15, 0x0051); + rtl8168_mdio_write(tp, 0x19, 0x58ca); + rtl8168_mdio_write(tp, 0x15, 0x0052); + rtl8168_mdio_write(tp, 0x19, 0x63c8); + rtl8168_mdio_write(tp, 0x15, 0x0053); + rtl8168_mdio_write(tp, 0x19, 0x63d8); + rtl8168_mdio_write(tp, 0x15, 0x0054); + rtl8168_mdio_write(tp, 0x19, 0x66a0); + rtl8168_mdio_write(tp, 0x15, 0x0055); + rtl8168_mdio_write(tp, 0x19, 0x9f00); + rtl8168_mdio_write(tp, 0x15, 0x0056); + rtl8168_mdio_write(tp, 0x19, 0x3000); + rtl8168_mdio_write(tp, 0x15, 0x00a1); + rtl8168_mdio_write(tp, 0x19, 0x3044); + rtl8168_mdio_write(tp, 0x15, 0x00ab); + rtl8168_mdio_write(tp, 0x19, 0x5820); + rtl8168_mdio_write(tp, 0x15, 0x00ac); + rtl8168_mdio_write(tp, 0x19, 0x5e04); + rtl8168_mdio_write(tp, 0x15, 0x00ad); + rtl8168_mdio_write(tp, 0x19, 0xb60c); + rtl8168_mdio_write(tp, 0x15, 0x00af); + rtl8168_mdio_write(tp, 0x19, 0x000a); + rtl8168_mdio_write(tp, 0x15, 0x00b2); + rtl8168_mdio_write(tp, 0x19, 0x30b9); + rtl8168_mdio_write(tp, 0x15, 0x00b9); + rtl8168_mdio_write(tp, 0x19, 0x4408); + rtl8168_mdio_write(tp, 0x15, 0x00ba); + rtl8168_mdio_write(tp, 0x19, 0x480b); + rtl8168_mdio_write(tp, 0x15, 0x00bb); + rtl8168_mdio_write(tp, 0x19, 0x5e00); + rtl8168_mdio_write(tp, 0x15, 0x00bc); + rtl8168_mdio_write(tp, 0x19, 0x405f); + rtl8168_mdio_write(tp, 0x15, 0x00bd); + rtl8168_mdio_write(tp, 0x19, 0x4448); + rtl8168_mdio_write(tp, 0x15, 0x00be); + rtl8168_mdio_write(tp, 0x19, 0x4020); + rtl8168_mdio_write(tp, 0x15, 0x00bf); + rtl8168_mdio_write(tp, 0x19, 0x4468); + rtl8168_mdio_write(tp, 0x15, 0x00c0); + rtl8168_mdio_write(tp, 0x19, 0x9c02); + rtl8168_mdio_write(tp, 0x15, 0x00c1); + rtl8168_mdio_write(tp, 0x19, 0x58a0); + rtl8168_mdio_write(tp, 0x15, 0x00c2); + rtl8168_mdio_write(tp, 0x19, 0xb605); + rtl8168_mdio_write(tp, 0x15, 0x00c3); + rtl8168_mdio_write(tp, 0x19, 0xc0d3); + rtl8168_mdio_write(tp, 0x15, 0x00c4); + rtl8168_mdio_write(tp, 0x19, 0x00e6); + rtl8168_mdio_write(tp, 0x15, 0x00c5); + rtl8168_mdio_write(tp, 0x19, 0xdaec); + rtl8168_mdio_write(tp, 0x15, 0x00c6); + rtl8168_mdio_write(tp, 0x19, 0x00fa); + rtl8168_mdio_write(tp, 0x15, 0x00c7); + rtl8168_mdio_write(tp, 0x19, 0x9df9); + rtl8168_mdio_write(tp, 0x15, 0x0112); + rtl8168_mdio_write(tp, 0x19, 0x6421); + rtl8168_mdio_write(tp, 0x15, 0x0113); + rtl8168_mdio_write(tp, 0x19, 0x7c08); + rtl8168_mdio_write(tp, 0x15, 0x0114); + rtl8168_mdio_write(tp, 0x19, 0x63f0); + rtl8168_mdio_write(tp, 0x15, 0x0115); + rtl8168_mdio_write(tp, 0x19, 0x4003); + rtl8168_mdio_write(tp, 0x15, 0x0116); + rtl8168_mdio_write(tp, 0x19, 0x4418); + rtl8168_mdio_write(tp, 0x15, 0x0117); + rtl8168_mdio_write(tp, 0x19, 0x9b00); + rtl8168_mdio_write(tp, 0x15, 0x0118); + rtl8168_mdio_write(tp, 0x19, 0x6461); + rtl8168_mdio_write(tp, 0x15, 0x0119); + rtl8168_mdio_write(tp, 0x19, 0x64e1); + rtl8168_mdio_write(tp, 0x15, 0x011a); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0150); + rtl8168_mdio_write(tp, 0x19, 0x7c80); + rtl8168_mdio_write(tp, 0x15, 0x0151); + rtl8168_mdio_write(tp, 0x19, 0x6461); + rtl8168_mdio_write(tp, 0x15, 0x0152); + rtl8168_mdio_write(tp, 0x19, 0x4003); + rtl8168_mdio_write(tp, 0x15, 0x0153); + rtl8168_mdio_write(tp, 0x19, 0x4540); + rtl8168_mdio_write(tp, 0x15, 0x0154); + rtl8168_mdio_write(tp, 0x19, 0x9f00); + rtl8168_mdio_write(tp, 0x15, 0x0155); + rtl8168_mdio_write(tp, 0x19, 0x9d00); + rtl8168_mdio_write(tp, 0x15, 0x0156); + rtl8168_mdio_write(tp, 0x19, 0x7c40); + rtl8168_mdio_write(tp, 0x15, 0x0157); + rtl8168_mdio_write(tp, 0x19, 0x6421); + rtl8168_mdio_write(tp, 0x15, 0x0158); + rtl8168_mdio_write(tp, 0x19, 0x7c80); + rtl8168_mdio_write(tp, 0x15, 0x0159); + rtl8168_mdio_write(tp, 0x19, 0x64a1); + rtl8168_mdio_write(tp, 0x15, 0x015a); + rtl8168_mdio_write(tp, 0x19, 0x30fe); + rtl8168_mdio_write(tp, 0x15, 0x029c); + rtl8168_mdio_write(tp, 0x19, 0x0070); + rtl8168_mdio_write(tp, 0x15, 0x02b2); + rtl8168_mdio_write(tp, 0x19, 0x005a); + rtl8168_mdio_write(tp, 0x15, 0x02bd); + rtl8168_mdio_write(tp, 0x19, 0xa522); + rtl8168_mdio_write(tp, 0x15, 0x02ce); + rtl8168_mdio_write(tp, 0x19, 0xb63e); + rtl8168_mdio_write(tp, 0x15, 0x02d9); + rtl8168_mdio_write(tp, 0x19, 0x32df); + rtl8168_mdio_write(tp, 0x15, 0x02df); + rtl8168_mdio_write(tp, 0x19, 0x4500); + rtl8168_mdio_write(tp, 0x15, 0x02f4); + rtl8168_mdio_write(tp, 0x19, 0xb618); + rtl8168_mdio_write(tp, 0x15, 0x02fb); + rtl8168_mdio_write(tp, 0x19, 0xb900); + rtl8168_mdio_write(tp, 0x15, 0x02fc); + rtl8168_mdio_write(tp, 0x19, 0x49b5); + rtl8168_mdio_write(tp, 0x15, 0x02fd); + rtl8168_mdio_write(tp, 0x19, 0x6812); + rtl8168_mdio_write(tp, 0x15, 0x02fe); + rtl8168_mdio_write(tp, 0x19, 0x66a0); + rtl8168_mdio_write(tp, 0x15, 0x02ff); + rtl8168_mdio_write(tp, 0x19, 0x9900); + rtl8168_mdio_write(tp, 0x15, 0x0300); + rtl8168_mdio_write(tp, 0x19, 0x64a0); + rtl8168_mdio_write(tp, 0x15, 0x0301); + rtl8168_mdio_write(tp, 0x19, 0x3316); + rtl8168_mdio_write(tp, 0x15, 0x0308); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x030c); + rtl8168_mdio_write(tp, 0x19, 0x3000); + rtl8168_mdio_write(tp, 0x15, 0x0312); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0313); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0314); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0315); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0316); + rtl8168_mdio_write(tp, 0x19, 0x49b5); + rtl8168_mdio_write(tp, 0x15, 0x0317); + rtl8168_mdio_write(tp, 0x19, 0x7d00); + rtl8168_mdio_write(tp, 0x15, 0x0318); + rtl8168_mdio_write(tp, 0x19, 0x4d00); + rtl8168_mdio_write(tp, 0x15, 0x0319); + rtl8168_mdio_write(tp, 0x19, 0x6810); + rtl8168_mdio_write(tp, 0x15, 0x031a); + rtl8168_mdio_write(tp, 0x19, 0x6c08); + rtl8168_mdio_write(tp, 0x15, 0x031b); + rtl8168_mdio_write(tp, 0x19, 0x4925); + rtl8168_mdio_write(tp, 0x15, 0x031c); + rtl8168_mdio_write(tp, 0x19, 0x403b); + rtl8168_mdio_write(tp, 0x15, 0x031d); + rtl8168_mdio_write(tp, 0x19, 0xa602); + rtl8168_mdio_write(tp, 0x15, 0x031e); + rtl8168_mdio_write(tp, 0x19, 0x402f); + rtl8168_mdio_write(tp, 0x15, 0x031f); + rtl8168_mdio_write(tp, 0x19, 0x4484); + rtl8168_mdio_write(tp, 0x15, 0x0320); + rtl8168_mdio_write(tp, 0x19, 0x40c8); + rtl8168_mdio_write(tp, 0x15, 0x0321); + rtl8168_mdio_write(tp, 0x19, 0x44c4); + rtl8168_mdio_write(tp, 0x15, 0x0322); + rtl8168_mdio_write(tp, 0x19, 0x404f); + rtl8168_mdio_write(tp, 0x15, 0x0323); + rtl8168_mdio_write(tp, 0x19, 0x44c8); + rtl8168_mdio_write(tp, 0x15, 0x0324); + rtl8168_mdio_write(tp, 0x19, 0xd64f); + rtl8168_mdio_write(tp, 0x15, 0x0325); + rtl8168_mdio_write(tp, 0x19, 0x00e7); + rtl8168_mdio_write(tp, 0x15, 0x0326); + rtl8168_mdio_write(tp, 0x19, 0x7c08); + rtl8168_mdio_write(tp, 0x15, 0x0327); + rtl8168_mdio_write(tp, 0x19, 0x8203); + rtl8168_mdio_write(tp, 0x15, 0x0328); + rtl8168_mdio_write(tp, 0x19, 0x4d48); + rtl8168_mdio_write(tp, 0x15, 0x0329); + rtl8168_mdio_write(tp, 0x19, 0x332b); + rtl8168_mdio_write(tp, 0x15, 0x032a); + rtl8168_mdio_write(tp, 0x19, 0x4d40); + rtl8168_mdio_write(tp, 0x15, 0x032c); + rtl8168_mdio_write(tp, 0x19, 0x00f8); + rtl8168_mdio_write(tp, 0x15, 0x032d); + rtl8168_mdio_write(tp, 0x19, 0x82b2); + rtl8168_mdio_write(tp, 0x15, 0x032f); + rtl8168_mdio_write(tp, 0x19, 0x00b0); + rtl8168_mdio_write(tp, 0x15, 0x0332); + rtl8168_mdio_write(tp, 0x19, 0x91f2); + rtl8168_mdio_write(tp, 0x15, 0x033f); + rtl8168_mdio_write(tp, 0x19, 0xb6cd); + rtl8168_mdio_write(tp, 0x15, 0x0340); + rtl8168_mdio_write(tp, 0x19, 0x9e01); + rtl8168_mdio_write(tp, 0x15, 0x0341); + rtl8168_mdio_write(tp, 0x19, 0xd11d); + rtl8168_mdio_write(tp, 0x15, 0x0342); + rtl8168_mdio_write(tp, 0x19, 0x009d); + rtl8168_mdio_write(tp, 0x15, 0x0343); + rtl8168_mdio_write(tp, 0x19, 0xbb1c); + rtl8168_mdio_write(tp, 0x15, 0x0344); + rtl8168_mdio_write(tp, 0x19, 0x8102); + rtl8168_mdio_write(tp, 0x15, 0x0345); + rtl8168_mdio_write(tp, 0x19, 0x3348); + rtl8168_mdio_write(tp, 0x15, 0x0346); + rtl8168_mdio_write(tp, 0x19, 0xa231); + rtl8168_mdio_write(tp, 0x15, 0x0347); + rtl8168_mdio_write(tp, 0x19, 0x335b); + rtl8168_mdio_write(tp, 0x15, 0x0348); + rtl8168_mdio_write(tp, 0x19, 0x91f7); + rtl8168_mdio_write(tp, 0x15, 0x0349); + rtl8168_mdio_write(tp, 0x19, 0xc218); + rtl8168_mdio_write(tp, 0x15, 0x034a); + rtl8168_mdio_write(tp, 0x19, 0x00f5); + rtl8168_mdio_write(tp, 0x15, 0x034b); + rtl8168_mdio_write(tp, 0x19, 0x335b); + rtl8168_mdio_write(tp, 0x15, 0x034c); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x034d); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x034e); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x034f); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0350); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x035b); + rtl8168_mdio_write(tp, 0x19, 0xa23c); + rtl8168_mdio_write(tp, 0x15, 0x035c); + rtl8168_mdio_write(tp, 0x19, 0x7c08); + rtl8168_mdio_write(tp, 0x15, 0x035d); + rtl8168_mdio_write(tp, 0x19, 0x4c00); + rtl8168_mdio_write(tp, 0x15, 0x035e); + rtl8168_mdio_write(tp, 0x19, 0x3397); + rtl8168_mdio_write(tp, 0x15, 0x0363); + rtl8168_mdio_write(tp, 0x19, 0xb6a9); + rtl8168_mdio_write(tp, 0x15, 0x0366); + rtl8168_mdio_write(tp, 0x19, 0x00f5); + rtl8168_mdio_write(tp, 0x15, 0x0382); + rtl8168_mdio_write(tp, 0x19, 0x7c40); + rtl8168_mdio_write(tp, 0x15, 0x0388); + rtl8168_mdio_write(tp, 0x19, 0x0084); + rtl8168_mdio_write(tp, 0x15, 0x0389); + rtl8168_mdio_write(tp, 0x19, 0xdd17); + rtl8168_mdio_write(tp, 0x15, 0x038a); + rtl8168_mdio_write(tp, 0x19, 0x000b); + rtl8168_mdio_write(tp, 0x15, 0x038b); + rtl8168_mdio_write(tp, 0x19, 0xa10a); + rtl8168_mdio_write(tp, 0x15, 0x038c); + rtl8168_mdio_write(tp, 0x19, 0x337e); + rtl8168_mdio_write(tp, 0x15, 0x038d); + rtl8168_mdio_write(tp, 0x19, 0x6c0b); + rtl8168_mdio_write(tp, 0x15, 0x038e); + rtl8168_mdio_write(tp, 0x19, 0xa107); + rtl8168_mdio_write(tp, 0x15, 0x038f); + rtl8168_mdio_write(tp, 0x19, 0x6c08); + rtl8168_mdio_write(tp, 0x15, 0x0390); + rtl8168_mdio_write(tp, 0x19, 0xc017); + rtl8168_mdio_write(tp, 0x15, 0x0391); + rtl8168_mdio_write(tp, 0x19, 0x0004); + rtl8168_mdio_write(tp, 0x15, 0x0392); + rtl8168_mdio_write(tp, 0x19, 0xd64f); + rtl8168_mdio_write(tp, 0x15, 0x0393); + rtl8168_mdio_write(tp, 0x19, 0x00f4); + rtl8168_mdio_write(tp, 0x15, 0x0397); + rtl8168_mdio_write(tp, 0x19, 0x4098); + rtl8168_mdio_write(tp, 0x15, 0x0398); + rtl8168_mdio_write(tp, 0x19, 0x4408); + rtl8168_mdio_write(tp, 0x15, 0x0399); + rtl8168_mdio_write(tp, 0x19, 0x55bf); + rtl8168_mdio_write(tp, 0x15, 0x039a); + rtl8168_mdio_write(tp, 0x19, 0x4bb9); + rtl8168_mdio_write(tp, 0x15, 0x039b); + rtl8168_mdio_write(tp, 0x19, 0x6810); + rtl8168_mdio_write(tp, 0x15, 0x039c); + rtl8168_mdio_write(tp, 0x19, 0x4b29); + rtl8168_mdio_write(tp, 0x15, 0x039d); + rtl8168_mdio_write(tp, 0x19, 0x4041); + rtl8168_mdio_write(tp, 0x15, 0x039e); + rtl8168_mdio_write(tp, 0x19, 0x442a); + rtl8168_mdio_write(tp, 0x15, 0x039f); + rtl8168_mdio_write(tp, 0x19, 0x4029); + rtl8168_mdio_write(tp, 0x15, 0x03aa); + rtl8168_mdio_write(tp, 0x19, 0x33b8); + rtl8168_mdio_write(tp, 0x15, 0x03b6); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03b7); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03b8); + rtl8168_mdio_write(tp, 0x19, 0x543f); + rtl8168_mdio_write(tp, 0x15, 0x03b9); + rtl8168_mdio_write(tp, 0x19, 0x499a); + rtl8168_mdio_write(tp, 0x15, 0x03ba); + rtl8168_mdio_write(tp, 0x19, 0x7c40); + rtl8168_mdio_write(tp, 0x15, 0x03bb); + rtl8168_mdio_write(tp, 0x19, 0x4c40); + rtl8168_mdio_write(tp, 0x15, 0x03bc); + rtl8168_mdio_write(tp, 0x19, 0x490a); + rtl8168_mdio_write(tp, 0x15, 0x03bd); + rtl8168_mdio_write(tp, 0x19, 0x405e); + rtl8168_mdio_write(tp, 0x15, 0x03c2); + rtl8168_mdio_write(tp, 0x19, 0x9a03); + rtl8168_mdio_write(tp, 0x15, 0x03c4); + rtl8168_mdio_write(tp, 0x19, 0x0015); + rtl8168_mdio_write(tp, 0x15, 0x03c5); + rtl8168_mdio_write(tp, 0x19, 0x9e03); + rtl8168_mdio_write(tp, 0x15, 0x03c8); + rtl8168_mdio_write(tp, 0x19, 0x9cf7); + rtl8168_mdio_write(tp, 0x15, 0x03c9); + rtl8168_mdio_write(tp, 0x19, 0x7c12); + rtl8168_mdio_write(tp, 0x15, 0x03ca); + rtl8168_mdio_write(tp, 0x19, 0x4c52); + rtl8168_mdio_write(tp, 0x15, 0x03cb); + rtl8168_mdio_write(tp, 0x19, 0x4458); + rtl8168_mdio_write(tp, 0x15, 0x03cd); + rtl8168_mdio_write(tp, 0x19, 0x4c40); + rtl8168_mdio_write(tp, 0x15, 0x03ce); + rtl8168_mdio_write(tp, 0x19, 0x33bf); + rtl8168_mdio_write(tp, 0x15, 0x03cf); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03d0); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03d1); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03d5); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03d6); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03d7); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03d8); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03d9); + rtl8168_mdio_write(tp, 0x19, 0x49bb); + rtl8168_mdio_write(tp, 0x15, 0x03da); + rtl8168_mdio_write(tp, 0x19, 0x4478); + rtl8168_mdio_write(tp, 0x15, 0x03db); + rtl8168_mdio_write(tp, 0x19, 0x492b); + rtl8168_mdio_write(tp, 0x15, 0x03dc); + rtl8168_mdio_write(tp, 0x19, 0x7c01); + rtl8168_mdio_write(tp, 0x15, 0x03dd); + rtl8168_mdio_write(tp, 0x19, 0x4c00); + rtl8168_mdio_write(tp, 0x15, 0x03de); + rtl8168_mdio_write(tp, 0x19, 0xbd1a); + rtl8168_mdio_write(tp, 0x15, 0x03df); + rtl8168_mdio_write(tp, 0x19, 0xc428); + rtl8168_mdio_write(tp, 0x15, 0x03e0); + rtl8168_mdio_write(tp, 0x19, 0x0008); + rtl8168_mdio_write(tp, 0x15, 0x03e1); + rtl8168_mdio_write(tp, 0x19, 0x9cfd); + rtl8168_mdio_write(tp, 0x15, 0x03e2); + rtl8168_mdio_write(tp, 0x19, 0x7c12); + rtl8168_mdio_write(tp, 0x15, 0x03e3); + rtl8168_mdio_write(tp, 0x19, 0x4c52); + rtl8168_mdio_write(tp, 0x15, 0x03e4); + rtl8168_mdio_write(tp, 0x19, 0x4458); + rtl8168_mdio_write(tp, 0x15, 0x03e5); + rtl8168_mdio_write(tp, 0x19, 0x7c12); + rtl8168_mdio_write(tp, 0x15, 0x03e6); + rtl8168_mdio_write(tp, 0x19, 0x4c40); + rtl8168_mdio_write(tp, 0x15, 0x03e7); + rtl8168_mdio_write(tp, 0x19, 0x33de); + rtl8168_mdio_write(tp, 0x15, 0x03e8); + rtl8168_mdio_write(tp, 0x19, 0xc218); + rtl8168_mdio_write(tp, 0x15, 0x03e9); + rtl8168_mdio_write(tp, 0x19, 0x0002); + rtl8168_mdio_write(tp, 0x15, 0x03ea); + rtl8168_mdio_write(tp, 0x19, 0x32df); + rtl8168_mdio_write(tp, 0x15, 0x03eb); + rtl8168_mdio_write(tp, 0x19, 0x3316); + rtl8168_mdio_write(tp, 0x15, 0x03ec); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03ed); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03ee); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03ef); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03f7); + rtl8168_mdio_write(tp, 0x19, 0x330c); + rtl8168_mdio_write(tp, 0x16, 0x0306); + rtl8168_mdio_write(tp, 0x16, 0x0300); + + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x0080); + rtl8168_mdio_write(tp, 0x05, 0x8000); + rtl8168_mdio_write(tp, 0x06, 0x0280); + rtl8168_mdio_write(tp, 0x06, 0x48f7); + rtl8168_mdio_write(tp, 0x06, 0x00e0); + rtl8168_mdio_write(tp, 0x06, 0xfff7); + rtl8168_mdio_write(tp, 0x06, 0xa080); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0xf602); + rtl8168_mdio_write(tp, 0x06, 0x0200); + rtl8168_mdio_write(tp, 0x06, 0x0280); + rtl8168_mdio_write(tp, 0x06, 0x9002); + rtl8168_mdio_write(tp, 0x06, 0x0224); + rtl8168_mdio_write(tp, 0x06, 0x0202); + rtl8168_mdio_write(tp, 0x06, 0x3402); + rtl8168_mdio_write(tp, 0x06, 0x027f); + rtl8168_mdio_write(tp, 0x06, 0x0280); + rtl8168_mdio_write(tp, 0x06, 0xa602); + rtl8168_mdio_write(tp, 0x06, 0x80bf); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x88e1); + rtl8168_mdio_write(tp, 0x06, 0x8b89); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8a1e); + rtl8168_mdio_write(tp, 0x06, 0x01e1); + rtl8168_mdio_write(tp, 0x06, 0x8b8b); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8c1e); + rtl8168_mdio_write(tp, 0x06, 0x01e1); + rtl8168_mdio_write(tp, 0x06, 0x8b8d); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8e1e); + rtl8168_mdio_write(tp, 0x06, 0x01a0); + rtl8168_mdio_write(tp, 0x06, 0x00c7); + rtl8168_mdio_write(tp, 0x06, 0xaebb); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0xe600); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0xee03); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0xefb8); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0xe902); + rtl8168_mdio_write(tp, 0x06, 0xee8b); + rtl8168_mdio_write(tp, 0x06, 0x8285); + rtl8168_mdio_write(tp, 0x06, 0xee8b); + rtl8168_mdio_write(tp, 0x06, 0x8520); + rtl8168_mdio_write(tp, 0x06, 0xee8b); + rtl8168_mdio_write(tp, 0x06, 0x8701); + rtl8168_mdio_write(tp, 0x06, 0xd481); + rtl8168_mdio_write(tp, 0x06, 0x35e4); + rtl8168_mdio_write(tp, 0x06, 0x8b94); + rtl8168_mdio_write(tp, 0x06, 0xe58b); + rtl8168_mdio_write(tp, 0x06, 0x95bf); + rtl8168_mdio_write(tp, 0x06, 0x8b88); + rtl8168_mdio_write(tp, 0x06, 0xec00); + rtl8168_mdio_write(tp, 0x06, 0x19a9); + rtl8168_mdio_write(tp, 0x06, 0x8b90); + rtl8168_mdio_write(tp, 0x06, 0xf9ee); + rtl8168_mdio_write(tp, 0x06, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0xfff7); + rtl8168_mdio_write(tp, 0x06, 0xffe0); + rtl8168_mdio_write(tp, 0x06, 0xe140); + rtl8168_mdio_write(tp, 0x06, 0xe1e1); + rtl8168_mdio_write(tp, 0x06, 0x41f7); + rtl8168_mdio_write(tp, 0x06, 0x2ff6); + rtl8168_mdio_write(tp, 0x06, 0x28e4); + rtl8168_mdio_write(tp, 0x06, 0xe140); + rtl8168_mdio_write(tp, 0x06, 0xe5e1); + rtl8168_mdio_write(tp, 0x06, 0x4104); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b89); + rtl8168_mdio_write(tp, 0x06, 0xad20); + rtl8168_mdio_write(tp, 0x06, 0x0dee); + rtl8168_mdio_write(tp, 0x06, 0x8b89); + rtl8168_mdio_write(tp, 0x06, 0x0002); + rtl8168_mdio_write(tp, 0x06, 0x82f4); + rtl8168_mdio_write(tp, 0x06, 0x021f); + rtl8168_mdio_write(tp, 0x06, 0x4102); + rtl8168_mdio_write(tp, 0x06, 0x2812); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b8d); + rtl8168_mdio_write(tp, 0x06, 0xad20); + rtl8168_mdio_write(tp, 0x06, 0x10ee); + rtl8168_mdio_write(tp, 0x06, 0x8b8d); + rtl8168_mdio_write(tp, 0x06, 0x0002); + rtl8168_mdio_write(tp, 0x06, 0x139d); + rtl8168_mdio_write(tp, 0x06, 0x0281); + rtl8168_mdio_write(tp, 0x06, 0xd602); + rtl8168_mdio_write(tp, 0x06, 0x1f99); + rtl8168_mdio_write(tp, 0x06, 0x0227); + rtl8168_mdio_write(tp, 0x06, 0xeafc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x2014); + rtl8168_mdio_write(tp, 0x06, 0xf620); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8e02); + rtl8168_mdio_write(tp, 0x06, 0x8104); + rtl8168_mdio_write(tp, 0x06, 0x021b); + rtl8168_mdio_write(tp, 0x06, 0xf402); + rtl8168_mdio_write(tp, 0x06, 0x2c9c); + rtl8168_mdio_write(tp, 0x06, 0x0281); + rtl8168_mdio_write(tp, 0x06, 0x7902); + rtl8168_mdio_write(tp, 0x06, 0x8443); + rtl8168_mdio_write(tp, 0x06, 0xad22); + rtl8168_mdio_write(tp, 0x06, 0x11f6); + rtl8168_mdio_write(tp, 0x06, 0x22e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0x022c); + rtl8168_mdio_write(tp, 0x06, 0x4602); + rtl8168_mdio_write(tp, 0x06, 0x2ac5); + rtl8168_mdio_write(tp, 0x06, 0x0229); + rtl8168_mdio_write(tp, 0x06, 0x2002); + rtl8168_mdio_write(tp, 0x06, 0x2b91); + rtl8168_mdio_write(tp, 0x06, 0xad25); + rtl8168_mdio_write(tp, 0x06, 0x11f6); + rtl8168_mdio_write(tp, 0x06, 0x25e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0x0284); + rtl8168_mdio_write(tp, 0x06, 0xe202); + rtl8168_mdio_write(tp, 0x06, 0x043a); + rtl8168_mdio_write(tp, 0x06, 0x021a); + rtl8168_mdio_write(tp, 0x06, 0x5902); + rtl8168_mdio_write(tp, 0x06, 0x2bfc); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x00e1); + rtl8168_mdio_write(tp, 0x06, 0xe001); + rtl8168_mdio_write(tp, 0x06, 0xad27); + rtl8168_mdio_write(tp, 0x06, 0x1fd1); + rtl8168_mdio_write(tp, 0x06, 0x01bf); + rtl8168_mdio_write(tp, 0x06, 0x8638); + rtl8168_mdio_write(tp, 0x06, 0x022f); + rtl8168_mdio_write(tp, 0x06, 0x50e0); + rtl8168_mdio_write(tp, 0x06, 0xe020); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x21ad); + rtl8168_mdio_write(tp, 0x06, 0x200e); + rtl8168_mdio_write(tp, 0x06, 0xd100); + rtl8168_mdio_write(tp, 0x06, 0xbf86); + rtl8168_mdio_write(tp, 0x06, 0x3802); + rtl8168_mdio_write(tp, 0x06, 0x2f50); + rtl8168_mdio_write(tp, 0x06, 0xbf3d); + rtl8168_mdio_write(tp, 0x06, 0x3902); + rtl8168_mdio_write(tp, 0x06, 0x2eb0); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfefc); + rtl8168_mdio_write(tp, 0x06, 0x0402); + rtl8168_mdio_write(tp, 0x06, 0x8591); + rtl8168_mdio_write(tp, 0x06, 0x0281); + rtl8168_mdio_write(tp, 0x06, 0x3c05); + rtl8168_mdio_write(tp, 0x06, 0xf8fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xe0e2); + rtl8168_mdio_write(tp, 0x06, 0xfee1); + rtl8168_mdio_write(tp, 0x06, 0xe2ff); + rtl8168_mdio_write(tp, 0x06, 0xad2d); + rtl8168_mdio_write(tp, 0x06, 0x1ae0); + rtl8168_mdio_write(tp, 0x06, 0xe14e); + rtl8168_mdio_write(tp, 0x06, 0xe1e1); + rtl8168_mdio_write(tp, 0x06, 0x4fac); + rtl8168_mdio_write(tp, 0x06, 0x2d22); + rtl8168_mdio_write(tp, 0x06, 0xf603); + rtl8168_mdio_write(tp, 0x06, 0x0203); + rtl8168_mdio_write(tp, 0x06, 0x36f7); + rtl8168_mdio_write(tp, 0x06, 0x03f7); + rtl8168_mdio_write(tp, 0x06, 0x06bf); + rtl8168_mdio_write(tp, 0x06, 0x8622); + rtl8168_mdio_write(tp, 0x06, 0x022e); + rtl8168_mdio_write(tp, 0x06, 0xb0ae); + rtl8168_mdio_write(tp, 0x06, 0x11e0); + rtl8168_mdio_write(tp, 0x06, 0xe14e); + rtl8168_mdio_write(tp, 0x06, 0xe1e1); + rtl8168_mdio_write(tp, 0x06, 0x4fad); + rtl8168_mdio_write(tp, 0x06, 0x2d08); + rtl8168_mdio_write(tp, 0x06, 0xbf86); + rtl8168_mdio_write(tp, 0x06, 0x2d02); + rtl8168_mdio_write(tp, 0x06, 0x2eb0); + rtl8168_mdio_write(tp, 0x06, 0xf606); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfefc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xf9fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x87ad); + rtl8168_mdio_write(tp, 0x06, 0x204c); + rtl8168_mdio_write(tp, 0x06, 0xd200); + rtl8168_mdio_write(tp, 0x06, 0xe0e2); + rtl8168_mdio_write(tp, 0x06, 0x0058); + rtl8168_mdio_write(tp, 0x06, 0x010c); + rtl8168_mdio_write(tp, 0x06, 0x021e); + rtl8168_mdio_write(tp, 0x06, 0x20e0); + rtl8168_mdio_write(tp, 0x06, 0xe000); + rtl8168_mdio_write(tp, 0x06, 0x5810); + rtl8168_mdio_write(tp, 0x06, 0x1e20); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x3658); + rtl8168_mdio_write(tp, 0x06, 0x031e); + rtl8168_mdio_write(tp, 0x06, 0x20e0); + rtl8168_mdio_write(tp, 0x06, 0xe022); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x2358); + rtl8168_mdio_write(tp, 0x06, 0xe01e); + rtl8168_mdio_write(tp, 0x06, 0x20e0); + rtl8168_mdio_write(tp, 0x06, 0x8ae6); + rtl8168_mdio_write(tp, 0x06, 0x1f02); + rtl8168_mdio_write(tp, 0x06, 0x9e22); + rtl8168_mdio_write(tp, 0x06, 0xe68a); + rtl8168_mdio_write(tp, 0x06, 0xe6ad); + rtl8168_mdio_write(tp, 0x06, 0x3214); + rtl8168_mdio_write(tp, 0x06, 0xad34); + rtl8168_mdio_write(tp, 0x06, 0x11ef); + rtl8168_mdio_write(tp, 0x06, 0x0258); + rtl8168_mdio_write(tp, 0x06, 0x039e); + rtl8168_mdio_write(tp, 0x06, 0x07ad); + rtl8168_mdio_write(tp, 0x06, 0x3508); + rtl8168_mdio_write(tp, 0x06, 0x5ac0); + rtl8168_mdio_write(tp, 0x06, 0x9f04); + rtl8168_mdio_write(tp, 0x06, 0xd101); + rtl8168_mdio_write(tp, 0x06, 0xae02); + rtl8168_mdio_write(tp, 0x06, 0xd100); + rtl8168_mdio_write(tp, 0x06, 0xbf86); + rtl8168_mdio_write(tp, 0x06, 0x3e02); + rtl8168_mdio_write(tp, 0x06, 0x2f50); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfefd); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8f9); + rtl8168_mdio_write(tp, 0x06, 0xfae0); + rtl8168_mdio_write(tp, 0x06, 0x8b81); + rtl8168_mdio_write(tp, 0x06, 0xac26); + rtl8168_mdio_write(tp, 0x06, 0x0ee0); + rtl8168_mdio_write(tp, 0x06, 0x8b81); + rtl8168_mdio_write(tp, 0x06, 0xac21); + rtl8168_mdio_write(tp, 0x06, 0x08e0); + rtl8168_mdio_write(tp, 0x06, 0x8b87); + rtl8168_mdio_write(tp, 0x06, 0xac24); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0x6bee); + rtl8168_mdio_write(tp, 0x06, 0xe0ea); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0xe0eb); + rtl8168_mdio_write(tp, 0x06, 0x00e2); + rtl8168_mdio_write(tp, 0x06, 0xe07c); + rtl8168_mdio_write(tp, 0x06, 0xe3e0); + rtl8168_mdio_write(tp, 0x06, 0x7da5); + rtl8168_mdio_write(tp, 0x06, 0x1111); + rtl8168_mdio_write(tp, 0x06, 0x15d2); + rtl8168_mdio_write(tp, 0x06, 0x60d6); + rtl8168_mdio_write(tp, 0x06, 0x6666); + rtl8168_mdio_write(tp, 0x06, 0x0207); + rtl8168_mdio_write(tp, 0x06, 0xf9d2); + rtl8168_mdio_write(tp, 0x06, 0xa0d6); + rtl8168_mdio_write(tp, 0x06, 0xaaaa); + rtl8168_mdio_write(tp, 0x06, 0x0207); + rtl8168_mdio_write(tp, 0x06, 0xf902); + rtl8168_mdio_write(tp, 0x06, 0x825c); + rtl8168_mdio_write(tp, 0x06, 0xae44); + rtl8168_mdio_write(tp, 0x06, 0xa566); + rtl8168_mdio_write(tp, 0x06, 0x6602); + rtl8168_mdio_write(tp, 0x06, 0xae38); + rtl8168_mdio_write(tp, 0x06, 0xa5aa); + rtl8168_mdio_write(tp, 0x06, 0xaa02); + rtl8168_mdio_write(tp, 0x06, 0xae32); + rtl8168_mdio_write(tp, 0x06, 0xeee0); + rtl8168_mdio_write(tp, 0x06, 0xea04); + rtl8168_mdio_write(tp, 0x06, 0xeee0); + rtl8168_mdio_write(tp, 0x06, 0xeb06); + rtl8168_mdio_write(tp, 0x06, 0xe2e0); + rtl8168_mdio_write(tp, 0x06, 0x7ce3); + rtl8168_mdio_write(tp, 0x06, 0xe07d); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x38e1); + rtl8168_mdio_write(tp, 0x06, 0xe039); + rtl8168_mdio_write(tp, 0x06, 0xad2e); + rtl8168_mdio_write(tp, 0x06, 0x21ad); + rtl8168_mdio_write(tp, 0x06, 0x3f13); + rtl8168_mdio_write(tp, 0x06, 0xe0e4); + rtl8168_mdio_write(tp, 0x06, 0x14e1); + rtl8168_mdio_write(tp, 0x06, 0xe415); + rtl8168_mdio_write(tp, 0x06, 0x6880); + rtl8168_mdio_write(tp, 0x06, 0xe4e4); + rtl8168_mdio_write(tp, 0x06, 0x14e5); + rtl8168_mdio_write(tp, 0x06, 0xe415); + rtl8168_mdio_write(tp, 0x06, 0x0282); + rtl8168_mdio_write(tp, 0x06, 0x5cae); + rtl8168_mdio_write(tp, 0x06, 0x0bac); + rtl8168_mdio_write(tp, 0x06, 0x3e02); + rtl8168_mdio_write(tp, 0x06, 0xae06); + rtl8168_mdio_write(tp, 0x06, 0x0282); + rtl8168_mdio_write(tp, 0x06, 0x8602); + rtl8168_mdio_write(tp, 0x06, 0x82b0); + rtl8168_mdio_write(tp, 0x06, 0xfefd); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e1); + rtl8168_mdio_write(tp, 0x06, 0x8b2e); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x81ad); + rtl8168_mdio_write(tp, 0x06, 0x2605); + rtl8168_mdio_write(tp, 0x06, 0x0221); + rtl8168_mdio_write(tp, 0x06, 0xf3f7); + rtl8168_mdio_write(tp, 0x06, 0x28e0); + rtl8168_mdio_write(tp, 0x06, 0x8b81); + rtl8168_mdio_write(tp, 0x06, 0xad21); + rtl8168_mdio_write(tp, 0x06, 0x0502); + rtl8168_mdio_write(tp, 0x06, 0x22f8); + rtl8168_mdio_write(tp, 0x06, 0xf729); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x87ad); + rtl8168_mdio_write(tp, 0x06, 0x2405); + rtl8168_mdio_write(tp, 0x06, 0x0282); + rtl8168_mdio_write(tp, 0x06, 0xebf7); + rtl8168_mdio_write(tp, 0x06, 0x2ae5); + rtl8168_mdio_write(tp, 0x06, 0x8b2e); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b81); + rtl8168_mdio_write(tp, 0x06, 0xad26); + rtl8168_mdio_write(tp, 0x06, 0x0302); + rtl8168_mdio_write(tp, 0x06, 0x2134); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x81ad); + rtl8168_mdio_write(tp, 0x06, 0x2109); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x2eac); + rtl8168_mdio_write(tp, 0x06, 0x2003); + rtl8168_mdio_write(tp, 0x06, 0x0283); + rtl8168_mdio_write(tp, 0x06, 0x52e0); + rtl8168_mdio_write(tp, 0x06, 0x8b87); + rtl8168_mdio_write(tp, 0x06, 0xad24); + rtl8168_mdio_write(tp, 0x06, 0x09e0); + rtl8168_mdio_write(tp, 0x06, 0x8b2e); + rtl8168_mdio_write(tp, 0x06, 0xac21); + rtl8168_mdio_write(tp, 0x06, 0x0302); + rtl8168_mdio_write(tp, 0x06, 0x8337); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e1); + rtl8168_mdio_write(tp, 0x06, 0x8b2e); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x81ad); + rtl8168_mdio_write(tp, 0x06, 0x2608); + rtl8168_mdio_write(tp, 0x06, 0xe085); + rtl8168_mdio_write(tp, 0x06, 0xd2ad); + rtl8168_mdio_write(tp, 0x06, 0x2502); + rtl8168_mdio_write(tp, 0x06, 0xf628); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x81ad); + rtl8168_mdio_write(tp, 0x06, 0x210a); + rtl8168_mdio_write(tp, 0x06, 0xe086); + rtl8168_mdio_write(tp, 0x06, 0x0af6); + rtl8168_mdio_write(tp, 0x06, 0x27a0); + rtl8168_mdio_write(tp, 0x06, 0x0502); + rtl8168_mdio_write(tp, 0x06, 0xf629); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x87ad); + rtl8168_mdio_write(tp, 0x06, 0x2408); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0xedad); + rtl8168_mdio_write(tp, 0x06, 0x2002); + rtl8168_mdio_write(tp, 0x06, 0xf62a); + rtl8168_mdio_write(tp, 0x06, 0xe58b); + rtl8168_mdio_write(tp, 0x06, 0x2ea1); + rtl8168_mdio_write(tp, 0x06, 0x0003); + rtl8168_mdio_write(tp, 0x06, 0x0221); + rtl8168_mdio_write(tp, 0x06, 0x11fc); + rtl8168_mdio_write(tp, 0x06, 0x04ee); + rtl8168_mdio_write(tp, 0x06, 0x8aed); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0x8aec); + rtl8168_mdio_write(tp, 0x06, 0x0004); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b87); + rtl8168_mdio_write(tp, 0x06, 0xad24); + rtl8168_mdio_write(tp, 0x06, 0x3ae0); + rtl8168_mdio_write(tp, 0x06, 0xe0ea); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0xeb58); + rtl8168_mdio_write(tp, 0x06, 0xf8d1); + rtl8168_mdio_write(tp, 0x06, 0x01e4); + rtl8168_mdio_write(tp, 0x06, 0xe0ea); + rtl8168_mdio_write(tp, 0x06, 0xe5e0); + rtl8168_mdio_write(tp, 0x06, 0xebe0); + rtl8168_mdio_write(tp, 0x06, 0xe07c); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x7d5c); + rtl8168_mdio_write(tp, 0x06, 0x00ff); + rtl8168_mdio_write(tp, 0x06, 0x3c00); + rtl8168_mdio_write(tp, 0x06, 0x1eab); + rtl8168_mdio_write(tp, 0x06, 0x1ce0); + rtl8168_mdio_write(tp, 0x06, 0xe04c); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x4d58); + rtl8168_mdio_write(tp, 0x06, 0xc1e4); + rtl8168_mdio_write(tp, 0x06, 0xe04c); + rtl8168_mdio_write(tp, 0x06, 0xe5e0); + rtl8168_mdio_write(tp, 0x06, 0x4de0); + rtl8168_mdio_write(tp, 0x06, 0xe0ee); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0x3ce4); + rtl8168_mdio_write(tp, 0x06, 0xe0ee); + rtl8168_mdio_write(tp, 0x06, 0xe5e0); + rtl8168_mdio_write(tp, 0x06, 0xeffc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x87ad); + rtl8168_mdio_write(tp, 0x06, 0x2412); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0xeee1); + rtl8168_mdio_write(tp, 0x06, 0xe0ef); + rtl8168_mdio_write(tp, 0x06, 0x59c3); + rtl8168_mdio_write(tp, 0x06, 0xe4e0); + rtl8168_mdio_write(tp, 0x06, 0xeee5); + rtl8168_mdio_write(tp, 0x06, 0xe0ef); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0xed01); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b81); + rtl8168_mdio_write(tp, 0x06, 0xac25); + rtl8168_mdio_write(tp, 0x06, 0x0502); + rtl8168_mdio_write(tp, 0x06, 0x8363); + rtl8168_mdio_write(tp, 0x06, 0xae03); + rtl8168_mdio_write(tp, 0x06, 0x0225); + rtl8168_mdio_write(tp, 0x06, 0x16fc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xf9fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xfae0); + rtl8168_mdio_write(tp, 0x06, 0x860a); + rtl8168_mdio_write(tp, 0x06, 0xa000); + rtl8168_mdio_write(tp, 0x06, 0x19e0); + rtl8168_mdio_write(tp, 0x06, 0x860b); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x331b); + rtl8168_mdio_write(tp, 0x06, 0x109e); + rtl8168_mdio_write(tp, 0x06, 0x04aa); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0x06ee); + rtl8168_mdio_write(tp, 0x06, 0x860a); + rtl8168_mdio_write(tp, 0x06, 0x01ae); + rtl8168_mdio_write(tp, 0x06, 0xe602); + rtl8168_mdio_write(tp, 0x06, 0x241e); + rtl8168_mdio_write(tp, 0x06, 0xae14); + rtl8168_mdio_write(tp, 0x06, 0xa001); + rtl8168_mdio_write(tp, 0x06, 0x1402); + rtl8168_mdio_write(tp, 0x06, 0x2426); + rtl8168_mdio_write(tp, 0x06, 0xbf26); + rtl8168_mdio_write(tp, 0x06, 0x6d02); + rtl8168_mdio_write(tp, 0x06, 0x2eb0); + rtl8168_mdio_write(tp, 0x06, 0xee86); + rtl8168_mdio_write(tp, 0x06, 0x0b00); + rtl8168_mdio_write(tp, 0x06, 0xee86); + rtl8168_mdio_write(tp, 0x06, 0x0a02); + rtl8168_mdio_write(tp, 0x06, 0xaf84); + rtl8168_mdio_write(tp, 0x06, 0x3ca0); + rtl8168_mdio_write(tp, 0x06, 0x0252); + rtl8168_mdio_write(tp, 0x06, 0xee86); + rtl8168_mdio_write(tp, 0x06, 0x0400); + rtl8168_mdio_write(tp, 0x06, 0xee86); + rtl8168_mdio_write(tp, 0x06, 0x0500); + rtl8168_mdio_write(tp, 0x06, 0xe086); + rtl8168_mdio_write(tp, 0x06, 0x0be1); + rtl8168_mdio_write(tp, 0x06, 0x8b32); + rtl8168_mdio_write(tp, 0x06, 0x1b10); + rtl8168_mdio_write(tp, 0x06, 0x9e04); + rtl8168_mdio_write(tp, 0x06, 0xaa02); + rtl8168_mdio_write(tp, 0x06, 0xaecb); + rtl8168_mdio_write(tp, 0x06, 0xee86); + rtl8168_mdio_write(tp, 0x06, 0x0b00); + rtl8168_mdio_write(tp, 0x06, 0x0224); + rtl8168_mdio_write(tp, 0x06, 0x3ae2); + rtl8168_mdio_write(tp, 0x06, 0x8604); + rtl8168_mdio_write(tp, 0x06, 0xe386); + rtl8168_mdio_write(tp, 0x06, 0x05ef); + rtl8168_mdio_write(tp, 0x06, 0x65e2); + rtl8168_mdio_write(tp, 0x06, 0x8606); + rtl8168_mdio_write(tp, 0x06, 0xe386); + rtl8168_mdio_write(tp, 0x06, 0x071b); + rtl8168_mdio_write(tp, 0x06, 0x56aa); + rtl8168_mdio_write(tp, 0x06, 0x0eef); + rtl8168_mdio_write(tp, 0x06, 0x56e6); + rtl8168_mdio_write(tp, 0x06, 0x8606); + rtl8168_mdio_write(tp, 0x06, 0xe786); + rtl8168_mdio_write(tp, 0x06, 0x07e2); + rtl8168_mdio_write(tp, 0x06, 0x8609); + rtl8168_mdio_write(tp, 0x06, 0xe686); + rtl8168_mdio_write(tp, 0x06, 0x08e0); + rtl8168_mdio_write(tp, 0x06, 0x8609); + rtl8168_mdio_write(tp, 0x06, 0xa000); + rtl8168_mdio_write(tp, 0x06, 0x07ee); + rtl8168_mdio_write(tp, 0x06, 0x860a); + rtl8168_mdio_write(tp, 0x06, 0x03af); + rtl8168_mdio_write(tp, 0x06, 0x8369); + rtl8168_mdio_write(tp, 0x06, 0x0224); + rtl8168_mdio_write(tp, 0x06, 0x8e02); + rtl8168_mdio_write(tp, 0x06, 0x2426); + rtl8168_mdio_write(tp, 0x06, 0xae48); + rtl8168_mdio_write(tp, 0x06, 0xa003); + rtl8168_mdio_write(tp, 0x06, 0x21e0); + rtl8168_mdio_write(tp, 0x06, 0x8608); + rtl8168_mdio_write(tp, 0x06, 0xe186); + rtl8168_mdio_write(tp, 0x06, 0x091b); + rtl8168_mdio_write(tp, 0x06, 0x019e); + rtl8168_mdio_write(tp, 0x06, 0x0caa); + rtl8168_mdio_write(tp, 0x06, 0x0502); + rtl8168_mdio_write(tp, 0x06, 0x249d); + rtl8168_mdio_write(tp, 0x06, 0xaee7); + rtl8168_mdio_write(tp, 0x06, 0x0224); + rtl8168_mdio_write(tp, 0x06, 0x8eae); + rtl8168_mdio_write(tp, 0x06, 0xe2ee); + rtl8168_mdio_write(tp, 0x06, 0x860a); + rtl8168_mdio_write(tp, 0x06, 0x04ee); + rtl8168_mdio_write(tp, 0x06, 0x860b); + rtl8168_mdio_write(tp, 0x06, 0x00af); + rtl8168_mdio_write(tp, 0x06, 0x8369); + rtl8168_mdio_write(tp, 0x06, 0xa004); + rtl8168_mdio_write(tp, 0x06, 0x15e0); + rtl8168_mdio_write(tp, 0x06, 0x860b); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x341b); + rtl8168_mdio_write(tp, 0x06, 0x109e); + rtl8168_mdio_write(tp, 0x06, 0x05aa); + rtl8168_mdio_write(tp, 0x06, 0x03af); + rtl8168_mdio_write(tp, 0x06, 0x8383); + rtl8168_mdio_write(tp, 0x06, 0xee86); + rtl8168_mdio_write(tp, 0x06, 0x0a05); + rtl8168_mdio_write(tp, 0x06, 0xae0c); + rtl8168_mdio_write(tp, 0x06, 0xa005); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0x0702); + rtl8168_mdio_write(tp, 0x06, 0x2309); + rtl8168_mdio_write(tp, 0x06, 0xee86); + rtl8168_mdio_write(tp, 0x06, 0x0a00); + rtl8168_mdio_write(tp, 0x06, 0xfeef); + rtl8168_mdio_write(tp, 0x06, 0x96fe); + rtl8168_mdio_write(tp, 0x06, 0xfdfc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xf9fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xfbe0); + rtl8168_mdio_write(tp, 0x06, 0x8b85); + rtl8168_mdio_write(tp, 0x06, 0xad25); + rtl8168_mdio_write(tp, 0x06, 0x22e0); + rtl8168_mdio_write(tp, 0x06, 0xe022); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x23e2); + rtl8168_mdio_write(tp, 0x06, 0xe036); + rtl8168_mdio_write(tp, 0x06, 0xe3e0); + rtl8168_mdio_write(tp, 0x06, 0x375a); + rtl8168_mdio_write(tp, 0x06, 0xc40d); + rtl8168_mdio_write(tp, 0x06, 0x0158); + rtl8168_mdio_write(tp, 0x06, 0x021e); + rtl8168_mdio_write(tp, 0x06, 0x20e3); + rtl8168_mdio_write(tp, 0x06, 0x8ae7); + rtl8168_mdio_write(tp, 0x06, 0xac31); + rtl8168_mdio_write(tp, 0x06, 0x60ac); + rtl8168_mdio_write(tp, 0x06, 0x3a08); + rtl8168_mdio_write(tp, 0x06, 0xac3e); + rtl8168_mdio_write(tp, 0x06, 0x26ae); + rtl8168_mdio_write(tp, 0x06, 0x67af); + rtl8168_mdio_write(tp, 0x06, 0x84db); + rtl8168_mdio_write(tp, 0x06, 0xad37); + rtl8168_mdio_write(tp, 0x06, 0x61e0); + rtl8168_mdio_write(tp, 0x06, 0x8ae8); + rtl8168_mdio_write(tp, 0x06, 0x10e4); + rtl8168_mdio_write(tp, 0x06, 0x8ae8); + rtl8168_mdio_write(tp, 0x06, 0xe18a); + rtl8168_mdio_write(tp, 0x06, 0xe91b); + rtl8168_mdio_write(tp, 0x06, 0x109e); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0x51d1); + rtl8168_mdio_write(tp, 0x06, 0x00bf); + rtl8168_mdio_write(tp, 0x06, 0x863b); + rtl8168_mdio_write(tp, 0x06, 0x022f); + rtl8168_mdio_write(tp, 0x06, 0x50ee); + rtl8168_mdio_write(tp, 0x06, 0x8ae8); + rtl8168_mdio_write(tp, 0x06, 0x00ae); + rtl8168_mdio_write(tp, 0x06, 0x43ad); + rtl8168_mdio_write(tp, 0x06, 0x3627); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0xeee1); + rtl8168_mdio_write(tp, 0x06, 0x8aef); + rtl8168_mdio_write(tp, 0x06, 0xef74); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0xeae1); + rtl8168_mdio_write(tp, 0x06, 0x8aeb); + rtl8168_mdio_write(tp, 0x06, 0x1b74); + rtl8168_mdio_write(tp, 0x06, 0x9e2e); + rtl8168_mdio_write(tp, 0x06, 0x14e4); + rtl8168_mdio_write(tp, 0x06, 0x8aea); + rtl8168_mdio_write(tp, 0x06, 0xe58a); + rtl8168_mdio_write(tp, 0x06, 0xebef); + rtl8168_mdio_write(tp, 0x06, 0x74e0); + rtl8168_mdio_write(tp, 0x06, 0x8aee); + rtl8168_mdio_write(tp, 0x06, 0xe18a); + rtl8168_mdio_write(tp, 0x06, 0xef1b); + rtl8168_mdio_write(tp, 0x06, 0x479e); + rtl8168_mdio_write(tp, 0x06, 0x0fae); + rtl8168_mdio_write(tp, 0x06, 0x19ee); + rtl8168_mdio_write(tp, 0x06, 0x8aea); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0x8aeb); + rtl8168_mdio_write(tp, 0x06, 0x00ae); + rtl8168_mdio_write(tp, 0x06, 0x0fac); + rtl8168_mdio_write(tp, 0x06, 0x390c); + rtl8168_mdio_write(tp, 0x06, 0xd101); + rtl8168_mdio_write(tp, 0x06, 0xbf86); + rtl8168_mdio_write(tp, 0x06, 0x3b02); + rtl8168_mdio_write(tp, 0x06, 0x2f50); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0xe800); + rtl8168_mdio_write(tp, 0x06, 0xe68a); + rtl8168_mdio_write(tp, 0x06, 0xe7ff); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfefd); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8f9); + rtl8168_mdio_write(tp, 0x06, 0xfaef); + rtl8168_mdio_write(tp, 0x06, 0x69e0); + rtl8168_mdio_write(tp, 0x06, 0xe022); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x2358); + rtl8168_mdio_write(tp, 0x06, 0xc4e1); + rtl8168_mdio_write(tp, 0x06, 0x8b6e); + rtl8168_mdio_write(tp, 0x06, 0x1f10); + rtl8168_mdio_write(tp, 0x06, 0x9e24); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x6ead); + rtl8168_mdio_write(tp, 0x06, 0x2218); + rtl8168_mdio_write(tp, 0x06, 0xac27); + rtl8168_mdio_write(tp, 0x06, 0x0dac); + rtl8168_mdio_write(tp, 0x06, 0x2605); + rtl8168_mdio_write(tp, 0x06, 0x0203); + rtl8168_mdio_write(tp, 0x06, 0x8fae); + rtl8168_mdio_write(tp, 0x06, 0x1302); + rtl8168_mdio_write(tp, 0x06, 0x03c8); + rtl8168_mdio_write(tp, 0x06, 0xae0e); + rtl8168_mdio_write(tp, 0x06, 0x0203); + rtl8168_mdio_write(tp, 0x06, 0xe102); + rtl8168_mdio_write(tp, 0x06, 0x8520); + rtl8168_mdio_write(tp, 0x06, 0xae06); + rtl8168_mdio_write(tp, 0x06, 0x0203); + rtl8168_mdio_write(tp, 0x06, 0x8f02); + rtl8168_mdio_write(tp, 0x06, 0x8566); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfefd); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x82ad); + rtl8168_mdio_write(tp, 0x06, 0x2737); + rtl8168_mdio_write(tp, 0x06, 0xbf86); + rtl8168_mdio_write(tp, 0x06, 0x4402); + rtl8168_mdio_write(tp, 0x06, 0x2f23); + rtl8168_mdio_write(tp, 0x06, 0xac28); + rtl8168_mdio_write(tp, 0x06, 0x2ed1); + rtl8168_mdio_write(tp, 0x06, 0x01bf); + rtl8168_mdio_write(tp, 0x06, 0x8647); + rtl8168_mdio_write(tp, 0x06, 0x022f); + rtl8168_mdio_write(tp, 0x06, 0x50bf); + rtl8168_mdio_write(tp, 0x06, 0x8641); + rtl8168_mdio_write(tp, 0x06, 0x022f); + rtl8168_mdio_write(tp, 0x06, 0x23e5); + rtl8168_mdio_write(tp, 0x06, 0x8af0); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x22e1); + rtl8168_mdio_write(tp, 0x06, 0xe023); + rtl8168_mdio_write(tp, 0x06, 0xac2e); + rtl8168_mdio_write(tp, 0x06, 0x04d1); + rtl8168_mdio_write(tp, 0x06, 0x01ae); + rtl8168_mdio_write(tp, 0x06, 0x02d1); + rtl8168_mdio_write(tp, 0x06, 0x00bf); + rtl8168_mdio_write(tp, 0x06, 0x8641); + rtl8168_mdio_write(tp, 0x06, 0x022f); + rtl8168_mdio_write(tp, 0x06, 0x50d1); + rtl8168_mdio_write(tp, 0x06, 0x01bf); + rtl8168_mdio_write(tp, 0x06, 0x8644); + rtl8168_mdio_write(tp, 0x06, 0x022f); + rtl8168_mdio_write(tp, 0x06, 0x50ef); + rtl8168_mdio_write(tp, 0x06, 0x96fe); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xbf86); + rtl8168_mdio_write(tp, 0x06, 0x4702); + rtl8168_mdio_write(tp, 0x06, 0x2f23); + rtl8168_mdio_write(tp, 0x06, 0xad28); + rtl8168_mdio_write(tp, 0x06, 0x19d1); + rtl8168_mdio_write(tp, 0x06, 0x00bf); + rtl8168_mdio_write(tp, 0x06, 0x8644); + rtl8168_mdio_write(tp, 0x06, 0x022f); + rtl8168_mdio_write(tp, 0x06, 0x50e1); + rtl8168_mdio_write(tp, 0x06, 0x8af0); + rtl8168_mdio_write(tp, 0x06, 0xbf86); + rtl8168_mdio_write(tp, 0x06, 0x4102); + rtl8168_mdio_write(tp, 0x06, 0x2f50); + rtl8168_mdio_write(tp, 0x06, 0xd100); + rtl8168_mdio_write(tp, 0x06, 0xbf86); + rtl8168_mdio_write(tp, 0x06, 0x4702); + rtl8168_mdio_write(tp, 0x06, 0x2f50); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfefc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xe0e2); + rtl8168_mdio_write(tp, 0x06, 0xfee1); + rtl8168_mdio_write(tp, 0x06, 0xe2ff); + rtl8168_mdio_write(tp, 0x06, 0xad2e); + rtl8168_mdio_write(tp, 0x06, 0x63e0); + rtl8168_mdio_write(tp, 0x06, 0xe038); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x39ad); + rtl8168_mdio_write(tp, 0x06, 0x2f10); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x34e1); + rtl8168_mdio_write(tp, 0x06, 0xe035); + rtl8168_mdio_write(tp, 0x06, 0xf726); + rtl8168_mdio_write(tp, 0x06, 0xe4e0); + rtl8168_mdio_write(tp, 0x06, 0x34e5); + rtl8168_mdio_write(tp, 0x06, 0xe035); + rtl8168_mdio_write(tp, 0x06, 0xae0e); + rtl8168_mdio_write(tp, 0x06, 0xe0e2); + rtl8168_mdio_write(tp, 0x06, 0xd6e1); + rtl8168_mdio_write(tp, 0x06, 0xe2d7); + rtl8168_mdio_write(tp, 0x06, 0xf728); + rtl8168_mdio_write(tp, 0x06, 0xe4e2); + rtl8168_mdio_write(tp, 0x06, 0xd6e5); + rtl8168_mdio_write(tp, 0x06, 0xe2d7); + rtl8168_mdio_write(tp, 0x06, 0xe0e2); + rtl8168_mdio_write(tp, 0x06, 0x34e1); + rtl8168_mdio_write(tp, 0x06, 0xe235); + rtl8168_mdio_write(tp, 0x06, 0xf72b); + rtl8168_mdio_write(tp, 0x06, 0xe4e2); + rtl8168_mdio_write(tp, 0x06, 0x34e5); + rtl8168_mdio_write(tp, 0x06, 0xe235); + rtl8168_mdio_write(tp, 0x06, 0xd07d); + rtl8168_mdio_write(tp, 0x06, 0xb0fe); + rtl8168_mdio_write(tp, 0x06, 0xe0e2); + rtl8168_mdio_write(tp, 0x06, 0x34e1); + rtl8168_mdio_write(tp, 0x06, 0xe235); + rtl8168_mdio_write(tp, 0x06, 0xf62b); + rtl8168_mdio_write(tp, 0x06, 0xe4e2); + rtl8168_mdio_write(tp, 0x06, 0x34e5); + rtl8168_mdio_write(tp, 0x06, 0xe235); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x34e1); + rtl8168_mdio_write(tp, 0x06, 0xe035); + rtl8168_mdio_write(tp, 0x06, 0xf626); + rtl8168_mdio_write(tp, 0x06, 0xe4e0); + rtl8168_mdio_write(tp, 0x06, 0x34e5); + rtl8168_mdio_write(tp, 0x06, 0xe035); + rtl8168_mdio_write(tp, 0x06, 0xe0e2); + rtl8168_mdio_write(tp, 0x06, 0xd6e1); + rtl8168_mdio_write(tp, 0x06, 0xe2d7); + rtl8168_mdio_write(tp, 0x06, 0xf628); + rtl8168_mdio_write(tp, 0x06, 0xe4e2); + rtl8168_mdio_write(tp, 0x06, 0xd6e5); + rtl8168_mdio_write(tp, 0x06, 0xe2d7); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xae20); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0xa725); + rtl8168_mdio_write(tp, 0x06, 0xe50a); + rtl8168_mdio_write(tp, 0x06, 0x1de5); + rtl8168_mdio_write(tp, 0x06, 0x0a2c); + rtl8168_mdio_write(tp, 0x06, 0xe50a); + rtl8168_mdio_write(tp, 0x06, 0x6de5); + rtl8168_mdio_write(tp, 0x06, 0x0a1d); + rtl8168_mdio_write(tp, 0x06, 0xe50a); + rtl8168_mdio_write(tp, 0x06, 0x1ce5); + rtl8168_mdio_write(tp, 0x06, 0x0a2d); + rtl8168_mdio_write(tp, 0x06, 0xa755); + rtl8168_mdio_write(tp, 0x06, 0x00e2); + rtl8168_mdio_write(tp, 0x06, 0x3488); + rtl8168_mdio_write(tp, 0x06, 0xe200); + rtl8168_mdio_write(tp, 0x06, 0xcce2); + rtl8168_mdio_write(tp, 0x06, 0x0055); + rtl8168_mdio_write(tp, 0x06, 0xe020); + rtl8168_mdio_write(tp, 0x06, 0x55e2); + rtl8168_mdio_write(tp, 0x06, 0xd600); + rtl8168_mdio_write(tp, 0x06, 0xe24a); + gphy_val = rtl8168_mdio_read(tp, 0x01); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp, 0x01, gphy_val); + gphy_val = rtl8168_mdio_read(tp, 0x00); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp, 0x00, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + rtl8168_mdio_write(tp, 0x1f, 0x0005); + for (i = 0; i < 200; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x00); + if (gphy_val & BIT_7) + break; + } + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0023); + gphy_val = rtl8168_mdio_read(tp, 0x17); + gphy_val &= ~(BIT_0); + if (tp->RequiredSecLanDonglePatch) + gphy_val &= ~(BIT_2); + rtl8168_mdio_write(tp, 0x17, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + } +} + +static void +rtl8168_set_phy_mcu_8168evl_1(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned int gphy_val,i; + + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x00, 0x1800); + gphy_val = rtl8168_mdio_read(tp, 0x15); + gphy_val &= ~(BIT_12); + rtl8168_mdio_write(tp, 0x15, gphy_val); + mdelay(20); + rtl8168_mdio_write(tp, 0x1f, 0x0004); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0023); + gphy_val = rtl8168_mdio_read(tp, 0x17); + if ((gphy_val & BIT_11) == 0x0000) { + gphy_val |= BIT_0; + rtl8168_mdio_write(tp, 0x17, gphy_val); + for (i = 0; i < 200; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x17); + if (gphy_val & BIT_11) + break; + } + } + gphy_val = rtl8168_mdio_read(tp, 0x17); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp, 0x17, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0004); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x002C); + rtl8168_mdio_write(tp, 0x1B, 0x5000); + rtl8168_mdio_write(tp, 0x1E, 0x002d); + rtl8168_mdio_write(tp, 0x19, 0x0004); + rtl8168_mdio_write(tp, 0x1f, 0x0002); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + for (i = 0; i < 200; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x1E); + if ((gphy_val & 0x03FF) == 0x0014) + break; + } + rtl8168_mdio_write(tp, 0x1f, 0x0005); + for (i = 0; i < 200; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x07); + if ((gphy_val & BIT_5) == 0) + break; + } + gphy_val = rtl8168_mdio_read(tp, 0x07); + if (gphy_val & BIT_5) { + rtl8168_mdio_write(tp, 0x1f, 0x0004); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x00a1); + rtl8168_mdio_write(tp, 0x17, 0x1000); + rtl8168_mdio_write(tp, 0x17, 0x0000); + rtl8168_mdio_write(tp, 0x17, 0x2000); + rtl8168_mdio_write(tp, 0x1e, 0x002f); + rtl8168_mdio_write(tp, 0x18, 0x9bfb); + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x07, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0002); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + } + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x0080); + gphy_val = rtl8168_mdio_read(tp, 0x00); + gphy_val &= ~(BIT_7); + rtl8168_mdio_write(tp, 0x00, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0004); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0023); + rtl8168_mdio_write(tp, 0x16, 0x0306); + rtl8168_mdio_write(tp, 0x16, 0x0307); + rtl8168_mdio_write(tp, 0x15, 0x0000); + rtl8168_mdio_write(tp, 0x19, 0x407d); + rtl8168_mdio_write(tp, 0x15, 0x0001); + rtl8168_mdio_write(tp, 0x19, 0x440f); + rtl8168_mdio_write(tp, 0x15, 0x0002); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x0003); + rtl8168_mdio_write(tp, 0x19, 0x6c03); + rtl8168_mdio_write(tp, 0x15, 0x0004); + rtl8168_mdio_write(tp, 0x19, 0xc4d5); + rtl8168_mdio_write(tp, 0x15, 0x0005); + rtl8168_mdio_write(tp, 0x19, 0x00ff); + rtl8168_mdio_write(tp, 0x15, 0x0006); + rtl8168_mdio_write(tp, 0x19, 0x74f0); + rtl8168_mdio_write(tp, 0x15, 0x0007); + rtl8168_mdio_write(tp, 0x19, 0x4880); + rtl8168_mdio_write(tp, 0x15, 0x0008); + rtl8168_mdio_write(tp, 0x19, 0x4c00); + rtl8168_mdio_write(tp, 0x15, 0x0009); + rtl8168_mdio_write(tp, 0x19, 0x4800); + rtl8168_mdio_write(tp, 0x15, 0x000a); + rtl8168_mdio_write(tp, 0x19, 0x5000); + rtl8168_mdio_write(tp, 0x15, 0x000b); + rtl8168_mdio_write(tp, 0x19, 0x4400); + rtl8168_mdio_write(tp, 0x15, 0x000c); + rtl8168_mdio_write(tp, 0x19, 0x7801); + rtl8168_mdio_write(tp, 0x15, 0x000d); + rtl8168_mdio_write(tp, 0x19, 0x4000); + rtl8168_mdio_write(tp, 0x15, 0x000e); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x000f); + rtl8168_mdio_write(tp, 0x19, 0x7010); + rtl8168_mdio_write(tp, 0x15, 0x0010); + rtl8168_mdio_write(tp, 0x19, 0x6804); + rtl8168_mdio_write(tp, 0x15, 0x0011); + rtl8168_mdio_write(tp, 0x19, 0x64a0); + rtl8168_mdio_write(tp, 0x15, 0x0012); + rtl8168_mdio_write(tp, 0x19, 0x63da); + rtl8168_mdio_write(tp, 0x15, 0x0013); + rtl8168_mdio_write(tp, 0x19, 0x63d8); + rtl8168_mdio_write(tp, 0x15, 0x0014); + rtl8168_mdio_write(tp, 0x19, 0x6f05); + rtl8168_mdio_write(tp, 0x15, 0x0015); + rtl8168_mdio_write(tp, 0x19, 0x5420); + rtl8168_mdio_write(tp, 0x15, 0x0016); + rtl8168_mdio_write(tp, 0x19, 0x58ce); + rtl8168_mdio_write(tp, 0x15, 0x0017); + rtl8168_mdio_write(tp, 0x19, 0x5cf3); + rtl8168_mdio_write(tp, 0x15, 0x0018); + rtl8168_mdio_write(tp, 0x19, 0xb600); + rtl8168_mdio_write(tp, 0x15, 0x0019); + rtl8168_mdio_write(tp, 0x19, 0xc659); + rtl8168_mdio_write(tp, 0x15, 0x001a); + rtl8168_mdio_write(tp, 0x19, 0x0018); + rtl8168_mdio_write(tp, 0x15, 0x001b); + rtl8168_mdio_write(tp, 0x19, 0xc403); + rtl8168_mdio_write(tp, 0x15, 0x001c); + rtl8168_mdio_write(tp, 0x19, 0x0016); + rtl8168_mdio_write(tp, 0x15, 0x001d); + rtl8168_mdio_write(tp, 0x19, 0xaa05); + rtl8168_mdio_write(tp, 0x15, 0x001e); + rtl8168_mdio_write(tp, 0x19, 0xc503); + rtl8168_mdio_write(tp, 0x15, 0x001f); + rtl8168_mdio_write(tp, 0x19, 0x0003); + rtl8168_mdio_write(tp, 0x15, 0x0020); + rtl8168_mdio_write(tp, 0x19, 0x89f8); + rtl8168_mdio_write(tp, 0x15, 0x0021); + rtl8168_mdio_write(tp, 0x19, 0x32ae); + rtl8168_mdio_write(tp, 0x15, 0x0022); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x0023); + rtl8168_mdio_write(tp, 0x19, 0x6c03); + rtl8168_mdio_write(tp, 0x15, 0x0024); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x0025); + rtl8168_mdio_write(tp, 0x19, 0x6801); + rtl8168_mdio_write(tp, 0x15, 0x0026); + rtl8168_mdio_write(tp, 0x19, 0x66a0); + rtl8168_mdio_write(tp, 0x15, 0x0027); + rtl8168_mdio_write(tp, 0x19, 0xa300); + rtl8168_mdio_write(tp, 0x15, 0x0028); + rtl8168_mdio_write(tp, 0x19, 0x64a0); + rtl8168_mdio_write(tp, 0x15, 0x0029); + rtl8168_mdio_write(tp, 0x19, 0x76f0); + rtl8168_mdio_write(tp, 0x15, 0x002a); + rtl8168_mdio_write(tp, 0x19, 0x7670); + rtl8168_mdio_write(tp, 0x15, 0x002b); + rtl8168_mdio_write(tp, 0x19, 0x7630); + rtl8168_mdio_write(tp, 0x15, 0x002c); + rtl8168_mdio_write(tp, 0x19, 0x31a6); + rtl8168_mdio_write(tp, 0x15, 0x002d); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x002e); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x002f); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0030); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0031); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0032); + rtl8168_mdio_write(tp, 0x19, 0x4801); + rtl8168_mdio_write(tp, 0x15, 0x0033); + rtl8168_mdio_write(tp, 0x19, 0x6803); + rtl8168_mdio_write(tp, 0x15, 0x0034); + rtl8168_mdio_write(tp, 0x19, 0x66a1); + rtl8168_mdio_write(tp, 0x15, 0x0035); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x0036); + rtl8168_mdio_write(tp, 0x19, 0x6c03); + rtl8168_mdio_write(tp, 0x15, 0x0037); + rtl8168_mdio_write(tp, 0x19, 0xa300); + rtl8168_mdio_write(tp, 0x15, 0x0038); + rtl8168_mdio_write(tp, 0x19, 0x64a1); + rtl8168_mdio_write(tp, 0x15, 0x0039); + rtl8168_mdio_write(tp, 0x19, 0x7c08); + rtl8168_mdio_write(tp, 0x15, 0x003a); + rtl8168_mdio_write(tp, 0x19, 0x74f8); + rtl8168_mdio_write(tp, 0x15, 0x003b); + rtl8168_mdio_write(tp, 0x19, 0x63d0); + rtl8168_mdio_write(tp, 0x15, 0x003c); + rtl8168_mdio_write(tp, 0x19, 0x7ff0); + rtl8168_mdio_write(tp, 0x15, 0x003d); + rtl8168_mdio_write(tp, 0x19, 0x77f0); + rtl8168_mdio_write(tp, 0x15, 0x003e); + rtl8168_mdio_write(tp, 0x19, 0x7ff0); + rtl8168_mdio_write(tp, 0x15, 0x003f); + rtl8168_mdio_write(tp, 0x19, 0x7750); + rtl8168_mdio_write(tp, 0x15, 0x0040); + rtl8168_mdio_write(tp, 0x19, 0x63d8); + rtl8168_mdio_write(tp, 0x15, 0x0041); + rtl8168_mdio_write(tp, 0x19, 0x7cf0); + rtl8168_mdio_write(tp, 0x15, 0x0042); + rtl8168_mdio_write(tp, 0x19, 0x7708); + rtl8168_mdio_write(tp, 0x15, 0x0043); + rtl8168_mdio_write(tp, 0x19, 0xa654); + rtl8168_mdio_write(tp, 0x15, 0x0044); + rtl8168_mdio_write(tp, 0x19, 0x304a); + rtl8168_mdio_write(tp, 0x15, 0x0045); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0046); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0047); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0048); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0049); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x004a); + rtl8168_mdio_write(tp, 0x19, 0x4802); + rtl8168_mdio_write(tp, 0x15, 0x004b); + rtl8168_mdio_write(tp, 0x19, 0x4003); + rtl8168_mdio_write(tp, 0x15, 0x004c); + rtl8168_mdio_write(tp, 0x19, 0x4440); + rtl8168_mdio_write(tp, 0x15, 0x004d); + rtl8168_mdio_write(tp, 0x19, 0x63c8); + rtl8168_mdio_write(tp, 0x15, 0x004e); + rtl8168_mdio_write(tp, 0x19, 0x6481); + rtl8168_mdio_write(tp, 0x15, 0x004f); + rtl8168_mdio_write(tp, 0x19, 0x9d00); + rtl8168_mdio_write(tp, 0x15, 0x0050); + rtl8168_mdio_write(tp, 0x19, 0x63e8); + rtl8168_mdio_write(tp, 0x15, 0x0051); + rtl8168_mdio_write(tp, 0x19, 0x7d00); + rtl8168_mdio_write(tp, 0x15, 0x0052); + rtl8168_mdio_write(tp, 0x19, 0x5900); + rtl8168_mdio_write(tp, 0x15, 0x0053); + rtl8168_mdio_write(tp, 0x19, 0x63f8); + rtl8168_mdio_write(tp, 0x15, 0x0054); + rtl8168_mdio_write(tp, 0x19, 0x64a1); + rtl8168_mdio_write(tp, 0x15, 0x0055); + rtl8168_mdio_write(tp, 0x19, 0x3116); + rtl8168_mdio_write(tp, 0x15, 0x0056); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0057); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0058); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0059); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x005a); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x005b); + rtl8168_mdio_write(tp, 0x19, 0x6c03); + rtl8168_mdio_write(tp, 0x15, 0x005c); + rtl8168_mdio_write(tp, 0x19, 0x7c08); + rtl8168_mdio_write(tp, 0x15, 0x005d); + rtl8168_mdio_write(tp, 0x19, 0x6000); + rtl8168_mdio_write(tp, 0x15, 0x005e); + rtl8168_mdio_write(tp, 0x19, 0x59ce); + rtl8168_mdio_write(tp, 0x15, 0x005f); + rtl8168_mdio_write(tp, 0x19, 0x4400); + rtl8168_mdio_write(tp, 0x15, 0x0060); + rtl8168_mdio_write(tp, 0x19, 0x7d00); + rtl8168_mdio_write(tp, 0x15, 0x0061); + rtl8168_mdio_write(tp, 0x19, 0x72b0); + rtl8168_mdio_write(tp, 0x15, 0x0062); + rtl8168_mdio_write(tp, 0x19, 0x400e); + rtl8168_mdio_write(tp, 0x15, 0x0063); + rtl8168_mdio_write(tp, 0x19, 0x4440); + rtl8168_mdio_write(tp, 0x15, 0x0064); + rtl8168_mdio_write(tp, 0x19, 0x9d00); + rtl8168_mdio_write(tp, 0x15, 0x0065); + rtl8168_mdio_write(tp, 0x19, 0x7f00); + rtl8168_mdio_write(tp, 0x15, 0x0066); + rtl8168_mdio_write(tp, 0x19, 0x70b0); + rtl8168_mdio_write(tp, 0x15, 0x0067); + rtl8168_mdio_write(tp, 0x19, 0x7c08); + rtl8168_mdio_write(tp, 0x15, 0x0068); + rtl8168_mdio_write(tp, 0x19, 0x6008); + rtl8168_mdio_write(tp, 0x15, 0x0069); + rtl8168_mdio_write(tp, 0x19, 0x7cf0); + rtl8168_mdio_write(tp, 0x15, 0x006a); + rtl8168_mdio_write(tp, 0x19, 0x7750); + rtl8168_mdio_write(tp, 0x15, 0x006b); + rtl8168_mdio_write(tp, 0x19, 0x4007); + rtl8168_mdio_write(tp, 0x15, 0x006c); + rtl8168_mdio_write(tp, 0x19, 0x4500); + rtl8168_mdio_write(tp, 0x15, 0x006d); + rtl8168_mdio_write(tp, 0x19, 0x4023); + rtl8168_mdio_write(tp, 0x15, 0x006e); + rtl8168_mdio_write(tp, 0x19, 0x4580); + rtl8168_mdio_write(tp, 0x15, 0x006f); + rtl8168_mdio_write(tp, 0x19, 0x9f00); + rtl8168_mdio_write(tp, 0x15, 0x0070); + rtl8168_mdio_write(tp, 0x19, 0xcd78); + rtl8168_mdio_write(tp, 0x15, 0x0071); + rtl8168_mdio_write(tp, 0x19, 0x0003); + rtl8168_mdio_write(tp, 0x15, 0x0072); + rtl8168_mdio_write(tp, 0x19, 0xbe02); + rtl8168_mdio_write(tp, 0x15, 0x0073); + rtl8168_mdio_write(tp, 0x19, 0x3070); + rtl8168_mdio_write(tp, 0x15, 0x0074); + rtl8168_mdio_write(tp, 0x19, 0x7cf0); + rtl8168_mdio_write(tp, 0x15, 0x0075); + rtl8168_mdio_write(tp, 0x19, 0x77f0); + rtl8168_mdio_write(tp, 0x15, 0x0076); + rtl8168_mdio_write(tp, 0x19, 0x4400); + rtl8168_mdio_write(tp, 0x15, 0x0077); + rtl8168_mdio_write(tp, 0x19, 0x4007); + rtl8168_mdio_write(tp, 0x15, 0x0078); + rtl8168_mdio_write(tp, 0x19, 0x4500); + rtl8168_mdio_write(tp, 0x15, 0x0079); + rtl8168_mdio_write(tp, 0x19, 0x4023); + rtl8168_mdio_write(tp, 0x15, 0x007a); + rtl8168_mdio_write(tp, 0x19, 0x4580); + rtl8168_mdio_write(tp, 0x15, 0x007b); + rtl8168_mdio_write(tp, 0x19, 0x9f00); + rtl8168_mdio_write(tp, 0x15, 0x007c); + rtl8168_mdio_write(tp, 0x19, 0xce80); + rtl8168_mdio_write(tp, 0x15, 0x007d); + rtl8168_mdio_write(tp, 0x19, 0x0004); + rtl8168_mdio_write(tp, 0x15, 0x007e); + rtl8168_mdio_write(tp, 0x19, 0xce80); + rtl8168_mdio_write(tp, 0x15, 0x007f); + rtl8168_mdio_write(tp, 0x19, 0x0002); + rtl8168_mdio_write(tp, 0x15, 0x0080); + rtl8168_mdio_write(tp, 0x19, 0x307c); + rtl8168_mdio_write(tp, 0x15, 0x0081); + rtl8168_mdio_write(tp, 0x19, 0x4400); + rtl8168_mdio_write(tp, 0x15, 0x0082); + rtl8168_mdio_write(tp, 0x19, 0x480f); + rtl8168_mdio_write(tp, 0x15, 0x0083); + rtl8168_mdio_write(tp, 0x19, 0x6802); + rtl8168_mdio_write(tp, 0x15, 0x0084); + rtl8168_mdio_write(tp, 0x19, 0x6680); + rtl8168_mdio_write(tp, 0x15, 0x0085); + rtl8168_mdio_write(tp, 0x19, 0x7c10); + rtl8168_mdio_write(tp, 0x15, 0x0086); + rtl8168_mdio_write(tp, 0x19, 0x6010); + rtl8168_mdio_write(tp, 0x15, 0x0087); + rtl8168_mdio_write(tp, 0x19, 0x400a); + rtl8168_mdio_write(tp, 0x15, 0x0088); + rtl8168_mdio_write(tp, 0x19, 0x4580); + rtl8168_mdio_write(tp, 0x15, 0x0089); + rtl8168_mdio_write(tp, 0x19, 0x9e00); + rtl8168_mdio_write(tp, 0x15, 0x008a); + rtl8168_mdio_write(tp, 0x19, 0x7d00); + rtl8168_mdio_write(tp, 0x15, 0x008b); + rtl8168_mdio_write(tp, 0x19, 0x5800); + rtl8168_mdio_write(tp, 0x15, 0x008c); + rtl8168_mdio_write(tp, 0x19, 0x63c8); + rtl8168_mdio_write(tp, 0x15, 0x008d); + rtl8168_mdio_write(tp, 0x19, 0x63d8); + rtl8168_mdio_write(tp, 0x15, 0x008e); + rtl8168_mdio_write(tp, 0x19, 0x66a0); + rtl8168_mdio_write(tp, 0x15, 0x008f); + rtl8168_mdio_write(tp, 0x19, 0x8300); + rtl8168_mdio_write(tp, 0x15, 0x0090); + rtl8168_mdio_write(tp, 0x19, 0x7ff0); + rtl8168_mdio_write(tp, 0x15, 0x0091); + rtl8168_mdio_write(tp, 0x19, 0x74f0); + rtl8168_mdio_write(tp, 0x15, 0x0092); + rtl8168_mdio_write(tp, 0x19, 0x3006); + rtl8168_mdio_write(tp, 0x15, 0x0093); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0094); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0095); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0096); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0097); + rtl8168_mdio_write(tp, 0x19, 0x4803); + rtl8168_mdio_write(tp, 0x15, 0x0098); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x0099); + rtl8168_mdio_write(tp, 0x19, 0x6c03); + rtl8168_mdio_write(tp, 0x15, 0x009a); + rtl8168_mdio_write(tp, 0x19, 0xa203); + rtl8168_mdio_write(tp, 0x15, 0x009b); + rtl8168_mdio_write(tp, 0x19, 0x64b1); + rtl8168_mdio_write(tp, 0x15, 0x009c); + rtl8168_mdio_write(tp, 0x19, 0x309e); + rtl8168_mdio_write(tp, 0x15, 0x009d); + rtl8168_mdio_write(tp, 0x19, 0x64b3); + rtl8168_mdio_write(tp, 0x15, 0x009e); + rtl8168_mdio_write(tp, 0x19, 0x4030); + rtl8168_mdio_write(tp, 0x15, 0x009f); + rtl8168_mdio_write(tp, 0x19, 0x440e); + rtl8168_mdio_write(tp, 0x15, 0x00a0); + rtl8168_mdio_write(tp, 0x19, 0x4020); + rtl8168_mdio_write(tp, 0x15, 0x00a1); + rtl8168_mdio_write(tp, 0x19, 0x4419); + rtl8168_mdio_write(tp, 0x15, 0x00a2); + rtl8168_mdio_write(tp, 0x19, 0x7801); + rtl8168_mdio_write(tp, 0x15, 0x00a3); + rtl8168_mdio_write(tp, 0x19, 0xc520); + rtl8168_mdio_write(tp, 0x15, 0x00a4); + rtl8168_mdio_write(tp, 0x19, 0x000b); + rtl8168_mdio_write(tp, 0x15, 0x00a5); + rtl8168_mdio_write(tp, 0x19, 0x4020); + rtl8168_mdio_write(tp, 0x15, 0x00a6); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x00a7); + rtl8168_mdio_write(tp, 0x19, 0x58a4); + rtl8168_mdio_write(tp, 0x15, 0x00a8); + rtl8168_mdio_write(tp, 0x19, 0x63da); + rtl8168_mdio_write(tp, 0x15, 0x00a9); + rtl8168_mdio_write(tp, 0x19, 0x5cb0); + rtl8168_mdio_write(tp, 0x15, 0x00aa); + rtl8168_mdio_write(tp, 0x19, 0x7d00); + rtl8168_mdio_write(tp, 0x15, 0x00ab); + rtl8168_mdio_write(tp, 0x19, 0x72b0); + rtl8168_mdio_write(tp, 0x15, 0x00ac); + rtl8168_mdio_write(tp, 0x19, 0x7f00); + rtl8168_mdio_write(tp, 0x15, 0x00ad); + rtl8168_mdio_write(tp, 0x19, 0x70b0); + rtl8168_mdio_write(tp, 0x15, 0x00ae); + rtl8168_mdio_write(tp, 0x19, 0x30b8); + rtl8168_mdio_write(tp, 0x15, 0x00AF); + rtl8168_mdio_write(tp, 0x19, 0x4060); + rtl8168_mdio_write(tp, 0x15, 0x00B0); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x00B1); + rtl8168_mdio_write(tp, 0x19, 0x7e00); + rtl8168_mdio_write(tp, 0x15, 0x00B2); + rtl8168_mdio_write(tp, 0x19, 0x72B0); + rtl8168_mdio_write(tp, 0x15, 0x00B3); + rtl8168_mdio_write(tp, 0x19, 0x7F00); + rtl8168_mdio_write(tp, 0x15, 0x00B4); + rtl8168_mdio_write(tp, 0x19, 0x73B0); + rtl8168_mdio_write(tp, 0x15, 0x00b5); + rtl8168_mdio_write(tp, 0x19, 0x58a0); + rtl8168_mdio_write(tp, 0x15, 0x00b6); + rtl8168_mdio_write(tp, 0x19, 0x63d2); + rtl8168_mdio_write(tp, 0x15, 0x00b7); + rtl8168_mdio_write(tp, 0x19, 0x5c00); + rtl8168_mdio_write(tp, 0x15, 0x00b8); + rtl8168_mdio_write(tp, 0x19, 0x5780); + rtl8168_mdio_write(tp, 0x15, 0x00b9); + rtl8168_mdio_write(tp, 0x19, 0xb60d); + rtl8168_mdio_write(tp, 0x15, 0x00ba); + rtl8168_mdio_write(tp, 0x19, 0x9bff); + rtl8168_mdio_write(tp, 0x15, 0x00bb); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x00bc); + rtl8168_mdio_write(tp, 0x19, 0x6001); + rtl8168_mdio_write(tp, 0x15, 0x00bd); + rtl8168_mdio_write(tp, 0x19, 0xc020); + rtl8168_mdio_write(tp, 0x15, 0x00be); + rtl8168_mdio_write(tp, 0x19, 0x002b); + rtl8168_mdio_write(tp, 0x15, 0x00bf); + rtl8168_mdio_write(tp, 0x19, 0xc137); + rtl8168_mdio_write(tp, 0x15, 0x00c0); + rtl8168_mdio_write(tp, 0x19, 0x0006); + rtl8168_mdio_write(tp, 0x15, 0x00c1); + rtl8168_mdio_write(tp, 0x19, 0x9af8); + rtl8168_mdio_write(tp, 0x15, 0x00c2); + rtl8168_mdio_write(tp, 0x19, 0x30c6); + rtl8168_mdio_write(tp, 0x15, 0x00c3); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x00c4); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x00c5); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x00c6); + rtl8168_mdio_write(tp, 0x19, 0x7d00); + rtl8168_mdio_write(tp, 0x15, 0x00c7); + rtl8168_mdio_write(tp, 0x19, 0x70b0); + rtl8168_mdio_write(tp, 0x15, 0x00c8); + rtl8168_mdio_write(tp, 0x19, 0x4400); + rtl8168_mdio_write(tp, 0x15, 0x00c9); + rtl8168_mdio_write(tp, 0x19, 0x4804); + rtl8168_mdio_write(tp, 0x15, 0x00ca); + rtl8168_mdio_write(tp, 0x19, 0x7c80); + rtl8168_mdio_write(tp, 0x15, 0x00cb); + rtl8168_mdio_write(tp, 0x19, 0x5c80); + rtl8168_mdio_write(tp, 0x15, 0x00cc); + rtl8168_mdio_write(tp, 0x19, 0x4010); + rtl8168_mdio_write(tp, 0x15, 0x00cd); + rtl8168_mdio_write(tp, 0x19, 0x4415); + rtl8168_mdio_write(tp, 0x15, 0x00ce); + rtl8168_mdio_write(tp, 0x19, 0x9b00); + rtl8168_mdio_write(tp, 0x15, 0x00cf); + rtl8168_mdio_write(tp, 0x19, 0x7f00); + rtl8168_mdio_write(tp, 0x15, 0x00d0); + rtl8168_mdio_write(tp, 0x19, 0x70b0); + rtl8168_mdio_write(tp, 0x15, 0x00d1); + rtl8168_mdio_write(tp, 0x19, 0x3177); + rtl8168_mdio_write(tp, 0x15, 0x00d2); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x00d3); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x00d4); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x00d5); + rtl8168_mdio_write(tp, 0x19, 0x4808); + rtl8168_mdio_write(tp, 0x15, 0x00d6); + rtl8168_mdio_write(tp, 0x19, 0x4007); + rtl8168_mdio_write(tp, 0x15, 0x00d7); + rtl8168_mdio_write(tp, 0x19, 0x4420); + rtl8168_mdio_write(tp, 0x15, 0x00d8); + rtl8168_mdio_write(tp, 0x19, 0x63d8); + rtl8168_mdio_write(tp, 0x15, 0x00d9); + rtl8168_mdio_write(tp, 0x19, 0xb608); + rtl8168_mdio_write(tp, 0x15, 0x00da); + rtl8168_mdio_write(tp, 0x19, 0xbcbd); + rtl8168_mdio_write(tp, 0x15, 0x00db); + rtl8168_mdio_write(tp, 0x19, 0xc60b); + rtl8168_mdio_write(tp, 0x15, 0x00dc); + rtl8168_mdio_write(tp, 0x19, 0x00fd); + rtl8168_mdio_write(tp, 0x15, 0x00dd); + rtl8168_mdio_write(tp, 0x19, 0x30e1); + rtl8168_mdio_write(tp, 0x15, 0x00de); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x00df); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x00e0); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x00e1); + rtl8168_mdio_write(tp, 0x19, 0x4809); + rtl8168_mdio_write(tp, 0x15, 0x00e2); + rtl8168_mdio_write(tp, 0x19, 0x7e40); + rtl8168_mdio_write(tp, 0x15, 0x00e3); + rtl8168_mdio_write(tp, 0x19, 0x5a40); + rtl8168_mdio_write(tp, 0x15, 0x00e4); + rtl8168_mdio_write(tp, 0x19, 0x305a); + rtl8168_mdio_write(tp, 0x15, 0x00e5); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x00e6); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x00e7); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x00e8); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x00e9); + rtl8168_mdio_write(tp, 0x19, 0x480a); + rtl8168_mdio_write(tp, 0x15, 0x00ea); + rtl8168_mdio_write(tp, 0x19, 0x5820); + rtl8168_mdio_write(tp, 0x15, 0x00eb); + rtl8168_mdio_write(tp, 0x19, 0x6c03); + rtl8168_mdio_write(tp, 0x15, 0x00ec); + rtl8168_mdio_write(tp, 0x19, 0xb60a); + rtl8168_mdio_write(tp, 0x15, 0x00ed); + rtl8168_mdio_write(tp, 0x19, 0xda07); + rtl8168_mdio_write(tp, 0x15, 0x00ee); + rtl8168_mdio_write(tp, 0x19, 0x0008); + rtl8168_mdio_write(tp, 0x15, 0x00ef); + rtl8168_mdio_write(tp, 0x19, 0xc60b); + rtl8168_mdio_write(tp, 0x15, 0x00f0); + rtl8168_mdio_write(tp, 0x19, 0x00fc); + rtl8168_mdio_write(tp, 0x15, 0x00f1); + rtl8168_mdio_write(tp, 0x19, 0x30f6); + rtl8168_mdio_write(tp, 0x15, 0x00f2); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x00f3); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x00f4); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x00f5); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x00f6); + rtl8168_mdio_write(tp, 0x19, 0x4408); + rtl8168_mdio_write(tp, 0x15, 0x00f7); + rtl8168_mdio_write(tp, 0x19, 0x480b); + rtl8168_mdio_write(tp, 0x15, 0x00f8); + rtl8168_mdio_write(tp, 0x19, 0x6f03); + rtl8168_mdio_write(tp, 0x15, 0x00f9); + rtl8168_mdio_write(tp, 0x19, 0x405f); + rtl8168_mdio_write(tp, 0x15, 0x00fa); + rtl8168_mdio_write(tp, 0x19, 0x4448); + rtl8168_mdio_write(tp, 0x15, 0x00fb); + rtl8168_mdio_write(tp, 0x19, 0x4020); + rtl8168_mdio_write(tp, 0x15, 0x00fc); + rtl8168_mdio_write(tp, 0x19, 0x4468); + rtl8168_mdio_write(tp, 0x15, 0x00fd); + rtl8168_mdio_write(tp, 0x19, 0x9c03); + rtl8168_mdio_write(tp, 0x15, 0x00fe); + rtl8168_mdio_write(tp, 0x19, 0x6f07); + rtl8168_mdio_write(tp, 0x15, 0x00ff); + rtl8168_mdio_write(tp, 0x19, 0x58a0); + rtl8168_mdio_write(tp, 0x15, 0x0100); + rtl8168_mdio_write(tp, 0x19, 0xd6d1); + rtl8168_mdio_write(tp, 0x15, 0x0101); + rtl8168_mdio_write(tp, 0x19, 0x0004); + rtl8168_mdio_write(tp, 0x15, 0x0102); + rtl8168_mdio_write(tp, 0x19, 0xc137); + rtl8168_mdio_write(tp, 0x15, 0x0103); + rtl8168_mdio_write(tp, 0x19, 0x0002); + rtl8168_mdio_write(tp, 0x15, 0x0104); + rtl8168_mdio_write(tp, 0x19, 0xa0e5); + rtl8168_mdio_write(tp, 0x15, 0x0105); + rtl8168_mdio_write(tp, 0x19, 0x9df8); + rtl8168_mdio_write(tp, 0x15, 0x0106); + rtl8168_mdio_write(tp, 0x19, 0x30c6); + rtl8168_mdio_write(tp, 0x15, 0x0107); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0108); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0109); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x010a); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x010b); + rtl8168_mdio_write(tp, 0x19, 0x4808); + rtl8168_mdio_write(tp, 0x15, 0x010c); + rtl8168_mdio_write(tp, 0x19, 0xc32d); + rtl8168_mdio_write(tp, 0x15, 0x010d); + rtl8168_mdio_write(tp, 0x19, 0x0003); + rtl8168_mdio_write(tp, 0x15, 0x010e); + rtl8168_mdio_write(tp, 0x19, 0xc8b3); + rtl8168_mdio_write(tp, 0x15, 0x010f); + rtl8168_mdio_write(tp, 0x19, 0x00fc); + rtl8168_mdio_write(tp, 0x15, 0x0110); + rtl8168_mdio_write(tp, 0x19, 0x4400); + rtl8168_mdio_write(tp, 0x15, 0x0111); + rtl8168_mdio_write(tp, 0x19, 0x3116); + rtl8168_mdio_write(tp, 0x15, 0x0112); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0113); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0114); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0115); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0116); + rtl8168_mdio_write(tp, 0x19, 0x4803); + rtl8168_mdio_write(tp, 0x15, 0x0117); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x0118); + rtl8168_mdio_write(tp, 0x19, 0x6c02); + rtl8168_mdio_write(tp, 0x15, 0x0119); + rtl8168_mdio_write(tp, 0x19, 0x7c04); + rtl8168_mdio_write(tp, 0x15, 0x011a); + rtl8168_mdio_write(tp, 0x19, 0x6000); + rtl8168_mdio_write(tp, 0x15, 0x011b); + rtl8168_mdio_write(tp, 0x19, 0x5cf7); + rtl8168_mdio_write(tp, 0x15, 0x011c); + rtl8168_mdio_write(tp, 0x19, 0x7c2a); + rtl8168_mdio_write(tp, 0x15, 0x011d); + rtl8168_mdio_write(tp, 0x19, 0x5800); + rtl8168_mdio_write(tp, 0x15, 0x011e); + rtl8168_mdio_write(tp, 0x19, 0x5400); + rtl8168_mdio_write(tp, 0x15, 0x011f); + rtl8168_mdio_write(tp, 0x19, 0x7c08); + rtl8168_mdio_write(tp, 0x15, 0x0120); + rtl8168_mdio_write(tp, 0x19, 0x74f0); + rtl8168_mdio_write(tp, 0x15, 0x0121); + rtl8168_mdio_write(tp, 0x19, 0x4019); + rtl8168_mdio_write(tp, 0x15, 0x0122); + rtl8168_mdio_write(tp, 0x19, 0x440d); + rtl8168_mdio_write(tp, 0x15, 0x0123); + rtl8168_mdio_write(tp, 0x19, 0xb6c1); + rtl8168_mdio_write(tp, 0x15, 0x0124); + rtl8168_mdio_write(tp, 0x19, 0xc05b); + rtl8168_mdio_write(tp, 0x15, 0x0125); + rtl8168_mdio_write(tp, 0x19, 0x00bf); + rtl8168_mdio_write(tp, 0x15, 0x0126); + rtl8168_mdio_write(tp, 0x19, 0xc025); + rtl8168_mdio_write(tp, 0x15, 0x0127); + rtl8168_mdio_write(tp, 0x19, 0x00bd); + rtl8168_mdio_write(tp, 0x15, 0x0128); + rtl8168_mdio_write(tp, 0x19, 0xc603); + rtl8168_mdio_write(tp, 0x15, 0x0129); + rtl8168_mdio_write(tp, 0x19, 0x00bb); + rtl8168_mdio_write(tp, 0x15, 0x012a); + rtl8168_mdio_write(tp, 0x19, 0x8805); + rtl8168_mdio_write(tp, 0x15, 0x012b); + rtl8168_mdio_write(tp, 0x19, 0x7801); + rtl8168_mdio_write(tp, 0x15, 0x012c); + rtl8168_mdio_write(tp, 0x19, 0x4001); + rtl8168_mdio_write(tp, 0x15, 0x012d); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x012e); + rtl8168_mdio_write(tp, 0x19, 0xa3dd); + rtl8168_mdio_write(tp, 0x15, 0x012f); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x0130); + rtl8168_mdio_write(tp, 0x19, 0x6c03); + rtl8168_mdio_write(tp, 0x15, 0x0131); + rtl8168_mdio_write(tp, 0x19, 0x8407); + rtl8168_mdio_write(tp, 0x15, 0x0132); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x0133); + rtl8168_mdio_write(tp, 0x19, 0x6c02); + rtl8168_mdio_write(tp, 0x15, 0x0134); + rtl8168_mdio_write(tp, 0x19, 0xd9b8); + rtl8168_mdio_write(tp, 0x15, 0x0135); + rtl8168_mdio_write(tp, 0x19, 0x0003); + rtl8168_mdio_write(tp, 0x15, 0x0136); + rtl8168_mdio_write(tp, 0x19, 0xc240); + rtl8168_mdio_write(tp, 0x15, 0x0137); + rtl8168_mdio_write(tp, 0x19, 0x0015); + rtl8168_mdio_write(tp, 0x15, 0x0138); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x0139); + rtl8168_mdio_write(tp, 0x19, 0x6c02); + rtl8168_mdio_write(tp, 0x15, 0x013a); + rtl8168_mdio_write(tp, 0x19, 0x9ae9); + rtl8168_mdio_write(tp, 0x15, 0x013b); + rtl8168_mdio_write(tp, 0x19, 0x3140); + rtl8168_mdio_write(tp, 0x15, 0x013c); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x013d); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x013e); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x013f); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0140); + rtl8168_mdio_write(tp, 0x19, 0x4807); + rtl8168_mdio_write(tp, 0x15, 0x0141); + rtl8168_mdio_write(tp, 0x19, 0x4004); + rtl8168_mdio_write(tp, 0x15, 0x0142); + rtl8168_mdio_write(tp, 0x19, 0x4410); + rtl8168_mdio_write(tp, 0x15, 0x0143); + rtl8168_mdio_write(tp, 0x19, 0x7c0c); + rtl8168_mdio_write(tp, 0x15, 0x0144); + rtl8168_mdio_write(tp, 0x19, 0x600c); + rtl8168_mdio_write(tp, 0x15, 0x0145); + rtl8168_mdio_write(tp, 0x19, 0x9b00); + rtl8168_mdio_write(tp, 0x15, 0x0146); + rtl8168_mdio_write(tp, 0x19, 0xa68f); + rtl8168_mdio_write(tp, 0x15, 0x0147); + rtl8168_mdio_write(tp, 0x19, 0x3116); + rtl8168_mdio_write(tp, 0x15, 0x0148); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0149); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x014a); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x014b); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x014c); + rtl8168_mdio_write(tp, 0x19, 0x4804); + rtl8168_mdio_write(tp, 0x15, 0x014d); + rtl8168_mdio_write(tp, 0x19, 0x54c0); + rtl8168_mdio_write(tp, 0x15, 0x014e); + rtl8168_mdio_write(tp, 0x19, 0xb703); + rtl8168_mdio_write(tp, 0x15, 0x014f); + rtl8168_mdio_write(tp, 0x19, 0x5cff); + rtl8168_mdio_write(tp, 0x15, 0x0150); + rtl8168_mdio_write(tp, 0x19, 0x315f); + rtl8168_mdio_write(tp, 0x15, 0x0151); + rtl8168_mdio_write(tp, 0x19, 0x7c08); + rtl8168_mdio_write(tp, 0x15, 0x0152); + rtl8168_mdio_write(tp, 0x19, 0x74f8); + rtl8168_mdio_write(tp, 0x15, 0x0153); + rtl8168_mdio_write(tp, 0x19, 0x6421); + rtl8168_mdio_write(tp, 0x15, 0x0154); + rtl8168_mdio_write(tp, 0x19, 0x7c08); + rtl8168_mdio_write(tp, 0x15, 0x0155); + rtl8168_mdio_write(tp, 0x19, 0x6000); + rtl8168_mdio_write(tp, 0x15, 0x0156); + rtl8168_mdio_write(tp, 0x19, 0x4003); + rtl8168_mdio_write(tp, 0x15, 0x0157); + rtl8168_mdio_write(tp, 0x19, 0x4418); + rtl8168_mdio_write(tp, 0x15, 0x0158); + rtl8168_mdio_write(tp, 0x19, 0x9b00); + rtl8168_mdio_write(tp, 0x15, 0x0159); + rtl8168_mdio_write(tp, 0x19, 0x6461); + rtl8168_mdio_write(tp, 0x15, 0x015a); + rtl8168_mdio_write(tp, 0x19, 0x64e1); + rtl8168_mdio_write(tp, 0x15, 0x015b); + rtl8168_mdio_write(tp, 0x19, 0x7c20); + rtl8168_mdio_write(tp, 0x15, 0x015c); + rtl8168_mdio_write(tp, 0x19, 0x5820); + rtl8168_mdio_write(tp, 0x15, 0x015d); + rtl8168_mdio_write(tp, 0x19, 0x5ccf); + rtl8168_mdio_write(tp, 0x15, 0x015e); + rtl8168_mdio_write(tp, 0x19, 0x7050); + rtl8168_mdio_write(tp, 0x15, 0x015f); + rtl8168_mdio_write(tp, 0x19, 0xd9b8); + rtl8168_mdio_write(tp, 0x15, 0x0160); + rtl8168_mdio_write(tp, 0x19, 0x0008); + rtl8168_mdio_write(tp, 0x15, 0x0161); + rtl8168_mdio_write(tp, 0x19, 0xdab1); + rtl8168_mdio_write(tp, 0x15, 0x0162); + rtl8168_mdio_write(tp, 0x19, 0x0015); + rtl8168_mdio_write(tp, 0x15, 0x0163); + rtl8168_mdio_write(tp, 0x19, 0xc244); + rtl8168_mdio_write(tp, 0x15, 0x0164); + rtl8168_mdio_write(tp, 0x19, 0x0013); + rtl8168_mdio_write(tp, 0x15, 0x0165); + rtl8168_mdio_write(tp, 0x19, 0xc021); + rtl8168_mdio_write(tp, 0x15, 0x0166); + rtl8168_mdio_write(tp, 0x19, 0x00f9); + rtl8168_mdio_write(tp, 0x15, 0x0167); + rtl8168_mdio_write(tp, 0x19, 0x3177); + rtl8168_mdio_write(tp, 0x15, 0x0168); + rtl8168_mdio_write(tp, 0x19, 0x5cf7); + rtl8168_mdio_write(tp, 0x15, 0x0169); + rtl8168_mdio_write(tp, 0x19, 0x4010); + rtl8168_mdio_write(tp, 0x15, 0x016a); + rtl8168_mdio_write(tp, 0x19, 0x4428); + rtl8168_mdio_write(tp, 0x15, 0x016b); + rtl8168_mdio_write(tp, 0x19, 0x9c00); + rtl8168_mdio_write(tp, 0x15, 0x016c); + rtl8168_mdio_write(tp, 0x19, 0x7c08); + rtl8168_mdio_write(tp, 0x15, 0x016d); + rtl8168_mdio_write(tp, 0x19, 0x6008); + rtl8168_mdio_write(tp, 0x15, 0x016e); + rtl8168_mdio_write(tp, 0x19, 0x7c08); + rtl8168_mdio_write(tp, 0x15, 0x016f); + rtl8168_mdio_write(tp, 0x19, 0x74f0); + rtl8168_mdio_write(tp, 0x15, 0x0170); + rtl8168_mdio_write(tp, 0x19, 0x6461); + rtl8168_mdio_write(tp, 0x15, 0x0171); + rtl8168_mdio_write(tp, 0x19, 0x6421); + rtl8168_mdio_write(tp, 0x15, 0x0172); + rtl8168_mdio_write(tp, 0x19, 0x64a1); + rtl8168_mdio_write(tp, 0x15, 0x0173); + rtl8168_mdio_write(tp, 0x19, 0x3116); + rtl8168_mdio_write(tp, 0x15, 0x0174); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0175); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0176); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0177); + rtl8168_mdio_write(tp, 0x19, 0x4805); + rtl8168_mdio_write(tp, 0x15, 0x0178); + rtl8168_mdio_write(tp, 0x19, 0xa103); + rtl8168_mdio_write(tp, 0x15, 0x0179); + rtl8168_mdio_write(tp, 0x19, 0x7c02); + rtl8168_mdio_write(tp, 0x15, 0x017a); + rtl8168_mdio_write(tp, 0x19, 0x6002); + rtl8168_mdio_write(tp, 0x15, 0x017b); + rtl8168_mdio_write(tp, 0x19, 0x7e00); + rtl8168_mdio_write(tp, 0x15, 0x017c); + rtl8168_mdio_write(tp, 0x19, 0x5400); + rtl8168_mdio_write(tp, 0x15, 0x017d); + rtl8168_mdio_write(tp, 0x19, 0x7c6b); + rtl8168_mdio_write(tp, 0x15, 0x017e); + rtl8168_mdio_write(tp, 0x19, 0x5c63); + rtl8168_mdio_write(tp, 0x15, 0x017f); + rtl8168_mdio_write(tp, 0x19, 0x407d); + rtl8168_mdio_write(tp, 0x15, 0x0180); + rtl8168_mdio_write(tp, 0x19, 0xa602); + rtl8168_mdio_write(tp, 0x15, 0x0181); + rtl8168_mdio_write(tp, 0x19, 0x4001); + rtl8168_mdio_write(tp, 0x15, 0x0182); + rtl8168_mdio_write(tp, 0x19, 0x4420); + rtl8168_mdio_write(tp, 0x15, 0x0183); + rtl8168_mdio_write(tp, 0x19, 0x4020); + rtl8168_mdio_write(tp, 0x15, 0x0184); + rtl8168_mdio_write(tp, 0x19, 0x44a1); + rtl8168_mdio_write(tp, 0x15, 0x0185); + rtl8168_mdio_write(tp, 0x19, 0xd6e0); + rtl8168_mdio_write(tp, 0x15, 0x0186); + rtl8168_mdio_write(tp, 0x19, 0x0009); + rtl8168_mdio_write(tp, 0x15, 0x0187); + rtl8168_mdio_write(tp, 0x19, 0x9efe); + rtl8168_mdio_write(tp, 0x15, 0x0188); + rtl8168_mdio_write(tp, 0x19, 0x7c02); + rtl8168_mdio_write(tp, 0x15, 0x0189); + rtl8168_mdio_write(tp, 0x19, 0x6000); + rtl8168_mdio_write(tp, 0x15, 0x018a); + rtl8168_mdio_write(tp, 0x19, 0x9c00); + rtl8168_mdio_write(tp, 0x15, 0x018b); + rtl8168_mdio_write(tp, 0x19, 0x318f); + rtl8168_mdio_write(tp, 0x15, 0x018c); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x018d); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x018e); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x018f); + rtl8168_mdio_write(tp, 0x19, 0x4806); + rtl8168_mdio_write(tp, 0x15, 0x0190); + rtl8168_mdio_write(tp, 0x19, 0x7c10); + rtl8168_mdio_write(tp, 0x15, 0x0191); + rtl8168_mdio_write(tp, 0x19, 0x5c10); + rtl8168_mdio_write(tp, 0x15, 0x0192); + rtl8168_mdio_write(tp, 0x19, 0x40fa); + rtl8168_mdio_write(tp, 0x15, 0x0193); + rtl8168_mdio_write(tp, 0x19, 0xa602); + rtl8168_mdio_write(tp, 0x15, 0x0194); + rtl8168_mdio_write(tp, 0x19, 0x4010); + rtl8168_mdio_write(tp, 0x15, 0x0195); + rtl8168_mdio_write(tp, 0x19, 0x4440); + rtl8168_mdio_write(tp, 0x15, 0x0196); + rtl8168_mdio_write(tp, 0x19, 0x9d00); + rtl8168_mdio_write(tp, 0x15, 0x0197); + rtl8168_mdio_write(tp, 0x19, 0x7c80); + rtl8168_mdio_write(tp, 0x15, 0x0198); + rtl8168_mdio_write(tp, 0x19, 0x6400); + rtl8168_mdio_write(tp, 0x15, 0x0199); + rtl8168_mdio_write(tp, 0x19, 0x4003); + rtl8168_mdio_write(tp, 0x15, 0x019a); + rtl8168_mdio_write(tp, 0x19, 0x4540); + rtl8168_mdio_write(tp, 0x15, 0x019b); + rtl8168_mdio_write(tp, 0x19, 0x7c08); + rtl8168_mdio_write(tp, 0x15, 0x019c); + rtl8168_mdio_write(tp, 0x19, 0x6008); + rtl8168_mdio_write(tp, 0x15, 0x019d); + rtl8168_mdio_write(tp, 0x19, 0x9f00); + rtl8168_mdio_write(tp, 0x15, 0x019e); + rtl8168_mdio_write(tp, 0x19, 0x7c40); + rtl8168_mdio_write(tp, 0x15, 0x019f); + rtl8168_mdio_write(tp, 0x19, 0x6400); + rtl8168_mdio_write(tp, 0x15, 0x01a0); + rtl8168_mdio_write(tp, 0x19, 0x7c80); + rtl8168_mdio_write(tp, 0x15, 0x01a1); + rtl8168_mdio_write(tp, 0x19, 0x6480); + rtl8168_mdio_write(tp, 0x15, 0x01a2); + rtl8168_mdio_write(tp, 0x19, 0x3140); + rtl8168_mdio_write(tp, 0x15, 0x01a3); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x01a4); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x01a5); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x01a6); + rtl8168_mdio_write(tp, 0x19, 0x4400); + rtl8168_mdio_write(tp, 0x15, 0x01a7); + rtl8168_mdio_write(tp, 0x19, 0x7c0b); + rtl8168_mdio_write(tp, 0x15, 0x01a8); + rtl8168_mdio_write(tp, 0x19, 0x6c01); + rtl8168_mdio_write(tp, 0x15, 0x01a9); + rtl8168_mdio_write(tp, 0x19, 0x64a8); + rtl8168_mdio_write(tp, 0x15, 0x01aa); + rtl8168_mdio_write(tp, 0x19, 0x6800); + rtl8168_mdio_write(tp, 0x15, 0x01ab); + rtl8168_mdio_write(tp, 0x19, 0x5cf0); + rtl8168_mdio_write(tp, 0x15, 0x01ac); + rtl8168_mdio_write(tp, 0x19, 0x588f); + rtl8168_mdio_write(tp, 0x15, 0x01ad); + rtl8168_mdio_write(tp, 0x19, 0xb628); + rtl8168_mdio_write(tp, 0x15, 0x01ae); + rtl8168_mdio_write(tp, 0x19, 0xc053); + rtl8168_mdio_write(tp, 0x15, 0x01af); + rtl8168_mdio_write(tp, 0x19, 0x0026); + rtl8168_mdio_write(tp, 0x15, 0x01b0); + rtl8168_mdio_write(tp, 0x19, 0xc02d); + rtl8168_mdio_write(tp, 0x15, 0x01b1); + rtl8168_mdio_write(tp, 0x19, 0x0024); + rtl8168_mdio_write(tp, 0x15, 0x01b2); + rtl8168_mdio_write(tp, 0x19, 0xc603); + rtl8168_mdio_write(tp, 0x15, 0x01b3); + rtl8168_mdio_write(tp, 0x19, 0x0022); + rtl8168_mdio_write(tp, 0x15, 0x01b4); + rtl8168_mdio_write(tp, 0x19, 0x8cf9); + rtl8168_mdio_write(tp, 0x15, 0x01b5); + rtl8168_mdio_write(tp, 0x19, 0x31ba); + rtl8168_mdio_write(tp, 0x15, 0x01b6); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x01b7); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x01b8); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x01b9); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x01ba); + rtl8168_mdio_write(tp, 0x19, 0x4400); + rtl8168_mdio_write(tp, 0x15, 0x01bb); + rtl8168_mdio_write(tp, 0x19, 0x5420); + rtl8168_mdio_write(tp, 0x15, 0x01bc); + rtl8168_mdio_write(tp, 0x19, 0x4811); + rtl8168_mdio_write(tp, 0x15, 0x01bd); + rtl8168_mdio_write(tp, 0x19, 0x5000); + rtl8168_mdio_write(tp, 0x15, 0x01be); + rtl8168_mdio_write(tp, 0x19, 0x4801); + rtl8168_mdio_write(tp, 0x15, 0x01bf); + rtl8168_mdio_write(tp, 0x19, 0x6800); + rtl8168_mdio_write(tp, 0x15, 0x01c0); + rtl8168_mdio_write(tp, 0x19, 0x31f5); + rtl8168_mdio_write(tp, 0x15, 0x01c1); + rtl8168_mdio_write(tp, 0x19, 0xb614); + rtl8168_mdio_write(tp, 0x15, 0x01c2); + rtl8168_mdio_write(tp, 0x19, 0x8ce4); + rtl8168_mdio_write(tp, 0x15, 0x01c3); + rtl8168_mdio_write(tp, 0x19, 0xb30c); + rtl8168_mdio_write(tp, 0x15, 0x01c4); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x01c5); + rtl8168_mdio_write(tp, 0x19, 0x6c02); + rtl8168_mdio_write(tp, 0x15, 0x01c6); + rtl8168_mdio_write(tp, 0x19, 0x8206); + rtl8168_mdio_write(tp, 0x15, 0x01c7); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x01c8); + rtl8168_mdio_write(tp, 0x19, 0x6c00); + rtl8168_mdio_write(tp, 0x15, 0x01c9); + rtl8168_mdio_write(tp, 0x19, 0x7c04); + rtl8168_mdio_write(tp, 0x15, 0x01ca); + rtl8168_mdio_write(tp, 0x19, 0x7404); + rtl8168_mdio_write(tp, 0x15, 0x01cb); + rtl8168_mdio_write(tp, 0x19, 0x31c0); + rtl8168_mdio_write(tp, 0x15, 0x01cc); + rtl8168_mdio_write(tp, 0x19, 0x7c04); + rtl8168_mdio_write(tp, 0x15, 0x01cd); + rtl8168_mdio_write(tp, 0x19, 0x7400); + rtl8168_mdio_write(tp, 0x15, 0x01ce); + rtl8168_mdio_write(tp, 0x19, 0x31c0); + rtl8168_mdio_write(tp, 0x15, 0x01cf); + rtl8168_mdio_write(tp, 0x19, 0x8df1); + rtl8168_mdio_write(tp, 0x15, 0x01d0); + rtl8168_mdio_write(tp, 0x19, 0x3248); + rtl8168_mdio_write(tp, 0x15, 0x01d1); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x01d2); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x01d3); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x01d4); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x01d5); + rtl8168_mdio_write(tp, 0x19, 0x4400); + rtl8168_mdio_write(tp, 0x15, 0x01d6); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x01d7); + rtl8168_mdio_write(tp, 0x19, 0x6c03); + rtl8168_mdio_write(tp, 0x15, 0x01d8); + rtl8168_mdio_write(tp, 0x19, 0x7670); + rtl8168_mdio_write(tp, 0x15, 0x01d9); + rtl8168_mdio_write(tp, 0x19, 0x4023); + rtl8168_mdio_write(tp, 0x15, 0x01da); + rtl8168_mdio_write(tp, 0x19, 0x4500); + rtl8168_mdio_write(tp, 0x15, 0x01db); + rtl8168_mdio_write(tp, 0x19, 0x4069); + rtl8168_mdio_write(tp, 0x15, 0x01dc); + rtl8168_mdio_write(tp, 0x19, 0x4580); + rtl8168_mdio_write(tp, 0x15, 0x01dd); + rtl8168_mdio_write(tp, 0x19, 0x9f00); + rtl8168_mdio_write(tp, 0x15, 0x01de); + rtl8168_mdio_write(tp, 0x19, 0xcff5); + rtl8168_mdio_write(tp, 0x15, 0x01df); + rtl8168_mdio_write(tp, 0x19, 0x00ff); + rtl8168_mdio_write(tp, 0x15, 0x01e0); + rtl8168_mdio_write(tp, 0x19, 0x76f0); + rtl8168_mdio_write(tp, 0x15, 0x01e1); + rtl8168_mdio_write(tp, 0x19, 0x4400); + rtl8168_mdio_write(tp, 0x15, 0x01e2); + rtl8168_mdio_write(tp, 0x19, 0x4023); + rtl8168_mdio_write(tp, 0x15, 0x01e3); + rtl8168_mdio_write(tp, 0x19, 0x4500); + rtl8168_mdio_write(tp, 0x15, 0x01e4); + rtl8168_mdio_write(tp, 0x19, 0x4069); + rtl8168_mdio_write(tp, 0x15, 0x01e5); + rtl8168_mdio_write(tp, 0x19, 0x4580); + rtl8168_mdio_write(tp, 0x15, 0x01e6); + rtl8168_mdio_write(tp, 0x19, 0x9f00); + rtl8168_mdio_write(tp, 0x15, 0x01e7); + rtl8168_mdio_write(tp, 0x19, 0xd0f5); + rtl8168_mdio_write(tp, 0x15, 0x01e8); + rtl8168_mdio_write(tp, 0x19, 0x00ff); + rtl8168_mdio_write(tp, 0x15, 0x01e9); + rtl8168_mdio_write(tp, 0x19, 0x4400); + rtl8168_mdio_write(tp, 0x15, 0x01ea); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x01eb); + rtl8168_mdio_write(tp, 0x19, 0x6800); + rtl8168_mdio_write(tp, 0x15, 0x01ec); + rtl8168_mdio_write(tp, 0x19, 0x66a0); + rtl8168_mdio_write(tp, 0x15, 0x01ed); + rtl8168_mdio_write(tp, 0x19, 0x8300); + rtl8168_mdio_write(tp, 0x15, 0x01ee); + rtl8168_mdio_write(tp, 0x19, 0x74f0); + rtl8168_mdio_write(tp, 0x15, 0x01ef); + rtl8168_mdio_write(tp, 0x19, 0x3006); + rtl8168_mdio_write(tp, 0x15, 0x01f0); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x01f1); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x01f2); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x01f3); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x01f4); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x01f5); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x01f6); + rtl8168_mdio_write(tp, 0x19, 0x6c02); + rtl8168_mdio_write(tp, 0x15, 0x01f7); + rtl8168_mdio_write(tp, 0x19, 0x409d); + rtl8168_mdio_write(tp, 0x15, 0x01f8); + rtl8168_mdio_write(tp, 0x19, 0x7c87); + rtl8168_mdio_write(tp, 0x15, 0x01f9); + rtl8168_mdio_write(tp, 0x19, 0xae14); + rtl8168_mdio_write(tp, 0x15, 0x01fa); + rtl8168_mdio_write(tp, 0x19, 0x4400); + rtl8168_mdio_write(tp, 0x15, 0x01fb); + rtl8168_mdio_write(tp, 0x19, 0x7c40); + rtl8168_mdio_write(tp, 0x15, 0x01fc); + rtl8168_mdio_write(tp, 0x19, 0x6800); + rtl8168_mdio_write(tp, 0x15, 0x01fd); + rtl8168_mdio_write(tp, 0x19, 0x7801); + rtl8168_mdio_write(tp, 0x15, 0x01fe); + rtl8168_mdio_write(tp, 0x19, 0x980e); + rtl8168_mdio_write(tp, 0x15, 0x01ff); + rtl8168_mdio_write(tp, 0x19, 0x930c); + rtl8168_mdio_write(tp, 0x15, 0x0200); + rtl8168_mdio_write(tp, 0x19, 0x9206); + rtl8168_mdio_write(tp, 0x15, 0x0201); + rtl8168_mdio_write(tp, 0x19, 0x4002); + rtl8168_mdio_write(tp, 0x15, 0x0202); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x0203); + rtl8168_mdio_write(tp, 0x19, 0x588f); + rtl8168_mdio_write(tp, 0x15, 0x0204); + rtl8168_mdio_write(tp, 0x19, 0x5520); + rtl8168_mdio_write(tp, 0x15, 0x0205); + rtl8168_mdio_write(tp, 0x19, 0x320c); + rtl8168_mdio_write(tp, 0x15, 0x0206); + rtl8168_mdio_write(tp, 0x19, 0x4000); + rtl8168_mdio_write(tp, 0x15, 0x0207); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x0208); + rtl8168_mdio_write(tp, 0x19, 0x588d); + rtl8168_mdio_write(tp, 0x15, 0x0209); + rtl8168_mdio_write(tp, 0x19, 0x5500); + rtl8168_mdio_write(tp, 0x15, 0x020a); + rtl8168_mdio_write(tp, 0x19, 0x320c); + rtl8168_mdio_write(tp, 0x15, 0x020b); + rtl8168_mdio_write(tp, 0x19, 0x4002); + rtl8168_mdio_write(tp, 0x15, 0x020c); + rtl8168_mdio_write(tp, 0x19, 0x3220); + rtl8168_mdio_write(tp, 0x15, 0x020d); + rtl8168_mdio_write(tp, 0x19, 0x4480); + rtl8168_mdio_write(tp, 0x15, 0x020e); + rtl8168_mdio_write(tp, 0x19, 0x9e03); + rtl8168_mdio_write(tp, 0x15, 0x020f); + rtl8168_mdio_write(tp, 0x19, 0x7c40); + rtl8168_mdio_write(tp, 0x15, 0x0210); + rtl8168_mdio_write(tp, 0x19, 0x6840); + rtl8168_mdio_write(tp, 0x15, 0x0211); + rtl8168_mdio_write(tp, 0x19, 0x7801); + rtl8168_mdio_write(tp, 0x15, 0x0212); + rtl8168_mdio_write(tp, 0x19, 0x980e); + rtl8168_mdio_write(tp, 0x15, 0x0213); + rtl8168_mdio_write(tp, 0x19, 0x930c); + rtl8168_mdio_write(tp, 0x15, 0x0214); + rtl8168_mdio_write(tp, 0x19, 0x9206); + rtl8168_mdio_write(tp, 0x15, 0x0215); + rtl8168_mdio_write(tp, 0x19, 0x4000); + rtl8168_mdio_write(tp, 0x15, 0x0216); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x0217); + rtl8168_mdio_write(tp, 0x19, 0x588f); + rtl8168_mdio_write(tp, 0x15, 0x0218); + rtl8168_mdio_write(tp, 0x19, 0x5520); + rtl8168_mdio_write(tp, 0x15, 0x0219); + rtl8168_mdio_write(tp, 0x19, 0x3220); + rtl8168_mdio_write(tp, 0x15, 0x021a); + rtl8168_mdio_write(tp, 0x19, 0x4002); + rtl8168_mdio_write(tp, 0x15, 0x021b); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x021c); + rtl8168_mdio_write(tp, 0x19, 0x588d); + rtl8168_mdio_write(tp, 0x15, 0x021d); + rtl8168_mdio_write(tp, 0x19, 0x5540); + rtl8168_mdio_write(tp, 0x15, 0x021e); + rtl8168_mdio_write(tp, 0x19, 0x3220); + rtl8168_mdio_write(tp, 0x15, 0x021f); + rtl8168_mdio_write(tp, 0x19, 0x4000); + rtl8168_mdio_write(tp, 0x15, 0x0220); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x0221); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x0222); + rtl8168_mdio_write(tp, 0x19, 0x6c00); + rtl8168_mdio_write(tp, 0x15, 0x0223); + rtl8168_mdio_write(tp, 0x19, 0x3231); + rtl8168_mdio_write(tp, 0x15, 0x0224); + rtl8168_mdio_write(tp, 0x19, 0xab06); + rtl8168_mdio_write(tp, 0x15, 0x0225); + rtl8168_mdio_write(tp, 0x19, 0xbf08); + rtl8168_mdio_write(tp, 0x15, 0x0226); + rtl8168_mdio_write(tp, 0x19, 0x4076); + rtl8168_mdio_write(tp, 0x15, 0x0227); + rtl8168_mdio_write(tp, 0x19, 0x7d07); + rtl8168_mdio_write(tp, 0x15, 0x0228); + rtl8168_mdio_write(tp, 0x19, 0x4502); + rtl8168_mdio_write(tp, 0x15, 0x0229); + rtl8168_mdio_write(tp, 0x19, 0x3231); + rtl8168_mdio_write(tp, 0x15, 0x022a); + rtl8168_mdio_write(tp, 0x19, 0x7d80); + rtl8168_mdio_write(tp, 0x15, 0x022b); + rtl8168_mdio_write(tp, 0x19, 0x5180); + rtl8168_mdio_write(tp, 0x15, 0x022c); + rtl8168_mdio_write(tp, 0x19, 0x322f); + rtl8168_mdio_write(tp, 0x15, 0x022d); + rtl8168_mdio_write(tp, 0x19, 0x7d80); + rtl8168_mdio_write(tp, 0x15, 0x022e); + rtl8168_mdio_write(tp, 0x19, 0x5000); + rtl8168_mdio_write(tp, 0x15, 0x022f); + rtl8168_mdio_write(tp, 0x19, 0x7d07); + rtl8168_mdio_write(tp, 0x15, 0x0230); + rtl8168_mdio_write(tp, 0x19, 0x4402); + rtl8168_mdio_write(tp, 0x15, 0x0231); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x0232); + rtl8168_mdio_write(tp, 0x19, 0x6c02); + rtl8168_mdio_write(tp, 0x15, 0x0233); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x0234); + rtl8168_mdio_write(tp, 0x19, 0xb309); + rtl8168_mdio_write(tp, 0x15, 0x0235); + rtl8168_mdio_write(tp, 0x19, 0xb204); + rtl8168_mdio_write(tp, 0x15, 0x0236); + rtl8168_mdio_write(tp, 0x19, 0xb105); + rtl8168_mdio_write(tp, 0x15, 0x0237); + rtl8168_mdio_write(tp, 0x19, 0x6c00); + rtl8168_mdio_write(tp, 0x15, 0x0238); + rtl8168_mdio_write(tp, 0x19, 0x31c1); + rtl8168_mdio_write(tp, 0x15, 0x0239); + rtl8168_mdio_write(tp, 0x19, 0x6c00); + rtl8168_mdio_write(tp, 0x15, 0x023a); + rtl8168_mdio_write(tp, 0x19, 0x3261); + rtl8168_mdio_write(tp, 0x15, 0x023b); + rtl8168_mdio_write(tp, 0x19, 0x6c00); + rtl8168_mdio_write(tp, 0x15, 0x023c); + rtl8168_mdio_write(tp, 0x19, 0x3250); + rtl8168_mdio_write(tp, 0x15, 0x023d); + rtl8168_mdio_write(tp, 0x19, 0xb203); + rtl8168_mdio_write(tp, 0x15, 0x023e); + rtl8168_mdio_write(tp, 0x19, 0x6c00); + rtl8168_mdio_write(tp, 0x15, 0x023f); + rtl8168_mdio_write(tp, 0x19, 0x327a); + rtl8168_mdio_write(tp, 0x15, 0x0240); + rtl8168_mdio_write(tp, 0x19, 0x6c00); + rtl8168_mdio_write(tp, 0x15, 0x0241); + rtl8168_mdio_write(tp, 0x19, 0x3293); + rtl8168_mdio_write(tp, 0x15, 0x0242); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0243); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0244); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0245); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0246); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0247); + rtl8168_mdio_write(tp, 0x19, 0x32a3); + rtl8168_mdio_write(tp, 0x15, 0x0248); + rtl8168_mdio_write(tp, 0x19, 0x5520); + rtl8168_mdio_write(tp, 0x15, 0x0249); + rtl8168_mdio_write(tp, 0x19, 0x403d); + rtl8168_mdio_write(tp, 0x15, 0x024a); + rtl8168_mdio_write(tp, 0x19, 0x440c); + rtl8168_mdio_write(tp, 0x15, 0x024b); + rtl8168_mdio_write(tp, 0x19, 0x4812); + rtl8168_mdio_write(tp, 0x15, 0x024c); + rtl8168_mdio_write(tp, 0x19, 0x5001); + rtl8168_mdio_write(tp, 0x15, 0x024d); + rtl8168_mdio_write(tp, 0x19, 0x4802); + rtl8168_mdio_write(tp, 0x15, 0x024e); + rtl8168_mdio_write(tp, 0x19, 0x6880); + rtl8168_mdio_write(tp, 0x15, 0x024f); + rtl8168_mdio_write(tp, 0x19, 0x31f5); + rtl8168_mdio_write(tp, 0x15, 0x0250); + rtl8168_mdio_write(tp, 0x19, 0xb685); + rtl8168_mdio_write(tp, 0x15, 0x0251); + rtl8168_mdio_write(tp, 0x19, 0x801c); + rtl8168_mdio_write(tp, 0x15, 0x0252); + rtl8168_mdio_write(tp, 0x19, 0xbaf5); + rtl8168_mdio_write(tp, 0x15, 0x0253); + rtl8168_mdio_write(tp, 0x19, 0xc07c); + rtl8168_mdio_write(tp, 0x15, 0x0254); + rtl8168_mdio_write(tp, 0x19, 0x00fb); + rtl8168_mdio_write(tp, 0x15, 0x0255); + rtl8168_mdio_write(tp, 0x19, 0x325a); + rtl8168_mdio_write(tp, 0x15, 0x0256); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0257); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0258); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0259); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x025a); + rtl8168_mdio_write(tp, 0x19, 0x481a); + rtl8168_mdio_write(tp, 0x15, 0x025b); + rtl8168_mdio_write(tp, 0x19, 0x5001); + rtl8168_mdio_write(tp, 0x15, 0x025c); + rtl8168_mdio_write(tp, 0x19, 0x401b); + rtl8168_mdio_write(tp, 0x15, 0x025d); + rtl8168_mdio_write(tp, 0x19, 0x480a); + rtl8168_mdio_write(tp, 0x15, 0x025e); + rtl8168_mdio_write(tp, 0x19, 0x4418); + rtl8168_mdio_write(tp, 0x15, 0x025f); + rtl8168_mdio_write(tp, 0x19, 0x6900); + rtl8168_mdio_write(tp, 0x15, 0x0260); + rtl8168_mdio_write(tp, 0x19, 0x31f5); + rtl8168_mdio_write(tp, 0x15, 0x0261); + rtl8168_mdio_write(tp, 0x19, 0xb64b); + rtl8168_mdio_write(tp, 0x15, 0x0262); + rtl8168_mdio_write(tp, 0x19, 0xdb00); + rtl8168_mdio_write(tp, 0x15, 0x0263); + rtl8168_mdio_write(tp, 0x19, 0x0048); + rtl8168_mdio_write(tp, 0x15, 0x0264); + rtl8168_mdio_write(tp, 0x19, 0xdb7d); + rtl8168_mdio_write(tp, 0x15, 0x0265); + rtl8168_mdio_write(tp, 0x19, 0x0002); + rtl8168_mdio_write(tp, 0x15, 0x0266); + rtl8168_mdio_write(tp, 0x19, 0xa0fa); + rtl8168_mdio_write(tp, 0x15, 0x0267); + rtl8168_mdio_write(tp, 0x19, 0x4408); + rtl8168_mdio_write(tp, 0x15, 0x0268); + rtl8168_mdio_write(tp, 0x19, 0x3248); + rtl8168_mdio_write(tp, 0x15, 0x0269); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x026a); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x026b); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x026c); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x026d); + rtl8168_mdio_write(tp, 0x19, 0xb806); + rtl8168_mdio_write(tp, 0x15, 0x026e); + rtl8168_mdio_write(tp, 0x19, 0x588d); + rtl8168_mdio_write(tp, 0x15, 0x026f); + rtl8168_mdio_write(tp, 0x19, 0x5500); + rtl8168_mdio_write(tp, 0x15, 0x0270); + rtl8168_mdio_write(tp, 0x19, 0x7801); + rtl8168_mdio_write(tp, 0x15, 0x0271); + rtl8168_mdio_write(tp, 0x19, 0x4002); + rtl8168_mdio_write(tp, 0x15, 0x0272); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x0273); + rtl8168_mdio_write(tp, 0x19, 0x4814); + rtl8168_mdio_write(tp, 0x15, 0x0274); + rtl8168_mdio_write(tp, 0x19, 0x500b); + rtl8168_mdio_write(tp, 0x15, 0x0275); + rtl8168_mdio_write(tp, 0x19, 0x4804); + rtl8168_mdio_write(tp, 0x15, 0x0276); + rtl8168_mdio_write(tp, 0x19, 0x40c4); + rtl8168_mdio_write(tp, 0x15, 0x0277); + rtl8168_mdio_write(tp, 0x19, 0x4425); + rtl8168_mdio_write(tp, 0x15, 0x0278); + rtl8168_mdio_write(tp, 0x19, 0x6a00); + rtl8168_mdio_write(tp, 0x15, 0x0279); + rtl8168_mdio_write(tp, 0x19, 0x31f5); + rtl8168_mdio_write(tp, 0x15, 0x027a); + rtl8168_mdio_write(tp, 0x19, 0xb632); + rtl8168_mdio_write(tp, 0x15, 0x027b); + rtl8168_mdio_write(tp, 0x19, 0xdc03); + rtl8168_mdio_write(tp, 0x15, 0x027c); + rtl8168_mdio_write(tp, 0x19, 0x0027); + rtl8168_mdio_write(tp, 0x15, 0x027d); + rtl8168_mdio_write(tp, 0x19, 0x80fc); + rtl8168_mdio_write(tp, 0x15, 0x027e); + rtl8168_mdio_write(tp, 0x19, 0x3283); + rtl8168_mdio_write(tp, 0x15, 0x027f); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0280); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0281); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0282); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0283); + rtl8168_mdio_write(tp, 0x19, 0xb806); + rtl8168_mdio_write(tp, 0x15, 0x0284); + rtl8168_mdio_write(tp, 0x19, 0x588f); + rtl8168_mdio_write(tp, 0x15, 0x0285); + rtl8168_mdio_write(tp, 0x19, 0x5520); + rtl8168_mdio_write(tp, 0x15, 0x0286); + rtl8168_mdio_write(tp, 0x19, 0x7801); + rtl8168_mdio_write(tp, 0x15, 0x0287); + rtl8168_mdio_write(tp, 0x19, 0x4000); + rtl8168_mdio_write(tp, 0x15, 0x0288); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x0289); + rtl8168_mdio_write(tp, 0x19, 0x4818); + rtl8168_mdio_write(tp, 0x15, 0x028a); + rtl8168_mdio_write(tp, 0x19, 0x5051); + rtl8168_mdio_write(tp, 0x15, 0x028b); + rtl8168_mdio_write(tp, 0x19, 0x4808); + rtl8168_mdio_write(tp, 0x15, 0x028c); + rtl8168_mdio_write(tp, 0x19, 0x4050); + rtl8168_mdio_write(tp, 0x15, 0x028d); + rtl8168_mdio_write(tp, 0x19, 0x4462); + rtl8168_mdio_write(tp, 0x15, 0x028e); + rtl8168_mdio_write(tp, 0x19, 0x40c4); + rtl8168_mdio_write(tp, 0x15, 0x028f); + rtl8168_mdio_write(tp, 0x19, 0x4473); + rtl8168_mdio_write(tp, 0x15, 0x0290); + rtl8168_mdio_write(tp, 0x19, 0x5041); + rtl8168_mdio_write(tp, 0x15, 0x0291); + rtl8168_mdio_write(tp, 0x19, 0x6b00); + rtl8168_mdio_write(tp, 0x15, 0x0292); + rtl8168_mdio_write(tp, 0x19, 0x31f5); + rtl8168_mdio_write(tp, 0x15, 0x0293); + rtl8168_mdio_write(tp, 0x19, 0xb619); + rtl8168_mdio_write(tp, 0x15, 0x0294); + rtl8168_mdio_write(tp, 0x19, 0x80d9); + rtl8168_mdio_write(tp, 0x15, 0x0295); + rtl8168_mdio_write(tp, 0x19, 0xbd06); + rtl8168_mdio_write(tp, 0x15, 0x0296); + rtl8168_mdio_write(tp, 0x19, 0xbb0d); + rtl8168_mdio_write(tp, 0x15, 0x0297); + rtl8168_mdio_write(tp, 0x19, 0xaf14); + rtl8168_mdio_write(tp, 0x15, 0x0298); + rtl8168_mdio_write(tp, 0x19, 0x8efa); + rtl8168_mdio_write(tp, 0x15, 0x0299); + rtl8168_mdio_write(tp, 0x19, 0x5049); + rtl8168_mdio_write(tp, 0x15, 0x029a); + rtl8168_mdio_write(tp, 0x19, 0x3248); + rtl8168_mdio_write(tp, 0x15, 0x029b); + rtl8168_mdio_write(tp, 0x19, 0x4c10); + rtl8168_mdio_write(tp, 0x15, 0x029c); + rtl8168_mdio_write(tp, 0x19, 0x44b0); + rtl8168_mdio_write(tp, 0x15, 0x029d); + rtl8168_mdio_write(tp, 0x19, 0x4c00); + rtl8168_mdio_write(tp, 0x15, 0x029e); + rtl8168_mdio_write(tp, 0x19, 0x3292); + rtl8168_mdio_write(tp, 0x15, 0x029f); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02a0); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02a1); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02a2); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02a3); + rtl8168_mdio_write(tp, 0x19, 0x481f); + rtl8168_mdio_write(tp, 0x15, 0x02a4); + rtl8168_mdio_write(tp, 0x19, 0x5005); + rtl8168_mdio_write(tp, 0x15, 0x02a5); + rtl8168_mdio_write(tp, 0x19, 0x480f); + rtl8168_mdio_write(tp, 0x15, 0x02a6); + rtl8168_mdio_write(tp, 0x19, 0xac00); + rtl8168_mdio_write(tp, 0x15, 0x02a7); + rtl8168_mdio_write(tp, 0x19, 0x31a6); + rtl8168_mdio_write(tp, 0x15, 0x02a8); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02a9); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02aa); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02ab); + rtl8168_mdio_write(tp, 0x19, 0x31ba); + rtl8168_mdio_write(tp, 0x15, 0x02ac); + rtl8168_mdio_write(tp, 0x19, 0x31d5); + rtl8168_mdio_write(tp, 0x15, 0x02ad); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02ae); + rtl8168_mdio_write(tp, 0x19, 0x5cf0); + rtl8168_mdio_write(tp, 0x15, 0x02af); + rtl8168_mdio_write(tp, 0x19, 0x588c); + rtl8168_mdio_write(tp, 0x15, 0x02b0); + rtl8168_mdio_write(tp, 0x19, 0x542f); + rtl8168_mdio_write(tp, 0x15, 0x02b1); + rtl8168_mdio_write(tp, 0x19, 0x7ffb); + rtl8168_mdio_write(tp, 0x15, 0x02b2); + rtl8168_mdio_write(tp, 0x19, 0x6ff8); + rtl8168_mdio_write(tp, 0x15, 0x02b3); + rtl8168_mdio_write(tp, 0x19, 0x64a4); + rtl8168_mdio_write(tp, 0x15, 0x02b4); + rtl8168_mdio_write(tp, 0x19, 0x64a0); + rtl8168_mdio_write(tp, 0x15, 0x02b5); + rtl8168_mdio_write(tp, 0x19, 0x6800); + rtl8168_mdio_write(tp, 0x15, 0x02b6); + rtl8168_mdio_write(tp, 0x19, 0x4400); + rtl8168_mdio_write(tp, 0x15, 0x02b7); + rtl8168_mdio_write(tp, 0x19, 0x4020); + rtl8168_mdio_write(tp, 0x15, 0x02b8); + rtl8168_mdio_write(tp, 0x19, 0x4480); + rtl8168_mdio_write(tp, 0x15, 0x02b9); + rtl8168_mdio_write(tp, 0x19, 0x9e00); + rtl8168_mdio_write(tp, 0x15, 0x02ba); + rtl8168_mdio_write(tp, 0x19, 0x4891); + rtl8168_mdio_write(tp, 0x15, 0x02bb); + rtl8168_mdio_write(tp, 0x19, 0x4cc0); + rtl8168_mdio_write(tp, 0x15, 0x02bc); + rtl8168_mdio_write(tp, 0x19, 0x4801); + rtl8168_mdio_write(tp, 0x15, 0x02bd); + rtl8168_mdio_write(tp, 0x19, 0xa609); + rtl8168_mdio_write(tp, 0x15, 0x02be); + rtl8168_mdio_write(tp, 0x19, 0xd64f); + rtl8168_mdio_write(tp, 0x15, 0x02bf); + rtl8168_mdio_write(tp, 0x19, 0x004e); + rtl8168_mdio_write(tp, 0x15, 0x02c0); + rtl8168_mdio_write(tp, 0x19, 0x87fe); + rtl8168_mdio_write(tp, 0x15, 0x02c1); + rtl8168_mdio_write(tp, 0x19, 0x32c6); + rtl8168_mdio_write(tp, 0x15, 0x02c2); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02c3); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02c4); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02c5); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02c6); + rtl8168_mdio_write(tp, 0x19, 0x48b2); + rtl8168_mdio_write(tp, 0x15, 0x02c7); + rtl8168_mdio_write(tp, 0x19, 0x4020); + rtl8168_mdio_write(tp, 0x15, 0x02c8); + rtl8168_mdio_write(tp, 0x19, 0x4822); + rtl8168_mdio_write(tp, 0x15, 0x02c9); + rtl8168_mdio_write(tp, 0x19, 0x4488); + rtl8168_mdio_write(tp, 0x15, 0x02ca); + rtl8168_mdio_write(tp, 0x19, 0xd64f); + rtl8168_mdio_write(tp, 0x15, 0x02cb); + rtl8168_mdio_write(tp, 0x19, 0x0042); + rtl8168_mdio_write(tp, 0x15, 0x02cc); + rtl8168_mdio_write(tp, 0x19, 0x8203); + rtl8168_mdio_write(tp, 0x15, 0x02cd); + rtl8168_mdio_write(tp, 0x19, 0x4cc8); + rtl8168_mdio_write(tp, 0x15, 0x02ce); + rtl8168_mdio_write(tp, 0x19, 0x32d0); + rtl8168_mdio_write(tp, 0x15, 0x02cf); + rtl8168_mdio_write(tp, 0x19, 0x4cc0); + rtl8168_mdio_write(tp, 0x15, 0x02d0); + rtl8168_mdio_write(tp, 0x19, 0xc4d4); + rtl8168_mdio_write(tp, 0x15, 0x02d1); + rtl8168_mdio_write(tp, 0x19, 0x00f9); + rtl8168_mdio_write(tp, 0x15, 0x02d2); + rtl8168_mdio_write(tp, 0x19, 0xa51a); + rtl8168_mdio_write(tp, 0x15, 0x02d3); + rtl8168_mdio_write(tp, 0x19, 0x32d9); + rtl8168_mdio_write(tp, 0x15, 0x02d4); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02d5); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02d6); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02d7); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02d8); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02d9); + rtl8168_mdio_write(tp, 0x19, 0x48b3); + rtl8168_mdio_write(tp, 0x15, 0x02da); + rtl8168_mdio_write(tp, 0x19, 0x4020); + rtl8168_mdio_write(tp, 0x15, 0x02db); + rtl8168_mdio_write(tp, 0x19, 0x4823); + rtl8168_mdio_write(tp, 0x15, 0x02dc); + rtl8168_mdio_write(tp, 0x19, 0x4410); + rtl8168_mdio_write(tp, 0x15, 0x02dd); + rtl8168_mdio_write(tp, 0x19, 0xb630); + rtl8168_mdio_write(tp, 0x15, 0x02de); + rtl8168_mdio_write(tp, 0x19, 0x7dc8); + rtl8168_mdio_write(tp, 0x15, 0x02df); + rtl8168_mdio_write(tp, 0x19, 0x8203); + rtl8168_mdio_write(tp, 0x15, 0x02e0); + rtl8168_mdio_write(tp, 0x19, 0x4c48); + rtl8168_mdio_write(tp, 0x15, 0x02e1); + rtl8168_mdio_write(tp, 0x19, 0x32e3); + rtl8168_mdio_write(tp, 0x15, 0x02e2); + rtl8168_mdio_write(tp, 0x19, 0x4c40); + rtl8168_mdio_write(tp, 0x15, 0x02e3); + rtl8168_mdio_write(tp, 0x19, 0x9bfa); + rtl8168_mdio_write(tp, 0x15, 0x02e4); + rtl8168_mdio_write(tp, 0x19, 0x84ca); + rtl8168_mdio_write(tp, 0x15, 0x02e5); + rtl8168_mdio_write(tp, 0x19, 0x85f8); + rtl8168_mdio_write(tp, 0x15, 0x02e6); + rtl8168_mdio_write(tp, 0x19, 0x32ec); + rtl8168_mdio_write(tp, 0x15, 0x02e7); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02e8); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02e9); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02ea); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02eb); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x02ec); + rtl8168_mdio_write(tp, 0x19, 0x48d4); + rtl8168_mdio_write(tp, 0x15, 0x02ed); + rtl8168_mdio_write(tp, 0x19, 0x4020); + rtl8168_mdio_write(tp, 0x15, 0x02ee); + rtl8168_mdio_write(tp, 0x19, 0x4844); + rtl8168_mdio_write(tp, 0x15, 0x02ef); + rtl8168_mdio_write(tp, 0x19, 0x4420); + rtl8168_mdio_write(tp, 0x15, 0x02f0); + rtl8168_mdio_write(tp, 0x19, 0x6800); + rtl8168_mdio_write(tp, 0x15, 0x02f1); + rtl8168_mdio_write(tp, 0x19, 0x7dc0); + rtl8168_mdio_write(tp, 0x15, 0x02f2); + rtl8168_mdio_write(tp, 0x19, 0x4c40); + rtl8168_mdio_write(tp, 0x15, 0x02f3); + rtl8168_mdio_write(tp, 0x19, 0x7c0b); + rtl8168_mdio_write(tp, 0x15, 0x02f4); + rtl8168_mdio_write(tp, 0x19, 0x6c08); + rtl8168_mdio_write(tp, 0x15, 0x02f5); + rtl8168_mdio_write(tp, 0x19, 0x3311); + rtl8168_mdio_write(tp, 0x15, 0x02f6); + rtl8168_mdio_write(tp, 0x19, 0x9cfd); + rtl8168_mdio_write(tp, 0x15, 0x02f7); + rtl8168_mdio_write(tp, 0x19, 0xb616); + rtl8168_mdio_write(tp, 0x15, 0x02f8); + rtl8168_mdio_write(tp, 0x19, 0xc42b); + rtl8168_mdio_write(tp, 0x15, 0x02f9); + rtl8168_mdio_write(tp, 0x19, 0x00e0); + rtl8168_mdio_write(tp, 0x15, 0x02fa); + rtl8168_mdio_write(tp, 0x19, 0xc455); + rtl8168_mdio_write(tp, 0x15, 0x02fb); + rtl8168_mdio_write(tp, 0x19, 0x00b3); + rtl8168_mdio_write(tp, 0x15, 0x02fc); + rtl8168_mdio_write(tp, 0x19, 0xb20a); + rtl8168_mdio_write(tp, 0x15, 0x02fd); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x02fe); + rtl8168_mdio_write(tp, 0x19, 0x6c02); + rtl8168_mdio_write(tp, 0x15, 0x02ff); + rtl8168_mdio_write(tp, 0x19, 0x8204); + rtl8168_mdio_write(tp, 0x15, 0x0300); + rtl8168_mdio_write(tp, 0x19, 0x7c04); + rtl8168_mdio_write(tp, 0x15, 0x0301); + rtl8168_mdio_write(tp, 0x19, 0x7404); + rtl8168_mdio_write(tp, 0x15, 0x0302); + rtl8168_mdio_write(tp, 0x19, 0x32f3); + rtl8168_mdio_write(tp, 0x15, 0x0303); + rtl8168_mdio_write(tp, 0x19, 0x7c04); + rtl8168_mdio_write(tp, 0x15, 0x0304); + rtl8168_mdio_write(tp, 0x19, 0x7400); + rtl8168_mdio_write(tp, 0x15, 0x0305); + rtl8168_mdio_write(tp, 0x19, 0x32f3); + rtl8168_mdio_write(tp, 0x15, 0x0306); + rtl8168_mdio_write(tp, 0x19, 0xefed); + rtl8168_mdio_write(tp, 0x15, 0x0307); + rtl8168_mdio_write(tp, 0x19, 0x3342); + rtl8168_mdio_write(tp, 0x15, 0x0308); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0309); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x030a); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x030b); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x030c); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x030d); + rtl8168_mdio_write(tp, 0x19, 0x3006); + rtl8168_mdio_write(tp, 0x15, 0x030e); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x030f); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0310); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0311); + rtl8168_mdio_write(tp, 0x19, 0x7c08); + rtl8168_mdio_write(tp, 0x15, 0x0312); + rtl8168_mdio_write(tp, 0x19, 0xa207); + rtl8168_mdio_write(tp, 0x15, 0x0313); + rtl8168_mdio_write(tp, 0x19, 0x4c00); + rtl8168_mdio_write(tp, 0x15, 0x0314); + rtl8168_mdio_write(tp, 0x19, 0x3322); + rtl8168_mdio_write(tp, 0x15, 0x0315); + rtl8168_mdio_write(tp, 0x19, 0x4041); + rtl8168_mdio_write(tp, 0x15, 0x0316); + rtl8168_mdio_write(tp, 0x19, 0x7d07); + rtl8168_mdio_write(tp, 0x15, 0x0317); + rtl8168_mdio_write(tp, 0x19, 0x4502); + rtl8168_mdio_write(tp, 0x15, 0x0318); + rtl8168_mdio_write(tp, 0x19, 0x3322); + rtl8168_mdio_write(tp, 0x15, 0x0319); + rtl8168_mdio_write(tp, 0x19, 0x4c08); + rtl8168_mdio_write(tp, 0x15, 0x031a); + rtl8168_mdio_write(tp, 0x19, 0x3322); + rtl8168_mdio_write(tp, 0x15, 0x031b); + rtl8168_mdio_write(tp, 0x19, 0x7d80); + rtl8168_mdio_write(tp, 0x15, 0x031c); + rtl8168_mdio_write(tp, 0x19, 0x5180); + rtl8168_mdio_write(tp, 0x15, 0x031d); + rtl8168_mdio_write(tp, 0x19, 0x3320); + rtl8168_mdio_write(tp, 0x15, 0x031e); + rtl8168_mdio_write(tp, 0x19, 0x7d80); + rtl8168_mdio_write(tp, 0x15, 0x031f); + rtl8168_mdio_write(tp, 0x19, 0x5000); + rtl8168_mdio_write(tp, 0x15, 0x0320); + rtl8168_mdio_write(tp, 0x19, 0x7d07); + rtl8168_mdio_write(tp, 0x15, 0x0321); + rtl8168_mdio_write(tp, 0x19, 0x4402); + rtl8168_mdio_write(tp, 0x15, 0x0322); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x0323); + rtl8168_mdio_write(tp, 0x19, 0x6c02); + rtl8168_mdio_write(tp, 0x15, 0x0324); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x0325); + rtl8168_mdio_write(tp, 0x19, 0xb30c); + rtl8168_mdio_write(tp, 0x15, 0x0326); + rtl8168_mdio_write(tp, 0x19, 0xb206); + rtl8168_mdio_write(tp, 0x15, 0x0327); + rtl8168_mdio_write(tp, 0x19, 0xb103); + rtl8168_mdio_write(tp, 0x15, 0x0328); + rtl8168_mdio_write(tp, 0x19, 0x6c00); + rtl8168_mdio_write(tp, 0x15, 0x0329); + rtl8168_mdio_write(tp, 0x19, 0x32f6); + rtl8168_mdio_write(tp, 0x15, 0x032a); + rtl8168_mdio_write(tp, 0x19, 0x6c00); + rtl8168_mdio_write(tp, 0x15, 0x032b); + rtl8168_mdio_write(tp, 0x19, 0x3352); + rtl8168_mdio_write(tp, 0x15, 0x032c); + rtl8168_mdio_write(tp, 0x19, 0xb103); + rtl8168_mdio_write(tp, 0x15, 0x032d); + rtl8168_mdio_write(tp, 0x19, 0x6c00); + rtl8168_mdio_write(tp, 0x15, 0x032e); + rtl8168_mdio_write(tp, 0x19, 0x336a); + rtl8168_mdio_write(tp, 0x15, 0x032f); + rtl8168_mdio_write(tp, 0x19, 0x6c00); + rtl8168_mdio_write(tp, 0x15, 0x0330); + rtl8168_mdio_write(tp, 0x19, 0x3382); + rtl8168_mdio_write(tp, 0x15, 0x0331); + rtl8168_mdio_write(tp, 0x19, 0xb206); + rtl8168_mdio_write(tp, 0x15, 0x0332); + rtl8168_mdio_write(tp, 0x19, 0xb103); + rtl8168_mdio_write(tp, 0x15, 0x0333); + rtl8168_mdio_write(tp, 0x19, 0x6c00); + rtl8168_mdio_write(tp, 0x15, 0x0334); + rtl8168_mdio_write(tp, 0x19, 0x3395); + rtl8168_mdio_write(tp, 0x15, 0x0335); + rtl8168_mdio_write(tp, 0x19, 0x6c00); + rtl8168_mdio_write(tp, 0x15, 0x0336); + rtl8168_mdio_write(tp, 0x19, 0x33c6); + rtl8168_mdio_write(tp, 0x15, 0x0337); + rtl8168_mdio_write(tp, 0x19, 0xb103); + rtl8168_mdio_write(tp, 0x15, 0x0338); + rtl8168_mdio_write(tp, 0x19, 0x6c00); + rtl8168_mdio_write(tp, 0x15, 0x0339); + rtl8168_mdio_write(tp, 0x19, 0x33d7); + rtl8168_mdio_write(tp, 0x15, 0x033a); + rtl8168_mdio_write(tp, 0x19, 0x6c00); + rtl8168_mdio_write(tp, 0x15, 0x033b); + rtl8168_mdio_write(tp, 0x19, 0x33f2); + rtl8168_mdio_write(tp, 0x15, 0x033c); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x033d); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x033e); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x033f); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0340); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0341); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0342); + rtl8168_mdio_write(tp, 0x19, 0x49b5); + rtl8168_mdio_write(tp, 0x15, 0x0343); + rtl8168_mdio_write(tp, 0x19, 0x7d00); + rtl8168_mdio_write(tp, 0x15, 0x0344); + rtl8168_mdio_write(tp, 0x19, 0x4d00); + rtl8168_mdio_write(tp, 0x15, 0x0345); + rtl8168_mdio_write(tp, 0x19, 0x6880); + rtl8168_mdio_write(tp, 0x15, 0x0346); + rtl8168_mdio_write(tp, 0x19, 0x7c08); + rtl8168_mdio_write(tp, 0x15, 0x0347); + rtl8168_mdio_write(tp, 0x19, 0x6c08); + rtl8168_mdio_write(tp, 0x15, 0x0348); + rtl8168_mdio_write(tp, 0x19, 0x4925); + rtl8168_mdio_write(tp, 0x15, 0x0349); + rtl8168_mdio_write(tp, 0x19, 0x403b); + rtl8168_mdio_write(tp, 0x15, 0x034a); + rtl8168_mdio_write(tp, 0x19, 0xa602); + rtl8168_mdio_write(tp, 0x15, 0x034b); + rtl8168_mdio_write(tp, 0x19, 0x402f); + rtl8168_mdio_write(tp, 0x15, 0x034c); + rtl8168_mdio_write(tp, 0x19, 0x4484); + rtl8168_mdio_write(tp, 0x15, 0x034d); + rtl8168_mdio_write(tp, 0x19, 0x40c8); + rtl8168_mdio_write(tp, 0x15, 0x034e); + rtl8168_mdio_write(tp, 0x19, 0x44c4); + rtl8168_mdio_write(tp, 0x15, 0x034f); + rtl8168_mdio_write(tp, 0x19, 0xd64f); + rtl8168_mdio_write(tp, 0x15, 0x0350); + rtl8168_mdio_write(tp, 0x19, 0x00bd); + rtl8168_mdio_write(tp, 0x15, 0x0351); + rtl8168_mdio_write(tp, 0x19, 0x3311); + rtl8168_mdio_write(tp, 0x15, 0x0352); + rtl8168_mdio_write(tp, 0x19, 0xc8ed); + rtl8168_mdio_write(tp, 0x15, 0x0353); + rtl8168_mdio_write(tp, 0x19, 0x00fc); + rtl8168_mdio_write(tp, 0x15, 0x0354); + rtl8168_mdio_write(tp, 0x19, 0x8221); + rtl8168_mdio_write(tp, 0x15, 0x0355); + rtl8168_mdio_write(tp, 0x19, 0xd11d); + rtl8168_mdio_write(tp, 0x15, 0x0356); + rtl8168_mdio_write(tp, 0x19, 0x001f); + rtl8168_mdio_write(tp, 0x15, 0x0357); + rtl8168_mdio_write(tp, 0x19, 0xde18); + rtl8168_mdio_write(tp, 0x15, 0x0358); + rtl8168_mdio_write(tp, 0x19, 0x0008); + rtl8168_mdio_write(tp, 0x15, 0x0359); + rtl8168_mdio_write(tp, 0x19, 0x91f6); + rtl8168_mdio_write(tp, 0x15, 0x035a); + rtl8168_mdio_write(tp, 0x19, 0x3360); + rtl8168_mdio_write(tp, 0x15, 0x035b); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x035c); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x035d); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x035e); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x035f); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0360); + rtl8168_mdio_write(tp, 0x19, 0x4bb6); + rtl8168_mdio_write(tp, 0x15, 0x0361); + rtl8168_mdio_write(tp, 0x19, 0x4064); + rtl8168_mdio_write(tp, 0x15, 0x0362); + rtl8168_mdio_write(tp, 0x19, 0x4b26); + rtl8168_mdio_write(tp, 0x15, 0x0363); + rtl8168_mdio_write(tp, 0x19, 0x4410); + rtl8168_mdio_write(tp, 0x15, 0x0364); + rtl8168_mdio_write(tp, 0x19, 0x4006); + rtl8168_mdio_write(tp, 0x15, 0x0365); + rtl8168_mdio_write(tp, 0x19, 0x4490); + rtl8168_mdio_write(tp, 0x15, 0x0366); + rtl8168_mdio_write(tp, 0x19, 0x6900); + rtl8168_mdio_write(tp, 0x15, 0x0367); + rtl8168_mdio_write(tp, 0x19, 0xb6a6); + rtl8168_mdio_write(tp, 0x15, 0x0368); + rtl8168_mdio_write(tp, 0x19, 0x9e02); + rtl8168_mdio_write(tp, 0x15, 0x0369); + rtl8168_mdio_write(tp, 0x19, 0x3311); + rtl8168_mdio_write(tp, 0x15, 0x036a); + rtl8168_mdio_write(tp, 0x19, 0xd11d); + rtl8168_mdio_write(tp, 0x15, 0x036b); + rtl8168_mdio_write(tp, 0x19, 0x000a); + rtl8168_mdio_write(tp, 0x15, 0x036c); + rtl8168_mdio_write(tp, 0x19, 0xbb0f); + rtl8168_mdio_write(tp, 0x15, 0x036d); + rtl8168_mdio_write(tp, 0x19, 0x8102); + rtl8168_mdio_write(tp, 0x15, 0x036e); + rtl8168_mdio_write(tp, 0x19, 0x3371); + rtl8168_mdio_write(tp, 0x15, 0x036f); + rtl8168_mdio_write(tp, 0x19, 0xa21e); + rtl8168_mdio_write(tp, 0x15, 0x0370); + rtl8168_mdio_write(tp, 0x19, 0x33b6); + rtl8168_mdio_write(tp, 0x15, 0x0371); + rtl8168_mdio_write(tp, 0x19, 0x91f6); + rtl8168_mdio_write(tp, 0x15, 0x0372); + rtl8168_mdio_write(tp, 0x19, 0xc218); + rtl8168_mdio_write(tp, 0x15, 0x0373); + rtl8168_mdio_write(tp, 0x19, 0x00f4); + rtl8168_mdio_write(tp, 0x15, 0x0374); + rtl8168_mdio_write(tp, 0x19, 0x33b6); + rtl8168_mdio_write(tp, 0x15, 0x0375); + rtl8168_mdio_write(tp, 0x19, 0x32ec); + rtl8168_mdio_write(tp, 0x15, 0x0376); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0377); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0378); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x0379); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x037a); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x037b); + rtl8168_mdio_write(tp, 0x19, 0x4b97); + rtl8168_mdio_write(tp, 0x15, 0x037c); + rtl8168_mdio_write(tp, 0x19, 0x402b); + rtl8168_mdio_write(tp, 0x15, 0x037d); + rtl8168_mdio_write(tp, 0x19, 0x4b07); + rtl8168_mdio_write(tp, 0x15, 0x037e); + rtl8168_mdio_write(tp, 0x19, 0x4422); + rtl8168_mdio_write(tp, 0x15, 0x037f); + rtl8168_mdio_write(tp, 0x19, 0x6980); + rtl8168_mdio_write(tp, 0x15, 0x0380); + rtl8168_mdio_write(tp, 0x19, 0xb608); + rtl8168_mdio_write(tp, 0x15, 0x0381); + rtl8168_mdio_write(tp, 0x19, 0x3311); + rtl8168_mdio_write(tp, 0x15, 0x0382); + rtl8168_mdio_write(tp, 0x19, 0xbc05); + rtl8168_mdio_write(tp, 0x15, 0x0383); + rtl8168_mdio_write(tp, 0x19, 0xc21c); + rtl8168_mdio_write(tp, 0x15, 0x0384); + rtl8168_mdio_write(tp, 0x19, 0x0032); + rtl8168_mdio_write(tp, 0x15, 0x0385); + rtl8168_mdio_write(tp, 0x19, 0xa1fb); + rtl8168_mdio_write(tp, 0x15, 0x0386); + rtl8168_mdio_write(tp, 0x19, 0x338d); + rtl8168_mdio_write(tp, 0x15, 0x0387); + rtl8168_mdio_write(tp, 0x19, 0x32ae); + rtl8168_mdio_write(tp, 0x15, 0x0388); + rtl8168_mdio_write(tp, 0x19, 0x330d); + rtl8168_mdio_write(tp, 0x15, 0x0389); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x038a); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x038b); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x038c); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x038d); + rtl8168_mdio_write(tp, 0x19, 0x4b97); + rtl8168_mdio_write(tp, 0x15, 0x038e); + rtl8168_mdio_write(tp, 0x19, 0x6a08); + rtl8168_mdio_write(tp, 0x15, 0x038f); + rtl8168_mdio_write(tp, 0x19, 0x4b07); + rtl8168_mdio_write(tp, 0x15, 0x0390); + rtl8168_mdio_write(tp, 0x19, 0x40ac); + rtl8168_mdio_write(tp, 0x15, 0x0391); + rtl8168_mdio_write(tp, 0x19, 0x4445); + rtl8168_mdio_write(tp, 0x15, 0x0392); + rtl8168_mdio_write(tp, 0x19, 0x404e); + rtl8168_mdio_write(tp, 0x15, 0x0393); + rtl8168_mdio_write(tp, 0x19, 0x4461); + rtl8168_mdio_write(tp, 0x15, 0x0394); + rtl8168_mdio_write(tp, 0x19, 0x3311); + rtl8168_mdio_write(tp, 0x15, 0x0395); + rtl8168_mdio_write(tp, 0x19, 0x9c0a); + rtl8168_mdio_write(tp, 0x15, 0x0396); + rtl8168_mdio_write(tp, 0x19, 0x63da); + rtl8168_mdio_write(tp, 0x15, 0x0397); + rtl8168_mdio_write(tp, 0x19, 0x6f0c); + rtl8168_mdio_write(tp, 0x15, 0x0398); + rtl8168_mdio_write(tp, 0x19, 0x5440); + rtl8168_mdio_write(tp, 0x15, 0x0399); + rtl8168_mdio_write(tp, 0x19, 0x4b98); + rtl8168_mdio_write(tp, 0x15, 0x039a); + rtl8168_mdio_write(tp, 0x19, 0x7c40); + rtl8168_mdio_write(tp, 0x15, 0x039b); + rtl8168_mdio_write(tp, 0x19, 0x4c00); + rtl8168_mdio_write(tp, 0x15, 0x039c); + rtl8168_mdio_write(tp, 0x19, 0x4b08); + rtl8168_mdio_write(tp, 0x15, 0x039d); + rtl8168_mdio_write(tp, 0x19, 0x63d8); + rtl8168_mdio_write(tp, 0x15, 0x039e); + rtl8168_mdio_write(tp, 0x19, 0x33a5); + rtl8168_mdio_write(tp, 0x15, 0x039f); + rtl8168_mdio_write(tp, 0x19, 0xd64f); + rtl8168_mdio_write(tp, 0x15, 0x03a0); + rtl8168_mdio_write(tp, 0x19, 0x00e8); + rtl8168_mdio_write(tp, 0x15, 0x03a1); + rtl8168_mdio_write(tp, 0x19, 0x820e); + rtl8168_mdio_write(tp, 0x15, 0x03a2); + rtl8168_mdio_write(tp, 0x19, 0xa10d); + rtl8168_mdio_write(tp, 0x15, 0x03a3); + rtl8168_mdio_write(tp, 0x19, 0x9df1); + rtl8168_mdio_write(tp, 0x15, 0x03a4); + rtl8168_mdio_write(tp, 0x19, 0x33af); + rtl8168_mdio_write(tp, 0x15, 0x03a5); + rtl8168_mdio_write(tp, 0x19, 0xd64f); + rtl8168_mdio_write(tp, 0x15, 0x03a6); + rtl8168_mdio_write(tp, 0x19, 0x00f9); + rtl8168_mdio_write(tp, 0x15, 0x03a7); + rtl8168_mdio_write(tp, 0x19, 0xc017); + rtl8168_mdio_write(tp, 0x15, 0x03a8); + rtl8168_mdio_write(tp, 0x19, 0x0007); + rtl8168_mdio_write(tp, 0x15, 0x03a9); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x03aa); + rtl8168_mdio_write(tp, 0x19, 0x6c03); + rtl8168_mdio_write(tp, 0x15, 0x03ab); + rtl8168_mdio_write(tp, 0x19, 0xa104); + rtl8168_mdio_write(tp, 0x15, 0x03ac); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x03ad); + rtl8168_mdio_write(tp, 0x19, 0x6c00); + rtl8168_mdio_write(tp, 0x15, 0x03ae); + rtl8168_mdio_write(tp, 0x19, 0x9df7); + rtl8168_mdio_write(tp, 0x15, 0x03af); + rtl8168_mdio_write(tp, 0x19, 0x7c03); + rtl8168_mdio_write(tp, 0x15, 0x03b0); + rtl8168_mdio_write(tp, 0x19, 0x6c08); + rtl8168_mdio_write(tp, 0x15, 0x03b1); + rtl8168_mdio_write(tp, 0x19, 0x33b6); + rtl8168_mdio_write(tp, 0x15, 0x03b2); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03b3); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03b4); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03b5); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03b6); + rtl8168_mdio_write(tp, 0x19, 0x55af); + rtl8168_mdio_write(tp, 0x15, 0x03b7); + rtl8168_mdio_write(tp, 0x19, 0x7ff0); + rtl8168_mdio_write(tp, 0x15, 0x03b8); + rtl8168_mdio_write(tp, 0x19, 0x6ff0); + rtl8168_mdio_write(tp, 0x15, 0x03b9); + rtl8168_mdio_write(tp, 0x19, 0x4bb9); + rtl8168_mdio_write(tp, 0x15, 0x03ba); + rtl8168_mdio_write(tp, 0x19, 0x6a80); + rtl8168_mdio_write(tp, 0x15, 0x03bb); + rtl8168_mdio_write(tp, 0x19, 0x4b29); + rtl8168_mdio_write(tp, 0x15, 0x03bc); + rtl8168_mdio_write(tp, 0x19, 0x4041); + rtl8168_mdio_write(tp, 0x15, 0x03bd); + rtl8168_mdio_write(tp, 0x19, 0x440a); + rtl8168_mdio_write(tp, 0x15, 0x03be); + rtl8168_mdio_write(tp, 0x19, 0x4029); + rtl8168_mdio_write(tp, 0x15, 0x03bf); + rtl8168_mdio_write(tp, 0x19, 0x4418); + rtl8168_mdio_write(tp, 0x15, 0x03c0); + rtl8168_mdio_write(tp, 0x19, 0x4090); + rtl8168_mdio_write(tp, 0x15, 0x03c1); + rtl8168_mdio_write(tp, 0x19, 0x4438); + rtl8168_mdio_write(tp, 0x15, 0x03c2); + rtl8168_mdio_write(tp, 0x19, 0x40c4); + rtl8168_mdio_write(tp, 0x15, 0x03c3); + rtl8168_mdio_write(tp, 0x19, 0x447b); + rtl8168_mdio_write(tp, 0x15, 0x03c4); + rtl8168_mdio_write(tp, 0x19, 0xb6c4); + rtl8168_mdio_write(tp, 0x15, 0x03c5); + rtl8168_mdio_write(tp, 0x19, 0x3311); + rtl8168_mdio_write(tp, 0x15, 0x03c6); + rtl8168_mdio_write(tp, 0x19, 0x9bfe); + rtl8168_mdio_write(tp, 0x15, 0x03c7); + rtl8168_mdio_write(tp, 0x19, 0x33cc); + rtl8168_mdio_write(tp, 0x15, 0x03c8); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03c9); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03ca); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03cb); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03cc); + rtl8168_mdio_write(tp, 0x19, 0x542f); + rtl8168_mdio_write(tp, 0x15, 0x03cd); + rtl8168_mdio_write(tp, 0x19, 0x499a); + rtl8168_mdio_write(tp, 0x15, 0x03ce); + rtl8168_mdio_write(tp, 0x19, 0x7c40); + rtl8168_mdio_write(tp, 0x15, 0x03cf); + rtl8168_mdio_write(tp, 0x19, 0x4c40); + rtl8168_mdio_write(tp, 0x15, 0x03d0); + rtl8168_mdio_write(tp, 0x19, 0x490a); + rtl8168_mdio_write(tp, 0x15, 0x03d1); + rtl8168_mdio_write(tp, 0x19, 0x405e); + rtl8168_mdio_write(tp, 0x15, 0x03d2); + rtl8168_mdio_write(tp, 0x19, 0x44f8); + rtl8168_mdio_write(tp, 0x15, 0x03d3); + rtl8168_mdio_write(tp, 0x19, 0x6b00); + rtl8168_mdio_write(tp, 0x15, 0x03d4); + rtl8168_mdio_write(tp, 0x19, 0xd64f); + rtl8168_mdio_write(tp, 0x15, 0x03d5); + rtl8168_mdio_write(tp, 0x19, 0x0028); + rtl8168_mdio_write(tp, 0x15, 0x03d6); + rtl8168_mdio_write(tp, 0x19, 0x3311); + rtl8168_mdio_write(tp, 0x15, 0x03d7); + rtl8168_mdio_write(tp, 0x19, 0xbd27); + rtl8168_mdio_write(tp, 0x15, 0x03d8); + rtl8168_mdio_write(tp, 0x19, 0x9cfc); + rtl8168_mdio_write(tp, 0x15, 0x03d9); + rtl8168_mdio_write(tp, 0x19, 0xc639); + rtl8168_mdio_write(tp, 0x15, 0x03da); + rtl8168_mdio_write(tp, 0x19, 0x000f); + rtl8168_mdio_write(tp, 0x15, 0x03db); + rtl8168_mdio_write(tp, 0x19, 0x9e03); + rtl8168_mdio_write(tp, 0x15, 0x03dc); + rtl8168_mdio_write(tp, 0x19, 0x7c01); + rtl8168_mdio_write(tp, 0x15, 0x03dd); + rtl8168_mdio_write(tp, 0x19, 0x4c01); + rtl8168_mdio_write(tp, 0x15, 0x03de); + rtl8168_mdio_write(tp, 0x19, 0x9af6); + rtl8168_mdio_write(tp, 0x15, 0x03df); + rtl8168_mdio_write(tp, 0x19, 0x7c12); + rtl8168_mdio_write(tp, 0x15, 0x03e0); + rtl8168_mdio_write(tp, 0x19, 0x4c52); + rtl8168_mdio_write(tp, 0x15, 0x03e1); + rtl8168_mdio_write(tp, 0x19, 0x4470); + rtl8168_mdio_write(tp, 0x15, 0x03e2); + rtl8168_mdio_write(tp, 0x19, 0x7c12); + rtl8168_mdio_write(tp, 0x15, 0x03e3); + rtl8168_mdio_write(tp, 0x19, 0x4c40); + rtl8168_mdio_write(tp, 0x15, 0x03e4); + rtl8168_mdio_write(tp, 0x19, 0x33d4); + rtl8168_mdio_write(tp, 0x15, 0x03e5); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03e6); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03e7); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03e8); + rtl8168_mdio_write(tp, 0x19, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x03e9); + rtl8168_mdio_write(tp, 0x19, 0x49bb); + rtl8168_mdio_write(tp, 0x15, 0x03ea); + rtl8168_mdio_write(tp, 0x19, 0x4478); + rtl8168_mdio_write(tp, 0x15, 0x03eb); + rtl8168_mdio_write(tp, 0x19, 0x492b); + rtl8168_mdio_write(tp, 0x15, 0x03ec); + rtl8168_mdio_write(tp, 0x19, 0x6b80); + rtl8168_mdio_write(tp, 0x15, 0x03ed); + rtl8168_mdio_write(tp, 0x19, 0x7c01); + rtl8168_mdio_write(tp, 0x15, 0x03ee); + rtl8168_mdio_write(tp, 0x19, 0x4c00); + rtl8168_mdio_write(tp, 0x15, 0x03ef); + rtl8168_mdio_write(tp, 0x19, 0xd64f); + rtl8168_mdio_write(tp, 0x15, 0x03f0); + rtl8168_mdio_write(tp, 0x19, 0x000d); + rtl8168_mdio_write(tp, 0x15, 0x03f1); + rtl8168_mdio_write(tp, 0x19, 0x3311); + rtl8168_mdio_write(tp, 0x15, 0x03f2); + rtl8168_mdio_write(tp, 0x19, 0xbd0c); + rtl8168_mdio_write(tp, 0x15, 0x03f3); + rtl8168_mdio_write(tp, 0x19, 0xc428); + rtl8168_mdio_write(tp, 0x15, 0x03f4); + rtl8168_mdio_write(tp, 0x19, 0x0008); + rtl8168_mdio_write(tp, 0x15, 0x03f5); + rtl8168_mdio_write(tp, 0x19, 0x9afa); + rtl8168_mdio_write(tp, 0x15, 0x03f6); + rtl8168_mdio_write(tp, 0x19, 0x7c12); + rtl8168_mdio_write(tp, 0x15, 0x03f7); + rtl8168_mdio_write(tp, 0x19, 0x4c52); + rtl8168_mdio_write(tp, 0x15, 0x03f8); + rtl8168_mdio_write(tp, 0x19, 0x4470); + rtl8168_mdio_write(tp, 0x15, 0x03f9); + rtl8168_mdio_write(tp, 0x19, 0x7c12); + rtl8168_mdio_write(tp, 0x15, 0x03fa); + rtl8168_mdio_write(tp, 0x19, 0x4c40); + rtl8168_mdio_write(tp, 0x15, 0x03fb); + rtl8168_mdio_write(tp, 0x19, 0x33ef); + rtl8168_mdio_write(tp, 0x15, 0x03fc); + rtl8168_mdio_write(tp, 0x19, 0x3342); + rtl8168_mdio_write(tp, 0x15, 0x03fd); + rtl8168_mdio_write(tp, 0x19, 0x330d); + rtl8168_mdio_write(tp, 0x15, 0x03fe); + rtl8168_mdio_write(tp, 0x19, 0x32ae); + rtl8168_mdio_write(tp, 0x15, 0x0000); + rtl8168_mdio_write(tp, 0x16, 0x0306); + rtl8168_mdio_write(tp, 0x16, 0x0300); + rtl8168_mdio_write(tp, 0x1f, 0x0002); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x0080); + rtl8168_mdio_write(tp, 0x05, 0x8000); + rtl8168_mdio_write(tp, 0x06, 0x0280); + rtl8168_mdio_write(tp, 0x06, 0x48f7); + rtl8168_mdio_write(tp, 0x06, 0x00e0); + rtl8168_mdio_write(tp, 0x06, 0xfff7); + rtl8168_mdio_write(tp, 0x06, 0xa080); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0xf602); + rtl8168_mdio_write(tp, 0x06, 0x0112); + rtl8168_mdio_write(tp, 0x06, 0x0201); + rtl8168_mdio_write(tp, 0x06, 0x1f02); + rtl8168_mdio_write(tp, 0x06, 0x012c); + rtl8168_mdio_write(tp, 0x06, 0x0201); + rtl8168_mdio_write(tp, 0x06, 0x3c02); + rtl8168_mdio_write(tp, 0x06, 0x0156); + rtl8168_mdio_write(tp, 0x06, 0x0201); + rtl8168_mdio_write(tp, 0x06, 0x6d02); + rtl8168_mdio_write(tp, 0x06, 0x809d); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x88e1); + rtl8168_mdio_write(tp, 0x06, 0x8b89); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8a1e); + rtl8168_mdio_write(tp, 0x06, 0x01e1); + rtl8168_mdio_write(tp, 0x06, 0x8b8b); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8c1e); + rtl8168_mdio_write(tp, 0x06, 0x01e1); + rtl8168_mdio_write(tp, 0x06, 0x8b8d); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8e1e); + rtl8168_mdio_write(tp, 0x06, 0x01a0); + rtl8168_mdio_write(tp, 0x06, 0x00c7); + rtl8168_mdio_write(tp, 0x06, 0xaebb); + rtl8168_mdio_write(tp, 0x06, 0xd100); + rtl8168_mdio_write(tp, 0x06, 0xbf82); + rtl8168_mdio_write(tp, 0x06, 0xc702); + rtl8168_mdio_write(tp, 0x06, 0x320a); + rtl8168_mdio_write(tp, 0x06, 0xd105); + rtl8168_mdio_write(tp, 0x06, 0xbf82); + rtl8168_mdio_write(tp, 0x06, 0xcd02); + rtl8168_mdio_write(tp, 0x06, 0x320a); + rtl8168_mdio_write(tp, 0x06, 0xd100); + rtl8168_mdio_write(tp, 0x06, 0xbf82); + rtl8168_mdio_write(tp, 0x06, 0xca02); + rtl8168_mdio_write(tp, 0x06, 0x320a); + rtl8168_mdio_write(tp, 0x06, 0xd105); + rtl8168_mdio_write(tp, 0x06, 0xbf82); + rtl8168_mdio_write(tp, 0x06, 0xd002); + rtl8168_mdio_write(tp, 0x06, 0x320a); + rtl8168_mdio_write(tp, 0x06, 0xd481); + rtl8168_mdio_write(tp, 0x06, 0xc9e4); + rtl8168_mdio_write(tp, 0x06, 0x8b90); + rtl8168_mdio_write(tp, 0x06, 0xe58b); + rtl8168_mdio_write(tp, 0x06, 0x91d4); + rtl8168_mdio_write(tp, 0x06, 0x81b8); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x92e5); + rtl8168_mdio_write(tp, 0x06, 0x8b93); + rtl8168_mdio_write(tp, 0x06, 0xbf8b); + rtl8168_mdio_write(tp, 0x06, 0x88ec); + rtl8168_mdio_write(tp, 0x06, 0x0019); + rtl8168_mdio_write(tp, 0x06, 0xa98b); + rtl8168_mdio_write(tp, 0x06, 0x90f9); + rtl8168_mdio_write(tp, 0x06, 0xeeff); + rtl8168_mdio_write(tp, 0x06, 0xf600); + rtl8168_mdio_write(tp, 0x06, 0xeeff); + rtl8168_mdio_write(tp, 0x06, 0xf7fc); + rtl8168_mdio_write(tp, 0x06, 0xd100); + rtl8168_mdio_write(tp, 0x06, 0xbf82); + rtl8168_mdio_write(tp, 0x06, 0xc102); + rtl8168_mdio_write(tp, 0x06, 0x320a); + rtl8168_mdio_write(tp, 0x06, 0xd101); + rtl8168_mdio_write(tp, 0x06, 0xbf82); + rtl8168_mdio_write(tp, 0x06, 0xc402); + rtl8168_mdio_write(tp, 0x06, 0x320a); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x201a); + rtl8168_mdio_write(tp, 0x06, 0xf620); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8e02); + rtl8168_mdio_write(tp, 0x06, 0x824b); + rtl8168_mdio_write(tp, 0x06, 0x0281); + rtl8168_mdio_write(tp, 0x06, 0x1902); + rtl8168_mdio_write(tp, 0x06, 0x2c9d); + rtl8168_mdio_write(tp, 0x06, 0x0203); + rtl8168_mdio_write(tp, 0x06, 0x9602); + rtl8168_mdio_write(tp, 0x06, 0x0473); + rtl8168_mdio_write(tp, 0x06, 0x022e); + rtl8168_mdio_write(tp, 0x06, 0x3902); + rtl8168_mdio_write(tp, 0x06, 0x044d); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x210b); + rtl8168_mdio_write(tp, 0x06, 0xf621); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8e02); + rtl8168_mdio_write(tp, 0x06, 0x0416); + rtl8168_mdio_write(tp, 0x06, 0x021b); + rtl8168_mdio_write(tp, 0x06, 0xa4e0); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xad22); + rtl8168_mdio_write(tp, 0x06, 0x05f6); + rtl8168_mdio_write(tp, 0x06, 0x22e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x2305); + rtl8168_mdio_write(tp, 0x06, 0xf623); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8ee0); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xad24); + rtl8168_mdio_write(tp, 0x06, 0x05f6); + rtl8168_mdio_write(tp, 0x06, 0x24e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x2505); + rtl8168_mdio_write(tp, 0x06, 0xf625); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8ee0); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xad26); + rtl8168_mdio_write(tp, 0x06, 0x08f6); + rtl8168_mdio_write(tp, 0x06, 0x26e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0x0281); + rtl8168_mdio_write(tp, 0x06, 0xdae0); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xad27); + rtl8168_mdio_write(tp, 0x06, 0x05f6); + rtl8168_mdio_write(tp, 0x06, 0x27e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0x0203); + rtl8168_mdio_write(tp, 0x06, 0x5cfc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xfaef); + rtl8168_mdio_write(tp, 0x06, 0x69e0); + rtl8168_mdio_write(tp, 0x06, 0x8b85); + rtl8168_mdio_write(tp, 0x06, 0xad21); + rtl8168_mdio_write(tp, 0x06, 0x57e0); + rtl8168_mdio_write(tp, 0x06, 0xe022); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x2358); + rtl8168_mdio_write(tp, 0x06, 0xc059); + rtl8168_mdio_write(tp, 0x06, 0x021e); + rtl8168_mdio_write(tp, 0x06, 0x01e1); + rtl8168_mdio_write(tp, 0x06, 0x8b3c); + rtl8168_mdio_write(tp, 0x06, 0x1f10); + rtl8168_mdio_write(tp, 0x06, 0x9e44); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x3cad); + rtl8168_mdio_write(tp, 0x06, 0x211d); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x84f7); + rtl8168_mdio_write(tp, 0x06, 0x29e5); + rtl8168_mdio_write(tp, 0x06, 0x8b84); + rtl8168_mdio_write(tp, 0x06, 0xac27); + rtl8168_mdio_write(tp, 0x06, 0x0dac); + rtl8168_mdio_write(tp, 0x06, 0x2605); + rtl8168_mdio_write(tp, 0x06, 0x0281); + rtl8168_mdio_write(tp, 0x06, 0x7fae); + rtl8168_mdio_write(tp, 0x06, 0x2b02); + rtl8168_mdio_write(tp, 0x06, 0x2c23); + rtl8168_mdio_write(tp, 0x06, 0xae26); + rtl8168_mdio_write(tp, 0x06, 0x022c); + rtl8168_mdio_write(tp, 0x06, 0x41ae); + rtl8168_mdio_write(tp, 0x06, 0x21e0); + rtl8168_mdio_write(tp, 0x06, 0x8b87); + rtl8168_mdio_write(tp, 0x06, 0xad22); + rtl8168_mdio_write(tp, 0x06, 0x18e0); + rtl8168_mdio_write(tp, 0x06, 0xfff7); + rtl8168_mdio_write(tp, 0x06, 0x58fc); + rtl8168_mdio_write(tp, 0x06, 0xe4ff); + rtl8168_mdio_write(tp, 0x06, 0xf7d1); + rtl8168_mdio_write(tp, 0x06, 0x00bf); + rtl8168_mdio_write(tp, 0x06, 0x2eee); + rtl8168_mdio_write(tp, 0x06, 0x0232); + rtl8168_mdio_write(tp, 0x06, 0x0ad1); + rtl8168_mdio_write(tp, 0x06, 0x00bf); + rtl8168_mdio_write(tp, 0x06, 0x82e8); + rtl8168_mdio_write(tp, 0x06, 0x0232); + rtl8168_mdio_write(tp, 0x06, 0x0a02); + rtl8168_mdio_write(tp, 0x06, 0x2bdf); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfefc); + rtl8168_mdio_write(tp, 0x06, 0x04d0); + rtl8168_mdio_write(tp, 0x06, 0x0202); + rtl8168_mdio_write(tp, 0x06, 0x1e97); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x87ad); + rtl8168_mdio_write(tp, 0x06, 0x2228); + rtl8168_mdio_write(tp, 0x06, 0xd100); + rtl8168_mdio_write(tp, 0x06, 0xbf82); + rtl8168_mdio_write(tp, 0x06, 0xd302); + rtl8168_mdio_write(tp, 0x06, 0x320a); + rtl8168_mdio_write(tp, 0x06, 0xd10c); + rtl8168_mdio_write(tp, 0x06, 0xbf82); + rtl8168_mdio_write(tp, 0x06, 0xd602); + rtl8168_mdio_write(tp, 0x06, 0x320a); + rtl8168_mdio_write(tp, 0x06, 0xd104); + rtl8168_mdio_write(tp, 0x06, 0xbf82); + rtl8168_mdio_write(tp, 0x06, 0xd902); + rtl8168_mdio_write(tp, 0x06, 0x320a); + rtl8168_mdio_write(tp, 0x06, 0xd101); + rtl8168_mdio_write(tp, 0x06, 0xbf82); + rtl8168_mdio_write(tp, 0x06, 0xe802); + rtl8168_mdio_write(tp, 0x06, 0x320a); + rtl8168_mdio_write(tp, 0x06, 0xe0ff); + rtl8168_mdio_write(tp, 0x06, 0xf768); + rtl8168_mdio_write(tp, 0x06, 0x03e4); + rtl8168_mdio_write(tp, 0x06, 0xfff7); + rtl8168_mdio_write(tp, 0x06, 0xd004); + rtl8168_mdio_write(tp, 0x06, 0x0228); + rtl8168_mdio_write(tp, 0x06, 0x7a04); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0xe234); + rtl8168_mdio_write(tp, 0x06, 0xe1e2); + rtl8168_mdio_write(tp, 0x06, 0x35f6); + rtl8168_mdio_write(tp, 0x06, 0x2be4); + rtl8168_mdio_write(tp, 0x06, 0xe234); + rtl8168_mdio_write(tp, 0x06, 0xe5e2); + rtl8168_mdio_write(tp, 0x06, 0x35fc); + rtl8168_mdio_write(tp, 0x06, 0x05f8); + rtl8168_mdio_write(tp, 0x06, 0xe0e2); + rtl8168_mdio_write(tp, 0x06, 0x34e1); + rtl8168_mdio_write(tp, 0x06, 0xe235); + rtl8168_mdio_write(tp, 0x06, 0xf72b); + rtl8168_mdio_write(tp, 0x06, 0xe4e2); + rtl8168_mdio_write(tp, 0x06, 0x34e5); + rtl8168_mdio_write(tp, 0x06, 0xe235); + rtl8168_mdio_write(tp, 0x06, 0xfc05); + rtl8168_mdio_write(tp, 0x06, 0xf8f9); + rtl8168_mdio_write(tp, 0x06, 0xfaef); + rtl8168_mdio_write(tp, 0x06, 0x69ac); + rtl8168_mdio_write(tp, 0x06, 0x1b4c); + rtl8168_mdio_write(tp, 0x06, 0xbf2e); + rtl8168_mdio_write(tp, 0x06, 0x3002); + rtl8168_mdio_write(tp, 0x06, 0x31dd); + rtl8168_mdio_write(tp, 0x06, 0xef01); + rtl8168_mdio_write(tp, 0x06, 0xe28a); + rtl8168_mdio_write(tp, 0x06, 0x76e4); + rtl8168_mdio_write(tp, 0x06, 0x8a76); + rtl8168_mdio_write(tp, 0x06, 0x1f12); + rtl8168_mdio_write(tp, 0x06, 0x9e3a); + rtl8168_mdio_write(tp, 0x06, 0xef12); + rtl8168_mdio_write(tp, 0x06, 0x5907); + rtl8168_mdio_write(tp, 0x06, 0x9f12); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b40); + rtl8168_mdio_write(tp, 0x06, 0xf721); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x40d0); + rtl8168_mdio_write(tp, 0x06, 0x0302); + rtl8168_mdio_write(tp, 0x06, 0x287a); + rtl8168_mdio_write(tp, 0x06, 0x0282); + rtl8168_mdio_write(tp, 0x06, 0x34fc); + rtl8168_mdio_write(tp, 0x06, 0xa000); + rtl8168_mdio_write(tp, 0x06, 0x1002); + rtl8168_mdio_write(tp, 0x06, 0x2dc3); + rtl8168_mdio_write(tp, 0x06, 0x022e); + rtl8168_mdio_write(tp, 0x06, 0x21e0); + rtl8168_mdio_write(tp, 0x06, 0x8b40); + rtl8168_mdio_write(tp, 0x06, 0xf621); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x40ae); + rtl8168_mdio_write(tp, 0x06, 0x0fbf); + rtl8168_mdio_write(tp, 0x06, 0x3fa5); + rtl8168_mdio_write(tp, 0x06, 0x0231); + rtl8168_mdio_write(tp, 0x06, 0x6cbf); + rtl8168_mdio_write(tp, 0x06, 0x3fa2); + rtl8168_mdio_write(tp, 0x06, 0x0231); + rtl8168_mdio_write(tp, 0x06, 0x6c02); + rtl8168_mdio_write(tp, 0x06, 0x2dc3); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfefd); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0xe2f4); + rtl8168_mdio_write(tp, 0x06, 0xe1e2); + rtl8168_mdio_write(tp, 0x06, 0xf5e4); + rtl8168_mdio_write(tp, 0x06, 0x8a78); + rtl8168_mdio_write(tp, 0x06, 0xe58a); + rtl8168_mdio_write(tp, 0x06, 0x79ee); + rtl8168_mdio_write(tp, 0x06, 0xe2f4); + rtl8168_mdio_write(tp, 0x06, 0xd8ee); + rtl8168_mdio_write(tp, 0x06, 0xe2f5); + rtl8168_mdio_write(tp, 0x06, 0x20fc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xf9fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x87ad); + rtl8168_mdio_write(tp, 0x06, 0x2065); + rtl8168_mdio_write(tp, 0x06, 0xd200); + rtl8168_mdio_write(tp, 0x06, 0xbf2e); + rtl8168_mdio_write(tp, 0x06, 0xe802); + rtl8168_mdio_write(tp, 0x06, 0x31dd); + rtl8168_mdio_write(tp, 0x06, 0x1e21); + rtl8168_mdio_write(tp, 0x06, 0xbf82); + rtl8168_mdio_write(tp, 0x06, 0xdf02); + rtl8168_mdio_write(tp, 0x06, 0x31dd); + rtl8168_mdio_write(tp, 0x06, 0x0c11); + rtl8168_mdio_write(tp, 0x06, 0x1e21); + rtl8168_mdio_write(tp, 0x06, 0xbf82); + rtl8168_mdio_write(tp, 0x06, 0xe202); + rtl8168_mdio_write(tp, 0x06, 0x31dd); + rtl8168_mdio_write(tp, 0x06, 0x0c12); + rtl8168_mdio_write(tp, 0x06, 0x1e21); + rtl8168_mdio_write(tp, 0x06, 0xbf82); + rtl8168_mdio_write(tp, 0x06, 0xe502); + rtl8168_mdio_write(tp, 0x06, 0x31dd); + rtl8168_mdio_write(tp, 0x06, 0x0c13); + rtl8168_mdio_write(tp, 0x06, 0x1e21); + rtl8168_mdio_write(tp, 0x06, 0xbf1f); + rtl8168_mdio_write(tp, 0x06, 0x5302); + rtl8168_mdio_write(tp, 0x06, 0x31dd); + rtl8168_mdio_write(tp, 0x06, 0x0c14); + rtl8168_mdio_write(tp, 0x06, 0x1e21); + rtl8168_mdio_write(tp, 0x06, 0xbf82); + rtl8168_mdio_write(tp, 0x06, 0xeb02); + rtl8168_mdio_write(tp, 0x06, 0x31dd); + rtl8168_mdio_write(tp, 0x06, 0x0c16); + rtl8168_mdio_write(tp, 0x06, 0x1e21); + rtl8168_mdio_write(tp, 0x06, 0xe083); + rtl8168_mdio_write(tp, 0x06, 0xe01f); + rtl8168_mdio_write(tp, 0x06, 0x029e); + rtl8168_mdio_write(tp, 0x06, 0x22e6); + rtl8168_mdio_write(tp, 0x06, 0x83e0); + rtl8168_mdio_write(tp, 0x06, 0xad31); + rtl8168_mdio_write(tp, 0x06, 0x14ad); + rtl8168_mdio_write(tp, 0x06, 0x3011); + rtl8168_mdio_write(tp, 0x06, 0xef02); + rtl8168_mdio_write(tp, 0x06, 0x580c); + rtl8168_mdio_write(tp, 0x06, 0x9e07); + rtl8168_mdio_write(tp, 0x06, 0xad36); + rtl8168_mdio_write(tp, 0x06, 0x085a); + rtl8168_mdio_write(tp, 0x06, 0x309f); + rtl8168_mdio_write(tp, 0x06, 0x04d1); + rtl8168_mdio_write(tp, 0x06, 0x01ae); + rtl8168_mdio_write(tp, 0x06, 0x02d1); + rtl8168_mdio_write(tp, 0x06, 0x00bf); + rtl8168_mdio_write(tp, 0x06, 0x82dc); + rtl8168_mdio_write(tp, 0x06, 0x0232); + rtl8168_mdio_write(tp, 0x06, 0x0aef); + rtl8168_mdio_write(tp, 0x06, 0x96fe); + rtl8168_mdio_write(tp, 0x06, 0xfdfc); + rtl8168_mdio_write(tp, 0x06, 0x0400); + rtl8168_mdio_write(tp, 0x06, 0xe140); + rtl8168_mdio_write(tp, 0x06, 0x77e1); + rtl8168_mdio_write(tp, 0x06, 0x4010); + rtl8168_mdio_write(tp, 0x06, 0xe150); + rtl8168_mdio_write(tp, 0x06, 0x32e1); + rtl8168_mdio_write(tp, 0x06, 0x5030); + rtl8168_mdio_write(tp, 0x06, 0xe144); + rtl8168_mdio_write(tp, 0x06, 0x74e1); + rtl8168_mdio_write(tp, 0x06, 0x44bb); + rtl8168_mdio_write(tp, 0x06, 0xe2d2); + rtl8168_mdio_write(tp, 0x06, 0x40e0); + rtl8168_mdio_write(tp, 0x06, 0x2cfc); + rtl8168_mdio_write(tp, 0x06, 0xe2cc); + rtl8168_mdio_write(tp, 0x06, 0xcce2); + rtl8168_mdio_write(tp, 0x06, 0x00cc); + rtl8168_mdio_write(tp, 0x06, 0xe000); + rtl8168_mdio_write(tp, 0x06, 0x99e0); + rtl8168_mdio_write(tp, 0x06, 0x3688); + rtl8168_mdio_write(tp, 0x06, 0xe036); + rtl8168_mdio_write(tp, 0x06, 0x99e1); + rtl8168_mdio_write(tp, 0x06, 0x40dd); + rtl8168_mdio_write(tp, 0x06, 0xe022); + rtl8168_mdio_write(tp, 0x05, 0xe142); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x05, 0xe140); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0005); + for (i = 0; i < 200; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x00); + if (gphy_val & BIT_7) + break; + } + rtl8168_mdio_write(tp, 0x1f, 0x0004); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0023); + gphy_val = rtl8168_mdio_read(tp, 0x17); + gphy_val &= ~(BIT_0); + gphy_val |= BIT_2; + rtl8168_mdio_write(tp, 0x17, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0002); + rtl8168_mdio_write(tp, 0x1f, 0x0000); +} + +static void +rtl8168_set_phy_mcu_8168evl_2(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + struct pci_dev *pdev = tp->pci_dev; + unsigned int gphy_val,i; + + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x00, 0x1800); + gphy_val = rtl8168_mdio_read(tp, 0x15); + gphy_val &= ~(BIT_12); + rtl8168_mdio_write(tp, 0x15, gphy_val); + rtl8168_mdio_write(tp, 0x00, 0x4800); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x002f); + for (i = 0; i < 1000; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x1c); + if ((gphy_val & 0x0080) == 0x0080) + break; + } + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x00, 0x1800); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0023); + for (i = 0; i < 200; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x17); + if (!(gphy_val & 0x0001)) + break; + } + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x0080); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0023); + rtl8168_mdio_write(tp, 0x16, 0x0306); + rtl8168_mdio_write(tp, 0x16, 0x0307); + rtl8168_mdio_write(tp, 0x15, 0x00AF); + rtl8168_mdio_write(tp, 0x19, 0x4060); + rtl8168_mdio_write(tp, 0x15, 0x00B0); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x00B1); + rtl8168_mdio_write(tp, 0x19, 0x7e00); + rtl8168_mdio_write(tp, 0x15, 0x00B2); + rtl8168_mdio_write(tp, 0x19, 0x72B0); + rtl8168_mdio_write(tp, 0x15, 0x00B3); + rtl8168_mdio_write(tp, 0x19, 0x7F00); + rtl8168_mdio_write(tp, 0x15, 0x00B4); + rtl8168_mdio_write(tp, 0x19, 0x73B0); + rtl8168_mdio_write(tp, 0x15, 0x0101); + rtl8168_mdio_write(tp, 0x19, 0x0005); + rtl8168_mdio_write(tp, 0x15, 0x0103); + rtl8168_mdio_write(tp, 0x19, 0x0003); + rtl8168_mdio_write(tp, 0x15, 0x0105); + rtl8168_mdio_write(tp, 0x19, 0x30FD); + rtl8168_mdio_write(tp, 0x15, 0x0106); + rtl8168_mdio_write(tp, 0x19, 0x9DF7); + rtl8168_mdio_write(tp, 0x15, 0x0107); + rtl8168_mdio_write(tp, 0x19, 0x30C6); + rtl8168_mdio_write(tp, 0x15, 0x0098); + rtl8168_mdio_write(tp, 0x19, 0x7c0b); + rtl8168_mdio_write(tp, 0x15, 0x0099); + rtl8168_mdio_write(tp, 0x19, 0x6c0b); + rtl8168_mdio_write(tp, 0x15, 0x00eb); + rtl8168_mdio_write(tp, 0x19, 0x6c0b); + rtl8168_mdio_write(tp, 0x15, 0x00f8); + rtl8168_mdio_write(tp, 0x19, 0x6f0b); + rtl8168_mdio_write(tp, 0x15, 0x00fe); + rtl8168_mdio_write(tp, 0x19, 0x6f0f); + rtl8168_mdio_write(tp, 0x15, 0x00db); + rtl8168_mdio_write(tp, 0x19, 0x6f09); + rtl8168_mdio_write(tp, 0x15, 0x00dc); + rtl8168_mdio_write(tp, 0x19, 0xaefd); + rtl8168_mdio_write(tp, 0x15, 0x00dd); + rtl8168_mdio_write(tp, 0x19, 0x6f0b); + rtl8168_mdio_write(tp, 0x15, 0x00de); + rtl8168_mdio_write(tp, 0x19, 0xc60b); + rtl8168_mdio_write(tp, 0x15, 0x00df); + rtl8168_mdio_write(tp, 0x19, 0x00fa); + rtl8168_mdio_write(tp, 0x15, 0x00e0); + rtl8168_mdio_write(tp, 0x19, 0x30e1); + rtl8168_mdio_write(tp, 0x15, 0x020c); + rtl8168_mdio_write(tp, 0x19, 0x3224); + rtl8168_mdio_write(tp, 0x15, 0x020e); + rtl8168_mdio_write(tp, 0x19, 0x9813); + rtl8168_mdio_write(tp, 0x15, 0x020f); + rtl8168_mdio_write(tp, 0x19, 0x7801); + rtl8168_mdio_write(tp, 0x15, 0x0210); + rtl8168_mdio_write(tp, 0x19, 0x930f); + rtl8168_mdio_write(tp, 0x15, 0x0211); + rtl8168_mdio_write(tp, 0x19, 0x9206); + rtl8168_mdio_write(tp, 0x15, 0x0212); + rtl8168_mdio_write(tp, 0x19, 0x4002); + rtl8168_mdio_write(tp, 0x15, 0x0213); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x0214); + rtl8168_mdio_write(tp, 0x19, 0x588f); + rtl8168_mdio_write(tp, 0x15, 0x0215); + rtl8168_mdio_write(tp, 0x19, 0x5520); + rtl8168_mdio_write(tp, 0x15, 0x0216); + rtl8168_mdio_write(tp, 0x19, 0x3224); + rtl8168_mdio_write(tp, 0x15, 0x0217); + rtl8168_mdio_write(tp, 0x19, 0x4002); + rtl8168_mdio_write(tp, 0x15, 0x0218); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x0219); + rtl8168_mdio_write(tp, 0x19, 0x588d); + rtl8168_mdio_write(tp, 0x15, 0x021a); + rtl8168_mdio_write(tp, 0x19, 0x5540); + rtl8168_mdio_write(tp, 0x15, 0x021b); + rtl8168_mdio_write(tp, 0x19, 0x9e03); + rtl8168_mdio_write(tp, 0x15, 0x021c); + rtl8168_mdio_write(tp, 0x19, 0x7c40); + rtl8168_mdio_write(tp, 0x15, 0x021d); + rtl8168_mdio_write(tp, 0x19, 0x6840); + rtl8168_mdio_write(tp, 0x15, 0x021e); + rtl8168_mdio_write(tp, 0x19, 0x3224); + rtl8168_mdio_write(tp, 0x15, 0x021f); + rtl8168_mdio_write(tp, 0x19, 0x4002); + rtl8168_mdio_write(tp, 0x15, 0x0220); + rtl8168_mdio_write(tp, 0x19, 0x3224); + rtl8168_mdio_write(tp, 0x15, 0x0221); + rtl8168_mdio_write(tp, 0x19, 0x9e03); + rtl8168_mdio_write(tp, 0x15, 0x0222); + rtl8168_mdio_write(tp, 0x19, 0x7c40); + rtl8168_mdio_write(tp, 0x15, 0x0223); + rtl8168_mdio_write(tp, 0x19, 0x6840); + rtl8168_mdio_write(tp, 0x15, 0x0224); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x0225); + rtl8168_mdio_write(tp, 0x19, 0x3231); + rtl8168_mdio_write(tp, 0x15, 0x0000); + rtl8168_mdio_write(tp, 0x16, 0x0306); + rtl8168_mdio_write(tp, 0x16, 0x0300); + rtl8168_mdio_write(tp, 0x1f, 0x0002); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x17, 0x2160); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0040); + rtl8168_mdio_write(tp, 0x18, 0x0004); + if (pdev->subsystem_vendor == 0x144d && + pdev->subsystem_device == 0xc0a6) { + rtl8168_mdio_write(tp, 0x18, 0x0724); + rtl8168_mdio_write(tp, 0x19, 0xfe00); + rtl8168_mdio_write(tp, 0x18, 0x0734); + rtl8168_mdio_write(tp, 0x19, 0xfd00); + rtl8168_mdio_write(tp, 0x18, 0x1824); + rtl8168_mdio_write(tp, 0x19, 0xfc00); + rtl8168_mdio_write(tp, 0x18, 0x1834); + rtl8168_mdio_write(tp, 0x19, 0xfd00); + } + rtl8168_mdio_write(tp, 0x18, 0x09d4); + rtl8168_mdio_write(tp, 0x19, 0x4000); + rtl8168_mdio_write(tp, 0x18, 0x09e4); + rtl8168_mdio_write(tp, 0x19, 0x0800); + rtl8168_mdio_write(tp, 0x18, 0x09f4); + rtl8168_mdio_write(tp, 0x19, 0xff00); + rtl8168_mdio_write(tp, 0x18, 0x0a04); + rtl8168_mdio_write(tp, 0x19, 0x4000); + rtl8168_mdio_write(tp, 0x18, 0x0a14); + rtl8168_mdio_write(tp, 0x19, 0x0c00); + rtl8168_mdio_write(tp, 0x18, 0x0a24); + rtl8168_mdio_write(tp, 0x19, 0xff00); + rtl8168_mdio_write(tp, 0x18, 0x0a74); + rtl8168_mdio_write(tp, 0x19, 0xf600); + rtl8168_mdio_write(tp, 0x18, 0x1a24); + rtl8168_mdio_write(tp, 0x19, 0x7d00); + rtl8168_mdio_write(tp, 0x18, 0x1a64); + rtl8168_mdio_write(tp, 0x19, 0x0500); + rtl8168_mdio_write(tp, 0x18, 0x1a74); + rtl8168_mdio_write(tp, 0x19, 0x9500); + rtl8168_mdio_write(tp, 0x18, 0x1a84); + rtl8168_mdio_write(tp, 0x19, 0x8000); + rtl8168_mdio_write(tp, 0x18, 0x1a94); + rtl8168_mdio_write(tp, 0x19, 0x7d00); + rtl8168_mdio_write(tp, 0x18, 0x1aa4); + rtl8168_mdio_write(tp, 0x19, 0x9600); + rtl8168_mdio_write(tp, 0x18, 0x1ac4); + rtl8168_mdio_write(tp, 0x19, 0x4000); + rtl8168_mdio_write(tp, 0x18, 0x1ad4); + rtl8168_mdio_write(tp, 0x19, 0x0800); + rtl8168_mdio_write(tp, 0x18, 0x1af4); + rtl8168_mdio_write(tp, 0x19, 0xc400); + rtl8168_mdio_write(tp, 0x18, 0x1b04); + rtl8168_mdio_write(tp, 0x19, 0x4000); + rtl8168_mdio_write(tp, 0x18, 0x1b14); + rtl8168_mdio_write(tp, 0x19, 0x0800); + rtl8168_mdio_write(tp, 0x18, 0x1b24); + rtl8168_mdio_write(tp, 0x19, 0xfd00); + rtl8168_mdio_write(tp, 0x18, 0x1b34); + rtl8168_mdio_write(tp, 0x19, 0x4000); + rtl8168_mdio_write(tp, 0x18, 0x1b44); + rtl8168_mdio_write(tp, 0x19, 0x0400); + rtl8168_mdio_write(tp, 0x18, 0x1b94); + rtl8168_mdio_write(tp, 0x19, 0xf100); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x17, 0x2100); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0040); + rtl8168_mdio_write(tp, 0x18, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x0080); + rtl8168_mdio_write(tp, 0x05, 0x8000); + rtl8168_mdio_write(tp, 0x06, 0x0280); + rtl8168_mdio_write(tp, 0x06, 0x48f7); + rtl8168_mdio_write(tp, 0x06, 0x00e0); + rtl8168_mdio_write(tp, 0x06, 0xfff7); + rtl8168_mdio_write(tp, 0x06, 0xa080); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0xf602); + rtl8168_mdio_write(tp, 0x06, 0x0115); + rtl8168_mdio_write(tp, 0x06, 0x0201); + rtl8168_mdio_write(tp, 0x06, 0x2202); + rtl8168_mdio_write(tp, 0x06, 0x80a0); + rtl8168_mdio_write(tp, 0x06, 0x0201); + rtl8168_mdio_write(tp, 0x06, 0x3f02); + rtl8168_mdio_write(tp, 0x06, 0x0159); + rtl8168_mdio_write(tp, 0x06, 0x0280); + rtl8168_mdio_write(tp, 0x06, 0xbd02); + rtl8168_mdio_write(tp, 0x06, 0x80da); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x88e1); + rtl8168_mdio_write(tp, 0x06, 0x8b89); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8a1e); + rtl8168_mdio_write(tp, 0x06, 0x01e1); + rtl8168_mdio_write(tp, 0x06, 0x8b8b); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8c1e); + rtl8168_mdio_write(tp, 0x06, 0x01e1); + rtl8168_mdio_write(tp, 0x06, 0x8b8d); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8e1e); + rtl8168_mdio_write(tp, 0x06, 0x01a0); + rtl8168_mdio_write(tp, 0x06, 0x00c7); + rtl8168_mdio_write(tp, 0x06, 0xaebb); + rtl8168_mdio_write(tp, 0x06, 0xd481); + rtl8168_mdio_write(tp, 0x06, 0xd2e4); + rtl8168_mdio_write(tp, 0x06, 0x8b92); + rtl8168_mdio_write(tp, 0x06, 0xe58b); + rtl8168_mdio_write(tp, 0x06, 0x93d1); + rtl8168_mdio_write(tp, 0x06, 0x03bf); + rtl8168_mdio_write(tp, 0x06, 0x859e); + rtl8168_mdio_write(tp, 0x06, 0x0237); + rtl8168_mdio_write(tp, 0x06, 0x23d1); + rtl8168_mdio_write(tp, 0x06, 0x02bf); + rtl8168_mdio_write(tp, 0x06, 0x85a1); + rtl8168_mdio_write(tp, 0x06, 0x0237); + rtl8168_mdio_write(tp, 0x06, 0x23ee); + rtl8168_mdio_write(tp, 0x06, 0x8608); + rtl8168_mdio_write(tp, 0x06, 0x03ee); + rtl8168_mdio_write(tp, 0x06, 0x860a); + rtl8168_mdio_write(tp, 0x06, 0x60ee); + rtl8168_mdio_write(tp, 0x06, 0x8610); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0x8611); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0x8abe); + rtl8168_mdio_write(tp, 0x06, 0x07ee); + rtl8168_mdio_write(tp, 0x06, 0x8abf); + rtl8168_mdio_write(tp, 0x06, 0x73ee); + rtl8168_mdio_write(tp, 0x06, 0x8a95); + rtl8168_mdio_write(tp, 0x06, 0x02bf); + rtl8168_mdio_write(tp, 0x06, 0x8b88); + rtl8168_mdio_write(tp, 0x06, 0xec00); + rtl8168_mdio_write(tp, 0x06, 0x19a9); + rtl8168_mdio_write(tp, 0x06, 0x8b90); + rtl8168_mdio_write(tp, 0x06, 0xf9ee); + rtl8168_mdio_write(tp, 0x06, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0xfff7); + rtl8168_mdio_write(tp, 0x06, 0xfed1); + rtl8168_mdio_write(tp, 0x06, 0x00bf); + rtl8168_mdio_write(tp, 0x06, 0x8595); + rtl8168_mdio_write(tp, 0x06, 0x0237); + rtl8168_mdio_write(tp, 0x06, 0x23d1); + rtl8168_mdio_write(tp, 0x06, 0x01bf); + rtl8168_mdio_write(tp, 0x06, 0x8598); + rtl8168_mdio_write(tp, 0x06, 0x0237); + rtl8168_mdio_write(tp, 0x06, 0x2304); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b8a); + rtl8168_mdio_write(tp, 0x06, 0xad20); + rtl8168_mdio_write(tp, 0x06, 0x14ee); + rtl8168_mdio_write(tp, 0x06, 0x8b8a); + rtl8168_mdio_write(tp, 0x06, 0x0002); + rtl8168_mdio_write(tp, 0x06, 0x1f9a); + rtl8168_mdio_write(tp, 0x06, 0xe0e4); + rtl8168_mdio_write(tp, 0x06, 0x26e1); + rtl8168_mdio_write(tp, 0x06, 0xe427); + rtl8168_mdio_write(tp, 0x06, 0xeee4); + rtl8168_mdio_write(tp, 0x06, 0x2623); + rtl8168_mdio_write(tp, 0x06, 0xe5e4); + rtl8168_mdio_write(tp, 0x06, 0x27fc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8dad); + rtl8168_mdio_write(tp, 0x06, 0x2014); + rtl8168_mdio_write(tp, 0x06, 0xee8b); + rtl8168_mdio_write(tp, 0x06, 0x8d00); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0x5a78); + rtl8168_mdio_write(tp, 0x06, 0x039e); + rtl8168_mdio_write(tp, 0x06, 0x0902); + rtl8168_mdio_write(tp, 0x06, 0x05db); + rtl8168_mdio_write(tp, 0x06, 0x0282); + rtl8168_mdio_write(tp, 0x06, 0x7b02); + rtl8168_mdio_write(tp, 0x06, 0x3231); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xad20); + rtl8168_mdio_write(tp, 0x06, 0x1df6); + rtl8168_mdio_write(tp, 0x06, 0x20e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0x0281); + rtl8168_mdio_write(tp, 0x06, 0x5c02); + rtl8168_mdio_write(tp, 0x06, 0x2bcb); + rtl8168_mdio_write(tp, 0x06, 0x022d); + rtl8168_mdio_write(tp, 0x06, 0x2902); + rtl8168_mdio_write(tp, 0x06, 0x03b4); + rtl8168_mdio_write(tp, 0x06, 0x0285); + rtl8168_mdio_write(tp, 0x06, 0x6402); + rtl8168_mdio_write(tp, 0x06, 0x2eca); + rtl8168_mdio_write(tp, 0x06, 0x0284); + rtl8168_mdio_write(tp, 0x06, 0xcd02); + rtl8168_mdio_write(tp, 0x06, 0x046f); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x210b); + rtl8168_mdio_write(tp, 0x06, 0xf621); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8e02); + rtl8168_mdio_write(tp, 0x06, 0x8520); + rtl8168_mdio_write(tp, 0x06, 0x021b); + rtl8168_mdio_write(tp, 0x06, 0xe8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xad22); + rtl8168_mdio_write(tp, 0x06, 0x05f6); + rtl8168_mdio_write(tp, 0x06, 0x22e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x2308); + rtl8168_mdio_write(tp, 0x06, 0xf623); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8e02); + rtl8168_mdio_write(tp, 0x06, 0x311c); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x2405); + rtl8168_mdio_write(tp, 0x06, 0xf624); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8ee0); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xad25); + rtl8168_mdio_write(tp, 0x06, 0x05f6); + rtl8168_mdio_write(tp, 0x06, 0x25e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x2608); + rtl8168_mdio_write(tp, 0x06, 0xf626); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8e02); + rtl8168_mdio_write(tp, 0x06, 0x2df5); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x2705); + rtl8168_mdio_write(tp, 0x06, 0xf627); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8e02); + rtl8168_mdio_write(tp, 0x06, 0x037a); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8f9); + rtl8168_mdio_write(tp, 0x06, 0xfaef); + rtl8168_mdio_write(tp, 0x06, 0x69e0); + rtl8168_mdio_write(tp, 0x06, 0x8b87); + rtl8168_mdio_write(tp, 0x06, 0xad20); + rtl8168_mdio_write(tp, 0x06, 0x65d2); + rtl8168_mdio_write(tp, 0x06, 0x00bf); + rtl8168_mdio_write(tp, 0x06, 0x2fe9); + rtl8168_mdio_write(tp, 0x06, 0x0236); + rtl8168_mdio_write(tp, 0x06, 0xf61e); + rtl8168_mdio_write(tp, 0x06, 0x21bf); + rtl8168_mdio_write(tp, 0x06, 0x2ff5); + rtl8168_mdio_write(tp, 0x06, 0x0236); + rtl8168_mdio_write(tp, 0x06, 0xf60c); + rtl8168_mdio_write(tp, 0x06, 0x111e); + rtl8168_mdio_write(tp, 0x06, 0x21bf); + rtl8168_mdio_write(tp, 0x06, 0x2ff8); + rtl8168_mdio_write(tp, 0x06, 0x0236); + rtl8168_mdio_write(tp, 0x06, 0xf60c); + rtl8168_mdio_write(tp, 0x06, 0x121e); + rtl8168_mdio_write(tp, 0x06, 0x21bf); + rtl8168_mdio_write(tp, 0x06, 0x2ffb); + rtl8168_mdio_write(tp, 0x06, 0x0236); + rtl8168_mdio_write(tp, 0x06, 0xf60c); + rtl8168_mdio_write(tp, 0x06, 0x131e); + rtl8168_mdio_write(tp, 0x06, 0x21bf); + rtl8168_mdio_write(tp, 0x06, 0x1f97); + rtl8168_mdio_write(tp, 0x06, 0x0236); + rtl8168_mdio_write(tp, 0x06, 0xf60c); + rtl8168_mdio_write(tp, 0x06, 0x141e); + rtl8168_mdio_write(tp, 0x06, 0x21bf); + rtl8168_mdio_write(tp, 0x06, 0x859b); + rtl8168_mdio_write(tp, 0x06, 0x0236); + rtl8168_mdio_write(tp, 0x06, 0xf60c); + rtl8168_mdio_write(tp, 0x06, 0x161e); + rtl8168_mdio_write(tp, 0x06, 0x21e0); + rtl8168_mdio_write(tp, 0x06, 0x8a8c); + rtl8168_mdio_write(tp, 0x06, 0x1f02); + rtl8168_mdio_write(tp, 0x06, 0x9e22); + rtl8168_mdio_write(tp, 0x06, 0xe68a); + rtl8168_mdio_write(tp, 0x06, 0x8cad); + rtl8168_mdio_write(tp, 0x06, 0x3114); + rtl8168_mdio_write(tp, 0x06, 0xad30); + rtl8168_mdio_write(tp, 0x06, 0x11ef); + rtl8168_mdio_write(tp, 0x06, 0x0258); + rtl8168_mdio_write(tp, 0x06, 0x0c9e); + rtl8168_mdio_write(tp, 0x06, 0x07ad); + rtl8168_mdio_write(tp, 0x06, 0x3608); + rtl8168_mdio_write(tp, 0x06, 0x5a30); + rtl8168_mdio_write(tp, 0x06, 0x9f04); + rtl8168_mdio_write(tp, 0x06, 0xd101); + rtl8168_mdio_write(tp, 0x06, 0xae02); + rtl8168_mdio_write(tp, 0x06, 0xd100); + rtl8168_mdio_write(tp, 0x06, 0xbf2f); + rtl8168_mdio_write(tp, 0x06, 0xf202); + rtl8168_mdio_write(tp, 0x06, 0x3723); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfefd); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8f9); + rtl8168_mdio_write(tp, 0x06, 0xface); + rtl8168_mdio_write(tp, 0x06, 0xfaef); + rtl8168_mdio_write(tp, 0x06, 0x69fa); + rtl8168_mdio_write(tp, 0x06, 0xd401); + rtl8168_mdio_write(tp, 0x06, 0x55b4); + rtl8168_mdio_write(tp, 0x06, 0xfebf); + rtl8168_mdio_write(tp, 0x06, 0x85a7); + rtl8168_mdio_write(tp, 0x06, 0x0236); + rtl8168_mdio_write(tp, 0x06, 0xf6ac); + rtl8168_mdio_write(tp, 0x06, 0x280b); + rtl8168_mdio_write(tp, 0x06, 0xbf85); + rtl8168_mdio_write(tp, 0x06, 0xa402); + rtl8168_mdio_write(tp, 0x06, 0x36f6); + rtl8168_mdio_write(tp, 0x06, 0xac28); + rtl8168_mdio_write(tp, 0x06, 0x49ae); + rtl8168_mdio_write(tp, 0x06, 0x64bf); + rtl8168_mdio_write(tp, 0x06, 0x85a4); + rtl8168_mdio_write(tp, 0x06, 0x0236); + rtl8168_mdio_write(tp, 0x06, 0xf6ac); + rtl8168_mdio_write(tp, 0x06, 0x285b); + rtl8168_mdio_write(tp, 0x06, 0xd000); + rtl8168_mdio_write(tp, 0x06, 0x0282); + rtl8168_mdio_write(tp, 0x06, 0x60ac); + rtl8168_mdio_write(tp, 0x06, 0x2105); + rtl8168_mdio_write(tp, 0x06, 0xac22); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0x4ebf); + rtl8168_mdio_write(tp, 0x06, 0xe0c4); + rtl8168_mdio_write(tp, 0x06, 0xbe86); + rtl8168_mdio_write(tp, 0x06, 0x14d2); + rtl8168_mdio_write(tp, 0x06, 0x04d8); + rtl8168_mdio_write(tp, 0x06, 0x19d9); + rtl8168_mdio_write(tp, 0x06, 0x1907); + rtl8168_mdio_write(tp, 0x06, 0xdc19); + rtl8168_mdio_write(tp, 0x06, 0xdd19); + rtl8168_mdio_write(tp, 0x06, 0x0789); + rtl8168_mdio_write(tp, 0x06, 0x89ef); + rtl8168_mdio_write(tp, 0x06, 0x645e); + rtl8168_mdio_write(tp, 0x06, 0x07ff); + rtl8168_mdio_write(tp, 0x06, 0x0d65); + rtl8168_mdio_write(tp, 0x06, 0x5cf8); + rtl8168_mdio_write(tp, 0x06, 0x001e); + rtl8168_mdio_write(tp, 0x06, 0x46dc); + rtl8168_mdio_write(tp, 0x06, 0x19dd); + rtl8168_mdio_write(tp, 0x06, 0x19b2); + rtl8168_mdio_write(tp, 0x06, 0xe2d4); + rtl8168_mdio_write(tp, 0x06, 0x0001); + rtl8168_mdio_write(tp, 0x06, 0xbf85); + rtl8168_mdio_write(tp, 0x06, 0xa402); + rtl8168_mdio_write(tp, 0x06, 0x3723); + rtl8168_mdio_write(tp, 0x06, 0xae1d); + rtl8168_mdio_write(tp, 0x06, 0xbee0); + rtl8168_mdio_write(tp, 0x06, 0xc4bf); + rtl8168_mdio_write(tp, 0x06, 0x8614); + rtl8168_mdio_write(tp, 0x06, 0xd204); + rtl8168_mdio_write(tp, 0x06, 0xd819); + rtl8168_mdio_write(tp, 0x06, 0xd919); + rtl8168_mdio_write(tp, 0x06, 0x07dc); + rtl8168_mdio_write(tp, 0x06, 0x19dd); + rtl8168_mdio_write(tp, 0x06, 0x1907); + rtl8168_mdio_write(tp, 0x06, 0xb2f4); + rtl8168_mdio_write(tp, 0x06, 0xd400); + rtl8168_mdio_write(tp, 0x06, 0x00bf); + rtl8168_mdio_write(tp, 0x06, 0x85a4); + rtl8168_mdio_write(tp, 0x06, 0x0237); + rtl8168_mdio_write(tp, 0x06, 0x23fe); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfec6); + rtl8168_mdio_write(tp, 0x06, 0xfefd); + rtl8168_mdio_write(tp, 0x06, 0xfc05); + rtl8168_mdio_write(tp, 0x06, 0xf9e2); + rtl8168_mdio_write(tp, 0x06, 0xe0ea); + rtl8168_mdio_write(tp, 0x06, 0xe3e0); + rtl8168_mdio_write(tp, 0x06, 0xeb5a); + rtl8168_mdio_write(tp, 0x06, 0x070c); + rtl8168_mdio_write(tp, 0x06, 0x031e); + rtl8168_mdio_write(tp, 0x06, 0x20e6); + rtl8168_mdio_write(tp, 0x06, 0xe0ea); + rtl8168_mdio_write(tp, 0x06, 0xe7e0); + rtl8168_mdio_write(tp, 0x06, 0xebe0); + rtl8168_mdio_write(tp, 0x06, 0xe0fc); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0xfdfd); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xf9e0); + rtl8168_mdio_write(tp, 0x06, 0x8b81); + rtl8168_mdio_write(tp, 0x06, 0xac26); + rtl8168_mdio_write(tp, 0x06, 0x1ae0); + rtl8168_mdio_write(tp, 0x06, 0x8b81); + rtl8168_mdio_write(tp, 0x06, 0xac21); + rtl8168_mdio_write(tp, 0x06, 0x14e0); + rtl8168_mdio_write(tp, 0x06, 0x8b85); + rtl8168_mdio_write(tp, 0x06, 0xac20); + rtl8168_mdio_write(tp, 0x06, 0x0ee0); + rtl8168_mdio_write(tp, 0x06, 0x8b85); + rtl8168_mdio_write(tp, 0x06, 0xac23); + rtl8168_mdio_write(tp, 0x06, 0x08e0); + rtl8168_mdio_write(tp, 0x06, 0x8b87); + rtl8168_mdio_write(tp, 0x06, 0xac24); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0x3802); + rtl8168_mdio_write(tp, 0x06, 0x1ab5); + rtl8168_mdio_write(tp, 0x06, 0xeee4); + rtl8168_mdio_write(tp, 0x06, 0x1c04); + rtl8168_mdio_write(tp, 0x06, 0xeee4); + rtl8168_mdio_write(tp, 0x06, 0x1d04); + rtl8168_mdio_write(tp, 0x06, 0xe2e0); + rtl8168_mdio_write(tp, 0x06, 0x7ce3); + rtl8168_mdio_write(tp, 0x06, 0xe07d); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x38e1); + rtl8168_mdio_write(tp, 0x06, 0xe039); + rtl8168_mdio_write(tp, 0x06, 0xad2e); + rtl8168_mdio_write(tp, 0x06, 0x1bad); + rtl8168_mdio_write(tp, 0x06, 0x390d); + rtl8168_mdio_write(tp, 0x06, 0xd101); + rtl8168_mdio_write(tp, 0x06, 0xbf21); + rtl8168_mdio_write(tp, 0x06, 0xd502); + rtl8168_mdio_write(tp, 0x06, 0x3723); + rtl8168_mdio_write(tp, 0x06, 0x0282); + rtl8168_mdio_write(tp, 0x06, 0xd8ae); + rtl8168_mdio_write(tp, 0x06, 0x0bac); + rtl8168_mdio_write(tp, 0x06, 0x3802); + rtl8168_mdio_write(tp, 0x06, 0xae06); + rtl8168_mdio_write(tp, 0x06, 0x0283); + rtl8168_mdio_write(tp, 0x06, 0x1802); + rtl8168_mdio_write(tp, 0x06, 0x8360); + rtl8168_mdio_write(tp, 0x06, 0x021a); + rtl8168_mdio_write(tp, 0x06, 0xc6fd); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e1); + rtl8168_mdio_write(tp, 0x06, 0x8af4); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x81ad); + rtl8168_mdio_write(tp, 0x06, 0x2605); + rtl8168_mdio_write(tp, 0x06, 0x0222); + rtl8168_mdio_write(tp, 0x06, 0xa4f7); + rtl8168_mdio_write(tp, 0x06, 0x28e0); + rtl8168_mdio_write(tp, 0x06, 0x8b81); + rtl8168_mdio_write(tp, 0x06, 0xad21); + rtl8168_mdio_write(tp, 0x06, 0x0502); + rtl8168_mdio_write(tp, 0x06, 0x23a9); + rtl8168_mdio_write(tp, 0x06, 0xf729); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x85ad); + rtl8168_mdio_write(tp, 0x06, 0x2005); + rtl8168_mdio_write(tp, 0x06, 0x0214); + rtl8168_mdio_write(tp, 0x06, 0xabf7); + rtl8168_mdio_write(tp, 0x06, 0x2ae0); + rtl8168_mdio_write(tp, 0x06, 0x8b85); + rtl8168_mdio_write(tp, 0x06, 0xad23); + rtl8168_mdio_write(tp, 0x06, 0x0502); + rtl8168_mdio_write(tp, 0x06, 0x12e7); + rtl8168_mdio_write(tp, 0x06, 0xf72b); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x87ad); + rtl8168_mdio_write(tp, 0x06, 0x2405); + rtl8168_mdio_write(tp, 0x06, 0x0283); + rtl8168_mdio_write(tp, 0x06, 0xbcf7); + rtl8168_mdio_write(tp, 0x06, 0x2ce5); + rtl8168_mdio_write(tp, 0x06, 0x8af4); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b81); + rtl8168_mdio_write(tp, 0x06, 0xad26); + rtl8168_mdio_write(tp, 0x06, 0x0302); + rtl8168_mdio_write(tp, 0x06, 0x21e5); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x81ad); + rtl8168_mdio_write(tp, 0x06, 0x2109); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0xf4ac); + rtl8168_mdio_write(tp, 0x06, 0x2003); + rtl8168_mdio_write(tp, 0x06, 0x0223); + rtl8168_mdio_write(tp, 0x06, 0x98e0); + rtl8168_mdio_write(tp, 0x06, 0x8b85); + rtl8168_mdio_write(tp, 0x06, 0xad20); + rtl8168_mdio_write(tp, 0x06, 0x09e0); + rtl8168_mdio_write(tp, 0x06, 0x8af4); + rtl8168_mdio_write(tp, 0x06, 0xac21); + rtl8168_mdio_write(tp, 0x06, 0x0302); + rtl8168_mdio_write(tp, 0x06, 0x13fb); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x85ad); + rtl8168_mdio_write(tp, 0x06, 0x2309); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0xf4ac); + rtl8168_mdio_write(tp, 0x06, 0x2203); + rtl8168_mdio_write(tp, 0x06, 0x0212); + rtl8168_mdio_write(tp, 0x06, 0xfae0); + rtl8168_mdio_write(tp, 0x06, 0x8b87); + rtl8168_mdio_write(tp, 0x06, 0xad24); + rtl8168_mdio_write(tp, 0x06, 0x09e0); + rtl8168_mdio_write(tp, 0x06, 0x8af4); + rtl8168_mdio_write(tp, 0x06, 0xac23); + rtl8168_mdio_write(tp, 0x06, 0x0302); + rtl8168_mdio_write(tp, 0x06, 0x83c1); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e1); + rtl8168_mdio_write(tp, 0x06, 0x8af4); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x81ad); + rtl8168_mdio_write(tp, 0x06, 0x2608); + rtl8168_mdio_write(tp, 0x06, 0xe083); + rtl8168_mdio_write(tp, 0x06, 0xd2ad); + rtl8168_mdio_write(tp, 0x06, 0x2502); + rtl8168_mdio_write(tp, 0x06, 0xf628); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x81ad); + rtl8168_mdio_write(tp, 0x06, 0x210a); + rtl8168_mdio_write(tp, 0x06, 0xe084); + rtl8168_mdio_write(tp, 0x06, 0x0af6); + rtl8168_mdio_write(tp, 0x06, 0x27a0); + rtl8168_mdio_write(tp, 0x06, 0x0502); + rtl8168_mdio_write(tp, 0x06, 0xf629); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x85ad); + rtl8168_mdio_write(tp, 0x06, 0x2008); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0xe8ad); + rtl8168_mdio_write(tp, 0x06, 0x2102); + rtl8168_mdio_write(tp, 0x06, 0xf62a); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x85ad); + rtl8168_mdio_write(tp, 0x06, 0x2308); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x20a0); + rtl8168_mdio_write(tp, 0x06, 0x0302); + rtl8168_mdio_write(tp, 0x06, 0xf62b); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x87ad); + rtl8168_mdio_write(tp, 0x06, 0x2408); + rtl8168_mdio_write(tp, 0x06, 0xe086); + rtl8168_mdio_write(tp, 0x06, 0x02a0); + rtl8168_mdio_write(tp, 0x06, 0x0302); + rtl8168_mdio_write(tp, 0x06, 0xf62c); + rtl8168_mdio_write(tp, 0x06, 0xe58a); + rtl8168_mdio_write(tp, 0x06, 0xf4a1); + rtl8168_mdio_write(tp, 0x06, 0x0008); + rtl8168_mdio_write(tp, 0x06, 0xd100); + rtl8168_mdio_write(tp, 0x06, 0xbf21); + rtl8168_mdio_write(tp, 0x06, 0xd502); + rtl8168_mdio_write(tp, 0x06, 0x3723); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xee86); + rtl8168_mdio_write(tp, 0x06, 0x0200); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x87ad); + rtl8168_mdio_write(tp, 0x06, 0x241e); + rtl8168_mdio_write(tp, 0x06, 0xe086); + rtl8168_mdio_write(tp, 0x06, 0x02a0); + rtl8168_mdio_write(tp, 0x06, 0x0005); + rtl8168_mdio_write(tp, 0x06, 0x0283); + rtl8168_mdio_write(tp, 0x06, 0xe8ae); + rtl8168_mdio_write(tp, 0x06, 0xf5a0); + rtl8168_mdio_write(tp, 0x06, 0x0105); + rtl8168_mdio_write(tp, 0x06, 0x0283); + rtl8168_mdio_write(tp, 0x06, 0xf8ae); + rtl8168_mdio_write(tp, 0x06, 0x0ba0); + rtl8168_mdio_write(tp, 0x06, 0x0205); + rtl8168_mdio_write(tp, 0x06, 0x0284); + rtl8168_mdio_write(tp, 0x06, 0x14ae); + rtl8168_mdio_write(tp, 0x06, 0x03a0); + rtl8168_mdio_write(tp, 0x06, 0x0300); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0x0284); + rtl8168_mdio_write(tp, 0x06, 0x2bee); + rtl8168_mdio_write(tp, 0x06, 0x8602); + rtl8168_mdio_write(tp, 0x06, 0x01ef); + rtl8168_mdio_write(tp, 0x06, 0x96fe); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8ee); + rtl8168_mdio_write(tp, 0x06, 0x8609); + rtl8168_mdio_write(tp, 0x06, 0x0002); + rtl8168_mdio_write(tp, 0x06, 0x8461); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xae10); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8608); + rtl8168_mdio_write(tp, 0x06, 0xe186); + rtl8168_mdio_write(tp, 0x06, 0x091f); + rtl8168_mdio_write(tp, 0x06, 0x019e); + rtl8168_mdio_write(tp, 0x06, 0x0611); + rtl8168_mdio_write(tp, 0x06, 0xe586); + rtl8168_mdio_write(tp, 0x06, 0x09ae); + rtl8168_mdio_write(tp, 0x06, 0x04ee); + rtl8168_mdio_write(tp, 0x06, 0x8602); + rtl8168_mdio_write(tp, 0x06, 0x01fc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xf9fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xfbbf); + rtl8168_mdio_write(tp, 0x06, 0x8604); + rtl8168_mdio_write(tp, 0x06, 0xef79); + rtl8168_mdio_write(tp, 0x06, 0xd200); + rtl8168_mdio_write(tp, 0x06, 0xd400); + rtl8168_mdio_write(tp, 0x06, 0x221e); + rtl8168_mdio_write(tp, 0x06, 0x02bf); + rtl8168_mdio_write(tp, 0x06, 0x2fec); + rtl8168_mdio_write(tp, 0x06, 0x0237); + rtl8168_mdio_write(tp, 0x06, 0x23bf); + rtl8168_mdio_write(tp, 0x06, 0x13f2); + rtl8168_mdio_write(tp, 0x06, 0x0236); + rtl8168_mdio_write(tp, 0x06, 0xf60d); + rtl8168_mdio_write(tp, 0x06, 0x4559); + rtl8168_mdio_write(tp, 0x06, 0x1fef); + rtl8168_mdio_write(tp, 0x06, 0x97dd); + rtl8168_mdio_write(tp, 0x06, 0xd308); + rtl8168_mdio_write(tp, 0x06, 0x1a93); + rtl8168_mdio_write(tp, 0x06, 0xdd12); + rtl8168_mdio_write(tp, 0x06, 0x17a2); + rtl8168_mdio_write(tp, 0x06, 0x04de); + rtl8168_mdio_write(tp, 0x06, 0xffef); + rtl8168_mdio_write(tp, 0x06, 0x96fe); + rtl8168_mdio_write(tp, 0x06, 0xfdfc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xf9fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xfbee); + rtl8168_mdio_write(tp, 0x06, 0x8602); + rtl8168_mdio_write(tp, 0x06, 0x03d5); + rtl8168_mdio_write(tp, 0x06, 0x0080); + rtl8168_mdio_write(tp, 0x06, 0xbf86); + rtl8168_mdio_write(tp, 0x06, 0x04ef); + rtl8168_mdio_write(tp, 0x06, 0x79ef); + rtl8168_mdio_write(tp, 0x06, 0x45bf); + rtl8168_mdio_write(tp, 0x06, 0x2fec); + rtl8168_mdio_write(tp, 0x06, 0x0237); + rtl8168_mdio_write(tp, 0x06, 0x23bf); + rtl8168_mdio_write(tp, 0x06, 0x13f2); + rtl8168_mdio_write(tp, 0x06, 0x0236); + rtl8168_mdio_write(tp, 0x06, 0xf6ad); + rtl8168_mdio_write(tp, 0x06, 0x2702); + rtl8168_mdio_write(tp, 0x06, 0x78ff); + rtl8168_mdio_write(tp, 0x06, 0xe186); + rtl8168_mdio_write(tp, 0x06, 0x0a1b); + rtl8168_mdio_write(tp, 0x06, 0x01aa); + rtl8168_mdio_write(tp, 0x06, 0x2eef); + rtl8168_mdio_write(tp, 0x06, 0x97d9); + rtl8168_mdio_write(tp, 0x06, 0x7900); + rtl8168_mdio_write(tp, 0x06, 0x9e2b); + rtl8168_mdio_write(tp, 0x06, 0x81dd); + rtl8168_mdio_write(tp, 0x06, 0xbf85); + rtl8168_mdio_write(tp, 0x06, 0xad02); + rtl8168_mdio_write(tp, 0x06, 0x3723); + rtl8168_mdio_write(tp, 0x06, 0xd101); + rtl8168_mdio_write(tp, 0x06, 0xef02); + rtl8168_mdio_write(tp, 0x06, 0x100c); + rtl8168_mdio_write(tp, 0x06, 0x11b0); + rtl8168_mdio_write(tp, 0x06, 0xfc0d); + rtl8168_mdio_write(tp, 0x06, 0x11bf); + rtl8168_mdio_write(tp, 0x06, 0x85aa); + rtl8168_mdio_write(tp, 0x06, 0x0237); + rtl8168_mdio_write(tp, 0x06, 0x23d1); + rtl8168_mdio_write(tp, 0x06, 0x00bf); + rtl8168_mdio_write(tp, 0x06, 0x85aa); + rtl8168_mdio_write(tp, 0x06, 0x0237); + rtl8168_mdio_write(tp, 0x06, 0x23ee); + rtl8168_mdio_write(tp, 0x06, 0x8602); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0x0413); + rtl8168_mdio_write(tp, 0x06, 0xa38b); + rtl8168_mdio_write(tp, 0x06, 0xb4d3); + rtl8168_mdio_write(tp, 0x06, 0x8012); + rtl8168_mdio_write(tp, 0x06, 0x17a2); + rtl8168_mdio_write(tp, 0x06, 0x04ad); + rtl8168_mdio_write(tp, 0x06, 0xffef); + rtl8168_mdio_write(tp, 0x06, 0x96fe); + rtl8168_mdio_write(tp, 0x06, 0xfdfc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xf9e0); + rtl8168_mdio_write(tp, 0x06, 0x8b85); + rtl8168_mdio_write(tp, 0x06, 0xad25); + rtl8168_mdio_write(tp, 0x06, 0x48e0); + rtl8168_mdio_write(tp, 0x06, 0x8a96); + rtl8168_mdio_write(tp, 0x06, 0xe18a); + rtl8168_mdio_write(tp, 0x06, 0x977c); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x9e35); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0x9600); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0x9700); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0xbee1); + rtl8168_mdio_write(tp, 0x06, 0x8abf); + rtl8168_mdio_write(tp, 0x06, 0xe286); + rtl8168_mdio_write(tp, 0x06, 0x10e3); + rtl8168_mdio_write(tp, 0x06, 0x8611); + rtl8168_mdio_write(tp, 0x06, 0x0236); + rtl8168_mdio_write(tp, 0x06, 0x1aad); + rtl8168_mdio_write(tp, 0x06, 0x2012); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0x9603); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0x97b7); + rtl8168_mdio_write(tp, 0x06, 0xee86); + rtl8168_mdio_write(tp, 0x06, 0x1000); + rtl8168_mdio_write(tp, 0x06, 0xee86); + rtl8168_mdio_write(tp, 0x06, 0x1100); + rtl8168_mdio_write(tp, 0x06, 0xae11); + rtl8168_mdio_write(tp, 0x06, 0x15e6); + rtl8168_mdio_write(tp, 0x06, 0x8610); + rtl8168_mdio_write(tp, 0x06, 0xe786); + rtl8168_mdio_write(tp, 0x06, 0x11ae); + rtl8168_mdio_write(tp, 0x06, 0x08ee); + rtl8168_mdio_write(tp, 0x06, 0x8610); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0x8611); + rtl8168_mdio_write(tp, 0x06, 0x00fd); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x00e1); + rtl8168_mdio_write(tp, 0x06, 0xe001); + rtl8168_mdio_write(tp, 0x06, 0xad27); + rtl8168_mdio_write(tp, 0x06, 0x32e0); + rtl8168_mdio_write(tp, 0x06, 0x8b40); + rtl8168_mdio_write(tp, 0x06, 0xf720); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x40bf); + rtl8168_mdio_write(tp, 0x06, 0x31f5); + rtl8168_mdio_write(tp, 0x06, 0x0236); + rtl8168_mdio_write(tp, 0x06, 0xf6ad); + rtl8168_mdio_write(tp, 0x06, 0x2821); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x20e1); + rtl8168_mdio_write(tp, 0x06, 0xe021); + rtl8168_mdio_write(tp, 0x06, 0xad20); + rtl8168_mdio_write(tp, 0x06, 0x18e0); + rtl8168_mdio_write(tp, 0x06, 0x8b40); + rtl8168_mdio_write(tp, 0x06, 0xf620); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x40ee); + rtl8168_mdio_write(tp, 0x06, 0x8b3b); + rtl8168_mdio_write(tp, 0x06, 0xffe0); + rtl8168_mdio_write(tp, 0x06, 0x8a8a); + rtl8168_mdio_write(tp, 0x06, 0xe18a); + rtl8168_mdio_write(tp, 0x06, 0x8be4); + rtl8168_mdio_write(tp, 0x06, 0xe000); + rtl8168_mdio_write(tp, 0x06, 0xe5e0); + rtl8168_mdio_write(tp, 0x06, 0x01ef); + rtl8168_mdio_write(tp, 0x06, 0x96fe); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x80ad); + rtl8168_mdio_write(tp, 0x06, 0x2722); + rtl8168_mdio_write(tp, 0x06, 0xbf44); + rtl8168_mdio_write(tp, 0x06, 0xfc02); + rtl8168_mdio_write(tp, 0x06, 0x36f6); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x441f); + rtl8168_mdio_write(tp, 0x06, 0x019e); + rtl8168_mdio_write(tp, 0x06, 0x15e5); + rtl8168_mdio_write(tp, 0x06, 0x8b44); + rtl8168_mdio_write(tp, 0x06, 0xad29); + rtl8168_mdio_write(tp, 0x06, 0x07ac); + rtl8168_mdio_write(tp, 0x06, 0x2804); + rtl8168_mdio_write(tp, 0x06, 0xd101); + rtl8168_mdio_write(tp, 0x06, 0xae02); + rtl8168_mdio_write(tp, 0x06, 0xd100); + rtl8168_mdio_write(tp, 0x06, 0xbf85); + rtl8168_mdio_write(tp, 0x06, 0xb002); + rtl8168_mdio_write(tp, 0x06, 0x3723); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfefc); + rtl8168_mdio_write(tp, 0x06, 0x0400); + rtl8168_mdio_write(tp, 0x06, 0xe140); + rtl8168_mdio_write(tp, 0x06, 0x77e1); + rtl8168_mdio_write(tp, 0x06, 0x40dd); + rtl8168_mdio_write(tp, 0x06, 0xe022); + rtl8168_mdio_write(tp, 0x06, 0x32e1); + rtl8168_mdio_write(tp, 0x06, 0x5074); + rtl8168_mdio_write(tp, 0x06, 0xe144); + rtl8168_mdio_write(tp, 0x06, 0xffe0); + rtl8168_mdio_write(tp, 0x06, 0xdaff); + rtl8168_mdio_write(tp, 0x06, 0xe0c0); + rtl8168_mdio_write(tp, 0x06, 0x52e0); + rtl8168_mdio_write(tp, 0x06, 0xeed9); + rtl8168_mdio_write(tp, 0x06, 0xe04c); + rtl8168_mdio_write(tp, 0x06, 0xbbe0); + rtl8168_mdio_write(tp, 0x06, 0x2a00); + rtl8168_mdio_write(tp, 0x05, 0xe142); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x05, 0xe140); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0005); + for (i = 0; i < 200; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x00); + if (gphy_val & BIT_7) + break; + } + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0042); + rtl8168_mdio_write(tp, 0x18, 0x2300); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0023); + if (tp->RequiredSecLanDonglePatch) { + gphy_val = rtl8168_mdio_read(tp, 0x17); + gphy_val &= ~BIT_2; + rtl8168_mdio_write(tp, 0x17, gphy_val); + } + + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x00, 0x9200); +} + +static void +rtl8168_set_phy_mcu_8168f_1(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned int gphy_val,i; + + rtl8168_mdio_write(tp,0x1f, 0x0000); + rtl8168_mdio_write(tp,0x00, 0x1800); + gphy_val = rtl8168_mdio_read(tp, 0x15); + gphy_val &= ~(BIT_12); + rtl8168_mdio_write(tp,0x15, gphy_val); + rtl8168_mdio_write(tp,0x00, 0x4800); + rtl8168_mdio_write(tp,0x1f, 0x0007); + rtl8168_mdio_write(tp,0x1e, 0x002f); + for (i = 0; i < 1000; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x1c); + if (gphy_val & 0x0080) + break; + } + rtl8168_mdio_write(tp,0x1f, 0x0000); + rtl8168_mdio_write(tp,0x00, 0x1800); + rtl8168_mdio_write(tp,0x1f, 0x0007); + rtl8168_mdio_write(tp,0x1e, 0x0023); + for (i = 0; i < 200; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x18); + if (!(gphy_val & 0x0001)) + break; + } + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x0080); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0023); + rtl8168_mdio_write(tp, 0x16, 0x0306); + rtl8168_mdio_write(tp, 0x16, 0x0307); + rtl8168_mdio_write(tp, 0x15, 0x0194); + rtl8168_mdio_write(tp, 0x19, 0x407D); + rtl8168_mdio_write(tp, 0x15, 0x0098); + rtl8168_mdio_write(tp, 0x19, 0x7c0b); + rtl8168_mdio_write(tp, 0x15, 0x0099); + rtl8168_mdio_write(tp, 0x19, 0x6c0b); + rtl8168_mdio_write(tp, 0x15, 0x00eb); + rtl8168_mdio_write(tp, 0x19, 0x6c0b); + rtl8168_mdio_write(tp, 0x15, 0x00f8); + rtl8168_mdio_write(tp, 0x19, 0x6f0b); + rtl8168_mdio_write(tp, 0x15, 0x00fe); + rtl8168_mdio_write(tp, 0x19, 0x6f0f); + rtl8168_mdio_write(tp, 0x15, 0x00db); + rtl8168_mdio_write(tp, 0x19, 0x6f09); + rtl8168_mdio_write(tp, 0x15, 0x00dc); + rtl8168_mdio_write(tp, 0x19, 0xaefd); + rtl8168_mdio_write(tp, 0x15, 0x00dd); + rtl8168_mdio_write(tp, 0x19, 0x6f0b); + rtl8168_mdio_write(tp, 0x15, 0x00de); + rtl8168_mdio_write(tp, 0x19, 0xc60b); + rtl8168_mdio_write(tp, 0x15, 0x00df); + rtl8168_mdio_write(tp, 0x19, 0x00fa); + rtl8168_mdio_write(tp, 0x15, 0x00e0); + rtl8168_mdio_write(tp, 0x19, 0x30e1); + rtl8168_mdio_write(tp, 0x15, 0x020c); + rtl8168_mdio_write(tp, 0x19, 0x3224); + rtl8168_mdio_write(tp, 0x15, 0x020e); + rtl8168_mdio_write(tp, 0x19, 0x9813); + rtl8168_mdio_write(tp, 0x15, 0x020f); + rtl8168_mdio_write(tp, 0x19, 0x7801); + rtl8168_mdio_write(tp, 0x15, 0x0210); + rtl8168_mdio_write(tp, 0x19, 0x930f); + rtl8168_mdio_write(tp, 0x15, 0x0211); + rtl8168_mdio_write(tp, 0x19, 0x9206); + rtl8168_mdio_write(tp, 0x15, 0x0212); + rtl8168_mdio_write(tp, 0x19, 0x4002); + rtl8168_mdio_write(tp, 0x15, 0x0213); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x0214); + rtl8168_mdio_write(tp, 0x19, 0x588f); + rtl8168_mdio_write(tp, 0x15, 0x0215); + rtl8168_mdio_write(tp, 0x19, 0x5520); + rtl8168_mdio_write(tp, 0x15, 0x0216); + rtl8168_mdio_write(tp, 0x19, 0x3224); + rtl8168_mdio_write(tp, 0x15, 0x0217); + rtl8168_mdio_write(tp, 0x19, 0x4002); + rtl8168_mdio_write(tp, 0x15, 0x0218); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x0219); + rtl8168_mdio_write(tp, 0x19, 0x588d); + rtl8168_mdio_write(tp, 0x15, 0x021a); + rtl8168_mdio_write(tp, 0x19, 0x5540); + rtl8168_mdio_write(tp, 0x15, 0x021b); + rtl8168_mdio_write(tp, 0x19, 0x9e03); + rtl8168_mdio_write(tp, 0x15, 0x021c); + rtl8168_mdio_write(tp, 0x19, 0x7c40); + rtl8168_mdio_write(tp, 0x15, 0x021d); + rtl8168_mdio_write(tp, 0x19, 0x6840); + rtl8168_mdio_write(tp, 0x15, 0x021e); + rtl8168_mdio_write(tp, 0x19, 0x3224); + rtl8168_mdio_write(tp, 0x15, 0x021f); + rtl8168_mdio_write(tp, 0x19, 0x4002); + rtl8168_mdio_write(tp, 0x15, 0x0220); + rtl8168_mdio_write(tp, 0x19, 0x3224); + rtl8168_mdio_write(tp, 0x15, 0x0221); + rtl8168_mdio_write(tp, 0x19, 0x9e03); + rtl8168_mdio_write(tp, 0x15, 0x0222); + rtl8168_mdio_write(tp, 0x19, 0x7c40); + rtl8168_mdio_write(tp, 0x15, 0x0223); + rtl8168_mdio_write(tp, 0x19, 0x6840); + rtl8168_mdio_write(tp, 0x15, 0x0224); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x0225); + rtl8168_mdio_write(tp, 0x19, 0x3231); + rtl8168_mdio_write(tp, 0x15, 0x0000); + rtl8168_mdio_write(tp, 0x16, 0x0306); + rtl8168_mdio_write(tp, 0x16, 0x0300); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x0080); + rtl8168_mdio_write(tp, 0x05, 0x8000); + rtl8168_mdio_write(tp, 0x06, 0x0280); + rtl8168_mdio_write(tp, 0x06, 0x48f7); + rtl8168_mdio_write(tp, 0x06, 0x00e0); + rtl8168_mdio_write(tp, 0x06, 0xfff7); + rtl8168_mdio_write(tp, 0x06, 0xa080); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0xf602); + rtl8168_mdio_write(tp, 0x06, 0x0118); + rtl8168_mdio_write(tp, 0x06, 0x0201); + rtl8168_mdio_write(tp, 0x06, 0x2502); + rtl8168_mdio_write(tp, 0x06, 0x8090); + rtl8168_mdio_write(tp, 0x06, 0x0201); + rtl8168_mdio_write(tp, 0x06, 0x4202); + rtl8168_mdio_write(tp, 0x06, 0x015c); + rtl8168_mdio_write(tp, 0x06, 0x0280); + rtl8168_mdio_write(tp, 0x06, 0xad02); + rtl8168_mdio_write(tp, 0x06, 0x80ca); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x88e1); + rtl8168_mdio_write(tp, 0x06, 0x8b89); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8a1e); + rtl8168_mdio_write(tp, 0x06, 0x01e1); + rtl8168_mdio_write(tp, 0x06, 0x8b8b); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8c1e); + rtl8168_mdio_write(tp, 0x06, 0x01e1); + rtl8168_mdio_write(tp, 0x06, 0x8b8d); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8e1e); + rtl8168_mdio_write(tp, 0x06, 0x01a0); + rtl8168_mdio_write(tp, 0x06, 0x00c7); + rtl8168_mdio_write(tp, 0x06, 0xaebb); + rtl8168_mdio_write(tp, 0x06, 0xd484); + rtl8168_mdio_write(tp, 0x06, 0x3ce4); + rtl8168_mdio_write(tp, 0x06, 0x8b92); + rtl8168_mdio_write(tp, 0x06, 0xe58b); + rtl8168_mdio_write(tp, 0x06, 0x93ee); + rtl8168_mdio_write(tp, 0x06, 0x8ac8); + rtl8168_mdio_write(tp, 0x06, 0x03ee); + rtl8168_mdio_write(tp, 0x06, 0x8aca); + rtl8168_mdio_write(tp, 0x06, 0x60ee); + rtl8168_mdio_write(tp, 0x06, 0x8ac0); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0x8ac1); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0x8abe); + rtl8168_mdio_write(tp, 0x06, 0x07ee); + rtl8168_mdio_write(tp, 0x06, 0x8abf); + rtl8168_mdio_write(tp, 0x06, 0x73ee); + rtl8168_mdio_write(tp, 0x06, 0x8a95); + rtl8168_mdio_write(tp, 0x06, 0x02bf); + rtl8168_mdio_write(tp, 0x06, 0x8b88); + rtl8168_mdio_write(tp, 0x06, 0xec00); + rtl8168_mdio_write(tp, 0x06, 0x19a9); + rtl8168_mdio_write(tp, 0x06, 0x8b90); + rtl8168_mdio_write(tp, 0x06, 0xf9ee); + rtl8168_mdio_write(tp, 0x06, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0xfff7); + rtl8168_mdio_write(tp, 0x06, 0xfed1); + rtl8168_mdio_write(tp, 0x06, 0x00bf); + rtl8168_mdio_write(tp, 0x06, 0x85a4); + rtl8168_mdio_write(tp, 0x06, 0x0238); + rtl8168_mdio_write(tp, 0x06, 0x7dd1); + rtl8168_mdio_write(tp, 0x06, 0x01bf); + rtl8168_mdio_write(tp, 0x06, 0x85a7); + rtl8168_mdio_write(tp, 0x06, 0x0238); + rtl8168_mdio_write(tp, 0x06, 0x7d04); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b8a); + rtl8168_mdio_write(tp, 0x06, 0xad20); + rtl8168_mdio_write(tp, 0x06, 0x14ee); + rtl8168_mdio_write(tp, 0x06, 0x8b8a); + rtl8168_mdio_write(tp, 0x06, 0x0002); + rtl8168_mdio_write(tp, 0x06, 0x204b); + rtl8168_mdio_write(tp, 0x06, 0xe0e4); + rtl8168_mdio_write(tp, 0x06, 0x26e1); + rtl8168_mdio_write(tp, 0x06, 0xe427); + rtl8168_mdio_write(tp, 0x06, 0xeee4); + rtl8168_mdio_write(tp, 0x06, 0x2623); + rtl8168_mdio_write(tp, 0x06, 0xe5e4); + rtl8168_mdio_write(tp, 0x06, 0x27fc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8dad); + rtl8168_mdio_write(tp, 0x06, 0x2014); + rtl8168_mdio_write(tp, 0x06, 0xee8b); + rtl8168_mdio_write(tp, 0x06, 0x8d00); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0x5a78); + rtl8168_mdio_write(tp, 0x06, 0x039e); + rtl8168_mdio_write(tp, 0x06, 0x0902); + rtl8168_mdio_write(tp, 0x06, 0x05e8); + rtl8168_mdio_write(tp, 0x06, 0x0281); + rtl8168_mdio_write(tp, 0x06, 0x4f02); + rtl8168_mdio_write(tp, 0x06, 0x326c); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xad20); + rtl8168_mdio_write(tp, 0x06, 0x1df6); + rtl8168_mdio_write(tp, 0x06, 0x20e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0x022f); + rtl8168_mdio_write(tp, 0x06, 0x0902); + rtl8168_mdio_write(tp, 0x06, 0x2ab0); + rtl8168_mdio_write(tp, 0x06, 0x0285); + rtl8168_mdio_write(tp, 0x06, 0x1602); + rtl8168_mdio_write(tp, 0x06, 0x03ba); + rtl8168_mdio_write(tp, 0x06, 0x0284); + rtl8168_mdio_write(tp, 0x06, 0xe502); + rtl8168_mdio_write(tp, 0x06, 0x2df1); + rtl8168_mdio_write(tp, 0x06, 0x0283); + rtl8168_mdio_write(tp, 0x06, 0x8302); + rtl8168_mdio_write(tp, 0x06, 0x0475); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x210b); + rtl8168_mdio_write(tp, 0x06, 0xf621); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8e02); + rtl8168_mdio_write(tp, 0x06, 0x83f8); + rtl8168_mdio_write(tp, 0x06, 0x021c); + rtl8168_mdio_write(tp, 0x06, 0x99e0); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xad22); + rtl8168_mdio_write(tp, 0x06, 0x08f6); + rtl8168_mdio_write(tp, 0x06, 0x22e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0x0235); + rtl8168_mdio_write(tp, 0x06, 0x63e0); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xad23); + rtl8168_mdio_write(tp, 0x06, 0x08f6); + rtl8168_mdio_write(tp, 0x06, 0x23e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0x0231); + rtl8168_mdio_write(tp, 0x06, 0x57e0); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xad24); + rtl8168_mdio_write(tp, 0x06, 0x05f6); + rtl8168_mdio_write(tp, 0x06, 0x24e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x2505); + rtl8168_mdio_write(tp, 0x06, 0xf625); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8ee0); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xad26); + rtl8168_mdio_write(tp, 0x06, 0x08f6); + rtl8168_mdio_write(tp, 0x06, 0x26e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0x022d); + rtl8168_mdio_write(tp, 0x06, 0x1ce0); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xad27); + rtl8168_mdio_write(tp, 0x06, 0x05f6); + rtl8168_mdio_write(tp, 0x06, 0x27e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0x0203); + rtl8168_mdio_write(tp, 0x06, 0x80fc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xf9e0); + rtl8168_mdio_write(tp, 0x06, 0x8b81); + rtl8168_mdio_write(tp, 0x06, 0xac26); + rtl8168_mdio_write(tp, 0x06, 0x1ae0); + rtl8168_mdio_write(tp, 0x06, 0x8b81); + rtl8168_mdio_write(tp, 0x06, 0xac21); + rtl8168_mdio_write(tp, 0x06, 0x14e0); + rtl8168_mdio_write(tp, 0x06, 0x8b85); + rtl8168_mdio_write(tp, 0x06, 0xac20); + rtl8168_mdio_write(tp, 0x06, 0x0ee0); + rtl8168_mdio_write(tp, 0x06, 0x8b85); + rtl8168_mdio_write(tp, 0x06, 0xac23); + rtl8168_mdio_write(tp, 0x06, 0x08e0); + rtl8168_mdio_write(tp, 0x06, 0x8b87); + rtl8168_mdio_write(tp, 0x06, 0xac24); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0x3802); + rtl8168_mdio_write(tp, 0x06, 0x1ac2); + rtl8168_mdio_write(tp, 0x06, 0xeee4); + rtl8168_mdio_write(tp, 0x06, 0x1c04); + rtl8168_mdio_write(tp, 0x06, 0xeee4); + rtl8168_mdio_write(tp, 0x06, 0x1d04); + rtl8168_mdio_write(tp, 0x06, 0xe2e0); + rtl8168_mdio_write(tp, 0x06, 0x7ce3); + rtl8168_mdio_write(tp, 0x06, 0xe07d); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x38e1); + rtl8168_mdio_write(tp, 0x06, 0xe039); + rtl8168_mdio_write(tp, 0x06, 0xad2e); + rtl8168_mdio_write(tp, 0x06, 0x1bad); + rtl8168_mdio_write(tp, 0x06, 0x390d); + rtl8168_mdio_write(tp, 0x06, 0xd101); + rtl8168_mdio_write(tp, 0x06, 0xbf22); + rtl8168_mdio_write(tp, 0x06, 0x7a02); + rtl8168_mdio_write(tp, 0x06, 0x387d); + rtl8168_mdio_write(tp, 0x06, 0x0281); + rtl8168_mdio_write(tp, 0x06, 0xacae); + rtl8168_mdio_write(tp, 0x06, 0x0bac); + rtl8168_mdio_write(tp, 0x06, 0x3802); + rtl8168_mdio_write(tp, 0x06, 0xae06); + rtl8168_mdio_write(tp, 0x06, 0x0281); + rtl8168_mdio_write(tp, 0x06, 0xe902); + rtl8168_mdio_write(tp, 0x06, 0x822e); + rtl8168_mdio_write(tp, 0x06, 0x021a); + rtl8168_mdio_write(tp, 0x06, 0xd3fd); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e1); + rtl8168_mdio_write(tp, 0x06, 0x8af4); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x81ad); + rtl8168_mdio_write(tp, 0x06, 0x2602); + rtl8168_mdio_write(tp, 0x06, 0xf728); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x81ad); + rtl8168_mdio_write(tp, 0x06, 0x2105); + rtl8168_mdio_write(tp, 0x06, 0x0222); + rtl8168_mdio_write(tp, 0x06, 0x8ef7); + rtl8168_mdio_write(tp, 0x06, 0x29e0); + rtl8168_mdio_write(tp, 0x06, 0x8b85); + rtl8168_mdio_write(tp, 0x06, 0xad20); + rtl8168_mdio_write(tp, 0x06, 0x0502); + rtl8168_mdio_write(tp, 0x06, 0x14b8); + rtl8168_mdio_write(tp, 0x06, 0xf72a); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x85ad); + rtl8168_mdio_write(tp, 0x06, 0x2305); + rtl8168_mdio_write(tp, 0x06, 0x0212); + rtl8168_mdio_write(tp, 0x06, 0xf4f7); + rtl8168_mdio_write(tp, 0x06, 0x2be0); + rtl8168_mdio_write(tp, 0x06, 0x8b87); + rtl8168_mdio_write(tp, 0x06, 0xad24); + rtl8168_mdio_write(tp, 0x06, 0x0502); + rtl8168_mdio_write(tp, 0x06, 0x8284); + rtl8168_mdio_write(tp, 0x06, 0xf72c); + rtl8168_mdio_write(tp, 0x06, 0xe58a); + rtl8168_mdio_write(tp, 0x06, 0xf4fc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x81ad); + rtl8168_mdio_write(tp, 0x06, 0x2600); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x81ad); + rtl8168_mdio_write(tp, 0x06, 0x2109); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0xf4ac); + rtl8168_mdio_write(tp, 0x06, 0x2003); + rtl8168_mdio_write(tp, 0x06, 0x0222); + rtl8168_mdio_write(tp, 0x06, 0x7de0); + rtl8168_mdio_write(tp, 0x06, 0x8b85); + rtl8168_mdio_write(tp, 0x06, 0xad20); + rtl8168_mdio_write(tp, 0x06, 0x09e0); + rtl8168_mdio_write(tp, 0x06, 0x8af4); + rtl8168_mdio_write(tp, 0x06, 0xac21); + rtl8168_mdio_write(tp, 0x06, 0x0302); + rtl8168_mdio_write(tp, 0x06, 0x1408); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x85ad); + rtl8168_mdio_write(tp, 0x06, 0x2309); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0xf4ac); + rtl8168_mdio_write(tp, 0x06, 0x2203); + rtl8168_mdio_write(tp, 0x06, 0x0213); + rtl8168_mdio_write(tp, 0x06, 0x07e0); + rtl8168_mdio_write(tp, 0x06, 0x8b87); + rtl8168_mdio_write(tp, 0x06, 0xad24); + rtl8168_mdio_write(tp, 0x06, 0x09e0); + rtl8168_mdio_write(tp, 0x06, 0x8af4); + rtl8168_mdio_write(tp, 0x06, 0xac23); + rtl8168_mdio_write(tp, 0x06, 0x0302); + rtl8168_mdio_write(tp, 0x06, 0x8289); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e1); + rtl8168_mdio_write(tp, 0x06, 0x8af4); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x81ad); + rtl8168_mdio_write(tp, 0x06, 0x2602); + rtl8168_mdio_write(tp, 0x06, 0xf628); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x81ad); + rtl8168_mdio_write(tp, 0x06, 0x210a); + rtl8168_mdio_write(tp, 0x06, 0xe083); + rtl8168_mdio_write(tp, 0x06, 0xecf6); + rtl8168_mdio_write(tp, 0x06, 0x27a0); + rtl8168_mdio_write(tp, 0x06, 0x0502); + rtl8168_mdio_write(tp, 0x06, 0xf629); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x85ad); + rtl8168_mdio_write(tp, 0x06, 0x2008); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0xe8ad); + rtl8168_mdio_write(tp, 0x06, 0x2102); + rtl8168_mdio_write(tp, 0x06, 0xf62a); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x85ad); + rtl8168_mdio_write(tp, 0x06, 0x2308); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x20a0); + rtl8168_mdio_write(tp, 0x06, 0x0302); + rtl8168_mdio_write(tp, 0x06, 0xf62b); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x87ad); + rtl8168_mdio_write(tp, 0x06, 0x2408); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0xc2a0); + rtl8168_mdio_write(tp, 0x06, 0x0302); + rtl8168_mdio_write(tp, 0x06, 0xf62c); + rtl8168_mdio_write(tp, 0x06, 0xe58a); + rtl8168_mdio_write(tp, 0x06, 0xf4a1); + rtl8168_mdio_write(tp, 0x06, 0x0008); + rtl8168_mdio_write(tp, 0x06, 0xd100); + rtl8168_mdio_write(tp, 0x06, 0xbf22); + rtl8168_mdio_write(tp, 0x06, 0x7a02); + rtl8168_mdio_write(tp, 0x06, 0x387d); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0xc200); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x87ad); + rtl8168_mdio_write(tp, 0x06, 0x241e); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0xc2a0); + rtl8168_mdio_write(tp, 0x06, 0x0005); + rtl8168_mdio_write(tp, 0x06, 0x0282); + rtl8168_mdio_write(tp, 0x06, 0xb0ae); + rtl8168_mdio_write(tp, 0x06, 0xf5a0); + rtl8168_mdio_write(tp, 0x06, 0x0105); + rtl8168_mdio_write(tp, 0x06, 0x0282); + rtl8168_mdio_write(tp, 0x06, 0xc0ae); + rtl8168_mdio_write(tp, 0x06, 0x0ba0); + rtl8168_mdio_write(tp, 0x06, 0x0205); + rtl8168_mdio_write(tp, 0x06, 0x0282); + rtl8168_mdio_write(tp, 0x06, 0xcaae); + rtl8168_mdio_write(tp, 0x06, 0x03a0); + rtl8168_mdio_write(tp, 0x06, 0x0300); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0x0282); + rtl8168_mdio_write(tp, 0x06, 0xe1ee); + rtl8168_mdio_write(tp, 0x06, 0x8ac2); + rtl8168_mdio_write(tp, 0x06, 0x01ef); + rtl8168_mdio_write(tp, 0x06, 0x96fe); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8ee); + rtl8168_mdio_write(tp, 0x06, 0x8ac9); + rtl8168_mdio_write(tp, 0x06, 0x0002); + rtl8168_mdio_write(tp, 0x06, 0x8317); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8ac8); + rtl8168_mdio_write(tp, 0x06, 0xe18a); + rtl8168_mdio_write(tp, 0x06, 0xc91f); + rtl8168_mdio_write(tp, 0x06, 0x019e); + rtl8168_mdio_write(tp, 0x06, 0x0611); + rtl8168_mdio_write(tp, 0x06, 0xe58a); + rtl8168_mdio_write(tp, 0x06, 0xc9ae); + rtl8168_mdio_write(tp, 0x06, 0x04ee); + rtl8168_mdio_write(tp, 0x06, 0x8ac2); + rtl8168_mdio_write(tp, 0x06, 0x01fc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xf9fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xfbbf); + rtl8168_mdio_write(tp, 0x06, 0x8ac4); + rtl8168_mdio_write(tp, 0x06, 0xef79); + rtl8168_mdio_write(tp, 0x06, 0xd200); + rtl8168_mdio_write(tp, 0x06, 0xd400); + rtl8168_mdio_write(tp, 0x06, 0x221e); + rtl8168_mdio_write(tp, 0x06, 0x02bf); + rtl8168_mdio_write(tp, 0x06, 0x3024); + rtl8168_mdio_write(tp, 0x06, 0x0238); + rtl8168_mdio_write(tp, 0x06, 0x7dbf); + rtl8168_mdio_write(tp, 0x06, 0x13ff); + rtl8168_mdio_write(tp, 0x06, 0x0238); + rtl8168_mdio_write(tp, 0x06, 0x500d); + rtl8168_mdio_write(tp, 0x06, 0x4559); + rtl8168_mdio_write(tp, 0x06, 0x1fef); + rtl8168_mdio_write(tp, 0x06, 0x97dd); + rtl8168_mdio_write(tp, 0x06, 0xd308); + rtl8168_mdio_write(tp, 0x06, 0x1a93); + rtl8168_mdio_write(tp, 0x06, 0xdd12); + rtl8168_mdio_write(tp, 0x06, 0x17a2); + rtl8168_mdio_write(tp, 0x06, 0x04de); + rtl8168_mdio_write(tp, 0x06, 0xffef); + rtl8168_mdio_write(tp, 0x06, 0x96fe); + rtl8168_mdio_write(tp, 0x06, 0xfdfc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xf9fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xfbee); + rtl8168_mdio_write(tp, 0x06, 0x8ac2); + rtl8168_mdio_write(tp, 0x06, 0x03d5); + rtl8168_mdio_write(tp, 0x06, 0x0080); + rtl8168_mdio_write(tp, 0x06, 0xbf8a); + rtl8168_mdio_write(tp, 0x06, 0xc4ef); + rtl8168_mdio_write(tp, 0x06, 0x79ef); + rtl8168_mdio_write(tp, 0x06, 0x45bf); + rtl8168_mdio_write(tp, 0x06, 0x3024); + rtl8168_mdio_write(tp, 0x06, 0x0238); + rtl8168_mdio_write(tp, 0x06, 0x7dbf); + rtl8168_mdio_write(tp, 0x06, 0x13ff); + rtl8168_mdio_write(tp, 0x06, 0x0238); + rtl8168_mdio_write(tp, 0x06, 0x50ad); + rtl8168_mdio_write(tp, 0x06, 0x2702); + rtl8168_mdio_write(tp, 0x06, 0x78ff); + rtl8168_mdio_write(tp, 0x06, 0xe18a); + rtl8168_mdio_write(tp, 0x06, 0xca1b); + rtl8168_mdio_write(tp, 0x06, 0x01aa); + rtl8168_mdio_write(tp, 0x06, 0x2eef); + rtl8168_mdio_write(tp, 0x06, 0x97d9); + rtl8168_mdio_write(tp, 0x06, 0x7900); + rtl8168_mdio_write(tp, 0x06, 0x9e2b); + rtl8168_mdio_write(tp, 0x06, 0x81dd); + rtl8168_mdio_write(tp, 0x06, 0xbf85); + rtl8168_mdio_write(tp, 0x06, 0xad02); + rtl8168_mdio_write(tp, 0x06, 0x387d); + rtl8168_mdio_write(tp, 0x06, 0xd101); + rtl8168_mdio_write(tp, 0x06, 0xef02); + rtl8168_mdio_write(tp, 0x06, 0x100c); + rtl8168_mdio_write(tp, 0x06, 0x11b0); + rtl8168_mdio_write(tp, 0x06, 0xfc0d); + rtl8168_mdio_write(tp, 0x06, 0x11bf); + rtl8168_mdio_write(tp, 0x06, 0x85aa); + rtl8168_mdio_write(tp, 0x06, 0x0238); + rtl8168_mdio_write(tp, 0x06, 0x7dd1); + rtl8168_mdio_write(tp, 0x06, 0x00bf); + rtl8168_mdio_write(tp, 0x06, 0x85aa); + rtl8168_mdio_write(tp, 0x06, 0x0238); + rtl8168_mdio_write(tp, 0x06, 0x7dee); + rtl8168_mdio_write(tp, 0x06, 0x8ac2); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0x0413); + rtl8168_mdio_write(tp, 0x06, 0xa38b); + rtl8168_mdio_write(tp, 0x06, 0xb4d3); + rtl8168_mdio_write(tp, 0x06, 0x8012); + rtl8168_mdio_write(tp, 0x06, 0x17a2); + rtl8168_mdio_write(tp, 0x06, 0x04ad); + rtl8168_mdio_write(tp, 0x06, 0xffef); + rtl8168_mdio_write(tp, 0x06, 0x96fe); + rtl8168_mdio_write(tp, 0x06, 0xfdfc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xf9e0); + rtl8168_mdio_write(tp, 0x06, 0x8b85); + rtl8168_mdio_write(tp, 0x06, 0xad25); + rtl8168_mdio_write(tp, 0x06, 0x48e0); + rtl8168_mdio_write(tp, 0x06, 0x8a96); + rtl8168_mdio_write(tp, 0x06, 0xe18a); + rtl8168_mdio_write(tp, 0x06, 0x977c); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x9e35); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0x9600); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0x9700); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0xbee1); + rtl8168_mdio_write(tp, 0x06, 0x8abf); + rtl8168_mdio_write(tp, 0x06, 0xe28a); + rtl8168_mdio_write(tp, 0x06, 0xc0e3); + rtl8168_mdio_write(tp, 0x06, 0x8ac1); + rtl8168_mdio_write(tp, 0x06, 0x0237); + rtl8168_mdio_write(tp, 0x06, 0x74ad); + rtl8168_mdio_write(tp, 0x06, 0x2012); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0x9603); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0x97b7); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0xc000); + rtl8168_mdio_write(tp, 0x06, 0xee8a); + rtl8168_mdio_write(tp, 0x06, 0xc100); + rtl8168_mdio_write(tp, 0x06, 0xae11); + rtl8168_mdio_write(tp, 0x06, 0x15e6); + rtl8168_mdio_write(tp, 0x06, 0x8ac0); + rtl8168_mdio_write(tp, 0x06, 0xe78a); + rtl8168_mdio_write(tp, 0x06, 0xc1ae); + rtl8168_mdio_write(tp, 0x06, 0x08ee); + rtl8168_mdio_write(tp, 0x06, 0x8ac0); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0x8ac1); + rtl8168_mdio_write(tp, 0x06, 0x00fd); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xae20); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x06, 0xf8fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x00e1); + rtl8168_mdio_write(tp, 0x06, 0xe001); + rtl8168_mdio_write(tp, 0x06, 0xad27); + rtl8168_mdio_write(tp, 0x06, 0x32e0); + rtl8168_mdio_write(tp, 0x06, 0x8b40); + rtl8168_mdio_write(tp, 0x06, 0xf720); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x40bf); + rtl8168_mdio_write(tp, 0x06, 0x3230); + rtl8168_mdio_write(tp, 0x06, 0x0238); + rtl8168_mdio_write(tp, 0x06, 0x50ad); + rtl8168_mdio_write(tp, 0x06, 0x2821); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x20e1); + rtl8168_mdio_write(tp, 0x06, 0xe021); + rtl8168_mdio_write(tp, 0x06, 0xad20); + rtl8168_mdio_write(tp, 0x06, 0x18e0); + rtl8168_mdio_write(tp, 0x06, 0x8b40); + rtl8168_mdio_write(tp, 0x06, 0xf620); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x40ee); + rtl8168_mdio_write(tp, 0x06, 0x8b3b); + rtl8168_mdio_write(tp, 0x06, 0xffe0); + rtl8168_mdio_write(tp, 0x06, 0x8a8a); + rtl8168_mdio_write(tp, 0x06, 0xe18a); + rtl8168_mdio_write(tp, 0x06, 0x8be4); + rtl8168_mdio_write(tp, 0x06, 0xe000); + rtl8168_mdio_write(tp, 0x06, 0xe5e0); + rtl8168_mdio_write(tp, 0x06, 0x01ef); + rtl8168_mdio_write(tp, 0x06, 0x96fe); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8f9); + rtl8168_mdio_write(tp, 0x06, 0xface); + rtl8168_mdio_write(tp, 0x06, 0xfaef); + rtl8168_mdio_write(tp, 0x06, 0x69fa); + rtl8168_mdio_write(tp, 0x06, 0xd401); + rtl8168_mdio_write(tp, 0x06, 0x55b4); + rtl8168_mdio_write(tp, 0x06, 0xfebf); + rtl8168_mdio_write(tp, 0x06, 0x1c1e); + rtl8168_mdio_write(tp, 0x06, 0x0238); + rtl8168_mdio_write(tp, 0x06, 0x50ac); + rtl8168_mdio_write(tp, 0x06, 0x280b); + rtl8168_mdio_write(tp, 0x06, 0xbf1c); + rtl8168_mdio_write(tp, 0x06, 0x1b02); + rtl8168_mdio_write(tp, 0x06, 0x3850); + rtl8168_mdio_write(tp, 0x06, 0xac28); + rtl8168_mdio_write(tp, 0x06, 0x49ae); + rtl8168_mdio_write(tp, 0x06, 0x64bf); + rtl8168_mdio_write(tp, 0x06, 0x1c1b); + rtl8168_mdio_write(tp, 0x06, 0x0238); + rtl8168_mdio_write(tp, 0x06, 0x50ac); + rtl8168_mdio_write(tp, 0x06, 0x285b); + rtl8168_mdio_write(tp, 0x06, 0xd000); + rtl8168_mdio_write(tp, 0x06, 0x0284); + rtl8168_mdio_write(tp, 0x06, 0xcaac); + rtl8168_mdio_write(tp, 0x06, 0x2105); + rtl8168_mdio_write(tp, 0x06, 0xac22); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0x4ebf); + rtl8168_mdio_write(tp, 0x06, 0xe0c4); + rtl8168_mdio_write(tp, 0x06, 0xbe85); + rtl8168_mdio_write(tp, 0x06, 0xf6d2); + rtl8168_mdio_write(tp, 0x06, 0x04d8); + rtl8168_mdio_write(tp, 0x06, 0x19d9); + rtl8168_mdio_write(tp, 0x06, 0x1907); + rtl8168_mdio_write(tp, 0x06, 0xdc19); + rtl8168_mdio_write(tp, 0x06, 0xdd19); + rtl8168_mdio_write(tp, 0x06, 0x0789); + rtl8168_mdio_write(tp, 0x06, 0x89ef); + rtl8168_mdio_write(tp, 0x06, 0x645e); + rtl8168_mdio_write(tp, 0x06, 0x07ff); + rtl8168_mdio_write(tp, 0x06, 0x0d65); + rtl8168_mdio_write(tp, 0x06, 0x5cf8); + rtl8168_mdio_write(tp, 0x06, 0x001e); + rtl8168_mdio_write(tp, 0x06, 0x46dc); + rtl8168_mdio_write(tp, 0x06, 0x19dd); + rtl8168_mdio_write(tp, 0x06, 0x19b2); + rtl8168_mdio_write(tp, 0x06, 0xe2d4); + rtl8168_mdio_write(tp, 0x06, 0x0001); + rtl8168_mdio_write(tp, 0x06, 0xbf1c); + rtl8168_mdio_write(tp, 0x06, 0x1b02); + rtl8168_mdio_write(tp, 0x06, 0x387d); + rtl8168_mdio_write(tp, 0x06, 0xae1d); + rtl8168_mdio_write(tp, 0x06, 0xbee0); + rtl8168_mdio_write(tp, 0x06, 0xc4bf); + rtl8168_mdio_write(tp, 0x06, 0x85f6); + rtl8168_mdio_write(tp, 0x06, 0xd204); + rtl8168_mdio_write(tp, 0x06, 0xd819); + rtl8168_mdio_write(tp, 0x06, 0xd919); + rtl8168_mdio_write(tp, 0x06, 0x07dc); + rtl8168_mdio_write(tp, 0x06, 0x19dd); + rtl8168_mdio_write(tp, 0x06, 0x1907); + rtl8168_mdio_write(tp, 0x06, 0xb2f4); + rtl8168_mdio_write(tp, 0x06, 0xd400); + rtl8168_mdio_write(tp, 0x06, 0x00bf); + rtl8168_mdio_write(tp, 0x06, 0x1c1b); + rtl8168_mdio_write(tp, 0x06, 0x0238); + rtl8168_mdio_write(tp, 0x06, 0x7dfe); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfec6); + rtl8168_mdio_write(tp, 0x06, 0xfefd); + rtl8168_mdio_write(tp, 0x06, 0xfc05); + rtl8168_mdio_write(tp, 0x06, 0xf9e2); + rtl8168_mdio_write(tp, 0x06, 0xe0ea); + rtl8168_mdio_write(tp, 0x06, 0xe3e0); + rtl8168_mdio_write(tp, 0x06, 0xeb5a); + rtl8168_mdio_write(tp, 0x06, 0x070c); + rtl8168_mdio_write(tp, 0x06, 0x031e); + rtl8168_mdio_write(tp, 0x06, 0x20e6); + rtl8168_mdio_write(tp, 0x06, 0xe0ea); + rtl8168_mdio_write(tp, 0x06, 0xe7e0); + rtl8168_mdio_write(tp, 0x06, 0xebe0); + rtl8168_mdio_write(tp, 0x06, 0xe0fc); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0xfdfd); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xfaef); + rtl8168_mdio_write(tp, 0x06, 0x69e0); + rtl8168_mdio_write(tp, 0x06, 0x8b80); + rtl8168_mdio_write(tp, 0x06, 0xad27); + rtl8168_mdio_write(tp, 0x06, 0x22bf); + rtl8168_mdio_write(tp, 0x06, 0x4616); + rtl8168_mdio_write(tp, 0x06, 0x0238); + rtl8168_mdio_write(tp, 0x06, 0x50e0); + rtl8168_mdio_write(tp, 0x06, 0x8b44); + rtl8168_mdio_write(tp, 0x06, 0x1f01); + rtl8168_mdio_write(tp, 0x06, 0x9e15); + rtl8168_mdio_write(tp, 0x06, 0xe58b); + rtl8168_mdio_write(tp, 0x06, 0x44ad); + rtl8168_mdio_write(tp, 0x06, 0x2907); + rtl8168_mdio_write(tp, 0x06, 0xac28); + rtl8168_mdio_write(tp, 0x06, 0x04d1); + rtl8168_mdio_write(tp, 0x06, 0x01ae); + rtl8168_mdio_write(tp, 0x06, 0x02d1); + rtl8168_mdio_write(tp, 0x06, 0x00bf); + rtl8168_mdio_write(tp, 0x06, 0x85b0); + rtl8168_mdio_write(tp, 0x06, 0x0238); + rtl8168_mdio_write(tp, 0x06, 0x7def); + rtl8168_mdio_write(tp, 0x06, 0x96fe); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b85); + rtl8168_mdio_write(tp, 0x06, 0xad26); + rtl8168_mdio_write(tp, 0x06, 0x30e0); + rtl8168_mdio_write(tp, 0x06, 0xe036); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x37e1); + rtl8168_mdio_write(tp, 0x06, 0x8b3f); + rtl8168_mdio_write(tp, 0x06, 0x1f10); + rtl8168_mdio_write(tp, 0x06, 0x9e23); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x3fac); + rtl8168_mdio_write(tp, 0x06, 0x200b); + rtl8168_mdio_write(tp, 0x06, 0xac21); + rtl8168_mdio_write(tp, 0x06, 0x0dac); + rtl8168_mdio_write(tp, 0x06, 0x250f); + rtl8168_mdio_write(tp, 0x06, 0xac27); + rtl8168_mdio_write(tp, 0x06, 0x11ae); + rtl8168_mdio_write(tp, 0x06, 0x1202); + rtl8168_mdio_write(tp, 0x06, 0x2c47); + rtl8168_mdio_write(tp, 0x06, 0xae0d); + rtl8168_mdio_write(tp, 0x06, 0x0285); + rtl8168_mdio_write(tp, 0x06, 0x4fae); + rtl8168_mdio_write(tp, 0x06, 0x0802); + rtl8168_mdio_write(tp, 0x06, 0x2c69); + rtl8168_mdio_write(tp, 0x06, 0xae03); + rtl8168_mdio_write(tp, 0x06, 0x022c); + rtl8168_mdio_write(tp, 0x06, 0x7cfc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xfaef); + rtl8168_mdio_write(tp, 0x06, 0x6902); + rtl8168_mdio_write(tp, 0x06, 0x856c); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x14e1); + rtl8168_mdio_write(tp, 0x06, 0xe015); + rtl8168_mdio_write(tp, 0x06, 0xad26); + rtl8168_mdio_write(tp, 0x06, 0x08d1); + rtl8168_mdio_write(tp, 0x06, 0x1ebf); + rtl8168_mdio_write(tp, 0x06, 0x2cd9); + rtl8168_mdio_write(tp, 0x06, 0x0238); + rtl8168_mdio_write(tp, 0x06, 0x7def); + rtl8168_mdio_write(tp, 0x06, 0x96fe); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b85); + rtl8168_mdio_write(tp, 0x06, 0xad27); + rtl8168_mdio_write(tp, 0x06, 0x2fd0); + rtl8168_mdio_write(tp, 0x06, 0x0b02); + rtl8168_mdio_write(tp, 0x06, 0x3682); + rtl8168_mdio_write(tp, 0x06, 0x5882); + rtl8168_mdio_write(tp, 0x06, 0x7882); + rtl8168_mdio_write(tp, 0x06, 0x9f24); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x32e1); + rtl8168_mdio_write(tp, 0x06, 0x8b33); + rtl8168_mdio_write(tp, 0x06, 0x1f10); + rtl8168_mdio_write(tp, 0x06, 0x9e1a); + rtl8168_mdio_write(tp, 0x06, 0x10e4); + rtl8168_mdio_write(tp, 0x06, 0x8b32); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x28e1); + rtl8168_mdio_write(tp, 0x06, 0xe029); + rtl8168_mdio_write(tp, 0x06, 0xf72c); + rtl8168_mdio_write(tp, 0x06, 0xe4e0); + rtl8168_mdio_write(tp, 0x06, 0x28e5); + rtl8168_mdio_write(tp, 0x06, 0xe029); + rtl8168_mdio_write(tp, 0x06, 0xf62c); + rtl8168_mdio_write(tp, 0x06, 0xe4e0); + rtl8168_mdio_write(tp, 0x06, 0x28e5); + rtl8168_mdio_write(tp, 0x06, 0xe029); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0x00e1); + rtl8168_mdio_write(tp, 0x06, 0x4077); + rtl8168_mdio_write(tp, 0x06, 0xe140); + rtl8168_mdio_write(tp, 0x06, 0x52e0); + rtl8168_mdio_write(tp, 0x06, 0xeed9); + rtl8168_mdio_write(tp, 0x06, 0xe04c); + rtl8168_mdio_write(tp, 0x06, 0xbbe0); + rtl8168_mdio_write(tp, 0x06, 0x2a00); + rtl8168_mdio_write(tp, 0x05, 0xe142); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp,0x06, gphy_val); + rtl8168_mdio_write(tp, 0x05, 0xe140); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp,0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp,0x1f, 0x0005); + for (i = 0; i < 200; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x00); + if (gphy_val & BIT_7) + break; + } + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0023); + gphy_val = rtl8168_mdio_read(tp, 0x17); + gphy_val |= BIT_1; + if (tp->RequiredSecLanDonglePatch) + gphy_val &= ~BIT_2; + rtl8168_mdio_write(tp, 0x17, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x09, 0xA20F); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0003); + rtl8168_mdio_write(tp, 0x01, 0x328A); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x00, 0x9200); +} + +static void +rtl8168_set_phy_mcu_8168f_2(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned int gphy_val,i; + + rtl8168_mdio_write(tp,0x1f, 0x0000); + rtl8168_mdio_write(tp,0x00, 0x1800); + gphy_val = rtl8168_mdio_read(tp, 0x15); + gphy_val &= ~(BIT_12); + rtl8168_mdio_write(tp,0x15, gphy_val); + rtl8168_mdio_write(tp,0x00, 0x4800); + rtl8168_mdio_write(tp,0x1f, 0x0007); + rtl8168_mdio_write(tp,0x1e, 0x002f); + for (i = 0; i < 1000; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x1c); + if (gphy_val & 0x0080) + break; + } + rtl8168_mdio_write(tp,0x1f, 0x0000); + rtl8168_mdio_write(tp,0x00, 0x1800); + rtl8168_mdio_write(tp,0x1f, 0x0007); + rtl8168_mdio_write(tp,0x1e, 0x0023); + for (i = 0; i < 200; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x18); + if (!(gphy_val & 0x0001)) + break; + } + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x0080); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0023); + rtl8168_mdio_write(tp, 0x16, 0x0306); + rtl8168_mdio_write(tp, 0x16, 0x0307); + rtl8168_mdio_write(tp, 0x15, 0x0098); + rtl8168_mdio_write(tp, 0x19, 0x7c0b); + rtl8168_mdio_write(tp, 0x15, 0x0099); + rtl8168_mdio_write(tp, 0x19, 0x6c0b); + rtl8168_mdio_write(tp, 0x15, 0x00eb); + rtl8168_mdio_write(tp, 0x19, 0x6c0b); + rtl8168_mdio_write(tp, 0x15, 0x00f8); + rtl8168_mdio_write(tp, 0x19, 0x6f0b); + rtl8168_mdio_write(tp, 0x15, 0x00fe); + rtl8168_mdio_write(tp, 0x19, 0x6f0f); + rtl8168_mdio_write(tp, 0x15, 0x00db); + rtl8168_mdio_write(tp, 0x19, 0x6f09); + rtl8168_mdio_write(tp, 0x15, 0x00dc); + rtl8168_mdio_write(tp, 0x19, 0xaefd); + rtl8168_mdio_write(tp, 0x15, 0x00dd); + rtl8168_mdio_write(tp, 0x19, 0x6f0b); + rtl8168_mdio_write(tp, 0x15, 0x00de); + rtl8168_mdio_write(tp, 0x19, 0xc60b); + rtl8168_mdio_write(tp, 0x15, 0x00df); + rtl8168_mdio_write(tp, 0x19, 0x00fa); + rtl8168_mdio_write(tp, 0x15, 0x00e0); + rtl8168_mdio_write(tp, 0x19, 0x30e1); + rtl8168_mdio_write(tp, 0x15, 0x020c); + rtl8168_mdio_write(tp, 0x19, 0x3224); + rtl8168_mdio_write(tp, 0x15, 0x020e); + rtl8168_mdio_write(tp, 0x19, 0x9813); + rtl8168_mdio_write(tp, 0x15, 0x020f); + rtl8168_mdio_write(tp, 0x19, 0x7801); + rtl8168_mdio_write(tp, 0x15, 0x0210); + rtl8168_mdio_write(tp, 0x19, 0x930f); + rtl8168_mdio_write(tp, 0x15, 0x0211); + rtl8168_mdio_write(tp, 0x19, 0x9206); + rtl8168_mdio_write(tp, 0x15, 0x0212); + rtl8168_mdio_write(tp, 0x19, 0x4002); + rtl8168_mdio_write(tp, 0x15, 0x0213); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x0214); + rtl8168_mdio_write(tp, 0x19, 0x588f); + rtl8168_mdio_write(tp, 0x15, 0x0215); + rtl8168_mdio_write(tp, 0x19, 0x5520); + rtl8168_mdio_write(tp, 0x15, 0x0216); + rtl8168_mdio_write(tp, 0x19, 0x3224); + rtl8168_mdio_write(tp, 0x15, 0x0217); + rtl8168_mdio_write(tp, 0x19, 0x4002); + rtl8168_mdio_write(tp, 0x15, 0x0218); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x0219); + rtl8168_mdio_write(tp, 0x19, 0x588d); + rtl8168_mdio_write(tp, 0x15, 0x021a); + rtl8168_mdio_write(tp, 0x19, 0x5540); + rtl8168_mdio_write(tp, 0x15, 0x021b); + rtl8168_mdio_write(tp, 0x19, 0x9e03); + rtl8168_mdio_write(tp, 0x15, 0x021c); + rtl8168_mdio_write(tp, 0x19, 0x7c40); + rtl8168_mdio_write(tp, 0x15, 0x021d); + rtl8168_mdio_write(tp, 0x19, 0x6840); + rtl8168_mdio_write(tp, 0x15, 0x021e); + rtl8168_mdio_write(tp, 0x19, 0x3224); + rtl8168_mdio_write(tp, 0x15, 0x021f); + rtl8168_mdio_write(tp, 0x19, 0x4002); + rtl8168_mdio_write(tp, 0x15, 0x0220); + rtl8168_mdio_write(tp, 0x19, 0x3224); + rtl8168_mdio_write(tp, 0x15, 0x0221); + rtl8168_mdio_write(tp, 0x19, 0x9e03); + rtl8168_mdio_write(tp, 0x15, 0x0222); + rtl8168_mdio_write(tp, 0x19, 0x7c40); + rtl8168_mdio_write(tp, 0x15, 0x0223); + rtl8168_mdio_write(tp, 0x19, 0x6840); + rtl8168_mdio_write(tp, 0x15, 0x0224); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x0225); + rtl8168_mdio_write(tp, 0x19, 0x3231); + rtl8168_mdio_write(tp, 0x15, 0x0000); + rtl8168_mdio_write(tp, 0x16, 0x0306); + rtl8168_mdio_write(tp, 0x16, 0x0300); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x0080); + rtl8168_mdio_write(tp, 0x05, 0x8000); + rtl8168_mdio_write(tp, 0x06, 0x0280); + rtl8168_mdio_write(tp, 0x06, 0x48f7); + rtl8168_mdio_write(tp, 0x06, 0x00e0); + rtl8168_mdio_write(tp, 0x06, 0xfff7); + rtl8168_mdio_write(tp, 0x06, 0xa080); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0xf602); + rtl8168_mdio_write(tp, 0x06, 0x011b); + rtl8168_mdio_write(tp, 0x06, 0x0201); + rtl8168_mdio_write(tp, 0x06, 0x2802); + rtl8168_mdio_write(tp, 0x06, 0x0135); + rtl8168_mdio_write(tp, 0x06, 0x0201); + rtl8168_mdio_write(tp, 0x06, 0x4502); + rtl8168_mdio_write(tp, 0x06, 0x015f); + rtl8168_mdio_write(tp, 0x06, 0x0280); + rtl8168_mdio_write(tp, 0x06, 0x6b02); + rtl8168_mdio_write(tp, 0x06, 0x80e5); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x88e1); + rtl8168_mdio_write(tp, 0x06, 0x8b89); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8a1e); + rtl8168_mdio_write(tp, 0x06, 0x01e1); + rtl8168_mdio_write(tp, 0x06, 0x8b8b); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8c1e); + rtl8168_mdio_write(tp, 0x06, 0x01e1); + rtl8168_mdio_write(tp, 0x06, 0x8b8d); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8e1e); + rtl8168_mdio_write(tp, 0x06, 0x01a0); + rtl8168_mdio_write(tp, 0x06, 0x00c7); + rtl8168_mdio_write(tp, 0x06, 0xaebb); + rtl8168_mdio_write(tp, 0x06, 0xbf8b); + rtl8168_mdio_write(tp, 0x06, 0x88ec); + rtl8168_mdio_write(tp, 0x06, 0x0019); + rtl8168_mdio_write(tp, 0x06, 0xa98b); + rtl8168_mdio_write(tp, 0x06, 0x90f9); + rtl8168_mdio_write(tp, 0x06, 0xeeff); + rtl8168_mdio_write(tp, 0x06, 0xf600); + rtl8168_mdio_write(tp, 0x06, 0xeeff); + rtl8168_mdio_write(tp, 0x06, 0xf7fe); + rtl8168_mdio_write(tp, 0x06, 0xd100); + rtl8168_mdio_write(tp, 0x06, 0xbf81); + rtl8168_mdio_write(tp, 0x06, 0x9802); + rtl8168_mdio_write(tp, 0x06, 0x39f3); + rtl8168_mdio_write(tp, 0x06, 0xd101); + rtl8168_mdio_write(tp, 0x06, 0xbf81); + rtl8168_mdio_write(tp, 0x06, 0x9b02); + rtl8168_mdio_write(tp, 0x06, 0x39f3); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8dad); + rtl8168_mdio_write(tp, 0x06, 0x2014); + rtl8168_mdio_write(tp, 0x06, 0xee8b); + rtl8168_mdio_write(tp, 0x06, 0x8d00); + rtl8168_mdio_write(tp, 0x06, 0xe08a); + rtl8168_mdio_write(tp, 0x06, 0x5a78); + rtl8168_mdio_write(tp, 0x06, 0x039e); + rtl8168_mdio_write(tp, 0x06, 0x0902); + rtl8168_mdio_write(tp, 0x06, 0x05fc); + rtl8168_mdio_write(tp, 0x06, 0x0280); + rtl8168_mdio_write(tp, 0x06, 0x8802); + rtl8168_mdio_write(tp, 0x06, 0x32dd); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8f9); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x81ac); + rtl8168_mdio_write(tp, 0x06, 0x261a); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x81ac); + rtl8168_mdio_write(tp, 0x06, 0x2114); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x85ac); + rtl8168_mdio_write(tp, 0x06, 0x200e); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x85ac); + rtl8168_mdio_write(tp, 0x06, 0x2308); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x87ac); + rtl8168_mdio_write(tp, 0x06, 0x2402); + rtl8168_mdio_write(tp, 0x06, 0xae38); + rtl8168_mdio_write(tp, 0x06, 0x021a); + rtl8168_mdio_write(tp, 0x06, 0xd6ee); + rtl8168_mdio_write(tp, 0x06, 0xe41c); + rtl8168_mdio_write(tp, 0x06, 0x04ee); + rtl8168_mdio_write(tp, 0x06, 0xe41d); + rtl8168_mdio_write(tp, 0x06, 0x04e2); + rtl8168_mdio_write(tp, 0x06, 0xe07c); + rtl8168_mdio_write(tp, 0x06, 0xe3e0); + rtl8168_mdio_write(tp, 0x06, 0x7de0); + rtl8168_mdio_write(tp, 0x06, 0xe038); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x39ad); + rtl8168_mdio_write(tp, 0x06, 0x2e1b); + rtl8168_mdio_write(tp, 0x06, 0xad39); + rtl8168_mdio_write(tp, 0x06, 0x0dd1); + rtl8168_mdio_write(tp, 0x06, 0x01bf); + rtl8168_mdio_write(tp, 0x06, 0x22c8); + rtl8168_mdio_write(tp, 0x06, 0x0239); + rtl8168_mdio_write(tp, 0x06, 0xf302); + rtl8168_mdio_write(tp, 0x06, 0x21f0); + rtl8168_mdio_write(tp, 0x06, 0xae0b); + rtl8168_mdio_write(tp, 0x06, 0xac38); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0x0602); + rtl8168_mdio_write(tp, 0x06, 0x222d); + rtl8168_mdio_write(tp, 0x06, 0x0222); + rtl8168_mdio_write(tp, 0x06, 0x7202); + rtl8168_mdio_write(tp, 0x06, 0x1ae7); + rtl8168_mdio_write(tp, 0x06, 0xfdfc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x201a); + rtl8168_mdio_write(tp, 0x06, 0xf620); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8e02); + rtl8168_mdio_write(tp, 0x06, 0x2afe); + rtl8168_mdio_write(tp, 0x06, 0x022c); + rtl8168_mdio_write(tp, 0x06, 0x5c02); + rtl8168_mdio_write(tp, 0x06, 0x03c5); + rtl8168_mdio_write(tp, 0x06, 0x0281); + rtl8168_mdio_write(tp, 0x06, 0x6702); + rtl8168_mdio_write(tp, 0x06, 0x2e4f); + rtl8168_mdio_write(tp, 0x06, 0x0204); + rtl8168_mdio_write(tp, 0x06, 0x8902); + rtl8168_mdio_write(tp, 0x06, 0x2f7a); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x210b); + rtl8168_mdio_write(tp, 0x06, 0xf621); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8e02); + rtl8168_mdio_write(tp, 0x06, 0x0445); + rtl8168_mdio_write(tp, 0x06, 0x021c); + rtl8168_mdio_write(tp, 0x06, 0xb8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xad22); + rtl8168_mdio_write(tp, 0x06, 0x08f6); + rtl8168_mdio_write(tp, 0x06, 0x22e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0x0235); + rtl8168_mdio_write(tp, 0x06, 0xd4e0); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xad23); + rtl8168_mdio_write(tp, 0x06, 0x08f6); + rtl8168_mdio_write(tp, 0x06, 0x23e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0x0231); + rtl8168_mdio_write(tp, 0x06, 0xc8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xad24); + rtl8168_mdio_write(tp, 0x06, 0x05f6); + rtl8168_mdio_write(tp, 0x06, 0x24e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x2505); + rtl8168_mdio_write(tp, 0x06, 0xf625); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8ee0); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xad26); + rtl8168_mdio_write(tp, 0x06, 0x08f6); + rtl8168_mdio_write(tp, 0x06, 0x26e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0x022d); + rtl8168_mdio_write(tp, 0x06, 0x6ae0); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xad27); + rtl8168_mdio_write(tp, 0x06, 0x05f6); + rtl8168_mdio_write(tp, 0x06, 0x27e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0x0203); + rtl8168_mdio_write(tp, 0x06, 0x8bfc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xfaef); + rtl8168_mdio_write(tp, 0x06, 0x69e0); + rtl8168_mdio_write(tp, 0x06, 0x8b80); + rtl8168_mdio_write(tp, 0x06, 0xad27); + rtl8168_mdio_write(tp, 0x06, 0x22bf); + rtl8168_mdio_write(tp, 0x06, 0x479a); + rtl8168_mdio_write(tp, 0x06, 0x0239); + rtl8168_mdio_write(tp, 0x06, 0xc6e0); + rtl8168_mdio_write(tp, 0x06, 0x8b44); + rtl8168_mdio_write(tp, 0x06, 0x1f01); + rtl8168_mdio_write(tp, 0x06, 0x9e15); + rtl8168_mdio_write(tp, 0x06, 0xe58b); + rtl8168_mdio_write(tp, 0x06, 0x44ad); + rtl8168_mdio_write(tp, 0x06, 0x2907); + rtl8168_mdio_write(tp, 0x06, 0xac28); + rtl8168_mdio_write(tp, 0x06, 0x04d1); + rtl8168_mdio_write(tp, 0x06, 0x01ae); + rtl8168_mdio_write(tp, 0x06, 0x02d1); + rtl8168_mdio_write(tp, 0x06, 0x00bf); + rtl8168_mdio_write(tp, 0x06, 0x819e); + rtl8168_mdio_write(tp, 0x06, 0x0239); + rtl8168_mdio_write(tp, 0x06, 0xf3ef); + rtl8168_mdio_write(tp, 0x06, 0x96fe); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0x00e1); + rtl8168_mdio_write(tp, 0x06, 0x4077); + rtl8168_mdio_write(tp, 0x06, 0xe140); + rtl8168_mdio_write(tp, 0x06, 0xbbe0); + rtl8168_mdio_write(tp, 0x06, 0x2a00); + rtl8168_mdio_write(tp, 0x05, 0xe142); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x05, 0xe140); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp,0x1f, 0x0005); + for (i = 0; i < 200; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x00); + if (gphy_val & BIT_7) + break; + } + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0023); + gphy_val = rtl8168_mdio_read(tp, 0x17); + gphy_val |= BIT_1; + if (tp->RequiredSecLanDonglePatch) + gphy_val &= ~BIT_2; + rtl8168_mdio_write(tp, 0x17, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x00, 0x9200); +} + +static void +rtl8168_set_phy_mcu_8411_1(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned int gphy_val,i; + + rtl8168_mdio_write(tp,0x1f, 0x0000); + rtl8168_mdio_write(tp,0x00, 0x1800); + gphy_val = rtl8168_mdio_read(tp, 0x15); + gphy_val &= ~(BIT_12); + rtl8168_mdio_write(tp,0x15, gphy_val); + rtl8168_mdio_write(tp,0x00, 0x4800); + rtl8168_mdio_write(tp,0x1f, 0x0007); + rtl8168_mdio_write(tp,0x1e, 0x002f); + for (i = 0; i < 1000; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x1c); + if (gphy_val & 0x0080) + break; + } + rtl8168_mdio_write(tp,0x1f, 0x0000); + rtl8168_mdio_write(tp,0x00, 0x1800); + rtl8168_mdio_write(tp,0x1f, 0x0007); + rtl8168_mdio_write(tp,0x1e, 0x0023); + for (i = 0; i < 200; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x18); + if (!(gphy_val & 0x0001)) + break; + } + rtl8168_mdio_write(tp,0x1f, 0x0005); + rtl8168_mdio_write(tp,0x05, 0xfff6); + rtl8168_mdio_write(tp,0x06, 0x0080); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x0023); + rtl8168_mdio_write(tp, 0x16, 0x0306); + rtl8168_mdio_write(tp, 0x16, 0x0307); + rtl8168_mdio_write(tp, 0x15, 0x0098); + rtl8168_mdio_write(tp, 0x19, 0x7c0b); + rtl8168_mdio_write(tp, 0x15, 0x0099); + rtl8168_mdio_write(tp, 0x19, 0x6c0b); + rtl8168_mdio_write(tp, 0x15, 0x00eb); + rtl8168_mdio_write(tp, 0x19, 0x6c0b); + rtl8168_mdio_write(tp, 0x15, 0x00f8); + rtl8168_mdio_write(tp, 0x19, 0x6f0b); + rtl8168_mdio_write(tp, 0x15, 0x00fe); + rtl8168_mdio_write(tp, 0x19, 0x6f0f); + rtl8168_mdio_write(tp, 0x15, 0x00db); + rtl8168_mdio_write(tp, 0x19, 0x6f09); + rtl8168_mdio_write(tp, 0x15, 0x00dc); + rtl8168_mdio_write(tp, 0x19, 0xaefd); + rtl8168_mdio_write(tp, 0x15, 0x00dd); + rtl8168_mdio_write(tp, 0x19, 0x6f0b); + rtl8168_mdio_write(tp, 0x15, 0x00de); + rtl8168_mdio_write(tp, 0x19, 0xc60b); + rtl8168_mdio_write(tp, 0x15, 0x00df); + rtl8168_mdio_write(tp, 0x19, 0x00fa); + rtl8168_mdio_write(tp, 0x15, 0x00e0); + rtl8168_mdio_write(tp, 0x19, 0x30e1); + rtl8168_mdio_write(tp, 0x15, 0x020c); + rtl8168_mdio_write(tp, 0x19, 0x3224); + rtl8168_mdio_write(tp, 0x15, 0x020e); + rtl8168_mdio_write(tp, 0x19, 0x9813); + rtl8168_mdio_write(tp, 0x15, 0x020f); + rtl8168_mdio_write(tp, 0x19, 0x7801); + rtl8168_mdio_write(tp, 0x15, 0x0210); + rtl8168_mdio_write(tp, 0x19, 0x930f); + rtl8168_mdio_write(tp, 0x15, 0x0211); + rtl8168_mdio_write(tp, 0x19, 0x9206); + rtl8168_mdio_write(tp, 0x15, 0x0212); + rtl8168_mdio_write(tp, 0x19, 0x4002); + rtl8168_mdio_write(tp, 0x15, 0x0213); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x0214); + rtl8168_mdio_write(tp, 0x19, 0x588f); + rtl8168_mdio_write(tp, 0x15, 0x0215); + rtl8168_mdio_write(tp, 0x19, 0x5520); + rtl8168_mdio_write(tp, 0x15, 0x0216); + rtl8168_mdio_write(tp, 0x19, 0x3224); + rtl8168_mdio_write(tp, 0x15, 0x0217); + rtl8168_mdio_write(tp, 0x19, 0x4002); + rtl8168_mdio_write(tp, 0x15, 0x0218); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x0219); + rtl8168_mdio_write(tp, 0x19, 0x588d); + rtl8168_mdio_write(tp, 0x15, 0x021a); + rtl8168_mdio_write(tp, 0x19, 0x5540); + rtl8168_mdio_write(tp, 0x15, 0x021b); + rtl8168_mdio_write(tp, 0x19, 0x9e03); + rtl8168_mdio_write(tp, 0x15, 0x021c); + rtl8168_mdio_write(tp, 0x19, 0x7c40); + rtl8168_mdio_write(tp, 0x15, 0x021d); + rtl8168_mdio_write(tp, 0x19, 0x6840); + rtl8168_mdio_write(tp, 0x15, 0x021e); + rtl8168_mdio_write(tp, 0x19, 0x3224); + rtl8168_mdio_write(tp, 0x15, 0x021f); + rtl8168_mdio_write(tp, 0x19, 0x4002); + rtl8168_mdio_write(tp, 0x15, 0x0220); + rtl8168_mdio_write(tp, 0x19, 0x3224); + rtl8168_mdio_write(tp, 0x15, 0x0221); + rtl8168_mdio_write(tp, 0x19, 0x9e03); + rtl8168_mdio_write(tp, 0x15, 0x0222); + rtl8168_mdio_write(tp, 0x19, 0x7c40); + rtl8168_mdio_write(tp, 0x15, 0x0223); + rtl8168_mdio_write(tp, 0x19, 0x6840); + rtl8168_mdio_write(tp, 0x15, 0x0224); + rtl8168_mdio_write(tp, 0x19, 0x7800); + rtl8168_mdio_write(tp, 0x15, 0x0225); + rtl8168_mdio_write(tp, 0x19, 0x3231); + rtl8168_mdio_write(tp, 0x15, 0x0000); + rtl8168_mdio_write(tp, 0x16, 0x0306); + rtl8168_mdio_write(tp, 0x16, 0x0300); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x0080); + rtl8168_mdio_write(tp, 0x05, 0x8000); + rtl8168_mdio_write(tp, 0x06, 0x0280); + rtl8168_mdio_write(tp, 0x06, 0x48f7); + rtl8168_mdio_write(tp, 0x06, 0x00e0); + rtl8168_mdio_write(tp, 0x06, 0xfff7); + rtl8168_mdio_write(tp, 0x06, 0xa080); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0xf602); + rtl8168_mdio_write(tp, 0x06, 0x011e); + rtl8168_mdio_write(tp, 0x06, 0x0201); + rtl8168_mdio_write(tp, 0x06, 0x2b02); + rtl8168_mdio_write(tp, 0x06, 0x8077); + rtl8168_mdio_write(tp, 0x06, 0x0201); + rtl8168_mdio_write(tp, 0x06, 0x4802); + rtl8168_mdio_write(tp, 0x06, 0x0162); + rtl8168_mdio_write(tp, 0x06, 0x0280); + rtl8168_mdio_write(tp, 0x06, 0x9402); + rtl8168_mdio_write(tp, 0x06, 0x810e); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x88e1); + rtl8168_mdio_write(tp, 0x06, 0x8b89); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8a1e); + rtl8168_mdio_write(tp, 0x06, 0x01e1); + rtl8168_mdio_write(tp, 0x06, 0x8b8b); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8c1e); + rtl8168_mdio_write(tp, 0x06, 0x01e1); + rtl8168_mdio_write(tp, 0x06, 0x8b8d); + rtl8168_mdio_write(tp, 0x06, 0x1e01); + rtl8168_mdio_write(tp, 0x06, 0xe18b); + rtl8168_mdio_write(tp, 0x06, 0x8e1e); + rtl8168_mdio_write(tp, 0x06, 0x01a0); + rtl8168_mdio_write(tp, 0x06, 0x00c7); + rtl8168_mdio_write(tp, 0x06, 0xaebb); + rtl8168_mdio_write(tp, 0x06, 0xd481); + rtl8168_mdio_write(tp, 0x06, 0xd4e4); + rtl8168_mdio_write(tp, 0x06, 0x8b92); + rtl8168_mdio_write(tp, 0x06, 0xe58b); + rtl8168_mdio_write(tp, 0x06, 0x9302); + rtl8168_mdio_write(tp, 0x06, 0x2e5a); + rtl8168_mdio_write(tp, 0x06, 0xbf8b); + rtl8168_mdio_write(tp, 0x06, 0x88ec); + rtl8168_mdio_write(tp, 0x06, 0x0019); + rtl8168_mdio_write(tp, 0x06, 0xa98b); + rtl8168_mdio_write(tp, 0x06, 0x90f9); + rtl8168_mdio_write(tp, 0x06, 0xeeff); + rtl8168_mdio_write(tp, 0x06, 0xf600); + rtl8168_mdio_write(tp, 0x06, 0xeeff); + rtl8168_mdio_write(tp, 0x06, 0xf7fc); + rtl8168_mdio_write(tp, 0x06, 0xd100); + rtl8168_mdio_write(tp, 0x06, 0xbf83); + rtl8168_mdio_write(tp, 0x06, 0x3c02); + rtl8168_mdio_write(tp, 0x06, 0x3a21); + rtl8168_mdio_write(tp, 0x06, 0xd101); + rtl8168_mdio_write(tp, 0x06, 0xbf83); + rtl8168_mdio_write(tp, 0x06, 0x3f02); + rtl8168_mdio_write(tp, 0x06, 0x3a21); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8aad); + rtl8168_mdio_write(tp, 0x06, 0x2014); + rtl8168_mdio_write(tp, 0x06, 0xee8b); + rtl8168_mdio_write(tp, 0x06, 0x8a00); + rtl8168_mdio_write(tp, 0x06, 0x0220); + rtl8168_mdio_write(tp, 0x06, 0x8be0); + rtl8168_mdio_write(tp, 0x06, 0xe426); + rtl8168_mdio_write(tp, 0x06, 0xe1e4); + rtl8168_mdio_write(tp, 0x06, 0x27ee); + rtl8168_mdio_write(tp, 0x06, 0xe426); + rtl8168_mdio_write(tp, 0x06, 0x23e5); + rtl8168_mdio_write(tp, 0x06, 0xe427); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b8d); + rtl8168_mdio_write(tp, 0x06, 0xad20); + rtl8168_mdio_write(tp, 0x06, 0x14ee); + rtl8168_mdio_write(tp, 0x06, 0x8b8d); + rtl8168_mdio_write(tp, 0x06, 0x00e0); + rtl8168_mdio_write(tp, 0x06, 0x8a5a); + rtl8168_mdio_write(tp, 0x06, 0x7803); + rtl8168_mdio_write(tp, 0x06, 0x9e09); + rtl8168_mdio_write(tp, 0x06, 0x0206); + rtl8168_mdio_write(tp, 0x06, 0x2802); + rtl8168_mdio_write(tp, 0x06, 0x80b1); + rtl8168_mdio_write(tp, 0x06, 0x0232); + rtl8168_mdio_write(tp, 0x06, 0xfdfc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xf9e0); + rtl8168_mdio_write(tp, 0x06, 0x8b81); + rtl8168_mdio_write(tp, 0x06, 0xac26); + rtl8168_mdio_write(tp, 0x06, 0x1ae0); + rtl8168_mdio_write(tp, 0x06, 0x8b81); + rtl8168_mdio_write(tp, 0x06, 0xac21); + rtl8168_mdio_write(tp, 0x06, 0x14e0); + rtl8168_mdio_write(tp, 0x06, 0x8b85); + rtl8168_mdio_write(tp, 0x06, 0xac20); + rtl8168_mdio_write(tp, 0x06, 0x0ee0); + rtl8168_mdio_write(tp, 0x06, 0x8b85); + rtl8168_mdio_write(tp, 0x06, 0xac23); + rtl8168_mdio_write(tp, 0x06, 0x08e0); + rtl8168_mdio_write(tp, 0x06, 0x8b87); + rtl8168_mdio_write(tp, 0x06, 0xac24); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0x3802); + rtl8168_mdio_write(tp, 0x06, 0x1b02); + rtl8168_mdio_write(tp, 0x06, 0xeee4); + rtl8168_mdio_write(tp, 0x06, 0x1c04); + rtl8168_mdio_write(tp, 0x06, 0xeee4); + rtl8168_mdio_write(tp, 0x06, 0x1d04); + rtl8168_mdio_write(tp, 0x06, 0xe2e0); + rtl8168_mdio_write(tp, 0x06, 0x7ce3); + rtl8168_mdio_write(tp, 0x06, 0xe07d); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x38e1); + rtl8168_mdio_write(tp, 0x06, 0xe039); + rtl8168_mdio_write(tp, 0x06, 0xad2e); + rtl8168_mdio_write(tp, 0x06, 0x1bad); + rtl8168_mdio_write(tp, 0x06, 0x390d); + rtl8168_mdio_write(tp, 0x06, 0xd101); + rtl8168_mdio_write(tp, 0x06, 0xbf22); + rtl8168_mdio_write(tp, 0x06, 0xe802); + rtl8168_mdio_write(tp, 0x06, 0x3a21); + rtl8168_mdio_write(tp, 0x06, 0x0222); + rtl8168_mdio_write(tp, 0x06, 0x10ae); + rtl8168_mdio_write(tp, 0x06, 0x0bac); + rtl8168_mdio_write(tp, 0x06, 0x3802); + rtl8168_mdio_write(tp, 0x06, 0xae06); + rtl8168_mdio_write(tp, 0x06, 0x0222); + rtl8168_mdio_write(tp, 0x06, 0x4d02); + rtl8168_mdio_write(tp, 0x06, 0x2292); + rtl8168_mdio_write(tp, 0x06, 0x021b); + rtl8168_mdio_write(tp, 0x06, 0x13fd); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xad20); + rtl8168_mdio_write(tp, 0x06, 0x1af6); + rtl8168_mdio_write(tp, 0x06, 0x20e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0x022b); + rtl8168_mdio_write(tp, 0x06, 0x1e02); + rtl8168_mdio_write(tp, 0x06, 0x82ae); + rtl8168_mdio_write(tp, 0x06, 0x0203); + rtl8168_mdio_write(tp, 0x06, 0xc002); + rtl8168_mdio_write(tp, 0x06, 0x827d); + rtl8168_mdio_write(tp, 0x06, 0x022e); + rtl8168_mdio_write(tp, 0x06, 0x6f02); + rtl8168_mdio_write(tp, 0x06, 0x047b); + rtl8168_mdio_write(tp, 0x06, 0x022f); + rtl8168_mdio_write(tp, 0x06, 0x9ae0); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xad21); + rtl8168_mdio_write(tp, 0x06, 0x0bf6); + rtl8168_mdio_write(tp, 0x06, 0x21e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0x0281); + rtl8168_mdio_write(tp, 0x06, 0x9002); + rtl8168_mdio_write(tp, 0x06, 0x1cd9); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x2208); + rtl8168_mdio_write(tp, 0x06, 0xf622); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8e02); + rtl8168_mdio_write(tp, 0x06, 0x35f4); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x2308); + rtl8168_mdio_write(tp, 0x06, 0xf623); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8e02); + rtl8168_mdio_write(tp, 0x06, 0x31e8); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x2405); + rtl8168_mdio_write(tp, 0x06, 0xf624); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8ee0); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xad25); + rtl8168_mdio_write(tp, 0x06, 0x05f6); + rtl8168_mdio_write(tp, 0x06, 0x25e4); + rtl8168_mdio_write(tp, 0x06, 0x8b8e); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x2608); + rtl8168_mdio_write(tp, 0x06, 0xf626); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8e02); + rtl8168_mdio_write(tp, 0x06, 0x2d8a); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x8ead); + rtl8168_mdio_write(tp, 0x06, 0x2705); + rtl8168_mdio_write(tp, 0x06, 0xf627); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x8e02); + rtl8168_mdio_write(tp, 0x06, 0x0386); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8fa); + rtl8168_mdio_write(tp, 0x06, 0xef69); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x00e1); + rtl8168_mdio_write(tp, 0x06, 0xe001); + rtl8168_mdio_write(tp, 0x06, 0xad27); + rtl8168_mdio_write(tp, 0x06, 0x32e0); + rtl8168_mdio_write(tp, 0x06, 0x8b40); + rtl8168_mdio_write(tp, 0x06, 0xf720); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x40bf); + rtl8168_mdio_write(tp, 0x06, 0x32c1); + rtl8168_mdio_write(tp, 0x06, 0x0239); + rtl8168_mdio_write(tp, 0x06, 0xf4ad); + rtl8168_mdio_write(tp, 0x06, 0x2821); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x20e1); + rtl8168_mdio_write(tp, 0x06, 0xe021); + rtl8168_mdio_write(tp, 0x06, 0xad20); + rtl8168_mdio_write(tp, 0x06, 0x18e0); + rtl8168_mdio_write(tp, 0x06, 0x8b40); + rtl8168_mdio_write(tp, 0x06, 0xf620); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x40ee); + rtl8168_mdio_write(tp, 0x06, 0x8b3b); + rtl8168_mdio_write(tp, 0x06, 0xffe0); + rtl8168_mdio_write(tp, 0x06, 0x8a8a); + rtl8168_mdio_write(tp, 0x06, 0xe18a); + rtl8168_mdio_write(tp, 0x06, 0x8be4); + rtl8168_mdio_write(tp, 0x06, 0xe000); + rtl8168_mdio_write(tp, 0x06, 0xe5e0); + rtl8168_mdio_write(tp, 0x06, 0x01ef); + rtl8168_mdio_write(tp, 0x06, 0x96fe); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8f9); + rtl8168_mdio_write(tp, 0x06, 0xface); + rtl8168_mdio_write(tp, 0x06, 0xfaef); + rtl8168_mdio_write(tp, 0x06, 0x69fa); + rtl8168_mdio_write(tp, 0x06, 0xd401); + rtl8168_mdio_write(tp, 0x06, 0x55b4); + rtl8168_mdio_write(tp, 0x06, 0xfebf); + rtl8168_mdio_write(tp, 0x06, 0x1c5e); + rtl8168_mdio_write(tp, 0x06, 0x0239); + rtl8168_mdio_write(tp, 0x06, 0xf4ac); + rtl8168_mdio_write(tp, 0x06, 0x280b); + rtl8168_mdio_write(tp, 0x06, 0xbf1c); + rtl8168_mdio_write(tp, 0x06, 0x5b02); + rtl8168_mdio_write(tp, 0x06, 0x39f4); + rtl8168_mdio_write(tp, 0x06, 0xac28); + rtl8168_mdio_write(tp, 0x06, 0x49ae); + rtl8168_mdio_write(tp, 0x06, 0x64bf); + rtl8168_mdio_write(tp, 0x06, 0x1c5b); + rtl8168_mdio_write(tp, 0x06, 0x0239); + rtl8168_mdio_write(tp, 0x06, 0xf4ac); + rtl8168_mdio_write(tp, 0x06, 0x285b); + rtl8168_mdio_write(tp, 0x06, 0xd000); + rtl8168_mdio_write(tp, 0x06, 0x0282); + rtl8168_mdio_write(tp, 0x06, 0x62ac); + rtl8168_mdio_write(tp, 0x06, 0x2105); + rtl8168_mdio_write(tp, 0x06, 0xac22); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0x4ebf); + rtl8168_mdio_write(tp, 0x06, 0xe0c4); + rtl8168_mdio_write(tp, 0x06, 0xbe85); + rtl8168_mdio_write(tp, 0x06, 0xecd2); + rtl8168_mdio_write(tp, 0x06, 0x04d8); + rtl8168_mdio_write(tp, 0x06, 0x19d9); + rtl8168_mdio_write(tp, 0x06, 0x1907); + rtl8168_mdio_write(tp, 0x06, 0xdc19); + rtl8168_mdio_write(tp, 0x06, 0xdd19); + rtl8168_mdio_write(tp, 0x06, 0x0789); + rtl8168_mdio_write(tp, 0x06, 0x89ef); + rtl8168_mdio_write(tp, 0x06, 0x645e); + rtl8168_mdio_write(tp, 0x06, 0x07ff); + rtl8168_mdio_write(tp, 0x06, 0x0d65); + rtl8168_mdio_write(tp, 0x06, 0x5cf8); + rtl8168_mdio_write(tp, 0x06, 0x001e); + rtl8168_mdio_write(tp, 0x06, 0x46dc); + rtl8168_mdio_write(tp, 0x06, 0x19dd); + rtl8168_mdio_write(tp, 0x06, 0x19b2); + rtl8168_mdio_write(tp, 0x06, 0xe2d4); + rtl8168_mdio_write(tp, 0x06, 0x0001); + rtl8168_mdio_write(tp, 0x06, 0xbf1c); + rtl8168_mdio_write(tp, 0x06, 0x5b02); + rtl8168_mdio_write(tp, 0x06, 0x3a21); + rtl8168_mdio_write(tp, 0x06, 0xae1d); + rtl8168_mdio_write(tp, 0x06, 0xbee0); + rtl8168_mdio_write(tp, 0x06, 0xc4bf); + rtl8168_mdio_write(tp, 0x06, 0x85ec); + rtl8168_mdio_write(tp, 0x06, 0xd204); + rtl8168_mdio_write(tp, 0x06, 0xd819); + rtl8168_mdio_write(tp, 0x06, 0xd919); + rtl8168_mdio_write(tp, 0x06, 0x07dc); + rtl8168_mdio_write(tp, 0x06, 0x19dd); + rtl8168_mdio_write(tp, 0x06, 0x1907); + rtl8168_mdio_write(tp, 0x06, 0xb2f4); + rtl8168_mdio_write(tp, 0x06, 0xd400); + rtl8168_mdio_write(tp, 0x06, 0x00bf); + rtl8168_mdio_write(tp, 0x06, 0x1c5b); + rtl8168_mdio_write(tp, 0x06, 0x023a); + rtl8168_mdio_write(tp, 0x06, 0x21fe); + rtl8168_mdio_write(tp, 0x06, 0xef96); + rtl8168_mdio_write(tp, 0x06, 0xfec6); + rtl8168_mdio_write(tp, 0x06, 0xfefd); + rtl8168_mdio_write(tp, 0x06, 0xfc05); + rtl8168_mdio_write(tp, 0x06, 0xf9e2); + rtl8168_mdio_write(tp, 0x06, 0xe0ea); + rtl8168_mdio_write(tp, 0x06, 0xe3e0); + rtl8168_mdio_write(tp, 0x06, 0xeb5a); + rtl8168_mdio_write(tp, 0x06, 0x070c); + rtl8168_mdio_write(tp, 0x06, 0x031e); + rtl8168_mdio_write(tp, 0x06, 0x20e6); + rtl8168_mdio_write(tp, 0x06, 0xe0ea); + rtl8168_mdio_write(tp, 0x06, 0xe7e0); + rtl8168_mdio_write(tp, 0x06, 0xebe0); + rtl8168_mdio_write(tp, 0x06, 0xe0fc); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0xfdfd); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xfaef); + rtl8168_mdio_write(tp, 0x06, 0x69e0); + rtl8168_mdio_write(tp, 0x06, 0x8b80); + rtl8168_mdio_write(tp, 0x06, 0xad27); + rtl8168_mdio_write(tp, 0x06, 0x22bf); + rtl8168_mdio_write(tp, 0x06, 0x47ba); + rtl8168_mdio_write(tp, 0x06, 0x0239); + rtl8168_mdio_write(tp, 0x06, 0xf4e0); + rtl8168_mdio_write(tp, 0x06, 0x8b44); + rtl8168_mdio_write(tp, 0x06, 0x1f01); + rtl8168_mdio_write(tp, 0x06, 0x9e15); + rtl8168_mdio_write(tp, 0x06, 0xe58b); + rtl8168_mdio_write(tp, 0x06, 0x44ad); + rtl8168_mdio_write(tp, 0x06, 0x2907); + rtl8168_mdio_write(tp, 0x06, 0xac28); + rtl8168_mdio_write(tp, 0x06, 0x04d1); + rtl8168_mdio_write(tp, 0x06, 0x01ae); + rtl8168_mdio_write(tp, 0x06, 0x02d1); + rtl8168_mdio_write(tp, 0x06, 0x00bf); + rtl8168_mdio_write(tp, 0x06, 0x8342); + rtl8168_mdio_write(tp, 0x06, 0x023a); + rtl8168_mdio_write(tp, 0x06, 0x21ef); + rtl8168_mdio_write(tp, 0x06, 0x96fe); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b85); + rtl8168_mdio_write(tp, 0x06, 0xad26); + rtl8168_mdio_write(tp, 0x06, 0x30e0); + rtl8168_mdio_write(tp, 0x06, 0xe036); + rtl8168_mdio_write(tp, 0x06, 0xe1e0); + rtl8168_mdio_write(tp, 0x06, 0x37e1); + rtl8168_mdio_write(tp, 0x06, 0x8b3f); + rtl8168_mdio_write(tp, 0x06, 0x1f10); + rtl8168_mdio_write(tp, 0x06, 0x9e23); + rtl8168_mdio_write(tp, 0x06, 0xe48b); + rtl8168_mdio_write(tp, 0x06, 0x3fac); + rtl8168_mdio_write(tp, 0x06, 0x200b); + rtl8168_mdio_write(tp, 0x06, 0xac21); + rtl8168_mdio_write(tp, 0x06, 0x0dac); + rtl8168_mdio_write(tp, 0x06, 0x250f); + rtl8168_mdio_write(tp, 0x06, 0xac27); + rtl8168_mdio_write(tp, 0x06, 0x11ae); + rtl8168_mdio_write(tp, 0x06, 0x1202); + rtl8168_mdio_write(tp, 0x06, 0x2cb5); + rtl8168_mdio_write(tp, 0x06, 0xae0d); + rtl8168_mdio_write(tp, 0x06, 0x0282); + rtl8168_mdio_write(tp, 0x06, 0xe7ae); + rtl8168_mdio_write(tp, 0x06, 0x0802); + rtl8168_mdio_write(tp, 0x06, 0x2cd7); + rtl8168_mdio_write(tp, 0x06, 0xae03); + rtl8168_mdio_write(tp, 0x06, 0x022c); + rtl8168_mdio_write(tp, 0x06, 0xeafc); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xfaef); + rtl8168_mdio_write(tp, 0x06, 0x6902); + rtl8168_mdio_write(tp, 0x06, 0x8304); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x14e1); + rtl8168_mdio_write(tp, 0x06, 0xe015); + rtl8168_mdio_write(tp, 0x06, 0xad26); + rtl8168_mdio_write(tp, 0x06, 0x08d1); + rtl8168_mdio_write(tp, 0x06, 0x1ebf); + rtl8168_mdio_write(tp, 0x06, 0x2d47); + rtl8168_mdio_write(tp, 0x06, 0x023a); + rtl8168_mdio_write(tp, 0x06, 0x21ef); + rtl8168_mdio_write(tp, 0x06, 0x96fe); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0x8b85); + rtl8168_mdio_write(tp, 0x06, 0xad27); + rtl8168_mdio_write(tp, 0x06, 0x2fd0); + rtl8168_mdio_write(tp, 0x06, 0x0b02); + rtl8168_mdio_write(tp, 0x06, 0x3826); + rtl8168_mdio_write(tp, 0x06, 0x5882); + rtl8168_mdio_write(tp, 0x06, 0x7882); + rtl8168_mdio_write(tp, 0x06, 0x9f24); + rtl8168_mdio_write(tp, 0x06, 0xe08b); + rtl8168_mdio_write(tp, 0x06, 0x32e1); + rtl8168_mdio_write(tp, 0x06, 0x8b33); + rtl8168_mdio_write(tp, 0x06, 0x1f10); + rtl8168_mdio_write(tp, 0x06, 0x9e1a); + rtl8168_mdio_write(tp, 0x06, 0x10e4); + rtl8168_mdio_write(tp, 0x06, 0x8b32); + rtl8168_mdio_write(tp, 0x06, 0xe0e0); + rtl8168_mdio_write(tp, 0x06, 0x28e1); + rtl8168_mdio_write(tp, 0x06, 0xe029); + rtl8168_mdio_write(tp, 0x06, 0xf72c); + rtl8168_mdio_write(tp, 0x06, 0xe4e0); + rtl8168_mdio_write(tp, 0x06, 0x28e5); + rtl8168_mdio_write(tp, 0x06, 0xe029); + rtl8168_mdio_write(tp, 0x06, 0xf62c); + rtl8168_mdio_write(tp, 0x06, 0xe4e0); + rtl8168_mdio_write(tp, 0x06, 0x28e5); + rtl8168_mdio_write(tp, 0x06, 0xe029); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0x00e1); + rtl8168_mdio_write(tp, 0x06, 0x4077); + rtl8168_mdio_write(tp, 0x06, 0xe140); + rtl8168_mdio_write(tp, 0x06, 0xbbe0); + rtl8168_mdio_write(tp, 0x06, 0x2a00); + rtl8168_mdio_write(tp, 0x05, 0xe142); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp,0x06, gphy_val); + rtl8168_mdio_write(tp, 0x05, 0xe140); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp,0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp,0x1f, 0x0005); + for (i = 0; i < 200; i++) { + udelay(100); + gphy_val = rtl8168_mdio_read(tp, 0x00); + if (gphy_val & BIT_7) + break; + } + rtl8168_mdio_write(tp,0x1f, 0x0007); + rtl8168_mdio_write(tp,0x1e, 0x0023); + gphy_val = rtl8168_mdio_read(tp, 0x17); + gphy_val |= BIT_1; + if (tp->RequiredSecLanDonglePatch) + gphy_val &= ~BIT_2; + rtl8168_mdio_write(tp,0x17, gphy_val); + rtl8168_mdio_write(tp,0x1f, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x09, 0xA20F); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0003); + rtl8168_mdio_write(tp, 0x01, 0x328A); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + rtl8168_mdio_write(tp,0x1f, 0x0000); + rtl8168_mdio_write(tp,0x00, 0x9200); +} + +static void +rtl8168_set_phy_mcu_8168g_1(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned int gphy_val; + + rtl8168_set_phy_mcu_patch_request(tp); + rtl8168_mdio_write(tp, 0x1f, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8146); + rtl8168_mdio_write(tp, 0x14, 0x2300); + rtl8168_mdio_write(tp, 0x13, 0xB820); + rtl8168_mdio_write(tp, 0x14, 0x0210); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0xB820); + rtl8168_mdio_write(tp, 0x14, 0x0290); + rtl8168_mdio_write(tp, 0x13, 0xA012); + rtl8168_mdio_write(tp, 0x14, 0x0000); + rtl8168_mdio_write(tp, 0x13, 0xA014); + rtl8168_mdio_write(tp, 0x14, 0x2c04); + rtl8168_mdio_write(tp, 0x14, 0x2c0c); + rtl8168_mdio_write(tp, 0x14, 0x2c6c); + rtl8168_mdio_write(tp, 0x14, 0x2d0d); + rtl8168_mdio_write(tp, 0x14, 0x31ce); + rtl8168_mdio_write(tp, 0x14, 0x506d); + rtl8168_mdio_write(tp, 0x14, 0xd708); + rtl8168_mdio_write(tp, 0x14, 0x3108); + rtl8168_mdio_write(tp, 0x14, 0x106d); + rtl8168_mdio_write(tp, 0x14, 0x1560); + rtl8168_mdio_write(tp, 0x14, 0x15a9); + rtl8168_mdio_write(tp, 0x14, 0x206e); + rtl8168_mdio_write(tp, 0x14, 0x175b); + rtl8168_mdio_write(tp, 0x14, 0x6062); + rtl8168_mdio_write(tp, 0x14, 0xd700); + rtl8168_mdio_write(tp, 0x14, 0x5fae); + rtl8168_mdio_write(tp, 0x14, 0xd708); + rtl8168_mdio_write(tp, 0x14, 0x3107); + rtl8168_mdio_write(tp, 0x14, 0x4c1e); + rtl8168_mdio_write(tp, 0x14, 0x4169); + rtl8168_mdio_write(tp, 0x14, 0x316a); + rtl8168_mdio_write(tp, 0x14, 0x0c19); + rtl8168_mdio_write(tp, 0x14, 0x31aa); + rtl8168_mdio_write(tp, 0x14, 0x0c19); + rtl8168_mdio_write(tp, 0x14, 0x2c1b); + rtl8168_mdio_write(tp, 0x14, 0x5e62); + rtl8168_mdio_write(tp, 0x14, 0x26b5); + rtl8168_mdio_write(tp, 0x14, 0x31ab); + rtl8168_mdio_write(tp, 0x14, 0x5c1e); + rtl8168_mdio_write(tp, 0x14, 0x2c0c); + rtl8168_mdio_write(tp, 0x14, 0xc040); + rtl8168_mdio_write(tp, 0x14, 0x8808); + rtl8168_mdio_write(tp, 0x14, 0xc520); + rtl8168_mdio_write(tp, 0x14, 0xc421); + rtl8168_mdio_write(tp, 0x14, 0xd05a); + rtl8168_mdio_write(tp, 0x14, 0xd19a); + rtl8168_mdio_write(tp, 0x14, 0xd709); + rtl8168_mdio_write(tp, 0x14, 0x608f); + rtl8168_mdio_write(tp, 0x14, 0xd06b); + rtl8168_mdio_write(tp, 0x14, 0xd18a); + rtl8168_mdio_write(tp, 0x14, 0x2c2c); + rtl8168_mdio_write(tp, 0x14, 0xd0be); + rtl8168_mdio_write(tp, 0x14, 0xd188); + rtl8168_mdio_write(tp, 0x14, 0x2c2c); + rtl8168_mdio_write(tp, 0x14, 0xd708); + rtl8168_mdio_write(tp, 0x14, 0x4072); + rtl8168_mdio_write(tp, 0x14, 0xc104); + rtl8168_mdio_write(tp, 0x14, 0x2c3e); + rtl8168_mdio_write(tp, 0x14, 0x4076); + rtl8168_mdio_write(tp, 0x14, 0xc110); + rtl8168_mdio_write(tp, 0x14, 0x2c3e); + rtl8168_mdio_write(tp, 0x14, 0x4071); + rtl8168_mdio_write(tp, 0x14, 0xc102); + rtl8168_mdio_write(tp, 0x14, 0x2c3e); + rtl8168_mdio_write(tp, 0x14, 0x4070); + rtl8168_mdio_write(tp, 0x14, 0xc101); + rtl8168_mdio_write(tp, 0x14, 0x2c3e); + rtl8168_mdio_write(tp, 0x14, 0x175b); + rtl8168_mdio_write(tp, 0x14, 0xd709); + rtl8168_mdio_write(tp, 0x14, 0x3390); + rtl8168_mdio_write(tp, 0x14, 0x5c39); + rtl8168_mdio_write(tp, 0x14, 0x2c4e); + rtl8168_mdio_write(tp, 0x14, 0x175b); + rtl8168_mdio_write(tp, 0x14, 0xd708); + rtl8168_mdio_write(tp, 0x14, 0x6193); + rtl8168_mdio_write(tp, 0x14, 0xd709); + rtl8168_mdio_write(tp, 0x14, 0x5f9d); + rtl8168_mdio_write(tp, 0x14, 0x408b); + rtl8168_mdio_write(tp, 0x14, 0xd71e); + rtl8168_mdio_write(tp, 0x14, 0x6042); + rtl8168_mdio_write(tp, 0x14, 0xb401); + rtl8168_mdio_write(tp, 0x14, 0x175b); + rtl8168_mdio_write(tp, 0x14, 0xd708); + rtl8168_mdio_write(tp, 0x14, 0x6073); + rtl8168_mdio_write(tp, 0x14, 0x5fbc); + rtl8168_mdio_write(tp, 0x14, 0x2c4d); + rtl8168_mdio_write(tp, 0x14, 0x26ed); + rtl8168_mdio_write(tp, 0x14, 0xb280); + rtl8168_mdio_write(tp, 0x14, 0xa841); + rtl8168_mdio_write(tp, 0x14, 0x9420); + rtl8168_mdio_write(tp, 0x14, 0x8710); + rtl8168_mdio_write(tp, 0x14, 0xd709); + rtl8168_mdio_write(tp, 0x14, 0x42ec); + rtl8168_mdio_write(tp, 0x14, 0x606d); + rtl8168_mdio_write(tp, 0x14, 0xd207); + rtl8168_mdio_write(tp, 0x14, 0x2c57); + rtl8168_mdio_write(tp, 0x14, 0xd203); + rtl8168_mdio_write(tp, 0x14, 0x33ff); + rtl8168_mdio_write(tp, 0x14, 0x563b); + rtl8168_mdio_write(tp, 0x14, 0x3275); + rtl8168_mdio_write(tp, 0x14, 0x7c5e); + rtl8168_mdio_write(tp, 0x14, 0xb240); + rtl8168_mdio_write(tp, 0x14, 0xb402); + rtl8168_mdio_write(tp, 0x14, 0x263b); + rtl8168_mdio_write(tp, 0x14, 0x6096); + rtl8168_mdio_write(tp, 0x14, 0xb240); + rtl8168_mdio_write(tp, 0x14, 0xb406); + rtl8168_mdio_write(tp, 0x14, 0x263b); + rtl8168_mdio_write(tp, 0x14, 0x31d7); + rtl8168_mdio_write(tp, 0x14, 0x7c67); + rtl8168_mdio_write(tp, 0x14, 0xb240); + rtl8168_mdio_write(tp, 0x14, 0xb40e); + rtl8168_mdio_write(tp, 0x14, 0x263b); + rtl8168_mdio_write(tp, 0x14, 0xb410); + rtl8168_mdio_write(tp, 0x14, 0x8802); + rtl8168_mdio_write(tp, 0x14, 0xb240); + rtl8168_mdio_write(tp, 0x14, 0x940e); + rtl8168_mdio_write(tp, 0x14, 0x263b); + rtl8168_mdio_write(tp, 0x14, 0xba04); + rtl8168_mdio_write(tp, 0x14, 0x1cd6); + rtl8168_mdio_write(tp, 0x14, 0xa902); + rtl8168_mdio_write(tp, 0x14, 0xd711); + rtl8168_mdio_write(tp, 0x14, 0x4045); + rtl8168_mdio_write(tp, 0x14, 0xa980); + rtl8168_mdio_write(tp, 0x14, 0x3003); + rtl8168_mdio_write(tp, 0x14, 0x59b1); + rtl8168_mdio_write(tp, 0x14, 0xa540); + rtl8168_mdio_write(tp, 0x14, 0xa601); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x4043); + rtl8168_mdio_write(tp, 0x14, 0xa910); + rtl8168_mdio_write(tp, 0x14, 0xd711); + rtl8168_mdio_write(tp, 0x14, 0x60a0); + rtl8168_mdio_write(tp, 0x14, 0xca33); + rtl8168_mdio_write(tp, 0x14, 0xcb33); + rtl8168_mdio_write(tp, 0x14, 0xa941); + rtl8168_mdio_write(tp, 0x14, 0x2c82); + rtl8168_mdio_write(tp, 0x14, 0xcaff); + rtl8168_mdio_write(tp, 0x14, 0xcbff); + rtl8168_mdio_write(tp, 0x14, 0xa921); + rtl8168_mdio_write(tp, 0x14, 0xce02); + rtl8168_mdio_write(tp, 0x14, 0xe070); + rtl8168_mdio_write(tp, 0x14, 0x0f10); + rtl8168_mdio_write(tp, 0x14, 0xaf01); + rtl8168_mdio_write(tp, 0x14, 0x8f01); + rtl8168_mdio_write(tp, 0x14, 0x1766); + rtl8168_mdio_write(tp, 0x14, 0x8e02); + rtl8168_mdio_write(tp, 0x14, 0x1787); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x609c); + rtl8168_mdio_write(tp, 0x14, 0xd71e); + rtl8168_mdio_write(tp, 0x14, 0x7fa4); + rtl8168_mdio_write(tp, 0x14, 0x2cd4); + rtl8168_mdio_write(tp, 0x14, 0x1ce9); + rtl8168_mdio_write(tp, 0x14, 0xce04); + rtl8168_mdio_write(tp, 0x14, 0xe070); + rtl8168_mdio_write(tp, 0x14, 0x0f20); + rtl8168_mdio_write(tp, 0x14, 0xaf01); + rtl8168_mdio_write(tp, 0x14, 0x8f01); + rtl8168_mdio_write(tp, 0x14, 0x1766); + rtl8168_mdio_write(tp, 0x14, 0x8e04); + rtl8168_mdio_write(tp, 0x14, 0x6044); + rtl8168_mdio_write(tp, 0x14, 0x2cd4); + rtl8168_mdio_write(tp, 0x14, 0xa520); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x4043); + rtl8168_mdio_write(tp, 0x14, 0x2cc1); + rtl8168_mdio_write(tp, 0x14, 0xe00f); + rtl8168_mdio_write(tp, 0x14, 0x0501); + rtl8168_mdio_write(tp, 0x14, 0x1cef); + rtl8168_mdio_write(tp, 0x14, 0xb801); + rtl8168_mdio_write(tp, 0x14, 0xd71e); + rtl8168_mdio_write(tp, 0x14, 0x4060); + rtl8168_mdio_write(tp, 0x14, 0x7fc4); + rtl8168_mdio_write(tp, 0x14, 0x2cd4); + rtl8168_mdio_write(tp, 0x14, 0x1cf5); + rtl8168_mdio_write(tp, 0x14, 0xe00f); + rtl8168_mdio_write(tp, 0x14, 0x0502); + rtl8168_mdio_write(tp, 0x14, 0x1cef); + rtl8168_mdio_write(tp, 0x14, 0xb802); + rtl8168_mdio_write(tp, 0x14, 0xd71e); + rtl8168_mdio_write(tp, 0x14, 0x4061); + rtl8168_mdio_write(tp, 0x14, 0x7fc4); + rtl8168_mdio_write(tp, 0x14, 0x2cd4); + rtl8168_mdio_write(tp, 0x14, 0x1cf5); + rtl8168_mdio_write(tp, 0x14, 0xe00f); + rtl8168_mdio_write(tp, 0x14, 0x0504); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x6099); + rtl8168_mdio_write(tp, 0x14, 0xd71e); + rtl8168_mdio_write(tp, 0x14, 0x7fa4); + rtl8168_mdio_write(tp, 0x14, 0x2cd4); + rtl8168_mdio_write(tp, 0x14, 0xc17f); + rtl8168_mdio_write(tp, 0x14, 0xc200); + rtl8168_mdio_write(tp, 0x14, 0xc43f); + rtl8168_mdio_write(tp, 0x14, 0xcc03); + rtl8168_mdio_write(tp, 0x14, 0xa701); + rtl8168_mdio_write(tp, 0x14, 0xa510); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x4018); + rtl8168_mdio_write(tp, 0x14, 0x9910); + rtl8168_mdio_write(tp, 0x14, 0x8510); + rtl8168_mdio_write(tp, 0x14, 0x2860); + rtl8168_mdio_write(tp, 0x14, 0xe00f); + rtl8168_mdio_write(tp, 0x14, 0x0504); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x6099); + rtl8168_mdio_write(tp, 0x14, 0xd71e); + rtl8168_mdio_write(tp, 0x14, 0x7fa4); + rtl8168_mdio_write(tp, 0x14, 0x2cd4); + rtl8168_mdio_write(tp, 0x14, 0xa608); + rtl8168_mdio_write(tp, 0x14, 0xc17d); + rtl8168_mdio_write(tp, 0x14, 0xc200); + rtl8168_mdio_write(tp, 0x14, 0xc43f); + rtl8168_mdio_write(tp, 0x14, 0xcc03); + rtl8168_mdio_write(tp, 0x14, 0xa701); + rtl8168_mdio_write(tp, 0x14, 0xa510); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x4018); + rtl8168_mdio_write(tp, 0x14, 0x9910); + rtl8168_mdio_write(tp, 0x14, 0x8510); + rtl8168_mdio_write(tp, 0x14, 0x2926); + rtl8168_mdio_write(tp, 0x14, 0x1792); + rtl8168_mdio_write(tp, 0x14, 0x27db); + rtl8168_mdio_write(tp, 0x14, 0xc000); + rtl8168_mdio_write(tp, 0x14, 0xc100); + rtl8168_mdio_write(tp, 0x14, 0xc200); + rtl8168_mdio_write(tp, 0x14, 0xc300); + rtl8168_mdio_write(tp, 0x14, 0xc400); + rtl8168_mdio_write(tp, 0x14, 0xc500); + rtl8168_mdio_write(tp, 0x14, 0xc600); + rtl8168_mdio_write(tp, 0x14, 0xc7c1); + rtl8168_mdio_write(tp, 0x14, 0xc800); + rtl8168_mdio_write(tp, 0x14, 0xcc00); + rtl8168_mdio_write(tp, 0x14, 0x0800); + rtl8168_mdio_write(tp, 0x14, 0xca0f); + rtl8168_mdio_write(tp, 0x14, 0xcbff); + rtl8168_mdio_write(tp, 0x14, 0xa901); + rtl8168_mdio_write(tp, 0x14, 0x8902); + rtl8168_mdio_write(tp, 0x14, 0xc900); + rtl8168_mdio_write(tp, 0x14, 0xca00); + rtl8168_mdio_write(tp, 0x14, 0xcb00); + rtl8168_mdio_write(tp, 0x14, 0x0800); + rtl8168_mdio_write(tp, 0x14, 0xb804); + rtl8168_mdio_write(tp, 0x14, 0x0800); + rtl8168_mdio_write(tp, 0x14, 0xd71e); + rtl8168_mdio_write(tp, 0x14, 0x6044); + rtl8168_mdio_write(tp, 0x14, 0x9804); + rtl8168_mdio_write(tp, 0x14, 0x0800); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x6099); + rtl8168_mdio_write(tp, 0x14, 0xd71e); + rtl8168_mdio_write(tp, 0x14, 0x7fa4); + rtl8168_mdio_write(tp, 0x14, 0x2cd4); + rtl8168_mdio_write(tp, 0x14, 0x0800); + rtl8168_mdio_write(tp, 0x14, 0xa510); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x6098); + rtl8168_mdio_write(tp, 0x14, 0xd71e); + rtl8168_mdio_write(tp, 0x14, 0x7fa4); + rtl8168_mdio_write(tp, 0x14, 0x2cd4); + rtl8168_mdio_write(tp, 0x14, 0x8510); + rtl8168_mdio_write(tp, 0x14, 0x0800); + rtl8168_mdio_write(tp, 0x14, 0xd711); + rtl8168_mdio_write(tp, 0x14, 0x3003); + rtl8168_mdio_write(tp, 0x14, 0x1d01); + rtl8168_mdio_write(tp, 0x14, 0x2d0b); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x60be); + rtl8168_mdio_write(tp, 0x14, 0xe060); + rtl8168_mdio_write(tp, 0x14, 0x0920); + rtl8168_mdio_write(tp, 0x14, 0x1cd6); + rtl8168_mdio_write(tp, 0x14, 0x2c89); + rtl8168_mdio_write(tp, 0x14, 0xd71e); + rtl8168_mdio_write(tp, 0x14, 0x3063); + rtl8168_mdio_write(tp, 0x14, 0x1948); + rtl8168_mdio_write(tp, 0x14, 0x288a); + rtl8168_mdio_write(tp, 0x14, 0x1cd6); + rtl8168_mdio_write(tp, 0x14, 0x29bd); + rtl8168_mdio_write(tp, 0x14, 0xa802); + rtl8168_mdio_write(tp, 0x14, 0xa303); + rtl8168_mdio_write(tp, 0x14, 0x843f); + rtl8168_mdio_write(tp, 0x14, 0x81ff); + rtl8168_mdio_write(tp, 0x14, 0x8208); + rtl8168_mdio_write(tp, 0x14, 0xa201); + rtl8168_mdio_write(tp, 0x14, 0xc001); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x30a0); + rtl8168_mdio_write(tp, 0x14, 0x0d1c); + rtl8168_mdio_write(tp, 0x14, 0x30a0); + rtl8168_mdio_write(tp, 0x14, 0x3d13); + rtl8168_mdio_write(tp, 0x14, 0xd71e); + rtl8168_mdio_write(tp, 0x14, 0x7f4c); + rtl8168_mdio_write(tp, 0x14, 0x2ab6); + rtl8168_mdio_write(tp, 0x14, 0xe003); + rtl8168_mdio_write(tp, 0x14, 0x0202); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x6090); + rtl8168_mdio_write(tp, 0x14, 0xd71e); + rtl8168_mdio_write(tp, 0x14, 0x7fac); + rtl8168_mdio_write(tp, 0x14, 0x2ab6); + rtl8168_mdio_write(tp, 0x14, 0xa20c); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x6091); + rtl8168_mdio_write(tp, 0x14, 0xd71e); + rtl8168_mdio_write(tp, 0x14, 0x7fac); + rtl8168_mdio_write(tp, 0x14, 0x2ab6); + rtl8168_mdio_write(tp, 0x14, 0x820e); + rtl8168_mdio_write(tp, 0x14, 0xa3e0); + rtl8168_mdio_write(tp, 0x14, 0xa520); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x609d); + rtl8168_mdio_write(tp, 0x14, 0xd71e); + rtl8168_mdio_write(tp, 0x14, 0x7fac); + rtl8168_mdio_write(tp, 0x14, 0x2ab6); + rtl8168_mdio_write(tp, 0x14, 0x8520); + rtl8168_mdio_write(tp, 0x14, 0x6703); + rtl8168_mdio_write(tp, 0x14, 0x2d34); + rtl8168_mdio_write(tp, 0x14, 0xa13e); + rtl8168_mdio_write(tp, 0x14, 0xc001); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x4000); + rtl8168_mdio_write(tp, 0x14, 0x6046); + rtl8168_mdio_write(tp, 0x14, 0x2d0d); + rtl8168_mdio_write(tp, 0x14, 0xa43f); + rtl8168_mdio_write(tp, 0x14, 0xa101); + rtl8168_mdio_write(tp, 0x14, 0xc020); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x3121); + rtl8168_mdio_write(tp, 0x14, 0x0d45); + rtl8168_mdio_write(tp, 0x14, 0x30c0); + rtl8168_mdio_write(tp, 0x14, 0x3d0d); + rtl8168_mdio_write(tp, 0x14, 0xd71e); + rtl8168_mdio_write(tp, 0x14, 0x7f4c); + rtl8168_mdio_write(tp, 0x14, 0x2ab6); + rtl8168_mdio_write(tp, 0x14, 0xa540); + rtl8168_mdio_write(tp, 0x14, 0xc001); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x4001); + rtl8168_mdio_write(tp, 0x14, 0xe00f); + rtl8168_mdio_write(tp, 0x14, 0x0501); + rtl8168_mdio_write(tp, 0x14, 0x1dac); + rtl8168_mdio_write(tp, 0x14, 0xc1c4); + rtl8168_mdio_write(tp, 0x14, 0xa268); + rtl8168_mdio_write(tp, 0x14, 0xa303); + rtl8168_mdio_write(tp, 0x14, 0x8420); + rtl8168_mdio_write(tp, 0x14, 0xe00f); + rtl8168_mdio_write(tp, 0x14, 0x0502); + rtl8168_mdio_write(tp, 0x14, 0x1dac); + rtl8168_mdio_write(tp, 0x14, 0xc002); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x4000); + rtl8168_mdio_write(tp, 0x14, 0x8208); + rtl8168_mdio_write(tp, 0x14, 0x8410); + rtl8168_mdio_write(tp, 0x14, 0xa121); + rtl8168_mdio_write(tp, 0x14, 0xc002); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x4000); + rtl8168_mdio_write(tp, 0x14, 0x8120); + rtl8168_mdio_write(tp, 0x14, 0x8180); + rtl8168_mdio_write(tp, 0x14, 0x1d97); + rtl8168_mdio_write(tp, 0x14, 0xa180); + rtl8168_mdio_write(tp, 0x14, 0xa13a); + rtl8168_mdio_write(tp, 0x14, 0x8240); + rtl8168_mdio_write(tp, 0x14, 0xa430); + rtl8168_mdio_write(tp, 0x14, 0xc010); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x30e1); + rtl8168_mdio_write(tp, 0x14, 0x0abc); + rtl8168_mdio_write(tp, 0x14, 0xd71e); + rtl8168_mdio_write(tp, 0x14, 0x7f8c); + rtl8168_mdio_write(tp, 0x14, 0x2ab6); + rtl8168_mdio_write(tp, 0x14, 0xa480); + rtl8168_mdio_write(tp, 0x14, 0xa230); + rtl8168_mdio_write(tp, 0x14, 0xa303); + rtl8168_mdio_write(tp, 0x14, 0xc001); + rtl8168_mdio_write(tp, 0x14, 0xd70c); + rtl8168_mdio_write(tp, 0x14, 0x4124); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x6120); + rtl8168_mdio_write(tp, 0x14, 0xd711); + rtl8168_mdio_write(tp, 0x14, 0x3128); + rtl8168_mdio_write(tp, 0x14, 0x3d76); + rtl8168_mdio_write(tp, 0x14, 0x2d70); + rtl8168_mdio_write(tp, 0x14, 0xa801); + rtl8168_mdio_write(tp, 0x14, 0x2d6c); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x4000); + rtl8168_mdio_write(tp, 0x14, 0xe018); + rtl8168_mdio_write(tp, 0x14, 0x0208); + rtl8168_mdio_write(tp, 0x14, 0xa1f8); + rtl8168_mdio_write(tp, 0x14, 0x8480); + rtl8168_mdio_write(tp, 0x14, 0xc004); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x4000); + rtl8168_mdio_write(tp, 0x14, 0x6046); + rtl8168_mdio_write(tp, 0x14, 0x2d0d); + rtl8168_mdio_write(tp, 0x14, 0xa43f); + rtl8168_mdio_write(tp, 0x14, 0xa105); + rtl8168_mdio_write(tp, 0x14, 0x8228); + rtl8168_mdio_write(tp, 0x14, 0xc004); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x4000); + rtl8168_mdio_write(tp, 0x14, 0x81bc); + rtl8168_mdio_write(tp, 0x14, 0xa220); + rtl8168_mdio_write(tp, 0x14, 0x1d97); + rtl8168_mdio_write(tp, 0x14, 0x8220); + rtl8168_mdio_write(tp, 0x14, 0xa1bc); + rtl8168_mdio_write(tp, 0x14, 0xc040); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x30e1); + rtl8168_mdio_write(tp, 0x14, 0x0abc); + rtl8168_mdio_write(tp, 0x14, 0x30e1); + rtl8168_mdio_write(tp, 0x14, 0x3d0d); + rtl8168_mdio_write(tp, 0x14, 0xd71e); + rtl8168_mdio_write(tp, 0x14, 0x7f4c); + rtl8168_mdio_write(tp, 0x14, 0x2ab6); + rtl8168_mdio_write(tp, 0x14, 0xa802); + rtl8168_mdio_write(tp, 0x14, 0xd70c); + rtl8168_mdio_write(tp, 0x14, 0x4244); + rtl8168_mdio_write(tp, 0x14, 0xa301); + rtl8168_mdio_write(tp, 0x14, 0xc004); + rtl8168_mdio_write(tp, 0x14, 0xd711); + rtl8168_mdio_write(tp, 0x14, 0x3128); + rtl8168_mdio_write(tp, 0x14, 0x3da5); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x5f80); + rtl8168_mdio_write(tp, 0x14, 0xd711); + rtl8168_mdio_write(tp, 0x14, 0x3109); + rtl8168_mdio_write(tp, 0x14, 0x3da7); + rtl8168_mdio_write(tp, 0x14, 0x2dab); + rtl8168_mdio_write(tp, 0x14, 0xa801); + rtl8168_mdio_write(tp, 0x14, 0x2d9a); + rtl8168_mdio_write(tp, 0x14, 0xa802); + rtl8168_mdio_write(tp, 0x14, 0xc004); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x4000); + rtl8168_mdio_write(tp, 0x14, 0x0800); + rtl8168_mdio_write(tp, 0x14, 0xa510); + rtl8168_mdio_write(tp, 0x14, 0xd710); + rtl8168_mdio_write(tp, 0x14, 0x609a); + rtl8168_mdio_write(tp, 0x14, 0xd71e); + rtl8168_mdio_write(tp, 0x14, 0x7fac); + rtl8168_mdio_write(tp, 0x14, 0x2ab6); + rtl8168_mdio_write(tp, 0x14, 0x8510); + rtl8168_mdio_write(tp, 0x14, 0x0800); + rtl8168_mdio_write(tp, 0x13, 0xA01A); + rtl8168_mdio_write(tp, 0x14, 0x0000); + rtl8168_mdio_write(tp, 0x13, 0xA006); + rtl8168_mdio_write(tp, 0x14, 0x0ad6); + rtl8168_mdio_write(tp, 0x13, 0xA004); + rtl8168_mdio_write(tp, 0x14, 0x07f5); + rtl8168_mdio_write(tp, 0x13, 0xA002); + rtl8168_mdio_write(tp, 0x14, 0x06a9); + rtl8168_mdio_write(tp, 0x13, 0xA000); + rtl8168_mdio_write(tp, 0x14, 0xf069); + rtl8168_mdio_write(tp, 0x13, 0xB820); + rtl8168_mdio_write(tp, 0x14, 0x0210); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x83a0); + rtl8168_mdio_write(tp, 0x14, 0xaf83); + rtl8168_mdio_write(tp, 0x14, 0xacaf); + rtl8168_mdio_write(tp, 0x14, 0x83b8); + rtl8168_mdio_write(tp, 0x14, 0xaf83); + rtl8168_mdio_write(tp, 0x14, 0xcdaf); + rtl8168_mdio_write(tp, 0x14, 0x83d3); + rtl8168_mdio_write(tp, 0x14, 0x0204); + rtl8168_mdio_write(tp, 0x14, 0x9a02); + rtl8168_mdio_write(tp, 0x14, 0x09a9); + rtl8168_mdio_write(tp, 0x14, 0x0284); + rtl8168_mdio_write(tp, 0x14, 0x61af); + rtl8168_mdio_write(tp, 0x14, 0x02fc); + rtl8168_mdio_write(tp, 0x14, 0xad20); + rtl8168_mdio_write(tp, 0x14, 0x0302); + rtl8168_mdio_write(tp, 0x14, 0x867c); + rtl8168_mdio_write(tp, 0x14, 0xad21); + rtl8168_mdio_write(tp, 0x14, 0x0302); + rtl8168_mdio_write(tp, 0x14, 0x85c9); + rtl8168_mdio_write(tp, 0x14, 0xad22); + rtl8168_mdio_write(tp, 0x14, 0x0302); + rtl8168_mdio_write(tp, 0x14, 0x1bc0); + rtl8168_mdio_write(tp, 0x14, 0xaf17); + rtl8168_mdio_write(tp, 0x14, 0xe302); + rtl8168_mdio_write(tp, 0x14, 0x8703); + rtl8168_mdio_write(tp, 0x14, 0xaf18); + rtl8168_mdio_write(tp, 0x14, 0x6201); + rtl8168_mdio_write(tp, 0x14, 0x06e0); + rtl8168_mdio_write(tp, 0x14, 0x8148); + rtl8168_mdio_write(tp, 0x14, 0xaf3c); + rtl8168_mdio_write(tp, 0x14, 0x69f8); + rtl8168_mdio_write(tp, 0x14, 0xf9fa); + rtl8168_mdio_write(tp, 0x14, 0xef69); + rtl8168_mdio_write(tp, 0x14, 0xee80); + rtl8168_mdio_write(tp, 0x14, 0x10f7); + rtl8168_mdio_write(tp, 0x14, 0xee80); + rtl8168_mdio_write(tp, 0x14, 0x131f); + rtl8168_mdio_write(tp, 0x14, 0xd104); + rtl8168_mdio_write(tp, 0x14, 0xbf87); + rtl8168_mdio_write(tp, 0x14, 0xf302); + rtl8168_mdio_write(tp, 0x14, 0x4259); + rtl8168_mdio_write(tp, 0x14, 0x0287); + rtl8168_mdio_write(tp, 0x14, 0x88bf); + rtl8168_mdio_write(tp, 0x14, 0x87cf); + rtl8168_mdio_write(tp, 0x14, 0xd7b8); + rtl8168_mdio_write(tp, 0x14, 0x22d0); + rtl8168_mdio_write(tp, 0x14, 0x0c02); + rtl8168_mdio_write(tp, 0x14, 0x4252); + rtl8168_mdio_write(tp, 0x14, 0xee80); + rtl8168_mdio_write(tp, 0x14, 0xcda0); + rtl8168_mdio_write(tp, 0x14, 0xee80); + rtl8168_mdio_write(tp, 0x14, 0xce8b); + rtl8168_mdio_write(tp, 0x14, 0xee80); + rtl8168_mdio_write(tp, 0x14, 0xd1f5); + rtl8168_mdio_write(tp, 0x14, 0xee80); + rtl8168_mdio_write(tp, 0x14, 0xd2a9); + rtl8168_mdio_write(tp, 0x14, 0xee80); + rtl8168_mdio_write(tp, 0x14, 0xd30a); + rtl8168_mdio_write(tp, 0x14, 0xee80); + rtl8168_mdio_write(tp, 0x14, 0xf010); + rtl8168_mdio_write(tp, 0x14, 0xee80); + rtl8168_mdio_write(tp, 0x14, 0xf38f); + rtl8168_mdio_write(tp, 0x14, 0xee81); + rtl8168_mdio_write(tp, 0x14, 0x011e); + rtl8168_mdio_write(tp, 0x14, 0xee81); + rtl8168_mdio_write(tp, 0x14, 0x0b4a); + rtl8168_mdio_write(tp, 0x14, 0xee81); + rtl8168_mdio_write(tp, 0x14, 0x0c7c); + rtl8168_mdio_write(tp, 0x14, 0xee81); + rtl8168_mdio_write(tp, 0x14, 0x127f); + rtl8168_mdio_write(tp, 0x14, 0xd100); + rtl8168_mdio_write(tp, 0x14, 0x0210); + rtl8168_mdio_write(tp, 0x14, 0xb5ee); + rtl8168_mdio_write(tp, 0x14, 0x8088); + rtl8168_mdio_write(tp, 0x14, 0xa4ee); + rtl8168_mdio_write(tp, 0x14, 0x8089); + rtl8168_mdio_write(tp, 0x14, 0x44ee); + rtl8168_mdio_write(tp, 0x14, 0x809a); + rtl8168_mdio_write(tp, 0x14, 0xa4ee); + rtl8168_mdio_write(tp, 0x14, 0x809b); + rtl8168_mdio_write(tp, 0x14, 0x44ee); + rtl8168_mdio_write(tp, 0x14, 0x809c); + rtl8168_mdio_write(tp, 0x14, 0xa7ee); + rtl8168_mdio_write(tp, 0x14, 0x80a5); + rtl8168_mdio_write(tp, 0x14, 0xa7d2); + rtl8168_mdio_write(tp, 0x14, 0x0002); + rtl8168_mdio_write(tp, 0x14, 0x0e66); + rtl8168_mdio_write(tp, 0x14, 0x0285); + rtl8168_mdio_write(tp, 0x14, 0xc0ee); + rtl8168_mdio_write(tp, 0x14, 0x87fc); + rtl8168_mdio_write(tp, 0x14, 0x00e0); + rtl8168_mdio_write(tp, 0x14, 0x8245); + rtl8168_mdio_write(tp, 0x14, 0xf622); + rtl8168_mdio_write(tp, 0x14, 0xe482); + rtl8168_mdio_write(tp, 0x14, 0x45ef); + rtl8168_mdio_write(tp, 0x14, 0x96fe); + rtl8168_mdio_write(tp, 0x14, 0xfdfc); + rtl8168_mdio_write(tp, 0x14, 0x0402); + rtl8168_mdio_write(tp, 0x14, 0x847a); + rtl8168_mdio_write(tp, 0x14, 0x0284); + rtl8168_mdio_write(tp, 0x14, 0xb302); + rtl8168_mdio_write(tp, 0x14, 0x0cab); + rtl8168_mdio_write(tp, 0x14, 0x020c); + rtl8168_mdio_write(tp, 0x14, 0xc402); + rtl8168_mdio_write(tp, 0x14, 0x0cef); + rtl8168_mdio_write(tp, 0x14, 0x020d); + rtl8168_mdio_write(tp, 0x14, 0x0802); + rtl8168_mdio_write(tp, 0x14, 0x0d33); + rtl8168_mdio_write(tp, 0x14, 0x020c); + rtl8168_mdio_write(tp, 0x14, 0x3d04); + rtl8168_mdio_write(tp, 0x14, 0xf8fa); + rtl8168_mdio_write(tp, 0x14, 0xef69); + rtl8168_mdio_write(tp, 0x14, 0xe182); + rtl8168_mdio_write(tp, 0x14, 0x2fac); + rtl8168_mdio_write(tp, 0x14, 0x291a); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x24ac); + rtl8168_mdio_write(tp, 0x14, 0x2102); + rtl8168_mdio_write(tp, 0x14, 0xae22); + rtl8168_mdio_write(tp, 0x14, 0x0210); + rtl8168_mdio_write(tp, 0x14, 0x57f6); + rtl8168_mdio_write(tp, 0x14, 0x21e4); + rtl8168_mdio_write(tp, 0x14, 0x8224); + rtl8168_mdio_write(tp, 0x14, 0xd101); + rtl8168_mdio_write(tp, 0x14, 0xbf44); + rtl8168_mdio_write(tp, 0x14, 0xd202); + rtl8168_mdio_write(tp, 0x14, 0x4259); + rtl8168_mdio_write(tp, 0x14, 0xae10); + rtl8168_mdio_write(tp, 0x14, 0x0212); + rtl8168_mdio_write(tp, 0x14, 0x4cf6); + rtl8168_mdio_write(tp, 0x14, 0x29e5); + rtl8168_mdio_write(tp, 0x14, 0x822f); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x24f6); + rtl8168_mdio_write(tp, 0x14, 0x21e4); + rtl8168_mdio_write(tp, 0x14, 0x8224); + rtl8168_mdio_write(tp, 0x14, 0xef96); + rtl8168_mdio_write(tp, 0x14, 0xfefc); + rtl8168_mdio_write(tp, 0x14, 0x04f8); + rtl8168_mdio_write(tp, 0x14, 0xe182); + rtl8168_mdio_write(tp, 0x14, 0x2fac); + rtl8168_mdio_write(tp, 0x14, 0x2a18); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x24ac); + rtl8168_mdio_write(tp, 0x14, 0x2202); + rtl8168_mdio_write(tp, 0x14, 0xae26); + rtl8168_mdio_write(tp, 0x14, 0x0284); + rtl8168_mdio_write(tp, 0x14, 0xf802); + rtl8168_mdio_write(tp, 0x14, 0x8565); + rtl8168_mdio_write(tp, 0x14, 0xd101); + rtl8168_mdio_write(tp, 0x14, 0xbf44); + rtl8168_mdio_write(tp, 0x14, 0xd502); + rtl8168_mdio_write(tp, 0x14, 0x4259); + rtl8168_mdio_write(tp, 0x14, 0xae0e); + rtl8168_mdio_write(tp, 0x14, 0x0284); + rtl8168_mdio_write(tp, 0x14, 0xea02); + rtl8168_mdio_write(tp, 0x14, 0x85a9); + rtl8168_mdio_write(tp, 0x14, 0xe182); + rtl8168_mdio_write(tp, 0x14, 0x2ff6); + rtl8168_mdio_write(tp, 0x14, 0x2ae5); + rtl8168_mdio_write(tp, 0x14, 0x822f); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x24f6); + rtl8168_mdio_write(tp, 0x14, 0x22e4); + rtl8168_mdio_write(tp, 0x14, 0x8224); + rtl8168_mdio_write(tp, 0x14, 0xfc04); + rtl8168_mdio_write(tp, 0x14, 0xf9e2); + rtl8168_mdio_write(tp, 0x14, 0x8011); + rtl8168_mdio_write(tp, 0x14, 0xad31); + rtl8168_mdio_write(tp, 0x14, 0x05d2); + rtl8168_mdio_write(tp, 0x14, 0x0002); + rtl8168_mdio_write(tp, 0x14, 0x0e66); + rtl8168_mdio_write(tp, 0x14, 0xfd04); + rtl8168_mdio_write(tp, 0x14, 0xf8f9); + rtl8168_mdio_write(tp, 0x14, 0xfaef); + rtl8168_mdio_write(tp, 0x14, 0x69e0); + rtl8168_mdio_write(tp, 0x14, 0x8011); + rtl8168_mdio_write(tp, 0x14, 0xad21); + rtl8168_mdio_write(tp, 0x14, 0x5cbf); + rtl8168_mdio_write(tp, 0x14, 0x43be); + rtl8168_mdio_write(tp, 0x14, 0x0242); + rtl8168_mdio_write(tp, 0x14, 0x97ac); + rtl8168_mdio_write(tp, 0x14, 0x281b); + rtl8168_mdio_write(tp, 0x14, 0xbf43); + rtl8168_mdio_write(tp, 0x14, 0xc102); + rtl8168_mdio_write(tp, 0x14, 0x4297); + rtl8168_mdio_write(tp, 0x14, 0xac28); + rtl8168_mdio_write(tp, 0x14, 0x12bf); + rtl8168_mdio_write(tp, 0x14, 0x43c7); + rtl8168_mdio_write(tp, 0x14, 0x0242); + rtl8168_mdio_write(tp, 0x14, 0x97ac); + rtl8168_mdio_write(tp, 0x14, 0x2804); + rtl8168_mdio_write(tp, 0x14, 0xd300); + rtl8168_mdio_write(tp, 0x14, 0xae07); + rtl8168_mdio_write(tp, 0x14, 0xd306); + rtl8168_mdio_write(tp, 0x14, 0xaf85); + rtl8168_mdio_write(tp, 0x14, 0x56d3); + rtl8168_mdio_write(tp, 0x14, 0x03e0); + rtl8168_mdio_write(tp, 0x14, 0x8011); + rtl8168_mdio_write(tp, 0x14, 0xad26); + rtl8168_mdio_write(tp, 0x14, 0x25bf); + rtl8168_mdio_write(tp, 0x14, 0x4559); + rtl8168_mdio_write(tp, 0x14, 0x0242); + rtl8168_mdio_write(tp, 0x14, 0x97e2); + rtl8168_mdio_write(tp, 0x14, 0x8073); + rtl8168_mdio_write(tp, 0x14, 0x0d21); + rtl8168_mdio_write(tp, 0x14, 0xf637); + rtl8168_mdio_write(tp, 0x14, 0x0d11); + rtl8168_mdio_write(tp, 0x14, 0xf62f); + rtl8168_mdio_write(tp, 0x14, 0x1b21); + rtl8168_mdio_write(tp, 0x14, 0xaa02); + rtl8168_mdio_write(tp, 0x14, 0xae10); + rtl8168_mdio_write(tp, 0x14, 0xe280); + rtl8168_mdio_write(tp, 0x14, 0x740d); + rtl8168_mdio_write(tp, 0x14, 0x21f6); + rtl8168_mdio_write(tp, 0x14, 0x371b); + rtl8168_mdio_write(tp, 0x14, 0x21aa); + rtl8168_mdio_write(tp, 0x14, 0x0313); + rtl8168_mdio_write(tp, 0x14, 0xae02); + rtl8168_mdio_write(tp, 0x14, 0x2b02); + rtl8168_mdio_write(tp, 0x14, 0x020e); + rtl8168_mdio_write(tp, 0x14, 0x5102); + rtl8168_mdio_write(tp, 0x14, 0x0e66); + rtl8168_mdio_write(tp, 0x14, 0x020f); + rtl8168_mdio_write(tp, 0x14, 0xa3ef); + rtl8168_mdio_write(tp, 0x14, 0x96fe); + rtl8168_mdio_write(tp, 0x14, 0xfdfc); + rtl8168_mdio_write(tp, 0x14, 0x04f8); + rtl8168_mdio_write(tp, 0x14, 0xf9fa); + rtl8168_mdio_write(tp, 0x14, 0xef69); + rtl8168_mdio_write(tp, 0x14, 0xe080); + rtl8168_mdio_write(tp, 0x14, 0x12ad); + rtl8168_mdio_write(tp, 0x14, 0x2733); + rtl8168_mdio_write(tp, 0x14, 0xbf43); + rtl8168_mdio_write(tp, 0x14, 0xbe02); + rtl8168_mdio_write(tp, 0x14, 0x4297); + rtl8168_mdio_write(tp, 0x14, 0xac28); + rtl8168_mdio_write(tp, 0x14, 0x09bf); + rtl8168_mdio_write(tp, 0x14, 0x43c1); + rtl8168_mdio_write(tp, 0x14, 0x0242); + rtl8168_mdio_write(tp, 0x14, 0x97ad); + rtl8168_mdio_write(tp, 0x14, 0x2821); + rtl8168_mdio_write(tp, 0x14, 0xbf45); + rtl8168_mdio_write(tp, 0x14, 0x5902); + rtl8168_mdio_write(tp, 0x14, 0x4297); + rtl8168_mdio_write(tp, 0x14, 0xe387); + rtl8168_mdio_write(tp, 0x14, 0xffd2); + rtl8168_mdio_write(tp, 0x14, 0x001b); + rtl8168_mdio_write(tp, 0x14, 0x45ac); + rtl8168_mdio_write(tp, 0x14, 0x2711); + rtl8168_mdio_write(tp, 0x14, 0xe187); + rtl8168_mdio_write(tp, 0x14, 0xfebf); + rtl8168_mdio_write(tp, 0x14, 0x87e4); + rtl8168_mdio_write(tp, 0x14, 0x0242); + rtl8168_mdio_write(tp, 0x14, 0x590d); + rtl8168_mdio_write(tp, 0x14, 0x11bf); + rtl8168_mdio_write(tp, 0x14, 0x87e7); + rtl8168_mdio_write(tp, 0x14, 0x0242); + rtl8168_mdio_write(tp, 0x14, 0x59ef); + rtl8168_mdio_write(tp, 0x14, 0x96fe); + rtl8168_mdio_write(tp, 0x14, 0xfdfc); + rtl8168_mdio_write(tp, 0x14, 0x04f8); + rtl8168_mdio_write(tp, 0x14, 0xfaef); + rtl8168_mdio_write(tp, 0x14, 0x69d1); + rtl8168_mdio_write(tp, 0x14, 0x00bf); + rtl8168_mdio_write(tp, 0x14, 0x87e4); + rtl8168_mdio_write(tp, 0x14, 0x0242); + rtl8168_mdio_write(tp, 0x14, 0x59bf); + rtl8168_mdio_write(tp, 0x14, 0x87e7); + rtl8168_mdio_write(tp, 0x14, 0x0242); + rtl8168_mdio_write(tp, 0x14, 0x59ef); + rtl8168_mdio_write(tp, 0x14, 0x96fe); + rtl8168_mdio_write(tp, 0x14, 0xfc04); + rtl8168_mdio_write(tp, 0x14, 0xee87); + rtl8168_mdio_write(tp, 0x14, 0xff46); + rtl8168_mdio_write(tp, 0x14, 0xee87); + rtl8168_mdio_write(tp, 0x14, 0xfe01); + rtl8168_mdio_write(tp, 0x14, 0x04f8); + rtl8168_mdio_write(tp, 0x14, 0xfaef); + rtl8168_mdio_write(tp, 0x14, 0x69e0); + rtl8168_mdio_write(tp, 0x14, 0x8241); + rtl8168_mdio_write(tp, 0x14, 0xa000); + rtl8168_mdio_write(tp, 0x14, 0x0502); + rtl8168_mdio_write(tp, 0x14, 0x85eb); + rtl8168_mdio_write(tp, 0x14, 0xae0e); + rtl8168_mdio_write(tp, 0x14, 0xa001); + rtl8168_mdio_write(tp, 0x14, 0x0502); + rtl8168_mdio_write(tp, 0x14, 0x1a5a); + rtl8168_mdio_write(tp, 0x14, 0xae06); + rtl8168_mdio_write(tp, 0x14, 0xa002); + rtl8168_mdio_write(tp, 0x14, 0x0302); + rtl8168_mdio_write(tp, 0x14, 0x1ae6); + rtl8168_mdio_write(tp, 0x14, 0xef96); + rtl8168_mdio_write(tp, 0x14, 0xfefc); + rtl8168_mdio_write(tp, 0x14, 0x04f8); + rtl8168_mdio_write(tp, 0x14, 0xf9fa); + rtl8168_mdio_write(tp, 0x14, 0xef69); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x29f6); + rtl8168_mdio_write(tp, 0x14, 0x21e4); + rtl8168_mdio_write(tp, 0x14, 0x8229); + rtl8168_mdio_write(tp, 0x14, 0xe080); + rtl8168_mdio_write(tp, 0x14, 0x10ac); + rtl8168_mdio_write(tp, 0x14, 0x2202); + rtl8168_mdio_write(tp, 0x14, 0xae76); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x27f7); + rtl8168_mdio_write(tp, 0x14, 0x21e4); + rtl8168_mdio_write(tp, 0x14, 0x8227); + rtl8168_mdio_write(tp, 0x14, 0xbf43); + rtl8168_mdio_write(tp, 0x14, 0x1302); + rtl8168_mdio_write(tp, 0x14, 0x4297); + rtl8168_mdio_write(tp, 0x14, 0xef21); + rtl8168_mdio_write(tp, 0x14, 0xbf43); + rtl8168_mdio_write(tp, 0x14, 0x1602); + rtl8168_mdio_write(tp, 0x14, 0x4297); + rtl8168_mdio_write(tp, 0x14, 0x0c11); + rtl8168_mdio_write(tp, 0x14, 0x1e21); + rtl8168_mdio_write(tp, 0x14, 0xbf43); + rtl8168_mdio_write(tp, 0x14, 0x1902); + rtl8168_mdio_write(tp, 0x14, 0x4297); + rtl8168_mdio_write(tp, 0x14, 0x0c12); + rtl8168_mdio_write(tp, 0x14, 0x1e21); + rtl8168_mdio_write(tp, 0x14, 0xe682); + rtl8168_mdio_write(tp, 0x14, 0x43a2); + rtl8168_mdio_write(tp, 0x14, 0x000a); + rtl8168_mdio_write(tp, 0x14, 0xe182); + rtl8168_mdio_write(tp, 0x14, 0x27f6); + rtl8168_mdio_write(tp, 0x14, 0x29e5); + rtl8168_mdio_write(tp, 0x14, 0x8227); + rtl8168_mdio_write(tp, 0x14, 0xae42); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x44f7); + rtl8168_mdio_write(tp, 0x14, 0x21e4); + rtl8168_mdio_write(tp, 0x14, 0x8244); + rtl8168_mdio_write(tp, 0x14, 0x0246); + rtl8168_mdio_write(tp, 0x14, 0xaebf); + rtl8168_mdio_write(tp, 0x14, 0x4325); + rtl8168_mdio_write(tp, 0x14, 0x0242); + rtl8168_mdio_write(tp, 0x14, 0x97ef); + rtl8168_mdio_write(tp, 0x14, 0x21bf); + rtl8168_mdio_write(tp, 0x14, 0x431c); + rtl8168_mdio_write(tp, 0x14, 0x0242); + rtl8168_mdio_write(tp, 0x14, 0x970c); + rtl8168_mdio_write(tp, 0x14, 0x121e); + rtl8168_mdio_write(tp, 0x14, 0x21bf); + rtl8168_mdio_write(tp, 0x14, 0x431f); + rtl8168_mdio_write(tp, 0x14, 0x0242); + rtl8168_mdio_write(tp, 0x14, 0x970c); + rtl8168_mdio_write(tp, 0x14, 0x131e); + rtl8168_mdio_write(tp, 0x14, 0x21bf); + rtl8168_mdio_write(tp, 0x14, 0x4328); + rtl8168_mdio_write(tp, 0x14, 0x0242); + rtl8168_mdio_write(tp, 0x14, 0x970c); + rtl8168_mdio_write(tp, 0x14, 0x141e); + rtl8168_mdio_write(tp, 0x14, 0x21bf); + rtl8168_mdio_write(tp, 0x14, 0x44b1); + rtl8168_mdio_write(tp, 0x14, 0x0242); + rtl8168_mdio_write(tp, 0x14, 0x970c); + rtl8168_mdio_write(tp, 0x14, 0x161e); + rtl8168_mdio_write(tp, 0x14, 0x21e6); + rtl8168_mdio_write(tp, 0x14, 0x8242); + rtl8168_mdio_write(tp, 0x14, 0xee82); + rtl8168_mdio_write(tp, 0x14, 0x4101); + rtl8168_mdio_write(tp, 0x14, 0xef96); + rtl8168_mdio_write(tp, 0x14, 0xfefd); + rtl8168_mdio_write(tp, 0x14, 0xfc04); + rtl8168_mdio_write(tp, 0x14, 0xf8fa); + rtl8168_mdio_write(tp, 0x14, 0xef69); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x46a0); + rtl8168_mdio_write(tp, 0x14, 0x0005); + rtl8168_mdio_write(tp, 0x14, 0x0286); + rtl8168_mdio_write(tp, 0x14, 0x96ae); + rtl8168_mdio_write(tp, 0x14, 0x06a0); + rtl8168_mdio_write(tp, 0x14, 0x0103); + rtl8168_mdio_write(tp, 0x14, 0x0219); + rtl8168_mdio_write(tp, 0x14, 0x19ef); + rtl8168_mdio_write(tp, 0x14, 0x96fe); + rtl8168_mdio_write(tp, 0x14, 0xfc04); + rtl8168_mdio_write(tp, 0x14, 0xf8fa); + rtl8168_mdio_write(tp, 0x14, 0xef69); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x29f6); + rtl8168_mdio_write(tp, 0x14, 0x20e4); + rtl8168_mdio_write(tp, 0x14, 0x8229); + rtl8168_mdio_write(tp, 0x14, 0xe080); + rtl8168_mdio_write(tp, 0x14, 0x10ac); + rtl8168_mdio_write(tp, 0x14, 0x2102); + rtl8168_mdio_write(tp, 0x14, 0xae54); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x27f7); + rtl8168_mdio_write(tp, 0x14, 0x20e4); + rtl8168_mdio_write(tp, 0x14, 0x8227); + rtl8168_mdio_write(tp, 0x14, 0xbf42); + rtl8168_mdio_write(tp, 0x14, 0xe602); + rtl8168_mdio_write(tp, 0x14, 0x4297); + rtl8168_mdio_write(tp, 0x14, 0xac28); + rtl8168_mdio_write(tp, 0x14, 0x22bf); + rtl8168_mdio_write(tp, 0x14, 0x430d); + rtl8168_mdio_write(tp, 0x14, 0x0242); + rtl8168_mdio_write(tp, 0x14, 0x97e5); + rtl8168_mdio_write(tp, 0x14, 0x8247); + rtl8168_mdio_write(tp, 0x14, 0xac28); + rtl8168_mdio_write(tp, 0x14, 0x20d1); + rtl8168_mdio_write(tp, 0x14, 0x03bf); + rtl8168_mdio_write(tp, 0x14, 0x4307); + rtl8168_mdio_write(tp, 0x14, 0x0242); + rtl8168_mdio_write(tp, 0x14, 0x59ee); + rtl8168_mdio_write(tp, 0x14, 0x8246); + rtl8168_mdio_write(tp, 0x14, 0x00e1); + rtl8168_mdio_write(tp, 0x14, 0x8227); + rtl8168_mdio_write(tp, 0x14, 0xf628); + rtl8168_mdio_write(tp, 0x14, 0xe582); + rtl8168_mdio_write(tp, 0x14, 0x27ae); + rtl8168_mdio_write(tp, 0x14, 0x21d1); + rtl8168_mdio_write(tp, 0x14, 0x04bf); + rtl8168_mdio_write(tp, 0x14, 0x4307); + rtl8168_mdio_write(tp, 0x14, 0x0242); + rtl8168_mdio_write(tp, 0x14, 0x59ae); + rtl8168_mdio_write(tp, 0x14, 0x08d1); + rtl8168_mdio_write(tp, 0x14, 0x05bf); + rtl8168_mdio_write(tp, 0x14, 0x4307); + rtl8168_mdio_write(tp, 0x14, 0x0242); + rtl8168_mdio_write(tp, 0x14, 0x59e0); + rtl8168_mdio_write(tp, 0x14, 0x8244); + rtl8168_mdio_write(tp, 0x14, 0xf720); + rtl8168_mdio_write(tp, 0x14, 0xe482); + rtl8168_mdio_write(tp, 0x14, 0x4402); + rtl8168_mdio_write(tp, 0x14, 0x46ae); + rtl8168_mdio_write(tp, 0x14, 0xee82); + rtl8168_mdio_write(tp, 0x14, 0x4601); + rtl8168_mdio_write(tp, 0x14, 0xef96); + rtl8168_mdio_write(tp, 0x14, 0xfefc); + rtl8168_mdio_write(tp, 0x14, 0x04f8); + rtl8168_mdio_write(tp, 0x14, 0xfaef); + rtl8168_mdio_write(tp, 0x14, 0x69e0); + rtl8168_mdio_write(tp, 0x14, 0x8013); + rtl8168_mdio_write(tp, 0x14, 0xad24); + rtl8168_mdio_write(tp, 0x14, 0x1cbf); + rtl8168_mdio_write(tp, 0x14, 0x87f0); + rtl8168_mdio_write(tp, 0x14, 0x0242); + rtl8168_mdio_write(tp, 0x14, 0x97ad); + rtl8168_mdio_write(tp, 0x14, 0x2813); + rtl8168_mdio_write(tp, 0x14, 0xe087); + rtl8168_mdio_write(tp, 0x14, 0xfca0); + rtl8168_mdio_write(tp, 0x14, 0x0005); + rtl8168_mdio_write(tp, 0x14, 0x0287); + rtl8168_mdio_write(tp, 0x14, 0x36ae); + rtl8168_mdio_write(tp, 0x14, 0x10a0); + rtl8168_mdio_write(tp, 0x14, 0x0105); + rtl8168_mdio_write(tp, 0x14, 0x0287); + rtl8168_mdio_write(tp, 0x14, 0x48ae); + rtl8168_mdio_write(tp, 0x14, 0x08e0); + rtl8168_mdio_write(tp, 0x14, 0x8230); + rtl8168_mdio_write(tp, 0x14, 0xf626); + rtl8168_mdio_write(tp, 0x14, 0xe482); + rtl8168_mdio_write(tp, 0x14, 0x30ef); + rtl8168_mdio_write(tp, 0x14, 0x96fe); + rtl8168_mdio_write(tp, 0x14, 0xfc04); + rtl8168_mdio_write(tp, 0x14, 0xf8e0); + rtl8168_mdio_write(tp, 0x14, 0x8245); + rtl8168_mdio_write(tp, 0x14, 0xf722); + rtl8168_mdio_write(tp, 0x14, 0xe482); + rtl8168_mdio_write(tp, 0x14, 0x4502); + rtl8168_mdio_write(tp, 0x14, 0x46ae); + rtl8168_mdio_write(tp, 0x14, 0xee87); + rtl8168_mdio_write(tp, 0x14, 0xfc01); + rtl8168_mdio_write(tp, 0x14, 0xfc04); + rtl8168_mdio_write(tp, 0x14, 0xf8fa); + rtl8168_mdio_write(tp, 0x14, 0xef69); + rtl8168_mdio_write(tp, 0x14, 0xfb02); + rtl8168_mdio_write(tp, 0x14, 0x46d3); + rtl8168_mdio_write(tp, 0x14, 0xad50); + rtl8168_mdio_write(tp, 0x14, 0x2fbf); + rtl8168_mdio_write(tp, 0x14, 0x87ed); + rtl8168_mdio_write(tp, 0x14, 0xd101); + rtl8168_mdio_write(tp, 0x14, 0x0242); + rtl8168_mdio_write(tp, 0x14, 0x59bf); + rtl8168_mdio_write(tp, 0x14, 0x87ed); + rtl8168_mdio_write(tp, 0x14, 0xd100); + rtl8168_mdio_write(tp, 0x14, 0x0242); + rtl8168_mdio_write(tp, 0x14, 0x59e0); + rtl8168_mdio_write(tp, 0x14, 0x8245); + rtl8168_mdio_write(tp, 0x14, 0xf622); + rtl8168_mdio_write(tp, 0x14, 0xe482); + rtl8168_mdio_write(tp, 0x14, 0x4502); + rtl8168_mdio_write(tp, 0x14, 0x46ae); + rtl8168_mdio_write(tp, 0x14, 0xd100); + rtl8168_mdio_write(tp, 0x14, 0xbf87); + rtl8168_mdio_write(tp, 0x14, 0xf002); + rtl8168_mdio_write(tp, 0x14, 0x4259); + rtl8168_mdio_write(tp, 0x14, 0xee87); + rtl8168_mdio_write(tp, 0x14, 0xfc00); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x30f6); + rtl8168_mdio_write(tp, 0x14, 0x26e4); + rtl8168_mdio_write(tp, 0x14, 0x8230); + rtl8168_mdio_write(tp, 0x14, 0xffef); + rtl8168_mdio_write(tp, 0x14, 0x96fe); + rtl8168_mdio_write(tp, 0x14, 0xfc04); + rtl8168_mdio_write(tp, 0x14, 0xf8f9); + rtl8168_mdio_write(tp, 0x14, 0xface); + rtl8168_mdio_write(tp, 0x14, 0xfaef); + rtl8168_mdio_write(tp, 0x14, 0x69fb); + rtl8168_mdio_write(tp, 0x14, 0xbf87); + rtl8168_mdio_write(tp, 0x14, 0xb3d7); + rtl8168_mdio_write(tp, 0x14, 0x001c); + rtl8168_mdio_write(tp, 0x14, 0xd819); + rtl8168_mdio_write(tp, 0x14, 0xd919); + rtl8168_mdio_write(tp, 0x14, 0xda19); + rtl8168_mdio_write(tp, 0x14, 0xdb19); + rtl8168_mdio_write(tp, 0x14, 0x07ef); + rtl8168_mdio_write(tp, 0x14, 0x9502); + rtl8168_mdio_write(tp, 0x14, 0x4259); + rtl8168_mdio_write(tp, 0x14, 0x073f); + rtl8168_mdio_write(tp, 0x14, 0x0004); + rtl8168_mdio_write(tp, 0x14, 0x9fec); + rtl8168_mdio_write(tp, 0x14, 0xffef); + rtl8168_mdio_write(tp, 0x14, 0x96fe); + rtl8168_mdio_write(tp, 0x14, 0xc6fe); + rtl8168_mdio_write(tp, 0x14, 0xfdfc); + rtl8168_mdio_write(tp, 0x14, 0x0400); + rtl8168_mdio_write(tp, 0x14, 0x0145); + rtl8168_mdio_write(tp, 0x14, 0x7d00); + rtl8168_mdio_write(tp, 0x14, 0x0345); + rtl8168_mdio_write(tp, 0x14, 0x5c00); + rtl8168_mdio_write(tp, 0x14, 0x0143); + rtl8168_mdio_write(tp, 0x14, 0x4f00); + rtl8168_mdio_write(tp, 0x14, 0x0387); + rtl8168_mdio_write(tp, 0x14, 0xdb00); + rtl8168_mdio_write(tp, 0x14, 0x0987); + rtl8168_mdio_write(tp, 0x14, 0xde00); + rtl8168_mdio_write(tp, 0x14, 0x0987); + rtl8168_mdio_write(tp, 0x14, 0xe100); + rtl8168_mdio_write(tp, 0x14, 0x0087); + rtl8168_mdio_write(tp, 0x14, 0xeaa4); + rtl8168_mdio_write(tp, 0x14, 0x00b8); + rtl8168_mdio_write(tp, 0x14, 0x20c4); + rtl8168_mdio_write(tp, 0x14, 0x1600); + rtl8168_mdio_write(tp, 0x14, 0x000f); + rtl8168_mdio_write(tp, 0x14, 0xf800); + rtl8168_mdio_write(tp, 0x14, 0x7098); + rtl8168_mdio_write(tp, 0x14, 0xa58a); + rtl8168_mdio_write(tp, 0x14, 0xb6a8); + rtl8168_mdio_write(tp, 0x14, 0x3e50); + rtl8168_mdio_write(tp, 0x14, 0xa83e); + rtl8168_mdio_write(tp, 0x14, 0x33bc); + rtl8168_mdio_write(tp, 0x14, 0xc622); + rtl8168_mdio_write(tp, 0x14, 0xbcc6); + rtl8168_mdio_write(tp, 0x14, 0xaaa4); + rtl8168_mdio_write(tp, 0x14, 0x42ff); + rtl8168_mdio_write(tp, 0x14, 0xc408); + rtl8168_mdio_write(tp, 0x14, 0x00c4); + rtl8168_mdio_write(tp, 0x14, 0x16a8); + rtl8168_mdio_write(tp, 0x14, 0xbcc0); + rtl8168_mdio_write(tp, 0x13, 0xb818); + rtl8168_mdio_write(tp, 0x14, 0x02f3); + rtl8168_mdio_write(tp, 0x13, 0xb81a); + rtl8168_mdio_write(tp, 0x14, 0x17d1); + rtl8168_mdio_write(tp, 0x13, 0xb81c); + rtl8168_mdio_write(tp, 0x14, 0x185a); + rtl8168_mdio_write(tp, 0x13, 0xb81e); + rtl8168_mdio_write(tp, 0x14, 0x3c66); + rtl8168_mdio_write(tp, 0x13, 0xb820); + rtl8168_mdio_write(tp, 0x14, 0x021f); + rtl8168_mdio_write(tp, 0x13, 0xc416); + rtl8168_mdio_write(tp, 0x14, 0x0500); + rtl8168_mdio_write(tp, 0x13, 0xb82e); + rtl8168_mdio_write(tp, 0x14, 0xfffc); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x0000); + rtl8168_mdio_write(tp, 0x14, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0B82); + gphy_val = rtl8168_mdio_read(tp, 0x10); + gphy_val &= ~(BIT_9); + rtl8168_mdio_write(tp, 0x10, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8146); + rtl8168_mdio_write(tp, 0x14, 0x0000); + + rtl8168_clear_phy_mcu_patch_request(tp); +} + +static void +rtl8168_set_phy_mcu_8168gu_2(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned int gphy_val; + + rtl8168_set_phy_mcu_patch_request(tp); + rtl8168_mdio_write(tp, 0x1f, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8146); + rtl8168_mdio_write(tp, 0x14, 0x0300); + rtl8168_mdio_write(tp, 0x13, 0xB82E); + rtl8168_mdio_write(tp, 0x14, 0x0001); + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0xb820); + rtl8168_mdio_write(tp, 0x14, 0x0290); + rtl8168_mdio_write(tp, 0x13, 0xa012); + rtl8168_mdio_write(tp, 0x14, 0x0000); + rtl8168_mdio_write(tp, 0x13, 0xa014); + rtl8168_mdio_write(tp, 0x14, 0x2c04); + rtl8168_mdio_write(tp, 0x14, 0x2c07); + rtl8168_mdio_write(tp, 0x14, 0x2c07); + rtl8168_mdio_write(tp, 0x14, 0x2c07); + rtl8168_mdio_write(tp, 0x14, 0xa304); + rtl8168_mdio_write(tp, 0x14, 0xa301); + rtl8168_mdio_write(tp, 0x14, 0x207e); + rtl8168_mdio_write(tp, 0x13, 0xa01a); + rtl8168_mdio_write(tp, 0x14, 0x0000); + rtl8168_mdio_write(tp, 0x13, 0xa006); + rtl8168_mdio_write(tp, 0x14, 0x0fff); + rtl8168_mdio_write(tp, 0x13, 0xa004); + rtl8168_mdio_write(tp, 0x14, 0x0fff); + rtl8168_mdio_write(tp, 0x13, 0xa002); + rtl8168_mdio_write(tp, 0x14, 0x0fff); + rtl8168_mdio_write(tp, 0x13, 0xa000); + rtl8168_mdio_write(tp, 0x14, 0x107c); + rtl8168_mdio_write(tp, 0x13, 0xb820); + rtl8168_mdio_write(tp, 0x14, 0x0210); + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x0000); + rtl8168_mdio_write(tp, 0x14, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0B82); + gphy_val = rtl8168_mdio_read(tp, 0x17); + gphy_val &= ~(BIT_0); + rtl8168_mdio_write(tp, 0x17, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8146); + rtl8168_mdio_write(tp, 0x14, 0x0000); + + rtl8168_clear_phy_mcu_patch_request(tp); +} + +static void +rtl8168_set_phy_mcu_8411b_1(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned int gphy_val; + + rtl8168_set_phy_mcu_patch_request(tp); + + rtl8168_mdio_write(tp,0x1f, 0x0A43); + rtl8168_mdio_write(tp,0x13, 0x8146); + rtl8168_mdio_write(tp,0x14, 0x0100); + rtl8168_mdio_write(tp,0x13, 0xB82E); + rtl8168_mdio_write(tp,0x14, 0x0001); + + + rtl8168_mdio_write(tp,0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0xb820); + rtl8168_mdio_write(tp, 0x14, 0x0290); + rtl8168_mdio_write(tp, 0x13, 0xa012); + rtl8168_mdio_write(tp, 0x14, 0x0000); + rtl8168_mdio_write(tp, 0x13, 0xa014); + rtl8168_mdio_write(tp, 0x14, 0x2c04); + rtl8168_mdio_write(tp, 0x14, 0x2c07); + rtl8168_mdio_write(tp, 0x14, 0x2c07); + rtl8168_mdio_write(tp, 0x14, 0x2c07); + rtl8168_mdio_write(tp, 0x14, 0xa304); + rtl8168_mdio_write(tp, 0x14, 0xa301); + rtl8168_mdio_write(tp, 0x14, 0x207e); + rtl8168_mdio_write(tp, 0x13, 0xa01a); + rtl8168_mdio_write(tp, 0x14, 0x0000); + rtl8168_mdio_write(tp, 0x13, 0xa006); + rtl8168_mdio_write(tp, 0x14, 0x0fff); + rtl8168_mdio_write(tp, 0x13, 0xa004); + rtl8168_mdio_write(tp, 0x14, 0x0fff); + rtl8168_mdio_write(tp, 0x13, 0xa002); + rtl8168_mdio_write(tp, 0x14, 0x0fff); + rtl8168_mdio_write(tp, 0x13, 0xa000); + rtl8168_mdio_write(tp, 0x14, 0x107c); + rtl8168_mdio_write(tp, 0x13, 0xb820); + rtl8168_mdio_write(tp, 0x14, 0x0210); + + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x0000); + rtl8168_mdio_write(tp, 0x14, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0B82); + gphy_val = rtl8168_mdio_read(tp, 0x17); + gphy_val &= ~(BIT_0); + rtl8168_mdio_write(tp, 0x17, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8146); + rtl8168_mdio_write(tp, 0x14, 0x0000); + + rtl8168_clear_phy_mcu_patch_request(tp); +} + +static void +rtl8168_set_phy_mcu_8168ep_2(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned int gphy_val; + + rtl8168_set_phy_mcu_patch_request(tp); + + rtl8168_mdio_write(tp,0x1f, 0x0A43); + rtl8168_mdio_write(tp,0x13, 0x8146); + rtl8168_mdio_write(tp,0x14, 0x8700); + rtl8168_mdio_write(tp,0x13, 0xB82E); + rtl8168_mdio_write(tp,0x14, 0x0001); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + + rtl8168_mdio_write(tp, 0x13, 0x83DD); + rtl8168_mdio_write(tp, 0x14, 0xAF83); + rtl8168_mdio_write(tp, 0x14, 0xE9AF); + rtl8168_mdio_write(tp, 0x14, 0x83EE); + rtl8168_mdio_write(tp, 0x14, 0xAF83); + rtl8168_mdio_write(tp, 0x14, 0xF1A1); + rtl8168_mdio_write(tp, 0x14, 0x83F4); + rtl8168_mdio_write(tp, 0x14, 0xD149); + rtl8168_mdio_write(tp, 0x14, 0xAF06); + rtl8168_mdio_write(tp, 0x14, 0x47AF); + rtl8168_mdio_write(tp, 0x14, 0x0000); + rtl8168_mdio_write(tp, 0x14, 0xAF00); + rtl8168_mdio_write(tp, 0x14, 0x00AF); + rtl8168_mdio_write(tp, 0x14, 0x0000); + + rtl8168_mdio_write(tp, 0x13, 0xB818); + rtl8168_mdio_write(tp, 0x14, 0x0645); + + rtl8168_mdio_write(tp, 0x13, 0xB81A); + rtl8168_mdio_write(tp, 0x14, 0x0000); + + rtl8168_mdio_write(tp, 0x13, 0xB81C); + rtl8168_mdio_write(tp, 0x14, 0x0000); + + rtl8168_mdio_write(tp, 0x13, 0xB81E); + rtl8168_mdio_write(tp, 0x14, 0x0000); + + rtl8168_mdio_write(tp, 0x13, 0xB832); + rtl8168_mdio_write(tp, 0x14, 0x0001); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x0000); + rtl8168_mdio_write(tp, 0x14, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0B82); + gphy_val = rtl8168_mdio_read(tp, 0x17); + gphy_val &= ~(BIT_0); + rtl8168_mdio_write(tp, 0x17, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8146); + rtl8168_mdio_write(tp, 0x14, 0x0000); + + rtl8168_clear_phy_mcu_patch_request(tp); +} + +static void +rtl8168_set_phy_mcu_8168h_1(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned int gphy_val; + + rtl8168_set_phy_mcu_patch_request(tp); + + rtl8168_mdio_write(tp, 0x1f, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8028); + rtl8168_mdio_write(tp, 0x14, 0x6200); + rtl8168_mdio_write(tp, 0x13, 0xB82E); + rtl8168_mdio_write(tp, 0x14, 0x0001); + + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0xB820); + rtl8168_mdio_write(tp, 0x14, 0x0290); + rtl8168_mdio_write(tp, 0x13, 0xA012); + rtl8168_mdio_write(tp, 0x14, 0x0000); + rtl8168_mdio_write(tp, 0x13, 0xA014); + rtl8168_mdio_write(tp, 0x14, 0x2c04); + rtl8168_mdio_write(tp, 0x14, 0x2c10); + rtl8168_mdio_write(tp, 0x14, 0x2c10); + rtl8168_mdio_write(tp, 0x14, 0x2c10); + rtl8168_mdio_write(tp, 0x14, 0xa210); + rtl8168_mdio_write(tp, 0x14, 0xa101); + rtl8168_mdio_write(tp, 0x14, 0xce10); + rtl8168_mdio_write(tp, 0x14, 0xe070); + rtl8168_mdio_write(tp, 0x14, 0x0f40); + rtl8168_mdio_write(tp, 0x14, 0xaf01); + rtl8168_mdio_write(tp, 0x14, 0x8f01); + rtl8168_mdio_write(tp, 0x14, 0x183e); + rtl8168_mdio_write(tp, 0x14, 0x8e10); + rtl8168_mdio_write(tp, 0x14, 0x8101); + rtl8168_mdio_write(tp, 0x14, 0x8210); + rtl8168_mdio_write(tp, 0x14, 0x28da); + rtl8168_mdio_write(tp, 0x13, 0xA01A); + rtl8168_mdio_write(tp, 0x14, 0x0000); + rtl8168_mdio_write(tp, 0x13, 0xA006); + rtl8168_mdio_write(tp, 0x14, 0x0017); + rtl8168_mdio_write(tp, 0x13, 0xA004); + rtl8168_mdio_write(tp, 0x14, 0x0015); + rtl8168_mdio_write(tp, 0x13, 0xA002); + rtl8168_mdio_write(tp, 0x14, 0x0013); + rtl8168_mdio_write(tp, 0x13, 0xA000); + rtl8168_mdio_write(tp, 0x14, 0x18d1); + rtl8168_mdio_write(tp, 0x13, 0xB820); + rtl8168_mdio_write(tp, 0x14, 0x0210); + + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x0000); + rtl8168_mdio_write(tp, 0x14, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0B82); + gphy_val = rtl8168_mdio_read(tp, 0x17); + gphy_val &= ~(BIT_0); + rtl8168_mdio_write(tp, 0x17, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8028); + rtl8168_mdio_write(tp, 0x14, 0x0000); + + rtl8168_clear_phy_mcu_patch_request(tp); +} + +static void +rtl8168_set_phy_mcu_8168h_2(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned int gphy_val; + + rtl8168_set_phy_mcu_patch_request(tp); + + rtl8168_mdio_write(tp, 0x1f, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8028); + rtl8168_mdio_write(tp, 0x14, 0x6201); + rtl8168_mdio_write(tp, 0x13, 0xB82E); + rtl8168_mdio_write(tp, 0x14, 0x0001); + + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0xB820); + rtl8168_mdio_write(tp, 0x14, 0x0290); + rtl8168_mdio_write(tp, 0x13, 0xA012); + rtl8168_mdio_write(tp, 0x14, 0x0000); + rtl8168_mdio_write(tp, 0x13, 0xA014); + rtl8168_mdio_write(tp, 0x14, 0x2c04); + rtl8168_mdio_write(tp, 0x14, 0x2c09); + rtl8168_mdio_write(tp, 0x14, 0x2c09); + rtl8168_mdio_write(tp, 0x14, 0x2c09); + rtl8168_mdio_write(tp, 0x14, 0xad01); + rtl8168_mdio_write(tp, 0x14, 0xad01); + rtl8168_mdio_write(tp, 0x14, 0xad01); + rtl8168_mdio_write(tp, 0x14, 0xad01); + rtl8168_mdio_write(tp, 0x14, 0x236c); + rtl8168_mdio_write(tp, 0x13, 0xA01A); + rtl8168_mdio_write(tp, 0x14, 0x0000); + rtl8168_mdio_write(tp, 0x13, 0xA006); + rtl8168_mdio_write(tp, 0x14, 0x0fff); + rtl8168_mdio_write(tp, 0x13, 0xA004); + rtl8168_mdio_write(tp, 0x14, 0x0fff); + rtl8168_mdio_write(tp, 0x13, 0xA002); + rtl8168_mdio_write(tp, 0x14, 0x0fff); + rtl8168_mdio_write(tp, 0x13, 0xA000); + rtl8168_mdio_write(tp, 0x14, 0x136b); + rtl8168_mdio_write(tp, 0x13, 0xB820); + rtl8168_mdio_write(tp, 0x14, 0x0210); + + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8323); + rtl8168_mdio_write(tp, 0x14, 0xaf83); + rtl8168_mdio_write(tp, 0x14, 0x2faf); + rtl8168_mdio_write(tp, 0x14, 0x853d); + rtl8168_mdio_write(tp, 0x14, 0xaf85); + rtl8168_mdio_write(tp, 0x14, 0x3daf); + rtl8168_mdio_write(tp, 0x14, 0x853d); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x45ad); + rtl8168_mdio_write(tp, 0x14, 0x2052); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x7ae3); + rtl8168_mdio_write(tp, 0x14, 0x85fe); + rtl8168_mdio_write(tp, 0x14, 0x1a03); + rtl8168_mdio_write(tp, 0x14, 0x10e4); + rtl8168_mdio_write(tp, 0x14, 0x85f6); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x7a1b); + rtl8168_mdio_write(tp, 0x14, 0x03e4); + rtl8168_mdio_write(tp, 0x14, 0x85fa); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x7be3); + rtl8168_mdio_write(tp, 0x14, 0x85fe); + rtl8168_mdio_write(tp, 0x14, 0x1a03); + rtl8168_mdio_write(tp, 0x14, 0x10e4); + rtl8168_mdio_write(tp, 0x14, 0x85f7); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x7b1b); + rtl8168_mdio_write(tp, 0x14, 0x03e4); + rtl8168_mdio_write(tp, 0x14, 0x85fb); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x7ce3); + rtl8168_mdio_write(tp, 0x14, 0x85fe); + rtl8168_mdio_write(tp, 0x14, 0x1a03); + rtl8168_mdio_write(tp, 0x14, 0x10e4); + rtl8168_mdio_write(tp, 0x14, 0x85f8); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x7c1b); + rtl8168_mdio_write(tp, 0x14, 0x03e4); + rtl8168_mdio_write(tp, 0x14, 0x85fc); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x7de3); + rtl8168_mdio_write(tp, 0x14, 0x85fe); + rtl8168_mdio_write(tp, 0x14, 0x1a03); + rtl8168_mdio_write(tp, 0x14, 0x10e4); + rtl8168_mdio_write(tp, 0x14, 0x85f9); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x7d1b); + rtl8168_mdio_write(tp, 0x14, 0x03e4); + rtl8168_mdio_write(tp, 0x14, 0x85fd); + rtl8168_mdio_write(tp, 0x14, 0xae50); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x7ee3); + rtl8168_mdio_write(tp, 0x14, 0x85ff); + rtl8168_mdio_write(tp, 0x14, 0x1a03); + rtl8168_mdio_write(tp, 0x14, 0x10e4); + rtl8168_mdio_write(tp, 0x14, 0x85f6); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x7e1b); + rtl8168_mdio_write(tp, 0x14, 0x03e4); + rtl8168_mdio_write(tp, 0x14, 0x85fa); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x7fe3); + rtl8168_mdio_write(tp, 0x14, 0x85ff); + rtl8168_mdio_write(tp, 0x14, 0x1a03); + rtl8168_mdio_write(tp, 0x14, 0x10e4); + rtl8168_mdio_write(tp, 0x14, 0x85f7); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x7f1b); + rtl8168_mdio_write(tp, 0x14, 0x03e4); + rtl8168_mdio_write(tp, 0x14, 0x85fb); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x80e3); + rtl8168_mdio_write(tp, 0x14, 0x85ff); + rtl8168_mdio_write(tp, 0x14, 0x1a03); + rtl8168_mdio_write(tp, 0x14, 0x10e4); + rtl8168_mdio_write(tp, 0x14, 0x85f8); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x801b); + rtl8168_mdio_write(tp, 0x14, 0x03e4); + rtl8168_mdio_write(tp, 0x14, 0x85fc); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x81e3); + rtl8168_mdio_write(tp, 0x14, 0x85ff); + rtl8168_mdio_write(tp, 0x14, 0x1a03); + rtl8168_mdio_write(tp, 0x14, 0x10e4); + rtl8168_mdio_write(tp, 0x14, 0x85f9); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x811b); + rtl8168_mdio_write(tp, 0x14, 0x03e4); + rtl8168_mdio_write(tp, 0x14, 0x85fd); + rtl8168_mdio_write(tp, 0x14, 0xe085); + rtl8168_mdio_write(tp, 0x14, 0xf6ad); + rtl8168_mdio_write(tp, 0x14, 0x2404); + rtl8168_mdio_write(tp, 0x14, 0xee85); + rtl8168_mdio_write(tp, 0x14, 0xf610); + rtl8168_mdio_write(tp, 0x14, 0xe085); + rtl8168_mdio_write(tp, 0x14, 0xf7ad); + rtl8168_mdio_write(tp, 0x14, 0x2404); + rtl8168_mdio_write(tp, 0x14, 0xee85); + rtl8168_mdio_write(tp, 0x14, 0xf710); + rtl8168_mdio_write(tp, 0x14, 0xe085); + rtl8168_mdio_write(tp, 0x14, 0xf8ad); + rtl8168_mdio_write(tp, 0x14, 0x2404); + rtl8168_mdio_write(tp, 0x14, 0xee85); + rtl8168_mdio_write(tp, 0x14, 0xf810); + rtl8168_mdio_write(tp, 0x14, 0xe085); + rtl8168_mdio_write(tp, 0x14, 0xf9ad); + rtl8168_mdio_write(tp, 0x14, 0x2404); + rtl8168_mdio_write(tp, 0x14, 0xee85); + rtl8168_mdio_write(tp, 0x14, 0xf910); + rtl8168_mdio_write(tp, 0x14, 0xe085); + rtl8168_mdio_write(tp, 0x14, 0xfaad); + rtl8168_mdio_write(tp, 0x14, 0x2704); + rtl8168_mdio_write(tp, 0x14, 0xee85); + rtl8168_mdio_write(tp, 0x14, 0xfa00); + rtl8168_mdio_write(tp, 0x14, 0xe085); + rtl8168_mdio_write(tp, 0x14, 0xfbad); + rtl8168_mdio_write(tp, 0x14, 0x2704); + rtl8168_mdio_write(tp, 0x14, 0xee85); + rtl8168_mdio_write(tp, 0x14, 0xfb00); + rtl8168_mdio_write(tp, 0x14, 0xe085); + rtl8168_mdio_write(tp, 0x14, 0xfcad); + rtl8168_mdio_write(tp, 0x14, 0x2704); + rtl8168_mdio_write(tp, 0x14, 0xee85); + rtl8168_mdio_write(tp, 0x14, 0xfc00); + rtl8168_mdio_write(tp, 0x14, 0xe085); + rtl8168_mdio_write(tp, 0x14, 0xfdad); + rtl8168_mdio_write(tp, 0x14, 0x2704); + rtl8168_mdio_write(tp, 0x14, 0xee85); + rtl8168_mdio_write(tp, 0x14, 0xfd00); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x44ad); + rtl8168_mdio_write(tp, 0x14, 0x203f); + rtl8168_mdio_write(tp, 0x14, 0xe085); + rtl8168_mdio_write(tp, 0x14, 0xf6e4); + rtl8168_mdio_write(tp, 0x14, 0x8288); + rtl8168_mdio_write(tp, 0x14, 0xe085); + rtl8168_mdio_write(tp, 0x14, 0xfae4); + rtl8168_mdio_write(tp, 0x14, 0x8289); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x440d); + rtl8168_mdio_write(tp, 0x14, 0x0458); + rtl8168_mdio_write(tp, 0x14, 0x01bf); + rtl8168_mdio_write(tp, 0x14, 0x8264); + rtl8168_mdio_write(tp, 0x14, 0x0215); + rtl8168_mdio_write(tp, 0x14, 0x38bf); + rtl8168_mdio_write(tp, 0x14, 0x824e); + rtl8168_mdio_write(tp, 0x14, 0x0213); + rtl8168_mdio_write(tp, 0x14, 0x06a0); + rtl8168_mdio_write(tp, 0x14, 0x010f); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x44f6); + rtl8168_mdio_write(tp, 0x14, 0x20e4); + rtl8168_mdio_write(tp, 0x14, 0x8244); + rtl8168_mdio_write(tp, 0x14, 0x580f); + rtl8168_mdio_write(tp, 0x14, 0xe582); + rtl8168_mdio_write(tp, 0x14, 0x5aae); + rtl8168_mdio_write(tp, 0x14, 0x0ebf); + rtl8168_mdio_write(tp, 0x14, 0x825e); + rtl8168_mdio_write(tp, 0x14, 0xe382); + rtl8168_mdio_write(tp, 0x14, 0x44f7); + rtl8168_mdio_write(tp, 0x14, 0x3ce7); + rtl8168_mdio_write(tp, 0x14, 0x8244); + rtl8168_mdio_write(tp, 0x14, 0x0212); + rtl8168_mdio_write(tp, 0x14, 0xf0ad); + rtl8168_mdio_write(tp, 0x14, 0x213f); + rtl8168_mdio_write(tp, 0x14, 0xe085); + rtl8168_mdio_write(tp, 0x14, 0xf7e4); + rtl8168_mdio_write(tp, 0x14, 0x8288); + rtl8168_mdio_write(tp, 0x14, 0xe085); + rtl8168_mdio_write(tp, 0x14, 0xfbe4); + rtl8168_mdio_write(tp, 0x14, 0x8289); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x440d); + rtl8168_mdio_write(tp, 0x14, 0x0558); + rtl8168_mdio_write(tp, 0x14, 0x01bf); + rtl8168_mdio_write(tp, 0x14, 0x826b); + rtl8168_mdio_write(tp, 0x14, 0x0215); + rtl8168_mdio_write(tp, 0x14, 0x38bf); + rtl8168_mdio_write(tp, 0x14, 0x824f); + rtl8168_mdio_write(tp, 0x14, 0x0213); + rtl8168_mdio_write(tp, 0x14, 0x06a0); + rtl8168_mdio_write(tp, 0x14, 0x010f); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x44f6); + rtl8168_mdio_write(tp, 0x14, 0x21e4); + rtl8168_mdio_write(tp, 0x14, 0x8244); + rtl8168_mdio_write(tp, 0x14, 0x580f); + rtl8168_mdio_write(tp, 0x14, 0xe582); + rtl8168_mdio_write(tp, 0x14, 0x5bae); + rtl8168_mdio_write(tp, 0x14, 0x0ebf); + rtl8168_mdio_write(tp, 0x14, 0x8265); + rtl8168_mdio_write(tp, 0x14, 0xe382); + rtl8168_mdio_write(tp, 0x14, 0x44f7); + rtl8168_mdio_write(tp, 0x14, 0x3de7); + rtl8168_mdio_write(tp, 0x14, 0x8244); + rtl8168_mdio_write(tp, 0x14, 0x0212); + rtl8168_mdio_write(tp, 0x14, 0xf0ad); + rtl8168_mdio_write(tp, 0x14, 0x223f); + rtl8168_mdio_write(tp, 0x14, 0xe085); + rtl8168_mdio_write(tp, 0x14, 0xf8e4); + rtl8168_mdio_write(tp, 0x14, 0x8288); + rtl8168_mdio_write(tp, 0x14, 0xe085); + rtl8168_mdio_write(tp, 0x14, 0xfce4); + rtl8168_mdio_write(tp, 0x14, 0x8289); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x440d); + rtl8168_mdio_write(tp, 0x14, 0x0658); + rtl8168_mdio_write(tp, 0x14, 0x01bf); + rtl8168_mdio_write(tp, 0x14, 0x8272); + rtl8168_mdio_write(tp, 0x14, 0x0215); + rtl8168_mdio_write(tp, 0x14, 0x38bf); + rtl8168_mdio_write(tp, 0x14, 0x8250); + rtl8168_mdio_write(tp, 0x14, 0x0213); + rtl8168_mdio_write(tp, 0x14, 0x06a0); + rtl8168_mdio_write(tp, 0x14, 0x010f); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x44f6); + rtl8168_mdio_write(tp, 0x14, 0x22e4); + rtl8168_mdio_write(tp, 0x14, 0x8244); + rtl8168_mdio_write(tp, 0x14, 0x580f); + rtl8168_mdio_write(tp, 0x14, 0xe582); + rtl8168_mdio_write(tp, 0x14, 0x5cae); + rtl8168_mdio_write(tp, 0x14, 0x0ebf); + rtl8168_mdio_write(tp, 0x14, 0x826c); + rtl8168_mdio_write(tp, 0x14, 0xe382); + rtl8168_mdio_write(tp, 0x14, 0x44f7); + rtl8168_mdio_write(tp, 0x14, 0x3ee7); + rtl8168_mdio_write(tp, 0x14, 0x8244); + rtl8168_mdio_write(tp, 0x14, 0x0212); + rtl8168_mdio_write(tp, 0x14, 0xf0ad); + rtl8168_mdio_write(tp, 0x14, 0x233f); + rtl8168_mdio_write(tp, 0x14, 0xe085); + rtl8168_mdio_write(tp, 0x14, 0xf9e4); + rtl8168_mdio_write(tp, 0x14, 0x8288); + rtl8168_mdio_write(tp, 0x14, 0xe085); + rtl8168_mdio_write(tp, 0x14, 0xfde4); + rtl8168_mdio_write(tp, 0x14, 0x8289); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x440d); + rtl8168_mdio_write(tp, 0x14, 0x0758); + rtl8168_mdio_write(tp, 0x14, 0x01bf); + rtl8168_mdio_write(tp, 0x14, 0x8279); + rtl8168_mdio_write(tp, 0x14, 0x0215); + rtl8168_mdio_write(tp, 0x14, 0x38bf); + rtl8168_mdio_write(tp, 0x14, 0x8251); + rtl8168_mdio_write(tp, 0x14, 0x0213); + rtl8168_mdio_write(tp, 0x14, 0x06a0); + rtl8168_mdio_write(tp, 0x14, 0x010f); + rtl8168_mdio_write(tp, 0x14, 0xe082); + rtl8168_mdio_write(tp, 0x14, 0x44f6); + rtl8168_mdio_write(tp, 0x14, 0x23e4); + rtl8168_mdio_write(tp, 0x14, 0x8244); + rtl8168_mdio_write(tp, 0x14, 0x580f); + rtl8168_mdio_write(tp, 0x14, 0xe582); + rtl8168_mdio_write(tp, 0x14, 0x5dae); + rtl8168_mdio_write(tp, 0x14, 0x0ebf); + rtl8168_mdio_write(tp, 0x14, 0x8273); + rtl8168_mdio_write(tp, 0x14, 0xe382); + rtl8168_mdio_write(tp, 0x14, 0x44f7); + rtl8168_mdio_write(tp, 0x14, 0x3fe7); + rtl8168_mdio_write(tp, 0x14, 0x8244); + rtl8168_mdio_write(tp, 0x14, 0x0212); + rtl8168_mdio_write(tp, 0x14, 0xf0ee); + rtl8168_mdio_write(tp, 0x14, 0x8288); + rtl8168_mdio_write(tp, 0x14, 0x10ee); + rtl8168_mdio_write(tp, 0x14, 0x8289); + rtl8168_mdio_write(tp, 0x14, 0x00af); + rtl8168_mdio_write(tp, 0x14, 0x14aa); + rtl8168_mdio_write(tp, 0x13, 0xb818); + rtl8168_mdio_write(tp, 0x14, 0x13cf); + rtl8168_mdio_write(tp, 0x13, 0xb81a); + rtl8168_mdio_write(tp, 0x14, 0xfffd); + rtl8168_mdio_write(tp, 0x13, 0xb81c); + rtl8168_mdio_write(tp, 0x14, 0xfffd); + rtl8168_mdio_write(tp, 0x13, 0xb81e); + rtl8168_mdio_write(tp, 0x14, 0xfffd); + rtl8168_mdio_write(tp, 0x13, 0xb832); + rtl8168_mdio_write(tp, 0x14, 0x0001); + + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x0000); + rtl8168_mdio_write(tp, 0x14, 0x0000); + rtl8168_mdio_write(tp, 0x1f, 0x0B82); + gphy_val = rtl8168_mdio_read(tp, 0x17); + gphy_val &= ~(BIT_0); + rtl8168_mdio_write(tp, 0x17, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8028); + rtl8168_mdio_write(tp, 0x14, 0x0000); + + rtl8168_clear_phy_mcu_patch_request(tp); + + if (tp->RequiredSecLanDonglePatch) { + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + gphy_val = rtl8168_mdio_read(tp, 0x11); + gphy_val &= ~BIT_6; + rtl8168_mdio_write(tp, 0x11, gphy_val); + } +} + +static void +rtl8168_init_hw_phy_mcu(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u8 require_disable_phy_disable_mode = FALSE; + + if (tp->NotWrRamCodeToMicroP == TRUE) return; + if (rtl8168_check_hw_phy_mcu_code_ver(dev)) return; + + if (FALSE == rtl8168_phy_ram_code_check(dev)) { + rtl8168_set_phy_ram_code_check_fail_flag(dev); + return; + } + + if (HW_SUPPORT_CHECK_PHY_DISABLE_MODE(tp) && rtl8168_is_in_phy_disable_mode(dev)) + require_disable_phy_disable_mode = TRUE; + + if (require_disable_phy_disable_mode) + rtl8168_disable_phy_disable_mode(dev); + + switch (tp->mcfg) { + case CFG_METHOD_14: + rtl8168_set_phy_mcu_8168e_1(dev); + break; + case CFG_METHOD_15: + rtl8168_set_phy_mcu_8168e_2(dev); + break; + case CFG_METHOD_16: + rtl8168_set_phy_mcu_8168evl_1(dev); + break; + case CFG_METHOD_17: + rtl8168_set_phy_mcu_8168evl_2(dev); + break; + case CFG_METHOD_18: + rtl8168_set_phy_mcu_8168f_1(dev); + break; + case CFG_METHOD_19: + rtl8168_set_phy_mcu_8168f_2(dev); + break; + case CFG_METHOD_20: + rtl8168_set_phy_mcu_8411_1(dev); + break; + case CFG_METHOD_21: + rtl8168_set_phy_mcu_8168g_1(dev); + break; + case CFG_METHOD_25: + rtl8168_set_phy_mcu_8168gu_2(dev); + break; + case CFG_METHOD_26: + rtl8168_set_phy_mcu_8411b_1(dev); + break; + case CFG_METHOD_28: + rtl8168_set_phy_mcu_8168ep_2(dev); + break; + case CFG_METHOD_29: + rtl8168_set_phy_mcu_8168h_1(dev); + break; + case CFG_METHOD_30: + rtl8168_set_phy_mcu_8168h_2(dev); + break; + } + + if (require_disable_phy_disable_mode) + rtl8168_enable_phy_disable_mode(dev); + + rtl8168_write_hw_phy_mcu_code_ver(dev); + + rtl8168_mdio_write(tp,0x1F, 0x0000); + + tp->HwHasWrRamCodeToMicroP = TRUE; +} +#endif + +static void +rtl8168_hw_phy_config(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + struct pci_dev *pdev = tp->pci_dev; + u16 gphy_val; + unsigned int i; + + tp->phy_reset_enable(dev); + + if (HW_DASH_SUPPORT_TYPE_3(tp) && tp->HwPkgDet == 0x06) return; + +#ifndef ENABLE_USE_FIRMWARE_FILE + if (!tp->rtl_fw) { + rtl8168_init_hw_phy_mcu(dev); + } +#endif + + if (tp->mcfg == CFG_METHOD_1) { + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x0B, 0x94B0); + + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x12, 0x6096); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x0D, 0xF8A0); + } else if (tp->mcfg == CFG_METHOD_2) { + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x0B, 0x94B0); + + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x12, 0x6096); + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } else if (tp->mcfg == CFG_METHOD_3) { + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x0B, 0x94B0); + + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x12, 0x6096); + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } else if (tp->mcfg == CFG_METHOD_4) { + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x12, 0x2300); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x16, 0x000A); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x12, 0xC096); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x00, 0x88DE); + rtl8168_mdio_write(tp, 0x01, 0x82B1); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x08, 0x9E30); + rtl8168_mdio_write(tp, 0x09, 0x01F0); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x0A, 0x5500); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x03, 0x7002); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x0C, 0x00C8); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x14, rtl8168_mdio_read(tp, 0x14) | (1 << 5)); + rtl8168_mdio_write(tp, 0x0D, rtl8168_mdio_read(tp, 0x0D) & ~(1 << 5)); + } else if (tp->mcfg == CFG_METHOD_5) { + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x12, 0x2300); + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x16, 0x0F0A); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x00, 0x88DE); + rtl8168_mdio_write(tp, 0x01, 0x82B1); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x0C, 0x7EB8); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x06, 0x0761); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x03, 0x802F); + rtl8168_mdio_write(tp, 0x02, 0x4F02); + rtl8168_mdio_write(tp, 0x01, 0x0409); + rtl8168_mdio_write(tp, 0x00, 0xF099); + rtl8168_mdio_write(tp, 0x04, 0x9800); + rtl8168_mdio_write(tp, 0x04, 0x9000); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x16, rtl8168_mdio_read(tp, 0x16) | (1 << 0)); + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x14, rtl8168_mdio_read(tp, 0x14) | (1 << 5)); + rtl8168_mdio_write(tp, 0x0D, rtl8168_mdio_read(tp, 0x0D) & ~(1 << 5)); + + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x1D, 0x3D98); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x17, 0x0CC0); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } else if (tp->mcfg == CFG_METHOD_6) { + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x12, 0x2300); + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x16, 0x0F0A); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x00, 0x88DE); + rtl8168_mdio_write(tp, 0x01, 0x82B1); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x0C, 0x7EB8); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x06, 0x5461); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x06, 0x5461); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x16, rtl8168_mdio_read(tp, 0x16) | (1 << 0)); + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x14, rtl8168_mdio_read(tp, 0x14) | (1 << 5)); + rtl8168_mdio_write(tp, 0x0D, rtl8168_mdio_read(tp, 0x0D) & ~(1 << 5)); + + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x1D, 0x3D98); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1f, 0x0001); + rtl8168_mdio_write(tp, 0x17, 0x0CC0); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } else if (tp->mcfg == CFG_METHOD_7) { + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x14, rtl8168_mdio_read(tp, 0x14) | (1 << 5)); + rtl8168_mdio_write(tp, 0x0D, rtl8168_mdio_read(tp, 0x0D) & ~(1 << 5)); + + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x1D, 0x3D98); + + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x14, 0xCAA3); + rtl8168_mdio_write(tp, 0x1C, 0x000A); + rtl8168_mdio_write(tp, 0x18, 0x65D0); + + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x17, 0xB580); + rtl8168_mdio_write(tp, 0x18, 0xFF54); + rtl8168_mdio_write(tp, 0x19, 0x3954); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x0D, 0x310C); + rtl8168_mdio_write(tp, 0x0E, 0x310C); + rtl8168_mdio_write(tp, 0x0F, 0x311C); + rtl8168_mdio_write(tp, 0x06, 0x0761); + + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x18, 0xFF55); + rtl8168_mdio_write(tp, 0x19, 0x3955); + rtl8168_mdio_write(tp, 0x18, 0xFF54); + rtl8168_mdio_write(tp, 0x19, 0x3954); + + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x17, 0x0CC0); + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } else if (tp->mcfg == CFG_METHOD_8) { + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x14, rtl8168_mdio_read(tp, 0x14) | (1 << 5)); + rtl8168_mdio_write(tp, 0x0D, rtl8168_mdio_read(tp, 0x0D) & ~(1 << 5)); + + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x14, 0xCAA3); + rtl8168_mdio_write(tp, 0x1C, 0x000A); + rtl8168_mdio_write(tp, 0x18, 0x65D0); + + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x17, 0xB580); + rtl8168_mdio_write(tp, 0x18, 0xFF54); + rtl8168_mdio_write(tp, 0x19, 0x3954); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x0D, 0x310C); + rtl8168_mdio_write(tp, 0x0E, 0x310C); + rtl8168_mdio_write(tp, 0x0F, 0x311C); + rtl8168_mdio_write(tp, 0x06, 0x0761); + + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x18, 0xFF55); + rtl8168_mdio_write(tp, 0x19, 0x3955); + rtl8168_mdio_write(tp, 0x18, 0xFF54); + rtl8168_mdio_write(tp, 0x19, 0x3954); + + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x17, 0x0CC0); + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x16, rtl8168_mdio_read(tp, 0x16) | (1 << 0)); + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } else if (tp->mcfg == CFG_METHOD_9) { + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x06, 0x4064); + rtl8168_mdio_write(tp, 0x07, 0x2863); + rtl8168_mdio_write(tp, 0x08, 0x059C); + rtl8168_mdio_write(tp, 0x09, 0x26B4); + rtl8168_mdio_write(tp, 0x0A, 0x6A19); + rtl8168_mdio_write(tp, 0x0B, 0xDCC8); + rtl8168_mdio_write(tp, 0x10, 0xF06D); + rtl8168_mdio_write(tp, 0x14, 0x7F68); + rtl8168_mdio_write(tp, 0x18, 0x7FD9); + rtl8168_mdio_write(tp, 0x1C, 0xF0FF); + rtl8168_mdio_write(tp, 0x1D, 0x3D9C); + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x12, 0xF49F); + rtl8168_mdio_write(tp, 0x13, 0x070B); + rtl8168_mdio_write(tp, 0x1A, 0x05AD); + rtl8168_mdio_write(tp, 0x14, 0x94C0); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + gphy_val = rtl8168_mdio_read(tp, 0x0B) & 0xFF00; + gphy_val |= 0x10; + rtl8168_mdio_write(tp, 0x0B, gphy_val); + gphy_val = rtl8168_mdio_read(tp, 0x0C) & 0x00FF; + gphy_val |= 0xA200; + rtl8168_mdio_write(tp, 0x0C, gphy_val); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x06, 0x5561); + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8332); + rtl8168_mdio_write(tp, 0x06, 0x5561); + + if (rtl8168_efuse_read(tp, 0x01) == 0xb1) { + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x05, 0x669A); + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8330); + rtl8168_mdio_write(tp, 0x06, 0x669A); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + gphy_val = rtl8168_mdio_read(tp, 0x0D); + if ((gphy_val & 0x00FF) != 0x006C) { + gphy_val &= 0xFF00; + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x0D, gphy_val | 0x0065); + rtl8168_mdio_write(tp, 0x0D, gphy_val | 0x0066); + rtl8168_mdio_write(tp, 0x0D, gphy_val | 0x0067); + rtl8168_mdio_write(tp, 0x0D, gphy_val | 0x0068); + rtl8168_mdio_write(tp, 0x0D, gphy_val | 0x0069); + rtl8168_mdio_write(tp, 0x0D, gphy_val | 0x006A); + rtl8168_mdio_write(tp, 0x0D, gphy_val | 0x006B); + rtl8168_mdio_write(tp, 0x0D, gphy_val | 0x006C); + } + } else { + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x05, 0x6662); + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8330); + rtl8168_mdio_write(tp, 0x06, 0x6662); + } + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + gphy_val = rtl8168_mdio_read(tp, 0x0D); + gphy_val |= BIT_9; + gphy_val |= BIT_8; + rtl8168_mdio_write(tp, 0x0D, gphy_val); + gphy_val = rtl8168_mdio_read(tp, 0x0F); + gphy_val |= BIT_4; + rtl8168_mdio_write(tp, 0x0F, gphy_val); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + gphy_val = rtl8168_mdio_read(tp, 0x02); + gphy_val &= ~BIT_10; + gphy_val &= ~BIT_9; + gphy_val |= BIT_8; + rtl8168_mdio_write(tp, 0x02, gphy_val); + gphy_val = rtl8168_mdio_read(tp, 0x03); + gphy_val &= ~BIT_15; + gphy_val &= ~BIT_14; + gphy_val &= ~BIT_13; + rtl8168_mdio_write(tp, 0x03, gphy_val); + + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x17, 0x0CC0); + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x001B); + if (rtl8168_mdio_read(tp, 0x06) == 0xBF00) { + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x0080); + rtl8168_mdio_write(tp, 0x05, 0x8000); + rtl8168_mdio_write(tp, 0x06, 0xf8f9); + rtl8168_mdio_write(tp, 0x06, 0xfaef); + rtl8168_mdio_write(tp, 0x06, 0x59ee); + rtl8168_mdio_write(tp, 0x06, 0xf8ea); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0xf8eb); + rtl8168_mdio_write(tp, 0x06, 0x00e0); + rtl8168_mdio_write(tp, 0x06, 0xf87c); + rtl8168_mdio_write(tp, 0x06, 0xe1f8); + rtl8168_mdio_write(tp, 0x06, 0x7d59); + rtl8168_mdio_write(tp, 0x06, 0x0fef); + rtl8168_mdio_write(tp, 0x06, 0x0139); + rtl8168_mdio_write(tp, 0x06, 0x029e); + rtl8168_mdio_write(tp, 0x06, 0x06ef); + rtl8168_mdio_write(tp, 0x06, 0x1039); + rtl8168_mdio_write(tp, 0x06, 0x089f); + rtl8168_mdio_write(tp, 0x06, 0x2aee); + rtl8168_mdio_write(tp, 0x06, 0xf8ea); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0xf8eb); + rtl8168_mdio_write(tp, 0x06, 0x01e0); + rtl8168_mdio_write(tp, 0x06, 0xf87c); + rtl8168_mdio_write(tp, 0x06, 0xe1f8); + rtl8168_mdio_write(tp, 0x06, 0x7d58); + rtl8168_mdio_write(tp, 0x06, 0x409e); + rtl8168_mdio_write(tp, 0x06, 0x0f39); + rtl8168_mdio_write(tp, 0x06, 0x46aa); + rtl8168_mdio_write(tp, 0x06, 0x0bbf); + rtl8168_mdio_write(tp, 0x06, 0x8290); + rtl8168_mdio_write(tp, 0x06, 0xd682); + rtl8168_mdio_write(tp, 0x06, 0x9802); + rtl8168_mdio_write(tp, 0x06, 0x014f); + rtl8168_mdio_write(tp, 0x06, 0xae09); + rtl8168_mdio_write(tp, 0x06, 0xbf82); + rtl8168_mdio_write(tp, 0x06, 0x98d6); + rtl8168_mdio_write(tp, 0x06, 0x82a0); + rtl8168_mdio_write(tp, 0x06, 0x0201); + rtl8168_mdio_write(tp, 0x06, 0x4fef); + rtl8168_mdio_write(tp, 0x06, 0x95fe); + rtl8168_mdio_write(tp, 0x06, 0xfdfc); + rtl8168_mdio_write(tp, 0x06, 0x05f8); + rtl8168_mdio_write(tp, 0x06, 0xf9fa); + rtl8168_mdio_write(tp, 0x06, 0xeef8); + rtl8168_mdio_write(tp, 0x06, 0xea00); + rtl8168_mdio_write(tp, 0x06, 0xeef8); + rtl8168_mdio_write(tp, 0x06, 0xeb00); + rtl8168_mdio_write(tp, 0x06, 0xe2f8); + rtl8168_mdio_write(tp, 0x06, 0x7ce3); + rtl8168_mdio_write(tp, 0x06, 0xf87d); + rtl8168_mdio_write(tp, 0x06, 0xa511); + rtl8168_mdio_write(tp, 0x06, 0x1112); + rtl8168_mdio_write(tp, 0x06, 0xd240); + rtl8168_mdio_write(tp, 0x06, 0xd644); + rtl8168_mdio_write(tp, 0x06, 0x4402); + rtl8168_mdio_write(tp, 0x06, 0x8217); + rtl8168_mdio_write(tp, 0x06, 0xd2a0); + rtl8168_mdio_write(tp, 0x06, 0xd6aa); + rtl8168_mdio_write(tp, 0x06, 0xaa02); + rtl8168_mdio_write(tp, 0x06, 0x8217); + rtl8168_mdio_write(tp, 0x06, 0xae0f); + rtl8168_mdio_write(tp, 0x06, 0xa544); + rtl8168_mdio_write(tp, 0x06, 0x4402); + rtl8168_mdio_write(tp, 0x06, 0xae4d); + rtl8168_mdio_write(tp, 0x06, 0xa5aa); + rtl8168_mdio_write(tp, 0x06, 0xaa02); + rtl8168_mdio_write(tp, 0x06, 0xae47); + rtl8168_mdio_write(tp, 0x06, 0xaf82); + rtl8168_mdio_write(tp, 0x06, 0x13ee); + rtl8168_mdio_write(tp, 0x06, 0x834e); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0x834d); + rtl8168_mdio_write(tp, 0x06, 0x0fee); + rtl8168_mdio_write(tp, 0x06, 0x834c); + rtl8168_mdio_write(tp, 0x06, 0x0fee); + rtl8168_mdio_write(tp, 0x06, 0x834f); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0x8351); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0x834a); + rtl8168_mdio_write(tp, 0x06, 0xffee); + rtl8168_mdio_write(tp, 0x06, 0x834b); + rtl8168_mdio_write(tp, 0x06, 0xffe0); + rtl8168_mdio_write(tp, 0x06, 0x8330); + rtl8168_mdio_write(tp, 0x06, 0xe183); + rtl8168_mdio_write(tp, 0x06, 0x3158); + rtl8168_mdio_write(tp, 0x06, 0xfee4); + rtl8168_mdio_write(tp, 0x06, 0xf88a); + rtl8168_mdio_write(tp, 0x06, 0xe5f8); + rtl8168_mdio_write(tp, 0x06, 0x8be0); + rtl8168_mdio_write(tp, 0x06, 0x8332); + rtl8168_mdio_write(tp, 0x06, 0xe183); + rtl8168_mdio_write(tp, 0x06, 0x3359); + rtl8168_mdio_write(tp, 0x06, 0x0fe2); + rtl8168_mdio_write(tp, 0x06, 0x834d); + rtl8168_mdio_write(tp, 0x06, 0x0c24); + rtl8168_mdio_write(tp, 0x06, 0x5af0); + rtl8168_mdio_write(tp, 0x06, 0x1e12); + rtl8168_mdio_write(tp, 0x06, 0xe4f8); + rtl8168_mdio_write(tp, 0x06, 0x8ce5); + rtl8168_mdio_write(tp, 0x06, 0xf88d); + rtl8168_mdio_write(tp, 0x06, 0xaf82); + rtl8168_mdio_write(tp, 0x06, 0x13e0); + rtl8168_mdio_write(tp, 0x06, 0x834f); + rtl8168_mdio_write(tp, 0x06, 0x10e4); + rtl8168_mdio_write(tp, 0x06, 0x834f); + rtl8168_mdio_write(tp, 0x06, 0xe083); + rtl8168_mdio_write(tp, 0x06, 0x4e78); + rtl8168_mdio_write(tp, 0x06, 0x009f); + rtl8168_mdio_write(tp, 0x06, 0x0ae0); + rtl8168_mdio_write(tp, 0x06, 0x834f); + rtl8168_mdio_write(tp, 0x06, 0xa010); + rtl8168_mdio_write(tp, 0x06, 0xa5ee); + rtl8168_mdio_write(tp, 0x06, 0x834e); + rtl8168_mdio_write(tp, 0x06, 0x01e0); + rtl8168_mdio_write(tp, 0x06, 0x834e); + rtl8168_mdio_write(tp, 0x06, 0x7805); + rtl8168_mdio_write(tp, 0x06, 0x9e9a); + rtl8168_mdio_write(tp, 0x06, 0xe083); + rtl8168_mdio_write(tp, 0x06, 0x4e78); + rtl8168_mdio_write(tp, 0x06, 0x049e); + rtl8168_mdio_write(tp, 0x06, 0x10e0); + rtl8168_mdio_write(tp, 0x06, 0x834e); + rtl8168_mdio_write(tp, 0x06, 0x7803); + rtl8168_mdio_write(tp, 0x06, 0x9e0f); + rtl8168_mdio_write(tp, 0x06, 0xe083); + rtl8168_mdio_write(tp, 0x06, 0x4e78); + rtl8168_mdio_write(tp, 0x06, 0x019e); + rtl8168_mdio_write(tp, 0x06, 0x05ae); + rtl8168_mdio_write(tp, 0x06, 0x0caf); + rtl8168_mdio_write(tp, 0x06, 0x81f8); + rtl8168_mdio_write(tp, 0x06, 0xaf81); + rtl8168_mdio_write(tp, 0x06, 0xa3af); + rtl8168_mdio_write(tp, 0x06, 0x81dc); + rtl8168_mdio_write(tp, 0x06, 0xaf82); + rtl8168_mdio_write(tp, 0x06, 0x13ee); + rtl8168_mdio_write(tp, 0x06, 0x8348); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0x8349); + rtl8168_mdio_write(tp, 0x06, 0x00e0); + rtl8168_mdio_write(tp, 0x06, 0x8351); + rtl8168_mdio_write(tp, 0x06, 0x10e4); + rtl8168_mdio_write(tp, 0x06, 0x8351); + rtl8168_mdio_write(tp, 0x06, 0x5801); + rtl8168_mdio_write(tp, 0x06, 0x9fea); + rtl8168_mdio_write(tp, 0x06, 0xd000); + rtl8168_mdio_write(tp, 0x06, 0xd180); + rtl8168_mdio_write(tp, 0x06, 0x1f66); + rtl8168_mdio_write(tp, 0x06, 0xe2f8); + rtl8168_mdio_write(tp, 0x06, 0xeae3); + rtl8168_mdio_write(tp, 0x06, 0xf8eb); + rtl8168_mdio_write(tp, 0x06, 0x5af8); + rtl8168_mdio_write(tp, 0x06, 0x1e20); + rtl8168_mdio_write(tp, 0x06, 0xe6f8); + rtl8168_mdio_write(tp, 0x06, 0xeae5); + rtl8168_mdio_write(tp, 0x06, 0xf8eb); + rtl8168_mdio_write(tp, 0x06, 0xd302); + rtl8168_mdio_write(tp, 0x06, 0xb3fe); + rtl8168_mdio_write(tp, 0x06, 0xe2f8); + rtl8168_mdio_write(tp, 0x06, 0x7cef); + rtl8168_mdio_write(tp, 0x06, 0x325b); + rtl8168_mdio_write(tp, 0x06, 0x80e3); + rtl8168_mdio_write(tp, 0x06, 0xf87d); + rtl8168_mdio_write(tp, 0x06, 0x9e03); + rtl8168_mdio_write(tp, 0x06, 0x7dff); + rtl8168_mdio_write(tp, 0x06, 0xff0d); + rtl8168_mdio_write(tp, 0x06, 0x581c); + rtl8168_mdio_write(tp, 0x06, 0x551a); + rtl8168_mdio_write(tp, 0x06, 0x6511); + rtl8168_mdio_write(tp, 0x06, 0xa190); + rtl8168_mdio_write(tp, 0x06, 0xd3e2); + rtl8168_mdio_write(tp, 0x06, 0x8348); + rtl8168_mdio_write(tp, 0x06, 0xe383); + rtl8168_mdio_write(tp, 0x06, 0x491b); + rtl8168_mdio_write(tp, 0x06, 0x56ab); + rtl8168_mdio_write(tp, 0x06, 0x08ef); + rtl8168_mdio_write(tp, 0x06, 0x56e6); + rtl8168_mdio_write(tp, 0x06, 0x8348); + rtl8168_mdio_write(tp, 0x06, 0xe783); + rtl8168_mdio_write(tp, 0x06, 0x4910); + rtl8168_mdio_write(tp, 0x06, 0xd180); + rtl8168_mdio_write(tp, 0x06, 0x1f66); + rtl8168_mdio_write(tp, 0x06, 0xa004); + rtl8168_mdio_write(tp, 0x06, 0xb9e2); + rtl8168_mdio_write(tp, 0x06, 0x8348); + rtl8168_mdio_write(tp, 0x06, 0xe383); + rtl8168_mdio_write(tp, 0x06, 0x49ef); + rtl8168_mdio_write(tp, 0x06, 0x65e2); + rtl8168_mdio_write(tp, 0x06, 0x834a); + rtl8168_mdio_write(tp, 0x06, 0xe383); + rtl8168_mdio_write(tp, 0x06, 0x4b1b); + rtl8168_mdio_write(tp, 0x06, 0x56aa); + rtl8168_mdio_write(tp, 0x06, 0x0eef); + rtl8168_mdio_write(tp, 0x06, 0x56e6); + rtl8168_mdio_write(tp, 0x06, 0x834a); + rtl8168_mdio_write(tp, 0x06, 0xe783); + rtl8168_mdio_write(tp, 0x06, 0x4be2); + rtl8168_mdio_write(tp, 0x06, 0x834d); + rtl8168_mdio_write(tp, 0x06, 0xe683); + rtl8168_mdio_write(tp, 0x06, 0x4ce0); + rtl8168_mdio_write(tp, 0x06, 0x834d); + rtl8168_mdio_write(tp, 0x06, 0xa000); + rtl8168_mdio_write(tp, 0x06, 0x0caf); + rtl8168_mdio_write(tp, 0x06, 0x81dc); + rtl8168_mdio_write(tp, 0x06, 0xe083); + rtl8168_mdio_write(tp, 0x06, 0x4d10); + rtl8168_mdio_write(tp, 0x06, 0xe483); + rtl8168_mdio_write(tp, 0x06, 0x4dae); + rtl8168_mdio_write(tp, 0x06, 0x0480); + rtl8168_mdio_write(tp, 0x06, 0xe483); + rtl8168_mdio_write(tp, 0x06, 0x4de0); + rtl8168_mdio_write(tp, 0x06, 0x834e); + rtl8168_mdio_write(tp, 0x06, 0x7803); + rtl8168_mdio_write(tp, 0x06, 0x9e0b); + rtl8168_mdio_write(tp, 0x06, 0xe083); + rtl8168_mdio_write(tp, 0x06, 0x4e78); + rtl8168_mdio_write(tp, 0x06, 0x049e); + rtl8168_mdio_write(tp, 0x06, 0x04ee); + rtl8168_mdio_write(tp, 0x06, 0x834e); + rtl8168_mdio_write(tp, 0x06, 0x02e0); + rtl8168_mdio_write(tp, 0x06, 0x8332); + rtl8168_mdio_write(tp, 0x06, 0xe183); + rtl8168_mdio_write(tp, 0x06, 0x3359); + rtl8168_mdio_write(tp, 0x06, 0x0fe2); + rtl8168_mdio_write(tp, 0x06, 0x834d); + rtl8168_mdio_write(tp, 0x06, 0x0c24); + rtl8168_mdio_write(tp, 0x06, 0x5af0); + rtl8168_mdio_write(tp, 0x06, 0x1e12); + rtl8168_mdio_write(tp, 0x06, 0xe4f8); + rtl8168_mdio_write(tp, 0x06, 0x8ce5); + rtl8168_mdio_write(tp, 0x06, 0xf88d); + rtl8168_mdio_write(tp, 0x06, 0xe083); + rtl8168_mdio_write(tp, 0x06, 0x30e1); + rtl8168_mdio_write(tp, 0x06, 0x8331); + rtl8168_mdio_write(tp, 0x06, 0x6801); + rtl8168_mdio_write(tp, 0x06, 0xe4f8); + rtl8168_mdio_write(tp, 0x06, 0x8ae5); + rtl8168_mdio_write(tp, 0x06, 0xf88b); + rtl8168_mdio_write(tp, 0x06, 0xae37); + rtl8168_mdio_write(tp, 0x06, 0xee83); + rtl8168_mdio_write(tp, 0x06, 0x4e03); + rtl8168_mdio_write(tp, 0x06, 0xe083); + rtl8168_mdio_write(tp, 0x06, 0x4ce1); + rtl8168_mdio_write(tp, 0x06, 0x834d); + rtl8168_mdio_write(tp, 0x06, 0x1b01); + rtl8168_mdio_write(tp, 0x06, 0x9e04); + rtl8168_mdio_write(tp, 0x06, 0xaaa1); + rtl8168_mdio_write(tp, 0x06, 0xaea8); + rtl8168_mdio_write(tp, 0x06, 0xee83); + rtl8168_mdio_write(tp, 0x06, 0x4e04); + rtl8168_mdio_write(tp, 0x06, 0xee83); + rtl8168_mdio_write(tp, 0x06, 0x4f00); + rtl8168_mdio_write(tp, 0x06, 0xaeab); + rtl8168_mdio_write(tp, 0x06, 0xe083); + rtl8168_mdio_write(tp, 0x06, 0x4f78); + rtl8168_mdio_write(tp, 0x06, 0x039f); + rtl8168_mdio_write(tp, 0x06, 0x14ee); + rtl8168_mdio_write(tp, 0x06, 0x834e); + rtl8168_mdio_write(tp, 0x06, 0x05d2); + rtl8168_mdio_write(tp, 0x06, 0x40d6); + rtl8168_mdio_write(tp, 0x06, 0x5554); + rtl8168_mdio_write(tp, 0x06, 0x0282); + rtl8168_mdio_write(tp, 0x06, 0x17d2); + rtl8168_mdio_write(tp, 0x06, 0xa0d6); + rtl8168_mdio_write(tp, 0x06, 0xba00); + rtl8168_mdio_write(tp, 0x06, 0x0282); + rtl8168_mdio_write(tp, 0x06, 0x17fe); + rtl8168_mdio_write(tp, 0x06, 0xfdfc); + rtl8168_mdio_write(tp, 0x06, 0x05f8); + rtl8168_mdio_write(tp, 0x06, 0xe0f8); + rtl8168_mdio_write(tp, 0x06, 0x60e1); + rtl8168_mdio_write(tp, 0x06, 0xf861); + rtl8168_mdio_write(tp, 0x06, 0x6802); + rtl8168_mdio_write(tp, 0x06, 0xe4f8); + rtl8168_mdio_write(tp, 0x06, 0x60e5); + rtl8168_mdio_write(tp, 0x06, 0xf861); + rtl8168_mdio_write(tp, 0x06, 0xe0f8); + rtl8168_mdio_write(tp, 0x06, 0x48e1); + rtl8168_mdio_write(tp, 0x06, 0xf849); + rtl8168_mdio_write(tp, 0x06, 0x580f); + rtl8168_mdio_write(tp, 0x06, 0x1e02); + rtl8168_mdio_write(tp, 0x06, 0xe4f8); + rtl8168_mdio_write(tp, 0x06, 0x48e5); + rtl8168_mdio_write(tp, 0x06, 0xf849); + rtl8168_mdio_write(tp, 0x06, 0xd000); + rtl8168_mdio_write(tp, 0x06, 0x0282); + rtl8168_mdio_write(tp, 0x06, 0x5bbf); + rtl8168_mdio_write(tp, 0x06, 0x8350); + rtl8168_mdio_write(tp, 0x06, 0xef46); + rtl8168_mdio_write(tp, 0x06, 0xdc19); + rtl8168_mdio_write(tp, 0x06, 0xddd0); + rtl8168_mdio_write(tp, 0x06, 0x0102); + rtl8168_mdio_write(tp, 0x06, 0x825b); + rtl8168_mdio_write(tp, 0x06, 0x0282); + rtl8168_mdio_write(tp, 0x06, 0x77e0); + rtl8168_mdio_write(tp, 0x06, 0xf860); + rtl8168_mdio_write(tp, 0x06, 0xe1f8); + rtl8168_mdio_write(tp, 0x06, 0x6158); + rtl8168_mdio_write(tp, 0x06, 0xfde4); + rtl8168_mdio_write(tp, 0x06, 0xf860); + rtl8168_mdio_write(tp, 0x06, 0xe5f8); + rtl8168_mdio_write(tp, 0x06, 0x61fc); + rtl8168_mdio_write(tp, 0x06, 0x04f9); + rtl8168_mdio_write(tp, 0x06, 0xfafb); + rtl8168_mdio_write(tp, 0x06, 0xc6bf); + rtl8168_mdio_write(tp, 0x06, 0xf840); + rtl8168_mdio_write(tp, 0x06, 0xbe83); + rtl8168_mdio_write(tp, 0x06, 0x50a0); + rtl8168_mdio_write(tp, 0x06, 0x0101); + rtl8168_mdio_write(tp, 0x06, 0x071b); + rtl8168_mdio_write(tp, 0x06, 0x89cf); + rtl8168_mdio_write(tp, 0x06, 0xd208); + rtl8168_mdio_write(tp, 0x06, 0xebdb); + rtl8168_mdio_write(tp, 0x06, 0x19b2); + rtl8168_mdio_write(tp, 0x06, 0xfbff); + rtl8168_mdio_write(tp, 0x06, 0xfefd); + rtl8168_mdio_write(tp, 0x06, 0x04f8); + rtl8168_mdio_write(tp, 0x06, 0xe0f8); + rtl8168_mdio_write(tp, 0x06, 0x48e1); + rtl8168_mdio_write(tp, 0x06, 0xf849); + rtl8168_mdio_write(tp, 0x06, 0x6808); + rtl8168_mdio_write(tp, 0x06, 0xe4f8); + rtl8168_mdio_write(tp, 0x06, 0x48e5); + rtl8168_mdio_write(tp, 0x06, 0xf849); + rtl8168_mdio_write(tp, 0x06, 0x58f7); + rtl8168_mdio_write(tp, 0x06, 0xe4f8); + rtl8168_mdio_write(tp, 0x06, 0x48e5); + rtl8168_mdio_write(tp, 0x06, 0xf849); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0x4d20); + rtl8168_mdio_write(tp, 0x06, 0x0002); + rtl8168_mdio_write(tp, 0x06, 0x4e22); + rtl8168_mdio_write(tp, 0x06, 0x0002); + rtl8168_mdio_write(tp, 0x06, 0x4ddf); + rtl8168_mdio_write(tp, 0x06, 0xff01); + rtl8168_mdio_write(tp, 0x06, 0x4edd); + rtl8168_mdio_write(tp, 0x06, 0xff01); + rtl8168_mdio_write(tp, 0x06, 0xf8fa); + rtl8168_mdio_write(tp, 0x06, 0xfbef); + rtl8168_mdio_write(tp, 0x06, 0x79bf); + rtl8168_mdio_write(tp, 0x06, 0xf822); + rtl8168_mdio_write(tp, 0x06, 0xd819); + rtl8168_mdio_write(tp, 0x06, 0xd958); + rtl8168_mdio_write(tp, 0x06, 0x849f); + rtl8168_mdio_write(tp, 0x06, 0x09bf); + rtl8168_mdio_write(tp, 0x06, 0x82be); + rtl8168_mdio_write(tp, 0x06, 0xd682); + rtl8168_mdio_write(tp, 0x06, 0xc602); + rtl8168_mdio_write(tp, 0x06, 0x014f); + rtl8168_mdio_write(tp, 0x06, 0xef97); + rtl8168_mdio_write(tp, 0x06, 0xfffe); + rtl8168_mdio_write(tp, 0x06, 0xfc05); + rtl8168_mdio_write(tp, 0x06, 0x17ff); + rtl8168_mdio_write(tp, 0x06, 0xfe01); + rtl8168_mdio_write(tp, 0x06, 0x1700); + rtl8168_mdio_write(tp, 0x06, 0x0102); + rtl8168_mdio_write(tp, 0x05, 0x83d8); + rtl8168_mdio_write(tp, 0x06, 0x8051); + rtl8168_mdio_write(tp, 0x05, 0x83d6); + rtl8168_mdio_write(tp, 0x06, 0x82a0); + rtl8168_mdio_write(tp, 0x05, 0x83d4); + rtl8168_mdio_write(tp, 0x06, 0x8000); + rtl8168_mdio_write(tp, 0x02, 0x2010); + rtl8168_mdio_write(tp, 0x03, 0xdc00); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x0b, 0x0600); + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x00fc); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + } + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x0D, 0xF880); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } else if (tp->mcfg == CFG_METHOD_10) { + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x06, 0x4064); + rtl8168_mdio_write(tp, 0x07, 0x2863); + rtl8168_mdio_write(tp, 0x08, 0x059C); + rtl8168_mdio_write(tp, 0x09, 0x26B4); + rtl8168_mdio_write(tp, 0x0A, 0x6A19); + rtl8168_mdio_write(tp, 0x0B, 0xDCC8); + rtl8168_mdio_write(tp, 0x10, 0xF06D); + rtl8168_mdio_write(tp, 0x14, 0x7F68); + rtl8168_mdio_write(tp, 0x18, 0x7FD9); + rtl8168_mdio_write(tp, 0x1C, 0xF0FF); + rtl8168_mdio_write(tp, 0x1D, 0x3D9C); + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x12, 0xF49F); + rtl8168_mdio_write(tp, 0x13, 0x070B); + rtl8168_mdio_write(tp, 0x1A, 0x05AD); + rtl8168_mdio_write(tp, 0x14, 0x94C0); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x06, 0x5561); + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8332); + rtl8168_mdio_write(tp, 0x06, 0x5561); + + if (rtl8168_efuse_read(tp, 0x01) == 0xb1) { + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x05, 0x669A); + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8330); + rtl8168_mdio_write(tp, 0x06, 0x669A); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + gphy_val = rtl8168_mdio_read(tp, 0x0D); + if ((gphy_val & 0x00FF) != 0x006C) { + gphy_val &= 0xFF00; + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x0D, gphy_val | 0x0065); + rtl8168_mdio_write(tp, 0x0D, gphy_val | 0x0066); + rtl8168_mdio_write(tp, 0x0D, gphy_val | 0x0067); + rtl8168_mdio_write(tp, 0x0D, gphy_val | 0x0068); + rtl8168_mdio_write(tp, 0x0D, gphy_val | 0x0069); + rtl8168_mdio_write(tp, 0x0D, gphy_val | 0x006A); + rtl8168_mdio_write(tp, 0x0D, gphy_val | 0x006B); + rtl8168_mdio_write(tp, 0x0D, gphy_val | 0x006C); + } + } else { + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x05, 0x2642); + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8330); + rtl8168_mdio_write(tp, 0x06, 0x2642); + } + + if (rtl8168_efuse_read(tp, 0x30) == 0x98) { + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x11, rtl8168_mdio_read(tp, 0x11) & ~BIT_1); + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x01, rtl8168_mdio_read(tp, 0x01) | BIT_9); + } else if (rtl8168_efuse_read(tp, 0x30) == 0x90) { + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x01, rtl8168_mdio_read(tp, 0x01) & ~BIT_9); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x16, 0x5101); + } + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + gphy_val = rtl8168_mdio_read(tp, 0x02); + gphy_val &= ~BIT_10; + gphy_val &= ~BIT_9; + gphy_val |= BIT_8; + rtl8168_mdio_write(tp, 0x02, gphy_val); + gphy_val = rtl8168_mdio_read(tp, 0x03); + gphy_val &= ~BIT_15; + gphy_val &= ~BIT_14; + gphy_val &= ~BIT_13; + rtl8168_mdio_write(tp, 0x03, gphy_val); + + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x17, 0x0CC0); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + gphy_val = rtl8168_mdio_read(tp, 0x0F); + gphy_val |= BIT_4; + gphy_val |= BIT_2; + gphy_val |= BIT_1; + gphy_val |= BIT_0; + rtl8168_mdio_write(tp, 0x0F, gphy_val); + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x001B); + if (rtl8168_mdio_read(tp, 0x06) == 0xB300) { + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x0080); + rtl8168_mdio_write(tp, 0x05, 0x8000); + rtl8168_mdio_write(tp, 0x06, 0xf8f9); + rtl8168_mdio_write(tp, 0x06, 0xfaee); + rtl8168_mdio_write(tp, 0x06, 0xf8ea); + rtl8168_mdio_write(tp, 0x06, 0x00ee); + rtl8168_mdio_write(tp, 0x06, 0xf8eb); + rtl8168_mdio_write(tp, 0x06, 0x00e2); + rtl8168_mdio_write(tp, 0x06, 0xf87c); + rtl8168_mdio_write(tp, 0x06, 0xe3f8); + rtl8168_mdio_write(tp, 0x06, 0x7da5); + rtl8168_mdio_write(tp, 0x06, 0x1111); + rtl8168_mdio_write(tp, 0x06, 0x12d2); + rtl8168_mdio_write(tp, 0x06, 0x40d6); + rtl8168_mdio_write(tp, 0x06, 0x4444); + rtl8168_mdio_write(tp, 0x06, 0x0281); + rtl8168_mdio_write(tp, 0x06, 0xc6d2); + rtl8168_mdio_write(tp, 0x06, 0xa0d6); + rtl8168_mdio_write(tp, 0x06, 0xaaaa); + rtl8168_mdio_write(tp, 0x06, 0x0281); + rtl8168_mdio_write(tp, 0x06, 0xc6ae); + rtl8168_mdio_write(tp, 0x06, 0x0fa5); + rtl8168_mdio_write(tp, 0x06, 0x4444); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0x4da5); + rtl8168_mdio_write(tp, 0x06, 0xaaaa); + rtl8168_mdio_write(tp, 0x06, 0x02ae); + rtl8168_mdio_write(tp, 0x06, 0x47af); + rtl8168_mdio_write(tp, 0x06, 0x81c2); + rtl8168_mdio_write(tp, 0x06, 0xee83); + rtl8168_mdio_write(tp, 0x06, 0x4e00); + rtl8168_mdio_write(tp, 0x06, 0xee83); + rtl8168_mdio_write(tp, 0x06, 0x4d0f); + rtl8168_mdio_write(tp, 0x06, 0xee83); + rtl8168_mdio_write(tp, 0x06, 0x4c0f); + rtl8168_mdio_write(tp, 0x06, 0xee83); + rtl8168_mdio_write(tp, 0x06, 0x4f00); + rtl8168_mdio_write(tp, 0x06, 0xee83); + rtl8168_mdio_write(tp, 0x06, 0x5100); + rtl8168_mdio_write(tp, 0x06, 0xee83); + rtl8168_mdio_write(tp, 0x06, 0x4aff); + rtl8168_mdio_write(tp, 0x06, 0xee83); + rtl8168_mdio_write(tp, 0x06, 0x4bff); + rtl8168_mdio_write(tp, 0x06, 0xe083); + rtl8168_mdio_write(tp, 0x06, 0x30e1); + rtl8168_mdio_write(tp, 0x06, 0x8331); + rtl8168_mdio_write(tp, 0x06, 0x58fe); + rtl8168_mdio_write(tp, 0x06, 0xe4f8); + rtl8168_mdio_write(tp, 0x06, 0x8ae5); + rtl8168_mdio_write(tp, 0x06, 0xf88b); + rtl8168_mdio_write(tp, 0x06, 0xe083); + rtl8168_mdio_write(tp, 0x06, 0x32e1); + rtl8168_mdio_write(tp, 0x06, 0x8333); + rtl8168_mdio_write(tp, 0x06, 0x590f); + rtl8168_mdio_write(tp, 0x06, 0xe283); + rtl8168_mdio_write(tp, 0x06, 0x4d0c); + rtl8168_mdio_write(tp, 0x06, 0x245a); + rtl8168_mdio_write(tp, 0x06, 0xf01e); + rtl8168_mdio_write(tp, 0x06, 0x12e4); + rtl8168_mdio_write(tp, 0x06, 0xf88c); + rtl8168_mdio_write(tp, 0x06, 0xe5f8); + rtl8168_mdio_write(tp, 0x06, 0x8daf); + rtl8168_mdio_write(tp, 0x06, 0x81c2); + rtl8168_mdio_write(tp, 0x06, 0xe083); + rtl8168_mdio_write(tp, 0x06, 0x4f10); + rtl8168_mdio_write(tp, 0x06, 0xe483); + rtl8168_mdio_write(tp, 0x06, 0x4fe0); + rtl8168_mdio_write(tp, 0x06, 0x834e); + rtl8168_mdio_write(tp, 0x06, 0x7800); + rtl8168_mdio_write(tp, 0x06, 0x9f0a); + rtl8168_mdio_write(tp, 0x06, 0xe083); + rtl8168_mdio_write(tp, 0x06, 0x4fa0); + rtl8168_mdio_write(tp, 0x06, 0x10a5); + rtl8168_mdio_write(tp, 0x06, 0xee83); + rtl8168_mdio_write(tp, 0x06, 0x4e01); + rtl8168_mdio_write(tp, 0x06, 0xe083); + rtl8168_mdio_write(tp, 0x06, 0x4e78); + rtl8168_mdio_write(tp, 0x06, 0x059e); + rtl8168_mdio_write(tp, 0x06, 0x9ae0); + rtl8168_mdio_write(tp, 0x06, 0x834e); + rtl8168_mdio_write(tp, 0x06, 0x7804); + rtl8168_mdio_write(tp, 0x06, 0x9e10); + rtl8168_mdio_write(tp, 0x06, 0xe083); + rtl8168_mdio_write(tp, 0x06, 0x4e78); + rtl8168_mdio_write(tp, 0x06, 0x039e); + rtl8168_mdio_write(tp, 0x06, 0x0fe0); + rtl8168_mdio_write(tp, 0x06, 0x834e); + rtl8168_mdio_write(tp, 0x06, 0x7801); + rtl8168_mdio_write(tp, 0x06, 0x9e05); + rtl8168_mdio_write(tp, 0x06, 0xae0c); + rtl8168_mdio_write(tp, 0x06, 0xaf81); + rtl8168_mdio_write(tp, 0x06, 0xa7af); + rtl8168_mdio_write(tp, 0x06, 0x8152); + rtl8168_mdio_write(tp, 0x06, 0xaf81); + rtl8168_mdio_write(tp, 0x06, 0x8baf); + rtl8168_mdio_write(tp, 0x06, 0x81c2); + rtl8168_mdio_write(tp, 0x06, 0xee83); + rtl8168_mdio_write(tp, 0x06, 0x4800); + rtl8168_mdio_write(tp, 0x06, 0xee83); + rtl8168_mdio_write(tp, 0x06, 0x4900); + rtl8168_mdio_write(tp, 0x06, 0xe083); + rtl8168_mdio_write(tp, 0x06, 0x5110); + rtl8168_mdio_write(tp, 0x06, 0xe483); + rtl8168_mdio_write(tp, 0x06, 0x5158); + rtl8168_mdio_write(tp, 0x06, 0x019f); + rtl8168_mdio_write(tp, 0x06, 0xead0); + rtl8168_mdio_write(tp, 0x06, 0x00d1); + rtl8168_mdio_write(tp, 0x06, 0x801f); + rtl8168_mdio_write(tp, 0x06, 0x66e2); + rtl8168_mdio_write(tp, 0x06, 0xf8ea); + rtl8168_mdio_write(tp, 0x06, 0xe3f8); + rtl8168_mdio_write(tp, 0x06, 0xeb5a); + rtl8168_mdio_write(tp, 0x06, 0xf81e); + rtl8168_mdio_write(tp, 0x06, 0x20e6); + rtl8168_mdio_write(tp, 0x06, 0xf8ea); + rtl8168_mdio_write(tp, 0x06, 0xe5f8); + rtl8168_mdio_write(tp, 0x06, 0xebd3); + rtl8168_mdio_write(tp, 0x06, 0x02b3); + rtl8168_mdio_write(tp, 0x06, 0xfee2); + rtl8168_mdio_write(tp, 0x06, 0xf87c); + rtl8168_mdio_write(tp, 0x06, 0xef32); + rtl8168_mdio_write(tp, 0x06, 0x5b80); + rtl8168_mdio_write(tp, 0x06, 0xe3f8); + rtl8168_mdio_write(tp, 0x06, 0x7d9e); + rtl8168_mdio_write(tp, 0x06, 0x037d); + rtl8168_mdio_write(tp, 0x06, 0xffff); + rtl8168_mdio_write(tp, 0x06, 0x0d58); + rtl8168_mdio_write(tp, 0x06, 0x1c55); + rtl8168_mdio_write(tp, 0x06, 0x1a65); + rtl8168_mdio_write(tp, 0x06, 0x11a1); + rtl8168_mdio_write(tp, 0x06, 0x90d3); + rtl8168_mdio_write(tp, 0x06, 0xe283); + rtl8168_mdio_write(tp, 0x06, 0x48e3); + rtl8168_mdio_write(tp, 0x06, 0x8349); + rtl8168_mdio_write(tp, 0x06, 0x1b56); + rtl8168_mdio_write(tp, 0x06, 0xab08); + rtl8168_mdio_write(tp, 0x06, 0xef56); + rtl8168_mdio_write(tp, 0x06, 0xe683); + rtl8168_mdio_write(tp, 0x06, 0x48e7); + rtl8168_mdio_write(tp, 0x06, 0x8349); + rtl8168_mdio_write(tp, 0x06, 0x10d1); + rtl8168_mdio_write(tp, 0x06, 0x801f); + rtl8168_mdio_write(tp, 0x06, 0x66a0); + rtl8168_mdio_write(tp, 0x06, 0x04b9); + rtl8168_mdio_write(tp, 0x06, 0xe283); + rtl8168_mdio_write(tp, 0x06, 0x48e3); + rtl8168_mdio_write(tp, 0x06, 0x8349); + rtl8168_mdio_write(tp, 0x06, 0xef65); + rtl8168_mdio_write(tp, 0x06, 0xe283); + rtl8168_mdio_write(tp, 0x06, 0x4ae3); + rtl8168_mdio_write(tp, 0x06, 0x834b); + rtl8168_mdio_write(tp, 0x06, 0x1b56); + rtl8168_mdio_write(tp, 0x06, 0xaa0e); + rtl8168_mdio_write(tp, 0x06, 0xef56); + rtl8168_mdio_write(tp, 0x06, 0xe683); + rtl8168_mdio_write(tp, 0x06, 0x4ae7); + rtl8168_mdio_write(tp, 0x06, 0x834b); + rtl8168_mdio_write(tp, 0x06, 0xe283); + rtl8168_mdio_write(tp, 0x06, 0x4de6); + rtl8168_mdio_write(tp, 0x06, 0x834c); + rtl8168_mdio_write(tp, 0x06, 0xe083); + rtl8168_mdio_write(tp, 0x06, 0x4da0); + rtl8168_mdio_write(tp, 0x06, 0x000c); + rtl8168_mdio_write(tp, 0x06, 0xaf81); + rtl8168_mdio_write(tp, 0x06, 0x8be0); + rtl8168_mdio_write(tp, 0x06, 0x834d); + rtl8168_mdio_write(tp, 0x06, 0x10e4); + rtl8168_mdio_write(tp, 0x06, 0x834d); + rtl8168_mdio_write(tp, 0x06, 0xae04); + rtl8168_mdio_write(tp, 0x06, 0x80e4); + rtl8168_mdio_write(tp, 0x06, 0x834d); + rtl8168_mdio_write(tp, 0x06, 0xe083); + rtl8168_mdio_write(tp, 0x06, 0x4e78); + rtl8168_mdio_write(tp, 0x06, 0x039e); + rtl8168_mdio_write(tp, 0x06, 0x0be0); + rtl8168_mdio_write(tp, 0x06, 0x834e); + rtl8168_mdio_write(tp, 0x06, 0x7804); + rtl8168_mdio_write(tp, 0x06, 0x9e04); + rtl8168_mdio_write(tp, 0x06, 0xee83); + rtl8168_mdio_write(tp, 0x06, 0x4e02); + rtl8168_mdio_write(tp, 0x06, 0xe083); + rtl8168_mdio_write(tp, 0x06, 0x32e1); + rtl8168_mdio_write(tp, 0x06, 0x8333); + rtl8168_mdio_write(tp, 0x06, 0x590f); + rtl8168_mdio_write(tp, 0x06, 0xe283); + rtl8168_mdio_write(tp, 0x06, 0x4d0c); + rtl8168_mdio_write(tp, 0x06, 0x245a); + rtl8168_mdio_write(tp, 0x06, 0xf01e); + rtl8168_mdio_write(tp, 0x06, 0x12e4); + rtl8168_mdio_write(tp, 0x06, 0xf88c); + rtl8168_mdio_write(tp, 0x06, 0xe5f8); + rtl8168_mdio_write(tp, 0x06, 0x8de0); + rtl8168_mdio_write(tp, 0x06, 0x8330); + rtl8168_mdio_write(tp, 0x06, 0xe183); + rtl8168_mdio_write(tp, 0x06, 0x3168); + rtl8168_mdio_write(tp, 0x06, 0x01e4); + rtl8168_mdio_write(tp, 0x06, 0xf88a); + rtl8168_mdio_write(tp, 0x06, 0xe5f8); + rtl8168_mdio_write(tp, 0x06, 0x8bae); + rtl8168_mdio_write(tp, 0x06, 0x37ee); + rtl8168_mdio_write(tp, 0x06, 0x834e); + rtl8168_mdio_write(tp, 0x06, 0x03e0); + rtl8168_mdio_write(tp, 0x06, 0x834c); + rtl8168_mdio_write(tp, 0x06, 0xe183); + rtl8168_mdio_write(tp, 0x06, 0x4d1b); + rtl8168_mdio_write(tp, 0x06, 0x019e); + rtl8168_mdio_write(tp, 0x06, 0x04aa); + rtl8168_mdio_write(tp, 0x06, 0xa1ae); + rtl8168_mdio_write(tp, 0x06, 0xa8ee); + rtl8168_mdio_write(tp, 0x06, 0x834e); + rtl8168_mdio_write(tp, 0x06, 0x04ee); + rtl8168_mdio_write(tp, 0x06, 0x834f); + rtl8168_mdio_write(tp, 0x06, 0x00ae); + rtl8168_mdio_write(tp, 0x06, 0xabe0); + rtl8168_mdio_write(tp, 0x06, 0x834f); + rtl8168_mdio_write(tp, 0x06, 0x7803); + rtl8168_mdio_write(tp, 0x06, 0x9f14); + rtl8168_mdio_write(tp, 0x06, 0xee83); + rtl8168_mdio_write(tp, 0x06, 0x4e05); + rtl8168_mdio_write(tp, 0x06, 0xd240); + rtl8168_mdio_write(tp, 0x06, 0xd655); + rtl8168_mdio_write(tp, 0x06, 0x5402); + rtl8168_mdio_write(tp, 0x06, 0x81c6); + rtl8168_mdio_write(tp, 0x06, 0xd2a0); + rtl8168_mdio_write(tp, 0x06, 0xd6ba); + rtl8168_mdio_write(tp, 0x06, 0x0002); + rtl8168_mdio_write(tp, 0x06, 0x81c6); + rtl8168_mdio_write(tp, 0x06, 0xfefd); + rtl8168_mdio_write(tp, 0x06, 0xfc05); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0xf860); + rtl8168_mdio_write(tp, 0x06, 0xe1f8); + rtl8168_mdio_write(tp, 0x06, 0x6168); + rtl8168_mdio_write(tp, 0x06, 0x02e4); + rtl8168_mdio_write(tp, 0x06, 0xf860); + rtl8168_mdio_write(tp, 0x06, 0xe5f8); + rtl8168_mdio_write(tp, 0x06, 0x61e0); + rtl8168_mdio_write(tp, 0x06, 0xf848); + rtl8168_mdio_write(tp, 0x06, 0xe1f8); + rtl8168_mdio_write(tp, 0x06, 0x4958); + rtl8168_mdio_write(tp, 0x06, 0x0f1e); + rtl8168_mdio_write(tp, 0x06, 0x02e4); + rtl8168_mdio_write(tp, 0x06, 0xf848); + rtl8168_mdio_write(tp, 0x06, 0xe5f8); + rtl8168_mdio_write(tp, 0x06, 0x49d0); + rtl8168_mdio_write(tp, 0x06, 0x0002); + rtl8168_mdio_write(tp, 0x06, 0x820a); + rtl8168_mdio_write(tp, 0x06, 0xbf83); + rtl8168_mdio_write(tp, 0x06, 0x50ef); + rtl8168_mdio_write(tp, 0x06, 0x46dc); + rtl8168_mdio_write(tp, 0x06, 0x19dd); + rtl8168_mdio_write(tp, 0x06, 0xd001); + rtl8168_mdio_write(tp, 0x06, 0x0282); + rtl8168_mdio_write(tp, 0x06, 0x0a02); + rtl8168_mdio_write(tp, 0x06, 0x8226); + rtl8168_mdio_write(tp, 0x06, 0xe0f8); + rtl8168_mdio_write(tp, 0x06, 0x60e1); + rtl8168_mdio_write(tp, 0x06, 0xf861); + rtl8168_mdio_write(tp, 0x06, 0x58fd); + rtl8168_mdio_write(tp, 0x06, 0xe4f8); + rtl8168_mdio_write(tp, 0x06, 0x60e5); + rtl8168_mdio_write(tp, 0x06, 0xf861); + rtl8168_mdio_write(tp, 0x06, 0xfc04); + rtl8168_mdio_write(tp, 0x06, 0xf9fa); + rtl8168_mdio_write(tp, 0x06, 0xfbc6); + rtl8168_mdio_write(tp, 0x06, 0xbff8); + rtl8168_mdio_write(tp, 0x06, 0x40be); + rtl8168_mdio_write(tp, 0x06, 0x8350); + rtl8168_mdio_write(tp, 0x06, 0xa001); + rtl8168_mdio_write(tp, 0x06, 0x0107); + rtl8168_mdio_write(tp, 0x06, 0x1b89); + rtl8168_mdio_write(tp, 0x06, 0xcfd2); + rtl8168_mdio_write(tp, 0x06, 0x08eb); + rtl8168_mdio_write(tp, 0x06, 0xdb19); + rtl8168_mdio_write(tp, 0x06, 0xb2fb); + rtl8168_mdio_write(tp, 0x06, 0xfffe); + rtl8168_mdio_write(tp, 0x06, 0xfd04); + rtl8168_mdio_write(tp, 0x06, 0xf8e0); + rtl8168_mdio_write(tp, 0x06, 0xf848); + rtl8168_mdio_write(tp, 0x06, 0xe1f8); + rtl8168_mdio_write(tp, 0x06, 0x4968); + rtl8168_mdio_write(tp, 0x06, 0x08e4); + rtl8168_mdio_write(tp, 0x06, 0xf848); + rtl8168_mdio_write(tp, 0x06, 0xe5f8); + rtl8168_mdio_write(tp, 0x06, 0x4958); + rtl8168_mdio_write(tp, 0x06, 0xf7e4); + rtl8168_mdio_write(tp, 0x06, 0xf848); + rtl8168_mdio_write(tp, 0x06, 0xe5f8); + rtl8168_mdio_write(tp, 0x06, 0x49fc); + rtl8168_mdio_write(tp, 0x06, 0x044d); + rtl8168_mdio_write(tp, 0x06, 0x2000); + rtl8168_mdio_write(tp, 0x06, 0x024e); + rtl8168_mdio_write(tp, 0x06, 0x2200); + rtl8168_mdio_write(tp, 0x06, 0x024d); + rtl8168_mdio_write(tp, 0x06, 0xdfff); + rtl8168_mdio_write(tp, 0x06, 0x014e); + rtl8168_mdio_write(tp, 0x06, 0xddff); + rtl8168_mdio_write(tp, 0x06, 0x01f8); + rtl8168_mdio_write(tp, 0x06, 0xfafb); + rtl8168_mdio_write(tp, 0x06, 0xef79); + rtl8168_mdio_write(tp, 0x06, 0xbff8); + rtl8168_mdio_write(tp, 0x06, 0x22d8); + rtl8168_mdio_write(tp, 0x06, 0x19d9); + rtl8168_mdio_write(tp, 0x06, 0x5884); + rtl8168_mdio_write(tp, 0x06, 0x9f09); + rtl8168_mdio_write(tp, 0x06, 0xbf82); + rtl8168_mdio_write(tp, 0x06, 0x6dd6); + rtl8168_mdio_write(tp, 0x06, 0x8275); + rtl8168_mdio_write(tp, 0x06, 0x0201); + rtl8168_mdio_write(tp, 0x06, 0x4fef); + rtl8168_mdio_write(tp, 0x06, 0x97ff); + rtl8168_mdio_write(tp, 0x06, 0xfefc); + rtl8168_mdio_write(tp, 0x06, 0x0517); + rtl8168_mdio_write(tp, 0x06, 0xfffe); + rtl8168_mdio_write(tp, 0x06, 0x0117); + rtl8168_mdio_write(tp, 0x06, 0x0001); + rtl8168_mdio_write(tp, 0x06, 0x0200); + rtl8168_mdio_write(tp, 0x05, 0x83d8); + rtl8168_mdio_write(tp, 0x06, 0x8000); + rtl8168_mdio_write(tp, 0x05, 0x83d6); + rtl8168_mdio_write(tp, 0x06, 0x824f); + rtl8168_mdio_write(tp, 0x02, 0x2010); + rtl8168_mdio_write(tp, 0x03, 0xdc00); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x0b, 0x0600); + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0xfff6); + rtl8168_mdio_write(tp, 0x06, 0x00fc); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + } + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x0D, 0xF880); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } else if (tp->mcfg == CFG_METHOD_11) { + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x10, 0x0008); + rtl8168_mdio_write(tp, 0x0D, 0x006C); + + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x17, 0x0CC0); + + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x0B, 0xA4D8); + rtl8168_mdio_write(tp, 0x09, 0x281C); + rtl8168_mdio_write(tp, 0x07, 0x2883); + rtl8168_mdio_write(tp, 0x0A, 0x6B35); + rtl8168_mdio_write(tp, 0x1D, 0x3DA4); + rtl8168_mdio_write(tp, 0x1C, 0xEFFD); + rtl8168_mdio_write(tp, 0x14, 0x7F52); + rtl8168_mdio_write(tp, 0x18, 0x7FC6); + rtl8168_mdio_write(tp, 0x08, 0x0601); + rtl8168_mdio_write(tp, 0x06, 0x4063); + rtl8168_mdio_write(tp, 0x10, 0xF074); + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x13, 0x0789); + rtl8168_mdio_write(tp, 0x12, 0xF4BD); + rtl8168_mdio_write(tp, 0x1A, 0x04FD); + rtl8168_mdio_write(tp, 0x14, 0x84B0); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x00, 0x9200); + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x01, 0x0340); + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x04, 0x4000); + rtl8168_mdio_write(tp, 0x03, 0x1D21); + rtl8168_mdio_write(tp, 0x02, 0x0C32); + rtl8168_mdio_write(tp, 0x01, 0x0200); + rtl8168_mdio_write(tp, 0x00, 0x5554); + rtl8168_mdio_write(tp, 0x04, 0x4800); + rtl8168_mdio_write(tp, 0x04, 0x4000); + rtl8168_mdio_write(tp, 0x04, 0xF000); + rtl8168_mdio_write(tp, 0x03, 0xDF01); + rtl8168_mdio_write(tp, 0x02, 0xDF20); + rtl8168_mdio_write(tp, 0x01, 0x101A); + rtl8168_mdio_write(tp, 0x00, 0xA0FF); + rtl8168_mdio_write(tp, 0x04, 0xF800); + rtl8168_mdio_write(tp, 0x04, 0xF000); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x0023); + rtl8168_mdio_write(tp, 0x16, 0x0000); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + gphy_val = rtl8168_mdio_read(tp, 0x0D); + gphy_val |= BIT_5; + rtl8168_mdio_write(tp, 0x0D, gphy_val); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + gphy_val = rtl8168_mdio_read(tp, 0x0C); + gphy_val |= BIT_10; + rtl8168_mdio_write(tp, 0x0C, gphy_val); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } else if (tp->mcfg == CFG_METHOD_12) { + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x17, 0x0CC0); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + gphy_val = rtl8168_mdio_read(tp, 0x0D); + gphy_val |= BIT_5; + rtl8168_mdio_write(tp, 0x0D, gphy_val); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + gphy_val = rtl8168_mdio_read(tp, 0x0C); + gphy_val |= BIT_10; + rtl8168_mdio_write(tp, 0x0C, gphy_val); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x002C); + rtl8168_mdio_write(tp, 0x15, 0x035D); + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x01, 0x0300); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } else if (tp->mcfg == CFG_METHOD_13) { + rtl8168_mdio_write(tp, 0x1F, 0x0000); + gphy_val = rtl8168_mdio_read(tp, 0x0D); + gphy_val |= BIT_5; + rtl8168_mdio_write(tp, 0x0D, gphy_val); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + gphy_val = rtl8168_mdio_read(tp, 0x0C); + gphy_val |= BIT_10; + rtl8168_mdio_write(tp, 0x0C, gphy_val); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } else if (tp->mcfg == CFG_METHOD_14 || tp->mcfg == CFG_METHOD_15) { + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x0023); + gphy_val = rtl8168_mdio_read(tp, 0x17) | BIT_1; + if (tp->RequiredSecLanDonglePatch) + gphy_val &= ~(BIT_2); + else + gphy_val |= (BIT_2); + rtl8168_mdio_write(tp, 0x17, gphy_val); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8b80); + rtl8168_mdio_write(tp, 0x06, 0xc896); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x0B, 0x6C20); + rtl8168_mdio_write(tp, 0x07, 0x2872); + rtl8168_mdio_write(tp, 0x1C, 0xEFFF); + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x14, 0x6420); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + gphy_val = rtl8168_mdio_read(tp, 0x08) & 0x00FF; + rtl8168_mdio_write(tp, 0x08, gphy_val | 0x8000); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x002D); + gphy_val = rtl8168_mdio_read(tp, 0x18); + rtl8168_mdio_write(tp, 0x18, gphy_val | 0x0010); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + gphy_val = rtl8168_mdio_read(tp, 0x14); + rtl8168_mdio_write(tp, 0x14, gphy_val | 0x8000); + + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x00, 0x080B); + rtl8168_mdio_write(tp, 0x0B, 0x09D7); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + if (aspm) { + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x1006); + } + } + + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x19, 0x7F46); + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8AD2); + rtl8168_mdio_write(tp, 0x06, 0x6810); + rtl8168_mdio_write(tp, 0x05, 0x8AD4); + rtl8168_mdio_write(tp, 0x06, 0x8002); + rtl8168_mdio_write(tp, 0x05, 0x8ADE); + rtl8168_mdio_write(tp, 0x06, 0x8025); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x002F); + rtl8168_mdio_write(tp, 0x15, 0x1919); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x002D); + gphy_val = rtl8168_mdio_read(tp, 0x18); + rtl8168_mdio_write(tp, 0x18, gphy_val | 0x0040); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B86); + gphy_val = rtl8168_mdio_read(tp, 0x06); + rtl8168_mdio_write(tp, 0x06, gphy_val | 0x0001); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x00AC); + rtl8168_mdio_write(tp, 0x18, 0x0006); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } else if (tp->mcfg == CFG_METHOD_16) { + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B80); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_2 | BIT_1; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + rtl8168_mdio_write(tp, 0x1f, 0x0004); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x002D); + gphy_val = rtl8168_mdio_read(tp, 0x18); + gphy_val |= BIT_4; + rtl8168_mdio_write(tp, 0x18, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0002); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + gphy_val = rtl8168_mdio_read(tp, 0x14); + gphy_val |= BIT_15; + rtl8168_mdio_write(tp, 0x14, gphy_val); + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x15, 0x1006); + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B86); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x0B, 0x6C14); + rtl8168_mdio_write(tp, 0x14, 0x7F3D); + rtl8168_mdio_write(tp, 0x1C, 0xFAFE); + rtl8168_mdio_write(tp, 0x08, 0x07C5); + rtl8168_mdio_write(tp, 0x10, 0xF090); + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x14, 0x641A); + rtl8168_mdio_write(tp, 0x1A, 0x0606); + rtl8168_mdio_write(tp, 0x12, 0xF480); + rtl8168_mdio_write(tp, 0x13, 0x0747); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0004); + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x0078); + rtl8168_mdio_write(tp, 0x15, 0xA408); + rtl8168_mdio_write(tp, 0x17, 0x5100); + rtl8168_mdio_write(tp, 0x19, 0x0008); + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x0D, 0x0207); + rtl8168_mdio_write(tp, 0x02, 0x5FD0); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0004); + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x00A1); + gphy_val = rtl8168_mdio_read(tp, 0x1A); + gphy_val &= ~BIT_2; + rtl8168_mdio_write(tp, 0x1A, gphy_val); + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0004); + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x002D); + gphy_val = rtl8168_mdio_read(tp, 0x16); + gphy_val |= BIT_5; + rtl8168_mdio_write(tp, 0x16, gphy_val); + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0004); + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x00AC); + rtl8168_mdio_write(tp, 0x18, 0x0006); + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x09, 0xA20F); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B5B); + rtl8168_mdio_write(tp, 0x06, 0x9222); + rtl8168_mdio_write(tp, 0x05, 0x8B6D); + rtl8168_mdio_write(tp, 0x06, 0x8000); + rtl8168_mdio_write(tp, 0x05, 0x8B76); + rtl8168_mdio_write(tp, 0x06, 0x8000); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + if (pdev->subsystem_vendor == 0x1043 && + pdev->subsystem_device == 0x13F7) { + + static const u16 evl_phy_value[] = { + 0x8B56, 0x8B5F, 0x8B68, 0x8B71, + 0x8B7A, 0x8A7B, 0x8A7E, 0x8A81, + 0x8A84, 0x8A87 + }; + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + for (i = 0; i < ARRAY_SIZE(evl_phy_value); i++) { + rtl8168_mdio_write(tp, 0x05, evl_phy_value[i]); + gphy_val = (0xAA << 8) | (rtl8168_mdio_read(tp, 0x06) & 0xFF); + rtl8168_mdio_write(tp, 0x06, gphy_val); + } + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x0078); + rtl8168_mdio_write(tp, 0x17, 0x51AA); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } + + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B54); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_11); + rtl8168_mdio_write(tp, 0x05, 0x8B5D); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_11); + rtl8168_mdio_write(tp, 0x05, 0x8A7C); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_8); + rtl8168_mdio_write(tp, 0x05, 0x8A7F); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) | BIT_8); + rtl8168_mdio_write(tp, 0x05, 0x8A82); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_8); + rtl8168_mdio_write(tp, 0x05, 0x8A85); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_8); + rtl8168_mdio_write(tp, 0x05, 0x8A88); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_8); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B85); + gphy_val = rtl8168_mdio_read(tp, 0x06) | BIT_14 | BIT_15; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + } else if (tp->mcfg == CFG_METHOD_17) { + if (pdev->subsystem_vendor == 0x144d && + pdev->subsystem_device == 0xc0a6) { + rtl8168_mdio_write(tp, 0x1F, 0x0001); + rtl8168_mdio_write(tp, 0x0e, 0x6b7f); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B86); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_4; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + } else { + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B80); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_2 | BIT_1; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B86); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val &= ~BIT_4; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + } + + rtl8168_mdio_write(tp, 0x1f, 0x0004); + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x002D); + gphy_val = rtl8168_mdio_read(tp, 0x18); + gphy_val |= BIT_4; + rtl8168_mdio_write(tp, 0x18, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0002); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + gphy_val = rtl8168_mdio_read(tp, 0x14); + gphy_val |= BIT_15; + rtl8168_mdio_write(tp, 0x14, gphy_val); + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B86); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0004); + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x00AC); + rtl8168_mdio_write(tp, 0x18, 0x0006); + rtl8168_mdio_write(tp, 0x1F, 0x0002); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x09, 0xA20F); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B85); + gphy_val = rtl8168_mdio_read(tp, 0x06) | BIT_14 | BIT_15; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B5B); + rtl8168_mdio_write(tp, 0x06, 0x9222); + rtl8168_mdio_write(tp, 0x05, 0x8B6D); + rtl8168_mdio_write(tp, 0x06, 0x8000); + rtl8168_mdio_write(tp, 0x05, 0x8B76); + rtl8168_mdio_write(tp, 0x06, 0x8000); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + if (pdev->subsystem_vendor == 0x1043 && + pdev->subsystem_device == 0x13F7) { + + static const u16 evl_phy_value[] = { + 0x8B56, 0x8B5F, 0x8B68, 0x8B71, + 0x8B7A, 0x8A7B, 0x8A7E, 0x8A81, + 0x8A84, 0x8A87 + }; + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + for (i = 0; i < ARRAY_SIZE(evl_phy_value); i++) { + rtl8168_mdio_write(tp, 0x05, evl_phy_value[i]); + gphy_val = (0xAA << 8) | (rtl8168_mdio_read(tp, 0x06) & 0xFF); + rtl8168_mdio_write(tp, 0x06, gphy_val); + } + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x0078); + rtl8168_mdio_write(tp, 0x17, 0x51AA); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } + + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B54); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_11); + rtl8168_mdio_write(tp, 0x05, 0x8B5D); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_11); + rtl8168_mdio_write(tp, 0x05, 0x8A7C); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_8); + rtl8168_mdio_write(tp, 0x05, 0x8A7F); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) | BIT_8); + rtl8168_mdio_write(tp, 0x05, 0x8A82); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_8); + rtl8168_mdio_write(tp, 0x05, 0x8A85); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_8); + rtl8168_mdio_write(tp, 0x05, 0x8A88); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_8); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + if (aspm) { + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + rtl8168_mdio_write(tp, 0x1f, 0x0000); + gphy_val = rtl8168_mdio_read(tp, 0x15); + gphy_val |= BIT_12; + rtl8168_mdio_write(tp, 0x15, gphy_val); + } + } + } else if (tp->mcfg == CFG_METHOD_18) { + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8b80); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_2 | BIT_1; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } + + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x002D); + gphy_val = rtl8168_mdio_read(tp, 0x18); + gphy_val |= BIT_4; + rtl8168_mdio_write(tp, 0x18, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + gphy_val = rtl8168_mdio_read(tp, 0x14); + gphy_val |= BIT_15; + rtl8168_mdio_write(tp, 0x14, gphy_val); + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B86); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B85); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_14; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x09, 0xA20F); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B55); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x05, 0x8B5E); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x05, 0x8B67); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x05, 0x8B70); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x0078); + rtl8168_mdio_write(tp, 0x17, 0x0000); + rtl8168_mdio_write(tp, 0x19, 0x00FB); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B79); + rtl8168_mdio_write(tp, 0x06, 0xAA00); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1f, 0x0003); + rtl8168_mdio_write(tp, 0x01, 0x328A); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B54); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_11); + rtl8168_mdio_write(tp, 0x05, 0x8B5D); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_11); + rtl8168_mdio_write(tp, 0x05, 0x8A7C); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_8); + rtl8168_mdio_write(tp, 0x05, 0x8A7F); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) | BIT_8); + rtl8168_mdio_write(tp, 0x05, 0x8A82); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_8); + rtl8168_mdio_write(tp, 0x05, 0x8A85); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_8); + rtl8168_mdio_write(tp, 0x05, 0x8A88); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_8); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8b85); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) | BIT_15); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + } + + if (aspm) { + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + rtl8168_mdio_write(tp, 0x1f, 0x0000); + gphy_val = rtl8168_mdio_read(tp, 0x15); + gphy_val |= BIT_12; + rtl8168_mdio_write(tp, 0x15, gphy_val); + } + } + } else if (tp->mcfg == CFG_METHOD_19) { + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8b80); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_2 | BIT_1; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } + + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x002D); + gphy_val = rtl8168_mdio_read(tp, 0x18); + gphy_val |= BIT_4; + rtl8168_mdio_write(tp, 0x18, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + gphy_val = rtl8168_mdio_read(tp, 0x14); + gphy_val |= BIT_15; + rtl8168_mdio_write(tp, 0x14, gphy_val); + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B86); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B54); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_11); + rtl8168_mdio_write(tp, 0x05, 0x8B5D); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_11); + rtl8168_mdio_write(tp, 0x05, 0x8A7C); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_8); + rtl8168_mdio_write(tp, 0x05, 0x8A7F); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) | BIT_8); + rtl8168_mdio_write(tp, 0x05, 0x8A82); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_8); + rtl8168_mdio_write(tp, 0x05, 0x8A85); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_8); + rtl8168_mdio_write(tp, 0x05, 0x8A88); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_8); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8b85); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) | BIT_15); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + } + + if (aspm) { + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + rtl8168_mdio_write(tp, 0x1f, 0x0000); + gphy_val = rtl8168_mdio_read(tp, 0x15); + gphy_val |= BIT_12; + rtl8168_mdio_write(tp, 0x15, gphy_val); + } + } + } else if (tp->mcfg == CFG_METHOD_20) { + + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8b80); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_2 | BIT_1; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } + + rtl8168_mdio_write(tp, 0x1f, 0x0007); + rtl8168_mdio_write(tp, 0x1e, 0x002D); + gphy_val = rtl8168_mdio_read(tp, 0x18); + gphy_val |= BIT_4; + rtl8168_mdio_write(tp, 0x18, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + gphy_val = rtl8168_mdio_read(tp, 0x14); + gphy_val |= BIT_15; + rtl8168_mdio_write(tp, 0x14, gphy_val); + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B86); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_0; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B85); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_14; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0003); + rtl8168_mdio_write(tp, 0x09, 0xA20F); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B55); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x05, 0x8B5E); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x05, 0x8B67); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x05, 0x8B70); + rtl8168_mdio_write(tp, 0x06, 0x0000); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x0078); + rtl8168_mdio_write(tp, 0x17, 0x0000); + rtl8168_mdio_write(tp, 0x19, 0x00FB); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B79); + rtl8168_mdio_write(tp, 0x06, 0xAA00); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B54); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_11); + rtl8168_mdio_write(tp, 0x05, 0x8B5D); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_11); + rtl8168_mdio_write(tp, 0x05, 0x8A7C); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_8); + rtl8168_mdio_write(tp, 0x05, 0x8A7F); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) | BIT_8); + rtl8168_mdio_write(tp, 0x05, 0x8A82); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_8); + rtl8168_mdio_write(tp, 0x05, 0x8A85); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_8); + rtl8168_mdio_write(tp, 0x05, 0x8A88); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) & ~BIT_8); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + rtl8168_mdio_write(tp, 0x1f, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8b85); + rtl8168_mdio_write(tp, 0x06, rtl8168_mdio_read(tp, 0x06) | BIT_15); + rtl8168_mdio_write(tp, 0x1f, 0x0000); + } + + if (aspm) { + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + rtl8168_mdio_write(tp, 0x1f, 0x0000); + gphy_val = rtl8168_mdio_read(tp, 0x15); + gphy_val |= BIT_12; + rtl8168_mdio_write(tp, 0x15, gphy_val); + } + } + } else if (tp->mcfg == CFG_METHOD_21) { + rtl8168_mdio_write(tp, 0x1F, 0x0A46); + gphy_val = rtl8168_mdio_read(tp, 0x10); + rtl8168_mdio_write(tp, 0x1F, 0x0BCC); + if (gphy_val & BIT_8) + rtl8168_clear_eth_phy_bit(tp, 0x12, BIT_15); + else + rtl8168_set_eth_phy_bit(tp, 0x12, BIT_15); + rtl8168_mdio_write(tp, 0x1F, 0x0A46); + gphy_val = rtl8168_mdio_read(tp, 0x13); + rtl8168_mdio_write(tp, 0x1F, 0x0C41); + if (gphy_val & BIT_8) + rtl8168_set_eth_phy_bit(tp, 0x15, BIT_1); + else + rtl8168_clear_eth_phy_bit(tp, 0x15, BIT_1); + + rtl8168_mdio_write(tp, 0x1F, 0x0A44); + rtl8168_mdio_write(tp, 0x11, rtl8168_mdio_read(tp, 0x11) | BIT_2 | BIT_3); + + rtl8168_mdio_write(tp, 0x1F, 0x0BCC); + rtl8168_mdio_write(tp, 0x14, rtl8168_mdio_read(tp, 0x14) & ~BIT_8); + rtl8168_mdio_write(tp, 0x1F, 0x0A44); + rtl8168_mdio_write(tp, 0x11, rtl8168_mdio_read(tp, 0x11) | BIT_7); + rtl8168_mdio_write(tp, 0x11, rtl8168_mdio_read(tp, 0x11) | BIT_6); + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8084); + rtl8168_mdio_write(tp, 0x14, rtl8168_mdio_read(tp, 0x14) & ~(BIT_14 | BIT_13)); + rtl8168_mdio_write(tp, 0x10, rtl8168_mdio_read(tp, 0x10) | BIT_12); + rtl8168_mdio_write(tp, 0x10, rtl8168_mdio_read(tp, 0x10) | BIT_1); + rtl8168_mdio_write(tp, 0x10, rtl8168_mdio_read(tp, 0x10) | BIT_0); + + rtl8168_mdio_write(tp, 0x1F, 0x0A4B); + rtl8168_mdio_write(tp, 0x11, rtl8168_mdio_read(tp, 0x11) | BIT_2); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8012); + rtl8168_mdio_write(tp, 0x14, rtl8168_mdio_read(tp, 0x14) | BIT_15); + + rtl8168_mdio_write(tp, 0x1F, 0x0C42); + gphy_val = rtl8168_mdio_read(tp, 0x11); + gphy_val |= BIT_14; + gphy_val &= ~BIT_13; + rtl8168_mdio_write(tp, 0x11, gphy_val); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x809A); + rtl8168_mdio_write(tp, 0x14, 0x8022); + rtl8168_mdio_write(tp, 0x13, 0x80A0); + gphy_val = rtl8168_mdio_read(tp, 0x14) & 0x00FF; + gphy_val |= 0x1000; + rtl8168_mdio_write(tp, 0x14, gphy_val); + rtl8168_mdio_write(tp, 0x13, 0x8088); + rtl8168_mdio_write(tp, 0x14, 0x9222); + + if (aspm) { + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x10, rtl8168_mdio_read(tp, 0x10) | BIT_2); + } + } + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } else if (tp->mcfg == CFG_METHOD_22) { + //do nothing + } else if (tp->mcfg == CFG_METHOD_23) { + rtl8168_mdio_write(tp, 0x1F, 0x0A44); + rtl8168_mdio_write(tp, 0x11, rtl8168_mdio_read(tp, 0x11) | (BIT_3 | BIT_2)); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0BCC); + rtl8168_mdio_write(tp, 0x14, rtl8168_mdio_read(tp, 0x14) & ~BIT_8); + rtl8168_mdio_write(tp, 0x1F, 0x0A44); + rtl8168_mdio_write(tp, 0x11, rtl8168_mdio_read(tp, 0x11) | BIT_7); + rtl8168_mdio_write(tp, 0x11, rtl8168_mdio_read(tp, 0x11) | BIT_6); + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8084); + rtl8168_mdio_write(tp, 0x14, rtl8168_mdio_read(tp, 0x14) & ~(BIT_14 | BIT_13)); + rtl8168_mdio_write(tp, 0x10, rtl8168_mdio_read(tp, 0x10) | BIT_12); + rtl8168_mdio_write(tp, 0x10, rtl8168_mdio_read(tp, 0x10) | BIT_1); + rtl8168_mdio_write(tp, 0x10, rtl8168_mdio_read(tp, 0x10) | BIT_0); + + rtl8168_mdio_write(tp, 0x1F, 0x0A4B); + rtl8168_mdio_write(tp, 0x11, rtl8168_mdio_read(tp, 0x11) | BIT_2); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8012); + rtl8168_mdio_write(tp, 0x14, rtl8168_mdio_read(tp, 0x14) | BIT_15); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0C42); + ClearAndSetEthPhyBit(tp, + 0x11, + BIT_13, + BIT_14 + ); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + if (aspm) { + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x10, rtl8168_mdio_read(tp, 0x10) | BIT_2); + } + } + } else if (tp->mcfg == CFG_METHOD_24) { + rtl8168_mdio_write(tp, 0x1F, 0x0BCC); + rtl8168_mdio_write(tp, 0x14, rtl8168_mdio_read(tp, 0x14) & ~BIT_8); + rtl8168_mdio_write(tp, 0x1F, 0x0A44); + rtl8168_mdio_write(tp, 0x11, rtl8168_mdio_read(tp, 0x11) | BIT_7); + rtl8168_mdio_write(tp, 0x11, rtl8168_mdio_read(tp, 0x11) | BIT_6); + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8084); + rtl8168_mdio_write(tp, 0x14, rtl8168_mdio_read(tp, 0x14) & ~(BIT_14 | BIT_13)); + rtl8168_mdio_write(tp, 0x10, rtl8168_mdio_read(tp, 0x10) | BIT_12); + rtl8168_mdio_write(tp, 0x10, rtl8168_mdio_read(tp, 0x10) | BIT_1); + rtl8168_mdio_write(tp, 0x10, rtl8168_mdio_read(tp, 0x10) | BIT_0); + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8012); + rtl8168_mdio_write(tp, 0x14, rtl8168_mdio_read(tp, 0x14) | BIT_15); + + rtl8168_mdio_write(tp, 0x1F, 0x0C42); + gphy_val = rtl8168_mdio_read(tp, 0x11); + gphy_val |= BIT_14; + gphy_val &= ~BIT_13; + rtl8168_mdio_write(tp, 0x11, gphy_val); + + if (aspm) { + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x10, rtl8168_mdio_read(tp, 0x10) | BIT_2); + } + } + } else if (tp->mcfg == CFG_METHOD_25 || tp->mcfg == CFG_METHOD_26) { + rtl8168_mdio_write(tp, 0x1F, 0x0BCC); + rtl8168_mdio_write(tp, 0x14, rtl8168_mdio_read(tp, 0x14) & ~BIT_8); + rtl8168_mdio_write(tp, 0x1F, 0x0A44); + rtl8168_mdio_write(tp, 0x11, rtl8168_mdio_read(tp, 0x11) | BIT_7); + rtl8168_mdio_write(tp, 0x11, rtl8168_mdio_read(tp, 0x11) | BIT_6); + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8084); + rtl8168_mdio_write(tp, 0x14, rtl8168_mdio_read(tp, 0x14) & ~(BIT_14 | BIT_13)); + rtl8168_mdio_write(tp, 0x10, rtl8168_mdio_read(tp, 0x10) | BIT_12); + rtl8168_mdio_write(tp, 0x10, rtl8168_mdio_read(tp, 0x10) | BIT_1); + rtl8168_mdio_write(tp, 0x10, rtl8168_mdio_read(tp, 0x10) | BIT_0); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8012); + rtl8168_mdio_write(tp, 0x14, rtl8168_mdio_read(tp, 0x14) | BIT_15); + + rtl8168_mdio_write(tp, 0x1F, 0x0BCE); + rtl8168_mdio_write(tp, 0x12, 0x8860); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x80F3); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x8B00); + rtl8168_mdio_write(tp, 0x13, 0x80F0); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x3A00); + rtl8168_mdio_write(tp, 0x13, 0x80EF); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x0500); + rtl8168_mdio_write(tp, 0x13, 0x80F6); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x6E00); + rtl8168_mdio_write(tp, 0x13, 0x80EC); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x6800); + rtl8168_mdio_write(tp, 0x13, 0x80ED); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x7C00); + rtl8168_mdio_write(tp, 0x13, 0x80F2); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0xF400); + rtl8168_mdio_write(tp, 0x13, 0x80F4); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x8500); + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8110); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0xA800); + rtl8168_mdio_write(tp, 0x13, 0x810F); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x1D00); + rtl8168_mdio_write(tp, 0x13, 0x8111); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0xF500); + rtl8168_mdio_write(tp, 0x13, 0x8113); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x6100); + rtl8168_mdio_write(tp, 0x13, 0x8115); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x9200); + rtl8168_mdio_write(tp, 0x13, 0x810E); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x0400); + rtl8168_mdio_write(tp, 0x13, 0x810C); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x7C00); + rtl8168_mdio_write(tp, 0x13, 0x810B); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x5A00); + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x80D1); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0xFF00); + rtl8168_mdio_write(tp, 0x13, 0x80CD); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x9E00); + rtl8168_mdio_write(tp, 0x13, 0x80D3); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x0E00); + rtl8168_mdio_write(tp, 0x13, 0x80D5); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0xCA00); + rtl8168_mdio_write(tp, 0x13, 0x80D7); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x8400); + + if (aspm) { + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x10, rtl8168_mdio_read(tp, 0x10) | BIT_2); + } + } + } else if (tp->mcfg == CFG_METHOD_27 || tp->mcfg == CFG_METHOD_28) { + rtl8168_mdio_write(tp, 0x1F, 0x0BCC); + rtl8168_mdio_write(tp, 0x14, rtl8168_mdio_read(tp, 0x14) & ~BIT_8); + rtl8168_mdio_write(tp, 0x1F, 0x0A44); + rtl8168_mdio_write(tp, 0x11, rtl8168_mdio_read(tp, 0x11) | BIT_7); + rtl8168_mdio_write(tp, 0x11, rtl8168_mdio_read(tp, 0x11) | BIT_6); + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8084); + rtl8168_mdio_write(tp, 0x14, rtl8168_mdio_read(tp, 0x14) & ~(BIT_14 | BIT_13)); + rtl8168_mdio_write(tp, 0x10, rtl8168_mdio_read(tp, 0x10) | BIT_12); + rtl8168_mdio_write(tp, 0x10, rtl8168_mdio_read(tp, 0x10) | BIT_1); + rtl8168_mdio_write(tp, 0x10, rtl8168_mdio_read(tp, 0x10) | BIT_0); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8012); + rtl8168_mdio_write(tp, 0x14, rtl8168_mdio_read(tp, 0x14) | BIT_15); + + rtl8168_mdio_write(tp, 0x1F, 0x0C42); + rtl8168_mdio_write(tp, 0x11, (rtl8168_mdio_read(tp, 0x11) & ~BIT_13) | BIT_14); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x80F3); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x8B00); + rtl8168_mdio_write(tp, 0x13, 0x80F0); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x3A00); + rtl8168_mdio_write(tp, 0x13, 0x80EF); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x0500); + rtl8168_mdio_write(tp, 0x13, 0x80F6); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x6E00); + rtl8168_mdio_write(tp, 0x13, 0x80EC); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x6800); + rtl8168_mdio_write(tp, 0x13, 0x80ED); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x7C00); + rtl8168_mdio_write(tp, 0x13, 0x80F2); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0xF400); + rtl8168_mdio_write(tp, 0x13, 0x80F4); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x8500); + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8110); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0xA800); + rtl8168_mdio_write(tp, 0x13, 0x810F); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x1D00); + rtl8168_mdio_write(tp, 0x13, 0x8111); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0xF500); + rtl8168_mdio_write(tp, 0x13, 0x8113); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x6100); + rtl8168_mdio_write(tp, 0x13, 0x8115); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x9200); + rtl8168_mdio_write(tp, 0x13, 0x810E); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x0400); + rtl8168_mdio_write(tp, 0x13, 0x810C); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x7C00); + rtl8168_mdio_write(tp, 0x13, 0x810B); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x5A00); + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x80D1); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0xFF00); + rtl8168_mdio_write(tp, 0x13, 0x80CD); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x9E00); + rtl8168_mdio_write(tp, 0x13, 0x80D3); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x0E00); + rtl8168_mdio_write(tp, 0x13, 0x80D5); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0xCA00); + rtl8168_mdio_write(tp, 0x13, 0x80D7); + rtl8168_mdio_write(tp, 0x14, (rtl8168_mdio_read(tp, 0x14) & ~0xFF00) | 0x8400); + + if (aspm) { + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x10, rtl8168_mdio_read(tp, 0x10) | BIT_2); + } + } + } else if (tp->mcfg == CFG_METHOD_29) { + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x809b); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xF800 , + 0x8000 + ); + rtl8168_mdio_write(tp, 0x13, 0x80A2); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xFF00 , + 0x8000 + ); + rtl8168_mdio_write(tp, 0x13, 0x80A4); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xFF00 , + 0x8500 + ); + rtl8168_mdio_write(tp, 0x13, 0x809C); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xFF00 , + 0xbd00 + ); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x80AD); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xF800 , + 0x7000 + ); + rtl8168_mdio_write(tp, 0x13, 0x80B4); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xFF00 , + 0x5000 + ); + rtl8168_mdio_write(tp, 0x13, 0x80AC); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xFF00 , + 0x4000 + ); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x808E); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xFF00 , + 0x1200 + ); + rtl8168_mdio_write(tp, 0x13, 0x8090); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xFF00 , + 0xE500 + ); + rtl8168_mdio_write(tp, 0x13, 0x8092); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xFF00 , + 0x9F00 + ); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + u16 dout_tapbin; + + dout_tapbin = 0x0000; + rtl8168_mdio_write( tp, 0x1F, 0x0A46 ); + gphy_val = rtl8168_mdio_read( tp, 0x13 ); + gphy_val &= (BIT_1|BIT_0); + gphy_val <<= 2; + dout_tapbin |= gphy_val; + + gphy_val = rtl8168_mdio_read( tp, 0x12 ); + gphy_val &= (BIT_15|BIT_14); + gphy_val >>= 14; + dout_tapbin |= gphy_val; + + dout_tapbin = ~( dout_tapbin^BIT_3 ); + dout_tapbin <<= 12; + dout_tapbin &= 0xF000; + + rtl8168_mdio_write( tp, 0x1F, 0x0A43 ); + + rtl8168_mdio_write( tp, 0x13, 0x827A ); + ClearAndSetEthPhyBit( tp, + 0x14, + BIT_15|BIT_14|BIT_13|BIT_12, + dout_tapbin + ); + + + rtl8168_mdio_write( tp, 0x13, 0x827B ); + ClearAndSetEthPhyBit( tp, + 0x14, + BIT_15|BIT_14|BIT_13|BIT_12, + dout_tapbin + ); + + + rtl8168_mdio_write( tp, 0x13, 0x827C ); + ClearAndSetEthPhyBit( tp, + 0x14, + BIT_15|BIT_14|BIT_13|BIT_12, + dout_tapbin + ); + + + rtl8168_mdio_write( tp, 0x13, 0x827D ); + ClearAndSetEthPhyBit( tp, + 0x14, + BIT_15|BIT_14|BIT_13|BIT_12, + dout_tapbin + ); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8011); + rtl8168_set_eth_phy_bit(tp, 0x14, BIT_11); + rtl8168_mdio_write(tp, 0x1F, 0x0A42); + rtl8168_set_eth_phy_bit(tp, 0x16, BIT_1); + } + + rtl8168_mdio_write(tp, 0x1F, 0x0A44); + rtl8168_set_eth_phy_bit( tp, 0x11, BIT_11 ); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + + rtl8168_mdio_write(tp, 0x1F, 0x0BCA); + ClearAndSetEthPhyBit( tp, + 0x17, + (BIT_13 | BIT_12) , + BIT_14 + ); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x803F); + rtl8168_clear_eth_phy_bit( tp, 0x14, (BIT_13 | BIT_12)); + rtl8168_mdio_write(tp, 0x13, 0x8047); + rtl8168_clear_eth_phy_bit( tp, 0x14, (BIT_13 | BIT_12)); + rtl8168_mdio_write(tp, 0x13, 0x804F); + rtl8168_clear_eth_phy_bit( tp, 0x14, (BIT_13 | BIT_12)); + rtl8168_mdio_write(tp, 0x13, 0x8057); + rtl8168_clear_eth_phy_bit( tp, 0x14, (BIT_13 | BIT_12)); + rtl8168_mdio_write(tp, 0x13, 0x805F); + rtl8168_clear_eth_phy_bit( tp, 0x14, (BIT_13 | BIT_12)); + rtl8168_mdio_write(tp, 0x13, 0x8067 ); + rtl8168_clear_eth_phy_bit( tp, 0x14, (BIT_13 | BIT_12)); + rtl8168_mdio_write(tp, 0x13, 0x806F ); + rtl8168_clear_eth_phy_bit( tp, 0x14, (BIT_13 | BIT_12)); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + if (aspm) { + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_set_eth_phy_bit( tp, 0x10, BIT_2 ); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } + } + } else if (tp->mcfg == CFG_METHOD_30) { + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x808A); + ClearAndSetEthPhyBit( tp, + 0x14, + BIT_5 | BIT_4 | BIT_3 | BIT_2 | BIT_1 | BIT_0, + 0x0A ); + + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8011); + rtl8168_set_eth_phy_bit(tp, 0x14, BIT_11); + rtl8168_mdio_write(tp, 0x1F, 0x0A42); + rtl8168_set_eth_phy_bit(tp, 0x16, BIT_1); + } + + rtl8168_mdio_write(tp, 0x1F, 0x0A44); + rtl8168_set_eth_phy_bit( tp, 0x11, BIT_11 ); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + if (tp->RequireAdcBiasPatch) { + rtl8168_mdio_write(tp, 0x1F, 0x0BCF); + rtl8168_mdio_write(tp, 0x16, tp->AdcBiasPatchIoffset); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } + + { + u16 rlen; + + rtl8168_mdio_write(tp, 0x1F, 0x0BCD); + gphy_val = rtl8168_mdio_read( tp, 0x16 ); + gphy_val &= 0x000F; + + if ( gphy_val > 3 ) { + rlen = gphy_val - 3; + } else { + rlen = 0; + } + + gphy_val = rlen | (rlen<<4) | (rlen<<8) | (rlen<<12); + + rtl8168_mdio_write(tp, 0x1F, 0x0BCD); + rtl8168_mdio_write(tp, 0x17, gphy_val); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } + + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x85FE); + ClearAndSetEthPhyBit( + tp, + 0x14, + BIT_15|BIT_14|BIT_13|BIT_12|BIT_11|BIT_10|BIT_8, + BIT_9); + rtl8168_mdio_write(tp, 0x13, 0x85FF); + ClearAndSetEthPhyBit( + tp, + 0x14, + BIT_15|BIT_14|BIT_13|BIT_12, + BIT_11|BIT_10|BIT_9|BIT_8); + rtl8168_mdio_write(tp, 0x13, 0x814B); + ClearAndSetEthPhyBit( + tp, + 0x14, + BIT_15|BIT_14|BIT_13|BIT_11|BIT_10|BIT_9|BIT_8, + BIT_12); + } + + if (aspm) { + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_set_eth_phy_bit( tp, 0x10, BIT_2 ); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } + } + } else if (tp->mcfg == CFG_METHOD_31 || tp->mcfg == CFG_METHOD_32 || + tp->mcfg == CFG_METHOD_33) { + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x808E); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xFF00 , + 0x4800 + ); + rtl8168_mdio_write(tp, 0x13, 0x8090); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xFF00 , + 0xCC00 + ); + rtl8168_mdio_write(tp, 0x13, 0x8092); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xFF00 , + 0xB000 + ); + rtl8168_mdio_write(tp, 0x13, 0x8088); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xFF00 , + 0x6000 + ); + rtl8168_mdio_write(tp, 0x13, 0x808B); + ClearAndSetEthPhyBit( tp, + 0x14, + 0x3F00 , + 0x0B00 + ); + rtl8168_mdio_write(tp, 0x13, 0x808D); + ClearAndSetEthPhyBit( tp, + 0x14, + 0x1F00 , + 0x0600 + ); + rtl8168_mdio_write(tp, 0x13, 0x808C); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xFF00 , + 0xB000 + ); + + rtl8168_mdio_write(tp, 0x13, 0x80A0); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xFF00 , + 0x2800 + ); + rtl8168_mdio_write(tp, 0x13, 0x80A2); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xFF00 , + 0x5000 + ); + rtl8168_mdio_write(tp, 0x13, 0x809B); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xF800 , + 0xB000 + ); + rtl8168_mdio_write(tp, 0x13, 0x809A); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xFF00 , + 0x4B00 + ); + rtl8168_mdio_write(tp, 0x13, 0x809D); + ClearAndSetEthPhyBit( tp, + 0x14, + 0x3F00 , + 0x0800 + ); + rtl8168_mdio_write(tp, 0x13, 0x80A1); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xFF00 , + 0x7000 + ); + rtl8168_mdio_write(tp, 0x13, 0x809F); + ClearAndSetEthPhyBit( tp, + 0x14, + 0x1F00 , + 0x0300 + ); + rtl8168_mdio_write(tp, 0x13, 0x809E); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xFF00 , + 0x8800 + ); + + rtl8168_mdio_write(tp, 0x13, 0x80B2); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xFF00 , + 0x2200 + ); + rtl8168_mdio_write(tp, 0x13, 0x80AD); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xF800 , + 0x9800 + ); + rtl8168_mdio_write(tp, 0x13, 0x80AF); + ClearAndSetEthPhyBit( tp, + 0x14, + 0x3F00 , + 0x0800 + ); + rtl8168_mdio_write(tp, 0x13, 0x80B3); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xFF00 , + 0x6F00 + ); + rtl8168_mdio_write(tp, 0x13, 0x80B1); + ClearAndSetEthPhyBit( tp, + 0x14, + 0x1F00 , + 0x0300 + ); + rtl8168_mdio_write(tp, 0x13, 0x80B0); + ClearAndSetEthPhyBit( tp, + 0x14, + 0xFF00 , + 0x9300 + ); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8011); + rtl8168_set_eth_phy_bit(tp, 0x14, BIT_11); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0A44); + rtl8168_set_eth_phy_bit(tp, 0x11, BIT_11); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_mdio_write(tp, 0x13, 0x8016); + rtl8168_set_eth_phy_bit(tp, 0x14, BIT_10); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + if (aspm) { + if (!HW_SUPP_SERDES_PHY(tp) && + HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + rtl8168_mdio_write(tp, 0x1F, 0x0A43); + rtl8168_set_eth_phy_bit( tp, 0x10, BIT_2 ); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } + } + } + +#ifdef ENABLE_FIBER_SUPPORT + if (HW_FIBER_MODE_ENABLED(tp)) + rtl8168_hw_fiber_phy_config(dev); +#endif //ENABLE_FIBER_SUPPORT + + //EthPhyPPSW + if (tp->mcfg == CFG_METHOD_21 || tp->mcfg == CFG_METHOD_22 || + tp->mcfg == CFG_METHOD_24 || tp->mcfg == CFG_METHOD_25 || + tp->mcfg == CFG_METHOD_26) { + //disable EthPhyPPSW + rtl8168_mdio_write(tp, 0x1F, 0x0BCD); + rtl8168_mdio_write(tp, 0x14, 0x5065); + rtl8168_mdio_write(tp, 0x14, 0xD065); + rtl8168_mdio_write(tp, 0x1F, 0x0BC8); + rtl8168_mdio_write(tp, 0x11, 0x5655); + rtl8168_mdio_write(tp, 0x1F, 0x0BCD); + rtl8168_mdio_write(tp, 0x14, 0x1065); + rtl8168_mdio_write(tp, 0x14, 0x9065); + rtl8168_mdio_write(tp, 0x14, 0x1065); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } else if (tp->mcfg == CFG_METHOD_29 || tp->mcfg == CFG_METHOD_30 || + tp->mcfg == CFG_METHOD_31 || tp->mcfg == CFG_METHOD_32 || + tp->mcfg == CFG_METHOD_33) { + //enable EthPhyPPSW + rtl8168_mdio_write(tp, 0x1F, 0x0A44); + rtl8168_set_eth_phy_bit( tp, 0x11, BIT_7 ); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } + + /*ocp phy power saving*/ + if (tp->mcfg == CFG_METHOD_25 || tp->mcfg == CFG_METHOD_26 || + tp->mcfg == CFG_METHOD_27 || tp->mcfg == CFG_METHOD_28 || + tp->mcfg == CFG_METHOD_29 || tp->mcfg == CFG_METHOD_30 || + tp->mcfg == CFG_METHOD_31 || tp->mcfg == CFG_METHOD_32 || + tp->mcfg == CFG_METHOD_33) { + if (aspm) + rtl8168_enable_ocp_phy_power_saving(dev); + } + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + if (HW_HAS_WRITE_PHY_MCU_RAM_CODE(tp)) { + if (tp->eee_enabled) + rtl8168_enable_EEE(tp); + else + rtl8168_disable_EEE(tp); + } +} + +static inline void rtl8168_delete_esd_timer(struct net_device *dev, struct timer_list *timer) +{ + del_timer_sync(timer); +} + +static inline void rtl8168_request_esd_timer(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + struct timer_list *timer = &tp->esd_timer; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) + setup_timer(timer, rtl8168_esd_timer, (unsigned long)dev); +#else + timer_setup(timer, rtl8168_esd_timer, 0); +#endif + mod_timer(timer, jiffies + RTL8168_ESD_TIMEOUT); +} + +static inline void rtl8168_delete_link_timer(struct net_device *dev, struct timer_list *timer) +{ + del_timer_sync(timer); +} + +static inline void rtl8168_request_link_timer(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + struct timer_list *timer = &tp->link_timer; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) + setup_timer(timer, rtl8168_link_timer, (unsigned long)dev); +#else + timer_setup(timer, rtl8168_link_timer, 0); +#endif + mod_timer(timer, jiffies + RTL8168_LINK_TIMEOUT); +} + +#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 +rtl8168_netpoll(struct net_device *dev) +{ + struct rtl8168_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) + rtl8168_interrupt(pdev->irq, dev, NULL); +#else + rtl8168_interrupt(pdev->irq, dev); +#endif + enable_irq(pdev->irq); +} +#endif + +static void +rtl8168_get_bios_setting(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + switch (tp->mcfg) { + case CFG_METHOD_9: + case CFG_METHOD_10: + case CFG_METHOD_11: + case CFG_METHOD_12: + case CFG_METHOD_13: + case CFG_METHOD_14: + case CFG_METHOD_15: + case CFG_METHOD_16: + case CFG_METHOD_17: + case CFG_METHOD_18: + case CFG_METHOD_19: + case CFG_METHOD_20: + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + tp->bios_setting = RTL_R32(tp, 0x8c); + break; + } +} + +static void +rtl8168_set_bios_setting(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + switch (tp->mcfg) { + case CFG_METHOD_9: + case CFG_METHOD_10: + case CFG_METHOD_11: + case CFG_METHOD_12: + case CFG_METHOD_13: + case CFG_METHOD_14: + case CFG_METHOD_15: + case CFG_METHOD_16: + case CFG_METHOD_17: + case CFG_METHOD_18: + case CFG_METHOD_19: + case CFG_METHOD_20: + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + RTL_W32(tp, 0x8C, tp->bios_setting); + break; + } +} + +static void +rtl8168_init_software_variable(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + struct pci_dev *pdev = tp->pci_dev; + + rtl8168_get_bios_setting(dev); + + switch (tp->mcfg) { + case CFG_METHOD_11: + case CFG_METHOD_12: + case CFG_METHOD_13: + tp->HwSuppDashVer = 1; + break; + case CFG_METHOD_23: + case CFG_METHOD_27: + case CFG_METHOD_28: + tp->HwSuppDashVer = 2; + break; + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + tp->HwSuppDashVer = 3; + break; + default: + tp->HwSuppDashVer = 0; + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + tp->HwPkgDet = rtl8168_mac_ocp_read(tp, 0xDC00); + tp->HwPkgDet = (tp->HwPkgDet >> 3) & 0x0F; + break; + } + + if (HW_SUPP_SERDES_PHY(tp)) + eee_enable = 0; + + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + tp->HwSuppNowIsOobVer = 1; + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + tp->HwSuppPhyOcpVer = 1; + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + tp->HwSuppUpsVer = 1; + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + tp->HwPcieSNOffset = 0x16C; + break; + case CFG_METHOD_DEFAULT: + tp->HwPcieSNOffset = 0; + break; + default: + tp->HwPcieSNOffset = 0x164; + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_14: + case CFG_METHOD_15: + case CFG_METHOD_16: + case CFG_METHOD_17: + case CFG_METHOD_18: + case CFG_METHOD_19: + case CFG_METHOD_20: + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + tp->HwSuppAspmClkIntrLock = 1; + break; + } + + if (!aspm || !tp->HwSuppAspmClkIntrLock) + dynamic_aspm = 0; + +#ifdef ENABLE_REALWOW_SUPPORT + rtl8168_get_realwow_hw_version(dev); +#endif //ENABLE_REALWOW_SUPPORT + + if (HW_DASH_SUPPORT_DASH(tp) && rtl8168_check_dash(tp)) + tp->DASH = 1; + else + tp->DASH = 0; + + if (tp->DASH) { + if (HW_DASH_SUPPORT_TYPE_3(tp)) { + u64 CmacMemPhysAddress; + void __iomem *cmac_ioaddr = NULL; + struct pci_dev *pdev_cmac; + + pdev_cmac = pci_get_slot(pdev->bus, PCI_DEVFN(PCI_SLOT(pdev->devfn), 0)); + + //map CMAC IO space + CmacMemPhysAddress = pci_resource_start(pdev_cmac, 2); + + /* ioremap MMIO region */ + cmac_ioaddr = ioremap(CmacMemPhysAddress, R8168_REGS_SIZE); + + if (cmac_ioaddr == NULL) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) + if (netif_msg_probe(tp)) + dev_err(&pdev->dev, "cannot remap CMAC MMIO, aborting\n"); +#endif //LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) + tp->DASH = 0; + } else { + tp->mapped_cmac_ioaddr = cmac_ioaddr; + } + } + + eee_enable = 0; + } + +#ifdef ENABLE_DASH_SUPPORT +#ifdef ENABLE_DASH_PRINTER_SUPPORT + if (tp->DASH) { + if (HW_DASH_SUPPORT_TYPE_3(tp) && tp->HwPkgDet == 0x0F) + tp->dash_printer_enabled = 1; + else if (HW_DASH_SUPPORT_TYPE_2(tp)) + tp->dash_printer_enabled = 1; + } +#endif //ENABLE_DASH_PRINTER_SUPPORT +#endif //ENABLE_DASH_SUPPORT + + if (HW_DASH_SUPPORT_TYPE_2(tp)) + tp->cmac_ioaddr = tp->mmio_addr; + else if (HW_DASH_SUPPORT_TYPE_3(tp)) + tp->cmac_ioaddr = tp->mapped_cmac_ioaddr; + + switch (tp->mcfg) { + case CFG_METHOD_1: + tp->intr_mask = RxDescUnavail | RxFIFOOver | TxDescUnavail | TxOK | RxOK | SWInt; + tp->timer_intr_mask = PCSTimeout | RxFIFOOver; + break; + case CFG_METHOD_2: + case CFG_METHOD_3: + case CFG_METHOD_4: + tp->intr_mask = RxDescUnavail | TxDescUnavail | TxOK | RxOK | SWInt; + tp->timer_intr_mask = PCSTimeout; + break; + default: + tp->intr_mask = RxDescUnavail | TxOK | RxOK | SWInt; + tp->timer_intr_mask = PCSTimeout; + break; + } + +#ifdef ENABLE_DASH_SUPPORT + if (tp->DASH) { + if (HW_DASH_SUPPORT_TYPE_2(tp) || HW_DASH_SUPPORT_TYPE_3(tp)) { + tp->timer_intr_mask |= ( ISRIMR_DASH_INTR_EN | ISRIMR_DASH_INTR_CMAC_RESET); + tp->intr_mask |= ( ISRIMR_DASH_INTR_EN | ISRIMR_DASH_INTR_CMAC_RESET); + } else { + tp->timer_intr_mask |= ( ISRIMR_DP_DASH_OK | ISRIMR_DP_HOST_OK | ISRIMR_DP_REQSYS_OK ); + tp->intr_mask |= ( ISRIMR_DP_DASH_OK | ISRIMR_DP_HOST_OK | ISRIMR_DP_REQSYS_OK ); + } + } +#endif + if (aspm) { + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + tp->org_pci_offset_99 = rtl8168_csi_fun0_read_byte(tp, 0x99); + tp->org_pci_offset_99 &= ~(BIT_5|BIT_6); + break; + } + switch (tp->mcfg) { + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + tp->org_pci_offset_180 = rtl8168_csi_fun0_read_byte(tp, 0x180); + break; + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + tp->org_pci_offset_180 = rtl8168_csi_fun0_read_byte(tp, 0x214); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_27: + case CFG_METHOD_28: + if (tp->org_pci_offset_99 & BIT_2) + tp->issue_offset_99_event = TRUE; + break; + } + } + + pci_read_config_byte(pdev, 0x80, &tp->org_pci_offset_80); + pci_read_config_byte(pdev, 0x81, &tp->org_pci_offset_81); + + switch (tp->mcfg) { + case CFG_METHOD_16: + case CFG_METHOD_17: + case CFG_METHOD_18: + case CFG_METHOD_19: + case CFG_METHOD_20: + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + if ((tp->features & RTL_FEATURE_MSI) && (tp->org_pci_offset_80 & BIT_1)) + tp->use_timer_interrrupt = FALSE; + else + tp->use_timer_interrrupt = TRUE; + break; + default: + tp->use_timer_interrrupt = TRUE; + break; + } + + if (timer_count == 0 || tp->mcfg == CFG_METHOD_DEFAULT) + tp->use_timer_interrrupt = FALSE; + + switch (tp->mcfg) { + case CFG_METHOD_1: + case CFG_METHOD_2: + case CFG_METHOD_3: + tp->ShortPacketSwChecksum = TRUE; + break; + case CFG_METHOD_16: + case CFG_METHOD_17: + tp->ShortPacketSwChecksum = TRUE; + tp->UseSwPaddingShortPkt = TRUE; + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_30: { + u16 ioffset_p3, ioffset_p2, ioffset_p1, ioffset_p0; + u16 TmpUshort; + + rtl8168_mac_ocp_write( tp, 0xDD02, 0x807D); + TmpUshort = rtl8168_mac_ocp_read( tp, 0xDD02 ); + ioffset_p3 = ( (TmpUshort & BIT_7) >>7 ); + ioffset_p3 <<= 3; + TmpUshort = rtl8168_mac_ocp_read( tp, 0xDD00 ); + + ioffset_p3 |= ((TmpUshort & (BIT_15 | BIT_14 | BIT_13))>>13); + + ioffset_p2 = ((TmpUshort & (BIT_12|BIT_11|BIT_10|BIT_9))>>9); + ioffset_p1 = ((TmpUshort & (BIT_8|BIT_7|BIT_6|BIT_5))>>5); + + ioffset_p0 = ( (TmpUshort & BIT_4) >>4 ); + ioffset_p0 <<= 3; + ioffset_p0 |= (TmpUshort & (BIT_2| BIT_1 | BIT_0)); + + if ((ioffset_p3 == 0x0F) && (ioffset_p2 == 0x0F) && (ioffset_p1 == 0x0F) && (ioffset_p0 == 0x0F)) { + tp->RequireAdcBiasPatch = FALSE; + } else { + tp->RequireAdcBiasPatch = TRUE; + tp->AdcBiasPatchIoffset = (ioffset_p3<<12)|(ioffset_p2<<8)|(ioffset_p1<<4)|(ioffset_p0); + } + } + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: { + u16 rg_saw_cnt; + + rtl8168_mdio_write(tp, 0x1F, 0x0C42); + rg_saw_cnt = rtl8168_mdio_read(tp, 0x13); + rg_saw_cnt &= ~(BIT_15|BIT_14); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + if ( rg_saw_cnt > 0) { + tp->SwrCnt1msIni = 16000000/rg_saw_cnt; + tp->SwrCnt1msIni &= 0x0FFF; + + tp->RequireAdjustUpsTxLinkPulseTiming = TRUE; + } + } + break; + } + +#ifdef ENABLE_FIBER_SUPPORT + rtl8168_check_hw_fiber_mode_support(dev); +#endif //ENABLE_FIBER_SUPPORT + + switch(tp->mcfg) { + case CFG_METHOD_32: + case CFG_METHOD_33: + if (tp->HwPkgDet == 0x06) { + u8 tmpUchar = rtl8168_eri_read(tp, 0xE6, 1, ERIAR_ExGMAC); + if (tmpUchar == 0x02) + tp->HwSuppSerDesPhyVer = 1; + else if (tmpUchar == 0x00) + tp->HwSuppSerDesPhyVer = 2; + } + break; + } + + if (pdev->subsystem_vendor == 0x144d) { + if (pdev->subsystem_device == 0xc098 || + pdev->subsystem_device == 0xc0b1 || + pdev->subsystem_device == 0xc0b8) + hwoptimize |= HW_PATCH_SAMSUNG_LAN_DONGLE; + } + + if (hwoptimize & HW_PATCH_SAMSUNG_LAN_DONGLE) { + switch (tp->mcfg) { + case CFG_METHOD_14: + case CFG_METHOD_15: + case CFG_METHOD_16: + case CFG_METHOD_17: + case CFG_METHOD_18: + case CFG_METHOD_19: + case CFG_METHOD_20: + case CFG_METHOD_30: + tp->RequiredSecLanDonglePatch = TRUE; + break; + } + } + + switch (tp->mcfg) { + case CFG_METHOD_16: + case CFG_METHOD_17: + case CFG_METHOD_18: + case CFG_METHOD_19: + case CFG_METHOD_20: + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + tp->HwSuppMagicPktVer = WAKEUP_MAGIC_PACKET_V2; + break; + case CFG_METHOD_DEFAULT: + tp->HwSuppMagicPktVer = WAKEUP_MAGIC_PACKET_NOT_SUPPORT; + break; + default: + tp->HwSuppMagicPktVer = WAKEUP_MAGIC_PACKET_V1; + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_29: + case CFG_METHOD_30: + tp->HwSuppEsdVer = 2; + break; + default: + tp->HwSuppEsdVer = 1; + break; + } + + if (tp->HwSuppEsdVer == 2) { + rtl8168_mdio_write(tp, 0x1F, 0x0A46); + tp->BackupPhyFuseDout_15_0 = rtl8168_mdio_read(tp, 0x10); + tp->BackupPhyFuseDout_47_32 = rtl8168_mdio_read(tp, 0x12); + tp->BackupPhyFuseDout_63_48 = rtl8168_mdio_read(tp, 0x13); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + + tp->TestPhyOcpReg = TRUE; + } + + switch (tp->mcfg) { + case CFG_METHOD_16: + case CFG_METHOD_17: + tp->HwSuppCheckPhyDisableModeVer = 1; + break; + case CFG_METHOD_18: + case CFG_METHOD_19: + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_29: + case CFG_METHOD_30: + tp->HwSuppCheckPhyDisableModeVer = 2; + break; + case CFG_METHOD_23: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + tp->HwSuppCheckPhyDisableModeVer = 3; + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_14: + case CFG_METHOD_15: + tp->sw_ram_code_ver = NIC_RAMCODE_VERSION_CFG_METHOD_14; + break; + case CFG_METHOD_16: + case CFG_METHOD_17: + tp->sw_ram_code_ver = NIC_RAMCODE_VERSION_CFG_METHOD_16; + break; + case CFG_METHOD_18: + case CFG_METHOD_19: + tp->sw_ram_code_ver = NIC_RAMCODE_VERSION_CFG_METHOD_18; + break; + case CFG_METHOD_20: + tp->sw_ram_code_ver = NIC_RAMCODE_VERSION_CFG_METHOD_20; + break; + case CFG_METHOD_21: + case CFG_METHOD_22: + tp->sw_ram_code_ver = NIC_RAMCODE_VERSION_CFG_METHOD_21; + break; + case CFG_METHOD_23: + case CFG_METHOD_27: + tp->sw_ram_code_ver = NIC_RAMCODE_VERSION_CFG_METHOD_23; + break; + case CFG_METHOD_24: + case CFG_METHOD_25: + tp->sw_ram_code_ver = NIC_RAMCODE_VERSION_CFG_METHOD_24; + break; + case CFG_METHOD_26: + tp->sw_ram_code_ver = NIC_RAMCODE_VERSION_CFG_METHOD_26; + break; + case CFG_METHOD_28: + tp->sw_ram_code_ver = NIC_RAMCODE_VERSION_CFG_METHOD_28; + break; + case CFG_METHOD_29: + case CFG_METHOD_30: + tp->sw_ram_code_ver = NIC_RAMCODE_VERSION_CFG_METHOD_29; + break; + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + tp->sw_ram_code_ver = NIC_RAMCODE_VERSION_CFG_METHOD_31; + break; + } + + if (tp->HwIcVerUnknown) { + tp->NotWrRamCodeToMicroP = TRUE; + tp->NotWrMcuPatchCode = TRUE; + } + + tp->NicCustLedValue = RTL_R16(tp, CustomLED); + + rtl8168_get_hw_wol(dev); + + rtl8168_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->max_jumbo_frame_size = rtl_chip_info[tp->chipset].jumbo_frame_sz; +#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->eee_enabled = eee_enable; + tp->eee_adv_t = MDIO_EEE_1000T | MDIO_EEE_100TX; + +#ifdef ENABLE_FIBER_SUPPORT + if (HW_FIBER_MODE_ENABLED(tp)) + rtl8168_set_fiber_mode_software_variable(dev); +#endif //ENABLE_FIBER_SUPPORT +} + +static void +rtl8168_release_board(struct pci_dev *pdev, + struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + void __iomem *ioaddr = tp->mmio_addr; + + rtl8168_set_bios_setting(dev); + rtl8168_rar_set(tp, tp->org_mac_addr); + tp->wol_enabled = WOL_DISABLED; + + if (!tp->DASH) + rtl8168_phy_power_down(dev); + +#ifdef ENABLE_DASH_SUPPORT + if (tp->DASH) + FreeAllocatedDashShareMemory(dev); +#endif + + if (tp->mapped_cmac_ioaddr != NULL) + iounmap(tp->mapped_cmac_ioaddr); + + iounmap(ioaddr); + pci_release_regions(pdev); + pci_clear_mwi(pdev); + pci_disable_device(pdev); + free_netdev(dev); +} + +static int +rtl8168_get_mac_address(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + int i; + u8 mac_addr[MAC_ADDR_LEN]; + + for (i = 0; i < MAC_ADDR_LEN; i++) + mac_addr[i] = RTL_R8(tp, MAC0 + i); + + if (tp->mcfg == CFG_METHOD_18 || + tp->mcfg == CFG_METHOD_19 || + tp->mcfg == CFG_METHOD_20 || + tp->mcfg == CFG_METHOD_21 || + tp->mcfg == CFG_METHOD_22 || + tp->mcfg == CFG_METHOD_23 || + tp->mcfg == CFG_METHOD_24 || + tp->mcfg == CFG_METHOD_25 || + tp->mcfg == CFG_METHOD_26 || + tp->mcfg == CFG_METHOD_27 || + tp->mcfg == CFG_METHOD_28 || + tp->mcfg == CFG_METHOD_29 || + tp->mcfg == CFG_METHOD_30 || + tp->mcfg == CFG_METHOD_31 || + tp->mcfg == CFG_METHOD_32 || + tp->mcfg == CFG_METHOD_33) { + *(u32*)&mac_addr[0] = rtl8168_eri_read(tp, 0xE0, 4, ERIAR_ExGMAC); + *(u16*)&mac_addr[4] = rtl8168_eri_read(tp, 0xE4, 2, ERIAR_ExGMAC); + } else { + if (tp->eeprom_type != EEPROM_TYPE_NONE) { + u16 *pUshort = (u16*)mac_addr; + /* Get MAC address from EEPROM */ + if (tp->mcfg == CFG_METHOD_16 || + tp->mcfg == CFG_METHOD_17 || + tp->mcfg == CFG_METHOD_18 || + tp->mcfg == CFG_METHOD_19 || + tp->mcfg == CFG_METHOD_20 || + tp->mcfg == CFG_METHOD_21 || + tp->mcfg == CFG_METHOD_22 || + tp->mcfg == CFG_METHOD_23 || + tp->mcfg == CFG_METHOD_24 || + tp->mcfg == CFG_METHOD_25 || + tp->mcfg == CFG_METHOD_26 || + tp->mcfg == CFG_METHOD_27 || + tp->mcfg == CFG_METHOD_28 || + tp->mcfg == CFG_METHOD_29 || + tp->mcfg == CFG_METHOD_30 || + tp->mcfg == CFG_METHOD_31 || + tp->mcfg == CFG_METHOD_32 || + tp->mcfg == CFG_METHOD_33) { + *pUshort++ = rtl8168_eeprom_read_sc(tp, 1); + *pUshort++ = rtl8168_eeprom_read_sc(tp, 2); + *pUshort = rtl8168_eeprom_read_sc(tp, 3); + } else { + *pUshort++ = rtl8168_eeprom_read_sc(tp, 7); + *pUshort++ = rtl8168_eeprom_read_sc(tp, 8); + *pUshort = rtl8168_eeprom_read_sc(tp, 9); + } + } + } + + if (!is_valid_ether_addr(mac_addr)) { + netif_err(tp, probe, dev, "Invalid ether addr %pM\n", + mac_addr); + eth_hw_addr_random(dev); + ether_addr_copy(mac_addr, dev->dev_addr); + netif_info(tp, probe, dev, "Random ether addr %pM\n", + mac_addr); + tp->random_mac = 1; + } + + rtl8168_rar_set(tp, mac_addr); + + for (i = 0; i < MAC_ADDR_LEN; i++) { + dev->dev_addr[i] = RTL_R8(tp, MAC0 + i); + tp->org_mac_addr[i] = dev->dev_addr[i]; /* keep the original MAC address */ + } +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13) + memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); +#endif +// memcpy(dev->dev_addr, dev->dev_addr, dev->addr_len); + + return 0; +} + +/** + * rtl8168_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 +rtl8168_set_mac_address(struct net_device *dev, + void *p) +{ + struct rtl8168_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); + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + + rtl8168_rar_set(tp, dev->dev_addr); + + spin_unlock_irqrestore(&tp->lock, flags); + + return 0; +} + +/****************************************************************************** + * rtl8168_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 +rtl8168_rar_set(struct rtl8168_private *tp, + uint8_t *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)); + + rtl8168_enable_cfg9346_write(tp); + RTL_W32(tp, MAC0, rar_low); + RTL_W32(tp, MAC4, rar_high); + + switch (tp->mcfg) { + case CFG_METHOD_14: + case CFG_METHOD_15: + RTL_W32(tp, SecMAC0, rar_low); + RTL_W16(tp, SecMAC4, (uint16_t)rar_high); + break; + } + + if (tp->mcfg == CFG_METHOD_17) { + rtl8168_eri_write(tp, 0xf0, 4, rar_low << 16, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xf4, 4, rar_low >> 16 | rar_high << 16, ERIAR_ExGMAC); + } + + rtl8168_disable_cfg9346_write(tp); +} + +#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 +rtl8168_do_ioctl(struct net_device *dev, + struct ifreq *ifr, + int cmd) +{ + struct rtl8168_private *tp = netdev_priv(dev); + struct mii_ioctl_data *data = if_mii(ifr); + int ret; + unsigned long flags; + + ret = 0; + switch (cmd) { + case SIOCGMIIPHY: + data->phy_id = 32; /* Internal PHY */ + break; + + case SIOCGMIIREG: + spin_lock_irqsave(&tp->lock, flags); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + data->val_out = rtl8168_mdio_read(tp, data->reg_num); + spin_unlock_irqrestore(&tp->lock, flags); + break; + + case SIOCSMIIREG: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + spin_lock_irqsave(&tp->lock, flags); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + rtl8168_mdio_write(tp, data->reg_num, data->val_in); + spin_unlock_irqrestore(&tp->lock, flags); + break; + +#ifdef ETHTOOL_OPS_COMPAT + case SIOCETHTOOL: + ret = ethtool_ioctl(ifr); + break; +#endif + case SIOCDEVPRIVATE_RTLASF: + if (!netif_running(dev)) { + ret = -ENODEV; + break; + } + + ret = rtl8168_asf_ioctl(dev, ifr); + break; + +#ifdef ENABLE_DASH_SUPPORT + case SIOCDEVPRIVATE_RTLDASH: + if (!netif_running(dev)) { + ret = -ENODEV; + break; + } + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + break; + } + + ret = rtl8168_dash_ioctl(dev, ifr); + break; +#endif + +#ifdef ENABLE_REALWOW_SUPPORT + case SIOCDEVPRIVATE_RTLREALWOW: + if (!netif_running(dev)) { + ret = -ENODEV; + break; + } + + ret = rtl8168_realwow_ioctl(dev, ifr); + break; +#endif + + case SIOCRTLTOOL: + ret = rtl8168_tool_ioctl(tp, ifr); + break; + + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +static void +rtl8168_phy_power_up(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + if (rtl8168_is_in_phy_disable_mode(dev)) + return; + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + switch (tp->mcfg) { + case CFG_METHOD_1: + case CFG_METHOD_2: + case CFG_METHOD_3: + case CFG_METHOD_4: + case CFG_METHOD_5: + case CFG_METHOD_6: + case CFG_METHOD_7: + case CFG_METHOD_8: + case CFG_METHOD_9: + case CFG_METHOD_10: + case CFG_METHOD_11: + case CFG_METHOD_12: + case CFG_METHOD_13: + rtl8168_mdio_write(tp, 0x0E, 0x0000); + break; + } + rtl8168_mdio_write(tp, MII_BMCR, BMCR_ANENABLE); + + //wait mdc/mdio ready + switch (tp->mcfg) { + case CFG_METHOD_23: + case CFG_METHOD_27: + case CFG_METHOD_28: + mdelay(10); + break; + } + + //wait ups resume (phy state 3) + if (HW_SUPPORT_UPS_MODE(tp)) + rtl8168_wait_phy_ups_resume(dev, HW_PHY_STATUS_LAN_ON); +} + +static void +rtl8168_phy_power_down(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u32 csi_tmp; + + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + csi_tmp = rtl8168_eri_read(tp, 0x1AB, 1, ERIAR_ExGMAC); + csi_tmp &= ~( BIT_2 | BIT_3 | BIT_4 | BIT_5 | BIT_6 | BIT_7 ); + rtl8168_eri_write(tp, 0x1AB, 1, csi_tmp, ERIAR_ExGMAC); + break; + } + + rtl8168_mdio_write(tp, 0x1F, 0x0000); + switch (tp->mcfg) { + case CFG_METHOD_1: + case CFG_METHOD_2: + case CFG_METHOD_3: + case CFG_METHOD_4: + case CFG_METHOD_5: + case CFG_METHOD_6: + case CFG_METHOD_7: + case CFG_METHOD_8: + case CFG_METHOD_9: + case CFG_METHOD_10: + case CFG_METHOD_11: + case CFG_METHOD_12: + case CFG_METHOD_13: + rtl8168_mdio_write(tp, 0x0E, 0x0200); + rtl8168_mdio_write(tp, MII_BMCR, BMCR_PDOWN); + break; + case CFG_METHOD_14: + case CFG_METHOD_15: + case CFG_METHOD_29: + case CFG_METHOD_30: + rtl8168_mdio_write(tp, MII_BMCR, BMCR_ANENABLE | BMCR_PDOWN); + break; + case CFG_METHOD_21: + case CFG_METHOD_22: + rtl8168_mdio_write(tp, MII_BMCR, BMCR_ANENABLE | BMCR_PDOWN); + break; + case CFG_METHOD_23: + case CFG_METHOD_24: + rtl8168_mdio_write(tp, MII_BMCR, BMCR_ANENABLE | BMCR_PDOWN); + break; + default: + rtl8168_mdio_write(tp, MII_BMCR, BMCR_PDOWN); + break; + } +} + +static int __devinit +rtl8168_init_board(struct pci_dev *pdev, + struct net_device **dev_out, + void __iomem **ioaddr_out) +{ + void __iomem *ioaddr; + struct net_device *dev; + struct rtl8168_private *tp; + int rc = -ENOMEM, i, pm_cap; + + assert(ioaddr_out != NULL); + + /* dev zeroed in alloc_etherdev */ + dev = alloc_etherdev(sizeof (*tp)); + if (dev == NULL) { +#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) + goto err_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, R8168_MSG_DEFAULT); + + if (!aspm || tp->mcfg == CFG_METHOD_9) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) + pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | + PCIE_LINK_STATE_CLKPM); +#endif + } + + /* 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; + } + + 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; + + pci_read_config_word(pdev, pm_cap + PCI_PM_CTRL, &pwr_command); + } 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, 2) & IORESOURCE_MEM)) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) + if (netif_msg_probe(tp)) + dev_err(&pdev->dev, "region #1 not an MMIO resource, aborting\n"); +#endif //LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) + rc = -ENODEV; + goto err_out_mwi; + } + /* check for weird/broken PCI region reporting */ + if (pci_resource_len(pdev, 2) < R8168_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; + } + + 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; + } + + if ((sizeof(dma_addr_t) > 4) && + use_dac && + !pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) && + !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64))) { + dev->features |= NETIF_F_HIGHDMA; + } else { + rc = pci_set_dma_mask(pdev, 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; + } + } + + /* ioremap MMIO region */ + ioaddr = ioremap(pci_resource_start(pdev, 2), R8168_REGS_SIZE); + if (ioaddr == NULL) { +#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; + } + + tp->mmio_addr = ioaddr; + + /* Identify chip attached to board */ + rtl8168_get_mac_version(tp); + + rtl8168_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-8168 */ +#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; + + *ioaddr_out = ioaddr; + *dev_out = dev; +out: + return rc; + +err_out_free_res: + pci_release_regions(pdev); +err_out_mwi: + pci_clear_mwi(pdev); + pci_disable_device(pdev); +err_out_free_dev: + free_netdev(dev); +err_out: + *ioaddr_out = NULL; + *dev_out = NULL; + goto out; +} + +static void +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) +rtl8168_esd_timer(unsigned long __opaque) +#else +rtl8168_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 rtl8168_private *tp = netdev_priv(dev); + struct timer_list *timer = &tp->esd_timer; +#else + struct rtl8168_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 = RTL8168_ESD_TIMEOUT; + unsigned long flags; + u8 cmd; + u16 io_base_l; + u16 mem_base_l; + u16 mem_base_h; + u8 ilr; + u16 resv_0x1c_h; + u16 resv_0x1c_l; + u16 resv_0x20_l; + u16 resv_0x20_h; + u16 resv_0x24_l; + u16 resv_0x24_h; + u16 resv_0x2c_h; + u16 resv_0x2c_l; + u32 pci_sn_l; + u32 pci_sn_h; + + 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_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_word(pdev, PCI_BASE_ADDRESS_3, &resv_0x1c_l); + if (resv_0x1c_l != tp->pci_cfg_space.resv_0x1c_l) { + printk(KERN_ERR "%s: resv_0x1c_l = 0x%04x, should be 0x%04x \n.", dev->name, resv_0x1c_l, tp->pci_cfg_space.resv_0x1c_l); + pci_write_config_word(pdev, PCI_BASE_ADDRESS_3, tp->pci_cfg_space.resv_0x1c_l); + tp->esd_flag |= BIT_4; + } + + pci_read_config_word(pdev, PCI_BASE_ADDRESS_3 + 2, &resv_0x1c_h); + if (resv_0x1c_h != tp->pci_cfg_space.resv_0x1c_h) { + printk(KERN_ERR "%s: resv_0x1c_h = 0x%04x, should be 0x%04x \n.", dev->name, resv_0x1c_h, tp->pci_cfg_space.resv_0x1c_h); + pci_write_config_word(pdev, PCI_BASE_ADDRESS_3 + 2, tp->pci_cfg_space.resv_0x1c_h); + tp->esd_flag |= BIT_5; + } + + 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; + } + + pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &resv_0x2c_l); + if (resv_0x2c_l != tp->pci_cfg_space.resv_0x2c_l) { + printk(KERN_ERR "%s: resv_0x2c_l = 0x%04x, should be 0x%04x \n.", dev->name, resv_0x2c_l, tp->pci_cfg_space.resv_0x2c_l); + pci_write_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, tp->pci_cfg_space.resv_0x2c_l); + tp->esd_flag |= BIT_11; + } + + pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID + 2, &resv_0x2c_h); + if (resv_0x2c_h != tp->pci_cfg_space.resv_0x2c_h) { + printk(KERN_ERR "%s: resv_0x2c_h = 0x%04x, should be 0x%04x \n.", dev->name, resv_0x2c_h, tp->pci_cfg_space.resv_0x2c_h); + pci_write_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID + 2, tp->pci_cfg_space.resv_0x2c_h); + tp->esd_flag |= BIT_12; + } + + if (tp->HwPcieSNOffset > 0) { + pci_sn_l = rtl8168_csi_read(tp, tp->HwPcieSNOffset); + if (pci_sn_l != tp->pci_cfg_space.pci_sn_l) { + printk(KERN_ERR "%s: pci_sn_l = 0x%08x, should be 0x%08x \n.", dev->name, pci_sn_l, tp->pci_cfg_space.pci_sn_l); + rtl8168_csi_write(tp, tp->HwPcieSNOffset, tp->pci_cfg_space.pci_sn_l); + tp->esd_flag |= BIT_13; + } + + pci_sn_h = rtl8168_csi_read(tp, tp->HwPcieSNOffset + 4); + if (pci_sn_h != tp->pci_cfg_space.pci_sn_h) { + printk(KERN_ERR "%s: pci_sn_h = 0x%08x, should be 0x%08x \n.", dev->name, pci_sn_h, tp->pci_cfg_space.pci_sn_h); + rtl8168_csi_write(tp, tp->HwPcieSNOffset + 4, tp->pci_cfg_space.pci_sn_h); + tp->esd_flag |= BIT_14; + } + } + + if (tp->TestPhyOcpReg && rtl8168_test_phy_ocp(tp)) + tp->esd_flag |= BIT_15; + + 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); + rtl8168_hw_reset(dev); + rtl8168_tx_clear(tp); + rtl8168_rx_clear(tp); + rtl8168_init_ring(dev); + rtl8168_hw_init(dev); + rtl8168_powerup_pll(dev); + rtl8168_hw_ephy_config(dev); + rtl8168_hw_phy_config(dev); + rtl8168_hw_config(dev); + rtl8168_set_speed(dev, tp->autoneg, tp->speed, tp->duplex, tp->advertising); + tp->esd_flag = 0; + } + spin_unlock_irqrestore(&tp->lock, flags); + + mod_timer(timer, jiffies + timeout); +} + +static void +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) +rtl8168_link_timer(unsigned long __opaque) +#else +rtl8168_link_timer(struct timer_list *t) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) + struct net_device *dev = (struct net_device *)__opaque; + struct rtl8168_private *tp = netdev_priv(dev); + struct timer_list *timer = &tp->link_timer; +#else + struct rtl8168_private *tp = from_timer(tp, t, link_timer); + struct net_device *dev = tp->dev; + struct timer_list *timer = t; +#endif + unsigned long flags; + + spin_lock_irqsave(&tp->lock, flags); + rtl8168_check_link_status(dev); + spin_unlock_irqrestore(&tp->lock, flags); + + mod_timer(timer, jiffies + RTL8168_LINK_TIMEOUT); +} + +/* Cfg9346_Unlock assumed. */ +static unsigned rtl8168_try_msi(struct pci_dev *pdev, struct rtl8168_private *tp) +{ + unsigned msi = 0; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13) + switch (tp->mcfg) { + case CFG_METHOD_1: + case CFG_METHOD_2: + case CFG_METHOD_3: + case CFG_METHOD_4: + case CFG_METHOD_5: + case CFG_METHOD_6: + case CFG_METHOD_7: + case CFG_METHOD_8: + dev_info(&pdev->dev, "Default use INTx.\n"); + break; + default: + if (pci_enable_msi(pdev)) + dev_info(&pdev->dev, "no MSI. Back to INTx.\n"); + else + msi |= RTL_FEATURE_MSI; + break; + } +#endif + + return msi; +} + +static void rtl8168_disable_msi(struct pci_dev *pdev, struct rtl8168_private *tp) +{ + if (tp->features & RTL_FEATURE_MSI) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,13) + pci_disable_msi(pdev); +#endif + tp->features &= ~RTL_FEATURE_MSI; + } +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) +static const struct net_device_ops rtl8168_netdev_ops = { + .ndo_open = rtl8168_open, + .ndo_stop = rtl8168_close, + .ndo_get_stats = rtl8168_get_stats, + .ndo_start_xmit = rtl8168_start_xmit, + .ndo_tx_timeout = rtl8168_tx_timeout, + .ndo_change_mtu = rtl8168_change_mtu, + .ndo_set_mac_address = rtl8168_set_mac_address, + .ndo_do_ioctl = rtl8168_do_ioctl, +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,1,0) + .ndo_set_multicast_list = rtl8168_set_rx_mode, +#else + .ndo_set_rx_mode = rtl8168_set_rx_mode, +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) +#ifdef CONFIG_R8168_VLAN + .ndo_vlan_rx_register = rtl8168_vlan_rx_register, +#endif +#else + .ndo_fix_features = rtl8168_fix_features, + .ndo_set_features = rtl8168_set_features, +#endif +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = rtl8168_netpoll, +#endif +}; +#endif + +static int __devinit +rtl8168_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *dev = NULL; + struct rtl8168_private *tp; + void __iomem *ioaddr = NULL; + static int board_idx = -1; + + int rc; + + assert(pdev != NULL); + assert(ent != NULL); + + board_idx++; + + if (netif_msg_drv(&debug)) + printk(KERN_INFO "%s Gigabit Ethernet driver %s loaded\n", + MODULENAME, RTL8168_VERSION); + + rc = rtl8168_init_board(pdev, &dev, &ioaddr); + if (rc) + goto out; + + tp = netdev_priv(dev); + assert(ioaddr != NULL); + + tp->set_speed = rtl8168_set_speed_xmii; + tp->get_settings = rtl8168_gset_xmii; + tp->phy_reset_enable = rtl8168_xmii_reset_enable; + tp->phy_reset_pending = rtl8168_xmii_reset_pending; + tp->link_ok = rtl8168_xmii_link_ok; + + tp->features |= rtl8168_try_msi(pdev, tp); + + RTL_NET_DEVICE_OPS(rtl8168_netdev_ops); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,22) + SET_ETHTOOL_OPS(dev, &rtl8168_ethtool_ops); +#endif + + dev->watchdog_timeo = RTL8168_TX_TIMEOUT; + dev->irq = pdev->irq; + dev->base_addr = (unsigned long) ioaddr; + +#ifdef CONFIG_R8168_NAPI + RTL_NAPI_CONFIG(dev, tp, rtl8168_poll, R8168_NAPI_WEIGHT); +#endif + +#ifdef CONFIG_R8168_VLAN + if (tp->mcfg != CFG_METHOD_DEFAULT) { + 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 = rtl8168_vlan_rx_kill_vid; +#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) + } +#endif + + /* 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! + */ + tp->cp_cmd |= RTL_R16(tp, CPlusCmd); + if (tp->mcfg != CFG_METHOD_DEFAULT) { + dev->features |= NETIF_F_IP_CSUM; +#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_RXCSUM | NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; + dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | + NETIF_F_HIGHDMA; + if ((tp->mcfg != CFG_METHOD_16) && (tp->mcfg != CFG_METHOD_17)) { + //dev->features |= NETIF_F_TSO; + dev->hw_features |= NETIF_F_TSO; + dev->vlan_features |= NETIF_F_TSO; + } +#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(2,6,22) + if ((tp->mcfg == CFG_METHOD_1) || (tp->mcfg == CFG_METHOD_2) || (tp->mcfg == CFG_METHOD_3)) { + dev->hw_features &= ~NETIF_F_IPV6_CSUM; + 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) + } else { + dev->hw_features |= NETIF_F_IPV6_CSUM; + dev->features |= NETIF_F_IPV6_CSUM; + if ((tp->mcfg != CFG_METHOD_16) && (tp->mcfg != CFG_METHOD_17)) { + dev->hw_features |= NETIF_F_TSO6; + //dev->features |= NETIF_F_TSO6; + } + netif_set_gso_max_size(dev, LSO_64K); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0) + dev->gso_max_segs = NIC_MAX_PHYS_BUF_COUNT_LSO2; +#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(2,6,22) +#endif //LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) + } + + tp->pci_dev = pdev; + + spin_lock_init(&tp->lock); + + rtl8168_init_software_variable(dev); + +#ifdef ENABLE_DASH_SUPPORT + if (tp->DASH) + AllocateDashShareMemory(dev); +#endif + + rtl8168_exit_oob(dev); + + rtl8168_hw_init(dev); + + rtl8168_hw_reset(dev); + + /* Get production from EEPROM */ + if (((tp->mcfg == CFG_METHOD_21 || tp->mcfg == CFG_METHOD_22 || + tp->mcfg == CFG_METHOD_25 || tp->mcfg == CFG_METHOD_29 || + tp->mcfg == CFG_METHOD_30) && (rtl8168_mac_ocp_read(tp, 0xDC00) & BIT_3)) || + ((tp->mcfg == CFG_METHOD_26) && (rtl8168_mac_ocp_read(tp, 0xDC00) & BIT_4))) + tp->eeprom_type = EEPROM_TYPE_NONE; + else + rtl8168_eeprom_type(tp); + + if (tp->eeprom_type == EEPROM_TYPE_93C46 || tp->eeprom_type == EEPROM_TYPE_93C56) + rtl8168_set_eeprom_sel_low(tp); + + rtl8168_get_mac_address(dev); + + tp->fw_name = rtl_chip_fw_infos[tp->mcfg].fw_name; + +#if defined(ENABLE_DASH_PRINTER_SUPPORT) + init_completion(&tp->fw_host_ok); + init_completion(&tp->fw_ack); + init_completion(&tp->fw_req); +#endif + + tp->tally_vaddr = dma_alloc_coherent(&pdev->dev, sizeof(*tp->tally_vaddr), + &tp->tally_paddr, GFP_KERNEL); + if (!tp->tally_vaddr) { + rc = -ENOMEM; + goto err_out; + } + + rtl8168_tally_counter_clear(tp); + + pci_set_drvdata(pdev, dev); + + rc = register_netdev(dev); + if (rc) + goto err_out; + + 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); + + rtl8168_disable_rxdvgate(dev); + + device_set_wakeup_enable(&pdev->dev, tp->wol_enabled); + + netif_carrier_off(dev); + + printk("%s", GPL_CLAIM); + +out: + return rc; + +err_out: + if (tp->tally_vaddr != NULL) { + dma_free_coherent(&pdev->dev, sizeof(*tp->tally_vaddr), tp->tally_vaddr, + tp->tally_paddr); + + tp->tally_vaddr = NULL; + } +#ifdef CONFIG_R8168_NAPI + RTL_NAPI_DEL(tp); +#endif + rtl8168_disable_msi(pdev, tp); + rtl8168_release_board(pdev, dev); + + goto out; +} + +static void __devexit +rtl8168_remove_one(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8168_private *tp = netdev_priv(dev); + + assert(dev != NULL); + assert(tp != NULL); + +#ifdef CONFIG_R8168_NAPI + RTL_NAPI_DEL(tp); +#endif + if (HW_DASH_SUPPORT_DASH(tp)) + rtl8168_driver_stop(tp); + + unregister_netdev(dev); + rtl8168_disable_msi(pdev, tp); +#ifdef ENABLE_R8168_PROCFS + rtl8168_proc_remove(dev); +#endif + if (tp->tally_vaddr != NULL) { + dma_free_coherent(&pdev->dev, sizeof(*tp->tally_vaddr), tp->tally_vaddr, tp->tally_paddr); + tp->tally_vaddr = NULL; + } + + rtl8168_release_board(pdev, dev); + +#ifdef ENABLE_USE_FIRMWARE_FILE + rtl8168_release_firmware(tp); +#endif + + pci_set_drvdata(pdev, NULL); +} + +static void +rtl8168_set_rxbufsize(struct rtl8168_private *tp, + struct net_device *dev) +{ + unsigned int mtu = dev->mtu; + + tp->rx_buf_sz = (mtu > ETH_DATA_LEN) ? mtu + ETH_HLEN + 8 + 1 : RX_BUF_SIZE; +} + +#ifdef ENABLE_USE_FIRMWARE_FILE +static void rtl8168_request_firmware(struct rtl8168_private *tp) +{ + struct rtl8168_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 = rtl8168_mdio_write; + rtl_fw->phy_read = rtl8168_mdio_read; + 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 (rtl8168_fw_request_firmware(rtl_fw)) + kfree(rtl_fw); + else + tp->rtl_fw = rtl_fw; +} +#endif + +static int rtl8168_open(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + struct pci_dev *pdev = tp->pci_dev; + unsigned long flags; + int retval; + + retval = -ENOMEM; + +#ifdef ENABLE_R8168_PROCFS + rtl8168_proc_init(dev); +#endif + rtl8168_set_rxbufsize(tp, dev); + /* + * Rx and Tx descriptors needs 256 bytes alignment. + * pci_alloc_consistent provides more. + */ + tp->TxDescArray = dma_alloc_coherent(&pdev->dev, R8168_TX_RING_BYTES, + &tp->TxPhyAddr, GFP_KERNEL); + if (!tp->TxDescArray) + goto err_free_all_allocated_mem; + + tp->RxDescArray = dma_alloc_coherent(&pdev->dev, R8168_RX_RING_BYTES, + &tp->RxPhyAddr, GFP_KERNEL); + if (!tp->RxDescArray) + goto err_free_all_allocated_mem; + + retval = rtl8168_init_ring(dev); + if (retval < 0) + goto err_free_all_allocated_mem; + + retval = request_irq(dev->irq, rtl8168_interrupt, (tp->features & RTL_FEATURE_MSI) ? 0 : SA_SHIRQ, dev->name, dev); + if (retval<0) + goto err_free_all_allocated_mem; + + if (netif_msg_probe(tp)) { + printk(KERN_INFO "%s: 0x%lx, " + "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " + "IRQ %d\n", + dev->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); + } + +#ifdef ENABLE_USE_FIRMWARE_FILE + rtl8168_request_firmware(tp); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + INIT_WORK(&tp->task, rtl8168_reset_task, dev); +#else + INIT_DELAYED_WORK(&tp->task, rtl8168_reset_task); +#endif + + pci_set_master(pdev); + +#ifdef CONFIG_R8168_NAPI +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) + RTL_NAPI_ENABLE(dev, &tp->napi); +#endif +#endif + spin_lock_irqsave(&tp->lock, flags); + + rtl8168_exit_oob(dev); + + rtl8168_hw_init(dev); + + rtl8168_hw_reset(dev); + + rtl8168_powerup_pll(dev); + + rtl8168_hw_ephy_config(dev); + + rtl8168_hw_phy_config(dev); + + rtl8168_hw_config(dev); + + rtl8168_dsm(dev, DSM_IF_UP); + + rtl8168_set_speed(dev, tp->autoneg, tp->speed, tp->duplex, tp->advertising); + + spin_unlock_irqrestore(&tp->lock, flags); + + if (tp->esd_flag == 0) + rtl8168_request_esd_timer(dev); + + rtl8168_request_link_timer(dev); + +out: + + return retval; + +err_free_all_allocated_mem: + if (tp->RxDescArray != NULL) { + dma_free_coherent(&pdev->dev, R8168_RX_RING_BYTES, tp->RxDescArray, + tp->RxPhyAddr); + tp->RxDescArray = NULL; + } + + if (tp->TxDescArray != NULL) { + dma_free_coherent(&pdev->dev, R8168_TX_RING_BYTES, tp->TxDescArray, + tp->TxPhyAddr); + tp->TxDescArray = NULL; + } + + goto out; +} + +static void +rtl8168_dsm(struct net_device *dev, int dev_state) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + switch (dev_state) { + case DSM_MAC_INIT: + if ((tp->mcfg == CFG_METHOD_5) || (tp->mcfg == CFG_METHOD_6)) { + if (RTL_R8(tp, MACDBG) & 0x80) + RTL_W8(tp, GPIO, RTL_R8(tp, GPIO) | GPIO_en); + else + RTL_W8(tp, GPIO, RTL_R8(tp, GPIO) & ~GPIO_en); + } + + break; + case DSM_NIC_GOTO_D3: + case DSM_IF_DOWN: + if ((tp->mcfg == CFG_METHOD_5) || (tp->mcfg == CFG_METHOD_6)) { + if (RTL_R8(tp, MACDBG) & 0x80) + RTL_W8(tp, GPIO, RTL_R8(tp, GPIO) & ~GPIO_en); + } + break; + + case DSM_NIC_RESUME_D3: + case DSM_IF_UP: + if ((tp->mcfg == CFG_METHOD_5) || (tp->mcfg == CFG_METHOD_6)) { + if (RTL_R8(tp, MACDBG) & 0x80) + RTL_W8(tp, GPIO, RTL_R8(tp, GPIO) | GPIO_en); + } + + break; + } + +} + +static void +set_offset70F(struct rtl8168_private *tp, u8 setting) +{ + u32 csi_tmp; + u32 temp = (u32)setting; + temp = temp << 24; + /*set PCI configuration space offset 0x70F to setting*/ + /*When the register offset of PCI configuration space larger than 0xff, use CSI to access it.*/ + + csi_tmp = rtl8168_csi_read(tp, 0x70c) & 0x00ffffff; + rtl8168_csi_write(tp, 0x70c, csi_tmp | temp); +} + +static void +set_offset79(struct rtl8168_private *tp, u8 setting) +{ + //Set PCI configuration space offset 0x79 to setting + + struct pci_dev *pdev = tp->pci_dev; + u8 device_control; + + if (hwoptimize & HW_PATCH_SOC_LAN) return; + + pci_read_config_byte(pdev, 0x79, &device_control); + device_control &= ~0x70; + device_control |= setting; + pci_write_config_byte(pdev, 0x79, device_control); +} + +static void +set_offset711(struct rtl8168_private *tp, u8 setting) +{ + u32 csi_tmp; + u32 temp = (u32)setting; + temp &= 0x0f; + temp = temp << 12; + /*set PCI configuration space offset 0x711 to setting*/ + + csi_tmp = rtl8168_csi_read(tp, 0x710) & 0xffff0fff; + rtl8168_csi_write(tp, 0x710, csi_tmp | temp); +} + +static void +rtl8168_hw_set_rx_packet_filter(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + 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; + unsigned 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 = mc_filter[0]; + mc_filter[0] = swab32(mc_filter[1]); + mc_filter[1] = swab32(tmp); + + tp->rtl8168_rx_config = rtl_chip_info[tp->chipset].RCR_Cfg; + tmp = tp->rtl8168_rx_config | rx_mode | (RTL_R32(tp, RxConfig) & rtl_chip_info[tp->chipset].RxConfigMask); + + RTL_W32(tp, RxConfig, tmp); + RTL_W32(tp, MAR0 + 0, mc_filter[0]); + RTL_W32(tp, MAR0 + 4, mc_filter[1]); +} + +static void +rtl8168_set_rx_mode(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&tp->lock, flags); + + rtl8168_hw_set_rx_packet_filter(dev); + + spin_unlock_irqrestore(&tp->lock, flags); +} + +static void +rtl8168_hw_config(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + struct pci_dev *pdev = tp->pci_dev; + u8 device_control; + u16 mac_ocp_data; + u32 csi_tmp; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) + if (dev->mtu > ETH_DATA_LEN) { + dev->features &= ~(NETIF_F_IP_CSUM); + } else { + dev->features |= NETIF_F_IP_CSUM; + } +#endif + + RTL_W32(tp, RxConfig, (RX_DMA_BURST << RxCfgDMAShift)); + + rtl8168_hw_reset(dev); + + rtl8168_enable_cfg9346_write(tp); + if (tp->HwSuppAspmClkIntrLock) { + RTL_W8(tp, 0xF1, RTL_R8(tp, 0xF1) & ~BIT_7); + rtl8168_hw_aspm_clkreq_enable(tp, false); + } + + //clear io_rdy_l23 + switch (tp->mcfg) { + case CFG_METHOD_20: + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~BIT_1); + break; + } + + //keep magic packet only + switch (tp->mcfg) { + case CFG_METHOD_16: + case CFG_METHOD_17: + case CFG_METHOD_18: + case CFG_METHOD_19: + case CFG_METHOD_20: + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + csi_tmp = rtl8168_eri_read(tp, 0xDE, 1, ERIAR_ExGMAC); + csi_tmp &= BIT_0; + rtl8168_eri_write(tp, 0xDE, 1, csi_tmp, ERIAR_ExGMAC); + break; + } + + RTL_W8(tp, MTPS, Reserved1_data); + + tp->cp_cmd |= INTT_1; + if (tp->use_timer_interrrupt) + tp->cp_cmd |= PktCntrDisable; + else + tp->cp_cmd &= ~PktCntrDisable; + + RTL_W16(tp, IntrMitigate, 0x5f51); + + rtl8168_tally_counter_addr_fill(tp); + + rtl8168_desc_addr_fill(tp); + + /* Set DMA burst size and Interframe Gap Time */ + if (tp->mcfg == CFG_METHOD_1) + RTL_W32(tp, TxConfig, (TX_DMA_BURST_512 << TxDMAShift) | + (InterFrameGap << TxInterFrameGapShift)); + else + RTL_W32(tp, TxConfig, (TX_DMA_BURST_unlimited << TxDMAShift) | + (InterFrameGap << TxInterFrameGapShift)); + + if (tp->mcfg == CFG_METHOD_4) { + set_offset70F(tp, 0x27); + + RTL_W8(tp, DBG_reg, (0x0E << 4) | Fix_Nak_1 | Fix_Nak_2); + + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); + + //disable clock request. + pci_write_config_byte(pdev, 0x81, 0x00); + + if (dev->mtu > ETH_DATA_LEN) { + RTL_W8(tp, Config3, RTL_R8(tp, Config3) | Jumbo_En0); + RTL_W8(tp, Config4, RTL_R8(tp, Config4) | Jumbo_En1); + + set_offset79(tp, 0x20); + } else { + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Jumbo_En0); + RTL_W8(tp, Config4, RTL_R8(tp, Config4) & ~Jumbo_En1); + + } + + //rx checksum offload enable +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) + tp->cp_cmd |= RxChkSum; +#else + dev->features |= NETIF_F_RXCSUM; +#endif + + tp->cp_cmd &= ~(EnableBist | Macdbgo_oe | Force_halfdup | + Force_rxflow_en | Force_txflow_en | Cxpl_dbg_sel | + ASF | PktCntrDisable | Macdbgo_sel); + } else if (tp->mcfg == CFG_METHOD_5) { + + set_offset70F(tp, 0x27); + + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); + + //disable clock request. + pci_write_config_byte(pdev, 0x81, 0x00); + + if (dev->mtu > ETH_DATA_LEN) { + RTL_W8(tp, Config3, RTL_R8(tp, Config3) | Jumbo_En0); + RTL_W8(tp, Config4, RTL_R8(tp, Config4) | Jumbo_En1); + + set_offset79(tp, 0x20); + } else { + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Jumbo_En0); + RTL_W8(tp, Config4, RTL_R8(tp, Config4) & ~Jumbo_En1); + + } + + //rx checksum offload enable +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) + tp->cp_cmd |= RxChkSum; +#else + dev->features |= NETIF_F_RXCSUM; +#endif + } else if (tp->mcfg == CFG_METHOD_6) { + set_offset70F(tp, 0x27); + + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); + + //disable clock request. + pci_write_config_byte(pdev, 0x81, 0x00); + + if (dev->mtu > ETH_DATA_LEN) { + RTL_W8(tp, Config3, RTL_R8(tp, Config3) | Jumbo_En0); + RTL_W8(tp, Config4, RTL_R8(tp, Config4) | Jumbo_En1); + + set_offset79(tp, 0x20); + } else { + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Jumbo_En0); + RTL_W8(tp, Config4, RTL_R8(tp, Config4) & ~Jumbo_En1); + + } + + //rx checksum offload enable +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) + tp->cp_cmd |= RxChkSum; +#else + dev->features |= NETIF_F_RXCSUM; +#endif + } else if (tp->mcfg == CFG_METHOD_7) { + set_offset70F(tp, 0x27); + + rtl8168_eri_write(tp, 0x1EC, 1, 0x07, ERIAR_ASF); + + //disable clock request. + pci_write_config_byte(pdev, 0x81, 0x00); + + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); + + if (dev->mtu > ETH_DATA_LEN) { + RTL_W8(tp, Config3, RTL_R8(tp, Config3) | Jumbo_En0); + RTL_W8(tp, Config4, RTL_R8(tp, Config4) | Jumbo_En1); + + set_offset79(tp, 0x20); + } else { + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Jumbo_En0); + RTL_W8(tp, Config4, RTL_R8(tp, Config4) & ~Jumbo_En1); + + } + } else if (tp->mcfg == CFG_METHOD_8) { + + set_offset70F(tp, 0x27); + + rtl8168_eri_write(tp, 0x1EC, 1, 0x07, ERIAR_ASF); + + //disable clock request. + pci_write_config_byte(pdev, 0x81, 0x00); + + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); + + RTL_W8(tp, 0xD1, 0x20); + + if (dev->mtu > ETH_DATA_LEN) { + RTL_W8(tp, Config3, RTL_R8(tp, Config3) | Jumbo_En0); + RTL_W8(tp, Config4, RTL_R8(tp, Config4) | Jumbo_En1); + + set_offset79(tp, 0x20); + } else { + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Jumbo_En0); + RTL_W8(tp, Config4, RTL_R8(tp, Config4) & ~Jumbo_En1); + } + } else if (tp->mcfg == CFG_METHOD_9) { + set_offset70F(tp, 0x27); + + /* disable clock request. */ + pci_write_config_byte(pdev, 0x81, 0x00); + + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~BIT_4); + RTL_W8(tp, DBG_reg, RTL_R8(tp, DBG_reg) | BIT_7 | BIT_1); + + if (dev->mtu > ETH_DATA_LEN) { + RTL_W8(tp, Config3, RTL_R8(tp, Config3) | Jumbo_En0); + RTL_W8(tp, Config4, RTL_R8(tp, Config4) | Jumbo_En1); + + set_offset79(tp, 0x20); + } else { + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Jumbo_En0); + RTL_W8(tp, Config4, RTL_R8(tp, Config4) & ~Jumbo_En1); + + } + + RTL_W8(tp, TDFNR, 0x8); + + } else if (tp->mcfg == CFG_METHOD_10) { + set_offset70F(tp, 0x27); + + RTL_W8(tp, DBG_reg, RTL_R8(tp, DBG_reg) | BIT_7 | BIT_1); + + if (dev->mtu > ETH_DATA_LEN) { + RTL_W8(tp, Config3, RTL_R8(tp, Config3) | Jumbo_En0); + RTL_W8(tp, Config4, RTL_R8(tp, Config4) | Jumbo_En1); + + set_offset79(tp, 0x20); + } else { + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Jumbo_En0); + RTL_W8(tp, Config4, RTL_R8(tp, Config4) & ~Jumbo_En1); + + } + + RTL_W8(tp, TDFNR, 0x8); + + RTL_W8(tp, Config1, RTL_R8(tp, Config1) | 0x10); + + /* disable clock request. */ + pci_write_config_byte(pdev, 0x81, 0x00); + } else if (tp->mcfg == CFG_METHOD_11 || tp->mcfg == CFG_METHOD_13) { + set_offset70F(tp, 0x27); + + if (dev->mtu > ETH_DATA_LEN) + RTL_W8(tp, Config3, RTL_R8(tp, Config3) | Jumbo_En0); + else + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Jumbo_En0); + + pci_write_config_byte(pdev, 0x81, 0x00); + + RTL_W8(tp, Config1, RTL_R8(tp, Config1) | 0x10); + + } else if (tp->mcfg == CFG_METHOD_12) { + set_offset70F(tp, 0x27); + + if (dev->mtu > ETH_DATA_LEN) + RTL_W8(tp, Config3, RTL_R8(tp, Config3) | Jumbo_En0); + else + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Jumbo_En0); + + pci_write_config_byte(pdev, 0x81, 0x01); + + RTL_W8(tp, Config1, RTL_R8(tp, Config1) | 0x10); + + } else if (tp->mcfg == CFG_METHOD_14 || tp->mcfg == CFG_METHOD_15) { + + set_offset70F(tp, 0x27); + + if (dev->mtu > ETH_DATA_LEN) { + RTL_W8(tp, MTPS, 0x24); + RTL_W8(tp, Config3, RTL_R8(tp, Config3) | Jumbo_En0); + RTL_W8(tp, Config4, RTL_R8(tp, Config4) | 0x01); + } else { + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Jumbo_En0); + RTL_W8(tp, Config4, RTL_R8(tp, Config4) & ~0x01); + } + + RTL_W8(tp, 0xF3, RTL_R8(tp, 0xF3) | BIT_5); + RTL_W8(tp, 0xF3, RTL_R8(tp, 0xF3) & ~BIT_5); + + RTL_W8(tp, 0xD0, RTL_R8(tp, 0xD0) | BIT_7 | BIT_6); + + RTL_W8(tp, 0xD1, RTL_R8(tp, 0xD1) | BIT_2 | BIT_3); + + RTL_W8(tp, 0xF1, RTL_R8(tp, 0xF1) | BIT_6 | BIT_5 | BIT_4 | BIT_2 | BIT_1); + + RTL_W8(tp, TDFNR, 0x8); + + /* + if (aspm) + RTL_W8(tp, 0xF1, RTL_R8(tp, 0xF1) | BIT_7); + */ + + RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~BIT_3); + + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); + + RTL_W8(tp, Config1, RTL_R8(tp, Config1) & ~0x10); + } else if (tp->mcfg == CFG_METHOD_16 || tp->mcfg == CFG_METHOD_17) { + set_offset70F(tp, 0x27); + + rtl8168_eri_write(tp, 0xC0, 2, 0x0000, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xB8, 4, 0x00000000, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xC8, 4, 0x00100002, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xE8, 4, 0x00100006, ERIAR_ExGMAC); + csi_tmp = rtl8168_eri_read(tp, 0x1D0, 4, ERIAR_ExGMAC); + csi_tmp |= BIT_1; + rtl8168_eri_write(tp, 0x1D0, 1, csi_tmp, ERIAR_ExGMAC); + + csi_tmp = rtl8168_eri_read(tp, 0xDC, 1, ERIAR_ExGMAC); + csi_tmp &= ~BIT_0; + rtl8168_eri_write(tp, 0xDC, 1, csi_tmp, ERIAR_ExGMAC); + csi_tmp |= BIT_0; + rtl8168_eri_write(tp, 0xDC, 1, csi_tmp, ERIAR_ExGMAC); + + RTL_W32(tp, TxConfig, RTL_R32(tp, TxConfig) | BIT_7); + RTL_W8(tp, 0xD3, RTL_R8(tp, 0xD3) & ~BIT_7); + RTL_W8(tp, 0x1B, RTL_R8(tp, 0x1B) & ~0x07); + + if (tp->mcfg == CFG_METHOD_16) { + RTL_W32(tp, 0xB0, 0xEE480010); + RTL_W8(tp, 0x1A, RTL_R8(tp, 0x1A) & ~(BIT_2|BIT_3)); + rtl8168_eri_write(tp, 0x1DC, 1, 0x64, ERIAR_ExGMAC); + } else { + csi_tmp = rtl8168_eri_read(tp, 0x1B0, 4, ERIAR_ExGMAC); + csi_tmp |= BIT_4; + rtl8168_eri_write(tp, 0x1B0, 1, csi_tmp, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xCC, 4, 0x00000050, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xD0, 4, 0x07ff0060, ERIAR_ExGMAC); + } + + RTL_W8(tp, TDFNR, 0x8); + + RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~PMSTS_En); + + RTL_W8(tp, 0xD0, RTL_R8(tp, 0xD0) | BIT_6); + RTL_W8(tp, 0xF2, RTL_R8(tp, 0xF2) | BIT_6); + + if (dev->mtu > ETH_DATA_LEN) + RTL_W8(tp, MTPS, 0x27); + + /* disable clock request. */ + pci_write_config_byte(pdev, 0x81, 0x00); + + } else if (tp->mcfg == CFG_METHOD_18 || tp->mcfg == CFG_METHOD_19) { + set_offset70F(tp, 0x27); + + rtl8168_eri_write(tp, 0xC8, 4, 0x00100002, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xE8, 4, 0x00100006, ERIAR_ExGMAC); + RTL_W32(tp, TxConfig, RTL_R32(tp, TxConfig) | BIT_7); + RTL_W8(tp, 0xD3, RTL_R8(tp, 0xD3) & ~BIT_7); + csi_tmp = rtl8168_eri_read(tp, 0xDC, 1, ERIAR_ExGMAC); + csi_tmp &= ~BIT_0; + rtl8168_eri_write(tp, 0xDC, 1, csi_tmp, ERIAR_ExGMAC); + csi_tmp |= BIT_0; + rtl8168_eri_write(tp, 0xDC, 1, csi_tmp, ERIAR_ExGMAC); + + /* + if (aspm) + RTL_W8(tp, 0xF1, RTL_R8(tp, 0xF1) | BIT_7); + */ + + if (dev->mtu > ETH_DATA_LEN) + RTL_W8(tp, MTPS, 0x27); + + RTL_W8(tp, TDFNR, 0x8); + + RTL_W8(tp, 0xD0, RTL_R8(tp, 0xD0) | BIT_6); + RTL_W8(tp, 0xF2, RTL_R8(tp, 0xF2) | BIT_6); + + rtl8168_eri_write(tp, 0xC0, 2, 0x0000, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xB8, 4, 0x00000000, ERIAR_ExGMAC); + RTL_W8(tp, 0x1B,RTL_R8(tp, 0x1B) & ~0x07); + + csi_tmp = rtl8168_eri_read(tp, 0x1B0, 1, ERIAR_ExGMAC); + csi_tmp |= BIT_4; + rtl8168_eri_write(tp, 0x1B0, 1, csi_tmp, ERIAR_ExGMAC); + csi_tmp = rtl8168_eri_read(tp, 0x1d0, 1, ERIAR_ExGMAC); + csi_tmp |= BIT_4 | BIT_1; + rtl8168_eri_write(tp, 0x1d0, 1, csi_tmp, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xCC, 4, 0x00000050, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xd0, 4, 0x00000060, ERIAR_ExGMAC); + } else if (tp->mcfg == CFG_METHOD_20) { + set_offset70F(tp, 0x27); + + rtl8168_eri_write(tp, 0xC8, 4, 0x00100002, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xE8, 4, 0x00100006, ERIAR_ExGMAC); + RTL_W32(tp, TxConfig, RTL_R32(tp, TxConfig) | BIT_7); + RTL_W8(tp, 0xD3, RTL_R8(tp, 0xD3) & ~BIT_7); + csi_tmp = rtl8168_eri_read(tp, 0xDC, 1, ERIAR_ExGMAC); + csi_tmp &= ~BIT_0; + rtl8168_eri_write(tp, 0xDC, 1, csi_tmp, ERIAR_ExGMAC); + csi_tmp |= BIT_0; + rtl8168_eri_write(tp, 0xDC, 1, csi_tmp, ERIAR_ExGMAC); + + /* + if (aspm) + RTL_W8(tp, 0xF1, RTL_R8(tp, 0xF1) | BIT_7); + */ + + if (dev->mtu > ETH_DATA_LEN) + RTL_W8(tp, MTPS, 0x27); + + RTL_W8(tp, TDFNR, 0x8); + + RTL_W8(tp, 0xD0, RTL_R8(tp, 0xD0) | BIT_6); + RTL_W8(tp, 0xF2, RTL_R8(tp, 0xF2) | BIT_6); + rtl8168_eri_write(tp, 0xC0, 2, 0x0000, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xB8, 4, 0x00000000, ERIAR_ExGMAC); + + csi_tmp = rtl8168_eri_read(tp, 0x1B0, 1, ERIAR_ExGMAC); + csi_tmp |= BIT_4; + rtl8168_eri_write(tp, 0x1B0, 1, csi_tmp, ERIAR_ExGMAC); + csi_tmp = rtl8168_eri_read(tp, 0x1d0, 1, ERIAR_ExGMAC); + csi_tmp |= BIT_4 | BIT_1; + rtl8168_eri_write(tp, 0x1d0, 1, csi_tmp, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xCC, 4, 0x00000050, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xd0, 4, 0x00000060, ERIAR_ExGMAC); + } else if (tp->mcfg == CFG_METHOD_21 || tp->mcfg == CFG_METHOD_22 || + tp->mcfg == CFG_METHOD_24 || tp->mcfg == CFG_METHOD_25 || + tp->mcfg == CFG_METHOD_26 || tp->mcfg == CFG_METHOD_29 || + tp->mcfg == CFG_METHOD_30) { + set_offset70F(tp, 0x27); + if (tp->mcfg == CFG_METHOD_21 || tp->mcfg == CFG_METHOD_22) + set_offset711(tp, 0x04); + + rtl8168_eri_write(tp, 0xC8, 4, 0x00080002, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xCC, 1, 0x38, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xD0, 1, 0x48, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xE8, 4, 0x00100006, ERIAR_ExGMAC); + + RTL_W32(tp, TxConfig, RTL_R32(tp, TxConfig) | BIT_7); + + csi_tmp = rtl8168_eri_read(tp, 0xDC, 1, ERIAR_ExGMAC); + csi_tmp &= ~BIT_0; + rtl8168_eri_write(tp, 0xDC, 1, csi_tmp, ERIAR_ExGMAC); + csi_tmp |= BIT_0; + rtl8168_eri_write(tp, 0xDC, 1, csi_tmp, ERIAR_ExGMAC); + + if (tp->mcfg == CFG_METHOD_26) { + mac_ocp_data = rtl8168_mac_ocp_read(tp, 0xD3C0); + mac_ocp_data &= ~(BIT_11 | BIT_10 | BIT_9 | BIT_8 | BIT_7 | BIT_6 | BIT_5 | BIT_4 | BIT_3 | BIT_2 | BIT_1 | BIT_0); + mac_ocp_data |= 0x0FFF; + rtl8168_mac_ocp_write(tp, 0xD3C0, mac_ocp_data); + mac_ocp_data = rtl8168_mac_ocp_read(tp, 0xD3C2); + mac_ocp_data &= ~(BIT_7 | BIT_6 | BIT_5 | BIT_4 | BIT_3 | BIT_2 | BIT_1 | BIT_0); + rtl8168_mac_ocp_write(tp, 0xD3C2, mac_ocp_data); + mac_ocp_data = rtl8168_mac_ocp_read(tp, 0xD3C4); + mac_ocp_data |= BIT_0; + rtl8168_mac_ocp_write(tp, 0xD3C4, mac_ocp_data); + } else if (tp->mcfg == CFG_METHOD_29 || tp->mcfg == CFG_METHOD_30) { + + if (tp->RequireAdjustUpsTxLinkPulseTiming) { + mac_ocp_data = rtl8168_mac_ocp_read(tp, 0xD412); + mac_ocp_data &= ~(0x0FFF); + mac_ocp_data |= tp->SwrCnt1msIni; + rtl8168_mac_ocp_write(tp, 0xD412, mac_ocp_data); + } + + mac_ocp_data = rtl8168_mac_ocp_read(tp, 0xE056); + mac_ocp_data &= ~(BIT_7 | BIT_6 | BIT_5 | BIT_4); + //mac_ocp_data |= (BIT_6 | BIT_5 | BIT_4); + rtl8168_mac_ocp_write(tp, 0xE056, mac_ocp_data); + + mac_ocp_data = rtl8168_mac_ocp_read(tp, 0xE052); + mac_ocp_data &= ~(BIT_15 | BIT_14 | BIT_13 | BIT_3); + mac_ocp_data |= BIT_15; + //mac_ocp_data |= BIT_3; + rtl8168_mac_ocp_write(tp, 0xE052, mac_ocp_data); + + mac_ocp_data = rtl8168_mac_ocp_read(tp, 0xD420); + mac_ocp_data &= ~(BIT_11 | BIT_10 | BIT_9 | BIT_8 | BIT_7 | BIT_6 | BIT_5 | BIT_4 | BIT_3 | BIT_2 | BIT_1 | BIT_0); + mac_ocp_data |= 0x45F; + rtl8168_mac_ocp_write(tp, 0xD420, mac_ocp_data); + + mac_ocp_data = rtl8168_mac_ocp_read(tp, 0xE0D6); + mac_ocp_data &= ~(BIT_8 | BIT_7 | BIT_6 | BIT_5 | BIT_4 | BIT_3 | BIT_2 | BIT_1 | BIT_0); + mac_ocp_data |= 0x17F; + rtl8168_mac_ocp_write(tp, 0xE0D6, mac_ocp_data); + } + + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); + + RTL_W8(tp, 0x1B, RTL_R8(tp, 0x1B) & ~0x07); + + RTL_W8(tp, TDFNR, 0x4); + + RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~PMSTS_En); + + /* + if (aspm) + RTL_W8(tp, 0xF1, RTL_R8(tp, 0xF1) | BIT_7); + */ + + if (dev->mtu > ETH_DATA_LEN) + RTL_W8(tp, MTPS, 0x27); + + RTL_W8(tp, 0xD0, RTL_R8(tp, 0xD0) | BIT_6); + RTL_W8(tp, 0xF2, RTL_R8(tp, 0xF2) | BIT_6); + + RTL_W8(tp, 0xD0, RTL_R8(tp, 0xD0) | BIT_7); + + rtl8168_eri_write(tp, 0xC0, 2, 0x0000, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xB8, 4, 0x00000000, ERIAR_ExGMAC); + + if (tp->mcfg == CFG_METHOD_29 || tp->mcfg == CFG_METHOD_30) { + rtl8168_mac_ocp_write(tp, 0xE054, 0x0000); + + rtl8168_eri_write(tp, 0x5F0, 2, 0x4000, ERIAR_ExGMAC); + } else { + rtl8168_eri_write(tp, 0x5F0, 2, 0x4F87, ERIAR_ExGMAC); + } + + if (tp->mcfg == CFG_METHOD_29 || tp->mcfg == CFG_METHOD_30) { + csi_tmp = rtl8168_eri_read(tp, 0xDC, 4, ERIAR_ExGMAC); + csi_tmp |= (BIT_2 | BIT_3 | BIT_4); + rtl8168_eri_write(tp, 0xDC, 4, csi_tmp, ERIAR_ExGMAC); + } + + if (tp->mcfg == CFG_METHOD_21 || tp->mcfg == CFG_METHOD_22 || + tp->mcfg == CFG_METHOD_24 || tp->mcfg == CFG_METHOD_25) { + rtl8168_mac_ocp_write(tp, 0xC140, 0xFFFF); + } else if (tp->mcfg == CFG_METHOD_29 || tp->mcfg == CFG_METHOD_30) { + rtl8168_mac_ocp_write(tp, 0xC140, 0xFFFF); + rtl8168_mac_ocp_write(tp, 0xC142, 0xFFFF); + } + + csi_tmp = rtl8168_eri_read(tp, 0x1B0, 4, ERIAR_ExGMAC); + csi_tmp &= ~BIT_12; + rtl8168_eri_write(tp, 0x1B0, 4, csi_tmp, ERIAR_ExGMAC); + + if (tp->mcfg == CFG_METHOD_29 || tp->mcfg == CFG_METHOD_30) { + csi_tmp = rtl8168_eri_read(tp, 0x2FC, 1, ERIAR_ExGMAC); + csi_tmp &= ~(BIT_2); + rtl8168_eri_write(tp, 0x2FC, 1, csi_tmp, ERIAR_ExGMAC); + } else { + csi_tmp = rtl8168_eri_read(tp, 0x2FC, 1, ERIAR_ExGMAC); + csi_tmp &= ~(BIT_0 | BIT_1 | BIT_2); + csi_tmp |= BIT_0; + rtl8168_eri_write(tp, 0x2FC, 1, csi_tmp, ERIAR_ExGMAC); + } + + csi_tmp = rtl8168_eri_read(tp, 0x1D0, 1, ERIAR_ExGMAC); + csi_tmp |= BIT_1; + rtl8168_eri_write(tp, 0x1D0, 1, csi_tmp, ERIAR_ExGMAC); + } else if (tp->mcfg == CFG_METHOD_23 || tp->mcfg == CFG_METHOD_27 || + tp->mcfg == CFG_METHOD_28) { + set_offset70F(tp, 0x27); + + rtl8168_eri_write(tp, 0xC8, 4, 0x00080002, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xCC, 1, 0x2F, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xD0, 1, 0x5F, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xE8, 4, 0x00100006, ERIAR_ExGMAC); + + RTL_W32(tp, TxConfig, RTL_R32(tp, TxConfig) | BIT_7); + + csi_tmp = rtl8168_eri_read(tp, 0xDC, 1, ERIAR_ExGMAC); + csi_tmp &= ~BIT_0; + rtl8168_eri_write(tp, 0xDC, 1, csi_tmp, ERIAR_ExGMAC); + csi_tmp |= BIT_0; + rtl8168_eri_write(tp, 0xDC, 1, csi_tmp, ERIAR_ExGMAC); + + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); + + RTL_W8(tp, 0xD0, RTL_R8(tp, 0xD0) | BIT_6); + RTL_W8(tp, 0xF2, RTL_R8(tp, 0xF2) | BIT_6); + + RTL_W8(tp, 0xD0, RTL_R8(tp, 0xD0) | BIT_7); + + rtl8168_eri_write(tp, 0xC0, 2, 0x0000, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xB8, 4, 0x00000000, ERIAR_ExGMAC); + RTL_W8(tp, 0x1B, RTL_R8(tp, 0x1B) & ~0x07); + + RTL_W8(tp, TDFNR, 0x4); + + /* + if (aspm) + RTL_W8(tp, 0xF1, RTL_R8(tp, 0xF1) | BIT_7); + */ + + csi_tmp = rtl8168_eri_read(tp, 0x1B0, 4, ERIAR_ExGMAC); + csi_tmp &= ~BIT_12; + rtl8168_eri_write(tp, 0x1B0, 4, csi_tmp, ERIAR_ExGMAC); + + csi_tmp = rtl8168_eri_read(tp, 0x2FC, 1, ERIAR_ExGMAC); + csi_tmp &= ~(BIT_0 | BIT_1 | BIT_2); + csi_tmp |= (BIT_0 | BIT_1); + rtl8168_eri_write(tp, 0x2FC, 1, csi_tmp, ERIAR_ExGMAC); + + csi_tmp = rtl8168_eri_read(tp, 0x1D0, 1, ERIAR_ExGMAC); + csi_tmp |= BIT_1; + rtl8168_eri_write(tp, 0x1D0, 1, csi_tmp, ERIAR_ExGMAC); + + if (dev->mtu > ETH_DATA_LEN) + RTL_W8(tp, MTPS, 0x27); + + if (tp->mcfg == CFG_METHOD_27 || tp->mcfg == CFG_METHOD_28) { + rtl8168_oob_mutex_lock(tp); + rtl8168_eri_write(tp, 0x5F0, 2, 0x4F87, ERIAR_ExGMAC); + rtl8168_oob_mutex_unlock(tp); + } + + rtl8168_mac_ocp_write(tp, 0xC140, 0xFFFF); + rtl8168_mac_ocp_write(tp, 0xC142, 0xFFFF); + + if (tp->mcfg == CFG_METHOD_28) { + mac_ocp_data = rtl8168_mac_ocp_read(tp, 0xD3E2); + mac_ocp_data &= 0xF000; + mac_ocp_data |= 0xAFD; + rtl8168_mac_ocp_write(tp, 0xD3E2, mac_ocp_data); + + mac_ocp_data = rtl8168_mac_ocp_read(tp, 0xD3E4); + mac_ocp_data &= 0xFF00; + rtl8168_mac_ocp_write(tp, 0xD3E4, mac_ocp_data); + + mac_ocp_data = rtl8168_mac_ocp_read(tp, 0xE860); + mac_ocp_data |= BIT_7; + rtl8168_mac_ocp_write(tp, 0xE860, mac_ocp_data); + } + } else if (tp->mcfg == CFG_METHOD_31 || tp->mcfg == CFG_METHOD_32 || + tp->mcfg == CFG_METHOD_33) { + set_offset70F(tp, 0x27); + + rtl8168_eri_write(tp, 0xC8, 4, 0x00080002, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xCC, 1, 0x2F, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xD0, 1, 0x5F, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xE8, 4, 0x00100006, ERIAR_ExGMAC); + + RTL_W32(tp, TxConfig, RTL_R32(tp, TxConfig) | BIT_7); + + csi_tmp = rtl8168_eri_read(tp, 0xDC, 1, ERIAR_ExGMAC); + csi_tmp &= ~BIT_0; + rtl8168_eri_write(tp, 0xDC, 1, csi_tmp, ERIAR_ExGMAC); + csi_tmp |= BIT_0; + rtl8168_eri_write(tp, 0xDC, 1, csi_tmp, ERIAR_ExGMAC); + + if (tp->RequireAdjustUpsTxLinkPulseTiming) { + mac_ocp_data = rtl8168_mac_ocp_read(tp, 0xD412); + mac_ocp_data &= ~(0x0FFF); + mac_ocp_data |= tp->SwrCnt1msIni; + rtl8168_mac_ocp_write(tp, 0xD412, mac_ocp_data); + } + + mac_ocp_data = rtl8168_mac_ocp_read(tp, 0xE056); + mac_ocp_data &= ~(BIT_7 | BIT_6 | BIT_5 | BIT_4); + rtl8168_mac_ocp_write(tp, 0xE056, mac_ocp_data); + if (FALSE == HW_SUPP_SERDES_PHY(tp)) + rtl8168_mac_ocp_write(tp, 0xEA80, 0x0003); + else + rtl8168_mac_ocp_write(tp, 0xEA80, 0x0000); + + rtl8168_oob_mutex_lock(tp); + mac_ocp_data = rtl8168_mac_ocp_read(tp, 0xE052); + mac_ocp_data &= ~(BIT_3 | BIT_0); + rtl8168_mac_ocp_write(tp, 0xE052, mac_ocp_data); + rtl8168_oob_mutex_unlock(tp); + + mac_ocp_data = rtl8168_mac_ocp_read(tp, 0xD420); + mac_ocp_data &= ~(BIT_11 | BIT_10 | BIT_9 | BIT_8 | BIT_7 | BIT_6 | BIT_5 | BIT_4 | BIT_3 | BIT_2 | BIT_1 | BIT_0); + mac_ocp_data |= 0x45F; + rtl8168_mac_ocp_write(tp, 0xD420, mac_ocp_data); + + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); + + RTL_W8(tp, 0x1B, RTL_R8(tp, 0x1B) & ~0x07); + + RTL_W8(tp, TDFNR, 0x4); + + RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~PMSTS_En); + + /* + if (aspm) + RTL_W8(tp, 0xF1, RTL_R8(tp, 0xF1) | BIT_7); + */ + + if (dev->mtu > ETH_DATA_LEN) + RTL_W8(tp, MTPS, 0x27); + + if (FALSE == HW_SUPP_SERDES_PHY(tp)) { + RTL_W8(tp, 0xD0, RTL_R8(tp, 0xD0) | BIT_6); + RTL_W8(tp, 0xF2, RTL_R8(tp, 0xF2) | BIT_6); + RTL_W8(tp, 0xD0, RTL_R8(tp, 0xD0) | BIT_7); + } else { + RTL_W8(tp, 0xD0, RTL_R8(tp, 0xD0) & ~BIT_6); + RTL_W8(tp, 0xF2, RTL_R8(tp, 0xF2) & ~BIT_6); + RTL_W8(tp, 0xD0, RTL_R8(tp, 0xD0) & ~BIT_7); + } + + rtl8168_eri_write(tp, 0xC0, 2, 0x0000, ERIAR_ExGMAC); + rtl8168_eri_write(tp, 0xB8, 4, 0x00000000, ERIAR_ExGMAC); + + rtl8168_oob_mutex_lock(tp); + rtl8168_eri_write(tp, 0x5F0, 2, 0x4000, ERIAR_ExGMAC); + rtl8168_oob_mutex_unlock(tp); + + if (tp->mcfg == CFG_METHOD_32 || tp->mcfg == CFG_METHOD_33) { + csi_tmp = rtl8168_eri_read(tp, 0xD4, 4, ERIAR_ExGMAC); + csi_tmp |= BIT_4; + rtl8168_eri_write(tp, 0xD4, 4, csi_tmp, ERIAR_ExGMAC); + } + + rtl8168_mac_ocp_write(tp, 0xC140, 0xFFFF); + rtl8168_mac_ocp_write(tp, 0xC142, 0xFFFF); + + csi_tmp = rtl8168_eri_read(tp, 0x1B0, 4, ERIAR_ExGMAC); + csi_tmp &= ~BIT_12; + rtl8168_eri_write(tp, 0x1B0, 4, csi_tmp, ERIAR_ExGMAC); + + csi_tmp = rtl8168_eri_read(tp, 0x2FC, 1, ERIAR_ExGMAC); + csi_tmp &= ~(BIT_0 | BIT_1); + csi_tmp |= BIT_0; + rtl8168_eri_write(tp, 0x2FC, 1, csi_tmp, ERIAR_ExGMAC); + + csi_tmp = rtl8168_eri_read(tp, 0x1D0, 1, ERIAR_ExGMAC); + csi_tmp &= ~BIT_1; + rtl8168_eri_write(tp, 0x1D0, 1, csi_tmp, ERIAR_ExGMAC); + } else if (tp->mcfg == CFG_METHOD_1) { + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); + + if (dev->mtu > ETH_DATA_LEN) { + pci_read_config_byte(pdev, 0x69, &device_control); + device_control &= ~0x70; + device_control |= 0x28; + pci_write_config_byte(pdev, 0x69, device_control); + } else { + pci_read_config_byte(pdev, 0x69, &device_control); + device_control &= ~0x70; + device_control |= 0x58; + pci_write_config_byte(pdev, 0x69, device_control); + } + } else if (tp->mcfg == CFG_METHOD_2) { + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); + + if (dev->mtu > ETH_DATA_LEN) { + pci_read_config_byte(pdev, 0x69, &device_control); + device_control &= ~0x70; + device_control |= 0x28; + pci_write_config_byte(pdev, 0x69, device_control); + + RTL_W8(tp, Config4, RTL_R8(tp, Config4) | (1 << 0)); + } else { + pci_read_config_byte(pdev, 0x69, &device_control); + device_control &= ~0x70; + device_control |= 0x58; + pci_write_config_byte(pdev, 0x69, device_control); + + RTL_W8(tp, Config4, RTL_R8(tp, Config4) & ~(1 << 0)); + } + } else if (tp->mcfg == CFG_METHOD_3) { + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); + + if (dev->mtu > ETH_DATA_LEN) { + pci_read_config_byte(pdev, 0x69, &device_control); + device_control &= ~0x70; + device_control |= 0x28; + pci_write_config_byte(pdev, 0x69, device_control); + + RTL_W8(tp, Config4, RTL_R8(tp, Config4) | (1 << 0)); + } else { + pci_read_config_byte(pdev, 0x69, &device_control); + device_control &= ~0x70; + device_control |= 0x58; + pci_write_config_byte(pdev, 0x69, device_control); + + RTL_W8(tp, Config4, RTL_R8(tp, Config4) & ~(1 << 0)); + } + } + + if ((tp->mcfg == CFG_METHOD_1) || (tp->mcfg == CFG_METHOD_2) || (tp->mcfg == CFG_METHOD_3)) { + /* csum offload command for RTL8168B/8111B */ + tp->tx_tcp_csum_cmd = TxTCPCS; + tp->tx_udp_csum_cmd = TxUDPCS; + tp->tx_ip_csum_cmd = TxIPCS; + tp->tx_ipv6_csum_cmd = 0; + } else { + /* csum offload command for RTL8168C/8111C and RTL8168CP/8111CP */ + tp->tx_tcp_csum_cmd = TxTCPCS_C; + tp->tx_udp_csum_cmd = TxUDPCS_C; + tp->tx_ip_csum_cmd = TxIPCS_C; + tp->tx_ipv6_csum_cmd = TxIPV6F_C; + } + + + //other hw parameters + if (tp->mcfg == CFG_METHOD_21 || tp->mcfg == CFG_METHOD_22 || + tp->mcfg == CFG_METHOD_23 || tp->mcfg == CFG_METHOD_24 || + tp->mcfg == CFG_METHOD_25 || tp->mcfg == CFG_METHOD_26 || + tp->mcfg == CFG_METHOD_27 || tp->mcfg == CFG_METHOD_28) + rtl8168_eri_write(tp, 0x2F8, 2, 0x1D8F, ERIAR_ExGMAC); + + if (tp->bios_setting & BIT_28) { + if (tp->mcfg == CFG_METHOD_18 || tp->mcfg == CFG_METHOD_19 || + tp->mcfg == CFG_METHOD_20) { + u32 gphy_val; + + rtl8168_mdio_write(tp, 0x1F, 0x0007); + rtl8168_mdio_write(tp, 0x1E, 0x002C); + gphy_val = rtl8168_mdio_read(tp, 0x16); + gphy_val |= BIT_10; + rtl8168_mdio_write(tp, 0x16, gphy_val); + rtl8168_mdio_write(tp, 0x1F, 0x0005); + rtl8168_mdio_write(tp, 0x05, 0x8B80); + gphy_val = rtl8168_mdio_read(tp, 0x06); + gphy_val |= BIT_7; + rtl8168_mdio_write(tp, 0x06, gphy_val); + rtl8168_mdio_write(tp, 0x1F, 0x0000); + } + } + + rtl8168_hw_clear_timer_int(dev); + + rtl8168_enable_exit_l1_mask(tp); + + switch (tp->mcfg) { + case CFG_METHOD_25: + rtl8168_mac_ocp_write(tp, 0xD3C0, 0x0B00); + rtl8168_mac_ocp_write(tp, 0xD3C2, 0x0000); + break; + case CFG_METHOD_29: + case CFG_METHOD_30: + rtl8168_mac_ocp_write(tp, 0xE098, 0x0AA2); + break; + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + rtl8168_mac_ocp_write(tp, 0xE098, 0xC302); + break; + } + + switch (tp->mcfg) { + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + if (aspm) { + rtl8168_init_pci_offset_99(tp); + } + break; + } + switch (tp->mcfg) { + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + if (aspm) { + rtl8168_init_pci_offset_180(tp); + } + break; + } + + tp->cp_cmd &= ~(EnableBist | Macdbgo_oe | Force_halfdup | + Force_rxflow_en | Force_txflow_en | Cxpl_dbg_sel | + ASF | Macdbgo_sel); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) + RTL_W16(tp, CPlusCmd, tp->cp_cmd); +#else + rtl8168_hw_set_features(dev, dev->features); +#endif + + switch (tp->mcfg) { + case CFG_METHOD_16: + case CFG_METHOD_17: + case CFG_METHOD_18: + case CFG_METHOD_19: + case CFG_METHOD_20: + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: { + int timeout; + for (timeout = 0; timeout < 10; timeout++) { + if ((rtl8168_eri_read(tp, 0x1AE, 2, ERIAR_ExGMAC) & BIT_13)==0) + break; + mdelay(1); + } + } + break; + } + + RTL_W16(tp, RxMaxSize, tp->rx_buf_sz); + + rtl8168_disable_rxdvgate(dev); + + if (tp->mcfg == CFG_METHOD_11 || tp->mcfg == CFG_METHOD_12) + rtl8168_mac_loopback_test(tp); + + if (!tp->pci_cfg_is_read) { + pci_read_config_byte(pdev, PCI_COMMAND, &tp->pci_cfg_space.cmd); + 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_word(pdev, PCI_BASE_ADDRESS_3, &tp->pci_cfg_space.resv_0x1c_l); + pci_read_config_word(pdev, PCI_BASE_ADDRESS_3 + 2, &tp->pci_cfg_space.resv_0x1c_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); + pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &tp->pci_cfg_space.resv_0x2c_l); + pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID + 2, &tp->pci_cfg_space.resv_0x2c_h); + if (tp->HwPcieSNOffset > 0) { + tp->pci_cfg_space.pci_sn_l = rtl8168_csi_read(tp, tp->HwPcieSNOffset); + tp->pci_cfg_space.pci_sn_h = rtl8168_csi_read(tp, tp->HwPcieSNOffset + 4); + } + + tp->pci_cfg_is_read = 1; + } + + rtl8168_dsm(dev, DSM_MAC_INIT); + + /* Set Rx packet filter */ + rtl8168_hw_set_rx_packet_filter(dev); + +#ifdef ENABLE_DASH_SUPPORT + if (tp->DASH && !tp->dash_printer_enabled) + NICChkTypeEnableDashInterrupt(tp); +#endif + + if (tp->HwSuppAspmClkIntrLock) + rtl8168_hw_aspm_clkreq_enable(tp, true); + + rtl8168_disable_cfg9346_write(tp); + + udelay(10); +} + +static void +rtl8168_hw_start(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + RTL_W8(tp, ChipCmd, CmdTxEnb | CmdRxEnb); + + rtl8168_enable_hw_interrupt(tp); +} + +static int +rtl8168_change_mtu(struct net_device *dev, + int new_mtu) +{ + struct rtl8168_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; + + rtl8168_down(dev); + + spin_lock_irqsave(&tp->lock, flags); + + rtl8168_set_rxbufsize(tp, dev); + + ret = rtl8168_init_ring(dev); + + if (ret < 0) { + spin_unlock_irqrestore(&tp->lock, flags); + goto err_out; + } + +#ifdef CONFIG_R8168_NAPI +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) + RTL_NAPI_ENABLE(dev, &tp->napi); +#endif +#endif//CONFIG_R8168_NAPI + + netif_stop_queue(dev); + netif_carrier_off(dev); + rtl8168_hw_config(dev); + spin_unlock_irqrestore(&tp->lock, flags); + + rtl8168_set_speed(dev, tp->autoneg, tp->speed, tp->duplex, tp->advertising); + + mod_timer(&tp->esd_timer, jiffies + RTL8168_ESD_TIMEOUT); + mod_timer(&tp->link_timer, jiffies + RTL8168_LINK_TIMEOUT); +out: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) + netdev_update_features(dev); +#endif + +err_out: + return ret; +} + +static inline void +rtl8168_make_unusable_by_asic(struct RxDesc *desc) +{ + desc->addr = 0x0badbadbadbadbadull; + desc->opts1 &= ~cpu_to_le32(DescOwn | RsvdMask); +} + +static void +rtl8168_free_rx_skb(struct rtl8168_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; + rtl8168_make_unusable_by_asic(desc); +} + +static inline void +rtl8168_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 +rtl8168_map_to_asic(struct RxDesc *desc, + dma_addr_t mapping, + u32 rx_buf_sz) +{ + desc->addr = cpu_to_le64(mapping); + wmb(); + rtl8168_mark_to_asic(desc, rx_buf_sz); +} + +static int +rtl8168_alloc_rx_skb(struct rtl8168_private *tp, + struct sk_buff **sk_buff, + struct RxDesc *desc, + int rx_buf_sz, + 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 + RTK_RX_ALIGN); + else + skb = dev_alloc_skb(rx_buf_sz + RTK_RX_ALIGN); + + if (unlikely(!skb)) + goto err_out; + + skb_reserve(skb, RTK_RX_ALIGN); + + mapping = dma_map_single(tp_to_dev(tp), skb->data, rx_buf_sz, + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(tp_to_dev(tp), mapping))) { + if (unlikely(net_ratelimit())) + netif_err(tp, drv, tp->dev, "Failed to map RX DMA!\n"); + goto err_out; + } + + *sk_buff = skb; + rtl8168_map_to_asic(desc, mapping, rx_buf_sz); +out: + return ret; + +err_out: + if (skb) + dev_kfree_skb(skb); + ret = -ENOMEM; + rtl8168_make_unusable_by_asic(desc); + goto out; +} + +static void +rtl8168_rx_clear(struct rtl8168_private *tp) +{ + int i; + + for (i = 0; i < NUM_RX_DESC; i++) { + if (tp->Rx_skbuff[i]) + rtl8168_free_rx_skb(tp, tp->Rx_skbuff + i, + tp->RxDescArray + i); + } +} + +static u32 +rtl8168_rx_fill(struct rtl8168_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 = rtl8168_alloc_rx_skb(tp, tp->Rx_skbuff + i, + tp->RxDescArray + i, + tp->rx_buf_sz, + in_intr); + if (ret < 0) + break; + } + return cur - start; +} + +static inline void +rtl8168_mark_as_last_descriptor(struct RxDesc *desc) +{ + desc->opts1 |= cpu_to_le32(RingEnd); +} + +static void +rtl8168_desc_addr_fill(struct rtl8168_private *tp) +{ + if (!tp->TxPhyAddr || !tp->RxPhyAddr) + return; + + RTL_W32(tp, TxDescStartAddrLow, ((u64) tp->TxPhyAddr & DMA_BIT_MASK(32))); + RTL_W32(tp, TxDescStartAddrHigh, ((u64) tp->TxPhyAddr >> 32)); + RTL_W32(tp, RxDescAddrLow, ((u64) tp->RxPhyAddr & DMA_BIT_MASK(32))); + RTL_W32(tp, RxDescAddrHigh, ((u64) tp->RxPhyAddr >> 32)); +} + +static void +rtl8168_tx_desc_init(struct rtl8168_private *tp) +{ + int i = 0; + + memset(tp->TxDescArray, 0x0, NUM_TX_DESC * sizeof(struct TxDesc)); + + for (i = 0; i < NUM_TX_DESC; i++) { + if (i == (NUM_TX_DESC - 1)) + tp->TxDescArray[i].opts1 = cpu_to_le32(RingEnd); + } +} + +static void +rtl8168_rx_desc_offset0_init(struct rtl8168_private *tp, int own) +{ + int i = 0; + int ownbit = 0; + + if (tp->RxDescArray == NULL) return; + + if (own) + ownbit = DescOwn; + + for (i = 0; i < NUM_RX_DESC; i++) { + if (i == (NUM_RX_DESC - 1)) + tp->RxDescArray[i].opts1 = cpu_to_le32((ownbit | RingEnd) | (unsigned long)tp->rx_buf_sz); + else + tp->RxDescArray[i].opts1 = cpu_to_le32(ownbit | (unsigned long)tp->rx_buf_sz); + } +} + +static void +rtl8168_rx_desc_init(struct rtl8168_private *tp) +{ + memset(tp->RxDescArray, 0x0, NUM_RX_DESC * sizeof(struct RxDesc)); +} + +static int +rtl8168_init_ring(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + rtl8168_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 *)); + + rtl8168_tx_desc_init(tp); + rtl8168_rx_desc_init(tp); + + if (rtl8168_rx_fill(tp, dev, 0, NUM_RX_DESC, 0) != NUM_RX_DESC) + goto err_out; + + rtl8168_mark_as_last_descriptor(tp->RxDescArray + NUM_RX_DESC - 1); + + return 0; + +err_out: + rtl8168_rx_clear(tp); + return -ENOMEM; +} + +static void +rtl8168_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 rtl8168_tx_clear_range(struct rtl8168_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; + + rtl8168_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 +rtl8168_tx_clear(struct rtl8168_private *tp) +{ + rtl8168_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 rtl8168_schedule_work(struct net_device *dev, void (*task)(void *)) +{ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) + struct rtl8168_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 rtl8168_cancel_schedule_work(a) + +#else +static void rtl8168_schedule_work(struct net_device *dev, work_func_t task) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + INIT_DELAYED_WORK(&tp->task, task); + schedule_delayed_work(&tp->task, 4); +} + +static void rtl8168_cancel_schedule_work(struct net_device *dev) +{ + struct rtl8168_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 +rtl8168_wait_for_quiescence(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + synchronize_irq(dev->irq); + + /* Wait for any pending NAPI task to complete */ +#ifdef CONFIG_R8168_NAPI +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) + RTL_NAPI_DISABLE(dev, &tp->napi); +#endif +#endif//CONFIG_R8168_NAPI + + rtl8168_irq_mask_and_ack(tp); + +#ifdef CONFIG_R8168_NAPI +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) + RTL_NAPI_ENABLE(dev, &tp->napi); +#endif +#endif//CONFIG_R8168_NAPI +} + +#if 0 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +static void rtl8168_reinit_task(void *_data) +#else +static void rtl8168_reinit_task(struct work_struct *work) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) + struct net_device *dev = _data; +#else + struct rtl8168_private *tp = + container_of(work, struct rtl8168_private, task.work); + struct net_device *dev = tp->dev; +#endif + int ret; + + if (netif_running(dev)) { + rtl8168_wait_for_quiescence(dev); + rtl8168_close(dev); + } + + ret = rtl8168_open(dev); + if (unlikely(ret < 0)) { + if (unlikely(net_ratelimit())) { + struct rtl8168_private *tp = netdev_priv(dev); + + if (netif_msg_drv(tp)) { + printk(PFX KERN_ERR + "%s: reinit failure (status = %d)." + " Rescheduling.\n", dev->name, ret); + } + } + rtl8168_schedule_work(dev, rtl8168_reinit_task); + } +} +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) +static void rtl8168_reset_task(void *_data) +{ + struct net_device *dev = _data; + struct rtl8168_private *tp = netdev_priv(dev); +#else +static void rtl8168_reset_task(struct work_struct *work) +{ + struct rtl8168_private *tp = + container_of(work, struct rtl8168_private, task.work); + struct net_device *dev = tp->dev; +#endif + u32 budget = ~(u32)0; + unsigned long flags; + + if (!netif_running(dev)) + return; + + rtl8168_wait_for_quiescence(dev); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + rtl8168_rx_interrupt(dev, tp, &budget); +#else + rtl8168_rx_interrupt(dev, tp, budget); +#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + + spin_lock_irqsave(&tp->lock, flags); + + rtl8168_tx_clear(tp); + + if (tp->dirty_rx == tp->cur_rx) { + rtl8168_rx_clear(tp); + rtl8168_init_ring(dev); + rtl8168_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 rtl8168_private *tp = netdev_priv(dev); + + if (netif_msg_intr(tp)) { + printk(PFX KERN_EMERG + "%s: Rx buffers shortage\n", dev->name); + } + } + rtl8168_schedule_work(dev, rtl8168_reset_task); + } +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,6,0) +static void +rtl8168_tx_timeout(struct net_device *dev, unsigned int txqueue) +#else +static void +rtl8168_tx_timeout(struct net_device *dev) +#endif +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&tp->lock, flags); + netif_stop_queue(dev); + netif_carrier_off(dev); + rtl8168_hw_reset(dev); + spin_unlock_irqrestore(&tp->lock, flags); + + /* Let's wait a bit while any (async) irq lands on */ + rtl8168_schedule_work(dev, rtl8168_reset_task); +} + +static u32 +rtl8168_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 +rtl8168_xmit_frags(struct rtl8168_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_to_dev(tp), addr, len, DMA_TO_DEVICE); + + if (unlikely(dma_mapping_error(tp_to_dev(tp), 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 = rtl8168_get_txd_opts1(opts[0], 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: + rtl8168_tx_clear_range(tp, tp->cur_tx + 1, cur_frag); + return -EIO; +} + +static inline +__be16 get_protocol(struct sk_buff *skb) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) + return vlan_get_protocol(skb); +#else + __be16 protocol; + + if (skb->protocol == htons(ETH_P_8021Q)) + protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto; + else + protocol = skb->protocol; + + return protocol; +#endif +} + +static bool rtl8168_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 +rtl8168_tx_csum(struct sk_buff *skb, + struct net_device *dev, + u32 *opts) +{ + struct rtl8168_private *tp = netdev_priv(dev); + u32 csum_cmd = 0; + u8 sw_calc_csum = FALSE; + + if (skb->ip_summed == CHECKSUM_PARTIAL) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) + const struct iphdr *ip = skb->nh.iph; + + if (dev->features & NETIF_F_IP_CSUM) { + if (ip->protocol == IPPROTO_TCP) + csum_cmd = tp->tx_ip_csum_cmd | tp->tx_tcp_csum_cmd; + else if (ip->protocol == IPPROTO_UDP) + csum_cmd = tp->tx_ip_csum_cmd | tp->tx_udp_csum_cmd; + else if (ip->protocol == IPPROTO_IP) + csum_cmd = tp->tx_ip_csum_cmd; + } +#else + u8 ip_protocol = IPPROTO_RAW; + + switch (get_protocol(skb)) { + case __constant_htons(ETH_P_IP): + if (dev->features & NETIF_F_IP_CSUM) { + ip_protocol = ip_hdr(skb)->protocol; + csum_cmd = tp->tx_ip_csum_cmd; + } + break; + case __constant_htons(ETH_P_IPV6): + if (dev->features & NETIF_F_IPV6_CSUM) { + u32 transport_offset = (u32)skb_transport_offset(skb); + if (transport_offset > 0 && transport_offset <= TCPHO_MAX) { + ip_protocol = ipv6_hdr(skb)->nexthdr; + csum_cmd = tp->tx_ipv6_csum_cmd; + csum_cmd |= transport_offset << TCPHO_SHIFT; + } + } + break; + default: + if (unlikely(net_ratelimit())) + dprintk("checksum_partial proto=%x!\n", skb->protocol); + break; + } + + if (ip_protocol == IPPROTO_TCP) + csum_cmd |= tp->tx_tcp_csum_cmd; + else if (ip_protocol == IPPROTO_UDP) + csum_cmd |= tp->tx_udp_csum_cmd; +#endif + 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 + } + } + + if (csum_cmd != 0) { + if (tp->ShortPacketSwChecksum && skb->len < ETH_ZLEN) { + sw_calc_csum = TRUE; + if (!rtl8168_skb_pad(skb)) + return false; + } else { + if ((tp->mcfg == CFG_METHOD_1) || (tp->mcfg == CFG_METHOD_2) || (tp->mcfg == CFG_METHOD_3)) + opts[0] |= csum_cmd; + else + opts[1] |= csum_cmd; + } + } + + if (tp->UseSwPaddingShortPkt && skb->len < ETH_ZLEN) + if (!rtl8168_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; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0) +/* r8169_csum_workaround() + * The hw limits the value the transport offset. When the offset is out of the + * range, calculate the checksum by sw. + */ +static void r8168_csum_workaround(struct rtl8168_private *tp, + struct sk_buff *skb) +{ + if (skb_shinfo(skb)->gso_size) { + netdev_features_t features = tp->dev->features; + struct sk_buff *segs, *nskb; + + features &= ~(NETIF_F_SG | NETIF_F_IPV6_CSUM | NETIF_F_TSO6); + segs = skb_gso_segment(skb, features); + if (IS_ERR(segs) || !segs) + goto drop; + + do { + nskb = segs; + segs = segs->next; + nskb->next = NULL; + rtl8168_start_xmit(nskb, tp->dev); + } while (segs); + + dev_consume_skb_any(skb); + } else if (skb->ip_summed == CHECKSUM_PARTIAL) { + if (skb_checksum_help(skb) < 0) + goto drop; + + rtl8168_start_xmit(skb, tp->dev); + } else { + struct net_device_stats *stats; + +drop: + stats = &tp->dev->stats; + stats->tx_dropped++; + dev_kfree_skb_any(skb); + } +} + +/* msdn_giant_send_check() + * According to the document of microsoft, the TCP Pseudo Header excludes the + * packet length for IPv6 TCP large packets. + */ +static int msdn_giant_send_check(struct sk_buff *skb) +{ + const struct ipv6hdr *ipv6h; + struct tcphdr *th; + int ret; + + ret = skb_cow_head(skb, 0); + if (ret) + return ret; + + ipv6h = ipv6_hdr(skb); + th = tcp_hdr(skb); + + th->check = 0; + th->check = ~tcp_v6_check(0, &ipv6h->saddr, &ipv6h->daddr, 0); + + return ret; +} +#endif + +static bool rtl8168_tx_slots_avail(struct rtl8168_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 +rtl8168_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned int entry; + struct TxDesc *txd; + dma_addr_t mapping; + u32 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(!rtl8168_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] = rtl8168_tx_vlan_tag(tp, skb); + + large_send = 0; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) + if (dev->features & (NETIF_F_TSO | NETIF_F_TSO6)) { +#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) + + /* TCP Segmentation Offload (or TCP Large Send) */ + if (mss) { + if ((tp->mcfg == CFG_METHOD_1) || + (tp->mcfg == CFG_METHOD_2) || + (tp->mcfg == CFG_METHOD_3)) { + opts[0] |= LargeSend | (min(mss, MSS_MAX) << 16); + large_send = 1; + } else { + u32 transport_offset = (u32)skb_transport_offset(skb); + switch (get_protocol(skb)) { + case __constant_htons(ETH_P_IP): + if (transport_offset <= GTTCPHO_MAX) { + opts[0] |= GiantSendv4; + opts[0] |= transport_offset << GTTCPHO_SHIFT; + opts[1] |= min(mss, MSS_MAX) << 18; + large_send = 1; + } + break; + case __constant_htons(ETH_P_IPV6): +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0) + if (msdn_giant_send_check(skb)) { + spin_unlock_irqrestore(&tp->lock, flags); + r8168_csum_workaround(tp, skb); + goto out; + } +#endif + if (transport_offset <= GTTCPHO_MAX) { + opts[0] |= GiantSendv6; + opts[0] |= transport_offset << GTTCPHO_SHIFT; + opts[1] |= min(mss, MSS_MAX) << 18; + large_send = 1; + } + break; + default: + if (unlikely(net_ratelimit())) + dprintk("tso proto=%x!\n", skb->protocol); + break; + } + } + + if (large_send == 0) + goto err_dma_0; + } + } +#endif //LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) + + if (large_send == 0) { + if (unlikely(!rtl8168_tx_csum(skb, dev, opts))) + goto err_dma_0; + } + + frags = rtl8168_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; + } + + opts[0] = rtl8168_get_txd_opts1(opts[0], len, entry); + mapping = dma_map_single(tp_to_dev(tp), skb->data, len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(tp_to_dev(tp), mapping))) { + if (unlikely(net_ratelimit())) + netif_err(tp, drv, dev, "Failed to map TX DMA!\n"); + goto err_dma_1; + } + tp->tx_skb[entry].len = len; + txd->addr = cpu_to_le64(mapping); + txd->opts2 = cpu_to_le32(opts[1]); + wmb(); + txd->opts1 = cpu_to_le32(opts[0]); + +#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; + + wmb(); + + RTL_W8(tp, TxPoll, NPQ); /* set polling bit */ + + if (!rtl8168_tx_slots_avail(tp, MAX_SKB_FRAGS)) { + netif_stop_queue(dev); + smp_rmb(); + if (rtl8168_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; + rtl8168_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 +rtl8168_tx_interrupt(struct net_device *dev, + struct rtl8168_private *tp) +{ + unsigned int dirty_tx, tx_left; + + assert(dev != NULL); + assert(tp != NULL); + + dirty_tx = tp->dirty_tx; + smp_rmb(); + tx_left = tp->cur_tx - dirty_tx; + tp->dynamic_aspm_packet_count += tx_left; + + 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++; + + rtl8168_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--; + } + + tp->dynamic_aspm_packet_count -= tx_left; + + if (tp->dirty_tx != dirty_tx) { + tp->dirty_tx = dirty_tx; + smp_wmb(); + if (netif_queue_stopped(dev) && + (rtl8168_tx_slots_avail(tp, MAX_SKB_FRAGS))) { + netif_wake_queue(dev); + } + smp_rmb(); + if (tp->cur_tx != dirty_tx) + RTL_W8(tp, TxPoll, NPQ); + } +} + +static inline int +rtl8168_fragmented_frame(u32 status) +{ + return (status & (FirstFrag | LastFrag)) != (FirstFrag | LastFrag); +} + +static inline void +rtl8168_rx_csum(struct rtl8168_private *tp, + struct sk_buff *skb, + struct RxDesc *desc) +{ + u32 opts1 = le32_to_cpu(desc->opts1); + u32 opts2 = le32_to_cpu(desc->opts2); + + if ((tp->mcfg == CFG_METHOD_1) || + (tp->mcfg == CFG_METHOD_2) || + (tp->mcfg == CFG_METHOD_3)) { + u32 status = opts1 & RxProtoMask; + + /* rx csum offload for RTL8168B/8111B */ + if (((status == RxProtoTCP) && !(opts1 & (RxTCPF | RxIPF))) || + ((status == RxProtoUDP) && !(opts1 & (RxUDPF | RxIPF)))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb->ip_summed = CHECKSUM_NONE; + } else { + /* rx csum offload for RTL8168C/8111C and RTL8168CP/8111CP */ + if (((opts2 & RxV4F) && !(opts1 & RxIPF)) || (opts2 & RxV6F)) { + if (((opts1 & RxTCPT) && !(opts1 & RxTCPF)) || + ((opts1 & RxUDPT) && !(opts1 & RxUDPF))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb->ip_summed = CHECKSUM_NONE; + } else + skb->ip_summed = CHECKSUM_NONE; + } +} + +static inline int +rtl8168_try_rx_copy(struct rtl8168_private *tp, + struct sk_buff **sk_buff, + int pkt_size, + struct RxDesc *desc, + int rx_buf_sz) +{ + int ret = -1; + + if (pkt_size < rx_copybreak) { + struct sk_buff *skb; + + skb = RTL_ALLOC_SKB_INTR(tp, pkt_size + RTK_RX_ALIGN); + if (skb) { + u8 *data; + + data = sk_buff[0]->data; + skb_reserve(skb, RTK_RX_ALIGN); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,37) + prefetch(data - RTK_RX_ALIGN); +#endif + eth_copy_and_sum(skb, data, pkt_size, 0); + *sk_buff = skb; + rtl8168_mark_to_asic(desc, rx_buf_sz); + ret = 0; + } + } + return ret; +} + +static inline void +rtl8168_rx_skb(struct rtl8168_private *tp, + struct sk_buff *skb) +{ +#ifdef CONFIG_R8168_NAPI +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29) + netif_receive_skb(skb); +#else + napi_gro_receive(&tp->napi, skb); +#endif +#else + netif_rx(skb); +#endif +} + +static int +rtl8168_rx_interrupt(struct net_device *dev, + struct rtl8168_private *tp, + 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); + + 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 = rtl8168_rx_quota(rx_left, (u32)rx_quota); + + for (; rx_left > 0; rx_left--) { + rmb(); + status = le32_to_cpu(desc->opts1); + if (status & DescOwn) + break; + 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; + + rtl8168_mark_to_asic(desc, tp->rx_buf_sz); + } else { + struct sk_buff *skb; + int pkt_size; + +process_pkt: + if (likely(!(dev->features & NETIF_F_RXFCS))) + pkt_size = (status & 0x00003fff) - 4; + else + pkt_size = status & 0x00003fff; + + /* + * The driver does not support incoming fragmented + * frames. They are seen as a symptom of over-mtu + * sized frames. + */ + if (unlikely(rtl8168_fragmented_frame(status))) { + RTLDEV->stats.rx_dropped++; + RTLDEV->stats.rx_length_errors++; + rtl8168_mark_to_asic(desc, tp->rx_buf_sz); + continue; + } + + skb = tp->Rx_skbuff[entry]; + + dma_sync_single_for_cpu(tp_to_dev(tp), + le64_to_cpu(desc->addr), tp->rx_buf_sz, + DMA_FROM_DEVICE); + + if (rtl8168_try_rx_copy(tp, &skb, pkt_size, + desc, tp->rx_buf_sz)) { + tp->Rx_skbuff[entry] = NULL; + dma_unmap_single(tp_to_dev(tp), le64_to_cpu(desc->addr), + tp->rx_buf_sz, DMA_FROM_DEVICE); + } else { + dma_sync_single_for_device(tp_to_dev(tp), le64_to_cpu(desc->addr), + tp->rx_buf_sz, DMA_FROM_DEVICE); + } + + if (tp->cp_cmd & RxChkSum) + rtl8168_rx_csum(tp, 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 (rtl8168_rx_vlan_skb(tp, desc, skb) < 0) + rtl8168_rx_skb(tp, 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 = rtl8168_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; + + tp->dynamic_aspm_packet_count += 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 rtl8168_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +#else +static irqreturn_t rtl8168_interrupt(int irq, void *dev_instance) +#endif +{ + struct net_device *dev = (struct net_device *) dev_instance; + struct rtl8168_private *tp = netdev_priv(dev); + int status; + int handled = 0; + + do { + status = RTL_R16(tp, IntrStatus); + + if (!(tp->features & RTL_FEATURE_MSI)) { + /* hotplug/major error/no more work/shared irq */ + if ((status == 0xFFFF) || !status) + break; + + if (!(status & (tp->intr_mask | tp->timer_intr_mask))) + break; + } + + handled = 1; + + rtl8168_disable_hw_interrupt(tp); + + switch (tp->mcfg) { + case CFG_METHOD_9: + case CFG_METHOD_10: + case CFG_METHOD_11: + case CFG_METHOD_12: + case CFG_METHOD_13: + case CFG_METHOD_14: + case CFG_METHOD_15: + case CFG_METHOD_16: + case CFG_METHOD_17: + case CFG_METHOD_18: + case CFG_METHOD_19: + case CFG_METHOD_20: + case CFG_METHOD_21: + case CFG_METHOD_22: + case CFG_METHOD_23: + case CFG_METHOD_24: + case CFG_METHOD_25: + case CFG_METHOD_26: + case CFG_METHOD_27: + case CFG_METHOD_28: + case CFG_METHOD_29: + case CFG_METHOD_30: + case CFG_METHOD_31: + case CFG_METHOD_32: + case CFG_METHOD_33: + /* RX_OVERFLOW RE-START mechanism now HW handles it automatically*/ + RTL_W16(tp, IntrStatus, status&~RxFIFOOver); + break; + default: + RTL_W16(tp, IntrStatus, status); + break; + } + + //Work around for rx fifo overflow + if (unlikely(status & RxFIFOOver)) { + if (tp->mcfg == CFG_METHOD_1) { + netif_stop_queue(dev); + udelay(300); + rtl8168_hw_reset(dev); + rtl8168_tx_clear(tp); + rtl8168_rx_clear(tp); + rtl8168_init_ring(dev); + rtl8168_hw_config(dev); + rtl8168_hw_start(dev); + netif_wake_queue(dev); + } + } + +#ifdef ENABLE_DASH_SUPPORT + if ( tp->DASH ) { + if (HW_DASH_SUPPORT_TYPE_2(tp) || HW_DASH_SUPPORT_TYPE_3(tp)) { + u8 DashIntType2Status; + + if (status & ISRIMR_DASH_INTR_CMAC_RESET) + tp->CmacResetIntr = TRUE; + + DashIntType2Status = RTL_CMAC_R8(tp, CMAC_IBISR0); + if (DashIntType2Status & ISRIMR_DASH_TYPE2_ROK) { + tp->RcvFwDashOkEvt = TRUE; + } + if (DashIntType2Status & ISRIMR_DASH_TYPE2_TOK) { + tp->SendFwHostOkEvt = TRUE; + } + if (DashIntType2Status & ISRIMR_DASH_TYPE2_RX_DISABLE_IDLE) { + tp->DashFwDisableRx = TRUE; + } + + RTL_CMAC_W8(tp, CMAC_IBISR0, DashIntType2Status); + } else { + if (status & ISRIMR_DP_REQSYS_OK) { + tp->RcvFwReqSysOkEvt = TRUE; + } + if (status & ISRIMR_DP_DASH_OK) { + tp->RcvFwDashOkEvt = TRUE; + } + if (status & ISRIMR_DP_HOST_OK) { + tp->SendFwHostOkEvt = TRUE; + } + } + } +#endif + +#ifdef CONFIG_R8168_NAPI + if (status & tp->intr_mask || tp->keep_intr_cnt-- > 0) { + if (status & tp->intr_mask) + tp->keep_intr_cnt = RTK_KEEP_INTERRUPT_COUNT; + + 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); + } else { + tp->keep_intr_cnt = RTK_KEEP_INTERRUPT_COUNT; + rtl8168_switch_to_hw_interrupt(tp); + } +#else + if (status & tp->intr_mask || tp->keep_intr_cnt-- > 0) { + u32 budget = ~(u32)0; + + if (status & tp->intr_mask) + tp->keep_intr_cnt = RTK_KEEP_INTERRUPT_COUNT; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + rtl8168_rx_interrupt(dev, tp, &budget); +#else + rtl8168_rx_interrupt(dev, tp, budget); +#endif //LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) + rtl8168_tx_interrupt(dev, tp); + +#ifdef ENABLE_DASH_SUPPORT + if ( tp->DASH ) { + struct net_device *dev = tp->dev; + + HandleDashInterrupt(dev); + } +#endif + + rtl8168_switch_to_timer_interrupt(tp); + } else { + tp->keep_intr_cnt = RTK_KEEP_INTERRUPT_COUNT; + rtl8168_switch_to_hw_interrupt(tp); + } +#endif + + } while (false); + + return IRQ_RETVAL(handled); +} + +#ifdef CONFIG_R8168_NAPI +static int rtl8168_poll(napi_ptr napi, napi_budget budget) +{ + struct rtl8168_private *tp = RTL_GET_PRIV(napi, struct rtl8168_private); + RTL_GET_NETDEV(tp) + unsigned int work_to_do = RTL_NAPI_QUOTA(budget, dev); + unsigned int work_done; + unsigned long flags; + + work_done = rtl8168_rx_interrupt(dev, tp, budget); + + spin_lock_irqsave(&tp->lock, flags); + rtl8168_tx_interrupt(dev, tp); + spin_unlock_irqrestore(&tp->lock, flags); + + RTL_NAPI_QUOTA_UPDATE(dev, work_done, budget); + + if (work_done < work_to_do) { +#ifdef ENABLE_DASH_SUPPORT + if ( tp->DASH ) { + struct net_device *dev = tp->dev; + + spin_lock_irqsave(&tp->lock, flags); + HandleDashInterrupt(dev); + spin_unlock_irqrestore(&tp->lock, flags); + } +#endif + +#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 + /* + * 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(); + + rtl8168_switch_to_timer_interrupt(tp); + } + + return RTL_NAPI_RETURN_VALUE; +} +#endif//CONFIG_R8168_NAPI + +static void rtl8168_sleep_rx_enable(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + + if (tp->wol_enabled != WOL_ENABLED) return; + + if ((tp->mcfg == CFG_METHOD_1) || (tp->mcfg == CFG_METHOD_2)) { + RTL_W8(tp, ChipCmd, CmdReset); + rtl8168_rx_desc_offset0_init(tp, 0); + RTL_W8(tp, ChipCmd, CmdRxEnb); + } else if (tp->mcfg == CFG_METHOD_14 || tp->mcfg == CFG_METHOD_15) { + rtl8168_ephy_write(tp, 0x19, 0xFF64); + RTL_W32(tp, RxConfig, RTL_R32(tp, RxConfig) | AcceptBroadcast | AcceptMulticast | AcceptMyPhys); + } +} + +static void rtl8168_down(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + unsigned long flags; + + rtl8168_delete_esd_timer(dev, &tp->esd_timer); + + rtl8168_delete_link_timer(dev, &tp->link_timer); + +#ifdef CONFIG_R8168_NAPI +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) + RTL_NAPI_DISABLE(dev, &tp->napi); +#endif +#endif//CONFIG_R8168_NAPI + + netif_stop_queue(dev); + +#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 + + spin_lock_irqsave(&tp->lock, flags); + + netif_carrier_off(dev); + + rtl8168_dsm(dev, DSM_IF_DOWN); + + rtl8168_hw_reset(dev); + + spin_unlock_irqrestore(&tp->lock, flags); + + synchronize_irq(dev->irq); + + spin_lock_irqsave(&tp->lock, flags); + + rtl8168_tx_clear(tp); + + rtl8168_rx_clear(tp); + + spin_unlock_irqrestore(&tp->lock, flags); +} + +static int rtl8168_close(struct net_device *dev) +{ + struct rtl8168_private *tp = netdev_priv(dev); + struct pci_dev *pdev = tp->pci_dev; + unsigned long flags; + + if (tp->TxDescArray!=NULL && tp->RxDescArray!=NULL) { + rtl8168_cancel_schedule_work(dev); + + rtl8168_down(dev); + + pci_clear_master(tp->pci_dev); + + spin_lock_irqsave(&tp->lock, flags); + + rtl8168_hw_d3_para(dev); + + rtl8168_powerdown_pll(dev); + + rtl8168_sleep_rx_enable(dev); + + spin_unlock_irqrestore(&tp->lock, flags); + + free_irq(dev->irq, dev); + + dma_free_coherent(&pdev->dev, R8168_RX_RING_BYTES, tp->RxDescArray, + tp->RxPhyAddr); + dma_free_coherent(&pdev->dev, R8168_TX_RING_BYTES, tp->TxDescArray, + tp->TxPhyAddr); + tp->TxDescArray = NULL; + tp->RxDescArray = NULL; + } else { + spin_lock_irqsave(&tp->lock, flags); + + rtl8168_hw_d3_para(dev); + + rtl8168_powerdown_pll(dev); + + spin_unlock_irqrestore(&tp->lock, flags); + } + + return 0; +} + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,11) +static void rtl8168_shutdown(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8168_private *tp = netdev_priv(dev); + + if (HW_DASH_SUPPORT_DASH(tp)) + rtl8168_driver_stop(tp); + + rtl8168_set_bios_setting(dev); + if (s5_keep_curr_mac == 0 && tp->random_mac == 0) + rtl8168_rar_set(tp, tp->org_mac_addr); + +#ifdef ENABLE_FIBER_SUPPORT + rtl8168_hw_fiber_nic_d3_para(dev); +#endif //ENABLE_FIBER_SUPPORT + + if (s5wol == 0) + tp->wol_enabled = WOL_DISABLED; + + rtl8168_close(dev); + rtl8168_disable_msi(pdev, tp); + + if (system_state == SYSTEM_POWER_OFF) { + pci_clear_master(tp->pci_dev); + rtl8168_sleep_rx_enable(dev); + pci_wake_from_d3(pdev, tp->wol_enabled); + pci_set_power_state(pdev, PCI_D3hot); + } +} +#endif + +/** + * rtl8168_get_stats - Get rtl8168 read/write statistics + * @dev: The Ethernet Device to get statistics for + * + * Get TX/RX statistics for rtl8168 + */ +static struct +net_device_stats *rtl8168_get_stats(struct net_device *dev) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) + struct rtl8168_private *tp = netdev_priv(dev); +#endif + if (netif_running(dev)) { +// spin_lock_irqsave(&tp->lock, flags); +// spin_unlock_irqrestore(&tp->lock, flags); + } + + return &RTLDEV->stats; +} + +#ifdef CONFIG_PM + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11) +static int +rtl8168_suspend(struct pci_dev *pdev, u32 state) +#else +static int +rtl8168_suspend(struct pci_dev *pdev, pm_message_t state) +#endif +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8168_private *tp = netdev_priv(dev); +#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; + + rtl8168_cancel_schedule_work(dev); + + rtl8168_delete_esd_timer(dev, &tp->esd_timer); + + rtl8168_delete_link_timer(dev, &tp->link_timer); + + netif_stop_queue(dev); + + netif_carrier_off(dev); + + netif_device_detach(dev); + + spin_lock_irqsave(&tp->lock, flags); + + rtl8168_dsm(dev, DSM_NIC_GOTO_D3); + + rtl8168_hw_reset(dev); + + pci_clear_master(pdev); + + rtl8168_hw_d3_para(dev); + +#ifdef ENABLE_FIBER_SUPPORT + rtl8168_hw_fiber_nic_d3_para(dev); +#endif //ENABLE_FIBER_SUPPORT + + rtl8168_powerdown_pll(dev); + + rtl8168_sleep_rx_enable(dev); + + spin_unlock_irqrestore(&tp->lock, flags); + +out: + if (HW_DASH_SUPPORT_DASH(tp)) { + spin_lock_irqsave(&tp->lock, flags); + rtl8168_driver_stop(tp); + spin_unlock_irqrestore(&tp->lock, flags); + } + +#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 +rtl8168_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8168_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 */ + rtl8168_rar_set(tp, dev->dev_addr); + + spin_unlock_irqrestore(&tp->lock, flags); + + if (!netif_running(dev)) { + if (HW_DASH_SUPPORT_DASH(tp)) { + spin_lock_irqsave(&tp->lock, flags); + rtl8168_driver_start(tp); + spin_unlock_irqrestore(&tp->lock, flags); + } + goto out; + } + + pci_set_master(pdev); + + spin_lock_irqsave(&tp->lock, flags); + + rtl8168_exit_oob(dev); + + rtl8168_dsm(dev, DSM_NIC_RESUME_D3); + + rtl8168_hw_init(dev); + + rtl8168_powerup_pll(dev); + + rtl8168_hw_ephy_config(dev); + + rtl8168_hw_phy_config(dev); + + rtl8168_hw_config(dev); + + spin_unlock_irqrestore(&tp->lock, flags); + + rtl8168_schedule_work(dev, rtl8168_reset_task); + + netif_device_attach(dev); + + mod_timer(&tp->esd_timer, jiffies + RTL8168_ESD_TIMEOUT); + mod_timer(&tp->link_timer, jiffies + RTL8168_LINK_TIMEOUT); +out: + return 0; +} + +#endif /* CONFIG_PM */ + +static struct pci_driver rtl8168_pci_driver = { + .name = MODULENAME, + .id_table = rtl8168_pci_tbl, + .probe = rtl8168_init_one, + .remove = __devexit_p(rtl8168_remove_one), +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,11) + .shutdown = rtl8168_shutdown, +#endif +#ifdef CONFIG_PM + .suspend = rtl8168_suspend, + .resume = rtl8168_resume, +#endif +}; + +static int __init +rtl8168_init_module(void) +{ +#ifdef ENABLE_R8168_PROCFS + rtl8168_proc_module_init(); +#endif +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0) + return pci_register_driver(&rtl8168_pci_driver); +#else + return pci_module_init(&rtl8168_pci_driver); +#endif +} + +static void __exit +rtl8168_cleanup_module(void) +{ + pci_unregister_driver(&rtl8168_pci_driver); +#ifdef ENABLE_R8168_PROCFS + if (rtl8168_proc) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) + remove_proc_subtree(MODULENAME, init_net.proc_net); +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32) + remove_proc_entry(MODULENAME, init_net.proc_net); +#else + remove_proc_entry(MODULENAME, proc_net); +#endif //LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32) +#endif //LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0) + rtl8168_proc = NULL; + } +#endif +} + +module_init(rtl8168_init_module); +module_exit(rtl8168_cleanup_module); diff --git a/drivers/net/ethernet/realtek/r8168/r8168_realwow.h b/drivers/net/ethernet/realtek/r8168/r8168_realwow.h new file mode 100644 index 000000000000..28e2579e75ad --- /dev/null +++ b/drivers/net/ethernet/realtek/r8168/r8168_realwow.h @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* +################################################################################ +# +# r8168 is the Linux device driver released for Realtek Gigabit Ethernet +# controllers with PCI-Express interface. +# +# Copyright(c) 2021 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. + ***********************************************************************************/ + +#ifndef _LINUX_R8168_REALWOW_H +#define _LINUX_R8168_REALWOW_H + +#define SIOCDEVPRIVATE_RTLREALWOW SIOCDEVPRIVATE+3 + +#define MAX_RealWoW_KCP_SIZE (100) +#define MAX_RealWoW_Payload (64) + +#define KA_TX_PACKET_SIZE (100) +#define KA_WAKEUP_PATTERN_SIZE (120) + +//HwSuppKeepAliveOffloadVer +#define HW_SUPPORT_KCP_OFFLOAD(_M) ((_M)->HwSuppKCPOffloadVer > 0) + +enum rtl_realwow_cmd { + + RTL_REALWOW_SET_KCP_DISABLE=0, + RTL_REALWOW_SET_KCP_INFO, + RTL_REALWOW_SET_KCP_CONTENT, + + RTL_REALWOW_SET_KCP_ACKPKTINFO, + RTL_REALWOW_SET_KCP_WPINFO, + RTL_REALWOW_SET_KCPDHCP_TIMEOUT, + + RTLT_REALWOW_COMMAND_INVALID +}; + +struct rtl_realwow_ioctl_struct { + __u32 cmd; + __u32 offset; + __u32 len; + union { + __u32 data; + void *data_buffer; + }; +}; + +typedef struct _MP_KCPInfo { + u8 DIPv4[4]; + u8 MacID[6]; + u16 UdpPort[2]; + u8 PKTLEN[2]; + + u16 ackLostCnt; + u8 KCP_WakePattern[MAX_RealWoW_Payload]; + u8 KCP_AckPacket[MAX_RealWoW_Payload]; + u32 KCP_interval; + u8 KCP_WakePattern_Len; + u8 KCP_AckPacket_Len; + u8 KCP_TxPacket[2][KA_TX_PACKET_SIZE]; +} MP_KCP_INFO, *PMP_KCP_INFO; + +typedef struct _KCPInfo { + u32 nId; // = id + u8 DIPv4[4]; + u8 MacID[6]; + u16 UdpPort; + u16 PKTLEN; +} KCPInfo, *PKCPInfo; + +typedef struct _KCPContent { + u32 id; // = id + u32 mSec; // = msec + u32 size; // =size + u8 bPacket[MAX_RealWoW_KCP_SIZE]; // put packet here +} KCPContent, *PKCPContent; + +typedef struct _RealWoWAckPktInfo { + u16 ackLostCnt; + u16 patterntSize; + u8 pattern[MAX_RealWoW_Payload]; +} RealWoWAckPktInfo,*PRealWoWAckPktInfo; + +typedef struct _RealWoWWPInfo { + u16 patterntSize; + u8 pattern[MAX_RealWoW_Payload]; +} RealWoWWPInfo,*PRealWoWWPInfo; + +int rtl8168_realwow_ioctl(struct net_device *dev, struct ifreq *ifr); +void rtl8168_realwow_hw_init(struct net_device *dev); +void rtl8168_get_realwow_hw_version(struct net_device *dev); +void rtl8168_set_realwow_d3_para(struct net_device *dev); + +#endif /* _LINUX_R8168_REALWOW_H */ diff --git a/drivers/net/ethernet/realtek/r8168/rtl_eeprom.c b/drivers/net/ethernet/realtek/r8168/rtl_eeprom.c new file mode 100644 index 000000000000..51bd3a12ac62 --- /dev/null +++ b/drivers/net/ethernet/realtek/r8168/rtl_eeprom.c @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* +################################################################################ +# +# r8168 is the Linux device driver released for Realtek Gigabit Ethernet +# controllers with PCI-Express interface. +# +# Copyright(c) 2021 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 +#include +#include +#include +#include +#include + +#include + +#include "r8168.h" +#include "rtl_eeprom.h" + +//------------------------------------------------------------------- +//rtl8168_eeprom_type(): +// tell the eeprom type +//return value: +// 0: the eeprom type is 93C46 +// 1: the eeprom type is 93C56 or 93C66 +//------------------------------------------------------------------- +void rtl8168_eeprom_type(struct rtl8168_private *tp) +{ + u16 magic = 0; + + if (tp->mcfg == CFG_METHOD_DEFAULT) + goto out_no_eeprom; + + if(RTL_R8(tp, 0xD2)&0x04) { + //not support + //tp->eeprom_type = EEPROM_TWSI; + //tp->eeprom_len = 256; + goto out_no_eeprom; + } else if(RTL_R32(tp, RxConfig) & RxCfg_9356SEL) { + tp->eeprom_type = EEPROM_TYPE_93C56; + tp->eeprom_len = 256; + } else { + tp->eeprom_type = EEPROM_TYPE_93C46; + tp->eeprom_len = 128; + } + + magic = rtl8168_eeprom_read_sc(tp, 0); + +out_no_eeprom: + if ((magic != 0x8129) && (magic != 0x8128)) { + tp->eeprom_type = EEPROM_TYPE_NONE; + tp->eeprom_len = 0; + } +} + +void rtl8168_eeprom_cleanup(struct rtl8168_private *tp) +{ + u8 x; + + x = RTL_R8(tp, Cfg9346); + x &= ~(Cfg9346_EEDI | Cfg9346_EECS); + + RTL_W8(tp, Cfg9346, x); + + rtl8168_raise_clock(tp, &x); + rtl8168_lower_clock(tp, &x); +} + +int rtl8168_eeprom_cmd_done(struct rtl8168_private *tp) +{ + u8 x; + int i; + + rtl8168_stand_by(tp); + + for (i = 0; i < 50000; i++) { + x = RTL_R8(tp, Cfg9346); + + if (x & Cfg9346_EEDO) { + udelay(RTL_CLOCK_RATE * 2 * 3); + return 0; + } + udelay(1); + } + + return -1; +} + +//------------------------------------------------------------------- +//rtl8168_eeprom_read_sc(): +// read one word from eeprom +//------------------------------------------------------------------- +u16 rtl8168_eeprom_read_sc(struct rtl8168_private *tp, u16 reg) +{ + int addr_sz = 6; + u8 x; + u16 data; + + if(tp->eeprom_type == EEPROM_TYPE_NONE) { + return -1; + } + + if (tp->eeprom_type==EEPROM_TYPE_93C46) + addr_sz = 6; + else if (tp->eeprom_type==EEPROM_TYPE_93C56) + addr_sz = 8; + + x = Cfg9346_EEM1 | Cfg9346_EECS; + RTL_W8(tp, Cfg9346, x); + + rtl8168_shift_out_bits(tp, RTL_EEPROM_READ_OPCODE, 3); + rtl8168_shift_out_bits(tp, reg, addr_sz); + + data = rtl8168_shift_in_bits(tp); + + rtl8168_eeprom_cleanup(tp); + + RTL_W8(tp, Cfg9346, 0); + + return data; +} + +//------------------------------------------------------------------- +//rtl8168_eeprom_write_sc(): +// write one word to a specific address in the eeprom +//------------------------------------------------------------------- +void rtl8168_eeprom_write_sc(struct rtl8168_private *tp, u16 reg, u16 data) +{ + u8 x; + int addr_sz = 6; + int w_dummy_addr = 4; + + if(tp->eeprom_type == EEPROM_TYPE_NONE) { + return ; + } + + if (tp->eeprom_type==EEPROM_TYPE_93C46) { + addr_sz = 6; + w_dummy_addr = 4; + } else if (tp->eeprom_type==EEPROM_TYPE_93C56) { + addr_sz = 8; + w_dummy_addr = 6; + } + + x = Cfg9346_EEM1 | Cfg9346_EECS; + RTL_W8(tp, Cfg9346, x); + + rtl8168_shift_out_bits(tp, RTL_EEPROM_EWEN_OPCODE, 5); + rtl8168_shift_out_bits(tp, reg, w_dummy_addr); + rtl8168_stand_by(tp); + + rtl8168_shift_out_bits(tp, RTL_EEPROM_ERASE_OPCODE, 3); + rtl8168_shift_out_bits(tp, reg, addr_sz); + if (rtl8168_eeprom_cmd_done(tp) < 0) { + return; + } + rtl8168_stand_by(tp); + + rtl8168_shift_out_bits(tp, RTL_EEPROM_WRITE_OPCODE, 3); + rtl8168_shift_out_bits(tp, reg, addr_sz); + rtl8168_shift_out_bits(tp, data, 16); + if (rtl8168_eeprom_cmd_done(tp) < 0) { + return; + } + rtl8168_stand_by(tp); + + rtl8168_shift_out_bits(tp, RTL_EEPROM_EWDS_OPCODE, 5); + rtl8168_shift_out_bits(tp, reg, w_dummy_addr); + + rtl8168_eeprom_cleanup(tp); + RTL_W8(tp, Cfg9346, 0); +} + +void rtl8168_raise_clock(struct rtl8168_private *tp, u8 *x) +{ + *x = *x | Cfg9346_EESK; + RTL_W8(tp, Cfg9346, *x); + udelay(RTL_CLOCK_RATE); +} + +void rtl8168_lower_clock(struct rtl8168_private *tp, u8 *x) +{ + + *x = *x & ~Cfg9346_EESK; + RTL_W8(tp, Cfg9346, *x); + udelay(RTL_CLOCK_RATE); +} + +void rtl8168_shift_out_bits(struct rtl8168_private *tp, int data, int count) +{ + u8 x; + int mask; + + mask = 0x01 << (count - 1); + x = RTL_R8(tp, Cfg9346); + x &= ~(Cfg9346_EEDI | Cfg9346_EEDO); + + do { + if (data & mask) + x |= Cfg9346_EEDI; + else + x &= ~Cfg9346_EEDI; + + RTL_W8(tp, Cfg9346, x); + udelay(RTL_CLOCK_RATE); + rtl8168_raise_clock(tp, &x); + rtl8168_lower_clock(tp, &x); + mask = mask >> 1; + } while(mask); + + x &= ~Cfg9346_EEDI; + RTL_W8(tp, Cfg9346, x); +} + +u16 rtl8168_shift_in_bits(struct rtl8168_private *tp) +{ + u8 x; + u16 d, i; + + x = RTL_R8(tp, Cfg9346); + x &= ~(Cfg9346_EEDI | Cfg9346_EEDO); + + d = 0; + + for (i = 0; i < 16; i++) { + d = d << 1; + rtl8168_raise_clock(tp, &x); + + x = RTL_R8(tp, Cfg9346); + x &= ~Cfg9346_EEDI; + + if (x & Cfg9346_EEDO) + d |= 1; + + rtl8168_lower_clock(tp, &x); + } + + return d; +} + +void rtl8168_stand_by(struct rtl8168_private *tp) +{ + u8 x; + + x = RTL_R8(tp, Cfg9346); + x &= ~(Cfg9346_EECS | Cfg9346_EESK); + RTL_W8(tp, Cfg9346, x); + udelay(RTL_CLOCK_RATE); + + x |= Cfg9346_EECS; + RTL_W8(tp, Cfg9346, x); +} + +void rtl8168_set_eeprom_sel_low(struct rtl8168_private *tp) +{ + RTL_W8(tp, Cfg9346, Cfg9346_EEM1); + RTL_W8(tp, Cfg9346, Cfg9346_EEM1 | Cfg9346_EESK); + + udelay(20); + + RTL_W8(tp, Cfg9346, Cfg9346_EEM1); +} diff --git a/drivers/net/ethernet/realtek/r8168/rtl_eeprom.h b/drivers/net/ethernet/realtek/r8168/rtl_eeprom.h new file mode 100644 index 000000000000..861defd0cb62 --- /dev/null +++ b/drivers/net/ethernet/realtek/r8168/rtl_eeprom.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* +################################################################################ +# +# r8168 is the Linux device driver released for Realtek Gigabit Ethernet +# controllers with PCI-Express interface. +# +# Copyright(c) 2021 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. + ***********************************************************************************/ + +//EEPROM opcodes +#define RTL_EEPROM_READ_OPCODE 06 +#define RTL_EEPROM_WRITE_OPCODE 05 +#define RTL_EEPROM_ERASE_OPCODE 07 +#define RTL_EEPROM_EWEN_OPCODE 19 +#define RTL_EEPROM_EWDS_OPCODE 16 + +#define RTL_CLOCK_RATE 3 + +void rtl8168_eeprom_type(struct rtl8168_private *tp); +void rtl8168_eeprom_cleanup(struct rtl8168_private *tp); +u16 rtl8168_eeprom_read_sc(struct rtl8168_private *tp, u16 reg); +void rtl8168_eeprom_write_sc(struct rtl8168_private *tp, u16 reg, u16 data); +void rtl8168_shift_out_bits(struct rtl8168_private *tp, int data, int count); +u16 rtl8168_shift_in_bits(struct rtl8168_private *tp); +void rtl8168_raise_clock(struct rtl8168_private *tp, u8 *x); +void rtl8168_lower_clock(struct rtl8168_private *tp, u8 *x); +void rtl8168_stand_by(struct rtl8168_private *tp); +void rtl8168_set_eeprom_sel_low(struct rtl8168_private *tp); + + + diff --git a/drivers/net/ethernet/realtek/r8168/rtltool.c b/drivers/net/ethernet/realtek/r8168/rtltool.c new file mode 100644 index 000000000000..2a1be0837bc9 --- /dev/null +++ b/drivers/net/ethernet/realtek/r8168/rtltool.c @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* +################################################################################ +# +# r8168 is the Linux device driver released for Realtek Gigabit Ethernet +# controllers with PCI-Express interface. +# +# Copyright(c) 2021 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 +#include +#include +#include +#include +#include +#include +#include +#include "r8168.h" +#include "rtl_eeprom.h" +#include "rtltool.h" + +int rtl8168_tool_ioctl(struct rtl8168_private *tp, struct ifreq *ifr) +{ + struct rtltool_cmd my_cmd; + unsigned long flags; + int ret; + + if (copy_from_user(&my_cmd, ifr->ifr_data, sizeof(my_cmd))) + return -EFAULT; + + ret = 0; + switch (my_cmd.cmd) { + case RTLTOOL_READ_MAC: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (my_cmd.len==1) + my_cmd.data = readb(tp->mmio_addr+my_cmd.offset); + else if (my_cmd.len==2) + my_cmd.data = readw(tp->mmio_addr+(my_cmd.offset&~1)); + else if (my_cmd.len==4) + my_cmd.data = readl(tp->mmio_addr+(my_cmd.offset&~3)); + else { + ret = -EOPNOTSUPP; + break; + } + + if (copy_to_user(ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { + ret = -EFAULT; + break; + } + break; + + case RTLTOOL_WRITE_MAC: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (my_cmd.len==1) + writeb(my_cmd.data, tp->mmio_addr+my_cmd.offset); + else if (my_cmd.len==2) + writew(my_cmd.data, tp->mmio_addr+(my_cmd.offset&~1)); + else if (my_cmd.len==4) + writel(my_cmd.data, tp->mmio_addr+(my_cmd.offset&~3)); + else { + ret = -EOPNOTSUPP; + break; + } + + break; + + case RTLTOOL_READ_PHY: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + spin_lock_irqsave(&tp->lock, flags); + my_cmd.data = rtl8168_mdio_prot_read(tp, my_cmd.offset); + spin_unlock_irqrestore(&tp->lock, flags); + + if (copy_to_user(ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { + ret = -EFAULT; + break; + } + + break; + + case RTLTOOL_WRITE_PHY: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + spin_lock_irqsave(&tp->lock, flags); + rtl8168_mdio_prot_write(tp, my_cmd.offset, my_cmd.data); + spin_unlock_irqrestore(&tp->lock, flags); + break; + + case RTLTOOL_READ_EPHY: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + spin_lock_irqsave(&tp->lock, flags); + my_cmd.data = rtl8168_ephy_read(tp, my_cmd.offset); + spin_unlock_irqrestore(&tp->lock, flags); + + if (copy_to_user(ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { + ret = -EFAULT; + break; + } + + break; + + case RTLTOOL_WRITE_EPHY: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + spin_lock_irqsave(&tp->lock, flags); + rtl8168_ephy_write(tp, my_cmd.offset, my_cmd.data); + spin_unlock_irqrestore(&tp->lock, flags); + break; + + case RTLTOOL_READ_ERI: + my_cmd.data = 0; + if (my_cmd.len==1 || my_cmd.len==2 || my_cmd.len==4) { + spin_lock_irqsave(&tp->lock, flags); + my_cmd.data = rtl8168_eri_read(tp, my_cmd.offset, my_cmd.len, ERIAR_ExGMAC); + spin_unlock_irqrestore(&tp->lock, flags); + } else { + ret = -EOPNOTSUPP; + break; + } + + if (copy_to_user(ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { + ret = -EFAULT; + break; + } + + break; + + case RTLTOOL_WRITE_ERI: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (my_cmd.len==1 || my_cmd.len==2 || my_cmd.len==4) { + spin_lock_irqsave(&tp->lock, flags); + rtl8168_eri_write(tp, my_cmd.offset, my_cmd.len, my_cmd.data, ERIAR_ExGMAC); + spin_unlock_irqrestore(&tp->lock, flags); + } else { + ret = -EOPNOTSUPP; + break; + } + break; + + case RTLTOOL_READ_PCI: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + my_cmd.data = 0; + if (my_cmd.len==1) + pci_read_config_byte(tp->pci_dev, my_cmd.offset, + (u8 *)&my_cmd.data); + else if (my_cmd.len==2) + pci_read_config_word(tp->pci_dev, my_cmd.offset, + (u16 *)&my_cmd.data); + else if (my_cmd.len==4) + pci_read_config_dword(tp->pci_dev, my_cmd.offset, + &my_cmd.data); + else { + ret = -EOPNOTSUPP; + break; + } + + if (copy_to_user(ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { + ret = -EFAULT; + break; + } + break; + + case RTLTOOL_WRITE_PCI: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (my_cmd.len==1) + pci_write_config_byte(tp->pci_dev, my_cmd.offset, + my_cmd.data); + else if (my_cmd.len==2) + pci_write_config_word(tp->pci_dev, my_cmd.offset, + my_cmd.data); + else if (my_cmd.len==4) + pci_write_config_dword(tp->pci_dev, my_cmd.offset, + my_cmd.data); + else { + ret = -EOPNOTSUPP; + break; + } + + break; + + case RTLTOOL_READ_EEPROM: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + spin_lock_irqsave(&tp->lock, flags); + my_cmd.data = rtl8168_eeprom_read_sc(tp, my_cmd.offset); + spin_unlock_irqrestore(&tp->lock, flags); + + if (copy_to_user(ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { + ret = -EFAULT; + break; + } + + break; + + case RTLTOOL_WRITE_EEPROM: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + spin_lock_irqsave(&tp->lock, flags); + rtl8168_eeprom_write_sc(tp, my_cmd.offset, my_cmd.data); + spin_unlock_irqrestore(&tp->lock, flags); + break; + + case RTL_READ_OOB_MAC: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + spin_lock_irqsave(&tp->lock, flags); + rtl8168_oob_mutex_lock(tp); + my_cmd.data = rtl8168_ocp_read(tp, my_cmd.offset, 4); + rtl8168_oob_mutex_unlock(tp); + spin_unlock_irqrestore(&tp->lock, flags); + + if (copy_to_user(ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { + ret = -EFAULT; + break; + } + break; + + case RTL_WRITE_OOB_MAC: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (my_cmd.len == 0 || my_cmd.len > 4) + return -EOPNOTSUPP; + + spin_lock_irqsave(&tp->lock, flags); + rtl8168_oob_mutex_lock(tp); + rtl8168_ocp_write(tp, my_cmd.offset, my_cmd.len, my_cmd.data); + rtl8168_oob_mutex_unlock(tp); + spin_unlock_irqrestore(&tp->lock, flags); + break; + + case RTL_ENABLE_PCI_DIAG: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + spin_lock_irqsave(&tp->lock, flags); + tp->rtk_enable_diag = 1; + spin_unlock_irqrestore(&tp->lock, flags); + + dprintk("enable rtk diag\n"); + break; + + case RTL_DISABLE_PCI_DIAG: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + spin_lock_irqsave(&tp->lock, flags); + tp->rtk_enable_diag = 0; + spin_unlock_irqrestore(&tp->lock, flags); + + dprintk("disable rtk diag\n"); + break; + + case RTL_READ_MAC_OCP: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (my_cmd.offset % 2) + return -EOPNOTSUPP; + + spin_lock_irqsave(&tp->lock, flags); + my_cmd.data = rtl8168_mac_ocp_read(tp, my_cmd.offset); + spin_unlock_irqrestore(&tp->lock, flags); + + if (copy_to_user(ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { + ret = -EFAULT; + break; + } + break; + + case RTL_WRITE_MAC_OCP: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if ((my_cmd.offset % 2) || (my_cmd.len != 2)) + return -EOPNOTSUPP; + + spin_lock_irqsave(&tp->lock, flags); + rtl8168_mac_ocp_write(tp, my_cmd.offset, (u16)my_cmd.data); + spin_unlock_irqrestore(&tp->lock, flags); + break; + + case RTL_DIRECT_READ_PHY_OCP: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + spin_lock_irqsave(&tp->lock, flags); + my_cmd.data = rtl8168_mdio_prot_direct_read_phy_ocp(tp, my_cmd.offset); + spin_unlock_irqrestore(&tp->lock, flags); + + if (copy_to_user(ifr->ifr_data, &my_cmd, sizeof(my_cmd))) { + ret = -EFAULT; + break; + } + + break; + + case RTL_DIRECT_WRITE_PHY_OCP: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + spin_lock_irqsave(&tp->lock, flags); + rtl8168_mdio_prot_direct_write_phy_ocp(tp, my_cmd.offset, my_cmd.data); + spin_unlock_irqrestore(&tp->lock, flags); + break; + + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} diff --git a/drivers/net/ethernet/realtek/r8168/rtltool.h b/drivers/net/ethernet/realtek/r8168/rtltool.h new file mode 100644 index 000000000000..d8731dff801f --- /dev/null +++ b/drivers/net/ethernet/realtek/r8168/rtltool.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* +################################################################################ +# +# r8168 is the Linux device driver released for Realtek Gigabit Ethernet +# controllers with PCI-Express interface. +# +# Copyright(c) 2021 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. + ***********************************************************************************/ + +#ifndef _LINUX_RTLTOOL_H +#define _LINUX_RTLTOOL_H + +#define SIOCRTLTOOL SIOCDEVPRIVATE+1 + +enum rtl_cmd { + RTLTOOL_READ_MAC=0, + RTLTOOL_WRITE_MAC, + RTLTOOL_READ_PHY, + RTLTOOL_WRITE_PHY, + RTLTOOL_READ_EPHY, + RTLTOOL_WRITE_EPHY, + RTLTOOL_READ_ERI, + RTLTOOL_WRITE_ERI, + RTLTOOL_READ_PCI, + RTLTOOL_WRITE_PCI, + RTLTOOL_READ_EEPROM, + RTLTOOL_WRITE_EEPROM, + + RTL_READ_OOB_MAC, + RTL_WRITE_OOB_MAC, + + RTL_ENABLE_PCI_DIAG, + RTL_DISABLE_PCI_DIAG, + + RTL_READ_MAC_OCP, + RTL_WRITE_MAC_OCP, + + RTL_DIRECT_READ_PHY_OCP, + RTL_DIRECT_WRITE_PHY_OCP, + + RTLTOOL_INVALID +}; + +struct rtltool_cmd { + __u32 cmd; + __u32 offset; + __u32 len; + __u32 data; +}; + +enum mode_access { + MODE_NONE=0, + MODE_READ, + MODE_WRITE +}; + +#ifdef __KERNEL__ +int rtl8168_tool_ioctl(struct rtl8168_private *tp, struct ifreq *ifr); +#endif + +#endif /* _LINUX_RTLTOOL_H */ -- Gitee From 62d28333e409e63e87753e416a5ddf168a728de0 Mon Sep 17 00:00:00 2001 From: Yinbo Zhu Date: Sun, 24 Apr 2022 15:36:48 +0800 Subject: [PATCH 13/56] loongarch: configs: make r8111G nic use r8168 driver use by default The r8168 driver has newer hardware parameters and it may consume lower power consumption than r8169 by upstream realtek maintainer said. In addition, use r8169 driver that fangzheng 3a5000-7a1000 externel r8111G nic will not work well, while the 8168 driver doesn't cause this issue. Based on the above considerations, it is decided to use r8168 driver by default and this patch is to remove "CONFIG_R8169=m" in loongson3_defconfig defconfig for loongarch. Signed-off-by: Yinbo Zhu Signed-off-by: Ming Wang Change-Id: I28d76a29a94c8a7656344c6b97e843bd56b282ec --- arch/loongarch/configs/loongson3_defconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index ace3efc63b08..63d0e3d5bd66 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -466,7 +466,7 @@ CONFIG_TXGBE=m # CONFIG_NET_VENDOR_RDC is not set CONFIG_8139CP=m CONFIG_8139TOO=m -CONFIG_R8169=y +CONFIG_R8168=m # CONFIG_NET_VENDOR_RENESAS is not set # CONFIG_NET_VENDOR_ROCKER is not set # CONFIG_NET_VENDOR_SAMSUNG is not set @@ -482,6 +482,7 @@ CONFIG_STMMAC_ETH=y # CONFIG_NET_VENDOR_VIA is not set # CONFIG_NET_VENDOR_WIZNET is not set # CONFIG_NET_VENDOR_XILINX is not set +CONFIG_REALTEK_PHY=y CONFIG_PPP=m CONFIG_PPP_BSDCOMP=m CONFIG_PPP_DEFLATE=m -- Gitee From d0376fffaf2a2f3d8051f2fff0066e9b669e597e Mon Sep 17 00:00:00 2001 From: liuyun Date: Mon, 5 Sep 2022 07:25:41 +0800 Subject: [PATCH 14/56] cpufreq:Add cpufreq driver for loongarch Signed-off-by: zhangtianyang Change-Id: Ic9390a1c78e1670422be1b03d35e3f0fdec1367f --- arch/loongarch/Kconfig | 6 + arch/loongarch/configs/loongson3_defconfig | 5 + drivers/cpufreq/Kconfig | 11 + drivers/cpufreq/Makefile | 1 + drivers/cpufreq/loongson3-acpi-cpufreq.c | 1569 ++++++++++++++++++++ 5 files changed, 1592 insertions(+) create mode 100644 drivers/cpufreq/loongson3-acpi-cpufreq.c diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 94a722515400..77de7a1370f7 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -514,4 +514,10 @@ source "drivers/acpi/Kconfig" endmenu +menu "CPU Power Management" + +source "drivers/cpufreq/Kconfig" + +endmenu + source "drivers/firmware/Kconfig" diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index 63d0e3d5bd66..36680ff00455 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -796,3 +796,8 @@ CONFIG_STRIP_ASM_SYMS=y CONFIG_MAGIC_SYSRQ=y # CONFIG_SCHED_DEBUG is not set CONFIG_SCHEDSTATS=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ=y +CONFIG_LOONGSON3_ACPI_CPUFREQ=y diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 85de313ddec2..c1540d446dba 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -287,6 +287,17 @@ config LOONGSON1_CPUFREQ If in doubt, say N. endif +if LOONGARCH +config LOONGSON3_ACPI_CPUFREQ + bool "Loongson3 ACPI cpufreq driver" + depends on ACPI_PROCESSOR + help + This driver adds a CPUFreq driver which utilizes the ACPI + Processor Performance States. + This driver supports Loongson 3A4000 compatible CPUs. + If in doubt, say N. +endif + if SPARC64 config SPARC_US3_CPUFREQ tristate "UltraSPARC-III CPU Frequency driver" diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 7c762e105146..97e34fa3efe1 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -106,6 +106,7 @@ obj-$(CONFIG_BMIPS_CPUFREQ) += bmips-cpufreq.o obj-$(CONFIG_IA64_ACPI_CPUFREQ) += ia64-acpi-cpufreq.o obj-$(CONFIG_LOONGSON2_CPUFREQ) += loongson2_cpufreq.o obj-$(CONFIG_LOONGSON1_CPUFREQ) += loongson1-cpufreq.o +obj-$(CONFIG_LOONGSON3_ACPI_CPUFREQ) += loongson3-acpi-cpufreq.o obj-$(CONFIG_SH_CPU_FREQ) += sh-cpufreq.o obj-$(CONFIG_SPARC_US2E_CPUFREQ) += sparc-us2e-cpufreq.o obj-$(CONFIG_SPARC_US3_CPUFREQ) += sparc-us3-cpufreq.o diff --git a/drivers/cpufreq/loongson3-acpi-cpufreq.c b/drivers/cpufreq/loongson3-acpi-cpufreq.c new file mode 100644 index 000000000000..e0947b3f9459 --- /dev/null +++ b/drivers/cpufreq/loongson3-acpi-cpufreq.c @@ -0,0 +1,1569 @@ +/* + * loongson3-acpi-cpufreq.c - Loongson ACPI Processor P-States Driver + * + * Copyright (C) 2020 lvjianmin + * Yijun + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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, write to the Free Software Foundation, Inc., + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "cpufreq_governor.h" + +#include +#define CPU_ID_FIELD 0xf + +#define COMPLETE_STATUS 0x80000000 +#define VOLTAGE_COMMAND 0x21 + +#define DVFS_INFO 0x22 +#define DVFS_INFO_BOOST_LEVEL 0x23 +#define DVFS_INFO_MIN_FREQ 0xf +#define DVFS_INFO_MAX_FREQ 0xf0 +#define DVFS_INFO_BOOST_CORE_FREQ 0xff00 +#define DVFS_INFO_NORMAL_CORE_UPPER_LIMIT 0xf0000 +#define DVFS_INFO_BOOST_CORES 0xf00000 + +#define BOOST_MODE 0x80000 +#define NORMAL_MODE 0x40000 + +MODULE_DESCRIPTION("Loongson 3A5000 ACPI Processor P-States Driver"); + +MODULE_LICENSE("GPL"); + +#define CPUFREQ_SAMPLING_INTERVAL (2 * TICK_NSEC / NSEC_PER_USEC) +#define LOONGSON_CONTROL_MASK (0xFF) +#define FACTOR (0xeac0c6e8) +#define BOOST_THRESHOLD (900) +#define MAX_CORES_PER_PACKAGE 64 +#define CPU_ID_FIELD 0xf +#define VOLTAGE_COMMAND 0x21 +#define MAX_READY_TIMEOUT 300000000 +#define RESERVED_FREQ 3 + +#define LOONGSON_BOOST_FREQ_MASK (0x7 << 8) +#define FREQ_STEP (25) + +static struct mutex boost_mutex[MAX_PACKAGES]; +static bool cpufreq_has_boost_freq; +static int max_boost_cores; +static int boost_gears; +static int boost_freqs[NR_CPUS + 1]; +struct package_data; +struct core_data; +static struct acpi_processor_performance __percpu *acpi_perf_data; +static struct cpufreq_driver loongson3_cpufreq_driver; +static struct freq_attr *loongson3_cpufreq_attr[]; +DECLARE_PER_CPU(struct clock_event_device, stable_clockevent_device); +static inline struct core_data *get_core_data(int cpu); +extern struct clk *cpu_clk_get(int cpu); + +static int min_freq_level; +static int max_freq_level; +static int max_upper_index; +static int max_boost_freq; + +/* threshold of core's get into msa */ +static int msa_count_threshold = 200; +/* threshold of core's get into lasx */ +static int lasx_count_threshold = 200; +/* other cores' upper load threshold when 1 core get into boost mode and enable msa/lasx */ +static int load_threshold = 60; + +DEFINE_PER_CPU(unsigned long, msa_count); +EXPORT_PER_CPU_SYMBOL(msa_count); + +#if defined(CONFIG_CPU_HAS_LASX) +DEFINE_PER_CPU(unsigned long, lasx_count); +EXPORT_PER_CPU_SYMBOL(lasx_count); +#endif + +struct ce_update_data { + struct clock_event_device *cd; + unsigned int new_freq; +}; + +static struct kthread_worker cpufreq_worker; +static struct task_struct *cpufreq_thread; +/** + * struct core_data - Store core related information + * @in_boost: the core is boosting to boost_freq + * @cpu: logical cpu of the core + * @update_util The update_util_data pointer of @cpu, is passed to the callback + * function, which will be called by cpufreq_update_util() + * @package The package_data structure the core belonged to + * @work_in_progress @work is busy + * @irq_work to enqueue callback handling on irq workqueue + * @work to enqueue work from irq workqueue on system workqueue + * @perf store frequency table related information from ACPI table + * @max_freq max normal freq of cpu + * @boost_freq max boost freq of cpu + * @clock_scale clock scale to calculate cpu_data[cpu].udelay_val in boost mode + * @package_id package id of core + * @shift clock shift to calculate cpu_data[cpu].udelay_val in boost mode + * @update_util_set if callback has been set for cpufreq_update_util() + * @load current load of the core + * @last_freq_update_time last freq update time + * @freq_update_delay_ns min interval of freq update, which is + * transition_latency configured in ACPI table + * + * following elements are used to calculate load of the core + * @prev_update_time + * @prev_cpu_idle + * @prev_load + * @sampling_rate + * + */ +struct core_data { + bool in_boost; + int cpu; + struct update_util_data update_util; + struct package_data *package; + bool work_in_progress; + struct irq_work irq_work; + struct kthread_work work; + struct acpi_processor_performance *perf; + unsigned int normal_max_freq; + unsigned int *boost_freq; + unsigned int *clock_scale; + unsigned int package_id; + unsigned int *shift; + bool update_util_set; + unsigned long long load; + + u64 last_freq_update_time; + s64 freq_update_delay_ns; + u64 prev_update_time; + u64 prev_cpu_idle; + u32 prev_load; + u32 sampling_rate; +}; + +struct package_data { + int boost_cores; + int max_boost_cores; + int nr_cores; + char in_boost; + int nr_full_load_cores; + struct core_data core[MAX_CORES_PER_PACKAGE]; +} all_package_data[MAX_PACKAGES]; + +static bool boost_supported(void) +{ + return loongson3_cpufreq_driver.set_boost; +} + +/* + * Check if target_freq is a boost freq + * + * target_freq must be a freq in freq table when + * calling the function. + * */ +static int boost_level(struct acpi_processor_performance *perf, unsigned int target_freq) +{ + int i; + + for (i = 0; i < perf->state_count; i++) { + if (target_freq == (perf->states[i].core_frequency * 1000)) { + return (perf->states[i].control & LOONGSON_BOOST_FREQ_MASK) >> 8; + } + } + return 0; +} + +#ifdef CONFIG_SMP +static int loongson3_cpu_freq_notifier(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct cpufreq_freqs *freqs; + struct clock_event_device __maybe_unused *cd; + struct core_data *core; + unsigned int __maybe_unused new_freq; + unsigned long cpu; + struct ce_update_data __maybe_unused ce_data; + int cur_boost_level; + + if (val == CPUFREQ_POSTCHANGE) { + freqs = (struct cpufreq_freqs *)data; + cpu = freqs->policy->cpu; + core = get_core_data(cpu); + cur_boost_level = boost_level(core->perf, freqs->new); + if (cur_boost_level != 0) { + lpj_fine = (unsigned int) (((int64_t)core->clock_scale[cur_boost_level] * + cpufreq_scale(loops_per_jiffy, boost_freqs[cur_boost_level] * 1000, + freqs->new)) / core->shift[cur_boost_level]); + } else { + lpj_fine = + cpufreq_scale(loops_per_jiffy, core->normal_max_freq * 1000, freqs->new); + } + } + + return 0; +} +#else +static int loongson3_cpu_freq_notifier(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct cpufreq_freqs *freqs; + struct clock_event_device __maybe_unused *cd; + struct core_data *core; + unsigned int __maybe_unused new_freq; + unsigned long cpu; + int cur_boost_level; + + if (val == CPUFREQ_POSTCHANGE) { + + freqs = (struct cpufreq_freqs *)data; + cpu = freqs->cpu; + core = get_core_data(cpu); + cur_boost_level = boost_level(core->perf, target_freq); + + if (cur_boost_level != 0) { + lpj_fine = (unsigned int) (((int64_t)core->clock_scale[cur_boost_level] * + loops_per_jiffy) / core->shift[cur_boost_level]); + } else { + lpj_fine = loops_per_jiffy; + } + } + + return 0; +} +#endif +static struct notifier_block loongson3_cpufreq_notifier_block = { + .notifier_call = loongson3_cpu_freq_notifier +}; + +static int cpufreq_perf_find_level(struct acpi_processor_performance *perf, + unsigned int target_freq, + unsigned int boost_level) +{ + int i; + for (i = 0; i < perf->state_count; i++) { + if (boost_level) { + if (perf->states[i].control & LOONGSON_BOOST_FREQ_MASK) { + if (target_freq == (perf->states[i].core_frequency * 1000)) + return perf->states[i].control & LOONGSON_CONTROL_MASK; + } + } else { + if (!(perf->states[i].control & LOONGSON_BOOST_FREQ_MASK)) + if (target_freq == (perf->states[i].core_frequency * 1000)) + return perf->states[i].control; + } + } + return 0; +} + +static int cpufreq_perf_find_freq(struct acpi_processor_performance *perf, + unsigned int target_index, + unsigned int boost_level) +{ + int i; + for (i = 0; i < perf->state_count; i++) { + if (boost_level) { + if (perf->states[i].control & LOONGSON_BOOST_FREQ_MASK) + if (target_index == (perf->states[i].control & LOONGSON_CONTROL_MASK)) + return perf->states[i].core_frequency; + } else { + if (!(perf->states[i].control & LOONGSON_BOOST_FREQ_MASK)) + if (target_index == perf->states[i].control) + return perf->states[i].core_frequency; + } + } + return 0; +} + + +static inline struct core_data *get_core_data(int cpu) +{ + int package_id = cpu_data[cpu].package; + struct package_data *package = &all_package_data[package_id]; + int core_id = cpu_logical_map(cpu) % package->nr_cores; + return &package->core[core_id]; +} + +static bool package_boost(struct package_data *package) +{ + int i; + int cur_full_load = 0; + +#if defined(CONFIG_CPU_HAS_LASX) + int lasx_enable_count = 0; + unsigned long lasx_num; + bool clear_lasx = false; +#endif + + int msa_enable_count = 0; + unsigned long msa_num; + bool clear_msa = false; + + for (i = 0; i < package->nr_cores; i++) { + +#if defined(CONFIG_CPU_HAS_LASX) + lasx_num = per_cpu(lasx_count, package->core[i].cpu); + + if (lasx_num) { + lasx_enable_count++; + } + + if (lasx_num >= lasx_count_threshold) { + clear_lasx = true; + } + + pr_debug("file %s, line %d, lasx enabled, i %d, cpu %d, lasx_num %lu\n", + __FILE__, __LINE__, i, package->core[i].cpu, lasx_num); +#endif + msa_num = per_cpu(msa_count, package->core[i].cpu); + + if (msa_num) { + msa_enable_count++; + } + + if (msa_num >= msa_count_threshold) { + clear_msa = true; + } + + pr_debug("file %s, line %d, msa enabled, i %d, cpu %d, msa_num %lu\n", + __FILE__, __LINE__, i, package->core[i].cpu, msa_num); + + if (package->core[i].prev_load >= load_threshold) { + cur_full_load++; + } + } + +#if defined(CONFIG_CPU_HAS_LASX) + if (clear_lasx) { + for (i = 0; i < package->nr_cores; i++) { + per_cpu(lasx_count, package->core[i].cpu) = 0; + } + } +#endif + + if (clear_msa) { + for (i = 0; i < package->nr_cores; i++) { + per_cpu(msa_count, package->core[i].cpu) = 0; + } + } + +#if defined(CONFIG_CPU_HAS_LASX) + if (lasx_enable_count > 1 + || (lasx_enable_count && package->nr_full_load_cores > 1) + || (lasx_enable_count && cur_full_load > 1)) { + return false; + } +#endif + + if (msa_enable_count > 1 + || (msa_enable_count && package->nr_full_load_cores > 1) + || (msa_enable_count && cur_full_load > 1)) { + return false; + } + + if (package->nr_full_load_cores && + package->nr_full_load_cores <= package->max_boost_cores) + return true; + + return false; +} + +/* + * check if the cpu can be boosted. + * + * call the function after load of cpu updated. + * */ +static bool cpu_can_boost(int cpu) +{ + struct core_data *core = get_core_data(cpu); + struct package_data *package = core->package; + if (package->boost_cores >= package->max_boost_cores) + return false; + if (core->load > BOOST_THRESHOLD) { + return true; + } + return false; +} + +static void do_set_freq_level(int cpu, int freq_level) +{ + uint32_t message; + uint32_t val; + + message = (0 << 31) | (VOLTAGE_COMMAND << 24) + | ((uint32_t)freq_level << 4) + | (cpu & CPU_ID_FIELD); + iocsr_write32(message, 0x51c); + val = iocsr_read32(0x420); + + val |= 1 << 10; + iocsr_write32(val, 0x420); +} + +static int wait_for_ready_timeout(int64_t timeout) +{ + int ret; + struct timespec64 prev_ts; + struct timespec64 curr_ts; + ktime_t delay = ktime_set(0, 100); + + ktime_get_ts64(&prev_ts); + ktime_get_ts64(&curr_ts); + + ret = -EPERM; + while (((curr_ts.tv_sec - prev_ts.tv_sec) * 1000000000 + (curr_ts.tv_nsec - prev_ts.tv_nsec)) < timeout) { + ktime_get_ts64(&curr_ts); + + if (iocsr_read32(0x51c) & COMPLETE_STATUS) { + ret = 0; + break; + } + + __set_current_state(TASK_UNINTERRUPTIBLE); + schedule_hrtimeout(&delay, HRTIMER_MODE_REL); + } + return ret; +} + +/* Find closest freq to target in a table in ascending order */ +static int cpufreq_table_find_freq_ac(struct cpufreq_policy *policy, + unsigned int target_freq, + int boost_level) +{ + struct cpufreq_frequency_table *table = policy->freq_table; + struct cpufreq_frequency_table *pos; + unsigned int freq; + unsigned int best_freq = 0; + int idx, best = -1; + cpufreq_for_each_valid_entry_idx(pos, table, idx) { + freq = pos->frequency; + + if (pos->driver_data != boost_level) + continue; + if (freq > policy->max || freq < policy->min) + continue; + if (freq == target_freq) + return freq; + + if (freq < target_freq) { + best = idx; + best_freq = freq; + continue; + } + + /* No freq found below target_freq, return freq above target_freq */ + if (best == -1) + return freq; + + /* Choose the closest freq */ + if (target_freq - table[best].frequency > freq - target_freq) + return freq; + + return best_freq; + } + + return best_freq; +} + +/* Find closest freq to target in a table in descending order */ +static int cpufreq_table_find_freq_dc(struct cpufreq_policy *policy, + unsigned int target_freq, + int boost_level) +{ + struct cpufreq_frequency_table *table = policy->freq_table; + struct cpufreq_frequency_table *pos; + unsigned int freq; + unsigned int best_freq = 0; + int idx, best = -1; + + cpufreq_for_each_valid_entry_idx(pos, table, idx) { + freq = pos->frequency; + + if (pos->driver_data != boost_level) + continue; + if (freq > policy->max || freq < policy->min) + continue; + + if (freq == target_freq) { + + return freq; + } + if (freq > target_freq) { + best = idx; + best_freq = freq; + continue; + } + + /* No freq found above target_freq, return freq below target_freq */ + if (best == -1) { + return freq; + } + /* Choose the closest freq */ + if (table[best].frequency - target_freq > target_freq - freq) { + + return freq; + } + return best_freq; + } + + return best_freq; +} + +/* Works only on sorted freq-tables */ +static int cpufreq_table_find_freq(struct cpufreq_policy *policy, + unsigned int target_freq, + int boost_level) +{ + target_freq = clamp_val(target_freq, policy->min, policy->max); + if (policy->freq_table_sorted == CPUFREQ_TABLE_SORTED_ASCENDING) + return cpufreq_table_find_freq_ac(policy, target_freq, boost_level); + else + return cpufreq_table_find_freq_dc(policy, target_freq, boost_level); +} + +static void transition_end(struct cpufreq_policy *policy, + struct cpufreq_freqs *freqs, bool failed) +{ + if (unlikely(!policy->transition_ongoing)) { + return; + } + cpufreq_freq_transition_end(policy, freqs, failed); +} +static void transition_begin(struct cpufreq_policy *policy, + struct cpufreq_freqs *freqs) +{ + if (unlikely(policy->transition_ongoing)) { + cpufreq_freq_transition_end(policy, freqs, true); + } + cpufreq_freq_transition_begin(policy, freqs); +} + +static void update_core_boost_info(struct core_data *core, bool boost_set) +{ + core->in_boost = boost_set; + if (boost_set) + core->package->boost_cores++; + else + core->package->boost_cores--; +} + +static unsigned int cores_freq_trans_notify(struct package_data *package, + bool before_trans, + bool trans_failed, + int find_level, + int find_freq, + unsigned int skip_cpumask) +{ + int i; + struct cpufreq_policy *policy; + struct cpufreq_freqs freqs; + unsigned int cores_level = 0; + unsigned int core_level; + + for (i = 0; i < package->nr_cores; i++) { + struct core_data *core = &package->core[i]; + policy = cpufreq_cpu_get_raw(core->cpu); + if (((1 << i) & skip_cpumask) || !policy) { + continue; + } + freqs.old = policy->cur; + freqs.flags = 0; + + /* find level from normal levels */ + core_level = cpufreq_perf_find_level(core->perf, policy->cur, find_level); + if (!core_level) { + pr_debug("cpu%d policy->cur=%d find_level=%d freq=%d skip_cpumask=%x \n", + policy->cpu, policy->cur, find_level, find_freq, skip_cpumask); + } + freqs.new = cpufreq_perf_find_freq(core->perf, core_level, find_freq) * 1000; + if (!freqs.new) { + pr_debug("file %s, line %d, find freq error\n", __FILE__, __LINE__); + } + + pr_debug("file %s, line %d, cpu %d, old freq %d, new freq %d, find_level %d, find_freq %d\n", + __FILE__, __LINE__, policy->cpu, freqs.old, freqs.new, find_level, find_freq); + cores_level |= (core_level << (i << 2)); + + if (before_trans) + transition_begin(policy, &freqs); + else { + transition_end(policy, &freqs, trans_failed); + } + } + return cores_level; +} +static int loongson3_set_freq(struct core_data *core, unsigned long freq, int boost_level) +{ + int ret = 0; + int freq_level; + int phy_cpu; + int target_freq; + struct cpufreq_freqs freqs; + struct cpufreq_policy *policy = cpufreq_cpu_get_raw(core->cpu); + + if (!policy) + return -EINVAL; + + ret = wait_for_ready_timeout(MAX_READY_TIMEOUT); + if (ret) + return ret; + + phy_cpu = cpu_logical_map(core->cpu); + target_freq = cpufreq_table_find_freq(policy, freq, boost_level); + if (!target_freq) + return -1; + if (target_freq == policy->cur) + return -1; + + freqs.flags = 0; + freqs.old = policy->cur; + freqs.new = target_freq; + freq_level = cpufreq_perf_find_level(core->perf, target_freq, boost_level); + if (!freq_level) { + pr_debug("loongson3_set_freq cpu%d freq=%lu targetfreq=%d boost_level=%d find level error\n", + core->cpu, freq, target_freq, boost_level); + } + + transition_begin(policy, &freqs); + do_set_freq_level(phy_cpu, freq_level); + ret = wait_for_ready_timeout(MAX_READY_TIMEOUT); + transition_end(policy, &freqs, !!ret); + + return ret; +} + +int loongson3_set_mode(int mode, int freq_level) +{ + uint32_t val; + int ret = 0; + uint32_t message; + + ret = wait_for_ready_timeout(MAX_READY_TIMEOUT); + if (ret) + return ret; + + message = mode | (VOLTAGE_COMMAND << 24) | freq_level; + iocsr_write32(message, 0x51c); + val = iocsr_read32(0x420); + val |= 1 << 10; + iocsr_write32(val, 0x420); + return wait_for_ready_timeout(MAX_READY_TIMEOUT); +} + +enum freq_adjust_action{ + FAA_NORMAL, + FAA_N2B, + FAA_B2N, + FAA_BOOST, +}; + +static int faa_normal(struct cpufreq_policy *policy, int load) +{ + int ret; + unsigned int freq_next, min_f, max_f; + struct core_data *core = get_core_data(policy->cpu); + if (!core) + return -1; + + pr_debug("file %s, line %d, func %s\n", __FILE__, __LINE__, __func__); + + min_f = policy->min; + max_f = policy->max; + freq_next = min_f + load * (max_f - min_f) / 100; + ret = loongson3_set_freq(core, freq_next, 0); + return ret; +} + +static void handle_boost_cores(struct core_data *core, struct package_data *package, + unsigned long target_freq, bool skip_update_and_notify, bool update_core, bool inc_boost) +{ + int boost_level; + int find_level; + int find_freq; + int ret; + int inc_core = inc_boost ? 1 : -1; + + if (boost_gears == 1) { + find_level = 0; + boost_level = boost_gears; + } else { + find_level = package->boost_cores; + if (update_core) + boost_level = package->boost_cores + inc_core; + else + boost_level = package->boost_cores; + } + find_freq = boost_level; + ret = loongson3_set_freq(core, target_freq, boost_level); + if (ret) + return; + + if (skip_update_and_notify) { + if (update_core) + update_core_boost_info(core, inc_boost); + return; + } + + if (boost_gears != 1) { + cores_freq_trans_notify(package, true, false, + find_level, find_freq, 1 << core->cpu); + cores_freq_trans_notify(package, false, false, + find_level, find_freq, 1 << core->cpu); + } + if (update_core) + update_core_boost_info(core, inc_boost); +} + +static void faa_boost(struct cpufreq_policy *policy, int load) +{ + unsigned int min_f, max_f; + struct core_data *core = get_core_data(policy->cpu); + struct package_data *package = core->package; + unsigned long target_freq; + + pr_debug("file %s, line %d, func %s\n", __FILE__, __LINE__, __func__); + + /* boost cores form n to n + 1 */ + if (core->load > BOOST_THRESHOLD) { + if (package->boost_cores < package->max_boost_cores + && !core->in_boost) { + if (boost_gears == 1) { + target_freq = policy->max; + } else { + target_freq = cpufreq_table_find_freq(policy, policy->max, package->boost_cores + 1); + if (!target_freq) { + pr_debug("file %s, line %d, find freq error ,boost_level %d, cur freq %d\n", + __FILE__, __LINE__, package->boost_cores, policy->max); + } + } + handle_boost_cores(core, package, target_freq, false, true, true); + } + } else { + /* 1. core not in boost, level up but not change pll + * 2. core in boost, boost cores from n to n - 1 */ + min_f = policy->min; + max_f = policy->max; + target_freq = min_f + load * (max_f - min_f) / 100; + handle_boost_cores(core, package, target_freq, !core->in_boost, core->in_boost, false); + } + + +} + +static void get_boost_cores(struct package_data *package, int *boost_cores, int *boost_count) +{ + struct core_data *core; + struct cpufreq_policy *policy; + int i; + + /* count boost cores */ + for (i = 0; i < package->nr_cores; i++) { + core = &package->core[i]; + policy = cpufreq_cpu_get_raw(core->cpu); + if (!policy) + continue; + + if (cpu_can_boost(core->cpu)) { + if (boost_cores) + *boost_cores |= (1 << i); + + (*boost_count)++; + } + } +} + +static void faa_n2b(struct package_data *package, struct core_data *core) +{ + int boost_cores = 0; + int boost_count = 0; + int freq_level; + + pr_debug("file %s, line %d func %s\n", __FILE__, __LINE__, __func__); + + get_boost_cores(package, &boost_cores, &boost_count); + + if (boost_gears == 1) { + boost_count = 1; + } + + freq_level = cores_freq_trans_notify(package, true, false, + 0, boost_count, 0); + if (!loongson3_set_mode(BOOST_MODE, freq_level)) { + int i; + cores_freq_trans_notify(package, false, false, + 0, boost_count, 0); + package->in_boost = true; + for (i = 0; i < package->nr_cores; i++) { + if (boost_cores & (1 << i)) + update_core_boost_info(&package->core[i], true); + } + } else + cores_freq_trans_notify(package, false, true, + 0, boost_count, 0); +} + +static void faa_b2n(struct package_data *package) +{ + int i; + int boost_count = package->boost_cores; + + if (boost_gears == 1) { + boost_count = 1; + } + + pr_debug("file %s, line %d, func %s\n", __FILE__, __LINE__, __func__); + + cores_freq_trans_notify(package, true, false, + boost_count, 0, 0); + if (!loongson3_set_mode(NORMAL_MODE, 0)) { + cores_freq_trans_notify(package, false, false, + boost_count, 0, 0); + for (i = 0; i < package->nr_cores; i++) { + if (package->core[i].in_boost) + update_core_boost_info(&package->core[i], false); + } + package->in_boost = false; + } else + cores_freq_trans_notify(package, false, true, + boost_count, 0, 0); +} + + +unsigned int load_update(struct core_data *core) +{ + int i; + u64 update_time, cur_idle_time; + unsigned int idle_time, time_elapsed; + unsigned int load = 0; + struct package_data *package = core->package; + + cur_idle_time = get_cpu_idle_time(core->cpu, &update_time, true); + + time_elapsed = update_time - core->prev_update_time; + core->prev_update_time = update_time; + + idle_time = cur_idle_time - core->prev_cpu_idle; + core->prev_cpu_idle = cur_idle_time; + + if (unlikely(!time_elapsed)) { + /* + * That can only happen when this function is called + * twice in a row with a very short interval between the + * calls, so the previous load value can be used then. + */ + load = core->prev_load; + } else if (unlikely((int)idle_time > 2 * core->sampling_rate && + core->prev_load)) { + + load = core->prev_load; + core->prev_load = 0; + } else { + if (time_elapsed >= idle_time) { + load = 100 * (time_elapsed - idle_time) / time_elapsed; + } else { + load = (int)idle_time < 0 ? 100 : 0; + } + core->prev_load = load; + } + + package->nr_full_load_cores = 0; + for (i = 0; i < package->nr_cores; i++) { + if (package->core[i].load > BOOST_THRESHOLD) { + package->nr_full_load_cores++; + } + } + + return load; +} + +static bool cpufreq_should_update_freq(struct core_data *core, u64 time) +{ + s64 delta_ns; + delta_ns = time - core->last_freq_update_time; + return delta_ns >= core->freq_update_delay_ns; +} + +static void cpufreq_update(struct cpufreq_policy *policy) +{ + int action; + struct core_data *core; + struct package_data *package; + unsigned long int load; + bool should_be_boost = 0; + + core = get_core_data(policy->cpu); + package = core->package; + + mutex_lock(&boost_mutex[core->package_id]); + + if (!core->update_util_set) { + mutex_unlock(&boost_mutex[core->package_id]); + return; + } + + load = load_update(core); + core->load = (u64)load + ((core->load * FACTOR) >> 32); + + if (cpufreq_boost_enabled()) { + should_be_boost = package_boost(package); + } else { + if (package->in_boost) + should_be_boost = false; + } + + action = (package->in_boost << 1) | should_be_boost; + switch (action) { + case FAA_NORMAL: + faa_normal(policy, load); + break; + case FAA_B2N: + faa_b2n(package); + break; + case FAA_N2B: + faa_n2b(package, core); + break; + case FAA_BOOST: + faa_boost(policy, load); + break; + } + mutex_unlock(&boost_mutex[core->package_id]); +} + +static void set_max_within_limits(struct cpufreq_policy *policy) +{ + struct core_data *core = get_core_data(policy->cpu); + /* + * policy->max <= cpu->pstate.max_freq indecates that + * the boost is disabled, so max freq is in normal range + * + * Skip performance policy with boost enabled!!! + * + * */ + if (policy->max <= (core->normal_max_freq * 1000)) { + mutex_lock(&boost_mutex[core->package_id]); + if (!loongson3_set_freq(core, policy->max, 0)) + pr_debug("Set cpu %d to performance mode under normal range.\n", policy->cpu); + mutex_unlock(&boost_mutex[core->package_id]); + } +} + +static void clear_update_util_hook(unsigned int cpu) +{ + struct core_data *core = get_core_data(cpu); + + if (!core->update_util_set) + return; + + cpufreq_remove_update_util_hook(cpu); + core->update_util_set = false; + synchronize_rcu(); +} + +static void update_util_handler(struct update_util_data *data, u64 time, + unsigned int flags) +{ + struct core_data *core = container_of(data, struct core_data, update_util); + + if (!cpufreq_should_update_freq(core, time)) + return; + if (!core->work_in_progress) { + core->last_freq_update_time = time; + core->work_in_progress = true; + irq_work_queue(&core->irq_work); + } +} +static void set_update_util_hook(unsigned int cpu) +{ + struct core_data *core = get_core_data(cpu); + if (core->update_util_set) + return; + + cpufreq_add_update_util_hook(cpu, &core->update_util, + update_util_handler); + core->update_util_set = true; +} +static int loongson3_cpufreq_set_policy(struct cpufreq_policy *policy) +{ + if (!policy->cpuinfo.max_freq) + return -ENODEV; + + if (policy->policy == CPUFREQ_POLICY_PERFORMANCE) { + clear_update_util_hook(policy->cpu); + set_max_within_limits(policy); + } else { + set_update_util_hook(policy->cpu); + } + + return 0; +} + +static int loongson3_cpufreq_verify_policy(struct cpufreq_policy_data *policy) +{ + cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, policy->cpuinfo.max_freq); + + return 0; +} + +static void set_boost_freq(bool has) +{ + cpufreq_has_boost_freq = has; +} + +static bool has_boost_freq(void) +{ + return cpufreq_has_boost_freq; +} + +static int compute_scale(int *shift, int dividor, int dividee) +{ + int i; + int result = 0; + int remainder = 0; + int scale_resolution = 8; + + result = dividor / dividee; + remainder = (dividor % dividee) * 10; + + for (i = 0; i < scale_resolution; i++) { + result = result * 10 + remainder / dividee; + remainder = (remainder % dividee) * 10; + *shift *= 10; + } + + return result; +} + +static void cpufreq_work_handler(struct kthread_work *work) +{ + struct core_data *core; + struct cpufreq_policy *policy; + + core = container_of(work, struct core_data, work); + policy = cpufreq_cpu_get_raw(core->cpu); + + if (policy) { + cpufreq_update(policy); + core->work_in_progress = false; + } +} + +static void cpufreq_irq_work(struct irq_work *irq_work) +{ + struct core_data *core = container_of(irq_work, struct core_data, irq_work); + kthread_queue_work(&cpufreq_worker, &core->work); +} + +static void cpufreq_kthread_stop(void) +{ + kthread_flush_worker(&cpufreq_worker); + kthread_stop(cpufreq_thread); +} +static int cpufreq_kthread_create(void) +{ + struct sched_attr attr = { + .size = sizeof(struct sched_attr), + .sched_policy = SCHED_DEADLINE, + .sched_flags = 0x10000000, + .sched_nice = 0, + .sched_priority = 0, + .sched_runtime = 1000000, + .sched_deadline = 10000000, + .sched_period = 10000000, + }; + int ret; + + kthread_init_worker(&cpufreq_worker); + cpufreq_thread = kthread_create(kthread_worker_fn, &cpufreq_worker, "lsfrq:%d", 0); + if (IS_ERR(cpufreq_thread)) { + return PTR_ERR(cpufreq_thread); + } + + ret = sched_setattr_nocheck(cpufreq_thread, &attr); + if (ret) { + kthread_stop(cpufreq_thread); + pr_warn("%s: failed to set SCHED_DEADLINE\n", __func__); + return ret; + } + + wake_up_process(cpufreq_thread); + + return 0; +} + +static int init_acpi(struct acpi_processor_performance *perf) +{ + int result = 0; + int i; + + perf->shared_type = 0; + perf->state_count = (max_freq_level - min_freq_level + 1) * (boost_gears + 1); + + perf->states = + kmalloc_array(perf->state_count, + sizeof(struct acpi_processor_px), + GFP_KERNEL); + + if (!perf->states) { + result = -ENOMEM; + return result; + } + + for (i = 0; i < perf->state_count; i++) { + perf->states[i].power = 0x3A98; + perf->states[i].transition_latency = 10000; + perf->states[i].bus_master_latency = 10000; + perf->states[i].status = (RESERVED_FREQ + i / (boost_gears + 1)); + perf->states[i].control = (RESERVED_FREQ + i / (boost_gears + 1)); + + switch (i % (boost_gears + 1)) { + case 0: + perf->states[i].core_frequency = (cpu_clock_freq / 1000000) * (8 - i / (boost_gears + 1)) / 8; + break; + case 1: + case 2: + case 3: + case 4: + perf->states[i].core_frequency = + boost_freqs[i % (boost_gears + 1)] * (8 - i / (boost_gears + 1)) / 8; + perf->states[i].control |= ((i % (boost_gears + 1)) << 8); + break; + default: + pr_info("file %s, line %d, i %d freq table error\n", __FILE__, __LINE__, i); + } + } + + return result; +} + +static int loongson3_cpufreq_cpu_init(struct cpufreq_policy *policy) +{ + unsigned int i; + struct acpi_processor_performance *perf; + struct cpufreq_frequency_table *freq_table; + struct core_data *core; + int package_id; + unsigned int cpu = policy->cpu; + unsigned int result = 0; + + perf = per_cpu_ptr(acpi_perf_data, cpu); + package_id = cpu_data[cpu].package; + core = get_core_data(cpu); + all_package_data[package_id].nr_cores = loongson_sysconf.cores_per_package; + all_package_data[package_id].max_boost_cores = max_boost_cores; + core->normal_max_freq = 0; + all_package_data[package_id].nr_full_load_cores = 0; + core->cpu = cpu; + core->work_in_progress = false; + core->last_freq_update_time = 0; + core->perf = perf; + core->package_id = package_id; + core->package = &all_package_data[package_id]; + + core->boost_freq = kmalloc_array(boost_gears + 1, sizeof(typeof(core->boost_freq)), GFP_KERNEL); + core->clock_scale = kmalloc_array(boost_gears + 1, sizeof(typeof(core->clock_scale)), GFP_KERNEL); + core->shift = kmalloc_array(boost_gears + 1, sizeof(typeof(core->shift)), GFP_KERNEL); + + for (i = 0; i < boost_gears + 1; i++) { + core->boost_freq[i] = boost_freqs[i]; + core->shift[i] = 1; + } + + if (!acpi_disabled) + result = acpi_processor_register_performance(perf, cpu); + else { + result = init_acpi(perf); + policy->shared_type = perf->shared_type; + } + + if (result) { + pr_info("CPU%d acpi_processor_register_performance failed.\n", cpu); + return result; + } + + for (i = 0; i < MAX_PACKAGES; i++) { + mutex_init(&boost_mutex[i]); + } + + /* capability check */ + if (perf->state_count <= 1) { + pr_debug("No P-States\n"); + result = -ENODEV; + goto err_unreg; + } + + freq_table = kcalloc(perf->state_count + 1, sizeof(*freq_table), + GFP_KERNEL); + if (!freq_table) { + result = -ENOMEM; + goto err_unreg; + } + + /* detect transition latency */ + policy->cpuinfo.transition_latency = 0; + for (i = 0; i < perf->state_count; i++) { + if ((perf->states[i].transition_latency * 1000) > + policy->cpuinfo.transition_latency) + policy->cpuinfo.transition_latency = + perf->states[i].transition_latency * 1000; + if (perf->states[i].control & LOONGSON_BOOST_FREQ_MASK) { + set_boost_freq(true); + } else { + if (perf->states[i].core_frequency > core->normal_max_freq) + core->normal_max_freq = perf->states[i].core_frequency; + } + } + + core->freq_update_delay_ns = policy->cpuinfo.transition_latency; + + for (i = 0; i < boost_gears + 1; i++) { + core->clock_scale[i] = compute_scale(&core->shift[i], boost_freqs[i], core->normal_max_freq); + pr_debug("file %s, line %d, boost_freqs[%d] %d, normal_max_freq %d, scale %d, shift %d\n", + __FILE__, __LINE__, i, boost_freqs[i], core->normal_max_freq, core->clock_scale[i], core->shift[i]); + } + + /* table init */ + for (i = 0; i < perf->state_count; i++) { + freq_table[i].driver_data = (perf->states[i].control & LOONGSON_BOOST_FREQ_MASK) >> 8; + if (freq_table[i].driver_data) + freq_table[i].flags |= CPUFREQ_BOOST_FREQ; + freq_table[i].frequency = + perf->states[i].core_frequency * 1000; + } + freq_table[i].frequency = CPUFREQ_TABLE_END; + policy->freq_table = freq_table; + perf->state = 0; + + /* add boost-attr if supported. */ + if (has_boost_freq() && boost_supported()) + loongson3_cpufreq_attr[1] = &cpufreq_freq_attr_scaling_boost_freqs; + + pr_info("CPU%u - ACPI performance management activated.\n", cpu); + for (i = 0; i < perf->state_count; i++) + pr_debug(" %cP%d: %d MHz, %d mW, %d uS %d level\n", + (i == perf->state ? '*' : ' '), i, + (u32) perf->states[i].core_frequency, + (u32) perf->states[i].power, + (u32) perf->states[i].transition_latency, + (u32) perf->states[i].control); + + /* + * the first call to ->target() should result in us actually + * writing something to the appropriate registers. + */ + policy->fast_switch_possible = false; + + init_irq_work(&core->irq_work, cpufreq_irq_work); + kthread_init_work(&core->work, cpufreq_work_handler); + core->sampling_rate = max_t(unsigned int, + CPUFREQ_SAMPLING_INTERVAL, + cpufreq_policy_transition_delay_us(policy)); + return result; + +err_unreg: + if (!acpi_disabled) + acpi_processor_unregister_performance(cpu); + + return result; +} + +static int loongson3_cpufreq_cpu_exit(struct cpufreq_policy *policy) +{ + struct core_data *core = get_core_data(policy->cpu); + clear_update_util_hook(policy->cpu); + irq_work_sync(&core->irq_work); + kthread_cancel_work_sync(&core->work); + core->work_in_progress = false; + policy->fast_switch_possible = false; + if (!acpi_disabled) + acpi_processor_unregister_performance(policy->cpu); + kfree(policy->freq_table); + kfree(core->boost_freq); + kfree(core->clock_scale); + kfree(core->shift); + return 0; +} + +static struct freq_attr *loongson3_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, /* Extra space for boost-attr if supported */ + NULL, +}; + +static struct cpufreq_driver loongson3_cpufreq_driver = { + .verify = loongson3_cpufreq_verify_policy, + .setpolicy = loongson3_cpufreq_set_policy, + .init = loongson3_cpufreq_cpu_init, + .exit = loongson3_cpufreq_cpu_exit, + .name = "acpi-cpufreq", + .attr = loongson3_cpufreq_attr, +}; + +static void free_acpi_perf_data(void) +{ + unsigned int i; + + /* Freeing a NULL pointer is OK, and alloc_percpu zeroes. */ + for_each_possible_cpu(i) + free_cpumask_var(per_cpu_ptr(acpi_perf_data, i) + ->shared_cpu_map); + free_percpu(acpi_perf_data); +} + +static int __init loongson3_cpufreq_early_init(void) +{ + unsigned int i; + pr_debug("acpi_cpufreq_early_init\n"); + + acpi_perf_data = alloc_percpu(struct acpi_processor_performance); + if (!acpi_perf_data) { + return -ENOMEM; + } + for_each_possible_cpu(i) { + if (!zalloc_cpumask_var_node( + &per_cpu_ptr(acpi_perf_data, i)->shared_cpu_map, + GFP_KERNEL, cpu_to_node(i))) { + free_acpi_perf_data(); + return -ENOMEM; + } + } + return 0; +} + +static bool support_boost(void) +{ + int message; + int val; + int i; + + if (wait_for_ready_timeout(MAX_READY_TIMEOUT)) + return false; + message = DVFS_INFO << 24; + iocsr_write32(message, 0x51c); + val = iocsr_read32(0x420); + + val |= 1 << 10; + iocsr_write32(val, 0x420); + if (wait_for_ready_timeout(MAX_READY_TIMEOUT)) { + pr_info("file %s, line %d, not support boost\n", __FILE__, __LINE__); + return false; + } + + val = iocsr_read32(0x51c); + + min_freq_level = val & DVFS_INFO_MIN_FREQ; + max_freq_level = (val & DVFS_INFO_MAX_FREQ) >> 4; + + if ((val & DVFS_INFO_BOOST_CORE_FREQ) && ((val & DVFS_INFO_BOOST_CORES) >> 20)) { + max_boost_cores = (val & DVFS_INFO_BOOST_CORES) >> 20; + max_boost_freq = ((val & DVFS_INFO_BOOST_CORE_FREQ) >> 8) * 25; + max_upper_index = (val & DVFS_INFO_NORMAL_CORE_UPPER_LIMIT) >> 16; + } else { + boost_gears = 0; + return false; + } + + /* Read boost levels */ + if (wait_for_ready_timeout(MAX_READY_TIMEOUT)) + return false; + + /* for version 1, single boost freq boost */ + message = DVFS_INFO_BOOST_LEVEL << 24; + iocsr_write32(message, 0x51c); + val = iocsr_read32(0x420); + + val |= 1 << 10; + iocsr_write32(val, 0x420); + + if (wait_for_ready_timeout(MAX_READY_TIMEOUT)) { + pr_info("file %s, line %d, single boost mode\n", __FILE__, __LINE__); + boost_gears = 1; + boost_freqs[0] = calc_const_freq() / 1000000; + for (i = 1; i < boost_gears + 1; i++) { + boost_freqs[i] = max_boost_freq; + } + + /* set 0x51c complete */ + iocsr_write32(COMPLETE_STATUS, 0x51c); + } else { + pr_info("file %s, line %d, multi boost mode\n", __FILE__, __LINE__); + boost_gears = max_boost_cores; + val = iocsr_read32(0x51c); + + boost_freqs[0] = calc_const_freq() / 1000000; + boost_freqs[1] = max_boost_freq; + + if (boost_gears > 1) { + for (i = 2; i < boost_gears + 1; i++) { + boost_freqs[i] = max_boost_freq - (((val >> ((i-2) * 4)) & 0xf) * FREQ_STEP); + } + } + } + + pr_info("file %s, line %d, min_freq_level %d, max_freq_level %d, max_boost_cores %d, boost_gears %d\n", + __FILE__, __LINE__, min_freq_level, max_freq_level, max_boost_cores, boost_gears); + + return true; +} + +static int cpufreq_table_cpuinfo(struct cpufreq_policy *policy, + struct cpufreq_frequency_table *table, + bool boost) +{ + struct cpufreq_frequency_table *pos; + unsigned int min_freq = ~0; + unsigned int max_freq = 0; + unsigned int freq; + + cpufreq_for_each_valid_entry(pos, table) { + freq = pos->frequency; + + if (!boost) { + if (pos->driver_data) + continue; + } + if (freq < min_freq) + min_freq = freq; + if (freq > max_freq) + max_freq = freq; + } + + policy->min = policy->cpuinfo.min_freq = min_freq; + policy->max = policy->cpuinfo.max_freq = max_freq; + if (policy->min == ~0) + return -EINVAL; + else + return 0; +} + +static int set_boost(struct cpufreq_policy *policy, int state) +{ + if (!has_boost_freq()) + return -EINVAL; + + if (!policy) + return -EINVAL; + + if (!state) { + if (policy->policy == CPUFREQ_POLICY_POWERSAVE) { + cpufreq_update(policy); + } + } + if (!policy->freq_table) + return -EINVAL; + + cpufreq_table_cpuinfo(policy, policy->freq_table, state); + down_write(&policy->rwsem); + up_write(&policy->rwsem); + + if (!state) { + set_max_within_limits(policy); + } + + + return 0; +} + +static void __init loongson3_cpufreq_boost_init(void) +{ + if (!support_boost()) { + pr_info("Boost capabilities not present in the processor\n"); + return; + } + + loongson3_cpufreq_driver.set_boost = set_boost; +} + +static int cpufreq_supported_detect(void) +{ + return wait_for_ready_timeout(MAX_READY_TIMEOUT); +} + +static int __init loongson3_cpufreq_init(void) +{ + int ret; +#ifdef CONFIG_LOONGARCH + if (!cpu_has_csr || !cpu_has_scalefreq) +#endif +#ifdef CONFIG_MIPS + if (!loongson_cpu_has_csr || !loongson_cpu_has_scalefreq) +#endif + return -ENODEV; + + /* don't keep reloading if cpufreq_driver exists */ + if (cpufreq_get_current_driver()) + return -EEXIST; + + pr_debug("loongson3_cpufreq_init\n"); + if (cpufreq_supported_detect()) { + pr_info("loongson3_cpufreq_init failed!\n"); + return -ENODEV; + } + + ret = loongson3_cpufreq_early_init(); + if (ret) + return ret; + loongson3_cpufreq_boost_init(); + + cpufreq_register_notifier(&loongson3_cpufreq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); + ret = cpufreq_register_driver(&loongson3_cpufreq_driver); + cpufreq_kthread_create(); + if (ret) { + free_acpi_perf_data(); + } + return ret; +} + +static void __exit loongson3_cpufreq_exit(void) +{ + pr_debug("loongson3_cpufreq_exit\n"); + + cpufreq_unregister_driver(&loongson3_cpufreq_driver); + free_acpi_perf_data(); + cpufreq_kthread_stop(); +} + +late_initcall(loongson3_cpufreq_init); +module_exit(loongson3_cpufreq_exit); + +static const struct acpi_device_id processor_device_ids[] = { + {ACPI_PROCESSOR_OBJECT_HID, }, + {ACPI_PROCESSOR_DEVICE_HID, }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, processor_device_ids); + +MODULE_ALIAS("acpi"); -- Gitee From f654825a643b619ad2cbb6bdecba97eb7d07ab83 Mon Sep 17 00:00:00 2001 From: Liu Peibao Date: Mon, 1 Aug 2022 09:23:27 +0800 Subject: [PATCH 15/56] loongarch: convert p v s cache to l1 l2 l3 format Change-Id: I0d38cd552c26b92a587465d3b5fb5e992f67ff9b Signed-off-by: Liu Peibao --- arch/loongarch/include/asm/cacheflush.h | 43 +--- arch/loongarch/include/asm/cacheops.h | 36 +-- arch/loongarch/include/asm/cpu-features.h | 5 - arch/loongarch/include/asm/cpu-info.h | 20 +- arch/loongarch/include/asm/loongarch.h | 7 + arch/loongarch/kernel/cacheinfo.c | 109 +++------ arch/loongarch/mm/cache.c | 274 +++++++++++++++------- arch/loongarch/pci/pci.c | 7 +- 8 files changed, 277 insertions(+), 224 deletions(-) diff --git a/arch/loongarch/include/asm/cacheflush.h b/arch/loongarch/include/asm/cacheflush.h index 670900141b7c..a9fa98b79405 100644 --- a/arch/loongarch/include/asm/cacheflush.h +++ b/arch/loongarch/include/asm/cacheflush.h @@ -9,8 +9,11 @@ #include #include -extern void local_flush_icache_range(unsigned long start, unsigned long end); +void local_flush_icache_range(unsigned long start, unsigned long end); +void flush_cache_line_hit(unsigned long addr); +asmlinkage void cpu_flush_caches(void); +#define invalid_cache_line_hit(addr) flush_cache_line_hit(addr) #define flush_icache_range local_flush_icache_range #define flush_icache_user_range local_flush_icache_range @@ -35,46 +38,26 @@ extern void local_flush_icache_range(unsigned long start, unsigned long end); : \ : "i" (op), "ZC" (*(unsigned char *)(addr))) -static inline void flush_icache_line_indexed(unsigned long addr) +static inline bool cache_present(struct cache_desc *cdesc) { - cache_op(Index_Invalidate_I, addr); + return cdesc->flags & CACHE_PRESENT; } -static inline void flush_dcache_line_indexed(unsigned long addr) +static inline bool cache_private(struct cache_desc *cdesc) { - cache_op(Index_Writeback_Inv_D, addr); + return cdesc->flags & CACHE_PRIVATE; } -static inline void flush_vcache_line_indexed(unsigned long addr) +static inline bool cache_inclusive(struct cache_desc *cdesc) { - cache_op(Index_Writeback_Inv_V, addr); + return cdesc->flags & CACHE_INCLUSIVE; } -static inline void flush_scache_line_indexed(unsigned long addr) +static inline unsigned int cpu_last_level_cache_line_size(void) { - cache_op(Index_Writeback_Inv_S, addr); -} + unsigned int cache_present = current_cpu_data.cache_leaves_present; -static inline void flush_icache_line(unsigned long addr) -{ - cache_op(Hit_Invalidate_I, addr); + return current_cpu_data.cache_leaves[cache_present - 1].linesz; } - -static inline void flush_dcache_line(unsigned long addr) -{ - cache_op(Hit_Writeback_Inv_D, addr); -} - -static inline void flush_vcache_line(unsigned long addr) -{ - cache_op(Hit_Writeback_Inv_V, addr); -} - -static inline void flush_scache_line(unsigned long addr) -{ - cache_op(Hit_Writeback_Inv_S, addr); -} - #include - #endif /* _ASM_CACHEFLUSH_H */ diff --git a/arch/loongarch/include/asm/cacheops.h b/arch/loongarch/include/asm/cacheops.h index dc280efecebd..0f4a86f8e2be 100644 --- a/arch/loongarch/include/asm/cacheops.h +++ b/arch/loongarch/include/asm/cacheops.h @@ -8,16 +8,18 @@ #define __ASM_CACHEOPS_H /* - * Most cache ops are split into a 2 bit field identifying the cache, and a 3 + * Most cache ops are split into a 3 bit field identifying the cache, and a 2 * bit field identifying the cache operation. */ -#define CacheOp_Cache 0x03 -#define CacheOp_Op 0x1c +#define CacheOp_Cache 0x07 +#define CacheOp_Op 0x18 -#define Cache_I 0x00 -#define Cache_D 0x01 -#define Cache_V 0x02 -#define Cache_S 0x03 +#define Cache_LEAF0 0x00 +#define Cache_LEAF1 0x01 +#define Cache_LEAF2 0x02 +#define Cache_LEAF3 0x03 +#define Cache_LEAF4 0x04 +#define Cache_LEAF5 0x05 #define Index_Invalidate 0x08 #define Index_Writeback_Inv 0x08 @@ -25,13 +27,17 @@ #define Hit_Writeback_Inv 0x10 #define CacheOp_User_Defined 0x18 -#define Index_Invalidate_I (Cache_I | Index_Invalidate) -#define Index_Writeback_Inv_D (Cache_D | Index_Writeback_Inv) -#define Index_Writeback_Inv_V (Cache_V | Index_Writeback_Inv) -#define Index_Writeback_Inv_S (Cache_S | Index_Writeback_Inv) -#define Hit_Invalidate_I (Cache_I | Hit_Invalidate) -#define Hit_Writeback_Inv_D (Cache_D | Hit_Writeback_Inv) -#define Hit_Writeback_Inv_V (Cache_V | Hit_Writeback_Inv) -#define Hit_Writeback_Inv_S (Cache_S | Hit_Writeback_Inv) +#define Index_Writeback_Inv_LEAF0 (Cache_LEAF0 | Index_Writeback_Inv) +#define Index_Writeback_Inv_LEAF1 (Cache_LEAF1 | Index_Writeback_Inv) +#define Index_Writeback_Inv_LEAF2 (Cache_LEAF2 | Index_Writeback_Inv) +#define Index_Writeback_Inv_LEAF3 (Cache_LEAF3 | Index_Writeback_Inv) +#define Index_Writeback_Inv_LEAF4 (Cache_LEAF4 | Index_Writeback_Inv) +#define Index_Writeback_Inv_LEAF5 (Cache_LEAF5 | Index_Writeback_Inv) +#define Hit_Writeback_Inv_LEAF0 (Cache_LEAF0 | Hit_Writeback_Inv) +#define Hit_Writeback_Inv_LEAF1 (Cache_LEAF1 | Hit_Writeback_Inv) +#define Hit_Writeback_Inv_LEAF2 (Cache_LEAF2 | Hit_Writeback_Inv) +#define Hit_Writeback_Inv_LEAF3 (Cache_LEAF3 | Hit_Writeback_Inv) +#define Hit_Writeback_Inv_LEAF4 (Cache_LEAF4 | Hit_Writeback_Inv) +#define Hit_Writeback_Inv_LEAF5 (Cache_LEAF5 | Hit_Writeback_Inv) #endif /* __ASM_CACHEOPS_H */ diff --git a/arch/loongarch/include/asm/cpu-features.h b/arch/loongarch/include/asm/cpu-features.h index a8d87c40a0eb..b07974218393 100644 --- a/arch/loongarch/include/asm/cpu-features.h +++ b/arch/loongarch/include/asm/cpu-features.h @@ -19,11 +19,6 @@ #define cpu_has_loongarch32 (cpu_data[0].isa_level & LOONGARCH_CPU_ISA_32BIT) #define cpu_has_loongarch64 (cpu_data[0].isa_level & LOONGARCH_CPU_ISA_64BIT) -#define cpu_icache_line_size() cpu_data[0].icache.linesz -#define cpu_dcache_line_size() cpu_data[0].dcache.linesz -#define cpu_vcache_line_size() cpu_data[0].vcache.linesz -#define cpu_scache_line_size() cpu_data[0].scache.linesz - #ifdef CONFIG_32BIT # define cpu_has_64bits (cpu_data[0].isa_level & LOONGARCH_CPU_ISA_64BIT) # define cpu_vabits 31 diff --git a/arch/loongarch/include/asm/cpu-info.h b/arch/loongarch/include/asm/cpu-info.h index b6c4f96079df..3fce27d92fcb 100644 --- a/arch/loongarch/include/asm/cpu-info.h +++ b/arch/loongarch/include/asm/cpu-info.h @@ -9,19 +9,28 @@ #include #include +/* cache_desc->flags */ +enum { + CACHE_PRESENT = (1 << 0), + CACHE_PRIVATE = (1 << 1), /* core private cache */ + CACHE_INCLUSIVE = (1 << 2), /* include the lower level caches */ +}; /* * Descriptor for a cache */ struct cache_desc { - unsigned int waysize; /* Bytes per way */ + unsigned char type; + unsigned char level; unsigned short sets; /* Number of lines per set */ unsigned char ways; /* Number of ways */ unsigned char linesz; /* Size of line in bytes */ - unsigned char waybit; /* Bits to select in a cache set */ unsigned char flags; /* Flags describing cache properties */ }; +#define CACHE_LEAVES_MAX 6 + +#define CACHE_LEVEL_MAX 3 struct cpuinfo_loongarch { u64 asid_cache; unsigned long asid_mask; @@ -40,11 +49,8 @@ struct cpuinfo_loongarch { int tlbsizemtlb; int tlbsizestlbsets; int tlbsizestlbways; - struct cache_desc icache; /* Primary I-cache */ - struct cache_desc dcache; /* Primary D or combined I/D cache */ - struct cache_desc vcache; /* Victim cache, between pcache and scache */ - struct cache_desc scache; /* Secondary cache */ - struct cache_desc tcache; /* Tertiary/split secondary cache */ + unsigned int cache_leaves_present; /* number of cache_leaves[] elements */ + struct cache_desc cache_leaves[CACHE_LEAVES_MAX]; int core; /* physical core number in package */ int package;/* physical package number */ int vabits; /* Virtual Address size in bits */ diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h index 3ba4f7e87cd2..7ead7a1f8aa0 100644 --- a/arch/loongarch/include/asm/loongarch.h +++ b/arch/loongarch/include/asm/loongarch.h @@ -224,6 +224,13 @@ static inline u32 read_cpucfg(u32 reg) #define CPUCFG48_VFPU_CG BIT(2) #define CPUCFG48_RAM_CG BIT(3) +#define CACHE_WAYS_M GENMASK(15, 0) +#define CACHE_SETS_M GENMASK(23, 16) +#define CACHE_LSIZE_M GENMASK(30, 24) +#define CACHE_WAYS 0 +#define CACHE_SETS 16 +#define CACHE_LSIZE 24 + #ifndef __ASSEMBLY__ /* CSR */ diff --git a/arch/loongarch/kernel/cacheinfo.c b/arch/loongarch/kernel/cacheinfo.c index 4662b06269f4..0d97a970ad4e 100644 --- a/arch/loongarch/kernel/cacheinfo.c +++ b/arch/loongarch/kernel/cacheinfo.c @@ -5,69 +5,28 @@ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited */ #include +#include #include #include -/* Populates leaf and increments to next leaf */ -#define populate_cache(cache, leaf, c_level, c_type) \ -do { \ - leaf->type = c_type; \ - leaf->level = c_level; \ - leaf->coherency_line_size = c->cache.linesz; \ - leaf->number_of_sets = c->cache.sets; \ - leaf->ways_of_associativity = c->cache.ways; \ - leaf->size = c->cache.linesz * c->cache.sets * \ - c->cache.ways; \ - if (leaf->level > 2) \ - leaf->size *= nodes_per_package; \ - leaf++; \ -} while (0) - int init_cache_level(unsigned int cpu) { - struct cpuinfo_loongarch *c = ¤t_cpu_data; struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); - int levels = 0, leaves = 0; - - /* - * If Dcache is not set, we assume the cache structures - * are not properly initialized. - */ - if (c->dcache.waysize) - levels += 1; - else - return -ENOENT; - - - leaves += (c->icache.waysize) ? 2 : 1; - - if (c->vcache.waysize) { - levels++; - leaves++; - } - - if (c->scache.waysize) { - levels++; - leaves++; - } - - if (c->tcache.waysize) { - levels++; - leaves++; - } - - this_cpu_ci->num_levels = levels; - this_cpu_ci->num_leaves = leaves; + unsigned int cache_present = current_cpu_data.cache_leaves_present; + this_cpu_ci->num_levels = + current_cpu_data.cache_leaves[cache_present - 1].level; + this_cpu_ci->num_leaves = cache_present; return 0; } static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf, - struct cacheinfo *sib_leaf) + struct cacheinfo *sib_leaf) { - return !((this_leaf->level == 1) || (this_leaf->level == 2)); + return (!(*(unsigned char *)(this_leaf->priv) & CACHE_PRIVATE) && + !(*(unsigned char *)(sib_leaf->priv) & CACHE_PRIVATE)); } -static void cache_cpumap_setup(unsigned int cpu) +static void __cache_cpumap_setup(unsigned int cpu) { struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); struct cacheinfo *this_leaf, *sib_leaf; @@ -85,8 +44,11 @@ static void cache_cpumap_setup(unsigned int cpu) for_each_online_cpu(i) { struct cpu_cacheinfo *sib_cpu_ci = get_cpu_cacheinfo(i); - if (i == cpu || !sib_cpu_ci->info_list) - continue;/* skip if itself or no cacheinfo */ + /* skip if itself or no cacheinfo or not in one + * physical node. */ + if (i == cpu || !sib_cpu_ci->info_list || + (cpu_to_node(i) != cpu_to_node(cpu))) + continue; sib_leaf = sib_cpu_ci->info_list + index; if (cache_leaves_are_shared(this_leaf, sib_leaf)) { cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map); @@ -98,33 +60,30 @@ static void cache_cpumap_setup(unsigned int cpu) int populate_cache_leaves(unsigned int cpu) { - int level = 1, nodes_per_package = 1; - struct cpuinfo_loongarch *c = ¤t_cpu_data; + struct cache_desc *cdesc_tmp, *cdesc = current_cpu_data.cache_leaves; + unsigned int cache_present = current_cpu_data.cache_leaves_present; struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); struct cacheinfo *this_leaf = this_cpu_ci->info_list; - - if (loongson_sysconf.nr_nodes > 1) - nodes_per_package = loongson_sysconf.cores_per_package - / loongson_sysconf.cores_per_node; - - if (c->icache.waysize) { - populate_cache(dcache, this_leaf, level, CACHE_TYPE_DATA); - populate_cache(icache, this_leaf, level++, CACHE_TYPE_INST); - } else { - populate_cache(dcache, this_leaf, level++, CACHE_TYPE_UNIFIED); + int i; + + for (i = 0; i < cache_present; i++) { + cdesc_tmp = cdesc + i; + + this_leaf->type = cdesc_tmp->type; + this_leaf->level = cdesc_tmp->level; + this_leaf->coherency_line_size = cdesc_tmp->linesz; + this_leaf->number_of_sets = cdesc_tmp->sets; + this_leaf->ways_of_associativity = cdesc_tmp->ways; + this_leaf->size = + cdesc_tmp->linesz * cdesc_tmp->sets * cdesc_tmp->ways; + this_leaf->priv = &cdesc_tmp->flags; + this_leaf++; } - if (c->vcache.waysize) - populate_cache(vcache, this_leaf, level++, CACHE_TYPE_UNIFIED); - - if (c->scache.waysize) - populate_cache(scache, this_leaf, level++, CACHE_TYPE_UNIFIED); - - if (c->tcache.waysize) - populate_cache(tcache, this_leaf, level++, CACHE_TYPE_UNIFIED); - - cache_cpumap_setup(cpu); - this_cpu_ci->cpu_map_populated = true; + if (!of_have_populated_dt()) { + __cache_cpumap_setup(cpu); + this_cpu_ci->cpu_map_populated = true; + } return 0; } diff --git a/arch/loongarch/mm/cache.c b/arch/loongarch/mm/cache.c index 9e5ce5aa73f7..e08ce053d969 100644 --- a/arch/loongarch/mm/cache.c +++ b/arch/loongarch/mm/cache.c @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2020-2022 Loongson Technology Corporation Limited - * - * Derived from MIPS: - * Copyright (C) 1994 - 2003, 06, 07 by Ralf Baechle (ralf@linux-mips.org) - * Copyright (C) 2007 MIPS Technologies, Inc. - */ +* Copyright (C) 2020-2022 Loongson Technology Corporation Limited +* +* Derived from MIPS: +* Copyright (C) 1994 - 2003, 06, 07 by Ralf Baechle (ralf@linux-mips.org) +* Copyright (C) 2007 MIPS Technologies, Inc. +*/ #include #include #include @@ -15,127 +15,225 @@ #include #include #include +#include -#include #include +#include #include #include #include #include #include +extern struct loongson_system_configuration loongson_sysconf; +void cache_error_setup(void) +{ + extern char __weak except_vec_cex; + set_merr_handler(0x0, &except_vec_cex, 0x80); +} -/* - * LoongArch maintains ICache/DCache coherency by hardware, - * we just need "ibar" to avoid instruction hazard here. - */ +/* Cache operations. */ void local_flush_icache_range(unsigned long start, unsigned long end) { asm volatile ("\tibar 0\n"::); } -EXPORT_SYMBOL(local_flush_icache_range); -void cache_error_setup(void) +static inline void __flush_cache_line_hit(int leaf, unsigned long addr) { - extern char __weak except_vec_cex; - set_merr_handler(0x0, &except_vec_cex, 0x80); + switch (leaf) { + case Cache_LEAF0: + cache_op(Hit_Writeback_Inv_LEAF0, addr); + break; + case Cache_LEAF1: + cache_op(Hit_Writeback_Inv_LEAF1, addr); + break; + case Cache_LEAF2: + cache_op(Hit_Writeback_Inv_LEAF2, addr); + break; + case Cache_LEAF3: + cache_op(Hit_Writeback_Inv_LEAF3, addr); + break; + case Cache_LEAF4: + cache_op(Hit_Writeback_Inv_LEAF4, addr); + break; + case Cache_LEAF5: + cache_op(Hit_Writeback_Inv_LEAF5, addr); + break; + default: + break; + } } -static unsigned long icache_size __read_mostly; -static unsigned long dcache_size __read_mostly; -static unsigned long vcache_size __read_mostly; -static unsigned long scache_size __read_mostly; +static inline void __flush_cache_line_indexed(int leaf, unsigned long addr) +{ + switch (leaf) { + case Cache_LEAF0: + cache_op(Index_Writeback_Inv_LEAF0, addr); + break; + case Cache_LEAF1: + cache_op(Index_Writeback_Inv_LEAF1, addr); + break; + case Cache_LEAF2: + cache_op(Index_Writeback_Inv_LEAF2, addr); + break; + case Cache_LEAF3: + cache_op(Index_Writeback_Inv_LEAF3, addr); + break; + case Cache_LEAF4: + cache_op(Index_Writeback_Inv_LEAF4, addr); + break; + case Cache_LEAF5: + cache_op(Index_Writeback_Inv_LEAF5, addr); + break; + default: + break; + } +} -static char *way_string[] = { NULL, "direct mapped", "2-way", - "3-way", "4-way", "5-way", "6-way", "7-way", "8-way", - "9-way", "10-way", "11-way", "12-way", - "13-way", "14-way", "15-way", "16-way", -}; +void flush_cache_line_hit(unsigned long addr) +{ + int leaf; + struct cache_desc *cdesc = current_cpu_data.cache_leaves; + unsigned int cache_present = current_cpu_data.cache_leaves_present; + + /* If last level cache is inclusive, no need to flush other caches. */ + leaf = cache_present - 1; + if (cache_inclusive(cdesc + leaf)) { + __flush_cache_line_hit(leaf, addr); + return; + } + + for (leaf = 0; leaf < cache_present; leaf++) + __flush_cache_line_hit(leaf, addr); +} -static void probe_pcache(void) +static void flush_cache_leaf(unsigned int leaf) +{ + u64 line; + int i, j, nr_nodes; + struct cache_desc *cdesc = current_cpu_data.cache_leaves + leaf; + + nr_nodes = loongson_sysconf.nr_nodes; + if (cache_private(cdesc)) + nr_nodes = 1; + + line = CSR_DMW0_BASE; + do { + for (i = 0; i < cdesc->sets; i++) { + for (j = 0; j < cdesc->ways; j++) { + __flush_cache_line_indexed(leaf, line); + line++; + } + + line -= cdesc->ways; + line += cdesc->linesz; + } + line += 0x100000000000; + } while (--nr_nodes > 0); +} + +asmlinkage __visible void cpu_flush_caches(void) +{ + int leaf; + struct cache_desc *cdesc = current_cpu_data.cache_leaves; + unsigned int cache_present = current_cpu_data.cache_leaves_present; + + /* If last level cache is inclusive, no need to flush other caches. */ + leaf = cache_present - 1; + if (cache_inclusive(cdesc + leaf)) { + flush_cache_leaf(leaf); + return; + } + + for (leaf = 0; leaf < cache_present; leaf++) + flush_cache_leaf(leaf); +} + +static inline void set_cache_basics(struct cache_desc *cdesc, unsigned int leaf) { - struct cpuinfo_loongarch *c = ¤t_cpu_data; - unsigned int lsize, sets, ways; unsigned int config; - config = read_cpucfg(LOONGARCH_CPUCFG17); - lsize = 1 << ((config & CPUCFG17_L1I_SIZE_M) >> CPUCFG17_L1I_SIZE); - sets = 1 << ((config & CPUCFG17_L1I_SETS_M) >> CPUCFG17_L1I_SETS); - ways = ((config & CPUCFG17_L1I_WAYS_M) >> CPUCFG17_L1I_WAYS) + 1; + config = read_cpucfg(LOONGARCH_CPUCFG17 + leaf); + cdesc->linesz = 1 << ((config & CACHE_LSIZE_M) >> CACHE_LSIZE); + cdesc->sets = 1 << ((config & CACHE_SETS_M) >> CACHE_SETS); + cdesc->ways = ((config & CACHE_WAYS_M) >> CACHE_WAYS) + 1; +} - c->icache.linesz = lsize; - c->icache.sets = sets; - c->icache.ways = ways; - icache_size = sets * ways * lsize; - c->icache.waysize = icache_size / c->icache.ways; +#define populate_cache_properties(conifg, cdesc, level, leaf) \ +{ \ + if (level == 1) { \ + cdesc->flags |= CACHE_PRIVATE; \ + } else { \ + if (config & IUPRIV) \ + cdesc->flags |= CACHE_PRIVATE; \ + if (config & IUINCL) \ + cdesc->flags |= CACHE_INCLUSIVE; \ + } \ + cdesc->flags |= CACHE_PRESENT; \ + cdesc->level = level; \ + set_cache_basics(cdesc, leaf); \ + cdesc++; \ + leaf++; \ +} - config = read_cpucfg(LOONGARCH_CPUCFG18); - lsize = 1 << ((config & CPUCFG18_L1D_SIZE_M) >> CPUCFG18_L1D_SIZE); - sets = 1 << ((config & CPUCFG18_L1D_SETS_M) >> CPUCFG18_L1D_SETS); - ways = ((config & CPUCFG18_L1D_WAYS_M) >> CPUCFG18_L1D_WAYS) + 1; +/* +* Each level cache occupies 7bits in order in CPUCFG16 +* except level 1 cache with bit0~2. +* +*/ +static void probe_cache_hierarchy(void) +{ + struct cache_desc *cdesc = current_cpu_data.cache_leaves; + unsigned int leaf = 0, level; + unsigned int config = read_cpucfg(LOONGARCH_CPUCFG16); - c->dcache.linesz = lsize; - c->dcache.sets = sets; - c->dcache.ways = ways; - dcache_size = sets * ways * lsize; - c->dcache.waysize = dcache_size / c->dcache.ways; +#define IUPRE (1 << 0) +#define IUUNIFY (1 << 1) +#define IUPRIV (1 << 2) +#define IUINCL (1 << 3) +#define DPRE (1 << 4) +#define DPRIV (1 << 5) +#define DINCL (1 << 6) - c->options |= LOONGARCH_CPU_PREFETCH; +#define L1DPRE (1 << 2) - pr_info("Primary instruction cache %ldkB, %s, %s, linesize %d bytes.\n", - icache_size >> 10, way_string[c->icache.ways], "VIPT", c->icache.linesz); + for (level = 1; level <= CACHE_LEVEL_MAX; level++) { + if (config & IUPRE) { + if (config & IUUNIFY) + cdesc->type = CACHE_TYPE_UNIFIED; + else + cdesc->type = CACHE_TYPE_INST; - pr_info("Primary data cache %ldkB, %s, %s, %s, linesize %d bytes\n", - dcache_size >> 10, way_string[c->dcache.ways], "VIPT", "no aliases", c->dcache.linesz); -} + populate_cache_properties(config, cdesc, level, leaf); + } -static void probe_vcache(void) -{ - struct cpuinfo_loongarch *c = ¤t_cpu_data; - unsigned int lsize, sets, ways; - unsigned int config; + if ((level == 1 && (config & L1DPRE)) || + (level != 1 && (config & DPRE))) { + cdesc->type = CACHE_TYPE_DATA; - config = read_cpucfg(LOONGARCH_CPUCFG19); - lsize = 1 << ((config & CPUCFG19_L2_SIZE_M) >> CPUCFG19_L2_SIZE); - sets = 1 << ((config & CPUCFG19_L2_SETS_M) >> CPUCFG19_L2_SETS); - ways = ((config & CPUCFG19_L2_WAYS_M) >> CPUCFG19_L2_WAYS) + 1; + populate_cache_properties(config, cdesc, level, leaf); + } - c->vcache.linesz = lsize; - c->vcache.sets = sets; - c->vcache.ways = ways; - vcache_size = lsize * sets * ways; - c->vcache.waysize = vcache_size / c->vcache.ways; + if (level == 1) + config = config >> 3; + else + config = config >> 7; - pr_info("Unified victim cache %ldkB %s, linesize %d bytes.\n", - vcache_size >> 10, way_string[c->vcache.ways], c->vcache.linesz); -} + if (!config) + break; -static void probe_scache(void) -{ - struct cpuinfo_loongarch *c = ¤t_cpu_data; - unsigned int lsize, sets, ways; - unsigned int config; + } - config = read_cpucfg(LOONGARCH_CPUCFG20); - lsize = 1 << ((config & CPUCFG20_L3_SIZE_M) >> CPUCFG20_L3_SIZE); - sets = 1 << ((config & CPUCFG20_L3_SETS_M) >> CPUCFG20_L3_SETS); - ways = ((config & CPUCFG20_L3_WAYS_M) >> CPUCFG20_L3_WAYS) + 1; + if (leaf > 0) + current_cpu_data.options |= LOONGARCH_CPU_PREFETCH; - c->scache.linesz = lsize; - c->scache.sets = sets; - c->scache.ways = ways; - /* 4 cores. scaches are shared */ - scache_size = lsize * sets * ways; - c->scache.waysize = scache_size / c->scache.ways; + BUG_ON(leaf > CACHE_LEAVES_MAX); - pr_info("Unified secondary cache %ldkB %s, linesize %d bytes.\n", - scache_size >> 10, way_string[c->scache.ways], c->scache.linesz); + current_cpu_data.cache_leaves_present = leaf; } void cpu_cache_init(void) { - probe_pcache(); - probe_vcache(); - probe_scache(); + probe_cache_hierarchy(); shm_align_mask = PAGE_SIZE - 1; } diff --git a/arch/loongarch/pci/pci.c b/arch/loongarch/pci/pci.c index 4456fafc243a..a552b9da72ec 100644 --- a/arch/loongarch/pci/pci.c +++ b/arch/loongarch/pci/pci.c @@ -10,6 +10,7 @@ #include #include #include +#include #define PCI_DEVICE_ID_LOONGSON_HOST 0x7a00 #define PCI_DEVICE_ID_LOONGSON_DC1 0x7a06 @@ -45,12 +46,10 @@ static int __init pcibios_init(void) unsigned int lsize; /* - * Set PCI cacheline size to that of the highest level in the + * Set PCI cacheline size to that of the last level in the * cache hierarchy. */ - lsize = cpu_dcache_line_size(); - lsize = cpu_vcache_line_size() ? : lsize; - lsize = cpu_scache_line_size() ? : lsize; + lsize = cpu_last_level_cache_line_size(); BUG_ON(!lsize); -- Gitee From e780f9f96d2fe08c482af9de4722e1dece238f3a Mon Sep 17 00:00:00 2001 From: Tianli Xiong Date: Tue, 13 Sep 2022 20:59:07 +0800 Subject: [PATCH 16/56] Fix patch "PCI: Check if entry->offset already exist for mem resource" Signed-off-by: Tianli Xiong Change-Id: Idd676753ee5f3af9f745a406cdebbe6935a65276 --- arch/loongarch/pci/acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/loongarch/pci/acpi.c b/arch/loongarch/pci/acpi.c index f7bfc3986ec6..06a33cbf76d2 100644 --- a/arch/loongarch/pci/acpi.c +++ b/arch/loongarch/pci/acpi.c @@ -210,7 +210,7 @@ static int acpi_prepare_root_resources(struct acpi_pci_root_info *ci) if (status > 0) { resource_list_for_each_entry_safe(entry, tmp, &ci->resources) { if (entry->res->flags & IORESOURCE_MEM) { - if(!entry_>offset) { + if(!entry->offset) { entry->offset = ci->root->mcfg_addr & GENMASK_ULL(63, 40); entry->res->start |= entry->offset; entry->res->end |= entry->offset; -- Gitee From 276d5031894a7cf2aee15cb018fd31a580f352b1 Mon Sep 17 00:00:00 2001 From: Ming Wang Date: Tue, 13 Sep 2022 21:05:29 +0800 Subject: [PATCH 17/56] defconfig: use make defconfig to save a clean defconfig Signed-off-by: Ming Wang Change-Id: I29c788f28847452f94cad139ad69d7f066ca940d --- arch/loongarch/configs/loongson3_defconfig | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index 36680ff00455..f106ada24a97 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -43,6 +43,10 @@ CONFIG_ACPI_SPCR_TABLE=y CONFIG_ACPI_DOCK=y CONFIG_ACPI_IPMI=m CONFIG_ACPI_PCI_SLOT=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_LOONGSON3_ACPI_CPUFREQ=y CONFIG_EFI_CAPSULE_LOADER=m CONFIG_EFI_TEST=m CONFIG_MODULES=y @@ -796,8 +800,3 @@ CONFIG_STRIP_ASM_SYMS=y CONFIG_MAGIC_SYSRQ=y # CONFIG_SCHED_DEBUG is not set CONFIG_SCHEDSTATS=y -CONFIG_CPU_FREQ=y -CONFIG_CPU_FREQ_STAT=y -CONFIG_CPU_FREQ_GOV_POWERSAVE=y -CONFIG_CPU_FREQ=y -CONFIG_LOONGSON3_ACPI_CPUFREQ=y -- Gitee From fff3c0baba96017089fc1af53c7b4e9476852d1f Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Sat, 3 Sep 2022 17:34:53 +0800 Subject: [PATCH 18/56] rtc: ls2x: Add support for the Loongson-2K/LS7A RTC This RTC module is integrated into the Loongson-2K SoC and the LS7A bridge chip. This version is almost entirely rewritten to make use of current kernel API, and it supports both ACPI and DT. This patch also make CONFIG_RTC_DRV_EFI=m. The purpose of this is to make rtc-ls2x device to /dev/rtc0. Signed-off-by: Huacai Chen Signed-off-by: WANG Xuerui Signed-off-by: Binbin Zhou Cc: Alexandre Belloni Cc: "Rafael J. Wysocki" Cc: devicetree@vger.kernel.org Cc: linux-acpi@vger.kernel.org Cc: linux-mips@vger.kernel.org Cc: loongarch@lists.linux.dev Signed-off-by: Ming Wang Change-Id: If950a9b210fd1ffb32a2d4d7f0ad6c1fb5058303 --- arch/loongarch/configs/loongson3_defconfig | 3 +- drivers/rtc/Kconfig | 11 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-ls2x.c | 325 +++++++++++++++++++++ 4 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 drivers/rtc/rtc-ls2x.c diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index f106ada24a97..32dc7672e22a 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -656,7 +656,8 @@ CONFIG_USB_SERIAL_OPTION=m CONFIG_USB_GADGET=y CONFIG_INFINIBAND=m CONFIG_RTC_CLASS=y -CONFIG_RTC_DRV_EFI=y +CONFIG_RTC_DRV_EFI=m +CONFIG_RTC_DRV_LS2X=y CONFIG_DMADEVICES=y CONFIG_UIO=m CONFIG_UIO_PDRV_GENIRQ=m diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index d5512b18a3ae..1d005a001a71 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1320,6 +1320,17 @@ config RTC_DRV_CROS_EC This driver can also be built as a module. If so, the module will be called rtc-cros-ec. +config RTC_DRV_LS2X + tristate "Loongson LS2X RTC" + depends on (ACPI || OF) && MACH_LOONGSON64 || COMPILE_TEST + select REGMAP_MMIO + help + If you say yes here you get support for the RTC on the Loongson-2K + SoC and LS7A bridge, which first appeared on the Loongson-2H. + + This driver can also be built as a module. If so, the module + will be called rtc-ls2x. + comment "on-CPU RTC drivers" config RTC_DRV_ASM9260 diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index fd1d53e789b7..ec0bed7fd4e4 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -88,6 +88,7 @@ obj-$(CONFIG_RTC_DRV_LOONGSON1) += rtc-ls1x.o obj-$(CONFIG_RTC_DRV_LP8788) += rtc-lp8788.o obj-$(CONFIG_RTC_DRV_LPC24XX) += rtc-lpc24xx.o obj-$(CONFIG_RTC_DRV_LPC32XX) += rtc-lpc32xx.o +obj-$(CONFIG_RTC_DRV_LS2X) += rtc-ls2x.o obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o obj-$(CONFIG_RTC_DRV_M41T93) += rtc-m41t93.o obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o diff --git a/drivers/rtc/rtc-ls2x.c b/drivers/rtc/rtc-ls2x.c new file mode 100644 index 000000000000..962fec171154 --- /dev/null +++ b/drivers/rtc/rtc-ls2x.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Loongson-2K/7A RTC driver + * + * Based on the original out-of-tree Loongson-2H RTC driver for Linux 2.6.32, + * by Shaozong Liu . + * + * Maintained out-of-tree by Huacai Chen . + * + * Rewritten for mainline by WANG Xuerui . + */ + +#include +#include +#include +#include +#include +#include +#include + +#define TOY_TRIM_REG 0x20 +#define TOY_WRITE0_REG 0x24 +#define TOY_WRITE1_REG 0x28 +#define TOY_READ0_REG 0x2c +#define TOY_READ1_REG 0x30 +#define TOY_MATCH0_REG 0x34 +#define TOY_MATCH1_REG 0x38 +#define TOY_MATCH2_REG 0x3c +#define RTC_CTRL_REG 0x40 +#define RTC_TRIM_REG 0x60 +#define RTC_WRITE0_REG 0x64 +#define RTC_READ0_REG 0x68 +#define RTC_MATCH0_REG 0x6c +#define RTC_MATCH1_REG 0x70 +#define RTC_MATCH2_REG 0x74 + +#define TOY_MON GENMASK(31, 26) +#define TOY_DAY GENMASK(25, 21) +#define TOY_HOUR GENMASK(20, 16) +#define TOY_MIN GENMASK(15, 10) +#define TOY_SEC GENMASK(9, 4) +#define TOY_MSEC GENMASK(3, 0) + +#define TOY_MATCH_YEAR GENMASK(31, 26) +#define TOY_MATCH_MON GENMASK(25, 22) +#define TOY_MATCH_DAY GENMASK(21, 17) +#define TOY_MATCH_HOUR GENMASK(16, 12) +#define TOY_MATCH_MIN GENMASK(11, 6) +#define TOY_MATCH_SEC GENMASK(5, 0) + +/* ACPI and RTC offset */ +#define ACPI_RTC_OFFSET 0x100 + +/* support rtc wakeup */ +#define ACPI_PM1_STS_REG 0x0c +#define ACPI_PM1_EN_REG 0x10 +#define RTC_EN BIT(10) +#define RTC_STS BIT(10) + +struct ls2x_rtc_priv { + struct regmap *regmap; + spinlock_t rtc_reglock; + void __iomem *acpi_base; + struct rtc_device *rtcdev; +}; + +static const struct regmap_config ls2x_rtc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +struct ls2x_rtc_regs { + u32 reg0; + u32 reg1; +}; + +#if defined(CONFIG_ACPI) +static u32 ls2x_acpi_fix_handler(void *id) +{ + int ret; + struct ls2x_rtc_priv *priv = (struct ls2x_rtc_priv *)id; + + spin_lock(&priv->rtc_reglock); + + /* Disable acpi rtc enabled */ + ret = readl(priv->acpi_base + ACPI_PM1_EN_REG) & ~RTC_EN; + writel(ret, priv->acpi_base + ACPI_PM1_EN_REG); + + /* Clear acpi rtc interrupt Status */ + writel(RTC_STS, priv->acpi_base + ACPI_PM1_STS_REG); + + spin_unlock(&priv->rtc_reglock); + + /* + * The TOY_MATCH0_REG should be cleared 0 here, + * otherwise the interrupt cannot be cleared. + * Because the match condition is still satisfied + */ + ret = regmap_write(priv->regmap, TOY_MATCH0_REG, 0); + if (unlikely(ret)) + return ret; + + return 0; +} +#endif + +static inline void ls2x_rtc_regs_to_time(struct ls2x_rtc_regs *regs, + struct rtc_time *tm) +{ + tm->tm_year = regs->reg1; + tm->tm_sec = FIELD_GET(TOY_SEC, regs->reg0); + tm->tm_min = FIELD_GET(TOY_MIN, regs->reg0); + tm->tm_hour = FIELD_GET(TOY_HOUR, regs->reg0); + tm->tm_mday = FIELD_GET(TOY_DAY, regs->reg0); + tm->tm_mon = FIELD_GET(TOY_MON, regs->reg0) - 1; +} + +static inline void ls2x_rtc_time_to_regs(struct rtc_time *tm, + struct ls2x_rtc_regs *regs) +{ + regs->reg0 = FIELD_PREP(TOY_SEC, tm->tm_sec); + regs->reg0 |= FIELD_PREP(TOY_MIN, tm->tm_min); + regs->reg0 |= FIELD_PREP(TOY_HOUR, tm->tm_hour); + regs->reg0 |= FIELD_PREP(TOY_DAY, tm->tm_mday); + regs->reg0 |= FIELD_PREP(TOY_MON, tm->tm_mon + 1); + regs->reg1 = tm->tm_year; +} + +static inline void ls2x_rtc_alarm_regs_to_time(struct ls2x_rtc_regs *regs, + struct rtc_time *tm) +{ + tm->tm_sec = FIELD_GET(TOY_MATCH_SEC, regs->reg0); + tm->tm_min = FIELD_GET(TOY_MATCH_MIN, regs->reg0); + tm->tm_hour = FIELD_GET(TOY_MATCH_HOUR, regs->reg0); + tm->tm_mday = FIELD_GET(TOY_MATCH_DAY, regs->reg0); + tm->tm_mon = FIELD_GET(TOY_MATCH_MON, regs->reg0) - 1; + /* + * The rtc SYS_TOYMATCH0/YEAR bit field is only 6 bits, + * so it means 63 years at most. Therefore, The RTC alarm + * years can be set from 1900 to 1963. + * This causes the initialization of alarm fail during + * call __rtc_read_alarm. We add 64 years offset to + * ls2x_rtc_read_alarm. After adding the offset, + * the RTC alarm clock can be set from 1964 to 2027. + */ + tm->tm_year = FIELD_GET(TOY_MATCH_YEAR, regs->reg0) + 64; +} + +static inline void ls2x_rtc_time_to_alarm_regs(struct rtc_time *tm, + struct ls2x_rtc_regs *regs) +{ + regs->reg0 = FIELD_PREP(TOY_MATCH_SEC, tm->tm_sec); + regs->reg0 |= FIELD_PREP(TOY_MATCH_MIN, tm->tm_min); + regs->reg0 |= FIELD_PREP(TOY_MATCH_HOUR, tm->tm_hour); + regs->reg0 |= FIELD_PREP(TOY_MATCH_DAY, tm->tm_mday); + regs->reg0 |= FIELD_PREP(TOY_MATCH_MON, tm->tm_mon + 1); + regs->reg0 |= FIELD_PREP(TOY_MATCH_YEAR, tm->tm_year); +} + +static int ls2x_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + int ret; + struct ls2x_rtc_regs regs; + struct ls2x_rtc_priv *priv = dev_get_drvdata(dev); + + ret = regmap_read(priv->regmap, TOY_READ1_REG, ®s.reg1); + if (unlikely(ret)) + return ret; + + ret = regmap_read(priv->regmap, TOY_READ0_REG, ®s.reg0); + if (unlikely(ret)) + return ret; + + ls2x_rtc_regs_to_time(®s, tm); + + return 0; +} + +static int ls2x_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + int ret; + struct ls2x_rtc_regs regs; + struct ls2x_rtc_priv *priv = dev_get_drvdata(dev); + + ls2x_rtc_time_to_regs(tm, ®s); + + ret = regmap_write(priv->regmap, TOY_WRITE0_REG, regs.reg0); + if (unlikely(ret)) + return ret; + + return regmap_write(priv->regmap, TOY_WRITE1_REG, regs.reg1); +} + +static int ls2x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + int ret; + struct ls2x_rtc_regs regs; + struct ls2x_rtc_priv *priv = dev_get_drvdata(dev); + + ret = regmap_read(priv->regmap, TOY_MATCH0_REG, ®s.reg0); + if (unlikely(ret)) + return ret; + + ls2x_rtc_alarm_regs_to_time(®s, &alrm->time); + +#if defined(CONFIG_ACPI) + ret = readl(priv->acpi_base + ACPI_PM1_EN_REG); + alrm->enabled = !!(ret & RTC_EN); +#endif + + return 0; +} + +static int ls2x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct ls2x_rtc_regs regs; + struct ls2x_rtc_priv *priv = dev_get_drvdata(dev); + + ls2x_rtc_time_to_alarm_regs(&alrm->time, ®s); + + return regmap_write(priv->regmap, TOY_MATCH0_REG, regs.reg0); +} + +static struct rtc_class_ops ls2x_rtc_ops = { + .read_time = ls2x_rtc_read_time, + .set_time = ls2x_rtc_set_time, + .read_alarm = ls2x_rtc_read_alarm, + .set_alarm = ls2x_rtc_set_alarm, +}; + +static int ls2x_rtc_probe(struct platform_device *pdev) +{ + int ret; + void __iomem *regs; + struct ls2x_rtc_priv *priv; + struct device *dev = &pdev->dev; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (unlikely(!priv)) + return -ENOMEM; + + spin_lock_init(&priv->rtc_reglock); + + platform_set_drvdata(pdev, priv); + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + priv->regmap = devm_regmap_init_mmio(dev, regs, + &ls2x_rtc_regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->rtcdev = devm_rtc_allocate_device(dev); + if (IS_ERR(priv->rtcdev)) + return PTR_ERR(priv->rtcdev); + + /* Due to hardware erratum, all years multiple of 4 are considered + * leap year, so only years 2000 through 2099 are usable. + * + * Previous out-of-tree versions of this driver wrote tm_year directly + * into the year register, so epoch 2000 must be used to preserve + * semantics on shipped systems. + */ + priv->rtcdev->range_min = RTC_TIMESTAMP_BEGIN_2000; + priv->rtcdev->range_max = RTC_TIMESTAMP_END_2099; + priv->rtcdev->ops = &ls2x_rtc_ops; + +#ifdef CONFIG_ACPI + priv->acpi_base = regs - ACPI_RTC_OFFSET; + acpi_install_fixed_event_handler(ACPI_EVENT_RTC, + ls2x_acpi_fix_handler, priv); +#endif + + if (!device_can_wakeup(&pdev->dev)) + device_init_wakeup(dev, 1); + + ret = rtc_register_device(priv->rtcdev); + if (unlikely(ret)) + return ret; + + /* An offset of -0.9s will call RTC set for wall clock time 10.0 s at 10.9 s */ + priv->rtcdev->set_offset_nsec = -900000000; + + /* If not cause hwclock huang */ + priv->rtcdev->uie_unsupported = 1; + + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id ls2x_rtc_of_match[] = { + { .compatible = "loongson,ls2x-rtc" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ls2x_rtc_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id ls2x_rtc_acpi_match[] = { + {"LOON0001"}, + {} +}; +MODULE_DEVICE_TABLE(acpi, ls2x_rtc_acpi_match); +#endif + +static struct platform_driver ls2x_rtc_driver = { + .probe = ls2x_rtc_probe, + .driver = { + .name = "ls2x-rtc", + .of_match_table = of_match_ptr(ls2x_rtc_of_match), + .acpi_match_table = ACPI_PTR(ls2x_rtc_acpi_match), + }, +}; + +module_platform_driver(ls2x_rtc_driver); + +MODULE_DESCRIPTION("LS2X RTC driver"); +MODULE_AUTHOR("WANG Xuerui"); +MODULE_AUTHOR("Huacai Chen"); +MODULE_AUTHOR("Binbin Zhou"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ls2x-rtc"); -- Gitee From e27b1c1332e5c404507a3d15543f993274505b4e Mon Sep 17 00:00:00 2001 From: Juxin Gao Date: Thu, 8 Sep 2022 16:32:52 +0800 Subject: [PATCH 19/56] gpio: loongson: Add 3A/3B/3C/7A gpio dirver support Change-Id: Ib1adc61f5279bba8020f26acc32a4de4dee95df5 Signed-off-by: Juxin Gao --- drivers/gpio/Kconfig | 3 +- drivers/gpio/gpio-loongson.c | 413 ++++++++++++++++++++++++++++------- 2 files changed, 340 insertions(+), 76 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index c91355c89ec6..c9401840422a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -370,7 +370,8 @@ config GPIO_LOGICVC config GPIO_LOONGSON bool "Loongson-2/3 GPIO support" - depends on CPU_LOONGSON2EF || CPU_LOONGSON64 + depends on CPU_LOONGSON2EF || CPU_LOONGSON64 || LOONGARCH + default m help driver for GPIO functionality on Loongson-2F/3A/3B processors. diff --git a/drivers/gpio/gpio-loongson.c b/drivers/gpio/gpio-loongson.c index a42145873cc9..217243027a94 100644 --- a/drivers/gpio/gpio-loongson.c +++ b/drivers/gpio/gpio-loongson.c @@ -1,13 +1,13 @@ -// SPDX-License-Identifier: GPL-2.0-or-later /* - * Loongson-2F/3A/3B GPIO Support + * Loongson-3A/3B/3C/7A GPIO Support * - * Copyright (c) 2008 Richard Liu, STMicroelectronics - * Copyright (c) 2008-2010 Arnaud Patard - * Copyright (c) 2013 Hongbing Hu - * Copyright (c) 2014 Huacai Chen + * 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. */ +#include #include #include #include @@ -17,119 +17,382 @@ #include #include #include -#include -#define STLS2F_N_GPIO 4 -#define STLS3A_N_GPIO 16 +/* ============== Data structrues =============== */ -#ifdef CONFIG_CPU_LOONGSON64 -#define LOONGSON_N_GPIO STLS3A_N_GPIO -#else -#define LOONGSON_N_GPIO STLS2F_N_GPIO -#endif +/* gpio data */ +struct platform_gpio_data { + u32 gpio_conf; + u32 gpio_out; + u32 gpio_in; + u32 in_start_bit; + u32 support_irq; + char *label; + int gpio_base; + int ngpio; +}; + +#define GPIO_IO_CONF(x) (x->base + x->conf_offset) +#define GPIO_OUT(x) (x->base + x->out_offset) +#define GPIO_IN(x) (x->base + x->in_offset) + +#define LS7A_GPIO_OEN_BYTE(x, gpio) (x->base + x->conf_offset + gpio) +#define LS7A_GPIO_OUT_BYTE(x, gpio) (x->base + x->out_offset + gpio) +#define LS7A_GPIO_IN_BYTE(x, gpio) (x->base + x->in_offset + gpio) + +struct loongson_gpio_chip { + struct gpio_chip chip; + spinlock_t lock; + void __iomem *base; + int conf_offset; + int out_offset; + int in_offset; + int in_start_bit; + u16 *gsi_idx_map; + u16 mapsize; + bool support_irq; +}; /* - * Offset into the register where we read lines, we write them from offset 0. - * This offset is the only thing that stand between us and using - * GPIO_GENERIC. + * GPIO primitives. */ -#define LOONGSON_GPIO_IN_OFFSET 16 +static int loongson_gpio_request(struct gpio_chip *chip, unsigned pin) +{ + if (pin >= chip->ngpio) + return -EINVAL; + else + return 0; +} + +static inline void +__set_direction(struct loongson_gpio_chip *lgpio, unsigned pin, int input) +{ + u64 temp; + u8 value; -static DEFINE_SPINLOCK(gpio_lock); + if (!strcmp(lgpio->chip.label, "loongson,loongson3-gpio") || + !strncmp(lgpio->chip.label, "LOON0007", 8)) { + temp = readq(GPIO_IO_CONF(lgpio)); + if (input) + temp |= 1ULL << pin; + else + temp &= ~(1ULL << pin); + writeq(temp, GPIO_IO_CONF(lgpio)); + return ; + } + if (!strcmp(lgpio->chip.label,"loongson,ls7a-gpio") || + !strncmp(lgpio->chip.label, "LOON0002", 8)){ + if (input) + value = 1; + else + value = 0; + writeb(value, LS7A_GPIO_OEN_BYTE(lgpio, pin)); + return ; + } +} -static int loongson_gpio_get_value(struct gpio_chip *chip, unsigned gpio) +static void __set_level(struct loongson_gpio_chip *lgpio, unsigned pin, int high) { - u32 val; + u64 temp; + u8 value; - spin_lock(&gpio_lock); - val = LOONGSON_GPIODATA; - spin_unlock(&gpio_lock); + /* If GPIO controller is on 3A,then... */ + if (!strcmp(lgpio->chip.label, "loongson,loongson3-gpio") || + !strncmp(lgpio->chip.label, "LOON0007", 8)) { + temp = readq(GPIO_OUT(lgpio)); + if (high) + temp |= 1ULL << pin; + else + temp &= ~(1ULL << pin); + writeq(temp, GPIO_OUT(lgpio)); + return; + } - return !!(val & BIT(gpio + LOONGSON_GPIO_IN_OFFSET)); + if (!strcmp(lgpio->chip.label,"loongson,ls7a-gpio") || + !strncmp(lgpio->chip.label,"LOON0002", 8)){ + if (high) + value = 1; + else + value = 0; + writeb(value, LS7A_GPIO_OUT_BYTE(lgpio, pin)); + return; + } } -static void loongson_gpio_set_value(struct gpio_chip *chip, - unsigned gpio, int value) +static int loongson_gpio_direction_input(struct gpio_chip *chip, unsigned pin) { - u32 val; + unsigned long flags; + struct loongson_gpio_chip *lgpio = + container_of(chip, struct loongson_gpio_chip, chip); - spin_lock(&gpio_lock); - val = LOONGSON_GPIODATA; - if (value) - val |= BIT(gpio); - else - val &= ~BIT(gpio); - LOONGSON_GPIODATA = val; - spin_unlock(&gpio_lock); + spin_lock_irqsave(&lgpio->lock, flags); + __set_direction(lgpio, pin, 1); + spin_unlock_irqrestore(&lgpio->lock, flags); + + return 0; } -static int loongson_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) +static int loongson_gpio_direction_output(struct gpio_chip *chip, + unsigned pin, int value) { - u32 temp; + struct loongson_gpio_chip *lgpio = + container_of(chip, struct loongson_gpio_chip, chip); + unsigned long flags; - spin_lock(&gpio_lock); - temp = LOONGSON_GPIOIE; - temp |= BIT(gpio); - LOONGSON_GPIOIE = temp; - spin_unlock(&gpio_lock); + spin_lock_irqsave(&lgpio->lock, flags); + __set_level(lgpio, pin, value); + __set_direction(lgpio, pin, 0); + spin_unlock_irqrestore(&lgpio->lock, flags); return 0; } -static int loongson_gpio_direction_output(struct gpio_chip *chip, - unsigned gpio, int level) +static int loongson_gpio_get(struct gpio_chip *chip, unsigned pin) { - u32 temp; + struct loongson_gpio_chip *lgpio = + container_of(chip, struct loongson_gpio_chip, chip); + u64 temp; + u8 value; - loongson_gpio_set_value(chip, gpio, level); - spin_lock(&gpio_lock); - temp = LOONGSON_GPIOIE; - temp &= ~BIT(gpio); - LOONGSON_GPIOIE = temp; - spin_unlock(&gpio_lock); + /* GPIO controller in 3A is different for 7A */ + if (!strcmp(lgpio->chip.label, "loongson,loongson3-gpio") || + !strncmp(lgpio->chip.label, "LOON0007", 8)) { + temp = readq(GPIO_IN(lgpio)); + return ((temp & (1ULL << (pin + lgpio->in_start_bit))) != 0); + } + + if (!strcmp(lgpio->chip.label,"loongson,ls7a-gpio") || + !strncmp(lgpio->chip.label, "LOON0002", 8)){ + value = readb(LS7A_GPIO_IN_BYTE(lgpio, pin)); + return (value & 1); + } + + return -ENXIO; +} + +static void loongson_gpio_set(struct gpio_chip *chip, unsigned pin, int value) +{ + struct loongson_gpio_chip *lgpio = + container_of(chip, struct loongson_gpio_chip, chip); + unsigned long flags; + + spin_lock_irqsave(&lgpio->lock, flags); + __set_level(lgpio, pin, value); + spin_unlock_irqrestore(&lgpio->lock, flags); +} + +static int loongson_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) +{ + struct platform_device *pdev = + container_of(chip->parent, struct platform_device, dev); + struct loongson_gpio_chip *lgpio = + container_of(chip, struct loongson_gpio_chip, chip); + + if (offset >= chip->ngpio) + return -EINVAL; + + if ((lgpio->gsi_idx_map != NULL) && (offset < lgpio->mapsize)) + offset = lgpio->gsi_idx_map[offset]; + + return platform_get_irq(pdev, offset); +} + +static int loongson_gpio_init(struct device *dev, struct loongson_gpio_chip *lgpio, struct device_node *np, + void __iomem *base) +{ + lgpio->chip.request = loongson_gpio_request; + lgpio->chip.direction_input = loongson_gpio_direction_input; + lgpio->chip.get = loongson_gpio_get; + lgpio->chip.direction_output = loongson_gpio_direction_output; + lgpio->chip.set = loongson_gpio_set; + lgpio->chip.can_sleep = 0; + lgpio->chip.of_node = np; + lgpio->chip.parent = dev; + spin_lock_init(&lgpio->lock); + lgpio->base = (void __iomem *)base; + + if (!strcmp(lgpio->chip.label, "loongson,ls7a-gpio") || + !strncmp(lgpio->chip.label, "LOON0002", 8) || + !strcmp(lgpio->chip.label, "loongson,loongson3-gpio") || + !strncmp(lgpio->chip.label, "LOON0007", 8)) { + + lgpio->chip.to_irq = loongson_gpio_to_irq; + } + gpiochip_add(&lgpio->chip); return 0; } + +static void of_loongson_gpio_get_props(struct device_node *np, + struct loongson_gpio_chip *lgpio) +{ + const char *name; + + of_property_read_u32(np, "ngpios", (u32 *)&lgpio->chip.ngpio); + of_property_read_u32(np, "gpio_base", (u32 *)&lgpio->chip.base); + of_property_read_u32(np, "conf_offset", (u32 *)&lgpio->conf_offset); + of_property_read_u32(np, "out_offset", (u32 *)&lgpio->out_offset); + of_property_read_u32(np, "in_offset", (u32 *)&lgpio->in_offset); + of_property_read_string(np, "compatible", &name); + if (!strcmp(name, "loongson,loongson3-gpio")) { + of_property_read_u32(np, "in_start_bit", + (u32 *)&lgpio->in_start_bit); + if (of_property_read_bool(np, "support_irq")) + lgpio->support_irq = true; + } + lgpio->chip.label = kstrdup(name, GFP_KERNEL); +} + +static void acpi_loongson_gpio_get_props(struct platform_device *pdev, + struct loongson_gpio_chip *lgpio) +{ + + struct device *dev = &pdev->dev; + int rval; + + device_property_read_u32(dev, "ngpios", (u32 *)&lgpio->chip.ngpio); + device_property_read_u32(dev, "gpio_base", (u32 *)&lgpio->chip.base); + device_property_read_u32(dev, "conf_offset", (u32 *)&lgpio->conf_offset); + device_property_read_u32(dev, "out_offset", (u32 *)&lgpio->out_offset); + device_property_read_u32(dev, "in_offset", (u32 *)&lgpio->in_offset); + rval = device_property_read_u16_array(dev, "gsi_idx_map", NULL, 0); + if (rval > 0) { + lgpio->gsi_idx_map = + kmalloc_array(rval, sizeof(*lgpio->gsi_idx_map), + GFP_KERNEL); + if (unlikely(!lgpio->gsi_idx_map)) { + dev_err(dev, "Alloc gsi_idx_map fail!\n"); + } else { + lgpio->mapsize = rval; + device_property_read_u16_array(dev, "gsi_idx_map", + lgpio->gsi_idx_map, lgpio->mapsize); + } + } + if (!strcmp(pdev->name, "LOON0007")) { + device_property_read_u32(dev, "in_start_bit", + (u32 *)&lgpio->in_start_bit); + if (device_property_read_bool(dev, "support_irq")) + lgpio->support_irq = true; + } + lgpio->chip.label = kstrdup(pdev->name, GFP_KERNEL); +} + +static void platform_loongson_gpio_get_props(struct platform_device *pdev, + struct loongson_gpio_chip *lgpio) +{ + struct platform_gpio_data *gpio_data = + (struct platform_gpio_data *)pdev->dev.platform_data; + + lgpio->chip.ngpio = gpio_data->ngpio; + lgpio->chip.base = gpio_data->gpio_base; + lgpio->conf_offset = gpio_data->gpio_conf; + lgpio->out_offset = gpio_data->gpio_out; + lgpio->in_offset = gpio_data->gpio_in; + if (!strcmp(gpio_data->label, "loongson,loongson3-gpio")) { + lgpio->in_start_bit = gpio_data->in_start_bit; + lgpio->support_irq = gpio_data->support_irq; + } + lgpio->chip.label = kstrdup(gpio_data->label, GFP_KERNEL); +} + static int loongson_gpio_probe(struct platform_device *pdev) { - struct gpio_chip *gc; + struct resource *iores; + void __iomem *base; + struct loongson_gpio_chip *lgpio; + struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; + int ret = 0; - gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); - if (!gc) + lgpio = kzalloc(sizeof(struct loongson_gpio_chip), GFP_KERNEL); + if (!lgpio) return -ENOMEM; - gc->label = "loongson-gpio-chip"; - gc->base = 0; - gc->ngpio = LOONGSON_N_GPIO; - gc->get = loongson_gpio_get_value; - gc->set = loongson_gpio_set_value; - gc->direction_input = loongson_gpio_direction_input; - gc->direction_output = loongson_gpio_direction_output; + if (np){ + of_loongson_gpio_get_props(np,lgpio); + } else if (ACPI_COMPANION(&pdev->dev)) { + acpi_loongson_gpio_get_props(pdev,lgpio); + } else { + platform_loongson_gpio_get_props(pdev,lgpio); + } + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iores) { + ret = -ENODEV; + goto out; + } + if (!request_mem_region(iores->start, resource_size(iores), + pdev->name)) { + ret = -EBUSY; + goto out; + } + base = ioremap(iores->start, resource_size(iores)); + if (!base) { + ret = -ENOMEM; + goto out; + } + platform_set_drvdata(pdev, lgpio); + loongson_gpio_init(dev,lgpio, np, base); - return gpiochip_add_data(gc, NULL); + return 0; +out: + pr_err("%s: %s: missing mandatory property\n", __func__, np->name); + return ret; } -static struct platform_driver loongson_gpio_driver = { +static int loongson_gpio_remove(struct platform_device *pdev) +{ + struct loongson_gpio_chip *lgpio = platform_get_drvdata(pdev); + struct resource *mem; + + platform_set_drvdata(pdev, NULL); + gpiochip_remove(&lgpio->chip); + iounmap(lgpio->base); + kfree(lgpio->gsi_idx_map); + kfree(lgpio); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, resource_size(mem)); + return 0; +} + +static const struct of_device_id loongson_gpio_dt_ids[] = { + { .compatible = "loongson,loongson3-gpio"}, + { .compatible = "loongson,ls7a-gpio"}, + {} +}; +MODULE_DEVICE_TABLE(of, loongson_gpio_dt_ids); + +static const struct acpi_device_id loongson_gpio_acpi_match[] = { + {"LOON0002"}, + {"LOON0007"}, + {} +}; +MODULE_DEVICE_TABLE(acpi, loongson_gpio_acpi_match); + +static struct platform_driver ls_gpio_driver = { .driver = { .name = "loongson-gpio", + .owner = THIS_MODULE, + .of_match_table = loongson_gpio_dt_ids, + .acpi_match_table = ACPI_PTR(loongson_gpio_acpi_match), }, .probe = loongson_gpio_probe, + .remove = loongson_gpio_remove, }; static int __init loongson_gpio_setup(void) { - struct platform_device *pdev; - int ret; - - ret = platform_driver_register(&loongson_gpio_driver); - if (ret) { - pr_err("error registering loongson GPIO driver\n"); - return ret; - } + return platform_driver_register(&ls_gpio_driver); +} +subsys_initcall(loongson_gpio_setup); - pdev = platform_device_register_simple("loongson-gpio", -1, NULL, 0); - return PTR_ERR_OR_ZERO(pdev); +static void __exit loongson_gpio_driver(void) +{ + platform_driver_unregister(&ls_gpio_driver); } -postcore_initcall(loongson_gpio_setup); +module_exit(loongson_gpio_driver); +MODULE_AUTHOR("Loongson Technology Corporation Limited"); +MODULE_DESCRIPTION("LOONGSON GPIO"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:loongson_gpio"); -- Gitee From de78ad1818d0d570604bda9f17287116e49c9886 Mon Sep 17 00:00:00 2001 From: Tianli Xiong Date: Tue, 13 Sep 2022 10:22:49 +0800 Subject: [PATCH 20/56] LoongArch: pci root bridige set acpi companion only when not acpi_disabled. Fix patch "LoongArch: Add PCI controller support" Signed-off-by: Tianli Xiong Change-Id: If6084f05cc42ac33b44a7b6aeabdd1366a0bccdd --- arch/loongarch/pci/acpi.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/arch/loongarch/pci/acpi.c b/arch/loongarch/pci/acpi.c index 06a33cbf76d2..0f227d9ae764 100644 --- a/arch/loongarch/pci/acpi.c +++ b/arch/loongarch/pci/acpi.c @@ -26,12 +26,14 @@ void pcibios_add_bus(struct pci_bus *bus) int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) { - struct pci_config_window *cfg = bridge->bus->sysdata; - struct acpi_device *adev = to_acpi_device(cfg->parent); - struct device *bus_dev = &bridge->bus->dev; + if (!acpi_disabled) { + struct pci_config_window *cfg = bridge->bus->sysdata; + struct acpi_device *adev = to_acpi_device(cfg->parent); + struct device *bus_dev = &bridge->bus->dev; - ACPI_COMPANION_SET(&bridge->dev, adev); - set_dev_node(bus_dev, pa_to_nid(cfg->res.start)); + ACPI_COMPANION_SET(&bridge->dev, adev); + set_dev_node(bus_dev, pa_to_nid(cfg->res.start)); + } return 0; } -- Gitee From 70a50987870066c28a5362ed8c7484761e22848a Mon Sep 17 00:00:00 2001 From: Tianli Xiong Date: Mon, 19 Sep 2022 09:59:50 +0800 Subject: [PATCH 21/56] Loongson: Add Loongson HDA controller support Signed-off-by: Huacai Chen Signed-off-by: Tianli Xiong Change-Id: I50df86b20b7e9ba5e8a128b2c317e00dad2d52b5 --- arch/loongarch/configs/loongson3_defconfig | 1 + sound/hda/hdac_bus.c | 6 +- sound/hda/hdac_controller.c | 65 +- sound/hda/hdac_stream.c | 61 +- sound/pci/hda/Kconfig | 11 + sound/pci/hda/Makefile | 2 + sound/pci/hda/hda_controller.c | 43 +- sound/pci/hda/hda_controller.h | 5 +- sound/pci/hda/hda_loongson.c | 987 +++++++++++++++++++++ sound/pci/hda/patch_realtek.c | 12 +- 10 files changed, 1164 insertions(+), 29 deletions(-) create mode 100644 sound/pci/hda/hda_loongson.c diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index 32dc7672e22a..a45ece42c6df 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -610,6 +610,7 @@ CONFIG_SND_SEQ_DUMMY=m CONFIG_SND_BT87X=m CONFIG_SND_BT87X_OVERCLOCK=y CONFIG_SND_HDA_INTEL=y +CONFIG_SND_HDA_LOONGSON=m CONFIG_SND_HDA_HWDEP=y CONFIG_SND_HDA_INPUT_BEEP=y CONFIG_SND_HDA_PATCH_LOADER=y diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c index 9766f6af8743..4f08768b5770 100644 --- a/sound/hda/hdac_bus.c +++ b/sound/hda/hdac_bus.c @@ -11,6 +11,7 @@ #include #include "local.h" #include "trace.h" +#include "../pci/hda/hda_controller.h" static void snd_hdac_bus_process_unsol_events(struct work_struct *work); @@ -108,6 +109,7 @@ int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr, { unsigned int tmp; int err; + struct azx *chip = bus_to_azx(bus); if (cmd == ~0) return -EINVAL; @@ -116,7 +118,9 @@ int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr, *res = -1; else if (bus->sync_write) res = &tmp; - for (;;) { + if (chip->driver_caps & AZX_DCAPS_LOONGSON_HDA_WORKAROUND) + err = bus->ops->command(bus, cmd); + else for (;;) { trace_hda_send_cmd(bus, cmd); err = bus->ops->command(bus, cmd); if (err != -EAGAIN) diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index 522d1897659c..73c0d561209b 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -10,6 +10,7 @@ #include #include #include "local.h" +#include "../pci/hda/hda_controller.h" /* clear CORB read pointer properly */ static void azx_clear_corbrp(struct hdac_bus *bus) @@ -42,6 +43,8 @@ static void azx_clear_corbrp(struct hdac_bus *bus) */ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus) { + struct azx *chip = bus_to_azx(bus); + WARN_ON_ONCE(!bus->rb.area); spin_lock_irq(&bus->reg_lock); @@ -58,11 +61,15 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus) /* reset the corb hw read pointer */ snd_hdac_chip_writew(bus, CORBRP, AZX_CORBRP_RST); - if (!bus->corbrp_self_clear) + if (chip->driver_caps & AZX_DCAPS_LOONGSON_HDA_WORKAROUND) + snd_hdac_chip_writew(bus, CORBRP, 0); + else if (!bus->corbrp_self_clear) azx_clear_corbrp(bus); /* enable corb dma */ snd_hdac_chip_writeb(bus, CORBCTL, AZX_CORBCTL_RUN); + if (chip->driver_caps & AZX_DCAPS_LOONGSON_HDA_WORKAROUND) + snd_hdac_chip_readb(bus, CORBCTL); /* RIRB set up */ bus->rirb.addr = bus->rb.addr + 2048; @@ -79,7 +86,12 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus) /* set N=1, get RIRB response interrupt for new entry */ snd_hdac_chip_writew(bus, RINTCNT, 1); /* enable rirb dma and response irq */ - snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN); + if (chip->driver_caps & AZX_DCAPS_LOONGSON_HDA_WORKAROUND) { + snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN); + snd_hdac_chip_readb(bus, RIRBCTL); + } + else + snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN); /* Accept unsolicited responses */ snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL); spin_unlock_irq(&bus->reg_lock); @@ -132,6 +144,18 @@ static unsigned int azx_command_addr(u32 cmd) return addr; } +static unsigned int azx_response_addr(u32 res) +{ + unsigned int addr = res & 0xf; + + if (addr >= AZX_MAX_CODECS) { + snd_BUG(); + addr = 0; + } + + return addr; +} + /** * snd_hdac_bus_send_cmd - send a command verb via CORB * @bus: HD-audio core bus @@ -207,13 +231,8 @@ void snd_hdac_bus_update_rirb(struct hdac_bus *bus) rp = bus->rirb.rp << 1; /* an RIRB entry is 8-bytes */ res_ex = le32_to_cpu(bus->rirb.buf[rp + 1]); res = le32_to_cpu(bus->rirb.buf[rp]); - addr = res_ex & 0xf; - if (addr >= HDA_MAX_CODECS) { - dev_err(bus->dev, - "spurious response %#x:%#x, rp = %d, wp = %d", - res, res_ex, bus->rirb.rp, wp); - snd_BUG(); - } else if (res_ex & AZX_RIRB_EX_UNSOL_EV) + addr = azx_response_addr(res_ex); + if (res_ex & AZX_RIRB_EX_UNSOL_EV) snd_hdac_bus_queue_event(bus, res, res_ex); else if (bus->rirb.cmds[addr]) { bus->rirb.res[addr] = res; @@ -241,6 +260,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_update_rirb); int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr, unsigned int *res) { + struct azx *chip = bus_to_azx(bus); unsigned long timeout; unsigned long loopcounter; wait_queue_entry_t wait; @@ -254,8 +274,11 @@ int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr, if (!bus->polling_mode) prepare_to_wait(&bus->rirb_wq, &wait, TASK_UNINTERRUPTIBLE); - if (bus->polling_mode) + if (bus->polling_mode) { + if (chip->driver_caps & AZX_DCAPS_LOONGSON_HDA_WORKAROUND) + bus->rirb.cmds[addr] %= AZX_MAX_RIRB_ENTRIES; snd_hdac_bus_update_rirb(bus); + } if (!bus->rirb.cmds[addr]) { if (res) *res = bus->rirb.res[addr]; /* the last value */ @@ -485,16 +508,24 @@ static void azx_int_disable(struct hdac_bus *bus) static void azx_int_clear(struct hdac_bus *bus) { struct hdac_stream *azx_dev; + struct azx *chip = bus_to_azx(bus); /* clear stream status */ - list_for_each_entry(azx_dev, &bus->stream_list, list) - snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); + list_for_each_entry(azx_dev, &bus->stream_list, list) { + if (chip->driver_caps & AZX_DCAPS_LOONGSON_HDA_WORKAROUND) + snd_hdac_stream_updateb(azx_dev, SD_STS, 0, 0); + else + snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); + } /* clear STATESTS */ snd_hdac_chip_writew(bus, STATESTS, STATESTS_INT_MASK); /* clear rirb status */ - snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK); + if (chip->driver_caps & AZX_DCAPS_LOONGSON_HDA_WORKAROUND) + snd_hdac_chip_updateb(bus, RIRBSTS, ~RIRB_INT_MASK, 0); + else + snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK); /* clear int status */ snd_hdac_chip_writel(bus, INTSTS, AZX_INT_CTRL_EN | AZX_INT_ALL_STREAM); @@ -575,11 +606,17 @@ int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, struct hdac_stream *azx_dev; u8 sd_status; int handled = 0; + struct azx *chip = bus_to_azx(bus); list_for_each_entry(azx_dev, &bus->stream_list, list) { if (status & azx_dev->sd_int_sta_mask) { sd_status = snd_hdac_stream_readb(azx_dev, SD_STS); - snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); + if (chip->driver_caps & AZX_DCAPS_LOONGSON_HDA_WORKAROUND) { + snd_hdac_stream_writeb(azx_dev, SD_STS, sd_status); + snd_hdac_stream_readb(azx_dev, SD_STS); + } + else + snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); handled |= 1 << azx_dev->index; if (!azx_dev->substream || !azx_dev->running || !(sd_status & SD_INT_COMPLETE)) diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index ce77a5320163..ec716f9f63c6 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -12,6 +12,7 @@ #include #include #include "trace.h" +#include "../pci/hda/hda_controller.h" /** * snd_hdac_get_stream_stripe_ctl - get stripe control value @@ -83,12 +84,13 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_init); void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start) { struct hdac_bus *bus = azx_dev->bus; + struct azx *chip = bus_to_azx(bus); int stripe_ctl; trace_snd_hdac_stream_start(bus, azx_dev); azx_dev->start_wallclk = snd_hdac_chip_readl(bus, WALLCLK); - if (!fresh_start) + if (!fresh_start && !(chip->driver_caps & AZX_DCAPS_LOONGSON_HDA_WORKAROUND)) azx_dev->start_wallclk -= azx_dev->period_wallclk; /* enable SIE */ @@ -105,7 +107,14 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start) stripe_ctl); } /* set DMA start and interrupt mask */ - snd_hdac_stream_updateb(azx_dev, SD_CTL, + if (chip->driver_caps & AZX_DCAPS_LOONGSON_HDA_WORKAROUND) { + snd_hdac_stream_updatel(azx_dev, SD_CTL, + 0, SD_CTL_DMA_START | SD_INT_MASK); + snd_hdac_stream_updatel(azx_dev, SD_CTL, SD_CTL_STREAM_TAG_MASK, + azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT); + } + else + snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_DMA_START | SD_INT_MASK); azx_dev->running = true; } @@ -117,9 +126,28 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_start); */ void snd_hdac_stream_clear(struct hdac_stream *azx_dev) { - snd_hdac_stream_updateb(azx_dev, SD_CTL, + int stream; + struct azx *chip = bus_to_azx(azx_dev->bus); + struct snd_pcm_substream *substream = azx_dev->substream; + + if (chip->driver_caps & AZX_DCAPS_LOONGSON_HDA_WORKAROUND) { + snd_hdac_stream_updatel(azx_dev, SD_CTL, + SD_CTL_DMA_START | SD_INT_MASK, 0); + snd_hdac_stream_updateb(azx_dev, SD_STS, 0, 0); /* to be sure */ + + if (!substream) + stream_to_azx_dev(azx_dev)->fix_prvpos = 0; + else { + stream = substream->stream; + stream_to_azx_dev(azx_dev)->fix_prvpos = + chip->get_position[stream](chip, stream_to_azx_dev(azx_dev)); + } + } + else { + snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_CTL_DMA_START | SD_INT_MASK, 0); - snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ + snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ + } if (azx_dev->stripe) snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0); azx_dev->running = false; @@ -151,6 +179,10 @@ void snd_hdac_stream_reset(struct hdac_stream *azx_dev) unsigned char val; int timeout; int dma_run_state; + struct azx *chip = bus_to_azx(azx_dev->bus); + + if (chip->driver_caps & AZX_DCAPS_LOONGSON_HDA_WORKAROUND) + goto out; snd_hdac_stream_clear(azx_dev); @@ -182,6 +214,7 @@ void snd_hdac_stream_reset(struct hdac_stream *azx_dev) break; } while (--timeout); +out: /* reset first position - may not be synced with hw at this time */ if (azx_dev->posbuf) *azx_dev->posbuf = 0; @@ -197,6 +230,7 @@ int snd_hdac_stream_setup(struct hdac_stream *azx_dev) struct hdac_bus *bus = azx_dev->bus; struct snd_pcm_runtime *runtime; unsigned int val; + struct azx *chip = bus_to_azx(bus); if (azx_dev->substream) runtime = azx_dev->substream->runtime; @@ -206,14 +240,24 @@ int snd_hdac_stream_setup(struct hdac_stream *azx_dev) snd_hdac_stream_clear(azx_dev); /* program the stream_tag */ val = snd_hdac_stream_readl(azx_dev, SD_CTL); - val = (val & ~SD_CTL_STREAM_TAG_MASK) | - (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT); + + if (!(chip->driver_caps & AZX_DCAPS_LOONGSON_HDA_WORKAROUND)) { + val = (val & ~SD_CTL_STREAM_TAG_MASK) | + (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT); + } if (!bus->snoop) val |= SD_CTL_TRAFFIC_PRIO; snd_hdac_stream_writel(azx_dev, SD_CTL, val); /* program the length of samples in cyclic buffer */ - snd_hdac_stream_writel(azx_dev, SD_CBL, azx_dev->bufsize); + if (chip->driver_caps & AZX_DCAPS_LOONGSON_HDA_WORKAROUND) { + if(azx_dev->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_hdac_stream_writel(azx_dev, SD_CBL, azx_dev->bufsize - 64); + else + snd_hdac_stream_writel(azx_dev, SD_CBL, azx_dev->bufsize - 16); + } + else + snd_hdac_stream_writel(azx_dev, SD_CBL, azx_dev->bufsize); /* program the stream format */ /* this value needs to be the same as the one programmed */ @@ -419,6 +463,7 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) __le32 *bdl; int i, ofs, periods, period_bytes; int pos_adj, pos_align; + struct azx *chip = bus_to_azx(bus); /* reset BDL address */ snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0); @@ -433,6 +478,8 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) azx_dev->frags = 0; pos_adj = bus->bdl_pos_adj; + if (chip->driver_caps & AZX_DCAPS_LOONGSON_HDA_WORKAROUND) + pos_adj = 0; if (!azx_dev->no_period_wakeup && pos_adj > 0) { pos_align = pos_adj; pos_adj = (pos_adj * runtime->rate + 47999) / 48000; diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 90759391cbac..266d34485df2 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -42,6 +42,17 @@ config SND_HDA_TEGRA To compile this driver as a module, choose M here: the module will be called snd-hda-tegra. +config SND_HDA_LOONGSON + tristate "Loongson HD Audio" + depends on MACH_LOONGSON64 + select SND_HDA + help + Say Y here to include support for Loongson "High + Definition Audio" controller. + + To compile this driver as a module, choose M here: the module + will be called snd-hda-loongson. + if SND_HDA config SND_HDA_HWDEP diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index b57432f00056..2b6d1b32409c 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 snd-hda-intel-objs := hda_intel.o snd-hda-tegra-objs := hda_tegra.o +snd-hda-loongson-objs := hda_loongson.o snd-hda-codec-y := hda_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o snd-hda-codec-y += hda_controller.o @@ -48,3 +49,4 @@ obj-$(CONFIG_SND_HDA_CODEC_HDMI) += snd-hda-codec-hdmi.o # when built in kernel obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o obj-$(CONFIG_SND_HDA_TEGRA) += snd-hda-tegra.o +obj-$(CONFIG_SND_HDA_LOONGSON) += snd-hda-loongson.o diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 3b17af0dac2c..0e99e754524b 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -295,6 +295,23 @@ unsigned int azx_get_position(struct azx *chip, int stream = substream->stream; int delay = 0; + if (chip->driver_caps & AZX_DCAPS_LOONGSON_HDA_WORKAROUND) { + pos = chip->get_position[stream](chip, azx_dev); + + if (pos >= azx_dev->fix_prvpos) { + pos = pos - azx_dev->fix_prvpos; + pos %= azx_dev->core.bufsize; + } else { + if (azx_dev->fix_prvpos > azx_dev->core.bufsize) + pos = (0x100000000ULL + pos-azx_dev->fix_prvpos) + % azx_dev->core.bufsize; + else + pos = pos + azx_dev->core.bufsize - azx_dev->fix_prvpos; + } + + return pos; + } + if (chip->get_position[stream]) pos = chip->get_position[stream](chip, azx_dev); else /* use the position buffer as default */ @@ -920,6 +937,8 @@ static int azx_send_cmd(struct hdac_bus *bus, unsigned int val) if (chip->disabled) return 0; + if (chip->driver_caps & AZX_DCAPS_LOONGSON_HDA_WORKAROUND) + udelay(500); if (chip->single_cmd) return azx_single_send_cmd(bus, val); else @@ -1088,8 +1107,9 @@ static void stream_update(struct hdac_bus *bus, struct hdac_stream *s) irqreturn_t azx_interrupt(int irq, void *dev_id) { struct azx *chip = dev_id; + struct hdac_stream *azx_dev; struct hdac_bus *bus = azx_bus(chip); - u32 status; + u32 i = 0, status = 0; bool active, handled = false; int repeat = 0; /* count for avoiding endless loop */ @@ -1105,8 +1125,19 @@ irqreturn_t azx_interrupt(int irq, void *dev_id) goto unlock; do { - status = azx_readl(chip, INTSTS); - if (status == 0 || status == 0xffffffff) + if (chip->driver_caps & AZX_DCAPS_LOONGSON_HDA_WORKAROUND) { + list_for_each_entry(azx_dev, &bus->stream_list, list) { + status |= (snd_hdac_stream_readb(azx_dev, SD_STS) & SD_INT_MASK) ? + (1 << i) : 0; + i++; + } + status |= (status & ~0) ? (1 << 31) : 0; + } + else + status = azx_readl(chip, INTSTS); + + if (status == 0 || + (status == 0xffffffff && !(chip->driver_caps & AZX_DCAPS_LOONGSON_HDA_WORKAROUND))) break; handled = true; @@ -1124,7 +1155,11 @@ irqreturn_t azx_interrupt(int irq, void *dev_id) * remain unserviced by IRQ, eventually falling back * to polling mode in azx_rirb_get_response. */ - azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); + if (chip->driver_caps & AZX_DCAPS_LOONGSON_HDA_WORKAROUND) + azx_writeb(chip, RIRBSTS, status & RIRB_INT_MASK); + else + azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); + active = true; if (status & RIRB_INT_RESPONSE) { if ((chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) || diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index c18aa6b654c6..b1b171e3566c 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -38,7 +38,7 @@ #define AZX_DCAPS_NO_ALIGN_BUFSIZE (1 << 21) /* no buffer size alignment */ /* 22 unused */ #define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */ -/* 24 unused */ +#define AZX_DCAPS_LOONGSON_HDA_WORKAROUND (1 << 24) /* Loongson HDA controller workaround */ #define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */ #define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */ #define AZX_DCAPS_RETRY_PROBE (1 << 27) /* retry probe if no codec is configured */ @@ -64,6 +64,9 @@ struct azx_dev { * when link position is not greater than FIFO size */ unsigned int insufficient:1; + + /* For Loongson */ + unsigned int fix_prvpos; }; #define azx_stream(dev) (&(dev)->core) diff --git a/sound/pci/hda/hda_loongson.c b/sound/pci/hda/hda_loongson.c new file mode 100644 index 000000000000..8e4cc39ebf8d --- /dev/null +++ b/sound/pci/hda/hda_loongson.c @@ -0,0 +1,987 @@ +/* + * + * hda_loongson.c - Implementation of primary alsa driver code base + * for Intel HD Audio. + * + * Copyright (c) 2004 Intel Corporation. All rights reserved. + * + * Copyright (c) 2004 Takashi Iwai + * PeiSen Hou + * + * Copyright (c) 2014 Huacai Chen + * + * 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, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * CONTACTS: + * + * Matt Jared matt.jared@intel.com + * Andy Kopp andy.kopp@intel.com + * Dan Kogan dan.d.kogan@intel.com + * + * CHANGES: + * + * 2004.12.01 Major rewrite by tiwai, merged the work of pshou + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "sound/hda_codec.h" +#include "hda_controller.h" + + +/* macros for convenience. */ +#define platform_resource_start(dev,bar) ((dev)->resource[(bar)].start) +#define platform_resource_end(dev,bar) ((dev)->resource[(bar)].end) +#define platform_resource_flags(dev,bar) ((dev)->resource[(bar)].flags) +#define platform_resource_len(dev,bar) \ + ((platform_resource_start((dev),(bar)) == 0 && \ + platform_resource_end((dev),(bar)) == \ + platform_resource_start((dev),(bar))) ? 0 : \ + \ + (platform_resource_end((dev),(bar)) - \ + platform_resource_start((dev),(bar)) + 1)) + +#define PCI_DEVICE_ID_LOONGSON_HDA 0x7a07 + +#define ICH6_NUM_CAPTURE 4 +#define ICH6_NUM_PLAYBACK 4 + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +static char *model[SNDRV_CARDS]; +static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1}; +static int probe_only[SNDRV_CARDS]; +static int jackpoll_ms[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1200}; +static bool single_cmd; +static int enable_msi = -1; +#ifdef CONFIG_SND_HDA_INPUT_BEEP +static bool beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = + CONFIG_SND_HDA_INPUT_BEEP_MODE}; +#endif + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for Loongson HD audio interface."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for Loongson HD audio interface."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable Loongson HD audio interface."); +module_param_array(model, charp, NULL, 0444); +MODULE_PARM_DESC(model, "Use the given board model."); +module_param_array(bdl_pos_adj, int, NULL, 0644); +MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset."); +module_param_array(probe_only, int, NULL, 0444); +MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization."); +module_param_array(jackpoll_ms, int, NULL, 0444); +MODULE_PARM_DESC(jackpoll_ms, "Ms between polling for jack events (default = 0, using unsol events only)"); +module_param(single_cmd, bool, 0444); +MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs " + "(for debugging only)."); +module_param(enable_msi, bint, 0444); +MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)"); +#ifdef CONFIG_SND_HDA_INPUT_BEEP +module_param_array(beep_mode, bool, NULL, 0444); +MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode " + "(0=off, 1=on) (default=1)."); +#endif + +#ifdef CONFIG_PM +static int param_set_xint(const char *val, const struct kernel_param *kp); +static struct kernel_param_ops param_ops_xint = { + .set = param_set_xint, + .get = param_get_int, +}; +#define param_check_xint param_check_int + +static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; +module_param(power_save, xint, 0644); +MODULE_PARM_DESC(power_save, "Automatic power-saving timeout " + "(in second, 0 = disable)."); + +/* reset the HD-audio controller in power save mode. + * this may give more power-saving, but will take longer time to + * wake up. + */ +static bool power_save_controller = 1; +module_param(power_save_controller, bool, 0644); +MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode."); +#else +#define power_save 0 +#endif /* CONFIG_PM */ + +static int align_buffer_size = -1; +module_param(align_buffer_size, bint, 0644); +MODULE_PARM_DESC(align_buffer_size, + "Force buffer and period sizes to be multiple of 128 bytes."); + +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Intel, ICH6}," + "{Intel, ICH6M}," + "{Intel, ICH7}," + "{Intel, ICH8}," + "{Intel, ICH9}," + "{Intel, ICH10}}"); +MODULE_DESCRIPTION("Loongson HDA driver"); + +/* driver types */ +enum { + AZX_DRIVER_ICH, + AZX_NUM_DRIVERS, /* keep this as last entry */ +}; + +static char *driver_short_names[] = { + [AZX_DRIVER_ICH] = "HDA Loongson", +}; + +struct hda_loongson { + struct azx chip; + + /* for pending irqs */ + struct work_struct irq_pending_work; + + /* sync probing */ + struct completion probe_wait; + struct work_struct probe_work; + + /* card list (for power_save trigger) */ + struct list_head list; + + /* extra flags */ + unsigned int irq_pending_warned:1; + unsigned int probe_continued:1; +}; + +static int azx_acquire_irq(struct azx *chip, int do_disconnect); + +static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev); + +/* called from IRQ */ +static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev) +{ + struct hda_loongson *hda = container_of(chip, struct hda_loongson, chip); + int ok; + + ok = azx_position_ok(chip, azx_dev); + if (ok == 1) { + azx_dev->irq_pending = 0; + return ok; + } else if (ok == 0) { + /* bogus IRQ, process it later */ + azx_dev->irq_pending = 1; + schedule_work(&hda->irq_pending_work); + } + return 0; +} + +/* + * Check whether the current DMA position is acceptable for updating + * periods. Returns non-zero if it's OK. + * + * Many HD-audio controllers appear pretty inaccurate about + * the update-IRQ timing. The IRQ is issued before actually the + * data is processed. So, we need to process it afterwords in a + * workqueue. + */ +static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) +{ + return 1; /* OK, it's fine */ +} + +/* + * The work for pending PCM period updates. + */ +static void azx_irq_pending_work(struct work_struct *work) +{ + struct hda_loongson *hda = container_of(work, struct hda_loongson, irq_pending_work); + struct azx *chip = &hda->chip; + struct hdac_bus *bus = azx_bus(chip); + struct hdac_stream *s; + int pending, ok; + + if (!hda->irq_pending_warned) { + dev_info(chip->card->dev, + "IRQ timing workaround is activated for card #%d. Suggest a bigger bdl_pos_adj.\n", + chip->card->number); + hda->irq_pending_warned = 1; + } + + for (;;) { + pending = 0; + spin_lock_irq(&bus->reg_lock); + list_for_each_entry(s, &bus->stream_list, list) { + struct azx_dev *azx_dev = stream_to_azx_dev(s); + if (!azx_dev->irq_pending || + !s->substream || + !s->running) + continue; + ok = azx_position_ok(chip, azx_dev); + if (ok > 0) { + azx_dev->irq_pending = 0; + spin_unlock(&bus->reg_lock); + snd_pcm_period_elapsed(s->substream); + spin_lock(&bus->reg_lock); + } else if (ok < 0) { + pending = 0; /* too early */ + } else + pending++; + } + spin_unlock_irq(&bus->reg_lock); + if (!pending) + return; + msleep(1); + } +} + +/* clear irq_pending flags and assure no on-going workq */ +static void azx_clear_irq_pending(struct azx *chip) +{ + struct hdac_bus *bus = azx_bus(chip); + struct hdac_stream *s; + + spin_lock_irq(&bus->reg_lock); + list_for_each_entry(s, &bus->stream_list, list) { + struct azx_dev *azx_dev = stream_to_azx_dev(s); + azx_dev->irq_pending = 0; + } + spin_unlock_irq(&bus->reg_lock); +} + +static int azx_acquire_irq(struct azx *chip, int do_disconnect) +{ + struct hdac_bus *bus = azx_bus(chip); + int irq; + + if (chip->pci) + irq = chip->pci->irq; + else + irq = platform_get_irq(to_platform_device(chip->card->dev), 0); + + if (request_irq(irq, azx_interrupt, chip->msi ? 0 : IRQF_SHARED, + KBUILD_MODNAME, chip)) { + dev_err(chip->card->dev, + "unable to grab IRQ %d, disabling device\n", irq); + if (do_disconnect) + snd_card_disconnect(chip->card); + return -1; + } + bus->irq = irq; + return 0; +} + +#ifdef CONFIG_PM +static DEFINE_MUTEX(card_list_lock); +static LIST_HEAD(card_list); + +static void azx_add_card_list(struct azx *chip) +{ + struct hda_loongson *hda = container_of(chip, struct hda_loongson, chip); + mutex_lock(&card_list_lock); + list_add(&hda->list, &card_list); + mutex_unlock(&card_list_lock); +} + +static void azx_del_card_list(struct azx *chip) +{ + struct hda_loongson *hda = container_of(chip, struct hda_loongson, chip); + mutex_lock(&card_list_lock); + list_del_init(&hda->list); + mutex_unlock(&card_list_lock); +} + +/* trigger power-save check at writing parameter */ +static int param_set_xint(const char *val, const struct kernel_param *kp) +{ + struct hda_loongson *hda; + struct azx *chip; + int prev = power_save; + int ret = param_set_int(val, kp); + + if (ret || prev == power_save) + return ret; + + mutex_lock(&card_list_lock); + list_for_each_entry(hda, &card_list, list) { + chip = &hda->chip; + if (!hda->probe_continued || chip->disabled) + continue; + snd_hda_set_power_save(&chip->bus, power_save * 1000); + } + mutex_unlock(&card_list_lock); + return 0; +} +#else +#define azx_add_card_list(chip) /* NOP */ +#define azx_del_card_list(chip) /* NOP */ +#endif /* CONFIG_PM */ + +#if defined(CONFIG_PM_SLEEP) +/* + * power management + */ +static int azx_suspend(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + struct hdac_bus *bus; + + if (chip->disabled) + return 0; + + bus = azx_bus(chip); + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + azx_clear_irq_pending(chip); + azx_stop_chip(chip); + azx_enter_link_reset(chip); + if (bus->irq >= 0) { + free_irq(bus->irq, chip); + bus->irq = -1; + } + return 0; +} + +static int azx_resume(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + + if (chip->disabled) + return 0; + + chip->msi = 0; + if (azx_acquire_irq(chip, 1) < 0) + return -EIO; + + azx_init_chip(chip, true); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM +static int azx_runtime_suspend(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + + if (chip->disabled) + return 0; + + if (!azx_has_pm_runtime(chip)) + return 0; + + /* enable controller wake up event */ + azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) | + STATESTS_INT_MASK); + + azx_stop_chip(chip); + azx_enter_link_reset(chip); + azx_clear_irq_pending(chip); + return 0; +} + +static int azx_runtime_resume(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + struct hda_codec *codec; + int status; + + if (chip->disabled) + return 0; + + if (!azx_has_pm_runtime(chip)) + return 0; + + /* Read STATESTS before controller reset */ + status = azx_readw(chip, STATESTS); + + azx_init_chip(chip, true); + + if (status) { + list_for_each_codec(codec, &chip->bus) + if (status & (1 << codec->addr)) + schedule_delayed_work(&codec->jackpoll_work, + codec->jackpoll_interval); + } + + /* disable controller Wake Up event*/ + azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) & + ~STATESTS_INT_MASK); + + return 0; +} + +static int azx_runtime_idle(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + + if (chip->disabled) + return 0; + + if (!power_save_controller || !azx_has_pm_runtime(chip) || + azx_bus(chip)->codec_powered) + return -EBUSY; + + return 0; +} + +#endif /* CONFIG_PM */ + +#ifdef CONFIG_PM +static const struct dev_pm_ops azx_pm = { + SET_SYSTEM_SLEEP_PM_OPS(azx_suspend, azx_resume) + SET_RUNTIME_PM_OPS(azx_runtime_suspend, azx_runtime_resume, azx_runtime_idle) +}; + +#define AZX_PM_OPS &azx_pm +#else +#define AZX_PM_OPS NULL +#endif /* CONFIG_PM */ + +static int azx_probe_continue(struct azx *chip); + +/* + * destructor + */ +static int azx_free(struct azx *chip) +{ + struct device *snddev = chip->card->dev; + struct hda_loongson *hda = container_of(chip, struct hda_loongson, chip); + struct hdac_bus *bus = azx_bus(chip); + + if (azx_has_pm_runtime(chip) && chip->running) + pm_runtime_get_noresume(snddev); + + azx_del_card_list(chip); + + complete_all(&hda->probe_wait); + + if (bus->chip_init) { + azx_clear_irq_pending(chip); + azx_stop_all_streams(chip); + azx_stop_chip(chip); + } + + if (bus->irq >= 0) + free_irq(bus->irq, (void*)chip); + if (bus->remap_addr) + iounmap(bus->remap_addr); + + azx_free_stream_pages(chip); + azx_free_streams(chip); + snd_hdac_bus_exit(bus); + kfree(hda); + + return 0; +} + +static int azx_dev_disconnect(struct snd_device *device) +{ + struct azx *chip = device->device_data; + + chip->bus.shutdown = 1; + return 0; +} + +static int azx_dev_free(struct snd_device *device) +{ + return azx_free(device->device_data); +} + +/* + * constructor + */ +static const struct hda_controller_ops loongson_hda_ops; +static void azx_probe_work(struct work_struct *work) +{ + struct hda_loongson *hda = container_of(work, struct hda_loongson, probe_work); + azx_probe_continue(&hda->chip); +} + +static int azx_create(struct snd_card *card, struct pci_dev *pcidev, + struct platform_device *platdev, int dev, + unsigned int driver_caps, struct azx **rchip) +{ + static struct snd_device_ops ops = { + .dev_disconnect = azx_dev_disconnect, + .dev_free = azx_dev_free, + }; + struct hda_loongson *hda; + struct azx *chip; + int err; + + *rchip = NULL; + + hda = kzalloc(sizeof(*hda), GFP_KERNEL); + if (!hda) { + dev_err(card->dev, "Cannot allocate hda\n"); + return -ENOMEM; + } + + chip = &hda->chip; + mutex_init(&chip->open_mutex); + chip->card = card; + chip->pci = pcidev; + chip->ops = &loongson_hda_ops; + chip->driver_caps = driver_caps; + chip->driver_type = driver_caps & 0xff; + chip->dev_index = dev; + chip->jackpoll_interval = msecs_to_jiffies(jackpoll_ms[dev]); + INIT_LIST_HEAD(&chip->pcm_list); + INIT_WORK(&hda->irq_pending_work, azx_irq_pending_work); + INIT_LIST_HEAD(&hda->list); + init_completion(&hda->probe_wait); + + chip->get_position[0] = chip->get_position[1] = azx_get_pos_lpib; + + chip->snoop = false; + chip->single_cmd = single_cmd; + + if (bdl_pos_adj[dev] < 0) { + switch (chip->driver_type) { + case AZX_DRIVER_ICH: + bdl_pos_adj[dev] = 1; + break; + default: + bdl_pos_adj[dev] = 32; + break; + } + } + chip->bdl_pos_adj = bdl_pos_adj[dev]; + + err = azx_bus_init(chip, model[dev]); + if (err < 0) { + kfree(hda); + return err; + } + + azx_bus(chip)->codec_mask = chip->codec_probe_mask = 0x1; + azx_bus(chip)->polling_mode = 1; + + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); + if (err < 0) { + dev_err(card->dev, "Error creating device [card]!\n"); + azx_free(chip); + return err; + } + + INIT_WORK(&hda->probe_work, azx_probe_work); + + *rchip = chip; + + return 0; +} + +static int azx_first_init(struct azx *chip) +{ + int dev = chip->dev_index; + struct snd_card *card = chip->card; + struct hdac_bus *bus = azx_bus(chip); + int err; + unsigned short gcap; + + if (chip->pci) { + bus->addr = pci_resource_start(chip->pci, 0); + bus->remap_addr = ioremap(bus->addr, + pci_resource_end(chip->pci, 0) - pci_resource_start(chip->pci, 0) + 1); + } else { + bus->addr = platform_resource_start(to_platform_device(chip->card->dev), 0); + bus->remap_addr = ioremap(bus->addr, + platform_resource_len(to_platform_device(chip->card->dev), 0)); + } + if (bus->remap_addr == NULL) { + dev_err(card->dev, "ioremap error\n"); + return -ENXIO; + } + + chip->msi = 0; + + if (azx_acquire_irq(chip, 0) < 0) + return -EBUSY; + + synchronize_irq(bus->irq); + + gcap = azx_readw(chip, GCAP); + dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap); + + /* disable 64bit DMA address on some devices */ + if (chip->driver_caps & AZX_DCAPS_NO_64BIT) { + dev_dbg(card->dev, "Disabling 64bit DMA\n"); + gcap &= ~AZX_GCAP_64OK; + } + + /* disable buffer size rounding to 128-byte multiples if supported */ + if (align_buffer_size >= 0) + chip->align_buffer_size = !!align_buffer_size; + else { + if (chip->driver_caps & AZX_DCAPS_NO_ALIGN_BUFSIZE) + chip->align_buffer_size = 0; + else + chip->align_buffer_size = 1; + } + + /* allow 64bit DMA address if supported by H/W */ + if ((gcap & AZX_GCAP_64OK) && !dma_set_mask(chip->card->dev, DMA_BIT_MASK(64))) + dma_set_coherent_mask(chip->card->dev, DMA_BIT_MASK(64)); + else { + dma_set_mask(chip->card->dev, DMA_BIT_MASK(32)); + dma_set_coherent_mask(chip->card->dev, DMA_BIT_MASK(32)); + } + + /* read number of streams from GCAP register instead of using + * hardcoded value + */ + chip->capture_streams = (gcap >> 8) & 0x0f; + chip->playback_streams = (gcap >> 12) & 0x0f; + if (!chip->playback_streams && !chip->capture_streams) { + /* gcap didn't give any info, switching to old method */ + chip->playback_streams = ICH6_NUM_PLAYBACK; + chip->capture_streams = ICH6_NUM_CAPTURE; + } + chip->capture_index_offset = 0; + chip->playback_index_offset = chip->capture_streams; + chip->num_streams = chip->playback_streams + chip->capture_streams; + + /* initialize streams */ + err = azx_init_streams(chip); + if (err < 0) + return err; + chip->playback_streams = chip->capture_streams = 1; /* Loongson */ + + err = azx_alloc_stream_pages(chip); + if (err < 0) + return err; + + /* initialize chip */ + azx_init_chip(chip, (probe_only[dev] & 2) == 0); + + /* codec detection */ + if (!azx_bus(chip)->codec_mask) { + dev_err(card->dev, "no codecs found!\n"); + return -ENODEV; + } + + strcpy(card->driver, "HDA-Loongson"); + strlcpy(card->shortname, driver_short_names[chip->driver_type], + sizeof(card->shortname)); + snprintf(card->longname, sizeof(card->longname), + "%s at 0x%lx irq %i", + card->shortname, bus->addr, bus->irq); + + return 0; +} + + +static const struct hda_controller_ops loongson_hda_ops = { + .position_check = azx_position_check, +}; + +static int azx_plat_probe(struct platform_device *pdev) +{ + int err; + bool probe_now; + static int dev; + struct snd_card *card; + struct azx *chip; + struct hda_loongson *hda; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE, + 0, &card); + if (err < 0) { + dev_err(&pdev->dev, "Error creating card!\n"); + return err; + } + + err = azx_create(card, NULL, pdev, dev, AZX_DRIVER_ICH | AZX_DCAPS_LOONGSON_HDA_WORKAROUND, &chip); + if (err < 0) + goto out_free; + card->private_data = chip; + hda = container_of(chip, struct hda_loongson, chip); + + dev_set_drvdata(&pdev->dev, card); + + probe_now = !chip->disabled; + + if (probe_now) + schedule_work(&hda->probe_work); + dev++; + if (chip->disabled) + complete_all(&hda->probe_wait); + return 0; + +out_free: + snd_card_free(card); + return err; +} + +/* number of codec slots for each chipset: 0 = default slots (i.e. 4) */ +static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] = {}; + +static int azx_probe_continue(struct azx *chip) +{ + struct hda_loongson *hda = container_of(chip, struct hda_loongson, chip); + struct hdac_bus *bus = azx_bus(chip); + int dev = chip->dev_index; + int err; + struct device *snddev = chip->card->dev; + + to_hda_bus(bus)->bus_probing = 1; + hda->probe_continued = 1; + + err = azx_first_init(chip); + if (err < 0) + goto out_free; + +#ifdef CONFIG_SND_HDA_INPUT_BEEP + chip->beep_mode = beep_mode[dev]; +#endif + + /* create codec instances */ + err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]); + if (err < 0) + goto out_free; + + if ((probe_only[dev] & 1) == 0) { + err = azx_codec_configure(chip); + if (err < 0) + goto out_free; + } + + err = snd_card_register(chip->card); + if (err < 0) + goto out_free; + + chip->running = 1; + azx_add_card_list(chip); +#ifdef CONFIG_PM + pm_runtime_forbid(snddev); + pm_runtime_set_active(snddev); +#endif + snd_hda_set_power_save(&chip->bus, power_save * 1000); + if (azx_has_pm_runtime(chip)) + pm_runtime_put_noidle(snddev); + +out_free: + complete_all(&hda->probe_wait); + to_hda_bus(bus)->bus_probing = 0; + return err; +} + +static int azx_plat_remove(struct platform_device *pdev) +{ + return snd_card_free(dev_get_drvdata(&pdev->dev)); +} + +static void azx_plat_shutdown(struct platform_device *pdev) +{ + struct snd_card *card = dev_get_drvdata(&pdev->dev); + struct azx *chip; + + if (!card) + return; + chip = card->private_data; + if (chip && chip->running) + azx_stop_chip(chip); +} + +#ifdef CONFIG_OF +static struct of_device_id audio_id_table[] = { + { .compatible = "loongson,loongson3-hda", }, +}; +#endif + +/* platform_driver definition */ +static struct platform_driver azx_plat_driver = { + .probe = azx_plat_probe, + .remove = azx_plat_remove, + .shutdown = azx_plat_shutdown, + .driver = { + .name = "loongson-audio", + .owner = THIS_MODULE, + .pm = AZX_PM_OPS, +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(audio_id_table), +#endif + }, +}; + +static const struct pci_device_id azx_ids[] = { + {PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_HDA)}, + {} +}; + +MODULE_DEVICE_TABLE(pci, azx_ids); + +static int azx_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid) +{ + int ret; + bool probe_now; + static int dev; + struct snd_card *card; + struct azx *chip; + struct hda_loongson *hda; + + /* Enable device in PCI config */ + ret = pci_enable_device(pdev); + if (ret < 0) { + printk(KERN_ERR "Loongson HDA (%s): Cannot enable PCI device\n", + pci_name(pdev)); + goto out; + } + + /* request the mem regions */ + ret = pci_request_region(pdev, 0, "Loongson HDA"); + if (ret < 0) { + printk( KERN_ERR "Loongson HDA (%s): cannot request region 0.\n", + pci_name(pdev)); + goto out; + } + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + ret = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE, + 0, &card); + if (ret < 0) { + dev_err(&pdev->dev, "Error creating card!\n"); + return ret; + } + + ret = azx_create(card, pdev, NULL, dev, AZX_DRIVER_ICH | AZX_DCAPS_LOONGSON_HDA_WORKAROUND, &chip); + if (ret < 0) + goto out_free; + card->private_data = chip; + hda = container_of(chip, struct hda_loongson, chip); + + dev_set_drvdata(&pdev->dev, card); + + probe_now = !chip->disabled; + + if (probe_now) + schedule_work(&hda->probe_work); + dev++; + if (chip->disabled) + complete_all(&hda->probe_wait); + return 0; + +out_free: + snd_card_free(card); +out: + return ret; +} + +static void azx_pci_remove(struct pci_dev *pdev) +{ + struct snd_card *card = dev_get_drvdata(&pdev->dev); + struct azx *chip; + struct hda_loongson *hda; + + if (card) { + /* cancel the pending probing work */ + chip = card->private_data; + hda = container_of(chip, struct hda_loongson, chip); + /* FIXME: below is an ugly workaround. + * Both device_release_driver() and driver_probe_device() + * take *both* the device's and its parent's lock before + * calling the remove() and probe() callbacks. The codec + * probe takes the locks of both the codec itself and its + * parent, i.e. the PCI controller dev. Meanwhile, when + * the PCI controller is unbound, it takes its lock, too + * ==> ouch, a deadlock! + * As a workaround, we unlock temporarily here the controller + * device during cancel_work_sync() call. + */ + device_unlock(&pdev->dev); + cancel_work_sync(&hda->probe_work); + device_lock(&pdev->dev); + + snd_card_free(card); + } +} + +static void azx_pci_shutdown(struct pci_dev *pdev) +{ + struct snd_card *card = dev_get_drvdata(&pdev->dev); + struct azx *chip; + + if (!card) + return; + chip = card->private_data; + if (chip && chip->running) + azx_stop_chip(chip); +} + +/* pci_driver definition */ +static struct pci_driver azx_pci_driver = { + .name = "loongson-audio", + .id_table = azx_ids, + .probe = azx_pci_probe, + .remove = azx_pci_remove, + .shutdown = azx_pci_shutdown, + .driver.pm = AZX_PM_OPS, +}; + +static int __init alsa_card_azx_init(void) +{ + int err; + + err = pci_register_driver(&azx_pci_driver); + if (err) + pr_err("hda azx pci driver register\n"); + + return platform_driver_register(&azx_plat_driver); +} + +static void __exit alsa_card_azx_exit(void) +{ + pci_unregister_driver(&azx_pci_driver); + platform_driver_unregister(&azx_plat_driver); +} + +module_init(alsa_card_azx_init) +module_exit(alsa_card_azx_exit) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6e679c86b6fa..e213bf1ab639 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -25,6 +25,7 @@ #include "hda_auto_parser.h" #include "hda_jack.h" #include "hda_generic.h" +#include "hda_controller.h" /* keep halting ALC5505 DSP, for power saving */ #define HALT_REALTEK_ALC5505 @@ -343,6 +344,13 @@ static void alc_fixup_micmute_led(struct hda_codec *codec, snd_hda_gen_add_micmute_led_cdev(codec, NULL); } +int has_loongson_workaround(struct hda_codec *codec) +{ + struct azx *chip = bus_to_azx(&codec->bus->core); + + return chip->driver_caps & AZX_DCAPS_LOONGSON_HDA_WORKAROUND; +} + /* * Fix hardware PLL issue * On some codecs, the analog PLL gating control must be off while @@ -684,10 +692,10 @@ static int alc_auto_parse_customize_define(struct hda_codec *codec) goto do_sku; } - if (!codec->bus->pci) + if (!codec->bus->pci && !has_loongson_workaround(codec)) return -1; ass = codec->core.subsystem_id & 0xffff; - if (ass != codec->bus->pci->subsystem_device && (ass & 1)) + if (codec->bus->pci && ass != codec->bus->pci->subsystem_device && (ass & 1)) goto do_sku; nid = 0x1d; -- Gitee From ca08ea56ff1b458144268ddc2319439ebc6f2364 Mon Sep 17 00:00:00 2001 From: Tianli Xiong Date: Mon, 19 Sep 2022 17:36:21 +0800 Subject: [PATCH 22/56] LS7A2000 : Add hdmi codec support Signed-off-by: Baoqi Zhang Signed-off-by: Tianli Xiong Change-Id: Iafb32ae2402ae39e3c63fd0106ca0313dc3085e1 --- sound/hda/hdac_device.c | 1 + sound/pci/hda/hda_loongson.c | 13 ++++++++++--- sound/pci/hda/patch_hdmi.c | 1 + 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index b7e5032b61c9..07fc3b4abd55 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -666,6 +666,7 @@ static const struct hda_vendor_id hda_vendor_ids[] = { { 0x434d, "C-Media" }, { 0x8086, "Intel" }, { 0x8384, "SigmaTel" }, + { 0x0014, "Loongson" }, {} /* terminator */ }; diff --git a/sound/pci/hda/hda_loongson.c b/sound/pci/hda/hda_loongson.c index 8e4cc39ebf8d..35c759854d02 100644 --- a/sound/pci/hda/hda_loongson.c +++ b/sound/pci/hda/hda_loongson.c @@ -74,6 +74,7 @@ platform_resource_start((dev),(bar)) + 1)) #define PCI_DEVICE_ID_LOONGSON_HDA 0x7a07 +#define PCI_DEVICE_ID_LOONGSON_HDMI 0x7a37 #define ICH6_NUM_CAPTURE 4 #define ICH6_NUM_PLAYBACK 4 @@ -158,11 +159,13 @@ MODULE_DESCRIPTION("Loongson HDA driver"); /* driver types */ enum { AZX_DRIVER_ICH, + AZX_DRIVER_HDMI, AZX_NUM_DRIVERS, /* keep this as last entry */ }; static char *driver_short_names[] = { [AZX_DRIVER_ICH] = "HDA Loongson", + [AZX_DRIVER_HDMI] = "HDA Loongson HDMI", }; struct hda_loongson { @@ -574,6 +577,7 @@ static int azx_create(struct snd_card *card, struct pci_dev *pcidev, if (bdl_pos_adj[dev] < 0) { switch (chip->driver_type) { case AZX_DRIVER_ICH: + case AZX_DRIVER_HDMI: bdl_pos_adj[dev] = 1; break; default: @@ -589,7 +593,7 @@ static int azx_create(struct snd_card *card, struct pci_dev *pcidev, return err; } - azx_bus(chip)->codec_mask = chip->codec_probe_mask = 0x1; + azx_bus(chip)->codec_mask = chip->codec_probe_mask = 0xf; azx_bus(chip)->polling_mode = 1; err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); @@ -847,7 +851,10 @@ static struct platform_driver azx_plat_driver = { }; static const struct pci_device_id azx_ids[] = { - {PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_HDA)}, + { PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_HDMI), + .driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_LOONGSON_HDA_WORKAROUND}, + { PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_HDA), + .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_LOONGSON_HDA_WORKAROUND }, {} }; @@ -892,7 +899,7 @@ static int azx_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid) return ret; } - ret = azx_create(card, pdev, NULL, dev, AZX_DRIVER_ICH | AZX_DCAPS_LOONGSON_HDA_WORKAROUND, &chip); + ret = azx_create(card, pdev, NULL, dev, pid->driver_data, &chip); if (ret < 0) goto out_free; card->private_data = chip; diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index de710e6dd56b..ef2c838e5821 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -4394,6 +4394,7 @@ HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi), HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI", patch_i915_icl_hdmi), HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi), +HDA_CODEC_ENTRY(0x00147a47, "Loongson HDMI", patch_generic_hdmi), HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi), HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi), HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI", patch_generic_hdmi), -- Gitee From 02ca9f5f10f9f53ff1fe8d51b804f758ee3a5ca3 Mon Sep 17 00:00:00 2001 From: zhangtianyang Date: Mon, 19 Sep 2022 11:01:21 +0800 Subject: [PATCH 23/56] rebase:cpufreq:Add cpufreq driver for loongarch To:18515570fe6514446eb346bfe32e18cb1c700c8a From: 1.This patch 2.6e8ead17458147863f96f51875537f778f84d017 CONFIG_ACPI_DOCK=y CONFIG_ACPI_IPMI=m CONFIG_ACPI_PCI_SLOT=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_LOONGSON3_ACPI_CPUFREQ=y CONFIG_EFI_CAPSULE_LOADER=m CONFIG_EFI_TEST=m CONFIG_MODULES=y Signed-off-by: zhangtianyang Change-Id: If91753d66afbc1bbadc33b7e533b3eb230fa76ec Signed-off-by: zhangtianyang --- arch/loongarch/include/asm/fpu.h | 13 ++++++++++++- drivers/cpufreq/Kconfig | 2 +- drivers/cpufreq/loongson3-acpi-cpufreq.c | 5 ----- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/arch/loongarch/include/asm/fpu.h b/arch/loongarch/include/asm/fpu.h index eaa8030f2d9c..1363f92929df 100644 --- a/arch/loongarch/include/asm/fpu.h +++ b/arch/loongarch/include/asm/fpu.h @@ -45,6 +45,10 @@ static inline void disable_lasx(void); static inline void save_lasx(struct task_struct *t); static inline void restore_lasx(struct task_struct *t); +#ifdef CONFIG_LOONGSON3_ACPI_CPUFREQ +DECLARE_PER_CPU(unsigned long, msa_count); +DECLARE_PER_CPU(unsigned long, lasx_count); +#endif /* * Mask the FCSR Cause bits according to the Enable bits, observing * that Unimplemented is always enabled. @@ -198,6 +202,9 @@ static inline void enable_lsx(void) { if (cpu_has_lsx) csr_xchg32(CSR_EUEN_LSXEN, CSR_EUEN_LSXEN, LOONGARCH_CSR_EUEN); +#ifdef CONFIG_LOONGSON3_ACPI_CPUFREQ + per_cpu(msa_count, raw_smp_processor_id())++; +#endif } static inline void disable_lsx(void) @@ -251,8 +258,12 @@ static inline void restore_lsx_upper(struct task_struct *t) {} static inline void enable_lasx(void) { - if (cpu_has_lasx) + if (cpu_has_lasx) { csr_xchg32(CSR_EUEN_LASXEN, CSR_EUEN_LASXEN, LOONGARCH_CSR_EUEN); +#ifdef CONFIG_LOONGSON3_ACPI_CPUFREQ + per_cpu(lasx_count, raw_smp_processor_id())++; +#endif + } } static inline void disable_lasx(void) diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index c1540d446dba..0d3ec7e7b972 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -294,7 +294,7 @@ config LOONGSON3_ACPI_CPUFREQ help This driver adds a CPUFreq driver which utilizes the ACPI Processor Performance States. - This driver supports Loongson 3A4000 compatible CPUs. + This driver supports Loongson 3A5000 compatible CPUs. If in doubt, say N. endif diff --git a/drivers/cpufreq/loongson3-acpi-cpufreq.c b/drivers/cpufreq/loongson3-acpi-cpufreq.c index e0947b3f9459..5bfbe3867c0a 100644 --- a/drivers/cpufreq/loongson3-acpi-cpufreq.c +++ b/drivers/cpufreq/loongson3-acpi-cpufreq.c @@ -1514,12 +1514,7 @@ static int cpufreq_supported_detect(void) static int __init loongson3_cpufreq_init(void) { int ret; -#ifdef CONFIG_LOONGARCH if (!cpu_has_csr || !cpu_has_scalefreq) -#endif -#ifdef CONFIG_MIPS - if (!loongson_cpu_has_csr || !loongson_cpu_has_scalefreq) -#endif return -ENODEV; /* don't keep reloading if cpufreq_driver exists */ -- Gitee From 6ebe61f5c6cf730cf2d170eaf78f4789169ff4cd Mon Sep 17 00:00:00 2001 From: Jianmin Lv Date: Wed, 27 Jul 2022 19:03:32 +0800 Subject: [PATCH 24/56] irqchip / ACPI: Introduce ACPI_IRQ_MODEL_LPIC for LoongArch For LoongArch, ACPI_IRQ_MODEL_LPIC is introduced, and high level trigger type is set for PCI devices legacy irq. Change-Id: I5f9d44e2d9c7d3492fedf89b64becac5fb5cc270 Signed-off-by: Jianmin Lv --- arch/loongarch/kernel/acpi.c | 1 + drivers/acpi/pci_irq.c | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c index 0d6a4de10f6c..3d9759c3ccb5 100644 --- a/arch/loongarch/kernel/acpi.c +++ b/arch/loongarch/kernel/acpi.c @@ -165,6 +165,7 @@ static void __init acpi_process_madt(void) acpi_table_parse_madt(ACPI_MADT_TYPE_EIO_PIC, acpi_parse_eio_master, MAX_IO_PICS); + acpi_irq_model = ACPI_IRQ_MODEL_LPIC; loongson_sysconf.nr_cpus = num_processors; } diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index 14ee631cb7cf..c2ca66cd2e3f 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -405,8 +405,9 @@ int acpi_pci_irq_enable(struct pci_dev *dev) * controller and must therefore be considered active high * as default. */ - int polarity = acpi_irq_model == ACPI_IRQ_MODEL_GIC ? - ACPI_ACTIVE_HIGH : ACPI_ACTIVE_LOW; + int polarity = acpi_irq_model == ACPI_IRQ_MODEL_GIC || + acpi_irq_model == ACPI_IRQ_MODEL_LPIC ? + ACPI_ACTIVE_HIGH : ACPI_ACTIVE_LOW; char *link = NULL; char link_desc[16]; int rc; -- Gitee From dc7e11d0e66c5e76819fca28cb3d2e7ea2bbc764 Mon Sep 17 00:00:00 2001 From: liuyun Date: Wed, 21 Sep 2022 18:05:48 +0800 Subject: [PATCH 25/56] LoongArch: Call irq_find_matching_fwnode get irq domain Fix patch "LoongArch: Old BPI compatibility" Signed-off-by: liuyun Change-Id: I67c2535a86213e620546889a081511eaa499289a --- arch/loongarch/kernel/legacy_boot.c | 12 ++++++++++-- drivers/irqchip/irq-loongarch-cpu.c | 5 ----- drivers/irqchip/irq-loongson-pch-pic.c | 5 ----- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/arch/loongarch/kernel/legacy_boot.c b/arch/loongarch/kernel/legacy_boot.c index f0934639de3e..d8f749653939 100644 --- a/arch/loongarch/kernel/legacy_boot.c +++ b/arch/loongarch/kernel/legacy_boot.c @@ -239,7 +239,11 @@ int setup_legacy_IRQ(void) printk("CPU domain init eror!\n"); return -1; } - cpu_domain = get_cpudomain(); + cpu_domain = irq_find_matching_fwnode(cpuintc_handle, DOMAIN_BUS_ANY); + if (!cpu_domain) { + printk("CPU domain error!\n"); + return -1; + } ret = liointc_acpi_init(cpu_domain, acpi_liointc); if (ret) { printk("Liointc domain init eror!\n"); @@ -269,7 +273,11 @@ int setup_legacy_IRQ(void) pch_msi_parse_madt((union acpi_subtable_headers *)acpi_pchmsi[0], 0); } - pic_domain = get_pchpic_irq_domain(); + pic_domain = irq_find_matching_fwnode(pch_pic_handle[0], DOMAIN_BUS_ANY); + if (!pic_domain) { + printk("Pic domain error!\n"); + return -1; + } if (pic_domain) pch_lpc_acpi_init(pic_domain, acpi_pchlpc); diff --git a/drivers/irqchip/irq-loongarch-cpu.c b/drivers/irqchip/irq-loongarch-cpu.c index d3a0bbe4a9f7..adbd32b58f77 100644 --- a/drivers/irqchip/irq-loongarch-cpu.c +++ b/drivers/irqchip/irq-loongarch-cpu.c @@ -119,11 +119,6 @@ static int __init acpi_cascade_irqdomain_init(void) return 0; } -struct irq_domain *get_cpudomain(void) -{ - return irq_domain; -} - int __init cpuintc_acpi_init(union acpi_subtable_headers *header, const unsigned long end) { diff --git a/drivers/irqchip/irq-loongson-pch-pic.c b/drivers/irqchip/irq-loongson-pch-pic.c index cd8b16293f39..e09078e1ac01 100644 --- a/drivers/irqchip/irq-loongson-pch-pic.c +++ b/drivers/irqchip/irq-loongson-pch-pic.c @@ -52,11 +52,6 @@ static struct pch_pic *pch_pic_priv[MAX_IO_PICS]; struct fwnode_handle *pch_pic_handle[MAX_IO_PICS]; -struct irq_domain *get_pchpic_irq_domain(void) -{ - return pch_pic_priv[0]->pic_domain; -} - static void pch_pic_bitset(struct pch_pic *priv, int offset, int bit) { u32 reg; -- Gitee From cf273cf215750ac557b7c44c0da917519be663b2 Mon Sep 17 00:00:00 2001 From: Juxin Gao Date: Fri, 2 Sep 2022 17:51:31 +0800 Subject: [PATCH 26/56] i2c: loongson: Add LS7A i2c driver support Change-Id: Ia57bb17a0663d038c61cdd52ce5b6fe63a979dde Signed-off-by: Juxin Gao --- drivers/i2c/busses/Kconfig | 8 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-loongson.c | 592 ++++++++++++++++++++++++++++++ 3 files changed, 601 insertions(+) create mode 100644 drivers/i2c/busses/i2c-loongson.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 9535e995ecc9..5763a1e9360b 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -866,6 +866,14 @@ config I2C_OWL Say Y here if you want to use the I2C bus controller on the Actions Semiconductor Owl SoC's. +config I2C_LOONGSON + tristate "Loongson I2C adapter" + depends on LOONGARCH + default m + help + If you say yes to this option, support will be included for the + I2C interface on the Loongson's LS7A Platform-Bridge. + config I2C_PASEMI tristate "PA Semi SMBus interface" depends on PPC_PASEMI && PCI diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index e6d5d108e22b..280e05622d50 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -89,6 +89,7 @@ obj-$(CONFIG_I2C_NPCM7XX) += i2c-npcm7xx.o obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o obj-$(CONFIG_I2C_OMAP) += i2c-omap.o obj-$(CONFIG_I2C_OWL) += i2c-owl.o +obj-$(CONFIG_I2C_LOONGSON) += i2c-loongson.o obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi.o obj-$(CONFIG_I2C_PCA_PLATFORM) += i2c-pca-platform.o obj-$(CONFIG_I2C_PMCMSP) += i2c-pmcmsp.o diff --git a/drivers/i2c/busses/i2c-loongson.c b/drivers/i2c/busses/i2c-loongson.c new file mode 100644 index 000000000000..a2dc0dd1e4f8 --- /dev/null +++ b/drivers/i2c/busses/i2c-loongson.c @@ -0,0 +1,592 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Loongson-7A I2C master mode driver + * + * Copyright (C) 2013 Loongson Technology Corporation Limited + * Copyright (C) 2014-2017 Lemote, Inc. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "loongson_i2c" + +#define LOONGSON_I2C_PRER_LO_REG 0x0 +#define LOONGSON_I2C_PRER_HI_REG 0x1 +#define LOONGSON_I2C_CTR_REG 0x2 +#define LOONGSON_I2C_TXR_REG 0x3 +#define LOONGSON_I2C_RXR_REG 0x3 +#define LOONGSON_I2C_CR_REG 0x4 +#define LOONGSON_I2C_SR_REG 0x4 +#define LOONGSON_I2C_BLTOP_REG 0x5 +#define LOONGSON_I2C_SADDR_REG 0x7 + +#define CTR_EN 0x80 +#define CTR_IEN 0x40 +#define CTR_TXROK 0x90 +#define CTR_RXROK 0x88 + +#define CR_START 0x81 +#define CR_STOP 0x41 +#define CR_READ 0x21 +#define CR_WRITE 0x11 +#define CR_ACK 0x8 +#define CR_IACK 0x1 + +#define SR_NOACK 0x80 +#define SR_BUSY 0x40 +#define SR_AL 0x20 +#define SR_SLAVE_ADDRESSED 0x10 +#define SR_SLAVE_RW 0x8 +#define SR_TIP 0x2 +#define SR_IF 0x1 + +#define i2c_readb(addr) readb(dev->base + addr) +#define i2c_writeb(val, addr) writeb(val, dev->base + addr) + +#ifdef LOONGSON_I2C_DEBUG +#define i2c_debug(fmt, args...) printk(KERN_CRIT fmt, ##args) +#else +#define i2c_debug(fmt, args...) +#endif + +static bool repeated_start = 1; +module_param(repeated_start, bool, S_IRUSR | S_IWUSR); +MODULE_PARM_DESC(repeated_start, "Compatible with devices that support repeated start"); + +enum loongson_i2c_slave_state { + LOONGSON_I2C_SLAVE_STOP, + LOONGSON_I2C_SLAVE_START, + LOONGSON_I2C_SLAVE_READ_REQUESTED, + LOONGSON_I2C_SLAVE_READ_PROCESSED, + LOONGSON_I2C_SLAVE_WRITE_REQUESTED, + LOONGSON_I2C_SLAVE_WRITE_RECEIVED, +}; + +struct loongson_i2c_dev { + spinlock_t lock; + unsigned int suspended:1; + struct device *dev; + void __iomem *base; + int irq; + struct completion cmd_complete; + struct resource *ioarea; + struct i2c_adapter adapter; +#if IS_ENABLED(CONFIG_I2C_SLAVE) + struct i2c_client *slave; + enum loongson_i2c_slave_state slave_state; +#endif /* CONFIG_I2C_SLAVE */ +}; + +static int i2c_stop(struct loongson_i2c_dev *dev) +{ + unsigned long time_left; + +again: + i2c_writeb(CR_STOP, LOONGSON_I2C_CR_REG); + time_left = wait_for_completion_timeout( + &dev->cmd_complete, + (&dev->adapter)->timeout); + if (!time_left) { + pr_info("Timeout abort message cmd\n"); + return -1; + } + + i2c_readb(LOONGSON_I2C_SR_REG); + while (i2c_readb(LOONGSON_I2C_SR_REG) & SR_BUSY) + goto again; + + return 0; +} + +static int i2c_start(struct loongson_i2c_dev *dev, + int dev_addr, int flags) +{ + unsigned long time_left; + int retry = 5; + unsigned char addr = (dev_addr & 0x7f) << 1; + addr |= (flags & I2C_M_RD)? 1:0; + +start: + mdelay(1); + i2c_writeb(addr, LOONGSON_I2C_TXR_REG); + i2c_debug("%s : i2c device address: 0x%x\n", + __func__, __LINE__, addr); + i2c_writeb((CR_START | CR_WRITE), LOONGSON_I2C_CR_REG); + time_left = wait_for_completion_timeout( + &dev->cmd_complete, + (&dev->adapter)->timeout); + if (!time_left) { + pr_info("Timeout abort message cmd\n"); + return -1; + } + + if (i2c_readb(LOONGSON_I2C_SR_REG) & SR_NOACK) { + if (i2c_stop(dev) < 0) + return -1; + while (retry--) + goto start; + pr_debug("There is no i2c device ack\n"); + return 0; + } + return 1; +} + +#if IS_ENABLED(CONFIG_I2C_SLAVE) +static void __loongson_i2c_reg_slave(struct loongson_i2c_dev *dev, u16 slave_addr) +{ + /* Set slave addr. */ + i2c_writeb(slave_addr & 0x7f, LOONGSON_I2C_SADDR_REG); + + /* Turn on slave mode. */ + i2c_writeb(0xc0, LOONGSON_I2C_CTR_REG); +} + +static int loongson_i2c_reg_slave(struct i2c_client *client) +{ + struct loongson_i2c_dev *dev = i2c_get_adapdata(client->adapter); + unsigned long flags; + + if (dev->slave) { + return -EINVAL; + } + + __loongson_i2c_reg_slave(dev, client->addr); + + dev->slave = client; + dev->slave_state = LOONGSON_I2C_SLAVE_STOP; + + return 0; +} + +static int loongson_i2c_unreg_slave(struct i2c_client *client) +{ + struct loongson_i2c_dev *dev = i2c_get_adapdata(client->adapter); + unsigned long flags; + + if (!dev->slave) { + return -EINVAL; + } + + /* Turn off slave mode. */ + i2c_writeb(0xa0, LOONGSON_I2C_CTR_REG); + + dev->slave = NULL; + + return 0; +} +#endif /* CONFIG_I2C_SLAVE */ + +static void loongson_i2c_reginit(struct loongson_i2c_dev *dev) +{ +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (dev->slave) { + __loongson_i2c_reg_slave(dev, dev->slave->addr); + return; + } +#endif /* CONFIG_I2C_SLAVE */ + i2c_writeb(i2c_readb(LOONGSON_I2C_CR_REG) | 0x01, LOONGSON_I2C_CR_REG); + i2c_writeb(i2c_readb(LOONGSON_I2C_CTR_REG) & ~0x80, LOONGSON_I2C_CTR_REG); + i2c_writeb(0x2c, LOONGSON_I2C_PRER_LO_REG); + i2c_writeb(0x1, LOONGSON_I2C_PRER_HI_REG); + i2c_writeb(i2c_readb(LOONGSON_I2C_CTR_REG) | 0xe0, LOONGSON_I2C_CTR_REG); +} + +static int i2c_read(struct loongson_i2c_dev *dev, + unsigned char *buf, int count) +{ + int i; + unsigned long time_left; + + for (i = 0; i < count; i++) { + i2c_writeb((i == count - 1)? + (CR_READ | CR_ACK) : CR_READ, + LOONGSON_I2C_CR_REG); + time_left = wait_for_completion_timeout( + &dev->cmd_complete, + (&dev->adapter)->timeout); + if (!time_left) { + pr_info("Timeout abort message cmd\n"); + return -1; + } + + buf[i] = i2c_readb(LOONGSON_I2C_RXR_REG); + i2c_debug("%s : read buf[%d] <= %02x\n", + __func__, __LINE__, i, buf[i]); + } + + return i; +} + +static int i2c_write(struct loongson_i2c_dev *dev, + unsigned char *buf, int count) +{ + int i; + unsigned long time_left; + + for (i = 0; i < count; i++) { + i2c_writeb(buf[i], LOONGSON_I2C_TXR_REG); + i2c_debug("%s : write buf[%d] => %02x\n", + __func__, __LINE__, i, buf[i]); + i2c_writeb(CR_WRITE, LOONGSON_I2C_CR_REG); + time_left = wait_for_completion_timeout( + &dev->cmd_complete, + (&dev->adapter)->timeout); + if (!time_left) { + pr_info("Timeout abort message cmd\n"); + return -1; + } + + if (i2c_readb(LOONGSON_I2C_SR_REG) & SR_NOACK) { + i2c_debug("%s : device no ack\n", + __func__, __LINE__); + if (i2c_stop(dev) < 0) + return -1; + return 0; + } + } + + return i; +} + +static int i2c_doxfer(struct loongson_i2c_dev *dev, + struct i2c_msg *msgs, int num) +{ + struct i2c_msg *m = msgs; + int i, err; + + for (i = 0; i < num; i++) { + reinit_completion(&dev->cmd_complete); + err = i2c_start(dev, m->addr, m->flags); + if (err <= 0) + return err; + + if (m->flags & I2C_M_RD) { + if (i2c_read(dev, m->buf, m->len) < 0) + return -1; + } else { + if (i2c_write(dev, m->buf, m->len) < 0) + return -1; + } + ++m; + if (!repeated_start && i2c_stop(dev) < 0) + return -1; + } + if (repeated_start && i2c_stop(dev) < 0) + return -1; + return i; +} + +static int i2c_xfer(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num) +{ + int ret; + int retry; + struct loongson_i2c_dev *dev; + + dev = i2c_get_adapdata(adap); + for (retry = 0; retry < adap->retries; retry++) { + ret = i2c_doxfer(dev, msgs, num); + if (ret != -EAGAIN) + return ret; + + udelay(100); + } + + return -EREMOTEIO; +} + +static unsigned int i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm loongson_i2c_algo = { + .master_xfer = i2c_xfer, + .functionality = i2c_func, +#if IS_ENABLED(CONFIG_I2C_SLAVE) + .reg_slave = loongson_i2c_reg_slave, + .unreg_slave = loongson_i2c_unreg_slave, +#endif /* CONFIG_I2C_SLAVE */ +}; + +#if IS_ENABLED(CONFIG_I2C_SLAVE) +static bool loongson_i2c_slave_irq(struct loongson_i2c_dev *dev) +{ + u32 stat; + struct i2c_client *slave = dev->slave; + u8 value; + + stat = i2c_readb(LOONGSON_I2C_SR_REG); + + /* Slave was requested, restart state machine. */ + if (stat & SR_SLAVE_ADDRESSED) { + dev->slave_state = LOONGSON_I2C_SLAVE_START; + i2c_writeb(CTR_RXROK | CTR_IEN, LOONGSON_I2C_CTR_REG); + } + + /* Slave is not currently active, irq was for someone else. */ + if (dev->slave_state == LOONGSON_I2C_SLAVE_STOP) { + return IRQ_NONE; + } + + /* Handle address frame. */ + if (dev->slave_state == LOONGSON_I2C_SLAVE_START) { + if (stat & SR_SLAVE_RW) //slave be read + dev->slave_state = + LOONGSON_I2C_SLAVE_READ_REQUESTED; + else + dev->slave_state = + LOONGSON_I2C_SLAVE_WRITE_REQUESTED; + } + + /* Slave was asked to stop. */ + if (stat & SR_NOACK) { + dev->slave_state = LOONGSON_I2C_SLAVE_STOP; + } + + value = i2c_readb(LOONGSON_I2C_RXR_REG); + switch (dev->slave_state) { + case LOONGSON_I2C_SLAVE_READ_REQUESTED: + dev->slave_state = LOONGSON_I2C_SLAVE_READ_PROCESSED; + i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value); + i2c_writeb(value, LOONGSON_I2C_TXR_REG); + i2c_writeb(CTR_TXROK | CTR_IEN, LOONGSON_I2C_CTR_REG); + break; + case LOONGSON_I2C_SLAVE_READ_PROCESSED: + i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value); + i2c_writeb(value, LOONGSON_I2C_TXR_REG); + i2c_writeb(CTR_TXROK | CTR_IEN, LOONGSON_I2C_CTR_REG); + break; + case LOONGSON_I2C_SLAVE_WRITE_REQUESTED: + dev->slave_state = LOONGSON_I2C_SLAVE_WRITE_RECEIVED; + i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value); + break; + case LOONGSON_I2C_SLAVE_WRITE_RECEIVED: + i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value); + i2c_writeb(CTR_RXROK | CTR_IEN, LOONGSON_I2C_CTR_REG); + break; + case LOONGSON_I2C_SLAVE_STOP: + i2c_slave_event(slave, I2C_SLAVE_STOP, &value); + i2c_writeb(0, LOONGSON_I2C_TXR_REG); + i2c_writeb(CTR_TXROK | CTR_IEN, LOONGSON_I2C_CTR_REG); + break; + default: + dev_err(dev->dev, "unhandled slave_state: %d\n", + dev->slave_state); + break; + } + +out: + return IRQ_HANDLED; +} +#endif /* CONFIG_I2C_SLAVE */ + +/* + * Interrupt service routine. This gets called whenever an I2C interrupt + * occurs. + */ +static irqreturn_t i2c_loongson_isr(int this_irq, void *dev_id) +{ + unsigned char iflag; + struct loongson_i2c_dev *dev = dev_id; + + iflag = i2c_readb(LOONGSON_I2C_SR_REG); + + if (iflag & SR_IF) { + i2c_writeb(CR_IACK, LOONGSON_I2C_CR_REG); +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (dev->slave) { + loongson_i2c_slave_irq(dev); + } +#endif + if (!(iflag & SR_TIP)) + complete(&dev->cmd_complete); + } else + return IRQ_NONE; + + return IRQ_HANDLED; +} + +static int loongson_i2c_probe(struct platform_device *pdev) +{ + struct loongson_i2c_dev *dev; + struct i2c_adapter *adap; + struct resource *mem, *ioarea; + int r, irq; + + /* NOTE: driver uses the static register mapping */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -ENODEV; + } + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(&pdev->dev, "no irq resource?\n"); + return -ENODEV; + } + + ioarea = request_mem_region(mem->start, resource_size(mem), + pdev->name); + if (!ioarea) { + dev_err(&pdev->dev, "I2C region already claimed\n"); + return -EBUSY; + } + + dev = kzalloc(sizeof(struct loongson_i2c_dev), GFP_KERNEL); + if (!dev) { + r = -ENOMEM; + goto err_release_region; + } + + init_completion(&dev->cmd_complete); + + dev->dev = &pdev->dev; + dev->irq = irq; + dev->base = ioremap(mem->start, resource_size(mem)); + if (!dev->base) { + r = -ENOMEM; + goto err_free_mem; + } + + platform_set_drvdata(pdev, dev); + + loongson_i2c_reginit(dev); + + r = request_irq(dev->irq, i2c_loongson_isr, IRQF_SHARED, DRIVER_NAME, dev); + if (r) + dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq); + + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + adap->nr = pdev->id; + strlcpy(adap->name, pdev->name, sizeof(adap->name)); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_HWMON; + adap->retries = 5; + adap->algo = &loongson_i2c_algo; + adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; + ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev)); + adap->timeout = msecs_to_jiffies(100); + + /* i2c device drivers may be active on return from add_adapter() */ + r = i2c_add_adapter(adap); + if (r) { + dev_err(dev->dev, "failure adding adapter\n"); + goto err_iounmap; + } + + return 0; + +err_iounmap: + iounmap(dev->base); +err_free_mem: + platform_set_drvdata(pdev, NULL); + kfree(dev); +err_release_region: + release_mem_region(mem->start, resource_size(mem)); + + return r; +} + +static int loongson_i2c_remove(struct platform_device *pdev) +{ + struct loongson_i2c_dev *dev = platform_get_drvdata(pdev); + struct resource *mem; + + platform_set_drvdata(pdev, NULL); + i2c_del_adapter(&dev->adapter); + iounmap(dev->base); + kfree(dev); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, resource_size(mem)); + return 0; +} + +#ifdef CONFIG_PM +static int loongson_i2c_suspend_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct loongson_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + + i2c_dev->suspended = 1; + + return 0; +} + +static int loongson_i2c_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct loongson_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + + i2c_dev->suspended = 0; + loongson_i2c_reginit(i2c_dev); + + return 0; +} + +static const struct dev_pm_ops loongson_i2c_dev_pm_ops = { + .suspend_noirq = loongson_i2c_suspend_noirq, + .resume = loongson_i2c_resume, +}; + +#define LOONGSON_DEV_PM_OPS (&loongson_i2c_dev_pm_ops) +#else +#define LOONGSON_DEV_PM_OPS NULL +#endif + +#ifdef CONFIG_OF +static struct of_device_id loongson_i2c_id_table[] = { + {.compatible = "loongson,ls7a-i2c"}, + {}, +}; +MODULE_DEVICE_TABLE(of, loongson_i2c_id_table); +#endif +static const struct acpi_device_id loongson_i2c_acpi_match[] = { + {"LOON0004"}, + {} +}; +MODULE_DEVICE_TABLE(acpi, loongson_i2c_acpi_match); + +static struct platform_driver loongson_i2c_driver = { + .probe = loongson_i2c_probe, + .remove = loongson_i2c_remove, + .driver = { + .name = "loongson-i2c", + .owner = THIS_MODULE, + .pm = LOONGSON_DEV_PM_OPS, +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(loongson_i2c_id_table), +#endif + .acpi_match_table = ACPI_PTR(loongson_i2c_acpi_match), + }, +}; + +static int __init loongson_i2c_init_driver(void) +{ + return platform_driver_register(&loongson_i2c_driver); +} +subsys_initcall(loongson_i2c_init_driver); + +static void __exit loongson_i2c_exit_driver(void) +{ + platform_driver_unregister(&loongson_i2c_driver); +} +module_exit(loongson_i2c_exit_driver); + +MODULE_AUTHOR("Loongson Technology Corporation Limited"); +MODULE_DESCRIPTION("Loongson LOONGSON I2C bus adapter"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:loongson-i2c"); -- Gitee From bf9f7ff72ab8ed09c19e292c5e850dfaba45589f Mon Sep 17 00:00:00 2001 From: Juxin Gao Date: Wed, 31 Aug 2022 11:48:00 +0800 Subject: [PATCH 27/56] spi: loongson: Add LS7A spi driver support Change-Id: Ib4552613f7c7706a0773839ddec7384d4a1d40a0 Signed-off-by: Juxin Gao --- drivers/spi/Kconfig | 7 + drivers/spi/Makefile | 1 + drivers/spi/spi-loongson.c | 540 +++++++++++++++++++++++++++++++++++++ 3 files changed, 548 insertions(+) create mode 100644 drivers/spi/spi-loongson.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 1bc68c0547c1..2c98fe65ab1a 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -1030,6 +1030,13 @@ config SPI_TLE62X0 sysfs interface, with each line presented as a kind of GPIO exposing both switch control and diagnostic feedback. +config SPI_LOONGSON + tristate "Loongson SPI Controller Support" + depends on LOONGARCH + default m + help + This is the driver for Loongson spi master controller. + # # Add new SPI protocol masters in alphabetical order above this line # diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index e1f88bd47ded..f21f7fd3673c 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -137,3 +137,4 @@ obj-$(CONFIG_SPI_AMD) += spi-amd.o # SPI slave protocol handlers obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o obj-$(CONFIG_SPI_SLAVE_SYSTEM_CONTROL) += spi-slave-system-control.o +obj-$(CONFIG_SPI_LOONGSON) += spi-loongson.o diff --git a/drivers/spi/spi-loongson.c b/drivers/spi/spi-loongson.c new file mode 100644 index 000000000000..dd5812bedfd2 --- /dev/null +++ b/drivers/spi/spi-loongson.c @@ -0,0 +1,540 @@ +/* + * Loongson SPI driver + * + * Copyright (C) 2013 Loongson Technology Corporation Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*define spi register */ +#define SPCR 0x00 +#define SPSR 0x01 +#define FIFO 0x02 +#define SPER 0x03 +#define PARA 0x04 +#define SPCS 0x04 +#define SFCS 0x05 +#define TIMI 0x06 + +#define PARA_MEM_EN 0x01 +#define SPSR_SPIF 0x80 +#define SPSR_WCOL 0x40 +#define SPCR_SPE 0x40 + +extern unsigned long bus_clock; +struct loongson_spi { + struct work_struct work; + spinlock_t lock; + + struct list_head msg_queue; + struct spi_master *master; + void __iomem *base; + int cs_active; + unsigned int hz; + unsigned char spcr, sper, spsr; + unsigned char para, sfcs, timi; + struct workqueue_struct *wq; + unsigned int mode; +} *loongson_spi_dev; + +static inline int set_cs(struct loongson_spi *loongson_spi, struct spi_device *spi, int val); + +static void loongson_spi_write_reg(struct loongson_spi *spi, + unsigned char reg, unsigned char data) +{ + writeb(data, spi->base +reg); +} + +static char loongson_spi_read_reg(struct loongson_spi *spi, + unsigned char reg) +{ + return readb(spi->base + reg); +} + +static int loongson_spi_update_state(struct loongson_spi *loongson_spi,struct spi_device *spi, + struct spi_transfer *t) +{ + unsigned int hz; + unsigned int div, div_tmp; + unsigned int bit; + unsigned long clk; + unsigned char val; + const char rdiv[12] = {0, 1, 4, 2, 3, 5, 6, 7, 8, 9, 10, 11}; + + hz = t ? t->speed_hz : spi->max_speed_hz; + + if (!hz) + hz = spi->max_speed_hz; + + if ((hz && loongson_spi->hz != hz) || ((spi->mode ^ loongson_spi->mode) & (SPI_CPOL | SPI_CPHA))) { + clk = 100000000; + div = DIV_ROUND_UP(clk, hz); + + if (div < 2) + div = 2; + + if (div > 4096) + div = 4096; + + bit = fls(div) - 1; + if ((1<dev, "clk = %ld hz = %d div_tmp = %d bit = %d\n", + clk, hz, div_tmp, bit); + + loongson_spi->hz = hz; + loongson_spi->spcr = div_tmp & 3; + loongson_spi->sper = (div_tmp >> 2) & 3; + + val = loongson_spi_read_reg(loongson_spi, SPCR); + val &= ~0xc; + if (spi->mode & SPI_CPOL) + val |= 8; + if (spi->mode & SPI_CPHA) + val |= 4; + loongson_spi_write_reg(loongson_spi, SPCR, (val & ~3) | loongson_spi->spcr); + val = loongson_spi_read_reg(loongson_spi, SPER); + loongson_spi_write_reg(loongson_spi, SPER, (val & ~3) | loongson_spi->sper); + loongson_spi->mode &= SPI_NO_CS; + loongson_spi->mode |= spi->mode; + } + + return 0; +} + + + +static int loongson_spi_setup(struct spi_device *spi) +{ + struct loongson_spi *loongson_spi; + + loongson_spi = spi_master_get_devdata(spi->master); + if (spi->bits_per_word %8) + return -EINVAL; + + if(spi->chip_select >= spi->master->num_chipselect) + return -EINVAL; + + loongson_spi_update_state(loongson_spi, spi, NULL); + + set_cs(loongson_spi, spi, 1); + + return 0; +} + +static int loongson_spi_write_read_8bit( struct spi_device *spi, + const u8 **tx_buf, u8 **rx_buf, unsigned int num) +{ + struct loongson_spi *loongson_spi; + loongson_spi = spi_master_get_devdata(spi->master); + + if (tx_buf && *tx_buf){ + loongson_spi_write_reg(loongson_spi, FIFO, *((*tx_buf)++)); + while((loongson_spi_read_reg(loongson_spi, SPSR) & 0x1) == 1); + }else{ + loongson_spi_write_reg(loongson_spi, FIFO, 0); + while((loongson_spi_read_reg(loongson_spi, SPSR) & 0x1) == 1); + } + + if (rx_buf && *rx_buf) { + *(*rx_buf)++ = loongson_spi_read_reg(loongson_spi, FIFO); + }else{ + loongson_spi_read_reg(loongson_spi, FIFO); + } + + return 1; +} + + +static unsigned int loongson_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer) +{ + struct loongson_spi *loongson_spi; + unsigned int count; + const u8 *tx = xfer->tx_buf; + u8 *rx = xfer->rx_buf; + + loongson_spi = spi_master_get_devdata(spi->master); + count = xfer->len; + + do { + if (loongson_spi_write_read_8bit(spi, &tx, &rx, count) < 0) + goto out; + count--; + } while (count); + +out: + return xfer->len - count; + +} + +static inline int set_cs(struct loongson_spi *loongson_spi, struct spi_device *spi, int val) +{ + if (spi->mode & SPI_CS_HIGH) + val = !val; + if (loongson_spi->mode & SPI_NO_CS) { + loongson_spi_write_reg(loongson_spi, SPCS, val); + } else { + int cs = loongson_spi_read_reg(loongson_spi, SFCS) & ~(0x11 << spi->chip_select); + loongson_spi_write_reg(loongson_spi, SFCS, (val ? (0x11 << spi->chip_select):(0x1 << spi->chip_select)) | cs); + } + return 0; +} + +static void loongson_spi_work(struct work_struct *work) +{ + struct loongson_spi *loongson_spi = + container_of(work, struct loongson_spi, work); + int param; + + spin_lock(&loongson_spi->lock); + param = loongson_spi_read_reg(loongson_spi, PARA); + loongson_spi_write_reg(loongson_spi, PARA, param&~1); + while (!list_empty(&loongson_spi->msg_queue)) { + + struct spi_message *m; + struct spi_device *spi; + struct spi_transfer *t = NULL; + + m = container_of(loongson_spi->msg_queue.next, struct spi_message, queue); + + list_del_init(&m->queue); + spin_unlock(&loongson_spi->lock); + + spi = m->spi; + + /*in here set cs*/ + set_cs(loongson_spi, spi, 0); + + list_for_each_entry(t, &m->transfers, transfer_list) { + + /*setup spi clock*/ + loongson_spi_update_state(loongson_spi, spi, t); + + if (t->len) + m->actual_length += + loongson_spi_write_read(spi, t); + } + + set_cs(loongson_spi, spi, 1); + m->complete(m->context); + + + spin_lock(&loongson_spi->lock); + } + + loongson_spi_write_reg(loongson_spi, PARA, param); + spin_unlock(&loongson_spi->lock); +} + + + +static int loongson_spi_transfer(struct spi_device *spi, struct spi_message *m) +{ + struct loongson_spi *loongson_spi; + struct spi_transfer *t = NULL; + + m->actual_length = 0; + m->status = 0; + if (list_empty(&m->transfers) || !m->complete) + return -EINVAL; + + loongson_spi = spi_master_get_devdata(spi->master); + + list_for_each_entry(t, &m->transfers, transfer_list) { + + if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { + dev_err(&spi->dev, + "message rejected : " + "invalid transfer data buffers\n"); + goto msg_rejected; + } + /*other things not check*/ + } + + spin_lock(&loongson_spi->lock); + list_add_tail(&m->queue, &loongson_spi->msg_queue); + queue_work(loongson_spi->wq, &loongson_spi->work); + spin_unlock(&loongson_spi->lock); + + return 0; +msg_rejected: + + m->status = -EINVAL; + if (m->complete) + m->complete(m->context); + return -EINVAL; +} + +static void loongson_spi_reginit(void) +{ + unsigned char val; + + val = loongson_spi_read_reg(loongson_spi_dev, SPCR); + val &= ~SPCR_SPE; + loongson_spi_write_reg(loongson_spi_dev, SPCR, val); + + loongson_spi_write_reg(loongson_spi_dev, SPSR, (SPSR_SPIF | SPSR_WCOL)); + + val = loongson_spi_read_reg(loongson_spi_dev, SPCR); + val |= SPCR_SPE; + loongson_spi_write_reg(loongson_spi_dev, SPCR, val); +} + +static int loongson_spi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct loongson_spi *spi; + struct resource *res; + int ret; + master = spi_alloc_master(&pdev->dev, sizeof(struct loongson_spi)); + + if (master == NULL) { + dev_dbg(&pdev->dev, "master allocation failed\n"); + return-ENOMEM; + } + + if (pdev->id != -1) + master->bus_num = pdev->id; + + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH ; + master->setup = loongson_spi_setup; + master->transfer = loongson_spi_transfer; + master->num_chipselect = 4; +#ifdef CONFIG_OF + master->dev.of_node = of_node_get(pdev->dev.of_node); +#endif + dev_set_drvdata(&pdev->dev, master); + + spi = spi_master_get_devdata(master); + + loongson_spi_dev = spi; + + spi->wq = create_singlethread_workqueue(pdev->name); + + spi->master = master; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); + ret = -ENOENT; + goto free_master; + } + + spi->base = ioremap(res->start, (res->end - res->start)+1); + if (spi->base == NULL) { + dev_err(&pdev->dev, "Cannot map IO\n"); + ret = -ENXIO; + goto unmap_io; + } + + loongson_spi_reginit(); + + spi->mode = 0; + if (of_get_property(pdev->dev.of_node, "spi-nocs", NULL)) + spi->mode |= SPI_NO_CS; + + INIT_WORK(&spi->work, loongson_spi_work); + + spin_lock_init(&spi->lock); + INIT_LIST_HEAD(&spi->msg_queue); + + ret = spi_register_master(master); + if (ret < 0) + goto unmap_io; + + return ret; + +unmap_io: + iounmap(spi->base); +free_master: + kfree(master); + spi_master_put(master); + return ret; + +} + +#ifdef CONFIG_PM +static int loongson_spi_suspend(struct device *dev) +{ + struct loongson_spi *loongson_spi; + struct spi_master *master; + + master = dev_get_drvdata(dev); + loongson_spi = spi_master_get_devdata(master); + + loongson_spi->spcr = loongson_spi_read_reg(loongson_spi, SPCR); + loongson_spi->sper = loongson_spi_read_reg(loongson_spi, SPER); + loongson_spi->spsr = loongson_spi_read_reg(loongson_spi, SPSR); + loongson_spi->para = loongson_spi_read_reg(loongson_spi, PARA); + loongson_spi->sfcs = loongson_spi_read_reg(loongson_spi, SFCS); + loongson_spi->timi = loongson_spi_read_reg(loongson_spi, TIMI); + + return 0; +} + +static int loongson_spi_resume(struct device *dev) +{ + struct loongson_spi *loongson_spi; + struct spi_master *master; + + master = dev_get_drvdata(dev); + loongson_spi = spi_master_get_devdata(master); + + loongson_spi_write_reg(loongson_spi, SPCR, loongson_spi->spcr); + loongson_spi_write_reg(loongson_spi, SPER, loongson_spi->sper); + loongson_spi_write_reg(loongson_spi, SPSR, loongson_spi->spsr); + loongson_spi_write_reg(loongson_spi, PARA, loongson_spi->para); + loongson_spi_write_reg(loongson_spi, SFCS, loongson_spi->sfcs); + loongson_spi_write_reg(loongson_spi, TIMI, loongson_spi->timi); + + return 0; +} + +static const struct dev_pm_ops loongson_spi_dev_pm_ops = { + .suspend = loongson_spi_suspend, + .resume = loongson_spi_resume, +}; + +#define LS_DEV_PM_OPS (&loongson_spi_dev_pm_ops) +#else +#define LS_DEV_PM_OPS NULL +#endif + + +#ifdef CONFIG_OF +static struct of_device_id loongson_spi_id_table[] = { + { .compatible = "loongson,ls7a-spi", }, + { }, +}; +MODULE_DEVICE_TABLE(of, loongson_spi_id_table); +#endif +static struct platform_driver loongson_spi_driver = { + .probe = loongson_spi_probe, + .driver = { + .name = "loongson-spi", + .owner = THIS_MODULE, + .bus = &platform_bus_type, + .pm = LS_DEV_PM_OPS, +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(loongson_spi_id_table), +#endif + }, +}; + +#ifdef CONFIG_PCI +static struct resource loongson_spi_resources[] = { + [0] = { + .flags = IORESOURCE_MEM, + }, + [1] = { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device loongson_spi_device = { + .name = "loongson-spi", + .id = 0, + .num_resources = ARRAY_SIZE(loongson_spi_resources), + .resource = loongson_spi_resources, +}; + + +static int loongson_spi_pci_register(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int ret; + unsigned char v8; + + pr_debug("loongson_spi_pci_register BEGIN\n"); + /* Enable device in PCI config */ + ret = pci_enable_device(pdev); + if (ret < 0) { + printk(KERN_ERR "loongson-pci (%s): Cannot enable PCI device\n", + pci_name(pdev)); + goto err_out; + } + + /* request the mem regions */ + ret = pci_request_region(pdev, 0, "loongson-spi io"); + if (ret < 0) { + printk( KERN_ERR "loongson-spi (%s): cannot request region 0.\n", + pci_name(pdev)); + goto err_out; + } + + loongson_spi_resources[0].start = pci_resource_start (pdev, 0); + loongson_spi_resources[0].end = pci_resource_end(pdev, 0); + /* need api from pci irq */ + ret = pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &v8); + + if (ret == PCIBIOS_SUCCESSFUL) { + + loongson_spi_resources[1].start = v8; + loongson_spi_resources[1].end = v8; + platform_device_register(&loongson_spi_device); + } + +err_out: + return ret; +} + +static void loongson_spi_pci_unregister(struct pci_dev *pdev) +{ + pci_release_region(pdev, 0); +} + +static struct pci_device_id loongson_spi_devices[] = { + {PCI_DEVICE(0x14, 0x7a0b)}, + {0, 0, 0, 0, 0, 0, 0} +}; + +static struct pci_driver loongson_spi_pci_driver = { + .name = "loongson-spi-pci", + .id_table = loongson_spi_devices, + .probe = loongson_spi_pci_register, + .remove = loongson_spi_pci_unregister, +}; +#endif + + +static int __init loongson_spi_init(void) +{ + int ret; + + ret = platform_driver_register(&loongson_spi_driver); +#ifdef CONFIG_PCI + if(!ret) + ret = pci_register_driver(&loongson_spi_pci_driver); +#endif + return ret; +} + +static void __exit loongson_spi_exit(void) +{ + platform_driver_unregister(&loongson_spi_driver); +#ifdef CONFIG_PCI + pci_unregister_driver(&loongson_spi_pci_driver); +#endif +} + +subsys_initcall(loongson_spi_init); +module_exit(loongson_spi_exit); + +MODULE_AUTHOR("Loongson Technology Corporation Limited"); +MODULE_DESCRIPTION("Loongson SPI driver"); +MODULE_LICENSE("GPL"); -- Gitee From 3bc842ba29826762ee3992d2e1f1ecc945645492 Mon Sep 17 00:00:00 2001 From: zhangtianyang Date: Mon, 19 Sep 2022 20:07:07 +0800 Subject: [PATCH 28/56] ACPI:Support ACPI_MACHINE_WIDTH for 64 Signed-off-by: zhangtianyang Change-Id: Ie79e0c0a3ebcc1c9016b2be1e434ad3d57bf334f --- include/acpi/platform/aclinux.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index 72f52a1342a0..1a60441ccc16 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -194,7 +194,7 @@ #if defined(__ia64__) || (defined(__x86_64__) && !defined(__ILP32__)) ||\ defined(__aarch64__) || defined(__PPC64__) ||\ - defined(__s390x__) ||\ + defined(__s390x__) || defined(__loongarch__)\ (defined(__riscv) && (defined(__LP64__) || defined(_LP64))) #define ACPI_MACHINE_WIDTH 64 #define COMPILER_DEPENDENT_INT64 long -- Gitee From 033b8a86e5989957aad2a34f0bc47704703eec18 Mon Sep 17 00:00:00 2001 From: zhangtianyang Date: Fri, 23 Sep 2022 16:03:11 +0800 Subject: [PATCH 29/56] LoongArch:Adapted SECTION_SIZE_BITS with page size Signed-off-by: zhangtianyang Change-Id: Ib32d4a2fc458fedd939e76e86d8bdddfe3c43914 --- arch/loongarch/include/asm/sparsemem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/loongarch/include/asm/sparsemem.h b/arch/loongarch/include/asm/sparsemem.h index ee55cdf933c6..0f6e76fb3174 100644 --- a/arch/loongarch/include/asm/sparsemem.h +++ b/arch/loongarch/include/asm/sparsemem.h @@ -8,7 +8,7 @@ * SECTION_SIZE_BITS 2^N: how big each section will be * MAX_PHYSMEM_BITS 2^N: how much memory we can have in that space */ -#define SECTION_SIZE_BITS 29 /* 2^29 = Largest Huge Page Size */ +#define SECTION_SIZE_BITS 28 #define MAX_PHYSMEM_BITS 48 #ifndef CONFIG_SPARSEMEM_VMEMMAP -- Gitee From 4cdbe4791eefb824bce46327094f6a24d20e9423 Mon Sep 17 00:00:00 2001 From: zhangtianyang Date: Mon, 19 Sep 2022 10:19:31 +0800 Subject: [PATCH 30/56] platform:Support loongson laptop generic driver Signed-off-by: zhangtianyang Change-Id: Ide7fce6caeffea9b001afd43cfaccfef1889d3a5 --- drivers/platform/Kconfig | 1 + drivers/platform/Makefile | 1 + drivers/platform/loongarch/Kconfig | 30 + drivers/platform/loongarch/Makefile | 1 + .../loongarch/loongson_generic_laptop.c | 663 ++++++++++++++++++ 5 files changed, 696 insertions(+) create mode 100644 drivers/platform/loongarch/Kconfig create mode 100644 drivers/platform/loongarch/Makefile create mode 100644 drivers/platform/loongarch/loongson_generic_laptop.c diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig index 971426bb4302..47063d4f4deb 100644 --- a/drivers/platform/Kconfig +++ b/drivers/platform/Kconfig @@ -13,3 +13,4 @@ source "drivers/platform/chrome/Kconfig" source "drivers/platform/mellanox/Kconfig" source "drivers/platform/olpc/Kconfig" +source "drivers/platform/loongarch/Kconfig" diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile index 6fda58c021ca..3cb2af6ff64a 100644 --- a/drivers/platform/Makefile +++ b/drivers/platform/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_MIPS) += mips/ obj-$(CONFIG_OLPC_EC) += olpc/ obj-$(CONFIG_GOLDFISH) += goldfish/ obj-$(CONFIG_CHROME_PLATFORMS) += chrome/ +obj-$(CONFIG_LOONGARCH) += loongarch/ diff --git a/drivers/platform/loongarch/Kconfig b/drivers/platform/loongarch/Kconfig new file mode 100644 index 000000000000..f444d41fd03a --- /dev/null +++ b/drivers/platform/loongarch/Kconfig @@ -0,0 +1,30 @@ +# +# LOONGARCH Platform Specific Drivers +# + +menuconfig LOONGARCH_PLATFORM_DEVICES + bool "LOONGARCH Platform Specific Device Drivers" + default y + help + Say Y here to get to see options for device drivers of various + LOONGARCH platforms, including vendor-specific netbook/laptop/desktop + extension and hardware monitor drivers. This option itself does + not add any kernel code. + + If you say N, all options in this submenu will be skipped and disabled. + +if LOONGARCH_PLATFORM_DEVICES + +config LOONGSON_GENERIC_LAPTOP + tristate "Generic Loongson-3 Laptop Driver" + depends on ACPI + depends on BACKLIGHT_CLASS_DEVICE + depends on INPUT + depends on MACH_LOONGSON64 + select ACPI_VIDEO + select INPUT_SPARSEKMAP + default y + help + ACPI-based Loongson-3 family laptops generic driver. + +endif # LOONGARCH_PLATFORM_DEVICES diff --git a/drivers/platform/loongarch/Makefile b/drivers/platform/loongarch/Makefile new file mode 100644 index 000000000000..f912a53b66a7 --- /dev/null +++ b/drivers/platform/loongarch/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_LOONGSON_GENERIC_LAPTOP) += loongson_generic_laptop.o diff --git a/drivers/platform/loongarch/loongson_generic_laptop.c b/drivers/platform/loongarch/loongson_generic_laptop.c new file mode 100644 index 000000000000..41520e90a98e --- /dev/null +++ b/drivers/platform/loongarch/loongson_generic_laptop.c @@ -0,0 +1,663 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * loongson_generic_laptop.c - Loongson processor + * based LAPTOP/ALL-IN-ONE driver + * + * lvjianmin + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define LSACPI_VERSION "1.0" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* ACPI HIDs */ +#define ACPI_LOONGSON_HKEY_HID "LOON0000" +#define ACPI_EC_HID "PNP0C09" + +/**************************************************************************** + * Main driver + */ + +#define LSACPI_NAME "loongson-laptop" +#define LSACPI_DESC "Loongson Laptop/all-in-one ACPI Driver" +#define LSACPI_FILE LSACPI_NAME "_acpi" +#define LSACPI_DRVR_NAME LSACPI_FILE +#define LSACPI_ACPI_EVENT_PREFIX "loongson_generic" +/**************************************************************************** + * Driver-wide structs and misc. variables + */ + +struct generic_sub_driver { + u32 type; + char *name; + acpi_handle *handle; + struct acpi_device *device; + struct platform_driver *driver; + int (*init)(struct generic_sub_driver *sub_driver); + void (*notify)(struct generic_sub_driver *sub_driver, u32 event); + u8 acpi_notify_installed; +}; + +static u32 input_device_registered; + +static int hotkey_status_get(int *status); + +static int loongson_laptop_backlight_update(struct backlight_device *bd); +/**************************************************************************** + **************************************************************************** + * + * ACPI Helpers and device model + * + **************************************************************************** + ****************************************************************************/ + +/************************************************************************* + * ACPI basic handles + */ + +static int acpi_evalf(acpi_handle handle, + int *res, char *method, char *fmt, ...); +static acpi_handle hkey_handle; + +static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data) +{ + struct generic_sub_driver *sub_driver = data; + + if (!sub_driver || !sub_driver->notify) + return; + sub_driver->notify(sub_driver, event); +} + +static int __init setup_acpi_notify(struct generic_sub_driver *sub_driver) +{ + acpi_status status; + int rc; + + if (!*sub_driver->handle) + return 0; + + rc = acpi_bus_get_device(*sub_driver->handle, &sub_driver->device); + if (rc < 0) { + pr_err("acpi_bus_get_device(%s) failed: %d\n", + sub_driver->name, rc); + return -ENODEV; + } + + sub_driver->device->driver_data = sub_driver; + sprintf(acpi_device_class(sub_driver->device), "%s/%s", + LSACPI_ACPI_EVENT_PREFIX, sub_driver->name); + + status = acpi_install_notify_handler(*sub_driver->handle, + sub_driver->type, dispatch_acpi_notify, sub_driver); + if (ACPI_FAILURE(status)) { + if (status == AE_ALREADY_EXISTS) { + pr_notice("another device driver is already " + "handling %s events\n", sub_driver->name); + } else { + pr_err("acpi_install_notify_handler(%s) failed: %s\n", + sub_driver->name, acpi_format_exception(status)); + } + return -ENODEV; + } + sub_driver->acpi_notify_installed = 1; + return 0; +} + +static struct input_dev *generic_inputdev; + +#ifdef CONFIG_PM +static int loongson_hkey_suspend(struct device *dev) +{ + return 0; +} +static int loongson_hkey_resume(struct device *dev) +{ + int status = 0; + struct key_entry ke; + + struct backlight_device *bd; + bd = backlight_device_get_by_type(BACKLIGHT_PLATFORM); + if (bd) { + loongson_laptop_backlight_update(bd) ? + pr_warn("Loongson_backlight:resume brightness failed") : + pr_info("Loongson_backlight:resume brightness %d\n", bd->props.brightness); + } + /* + * Only if the firmware supports SW_LID event model, we can handle the + * event. This is for the consideration of development board without + * EC. + */ + if (test_bit(SW_LID, generic_inputdev->swbit)) { + if (hotkey_status_get(&status)) + return -EIO; + /* + * The input device sw element records the last lid status. + * When the system is awakened by other wake-up sources, + * the lid event will also be reported. The judgment of + * adding SW_LID bit which in sw element can avoid this + * case. + * + * input system will drop lid event when current lid event + * value and last lid status in the same data set,which + * data set inclue zero set and no zero set. so laptop + * driver doesn't report repeated events. + * + * Lid status is generally 0, but hardware exception is + * considered. So add lid status confirmation. + */ + if (test_bit(SW_LID, generic_inputdev->sw) && !(status & (1 << SW_LID))) { + ke.type = KE_SW; + ke.sw.value = (u8)status; + ke.sw.code = SW_LID; + sparse_keymap_report_entry(generic_inputdev, &ke, + 1, true); + } + } + + return 0; +} + +static const struct dev_pm_ops loongson_hkey_dev_pm_ops = { + .suspend_noirq = loongson_hkey_suspend, + .resume = loongson_hkey_resume, +}; + +#define LOONGSON_HKEY_DEV_PM_OPS (&loongson_hkey_dev_pm_ops) +#else +#define LOONGSON_HKEY_DEV_PM_OPS NULL +#endif +static int loongson_hkey_probe(struct platform_device *pdev) +{ + hkey_handle = ACPI_HANDLE(&pdev->dev); + + if (!hkey_handle) + return -ENODEV; + + return 0; +} + +static const struct acpi_device_id loongson_htk_device_ids[] = { + {ACPI_LOONGSON_HKEY_HID, 0}, + {"", 0}, +}; + +static struct platform_driver loongson_hkey_driver = { + .probe = loongson_hkey_probe, + .driver = { + .name = "loongson-hkey", + .owner = THIS_MODULE, + .pm = LOONGSON_HKEY_DEV_PM_OPS, + .acpi_match_table = ACPI_PTR(loongson_htk_device_ids), + }, +}; + +/* + * Loongson generic laptop firmware event model + * + */ + +#define GENERIC_HOTKEY_MAP_MAX 64 +#define METHOD_NAME__KMAP "KMAP" +static struct key_entry hotkey_keycode_map[GENERIC_HOTKEY_MAP_MAX]; +static int hkey_map(void) +{ + struct acpi_buffer buf; + union acpi_object *pack; + acpi_status status; + u32 index; + + buf.length = ACPI_ALLOCATE_BUFFER; + status = acpi_evaluate_object_typed(hkey_handle, + METHOD_NAME__KMAP, NULL, &buf, ACPI_TYPE_PACKAGE); + if (status != AE_OK) { + printk(KERN_ERR ": ACPI exception: %s\n", + acpi_format_exception(status)); + return -1; + } + pack = buf.pointer; + for (index = 0; index < pack->package.count; index++) { + union acpi_object *sub_pack = &pack->package.elements[index]; + union acpi_object *element = &sub_pack->package.elements[0]; + + hotkey_keycode_map[index].type = element->integer.value; + element = &sub_pack->package.elements[1]; + hotkey_keycode_map[index].code = element->integer.value; + element = &sub_pack->package.elements[2]; + hotkey_keycode_map[index].keycode = element->integer.value; + } + return 0; +} + +static int hotkey_backlight_set(bool enable) +{ + if (!acpi_evalf(hkey_handle, NULL, "VCBL", "vd", enable ? 1 : 0)) + return -EIO; + + return 0; +} +static int event_init(struct generic_sub_driver *sub_driver) +{ + int ret; + + ret = hkey_map(); + if (ret) { + printk(KERN_ERR "Fail to parse keymap from DSDT.\n"); + return ret; + } + + ret = sparse_keymap_setup(generic_inputdev, hotkey_keycode_map, NULL); + if (ret) { + printk(KERN_ERR "Fail to setup input device keymap\n"); + input_free_device(generic_inputdev); + + return ret; + } + + /* + * This hotkey driver handle backlight event when + * acpi_video_get_backlight_type() gets acpi_backlight_vendor + */ + if (acpi_video_get_backlight_type() != acpi_backlight_vendor) + hotkey_backlight_set(false); + else + hotkey_backlight_set(true); + + printk("ACPI:enabling firmware HKEY event interface...\n"); + return ret; + +} + +#define GENERIC_EVENT_TYPE_OFF 12 +#define GENERIC_EVENT_MASK 0xFFF +#define TPACPI_MAX_ACPI_ARGS 3 +static int acpi_evalf(acpi_handle handle, + int *res, char *method, char *fmt, ...) +{ + char *fmt0 = fmt; + struct acpi_object_list params; + union acpi_object in_objs[TPACPI_MAX_ACPI_ARGS]; + struct acpi_buffer result, *resultp; + union acpi_object out_obj; + acpi_status status; + va_list ap; + char res_type; + int success; + int quiet; + + if (!*fmt) { + pr_err("acpi_evalf() called with empty format\n"); + return 0; + } + + if (*fmt == 'q') { + quiet = 1; + fmt++; + } else + quiet = 0; + + res_type = *(fmt++); + + params.count = 0; + params.pointer = &in_objs[0]; + + va_start(ap, fmt); + while (*fmt) { + char c = *(fmt++); + switch (c) { + case 'd': /* int */ + in_objs[params.count].integer.value = va_arg(ap, int); + in_objs[params.count++].type = ACPI_TYPE_INTEGER; + break; + /* add more types as needed */ + default: + pr_err("acpi_evalf() called with invalid format character '%c'\n", + c); + va_end(ap); + return 0; + } + } + va_end(ap); + + if (res_type != 'v') { + result.length = sizeof(out_obj); + result.pointer = &out_obj; + resultp = &result; + } else + resultp = NULL; + + status = acpi_evaluate_object(handle, method, ¶ms, resultp); + + switch (res_type) { + case 'd': /* int */ + success = (status == AE_OK && + out_obj.type == ACPI_TYPE_INTEGER); + if (success && res) + *res = out_obj.integer.value; + break; + case 'v': /* void */ + success = status == AE_OK; + break; + /* add more types as needed */ + default: + pr_err("acpi_evalf() called with invalid format character '%c'\n", + res_type); + return 0; + } + + if (!success && !quiet) + pr_err("acpi_evalf(%s, %s, ...) failed: %s\n", + method, fmt0, acpi_format_exception(status)); + + return success; +} + +int ec_get_brightness(void) +{ + int status = 0; + + if (!hkey_handle) + return -ENXIO; + + if (!acpi_evalf(hkey_handle, &status, "ECBG", "d")) + return -EIO; + + if (status < 0) + return status; + + return status; +} +EXPORT_SYMBOL(ec_get_brightness); + +int ec_set_brightness(int level) +{ + + int ret = 0; + if (!hkey_handle) + return -ENXIO; + + if (!acpi_evalf(hkey_handle, NULL, "ECBS", "vd", level)) + ret = -EIO; + + return ret; +} +EXPORT_SYMBOL(ec_set_brightness); + +int ec_bl_level(u8 level) +{ + int status = 0; + + if (!hkey_handle) + return -ENXIO; + + if (!acpi_evalf(hkey_handle, &status, "ECLL", "d")) + return -EIO; + if (status < 0 || level > status) + return status; + + if (!acpi_evalf(hkey_handle, &status, "ECSL", "d")) + return -EIO; + + if (status < 0 || level < status) + return status; + + return level; +} +EXPORT_SYMBOL(ec_bl_level); + +static int loongson_laptop_backlight_update(struct backlight_device *bd) +{ + int lvl = ec_bl_level(bd->props.brightness); + if (lvl < 0) + return -EIO; + if (ec_set_brightness(lvl)) + return -EIO; + return 0; +} + +static int loongson_laptop_get_brightness(struct backlight_device *bd) +{ + u8 __maybe_unused level; + + level = ec_get_brightness(); + if (level >= 0) + return level; + return -EIO; +} + +static const struct backlight_ops ls_backlight_laptop_ops = { + .update_status = loongson_laptop_backlight_update, + .get_brightness = loongson_laptop_get_brightness, +}; + +static int ls_laptop_backlight_register(void) +{ + struct backlight_properties props; + int status = 0; + memset(&props, 0, sizeof(props)); + props.type = BACKLIGHT_PLATFORM; + + if (!acpi_evalf(hkey_handle, &status, "ECLL", "d")) + return -EIO; + props.max_brightness = status; + props.brightness = 1; + + if (backlight_device_register("loongson_laptop", + NULL, NULL, + &ls_backlight_laptop_ops, &props)) + return 0; + + return -EIO; +} + +static int hotkey_status_get(int *status) +{ + if (!acpi_evalf(hkey_handle, status, "GSWS", "d")) + return -EIO; + + return 0; +} +int turn_off_lvds(void) +{ + int status; + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list args = { 1, &arg0 }; + arg0.integer.value = 0; + status = acpi_evaluate_object(NULL, "\\BLSW", &args, NULL); + if (ACPI_FAILURE(status)) { + pr_info("Loongson lvds error:0x%x\n", status); + return -ENODEV; + } + return 0; +} + +int turn_on_lvds(void) +{ + int status; + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list args = { 1, &arg0 }; + arg0.integer.value = 1; + status = acpi_evaluate_object(NULL, "\\BLSW", &args, NULL); + if (ACPI_FAILURE(status)) { + pr_info("Loongson lvds error:0x%x\n", status); + return -ENODEV; + } + return 0; +} +static void event_notify(struct generic_sub_driver *sub_driver, u32 event) +{ + struct key_entry *ke = NULL; + int scan_code = event & GENERIC_EVENT_MASK; + int type = (event >> GENERIC_EVENT_TYPE_OFF) & 0xF; + + ke = sparse_keymap_entry_from_scancode(generic_inputdev, scan_code); + if (ke) { + if (type == KE_SW) { + int status = 0; + + if (hotkey_status_get(&status)) + return; + ke->sw.value = !!(status & (1 << ke->sw.code)); + } + sparse_keymap_report_entry(generic_inputdev, ke, 1, true); + } +} + +/**************************************************************************** + **************************************************************************** + * + * Infrastructure + * + **************************************************************************** + ****************************************************************************/ +static void generic_exit(struct generic_sub_driver *sub_driver) +{ + + if (sub_driver->acpi_notify_installed) { + acpi_remove_notify_handler(*sub_driver->handle, + sub_driver->type, dispatch_acpi_notify); + sub_driver->acpi_notify_installed = 0; + } +} + +static int __init generic_subdriver_init(struct generic_sub_driver *sub_driver) +{ + int ret; + + if (!sub_driver || !sub_driver->driver) + return -EINVAL; + + ret = platform_driver_register(sub_driver->driver); + if (ret) + return -EINVAL; + + if (sub_driver->init) + sub_driver->init(sub_driver); + + if (sub_driver->notify) { + ret = setup_acpi_notify(sub_driver); + if (ret == -ENODEV) { + ret = 0; + goto err_out; + } + if (ret < 0) + goto err_out; + } + + return 0; + +err_out: + generic_exit(sub_driver); + return (ret < 0) ? ret : 0; +} + +/* Module init, exit, parameters */ +static struct generic_sub_driver generic_sub_drivers[] = { + { + .name = "hkey", + .init = event_init, + .notify = event_notify, + .handle = &hkey_handle, + .type = ACPI_DEVICE_NOTIFY, + .driver = &loongson_hkey_driver, + }, +}; + +static void generic_acpi_module_exit(void) +{ + if (generic_inputdev) { + if (input_device_registered) + input_unregister_device(generic_inputdev); + else + input_free_device(generic_inputdev); + } +} + +static int __init generic_acpi_module_init(void) +{ + int ret, i; + int status; + bool ec_found; + + if (acpi_disabled) + return -ENODEV; + + /* The EC device is required */ + ec_found = acpi_dev_found(ACPI_EC_HID); + if (!ec_found) + return -ENODEV; + + generic_inputdev = input_allocate_device(); + if (!generic_inputdev) { + pr_err("unable to allocate input device\n"); + generic_acpi_module_exit(); + return -ENOMEM; + } + + /* Prepare input device, but don't register */ + generic_inputdev->name = + "Loongson Generic Laptop/All-in-one Extra Buttons"; + generic_inputdev->phys = LSACPI_DRVR_NAME "/input0"; + generic_inputdev->id.bustype = BUS_HOST; + generic_inputdev->dev.parent = NULL; + + /* Init subdrivers */ + for (i = 0; i < ARRAY_SIZE(generic_sub_drivers); i++) { + ret = generic_subdriver_init(&generic_sub_drivers[i]); + if (ret < 0) { + generic_acpi_module_exit(); + return ret; + } + } + + ret = input_register_device(generic_inputdev); + if (ret < 0) { + pr_err("unable to register input device\n"); + generic_acpi_module_exit(); + return ret; + } + + input_device_registered = 1; + + if (acpi_evalf(hkey_handle, &status, "ECBG", "d")) { + pr_info("Loongson Laptop used, init brightness is 0x%x\n", status); + ret = ls_laptop_backlight_register(); + if (ret < 0) + pr_err("Loongson Laptop:laptop-backlight device register failed\n"); + } else + pr_info("Loongson Laptop :laptop-backlight device is not in use\n"); + return 0; +} + +MODULE_ALIAS("platform:ls-laptop"); +MODULE_AUTHOR("lvjianmin "); +MODULE_DESCRIPTION(LSACPI_DESC); +MODULE_VERSION(LSACPI_VERSION); +MODULE_LICENSE("GPL"); + +module_init(generic_acpi_module_init); +module_exit(generic_acpi_module_exit); -- Gitee From 652c22c8504aab8e56a526324af37554095a0abc Mon Sep 17 00:00:00 2001 From: Tianli Xiong Date: Fri, 28 Oct 2022 17:36:41 +0800 Subject: [PATCH 31/56] irqchip/loongson-liointc: Set different isr for differnt core Signed-off-by: Tianli Xiong Change-Id: I397b141f87598267d52917cb273334ce647495cf --- drivers/irqchip/irq-loongson-liointc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-loongson-liointc.c b/drivers/irqchip/irq-loongson-liointc.c index 1ba889a165f2..9f0b2ec2a2d2 100644 --- a/drivers/irqchip/irq-loongson-liointc.c +++ b/drivers/irqchip/irq-loongson-liointc.c @@ -28,7 +28,7 @@ #define LIOINTC_INTC_CHIP_START 0x20 -#define LIOINTC_REG_INTC_STATUS (LIOINTC_INTC_CHIP_START + 0x20) +#define LIOINTC_REG_INTC_STATUS(cpuid) (LIOINTC_INTC_CHIP_START + 0x20 + (cpuid) * 8) #define LIOINTC_REG_INTC_EN_STATUS (LIOINTC_INTC_CHIP_START + 0x04) #define LIOINTC_REG_INTC_ENABLE (LIOINTC_INTC_CHIP_START + 0x08) #define LIOINTC_REG_INTC_DISABLE (LIOINTC_INTC_CHIP_START + 0x0c) @@ -196,7 +196,7 @@ static int liointc_init(phys_addr_t addr, unsigned long size, int revision, goto out_free_priv; for (i = 0; i < LIOINTC_NUM_CORES; i++) - priv->core_isr[i] = base + LIOINTC_REG_INTC_STATUS; + priv->core_isr[i] = base + LIOINTC_REG_INTC_STATUS(i); for (i = 0; i < LIOINTC_NUM_PARENT; i++) priv->handler[i].parent_int_map = parent_int_map[i]; -- Gitee From 21c73d740c572caa4b983142753e54e415dfecf9 Mon Sep 17 00:00:00 2001 From: Qiyuan Pu Date: Sat, 29 Oct 2022 16:10:08 +0800 Subject: [PATCH 32/56] kernel/locking: Optimize multi-node cache thrashing problem. Signen-off-by: Qiyuan Pu Change-Id: Ie87f61520e5c59920dd1759a4d088328859d49f1 --- fs/xfs/xfs_inode.c | 4 + include/linux/fs.h | 4 + include/linux/rwsem.h | 15 +++ include/linux/sched.h | 6 ++ kernel/Kconfig.locks | 16 +++ kernel/fork.c | 7 ++ kernel/locking/Makefile | 1 + kernel/locking/avoid_thrash.c | 192 ++++++++++++++++++++++++++++++++++ kernel/locking/rwsem.c | 4 + kernel/sched/fair.c | 47 +++++++++ 10 files changed, 296 insertions(+) create mode 100644 kernel/locking/avoid_thrash.c diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index c9cf34a4fee8..f82d0ff6e972 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -167,6 +167,10 @@ xfs_ilock( xfs_inode_t *ip, uint lock_flags) { +#ifdef CONFIG_NODE_CACHE_THRASH_OPTIMIZATION + extern void update_access_stat(u64 *ptr, int kind); + update_access_stat(&((&VFS_I(ip)->i_rwsem)->stat), 1); +#endif trace_xfs_ilock(ip, lock_flags, _RET_IP_); /* diff --git a/include/linux/fs.h b/include/linux/fs.h index 98236a86cca0..e4e4d9a93df1 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -794,6 +794,10 @@ enum inode_i_mutex_lock_class static inline void inode_lock(struct inode *inode) { +#ifdef CONFIG_NODE_CACHE_THRASH_OPTIMIZATION + extern void update_access_stat(u64 *ptr, int kind); + update_access_stat(&inode->i_rwsem.stat, 1); +#endif down_write(&inode->i_rwsem); } diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h index 4c715be48717..6861744bd04f 100644 --- a/include/linux/rwsem.h +++ b/include/linux/rwsem.h @@ -51,6 +51,9 @@ struct rw_semaphore { #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; #endif +#ifdef CONFIG_NODE_CACHE_THRASH_OPTIMIZATION + u64 stat; +#endif }; /* In all implementations count != 0 means locked */ @@ -86,6 +89,7 @@ static inline int rwsem_is_locked(struct rw_semaphore *sem) #define __RWSEM_OPT_INIT(lockname) #endif +#ifndef CONFIG_NODE_CACHE_THRASH_OPTIMIZATION #define __RWSEM_INITIALIZER(name) \ { __RWSEM_COUNT_INIT(name), \ .owner = ATOMIC_LONG_INIT(0), \ @@ -94,6 +98,17 @@ static inline int rwsem_is_locked(struct rw_semaphore *sem) .wait_list = LIST_HEAD_INIT((name).wait_list), \ __RWSEM_DEBUG_INIT(name) \ __RWSEM_DEP_MAP_INIT(name) } +#else +#define __RWSEM_INITIALIZER(name) \ + { __RWSEM_COUNT_INIT(name), \ + .stat = 0, \ + .owner = ATOMIC_LONG_INIT(0), \ + __RWSEM_OPT_INIT(name) \ + .wait_lock = __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock),\ + .wait_list = LIST_HEAD_INIT((name).wait_list), \ + __RWSEM_DEBUG_INIT(name) \ + __RWSEM_DEP_MAP_INIT(name) } +#endif #define DECLARE_RWSEM(name) \ struct rw_semaphore name = __RWSEM_INITIALIZER(name) diff --git a/include/linux/sched.h b/include/linux/sched.h index a84372945f9e..dc9068d64581 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1173,6 +1173,12 @@ struct task_struct { u64 node_stamp; u64 last_task_numa_placement; u64 last_sum_exec_runtime; +#ifdef CONFIG_NODE_CACHE_THRASH_OPTIMIZATION + u64 fixed_stamp, stat; + short pinned, init_pin; + atomic_t in_progress; + struct callback_head affinity_work; +#endif struct callback_head numa_work; /* diff --git a/kernel/Kconfig.locks b/kernel/Kconfig.locks index 3de8fd11873b..a08b583a38b6 100644 --- a/kernel/Kconfig.locks +++ b/kernel/Kconfig.locks @@ -232,6 +232,22 @@ config RWSEM_SPIN_ON_OWNER def_bool y depends on SMP && ARCH_SUPPORTS_ATOMIC_RMW +config NODE_CACHE_THRASH_OPTIMIZATION + def_bool y + depends on SMP && LOONGARCH && NUMA_BALANCING + help + When there are multiple processes coompeting for the same lock + the performance will become poor. This config will automatically + detect such a situation and migrate these processes to the same + node. + +config DEBUG_AVOID_NODE_THRASH + def_bool n + depends on NODE_CACHE_THRASH_OPTIMIZATION + help + When a process is affinity to a node, print the reason and + process information. + config LOCK_SPIN_ON_OWNER def_bool y depends on MUTEX_SPIN_ON_OWNER || RWSEM_SPIN_ON_OWNER diff --git a/kernel/fork.c b/kernel/fork.c index a01cda37dd25..8bf15bdeb8eb 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -970,6 +970,13 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node) #ifdef CONFIG_MEMCG tsk->active_memcg = NULL; +#endif +#ifdef CONFIG_NODE_CACHE_THRASH_OPTIMIZATION + tsk->fixed_stamp = jiffies; + tsk->pinned = -1; + tsk->init_pin = -1; + tsk->stat = 0; + atomic_set(&tsk->in_progress, 0); #endif return tsk; diff --git a/kernel/locking/Makefile b/kernel/locking/Makefile index 6d11cfb9b41f..0364997a5427 100644 --- a/kernel/locking/Makefile +++ b/kernel/locking/Makefile @@ -31,4 +31,5 @@ obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock_debug.o obj-$(CONFIG_QUEUED_RWLOCKS) += qrwlock.o obj-$(CONFIG_LOCK_TORTURE_TEST) += locktorture.o obj-$(CONFIG_WW_MUTEX_SELFTEST) += test-ww_mutex.o +obj-$(CONFIG_NODE_CACHE_THRASH_OPTIMIZATION) += avoid_thrash.o obj-$(CONFIG_LOCK_EVENT_COUNTS) += lock_events.o diff --git a/kernel/locking/avoid_thrash.c b/kernel/locking/avoid_thrash.c new file mode 100644 index 000000000000..e134a400d909 --- /dev/null +++ b/kernel/locking/avoid_thrash.c @@ -0,0 +1,192 @@ +#include +#include +#include +#include +#include + +#define FREQ_OFFSET 0 +#define FREQ_SIZE 20 + +#define PREV_JIFF_OFFSET 20 +#define PREV_JIFF_SIZE 32 + +#define PREV_PID_OFFSET 52 +#define PREV_PID_SIZE 8 + +#define NID_OFFSET 60 +#define NID_SIZE 4 + +static inline u64 value_normalize(u64 value, int cnt) +{ + return ~0UL >> (64 - cnt) & value; +} + +static inline u64 get_field(u64 val, u32 offset, u32 cnt) +{ + return (u64)(val >> offset) & (~0UL >> (64 - cnt)); +} + +static inline u64 set_field(u64 old, u32 offset, u32 cnt, u64 field_val) +{ + u64 mask = 0; + + if (offset + cnt >= 63) + mask = ~0UL >> (64 - offset); + if (64 - offset >= 63) + mask = ~0UL << (offset + cnt); + if (mask == 0) + mask = ~0UL >> (64 - offset) | ~0UL << (offset + cnt); + + return (old & mask) | (field_val << offset); +} + +static inline u64 get_freq(u64 val) +{ + return get_field(val, FREQ_OFFSET, FREQ_SIZE); +} + +static inline u64 set_freq(u64 val, u64 field_val) +{ + return set_field(val, FREQ_OFFSET, FREQ_SIZE, field_val); +} + +static inline u64 get_prev_jiff(u64 val) +{ + return get_field(val, PREV_JIFF_OFFSET, PREV_JIFF_SIZE); +} + +static inline u64 set_prev_jiff(u64 val, u64 field_val) +{ + return set_field(val, PREV_JIFF_OFFSET, PREV_JIFF_SIZE, field_val); +} + +static inline u64 get_prev_pid(u64 val) +{ + return get_field(val, PREV_PID_OFFSET, PREV_PID_SIZE); +} + +static inline u64 set_prev_pid(u64 val, u64 field_val) +{ + return set_field(val, PREV_PID_OFFSET, PREV_PID_SIZE, field_val); +} + +static inline u64 get_nid(u64 val) +{ + return get_field(val, NID_OFFSET, NID_SIZE); +} + +static inline u64 set_nid(u64 val, u64 field_val) +{ + return set_field(val, NID_OFFSET, NID_SIZE, field_val); +} + +#ifndef CONFIG_NODE_CACHE_THRASH_OPTIMIZATION +static inline void do_fixed_set(u64 nid) +{ + return; +} + +static inline int make_decision_fixed(u64 *ptr) +{ + return -1; +} + +void update_access_stat(u64 *ptr, int kind) +{ + return; +} +#else +static inline void do_fixed_set(u64 nid) +{ + if (atomic_cmpxchg(¤t->in_progress, 0, 1) != 0) + return; + + if (in_task()) { + sched_setaffinity(current->pid, cpumask_of_node(nid)); +#ifdef CONFIG_DEBUG_AVOID_NODE_THRASH + printk("pinned, jiffies: %lu pid: %d comm: %s nid: %llu\n", + jiffies, current->pid, current->comm, nid); +#endif + current->pinned = nid; + smp_mb(); + } + atomic_set(¤t->in_progress, 0); + + return; +} + +static inline int make_decision_fixed(u64 *ptr) +{ + u64 jiff_normal = value_normalize(jiffies, PREV_JIFF_SIZE); + u64 jiff_diff; + int ret = -1; + + if (unlikely(current->init_pin == -1)) { + if (current->nr_cpus_allowed >= nr_cpu_ids) + current->init_pin = 0; + else + current->init_pin = 1; + } + + jiff_diff = jiff_normal - get_prev_jiff(*ptr); + if (jiff_diff > 2 && jiff_diff * 256 < get_freq(*ptr)) { + if (current->pinned == -1) { + if (!current->init_pin) { + ret = get_nid(*ptr); +#ifdef CONFIG_DEBUG_AVOID_NODE_THRASH + printk("decision1, jiffies: %lu pid: %d comm: %s freq: %llu nid: %llu diff: %llu\n", + jiffies, current->pid, current->comm, get_freq(*ptr), get_nid(*ptr), jiff_diff); +#endif + } + } else { + if (get_nid(*ptr) < current->pinned) { + ret = get_nid(*ptr); +#ifdef CONFIG_DEBUG_AVOID_NODE_THRASH + printk("decision2, jiffies: %lu pid: %d comm: %s freq: %llu nid: %llu diff: %llu\n", + jiffies, current->pid, current->comm, get_freq(*ptr), get_nid(*ptr), jiff_diff); +#endif + } + } + current->fixed_stamp = jiffies; + } + + return ret; +} + +/* + Call this func where the lock is requested + */ +void update_access_stat(u64 *ptr, int kind) +{ + u64 jiff_normal = value_normalize(jiffies, PREV_JIFF_SIZE); + u64 jiff_diff; + u64 pid_normal = value_normalize(current->pid, PREV_PID_SIZE); + int nid; + + if (unlikely(*ptr == 0)) { + *ptr = set_prev_jiff(*ptr, jiff_normal); + *ptr = set_nid(*ptr, numa_node_id()); + } + + if (pid_normal != get_prev_pid(*ptr)) { + *ptr = set_freq(*ptr, get_freq(*ptr) + 1); + *ptr = set_prev_pid(*ptr, pid_normal); + } + + jiff_diff = jiff_normal - get_prev_jiff(*ptr); + if (jiff_diff > 4) { + *ptr = set_freq(*ptr, 0); + *ptr = set_prev_jiff(*ptr, jiff_normal); + } + current->stat = *ptr; + + nid = make_decision_fixed(¤t->stat); + if (nid >= 0) + do_fixed_set(nid); + + return; +} +#endif +EXPORT_SYMBOL(update_access_stat); +EXPORT_SYMBOL(make_decision_fixed); +EXPORT_SYMBOL(do_fixed_set); diff --git a/kernel/locking/rwsem.c b/kernel/locking/rwsem.c index cc5cc889b5b7..478900e7d6c5 100644 --- a/kernel/locking/rwsem.c +++ b/kernel/locking/rwsem.c @@ -340,6 +340,10 @@ void __init_rwsem(struct rw_semaphore *sem, const char *name, #ifdef CONFIG_RWSEM_SPIN_ON_OWNER osq_lock_init(&sem->osq); #endif + +#ifdef CONFIG_NODE_CACHE_THRASH_OPTIMIZATION + sem->stat = 0; +#endif } EXPORT_SYMBOL(__init_rwsem); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index d3c4b945c019..5510c872fa09 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -11882,6 +11882,48 @@ bool cfs_prio_less(struct task_struct *a, struct task_struct *b, bool in_fi) static inline void task_tick_core(struct rq *rq, struct task_struct *curr) {} #endif +#ifdef CONFIG_NODE_CACHE_THRASH_OPTIMIZATION +void do_set_to_all_node(struct callback_head *work) +{ + if (in_task()) { + sched_setaffinity(current->pid, cpu_active_mask); + current->pinned = -1; + smp_mb(); + } + atomic_set(¤t->in_progress, 0); + +#ifdef CONFIG_DEBUG_AVOID_NODE_THRASH + printk("all, jiffies: %lu pid: %d comm: %s\n", jiffies, current->pid, current->comm); +#endif + + return; +} + +static inline void set_to_all_node(struct task_struct *p, struct callback_head *work) +{ + init_task_work(work, do_set_to_all_node); + task_work_add(p, work, true); + + return; +} + +static inline void cancel_fixed(struct task_struct *curr, struct callback_head *work) +{ + if (curr->pinned == -1 || jiffies < curr->fixed_stamp + 4 * HZ + || atomic_cmpxchg(&curr->in_progress, 0, 1) != 0) + return; + + set_to_all_node(curr, work); + + return; +} +#else +static inline void cancel_fixed(struct task_struct *curr, struct callback_head *work) +{ + return; +} +#endif + /* * scheduler tick hitting a task of our scheduling class. * @@ -11895,6 +11937,11 @@ static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued) struct cfs_rq *cfs_rq; struct sched_entity *se = &curr->se; +#ifdef CONFIG_NODE_CACHE_THRASH_OPTIMIZATION + struct callback_head *work = &curr->affinity_work; + cancel_fixed(curr, work); +#endif + for_each_sched_entity(se) { cfs_rq = cfs_rq_of(se); entity_tick(cfs_rq, se, queued); -- Gitee From 35e78b243fde41cf88503cbf71ea4dd9d4e2442f Mon Sep 17 00:00:00 2001 From: zhangtianyang Date: Mon, 19 Sep 2022 11:31:22 +0800 Subject: [PATCH 33/56] LoongArch: Support Power Manager Signed-off-by: yangqiming Signed-off-by: zhangtianyang Change-Id: If6112fafdf5968ea731fb97ab6c764b34e4a7740 --- arch/loongarch/Kconfig | 8 ++ arch/loongarch/Makefile | 4 + arch/loongarch/configs/loongson3_defconfig | 2 +- arch/loongarch/include/asm/acpi.h | 8 +- arch/loongarch/include/asm/bootinfo.h | 4 + arch/loongarch/include/asm/loongson.h | 32 ----- arch/loongarch/include/asm/suspend.h | 10 ++ arch/loongarch/include/asm/time.h | 1 + arch/loongarch/kernel/Makefile | 3 +- arch/loongarch/kernel/acpi.c | 32 +++++ arch/loongarch/kernel/asm-offsets.c | 12 ++ arch/loongarch/kernel/platform.c | 45 +++++++ arch/loongarch/kernel/setup.c | 16 +++ arch/loongarch/kernel/smp.c | 1 + arch/loongarch/kernel/time.c | 7 +- arch/loongarch/power/Makefile | 4 + arch/loongarch/power/cpu.c | 45 +++++++ arch/loongarch/power/hibernate.c | 21 +++ arch/loongarch/power/hibernate_asm.S | 68 ++++++++++ arch/loongarch/power/suspend.c | 145 +++++++++++++++++++++ arch/loongarch/power/suspend_asm.S | 120 +++++++++++++++++ 21 files changed, 552 insertions(+), 36 deletions(-) create mode 100644 arch/loongarch/include/asm/suspend.h create mode 100644 arch/loongarch/kernel/platform.c create mode 100644 arch/loongarch/power/Makefile create mode 100644 arch/loongarch/power/cpu.c create mode 100644 arch/loongarch/power/hibernate.c create mode 100644 arch/loongarch/power/hibernate_asm.S create mode 100644 arch/loongarch/power/suspend.c create mode 100644 arch/loongarch/power/suspend_asm.S diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 77de7a1370f7..c8619a3c16f8 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -510,6 +510,14 @@ config ARCH_MMAP_RND_BITS_MAX menu "Power management options" +config ARCH_HIBERNATION_POSSIBLE + def_bool y + +config ARCH_SUSPEND_POSSIBLE + def_bool y + +source "kernel/power/Kconfig" + source "drivers/acpi/Kconfig" endmenu diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile index 3ab3625946a9..964b779b130d 100644 --- a/arch/loongarch/Makefile +++ b/arch/loongarch/Makefile @@ -96,9 +96,13 @@ endif head-y := arch/loongarch/kernel/head.o core-y += arch/loongarch/ + libs-y += arch/loongarch/lib/ libs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a +# suspend and hibernation support +drivers-$(CONFIG_PM) += arch/loongarch/power/ + ifeq ($(KBUILD_EXTMOD),) prepare: vdso_prepare vdso_prepare: prepare0 diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index a45ece42c6df..0773937ebcb9 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -37,8 +37,8 @@ CONFIG_PERF_EVENTS=y # CONFIG_COMPAT_BRK is not set CONFIG_CPU_HAS_LSX=y CONFIG_CPU_HAS_LASX=y -CONFIG_HOTPLUG_CPU=y CONFIG_NUMA=y +CONFIG_HIBERNATION=y CONFIG_ACPI_SPCR_TABLE=y CONFIG_ACPI_DOCK=y CONFIG_ACPI_IPMI=m diff --git a/arch/loongarch/include/asm/acpi.h b/arch/loongarch/include/asm/acpi.h index 825c2519b9d1..31f140d4ef26 100644 --- a/arch/loongarch/include/asm/acpi.h +++ b/arch/loongarch/include/asm/acpi.h @@ -7,7 +7,7 @@ #ifndef _ASM_LOONGARCH_ACPI_H #define _ASM_LOONGARCH_ACPI_H - +#include #ifdef CONFIG_ACPI extern int acpi_strict; extern int acpi_disabled; @@ -35,4 +35,10 @@ extern struct list_head acpi_wakeup_device_list; #define ACPI_TABLE_UPGRADE_MAX_PHYS ARCH_LOW_ADDRESS_LIMIT +static inline unsigned long acpi_get_wakeup_address(void) +{ + return (unsigned long)loongarch_wakeup_start; +} +extern int loongarch_acpi_suspend(void); +extern int (*acpi_suspend_lowlevel)(void); #endif /* _ASM_LOONGARCH_ACPI_H */ diff --git a/arch/loongarch/include/asm/bootinfo.h b/arch/loongarch/include/asm/bootinfo.h index 8e5881bc5ad1..068ea523260e 100644 --- a/arch/loongarch/include/asm/bootinfo.h +++ b/arch/loongarch/include/asm/bootinfo.h @@ -33,6 +33,10 @@ struct loongson_system_configuration { int cores_per_package; unsigned long cores_io_master; const char *cpuname; + u64 suspend_addr; + u64 gpe0_ena_reg; + u8 pcie_wake_enabled; + u8 is_soc_cpu; }; extern u64 efi_system_table; diff --git a/arch/loongarch/include/asm/loongson.h b/arch/loongarch/include/asm/loongson.h index 6e8f6972ceb6..e4108f674c4e 100644 --- a/arch/loongarch/include/asm/loongson.h +++ b/arch/loongarch/include/asm/loongson.h @@ -70,8 +70,6 @@ static inline void xconf_writeq(u64 val64, volatile void __iomem *addr) #define LS7A_CHIPCFG_REG_BASE (LS7A_PCH_REG_BASE + 0x00010000) /* MISC reg base */ #define LS7A_MISC_REG_BASE (LS7A_PCH_REG_BASE + 0x00080000) -/* ACPI regs */ -#define LS7A_ACPI_REG_BASE (LS7A_MISC_REG_BASE + 0x00050000) /* RTC regs */ #define LS7A_RTC_REG_BASE (LS7A_MISC_REG_BASE + 0x00050100) @@ -93,36 +91,6 @@ static inline void xconf_writeq(u64 val64, volatile void __iomem *addr) #define LS7A_LPC_INT_CLR (volatile void *)TO_UNCACHE(LS7A_PCH_REG_BASE + 0x200c) #define LS7A_LPC_INT_POL (volatile void *)TO_UNCACHE(LS7A_PCH_REG_BASE + 0x2010) -#define LS7A_PMCON_SOC_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x000) -#define LS7A_PMCON_RESUME_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x004) -#define LS7A_PMCON_RTC_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x008) -#define LS7A_PM1_EVT_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x00c) -#define LS7A_PM1_ENA_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x010) -#define LS7A_PM1_CNT_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x014) -#define LS7A_PM1_TMR_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x018) -#define LS7A_P_CNT_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x01c) -#define LS7A_GPE0_STS_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x028) -#define LS7A_GPE0_ENA_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x02c) -#define LS7A_RST_CNT_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x030) -#define LS7A_WD_SET_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x034) -#define LS7A_WD_TIMER_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x038) -#define LS7A_THSENS_CNT_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x04c) -#define LS7A_GEN_RTC_1_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x050) -#define LS7A_GEN_RTC_2_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x054) -#define LS7A_DPM_CFG_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x400) -#define LS7A_DPM_STS_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x404) -#define LS7A_DPM_CNT_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x408) - -typedef enum { - ACPI_PCI_HOTPLUG_STATUS = 1 << 1, - ACPI_CPU_HOTPLUG_STATUS = 1 << 2, - ACPI_MEM_HOTPLUG_STATUS = 1 << 3, - ACPI_POWERBUTTON_STATUS = 1 << 8, - ACPI_RTC_WAKE_STATUS = 1 << 10, - ACPI_PCI_WAKE_STATUS = 1 << 14, - ACPI_ANY_WAKE_STATUS = 1 << 15, -} AcpiEventStatusBits; - #define HT1LO_OFFSET 0xe0000000000UL /* PCI Configuration Space Base */ diff --git a/arch/loongarch/include/asm/suspend.h b/arch/loongarch/include/asm/suspend.h new file mode 100644 index 000000000000..9c42f0fee92f --- /dev/null +++ b/arch/loongarch/include/asm/suspend.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_LOONGARCH_SUSPEND_H +#define _ASM_LOONGARCH_SUSPEND_H + +void arch_common_resume(void); +void arch_common_suspend(void); +extern void loongarch_suspend_enter(void); +extern void loongarch_wakeup_start(void); + +#endif /* _ASM_LOONGARCH_SUSPEND_H */ diff --git a/arch/loongarch/include/asm/time.h b/arch/loongarch/include/asm/time.h index 2eae219301d0..d160d2cf6831 100644 --- a/arch/loongarch/include/asm/time.h +++ b/arch/loongarch/include/asm/time.h @@ -13,6 +13,7 @@ extern u64 cpu_clock_freq; extern u64 const_clock_freq; extern void sync_counter(void); +extern void save_counter(void); static inline unsigned int calc_const_freq(void) { diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile index 0b2aea3d94ef..11cc09679b40 100644 --- a/arch/loongarch/kernel/Makefile +++ b/arch/loongarch/kernel/Makefile @@ -7,7 +7,8 @@ extra-y := head.o vmlinux.lds obj-y += cpu-probe.o cacheinfo.o env.o setup.o entry.o genex.o \ traps.o irq.o idle.o process.o dma.o mem.o io.o reset.o switch.o \ - elf.o legacy_boot.o syscall.o signal.o time.o topology.o inst.o ptrace.o vdso.o + elf.o legacy_boot.o syscall.o signal.o time.o topology.o inst.o ptrace.o vdso.o\ + platform.o obj-$(CONFIG_ACPI) += acpi.o obj-$(CONFIG_EFI) += efi.o diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c index 3d9759c3ccb5..1a2e4c478c9e 100644 --- a/arch/loongarch/kernel/acpi.c +++ b/arch/loongarch/kernel/acpi.c @@ -74,6 +74,30 @@ void __init acpi_boot_table_init(void) } } +static int __init acpi_parse_fadt(struct acpi_table_header *table) +{ + u64 gpe0_ena; + + if (acpi_gbl_reduced_hardware) + return 0; + + if (acpi_gbl_FADT.xgpe0_block.space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) + goto err; + gpe0_ena = acpi_gbl_FADT.xgpe0_block.address + + acpi_gbl_FADT.gpe0_block_length / 2; + if (!gpe0_ena) + goto err; + + loongson_sysconf.gpe0_ena_reg = TO_UNCACHE(gpe0_ena); + + return 0; +err: + pr_err(PREFIX "Invalid BIOS FADT, disabling ACPI\n"); + disable_acpi(); + return -1; +} + + #ifdef CONFIG_SMP int set_processor_mask(u32 id, u32 flags) { @@ -169,6 +193,12 @@ static void __init acpi_process_madt(void) loongson_sysconf.nr_cpus = num_processors; } +#ifdef CONFIG_ACPI_SLEEP +int (*acpi_suspend_lowlevel)(void) = loongarch_acpi_suspend; +#else +int (*acpi_suspend_lowlevel)(void); +#endif + int __init acpi_boot_init(void) { /* @@ -179,6 +209,8 @@ int __init acpi_boot_init(void) loongson_sysconf.boot_cpu_id = read_csr_cpuid(); + acpi_table_parse(ACPI_SIG_FADT, acpi_parse_fadt); + /* * Process the Multiple APIC Description Table (MADT), if present */ diff --git a/arch/loongarch/kernel/asm-offsets.c b/arch/loongarch/kernel/asm-offsets.c index 812b0002dbc8..8733fc347b3e 100644 --- a/arch/loongarch/kernel/asm-offsets.c +++ b/arch/loongarch/kernel/asm-offsets.c @@ -257,3 +257,15 @@ void output_smpboot_defines(void) BLANK(); } #endif +#ifdef CONFIG_HIBERNATION +void output_pbe_defines(void) +{ + COMMENT(" Linux struct pbe offsets. "); + OFFSET(PBE_ADDRESS, pbe, address); + OFFSET(PBE_ORIG_ADDRESS, pbe, orig_address); + OFFSET(PBE_NEXT, pbe, next); + DEFINE(PBE_SIZE, sizeof(struct pbe)); + BLANK(); +} +#endif + diff --git a/arch/loongarch/kernel/platform.c b/arch/loongarch/kernel/platform.c new file mode 100644 index 000000000000..da158221fae1 --- /dev/null +++ b/arch/loongarch/kernel/platform.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2009 Lemote Inc. + * Author: Wu Zhangjin, wuzhangjin@gmail.com + * Copyright (C) 2020 Loongson Technology Co., Ltd. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +extern int loongson_acpi_init(void); + +static int __init loongson3_acpi_suspend_init(void) +{ +#ifdef CONFIG_ACPI + acpi_status status; + unsigned long long suspend_addr = 0; + + if (acpi_disabled || acpi_gbl_reduced_hardware) + return 0; + + acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1); + + status = acpi_evaluate_integer(NULL, "\\SADR", NULL, &suspend_addr); + if (ACPI_FAILURE(status) || !suspend_addr) { + pr_err("ACPI S3 is not support!\n"); + return -1; + } + loongson_sysconf.suspend_addr = (u64)phys_to_virt(TO_PHYS(suspend_addr)); +#endif + return 0; +} + +device_initcall(loongson3_acpi_suspend_init); diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c index eca98e9cca25..887ae4a92438 100644 --- a/arch/loongarch/kernel/setup.c +++ b/arch/loongarch/kernel/setup.c @@ -198,6 +198,21 @@ static int __init early_parse_mem(char *p) return 0; } early_param("mem", early_parse_mem); +static void __init set_pcie_wakeup(void) +{ + acpi_status status; + u32 value; + + if (loongson_sysconf.is_soc_cpu || acpi_gbl_reduced_hardware) + return; + + status = acpi_read_bit_register(ACPI_BITREG_PCIEXP_WAKE_DISABLE, &value); + if (ACPI_FAILURE(status)) { + return; + } + loongson_sysconf.pcie_wake_enabled = !value; +} + void __init platform_init(void) { @@ -210,6 +225,7 @@ void __init platform_init(void) acpi_boot_table_init(); acpi_boot_init(); #endif + set_pcie_wakeup(); #ifdef CONFIG_NUMA init_numa_memory(); diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c index 883d8d540f67..7e6dde83e6ac 100644 --- a/arch/loongarch/kernel/smp.c +++ b/arch/loongarch/kernel/smp.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/loongarch/kernel/time.c b/arch/loongarch/kernel/time.c index 786735dcc8d6..8d331a5fae5a 100644 --- a/arch/loongarch/kernel/time.c +++ b/arch/loongarch/kernel/time.c @@ -115,7 +115,12 @@ static unsigned long __init get_loops_per_jiffy(void) return lpj; } -static long init_timeval; +static long init_timeval __nosavedata; + +void save_counter(void) +{ + init_timeval = drdtime(); +} void sync_counter(void) { diff --git a/arch/loongarch/power/Makefile b/arch/loongarch/power/Makefile new file mode 100644 index 000000000000..a77c31a96178 --- /dev/null +++ b/arch/loongarch/power/Makefile @@ -0,0 +1,4 @@ +OBJECT_FILES_NON_STANDARD_suspend_asm.o := y + +obj-$(CONFIG_SUSPEND) += suspend.o suspend_asm.o +obj-$(CONFIG_HIBERNATION) += cpu.o hibernate.o hibernate_asm.o diff --git a/arch/loongarch/power/cpu.c b/arch/loongarch/power/cpu.c new file mode 100644 index 000000000000..e3d8fc1099e2 --- /dev/null +++ b/arch/loongarch/power/cpu.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Suspend support specific for loongarch. + * + * Licensed under the GPLv2 + * Copyright (C) 2020 Loongson Technology Co., Ltd. + */ +#include +#include + +static u64 saved_crmd; +static u64 saved_prmd; +static u64 saved_euen; +static u64 saved_ecfg; +struct pt_regs saved_regs; + +void save_processor_state(void) +{ + saved_crmd = csr_read32(LOONGARCH_CSR_CRMD); + saved_prmd = csr_read32(LOONGARCH_CSR_PRMD); + saved_euen = csr_read32(LOONGARCH_CSR_EUEN); + saved_ecfg = csr_read32(LOONGARCH_CSR_ECFG); + + if (is_fpu_owner()) + save_fp(current); +} + +void restore_processor_state(void) +{ + csr_write32(saved_crmd, LOONGARCH_CSR_CRMD); + csr_write32(saved_prmd, LOONGARCH_CSR_PRMD); + csr_write32(saved_euen, LOONGARCH_CSR_EUEN); + csr_write32(saved_ecfg, LOONGARCH_CSR_ECFG); + + if (is_fpu_owner()) + restore_fp(current); +} + +int pfn_is_nosave(unsigned long pfn) +{ + unsigned long nosave_begin_pfn = PFN_DOWN(__pa(&__nosave_begin)); + unsigned long nosave_end_pfn = PFN_UP(__pa(&__nosave_end)); + + return (pfn >= nosave_begin_pfn) && (pfn < nosave_end_pfn); +} diff --git a/arch/loongarch/power/hibernate.c b/arch/loongarch/power/hibernate.c new file mode 100644 index 000000000000..9050225d0d34 --- /dev/null +++ b/arch/loongarch/power/hibernate.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +#include + +extern int restore_image(void); +extern void enable_pcie_wakeup(void); +extern void swsusp_arch_save(void); + +int swsusp_arch_suspend(void) +{ + enable_pcie_wakeup(); + swsusp_arch_save(); + + return 0; +} + +int swsusp_arch_resume(void) +{ + /* Avoid TLB mismatch during and after kernel resume */ + local_flush_tlb_all(); + return restore_image(); +} diff --git a/arch/loongarch/power/hibernate_asm.S b/arch/loongarch/power/hibernate_asm.S new file mode 100644 index 000000000000..1874e473b293 --- /dev/null +++ b/arch/loongarch/power/hibernate_asm.S @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Hibernation support specific for loongarch - temporary page tables + * + * Licensed under the GPLv2 + * + * Copyright (C) 2009 Lemote Inc. + * Author: Hu Hongbing + * Wu Zhangjin + * Copyright (C) 2020 Loongson Technology Co., Ltd. + */ +#include +#include +#include +#include + +.text +SYM_FUNC_START(swsusp_arch_save) + la.abs t0, saved_regs + PTR_S ra, t0, PT_R1 + PTR_S sp, t0, PT_R3 + PTR_S fp, t0, PT_R22 + PTR_S tp, t0, PT_R2 + PTR_S s0, t0, PT_R23 + PTR_S s1, t0, PT_R24 + PTR_S s2, t0, PT_R25 + PTR_S s3, t0, PT_R26 + PTR_S s4, t0, PT_R27 + PTR_S s5, t0, PT_R28 + PTR_S s6, t0, PT_R29 + PTR_S s7, t0, PT_R30 + PTR_S s8, t0, PT_R31 + b swsusp_save +SYM_FUNC_END(swsusp_arch_save) + +SYM_FUNC_START(restore_image) + la.pcrel t0, restore_pblist + PTR_L t0, t0, 0 +0: + PTR_L t1, t0, PBE_ADDRESS /* source */ + PTR_L t2, t0, PBE_ORIG_ADDRESS /* destination */ + PTR_LI t3, _PAGE_SIZE + PTR_ADD t3, t3, t1 +1: + REG_L t8, t1, 0 + REG_S t8, t2, 0 + PTR_ADDI t1, t1, SZREG + PTR_ADDI t2, t2, SZREG + bne t1, t3, 1b + PTR_L t0, t0, PBE_NEXT + bnez t0, 0b + la.pcrel t0, saved_regs + PTR_L ra, t0, PT_R1 + PTR_L sp, t0, PT_R3 + PTR_L fp, t0, PT_R22 + PTR_L tp, t0, PT_R2 + PTR_L s0, t0, PT_R23 + PTR_L s1, t0, PT_R24 + PTR_L s2, t0, PT_R25 + PTR_L s3, t0, PT_R26 + PTR_L s4, t0, PT_R27 + PTR_L s5, t0, PT_R28 + PTR_L s6, t0, PT_R29 + PTR_L s7, t0, PT_R30 + PTR_L s8, t0, PT_R31 + PTR_LI a0, 0x0 + jirl zero, ra, 0 +SYM_FUNC_END(restore_image) diff --git a/arch/loongarch/power/suspend.c b/arch/loongarch/power/suspend.c new file mode 100644 index 000000000000..3d0b0291e8f0 --- /dev/null +++ b/arch/loongarch/power/suspend.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * loongson-specific suspend support + * + * Copyright (C) 2020 Loongson Technology Co., Ltd. + * Author: Huacai Chen + * + * 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. + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +u64 loongarch_suspend_addr; + +extern unsigned long eentry; +extern unsigned long tlbrentry; +struct saved_registers { + u32 ecfg; + u64 pgd; + u64 kpgd; + u32 pwctl0; + u32 pwctl1; + u32 euen; +}; +static struct saved_registers saved_regs; + +void arch_common_suspend(void) +{ + save_counter(); + saved_regs.pgd = csr_read64(LOONGARCH_CSR_PGDL); + saved_regs.kpgd = csr_read64(LOONGARCH_CSR_PGDH); + saved_regs.pwctl0 = csr_read32(LOONGARCH_CSR_PWCTL0); + saved_regs.pwctl1 = csr_read32(LOONGARCH_CSR_PWCTL1); + saved_regs.ecfg = csr_read32(LOONGARCH_CSR_ECFG); + saved_regs.euen = csr_read32(LOONGARCH_CSR_EUEN); + + loongarch_suspend_addr = loongson_sysconf.suspend_addr; +} + +void arch_common_resume(void) +{ + sync_counter(); + local_flush_tlb_all(); + csr_write64(per_cpu_offset(0), PERCPU_BASE_KS); + + csr_write64(saved_regs.pgd, LOONGARCH_CSR_PGDL); + csr_write64(saved_regs.kpgd, LOONGARCH_CSR_PGDH); + csr_write32(saved_regs.pwctl0, LOONGARCH_CSR_PWCTL0); + csr_write32(saved_regs.pwctl1, LOONGARCH_CSR_PWCTL1); + csr_write32(saved_regs.ecfg, LOONGARCH_CSR_ECFG); + csr_write32(saved_regs.euen, LOONGARCH_CSR_EUEN); + csr_write64(eentry, LOONGARCH_CSR_EENTRY); + csr_write64(tlbrentry, LOONGARCH_CSR_TLBRENTRY); + csr_write64(eentry, LOONGARCH_CSR_MERRENTRY); +} + +static void enable_gpe_wakeup(void) +{ + struct list_head *node, *next; + u32 data = 0; + + data = readl((volatile void *)loongson_sysconf.gpe0_ena_reg); + + list_for_each_safe(node, next, &acpi_wakeup_device_list) { + struct acpi_device *dev = + container_of(node, struct acpi_device, wakeup_list); + + if (!dev->wakeup.flags.valid + || ACPI_STATE_S3 > (u32) dev->wakeup.sleep_state + || !(device_may_wakeup(&dev->dev) + || dev->wakeup.prepare_count)) + continue; + + data |= (1 << dev->wakeup.gpe_number); + } + writel(data, (volatile void *)loongson_sysconf.gpe0_ena_reg); +} + +void enable_pcie_wakeup(void) +{ + u16 value; + + if (loongson_sysconf.is_soc_cpu || acpi_gbl_reduced_hardware) + return; + + acpi_write_bit_register(ACPI_BITREG_PCIEXP_WAKE_STATUS, 1); + + if (loongson_sysconf.pcie_wake_enabled) { + acpi_write_bit_register(ACPI_BITREG_PCIEXP_WAKE_DISABLE, 0); + } +} +EXPORT_SYMBOL_GPL(enable_pcie_wakeup); + +int loongarch_acpi_suspend(void) +{ + arch_common_suspend(); + enable_gpe_wakeup(); + enable_pcie_wakeup(); + /* processor specific suspend */ + loongarch_suspend_enter(); + arch_common_resume(); + + return 0; +} + +static int plat_pm_callback(struct notifier_block *nb, unsigned long action, void *ptr) +{ + int ret = 0; + + switch (action) { + case PM_POST_SUSPEND: + enable_gpe_wakeup(); + break; + default: + break; + } + + return notifier_from_errno(ret); +} + +static int __init plat_pm_post_init(void) +{ + if (loongson_sysconf.is_soc_cpu || acpi_gbl_reduced_hardware) + return 0; + + enable_gpe_wakeup(); + pm_notifier(plat_pm_callback, -INT_MAX); + return 0; +} + +late_initcall_sync(plat_pm_post_init); diff --git a/arch/loongarch/power/suspend_asm.S b/arch/loongarch/power/suspend_asm.S new file mode 100644 index 000000000000..781e38cd35e7 --- /dev/null +++ b/arch/loongarch/power/suspend_asm.S @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Sleep helper for Loongson-3 sleep mode. + * + * Copyright (C) 2020 Loongson Technology Co., Ltd. + * Author: Huacai Chen + */ + +#include +#include +#include +#include +#include + + .extern loongarch_nr_nodes + .extern loongarch_suspend_addr + .extern loongarch_pcache_ways + .extern loongarch_pcache_sets + .extern loongarch_pcache_linesz + .extern loongarch_scache_ways + .extern loongarch_scache_sets + .extern loongarch_scache_linesz + + .text + .align 5 + +/* preparatory stuff */ +.macro SETUP_SLEEP + addi.d sp, sp, -PT_SIZE + st.d $r1, sp, PT_R1 + st.d $r2, sp, PT_R2 + st.d $r3, sp, PT_R3 + st.d $r4, sp, PT_R4 + st.d $r5, sp, PT_R5 + st.d $r6, sp, PT_R6 + st.d $r7, sp, PT_R7 + st.d $r8, sp, PT_R8 + st.d $r9, sp, PT_R9 + st.d $r10, sp, PT_R10 + st.d $r11, sp, PT_R11 + st.d $r20, sp, PT_R20 + st.d $r21, sp, PT_R21 + st.d $r22, sp, PT_R22 + st.d $r23, sp, PT_R23 + st.d $r24, sp, PT_R24 + st.d $r25, sp, PT_R25 + st.d $r26, sp, PT_R26 + st.d $r27, sp, PT_R27 + st.d $r28, sp, PT_R28 + st.d $r29, sp, PT_R29 + st.d $r30, sp, PT_R30 + st.d $r31, sp, PT_R31 + +#ifdef CONFIG_ACPI + la.pcrel t0, acpi_saved_sp + st.d sp, t0, 0 +#endif +.endm +/* Sleep code for Loongson-3 */ +SYM_CODE_START(loongarch_suspend_enter) + SETUP_SLEEP + bl cpu_flush_caches + + /* Pass RA and SP to BIOS, for machines without CMOS RAM */ + addi.d a1, sp, 0 + la.pcrel a0, loongarch_wakeup_start + + la.pcrel t0, loongarch_suspend_addr + ld.d t0, t0, 0 /* Call BIOS's STR sleep routine */ + jr t0 + nop +SYM_CODE_END(loongarch_suspend_enter) + +.macro SETUP_WAKEUP + + nop + ld.d $r1, sp, PT_R1 + ld.d $r2, sp, PT_R2 + ld.d $r3, sp, PT_R3 + ld.d $r4, sp, PT_R4 + ld.d $r5, sp, PT_R5 + ld.d $r6, sp, PT_R6 + ld.d $r7, sp, PT_R7 + ld.d $r8, sp, PT_R8 + ld.d $r9, sp, PT_R9 + ld.d $r10, sp, PT_R10 + ld.d $r11, sp, PT_R11 + ld.d $r20, sp, PT_R20 + ld.d $r21, sp, PT_R21 + ld.d $r22, sp, PT_R22 + ld.d $r23, sp, PT_R23 + ld.d $r24, sp, PT_R24 + ld.d $r25, sp, PT_R25 + ld.d $r26, sp, PT_R26 + ld.d $r27, sp, PT_R27 + ld.d $r28, sp, PT_R28 + ld.d $r29, sp, PT_R29 + ld.d $r30, sp, PT_R30 + ld.d $r31, sp, PT_R31 +.endm + + /* This is where we return upon wakeup. + * Reload all of the registers and return. + */ +SYM_CODE_START(loongarch_wakeup_start) + li.d t0, CSR_DMW0_INIT # UC, PLV0 + csrwr t0, LOONGARCH_CSR_DMWIN0 + li.d t0, CSR_DMW1_INIT # CA, PLV0 + csrwr t0, LOONGARCH_CSR_DMWIN1 + + la.pcrel t0, acpi_saved_sp + ld.d sp, t0, 0 + SETUP_WAKEUP + addi.d sp, sp, PT_SIZE + jr ra +SYM_CODE_END(loongarch_wakeup_start) -- Gitee From abd6eb441db62294c09f42d018032938c263013b Mon Sep 17 00:00:00 2001 From: Jun Yi Date: Fri, 11 Nov 2022 17:41:49 +0800 Subject: [PATCH 34/56] LoongArch: Remove redudant csr save/restore Signed-off-by: Jun Yi Change-Id: Ib034b577cbebbbe56e936bf541348db271c2a9db --- arch/loongarch/include/asm/stackframe.h | 9 --------- arch/loongarch/kernel/entry.S | 8 -------- arch/loongarch/kernel/switch.S | 6 ------ 3 files changed, 23 deletions(-) diff --git a/arch/loongarch/include/asm/stackframe.h b/arch/loongarch/include/asm/stackframe.h index 4ca953062b5b..733dc9e96241 100644 --- a/arch/loongarch/include/asm/stackframe.h +++ b/arch/loongarch/include/asm/stackframe.h @@ -114,14 +114,6 @@ LONG_S zero, sp, PT_R0 csrrd t0, LOONGARCH_CSR_PRMD LONG_S t0, sp, PT_PRMD - csrrd t0, LOONGARCH_CSR_CRMD - LONG_S t0, sp, PT_CRMD - csrrd t0, LOONGARCH_CSR_EUEN - LONG_S t0, sp, PT_EUEN - csrrd t0, LOONGARCH_CSR_ECFG - LONG_S t0, sp, PT_ECFG - csrrd t0, LOONGARCH_CSR_ESTAT - PTR_S t0, sp, PT_ESTAT cfi_st ra, PT_R1, \docfi cfi_st a0, PT_R4, \docfi cfi_st a1, PT_R5, \docfi @@ -140,7 +132,6 @@ cfi_st fp, PT_R22, \docfi /* Set thread_info if we're coming from user mode */ - csrrd t0, LOONGARCH_CSR_PRMD andi t0, t0, 0x3 /* extract pplv bit */ beqz t0, 9f diff --git a/arch/loongarch/kernel/entry.S b/arch/loongarch/kernel/entry.S index d53b631c9022..893e632e76da 100644 --- a/arch/loongarch/kernel/entry.S +++ b/arch/loongarch/kernel/entry.S @@ -31,14 +31,6 @@ SYM_FUNC_START(handle_syscall) st.d zero, sp, PT_R0 csrrd t2, LOONGARCH_CSR_PRMD st.d t2, sp, PT_PRMD - csrrd t2, LOONGARCH_CSR_CRMD - st.d t2, sp, PT_CRMD - csrrd t2, LOONGARCH_CSR_EUEN - st.d t2, sp, PT_EUEN - csrrd t2, LOONGARCH_CSR_ECFG - st.d t2, sp, PT_ECFG - csrrd t2, LOONGARCH_CSR_ESTAT - st.d t2, sp, PT_ESTAT cfi_st ra, PT_R1 cfi_st a0, PT_R4 cfi_st a1, PT_R5 diff --git a/arch/loongarch/kernel/switch.S b/arch/loongarch/kernel/switch.S index 43ebbc3990f7..d0363437d25f 100644 --- a/arch/loongarch/kernel/switch.S +++ b/arch/loongarch/kernel/switch.S @@ -16,9 +16,6 @@ */ .align 5 SYM_FUNC_START(__switch_to) - csrrd t1, LOONGARCH_CSR_PRMD - stptr.d t1, a0, THREAD_CSRPRMD - cpu_save_nonscratch a0 stptr.d ra, a0, THREAD_REG01 stptr.d a3, a0, THREAD_SCHED_RA @@ -30,8 +27,5 @@ SYM_FUNC_START(__switch_to) PTR_ADD t0, t0, tp set_saved_sp t0, t1, t2 - ldptr.d t1, a1, THREAD_CSRPRMD - csrwr t1, LOONGARCH_CSR_PRMD - jr ra SYM_FUNC_END(__switch_to) -- Gitee From 0ff17373facc0fb80c60f9837ca8d102fc789835 Mon Sep 17 00:00:00 2001 From: Jianmin Lv Date: Thu, 8 Sep 2022 16:58:31 +0800 Subject: [PATCH 35/56] LoongArch: Enhance booting and resume compatibility - For bootstrap processor booting, firmare legacy mmu environment is used on calling kernel_entry(), if the first instruction and next instructions of it are not in one page built by firmware, the instructions outside the page containing first entry instruction will trigger tlb refill exception, which will cause bug because the tlb refill ebase has not been configured. To avoid the disaster, we should ensure the kernel_entry to be page aligned with 4k page size, so that for pages of size >= 4k built by firmware, the instrutions of kernel_entry() will be in one firmware's page. - For S3 resume from firmware, firmare legacy mmu environment is also used on calling wakeup entry, so the wakeup entry is required to be 4k page aligned too. And for compatibility with calling wakeup entry by using physical address, a jumping to itself in the wakeup entry is required. REBASE: 1 rebase changes at head.s to commit a8fc1e90de478fb711ccf7e1a7115370388ab7fc LoongArch: Add boot and setup routines 2 rebase changes at suspend_asm.S to b4206b674b85d365097a40485c87187dfbea9a39 LoongArch:Support Power Manager Change-Id: I5304f271c9440a8526466cfe6be44da43466cd90 Signed-off-by: Jianmin Lv --- arch/loongarch/kernel/head.S | 1 + arch/loongarch/power/suspend_asm.S | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S index e2074cd4fff4..58254fd1999d 100644 --- a/arch/loongarch/kernel/head.S +++ b/arch/loongarch/kernel/head.S @@ -34,6 +34,7 @@ SYM_DATA(kernel_offset, .long kernel_offset - _text); __REF +.align 12 SYM_CODE_START(kernel_entry) # kernel entry point /* Config direct window and set PG */ diff --git a/arch/loongarch/power/suspend_asm.S b/arch/loongarch/power/suspend_asm.S index 781e38cd35e7..ceac577c3794 100644 --- a/arch/loongarch/power/suspend_asm.S +++ b/arch/loongarch/power/suspend_asm.S @@ -106,12 +106,16 @@ SYM_CODE_END(loongarch_suspend_enter) /* This is where we return upon wakeup. * Reload all of the registers and return. */ + .align 12 SYM_CODE_START(loongarch_wakeup_start) li.d t0, CSR_DMW0_INIT # UC, PLV0 csrwr t0, LOONGARCH_CSR_DMWIN0 li.d t0, CSR_DMW1_INIT # CA, PLV0 csrwr t0, LOONGARCH_CSR_DMWIN1 + la.abs t0, 0f + jirl zero, t0, 0 +0: la.pcrel t0, acpi_saved_sp ld.d sp, t0, 0 SETUP_WAKEUP -- Gitee From 925bb3fddb0cd986dc211e423d35f164bbc1c083 Mon Sep 17 00:00:00 2001 From: yangqiming Date: Mon, 12 Dec 2022 14:37:46 +0800 Subject: [PATCH 36/56] LoongArch: Support loader and kernel interface V40 1. LoongArch uses the new interface parameter form: a0: efi flag, a1: command line, a2: system table 2. Boot memmap and initrd are passed to kernel via GUID. Signed-off-by: Qiming Yang Signed-off-by: Jianmin Lv Change-Id: I193db4238e28962bdc4ae82a232d0c343f4f32f3 --- arch/loongarch/include/asm/efi.h | 17 +++++++++ arch/loongarch/kernel/efi.c | 55 ++++++++++++++++++++++++++++- arch/loongarch/kernel/env.c | 17 ++++++++- arch/loongarch/kernel/legacy_boot.c | 2 +- arch/loongarch/kernel/setup.c | 2 +- 5 files changed, 89 insertions(+), 4 deletions(-) diff --git a/arch/loongarch/include/asm/efi.h b/arch/loongarch/include/asm/efi.h index 85176fc629b0..9499abaa6baf 100644 --- a/arch/loongarch/include/asm/efi.h +++ b/arch/loongarch/include/asm/efi.h @@ -18,6 +18,23 @@ void __init efi_runtime_init(void); #define EFI_ALLOC_ALIGN SZ_64K #define EFI_RT_VIRTUAL_OFFSET CSR_DMW0_BASE +#define LINUX_EFI_INITRD_MEDIA_GUID EFI_GUID(0x5568e427, 0x68fc, 0x4f3d, 0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68) +#define LINUX_EFI_NEW_MEMMAP_GUID EFI_GUID(0x800f683f, 0xd08b, 0x423a, 0xa2, 0x93, 0x96, 0x5c, 0x3c, 0x6f, 0xe2, 0xb4) + +struct linux_efi_initrd { + unsigned long base; + unsigned long size; +}; + +struct efi_new_memmap { + unsigned long map_size; + unsigned long desc_size; + u32 desc_ver; + unsigned long map_key; + unsigned long buff_size; + efi_memory_desc_t map[]; +}; + static inline struct screen_info *alloc_screen_info(void) { return &screen_info; diff --git a/arch/loongarch/kernel/efi.c b/arch/loongarch/kernel/efi.c index f48ef0d8d518..7cf4a11732d3 100644 --- a/arch/loongarch/kernel/efi.c +++ b/arch/loongarch/kernel/efi.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -27,11 +28,18 @@ #include #include "legacy_boot.h" +static __initdata unsigned long new_memmap = EFI_INVALID_TABLE_ADDR; +static __initdata unsigned long initrd = EFI_INVALID_TABLE_ADDR; + static unsigned long efi_nr_tables; static unsigned long efi_config_table; static efi_system_table_t *efi_systab; -static efi_config_table_type_t arch_tables[] __initdata = {{},}; +static efi_config_table_type_t arch_tables[] __initdata = { + {LINUX_EFI_NEW_MEMMAP_GUID, &new_memmap, "NEWMEM"}, + {LINUX_EFI_INITRD_MEDIA_GUID, &initrd, "INITRD"}, + {}, +}; static __initdata pgd_t *pgd_efi; static int __init efimap_populate_hugepages( @@ -184,6 +192,9 @@ static int __init set_virtual_map(void) (efi_memory_desc_t *)TO_PHYS((unsigned long)runtime_map)); efi_unmap_pgt(); + if (status != EFI_SUCCESS) + return -1; + return 0; } @@ -213,6 +224,44 @@ void __init efi_runtime_init(void) set_bit(EFI_RUNTIME_SERVICES, &efi.flags); } +static void __init get_initrd(void) +{ + if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && + initrd != EFI_INVALID_TABLE_ADDR && phys_initrd_size == 0) { + struct linux_efi_initrd *tbl; + + tbl = early_memremap(initrd, sizeof(*tbl)); + if (tbl) { + phys_initrd_start = tbl->base; + phys_initrd_size = tbl->size; + early_memunmap(tbl, sizeof(*tbl)); + } + } +} + +static void __init init_new_memmap(void) +{ + struct efi_new_memmap *tbl; + + if (new_memmap == EFI_INVALID_TABLE_ADDR) + return; + + tbl = early_memremap_ro(new_memmap, sizeof(*tbl)); + if (tbl) { + struct efi_memory_map_data data; + + data.phys_map = new_memmap + sizeof(*tbl); + data.size = tbl->map_size; + data.desc_size = tbl->desc_size; + data.desc_version = tbl->desc_ver; + + if (efi_memmap_init_early(&data) < 0) + panic("Unable to map EFI memory map.\n"); + + early_memunmap(tbl, sizeof(*tbl)); + } +} + void __init loongson_efi_init(void) { int size; @@ -237,6 +286,10 @@ void __init loongson_efi_init(void) efi_config_parse_tables(config_tables, efi_systab->nr_tables, arch_tables); early_memunmap(config_tables, efi_nr_tables * size); + get_initrd(); + + init_new_memmap(); + if (screen_info.orig_video_isVGA == VIDEO_TYPE_EFI) memblock_reserve(screen_info.lfb_base, screen_info.lfb_size); } diff --git a/arch/loongarch/kernel/env.c b/arch/loongarch/kernel/env.c index 596e6635368e..64490a2764d2 100644 --- a/arch/loongarch/kernel/env.c +++ b/arch/loongarch/kernel/env.c @@ -22,7 +22,8 @@ void __init init_environ(void) { int efi_boot = fw_arg0; struct efi_memory_map_data data; - void *fdt_ptr = early_memremap_ro(fw_arg1, SZ_64K); + char *cmdline; + void *fdt_ptr; if (efi_bp) return; @@ -32,6 +33,20 @@ void __init init_environ(void) else clear_bit(EFI_BOOT, &efi.flags); + if (fw_arg2 == 0) + goto parse_fdt; + + cmdline = early_memremap_ro(fw_arg1, COMMAND_LINE_SIZE); + strscpy(boot_command_line, cmdline, COMMAND_LINE_SIZE); + early_memunmap(cmdline, COMMAND_LINE_SIZE); + + efi_system_table = fw_arg2; + + return; + +parse_fdt: + fdt_ptr = early_memremap_ro(fw_arg1, SZ_64K); + early_init_dt_scan(fdt_ptr); early_init_fdt_reserve_self(); efi_system_table = efi_get_fdt_params(&data); diff --git a/arch/loongarch/kernel/legacy_boot.c b/arch/loongarch/kernel/legacy_boot.c index d8f749653939..99b69173460d 100644 --- a/arch/loongarch/kernel/legacy_boot.c +++ b/arch/loongarch/kernel/legacy_boot.c @@ -525,7 +525,7 @@ unsigned long legacy_boot_init(unsigned long argc, unsigned long cmdptr, unsigne { int ret; - if (!bpi) + if (!bpi || (argc < 2)) return -1; efi_bp = (struct boot_params *)bpi; bpi_version = get_bpi_version(&efi_bp->signature); diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c index 887ae4a92438..35d99525485b 100644 --- a/arch/loongarch/kernel/setup.c +++ b/arch/loongarch/kernel/setup.c @@ -216,7 +216,6 @@ static void __init set_pcie_wakeup(void) void __init platform_init(void) { - loongson_efi_init(); #ifdef CONFIG_ACPI_TABLE_UPGRADE acpi_table_upgrade(); #endif @@ -378,6 +377,7 @@ void __init setup_arch(char **cmdline_p) legacy_boot_init(fw_arg0, fw_arg1, fw_arg2); init_environ(); + loongson_efi_init(); memblock_init(); pagetable_init(); parse_early_param(); -- Gitee From 9f04b7c4d0efc7e2d07fdf9d0f29488d5a532367 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 14 Nov 2022 09:46:21 +0800 Subject: [PATCH 37/56] LoongArch: use non-atomic op during copy_process there is no competition during copy_process, non-atomic op is safe here. Change-Id: Iec2d80dc51022c030908ee14dbb9b654e80ee68d Signed-off-by: Bibo Mao --- arch/loongarch/kernel/process.c | 32 +++++++++++++++----------------- include/linux/sched.h | 10 ++++++++++ include/linux/thread_bits.h | 15 +++++++++++++++ 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/arch/loongarch/kernel/process.c b/arch/loongarch/kernel/process.c index 86600e39799e..328b53e78555 100644 --- a/arch/loongarch/kernel/process.c +++ b/arch/loongarch/kernel/process.c @@ -71,10 +71,6 @@ void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp) unsigned long euen; /* New thread loses kernel privileges. */ - crmd = regs->csr_crmd & ~(PLV_MASK); - crmd |= PLV_USER; - regs->csr_crmd = crmd; - prmd = regs->csr_prmd & ~(PLV_MASK); prmd |= PLV_USER; regs->csr_prmd = prmd; @@ -83,11 +79,12 @@ void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp) regs->csr_euen = euen; lose_fpu(0); - clear_thread_flag(TIF_LSX_CTX_LIVE); - clear_thread_flag(TIF_LASX_CTX_LIVE); + __clear_thread_flag(TIF_LSX_CTX_LIVE); + __clear_thread_flag(TIF_LASX_CTX_LIVE); clear_used_math(); regs->csr_era = pc; regs->regs[3] = sp; + smp_wmb(); } void exit_thread(struct task_struct *tsk) @@ -140,10 +137,6 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, /* Put the stack after the struct pt_regs. */ childksp = (unsigned long) childregs; p->thread.sched_cfa = 0; - p->thread.csr_euen = 0; - p->thread.csr_crmd = csr_read32(LOONGARCH_CSR_CRMD); - p->thread.csr_prmd = csr_read32(LOONGARCH_CSR_PRMD); - p->thread.csr_ecfg = csr_read32(LOONGARCH_CSR_ECFG); if (unlikely(p->flags & (PF_KTHREAD | PF_IO_WORKER))) { /* kernel thread */ p->thread.reg23 = usp; /* fn */ @@ -152,15 +145,19 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, p->thread.reg01 = (unsigned long) ret_from_kernel_thread; p->thread.sched_ra = (unsigned long) ret_from_kernel_thread; memset(childregs, 0, sizeof(struct pt_regs)); - childregs->csr_euen = p->thread.csr_euen; - childregs->csr_crmd = p->thread.csr_crmd; - childregs->csr_prmd = p->thread.csr_prmd; + p->thread.csr_ecfg = csr_read32(LOONGARCH_CSR_ECFG); + childregs->csr_euen = 0; + childregs->csr_prmd = csr_read32(LOONGARCH_CSR_PRMD); childregs->csr_ecfg = p->thread.csr_ecfg; return 0; } /* user thread */ *childregs = *regs; + + childregs->csr_prmd &= ~CSR_PRMD_PPLV; + childregs->csr_prmd |= PLV_USER; + childregs->regs[4] = 0; /* Child gets zero as return value */ if (usp) childregs->regs[3] = usp; @@ -175,14 +172,15 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, */ childregs->csr_euen = 0; - clear_tsk_thread_flag(p, TIF_USEDFPU); - clear_tsk_thread_flag(p, TIF_USEDSIMD); - clear_tsk_thread_flag(p, TIF_LSX_CTX_LIVE); - clear_tsk_thread_flag(p, TIF_LASX_CTX_LIVE); + __clear_tsk_thread_flag(p, TIF_USEDFPU); + __clear_tsk_thread_flag(p, TIF_USEDSIMD); + __clear_tsk_thread_flag(p, TIF_LSX_CTX_LIVE); + __clear_tsk_thread_flag(p, TIF_LASX_CTX_LIVE); if (clone_flags & CLONE_SETTLS) childregs->regs[2] = tls; + smp_wmb(); return 0; } diff --git a/include/linux/sched.h b/include/linux/sched.h index dc9068d64581..084a21ddb096 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1889,6 +1889,16 @@ static inline unsigned long wait_task_inactive(struct task_struct *p, long match * Set thread flags in other task's structures. * See asm/thread_info.h for TIF_xxxx flags available: */ +static inline void __set_tsk_thread_flag(struct task_struct *tsk, int flag) +{ + __set_ti_thread_flag(task_thread_info(tsk), flag); +} + +static inline void __clear_tsk_thread_flag(struct task_struct *tsk, int flag) +{ + __clear_ti_thread_flag(task_thread_info(tsk), flag); +} + static inline void set_tsk_thread_flag(struct task_struct *tsk, int flag) { set_ti_thread_flag(task_thread_info(tsk), flag); diff --git a/include/linux/thread_bits.h b/include/linux/thread_bits.h index 0f6fe55744f1..0d65206235e0 100644 --- a/include/linux/thread_bits.h +++ b/include/linux/thread_bits.h @@ -36,6 +36,16 @@ enum { * - pass TIF_xxxx constants to these functions */ +static inline void __set_ti_thread_flag(struct thread_info *ti, int flag) +{ + __set_bit(flag, (unsigned long *)&ti->flags); +} + +static inline void __clear_ti_thread_flag(struct thread_info *ti, int flag) +{ + __clear_bit(flag, (unsigned long *)&ti->flags); +} + static inline void set_ti_thread_flag(struct thread_info *ti, int flag) { set_bit(flag, (unsigned long *)&ti->flags); @@ -70,6 +80,11 @@ static inline int test_ti_thread_flag(struct thread_info *ti, int flag) return test_bit(flag, (unsigned long *)&ti->flags); } +#define __set_thread_flag(flag) \ + __set_ti_thread_flag(current_thread_info(), flag) +#define __clear_thread_flag(flag) \ + __clear_ti_thread_flag(current_thread_info(), flag) + #define set_thread_flag(flag) \ set_ti_thread_flag(current_thread_info(), flag) #define clear_thread_flag(flag) \ -- Gitee From 79ed0fa8d577c82ae0738779e6b6263dc0f2aeae Mon Sep 17 00:00:00 2001 From: Hongchen Zhang Date: Wed, 30 Nov 2022 18:38:34 +0800 Subject: [PATCH 38/56] LoongArch: use 40 bits address space for user default to use 40 bits address space for user. Signed-off-by: Hongchen Zhang Change-Id: I23088f43e700ed9b4eb15df2cc9c58299a0582ff --- arch/loongarch/Kconfig | 7 +++++++ arch/loongarch/include/asm/pgtable.h | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index c8619a3c16f8..7ff49eba631b 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -426,6 +426,13 @@ config NODES_SHIFT default "6" depends on NUMA +config VA_BITS_40 + bool "40-bits" + default y + depends on 64BIT + help + Support a maximum at least 40 bits of application virtual memory. + config FORCE_MAX_ZONEORDER int "Maximum zone order" range 14 64 if PAGE_SIZE_64KB diff --git a/arch/loongarch/include/asm/pgtable.h b/arch/loongarch/include/asm/pgtable.h index e5ed54fce402..27aa3d03162d 100644 --- a/arch/loongarch/include/asm/pgtable.h +++ b/arch/loongarch/include/asm/pgtable.h @@ -42,7 +42,11 @@ #define PGDIR_SIZE (1UL << PGDIR_SHIFT) #define PGDIR_MASK (~(PGDIR_SIZE-1)) +#ifdef CONFIG_VA_BITS_40 +#define VA_BITS 40 +#else #define VA_BITS (PGDIR_SHIFT + (PAGE_SHIFT - 3)) +#endif #define PTRS_PER_PGD (PAGE_SIZE >> 3) #if CONFIG_PGTABLE_LEVELS > 3 -- Gitee From 70eeb917bafa69d2696c3e34494982af02eef067 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 9 Dec 2022 18:05:54 +0800 Subject: [PATCH 39/56] LoongArch: refresh usage of sync sync is used for ll/sc pair for read barrier, instead only ll is required for read barrier, sc does not need. This patch fixes the issue. Change-Id: Ib8a42f50d494e3e80cda0843d069668604b486e6 Signed-off-by: Bibo Mao --- arch/loongarch/include/asm/atomic.h | 8 ++++++++ arch/loongarch/include/asm/cmpxchg.h | 2 ++ arch/loongarch/include/asm/futex.h | 2 ++ arch/loongarch/include/asm/pgtable.h | 3 ++- 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/include/asm/atomic.h b/arch/loongarch/include/asm/atomic.h index 98a24f221746..aad2df1af323 100644 --- a/arch/loongarch/include/asm/atomic.h +++ b/arch/loongarch/include/asm/atomic.h @@ -162,8 +162,10 @@ static inline int arch_atomic_sub_if_positive(int i, atomic_t *v) " bltz %0, 2f \n" " sc.w %1, %2 \n" " beqz %1, 1b \n" + " b 3f \n" "2: \n" __WEAK_LLSC_MB + "3: \n" : "=&r" (result), "=&r" (temp), "+ZC" (v->counter) : "I" (-i)); } else { @@ -174,8 +176,10 @@ static inline int arch_atomic_sub_if_positive(int i, atomic_t *v) " bltz %0, 2f \n" " sc.w %1, %2 \n" " beqz %1, 1b \n" + " b 3f \n" "2: \n" __WEAK_LLSC_MB + "3: \n" : "=&r" (result), "=&r" (temp), "+ZC" (v->counter) : "r" (i)); } @@ -323,8 +327,10 @@ static inline long arch_atomic64_sub_if_positive(long i, atomic64_t *v) " bltz %0, 2f \n" " sc.d %1, %2 \n" " beqz %1, 1b \n" + " b 3f \n" "2: \n" __WEAK_LLSC_MB + "3: \n" : "=&r" (result), "=&r" (temp), "+ZC" (v->counter) : "I" (-i)); } else { @@ -335,8 +341,10 @@ static inline long arch_atomic64_sub_if_positive(long i, atomic64_t *v) " bltz %0, 2f \n" " sc.d %1, %2 \n" " beqz %1, 1b \n" + " b 3f \n" "2: \n" __WEAK_LLSC_MB + "3: \n" : "=&r" (result), "=&r" (temp), "+ZC" (v->counter) : "r" (i)); } diff --git a/arch/loongarch/include/asm/cmpxchg.h b/arch/loongarch/include/asm/cmpxchg.h index ae19e33c7754..1a47d5ee1196 100644 --- a/arch/loongarch/include/asm/cmpxchg.h +++ b/arch/loongarch/include/asm/cmpxchg.h @@ -102,8 +102,10 @@ static inline unsigned long __xchg(volatile void *ptr, unsigned long x, " move $t0, %z4 \n" \ " " st " $t0, %1 \n" \ " beqz $t0, 1b \n" \ + " b 3f \n" \ "2: \n" \ __WEAK_LLSC_MB \ + "3: \n" \ : "=&r" (__ret), "=ZB"(*m) \ : "ZB"(*m), "Jr" (old), "Jr" (new) \ : "t0", "memory"); \ diff --git a/arch/loongarch/include/asm/futex.h b/arch/loongarch/include/asm/futex.h index feb6658c84ff..f0208f4aec3e 100644 --- a/arch/loongarch/include/asm/futex.h +++ b/arch/loongarch/include/asm/futex.h @@ -84,8 +84,10 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, u32 oldval, u32 newv " move $t0, %z5 \n" "2: sc.w $t0, %2 \n" " beqz $t0, 1b \n" + " b 5f \n" "3: \n" __WEAK_LLSC_MB + "5: \n" " .section .fixup,\"ax\" \n" "4: li.d %0, %6 \n" " b 3b \n" diff --git a/arch/loongarch/include/asm/pgtable.h b/arch/loongarch/include/asm/pgtable.h index 27aa3d03162d..eb3318927644 100644 --- a/arch/loongarch/include/asm/pgtable.h +++ b/arch/loongarch/include/asm/pgtable.h @@ -298,9 +298,10 @@ static inline void set_pte(pte_t *ptep, pte_t pteval) " or %[tmp], %[tmp], %[global] \n" __SC "%[tmp], %[buddy] \n" " beqz %[tmp], 1b \n" - " nop \n" + " b 3f \n" "2: \n" __WEAK_LLSC_MB + "3: \n" : [buddy] "+m" (buddy->pte), [tmp] "=&r" (tmp) : [global] "r" (page_global)); #else /* !CONFIG_SMP */ -- Gitee From da37d691342c61e813bb65d74c5cf338b3c5d0b4 Mon Sep 17 00:00:00 2001 From: Hongchen Zhang Date: Wed, 14 Dec 2022 18:15:42 +0800 Subject: [PATCH 40/56] LoongArch: optimize for syscall return If the syscall is not rt_sigreturn,there is no need to do RESTORE_STATIC and RESTORE_TEMP. Signed-off-by: Hongchen Zhang Change-Id: I61804bb16ce678dd39e9f197bd88d91e13b972cb --- arch/loongarch/kernel/entry.S | 17 +++++++++++++---- arch/loongarch/kernel/syscall.c | 10 +++++----- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/arch/loongarch/kernel/entry.S b/arch/loongarch/kernel/entry.S index 893e632e76da..8670e9d128ab 100644 --- a/arch/loongarch/kernel/entry.S +++ b/arch/loongarch/kernel/entry.S @@ -14,13 +14,14 @@ #include #include #include +#include .text .cfi_sections .debug_frame .align 5 SYM_FUNC_START(handle_syscall) csrrd t0, PERCPU_BASE_KS - la.abs t1, kernelsp + la.pcrel t1, kernelsp add.d t1, t1, t0 move t2, sp ld.d sp, t1, 0 @@ -28,11 +29,10 @@ SYM_FUNC_START(handle_syscall) addi.d sp, sp, -PT_SIZE cfi_st t2, PT_R3 cfi_rel_offset sp, PT_R3 - st.d zero, sp, PT_R0 csrrd t2, LOONGARCH_CSR_PRMD st.d t2, sp, PT_PRMD cfi_st ra, PT_R1 - cfi_st a0, PT_R4 + cfi_st a0, PT_ORIG_A0 cfi_st a1, PT_R5 cfi_st a2, PT_R6 cfi_st a3, PT_R7 @@ -41,6 +41,7 @@ SYM_FUNC_START(handle_syscall) cfi_st a6, PT_R10 cfi_st a7, PT_R11 csrrd ra, LOONGARCH_CSR_ERA + addi.d ra, ra, 4 st.d ra, sp, PT_ERA cfi_rel_offset ra, PT_ERA @@ -55,9 +56,17 @@ SYM_FUNC_START(handle_syscall) and tp, tp, sp move a0, sp + move a1, a7 bl do_syscall - RESTORE_ALL_AND_RET + addi.w t0, zero, __NR_rt_sigreturn + bne a0, t0, 1f + + RESTORE_STATIC + RESTORE_TEMP +1: + RESTORE_SOME + RESTORE_SP_AND_RET SYM_FUNC_END(handle_syscall) SYM_CODE_START(ret_from_fork) diff --git a/arch/loongarch/kernel/syscall.c b/arch/loongarch/kernel/syscall.c index 3fc4211db989..796fcdcaa6a7 100644 --- a/arch/loongarch/kernel/syscall.c +++ b/arch/loongarch/kernel/syscall.c @@ -37,18 +37,16 @@ void *sys_call_table[__NR_syscalls] = { typedef long (*sys_call_fn)(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); -void noinstr do_syscall(struct pt_regs *regs) +unsigned long noinstr do_syscall(struct pt_regs *regs, unsigned long nr) { - unsigned long nr; sys_call_fn syscall_fn; - nr = regs->regs[11]; /* Set for syscall restarting */ if (nr < NR_syscalls) regs->regs[0] = nr + 1; + else + regs->regs[0] = 0; - regs->csr_era += 4; - regs->orig_a0 = regs->regs[4]; regs->regs[4] = -ENOSYS; nr = syscall_enter_from_user_mode(regs, nr); @@ -60,4 +58,6 @@ void noinstr do_syscall(struct pt_regs *regs) } syscall_exit_to_user_mode(regs); + + return nr; } -- Gitee From 41d0d05aab64955412f2d8a4f4d9eb87ae502917 Mon Sep 17 00:00:00 2001 From: Hongchen Zhang Date: Wed, 14 Dec 2022 18:26:13 +0800 Subject: [PATCH 41/56] LoongArch: save one instruction for arch_local_irq_{enable,disable} Signed-off-by: Hongchen Zhang Change-Id: I607e48f5abea1184b22326e772d809f6994603cc --- arch/loongarch/include/asm/irqflags.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/loongarch/include/asm/irqflags.h b/arch/loongarch/include/asm/irqflags.h index 319a8c616f1f..53eb33b2c258 100644 --- a/arch/loongarch/include/asm/irqflags.h +++ b/arch/loongarch/include/asm/irqflags.h @@ -17,16 +17,15 @@ static inline void arch_local_irq_enable(void) __asm__ __volatile__( "csrxchg %[val], %[mask], %[reg]\n\t" : [val] "+r" (flags) - : [mask] "r" (CSR_CRMD_IE), [reg] "i" (LOONGARCH_CSR_CRMD) + : [mask] "r" (flags), [reg] "i" (LOONGARCH_CSR_CRMD) : "memory"); } static inline void arch_local_irq_disable(void) { - u32 flags = 0; __asm__ __volatile__( - "csrxchg %[val], %[mask], %[reg]\n\t" - : [val] "+r" (flags) + "csrxchg $zero, %[mask], %[reg]\n\t" + : : [mask] "r" (CSR_CRMD_IE), [reg] "i" (LOONGARCH_CSR_CRMD) : "memory"); } -- Gitee From c8e814e74623ab5267e8b8e72dda5cada3b3dca9 Mon Sep 17 00:00:00 2001 From: Hongchen Zhang Date: Thu, 15 Dec 2022 18:09:15 +0800 Subject: [PATCH 42/56] LoongArch: enable hugetlbfs support Signed-off-by: Hongchen Zhang Change-Id: I4631175847064093af8f143d0c46b267a82de069 --- arch/loongarch/Kconfig | 3 +++ arch/loongarch/configs/loongson3_defconfig | 1 + arch/loongarch/mm/hugetlbpage.c | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 7ff49eba631b..0436e79e3928 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -497,6 +497,9 @@ config ARCH_SPARSEMEM_ENABLE or have huge holes in the physical address space for other reasons. See for more. +config SYS_SUPPORTS_HUGETLBFS + def_bool y + config ARCH_ENABLE_THP_MIGRATION def_bool y depends on TRANSPARENT_HUGEPAGE diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index 0773937ebcb9..63b38c184853 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -735,6 +735,7 @@ CONFIG_FAT_DEFAULT_IOCHARSET="gb2312" CONFIG_PROC_KCORE=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y +CONFIG_HUGETLBFS=y CONFIG_CONFIGFS_FS=y CONFIG_HFS_FS=m CONFIG_HFSPLUS_FS=m diff --git a/arch/loongarch/mm/hugetlbpage.c b/arch/loongarch/mm/hugetlbpage.c index ba138117b124..97ed6f1d1c9b 100644 --- a/arch/loongarch/mm/hugetlbpage.c +++ b/arch/loongarch/mm/hugetlbpage.c @@ -13,8 +13,8 @@ #include #include -pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma, - unsigned long addr, unsigned long sz) +pte_t *huge_pte_alloc(struct mm_struct *mm, + unsigned long addr, unsigned long sz) { pgd_t *pgd; p4d_t *p4d; -- Gitee From 675c72dc2b21f1b937f18f19e402735b1f2d4f41 Mon Sep 17 00:00:00 2001 From: Hongchen Zhang Date: Fri, 30 Dec 2022 03:06:32 +0800 Subject: [PATCH 43/56] LoongArch: fix vmlinux ld error When CONFIG_DEBUG_INFO_BTF is enabled, ld load error. To fix this issue, we should use -mdirect-extern-access. Signed-off-by: Xing Li Signed-off-by: Hongchen Zhang Change-Id: I698169b2af48369d531ef3aa4f9c53b97096da83 --- arch/loongarch/Makefile | 1 + drivers/firmware/efi/libstub/Makefile | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile index 964b779b130d..cb14e7f96401 100644 --- a/arch/loongarch/Makefile +++ b/arch/loongarch/Makefile @@ -54,6 +54,7 @@ LDFLAGS_vmlinux += -G0 -static -n -nostdlib # upgrade the compiler or downgrade the assembler. ifdef CONFIG_AS_HAS_EXPLICIT_RELOCS cflags-y += -mexplicit-relocs +KBUILD_CFLAGS_KERNEL += -mdirect-extern-access else cflags-y += $(call cc-option,-mno-explicit-relocs) KBUILD_AFLAGS_KERNEL += -Wa,-mla-global-with-pcrel diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 6d4add16e9a3..fc5c4f0b14a5 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -27,7 +27,7 @@ cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ cflags-$(CONFIG_RISCV) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ -fpic cflags-$(CONFIG_LOONGARCH) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ - -fpic + -fpie cflags-$(CONFIG_EFI_GENERIC_STUB) += -I$(srctree)/scripts/dtc/libfdt -- Gitee From 0207a57ad0926f4a08198f5601cd945b610d8ed2 Mon Sep 17 00:00:00 2001 From: Hongchen Zhang Date: Sat, 31 Dec 2022 00:58:44 +0800 Subject: [PATCH 44/56] LoongArch: fix SECCOMP test error When we test the ltp testcase prctl04,we get failed.The reason is _TIF_SECOMP not defined for LoongArch.So define the _TIF_SECCOMP for LoongArch. Signed-off-by: Hongchen Zhang Change-Id: I6e890cf347839f301b45036693a0cce3736d3ce3 --- arch/loongarch/include/asm/thread_info.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/loongarch/include/asm/thread_info.h b/arch/loongarch/include/asm/thread_info.h index c9030464cbb5..43e74b37df60 100644 --- a/arch/loongarch/include/asm/thread_info.h +++ b/arch/loongarch/include/asm/thread_info.h @@ -112,6 +112,7 @@ static inline unsigned long current_stack_pointer(void) #define _TIF_LASX_CTX_LIVE (1< Date: Mon, 5 Dec 2022 17:23:23 +0800 Subject: [PATCH 45/56] LoongArch: kvm: add initial kvm support add initial kvm support. Signed-off-by: qemudev Change-Id: I1f6d361dd54299d97748a360686171e2c34a203c --- arch/loongarch/Kbuild | 1 + arch/loongarch/Kconfig | 2 + arch/loongarch/include/asm/kvm_host.h | 356 ++++ arch/loongarch/include/asm/kvm_types.h | 8 + arch/loongarch/include/uapi/asm/kvm.h | 268 +++ arch/loongarch/kvm/Kconfig | 54 + arch/loongarch/kvm/Makefile | 23 + arch/loongarch/kvm/csr.c | 682 +++++++ arch/loongarch/kvm/emulate.c | 331 ++++ arch/loongarch/kvm/entry.S | 328 ++++ arch/loongarch/kvm/exit.c | 497 +++++ arch/loongarch/kvm/fpu.c | 85 + arch/loongarch/kvm/hypcall.c | 104 ++ arch/loongarch/kvm/intc/irqchip-debug.c | 49 + arch/loongarch/kvm/intc/ls3a_ext_irq.c | 937 ++++++++++ arch/loongarch/kvm/intc/ls3a_ext_irq.h | 131 ++ arch/loongarch/kvm/intc/ls3a_ipi.c | 290 +++ arch/loongarch/kvm/intc/ls3a_ipi.h | 76 + arch/loongarch/kvm/intc/ls7a_irq.c | 488 +++++ arch/loongarch/kvm/intc/ls7a_irq.h | 112 ++ arch/loongarch/kvm/interrupt.c | 133 ++ arch/loongarch/kvm/irqfd.c | 101 ++ arch/loongarch/kvm/kvm_compat.c | 58 + arch/loongarch/kvm/kvm_compat.h | 766 ++++++++ arch/loongarch/kvm/kvmcpu.h | 124 ++ arch/loongarch/kvm/kvmcsr.h | 115 ++ arch/loongarch/kvm/loongarch.c | 2215 +++++++++++++++++++++++ arch/loongarch/kvm/ls_irq.h | 14 + arch/loongarch/kvm/mmu.c | 1336 ++++++++++++++ arch/loongarch/kvm/timer.c | 274 +++ arch/loongarch/kvm/trace.h | 201 ++ 31 files changed, 10159 insertions(+) create mode 100644 arch/loongarch/include/asm/kvm_host.h create mode 100644 arch/loongarch/include/asm/kvm_types.h create mode 100644 arch/loongarch/include/uapi/asm/kvm.h create mode 100644 arch/loongarch/kvm/Kconfig create mode 100644 arch/loongarch/kvm/Makefile create mode 100644 arch/loongarch/kvm/csr.c create mode 100644 arch/loongarch/kvm/emulate.c create mode 100644 arch/loongarch/kvm/entry.S create mode 100644 arch/loongarch/kvm/exit.c create mode 100644 arch/loongarch/kvm/fpu.c create mode 100644 arch/loongarch/kvm/hypcall.c create mode 100644 arch/loongarch/kvm/intc/irqchip-debug.c create mode 100644 arch/loongarch/kvm/intc/ls3a_ext_irq.c create mode 100644 arch/loongarch/kvm/intc/ls3a_ext_irq.h create mode 100644 arch/loongarch/kvm/intc/ls3a_ipi.c create mode 100644 arch/loongarch/kvm/intc/ls3a_ipi.h create mode 100644 arch/loongarch/kvm/intc/ls7a_irq.c create mode 100644 arch/loongarch/kvm/intc/ls7a_irq.h create mode 100644 arch/loongarch/kvm/interrupt.c create mode 100644 arch/loongarch/kvm/irqfd.c create mode 100644 arch/loongarch/kvm/kvm_compat.c create mode 100644 arch/loongarch/kvm/kvm_compat.h create mode 100644 arch/loongarch/kvm/kvmcpu.h create mode 100644 arch/loongarch/kvm/kvmcsr.h create mode 100644 arch/loongarch/kvm/loongarch.c create mode 100644 arch/loongarch/kvm/ls_irq.h create mode 100644 arch/loongarch/kvm/mmu.c create mode 100644 arch/loongarch/kvm/timer.c create mode 100644 arch/loongarch/kvm/trace.h diff --git a/arch/loongarch/Kbuild b/arch/loongarch/Kbuild index ab5373d0a24f..30d9afffc837 100644 --- a/arch/loongarch/Kbuild +++ b/arch/loongarch/Kbuild @@ -1,6 +1,7 @@ obj-y += kernel/ obj-y += mm/ obj-y += vdso/ +obj-$(CONFIG_KVM) += kvm/ # for cleaning subdir- += boot diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 0436e79e3928..34eff7c52582 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -106,6 +106,7 @@ config LOONGARCH select HAVE_SETUP_PER_CPU_AREA if NUMA select HAVE_SYSCALL_TRACEPOINTS select HAVE_TIF_NOHZ + select HAVE_KVM select HAVE_VIRT_CPU_ACCOUNTING_GEN if !SMP select IRQ_FORCED_THREADING select IRQ_LOONGARCH_CPU @@ -539,3 +540,4 @@ source "drivers/cpufreq/Kconfig" endmenu source "drivers/firmware/Kconfig" +source "arch/loongarch/kvm/Kconfig" diff --git a/arch/loongarch/include/asm/kvm_host.h b/arch/loongarch/include/asm/kvm_host.h new file mode 100644 index 000000000000..56319843ddfd --- /dev/null +++ b/arch/loongarch/include/asm/kvm_host.h @@ -0,0 +1,356 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#ifndef __LOONGARCH_KVM_HOST_H__ +#define __LOONGARCH_KVM_HOST_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Loongarch KVM register ids */ +#define LOONGARCH_CSR_32(_R, _S) \ + (KVM_REG_LOONGARCH_CSR | KVM_REG_SIZE_U32 | (8 * (_R) + (_S))) + +#define LOONGARCH_CSR_64(_R, _S) \ + (KVM_REG_LOONGARCH_CSR | KVM_REG_SIZE_U64 | (8 * (_R) + (_S))) + +#define KVM_IOC_CSRID(id) LOONGARCH_CSR_64(id, 0) +#define KVM_GET_IOC_CSRIDX(id) ((id & KVM_CSR_IDX_MASK) >> 3) + +#define LOONGSON_VIRT_REG_BASE 0x1f000000 +#define KVM_MAX_VCPUS 256 +#define KVM_USER_MEM_SLOTS 256 +/* memory slots that does not exposed to userspace */ +#define KVM_PRIVATE_MEM_SLOTS 0 + +#define KVM_HALT_POLL_NS_DEFAULT 500000 + +#define KVM_REQ_RECORD_STEAL KVM_ARCH_REQ(1) +#define KVM_INVALID_ADDR 0xdeadbeef +#define KVM_HVA_ERR_BAD (-1UL) +#define KVM_HVA_ERR_RO_BAD (-2UL) +static inline bool kvm_is_error_hva(unsigned long addr) +{ + return IS_ERR_VALUE(addr); +} + +struct kvm_vm_stat { + ulong remote_tlb_flush; + u64 vm_ioctl_irq_line; + u64 ls7a_ioapic_update; + u64 ls7a_ioapic_set_irq; + u64 ioapic_reg_write; + u64 ioapic_reg_read; + u64 set_ls7a_ioapic; + u64 get_ls7a_ioapic; + u64 set_ls3a_ext_irq; + u64 get_ls3a_ext_irq; + u64 trigger_ls3a_ext_irq; + u64 pip_read_exits; + u64 pip_write_exits; + u64 ls7a_msi_irq; +}; +struct kvm_vcpu_stat { + u64 excep_exits[EXCCODE_INT_START]; + u64 idle_exits; + u64 signal_exits; + u64 int_exits; + u64 rdcsr_cpu_feature_exits; + u64 rdcsr_misc_func_exits; + u64 rdcsr_ipi_access_exits; + u64 cpucfg_exits; + u64 huge_dec_exits; + u64 huge_thp_exits; + u64 huge_adjust_exits; + u64 huge_set_exits; + u64 huge_merge_exits; + u64 halt_successful_poll; + u64 halt_attempted_poll; + u64 halt_poll_invalid; + u64 halt_wakeup; +}; + +#define KVM_MEMSLOT_DISABLE_THP (1UL << 17) +struct kvm_arch_memory_slot { + unsigned int flags; +}; + +enum { + IOCSR_FEATURES, + IOCSR_VENDOR, + IOCSR_CPUNAME, + IOCSR_NODECNT, + IOCSR_MISC_FUNC, + IOCSR_MAX +}; + +struct kvm_context { + unsigned long gid_mask; + unsigned long gid_ver_mask; + unsigned long gid_fisrt_ver; + unsigned long vpid_cache; + struct kvm_vcpu *last_vcpu; +}; + +struct kvm_arch { + /* Guest physical mm */ + struct mm_struct gpa_mm; + /* Mask of CPUs needing GPA ASID flush */ + cpumask_t asid_flush_mask; + + unsigned char online_vcpus; + unsigned char is_migrate; + s64 stablecounter_gftoffset; + u32 cpucfg_lasx; + struct ls7a_kvm_ioapic *v_ioapic; + struct ls3a_kvm_ipi *v_gipi; + struct ls3a_kvm_routerirq *v_routerirq; + struct ls3a_kvm_extirq *v_extirq; + spinlock_t iocsr_lock; + struct kvm_iocsr_entry iocsr[IOCSR_MAX]; + struct kvm_cpucfg cpucfgs; + struct kvm_context __percpu *vmcs; +}; + + +#define LOONGARCH_CSRS 0x100 +#define CSR_UCWIN_BASE 0x100 +#define CSR_UCWIN_SIZE 0x10 +#define CSR_DMWIN_BASE 0x180 +#define CSR_DMWIN_SIZE 0x4 +#define CSR_PERF_BASE 0x200 +#define CSR_PERF_SIZE 0x8 +#define CSR_DEBUG_BASE 0x500 +#define CSR_DEBUG_SIZE 0x3 +#define CSR_ALL_SIZE 0x800 + +struct loongarch_csrs { + unsigned long csrs[CSR_ALL_SIZE]; +}; + +/* Resume Flags */ +#define RESUME_FLAG_DR (1<<0) /* Reload guest nonvolatile state? */ +#define RESUME_FLAG_HOST (1<<1) /* Resume host? */ + +#define RESUME_GUEST 0 +#define RESUME_GUEST_DR RESUME_FLAG_DR +#define RESUME_HOST RESUME_FLAG_HOST + +enum emulation_result { + EMULATE_DONE, /* no further processing */ + EMULATE_DO_MMIO, /* kvm_run filled with MMIO request */ + EMULATE_FAIL, /* can't emulate this instruction */ + EMULATE_WAIT, /* WAIT instruction */ + EMULATE_PRIV_FAIL, + EMULATE_EXCEPT, /* A guest exception has been generated */ + EMULATE_PV_HYPERCALL, /* HYPCALL instruction */ + EMULATE_DEBUG, /* Emulate guest kernel debug */ + EMULATE_DO_IOCSR, /* handle IOCSR request */ +}; + +#define KVM_NR_MEM_OBJS 4 +/* + * We don't want allocation failures within the mmu code, so we preallocate + * enough memory for a single page fault in a cache. + */ +struct kvm_mmu_memory_cache { + int nobjs; + void *objects[KVM_NR_MEM_OBJS]; +}; + +#if defined(CONFIG_CPU_HAS_LASX) +#define FPU_ALIGN __aligned(32) +#elif defined(CONFIG_CPU_HAS_LSX) +#define FPU_ALIGN __aligned(16) +#else +#define FPU_ALIGN +#endif +#define KVM_LARCH_FPU (0x1 << 0) +#define KVM_LARCH_LSX (0x1 << 1) +#define KVM_LARCH_LASX (0x1 << 2) +#define KVM_LARCH_DATA_HWBP (0x1 << 3) +#define KVM_LARCH_INST_HWBP (0x1 << 4) +#define KVM_LARCH_HWBP (KVM_LARCH_DATA_HWBP | KVM_LARCH_INST_HWBP) +#define KVM_LARCH_RESET (0x1 << 5) +#define KVM_LARCH_PERF (0x1 << 6) + +struct kvm_vcpu_arch { + unsigned long guest_eentry; + unsigned long host_eentry; + int (*vcpu_run)(struct kvm_run *run, struct kvm_vcpu *vcpu); + int (*handle_exit)(struct kvm_run *run, struct kvm_vcpu *vcpu); + + /* Host registers preserved across guest mode execution */ + unsigned long host_stack; + unsigned long host_gp; + unsigned long host_pgd; + unsigned long host_pgdhi; + unsigned long host_entryhi; + + /* Host CSR registers used when handling exits from guest */ + unsigned long badv; + unsigned long host_estat; + unsigned long badi; + unsigned long host_ecfg; + unsigned long host_percpu; + + u32 is_hypcall; + /* GPRS */ + unsigned long gprs[32]; + unsigned long pc; + + /* FPU State */ + struct loongarch_fpu fpu FPU_ALIGN; + /* Which auxiliary state is loaded (KVM_LOONGARCH_AUX_*) */ + unsigned int aux_inuse; + + /* CSR State */ + struct loongarch_csrs *csr; + + /* GPR used as IO source/target */ + u32 io_gpr; + + struct hrtimer swtimer; + /* Count timer control KVM register */ + u32 count_ctl; + + /* Bitmask of exceptions that are pending */ + unsigned long irq_pending; + /* Bitmask of pending exceptions to be cleared */ + unsigned long irq_clear; + + /* Cache some mmu pages needed inside spinlock regions */ + struct kvm_mmu_memory_cache mmu_page_cache; + + /* vcpu's vpid is different on each host cpu in an smp system */ + u64 vpid[NR_CPUS]; + + /* Period of stable timer tick in ns */ + u64 timer_period; + /* Frequency of stable timer in Hz */ + u64 timer_mhz; + /* Stable bias from the raw time */ + u64 timer_bias; + /* Dynamic nanosecond bias (multiple of timer_period) to avoid overflow */ + s64 timer_dyn_bias; + /* Save ktime */ + ktime_t stable_ktime_saved; + + u64 core_ext_ioisr[4]; + + /* Last CPU the VCPU state was loaded on */ + int last_sched_cpu; + /* Last CPU the VCPU actually executed guest code on */ + int last_exec_cpu; + + u8 fpu_enabled; + u8 lsx_enabled; + /* paravirt steal time */ + struct { + u64 guest_addr; + u64 last_steal; + struct gfn_to_pfn_cache cache; + } st; + struct kvm_guest_debug_arch guest_debug; + /* save host pmu csr */ + u64 perf_ctrl[4]; + u64 perf_cntr[4]; + +}; + +static inline unsigned long readl_sw_gcsr(struct loongarch_csrs *csr, int reg) +{ + return csr->csrs[reg]; +} + +static inline void writel_sw_gcsr(struct loongarch_csrs *csr, int reg, \ + unsigned long val) +{ + csr->csrs[reg] = val; +} + +/* Helpers */ +static inline bool _kvm_guest_has_fpu(struct kvm_vcpu_arch *arch) +{ + return cpu_has_fpu && arch->fpu_enabled; +} + + +static inline bool _kvm_guest_has_lsx(struct kvm_vcpu_arch *arch) +{ + return cpu_has_lsx && arch->lsx_enabled; +} + +bool _kvm_guest_has_lasx(struct kvm_vcpu *vcpu); +void _kvm_init_fault(void); + +/* Debug: dump vcpu state */ +int kvm_arch_vcpu_dump_regs(struct kvm_vcpu *vcpu); + +/* MMU handling */ +int kvm_handle_mm_fault(struct kvm_vcpu *vcpu, unsigned long badv, bool write); +void kvm_flush_tlb_all(void); +void _kvm_destroy_mm(struct kvm *kvm); +pgd_t *kvm_pgd_alloc(void); +void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu); + +enum _kvm_fault_result { + KVM_LOONGARCH_MAPPED = 0, + KVM_LOONGARCH_GVA, + KVM_LOONGARCH_GPA, + KVM_LOONGARCH_TLB, + KVM_LOONGARCH_TLBINV, + KVM_LOONGARCH_TLBMOD, +}; + +#define KVM_ARCH_WANT_MMU_NOTIFIER +int kvm_unmap_hva_range(struct kvm *kvm, + unsigned long start, unsigned long end, bool blockable); +void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte); +int kvm_age_hva(struct kvm *kvm, unsigned long start, unsigned long end); +int kvm_test_age_hva(struct kvm *kvm, unsigned long hva); + +static inline void update_pc(struct kvm_vcpu_arch *arch) +{ + arch->pc += 4; +} + +/** + * kvm_is_ifetch_fault() - Find whether a TLBL exception is due to ifetch fault. + * @vcpu: Virtual CPU. + * + * Returns: Whether the TLBL exception was likely due to an instruction + * fetch fault rather than a data load fault. + */ +static inline bool kvm_is_ifetch_fault(struct kvm_vcpu_arch *arch) +{ + if (arch->pc == arch->badv) + return true; + + return false; +} + +/* Misc */ +static inline void kvm_arch_hardware_unsetup(void) {} +static inline void kvm_arch_sync_events(struct kvm *kvm) {} +static inline void kvm_arch_free_memslot(struct kvm *kvm, + struct kvm_memory_slot *free, struct kvm_memory_slot *dont) {} +static inline void kvm_arch_memslots_updated(struct kvm *kvm, u64 gen) {} +static inline void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu) {} +static inline void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu) {} +static inline void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu) {} +static inline void kvm_arch_vcpu_block_finish(struct kvm_vcpu *vcpu) {} + +extern int kvm_enter_guest(struct kvm_run *run, struct kvm_vcpu *vcpu); +extern void kvm_exception_entry(void); +#endif /* __LOONGARCH_KVM_HOST_H__ */ diff --git a/arch/loongarch/include/asm/kvm_types.h b/arch/loongarch/include/asm/kvm_types.h new file mode 100644 index 000000000000..b79845a347f5 --- /dev/null +++ b/arch/loongarch/include/asm/kvm_types.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_LOONGARCH64_KVM_TYPES_H +#define _ASM_LOONGARCH64_KVM_TYPES_H + +#define KVM_ARCH_NR_OBJS_PER_MEMORY_CACHE 4 + +#endif /* _ASM_LOONGARCH64_KVM_TYPES_H */ + diff --git a/arch/loongarch/include/uapi/asm/kvm.h b/arch/loongarch/include/uapi/asm/kvm.h new file mode 100644 index 000000000000..3e785a2c6dd0 --- /dev/null +++ b/arch/loongarch/include/uapi/asm/kvm.h @@ -0,0 +1,268 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2020 Loongson Technologies, Inc. All rights reserved. + * Authors: Sanjay Lal + * Authors: Xing Li + */ + +#ifndef __LINUX_KVM_LOONGARCH_H +#define __LINUX_KVM_LOONGARCH_H + +#include +#ifndef __KERNEL__ +#include +#endif + +#define __KVM_HAVE_GUEST_DEBUG +#define KVM_GUESTDBG_USE_SW_BP 0x00010000 +#define KVM_GUESTDBG_USE_HW_BP 0x00020000 +#define KVM_DATA_HW_BREAKPOINT_NUM 8 +#define KVM_INST_HW_BREAKPOINT_NUM 8 + +/* + * KVM Loongarch specific structures and definitions. + * + * Some parts derived from the x86 version of this file. + */ + +#define __KVM_HAVE_READONLY_MEM + +#define KVM_COALESCED_MMIO_PAGE_OFFSET 1 + +/* + * for KVM_GET_REGS and KVM_SET_REGS + */ +struct kvm_regs { + /* out (KVM_GET_REGS) / in (KVM_SET_REGS) */ + __u64 gpr[32]; + __u64 pc; +}; + +/* + * for KVM_GET_CPUCFG + */ +struct kvm_cpucfg { + /* out (KVM_GET_CPUCFG) */ + __u32 cpucfg[64]; +}; + +/* + * for KVM_GET_FPU and KVM_SET_FPU + */ +struct kvm_fpu { + __u32 fcsr; + __u32 none; + __u64 fcc; /* 8x8 */ + struct kvm_fpureg { + __u64 val64[4]; //support max 256 bits + } fpr[32]; +}; + +/* + * For LOONGARCH, we use KVM_SET_ONE_REG and KVM_GET_ONE_REG to access various + * registers. The id field is broken down as follows: + * + * bits[63..52] - As per linux/kvm.h + * bits[51..32] - Must be zero. + * bits[31..16] - Register set. + * + * Register set = 0: GP registers from kvm_regs (see definitions below). + * + * Register set = 1: CSR registers. + * + * Register set = 2: KVM specific registers (see definitions below). + * + * Register set = 3: FPU / SIMD registers (see definitions below). + * + * Other sets registers may be added in the future. Each set would + * have its own identifier in bits[31..16]. + */ + +#define KVM_REG_LOONGARCH_GP (KVM_REG_LOONGARCH | 0x00000ULL) +#define KVM_REG_LOONGARCH_CSR (KVM_REG_LOONGARCH | 0x10000ULL) +#define KVM_REG_LOONGARCH_KVM (KVM_REG_LOONGARCH | 0x20000ULL) +#define KVM_REG_LOONGARCH_FPU (KVM_REG_LOONGARCH | 0x30000ULL) +#define KVM_REG_LOONGARCH_MASK (KVM_REG_LOONGARCH | 0x30000ULL) +#define KVM_CSR_IDX_MASK (0x10000 - 1) + +/* + * KVM_REG_LOONGARCH_KVM - KVM specific control registers. + */ + +#define KVM_REG_LOONGARCH_COUNTER (KVM_REG_LOONGARCH_KVM | KVM_REG_SIZE_U64 | 3) +#define KVM_REG_LOONGARCH_VCPU_RESET (KVM_REG_LOONGARCH_KVM | KVM_REG_SIZE_U64 | 4) + +#define __KVM_HAVE_IRQ_LINE + +struct kvm_debug_exit_arch { + __u64 era; + __u32 fwps; + __u32 mwps; + __u32 exception; +}; + +/* for KVM_SET_GUEST_DEBUG */ +struct hw_breakpoint { + __u64 addr; + __u64 mask; + __u32 asid; + __u32 ctrl; +}; + +struct kvm_guest_debug_arch { + struct hw_breakpoint data_breakpoint[KVM_DATA_HW_BREAKPOINT_NUM]; + struct hw_breakpoint inst_breakpoint[KVM_INST_HW_BREAKPOINT_NUM]; + int inst_bp_nums, data_bp_nums; +}; + +/* definition of registers in kvm_run */ +struct kvm_sync_regs { +}; + +/* dummy definition */ +struct kvm_sregs { +}; + +struct kvm_iocsr_entry { + __u32 addr; + __u32 pad; + __u64 data; +}; + +struct kvm_csr_entry { + __u32 index; + __u32 reserved; + __u64 data; +}; + +/* for KVM_GET_MSRS and KVM_SET_MSRS */ +struct kvm_msrs { + __u32 ncsrs; /* number of msrs in entries */ + __u32 pad; + + struct kvm_csr_entry entries[0]; +}; + +struct kvm_loongarch_interrupt { + /* in */ + __u32 cpu; + __u32 irq; +}; + +#define KVM_IRQCHIP_LS7A_IOAPIC 0x0 +#define KVM_IRQCHIP_LS3A_GIPI 0x1 +#define KVM_IRQCHIP_LS3A_HT_IRQ 0x2 +#define KVM_IRQCHIP_LS3A_ROUTE 0x3 +#define KVM_IRQCHIP_LS3A_EXTIRQ 0x4 +#define KVM_IRQCHIP_LS3A_IPMASK 0x5 +#define KVM_NR_IRQCHIPS 1 +#define KVM_IRQCHIP_NUM_PINS 64 + +#define KVM_MAX_CORES 256 +#define KVM_EXTIOI_IRQS (256) +#define KVM_EXTIOI_IRQS_BITMAP_SIZE (KVM_EXTIOI_IRQS / 8) +/* map to ipnum per 32 irqs */ +#define KVM_EXTIOI_IRQS_IPMAP_SIZE (KVM_EXTIOI_IRQS / 32) +#define KVM_EXTIOI_IRQS_PER_GROUP 32 +#define KVM_EXTIOI_IRQS_COREMAP_SIZE (KVM_EXTIOI_IRQS) +#define KVM_EXTIOI_IRQS_NODETYPE_SIZE 16 + +struct ls7a_ioapic_state { + /* 0x000 interrupt id register */ + __u64 int_id; + /* 0x020 interrupt mask register */ + __u64 int_mask; + /* 0x040 1=msi */ + __u64 htmsi_en; + /* 0x060 edge=1 level =0 */ + __u64 intedge; + /* 0x080 for clean edge int,set 1 clean,set 0 is noused */ + __u64 intclr; + /* 0x0c0 */ + __u64 auto_crtl0; + /* 0x0e0 */ + __u64 auto_crtl1; + /* 0x100 - 0x140 */ + __u8 route_entry[64]; + /* 0x200 - 0x240 */ + __u8 htmsi_vector[64]; + /* 0x300 */ + __u64 intisr_chip0; + /* 0x320 */ + __u64 intisr_chip1; + /* edge detection */ + __u64 last_intirr; + /* 0x380 interrupt request register */ + __u64 intirr; + /* 0x3a0 interrupt service register */ + __u64 intisr; + /* 0x3e0 interrupt level polarity selection register, + * 0 for high level tirgger + */ + __u64 int_polarity; +}; + +struct loongarch_gipi_single { + __u32 status; + __u32 en; + __u32 set; + __u32 clear; + __u64 buf[4]; +}; + +struct loongarch_gipiState { + struct loongarch_gipi_single core[KVM_MAX_CORES]; +}; + +struct kvm_loongarch_ls3a_extirq_state { + union ext_en_r { + uint64_t reg_u64[KVM_EXTIOI_IRQS_BITMAP_SIZE / 8]; + uint32_t reg_u32[KVM_EXTIOI_IRQS_BITMAP_SIZE / 4]; + uint8_t reg_u8[KVM_EXTIOI_IRQS_BITMAP_SIZE]; + } ext_en_r; + union bounce_r { + uint64_t reg_u64[KVM_EXTIOI_IRQS_BITMAP_SIZE / 8]; + uint32_t reg_u32[KVM_EXTIOI_IRQS_BITMAP_SIZE / 4]; + uint8_t reg_u8[KVM_EXTIOI_IRQS_BITMAP_SIZE]; + } bounce_r; + union ext_isr_r { + uint64_t reg_u64[KVM_EXTIOI_IRQS_BITMAP_SIZE / 8]; + uint32_t reg_u32[KVM_EXTIOI_IRQS_BITMAP_SIZE / 4]; + uint8_t reg_u8[KVM_EXTIOI_IRQS_BITMAP_SIZE]; + } ext_isr_r; + union ext_core_isr_r { + uint64_t reg_u64[KVM_MAX_CORES][KVM_EXTIOI_IRQS_BITMAP_SIZE / 8]; + uint32_t reg_u32[KVM_MAX_CORES][KVM_EXTIOI_IRQS_BITMAP_SIZE / 4]; + uint8_t reg_u8[KVM_MAX_CORES][KVM_EXTIOI_IRQS_BITMAP_SIZE]; + } ext_core_isr_r; + union ip_map_r { + uint64_t reg_u64; + uint32_t reg_u32[KVM_EXTIOI_IRQS_IPMAP_SIZE / 4]; + uint8_t reg_u8[KVM_EXTIOI_IRQS_IPMAP_SIZE]; + } ip_map_r; + union core_map_r { + uint64_t reg_u64[KVM_EXTIOI_IRQS_COREMAP_SIZE / 8]; + uint32_t reg_u32[KVM_EXTIOI_IRQS_COREMAP_SIZE / 4]; + uint8_t reg_u8[KVM_EXTIOI_IRQS_COREMAP_SIZE]; + } core_map_r; + union node_type_r { + uint64_t reg_u64[KVM_EXTIOI_IRQS_NODETYPE_SIZE / 4]; + uint32_t reg_u32[KVM_EXTIOI_IRQS_NODETYPE_SIZE / 2]; + uint16_t reg_u16[KVM_EXTIOI_IRQS_NODETYPE_SIZE]; + uint8_t reg_u8[KVM_EXTIOI_IRQS_NODETYPE_SIZE * 2]; + } node_type_r; +}; + +struct loongarch_kvm_irqchip { + __u16 chip_id; + __u16 len; + __u16 vcpu_id; + __u16 reserved; + char data[0]; +}; + +#endif /* __LINUX_KVM_LOONGARCH_H */ diff --git a/arch/loongarch/kvm/Kconfig b/arch/loongarch/kvm/Kconfig new file mode 100644 index 000000000000..8cae26f5647f --- /dev/null +++ b/arch/loongarch/kvm/Kconfig @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# KVM configuration +# +source "virt/kvm/Kconfig" + +menuconfig VIRTUALIZATION + bool "Virtualization" + help + Say Y here to get to see options for using your Linux host to run + other operating systems inside virtual machines (guests). + This option alone does not add any kernel code. + + If you say N, all options in this submenu will be skipped and disabled. + +if VIRTUALIZATION + +config KVM + tristate "Kernel-based Virtual Machine (KVM) support" + depends on HAVE_KVM + select EXPORT_UASM + select PREEMPT_NOTIFIERS + select ANON_INODES + select KVM_GENERIC_DIRTYLOG_READ_PROTECT + select HAVE_KVM_VCPU_ASYNC_IOCTL + select KVM_MMIO + select MMU_NOTIFIER + select HAVE_KVM_IRQCHIP + select HAVE_KVM_IRQFD + select HAVE_KVM_IRQ_ROUTING + select HAVE_KVM_EVENTFD + select HAVE_KVM_MSI + select SRCU + select KVM_VFIO + help + Support for hosting Guest kernels. + +choice + prompt "Virtualization mode" + depends on KVM + default KVM_LOONGARCH_LVZ + +config KVM_LOONGARCH_LVZ + bool "LOONGARCH Virtualization (VZ) ASE" + help + Use the LOONGARCH Virtualization (VZ) ASE to virtualize guests. This + supports running unmodified guest kernels, but requires hardware + support. + +endchoice + +source "drivers/vhost/Kconfig" + +endif # VIRTUALIZATION diff --git a/arch/loongarch/kvm/Makefile b/arch/loongarch/kvm/Makefile new file mode 100644 index 000000000000..29dc154609d7 --- /dev/null +++ b/arch/loongarch/kvm/Makefile @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0 +# Makefile for KVM support for LoongArch +# + +OBJECT_FILES_NON_STANDARD_entry.o := y + +common-objs-y = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o \ + irqchip.o eventfd.o) + +KVM := ../../../virt/kvm +common-objs-$(CONFIG_KVM_VFIO) += $(KVM)/vfio.o + +EXTRA_CFLAGS += -Ivirt/kvm -Iarch/loongarch/kvm + +kvm-objs := $(common-objs-y) loongarch.o emulate.o interrupt.o +kvm-objs += hypcall.o +kvm-objs += mmu.o +kvm-objs += kvm_compat.o + +kvm-objs += exit.o intc/ls7a_irq.o intc/ls3a_ipi.o intc/irqchip-debug.o\ + timer.o intc/ls3a_ext_irq.o irqfd.o csr.o +obj-$(CONFIG_KVM) += kvm.o +obj-y += entry.o fpu.o diff --git a/arch/loongarch/kvm/csr.c b/arch/loongarch/kvm/csr.c new file mode 100644 index 000000000000..763b00719469 --- /dev/null +++ b/arch/loongarch/kvm/csr.c @@ -0,0 +1,682 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include "kvmcpu.h" +#include "intc/ls3a_ipi.h" +#include "intc/ls3a_ext_irq.h" +#include "ls_irq.h" +#include "kvm_compat.h" +#include "kvmcsr.h" + +#define CASE_READ_SW_GCSR(csr, regid, csrid) \ + do { \ + if (regid == csrid) { \ + return kvm_read_sw_gcsr(csr, csrid); \ + } \ + } while (0) + +unsigned long _kvm_emu_read_csr(struct kvm_vcpu *vcpu, int csrid) +{ + struct loongarch_csrs *csr = vcpu->arch.csr; + unsigned long val = 0; + + CASE_READ_SW_GCSR(csr, csrid, KVM_CSR_ERRCTL); + CASE_READ_SW_GCSR(csr, csrid, KVM_CSR_ERRINFO1); + CASE_READ_SW_GCSR(csr, csrid, KVM_CSR_ERRINFO2); + CASE_READ_SW_GCSR(csr, csrid, KVM_CSR_MERRENTRY); + CASE_READ_SW_GCSR(csr, csrid, KVM_CSR_MERRERA); + CASE_READ_SW_GCSR(csr, csrid, KVM_CSR_ERRSAVE); + /* read sw csr when not config pmu to guest */ + CASE_READ_SW_GCSR(csr, csrid, KVM_CSR_PERFCTRL0); + CASE_READ_SW_GCSR(csr, csrid, KVM_CSR_PERFCTRL1); + CASE_READ_SW_GCSR(csr, csrid, KVM_CSR_PERFCTRL2); + CASE_READ_SW_GCSR(csr, csrid, KVM_CSR_PERFCTRL3); + CASE_READ_SW_GCSR(csr, csrid, KVM_CSR_PERFCNTR0); + CASE_READ_SW_GCSR(csr, csrid, KVM_CSR_PERFCNTR1); + CASE_READ_SW_GCSR(csr, csrid, KVM_CSR_PERFCNTR2); + CASE_READ_SW_GCSR(csr, csrid, KVM_CSR_PERFCNTR3); + + val = 0; + if (csrid < 4096) + val = kvm_read_sw_gcsr(csr, csrid); + else + pr_warn_once("Unsupport csrread 0x%x with pc %lx\n", + csrid, vcpu->arch.pc); + return val; +} + +#define CASE_WRITE_SW_GCSR(csr, regid, csrid, val) \ + do { \ + if (regid == csrid) { \ + kvm_write_sw_gcsr(csr, csrid, val); \ + return ; \ + } \ + } while (0) + +void _kvm_emu_write_csr(struct kvm_vcpu *vcpu, int csrid, + unsigned long val) +{ + struct loongarch_csrs *csr = vcpu->arch.csr; + + CASE_WRITE_SW_GCSR(csr, csrid, KVM_CSR_ERRCTL, val); + CASE_WRITE_SW_GCSR(csr, csrid, KVM_CSR_ERRINFO1, val); + CASE_WRITE_SW_GCSR(csr, csrid, KVM_CSR_ERRINFO2, val); + CASE_WRITE_SW_GCSR(csr, csrid, KVM_CSR_MERRENTRY, val); + CASE_WRITE_SW_GCSR(csr, csrid, KVM_CSR_MERRERA, val); + CASE_WRITE_SW_GCSR(csr, csrid, KVM_CSR_ERRSAVE, val); + + /* give pmu register to guest when config perfctrl */ + CASE_WRITE_HW_PMU(vcpu, csr, csrid, KVM_CSR_PERFCTRL0, val); + CASE_WRITE_HW_PMU(vcpu, csr, csrid, KVM_CSR_PERFCTRL1, val); + CASE_WRITE_HW_PMU(vcpu, csr, csrid, KVM_CSR_PERFCTRL2, val); + CASE_WRITE_HW_PMU(vcpu, csr, csrid, KVM_CSR_PERFCTRL3, val); + /* write sw pmu csr if not config ctrl */ + CASE_WRITE_SW_GCSR(csr, csrid, KVM_CSR_PERFCNTR0, val); + CASE_WRITE_SW_GCSR(csr, csrid, KVM_CSR_PERFCNTR1, val); + CASE_WRITE_SW_GCSR(csr, csrid, KVM_CSR_PERFCNTR2, val); + CASE_WRITE_SW_GCSR(csr, csrid, KVM_CSR_PERFCNTR3, val); + + + if (csrid < 4096) + kvm_write_sw_gcsr(csr, csrid, val); + else + pr_warn_once("Unsupport csrwrite 0x%x with pc %lx\n", + csrid, vcpu->arch.pc); +} + +#define CASE_CHANGE_SW_GCSR(csr, regid, csrid, mask, val) \ + do { \ + if (regid == csrid) { \ + kvm_change_sw_gcsr(csr, csrid, mask, val); \ + return ; \ + } \ + } while (0) + +void _kvm_emu_xchg_csr(struct kvm_vcpu *vcpu, int csrid, + unsigned long csr_mask, unsigned long val) +{ + struct loongarch_csrs *csr = vcpu->arch.csr; + + CASE_CHANGE_SW_GCSR(csr, csrid, KVM_CSR_IMPCTL1, csr_mask, val); + CASE_CHANGE_SW_GCSR(csr, csrid, KVM_CSR_ERRCTL, csr_mask, val); + CASE_CHANGE_SW_GCSR(csr, csrid, KVM_CSR_ERRINFO1, csr_mask, val); + CASE_CHANGE_SW_GCSR(csr, csrid, KVM_CSR_ERRINFO2, csr_mask, val); + CASE_CHANGE_SW_GCSR(csr, csrid, KVM_CSR_MERRENTRY, csr_mask, val); + CASE_CHANGE_SW_GCSR(csr, csrid, KVM_CSR_MERRERA, csr_mask, val); + CASE_CHANGE_SW_GCSR(csr, csrid, KVM_CSR_ERRSAVE, csr_mask, val); + + if (csrid < 4096) { + unsigned long orig; + + orig = kvm_read_sw_gcsr(csr, csrid); + orig &= ~csr_mask; + orig |= val & csr_mask; + kvm_write_sw_gcsr(csr, csrid, orig); + } + pr_warn_once("Unsupport csrxchg 0x%x with pc %lx\n", + csrid, vcpu->arch.pc); +} + +int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *v, int force) +{ + struct loongarch_csrs *csr = vcpu->arch.csr; + + GET_HW_GCSR(id, KVM_CSR_CRMD, v); + GET_HW_GCSR(id, KVM_CSR_PRMD, v); + GET_HW_GCSR(id, KVM_CSR_EUEN, v); + GET_HW_GCSR(id, KVM_CSR_MISC, v); + GET_HW_GCSR(id, KVM_CSR_ECFG, v); + GET_HW_GCSR(id, KVM_CSR_ESTAT, v); + GET_HW_GCSR(id, KVM_CSR_ERA, v); + GET_HW_GCSR(id, KVM_CSR_BADV, v); + GET_HW_GCSR(id, KVM_CSR_BADI, v); + GET_HW_GCSR(id, KVM_CSR_EENTRY, v); + GET_HW_GCSR(id, KVM_CSR_TLBIDX, v); + GET_HW_GCSR(id, KVM_CSR_TLBEHI, v); + GET_HW_GCSR(id, KVM_CSR_TLBELO0, v); + GET_HW_GCSR(id, KVM_CSR_TLBELO1, v); + GET_HW_GCSR(id, KVM_CSR_ASID, v); + GET_HW_GCSR(id, KVM_CSR_PGDL, v); + GET_HW_GCSR(id, KVM_CSR_PGDH, v); + GET_HW_GCSR(id, KVM_CSR_PWCTL0, v); + GET_HW_GCSR(id, KVM_CSR_PWCTL1, v); + GET_HW_GCSR(id, KVM_CSR_STLBPGSIZE, v); + GET_HW_GCSR(id, KVM_CSR_RVACFG, v); + GET_HW_GCSR(id, KVM_CSR_CPUID, v); + GET_HW_GCSR(id, KVM_CSR_PRCFG1, v); + GET_HW_GCSR(id, KVM_CSR_PRCFG2, v); + GET_HW_GCSR(id, KVM_CSR_PRCFG3, v); + GET_HW_GCSR(id, KVM_CSR_KS0, v); + GET_HW_GCSR(id, KVM_CSR_KS1, v); + GET_HW_GCSR(id, KVM_CSR_KS2, v); + GET_HW_GCSR(id, KVM_CSR_KS3, v); + GET_HW_GCSR(id, KVM_CSR_KS4, v); + GET_HW_GCSR(id, KVM_CSR_KS5, v); + GET_HW_GCSR(id, KVM_CSR_KS6, v); + GET_HW_GCSR(id, KVM_CSR_KS7, v); + GET_HW_GCSR(id, KVM_CSR_TMID, v); + GET_HW_GCSR(id, KVM_CSR_TCFG, v); + GET_HW_GCSR(id, KVM_CSR_TVAL, v); + GET_HW_GCSR(id, KVM_CSR_CNTC, v); + GET_HW_GCSR(id, KVM_CSR_LLBCTL, v); + GET_HW_GCSR(id, KVM_CSR_TLBRENTRY, v); + GET_HW_GCSR(id, KVM_CSR_TLBRBADV, v); + GET_HW_GCSR(id, KVM_CSR_TLBRERA, v); + GET_HW_GCSR(id, KVM_CSR_TLBRSAVE, v); + GET_HW_GCSR(id, KVM_CSR_TLBRELO0, v); + GET_HW_GCSR(id, KVM_CSR_TLBRELO1, v); + GET_HW_GCSR(id, KVM_CSR_TLBREHI, v); + GET_HW_GCSR(id, KVM_CSR_TLBRPRMD, v); + GET_HW_GCSR(id, KVM_CSR_DMWIN0, v); + GET_HW_GCSR(id, KVM_CSR_DMWIN1, v); + GET_HW_GCSR(id, KVM_CSR_DMWIN2, v); + GET_HW_GCSR(id, KVM_CSR_DMWIN3, v); + GET_HW_GCSR(id, KVM_CSR_MWPS, v); + GET_HW_GCSR(id, KVM_CSR_FWPS, v); + + GET_SW_GCSR(csr, id, KVM_CSR_IMPCTL1, v); + GET_SW_GCSR(csr, id, KVM_CSR_IMPCTL2, v); + GET_SW_GCSR(csr, id, KVM_CSR_ERRCTL, v); + GET_SW_GCSR(csr, id, KVM_CSR_ERRINFO1, v); + GET_SW_GCSR(csr, id, KVM_CSR_ERRINFO2, v); + GET_SW_GCSR(csr, id, KVM_CSR_MERRENTRY, v); + GET_SW_GCSR(csr, id, KVM_CSR_MERRERA, v); + GET_SW_GCSR(csr, id, KVM_CSR_ERRSAVE, v); + GET_SW_GCSR(csr, id, KVM_CSR_CTAG, v); + GET_SW_GCSR(csr, id, KVM_CSR_DEBUG, v); + GET_SW_GCSR(csr, id, KVM_CSR_DERA, v); + GET_SW_GCSR(csr, id, KVM_CSR_DESAVE, v); + + GET_SW_GCSR(csr, id, KVM_CSR_TINTCLR, v); + + if (force && (id < CSR_ALL_SIZE)) { + *v = kvm_read_sw_gcsr(csr, id); + return 0; + } + + return -1; +} + +int _kvm_setcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *v, int force) +{ + struct loongarch_csrs *csr = vcpu->arch.csr; + int ret; + + SET_HW_GCSR(csr, id, KVM_CSR_CRMD, v); + SET_HW_GCSR(csr, id, KVM_CSR_PRMD, v); + SET_HW_GCSR(csr, id, KVM_CSR_EUEN, v); + SET_HW_GCSR(csr, id, KVM_CSR_MISC, v); + SET_HW_GCSR(csr, id, KVM_CSR_ECFG, v); + SET_HW_GCSR(csr, id, KVM_CSR_ERA, v); + SET_HW_GCSR(csr, id, KVM_CSR_BADV, v); + SET_HW_GCSR(csr, id, KVM_CSR_BADI, v); + SET_HW_GCSR(csr, id, KVM_CSR_EENTRY, v); + SET_HW_GCSR(csr, id, KVM_CSR_TLBIDX, v); + SET_HW_GCSR(csr, id, KVM_CSR_TLBEHI, v); + SET_HW_GCSR(csr, id, KVM_CSR_TLBELO0, v); + SET_HW_GCSR(csr, id, KVM_CSR_TLBELO1, v); + SET_HW_GCSR(csr, id, KVM_CSR_ASID, v); + SET_HW_GCSR(csr, id, KVM_CSR_PGDL, v); + SET_HW_GCSR(csr, id, KVM_CSR_PGDH, v); + SET_HW_GCSR(csr, id, KVM_CSR_PWCTL0, v); + SET_HW_GCSR(csr, id, KVM_CSR_PWCTL1, v); + SET_HW_GCSR(csr, id, KVM_CSR_STLBPGSIZE, v); + SET_HW_GCSR(csr, id, KVM_CSR_RVACFG, v); + SET_HW_GCSR(csr, id, KVM_CSR_CPUID, v); + SET_HW_GCSR(csr, id, KVM_CSR_KS0, v); + SET_HW_GCSR(csr, id, KVM_CSR_KS1, v); + SET_HW_GCSR(csr, id, KVM_CSR_KS2, v); + SET_HW_GCSR(csr, id, KVM_CSR_KS3, v); + SET_HW_GCSR(csr, id, KVM_CSR_KS4, v); + SET_HW_GCSR(csr, id, KVM_CSR_KS5, v); + SET_HW_GCSR(csr, id, KVM_CSR_KS6, v); + SET_HW_GCSR(csr, id, KVM_CSR_KS7, v); + SET_HW_GCSR(csr, id, KVM_CSR_TMID, v); + SET_HW_GCSR(csr, id, KVM_CSR_TCFG, v); + SET_HW_GCSR(csr, id, KVM_CSR_TVAL, v); + SET_HW_GCSR(csr, id, KVM_CSR_CNTC, v); + SET_HW_GCSR(csr, id, KVM_CSR_LLBCTL, v); + SET_HW_GCSR(csr, id, KVM_CSR_TLBRENTRY, v); + SET_HW_GCSR(csr, id, KVM_CSR_TLBRBADV, v); + SET_HW_GCSR(csr, id, KVM_CSR_TLBRERA, v); + SET_HW_GCSR(csr, id, KVM_CSR_TLBRSAVE, v); + SET_HW_GCSR(csr, id, KVM_CSR_TLBRELO0, v); + SET_HW_GCSR(csr, id, KVM_CSR_TLBRELO1, v); + SET_HW_GCSR(csr, id, KVM_CSR_TLBREHI, v); + SET_HW_GCSR(csr, id, KVM_CSR_TLBRPRMD, v); + SET_HW_GCSR(csr, id, KVM_CSR_DMWIN0, v); + SET_HW_GCSR(csr, id, KVM_CSR_DMWIN1, v); + SET_HW_GCSR(csr, id, KVM_CSR_DMWIN2, v); + SET_HW_GCSR(csr, id, KVM_CSR_DMWIN3, v); + SET_HW_GCSR(csr, id, KVM_CSR_MWPS, v); + SET_HW_GCSR(csr, id, KVM_CSR_FWPS, v); + + SET_SW_GCSR(csr, id, KVM_CSR_IMPCTL1, v); + SET_SW_GCSR(csr, id, KVM_CSR_IMPCTL2, v); + SET_SW_GCSR(csr, id, KVM_CSR_ERRCTL, v); + SET_SW_GCSR(csr, id, KVM_CSR_ERRINFO1, v); + SET_SW_GCSR(csr, id, KVM_CSR_ERRINFO2, v); + SET_SW_GCSR(csr, id, KVM_CSR_MERRENTRY, v); + SET_SW_GCSR(csr, id, KVM_CSR_MERRERA, v); + SET_SW_GCSR(csr, id, KVM_CSR_ERRSAVE, v); + SET_SW_GCSR(csr, id, KVM_CSR_CTAG, v); + SET_SW_GCSR(csr, id, KVM_CSR_DEBUG, v); + SET_SW_GCSR(csr, id, KVM_CSR_DERA, v); + SET_SW_GCSR(csr, id, KVM_CSR_DESAVE, v); + SET_SW_GCSR(csr, id, KVM_CSR_PRCFG1, v); + SET_SW_GCSR(csr, id, KVM_CSR_PRCFG2, v); + SET_SW_GCSR(csr, id, KVM_CSR_PRCFG3, v); + + SET_SW_GCSR(csr, id, KVM_CSR_PGD, v); + SET_SW_GCSR(csr, id, KVM_CSR_TINTCLR, v); + + ret = -1; + switch (id) { + case KVM_CSR_ESTAT: + kvm_write_gcsr_estat(*v); + /* estat IP0~IP7 inject through guestexcept */ + kvm_write_csr_gintc(((*v) >> 2) & 0xff); + ret = 0; + break; + default: + if (force && (id < CSR_ALL_SIZE)) { + kvm_set_sw_gcsr(csr, id, *v); + ret = 0; + } + break; + } + + return ret; +} + +struct kvm_iocsr { + u32 start, end; + int (*get) (struct kvm_run *run, struct kvm_vcpu *vcpu, u32 addr, u64 *res); + int (*set) (struct kvm_run *run, struct kvm_vcpu *vcpu, u32 addr, u64 val); +}; + +static struct kvm_iocsr_entry *_kvm_find_iocsr(struct kvm *kvm, u32 addr) +{ + int i = 0; + + for (i = 0; i < IOCSR_MAX; i++) { + if (addr == kvm->arch.iocsr[i].addr) + return &kvm->arch.iocsr[i]; + } + + return NULL; +} + +static int kvm_iocsr_common_get(struct kvm_run *run, struct kvm_vcpu *vcpu, + u32 addr, u64 *res) +{ + int r = EMULATE_FAIL; + struct kvm_iocsr_entry *entry; + + spin_lock(&vcpu->kvm->arch.iocsr_lock); + entry = _kvm_find_iocsr(vcpu->kvm, addr); + if (entry) { + r = EMULATE_DONE; + *res = entry->data; + } + spin_unlock(&vcpu->kvm->arch.iocsr_lock); + return r; +} + +static int kvm_iocsr_common_set(struct kvm_run *run, struct kvm_vcpu *vcpu, + u32 addr, u64 val) +{ + int r = EMULATE_FAIL; + struct kvm_iocsr_entry *entry; + + spin_lock(&vcpu->kvm->arch.iocsr_lock); + entry = _kvm_find_iocsr(vcpu->kvm, addr); + if (entry) { + r = EMULATE_DONE; + entry->data = val; + } + spin_unlock(&vcpu->kvm->arch.iocsr_lock); + return r; +} + +static int kvm_misc_set(struct kvm_run *run, struct kvm_vcpu *vcpu, u32 addr, + u64 val) +{ + if ((val & KVM_IOCSRF_MISC_FUNC_EXT_IOI_EN) && vcpu->vcpu_id == 0) + kvm_setup_ls3a_extirq(vcpu->kvm); + return kvm_iocsr_common_set(run, vcpu, addr, val); +} + +static int kvm_ipi_get(struct kvm_run *run, struct kvm_vcpu *vcpu, u32 addr, + u64 *res) +{ + int ret; + + ++vcpu->stat.rdcsr_ipi_access_exits; + run->mmio.phys_addr = KVM_IPI_REG_ADDRESS(vcpu->vcpu_id, (addr & 0xff)); + ret = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, run->mmio.phys_addr, + run->mmio.len, res); + if (ret) { + run->mmio.is_write = 0; + vcpu->mmio_needed = 1; + vcpu->mmio_is_write = 0; + return EMULATE_DO_MMIO; + } + return EMULATE_DONE; +} + +static int kvm_extioi_isr_get(struct kvm_run *run, struct kvm_vcpu *vcpu, + u32 addr, u64 *res) +{ + int ret; + + run->mmio.phys_addr = EXTIOI_PERCORE_ADDR(vcpu->vcpu_id, (addr & 0xff)); + ret = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, run->mmio.phys_addr, + run->mmio.len, res); + if (ret) { + run->mmio.is_write = 0; + vcpu->mmio_needed = 1; + vcpu->mmio_is_write = 0; + return EMULATE_FAIL; + } + + return EMULATE_DONE; +} + +static int kvm_ipi_set(struct kvm_run *run, struct kvm_vcpu *vcpu, u32 addr, + u64 val) +{ + int ret; + + run->mmio.phys_addr = KVM_IPI_REG_ADDRESS(vcpu->vcpu_id, (addr & 0xff)); + ret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, run->mmio.phys_addr, + run->mmio.len, &val); + if (ret < 0) { + run->mmio.is_write = 1; + vcpu->mmio_needed = 1; + vcpu->mmio_is_write = 1; + return EMULATE_DO_MMIO; + } + + return EMULATE_DONE; +} + +static int kvm_extioi_set(struct kvm_run *run, struct kvm_vcpu *vcpu, u32 addr, + u64 val) +{ + int ret; + + if ((addr & 0x1f00) == KVM_IOCSR_EXTIOI_ISR_BASE) { + run->mmio.phys_addr = EXTIOI_PERCORE_ADDR(vcpu->vcpu_id, (addr & 0xff)); + } else { + run->mmio.phys_addr = EXTIOI_ADDR((addr & 0x1fff)); + } + + ret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, run->mmio.phys_addr, + run->mmio.len, &val); + if (ret < 0) { + memcpy(run->mmio.data, &val, run->mmio.len); + run->mmio.is_write = 1; + vcpu->mmio_needed = 1; + vcpu->mmio_is_write = 1; + return EMULATE_DO_MMIO; + } + + return EMULATE_DONE; +} + +static int kvm_nop_set(struct kvm_run *run, struct kvm_vcpu *vcpu, u32 addr, + u64 val) +{ + return EMULATE_DONE; +} + +/* we put these iocsrs with access frequency, from high to low */ +static struct kvm_iocsr kvm_iocsrs[] = { + /* extioi iocsr */ + {KVM_IOCSR_EXTIOI_EN_BASE, KVM_IOCSR_EXTIOI_EN_BASE + 0x100, + NULL, kvm_extioi_set}, + {KVM_IOCSR_EXTIOI_NODEMAP_BASE, KVM_IOCSR_EXTIOI_NODEMAP_BASE+0x28, + NULL, kvm_extioi_set}, + {KVM_IOCSR_EXTIOI_ROUTE_BASE, KVM_IOCSR_EXTIOI_ROUTE_BASE + 0x100, + NULL, kvm_extioi_set}, + {KVM_IOCSR_EXTIOI_ISR_BASE, KVM_IOCSR_EXTIOI_ISR_BASE + 0x1c, + kvm_extioi_isr_get, kvm_extioi_set}, + + {KVM_IOCSR_IPI_STATUS, KVM_IOCSR_IPI_STATUS + 0x40, + kvm_ipi_get, kvm_ipi_set}, + {KVM_IOCSR_IPI_SEND, KVM_IOCSR_IPI_SEND + 0x1, + NULL, kvm_ipi_set}, + {KVM_IOCSR_MBUF_SEND, KVM_IOCSR_MBUF_SEND + 0x1, + NULL, kvm_ipi_set}, + + {KVM_IOCSR_FEATURES, KVM_IOCSR_FEATURES + 0x1, + kvm_iocsr_common_get, kvm_nop_set}, + {KVM_IOCSR_VENDOR, KVM_IOCSR_VENDOR + 0x1, + kvm_iocsr_common_get, kvm_nop_set}, + {KVM_IOCSR_CPUNAME, KVM_IOCSR_CPUNAME + 0x1, + kvm_iocsr_common_get, kvm_nop_set}, + {KVM_IOCSR_NODECNT, KVM_IOCSR_NODECNT + 0x1, + kvm_iocsr_common_get, kvm_nop_set}, + {KVM_IOCSR_MISC_FUNC, KVM_IOCSR_MISC_FUNC + 0x1, + kvm_iocsr_common_get, kvm_misc_set}, +}; + +static int _kvm_emu_iocsr_read(struct kvm_run *run, struct kvm_vcpu *vcpu, + u32 addr, u64 *res) +{ + enum emulation_result er = EMULATE_FAIL; + int i = 0; + struct kvm_iocsr *iocsr = NULL; + + if (!irqchip_in_kernel(vcpu->kvm)) { + run->iocsr_io.len = run->mmio.len; + run->iocsr_io.phys_addr = addr; + run->iocsr_io.is_write = 0; + return EMULATE_DO_IOCSR; + } + for (i = 0; i < sizeof(kvm_iocsrs) / sizeof(struct kvm_iocsr); i++) { + iocsr = &kvm_iocsrs[i]; + if (addr >= iocsr->start && addr < iocsr->end) { + if (iocsr->get) + er = iocsr->get(run, vcpu, addr, res); + } + } + + if (er != EMULATE_DONE) + kvm_debug("%s iocsr 0x%x not support in kvm\n", __func__, addr); + + return er; +} + +static int _kvm_emu_iocsr_write(struct kvm_run *run, struct kvm_vcpu *vcpu, + u32 addr, u64 val) +{ + enum emulation_result er = EMULATE_FAIL; + int i = 0; + struct kvm_iocsr *iocsr = NULL; + + if (!irqchip_in_kernel(vcpu->kvm)) { + run->iocsr_io.len = run->mmio.len; + memcpy(run->iocsr_io.data, &val, run->iocsr_io.len); + run->iocsr_io.phys_addr = addr; + run->iocsr_io.is_write = 1; + return EMULATE_DO_IOCSR; + } + for (i = 0; i < sizeof(kvm_iocsrs) / sizeof(struct kvm_iocsr); i++) { + iocsr = &kvm_iocsrs[i]; + if (addr >= iocsr->start && addr < iocsr->end) { + if (iocsr->set) + er = iocsr->set(run, vcpu, addr, val); + } + } + if (er != EMULATE_DONE) + kvm_debug("%s iocsr 0x%x not support in kvm\n", __func__, addr); + + return er; +} + +/* all iocsr operation should in kvm, no mmio */ +int _kvm_emu_iocsr(larch_inst inst, + struct kvm_run *run, struct kvm_vcpu *vcpu) +{ + u32 rd, rj, opcode; + u32 val; + u64 res = 0; + int ret; + + /* + * Each IOCSR with different opcode + */ + rd = inst.reg2_format.rd; + rj = inst.reg2_format.rj; + opcode = inst.reg2_format.opcode; + val = vcpu->arch.gprs[rj]; + res = vcpu->arch.gprs[rd]; + /* LoongArch is Little endian */ + switch (opcode) { + case iocsrrdb_op: + run->mmio.len = 1; + ret = _kvm_emu_iocsr_read(run, vcpu, val, &res); + vcpu->arch.gprs[rd] = (u8) res; + break; + case iocsrrdh_op: + run->mmio.len = 2; + ret = _kvm_emu_iocsr_read(run, vcpu, val, &res); + vcpu->arch.gprs[rd] = (u16) res; + break; + case iocsrrdw_op: + run->mmio.len = 4; + ret = _kvm_emu_iocsr_read(run, vcpu, val, &res); + vcpu->arch.gprs[rd] = (u32) res; + break; + case iocsrrdd_op: + run->mmio.len = 8; + ret = _kvm_emu_iocsr_read(run, vcpu, val, &res); + vcpu->arch.gprs[rd] = res; + break; + case iocsrwrb_op: + run->mmio.len = 1; + ret = _kvm_emu_iocsr_write(run, vcpu, val, (u8)res); + break; + case iocsrwrh_op: + run->mmio.len = 2; + ret = _kvm_emu_iocsr_write(run, vcpu, val, (u16)res); + break; + case iocsrwrw_op: + run->mmio.len = 4; + ret = _kvm_emu_iocsr_write(run, vcpu, val, (u32)res); + break; + case iocsrwrd_op: + run->mmio.len = 8; + ret = _kvm_emu_iocsr_write(run, vcpu, val, res); + break; + default: + ret = EMULATE_FAIL; + break; + } + + if (ret == EMULATE_DO_IOCSR) { + vcpu->arch.io_gpr = rd; + } + + return ret; +} + +int _kvm_complete_iocsr_read(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + unsigned long *gpr = &vcpu->arch.gprs[vcpu->arch.io_gpr]; + enum emulation_result er = EMULATE_DONE; + + switch (run->iocsr_io.len) { + case 8: + *gpr = *(s64 *)run->iocsr_io.data; + break; + case 4: + *gpr = *(int *)run->iocsr_io.data; + break; + case 2: + *gpr = *(short *)run->iocsr_io.data; + break; + case 1: + *gpr = *(char *) run->iocsr_io.data; + break; + default: + kvm_err("Bad IOCSR length: %d,addr is 0x%lx", + run->iocsr_io.len, vcpu->arch.badv); + er = EMULATE_FAIL; + break; + } + + return er; +} + +int _kvm_get_iocsr(struct kvm *kvm, struct kvm_iocsr_entry *__user argp) +{ + struct kvm_iocsr_entry *entry, tmp; + int r = -EFAULT; + + if (copy_from_user(&tmp, argp, sizeof(tmp))) + goto out; + + spin_lock(&kvm->arch.iocsr_lock); + entry = _kvm_find_iocsr(kvm, tmp.addr); + if (entry != NULL) + tmp.data = entry->data; + spin_unlock(&kvm->arch.iocsr_lock); + + if (entry) + r = copy_to_user(argp, &tmp, sizeof(tmp)); + +out: + return r; +} + +int _kvm_set_iocsr(struct kvm *kvm, struct kvm_iocsr_entry *__user argp) +{ + struct kvm_iocsr_entry *entry, tmp; + int r = -EFAULT; + + if (copy_from_user(&tmp, argp, sizeof(tmp))) + goto out; + + spin_lock(&kvm->arch.iocsr_lock); + entry = _kvm_find_iocsr(kvm, tmp.addr); + if (entry != NULL) { + r = 0; + entry->data = tmp.data; + } + spin_unlock(&kvm->arch.iocsr_lock); + + if (tmp.addr == KVM_IOCSR_MISC_FUNC) + kvm_enable_ls3a_extirq(kvm, tmp.data & KVM_IOCSRF_MISC_FUNC_EXT_IOI_EN); + +out: + return r; +} + +static struct kvm_iocsr_entry iocsr_array[IOCSR_MAX] = { + {KVM_IOCSR_FEATURES, .data = KVM_IOCSRF_NODECNT|KVM_IOCSRF_MSI + |KVM_IOCSRF_EXTIOI|KVM_IOCSRF_CSRIPI|KVM_IOCSRF_VM}, + {KVM_IOCSR_VENDOR, .data = 0x6e6f73676e6f6f4c}, /* Loongson */ + {KVM_IOCSR_CPUNAME, .data = 0x303030354133}, /* 3A5000 */ + {KVM_IOCSR_NODECNT, .data = 0x4}, + {KVM_IOCSR_MISC_FUNC, .data = 0x0}, +}; + +int _kvm_init_iocsr(struct kvm *kvm) +{ + int i = 0; + + spin_lock_init(&kvm->arch.iocsr_lock); + for (i = 0; i < IOCSR_MAX; i++) { + kvm->arch.iocsr[i].addr = iocsr_array[i].addr; + kvm->arch.iocsr[i].data = iocsr_array[i].data; + } + return 0; +} diff --git a/arch/loongarch/kvm/emulate.c b/arch/loongarch/kvm/emulate.c new file mode 100644 index 000000000000..01bafcb3abb3 --- /dev/null +++ b/arch/loongarch/kvm/emulate.c @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kvmcpu.h" +#include "trace.h" + +int _kvm_emu_idle(struct kvm_vcpu *vcpu) +{ + ++vcpu->stat.idle_exits; + trace_kvm_exit(vcpu, KVM_TRACE_EXIT_IDLE); + if (!vcpu->arch.irq_pending) { + kvm_save_timer(vcpu); + kvm_vcpu_block(vcpu); + + /* + * We we are runnable, then definitely go off to user space to + * check if any I/O interrupts are pending. + */ + if (kvm_check_request(KVM_REQ_UNHALT, vcpu)) { + kvm_clear_request(KVM_REQ_UNHALT, vcpu); + vcpu->run->exit_reason = KVM_EXIT_IRQ_WINDOW_OPEN; + } + } + + return EMULATE_DONE; +} + +int _kvm_emu_mmio_write(struct kvm_vcpu *vcpu, larch_inst inst) +{ + struct kvm_run *run = vcpu->run; + unsigned int rd, op8, opcode; + unsigned long rd_val = 0; + void *data = run->mmio.data; + unsigned long curr_pc; + int ret = 0; + + /* + * Update PC and hold onto current PC in case there is + * an error and we want to rollback the PC + */ + curr_pc = vcpu->arch.pc; + update_pc(&vcpu->arch); + + op8 = (inst.word >> 24) & 0xff; + run->mmio.phys_addr = vcpu->arch.badv; + if (run->mmio.phys_addr == KVM_INVALID_ADDR) + goto out_fail; + + if (op8 < 0x28) { + /* stptrw/d process */ + rd = inst.reg2i14_format.rd; + opcode = inst.reg2i14_format.opcode; + + switch (opcode) { + case stptrd_op: + run->mmio.len = 8; + *(unsigned long *)data = vcpu->arch.gprs[rd]; + break; + case stptrw_op: + run->mmio.len = 4; + *(unsigned int *)data = vcpu->arch.gprs[rd]; + break; + default: + break; + } + } else if (op8 < 0x30) { + /* st.b/h/w/d process */ + rd = inst.reg2i12_format.rd; + opcode = inst.reg2i12_format.opcode; + rd_val = vcpu->arch.gprs[rd]; + + switch (opcode) { + case std_op: + run->mmio.len = 8; + *(unsigned long *)data = rd_val; + break; + case stw_op: + run->mmio.len = 4; + *(unsigned int *)data = rd_val; + break; + case sth_op: + run->mmio.len = 2; + *(unsigned short *)data = rd_val; + break; + case stb_op: + run->mmio.len = 1; + *(unsigned char *)data = rd_val; + break; + default: + kvm_err("Store not yet supporded (inst=0x%08x)\n", + inst.word); + kvm_arch_vcpu_dump_regs(vcpu); + goto out_fail; + } + } else if (op8 == 0x38) { + /* stxb/h/w/d process */ + rd = inst.reg3_format.rd; + opcode = inst.reg3_format.opcode; + + switch (opcode) { + case stxb_op: + run->mmio.len = 1; + *(unsigned char *)data = vcpu->arch.gprs[rd]; + break; + case stxh_op: + run->mmio.len = 2; + *(unsigned short *)data = vcpu->arch.gprs[rd]; + break; + case stxw_op: + run->mmio.len = 4; + *(unsigned int *)data = vcpu->arch.gprs[rd]; + break; + case stxd_op: + run->mmio.len = 8; + *(unsigned long *)data = vcpu->arch.gprs[rd]; + break; + default: + kvm_err("Store not yet supporded (inst=0x%08x)\n", + inst.word); + kvm_arch_vcpu_dump_regs(vcpu); + goto out_fail; + } + } else { + kvm_err("Store not yet supporded (inst=0x%08x)\n", + inst.word); + kvm_arch_vcpu_dump_regs(vcpu); + goto out_fail; + } + + /* All MMIO emulate in kernel go through the common interface */ + ret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, run->mmio.phys_addr, + run->mmio.len, data); + if (!ret) { + vcpu->mmio_needed = 0; + return EMULATE_DONE; + } + + run->mmio.is_write = 1; + vcpu->mmio_needed = 1; + vcpu->mmio_is_write = 1; + + return EMULATE_DO_MMIO; + +out_fail: + /* Rollback PC if emulation was unsuccessful */ + vcpu->arch.pc = curr_pc; + return EMULATE_FAIL; +} + + +int _kvm_emu_mmio_read(struct kvm_vcpu *vcpu, larch_inst inst) +{ + unsigned int op8, opcode, rd; + int ret = 0; + struct kvm_run *run = vcpu->run; + + run->mmio.phys_addr = vcpu->arch.badv; + if (run->mmio.phys_addr == KVM_INVALID_ADDR) + return EMULATE_FAIL; + + vcpu->mmio_needed = 2; /* signed */ + op8 = (inst.word >> 24) & 0xff; + + if (op8 < 0x28) { + /* ldptr.w/d process */ + rd = inst.reg2i14_format.rd; + opcode = inst.reg2i14_format.opcode; + + switch (opcode) { + case ldptrd_op: + run->mmio.len = 8; + break; + case ldptrw_op: + run->mmio.len = 4; + break; + default: + break; + } + } else if (op8 < 0x2f) { + /* ld.b/h/w/d, ld.bu/hu/wu process */ + rd = inst.reg2i12_format.rd; + opcode = inst.reg2i12_format.opcode; + + switch (opcode) { + case ldd_op: + run->mmio.len = 8; + break; + case ldwu_op: + vcpu->mmio_needed = 1; /* unsigned */ + run->mmio.len = 4; + break; + case ldw_op: + run->mmio.len = 4; + break; + case ldhu_op: + vcpu->mmio_needed = 1; /* unsigned */ + run->mmio.len = 2; + break; + case ldh_op: + run->mmio.len = 2; + break; + case ldbu_op: + vcpu->mmio_needed = 1; /* unsigned */ + run->mmio.len = 1; + break; + case ldb_op: + run->mmio.len = 1; + break; + default: + kvm_err("Load not yet supporded (inst=0x%08x)\n", + inst.word); + kvm_arch_vcpu_dump_regs(vcpu); + vcpu->mmio_needed = 0; + return EMULATE_FAIL; + } + } else if (op8 == 0x38) { + /* ldxb/h/w/d, ldxb/h/wu, ldgtb/h/w/d, ldleb/h/w/d process */ + rd = inst.reg3_format.rd; + opcode = inst.reg3_format.opcode; + + switch (opcode) { + case ldxb_op: + run->mmio.len = 1; + break; + case ldxbu_op: + run->mmio.len = 1; + vcpu->mmio_needed = 1; /* unsigned */ + break; + case ldxh_op: + run->mmio.len = 2; + break; + case ldxhu_op: + run->mmio.len = 2; + vcpu->mmio_needed = 1; /* unsigned */ + break; + case ldxw_op: + run->mmio.len = 4; + break; + case ldxwu_op: + run->mmio.len = 4; + vcpu->mmio_needed = 1; /* unsigned */ + break; + case ldxd_op: + run->mmio.len = 8; + break; + default: + kvm_err("Load not yet supporded (inst=0x%08x)\n", + inst.word); + kvm_arch_vcpu_dump_regs(vcpu); + vcpu->mmio_needed = 0; + return EMULATE_FAIL; + } + } else { + kvm_err("Load not yet supporded (inst=0x%08x) @ %lx\n", + inst.word, vcpu->arch.pc); + vcpu->mmio_needed = 0; + return EMULATE_FAIL; + } + + /* Set for _kvm_complete_mmio_read use */ + vcpu->arch.io_gpr = rd; + ret = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, run->mmio.phys_addr, + run->mmio.len, run->mmio.data); + run->mmio.is_write = 0; + vcpu->mmio_is_write = 0; + + if (!ret) { + _kvm_complete_mmio_read(vcpu, run); + vcpu->mmio_needed = 0; + return EMULATE_DONE; + } + return EMULATE_DO_MMIO; +} + +int _kvm_complete_mmio_read(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + unsigned long *gpr = &vcpu->arch.gprs[vcpu->arch.io_gpr]; + enum emulation_result er = EMULATE_DONE; + + /* update with new PC */ + update_pc(&vcpu->arch); + switch (run->mmio.len) { + case 8: + *gpr = *(s64 *)run->mmio.data; + break; + + case 4: + if (vcpu->mmio_needed == 2) { + *gpr = *(int *)run->mmio.data; + } else + *gpr = *(unsigned int *)run->mmio.data; + break; + + case 2: + if (vcpu->mmio_needed == 2) + *gpr = *(short *) run->mmio.data; + else + *gpr = *(unsigned short *)run->mmio.data; + + break; + case 1: + if (vcpu->mmio_needed == 2) + *gpr = *(char *) run->mmio.data; + else + *gpr = *(unsigned char *) run->mmio.data; + break; + default: + kvm_err("Bad MMIO length: %d,addr is 0x%lx", + run->mmio.len, vcpu->arch.badv); + er = EMULATE_FAIL; + break; + } + + return er; +} diff --git a/arch/loongarch/kvm/entry.S b/arch/loongarch/kvm/entry.S new file mode 100644 index 000000000000..5a51a1b026e9 --- /dev/null +++ b/arch/loongarch/kvm/entry.S @@ -0,0 +1,328 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include +#include +#include "kvm_compat.h" + +#define RESUME_HOST (1 << 1) + +#define GGPR_OFFSET(x) (KVM_ARCH_GGPR + 8*x) +#define PT_GPR_OFFSET(x) (PT_R0 + 8*x) + + .text + +.macro kvm_save_guest_gprs base + .irp n,1,2,3,4,5,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31 + KVM_LONG_S $r\n, \base, GGPR_OFFSET(\n) + .endr +.endm + +.macro kvm_restore_guest_gprs base + .irp n,1,2,3,4,5,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31 + KVM_LONG_L $r\n, \base, GGPR_OFFSET(\n) + .endr +.endm + +.macro kvm_save_host_gpr base + .irp n,1,2,3,22,23,24,25,26,27,28,29,30,31 + KVM_LONG_S $r\n, \base, PT_GPR_OFFSET(\n) + .endr +.endm + +.macro kvm_restore_host_gpr base + .irp n,1,2,3,22,23,24,25,26,27,28,29,30,31 + KVM_LONG_L $r\n, \base, PT_GPR_OFFSET(\n) + .endr +.endm + +/* + * prepare switch to guest + * @param: + * KVM_ARCH: kvm_vcpu_arch, don't touch it until 'ertn' + * GPRNUM: KVM_ARCH gpr number + * tmp, tmp1: temp register + */ +.macro kvm_switch_to_guest KVM_ARCH GPRNUM tmp tmp1 + /* set host excfg.VS=0, all exceptions share one exception entry */ + csrrd \tmp, KVM_CSR_ECFG + bstrins.w \tmp, zero, (KVM_ECFG_VS_SHIFT + KVM_ECFG_VS_WIDTH - 1), KVM_ECFG_VS_SHIFT + csrwr \tmp, KVM_CSR_ECFG + + /* Load up the new EENTRY */ + KVM_LONG_L \tmp, \KVM_ARCH, KVM_ARCH_GEENTRY + csrwr \tmp, KVM_CSR_EENTRY + + /* Set Guest ERA */ + KVM_LONG_L \tmp, \KVM_ARCH, KVM_ARCH_GPC + csrwr \tmp, KVM_CSR_ERA + + /* Save host PGDL */ + csrrd \tmp, KVM_CSR_PGDL + KVM_LONG_S \tmp, \KVM_ARCH, KVM_ARCH_HPGD + + /* Switch to kvm */ + KVM_LONG_L \tmp1, \KVM_ARCH, KVM_VCPU_KVM - KVM_VCPU_ARCH + + /* Load guest PGDL */ + lu12i.w \tmp, KVM_GPGD + srli.w \tmp, \tmp, 12 + ldx.d \tmp, \tmp1, \tmp + csrwr \tmp, KVM_CSR_PGDL + + /* Mix GID and RID */ + csrrd \tmp1, KVM_CSR_GSTAT + bstrpick.w \tmp1, \tmp1, (KVM_GSTAT_GID_SHIFT + KVM_GSTAT_GID_WIDTH - 1), KVM_GSTAT_GID_SHIFT + csrrd \tmp, KVM_CSR_GTLBC + bstrins.w \tmp, \tmp1, (KVM_GTLBC_TGID_SHIFT + KVM_GTLBC_TGID_WIDTH - 1), KVM_GTLBC_TGID_SHIFT + csrwr \tmp, KVM_CSR_GTLBC + + /* + * Switch to guest: + * GSTAT.PGM = 1, ERRCTL.ISERR = 0, TLBRPRMD.ISTLBR = 0 + * ertn + */ + + /* Prepare enable Intr before enter guest */ + ori \tmp, zero, KVM_PRMD_PIE + csrxchg \tmp, \tmp, KVM_CSR_PRMD + + /* Set PVM bit to setup ertn to guest context */ + ori \tmp, zero, KVM_GSTAT_PVM + csrxchg \tmp, \tmp, KVM_CSR_GSTAT + + /* Load Guest gprs */ + kvm_restore_guest_gprs \KVM_ARCH + + /* Load KVM_ARCH register */ + KVM_LONG_L \KVM_ARCH, \KVM_ARCH, GGPR_OFFSET(\GPRNUM) + + ertn +.endm + +#ifndef EXCPTION_ENTRY +#define EXCPTION_ENTRY(name) \ + .globl name ASM_NL \ + .p2align 12; \ + name: \ + .cfi_startproc; +#endif +#ifndef EXCPTION_ENDPROC +#define EXCPTION_ENDPROC(name) \ + .cfi_endproc; \ + SYM_END(name, SYM_T_FUNC) +#endif + +/* load kvm_vcpu to a2 and store a1 for free use */ +EXCPTION_ENTRY(kvm_exception_entry) + csrwr a2, KVM_TEMP_KS + csrrd a2, KVM_VCPU_KS + KVM_LONG_ADDI a2, a2, KVM_VCPU_ARCH + + /* After save gprs, free to use any gpr */ + kvm_save_guest_gprs a2 + /* Save guest a2 */ + csrrd t0, KVM_TEMP_KS + KVM_LONG_S t0, a2, GGPR_OFFSET(REG_A2) + + b kvm_exit_entry +EXCPTION_ENDPROC(kvm_exception_entry) + +/* a2: kvm_vcpu_arch, a1 is free to use */ +SYM_FUNC_START(kvm_exit_entry) + csrrd s1, KVM_VCPU_KS + KVM_LONG_L s0, s1, KVM_VCPU_RUN + + csrrd t0, KVM_CSR_ESTAT + KVM_LONG_S t0, a2, KVM_ARCH_HESTAT + csrrd t0, KVM_CSR_ERA + KVM_LONG_S t0, a2, KVM_ARCH_GPC + csrrd t0, KVM_CSR_BADV + KVM_LONG_S t0, a2, KVM_ARCH_HBADV + csrrd t0, KVM_CSR_BADI + KVM_LONG_S t0, a2, KVM_ARCH_HBADI + + /* Restore host excfg.VS */ + csrrd t0, KVM_CSR_ECFG + KVM_LONG_L t1, a2, KVM_ARCH_HECFG + or t0, t0, t1 + csrwr t0, KVM_CSR_ECFG + + /* Restore host eentry */ + KVM_LONG_L t0, a2, KVM_ARCH_HEENTRY + csrwr t0, KVM_CSR_EENTRY + +#if defined(CONFIG_CPU_HAS_FPU) + /* Save FPU context */ + csrrd t0, KVM_CSR_EUEN + ori t1, zero, KVM_EUEN_FPEN | KVM_EUEN_LSXEN | KVM_EUEN_LASXEN + and t2, t0, t1 + beqz t2, 1f + movfcsr2gr t3, fcsr0 + INT_S t3, a2, VCPU_FCSR0 + + movcf2gr t3, $fcc0 + or t2, t3, zero + movcf2gr t3, $fcc1 + bstrins.d t2, t3, 0xf, 0x8 + movcf2gr t3, $fcc2 + bstrins.d t2, t3, 0x17, 0x10 + movcf2gr t3, $fcc3 + bstrins.d t2, t3, 0x1f, 0x18 + movcf2gr t3, $fcc4 + bstrins.d t2, t3, 0x27, 0x20 + movcf2gr t3, $fcc5 + bstrins.d t2, t3, 0x2f, 0x28 + movcf2gr t3, $fcc6 + bstrins.d t2, t3, 0x37, 0x30 + movcf2gr t3, $fcc7 + bstrins.d t2, t3, 0x3f, 0x38 + KVM_LONG_S t2, a2, VCPU_FCC + movgr2fcsr fcsr0, zero +1: +#endif + + KVM_LONG_L t0, a2, KVM_ARCH_HPGD + csrwr t0, KVM_CSR_PGDL + + /* Disable PVM bit for keeping from into guest */ + ori t0, zero, KVM_GSTAT_PVM + csrxchg zero, t0, KVM_CSR_GSTAT + + /* Clear GTLBC.TGID field */ + csrrd t0, KVM_CSR_GTLBC + bstrins.w t0, zero, KVM_GTLBC_TGID_SHIFT + KVM_GTLBC_TGID_WIDTH - 1, KVM_GTLBC_TGID_SHIFT + csrwr t0, KVM_CSR_GTLBC + + /* Enable Address Map mode */ + ori t0, zero, (1 << KVM_CRMD_DACM_SHIFT)|(1 << KVM_CRMD_DACF_SHIFT) | KVM_CRMD_PG |PLV_KERN + csrwr t0, KVM_CSR_CRMD + + KVM_LONG_L tp, a2, KVM_ARCH_HGP + KVM_LONG_L sp, a2, KVM_ARCH_HSTACK + /* restore per cpu register */ + KVM_LONG_L $r21, a2, KVM_ARCH_HPERCPU + + KVM_LONG_ADDI sp, sp, -PT_SIZE + + /* Prepare handle exception */ + or a0, s0, zero + or a1, s1, zero + KVM_LONG_L t8, a2, KVM_ARCH_HANDLE_EXIT + jirl ra,t8, 0 + + ori t0, zero, KVM_CRMD_IE + csrxchg zero, t0, KVM_CSR_CRMD + or a2, s1, zero + KVM_LONG_ADDI a2, a2, KVM_VCPU_ARCH + + andi t0, v0, RESUME_HOST + bnez t0, ret_to_host + INT_S zero, a2, KVM_ARCH_ISHYPCALL + +ret_to_guest: + /* Save per cpu register again, maybe switched to another cpu */ + KVM_LONG_S $r21, a2, KVM_ARCH_HPERCPU + + /* Save kvm_vcpu to kscratch */ + csrwr s1, KVM_VCPU_KS + kvm_switch_to_guest a2 REG_A2 t0 t1 + +ret_to_host: + KVM_LONG_L a2, a2, KVM_ARCH_HSTACK + addi.d a2, a2, -PT_SIZE + srai.w a3, v0, 2 + or v0, a3, zero + kvm_restore_host_gpr a2 + jirl zero, ra, 0 +SYM_FUNC_END(kvm_exit_entry) + +/* + * int kvm_enter_guest(struct kvm_run *run, struct kvm_vcpu *vcpu) + * + * @register_param: + * a0: kvm_run* run + * a1: kvm_vcpu* vcpu + */ +SYM_FUNC_START(kvm_enter_guest) + /* allocate space in stack bottom */ + KVM_LONG_ADDI a2, sp, -PT_SIZE + + /* save host gprs */ + kvm_save_host_gpr a2 + + /* save host crmd,prmd csr to stack */ + csrrd a3, KVM_CSR_CRMD + KVM_LONG_S a3, a2, PT_CRMD + csrrd a3, KVM_CSR_PRMD + KVM_LONG_S a3, a2, PT_PRMD + + KVM_LONG_ADDI a2, a1, KVM_VCPU_ARCH + KVM_LONG_S sp, a2, KVM_ARCH_HSTACK + KVM_LONG_S tp, a2, KVM_ARCH_HGP + /* Save per cpu register */ + KVM_LONG_S $r21, a2, KVM_ARCH_HPERCPU + + /* Save kvm_vcpu to kscratch */ + csrwr a1, KVM_VCPU_KS + + kvm_switch_to_guest a2 REG_A2 t0 t1 + +SYM_FUNC_END(kvm_enter_guest) + +SYM_FUNC_START(__kvm_save_fpu) + fpu_save_double a0 t1 + jirl zero, ra, 0 +SYM_FUNC_END(__kvm_save_fpu) + +SYM_FUNC_START(__kvm_restore_fpu) + fpu_restore_double a0 t1 + jirl zero, ra, 0 +SYM_FUNC_END(__kvm_restore_fpu) + +SYM_FUNC_START(__kvm_restore_fcsr) + fpu_restore_csr a0 t1 + fpu_restore_cc a0 t1 t2 + + jirl zero, ra, 0 +SYM_FUNC_END(__kvm_restore_fcsr) + +#ifdef CONFIG_CPU_HAS_LSX +SYM_FUNC_START(__kvm_save_lsx) + lsx_save_data a0 t1 + + jirl zero, ra, 0 +SYM_FUNC_END(__kvm_save_lsx) + +SYM_FUNC_START(__kvm_restore_lsx) + lsx_restore_data a0 t1 + + jirl zero, ra, 0 +SYM_FUNC_END(__kvm_restore_lsx) + +SYM_FUNC_START(__kvm_restore_lsx_upper) + lsx_restore_all_upper a0 t0 t1 + + jirl zero, ra, 0 +SYM_FUNC_END(__kvm_restore_lsx_upper) +#endif + +#ifdef CONFIG_CPU_HAS_LASX +SYM_FUNC_START(__kvm_save_lasx) + lasx_save_data a0 t7 + + jirl zero, ra, 0 +SYM_FUNC_END(__kvm_save_lasx) + +SYM_FUNC_START(__kvm_restore_lasx) + lasx_restore_data a0 t7 + + jirl zero, ra, 0 +SYM_FUNC_END(__kvm_restore_lasx) +#endif + diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c new file mode 100644 index 000000000000..c6abcc1e884f --- /dev/null +++ b/arch/loongarch/kvm/exit.c @@ -0,0 +1,497 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kvmcpu.h" +#include +#include + +#include "trace.h" +#include "kvm_compat.h" +#include "kvmcsr.h" +#include "intc/ls3a_ext_irq.h" + +/* + * Loongarch KVM callback handling for not implemented guest exiting + */ +static int _kvm_fault_ni(struct kvm_vcpu *vcpu) +{ + unsigned long estat, badv; + unsigned int exccode, inst; + + /* + * Fetch the instruction. + */ + badv = vcpu->arch.badv; + estat = vcpu->arch.host_estat; + exccode = (estat & KVM_ESTAT_EXC) >> KVM_ESTAT_EXC_SHIFT; + inst = vcpu->arch.badi; + kvm_err("Exccode: %d PC=%#lx inst=0x%08x BadVaddr=%#lx estat=%#llx\n", + exccode, vcpu->arch.pc, inst, badv, kvm_read_gcsr_estat()); + kvm_arch_vcpu_dump_regs(vcpu); + vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; + return RESUME_HOST; +} + +static int _kvm_handle_csr(struct kvm_vcpu *vcpu, larch_inst inst) +{ + enum emulation_result er = EMULATE_DONE; + unsigned int rd, rj, csrid; + unsigned long csr_mask; + unsigned long val = 0; + + /* + * CSR value mask imm + * rj = 0 means csrrd + * rj = 1 means csrwr + * rj != 0,1 means csrxchg + */ + rd = inst.reg2csr_format.rd; + rj = inst.reg2csr_format.rj; + csrid = inst.reg2csr_format.csr; + + /* Process CSR ops */ + if (rj == 0) { + /* process csrrd */ + val = _kvm_emu_read_csr(vcpu, csrid); + if (er != EMULATE_FAIL) + vcpu->arch.gprs[rd] = val; + } else if (rj == 1) { + /* process csrwr */ + val = vcpu->arch.gprs[rd]; + _kvm_emu_write_csr(vcpu, csrid, val); + } else { + /* process csrxchg */ + val = vcpu->arch.gprs[rd]; + csr_mask = vcpu->arch.gprs[rj]; + _kvm_emu_xchg_csr(vcpu, csrid, csr_mask, val); + } + + return er; +} + +static int _kvm_emu_cache(struct kvm_vcpu *vcpu, larch_inst inst) +{ + return EMULATE_DONE; +} + +static int _kvm_trap_handle_gspr(struct kvm_vcpu *vcpu) +{ + enum emulation_result er = EMULATE_DONE; + struct kvm_run *run = vcpu->run; + larch_inst inst; + unsigned long curr_pc; + int rd, rj; + unsigned int index; + + /* + * Fetch the instruction. + */ + inst.word = vcpu->arch.badi; + curr_pc = vcpu->arch.pc; + update_pc(&vcpu->arch); + + er = EMULATE_FAIL; + switch (((inst.word >> 24) & 0xff)) { + case 0x0: + /* cpucfg GSPR */ + if (inst.reg2_format.opcode == 0x1B) { + rd = inst.reg2_format.rd; + rj = inst.reg2_format.rj; + ++vcpu->stat.cpucfg_exits; + index = vcpu->arch.gprs[rj]; + vcpu->arch.gprs[rd] = vcpu->kvm->arch.cpucfgs.cpucfg[index]; + if (vcpu->arch.gprs[rd] == 0) { + /* + * Fallback to get host cpucfg info, this is just for + * compatible with older qemu. + */ + vcpu->arch.gprs[rd] = read_cpucfg(index); + /* Nested KVM is not supported */ + if (index == 2) + vcpu->arch.gprs[rd] &= ~CPUCFG2_LVZP; + } + er = EMULATE_DONE; + } + break; + case 0x4: + /* csr GSPR */ + er = _kvm_handle_csr(vcpu, inst); + break; + case 0x6: + /* iocsr,cache,idle GSPR */ + switch (((inst.word >> 22) & 0x3ff)) { + case 0x18: + /* cache GSPR */ + er = _kvm_emu_cache(vcpu, inst); + trace_kvm_exit(vcpu, KVM_TRACE_EXIT_CACHE); + break; + case 0x19: + /* iocsr/idle GSPR */ + switch (((inst.word >> 15) & 0x1ffff)) { + case 0xc90: + /* iocsr GSPR */ + er = _kvm_emu_iocsr(inst, run, vcpu); + break; + case 0xc91: + /* idle GSPR */ + er = _kvm_emu_idle(vcpu); + break; + default: + er = EMULATE_FAIL; + break; + } + break; + default: + er = EMULATE_FAIL; + break; + } + break; + default: + er = EMULATE_FAIL; + break; + } + + /* Rollback PC only if emulation was unsuccessful */ + if (er == EMULATE_FAIL) { + kvm_err("[%#lx]%s: unsupported gspr instruction 0x%08x\n", + curr_pc, __func__, inst.word); + + kvm_arch_vcpu_dump_regs(vcpu); + vcpu->arch.pc = curr_pc; + } + return er; +} + +static int _kvm_check_hypcall(struct kvm_vcpu *vcpu) +{ + enum emulation_result ret; + larch_inst inst; + unsigned long curr_pc; + unsigned int code; + + /* + * Update PC and hold onto current PC in case there is + * an error and we want to rollback the PC + */ + inst.word = vcpu->arch.badi; + code = inst.reg0i15_format.simmediate; + curr_pc = vcpu->arch.pc; + update_pc(&vcpu->arch); + + ret = EMULATE_DONE; + switch (code) { + case KVM_HC_CODE_SERIVCE: + ret = EMULATE_PV_HYPERCALL; + break; + case KVM_HC_CODE_SWDBG: + /* + * Only SWDBG(SoftWare DeBug) could stop vm + * code other than 0 is ignored. + */ + ret = EMULATE_DEBUG; + break; + default: + kvm_info("[%#lx] HYPCALL %#03x unsupported\n", vcpu->arch.pc, code); + break; + } + + if (ret == EMULATE_DEBUG) + vcpu->arch.pc = curr_pc; + + return ret; +} + +/* Execute cpucfg instruction will tirggerGSPR, + * Also the access to unimplemented csrs 0x15 + * 0x16, 0x50~0x53, 0x80, 0x81, 0x90~0x95, 0x98 + * 0xc0~0xff, 0x100~0x109, 0x500~0x502, + * cache_op, idle_op iocsr ops the same */ +static int _kvm_handle_gspr(struct kvm_vcpu *vcpu) +{ + enum emulation_result er = EMULATE_DONE; + int ret = RESUME_GUEST; + + vcpu->arch.is_hypcall = 0; + + er = _kvm_trap_handle_gspr(vcpu); + + if (er == EMULATE_DONE) { + ret = RESUME_GUEST; + } else if (er == EMULATE_DO_MMIO) { + vcpu->run->exit_reason = KVM_EXIT_MMIO; + ret = RESUME_HOST; + } else if (er == EMULATE_DO_IOCSR) { + vcpu->run->exit_reason = KVM_EXIT_LOONGARCH_IOCSR; + ret = RESUME_HOST; + } else { + kvm_err("%s internal error\n", __func__); + vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; + ret = RESUME_HOST; + } + return ret; +} + +static int _kvm_handle_hypcall(struct kvm_vcpu *vcpu) +{ + enum emulation_result er = EMULATE_DONE; + int ret = RESUME_GUEST; + + vcpu->arch.is_hypcall = 0; + er = _kvm_check_hypcall(vcpu); + + if (er == EMULATE_PV_HYPERCALL) + ret = _kvm_handle_pv_hcall(vcpu); + else if (er == EMULATE_DEBUG) { + vcpu->run->exit_reason = KVM_EXIT_DEBUG; + ret = RESUME_HOST; + } else + ret = RESUME_GUEST; + + return ret; +} + +static int _kvm_handle_gcm(struct kvm_vcpu *vcpu) +{ + int ret, subcode; + + vcpu->arch.is_hypcall = 0; + ret = RESUME_GUEST; + subcode = (vcpu->arch.host_estat & KVM_ESTAT_ESUBCODE) >> KVM_ESTAT_ESUBCODE_SHIFT; + if ((subcode != EXCSUBCODE_GCSC) && (subcode != EXCSUBCODE_GCHC)) { + kvm_err("%s internal error\n", __func__); + vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; + ret = RESUME_HOST; + } + + return ret; +} + +/** + * _kvm_handle_fpu_disabled() - Guest used fpu however it is disabled at host + * @vcpu: Virtual CPU context. + * + * Handle when the guest attempts to use fpu which hasn't been allowed + * by the root context. + */ +static int _kvm_handle_fpu_disabled(struct kvm_vcpu *vcpu) +{ + struct kvm_run *run = vcpu->run; + + /* + * If guest FPU not present, the FPU operation should have been + * treated as a reserved instruction! + * If FPU already in use, we shouldn't get this at all. + */ + if (WARN_ON(!_kvm_guest_has_fpu(&vcpu->arch) || + vcpu->arch.aux_inuse & KVM_LARCH_FPU)) { + kvm_err("%s internal error\n", __func__); + run->exit_reason = KVM_EXIT_INTERNAL_ERROR; + return RESUME_HOST; + } + + kvm_own_fpu(vcpu); + return RESUME_GUEST; +} + +/** + * _kvm_handle_lsx_disabled() - Guest used LSX while disabled in root. + * @vcpu: Virtual CPU context. + * + * Handle when the guest attempts to use LSX when it is disabled in the root + * context. + */ +static int _kvm_handle_lsx_disabled(struct kvm_vcpu *vcpu) +{ + struct kvm_run *run = vcpu->run; + + /* + * If LSX not present or not exposed to guest, the LSX operation + * should have been treated as a reserved instruction! + * If LSX already in use, we shouldn't get this at all. + */ + if (!_kvm_guest_has_lsx(&vcpu->arch) || + !(kvm_read_gcsr_euen() & KVM_EUEN_LSXEN) || + vcpu->arch.aux_inuse & KVM_LARCH_LSX) { + kvm_err("%s internal error, lsx %d guest euen %llx aux %x", + __func__, _kvm_guest_has_lsx(&vcpu->arch), + kvm_read_gcsr_euen(), vcpu->arch.aux_inuse); + run->exit_reason = KVM_EXIT_INTERNAL_ERROR; + return RESUME_HOST; + } + + kvm_own_lsx(vcpu); + return RESUME_GUEST; +} + +bool _kvm_guest_has_lasx(struct kvm_vcpu *vcpu) +{ + return cpu_has_lasx && vcpu->arch.lsx_enabled && vcpu->kvm->arch.cpucfg_lasx; +} + +/** + * _kvm_handle_lasx_disabled() - Guest used LASX while disabled in root. + * @vcpu: Virtual CPU context. + * + * Handle when the guest attempts to use LASX when it is disabled in the root + * context. + */ +static int _kvm_handle_lasx_disabled(struct kvm_vcpu *vcpu) +{ + struct kvm_run *run = vcpu->run; + + /* + * If LASX not present or not exposed to guest, the LASX operation + * should have been treated as a reserved instruction! + * If LASX already in use, we shouldn't get this at all. + */ + if (!_kvm_guest_has_lasx(vcpu) || + !(kvm_read_gcsr_euen() & KVM_EUEN_LSXEN) || + !(kvm_read_gcsr_euen() & KVM_EUEN_LASXEN) || + vcpu->arch.aux_inuse & KVM_LARCH_LASX) { + kvm_err("%s internal error, lasx %d guest euen %llx aux %x", + __func__, _kvm_guest_has_lasx(vcpu), + kvm_read_gcsr_euen(), vcpu->arch.aux_inuse); + run->exit_reason = KVM_EXIT_INTERNAL_ERROR; + return RESUME_HOST; + } + + kvm_own_lasx(vcpu); + + return RESUME_GUEST; +} + + +static int _kvm_handle_read_fault(struct kvm_vcpu *vcpu) +{ + struct kvm_run *run = vcpu->run; + ulong badv = vcpu->arch.badv; + larch_inst inst; + enum emulation_result er = EMULATE_DONE; + int ret = RESUME_GUEST; + + if (kvm_handle_mm_fault(vcpu, badv, false)) { + /* A code fetch fault doesn't count as an MMIO */ + if (kvm_is_ifetch_fault(&vcpu->arch)) { + kvm_err("%s ifetch error addr:%lx\n", __func__, badv); + run->exit_reason = KVM_EXIT_INTERNAL_ERROR; + return RESUME_HOST; + } + + /* Treat as MMIO */ + inst.word = vcpu->arch.badi; + er = _kvm_emu_mmio_read(vcpu, inst); + if (er == EMULATE_FAIL) { + kvm_err("Guest Emulate Load failed: PC: %#lx, BadVaddr: %#lx\n", + vcpu->arch.pc, badv); + run->exit_reason = KVM_EXIT_INTERNAL_ERROR; + } + } + + if (er == EMULATE_DONE) { + ret = RESUME_GUEST; + } else if (er == EMULATE_DO_MMIO) { + run->exit_reason = KVM_EXIT_MMIO; + ret = RESUME_HOST; + } else { + run->exit_reason = KVM_EXIT_INTERNAL_ERROR; + ret = RESUME_HOST; + } + return ret; +} + +static int _kvm_handle_write_fault(struct kvm_vcpu *vcpu) +{ + struct kvm_run *run = vcpu->run; + ulong badv = vcpu->arch.badv; + larch_inst inst; + enum emulation_result er = EMULATE_DONE; + int ret = RESUME_GUEST; + + if (kvm_handle_mm_fault(vcpu, badv, true)) { + + /* Treat as MMIO */ + inst.word = vcpu->arch.badi; + er = _kvm_emu_mmio_write(vcpu, inst); + if (er == EMULATE_FAIL) { + kvm_err("Guest Emulate Store failed: PC: %#lx, BadVaddr: %#lx\n", + vcpu->arch.pc, badv); + run->exit_reason = KVM_EXIT_INTERNAL_ERROR; + } + } + + if (er == EMULATE_DONE) { + ret = RESUME_GUEST; + } else if (er == EMULATE_DO_MMIO) { + run->exit_reason = KVM_EXIT_MMIO; + ret = RESUME_HOST; + } else { + run->exit_reason = KVM_EXIT_INTERNAL_ERROR; + ret = RESUME_HOST; + } + return ret; +} + +static int _kvm_handle_debug(struct kvm_vcpu *vcpu) +{ + uint32_t fwps, mwps; + + fwps = kvm_csr_readq(KVM_CSR_FWPS); + mwps = kvm_csr_readq(KVM_CSR_MWPS); + if (fwps & 0xff) + kvm_csr_writeq(fwps, KVM_CSR_FWPS); + if (mwps & 0xff) + kvm_csr_writeq(mwps, KVM_CSR_MWPS); + vcpu->run->debug.arch.exception = EXCCODE_WATCH; + vcpu->run->debug.arch.fwps = fwps; + vcpu->run->debug.arch.mwps = mwps; + vcpu->run->exit_reason = KVM_EXIT_DEBUG; + return RESUME_HOST; +} + +static exit_handle_fn _kvm_fault_tables[EXCCODE_INT_START] = { + [EXCCODE_TLBL] = _kvm_handle_read_fault, + [EXCCODE_TLBS] = _kvm_handle_write_fault, + [EXCCODE_TLBI] = _kvm_handle_read_fault, + [EXCCODE_TLBM] = _kvm_handle_write_fault, + [EXCCODE_TLBRI] = _kvm_handle_read_fault, + [EXCCODE_TLBXI] = _kvm_handle_read_fault, + [EXCCODE_FPDIS] = _kvm_handle_fpu_disabled, + [EXCCODE_LSXDIS] = _kvm_handle_lsx_disabled, + [EXCCODE_LASXDIS] = _kvm_handle_lasx_disabled, + [EXCCODE_WATCH] = _kvm_handle_debug, + [EXCCODE_GSPR] = _kvm_handle_gspr, + [EXCCODE_HYP] = _kvm_handle_hypcall, + [EXCCODE_GCM] = _kvm_handle_gcm, +}; + +int _kvm_handle_fault(struct kvm_vcpu *vcpu, int fault) +{ + return _kvm_fault_tables[fault](vcpu); +} + +void _kvm_init_fault(void) +{ + int i; + + for (i = 0; i < EXCCODE_INT_START; i++) + if (!_kvm_fault_tables[i]) + _kvm_fault_tables[i] = _kvm_fault_ni; +} diff --git a/arch/loongarch/kvm/fpu.c b/arch/loongarch/kvm/fpu.c new file mode 100644 index 000000000000..180e874f04fe --- /dev/null +++ b/arch/loongarch/kvm/fpu.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include + +/* FPU/LSX context management */ +void __kvm_save_fpu(struct loongarch_fpu *fpu); +void __kvm_restore_fpu(struct loongarch_fpu *fpu); +void __kvm_restore_fcsr(struct loongarch_fpu *fpu); + +void kvm_save_fpu(struct kvm_vcpu *cpu) +{ + return __kvm_save_fpu(&cpu->arch.fpu); +} +EXPORT_SYMBOL_GPL(kvm_save_fpu); + +void kvm_restore_fpu(struct kvm_vcpu *cpu) +{ + return __kvm_restore_fpu(&cpu->arch.fpu); +} +EXPORT_SYMBOL_GPL(kvm_restore_fpu); + +void kvm_restore_fcsr(struct kvm_vcpu *cpu) +{ + return __kvm_restore_fcsr(&cpu->arch.fpu); +} +EXPORT_SYMBOL_GPL(kvm_restore_fcsr); + +#ifdef CONFIG_CPU_HAS_LSX +void __kvm_save_lsx(struct loongarch_fpu *fpu); +void __kvm_restore_lsx(struct loongarch_fpu *fpu); +void __kvm_restore_lsx_upper(struct loongarch_fpu *fpu); + +void kvm_save_lsx(struct kvm_vcpu *cpu) +{ + return __kvm_save_lsx(&cpu->arch.fpu); +} +EXPORT_SYMBOL_GPL(kvm_save_lsx); + +void kvm_restore_lsx(struct kvm_vcpu *cpu) +{ + return __kvm_restore_lsx(&cpu->arch.fpu); +} +EXPORT_SYMBOL_GPL(kvm_restore_lsx); + +void kvm_restore_lsx_upper(struct kvm_vcpu *cpu) +{ + return __kvm_restore_lsx_upper(&cpu->arch.fpu); +} +EXPORT_SYMBOL_GPL(kvm_restore_lsx_upper); + +#endif + +#ifdef CONFIG_CPU_HAS_LSX +void __kvm_save_lasx(struct loongarch_fpu *fpu); +void __kvm_restore_lasx(struct loongarch_fpu *fpu); +void __kvm_restore_lasx_upper(struct loongarch_fpu *fpu); + +void kvm_save_lasx(struct kvm_vcpu *cpu) +{ + return __kvm_save_lasx(&cpu->arch.fpu); +} +EXPORT_SYMBOL_GPL(kvm_save_lasx); + +void kvm_restore_lasx(struct kvm_vcpu *cpu) +{ + return __kvm_restore_lasx(&cpu->arch.fpu); +} +EXPORT_SYMBOL_GPL(kvm_restore_lasx); + +void kvm_restore_lasx_upper(struct kvm_vcpu *cpu) +{ + return _restore_lasx_upper(&cpu->arch.fpu); +} +EXPORT_SYMBOL_GPL(kvm_restore_lasx_upper); +#endif + +EXPORT_SYMBOL_GPL(kvm_enter_guest); +EXPORT_SYMBOL_GPL(kvm_exception_entry); + diff --git a/arch/loongarch/kvm/hypcall.c b/arch/loongarch/kvm/hypcall.c new file mode 100644 index 000000000000..180d110a08a7 --- /dev/null +++ b/arch/loongarch/kvm/hypcall.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include +#include +#include "intc/ls3a_ipi.h" + +int kvm_virt_ipi(struct kvm_vcpu *vcpu) +{ + int ret = 0; + u64 ipi_bitmap; + unsigned int min, action, cpu; + + ipi_bitmap = vcpu->arch.gprs[REG_A1]; + min = vcpu->arch.gprs[REG_A2]; + action = vcpu->arch.gprs[REG_A3]; + + if (ipi_bitmap) { + cpu = find_first_bit((void *)&ipi_bitmap, BITS_PER_LONG); + while (cpu < BITS_PER_LONG) { + kvm_helper_send_ipi(vcpu, cpu + min, action); + cpu = find_next_bit((void *)&ipi_bitmap, BITS_PER_LONG, cpu + 1); + } + } + + return ret; +} + +int kvm_save_notify(struct kvm_vcpu *vcpu) +{ + unsigned long num, id, data; + + int ret = 0; + + num = vcpu->arch.gprs[REG_A0]; + id = vcpu->arch.gprs[REG_A1]; + data = vcpu->arch.gprs[REG_A2]; + + switch (id) { + case KVM_FEATURE_STEAL_TIME: + if (!sched_info_on()) + break; + vcpu->arch.st.guest_addr = data; + kvm_debug("cpu :%d addr:%lx\n", vcpu->vcpu_id, data); + vcpu->arch.st.last_steal = current->sched_info.run_delay; + kvm_make_request(KVM_REQ_RECORD_STEAL, vcpu); + break; + default: + break; + }; + + return ret; +}; + +static int _kvm_pv_feature(struct kvm_vcpu *vcpu) +{ + int feature = vcpu->arch.gprs[REG_A1]; + int ret = KVM_RET_NOT_SUPPORTED; + switch (feature) { + case KVM_FEATURE_STEAL_TIME: + if (sched_info_on()) + ret = KVM_RET_SUC; + break; + case KVM_FEATURE_MULTI_IPI: + ret = KVM_RET_SUC; + break; + default: + break; + } + return ret; +} + +/* + * hypcall emulation always return to guest, Caller should check retval. + */ +int _kvm_handle_pv_hcall(struct kvm_vcpu *vcpu) +{ + unsigned long func = vcpu->arch.gprs[REG_A0]; + int hyp_ret = KVM_RET_NOT_SUPPORTED; + + switch (func) { + case KVM_HC_FUNC_FEATURE: + hyp_ret = _kvm_pv_feature(vcpu); + break; + case KVM_HC_FUNC_NOTIFY: + hyp_ret = kvm_save_notify(vcpu); + break; + case KVM_HC_FUNC_IPI: + hyp_ret = kvm_virt_ipi(vcpu); + break; + default: + kvm_info("[%#lx] hvc func:%#lx unsupported\n", vcpu->arch.pc, func); + break; + }; + + vcpu->arch.gprs[REG_V0] = hyp_ret; + + return RESUME_GUEST; +} diff --git a/arch/loongarch/kvm/intc/irqchip-debug.c b/arch/loongarch/kvm/intc/irqchip-debug.c new file mode 100644 index 000000000000..488b00366b47 --- /dev/null +++ b/arch/loongarch/kvm/intc/irqchip-debug.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include "kvmcpu.h" +#include "ls3a_ext_irq.h" +#include "ls7a_irq.h" + +#ifdef CONFIG_DEBUG_FS +static int irqchip_state_show(struct seq_file *m, void *v) +{ + struct kvm *kvm = m->private; + + kvm_get_kvm(kvm); + kvm_dump_ls3a_extirq_state(m, kvm->arch.v_extirq); + kvm_dump_ls7a_ioapic_state(m, kvm->arch.v_ioapic); + kvm_put_kvm(kvm); + + return 0; +} + +static int irqchip_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, irqchip_state_show, inode->i_private); +} + +static const struct file_operations irqchip_debug_fops = { + .open = irqchip_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void irqchip_debug_init(struct kvm *kvm) +{ + debugfs_create_file("irqchip-state", 0444, kvm->debugfs_dentry, kvm, + &irqchip_debug_fops); +} +#else + +void irqchip_debug_init(struct kvm *kvm) {} +#endif +void irqchip_debug_destroy(struct kvm *kvm) +{ +} diff --git a/arch/loongarch/kvm/intc/ls3a_ext_irq.c b/arch/loongarch/kvm/intc/ls3a_ext_irq.c new file mode 100644 index 000000000000..33e8b45a3532 --- /dev/null +++ b/arch/loongarch/kvm/intc/ls3a_ext_irq.c @@ -0,0 +1,937 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#include +#include "kvmcpu.h" +#include "kvm_compat.h" +#include "ls3a_ipi.h" +#include "ls7a_irq.h" +#include "ls3a_ext_irq.h" + +#define ls3a_ext_irq_lock(s, flags) spin_lock_irqsave(&s->lock, flags) +#define ls3a_ext_irq_unlock(s, flags) spin_unlock_irqrestore(&s->lock, flags) + +extern int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, + struct kvm_loongarch_interrupt *irq); +void ext_deactive_core_isr(struct kvm *kvm, int irq_num, int vcpu_id) +{ + int ipnum; + unsigned long found1; + struct kvm_loongarch_interrupt irq; + struct ls3a_kvm_extirq *s = ls3a_ext_irqchip(kvm); + struct kvm_ls3a_extirq_state *state = &(s->ls3a_ext_irq); + + ipnum = state->ext_sw_ipmap[irq_num]; + + bitmap_clear((void *)state->ext_isr.reg_u8, irq_num, 1); + bitmap_clear((void *)state->ext_core_isr.reg_u8[vcpu_id], irq_num, 1); + + bitmap_clear((void *)state->ext_sw_ipisr[vcpu_id][ipnum + 2], irq_num, 1); + found1 = find_next_bit((void *)state->ext_sw_ipisr[vcpu_id][ipnum + 2], EXTIOI_IRQS, 0); + kvm_debug("vcpu_id %d irqnum %d found:0x%lx ipnum %d down\n", vcpu_id, irq_num, found1, ipnum); + if (found1 == EXTIOI_IRQS) { + irq.cpu = vcpu_id; + irq.irq = -(ipnum + 2); /* IP2~IP5 */ + if (likely(kvm->vcpus[vcpu_id])) + kvm_vcpu_ioctl_interrupt(kvm->vcpus[vcpu_id], &irq); + kvm->stat.trigger_ls3a_ext_irq++; + } +} + +/** + * ext_irq_update_core() + * @kvm: KVM structure pointer + * @irq_num: 0~256 ext irq num + * @level: 0~1 High and low level + * + * Route the status of the extended interrupt to the host CPU core. + * + */ +void ext_irq_update_core(struct kvm *kvm, int irq_num, int level) +{ + int nrcpus, ipnum, vcpu_id; + unsigned long found1; + struct kvm_loongarch_interrupt irq; + struct ls3a_kvm_extirq *s = ls3a_ext_irqchip(kvm); + struct kvm_ls3a_extirq_state *state = &(s->ls3a_ext_irq); + + nrcpus = atomic_read(&kvm->online_vcpus); + vcpu_id = state->ext_sw_coremap[irq_num]; + ipnum = state->ext_sw_ipmap[irq_num]; + + if (vcpu_id > (nrcpus - 1)) { + vcpu_id = 0; + } + + if (level == 1) { + if (test_bit(irq_num, (void *)state->ext_en.reg_u8) == false) { + return; + } + if (test_bit(irq_num, (void *)state->ext_isr.reg_u8) == false) { + return; + } + bitmap_set((void *)state->ext_core_isr.reg_u8[vcpu_id], irq_num, 1); + + found1 = find_next_bit((void *)state->ext_sw_ipisr[vcpu_id][ipnum + 2], EXTIOI_IRQS, 0); + bitmap_set((void *)state->ext_sw_ipisr[vcpu_id][ipnum + 2], irq_num, 1); + kvm_debug("%s:%d --- vcpu_id %d irqnum %d found1 0x%lx ipnum %d\n", + __FUNCTION__, __LINE__, vcpu_id, irq_num, found1, ipnum); + if (found1 == EXTIOI_IRQS) { + irq.cpu = vcpu_id; + irq.irq = ipnum + 2; /* IP2~IP5 */ + kvm_debug("%s:%d --- vcpu_id %d ipnum %d raise\n", + __FUNCTION__, __LINE__, vcpu_id, ipnum); + if (likely(kvm->vcpus[vcpu_id])) + kvm_vcpu_ioctl_interrupt(kvm->vcpus[vcpu_id], &irq); + kvm->stat.trigger_ls3a_ext_irq++; + } + } else { + bitmap_clear((void *)state->ext_isr.reg_u8, irq_num, 1); + bitmap_clear((void *)state->ext_core_isr.reg_u8[vcpu_id], irq_num, 1); + + bitmap_clear((void *)state->ext_sw_ipisr[vcpu_id][ipnum + 2], irq_num, 1); + found1 = find_next_bit((void *)state->ext_sw_ipisr[vcpu_id][ipnum + 2], EXTIOI_IRQS, 0); + if (found1 == EXTIOI_IRQS) { + irq.cpu = vcpu_id; + irq.irq = -(ipnum + 2); /* IP2~IP5 */ + if (likely(kvm->vcpus[vcpu_id])) + kvm_vcpu_ioctl_interrupt(kvm->vcpus[vcpu_id], &irq); + kvm->stat.trigger_ls3a_ext_irq++; + } + + } +} + +void msi_irq_handler(struct kvm *kvm, int irq, int level) +{ + unsigned long flags; + struct ls3a_kvm_extirq *s = ls3a_ext_irqchip(kvm); + struct kvm_ls3a_extirq_state *state = &(s->ls3a_ext_irq); + + if (!atomic64_read(&s->enabled)) + return; + + kvm_debug("ext_irq_handler:irq = %d,level = %d\n", irq, level); + + ls3a_ext_irq_lock(s, flags); + if (level == 1) { + if (test_bit(irq, (void *)&state->ext_isr)) + goto out; + __set_bit(irq, (void *)&state->ext_isr); + } else { + if (!test_bit(irq, (void *)&state->ext_isr)) + goto out; + __clear_bit(irq, (void *)&state->ext_isr); + } + + ext_irq_update_core(kvm, irq, level); +out: + ls3a_ext_irq_unlock(s, flags); +} + +static int ls3a_ext_intctl_readb(struct kvm_vcpu *vcpu, + struct kvm_io_device *dev, + gpa_t addr, void *val) +{ + uint64_t offset, reg_count; + struct ls3a_kvm_extirq *s = NULL; + struct kvm_ls3a_extirq_state *state = NULL; + int vcpu_id; + + s = container_of(dev, struct ls3a_kvm_extirq, device); + + state = &(s->ls3a_ext_irq); + + offset = addr & 0xfffff; + + if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) { + reg_count = (offset - EXTIOI_ENABLE_START); + *(uint8_t *)val = state->ext_en.reg_u8[reg_count]; + } else if ((offset >= EXTIOI_BOUNCE_START) && (offset < EXTIOI_BOUNCE_END)) { + reg_count = (offset - EXTIOI_BOUNCE_START); + *(uint8_t *)val = state->bounce.reg_u8[reg_count]; + } else if ((offset >= EXTIOI_ISR_START) && (offset < EXTIOI_ISR_END)) { + reg_count = (offset - EXTIOI_ISR_START); + *(uint8_t *)val = state->ext_isr.reg_u8[reg_count]; + } else if ((offset >= EXTIOI_COREISR_START) && (offset < EXTIOI_COREISR_END)) { + /* percpu(32 bytes) coreisr reg_count is 0~31 */ + vcpu_id = (offset >> 8) & 0xff; + reg_count = offset & 0xff; + *(uint8_t *)val = state->ext_core_isr.reg_u8[vcpu_id][reg_count]; + } else if ((offset >= EXTIOI_IPMAP_START) && (offset < EXTIOI_IPMAP_END)) { + reg_count = (offset - EXTIOI_IPMAP_START); + *(uint8_t *)val = state->ip_map.reg_u8[reg_count]; + } else if ((offset >= EXTIOI_COREMAP_START) && (offset < EXTIOI_COREMAP_END)) { + reg_count = (offset - EXTIOI_COREMAP_START); + *(uint8_t *)val = state->core_map.reg_u8[reg_count]; + } else if ((offset >= EXTIOI_NODETYPE_START) && (offset < EXTIOI_NODETYPE_END)) { + reg_count = (offset - EXTIOI_NODETYPE_START); + *(uint8_t *)val = state->node_type.reg_u8[reg_count]; + } + kvm_debug("%s: addr=0x%llx,val=0x%x\n", + __FUNCTION__, addr, *(uint8_t *)val); + return 0; +} + +static int ls3a_ext_intctl_readw(struct kvm_vcpu *vcpu, + struct kvm_io_device *dev, + gpa_t addr, void *val) +{ + uint64_t offset, reg_count; + struct ls3a_kvm_extirq *s = NULL; + struct kvm_ls3a_extirq_state *state = NULL; + int vcpu_id; + + s = container_of(dev, struct ls3a_kvm_extirq, device); + + state = &(s->ls3a_ext_irq); + + offset = addr & 0xfffff; + + if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) { + reg_count = (offset - EXTIOI_ENABLE_START) / 4; + *(uint32_t *)val = state->ext_en.reg_u32[reg_count]; + } else if ((offset >= EXTIOI_BOUNCE_START) && (offset < EXTIOI_BOUNCE_END)) { + reg_count = (offset - EXTIOI_BOUNCE_START) / 4; + *(uint32_t *)val = state->bounce.reg_u32[reg_count]; + } else if ((offset >= EXTIOI_ISR_START) && (offset < EXTIOI_ISR_END)) { + reg_count = (offset - EXTIOI_ISR_START) / 4; + *(uint32_t *)val = state->ext_isr.reg_u32[reg_count]; + } else if ((offset >= EXTIOI_COREISR_START) && (offset < EXTIOI_COREISR_END)) { + /* percpu(32 bytes) coreisr reg_count is 0~7*/ + vcpu_id = (offset >> 8) & 0xff; + reg_count = (offset & 0xff) / 4; + *(uint32_t *)val = state->ext_core_isr.reg_u32[vcpu_id][reg_count]; + } else if ((offset >= EXTIOI_IPMAP_START) && (offset < EXTIOI_IPMAP_END)) { + reg_count = (offset - EXTIOI_IPMAP_START) / 4; + *(uint32_t *)val = state->ip_map.reg_u32[reg_count]; + } else if ((offset >= EXTIOI_COREMAP_START) && (offset < EXTIOI_COREMAP_END)) { + reg_count = (offset - EXTIOI_COREMAP_START) / 4; + *(uint32_t *)val = state->core_map.reg_u32[reg_count]; + } else if ((offset >= EXTIOI_NODETYPE_START) && (offset < EXTIOI_NODETYPE_END)) { + reg_count = (offset - EXTIOI_NODETYPE_START) / 4; + *(uint32_t *)val = state->node_type.reg_u32[reg_count]; + } + kvm_debug("%s: addr=0x%llx,val=0x%x\n", + __FUNCTION__, addr, *(uint32_t *)val); + + return 0; +} + +static int ls3a_ext_intctl_readl(struct kvm_vcpu *vcpu, + struct kvm_io_device *dev, + gpa_t addr, void *val) +{ + uint64_t offset, reg_count; + struct ls3a_kvm_extirq *s = NULL; + struct kvm_ls3a_extirq_state *state = NULL; + int vcpu_id; + + s = container_of(dev, struct ls3a_kvm_extirq, device); + + state = &(s->ls3a_ext_irq); + + offset = addr & 0xfffff; + + if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) { + reg_count = (offset - EXTIOI_ENABLE_START) / 8; + *(uint64_t *)val = state->ext_en.reg_u64[reg_count]; + } else if ((offset >= EXTIOI_BOUNCE_START) && (offset < EXTIOI_BOUNCE_END)) { + reg_count = (offset - EXTIOI_BOUNCE_START) / 8; + *(uint64_t *)val = state->bounce.reg_u64[reg_count]; + } else if ((offset >= EXTIOI_ISR_START) && (offset < EXTIOI_ISR_END)) { + reg_count = (offset - EXTIOI_ISR_START) / 8; + *(uint64_t *)val = state->ext_isr.reg_u64[reg_count]; + } else if ((offset >= EXTIOI_COREISR_START) && (offset < EXTIOI_COREISR_END)) { + /* percpu(32 bytes) coreisr reg_count is 0~3*/ + vcpu_id = (offset >> 8) & 0xff; + reg_count = (offset & 0xff) / 8; + + *(uint64_t *)val = state->ext_core_isr.reg_u64[vcpu_id][reg_count]; + } else if ((offset >= EXTIOI_IPMAP_START) && (offset < EXTIOI_IPMAP_END)) { + *(uint64_t *)val = state->ip_map.reg_u64; + } else if ((offset >= EXTIOI_COREMAP_START) && (offset < EXTIOI_COREMAP_END)) { + reg_count = (offset - EXTIOI_COREMAP_START) / 8; + *(uint64_t *)val = state->core_map.reg_u64[reg_count]; + } else if ((offset >= EXTIOI_NODETYPE_START) && (offset < EXTIOI_NODETYPE_END)) { + reg_count = (offset - EXTIOI_NODETYPE_START) / 8; + *(uint64_t *)val = state->node_type.reg_u64[reg_count]; + } + kvm_debug("%s: addr=0x%llx,val=0x%llx\n", + __FUNCTION__, addr, *(uint64_t *)val); + return 0; +} +/** + * ls3a_ext_intctl_read() + * @kvm: KVM structure pointer + * @addr: Register address + * @size: The width of the register to be read. + * @val: The pointer to the read result. + * + * Analog extended interrupt related register read. + * + */ +static int ls3a_ext_intctl_read(struct kvm_vcpu *vcpu, + struct kvm_io_device *dev, + gpa_t addr, int size, void *val) +{ + struct ls3a_kvm_extirq *s = NULL; + unsigned long flags; + uint64_t offset; + + s = container_of(dev, struct ls3a_kvm_extirq, device); + + offset = addr & 0xfffff; + if (offset & (size - 1)) { + printk("%s:unaligned address access %llx size %d\n", + __FUNCTION__, addr, size); + return 0; + } + addr = (addr & 0xfffff) - EXTIOI_ADDR_OFF; + ls3a_ext_irq_lock(s, flags); + + switch (size) { + case 1: + ls3a_ext_intctl_readb(vcpu, dev, addr, val); + break; + case 4: + ls3a_ext_intctl_readw(vcpu, dev, addr, val); + break; + case 8: + ls3a_ext_intctl_readl(vcpu, dev, addr, val); + break; + default: + WARN_ONCE(1, "%s: Abnormal address access:addr 0x%llx, size %d\n", + __FUNCTION__, addr, size); + } + ls3a_ext_irq_unlock(s, flags); + kvm_debug("%s(%d):address access %llx size %d\n", + __FUNCTION__, __LINE__, offset, size); + + return 0; +} + +static int ls3a_ext_intctl_writeb(struct kvm_vcpu *vcpu, + struct kvm_io_device *dev, + gpa_t addr, const void *__val) +{ + uint64_t offset, reg_count; + uint8_t val_data_u8, old_data_u8; + struct ls3a_kvm_extirq *s = NULL; + struct kvm_ls3a_extirq_state *state = NULL; + struct kvm *kvm = NULL; + int mask, level, i, irqnum, ipnum; + int vcpu_id; + + unsigned long val = *(unsigned long *)__val; + + s = container_of(dev, struct ls3a_kvm_extirq, device); + + state = &(s->ls3a_ext_irq); + kvm = s->kvm; + + offset = addr & 0xfffff; + val_data_u8 = val & 0xffUL; + + kvm_debug("%s: addr=0x%llx,val=0x%lx\n", __FUNCTION__, addr, val); + + if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) { + reg_count = (offset - EXTIOI_ENABLE_START); + old_data_u8 = state->ext_en.reg_u8[reg_count]; + if (old_data_u8 != val_data_u8) { + state->ext_en.reg_u8[reg_count] = val_data_u8; + old_data_u8 = old_data_u8 ^ val_data_u8; + mask = 0x1; + for (i = 0; i < 8; i++) { + if (old_data_u8 & mask) { + level = !!(val_data_u8 & (0x1 << i)); + if (level) + ext_irq_update_core(kvm, i + reg_count * 8, level); + } + mask = mask << 1; + } + } + } else if ((offset >= EXTIOI_BOUNCE_START) && (offset < EXTIOI_BOUNCE_END)) { + reg_count = (offset - EXTIOI_BOUNCE_START); + state->bounce.reg_u8[reg_count] = val_data_u8; + } else if ((offset >= EXTIOI_ISR_START) && (offset < EXTIOI_ISR_END)) { + /*can not be writen*/ + reg_count = (offset - EXTIOI_ISR_START) & 0x1f; + old_data_u8 = state->ext_isr.reg_u8[reg_count]; + state->ext_isr.reg_u8[reg_count] = old_data_u8 & (~val_data_u8); + + mask = 0x1; + for (i = 0; i < 8; i++) { + if ((old_data_u8 & mask) && (val_data_u8 & mask)) { + ext_irq_update_core(kvm, i + reg_count * 8, 0); + } + mask = mask << 1; + } + + } else if ((offset >= EXTIOI_COREISR_START) && (offset < EXTIOI_COREISR_END)) { + int bits; + /* percpu(32 bytes) coreisr reg_count is 0~31 */ + vcpu_id = (offset >> 8) & 0xff; + reg_count = offset & 0xff; + + state->ext_core_isr.reg_u8[vcpu_id][reg_count] &= ~val_data_u8; + + bits = sizeof(val_data_u8) * 8; + i = find_first_bit((void *)&val_data_u8, bits); + while (i < bits) { + ext_deactive_core_isr(kvm, i + reg_count * bits, vcpu_id); + bitmap_clear((void *)&val_data_u8, i, 1); + i = find_first_bit((void *)&val_data_u8, bits); + } + } else if ((offset >= EXTIOI_IPMAP_START) && (offset < EXTIOI_IPMAP_END)) { + /*drop arch.core_ip_mask use state->ip_map*/ + reg_count = (offset - EXTIOI_IPMAP_START); + state->ip_map.reg_u8[reg_count] = val_data_u8; + + ipnum = 0; + + for (i = 0; i < 4; i++) { + if (val_data_u8 & (0x1 << i)) { + ipnum = i; + break; + } + } + + if (val_data_u8) { + for (i = 0; i < 32; i++) { + irqnum = reg_count * 32 + i; + state->ext_sw_ipmap[irqnum] = ipnum; + } + } else { + for (i = 0; i < 32; i++) { + irqnum = reg_count * 32 + i; + state->ext_sw_ipmap[irqnum] = 0; + } + } + } else if ((offset >= EXTIOI_COREMAP_START) && (offset < EXTIOI_COREMAP_END)) { + reg_count = (offset - EXTIOI_COREMAP_START); + state->core_map.reg_u8[reg_count] = val_data_u8; + state->ext_sw_coremap[reg_count] = val_data_u8; + } else if ((offset >= EXTIOI_NODETYPE_START) && (offset < EXTIOI_NODETYPE_END)) { + reg_count = (offset - EXTIOI_NODETYPE_START); + state->node_type.reg_u8[reg_count] = val_data_u8; + } else { + WARN_ONCE(1, "%s: Abnormal address access:addr 0x%llx\n", + __FUNCTION__, addr); + } + + return 0; +} + +static int ls3a_ext_intctl_writew(struct kvm_vcpu *vcpu, + struct kvm_io_device *dev, + gpa_t addr, const void *__val) +{ + uint64_t offset, reg_count; + uint32_t val_data_u32, old_data_u32, mask; + struct ls3a_kvm_extirq *s = NULL; + struct kvm_ls3a_extirq_state *state = NULL; + struct kvm *kvm = NULL; + uint8_t tmp_data_u8; + int i, level, vcpu_id; + unsigned long val; + + val = *(unsigned long *)__val; + + s = container_of(dev, struct ls3a_kvm_extirq, device); + + state = &(s->ls3a_ext_irq); + kvm = s->kvm; + + offset = addr & 0xfffff; + val_data_u32 = val & 0xffffffffUL; + + kvm_debug("%s: addr=0x%llx,val=0x%lx\n", __FUNCTION__, addr, val); + + if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) { + reg_count = (offset - EXTIOI_ENABLE_START) / 4; + old_data_u32 = state->ext_en.reg_u32[reg_count]; + if (old_data_u32 != val_data_u32) { + state->ext_en.reg_u32[reg_count] = val_data_u32; + old_data_u32 = old_data_u32 ^ val_data_u32; + + mask = 0x1; + for (i = 0; i < 8 * sizeof(old_data_u32); i++) { + if (old_data_u32 & mask) { + level = !!(val_data_u32 & (0x1 << i)); + if (level) + ext_irq_update_core(kvm, i + reg_count * 32, level); + } + mask = mask << 1; + } + } + } else if ((offset >= EXTIOI_BOUNCE_START) && (offset < EXTIOI_BOUNCE_END)) { + reg_count = (offset - EXTIOI_BOUNCE_START) / 4; + state->bounce.reg_u32[reg_count] = val_data_u32; + } else if ((offset >= EXTIOI_ISR_START) && (offset < EXTIOI_ISR_END)) { + /*can not be writen*/ + reg_count = (offset - EXTIOI_ISR_START) / 4; + old_data_u32 = state->ext_isr.reg_u32[reg_count]; + state->ext_isr.reg_u32[reg_count] = old_data_u32 & (~val_data_u32); + + mask = 0x1; + for (i = 0; i < 8 * sizeof(old_data_u32); i++) { + if ((old_data_u32 & mask) && (val_data_u32 & mask)) { + ext_irq_update_core(kvm, i + reg_count * 32, 0); + } + mask = mask << 1; + } + } else if ((offset >= EXTIOI_COREISR_START) && (offset < EXTIOI_COREISR_END)) { + int bits; + /* percpu(32 bytes) coreisr reg_count is 0~7*/ + vcpu_id = (offset >> 8) & 0xff; + reg_count = (offset & 0xff) / 4; + + /*ext_core_ioisr*/ + state->ext_core_isr.reg_u32[vcpu_id][reg_count] &= ~val_data_u32; + + bits = sizeof(val_data_u32) * 8; + i = find_first_bit((void *)&val_data_u32, bits); + while (i < bits) { + ext_deactive_core_isr(kvm, i + reg_count * bits, vcpu_id); + bitmap_clear((void *)&val_data_u32, i, 1); + i = find_first_bit((void *)&val_data_u32, bits); + } + } else if ((offset >= EXTIOI_IPMAP_START) && (offset < EXTIOI_IPMAP_END)) { + tmp_data_u8 = val_data_u32 & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr, &tmp_data_u8); + tmp_data_u8 = (val_data_u32 >> 8) & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr + 1, &tmp_data_u8); + tmp_data_u8 = (val_data_u32 >> 16) & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr + 2, &tmp_data_u8); + tmp_data_u8 = (val_data_u32 >> 24) & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr + 3, &tmp_data_u8); + } else if ((offset >= EXTIOI_COREMAP_START) && (offset < EXTIOI_COREMAP_END)) { + tmp_data_u8 = val_data_u32 & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr, &tmp_data_u8); + tmp_data_u8 = (val_data_u32 >> 8) & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr + 1, &tmp_data_u8); + tmp_data_u8 = (val_data_u32 >> 16) & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr + 2, &tmp_data_u8); + tmp_data_u8 = (val_data_u32 >> 24) & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr + 3, &tmp_data_u8); + kvm_debug("%s:id:%d addr=0x%llx, offset 0x%llx val 0x%x\n", + __FUNCTION__, vcpu->vcpu_id, addr, offset, val_data_u32); + + } else if ((offset >= EXTIOI_NODETYPE_START) && (offset < EXTIOI_NODETYPE_END)) { + reg_count = (offset - EXTIOI_NODETYPE_START) / 4; + state->node_type.reg_u32[reg_count] = val_data_u32; + } else { + WARN_ONCE(1, "%s:%d Abnormal address access:addr 0x%llx\n", + __FUNCTION__, __LINE__, addr); + } + + return 0; +} + +static int ls3a_ext_intctl_writel(struct kvm_vcpu *vcpu, + struct kvm_io_device *dev, + gpa_t addr, const void *__val) +{ + uint64_t offset, val_data_u64, old_data_u64, reg_count, mask, i; + struct ls3a_kvm_extirq *s = NULL; + struct kvm_ls3a_extirq_state *state = NULL; + struct kvm *kvm = NULL; + uint8_t tmp_data_u8; + int level, vcpu_id; + + unsigned long val = *(unsigned long *)__val; + + s = container_of(dev, struct ls3a_kvm_extirq, device); + + state = &(s->ls3a_ext_irq); + kvm = s->kvm; + + offset = addr & 0xfffff; + val_data_u64 = val; + + kvm_debug("%s: addr=0x%llx,val=0x%lx\n", __FUNCTION__, addr, val); + + if ((offset >= EXTIOI_ENABLE_START) && (offset < EXTIOI_ENABLE_END)) { + reg_count = (offset - EXTIOI_ENABLE_START) / 8; + old_data_u64 = state->ext_en.reg_u64[reg_count]; + if (old_data_u64 != val_data_u64) { + state->ext_en.reg_u64[reg_count] = val_data_u64; + old_data_u64 = old_data_u64 ^ val_data_u64; + + mask = 0x1; + for (i = 0; i < 8 * sizeof(old_data_u64); i++) { + if (old_data_u64 & mask) { + level = !!(val_data_u64 & (0x1 << i)); + if (level) + ext_irq_update_core(kvm, i + reg_count * 64, level); + } + mask = mask << 1; + } + } + } else if ((offset >= EXTIOI_BOUNCE_START) && (offset < EXTIOI_BOUNCE_END)) { + reg_count = (offset - EXTIOI_BOUNCE_START) / 8; + state->bounce.reg_u64[reg_count] = val_data_u64; + } else if ((offset >= EXTIOI_ISR_START) && (offset < EXTIOI_ISR_END)) { + /*can not be writen*/ + reg_count = (offset - EXTIOI_ISR_START) / 8; + old_data_u64 = state->ext_isr.reg_u64[reg_count]; + state->ext_isr.reg_u64[reg_count] = old_data_u64 & (~val_data_u64); + + mask = 0x1; + for (i = 0; i < 8 * sizeof(old_data_u64); i++) { + if ((old_data_u64 & mask) && (val_data_u64 & mask)) { + ext_irq_update_core(kvm, i + reg_count * 64, 0); + } + mask = mask << 1; + } + } else if ((offset >= EXTIOI_COREISR_START) && (offset < EXTIOI_COREISR_END)) { + int bits; + vcpu_id = (offset >> 8) & 0xff; + reg_count = (offset & 0x1f) / 8; + + /*core_ext_ioisr*/ + state->ext_core_isr.reg_u64[vcpu_id][reg_count] &= ~val_data_u64; + + bits = sizeof(val_data_u64) * 8; + i = find_first_bit((void *)&val_data_u64, bits); + while (i < bits) { + ext_deactive_core_isr(kvm, i + reg_count * bits, vcpu_id); + bitmap_clear((void *)&val_data_u64, i, 1); + i = find_first_bit((void *)&val_data_u64, bits); + } + } else if ((offset >= EXTIOI_IPMAP_START) && (offset < EXTIOI_IPMAP_END)) { + tmp_data_u8 = val_data_u64 & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr, &tmp_data_u8); + tmp_data_u8 = (val_data_u64 >> 8) & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr + 1, &tmp_data_u8); + tmp_data_u8 = (val_data_u64 >> 16) & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr + 2, &tmp_data_u8); + tmp_data_u8 = (val_data_u64 >> 24) & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr + 3, &tmp_data_u8); + + tmp_data_u8 = (val_data_u64 >> 32) & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr + 4, &tmp_data_u8); + tmp_data_u8 = (val_data_u64 >> 40) & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr + 5, &tmp_data_u8); + tmp_data_u8 = (val_data_u64 >> 48) & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr + 6, &tmp_data_u8); + tmp_data_u8 = (val_data_u64 >> 56) & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr + 7, &tmp_data_u8); + } else if ((offset >= EXTIOI_COREMAP_START) && (offset < EXTIOI_COREMAP_END)) { + tmp_data_u8 = val_data_u64 & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr, &tmp_data_u8); + tmp_data_u8 = (val_data_u64 >> 8) & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr + 1, &tmp_data_u8); + tmp_data_u8 = (val_data_u64 >> 16) & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr + 2, &tmp_data_u8); + tmp_data_u8 = (val_data_u64 >> 24) & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr + 3, &tmp_data_u8); + + tmp_data_u8 = (val_data_u64 >> 32) & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr + 4, &tmp_data_u8); + tmp_data_u8 = (val_data_u64 >> 40) & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr + 5, &tmp_data_u8); + tmp_data_u8 = (val_data_u64 >> 48) & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr + 6, &tmp_data_u8); + tmp_data_u8 = (val_data_u64 >> 56) & 0xff; + ls3a_ext_intctl_writeb(vcpu, dev, addr + 7, &tmp_data_u8); + } else if ((offset >= EXTIOI_NODETYPE_START) && (offset < EXTIOI_NODETYPE_END)) { + reg_count = (offset - EXTIOI_NODETYPE_START) / 8; + state->node_type.reg_u64[reg_count] = val_data_u64; + } else { + WARN_ONCE(1, "%s:%d Abnormal address access:addr 0x%llx\n", + __FUNCTION__, __LINE__, addr); + } + return 0; +} +/** + * ls3a_ext_intctl_write() + * @kvm: KVM structure pointer + * @addr: Register address + * @size: The width of the register to be writen. + * @val: Value to be written. + * + * Analog extended interrupt related register write. + * + */ +static int ls3a_ext_intctl_write(struct kvm_vcpu *vcpu, + struct kvm_io_device *dev, + gpa_t addr, int size, const void *__val) +{ + struct ls3a_kvm_extirq *s = NULL; + unsigned long flags; + uint64_t offset; + + s = container_of(dev, struct ls3a_kvm_extirq, device); + + offset = addr & 0xfffff; + if (offset & (size - 1)) { + printk("%s(%d):unaligned address access %llx size %d\n", + __FUNCTION__, __LINE__, addr, size); + return 0; + } + + addr = (addr & 0xfffff) - EXTIOI_ADDR_OFF; + ls3a_ext_irq_lock(s, flags); + + switch (size) { + case 1: + ls3a_ext_intctl_writeb(vcpu, dev, addr, __val); + break; + case 4: + ls3a_ext_intctl_writew(vcpu, dev, addr, __val); + break; + case 8: + ls3a_ext_intctl_writel(vcpu, dev, addr, __val); + break; + default: + WARN_ONCE(1, "%s: Abnormal address access:addr 0x%llx,size %d\n", + __FUNCTION__, addr, size); + } + + ls3a_ext_irq_unlock(s, flags); + + kvm_debug("%s(%d):address access %llx size %d\n", + __FUNCTION__, __LINE__, offset, size); + return 0; +} + +static const struct kvm_io_device_ops kvm_ls3a_ext_irq_ops = { + .read = ls3a_ext_intctl_read, + .write = ls3a_ext_intctl_write, +}; + +void kvm_destroy_ls3a_ext_irq(struct kvm *kvm) +{ + struct ls3a_kvm_extirq *s = kvm->arch.v_extirq; + + if (!s) + return; + kvm_io_bus_unregister_dev(s->kvm, KVM_MMIO_BUS, &s->device); + kfree(s); +} +/* + * kvm_create_ls3a_ext_irq() + * @kvm KVM structure pointer + * Create an extended interrupt resource instance for a virtual machine + * Returns: Extended interrupt structure pointer + */ +int kvm_create_ls3a_ext_irq(struct kvm *kvm) +{ + struct ls3a_kvm_extirq *s; + int ret; + + s = kzalloc(sizeof(struct ls3a_kvm_extirq), GFP_KERNEL); + if (!s) + return -ENOMEM; + + memset((void *)&s->ls3a_ext_irq, 0x0, sizeof(struct kvm_ls3a_extirq_state)); + + spin_lock_init(&s->lock); + atomic64_set(&s->enabled, 0); + s->kvm = kvm; + + /* + * Initialize MMIO device + */ + kvm_iodevice_init(&s->device, &kvm_ls3a_ext_irq_ops); + mutex_lock(&kvm->slots_lock); + ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, + EXTIOI_REG_BASE, EXTIOI_ADDR_SIZE, &s->device); + mutex_unlock(&kvm->slots_lock); + if (ret < 0) { + printk("%s dev_ls3a_ext_irq register error ret %d\n", __FUNCTION__, ret); + goto err_register; + } + + kvm->arch.v_extirq = s; + + return 0; + +err_register: + kfree(s); + return -EFAULT; +} + +static int kvm_set_ext_sw_ipmap(struct kvm_ls3a_extirq_state *state) +{ + uint8_t val_data_u8; + int i, j, base_irq, irqnum, ipnum; + + ipnum = 0; + for (i = 0; i < EXTIOI_IRQS_IPMAP_SIZE; i++) { + val_data_u8 = state->ip_map.reg_u8[i]; + for (j = 0; j < 4; j++) { + if (val_data_u8 & (0x1 << j)) { + ipnum = j; + break; + } + } + kvm_debug("%s:%d ipnum:%d i:%d val_data_u8:0x%x\n", __FUNCTION__, __LINE__, + ipnum, i, val_data_u8); + + if (val_data_u8) { + for (base_irq = 0; base_irq < EXTIOI_IRQS_PER_GROUP; base_irq++) { + irqnum = i * EXTIOI_IRQS_PER_GROUP + base_irq; + state->ext_sw_ipmap[irqnum] = ipnum; + } + } else { + for (base_irq = 0; base_irq < EXTIOI_IRQS_PER_GROUP; base_irq++) { + irqnum = i * EXTIOI_IRQS_PER_GROUP + base_irq; + state->ext_sw_ipmap[irqnum] = 0; + } + } + } + + return 0; +} + +static int kvm_set_ext_sw_coremap(struct kvm *kvm, struct kvm_ls3a_extirq_state *state) +{ + int reg_count; + + for (reg_count = 0; reg_count < EXTIOI_IRQS; reg_count++) { + state->ext_sw_coremap[reg_count] = state->core_map.reg_u8[reg_count]; + + kvm_debug("%s:%d -- reg_count:%d vcpu %d\n", + __FUNCTION__, __LINE__, reg_count, state->core_map.reg_u8[reg_count]); + } + + return 0; +} + +static int kvm_set_ext_sw_ipisr(struct kvm *kvm, struct kvm_ls3a_extirq_state *state) +{ + int ipnum, core, irq_num; + + for (irq_num = 0; irq_num < EXTIOI_IRQS; irq_num++) { + core = state->ext_sw_coremap[irq_num]; + ipnum = state->ext_sw_ipmap[irq_num]; + + if (test_bit(irq_num, (void *)state->ext_core_isr.reg_u8[core]) == false) { + bitmap_clear((void *)state->ext_sw_ipisr[core][ipnum + 2], irq_num, 1); + } else { + bitmap_set((void *)state->ext_sw_ipisr[core][ipnum + 2], irq_num, 1); + } + + } + return 0; +} + +int kvm_get_ls3a_extirq(struct kvm *kvm, struct kvm_loongarch_ls3a_extirq_state *state) +{ + struct ls3a_kvm_extirq *v_extirq = ls3a_ext_irqchip(kvm); + struct kvm_ls3a_extirq_state *extirq_state = &(v_extirq->ls3a_ext_irq); + unsigned long flags; + if (!v_extirq) + return -EINVAL; + + ls3a_ext_irq_lock(v_extirq, flags); + memcpy(state, extirq_state, + sizeof(struct kvm_loongarch_ls3a_extirq_state)); + ls3a_ext_irq_unlock(v_extirq, flags); + kvm->stat.get_ls3a_ext_irq++; + + return 0; +} + +int kvm_set_ls3a_extirq(struct kvm *kvm, struct kvm_loongarch_ls3a_extirq_state *state) +{ + struct ls3a_kvm_extirq *v_extirq = ls3a_ext_irqchip(kvm); + struct kvm_ls3a_extirq_state *extirq_state = &(v_extirq->ls3a_ext_irq); + unsigned long flags; + if (!v_extirq) + return -EINVAL; + + ls3a_ext_irq_lock(v_extirq, flags); + memcpy(extirq_state, state, + sizeof(struct kvm_loongarch_ls3a_extirq_state)); + kvm_set_ext_sw_ipmap(extirq_state); + kvm_set_ext_sw_coremap(kvm, extirq_state); + kvm_set_ext_sw_ipisr(kvm, extirq_state); + + ls3a_ext_irq_unlock(v_extirq, flags); + kvm->stat.set_ls3a_ext_irq++; + + return 0; +} + +int kvm_setup_ls3a_extirq(struct kvm *kvm) +{ + struct ls3a_kvm_extirq *v_extirq = ls3a_ext_irqchip(kvm); + struct kvm_ls3a_extirq_state *extirq_state = &(v_extirq->ls3a_ext_irq); + unsigned long flags; + + if (!v_extirq) + return -EINVAL; + + ls3a_ext_irq_lock(v_extirq, flags); + memset(extirq_state, 0, sizeof(struct kvm_ls3a_extirq_state)); + ls3a_ext_irq_unlock(v_extirq, flags); + + atomic64_set(&v_extirq->enabled, 1); + + return 0; +} + +int kvm_enable_ls3a_extirq(struct kvm *kvm, bool enable) +{ + struct ls3a_kvm_extirq *v_extirq = ls3a_ext_irqchip(kvm); + + if (v_extirq) + atomic64_set(&v_extirq->enabled, enable); + + return 0; +} + +void kvm_dump_ls3a_extirq_state(struct seq_file *s, + struct ls3a_kvm_extirq *irqchip) +{ + struct kvm_ls3a_extirq_state *extirq; + int i = 0, j = 0; + unsigned long flags; + + seq_puts(s, "LS3A ext irqchip state:\n"); + + if (!irqchip) + return; + + extirq = &(irqchip->ls3a_ext_irq); + ls3a_ext_irq_lock(irqchip, flags); + i = (int)atomic64_read(&irqchip->enabled); + seq_printf(s, "ext irq enabled:%d", i); + seq_puts(s, "\nenabled:(Not Enabled)"); + for (i = 0; i < EXTIOI_IRQS; i++) { + if (!test_bit(i, (void *)&extirq->ext_en)) + seq_printf(s, "%d ", i); + } + seq_puts(s, "\nbounce:(Not bounce)"); + for (i = 0; i < EXTIOI_IRQS; i++) { + if (!test_bit(i, (void *)&extirq->bounce)) + seq_printf(s, "%d ", i); + } + seq_puts(s, "\next_isr:"); + for (i = 0; i < EXTIOI_IRQS; i++) { + if (test_bit(i, (void *)&extirq->ext_isr)) + seq_printf(s, "%d ", i); + } + + seq_puts(s, "\ncore_isr:"); + for (i = 0; i < KVM_MAX_VCPUS && kvm_get_vcpu_by_id(irqchip->kvm, i); i++) { + seq_printf(s, "\n\t CPU%d:", i); + for (j = 0; j < EXTIOI_IRQS; j++) { + if (test_bit(j, (void *)&extirq->ext_core_isr.reg_u8[i])) + seq_printf(s, "%d ", j); + } + } + seq_printf(s, "\nip_map:%llx", extirq->ip_map.reg_u64); + seq_puts(s, "\ncore_map: (only display router to slave cpu)\n"); + for (i = 0; i < EXTIOI_IRQS_COREMAP_SIZE; i++) + if (extirq->core_map.reg_u8[i]) + seq_printf(s, "\tirq:%d -> cpu:%d\n", i, + extirq->core_map.reg_u8[i]); + ls3a_ext_irq_unlock(irqchip, flags); +} diff --git a/arch/loongarch/kvm/intc/ls3a_ext_irq.h b/arch/loongarch/kvm/intc/ls3a_ext_irq.h new file mode 100644 index 000000000000..62fa06239b28 --- /dev/null +++ b/arch/loongarch/kvm/intc/ls3a_ext_irq.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#ifndef __LS3A_KVM_EXT_IRQ_H +#define __LS3A_KVM_EXT_IRQ_H + +#include +#include +#include +#include +#include + +#include + +#define IOCSR_EXTIOI_ADDR KVM_IOCSR_EXTIOI_NODEMAP_BASE + +#define EXTIOI_ADDR_OFF 0x10000 +#define EXTIOI_REG_BASE (LOONGSON_VIRT_REG_BASE + EXTIOI_ADDR_OFF) +#define EXTIOI_REG_END (EXTIOI_REG_BASE + 0x20000) +#define EXTIOI_ADDR_SIZE (EXTIOI_REG_END - EXTIOI_REG_BASE) +#define EXTIOI_PERCORE_REG_OFF 0x10000 +#define EXTIOI_PERCORE_REG_END (EXTIOI_PERCORE_REG_OFF + 0x10000) + +#define EXTIOI_ADDR(off) (EXTIOI_REG_BASE + (off) - IOCSR_EXTIOI_ADDR) +#define EXTIOI_PERCORE_ADDR(id, off) \ + (EXTIOI_REG_BASE + EXTIOI_PERCORE_REG_OFF + ((id) << 8) + (off)) + +#define EXTIOI_NODETYPE_START (KVM_IOCSR_EXTIOI_NODEMAP_BASE - IOCSR_EXTIOI_ADDR) +#define EXTIOI_NODETYPE_END (EXTIOI_NODETYPE_START + 0x20) +#define EXTIOI_IPMAP_START (KVM_IOCSR_EXTIOI_IPMAP_BASE - IOCSR_EXTIOI_ADDR) +#define EXTIOI_IPMAP_END (EXTIOI_IPMAP_START + 0x8) +#define EXTIOI_ENABLE_START (KVM_IOCSR_EXTIOI_EN_BASE - IOCSR_EXTIOI_ADDR) +#define EXTIOI_ENABLE_END (EXTIOI_ENABLE_START + 0x20) +#define EXTIOI_BOUNCE_START (KVM_IOCSR_EXTIOI_BOUNCE_BASE - IOCSR_EXTIOI_ADDR) +#define EXTIOI_BOUNCE_END (EXTIOI_BOUNCE_START + 0x20) +#define EXTIOI_ISR_START (0x1700 - IOCSR_EXTIOI_ADDR) +#define EXTIOI_ISR_END (EXTIOI_ISR_START + 0x20) +#define EXTIOI_COREMAP_START (KVM_IOCSR_EXTIOI_ROUTE_BASE - IOCSR_EXTIOI_ADDR) +#define EXTIOI_COREMAP_END (EXTIOI_COREMAP_START + 0x100) +#define EXTIOI_COREISR_START (EXTIOI_PERCORE_REG_OFF) +#define EXTIOI_COREISR_END (EXTIOI_PERCORE_REG_END) + +#define LS3A_INTC_IP 8 +#define EXTIOI_IRQS KVM_EXTIOI_IRQS +#define EXTIOI_IRQS_BITMAP_SIZE (EXTIOI_IRQS / 8) +/* map to ipnum per 32 irqs */ +#define EXTIOI_IRQS_IPMAP_SIZE (EXTIOI_IRQS / 32) +#define EXTIOI_IRQS_PER_GROUP KVM_EXTIOI_IRQS_PER_GROUP +#define EXTIOI_IRQS_COREMAP_SIZE (EXTIOI_IRQS) +#define EXTIOI_IRQS_NODETYPE_SIZE KVM_EXTIOI_IRQS_NODETYPE_SIZE + +typedef struct kvm_ls3a_extirq_state { + union ext_en { + uint64_t reg_u64[EXTIOI_IRQS_BITMAP_SIZE / 8]; + uint32_t reg_u32[EXTIOI_IRQS_BITMAP_SIZE / 4]; + uint8_t reg_u8[EXTIOI_IRQS_BITMAP_SIZE]; + } ext_en; + union bounce { + uint64_t reg_u64[EXTIOI_IRQS_BITMAP_SIZE / 8]; + uint32_t reg_u32[EXTIOI_IRQS_BITMAP_SIZE / 4]; + uint8_t reg_u8[EXTIOI_IRQS_BITMAP_SIZE]; + } bounce; + union ext_isr { + uint64_t reg_u64[EXTIOI_IRQS_BITMAP_SIZE / 8]; + uint32_t reg_u32[EXTIOI_IRQS_BITMAP_SIZE / 4]; + uint8_t reg_u8[EXTIOI_IRQS_BITMAP_SIZE]; + } ext_isr; + union ext_core_isr { + uint64_t reg_u64[KVM_MAX_VCPUS][EXTIOI_IRQS_BITMAP_SIZE / 8]; + uint32_t reg_u32[KVM_MAX_VCPUS][EXTIOI_IRQS_BITMAP_SIZE / 4]; + uint8_t reg_u8[KVM_MAX_VCPUS][EXTIOI_IRQS_BITMAP_SIZE]; + } ext_core_isr; + union ip_map { + uint64_t reg_u64; + uint32_t reg_u32[EXTIOI_IRQS_IPMAP_SIZE / 4]; + uint8_t reg_u8[EXTIOI_IRQS_IPMAP_SIZE]; + } ip_map; + union core_map { + uint64_t reg_u64[EXTIOI_IRQS_COREMAP_SIZE / 8]; + uint32_t reg_u32[EXTIOI_IRQS_COREMAP_SIZE / 4]; + uint8_t reg_u8[EXTIOI_IRQS_COREMAP_SIZE]; + } core_map; + union { + uint64_t reg_u64[EXTIOI_IRQS_NODETYPE_SIZE / 4]; + uint32_t reg_u32[EXTIOI_IRQS_NODETYPE_SIZE / 2]; + uint16_t reg_u16[EXTIOI_IRQS_NODETYPE_SIZE]; + uint8_t reg_u8[EXTIOI_IRQS_NODETYPE_SIZE * 2]; + } node_type; + + /*software state */ + uint8_t ext_sw_ipmap[EXTIOI_IRQS]; + uint8_t ext_sw_coremap[EXTIOI_IRQS]; + uint8_t ext_sw_ipisr[KVM_MAX_VCPUS][LS3A_INTC_IP][EXTIOI_IRQS_BITMAP_SIZE]; +} LS3AExtirqState; + +struct ls3a_kvm_extirq { + spinlock_t lock; + struct kvm *kvm; + atomic64_t enabled; + struct kvm_io_device device; + struct kvm_ls3a_extirq_state ls3a_ext_irq; +}; + +static inline struct ls3a_kvm_extirq *ls3a_ext_irqchip(struct kvm *kvm) +{ + return kvm->arch.v_extirq; +} + +static inline int ls3a_extirq_in_kernel(struct kvm *kvm) +{ + int ret; + + ret = (ls3a_ext_irqchip(kvm) != NULL); + return ret; +} + + +void ext_irq_handler(struct kvm *kvm, int irq, int level); +int kvm_create_ls3a_ext_irq(struct kvm *kvm); +int kvm_get_ls3a_extirq(struct kvm *kvm, + struct kvm_loongarch_ls3a_extirq_state *state); +int kvm_set_ls3a_extirq(struct kvm *kvm, + struct kvm_loongarch_ls3a_extirq_state *state); +void kvm_destroy_ls3a_ext_irq(struct kvm *kvm); +void msi_irq_handler(struct kvm *kvm, int irq, int level); +int kvm_setup_ls3a_extirq(struct kvm *kvm); +int kvm_enable_ls3a_extirq(struct kvm *kvm, bool enable); +void kvm_dump_ls3a_extirq_state(struct seq_file *m, struct ls3a_kvm_extirq *irqchip); +#endif diff --git a/arch/loongarch/kvm/intc/ls3a_ipi.c b/arch/loongarch/kvm/intc/ls3a_ipi.c new file mode 100644 index 000000000000..6c05eaad9f80 --- /dev/null +++ b/arch/loongarch/kvm/intc/ls3a_ipi.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#include "kvmcpu.h" +#include "ls3a_ipi.h" +#include "ls7a_irq.h" +#include "ls3a_ext_irq.h" + +#define ls3a_gipi_lock(s, flags) spin_lock_irqsave(&s->lock, flags) +#define ls3a_gipi_unlock(s, flags) spin_unlock_irqrestore(&s->lock, flags) + +extern int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, + struct kvm_loongarch_interrupt *irq); +int kvm_helper_send_ipi(struct kvm_vcpu *vcpu, unsigned int cpu, unsigned int action) +{ + struct kvm *kvm = vcpu->kvm; + struct ls3a_kvm_ipi *ipi = ls3a_ipi_irqchip(kvm); + gipiState *s = &(ipi->ls3a_gipistate); + unsigned long flags; + struct kvm_loongarch_interrupt irq; + + kvm->stat.pip_write_exits++; + + ls3a_gipi_lock(ipi, flags); + if (s->core[cpu].status == 0) { + irq.cpu = cpu; + irq.irq = LARCH_INT_IPI; + kvm_vcpu_ioctl_interrupt(kvm->vcpus[cpu], &irq); + } + + s->core[cpu].status |= action; + ls3a_gipi_unlock(ipi, flags); + + return 0; +} + +static int ls3a_gipi_writel(struct ls3a_kvm_ipi *ipi, gpa_t addr, + int len, const void *val) +{ + uint64_t data, offset; + struct kvm_loongarch_interrupt irq; + gipiState *s = &(ipi->ls3a_gipistate); + uint32_t cpu, action_data; + struct kvm *kvm; + void *pbuf; + int mailbox, action; + + kvm = ipi->kvm; + cpu = (addr >> 8) & 0xff; + + data = *(uint64_t *)val; + offset = addr & 0xFF; + + BUG_ON(offset & (len - 1)); + + switch (offset) { + case CORE0_STATUS_OFF: + printk("CORE0_SET_OFF Can't be write\n"); + + break; + case CORE0_EN_OFF: + s->core[cpu].en = data; + + break; + case CORE0_IPI_SEND: + cpu = ((data & 0xffffffff) >> 16) & 0x3ff; + action = (data & 0x1f); + action_data = (1 << action); + + if (s->core[cpu].status == 0) { + irq.cpu = cpu; + irq.irq = LARCH_INT_IPI; + + if (likely(kvm->vcpus[cpu])) { + kvm_vcpu_ioctl_interrupt(kvm->vcpus[cpu], &irq); + } + } + s->core[cpu].status |= action_data; + break; + case CORE0_SET_OFF: + pr_info("CORE0_SET_OFF simulation is required\n"); + break; + case CORE0_CLEAR_OFF: + s->core[cpu].status &= ~data; + if (!s->core[cpu].status) { + irq.cpu = cpu; + irq.irq = -LARCH_INT_IPI; + if (likely(kvm->vcpus[cpu])) + kvm_vcpu_ioctl_interrupt(kvm->vcpus[cpu], &irq); + else + kvm_err("Failed lower ipi irq target cpu:%d\n", cpu); + } + + break; + case CORE0_MAIL_SEND: + cpu = ((data & 0xffffffff) >> 16) & 0x3ff; + mailbox = ((data & 0xffffffff) >> 2) & 0x7; + pbuf = (void *)s->core[cpu].buf + mailbox * 4; + + *(unsigned int *)pbuf = (unsigned int)(data >> 32); + break; + case 0x20 ... 0x3c: + pbuf = (void *)s->core[cpu].buf + (offset - 0x20); + if (len == 1) + *(unsigned char *)pbuf = (unsigned char)data; + else if (len == 2) + *(unsigned short *)pbuf = (unsigned short)data; + else if (len == 4) + *(unsigned int *)pbuf = (unsigned int)data; + else if (len == 8) + *(unsigned long *)pbuf = (unsigned long)data; + + break; + default: + printk("ls3a_gipi_writel with unknown addr %llx \n", addr); + break; + } + return 0; +} + +static uint64_t ls3a_gipi_readl(struct ls3a_kvm_ipi *ipi, + gpa_t addr, int len, void *val) +{ + uint64_t offset; + uint64_t ret = 0; + + gipiState *s = &(ipi->ls3a_gipistate); + uint32_t cpu; + void *pbuf; + + cpu = (addr >> 8) & 0xff; + + offset = addr & 0xFF; + + BUG_ON(offset & (len - 1)); + switch (offset) { + case CORE0_STATUS_OFF: + ret = s->core[cpu].status; + break; + case CORE0_EN_OFF: + ret = s->core[cpu].en; + break; + case CORE0_SET_OFF: + ret = 0; + break; + case CORE0_CLEAR_OFF: + ret = 0; + break; + case 0x20 ... 0x3c: + pbuf = (void *)s->core[cpu].buf + (offset - 0x20); + if (len == 1) + ret = *(unsigned char *)pbuf; + else if (len == 2) + ret = *(unsigned short *)pbuf; + else if (len == 4) + ret = *(unsigned int *)pbuf; + else if (len == 8) + ret = *(unsigned long *)pbuf; + break; + default: + printk("ls3a_gipi_readl with unknown addr %llx \n", addr); + break; + } + + *(uint64_t *)val = ret; + + return ret; +} + +static int kvm_ls3a_ipi_write(struct kvm_vcpu *vcpu, + struct kvm_io_device *dev, + gpa_t addr, int len, const void *val) +{ + struct ls3a_kvm_ipi *ipi; + ipi_io_device *ipi_device; + unsigned long flags; + + ipi_device = container_of(dev, ipi_io_device, device); + ipi = ipi_device->ipi; + ipi->kvm->stat.pip_write_exits++; + + ls3a_gipi_lock(ipi, flags); + ls3a_gipi_writel(ipi, addr, len, val); + ls3a_gipi_unlock(ipi, flags); + return 0; +} + + +static int kvm_ls3a_ipi_read(struct kvm_vcpu *vcpu, + struct kvm_io_device *dev, + gpa_t addr, int len, void *val) +{ + struct ls3a_kvm_ipi *ipi; + ipi_io_device *ipi_device; + unsigned long flags; + + ipi_device = container_of(dev, ipi_io_device, device); + ipi = ipi_device->ipi; + ipi->kvm->stat.pip_read_exits++; + + ls3a_gipi_lock(ipi, flags); + ls3a_gipi_readl(ipi, addr, len, val); + ls3a_gipi_unlock(ipi, flags); + return 0; +} + + +static const struct kvm_io_device_ops kvm_ls3a_ipi_ops = { + .read = kvm_ls3a_ipi_read, + .write = kvm_ls3a_ipi_write, +}; + +void kvm_destroy_ls3a_ipi(struct kvm *kvm) +{ + struct kvm_io_device *device; + struct ls3a_kvm_ipi *vipi = kvm->arch.v_gipi; + + if (!vipi) + return; + device = &vipi->dev_ls3a_ipi.device; + kvm_io_bus_unregister_dev(vipi->kvm, KVM_MMIO_BUS, device); + kfree(vipi); +} + +int kvm_create_ls3a_ipi(struct kvm *kvm) +{ + struct ls3a_kvm_ipi *s; + unsigned long addr; + struct kvm_io_device *device; + int ret; + + s = kzalloc(sizeof(struct ls3a_kvm_ipi), GFP_KERNEL); + if (!s) + return -ENOMEM; + spin_lock_init(&s->lock); + s->kvm = kvm; + + /* + * Initialize MMIO device + */ + device = &s->dev_ls3a_ipi.device; + kvm_iodevice_init(device, &kvm_ls3a_ipi_ops); + addr = SMP_MAILBOX; + mutex_lock(&kvm->slots_lock); + ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, + addr, KVM_IOCSR_IPI_ADDR_SIZE, device); + mutex_unlock(&kvm->slots_lock); + if (ret < 0) { + kvm_err("%s Initialize MMIO dev err ret:%d\n", __func__, ret); + goto err; + } else { + s->dev_ls3a_ipi.ipi = s; + } + + kvm->arch.v_gipi = s; + return 0; + +err: + kfree(s); + return -EFAULT; +} + +int kvm_get_ls3a_ipi(struct kvm *kvm, struct loongarch_gipiState *state) +{ + struct ls3a_kvm_ipi *ipi = ls3a_ipi_irqchip(kvm); + gipiState *ipi_state = &(ipi->ls3a_gipistate); + unsigned long flags; + + ls3a_gipi_lock(ipi, flags); + memcpy(state, ipi_state, sizeof(gipiState)); + ls3a_gipi_unlock(ipi, flags); + return 0; +} + +int kvm_set_ls3a_ipi(struct kvm *kvm, struct loongarch_gipiState *state) +{ + struct ls3a_kvm_ipi *ipi = ls3a_ipi_irqchip(kvm); + gipiState *ipi_state = &(ipi->ls3a_gipistate); + unsigned long flags; + + if (!ipi) + return -EINVAL; + + ls3a_gipi_lock(ipi, flags); + memcpy(ipi_state, state, sizeof(gipiState)); + ls3a_gipi_unlock(ipi, flags); + return 0; +} diff --git a/arch/loongarch/kvm/intc/ls3a_ipi.h b/arch/loongarch/kvm/intc/ls3a_ipi.h new file mode 100644 index 000000000000..3d40487177a7 --- /dev/null +++ b/arch/loongarch/kvm/intc/ls3a_ipi.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#ifndef __LS3A_KVM_IPI_H +#define __LS3A_KVM_IPI_H + +#include +#include +#include +#include +#include + +typedef struct gipi_single { + uint32_t status; + uint32_t en; + uint32_t set; + uint32_t clear; + uint64_t buf[4]; +} gipi_single; + +typedef struct gipiState { + gipi_single core[KVM_MAX_VCPUS]; +} gipiState; + +struct ls3a_kvm_ipi; + +typedef struct ipi_io_device { + struct ls3a_kvm_ipi *ipi; + struct kvm_io_device device; + int nodeNum; +} ipi_io_device; + +struct ls3a_kvm_ipi { + spinlock_t lock; + struct kvm *kvm; + gipiState ls3a_gipistate; + int nodeNum; + ipi_io_device dev_ls3a_ipi; +}; + +#define SMP_MAILBOX (LOONGSON_VIRT_REG_BASE + 0x0000) +#define KVM_IPI_REG_ADDRESS(id, off) (SMP_MAILBOX | (id << 8) | off) +#define KVM_IOCSR_IPI_ADDR_SIZE 0x10000 + +#define CORE0_STATUS_OFF 0x000 +#define CORE0_EN_OFF 0x004 +#define CORE0_SET_OFF 0x008 +#define CORE0_CLEAR_OFF 0x00c +#define CORE0_BUF_20 0x020 +#define CORE0_BUF_28 0x028 +#define CORE0_BUF_30 0x030 +#define CORE0_BUF_38 0x038 +#define CORE0_IPI_SEND 0x040 +#define CORE0_MAIL_SEND 0x048 + +static inline struct ls3a_kvm_ipi *ls3a_ipi_irqchip(struct kvm *kvm) +{ + return kvm->arch.v_gipi; +} + +static inline int ls3a_ipi_in_kernel(struct kvm *kvm) +{ + int ret; + + ret = (ls3a_ipi_irqchip(kvm) != NULL); + return ret; +} + +int kvm_create_ls3a_ipi(struct kvm *kvm); +void kvm_destroy_ls3a_ipi(struct kvm *kvm); +int kvm_set_ls3a_ipi(struct kvm *kvm, struct loongarch_gipiState *state); +int kvm_get_ls3a_ipi(struct kvm *kvm, struct loongarch_gipiState *state); +int kvm_helper_send_ipi(struct kvm_vcpu *vcpu, unsigned int cpu, unsigned int action); +#endif diff --git a/arch/loongarch/kvm/intc/ls7a_irq.c b/arch/loongarch/kvm/intc/ls7a_irq.c new file mode 100644 index 000000000000..473c2a82652a --- /dev/null +++ b/arch/loongarch/kvm/intc/ls7a_irq.c @@ -0,0 +1,488 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#include +#include +#include "ls3a_ipi.h" +#include "ls7a_irq.h" +#include "ls3a_ext_irq.h" + +void ls7a_ioapic_lock(struct ls7a_kvm_ioapic *s, unsigned long *flags) +{ + unsigned long tmp; + spin_lock_irqsave(&s->lock, tmp); + *flags = tmp; +} + +void ls7a_ioapic_unlock(struct ls7a_kvm_ioapic *s, unsigned long *flags) +{ + unsigned long tmp; + tmp = *flags; + spin_unlock_irqrestore(&s->lock, tmp); +} + +static void kvm_ls7a_ioapic_raise(struct kvm *kvm, unsigned long mask) +{ + unsigned long irqnum, val; + struct ls7a_kvm_ioapic *s = ls7a_ioapic_irqchip(kvm); + struct kvm_ls7a_ioapic_state *state; + struct kvm_loongarch_interrupt irq; + int i; + + state = &s->ls7a_ioapic; + irq.cpu = -1; + val = mask & state->intirr & (~state->int_mask); + val &= ~state->intisr; + for_each_set_bit(i, &val, 64) { + state->intisr |= 0x1ULL << i; + irqnum = state->htmsi_vector[i]; + kvm_debug("msi_irq_handler,%ld,up\n", irqnum); + msi_irq_handler(kvm, irqnum, 1); + } + + kvm->stat.ls7a_ioapic_update++; +} + +static void kvm_ls7a_ioapic_lower(struct kvm *kvm, unsigned long mask) +{ + unsigned long irqnum, val; + struct ls7a_kvm_ioapic *s = ls7a_ioapic_irqchip(kvm); + struct kvm_ls7a_ioapic_state *state; + struct kvm_loongarch_interrupt irq; + int i; + + state = &s->ls7a_ioapic; + irq.cpu = -1; + val = mask & state->intisr; + for_each_set_bit(i, &val, 64) { + state->intisr &= ~(0x1ULL << i); + irqnum = state->htmsi_vector[i]; + kvm_debug("msi_irq_handler,%ld,down\n", irqnum); + msi_irq_handler(kvm, irqnum, 0); + } + + kvm->stat.ls7a_ioapic_update++; +} + +int kvm_ls7a_set_msi(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, int level, bool line_status) +{ + if (!level) + return -1; + + kvm_debug("msi data is 0x%x\n", e->msi.data); + msi_irq_handler(kvm, e->msi.data, 1); + return 0; +} + +int kvm_ls7a_send_userspace_msi(struct kvm *kvm, struct kvm_msi *msi) +{ + struct kvm_kernel_irq_routing_entry route; + + if (msi->flags != 0) + return -EINVAL; + + kvm->stat.ls7a_msi_irq++; + route.msi.address_lo = msi->address_lo; + route.msi.address_hi = msi->address_hi; + route.msi.data = msi->data; + + kvm_debug("msi data is 0x%x\n", route.msi.data); + return kvm_ls7a_set_msi(&route, kvm, + KVM_USERSPACE_IRQ_SOURCE_ID, 1, false); + +} + +int kvm_ls7a_ioapic_set_irq(struct kvm *kvm, int irq, int level) +{ + struct ls7a_kvm_ioapic *s; + struct kvm_ls7a_ioapic_state *state; + uint64_t mask = 1ULL << irq; + s = ls7a_ioapic_irqchip(kvm); + state = &s->ls7a_ioapic; + BUG_ON(irq < 0 || irq >= LS7A_IOAPIC_NUM_PINS); + + if (state->intedge & mask) { + /* edge triggered */ + if (level) { + if ((state->last_intirr & mask) == 0) { + state->intirr |= mask; + kvm_ls7a_ioapic_raise(kvm, mask); + } + state->last_intirr |= mask; + } else + state->last_intirr &= ~mask; + } else { + /* level triggered */ + if (!!level) { + if ((state->intirr & mask) == 0) { + state->intirr |= mask; + kvm_ls7a_ioapic_raise(kvm, mask); + } + } else { + if (state->intirr & mask) { + state->intirr &= ~mask; + kvm_ls7a_ioapic_lower(kvm, mask); + } + } + } + kvm->stat.ls7a_ioapic_set_irq++; + return 0; +} + +static int ls7a_ioapic_reg_write(struct ls7a_kvm_ioapic *s, + gpa_t addr, int len, const void *val) +{ + struct kvm *kvm; + struct kvm_ls7a_ioapic_state *state; + int64_t offset_tmp; + uint64_t offset; + uint64_t data, old; + + offset = addr & 0xfff; + kvm = s->kvm; + state = &(s->ls7a_ioapic); + + if (offset & (len - 1)) { + printk("%s(%d):unaligned address access %llx size %d \n", + __FUNCTION__, __LINE__, addr, len); + return 0; + } + + if (8 == len) { + data = *(uint64_t *)val; + switch (offset) { + case LS7A_INT_MASK_OFFSET: + old = state->int_mask; + state->int_mask = data; + if (old & ~data) + kvm_ls7a_ioapic_raise(kvm, old & ~data); + if (~old & data) + kvm_ls7a_ioapic_lower(kvm, ~old & data); + break; + case LS7A_INT_STATUS_OFFSET: + state->intisr = data; + break; + case LS7A_INT_EDGE_OFFSET: + state->intedge = data; + break; + case LS7A_INT_CLEAR_OFFSET: + /* + * only clear edge triggered irq on writing INTCLR reg + * no effect on level triggered irq + */ + data = data & state->intedge; + state->intirr &= ~data; + kvm_ls7a_ioapic_lower(kvm, data); + state->intisr &= (~data); + break; + case LS7A_INT_POL_OFFSET: + state->int_polarity = data; + break; + case LS7A_HTMSI_EN_OFFSET: + state->htmsi_en = data; + break; + case LS7A_AUTO_CTRL0_OFFSET: + case LS7A_AUTO_CTRL1_OFFSET: + break; + default: + WARN_ONCE(1, "Abnormal address access:addr 0x%llx,len %d\n", addr, len); + break; + } + } else if (1 == len) { + data = *(unsigned char *)val; + if (offset >= LS7A_HTMSI_VEC_OFFSET) { + offset_tmp = offset - LS7A_HTMSI_VEC_OFFSET; + if (offset_tmp >= 0 && offset_tmp < 64) { + state->htmsi_vector[offset_tmp] = + (uint8_t)(data & 0xff); + } + } else if (offset >= LS7A_ROUTE_ENTRY_OFFSET) { + offset_tmp = offset - LS7A_ROUTE_ENTRY_OFFSET; + if (offset_tmp >= 0 && offset_tmp < 64) { + state->route_entry[offset_tmp] = + (uint8_t)(data & 0xff); + } + } else { + WARN_ONCE(1, "Abnormal address access:addr 0x%llx,len %d\n", addr, len); + } + } else { + WARN_ONCE(1, "Abnormal address access:addr 0x%llx,len %d\n", addr, len); + } + kvm->stat.ioapic_reg_write++; + return 0; +} + +static inline struct ls7a_kvm_ioapic *to_ioapic(struct kvm_io_device *dev) +{ + return container_of(dev, struct ls7a_kvm_ioapic, dev_ls7a_ioapic); +} + +static int kvm_ls7a_ioapic_write(struct kvm_vcpu *vcpu, + struct kvm_io_device *this, + gpa_t addr, int len, const void *val) +{ + struct ls7a_kvm_ioapic *s = to_ioapic(this); + unsigned long flags; + + ls7a_ioapic_lock(s->kvm->arch.v_ioapic, &flags); + ls7a_ioapic_reg_write(s, addr, len, val); + ls7a_ioapic_unlock(s->kvm->arch.v_ioapic, &flags); + + return 0; +} + +static int ls7a_ioapic_reg_read(struct ls7a_kvm_ioapic *s, + gpa_t addr, int len, void *val) +{ + uint64_t offset, offset_tmp; + struct kvm *kvm; + struct kvm_ls7a_ioapic_state *state; + uint64_t result = 0; + + state = &(s->ls7a_ioapic); + kvm = s->kvm; + offset = addr & 0xfff; + if (offset & (len - 1)) { + printk("%s(%d):unaligned address access %llx size %d \n", + __FUNCTION__, __LINE__, addr, len); + return 0; + } + + if (8 == len) { + switch (offset) { + case LS7A_INT_MASK_OFFSET: + result = state->int_mask; + break; + case LS7A_INT_STATUS_OFFSET: + result = state->intisr & (~state->int_mask); + break; + case LS7A_INT_EDGE_OFFSET: + result = state->intedge; + break; + case LS7A_INT_POL_OFFSET: + result = state->int_polarity; + break; + case LS7A_HTMSI_EN_OFFSET: + result = state->htmsi_en; + break; + case LS7A_AUTO_CTRL0_OFFSET: + case LS7A_AUTO_CTRL1_OFFSET: + break; + case LS7A_INT_ID_OFFSET: + result = LS7A_INT_ID_VER; + result = (result << 32) + LS7A_INT_ID_VAL; + break; + default: + WARN_ONCE(1, "Abnormal address access:addr 0x%llx,len %d\n", addr, len); + break; + } + if (val != NULL) + *(uint64_t *)val = result; + } else if (1 == len) { + if (offset >= LS7A_HTMSI_VEC_OFFSET) { + offset_tmp = offset - LS7A_HTMSI_VEC_OFFSET; + if (offset_tmp >= 0 && offset_tmp < 64) { + result = state->htmsi_vector[offset_tmp]; + } + } else if (offset >= LS7A_ROUTE_ENTRY_OFFSET) { + offset_tmp = offset - LS7A_ROUTE_ENTRY_OFFSET; + if (offset_tmp >= 0 && offset_tmp < 64) { + result = state->route_entry[offset_tmp]; + } + } else { + WARN_ONCE(1, "Abnormal address access:addr 0x%llx,len %d\n", addr, len); + } + if (val != NULL) + *(unsigned char *)val = result; + } else { + WARN_ONCE(1, "Abnormal address access:addr 0x%llx,len %d\n", addr, len); + } + kvm->stat.ioapic_reg_read++; + return result; +} + +static int kvm_ls7a_ioapic_read(struct kvm_vcpu *vcpu, + struct kvm_io_device *this, + gpa_t addr, int len, void *val) +{ + struct ls7a_kvm_ioapic *s = to_ioapic(this); + unsigned long flags; + uint64_t result = 0; + + ls7a_ioapic_lock(s->kvm->arch.v_ioapic, &flags); + result = ls7a_ioapic_reg_read(s, addr, len, val); + ls7a_ioapic_unlock(s->kvm->arch.v_ioapic, &flags); + return 0; +} + +static const struct kvm_io_device_ops kvm_ls7a_ioapic_ops = { + .read = kvm_ls7a_ioapic_read, + .write = kvm_ls7a_ioapic_write, +}; + +static int kvm_ls7a_ioapic_alias_read(struct kvm_vcpu *vcpu, + struct kvm_io_device *this, gpa_t addr, int len, void *val) +{ + struct ls7a_kvm_ioapic *s; + unsigned long flags; + + s = container_of(this, struct ls7a_kvm_ioapic, ls7a_ioapic_alias); + ls7a_ioapic_lock(s->kvm->arch.v_ioapic, &flags); + ls7a_ioapic_reg_read(s, addr, len, val); + ls7a_ioapic_unlock(s->kvm->arch.v_ioapic, &flags); + return 0; +} + +static int kvm_ls7a_ioapic_alias_write(struct kvm_vcpu *vcpu, + struct kvm_io_device *this, gpa_t addr, int len, const void *val) +{ + struct ls7a_kvm_ioapic *s; + unsigned long flags; + + s = container_of(this, struct ls7a_kvm_ioapic, ls7a_ioapic_alias); + ls7a_ioapic_lock(s->kvm->arch.v_ioapic, &flags); + ls7a_ioapic_reg_write(s, addr, len, val); + ls7a_ioapic_unlock(s->kvm->arch.v_ioapic, &flags); + + return 0; +} + +static const struct kvm_io_device_ops kvm_ls7a_ioapic_ops_alias = { + .read = kvm_ls7a_ioapic_alias_read, + .write = kvm_ls7a_ioapic_alias_write, +}; + +int kvm_create_ls7a_ioapic(struct kvm *kvm) +{ + struct ls7a_kvm_ioapic *s; + int ret; + unsigned long ls7a_ioapic_reg_base; + + s = kzalloc(sizeof(struct ls7a_kvm_ioapic), GFP_KERNEL); + if (!s) + return -ENOMEM; + spin_lock_init(&s->lock); + s->kvm = kvm; + + ls7a_ioapic_reg_base = LS7A_IOAPIC_GUEST_REG_BASE; + + /* + * Initialize MMIO device + */ + kvm_iodevice_init(&s->dev_ls7a_ioapic, &kvm_ls7a_ioapic_ops); + mutex_lock(&kvm->slots_lock); + ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, ls7a_ioapic_reg_base, + 0x1000, &s->dev_ls7a_ioapic); + if (ret < 0) { + kvm_err("Failed register ioapic, err:%d\n", ret); + goto fail_unlock; + } + + ls7a_ioapic_reg_base = LS7A_IOAPIC_GUEST_REG_BASE_ALIAS; + kvm_iodevice_init(&s->ls7a_ioapic_alias, &kvm_ls7a_ioapic_ops_alias); + ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, ls7a_ioapic_reg_base, + 0x1000, &s->ls7a_ioapic_alias); + if (ret < 0) { + kvm_err("Failed register alias ioapic, err:%d\n", ret); + kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, + &s->dev_ls7a_ioapic); + goto fail_unlock; + } + mutex_unlock(&kvm->slots_lock); + + kvm->arch.v_ioapic = s; + + return 0; + +fail_unlock: + mutex_unlock(&kvm->slots_lock); + kfree(s); + + return -EFAULT; +} + + +int kvm_get_ls7a_ioapic(struct kvm *kvm, struct ls7a_ioapic_state *state) +{ + struct ls7a_kvm_ioapic *ls7a_ioapic = ls7a_ioapic_irqchip(kvm); + struct kvm_ls7a_ioapic_state *ioapic_state = + &(ls7a_ioapic->ls7a_ioapic); + unsigned long flags; + + ls7a_ioapic_lock(ls7a_ioapic, &flags); + memcpy(state, ioapic_state, sizeof(struct kvm_ls7a_ioapic_state)); + ls7a_ioapic_unlock(ls7a_ioapic, &flags); + kvm->stat.get_ls7a_ioapic++; + return 0; +} + +int kvm_set_ls7a_ioapic(struct kvm *kvm, struct ls7a_ioapic_state *state) +{ + struct ls7a_kvm_ioapic *ls7a_ioapic = ls7a_ioapic_irqchip(kvm); + struct kvm_ls7a_ioapic_state *ioapic_state = + &(ls7a_ioapic->ls7a_ioapic); + unsigned long flags; + + if (!ls7a_ioapic) + return -EINVAL; + + ls7a_ioapic_lock(ls7a_ioapic, &flags); + memcpy(ioapic_state, state, sizeof(struct kvm_ls7a_ioapic_state)); + ls7a_ioapic_unlock(ls7a_ioapic, &flags); + kvm->stat.set_ls7a_ioapic++; + return 0; +} + +void kvm_destroy_ls7a_ioapic(struct kvm *kvm) +{ + struct ls7a_kvm_ioapic *vpic = kvm->arch.v_ioapic; + if (!vpic) + return; + kvm_io_bus_unregister_dev(vpic->kvm, KVM_MMIO_BUS, + &vpic->ls7a_ioapic_alias); + kvm_io_bus_unregister_dev(vpic->kvm, KVM_MMIO_BUS, + &vpic->dev_ls7a_ioapic); + kfree(vpic); +} + +void kvm_dump_ls7a_ioapic_state(struct seq_file *m, + struct ls7a_kvm_ioapic *ioapic) +{ + struct kvm_ls7a_ioapic_state *ioapic_state; + unsigned long flags; + int i = 0; + + if (!ioapic) + return; + + seq_puts(m, "\nIOAPIC state:\n"); + ioapic_state = &(ioapic->ls7a_ioapic); + + ls7a_ioapic_lock(ioapic, &flags); + seq_puts(m, "irq masked: "); + for (i = 0; i < 64; i++) { + if (!test_bit(i, (void *)&ioapic_state->int_mask)) + seq_printf(m, "%d ", i); + } + seq_printf(m, "\nhtmsi_en:0x%016llx\n" + "intedge:0x%016llx", + ioapic_state->htmsi_en, + ioapic_state->intedge); + + seq_puts(m, "\nroute_entry: "); + for (i = 0; i < 64; i++) + seq_printf(m, "%d ", ioapic_state->route_entry[i]); + + seq_puts(m, "\nhtmsi_vector: "); + for (i = 0; i < 64; i++) + seq_printf(m, "%d ", ioapic_state->htmsi_vector[i]); + + seq_printf(m, "\nintirr:%016llx\n" + "intisr:%016llx\n", + ioapic_state->intirr, + ioapic_state->intisr); + ls7a_ioapic_unlock(ioapic, &flags); +} diff --git a/arch/loongarch/kvm/intc/ls7a_irq.h b/arch/loongarch/kvm/intc/ls7a_irq.h new file mode 100644 index 000000000000..fad85d55240d --- /dev/null +++ b/arch/loongarch/kvm/intc/ls7a_irq.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#ifndef __LS7A_KVM_IRQ_H +#define __LS7A_KVM_IRQ_H + +#include +#include +#include +#include +#include +#include +#include "kvmcpu.h" + +#include + +#define LS7A_APIC_NUM_PINS 64 + +#define LS7A_ROUTE_ENTRY_OFFSET 0x100 +#define LS7A_INT_ID_OFFSET 0x0 +#define LS7A_INT_ID_VAL 0x7000000UL +#define LS7A_INT_ID_VER 0x1f0001UL +#define LS7A_INT_MASK_OFFSET 0x20 +#define LS7A_INT_EDGE_OFFSET 0x60 +#define LS7A_INT_CLEAR_OFFSET 0x80 +#define LS7A_INT_STATUS_OFFSET 0x3a0 +#define LS7A_INT_POL_OFFSET 0x3e0 +#define LS7A_HTMSI_EN_OFFSET 0x40 +#define LS7A_HTMSI_VEC_OFFSET 0x200 +#define LS7A_AUTO_CTRL0_OFFSET 0xc0 +#define LS7A_AUTO_CTRL1_OFFSET 0xe0 + +#define LS7A_IOAPIC_GUEST_REG_BASE 0x10000000UL +#define LS7A_IOAPIC_GUEST_REG_BASE_ALIAS 0xe0010000000UL + +#define LS7A_IOAPIC_NUM_PINS 32 + +typedef struct kvm_ls7a_ioapic_state { + u64 int_id; + /* 0x020 interrupt mask register */ + u64 int_mask; + /* 0x040 1=msi */ + u64 htmsi_en; + /* 0x060 edge=1 level =0 */ + u64 intedge; + /* 0x080 for clean edge int,set 1 clean,set 0 is noused */ + u64 intclr; + /* 0x0c0 */ + u64 auto_crtl0; + /* 0x0e0 */ + u64 auto_crtl1; + /* 0x100 - 0x140 */ + u8 route_entry[64]; + /* 0x200 - 0x240 */ + u8 htmsi_vector[64]; + /* 0x300 */ + u64 intisr_chip0; + /* 0x320 */ + u64 intisr_chip1; + /* edge detection */ + u64 last_intirr; + /* 0x380 interrupt request register */ + u64 intirr; + /* 0x3a0 interrupt service register */ + u64 intisr; + /* 0x3e0 interrupt level polarity selection register, + * 0 for high level tirgger + */ + u64 int_polarity; +} LS7AApicState; + +struct ls7a_kvm_ioapic { + spinlock_t lock; + bool wakeup_needed; + unsigned pending_acks; + struct kvm *kvm; + struct kvm_ls7a_ioapic_state ls7a_ioapic; + struct kvm_io_device dev_ls7a_ioapic; + struct kvm_io_device ls7a_ioapic_alias; + void (*ack_notifier)(void *opaque, int irq); + unsigned long irq_states[LS7A_APIC_NUM_PINS]; +}; + +static inline struct ls7a_kvm_ioapic *ls7a_ioapic_irqchip(struct kvm *kvm) +{ + return kvm->arch.v_ioapic; +} + +static inline int ls7a_ioapic_in_kernel(struct kvm *kvm) +{ + int ret; + + ret = (ls7a_ioapic_irqchip(kvm) != NULL); + return ret; +} + +int kvm_set_ls7a_ioapic(struct kvm *kvm, struct ls7a_ioapic_state *state); +int kvm_get_ls7a_ioapic(struct kvm *kvm, struct ls7a_ioapic_state *state); +int kvm_create_ls7a_ioapic(struct kvm *kvm); +void kvm_destroy_ls7a_ioapic(struct kvm *kvm); +int kvm_ls7a_ioapic_set_irq(struct kvm *kvm, int irq, int level); + +void ls7a_ioapic_lock(struct ls7a_kvm_ioapic *s, unsigned long *flags); +void ls7a_ioapic_unlock(struct ls7a_kvm_ioapic *s, unsigned long *flags); +int kvm_ls7a_send_userspace_msi(struct kvm *kvm, struct kvm_msi *msi); +int kvm_ls7a_set_msi(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm, + int irq_source_id, int level, bool line_status); + +void kvm_dump_ls7a_ioapic_state(struct seq_file *m, struct ls7a_kvm_ioapic *ioapic); +#endif diff --git a/arch/loongarch/kvm/interrupt.c b/arch/loongarch/kvm/interrupt.c new file mode 100644 index 000000000000..fb7c4ef7f21f --- /dev/null +++ b/arch/loongarch/kvm/interrupt.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include +#include +#include +#include "kvmcpu.h" +#include +#include "kvm_compat.h" + +static u32 int_to_coreint[LOONGARCH_EXC_MAX] = { + [LARCH_INT_TIMER] = CPU_TIMER, + [LARCH_INT_IPI] = CPU_IPI, + [LARCH_INT_SIP0] = CPU_SIP0, + [LARCH_INT_SIP1] = CPU_SIP1, + [LARCH_INT_IP0] = CPU_IP0, + [LARCH_INT_IP1] = CPU_IP1, + [LARCH_INT_IP2] = CPU_IP2, + [LARCH_INT_IP3] = CPU_IP3, + [LARCH_INT_IP4] = CPU_IP4, + [LARCH_INT_IP5] = CPU_IP5, + [LARCH_INT_IP6] = CPU_IP6, + [LARCH_INT_IP7] = CPU_IP7, +}; + +static int _kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority) +{ + unsigned int irq = 0; + + clear_bit(priority, &vcpu->arch.irq_pending); + if (priority < LOONGARCH_EXC_MAX) + irq = int_to_coreint[priority]; + + switch (priority) { + case LARCH_INT_TIMER: + case LARCH_INT_IPI: + case LARCH_INT_SIP0: + case LARCH_INT_SIP1: + kvm_set_gcsr_estat(irq); + break; + + case LARCH_INT_IP0: + case LARCH_INT_IP1: + case LARCH_INT_IP2: + case LARCH_INT_IP3: + case LARCH_INT_IP4: + case LARCH_INT_IP5: + case LARCH_INT_IP6: + case LARCH_INT_IP7: + kvm_set_csr_gintc(irq); + break; + + default: + break; + } + + return 1; +} + +static int _kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority) +{ + unsigned int irq = 0; + + clear_bit(priority, &vcpu->arch.irq_clear); + if (priority < LOONGARCH_EXC_MAX) + irq = int_to_coreint[priority]; + + switch (priority) { + case LARCH_INT_TIMER: + case LARCH_INT_IPI: + case LARCH_INT_SIP0: + case LARCH_INT_SIP1: + kvm_clear_gcsr_estat(irq); + break; + + case LARCH_INT_IP0: + case LARCH_INT_IP1: + case LARCH_INT_IP2: + case LARCH_INT_IP3: + case LARCH_INT_IP4: + case LARCH_INT_IP5: + case LARCH_INT_IP6: + case LARCH_INT_IP7: + kvm_clear_csr_gintc(irq); + break; + + default: + break; + } + + return 1; +} + +void _kvm_deliver_intr(struct kvm_vcpu *vcpu) +{ + unsigned long *pending = &vcpu->arch.irq_pending; + unsigned long *pending_clr = &vcpu->arch.irq_clear; + unsigned int priority; + + if (!(*pending) && !(*pending_clr)) + return; + + if (*pending_clr) { + priority = __ffs(*pending_clr); + while (priority <= LOONGARCH_EXC_IPNUM) { + _kvm_irq_clear(vcpu, priority); + priority = find_next_bit(pending_clr, + BITS_PER_BYTE * sizeof(*pending_clr), + priority + 1); + } + } + + if (*pending) { + priority = __ffs(*pending); + while (priority <= LOONGARCH_EXC_IPNUM) { + _kvm_irq_deliver(vcpu, priority); + priority = find_next_bit(pending, + BITS_PER_BYTE * sizeof(*pending), + priority + 1); + } + } + +} + +int _kvm_pending_timer(struct kvm_vcpu *vcpu) +{ + return test_bit(LARCH_INT_TIMER, &vcpu->arch.irq_pending); +} diff --git a/arch/loongarch/kvm/irqfd.c b/arch/loongarch/kvm/irqfd.c new file mode 100644 index 000000000000..be2b1bfd07e2 --- /dev/null +++ b/arch/loongarch/kvm/irqfd.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#include +#include +#include "intc/ls7a_irq.h" + +static int kvm_ls7a_set_ioapic_irq(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, + int level, bool line_status) +{ + /* ioapic pin (0 ~ 64) <---> gsi(0 ~ 64) */ + u32 irq = e->irqchip.pin; + unsigned long flags; + int ret; + + if (!ls7a_ioapic_in_kernel(kvm)) + return -ENXIO; + + ls7a_ioapic_lock(ls7a_ioapic_irqchip(kvm), &flags); + ret = kvm_ls7a_ioapic_set_irq(kvm, irq, level); + ls7a_ioapic_unlock(ls7a_ioapic_irqchip(kvm), &flags); + + return ret; +} + +/** + * kvm_set_routing_entry: populate a kvm routing entry + * from a user routing entry + * + * @kvm: the VM this entry is applied to + * @e: kvm kernel routing entry handle + * @ue: user api routing entry handle + * return 0 on success, -EINVAL on errors. + */ +int kvm_set_routing_entry(struct kvm *kvm, + struct kvm_kernel_irq_routing_entry *e, + const struct kvm_irq_routing_entry *ue) +{ + int r = -EINVAL; + + switch (ue->type) { + case KVM_IRQ_ROUTING_IRQCHIP: + e->set = kvm_ls7a_set_ioapic_irq; + + e->irqchip.irqchip = ue->u.irqchip.irqchip; + e->irqchip.pin = ue->u.irqchip.pin; + + if (e->irqchip.pin >= LS7A_APIC_NUM_PINS) + goto out; + break; + case KVM_IRQ_ROUTING_MSI: + e->set = kvm_set_msi; + e->msi.address_lo = ue->u.msi.address_lo; + e->msi.address_hi = ue->u.msi.address_hi; + e->msi.data = ue->u.msi.data; + /* + * linux4.19 kernel have flags and devid + * e->msi.flags = ue->flags; + * e->msi.devid = ue->u.msi.devid; + */ + break; + default: + goto out; + } + r = 0; +out: + return r; +} + +int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, + int level, bool line_status) +{ + if (e->type == KVM_IRQ_ROUTING_MSI) + return kvm_ls7a_set_msi(e, kvm, irq_source_id, 1, false); + + return -EWOULDBLOCK; +} + +/** + * kvm_set_msi: inject the MSI corresponding to the + * MSI routing entry + * + * This is the entry point for irqfd MSI injection + * and userspace MSI injection. + */ +int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, + struct kvm *kvm, int irq_source_id, + int level, bool line_status) +{ + unsigned int ret; + + if (!level) + return -1; + + ret = kvm_ls7a_set_msi(e, kvm, irq_source_id, 1, false); + return ret; +} diff --git a/arch/loongarch/kvm/kvm_compat.c b/arch/loongarch/kvm/kvm_compat.c new file mode 100644 index 000000000000..a8931cf0ab47 --- /dev/null +++ b/arch/loongarch/kvm/kvm_compat.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ +#include +#include +#include +#include "kvm_compat.h" + +extern int _kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) +void kvm_arch_check_processor_compat(void *rtn) +{ + *(int *)rtn = 0; +} + +void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte) +{ + _kvm_set_spte_hva(kvm, hva, pte); + return; +} + +#else +int kvm_arch_check_processor_compat(void) +{ + return 0; +} + +int kvm_vm_ioctl_clear_dirty_log(struct kvm *kvm, struct kvm_clear_dirty_log *log) +{ + struct kvm_memslots *slots; + struct kvm_memory_slot *memslot; + bool is_dirty = false; + int r; + + mutex_lock(&kvm->slots_lock); + + r = kvm_clear_dirty_log_protect(kvm, log, &is_dirty); + + if (is_dirty) { + slots = kvm_memslots(kvm); + memslot = id_to_memslot(slots, log->slot); + + /* Let implementation handle TLB/GVA invalidation */ + kvm_flush_remote_tlbs(kvm); + } + + mutex_unlock(&kvm->slots_lock); + return r; +} + +int kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte) +{ + return _kvm_set_spte_hva(kvm, hva, pte); +} +#endif + diff --git a/arch/loongarch/kvm/kvm_compat.h b/arch/loongarch/kvm/kvm_compat.h new file mode 100644 index 000000000000..ff68ab1d8aba --- /dev/null +++ b/arch/loongarch/kvm/kvm_compat.h @@ -0,0 +1,766 @@ +#ifndef __LOONGARCH_KVM_COMPAT_H__ +#define __LOONGARCH_KVM_COMPAT_H__ + +#ifdef __ASSEMBLY__ +#define _ULCAST_ +#else +#define _ULCAST_ (unsigned long) +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) +#include +#endif +#endif + +#define KVM_CSR_CRMD 0x0 /* Current mode info */ +#define KVM_CRMD_WE_SHIFT 9 +#define KVM_CRMD_WE (_ULCAST_(0x1) << KVM_CRMD_WE_SHIFT) +#define KVM_CRMD_DACM_SHIFT 7 +#define KVM_CRMD_DACM_WIDTH 2 +#define KVM_CRMD_DACM (_ULCAST_(0x3) << KVM_CRMD_DACM_SHIFT) +#define KVM_CRMD_DACF_SHIFT 5 +#define KVM_CRMD_DACF_WIDTH 2 +#define KVM_CRMD_DACF (_ULCAST_(0x3) << KVM_CRMD_DACF_SHIFT) +#define KVM_CRMD_PG_SHIFT 4 +#define KVM_CRMD_PG (_ULCAST_(0x1) << KVM_CRMD_PG_SHIFT) +#define KVM_CRMD_DA_SHIFT 3 +#define KVM_CRMD_DA (_ULCAST_(0x1) << KVM_CRMD_DA_SHIFT) +#define KVM_CRMD_IE_SHIFT 2 +#define KVM_CRMD_IE (_ULCAST_(0x1) << KVM_CRMD_IE_SHIFT) +#define KVM_CRMD_PLV_SHIFT 0 +#define KVM_CRMD_PLV_WIDTH 2 +#define KVM_CRMD_PLV (_ULCAST_(0x3) << KVM_CRMD_PLV_SHIFT) + +#define KVM_CSR_PRMD 0x1 /* Prev-exception mode info */ +#define KVM_PRMD_PIE_SHIFT 2 +#define KVM_PRMD_PWE_SHIFT 3 +#define KVM_PRMD_PIE (_ULCAST_(0x1) << KVM_PRMD_PIE_SHIFT) +#define KVM_PRMD_PWE (_ULCAST_(0x1) << KVM_PRMD_PWE_SHIFT) +#define KVM_PRMD_PPLV_SHIFT 0 +#define KVM_PRMD_PPLV_WIDTH 2 +#define KVM_PRMD_PPLV (_ULCAST_(0x3) << KVM_PRMD_PPLV_SHIFT) + +#define KVM_CSR_EUEN 0x2 /* Extended unit enable */ +#define KVM_EUEN_LBTEN_SHIFT 3 +#define KVM_EUEN_LBTEN (_ULCAST_(0x1) << KVM_EUEN_LBTEN_SHIFT) +#define KVM_EUEN_LASXEN_SHIFT 2 +#define KVM_EUEN_LASXEN (_ULCAST_(0x1) << KVM_EUEN_LASXEN_SHIFT) +#define KVM_EUEN_LSXEN_SHIFT 1 +#define KVM_EUEN_LSXEN (_ULCAST_(0x1) << KVM_EUEN_LSXEN_SHIFT) +#define KVM_EUEN_FPEN_SHIFT 0 +#define KVM_EUEN_FPEN (_ULCAST_(0x1) << KVM_EUEN_FPEN_SHIFT) + +#define KVM_CSR_MISC 0x3 /* Misc config */ +#define KVM_CSR_ECFG 0x4 /* Exception config */ +#define KVM_ECFG_VS_SHIFT 16 +#define KVM_ECFG_VS_WIDTH 3 +#define KVM_ECFG_VS (_ULCAST_(0x7) << KVM_ECFG_VS_SHIFT) +#define KVM_ECFG_IM_SHIFT 0 +#define KVM_ECFG_IM_WIDTH 13 +#define KVM_ECFG_IM (_ULCAST_(0x1fff) << KVM_ECFG_IM_SHIFT) + +#define KVM_CSR_ESTAT 0x5 /* Exception status */ +#define KVM_ESTAT_ESUBCODE_SHIFT 22 +#define KVM_ESTAT_ESUBCODE_WIDTH 9 +#define KVM_ESTAT_ESUBCODE (_ULCAST_(0x1ff) << KVM_ESTAT_ESUBCODE_SHIFT) +#define KVM_ESTAT_EXC_SHIFT 16 +#define KVM_ESTAT_EXC_WIDTH 6 +#define KVM_ESTAT_EXC (_ULCAST_(0x3f) << KVM_ESTAT_EXC_SHIFT) +#define KVM_ESTAT_IS_SHIFT 0 +#define KVM_ESTAT_IS_WIDTH 15 +#define KVM_ESTAT_IS (_ULCAST_(0x7fff) << KVM_ESTAT_IS_SHIFT) + +#define KVM_CSR_ERA 0x6 /* ERA */ +#define KVM_CSR_BADV 0x7 /* Bad virtual address */ +#define KVM_CSR_BADI 0x8 /* Bad instruction */ +#define KVM_CSR_EENTRY 0xc /* Exception entry base address */ +#define KVM_CSR_TLBIDX 0x10 /* TLB Index, EHINV, PageSize, NP */ +#define KVM_CSR_TLBEHI 0x11 /* TLB EntryHi */ +#define KVM_CSR_TLBELO0 0x12 /* TLB EntryLo0 */ +#define KVM_CSR_TLBELO1 0x13 /* TLB EntryLo1 */ +#define KVM_CSR_GTLBC 0x15 /* Guest TLB control */ +#define KVM_GTLBC_TGID_SHIFT 16 +#define KVM_GTLBC_TGID_WIDTH 8 +#define KVM_GTLBC_TGID (_ULCAST_(0xff) << KVM_GTLBC_TGID_SHIFT) +#define KVM_GTLBC_TOTI_SHIFT 13 +#define KVM_GTLBC_TOTI (_ULCAST_(0x1) << KVM_GTLBC_TOTI_SHIFT) +#define KVM_GTLBC_USETGID_SHIFT 12 +#define KVM_GTLBC_USETGID (_ULCAST_(0x1) << KVM_GTLBC_USETGID_SHIFT) +#define KVM_GTLBC_GMTLBSZ_SHIFT 0 +#define KVM_GTLBC_GMTLBSZ_WIDTH 6 +#define KVM_GTLBC_GMTLBSZ (_ULCAST_(0x3f) << KVM_GTLBC_GMTLBSZ_SHIFT) + +#define KVM_CSR_TRGP 0x16 /* TLBR read guest info */ +#define KVM_CSR_ASID 0x18 /* ASID */ +#define KVM_CSR_PGDL 0x19 /* Page table base address when VA[47] = 0 */ +#define KVM_CSR_PGDH 0x1a /* Page table base address when VA[47] = 1 */ +#define KVM_CSR_PGD 0x1b /* Page table base */ +#define KVM_CSR_PWCTL0 0x1c /* PWCtl0 */ +#define KVM_CSR_PWCTL1 0x1d /* PWCtl1 */ +#define KVM_CSR_STLBPGSIZE 0x1e +#define KVM_CSR_RVACFG 0x1f +#define KVM_CSR_CPUID 0x20 /* CPU core number */ +#define KVM_CSR_PRCFG1 0x21 /* Config1 */ +#define KVM_CSR_PRCFG2 0x22 /* Config2 */ +#define KVM_CSR_PRCFG3 0x23 /* Config3 */ +#define KVM_CSR_KS0 0x30 +#define KVM_CSR_KS1 0x31 +#define KVM_CSR_KS2 0x32 +#define KVM_CSR_KS3 0x33 +#define KVM_CSR_KS4 0x34 +#define KVM_CSR_KS5 0x35 +#define KVM_CSR_KS6 0x36 +#define KVM_CSR_KS7 0x37 +#define KVM_CSR_KS8 0x38 +#define KVM_CSR_TMID 0x40 /* Timer ID */ +#define KVM_CSR_TCFG 0x41 /* Timer config */ +#define KVM_TCFG_VAL_SHIFT 2 +#define KVM_TCFG_VAL_WIDTH 48 +#define KVM_TCFG_VAL (_ULCAST_(0x3fffffffffff) << KVM_TCFG_VAL_SHIFT) +#define KVM_TCFG_PERIOD_SHIFT 1 +#define KVM_TCFG_PERIOD (_ULCAST_(0x1) << KVM_TCFG_PERIOD_SHIFT) +#define KVM_TCFG_EN (_ULCAST_(0x1)) + +#define KVM_CSR_TVAL 0x42 /* Timer value */ +#define KVM_CSR_CNTC 0x43 /* Timer offset */ +#define KVM_CSR_TINTCLR 0x44 /* Timer interrupt clear */ +#define KVM_CSR_GSTAT 0x50 /* Guest status */ +#define KVM_GSTAT_GID_SHIFT 16 +#define KVM_GSTAT_GID_WIDTH 8 +#define KVM_GSTAT_GID (_ULCAST_(0xff) << KVM_GSTAT_GID_SHIFT) +#define KVM_GSTAT_GIDBIT_SHIFT 4 +#define KVM_GSTAT_GIDBIT_WIDTH 6 +#define KVM_GSTAT_GIDBIT (_ULCAST_(0x3f) << KVM_GSTAT_GIDBIT_SHIFT) +#define KVM_GSTAT_PVM_SHIFT 1 +#define KVM_GSTAT_PVM (_ULCAST_(0x1) << KVM_GSTAT_PVM_SHIFT) +#define KVM_GSTAT_VM_SHIFT 0 +#define KVM_GSTAT_VM (_ULCAST_(0x1) << KVM_GSTAT_VM_SHIFT) + +#define KVM_CSR_GCFG 0x51 /* Guest config */ +#define KVM_GCFG_GPERF_SHIFT 24 +#define KVM_GCFG_GPERF_WIDTH 3 +#define KVM_GCFG_GPERF (_ULCAST_(0x7) << KVM_GCFG_GPERF_SHIFT) +#define KVM_GCFG_GCI_SHIFT 20 +#define KVM_GCFG_GCI_WIDTH 2 +#define KVM_GCFG_GCI (_ULCAST_(0x3) << KVM_GCFG_GCI_SHIFT) +#define KVM_GCFG_GCI_ALL (_ULCAST_(0x0) << KVM_GCFG_GCI_SHIFT) +#define KVM_GCFG_GCI_HIT (_ULCAST_(0x1) << KVM_GCFG_GCI_SHIFT) +#define KVM_GCFG_GCI_SECURE (_ULCAST_(0x2) << KVM_GCFG_GCI_SHIFT) +#define KVM_GCFG_GCIP_SHIFT 16 +#define KVM_GCFG_GCIP (_ULCAST_(0xf) << KVM_GCFG_GCIP_SHIFT) +#define KVM_GCFG_GCIP_ALL (_ULCAST_(0x1) << KVM_GCFG_GCIP_SHIFT) +#define KVM_GCFG_GCIP_HIT (_ULCAST_(0x1) << (KVM_GCFG_GCIP_SHIFT + 1)) +#define KVM_GCFG_GCIP_SECURE (_ULCAST_(0x1) << (KVM_GCFG_GCIP_SHIFT + 2)) +#define KVM_GCFG_TORU_SHIFT 15 +#define KVM_GCFG_TORU (_ULCAST_(0x1) << KVM_GCFG_TORU_SHIFT) +#define KVM_GCFG_TORUP_SHIFT 14 +#define KVM_GCFG_TORUP (_ULCAST_(0x1) << KVM_GCFG_TORUP_SHIFT) +#define KVM_GCFG_TOP_SHIFT 13 +#define KVM_GCFG_TOP (_ULCAST_(0x1) << KVM_GCFG_TOP_SHIFT) +#define KVM_GCFG_TOPP_SHIFT 12 +#define KVM_GCFG_TOPP (_ULCAST_(0x1) << KVM_GCFG_TOPP_SHIFT) +#define KVM_GCFG_TOE_SHIFT 11 +#define KVM_GCFG_TOE (_ULCAST_(0x1) << KVM_GCFG_TOE_SHIFT) +#define KVM_GCFG_TOEP_SHIFT 10 +#define KVM_GCFG_TOEP (_ULCAST_(0x1) << KVM_GCFG_TOEP_SHIFT) +#define KVM_GCFG_TIT_SHIFT 9 +#define KVM_GCFG_TIT (_ULCAST_(0x1) << KVM_GCFG_TIT_SHIFT) +#define KVM_GCFG_TITP_SHIFT 8 +#define KVM_GCFG_TITP (_ULCAST_(0x1) << KVM_GCFG_TITP_SHIFT) +#define KVM_GCFG_SIT_SHIFT 7 +#define KVM_GCFG_SIT (_ULCAST_(0x1) << KVM_GCFG_SIT_SHIFT) +#define KVM_GCFG_SITP_SHIFT 6 +#define KVM_GCFG_SITP (_ULCAST_(0x1) << KVM_GCFG_SITP_SHIFT) +#define KVM_GCFG_MATC_SHITF 4 +#define KVM_GCFG_MATC_WIDTH 2 +#define KVM_GCFG_MATC_MASK (_ULCAST_(0x3) << KVM_GCFG_MATC_SHITF) +#define KVM_GCFG_MATC_GUEST (_ULCAST_(0x0) << KVM_GCFG_MATC_SHITF) +#define KVM_GCFG_MATC_ROOT (_ULCAST_(0x1) << KVM_GCFG_MATC_SHITF) +#define KVM_GCFG_MATC_NEST (_ULCAST_(0x2) << KVM_GCFG_MATC_SHITF) +#define KVM_GCFG_MATP_SHITF 0 +#define KVM_GCFG_MATP_WIDTH 4 +#define KVM_GCFG_MATR_MASK (_ULCAST_(0x3) << KVM_GCFG_MATP_SHITF) +#define KVM_GCFG_MATP_GUEST (_ULCAST_(0x0) << KVM_GCFG_MATP_SHITF) +#define KVM_GCFG_MATP_ROOT (_ULCAST_(0x1) << KVM_GCFG_MATP_SHITF) +#define KVM_GCFG_MATP_NEST (_ULCAST_(0x2) << KVM_GCFG_MATP_SHITF) + +#define KVM_CSR_GINTC 0x52 /* Guest interrupt control */ +#define KVM_CSR_GCNTC 0x53 /* Guest timer offset */ +#define KVM_CSR_LLBCTL 0x60 /* LLBit control */ +#define KVM_LLBCTL_ROLLB_SHIFT 0 +#define KVM_LLBCTL_ROLLB (_ULCAST_(1) << KVM_LLBCTL_ROLLB_SHIFT) +#define KVM_LLBCTL_WCLLB_SHIFT 1 +#define KVM_LLBCTL_WCLLB (_ULCAST_(1) << KVM_LLBCTL_WCLLB_SHIFT) +#define KVM_LLBCTL_KLO_SHIFT 2 +#define KVM_LLBCTL_KLO (_ULCAST_(1) << KVM_LLBCTL_KLO_SHIFT) + +#define KVM_CSR_IMPCTL1 0x80 /* Loongson config1 */ +#define KVM_CSR_IMPCTL2 0x81 /* Loongson config2 */ +#define KVM_CSR_GNMI 0x82 +#define KVM_CSR_TLBRENTRY 0x88 /* TLB refill exception base address */ +#define KVM_CSR_TLBRBADV 0x89 /* TLB refill badvaddr */ +#define KVM_CSR_TLBRERA 0x8a /* TLB refill ERA */ +#define KVM_CSR_TLBRSAVE 0x8b /* KScratch for TLB refill exception */ +#define KVM_CSR_TLBRELO0 0x8c /* TLB refill entrylo0 */ +#define KVM_CSR_TLBRELO1 0x8d /* TLB refill entrylo1 */ +#define KVM_CSR_TLBREHI 0x8e /* TLB refill entryhi */ +#define KVM_CSR_TLBRPRMD 0x8f /* TLB refill mode info */ +#define KVM_CSR_ERRCTL 0x90 /* ERRCTL */ +#define KVM_CSR_ERRINFO1 0x91 /* Error info1 */ +#define KVM_CSR_ERRINFO2 0x92 /* Error info2 */ +#define KVM_CSR_MERRENTRY 0x93 /* Error exception base address */ +#define KVM_CSR_MERRERA 0x94 /* Error exception PC */ +#define KVM_CSR_ERRSAVE 0x95 /* KScratch for machine error exception */ +#define KVM_CSR_CTAG 0x98 /* TagLo + TagHi */ +#define KVM_CSR_DMWIN0 0x180 /* 64 direct map win0: MEM & IF */ +#define KVM_CSR_DMWIN1 0x181 /* 64 direct map win1: MEM & IF */ +#define KVM_CSR_DMWIN2 0x182 /* 64 direct map win2: MEM */ +#define KVM_CSR_DMWIN3 0x183 /* 64 direct map win3: MEM */ +#define KVM_CSR_PERFCTRL0 0x200 /* 32 perf event 0 config */ +#define KVM_CSR_PERFCNTR0 0x201 /* 64 perf event 0 count value */ +#define KVM_CSR_PERFCTRL1 0x202 /* 32 perf event 1 config */ +#define KVM_CSR_PERFCNTR1 0x203 /* 64 perf event 1 count value */ +#define KVM_CSR_PERFCTRL2 0x204 /* 32 perf event 2 config */ +#define KVM_CSR_PERFCNTR2 0x205 /* 64 perf event 2 count value */ +#define KVM_CSR_PERFCTRL3 0x206 /* 32 perf event 3 config */ +#define KVM_CSR_PERFCNTR3 0x207 /* 64 perf event 3 count value */ +#define KVM_CSR_MWPC 0x300 /* data breakpoint config */ +#define KVM_CSR_MWPS 0x301 /* data breakpoint status */ +#define KVM_CSR_FWPC 0x380 /* instruction breakpoint config */ +#define KVM_CSR_FWPS 0x381 /* instruction breakpoint status */ +#define KVM_CSR_DEBUG 0x500 /* debug config */ +#define KVM_CSR_DERA 0x501 /* debug era */ +#define KVM_CSR_DESAVE 0x502 /* debug save */ + +#define KVM_IOCSR_FEATURES 0x8 +#define KVM_IOCSRF_TEMP BIT_ULL(0) +#define KVM_IOCSRF_NODECNT BIT_ULL(1) +#define KVM_IOCSRF_MSI BIT_ULL(2) +#define KVM_IOCSRF_EXTIOI BIT_ULL(3) +#define KVM_IOCSRF_CSRIPI BIT_ULL(4) +#define KVM_IOCSRF_FREQCSR BIT_ULL(5) +#define KVM_IOCSRF_FREQSCALE BIT_ULL(6) +#define KVM_IOCSRF_DVFSV1 BIT_ULL(7) +#define KVM_IOCSRF_EXTIOI_DECODE BIT_ULL(9) +#define KVM_IOCSRF_FLATMODE BIT_ULL(10) +#define KVM_IOCSRF_VM BIT_ULL(11) + +#define KVM_IOCSR_VENDOR 0x10 +#define KVM_IOCSR_CPUNAME 0x20 +#define KVM_IOCSR_NODECNT 0x408 + +#define KVM_IOCSR_MISC_FUNC 0x420 +#define KVM_IOCSRF_MISC_FUNC_EXT_IOI_EN BIT_ULL(48) + +/* PerCore CSR, only accessable by local cores */ +#define KVM_IOCSR_IPI_STATUS 0x1000 +#define KVM_IOCSR_IPI_SEND 0x1040 +#define KVM_IOCSR_MBUF_SEND 0x1048 +#define KVM_IOCSR_EXTIOI_NODEMAP_BASE 0x14a0 +#define KVM_IOCSR_EXTIOI_IPMAP_BASE 0x14c0 +#define KVM_IOCSR_EXTIOI_EN_BASE 0x1600 +#define KVM_IOCSR_EXTIOI_BOUNCE_BASE 0x1680 +#define KVM_IOCSR_EXTIOI_ISR_BASE 0x1800 +#define KVM_IOCSR_EXTIOI_ROUTE_BASE 0x1c00 + +#ifndef __ASSEMBLY__ + +/* CSR */ +static inline u32 kvm_csr_readl(u32 reg) +{ + u32 val; + + asm volatile ( + "csrrd %[val], %[reg] \n" + : [val] "=r" (val) + : [reg] "i" (reg) + : "memory"); + return val; +} + +static inline u64 kvm_csr_readq(u32 reg) +{ + u64 val; + + asm volatile ( + "csrrd %[val], %[reg] \n" + : [val] "=r" (val) + : [reg] "i" (reg) + : "memory"); + return val; +} + +static inline void kvm_csr_writel(u32 val, u32 reg) +{ + asm volatile ( + "csrwr %[val], %[reg] \n" + : [val] "+r" (val) + : [reg] "i" (reg) + : "memory"); +} + +static inline void kvm_csr_writeq(u64 val, u32 reg) +{ + asm volatile ( + "csrwr %[val], %[reg] \n" + : [val] "+r" (val) + : [reg] "i" (reg) + : "memory"); +} + +static inline u32 kvm_csr_xchgl(u32 val, u32 mask, u32 reg) +{ + asm volatile ( + "csrxchg %[val], %[mask], %[reg] \n" + : [val] "+r" (val) + : [mask] "r" (mask), [reg] "i" (reg) + : "memory"); + return val; +} + +static inline u64 kvm_csr_xchgq(u64 val, u64 mask, u32 reg) +{ + asm volatile ( + "csrxchg %[val], %[mask], %[reg] \n" + : [val] "+r" (val) + : [mask] "r" (mask), [reg] "i" (reg) + : "memory"); + return val; +} + + +/* IOCSR */ +static inline u32 kvm_iocsr_readl(u32 reg) +{ + u32 val; + + asm volatile ( + "iocsrrd.w %[val], %[reg] \n" + : [val] "=r" (val) + : [reg] "r" (reg) + : "memory"); + return val; +} + +static inline u64 kvm_iocsr_readq(u32 reg) +{ + u64 val; + + asm volatile ( + "iocsrrd.d %[val], %[reg] \n" + : [val] "=r" (val) + : [reg] "r" (reg) + : "memory"); + return val; +} + +static inline void kvm_iocsr_writeb(u8 val, u32 reg) +{ + asm volatile ( + "iocsrwr.b %[val], %[reg] \n" + : + : [val] "r" (val), [reg] "r" (reg) + : "memory"); +} + +static inline void kvm_iocsr_writel(u32 val, u32 reg) +{ + asm volatile ( + "iocsrwr.w %[val], %[reg] \n" + : + : [val] "r" (val), [reg] "r" (reg) + : "memory"); +} + +static inline void kvm_iocsr_writeq(u64 val, u32 reg) +{ + asm volatile ( + "iocsrwr.d %[val], %[reg] \n" + : + : [val] "r" (val), [reg] "r" (reg) + : "memory"); +} + + +/* GCSR */ +static inline u64 kvm_gcsr_read(u32 reg) +{ + u64 val = 0; + + asm volatile ( + "parse_r __reg, %[val] \n" + ".word 0x5 << 24 | %[reg] << 10 | 0 << 5 | __reg \n" + : [val] "+r" (val) + : [reg] "i" (reg) + : "memory"); + return val; +} + +static inline void kvm_gcsr_write(u64 val, u32 reg) +{ + asm volatile ( + "parse_r __reg, %[val] \n" + ".word 0x5 << 24 | %[reg] << 10 | 1 << 5 | __reg \n" + : [val] "+r" (val) + : [reg] "i" (reg) + : "memory"); +} + +static inline u64 kvm_gcsr_xchg(u64 val, u64 mask, u32 reg) +{ + asm volatile ( + "parse_r __rd, %[val] \n" + "parse_r __rj, %[mask] \n" + ".word 0x5 << 24 | %[reg] << 10 | __rj << 5 | __rd \n" + : [val] "+r" (val) + : [mask] "r" (mask), [reg] "i" (reg) + : "memory"); + return val; +} + +#endif /* !__ASSEMBLY__ */ + +#define kvm_read_csr_euen() kvm_csr_readq(KVM_CSR_EUEN) +#define kvm_write_csr_euen(val) kvm_csr_writeq(val, KVM_CSR_EUEN) +#define kvm_read_csr_ecfg() kvm_csr_readq(KVM_CSR_ECFG) +#define kvm_write_csr_ecfg(val) kvm_csr_writeq(val, KVM_CSR_ECFG) +#define kvm_write_csr_perfctrl0(val) kvm_csr_writeq(val, KVM_CSR_PERFCTRL0) +#define kvm_write_csr_perfcntr0(val) kvm_csr_writeq(val, LOONGARCH_CSR_PERFCNTR0) +#define kvm_write_csr_perfctrl1(val) kvm_csr_writeq(val, LOONGARCH_CSR_PERFCTRL1) +#define kvm_write_csr_perfcntr1(val) kvm_csr_writeq(val, LOONGARCH_CSR_PERFCNTR1) +#define kvm_write_csr_perfctrl2(val) kvm_csr_writeq(val, LOONGARCH_CSR_PERFCTRL2) +#define kvm_write_csr_perfcntr2(val) kvm_csr_writeq(val, LOONGARCH_CSR_PERFCNTR2) +#define kvm_write_csr_perfctrl3(val) kvm_csr_writeq(val, LOONGARCH_CSR_PERFCTRL3) +#define kvm_write_csr_perfcntr3(val) kvm_csr_writeq(val, LOONGARCH_CSR_PERFCNTR3) +#define kvm_read_csr_impctl1() kvm_csr_readq(LOONGARCH_CSR_IMPCTL1) +#define kvm_write_csr_impctl1(val) kvm_csr_writeq(val, LOONGARCH_CSR_IMPCTL1) + + +/* Guest related CSRS */ +#define kvm_read_csr_gtlbc() kvm_csr_readq(KVM_CSR_GTLBC) +#define kvm_write_csr_gtlbc(val) kvm_csr_writeq(val, KVM_CSR_GTLBC) +#define kvm_read_csr_trgp() kvm_csr_readq(KVM_CSR_TRGP) +#define kvm_read_csr_gcfg() kvm_csr_readq(KVM_CSR_GCFG) +#define kvm_write_csr_gcfg(val) kvm_csr_writeq(val, KVM_CSR_GCFG) +#define kvm_read_csr_gstat() kvm_csr_readq(KVM_CSR_GSTAT) +#define kvm_write_csr_gstat(val) kvm_csr_writeq(val, KVM_CSR_GSTAT) +#define kvm_read_csr_gintc() kvm_csr_readq(KVM_CSR_GINTC) +#define kvm_write_csr_gintc(val) kvm_csr_writeq(val, KVM_CSR_GINTC) +#define kvm_read_csr_gcntc() kvm_csr_readq(KVM_CSR_GCNTC) +#define kvm_write_csr_gcntc(val) kvm_csr_writeq(val, KVM_CSR_GCNTC) + +/* Guest CSRS read and write */ +#define kvm_read_gcsr_crmd() kvm_gcsr_read(KVM_CSR_CRMD) +#define kvm_write_gcsr_crmd(val) kvm_gcsr_write(val, KVM_CSR_CRMD) +#define kvm_read_gcsr_prmd() kvm_gcsr_read(KVM_CSR_PRMD) +#define kvm_write_gcsr_prmd(val) kvm_gcsr_write(val, KVM_CSR_PRMD) +#define kvm_read_gcsr_euen() kvm_gcsr_read(KVM_CSR_EUEN) +#define kvm_write_gcsr_euen(val) kvm_gcsr_write(val, KVM_CSR_EUEN) +#define kvm_read_gcsr_misc() kvm_gcsr_read(KVM_CSR_MISC) +#define kvm_write_gcsr_misc(val) kvm_gcsr_write(val, KVM_CSR_MISC) +#define kvm_read_gcsr_ecfg() kvm_gcsr_read(KVM_CSR_ECFG) +#define kvm_write_gcsr_ecfg(val) kvm_gcsr_write(val, KVM_CSR_ECFG) +#define kvm_read_gcsr_estat() kvm_gcsr_read(KVM_CSR_ESTAT) +#define kvm_write_gcsr_estat(val) kvm_gcsr_write(val, KVM_CSR_ESTAT) +#define kvm_read_gcsr_era() kvm_gcsr_read(KVM_CSR_ERA) +#define kvm_write_gcsr_era(val) kvm_gcsr_write(val, KVM_CSR_ERA) +#define kvm_read_gcsr_badv() kvm_gcsr_read(KVM_CSR_BADV) +#define kvm_write_gcsr_badv(val) kvm_gcsr_write(val, KVM_CSR_BADV) +#define kvm_read_gcsr_badi() kvm_gcsr_read(KVM_CSR_BADI) +#define kvm_write_gcsr_badi(val) kvm_gcsr_write(val, KVM_CSR_BADI) +#define kvm_read_gcsr_eentry() kvm_gcsr_read(KVM_CSR_EENTRY) +#define kvm_write_gcsr_eentry(val) kvm_gcsr_write(val, KVM_CSR_EENTRY) + +#define kvm_read_gcsr_tlbidx() kvm_gcsr_read(KVM_CSR_TLBIDX) +#define kvm_write_gcsr_tlbidx(val) kvm_gcsr_write(val, KVM_CSR_TLBIDX) +#define kvm_read_gcsr_tlbhi() kvm_gcsr_read(KVM_CSR_TLBEHI) +#define kvm_write_gcsr_tlbhi(val) kvm_gcsr_write(val, KVM_CSR_TLBEHI) +#define kvm_read_gcsr_tlblo0() kvm_gcsr_read(KVM_CSR_TLBELO0) +#define kvm_write_gcsr_tlblo0(val) kvm_gcsr_write(val, KVM_CSR_TLBELO0) +#define kvm_read_gcsr_tlblo1() kvm_gcsr_read(KVM_CSR_TLBELO1) +#define kvm_write_gcsr_tlblo1(val) kvm_gcsr_write(val, KVM_CSR_TLBELO1) + +#define kvm_read_gcsr_asid() kvm_gcsr_read(KVM_CSR_ASID) +#define kvm_write_gcsr_asid(val) kvm_gcsr_write(val, KVM_CSR_ASID) +#define kvm_read_gcsr_pgdl() kvm_gcsr_read(KVM_CSR_PGDL) +#define kvm_write_gcsr_pgdl(val) kvm_gcsr_write(val, KVM_CSR_PGDL) +#define kvm_read_gcsr_pgdh() kvm_gcsr_read(KVM_CSR_PGDH) +#define kvm_write_gcsr_pgdh(val) kvm_gcsr_write(val, KVM_CSR_PGDH) +#define kvm_write_gcsr_pgd(val) kvm_gcsr_write(val, KVM_CSR_PGD) +#define kvm_read_gcsr_pgd() kvm_gcsr_read(KVM_CSR_PGD) +#define kvm_read_gcsr_pwctl0() kvm_gcsr_read(KVM_CSR_PWCTL0) +#define kvm_write_gcsr_pwctl0(val) kvm_gcsr_write(val, KVM_CSR_PWCTL0) +#define kvm_read_gcsr_pwctl1() kvm_gcsr_read(KVM_CSR_PWCTL1) +#define kvm_write_gcsr_pwctl1(val) kvm_gcsr_write(val, KVM_CSR_PWCTL1) +#define kvm_read_gcsr_stlbpgsize() kvm_gcsr_read(KVM_CSR_STLBPGSIZE) +#define kvm_write_gcsr_stlbpgsize(val) kvm_gcsr_write(val, KVM_CSR_STLBPGSIZE) +#define kvm_read_gcsr_rvacfg() kvm_gcsr_read(KVM_CSR_RVACFG) +#define kvm_write_gcsr_rvacfg(val) kvm_gcsr_write(val, KVM_CSR_RVACFG) + +#define kvm_read_gcsr_cpuid() kvm_gcsr_read(KVM_CSR_CPUID) +#define kvm_write_gcsr_cpuid(val) kvm_gcsr_write(val, KVM_CSR_CPUID) +#define kvm_read_gcsr_prcfg1() kvm_gcsr_read(KVM_CSR_PRCFG1) +#define kvm_write_gcsr_prcfg1(val) kvm_gcsr_write(val, KVM_CSR_PRCFG1) +#define kvm_read_gcsr_prcfg2() kvm_gcsr_read(KVM_CSR_PRCFG2) +#define kvm_write_gcsr_prcfg2(val) kvm_gcsr_write(val, KVM_CSR_PRCFG2) +#define kvm_read_gcsr_prcfg3() kvm_gcsr_read(KVM_CSR_PRCFG3) +#define kvm_write_gcsr_prcfg3(val) kvm_gcsr_write(val, KVM_CSR_PRCFG3) + +#define kvm_read_gcsr_kscratch0() kvm_gcsr_read(KVM_CSR_KS0) +#define kvm_write_gcsr_kscratch0(val) kvm_gcsr_write(val, KVM_CSR_KS0) +#define kvm_read_gcsr_kscratch1() kvm_gcsr_read(KVM_CSR_KS1) +#define kvm_write_gcsr_kscratch1(val) kvm_gcsr_write(val, KVM_CSR_KS1) +#define kvm_read_gcsr_kscratch2() kvm_gcsr_read(KVM_CSR_KS2) +#define kvm_write_gcsr_kscratch2(val) kvm_gcsr_write(val, KVM_CSR_KS2) +#define kvm_read_gcsr_kscratch3() kvm_gcsr_read(KVM_CSR_KS3) +#define kvm_write_gcsr_kscratch3(val) kvm_gcsr_write(val, KVM_CSR_KS3) +#define kvm_read_gcsr_kscratch4() kvm_gcsr_read(KVM_CSR_KS4) +#define kvm_write_gcsr_kscratch4(val) kvm_gcsr_write(val, KVM_CSR_KS4) +#define kvm_read_gcsr_kscratch5() kvm_gcsr_read(KVM_CSR_KS5) +#define kvm_write_gcsr_kscratch5(val) kvm_gcsr_write(val, KVM_CSR_KS5) +#define kvm_read_gcsr_kscratch6() kvm_gcsr_read(KVM_CSR_KS6) +#define kvm_write_gcsr_kscratch6(val) kvm_gcsr_write(val, KVM_CSR_KS6) +#define kvm_read_gcsr_kscratch7() kvm_gcsr_read(KVM_CSR_KS7) +#define kvm_write_gcsr_kscratch7(val) kvm_gcsr_write(val, KVM_CSR_KS7) + +#define kvm_read_gcsr_timerid() kvm_gcsr_read(KVM_CSR_TMID) +#define kvm_write_gcsr_timerid(val) kvm_gcsr_write(val, KVM_CSR_TMID) +#define kvm_read_gcsr_timercfg() kvm_gcsr_read(KVM_CSR_TCFG) +#define kvm_write_gcsr_timercfg(val) kvm_gcsr_write(val, KVM_CSR_TCFG) +#define kvm_read_gcsr_timertick() kvm_gcsr_read(KVM_CSR_TVAL) +#define kvm_write_gcsr_timertick(val) kvm_gcsr_write(val, KVM_CSR_TVAL) +#define kvm_read_gcsr_timeroffset() kvm_gcsr_read(KVM_CSR_CNTC) +#define kvm_write_gcsr_timeroffset(val) kvm_gcsr_write(val, KVM_CSR_CNTC) + +#define kvm_read_gcsr_llbctl() kvm_gcsr_read(KVM_CSR_LLBCTL) +#define kvm_write_gcsr_llbctl(val) kvm_gcsr_write(val, KVM_CSR_LLBCTL) + +#define kvm_read_gcsr_tlbrentry() kvm_gcsr_read(KVM_CSR_TLBRENTRY) +#define kvm_write_gcsr_tlbrentry(val) kvm_gcsr_write(val, KVM_CSR_TLBRENTRY) +#define kvm_read_gcsr_tlbrbadv() kvm_gcsr_read(KVM_CSR_TLBRBADV) +#define kvm_write_gcsr_tlbrbadv(val) kvm_gcsr_write(val, KVM_CSR_TLBRBADV) +#define kvm_read_gcsr_tlbrera() kvm_gcsr_read(KVM_CSR_TLBRERA) +#define kvm_write_gcsr_tlbrera(val) kvm_gcsr_write(val, KVM_CSR_TLBRERA) +#define kvm_read_gcsr_tlbrsave() kvm_gcsr_read(KVM_CSR_TLBRSAVE) +#define kvm_write_gcsr_tlbrsave(val) kvm_gcsr_write(val, KVM_CSR_TLBRSAVE) +#define kvm_read_gcsr_tlbrelo0() kvm_gcsr_read(KVM_CSR_TLBRELO0) +#define kvm_write_gcsr_tlbrelo0(val) kvm_gcsr_write(val, KVM_CSR_TLBRELO0) +#define kvm_read_gcsr_tlbrelo1() kvm_gcsr_read(KVM_CSR_TLBRELO1) +#define kvm_write_gcsr_tlbrelo1(val) kvm_gcsr_write(val, KVM_CSR_TLBRELO1) +#define kvm_read_gcsr_tlbrehi() kvm_gcsr_read(KVM_CSR_TLBREHI) +#define kvm_write_gcsr_tlbrehi(val) kvm_gcsr_write(val, KVM_CSR_TLBREHI) +#define kvm_read_gcsr_tlbrprmd() kvm_gcsr_read(KVM_CSR_TLBRPRMD) +#define kvm_write_gcsr_tlbrprmd(val) kvm_gcsr_write(val, KVM_CSR_TLBRPRMD) + +#define kvm_read_gcsr_directwin0() kvm_gcsr_read(KVM_CSR_DMWIN0) +#define kvm_write_gcsr_directwin0(val) kvm_gcsr_write(val, KVM_CSR_DMWIN0) +#define kvm_read_gcsr_directwin1() kvm_gcsr_read(KVM_CSR_DMWIN1) +#define kvm_write_gcsr_directwin1(val) kvm_gcsr_write(val, KVM_CSR_DMWIN1) +#define kvm_read_gcsr_directwin2() kvm_gcsr_read(KVM_CSR_DMWIN2) +#define kvm_write_gcsr_directwin2(val) kvm_gcsr_write(val, KVM_CSR_DMWIN2) +#define kvm_read_gcsr_directwin3() kvm_gcsr_read(KVM_CSR_DMWIN3) +#define kvm_write_gcsr_directwin3(val) kvm_gcsr_write(val, KVM_CSR_DMWIN3) + +#ifndef __ASSEMBLY__ + +static inline unsigned long +kvm_set_csr_gtlbc(unsigned long set) +{ + unsigned long res, new; + + res = kvm_read_csr_gtlbc(); + new = res | set; + kvm_write_csr_gtlbc(new); + + return res; +} + +static inline unsigned long +kvm_set_csr_euen(unsigned long set) +{ + unsigned long res, new; + + res = kvm_read_csr_euen(); + new = res | set; + kvm_write_csr_euen(new); + + return res; +} + +static inline unsigned long +kvm_set_csr_gintc(unsigned long set) +{ + unsigned long res, new; + + res = kvm_read_csr_gintc(); + new = res | set; + kvm_write_csr_gintc(new); + + return res; +} + +static inline unsigned long +kvm_set_gcsr_llbctl(unsigned long set) +{ + unsigned long res, new; + + res = kvm_read_gcsr_llbctl(); + new = res | set; + kvm_write_gcsr_llbctl(new); + + return res; +} + + +static inline unsigned long +kvm_clear_csr_gtlbc(unsigned long clear) +{ + unsigned long res, new; + + res = kvm_read_csr_gtlbc(); + new = res & ~clear; + kvm_write_csr_gtlbc(new); + + return res; +} + +static inline unsigned long +kvm_clear_csr_euen(unsigned long clear) +{ + unsigned long res, new; + + res = kvm_read_csr_euen(); + new = res & ~clear; + kvm_write_csr_euen(new); + + return res; +} + +static inline unsigned long +kvm_clear_csr_gintc(unsigned long clear) +{ + unsigned long res, new; + + res = kvm_read_csr_gintc(); + new = res & ~clear; + kvm_write_csr_gintc(new); + + return res; +} + +static inline unsigned long +kvm_change_csr_gstat(unsigned long change, unsigned long val) +{ + unsigned long res, new; + + res = read_csr_gstat(); + new = res & ~change; + new |= (val & change); + write_csr_gstat(new); + + return res; +} + +static inline unsigned long +kvm_change_csr_gcfg(unsigned long change, unsigned long val) +{ + unsigned long res, new; + + res = read_csr_gcfg(); + new = res & ~change; + new |= (val & change); + write_csr_gcfg(new); + + return res; +} + + +#define kvm_set_gcsr_estat(val) \ + kvm_gcsr_xchg(val, val, KVM_CSR_ESTAT) +#define kvm_clear_gcsr_estat(val) \ + kvm_gcsr_xchg(~(val), val, KVM_CSR_ESTAT) + +#endif + + +#if (_LOONGARCH_SZLONG == 32) +#define KVM_LONG_ADD add.w +#define KVM_LONG_ADDI addi.w +#define KVM_LONG_SUB sub.w +#define KVM_LONG_L ld.w +#define KVM_LONG_S st.w +#define KVM_LONG_SLL slli.w +#define KVM_LONG_SLLV sll.w +#define KVM_LONG_SRL srli.w +#define KVM_LONG_SRLV srl.w +#define KVM_LONG_SRA srai.w +#define KVM_LONG_SRAV sra.w + +#define KVM_LONGSIZE 4 +#define KVM_LONGMASK 3 +#define KVM_LONGLOG 2 + +/* + * How to add/sub/load/store/shift pointers. + */ + +#define KVM_PTR_ADD add.w +#define KVM_PTR_ADDI addi.w +#define KVM_PTR_SUB sub.w +#define KVM_PTR_L ld.w +#define KVM_PTR_S st.w +#define KVM_PTR_LI li.w +#define KVM_PTR_SLL slli.w +#define KVM_PTR_SLLV sll.w +#define KVM_PTR_SRL srli.w +#define KVM_PTR_SRLV srl.w +#define KVM_PTR_SRA srai.w +#define KVM_PTR_SRAV sra.w + +#define KVM_PTR_SCALESHIFT 2 + +#define KVM_PTRSIZE 4 +#define KVM_PTRLOG 2 + +#endif + +#if (_LOONGARCH_SZLONG == 64) +#define KVM_LONG_ADD add.d +#define KVM_LONG_ADDI addi.d +#define KVM_LONG_SUB sub.d +#define KVM_LONG_L ld.d +#define KVM_LONG_S st.d +#define KVM_LONG_SLL slli.d +#define KVM_LONG_SLLV sll.d +#define KVM_LONG_SRL srli.d +#define KVM_LONG_SRLV srl.d +#define KVM_LONG_SRA sra.w +#define KVM_LONG_SRAV sra.d + +#define KVM_LONGSIZE 8 +#define KVM_LONGMASK 7 +#define KVM_LONGLOG 3 + +/* + * How to add/sub/load/store/shift pointers. + */ + +#define KVM_PTR_ADD add.d +#define KVM_PTR_ADDI addi.d +#define KVM_PTR_SUB sub.d +#define KVM_PTR_L ld.d +#define KVM_PTR_S st.d +#define KVM_PTR_LI li.d +#define KVM_PTR_SLL slli.d +#define KVM_PTR_SLLV sll.d +#define KVM_PTR_SRL srli.d +#define KVM_PTR_SRLV srl.d +#define KVM_PTR_SRA srai.d +#define KVM_PTR_SRAV sra.d + +#define KVM_PTR_SCALESHIFT 3 + +#define KVM_PTRSIZE 8 +#define KVM_PTRLOG 3 +#endif + +#endif /* __LOONGARCH_KVM_COMPAT_H__ */ diff --git a/arch/loongarch/kvm/kvmcpu.h b/arch/loongarch/kvm/kvmcpu.h new file mode 100644 index 000000000000..0962becea1b5 --- /dev/null +++ b/arch/loongarch/kvm/kvmcpu.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#ifndef __ASM_LOONGARCH_KVMCPU_H__ +#define __ASM_LOONGARCH_KVMCPU_H__ + +#include +#include + +#define LARCH_INT_SIP0 0 +#define LARCH_INT_SIP1 1 +#define LARCH_INT_IP0 2 +#define LARCH_INT_IP1 3 +#define LARCH_INT_IP2 4 +#define LARCH_INT_IP3 5 +#define LARCH_INT_IP4 6 +#define LARCH_INT_IP5 7 +#define LARCH_INT_IP6 8 +#define LARCH_INT_IP7 9 +#define LARCH_INT_PMU 10 +#define LARCH_INT_TIMER 11 +#define LARCH_INT_IPI 12 +#define LOONGARCH_EXC_MAX (LARCH_INT_IPI + 1) +#define LOONGARCH_EXC_IPNUM (LOONGARCH_EXC_MAX) + +/* Controlled by 0x5 guest exst */ +#define CPU_SIP0 (_ULCAST_(1)) +#define CPU_SIP1 (_ULCAST_(1) << 1) +#define CPU_PMU (_ULCAST_(1) << 10) +#define CPU_TIMER (_ULCAST_(1) << 11) +#define CPU_IPI (_ULCAST_(1) << 12) + +/* Controlled by 0x52 guest exception VIP + * aligned to exst bit 5~12 + */ +#define CPU_IP0 (_ULCAST_(1)) +#define CPU_IP1 (_ULCAST_(1) << 1) +#define CPU_IP2 (_ULCAST_(1) << 2) +#define CPU_IP3 (_ULCAST_(1) << 3) +#define CPU_IP4 (_ULCAST_(1) << 4) +#define CPU_IP5 (_ULCAST_(1) << 5) +#define CPU_IP6 (_ULCAST_(1) << 6) +#define CPU_IP7 (_ULCAST_(1) << 7) + +#define MNSEC_PER_SEC (NSEC_PER_SEC >> 20) + +/* KVM_IRQ_LINE irq field index values */ +#define KVM_LOONGSON_IRQ_TYPE_SHIFT 24 +#define KVM_LOONGSON_IRQ_TYPE_MASK 0xff +#define KVM_LOONGSON_IRQ_VCPU_SHIFT 16 +#define KVM_LOONGSON_IRQ_VCPU_MASK 0xff +#define KVM_LOONGSON_IRQ_NUM_SHIFT 0 +#define KVM_LOONGSON_IRQ_NUM_MASK 0xffff + +/* irq_type field */ +#define KVM_LOONGSON_IRQ_TYPE_CPU_IP 0 +#define KVM_LOONGSON_IRQ_TYPE_CPU_IO 1 +#define KVM_LOONGSON_IRQ_TYPE_HT 2 +#define KVM_LOONGSON_IRQ_TYPE_MSI 3 +#define KVM_LOONGSON_IRQ_TYPE_IOAPIC 4 +#define KVM_LOONGSON_IRQ_TYPE_ROUTE 5 + +/* out-of-kernel GIC cpu interrupt injection irq_number field */ +#define KVM_LOONGSON_IRQ_CPU_IRQ 0 +#define KVM_LOONGSON_IRQ_CPU_FIQ 1 +#define KVM_LOONGSON_CPU_IP_NUM 8 + +typedef union loongarch_instruction larch_inst; +typedef int (*exit_handle_fn)(struct kvm_vcpu *); + +int _kvm_emu_mmio_write(struct kvm_vcpu *vcpu, larch_inst inst); +int _kvm_emu_mmio_read(struct kvm_vcpu *vcpu, larch_inst inst); +int _kvm_complete_mmio_read(struct kvm_vcpu *vcpu, struct kvm_run *run); +int _kvm_complete_iocsr_read(struct kvm_vcpu *vcpu, struct kvm_run *run); +int _kvm_emu_idle(struct kvm_vcpu *vcpu); +int _kvm_handle_pv_hcall(struct kvm_vcpu *vcpu); +int _kvm_pending_timer(struct kvm_vcpu *vcpu); +int _kvm_handle_fault(struct kvm_vcpu *vcpu, int fault); +void _kvm_deliver_intr(struct kvm_vcpu *vcpu); +void irqchip_debug_init(struct kvm *kvm); +void irqchip_debug_destroy(struct kvm *kvm); + +void kvm_own_fpu(struct kvm_vcpu *vcpu); +void kvm_own_lsx(struct kvm_vcpu *vcpu); +void kvm_lose_fpu(struct kvm_vcpu *vcpu); +void kvm_own_lasx(struct kvm_vcpu *vcpu); +void kvm_save_fpu(struct kvm_vcpu *cpu); +void kvm_restore_fpu(struct kvm_vcpu *cpu); +void kvm_restore_fcsr(struct kvm_vcpu *cpu); +void kvm_save_lsx(struct kvm_vcpu *cpu); +void kvm_restore_lsx(struct kvm_vcpu *cpu); +void kvm_restore_lsx_upper(struct kvm_vcpu *cpu); +void kvm_save_lasx(struct kvm_vcpu *cpu); +void kvm_restore_lasx(struct kvm_vcpu *cpu); +void kvm_restore_lasx_upper(struct kvm_vcpu *cpu); + +void kvm_lose_hw_perf(struct kvm_vcpu *vcpu); +void kvm_restore_hw_perf(struct kvm_vcpu *vcpu); + +void kvm_acquire_timer(struct kvm_vcpu *vcpu); +void kvm_reset_timer(struct kvm_vcpu *vcpu); +enum hrtimer_restart kvm_count_timeout(struct kvm_vcpu *vcpu); +void kvm_init_timer(struct kvm_vcpu *vcpu, unsigned long hz); +void kvm_restore_timer(struct kvm_vcpu *vcpu); +void kvm_save_timer(struct kvm_vcpu *vcpu); + +/* + * Loongarch KVM guest interrupt handling. + */ +static inline void _kvm_queue_irq(struct kvm_vcpu *vcpu, unsigned int irq) +{ + set_bit(irq, &vcpu->arch.irq_pending); + clear_bit(irq, &vcpu->arch.irq_clear); +} + +static inline void _kvm_dequeue_irq(struct kvm_vcpu *vcpu, unsigned int irq) +{ + clear_bit(irq, &vcpu->arch.irq_pending); + set_bit(irq, &vcpu->arch.irq_clear); +} + +#endif /* __ASM_LOONGARCH_KVMCPU_H__ */ diff --git a/arch/loongarch/kvm/kvmcsr.h b/arch/loongarch/kvm/kvmcsr.h new file mode 100644 index 000000000000..303303a8a245 --- /dev/null +++ b/arch/loongarch/kvm/kvmcsr.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#ifndef __LOONGARCH_KVM_CSR_H__ +#define __LOONGARCH_KVM_CSR_H__ +#include +#include "kvmcpu.h" +#include +#include +#include + +#define kvm_read_hw_gcsr(id) kvm_gcsr_read(id) +#define kvm_write_hw_gcsr(csr, id, val) kvm_gcsr_write(val, id) + +int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *v, int force); +int _kvm_setcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *v, int force); +unsigned long _kvm_emu_read_csr(struct kvm_vcpu *vcpu, int csrid); +void _kvm_emu_write_csr(struct kvm_vcpu *vcpu, int csrid, unsigned long val); +void _kvm_emu_xchg_csr(struct kvm_vcpu *vcpu, int csrid, + unsigned long csr_mask, unsigned long val); +int _kvm_emu_iocsr(larch_inst inst, struct kvm_run *run, struct kvm_vcpu *vcpu); + +static inline void kvm_save_hw_gcsr(struct loongarch_csrs *csr, u32 gid) +{ + csr->csrs[gid] = kvm_gcsr_read(gid); +} + +static inline void kvm_restore_hw_gcsr(struct loongarch_csrs *csr, u32 gid) +{ + kvm_gcsr_write(csr->csrs[gid], gid); +} + +static inline unsigned long kvm_read_sw_gcsr(struct loongarch_csrs *csr, u32 gid) +{ + return csr->csrs[gid]; +} + +static inline void kvm_write_sw_gcsr(struct loongarch_csrs *csr, u32 gid, unsigned long val) +{ + csr->csrs[gid] = val; +} + +static inline void kvm_set_sw_gcsr(struct loongarch_csrs *csr, u32 gid, unsigned long val) +{ + csr->csrs[gid] |= val; +} + +static inline void kvm_change_sw_gcsr(struct loongarch_csrs *csr, u32 gid, unsigned mask, + unsigned long val) +{ + unsigned long _mask = mask; + csr->csrs[gid] &= ~_mask; + csr->csrs[gid] |= val & _mask; +} + + +#define GET_HW_GCSR(id, csrid, v) \ + do { \ + if (csrid == id) { \ + *v = (long)kvm_read_hw_gcsr(csrid); \ + return 0; \ + } \ + } while (0) + +#define GET_SW_GCSR(csr, id, csrid, v) \ + do { \ + if (csrid == id) { \ + *v = kvm_read_sw_gcsr(csr, id); \ + return 0; \ + } \ + } while (0) + +#define SET_HW_GCSR(csr, id, csrid, v) \ + do { \ + if (csrid == id) { \ + kvm_write_hw_gcsr(csr, csrid, *v); \ + return 0; \ + } \ + } while (0) + +#define SET_SW_GCSR(csr, id, csrid, v) \ + do { \ + if (csrid == id) { \ + kvm_write_sw_gcsr(csr, csrid, *v); \ + return 0; \ + } \ + } while (0) + +int _kvm_init_iocsr(struct kvm *kvm); +int _kvm_set_iocsr(struct kvm *kvm, struct kvm_iocsr_entry *__user argp); +int _kvm_get_iocsr(struct kvm *kvm, struct kvm_iocsr_entry *__user argp); + +#define KVM_PMU_PLV_ENABLE (CSR_PERFCTRL_PLV0 | \ + CSR_PERFCTRL_PLV1 | \ + CSR_PERFCTRL_PLV2 | \ + CSR_PERFCTRL_PLV3) + +#define CASE_WRITE_HW_PMU(vcpu, csr, id, csrid, v) \ + do { \ + if (csrid == id) { \ + if (v & KVM_PMU_PLV_ENABLE) { \ + kvm_write_csr_gcfg(kvm_read_csr_gcfg() | CSR_GCFG_GPERF); \ + kvm_write_hw_gcsr(csr, csrid, v | CSR_PERFCTRL_GMOD); \ + vcpu->arch.aux_inuse |= KVM_LARCH_PERF; \ + return ; \ + } else { \ + kvm_write_sw_gcsr(csr, csrid, v); \ + return; \ + } \ + } \ + } while (0) + +#endif /* __LOONGARCH_KVM_CSR_H__ */ diff --git a/arch/loongarch/kvm/loongarch.c b/arch/loongarch/kvm/loongarch.c new file mode 100644 index 000000000000..2268176f5cc6 --- /dev/null +++ b/arch/loongarch/kvm/loongarch.c @@ -0,0 +1,2215 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kvmcpu.h" +#include +#include +#include + +#include "intc/ls3a_ipi.h" +#include "intc/ls7a_irq.h" +#include "intc/ls3a_ext_irq.h" +#include "kvm_compat.h" +#include "kvmcsr.h" + +/* + * Define loongarch kvm version. + * Add version number when qemu/kvm interface changed + */ +#define KVM_LOONGARCH_VERSION 1 +#define CREATE_TRACE_POINTS +#include "trace.h" +#define VCPU_STAT(x) offsetof(struct kvm_vcpu, stat.x) +#define VM_STAT(x) offsetof(struct kvm, stat.x) +struct kvm_stats_debugfs_item vcpu_debugfs_entries[] = { + { "idle", VCPU_STAT(idle_exits), KVM_STAT_VCPU}, + { "signal", VCPU_STAT(signal_exits), KVM_STAT_VCPU}, + { "interrupt", VCPU_STAT(int_exits), KVM_STAT_VCPU}, + { "tlbmiss_ld", VCPU_STAT(excep_exits[EXCCODE_TLBL]), KVM_STAT_VCPU}, + { "tlbmiss_st", VCPU_STAT(excep_exits[EXCCODE_TLBS]), KVM_STAT_VCPU}, + { "tlb_ifetch", VCPU_STAT(excep_exits[EXCCODE_TLBI]), KVM_STAT_VCPU}, + { "tlbmod", VCPU_STAT(excep_exits[EXCCODE_TLBM]), KVM_STAT_VCPU}, + { "tlbri", VCPU_STAT(excep_exits[EXCCODE_TLBRI]), KVM_STAT_VCPU}, + { "tlbxi", VCPU_STAT(excep_exits[EXCCODE_TLBXI]), KVM_STAT_VCPU}, + { "fp_dis", VCPU_STAT(excep_exits[EXCCODE_FPDIS]), KVM_STAT_VCPU}, + { "lsx_dis", VCPU_STAT(excep_exits[EXCCODE_LSXDIS]), KVM_STAT_VCPU}, + { "lasx_dis", VCPU_STAT(excep_exits[EXCCODE_LASXDIS]), KVM_STAT_VCPU}, + { "fpe", VCPU_STAT(excep_exits[EXCCODE_FPE]), KVM_STAT_VCPU}, + { "watch", VCPU_STAT(excep_exits[EXCCODE_WATCH]), KVM_STAT_VCPU}, + { "gspr", VCPU_STAT(excep_exits[EXCCODE_GSPR]), KVM_STAT_VCPU}, + { "gcm", VCPU_STAT(excep_exits[EXCCODE_GCM]), KVM_STAT_VCPU}, + { "hc", VCPU_STAT(excep_exits[EXCCODE_HYP]), KVM_STAT_VCPU}, + + { "rdcsr_cpu_feature", VCPU_STAT(rdcsr_cpu_feature_exits), KVM_STAT_VCPU }, + { "rdcsr_misc_func", VCPU_STAT(rdcsr_misc_func_exits), KVM_STAT_VCPU }, + { "rdcsr_ipi_access", VCPU_STAT(rdcsr_ipi_access_exits), KVM_STAT_VCPU }, + { "cpucfg", VCPU_STAT(cpucfg_exits), KVM_STAT_VCPU }, + { "huge_dec", VCPU_STAT(huge_dec_exits), KVM_STAT_VCPU }, + { "huge_thp", VCPU_STAT(huge_thp_exits), KVM_STAT_VCPU }, + { "huge_adj", VCPU_STAT(huge_adjust_exits), KVM_STAT_VCPU }, + { "huge_set", VCPU_STAT(huge_set_exits), KVM_STAT_VCPU }, + { "huge_merg", VCPU_STAT(huge_merge_exits), KVM_STAT_VCPU }, + + { "halt_successful_poll", VCPU_STAT(halt_successful_poll), KVM_STAT_VCPU }, + { "halt_attempted_poll", VCPU_STAT(halt_attempted_poll), KVM_STAT_VCPU }, + { "halt_poll_invalid", VCPU_STAT(halt_poll_invalid), KVM_STAT_VCPU }, + { "halt_wakeup", VCPU_STAT(halt_wakeup), KVM_STAT_VCPU }, + {NULL} +}; + +struct kvm_stats_debugfs_item debugfs_entries[] = { + { "remote_tlb_flush", VM_STAT(remote_tlb_flush), KVM_STAT_VM }, + { "pip_read_exits", VM_STAT(pip_read_exits), KVM_STAT_VM }, + { "pip_write_exits", VM_STAT(pip_write_exits), KVM_STAT_VM }, + { "vm_ioctl_irq_line", VM_STAT(vm_ioctl_irq_line), KVM_STAT_VM }, + { "ls7a_ioapic_update", VM_STAT(ls7a_ioapic_update), KVM_STAT_VM }, + { "ls7a_ioapic_set_irq", VM_STAT(ls7a_ioapic_set_irq), KVM_STAT_VM }, + { "ls7a_msi_irq", VM_STAT(ls7a_msi_irq), KVM_STAT_VM }, + { "ioapic_reg_write", VM_STAT(ioapic_reg_write), KVM_STAT_VM }, + { "ioapic_reg_read", VM_STAT(ioapic_reg_read), KVM_STAT_VM }, + { "set_ls7a_ioapic", VM_STAT(set_ls7a_ioapic), KVM_STAT_VM }, + { "get_ls7a_ioapic", VM_STAT(get_ls7a_ioapic), KVM_STAT_VM }, + { "set_ls3a_ext_irq", VM_STAT(set_ls3a_ext_irq), KVM_STAT_VM }, + { "get_ls3a_ext_irq", VM_STAT(get_ls3a_ext_irq), KVM_STAT_VM }, + { "ls3a_ext_irq", VM_STAT(trigger_ls3a_ext_irq), KVM_STAT_VM }, + {NULL} +}; + +bool kvm_trace_guest_mode_change; +static struct kvm_context __percpu *vmcs; + +int kvm_guest_mode_change_trace_reg(void) +{ + kvm_trace_guest_mode_change = 1; + return 0; +} + +void kvm_guest_mode_change_trace_unreg(void) +{ + kvm_trace_guest_mode_change = 0; +} + +/* + * XXXKYMA: We are simulatoring a processor that has the WII bit set in + * Config7, so we are "runnable" if interrupts are pending + */ +int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu) +{ + return !!(vcpu->arch.irq_pending); +} + +bool kvm_arch_vcpu_in_kernel(struct kvm_vcpu *vcpu) +{ + return false; +} + +int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) +{ + return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE; +} + +#ifdef CONFIG_PARAVIRT +void kvm_update_stolen_time(struct kvm_vcpu *vcpu) +{ + struct kvm_host_map map; + struct kvm_steal_time *st; + int ret = 0; + + if (vcpu->arch.st.guest_addr == 0) + return; + + ret = kvm_map_gfn(vcpu, vcpu->arch.st.guest_addr >> PAGE_SHIFT, + &map, &vcpu->arch.st.cache, false); + if (ret) { + kvm_info("%s ret:%d\n", __func__, ret); + return; + } + st = map.hva + offset_in_page(vcpu->arch.st.guest_addr); + if (st->version & 1) + st->version += 1; /* first time write, random junk */ + st->version += 1; + smp_wmb(); + st->steal += current->sched_info.run_delay - + vcpu->arch.st.last_steal; + vcpu->arch.st.last_steal = current->sched_info.run_delay; + smp_wmb(); + st->version += 1; + + kvm_unmap_gfn(vcpu, &map, &vcpu->arch.st.cache, true, false); +} + +bool _kvm_pvtime_supported(void) +{ + return !!sched_info_on(); +} + +int _kvm_pvtime_set_attr(struct kvm_vcpu *vcpu, + struct kvm_device_attr *attr) +{ + u64 __user *user = (u64 __user *)attr->addr; + struct kvm *kvm = vcpu->kvm; + u64 ipa; + int ret = 0; + int idx; + + if (!_kvm_pvtime_supported() || + attr->attr != KVM_LARCH_VCPU_PVTIME_IPA) + return -ENXIO; + + if (get_user(ipa, user)) + return -EFAULT; + if (!IS_ALIGNED(ipa, 64)) + return -EINVAL; + + /* Check the address is in a valid memslot */ + idx = srcu_read_lock(&kvm->srcu); + if (kvm_is_error_hva(gfn_to_hva(kvm, ipa >> PAGE_SHIFT))) + ret = -EINVAL; + srcu_read_unlock(&kvm->srcu, idx); + + if (!ret) + vcpu->arch.st.guest_addr = ipa; + + return ret; +} + +int _kvm_pvtime_get_attr(struct kvm_vcpu *vcpu, + struct kvm_device_attr *attr) +{ + u64 __user *user = (u64 __user *)attr->addr; + u64 ipa; + + if (!_kvm_pvtime_supported() || + attr->attr != KVM_LARCH_VCPU_PVTIME_IPA) + return -ENXIO; + + ipa = vcpu->arch.st.guest_addr; + + if (put_user(ipa, user)) + return -EFAULT; + + return 0; +} + +int _kvm_pvtime_has_attr(struct kvm_vcpu *vcpu, + struct kvm_device_attr *attr) +{ + switch (attr->attr) { + case KVM_LARCH_VCPU_PVTIME_IPA: + if (_kvm_pvtime_supported()) + return 0; + } + + return -ENXIO; +} +#endif + +int kvm_arch_hardware_enable(void) +{ + unsigned long gcfg = 0; + + /* First init gtlbc, gcfg, gstat, gintc. All guest use the same config */ + kvm_clear_csr_gtlbc(KVM_GTLBC_USETGID | KVM_GTLBC_TOTI); + kvm_write_csr_gcfg(0); + kvm_write_csr_gstat(0); + kvm_write_csr_gintc(0); + + /* + * Enable virtualization features granting guest direct control of + * certain features: + * GCI=2: Trap on init or unimplement cache instruction. + * TORU=0: Trap on Root Unimplement. + * CACTRL=1: Root control cache. + * TOP=0: Trap on Previlege. + * TOE=0: Trap on Exception. + * TIT=0: Trap on Timer. + */ + if (cpu_has_gcip_all) + gcfg |= KVM_GCFG_GCI_SECURE; + if (cpu_has_matc_root) + gcfg |= KVM_GCFG_MATC_ROOT; + + gcfg |= KVM_GCFG_TIT; + kvm_write_csr_gcfg(gcfg); + + kvm_flush_tlb_all(); + + /* Enable using TGID */ + kvm_set_csr_gtlbc(KVM_GTLBC_USETGID); + kvm_debug("gtlbc:%llx gintc:%llx gstat:%llx gcfg:%llx", + kvm_read_csr_gtlbc(), kvm_read_csr_gintc(), + kvm_read_csr_gstat(), kvm_read_csr_gcfg()); + return 0; +} + +void kvm_arch_hardware_disable(void) +{ + kvm_clear_csr_gtlbc(KVM_GTLBC_USETGID | KVM_GTLBC_TOTI); + kvm_write_csr_gcfg(0); + kvm_write_csr_gstat(0); + kvm_write_csr_gintc(0); + + /* Flush any remaining guest TLB entries */ + kvm_flush_tlb_all(); +} + +int kvm_arch_hardware_setup(void) +{ + return 0; +} + +int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) +{ + /* Allocate page table to map GPA -> RPA */ + kvm->arch.gpa_mm.pgd = kvm_pgd_alloc(); + if (!kvm->arch.gpa_mm.pgd) + return -ENOMEM; + + kvm->arch.cpucfg_lasx = (read_cpucfg(LOONGARCH_CPUCFG2) & + CPUCFG2_LASX); + + _kvm_init_iocsr(kvm); + kvm->arch.vmcs = vmcs; + + return 0; +} + +static int lvcpu_stat_get(void *address, u64 *val) +{ + *val = *(u64 *)address; + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(lvcpu_stat_fops, lvcpu_stat_get, NULL, "%llu\n"); + +static int vcpu_pid_get(void *arg, u64 *val) +{ + struct kvm_vcpu *vcpu = (struct kvm_vcpu *)arg; + if (vcpu) + *val = pid_vnr(vcpu->pid); + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(vcpu_pid_fops, vcpu_pid_get, NULL, "%llu\n"); + +bool kvm_arch_has_vcpu_debugfs(void) +{ + return true; +} + +int kvm_arch_create_vcpu_debugfs(struct kvm_vcpu *vcpu) +{ + struct kvm_stats_debugfs_item *p; + debugfs_create_file("pid", 0444, vcpu->debugfs_dentry, vcpu, &vcpu_pid_fops); + for (p = vcpu_debugfs_entries; p->name && p->kind == KVM_STAT_VCPU; ++p) { + debugfs_create_file(p->name, 0444, vcpu->debugfs_dentry, + (void *)vcpu + p->offset, &lvcpu_stat_fops); + } + + return 0; +} + +static void kvm_free_vcpus(struct kvm *kvm) +{ + unsigned int i; + struct kvm_vcpu *vcpu; + + kvm_for_each_vcpu(i, vcpu, kvm) { + kvm_arch_vcpu_free(vcpu); + } + + mutex_lock(&kvm->lock); + + for (i = 0; i < atomic_read(&kvm->online_vcpus); i++) + kvm->vcpus[i] = NULL; + + atomic_set(&kvm->online_vcpus, 0); + + mutex_unlock(&kvm->lock); +} + +void kvm_arch_destroy_vm(struct kvm *kvm) +{ + kvm_destroy_ls3a_ipi(kvm); + kvm_destroy_ls7a_ioapic(kvm); + kvm_destroy_ls3a_ext_irq(kvm); + kvm_free_vcpus(kvm); + _kvm_destroy_mm(kvm); +} + +long kvm_arch_dev_ioctl(struct file *filp, unsigned int ioctl, + unsigned long arg) +{ + return -ENOIOCTLCMD; +} + +int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot, + unsigned long npages) +{ + return 0; +} + +int kvm_arch_prepare_memory_region(struct kvm *kvm, + struct kvm_memory_slot *memslot, + const struct kvm_userspace_memory_region *mem, + enum kvm_mr_change change) +{ + return 0; +} + +static void _kvm_new_vpid(unsigned long cpu, struct kvm_vcpu *vcpu) +{ + struct kvm_context *context; + unsigned long vpid; + + context = per_cpu_ptr(vcpu->kvm->arch.vmcs, cpu); + vpid = context->vpid_cache; + if (!(++vpid & context->gid_mask)) { + if (!vpid) /* fix version if needed */ + vpid = context->gid_fisrt_ver; + + ++vpid; /* vpid 0 reserved for root */ + + /* start new vpid cycle */ + kvm_flush_tlb_all(); + } + + context->vpid_cache = vpid; + vcpu->arch.vpid[cpu] = vpid; +} + +/* Returns 1 if the guest TLB may be clobbered */ +static int _kvm_check_requests(struct kvm_vcpu *vcpu, int cpu) +{ + int ret = 0; + int i; + + if (!kvm_request_pending(vcpu)) + return 0; + + if (kvm_check_request(KVM_REQ_TLB_FLUSH, vcpu)) { + /* Drop all vpids for this VCPU */ + for_each_possible_cpu(i) + vcpu->arch.vpid[i] = 0; + /* This will clobber guest TLB contents too */ + ret = 1; + } + + return ret; +} + +static void _kvm_update_vmid(struct kvm_vcpu *vcpu, int cpu) +{ + struct kvm_context *context; + bool migrated; + unsigned int gstinfo_gidmask, gstinfo_gid = 0; + + /* + * Are we entering guest context on a different CPU to last time? + * If so, the VCPU's guest TLB state on this CPU may be stale. + */ + context = per_cpu_ptr(vcpu->kvm->arch.vmcs, cpu); + migrated = (vcpu->arch.last_exec_cpu != cpu); + vcpu->arch.last_exec_cpu = cpu; + + /* + * Check if our vpid is of an older version and thus invalid. + * + * We also discard the stored vpid if we've executed on + * another CPU, as the guest mappings may have changed without + * hypervisor knowledge. + */ + gstinfo_gidmask = context->gid_mask << KVM_GSTAT_GID_SHIFT; + if (migrated || + (vcpu->arch.vpid[cpu] ^ context->vpid_cache) & + context->gid_ver_mask) { + _kvm_new_vpid(cpu, vcpu); + trace_kvm_vpid_change(vcpu, vcpu->arch.vpid[cpu]); + } + gstinfo_gid = (vcpu->arch.vpid[cpu] & context->gid_mask) << + KVM_GSTAT_GID_SHIFT; + + /* Restore GSTAT(0x50).vpid */ + kvm_change_csr_gstat(gstinfo_gidmask, gstinfo_gid); +} + +/* + * Return value is in the form (errcode<<2 | RESUME_FLAG_HOST | RESUME_FLAG_NV) + */ +static int _kvm_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu) +{ + unsigned long exst = vcpu->arch.host_estat; + u32 intr = exst & 0x1fff; /* ignore NMI */ + u32 exccode = (exst & KVM_ESTAT_EXC) >> KVM_ESTAT_EXC_SHIFT; + u32 __user *opc = (u32 __user *) vcpu->arch.pc; + int ret = RESUME_GUEST, cpu; + + vcpu->mode = OUTSIDE_GUEST_MODE; + + /* Set a default exit reason */ + run->exit_reason = KVM_EXIT_UNKNOWN; + run->ready_for_interrupt_injection = 1; + + /* + * Set the appropriate status bits based on host CPU features, + * before we hit the scheduler + */ + + local_irq_enable(); + + kvm_debug("%s: exst: %lx, PC: %p, kvm_run: %p, kvm_vcpu: %p\n", + __func__, exst, opc, run, vcpu); + trace_kvm_exit(vcpu, exccode); + if (exccode) { + vcpu->stat.excep_exits[exccode]++; + ret = _kvm_handle_fault(vcpu, exccode); + } else { + WARN(!intr, "suspicious vm exiting"); + ++vcpu->stat.int_exits; + + if (need_resched()) + cond_resched(); + + ret = RESUME_GUEST; + } + +#ifdef CONFIG_PARAVIRT + if (kvm_check_request(KVM_REQ_RECORD_STEAL, vcpu)) + kvm_update_stolen_time(vcpu); +#endif + + cond_resched(); + + local_irq_disable(); + + if (ret == RESUME_GUEST) + kvm_acquire_timer(vcpu); + + if (!(ret & RESUME_HOST)) { + _kvm_deliver_intr(vcpu); + /* Only check for signals if not already exiting to userspace */ + if (signal_pending(current)) { + run->exit_reason = KVM_EXIT_INTR; + ret = (-EINTR << 2) | RESUME_HOST; + ++vcpu->stat.signal_exits; + trace_kvm_exit(vcpu, KVM_TRACE_EXIT_SIGNAL); + } + } + + if (ret == RESUME_GUEST) { + trace_kvm_reenter(vcpu); + + /* + * Make sure the read of VCPU requests in vcpu_reenter() + * callback is not reordered ahead of the write to vcpu->mode, + * or we could miss a TLB flush request while the requester sees + * the VCPU as outside of guest mode and not needing an IPI. + */ + smp_store_mb(vcpu->mode, IN_GUEST_MODE); + + cpu = smp_processor_id(); + _kvm_check_requests(vcpu, cpu); + _kvm_update_vmid(vcpu, cpu); + + /* + * If FPU / LSX are enabled (i.e. the guest's FPU / LSX context + * is live), restore FCSR0. + */ + if (_kvm_guest_has_fpu(&vcpu->arch) && + kvm_read_csr_euen() & (KVM_EUEN_FPEN | KVM_EUEN_LSXEN)) { + kvm_restore_fcsr(vcpu); + } + } + + return ret; +} + +struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id) +{ + int err; + struct kvm_vcpu *vcpu = kzalloc(sizeof(struct kvm_vcpu), GFP_KERNEL); + + if (!vcpu) { + err = -ENOMEM; + goto out; + } + + err = kvm_vcpu_init(vcpu, kvm, id); + + if (err) + goto out_free_cpu; + + kvm->arch.online_vcpus = id + 1; + + vcpu->arch.host_eentry = kvm_csr_readq(KVM_CSR_EENTRY); + vcpu->arch.guest_eentry = (unsigned long)kvm_exception_entry; + vcpu->arch.vcpu_run = kvm_enter_guest; + vcpu->arch.handle_exit = _kvm_handle_exit; + vcpu->arch.csr = kzalloc(sizeof(struct loongarch_csrs), GFP_KERNEL); + /* + * kvm all exceptions share one exception entry, and host <-> guest switch + * also switch excfg.VS field, keep host excfg.VS info here + */ + vcpu->arch.host_ecfg = (kvm_read_csr_ecfg() & KVM_ECFG_VS); + + if (!vcpu->arch.csr) { + err = -ENOMEM; + goto out_uninit_cpu; + } + + /* Init */ + vcpu->arch.last_sched_cpu = -1; + vcpu->arch.last_exec_cpu = -1; + + return vcpu; + +out_uninit_cpu: + kvm_vcpu_uninit(vcpu); + +out_free_cpu: + kfree(vcpu); + +out: + return ERR_PTR(err); +} + +void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu) +{ + struct gfn_to_pfn_cache *cache = &vcpu->arch.st.cache; + + hrtimer_cancel(&vcpu->arch.swtimer); + + kvm_vcpu_uninit(vcpu); + kvm_mmu_free_memory_caches(vcpu); + kvm_release_pfn(cache->pfn, cache->dirty, cache); + kfree(vcpu->arch.csr); + kfree(vcpu); +} + +void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) +{ + kvm_arch_vcpu_free(vcpu); +} +#define KVM_GUESTDBG_VALID_MASK (KVM_GUESTDBG_ENABLE | \ + KVM_GUESTDBG_USE_SW_BP | KVM_GUESTDBG_SINGLESTEP) +int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, + struct kvm_guest_debug *dbg) +{ + int ret = 0; + + if (dbg->control & ~KVM_GUESTDBG_VALID_MASK) { + ret = -EINVAL; + goto out; + } + if (dbg->control & KVM_GUESTDBG_ENABLE) { + vcpu->guest_debug = dbg->control; + /* No hardware breakpoint */ + } else { + vcpu->guest_debug = 0; + } +out: + return ret; +} + +int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + int r = -EINTR; + int cpu; + + vcpu_load(vcpu); + + kvm_sigset_activate(vcpu); + + if (vcpu->mmio_needed) { + if (!vcpu->mmio_is_write) + _kvm_complete_mmio_read(vcpu, run); + vcpu->mmio_needed = 0; + } else if (vcpu->arch.is_hypcall) { + /* set return value for hypercall v0 register */ + vcpu->arch.gprs[REG_V0] = run->hypercall.ret; + vcpu->arch.is_hypcall = 0; + } + + if (run->exit_reason == KVM_EXIT_LOONGARCH_IOCSR) { + if (!run->iocsr_io.is_write) + _kvm_complete_iocsr_read(vcpu, run); + } + + /* clear exit_reason */ + run->exit_reason = KVM_EXIT_UNKNOWN; + if (run->immediate_exit) + goto out; + + lose_fpu(1); + +#ifdef CONFIG_PARAVIRT + if (kvm_check_request(KVM_REQ_RECORD_STEAL, vcpu)) + kvm_update_stolen_time(vcpu); +#endif + local_irq_disable(); + guest_enter_irqoff(); + trace_kvm_enter(vcpu); + + /* + * Make sure the read of VCPU requests in vcpu_run() callback is not + * reordered ahead of the write to vcpu->mode, or we could miss a TLB + * flush request while the requester sees the VCPU as outside of guest + * mode and not needing an IPI. + */ + smp_store_mb(vcpu->mode, IN_GUEST_MODE); + + cpu = smp_processor_id(); + kvm_acquire_timer(vcpu); + /* Check if we have any exceptions/interrupts pending */ + _kvm_deliver_intr(vcpu); + + _kvm_check_requests(vcpu, cpu); + _kvm_update_vmid(vcpu, cpu); + r = kvm_enter_guest(run, vcpu); + + trace_kvm_out(vcpu); + guest_exit_irqoff(); + local_irq_enable(); + +out: + kvm_sigset_deactivate(vcpu); + + vcpu_put(vcpu); + return r; +} + +int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, + struct kvm_loongarch_interrupt *irq) +{ + int intr = (int)irq->irq; + + if (intr < 0) { + _kvm_dequeue_irq(vcpu, -intr); + return 0; + } + + _kvm_queue_irq(vcpu, intr); + kvm_vcpu_kick(vcpu); + return 0; +} + +int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu, + struct kvm_mp_state *mp_state) +{ + return -ENOIOCTLCMD; +} + +int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, + struct kvm_mp_state *mp_state) +{ + return -ENOIOCTLCMD; +} + +/** + * kvm_migrate_count() - Migrate timer. + * @vcpu: Virtual CPU. + * + * Migrate hrtimer to the current CPU by cancelling and restarting it + * if it was running prior to being cancelled. + * + * Must be called when the VCPU is migrated to a different CPU to ensure that + * timer expiry during guest execution interrupts the guest and causes the + * interrupt to be delivered in a timely manner. + */ +static void kvm_migrate_count(struct kvm_vcpu *vcpu) +{ + if (hrtimer_cancel(&vcpu->arch.swtimer)) + hrtimer_restart(&vcpu->arch.swtimer); +} + +static int _kvm_vcpu_load(struct kvm_vcpu *vcpu, int cpu) +{ + struct kvm_context *context; + struct loongarch_csrs *csr = vcpu->arch.csr; + bool migrated, all; + + /* + * Have we migrated to a different CPU? + * If so, any old guest TLB state may be stale. + */ + migrated = (vcpu->arch.last_sched_cpu != cpu); + + /* + * Was this the last VCPU to run on this CPU? + * If not, any old guest state from this VCPU will have been clobbered. + */ + context = per_cpu_ptr(vcpu->kvm->arch.vmcs, cpu); + all = migrated || (context->last_vcpu != vcpu); + context->last_vcpu = vcpu; + + /* + * Restore timer state regardless + */ + kvm_restore_timer(vcpu); + + /* Control guest page CCA attribute */ + kvm_change_csr_gcfg(KVM_GCFG_MATC_MASK, KVM_GCFG_MATC_ROOT); + /* Restore hardware perf csr */ + kvm_restore_hw_perf(vcpu); + +#ifdef CONFIG_PARAVIRT + kvm_make_request(KVM_REQ_RECORD_STEAL, vcpu); +#endif + /* Don't bother restoring registers multiple times unless necessary */ + if (!all) + return 0; + + kvm_write_csr_gcntc((ulong)vcpu->kvm->arch.stablecounter_gftoffset); + /* + * Restore guest CSR registers + */ + kvm_restore_hw_gcsr(csr, KVM_CSR_CRMD); + kvm_restore_hw_gcsr(csr, KVM_CSR_PRMD); + kvm_restore_hw_gcsr(csr, KVM_CSR_EUEN); + kvm_restore_hw_gcsr(csr, KVM_CSR_MISC); + kvm_restore_hw_gcsr(csr, KVM_CSR_ECFG); + kvm_restore_hw_gcsr(csr, KVM_CSR_ERA); + kvm_restore_hw_gcsr(csr, KVM_CSR_BADV); + kvm_restore_hw_gcsr(csr, KVM_CSR_BADI); + kvm_restore_hw_gcsr(csr, KVM_CSR_EENTRY); + kvm_restore_hw_gcsr(csr, KVM_CSR_TLBIDX); + kvm_restore_hw_gcsr(csr, KVM_CSR_TLBEHI); + kvm_restore_hw_gcsr(csr, KVM_CSR_TLBELO0); + kvm_restore_hw_gcsr(csr, KVM_CSR_TLBELO1); + kvm_restore_hw_gcsr(csr, KVM_CSR_ASID); + kvm_restore_hw_gcsr(csr, KVM_CSR_PGDL); + kvm_restore_hw_gcsr(csr, KVM_CSR_PGDH); + kvm_restore_hw_gcsr(csr, KVM_CSR_PWCTL0); + kvm_restore_hw_gcsr(csr, KVM_CSR_PWCTL1); + kvm_restore_hw_gcsr(csr, KVM_CSR_STLBPGSIZE); + kvm_restore_hw_gcsr(csr, KVM_CSR_RVACFG); + kvm_restore_hw_gcsr(csr, KVM_CSR_CPUID); + kvm_restore_hw_gcsr(csr, KVM_CSR_KS0); + kvm_restore_hw_gcsr(csr, KVM_CSR_KS1); + kvm_restore_hw_gcsr(csr, KVM_CSR_KS2); + kvm_restore_hw_gcsr(csr, KVM_CSR_KS3); + kvm_restore_hw_gcsr(csr, KVM_CSR_KS4); + kvm_restore_hw_gcsr(csr, KVM_CSR_KS5); + kvm_restore_hw_gcsr(csr, KVM_CSR_KS6); + kvm_restore_hw_gcsr(csr, KVM_CSR_KS7); + kvm_restore_hw_gcsr(csr, KVM_CSR_TMID); + kvm_restore_hw_gcsr(csr, KVM_CSR_CNTC); + kvm_restore_hw_gcsr(csr, KVM_CSR_TLBRENTRY); + kvm_restore_hw_gcsr(csr, KVM_CSR_TLBRBADV); + kvm_restore_hw_gcsr(csr, KVM_CSR_TLBRERA); + kvm_restore_hw_gcsr(csr, KVM_CSR_TLBRSAVE); + kvm_restore_hw_gcsr(csr, KVM_CSR_TLBRELO0); + kvm_restore_hw_gcsr(csr, KVM_CSR_TLBRELO1); + kvm_restore_hw_gcsr(csr, KVM_CSR_TLBREHI); + kvm_restore_hw_gcsr(csr, KVM_CSR_TLBRPRMD); + kvm_restore_hw_gcsr(csr, KVM_CSR_DMWIN0); + kvm_restore_hw_gcsr(csr, KVM_CSR_DMWIN1); + kvm_restore_hw_gcsr(csr, KVM_CSR_DMWIN2); + kvm_restore_hw_gcsr(csr, KVM_CSR_DMWIN3); + kvm_restore_hw_gcsr(csr, KVM_CSR_LLBCTL); + + /* restore Root.Guestexcept from unused Guest guestexcept register */ + kvm_write_csr_gintc(csr->csrs[KVM_CSR_GINTC]); + + /* + * We should clear linked load bit to break interrupted atomics. This + * prevents a SC on the next VCPU from succeeding by matching a LL on + * the previous VCPU. + */ + if (vcpu->kvm->created_vcpus > 1) + kvm_set_gcsr_llbctl(KVM_LLBCTL_WCLLB); + + return 0; +} + +/* Restore ASID once we are scheduled back after preemption */ +void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) +{ + unsigned long flags; + + local_irq_save(flags); + vcpu->cpu = cpu; + if (vcpu->arch.last_sched_cpu != cpu) { + kvm_debug("[%d->%d]KVM VCPU[%d] switch\n", + vcpu->arch.last_sched_cpu, cpu, vcpu->vcpu_id); + /* + * Migrate the timer interrupt to the current CPU so that it + * always interrupts the guest and synchronously triggers a + * guest timer interrupt. + */ + kvm_migrate_count(vcpu); + } + + /* restore guest state to registers */ + _kvm_vcpu_load(vcpu, cpu); + local_irq_restore(flags); +} + +static int _kvm_vcpu_put(struct kvm_vcpu *vcpu, int cpu) +{ + struct loongarch_csrs *csr = vcpu->arch.csr; + + kvm_lose_fpu(vcpu); + kvm_lose_hw_perf(vcpu); + + kvm_save_hw_gcsr(csr, KVM_CSR_CRMD); + kvm_save_hw_gcsr(csr, KVM_CSR_PRMD); + kvm_save_hw_gcsr(csr, KVM_CSR_EUEN); + kvm_save_hw_gcsr(csr, KVM_CSR_MISC); + kvm_save_hw_gcsr(csr, KVM_CSR_ECFG); + kvm_save_hw_gcsr(csr, KVM_CSR_ERA); + kvm_save_hw_gcsr(csr, KVM_CSR_BADV); + kvm_save_hw_gcsr(csr, KVM_CSR_BADI); + kvm_save_hw_gcsr(csr, KVM_CSR_EENTRY); + kvm_save_hw_gcsr(csr, KVM_CSR_TLBIDX); + kvm_save_hw_gcsr(csr, KVM_CSR_TLBEHI); + kvm_save_hw_gcsr(csr, KVM_CSR_TLBELO0); + kvm_save_hw_gcsr(csr, KVM_CSR_TLBELO1); + kvm_save_hw_gcsr(csr, KVM_CSR_ASID); + kvm_save_hw_gcsr(csr, KVM_CSR_PGDL); + kvm_save_hw_gcsr(csr, KVM_CSR_PGDH); + kvm_save_hw_gcsr(csr, KVM_CSR_PGD); + kvm_save_hw_gcsr(csr, KVM_CSR_PWCTL0); + kvm_save_hw_gcsr(csr, KVM_CSR_PWCTL1); + kvm_save_hw_gcsr(csr, KVM_CSR_STLBPGSIZE); + kvm_save_hw_gcsr(csr, KVM_CSR_RVACFG); + kvm_save_hw_gcsr(csr, KVM_CSR_CPUID); + kvm_save_hw_gcsr(csr, KVM_CSR_PRCFG1); + kvm_save_hw_gcsr(csr, KVM_CSR_PRCFG2); + kvm_save_hw_gcsr(csr, KVM_CSR_PRCFG3); + kvm_save_hw_gcsr(csr, KVM_CSR_KS0); + kvm_save_hw_gcsr(csr, KVM_CSR_KS1); + kvm_save_hw_gcsr(csr, KVM_CSR_KS2); + kvm_save_hw_gcsr(csr, KVM_CSR_KS3); + kvm_save_hw_gcsr(csr, KVM_CSR_KS4); + kvm_save_hw_gcsr(csr, KVM_CSR_KS5); + kvm_save_hw_gcsr(csr, KVM_CSR_KS6); + kvm_save_hw_gcsr(csr, KVM_CSR_KS7); + kvm_save_hw_gcsr(csr, KVM_CSR_TMID); + kvm_save_hw_gcsr(csr, KVM_CSR_CNTC); + kvm_save_hw_gcsr(csr, KVM_CSR_LLBCTL); + kvm_save_hw_gcsr(csr, KVM_CSR_TLBRENTRY); + kvm_save_hw_gcsr(csr, KVM_CSR_TLBRBADV); + kvm_save_hw_gcsr(csr, KVM_CSR_TLBRERA); + kvm_save_hw_gcsr(csr, KVM_CSR_TLBRSAVE); + kvm_save_hw_gcsr(csr, KVM_CSR_TLBRELO0); + kvm_save_hw_gcsr(csr, KVM_CSR_TLBRELO1); + kvm_save_hw_gcsr(csr, KVM_CSR_TLBREHI); + kvm_save_hw_gcsr(csr, KVM_CSR_TLBRPRMD); + kvm_save_hw_gcsr(csr, KVM_CSR_DMWIN0); + kvm_save_hw_gcsr(csr, KVM_CSR_DMWIN1); + kvm_save_hw_gcsr(csr, KVM_CSR_DMWIN2); + kvm_save_hw_gcsr(csr, KVM_CSR_DMWIN3); + + /* save Root.Guestexcept in unused Guest guestexcept register */ + kvm_save_timer(vcpu); + csr->csrs[KVM_CSR_GINTC] = kvm_read_csr_gintc(); + return 0; +} + +/* ASID can change if another task is scheduled during preemption */ +void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) +{ + unsigned long flags; + int cpu; + + local_irq_save(flags); + cpu = smp_processor_id(); + vcpu->arch.last_sched_cpu = cpu; + vcpu->cpu = -1; + + /* save guest state in registers */ + _kvm_vcpu_put(vcpu, cpu); + local_irq_restore(flags); +} + +static int _kvm_get_one_reg(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg, s64 *v) +{ + struct loongarch_csrs *csr = vcpu->arch.csr; + int reg_idx, ret; + + if ((reg->id & KVM_IOC_CSRID(0)) == KVM_IOC_CSRID(0)) { + reg_idx = KVM_GET_IOC_CSRIDX(reg->id); + ret = _kvm_getcsr(vcpu, reg_idx, v, 0); + if (ret == 0) + return ret; + } + + switch (reg->id) { + case KVM_REG_LOONGARCH_COUNTER: + *v = drdtime() + vcpu->kvm->arch.stablecounter_gftoffset; + break; + default: + if ((reg->id & KVM_REG_LOONGARCH_MASK) != KVM_REG_LOONGARCH_CSR) + return -EINVAL; + + reg_idx = KVM_GET_IOC_CSRIDX(reg->id); + if (reg_idx < CSR_ALL_SIZE) + *v = kvm_read_sw_gcsr(csr, reg_idx); + else + return -EINVAL; + } + return 0; +} + +static int _kvm_set_one_reg(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg, + s64 v) +{ + struct loongarch_csrs *csr = vcpu->arch.csr; + int ret = 0; + unsigned long flags; + u64 val; + int reg_idx; + + val = v; + if ((reg->id & KVM_IOC_CSRID(0)) == KVM_IOC_CSRID(0)) { + reg_idx = KVM_GET_IOC_CSRIDX(reg->id); + ret = _kvm_setcsr(vcpu, reg_idx, &val, 0); + if (ret == 0) + return ret; + } + + switch (reg->id) { + case KVM_REG_LOONGARCH_COUNTER: + local_irq_save(flags); + /* + * gftoffset is relative with board, not vcpu + * only set for the first time for smp system + */ + if (vcpu->vcpu_id == 0) + vcpu->kvm->arch.stablecounter_gftoffset = (signed long)(v - drdtime()); + kvm_write_csr_gcntc((ulong)vcpu->kvm->arch.stablecounter_gftoffset); + local_irq_restore(flags); + break; + case KVM_REG_LOONGARCH_VCPU_RESET: + kvm_reset_timer(vcpu); + if (vcpu->vcpu_id == 0) + kvm_enable_ls3a_extirq(vcpu->kvm, false); + memset(&vcpu->arch.irq_pending, 0, sizeof(vcpu->arch.irq_pending)); + memset(&vcpu->arch.irq_clear, 0, sizeof(vcpu->arch.irq_clear)); + + /* disable pv timer when cpu resetting */ + vcpu->arch.st.guest_addr = 0; + break; + default: + if ((reg->id & KVM_REG_LOONGARCH_MASK) != KVM_REG_LOONGARCH_CSR) + return -EINVAL; + + reg_idx = KVM_GET_IOC_CSRIDX(reg->id); + if (reg_idx < CSR_ALL_SIZE) + kvm_write_sw_gcsr(csr, reg_idx, v); + else + return -EINVAL; + } + return ret; +} + +static int _kvm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) +{ + int ret; + s64 v; + + ret = _kvm_get_one_reg(vcpu, reg, &v); + if (ret) + return ret; + + ret = -EINVAL; + if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64) { + u64 __user *uaddr64 = (u64 __user *)(long)reg->addr; + + ret = put_user(v, uaddr64); + } else if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32) { + u32 __user *uaddr32 = (u32 __user *)(long)reg->addr; + u32 v32 = (u32)v; + + ret = put_user(v32, uaddr32); + } + + return ret; +} + +static int _kvm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) +{ + s64 v; + int ret; + + ret = -EINVAL; + if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64) { + u64 __user *uaddr64 = (u64 __user *)(long)reg->addr; + ret = get_user(v, uaddr64); + } else if ((reg->id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32) { + u32 __user *uaddr32 = (u32 __user *)(long)reg->addr; + s32 v32; + + ret = get_user(v32, uaddr32); + v = (s64)v32; + } + + if (ret) + return -EFAULT; + + return _kvm_set_one_reg(vcpu, reg, v); +} + +static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu, + struct kvm_enable_cap *cap) +{ + int r = 0; + + if (!kvm_vm_ioctl_check_extension(vcpu->kvm, cap->cap)) + return -EINVAL; + if (cap->flags) + return -EINVAL; + if (cap->args[0]) + return -EINVAL; + + switch (cap->cap) { + case KVM_CAP_LOONGARCH_FPU: + case KVM_CAP_LOONGARCH_LSX: + break; + default: + r = -EINVAL; + break; + } + + return r; +} + +long kvm_arch_vcpu_async_ioctl(struct file *filp, unsigned int ioctl, + unsigned long arg) +{ + struct kvm_vcpu *vcpu = filp->private_data; + void __user *argp = (void __user *)arg; + + if (ioctl == KVM_INTERRUPT) { + struct kvm_loongarch_interrupt irq; + + if (copy_from_user(&irq, argp, sizeof(irq))) + return -EFAULT; + kvm_debug("[%d] %s: irq: %d\n", vcpu->vcpu_id, __func__, + irq.irq); + + return kvm_vcpu_ioctl_interrupt(vcpu, &irq); + } + + return -ENOIOCTLCMD; +} + +int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level, + bool line_status) +{ + u32 irq = irq_level->irq; + unsigned int irq_type, vcpu_idx, irq_num, ret; + int nrcpus = atomic_read(&kvm->online_vcpus); + bool level = irq_level->level; + unsigned long flags; + + irq_type = (irq >> KVM_LOONGSON_IRQ_TYPE_SHIFT) & KVM_LOONGSON_IRQ_TYPE_MASK; + vcpu_idx = (irq >> KVM_LOONGSON_IRQ_VCPU_SHIFT) & KVM_LOONGSON_IRQ_VCPU_MASK; + irq_num = (irq >> KVM_LOONGSON_IRQ_NUM_SHIFT) & KVM_LOONGSON_IRQ_NUM_MASK; + + switch (irq_type) { + case KVM_LOONGSON_IRQ_TYPE_IOAPIC: + if (!ls7a_ioapic_in_kernel(kvm)) + return -ENXIO; + + if (vcpu_idx >= nrcpus) + return -EINVAL; + + ls7a_ioapic_lock(ls7a_ioapic_irqchip(kvm), &flags); + ret = kvm_ls7a_ioapic_set_irq(kvm, irq_num, level); + ls7a_ioapic_unlock(ls7a_ioapic_irqchip(kvm), &flags); + return ret; + } + kvm->stat.vm_ioctl_irq_line++; + + return -EINVAL; +} + +static int kvm_vm_ioctl_get_irqchip(struct kvm *kvm, struct loongarch_kvm_irqchip *chip) +{ + int r, dlen; + + r = 0; + dlen = chip->len - sizeof(struct loongarch_kvm_irqchip); + switch (chip->chip_id) { + case KVM_IRQCHIP_LS7A_IOAPIC: + if (dlen != sizeof(struct kvm_ls7a_ioapic_state)) { + kvm_err("get ls7a state err dlen:%d\n", dlen); + goto dlen_err; + } + r = kvm_get_ls7a_ioapic(kvm, (void *)chip->data); + break; + case KVM_IRQCHIP_LS3A_GIPI: + if (dlen != sizeof(gipiState)) { + kvm_err("get gipi state err dlen:%d\n", dlen); + goto dlen_err; + } + r = kvm_get_ls3a_ipi(kvm, (void *)chip->data); + break; + case KVM_IRQCHIP_LS3A_HT_IRQ: + case KVM_IRQCHIP_LS3A_ROUTE: + break; + case KVM_IRQCHIP_LS3A_EXTIRQ: + if (dlen != sizeof(struct kvm_loongarch_ls3a_extirq_state)) { + kvm_err("get extioi state err dlen:%d\n", dlen); + goto dlen_err; + } + r = kvm_get_ls3a_extirq(kvm, (void *)chip->data); + break; + case KVM_IRQCHIP_LS3A_IPMASK: + break; + default: + r = -EINVAL; + break; + } + return r; +dlen_err: + r = -EINVAL; + return r; +} + +static int kvm_vm_ioctl_set_irqchip(struct kvm *kvm, struct loongarch_kvm_irqchip *chip) +{ + int r, dlen; + + r = 0; + dlen = chip->len - sizeof(struct loongarch_kvm_irqchip); + switch (chip->chip_id) { + case KVM_IRQCHIP_LS7A_IOAPIC: + if (dlen != sizeof(struct kvm_ls7a_ioapic_state)) { + kvm_err("set ls7a state err dlen:%d\n", dlen); + goto dlen_err; + } + r = kvm_set_ls7a_ioapic(kvm, (void *)chip->data); + break; + case KVM_IRQCHIP_LS3A_GIPI: + if (dlen != sizeof(gipiState)) { + kvm_err("set gipi state err dlen:%d\n", dlen); + goto dlen_err; + } + r = kvm_set_ls3a_ipi(kvm, (void *)chip->data); + break; + case KVM_IRQCHIP_LS3A_HT_IRQ: + case KVM_IRQCHIP_LS3A_ROUTE: + break; + case KVM_IRQCHIP_LS3A_EXTIRQ: + if (dlen != sizeof(struct kvm_loongarch_ls3a_extirq_state)) { + kvm_err("set extioi state err dlen:%d\n", dlen); + goto dlen_err; + } + r = kvm_set_ls3a_extirq(kvm, (void *)chip->data); + break; + case KVM_IRQCHIP_LS3A_IPMASK: + break; + default: + r = -EINVAL; + break; + } + return r; +dlen_err: + r = -EINVAL; + return r; +} + +/* + * Read or write a bunch of msrs. All parameters are kernel addresses. + * + * @return number of msrs set successfully. + */ +static int _kvm_csr_io(struct kvm_vcpu *vcpu, struct kvm_msrs *msrs, + struct kvm_csr_entry *entries, + int (*do_csr)(struct kvm_vcpu *vcpu, + unsigned index, u64 *data, int force)) +{ + int i; + + for (i = 0; i < msrs->ncsrs; ++i) + if (do_csr(vcpu, entries[i].index, &entries[i].data, 1)) + break; + + return i; +} + +static int kvm_csr_io(struct kvm_vcpu *vcpu, struct kvm_msrs __user *user_msrs, + int (*do_csr)(struct kvm_vcpu *vcpu, + unsigned index, u64 *data, int force)) +{ + struct kvm_msrs msrs; + struct kvm_csr_entry *entries; + int r, n; + unsigned size; + + r = -EFAULT; + if (copy_from_user(&msrs, user_msrs, sizeof msrs)) + goto out; + + r = -E2BIG; + if (msrs.ncsrs >= CSR_ALL_SIZE) + goto out; + + size = sizeof(struct kvm_csr_entry) * msrs.ncsrs; + entries = memdup_user(user_msrs->entries, size); + if (IS_ERR(entries)) { + r = PTR_ERR(entries); + goto out; + } + + r = n = _kvm_csr_io(vcpu, &msrs, entries, do_csr); + if (r < 0) + goto out_free; + + r = -EFAULT; + if (copy_to_user(user_msrs->entries, entries, size)) + goto out_free; + + r = n; + +out_free: + kfree(entries); +out: + return r; +} + +static int _kvm_vcpu_set_attr(struct kvm_vcpu *vcpu, + struct kvm_device_attr *attr) +{ + int ret = -ENXIO; + + switch (attr->group) { +#ifdef CONFIG_PARAVIRT + case KVM_LARCH_VCPU_PVTIME_CTRL: + ret = _kvm_pvtime_set_attr(vcpu, attr); + break; +#endif + default: + ret = -ENXIO; + break; + } + + return ret; +} + +static int _kvm_vcpu_get_attr(struct kvm_vcpu *vcpu, + struct kvm_device_attr *attr) +{ + int ret = -ENXIO; + + switch (attr->group) { +#ifdef CONFIG_PARAVIRT + case KVM_LARCH_VCPU_PVTIME_CTRL: + ret = _kvm_pvtime_get_attr(vcpu, attr); + break; +#endif + default: + ret = -ENXIO; + break; + } + + return ret; +} + +static int _kvm_vcpu_has_attr(struct kvm_vcpu *vcpu, + struct kvm_device_attr *attr) +{ + int ret = -ENXIO; + + switch (attr->group) { +#ifdef CONFIG_PARAVIRT + case KVM_LARCH_VCPU_PVTIME_CTRL: + ret = _kvm_pvtime_has_attr(vcpu, attr); + break; +#endif + default: + ret = -ENXIO; + break; + } + + return ret; +} + +long kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl, + unsigned long arg) +{ + struct kvm_vcpu *vcpu = filp->private_data; + void __user *argp = (void __user *)arg; + struct kvm_device_attr attr; + long r; + + vcpu_load(vcpu); + + switch (ioctl) { + case KVM_SET_ONE_REG: + case KVM_GET_ONE_REG: { + struct kvm_one_reg reg; + + r = -EFAULT; + if (copy_from_user(®, argp, sizeof(reg))) + break; + if (ioctl == KVM_SET_ONE_REG) + r = _kvm_set_reg(vcpu, ®); + else + r = _kvm_get_reg(vcpu, ®); + break; + } + case KVM_ENABLE_CAP: { + struct kvm_enable_cap cap; + + r = -EFAULT; + if (copy_from_user(&cap, argp, sizeof(cap))) + break; + r = kvm_vcpu_ioctl_enable_cap(vcpu, &cap); + break; + } + case KVM_CHECK_EXTENSION: { + unsigned int ext; + if (copy_from_user(&ext, argp, sizeof(ext))) + return -EFAULT; + switch (ext) { + case KVM_CAP_LOONGARCH_FPU: + r = !!cpu_has_fpu; + break; + case KVM_CAP_LOONGARCH_LSX: + r = !!cpu_has_lsx; + break; + default: + break; + } + break; + } + + case KVM_LOONGARCH_GET_VCPU_STATE: + { + int i; + struct kvm_loongarch_vcpu_state vcpu_state; + r = -EFAULT; + + vcpu_state.online_vcpus = vcpu->kvm->arch.online_vcpus; + vcpu_state.is_migrate = 1; + for (i = 0; i < 4; i++) + vcpu_state.core_ext_ioisr[i] = vcpu->arch.core_ext_ioisr[i]; + + vcpu_state.irq_pending = vcpu->arch.irq_pending; + vcpu_state.irq_clear = vcpu->arch.irq_clear; + + if (copy_to_user(argp, &vcpu_state, sizeof(struct kvm_loongarch_vcpu_state))) + break; + r = 0; + break; + } + + case KVM_LOONGARCH_SET_VCPU_STATE: + { + int i; + struct kvm_loongarch_vcpu_state vcpu_state; + r = -EFAULT; + + if (copy_from_user(&vcpu_state, argp, sizeof(struct kvm_loongarch_vcpu_state))) + return -EFAULT; + + vcpu->kvm->arch.online_vcpus = vcpu_state.online_vcpus; + vcpu->kvm->arch.is_migrate = vcpu_state.is_migrate; + for (i = 0; i < 4; i++) + vcpu->arch.core_ext_ioisr[i] = vcpu_state.core_ext_ioisr[i]; + + vcpu->arch.irq_pending = vcpu_state.irq_pending; + vcpu->arch.irq_clear = vcpu_state.irq_clear; + r = 0; + break; + } + case KVM_GET_MSRS: { + r = kvm_csr_io(vcpu, argp, _kvm_getcsr); + break; + } + case KVM_SET_MSRS: { + r = kvm_csr_io(vcpu, argp, _kvm_setcsr); + break; + } + case KVM_SET_DEVICE_ATTR: { + r = -EFAULT; + if (copy_from_user(&attr, argp, sizeof(attr))) + break; + r = _kvm_vcpu_set_attr(vcpu, &attr); + break; + } + case KVM_GET_DEVICE_ATTR: { + r = -EFAULT; + if (copy_from_user(&attr, argp, sizeof(attr))) + break; + r = _kvm_vcpu_get_attr(vcpu, &attr); + break; + } + case KVM_HAS_DEVICE_ATTR: { + r = -EFAULT; + if (copy_from_user(&attr, argp, sizeof(attr))) + break; + r = _kvm_vcpu_has_attr(vcpu, &attr); + break; + } + default: + r = -ENOIOCTLCMD; + } + + vcpu_put(vcpu); + return r; +} + +/** + * kvm_vm_ioctl_get_dirty_log - get and clear the log of dirty pages in a slot + * @kvm: kvm instance + * @log: slot id and address to which we copy the log + * + * Steps 1-4 below provide general overview of dirty page logging. See + * kvm_get_dirty_log_protect() function description for additional details. + * + * We call kvm_get_dirty_log_protect() to handle steps 1-3, upon return we + * always flush the TLB (step 4) even if previous step failed and the dirty + * bitmap may be corrupt. Regardless of previous outcome the KVM logging API + * does not preclude user space subsequent dirty log read. Flushing TLB ensures + * writes will be marked dirty for next log read. + * + * 1. Take a snapshot of the bit and clear it if needed. + * 2. Write protect the corresponding page. + * 3. Copy the snapshot to the userspace. + * 4. Flush TLB's if needed. + */ +int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log) +{ + struct kvm_memslots *slots; + struct kvm_memory_slot *memslot; + bool is_dirty = false; + int r; + + mutex_lock(&kvm->slots_lock); + + r = kvm_get_dirty_log_protect(kvm, log, &is_dirty); + + if (is_dirty) { + slots = kvm_memslots(kvm); + memslot = id_to_memslot(slots, log->slot); + + /* + * FIXME: disable THP to improve vm migration success ratio, + * how to know migration failure to enable THP again + */ + memslot->arch.flags |= KVM_MEMSLOT_DISABLE_THP; + + /* Let implementation handle TLB/GVA invalidation */ + kvm_flush_remote_tlbs(kvm); + } + + mutex_unlock(&kvm->slots_lock); + return r; +} + +long kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) +{ + struct kvm *kvm = filp->private_data; + void __user *argp = (void __user *)arg; + long r; + + switch (ioctl) { + case KVM_CREATE_IRQCHIP: + { + mutex_lock(&kvm->lock); + r = -EEXIST; + if (kvm->arch.v_ioapic) + goto create_irqchip_unlock; + + r = kvm_create_ls7a_ioapic(kvm); + if (r < 0) + goto create_irqchip_unlock; + r = kvm_create_ls3a_ipi(kvm); + if (r < 0) { + mutex_lock(&kvm->slots_lock); + kvm_destroy_ls7a_ioapic(kvm); + mutex_unlock(&kvm->slots_lock); + goto create_irqchip_unlock; + } + r = kvm_create_ls3a_ext_irq(kvm); + if (r < 0) { + mutex_lock(&kvm->slots_lock); + kvm_destroy_ls3a_ipi(kvm); + kvm_destroy_ls7a_ioapic(kvm); + mutex_unlock(&kvm->slots_lock); + } + irqchip_debug_init(kvm); + /* Write kvm->irq_routing before kvm->arch.vpic. */ + smp_wmb(); +create_irqchip_unlock: + mutex_unlock(&kvm->lock); + break; + } + case KVM_GET_IRQCHIP: { + struct loongarch_kvm_irqchip *kchip; + struct loongarch_kvm_irqchip uchip; + if (copy_from_user(&uchip, argp, sizeof(struct loongarch_kvm_irqchip))) + goto out; + kchip = memdup_user(argp, uchip.len); + if (IS_ERR(kchip)) { + r = PTR_ERR(kchip); + goto out; + } + + r = -ENXIO; + if (!ls7a_ioapic_in_kernel(kvm)) + goto get_irqchip_out; + r = kvm_vm_ioctl_get_irqchip(kvm, kchip); + if (r) + goto get_irqchip_out; + if (copy_to_user(argp, kchip, kchip->len)) + goto get_irqchip_out; + r = 0; +get_irqchip_out: + kfree(kchip); + break; + } + case KVM_SET_IRQCHIP: { + struct loongarch_kvm_irqchip *kchip; + struct loongarch_kvm_irqchip uchip; + if (copy_from_user(&uchip, argp, sizeof(struct loongarch_kvm_irqchip))) + goto out; + + kchip = memdup_user(argp, uchip.len); + if (IS_ERR(kchip)) { + r = PTR_ERR(kchip); + goto out; + } + + r = -ENXIO; + if (!ls7a_ioapic_in_kernel(kvm)) + goto set_irqchip_out; + r = kvm_vm_ioctl_set_irqchip(kvm, kchip); + if (r) + goto set_irqchip_out; + r = 0; +set_irqchip_out: + kfree(kchip); + break; + } + case KVM_LOONGARCH_GET_IOCSR: + { + r = _kvm_get_iocsr(kvm, argp); + break; + } + case KVM_LOONGARCH_SET_IOCSR: + { + r = _kvm_set_iocsr(kvm, argp); + break; + } + case KVM_LOONGARCH_SET_CPUCFG: + { + r = 0; + if (copy_from_user(&kvm->arch.cpucfgs, argp, sizeof(struct kvm_cpucfg))) + r = -EFAULT; + break; + } + case KVM_LOONGARCH_GET_CPUCFG: + { + r = 0; + if (copy_to_user(argp, &kvm->arch.cpucfgs, sizeof(struct kvm_cpucfg))) + r = -EFAULT; + break; + } + default: + r = -ENOIOCTLCMD; + } +out: + + return r; +} + +int kvm_arch_init(void *opaque) +{ + struct kvm_context *context; + unsigned long vpid_mask; + int cpu; + + vmcs = alloc_percpu(struct kvm_context); + if (!vmcs) { + printk(KERN_ERR "kvm: failed to allocate percpu kvm_context\n"); + return -ENOMEM; + } + + vpid_mask = kvm_read_csr_gstat(); + vpid_mask = (vpid_mask & KVM_GSTAT_GIDBIT) >> KVM_GSTAT_GIDBIT_SHIFT; + if (vpid_mask) + vpid_mask = GENMASK(vpid_mask - 1, 0); + + for_each_possible_cpu(cpu) { + context = per_cpu_ptr(vmcs, cpu); + context->gid_mask = vpid_mask; + context->gid_ver_mask = ~context->gid_mask; + context->gid_fisrt_ver = context->gid_mask + 1; + context->vpid_cache = context->gid_mask + 1; + context->last_vcpu = NULL; + } + + _kvm_init_fault(); + return 0; +} + +void kvm_arch_exit(void) +{ + free_percpu(vmcs); +} + +int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu, + struct kvm_sregs *sregs) +{ + return -ENOIOCTLCMD; +} + +int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu, + struct kvm_sregs *sregs) +{ + return -ENOIOCTLCMD; +} + +void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu) +{ +} + +int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) +{ + int i = 0; + + /* no need vcpu_load and vcpu_put */ + fpu->fcsr = vcpu->arch.fpu.fcsr; + fpu->fcc = vcpu->arch.fpu.fcc; + for (i = 0; i < NUM_FPU_REGS; i++) + memcpy(&fpu->fpr[i], &vcpu->arch.fpu.fpr[i], FPU_REG_WIDTH / 64); + + return 0; +} + +int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) +{ + int i = 0; + + /* no need vcpu_load and vcpu_put */ + vcpu->arch.fpu.fcsr = fpu->fcsr; + vcpu->arch.fpu.fcc = fpu->fcc; + for (i = 0; i < NUM_FPU_REGS; i++) + memcpy(&vcpu->arch.fpu.fpr[i], &fpu->fpr[i], FPU_REG_WIDTH / 64); + + return 0; +} + +vm_fault_t kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf) +{ + return VM_FAULT_SIGBUS; +} + +int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) +{ + int r; + + switch (ext) { + case KVM_CAP_ONE_REG: + case KVM_CAP_ENABLE_CAP: + case KVM_CAP_READONLY_MEM: + case KVM_CAP_SYNC_MMU: +#ifdef CONFIG_HAVE_LS_KVM_MSI + case KVM_CAP_SIGNAL_MSI: +#endif + case KVM_CAP_IMMEDIATE_EXIT: + r = 1; + break; + case KVM_CAP_NR_VCPUS: + r = num_online_cpus(); + break; + case KVM_CAP_MAX_VCPUS: + r = KVM_MAX_VCPUS; + break; + case KVM_CAP_MAX_VCPU_ID: + r = KVM_MAX_VCPU_ID; + break; + case KVM_CAP_NR_MEMSLOTS: + r = KVM_USER_MEM_SLOTS; + break; + case KVM_CAP_LOONGARCH_FPU: + /* We don't handle systems with inconsistent cpu_has_fpu */ + r = !!cpu_has_fpu; + break; + case KVM_CAP_LOONGARCH_LSX: + /* + * We don't support LSX vector partitioning yet: + * 1) It would require explicit support which can't be tested + * yet due to lack of support in current hardware. + * 2) It extends the state that would need to be saved/restored + * by e.g. QEMU for migration. + * + * When vector partitioning hardware becomes available, support + * could be added by requiring a flag when enabling + * KVM_CAP_LOONGARCH_LSX capability to indicate that userland knows + * to save/restore the appropriate extra state. + */ + r = cpu_has_lsx; + break; + case KVM_CAP_IRQCHIP: + case KVM_CAP_IOEVENTFD: + /* we wouldn't be here unless cpu_has_lvz */ + r = 1; + break; + case KVM_CAP_LOONGARCH_VZ: + /* get user defined kvm version */ + r = KVM_LOONGARCH_VERSION; + break; + default: + r = 0; + break; + } + return r; +} + +int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu) +{ + return _kvm_pending_timer(vcpu) || + kvm_read_hw_gcsr(KVM_CSR_ESTAT) & + (1 << (EXCCODE_TIMER - EXCCODE_INT_START)); +} + +int kvm_arch_vcpu_dump_regs(struct kvm_vcpu *vcpu) +{ + int i; + struct loongarch_csrs *csr; + + if (!vcpu) + return -1; + + kvm_debug("VCPU Register Dump:\n"); + kvm_debug("\tpc = 0x%08lx\n", vcpu->arch.pc); + kvm_debug("\texceptions: %08lx\n", vcpu->arch.irq_pending); + + for (i = 0; i < 32; i += 4) { + kvm_debug("\tgpr%02d: %08lx %08lx %08lx %08lx\n", i, + vcpu->arch.gprs[i], + vcpu->arch.gprs[i + 1], + vcpu->arch.gprs[i + 2], vcpu->arch.gprs[i + 3]); + } + + csr = vcpu->arch.csr; + kvm_debug("\tCRMOD: 0x%08llx, exst: 0x%08llx\n", + kvm_read_hw_gcsr(KVM_CSR_CRMD), + kvm_read_hw_gcsr(KVM_CSR_ESTAT)); + + kvm_debug("\tERA: 0x%08llx\n", kvm_read_hw_gcsr(KVM_CSR_ERA)); + + return 0; +} + +int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) +{ + int i; + + vcpu_load(vcpu); + + for (i = 1; i < ARRAY_SIZE(vcpu->arch.gprs); i++) + vcpu->arch.gprs[i] = regs->gpr[i]; + vcpu->arch.gprs[0] = 0; /* zero is special, and cannot be set. */ + vcpu->arch.pc = regs->pc; + + vcpu_put(vcpu); + return 0; +} + +int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) +{ + int i; + + vcpu_load(vcpu); + + for (i = 0; i < ARRAY_SIZE(vcpu->arch.gprs); i++) + regs->gpr[i] = vcpu->arch.gprs[i]; + + regs->pc = vcpu->arch.pc; + + vcpu_put(vcpu); + return 0; +} + +static void kvm_swtimer_func(unsigned long data) +{ + struct kvm_vcpu *vcpu = (struct kvm_vcpu *)data; + + _kvm_queue_irq(vcpu, LARCH_INT_TIMER); + if (swq_has_sleeper(&vcpu->wq)) + swake_up_one(&vcpu->wq); +} + +/* low level hrtimer wake routine */ +static enum hrtimer_restart kvm_swtimer_wakeup(struct hrtimer *timer) +{ + struct kvm_vcpu *vcpu; + + vcpu = container_of(timer, struct kvm_vcpu, arch.swtimer); + kvm_swtimer_func((unsigned long) vcpu); + return kvm_count_timeout(vcpu); +} + +int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) +{ + int i; + + for_each_possible_cpu(i) + vcpu->arch.vpid[i] = 0; + + hrtimer_init(&vcpu->arch.swtimer, CLOCK_MONOTONIC, + HRTIMER_MODE_ABS_PINNED); + vcpu->arch.swtimer.function = kvm_swtimer_wakeup; + vcpu->arch.fpu_enabled = true; + vcpu->arch.lsx_enabled = true; + return 0; +} + +void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) +{ + int cpu; + struct kvm_context *context; + + /* + * If the VCPU is freed and reused as another VCPU, we don't want the + * matching pointer wrongly hanging around in last_vcpu. + */ + for_each_possible_cpu(cpu) { + context = per_cpu_ptr(vcpu->kvm->arch.vmcs, cpu); + if (context->last_vcpu == vcpu) + context->last_vcpu = NULL; + } +} + +int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu, + struct kvm_translation *tr) +{ + return 0; +} + +/* Initial guest state */ +int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu) +{ + struct loongarch_csrs *csr = vcpu->arch.csr; + unsigned long timer_hz; + + /* + * Initialize guest register state to valid architectural reset state. + */ + timer_hz = calc_const_freq(); + kvm_init_timer(vcpu, timer_hz); + + /* Set Initialize mode for GUEST */ + kvm_write_sw_gcsr(csr, KVM_CSR_CRMD, KVM_CRMD_DA); + + /* Set cpuid */ + kvm_write_sw_gcsr(csr, KVM_CSR_TMID, vcpu->vcpu_id); + + /* start with no pending virtual guest interrupts */ + csr->csrs[KVM_CSR_GINTC] = 0; + return 0; +} + +/* Enable FPU for guest and restore context */ +void kvm_own_fpu(struct kvm_vcpu *vcpu) +{ + unsigned long sr; + + preempt_disable(); + + sr = kvm_read_hw_gcsr(KVM_CSR_EUEN); + + /* + * If LSX state is already live, it is undefined how it interacts with + * FR=0 FPU state, and we don't want to hit reserved instruction + * exceptions trying to save the LSX state later when CU=1 && FR=1, so + * play it safe and save it first. + * + * In theory we shouldn't ever hit this case since kvm_lose_fpu() should + * get called when guest CU1 is set, however we can't trust the guest + * not to clobber the status register directly via the commpage. + */ + if (cpu_has_lsx && sr & KVM_EUEN_FPEN && + vcpu->arch.aux_inuse & (KVM_LARCH_LSX | KVM_LARCH_LASX)) + kvm_lose_fpu(vcpu); + + /* + * Enable FPU for guest + * We set FR and FRE according to guest context + */ + kvm_set_csr_euen(KVM_EUEN_FPEN); + + /* If guest FPU state not active, restore it now */ + if (!(vcpu->arch.aux_inuse & KVM_LARCH_FPU)) { + kvm_restore_fpu(vcpu); + vcpu->arch.aux_inuse |= KVM_LARCH_FPU; + trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_FPU); + } else { + trace_kvm_aux(vcpu, KVM_TRACE_AUX_ENABLE, KVM_TRACE_AUX_FPU); + } + + preempt_enable(); +} + +#ifdef CONFIG_CPU_HAS_LSX +/* Enable LSX for guest and restore context */ +void kvm_own_lsx(struct kvm_vcpu *vcpu) +{ + preempt_disable(); + + /* + * Enable FP if enabled in guest, since we're restoring FP context + * anyway. + */ + if (_kvm_guest_has_fpu(&vcpu->arch)) { + + kvm_set_csr_euen(KVM_EUEN_FPEN); + } + + /* Enable LSX for guest */ + kvm_set_csr_euen(KVM_EUEN_LSXEN); + + switch (vcpu->arch.aux_inuse & (KVM_LARCH_FPU | + KVM_LARCH_LSX | KVM_LARCH_LASX)) { + case KVM_LARCH_FPU: + /* + * Guest FPU state already loaded, + * only restore upper LSX state + */ + kvm_restore_lsx_upper(vcpu); + vcpu->arch.aux_inuse |= KVM_LARCH_LSX; + trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, + KVM_TRACE_AUX_LSX); + break; + case 0: + /* Neither FP or LSX already active, + * restore full LSX state + */ + kvm_restore_lsx(vcpu); + vcpu->arch.aux_inuse |= KVM_LARCH_LSX; + if (_kvm_guest_has_fpu(&vcpu->arch)) + vcpu->arch.aux_inuse |= KVM_LARCH_FPU; + trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, + KVM_TRACE_AUX_FPU_LSX); + break; + default: + trace_kvm_aux(vcpu, KVM_TRACE_AUX_ENABLE, KVM_TRACE_AUX_LSX); + break; + } + + preempt_enable(); +} +#endif + +#ifdef CONFIG_CPU_HAS_LASX +/* Enable LASX for guest and restore context */ +void kvm_own_lasx(struct kvm_vcpu *vcpu) +{ + preempt_disable(); + + /* + * Enable FP if enabled in guest, since we're restoring FP context + * anyway. + */ + if (_kvm_guest_has_lsx(&vcpu->arch)) { + /* Enable LSX for guest */ + kvm_set_csr_euen(KVM_EUEN_LSXEN); + } + + /* + * Enable FPU if enabled in guest, since we're restoring FPU context + * anyway. We set FR and FRE according to guest context. + */ + if (_kvm_guest_has_fpu(&vcpu->arch)) { + kvm_set_csr_euen(KVM_EUEN_FPEN); + } + + /* Enable LASX for guest */ + kvm_set_csr_euen(KVM_EUEN_LASXEN); + + switch (vcpu->arch.aux_inuse & (KVM_LARCH_FPU | + KVM_LARCH_LSX | KVM_LARCH_LASX)) { + case (KVM_LARCH_LSX | KVM_LARCH_FPU): + case KVM_LARCH_LSX: + /* + * Guest LSX state already loaded, only restore upper LASX state + */ + kvm_restore_lasx_upper(vcpu); + vcpu->arch.aux_inuse |= KVM_LARCH_LASX; + trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_LASX); + break; + case KVM_LARCH_FPU: + /* + * Guest FP state already loaded, only restore 64~256 LASX state + */ + kvm_restore_lsx_upper(vcpu); + kvm_restore_lasx_upper(vcpu); + vcpu->arch.aux_inuse |= KVM_LARCH_LASX; + if (_kvm_guest_has_lsx(&vcpu->arch)) + vcpu->arch.aux_inuse |= KVM_LARCH_LSX; + trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_LASX); + break; + case 0: + /* Neither FP or LSX already active, restore full LASX state */ + kvm_restore_lasx(vcpu); + vcpu->arch.aux_inuse |= KVM_LARCH_LASX; + if (_kvm_guest_has_lsx(&vcpu->arch)) + vcpu->arch.aux_inuse |= KVM_LARCH_LSX; + if (_kvm_guest_has_fpu(&vcpu->arch)) + vcpu->arch.aux_inuse |= KVM_LARCH_FPU; + trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, + KVM_TRACE_AUX_FPU_LSX_LASX); + break; + default: + trace_kvm_aux(vcpu, KVM_TRACE_AUX_ENABLE, KVM_TRACE_AUX_LASX); + break; + } + + preempt_enable(); +} +#endif + +/* Save and disable FPU & LSX & LASX */ +void kvm_lose_fpu(struct kvm_vcpu *vcpu) +{ + preempt_disable(); + if (cpu_has_lasx && (vcpu->arch.aux_inuse & KVM_LARCH_LASX)) { + + kvm_save_lasx(vcpu); + trace_kvm_aux(vcpu, KVM_TRACE_AUX_SAVE, KVM_TRACE_AUX_FPU_LSX_LASX); + + /* Disable LASX & MAS & FPU */ + disable_lasx(); + disable_lsx(); + + if (vcpu->arch.aux_inuse & KVM_LARCH_FPU) { + kvm_clear_csr_euen(KVM_EUEN_FPEN); + } + vcpu->arch.aux_inuse &= ~(KVM_LARCH_FPU | + KVM_LARCH_LSX | KVM_LARCH_LASX); + } else if (cpu_has_lsx && vcpu->arch.aux_inuse & KVM_LARCH_LSX) { + + kvm_save_lsx(vcpu); + trace_kvm_aux(vcpu, KVM_TRACE_AUX_SAVE, KVM_TRACE_AUX_FPU_LSX); + + /* Disable LSX & FPU */ + disable_lsx(); + if (vcpu->arch.aux_inuse & KVM_LARCH_FPU) { + kvm_clear_csr_euen(KVM_EUEN_FPEN); + } + vcpu->arch.aux_inuse &= ~(KVM_LARCH_FPU | KVM_LARCH_LSX); + } else if (vcpu->arch.aux_inuse & KVM_LARCH_FPU) { + + kvm_save_fpu(vcpu); + vcpu->arch.aux_inuse &= ~KVM_LARCH_FPU; + trace_kvm_aux(vcpu, KVM_TRACE_AUX_SAVE, KVM_TRACE_AUX_FPU); + + /* Disable FPU */ + kvm_clear_csr_euen(KVM_EUEN_FPEN); + } + preempt_enable(); +} + +void kvm_lose_hw_perf(struct kvm_vcpu *vcpu) +{ + if (vcpu->arch.aux_inuse & KVM_LARCH_PERF) { + struct loongarch_csrs *csr = vcpu->arch.csr; + /* save guest pmu csr */ + kvm_save_hw_gcsr(csr, KVM_CSR_PERFCTRL0); + kvm_save_hw_gcsr(csr, KVM_CSR_PERFCNTR0); + kvm_save_hw_gcsr(csr, KVM_CSR_PERFCTRL1); + kvm_save_hw_gcsr(csr, KVM_CSR_PERFCNTR1); + kvm_save_hw_gcsr(csr, KVM_CSR_PERFCTRL2); + kvm_save_hw_gcsr(csr, KVM_CSR_PERFCNTR2); + kvm_save_hw_gcsr(csr, KVM_CSR_PERFCTRL3); + kvm_save_hw_gcsr(csr, KVM_CSR_PERFCNTR3); + if (((kvm_read_sw_gcsr(csr, KVM_CSR_PERFCTRL0) | + kvm_read_sw_gcsr(csr, KVM_CSR_PERFCTRL1) | + kvm_read_sw_gcsr(csr, KVM_CSR_PERFCTRL2) | + kvm_read_sw_gcsr(csr, KVM_CSR_PERFCTRL3)) + & KVM_PMU_PLV_ENABLE) == 0) + vcpu->arch.aux_inuse &= ~KVM_LARCH_PERF; + /* config host pmu csr */ + kvm_write_csr_gcfg(kvm_read_csr_gcfg() & ~KVM_GCFG_GPERF); + /* TODO: pmu csr used by host and guest at the same time */ + kvm_write_csr_perfctrl0(0); + kvm_write_csr_perfcntr0(0); + kvm_write_csr_perfctrl1(0); + kvm_write_csr_perfcntr1(0); + kvm_write_csr_perfctrl2(0); + kvm_write_csr_perfcntr2(0); + kvm_write_csr_perfctrl3(0); + kvm_write_csr_perfcntr3(0); + } +} + +void kvm_restore_hw_perf(struct kvm_vcpu *vcpu) +{ + if (vcpu->arch.aux_inuse & KVM_LARCH_PERF) { + struct loongarch_csrs *csr = vcpu->arch.csr; + /* enable guest pmu */ + kvm_write_csr_gcfg(kvm_read_csr_gcfg() | KVM_GCFG_GPERF); + kvm_restore_hw_gcsr(csr, KVM_CSR_PERFCTRL0); + kvm_restore_hw_gcsr(csr, KVM_CSR_PERFCNTR0); + kvm_restore_hw_gcsr(csr, KVM_CSR_PERFCTRL1); + kvm_restore_hw_gcsr(csr, KVM_CSR_PERFCNTR1); + kvm_restore_hw_gcsr(csr, KVM_CSR_PERFCTRL2); + kvm_restore_hw_gcsr(csr, KVM_CSR_PERFCNTR2); + kvm_restore_hw_gcsr(csr, KVM_CSR_PERFCTRL3); + kvm_restore_hw_gcsr(csr, KVM_CSR_PERFCNTR3); + } +} + +static int __init kvm_loongarch_init(void) +{ + int ret; + + if (!cpu_has_lvz) + return 0; + + ret = kvm_init(NULL, sizeof(struct kvm_vcpu), 0, THIS_MODULE); + + if (ret) + return ret; + + return 0; +} + +static void __exit kvm_loongarch_exit(void) +{ + kvm_exit(); +} + +module_init(kvm_loongarch_init); +module_exit(kvm_loongarch_exit); + +static const struct cpu_feature loongarch_kvm_feature[] = { + { .feature = cpu_feature(LOONGARCH_LVZ) }, + {}, +}; +MODULE_DEVICE_TABLE(cpu, loongarch_kvm_feature); + +EXPORT_TRACEPOINT_SYMBOL(kvm_exit); diff --git a/arch/loongarch/kvm/ls_irq.h b/arch/loongarch/kvm/ls_irq.h new file mode 100644 index 000000000000..344ba5ebc4b5 --- /dev/null +++ b/arch/loongarch/kvm/ls_irq.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#ifndef __LOONGHARCH_KVM_IRQ_H__ +#define __LOONGHARCH_KVM_IRQ_H__ + +static inline int irqchip_in_kernel(struct kvm *kvm) +{ + return kvm->arch.v_ioapic ? 1 : 0; +} + +#endif diff --git a/arch/loongarch/kvm/mmu.c b/arch/loongarch/kvm/mmu.c new file mode 100644 index 000000000000..8ddfb0e8a89a --- /dev/null +++ b/arch/loongarch/kvm/mmu.c @@ -0,0 +1,1336 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "kvm_compat.h" + +/* + * KVM_MMU_CACHE_MIN_PAGES is the number of GPA page table translation levels + * for which pages need to be cached. + */ +#if defined(__PAGETABLE_PMD_FOLDED) +#define KVM_MMU_CACHE_MIN_PAGES 1 +#else +#define KVM_MMU_CACHE_MIN_PAGES 2 +#endif + +static int kvm_tlb_flush_gpa(struct kvm_vcpu *vcpu, unsigned long gpa) +{ + preempt_disable(); + gpa &= (PAGE_MASK << 1); + invtlb(INVTLB_GID_ADDR, kvm_read_csr_gstat() & KVM_GSTAT_GID, gpa); + preempt_enable(); + return 0; +} + +static inline pmd_t kvm_pmd_mkhuge(pmd_t pmd) +{ +#ifdef CONFIG_LOONGARCH_HUGE_TLB_SUPPORT +#ifdef CONFIG_TRANSPARENT_HUGEPAGE + return pmd_mkhuge(pmd); +#else + pte_t entry; + + pte_val(entry) = pmd_val(pmd); + entry = pte_mkhuge(entry); + pmd_val(pmd) = pte_val(entry); +#endif +#endif + return pmd; +} + +static inline pmd_t kvm_pmd_mkclean(pmd_t pmd) +{ +#ifdef CONFIG_LOONGARCH_HUGE_TLB_SUPPORT +#ifdef CONFIG_TRANSPARENT_HUGEPAGE + return pmd_mkclean(pmd); +#else + pte_t entry; + + pte_val(entry) = pmd_val(pmd); + entry = pte_mkclean(entry); + pmd_val(pmd) = pte_val(entry); +#endif +#endif + return pmd; +} + +static inline pmd_t kvm_pmd_mkold(pmd_t pmd) +{ +#ifdef CONFIG_LOONGARCH_HUGE_TLB_SUPPORT +#ifdef CONFIG_TRANSPARENT_HUGEPAGE + return pmd_mkold(pmd); +#else + pte_t entry; + + pte_val(entry) = pmd_val(pmd); + entry = pte_mkold(entry); + pmd_val(pmd) = pte_val(entry); +#endif +#endif + return pmd; +} + +static int mmu_topup_memory_cache(struct kvm_mmu_memory_cache *cache, + int min, int max) +{ + void *page; + + BUG_ON(max > KVM_NR_MEM_OBJS); + if (cache->nobjs >= min) + return 0; + while (cache->nobjs < max) { + page = (void *)__get_free_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + cache->objects[cache->nobjs++] = page; + } + return 0; +} + +static void mmu_free_memory_cache(struct kvm_mmu_memory_cache *mc) +{ + while (mc->nobjs) + free_page((unsigned long)mc->objects[--mc->nobjs]); +} + +static void *mmu_memory_cache_alloc(struct kvm_mmu_memory_cache *mc, + struct vm_area_struct *vma, unsigned long hva) +{ + void *p = NULL; + struct mempolicy *pol = __get_vma_policy(vma, hva); + struct page *page; + + + BUG_ON(!mc || !mc->nobjs); + if (pol) { + page = alloc_page_vma(GFP_KERNEL_ACCOUNT | GFP_ATOMIC, vma, hva); + if (page) + p = page_address(page); + } + if (!p) + p = mc->objects[--mc->nobjs]; + + return p; +} + +void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu) +{ + mmu_free_memory_cache(&vcpu->arch.mmu_page_cache); +} + +/** + * kvm_pgd_alloc() - Allocate and initialise a KVM GPA page directory. + * + * Allocate a blank KVM GPA page directory (PGD) for representing guest physical + * to host physical page mappings. + * + * Returns: Pointer to new KVM GPA page directory. + * NULL on allocation failure. + */ +pgd_t *kvm_pgd_alloc(void) +{ + pgd_t *ret; + struct page *page; + + page = alloc_pages(GFP_KERNEL, 0); + if (!page) + return NULL; + ret = (pgd_t *) page_address(page); + if (ret) + pgd_init(ret); + + return ret; +} + +/** + * kvm_walk_pgd() - Walk page table with optional allocation. + * @pgd: Page directory pointer. + * @addr: Address to index page table using. + * @cache: MMU page cache to allocate new page tables from, or NULL. + * + * Walk the page tables pointed to by @pgd to find the PTE corresponding to the + * address @addr. If page tables don't exist for @addr, they will be created + * from the MMU cache if @cache is not NULL. + * + * Returns: Pointer to pte_t corresponding to @addr. + * NULL if a page table doesn't exist for @addr and !@cache. + * NULL if a page table allocation failed. + */ +static pte_t *kvm_walk_pgd(pgd_t *pgd, struct kvm_mmu_memory_cache *cache, + struct vm_area_struct *vma, unsigned long hva, + unsigned long addr) +{ + pud_t *pud; + pmd_t *pmd; + + pgd += pgd_index(addr); + if (pgd_none(*pgd)) { + /* Not used yet */ + BUG(); + return NULL; + } + pud = pud_offset(pgd, addr); + if (pud_none(*pud)) { + pmd_t *new_pmd; + + if (!cache) + return NULL; + new_pmd = mmu_memory_cache_alloc(cache, vma, hva); + pmd_init(new_pmd); + pud_populate(NULL, pud, new_pmd); + } + pmd = pmd_offset(pud, addr); +#ifdef CONFIG_LOONGARCH_HUGE_TLB_SUPPORT + if (pmd_huge(*pmd)) { + return (pte_t *)pmd; + } +#endif + if (pmd_none(*pmd)) { + pte_t *new_pte; + + if (!cache) + return NULL; + new_pte = mmu_memory_cache_alloc(cache, vma, hva); + clear_page(new_pte); + pmd_populate_kernel(NULL, pmd, new_pte); + } + return pte_offset(pmd, addr); +} + +/* Caller must hold kvm->mm_lock */ +static pte_t *kvm_pte_for_gpa(struct kvm *kvm, + struct kvm_mmu_memory_cache *cache, + struct vm_area_struct *vma, unsigned long hva, + unsigned long addr) +{ + return kvm_walk_pgd(kvm->arch.gpa_mm.pgd, cache, vma, hva, addr); +} + +#define kvm_pte_for_gpa_fast(kvm, gpa) kvm_pte_for_gpa(kvm, NULL, NULL, 0, gpa) +/* + * kvm_flush_gpa_{pte,pmd,pud,pgd,pt}. + * Flush a range of guest physical address space from the VM's GPA page tables. + */ + +static bool kvm_flush_gpa_pte(pte_t *pte, unsigned long start_gpa, + unsigned long end_gpa, unsigned long *data) +{ + int i_min = __pte_offset(start_gpa); + int i_max = __pte_offset(end_gpa); + bool safe_to_remove = (i_min == 0 && i_max == PTRS_PER_PTE - 1); + int i; + + for (i = i_min; i <= i_max; ++i) { + if (!pte_present(pte[i])) + continue; + + set_pte(pte + i, __pte(0)); + if (data) + *data = *data + 1; + } + return safe_to_remove; +} + +static bool kvm_flush_gpa_pmd(pmd_t *pmd, unsigned long start_gpa, + unsigned long end_gpa, unsigned long *data) +{ + pte_t *pte; + unsigned long end = ~0ul; + int i_min = __pmd_offset(start_gpa); + int i_max = __pmd_offset(end_gpa); + bool safe_to_remove = (i_min == 0 && i_max == PTRS_PER_PMD - 1); + int i; + + for (i = i_min; i <= i_max; ++i, start_gpa = 0) { + if (!pmd_present(pmd[i])) + continue; + + if (pmd_huge(pmd[i]) && pmd_present(pmd[i])) { + pmd_clear(pmd + i); + if (data) + *data += PTRS_PER_PMD; + continue; + } + + pte = pte_offset(pmd + i, 0); + if (i == i_max) + end = end_gpa; + + if (kvm_flush_gpa_pte(pte, start_gpa, end, data)) { + pmd_clear(pmd + i); + pte_free_kernel(NULL, pte); + } else { + safe_to_remove = false; + } + } + return safe_to_remove; +} + +static bool kvm_flush_gpa_pud(pud_t *pud, unsigned long start_gpa, + unsigned long end_gpa, unsigned long *data) +{ + pmd_t *pmd; + unsigned long end = ~0ul; + int i_min = __pud_offset(start_gpa); + int i_max = __pud_offset(end_gpa); + bool safe_to_remove = (i_min == 0 && i_max == PTRS_PER_PUD - 1); + int i; + + for (i = i_min; i <= i_max; ++i, start_gpa = 0) { + if (!pud_present(pud[i])) + continue; + + pmd = pmd_offset(pud + i, 0); + if (i == i_max) + end = end_gpa; + + if (kvm_flush_gpa_pmd(pmd, start_gpa, end, data)) { + pud_clear(pud + i); + pmd_free(NULL, pmd); + } else { + safe_to_remove = false; + } + } + return safe_to_remove; +} + +static bool kvm_flush_gpa_pgd(pgd_t *pgd, unsigned long start_gpa, + unsigned long end_gpa, unsigned long *data) +{ + pud_t *pud; + unsigned long end = ~0ul; + int i_min = pgd_index(start_gpa); + int i_max = pgd_index(end_gpa); + bool safe_to_remove = (i_min == 0 && i_max == PTRS_PER_PGD - 1); + int i; + + for (i = i_min; i <= i_max; ++i, start_gpa = 0) { + if (!pgd_present(pgd[i])) + continue; + + pud = pud_offset(pgd + i, 0); + if (i == i_max) + end = end_gpa; + + if (kvm_flush_gpa_pud(pud, start_gpa, end, data)) { + pgd_clear(pgd + i); + pud_free(NULL, pud); + } else { + safe_to_remove = false; + } + } + return safe_to_remove; +} + +/** + * kvm_flush_gpa_pt() - Flush a range of guest physical addresses. + * @kvm: KVM pointer. + * @start_gfn: Guest frame number of first page in GPA range to flush. + * @end_gfn: Guest frame number of last page in GPA range to flush. + * + * Flushes a range of GPA mappings from the GPA page tables. + * + * The caller must hold the @kvm->mmu_lock spinlock. + * + * Returns: Whether its safe to remove the top level page directory because + * all lower levels have been removed. + */ +static bool kvm_flush_gpa_pt(struct kvm *kvm, gfn_t start_gfn, gfn_t end_gfn, void *data) +{ + return kvm_flush_gpa_pgd(kvm->arch.gpa_mm.pgd, + start_gfn << PAGE_SHIFT, + end_gfn << PAGE_SHIFT, (unsigned long *)data); +} + +/* + * kvm_mkclean_gpa_pt. + * Mark a range of guest physical address space clean (writes fault) in the VM's + * GPA page table to allow dirty page tracking. + */ + +static int kvm_mkclean_pte(pte_t *pte, unsigned long start, unsigned long end) +{ + int ret = 0; + int i_min = __pte_offset(start); + int i_max = __pte_offset(end); + int i; + pte_t val; + + for (i = i_min; i <= i_max; ++i) { + val = pte[i]; + if (pte_present(val) && pte_dirty(val)) { + set_pte(pte + i, pte_mkclean(val)); + ret = 1; + } + } + return ret; +} + +static int kvm_mkclean_pmd(pmd_t *pmd, unsigned long start, unsigned long end) +{ + int ret = 0; + pte_t *pte; + unsigned long cur_end = ~0ul; + int i_min = __pmd_offset(start); + int i_max = __pmd_offset(end); + int i; + pmd_t old, new; + + for (i = i_min; i <= i_max; ++i, start = 0) { + if (!pmd_present(pmd[i])) + continue; + + if (pmd_huge(pmd[i])) { + old = pmd[i]; + new = kvm_pmd_mkclean(old); + if (pmd_val(new) == pmd_val(old)) + continue; + set_pmd(pmd + i, new); + ret = 1; + continue; + } + + pte = pte_offset(pmd + i, 0); + if (i == i_max) + cur_end = end; + + ret |= kvm_mkclean_pte(pte, start, cur_end); + } + + return ret; +} + +static int kvm_mkclean_pud(pud_t *pud, unsigned long start, unsigned long end) +{ + int ret = 0; + pmd_t *pmd; + unsigned long cur_end = ~0ul; + int i_min = __pud_offset(start); + int i_max = __pud_offset(end); + int i; + + for (i = i_min; i <= i_max; ++i, start = 0) { + if (!pud_present(pud[i])) + continue; + + pmd = pmd_offset(pud + i, 0); + if (i == i_max) + cur_end = end; + + ret |= kvm_mkclean_pmd(pmd, start, cur_end); + } + return ret; +} + +static int kvm_mkclean_pgd(pgd_t *pgd, unsigned long start, unsigned long end) +{ + int ret = 0; + pud_t *pud; + unsigned long cur_end = ~0ul; + int i_min = pgd_index(start); + int i_max = pgd_index(end); + int i; + + for (i = i_min; i <= i_max; ++i, start = 0) { + if (!pgd_present(pgd[i])) + continue; + + pud = pud_offset(pgd + i, 0); + if (i == i_max) + cur_end = end; + + ret |= kvm_mkclean_pud(pud, start, cur_end); + } + return ret; +} + +/** + * kvm_mkclean_gpa_pt() - Make a range of guest physical addresses clean. + * @kvm: KVM pointer. + * @start_gfn: Guest frame number of first page in GPA range to flush. + * @end_gfn: Guest frame number of last page in GPA range to flush. + * + * Make a range of GPA mappings clean so that guest writes will fault and + * trigger dirty page logging. + * + * The caller must hold the @kvm->mmu_lock spinlock. + * + * Returns: Whether any GPA mappings were modified, which would require + * derived mappings (GVA page tables & TLB enties) to be + * invalidated. + */ +static int kvm_mkclean_gpa_pt(struct kvm *kvm, gfn_t start_gfn, gfn_t end_gfn) +{ + return kvm_mkclean_pgd(kvm->arch.gpa_mm.pgd, start_gfn << PAGE_SHIFT, + end_gfn << PAGE_SHIFT); +} + +/** + * kvm_arch_mmu_enable_log_dirty_pt_masked() - write protect dirty pages + * @kvm: The KVM pointer + * @slot: The memory slot associated with mask + * @gfn_offset: The gfn offset in memory slot + * @mask: The mask of dirty pages at offset 'gfn_offset' in this memory + * slot to be write protected + * + * Walks bits set in mask write protects the associated pte's. Caller must + * acquire @kvm->mmu_lock. + */ +void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm, + struct kvm_memory_slot *slot, + gfn_t gfn_offset, unsigned long mask) +{ + gfn_t base_gfn = slot->base_gfn + gfn_offset; + gfn_t start = base_gfn + __ffs(mask); + gfn_t end = base_gfn + __fls(mask); + + kvm_mkclean_gpa_pt(kvm, start, end); +} + +void kvm_arch_commit_memory_region(struct kvm *kvm, + const struct kvm_userspace_memory_region *mem, + const struct kvm_memory_slot *old, + const struct kvm_memory_slot *new, + enum kvm_mr_change change) +{ + int needs_flush; + + kvm_debug("%s: kvm: %p slot: %d, GPA: %llx, size: %llx, QVA: %llx\n", + __func__, kvm, mem->slot, mem->guest_phys_addr, + mem->memory_size, mem->userspace_addr); + + /* + * If dirty page logging is enabled, write protect all pages in the slot + * ready for dirty logging. + * + * There is no need to do this in any of the following cases: + * CREATE: No dirty mappings will already exist. + * MOVE/DELETE: The old mappings will already have been cleaned up by + * kvm_arch_flush_shadow_memslot() + */ + if (change == KVM_MR_FLAGS_ONLY && + (!(old->flags & KVM_MEM_LOG_DIRTY_PAGES) && + new->flags & KVM_MEM_LOG_DIRTY_PAGES)) { + spin_lock(&kvm->mmu_lock); + /* Write protect GPA page table entries */ + needs_flush = kvm_mkclean_gpa_pt(kvm, new->base_gfn, + new->base_gfn + new->npages - 1); + /* Let implementation do the rest */ + if (needs_flush) + kvm_flush_remote_tlbs(kvm); + spin_unlock(&kvm->mmu_lock); + } +} + +void kvm_arch_flush_shadow_all(struct kvm *kvm) +{ + /* Flush whole GPA */ + kvm_flush_gpa_pt(kvm, 0, ~0UL, NULL); + + /* Flush vpid for each VCPU individually */ + kvm_flush_remote_tlbs(kvm); +} + +void kvm_arch_flush_shadow_memslot(struct kvm *kvm, + struct kvm_memory_slot *slot) +{ + unsigned long npages; + + /* + * The slot has been made invalid (ready for moving or deletion), so we + * need to ensure that it can no longer be accessed by any guest VCPUs. + */ + + npages = 0; + spin_lock(&kvm->mmu_lock); + /* Flush slot from GPA */ + kvm_flush_gpa_pt(kvm, slot->base_gfn, + slot->base_gfn + slot->npages - 1, &npages); + /* Let implementation do the rest */ + if (npages) + kvm_flush_remote_tlbs(kvm); + spin_unlock(&kvm->mmu_lock); +} + +void _kvm_destroy_mm(struct kvm *kvm) +{ + /* It should always be safe to remove after flushing the whole range */ + WARN_ON(!kvm_flush_gpa_pt(kvm, 0, ~0UL, NULL)); + pgd_free(NULL, kvm->arch.gpa_mm.pgd); + kvm->arch.gpa_mm.pgd = NULL; +} + +/* + * Mark a range of guest physical address space old (all accesses fault) in the + * VM's GPA page table to allow detection of commonly used pages. + */ + +static int kvm_mkold_pte(pte_t *pte, unsigned long start, + unsigned long end) +{ + int ret = 0; + int i_min = __pte_offset(start); + int i_max = __pte_offset(end); + int i; + pte_t old, new; + + for (i = i_min; i <= i_max; ++i) { + if (!pte_present(pte[i])) + continue; + + old = pte[i]; + new = pte_mkold(old); + if (pte_val(new) == pte_val(old)) + continue; + set_pte(pte + i, new); + ret = 1; + } + + return ret; +} + +static int kvm_mkold_pmd(pmd_t *pmd, unsigned long start, unsigned long end) +{ + int ret = 0; + pte_t *pte; + unsigned long cur_end = ~0ul; + int i_min = __pmd_offset(start); + int i_max = __pmd_offset(end); + int i; + pmd_t old, new; + + for (i = i_min; i <= i_max; ++i, start = 0) { + if (!pmd_present(pmd[i])) + continue; + + if (pmd_huge(pmd[i])) { + old = pmd[i]; + new = kvm_pmd_mkold(old); + if (pmd_val(new) == pmd_val(old)) + continue; + set_pmd(pmd + i, new); + ret = 1; + continue; + } + + pte = pte_offset(pmd + i, 0); + if (i == i_max) + cur_end = end; + + ret |= kvm_mkold_pte(pte, start, cur_end); + } + + return ret; +} + +static int kvm_mkold_pud(pud_t *pud, unsigned long start, unsigned long end) +{ + int ret = 0; + pmd_t *pmd; + unsigned long cur_end = ~0ul; + int i_min = __pud_offset(start); + int i_max = __pud_offset(end); + int i; + + for (i = i_min; i <= i_max; ++i, start = 0) { + if (!pud_present(pud[i])) + continue; + + pmd = pmd_offset(pud + i, 0); + if (i == i_max) + cur_end = end; + + ret |= kvm_mkold_pmd(pmd, start, cur_end); + } + + return ret; +} + +static int kvm_mkold_pgd(pgd_t *pgd, unsigned long start, unsigned long end) +{ + int ret = 0; + pud_t *pud; + unsigned long cur_end = ~0ul; + int i_min = pgd_index(start); + int i_max = pgd_index(end); + int i; + + for (i = i_min; i <= i_max; ++i, start = 0) { + if (!pgd_present(pgd[i])) + continue; + + pud = pud_offset(pgd + i, 0); + if (i == i_max) + cur_end = end; + + ret |= kvm_mkold_pud(pud, start, cur_end); + } + + return ret; +} + +static int handle_hva_to_gpa(struct kvm *kvm, + unsigned long start, + unsigned long end, + int (*handler)(struct kvm *kvm, gfn_t gfn, + gpa_t gfn_end, + struct kvm_memory_slot *memslot, + void *data), + void *data) +{ + struct kvm_memslots *slots; + struct kvm_memory_slot *memslot; + int ret = 0; + + slots = kvm_memslots(kvm); + + /* we only care about the pages that the guest sees */ + kvm_for_each_memslot(memslot, slots) { + unsigned long hva_start, hva_end; + gfn_t gfn, gfn_end; + + hva_start = max(start, memslot->userspace_addr); + hva_end = min(end, memslot->userspace_addr + + (memslot->npages << PAGE_SHIFT)); + if (hva_start >= hva_end) + continue; + + /* + * {gfn(page) | page intersects with [hva_start, hva_end)} = + * {gfn_start, gfn_start+1, ..., gfn_end-1}. + */ + gfn = hva_to_gfn_memslot(hva_start, memslot); + gfn_end = hva_to_gfn_memslot(hva_end + PAGE_SIZE - 1, memslot); + ret |= handler(kvm, gfn, gfn_end, memslot, data); + } + + return ret; +} + + +static int kvm_unmap_hva_handler(struct kvm *kvm, gfn_t gfn, gfn_t gfn_end, + struct kvm_memory_slot *memslot, void *data) +{ + unsigned long npages; + + npages = 0; + kvm_flush_gpa_pt(kvm, gfn, gfn_end - 1, &npages); + *(unsigned long *)data = *(unsigned long *)data + npages; + + return npages > 0; +} + +int kvm_unmap_hva_range(struct kvm *kvm, unsigned long start, unsigned long end, bool blockable) +{ + unsigned long npages; + + npages = 0; + return handle_hva_to_gpa(kvm, start, end, &kvm_unmap_hva_handler, &npages); +} + +static int kvm_set_spte_handler(struct kvm *kvm, gfn_t gfn, gfn_t gfn_end, + struct kvm_memory_slot *memslot, void *data) +{ + gpa_t gpa = gfn << PAGE_SHIFT; + pte_t hva_pte = *(pte_t *)data; + pte_t *gpa_pte = kvm_pte_for_gpa_fast(kvm, gpa); + pte_t old_pte; + + if (!gpa_pte) + return 0; + + /* Mapping may need adjusting depending on memslot flags */ + old_pte = *gpa_pte; + if (memslot->flags & KVM_MEM_LOG_DIRTY_PAGES && !pte_dirty(old_pte)) + hva_pte = pte_mkclean(hva_pte); + else if (memslot->flags & KVM_MEM_READONLY) + hva_pte = pte_wrprotect(hva_pte); + + set_pte(gpa_pte, hva_pte); + + /* Replacing an absent or old page doesn't need flushes */ + if (!pte_present(old_pte) || !pte_young(old_pte)) + return 0; + + /* Pages swapped, aged, moved, or cleaned require flushes */ + return !pte_present(hva_pte) || + !pte_young(hva_pte) || + pte_pfn(old_pte) != pte_pfn(hva_pte) || + (pte_dirty(old_pte) && !pte_dirty(hva_pte)); +} + +int _kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte) +{ + unsigned long end = hva + PAGE_SIZE; + int ret; + + ret = handle_hva_to_gpa(kvm, hva, end, &kvm_set_spte_handler, &pte); + if (ret) + /* Flush vpid for each VCPU individually */ + kvm_flush_remote_tlbs(kvm); + return 0; +} + +static int kvm_age_hva_handler(struct kvm *kvm, gfn_t gfn, gfn_t gfn_end, + struct kvm_memory_slot *memslot, void *data) +{ + return kvm_mkold_pgd(kvm->arch.gpa_mm.pgd, gfn << PAGE_SHIFT, + gfn_end << PAGE_SHIFT); +} + +static int kvm_test_age_hva_handler(struct kvm *kvm, gfn_t gfn, gfn_t gfn_end, + struct kvm_memory_slot *memslot, void *data) +{ + gpa_t gpa = gfn << PAGE_SHIFT; + pte_t *gpa_pte = kvm_pte_for_gpa_fast(kvm, gpa); + + if (!gpa_pte) + return 0; + return pte_young(*gpa_pte); +} + +int kvm_age_hva(struct kvm *kvm, unsigned long start, unsigned long end) +{ + return handle_hva_to_gpa(kvm, start, end, kvm_age_hva_handler, NULL); +} + +int kvm_test_age_hva(struct kvm *kvm, unsigned long hva) +{ + return handle_hva_to_gpa(kvm, hva, hva, kvm_test_age_hva_handler, NULL); +} + +static pud_t *kvm_get_pud(struct kvm *kvm, + struct kvm_mmu_memory_cache *cache, phys_addr_t addr) +{ + pgd_t *pgd; + + pgd = kvm->arch.gpa_mm.pgd + pgd_index(addr); + if (pgd_none(*pgd)) { + /* Not used yet */ + BUG(); + return NULL; + } + + return pud_offset(pgd, addr); +} + +static pmd_t *kvm_get_pmd(struct kvm *kvm, + struct vm_area_struct *vma, unsigned long hva, + struct kvm_mmu_memory_cache *cache, phys_addr_t addr) +{ + pud_t *pud; + pmd_t *pmd; + + pud = kvm_get_pud(kvm, cache, addr); + if (!pud || pud_huge(*pud)) + return NULL; + + if (pud_none(*pud)) { + if (!cache) + return NULL; + pmd = mmu_memory_cache_alloc(cache, vma, hva); + pmd_init(pmd); + pud_populate(NULL, pud, pmd); + } + + return pmd_offset(pud, addr); +} + +static int kvm_set_pmd_huge(struct kvm_vcpu *vcpu, struct kvm_mmu_memory_cache + *cache, phys_addr_t addr, const pmd_t *new_pmd, + struct vm_area_struct *vma, unsigned long hva) +{ + pmd_t *pmd, old_pmd; + +retry: + pmd = kvm_get_pmd(vcpu->kvm, vma, hva, cache, addr); + VM_BUG_ON(!pmd); + + old_pmd = *pmd; + /* + * Multiple vcpus faulting on the same PMD entry, can + * lead to them sequentially updating the PMD with the + * same value. Following the break-before-make + * (pmd_clear() followed by tlb_flush()) process can + * hinder forward progress due to refaults generated + * on missing translations. + * + * Skip updating the page table if the entry is + * unchanged. + */ + if (pmd_val(old_pmd) == pmd_val(*new_pmd)) + return 0; + + if (pmd_present(old_pmd)) { + /* + * If we already have PTE level mapping for this block, + * we must unmap it to avoid inconsistent TLB state and + * leaking the table page. We could end up in this situation + * if the memory slot was marked for dirty logging and was + * reverted, leaving PTE level mappings for the pages accessed + * during the period. So, unmap the PTE level mapping for this + * block and retry, as we could have released the upper level + * table in the process. + * + * Normal THP split/merge follows mmu_notifier callbacks and do + * get handled accordingly. + */ + if (!pmd_huge(old_pmd)) { + ++vcpu->stat.huge_merge_exits; + kvm_flush_gpa_pt(vcpu->kvm, + (addr & PMD_MASK) >> PAGE_SHIFT, + ((addr & PMD_MASK) + PMD_SIZE - 1) >> PAGE_SHIFT, NULL); + goto retry; + } + /* + * Mapping in huge pages should only happen through a + * fault. If a page is merged into a transparent huge + * page, the individual subpages of that huge page + * should be unmapped through MMU notifiers before we + * get here. + * + * Merging of CompoundPages is not supported; they + * should become splitting first, unmapped, merged, + * and mapped back in on-demand. + */ +#ifdef CONFIG_TRANSPARENT_HUGEPAGE + WARN_ON_ONCE(pmd_pfn(old_pmd) != pmd_pfn(*new_pmd)); +#endif + pmd_clear(pmd); + } + + kvm_tlb_flush_gpa(vcpu, addr & PMD_MASK); + set_pmd(pmd, *new_pmd); + return 0; +} + +/* + * Adjust pfn start boundary if support for transparent hugepage + */ +static bool transparent_hugepage_adjust(kvm_pfn_t *pfnp, unsigned long *gpap) +{ + kvm_pfn_t pfn = *pfnp; + gfn_t gfn = *gpap >> PAGE_SHIFT; + struct page *page = pfn_to_page(pfn); + + /* + * PageTransCompoundMap() returns true for THP and + * hugetlbfs. Make sure the adjustment is done only for THP + * pages. + */ + if ((!PageHuge(page)) && PageTransCompound(page) && + (atomic_read(&page->_mapcount) < 0)) { + unsigned long mask; + /* + * The address we faulted on is backed by a transparent huge + * page. However, because we map the compound huge page and + * not the individual tail page, we need to transfer the + * refcount to the head page. We have to be careful that the + * THP doesn't start to split while we are adjusting the + * refcounts. + * + * We are sure this doesn't happen, because mmu_notifier_retry + * was successful and we are holding the mmu_lock, so if this + * THP is trying to split, it will be blocked in the mmu + * notifier before touching any of the pages, specifically + * before being able to call __split_huge_page_refcount(). + * + * We can therefore safely transfer the refcount from PG_tail + * to PG_head and switch the pfn from a tail page to the head + * page accordingly. + */ + mask = PTRS_PER_PMD - 1; + VM_BUG_ON((gfn & mask) != (pfn & mask)); + if (pfn & mask) { + *gpap &= PMD_MASK; + kvm_release_pfn_clean(pfn); + pfn &= ~mask; + kvm_get_pfn(pfn); + *pfnp = pfn; + } + + return true; + } + + return false; +} + +static bool fault_supports_huge_mapping(struct kvm_memory_slot *memslot, + unsigned long hva, + unsigned long map_size) +{ + gpa_t gpa_start; + hva_t uaddr_start, uaddr_end; + size_t size; + + if (memslot->arch.flags & KVM_MEMSLOT_DISABLE_THP) + return false; + + size = memslot->npages * PAGE_SIZE; + gpa_start = memslot->base_gfn << PAGE_SHIFT; + uaddr_start = memslot->userspace_addr; + uaddr_end = uaddr_start + size; + + /* + * Pages belonging to memslots that don't have the same alignment + * within a PMD/PUD for userspace and GPA cannot be mapped with stage-2 + * PMD/PUD entries, because we'll end up mapping the wrong pages. + * + * Consider a layout like the following: + * + * memslot->userspace_addr: + * +-----+--------------------+--------------------+---+ + * |abcde|fgh Stage-1 block | Stage-1 block tv|xyz| + * +-----+--------------------+--------------------+---+ + * + * memslot->base_gfn << PAGE_SIZE: + * +---+--------------------+--------------------+-----+ + * |abc|def Stage-2 block | Stage-2 block |tvxyz| + * +---+--------------------+--------------------+-----+ + * + * If we create those stage-2 blocks, we'll end up with this incorrect + * mapping: + * d -> f + * e -> g + * f -> h + */ + if ((gpa_start & (map_size - 1)) != (uaddr_start & (map_size - 1))) + return false; + + /* + * Next, let's make sure we're not trying to map anything not covered + * by the memslot. This means we have to prohibit block size mappings + * for the beginning and end of a non-block aligned and non-block sized + * memory slot (illustrated by the head and tail parts of the + * userspace view above containing pages 'abcde' and 'xyz', + * respectively). + * + * Note that it doesn't matter if we do the check using the + * userspace_addr or the base_gfn, as both are equally aligned (per + * the check above) and equally sized. + */ + return (hva & ~(map_size - 1)) >= uaddr_start && + (hva & ~(map_size - 1)) + map_size <= uaddr_end; +} + +/** + * kvm_map_page_fast() - Fast path GPA fault handler. + * @vcpu: VCPU pointer. + * @gpa: Guest physical address of fault. + * @write: Whether the fault was due to a write. + * @out_entry: New PTE for @gpa (written on success unless NULL). + * @out_buddy: New PTE for @gpa's buddy (written on success unless + * NULL). + * + * Perform fast path GPA fault handling, doing all that can be done without + * calling into KVM. This handles marking old pages young (for idle page + * tracking), and dirtying of clean pages (for dirty page logging). + * + * Returns: 0 on success, in which case we can update derived mappings and + * resume guest execution. + * -EFAULT on failure due to absent GPA mapping or write to + * read-only page, in which case KVM must be consulted. + */ +static int kvm_map_page_fast(struct kvm_vcpu *vcpu, unsigned long gpa, + bool write, + pte_t *out_entry, pte_t *out_buddy) +{ + struct kvm *kvm = vcpu->kvm; + gfn_t gfn = gpa >> PAGE_SHIFT; + pte_t *ptep; + kvm_pfn_t pfn = 0; /* silence bogus GCC warning */ + bool pfn_valid = false; + int ret = 0; + + spin_lock(&kvm->mmu_lock); + + /* Fast path - just check GPA page table for an existing entry */ + ptep = kvm_pte_for_gpa_fast(kvm, gpa); + if (!ptep || !pte_present(*ptep)) { + ret = -EFAULT; + goto out; + } + + /* Track access to pages marked old */ + if (!pte_young(*ptep)) { + set_pte(ptep, pte_mkyoung(*ptep)); + pfn = pte_pfn(*ptep); + pfn_valid = true; + /* call kvm_set_pfn_accessed() after unlock */ + } + if (write && !pte_dirty(*ptep)) { + if (!pte_write(*ptep)) { + ret = -EFAULT; + goto out; + } + + /* Track dirtying of writeable pages */ + set_pte(ptep, pte_mkdirty(*ptep)); + pfn = pte_pfn(*ptep); + if (pmd_huge(*((pmd_t *)ptep))) { + int i; + gfn_t base_gfn = (gpa & PMD_MASK) >> PAGE_SHIFT; + + for (i = 0; i < PTRS_PER_PTE; i++) + mark_page_dirty(kvm, base_gfn + i); + } else + mark_page_dirty(kvm, gfn); + kvm_set_pfn_dirty(pfn); + } + + if (out_entry) + *out_entry = *ptep; + if (out_buddy) + *out_buddy = *ptep_buddy(ptep); + +out: + spin_unlock(&kvm->mmu_lock); + if (pfn_valid) + kvm_set_pfn_accessed(pfn); + return ret; +} + +/** + * kvm_map_page() - Map a guest physical page. + * @vcpu: VCPU pointer. + * @gpa: Guest physical address of fault. + * @write: Whether the fault was due to a write. + * @out_entry: New PTE for @gpa (written on success unless NULL). + * @out_buddy: New PTE for @gpa's buddy (written on success unless + * NULL). + * + * Handle GPA faults by creating a new GPA mapping (or updating an existing + * one). + * + * This takes care of marking pages young or dirty (idle/dirty page tracking), + * asking KVM for the corresponding PFN, and creating a mapping in the GPA page + * tables. Derived mappings (GVA page tables and TLBs) must be handled by the + * caller. + * + * Returns: 0 on success, in which case the caller may use the @out_entry + * and @out_buddy PTEs to update derived mappings and resume guest + * execution. + * -EFAULT if there is no memory region at @gpa or a write was + * attempted to a read-only memory region. This is usually handled + * as an MMIO access. + */ +static int kvm_map_page(struct kvm_vcpu *vcpu, unsigned long gpa, + bool write, + pte_t *out_entry, pte_t *out_buddy) +{ + struct kvm *kvm = vcpu->kvm; + struct kvm_mmu_memory_cache *memcache = &vcpu->arch.mmu_page_cache; + gfn_t gfn = gpa >> PAGE_SHIFT; + int srcu_idx, err = 0; + kvm_pfn_t pfn; + pte_t *ptep; + bool writeable; + unsigned long prot_bits; + unsigned long mmu_seq; + u32 exccode = (vcpu->arch.host_estat & KVM_ESTAT_EXC) >> KVM_ESTAT_EXC_SHIFT; + + unsigned long hva; + struct kvm_memory_slot *memslot; + bool force_pte = false; + struct vm_area_struct *vma; + unsigned long vma_pagesize; + bool writable; + int ret, retry_no = 0; + + /* Try the fast path to handle old / clean pages */ + srcu_idx = srcu_read_lock(&kvm->srcu); + if ((exccode != EXCCODE_TLBRI) && (exccode != EXCCODE_TLBXI)) { + err = kvm_map_page_fast(vcpu, gpa, write, out_entry, + out_buddy); + if (!err) + goto out; + } + + memslot = gfn_to_memslot(kvm, gfn); + hva = gfn_to_hva_memslot_prot(memslot, gfn, &writable); + if (kvm_is_error_hva(hva) || (write && !writable)) + goto out; + + /* Let's check if we will get back a huge page backed by hugetlbfs */ + down_read(¤t->mm->mmap_sem); + vma = find_vma_intersection(current->mm, hva, hva + 1); + if (unlikely(!vma)) { + kvm_err("Failed to find VMA for hva 0x%lx\n", hva); + up_read(¤t->mm->mmap_sem); + err = -EFAULT; + goto out; + } + + vma_pagesize = vma_kernel_pagesize(vma); + + if (fault_supports_huge_mapping(memslot, hva, vma_pagesize)) { + force_pte = true; + vma_pagesize = PAGE_SIZE; + ++vcpu->stat.huge_dec_exits; + } + + /* PMD is not folded, adjust gfn to new boundary */ + if (vma_pagesize == PMD_SIZE) + gfn = (gpa & huge_page_mask(hstate_vma(vma))) >> PAGE_SHIFT; + + up_read(¤t->mm->mmap_sem); + + /* We need a minimum of cached pages ready for page table creation */ + err = mmu_topup_memory_cache(memcache, KVM_MMU_CACHE_MIN_PAGES, + KVM_NR_MEM_OBJS); + if (err) + goto out; + +retry: + /* + * Used to check for invalidations in progress, of the pfn that is + * returned by pfn_to_pfn_prot below. + */ + mmu_seq = kvm->mmu_notifier_seq; + /* + * Ensure the read of mmu_notifier_seq isn't reordered with PTE reads in + * gfn_to_pfn_prot() (which calls get_user_pages()), so that we don't + * risk the page we get a reference to getting unmapped before we have a + * chance to grab the mmu_lock without mmu_notifier_retry() noticing. + * + * This smp_rmb() pairs with the effective smp_wmb() of the combination + * of the pte_unmap_unlock() after the PTE is zapped, and the + * spin_lock() in kvm_mmu_notifier_invalidate_() before + * mmu_notifier_seq is incremented. + */ + smp_rmb(); + + /* Slow path - ask KVM core whether we can access this GPA */ + pfn = gfn_to_pfn_prot(kvm, gfn, write, &writeable); + if (is_error_noslot_pfn(pfn)) { + err = -EFAULT; + goto out; + } + + spin_lock(&kvm->mmu_lock); + /* Check if an invalidation has taken place since we got pfn */ + if (mmu_notifier_retry(kvm, mmu_seq)) { + /* + * This can happen when mappings are changed asynchronously, but + * also synchronously if a COW is triggered by + * gfn_to_pfn_prot(). + */ + spin_unlock(&kvm->mmu_lock); + kvm_release_pfn_clean(pfn); + if (retry_no > 100) { + retry_no = 0; + schedule(); + } + retry_no++; + goto retry; + } + + if (vma_pagesize == PAGE_SIZE && !force_pte) { + /* + * Only PMD_SIZE transparent hugepages(THP) are + * currently supported. This code will need to be + * updated to support other THP sizes. + * + * Make sure the host VA and the guest IPA are sufficiently + * aligned and that the block is contained within the memslot. + */ + ++vcpu->stat.huge_thp_exits; + if (fault_supports_huge_mapping(memslot, hva, PMD_SIZE) && + transparent_hugepage_adjust(&pfn, &gpa)) { + ++vcpu->stat.huge_adjust_exits; + vma_pagesize = PMD_SIZE; + } + } + + /* Set up the prot bits */ + prot_bits = _PAGE_PRESENT | __READABLE; + if (vma->vm_flags & (VM_IO | VM_PFNMAP)) + prot_bits |= _CACHE_SUC; + else + prot_bits |= _CACHE_CC; + + if (writeable) { + prot_bits |= _PAGE_WRITE; + if (write) { + prot_bits |= __WRITEABLE; + mark_page_dirty(kvm, gfn); + kvm_set_pfn_dirty(pfn); + } + } + + if (vma_pagesize == PMD_SIZE) { + pmd_t new_pmd = pfn_pmd(pfn, __pgprot(prot_bits)); + + new_pmd = kvm_pmd_mkhuge(new_pmd); + + if (writeable && write) { + int i; + gfn_t base_gfn = (gpa & PMD_MASK) >> PAGE_SHIFT; + + for (i = 0; i < PTRS_PER_PTE; i++) + mark_page_dirty(kvm, base_gfn + i); + } + ++vcpu->stat.huge_set_exits; + ret = kvm_set_pmd_huge(vcpu, memcache, gpa, &new_pmd, vma, hva); + } else { + pte_t new_pte = pfn_pte(pfn, __pgprot(prot_bits)); + + if (writeable && write) + mark_page_dirty(kvm, gfn); + + /* Ensure page tables are allocated */ + ptep = kvm_pte_for_gpa(kvm, memcache, vma, hva, gpa); + set_pte(ptep, new_pte); + + err = 0; + if (out_entry) + *out_entry = new_pte; + if (out_buddy) + *out_buddy = *ptep_buddy(&new_pte); + } + + spin_unlock(&kvm->mmu_lock); + kvm_release_pfn_clean(pfn); + kvm_set_pfn_accessed(pfn); +out: + srcu_read_unlock(&kvm->srcu, srcu_idx); + return err; +} + +int kvm_handle_mm_fault(struct kvm_vcpu *vcpu, unsigned long badv, + bool write) +{ + int ret; + + ret = kvm_map_page(vcpu, badv, write, NULL, NULL); + if (ret) + return ret; + + /* Invalidate this entry in the TLB */ + return kvm_tlb_flush_gpa(vcpu, badv); +} + +/** + * kvm_flush_tlb_all() - Flush all root TLB entries for + * guests. + * + * Invalidate all entries including GVA-->GPA and GPA-->HPA mappings. + */ +void kvm_flush_tlb_all(void) +{ + unsigned long flags; + + local_irq_save(flags); + invtlb_all(INVTLB_ALLGID, 0, 0); + local_irq_restore(flags); +} diff --git a/arch/loongarch/kvm/timer.c b/arch/loongarch/kvm/timer.c new file mode 100644 index 000000000000..6fa063434ae2 --- /dev/null +++ b/arch/loongarch/kvm/timer.c @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kvmcpu.h" +#include "trace.h" +#include "kvm_compat.h" + +/* + * ktime_to_tick() - Scale ktime_t to a 64-bit stable timer. + * + * Caches the dynamic nanosecond bias in vcpu->arch.timer_dyn_bias. + */ +static u64 ktime_to_tick(struct kvm_vcpu *vcpu, ktime_t now) +{ + s64 now_ns, periods; + u64 delta; + + now_ns = ktime_to_ns(now); + delta = now_ns + vcpu->arch.timer_dyn_bias; + + if (delta >= vcpu->arch.timer_period) { + /* If delta is out of safe range the bias needs adjusting */ + periods = div64_s64(now_ns, vcpu->arch.timer_period); + vcpu->arch.timer_dyn_bias = -periods * vcpu->arch.timer_period; + /* Recalculate delta with new bias */ + delta = now_ns + vcpu->arch.timer_dyn_bias; + } + + /* + * We've ensured that: + * delta < timer_period + */ + return div_u64(delta * vcpu->arch.timer_mhz, MNSEC_PER_SEC); +} + +/** + * kvm_resume_hrtimer() - Resume hrtimer, updating expiry. + * @vcpu: Virtual CPU. + * @now: ktime at point of resume. + * @stable_timer: stable timer at point of resume. + * + * Resumes the timer and updates the timer expiry based on @now and @count. + */ +static void kvm_resume_hrtimer(struct kvm_vcpu *vcpu, ktime_t now, u64 stable_timer) +{ + u64 delta; + ktime_t expire; + + /* Stable timer decreased to zero or + * initialize to zero, set 4 second timer + */ + delta = div_u64(stable_timer * MNSEC_PER_SEC, vcpu->arch.timer_mhz); + expire = ktime_add_ns(now, delta); + + /* Update hrtimer to use new timeout */ + hrtimer_cancel(&vcpu->arch.swtimer); + hrtimer_start(&vcpu->arch.swtimer, expire, HRTIMER_MODE_ABS_PINNED); +} + +/** + * kvm_init_timer() - Initialise stable timer. + * @vcpu: Virtual CPU. + * @timer_hz: Frequency of timer. + * + * Initialise the timer to the specified frequency, zero it, and set it going if + * it's enabled. + */ +void kvm_init_timer(struct kvm_vcpu *vcpu, unsigned long timer_hz) +{ + ktime_t now; + unsigned long ticks; + struct loongarch_csrs *csr = vcpu->arch.csr; + + vcpu->arch.timer_mhz = timer_hz >> 20; + vcpu->arch.timer_period = div_u64((u64)MNSEC_PER_SEC * IOCSR_TIMER_MASK, vcpu->arch.timer_mhz); + vcpu->arch.timer_dyn_bias = 0; + + /* Starting at 0 */ + ticks = 0; + now = ktime_get(); + vcpu->arch.timer_bias = ticks - ktime_to_tick(vcpu, now); + vcpu->arch.timer_bias &= IOCSR_TIMER_MASK; + + kvm_write_sw_gcsr(csr, KVM_CSR_TVAL, ticks); +} + +/** + * kvm_count_timeout() - Push timer forward on timeout. + * @vcpu: Virtual CPU. + * + * Handle an hrtimer event by push the hrtimer forward a period. + * + * Returns: The hrtimer_restart value to return to the hrtimer subsystem. + */ +enum hrtimer_restart kvm_count_timeout(struct kvm_vcpu *vcpu) +{ + unsigned long timer_cfg; + + /* Add the Count period to the current expiry time */ + timer_cfg = kvm_read_sw_gcsr(vcpu->arch.csr, KVM_CSR_TCFG); + if (timer_cfg & KVM_TCFG_PERIOD) { + hrtimer_add_expires_ns(&vcpu->arch.swtimer, timer_cfg & KVM_TCFG_VAL); + return HRTIMER_RESTART; + } else + return HRTIMER_NORESTART; +} + +/* + * kvm_restore_timer() - Restore timer state. + * @vcpu: Virtual CPU. + * + * Restore soft timer state from saved context. + */ +void kvm_restore_timer(struct kvm_vcpu *vcpu) +{ + struct loongarch_csrs *csr = vcpu->arch.csr; + ktime_t saved_ktime, now; + u64 stable_timer, new_timertick = 0; + u64 delta = 0; + int expired = 0; + unsigned long timer_cfg; + + /* + * Set guest stable timer cfg csr + */ + timer_cfg = kvm_read_sw_gcsr(csr, KVM_CSR_TCFG); + kvm_restore_hw_gcsr(csr, KVM_CSR_ESTAT); + if (!(timer_cfg & KVM_TCFG_EN)) { + kvm_restore_hw_gcsr(csr, KVM_CSR_TCFG); + kvm_restore_hw_gcsr(csr, KVM_CSR_TVAL); + return; + } + + now = ktime_get(); + saved_ktime = vcpu->arch.stable_ktime_saved; + stable_timer = kvm_read_sw_gcsr(csr, KVM_CSR_TVAL); + + /*hrtimer not expire */ + delta = ktime_to_tick(vcpu, ktime_sub(now, saved_ktime)); + if (delta >= stable_timer) + expired = 1; + + if (expired) { + if (timer_cfg & KVM_TCFG_PERIOD) { + new_timertick = (delta - stable_timer) % (timer_cfg & KVM_TCFG_VAL); + } else { + new_timertick = 1; + } + } else { + new_timertick = stable_timer - delta; + } + + new_timertick &= KVM_TCFG_VAL; + kvm_write_gcsr_timercfg(timer_cfg); + kvm_write_gcsr_timertick(new_timertick); + if (expired) + _kvm_queue_irq(vcpu, LARCH_INT_TIMER); +} + +/* + * kvm_acquire_timer() - Switch to hard timer state. + * @vcpu: Virtual CPU. + * + * Restore hard timer state on top of existing soft timer state if possible. + * + * Since hard timer won't remain active over preemption, preemption should be + * disabled by the caller. + */ +void kvm_acquire_timer(struct kvm_vcpu *vcpu) +{ + unsigned long flags, guestcfg; + + guestcfg = kvm_read_csr_gcfg(); + if (!(guestcfg & KVM_GCFG_TIT)) + return; + + /* enable guest access to hard timer */ + kvm_write_csr_gcfg(guestcfg & ~KVM_GCFG_TIT); + + /* + * Freeze the soft-timer and sync the guest stable timer with it. We do + * this with interrupts disabled to avoid latency. + */ + local_irq_save(flags); + hrtimer_cancel(&vcpu->arch.swtimer); + local_irq_restore(flags); +} + + +/* + * _kvm_save_timer() - Switch to software emulation of guest timer. + * @vcpu: Virtual CPU. + * + * Save guest timer state and switch to software emulation of guest + * timer. The hard timer must already be in use, so preemption should be + * disabled. + */ +static ktime_t _kvm_save_timer(struct kvm_vcpu *vcpu, u64 *stable_timer) +{ + u64 end_stable_timer; + ktime_t before_time; + + before_time = ktime_get(); + + /* + * Record a final stable timer which we will transfer to the soft-timer. + */ + end_stable_timer = kvm_read_gcsr_timertick(); + *stable_timer = end_stable_timer; + + kvm_resume_hrtimer(vcpu, before_time, end_stable_timer); + return before_time; +} + +/* + * kvm_save_timer() - Save guest timer state. + * @vcpu: Virtual CPU. + * + * Save guest timer state and switch to soft guest timer if hard timer was in + * use. + */ +void kvm_save_timer(struct kvm_vcpu *vcpu) +{ + struct loongarch_csrs *csr = vcpu->arch.csr; + unsigned long guestcfg; + u64 stable_timer = 0; + ktime_t save_ktime; + + preempt_disable(); + guestcfg = kvm_read_csr_gcfg(); + if (!(guestcfg & KVM_GCFG_TIT)) { + /* disable guest use of hard timer */ + kvm_write_csr_gcfg(guestcfg | KVM_GCFG_TIT); + + /* save hard timer state */ + kvm_save_hw_gcsr(csr, KVM_CSR_TCFG); + if (kvm_read_sw_gcsr(csr, KVM_CSR_TCFG) & KVM_TCFG_EN) { + save_ktime = _kvm_save_timer(vcpu, &stable_timer); + kvm_write_sw_gcsr(csr, KVM_CSR_TVAL, stable_timer); + vcpu->arch.stable_ktime_saved = save_ktime; + if (stable_timer == IOCSR_TIMER_MASK) + _kvm_queue_irq(vcpu, LARCH_INT_TIMER); + } else { + kvm_save_hw_gcsr(csr, KVM_CSR_TVAL); + } + } + + /* save timer-related state to VCPU context */ + kvm_save_hw_gcsr(csr, KVM_CSR_ESTAT); + preempt_enable(); +} + +void kvm_reset_timer(struct kvm_vcpu *vcpu) +{ + kvm_write_gcsr_timercfg(0); + kvm_write_sw_gcsr(vcpu->arch.csr, KVM_CSR_TCFG, 0); + hrtimer_cancel(&vcpu->arch.swtimer); +} diff --git a/arch/loongarch/kvm/trace.h b/arch/loongarch/kvm/trace.h new file mode 100644 index 000000000000..d72d10cb39fd --- /dev/null +++ b/arch/loongarch/kvm/trace.h @@ -0,0 +1,201 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#if !defined(_TRACE_KVM_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_KVM_H + +#include +#include "kvm_compat.h" +#include "kvmcsr.h" + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM kvm +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE trace + +/* + * arch/loongarch/kvm/loongarch.c + */ +extern bool kvm_trace_guest_mode_change; +int kvm_guest_mode_change_trace_reg(void); +void kvm_guest_mode_change_trace_unreg(void); + +/* + * Tracepoints for VM enters + */ +DECLARE_EVENT_CLASS(kvm_transition, + TP_PROTO(struct kvm_vcpu *vcpu), + TP_ARGS(vcpu), + TP_STRUCT__entry( + __field(unsigned long, pc) + ), + + TP_fast_assign( + __entry->pc = vcpu->arch.pc; + ), + + TP_printk("PC: 0x%08lx", + __entry->pc) +); + +DEFINE_EVENT(kvm_transition, kvm_enter, + TP_PROTO(struct kvm_vcpu *vcpu), + TP_ARGS(vcpu)); + +DEFINE_EVENT(kvm_transition, kvm_reenter, + TP_PROTO(struct kvm_vcpu *vcpu), + TP_ARGS(vcpu)); + +DEFINE_EVENT(kvm_transition, kvm_out, + TP_PROTO(struct kvm_vcpu *vcpu), + TP_ARGS(vcpu)); + +/* The first 32 exit reasons correspond to Cause.ExcCode */ +#define KVM_TRACE_EXIT_INT 0 +#define KVM_TRACE_EXIT_TLBLD (EXCCODE_TLBL) +#define KVM_TRACE_EXIT_TLBST (EXCCODE_TLBS) +#define KVM_TRACE_EXIT_TLBI (EXCCODE_TLBI) +#define KVM_TRACE_EXIT_TLBMOD (EXCCODE_TLBM) +#define KVM_TRACE_EXIT_TLBRI (EXCCODE_TLBRI) +#define KVM_TRACE_EXIT_TLBXI (EXCCODE_TLBXI) +#define KVM_TRACE_EXIT_TLBPE (EXCCODE_TLBPE) +#define KVM_TRACE_EXIT_ADDE (EXCCODE_ADE) +#define KVM_TRACE_EXIT_UNALIGN (EXCCODE_ALE) +#define KVM_TRACE_EXIT_ODB (EXCCODE_OOB) +#define KVM_TRACE_EXIT_SYSCALL (EXCCODE_SYS) +#define KVM_TRACE_EXIT_BP (EXCCODE_BP) +#define KVM_TRACE_EXIT_INE (EXCCODE_INE) +#define KVM_TRACE_EXIT_IPE (EXCCODE_IPE) +#define KVM_TRACE_EXIT_FPDIS (EXCCODE_FPDIS) +#define KVM_TRACE_EXIT_LSXDIS (EXCCODE_LSXDIS) +#define KVM_TRACE_EXIT_LASXDIS (EXCCODE_LASXDIS) +#define KVM_TRACE_EXIT_FPE (EXCCODE_FPE) +#define KVM_TRACE_EXIT_WATCH (EXCCODE_WATCH) +#define KVM_TRACE_EXIT_GSPR (EXCCODE_GSPR) +#define KVM_TRACE_EXIT_HC (EXCCODE_HYP) +#define KVM_TRACE_EXIT_GCM (EXCCODE_GCM) + +/* Further exit reasons */ +#define KVM_TRACE_EXIT_IDLE 64 +#define KVM_TRACE_EXIT_CACHE 65 +#define KVM_TRACE_EXIT_SIGNAL 66 + +/* Tracepoints for VM exits */ +#define kvm_trace_symbol_exit_types \ + { KVM_TRACE_EXIT_INT, "Interrupt" }, \ + { KVM_TRACE_EXIT_TLBLD, "TLB (LD)" }, \ + { KVM_TRACE_EXIT_TLBST, "TLB (ST)" }, \ + { KVM_TRACE_EXIT_TLBI, "TLB Ifetch" }, \ + { KVM_TRACE_EXIT_TLBMOD, "TLB Mod" }, \ + { KVM_TRACE_EXIT_TLBRI, "TLB RI" }, \ + { KVM_TRACE_EXIT_TLBXI, "TLB XI" }, \ + { KVM_TRACE_EXIT_TLBPE, "TLB Previlege Error" },\ + { KVM_TRACE_EXIT_ADDE, "Address Error" }, \ + { KVM_TRACE_EXIT_UNALIGN, "Address unalign" }, \ + { KVM_TRACE_EXIT_ODB, "Out boundary" }, \ + { KVM_TRACE_EXIT_SYSCALL, "System Call" }, \ + { KVM_TRACE_EXIT_BP, "Breakpoint" }, \ + { KVM_TRACE_EXIT_INE, "Reserved Inst" }, \ + { KVM_TRACE_EXIT_IPE, "Inst prev error" }, \ + { KVM_TRACE_EXIT_FPDIS, "FPU disable" }, \ + { KVM_TRACE_EXIT_LSXDIS, "LSX disable" }, \ + { KVM_TRACE_EXIT_LASXDIS, "LASX disable" }, \ + { KVM_TRACE_EXIT_FPE, "FPE" }, \ + { KVM_TRACE_EXIT_WATCH, "DEBUG" }, \ + { KVM_TRACE_EXIT_GSPR, "GSPR" }, \ + { KVM_TRACE_EXIT_HC, "Hypercall" }, \ + { KVM_TRACE_EXIT_GCM, "CSR Mod" }, \ + { KVM_TRACE_EXIT_IDLE, "IDLE" }, \ + { KVM_TRACE_EXIT_CACHE, "CACHE" }, \ + { KVM_TRACE_EXIT_SIGNAL, "Signal" } + +TRACE_EVENT(kvm_exit, + TP_PROTO(struct kvm_vcpu *vcpu, unsigned int reason), + TP_ARGS(vcpu, reason), + TP_STRUCT__entry( + __field(unsigned long, pc) + __field(unsigned int, reason) + ), + + TP_fast_assign( + __entry->pc = vcpu->arch.pc; + __entry->reason = reason; + ), + + TP_printk("[%s]PC: 0x%08lx", + __print_symbolic(__entry->reason, + kvm_trace_symbol_exit_types), + __entry->pc) +); + +#define KVM_TRACE_AUX_RESTORE 0 +#define KVM_TRACE_AUX_SAVE 1 +#define KVM_TRACE_AUX_ENABLE 2 +#define KVM_TRACE_AUX_DISABLE 3 +#define KVM_TRACE_AUX_DISCARD 4 + +#define KVM_TRACE_AUX_FPU 1 +#define KVM_TRACE_AUX_LSX 2 +#define KVM_TRACE_AUX_FPU_LSX 3 +#define KVM_TRACE_AUX_LASX 4 +#define KVM_TRACE_AUX_FPU_LSX_LASX 7 + +#define kvm_trace_symbol_aux_op \ + { KVM_TRACE_AUX_RESTORE, "restore" }, \ + { KVM_TRACE_AUX_SAVE, "save" }, \ + { KVM_TRACE_AUX_ENABLE, "enable" }, \ + { KVM_TRACE_AUX_DISABLE, "disable" }, \ + { KVM_TRACE_AUX_DISCARD, "discard" } + +#define kvm_trace_symbol_aux_state \ + { KVM_TRACE_AUX_FPU, "FPU" }, \ + { KVM_TRACE_AUX_LSX, "LSX" }, \ + { KVM_TRACE_AUX_LASX, "LASX" }, \ + { KVM_TRACE_AUX_FPU_LSX, "FPU & LSX" }, \ + { KVM_TRACE_AUX_FPU_LSX_LASX, "FPU & LSX & LASX" } + +TRACE_EVENT(kvm_aux, + TP_PROTO(struct kvm_vcpu *vcpu, unsigned int op, + unsigned int state), + TP_ARGS(vcpu, op, state), + TP_STRUCT__entry( + __field(unsigned long, pc) + __field(u8, op) + __field(u8, state) + ), + + TP_fast_assign( + __entry->pc = vcpu->arch.pc; + __entry->op = op; + __entry->state = state; + ), + + TP_printk("%s %s PC: 0x%08lx", + __print_symbolic(__entry->op, + kvm_trace_symbol_aux_op), + __print_symbolic(__entry->state, + kvm_trace_symbol_aux_state), + __entry->pc) +); + +TRACE_EVENT(kvm_vpid_change, + TP_PROTO(struct kvm_vcpu *vcpu, unsigned long vpid), + TP_ARGS(vcpu, vpid), + TP_STRUCT__entry( + __field(unsigned long, vpid) + ), + + TP_fast_assign( + __entry->vpid = vpid; + ), + + TP_printk("vpid: 0x%08lx", + __entry->vpid) +); + +#endif /* _TRACE_KVM_H */ + +/* This part must be outside protection */ +#include -- Gitee From b48b23f2c1f2c4414534dd16a51dcc624d0add93 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 5 Jan 2023 17:01:56 +0800 Subject: [PATCH 46/56] LoongArch: Export symbol for function smp_send_reschedule() From upstream commit 143d64bdbdb85787953a70332f9e5f658b678550 Function smp_send_reschedule() is standard kernel API, which is defined in header file include/linux/smp.h. However, on LoongArch it is defined as an inline function, this is confusing and kernel modules can not use this function. Now we define smp_send_reschedule() as a general function, and add a EXPORT_SYMBOL_GPL on this function, so that kernel modules can use it. Signed-off-by: Bibo Mao Signed-off-by: Huacai Chen Change-Id: Iafc643ec9282b60ad211f86d66804f30f9932304 --- arch/loongarch/include/asm/smp.h | 10 ---------- arch/loongarch/kernel/smp.c | 11 +++++++++++ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/arch/loongarch/include/asm/smp.h b/arch/loongarch/include/asm/smp.h index 71189b28bfb2..92cfe1703135 100644 --- a/arch/loongarch/include/asm/smp.h +++ b/arch/loongarch/include/asm/smp.h @@ -78,16 +78,6 @@ extern void calculate_cpu_foreign_map(void); */ extern void show_ipi_list(struct seq_file *p, int prec); -/* - * This function sends a 'reschedule' IPI to another CPU. - * it goes straight through and wastes no time serializing - * anything. Worst case is that we lose a reschedule ... - */ -static inline void smp_send_reschedule(int cpu) -{ - loongson3_send_ipi_single(cpu, SMP_RESCHEDULE); -} - static inline void arch_send_call_function_single_ipi(int cpu) { loongson3_send_ipi_single(cpu, SMP_CALL_FUNCTION); diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c index 7e6dde83e6ac..4b7b42cf19bd 100644 --- a/arch/loongarch/kernel/smp.c +++ b/arch/loongarch/kernel/smp.c @@ -151,6 +151,17 @@ void loongson3_send_ipi_mask(const struct cpumask *mask, unsigned int action) ipi_write_action(cpu_logical_map(i), (u32)action); } +/* + * This function sends a 'reschedule' IPI to another CPU. + * it goes straight through and wastes no time serializing + * anything. Worst case is that we lose a reschedule ... + */ +void smp_send_reschedule(int cpu) +{ + loongson3_send_ipi_single(cpu, SMP_RESCHEDULE); +} +EXPORT_SYMBOL_GPL(smp_send_reschedule); + irqreturn_t loongson3_ipi_interrupt(int irq, void *dev) { unsigned int action; -- Gitee From 2dc12f32dbd82a01c9ed257b2a159f40e862eed1 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 6 Jan 2023 09:41:23 +0800 Subject: [PATCH 47/56] LoongArch: kvm: KVM support for 5.10 KVM adapts to 5.10 kernel based on 4.19 kernel KVM code. Signed-off-by: XiangLai Li Signed-off-by: Bibo Mao Change-Id: Iea4333d8e0905ab5c04c725defd0e4c421bfe916 --- arch/loongarch/Kbuild | 5 +- arch/loongarch/Kconfig | 19 ++ arch/loongarch/configs/loongson3_defconfig | 5 + arch/loongarch/include/asm/Kbuild | 1 - arch/loongarch/include/asm/inst.h | 103 ++++++- arch/loongarch/include/asm/kvm_host.h | 23 +- arch/loongarch/include/asm/kvm_para.h | 43 +++ arch/loongarch/kernel/asm-offsets.c | 32 ++ arch/loongarch/kvm/csr.c | 2 +- arch/loongarch/kvm/entry.S | 6 +- arch/loongarch/kvm/exit.c | 35 +-- arch/loongarch/kvm/hypcall.c | 21 +- arch/loongarch/kvm/intc/ls7a_irq.h | 1 - arch/loongarch/kvm/{ls_irq.h => irq.h} | 0 arch/loongarch/kvm/kvm_compat.c | 31 +- arch/loongarch/kvm/kvm_compat.h | 69 ++++- arch/loongarch/kvm/kvmcsr.h | 13 +- arch/loongarch/kvm/loongarch.c | 335 ++++++--------------- arch/loongarch/kvm/mmu.c | 178 +++++------ arch/loongarch/kvm/trace.h | 44 +-- include/uapi/linux/kvm.h | 30 ++ 21 files changed, 560 insertions(+), 436 deletions(-) create mode 100644 arch/loongarch/include/asm/kvm_para.h rename arch/loongarch/kvm/{ls_irq.h => irq.h} (100%) diff --git a/arch/loongarch/Kbuild b/arch/loongarch/Kbuild index 30d9afffc837..7760f9cb9777 100644 --- a/arch/loongarch/Kbuild +++ b/arch/loongarch/Kbuild @@ -1,7 +1,10 @@ obj-y += kernel/ obj-y += mm/ obj-y += vdso/ -obj-$(CONFIG_KVM) += kvm/ + +ifdef CONFIG_KVM +obj-y += kvm/ +endif # for cleaning subdir- += boot diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 34eff7c52582..5f01a3e4b851 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -505,6 +505,25 @@ config ARCH_ENABLE_THP_MIGRATION def_bool y depends on TRANSPARENT_HUGEPAGE +config PARAVIRT + bool "Enable paravirtualization code" + help + This changes the kernel so it can modify itself when it is run + under a hypervisor, potentially improving performance significantly + over full virtualization. + +config PARAVIRT_TIME_ACCOUNTING + bool "Paravirtual steal time accounting" + select PARAVIRT + default n + help + Select this option to enable fine granularity task steal time + accounting. Time spent executing other tasks in parallel with + the current vCPU is discounted from the vCPU power. To account for + that, there can be a small performance impact. + + If in doubt, say N here. + config ARCH_MEMORY_PROBE def_bool y depends on MEMORY_HOTPLUG diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index 63b38c184853..cd96f0607993 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -49,6 +49,11 @@ CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_LOONGSON3_ACPI_CPUFREQ=y CONFIG_EFI_CAPSULE_LOADER=m CONFIG_EFI_TEST=m +CONFIG_VIRTUALIZATION=y +CONFIG_KVM=m +CONFIG_VHOST_NET=m +CONFIG_VHOST_SCSI=m +CONFIG_VHOST_VSOCK=m CONFIG_MODULES=y CONFIG_MODULE_FORCE_LOAD=y CONFIG_MODULE_UNLOAD=y diff --git a/arch/loongarch/include/asm/Kbuild b/arch/loongarch/include/asm/Kbuild index a0eed6076c79..3e0c3e4e57ae 100644 --- a/arch/loongarch/include/asm/Kbuild +++ b/arch/loongarch/include/asm/Kbuild @@ -26,4 +26,3 @@ generic-y += poll.h generic-y += param.h generic-y += posix_types.h generic-y += resource.h -generic-y += kvm_para.h diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h index 7b07cbb3188c..235fa5c537cd 100644 --- a/arch/loongarch/include/asm/inst.h +++ b/arch/loongarch/include/asm/inst.h @@ -29,17 +29,18 @@ enum reg1i21_op { }; enum reg2i12_op { - addiw_op = 0x0a, - addid_op = 0x0b, - lu52id_op = 0x0c, - ldb_op = 0xa0, - ldh_op = 0xa1, - ldw_op = 0xa2, - ldd_op = 0xa3, - stb_op = 0xa4, - sth_op = 0xa5, - stw_op = 0xa6, - std_op = 0xa7, + slti_op = 0x8, sltui_op, addiw_op, addid_op, + lu52id_op, cache_op = 0x18, xvldreplb_op = 0xca, + ldb_op = 0xa0, ldh_op, ldw_op, ldd_op, stb_op, sth_op, + stw_op, std_op, ldbu_op, ldhu_op, ldwu_op, preld_op, + flds_op, fsts_op, fldd_op, fstd_op, vld_op, vst_op, xvld_op, + xvst_op, ldlw_op = 0xb8, ldrw_op, ldld_op, ldrd_op, stlw_op, + strw_op, stld_op, strd_op, vldreplb_op = 0xc2, +}; + +enum reg2i14_op { + llw_op = 0x20, scw_op, lld_op, scd_op, ldptrw_op, stptrw_op, + ldptrd_op, stptrd_op, }; enum reg2i16_op { @@ -52,6 +53,49 @@ enum reg2i16_op { bgeu_op = 0x1b, }; +enum reg3_op { + asrtled_op = 0x2, asrtgtd_op, + addw_op = 0x20, addd_op, subw_op, subd_op, + slt_op, sltu_op, maskeqz_op, masknez_op, + nor_op, and_op, or_op, xor_op, orn_op, + andn_op, sllw_op, srlw_op, sraw_op, slld_op, + srld_op, srad_op, rotrb_op, rotrh_op, + rotrw_op, rotrd_op, mulw_op, mulhw_op, + mulhwu_op, muld_op, mulhd_op, mulhdu_op, + mulwdw_op, mulwdwu_op, divw_op, modw_op, + divwu_op, modwu_op, divd_op, modd_op, + divdu_op, moddu_op, crcwbw_op, + crcwhw_op, crcwww_op, crcwdw_op, crccwbw_op, + crccwhw_op, crccwww_op, crccwdw_op, addu12iw_op, + addu12id_op, + adcb_op = 0x60, adch_op, adcw_op, adcd_op, + sbcb_op, sbch_op, sbcw_op, sbcd_op, + rcrb_op, rcrh_op, rcrw_op, rcrd_op, + ldxb_op = 0x7000, ldxh_op = 0x7008, ldxw_op = 0x7010, ldxd_op = 0x7018, + stxb_op = 0x7020, stxh_op = 0x7028, stxw_op = 0x7030, stxd_op = 0x7038, + ldxbu_op = 0x7040, ldxhu_op = 0x7048, ldxwu_op = 0x7050, + preldx_op = 0x7058, fldxs_op = 0x7060, fldxd_op = 0x7068, + fstxs_op = 0x7070, fstxd_op = 0x7078, vldx_op = 0x7080, + vstx_op = 0x7088, xvldx_op = 0x7090, xvstx_op = 0x7098, + amswapw_op = 0x70c0, amswapd_op, amaddw_op, amaddd_op, amandw_op, + amandd_op, amorw_op, amord_op, amxorw_op, amxord_op, ammaxw_op, + ammaxd_op, amminw_op, ammind_op, ammaxwu_op, ammaxdu_op, + amminwu_op, ammindu_op, amswap_dbw_op, amswap_dbd_op, amadd_dbw_op, + amadd_dbd_op, amand_dbw_op, amand_dbd_op, amor_dbw_op, amor_dbd_op, + amxor_dbw_op, amxor_dbd_op, ammax_dbw_op, ammax_dbd_op, ammin_dbw_op, + ammin_dbd_op, ammax_dbwu_op, ammax_dbdu_op, ammin_dbwu_op, + ammin_dbdu_op, fldgts_op = 0x70e8, fldgtd_op, + fldles_op, fldled_op, fstgts_op, fstgtd_op, fstles_op, fstled_op, + ldgtb_op, ldgth_op, ldgtw_op, ldgtd_op, ldleb_op, ldleh_op, ldlew_op, + ldled_op, stgtb_op, stgth_op, stgtw_op, stgtd_op, stleb_op, stleh_op, + stlew_op, stled_op, +}; + +enum reg2_op { + iocsrrdb_op = 0x19200, iocsrrdh_op, iocsrrdw_op, iocsrrdd_op, + iocsrwrb_op, iocsrwrh_op, iocsrwrw_op, iocsrwrd_op, +}; + struct reg0i26_format { unsigned int immediate_h : 10; unsigned int immediate_l : 16; @@ -71,6 +115,12 @@ struct reg1i21_format { unsigned int opcode : 6; }; +struct reg2_format { + unsigned int rd : 5; + unsigned int rj : 5; + unsigned int opcode : 22; +}; + struct reg2i12_format { unsigned int rd : 5; unsigned int rj : 5; @@ -78,6 +128,18 @@ struct reg2i12_format { unsigned int opcode : 10; }; +struct reg2i14_format { + unsigned int rd : 5; + unsigned int rj : 5; + unsigned int simmediate : 14; + unsigned int opcode : 8; +}; + +struct reg0i15_format { + unsigned int simmediate : 15; + unsigned int opcode : 17; +}; + struct reg2i16_format { unsigned int rd : 5; unsigned int rj : 5; @@ -85,13 +147,32 @@ struct reg2i16_format { unsigned int opcode : 6; }; +struct reg3_format { + unsigned int rd : 5; + unsigned int rj : 5; + unsigned int rk : 5; + unsigned int opcode : 17; +}; + +struct reg2csr_format { + unsigned int rd : 5; + unsigned int rj : 5; + unsigned int csr : 14; + unsigned int opcode : 8; +}; + union loongarch_instruction { unsigned int word; struct reg0i26_format reg0i26_format; struct reg1i20_format reg1i20_format; struct reg1i21_format reg1i21_format; + struct reg3_format reg3_format; + struct reg2_format reg2_format; struct reg2i12_format reg2i12_format; + struct reg2i14_format reg2i14_format; struct reg2i16_format reg2i16_format; + struct reg2csr_format reg2csr_format; + struct reg0i15_format reg0i15_format; }; #define LOONGARCH_INSN_SIZE sizeof(union loongarch_instruction) diff --git a/arch/loongarch/include/asm/kvm_host.h b/arch/loongarch/include/asm/kvm_host.h index 56319843ddfd..8f181f8cfa8c 100644 --- a/arch/loongarch/include/asm/kvm_host.h +++ b/arch/loongarch/include/asm/kvm_host.h @@ -76,6 +76,8 @@ struct kvm_vcpu_stat { u64 huge_merge_exits; u64 halt_successful_poll; u64 halt_attempted_poll; + u64 halt_poll_success_ns; + u64 halt_poll_fail_ns; u64 halt_poll_invalid; u64 halt_wakeup; }; @@ -158,23 +160,6 @@ enum emulation_result { EMULATE_DO_IOCSR, /* handle IOCSR request */ }; -#define KVM_NR_MEM_OBJS 4 -/* - * We don't want allocation failures within the mmu code, so we preallocate - * enough memory for a single page fault in a cache. - */ -struct kvm_mmu_memory_cache { - int nobjs; - void *objects[KVM_NR_MEM_OBJS]; -}; - -#if defined(CONFIG_CPU_HAS_LASX) -#define FPU_ALIGN __aligned(32) -#elif defined(CONFIG_CPU_HAS_LSX) -#define FPU_ALIGN __aligned(16) -#else -#define FPU_ALIGN -#endif #define KVM_LARCH_FPU (0x1 << 0) #define KVM_LARCH_LSX (0x1 << 1) #define KVM_LARCH_LASX (0x1 << 2) @@ -316,7 +301,7 @@ enum _kvm_fault_result { #define KVM_ARCH_WANT_MMU_NOTIFIER int kvm_unmap_hva_range(struct kvm *kvm, unsigned long start, unsigned long end, bool blockable); -void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte); +int kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte); int kvm_age_hva(struct kvm *kvm, unsigned long start, unsigned long end); int kvm_test_age_hva(struct kvm *kvm, unsigned long hva); @@ -344,7 +329,7 @@ static inline bool kvm_is_ifetch_fault(struct kvm_vcpu_arch *arch) static inline void kvm_arch_hardware_unsetup(void) {} static inline void kvm_arch_sync_events(struct kvm *kvm) {} static inline void kvm_arch_free_memslot(struct kvm *kvm, - struct kvm_memory_slot *free, struct kvm_memory_slot *dont) {} + struct kvm_memory_slot *slot) {} static inline void kvm_arch_memslots_updated(struct kvm *kvm, u64 gen) {} static inline void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu) {} static inline void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu) {} diff --git a/arch/loongarch/include/asm/kvm_para.h b/arch/loongarch/include/asm/kvm_para.h new file mode 100644 index 000000000000..bd9bb0bf0f70 --- /dev/null +++ b/arch/loongarch/include/asm/kvm_para.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_LOONGARCH_KVM_PARA_H +#define _ASM_LOONGARCH_KVM_PARA_H + +/* + * Hypcall code field + */ +#define KVM_HC_CODE_SERIVCE 0x0 +#define KVM_HC_CODE_SWDBG 0x5 +/* + * function id + * 0x00000 ~ 0xfffff Standard Hypervisor Calls + */ +#define KVM_HC_FUNC_FEATURE 0x0 +#define KVM_HC_FUNC_NOTIFY 0x1 +#define KVM_HC_FUNC_IPI 0x2 +/* + * LoongArch support PV feature list + */ +#define KVM_FEATURE_STEAL_TIME 0 +#define KVM_FEATURE_MULTI_IPI 1 +/* + * LoongArch hypcall return code + */ +#define KVM_RET_SUC 1 +#define KVM_RET_NOT_SUPPORTED -1 + +static inline bool kvm_check_and_clear_guest_paused(void) +{ + return false; +} + +static inline unsigned int kvm_arch_para_features(void) +{ + return 0; +} + +static inline unsigned int kvm_arch_para_hints(void) +{ + return 0; +} + +#endif /* _ASM_LOONGARCH_KVM_PARA_H */ diff --git a/arch/loongarch/kernel/asm-offsets.c b/arch/loongarch/kernel/asm-offsets.c index 8733fc347b3e..1a955da153ad 100644 --- a/arch/loongarch/kernel/asm-offsets.c +++ b/arch/loongarch/kernel/asm-offsets.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -269,3 +270,34 @@ void output_pbe_defines(void) } #endif +void output_kvm_defines(void) +{ + COMMENT(" KVM/LOONGISA Specific offsets. "); + + OFFSET(VCPU_FCSR0, kvm_vcpu_arch, fpu.fcsr); + OFFSET(VCPU_FCC, kvm_vcpu_arch, fpu.fcc); + BLANK(); + + OFFSET(KVM_VCPU_ARCH, kvm_vcpu, arch); + OFFSET(KVM_VCPU_KVM, kvm_vcpu, kvm); + OFFSET(KVM_VCPU_RUN, kvm_vcpu, run); + BLANK(); + + OFFSET(KVM_ARCH_HSTACK, kvm_vcpu_arch, host_stack); + OFFSET(KVM_ARCH_HGP, kvm_vcpu_arch, host_gp); + OFFSET(KVM_ARCH_HANDLE_EXIT, kvm_vcpu_arch, handle_exit); + OFFSET(KVM_ARCH_HPGD, kvm_vcpu_arch, host_pgd); + OFFSET(KVM_ARCH_GEENTRY, kvm_vcpu_arch, guest_eentry); + OFFSET(KVM_ARCH_GPC, kvm_vcpu_arch, pc); + OFFSET(KVM_ARCH_GGPR, kvm_vcpu_arch, gprs); + OFFSET(KVM_ARCH_HESTAT, kvm_vcpu_arch, host_estat); + OFFSET(KVM_ARCH_HBADV, kvm_vcpu_arch, badv); + OFFSET(KVM_ARCH_HBADI, kvm_vcpu_arch, badi); + OFFSET(KVM_ARCH_ISHYPCALL, kvm_vcpu_arch, is_hypcall); + OFFSET(KVM_ARCH_HECFG, kvm_vcpu_arch, host_ecfg); + OFFSET(KVM_ARCH_HEENTRY, kvm_vcpu_arch, host_eentry); + OFFSET(KVM_ARCH_HPERCPU, kvm_vcpu_arch, host_percpu); + + OFFSET(KVM_GPGD, kvm, arch.gpa_mm.pgd); + BLANK(); +} diff --git a/arch/loongarch/kvm/csr.c b/arch/loongarch/kvm/csr.c index 763b00719469..17d236a6de22 100644 --- a/arch/loongarch/kvm/csr.c +++ b/arch/loongarch/kvm/csr.c @@ -9,9 +9,9 @@ #include "kvmcpu.h" #include "intc/ls3a_ipi.h" #include "intc/ls3a_ext_irq.h" -#include "ls_irq.h" #include "kvm_compat.h" #include "kvmcsr.h" +#include "irq.h" #define CASE_READ_SW_GCSR(csr, regid, csrid) \ do { \ diff --git a/arch/loongarch/kvm/entry.S b/arch/loongarch/kvm/entry.S index 5a51a1b026e9..cc0856af60d9 100644 --- a/arch/loongarch/kvm/entry.S +++ b/arch/loongarch/kvm/entry.S @@ -221,7 +221,7 @@ SYM_FUNC_START(kvm_exit_entry) or a2, s1, zero KVM_LONG_ADDI a2, a2, KVM_VCPU_ARCH - andi t0, v0, RESUME_HOST + andi t0, a0, RESUME_HOST bnez t0, ret_to_host INT_S zero, a2, KVM_ARCH_ISHYPCALL @@ -236,8 +236,8 @@ ret_to_guest: ret_to_host: KVM_LONG_L a2, a2, KVM_ARCH_HSTACK addi.d a2, a2, -PT_SIZE - srai.w a3, v0, 2 - or v0, a3, zero + srai.w a3, a0, 2 + or a0, a3, zero kvm_restore_host_gpr a2 jirl zero, ra, 0 SYM_FUNC_END(kvm_exit_entry) diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c index c6abcc1e884f..74082a2fba7f 100644 --- a/arch/loongarch/kvm/exit.c +++ b/arch/loongarch/kvm/exit.c @@ -18,11 +18,8 @@ #include #include #include -#include #include "kvmcpu.h" #include -#include - #include "trace.h" #include "kvm_compat.h" #include "kvmcsr.h" @@ -459,27 +456,27 @@ static int _kvm_handle_debug(struct kvm_vcpu *vcpu) kvm_csr_writeq(fwps, KVM_CSR_FWPS); if (mwps & 0xff) kvm_csr_writeq(mwps, KVM_CSR_MWPS); - vcpu->run->debug.arch.exception = EXCCODE_WATCH; + vcpu->run->debug.arch.exception = KVM_EXCCODE_WATCH; vcpu->run->debug.arch.fwps = fwps; vcpu->run->debug.arch.mwps = mwps; vcpu->run->exit_reason = KVM_EXIT_DEBUG; return RESUME_HOST; } -static exit_handle_fn _kvm_fault_tables[EXCCODE_INT_START] = { - [EXCCODE_TLBL] = _kvm_handle_read_fault, - [EXCCODE_TLBS] = _kvm_handle_write_fault, - [EXCCODE_TLBI] = _kvm_handle_read_fault, - [EXCCODE_TLBM] = _kvm_handle_write_fault, - [EXCCODE_TLBRI] = _kvm_handle_read_fault, - [EXCCODE_TLBXI] = _kvm_handle_read_fault, - [EXCCODE_FPDIS] = _kvm_handle_fpu_disabled, - [EXCCODE_LSXDIS] = _kvm_handle_lsx_disabled, - [EXCCODE_LASXDIS] = _kvm_handle_lasx_disabled, - [EXCCODE_WATCH] = _kvm_handle_debug, - [EXCCODE_GSPR] = _kvm_handle_gspr, - [EXCCODE_HYP] = _kvm_handle_hypcall, - [EXCCODE_GCM] = _kvm_handle_gcm, +static exit_handle_fn _kvm_fault_tables[KVM_INT_START] = { + [KVM_EXCCODE_TLBL] = _kvm_handle_read_fault, + [KVM_EXCCODE_TLBS] = _kvm_handle_write_fault, + [KVM_EXCCODE_TLBI] = _kvm_handle_read_fault, + [KVM_EXCCODE_TLBM] = _kvm_handle_write_fault, + [KVM_EXCCODE_TLBRI] = _kvm_handle_read_fault, + [KVM_EXCCODE_TLBXI] = _kvm_handle_read_fault, + [KVM_EXCCODE_FPDIS] = _kvm_handle_fpu_disabled, + [KVM_EXCCODE_LSXDIS] = _kvm_handle_lsx_disabled, + [KVM_EXCCODE_LASXDIS] = _kvm_handle_lasx_disabled, + [KVM_EXCCODE_WATCH] = _kvm_handle_debug, + [KVM_EXCCODE_GSPR] = _kvm_handle_gspr, + [KVM_EXCCODE_HYP] = _kvm_handle_hypcall, + [KVM_EXCCODE_GCM] = _kvm_handle_gcm, }; int _kvm_handle_fault(struct kvm_vcpu *vcpu, int fault) @@ -491,7 +488,7 @@ void _kvm_init_fault(void) { int i; - for (i = 0; i < EXCCODE_INT_START; i++) + for (i = 0; i < KVM_INT_START; i++) if (!_kvm_fault_tables[i]) _kvm_fault_tables[i] = _kvm_fault_ni; } diff --git a/arch/loongarch/kvm/hypcall.c b/arch/loongarch/kvm/hypcall.c index 180d110a08a7..aaf3a07f23f0 100644 --- a/arch/loongarch/kvm/hypcall.c +++ b/arch/loongarch/kvm/hypcall.c @@ -7,8 +7,9 @@ #include #include #include -#include #include "intc/ls3a_ipi.h" +#include "kvm_compat.h" + int kvm_virt_ipi(struct kvm_vcpu *vcpu) { @@ -16,9 +17,9 @@ int kvm_virt_ipi(struct kvm_vcpu *vcpu) u64 ipi_bitmap; unsigned int min, action, cpu; - ipi_bitmap = vcpu->arch.gprs[REG_A1]; - min = vcpu->arch.gprs[REG_A2]; - action = vcpu->arch.gprs[REG_A3]; + ipi_bitmap = vcpu->arch.gprs[KVM_REG_A1]; + min = vcpu->arch.gprs[KVM_REG_A2]; + action = vcpu->arch.gprs[KVM_REG_A3]; if (ipi_bitmap) { cpu = find_first_bit((void *)&ipi_bitmap, BITS_PER_LONG); @@ -37,9 +38,9 @@ int kvm_save_notify(struct kvm_vcpu *vcpu) int ret = 0; - num = vcpu->arch.gprs[REG_A0]; - id = vcpu->arch.gprs[REG_A1]; - data = vcpu->arch.gprs[REG_A2]; + num = vcpu->arch.gprs[KVM_REG_A0]; + id = vcpu->arch.gprs[KVM_REG_A1]; + data = vcpu->arch.gprs[KVM_REG_A2]; switch (id) { case KVM_FEATURE_STEAL_TIME: @@ -59,7 +60,7 @@ int kvm_save_notify(struct kvm_vcpu *vcpu) static int _kvm_pv_feature(struct kvm_vcpu *vcpu) { - int feature = vcpu->arch.gprs[REG_A1]; + int feature = vcpu->arch.gprs[KVM_REG_A1]; int ret = KVM_RET_NOT_SUPPORTED; switch (feature) { case KVM_FEATURE_STEAL_TIME: @@ -80,7 +81,7 @@ static int _kvm_pv_feature(struct kvm_vcpu *vcpu) */ int _kvm_handle_pv_hcall(struct kvm_vcpu *vcpu) { - unsigned long func = vcpu->arch.gprs[REG_A0]; + unsigned long func = vcpu->arch.gprs[KVM_REG_A0]; int hyp_ret = KVM_RET_NOT_SUPPORTED; switch (func) { @@ -98,7 +99,7 @@ int _kvm_handle_pv_hcall(struct kvm_vcpu *vcpu) break; }; - vcpu->arch.gprs[REG_V0] = hyp_ret; + vcpu->arch.gprs[KVM_REG_A0] = hyp_ret; return RESUME_GUEST; } diff --git a/arch/loongarch/kvm/intc/ls7a_irq.h b/arch/loongarch/kvm/intc/ls7a_irq.h index fad85d55240d..0c91b63bf88f 100644 --- a/arch/loongarch/kvm/intc/ls7a_irq.h +++ b/arch/loongarch/kvm/intc/ls7a_irq.h @@ -10,7 +10,6 @@ #include #include #include -#include #include #include "kvmcpu.h" diff --git a/arch/loongarch/kvm/ls_irq.h b/arch/loongarch/kvm/irq.h similarity index 100% rename from arch/loongarch/kvm/ls_irq.h rename to arch/loongarch/kvm/irq.h diff --git a/arch/loongarch/kvm/kvm_compat.c b/arch/loongarch/kvm/kvm_compat.c index a8931cf0ab47..277d7760aa11 100644 --- a/arch/loongarch/kvm/kvm_compat.c +++ b/arch/loongarch/kvm/kvm_compat.c @@ -21,7 +21,7 @@ void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte) return; } -#else +#elif (LINUX_VERSION_CODE == KERNEL_VERSION(5, 4, 0)) int kvm_arch_check_processor_compat(void) { return 0; @@ -54,5 +54,32 @@ int kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte) { return _kvm_set_spte_hva(kvm, hva, pte); } -#endif +#else +int kvm_arch_check_processor_compat(void *opaque) +{ + return 0; +} +int kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte) +{ + return _kvm_set_spte_hva(kvm, hva, pte); +} + +void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot) +{ + +} + +int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id) +{ + return 0; +} + +void kvm_arch_flush_remote_tlbs_memslot(struct kvm *kvm, + struct kvm_memory_slot *memslot) +{ +#ifndef CONFIG_HAVE_KVM_ARCH_TLB_FLUSH_ALL + kvm_flush_remote_tlbs (kvm); +#endif +} +#endif diff --git a/arch/loongarch/kvm/kvm_compat.h b/arch/loongarch/kvm/kvm_compat.h index ff68ab1d8aba..6cefd8d2ae4a 100644 --- a/arch/loongarch/kvm/kvm_compat.h +++ b/arch/loongarch/kvm/kvm_compat.h @@ -8,9 +8,62 @@ #include #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) #include +#else +#include #endif #endif +#define KVM_REG_A0 0x4 +#define KVM_REG_A1 0x5 +#define KVM_REG_A2 0x6 +#define KVM_REG_A3 0x7 +/* + * ExStatus.ExcCode + */ +#define KVM_EXCCODE_RSV 0 /* Reserved */ +#define KVM_EXCCODE_TLBL 1 /* TLB miss on a load */ +#define KVM_EXCCODE_TLBS 2 /* TLB miss on a store */ +#define KVM_EXCCODE_TLBI 3 /* TLB miss on a ifetch */ +#define KVM_EXCCODE_TLBM 4 /* TLB modified fault */ +#define KVM_EXCCODE_TLBRI 5 /* TLB Read-Inhibit exception */ +#define KVM_EXCCODE_TLBXI 6 /* TLB Execution-Inhibit exception */ +#define KVM_EXCCODE_TLBPE 7 /* TLB Privilege Error */ +#define KVM_EXCCODE_ADE 8 /* Address Error */ +#define KVM_EXCCODE_ALE 9 /* Unalign Access */ +#define KVM_EXCCODE_OOB 10 /* Out of bounds */ +#define KVM_EXCCODE_SYS 11 /* System call */ +#define KVM_EXCCODE_BP 12 /* Breakpoint */ +#define KVM_EXCCODE_INE 13 /* Inst. Not Exist */ +#define KVM_EXCCODE_IPE 14 /* Inst. Privileged Error */ +#define KVM_EXCCODE_FPDIS 15 /* FPU Disabled */ +#define KVM_EXCCODE_LSXDIS 16 /* LSX Disabled */ +#define KVM_EXCCODE_LASXDIS 17 /* LASX Disabled */ +#define KVM_EXCCODE_FPE 18 /* Floating Point Exception */ +#define KVM_EXCCODE_WATCH 19 /* Watch address reference */ +#define KVM_EXCCODE_BTDIS 20 /* Binary Trans. Disabled */ +#define KVM_EXCCODE_BTE 21 /* Binary Trans. Exception */ +#define KVM_EXCCODE_GSPR 22 /* Guest Privileged Error */ +#define KVM_EXCCODE_HYP 23 /* Hypercall */ +#define KVM_EXCCODE_GCM 24 /* Guest CSR modified */ + +#define KVM_INT_START 64 +#define KVM_INT_SIP0 64 +#define KVM_INT_SIP1 65 +#define KVM_INT_IP0 66 +#define KVM_INT_IP1 67 +#define KVM_INT_IP2 68 +#define KVM_INT_IP3 69 +#define KVM_INT_IP4 70 +#define KVM_INT_IP5 71 +#define KVM_INT_IP6 72 +#define KVM_INT_IP7 73 +#define KVM_INT_PC 74 /* Performance Counter */ +#define KVM_INT_TIMER 75 +#define KVM_INT_IPI 76 +#define KVM_INT_NMI 77 +#define KVM_INT_END 78 +#define KVM_INT_NUM (KVM_INT_END - KVM_INT_START) + #define KVM_CSR_CRMD 0x0 /* Current mode info */ #define KVM_CRMD_WE_SHIFT 9 #define KVM_CRMD_WE (_ULCAST_(0x1) << KVM_CRMD_WE_SHIFT) @@ -223,6 +276,14 @@ #define KVM_CSR_PERFCNTR2 0x205 /* 64 perf event 2 count value */ #define KVM_CSR_PERFCTRL3 0x206 /* 32 perf event 3 config */ #define KVM_CSR_PERFCNTR3 0x207 /* 64 perf event 3 count value */ +#define KVM_PERFCTRL_PLV0 (_ULCAST_(1) << 16) +#define KVM_PERFCTRL_PLV1 (_ULCAST_(1) << 17) +#define KVM_PERFCTRL_PLV2 (_ULCAST_(1) << 18) +#define KVM_PERFCTRL_PLV3 (_ULCAST_(1) << 19) +#define KVM_PERFCTRL_IE (_ULCAST_(1) << 20) +#define KVM_PERFCTRL_GMOD (_ULCAST_(3) << 21) +#define KVM_PERFCTRL_EVENT 0x3ff + #define KVM_CSR_MWPC 0x300 /* data breakpoint config */ #define KVM_CSR_MWPS 0x301 /* data breakpoint status */ #define KVM_CSR_FWPC 0x380 /* instruction breakpoint config */ @@ -652,10 +713,10 @@ kvm_change_csr_gstat(unsigned long change, unsigned long val) { unsigned long res, new; - res = read_csr_gstat(); + res = kvm_read_csr_gstat(); new = res & ~change; new |= (val & change); - write_csr_gstat(new); + kvm_write_csr_gstat(new); return res; } @@ -665,10 +726,10 @@ kvm_change_csr_gcfg(unsigned long change, unsigned long val) { unsigned long res, new; - res = read_csr_gcfg(); + res = kvm_read_csr_gcfg(); new = res & ~change; new |= (val & change); - write_csr_gcfg(new); + kvm_write_csr_gcfg(new); return res; } diff --git a/arch/loongarch/kvm/kvmcsr.h b/arch/loongarch/kvm/kvmcsr.h index 303303a8a245..24a84a3f72cd 100644 --- a/arch/loongarch/kvm/kvmcsr.h +++ b/arch/loongarch/kvm/kvmcsr.h @@ -7,7 +7,6 @@ #define __LOONGARCH_KVM_CSR_H__ #include #include "kvmcpu.h" -#include #include #include @@ -92,17 +91,17 @@ int _kvm_init_iocsr(struct kvm *kvm); int _kvm_set_iocsr(struct kvm *kvm, struct kvm_iocsr_entry *__user argp); int _kvm_get_iocsr(struct kvm *kvm, struct kvm_iocsr_entry *__user argp); -#define KVM_PMU_PLV_ENABLE (CSR_PERFCTRL_PLV0 | \ - CSR_PERFCTRL_PLV1 | \ - CSR_PERFCTRL_PLV2 | \ - CSR_PERFCTRL_PLV3) +#define KVM_PMU_PLV_ENABLE (KVM_PERFCTRL_PLV0 | \ + KVM_PERFCTRL_PLV1 | \ + KVM_PERFCTRL_PLV2 | \ + KVM_PERFCTRL_PLV3) #define CASE_WRITE_HW_PMU(vcpu, csr, id, csrid, v) \ do { \ if (csrid == id) { \ if (v & KVM_PMU_PLV_ENABLE) { \ - kvm_write_csr_gcfg(kvm_read_csr_gcfg() | CSR_GCFG_GPERF); \ - kvm_write_hw_gcsr(csr, csrid, v | CSR_PERFCTRL_GMOD); \ + kvm_write_csr_gcfg(kvm_read_csr_gcfg() | KVM_GCFG_GPERF); \ + kvm_write_hw_gcsr(csr, csrid, v | KVM_PERFCTRL_GMOD); \ vcpu->arch.aux_inuse |= KVM_LARCH_PERF; \ return ; \ } else { \ diff --git a/arch/loongarch/kvm/loongarch.c b/arch/loongarch/kvm/loongarch.c index 2268176f5cc6..2159007d0601 100644 --- a/arch/loongarch/kvm/loongarch.c +++ b/arch/loongarch/kvm/loongarch.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -29,7 +28,6 @@ #include "kvmcpu.h" #include #include -#include #include "intc/ls3a_ipi.h" #include "intc/ls7a_irq.h" @@ -44,59 +42,55 @@ #define KVM_LOONGARCH_VERSION 1 #define CREATE_TRACE_POINTS #include "trace.h" -#define VCPU_STAT(x) offsetof(struct kvm_vcpu, stat.x) -#define VM_STAT(x) offsetof(struct kvm, stat.x) struct kvm_stats_debugfs_item vcpu_debugfs_entries[] = { - { "idle", VCPU_STAT(idle_exits), KVM_STAT_VCPU}, - { "signal", VCPU_STAT(signal_exits), KVM_STAT_VCPU}, - { "interrupt", VCPU_STAT(int_exits), KVM_STAT_VCPU}, - { "tlbmiss_ld", VCPU_STAT(excep_exits[EXCCODE_TLBL]), KVM_STAT_VCPU}, - { "tlbmiss_st", VCPU_STAT(excep_exits[EXCCODE_TLBS]), KVM_STAT_VCPU}, - { "tlb_ifetch", VCPU_STAT(excep_exits[EXCCODE_TLBI]), KVM_STAT_VCPU}, - { "tlbmod", VCPU_STAT(excep_exits[EXCCODE_TLBM]), KVM_STAT_VCPU}, - { "tlbri", VCPU_STAT(excep_exits[EXCCODE_TLBRI]), KVM_STAT_VCPU}, - { "tlbxi", VCPU_STAT(excep_exits[EXCCODE_TLBXI]), KVM_STAT_VCPU}, - { "fp_dis", VCPU_STAT(excep_exits[EXCCODE_FPDIS]), KVM_STAT_VCPU}, - { "lsx_dis", VCPU_STAT(excep_exits[EXCCODE_LSXDIS]), KVM_STAT_VCPU}, - { "lasx_dis", VCPU_STAT(excep_exits[EXCCODE_LASXDIS]), KVM_STAT_VCPU}, - { "fpe", VCPU_STAT(excep_exits[EXCCODE_FPE]), KVM_STAT_VCPU}, - { "watch", VCPU_STAT(excep_exits[EXCCODE_WATCH]), KVM_STAT_VCPU}, - { "gspr", VCPU_STAT(excep_exits[EXCCODE_GSPR]), KVM_STAT_VCPU}, - { "gcm", VCPU_STAT(excep_exits[EXCCODE_GCM]), KVM_STAT_VCPU}, - { "hc", VCPU_STAT(excep_exits[EXCCODE_HYP]), KVM_STAT_VCPU}, - - { "rdcsr_cpu_feature", VCPU_STAT(rdcsr_cpu_feature_exits), KVM_STAT_VCPU }, - { "rdcsr_misc_func", VCPU_STAT(rdcsr_misc_func_exits), KVM_STAT_VCPU }, - { "rdcsr_ipi_access", VCPU_STAT(rdcsr_ipi_access_exits), KVM_STAT_VCPU }, - { "cpucfg", VCPU_STAT(cpucfg_exits), KVM_STAT_VCPU }, - { "huge_dec", VCPU_STAT(huge_dec_exits), KVM_STAT_VCPU }, - { "huge_thp", VCPU_STAT(huge_thp_exits), KVM_STAT_VCPU }, - { "huge_adj", VCPU_STAT(huge_adjust_exits), KVM_STAT_VCPU }, - { "huge_set", VCPU_STAT(huge_set_exits), KVM_STAT_VCPU }, - { "huge_merg", VCPU_STAT(huge_merge_exits), KVM_STAT_VCPU }, - - { "halt_successful_poll", VCPU_STAT(halt_successful_poll), KVM_STAT_VCPU }, - { "halt_attempted_poll", VCPU_STAT(halt_attempted_poll), KVM_STAT_VCPU }, - { "halt_poll_invalid", VCPU_STAT(halt_poll_invalid), KVM_STAT_VCPU }, - { "halt_wakeup", VCPU_STAT(halt_wakeup), KVM_STAT_VCPU }, + VCPU_STAT("idle", idle_exits), + VCPU_STAT("signal", signal_exits), + VCPU_STAT("interrupt", int_exits), + VCPU_STAT("rdcsr_cpu_feature", rdcsr_cpu_feature_exits), + VCPU_STAT("rdcsr_misc_func", rdcsr_misc_func_exits), + VCPU_STAT("rdcsr_ipi_access", rdcsr_ipi_access_exits), + VCPU_STAT("cpucfg", cpucfg_exits), + VCPU_STAT("huge_dec", huge_dec_exits), + VCPU_STAT("huge_thp", huge_thp_exits), + VCPU_STAT("huge_adj", huge_adjust_exits), + VCPU_STAT("huge_set", huge_set_exits), + VCPU_STAT("huge_merg", huge_merge_exits), + VCPU_STAT("halt_successful_poll", halt_successful_poll), + VCPU_STAT("halt_attempted_poll", halt_attempted_poll), + VCPU_STAT("halt_poll_invalid", halt_poll_invalid), + VCPU_STAT("halt_wakeup", halt_wakeup), + VCPU_STAT("tlbmiss_ld", excep_exits[KVM_EXCCODE_TLBL]), + VCPU_STAT("tlbmiss_st", excep_exits[KVM_EXCCODE_TLBS]), + VCPU_STAT("tlb_ifetch", excep_exits[KVM_EXCCODE_TLBI]), + VCPU_STAT("tlbmod", excep_exits[KVM_EXCCODE_TLBM]), + VCPU_STAT("tlbri", excep_exits[KVM_EXCCODE_TLBRI]), + VCPU_STAT("tlbxi", excep_exits[KVM_EXCCODE_TLBXI]), + VCPU_STAT("fp_dis", excep_exits[KVM_EXCCODE_FPDIS]), + VCPU_STAT("lsx_dis", excep_exits[KVM_EXCCODE_LSXDIS]), + VCPU_STAT("lasx_dis", excep_exits[KVM_EXCCODE_LASXDIS]), + VCPU_STAT("fpe", excep_exits[KVM_EXCCODE_FPE]), + VCPU_STAT("watch", excep_exits[KVM_EXCCODE_WATCH]), + VCPU_STAT("gspr", excep_exits[KVM_EXCCODE_GSPR]), + VCPU_STAT("gcm", excep_exits[KVM_EXCCODE_GCM]), + VCPU_STAT("hc", excep_exits[KVM_EXCCODE_HYP]), {NULL} }; struct kvm_stats_debugfs_item debugfs_entries[] = { - { "remote_tlb_flush", VM_STAT(remote_tlb_flush), KVM_STAT_VM }, - { "pip_read_exits", VM_STAT(pip_read_exits), KVM_STAT_VM }, - { "pip_write_exits", VM_STAT(pip_write_exits), KVM_STAT_VM }, - { "vm_ioctl_irq_line", VM_STAT(vm_ioctl_irq_line), KVM_STAT_VM }, - { "ls7a_ioapic_update", VM_STAT(ls7a_ioapic_update), KVM_STAT_VM }, - { "ls7a_ioapic_set_irq", VM_STAT(ls7a_ioapic_set_irq), KVM_STAT_VM }, - { "ls7a_msi_irq", VM_STAT(ls7a_msi_irq), KVM_STAT_VM }, - { "ioapic_reg_write", VM_STAT(ioapic_reg_write), KVM_STAT_VM }, - { "ioapic_reg_read", VM_STAT(ioapic_reg_read), KVM_STAT_VM }, - { "set_ls7a_ioapic", VM_STAT(set_ls7a_ioapic), KVM_STAT_VM }, - { "get_ls7a_ioapic", VM_STAT(get_ls7a_ioapic), KVM_STAT_VM }, - { "set_ls3a_ext_irq", VM_STAT(set_ls3a_ext_irq), KVM_STAT_VM }, - { "get_ls3a_ext_irq", VM_STAT(get_ls3a_ext_irq), KVM_STAT_VM }, - { "ls3a_ext_irq", VM_STAT(trigger_ls3a_ext_irq), KVM_STAT_VM }, + VM_STAT("remote_tlb_flush", remote_tlb_flush), + VM_STAT("pip_read_exits", pip_read_exits), + VM_STAT("pip_write_exits", pip_write_exits), + VM_STAT("vm_ioctl_irq_line", vm_ioctl_irq_line), + VM_STAT("ls7a_ioapic_update", ls7a_ioapic_update), + VM_STAT("ls7a_ioapic_set_irq", ls7a_ioapic_set_irq), + VM_STAT("ls7a_msi_irq", ls7a_msi_irq), + VM_STAT("ioapic_reg_write", ioapic_reg_write), + VM_STAT("ioapic_reg_read", ioapic_reg_read), + VM_STAT("set_ls7a_ioapic", set_ls7a_ioapic), + VM_STAT("get_ls7a_ioapic", get_ls7a_ioapic), + VM_STAT("set_ls3a_ext_irq", set_ls3a_ext_irq), + VM_STAT("get_ls3a_ext_irq", get_ls3a_ext_irq), + VM_STAT("ls3a_ext_irq", trigger_ls3a_ext_irq), {NULL} }; @@ -249,14 +243,10 @@ int kvm_arch_hardware_enable(void) * TOE=0: Trap on Exception. * TIT=0: Trap on Timer. */ - if (cpu_has_gcip_all) - gcfg |= KVM_GCFG_GCI_SECURE; - if (cpu_has_matc_root) - gcfg |= KVM_GCFG_MATC_ROOT; - + gcfg |= KVM_GCFG_GCI_SECURE; + gcfg |= KVM_GCFG_MATC_ROOT; gcfg |= KVM_GCFG_TIT; kvm_write_csr_gcfg(gcfg); - kvm_flush_tlb_all(); /* Enable using TGID */ @@ -278,7 +268,7 @@ void kvm_arch_hardware_disable(void) kvm_flush_tlb_all(); } -int kvm_arch_hardware_setup(void) +int kvm_arch_hardware_setup(void *opaque) { return 0; } @@ -299,56 +289,17 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) return 0; } -static int lvcpu_stat_get(void *address, u64 *val) -{ - *val = *(u64 *)address; - return 0; -} -DEFINE_SIMPLE_ATTRIBUTE(lvcpu_stat_fops, lvcpu_stat_get, NULL, "%llu\n"); - -static int vcpu_pid_get(void *arg, u64 *val) -{ - struct kvm_vcpu *vcpu = (struct kvm_vcpu *)arg; - if (vcpu) - *val = pid_vnr(vcpu->pid); - return 0; -} -DEFINE_SIMPLE_ATTRIBUTE(vcpu_pid_fops, vcpu_pid_get, NULL, "%llu\n"); - -bool kvm_arch_has_vcpu_debugfs(void) -{ - return true; -} - -int kvm_arch_create_vcpu_debugfs(struct kvm_vcpu *vcpu) -{ - struct kvm_stats_debugfs_item *p; - debugfs_create_file("pid", 0444, vcpu->debugfs_dentry, vcpu, &vcpu_pid_fops); - for (p = vcpu_debugfs_entries; p->name && p->kind == KVM_STAT_VCPU; ++p) { - debugfs_create_file(p->name, 0444, vcpu->debugfs_dentry, - (void *)vcpu + p->offset, &lvcpu_stat_fops); - } - - return 0; -} - static void kvm_free_vcpus(struct kvm *kvm) { unsigned int i; struct kvm_vcpu *vcpu; kvm_for_each_vcpu(i, vcpu, kvm) { - kvm_arch_vcpu_free(vcpu); - } - - mutex_lock(&kvm->lock); - - for (i = 0; i < atomic_read(&kvm->online_vcpus); i++) + kvm_vcpu_destroy(vcpu); kvm->vcpus[i] = NULL; + } atomic_set(&kvm->online_vcpus, 0); - - mutex_unlock(&kvm->lock); } void kvm_arch_destroy_vm(struct kvm *kvm) @@ -547,71 +498,82 @@ static int _kvm_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu) return ret; } -struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id) +/* low level hrtimer wake routine */ +static enum hrtimer_restart kvm_swtimer_wakeup(struct hrtimer *timer) { - int err; - struct kvm_vcpu *vcpu = kzalloc(sizeof(struct kvm_vcpu), GFP_KERNEL); + struct kvm_vcpu *vcpu; - if (!vcpu) { - err = -ENOMEM; - goto out; - } + vcpu = container_of(timer, struct kvm_vcpu, arch.swtimer); + _kvm_queue_irq(vcpu, LARCH_INT_TIMER); + kvm_vcpu_wake_up(vcpu); + return kvm_count_timeout(vcpu); +} - err = kvm_vcpu_init(vcpu, kvm, id); +static void _kvm_vcpu_init(struct kvm_vcpu *vcpu) +{ + int i; - if (err) - goto out_free_cpu; + for_each_possible_cpu(i) + vcpu->arch.vpid[i] = 0; - kvm->arch.online_vcpus = id + 1; + hrtimer_init(&vcpu->arch.swtimer, CLOCK_MONOTONIC, + HRTIMER_MODE_ABS_PINNED); + vcpu->arch.swtimer.function = kvm_swtimer_wakeup; + vcpu->arch.fpu_enabled = true; + vcpu->arch.lsx_enabled = true; +} +int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) +{ vcpu->arch.host_eentry = kvm_csr_readq(KVM_CSR_EENTRY); vcpu->arch.guest_eentry = (unsigned long)kvm_exception_entry; vcpu->arch.vcpu_run = kvm_enter_guest; vcpu->arch.handle_exit = _kvm_handle_exit; - vcpu->arch.csr = kzalloc(sizeof(struct loongarch_csrs), GFP_KERNEL); + vcpu->arch.host_ecfg = (kvm_read_csr_ecfg() & KVM_ECFG_VS); + /* * kvm all exceptions share one exception entry, and host <-> guest switch * also switch excfg.VS field, keep host excfg.VS info here */ - vcpu->arch.host_ecfg = (kvm_read_csr_ecfg() & KVM_ECFG_VS); - + vcpu->arch.csr = kzalloc(sizeof(struct loongarch_csrs), GFP_KERNEL); if (!vcpu->arch.csr) { - err = -ENOMEM; - goto out_uninit_cpu; + return -ENOMEM; } /* Init */ vcpu->arch.last_sched_cpu = -1; vcpu->arch.last_exec_cpu = -1; + _kvm_vcpu_init(vcpu); + return 0; +} - return vcpu; - -out_uninit_cpu: - kvm_vcpu_uninit(vcpu); - -out_free_cpu: - kfree(vcpu); +static void _kvm_vcpu_uninit(struct kvm_vcpu *vcpu) +{ + int cpu; + struct kvm_context *context; -out: - return ERR_PTR(err); + /* + * If the VCPU is freed and reused as another VCPU, we don't want the + * matching pointer wrongly hanging around in last_vcpu. + */ + for_each_possible_cpu(cpu) { + context = per_cpu_ptr(vcpu->kvm->arch.vmcs, cpu); + if (context->last_vcpu == vcpu) + context->last_vcpu = NULL; + } } -void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu) +void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) { struct gfn_to_pfn_cache *cache = &vcpu->arch.st.cache; - hrtimer_cancel(&vcpu->arch.swtimer); + _kvm_vcpu_uninit(vcpu); - kvm_vcpu_uninit(vcpu); - kvm_mmu_free_memory_caches(vcpu); - kvm_release_pfn(cache->pfn, cache->dirty, cache); + hrtimer_cancel(&vcpu->arch.swtimer); + kvm_mmu_free_memory_cache(&vcpu->arch.mmu_page_cache); + if (vcpu->arch.st.guest_addr) + kvm_release_pfn(cache->pfn, cache->dirty, cache); kfree(vcpu->arch.csr); - kfree(vcpu); -} - -void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) -{ - kvm_arch_vcpu_free(vcpu); } #define KVM_GUESTDBG_VALID_MASK (KVM_GUESTDBG_ENABLE | \ KVM_GUESTDBG_USE_SW_BP | KVM_GUESTDBG_SINGLESTEP) @@ -634,10 +596,11 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, return ret; } -int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) +int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) { int r = -EINTR; int cpu; + struct kvm_run *run = vcpu->run; vcpu_load(vcpu); @@ -649,7 +612,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) vcpu->mmio_needed = 0; } else if (vcpu->arch.is_hypcall) { /* set return value for hypercall v0 register */ - vcpu->arch.gprs[REG_V0] = run->hypercall.ret; + vcpu->arch.gprs[KVM_REG_A0] = run->hypercall.ret; vcpu->arch.is_hypcall = 0; } @@ -1479,54 +1442,6 @@ long kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl, return r; } -/** - * kvm_vm_ioctl_get_dirty_log - get and clear the log of dirty pages in a slot - * @kvm: kvm instance - * @log: slot id and address to which we copy the log - * - * Steps 1-4 below provide general overview of dirty page logging. See - * kvm_get_dirty_log_protect() function description for additional details. - * - * We call kvm_get_dirty_log_protect() to handle steps 1-3, upon return we - * always flush the TLB (step 4) even if previous step failed and the dirty - * bitmap may be corrupt. Regardless of previous outcome the KVM logging API - * does not preclude user space subsequent dirty log read. Flushing TLB ensures - * writes will be marked dirty for next log read. - * - * 1. Take a snapshot of the bit and clear it if needed. - * 2. Write protect the corresponding page. - * 3. Copy the snapshot to the userspace. - * 4. Flush TLB's if needed. - */ -int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log) -{ - struct kvm_memslots *slots; - struct kvm_memory_slot *memslot; - bool is_dirty = false; - int r; - - mutex_lock(&kvm->slots_lock); - - r = kvm_get_dirty_log_protect(kvm, log, &is_dirty); - - if (is_dirty) { - slots = kvm_memslots(kvm); - memslot = id_to_memslot(slots, log->slot); - - /* - * FIXME: disable THP to improve vm migration success ratio, - * how to know migration failure to enable THP again - */ - memslot->arch.flags |= KVM_MEMSLOT_DISABLE_THP; - - /* Let implementation handle TLB/GVA invalidation */ - kvm_flush_remote_tlbs(kvm); - } - - mutex_unlock(&kvm->slots_lock); - return r; -} - long kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { struct kvm *kvm = filp->private_data; @@ -1792,7 +1707,7 @@ int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu) { return _kvm_pending_timer(vcpu) || kvm_read_hw_gcsr(KVM_CSR_ESTAT) & - (1 << (EXCCODE_TIMER - EXCCODE_INT_START)); + (1 << (KVM_INT_TIMER - KVM_INT_START)); } int kvm_arch_vcpu_dump_regs(struct kvm_vcpu *vcpu) @@ -1854,56 +1769,6 @@ int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) return 0; } -static void kvm_swtimer_func(unsigned long data) -{ - struct kvm_vcpu *vcpu = (struct kvm_vcpu *)data; - - _kvm_queue_irq(vcpu, LARCH_INT_TIMER); - if (swq_has_sleeper(&vcpu->wq)) - swake_up_one(&vcpu->wq); -} - -/* low level hrtimer wake routine */ -static enum hrtimer_restart kvm_swtimer_wakeup(struct hrtimer *timer) -{ - struct kvm_vcpu *vcpu; - - vcpu = container_of(timer, struct kvm_vcpu, arch.swtimer); - kvm_swtimer_func((unsigned long) vcpu); - return kvm_count_timeout(vcpu); -} - -int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) -{ - int i; - - for_each_possible_cpu(i) - vcpu->arch.vpid[i] = 0; - - hrtimer_init(&vcpu->arch.swtimer, CLOCK_MONOTONIC, - HRTIMER_MODE_ABS_PINNED); - vcpu->arch.swtimer.function = kvm_swtimer_wakeup; - vcpu->arch.fpu_enabled = true; - vcpu->arch.lsx_enabled = true; - return 0; -} - -void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) -{ - int cpu; - struct kvm_context *context; - - /* - * If the VCPU is freed and reused as another VCPU, we don't want the - * matching pointer wrongly hanging around in last_vcpu. - */ - for_each_possible_cpu(cpu) { - context = per_cpu_ptr(vcpu->kvm->arch.vmcs, cpu); - if (context->last_vcpu == vcpu) - context->last_vcpu = NULL; - } -} - int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu, struct kvm_translation *tr) { diff --git a/arch/loongarch/kvm/mmu.c b/arch/loongarch/kvm/mmu.c index 8ddfb0e8a89a..ba9fa7895d25 100644 --- a/arch/loongarch/kvm/mmu.c +++ b/arch/loongarch/kvm/mmu.c @@ -32,6 +32,24 @@ static int kvm_tlb_flush_gpa(struct kvm_vcpu *vcpu, unsigned long gpa) return 0; } +static inline int kvm_pmd_huge(pmd_t pmd) +{ +#ifdef CONFIG_LOONGARCH_HUGE_TLB_SUPPORT + return (pmd_val(pmd) & _PAGE_HUGE) != 0; +#else + return 0; +#endif +} + +static inline int kvm_pud_huge(pud_t pud) +{ +#ifdef CONFIG_LOONGARCH_HUGE_TLB_SUPPORT + return (pud_val(pud) & _PAGE_HUGE) != 0; +#else + return 0; +#endif +} + static inline pmd_t kvm_pmd_mkhuge(pmd_t pmd) { #ifdef CONFIG_LOONGARCH_HUGE_TLB_SUPPORT @@ -80,54 +98,6 @@ static inline pmd_t kvm_pmd_mkold(pmd_t pmd) return pmd; } -static int mmu_topup_memory_cache(struct kvm_mmu_memory_cache *cache, - int min, int max) -{ - void *page; - - BUG_ON(max > KVM_NR_MEM_OBJS); - if (cache->nobjs >= min) - return 0; - while (cache->nobjs < max) { - page = (void *)__get_free_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - cache->objects[cache->nobjs++] = page; - } - return 0; -} - -static void mmu_free_memory_cache(struct kvm_mmu_memory_cache *mc) -{ - while (mc->nobjs) - free_page((unsigned long)mc->objects[--mc->nobjs]); -} - -static void *mmu_memory_cache_alloc(struct kvm_mmu_memory_cache *mc, - struct vm_area_struct *vma, unsigned long hva) -{ - void *p = NULL; - struct mempolicy *pol = __get_vma_policy(vma, hva); - struct page *page; - - - BUG_ON(!mc || !mc->nobjs); - if (pol) { - page = alloc_page_vma(GFP_KERNEL_ACCOUNT | GFP_ATOMIC, vma, hva); - if (page) - p = page_address(page); - } - if (!p) - p = mc->objects[--mc->nobjs]; - - return p; -} - -void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu) -{ - mmu_free_memory_cache(&vcpu->arch.mmu_page_cache); -} - /** * kvm_pgd_alloc() - Allocate and initialise a KVM GPA page directory. * @@ -170,6 +140,7 @@ static pte_t *kvm_walk_pgd(pgd_t *pgd, struct kvm_mmu_memory_cache *cache, struct vm_area_struct *vma, unsigned long hva, unsigned long addr) { + p4d_t *p4d; pud_t *pud; pmd_t *pmd; @@ -179,32 +150,31 @@ static pte_t *kvm_walk_pgd(pgd_t *pgd, struct kvm_mmu_memory_cache *cache, BUG(); return NULL; } - pud = pud_offset(pgd, addr); + p4d = p4d_offset(pgd, addr); + pud = pud_offset(p4d, addr); if (pud_none(*pud)) { pmd_t *new_pmd; if (!cache) return NULL; - new_pmd = mmu_memory_cache_alloc(cache, vma, hva); + new_pmd = kvm_mmu_memory_cache_alloc(cache); pmd_init(new_pmd); pud_populate(NULL, pud, new_pmd); } pmd = pmd_offset(pud, addr); -#ifdef CONFIG_LOONGARCH_HUGE_TLB_SUPPORT - if (pmd_huge(*pmd)) { + if (kvm_pmd_huge(*pmd)) { return (pte_t *)pmd; } -#endif if (pmd_none(*pmd)) { pte_t *new_pte; if (!cache) return NULL; - new_pte = mmu_memory_cache_alloc(cache, vma, hva); + new_pte = kvm_mmu_memory_cache_alloc(cache); clear_page(new_pte); pmd_populate_kernel(NULL, pmd, new_pte); } - return pte_offset(pmd, addr); + return pte_offset_kernel(pmd, addr); } /* Caller must hold kvm->mm_lock */ @@ -225,8 +195,8 @@ static pte_t *kvm_pte_for_gpa(struct kvm *kvm, static bool kvm_flush_gpa_pte(pte_t *pte, unsigned long start_gpa, unsigned long end_gpa, unsigned long *data) { - int i_min = __pte_offset(start_gpa); - int i_max = __pte_offset(end_gpa); + int i_min = pte_index(start_gpa); + int i_max = pte_index(end_gpa); bool safe_to_remove = (i_min == 0 && i_max == PTRS_PER_PTE - 1); int i; @@ -246,8 +216,8 @@ static bool kvm_flush_gpa_pmd(pmd_t *pmd, unsigned long start_gpa, { pte_t *pte; unsigned long end = ~0ul; - int i_min = __pmd_offset(start_gpa); - int i_max = __pmd_offset(end_gpa); + int i_min = pmd_index(start_gpa); + int i_max = pmd_index(end_gpa); bool safe_to_remove = (i_min == 0 && i_max == PTRS_PER_PMD - 1); int i; @@ -255,14 +225,14 @@ static bool kvm_flush_gpa_pmd(pmd_t *pmd, unsigned long start_gpa, if (!pmd_present(pmd[i])) continue; - if (pmd_huge(pmd[i]) && pmd_present(pmd[i])) { + if (kvm_pmd_huge(pmd[i]) && pmd_present(pmd[i])) { pmd_clear(pmd + i); if (data) *data += PTRS_PER_PMD; continue; } - pte = pte_offset(pmd + i, 0); + pte = pte_offset_kernel(pmd + i, 0); if (i == i_max) end = end_gpa; @@ -281,8 +251,8 @@ static bool kvm_flush_gpa_pud(pud_t *pud, unsigned long start_gpa, { pmd_t *pmd; unsigned long end = ~0ul; - int i_min = __pud_offset(start_gpa); - int i_max = __pud_offset(end_gpa); + int i_min = pud_index(start_gpa); + int i_max = pud_index(end_gpa); bool safe_to_remove = (i_min == 0 && i_max == PTRS_PER_PUD - 1); int i; @@ -307,6 +277,7 @@ static bool kvm_flush_gpa_pud(pud_t *pud, unsigned long start_gpa, static bool kvm_flush_gpa_pgd(pgd_t *pgd, unsigned long start_gpa, unsigned long end_gpa, unsigned long *data) { + p4d_t *p4d; pud_t *pud; unsigned long end = ~0ul; int i_min = pgd_index(start_gpa); @@ -318,7 +289,8 @@ static bool kvm_flush_gpa_pgd(pgd_t *pgd, unsigned long start_gpa, if (!pgd_present(pgd[i])) continue; - pud = pud_offset(pgd + i, 0); + p4d = p4d_offset(pgd + i, 0); + pud = pud_offset(p4d, 0); if (i == i_max) end = end_gpa; @@ -361,8 +333,8 @@ static bool kvm_flush_gpa_pt(struct kvm *kvm, gfn_t start_gfn, gfn_t end_gfn, vo static int kvm_mkclean_pte(pte_t *pte, unsigned long start, unsigned long end) { int ret = 0; - int i_min = __pte_offset(start); - int i_max = __pte_offset(end); + int i_min = pte_index(start); + int i_max = pte_index(end); int i; pte_t val; @@ -381,8 +353,8 @@ static int kvm_mkclean_pmd(pmd_t *pmd, unsigned long start, unsigned long end) int ret = 0; pte_t *pte; unsigned long cur_end = ~0ul; - int i_min = __pmd_offset(start); - int i_max = __pmd_offset(end); + int i_min = pmd_index(start); + int i_max = pmd_index(end); int i; pmd_t old, new; @@ -390,7 +362,7 @@ static int kvm_mkclean_pmd(pmd_t *pmd, unsigned long start, unsigned long end) if (!pmd_present(pmd[i])) continue; - if (pmd_huge(pmd[i])) { + if (kvm_pmd_huge(pmd[i])) { old = pmd[i]; new = kvm_pmd_mkclean(old); if (pmd_val(new) == pmd_val(old)) @@ -400,7 +372,7 @@ static int kvm_mkclean_pmd(pmd_t *pmd, unsigned long start, unsigned long end) continue; } - pte = pte_offset(pmd + i, 0); + pte = pte_offset_kernel(pmd + i, 0); if (i == i_max) cur_end = end; @@ -415,8 +387,8 @@ static int kvm_mkclean_pud(pud_t *pud, unsigned long start, unsigned long end) int ret = 0; pmd_t *pmd; unsigned long cur_end = ~0ul; - int i_min = __pud_offset(start); - int i_max = __pud_offset(end); + int i_min = pud_index(start); + int i_max = pud_index(end); int i; for (i = i_min; i <= i_max; ++i, start = 0) { @@ -435,6 +407,7 @@ static int kvm_mkclean_pud(pud_t *pud, unsigned long start, unsigned long end) static int kvm_mkclean_pgd(pgd_t *pgd, unsigned long start, unsigned long end) { int ret = 0; + p4d_t *p4d; pud_t *pud; unsigned long cur_end = ~0ul; int i_min = pgd_index(start); @@ -445,7 +418,8 @@ static int kvm_mkclean_pgd(pgd_t *pgd, unsigned long start, unsigned long end) if (!pgd_present(pgd[i])) continue; - pud = pud_offset(pgd + i, 0); + p4d = p4d_offset(pgd + i, 0); + pud = pud_offset(p4d, 0); if (i == i_max) cur_end = end; @@ -495,20 +469,22 @@ void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm, gfn_t end = base_gfn + __fls(mask); kvm_mkclean_gpa_pt(kvm, start, end); + + /* + * FIXME: disable THP to improve vm migration success ratio, + * how to know migration failure to enable THP again + */ + slot->arch.flags |= KVM_MEMSLOT_DISABLE_THP; } void kvm_arch_commit_memory_region(struct kvm *kvm, const struct kvm_userspace_memory_region *mem, - const struct kvm_memory_slot *old, + struct kvm_memory_slot *old, const struct kvm_memory_slot *new, enum kvm_mr_change change) { int needs_flush; - kvm_debug("%s: kvm: %p slot: %d, GPA: %llx, size: %llx, QVA: %llx\n", - __func__, kvm, mem->slot, mem->guest_phys_addr, - mem->memory_size, mem->userspace_addr); - /* * If dirty page logging is enabled, write protect all pages in the slot * ready for dirty logging. @@ -579,8 +555,8 @@ static int kvm_mkold_pte(pte_t *pte, unsigned long start, unsigned long end) { int ret = 0; - int i_min = __pte_offset(start); - int i_max = __pte_offset(end); + int i_min = pte_index(start); + int i_max = pte_index(end); int i; pte_t old, new; @@ -604,8 +580,8 @@ static int kvm_mkold_pmd(pmd_t *pmd, unsigned long start, unsigned long end) int ret = 0; pte_t *pte; unsigned long cur_end = ~0ul; - int i_min = __pmd_offset(start); - int i_max = __pmd_offset(end); + int i_min = pmd_index(start); + int i_max = pmd_index(end); int i; pmd_t old, new; @@ -613,7 +589,7 @@ static int kvm_mkold_pmd(pmd_t *pmd, unsigned long start, unsigned long end) if (!pmd_present(pmd[i])) continue; - if (pmd_huge(pmd[i])) { + if (kvm_pmd_huge(pmd[i])) { old = pmd[i]; new = kvm_pmd_mkold(old); if (pmd_val(new) == pmd_val(old)) @@ -623,7 +599,7 @@ static int kvm_mkold_pmd(pmd_t *pmd, unsigned long start, unsigned long end) continue; } - pte = pte_offset(pmd + i, 0); + pte = pte_offset_kernel(pmd + i, 0); if (i == i_max) cur_end = end; @@ -638,8 +614,8 @@ static int kvm_mkold_pud(pud_t *pud, unsigned long start, unsigned long end) int ret = 0; pmd_t *pmd; unsigned long cur_end = ~0ul; - int i_min = __pud_offset(start); - int i_max = __pud_offset(end); + int i_min = pud_index(start); + int i_max = pud_index(end); int i; for (i = i_min; i <= i_max; ++i, start = 0) { @@ -659,6 +635,7 @@ static int kvm_mkold_pud(pud_t *pud, unsigned long start, unsigned long end) static int kvm_mkold_pgd(pgd_t *pgd, unsigned long start, unsigned long end) { int ret = 0; + p4d_t *p4d; pud_t *pud; unsigned long cur_end = ~0ul; int i_min = pgd_index(start); @@ -669,7 +646,8 @@ static int kvm_mkold_pgd(pgd_t *pgd, unsigned long start, unsigned long end) if (!pgd_present(pgd[i])) continue; - pud = pud_offset(pgd + i, 0); + p4d = p4d_offset(pgd + i, 0); + pud = pud_offset(p4d, 0); if (i == i_max) cur_end = end; @@ -813,6 +791,7 @@ static pud_t *kvm_get_pud(struct kvm *kvm, struct kvm_mmu_memory_cache *cache, phys_addr_t addr) { pgd_t *pgd; + p4d_t *p4d; pgd = kvm->arch.gpa_mm.pgd + pgd_index(addr); if (pgd_none(*pgd)) { @@ -821,7 +800,8 @@ static pud_t *kvm_get_pud(struct kvm *kvm, return NULL; } - return pud_offset(pgd, addr); + p4d = p4d_offset(pgd, addr); + return pud_offset(p4d, addr); } static pmd_t *kvm_get_pmd(struct kvm *kvm, @@ -832,13 +812,13 @@ static pmd_t *kvm_get_pmd(struct kvm *kvm, pmd_t *pmd; pud = kvm_get_pud(kvm, cache, addr); - if (!pud || pud_huge(*pud)) + if (!pud || kvm_pud_huge(*pud)) return NULL; if (pud_none(*pud)) { if (!cache) return NULL; - pmd = mmu_memory_cache_alloc(cache, vma, hva); + pmd = kvm_mmu_memory_cache_alloc(cache); pmd_init(pmd); pud_populate(NULL, pud, pmd); } @@ -885,7 +865,7 @@ static int kvm_set_pmd_huge(struct kvm_vcpu *vcpu, struct kvm_mmu_memory_cache * Normal THP split/merge follows mmu_notifier callbacks and do * get handled accordingly. */ - if (!pmd_huge(old_pmd)) { + if (!kvm_pmd_huge(old_pmd)) { ++vcpu->stat.huge_merge_exits; kvm_flush_gpa_pt(vcpu->kvm, (addr & PMD_MASK) >> PAGE_SHIFT, @@ -1077,7 +1057,7 @@ static int kvm_map_page_fast(struct kvm_vcpu *vcpu, unsigned long gpa, /* Track dirtying of writeable pages */ set_pte(ptep, pte_mkdirty(*ptep)); pfn = pte_pfn(*ptep); - if (pmd_huge(*((pmd_t *)ptep))) { + if (kvm_pmd_huge(*((pmd_t *)ptep))) { int i; gfn_t base_gfn = (gpa & PMD_MASK) >> PAGE_SHIFT; @@ -1149,7 +1129,7 @@ static int kvm_map_page(struct kvm_vcpu *vcpu, unsigned long gpa, /* Try the fast path to handle old / clean pages */ srcu_idx = srcu_read_lock(&kvm->srcu); - if ((exccode != EXCCODE_TLBRI) && (exccode != EXCCODE_TLBXI)) { + if ((exccode != KVM_EXCCODE_TLBRI) && (exccode != KVM_EXCCODE_TLBXI)) { err = kvm_map_page_fast(vcpu, gpa, write, out_entry, out_buddy); if (!err) @@ -1162,11 +1142,11 @@ static int kvm_map_page(struct kvm_vcpu *vcpu, unsigned long gpa, goto out; /* Let's check if we will get back a huge page backed by hugetlbfs */ - down_read(¤t->mm->mmap_sem); + mmap_read_lock(current->mm); vma = find_vma_intersection(current->mm, hva, hva + 1); if (unlikely(!vma)) { kvm_err("Failed to find VMA for hva 0x%lx\n", hva); - up_read(¤t->mm->mmap_sem); + mmap_read_unlock(current->mm); err = -EFAULT; goto out; } @@ -1182,12 +1162,10 @@ static int kvm_map_page(struct kvm_vcpu *vcpu, unsigned long gpa, /* PMD is not folded, adjust gfn to new boundary */ if (vma_pagesize == PMD_SIZE) gfn = (gpa & huge_page_mask(hstate_vma(vma))) >> PAGE_SHIFT; - - up_read(¤t->mm->mmap_sem); + mmap_read_unlock(current->mm); /* We need a minimum of cached pages ready for page table creation */ - err = mmu_topup_memory_cache(memcache, KVM_MMU_CACHE_MIN_PAGES, - KVM_NR_MEM_OBJS); + err = kvm_mmu_topup_memory_cache(memcache, KVM_MMU_CACHE_MIN_PAGES); if (err) goto out; @@ -1262,7 +1240,7 @@ static int kvm_map_page(struct kvm_vcpu *vcpu, unsigned long gpa, if (writeable) { prot_bits |= _PAGE_WRITE; if (write) { - prot_bits |= __WRITEABLE; + prot_bits |= __WRITEABLE | _PAGE_MODIFIED; mark_page_dirty(kvm, gfn); kvm_set_pfn_dirty(pfn); } diff --git a/arch/loongarch/kvm/trace.h b/arch/loongarch/kvm/trace.h index d72d10cb39fd..89a2779e0047 100644 --- a/arch/loongarch/kvm/trace.h +++ b/arch/loongarch/kvm/trace.h @@ -54,28 +54,28 @@ DEFINE_EVENT(kvm_transition, kvm_out, /* The first 32 exit reasons correspond to Cause.ExcCode */ #define KVM_TRACE_EXIT_INT 0 -#define KVM_TRACE_EXIT_TLBLD (EXCCODE_TLBL) -#define KVM_TRACE_EXIT_TLBST (EXCCODE_TLBS) -#define KVM_TRACE_EXIT_TLBI (EXCCODE_TLBI) -#define KVM_TRACE_EXIT_TLBMOD (EXCCODE_TLBM) -#define KVM_TRACE_EXIT_TLBRI (EXCCODE_TLBRI) -#define KVM_TRACE_EXIT_TLBXI (EXCCODE_TLBXI) -#define KVM_TRACE_EXIT_TLBPE (EXCCODE_TLBPE) -#define KVM_TRACE_EXIT_ADDE (EXCCODE_ADE) -#define KVM_TRACE_EXIT_UNALIGN (EXCCODE_ALE) -#define KVM_TRACE_EXIT_ODB (EXCCODE_OOB) -#define KVM_TRACE_EXIT_SYSCALL (EXCCODE_SYS) -#define KVM_TRACE_EXIT_BP (EXCCODE_BP) -#define KVM_TRACE_EXIT_INE (EXCCODE_INE) -#define KVM_TRACE_EXIT_IPE (EXCCODE_IPE) -#define KVM_TRACE_EXIT_FPDIS (EXCCODE_FPDIS) -#define KVM_TRACE_EXIT_LSXDIS (EXCCODE_LSXDIS) -#define KVM_TRACE_EXIT_LASXDIS (EXCCODE_LASXDIS) -#define KVM_TRACE_EXIT_FPE (EXCCODE_FPE) -#define KVM_TRACE_EXIT_WATCH (EXCCODE_WATCH) -#define KVM_TRACE_EXIT_GSPR (EXCCODE_GSPR) -#define KVM_TRACE_EXIT_HC (EXCCODE_HYP) -#define KVM_TRACE_EXIT_GCM (EXCCODE_GCM) +#define KVM_TRACE_EXIT_TLBLD (KVM_EXCCODE_TLBL) +#define KVM_TRACE_EXIT_TLBST (KVM_EXCCODE_TLBS) +#define KVM_TRACE_EXIT_TLBI (KVM_EXCCODE_TLBI) +#define KVM_TRACE_EXIT_TLBMOD (KVM_EXCCODE_TLBM) +#define KVM_TRACE_EXIT_TLBRI (KVM_EXCCODE_TLBRI) +#define KVM_TRACE_EXIT_TLBXI (KVM_EXCCODE_TLBXI) +#define KVM_TRACE_EXIT_TLBPE (KVM_EXCCODE_TLBPE) +#define KVM_TRACE_EXIT_ADDE (KVM_EXCCODE_ADE) +#define KVM_TRACE_EXIT_UNALIGN (KVM_EXCCODE_ALE) +#define KVM_TRACE_EXIT_ODB (KVM_EXCCODE_OOB) +#define KVM_TRACE_EXIT_SYSCALL (KVM_EXCCODE_SYS) +#define KVM_TRACE_EXIT_BP (KVM_EXCCODE_BP) +#define KVM_TRACE_EXIT_INE (KVM_EXCCODE_INE) +#define KVM_TRACE_EXIT_IPE (KVM_EXCCODE_IPE) +#define KVM_TRACE_EXIT_FPDIS (KVM_EXCCODE_FPDIS) +#define KVM_TRACE_EXIT_LSXDIS (KVM_EXCCODE_LSXDIS) +#define KVM_TRACE_EXIT_LASXDIS (KVM_EXCCODE_LASXDIS) +#define KVM_TRACE_EXIT_FPE (KVM_EXCCODE_FPE) +#define KVM_TRACE_EXIT_WATCH (KVM_EXCCODE_WATCH) +#define KVM_TRACE_EXIT_GSPR (KVM_EXCCODE_GSPR) +#define KVM_TRACE_EXIT_HC (KVM_EXCCODE_HYP) +#define KVM_TRACE_EXIT_GCM (KVM_EXCCODE_GCM) /* Further exit reasons */ #define KVM_TRACE_EXIT_IDLE 64 diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index aac92756317a..4229f98c7646 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -252,6 +252,7 @@ struct kvm_hyperv_exit { #define KVM_EXIT_X86_WRMSR 30 #define KVM_EXIT_RISCV_SBI 31 #define KVM_EXIT_X86_BUS_LOCK 33 +#define KVM_EXIT_LOONGARCH_IOCSR 36 #define KVM_EXIT_NOTIFY 37 /* For KVM_EXIT_INTERNAL_ERROR */ @@ -322,6 +323,13 @@ struct kvm_run { __u32 len; __u8 is_write; } mmio; + /* KVM_EXIT_LOONGARCH_IOCSR */ + struct { + __u64 phys_addr; + __u8 data[8]; + __u32 len; + __u8 is_write; + } iocsr_io; /* KVM_EXIT_HYPERCALL */ struct { __u64 nr; @@ -710,6 +718,16 @@ struct kvm_s390_irq_state { __u32 reserved[4]; /* will stay unused for compatibility reasons */ }; +struct kvm_loongarch_vcpu_state { + __u8 online_vcpus; + __u8 is_migrate; + __u32 cpu_freq; + __u32 count_ctl; + __u64 irq_pending; + __u64 irq_clear; + __u64 core_ext_ioisr[4]; +}; + /* for KVM_SET_GUEST_DEBUG */ #define KVM_GUESTDBG_ENABLE 0x00000001 @@ -1074,6 +1092,9 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_SYS_ATTRIBUTES 209 #define KVM_CAP_X86_TRIPLE_FAULT_EVENT 218 #define KVM_CAP_X86_NOTIFY_VMEXIT 219 +#define KVM_CAP_LOONGARCH_FPU 220 +#define KVM_CAP_LOONGARCH_LSX 221 +#define KVM_CAP_LOONGARCH_VZ 222 #define KVM_CAP_ARM_CPU_FEATURE 555 @@ -1223,6 +1244,7 @@ struct kvm_dirty_tlb { #define KVM_REG_ARM64 0x6000000000000000ULL #define KVM_REG_MIPS 0x7000000000000000ULL #define KVM_REG_RISCV 0x8000000000000000ULL +#define KVM_REG_LOONGARCH 0x8000000000000000ULL #define KVM_REG_SIZE_SHIFT 52 #define KVM_REG_SIZE_MASK 0x00f0000000000000ULL @@ -1560,6 +1582,14 @@ struct kvm_enc_region { #define KVM_S390_NORMAL_RESET _IO(KVMIO, 0xc3) #define KVM_S390_CLEAR_RESET _IO(KVMIO, 0xc4) +/* Add for LOONGSON read nodecounter */ +#define KVM_LOONGARCH_GET_VCPU_STATE _IOR(KVMIO, 0xc0, struct kvm_loongarch_vcpu_state) +#define KVM_LOONGARCH_SET_VCPU_STATE _IOW(KVMIO, 0xc1, struct kvm_loongarch_vcpu_state) +#define KVM_LOONGARCH_GET_CPUCFG _IOR(KVMIO, 0xc2, struct kvm_cpucfg) +#define KVM_LOONGARCH_GET_IOCSR _IOR(KVMIO, 0xc3, struct kvm_iocsr_entry) +#define KVM_LOONGARCH_SET_IOCSR _IOW(KVMIO, 0xc4, struct kvm_iocsr_entry) +#define KVM_LOONGARCH_SET_CPUCFG _IOR(KVMIO, 0xc5, struct kvm_cpucfg) + /* Available with KVM_CAP_XSAVE2 */ #define KVM_GET_XSAVE2 _IOR(KVMIO, 0xcf, struct kvm_xsave) -- Gitee From 511ac95b44e7f0fb99c9ab2d10cf4845fb2cc8f7 Mon Sep 17 00:00:00 2001 From: Youling Tang Date: Sat, 10 Dec 2022 22:39:48 +0800 Subject: [PATCH 48/56] LoongArch: Consolidate __ex_table construction Consolidate all the __ex_table constuction code with a _ASM_EXTABLE or _asm_extable helper. There should be no functional change as a result of this patch. Signed-off-by: Youling Tang Signed-off-by: Huacai Chen Change-Id: I24db0d24f7ec183f687221f9d99c4eb8f17cebd2 --- arch/loongarch/include/asm/asm-extable.h | 35 ++++++++++++++++++++++++ arch/loongarch/include/asm/futex.h | 13 ++++----- arch/loongarch/include/asm/uaccess.h | 9 ++---- arch/loongarch/kernel/fpu.S | 13 +++------ arch/loongarch/lib/clear_user.S | 5 ++-- arch/loongarch/lib/copy_user.S | 5 ++-- arch/loongarch/lib/strncpy_user.S | 6 ++-- arch/loongarch/lib/strnlen_user.S | 5 ++-- 8 files changed, 55 insertions(+), 36 deletions(-) create mode 100644 arch/loongarch/include/asm/asm-extable.h diff --git a/arch/loongarch/include/asm/asm-extable.h b/arch/loongarch/include/asm/asm-extable.h new file mode 100644 index 000000000000..4f615bf56727 --- /dev/null +++ b/arch/loongarch/include/asm/asm-extable.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ASM_ASM_EXTABLE_H +#define __ASM_ASM_EXTABLE_H + +#ifdef __ASSEMBLY__ + +#define __ASM_EXTABLE_RAW(insn, fixup) \ + .pushsection __ex_table, "a"; \ + .balign 8; \ + .quad (insn); \ + .quad (fixup); \ + .popsection; + + .macro _asm_extable, insn, fixup + __ASM_EXTABLE_RAW(\insn, \fixup) + .endm + +#else /* __ASSEMBLY__ */ + +#include +#include + +#define __ASM_EXTABLE_RAW(insn, fixup) \ + ".pushsection __ex_table, \"a\"\n" \ + ".balign 8\n" \ + ".quad ((" insn "))\n" \ + ".quad ((" fixup "))\n" \ + ".popsection\n" + +#define _ASM_EXTABLE(insn, fixup) \ + __ASM_EXTABLE_RAW(#insn, #fixup) + +#endif /* __ASSEMBLY__ */ + +#endif /* __ASM_ASM_EXTABLE_H */ diff --git a/arch/loongarch/include/asm/futex.h b/arch/loongarch/include/asm/futex.h index f0208f4aec3e..056f89a87d1f 100644 --- a/arch/loongarch/include/asm/futex.h +++ b/arch/loongarch/include/asm/futex.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -22,10 +23,8 @@ "4: li.w %0, %6 \n" \ " b 3b \n" \ " .previous \n" \ - " .section __ex_table,\"a\" \n" \ - " "__UA_ADDR "\t1b, 4b \n" \ - " "__UA_ADDR "\t2b, 4b \n" \ - " .previous \n" \ + _ASM_EXTABLE(1b, 4b) \ + _ASM_EXTABLE(2b, 4b) \ : "=r" (ret), "=&r" (oldval), \ "=ZC" (*uaddr) \ : "0" (0), "ZC" (*uaddr), "Jr" (oparg), \ @@ -92,10 +91,8 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, u32 oldval, u32 newv "4: li.d %0, %6 \n" " b 3b \n" " .previous \n" - " .section __ex_table,\"a\" \n" - " "__UA_ADDR "\t1b, 4b \n" - " "__UA_ADDR "\t2b, 4b \n" - " .previous \n" + _ASM_EXTABLE(1b, 4b) + _ASM_EXTABLE(2b, 4b) : "+r" (ret), "=&r" (val), "=ZC" (*uaddr) : "ZC" (*uaddr), "Jr" (oldval), "Jr" (newval), "i" (-EFAULT) diff --git a/arch/loongarch/include/asm/uaccess.h b/arch/loongarch/include/asm/uaccess.h index f124b307122e..58545c8686e3 100644 --- a/arch/loongarch/include/asm/uaccess.h +++ b/arch/loongarch/include/asm/uaccess.h @@ -15,6 +15,7 @@ #include #include #include +#include #include extern u64 __ua_limit; @@ -207,9 +208,7 @@ do { \ " move %1, $zero \n" \ " b 2b \n" \ " .previous \n" \ - " .section __ex_table,\"a\" \n" \ - " "__UA_ADDR "\t1b, 3b \n" \ - " .previous \n" \ + _ASM_EXTABLE(1b, 3b) \ : "+r" (__gu_err), "=r" (__gu_tmp) \ : "m" (__m(ptr)), "i" (-EFAULT)); \ \ @@ -238,9 +237,7 @@ do { \ "3: li.w %0, %3 \n" \ " b 2b \n" \ " .previous \n" \ - " .section __ex_table,\"a\" \n" \ - " " __UA_ADDR " 1b, 3b \n" \ - " .previous \n" \ + _ASM_EXTABLE(1b, 3b) \ : "+r" (__pu_err), "=m" (__m(ptr)) \ : "Jr" (__pu_val), "i" (-EFAULT)); \ } diff --git a/arch/loongarch/kernel/fpu.S b/arch/loongarch/kernel/fpu.S index 65d245a0f409..2e45dfb1b5e2 100644 --- a/arch/loongarch/kernel/fpu.S +++ b/arch/loongarch/kernel/fpu.S @@ -8,6 +8,7 @@ */ #include #include +#include #include #include #include @@ -21,9 +22,7 @@ .macro EX insn, reg, src, offs .ex\@: \insn \reg, \src, \offs - .section __ex_table,"a" - PTR .ex\@, fault - .previous + _asm_extable .ex\@, fault .endm .macro EX_V insn, reg, src, offs @@ -34,9 +33,7 @@ .ex\@: .word __insn << 22 | __offs << 10 | __src << 5 | __reg - .section __ex_table,"a" - PTR .ex\@, fault - .previous + _asm_extable .ex\@, fault .endm .macro EX_XV insn, reg, src, offs @@ -47,9 +44,7 @@ .ex\@: .word __insn << 22 | __offs << 10 | __src << 5 | __reg - .section __ex_table,"a" - PTR .ex\@, fault - .previous + _asm_extable .ex\@, fault .endm .macro sc_save_fp base diff --git a/arch/loongarch/lib/clear_user.S b/arch/loongarch/lib/clear_user.S index 16ba2b8dd68a..7a066d6a41b8 100644 --- a/arch/loongarch/lib/clear_user.S +++ b/arch/loongarch/lib/clear_user.S @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -15,9 +16,7 @@ jr ra .previous .endif - .section __ex_table, "a" - PTR \from\()b, \to\()b - .previous + _asm_extable \from\()b, \to\()b .endm /* diff --git a/arch/loongarch/lib/copy_user.S b/arch/loongarch/lib/copy_user.S index 97d20327a69e..f8ace04586c2 100644 --- a/arch/loongarch/lib/copy_user.S +++ b/arch/loongarch/lib/copy_user.S @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -15,9 +16,7 @@ jr ra .previous .endif - .section __ex_table, "a" - PTR \from\()b, \to\()b - .previous + _asm_extable \from\()b, \to\()b .endm /* diff --git a/arch/loongarch/lib/strncpy_user.S b/arch/loongarch/lib/strncpy_user.S index b1dcfc1e1fc0..90d8df2b6ecf 100644 --- a/arch/loongarch/lib/strncpy_user.S +++ b/arch/loongarch/lib/strncpy_user.S @@ -7,6 +7,7 @@ #include #include #include +#include /* * long __strncpy_from_user(char *to, const char *from, long len) @@ -39,10 +40,7 @@ SYM_FUNC_START(__strncpy_from_user) 3: li.w a0, -EFAULT jr ra .previous - - .section __ex_table, "a" - PTR 1b, 3b - .previous + _asm_extable 1b, 3b SYM_FUNC_END(__strncpy_from_user) EXPORT_SYMBOL(__strncpy_from_user) diff --git a/arch/loongarch/lib/strnlen_user.S b/arch/loongarch/lib/strnlen_user.S index bf9d5ad6d047..795aef7441ea 100644 --- a/arch/loongarch/lib/strnlen_user.S +++ b/arch/loongarch/lib/strnlen_user.S @@ -6,6 +6,7 @@ #include #include #include +#include /* * long __strnlen_user(const char *s, long n) @@ -36,9 +37,7 @@ SYM_FUNC_START(__strnlen_user) jr ra .previous - .section __ex_table, "a" - PTR 1b, 3b - .previous + _asm_extable 1b, 3b SYM_FUNC_END(__strnlen_user) EXPORT_SYMBOL(__strnlen_user) -- Gitee From c34e3c80ad31b144c527df12e1fe1bcb7aefd054 Mon Sep 17 00:00:00 2001 From: Youling Tang Date: Sat, 10 Dec 2022 22:39:48 +0800 Subject: [PATCH 49/56] LoongArch: Switch to relative exception tables Similar to other architectures such as arm64, x86, riscv and so on, use offsets relative to the exception table entry values rather than their absolute addresses for both the exception location and the fixup. However, LoongArch label difference because it will actually produce two relocations, a pair of R_LARCH_ADD32 and R_LARCH_SUB32. Take simple code below for example: $ cat test_ex_table.S .section .text 1: nop .section __ex_table,"a" .balign 4 .long (1b - .) .previous $ loongarch64-unknown-linux-gnu-gcc -c test_ex_table.S $ loongarch64-unknown-linux-gnu-readelf -Wr test_ex_table.o Relocation section '.rela__ex_table' at offset 0x100 contains 2 entries: Offset Info Type Symbol's Value Symbol's Name + Addend 0000000000000000 0000000600000032 R_LARCH_ADD32 0000000000000000 .L1^B1 + 0 0000000000000000 0000000500000037 R_LARCH_SUB32 0000000000000000 L0^A + 0 The modpost will complain the R_LARCH_SUB32 relocation, so we need to patch modpost.c to skip this relocation for .rela__ex_table section. Signed-off-by: Youling Tang Signed-off-by: Huacai Chen Change-Id: If42e54904f36a0d7cafc40a75fd89b582fbaa09e --- arch/loongarch/include/asm/asm-extable.h | 12 ++++----- arch/loongarch/include/asm/extable.h | 26 +++++++++++++++++++ arch/loongarch/include/asm/uaccess.h | 2 +- arch/loongarch/mm/extable.c | 32 ++++++++++++++++-------- scripts/mod/modpost.c | 15 +++++++++++ scripts/sorttable.c | 2 +- 6 files changed, 71 insertions(+), 18 deletions(-) create mode 100644 arch/loongarch/include/asm/extable.h diff --git a/arch/loongarch/include/asm/asm-extable.h b/arch/loongarch/include/asm/asm-extable.h index 4f615bf56727..74f8bc75472a 100644 --- a/arch/loongarch/include/asm/asm-extable.h +++ b/arch/loongarch/include/asm/asm-extable.h @@ -6,9 +6,9 @@ #define __ASM_EXTABLE_RAW(insn, fixup) \ .pushsection __ex_table, "a"; \ - .balign 8; \ - .quad (insn); \ - .quad (fixup); \ + .balign 4; \ + .long ((insn) - .); \ + .long ((fixup) - .); \ .popsection; .macro _asm_extable, insn, fixup @@ -22,9 +22,9 @@ #define __ASM_EXTABLE_RAW(insn, fixup) \ ".pushsection __ex_table, \"a\"\n" \ - ".balign 8\n" \ - ".quad ((" insn "))\n" \ - ".quad ((" fixup "))\n" \ + ".balign 4\n" \ + ".long ((" insn ") - .)\n" \ + ".long ((" fixup ") - .)\n" \ ".popsection\n" #define _ASM_EXTABLE(insn, fixup) \ diff --git a/arch/loongarch/include/asm/extable.h b/arch/loongarch/include/asm/extable.h new file mode 100644 index 000000000000..b571c89705d1 --- /dev/null +++ b/arch/loongarch/include/asm/extable.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_LOONGARCH_EXTABLE_H +#define _ASM_LOONGARCH_EXTABLE_H + +/* + * The exception table consists of pairs of relative offsets: the first + * is the relative offset to an instruction that is allowed to fault, + * and the second is the relative offset at which the program should + * continue. No registers are modified, so it is entirely up to the + * continuation code to figure out what to do. + * + * All the routines below use bits of fixup code that are out of line + * with the main instruction path. This means when everything is well, + * we don't even have to jump over them. Further, they do not intrude + * on our cache or tlb entries. + */ + +struct exception_table_entry { + int insn, fixup; +}; + +#define ARCH_HAS_RELATIVE_EXTABLE + +bool fixup_exception(struct pt_regs *regs); + +#endif diff --git a/arch/loongarch/include/asm/uaccess.h b/arch/loongarch/include/asm/uaccess.h index 58545c8686e3..33d9d7be8a02 100644 --- a/arch/loongarch/include/asm/uaccess.h +++ b/arch/loongarch/include/asm/uaccess.h @@ -15,8 +15,8 @@ #include #include #include +#include #include -#include extern u64 __ua_limit; diff --git a/arch/loongarch/mm/extable.c b/arch/loongarch/mm/extable.c index bc20988f2b87..08a9a7d6357a 100644 --- a/arch/loongarch/mm/extable.c +++ b/arch/loongarch/mm/extable.c @@ -3,20 +3,32 @@ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited */ #include -#include -#include #include +#include +#include + +static inline unsigned long +get_ex_fixup(const struct exception_table_entry *ex) +{ + return ((unsigned long)&ex->fixup + ex->fixup); +} -int fixup_exception(struct pt_regs *regs) +static bool ex_handler_fixup(const struct exception_table_entry *ex, + struct pt_regs *regs) { - const struct exception_table_entry *fixup; + regs->csr_era = get_ex_fixup(ex); - fixup = search_exception_tables(exception_era(regs)); - if (fixup) { - regs->csr_era = fixup->fixup; + return true; +} + + +bool fixup_exception(struct pt_regs *regs) +{ + const struct exception_table_entry *ex; - return 1; - } + ex = search_exception_tables(exception_era(regs)); + if (!ex) + return false; - return 0; + return ex_handler_fixup(ex, regs); } diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index e48742760fec..8545e49cfb77 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1842,6 +1842,14 @@ static int addend_mips_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r) return 0; } +#ifndef EM_LOONGARCH +#define EM_LOONGARCH 258 +#endif + +#ifndef R_LARCH_SUB32 +#define R_LARCH_SUB32 55 +#endif + static void section_rela(const char *modname, struct elf_info *elf, Elf_Shdr *sechdr) { @@ -1878,6 +1886,13 @@ static void section_rela(const char *modname, struct elf_info *elf, r_sym = ELF_R_SYM(r.r_info); #endif r.r_addend = TO_NATIVE(rela->r_addend); + switch (elf->hdr->e_machine) { + case EM_LOONGARCH: + if (!strcmp("__ex_table", fromsec) && + ELF_R_TYPE(r.r_info) == R_LARCH_SUB32) + continue; + break; + } sym = elf->symtab_start + r_sym; /* Skip special sections */ if (is_shndx_special(sym->st_shndx)) diff --git a/scripts/sorttable.c b/scripts/sorttable.c index 3edef1c6aaa0..5ce49722d5bc 100644 --- a/scripts/sorttable.c +++ b/scripts/sorttable.c @@ -347,11 +347,11 @@ static int do_file(char const *const fname, void *addr) case EM_PPC: case EM_ARM: case EM_PPC64: + case EM_LOONGARCH: custom_sort = sort_relative_table; break; case EM_ARCOMPACT: case EM_ARCV2: - case EM_LOONGARCH: case EM_MICROBLAZE: case EM_MIPS: case EM_XTENSA: -- Gitee From b34ef80cf7be5ac415b9bc7a95ad66c5cba5fce6 Mon Sep 17 00:00:00 2001 From: Youling Tang Date: Sat, 10 Dec 2022 22:39:59 +0800 Subject: [PATCH 50/56] LoongArch: extable: Add `type` and `data` fields This is a LoongArch port of commit d6e2cc564775 ("arm64: extable: add `type` and `data` fields"). Subsequent patches will add specialized handlers for fixups, in addition to the simple PC fixup we have today. In preparation, this patch adds a new `type` field to struct exception_table_entry, and uses this to distinguish the fixup and other cases. A `data` field is also added so that subsequent patches can associate data specific to each exception site (e.g. register numbers). Handlers are named ex_handler_*() for consistency, following the example of x86. At the same time, get_ex_fixup() is split out into a helper so that it can be used by other ex_handler_*() functions in the subsequent patches. Signed-off-by: Youling Tang Signed-off-by: Huacai Chen Change-Id: I95ac7a755a8a3389b63b92c1e98a851c1993c0aa --- arch/loongarch/include/asm/asm-extable.h | 15 ++++++++--- arch/loongarch/include/asm/extable.h | 11 ++++++++ arch/loongarch/kernel/vmlinux.lds.S | 3 +-- arch/loongarch/mm/extable.c | 7 +++++- scripts/sorttable.c | 32 +++++++++++++++++++++++- 5 files changed, 60 insertions(+), 8 deletions(-) diff --git a/arch/loongarch/include/asm/asm-extable.h b/arch/loongarch/include/asm/asm-extable.h index 74f8bc75472a..634bd770e3c4 100644 --- a/arch/loongarch/include/asm/asm-extable.h +++ b/arch/loongarch/include/asm/asm-extable.h @@ -2,17 +2,22 @@ #ifndef __ASM_ASM_EXTABLE_H #define __ASM_ASM_EXTABLE_H +#define EX_TYPE_NONE 0 +#define EX_TYPE_FIXUP 1 + #ifdef __ASSEMBLY__ -#define __ASM_EXTABLE_RAW(insn, fixup) \ +#define __ASM_EXTABLE_RAW(insn, fixup, type, data) \ .pushsection __ex_table, "a"; \ .balign 4; \ .long ((insn) - .); \ .long ((fixup) - .); \ + .short (type); \ + .short (data); \ .popsection; .macro _asm_extable, insn, fixup - __ASM_EXTABLE_RAW(\insn, \fixup) + __ASM_EXTABLE_RAW(\insn, \fixup, EX_TYPE_FIXUP, 0) .endm #else /* __ASSEMBLY__ */ @@ -20,15 +25,17 @@ #include #include -#define __ASM_EXTABLE_RAW(insn, fixup) \ +#define __ASM_EXTABLE_RAW(insn, fixup, type, data) \ ".pushsection __ex_table, \"a\"\n" \ ".balign 4\n" \ ".long ((" insn ") - .)\n" \ ".long ((" fixup ") - .)\n" \ + ".short (" type ")\n" \ + ".short (" data ")\n" \ ".popsection\n" #define _ASM_EXTABLE(insn, fixup) \ - __ASM_EXTABLE_RAW(#insn, #fixup) + __ASM_EXTABLE_RAW(#insn, #fixup, __stringify(EX_TYPE_FIXUP), "0") #endif /* __ASSEMBLY__ */ diff --git a/arch/loongarch/include/asm/extable.h b/arch/loongarch/include/asm/extable.h index b571c89705d1..92612b4364a1 100644 --- a/arch/loongarch/include/asm/extable.h +++ b/arch/loongarch/include/asm/extable.h @@ -17,10 +17,21 @@ struct exception_table_entry { int insn, fixup; + short type, data; }; #define ARCH_HAS_RELATIVE_EXTABLE +#define swap_ex_entry_fixup(a, b, tmp, delta) \ +do { \ + (a)->fixup = (b)->fixup + (delta); \ + (b)->fixup = (tmp).fixup - (delta); \ + (a)->type = (b)->type; \ + (b)->type = (tmp).type; \ + (a)->data = (b)->data; \ + (b)->data = (tmp).data; \ +} while (0) + bool fixup_exception(struct pt_regs *regs); #endif diff --git a/arch/loongarch/kernel/vmlinux.lds.S b/arch/loongarch/kernel/vmlinux.lds.S index 36d042739f3c..e956e2e3c510 100644 --- a/arch/loongarch/kernel/vmlinux.lds.S +++ b/arch/loongarch/kernel/vmlinux.lds.S @@ -4,6 +4,7 @@ #include #define PAGE_SIZE _PAGE_SIZE +#define RO_EXCEPTION_TABLE_ALIGN 4 /* * Put .bss..swapper_pg_dir as the first thing in .bss. This will @@ -53,8 +54,6 @@ SECTIONS . = ALIGN(PECOFF_SEGMENT_ALIGN); _etext = .; - EXCEPTION_TABLE(16) - . = ALIGN(PECOFF_SEGMENT_ALIGN); __init_begin = .; __inittext_begin = .; diff --git a/arch/loongarch/mm/extable.c b/arch/loongarch/mm/extable.c index 08a9a7d6357a..fd2395221cff 100644 --- a/arch/loongarch/mm/extable.c +++ b/arch/loongarch/mm/extable.c @@ -30,5 +30,10 @@ bool fixup_exception(struct pt_regs *regs) if (!ex) return false; - return ex_handler_fixup(ex, regs); + switch (ex->type) { + case EX_TYPE_FIXUP: + return ex_handler_fixup(ex, regs); + } + + BUG(); } diff --git a/scripts/sorttable.c b/scripts/sorttable.c index 5ce49722d5bc..92f1951b46f6 100644 --- a/scripts/sorttable.c +++ b/scripts/sorttable.c @@ -231,6 +231,34 @@ static void sort_relative_table(char *extab_image, int image_size) } } +static void sort_relative_table_with_data(char *extab_image, int image_size) +{ + int i = 0; + + while (i < image_size) { + uint32_t *loc = (uint32_t *)(extab_image + i); + + w(r(loc) + i, loc); + w(r(loc + 1) + i + 4, loc + 1); + /* Don't touch the fixup type or data */ + + i += sizeof(uint32_t) * 3; + } + + qsort(extab_image, image_size / 12, 12, compare_relative_table); + + i = 0; + while (i < image_size) { + uint32_t *loc = (uint32_t *)(extab_image + i); + + w(r(loc) - i, loc); + w(r(loc + 1) - (i + 4), loc + 1); + /* Don't touch the fixup type or data */ + + i += sizeof(uint32_t) * 3; + } +} + static void x86_sort_relative_table(char *extab_image, int image_size) { int i = 0; @@ -339,6 +367,9 @@ static int do_file(char const *const fname, void *addr) case EM_X86_64: custom_sort = x86_sort_relative_table; break; + case EM_LOONGARCH: + custom_sort = sort_relative_table_with_data; + break; case EM_S390: custom_sort = s390_sort_relative_table; break; @@ -347,7 +378,6 @@ static int do_file(char const *const fname, void *addr) case EM_PPC: case EM_ARM: case EM_PPC64: - case EM_LOONGARCH: custom_sort = sort_relative_table; break; case EM_ARCOMPACT: -- Gitee From 78906f218551a8a547090877765553da8098a857 Mon Sep 17 00:00:00 2001 From: Youling Tang Date: Sat, 10 Dec 2022 22:39:59 +0800 Subject: [PATCH 51/56] LoongArch: extable: Add a dedicated uaccess handler Inspired by commit 2e77a62cb3a6("arm64: extable: add a dedicated uaccess handler"), do similar to LoongArch to add a dedicated uaccess exception handler to update registers in exception context and subsequently return back into the function which faulted, so we remove the need for fixups specialized to each faulting instruction. Add gpr-num.h here because we need to map the same GPR names to integer constants, so that we can use this to build meta-data for the exception fixups. The compiler treats gpr 0 as zero rather than $r0, so set it separately to .L__gpr_num_zero, otherwise the following assembly error will occurs: {standard input}: Assembler messages: {standard input}:1074: Error: invalid operands (*UND* and *ABS* sections) for `<<' {standard input}:1160: Error: invalid operands (*UND* and *ABS* sections) for `<<' make[1]: *** [scripts/Makefile.build:249: fs/fcntl.o] Error 1 Signed-off-by: Youling Tang Signed-off-by: Huacai Chen Change-Id: Ifda81e961c8147c6f652891802dc6b5354bb5491 --- arch/loongarch/include/asm/asm-extable.h | 22 ++++++++++++++++++++++ arch/loongarch/include/asm/futex.h | 22 ++++++---------------- arch/loongarch/include/asm/gpr-num.h | 22 ++++++++++++++++++++++ arch/loongarch/include/asm/uaccess.h | 17 ++++------------- arch/loongarch/mm/extable.c | 22 ++++++++++++++++++++++ 5 files changed, 76 insertions(+), 29 deletions(-) create mode 100644 arch/loongarch/include/asm/gpr-num.h diff --git a/arch/loongarch/include/asm/asm-extable.h b/arch/loongarch/include/asm/asm-extable.h index 634bd770e3c4..f5502cb50c6e 100644 --- a/arch/loongarch/include/asm/asm-extable.h +++ b/arch/loongarch/include/asm/asm-extable.h @@ -4,6 +4,7 @@ #define EX_TYPE_NONE 0 #define EX_TYPE_FIXUP 1 +#define EX_TYPE_UACCESS_ERR_ZERO 2 #ifdef __ASSEMBLY__ @@ -24,6 +25,7 @@ #include #include +#include #define __ASM_EXTABLE_RAW(insn, fixup, type, data) \ ".pushsection __ex_table, \"a\"\n" \ @@ -37,6 +39,26 @@ #define _ASM_EXTABLE(insn, fixup) \ __ASM_EXTABLE_RAW(#insn, #fixup, __stringify(EX_TYPE_FIXUP), "0") +#define EX_DATA_REG_ERR_SHIFT 0 +#define EX_DATA_REG_ERR GENMASK(4, 0) +#define EX_DATA_REG_ZERO_SHIFT 5 +#define EX_DATA_REG_ZERO GENMASK(9, 5) + +#define EX_DATA_REG(reg, gpr) \ + "((.L__gpr_num_" #gpr ") << " __stringify(EX_DATA_REG_##reg##_SHIFT) ")" + +#define _ASM_EXTABLE_UACCESS_ERR_ZERO(insn, fixup, err, zero) \ + __DEFINE_ASM_GPR_NUMS \ + __ASM_EXTABLE_RAW(#insn, #fixup, \ + __stringify(EX_TYPE_UACCESS_ERR_ZERO), \ + "(" \ + EX_DATA_REG(ERR, err) " | " \ + EX_DATA_REG(ZERO, zero) \ + ")") + +#define _ASM_EXTABLE_UACCESS_ERR(insn, fixup, err) \ + _ASM_EXTABLE_UACCESS_ERR_ZERO(insn, fixup, err, zero) + #endif /* __ASSEMBLY__ */ #endif /* __ASM_ASM_EXTABLE_H */ diff --git a/arch/loongarch/include/asm/futex.h b/arch/loongarch/include/asm/futex.h index 056f89a87d1f..af6ee7a8ee88 100644 --- a/arch/loongarch/include/asm/futex.h +++ b/arch/loongarch/include/asm/futex.h @@ -19,16 +19,11 @@ "2: sc.w $t0, %2 \n" \ " beqz $t0, 1b \n" \ "3: \n" \ - " .section .fixup,\"ax\" \n" \ - "4: li.w %0, %6 \n" \ - " b 3b \n" \ - " .previous \n" \ - _ASM_EXTABLE(1b, 4b) \ - _ASM_EXTABLE(2b, 4b) \ + _ASM_EXTABLE_UACCESS_ERR(1b, 3b, %0) \ + _ASM_EXTABLE_UACCESS_ERR(2b, 3b, %0) \ : "=r" (ret), "=&r" (oldval), \ "=ZC" (*uaddr) \ - : "0" (0), "ZC" (*uaddr), "Jr" (oparg), \ - "i" (-EFAULT) \ + : "0" (0), "ZC" (*uaddr), "Jr" (oparg) \ : "memory", "t0"); \ } @@ -87,15 +82,10 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, u32 oldval, u32 newv "3: \n" __WEAK_LLSC_MB "5: \n" - " .section .fixup,\"ax\" \n" - "4: li.d %0, %6 \n" - " b 3b \n" - " .previous \n" - _ASM_EXTABLE(1b, 4b) - _ASM_EXTABLE(2b, 4b) + _ASM_EXTABLE_UACCESS_ERR(1b, 3b, %0) + _ASM_EXTABLE_UACCESS_ERR(2b, 3b, %0) : "+r" (ret), "=&r" (val), "=ZC" (*uaddr) - : "ZC" (*uaddr), "Jr" (oldval), "Jr" (newval), - "i" (-EFAULT) + : "ZC" (*uaddr), "Jr" (oldval), "Jr" (newval) : "memory", "t0"); *uval = val; diff --git a/arch/loongarch/include/asm/gpr-num.h b/arch/loongarch/include/asm/gpr-num.h new file mode 100644 index 000000000000..e0941af20c7e --- /dev/null +++ b/arch/loongarch/include/asm/gpr-num.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ASM_GPR_NUM_H +#define __ASM_GPR_NUM_H + +#ifdef __ASSEMBLY__ + + .equ .L__gpr_num_zero, 0 + .irp num,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31 + .equ .L__gpr_num_$r\num, \num + .endr + +#else /* __ASSEMBLY__ */ + +#define __DEFINE_ASM_GPR_NUMS \ +" .equ .L__gpr_num_zero, 0\n" \ +" .irp num,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31\n" \ +" .equ .L__gpr_num_$r\\num, \\num\n" \ +" .endr\n" \ + +#endif /* __ASSEMBLY__ */ + +#endif /* __ASM_GPR_NUM_H */ diff --git a/arch/loongarch/include/asm/uaccess.h b/arch/loongarch/include/asm/uaccess.h index 33d9d7be8a02..306bef8f4972 100644 --- a/arch/loongarch/include/asm/uaccess.h +++ b/arch/loongarch/include/asm/uaccess.h @@ -203,14 +203,9 @@ do { \ __asm__ __volatile__( \ "1: " insn " %1, %2 \n" \ "2: \n" \ - " .section .fixup,\"ax\" \n" \ - "3: li.w %0, %3 \n" \ - " move %1, $zero \n" \ - " b 2b \n" \ - " .previous \n" \ - _ASM_EXTABLE(1b, 3b) \ + _ASM_EXTABLE_UACCESS_ERR_ZERO(1b, 2b, %0, %1) \ : "+r" (__gu_err), "=r" (__gu_tmp) \ - : "m" (__m(ptr)), "i" (-EFAULT)); \ + : "m" (__m(ptr))); \ \ (val) = (__typeof__(*(ptr))) __gu_tmp; \ } @@ -233,13 +228,9 @@ do { \ __asm__ __volatile__( \ "1: " insn " %z2, %1 # __put_user_asm\n" \ "2: \n" \ - " .section .fixup,\"ax\" \n" \ - "3: li.w %0, %3 \n" \ - " b 2b \n" \ - " .previous \n" \ - _ASM_EXTABLE(1b, 3b) \ + _ASM_EXTABLE_UACCESS_ERR(1b, 2b,%0) \ : "+r" (__pu_err), "=m" (__m(ptr)) \ - : "Jr" (__pu_val), "i" (-EFAULT)); \ + : "Jr" (__pu_val)); \ } #define HAVE_GET_KERNEL_NOFAULT diff --git a/arch/loongarch/mm/extable.c b/arch/loongarch/mm/extable.c index fd2395221cff..9b0cfd898940 100644 --- a/arch/loongarch/mm/extable.c +++ b/arch/loongarch/mm/extable.c @@ -2,6 +2,7 @@ /* * Copyright (C) 2020-2022 Loongson Technology Corporation Limited */ +#include #include #include #include @@ -13,6 +14,13 @@ get_ex_fixup(const struct exception_table_entry *ex) return ((unsigned long)&ex->fixup + ex->fixup); } +static inline void regs_set_gpr(struct pt_regs *regs, + unsigned int offset, unsigned long val) +{ + if (offset && offset <= MAX_REG_OFFSET) + *(unsigned long *)((unsigned long)regs + offset) = val; +} + static bool ex_handler_fixup(const struct exception_table_entry *ex, struct pt_regs *regs) { @@ -21,6 +29,18 @@ static bool ex_handler_fixup(const struct exception_table_entry *ex, return true; } +static bool ex_handler_uaccess_err_zero(const struct exception_table_entry *ex, + struct pt_regs *regs) +{ + int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data); + int reg_zero = FIELD_GET(EX_DATA_REG_ZERO, ex->data); + + regs_set_gpr(regs, reg_err * sizeof(unsigned long), -EFAULT); + regs_set_gpr(regs, reg_zero * sizeof(unsigned long), 0); + regs->csr_era = get_ex_fixup(ex); + + return true; +} bool fixup_exception(struct pt_regs *regs) { @@ -33,6 +53,8 @@ bool fixup_exception(struct pt_regs *regs) switch (ex->type) { case EX_TYPE_FIXUP: return ex_handler_fixup(ex, regs); + case EX_TYPE_UACCESS_ERR_ZERO: + return ex_handler_uaccess_err_zero(ex, regs); } BUG(); -- Gitee From 58203038e0d661a68cb6e31dd4e9ce685b118532 Mon Sep 17 00:00:00 2001 From: Youling Tang Date: Sat, 10 Dec 2022 22:39:59 +0800 Subject: [PATCH 52/56] LoongArch: Remove the .fixup section usage Use the `.L_xxx` label to improve fixup code and then remove the .fixup section usage. Signed-off-by: Youling Tang Signed-off-by: Huacai Chen Change-Id: I01c14c9bbc99baffe611d7e27f4c87d494b73026 --- arch/loongarch/lib/clear_user.S | 14 +++++--------- arch/loongarch/lib/copy_user.S | 16 ++++++---------- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/arch/loongarch/lib/clear_user.S b/arch/loongarch/lib/clear_user.S index 7a066d6a41b8..d5c9e44ac8c4 100644 --- a/arch/loongarch/lib/clear_user.S +++ b/arch/loongarch/lib/clear_user.S @@ -9,15 +9,11 @@ #include #include -.macro fixup_ex from, to, offset, fix -.if \fix - .section .fixup, "ax" -\to: addi.d a0, a1, \offset +.irp to, 0 +.L_fixup_handle_\to\(): + addi.d a0, a1, (\to) * (-8) jr ra - .previous -.endif - _asm_extable \from\()b, \to\()b -.endm +.endr /* * unsigned long __clear_user(void *addr, size_t size) @@ -36,7 +32,7 @@ SYM_FUNC_START(__clear_user) 2: move a0, a1 jr ra - fixup_ex 1, 3, 0, 1 + _asm_extable 1b, .L_fixup_handle_0 SYM_FUNC_END(__clear_user) EXPORT_SYMBOL(__clear_user) diff --git a/arch/loongarch/lib/copy_user.S b/arch/loongarch/lib/copy_user.S index f8ace04586c2..61933d964da0 100644 --- a/arch/loongarch/lib/copy_user.S +++ b/arch/loongarch/lib/copy_user.S @@ -9,15 +9,11 @@ #include #include -.macro fixup_ex from, to, offset, fix -.if \fix - .section .fixup, "ax" -\to: addi.d a0, a2, \offset +.irp to, 0 +.L_fixup_handle_\to\(): + addi.d a0, a2, (\to) * (-8) jr ra - .previous -.endif - _asm_extable \from\()b, \to\()b -.endm +.endr /* * unsigned long __copy_user(void *to, const void *from, size_t n) @@ -39,8 +35,8 @@ SYM_FUNC_START(__copy_user) 3: move a0, a2 jr ra - fixup_ex 1, 4, 0, 1 - fixup_ex 2, 4, 0, 0 + _asm_extable 1b, .L_fixup_handle_0 + _asm_extable 2b, .L_fixup_handle_0 SYM_FUNC_END(__copy_user) EXPORT_SYMBOL(__copy_user) -- Gitee From 5dbb7b7d58f094c8a0e8a0678e3ed6d6283a8024 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Sat, 10 Dec 2022 22:39:59 +0800 Subject: [PATCH 53/56] LoongArch: Add alternative runtime patching mechanism Introduce the "alternative" mechanism from ARM64 and x86 for LoongArch to apply runtime patching. The main purpose of this patch is to provide a framework. In future we can use this mechanism (i.e., the ALTERNATIVE and ALTERNATIVE_2 macros) to optimize hotspot functions according to cpu features. Signed-off-by: Jun Yi Signed-off-by: Huacai Chen Change-Id: I204372e8f1ce7cf3c7bc7de9e8f6f16dbf06239a --- arch/loongarch/include/asm/alternative-asm.h | 82 +++++++ arch/loongarch/include/asm/alternative.h | 111 +++++++++ arch/loongarch/include/asm/bugs.h | 15 ++ arch/loongarch/include/asm/inst.h | 27 ++ arch/loongarch/kernel/Makefile | 2 +- arch/loongarch/kernel/alternative.c | 246 +++++++++++++++++++ arch/loongarch/kernel/module.c | 15 ++ arch/loongarch/kernel/setup.c | 7 + arch/loongarch/kernel/vmlinux.lds.S | 12 + 9 files changed, 516 insertions(+), 1 deletion(-) create mode 100644 arch/loongarch/include/asm/alternative-asm.h create mode 100644 arch/loongarch/include/asm/alternative.h create mode 100644 arch/loongarch/include/asm/bugs.h create mode 100644 arch/loongarch/kernel/alternative.c diff --git a/arch/loongarch/include/asm/alternative-asm.h b/arch/loongarch/include/asm/alternative-asm.h new file mode 100644 index 000000000000..ff3d10ac393f --- /dev/null +++ b/arch/loongarch/include/asm/alternative-asm.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_ALTERNATIVE_ASM_H +#define _ASM_ALTERNATIVE_ASM_H + +#ifdef __ASSEMBLY__ + +#include + +/* + * Issue one struct alt_instr descriptor entry (need to put it into + * the section .altinstructions, see below). This entry contains + * enough information for the alternatives patching code to patch an + * instruction. See apply_alternatives(). + */ +.macro altinstruction_entry orig alt feature orig_len alt_len + .long \orig - . + .long \alt - . + .short \feature + .byte \orig_len + .byte \alt_len +.endm + +/* + * Define an alternative between two instructions. If @feature is + * present, early code in apply_alternatives() replaces @oldinstr with + * @newinstr. ".fill" directive takes care of proper instruction padding + * in case @newinstr is longer than @oldinstr. + */ +.macro ALTERNATIVE oldinstr, newinstr, feature +140 : + \oldinstr +141 : + .fill - (((144f-143f)-(141b-140b)) > 0) * ((144f-143f)-(141b-140b)) / 4, 4, 0x03400000 +142 : + + .pushsection .altinstructions, "a" + altinstruction_entry 140b, 143f, \feature, 142b-140b, 144f-143f + .popsection + + .subsection 1 +143 : + \newinstr +144 : + .previous +.endm + +#define old_len (141b-140b) +#define new_len1 (144f-143f) +#define new_len2 (145f-144f) + +#define alt_max_short(a, b) ((a) ^ (((a) ^ (b)) & -(-((a) < (b))))) + +/* + * Same as ALTERNATIVE macro above but for two alternatives. If CPU + * has @feature1, it replaces @oldinstr with @newinstr1. If CPU has + * @feature2, it replaces @oldinstr with @feature2. + */ +.macro ALTERNATIVE_2 oldinstr, newinstr1, feature1, newinstr2, feature2 +140 : + \oldinstr +141 : + .fill - ((alt_max_short(new_len1, new_len2) - (old_len)) > 0) * \ + (alt_max_short(new_len1, new_len2) - (old_len)) / 4, 4, 0x03400000 +142 : + + .pushsection .altinstructions, "a" + altinstruction_entry 140b, 143f, \feature1, 142b-140b, 144f-143f, 142b-141b + altinstruction_entry 140b, 144f, \feature2, 142b-140b, 145f-144f, 142b-141b + .popsection + + .subsection 1 +143 : + \newinstr1 +144 : + \newinstr2 +145 : + .previous +.endm + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_ALTERNATIVE_ASM_H */ diff --git a/arch/loongarch/include/asm/alternative.h b/arch/loongarch/include/asm/alternative.h new file mode 100644 index 000000000000..cee7b29785ab --- /dev/null +++ b/arch/loongarch/include/asm/alternative.h @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_ALTERNATIVE_H +#define _ASM_ALTERNATIVE_H + +#ifndef __ASSEMBLY__ + +#include +#include +#include +#include + +struct alt_instr { + s32 instr_offset; /* offset to original instruction */ + s32 replace_offset; /* offset to replacement instruction */ + u16 feature; /* feature bit set for replacement */ + u8 instrlen; /* length of original instruction */ + u8 replacementlen; /* length of new instruction */ +} __packed; + +/* + * Debug flag that can be tested to see whether alternative + * instructions were patched in already: + */ +extern int alternatives_patched; +extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; + +extern void alternative_instructions(void); +extern void apply_alternatives(struct alt_instr *start, struct alt_instr *end); + +#define b_replacement(num) "664"#num +#define e_replacement(num) "665"#num + +#define alt_end_marker "663" +#define alt_slen "662b-661b" +#define alt_total_slen alt_end_marker"b-661b" +#define alt_rlen(num) e_replacement(num)"f-"b_replacement(num)"f" + +#define __OLDINSTR(oldinstr, num) \ + "661:\n\t" oldinstr "\n662:\n" \ + ".fill -(((" alt_rlen(num) ")-(" alt_slen ")) > 0) * " \ + "((" alt_rlen(num) ")-(" alt_slen ")) / 4, 4, 0x03400000\n" + +#define OLDINSTR(oldinstr, num) \ + __OLDINSTR(oldinstr, num) \ + alt_end_marker ":\n" + +#define alt_max_short(a, b) "((" a ") ^ (((" a ") ^ (" b ")) & -(-((" a ") < (" b ")))))" + +/* + * Pad the second replacement alternative with additional NOPs if it is + * additionally longer than the first replacement alternative. + */ +#define OLDINSTR_2(oldinstr, num1, num2) \ + "661:\n\t" oldinstr "\n662:\n" \ + ".fill -((" alt_max_short(alt_rlen(num1), alt_rlen(num2)) " - (" alt_slen ")) > 0) * " \ + "(" alt_max_short(alt_rlen(num1), alt_rlen(num2)) " - (" alt_slen ")) / 4, " \ + "4, 0x03400000\n" \ + alt_end_marker ":\n" + +#define ALTINSTR_ENTRY(feature, num) \ + " .long 661b - .\n" /* label */ \ + " .long " b_replacement(num)"f - .\n" /* new instruction */ \ + " .short " __stringify(feature) "\n" /* feature bit */ \ + " .byte " alt_total_slen "\n" /* source len */ \ + " .byte " alt_rlen(num) "\n" /* replacement len */ + +#define ALTINSTR_REPLACEMENT(newinstr, feature, num) /* replacement */ \ + b_replacement(num)":\n\t" newinstr "\n" e_replacement(num) ":\n\t" + +/* alternative assembly primitive: */ +#define ALTERNATIVE(oldinstr, newinstr, feature) \ + OLDINSTR(oldinstr, 1) \ + ".pushsection .altinstructions,\"a\"\n" \ + ALTINSTR_ENTRY(feature, 1) \ + ".popsection\n" \ + ".subsection 1\n" \ + ALTINSTR_REPLACEMENT(newinstr, feature, 1) \ + ".previous\n" + +#define ALTERNATIVE_2(oldinstr, newinstr1, feature1, newinstr2, feature2)\ + OLDINSTR_2(oldinstr, 1, 2) \ + ".pushsection .altinstructions,\"a\"\n" \ + ALTINSTR_ENTRY(feature1, 1) \ + ALTINSTR_ENTRY(feature2, 2) \ + ".popsection\n" \ + ".subsection 1\n" \ + ALTINSTR_REPLACEMENT(newinstr1, feature1, 1) \ + ALTINSTR_REPLACEMENT(newinstr2, feature2, 2) \ + ".previous\n" + +/* + * Alternative instructions for different CPU types or capabilities. + * + * This allows to use optimized instructions even on generic binary + * kernels. + * + * length of oldinstr must be longer or equal the length of newinstr + * It can be padded with nops as needed. + * + * For non barrier like inlines please define new variants + * without volatile and memory clobber. + */ +#define alternative(oldinstr, newinstr, feature) \ + (asm volatile (ALTERNATIVE(oldinstr, newinstr, feature) : : : "memory")) + +#define alternative_2(oldinstr, newinstr1, feature1, newinstr2, feature2) \ + (asm volatile(ALTERNATIVE_2(oldinstr, newinstr1, feature1, newinstr2, feature2) ::: "memory")) + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_ALTERNATIVE_H */ diff --git a/arch/loongarch/include/asm/bugs.h b/arch/loongarch/include/asm/bugs.h new file mode 100644 index 000000000000..98396535163b --- /dev/null +++ b/arch/loongarch/include/asm/bugs.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This is included by init/main.c to check for architecture-dependent bugs. + * + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ +#ifndef _ASM_BUGS_H +#define _ASM_BUGS_H + +#include +#include + +extern void check_bugs(void); + +#endif /* _ASM_BUGS_H */ diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h index 235fa5c537cd..b7ebf2aa217a 100644 --- a/arch/loongarch/include/asm/inst.h +++ b/arch/loongarch/include/asm/inst.h @@ -8,6 +8,8 @@ #include #include +#define INSN_NOP 0x03400000 + #define ADDR_IMMMASK_LU52ID 0xFFF0000000000000 #define ADDR_IMMMASK_LU32ID 0x000FFFFF00000000 #define ADDR_IMMMASK_ADDU16ID 0x00000000FFFF0000 @@ -18,14 +20,25 @@ #define ADDR_IMM(addr, INSN) ((addr & ADDR_IMMMASK_##INSN) >> ADDR_IMMSHIFT_##INSN) +enum reg0i26_op { + b_op = 0x14, + bl_op = 0x15, +}; + enum reg1i20_op { lu12iw_op = 0x0a, lu32id_op = 0x0b, + pcaddi_op = 0x0c, + pcalau12i_op = 0x0d, + pcaddu12i_op = 0x0e, + pcaddu18i_op = 0x0f, }; enum reg1i21_op { beqz_op = 0x10, bnez_op = 0x11, + bceqz_op = 0x12, /* bits[9:8] = 0x00 */ + bcnez_op = 0x12, /* bits[9:8] = 0x01 */ }; enum reg2i12_op { @@ -219,6 +232,20 @@ static inline bool is_imm_negative(unsigned long val, unsigned int bit) return val & (1UL << (bit - 1)); } +static inline unsigned long sign_extend(unsigned long val, unsigned int idx) +{ + if (!is_imm_negative(val, idx + 1)) + return ((1UL << idx) - 1) & val; + else + return ~((1UL << idx) - 1) | val; +} + +static inline bool is_pc_ins(union loongarch_instruction *ip) +{ + return ip->reg1i20_format.opcode >= pcaddi_op && + ip->reg1i20_format.opcode <= pcaddu18i_op; +} + static inline bool is_branch_ins(union loongarch_instruction *ip) { return ip->reg1i21_format.opcode >= beqz_op && diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile index 11cc09679b40..478a8e1709c9 100644 --- a/arch/loongarch/kernel/Makefile +++ b/arch/loongarch/kernel/Makefile @@ -8,7 +8,7 @@ extra-y := head.o vmlinux.lds obj-y += cpu-probe.o cacheinfo.o env.o setup.o entry.o genex.o \ traps.o irq.o idle.o process.o dma.o mem.o io.o reset.o switch.o \ elf.o legacy_boot.o syscall.o signal.o time.o topology.o inst.o ptrace.o vdso.o\ - platform.o + alternative.o platform.o obj-$(CONFIG_ACPI) += acpi.o obj-$(CONFIG_EFI) += efi.o diff --git a/arch/loongarch/kernel/alternative.c b/arch/loongarch/kernel/alternative.c new file mode 100644 index 000000000000..c5aebeac960b --- /dev/null +++ b/arch/loongarch/kernel/alternative.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include + +int __read_mostly alternatives_patched; + +EXPORT_SYMBOL_GPL(alternatives_patched); + +#define MAX_PATCH_SIZE (((u8)(-1)) / LOONGARCH_INSN_SIZE) + +static int __initdata_or_module debug_alternative; + +static int __init debug_alt(char *str) +{ + debug_alternative = 1; + return 1; +} +__setup("debug-alternative", debug_alt); + +#define DPRINTK(fmt, args...) \ +do { \ + if (debug_alternative) \ + printk(KERN_DEBUG "%s: " fmt "\n", __func__, ##args); \ +} while (0) + +#define DUMP_WORDS(buf, count, fmt, args...) \ +do { \ + if (unlikely(debug_alternative)) { \ + int _j; \ + union loongarch_instruction *_buf = buf; \ + \ + if (!(count)) \ + break; \ + \ + printk(KERN_DEBUG fmt, ##args); \ + for (_j = 0; _j < count - 1; _j++) \ + printk(KERN_CONT "<%08x> ", _buf[_j].word); \ + printk(KERN_CONT "<%08x>\n", _buf[_j].word); \ + } \ +} while (0) + +/* Use this to add nops to a buffer, then text_poke the whole buffer. */ +static void __init_or_module add_nops(union loongarch_instruction *insn, int count) +{ + while (count--) { + insn->word = INSN_NOP; + insn++; + } +} + +/* Is the jump addr in local .altinstructions */ +static inline bool in_alt_jump(unsigned long jump, void *start, void *end) +{ + return jump >= (unsigned long)start && jump < (unsigned long)end; +} + +static void __init_or_module recompute_jump(union loongarch_instruction *buf, + union loongarch_instruction *dest, union loongarch_instruction *src, + void *start, void *end) +{ + unsigned int si, si_l, si_h; + unsigned long cur_pc, jump_addr, pc; + long offset; + + cur_pc = (unsigned long)src; + pc = (unsigned long)dest; + + si_l = src->reg0i26_format.immediate_l; + si_h = src->reg0i26_format.immediate_h; + switch (src->reg0i26_format.opcode) { + case b_op: + case bl_op: + jump_addr = cur_pc + sign_extend((si_h << 16 | si_l) << 2, 27); + if (in_alt_jump(jump_addr, start, end)) + return; + offset = jump_addr - pc; + BUG_ON(offset < -SZ_128M || offset >= SZ_128M); + offset >>= 2; + buf->reg0i26_format.immediate_h = offset >> 16; + buf->reg0i26_format.immediate_l = offset; + return; + } + + si_l = src->reg1i21_format.immediate_l; + si_h = src->reg1i21_format.immediate_h; + switch (src->reg1i21_format.opcode) { + case bceqz_op: /* bceqz_op = bcnez_op */ + BUG_ON(buf->reg1i21_format.rj & BIT(4)); + fallthrough; + case beqz_op: + case bnez_op: + jump_addr = cur_pc + sign_extend((si_h << 16 | si_l) << 2, 22); + if (in_alt_jump(jump_addr, start, end)) + return; + offset = jump_addr - pc; + BUG_ON(offset < -SZ_4M || offset >= SZ_4M); + offset >>= 2; + buf->reg1i21_format.immediate_h = offset >> 16; + buf->reg1i21_format.immediate_l = offset; + return; + } + + si = src->reg2i16_format.immediate; + switch (src->reg2i16_format.opcode) { + case beq_op: + case bne_op: + case blt_op: + case bge_op: + case bltu_op: + case bgeu_op: + jump_addr = cur_pc + sign_extend(si << 2, 17); + if (in_alt_jump(jump_addr, start, end)) + return; + offset = jump_addr - pc; + BUG_ON(offset < -SZ_128K || offset >= SZ_128K); + offset >>= 2; + buf->reg2i16_format.immediate = offset; + return; + } +} + +static int __init_or_module copy_alt_insns(union loongarch_instruction *buf, + union loongarch_instruction *dest, union loongarch_instruction *src, int nr) +{ + int i; + + for (i = 0; i < nr; i++) { + buf[i].word = src[i].word; + + if (is_pc_ins(&src[i])) { + pr_err("Not support pcrel instruction at present!"); + return -EINVAL; + } + + if (is_branch_ins(&src[i]) && + src[i].reg2i16_format.opcode != jirl_op) { + recompute_jump(&buf[i], &dest[i], &src[i], src, src + nr); + } + } + + return 0; +} + +/* + * text_poke_early - Update instructions on a live kernel at boot time + * + * When you use this code to patch more than one byte of an instruction + * you need to make sure that other CPUs cannot execute this code in parallel. + * Also no thread must be currently preempted in the middle of these + * instructions. And on the local CPU you need to be protected again NMI or MCE + * handlers seeing an inconsistent instruction while you patch. + */ +static void *__init_or_module text_poke_early(union loongarch_instruction *insn, + union loongarch_instruction *buf, unsigned int nr) +{ + int i; + unsigned long flags; + + local_irq_save(flags); + + for (i = 0; i < nr; i++) + insn[i].word = buf[i].word; + + local_irq_restore(flags); + + wbflush(); + flush_icache_range((unsigned long)insn, (unsigned long)(insn + nr)); + + return insn; +} + +/* + * Replace instructions with better alternatives for this CPU type. This runs + * before SMP is initialized to avoid SMP problems with self modifying code. + * This implies that asymmetric systems where APs have less capabilities than + * the boot processor are not handled. Tough. Make sure you disable such + * features by hand. + */ +void __init_or_module apply_alternatives(struct alt_instr *start, struct alt_instr *end) +{ + struct alt_instr *a; + unsigned int nr_instr, nr_repl, nr_insnbuf; + union loongarch_instruction *instr, *replacement; + union loongarch_instruction insnbuf[MAX_PATCH_SIZE]; + + DPRINTK("alt table %px, -> %px", start, end); + /* + * The scan order should be from start to end. A later scanned + * alternative code can overwrite previously scanned alternative code. + * Some kernel functions (e.g. memcpy, memset, etc) use this order to + * patch code. + * + * So be careful if you want to change the scan order to any other + * order. + */ + for (a = start; a < end; a++) { + nr_insnbuf = 0; + + instr = (void *)&a->instr_offset + a->instr_offset; + replacement = (void *)&a->replace_offset + a->replace_offset; + + BUG_ON(a->instrlen > sizeof(insnbuf)); + BUG_ON(a->instrlen & 0x3); + BUG_ON(a->replacementlen & 0x3); + + nr_instr = a->instrlen / LOONGARCH_INSN_SIZE; + nr_repl = a->replacementlen / LOONGARCH_INSN_SIZE; + + if (!cpu_has(a->feature)) { + DPRINTK("feat not exist: %d, old: (%px len: %d), repl: (%px, len: %d)", + a->feature, instr, a->instrlen, + replacement, a->replacementlen); + + continue; + } + + DPRINTK("feat: %d, old: (%px len: %d), repl: (%px, len: %d)", + a->feature, instr, a->instrlen, + replacement, a->replacementlen); + + DUMP_WORDS(instr, nr_instr, "%px: old_insn: ", instr); + DUMP_WORDS(replacement, nr_repl, "%px: rpl_insn: ", replacement); + + copy_alt_insns(insnbuf, instr, replacement, nr_repl); + nr_insnbuf = nr_repl; + + if (nr_instr > nr_repl) { + add_nops(insnbuf + nr_repl, nr_instr - nr_repl); + nr_insnbuf += nr_instr - nr_repl; + } + DUMP_WORDS(insnbuf, nr_insnbuf, "%px: final_insn: ", instr); + + text_poke_early(instr, insnbuf, nr_insnbuf); + } +} + +void __init alternative_instructions(void) +{ + apply_alternatives(__alt_instructions, __alt_instructions_end); + + alternatives_patched = 1; +} diff --git a/arch/loongarch/kernel/module.c b/arch/loongarch/kernel/module.c index bee7457db804..8127df5dd0ad 100644 --- a/arch/loongarch/kernel/module.c +++ b/arch/loongarch/kernel/module.c @@ -17,6 +17,7 @@ #include #include #include +#include static inline bool signed_imm_check(long val, unsigned int bit) { @@ -466,3 +467,17 @@ void *module_alloc(unsigned long size) return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END, GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE, __builtin_return_address(0)); } + +int module_finalize(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, struct module *mod) +{ + const Elf_Shdr *s, *se; + const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; + + for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) { + if (!strcmp(".altinstructions", secstrs + s->sh_name)) + apply_alternatives((void *)s->sh_addr, (void *)s->sh_addr + s->sh_size); + } + + return 0; +} diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c index 35d99525485b..075fd4f778ea 100644 --- a/arch/loongarch/kernel/setup.c +++ b/arch/loongarch/kernel/setup.c @@ -29,7 +29,9 @@ #include #include +#include #include +#include #include #include #include @@ -79,6 +81,11 @@ const char *get_system_type(void) return "generic-loongson-machine"; } +void __init check_bugs(void) +{ + alternative_instructions(); +} + static const char *dmi_string_parse(const struct dmi_header *dm, u8 s) { const u8 *bp = ((u8 *) dm) + dm->length; diff --git a/arch/loongarch/kernel/vmlinux.lds.S b/arch/loongarch/kernel/vmlinux.lds.S index e956e2e3c510..c673347a80bb 100644 --- a/arch/loongarch/kernel/vmlinux.lds.S +++ b/arch/loongarch/kernel/vmlinux.lds.S @@ -54,6 +54,18 @@ SECTIONS . = ALIGN(PECOFF_SEGMENT_ALIGN); _etext = .; + /* + * struct alt_inst entries. From the header (alternative.h): + * "Alternative instructions for different CPU types or capabilities" + * Think locking instructions on spinlocks. + */ + . = ALIGN(4); + .altinstructions : AT(ADDR(.altinstructions) - LOAD_OFFSET) { + __alt_instructions = .; + *(.altinstructions) + __alt_instructions_end = .; + } + . = ALIGN(PECOFF_SEGMENT_ALIGN); __init_begin = .; __inittext_begin = .; -- Gitee From f41046c96ef34e90caa4b9f07d676f4e13b83045 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Sat, 10 Dec 2022 22:39:59 +0800 Subject: [PATCH 54/56] LoongArch: Use alternative to optimize libraries Use the alternative to optimize common libraries according whether CPU has UAL (hardware unaligned access support) feature, including memset(), memcopy(), memmove(), copy_user() and clear_user(). We have tested UnixBench on a Loongson-3A5000 quad-core machine (1.6GHz): 1, One copy, before patch: System Benchmarks Index Values BASELINE RESULT INDEX Dhrystone 2 using register variables 116700.0 9566582.0 819.8 Double-Precision Whetstone 55.0 2805.3 510.1 Execl Throughput 43.0 2120.0 493.0 File Copy 1024 bufsize 2000 maxblocks 3960.0 209833.0 529.9 File Copy 256 bufsize 500 maxblocks 1655.0 89400.0 540.2 File Copy 4096 bufsize 8000 maxblocks 5800.0 320036.0 551.8 Pipe Throughput 12440.0 340624.0 273.8 Pipe-based Context Switching 4000.0 109939.1 274.8 Process Creation 126.0 4728.7 375.3 Shell Scripts (1 concurrent) 42.4 2223.1 524.3 Shell Scripts (8 concurrent) 6.0 883.1 1471.9 System Call Overhead 15000.0 518639.1 345.8 ======== System Benchmarks Index Score 500.2 2, One copy, after patch: System Benchmarks Index Values BASELINE RESULT INDEX Dhrystone 2 using register variables 116700.0 9567674.7 819.9 Double-Precision Whetstone 55.0 2805.5 510.1 Execl Throughput 43.0 2392.7 556.4 File Copy 1024 bufsize 2000 maxblocks 3960.0 417804.0 1055.1 File Copy 256 bufsize 500 maxblocks 1655.0 112909.5 682.2 File Copy 4096 bufsize 8000 maxblocks 5800.0 1255207.4 2164.2 Pipe Throughput 12440.0 555712.0 446.7 Pipe-based Context Switching 4000.0 99964.5 249.9 Process Creation 126.0 5192.5 412.1 Shell Scripts (1 concurrent) 42.4 2302.4 543.0 Shell Scripts (8 concurrent) 6.0 919.6 1532.6 System Call Overhead 15000.0 511159.3 340.8 ======== System Benchmarks Index Score 640.1 3, Four copies, before patch: System Benchmarks Index Values BASELINE RESULT INDEX Dhrystone 2 using register variables 116700.0 38268610.5 3279.2 Double-Precision Whetstone 55.0 11222.2 2040.4 Execl Throughput 43.0 7892.0 1835.3 File Copy 1024 bufsize 2000 maxblocks 3960.0 235149.6 593.8 File Copy 256 bufsize 500 maxblocks 1655.0 74959.6 452.9 File Copy 4096 bufsize 8000 maxblocks 5800.0 545048.5 939.7 Pipe Throughput 12440.0 1337359.0 1075.0 Pipe-based Context Switching 4000.0 473663.9 1184.2 Process Creation 126.0 17491.2 1388.2 Shell Scripts (1 concurrent) 42.4 6865.7 1619.3 Shell Scripts (8 concurrent) 6.0 1015.9 1693.1 System Call Overhead 15000.0 1899535.2 1266.4 ======== System Benchmarks Index Score 1278.3 4, Four copies, after patch: System Benchmarks Index Values BASELINE RESULT INDEX Dhrystone 2 using register variables 116700.0 38272815.5 3279.6 Double-Precision Whetstone 55.0 11222.8 2040.5 Execl Throughput 43.0 8839.2 2055.6 File Copy 1024 bufsize 2000 maxblocks 3960.0 313912.9 792.7 File Copy 256 bufsize 500 maxblocks 1655.0 80976.1 489.3 File Copy 4096 bufsize 8000 maxblocks 5800.0 1176594.3 2028.6 Pipe Throughput 12440.0 2100941.9 1688.9 Pipe-based Context Switching 4000.0 476696.4 1191.7 Process Creation 126.0 18394.7 1459.9 Shell Scripts (1 concurrent) 42.4 7172.2 1691.6 Shell Scripts (8 concurrent) 6.0 1058.3 1763.9 System Call Overhead 15000.0 1874714.7 1249.8 ======== System Benchmarks Index Score 1488.8 Signed-off-by: Jun Yi Signed-off-by: Huacai Chen Change-Id: I5dc8f19abfe6fcdfbe473b1433e65a7d4d5d110b --- arch/loongarch/include/asm/string.h | 5 ++ arch/loongarch/lib/Makefile | 3 +- arch/loongarch/lib/clear_user.S | 70 ++++++++++++++-- arch/loongarch/lib/copy_user.S | 91 +++++++++++++++++++-- arch/loongarch/lib/memcpy.S | 95 ++++++++++++++++++++++ arch/loongarch/lib/memmove.S | 121 ++++++++++++++++++++++++++++ arch/loongarch/lib/memset.S | 91 +++++++++++++++++++++ 7 files changed, 465 insertions(+), 11 deletions(-) create mode 100644 arch/loongarch/lib/memcpy.S create mode 100644 arch/loongarch/lib/memmove.S create mode 100644 arch/loongarch/lib/memset.S diff --git a/arch/loongarch/include/asm/string.h b/arch/loongarch/include/asm/string.h index b07e60ded957..7b29cc9c70aa 100644 --- a/arch/loongarch/include/asm/string.h +++ b/arch/loongarch/include/asm/string.h @@ -5,8 +5,13 @@ #ifndef _ASM_STRING_H #define _ASM_STRING_H +#define __HAVE_ARCH_MEMSET extern void *memset(void *__s, int __c, size_t __count); + +#define __HAVE_ARCH_MEMCPY extern void *memcpy(void *__to, __const__ void *__from, size_t __n); + +#define __HAVE_ARCH_MEMMOVE extern void *memmove(void *__dest, __const__ void *__src, size_t __n); #endif /* _ASM_STRING_H */ diff --git a/arch/loongarch/lib/Makefile b/arch/loongarch/lib/Makefile index 1bb75cd8caf7..8263fe4ca6a2 100644 --- a/arch/loongarch/lib/Makefile +++ b/arch/loongarch/lib/Makefile @@ -3,4 +3,5 @@ # Makefile for LoongArch-specific library files. # -lib-y += delay.o clear_user.o strnlen_user.o strncpy_user.o copy_user.o dump_tlb.o +lib-y += delay.o clear_user.o strnlen_user.o strncpy_user.o copy_user.o dump_tlb.o \ + memset.o memcpy.o memmove.o diff --git a/arch/loongarch/lib/clear_user.S b/arch/loongarch/lib/clear_user.S index d5c9e44ac8c4..2dc48e61a2c8 100644 --- a/arch/loongarch/lib/clear_user.S +++ b/arch/loongarch/lib/clear_user.S @@ -3,25 +3,37 @@ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited */ +#include #include #include #include +#include #include #include -.irp to, 0 +.irp to, 0, 1, 2, 3, 4, 5, 6, 7 .L_fixup_handle_\to\(): addi.d a0, a1, (\to) * (-8) jr ra .endr +SYM_FUNC_START(__clear_user) + /* + * Some CPUs support hardware unaligned access + */ + ALTERNATIVE "b __clear_user_generic", \ + "b __clear_user_fast", CPU_FEATURE_UAL +SYM_FUNC_END(__clear_user) + +EXPORT_SYMBOL(__clear_user) + /* - * unsigned long __clear_user(void *addr, size_t size) + * unsigned long __clear_user_generic(void *addr, size_t size) * * a0: addr * a1: size */ -SYM_FUNC_START(__clear_user) +SYM_FUNC_START(__clear_user_generic) beqz a1, 2f 1: st.b zero, a0, 0 @@ -33,6 +45,54 @@ SYM_FUNC_START(__clear_user) jr ra _asm_extable 1b, .L_fixup_handle_0 -SYM_FUNC_END(__clear_user) +SYM_FUNC_END(__clear_user_generic) -EXPORT_SYMBOL(__clear_user) +/* + * unsigned long __clear_user_fast(void *addr, unsigned long size) + * + * a0: addr + * a1: size + */ +SYM_FUNC_START(__clear_user_fast) + beqz a1, 10f + + ori a2, zero, 64 + blt a1, a2, 9f + + /* set 64 bytes at a time */ +1: st.d zero, a0, 0 +2: st.d zero, a0, 8 +3: st.d zero, a0, 16 +4: st.d zero, a0, 24 +5: st.d zero, a0, 32 +6: st.d zero, a0, 40 +7: st.d zero, a0, 48 +8: st.d zero, a0, 56 + + addi.d a0, a0, 64 + addi.d a1, a1, -64 + bge a1, a2, 1b + + beqz a1, 10f + + /* set the remaining bytes */ +9: st.b zero, a0, 0 + addi.d a0, a0, 1 + addi.d a1, a1, -1 + bgt a1, zero, 9b + + /* return */ +10: move a0, a1 + jr ra + + /* fixup and ex_table */ + _asm_extable 1b, .L_fixup_handle_0 + _asm_extable 2b, .L_fixup_handle_1 + _asm_extable 3b, .L_fixup_handle_2 + _asm_extable 4b, .L_fixup_handle_3 + _asm_extable 5b, .L_fixup_handle_4 + _asm_extable 6b, .L_fixup_handle_5 + _asm_extable 7b, .L_fixup_handle_6 + _asm_extable 8b, .L_fixup_handle_7 + _asm_extable 9b, .L_fixup_handle_0 +SYM_FUNC_END(__clear_user_fast) diff --git a/arch/loongarch/lib/copy_user.S b/arch/loongarch/lib/copy_user.S index 61933d964da0..55ac6020a1ad 100644 --- a/arch/loongarch/lib/copy_user.S +++ b/arch/loongarch/lib/copy_user.S @@ -3,26 +3,38 @@ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited */ +#include #include #include #include +#include #include #include -.irp to, 0 +.irp to, 0, 1, 2, 3, 4, 5, 6, 7 .L_fixup_handle_\to\(): addi.d a0, a2, (\to) * (-8) jr ra .endr +SYM_FUNC_START(__copy_user) + /* + * Some CPUs support hardware unaligned access + */ + ALTERNATIVE "b __copy_user_generic", \ + "b __copy_user_fast", CPU_FEATURE_UAL +SYM_FUNC_END(__copy_user) + +EXPORT_SYMBOL(__copy_user) + /* - * unsigned long __copy_user(void *to, const void *from, size_t n) + * unsigned long __copy_user_generic(void *to, const void *from, size_t n) * * a0: to * a1: from * a2: n */ -SYM_FUNC_START(__copy_user) +SYM_FUNC_START(__copy_user_generic) beqz a2, 3f 1: ld.b t0, a1, 0 @@ -37,6 +49,75 @@ SYM_FUNC_START(__copy_user) _asm_extable 1b, .L_fixup_handle_0 _asm_extable 2b, .L_fixup_handle_0 -SYM_FUNC_END(__copy_user) +SYM_FUNC_END(__copy_user_generic) -EXPORT_SYMBOL(__copy_user) +/* + * unsigned long __copy_user_fast(void *to, const void *from, unsigned long n) + * + * a0: to + * a1: from + * a2: n + */ +SYM_FUNC_START(__copy_user_fast) + beqz a2, 19f + + ori a3, zero, 64 + blt a2, a3, 17f + + /* copy 64 bytes at a time */ +1: ld.d t0, a1, 0 +2: ld.d t1, a1, 8 +3: ld.d t2, a1, 16 +4: ld.d t3, a1, 24 +5: ld.d t4, a1, 32 +6: ld.d t5, a1, 40 +7: ld.d t6, a1, 48 +8: ld.d t7, a1, 56 +9: st.d t0, a0, 0 +10: st.d t1, a0, 8 +11: st.d t2, a0, 16 +12: st.d t3, a0, 24 +13: st.d t4, a0, 32 +14: st.d t5, a0, 40 +15: st.d t6, a0, 48 +16: st.d t7, a0, 56 + + addi.d a0, a0, 64 + addi.d a1, a1, 64 + addi.d a2, a2, -64 + bge a2, a3, 1b + + beqz a2, 19f + + /* copy the remaining bytes */ +17: ld.b t0, a1, 0 +18: st.b t0, a0, 0 + addi.d a0, a0, 1 + addi.d a1, a1, 1 + addi.d a2, a2, -1 + bgt a2, zero, 17b + + /* return */ +19: move a0, a2 + jr ra + + /* fixup and ex_table */ + _asm_extable 1b, .L_fixup_handle_0 + _asm_extable 2b, .L_fixup_handle_1 + _asm_extable 3b, .L_fixup_handle_2 + _asm_extable 4b, .L_fixup_handle_3 + _asm_extable 5b, .L_fixup_handle_4 + _asm_extable 6b, .L_fixup_handle_5 + _asm_extable 7b, .L_fixup_handle_6 + _asm_extable 8b, .L_fixup_handle_7 + _asm_extable 9b, .L_fixup_handle_0 + _asm_extable 10b, .L_fixup_handle_1 + _asm_extable 11b, .L_fixup_handle_2 + _asm_extable 12b, .L_fixup_handle_3 + _asm_extable 13b, .L_fixup_handle_4 + _asm_extable 14b, .L_fixup_handle_5 + _asm_extable 15b, .L_fixup_handle_6 + _asm_extable 16b, .L_fixup_handle_7 + _asm_extable 17b, .L_fixup_handle_0 + _asm_extable 18b, .L_fixup_handle_0 +SYM_FUNC_END(__copy_user_fast) diff --git a/arch/loongarch/lib/memcpy.S b/arch/loongarch/lib/memcpy.S new file mode 100644 index 000000000000..7c07d595ee89 --- /dev/null +++ b/arch/loongarch/lib/memcpy.S @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include +#include +#include + +SYM_FUNC_START(memcpy) + /* + * Some CPUs support hardware unaligned access + */ + ALTERNATIVE "b __memcpy_generic", \ + "b __memcpy_fast", CPU_FEATURE_UAL +SYM_FUNC_END(memcpy) + +EXPORT_SYMBOL(memcpy) + +/* + * void *__memcpy_generic(void *dst, const void *src, size_t n) + * + * a0: dst + * a1: src + * a2: n + */ +SYM_FUNC_START(__memcpy_generic) + move a3, a0 + beqz a2, 2f + +1: ld.b t0, a1, 0 + st.b t0, a0, 0 + addi.d a0, a0, 1 + addi.d a1, a1, 1 + addi.d a2, a2, -1 + bgt a2, zero, 1b + +2: move a0, a3 + jr ra +SYM_FUNC_END(__memcpy_generic) + +/* + * void *__memcpy_fast(void *dst, const void *src, size_t n) + * + * a0: dst + * a1: src + * a2: n + */ +SYM_FUNC_START(__memcpy_fast) + move a3, a0 + beqz a2, 3f + + ori a4, zero, 64 + blt a2, a4, 2f + + /* copy 64 bytes at a time */ +1: ld.d t0, a1, 0 + ld.d t1, a1, 8 + ld.d t2, a1, 16 + ld.d t3, a1, 24 + ld.d t4, a1, 32 + ld.d t5, a1, 40 + ld.d t6, a1, 48 + ld.d t7, a1, 56 + st.d t0, a0, 0 + st.d t1, a0, 8 + st.d t2, a0, 16 + st.d t3, a0, 24 + st.d t4, a0, 32 + st.d t5, a0, 40 + st.d t6, a0, 48 + st.d t7, a0, 56 + + addi.d a0, a0, 64 + addi.d a1, a1, 64 + addi.d a2, a2, -64 + bge a2, a4, 1b + + beqz a2, 3f + + /* copy the remaining bytes */ +2: ld.b t0, a1, 0 + st.b t0, a0, 0 + addi.d a0, a0, 1 + addi.d a1, a1, 1 + addi.d a2, a2, -1 + bgt a2, zero, 2b + + /* return */ +3: move a0, a3 + jr ra +SYM_FUNC_END(__memcpy_fast) diff --git a/arch/loongarch/lib/memmove.S b/arch/loongarch/lib/memmove.S new file mode 100644 index 000000000000..6ffdb46da78f --- /dev/null +++ b/arch/loongarch/lib/memmove.S @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include +#include +#include + +SYM_FUNC_START(memmove) + blt a0, a1, 1f /* dst < src, memcpy */ + blt a1, a0, 3f /* src < dst, rmemcpy */ + jr ra /* dst == src, return */ + + /* if (src - dst) < 64, copy 1 byte at a time */ +1: ori a3, zero, 64 + sub.d t0, a1, a0 + blt t0, a3, 2f + b memcpy +2: b __memcpy_generic + + /* if (dst - src) < 64, copy 1 byte at a time */ +3: ori a3, zero, 64 + sub.d t0, a0, a1 + blt t0, a3, 4f + b rmemcpy +4: b __rmemcpy_generic +SYM_FUNC_END(memmove) + +EXPORT_SYMBOL(memmove) + +SYM_FUNC_START(rmemcpy) + /* + * Some CPUs support hardware unaligned access + */ + ALTERNATIVE "b __rmemcpy_generic", \ + "b __rmemcpy_fast", CPU_FEATURE_UAL +SYM_FUNC_END(rmemcpy) + +/* + * void *__rmemcpy_generic(void *dst, const void *src, size_t n) + * + * a0: dst + * a1: src + * a2: n + */ +SYM_FUNC_START(__rmemcpy_generic) + move a3, a0 + beqz a2, 2f + + add.d a0, a0, a2 + add.d a1, a1, a2 + +1: ld.b t0, a1, -1 + st.b t0, a0, -1 + addi.d a0, a0, -1 + addi.d a1, a1, -1 + addi.d a2, a2, -1 + bgt a2, zero, 1b + +2: move a0, a3 + jr ra +SYM_FUNC_END(__rmemcpy_generic) + +/* + * void *__rmemcpy_fast(void *dst, const void *src, size_t n) + * + * a0: dst + * a1: src + * a2: n + */ +SYM_FUNC_START(__rmemcpy_fast) + move a3, a0 + beqz a2, 3f + + add.d a0, a0, a2 + add.d a1, a1, a2 + + ori a4, zero, 64 + blt a2, a4, 2f + + /* copy 64 bytes at a time */ +1: ld.d t0, a1, -8 + ld.d t1, a1, -16 + ld.d t2, a1, -24 + ld.d t3, a1, -32 + ld.d t4, a1, -40 + ld.d t5, a1, -48 + ld.d t6, a1, -56 + ld.d t7, a1, -64 + st.d t0, a0, -8 + st.d t1, a0, -16 + st.d t2, a0, -24 + st.d t3, a0, -32 + st.d t4, a0, -40 + st.d t5, a0, -48 + st.d t6, a0, -56 + st.d t7, a0, -64 + + addi.d a0, a0, -64 + addi.d a1, a1, -64 + addi.d a2, a2, -64 + bge a2, a4, 1b + + beqz a2, 3f + + /* copy the remaining bytes */ +2: ld.b t0, a1, -1 + st.b t0, a0, -1 + addi.d a0, a0, -1 + addi.d a1, a1, -1 + addi.d a2, a2, -1 + bgt a2, zero, 2b + + /* return */ +3: move a0, a3 + jr ra +SYM_FUNC_END(__rmemcpy_fast) diff --git a/arch/loongarch/lib/memset.S b/arch/loongarch/lib/memset.S new file mode 100644 index 000000000000..e7cb4ea3747d --- /dev/null +++ b/arch/loongarch/lib/memset.S @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited + */ + +#include +#include +#include +#include +#include +#include + +.macro fill_to_64 r0 + bstrins.d \r0, \r0, 15, 8 + bstrins.d \r0, \r0, 31, 16 + bstrins.d \r0, \r0, 63, 32 +.endm + +SYM_FUNC_START(memset) + /* + * Some CPUs support hardware unaligned access + */ + ALTERNATIVE "b __memset_generic", \ + "b __memset_fast", CPU_FEATURE_UAL +SYM_FUNC_END(memset) + +EXPORT_SYMBOL(memset) + +/* + * void *__memset_generic(void *s, int c, size_t n) + * + * a0: s + * a1: c + * a2: n + */ +SYM_FUNC_START(__memset_generic) + move a3, a0 + beqz a2, 2f + +1: st.b a1, a0, 0 + addi.d a0, a0, 1 + addi.d a2, a2, -1 + bgt a2, zero, 1b + +2: move a0, a3 + jr ra +SYM_FUNC_END(__memset_generic) + +/* + * void *__memset_fast(void *s, int c, size_t n) + * + * a0: s + * a1: c + * a2: n + */ +SYM_FUNC_START(__memset_fast) + move a3, a0 + beqz a2, 3f + + ori a4, zero, 64 + blt a2, a4, 2f + + /* fill a1 to 64 bits */ + fill_to_64 a1 + + /* set 64 bytes at a time */ +1: st.d a1, a0, 0 + st.d a1, a0, 8 + st.d a1, a0, 16 + st.d a1, a0, 24 + st.d a1, a0, 32 + st.d a1, a0, 40 + st.d a1, a0, 48 + st.d a1, a0, 56 + + addi.d a0, a0, 64 + addi.d a2, a2, -64 + bge a2, a4, 1b + + beqz a2, 3f + + /* set the remaining bytes */ +2: st.b a1, a0, 0 + addi.d a0, a0, 1 + addi.d a2, a2, -1 + bgt a2, zero, 2b + + /* return */ +3: move a0, a3 + jr ra +SYM_FUNC_END(__memset_fast) -- Gitee From bacbf24b3957cd1aa5920ebd00f0116221e13640 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Tue, 22 May 2018 16:54:04 +0800 Subject: [PATCH 55/56] drm/loongson: add kernel modesetting driver support for ls7a1000/ls7a2000 1) Support Double Screen HW Cursor 2) Support LS7A1000/LS7A2000 + 3A5000 3) CRTC's DMA Step is remain 256 bytes Change-Id: Id3c3a7bebf1e95dcc882f69ba20ff3b7e57d275d Signed-off-by: Jingfeng Sui Signed-off-by: Huacai Chen --- arch/loongarch/configs/loongson3_defconfig | 1 + drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/loongson/Kconfig | 26 + drivers/gpu/drm/loongson/Makefile | 16 + drivers/gpu/drm/loongson/lsdc_crtc.c | 420 +++++++++++++++ drivers/gpu/drm/loongson/lsdc_debugfs.c | 176 ++++++ drivers/gpu/drm/loongson/lsdc_debugfs.h | 17 + drivers/gpu/drm/loongson/lsdc_drv.c | 427 +++++++++++++++ drivers/gpu/drm/loongson/lsdc_drv.h | 184 +++++++ drivers/gpu/drm/loongson/lsdc_i2c.c | 288 ++++++++++ drivers/gpu/drm/loongson/lsdc_i2c.h | 38 ++ drivers/gpu/drm/loongson/lsdc_irq.c | 57 ++ drivers/gpu/drm/loongson/lsdc_irq.h | 17 + drivers/gpu/drm/loongson/lsdc_output.c | 412 ++++++++++++++ drivers/gpu/drm/loongson/lsdc_output.h | 21 + drivers/gpu/drm/loongson/lsdc_pci_drv.c | 352 ++++++++++++ drivers/gpu/drm/loongson/lsdc_plane.c | 476 ++++++++++++++++ drivers/gpu/drm/loongson/lsdc_pll.c | 599 +++++++++++++++++++++ drivers/gpu/drm/loongson/lsdc_pll.h | 87 +++ drivers/gpu/drm/loongson/lsdc_regs.h | 252 +++++++++ 21 files changed, 3869 insertions(+) create mode 100644 drivers/gpu/drm/loongson/Kconfig create mode 100644 drivers/gpu/drm/loongson/Makefile create mode 100644 drivers/gpu/drm/loongson/lsdc_crtc.c create mode 100644 drivers/gpu/drm/loongson/lsdc_debugfs.c create mode 100644 drivers/gpu/drm/loongson/lsdc_debugfs.h create mode 100644 drivers/gpu/drm/loongson/lsdc_drv.c create mode 100644 drivers/gpu/drm/loongson/lsdc_drv.h create mode 100644 drivers/gpu/drm/loongson/lsdc_i2c.c create mode 100644 drivers/gpu/drm/loongson/lsdc_i2c.h create mode 100644 drivers/gpu/drm/loongson/lsdc_irq.c create mode 100644 drivers/gpu/drm/loongson/lsdc_irq.h create mode 100644 drivers/gpu/drm/loongson/lsdc_output.c create mode 100644 drivers/gpu/drm/loongson/lsdc_output.h create mode 100644 drivers/gpu/drm/loongson/lsdc_pci_drv.c create mode 100644 drivers/gpu/drm/loongson/lsdc_plane.c create mode 100644 drivers/gpu/drm/loongson/lsdc_pll.c create mode 100644 drivers/gpu/drm/loongson/lsdc_pll.h create mode 100644 drivers/gpu/drm/loongson/lsdc_regs.h diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index cd96f0607993..9ca381643390 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -600,6 +600,7 @@ CONFIG_DRM_AMDGPU_SI=y CONFIG_DRM_AMDGPU_CIK=y CONFIG_DRM_AMDGPU_USERPTR=y CONFIG_DRM_AST=y +CONFIG_DRM_LOONGSON=y CONFIG_DRM_QXL=m CONFIG_DRM_VIRTIO_GPU=m CONFIG_FB_EFI=y diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index ca868271f4c4..af407d01e1c4 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -312,6 +312,8 @@ source "drivers/gpu/drm/udl/Kconfig" source "drivers/gpu/drm/ast/Kconfig" +source "drivers/gpu/drm/loongson/Kconfig" + source "drivers/gpu/drm/mgag200/Kconfig" source "drivers/gpu/drm/armada/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 81569009f884..e9dd6847c9fa 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -86,6 +86,7 @@ obj-$(CONFIG_DRM_ROCKCHIP) +=rockchip/ obj-$(CONFIG_DRM_GMA500) += gma500/ obj-$(CONFIG_DRM_UDL) += udl/ obj-$(CONFIG_DRM_AST) += ast/ +obj-$(CONFIG_DRM_LOONGSON) +=loongson/ obj-$(CONFIG_DRM_ARMADA) += armada/ obj-$(CONFIG_DRM_ATMEL_HLCDC) += atmel-hlcdc/ obj-y += rcar-du/ diff --git a/drivers/gpu/drm/loongson/Kconfig b/drivers/gpu/drm/loongson/Kconfig new file mode 100644 index 000000000000..5d747271ffaf --- /dev/null +++ b/drivers/gpu/drm/loongson/Kconfig @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0 + +config DRM_LOONGSON + tristate "DRM Support for loongson's display controller" + depends on DRM && PCI + depends on MACH_LOONGSON64 || LOONGARCH || MIPS || COMPILE_TEST + select OF + select CMA if HAVE_DMA_CONTIGUOUS + select DMA_CMA if HAVE_DMA_CONTIGUOUS + select DRM_KMS_HELPER + select DRM_KMS_FB_HELPER + select DRM_KMS_CMA_HELPER + select DRM_GEM_CMA_HELPER + select DRM_TTM + select DRM_TTM_HELPER + select DRM_VRAM_HELPER + select VIDEOMODE_HELPERS + select DRM_BRIDGE + select DRM_PANEL_BRIDGE + default y + help + This is a KMS driver for the display controller in the LS7A1000 + bridge chip and LS2K1000/LS2K0500 SoC. + If "M" is selected, the module will be called loongson. + + If in doubt, say "Y". diff --git a/drivers/gpu/drm/loongson/Makefile b/drivers/gpu/drm/loongson/Makefile new file mode 100644 index 000000000000..cf6391b8eb17 --- /dev/null +++ b/drivers/gpu/drm/loongson/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 + +loongson-y := \ + lsdc_drv.o \ + lsdc_crtc.o \ + lsdc_irq.o \ + lsdc_plane.o \ + lsdc_pll.o \ + lsdc_i2c.o \ + lsdc_output.o \ + lsdc_pci_drv.o \ + lsdc_debugfs.o \ + +lsdc-$(CONFIG_DEBUG_FS) += lsdc_debugfs.o + +obj-$(CONFIG_DRM_LOONGSON) += loongson.o diff --git a/drivers/gpu/drm/loongson/lsdc_crtc.c b/drivers/gpu/drm/loongson/lsdc_crtc.c new file mode 100644 index 000000000000..e59b75d79098 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_crtc.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Loongson Corporation + */ + +/* + * Authors: + * Sui Jingfeng + */ +#include +#include +#include + +#include "lsdc_drv.h" +#include "lsdc_regs.h" +#include "lsdc_pll.h" + +static int lsdc_crtc_enable_vblank(struct drm_crtc *crtc) +{ + struct lsdc_device *ldev = to_lsdc(crtc->dev); + unsigned int index = drm_crtc_index(crtc); + struct drm_crtc_state *state = crtc->state; + u32 val; + + if (state->enable) { + val = readl(ldev->reg_base + LSDC_INT_REG); + + if (index == 0) + val |= INT_CRTC0_VS_EN; + else if (index == 1) + val |= INT_CRTC1_VS_EN; + + writel(val, ldev->reg_base + LSDC_INT_REG); + } + + return 0; +} + +static void lsdc_crtc_disable_vblank(struct drm_crtc *crtc) +{ + struct lsdc_device *ldev = to_lsdc(crtc->dev); + unsigned int index = drm_crtc_index(crtc); + u32 val; + + val = readl(ldev->reg_base + LSDC_INT_REG); + + if (index == 0) + val &= ~INT_CRTC0_VS_EN; + else if (index == 1) + val &= ~INT_CRTC1_VS_EN; + + writel(val, ldev->reg_base + LSDC_INT_REG); +} + +static void lsdc_crtc_reset(struct drm_crtc *crtc) +{ + struct drm_device *ddev = crtc->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + unsigned int index = drm_crtc_index(crtc); + struct lsdc_crtc_state *priv_crtc_state; + u32 val = CFG_RESET_BIT | CFG_OUTPUT_EN_BIT | LSDC_PF_XRGB8888; + + if (ldev->enable_gamma) + val |= CFG_GAMMAR_EN_BIT; + + /* align to 64 */ + if (ldev->desc->chip == LSDC_CHIP_7A2000) { + val &= ~LS7A2000_DMA_STEP_MASK; + val |= DMA_STEP_256_BYTE; + } + + if (index == 0) + writel(val, ldev->reg_base + LSDC_CRTC0_CFG_REG); + else if (index == 1) + writel(val, ldev->reg_base + LSDC_CRTC1_CFG_REG); + + if (crtc->state) { + priv_crtc_state = to_lsdc_crtc_state(crtc->state); + __drm_atomic_helper_crtc_destroy_state(&priv_crtc_state->base); + kfree(priv_crtc_state); + } + + priv_crtc_state = kzalloc(sizeof(*priv_crtc_state), GFP_KERNEL); + if (!priv_crtc_state) + return; + + __drm_atomic_helper_crtc_reset(crtc, &priv_crtc_state->base); + + drm_dbg(ddev, "crtc%u reset\n", index); +} + +static void lsdc_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state) +{ + struct lsdc_crtc_state *priv_crtc_state = to_lsdc_crtc_state(state); + + __drm_atomic_helper_crtc_destroy_state(&priv_crtc_state->base); + + kfree(priv_crtc_state); +} + +static struct drm_crtc_state *lsdc_crtc_atomic_duplicate_state(struct drm_crtc *crtc) +{ + struct lsdc_crtc_state *new_priv_state; + struct lsdc_crtc_state *old_priv_state; + struct drm_device *ddev = crtc->dev; + + if (drm_WARN_ON(ddev, !crtc->state)) + return NULL; + + new_priv_state = kmalloc(sizeof(*new_priv_state), GFP_KERNEL); + if (!new_priv_state) + return NULL; + + __drm_atomic_helper_crtc_duplicate_state(crtc, &new_priv_state->base); + + old_priv_state = to_lsdc_crtc_state(crtc->state); + + memcpy(&new_priv_state->pparams, &old_priv_state->pparams, sizeof(new_priv_state->pparams)); + + return &new_priv_state->base; +} + +static const struct drm_crtc_funcs lsdc_crtc_funcs = { + .reset = lsdc_crtc_reset, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = lsdc_crtc_atomic_duplicate_state, + .atomic_destroy_state = lsdc_crtc_atomic_destroy_state, + .enable_vblank = lsdc_crtc_enable_vblank, + .disable_vblank = lsdc_crtc_disable_vblank, +}; + +static enum drm_mode_status +lsdc_crtc_helper_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + struct drm_device *ddev = crtc->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + const struct lsdc_chip_desc * const descp = ldev->desc; + + if (mode->hdisplay > descp->max_width) + return MODE_BAD_HVALUE; + if (mode->vdisplay > descp->max_height) + return MODE_BAD_VVALUE; + + if (mode->clock > descp->max_pixel_clk) { + drm_dbg(ddev, "mode %dx%d, pixel clock=%d is too high\n", + mode->hdisplay, mode->vdisplay, mode->clock); + return MODE_CLOCK_HIGH; + } + + /* The CRTC hardware dma take 256 bytes once a time, + * this is a limitation of the CRTC. + * TODO: check RGB565 support + */ + if (!ldev->relax_alignment) { + if ((mode->hdisplay * 4) % descp->stride_alignment) { + drm_dbg(ddev, "mode %dx%d, stride is not %u bytes aligned\n", + mode->hdisplay, mode->vdisplay, descp->stride_alignment); + return MODE_BAD; + } + } + + return MODE_OK; +} + +static int lsdc_pixpll_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct lsdc_display_pipe * const dispipe = drm_crtc_to_dispipe(crtc); + struct lsdc_pll * const pixpll = &dispipe->pixpll; + const struct lsdc_pixpll_funcs * const pfuncs = pixpll->funcs; + struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(state); + bool ret; + + ret = pfuncs->compute(pixpll, state->mode.clock, &priv_state->pparams); + if (ret) + return 0; + + drm_warn(crtc->dev, "failed find PLL parameters for %u\n", state->mode.clock); + + return -EINVAL; +} + +static int lsdc_crtc_helper_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + if (!state->enable) + return 0; /* no mode checks if CRTC is being disabled */ + + if (state->mode_changed || state->active_changed || state->connectors_changed) + return lsdc_pixpll_atomic_check(crtc, state); + + return 0; +} + +static void lsdc_update_pixclk(struct drm_crtc *crtc) +{ + struct lsdc_display_pipe * const dispipe = drm_crtc_to_dispipe(crtc); + struct lsdc_pll * const pixpll = &dispipe->pixpll; + const struct lsdc_pixpll_funcs * const clkfun = pixpll->funcs; + struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(crtc->state); + + clkfun->update(pixpll, &priv_state->pparams); +} + +static void lsdc_crtc_helper_mode_set_nofb(struct drm_crtc *crtc) +{ + struct drm_device *ddev = crtc->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + struct drm_display_mode *mode = &crtc->state->adjusted_mode; + unsigned int index = drm_crtc_index(crtc); + u32 h_sync, v_sync, h_val, v_val; + + /* 26:16 total pixels, 10:0 visiable pixels, in horizontal */ + h_val = (mode->crtc_htotal << 16) | mode->crtc_hdisplay; + /* Hack to support non 256 bytes aligned stride, for example: + * 800x480 DPI panel. In this case userspace do the work to + * guarantee the horizontal pixel size is aligned by padding it. + * In actual, We allocate 832x480x4 bytes in size. + */ + if (ldev->relax_alignment) + h_val = (h_val + 63) & ~63; + + /* 26:16 total pixels, 10:0 visiable pixels, in vertical */ + v_val = (mode->crtc_vtotal << 16) | mode->crtc_vdisplay; + /* 26:16 hsync end, 10:0 hsync start, bit 30 is hsync enable */ + h_sync = (mode->crtc_hsync_end << 16) | mode->crtc_hsync_start | EN_HSYNC_BIT; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + h_sync |= INV_HSYNC_BIT; + + /* 26:16 vsync end, 10:0 vsync start, bit 30 is vsync enable */ + v_sync = (mode->crtc_vsync_end << 16) | mode->crtc_vsync_start | EN_VSYNC_BIT; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + v_sync |= INV_VSYNC_BIT; + + if (index == 0) { + writel(0, ldev->reg_base + LSDC_CRTC0_FB_ORIGIN_REG); + writel(h_val, ldev->reg_base + LSDC_CRTC0_HDISPLAY_REG); + writel(v_val, ldev->reg_base + LSDC_CRTC0_VDISPLAY_REG); + writel(h_sync, ldev->reg_base + LSDC_CRTC0_HSYNC_REG); + writel(v_sync, ldev->reg_base + LSDC_CRTC0_VSYNC_REG); + } else if (index == 1) { + writel(0, ldev->reg_base + LSDC_CRTC1_FB_ORIGIN_REG); + writel(h_val, ldev->reg_base + LSDC_CRTC1_HDISPLAY_REG); + writel(v_val, ldev->reg_base + LSDC_CRTC1_VDISPLAY_REG); + writel(h_sync, ldev->reg_base + LSDC_CRTC1_HSYNC_REG); + writel(v_sync, ldev->reg_base + LSDC_CRTC1_VSYNC_REG); + } + + drm_dbg(ddev, "%s modeset: %ux%u\n", crtc->name, mode->hdisplay, mode->vdisplay); + + lsdc_update_pixclk(crtc); +} + +static void lsdc_enable_display(struct lsdc_device *ldev, unsigned int index) +{ + u32 val; + + if (index == 0) { + val = readl(ldev->reg_base + LSDC_CRTC0_CFG_REG); + val |= CFG_OUTPUT_EN_BIT; + writel(val, ldev->reg_base + LSDC_CRTC0_CFG_REG); + } else if (index == 1) { + val = readl(ldev->reg_base + LSDC_CRTC1_CFG_REG); + val |= CFG_OUTPUT_EN_BIT; + writel(val, ldev->reg_base + LSDC_CRTC1_CFG_REG); + } +} + +static void lsdc_disable_display(struct lsdc_device *ldev, unsigned int index) +{ + u32 val; + + if (index == 0) { + val = readl(ldev->reg_base + LSDC_CRTC0_CFG_REG); + val &= ~CFG_OUTPUT_EN_BIT; + writel(val, ldev->reg_base + LSDC_CRTC0_CFG_REG); + } else if (index == 1) { + val = readl(ldev->reg_base + LSDC_CRTC1_CFG_REG); + val &= ~CFG_OUTPUT_EN_BIT; + writel(val, ldev->reg_base + LSDC_CRTC1_CFG_REG); + } +} + +/* + * @lsdc_crtc_helper_atomic_enable: + * + * This callback should be used to enable the CRTC. With the atomic + * drivers it is called before all encoders connected to this CRTC are + * enabled through the encoder's own &drm_encoder_helper_funcs.enable + * hook. If that sequence is too simple drivers can just add their own + * hooks and call it from this CRTC callback here by looping over all + * encoders connected to it using for_each_encoder_on_crtc(). + * + * This hook is used only by atomic helpers, for symmetry with + * @atomic_disable. Atomic drivers don't need to implement it if there's + * no need to enable anything at the CRTC level. To ensure that runtime + * PM handling (using either DPMS or the new "ACTIVE" property) works + * @atomic_enable must be the inverse of @atomic_disable for atomic + * drivers. + * + * Drivers can use the @old_crtc_state input parameter if the operations + * needed to enable the CRTC don't depend solely on the new state but + * also on the transition between the old state and the new state. + * + * This function is optional. + */ +static void lsdc_crtc_helper_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct drm_device *ddev = crtc->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + + drm_crtc_vblank_on(crtc); + + lsdc_enable_display(ldev, drm_crtc_index(crtc)); + + drm_dbg(ddev, "%s: enabled\n", crtc->name); +} + +static void lsdc_crtc_helper_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct drm_device *ddev = crtc->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + + drm_crtc_vblank_off(crtc); + + lsdc_disable_display(ldev, drm_crtc_index(crtc)); + + drm_dbg(ddev, "%s: disabled\n", crtc->name); +} + +static void lsdc_crtc_update_clut(struct drm_crtc *crtc) +{ + struct lsdc_device *ldev = to_lsdc(crtc->dev); + unsigned int index = drm_crtc_index(crtc); + struct drm_color_lut *lut; + unsigned int i; + + if (!ldev->enable_gamma) + return; + + if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut) + return; + + lut = (struct drm_color_lut *)crtc->state->gamma_lut->data; + + writel(0, ldev->reg_base + LSDC_CRTC0_GAMMA_INDEX_REG); + + for (i = 0; i < 256; i++) { + u32 val = ((lut->red << 8) & 0xff0000) | + (lut->green & 0xff00) | + (lut->blue >> 8); + + if (index == 0) + writel(val, ldev->reg_base + LSDC_CRTC0_GAMMA_DATA_REG); + else if (index == 1) + writel(val, ldev->reg_base + LSDC_CRTC1_GAMMA_DATA_REG); + + lut++; + } +} + +static void lsdc_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct drm_pending_vblank_event *event = crtc->state->event; + + lsdc_crtc_update_clut(crtc); + + if (event) { + crtc->state->event = NULL; + + spin_lock_irq(&crtc->dev->event_lock); + if (drm_crtc_vblank_get(crtc) == 0) + drm_crtc_arm_vblank_event(crtc, event); + else + drm_crtc_send_vblank_event(crtc, event); + spin_unlock_irq(&crtc->dev->event_lock); + } +} + +static const struct drm_crtc_helper_funcs lsdc_crtc_helper_funcs = { + .mode_valid = lsdc_crtc_helper_mode_valid, + .mode_set_nofb = lsdc_crtc_helper_mode_set_nofb, + .atomic_enable = lsdc_crtc_helper_atomic_enable, + .atomic_disable = lsdc_crtc_helper_atomic_disable, + .atomic_check = lsdc_crtc_helper_atomic_check, + .atomic_flush = lsdc_crtc_atomic_flush, +}; + +int lsdc_crtc_init(struct drm_device *ddev, + struct drm_crtc *crtc, + unsigned int index, + struct drm_plane *primary, + struct drm_plane *cursor) +{ + int ret; + + ret = drm_crtc_init_with_planes(ddev, crtc, primary, cursor, + &lsdc_crtc_funcs, "crtc-%d", index); + + if (ret) { + drm_err(ddev, "crtc init with planes failed: %d\n", ret); + return ret; + } + + drm_crtc_helper_add(crtc, &lsdc_crtc_helper_funcs); + + ret = drm_mode_crtc_set_gamma_size(crtc, 256); + if (ret) + drm_warn(ddev, "set the gamma table size failed\n"); + + drm_crtc_enable_color_mgmt(crtc, 0, false, 256); + + return 0; +} diff --git a/drivers/gpu/drm/loongson/lsdc_debugfs.c b/drivers/gpu/drm/loongson/lsdc_debugfs.c new file mode 100644 index 000000000000..1aad413ee97c --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_debugfs.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Loongson Corporation + */ + +/* + * Authors: + * Sui Jingfeng + */ + +#include +#include +#include +#include +#include + +#include "lsdc_drv.h" +#include "lsdc_pll.h" +#include "lsdc_regs.h" +#include "lsdc_debugfs.h" + +#ifdef CONFIG_DEBUG_FS + +static int lsdc_show_clock(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *ddev = node->minor->dev; + struct drm_crtc *crtc; + + drm_for_each_crtc(crtc, ddev) { + struct lsdc_display_pipe *pipe; + struct lsdc_pll *pixpll; + const struct lsdc_pixpll_funcs *funcs; + struct lsdc_pll_core_values params; + unsigned int out_khz; + struct drm_display_mode *adj; + + pipe = container_of(crtc, struct lsdc_display_pipe, crtc); + if (!pipe->available) + continue; + + adj = &crtc->state->adjusted_mode; + + pixpll = &pipe->pixpll; + funcs = pixpll->funcs; + out_khz = funcs->get_clock_rate(pixpll, ¶ms); + + seq_printf(m, "Display pipe %u: %dx%d\n", + pipe->index, adj->hdisplay, adj->vdisplay); + + seq_printf(m, "Frequency actually output: %u kHz\n", out_khz); + seq_printf(m, "Pixel clock required: %d kHz\n", adj->clock); + seq_printf(m, "diff: %d kHz\n", adj->clock); + + seq_printf(m, "div_ref=%u, loopc=%u, div_out=%u\n", + params.div_ref, params.loopc, params.div_out); + + seq_printf(m, "hsync_start=%d, hsync_end=%d, htotal=%d\n", + adj->hsync_start, adj->hsync_end, adj->htotal); + seq_printf(m, "vsync_start=%d, vsync_end=%d, vtotal=%d\n\n", + adj->vsync_start, adj->vsync_end, adj->vtotal); + } + + return 0; +} + +static int lsdc_show_mm(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *ddev = node->minor->dev; + struct drm_printer p = drm_seq_file_printer(m); + + drm_mm_print(&ddev->vma_offset_manager->vm_addr_space_mm, &p); + + return 0; +} + +#define REGDEF(reg) { __stringify_1(LSDC_##reg##_REG), LSDC_##reg##_REG } +static const struct { + const char *name; + u32 reg_offset; +} lsdc_regs_array[] = { + REGDEF(INT), + REGDEF(CRTC0_CFG), + REGDEF(CRTC0_FB_ADDR0), + REGDEF(CRTC0_FB_ADDR1), + REGDEF(CRTC0_FB_HI_ADDR), + REGDEF(CRTC0_STRIDE), + REGDEF(CRTC0_FB_ORIGIN), + REGDEF(CRTC0_HDISPLAY), + REGDEF(CRTC0_HSYNC), + REGDEF(CRTC0_VDISPLAY), + REGDEF(CRTC0_VSYNC), + REGDEF(CRTC0_GAMMA_INDEX), + REGDEF(CRTC0_GAMMA_DATA), + REGDEF(CRTC1_CFG), + REGDEF(CRTC1_FB_ADDR0), + REGDEF(CRTC1_FB_ADDR1), + REGDEF(CRTC1_FB_HI_ADDR), + REGDEF(CRTC1_STRIDE), + REGDEF(CRTC1_FB_ORIGIN), + REGDEF(CRTC1_HDISPLAY), + REGDEF(CRTC1_HSYNC), + REGDEF(CRTC1_VDISPLAY), + REGDEF(CRTC1_VSYNC), + REGDEF(CRTC1_GAMMA_INDEX), + REGDEF(CRTC1_GAMMA_DATA), + REGDEF(CURSOR0_CFG), + REGDEF(CURSOR0_ADDR), + REGDEF(CURSOR0_POSITION), + REGDEF(CURSOR0_BG_COLOR), + REGDEF(CURSOR0_FG_COLOR), +}; + +static int lsdc_show_regs(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *ddev = node->minor->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + int i; + + for (i = 0; i < ARRAY_SIZE(lsdc_regs_array); i++) { + u32 offset = lsdc_regs_array[i].reg_offset; + const char *name = lsdc_regs_array[i].name; + + seq_printf(m, "%s (0x%04x): 0x%08x\n", + name, offset, + readl(ldev->reg_base + offset)); + } + + return 0; +} + +static const struct drm_info_list lsdc_debugfs_list[] = { + { "clocks", lsdc_show_clock, 0 }, + { "mm", lsdc_show_mm, 0, NULL }, + { "regs", lsdc_show_regs, 0 }, +}; + +void lsdc_debugfs_init(struct drm_minor *minor) +{ + drm_debugfs_create_files(lsdc_debugfs_list, + ARRAY_SIZE(lsdc_debugfs_list), + minor->debugfs_root, + minor); +} + +/* + * vram debugfs related. + */ +static int lsdc_vram_mm_show(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_vram_mm *vmm = node->minor->dev->vram_mm; + struct ttm_resource_manager *man = ttm_manager_type(&vmm->bdev, TTM_PL_VRAM); + struct drm_printer p = drm_seq_file_printer(m); + + ttm_resource_manager_debug(man, &p); + return 0; +} + +static const struct drm_info_list lsdc_vram_mm_debugfs_list[] = { + { "clocks", lsdc_show_clock, 0 }, + { "vram-mm", lsdc_vram_mm_show, 0, NULL }, + { "regs", lsdc_show_regs, 0 }, +}; + +void lsdc_vram_mm_debugfs_init(struct drm_minor *minor) +{ + drm_debugfs_create_files(lsdc_vram_mm_debugfs_list, + ARRAY_SIZE(lsdc_vram_mm_debugfs_list), + minor->debugfs_root, + minor); +} + +#endif diff --git a/drivers/gpu/drm/loongson/lsdc_debugfs.h b/drivers/gpu/drm/loongson/lsdc_debugfs.h new file mode 100644 index 000000000000..d5e630534f83 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_debugfs.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022 Loongson Corporation + */ + +/* + * Authors: + * Sui Jingfeng + */ + +#ifndef __LSDC_DEBUGFS_H__ +#define __LSDC_DEBUGFS_H__ + +void lsdc_debugfs_init(struct drm_minor *minor); +void lsdc_vram_mm_debugfs_init(struct drm_minor *minor); + +#endif diff --git a/drivers/gpu/drm/loongson/lsdc_drv.c b/drivers/gpu/drm/loongson/lsdc_drv.c new file mode 100644 index 000000000000..306b0de0d2e8 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_drv.c @@ -0,0 +1,427 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2022 Loongson Corporation + */ + +/* + * Authors: + * Sui Jingfeng + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lsdc_drv.h" +#include "lsdc_irq.h" +#include "lsdc_output.h" +#include "lsdc_debugfs.h" + +static const struct lsdc_chip_desc dc_in_ls2k1000 = { + .chip = LSDC_CHIP_2K1000, + .num_of_crtc = LSDC_NUM_CRTC, + /* ls2k1000 user manual say the max pixel clock can be about 200MHz */ + .max_pixel_clk = 200000, + .max_width = 2560, + .max_height = 2048, + .num_of_hw_cursor = 1, + .hw_cursor_w = 32, + .hw_cursor_h = 32, + .stride_alignment = 256, + .has_builtin_i2c = false, + .has_vram = false, + .broken_gamma = true, +}; + +static const struct lsdc_chip_desc dc_in_ls2k0500 = { + .chip = LSDC_CHIP_2K0500, + .num_of_crtc = LSDC_NUM_CRTC, + .max_pixel_clk = 200000, + .max_width = 2048, + .max_height = 2048, + .num_of_hw_cursor = 1, + .hw_cursor_w = 32, + .hw_cursor_h = 32, + .stride_alignment = 256, + .has_builtin_i2c = false, + .has_vram = false, + .broken_gamma = true, +}; + +static const struct lsdc_chip_desc dc_in_ls7a1000 = { + .chip = LSDC_CHIP_7A1000, + .num_of_crtc = LSDC_NUM_CRTC, + .max_pixel_clk = 200000, + .max_width = 2048, + .max_height = 2048, + .num_of_hw_cursor = 1, + .hw_cursor_w = 32, + .hw_cursor_h = 32, + .stride_alignment = 256, + .has_builtin_i2c = true, + .has_vram = true, + .broken_gamma = true, +}; + +static const struct lsdc_chip_desc dc_in_ls7a2000 = { + .chip = LSDC_CHIP_7A2000, + .num_of_crtc = LSDC_NUM_CRTC, + .max_pixel_clk = 200000, + .max_width = 2048, + .max_height = 2048, + .num_of_hw_cursor = 2, + .hw_cursor_w = 64, + .hw_cursor_h = 64, + .stride_alignment = 256, + .has_builtin_i2c = true, + .has_vram = true, + .broken_gamma = true, +}; + +static enum drm_mode_status +lsdc_device_mode_valid(struct drm_device *ddev, const struct drm_display_mode *mode) +{ + struct lsdc_device *ldev = to_lsdc(ddev); + + if (ldev->use_vram_helper) + return drm_vram_helper_mode_valid(ddev, mode); + + return MODE_OK; +} + +static const struct drm_mode_config_funcs lsdc_mode_config_funcs = { + .fb_create = drm_gem_fb_create, + .output_poll_changed = drm_fb_helper_output_poll_changed, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, + .mode_valid = lsdc_device_mode_valid, +}; + +static int lsdc_gem_cma_dumb_create(struct drm_file *file, + struct drm_device *ddev, + struct drm_mode_create_dumb *args) +{ + struct lsdc_device *ldev = to_lsdc(ddev); + const struct lsdc_chip_desc *desc = ldev->desc; + unsigned int bytes_per_pixel = (args->bpp + 7) / 8; + unsigned int pitch = bytes_per_pixel * args->width; + + /* + * The dc in ls7a1000/ls2k1000/ls2k0500 require the stride be a + * multiple of 256 bytes which is for sake of optimize dma data + * transfer. + */ + args->pitch = roundup(pitch, desc->stride_alignment); + + return drm_gem_cma_dumb_create_internal(file, ddev, args); +} + +DEFINE_DRM_GEM_CMA_FOPS(lsdc_drv_fops); + +static struct drm_driver lsdc_drm_driver_cma_stub = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, + .lastclose = drm_fb_helper_lastclose, + .fops = &lsdc_drv_fops, + + .name = "lsdc", + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, + + DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(lsdc_gem_cma_dumb_create), + +#ifdef CONFIG_DEBUG_FS + .debugfs_init = lsdc_debugfs_init, +#endif +}; + +DEFINE_DRM_GEM_FOPS(lsdc_gem_fops); + +static struct drm_driver lsdc_vram_driver_stub = { + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, + .fops = &lsdc_gem_fops, + + .name = "loongson-drm", + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, +#ifdef CONFIG_DEBUG_FS + .debugfs_init = lsdc_vram_mm_debugfs_init, +#endif + .dumb_create = drm_gem_vram_driver_dumb_create, + .dumb_map_offset = drm_gem_vram_driver_dumb_mmap_offset, + .gem_prime_mmap = drm_gem_prime_mmap, +}; + +static int lsdc_modeset_init(struct lsdc_device *ldev, uint32_t num_crtc) +{ + struct drm_device *ddev = ldev->ddev; + unsigned int i; + int ret; + + if (ldev->has_ports_node) { + drm_info(ddev, "Has OF graph support\n"); + ret = lsdc_attach_output(ldev, num_crtc); + if (ret) + return ret; + } else { + drm_info(ddev, "No OF graph support\n"); + for (i = 0; i < num_crtc; i++) { + ret = lsdc_create_output(ldev, i, num_crtc); + if (ret) + return ret; + } + } + + for (i = 0; i < num_crtc; i++) { + struct lsdc_display_pipe * const dispipe = &ldev->dispipe[i]; + struct drm_plane * const primary = &dispipe->primary; + struct drm_plane * const cursor = &dispipe->cursor; + struct drm_crtc * const crtc = &dispipe->crtc; + struct lsdc_pll * const pixpll = &dispipe->pixpll; + + dispipe->index = i; + + ret = lsdc_pixpll_init(pixpll, ddev, i); + if (ret) + return ret; + + ret = lsdc_plane_init(ldev, primary, DRM_PLANE_TYPE_PRIMARY, i); + if (ret) + return ret; + + ret = lsdc_plane_init(ldev, cursor, DRM_PLANE_TYPE_CURSOR, i); + if (ret) + return ret; + + /* + * Initial all of the CRTC available, in this way the crtc + * index is equal to the hardware crtc index. That is: + * display pipe 0 => crtc0 + dvo0 + encoder0 + * display pipe 1 => crtc1 + dvo1 + encoder1 + */ + ret = lsdc_crtc_init(ddev, crtc, i, primary, cursor); + if (ret) + return ret; + + drm_info(ddev, "display pipe %u initialized\n", i); + } + + return 0; +} + +static int lsdc_mode_config_init(struct lsdc_device *ldev) +{ + const struct lsdc_chip_desc * const descp = ldev->desc; + struct drm_device *ddev = ldev->ddev; + int ret; + + ret = drmm_mode_config_init(ddev); + if (ret) + return ret; + + ddev->mode_config.funcs = &lsdc_mode_config_funcs; + ddev->mode_config.min_width = 1; + ddev->mode_config.min_height = 1; + ddev->mode_config.max_width = 4096; + ddev->mode_config.max_height = 4096; + ddev->mode_config.preferred_depth = 24; + ddev->mode_config.prefer_shadow = ldev->use_vram_helper; + + ddev->mode_config.cursor_width = descp->hw_cursor_h; + ddev->mode_config.cursor_height = descp->hw_cursor_h; + + if (ldev->vram_base) + ddev->mode_config.fb_base = ldev->vram_base; + + return lsdc_modeset_init(ldev, descp->num_of_crtc); +} + +static void lsdc_mode_config_fini(struct drm_device *ddev) +{ + drm_atomic_helper_shutdown(ddev); + + drm_mode_config_cleanup(ddev); +} + +/* + * lsdc_detect_chip - a function to tell different chips apart. + */ +const struct lsdc_chip_desc * +lsdc_detect_chip(struct pci_dev *pdev, const struct pci_device_id * const ent) +{ + static const struct lsdc_match { + char name[128]; + const void *data; + } compat[] = { + { .name = "loongson,ls7a1000-dc", .data = &dc_in_ls7a1000 }, + { .name = "loongson,ls2k1000-dc", .data = &dc_in_ls2k1000 }, + { .name = "loongson,ls2k0500-dc", .data = &dc_in_ls2k0500 }, + { .name = "loongson,ls7a2000-dc", .data = &dc_in_ls7a2000 }, + { .name = "loongson,loongson64c-4core-ls7a", .data = &dc_in_ls7a1000 }, + { .name = "loongson,loongson64g-4core-ls7a", .data = &dc_in_ls7a1000 }, + { .name = "loongson,loongson64-2core-2k1000", .data = &dc_in_ls2k1000 }, + { .name = "loongson,loongson2k1000", .data = &dc_in_ls2k1000 }, + { /* sentinel */ }, + }; + + struct device_node *np; + unsigned int i; + + if (ent->driver_data == LSDC_CHIP_7A2000) + return &dc_in_ls7a2000; + + if (ent->driver_data == LSDC_CHIP_7A1000) + return &dc_in_ls7a1000; + + /* Deduce DC variant information from the DC device node */ + for (i = 0; i < ARRAY_SIZE(compat); ++i) { + np = of_find_compatible_node(NULL, NULL, compat[i].name); + if (!np) + continue; + + of_node_put(np); + + return compat[i].data; + } + + dev_info(&pdev->dev, "No Compatible Device Node Found\n"); + + if (pci_get_device(PCI_VENDOR_ID_LOONGSON, 0x7A15, NULL)) + return &dc_in_ls7a1000; + else if (pci_get_device(PCI_VENDOR_ID_LOONGSON, 0x7A05, NULL)) + return &dc_in_ls2k1000; + + return NULL; +} + +static int lsdc_remove_conflicting_framebuffers(const struct drm_driver *drv) +{ + struct apertures_struct *ap; + + ap = alloc_apertures(1); + if (!ap) + return -ENOMEM; + + /* lsdc is a pci device, but it don't have a dedicate vram bar because + * of historic reason. The display controller is ported from Loongson + * 2H series SoC which date back to 2012. + * And simplefb node may have been located anywhere in memory. + */ + + ap->ranges[0].base = 0; + ap->ranges[0].size = ~0; + + return drm_fb_helper_remove_conflicting_framebuffers(ap, "loongsondrmfb", false); +} + +static int lsdc_platform_probe(struct platform_device *pdev) +{ + struct lsdc_device *ldev = dev_get_drvdata(pdev->dev.parent); + struct drm_driver *driver; + struct drm_device *ddev; + int ret; + + if (ldev->use_vram_helper) + driver = &lsdc_vram_driver_stub; + else + driver = &lsdc_drm_driver_cma_stub; + + lsdc_remove_conflicting_framebuffers(driver); + + ddev = drm_dev_alloc(driver, &pdev->dev); + if (IS_ERR(ddev)) + return PTR_ERR(ddev); + + platform_set_drvdata(pdev, ddev); + ldev->ddev = ddev; + ddev->dev_private = ldev; + + if (ldev->use_vram_helper) { + ret = drmm_vram_helper_init(ddev, ldev->vram_base, ldev->vram_size); + if (ret) { + drm_err(ddev, "vram helper init failed: %d\n", ret); + goto err_kms; + } + }; + + ret = lsdc_mode_config_init(ldev); + if (ret) { + drm_dbg(ddev, "%s: %d\n", __func__, ret); + goto err_kms; + } + + ret = devm_request_threaded_irq(&pdev->dev, ldev->irq, + lsdc_irq_handler_cb, + lsdc_irq_thread_cb, + IRQF_ONESHOT, NULL, + ddev); + if (ret) { + drm_err(ddev, "Failed to register lsdc interrupt\n"); + goto err_kms; + } + + ret = drm_vblank_init(ddev, ldev->desc->num_of_crtc); + if (ret) + goto err_kms; + + drm_mode_config_reset(ddev); + + drm_kms_helper_poll_init(ddev); + + ret = drm_dev_register(ddev, 0); + if (ret) + goto err_poll_fini; + + drm_fbdev_generic_setup(ddev, 32); + + return 0; + +err_poll_fini: + drm_kms_helper_poll_fini(ddev); +err_kms: + drm_dev_put(ddev); + + return ret; +} + +static int lsdc_platform_remove(struct platform_device *pdev) +{ + struct drm_device *ddev = platform_get_drvdata(pdev); + struct lsdc_device *ldev = to_lsdc(ddev); + + drm_dev_unregister(ddev); + + drm_kms_helper_poll_fini(ddev); + + devm_free_irq(ddev->dev, ldev->irq, ddev); + + lsdc_mode_config_fini(ddev); + + platform_set_drvdata(pdev, NULL); + + drm_dev_put(ddev); + + return 0; +} + +struct platform_driver lsdc_platform_driver = { + .probe = lsdc_platform_probe, + .remove = lsdc_platform_remove, + .driver = { + .name = "lsdc", + }, +}; diff --git a/drivers/gpu/drm/loongson/lsdc_drv.h b/drivers/gpu/drm/loongson/lsdc_drv.h new file mode 100644 index 000000000000..e07b049dcd21 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_drv.h @@ -0,0 +1,184 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022 Loongson Corporation + */ + +/* + * Authors: + * Sui Jingfeng + */ + +#ifndef __LSDC_DRV_H__ +#define __LSDC_DRV_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lsdc_pll.h" + +#define DRIVER_AUTHOR "Sui Jingfeng " +#define DRIVER_NAME "lsdc" +#define DRIVER_DESC "drm driver for loongson's display controller" +#define DRIVER_DATE "20200701" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 0 + +#define LSDC_NUM_CRTC 2 + +enum loongson_dc_family { + LSDC_CHIP_UNKNOWN = 0, + LSDC_CHIP_2K1000 = 1, /* 2-Core Mips64r2 compatible SoC */ + LSDC_CHIP_7A1000 = 2, /* North bridge of LS3A3000/LS3A4000/LS3A5000 */ + LSDC_CHIP_2K0500 = 3, /* Single core, reduced version of LS2K1000 */ + LSDC_CHIP_7A2000 = 4, /* Enhancement version of LS7a1000 */ + LSDC_CHIP_LAST, +}; + +struct lsdc_chip_desc { + enum loongson_dc_family chip; + u32 num_of_crtc; + u32 max_pixel_clk; + u32 max_width; + u32 max_height; + u32 num_of_hw_cursor; + u32 hw_cursor_w; + u32 hw_cursor_h; + /* DMA alignment constraint (must be multiple of 256 bytes) */ + u32 stride_alignment; + bool has_builtin_i2c; + bool has_vram; + bool broken_gamma; +}; + +/* There is only a 1:1 mapping of encoders and connectors for lsdc */ +struct lsdc_output { + struct drm_encoder encoder; + struct drm_connector connector; + struct lsdc_i2c *li2c; +}; + +static inline struct lsdc_output * +drm_connector_to_lsdc_output(struct drm_connector *connp) +{ + return container_of(connp, struct lsdc_output, connector); +} + +/* + * struct lsdc_display_pipe - Abstraction of hardware display pipeline. + * @crtc: CRTC control structure + * @plane: Plane control structure + * @encoder: Encoder control structure + * @pixpll: Pll control structure + * @connector: point to connector control structure this display pipe bind + * @index: the index corresponding to the hardware display pipe + * @available: is this display pipe is available on the motherboard, The + * downstream mother board manufacturer may use only one of them. + * For example, LEMOTE LX-6901 board just has only one VGA output. + * + * Display pipeline with planes, crtc, pll and output collapsed into one entity. + */ +struct lsdc_display_pipe { + struct drm_crtc crtc; + struct drm_plane primary; + struct drm_plane cursor; + struct lsdc_pll pixpll; + struct lsdc_output *output; + int index; + bool available; +}; + +static inline struct lsdc_display_pipe * +drm_crtc_to_dispipe(struct drm_crtc *crtc) +{ + return container_of(crtc, struct lsdc_display_pipe, crtc); +} + +static inline struct lsdc_display_pipe * +lsdc_cursor_to_dispipe(struct drm_plane *plane) +{ + return container_of(plane, struct lsdc_display_pipe, cursor); +} + +struct lsdc_crtc_state { + struct drm_crtc_state base; + struct lsdc_pll_core_values pparams; +}; + +struct lsdc_device { + struct device *dev; + struct drm_device *ddev; + /* @dc: pointer to the platform device created at runtime */ + struct platform_device *dc; + /* @desc: device dependent data and feature descriptions */ + const struct lsdc_chip_desc *desc; + + /* LS7A1000/LS7A2000 has a dediacted video RAM */ + void __iomem *reg_base; + void __iomem *vram; + resource_size_t vram_base; + resource_size_t vram_size; + + struct lsdc_display_pipe dispipe[LSDC_NUM_CRTC]; + + /* + * @num_output: count the number of active display pipe. + */ + unsigned int num_output; + + int irq; + u32 irq_status; + + /* + * @use_vram_helper: using vram helper base solution instead of + * CMA helper based solution. The DC scanout from the VRAM is + * proved to be more reliable, but graphic application is may + * become slow when using this driver mode. + */ + bool use_vram_helper; + /* + * @enable_gamma: control whether hardware gamma support should be + * enabled or not. It is broken though, but you can know that only + * when you can enable it. + */ + bool enable_gamma; + /* @relax_alignment: for 800x480, 1366x768 resulotion support */ + bool relax_alignment; + /* @has_dt: true if there are DT support*/ + bool has_dt; + /* @has_ports_node: true if there are OF graph in the DT */ + bool has_ports_node; +}; + +static inline struct lsdc_device *to_lsdc(struct drm_device *ddev) +{ + return ddev->dev_private; +} + +static inline struct lsdc_crtc_state * +to_lsdc_crtc_state(struct drm_crtc_state *base) +{ + return container_of(base, struct lsdc_crtc_state, base); +} + +int lsdc_crtc_init(struct drm_device *ddev, + struct drm_crtc *crtc, + unsigned int index, + struct drm_plane *primary, + struct drm_plane *cursor); + +int lsdc_plane_init(struct lsdc_device *ldev, struct drm_plane *plane, + enum drm_plane_type type, unsigned int index); + +const struct lsdc_chip_desc * +lsdc_detect_chip(struct pci_dev *pdev, const struct pci_device_id * const ent); + +extern struct platform_driver lsdc_platform_driver; + +#endif diff --git a/drivers/gpu/drm/loongson/lsdc_i2c.c b/drivers/gpu/drm/loongson/lsdc_i2c.c new file mode 100644 index 000000000000..925bb8b866ad --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_i2c.c @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Loongson Corporation + */ + +/* + * Authors: + * Sui Jingfeng + */ + +#include + +#include +#include +#include + +#include "lsdc_regs.h" +#include "lsdc_i2c.h" + +/* + * ls7a_gpio_i2c_set - set the state of a gpio pin, either high or low. + * @mask: gpio pin mask indicate which pin to set + */ +static void ls7a_gpio_i2c_set(struct lsdc_i2c * const li2c, int mask, int state) +{ + unsigned long flags; + u8 val; + + spin_lock_irqsave(&li2c->reglock, flags); + + if (state) { + /* + * The high state is achieved by setting the direction as + * input, because the GPIO is open drained with external + * pull up resistance. + */ + val = readb(li2c->dir_reg); + val |= mask; + writeb(val, li2c->dir_reg); + } else { + /* First, set this pin as output */ + val = readb(li2c->dir_reg); + val &= ~mask; + writeb(val, li2c->dir_reg); + + /* Then, set the state to it */ + val = readb(li2c->dat_reg); + val &= ~mask; + writeb(val, li2c->dat_reg); + } + + spin_unlock_irqrestore(&li2c->reglock, flags); +} + +/* + * ls7a_gpio_i2c_get - read value back from gpio pin + * @mask: gpio pin mask indicate which pin to read from + */ +static int ls7a_gpio_i2c_get(struct lsdc_i2c * const li2c, int mask) +{ + unsigned long flags; + u8 val; + + spin_lock_irqsave(&li2c->reglock, flags); + + /* First, set this pin as input */ + val = readb(li2c->dir_reg); + val |= mask; + writeb(val, li2c->dir_reg); + + /* Then, get level state from this pin */ + val = readb(li2c->dat_reg); + + spin_unlock_irqrestore(&li2c->reglock, flags); + + return (val & mask) ? 1 : 0; +} + +/* set the state on the i2c->sda pin */ +static void ls7a_i2c_set_sda(void *i2c, int state) +{ + struct lsdc_i2c * const li2c = (struct lsdc_i2c *)i2c; + + return ls7a_gpio_i2c_set(li2c, li2c->sda, state); +} + +/* set the state on the i2c->scl pin */ +static void ls7a_i2c_set_scl(void *i2c, int state) +{ + struct lsdc_i2c * const li2c = (struct lsdc_i2c *)i2c; + + return ls7a_gpio_i2c_set(li2c, li2c->scl, state); +} + +/* read the value from the i2c->sda pin */ +static int ls7a_i2c_get_sda(void *i2c) +{ + struct lsdc_i2c * const li2c = (struct lsdc_i2c *)i2c; + + return ls7a_gpio_i2c_get(li2c, li2c->sda); +} + +/* read the value from the i2c->scl pin */ +static int ls7a_i2c_get_scl(void *i2c) +{ + struct lsdc_i2c * const li2c = (struct lsdc_i2c *)i2c; + + return ls7a_gpio_i2c_get(li2c, li2c->scl); +} + +/* + * Mainly for dc in ls7a1000 which have dedicated gpio hardware + */ +static void lsdc_of_release_i2c_adapter(void *res) +{ + struct lsdc_i2c *li2c = res; + struct i2c_adapter *adapter; + struct device_node *i2c_np; + + adapter = &li2c->adapter; + i2c_np = adapter->dev.of_node; + if (i2c_np) + of_node_put(i2c_np); + + i2c_del_adapter(adapter); + + kfree(li2c); +} + +struct lsdc_i2c *lsdc_of_create_i2c_adapter(struct device *parent, + void *reg_base, + struct device_node *i2c_np) +{ + unsigned int udelay = 5; + unsigned int timeout = 2200; + int nr = -1; + struct i2c_adapter *adapter; + struct lsdc_i2c *li2c; + u32 sda, scl; + int ret; + + li2c = kzalloc(sizeof(*li2c), GFP_KERNEL); + if (!li2c) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&li2c->reglock); + + ret = of_property_read_u32(i2c_np, "loongson,sda", &sda); + if (ret) { + dev_err(parent, "No sda pin number provided\n"); + return ERR_PTR(ret); + } + + ret = of_property_read_u32(i2c_np, "loongson,scl", &scl); + if (ret) { + dev_err(parent, "No scl pin number provided\n"); + return ERR_PTR(ret); + } + + ret = of_property_read_u32(i2c_np, "loongson,nr", &nr); + if (ret) { + int id; + + if (ret == -EINVAL) + dev_dbg(parent, "no nr provided\n"); + + id = of_alias_get_id(i2c_np, "i2c"); + if (id >= 0) + nr = id; + } + + li2c->sda = 1 << sda; + li2c->scl = 1 << scl; + + /* Optional properties which made the driver more flexible */ + of_property_read_u32(i2c_np, "loongson,udelay", &udelay); + of_property_read_u32(i2c_np, "loongson,timeout", &timeout); + + li2c->dir_reg = reg_base + LS7A_DC_GPIO_DIR_REG; + li2c->dat_reg = reg_base + LS7A_DC_GPIO_DAT_REG; + + li2c->bit.setsda = ls7a_i2c_set_sda; + li2c->bit.setscl = ls7a_i2c_set_scl; + li2c->bit.getsda = ls7a_i2c_get_sda; + li2c->bit.getscl = ls7a_i2c_get_scl; + li2c->bit.udelay = udelay; + li2c->bit.timeout = usecs_to_jiffies(timeout); + li2c->bit.data = li2c; + + adapter = &li2c->adapter; + adapter->algo_data = &li2c->bit; + adapter->owner = THIS_MODULE; + adapter->class = I2C_CLASS_DDC; + adapter->dev.parent = parent; + adapter->nr = nr; + adapter->dev.of_node = i2c_np; + + snprintf(adapter->name, sizeof(adapter->name), "gpio-i2c-%d", nr); + + i2c_set_adapdata(adapter, li2c); + + ret = i2c_bit_add_numbered_bus(adapter); + if (ret) { + if (i2c_np) + of_node_put(i2c_np); + + kfree(li2c); + return ERR_PTR(ret); + } + + dev_info(parent, "sda=%u, scl=%u, nr=%d, udelay=%u, timeout=%u\n", + li2c->sda, li2c->scl, nr, udelay, timeout); + + ret = devm_add_action_or_reset(parent, lsdc_of_release_i2c_adapter, li2c); + if (ret) + return NULL; + + return li2c; +} + +static void lsdc_release_i2c_chan(struct drm_device *dev, void *res) +{ + struct lsdc_i2c *li2c = res; + + i2c_del_adapter(&li2c->adapter); + + kfree(li2c); +} + +struct lsdc_i2c *lsdc_create_i2c_chan(struct drm_device *ddev, + void *reg_base, + unsigned int index) +{ + struct i2c_adapter *adapter; + struct lsdc_i2c *li2c; + int ret; + + li2c = kzalloc(sizeof(*li2c), GFP_KERNEL); + if (!li2c) + return ERR_PTR(-ENOMEM); + + if (index == 0) { + li2c->sda = 0x01; + li2c->scl = 0x02; + } else if (index == 1) { + li2c->sda = 0x04; + li2c->scl = 0x08; + } + + spin_lock_init(&li2c->reglock); + + li2c->dir_reg = reg_base + LS7A_DC_GPIO_DIR_REG; + li2c->dat_reg = reg_base + LS7A_DC_GPIO_DAT_REG; + + li2c->bit.setsda = ls7a_i2c_set_sda; + li2c->bit.setscl = ls7a_i2c_set_scl; + li2c->bit.getsda = ls7a_i2c_get_sda; + li2c->bit.getscl = ls7a_i2c_get_scl; + li2c->bit.udelay = 5; + li2c->bit.timeout = usecs_to_jiffies(2200); + li2c->bit.data = li2c; + + adapter = &li2c->adapter; + adapter->algo_data = &li2c->bit; + adapter->owner = THIS_MODULE; + adapter->class = I2C_CLASS_DDC; + adapter->dev.parent = ddev->dev; + adapter->nr = -1; + + snprintf(adapter->name, sizeof(adapter->name), "gpio-i2c-%d", index); + + i2c_set_adapdata(adapter, li2c); + + ret = i2c_bit_add_bus(adapter); + if (ret) { + kfree(li2c); + return ERR_PTR(ret); + } + + ret = drmm_add_action_or_reset(ddev, lsdc_release_i2c_chan, li2c); + if (ret) + return NULL; + + drm_info(ddev, "%s: sda=%u, scl=%u\n", + adapter->name, li2c->sda, li2c->scl); + + return li2c; +} diff --git a/drivers/gpu/drm/loongson/lsdc_i2c.h b/drivers/gpu/drm/loongson/lsdc_i2c.h new file mode 100644 index 000000000000..62cbf2aaab2e --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_i2c.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022 Loongson Corporation + */ + +/* + * Authors: + * Sui Jingfeng + */ + +#ifndef __LSDC_I2C__ +#define __LSDC_I2C__ + +#include +#include +#include + +struct lsdc_i2c { + struct i2c_adapter adapter; + struct i2c_algo_bit_data bit; + /* @reglock: protects concurrent register access */ + spinlock_t reglock; + void __iomem *dir_reg; + void __iomem *dat_reg; + /* pin bit mask */ + u8 sda; + u8 scl; +}; + +struct lsdc_i2c *lsdc_create_i2c_chan(struct drm_device *ddev, + void *reg_base, + unsigned int index); + +struct lsdc_i2c *lsdc_of_create_i2c_adapter(struct device *dev, + void *reg_base, + struct device_node *i2c_np); + +#endif diff --git a/drivers/gpu/drm/loongson/lsdc_irq.c b/drivers/gpu/drm/loongson/lsdc_irq.c new file mode 100644 index 000000000000..35616f976fad --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_irq.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Loongson Corporation + */ + +/* + * Authors: + * Sui Jingfeng + */ + +#include + +#include "lsdc_drv.h" +#include "lsdc_regs.h" +#include "lsdc_irq.h" + +/* Function to be called in a threaded interrupt context. */ +irqreturn_t lsdc_irq_thread_cb(int irq, void *arg) +{ + struct drm_device *ddev = arg; + struct lsdc_device *ldev = to_lsdc(ddev); + struct drm_crtc *crtc; + + /* trigger the vblank event */ + if (ldev->irq_status & INT_CRTC0_VS) { + crtc = drm_crtc_from_index(ddev, 0); + drm_crtc_handle_vblank(crtc); + } + + if (ldev->irq_status & INT_CRTC1_VS) { + crtc = drm_crtc_from_index(ddev, 1); + drm_crtc_handle_vblank(crtc); + } + + writel(INT_CRTC0_VS_EN | INT_CRTC1_VS_EN, ldev->reg_base + LSDC_INT_REG); + + return IRQ_HANDLED; +} + +/* Function to be called when the IRQ occurs */ +irqreturn_t lsdc_irq_handler_cb(int irq, void *arg) +{ + struct drm_device *ddev = arg; + struct lsdc_device *ldev = to_lsdc(ddev); + + /* Read & Clear the interrupt status */ + ldev->irq_status = readl(ldev->reg_base + LSDC_INT_REG); + if ((ldev->irq_status & INT_STATUS_MASK) == 0) { + drm_warn(ddev, "no interrupt occurs\n"); + return IRQ_NONE; + } + + /* clear all interrupt */ + writel(ldev->irq_status, ldev->reg_base + LSDC_INT_REG); + + return IRQ_WAKE_THREAD; +} diff --git a/drivers/gpu/drm/loongson/lsdc_irq.h b/drivers/gpu/drm/loongson/lsdc_irq.h new file mode 100644 index 000000000000..528a74d9bf83 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_irq.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022 Loongson Corporation + */ + +/* + * Authors: + * Sui Jingfeng + */ + +#ifndef __LSDC_IRQ_H__ +#define __LSDC_IRQ_H__ + +irqreturn_t lsdc_irq_thread_cb(int irq, void *arg); +irqreturn_t lsdc_irq_handler_cb(int irq, void *arg); + +#endif diff --git a/drivers/gpu/drm/loongson/lsdc_output.c b/drivers/gpu/drm/loongson/lsdc_output.c new file mode 100644 index 000000000000..e8cefe6edd4d --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_output.c @@ -0,0 +1,412 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Loongson Corporation + */ + +/* + * Authors: + * Sui Jingfeng + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "lsdc_drv.h" +#include "lsdc_i2c.h" +#include "lsdc_output.h" +#include "lsdc_regs.h" + +static int lsdc_get_modes(struct drm_connector *connector) +{ + unsigned int num = 0; + struct lsdc_output *lop = drm_connector_to_lsdc_output(connector); + struct lsdc_i2c *li2c = lop->li2c; + struct i2c_adapter *ddc = &li2c->adapter; + + if (ddc) { + struct edid *edid; + + edid = drm_get_edid(connector, ddc); + if (edid) { + drm_connector_update_edid_property(connector, edid); + num = drm_add_edid_modes(connector, edid); + kfree(edid); + } + + return num; + } + + drm_dbg(connector->dev, "Failed to get mode from ddc\n"); + + num = drm_add_modes_noedid(connector, 1920, 1200); + + drm_set_preferred_mode(connector, 1024, 768); + + return num; +} + +static enum drm_connector_status +lsdc_connector_detect(struct drm_connector *connector, bool force) +{ + struct lsdc_output *lop = drm_connector_to_lsdc_output(connector); + struct lsdc_i2c *li2c = lop->li2c; + struct i2c_adapter *ddc = &li2c->adapter; + + if (ddc && drm_probe_ddc(ddc)) + return connector_status_connected; + + if (connector->connector_type == DRM_MODE_CONNECTOR_VIRTUAL) + return connector_status_connected; + + if (connector->connector_type == DRM_MODE_CONNECTOR_DVIA || + connector->connector_type == DRM_MODE_CONNECTOR_DVID || + connector->connector_type == DRM_MODE_CONNECTOR_DVII) + return connector_status_disconnected; + + if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || + connector->connector_type == DRM_MODE_CONNECTOR_HDMIB) + return connector_status_disconnected; + + return connector_status_unknown; +} + +static void lsdc_connector_destroy(struct drm_connector *connector) +{ + drm_connector_cleanup(connector); +} + +static const struct drm_connector_helper_funcs lsdc_connector_helpers = { + .get_modes = lsdc_get_modes, +}; + +static const struct drm_connector_funcs lsdc_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = lsdc_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = lsdc_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static enum drm_mode_status +ls7a2000_hdmi_encoder_mode_valid(struct drm_encoder *crtc, + const struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static void ls7a2000_hdmi_encoder_disable(struct drm_encoder *encoder) +{ + int index = encoder->index; + struct lsdc_device *ldev = to_lsdc(encoder->dev); + + if (index == 0) { + /* Enable hdmi */ + writel(0, ldev->reg_base + HDMI0_CTRL_REG); + + } else if (index == 1) { + /* Enable hdmi */ + writel(0, ldev->reg_base + HDMI1_CTRL_REG); + } + + drm_dbg(encoder->dev, "HDMI%d disable\n", index); +} + +static void ls7a2000_hdmi_encoder_enable(struct drm_encoder *encoder) +{ + int index = encoder->index; + struct lsdc_device *ldev = to_lsdc(encoder->dev); + + if (index == 0) { + /* Enable hdmi */ + writel(0x280 | HDMI_EN | HDMI_PACKET_EN, ldev->reg_base + HDMI0_CTRL_REG); + + /* hdmi zone idle */ + writel(0x00400040, ldev->reg_base + HDMI0_ZONE_REG); + } else if (index == 1) { + /* Enable hdmi */ + writel(0x280 | HDMI_EN | HDMI_PACKET_EN, ldev->reg_base + HDMI1_CTRL_REG); + + /* hdmi zone idle */ + writel(0x00400040, ldev->reg_base + HDMI1_ZONE_REG); + } + + drm_dbg(encoder->dev, "HDMI%d enable\n", index); +} + +static void +ls7a2000_hdmi_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + int index = encoder->index; + struct drm_device *ddev = encoder->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + int clock = mode->clock; + u32 val; + int counter; + + if (index == 0) { + writel(0x0, ldev->reg_base + HDMI0_PLL_REG); + writel(0x0, ldev->reg_base + HDMI0_PHY_CTRL_REG); + } else { + writel(0x0, ldev->reg_base + HDMI1_PLL_REG); + writel(0x0, ldev->reg_base + HDMI1_PHY_CTRL_REG); + } + + if (clock >= 170000) + val = (0x0 << 13) | (0x28 << 6) | (0x10 << 1) | HDMI_PLL_EN; + else if (clock >= 85000 && clock < 170000) + val = (0x1 << 13) | (0x28 << 6) | (0x8 << 1) | HDMI_PLL_EN; + else if (clock >= 42500 && clock < 85000) + val = (0x2 << 13) | (0x28 << 6) | (0x4 << 1) | HDMI_PLL_EN; + else if (clock >= 21250 && clock < 42500) + val = (0x3 << 13) | (0x28 << 6) | (0x2 << 1) | HDMI_PLL_EN; + + if (index == 0) { + writel(val, ldev->reg_base + HDMI0_PLL_REG); + } else { + writel(val, ldev->reg_base + HDMI1_PLL_REG); + } + + do { + /* wait pll lock */ + if (index == 0) + val = readl(ldev->reg_base + HDMI0_PLL_REG); + else if (index == 1) + val = readl(ldev->reg_base + HDMI1_PLL_REG); + + ++counter; + } while (((val & HDMI_PLL_LOCKED) == 0) && (counter < 1000)); + + drm_dbg(ddev, "HDMI%d modeset, PLL: %u loop waited\n", index, counter); + + if (index == 0) { + writel(0x0f03, ldev->reg_base + HDMI0_PHY_CTRL_REG); + } else if (index == 1) { + writel(0x0f03, ldev->reg_base + HDMI1_PHY_CTRL_REG); + } +} + +static const struct drm_encoder_helper_funcs ls7a2000_hdmi_encoder_helper_funcs = { + .mode_valid = ls7a2000_hdmi_encoder_mode_valid, + .disable = ls7a2000_hdmi_encoder_disable, + .enable = ls7a2000_hdmi_encoder_enable, + .mode_set = ls7a2000_hdmi_encoder_mode_set, +}; + +static void lsdc_encoder_reset(struct drm_encoder *encoder) +{ + struct lsdc_device *ldev = to_lsdc(encoder->dev); + + if (ldev->desc->chip == LSDC_CHIP_7A2000) + ls7a2000_hdmi_encoder_enable(encoder); +} + +static const struct drm_encoder_funcs lsdc_encoder_funcs = { + .reset = lsdc_encoder_reset, + .destroy = drm_encoder_cleanup, +}; + +static int lsdc_attach_bridges(struct lsdc_device *ldev, + struct device_node *ports, + unsigned int i) +{ + struct lsdc_display_pipe * const dispipe = &ldev->dispipe[i]; + struct drm_device *ddev = ldev->ddev; + struct drm_bridge *bridge; + struct drm_panel *panel; + struct drm_connector *connector; + struct drm_encoder *encoder; + struct lsdc_output *output; + int ret; + + ret = drm_of_find_panel_or_bridge(ports, i, 0, &panel, &bridge); + + if (panel) { + bridge = devm_drm_panel_bridge_add_typed(ddev->dev, panel, DRM_MODE_CONNECTOR_DPI); + drm_info(ddev, "output-%u is a DPI panel\n", i); + } + + if (!bridge) + return ret; + + output = devm_kzalloc(ddev->dev, sizeof(*output), GFP_KERNEL); + if (!output) + return -ENOMEM; + + encoder = &output->encoder; + + ret = drm_encoder_init(ddev, encoder, &lsdc_encoder_funcs, + DRM_MODE_ENCODER_DPI, "encoder-%u", i); + + if (ret) { + drm_err(ddev, "Failed to init encoder: %d\n", ret); + return ret; + } + + encoder->possible_crtcs = BIT(i); + + ret = drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (ret) { + drm_err(ddev, + "failed to attach bridge %pOF for output %u (%d)\n", + bridge->of_node, i, ret); + return ret; + } + + connector = drm_bridge_connector_init(ddev, encoder); + if (IS_ERR(connector)) { + drm_err(ddev, "Unable to init connector\n"); + return PTR_ERR(connector); + } + + drm_connector_attach_encoder(connector, encoder); + + drm_info(ddev, "bridge-%u attached to %s\n", i, encoder->name); + + dispipe->output = output; + + return 0; +} + +int lsdc_attach_output(struct lsdc_device *ldev, uint32_t num_crtc) +{ + struct drm_device *ddev = ldev->ddev; + struct device_node *ports; + struct lsdc_display_pipe *disp; + unsigned int i; + int ret; + + ldev->num_output = 0; + + ports = of_get_child_by_name(ldev->dev->of_node, "ports"); + + for (i = 0; i < num_crtc; i++) { + struct drm_bridge *b; + struct drm_panel *p; + + disp = &ldev->dispipe[i]; + disp->available = false; + + ret = drm_of_find_panel_or_bridge(ports, i, 0, &p, &b); + if (ret) { + if (ret == -ENODEV) { + drm_dbg(ddev, "No active panel or bridge for port%u\n", i); + disp->available = false; + continue; + } + + if (ret == -EPROBE_DEFER) + drm_dbg(ddev, "Bridge for port%d is defer probed\n", i); + + goto RET; + } + + disp->available = true; + ldev->num_output++; + } + + if (ldev->num_output == 0) { + drm_err(ddev, "No valid output, abort\n"); + ret = -ENODEV; + goto RET; + } + + for (i = 0; i < num_crtc; i++) { + disp = &ldev->dispipe[i]; + if (disp->available) { + ret = lsdc_attach_bridges(ldev, ports, i); + if (ret) + goto RET; + } else { + drm_info(ddev, "output-%u is not available\n", i); + } + } + + drm_info(ddev, "number of outputs: %u\n", ldev->num_output); +RET: + of_node_put(ports); + return ret; +} + +/* No DT support, provide a minimal support */ +int lsdc_create_output(struct lsdc_device *ldev, + unsigned int index, + unsigned int num_crtc) +{ + const struct lsdc_chip_desc * const descp = ldev->desc; + struct lsdc_display_pipe * const dispipe = &ldev->dispipe[index]; + struct drm_device *ddev = ldev->ddev; + int encoder_type = DRM_MODE_ENCODER_DPI; + int connector_type = DRM_MODE_CONNECTOR_DPI; + struct lsdc_output *output; + struct drm_encoder *encoder; + struct drm_connector *connector; + int ret; + + output = devm_kzalloc(ddev->dev, sizeof(*output), GFP_KERNEL); + if (!output) + return -ENOMEM; + + encoder = &output->encoder; + + if (descp->chip == LSDC_CHIP_7A2000) { + encoder_type = DRM_MODE_ENCODER_TMDS; + connector_type = DRM_MODE_CONNECTOR_HDMIA; + } + + ret = drm_encoder_init(ddev, encoder, &lsdc_encoder_funcs, + encoder_type, "encoder-%u", index); + + if (ret) { + drm_err(ddev, "Failed to init encoder: %d\n", ret); + return ret; + } + + if (descp->chip == LSDC_CHIP_7A2000) + drm_encoder_helper_add(encoder, &ls7a2000_hdmi_encoder_helper_funcs); + + encoder->possible_crtcs = BIT(index); + + if (descp->has_builtin_i2c) { + output->li2c = lsdc_create_i2c_chan(ddev, ldev->reg_base, index); + if (IS_ERR(output->li2c)) { + drm_err(ddev, "Failed to create i2c adapter\n"); + return PTR_ERR(output->li2c); + } + } else { + drm_warn(ddev, "output-%u don't has ddc\n", index); + output->li2c = NULL; + } + + connector = &output->connector; + + ret = drm_connector_init_with_ddc(ddev, + connector, + &lsdc_connector_funcs, + connector_type, + &output->li2c->adapter); + if (ret) { + drm_err(ddev, "Init connector%d failed\n", index); + return ret; + } + + drm_connector_helper_add(connector, &lsdc_connector_helpers); + + connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; + + drm_connector_attach_encoder(connector, encoder); + + dispipe->available = true; + dispipe->output = output; + ldev->num_output++; + + return 0; +} diff --git a/drivers/gpu/drm/loongson/lsdc_output.h b/drivers/gpu/drm/loongson/lsdc_output.h new file mode 100644 index 000000000000..97ad9345ec10 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_output.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022 Loongson Corporation + */ + +/* + * Authors: + * Sui Jingfeng + */ + +#ifndef __LSDC_OUTPUT_H__ +#define __LSDC_OUTPUT_H__ + +#include +#include + +int lsdc_create_output(struct lsdc_device *ldev, unsigned int i, unsigned int num_crtc); + +int lsdc_attach_output(struct lsdc_device *ldev, uint32_t num_crtc); + +#endif diff --git a/drivers/gpu/drm/loongson/lsdc_pci_drv.c b/drivers/gpu/drm/loongson/lsdc_pci_drv.c new file mode 100644 index 000000000000..e202da6621bc --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_pci_drv.c @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * KMS driver for Loongson display controller + * Copyright (C) 2022 Loongson Corporation + */ + +/* + * Authors: + * Sui Jingfeng + */ + +#include +#include +#include +#include + +#include + +#include "lsdc_drv.h" +#include "lsdc_i2c.h" + +static int lsdc_use_vram_helper = -1; +MODULE_PARM_DESC(use_vram_helper, "Using vram helper based driver(0 = disabled)"); +module_param_named(use_vram_helper, lsdc_use_vram_helper, int, 0644); + +static int lsdc_gamma = -1; +MODULE_PARM_DESC(gamma, "enable gamma (-1 = disabled (default), >0 = enabled)"); +module_param_named(gamma, lsdc_gamma, int, 0644); + +static int lsdc_relax_alignment = -1; +MODULE_PARM_DESC(relax_alignment, + "relax crtc stride alignment (-1 = disabled (default), >0 = enabled)"); +module_param_named(relax_alignment, lsdc_relax_alignment, int, 0644); + + +static struct platform_device * +lsdc_create_platform_device(const char *name, + struct device *parent, + const struct lsdc_chip_desc *descp, + struct resource *res) +{ + struct device *dev; + struct platform_device *pdev; + int ret; + + pdev = platform_device_alloc(name, PLATFORM_DEVID_NONE); + if (!pdev) { + dev_err(parent, "can not create platform device\n"); + return ERR_PTR(-ENOMEM); + } + + dev_info(parent, "platform device %s created\n", name); + + dev = &pdev->dev; + dev->parent = parent; + + if (descp) { + ret = platform_device_add_data(pdev, descp, sizeof(*descp)); + if (ret) { + dev_err(parent, "add platform data failed: %d\n", ret); + goto ERROR_RET; + } + } + + if (res) { + ret = platform_device_add_resources(pdev, res, 1); + if (ret) { + dev_err(parent, "add platform resources failed: %d\n", ret); + goto ERROR_RET; + } + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(parent, "add platform device failed: %d\n", ret); + goto ERROR_RET; + } + + return pdev; + +ERROR_RET: + platform_device_put(pdev); + return ERR_PTR(ret); +} + +static int lsdc_vram_init(struct lsdc_device *ldev) +{ + const struct lsdc_chip_desc * const descp = ldev->desc; + struct pci_dev *gpu; + resource_size_t base, size; + + if (descp->chip == LSDC_CHIP_7A2000) { + /* BAR 2 of LS7A2000's GPU contain VRAM */ + gpu = pci_get_device(PCI_VENDOR_ID_LOONGSON, 0x7A25, NULL); + } else if (descp->chip == LSDC_CHIP_7A1000) { + /* BAR 2 of LS7A1000's GPU(GC1000) contain VRAM */ + gpu = pci_get_device(PCI_VENDOR_ID_LOONGSON, 0x7A15, NULL); + } else { + dev_err(ldev->dev, "Unknown chip, the driver need update\n"); + return -ENOENT; + } + + if (IS_ERR_OR_NULL(gpu)) { + dev_err(ldev->dev, "Can not get VRAM\n"); + return -ENOENT; + } + + base = pci_resource_start(gpu, 2); + size = pci_resource_len(gpu, 2); + + ldev->vram_base = base; + ldev->vram_size = size; + + dev_info(ldev->dev, "vram start: 0x%llx, size: %uMB\n", + (u64)base, (u32)(size >> 20)); + + return 0; +} + +static void lsdc_of_probe(struct lsdc_device *ldev, struct device_node *np) +{ + struct device_node *ports; + + if (!np) { + ldev->has_dt = false; + ldev->has_ports_node = false; + dev_info(ldev->dev, "don't has DT support\n"); + return; + } + + ports = of_get_child_by_name(np, "ports"); + ldev->has_ports_node = ports ? true : false; + of_node_put(ports); +} + +static int lsdc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct device *dev = &pdev->dev; + const struct lsdc_chip_desc *descp; + struct lsdc_device *ldev; + int ret; + + descp = lsdc_detect_chip(pdev, ent); + if (!descp) { + dev_info(dev, "unknown dc ip core, abort\n"); + return -ENOENT; + } + + ldev = devm_kzalloc(dev, sizeof(*ldev), GFP_KERNEL); + if (IS_ERR(ldev)) + return PTR_ERR(ldev); + + ldev->desc = descp; + ldev->dev = dev; + + if (lsdc_use_vram_helper > 0) + ldev->use_vram_helper = true; + else if ((lsdc_use_vram_helper < 0) && descp->has_vram) + ldev->use_vram_helper = true; + else + ldev->use_vram_helper = false; + + if (!descp->broken_gamma) + ldev->enable_gamma = true; + else + ldev->enable_gamma = lsdc_gamma > 0 ? true : false; + + ldev->relax_alignment = lsdc_relax_alignment > 0 ? true : false; + + lsdc_of_probe(ldev, dev->of_node); + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + pci_set_master(pdev); + + /* BAR 0 contains registers */ + ldev->reg_base = devm_ioremap_resource(dev, &pdev->resource[0]); + if (IS_ERR(ldev->reg_base)) + return PTR_ERR(ldev->reg_base); + + /* Create GPIO emulated i2c driver as early as possible */ + if (descp->has_builtin_i2c && ldev->has_ports_node) { + struct device_node *i2c_node; + + for_each_compatible_node(i2c_node, NULL, "loongson,gpio-i2c") { + if (!of_device_is_available(i2c_node)) + continue; + + lsdc_of_create_i2c_adapter(dev, ldev->reg_base, i2c_node); + } + } + + if (ldev->has_dt) { + /* Get the optional framebuffer memory resource */ + ret = of_reserved_mem_device_init(dev); + if (ret && (ret != -ENODEV)) + return ret; + } + + if (descp->has_vram && ldev->use_vram_helper) { + ret = lsdc_vram_init(ldev); + if (ret) { + dev_err(dev, "VRAM is unavailable\n"); + ldev->use_vram_helper = false; + } + } + + ldev->irq = pdev->irq; + + dev_set_drvdata(dev, ldev); + + if (descp->has_vram && ldev->use_vram_helper) { + struct resource res; + + memset(&res, 0, sizeof(res)); + res.flags = IORESOURCE_MEM; + res.name = "LS7A_VRAM"; + res.start = ldev->vram_base; + res.end = ldev->vram_size; + } + + ldev->dc = lsdc_create_platform_device("lsdc", dev, descp, NULL); + if (IS_ERR(ldev->dc)) + return PTR_ERR(ldev->dc); + + return platform_driver_register(&lsdc_platform_driver); +} + +static void lsdc_pci_remove(struct pci_dev *pdev) +{ + struct lsdc_device *ldev = pci_get_drvdata(pdev); + + platform_device_unregister(ldev->dc); + + pci_set_drvdata(pdev, NULL); + + pci_clear_master(pdev); + + pci_release_regions(pdev); +} + +static int lsdc_drm_suspend(struct device *dev) +{ + struct lsdc_device *ldev = dev_get_drvdata(dev); + + return drm_mode_config_helper_suspend(ldev->ddev); +} + +static int lsdc_drm_resume(struct device *dev) +{ + struct lsdc_device *ldev = dev_get_drvdata(dev); + + return drm_mode_config_helper_resume(ldev->ddev); +} + +static int lsdc_pm_freeze(struct device *dev) +{ + return lsdc_drm_suspend(dev); +} + +static int lsdc_pm_thaw(struct device *dev) +{ + return lsdc_drm_resume(dev); +} + +static int lsdc_pm_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + int error; + + error = lsdc_pm_freeze(dev); + if (error) + return error; + + pci_save_state(pdev); + /* Shut down the device */ + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + + return 0; +} + +static int lsdc_pm_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + if (pcim_enable_device(pdev)) + return -EIO; + + pci_set_power_state(pdev, PCI_D0); + + pci_restore_state(pdev); + + return lsdc_pm_thaw(dev); +} + +static const struct dev_pm_ops lsdc_pm_ops = { + .suspend = lsdc_pm_suspend, + .resume = lsdc_pm_resume, + .freeze = lsdc_pm_freeze, + .thaw = lsdc_pm_thaw, + .poweroff = lsdc_pm_freeze, + .restore = lsdc_pm_resume, +}; + +static const struct pci_device_id lsdc_pciid_list[] = { + {PCI_VENDOR_ID_LOONGSON, 0x7a06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (kernel_ulong_t)LSDC_CHIP_7A1000}, + {PCI_VENDOR_ID_LOONGSON, 0x7a36, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (kernel_ulong_t)LSDC_CHIP_7A2000}, + {0, 0, 0, 0, 0, 0, 0} +}; + +static struct pci_driver lsdc_pci_driver = { + .name = DRIVER_NAME, + .id_table = lsdc_pciid_list, + .probe = lsdc_pci_probe, + .remove = lsdc_pci_remove, + .driver.pm = &lsdc_pm_ops, +}; + +static int __init lsdc_drm_init(void) +{ + struct pci_dev *pdev = NULL; + + while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev))) { + /* + * Multiple video card workaround + * + * This integrated video card will always be selected as + * default boot device by vgaarb subsystem. + */ + if (pdev->vendor != PCI_VENDOR_ID_LOONGSON) { + pr_info("Discrete graphic card detected, abort\n"); + return 0; + } + } + + return pci_register_driver(&lsdc_pci_driver); +} +module_init(lsdc_drm_init); + +static void __exit lsdc_drm_exit(void) +{ + pci_unregister_driver(&lsdc_pci_driver); +} +module_exit(lsdc_drm_exit); + +MODULE_DEVICE_TABLE(pci, lsdc_pciid_list); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/loongson/lsdc_plane.c b/drivers/gpu/drm/loongson/lsdc_plane.c new file mode 100644 index 000000000000..6f65c9fd687e --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_plane.c @@ -0,0 +1,476 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Loongson Corporation + */ + +/* + * Authors: + * Sui Jingfeng + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lsdc_drv.h" +#include "lsdc_regs.h" + +static const u32 lsdc_primary_formats[] = { + DRM_FORMAT_RGB565, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, +}; + +static const u32 lsdc_cursor_formats[] = { + DRM_FORMAT_ARGB8888, +}; + +static const u64 lsdc_fb_format_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +static void lsdc_update_fb_format(struct lsdc_device *ldev, + struct drm_crtc *crtc, + const struct drm_format_info *fmt_info) +{ + unsigned int index = drm_crtc_index(crtc); + u32 val = 0; + u32 fmt; + + switch (fmt_info->format) { + case DRM_FORMAT_RGB565: + fmt = LSDC_PF_RGB565; + break; + case DRM_FORMAT_XRGB8888: + fmt = LSDC_PF_XRGB8888; + break; + case DRM_FORMAT_ARGB8888: + fmt = LSDC_PF_XRGB8888; + break; + default: + fmt = LSDC_PF_XRGB8888; + break; + } + + if (index == 0) { + val = readl(ldev->reg_base + LSDC_CRTC0_CFG_REG); + val = (val & ~CFG_PIX_FMT_MASK) | fmt; + writel(val, ldev->reg_base + LSDC_CRTC0_CFG_REG); + } else if (index == 1) { + val = readl(ldev->reg_base + LSDC_CRTC1_CFG_REG); + val = (val & ~CFG_PIX_FMT_MASK) | fmt; + writel(val, ldev->reg_base + LSDC_CRTC1_CFG_REG); + } +} + +static void lsdc_update_fb_start_addr(struct lsdc_device *ldev, + struct drm_crtc *crtc, + u64 paddr) +{ + unsigned int index = drm_crtc_index(crtc); + u32 lo32_addr_reg; + u32 hi32_addr_reg; + u32 cfg_reg; + u32 val; + + /* + * Find which framebuffer address register should update. + * if FB_ADDR0_REG is in using, we write the addr to FB_ADDR1_REG, + * if FB_ADDR1_REG is in using, we write the addr to FB_ADDR0_REG + */ + if (index == 0) { + /* CRTC0 */ + val = readl(ldev->reg_base + LSDC_CRTC0_CFG_REG); + + cfg_reg = LSDC_CRTC0_CFG_REG; + hi32_addr_reg = LSDC_CRTC0_FB_HI_ADDR_REG; + + if (val & CFG_FB_IDX_BIT) + lo32_addr_reg = LSDC_CRTC0_FB_ADDR0_REG; + else + lo32_addr_reg = LSDC_CRTC0_FB_ADDR1_REG; + } else if (index == 1) { + /* CRTC1 */ + val = readl(ldev->reg_base + LSDC_CRTC1_CFG_REG); + + cfg_reg = LSDC_CRTC1_CFG_REG; + hi32_addr_reg = LSDC_CRTC1_FB_HI_ADDR_REG; + + if (val & CFG_FB_IDX_BIT) + lo32_addr_reg = LSDC_CRTC1_FB_ADDR0_REG; + else + lo32_addr_reg = LSDC_CRTC1_FB_ADDR1_REG; + } + + drm_dbg(ldev->ddev, "crtc%u scantout from 0x%llx\n", index, paddr); + + /* The bridge's bus width is 40 */ + writel(paddr, ldev->reg_base + lo32_addr_reg); + writel((paddr >> 32) & 0xFF, ldev->reg_base + hi32_addr_reg); + /* + * Then, we triger the fb switch, the switch of the framebuffer + * to be scanout will complete at the next vblank. + */ + writel(val | CFG_PAGE_FLIP_BIT, ldev->reg_base + cfg_reg); +} + +static unsigned int lsdc_get_fb_offset(struct drm_framebuffer *fb, + struct drm_plane_state *state, + unsigned int plane) +{ + unsigned int offset = fb->offsets[plane]; + + offset += fb->format->cpp[plane] * (state->src_x >> 16); + offset += fb->pitches[plane] * (state->src_y >> 16); + + return offset; +} + +static s64 lsdc_get_vram_bo_offset(struct drm_framebuffer *fb) +{ + struct drm_gem_vram_object *gbo; + s64 gpu_addr; + + gbo = drm_gem_vram_of_gem(fb->obj[0]); + gpu_addr = drm_gem_vram_offset(gbo); + + return gpu_addr; +} + +static int lsdc_primary_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_crtc *crtc = state->crtc; + struct drm_crtc_state *crtc_state; + struct drm_framebuffer *fb = state->fb; + + /* no need for further checks if the plane is being disabled */ + if (!crtc || !fb) + return 0; + + crtc_state = drm_atomic_get_crtc_state(state->state, crtc); + if (WARN_ON(!crtc_state)) + return -EINVAL; + + return drm_atomic_helper_check_plane_state(plane->state, + crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, true); +} + +static void lsdc_update_stride(struct lsdc_device *ldev, + struct drm_crtc *crtc, + unsigned int stride) +{ + unsigned int index = drm_crtc_index(crtc); + + if (index == 0) + writel(stride, ldev->reg_base + LSDC_CRTC0_STRIDE_REG); + else if (index == 1) + writel(stride, ldev->reg_base + LSDC_CRTC1_STRIDE_REG); + + drm_dbg(ldev->ddev, "update stride to %u\n", stride); +} + +static void lsdc_primary_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_device *ddev = plane->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + struct drm_plane_state *new_plane_state = plane->state; + struct drm_crtc *crtc = new_plane_state->crtc; + struct drm_framebuffer *fb = new_plane_state->fb; + u32 fb_offset = lsdc_get_fb_offset(fb, new_plane_state, 0); + dma_addr_t fb_addr; + + if (ldev->use_vram_helper) { + s64 gpu_addr; + + gpu_addr = lsdc_get_vram_bo_offset(fb); + if (gpu_addr < 0) + return; + + fb_addr = ldev->vram_base + gpu_addr + fb_offset; + } else { + struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, 0); + + fb_addr = obj->paddr + fb_offset; + } + + lsdc_update_fb_start_addr(ldev, crtc, fb_addr); + + lsdc_update_stride(ldev, crtc, fb->pitches[0]); + + if (drm_atomic_crtc_needs_modeset(crtc->state)) + lsdc_update_fb_format(ldev, crtc, fb->format); +} + +static void lsdc_primary_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + drm_dbg(plane->dev, "%s disabled\n", plane->name); +} + +static int lsdc_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + struct lsdc_device *ldev = to_lsdc(plane->dev); + + if (ldev->use_vram_helper) + return drm_gem_vram_plane_helper_prepare_fb(plane, new_state); + + return drm_gem_fb_prepare_fb(plane, new_state); +} + +static void lsdc_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_device *ddev = plane->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + + if (ldev->use_vram_helper) + return drm_gem_vram_plane_helper_cleanup_fb(plane, old_state); +} + +static const struct drm_plane_helper_funcs lsdc_primary_plane_helpers = { + .prepare_fb = lsdc_plane_prepare_fb, + .cleanup_fb = lsdc_plane_cleanup_fb, + .atomic_check = lsdc_primary_plane_atomic_check, + .atomic_update = lsdc_primary_plane_atomic_update, + .atomic_disable = lsdc_primary_plane_atomic_disable, +}; + +static int lsdc_cursor_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_framebuffer *fb = state->fb; + struct drm_crtc *crtc = state->crtc; + struct drm_crtc_state *crtc_state; + + /* no need for further checks if the plane is being disabled */ + if (!crtc || !fb) + return 0; + + if (!state->visible) + return 0; + + crtc_state = drm_atomic_get_crtc_state(state->state, crtc); + if (WARN_ON(!crtc_state)) + return -EINVAL; + + return drm_atomic_helper_check_plane_state(state, + crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + true, + true); +} + +static void lsdc_cursor_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_plane_state) +{ + struct lsdc_display_pipe * const dispipe = lsdc_cursor_to_dispipe(plane); + struct drm_device *ddev = plane->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + const struct lsdc_chip_desc * const descp = ldev->desc; + struct drm_plane_state *new_plane_state = plane->state; + struct drm_framebuffer *new_fb = new_plane_state->fb; + struct drm_framebuffer *old_fb = old_plane_state->fb; + int dst_x = new_plane_state->crtc_x; + int dst_y = new_plane_state->crtc_y; + u32 val; + + if (new_fb != old_fb) { + u64 cursor_addr; + + if (ldev->use_vram_helper) { + s64 offset; + + offset = lsdc_get_vram_bo_offset(new_fb); + cursor_addr = ldev->vram_base + offset; + + drm_dbg(ddev, "%s offset: %llx\n", plane->name, offset); + } else { + struct drm_gem_cma_object *cursor_obj; + + cursor_obj = drm_fb_cma_get_gem_obj(new_fb, 0); + if (!cursor_obj) + return; + + cursor_addr = cursor_obj->paddr; + } + + if ((descp->chip == LSDC_CHIP_7A2000) && (dispipe->index == 1)) + writel(cursor_addr, ldev->reg_base + LSDC_CURSOR1_ADDR_REG); + else + writel(cursor_addr, ldev->reg_base + LSDC_CURSOR0_ADDR_REG); + } + + /* Update cursor's position */ + if (dst_x < 0) + dst_x = 0; + + if (dst_y < 0) + dst_y = 0; + + val = (dst_y << 16) | dst_x; + + if ((descp->chip == LSDC_CHIP_7A2000) && (dispipe->index == 1)) + writel(val, ldev->reg_base + LSDC_CURSOR1_POSITION_REG); + else + writel(val, ldev->reg_base + LSDC_CURSOR0_POSITION_REG); + + /* Update cursor's location and format */ + val = CURSOR_FORMAT_ARGB8888; + + if (descp->chip == LSDC_CHIP_7A2000) { + /* LS7A2000 support 64x64 and 32x32 */ + val |= CURSOR_SIZE_64X64; + if (dispipe->index == 1) { + val |= CURSOR_LOCATION_BIT; + writel(val, ldev->reg_base + LSDC_CURSOR1_CFG_REG); + } else if (dispipe->index == 0) { + val &= ~CURSOR_LOCATION_BIT; + writel(val, ldev->reg_base + LSDC_CURSOR0_CFG_REG); + } + } else { + /* + * Update the location of the cursor + * if bit 4 of LSDC_CURSOR_CFG_REG is 1, then the cursor will be + * locate at CRTC1, if bit 4 of LSDC_CURSOR_CFG_REG is 0, then + * the cursor will be locate at CRTC0. + */ + if (dispipe->index) + val |= CURSOR_LOCATION_BIT; + + writel(val, ldev->reg_base + LSDC_CURSOR0_CFG_REG); + } +} + +static void lsdc_cursor_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + const struct lsdc_display_pipe * const dispipe = lsdc_cursor_to_dispipe(plane); + struct drm_device *ddev = plane->dev; + struct lsdc_device *ldev = to_lsdc(ddev); + const struct lsdc_chip_desc * const descp = ldev->desc; + u32 val; + + if ((descp->chip == LSDC_CHIP_7A2000) && (dispipe->index == 1)) { + val = readl(ldev->reg_base + LSDC_CURSOR1_CFG_REG); + val &= ~CURSOR_FORMAT_MASK; + val |= CURSOR_FORMAT_DISABLE; + writel(val, ldev->reg_base + LSDC_CURSOR1_CFG_REG); + } else { + val = readl(ldev->reg_base + LSDC_CURSOR0_CFG_REG); + val &= ~CURSOR_FORMAT_MASK; + val |= CURSOR_FORMAT_DISABLE; + writel(val, ldev->reg_base + LSDC_CURSOR0_CFG_REG); + } + + drm_dbg(ddev, "%s disabled\n", plane->name); +} + +static const struct drm_plane_helper_funcs lsdc_cursor_plane_helpers = { + .prepare_fb = lsdc_plane_prepare_fb, + .cleanup_fb = lsdc_plane_cleanup_fb, + .atomic_check = lsdc_cursor_atomic_check, + .atomic_update = lsdc_cursor_atomic_update, + .atomic_disable = lsdc_cursor_atomic_disable, +}; + +static int lsdc_plane_get_default_zpos(enum drm_plane_type type) +{ + switch (type) { + case DRM_PLANE_TYPE_PRIMARY: + return 0; + case DRM_PLANE_TYPE_OVERLAY: + return 1; + case DRM_PLANE_TYPE_CURSOR: + return 7; + } + + return 0; +} + +static void lsdc_plane_reset(struct drm_plane *plane) +{ + drm_atomic_helper_plane_reset(plane); + + plane->state->zpos = lsdc_plane_get_default_zpos(plane->type); + + drm_dbg(plane->dev, "%s reset\n", plane->name); +} + +static const struct drm_plane_funcs lsdc_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = lsdc_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +int lsdc_plane_init(struct lsdc_device *ldev, + struct drm_plane *plane, + enum drm_plane_type type, + unsigned int index) +{ + struct drm_device *ddev = ldev->ddev; + int zpos = lsdc_plane_get_default_zpos(type); + unsigned int format_count; + const u32 *formats; + const char *name; + int ret; + + switch (type) { + case DRM_PLANE_TYPE_PRIMARY: + formats = lsdc_primary_formats; + format_count = ARRAY_SIZE(lsdc_primary_formats); + name = "primary-%u"; + break; + case DRM_PLANE_TYPE_CURSOR: + formats = lsdc_cursor_formats; + format_count = ARRAY_SIZE(lsdc_cursor_formats); + name = "cursor-%u"; + break; + case DRM_PLANE_TYPE_OVERLAY: + drm_err(ddev, "overlay plane is not supported\n"); + break; + } + + ret = drm_universal_plane_init(ddev, plane, 1 << index, + &lsdc_plane_funcs, + formats, format_count, + lsdc_fb_format_modifiers, + type, name, index); + if (ret) { + drm_err(ddev, "%s failed: %d\n", __func__, ret); + return ret; + } + + switch (type) { + case DRM_PLANE_TYPE_PRIMARY: + drm_plane_helper_add(plane, &lsdc_primary_plane_helpers); + drm_plane_create_zpos_property(plane, zpos, 0, 6); + break; + case DRM_PLANE_TYPE_CURSOR: + drm_plane_helper_add(plane, &lsdc_cursor_plane_helpers); + drm_plane_create_zpos_immutable_property(plane, zpos); + break; + case DRM_PLANE_TYPE_OVERLAY: + drm_err(ddev, "overlay plane is not supported\n"); + break; + } + + drm_plane_create_alpha_property(plane); + + return 0; +} diff --git a/drivers/gpu/drm/loongson/lsdc_pll.c b/drivers/gpu/drm/loongson/lsdc_pll.c new file mode 100644 index 000000000000..4b3e7650c99a --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_pll.c @@ -0,0 +1,599 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Loongson Corporation + */ + +/* + * Authors: + * Sui Jingfeng + */ + +#include "lsdc_drv.h" +#include "lsdc_regs.h" +#include "lsdc_pll.h" + +/* + * The structure of the pixel PLL register is evolved with times. + * All loongson's cpu is little endian. + */ + +/* u64 */ +struct ls7a1000_pixpll_bitmap { + /* Byte 0 ~ Byte 3 */ + unsigned div_out : 7; /* 0 : 6 output clock divider */ + unsigned reserved_1 : 14; /* 7 : 20 */ + unsigned loopc : 9; /* 21 : 29 */ + unsigned reserved_2 : 2; /* 30 : 31 */ + + /* Byte 4 ~ Byte 7 */ + unsigned div_ref : 7; /* 0 : 6 input clock divider */ + unsigned locked : 1; /* 7 PLL locked flag */ + unsigned sel_out : 1; /* 8 output clk selector */ + unsigned reserved_3 : 2; /* 9 : 10 reserved */ + unsigned set_param : 1; /* 11 set pll param */ + unsigned bypass : 1; /* 12 */ + unsigned powerdown : 1; /* 13 */ + unsigned reserved_4 : 18; /* 14 : 31 */ +}; + +/* u128 */ +struct ls2k1000_pixpll_bitmap { + /* Byte 0 ~ Byte 3 */ + unsigned sel_out : 1; /* 0 select this PLL */ + unsigned reserved_1 : 1; /* 1 */ + unsigned sw_adj_en : 1; /* 2 allow software adjust */ + unsigned bypass : 1; /* 3 bypass L1 PLL */ + unsigned reserved_2 : 3; /* 4:6 */ + unsigned lock_en : 1; /* 7 enable lock L1 PLL */ + unsigned reserved_3 : 2; /* 8:9 */ + unsigned lock_check : 2; /* 10:11 precision check */ + unsigned reserved_4 : 4; /* 12:15 */ + + unsigned locked : 1; /* 16 PLL locked flag bit */ + unsigned reserved_5 : 2; /* 17:18 */ + unsigned powerdown : 1; /* 19 powerdown the pll if set */ + unsigned reserved_6 : 6; /* 20:25 */ + unsigned div_ref : 6; /* 26:31 L1 Prescaler */ + + /* Byte 4 ~ Byte 7 */ + unsigned loopc : 10; /* 32:41 Clock Multiplier */ + unsigned l1_div : 6; /* 42:47 not used */ + unsigned reserved_7 : 16; /* 48:63 */ + + /* Byte 8 ~ Byte 15 */ + unsigned div_out : 6; /* 0 : 5 output clock divider */ + unsigned reserved_8 : 26; /* 6 : 31 */ + unsigned reserved_9 : 32; /* 70: 127 */ +}; + +/* u32 */ +struct ls2k0500_pixpll_bitmap { + /* Byte 0 ~ Byte 1 */ + unsigned sel_out : 1; + unsigned reserved_1 : 2; + unsigned sw_adj_en : 1; /* allow software adjust */ + unsigned bypass : 1; /* bypass L1 PLL */ + unsigned powerdown : 1; /* write 1 to powerdown the PLL */ + unsigned reserved_2 : 1; + unsigned locked : 1; /* 7 Is L1 PLL locked, read only */ + unsigned div_ref : 6; /* 8:13 ref clock divider */ + unsigned reserved_3 : 2; /* 14:15 */ + /* Byte 2 ~ Byte 3 */ + unsigned loopc : 8; /* 16:23 Clock Multiplier */ + unsigned div_out : 6; /* 24:29 output clock divider */ + unsigned reserved_4 : 2; /* 30:31 */ +}; + +union lsdc_pixpll_bitmap { + struct ls7a1000_pixpll_bitmap ls7a2000; + struct ls7a1000_pixpll_bitmap ls7a1000; + struct ls2k1000_pixpll_bitmap ls2k1000; + struct ls2k0500_pixpll_bitmap ls2k0500; + + u32 dword[4]; +}; + +struct pixclk_to_pll_parm { + /* kHz */ + unsigned int clock; + + /* unrelated information */ + unsigned short width; + unsigned short height; + unsigned short vrefresh; + + /* Stores parameters for programming the Hardware PLLs */ + unsigned short div_out; + unsigned short loopc; + unsigned short div_ref; +}; + +/* + * Pixel clock to PLL parameters translation table. + * Small static cached value to speed up PLL parameters calculation. + */ +static const struct pixclk_to_pll_parm pll_param_table[] = { + {148500, 1920, 1080, 60, 11, 49, 3}, /* 1920x1080@60Hz */ + /* 1920x1080@50Hz */ + {174500, 1920, 1080, 75, 17, 89, 3}, /* 1920x1080@75Hz */ + {181250, 2560, 1080, 75, 8, 58, 4}, /* 2560x1080@75Hz */ + {146250, 1680, 1050, 60, 16, 117, 5}, /* 1680x1050@60Hz */ + {135000, 1280, 1024, 75, 10, 54, 4}, /* 1280x1024@75Hz */ + + {108000, 1600, 900, 60, 15, 81, 5}, /* 1600x900@60Hz */ + /* 1280x1024@60Hz */ + /* 1280x960@60Hz */ + /* 1152x864@75Hz */ + + {106500, 1440, 900, 60, 19, 81, 4}, /* 1440x900@60Hz */ + {88750, 1440, 900, 60, 16, 71, 5}, /* 1440x900@60Hz */ + {83500, 1280, 800, 60, 17, 71, 5}, /* 1280x800@60Hz */ + {71000, 1280, 800, 60, 20, 71, 5}, /* 1280x800@60Hz */ + + {74250, 1280, 720, 60, 22, 49, 3}, /* 1280x720@60Hz */ + /* 1280x720@50Hz */ + + {78750, 1024, 768, 75, 16, 63, 5}, /* 1024x768@75Hz */ + {75000, 1024, 768, 70, 29, 87, 4}, /* 1024x768@70Hz */ + {65000, 1024, 768, 60, 20, 39, 3}, /* 1024x768@60Hz */ + + {51200, 1024, 600, 60, 25, 64, 5}, /* 1024x600@60Hz */ + + {57284, 832, 624, 75, 24, 55, 4}, /* 832x624@75Hz */ + {49500, 800, 600, 75, 40, 99, 5}, /* 800x600@75Hz */ + {50000, 800, 600, 72, 44, 88, 4}, /* 800x600@72Hz */ + {40000, 800, 600, 60, 30, 36, 3}, /* 800x600@60Hz */ + {36000, 800, 600, 56, 50, 72, 4}, /* 800x600@56Hz */ + {31500, 640, 480, 75, 40, 63, 5}, /* 640x480@75Hz */ + /* 640x480@73Hz */ + + {30240, 640, 480, 67, 62, 75, 4}, /* 640x480@67Hz */ + {27000, 720, 576, 50, 50, 54, 4}, /* 720x576@60Hz */ + {25175, 640, 480, 60, 85, 107, 5}, /* 640x480@60Hz */ + {25200, 640, 480, 60, 50, 63, 5}, /* 640x480@60Hz */ + /* 720x480@60Hz */ +}; + +/** + * lsdc_pixpll_setup - ioremap the device dependent PLL registers + * + * @this: point to the object which this function is called from + */ +static int lsdc_pixpll_setup(struct lsdc_pll * const this) +{ + this->mmio = ioremap(this->reg_base, this->reg_size); + + return 0; +} + +/* + * Find a set of pll parameters (to generate pixel clock) from a static + * local table, which avoid to compute the pll parameter eachtime a + * modeset is triggered. + * + * @this: point to the object which this function is called from + * @clock: the desired output pixel clock, the unit is kHz + * @pout: point to where the parameters to store if found + * + * Return true if hit, otherwise return false. + */ +static bool lsdc_pixpll_find(struct lsdc_pll * const this, + unsigned int clock, + struct lsdc_pll_core_values * const pout) +{ + unsigned int num = ARRAY_SIZE(pll_param_table); + unsigned int i; + + for (i = 0; i < num; i++) { + if (clock != pll_param_table[i].clock) + continue; + + pout->div_ref = pll_param_table[i].div_ref; + pout->loopc = pll_param_table[i].loopc; + pout->div_out = pll_param_table[i].div_out; + + return true; + } + + drm_dbg(this->ddev, "pixel clock %u: miss\n", clock); + + return false; +} + +/* + * Find a set of pll parameters which have minimal difference with the desired + * pixel clock frequency. It does that by computing all of the possible + * combination. Compute the diff and find the combination with minimal diff. + * + * clock_out = refclk / div_ref * loopc / div_out + * + * refclk is fixed as 100MHz in ls7a1000, ls2k1000 and ls2k0500 + * + * @this: point to the object from which this function is called + * @clk: the desired output pixel clock, the unit is kHz + * @pout: point to where the parameters to store if success + * + * Return true if a parameter is found, otherwise return false + */ +static bool lsdc_pixpll_compute(struct lsdc_pll * const this, + unsigned int clk, + struct lsdc_pll_core_values *pout) +{ + unsigned int refclk = this->ref_clock; + const unsigned int tolerance = 1000; + unsigned int min = tolerance; + unsigned int div_out, loopc, div_ref; + + if (lsdc_pixpll_find(this, clk, pout)) + return true; + + for (div_out = 6; div_out < 64; div_out++) { + for (div_ref = 3; div_ref < 6; div_ref++) { + for (loopc = 6; loopc < 161; loopc++) { + int diff; + + if (loopc < 12 * div_ref) + continue; + if (loopc > 32 * div_ref) + continue; + + diff = clk * div_out - refclk * loopc / div_ref; + + if (diff < 0) + diff = -diff; + + if (diff < min) { + min = diff; + pout->div_ref = div_ref; + pout->div_out = div_out; + pout->loopc = loopc; + + if (diff == 0) + return true; + } + } + } + } + + return min < tolerance; +} + +/* + * Update the pll parameters to hardware, target to the pixpll in ls7a1000 + * + * @this: point to the object from which this function is called + * @param: point to the core parameters passed in + * + * return 0 if successful. + */ +static int ls7a1000_pixpll_param_update(struct lsdc_pll * const this, + struct lsdc_pll_core_values const *param) +{ + void __iomem *reg = this->mmio; + unsigned int counter = 0; + bool locked; + u32 val; + + /* Bypass the software configured PLL, using refclk directly */ + val = readl(reg + 0x4); + val &= ~(1 << 8); + writel(val, reg + 0x4); + + /* Powerdown the PLL */ + val = readl(reg + 0x4); + val |= (1 << 13); + writel(val, reg + 0x4); + + /* Clear the pll parameters */ + val = readl(reg + 0x4); + val &= ~(1 << 11); + writel(val, reg + 0x4); + + /* clear old value & config new value */ + val = readl(reg + 0x04); + val &= ~0x7F; + val |= param->div_ref; /* div_ref */ + writel(val, reg + 0x4); + + val = readl(reg); + val &= ~0x7f; + val |= param->div_out; /* div_out */ + + val &= ~(0x1ff << 21); + val |= param->loopc << 21; /* loopc */ + writel(val, reg); + + /* Set the pll the parameters */ + val = readl(reg + 0x4); + val |= (1 << 11); + writel(val, reg + 0x4); + + /* Powerup the PLL */ + val = readl(reg + 0x4); + val &= ~(1 << 13); + writel(val, reg + 0x4); + + /* Wait the PLL lock */ + do { + val = readl(reg + 0x4); + locked = val & 0x80; + counter++; + } while (!locked && (counter < 10000)); + + drm_dbg(this->ddev, "%u loop waited\n", counter); + + /* Switch to the software configured pll */ + val = readl(reg + 0x4); + val |= (1UL << 8); + writel(val, reg + 0x4); + + return 0; +} + +/* + * Update the pll parameters to hardware, target to the pixpll in ls2k1000 + * + * @this: point to the object from which this function is called + * @param: pointer to where the parameter is passed in + * + * return 0 if successful. + */ +static int ls2k1000_pixpll_param_update(struct lsdc_pll * const this, + struct lsdc_pll_core_values const *param) +{ + void __iomem *reg = this->mmio; + unsigned int counter = 0; + bool locked = false; + u32 val; + + val = readl(reg); + /* Bypass the software configured PLL, using refclk directly */ + val &= ~(1 << 0); + writel(val, reg); + + /* Powerdown the PLL */ + val |= (1 << 19); + writel(val, reg); + + /* Allow the software configuration */ + val &= ~(1 << 2); + writel(val, reg); + + /* allow L1 PLL lock */ + val = (1L << 7) | (3L << 10); + writel(val, reg); + + /* clear div_ref bit field */ + val &= ~(0x3f << 26); + /* set div_ref bit field */ + val = val | (param->div_ref << 26); + writel(val, reg); + + val = readl(reg + 4); + /* clear loopc bit field */ + val &= ~0x0fff; + /* set loopc bit field */ + val |= param->loopc; + writel(val, reg + 4); + + /* set div_out */ + writel(param->div_out, reg + 8); + + val = readl(reg); + /* use the software configure param */ + val |= (1 << 2); + /* powerup the PLL */ + val &= ~(1 << 19); + writel(val, reg); + + /* wait pll setup and locked */ + do { + val = readl(reg); + locked = val & 0x10000; + counter++; + } while (!locked && (counter < 10000)); + + drm_dbg(this->ddev, "%u loop waited\n", counter); + + /* Switch to the above software configured PLL instead of refclk */ + val |= 1; + writel(val, reg); + + return 0; +} + +/* + * Update the pll parameters to hardware, target to the pixpll in ls2k0500 + * + * @this: point to the object which calling this function + * @param: pointer to where the parameters passed in + * + * return 0 if successful. + */ +static int ls2k0500_pixpll_param_update(struct lsdc_pll * const this, + struct lsdc_pll_core_values const *param) +{ + void __iomem *reg = this->mmio; + unsigned int counter = 0; + bool locked = false; + u32 val; + + /* Bypass the software configured PLL, using refclk directly */ + val = readl(reg); + val &= ~(1 << 0); + writel(val, reg); + + /* Powerdown the PLL */ + val = readl(reg); + val |= (1 << 5); + writel(val, reg); + + /* Allow the software configuration */ + val |= (1 << 3); + writel(val, reg); + + /* Update the pll params */ + val = (param->div_out << 24) | + (param->loopc << 16) | + (param->div_ref << 8); + + writel(val, reg); + + /* Powerup the PLL */ + val = readl(reg); + val &= ~(1 << 5); + writel(val, reg); + + /* wait pll setup and locked */ + do { + val = readl(reg); + locked = val & 0x80; + counter++; + } while (!locked && (counter < 10000)); + + drm_dbg(this->ddev, "%u loop waited\n", counter); + + /* Switch to the above software configured PLL instead of refclk */ + writel((val | 1), reg); + + return 0; +} + +static unsigned int lsdc_get_clock_rate(struct lsdc_pll * const this, + struct lsdc_pll_core_values *pout) +{ + struct drm_device *ddev = this->ddev; + struct lsdc_device *ldev = to_lsdc(ddev); + const struct lsdc_chip_desc * const desc = ldev->desc; + unsigned int out; + union lsdc_pixpll_bitmap parms; + + if (desc->chip == LSDC_CHIP_7A2000) { + struct ls7a1000_pixpll_bitmap *obj = &parms.ls7a2000; + + parms.dword[0] = readl(this->mmio); + parms.dword[1] = readl(this->mmio + 4); + out = this->ref_clock / obj->div_ref * obj->loopc / obj->div_out; + if (pout) { + pout->div_ref = obj->div_ref; + pout->loopc = obj->loopc; + pout->div_out = obj->div_out; + } + } else if (desc->chip == LSDC_CHIP_7A1000) { + struct ls7a1000_pixpll_bitmap *obj = &parms.ls7a1000; + + parms.dword[0] = readl(this->mmio); + parms.dword[1] = readl(this->mmio + 4); + out = this->ref_clock / obj->div_ref * obj->loopc / obj->div_out; + if (pout) { + pout->div_ref = obj->div_ref; + pout->loopc = obj->loopc; + pout->div_out = obj->div_out; + } + } else if (desc->chip == LSDC_CHIP_2K1000) { + struct ls2k1000_pixpll_bitmap *obj = &parms.ls2k1000; + + parms.dword[0] = readl(this->mmio); + parms.dword[1] = readl(this->mmio + 4); + parms.dword[2] = readl(this->mmio + 8); + parms.dword[3] = readl(this->mmio + 12); + out = this->ref_clock / obj->div_ref * obj->loopc / obj->div_out; + if (pout) { + pout->div_ref = obj->div_ref; + pout->loopc = obj->loopc; + pout->div_out = obj->div_out; + } + } else if (desc->chip == LSDC_CHIP_2K0500) { + struct ls2k0500_pixpll_bitmap *obj = &parms.ls2k0500; + + parms.dword[0] = readl(this->mmio); + out = this->ref_clock / obj->div_ref * obj->loopc / obj->div_out; + if (pout) { + pout->div_ref = obj->div_ref; + pout->loopc = obj->loopc; + pout->div_out = obj->div_out; + } + } else { + drm_err(ddev, "unknown chip, the driver need update\n"); + return 0; + } + + return out; +} + +static const struct lsdc_pixpll_funcs ls7a2000_pixpll_funcs = { + .setup = lsdc_pixpll_setup, + .compute = lsdc_pixpll_compute, + .update = ls7a1000_pixpll_param_update, + .get_clock_rate = lsdc_get_clock_rate, +}; + +static const struct lsdc_pixpll_funcs ls7a1000_pixpll_funcs = { + .setup = lsdc_pixpll_setup, + .compute = lsdc_pixpll_compute, + .update = ls7a1000_pixpll_param_update, + .get_clock_rate = lsdc_get_clock_rate, +}; + +static const struct lsdc_pixpll_funcs ls2k1000_pixpll_funcs = { + .setup = lsdc_pixpll_setup, + .compute = lsdc_pixpll_compute, + .update = ls2k1000_pixpll_param_update, + .get_clock_rate = lsdc_get_clock_rate, +}; + +static const struct lsdc_pixpll_funcs ls2k0500_pixpll_funcs = { + .setup = lsdc_pixpll_setup, + .compute = lsdc_pixpll_compute, + .update = ls2k0500_pixpll_param_update, + .get_clock_rate = lsdc_get_clock_rate, +}; + +int lsdc_pixpll_init(struct lsdc_pll * const this, + struct drm_device *ddev, + unsigned int index) +{ + struct lsdc_device *ldev = to_lsdc(ddev); + const struct lsdc_chip_desc * const descp = ldev->desc; + + this->ddev = ddev; + this->index = index; + this->ref_clock = LSDC_PLL_REF_CLK; + + if (descp->chip == LSDC_CHIP_7A2000) { + if (index == 0) + this->reg_base = LS7A1000_CFG_REG_BASE + LS7A1000_PIX_PLL0_REG; + else if (index == 1) + this->reg_base = LS7A1000_CFG_REG_BASE + LS7A1000_PIX_PLL1_REG; + this->reg_size = 8; + this->funcs = &ls7a2000_pixpll_funcs; + } else if (descp->chip == LSDC_CHIP_7A1000) { + if (index == 0) + this->reg_base = LS7A1000_CFG_REG_BASE + LS7A1000_PIX_PLL0_REG; + else if (index == 1) + this->reg_base = LS7A1000_CFG_REG_BASE + LS7A1000_PIX_PLL1_REG; + this->reg_size = 8; + this->funcs = &ls7a1000_pixpll_funcs; + } else if (descp->chip == LSDC_CHIP_2K1000) { + if (index == 0) + this->reg_base = LS2K1000_CFG_REG_BASE + LS2K1000_PIX_PLL0_REG; + else if (index == 1) + this->reg_base = LS2K1000_CFG_REG_BASE + LS2K1000_PIX_PLL1_REG; + + this->reg_size = 16; + this->funcs = &ls2k1000_pixpll_funcs; + } else if (descp->chip == LSDC_CHIP_2K0500) { + if (index == 0) + this->reg_base = LS2K0500_CFG_REG_BASE + LS2K0500_PIX_PLL0_REG; + else if (index == 1) + this->reg_base = LS2K0500_CFG_REG_BASE + LS2K0500_PIX_PLL1_REG; + + this->reg_size = 4; + this->funcs = &ls2k0500_pixpll_funcs; + } else { + drm_err(this->ddev, "unknown chip, the driver need update\n"); + return -ENOENT; + } + + return this->funcs->setup(this); +} diff --git a/drivers/gpu/drm/loongson/lsdc_pll.h b/drivers/gpu/drm/loongson/lsdc_pll.h new file mode 100644 index 000000000000..2b9101f56097 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_pll.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022 Loongson Corporation + */ + +/* + * Authors: + * Sui Jingfeng + */ + +#ifndef __LSDC_PLL_H__ +#define __LSDC_PLL_H__ + +#include + +/* + * PIXEL PLL hardware structure + * + * refclk: reference frequency, 100 MHz from external oscillator + * outclk: output frequency desired. + * + * + * L1 Fref Fvco L2 + * refclk +-----------+ +------------------+ +---------+ outclk + * ---+---> | Prescaler | ---> | Clock Multiplier | ---> | divider | --------> + * | +-----------+ +------------------+ +---------+ ^ + * | ^ ^ ^ | + * | | | | | + * | | | | | + * | div_ref loopc div_out | + * | | + * +---- sel_out (bypass above software configurable clock if==1) ----+ + * + * sel_out: PLL clock output selector. + * + * If sel_out == 1, it will take refclk as output directly, + * the L1 Prescaler and the out divider is bypassed. + * + * If sel_out == 0, then: + * outclk = refclk / div_ref * loopc / div_out; + * + * PLL hardware working requirements: + * + * 1) 20 MHz <= refclk / div_ref <= 40Mhz + * 2) 1.2 GHz <= refclk /div_out * loopc <= 3.2 Ghz + * + */ + +struct lsdc_pll_core_values { + unsigned int div_ref; + unsigned int loopc; + unsigned int div_out; +}; + +struct lsdc_pll; + +struct lsdc_pixpll_funcs { + int (*setup)(struct lsdc_pll * const this); + bool (*compute)(struct lsdc_pll * const this, unsigned int clock, + struct lsdc_pll_core_values *params_out); + int (*update)(struct lsdc_pll * const this, + struct lsdc_pll_core_values const *params_in); + unsigned int (*get_clock_rate)(struct lsdc_pll * const this, + struct lsdc_pll_core_values *pout); +}; + +struct lsdc_pll { + const struct lsdc_pixpll_funcs *funcs; + struct drm_device *ddev; + void __iomem *mmio; + + /* PLL register offset */ + u32 reg_base; + /* PLL register size in bytes */ + u32 reg_size; + + /* 100000kHz, fixed on all board found */ + unsigned int ref_clock; + + unsigned int index; +}; + +int lsdc_pixpll_init(struct lsdc_pll * const this, + struct drm_device *ddev, + unsigned int index); + +#endif diff --git a/drivers/gpu/drm/loongson/lsdc_regs.h b/drivers/gpu/drm/loongson/lsdc_regs.h new file mode 100644 index 000000000000..ffa6285530d7 --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_regs.h @@ -0,0 +1,252 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022 Loongson Corporation + */ + +/* + * Authors: + * Sui Jingfeng + */ + +#ifndef __LSDC_REGS_H__ +#define __LSDC_REGS_H__ + +#include +#include + +/* + * PIXEL PLL + */ +#define LSDC_PLL_REF_CLK 100000 /* kHz */ + +/* + * Those PLL registers are not located at DC reg bar space, + * there are relative to LSXXXXX_CFG_REG_BASE. + * XXXXX = 7A1000, 2K1000, 2K0500 + */ + +/* LS2K1000 */ +#define LS2K1000_PIX_PLL0_REG 0x04B0 +#define LS2K1000_PIX_PLL1_REG 0x04C0 +#define LS2K1000_CFG_REG_BASE 0x1fe10000 + +/* LS7A1000 */ +#define LS7A1000_PIX_PLL0_REG 0x04B0 +#define LS7A1000_PIX_PLL1_REG 0x04C0 +#define LS7A1000_CFG_REG_BASE 0x10010000 + +/* LS2K0500 */ +#define LS2K0500_PIX_PLL0_REG 0x0418 +#define LS2K0500_PIX_PLL1_REG 0x0420 +#define LS2K0500_CFG_REG_BASE 0x1fe10000 + +/* + * CRTC CFG REG + */ +#define CFG_PIX_FMT_MASK GENMASK(2, 0) + +enum lsdc_pixel_format { + LSDC_PF_NONE = 0, + LSDC_PF_ARGB4444 = 1, /* ARGB A:4 bits R/G/B: 4 bits each [16 bits] */ + LSDC_PF_ARGB1555 = 2, /* ARGB A:1 bit RGB:15 bits [16 bits] */ + LSDC_PF_RGB565 = 3, /* RGB [16 bits] */ + LSDC_PF_XRGB8888 = 4, /* XRGB [32 bits] */ +}; + +/* Each CRTC has two FB address registers, CFG_FB_IDX_BIT specify + * which fb address register is currently in using by the CRTC. + * Setting CFG_PAGE_FLIP_BIT bit will triger the switch. The switch + * finished at the vblank and if you want switch back you can set + * CFG_PAGE_FLIP_BIT again. + */ +#define CFG_PAGE_FLIP_BIT BIT(7) +#define CFG_OUTPUT_EN_BIT BIT(8) +/* CRTC0 clone from CRTC1 or CRTC1 clone from CRTC0 using hardware logic */ +#define CFG_PANEL_SWITCH BIT(9) +/* Indicate witch fb addr reg is in using, currently */ +#define CFG_FB_IDX_BIT BIT(11) +#define CFG_GAMMAR_EN_BIT BIT(12) + +/* CRTC get soft reset if voltage level change from 1 -> 0 */ +#define CFG_RESET_BIT BIT(20) + +#define EN_HSYNC_BIT BIT(30) +#define INV_HSYNC_BIT BIT(31) +#define EN_VSYNC_BIT BIT(30) +#define INV_VSYNC_BIT BIT(31) + +/******** CRTC0 & DVO0 ********/ +#define LSDC_CRTC0_CFG_REG 0x1240 +#define LSDC_CRTC0_FB_ADDR0_REG 0x1260 +#define LSDC_CRTC0_FB_ADDR1_REG 0x1580 +#define LSDC_CRTC0_FB_HI_ADDR_REG 0x15A0 +#define LSDC_CRTC0_STRIDE_REG 0x1280 +#define LSDC_CRTC0_FB_ORIGIN_REG 0x1300 +#define LSDC_CRTC0_HDISPLAY_REG 0x1400 +#define LSDC_CRTC0_HSYNC_REG 0x1420 +#define LSDC_CRTC0_VDISPLAY_REG 0x1480 +#define LSDC_CRTC0_VSYNC_REG 0x14A0 +#define LSDC_CRTC0_GAMMA_INDEX_REG 0x14E0 +#define LSDC_CRTC0_GAMMA_DATA_REG 0x1500 + +/******** CTRC1 & DVO1 ********/ +#define LSDC_CRTC1_CFG_REG 0x1250 +#define LSDC_CRTC1_FB_ADDR0_REG 0x1270 +#define LSDC_CRTC1_FB_ADDR1_REG 0x1590 +#define LSDC_CRTC1_FB_HI_ADDR_REG 0x15C0 +#define LSDC_CRTC1_STRIDE_REG 0x1290 +#define LSDC_CRTC1_FB_ORIGIN_REG 0x1310 +#define LSDC_CRTC1_HDISPLAY_REG 0x1410 +#define LSDC_CRTC1_HSYNC_REG 0x1430 +#define LSDC_CRTC1_VDISPLAY_REG 0x1490 +#define LSDC_CRTC1_VSYNC_REG 0x14B0 +#define LSDC_CRTC1_GAMMA_INDEX_REG 0x14F0 +#define LSDC_CRTC1_GAMMA_DATA_REG 0x1510 + +#define LSDC_REGS_OFFSET 0x0010 + +/* + * Hardware cursor + * There is only one hardware cursor shared by two CRTC in ls7a1000, + * ls2k1000 and ls2k0500. + */ +#define LSDC_CURSOR0_CFG_REG 0x1520 +#define LSDC_CURSOR0_ADDR_REG 0x1530 +#define LSDC_CURSOR0_POSITION_REG 0x1540 +#define LSDC_CURSOR0_BG_COLOR_REG 0x1550 /* background color */ +#define LSDC_CURSOR0_FG_COLOR_REG 0x1560 /* foreground color */ + +#define LSDC_CURS_MIN_SIZE 1 +#define LSDC_CURS_MAX_SIZE 64 +#define CURSOR_FORMAT_MASK GENMASK(1, 0) +#define CURSOR_FORMAT_DISABLE 0 +#define CURSOR_FORMAT_MONOCHROME 1 +#define CURSOR_FORMAT_ARGB8888 2 +#define CURSOR_SIZE_64X64 BIT(2) +#define CURSOR_LOCATION_BIT BIT(4) + +/* LS7A2000 have two hardware cursor */ + +#define LSDC_CURSOR1_CFG_REG 0x1670 +#define LSDC_CURSOR1_ADDR_REG 0x1680 +#define LSDC_CURSOR1_POSITION_REG 0x1690 +#define LSDC_CURSOR1_BG_COLOR_REG 0x16A0 /* background color */ +#define LSDC_CURSOR1_FG_COLOR_REG 0x16B0 /* foreground color */ + +/* + * DC Interrupt Control Register, 32bit, Address Offset: 1570 + * + * Bits 0:10 inidicate the interrupt type, read only + * Bits 16:26 control if the specific interrupt corresponding to bit 0~10 + * is enabled or not. Write 1 to enable, write 0 to disable + * + * RF: Read Finished + * IDBU : Internal Data Buffer Underflow + * IDBFU : Internal Data Buffer Fatal Underflow + * + * + * +-------+-------------------------------+-------+--------+--------+-------+ + * | 31:27 | 26:16 | 15:11 | 10 | 9 | 8 | + * +-------+-------------------------------+-------+--------+--------+-------+ + * | N/A | Interrupt Enable Control Bits | N/A | IDBFU0 | IDBFU1 | IDBU0 | + * +-------+-------------------------------+-------+--------+--------+-------+ + * + * Bit 4 is cursor buffer read finished, no use. + * + * +-------+-----+-----+-----+--------+--------+--------+--------+ + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * +-------+-----+-----+-----+--------+--------+--------+--------+ + * | IDBU1 | RF0 | RF1 | | HSYNC0 | VSYNC0 | HSYNC1 | VSYNC1 | + * +-------+-----+-----+-----+--------+--------+--------+--------+ + * + */ + +#define LSDC_INT_REG 0x1570 + +#define INT_CRTC0_VS BIT(2) +#define INT_CRTC0_HS BIT(3) +#define INT_CRTC0_RF BIT(6) +#define INT_CRTC0_IDBU BIT(8) +#define INT_CRTC0_IDBFU BIT(10) + +#define INT_CURSOR_RF BIT(4) + +#define INT_CRTC1_VS BIT(0) +#define INT_CRTC1_HS BIT(1) +#define INT_CRTC1_RF BIT(5) +#define INT_CRTC1_IDBU BIT(7) +#define INT_CRTC1_IDBFU BIT(9) + +#define INT_CRTC0_VS_EN BIT(18) +#define INT_CRTC0_HS_EN BIT(19) +#define INT_CRTC0_RF_EN BIT(22) +#define INT_CRTC0_IDBU_EN BIT(24) +#define INT_CRTC0_IDBFU_EN BIT(26) + +#define INT_CURSOR_RF_EN BIT(20) + +#define INT_CRTC1_VS_EN BIT(16) +#define INT_CRTC1_HS_EN BIT(17) +#define INT_CRTC1_RF_EN BIT(21) +#define INT_CRTC1_IDBU_EN BIT(23) +#define INT_CRTC1_IDBFU_EN BIT(25) + +#define INT_STATUS_MASK GENMASK(10, 0) + +/* + * LS7A1000 have 4 gpios which is under control of the LS7A_DC_GPIO_DAT_REG + * and LS7A_DC_GPIO_DIR_REG register, it has no relationship whth the general + * GPIO hardware. Those registers are in the DC register space on LS7A1000. + * + * Those GPIOs are used to emulated I2C, for reading edid and monitor detection + * + * LS2k1000 and LS2K0500 don't have those registers, they use hardware i2c or + * generial GPIO emulated i2c from other module. + * + * GPIO data register + * Address offset: 0x1650 + * +---------------+-----------+-----------+ + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + * +---------------+-----------+-----------+ + * | | DVO1 | DVO0 | + * + N/A +-----------+-----------+ + * | | SCL | SDA | SCL | SDA | + * +---------------+-----------+-----------+ + */ +#define LS7A_DC_GPIO_DAT_REG 0x1650 + +/* + * GPIO Input/Output direction control register + * Address offset: 0x1660 + * write 1 for Input, 0 for Output. + */ +#define LS7A_DC_GPIO_DIR_REG 0x1660 + +/* + * LS7A2000 Built-in HDMI Encoder + */ +#define HDMI_EN BIT(0) +#define HDMI_PACKET_EN BIT(1) + +#define HDMI0_ZONE_REG 0x1700 +#define HDMI1_ZONE_REG 0x1710 + +#define HDMI0_CTRL_REG 0x1720 +#define HDMI1_CTRL_REG 0x1730 + +#define HDMI_PLL_EN BIT(0) +#define HDMI_PLL_LOCKED BIT(16) + +#define HDMI0_PHY_CTRL_REG 0x1800 +#define HDMI0_PLL_REG 0x1820 + +#define HDMI1_PHY_CTRL_REG 0x1810 +#define HDMI1_PLL_REG 0x1830 + +#define LS7A2000_DMA_STEP_MASK GENMASK(17, 16) +#define DMA_STEP_256_BYTE (0 << 16) +#define DMA_STEP_128_BYTE (1 << 16) +#define DMA_STEP_64_BYTE (2 << 16) +#define DMA_STEP_32_BYTE (3 << 16) + +#endif -- Gitee From 920fc93c62def3287a92b66c375050e63879d281 Mon Sep 17 00:00:00 2001 From: Hongchen Zhang Date: Sun, 29 Jan 2023 17:43:12 +0800 Subject: [PATCH 56/56] pci: fix kabi error caused by pm_suspend_target_state fix kabi error caused by pm_suspend_target_state,used only by loongson devices. Signed-off-by: Hongchen Zhang --- drivers/pci/pci.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 99eeafead49e..ff973a8d147a 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -31,7 +31,9 @@ #include #include #include +#ifdef CONFIG_MACH_LOONGSON64 #include +#endif #include "pci.h" DEFINE_MUTEX(pci_slot_mutex); @@ -5756,8 +5758,9 @@ int pcie_set_readrq(struct pci_dev *dev, int rq) { u16 v; int ret; +#ifdef CONFIG_MACH_LOONGSON64 struct pci_host_bridge *bridge = pci_find_host_bridge(dev->bus); - +#endif if (rq < 128 || rq > 4096 || !is_power_of_2(rq)) return -EINVAL; @@ -5774,12 +5777,13 @@ int pcie_set_readrq(struct pci_dev *dev, int rq) } v = (ffs(rq) - 8) << 12; - +#ifdef CONFIG_MACH_LOONGSON64 if (pm_suspend_target_state == PM_SUSPEND_ON && bridge->no_inc_mrrs) { if (rq > pcie_get_readrq(dev)) return -EINVAL; } +#endif ret = pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_READRQ, v); -- Gitee