From stanley_chang at realtek.com  Thu Sep 17 04:38:25 2020
From: stanley_chang at realtek.com (stanley_chang)
Date: Thu, 17 Sep 2020 16:38:25 +0800
Subject: [PATCH 00/10] Realtek DHC SoCs USB module driver
Message-ID: <20200917083834.20782-1-stanley_chang@realtek.com>

Hi Andreas,

This series add the usb host driver for Realtek SoCs embed DWC3 USB core IP.
It supports DHC SoC series included 129x, 139x, 16xx, 13xx.

Note:
In these patches, we focus on the porting of the host driver.
The realtek demo boards must enable reset/clock and enable the GPIO of
USB power control to let USB host/device can work. For these condition,
you can set register of reset/clock and GPIO by default enable.

Thanks,
Stanley

stanley_chang (10):
  usb: dwc3: add an option to adjust the dwc3 global register start
    offset
  doc: dt: bindings: usb: dwc3: add an option to adjust the dwc3 global
    register start offset
  usb: dwc3: rtk: Add new realtek soc dwc3 controller glue driver
  doc: dt: bindings: usb: dwc3-rtk: Add the doc about the new dwc3-rtk
    driver
  usb: phy: rtk: add the new realtek usb2 phy and usb3 phy drivers
  doc: dt: bindings: phy: rtk: Add the doc about new rtk usb phy driver
  arm64: dts: realtek: rtd129x-usb: Add rtd129x usb module nodes
  arm64: dts: realtek: rtd139x-usb: Add rtd139x usb module nodes
  arm64: dts: realtek: rtd16xx-usb: Add rtd16xx usb module nodes
  arm64: dts: realtek: rtd13xx-usb: Add rtd13xx usb module nodes

 .../devicetree/bindings/phy/phy-rtk-usb2.yaml |   73 ++
 .../devicetree/bindings/phy/phy-rtk-usb3.yaml |   76 ++
 .../devicetree/bindings/usb/dwc3-rtk.yaml     |   61 +
 .../devicetree/bindings/usb/dwc3.txt          |    1 +
 arch/arm64/boot/dts/realtek/rtd129x-usb.dtsi  |  203 +++
 arch/arm64/boot/dts/realtek/rtd139x-usb.dtsi  |  120 ++
 arch/arm64/boot/dts/realtek/rtd13xx-usb.dtsi  |  224 ++++
 arch/arm64/boot/dts/realtek/rtd16xx-usb.dtsi  |  226 ++++
 drivers/usb/dwc3/Kconfig                      |   10 +
 drivers/usb/dwc3/Makefile                     |    1 +
 drivers/usb/dwc3/core.c                       |   13 +
 drivers/usb/dwc3/core.h                       |    5 +-
 drivers/usb/dwc3/dwc3-rtk-debugfs.c           |  501 ++++++++
 drivers/usb/dwc3/dwc3-rtk.c                   |  336 +++++
 drivers/usb/dwc3/dwc3-rtk.h                   |   45 +
 drivers/usb/phy/Kconfig                       |   12 +
 drivers/usb/phy/Makefile                      |    2 +
 drivers/usb/phy/phy-rtk-usb.h                 |   53 +
 drivers/usb/phy/phy-rtk-usb2.c                | 1121 +++++++++++++++++
 drivers/usb/phy/phy-rtk-usb3.c                |  635 ++++++++++
 20 files changed, 3717 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/phy/phy-rtk-usb2.yaml
 create mode 100644 Documentation/devicetree/bindings/phy/phy-rtk-usb3.yaml
 create mode 100644 Documentation/devicetree/bindings/usb/dwc3-rtk.yaml
 create mode 100644 arch/arm64/boot/dts/realtek/rtd129x-usb.dtsi
 create mode 100644 arch/arm64/boot/dts/realtek/rtd139x-usb.dtsi
 create mode 100644 arch/arm64/boot/dts/realtek/rtd13xx-usb.dtsi
 create mode 100644 arch/arm64/boot/dts/realtek/rtd16xx-usb.dtsi
 create mode 100644 drivers/usb/dwc3/dwc3-rtk-debugfs.c
 create mode 100644 drivers/usb/dwc3/dwc3-rtk.c
 create mode 100644 drivers/usb/dwc3/dwc3-rtk.h
 create mode 100644 drivers/usb/phy/phy-rtk-usb.h
 create mode 100644 drivers/usb/phy/phy-rtk-usb2.c
 create mode 100644 drivers/usb/phy/phy-rtk-usb3.c

-- 
2.28.0



From stanley_chang at realtek.com  Thu Sep 17 04:38:31 2020
From: stanley_chang at realtek.com (stanley_chang)
Date: Thu, 17 Sep 2020 16:38:31 +0800
Subject: [PATCH 06/10] doc: dt: bindings: phy: rtk: Add the doc about new rtk
 usb phy driver
In-Reply-To: <20200917083834.20782-1-stanley_chang@realtek.com>
References: <20200917083834.20782-1-stanley_chang@realtek.com>
Message-ID: <20200917083834.20782-7-stanley_chang@realtek.com>

Two documentations explain the property about realtek USB2 and USB3 phy drivers.

Signed-off-by: stanley_chang <stanley_chang at realtek.com>
---
 .../devicetree/bindings/phy/phy-rtk-usb2.yaml | 73 ++++++++++++++++++
 .../devicetree/bindings/phy/phy-rtk-usb3.yaml | 76 +++++++++++++++++++
 2 files changed, 149 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/phy-rtk-usb2.yaml
 create mode 100644 Documentation/devicetree/bindings/phy/phy-rtk-usb3.yaml

diff --git a/Documentation/devicetree/bindings/phy/phy-rtk-usb2.yaml b/Documentation/devicetree/bindings/phy/phy-rtk-usb2.yaml
new file mode 100644
index 000000000000..49382c9587b2
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/phy-rtk-usb2.yaml
@@ -0,0 +1,73 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright 2020 Realtek Semiconductor Corporation
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/phy/phy-rtk-usb2.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Realtek DHC SoCs USB2 PHY
+
+maintainers:
+  - Stanley Chang <stanley_chang at realtek.com>
+
+properties:
+  compatible:
+    enum:
+      - realtek,usb2phy
+
+  reg:
+    maxItems: 2
+
+  phyN:
+    description:
+        The number of USB 2.0 PHY  in controller.
+
+  phy0:
+     description:
+       The node of PHY parameter.
+
+required:
+  - compatible
+  - reg
+  - phyN
+  - phy0
+
+examples:
+  - |
+    dwc3_u3drd_usb2phy: dwc3_u3drd_usb2phy at 98013E14 {
+        compatible = "realtek,usb2phy";
+        reg = <0x98013E14 0x4>, <0x98058280 0x4>;
+        #address-cells = <1>;
+        #size-cells = <1>;
+        ranges;
+        status = "okay";
+        phyN = <1>;
+
+        phy0 {
+            phy_data_page0_size = <16>;
+            phy_data_page0_addr = /bits/ 8
+                <0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xF0 0xF1
+                0xF2 0xF3 0xF4 0xF5 0xF6 0xF7>;
+            phy_data_page0_A00 = /bits/ 8
+                <0xE0 0x30 0x79 0x8D 0x6A 0x65 0x01 0x71 0xFC 0x8C
+                0x00 0x11 0x9B 0x00 0x00 0x0A>;
+            phy_data_page0_B00 = /bits/ 8
+                <0x18 0x30 0x79 0x8D 0x6A 0x65 0x01 0x71 0xFC 0x8C
+                0x00 0x11 0x9B 0x00 0x00 0x32>;
+            phy_data_page1_size = <8>;
+            phy_data_page1_addr = /bits/ 8
+                <0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7>;
+            phy_data_page1_A00 = /bits/ 8
+                <0x25 0xEF 0x60 0x44 0x00 0x0F 0x18 0xE3>;
+            phy_data_page2_size = <1>;
+            phy_data_page2_addr = /bits/ 8
+                <0xE0>;
+            phy_data_page2_A00 = /bits/ 8
+                <0x01>;
+            do_toggle;
+            check_efuse;
+            //use_default_parameter;
+            is_double_sensitivity_mode;
+            ldo_page0_e4_compensate = <(-2)>;
+        };
+    };
diff --git a/Documentation/devicetree/bindings/phy/phy-rtk-usb3.yaml b/Documentation/devicetree/bindings/phy/phy-rtk-usb3.yaml
new file mode 100644
index 000000000000..3ff6449b5d2f
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/phy-rtk-usb3.yaml
@@ -0,0 +1,76 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright 2020 Realtek Semiconductor Corporation
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/phy/phy-rtk-usb2.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Realtek DHC SoCs USB3 PHY
+
+maintainers:
+  - Stanley Chang <stanley_chang at realtek.com>
+
+properties:
+  compatible:
+    enum:
+      - realtek,usb3phy
+
+  reg:
+    maxItems: 1
+
+  phyN:
+    description:
+        The number of USB 3.0 PHY  in controller.
+
+  phy0:
+     description:
+       The node of PHY parameter.
+
+required:
+  - compatible
+  - reg
+  - phyN
+  - phy0
+
+examples:
+  - |
+    dwc3_u3drd_usb3phy: dwc3_u3drd_usb3phy at 98013E10 {
+        compatible = "realtek,usb3phy";
+        reg = <0x98013E10 0x4>;
+        #address-cells = <1>;
+        #size-cells = <1>;
+        ranges;
+        status = "okay";
+        port_index = <0>; /* index in u3 port */
+        phyN = <1>;
+
+        phy0 {
+            phy_data_size = <0x30>;
+            phy_data_addr = /bits/ 8
+                <0x00 0x01 0x02 0x03 0x04 0x05 0x06
+                0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D
+                0x0E 0x0F 0x10 0x11 0x12 0x13 0x14
+                0x15 0x16 0x17 0x18 0x19 0x1A 0x1B
+                0x1C 0x1D 0x1E 0x1F 0x20 0x21 0x22
+                0x23 0x24 0x25 0x26 0x27 0x28 0x29
+                0x2A 0x2B 0x2C 0x2D 0x2E 0x2F>;
+            phy_data_A00 = /bits/ 16
+                <0x400C 0xAC86 0x6042 0x2771 0x72F5 0x2AD3 0x0003
+                    0x2E00 0x3591 0x925C 0xA608 0xA905 0xC000 0xEF1E
+                    0x2010 0x8D50 0x000C 0x4C10 0xFC00 0x0C81 0xDE01
+                    0x0000 0x0000 0x0000 0x0000 0x6000 0x0085 0x2014
+                    0xC900 0xA03F 0xC2E0 0x7E00 0x705A 0xF645 0x0013
+                    0xCB66 0x4770 0x126C 0x840A 0x01D6 0xF802 0xff04
+                    0x3040 0x8028 0xFFFF 0xFFFF 0x0000 0x8600>;
+            phy_data_B00 = /bits/ 16
+                <0x400C 0xAC86 0x6042 0x2771 0x72F5 0x2AD3 0x0003
+                    0x2E00 0x3591 0x924C 0xA608 0xB905 0xC000 0xEF1E
+                    0x2010 0x8D50 0x000C 0x4C10 0xFC00 0x0C81 0xDE01
+                    0x0000 0x0000 0x0000 0x0000 0x6000 0x0085 0x2014
+                    0xC900 0xA03F 0xC2E0 0x7E00 0x705A 0xF645 0x0013
+                    0xCB66 0x4770 0x126C 0x840A 0x01D6 0xF802 0xff04
+                    0x3040 0x8028 0xFFFF 0xFFFF 0x0000 0x8600>;
+            do_toggle;
+            //use_default_parameter;
+        };
+    };
-- 
2.28.0



From stanley_chang at realtek.com  Thu Sep 17 04:38:27 2020
From: stanley_chang at realtek.com (stanley_chang)
Date: Thu, 17 Sep 2020 16:38:27 +0800
Subject: [PATCH 02/10] doc: dt: bindings: usb: dwc3: add an option to adjust
 the dwc3 global register start offset
In-Reply-To: <20200917083834.20782-1-stanley_chang@realtek.com>
References: <20200917083834.20782-1-stanley_chang@realtek.com>
Message-ID: <20200917083834.20782-3-stanley_chang@realtek.com>

This patch updates the documentation with the option related to adjust global
register start offset

Signed-off-by: stanley_chang <stanley_chang at realtek.com>
---
 Documentation/devicetree/bindings/usb/dwc3.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt
index 66780a47ad85..8e63a0047900 100644
--- a/Documentation/devicetree/bindings/usb/dwc3.txt
+++ b/Documentation/devicetree/bindings/usb/dwc3.txt
@@ -108,6 +108,7 @@ Optional properties:
 			When just one value, which means INCRX burst mode enabled. When
 			more than one value, which means undefined length INCR burst type
 			enabled. The values can be 1, 4, 8, 16, 32, 64, 128 and 256.
+ - snps,fixed_dwc3_globals_regs_start: value for fix the global registers start offset.
 
  - in addition all properties from usb-xhci.txt from the current directory are
    supported as well
-- 
2.28.0



From stanley_chang at realtek.com  Thu Sep 17 04:38:26 2020
From: stanley_chang at realtek.com (stanley_chang)
Date: Thu, 17 Sep 2020 16:38:26 +0800
Subject: [PATCH 01/10] usb: dwc3: add an option to adjust the dwc3 global
 register start offset
In-Reply-To: <20200917083834.20782-1-stanley_chang@realtek.com>
References: <20200917083834.20782-1-stanley_chang@realtek.com>
Message-ID: <20200917083834.20782-2-stanley_chang@realtek.com>

In general, the dwc3 usb controller designs the global register start offset
at 0xc100. The dwc3 global register start offset of Realtek SoC is at 0x8100.
Therefore, we add a property to adjust this offset in dts.

Signed-off-by: stanley_chang <stanley_chang at realtek.com>
---
 drivers/usb/dwc3/core.c | 13 +++++++++++++
 drivers/usb/dwc3/core.h |  5 ++++-
 2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index f561c6c9e8a9..e842e926b535 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1464,6 +1464,19 @@ static int dwc3_probe(struct platform_device *pdev)
 	dwc_res = *res;
 	dwc_res.start += DWC3_GLOBALS_REGS_START;
 
+	/* For some dwc3 controller, the dwc3 global register start address is
+	 * not at DWC3_GLOBALS_REGS_START (0xc100).
+	 */
+	device_property_read_u32(dev, "snps,fixed_dwc3_globals_regs_start",
+				 &dwc->fixed_dwc3_globals_regs_start);
+	if (dwc->fixed_dwc3_globals_regs_start) {
+		dwc_res.start -= DWC3_GLOBALS_REGS_START;
+		dwc_res.start += dwc->fixed_dwc3_globals_regs_start;
+		dev_info(dev,
+		    "fixed dwc3 globals register start address from 0x%llx to end 0x%llx\n",
+		    dwc_res.start, dwc_res.end);
+	}
+
 	regs = devm_ioremap_resource(dev, &dwc_res);
 	if (IS_ERR(regs))
 		return PTR_ERR(regs);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 1c8b349379af..6b9eb777085f 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1031,7 +1031,8 @@ struct dwc3_scratchpad_array {
  * @dis_metastability_quirk: set to disable metastability quirk.
  * @imod_interval: set the interrupt moderation interval in 250ns
  *                 increments or 0 to disable.
- */
+ * @fixed_dwc3_globals_regs_start: fix the dwc3 global register start address.
+*/
 struct dwc3 {
 	struct work_struct	drd_work;
 	struct dwc3_trb		*ep0_trb;
@@ -1220,6 +1221,8 @@ struct dwc3 {
 	unsigned		dis_metastability_quirk:1;
 
 	u16			imod_interval;
+
+	u32			fixed_dwc3_globals_regs_start;
 };
 
 #define INCRX_BURST_MODE 0
-- 
2.28.0



From stanley_chang at realtek.com  Thu Sep 17 04:38:33 2020
From: stanley_chang at realtek.com (stanley_chang)
Date: Thu, 17 Sep 2020 16:38:33 +0800
Subject: [PATCH 08/10] arm64: dts: realtek: rtd139x-usb: Add rtd139x usb
 module nodes
In-Reply-To: <20200917083834.20782-1-stanley_chang@realtek.com>
References: <20200917083834.20782-1-stanley_chang@realtek.com>
Message-ID: <20200917083834.20782-9-stanley_chang@realtek.com>

Signed-off-by: stanley_chang <stanley_chang at realtek.com>
---
 arch/arm64/boot/dts/realtek/rtd139x-usb.dtsi | 120 +++++++++++++++++++
 1 file changed, 120 insertions(+)
 create mode 100644 arch/arm64/boot/dts/realtek/rtd139x-usb.dtsi

diff --git a/arch/arm64/boot/dts/realtek/rtd139x-usb.dtsi b/arch/arm64/boot/dts/realtek/rtd139x-usb.dtsi
new file mode 100644
index 000000000000..a98cf134aa52
--- /dev/null
+++ b/arch/arm64/boot/dts/realtek/rtd139x-usb.dtsi
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Realtek RTD139x SoC USB
+ *
+ * Copyright (c) 2020 Realtek Semiconductor Corp.
+ */
+
+/ {
+	dwc3_drd_usb2phy: dwc3_drd_usb2phy at 98013214 {
+		compatible = "realtek,usb2phy";
+		reg = <0x98013214 0x4>, <0x98028280 0x4>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		phyN = <1>;
+		phy0 {
+			phy_data_page0_size = <16>;
+			phy_data_page0_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xF0 0xF1
+				0xF2 0xF3 0xF4 0xF5 0xF6 0xF7>;
+			phy_data_page0_data = /bits/ 8
+				<0xE0 0x30 0x79 0x8D 0xAC 0x65 0x01 0x81 0xFC 0x8C
+				0x00 0x11 0x9B 0x00 0x00 0x02>;
+			phy_data_page1_size = <8>;
+			phy_data_page1_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7>;
+			phy_data_page1_data = /bits/ 8
+				<0x25 0xEF 0x60 0x00 0x00 0x0F 0x18 0xE3>;
+			do_toggle;
+			check_efuse;
+			//use_default_parameter;
+		};
+	};
+
+	dwc3_drd: rtk_dwc3_drd at 98013200 {
+		compatible = "realtek,dwc3";
+		reg = <0x98013200 0x200>;
+		interrupts = <0 21 4>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		delay_probe_work; //To delay probe work
+		status = "okay";
+
+		dwc3_drd at 98020000 {
+			compatible = "synopsys,dwc3";
+			reg = <0x98020000 0x9000>;
+			interrupts = <0 21 4>;
+			snps,fixed_dwc3_globals_regs_start = <0x8100>;
+			usb-phy = <&dwc3_drd_usb2phy>;
+			dr_mode = "host"; /*otg, host, peripheral*/
+			snps,dis_u2_susphy_quirk; // Add workaround for Usb3.0 hub suspend
+		};
+	};
+
+	dwc3_u2host_usb2phy: dwc3_u2host_usb2phy at 98013C14 {
+		compatible = "realtek,usb2phy";
+		reg = <0x98013C14 0x4>, <0x98031280 0x4>, <0x98031284 0x4>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		phyN = <2>;
+		phy0 {
+			phy_data_page0_size = <16>;
+			phy_data_page0_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xF0 0xF1
+				0xF2 0xF3 0xF4 0xF5 0xF6 0xF7>;
+			phy_data_page0_data = /bits/ 8
+				<0xE0 0x30 0x79 0x8D 0xAC 0x65 0x01 0x81 0xFC 0x8C
+				0x00 0x11 0x9B 0x00 0x00 0x02>;
+			phy_data_page1_size = <8>;
+			phy_data_page1_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7>;
+			phy_data_page1_data = /bits/ 8
+				<0x25 0xEF 0x60 0x00 0x00 0x0F 0x18 0xE3>;
+			do_toggle;
+			check_efuse;
+			//use_default_parameter;
+		};
+		phy1 {
+			phy_data_page0_size = <16>;
+			phy_data_page0_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xF0 0xF1
+				0xF2 0xF3 0xF4 0xF5 0xF6 0xF7>;
+			phy_data_page0_data = /bits/ 8
+				<0xE0 0x30 0x79 0x8D 0xAC 0x65 0x01 0x81 0xFC 0x8C
+				0x00 0x11 0x9B 0x00 0x00 0x02>;
+			phy_data_page1_size = <8>;
+			phy_data_page1_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7>;
+			phy_data_page1_data = /bits/ 8
+				<0x25 0xEF 0x60 0x00 0x00 0x0F 0x18 0xE3>;
+			do_toggle;
+			check_efuse;
+			//use_default_parameter;
+		};
+	};
+
+	dwc3_u2host: rtk_dwc3_u2host at 98013E00 {
+		compatible = "realtek,dwc3";
+		reg = <0x98013C00 0x200>;
+		interrupts = <0 21 4>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		delay_probe_work; //To delay probe work
+		status = "okay";
+
+		dwc3_u2host at 98029000 {
+			compatible = "synopsys,dwc3";
+			reg = <0x98029000 0x9000>;
+			interrupts = <0 21 4>;
+			snps,fixed_dwc3_globals_regs_start = <0x8100>;
+			usb-phy = <&dwc3_u2host_usb2phy>;
+			dr_mode = "host"; /*only host*/
+			snps,dis_u2_susphy_quirk; // Add workaround for Usb3.0 hub suspend
+		};
+	};
+
+};
-- 
2.28.0



From stanley_chang at realtek.com  Thu Sep 17 04:38:32 2020
From: stanley_chang at realtek.com (stanley_chang)
Date: Thu, 17 Sep 2020 16:38:32 +0800
Subject: [PATCH 07/10] arm64: dts: realtek: rtd129x-usb: Add rtd129x usb
 module nodes
In-Reply-To: <20200917083834.20782-1-stanley_chang@realtek.com>
References: <20200917083834.20782-1-stanley_chang@realtek.com>
Message-ID: <20200917083834.20782-8-stanley_chang@realtek.com>

Signed-off-by: stanley_chang <stanley_chang at realtek.com>
---
 arch/arm64/boot/dts/realtek/rtd129x-usb.dtsi | 203 +++++++++++++++++++
 1 file changed, 203 insertions(+)
 create mode 100644 arch/arm64/boot/dts/realtek/rtd129x-usb.dtsi

diff --git a/arch/arm64/boot/dts/realtek/rtd129x-usb.dtsi b/arch/arm64/boot/dts/realtek/rtd129x-usb.dtsi
new file mode 100644
index 000000000000..82537da8f4fb
--- /dev/null
+++ b/arch/arm64/boot/dts/realtek/rtd129x-usb.dtsi
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Realtek RTD129x SoC USB
+ *
+ * Copyright (c) 2020 Realtek Semiconductor Corp.
+ */
+
+/ {
+	dwc3_drd_usb3phy: dwc3_drd_usb3phy at 98013210 {
+		compatible = "realtek,usb3phy";
+		reg = <0x98013210 0x4>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		phyN = <1>;
+		phy0 {
+			phy_data_size = <0x30>;
+			phy_data_addr = /bits/ 8
+				<0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09
+				0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13
+				0x14 0x15 0x16 0x17 0x18 0x19 0x1A 0x1B 0x1C 0x1D
+				0x1E 0x1F 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27
+				0x28 0x29 0x2A 0x2B 0x2C 0x2D 0x2E 0x2F>;
+			phy_data_revA = /bits/ 16
+				<0x4008 0xE046 0x6046 0x2779 0x72F5 0x2AD3 0x000E
+				0x2E00 0x3591 0x525C 0xA600 0xA904 0xC000 0xEF1C
+				0x2000 0x0000 0x000C 0x4C00 0xFC00 0x0C81 0xDE01
+				0x0000 0x0000 0x0000 0x0000 0x4004 0x1260 0xFF00
+				0xCB00 0xA03F 0xC2E0 0x2807 0x947A 0x88AA 0x0057
+				0xAB66 0x0800 0x0000 0x040A 0x01D6 0xF8C2 0x3080
+				0x3082 0x2078 0xFFFF 0xFFFF 0x0000 0x0040>;
+			do_toggle;
+		};
+	};
+
+	dwc3_drd_usb2phy: dwc3_drd_usb2phy at 98013214 {
+		compatible = "realtek,usb2phy";
+		reg = <0x98013214 0x4>, <0x98028280 0x4>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		phyN = <1>;
+		phy0 {
+			phy_data_page0_size = <16>;
+			phy_data_page0_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xF0 0xF1
+				0xF2 0xF3 0xF4 0xF5 0xF6 0xF7>;
+			phy_data_page0_data = /bits/ 8
+				<0x90 0x30 0x3A 0x8D 0xA8 0x65 0x91 0x81 0xFC 0x8C
+				0x00 0x11 0x9B 0x81 0x00 0x02>;
+			phy_data_page1_size = <8>;
+			phy_data_page1_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7>;
+			phy_data_page1_data = /bits/ 8
+				<0x25 0xEF 0x60 0x00 0x00 0x0F 0x18 0xE3>;
+			do_toggle;
+			//check_efuse;
+		};
+	};
+
+	dwc3_drd: rtk_dwc3_drd at 98013200 {
+		compatible = "realtek,dwc3";
+		reg = <0x98013200 0x200>;
+		interrupts = <0 21 4>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		delay_probe_work; //To delay probe work
+
+		dwc3_drd at 98020000 {
+			compatible = "synopsys,dwc3";
+			reg = <0x98020000 0x9000>;
+			interrupts = <0 21 4>;
+			snps,fixed_dwc3_globals_regs_start = <0x8100>;
+			usb-phy = <&dwc3_drd_usb2phy &dwc3_drd_usb3phy>;
+			dr_mode = "host"; /*otg, host, peripheral*/
+			snps,dis_u2_susphy_quirk; // Add workaround for Usb3.0 hub suspend
+			snps,parkmode-disable-ss-quirk; // disable usb3.0 park mode
+		};
+	};
+
+	dwc3_u2host_usb2phy: dwc3_u2host_usb2phy at 98013C14 {
+		compatible = "realtek,usb2phy";
+		reg = <0x98013C14 0x4>, <0x98031280 0x4>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		phyN = <1>;
+		phy0 {
+			phy_data_page0_size = <16>;
+			phy_data_page0_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xF0 0xF1
+				0xF2 0xF3 0xF4 0xF5 0xF6 0xF7>;
+			phy_data_page0_data = /bits/ 8
+				<0x90 0x30 0x3A 0x8D 0xA6 0x65 0x91 0x81 0xFC 0x8C
+				0x00 0x11 0x9B 0x81 0x00 0x02>;
+			phy_data_page1_size = <8>;
+			phy_data_page1_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7>;
+			phy_data_page1_data = /bits/ 8
+				<0x25 0xEF 0x60 0x00 0x00 0x0F 0x18 0xE3>;
+			do_toggle;
+			//check_efuse;
+		};
+	};
+
+	dwc3_u2host: rtk_dwc3_u2host at 98013E00 {
+		compatible = "realtek,dwc3";
+		reg = <0x98013C00 0x200>;
+		interrupts = <0 21 4>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		delay_probe_work; //To delay probe work
+
+		dwc3_u2host at 98029000 {
+			compatible = "synopsys,dwc3";
+			reg = <0x98029000 0x9000>;
+			interrupts = <0 21 4>;
+			snps,fixed_dwc3_globals_regs_start = <0x8100>;
+			usb-phy = <&dwc3_u2host_usb2phy>;
+			dr_mode = "host"; /*only host*/
+			snps,dis_u2_susphy_quirk; // Add workaround for Usb3.0 hub suspend
+		};
+	};
+
+	dwc3_u3host_usb3phy: dwc3_u3host_usb3phy at 98013E10 {
+		compatible = "realtek,usb3phy";
+		reg = <0x98013E10 0x4>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		status = "disable";
+		phyN = <1>;
+		phy0 {
+			phy_data_size = <0x30>;
+			phy_data_addr = /bits/ 8
+				<0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09
+				0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12 0x13
+				0x14 0x15 0x16 0x17 0x18 0x19 0x1A 0x1B 0x1C 0x1D
+				0x1E 0x1F 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27
+				0x28 0x29 0x2A 0x2B 0x2C 0x2D 0x2E 0x2F>;
+			phy_data_revA = /bits/ 16
+				<0x4008 0xE046 0x6046 0x2779 0x72F5 0x2AD3 0x000E
+				0x2E00 0x3591 0x525C 0xA600 0xA904 0xC000 0xEF1C
+				0x2000 0x0000 0x000C 0x4C00 0xFC00 0x0C81 0xDE01
+				0x0000 0x0000 0x0000 0x0000 0x4004 0x1260 0xFF00
+				0xCB00 0xA03F 0xC2E0 0x2807 0x9424 0x284A 0x0057
+				0xAB66 0x0800 0x0000 0x040A 0x01D6 0xF8C2 0x3080
+				0x3082 0x2078 0xFFFF 0xFFFF 0x0000 0x0040>;
+			do_toggle;
+		};
+	};
+
+	dwc3_u3host_usb2phy: dwc3_u3host_usb2phy at 98013E14 {
+		compatible = "realtek,usb2phy";
+		reg = <0x98013E14 0x4>, <0x981F8280 0x4>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		status = "disable";
+		phyN = <1>;
+		phy0 {
+			phy_data_page0_size = <16>;
+			phy_data_page0_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xF0 0xF1
+				0xF2 0xF3 0xF4 0xF5 0xF6 0xF7>;
+			phy_data_page0_data = /bits/ 8
+				<0x90 0x30 0x3A 0x8D 0xA6 0x65 0x91 0x81 0xFC 0x8C
+				0x00 0x11 0x9B 0x81 0x00 0x02>;
+			phy_data_page1_size = <8>;
+			phy_data_page1_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7>;
+			phy_data_page1_data = /bits/ 8
+				<0x25 0xEF 0x60 0x00 0x00 0x0F 0x18 0xE3>;
+			do_toggle;
+			//check_efuse;
+		};
+	};
+
+	dwc3_u3host: rtk_dwc3_u3host at 98013E00 {
+		compatible = "realtek,dwc3";
+		reg = <0x98013E00 0x200>;
+		interrupts = <0 21 4>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		delay_probe_work; //To delay probe work
+
+		status = "disable";
+
+		dwc3_u3host at 981F0000 {
+			compatible = "synopsys,dwc3";
+			reg = <0x981F0000 0x9000>;
+			interrupts = <0 21 4>;
+			snps,fixed_dwc3_globals_regs_start = <0x8100>;
+			usb-phy = <&dwc3_u3host_usb2phy &dwc3_u3host_usb3phy>;
+			dr_mode = "host"; /*only host*/
+			snps,dis_u2_susphy_quirk; // Add workaround for Usb3.0 hub suspend
+			snps,parkmode-disable-ss-quirk; // disable usb3.0 park mode
+		};
+	};
+};
-- 
2.28.0



From stanley_chang at realtek.com  Thu Sep 17 04:38:29 2020
From: stanley_chang at realtek.com (stanley_chang)
Date: Thu, 17 Sep 2020 16:38:29 +0800
Subject: [PATCH 04/10] doc: dt: bindings: usb: dwc3-rtk: Add the doc about the
 new dwc3-rtk driver
In-Reply-To: <20200917083834.20782-1-stanley_chang@realtek.com>
References: <20200917083834.20782-1-stanley_chang@realtek.com>
Message-ID: <20200917083834.20782-5-stanley_chang@realtek.com>

Realtek DHC SoCs embeds a DWC3 USB IP Core. This documentation explain the
property about dwc3-rtk glue driver

Signed-off-by: stanley_chang <stanley_chang at realtek.com>
---
 .../devicetree/bindings/usb/dwc3-rtk.yaml     | 61 +++++++++++++++++++
 1 file changed, 61 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/usb/dwc3-rtk.yaml

diff --git a/Documentation/devicetree/bindings/usb/dwc3-rtk.yaml b/Documentation/devicetree/bindings/usb/dwc3-rtk.yaml
new file mode 100644
index 000000000000..ba079c17f65c
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/dwc3-rtk.yaml
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright 2020 Realtek Semiconductor Corporation
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/usb/dwc3-rtk.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Realtek DWC3 USB SoC Controller Glue
+
+maintainers:
+  - Stanley Chang <stanley_chang at realtek.com>
+
+description: |
+  The Realtek DHC Soc embeds a DWC3 USB IP Core configured for USB2 and USB3
+  in host or dual-role mode.
+
+  A glue connects the DWC3 core to an USB2 PHY and an USB3 PHY.
+
+  The USB2 PHY is phy-rtk-usb2 and USB3 PHY is phy-rtk-usb3.
+
+properties:
+  compatible:
+    enum:
+      - realtek,dwc3
+
+  ranges: true
+
+  reg:
+    maxItems: 1
+
+required:
+  - compatible
+  - "#address-cells"
+  - "#size-cells"
+  - ranges
+  - reg
+
+examples:
+  - |
+    dwc3_u3drd: rtk_dwc3_u3drd at 98013e00 {
+        compatible = "realtek,dwc3";
+        reg = <0x98013e00 0x200>;
+        #address-cells = <1>;
+        #size-cells = <1>;
+        ranges;
+        delay_probe_work; //To delay probe work
+        status = "okay";
+
+        dwc3_u3drd at 981f0000 {
+            compatible = "synopsys,dwc3";
+            reg = <0x98050000 0x9000>;
+            interrupts = <0 94 4>;
+            snps,fixed_dwc3_globals_regs_start = <0x8100>;
+            usb-phy = <&dwc3_u3drd_usb2phy &dwc3_u3drd_usb3phy>;
+            dr_mode = "host"; /*only host*/
+            //tx-fifo-resize;
+            snps,dis_u2_susphy_quirk; // Disable u2phy suspend for drd
+            snps,dis_ss_park_mode; // disable usb3.0 park mode
+
+            status = "okay";
+        };
-- 
2.28.0



From stanley_chang at realtek.com  Thu Sep 17 04:38:34 2020
From: stanley_chang at realtek.com (stanley_chang)
Date: Thu, 17 Sep 2020 16:38:34 +0800
Subject: [PATCH 09/10] arm64: dts: realtek: rtd16xx-usb: Add rtd16xx usb
 module nodes
In-Reply-To: <20200917083834.20782-1-stanley_chang@realtek.com>
References: <20200917083834.20782-1-stanley_chang@realtek.com>
Message-ID: <20200917083834.20782-10-stanley_chang@realtek.com>

Signed-off-by: stanley_chang <stanley_chang at realtek.com>
---
 arch/arm64/boot/dts/realtek/rtd16xx-usb.dtsi | 226 +++++++++++++++++++
 1 file changed, 226 insertions(+)
 create mode 100644 arch/arm64/boot/dts/realtek/rtd16xx-usb.dtsi

diff --git a/arch/arm64/boot/dts/realtek/rtd16xx-usb.dtsi b/arch/arm64/boot/dts/realtek/rtd16xx-usb.dtsi
new file mode 100644
index 000000000000..cd4e28be68a8
--- /dev/null
+++ b/arch/arm64/boot/dts/realtek/rtd16xx-usb.dtsi
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Realtek RTD16xx SoC USB
+ *
+ * Copyright (c) 2020 Realtek Semiconductor Corp.
+ */
+
+/ {
+
+	dwc3_u2drd_usb2phy: dwc3_u2drd_usb2phy at 9803214 {
+		compatible = "realtek,usb2phy";
+		reg = <0x98013214 0x4>, <0x98028280 0x4>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		status = "okay";
+		phyN = <1>;
+		phy0 {
+			phy_data_page0_size = <16>;
+			phy_data_page0_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xF0 0xF1
+				0xF2 0xF3 0xF4 0xF5 0xF6 0xF7>;
+			phy_data_page0_A00 = /bits/ 8
+				<0xE0 0x30 0x79 0x8D 0x6A 0x65 0x01 0x71 0xFC 0x8C
+				0x00 0x11 0x9B 0x00 0x00 0x0A>;
+			phy_data_page0_B00 = /bits/ 8
+				<0x18 0x30 0x79 0x8D 0x6A 0x65 0x01 0x71 0xFC 0x8C
+				0x00 0x11 0x9B 0x00 0x00 0x32>;
+			phy_data_page1_size = <8>;
+			phy_data_page1_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7>;
+			phy_data_page1_A00 = /bits/ 8
+				<0x25 0xEF 0x60 0x44 0x00 0x0F 0x18 0xE3>;
+			phy_data_page2_size = <1>;
+			phy_data_page2_addr = /bits/ 8
+				<0xE0>;
+			phy_data_page2_A00 = /bits/ 8
+				<0x01>;
+			do_toggle;
+			check_efuse;
+			//use_default_parameter;
+			is_double_sensitivity_mode;
+			ldo_page0_e4_compensate = <(-2)>;
+		};
+	};
+
+	dwc3_u2drd: rtk_dwc3_u2drd at 98013200 {
+		compatible = "realtek,dwc3";
+		reg = <0x98013200 0x200>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		delay_probe_work; //To delay probe work
+		status = "okay";
+
+		dwc3_u2drd at 98020000 {
+			compatible = "synopsys,dwc3";
+			reg = <0x98020000 0x9000>;
+			interrupts = <0 21 4>;
+			snps,fixed_dwc3_globals_regs_start = <0x8100>;
+			usb-phy = <&dwc3_u2drd_usb2phy>;
+			dr_mode = "host"; /*otg, host, peripheral*/
+			snps,dis_u2_susphy_quirk; // Disable u2phy suspend for drd
+			snps,dis-u2-freeclk-exists-quirk; // Fixed u2drd host die issue
+
+			status = "okay";
+		};
+	};
+
+	dwc3_u2host_usb2phy: dwc3_u2host_usb2phy at 98013C14 {
+		compatible = "realtek,usb2phy";
+		reg = <0x98013C14 0x4>, <0x98031280 0x4>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		status = "okay";
+		phyN = <1>;
+		phy0 {
+			phy_data_page0_size = <16>;
+			phy_data_page0_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xF0 0xF1
+				0xF2 0xF3 0xF4 0xF5 0xF6 0xF7>;
+			phy_data_page0_A00 = /bits/ 8
+				<0xE0 0x30 0x79 0x8D 0x6A 0x65 0x01 0x71 0xFC 0x8C
+				0x00 0x11 0x9B 0x00 0x00 0x0A>;
+			phy_data_page0_B00 = /bits/ 8
+				<0x18 0x30 0x79 0x8D 0x6A 0x65 0x01 0x71 0xFC 0x8C
+				0x00 0x11 0x9B 0x00 0x00 0x32>;
+			phy_data_page1_size = <8>;
+			phy_data_page1_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7>;
+			phy_data_page1_A00 = /bits/ 8
+				<0x25 0xEF 0x60 0x44 0x00 0x0F 0x18 0xE3>;
+			phy_data_page2_size = <1>;
+			phy_data_page2_addr = /bits/ 8
+				<0xE0>;
+			phy_data_page2_A00 = /bits/ 8
+				<0x01>;
+			do_toggle;
+			check_efuse;
+			//use_default_parameter;
+			is_double_sensitivity_mode;
+			ldo_page0_e4_compensate = <(-2)>;
+		};
+	};
+
+	dwc3_u2host: rtk_dwc3_u2host at 98013C00 {
+		compatible = "realtek,dwc3";
+		reg = <0x98013C00 0x200>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		delay_probe_work; //To delay probe work
+		status = "okay";
+
+		dwc3_u2host at 98029000 {
+			compatible = "synopsys,dwc3";
+			reg = <0x98029000 0x9000>;
+			interrupts = <0 21 4>;
+			snps,fixed_dwc3_globals_regs_start = <0x8100>;
+			usb-phy = <&dwc3_u2host_usb2phy>;
+			dr_mode = "host"; /*only host*/
+			//snps,dis_u2_susphy_quirk; // Add workaround for Usb3.0 hub suspend
+		};
+	};
+
+	dwc3_u3drd_usb3phy: dwc3_u3drd_usb3phy at 98013E10 {
+		compatible = "realtek,usb3phy";
+		reg = <0x98013E10 0x4>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		status = "okay";
+		port_index = <0>; /* index in u3 port */
+		phyN = <1>;
+		phy0 {
+			phy_data_size = <0x30>;
+			phy_data_addr = /bits/ 8
+				<0x00 0x01 0x02 0x03 0x04 0x05 0x06
+				0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D
+				0x0E 0x0F 0x10 0x11 0x12 0x13 0x14
+				0x15 0x16 0x17 0x18 0x19 0x1A 0x1B
+				0x1C 0x1D 0x1E 0x1F 0x20 0x21 0x22
+				0x23 0x24 0x25 0x26 0x27 0x28 0x29
+				0x2A 0x2B 0x2C 0x2D 0x2E 0x2F>;
+			phy_data_A00 = /bits/ 16
+				<0x400C 0xAC86 0x6042 0x2771 0x72F5 0x2AD3 0x0003
+					0x2E00 0x3591 0x925C 0xA608 0xA905 0xC000 0xEF1E
+					0x2010 0x8D50 0x000C 0x4C10 0xFC00 0x0C81 0xDE01
+					0x0000 0x0000 0x0000 0x0000 0x6000 0x0085 0x2014
+					0xC900 0xA03F 0xC2E0 0x7E00 0x705A 0xF645 0x0013
+					0xCB66 0x4770 0x126C 0x840A 0x01D6 0xF802 0xff04
+					0x3040 0x8028 0xFFFF 0xFFFF 0x0000 0x8600>;
+			phy_data_B00 = /bits/ 16
+				<0x400C 0xAC86 0x6042 0x2771 0x72F5 0x2AD3 0x0003
+					0x2E00 0x3591 0x924C 0xA608 0xB905 0xC000 0xEF1E
+					0x2010 0x8D50 0x000C 0x4C10 0xFC00 0x0C81 0xDE01
+					0x0000 0x0000 0x0000 0x0000 0x6000 0x0085 0x2014
+					0xC900 0xA03F 0xC2E0 0x7E00 0x705A 0xF645 0x0013
+					0xCB66 0x4770 0x126C 0x840A 0x01D6 0xF802 0xff04
+					0x3040 0x8028 0xFFFF 0xFFFF 0x0000 0x8600>;
+			do_toggle;
+			//use_default_parameter;
+		};
+	};
+
+	dwc3_u3drd_usb2phy: dwc3_u3drd_usb2phy at 98013E14 {
+		compatible = "realtek,usb2phy";
+		reg = <0x98013E14 0x4>, <0x98058280 0x4>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		status = "okay";
+		phyN = <1>;
+		phy0 {
+			phy_data_page0_size = <16>;
+			phy_data_page0_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xF0 0xF1
+				0xF2 0xF3 0xF4 0xF5 0xF6 0xF7>;
+			phy_data_page0_A00 = /bits/ 8
+				<0xE0 0x30 0x79 0x8D 0x6A 0x65 0x01 0x71 0xFC 0x8C
+				0x00 0x11 0x9B 0x00 0x00 0x0A>;
+			phy_data_page0_B00 = /bits/ 8
+				<0x18 0x30 0x79 0x8D 0x6A 0x65 0x01 0x71 0xFC 0x8C
+				0x00 0x11 0x9B 0x00 0x00 0x32>;
+			phy_data_page1_size = <8>;
+			phy_data_page1_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7>;
+			phy_data_page1_A00 = /bits/ 8
+				<0x25 0xEF 0x60 0x44 0x00 0x0F 0x18 0xE3>;
+			phy_data_page2_size = <1>;
+			phy_data_page2_addr = /bits/ 8
+				<0xE0>;
+			phy_data_page2_A00 = /bits/ 8
+				<0x01>;
+			do_toggle;
+			check_efuse;
+			//use_default_parameter;
+			is_double_sensitivity_mode;
+			ldo_page0_e4_compensate = <(-2)>;
+		};
+	};
+
+	dwc3_u3drd: rtk_dwc3_u3drd at 98013E00 {
+		compatible = "realtek,dwc3";
+		reg = <0x98013E00 0x200>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		delay_probe_work; //To delay probe work
+		status = "okay";
+
+		dwc3_u3drd at 981f0000 {
+			compatible = "synopsys,dwc3";
+			reg = <0x98050000 0x9000>;
+			interrupts = <0 21 4>;
+			snps,fixed_dwc3_globals_regs_start = <0x8100>;
+			usb-phy = <&dwc3_u3drd_usb2phy &dwc3_u3drd_usb3phy>;
+			dr_mode = "host"; /*only host*/
+			snps,dis_u2_susphy_quirk; // Disable u2phy suspend for drd
+			snps,parkmode-disable-ss-quirk; // disable usb3.0 park mode
+
+			status = "okay";
+		};
+	};
+};
-- 
2.28.0



From stanley_chang at realtek.com  Thu Sep 17 04:38:28 2020
From: stanley_chang at realtek.com (stanley_chang)
Date: Thu, 17 Sep 2020 16:38:28 +0800
Subject: [PATCH 03/10] usb: dwc3: rtk: Add new realtek soc dwc3 controller
 glue driver
In-Reply-To: <20200917083834.20782-1-stanley_chang@realtek.com>
References: <20200917083834.20782-1-stanley_chang@realtek.com>
Message-ID: <20200917083834.20782-4-stanley_chang@realtek.com>

Realtek DHC SoCs embeds a DWC3 USB IP Core. This USB module can be drive
by dwc3-rtk glue driver

Signed-off-by: stanley_chang <stanley_chang at realtek.com>
---
 drivers/usb/dwc3/Kconfig            |  10 +
 drivers/usb/dwc3/Makefile           |   1 +
 drivers/usb/dwc3/dwc3-rtk-debugfs.c | 501 ++++++++++++++++++++++++++++
 drivers/usb/dwc3/dwc3-rtk.c         | 336 +++++++++++++++++++
 drivers/usb/dwc3/dwc3-rtk.h         |  45 +++
 5 files changed, 893 insertions(+)
 create mode 100644 drivers/usb/dwc3/dwc3-rtk-debugfs.c
 create mode 100644 drivers/usb/dwc3/dwc3-rtk.c
 create mode 100644 drivers/usb/dwc3/dwc3-rtk.h

diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 206caa0ea1c6..5353d05d9f6c 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -52,6 +52,16 @@ endchoice
 
 comment "Platform Glue Driver Support"
 
+config USB_DWC3_RTK
+	tristate "Realtek DWC3 Platform Driver"
+	default USB_DWC3
+	select RTK_USB2PHY
+	select RTK_USB3PHY
+	help
+	  RTK SoCs with DesignWare Core USB3 IP inside,
+	  say 'Y' or 'M' if you have such device.
+
+
 config USB_DWC3_OMAP
 	tristate "Texas Instruments OMAP5 and similar Platforms"
 	depends on ARCH_OMAP2PLUS || COMPILE_TEST
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index ae86da0dc5bd..d57cb12d000a 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -51,3 +51,4 @@ obj-$(CONFIG_USB_DWC3_MESON_G12A)	+= dwc3-meson-g12a.o
 obj-$(CONFIG_USB_DWC3_OF_SIMPLE)	+= dwc3-of-simple.o
 obj-$(CONFIG_USB_DWC3_ST)		+= dwc3-st.o
 obj-$(CONFIG_USB_DWC3_QCOM)		+= dwc3-qcom.o
+obj-$(CONFIG_USB_DWC3_RTK)		+= dwc3-rtk.o dwc3-rtk-debugfs.o
diff --git a/drivers/usb/dwc3/dwc3-rtk-debugfs.c b/drivers/usb/dwc3/dwc3-rtk-debugfs.c
new file mode 100644
index 000000000000..367f194c4e8e
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-rtk-debugfs.c
@@ -0,0 +1,501 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * dwc3-rtk-debugfs.c - Reltek DesignWare USB3 DRD Controller DebugFS file
+ *
+ * Copyright (C) 2020 Realtek Semiconductor Corporation
+ *
+ */
+
+#include <linux/spinlock.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/of_address.h>
+
+#include "dwc3-rtk.h"
+#include "core.h"
+#include "io.h"
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+
+struct rtk_debugfs_reg32 {
+	char *name;
+	unsigned long offset;
+	int default_val;
+};
+
+#define dump_register(nm)				\
+{							\
+	.name	= __stringify(nm) " [" __stringify(DWC3_ ##nm) "]", \
+	.offset	= DWC3_ ##nm,				\
+}
+
+#define dump_ep_register_set_DEPCMDPAR2(n)	\
+	{					\
+		.name = "DEPCMDPAR2("__stringify(n)") ["	\
+			    __stringify(DWC3_DEP_BASE(n)) " + " \
+			    __stringify(DWC3_DEPCMDPAR2) "]",	\
+		.offset = DWC3_DEP_BASE(n) +	\
+			DWC3_DEPCMDPAR2,	\
+	},
+#define dump_ep_register_set_DEPCMDPAR1(n)	\
+	{					\
+		.name = "DEPCMDPAR1("__stringify(n)" ["		\
+			    __stringify(DWC3_DEP_BASE(n)) " + " \
+			    __stringify(DWC3_DEPCMDPAR1) "]",	\
+		.offset = DWC3_DEP_BASE(n) +	\
+			DWC3_DEPCMDPAR1,	\
+	},
+#define dump_ep_register_set_DEPCMDPAR0(n)	\
+	{					\
+		.name = "DEPCMDPAR0("__stringify(n)") ["	\
+			    __stringify(DWC3_DEP_BASE(n)) " + " \
+			    __stringify(DWC3_DEPCMDPAR0) "]",	\
+		.offset = DWC3_DEP_BASE(n) +	\
+			DWC3_DEPCMDPAR0,	\
+	},
+#define dump_ep_register_set_CMD(n)		\
+	{					\
+		.name = "DEPCMD("__stringify(n)") [" \
+			    __stringify(DWC3_DEP_BASE(n)) " + " \
+			    __stringify(DWC3_DEPCMD) "]",	\
+		.offset = DWC3_DEP_BASE(n) +	\
+			DWC3_DEPCMD,		\
+	}
+
+#define dump_ep_register_set(n)				\
+	dump_ep_register_set_DEPCMDPAR2(n)		\
+	dump_ep_register_set_DEPCMDPAR1(n)		\
+	dump_ep_register_set_DEPCMDPAR0(n)		\
+	dump_ep_register_set_CMD(n)			\
+
+static const struct rtk_debugfs_reg32 dwc3_regs[] = {
+	dump_register(GSBUSCFG0),
+	dump_register(GSBUSCFG1),
+	dump_register(GTXTHRCFG),
+	dump_register(GRXTHRCFG),
+	dump_register(GCTL),
+	dump_register(GEVTEN),
+	dump_register(GSTS),
+	dump_register(GUCTL1),
+	dump_register(GSNPSID),
+	dump_register(GGPIO),
+	dump_register(GUID),
+	dump_register(GUCTL),
+	dump_register(GBUSERRADDR0),
+	dump_register(GBUSERRADDR1),
+	dump_register(GPRTBIMAP0),
+	dump_register(GPRTBIMAP1),
+	dump_register(GHWPARAMS0),
+	dump_register(GHWPARAMS1),
+	dump_register(GHWPARAMS2),
+	dump_register(GHWPARAMS3),
+	dump_register(GHWPARAMS4),
+	dump_register(GHWPARAMS5),
+	dump_register(GHWPARAMS6),
+	dump_register(GHWPARAMS7),
+	dump_register(GDBGFIFOSPACE),
+	dump_register(GDBGLTSSM),
+	dump_register(GPRTBIMAP_HS0),
+	dump_register(GPRTBIMAP_HS1),
+	dump_register(GPRTBIMAP_FS0),
+	dump_register(GPRTBIMAP_FS1),
+
+	dump_register(GUSB2PHYCFG(0)),
+	dump_register(GUSB2PHYCFG(1)),
+	dump_register(GUSB2PHYCFG(2)),
+	dump_register(GUSB2PHYCFG(3)),
+	dump_register(GUSB2PHYCFG(4)),
+	dump_register(GUSB2PHYCFG(5)),
+	dump_register(GUSB2PHYCFG(6)),
+	dump_register(GUSB2PHYCFG(7)),
+	dump_register(GUSB2PHYCFG(8)),
+	dump_register(GUSB2PHYCFG(9)),
+	dump_register(GUSB2PHYCFG(10)),
+	dump_register(GUSB2PHYCFG(11)),
+	dump_register(GUSB2PHYCFG(12)),
+	dump_register(GUSB2PHYCFG(13)),
+	dump_register(GUSB2PHYCFG(14)),
+	dump_register(GUSB2PHYCFG(15)),
+
+	dump_register(GUSB2I2CCTL(0)),
+	dump_register(GUSB2I2CCTL(1)),
+	dump_register(GUSB2I2CCTL(2)),
+	dump_register(GUSB2I2CCTL(3)),
+	dump_register(GUSB2I2CCTL(4)),
+	dump_register(GUSB2I2CCTL(5)),
+	dump_register(GUSB2I2CCTL(6)),
+	dump_register(GUSB2I2CCTL(7)),
+	dump_register(GUSB2I2CCTL(8)),
+	dump_register(GUSB2I2CCTL(9)),
+	dump_register(GUSB2I2CCTL(10)),
+	dump_register(GUSB2I2CCTL(11)),
+	dump_register(GUSB2I2CCTL(12)),
+	dump_register(GUSB2I2CCTL(13)),
+	dump_register(GUSB2I2CCTL(14)),
+	dump_register(GUSB2I2CCTL(15)),
+
+	dump_register(GUSB2PHYACC(0)),
+	dump_register(GUSB2PHYACC(1)),
+	dump_register(GUSB2PHYACC(2)),
+	dump_register(GUSB2PHYACC(3)),
+	dump_register(GUSB2PHYACC(4)),
+	dump_register(GUSB2PHYACC(5)),
+	dump_register(GUSB2PHYACC(6)),
+	dump_register(GUSB2PHYACC(7)),
+	dump_register(GUSB2PHYACC(8)),
+	dump_register(GUSB2PHYACC(9)),
+	dump_register(GUSB2PHYACC(10)),
+	dump_register(GUSB2PHYACC(11)),
+	dump_register(GUSB2PHYACC(12)),
+	dump_register(GUSB2PHYACC(13)),
+	dump_register(GUSB2PHYACC(14)),
+	dump_register(GUSB2PHYACC(15)),
+
+	dump_register(GUSB3PIPECTL(0)),
+	dump_register(GUSB3PIPECTL(1)),
+	dump_register(GUSB3PIPECTL(2)),
+	dump_register(GUSB3PIPECTL(3)),
+	dump_register(GUSB3PIPECTL(4)),
+	dump_register(GUSB3PIPECTL(5)),
+	dump_register(GUSB3PIPECTL(6)),
+	dump_register(GUSB3PIPECTL(7)),
+	dump_register(GUSB3PIPECTL(8)),
+	dump_register(GUSB3PIPECTL(9)),
+	dump_register(GUSB3PIPECTL(10)),
+	dump_register(GUSB3PIPECTL(11)),
+	dump_register(GUSB3PIPECTL(12)),
+	dump_register(GUSB3PIPECTL(13)),
+	dump_register(GUSB3PIPECTL(14)),
+	dump_register(GUSB3PIPECTL(15)),
+
+	dump_register(GTXFIFOSIZ(0)),
+	dump_register(GTXFIFOSIZ(1)),
+	dump_register(GTXFIFOSIZ(2)),
+	dump_register(GTXFIFOSIZ(3)),
+	dump_register(GTXFIFOSIZ(4)),
+	dump_register(GTXFIFOSIZ(5)),
+	dump_register(GTXFIFOSIZ(6)),
+	dump_register(GTXFIFOSIZ(7)),
+	dump_register(GTXFIFOSIZ(8)),
+	dump_register(GTXFIFOSIZ(9)),
+	dump_register(GTXFIFOSIZ(10)),
+	dump_register(GTXFIFOSIZ(11)),
+	dump_register(GTXFIFOSIZ(12)),
+	dump_register(GTXFIFOSIZ(13)),
+	dump_register(GTXFIFOSIZ(14)),
+	dump_register(GTXFIFOSIZ(15)),
+	dump_register(GTXFIFOSIZ(16)),
+	dump_register(GTXFIFOSIZ(17)),
+	dump_register(GTXFIFOSIZ(18)),
+	dump_register(GTXFIFOSIZ(19)),
+	dump_register(GTXFIFOSIZ(20)),
+	dump_register(GTXFIFOSIZ(21)),
+	dump_register(GTXFIFOSIZ(22)),
+	dump_register(GTXFIFOSIZ(23)),
+	dump_register(GTXFIFOSIZ(24)),
+	dump_register(GTXFIFOSIZ(25)),
+	dump_register(GTXFIFOSIZ(26)),
+	dump_register(GTXFIFOSIZ(27)),
+	dump_register(GTXFIFOSIZ(28)),
+	dump_register(GTXFIFOSIZ(29)),
+	dump_register(GTXFIFOSIZ(30)),
+	dump_register(GTXFIFOSIZ(31)),
+
+	dump_register(GRXFIFOSIZ(0)),
+	dump_register(GRXFIFOSIZ(1)),
+	dump_register(GRXFIFOSIZ(2)),
+	dump_register(GRXFIFOSIZ(3)),
+	dump_register(GRXFIFOSIZ(4)),
+	dump_register(GRXFIFOSIZ(5)),
+	dump_register(GRXFIFOSIZ(6)),
+	dump_register(GRXFIFOSIZ(7)),
+	dump_register(GRXFIFOSIZ(8)),
+	dump_register(GRXFIFOSIZ(9)),
+	dump_register(GRXFIFOSIZ(10)),
+	dump_register(GRXFIFOSIZ(11)),
+	dump_register(GRXFIFOSIZ(12)),
+	dump_register(GRXFIFOSIZ(13)),
+	dump_register(GRXFIFOSIZ(14)),
+	dump_register(GRXFIFOSIZ(15)),
+	dump_register(GRXFIFOSIZ(16)),
+	dump_register(GRXFIFOSIZ(17)),
+	dump_register(GRXFIFOSIZ(18)),
+	dump_register(GRXFIFOSIZ(19)),
+	dump_register(GRXFIFOSIZ(20)),
+	dump_register(GRXFIFOSIZ(21)),
+	dump_register(GRXFIFOSIZ(22)),
+	dump_register(GRXFIFOSIZ(23)),
+	dump_register(GRXFIFOSIZ(24)),
+	dump_register(GRXFIFOSIZ(25)),
+	dump_register(GRXFIFOSIZ(26)),
+	dump_register(GRXFIFOSIZ(27)),
+	dump_register(GRXFIFOSIZ(28)),
+	dump_register(GRXFIFOSIZ(29)),
+	dump_register(GRXFIFOSIZ(30)),
+	dump_register(GRXFIFOSIZ(31)),
+
+	dump_register(GEVNTADRLO(0)),
+	dump_register(GEVNTADRHI(0)),
+	dump_register(GEVNTSIZ(0)),
+	dump_register(GEVNTCOUNT(0)),
+
+	dump_register(GHWPARAMS8),
+	dump_register(DCFG),
+	dump_register(DCTL),
+	dump_register(DEVTEN),
+	dump_register(DSTS),
+	dump_register(DGCMDPAR),
+	dump_register(DGCMD),
+	dump_register(DALEPENA),
+
+	dump_ep_register_set(0),
+	dump_ep_register_set(1),
+	dump_ep_register_set(2),
+	dump_ep_register_set(3),
+	dump_ep_register_set(4),
+	dump_ep_register_set(5),
+	dump_ep_register_set(6),
+	dump_ep_register_set(7),
+	dump_ep_register_set(8),
+	dump_ep_register_set(9),
+	dump_ep_register_set(10),
+	dump_ep_register_set(11),
+	dump_ep_register_set(12),
+	dump_ep_register_set(13),
+	dump_ep_register_set(14),
+	dump_ep_register_set(15),
+	dump_ep_register_set(16),
+	dump_ep_register_set(17),
+	dump_ep_register_set(18),
+	dump_ep_register_set(19),
+	dump_ep_register_set(20),
+	dump_ep_register_set(21),
+	dump_ep_register_set(22),
+	dump_ep_register_set(23),
+	dump_ep_register_set(24),
+	dump_ep_register_set(25),
+	dump_ep_register_set(26),
+	dump_ep_register_set(27),
+	dump_ep_register_set(28),
+	dump_ep_register_set(29),
+	dump_ep_register_set(30),
+	dump_ep_register_set(31),
+
+	dump_register(OCFG),
+	dump_register(OCTL),
+	dump_register(OEVT),
+	dump_register(OEVTEN),
+	dump_register(OSTS),
+};
+
+#define dump_wrap_register(nm)				\
+{							\
+	.name	= __stringify(nm) " [" __stringify(WRAP_ ##nm) "]", \
+	.offset	= WRAP_ ##nm,				\
+}
+
+static const struct rtk_debugfs_reg32 wrap_regs[] = {
+	dump_wrap_register(CTR_reg),
+	dump_wrap_register(USB_HMAC_CTR0_reg),
+	dump_wrap_register(USB2_PHY_reg),
+};
+
+static int dwc3_rtk_dwc_regdump_show(struct seq_file *s, void *unused)
+{
+	struct dwc3_rtk		*dwc3_rtk = s->private;
+	struct dwc3		*dwc;
+	unsigned long		flags;
+	struct rtk_debugfs_reg32 *regs = (struct rtk_debugfs_reg32 *)dwc3_regs;
+	u32 nregs = ARRAY_SIZE(dwc3_regs);
+	int i;
+
+	dwc = dwc3_rtk->dwc;
+
+	spin_lock_irqsave(&dwc->lock, flags);
+
+	for (i = 0; i < nregs; i++, regs++) {
+		u32 val = dwc3_readl(dwc->regs, regs->offset);
+
+		seq_printf(s, "%s = 0x%08x -> 0x%08x (diff=0x%08x)\n",
+			    regs->name,
+			    regs->default_val, val, regs->default_val ^ val);
+		if (seq_has_overflowed(s))
+			break;
+	}
+
+	spin_unlock_irqrestore(&dwc->lock, flags);
+
+	return 0;
+}
+
+static int dwc3_rtk_dwc_regdump_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dwc3_rtk_dwc_regdump_show, inode->i_private);
+}
+
+static const struct file_operations dwc3_rtk_dwc_regdump_fops = {
+	.open			= dwc3_rtk_dwc_regdump_open,
+	.read			= seq_read,
+	.llseek			= seq_lseek,
+	.release		= single_release,
+};
+
+static int dwc3_rtk_wrap_regdump_show(struct seq_file *s, void *unused)
+{
+	struct dwc3_rtk		*dwc3_rtk = s->private;
+	struct rtk_debugfs_reg32 *regs = (struct rtk_debugfs_reg32 *)wrap_regs;
+	u32 nregs = ARRAY_SIZE(wrap_regs);
+	int i;
+
+	for (i = 0; i < nregs; i++, regs++) {
+		u32 val = readl(dwc3_rtk->regs + regs->offset);
+
+		seq_printf(s, "%s = 0x%08x -> 0x%08x (diff=0x%08x)\n",
+			    regs->name,
+			    regs->default_val, val, regs->default_val ^ val);
+		if (seq_has_overflowed(s))
+			break;
+	}
+
+	return 0;
+}
+
+static int dwc3_rtk_wrap_regdump_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dwc3_rtk_wrap_regdump_show, inode->i_private);
+}
+
+static const struct file_operations dwc3_rtk_wrap_regdump_fops = {
+	.open			= dwc3_rtk_wrap_regdump_open,
+	.read			= seq_read,
+	.llseek			= seq_lseek,
+	.release		= single_release,
+};
+
+void dwc3_rtk_regdump_init(struct dwc3_rtk *dwc3_rtk)
+{
+	struct device		*dev;
+	struct device_node	*node;
+	struct device_node	*dwc3_node;
+
+	if (!dwc3_rtk) {
+		pr_err("%s: No device for dwc3_rtk!\n", __func__);
+		return;
+	}
+
+	dev = dwc3_rtk->dev;
+	if (!dev) {
+		pr_err("%s: No device for dwc3_rtk!\n", __func__);
+		return;
+	}
+
+	node = dev->of_node;
+	if (!node) {
+		pr_err("%s: No device node for dwc3_rtk!\n", __func__);
+		return;
+	}
+
+	dwc3_node = of_get_next_child(node, NULL);
+	if (dwc3_node) {
+		const __be32	*addrp;
+		u64		addr;
+		u64		size;
+		unsigned int	flags;
+		u32		fixed_dwc3_globals_regs_start;
+		struct rtk_debugfs_reg32 *regs;
+		void __iomem *base;
+		int i;
+
+		regs = (struct rtk_debugfs_reg32 *)dwc3_regs;
+
+		addrp = of_get_address(dwc3_node, 0, &size, &flags);
+		if (addrp == NULL) {
+			dev_err(dev, "%s: Error! Can't get dwc3 reg address!\n",
+				    __func__);
+			return;
+		}
+		addr = of_translate_address(dwc3_node, addrp);
+
+		of_property_read_u32(dwc3_node,
+			    "snps,fixed_dwc3_globals_regs_start",
+			    &fixed_dwc3_globals_regs_start);
+		if (fixed_dwc3_globals_regs_start) {
+			addr += fixed_dwc3_globals_regs_start;
+			size -= fixed_dwc3_globals_regs_start;
+		}
+		dev_dbg(dev, "dwc3 register start address to 0x%x (size=%x)\n",
+			    (u32)addr, (u32)size);
+
+		base = ioremap(addr, size);
+		for (i = 0; i < ARRAY_SIZE(dwc3_regs); i++, regs++)
+			regs->default_val = dwc3_readl(base, regs->offset);
+
+		iounmap(base);
+	}
+
+	if (dwc3_rtk) {
+		struct rtk_debugfs_reg32 *regs;
+		int i;
+
+		regs = (struct rtk_debugfs_reg32 *)wrap_regs;
+
+		for (i = 0; i < ARRAY_SIZE(wrap_regs); i++, regs++) {
+			regs->default_val = readl(dwc3_rtk->regs +
+			    regs->offset);
+		}
+	}
+}
+
+void dwc3_rtk_debugfs_init(struct dwc3_rtk *dwc3_rtk)
+{
+	struct dentry		*debug_dir;
+	struct dentry		*file;
+	struct device		*dev;
+
+	if (!dwc3_rtk) {
+		pr_err("%s: No device for dwc3_rtk!\n", __func__);
+		return;
+	}
+
+	dev = dwc3_rtk->dev;
+	if (!dev) {
+		pr_err("%s: No device for dwc3_rtk!\n", __func__);
+		return;
+	}
+
+	dwc3_rtk_regdump_init(dwc3_rtk);
+
+	debug_dir = debugfs_create_dir(dev_name(dev), usb_debug_root);
+	if (!debug_dir) {
+		dev_err(dev, "%s: Can't create debugfs dir\n", __func__);
+		return;
+	}
+	dwc3_rtk->debug_dir = debug_dir;
+
+	file = debugfs_create_file("dwc3_regdump", S_IRUGO,
+		    debug_dir, dwc3_rtk, &dwc3_rtk_dwc_regdump_fops);
+	if (!file)
+		dev_err(dev, "%s: Can't create debugfs regdump\n", __func__);
+
+	file = debugfs_create_file("wrap_regdump", S_IRUGO,
+		    debug_dir, dwc3_rtk, &dwc3_rtk_wrap_regdump_fops);
+	if (!file)
+		dev_err(dev, "%s: Can't create debugfs regdump\n", __func__);
+}
+
+void dwc3_rtk_debugfs_exit(struct dwc3_rtk *dwc3_rtk)
+{
+	debugfs_remove_recursive(dwc3_rtk->debug_dir);
+}
+
+#else
+void dwc3_rtk_debugfs_init(struct dwc3_rtk *dwc3_rtk)
+{
+
+}
+
+void dwc3_rtk_debugfs_exit(struct dwc3_rtk *dwc3_rtk)
+{
+
+}
+#endif /* CONFIG_DYNAMIC_DEBUG */
diff --git a/drivers/usb/dwc3/dwc3-rtk.c b/drivers/usb/dwc3/dwc3-rtk.c
new file mode 100644
index 000000000000..3291ee401a64
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-rtk.c
@@ -0,0 +1,336 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * dwc3-rtk.c - Realtek DWC3 Specific Glue layer
+ *
+ * Copyright (C) 2017 Realtek Semiconductor Corporation
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/of.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/suspend.h>
+#include <linux/sys_soc.h>
+
+#include "dwc3-rtk.h"
+#include "core.h"
+#include "io.h"
+
+static const struct soc_device_attribute rtk_soc_kylin_a00[] = {
+	{
+		.family = "Realtek Kylin",
+		.revision = "A00",
+	},
+	{
+		/* empty */
+	}
+};
+
+static const struct soc_device_attribute rtk_soc_hercules[] = {
+	{
+		.family = "Realtek Hercules",
+	},
+	{
+		/* empty */
+	}
+};
+
+static void switch_u2_dr_mode(struct dwc3_rtk *rtk, int dr_mode)
+{
+	switch (dr_mode) {
+	case USB_DR_MODE_PERIPHERAL:
+		writel(USB2_PHY_SWITCH_DEVICE |
+			    (~USB2_PHY_SWITCH_MASK &
+			      readl(rtk->regs + WRAP_USB2_PHY_reg)),
+			    rtk->regs + WRAP_USB2_PHY_reg);
+		break;
+	case USB_DR_MODE_HOST:
+		writel(USB2_PHY_SWITCH_HOST |
+			    (~USB2_PHY_SWITCH_MASK &
+			      readl(rtk->regs + WRAP_USB2_PHY_reg)),
+			    rtk->regs + WRAP_USB2_PHY_reg);
+		break;
+	case USB_DR_MODE_OTG:
+		//writel(BIT(11) , rtk->regs + WRAP_USB2_PHY_reg);
+		dev_info(rtk->dev, "%s: USB_DR_MODE_OTG\n", __func__);
+		break;
+	}
+}
+
+static int dwc3_rtk_init(struct dwc3_rtk *rtk)
+{
+	struct device		*dev = rtk->dev;
+	void __iomem		*regs = rtk->regs;
+
+	if (soc_device_match(rtk_soc_kylin_a00)) {
+		writel(DISABLE_MULTI_REQ | readl(regs + WRAP_CTR_reg),
+				regs + WRAP_CTR_reg);
+		dev_info(dev, "[bug fixed] 1295/1296 A00: add workaround to disable multiple request for D-Bus");
+	}
+
+	if (soc_device_match(rtk_soc_hercules)) {
+		writel(USB2_PHY_EN_PHY_PLL_PORT1 |
+			    readl(regs + WRAP_USB2_PHY_reg),
+			    regs + WRAP_USB2_PHY_reg);
+		dev_info(dev, "[bug fixed] 1395 add workaround to disable usb2 port 2 suspend!");
+
+	}
+	return 0;
+}
+
+static int dwc3_rtk_probe_dwc3core(struct dwc3_rtk *rtk)
+{
+	struct device		*dev = rtk->dev;
+	struct device_node	*node = dev->of_node;
+	struct device_node	*dwc3_node;
+	int    ret = 0;
+
+	dwc3_rtk_init(rtk);
+
+	if (node) {
+		ret = of_platform_populate(node, NULL, NULL, dev);
+		if (ret) {
+			dev_err(dev, "failed to add dwc3 core\n");
+			return ret;
+		}
+
+		dwc3_node = of_get_next_child(node, NULL);
+		if (dwc3_node != NULL) {
+			struct device *dwc3_dev;
+			struct platform_device *dwc3_pdev;
+			int dr_mode;
+
+			dwc3_pdev = of_find_device_by_node(dwc3_node);
+			dwc3_dev = &dwc3_pdev->dev;
+			rtk->dwc = platform_get_drvdata(dwc3_pdev);
+
+			dr_mode = usb_get_dr_mode(dwc3_dev);
+			rtk->default_dwc3_dr_mode = dr_mode;
+			rtk->cur_dr_mode = dr_mode;
+
+			switch_u2_dr_mode(rtk, dr_mode);
+
+		} else {
+			dev_err(dev, "dwc3 node is NULL\n");
+		}
+	} else {
+		dev_err(dev, "dwc3_rtk node is NULL\n");
+	}
+
+	return ret;
+}
+
+static void dwc3_rtk_probe_work(struct work_struct *work)
+{
+	struct dwc3_rtk *rtk = container_of(work, struct dwc3_rtk, work);
+	struct device		*dev = rtk->dev;
+	int    ret = 0;
+
+	unsigned long probe_time = jiffies;
+
+	dev_info(dev, "%s Start ...\n", __func__);
+
+	ret = dwc3_rtk_probe_dwc3core(rtk);
+
+	if (ret)
+		dev_err(dev, "%s failed to add dwc3 core\n", __func__);
+
+	dev_info(dev, "%s End ... ok! (take %d ms)\n", __func__,
+		    jiffies_to_msecs(jiffies - probe_time));
+}
+
+static int dwc3_rtk_probe(struct platform_device *pdev)
+{
+	struct dwc3_rtk	*rtk;
+	struct device		*dev = &pdev->dev;
+	struct device_node	*node = dev->of_node;
+
+	struct resource         *res;
+	void __iomem            *regs;
+
+	int			ret = -ENOMEM;
+	unsigned long probe_time = jiffies;
+
+	dev_info(&pdev->dev, "Probe Realtek-SoC USB DWC3 Host Controller\n");
+
+	rtk = devm_kzalloc(dev, sizeof(*rtk), GFP_KERNEL);
+	if (!rtk)
+		goto err1;
+
+	/*
+	 * Right now device-tree probed devices don't get dma_mask set.
+	 * Since shared usb code relies on it, set it here for now.
+	 * Once we move to full device tree support this will vanish off.
+	 */
+	if (!dev->dma_mask)
+		dev->dma_mask = &dev->coherent_dma_mask;
+	if (!dev->coherent_dma_mask)
+		dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+	platform_set_drvdata(pdev, rtk);
+
+	rtk->dev	= dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "missing memory resource\n");
+		ret = -ENODEV;
+		goto err1;
+	}
+
+	regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(regs)) {
+		ret = PTR_ERR(regs);
+		goto err1;
+	}
+
+	rtk->regs = regs;
+	rtk->regs_size = resource_size(res);
+
+	dwc3_rtk_debugfs_init(rtk);
+
+	if (node) {
+		if (of_property_read_bool(node, "dis_u3_port")) {
+			void __iomem *usb_hmac_ctr0 = rtk->regs +
+				    WRAP_USB_HMAC_CTR0_reg;
+			int val_u3port_dis = U3PORT_DIS | readl(usb_hmac_ctr0);
+
+			writel(val_u3port_dis, usb_hmac_ctr0);
+
+			dev_info(rtk->dev, "%s: disable usb 3.0 port (usb_hmac_ctr0=0x%x)\n",
+				    __func__, readl(usb_hmac_ctr0));
+		}
+
+	}
+
+	if (node) {
+		if (of_property_read_bool(node, "delay_probe_work")) {
+			INIT_WORK(&rtk->work, dwc3_rtk_probe_work);
+			schedule_work(&rtk->work);
+		} else {
+			ret = dwc3_rtk_probe_dwc3core(rtk);
+			if (ret) {
+				dev_err(dev, "%s failed to add dwc3 core\n",
+					    __func__);
+				goto err1;
+			}
+		}
+	} else {
+		dev_err(dev, "no device node, failed to add dwc3 core\n");
+		ret = -ENODEV;
+		goto err1;
+	}
+
+	dev_info(dev, "%s ok! (take %d ms)\n", __func__,
+		    jiffies_to_msecs(jiffies - probe_time));
+
+	return 0;
+
+err1:
+	return ret;
+}
+
+static int dwc3_rtk_remove(struct platform_device *pdev)
+{
+	struct dwc3_rtk	*rtk = platform_get_drvdata(pdev);
+
+	dwc3_rtk_debugfs_exit(rtk);
+
+	of_platform_depopulate(rtk->dev);
+
+	return 0;
+}
+
+static void dwc3_rtk_shutdown(struct platform_device *pdev)
+{
+	struct dwc3_rtk	*rtk = platform_get_drvdata(pdev);
+	struct device		*dev = &pdev->dev;
+
+	dev_info(dev, "%s start ...\n", __func__);
+
+	of_platform_depopulate(rtk->dev);
+
+	dev_info(dev, "%s ok!\n", __func__);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtk_dwc3_match[] = {
+	{ .compatible = "realtek,dwc3" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, rtk_dwc3_match);
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int dwc3_rtk_suspend(struct device *dev)
+{
+	struct dwc3_rtk *rtk = dev_get_drvdata(dev);
+
+	dev_info(dev, "[USB] Enter %s\n", __func__);
+
+	if (!rtk) {
+		dev_err(dev, "[USB] %s dwc3_rtk is NULL\n", __func__);
+		goto out;
+	}
+
+out:
+	dev_info(dev, "[USB] Exit %s", __func__);
+	return 0;
+}
+
+static int dwc3_rtk_resume(struct device *dev)
+{
+	struct dwc3_rtk *rtk = dev_get_drvdata(dev);
+	struct dwc3 *dwc = rtk->dwc;
+
+	dev_info(dev, "[USB] Enter %s", __func__);
+
+	if (!rtk) {
+		dev_err(dev, "[USB] %s dwc3_rtk is NULL\n", __func__);
+		goto out;
+	}
+
+	dwc3_rtk_init(rtk);
+
+	switch_u2_dr_mode(rtk, dwc->dr_mode);
+
+	/* runtime set active to reflect active state. */
+	pm_runtime_disable(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+out:
+	dev_info(dev, "[USB] Exit %s\n", __func__);
+	return 0;
+}
+
+static const struct dev_pm_ops dwc3_rtk_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(dwc3_rtk_suspend, dwc3_rtk_resume)
+};
+
+#define DEV_PM_OPS	(&dwc3_rtk_dev_pm_ops)
+#else
+#define DEV_PM_OPS	NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static struct platform_driver dwc3_rtk_driver = {
+	.probe		= dwc3_rtk_probe,
+	.remove		= dwc3_rtk_remove,
+	.driver		= {
+		.name	= "rtk-dwc3",
+		.of_match_table = of_match_ptr(rtk_dwc3_match),
+		.pm	= DEV_PM_OPS,
+	},
+	.shutdown	= dwc3_rtk_shutdown,
+};
+
+module_platform_driver(dwc3_rtk_driver);
+
+MODULE_ALIAS("platform:rtk-dwc3");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/dwc3/dwc3-rtk.h b/drivers/usb/dwc3/dwc3-rtk.h
new file mode 100644
index 000000000000..9b4e7683def7
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-rtk.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/**
+ * dwc3-rtk.h - Realtek DWC3 Specific Glue layer
+ *
+ * Copyright (C) 2020 Realtek Semiconductor Corporation
+ *
+ */
+
+#ifndef __DRIVERS_USB_DWC3_RTK_H
+#define __DRIVERS_USB_DWC3_RTK_H
+
+#define WRAP_CTR_reg  0x0
+#define DISABLE_MULTI_REQ BIT(1)
+
+#define WRAP_USB_HMAC_CTR0_reg 0x60
+#define U3PORT_DIS BIT(8)
+
+#define WRAP_USB2_PHY_reg  0x70
+#define USB2_PHY_EN_PHY_PLL_PORT0 BIT(12)
+#define USB2_PHY_EN_PHY_PLL_PORT1 BIT(13)
+#define USB2_PHY_SWITCH_MASK 0x707
+#define USB2_PHY_SWITCH_DEVICE 0x0
+#define USB2_PHY_SWITCH_HOST 0x606
+
+struct dwc3_rtk {
+	struct device		*dev;
+
+	void __iomem		*regs;
+	size_t		regs_size;
+
+	struct dwc3 *dwc;
+
+	struct work_struct work;
+
+	int default_dwc3_dr_mode; /* define by dwc3 driver, and it is fixed */
+	int cur_dr_mode; /* current dr mode */
+
+	/* For debugfs */
+	struct dentry		*debug_dir;
+};
+
+void dwc3_rtk_debugfs_init(struct dwc3_rtk *dwc3_rtk);
+void dwc3_rtk_debugfs_exit(struct dwc3_rtk *dwc3_rtk);
+
+#endif /* __DRIVERS_USB_DWC3_RTK_H */
-- 
2.28.0



From stanley_chang at realtek.com  Thu Sep 17 05:26:35 2020
From: stanley_chang at realtek.com (stanley_chang)
Date: Thu, 17 Sep 2020 17:26:35 +0800
Subject: [PATCH 10/10] arm64: dts: realtek: rtd13xx-usb: Add rtd13xx usb
 module nodes
Message-ID: <20200917092635.18858-1-stanley_chang@realtek.com>

Signed-off-by: stanley_chang <stanley_chang at realtek.com>
---
 arch/arm64/boot/dts/realtek/rtd13xx-usb.dtsi | 224 +++++++++++++++++++
 1 file changed, 224 insertions(+)
 create mode 100644 arch/arm64/boot/dts/realtek/rtd13xx-usb.dtsi

diff --git a/arch/arm64/boot/dts/realtek/rtd13xx-usb.dtsi b/arch/arm64/boot/dts/realtek/rtd13xx-usb.dtsi
new file mode 100644
index 000000000000..6b55a873c941
--- /dev/null
+++ b/arch/arm64/boot/dts/realtek/rtd13xx-usb.dtsi
@@ -0,0 +1,224 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Realtek RTD13xx SoC USB
+ *
+ * Copyright (c) 2020 Realtek Semiconductor Corp.
+ */
+
+/ {
+	dwc3_u2drd_usb2phy: dwc3_u2drd_usb2phy at 98013214 {
+		compatible = "realtek,usb2phy";
+		reg = <0x98013214 0x4>, <0x98028280 0x4>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		status = "okay";
+		phyN = <1>;
+		phy0 {
+			phy_data_page0_size = <16>;
+			phy_data_page0_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xF0 0xF1
+				0xF2 0xF3 0xF4 0xF5 0xF6 0xF7>;
+			phy_data_page0_A00 = /bits/ 8
+				<0xE0 0x30 0x79 0x8D 0x6A 0x65 0x01 0x71 0xFC 0x8C
+				0x00 0x11 0x9B 0x00 0x00 0x0A>;
+			phy_data_page0_B00 = /bits/ 8
+				<0x18 0x30 0x79 0x8D 0x6A 0x65 0x01 0x71 0xFC 0x8C
+				0x00 0x11 0x9B 0x00 0x00 0x32>;
+			phy_data_page1_size = <8>;
+			phy_data_page1_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7>;
+			phy_data_page1_A00 = /bits/ 8
+				<0x25 0xEF 0x60 0x44 0x00 0x0F 0x18 0xE3>;
+			phy_data_page2_size = <1>;
+			phy_data_page2_addr = /bits/ 8
+				<0xE0>;
+			phy_data_page2_A00 = /bits/ 8
+				<0x01>;
+			do_toggle;
+			check_efuse;
+			//use_default_parameter;
+			is_double_sensitivity_mode;
+			ldo_page0_e4_compensate = <(-2)>;
+		};
+	};
+
+	dwc3_u2drd: rtk_dwc3_u2drd at 98013200 {
+		compatible = "realtek,dwc3";
+		reg = <0x98013200 0x200>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		delay_probe_work; //To delay probe work
+		status = "okay";
+
+		dwc3_u2drd at 98020000 {
+			compatible = "synopsys,dwc3";
+			reg = <0x98020000 0x9000>;
+			interrupts = <0 95 4>;
+			snps,fixed_dwc3_globals_regs_start = <0x8100>;
+			usb-phy = <&dwc3_u2drd_usb2phy>;
+			dr_mode = "host"; /*otg, host, peripheral*/
+			snps,dis_u2_susphy_quirk; // Disable u2phy suspend for drd
+			snps,dis-u2-freeclk-exists-quirk; // Fixed u2drd host die issue
+
+			status = "okay";
+		};
+	};
+
+	dwc3_u2host_usb2phy: dwc3_u2host_usb2phy at 98013C14 {
+		compatible = "realtek,usb2phy";
+		reg = <0x98013C14 0x4>, <0x98031280 0x4>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		status = "okay";
+		phyN = <1>;
+		phy0 {
+			phy_data_page0_size = <16>;
+			phy_data_page0_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xF0 0xF1
+				0xF2 0xF3 0xF4 0xF5 0xF6 0xF7>;
+			phy_data_page0_A00 = /bits/ 8
+				<0xE0 0x30 0x79 0x8D 0x6A 0x65 0x01 0x71 0xFC 0x8C
+				0x00 0x11 0x9B 0x00 0x00 0x0A>;
+			phy_data_page0_B00 = /bits/ 8
+				<0x18 0x30 0x79 0x8D 0x6A 0x65 0x01 0x71 0xFC 0x8C
+				0x00 0x11 0x9B 0x00 0x00 0x32>;
+			phy_data_page1_size = <8>;
+			phy_data_page1_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7>;
+			phy_data_page1_A00 = /bits/ 8
+				<0x25 0xEF 0x60 0x44 0x00 0x0F 0x18 0xE3>;
+			phy_data_page2_size = <1>;
+			phy_data_page2_addr = /bits/ 8
+				<0xE0>;
+			phy_data_page2_A00 = /bits/ 8
+				<0x01>;
+			do_toggle;
+			check_efuse;
+			//use_default_parameter;
+			is_double_sensitivity_mode;
+			ldo_page0_e4_compensate = <(-2)>;
+		};
+	};
+
+	dwc3_u2host: rtk_dwc3_u2host at 98013C00 {
+		compatible = "realtek,dwc3";
+		reg = <0x98013C00 0x200>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		delay_probe_work; //To delay probe work
+		status = "okay";
+
+		dwc3_u2host at 98029000 {
+			compatible = "synopsys,dwc3";
+			reg = <0x98029000 0x9000>;
+			interrupts = <0 21 4>;
+			snps,fixed_dwc3_globals_regs_start = <0x8100>;
+			usb-phy = <&dwc3_u2host_usb2phy>;
+			dr_mode = "host"; /*only host*/
+		};
+	};
+
+	dwc3_u3drd_usb3phy: dwc3_u3drd_usb3phy at 98013E10 {
+		compatible = "realtek,usb3phy";
+		reg = <0x98013E10 0x4>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		status = "okay";
+		port_index = <0>; /* index in u3 port */
+		phyN = <1>;
+		phy0 {
+			phy_data_size = <0x30>;
+			phy_data_addr = /bits/ 8
+				<0x00 0x01 0x02 0x03 0x04 0x05 0x06
+				0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D
+				0x0E 0x0F 0x10 0x11 0x12 0x13 0x14
+				0x15 0x16 0x17 0x18 0x19 0x1A 0x1B
+				0x1C 0x1D 0x1E 0x1F 0x20 0x21 0x22
+				0x23 0x24 0x25 0x26 0x27 0x28 0x29
+				0x2A 0x2B 0x2C 0x2D 0x2E 0x2F>;
+			phy_data_A00 = /bits/ 16
+				<0x400C 0xAC86 0x6042 0x2771 0x72F5 0x2AD3 0x0003
+					0x2E00 0x3591 0x925C 0xA608 0xA905 0xC000 0xEF1E
+					0x2010 0x8D50 0x000C 0x4C10 0xFC00 0x0C81 0xDE01
+					0x0000 0x0000 0x0000 0x0000 0x6000 0x0085 0x2014
+					0xC900 0xA03F 0xC2E0 0x7E00 0x705A 0xF645 0x0013
+					0xCB66 0x4770 0x126C 0x840A 0x01D6 0xF802 0xff04
+					0x3040 0x8028 0xFFFF 0xFFFF 0x0000 0x8600>;
+			phy_data_B00 = /bits/ 16
+				<0x400C 0xAC86 0x6042 0x2771 0x72F5 0x2AD3 0x0003
+					0x2E00 0x3591 0x924C 0xA608 0xB905 0xC000 0xEF1E
+					0x2010 0x8D50 0x000C 0x4C10 0xFC00 0x0C81 0xDE01
+					0x0000 0x0000 0x0000 0x0000 0x6000 0x0085 0x2014
+					0xC900 0xA03F 0xC2E0 0x7E00 0x705A 0xF645 0x0013
+					0xCB66 0x4770 0x126C 0x840A 0x01D6 0xF802 0xff04
+					0x3040 0x8028 0xFFFF 0xFFFF 0x0000 0x8600>;
+			do_toggle;
+			//use_default_parameter;
+		};
+	};
+
+	dwc3_u3drd_usb2phy: dwc3_u3drd_usb2phy at 98013E14 {
+		compatible = "realtek,usb2phy";
+		reg = <0x98013E14 0x4>, <0x98058280 0x4>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		status = "okay";
+		phyN = <1>;
+		phy0 {
+			phy_data_page0_size = <16>;
+			phy_data_page0_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xF0 0xF1
+				0xF2 0xF3 0xF4 0xF5 0xF6 0xF7>;
+			phy_data_page0_A00 = /bits/ 8
+				<0xE0 0x30 0x79 0x8D 0x6A 0x65 0x01 0x71 0xFC 0x8C
+				0x00 0x11 0x9B 0x00 0x00 0x0A>;
+			phy_data_page0_B00 = /bits/ 8
+				<0x18 0x30 0x79 0x8D 0x6A 0x65 0x01 0x71 0xFC 0x8C
+				0x00 0x11 0x9B 0x00 0x00 0x32>;
+			phy_data_page1_size = <8>;
+			phy_data_page1_addr = /bits/ 8
+				<0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7>;
+			phy_data_page1_A00 = /bits/ 8
+				<0x25 0xEF 0x60 0x44 0x00 0x0F 0x18 0xE3>;
+			phy_data_page2_size = <1>;
+			phy_data_page2_addr = /bits/ 8
+				<0xE0>;
+			phy_data_page2_A00 = /bits/ 8
+				<0x01>;
+			do_toggle;
+			check_efuse;
+			//use_default_parameter;
+			is_double_sensitivity_mode;
+			ldo_page0_e4_compensate = <(-2)>;
+		};
+	};
+
+	dwc3_u3drd: rtk_dwc3_u3drd at 98013E00 {
+		compatible = "realtek,dwc3";
+		reg = <0x98013E00 0x200>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		delay_probe_work; //To delay probe work
+		status = "okay";
+
+		dwc3_u3drd at 981f0000 {
+			compatible = "synopsys,dwc3";
+			reg = <0x98050000 0x9000>;
+			interrupts = <0 94 4>;
+			snps,fixed_dwc3_globals_regs_start = <0x8100>;
+			usb-phy = <&dwc3_u3drd_usb2phy &dwc3_u3drd_usb3phy>;
+			dr_mode = "host"; /*only host*/
+			snps,dis_u2_susphy_quirk; // Disable u2phy suspend for drd
+			snps,parkmode-disable-ss-quirk; // disable usb3.0 park mode
+
+			status = "okay";
+		};
+	};
+};
-- 
2.28.0



From ericwang at realtek.com  Fri Sep 18 04:36:52 2020
From: ericwang at realtek.com (Eric Wang)
Date: Fri, 18 Sep 2020 16:36:52 +0800
Subject: [RFC v1 0/3] Realtek DHC SoCs Ethernet driver
Message-ID: <20200918083655.20387-1-ericwang@realtek.com>

Hi Andreas,

Please help to review the refactored r8169soc driver.
It is planned to support all current Realtek DHC SoCs,
but only rtd1619 platform is tested.

To test this driver, extra patches about Realtek clk & reset APIs are needed.
Besides, pinctrl and nvmem parts in gmac node of device tree should be removed
temporarily.

Thanks,
Eric

Eric Wang (3):
  ethernet: r8169soc: new realtek soc ethernet driver
  doc: dt-bindings: net: Add bindings for Realtek DHC SoCs Ethernet
    driver
  arm64: dts: realtek: rtd16xx: Add rtd16xx gmac node

 .../bindings/net/realtek,r8169soc.txt         |   51 +
 arch/arm64/boot/dts/realtek/rtd16xx.dtsi      |   50 +
 drivers/net/ethernet/realtek/Kconfig          |   23 +-
 drivers/net/ethernet/realtek/Makefile         |    1 +
 drivers/net/ethernet/realtek/r8169soc.c       | 9285 +++++++++++++++++
 5 files changed, 9409 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/net/realtek,r8169soc.txt
 create mode 100644 drivers/net/ethernet/realtek/r8169soc.c

-- 
2.28.0



From ericwang at realtek.com  Fri Sep 18 04:36:55 2020
From: ericwang at realtek.com (Eric Wang)
Date: Fri, 18 Sep 2020 16:36:55 +0800
Subject: [RFC v1 3/3] arm64: dts: realtek: rtd16xx: Add rtd16xx gmac node
In-Reply-To: <20200918083655.20387-1-ericwang@realtek.com>
References: <20200918083655.20387-1-ericwang@realtek.com>
Message-ID: <20200918083655.20387-4-ericwang@realtek.com>

Signed-off-by: Eric Wang <ericwang at realtek.com>
---
 arch/arm64/boot/dts/realtek/rtd16xx.dtsi | 50 ++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/arch/arm64/boot/dts/realtek/rtd16xx.dtsi b/arch/arm64/boot/dts/realtek/rtd16xx.dtsi
index 4b944e2cf346..ea02c4d8d7bc 100644
--- a/arch/arm64/boot/dts/realtek/rtd16xx.dtsi
+++ b/arch/arm64/boot/dts/realtek/rtd16xx.dtsi
@@ -189,6 +189,56 @@ scpu_wrapper: syscon at 1d000 {
 				#size-cells = <1>;
 				ranges = <0x0 0x1d000 0x1000>;
 			};
+
+			gmac: r8169soc at 16000 {
+				compatible = "realtek,rtd16xx-r8169soc";
+				reg = <0x16000 0x1000>,   /* ETN */
+				      <0x7000 0x1000>, /* ISO */
+				      <0x1c000 0x2000>, /* SBX and SC_WRAP for ACP ctrl */
+				      <0x1c8000 0x2000>, /* SDS */
+				      <0x4e000 0x1000>; /* ISO pinmux */
+				interrupts = <0 22 4>;
+				pinctrl-names = "default",
+						"sgmii";
+				pinctrl-0 = <&etn_led_pins>;
+				pinctrl-1 = <&sgmii_mdio_pins>;
+				local-mac-address = [00 10 20 30 40 50];
+				output-mode = <0>; /* 0:embedded PHY, 3:SGMII to MAC, 4:SGMII to PHY */
+				sgmii-swing = <0>; /* 0:640mV, 1:380mV, 2:250mV, 3:190mV */
+				acp = <0>; /* 0: disable, 1: enable */
+				eee = <1>; /* 0: disable, 1: enable */
+				clocks = <&ic IC_CKE_ETN_250M>,
+					 <&ic IC_CKE_ETN_SYS>,
+					 <&cc CC_CKE_SDS>;
+				clock-names = "etn_250m",
+					      "etn_sys",
+					      "sds";
+				resets = <&ic IC_RSTN_GMAC>,
+					 <&ic IC_RSTN_GPHY>,
+					 <&cc CC_RSTN_SDS_REG>,
+					 <&cc CC_RSTN_SDS>,
+					 <&cc CC_RSTN_PCIE0_POWER>,
+					 <&cc CC_RSTN_PCIE0_PHY>,
+					 <&cc CC_RSTN_PCIE0_SGMII_MDIO>,
+					 <&cc CC_RSTN_PCIE0_PHY_MDIO>;
+				reset-names = "gmac",
+					      "gphy",
+					      "sds_reg",
+					      "sds",
+					      "pcie0_power",
+					      "pcie0_phy",
+					      "pcie0_sgmii_mdio",
+					      "pcie0_phy_mdio";
+				nvmem-cells = <&otp_etn_rc_cal>, <&otp_etn_r_amp_cal>;
+				nvmem-cell-names = "rc_k", "r_amp_k";
+				status = "okay";
+				phy_0 {
+					interrupt-parent = <&iso_irq_mux>;
+					interrupts = <28>, <29>, <30>;
+					irq-mask = <0x70000000>;
+					por-xv-mask = <0x00000111>;
+				};
+			};
 		};
 
 		gic: interrupt-controller at ff100000 {
-- 
2.28.0



From ericwang at realtek.com  Fri Sep 18 04:36:54 2020
From: ericwang at realtek.com (Eric Wang)
Date: Fri, 18 Sep 2020 16:36:54 +0800
Subject: [RFC v1 2/3] doc: dt-bindings: net: Add bindings for Realtek DHC SoCs
 Ethernet driver
In-Reply-To: <20200918083655.20387-1-ericwang@realtek.com>
References: <20200918083655.20387-1-ericwang@realtek.com>
Message-ID: <20200918083655.20387-3-ericwang@realtek.com>

Add Device Tree bindings documentation for Ethernet driver in Realtek DHC SoCs
RTD119x, RTD129x, RTD139x, RTD16xx, and RTD13xx.

Signed-off-by: Eric Wang <ericwang at realtek.com>
---
 .../bindings/net/realtek,r8169soc.txt         | 51 +++++++++++++++++++
 1 file changed, 51 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/realtek,r8169soc.txt

diff --git a/Documentation/devicetree/bindings/net/realtek,r8169soc.txt b/Documentation/devicetree/bindings/net/realtek,r8169soc.txt
new file mode 100644
index 000000000000..bf45dcc10cc5
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/realtek,r8169soc.txt
@@ -0,0 +1,51 @@
+Realtek r8169soc Ethernet driver
+
+Required properties:
+- compatible: should be one of below strings
+  "realtek,rtd119x-r8169soc" for Phoenix (RTD1195) SoC
+  "realtek,rtd129x-r8169soc" for Kylin (RTD1293, RTD1295, RTD1296) SoC
+  "realtek,rtd139x-r8169soc" for Hercules (RTD1395) SoC
+  "realtek,rtd16xx-r8169soc" for Thor (RTD1619) SoC
+  "realtek,rtd13xx-r8169soc" for Hank (RTD1319) SoC
+- reg: Address and length of the register set for the device
+- interrupts:  Should contain the MAC interrupts
+- pinctrl-names: names of pinmux set of corresponding output mode
+- pinctrl-0: the default pinmux set
+- local-mac-address: See ethernet-controller.yaml in the same directory.
+  This property is overwrited by bootloader.
+- output-mode: 0:embedded PHY (default),
+  1:RGMII to MAC (valid for RTD1295, and RTD1319),
+  2:RGMII to PHY (valid for RTD1319),
+  3:SGMII to MAC (valid for RTD1395, and RTD1619),
+  4:SGMII to PHY (valid for RTD1395, and RTD1619),
+  5:RMII (valid for RTD1319),
+  6:forced link (valid for RTD1319)
+- acp: support ACP (Accelerator Coherence Port) or not
+- eee: support EEE (Energy-Efficient Ethernet) or not
+- clocks: Must contain a handle for each entry in clock-names.
+- clock-names: The name of the clock listed in the clocks property.
+- resets: Must contain a handle for each entry in reset-names.
+- reset-names: The name of the reset listed in the resets property.
+
+Optional properties:
+- bypass: valid for RTD1395
+  1: ETN MAC bypasses efuse update flow. SW needs to take this sequence.
+  0: ETN MAC sets efuse update and efuse_rdy setting
+- wol-enable: enable WoL or not
+  BIT 0: WoL enable
+  BIT 1: CRC match (up to 16 match patterns)
+  BIT 2: WPD (Wake Packet Detection)
+- led-cfg: customized LED setting
+- pinctrl-1: the extra pinmux set of of corresponding output mode
+- ext-phy-id: 0 ~ 31, only valid when output-mode = "SGMII to PHY"
+- sgmii-swing: 0:640mV, 1:380mV, 2:250mV, 3:190mV,
+  only valid when output-mode = "SGMII to MAC" or "SGMII to PHY"
+- voltage: 1:1.8V, 2:2.5V, 3: 3.3V;
+  only valid when output-mode = "RGMII to MAC", "RGMII to PHY", or "RMII"
+- tx-delay: 0:0ns, 1:2ns;
+  only valid when output-mode = "RGMII to MAC", "RGMII to PHY", or "RMII"
+- rx-delay: 0:0ns, 1:2ns;
+  only valid when output-mode = "RGMII to MAC", "RGMII to PHY", or "RMII"
+- force-Gb-off: disable gigabit feature if defined
+- wake-mask[0-15]: pattern mask of a packet (for WoL via CRC match)
+- wake-crc[0-15]: CRC value of a masked packet (for WoL via CRC match)
-- 
2.28.0



From stanley_chang at realtek.com  Thu Sep 17 04:38:30 2020
From: stanley_chang at realtek.com (stanley_chang)
Date: Thu, 17 Sep 2020 16:38:30 +0800
Subject: [PATCH 05/10] usb: phy: rtk: add the new realtek usb2 phy and usb3
 phy drivers
In-Reply-To: <20200917083834.20782-1-stanley_chang@realtek.com>
References: <20200917083834.20782-1-stanley_chang@realtek.com>
Message-ID: <20200917083834.20782-6-stanley_chang@realtek.com>

The rtk USB2 PHY and USB3 PHY is used on realtek dwc3 usb controller.

Signed-off-by: stanley_chang <stanley_chang at realtek.com>
---
 drivers/usb/phy/Kconfig        |   12 +
 drivers/usb/phy/Makefile       |    2 +
 drivers/usb/phy/phy-rtk-usb.h  |   53 ++
 drivers/usb/phy/phy-rtk-usb2.c | 1121 ++++++++++++++++++++++++++++++++
 drivers/usb/phy/phy-rtk-usb3.c |  635 ++++++++++++++++++
 5 files changed, 1823 insertions(+)
 create mode 100644 drivers/usb/phy/phy-rtk-usb.h
 create mode 100644 drivers/usb/phy/phy-rtk-usb2.c
 create mode 100644 drivers/usb/phy/phy-rtk-usb3.c

diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index 24b4f091acb8..8837af01835b 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -184,4 +184,16 @@ config USB_ULPI_VIEWPORT
 	  Provides read/write operations to the ULPI phy register set for
 	  controllers with a viewport register (e.g. Chipidea/ARC controllers).
 
+config RTK_USB2PHY
+	tristate "Realtek USB2 PHY Controller Driver"
+	select USB_PHY
+	help
+	  Enable this to support Realtek USB2 phy controller SoC.
+
+config RTK_USB3PHY
+	tristate "Realtek USB3 PHY Controller Driver"
+	select USB_PHY
+	help
+	  Enable this to support Realtek USB3 phy controller SoC.
+
 endmenu
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index df1d99010079..07dffdb81a7c 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -24,3 +24,5 @@ obj-$(CONFIG_USB_MXS_PHY)		+= phy-mxs-usb.o
 obj-$(CONFIG_USB_ULPI)			+= phy-ulpi.o
 obj-$(CONFIG_USB_ULPI_VIEWPORT)		+= phy-ulpi-viewport.o
 obj-$(CONFIG_KEYSTONE_USB_PHY)		+= phy-keystone.o
+obj-$(CONFIG_RTK_USB2PHY)		+= phy-rtk-usb2.o
+obj-$(CONFIG_RTK_USB3PHY)		+= phy-rtk-usb3.o
diff --git a/drivers/usb/phy/phy-rtk-usb.h b/drivers/usb/phy/phy-rtk-usb.h
new file mode 100644
index 000000000000..bd28e27168fd
--- /dev/null
+++ b/drivers/usb/phy/phy-rtk-usb.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  phy-rtk-usb.h RTK usb phy header file
+ *
+ * copyright (c) 2017 realtek semiconductor corporation
+ *
+ */
+
+#ifndef __PHY_RTK_USB_H__
+#define __PHY_RTK_USB_H__
+
+struct rtk_usb_phy_s {
+	struct usb_phy phy;
+	struct device *dev;
+
+	int port_index;
+	int phyN;
+	void *reg_addr;
+	void *phy_data;
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+	struct dentry		*debug_dir;
+#endif
+};
+
+struct rtk_usb_phy_data_s {
+	char addr;
+	char data;
+};
+
+#define phy_read(addr)			__raw_readl(addr)
+#define phy_write(addr, val)	do { \
+	/* Do smp_wmb */ \
+	smp_wmb(); __raw_writel(val, addr); \
+} while (0)
+#define PHY_IO_TIMEOUT_MSEC		(50)
+
+static inline int utmi_wait_register(void __iomem *reg, u32 mask, u32 result)
+{
+	unsigned long timeout = jiffies + msecs_to_jiffies(PHY_IO_TIMEOUT_MSEC);
+
+	while (time_before(jiffies, timeout)) {
+		/* Do smp_rmb */
+		smp_rmb();
+		if ((phy_read(reg) & mask) == result)
+			return 0;
+		udelay(100);
+	}
+	pr_err("\033[0;32;31m can't program USB phy \033[m\n");
+	return -ETIMEDOUT;
+}
+
+#endif /* __PHY_RTK_USB_H__ */
diff --git a/drivers/usb/phy/phy-rtk-usb2.c b/drivers/usb/phy/phy-rtk-usb2.c
new file mode 100644
index 000000000000..6993a1948ec5
--- /dev/null
+++ b/drivers/usb/phy/phy-rtk-usb2.c
@@ -0,0 +1,1121 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  phy-rtk-usb2.c RTK usb2.0 PHY driver
+ *
+ * Copyright (C) 2017 Realtek Semiconductor Corporation
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/sys_soc.h>
+#include <linux/usb/phy.h>
+
+#include "phy-rtk-usb.h"
+
+#define RTK_USB2PHY_NAME "rtk-usb2phy"
+
+#define OFFEST_PHY_READ	0x20
+
+#define USB_ST_BUSY		BIT(23)
+#define MAX_PHY_DATA_SIZE 20
+
+#define PAGE0_0xE4 0xE4
+#define PAGE0_0xE7 0xE7
+#define PAGE1_0xE0 0xE0
+
+struct reg_addr {
+	void __iomem *REG_WRAP_VStatusOut2;
+	void __iomem *REG_GUSB2PHYACC0;
+	int vstatus_index;
+};
+
+struct phy_data {
+	int page0_size;
+	struct rtk_usb_phy_data_s *page0;
+	int page1_size;
+	struct rtk_usb_phy_data_s *page1;
+	int page2_size;
+	struct rtk_usb_phy_data_s *page2;
+
+	bool check_efuse;
+	int8_t efuse_usb_dc_cal;
+	bool do_toggle;
+	bool use_default_parameter;
+	bool is_double_sensitivity_mode;
+	s32 ldo_page0_e4_compensate;
+};
+
+static char rtk_usb_phy_read(struct reg_addr *regAddr, char addr)
+{
+	unsigned int regVal;
+	void __iomem *REG_GUSB2PHYACC0 = regAddr->REG_GUSB2PHYACC0;
+	int ret = 0;
+
+	addr -= OFFEST_PHY_READ;
+
+	// polling until VBusy == 0
+	ret = utmi_wait_register(REG_GUSB2PHYACC0, USB_ST_BUSY, 0);
+	if (ret)
+		return (char)ret;
+
+	// VCtrl = low nibble of addr, VLoadM = 1
+	regVal = BIT(25) | // vload
+			 ((addr & 0x0f) << 8);	// vcontrol
+	phy_write(REG_GUSB2PHYACC0, regVal);
+	ret = utmi_wait_register(REG_GUSB2PHYACC0, USB_ST_BUSY, 0);
+	if (ret)
+		return (char)ret;
+
+	// VCtrl = high nibble of addr, VLoadM = 1
+	regVal = BIT(25) | // vload
+			 ((addr & 0xf0) << 4);	// vcontrol
+	phy_write(REG_GUSB2PHYACC0, regVal);
+	ret = utmi_wait_register(REG_GUSB2PHYACC0, USB_ST_BUSY, 0);
+	if (ret)
+		return (char)ret;
+
+	/* rmb  for reg read */
+	smp_rmb();
+	regVal = phy_read(REG_GUSB2PHYACC0);
+
+	return (char) (regVal & 0xff);
+}
+
+static int rtk_usb_phy_write(struct reg_addr *regAddr, char addr, char data)
+{
+	unsigned int regVal;
+	void __iomem *REG_WRAP_VStatusOut2 = regAddr->REG_WRAP_VStatusOut2;
+	void __iomem *REG_GUSB2PHYACC0 = regAddr->REG_GUSB2PHYACC0;
+	int shift_bits = regAddr->vstatus_index * 8;
+	int ret = 0;
+
+	//write data to VStatusOut2 (data output to phy)
+	phy_write(REG_WRAP_VStatusOut2, (u32)data<<shift_bits);
+	ret = utmi_wait_register(REG_GUSB2PHYACC0, USB_ST_BUSY, 0);
+	if (ret)
+		return ret;
+
+	// VCtrl = low nibble of addr, VLoadM = 1
+	regVal = BIT(25) |
+			 ((addr & 0x0f) << 8);
+
+	phy_write(REG_GUSB2PHYACC0, regVal);
+	ret = utmi_wait_register(REG_GUSB2PHYACC0, USB_ST_BUSY, 0);
+	if (ret)
+		return ret;
+
+	// VCtrl = high nibble of addr, VLoadM = 1
+	regVal = BIT(25) |
+			 ((addr & 0xf0) << 4);
+
+	phy_write(REG_GUSB2PHYACC0, regVal);
+	ret = utmi_wait_register(REG_GUSB2PHYACC0, USB_ST_BUSY, 0);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int rtk_usb_phy_set_page(struct reg_addr *regAddr, int page)
+{
+#define SET_PAGE_OFFSET 0xf4
+#define SET_PAGE_0 0x9b
+#define SET_PAGE_1 0xbb
+#define SET_PAGE_2 0xdb
+
+	switch (page) {
+	case 0:
+		return rtk_usb_phy_write(regAddr, SET_PAGE_OFFSET, SET_PAGE_0);
+	case 1:
+		return rtk_usb_phy_write(regAddr, SET_PAGE_OFFSET, SET_PAGE_1);
+	case 2:
+		return rtk_usb_phy_write(regAddr, SET_PAGE_OFFSET, SET_PAGE_2);
+	default:
+		pr_err("%s error page=%d\n", __func__, page);
+	}
+
+	return -1;
+}
+
+static void rtk_usb2_phy_shutdown(struct usb_phy *phy)
+{
+	/* Todo */
+}
+
+static int updated_phy_parameter_by_efuse(struct rtk_usb_phy_s *rtk_phy,
+	    struct phy_data *phy_data, int index)
+{
+	u8 value = 0;
+	struct rtk_usb_phy_data_s *phy_page0_default_setting = phy_data->page0;
+	struct nvmem_cell *cell;
+	int port_index = rtk_phy->port_index + index;
+	int i;
+
+	cell = nvmem_cell_get(rtk_phy->dev, "usb-dc-cal");
+	if (IS_ERR(cell)) {
+		dev_warn(rtk_phy->dev, "%s failed to get usb-dc-cal: %ld\n",
+			    __func__, PTR_ERR(cell));
+	} else {
+		unsigned char *buf;
+		size_t buf_size;
+		int buf_index = port_index / 2;
+		int buf_shift = port_index % 2;
+		int value_size = 4;
+		int value_mask = (BIT(value_size) - 1);
+
+		buf = nvmem_cell_read(cell, &buf_size);
+
+		value = (buf[buf_index] >> (buf_shift * value_size)) &
+			    value_mask;
+
+		dev_dbg(rtk_phy->dev,
+			    "port_index=%d (buf_index=%d buf_shift=%d, buf_size=%d) value=%x mask=%x\n",
+			    port_index, buf_index, buf_shift, (int)buf_size,
+			    value, value_mask);
+		kfree(buf);
+		nvmem_cell_put(cell);
+	}
+
+	dev_dbg(rtk_phy->dev, "Get Efuse usb_dc_cal for port_index=%d value=%x\n",
+		    port_index, value);
+
+	if (value < 0x8)
+		phy_data->efuse_usb_dc_cal = (int8_t)value;
+	else
+		phy_data->efuse_usb_dc_cal = -(int8_t)(0x7 & value);
+
+	for (i = 0; i < phy_data->page0_size; i++) {
+		if ((phy_page0_default_setting + i)->addr == (char)PAGE0_0xE4) {
+			(phy_page0_default_setting + i)->data +=
+				    phy_data->efuse_usb_dc_cal;
+
+			dev_info(rtk_phy->dev,
+				    "Set addr %x value %x by efuse_usb_dc_cal=%d\n",
+				    (phy_page0_default_setting + i)->addr,
+				    (phy_page0_default_setting + i)->data,
+				    phy_data->efuse_usb_dc_cal);
+		}
+	}
+	return 0;
+}
+
+static void do_rtk_usb2_phy_toggle(struct rtk_usb_phy_s *rtk_phy,
+	    int index, bool isConnect);
+
+static int do_rtk_usb2_phy_init(struct usb_phy *phy, int index)
+{
+	int i;
+	struct rtk_usb_phy_s *rtk_phy = (struct rtk_usb_phy_s *) phy;
+	struct reg_addr *regAddr;
+	struct phy_data *phy_data;
+	struct rtk_usb_phy_data_s *phy_page_setting;
+	s32 ldo_page0_e4_compensate = 0;
+
+	if (!rtk_phy) {
+		pr_err("%s, rtk_phy is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	dev_info(phy->dev, "%s Init RTK USB 2.0 PHY phy#%d\n",
+		    __func__, index);
+
+	regAddr = &((struct reg_addr *)rtk_phy->reg_addr)[index];
+	phy_data = &((struct phy_data *)rtk_phy->phy_data)[index];
+
+	if (!phy_data) {
+		pr_err("%s, phy_data is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if (phy_data->use_default_parameter) {
+		dev_info(phy->dev, "%s phy#%d use default parameter\n",
+			    __func__, index);
+		goto do_toggle;
+	}
+
+	if (phy_data->check_efuse)
+		updated_phy_parameter_by_efuse(rtk_phy, phy_data, index);
+
+	/* Set page 0 */
+	phy_page_setting = phy_data->page0;
+	rtk_usb_phy_set_page(regAddr, 0);
+
+	for (i = 0; i < phy_data->page0_size; i++) {
+		u8 addr = (phy_page_setting + i)->addr;
+		u8 data = (phy_page_setting + i)->data;
+
+		if (addr == PAGE0_0xE4)
+			data = (u8)((s32)data + ldo_page0_e4_compensate);
+
+		if (rtk_usb_phy_write(regAddr, addr, data)) {
+			dev_err(phy->dev,
+				    "[%s:%d] Error page0 addr=0x%x value=0x%x\n",
+				    __func__, __LINE__, addr, data);
+			return -1;
+		}
+		dev_dbg(phy->dev, "[%s:%d] Good page0 addr=0x%x value=0x%x\n",
+			    __func__, __LINE__, addr,
+			    rtk_usb_phy_read(regAddr, addr));
+	}
+
+	/* Set page 1 */
+	phy_page_setting = phy_data->page1;
+	rtk_usb_phy_set_page(regAddr, 1);
+
+	for (i = 0; i < phy_data->page1_size; i++) {
+		u8 addr = (phy_page_setting + i)->addr;
+		u8 data = (phy_page_setting + i)->data;
+
+		if (rtk_usb_phy_write(regAddr, addr, data)) {
+			dev_err(phy->dev,
+				    "[%s:%d] Error page1 addr=0x%x value=0x%x\n",
+				    __func__, __LINE__,
+				    addr, data);
+			return -1;
+		}
+		dev_dbg(phy->dev, "[%s:%d] Good page1 addr=0x%x value=0x%x\n",
+			    __func__, __LINE__, addr,
+			    rtk_usb_phy_read(regAddr, addr));
+	}
+
+	if (phy_data->page2_size == 0)
+		goto do_toggle;
+
+	/* Set page 2 */
+	phy_page_setting = phy_data->page2;
+	rtk_usb_phy_set_page(regAddr, 2);
+
+	for (i = 0; i < phy_data->page2_size; i++) {
+		u8 addr = (phy_page_setting + i)->addr;
+		u8 data = (phy_page_setting + i)->data;
+
+		if (rtk_usb_phy_write(regAddr, addr, data)) {
+			dev_err(phy->dev,
+				    "[%s:%d] Error page2 addr=0x%x value=0x%x\n",
+				    __func__, __LINE__, addr, data);
+			return -1;
+		}
+		dev_dbg(phy->dev, "[%s:%d] Good page2 addr=0x%x value=0x%x\n",
+			    __func__, __LINE__,
+			    (phy_page_setting + i)->addr,
+			    rtk_usb_phy_read(regAddr,
+			      (phy_page_setting + i)->addr));
+	}
+
+do_toggle:
+	do_rtk_usb2_phy_toggle(rtk_phy, index, false);
+
+	return 0;
+}
+
+static int rtk_usb2_phy_init(struct usb_phy *phy)
+{
+	struct rtk_usb_phy_s *rtk_phy = (struct rtk_usb_phy_s *) phy;
+	int i, ret = 0;
+	unsigned long phy_init_time = jiffies;
+
+	dev_info(phy->dev, "%s Init RTK USB 2.0 PHY\n", __func__);
+	for (i = 0; i < rtk_phy->phyN; i++)
+		ret = do_rtk_usb2_phy_init(phy, i);
+
+	dev_info(phy->dev, "%s Initialized RTK USB 2.0 PHY (take %dms)\n",
+		    __func__,
+		    jiffies_to_msecs(jiffies - phy_init_time));
+	return ret;
+}
+
+static void do_rtk_usb2_phy_toggle(struct rtk_usb_phy_s *rtk_phy,
+	    int index, bool isConnect)
+{
+	struct reg_addr *regAddr;
+	struct phy_data *phy_data;
+	struct rtk_usb_phy_data_s *phy_page_setting;
+	int i;
+
+	if (!rtk_phy) {
+		pr_err("%s phy_data is NULL\n", __func__);
+		return;
+	}
+
+	regAddr = &((struct reg_addr *)rtk_phy->reg_addr)[index];
+	phy_data = &((struct phy_data *)rtk_phy->phy_data)[index];
+
+	if (!phy_data) {
+		dev_err(rtk_phy->dev, "%s phy_data is NULL\n", __func__);
+		return;
+	}
+
+	if (!phy_data->do_toggle)
+		return;
+
+	/* Set page 1 */
+	phy_page_setting = phy_data->page1;
+	rtk_usb_phy_set_page(regAddr, 1);
+
+	for (i = 0; i < phy_data->page1_size; i++) {
+		u8 addr = (phy_page_setting + i)->addr;
+		u8 data = (phy_page_setting + i)->data;
+
+		if (addr == PAGE1_0xE0) {
+			dev_info(rtk_phy->dev,
+				    "%s ########## to toggle Page1 0xe0 BIT(2)\n",
+				    __func__);
+			rtk_usb_phy_write(regAddr, addr, data & (~BIT(2)));
+			mdelay(1);
+			rtk_usb_phy_write(regAddr, addr, data | (BIT(2)));
+		}
+	}
+
+	if (phy_data->is_double_sensitivity_mode)
+		return;
+
+	/* Set page 0 */
+	phy_page_setting = phy_data->page0;
+	rtk_usb_phy_set_page(regAddr, 0);
+
+	for (i = 0; i < phy_data->page0_size; i++) {
+		u8 addr = (phy_page_setting + i)->addr;
+		u8 data = (phy_page_setting + i)->data;
+
+		if (addr == PAGE0_0xE7) {
+			if (isConnect) {
+				rtk_usb_phy_write(regAddr, addr, data &
+					    (~(BIT(4) | BIT(5) | BIT(6))));
+			} else {
+				rtk_usb_phy_write(regAddr, addr, data |
+					    (BIT(4) | BIT(5) | BIT(6)));
+			}
+			dev_info(rtk_phy->dev,
+				    "%s %sconnect to set Page0 0xE7=%x\n",
+				    __func__,
+				    isConnect?"":"dis",
+				    rtk_usb_phy_read(regAddr, addr));
+		}
+	}
+}
+
+void rtk_usb2_phy_toggle(struct usb_phy *usb2_phy, bool isConnect, int port)
+{
+	int index = port;
+	struct rtk_usb_phy_s *rtk_phy = NULL;
+
+	if (usb2_phy != NULL && usb2_phy->dev != NULL)
+		rtk_phy = dev_get_drvdata(usb2_phy->dev);
+
+	if (rtk_phy == NULL) {
+		pr_err("%s %d ERROR! NO this device\n", __func__, __LINE__);
+		return;
+	}
+	if (index > rtk_phy->phyN) {
+		pr_err("%s %d ERROR! port=%d > phyN=%d\n",
+			    __func__, __LINE__, index, rtk_phy->phyN);
+		return;
+	}
+
+	do_rtk_usb2_phy_toggle(rtk_phy, index, isConnect);
+
+}
+EXPORT_SYMBOL(rtk_usb2_phy_toggle);
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+static struct dentry *create_phy_debug_root(void)
+{
+	struct dentry *phy_debug_root;
+
+	phy_debug_root = debugfs_lookup("phy", usb_debug_root);
+	if (!phy_debug_root) {
+		phy_debug_root = debugfs_create_dir("phy", usb_debug_root);
+		if (!phy_debug_root)
+			pr_err("%s Error phy_debug_root is NULL\n", __func__);
+		else
+			pr_info("%s Create phy_debug_root folder\n", __func__);
+	}
+
+	return phy_debug_root;
+}
+
+static int rtk_usb2_parameter_show(struct seq_file *s, void *unused)
+{
+	struct rtk_usb_phy_s		*rtk_phy = s->private;
+	int i, index;
+
+	for (index = 0; index < rtk_phy->phyN; index++) {
+		struct reg_addr *regAddr =
+			    &((struct reg_addr *)rtk_phy->reg_addr)[index];
+		struct phy_data *phy_data =
+			    &((struct phy_data *)rtk_phy->phy_data)[index];
+		struct rtk_usb_phy_data_s *phy_page_setting;
+
+		seq_printf(s, "PHY %d:\n", index);
+
+		seq_puts(s, "Page 0:\n");
+		/* Set page 0 */
+		phy_page_setting = phy_data->page0;
+		rtk_usb_phy_set_page(regAddr, 0);
+
+		for (i = 0; i < phy_data->page0_size; i++) {
+			u8 addr = (phy_page_setting + i)->addr;
+			u8 data = (phy_page_setting + i)->data;
+
+			seq_printf(s, "Page 0: addr=0x%x data=0x%02x ==> value=0x%02x\n",
+				    addr, data,
+				    rtk_usb_phy_read(regAddr, addr));
+		}
+
+		seq_puts(s, "Page 1:\n");
+		/* Set page 1 */
+		phy_page_setting = phy_data->page1;
+		rtk_usb_phy_set_page(regAddr, 1);
+
+		for (i = 0; i < phy_data->page1_size; i++) {
+			u8 addr = (phy_page_setting + i)->addr;
+			u8 data = (phy_page_setting + i)->data;
+
+			seq_printf(s, "Page 1: addr=0x%x data=0x%02x ==> value=0x%02x\n",
+				    addr, data,
+				    rtk_usb_phy_read(regAddr, addr));
+		}
+
+		if (phy_data->page2_size == 0)
+			goto out;
+
+		seq_puts(s, "Page 2:\n");
+		/* Set page 2 */
+		phy_page_setting = phy_data->page2;
+		rtk_usb_phy_set_page(regAddr, 2);
+
+		for (i = 0; i < phy_data->page2_size; i++) {
+			u8 addr = (phy_page_setting + i)->addr;
+			u8 data = (phy_page_setting + i)->data;
+
+			seq_printf(s, "Page 2: addr=0x%x data=0x%02x ==> value=0x%02x\n",
+				    addr, data,
+				    rtk_usb_phy_read(regAddr, addr));
+		}
+	}
+
+out:
+	return 0;
+}
+
+static int rtk_usb2_parameter_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, rtk_usb2_parameter_show, inode->i_private);
+}
+
+static const struct file_operations rtk_usb2_parameter_fops = {
+	.open			= rtk_usb2_parameter_open,
+	.read			= seq_read,
+	.llseek			= seq_lseek,
+	.release		= single_release,
+};
+
+static int rtk_usb2_set_parameter_show(struct seq_file *s, void *unused)
+{
+	//struct rtk_usb_phy_s	*rtk_phy = s->private;
+
+	seq_puts(s, "Set Phy parameter by following command\n");
+	seq_puts(s, "echo \"phy_num page addr value\" > set_parameter\n");
+	seq_puts(s, "echo \"0 page0 0xE1 0x30\" > set_parameter\n");
+	seq_puts(s, "echo \"0 page1 0xE1 0xEF\" > set_parameter\n");
+
+	return 0;
+}
+
+static int rtk_usb2_set_parameter_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, rtk_usb2_set_parameter_show, inode->i_private);
+}
+
+static ssize_t rtk_usb2_set_parameter_write(struct file *file,
+		const char __user *ubuf, size_t count, loff_t *ppos)
+{
+	struct seq_file		*s = file->private_data;
+	struct rtk_usb_phy_s		*rtk_phy = s->private;
+	struct reg_addr *regAddr;
+	struct phy_data *phy_data;
+	struct rtk_usb_phy_data_s *phy_page_setting;
+	int page_size = 0;
+	int ret = 0;
+	char			buffer[40];
+	char *buf = buffer;
+	int i, index;
+	u32 addr;
+	u32 value;
+
+	if (copy_from_user(&buffer, ubuf,
+		    min_t(size_t, sizeof(buffer) - 1, count)))
+		return -EFAULT;
+
+	ret = kstrtoint(buf, 0, &index);
+	if (ret < 0)
+		return -EFAULT;
+
+	buf = buf + 2;
+	regAddr = &((struct reg_addr *)rtk_phy->reg_addr)[index];
+	phy_data = &((struct phy_data *)rtk_phy->phy_data)[index];
+
+	if (!strncmp(buf, "page0", 5)) {
+		buf = buf + 5;
+		buf = skip_spaces(buf);
+		ret = sscanf(buf, "%x %x", &addr, &value);
+		if (ret < 0)
+			return -EFAULT;
+
+		phy_page_setting = phy_data->page0;
+		page_size = phy_data->page0_size;
+		rtk_usb_phy_set_page(regAddr, 0);
+		dev_dbg(rtk_phy->dev, "%s page0 addr = 0x%x, value = 0x%x\n",
+			    __func__, addr, value);
+
+	} else if (!strncmp(buf, "page1", 5)) {
+		buf = buf + 5;
+		buf = skip_spaces(buf);
+		ret = sscanf(buf, "%x %x", &addr, &value);
+		if (ret < 0)
+			return -EFAULT;
+
+		phy_page_setting = phy_data->page1;
+		page_size = phy_data->page1_size;
+		rtk_usb_phy_set_page(regAddr, 1);
+		dev_dbg(rtk_phy->dev, "%s page1 addr = 0x%x, value = 0x%x\n",
+			    __func__, addr, value);
+	} else
+		dev_err(rtk_phy->dev, "UNKNOWN input (%s)", buf);
+
+	for (i = 0; i < page_size; i++) {
+		if ((phy_page_setting + i)->addr == addr) {
+			(phy_page_setting + i)->data = value;
+			if (rtk_usb_phy_write(regAddr, addr, value))
+				dev_err(rtk_phy->dev,
+					    "[%s:%d] Error: addr=0x%x value=0x%x\n",
+					    __func__, __LINE__, addr, value);
+			else
+				dev_dbg(rtk_phy->dev,
+					    "[%s:%d] Good: addr=0x%x value=0x%x\n",
+					    __func__, __LINE__, addr,
+					    rtk_usb_phy_read(regAddr, addr));
+		}
+	}
+
+	return count;
+}
+
+static const struct file_operations rtk_usb2_set_parameter_fops = {
+	.open			= rtk_usb2_set_parameter_open,
+	.write			= rtk_usb2_set_parameter_write,
+	.read			= seq_read,
+	.llseek			= seq_lseek,
+	.release		= single_release,
+};
+
+static int rtk_usb2_toggle_show(struct seq_file *s, void *unused)
+{
+	struct rtk_usb_phy_s *rtk_phy = s->private;
+	struct phy_data *phy_data;
+	int i;
+
+	for (i = 0; i < rtk_phy->phyN; i++) {
+		phy_data = &((struct phy_data *)rtk_phy->phy_data)[i];
+		seq_printf(s, "Now phy#%d do_toggle is %s.\n",
+			    i, phy_data->do_toggle?"Enable":"Disable");
+	}
+	seq_puts(s, "ehco 1 to enable toggle phy parameter.\n");
+
+	return 0;
+}
+
+static int rtk_usb2_toggle_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, rtk_usb2_toggle_show, inode->i_private);
+}
+
+static ssize_t rtk_usb2_toggle_write(struct file *file,
+		const char __user *ubuf, size_t count, loff_t *ppos)
+{
+	struct seq_file		*s = file->private_data;
+	struct rtk_usb_phy_s		*rtk_phy = s->private;
+	char			buf[32];
+	struct phy_data *phy_data;
+	bool enable = false;
+	int i;
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	if (!strncmp(buf, "1", 1))
+		enable = true;
+
+	for (i = 0; i < rtk_phy->phyN; i++) {
+		phy_data = &((struct phy_data *)rtk_phy->phy_data)[i];
+		phy_data->do_toggle = enable;
+		dev_info(rtk_phy->dev, "Set phy#%d do_toggle is %s.\n",
+			    i, phy_data->do_toggle?"Enable":"Disable");
+	}
+
+	return count;
+}
+
+static const struct file_operations rtk_usb2_toggle_fops = {
+	.open			= rtk_usb2_toggle_open,
+	.write			= rtk_usb2_toggle_write,
+	.read			= seq_read,
+	.llseek			= seq_lseek,
+	.release		= single_release,
+};
+
+static inline void create_debug_files(struct rtk_usb_phy_s *rtk_phy)
+{
+	struct dentry *phy_debug_root = NULL;
+
+	dev_dbg(rtk_phy->dev, "%s", __func__);
+
+	phy_debug_root = create_phy_debug_root();
+
+	if (!phy_debug_root) {
+		dev_err(rtk_phy->dev, "%s Error phy_debug_root is NULL",
+			    __func__);
+		return;
+	}
+
+	rtk_phy->debug_dir = debugfs_create_dir(dev_name(rtk_phy->dev),
+		    phy_debug_root);
+	if (!rtk_phy->debug_dir) {
+		dev_err(rtk_phy->dev, "%s Error debug_dir is NULL", __func__);
+		return;
+	}
+
+	if (!debugfs_create_file("parameter", 0444, rtk_phy->debug_dir, rtk_phy,
+		    &rtk_usb2_parameter_fops))
+		goto file_error;
+
+	if (!debugfs_create_file("set_parameter", 0644,
+		    rtk_phy->debug_dir, rtk_phy, &rtk_usb2_set_parameter_fops))
+		goto file_error;
+
+	if (!debugfs_create_file("toggle", 0644,
+		    rtk_phy->debug_dir, rtk_phy, &rtk_usb2_toggle_fops))
+		goto file_error;
+
+	return;
+
+file_error:
+	debugfs_remove_recursive(rtk_phy->debug_dir);
+}
+#endif //CONFIG_DYNAMIC_DEBUG
+
+#define DEFAULT_CHIP_REVISION 0xA00
+#define MAX_CHIP_REVISION 0xC00
+
+static int __get_chip_revision(void)
+{
+	int chip_revision = 0xFFF;
+	char revision[] = "FFF";
+	struct soc_device_attribute soc_att[] = {{.revision = revision}, {}};
+	struct soc_device_attribute *soc_att_match = NULL;
+
+	while (soc_att_match == NULL) {
+		chip_revision--;
+
+		if (chip_revision <= DEFAULT_CHIP_REVISION)
+			break;
+		if (chip_revision > MAX_CHIP_REVISION)
+			chip_revision = MAX_CHIP_REVISION;
+		else if ((chip_revision & 0xFF) > 0xF)
+			chip_revision = (chip_revision & 0xF00) + 0xF;
+
+		snprintf(revision, 4, "%X", chip_revision);
+
+		soc_att_match = (struct soc_device_attribute *)
+			    soc_device_match(soc_att);
+	}
+
+	if (soc_att_match) {
+		pr_debug("%s get chip_revision %x\n", __func__, chip_revision);
+		return chip_revision;
+	}
+
+	pr_err("%s ERROR: no match chip_revision\n", __func__);
+	return DEFAULT_CHIP_REVISION;
+}
+
+static int __get_phy_parameter(struct rtk_usb_phy_s *rtk_usb_phy, int index)
+{
+	struct device *dev = rtk_usb_phy->dev;
+	struct reg_addr *addr =
+		    &((struct reg_addr *)rtk_usb_phy->reg_addr)[index];
+	struct phy_data *phy_data =
+		    &((struct phy_data *)rtk_usb_phy->phy_data)[index];
+	int phy_data_page0_size, phy_data_page1_size;
+	int phy_data_page2_size;
+	char phy_name[5];
+	struct device_node *sub_node;
+	char tmp_addr[MAX_PHY_DATA_SIZE];
+	char tmp_data[MAX_PHY_DATA_SIZE];
+	int i, chip_revision, revision, ret = 0;
+
+	chip_revision = __get_chip_revision();
+
+	dev_dbg(dev, "%s: Chip revision is %x\n", __func__, chip_revision);
+
+	addr->REG_WRAP_VStatusOut2 = of_iomap(dev->of_node, 0);
+	addr->REG_GUSB2PHYACC0     = of_iomap(dev->of_node, index + 1);
+	addr->vstatus_index = index;
+	dev_dbg(dev, "%s %d #%d REG_WRAP_VStatusOut2=%p\n",
+		    __func__, __LINE__,
+		    index, addr->REG_WRAP_VStatusOut2);
+	dev_dbg(dev, "%s %d #%d REG_GUSB2PHYACC0=%p\n",
+		    __func__, __LINE__,
+		    index, addr->REG_GUSB2PHYACC0);
+
+	snprintf(phy_name, 5, "phy%d", index);
+
+	sub_node = of_get_child_by_name(dev->of_node, phy_name);
+
+	if (!sub_node) {
+		dev_err(dev, "%s %d No device sub node for %s\n",
+			    __func__, __LINE__, phy_name);
+		ret = -ENODEV;
+		goto err;
+	}
+
+	ret = of_property_read_u32_index(sub_node,
+		    "phy_data_page0_size", 0, &phy_data_page0_size);
+	if (ret)
+		goto err;
+
+	ret = of_property_read_u32_index(sub_node,
+		    "phy_data_page1_size", 0, &phy_data_page1_size);
+	if (ret)
+		goto err;
+
+	dev_dbg(dev, "%s %d #%d phy_data_page0_size=%d, phy_data_page1_size=%d\n",
+		    __func__, __LINE__, index,
+		    phy_data_page0_size, phy_data_page1_size);
+
+	if (phy_data_page0_size > MAX_PHY_DATA_SIZE ||
+		    phy_data_page1_size > MAX_PHY_DATA_SIZE) {
+		dev_err(dev, "%s phy_data size > MAX_PHY_DATA_SIZE\n",
+			    __func__);
+		goto err;
+	}
+
+	ret = of_property_read_u32_index(sub_node,
+		    "phy_data_page2_size", 0, &phy_data_page2_size);
+	if (ret)
+		phy_data_page2_size = 0;
+	dev_dbg(dev, "%s %d #%d phy_data_page2_size=%d\n",
+		    __func__, __LINE__, index,
+		    phy_data_page2_size);
+
+	if (phy_data_page2_size > MAX_PHY_DATA_SIZE) {
+		dev_err(dev, "%s page2 phy_data size=%d > MAX_PHY_DATA_SIZE\n",
+			    __func__, phy_data_page2_size);
+		goto err;
+	}
+
+	phy_data->page0_size = phy_data_page0_size;
+	phy_data->page0 = devm_kzalloc(dev,
+		    sizeof(struct rtk_usb_phy_data_s) *
+			phy_data_page0_size,
+		    GFP_KERNEL);
+	if (!phy_data->page0) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	phy_data->page1_size = phy_data_page1_size;
+	phy_data->page1 = devm_kzalloc(dev,
+		    sizeof(struct rtk_usb_phy_data_s) *
+			phy_data_page1_size,
+		    GFP_KERNEL);
+	if (!phy_data->page1) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	phy_data->page2_size = phy_data_page2_size;
+	if (phy_data->page2_size > 0) {
+		phy_data->page2 = devm_kzalloc(dev,
+			    sizeof(struct rtk_usb_phy_data_s) *
+				phy_data->page2_size,
+			    GFP_KERNEL);
+		if (!phy_data->page2) {
+			ret = -ENOMEM;
+			goto err;
+		}
+	}
+
+	ret = of_property_read_u8_array(sub_node, "phy_data_page0_addr",
+		    tmp_addr, phy_data_page0_size);
+	if (ret)
+		goto err;
+
+	revision = chip_revision;
+	while (revision >= DEFAULT_CHIP_REVISION) {
+		char phy_data_revision[20] = {0};
+
+		snprintf(phy_data_revision, 19, "phy_data_page0_%X", revision);
+
+		ret = of_property_read_u8_array(sub_node, phy_data_revision,
+			    tmp_data, phy_data_page0_size);
+		if (!ret) {
+			dev_dbg(dev, "%s load %s parameter\n",
+				    __func__, phy_data_revision);
+			break;
+		}
+		revision--;
+		if ((revision & 0xFF) > 0xF)
+			revision = (revision & 0xF00) + 0xF;
+	}
+
+	/* For old device tree */
+	if (ret) {
+		ret = of_property_read_u8_array(sub_node, "phy_data_page0_data",
+			    tmp_data, phy_data_page0_size);
+		if (ret)
+			goto err;
+		else
+			dev_info(dev, "%s load page0 parameter\n",
+				    __func__);
+	}
+
+	for (i = 0; i < phy_data_page0_size; i++) {
+		struct rtk_usb_phy_data_s *phy_data_page0 =
+			    (phy_data->page0 + i);
+
+		phy_data_page0->addr = tmp_addr[i];
+		phy_data_page0->data = tmp_data[i];
+	}
+
+	ret = of_property_read_u8_array(sub_node, "phy_data_page1_addr",
+		    tmp_addr, phy_data_page1_size);
+	if (ret)
+		goto err;
+
+	revision = chip_revision;
+	while (revision >= DEFAULT_CHIP_REVISION) {
+		char phy_data_revision[20] = {0};
+
+		snprintf(phy_data_revision, 19, "phy_data_page1_%X", revision);
+
+		ret = of_property_read_u8_array(sub_node, phy_data_revision,
+			    tmp_data, phy_data_page1_size);
+		if (!ret) {
+			dev_dbg(dev, "%s load %s parameter\n",
+				    __func__, phy_data_revision);
+			break;
+		}
+		revision--;
+		if ((revision & 0xFF) > 0xF)
+			revision = (revision & 0xF00) + 0xF;
+	}
+
+	/* For old device tree */
+	if (ret) {
+		ret = of_property_read_u8_array(sub_node, "phy_data_page1_data",
+			    tmp_data, phy_data_page1_size);
+		if (ret)
+			goto err;
+		else
+			dev_info(dev, "%s load page1 parameter\n",
+				    __func__);
+	}
+
+	for (i = 0; i < phy_data_page1_size; i++) {
+		struct rtk_usb_phy_data_s *phy_data_page1 =
+			    (phy_data->page1 + i);
+
+		phy_data_page1->addr = tmp_addr[i];
+		phy_data_page1->data = tmp_data[i];
+	}
+
+	if (phy_data->page2_size > 0) {
+		ret = of_property_read_u8_array(sub_node,
+			    "phy_data_page2_addr",
+			    tmp_addr, phy_data->page2_size);
+		if (ret)
+			goto err;
+
+		revision = chip_revision;
+		while (revision >= DEFAULT_CHIP_REVISION) {
+			char phy_data_revision[20] = {0};
+
+			snprintf(phy_data_revision, 19, "phy_data_page2_%X",
+				    revision);
+
+			ret = of_property_read_u8_array(sub_node,
+				    phy_data_revision,
+				    tmp_data, phy_data_page2_size);
+			if (!ret) {
+				dev_dbg(dev, "%s load %s parameter\n",
+					    __func__, phy_data_revision);
+				break;
+			}
+			revision--;
+			if ((revision & 0xFF) > 0xF)
+				revision = (revision & 0xF00) + 0xF;
+		}
+
+		/* For old device tree */
+		if (ret) {
+			ret = of_property_read_u8_array(sub_node,
+				    "phy_data_page2_data",
+				    tmp_data, phy_data->page2_size);
+			if (ret)
+				goto err;
+			else
+				dev_info(dev, "%s load page2 parameter\n",
+					    __func__);
+		}
+		for (i = 0; i < phy_data->page2_size; i++) {
+			struct rtk_usb_phy_data_s *phy_data_page2 =
+				    (phy_data->page2 + i);
+			phy_data_page2->addr = tmp_addr[i];
+			phy_data_page2->data = tmp_data[i];
+		}
+	}
+
+	if (of_property_read_bool(sub_node, "do_toggle"))
+		phy_data->do_toggle = true;
+	else
+		phy_data->do_toggle = false;
+
+	if (of_property_read_bool(sub_node, "check_efuse"))
+		phy_data->check_efuse = true;
+	else
+		phy_data->check_efuse = false;
+
+	if (of_property_read_bool(sub_node, "use_default_parameter"))
+		phy_data->use_default_parameter = true;
+	else
+		phy_data->use_default_parameter = false;
+
+	if (of_property_read_bool(sub_node,
+		    "is_double_sensitivity_mode"))
+		phy_data->is_double_sensitivity_mode = true;
+	else
+		phy_data->is_double_sensitivity_mode = false;
+
+	if (of_property_read_s32(sub_node,
+		 "ldo_page0_e4_compensate", &phy_data->ldo_page0_e4_compensate))
+		phy_data->ldo_page0_e4_compensate = 0;
+
+err:
+	return ret;
+}
+
+static int rtk_usb2phy_probe(struct platform_device *pdev)
+{
+	struct rtk_usb_phy_s *rtk_usb_phy;
+	struct device *dev = &pdev->dev;
+	int index, ret = 0;
+	int port_index, phyN;
+
+	rtk_usb_phy = devm_kzalloc(dev, sizeof(*rtk_usb_phy), GFP_KERNEL);
+	if (!rtk_usb_phy)
+		return -ENOMEM;
+
+	rtk_usb_phy->dev			= &pdev->dev;
+	rtk_usb_phy->phy.dev		= rtk_usb_phy->dev;
+	rtk_usb_phy->phy.label		= RTK_USB2PHY_NAME;
+	rtk_usb_phy->phy.init		= rtk_usb2_phy_init;
+	rtk_usb_phy->phy.shutdown	= rtk_usb2_phy_shutdown;
+
+	if (!dev->of_node) {
+		dev_err(dev, "%s %d No device node\n", __func__, __LINE__);
+		goto err;
+	}
+
+	ret = of_property_read_u32_index(dev->of_node, "port_index", 0,
+		    &port_index);
+	if (ret)
+		port_index = -1;
+
+	ret = of_property_read_u32_index(dev->of_node, "phyN", 0,
+		    &phyN);
+	if (ret)
+		goto err;
+
+	dev_dbg(dev, "%s %d port_index=%d phyN=%d\n",
+		    __func__, __LINE__, port_index, phyN);
+
+	rtk_usb_phy->port_index = port_index;
+	rtk_usb_phy->phyN = phyN;
+	rtk_usb_phy->reg_addr = devm_kzalloc(dev,
+		    sizeof(struct reg_addr) * phyN, GFP_KERNEL);
+	if (!rtk_usb_phy->reg_addr)
+		return -ENOMEM;
+
+	rtk_usb_phy->phy_data = devm_kzalloc(dev,
+		    sizeof(struct phy_data) * phyN,
+		    GFP_KERNEL);
+
+	if (!rtk_usb_phy->phy_data)
+		return -ENOMEM;
+
+	for (index = 0; index < phyN; index++) {
+		ret = __get_phy_parameter(rtk_usb_phy, index);
+		if (ret) {
+			dev_err(dev, "%s %d: __get_phy_parameter fail ret=%d\n",
+				    __func__, __LINE__, ret);
+			goto err;
+		}
+	}
+
+	platform_set_drvdata(pdev, rtk_usb_phy);
+
+	ret = usb_add_phy_dev(&rtk_usb_phy->phy);
+	if (ret)
+		goto err;
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+	create_debug_files(rtk_usb_phy);
+#endif
+
+	dev_info(&pdev->dev, "%s Probe RTK USB 2.0 PHY\n", __FILE__);
+err:
+	return ret;
+}
+
+static int rtk_usb2phy_remove(struct platform_device *pdev)
+{
+	struct rtk_usb_phy_s *rtk_usb_phy = platform_get_drvdata(pdev);
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+	debugfs_remove_recursive(rtk_usb_phy->debug_dir);
+#endif
+
+	usb_remove_phy(&rtk_usb_phy->phy);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id usbphy_rtk_dt_match[] = {
+	{ .compatible = "realtek,usb2phy", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, usbphy_rtk_dt_match);
+#endif
+
+static struct platform_driver rtk_usb2phy_driver = {
+	.probe		= rtk_usb2phy_probe,
+	.remove		= rtk_usb2phy_remove,
+	.driver		= {
+		.name	= RTK_USB2PHY_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(usbphy_rtk_dt_match),
+	},
+};
+
+module_platform_driver(rtk_usb2phy_driver);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" RTK_USB2PHY_NAME);
diff --git a/drivers/usb/phy/phy-rtk-usb3.c b/drivers/usb/phy/phy-rtk-usb3.c
new file mode 100644
index 000000000000..3a2bbbbbc74d
--- /dev/null
+++ b/drivers/usb/phy/phy-rtk-usb3.c
@@ -0,0 +1,635 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  phy-rtk-usb3.c RTK usb3.0 phy driver
+ *
+ * copyright (c) 2017 realtek semiconductor corporation
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/sys_soc.h>
+#include <linux/usb/phy.h>
+
+#include "phy-rtk-usb.h"
+
+#define RTK_USB3PHY_NAME "rtk-usb3phy"
+
+#define USB_ST_BUSY		BIT(7)
+#define PHY_ADDR_0x09 0x09
+#define PHY_ADDR_0x1F 0x1F
+
+struct reg_addr {
+	void __iomem *REG_MDIO_CTL;
+};
+
+struct phy_data {
+	int size;
+	u8 *addr;
+	u16 *data;
+
+	u16 saved_trim_value;//=0xFFFF;
+	u8 connected;//=0;
+
+	bool do_toggle;
+	bool use_default_parameter;
+};
+
+static int rtk_usb_phy3_wait_vbusy(struct reg_addr *regAddr)
+{
+	return utmi_wait_register(regAddr->REG_MDIO_CTL, USB_ST_BUSY, 0);
+}
+
+static u32 rtk_usb_phy_read(struct reg_addr *regAddr, char addr)
+{
+	unsigned int regVal;
+
+	regVal = (addr << 8);
+
+	writel(regVal, regAddr->REG_MDIO_CTL);
+
+	rtk_usb_phy3_wait_vbusy(regAddr);
+
+	return readl(regAddr->REG_MDIO_CTL);
+}
+
+static int rtk_usb_phy_write(struct reg_addr *regAddr, char addr, u16 data)
+{
+	unsigned int regVal;
+
+	regVal = BIT(0)     |
+			(addr << 8) |
+			(data << 16);
+
+	writel(regVal, regAddr->REG_MDIO_CTL);
+
+	rtk_usb_phy3_wait_vbusy(regAddr);
+
+	return 0;
+}
+
+static void rtk_usb_phy_shutdown(struct usb_phy *phy)
+{
+	/* Todo */
+}
+
+static void do_rtk_usb3_phy_toggle(struct rtk_usb_phy_s *rtk_phy,
+	    int index, bool isConnect);
+
+static int do_rtk_usb_phy_init(struct usb_phy *phy, int phy_index)
+{
+	struct rtk_usb_phy_s *rtk_phy = (struct rtk_usb_phy_s *) phy;
+	struct reg_addr *regAddr =
+		    &((struct reg_addr *)rtk_phy->reg_addr)[phy_index];
+	struct phy_data *phy_data =
+		    &((struct phy_data *)rtk_phy->phy_data)[phy_index];
+	int index = 0;
+	u8 *addr = phy_data->addr;
+	u16 *data = phy_data->data;
+
+	dev_info(phy->dev, "%s Init RTK USB 3.0 PHY phy#%d\n",
+		    __func__, phy_index);
+
+	if (phy_data->use_default_parameter) {
+		dev_info(phy->dev, "%s phy#%d use default parameter\n",
+			    __func__, phy_index);
+		goto do_toggle;
+	}
+
+	for (index = 0; index < phy_data->size; index++)
+		rtk_usb_phy_write(regAddr, *(addr + index), *(data + index));
+
+	for (index = 0; index < phy_data->size; index++)
+		dev_dbg(phy->dev, "[USB3_PHY], addr = 0x%02x, data = 0x%08x\n",
+			    *(addr + index),
+			    rtk_usb_phy_read(regAddr, *(addr + index)));
+
+do_toggle:
+	do_rtk_usb3_phy_toggle(rtk_phy, phy_index, false);
+
+	return 0;
+}
+
+static int rtk_usb_phy_init(struct usb_phy *phy)
+{
+	struct rtk_usb_phy_s *rtk_phy = (struct rtk_usb_phy_s *) phy;
+	int ret = 0;
+	int i;
+	unsigned long phy_init_time = jiffies;
+
+	if (!rtk_phy) {
+		pr_err("%s rtk_phy is NULL!\n", __func__);
+		return -1;
+	}
+
+	dev_info(phy->dev, "%s Init RTK USB 3.0 PHY\n", __func__);
+	for (i = 0; i < rtk_phy->phyN; i++)
+		ret = do_rtk_usb_phy_init(phy, i);
+
+	dev_info(phy->dev, "%s Initialized RTK USB 3.0 PHY (take %dms)\n",
+		    __func__,
+		    jiffies_to_msecs(jiffies - phy_init_time));
+	return ret;
+}
+
+static void do_rtk_usb3_phy_toggle(struct rtk_usb_phy_s *rtk_phy, int i,
+	    bool isConnect)
+{
+	struct reg_addr *regAddr = &((struct reg_addr *)rtk_phy->reg_addr)[i];
+	struct phy_data *phy_data = &((struct phy_data *)rtk_phy->phy_data)[i];
+	u8 *addr;
+	u16 *data;
+	size_t index;
+
+	if (!rtk_phy) {
+		pr_err("%s rtk_phy is NULL!\n", __func__);
+		return;
+	}
+
+	regAddr = &((struct reg_addr *)rtk_phy->reg_addr)[i];
+	phy_data = &((struct phy_data *)rtk_phy->phy_data)[i];
+
+	if (!phy_data) {
+		dev_err(rtk_phy->dev, "%s phy_data is NULL!\n", __func__);
+		return;
+	}
+
+	if (!phy_data->do_toggle)
+		return;
+
+	addr = phy_data->addr;
+	data = phy_data->data;
+
+	for (index = 0; index < phy_data->size; index++) {
+		if (*(addr + index) == PHY_ADDR_0x09) {
+			mdelay(1);
+			dev_info(rtk_phy->dev,
+				    "%s ########## to toggle PHY addr 0x09 BIT(9)\n",
+				    __func__);
+			rtk_usb_phy_write(regAddr, *(addr + index),
+				    *(data + index) & (~BIT(9)));
+			mdelay(1);
+			rtk_usb_phy_write(regAddr,
+				    *(addr + index), *(data + index));
+		}
+	}
+	dev_info(rtk_phy->dev, "%s ########## PHY addr 0x1f = 0x%08x\n",
+		    __func__, rtk_usb_phy_read(regAddr, PHY_ADDR_0x1F));
+}
+
+void rtk_usb3_phy_toggle(struct usb_phy *usb3_phy, bool isConnect, int port)
+{
+	int index = port;
+	struct rtk_usb_phy_s *rtk_phy = NULL;
+
+	if (usb3_phy != NULL && usb3_phy->dev != NULL)
+		rtk_phy = dev_get_drvdata(usb3_phy->dev);
+
+	if (rtk_phy == NULL) {
+		pr_err("%s ERROR! NO this device\n", __func__);
+		return;
+	}
+
+	if (index > rtk_phy->phyN) {
+		pr_err("%s %d ERROR! port=%d > phyN=%d\n",
+			    __func__, __LINE__, index, rtk_phy->phyN);
+		return;
+	}
+
+	do_rtk_usb3_phy_toggle(rtk_phy, index, isConnect);
+}
+EXPORT_SYMBOL(rtk_usb3_phy_toggle);
+
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+static struct dentry *create_phy_debug_root(void)
+{
+	struct dentry *phy_debug_root;
+
+	phy_debug_root = debugfs_lookup("phy", usb_debug_root);
+	if (!phy_debug_root) {
+		phy_debug_root = debugfs_create_dir("phy", usb_debug_root);
+		if (!phy_debug_root)
+			pr_err("%s Error phy_debug_root is NULL\n", __func__);
+		else
+			pr_info("%s Create phy_debug_root folder\n", __func__);
+	}
+
+	return phy_debug_root;
+}
+
+static int rtk_usb3_parameter_show(struct seq_file *s, void *unused)
+{
+	struct rtk_usb_phy_s		*rtk_phy = s->private;
+	int i, index;
+
+	for (i = 0; i < rtk_phy->phyN; i++) {
+		struct reg_addr *regAddr =
+			    &((struct reg_addr *)rtk_phy->reg_addr)[i];
+		struct phy_data *phy_data =
+			    &((struct phy_data *)rtk_phy->phy_data)[i];
+		u8 *addr = phy_data->addr;
+
+		seq_printf(s, "[USB3_PHY] PHY %d\n", i);
+
+		for (index = 0; index < phy_data->size; index++)
+			seq_printf(s, "[USB3_PHY], addr = 0x%02x, data = 0x%08x\n",
+				    *(addr + index),
+				    rtk_usb_phy_read(regAddr, *(addr + index)));
+	}
+	return 0;
+}
+
+static int rtk_usb3_parameter_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, rtk_usb3_parameter_show, inode->i_private);
+}
+
+static const struct file_operations rtk_usb3_parameter_fops = {
+	.open			= rtk_usb3_parameter_open,
+	.read			= seq_read,
+	.llseek			= seq_lseek,
+	.release		= single_release,
+};
+
+static int rtk_usb3_set_parameter_show(struct seq_file *s, void *unused)
+{
+	//struct rtk_usb_phy_s	*rtk_phy = s->private;
+
+	seq_puts(s, "Set Phy parameter by following command\n");
+	seq_puts(s, "echo \"phy_num addr value\" > set_parameter\n");
+	seq_puts(s, "echo \"0 0x00 0x4008\" > set_parameter\n");
+	seq_puts(s, "echo \"0 0x21 0x88AA\" > set_parameter\n");
+
+	return 0;
+}
+
+static int rtk_usb3_set_parameter_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, rtk_usb3_set_parameter_show, inode->i_private);
+}
+
+static ssize_t rtk_usb3_set_parameter_write(struct file *file,
+		const char __user *ubuf, size_t count, loff_t *ppos)
+{
+	struct seq_file		*s = file->private_data;
+	struct rtk_usb_phy_s		*rtk_phy = s->private;
+	struct reg_addr *regAddr;
+	struct phy_data *phy_data;
+	u8 *addr;
+	u16 *data;
+	int index, i, ret = 0;
+	char buffer[40];
+	char *buf = buffer;
+	u32 _addr;
+	u32 value;
+
+	if (copy_from_user(&buffer, ubuf,
+		    min_t(size_t, sizeof(buffer) - 1, count)))
+		return -EFAULT;
+
+	ret = kstrtoint(buf, 0, &i);
+	if (ret < 0)
+		return -EFAULT;
+
+	buf = buf + 1;
+
+	regAddr = &((struct reg_addr *)rtk_phy->reg_addr)[i];
+	phy_data = &((struct phy_data *)rtk_phy->phy_data)[i];
+	addr = phy_data->addr;
+	data = phy_data->data;
+
+	buf = skip_spaces(buf);
+	ret = sscanf(buf, "%x %x", &_addr, &value);
+	if (ret < 0)
+		return -EFAULT;
+
+	for (index = 0; index < phy_data->size; index++) {
+		if (_addr == *(addr + index)) {
+			*(data + index) = value;
+			rtk_usb_phy_write(regAddr,
+			    *(addr + index), *(data + index));
+		}
+	}
+
+	return count;
+}
+
+static const struct file_operations rtk_usb3_set_parameter_fops = {
+	.open			= rtk_usb3_set_parameter_open,
+	.write			= rtk_usb3_set_parameter_write,
+	.read			= seq_read,
+	.llseek			= seq_lseek,
+	.release		= single_release,
+};
+
+static int rtk_usb3_toggle_show(struct seq_file *s, void *unused)
+{
+	struct rtk_usb_phy_s		*rtk_phy = s->private;
+	struct phy_data *phy_data;
+	int i;
+
+	for (i = 0; i < rtk_phy->phyN; i++) {
+		phy_data = &((struct phy_data *)rtk_phy->phy_data)[i];
+		seq_printf(s, "Now phy#%d do_toggle is %s.\n",
+			    i, phy_data->do_toggle?"Enable":"Disable");
+	}
+	seq_puts(s, "ehco 1 to enable toggle phy parameter.\n");
+
+	return 0;
+}
+
+static int rtk_usb3_toggle_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, rtk_usb3_toggle_show, inode->i_private);
+}
+
+static ssize_t rtk_usb3_toggle_write(struct file *file,
+		const char __user *ubuf, size_t count, loff_t *ppos)
+{
+	struct seq_file		*s = file->private_data;
+	struct rtk_usb_phy_s		*rtk_phy = s->private;
+	char			buf[32];
+	struct phy_data *phy_data;
+	bool enable = false;
+	int i;
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	if (!strncmp(buf, "1", 1))
+		enable = true;
+
+	for (i = 0; i < rtk_phy->phyN; i++) {
+		phy_data = &((struct phy_data *)rtk_phy->phy_data)[i];
+		phy_data->do_toggle = enable;
+		dev_info(rtk_phy->dev, "Set phy#%d do_toggle is %s.\n",
+			    i, phy_data->do_toggle?"Enable":"Disable");
+	}
+
+	return count;
+}
+
+static const struct file_operations rtk_usb3_toggle_fops = {
+	.open			= rtk_usb3_toggle_open,
+	.write			= rtk_usb3_toggle_write,
+	.read			= seq_read,
+	.llseek			= seq_lseek,
+	.release		= single_release,
+};
+
+static inline void create_debug_files(struct rtk_usb_phy_s *rtk_phy)
+{
+	struct dentry *phy_debug_root = NULL;
+
+	dev_dbg(rtk_phy->dev, "%s", __func__);
+
+	phy_debug_root = create_phy_debug_root();
+
+	if (!phy_debug_root) {
+		dev_err(rtk_phy->dev, "%s Error phy_debug_root is NULL",
+			    __func__);
+		return;
+	}
+	rtk_phy->debug_dir = debugfs_create_dir(dev_name(rtk_phy->dev),
+		    phy_debug_root);
+	if (!rtk_phy->debug_dir) {
+		dev_err(rtk_phy->dev, "%s Error debug_dir is NULL", __func__);
+		return;
+	}
+
+	if (!debugfs_create_file("parameter", 0444,
+		    rtk_phy->debug_dir, rtk_phy,
+		    &rtk_usb3_parameter_fops))
+		goto file_error;
+
+	if (!debugfs_create_file("set_parameter", 0644,
+		    rtk_phy->debug_dir, rtk_phy,
+		    &rtk_usb3_set_parameter_fops))
+		goto file_error;
+
+	if (!debugfs_create_file("toggle", 0644, rtk_phy->debug_dir, rtk_phy,
+		    &rtk_usb3_toggle_fops))
+		goto file_error;
+
+	return;
+
+file_error:
+	debugfs_remove_recursive(rtk_phy->debug_dir);
+}
+#endif //CONFIG_DYNAMIC_DEBUG
+
+#define DEFAULT_CHIP_REVISION 0xA00
+#define MAX_CHIP_REVISION 0xC00
+
+static int __get_chip_revision(void)
+{
+	int chip_revision = 0xFFF;
+	char revision[] = "FFF";
+	struct soc_device_attribute soc_att[] = {{.revision = revision}, {}};
+	struct soc_device_attribute *soc_att_match = NULL;
+
+	while (soc_att_match == NULL) {
+		chip_revision--;
+
+		if (chip_revision <= DEFAULT_CHIP_REVISION)
+			break;
+		if (chip_revision > MAX_CHIP_REVISION)
+			chip_revision = MAX_CHIP_REVISION;
+		else if ((chip_revision & 0xFF) > 0xF)
+			chip_revision = (chip_revision & 0xF00) + 0xF;
+
+		snprintf(revision, 4, "%X", chip_revision);
+
+		soc_att_match = (struct soc_device_attribute *)
+			    soc_device_match(soc_att);
+	}
+
+	if (soc_att_match) {
+		pr_debug("%s get chip_revision %x\n", __func__, chip_revision);
+		return chip_revision;
+	}
+
+	pr_err("%s ERROR: no match chip_revision\n", __func__);
+	return DEFAULT_CHIP_REVISION;
+}
+
+static int rtk_usb3phy_probe(struct platform_device *pdev)
+{
+	struct rtk_usb_phy_s *rtk_usb_phy;
+	struct device *dev = &pdev->dev;
+	int phyN;
+	int i, chip_revision, ret = 0;
+
+	rtk_usb_phy = devm_kzalloc(dev, sizeof(*rtk_usb_phy), GFP_KERNEL);
+	if (!rtk_usb_phy)
+		return -ENOMEM;
+
+	rtk_usb_phy->dev			= &pdev->dev;
+	rtk_usb_phy->phy.dev		= rtk_usb_phy->dev;
+	rtk_usb_phy->phy.label		= RTK_USB3PHY_NAME;
+	rtk_usb_phy->phy.init		= rtk_usb_phy_init;
+	rtk_usb_phy->phy.shutdown	= rtk_usb_phy_shutdown;
+
+	if (!dev->of_node) {
+		dev_err(dev, "%s %d No device node\n", __func__, __LINE__);
+		goto err;
+	}
+
+	ret = of_property_read_u32_index(dev->of_node, "phyN", 0,
+		    &phyN);
+	if (ret)
+		goto err;
+
+	rtk_usb_phy->phyN = phyN;
+
+	rtk_usb_phy->reg_addr = devm_kzalloc(dev,
+		    sizeof(struct reg_addr) * phyN, GFP_KERNEL);
+	if (!rtk_usb_phy->reg_addr)
+		return -ENOMEM;
+	rtk_usb_phy->phy_data = devm_kzalloc(dev,
+		    sizeof(struct phy_data) * phyN, GFP_KERNEL);
+	if (!rtk_usb_phy->phy_data)
+		return -ENOMEM;
+
+	chip_revision = __get_chip_revision();
+
+	dev_dbg(dev, "%s: Chip revision is %x\n", __func__, chip_revision);
+
+	for (i = 0; i < phyN; i++) {
+		struct reg_addr *addr =
+			    &((struct reg_addr *)rtk_usb_phy->reg_addr)[i];
+		struct phy_data *phy_data =
+			    &((struct phy_data *)rtk_usb_phy->phy_data)[i];
+
+		char phy_name[5];
+		struct device_node *sub_node;
+		int revision = chip_revision;
+
+		addr->REG_MDIO_CTL = of_iomap(dev->of_node, i);
+		dev_dbg(dev, "%s %d #%d REG_MDIO_CTL=%p\n",
+			    __func__, __LINE__, i, addr->REG_MDIO_CTL);
+
+		snprintf(phy_name, 5, "phy%d", i);
+
+		sub_node = of_get_child_by_name(dev->of_node, phy_name);
+		if (!sub_node) {
+			dev_err(dev, "%s %d No device sub node for %s\n",
+				    __func__, __LINE__, phy_name);
+			goto err;
+		}
+
+		phy_data->saved_trim_value = 0xFFFF;
+		phy_data->connected = 0;
+
+		ret = of_property_read_u32_index(sub_node, "phy_data_size", 0,
+			    &phy_data->size);
+		if (ret)
+			goto err;
+		phy_data->addr = devm_kzalloc(dev, sizeof(u8)*phy_data->size,
+			    GFP_KERNEL);
+		if (!phy_data->addr)
+			return -ENOMEM;
+		phy_data->data = devm_kzalloc(dev, sizeof(u16)*phy_data->size,
+			    GFP_KERNEL);
+		if (!phy_data->data)
+			return -ENOMEM;
+		ret = of_property_read_u8_array(sub_node, "phy_data_addr",
+			    phy_data->addr, phy_data->size);
+		if (ret)
+			goto err;
+
+		while (revision >= DEFAULT_CHIP_REVISION) {
+			char phy_data_revision[14] = {0};
+
+			snprintf(phy_data_revision, 13, "phy_data_%X",
+				    revision);
+			ret = of_property_read_u16_array(sub_node,
+				    phy_data_revision,
+				    phy_data->data, phy_data->size);
+			if (!ret) {
+				dev_dbg(dev, "%s load %s parameter\n",
+					    __func__, phy_data_revision);
+				break;
+			}
+			revision--;
+			if ((revision & 0xFF) > 0xF)
+				revision = (revision & 0xF00) + 0xF;
+		}
+
+		/* For old device tree */
+		if (ret) {
+			ret = of_property_read_u16_array(sub_node,
+				    "phy_data_revA",
+				    phy_data->data, phy_data->size);
+			if (ret)
+				goto err;
+			else
+				dev_info(dev, "%s load parameter\n", __func__);
+		}
+
+		if (of_property_read_bool(sub_node, "do_toggle"))
+			phy_data->do_toggle = true;
+		else
+			phy_data->do_toggle = false;
+
+		if (of_property_read_bool(sub_node, "use_default_parameter"))
+			phy_data->use_default_parameter = true;
+		else
+			phy_data->use_default_parameter = false;
+	}
+
+	platform_set_drvdata(pdev, rtk_usb_phy);
+
+	ret = usb_add_phy_dev(&rtk_usb_phy->phy);
+	if (ret)
+		goto err;
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+	create_debug_files(rtk_usb_phy);
+#endif
+
+	dev_info(&pdev->dev, "%s Probe RTK USB 3.0 PHY\n", __FILE__);
+err:
+	return ret;
+}
+
+static int rtk_usb3phy_remove(struct platform_device *pdev)
+{
+	struct rtk_usb_phy_s *rtk_usb_phy = platform_get_drvdata(pdev);
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+	debugfs_remove_recursive(rtk_usb_phy->debug_dir);
+#endif
+
+	usb_remove_phy(&rtk_usb_phy->phy);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id usbphy_rtk_dt_match[] = {
+	{ .compatible = "realtek,usb3phy", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, usbphy_rtk_dt_match);
+#endif
+
+static struct platform_driver rtk_usb3phy_driver = {
+	.probe		= rtk_usb3phy_probe,
+	.remove		= rtk_usb3phy_remove,
+	.driver		= {
+		.name	= RTK_USB3PHY_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(usbphy_rtk_dt_match),
+	},
+};
+
+module_platform_driver(rtk_usb3phy_driver);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" RTK_USB3PHY_NAME);
-- 
2.28.0



From ericwang at realtek.com  Fri Sep 18 04:36:53 2020
From: ericwang at realtek.com (Eric Wang)
Date: Fri, 18 Sep 2020 16:36:53 +0800
Subject: [RFC v1 1/3] ethernet: r8169soc: new realtek soc ethernet driver
In-Reply-To: <20200918083655.20387-1-ericwang@realtek.com>
References: <20200918083655.20387-1-ericwang@realtek.com>
Message-ID: <20200918083655.20387-2-ericwang@realtek.com>

r8169soc could support Realtek DHC SoCs RTD119x, RTD129x, RTD139x, RTD16xx, and
RTD13xx.

Signed-off-by: Eric Wang <ericwang at realtek.com>
---
 drivers/net/ethernet/realtek/Kconfig    |   23 +-
 drivers/net/ethernet/realtek/Makefile   |    1 +
 drivers/net/ethernet/realtek/r8169soc.c | 9285 +++++++++++++++++++++++
 3 files changed, 9308 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/realtek/r8169soc.c

diff --git a/drivers/net/ethernet/realtek/Kconfig b/drivers/net/ethernet/realtek/Kconfig
index 93d9df55b361..b2bb60cadfde 100644
--- a/drivers/net/ethernet/realtek/Kconfig
+++ b/drivers/net/ethernet/realtek/Kconfig
@@ -6,7 +6,7 @@
 config NET_VENDOR_REALTEK
 	bool "Realtek devices"
 	default y
-	depends on PCI || (PARPORT && X86)
+	depends on PCI || (PARPORT && X86) || ARCH_REALTEK
 	help
 	  If you have a network (Ethernet) card belonging to this class, say Y.
 
@@ -113,4 +113,25 @@ config R8169
 	  To compile this driver as a module, choose M here: the module
 	  will be called r8169.  This is recommended.
 
+config R8169SOC
+	tristate "Realtek 8169SoC gigabit ethernet support"
+	depends on ARCH_REALTEK
+	select CRC32
+	select MII
+	help
+	  Say Y here if you have a Realtek 8169 SoC for RTK Gigabit Ethernet
+	  adapter.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called r8169soc.  This is recommended.
+
+config RTL_RX_NO_COPY
+	bool "Support to receive packets without memory copy"
+	default n
+	depends on R8169SOC
+	help
+	  This is a feature of Realtek 8169SoC gigabit ethernet driver.
+	  Say Y here to impove performance and reduce CPU loading.
+	  Say N here to save memory for packet buffers.
+
 endif # NET_VENDOR_REALTEK
diff --git a/drivers/net/ethernet/realtek/Makefile b/drivers/net/ethernet/realtek/Makefile
index 2e1d78b106b0..5b3af94eb565 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_R8169SOC) += r8169soc.o
diff --git a/drivers/net/ethernet/realtek/r8169soc.c b/drivers/net/ethernet/realtek/r8169soc.c
new file mode 100644
index 000000000000..6358c8802da9
--- /dev/null
+++ b/drivers/net/ethernet/realtek/r8169soc.c
@@ -0,0 +1,9285 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* r8169soc.c: RealTek 8169soc ethernet driver.
+ *
+ * Copyright (c) 2002 ShuChen <shuchen at realtek.com.tw>
+ * Copyright (c) 2003 - 2007 Francois Romieu <romieu at fr.zoreil.com>
+ * Copyright (c) 2014 YuKuen Wu <yukuen at realtek.com>
+ * Copyright (c) 2015 Eric Wang <ericwang at realtek.com>
+ * Copyright (c) 2019 Realtek Semiconductor Corp.
+ * Copyright (c) a lot of people too. Please respect their work.
+ *
+ * See MAINTAINERS file for support contact information.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/proc_fs.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/if_vlan.h>
+#include <linux/crc32.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_net.h>
+#include <linux/of_address.h>
+#include <linux/version.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/suspend.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/reset.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/sys_soc.h>
+
+#define RTL8169_VERSION "1.0.0"
+#define MODULENAME "r8169"
+#define PFX MODULENAME ": "
+
+#ifdef RTL8169_DEBUG
+#define assert(expr)							\
+do {									\
+	if (!(expr)) {							\
+		pr_debug("Assertion failed! %s,%s,%s,line=%d\n",	\
+		#expr, __FILE__, __func__, __LINE__);			\
+	}								\
+} while (0)
+#define dprintk(fmt, args...) pr_debug(PFX fmt, ## args)
+#else
+#define assert(expr) do {} while (0)
+#define dprintk(fmt, args...)	do {} while (0)
+#endif /* RTL8169_DEBUG */
+
+#define R8169_MSG_DEFAULT \
+	(NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN)
+
+/* 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;
+
+#define MAX_READ_REQUEST_SHIFT	12
+#define TX_DMA_BURST	4	/* Maximum DMA burst, '7' is unlimited */
+#define INTER_FRAME_GAP	0x03	/* 3 means INTER_FRAME_GAP = the shortest one */
+
+#define R8169_REGS_SIZE		256
+#define R8169_NAPI_WEIGHT	64
+#define NUM_TX_DESC	1024	/* Number of Tx descriptor registers */
+#if defined(CONFIG_RTL_RX_NO_COPY)
+#define NUM_RX_DESC	4096	/* Number of Rx descriptor registers */
+#else
+#define NUM_RX_DESC	1024	/* Number of Rx descriptor registers */
+#endif /* CONFIG_RTL_RX_NO_COPY */
+#define R8169_TX_RING_BYTES	(NUM_TX_DESC * sizeof(struct tx_desc))
+#define R8169_RX_RING_BYTES	(NUM_RX_DESC * sizeof(struct rx_desc))
+
+#define RTL8169_TX_TIMEOUT	(6 * HZ)
+
+#if defined(CONFIG_RTL_RX_NO_COPY)
+#define RX_BUF_SIZE	0x05F3	/* 0x05F3 = 1522bye + 1 */
+#define RTK_RX_ALIGN	8
+#endif /* CONFIG_RTL_RX_NO_COPY */
+
+#define RTL_PROC 1
+
+/* write/read MMIO register */
+#define RTL_W8(reg, val8)	(writeb((val8), ioaddr + (reg)))
+#define RTL_W16(reg, val16)	(writew((val16), ioaddr + (reg)))
+#define RTL_W32(reg, val32)	(writel((val32), ioaddr + (reg)))
+#define RTL_R8(reg)		(readb(ioaddr + (reg)))
+#define RTL_R16(reg)		(readw(ioaddr + (reg)))
+#define RTL_R32(reg)		(readl(ioaddr + (reg)))
+
+enum rtl_tx_desc_version {
+	RTL_TD_0	= 0,
+	RTL_TD_1	= 1,
+};
+
+#define JUMBO_1K	ETH_DATA_LEN
+#define JUMBO_4K	(4 * 1024 - ETH_HLEN - 2)
+#define JUMBO_6K	(6 * 1024 - ETH_HLEN - 2)
+#define JUMBO_7K	(7 * 1024 - ETH_HLEN - 2)
+#define JUMBO_9K	(9 * 1024 - ETH_HLEN - 2)
+
+enum cfg_version {
+	RTL_CFG_0 = 0x00,
+	RTL_CFG_1,
+	RTL_CFG_2
+};
+
+#if defined(CONFIG_RTL_RX_NO_COPY)
+static int rx_buf_sz = 1523;	/* 0x05F3 = 1522bye + 1 */
+static int rx_buf_sz_new = 1523;
+#else
+static int rx_buf_sz = 16383;
+#endif /* CONFIG_RTL_RX_NO_COPY */
+
+static struct {
+	u32 msg_enable;
+} debug = {-1};
+
+enum rtl_registers {
+	MAC0				= 0,	/* Ethernet hardware address. */
+	MAC4				= 4,
+	MAR0				= 8,	/* Multicast filter. */
+	COUNTER_ADDR_LOW		= 0x10,
+	COUNTER_ADDR_HIGH		= 0x14,
+	LEDSEL				= 0x18,
+	TX_DESC_START_ADDR_LOW		= 0x20,
+	TX_DESC_START_ADDR_HIGH		= 0x24,
+	TXH_DESC_START_ADDR_LOW		= 0x28,
+	TXH_DESC_START_ADDR_HIGH	= 0x2c,
+	FLASH				= 0x30,
+	ERSR				= 0x36,
+	CHIP_CMD			= 0x37,
+	TX_POLL				= 0x38,
+	INTR_MASK			= 0x3c,
+	INTR_STATUS			= 0x3e,
+
+	TX_CONFIG			= 0x40,
+#define	TXCFG_AUTO_FIFO			BIT(7)	/* 8111e-vl */
+#define	TXCFG_EMPTY			BIT(11)	/* 8111e-vl */
+
+	RX_CONFIG			= 0x44,
+#define	RX128_INT_EN			BIT(15)	/* 8111c and later */
+#define	RX_MULTI_EN			BIT(14)	/* 8111c only */
+#define	RXCFG_FIFO_SHIFT		13
+					/* No threshold before first PCI xfer */
+#define	RX_FIFO_THRESH			(7 << RXCFG_FIFO_SHIFT)
+#define	RX_EARLY_OFF			BIT(11)
+#define	RXCFG_DMA_SHIFT			8
+					/* Unlimited maximum PCI burst. */
+#define	RX_DMA_BURST			(3 << RXCFG_DMA_SHIFT)	/* 128 bytes */
+
+	RX_MISSED			= 0x4c,
+	CFG9346				= 0x50,
+	CONFIG0				= 0x51,
+	CONFIG1				= 0x52,
+	CONFIG2				= 0x53,
+#define PME_SIGNAL			BIT(5)	/* 8168c and later */
+
+	CONFIG3				= 0x54,
+	CONFIG4				= 0x55,
+	CONFIG5				= 0x56,
+	MULTI_INTR			= 0x5c,
+	PHYAR				= 0x60,
+	PHY_STATUS			= 0x6c,
+	RX_MAX_SIZE			= 0xda,
+	C_PLUS_CMD			= 0xe0,
+	INTR_MITIGATE			= 0xe2,
+	RX_DESC_ADDR_LOW		= 0xe4,
+	RX_DESC_ADDR_HIGH		= 0xe8,
+	EARLY_TX_THRES			= 0xec,	/* 8169. Unit of 32 bytes. */
+
+#define NO_EARLY_TX	0x3f	/* Max value : no early transmit. */
+
+	MAX_TX_PACKET_SIZE		= 0xec,	/* Unit of 128 bytes. */
+
+#define TX_PACKET_MAX	(8064 >> 7)
+#define EARLY_SIZE	0x27
+
+	FUNC_EVENT			= 0xf0,
+	FUNC_EVENT_MASK			= 0xf4,
+	FUNC_PRESET_STATE		= 0xf8,
+	FUNC_FORCE_EVENT		= 0xfc,
+};
+
+enum rtl8168_8101_registers {
+	CSIDR			= 0x64,
+	CSIAR			= 0x68,
+#define	CSIAR_FLAG			0x80000000
+#define	CSIAR_WRITE_CMD			0x80000000
+#define	CSIAR_BYTE_ENABLE		0x0f
+#define	CSIAR_BYTE_ENABLE_SHIFT		12
+#define	CSIAR_ADDR_MASK			0x0fff
+#define CSIAR_FUNC_CARD			0x00000000
+#define CSIAR_FUNC_SDIO			0x00010000
+#define CSIAR_FUNC_NIC			0x00020000
+	PMCH			= 0x6f,
+	EPHYAR			= 0x80,
+#define	EPHYAR_FLAG			0x80000000
+#define	EPHYAR_WRITE_CMD		0x80000000
+#define	EPHYAR_REG_MASK			0x1f
+#define	EPHYAR_REG_SHIFT		16
+#define	EPHYAR_DATA_MASK		0xffff
+	DLLPR			= 0xd0,
+#define	PFM_EN				BIT(6)
+	DBG_REG			= 0xd1,
+#define	FIX_NAK_1			BIT(4)
+#define	FIX_NAK_2			BIT(3)
+	TWSI			= 0xd2,
+	MCU			= 0xd3,
+#define	NOW_IS_OOB			BIT(7)
+#define	TX_EMPTY			BIT(5)
+#define	RX_EMPTY			BIT(4)
+#define	RXTX_EMPTY			(TX_EMPTY | RX_EMPTY)
+#define	EN_NDP				BIT(3)
+#define	EN_OOB_RESET			BIT(2)
+#define	LINK_LIST_RDY			BIT(1)
+#define	DIS_MCU_CLROOB			BIT(0)
+	EFUSEAR			= 0xdc,
+#define	EFUSEAR_FLAG			0x80000000
+#define	EFUSEAR_WRITE_CMD		0x80000000
+#define	EFUSEAR_READ_CMD		0x00000000
+#define	EFUSEAR_REG_MASK		0x03ff
+#define	EFUSEAR_REG_SHIFT		8
+#define	EFUSEAR_DATA_MASK		0xff
+};
+
+enum rtl8168_registers {
+	LED_FREQ		= 0x1a,
+	EEE_LED			= 0x1b,
+	ERIDR			= 0x70,
+	ERIAR			= 0x74,
+#define ERIAR_FLAG			0x80000000
+#define ERIAR_WRITE_CMD			0x80000000
+#define ERIAR_READ_CMD			0x00000000
+#define ERIAR_ADDR_BYTE_ALIGN		4
+#define ERIAR_TYPE_SHIFT		16
+#define ERIAR_EXGMAC			(0x00 << ERIAR_TYPE_SHIFT)
+#define ERIAR_MSIX			(0x01 << ERIAR_TYPE_SHIFT)
+#define ERIAR_ASF			(0x02 << ERIAR_TYPE_SHIFT)
+#define ERIAR_MASK_SHIFT		12
+#define ERIAR_MASK_0001			(0x1 << ERIAR_MASK_SHIFT)
+#define ERIAR_MASK_0010			(0x2 << ERIAR_MASK_SHIFT)
+#define ERIAR_MASK_0011			(0x3 << ERIAR_MASK_SHIFT)
+#define ERIAR_MASK_0100			(0x4 << ERIAR_MASK_SHIFT)
+#define ERIAR_MASK_0101			(0x5 << ERIAR_MASK_SHIFT)
+#define ERIAR_MASK_1000			(0x8 << ERIAR_MASK_SHIFT)
+#define ERIAR_MASK_1100			(0xc << ERIAR_MASK_SHIFT)
+#define ERIAR_MASK_1111			(0xf << ERIAR_MASK_SHIFT)
+	EPHY_RXER_NUM		= 0x7c,
+	OCPDR			= 0xb0,	/* OCP GPHY access */
+#define OCPDR_WRITE_CMD			0x80000000
+#define OCPDR_READ_CMD			0x00000000
+#define OCPDR_REG_MASK			0x7fff
+#define OCPDR_REG_SHIFT			16
+#define OCPDR_DATA_MASK			0xffff
+	RDSAR1			= 0xd0,	/* 8168c only. Undocumented on 8168dp */
+	MISC			= 0xf0,	/* 8168e only. */
+#define TXPLA_RST			BIT(29)
+#define DISABLE_LAN_EN			BIT(23) /* Enable GPIO pin */
+#define PWM_EN				BIT(22)
+#define RXDV_GATED_EN			BIT(19)
+#define EARLY_TALLY_EN			BIT(16)
+};
+
+enum r8169soc_registers {
+	TX_DESC_TAIL_IDX	= 0x20,	/* the last descriptor index */
+	TX_DESC_CLOSE_IDX	= 0x22,	/* the closed descriptor index */
+#define TX_DESC_CNT_MASK		0x3FFF
+#define TX_DESC_CNT_SIZE		0x4000
+};
+
+enum rtl_register_content {
+	/* InterruptStatusBits */
+	SYS_ERR		= 0x8000,
+	PCS_TIMEOUT	= 0x4000,
+	SW_INT		= 0x0100,
+	TX_DESC_UNAVAIL	= 0x0080,
+	RX_FIFO_OVER	= 0x0040,
+	LINK_CHG	= 0x0020,
+	RX_OVERFLOW	= 0x0010,
+	TX_ERR		= 0x0008,
+	TX_OK		= 0x0004,
+	RX_ERR		= 0x0002,
+	RX_OK		= 0x0001,
+
+	/* RxStatusDesc */
+	RX_BOVF		= BIT(24),
+	RX_FOVF		= BIT(23),
+	RX_RWT		= BIT(22),
+	RX_RES		= BIT(21),
+	RX_RUNT		= BIT(20),
+	RX_CRC		= BIT(19),
+
+	/* ChipCmdBits */
+	STOP_REQ	= 0x80,
+	CMD_RESET	= 0x10,
+	CMD_RX_ENB	= 0x08,
+	CMD_TX_ENB	= 0x04,
+	RX_BUF_EMPTY	= 0x01,
+
+	/* TXPoll register p.5 */
+	HPQ		= 0x80,		/* Poll cmd on the high prio queue */
+	NPQ		= 0x40,		/* Poll cmd on the low prio queue */
+	FSW_INT		= 0x01,		/* Forced software interrupt */
+
+	/* Cfg9346Bits */
+	CFG9346_LOCK	= 0x00,
+	CFG9346_UNLOCK	= 0xc0,
+
+	/* rx_mode_bits */
+	ACCEPT_ERR		= 0x20,
+	ACCEPT_RUNT		= 0x10,
+	ACCEPT_BROADCAST	= 0x08,
+	ACCEPT_MULTICAST	= 0x04,
+	ACCEPT_MY_PHYS		= 0x02,
+	ACCEPT_ALL_PHYS		= 0x01,
+#define RX_CONFIG_ACCEPT_MASK		0x3f
+
+	/* TxConfigBits */
+	TX_INTER_FRAME_GAP_SHIFT = 24,
+	TX_DMA_SHIFT = 8,	/* DMA burst value (0-7)
+				 * is shift this many bits
+				 */
+
+	/* CONFIG1 register p.24 */
+	LEDS1		= BIT(7),
+	LEDS0		= BIT(6),
+	SPEED_DOWN	= BIT(4),
+	MEMMAP		= BIT(3),
+	IOMAP		= BIT(2),
+	VPD		= BIT(1),
+	PM_ENABLE	= BIT(0),	/* Power Management Enable */
+
+	/* CONFIG2 register p. 25 */
+	CLK_REQ_EN	= BIT(7),	/* Clock Request Enable */
+	MSI_ENABLE	= BIT(5),	/* 8169 only. Reserved in the 8168. */
+	PCI_CLOCK_66MHZ = 0x01,
+	PCI_CLOCK_33MHZ = 0x00,
+
+	/* CONFIG3 register p.25 */
+	MAGIC_PKT	= BIT(5),	/* Wake up when receives a Magic Pkt */
+	LINK_UP		= BIT(4),	/* Wake up when the cable connection
+					 * is re-established
+					 */
+	JUMBO_EN0	= BIT(2),	/* 8168 only. Reserved in the 8168b */
+	BEACON_EN	= BIT(0),	/* 8168 only. Reserved in the 8168b */
+
+	/* CONFIG4 register */
+	JUMBO_EN1	= BIT(1),	/* 8168 only. Reserved in the 8168b */
+
+	/* CONFIG5 register p.27 */
+	BWF		= BIT(6),	/* Accept Broadcast wakeup frame */
+	MWF		= BIT(5),	/* Accept Multicast wakeup frame */
+	UWF		= BIT(4),	/* Accept Unicast wakeup frame */
+	SPI_EN		= BIT(3),
+	LAN_WAKE	= BIT(1),	/* LAN_WAKE enable/disable */
+	PME_STATUS	= BIT(0),	/* PME status can be reset by PCI RST */
+	ASPM_EN		= BIT(0),	/* ASPM enable */
+
+	/* C_PLUS_CMD p.31 */
+	ENABLE_BIST		= BIT(15),	/* 8168 8101 */
+	MAC_DBGO_OE		= BIT(14),	/* 8168 8101 */
+	NORMAL_MODE		= BIT(13),	/* unused */
+	FORCE_HALF_DUP		= BIT(12),	/* 8168 8101 */
+	FORCE_RXFLOW_EN		= BIT(11),	/* 8168 8101 */
+	FORCE_TXFLOW_EN		= BIT(10),	/* 8168 8101 */
+	CXPL_DBG_SEL		= BIT(9),	/* 8168 8101 */
+	ASF			= BIT(8),	/* 8168 8101 */
+	PKT_CNTR_DISABLE	= BIT(7),	/* 8168 8101 */
+	MAC_DBGO_SEL		= 0x001c,	/* 8168 */
+	RX_VLAN			= BIT(6),
+	RX_CHK_SUM		= BIT(5),
+	PCIDAC			= BIT(4),
+	PCI_MUL_RW		= BIT(3),
+	INTT_0			= 0x0000,	/* 8168 */
+	INTT_1			= 0x0001,	/* 8168 */
+	INTT_2			= 0x0002,	/* 8168 */
+	INTT_3			= 0x0003,	/* 8168 */
+
+	/* rtl8169_PHYstatus */
+	PWR_SAVE_STATUS	= 0x80,
+	TX_FLOW_CTRL	= 0x40,
+	RX_FLOW_CTRL	= 0x20,
+	_1000BPSF	= 0x10,
+	_100BPS		= 0x08,
+	_10BPS		= 0x04,
+	LINK_STATUS	= 0x02,
+	FULL_DUP	= 0x01,
+
+	/* DumpCounterCommand */
+	COUNTER_DUMP	= 0x8,
+};
+
+enum rtl_desc_bit {
+	/* First doubleword. */
+	DESC_OWN	= BIT(31), /* Descriptor is owned by NIC */
+	RING_END	= BIT(30), /* End of descriptor ring */
+	FIRST_FRAG	= BIT(29), /* First segment of a packet */
+	LAST_FRAG	= BIT(28), /* Final segment of a packet */
+};
+
+/* Generic case. */
+enum rtl_tx_desc_bit {
+	/* First doubleword. */
+	TD_LSO		= BIT(27),		/* Large Send Offload */
+#define TD_MSS_MAX			0x07ffu	/* MSS value */
+
+	/* Second doubleword. */
+	TX_VLAN_TAG	= BIT(17),		/* Add VLAN tag */
+};
+
+/* 8169, 8168b and 810x except 8102e. */
+enum rtl_tx_desc_bit_0 {
+	/* First doubleword. */
+#define TD0_MSS_SHIFT			16	/* MSS position (11 bits) */
+	TD0_TCP_CS	= BIT(16),		/* Calculate TCP/IP checksum */
+	TD0_UDP_CS	= BIT(17),		/* Calculate UDP/IP checksum */
+	TD0_IP_CS	= BIT(18),		/* Calculate IP checksum */
+};
+
+/* 8102e, 8168c and beyond. */
+enum rtl_tx_desc_bit_1 {
+	/* Second doubleword. */
+#define TD1_MSS_SHIFT			18	/* MSS position (11 bits) */
+	TD1_IP_CS	= BIT(29),		/* Calculate IP checksum */
+	TD1_TCP_CS	= BIT(30),		/* Calculate TCP/IP checksum */
+	TD1_UDP_CS	= BIT(31),		/* Calculate UDP/IP checksum */
+};
+
+static const struct rtl_tx_desc_info {
+	struct {
+		u32 udp;
+		u32 tcp;
+	} checksum;
+	u16 mss_shift;
+	u16 opts_offset;
+} tx_desc_info = {
+	.checksum = {
+		.udp	= TD1_IP_CS | TD1_UDP_CS,
+		.tcp	= TD1_IP_CS | TD1_TCP_CS
+	},
+	.mss_shift	= TD1_MSS_SHIFT,
+	.opts_offset	= 1
+};
+
+enum rtl_rx_desc_bit {
+	/* Rx private */
+	PID1		= BIT(18), /* Protocol ID bit 1/2 */
+	PID0		= BIT(17), /* Protocol ID bit 2/2 */
+
+#define RX_PROTO_UDP	(PID1)
+#define RX_PROTO_TCP	(PID0)
+#define RX_PROTO_IP	(PID1 | PID0)
+#define RX_PROTO_MASK	RX_PROTO_IP
+
+	IP_FAIL		= BIT(16), /* IP checksum failed */
+	UDP_FAIL	= BIT(15), /* UDP/IP checksum failed */
+	TCP_FAIL	= BIT(14), /* TCP/IP checksum failed */
+	RX_VLAN_TAG	= BIT(16), /* VLAN tag available */
+};
+
+#define RSVD_MASK	0x3fffc000
+
+struct tx_desc {
+	__le32 opts1;
+	__le32 opts2;
+	__le64 addr;
+};
+
+struct rx_desc {
+	__le32 opts1;
+	__le32 opts2;
+	__le64 addr;
+};
+
+struct ring_info {
+	struct sk_buff	*skb;
+	u32		len;
+	u8		__pad[sizeof(void *) - sizeof(u32)];
+};
+
+enum features {
+	RTL_FEATURE_WOL		= BIT(0),
+	RTL_FEATURE_MSI		= BIT(1),
+	RTL_FEATURE_GMII	= BIT(2),
+	RTL_FEATURE_TX_NO_CLOSE	= BIT(3),
+	RTL_FEATURE_RX_NO_COPY	= BIT(4),
+	RTL_FEATURE_ADJUST_FIFO	= BIT(5),
+	RTL_FEATURE_ACP		= BIT(6),
+	RTL_FEATURE_EEE		= BIT(7),
+};
+
+struct rtl8169_counters {
+	__le64	tx_packets;
+	__le64	rx_packets;
+	__le64	tx_errors;
+	__le32	rx_errors;
+	__le16	rx_missed;
+	__le16	align_errors;
+	__le32	tx_one_collision;
+	__le32	tx_multi_collision;
+	__le64	rx_unicast;
+	__le64	rx_broadcast;
+	__le32	rx_multicast;
+	__le16	tx_aborted;
+	__le16	tx_underrun;
+};
+
+enum rtl_flag {
+	RTL_FLAG_TASK_ENABLED,
+	RTL_FLAG_TASK_SLOW_PENDING,
+	RTL_FLAG_TASK_RESET_PENDING,
+	RTL_FLAG_TASK_PHY_PENDING,
+	RTL_FLAG_MAX
+};
+
+struct rtl8169_stats {
+	u64			packets;
+	u64			bytes;
+	struct u64_stats_sync	syncp;
+};
+
+enum rtl_output_mode {
+	OUTPUT_EMBEDDED_PHY,
+	OUTPUT_RGMII_TO_MAC,
+	OUTPUT_RGMII_TO_PHY,
+	OUTPUT_SGMII_TO_MAC,
+	OUTPUT_SGMII_TO_PHY,
+	OUTPUT_RMII,
+	OUTPUT_FORCE_LINK,
+	OUTPUT_MAX
+};
+
+/* ISO base addr 0x98007000 */
+enum common_iso_registers {
+	ISO_UMSK_ISR			= 0x0004,
+	ISO_POWERCUT_ETN		= 0x005c,
+	ISO_ETN_TESTIO			= 0x0060,
+	ISO_SOFT_RESET			= 0x0088,
+	ISO_CLOCK_ENABLE		= 0x008c,
+	ISO_POR_CTRL			= 0x0210,
+	ISO_POR_VTH			= 0x0214,
+	ISO_POR_DATAI			= 0x0218,
+};
+
+#define POR_XV_MASK	0x00000111
+#define POR_NUM		3
+
+/* RTD129X */
+#define RTD129X_RGMII0_PAD_CTRL_ADDR	0x9801a960
+#define RTD129X_ISO_RGMII_MDIO_TO_GMAC	0x98007064
+
+enum rtd129x_rgmii_voltage {
+	RTD129X_VOLTAGE_1_DOT_8V = 1,
+	RTD129X_VOLTAGE_2_DOT_5V,
+	RTD129X_VOLTAGE_3_DOT_3V,
+	RTD129X_VOLTAGE_MAX
+};
+
+enum rtd129x_rgmii_delay {
+	RTD129X_RGMII_DELAY_0NS,
+	RTD129X_RGMII_DELAY_2NS,
+	RTD129X_RGMII_DELAY_MAX
+};
+
+/* RTD139X */
+#define RTD139X_R_K_DEFAULT		0x8
+#define RTD139X_IDAC_FINE_DEFAULT	0x33
+
+/* SDS base addr 0x981c8000 */
+enum rtd139x_sds_registers {
+	RTD139X_SDS_REG02	= 0x0008,
+	RTD139X_SDS_REG28	= 0x0070,
+	RTD139X_SDS_REG29	= 0x0074,
+	RTD139X_SDS_MISC	= 0x1804,
+	RTD139X_SDS_LINK	= 0x1810,
+};
+
+/* ISO testmux base addr 0x9804e000 */
+enum rtd139x_testmux_registers {
+	RTD139X_ISO_TESTMUX_MUXPAD0	= 0x0000,
+	RTD139X_ISO_TESTMUX_MUXPAD1	= 0x0004,
+	RTD139X_ISO_TESTMUX_MUXPAD2	= 0x0008,
+};
+
+/* SBX and SC_WRAP base addr 0x9801c000 */
+enum rtd139x_sbx_sc_wrap_registers {
+	RTD139X_SBX_SB3_CHANNEL_REQ_MASK	= 0x020c,
+	RTD139X_SBX_SB3_CHANNEL_REQ_BUSY	= 0x0210,
+	RTD139X_SBX_ACP_CHANNEL_REQ_MASK	= 0x080c,
+	RTD139X_SBX_ACP_CHANNEL_REQ_BUSY	= 0x0810,
+	RTD139X_SBX_ACP_MISC_CTRL		= 0x0814,
+	RTD139X_SC_WRAP_ACP_CRT_CTRL		= 0x1030,
+	RTD139X_SC_WRAP_CRT_CTRL		= 0x1100,
+	RTD139X_SC_WRAP_INTERFACE_EN		= 0x1124,
+	RTD139X_SC_WRAP_ACP_CTRL		= 0x1800,
+};
+
+enum rtd139x_phy_addr_e {
+	/* embedded PHY PHY ID */
+	RTD139X_INT_PHY_ADDR		= 1,
+	/* embedded SerDes DPHY PHY ID 0, RL6481_T28_SGMII.doc ANA00~ANA0F */
+	RTD139X_SERDES_DPHY_0		= 0,
+	/* embedded SerDes DPHY PHY ID 1, RL6481_T28_SGMII.doc ANA20~ANA2F */
+	RTD139X_SERDES_DPHY_1		= 1,
+	/* external RTL8211FS SGMII PHY ID */
+	RTD139X_EXT_PHY_ADDR		= 3,
+};
+
+enum rtd139x_sgmii_swing_e {
+	RTD139X_TX_SWING_1040MV		= (0X0 << 8),	/* DEFAULT */
+	RTD139X_TX_SWING_693MV		= (0X1 << 8),
+	RTD139X_TX_SWING_474MV		= (0X2 << 8),
+	RTD139X_TX_SWING_352MV		= (0X3 << 8),
+	RTD139X_TX_SWING_312MV		= (0X4 << 8),
+};
+
+#define RTD139X_SGMII_SWING		(0X3 << 8)
+
+/* RTD16XX */
+#define RTD16XX_RC_K_DEFAULT		0x8888
+#define RTD16XX_R_K_DEFAULT		0x8888
+#define RTD16XX_AMP_K_DEFAULT		0x7777
+#define RTD16XX_ADC_BIAS_K_DEFAULT	0x8888
+
+/* SDS base addr 0x981c8000 */
+enum rtd16xx_sds_registers {
+	RTD16XX_SDS_REG02	= 0x0008,
+	RTD16XX_SDS_MISC	= 0x1804,
+	RTD16XX_SDS_LINK	= 0x180c,
+	RTD16XX_SDS_DEBUG	= 0x1810,
+};
+
+/* ISO testmux base addr 0x9804e000 */
+enum rtd16xx_testmux_registers {
+	RTD16XX_ISO_TESTMUX_MUXPAD0	= 0x0000,
+	RTD16XX_ISO_TESTMUX_MUXPAD1	= 0x0004,
+	RTD16XX_ISO_TESTMUX_MUXPAD2	= 0x0008,
+};
+
+/* SBX and SC_WRAP base addr 0x9801c000 */
+enum rtd16xx_sbx_sc_wrap_registers {
+	RTD16XX_SBX_SB3_CHANNEL_REQ_MASK	= 0x020c,
+	RTD16XX_SBX_SB3_CHANNEL_REQ_BUSY	= 0x0210,
+	RTD16XX_SBX_ACP_CHANNEL_REQ_MASK	= 0x080c,
+	RTD16XX_SBX_ACP_CHANNEL_REQ_BUSY	= 0x0810,
+	RTD16XX_SBX_ACP_MISC_CTRL		= 0x0814,
+	RTD16XX_SC_WRAP_ACP_CRT_CTRL		= 0x1030,
+	RTD16XX_SC_WRAP_CRT_CTRL		= 0x1100,
+	RTD16XX_SC_WRAP_INTERFACE_EN		= 0x1124,
+	RTD16XX_SC_WRAP_ACP_CTRL		= 0x1800,
+};
+
+enum rtd16xx_phy_addr_e {
+	/* embedded PHY PHY ID */
+	RTD16XX_INT_PHY_ADDR		= 1,
+	/* embedded SerDes DPHY PHY ID 0, RL6481_T28_SGMII.doc ANA00~ANA0F */
+	RTD16XX_SERDES_DPHY_0		= 0,
+	/* embedded SerDes DPHY PHY ID 1, RL6481_T28_SGMII.doc ANA20~ANA2F */
+	RTD16XX_SERDES_DPHY_1		= 1,
+	/* external RTL8211FS SGMII PHY ID */
+	RTD16XX_EXT_PHY_ADDR		= 3,
+};
+
+enum rtd16xx_sgmii_swing_e {
+	RTD16XX_TX_SWING_550MV,
+	RTD16XX_TX_SWING_380MV,
+	RTD16XX_TX_SWING_250MV,
+	RTD16XX_TX_SWING_190MV
+};
+
+/* RTD13XX */
+#define RTD13XX_R_K_DEFAULT		0x8
+#define RTD13XX_IDAC_FINE_DEFAULT	0x77
+
+/* ISO testmux base addr 0x9804e000 */
+enum rtd13xx_testmux_registers {
+	RTD13XX_ISO_TESTMUX_MUXPAD0	= 0x0000,
+	RTD13XX_ISO_TESTMUX_MUXPAD1	= 0x0004,
+	RTD13XX_ISO_TESTMUX_MUXPAD2	= 0x0008,
+	RTD13XX_ISO_TESTMUX_MUXPAD5	= 0x0014,
+	RTD13XX_ISO_TESTMUX_MUXPAD6	= 0x0018,
+	RTD13XX_ISO_TESTMUX_PFUNC9	= 0x0040, /* MDC/MDIO current */
+	RTD13XX_ISO_TESTMUX_PFUNC20	= 0x006c, /* RGMII current */
+	RTD13XX_ISO_TESTMUX_PFUNC21	= 0x0070, /* RGMII current */
+	RTD13XX_ISO_TESTMUX_PFUNC25	= 0x0090, /* RGMII BIAS */
+};
+
+/* SBX and SC_WRAP base addr 0x9801c000 */
+enum rtd13xx_sbx_sc_wrap_registers {
+	RTD13XX_SBX_SB3_CHANNEL_REQ_MASK	= 0x020c,
+	RTD13XX_SBX_SB3_CHANNEL_REQ_BUSY	= 0x0210,
+	RTD13XX_SBX_ACP_CHANNEL_REQ_MASK	= 0x080c,
+	RTD13XX_SBX_ACP_CHANNEL_REQ_BUSY	= 0x0810,
+	RTD13XX_SBX_ACP_MISC_CTRL		= 0x0814,
+	RTD13XX_SC_WRAP_ACP_CRT_CTRL		= 0x1030,
+	RTD13XX_SC_WRAP_CRT_CTRL		= 0x1100,
+	RTD13XX_SC_WRAP_INTERFACE_EN		= 0x1124,
+	RTD13XX_SC_WRAP_ACP_CTRL		= 0x1800,
+};
+
+/* end of register locations per chip */
+
+#define RTL_WPD_SIZE		16
+#define RTL_WPD_MASK_SIZE	16
+
+#ifdef RTL_PROC
+static struct proc_dir_entry *rtw_proc;
+#endif
+/* wol_enable
+ * BIT 0: WoL enable
+ * BIT 1: CRC match
+ * BIT 2: WPD
+ */
+enum wol_flags {
+	WOL_MAGIC			= 0x1,
+	WOL_CRC_MATCH			= 0x2,
+	WOL_WPD				= 0x4,
+};
+
+#define WOL_BUF_LEN		128
+
+struct rtl8169_rgmii_info {
+	u8 voltage; /* 1:1.8V, 2: 2.5V, 3:3.3V */
+	u8 tx_delay; /* 0: 0ns, 1: 2ns */
+	u8 rx_delay; /* 0: 0ns, 1: 2ns */
+};
+
+struct rtl8169_sgmii_info {
+	u8 swing; /* 0:640mV, 1:380mV, 2:250mV, 3:190mV */
+};
+
+struct rtl8169_rmii_info {
+	u8 voltage; /* 1:1.8V, 2: 2.5V, 3:3.3V */
+	u8 tx_delay; /* 0: 0ns, 1: 2ns */
+	u8 rx_delay; /* 0 ~ 7 x 0.5ns */
+};
+
+struct rtl8169_private {
+	void __iomem *mmio_addr;	/* memory map physical address */
+	void __iomem *mmio_clkaddr;
+	void __iomem *mmio_sbxaddr;
+	void __iomem *mmio_sdsaddr;
+	void __iomem *mmio_pinmuxaddr;
+
+	struct platform_device *pdev;
+	struct net_device *dev;
+	struct napi_struct napi;
+	u32 msg_enable;
+	u16 txd_version;
+	u16 mac_version;
+	u32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */
+	u32 cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */
+	u32 dirty_tx;
+
+#if defined(CONFIG_RTL_RX_NO_COPY)
+	u32 dirty_rx;
+	u32 last_dirty_tx;
+	u32 tx_reset_count;
+	u32 last_cur_rx;
+	u32 rx_reset_count;
+	u8 check_rdu;
+#endif				/* CONFIG_RTL_RX_NO_COPY */
+
+	struct rtl8169_stats rx_stats;
+	struct rtl8169_stats tx_stats;
+	struct tx_desc *tx_desc_array;	/* 256-aligned Tx descriptor ring */
+	struct rx_desc *rx_desc_array;	/* 256-aligned Rx descriptor ring */
+	dma_addr_t tx_phy_addr;
+	dma_addr_t rx_phy_addr;
+
+	/* Rx data buffers */
+	#if defined(CONFIG_RTL_RX_NO_COPY)
+	struct sk_buff *rx_databuff[NUM_RX_DESC]; /* RTL_FEATURE_RX_NO_COPY */
+	#else
+	void *rx_databuff[NUM_RX_DESC];
+	#endif /* CONFIG_RTL_RX_NO_COPY */
+
+	struct ring_info tx_skb[NUM_TX_DESC];	/* Tx data buffers */
+	u16 cp_cmd;
+
+	u16 event_slow;
+
+#ifdef RTL_PROC
+	struct proc_dir_entry *dir_dev;
+#endif
+
+	struct {
+		DECLARE_BITMAP(flags, RTL_FLAG_MAX);
+		struct mutex mutex; /* mutex for work */
+		struct work_struct work;
+	} wk;
+
+	unsigned int features;
+
+	struct mii_if_info mii;
+	struct rtl8169_counters counters;
+	u32 saved_wolopts;
+	u32 opts1_mask;
+
+	u8 wol_enable;
+	u8 wol_crc_cnt;
+	u8 wol_mask[RTL_WPD_SIZE][RTL_WPD_MASK_SIZE];
+	u8 wol_mask_size[RTL_WPD_SIZE];
+	u32 wol_crc[RTL_WPD_SIZE];
+
+	u32 ocp_base;
+	u32 led_cfg;
+
+	struct r8169soc_chip_info *chip;
+	u32 phy_irq[POR_NUM];
+	u8 phy_irq_map[POR_NUM];
+	u8 phy_irq_num;
+	u32 phy_por_xv_mask;
+	atomic_t phy_reinit_flag;
+	enum rtl_output_mode output_mode;
+	union {
+		struct rtl8169_rgmii_info rgmii;
+		struct rtl8169_sgmii_info sgmii;
+		struct rtl8169_rmii_info rmii;
+	};
+
+	bool bypass_enable;	/* 0: disable, 1: enable */
+	u8 ext_phy_id;		/* 0 ~ 31 */
+	bool eee_enable;	/* 0: disable, 1: enable */
+	bool acp_enable;	/* 0: disable, 1: enable */
+	bool ext_phy;
+};
+
+#define REVISION_A00	0xA00
+#define REVISION_A01	0xA01
+#define REVISION_A02	0xA02
+#define REVISION_B00	0xB00
+#define REVISION_B01	0xB01
+#define REVISION_B02	0xB02
+#define REVISION_NONE	0xFFF
+
+struct r8169soc_chip_info {
+	const char *name;
+	void (*mdio_write)(struct rtl8169_private *tp, int reg, int value);
+	int (*mdio_read)(struct rtl8169_private *tp, int reg);
+	void (*mdio_lock)(struct rtl8169_private *tp);
+	void (*mdio_unlock)(struct rtl8169_private *tp);
+	void (*pll_power_down)(struct rtl8169_private *tp);
+	void (*pll_power_up)(struct rtl8169_private *tp);
+	void (*csi_write)(struct rtl8169_private *tp, int addr, int value);
+	u32 (*csi_read)(struct rtl8169_private *tp, int addr);
+	int (*set_speed)(struct net_device *dev, u8 aneg, u16 sp, u8 dpx,
+			 u32 adv);
+	void (*phy_reset_enable)(struct rtl8169_private *tp);
+	u32 (*phy_reset_pending)(struct rtl8169_private *tp);
+	u32 (*link_ok)(void __iomem *ioaddr);
+	int (*do_ioctl)(struct rtl8169_private *tp,
+			struct mii_ioctl_data *data, int cmd);
+	void (*hw_start)(struct net_device *dev);
+	void (*reset_phy_gmac)(struct rtl8169_private *tp);
+	void (*acp_init)(struct rtl8169_private *tp);
+	void (*pll_clock_init)(struct rtl8169_private *tp);
+	void (*mdio_init)(struct rtl8169_private *tp);
+	void (*mac_mcu_patch)(struct rtl8169_private *tp);
+	void (*hw_phy_config)(struct rtl8169_private *tp);
+	void (*crc_wakeup)(struct rtl8169_private *tp);
+	void (*eee_set)(struct rtl8169_private *tp, bool enable);
+	void (*led_set)(struct rtl8169_private *tp, bool enable);
+	void (*dump_regs)(struct seq_file *m, struct rtl8169_private *tp);
+	void (*dump_var)(struct seq_file *m, struct rtl8169_private *tp);
+
+	int mac_version;
+	int cfg_version;
+	enum rtl_tx_desc_version txd_version;
+	u32 led_cfg;
+	u32 region;
+	u32 align;
+	u32 features;
+	u16 event_slow;
+	u16 jumbo_max;
+	bool jumbo_tx_csum;
+};
+
+MODULE_AUTHOR("Realtek and the Linux r8169soc crew <netdev at vger.kernel.org>");
+MODULE_DESCRIPTION("RealTek RTL-8169soc Gigabit Ethernet driver");
+module_param_named(debug, debug.msg_enable, int, 0644);
+
+MODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., 16=all)");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RTL8169_VERSION);
+
+static void rtl_lock_work(struct rtl8169_private *tp)
+{
+	mutex_lock(&tp->wk.mutex);
+}
+
+static void rtl_unlock_work(struct rtl8169_private *tp)
+{
+	mutex_unlock(&tp->wk.mutex);
+}
+
+struct rtl_cond {
+	bool (*check)(struct rtl8169_private *tp);
+	const char *msg;
+};
+
+static void rtl_udelay(unsigned int d)
+{
+	usleep_range(d, d + 1);
+}
+
+static bool rtl_loop_wait(struct rtl8169_private *tp, const struct rtl_cond *c,
+			  void (*delay)(unsigned int),
+			  unsigned int d, int n, bool high)
+{
+	int i;
+
+	for (i = 0; i < n; i++) {
+		delay(d);
+		if (c->check(tp) == high)
+			return true;
+	}
+	netif_err(tp, drv, tp->dev, "%s == %d (loop: %d, delay: %d).\n",
+		  c->msg, !high, n, d);
+	return false;
+}
+
+static bool rtl_udelay_loop_wait_high(struct rtl8169_private *tp,
+				      const struct rtl_cond *c,
+				      unsigned int d, int n)
+{
+	return rtl_loop_wait(tp, c, rtl_udelay, d, n, true);
+}
+
+static bool rtl_udelay_loop_wait_low(struct rtl8169_private *tp,
+				     const struct rtl_cond *c,
+				     unsigned int d, int n)
+{
+	return rtl_loop_wait(tp, c, rtl_udelay, d, n, false);
+}
+
+static __maybe_unused bool
+rtl_msleep_loop_wait_high(struct rtl8169_private *tp,
+			  const struct rtl_cond *c, unsigned int d, int n)
+{
+	return rtl_loop_wait(tp, c, msleep, d, n, true);
+}
+
+static bool rtl_msleep_loop_wait_low(struct rtl8169_private *tp,
+				     const struct rtl_cond *c,
+				     unsigned int d, int n)
+{
+	return rtl_loop_wait(tp, c, msleep, d, n, false);
+}
+
+#define DECLARE_RTL_COND(name)				\
+static bool name ## _check(struct rtl8169_private *);	\
+							\
+static const struct rtl_cond name = {			\
+	.check	= name ## _check,			\
+	.msg	= #name					\
+};							\
+							\
+static bool name ## _check(struct rtl8169_private *tp)
+
+DECLARE_RTL_COND(rtl_eriar_cond)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	return RTL_R32(ERIAR) & ERIAR_FLAG;
+}
+
+static bool rtl_ocp_reg_failure(struct rtl8169_private *tp, u32 reg)
+{
+	if (reg & 0xffff0001) {
+		netif_err(tp, drv, tp->dev, "Invalid ocp reg %x!\n", reg);
+		return true;
+	}
+	return false;
+}
+
+static void rtl_ocp_write(struct rtl8169_private *tp, u32 reg, u32 value)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+	unsigned int wdata;
+
+	if (rtl_ocp_reg_failure(tp, reg))
+		return;
+
+	wdata = OCPDR_WRITE_CMD |
+		(((reg >> 1) & OCPDR_REG_MASK) << OCPDR_REG_SHIFT) |
+		(value & OCPDR_DATA_MASK);
+	RTL_W32(OCPDR, wdata);
+}
+
+static u32 rtl_ocp_read(struct rtl8169_private *tp, u32 reg)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+	unsigned int wdata;
+	unsigned int rdata;
+
+	if (rtl_ocp_reg_failure(tp, reg))
+		return 0;
+
+	wdata = OCPDR_READ_CMD |
+		(((reg >> 1) & OCPDR_REG_MASK) << OCPDR_REG_SHIFT);
+	RTL_W32(OCPDR, wdata);
+	rdata = RTL_R32(OCPDR);
+	return (rdata & OCPDR_DATA_MASK);
+}
+
+#define OCP_STD_PHY_BASE	0xa400
+
+static __maybe_unused void mac_mcu_write(struct rtl8169_private *tp, int reg,
+					 int value)
+{
+	if (reg == 0x1f) {
+		tp->ocp_base = value << 4;
+		return;
+	}
+
+	rtl_ocp_write(tp, tp->ocp_base + reg, value);
+}
+
+static __maybe_unused int mac_mcu_read(struct rtl8169_private *tp, int reg)
+{
+	return rtl_ocp_read(tp, tp->ocp_base + reg);
+}
+
+DECLARE_RTL_COND(rtl_ephyar_cond)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	return RTL_R32(EPHYAR) & EPHYAR_FLAG;
+}
+
+static void rtl_ephy_write(struct rtl8169_private *tp, int reg_addr, int value)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	RTL_W32(EPHYAR, EPHYAR_WRITE_CMD | (value & EPHYAR_DATA_MASK) |
+		(reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT);
+
+	rtl_udelay_loop_wait_low(tp, &rtl_ephyar_cond, 10, 100);
+
+	usleep_range(10, 11);
+}
+
+static u16 rtl_ephy_read(struct rtl8169_private *tp, int reg_addr)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	RTL_W32(EPHYAR, (reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT);
+
+	return rtl_udelay_loop_wait_high(tp, &rtl_ephyar_cond, 10, 100) ?
+		RTL_R32(EPHYAR) & EPHYAR_DATA_MASK : ~0;
+}
+
+DECLARE_RTL_COND(rtl_phyar_cond)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	return RTL_R32(PHYAR) & 0x80000000;
+}
+
+static void r8169_mdio_write(struct rtl8169_private *tp, int reg, int value)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	if (tp->ext_phy) {
+		rtl_ephy_write(tp, reg, value);
+		return;
+	}
+	RTL_W32(PHYAR, 0x80000000 | (reg & 0x1f) << 16 | (value & 0xffff));
+
+	rtl_udelay_loop_wait_low(tp, &rtl_phyar_cond, 25, 20);
+	/* According to hardware specs a 20us delay is required after write
+	 * complete indication, but before sending next command.
+	 */
+	usleep_range(20, 21);
+}
+
+static int r8169_mdio_read(struct rtl8169_private *tp, int reg)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+	int value;
+
+	if (tp->ext_phy)
+		return rtl_ephy_read(tp, reg);
+
+	RTL_W32(PHYAR, 0x0 | (reg & 0x1f) << 16);
+
+	value = rtl_udelay_loop_wait_high(tp, &rtl_phyar_cond, 25, 20) ?
+		RTL_R32(PHYAR) & 0xffff : ~0;
+
+	/* According to hardware specs a 20us delay is required after read
+	 * complete indication, but before sending next command.
+	 */
+	usleep_range(20, 21);
+
+	return value;
+}
+
+static inline void rtl_writephy(struct rtl8169_private *tp,
+				int location, u32 val)
+{
+	tp->chip->mdio_write(tp, location, val);
+}
+
+static inline int rtl_readphy(struct rtl8169_private *tp, int location)
+{
+	return tp->chip->mdio_read(tp, location);
+}
+
+static inline void rtl_patchphy(struct rtl8169_private *tp, int reg_addr,
+				int value)
+{
+	rtl_writephy(tp, reg_addr, rtl_readphy(tp, reg_addr) | value);
+}
+
+static __maybe_unused void rtl_w1w0_phy(struct rtl8169_private *tp,
+					int reg_addr, int p, int m)
+{
+	int val;
+
+	val = rtl_readphy(tp, reg_addr);
+	rtl_writephy(tp, reg_addr, (val | p) & ~m);
+}
+
+static void rtl_mdio_write(struct net_device *dev, int phy_id, int location,
+			   int val)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	tp->chip->mdio_lock(tp);
+	rtl_writephy(tp, location, val);
+	tp->chip->mdio_unlock(tp);
+}
+
+static int rtl_mdio_read(struct net_device *dev, int phy_id, int location)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+	int ret;
+
+	tp->chip->mdio_lock(tp);
+	ret = rtl_readphy(tp, location);
+	tp->chip->mdio_unlock(tp);
+	return ret;
+}
+
+static void rtl_eri_write(struct rtl8169_private *tp, int addr, u32 mask,
+			  u32 val, int type)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	WARN_ON((addr & 3) || (mask == 0));
+	RTL_W32(ERIDR, val);
+	RTL_W32(ERIAR, ERIAR_WRITE_CMD | type | mask | addr);
+
+	rtl_udelay_loop_wait_low(tp, &rtl_eriar_cond, 100, 100);
+}
+
+static u32 rtl_eri_read(struct rtl8169_private *tp, int addr, int type)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	RTL_W32(ERIAR, ERIAR_READ_CMD | type | ERIAR_MASK_1111 | addr);
+
+	return rtl_udelay_loop_wait_high(tp, &rtl_eriar_cond, 100, 100) ?
+		RTL_R32(ERIDR) : ~0;
+}
+
+static void rtl_w1w0_eri(struct rtl8169_private *tp, int addr, u32 mask, u32 p,
+			 u32 m, int type)
+{
+	u32 val;
+
+	val = rtl_eri_read(tp, addr, type);
+	rtl_eri_write(tp, addr, mask, (val & ~m) | p, type);
+}
+
+struct exgmac_reg {
+	u16 addr;
+	u16 mask;
+	u32 val;
+};
+
+static void rtl_write_exgmac_batch(struct rtl8169_private *tp,
+				   const struct exgmac_reg *r, int len)
+{
+	while (len-- > 0) {
+		rtl_eri_write(tp, r->addr, r->mask, r->val, ERIAR_EXGMAC);
+		r++;
+	}
+}
+
+static u16 rtl_get_events(struct rtl8169_private *tp)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	return RTL_R16(INTR_STATUS);
+}
+
+static void rtl_ack_events(struct rtl8169_private *tp, u16 bits)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	RTL_W16(INTR_STATUS, bits);
+}
+
+static void rtl_irq_disable(struct rtl8169_private *tp)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	RTL_W16(INTR_MASK, 0);
+}
+
+static void rtl_irq_enable(struct rtl8169_private *tp, u16 bits)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	RTL_W16(INTR_MASK, bits);
+}
+
+#define RTL_EVENT_NAPI_RX	(RX_OK | RX_ERR | RX_OVERFLOW)
+#define RTL_EVENT_NAPI_TX	(TX_OK | TX_ERR)
+#define RTL_EVENT_NAPI		(RTL_EVENT_NAPI_RX | RTL_EVENT_NAPI_TX)
+
+static void rtl_irq_enable_all(struct rtl8169_private *tp)
+{
+	rtl_irq_enable(tp, RTL_EVENT_NAPI | tp->event_slow);
+}
+
+static void rtl8169_irq_mask_and_ack(struct rtl8169_private *tp)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	rtl_irq_disable(tp);
+	rtl_ack_events(tp, RTL_EVENT_NAPI | tp->event_slow);
+	RTL_R8(CHIP_CMD);
+}
+
+static unsigned int rtl8169_xmii_reset_pending(struct rtl8169_private *tp)
+{
+	int ret;
+
+	tp->chip->mdio_lock(tp);
+	ret = rtl_readphy(tp, MII_BMCR);
+	tp->chip->mdio_unlock(tp);
+	return ret & BMCR_RESET;
+}
+
+static unsigned int rtl8169_xmii_link_ok(void __iomem *ioaddr)
+{
+	return RTL_R8(PHY_STATUS) & LINK_STATUS;
+}
+
+static unsigned int rtl8169_xmii_always_link_ok(void __iomem *ioaddr)
+{
+	return true;
+}
+
+static void rtl8169_xmii_reset_enable(struct rtl8169_private *tp)
+{
+	unsigned int val;
+
+	tp->chip->mdio_lock(tp);
+	val = rtl_readphy(tp, MII_BMCR) | BMCR_RESET;
+	rtl_writephy(tp, MII_BMCR, val & 0xffff);
+	tp->chip->mdio_unlock(tp);
+}
+
+void r8169_mmd_write(struct rtl8169_private *tp,
+		     u32 dev_addr, u32 reg_addr, u32 value)
+{
+	/* page 0 */
+	r8169_mdio_write(tp, 0x1f, 0);
+	/* address mode */
+	r8169_mdio_write(tp, 13, (0x3fff & dev_addr));
+	/* write the desired address */
+	r8169_mdio_write(tp, 14, reg_addr);
+	/* data mode, no post increment */
+	r8169_mdio_write(tp, 13, ((0x3fff & dev_addr) | (0x1 << 14)));
+	/* write the content of the selected register */
+	r8169_mdio_write(tp, 14, value);
+}
+
+u32 r8169_mmd_read(struct rtl8169_private *tp, u32 dev_addr, u32 reg_addr)
+{
+	/* page 0 */
+	r8169_mdio_write(tp, 0x1f, 0);
+	/* address mode */
+	r8169_mdio_write(tp, 13, (0x3fff & dev_addr));
+	/* write the desired address */
+	r8169_mdio_write(tp, 14, reg_addr);
+	/* data mode, no post increment */
+	r8169_mdio_write(tp, 13, ((0x3fff & dev_addr) | (0x1 << 14)));
+	/* read the content of the selected register */
+	return r8169_mdio_read(tp, 14);
+}
+
+void r8169_display_eee_info(struct net_device *dev, struct seq_file *m,
+			    struct rtl8169_private *tp)
+{
+	/* display DUT and link partner EEE capability */
+	unsigned int temp, tmp1, tmp2;
+	int speed = 0;
+	bool eee1000, eee100, eee10;
+	int duplex;
+
+	tp->chip->mdio_lock(tp);
+	/* page 0xa43 */
+	r8169_mdio_write(tp, 0x1f, 0xa43);
+	temp = r8169_mdio_read(tp, 26);
+	if (0 == ((0x1 << 2) & temp)) {
+		tp->chip->mdio_unlock(tp);
+		seq_printf(m, "%s: link is down\n", dev->name);
+		return;
+	}
+
+	if ((0x1 << 3) & temp)
+		duplex = 1;
+	else
+		duplex = 0;
+
+	if ((0x0 << 4) == ((0x3 << 4) & temp)) {
+		speed = 10;
+
+		r8169_mdio_write(tp, 0x1f, 0xbcd);
+		tmp1 = r8169_mdio_read(tp, 19);
+
+		r8169_mdio_write(tp, 0x1f, 0xa60);
+		tmp2 = r8169_mdio_read(tp, 16);
+
+		if (0 == ((0x1 << 4) & tmp1))
+			eee10 = false;
+		else
+			eee10 = true;
+
+		tp->chip->mdio_unlock(tp);
+		seq_printf(m, "%s: link speed = %dM %s, EEE = %s, PCS_Status = 0x%02x\n",
+			   dev->name, speed,
+			   duplex ? "full" : "half",
+			   eee10 ? "Y" : "N",
+			   tmp2 & 0xff);
+		return;
+	}
+	if ((0x1 << 4) == ((0x3 << 4) & temp))
+		speed = 100;
+	if ((0x2 << 4) == ((0x3 << 4) & temp))
+		speed = 1000;
+	if ((0x1 << 8) == ((0x1 << 8) & temp)) {
+		seq_printf(m, "%s: link speed = %dM %s, EEE = Y\n", dev->name,
+			   speed, duplex ? "full" : "half");
+
+		r8169_mdio_write(tp, 0x1f, 0xa60);
+		tmp1 = r8169_mdio_read(tp, 16);
+
+		r8169_mdio_write(tp, 0x1f, 0xa5c);
+		tmp2 = r8169_mdio_read(tp, 19);
+		seq_printf(m, "PCS_Status = 0x%02x, EEE_wake_error = 0x%04x\n",
+			   tmp1 & 0xff, tmp2);
+	} else {
+		seq_printf(m, "%s: link speed = %dM %s, EEE = N\n", dev->name,
+			   speed, duplex ? "full" : "half");
+
+		temp = r8169_mmd_read(tp, 0x7, 0x3d);
+		if (0 == (temp & (0x1 << 2)))
+			eee1000 = false;
+		else
+			eee1000 = true;
+
+		if (0 == (temp & (0x1 << 1)))
+			eee100 = false;
+		else
+			eee100 = true;
+
+		seq_printf(m, "%s: Link Partner EEE1000=%s, EEE100=%s\n",
+			   dev->name, eee1000 ? "Y" : "N", eee100 ? "Y" : "N");
+	}
+	tp->chip->mdio_unlock(tp);
+}
+
+static void __rtl8169_check_link_status(struct net_device *dev,
+					struct rtl8169_private *tp,
+					void __iomem *ioaddr, bool pm)
+{
+	if (tp->chip->link_ok(ioaddr)) {
+		/* This is to cancel a scheduled suspend if there's one. */
+		if (pm)
+			pm_request_resume(&tp->pdev->dev);
+		netif_carrier_on(dev);
+		if (net_ratelimit())
+			netif_info(tp, ifup, dev, "link up\n");
+	} else {
+		netif_carrier_off(dev);
+		netif_info(tp, ifdown, dev, "link down\n");
+		if (pm)
+			pm_schedule_suspend(&tp->pdev->dev, 5000);
+	}
+}
+
+static void rtl8169_check_link_status(struct net_device *dev,
+				      struct rtl8169_private *tp,
+				      void __iomem *ioaddr)
+{
+	__rtl8169_check_link_status(dev, tp, ioaddr, false);
+}
+
+#define WAKE_ANY (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST)
+
+static u32 __rtl8169_get_wol(struct rtl8169_private *tp)
+{
+	u32 wolopts = 0;
+
+	if (tp->wol_enable & WOL_MAGIC)
+		wolopts |= WAKE_MAGIC;
+
+	return wolopts;
+}
+
+static void rtl8169_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	rtl_lock_work(tp);
+
+	wol->supported = WAKE_ANY;
+	wol->wolopts = __rtl8169_get_wol(tp);
+
+	rtl_unlock_work(tp);
+}
+
+static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts)
+{
+	if (wolopts & WAKE_MAGIC)
+		tp->wol_enable |= WOL_MAGIC;
+	else
+		tp->wol_enable &= ~WOL_MAGIC;
+}
+
+static int rtl8169_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	rtl_lock_work(tp);
+
+	if (wol->wolopts)
+		tp->features |= RTL_FEATURE_WOL;
+	else
+		tp->features &= ~RTL_FEATURE_WOL;
+	__rtl8169_set_wol(tp, wol->wolopts);
+
+	rtl_unlock_work(tp);
+
+	device_set_wakeup_enable(&tp->pdev->dev, wol->wolopts);
+
+	return 0;
+}
+
+static void rtl8169_get_drvinfo(struct net_device *dev,
+				struct ethtool_drvinfo *info)
+{
+	strscpy(info->driver, MODULENAME, sizeof(info->driver));
+	strscpy(info->version, RTL8169_VERSION, sizeof(info->version));
+	strscpy(info->bus_info, "RTK-ETN", sizeof(info->bus_info));
+}
+
+static int rtl8169_get_regs_len(struct net_device *dev)
+{
+	return R8169_REGS_SIZE;
+}
+
+static int rtl8169_set_speed_xmii(struct net_device *dev,
+				  u8 autoneg, u16 speed, u8 duplex, u32 adv)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+	int giga_ctrl, bmcr;
+	int rc = -EINVAL;
+
+	tp->chip->mdio_lock(tp);
+	rtl_writephy(tp, 0x1f, 0x0000);
+
+	if (autoneg == AUTONEG_ENABLE) {
+		int auto_nego;
+
+		auto_nego = rtl_readphy(tp, MII_ADVERTISE);
+		auto_nego &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL |
+			ADVERTISE_100HALF | ADVERTISE_100FULL);
+
+		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;
+
+		auto_nego |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
+
+		giga_ctrl = rtl_readphy(tp, MII_CTRL1000);
+		giga_ctrl &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
+
+		/* The 8100e/8101e/8102e do Fast Ethernet only. */
+		if (tp->mii.supports_gmii) {
+			if (adv & ADVERTISED_1000baseT_Half)
+				giga_ctrl |= ADVERTISE_1000HALF;
+			if (adv & ADVERTISED_1000baseT_Full)
+				giga_ctrl |= ADVERTISE_1000FULL;
+		} else if (adv & (ADVERTISED_1000baseT_Half |
+				ADVERTISED_1000baseT_Full)) {
+			netif_info(tp, link, dev,
+				   "PHY does not support 1000Mbps\n");
+			goto out;
+		}
+
+		bmcr = BMCR_ANENABLE | BMCR_ANRESTART;
+
+		rtl_writephy(tp, MII_ADVERTISE, auto_nego);
+		rtl_writephy(tp, MII_CTRL1000, giga_ctrl);
+	} else {
+		giga_ctrl = 0;
+
+		if (speed == SPEED_10)
+			bmcr = 0;
+		else if (speed == SPEED_100)
+			bmcr = BMCR_SPEED100;
+		else
+			goto out;
+
+		if (duplex == DUPLEX_FULL)
+			bmcr |= BMCR_FULLDPLX;
+	}
+
+	rtl_writephy(tp, MII_BMCR, bmcr);
+
+	rc = 0;
+out:
+	tp->chip->mdio_unlock(tp);
+	return rc;
+}
+
+static int rtl8169_set_speed(struct net_device *dev,
+			     u8 autoneg, u16 speed, u8 duplex, u32 advertising)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+	int ret;
+
+	ret = tp->chip->set_speed(dev, autoneg, speed, duplex, advertising);
+	return ret;
+}
+
+static netdev_features_t rtl8169_fix_features(struct net_device *dev,
+					      netdev_features_t features)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	if (dev->mtu > TD_MSS_MAX)
+		features &= ~NETIF_F_ALL_TSO;
+
+	if (dev->mtu > JUMBO_1K && !tp->chip->jumbo_tx_csum)
+		features &= ~NETIF_F_IP_CSUM;
+
+	return features;
+}
+
+static void __rtl8169_set_features(struct net_device *dev,
+				   netdev_features_t features)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+	netdev_features_t changed = features ^ dev->features;
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	if (!(changed & (NETIF_F_RXALL | NETIF_F_RXCSUM |
+			 NETIF_F_HW_VLAN_CTAG_RX)))
+		return;
+
+	if (changed & (NETIF_F_RXCSUM | NETIF_F_HW_VLAN_CTAG_RX)) {
+		if (features & NETIF_F_RXCSUM)
+			tp->cp_cmd |= RX_CHK_SUM;
+		else
+			tp->cp_cmd &= ~RX_CHK_SUM;
+
+		if (dev->features & NETIF_F_HW_VLAN_CTAG_RX)
+			tp->cp_cmd |= RX_VLAN;
+		else
+			tp->cp_cmd &= ~RX_VLAN;
+
+		RTL_W16(C_PLUS_CMD, tp->cp_cmd);
+		RTL_R16(C_PLUS_CMD);
+	}
+	if (changed & NETIF_F_RXALL) {
+		int tmp = (RTL_R32(RX_CONFIG) & ~(ACCEPT_ERR | ACCEPT_RUNT));
+
+		if (features & NETIF_F_RXALL)
+			tmp |= (ACCEPT_ERR | ACCEPT_RUNT);
+		RTL_W32(RX_CONFIG, tmp);
+	}
+}
+
+static int rtl8169_set_features(struct net_device *dev,
+				netdev_features_t features)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	rtl_lock_work(tp);
+	__rtl8169_set_features(dev, features);
+	rtl_unlock_work(tp);
+
+	return 0;
+}
+
+static inline u32 rtl8169_tx_vlan_tag(struct sk_buff *skb)
+{
+	return (skb_vlan_tag_present(skb)) ?
+		TX_VLAN_TAG | swab16(skb_vlan_tag_get(skb)) : 0x00;
+}
+
+static void rtl8169_rx_vlan_tag(struct rx_desc *desc, struct sk_buff *skb)
+{
+	u32 opts2 = le32_to_cpu(desc->opts2);
+
+	if (opts2 & RX_VLAN_TAG)
+		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
+				       swab16(opts2 & 0xffff));
+}
+
+static void rtl8169_get_regs(struct net_device *dev, struct ethtool_regs *regs,
+			     void *p)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+	u32 __iomem *data = tp->mmio_addr;
+	u32 *dw = p;
+	int i;
+
+	rtl_lock_work(tp);
+	for (i = 0; i < R8169_REGS_SIZE; i += 4)
+		memcpy_fromio(dw++, data++, 4);
+	rtl_unlock_work(tp);
+}
+
+static u32 rtl8169_get_msglevel(struct net_device *dev)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	return tp->msg_enable;
+}
+
+static void rtl8169_set_msglevel(struct net_device *dev, u32 value)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	tp->msg_enable = value;
+}
+
+static const char rtl8169_gstrings[][ETH_GSTRING_LEN] = {
+	"tx_packets",
+	"rx_packets",
+	"tx_errors",
+	"rx_errors",
+	"rx_missed",
+	"align_errors",
+	"tx_single_collisions",
+	"tx_multi_collisions",
+	"unicast",
+	"broadcast",
+	"multicast",
+	"tx_aborted",
+	"tx_underrun",
+};
+
+static int rtl8169_get_sset_count(struct net_device *dev, int sset)
+{
+	switch (sset) {
+	case ETH_SS_STATS:
+		return ARRAY_SIZE(rtl8169_gstrings);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+DECLARE_RTL_COND(rtl_counters_cond)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	return RTL_R32(COUNTER_ADDR_LOW) & COUNTER_DUMP;
+}
+
+static void rtl8169_update_counters(struct net_device *dev)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+	void __iomem *ioaddr = tp->mmio_addr;
+	struct device *d = &tp->pdev->dev;
+	struct rtl8169_counters *counters;
+	dma_addr_t paddr;
+	u32 cmd;
+	int node = dev->dev.parent ? dev_to_node(dev->dev.parent) : -1;
+
+	/* Some chips are unable to dump tally counters when the receiver
+	 * is disabled.
+	 */
+	if ((RTL_R8(CHIP_CMD) & CMD_RX_ENB) == 0)
+		return;
+
+	if (tp->acp_enable) {
+		counters = kzalloc_node(sizeof(*counters), GFP_KERNEL, node);
+		paddr = virt_to_phys(counters);
+	} else {
+		counters = dma_alloc_coherent(d, sizeof(*counters), &paddr,
+					      GFP_KERNEL);
+	}
+	if (!counters)
+		return;
+
+	RTL_W32(COUNTER_ADDR_HIGH, (u64)paddr >> 32);
+	cmd = (u64)paddr & DMA_BIT_MASK(32);
+	RTL_W32(COUNTER_ADDR_LOW, cmd);
+	RTL_W32(COUNTER_ADDR_LOW, cmd | COUNTER_DUMP);
+
+	if (rtl_udelay_loop_wait_low(tp, &rtl_counters_cond, 10, 1000))
+		memcpy(&tp->counters, counters, sizeof(*counters));
+
+	RTL_W32(COUNTER_ADDR_LOW, 0);
+	RTL_W32(COUNTER_ADDR_HIGH, 0);
+
+	if (tp->acp_enable)
+		kfree(counters);
+	else
+		dma_free_coherent(d, sizeof(*counters), counters, paddr);
+}
+
+static void rtl8169_get_ethtool_stats(struct net_device *dev,
+				      struct ethtool_stats *stats, u64 *data)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	ASSERT_RTNL();
+
+	rtl8169_update_counters(dev);
+
+	data[0] = le64_to_cpu(tp->counters.tx_packets);
+	data[1] = le64_to_cpu(tp->counters.rx_packets);
+	data[2] = le64_to_cpu(tp->counters.tx_errors);
+	data[3] = le32_to_cpu(tp->counters.rx_errors);
+	data[4] = le16_to_cpu(tp->counters.rx_missed);
+	data[5] = le16_to_cpu(tp->counters.align_errors);
+	data[6] = le32_to_cpu(tp->counters.tx_one_collision);
+	data[7] = le32_to_cpu(tp->counters.tx_multi_collision);
+	data[8] = le64_to_cpu(tp->counters.rx_unicast);
+	data[9] = le64_to_cpu(tp->counters.rx_broadcast);
+	data[10] = le32_to_cpu(tp->counters.rx_multicast);
+	data[11] = le16_to_cpu(tp->counters.tx_aborted);
+	data[12] = le16_to_cpu(tp->counters.tx_underrun);
+}
+
+static void rtl8169_get_strings(struct net_device *dev, u32 stringset, u8 *data)
+{
+	switch (stringset) {
+	case ETH_SS_STATS:
+		memcpy(data, rtl8169_gstrings, sizeof(rtl8169_gstrings));
+		break;
+	}
+}
+
+static const struct ethtool_ops rtl8169_ethtool_ops = {
+	.get_drvinfo		= rtl8169_get_drvinfo,
+	.get_regs_len		= rtl8169_get_regs_len,
+	.get_link		= ethtool_op_get_link,
+	.get_msglevel		= rtl8169_get_msglevel,
+	.set_msglevel		= rtl8169_set_msglevel,
+	.get_regs		= rtl8169_get_regs,
+	.get_wol		= rtl8169_get_wol,
+	.set_wol		= rtl8169_set_wol,
+	.get_strings		= rtl8169_get_strings,
+	.get_sset_count		= rtl8169_get_sset_count,
+	.get_ethtool_stats	= rtl8169_get_ethtool_stats,
+	.get_ts_info		= ethtool_op_get_ts_info,
+};
+
+struct phy_reg {
+	u16 reg;
+	u16 val;
+};
+
+static __maybe_unused void rtl_writephy_batch(struct rtl8169_private *tp,
+					      const struct phy_reg *regs,
+					      int len)
+{
+	while (len-- > 0) {
+		rtl_writephy(tp, regs->reg, regs->val);
+		regs++;
+	}
+}
+
+static __maybe_unused void rtl_rar_exgmac_set(struct rtl8169_private *tp,
+					      u8 *addr)
+{
+	const u16 w[] = {
+		addr[0] | (addr[1] << 8),
+		addr[2] | (addr[3] << 8),
+		addr[4] | (addr[5] << 8)
+	};
+	const struct exgmac_reg e[] = {
+		{.addr = 0xe0, ERIAR_MASK_1111, .val = w[0] | (w[1] << 16)},
+		{.addr = 0xe4, ERIAR_MASK_1111, .val = w[2]},
+		{.addr = 0xf0, ERIAR_MASK_1111, .val = w[0] << 16},
+		{.addr = 0xf4, ERIAR_MASK_1111, .val = w[1] | (w[2] << 16)}
+	};
+
+	rtl_write_exgmac_batch(tp, e, ARRAY_SIZE(e));
+}
+
+static void rtl8168g_2_hw_mac_mcu_patch(struct rtl8169_private *tp)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	tp->chip->mdio_lock(tp);
+	/* power down PHY */
+	rtl_writephy(tp, 0x1f, 0x0000);
+	rtl_patchphy(tp, MII_BMCR, BMCR_PDOWN);
+	tp->chip->mdio_unlock(tp);
+
+	tp->chip->mac_mcu_patch(tp);
+
+	/* set dis_mcu_clroob to avoid WOL fail when ALDPS mode is enabled */
+	RTL_W8(MCU, RTL_R8(MCU) | DIS_MCU_CLROOB);
+}
+
+static void rtl8168g_2_hw_phy_config(struct rtl8169_private *tp)
+{
+	tp->chip->hw_phy_config(tp);
+}
+
+static void rtl_hw_phy_config(struct net_device *dev)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	rtl8168g_2_hw_mac_mcu_patch(tp);
+	rtl8168g_2_hw_phy_config(tp);
+}
+
+static void rtl_schedule_task(struct rtl8169_private *tp, enum rtl_flag flag)
+{
+	if (!test_and_set_bit(flag, tp->wk.flags))
+		schedule_work(&tp->wk.work);
+}
+
+static void rtl_rar_set(struct rtl8169_private *tp, u8 *addr)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	rtl_lock_work(tp);
+
+	RTL_W8(CFG9346, CFG9346_UNLOCK);
+
+	RTL_W32(MAC4, addr[4] | addr[5] << 8);
+	RTL_R32(MAC4);
+
+	RTL_W32(MAC0, addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24);
+	RTL_R32(MAC0);
+
+	RTL_W8(CFG9346, CFG9346_LOCK);
+
+	rtl_unlock_work(tp);
+}
+
+static void rtl_phy_reinit(struct rtl8169_private *tp)
+{
+	u32 tmp;
+
+	tp->chip->mdio_lock(tp);
+
+	/* fill fuse_rdy & rg_ext_ini_done */
+	r8169_mdio_write(tp, 31, 0x0a46);
+	r8169_mdio_write(tp, 20,
+			 (r8169_mdio_read(tp, 20) | (BIT(1) | BIT(0))));
+
+	/* init_autoload_done = 1 */
+	tmp = rtl_ocp_read(tp, 0xe004);
+	tmp |= BIT(7);
+	rtl_ocp_write(tp, 0xe004, tmp);
+
+	/* wait LAN-ON */
+	r8169_mdio_write(tp, 31, 0x0a42);
+	tmp = 0;
+	do {
+		tmp += 1;
+		mdelay(1);
+		if (tmp >= 2000) {
+			pr_err("PHY status is not 0x3, current = 0x%02x\n",
+			       (r8169_mdio_read(tp, 16) & 0x07));
+			break;
+		}
+	} while (0x3 != (r8169_mdio_read(tp, 16) & 0x07));
+	pr_info("wait %d ms for PHY ready, current = 0x%x\n",
+		tmp, r8169_mdio_read(tp, 16));
+
+	tp->chip->mdio_unlock(tp);
+
+	tp->chip->phy_reset_enable(tp);
+}
+
+static void rtl_phy_work(struct rtl8169_private *tp)
+{
+	rtl_phy_reinit(tp);
+	atomic_dec(&tp->phy_reinit_flag);
+}
+
+static void rtl8169_release_board(struct platform_device *pdev,
+				  struct net_device *dev, void __iomem *ioaddr)
+{
+	free_netdev(dev);
+}
+
+DECLARE_RTL_COND(rtl_phy_reset_cond)
+{
+	return tp->chip->phy_reset_pending(tp);
+}
+
+static void rtl8169_phy_reset(struct net_device *dev,
+			      struct rtl8169_private *tp)
+{
+	tp->chip->phy_reset_enable(tp);
+	rtl_msleep_loop_wait_low(tp, &rtl_phy_reset_cond, 1, 100);
+}
+
+static void rtl8169_init_phy(struct net_device *dev, struct rtl8169_private *tp)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	rtl_hw_phy_config(dev);
+
+	/* disable now_is_oob */
+	RTL_W8(MCU, RTL_R8(MCU) & ~NOW_IS_OOB);
+
+	rtl8169_phy_reset(dev, tp);
+
+	rtl8169_set_speed(dev, AUTONEG_ENABLE, SPEED_1000, DUPLEX_FULL,
+			  ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
+			  ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full |
+			  (tp->mii.supports_gmii ?
+			   ADVERTISED_1000baseT_Half |
+			   ADVERTISED_1000baseT_Full : 0));
+}
+
+static int rtl_set_mac_address(struct net_device *dev, void *p)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+	struct sockaddr *addr = p;
+
+	if (!is_valid_ether_addr(addr->sa_data))
+		return -EADDRNOTAVAIL;
+
+	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+
+	rtl_rar_set(tp, dev->dev_addr);
+
+	return 0;
+}
+
+static int rtl8169_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+	struct mii_ioctl_data *data = if_mii(ifr);
+
+	return netif_running(dev) ? tp->chip->do_ioctl(tp, data, cmd) : -ENODEV;
+}
+
+static int rtl_xmii_ioctl(struct rtl8169_private *tp,
+			  struct mii_ioctl_data *data, int cmd)
+{
+	switch (cmd) {
+	case SIOCGMIIPHY:
+		data->phy_id = 32;	/* Internal PHY */
+		return 0;
+
+	case SIOCGMIIREG:
+		tp->chip->mdio_lock(tp);
+		data->val_out = rtl_readphy(tp, data->reg_num & 0x1f);
+		tp->chip->mdio_unlock(tp);
+		return 0;
+
+	case SIOCSMIIREG:
+		tp->chip->mdio_lock(tp);
+		rtl_writephy(tp, data->reg_num & 0x1f, data->val_in);
+		tp->chip->mdio_unlock(tp);
+		return 0;
+	}
+	return -EOPNOTSUPP;
+}
+
+static void rtl_write_wakeup_pattern(struct rtl8169_private *tp, u32 idx)
+{
+	u8 i;
+	u8 j;
+	u32 reg_mask;
+	u32 reg_shift;
+	u32 reg_offset;
+
+	reg_offset = idx & ~(BIT(0) | BIT(1));
+	reg_shift = (idx % 4) * 8;
+	switch (reg_shift) {
+	case 0:
+		reg_mask = ERIAR_MASK_0001;
+		break;
+	case 8:
+		reg_mask = ERIAR_MASK_0010;
+		break;
+	case 16:
+		reg_mask = ERIAR_MASK_0100;
+		break;
+	case 24:
+		reg_mask = ERIAR_MASK_1000;
+		break;
+	default:
+		pr_err("Invalid shift bit 0x%x, idx = %d\n", reg_shift, idx);
+		return;
+	}
+
+	for (i = 0, j = 0; i < 0x80; i += 8, j++) {
+		rtl_eri_write(tp, i + reg_offset, reg_mask,
+			      tp->wol_mask[idx][j] << reg_shift, ERIAR_EXGMAC);
+	}
+
+	reg_offset = idx * 2;
+	if (idx % 2) {
+		reg_mask = ERIAR_MASK_1100;
+		reg_offset -= 2;
+		reg_shift = 16;
+	} else {
+		reg_mask = ERIAR_MASK_0011;
+		reg_shift = 0;
+	}
+	rtl_eri_write(tp, (int)(0x80 + reg_offset), reg_mask,
+		      tp->wol_crc[idx] << reg_shift, ERIAR_EXGMAC);
+}
+
+static void rtl_mdns_crc_wakeup(struct rtl8169_private *tp)
+{
+	int i;
+
+	for (i = 0; i < tp->wol_crc_cnt; i++)
+		rtl_write_wakeup_pattern(tp, i);
+}
+
+static void rtl_clear_wakeup_pattern(struct rtl8169_private *tp)
+{
+	u8 i;
+
+	for (i = 0; i < 0x80; i += 4)
+		rtl_eri_write(tp, i, ERIAR_MASK_1111, 0x0, ERIAR_EXGMAC);
+
+	for (i = 0x80; i < 0x90; i += 4)
+		rtl_eri_write(tp, i, ERIAR_MASK_1111, 0x0, ERIAR_EXGMAC);
+}
+
+static void rtl_crc_wakeup(struct rtl8169_private *tp)
+{
+	rtl_clear_wakeup_pattern(tp);
+	if (tp->wol_crc_cnt > 0 && (tp->wol_enable & WOL_CRC_MATCH))
+		rtl_mdns_crc_wakeup(tp);
+}
+
+static void rtl_wpd_set(struct rtl8169_private *tp)
+{
+	u32 tmp;
+	u32 i;
+
+	/* clear WPD registers */
+	rtl_ocp_write(tp, 0xD23A, 0);
+	rtl_ocp_write(tp, 0xD23C, 0);
+	rtl_ocp_write(tp, 0xD23E, 0);
+	for (i = 0; i < 128; i += 2)
+		rtl_ocp_write(tp, 0xD240 + i, 0);
+
+	tmp = rtl_ocp_read(tp, 0xC0C2);
+	tmp &= ~BIT(4);
+	rtl_ocp_write(tp, 0xC0C2, tmp);
+
+	/* enable WPD */
+	tmp = rtl_ocp_read(tp, 0xC0C2);
+	tmp |= BIT(0);
+	rtl_ocp_write(tp, 0xC0C2, tmp);
+}
+
+static void rtl_speed_down(struct rtl8169_private *tp)
+{
+	u32 adv;
+	int lpa;
+
+	tp->chip->mdio_lock(tp);
+	rtl_writephy(tp, 0x1f, 0x0000);
+	lpa = rtl_readphy(tp, MII_LPA);
+	tp->chip->mdio_unlock(tp);
+
+	if (lpa & (LPA_10HALF | LPA_10FULL))
+		adv = ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full;
+	else if (lpa & (LPA_100HALF | LPA_100FULL))
+		adv = ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
+			ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full;
+	else
+		adv = ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
+			ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full |
+			(tp->mii.supports_gmii ?
+			ADVERTISED_1000baseT_Half |
+			ADVERTISED_1000baseT_Full : 0);
+
+	rtl8169_set_speed(tp->dev, AUTONEG_ENABLE, SPEED_1000, DUPLEX_FULL,
+			  adv);
+}
+
+static void rtl_wol_suspend_quirk(struct rtl8169_private *tp)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	RTL_W32(RX_CONFIG, RTL_R32(RX_CONFIG) |
+		ACCEPT_BROADCAST | ACCEPT_MULTICAST | ACCEPT_MY_PHYS);
+}
+
+static bool rtl_wol_pll_power_down(struct rtl8169_private *tp)
+{
+	if (!(__rtl8169_get_wol(tp) & WAKE_ANY))
+		return false;
+
+	rtl_speed_down(tp);
+	rtl_wol_suspend_quirk(tp);
+
+	return true;
+}
+
+static void r8168_phy_power_up(struct rtl8169_private *tp)
+{
+	rtl_writephy(tp, 0x1f, 0x0000);
+	rtl_writephy(tp, MII_BMCR, BMCR_ANENABLE);
+}
+
+static void r8168_phy_power_down(struct rtl8169_private *tp)
+{
+	rtl_writephy(tp, 0x1f, 0x0000);
+	rtl_writephy(tp, MII_BMCR, BMCR_PDOWN);
+}
+
+static void r8168_pll_power_down(struct rtl8169_private *tp)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	RTL_W8(CFG9346, CFG9346_UNLOCK);
+	if (tp->wol_enable & WOL_MAGIC) {
+		/* enable now_is_oob */
+		RTL_W8(MCU, RTL_R8(MCU) | NOW_IS_OOB);
+		RTL_W8(CONFIG5, RTL_R8(CONFIG5) | LAN_WAKE);
+		RTL_W8(CONFIG3, RTL_R8(CONFIG3) | MAGIC_PKT);
+
+		tp->chip->crc_wakeup(tp);
+		if (tp->wol_enable & WOL_WPD)
+			rtl_wpd_set(tp);
+	} else {
+		RTL_W8(CONFIG5, RTL_R8(CONFIG5) & ~LAN_WAKE);
+		RTL_W8(CONFIG3, RTL_R8(CONFIG3) & ~MAGIC_PKT);
+	}
+	RTL_W8(CFG9346, CFG9346_LOCK);
+
+	if (rtl_wol_pll_power_down(tp))
+		return;
+
+	r8168_phy_power_down(tp);
+}
+
+static void r8168_pll_power_up(struct rtl8169_private *tp)
+{
+	r8168_phy_power_up(tp);
+}
+
+static inline void rtl_generic_op(struct rtl8169_private *tp,
+				  void (*op)(struct rtl8169_private *))
+{
+	if (op)
+		op(tp);
+}
+
+static inline void rtl_pll_power_down(struct rtl8169_private *tp)
+{
+	rtl_generic_op(tp, tp->chip->pll_power_down);
+}
+
+static inline void rtl_pll_power_up(struct rtl8169_private *tp)
+{
+	rtl_generic_op(tp, tp->chip->pll_power_up);
+}
+
+static void rtl_init_rxcfg(struct rtl8169_private *tp)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	/* disable RX128_INT_EN to reduce CPU loading */
+	RTL_W32(RX_CONFIG, RX_DMA_BURST | RX_EARLY_OFF);
+}
+
+static void rtl8169_init_ring_indexes(struct rtl8169_private *tp)
+{
+	tp->dirty_tx = 0;
+	tp->cur_tx = 0;
+	tp->cur_rx = 0;
+
+#if defined(CONFIG_RTL_RX_NO_COPY)
+	tp->dirty_rx = 0;
+#endif /* CONFIG_RTL_RX_NO_COPY */
+}
+
+DECLARE_RTL_COND(rtl_chipcmd_cond)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	return RTL_R8(CHIP_CMD) & CMD_RESET;
+}
+
+static void rtl_hw_reset(struct rtl8169_private *tp)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	RTL_W8(CHIP_CMD, CMD_RESET);
+
+	rtl_udelay_loop_wait_low(tp, &rtl_chipcmd_cond, 100, 100);
+}
+
+static void rtl_rx_close(struct rtl8169_private *tp)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	RTL_W32(RX_CONFIG, RTL_R32(RX_CONFIG) & ~RX_CONFIG_ACCEPT_MASK);
+}
+
+DECLARE_RTL_COND(rtl_txcfg_empty_cond)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	return RTL_R32(TX_CONFIG) & TXCFG_EMPTY;
+}
+
+static void rtl8169_hw_reset(struct rtl8169_private *tp)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	/* Disable interrupts */
+	rtl8169_irq_mask_and_ack(tp);
+
+	rtl_rx_close(tp);
+
+	RTL_W8(CHIP_CMD, RTL_R8(CHIP_CMD) | STOP_REQ);
+	rtl_udelay_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 666);
+
+	rtl_hw_reset(tp);
+
+	if (tp->chip->features & RTL_FEATURE_TX_NO_CLOSE) {
+		/* disable tx no close mode */
+		rtl_ocp_write(tp, 0xE610,
+			      rtl_ocp_read(tp, 0xE610) & ~(BIT(4) | BIT(6)));
+	};
+}
+
+static void rtl_set_rx_tx_config_registers(struct rtl8169_private *tp)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	/* Set DMA burst size and Interframe Gap Time */
+	RTL_W32(TX_CONFIG, (TX_DMA_BURST << TX_DMA_SHIFT) |
+		(INTER_FRAME_GAP << TX_INTER_FRAME_GAP_SHIFT));
+}
+
+static void rtl_hw_start(struct net_device *dev)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	tp->chip->hw_start(dev);
+
+	rtl_irq_enable_all(tp);
+}
+
+static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp,
+					 void __iomem *ioaddr)
+{
+	/* Magic spell: some iop3xx ARM board needs the TxDescAddrHigh
+	 * register to be written before TxDescAddrLow to work.
+	 * Switching from MMIO to I/O access fixes the issue as well.
+	 */
+	RTL_W32(TX_DESC_START_ADDR_HIGH, ((u64)tp->tx_phy_addr) >> 32);
+	RTL_W32(TX_DESC_START_ADDR_LOW,
+		((u64)tp->tx_phy_addr) & DMA_BIT_MASK(32));
+	RTL_W32(RX_DESC_ADDR_HIGH, ((u64)tp->rx_phy_addr) >> 32);
+	RTL_W32(RX_DESC_ADDR_LOW, ((u64)tp->rx_phy_addr) & DMA_BIT_MASK(32));
+}
+
+static void rtl_set_rx_max_size(void __iomem *ioaddr, unsigned int rx_buf_sz)
+{
+	/* Low hurts. Let's disable the filtering. */
+	RTL_W16(RX_MAX_SIZE, rx_buf_sz);
+}
+
+static void rtl_set_rx_mode(struct net_device *dev)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+	void __iomem *ioaddr = tp->mmio_addr;
+	u32 mc_filter[2];	/* Multicast hash filter */
+	int rx_mode;
+	u32 tmp = 0;
+	u32 data;
+
+	if (dev->flags & IFF_PROMISC) {
+		/* Unconditionally log net taps. */
+		netif_notice(tp, link, dev, "Promiscuous mode enabled\n");
+		rx_mode =
+			ACCEPT_BROADCAST | ACCEPT_MULTICAST | ACCEPT_MY_PHYS |
+			ACCEPT_ALL_PHYS;
+		mc_filter[1] = 0xffffffff;
+		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 = ACCEPT_BROADCAST | ACCEPT_MULTICAST | ACCEPT_MY_PHYS;
+		mc_filter[1] = 0xffffffff;
+		mc_filter[0] = 0xffffffff;
+	} else {
+		struct netdev_hw_addr *ha;
+
+		rx_mode = ACCEPT_BROADCAST | ACCEPT_MY_PHYS;
+		mc_filter[1] = 0;
+		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 |= ACCEPT_MULTICAST;
+		}
+	}
+
+	if (dev->features & NETIF_F_RXALL)
+		rx_mode |= (ACCEPT_ERR | ACCEPT_RUNT);
+
+	tmp = (RTL_R32(RX_CONFIG) & ~RX_CONFIG_ACCEPT_MASK) | rx_mode;
+
+	data = mc_filter[0];
+
+	mc_filter[0] = swab32(mc_filter[1]);
+	mc_filter[1] = swab32(data);
+
+	RTL_W32(MAR0 + 4, mc_filter[1]);
+	RTL_W32(MAR0 + 0, mc_filter[0]);
+
+	RTL_W32(RX_CONFIG, tmp);
+}
+
+static inline void rtl_csi_write(struct rtl8169_private *tp, int addr,
+				 int value)
+{
+	tp->chip->csi_write(tp, addr, value);
+}
+
+static inline u32 rtl_csi_read(struct rtl8169_private *tp, int addr)
+{
+	return tp->chip->csi_read(tp, addr);
+}
+
+static void rtl_csi_access_enable(struct rtl8169_private *tp, u32 bits)
+{
+	u32 csi;
+
+	csi = rtl_csi_read(tp, 0x070c) & 0x00ffffff;
+	rtl_csi_write(tp, 0x070c, csi | bits);
+}
+
+static void rtl_csi_access_enable_1(struct rtl8169_private *tp)
+{
+	rtl_csi_access_enable(tp, 0x17000000);
+}
+
+DECLARE_RTL_COND(rtl_csiar_cond)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	return RTL_R32(CSIAR) & CSIAR_FLAG;
+}
+
+static void r8169_csi_write(struct rtl8169_private *tp, int addr, int value)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	RTL_W32(CSIDR, value);
+	RTL_W32(CSIAR, CSIAR_WRITE_CMD | (addr & CSIAR_ADDR_MASK) |
+		CSIAR_BYTE_ENABLE << CSIAR_BYTE_ENABLE_SHIFT);
+
+	rtl_udelay_loop_wait_low(tp, &rtl_csiar_cond, 10, 100);
+}
+
+static u32 r8169_csi_read(struct rtl8169_private *tp, int addr)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	RTL_W32(CSIAR, (addr & CSIAR_ADDR_MASK) |
+		CSIAR_BYTE_ENABLE << CSIAR_BYTE_ENABLE_SHIFT);
+
+	return rtl_udelay_loop_wait_high(tp, &rtl_csiar_cond, 10, 100) ?
+		RTL_R32(CSIDR) : ~0;
+}
+
+static void rtl_led_set(struct rtl8169_private *tp)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	/* LED setting */
+	if (tp->led_cfg)
+		RTL_W32(LEDSEL, 0x00060000 | tp->led_cfg);
+	else
+		RTL_W32(LEDSEL, tp->chip->led_cfg);
+}
+
+static void rtl_hw_start_8168g_1(struct rtl8169_private *tp)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	RTL_W32(TX_CONFIG, RTL_R32(TX_CONFIG) | TXCFG_AUTO_FIFO);
+
+	rtl_csi_access_enable_1(tp);
+
+	rtl_w1w0_eri(tp, 0xdc, ERIAR_MASK_0001, 0x00, 0x01, ERIAR_EXGMAC);
+	rtl_w1w0_eri(tp, 0xdc, ERIAR_MASK_0001, 0x01, 0x00, ERIAR_EXGMAC);
+	rtl_eri_write(tp, 0x2f8, ERIAR_MASK_0011, 0x1d8f, ERIAR_EXGMAC);
+
+	RTL_W8(CHIP_CMD, CMD_TX_ENB | CMD_RX_ENB);
+	RTL_W32(MISC, RTL_R32(MISC) & ~RXDV_GATED_EN);
+	RTL_W8(MAX_TX_PACKET_SIZE, EARLY_SIZE);
+
+	rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000, ERIAR_EXGMAC);
+	rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000, ERIAR_EXGMAC);
+
+	/* Adjust EEE LED frequency */
+	RTL_W8(EEE_LED, RTL_R8(EEE_LED) & ~0x07);
+
+	rtl_w1w0_eri(tp, 0x2fc, ERIAR_MASK_0001, 0x01, 0x06, ERIAR_EXGMAC);
+	rtl_w1w0_eri(tp, 0x1b0, ERIAR_MASK_0011, 0x0000, 0x1000, ERIAR_EXGMAC);
+}
+
+static void rtl_hw_start_8168g_2(struct rtl8169_private *tp)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	rtl_hw_start_8168g_1(tp);
+
+	rtl_led_set(tp);
+
+	/* disable aspm and clock request before access ephy */
+	RTL_W8(CONFIG2, RTL_R8(CONFIG2) & ~CLK_REQ_EN);
+	RTL_W8(CONFIG5, RTL_R8(CONFIG5) & ~ASPM_EN);
+}
+
+static void rtl_hw_start_8168(struct net_device *dev)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+	void __iomem *ioaddr = tp->mmio_addr;
+	u32 tmp;
+	u32 phy_status;
+	const struct soc_device_attribute rtk_soc_rtd161x_a01[] = {
+		{
+			.family = "Realtek Thor",
+			.revision = "A01",
+		},
+		{
+		/* empty */
+		}
+	};
+	const struct soc_device_attribute rtk_soc_rtd131x[] = {
+		{
+			.family = "Realtek Hank",
+		},
+		{
+		/* empty */
+		}
+	};
+
+	RTL_W8(CFG9346, CFG9346_UNLOCK);
+
+	RTL_W8(MAX_TX_PACKET_SIZE, TX_PACKET_MAX);
+
+	rtl_set_rx_max_size(ioaddr, rx_buf_sz);
+
+#if defined(CONFIG_RTL_RX_NO_COPY)
+	tp->cp_cmd |= RTL_R16(C_PLUS_CMD) | PKT_CNTR_DISABLE | INTT_3;
+#else
+	tp->cp_cmd |= RTL_R16(C_PLUS_CMD) | PKT_CNTR_DISABLE | INTT_1;
+#endif /* CONFIG_RTL_RX_NO_COPY */
+
+	RTL_W16(C_PLUS_CMD, tp->cp_cmd);
+
+	RTL_W16(INTR_MITIGATE, 0x5151);
+
+	rtl_set_rx_tx_desc_registers(tp, ioaddr);
+
+	rtl_set_rx_tx_config_registers(tp);
+
+	if (tp->chip->features & RTL_FEATURE_TX_NO_CLOSE) {
+		/* enable tx no close mode */
+		rtl_ocp_write(tp, 0xE610,
+			      rtl_ocp_read(tp, 0xE610) | (BIT(4) | BIT(6)));
+	}
+
+	/* disable pause frame resend caused by nearfull for rtd161x A01 */
+	if (soc_device_match(rtk_soc_rtd161x_a01))
+		rtl_ocp_write(tp, 0xE862, rtl_ocp_read(tp, 0xE862) | BIT(0));
+
+	tp->event_slow &= ~RX_OVERFLOW;
+
+	if (tp->chip->features & RTL_FEATURE_ADJUST_FIFO) {
+		/* TX FIFO threshold */
+		rtl_ocp_write(tp, 0xE618, 0x0006);
+		rtl_ocp_write(tp, 0xE61A, 0x0010);
+
+		/* RX FIFO threshold */
+		rtl_ocp_write(tp, 0xC0A0, 0x0002);
+		rtl_ocp_write(tp, 0xC0A2, 0x0008);
+
+		phy_status = rtl_ocp_read(tp, 0xde40);
+		if ((phy_status & 0x0030) == 0x0020) {
+			/* 1000 Mbps */
+			rtl_ocp_write(tp, 0xC0A4, 0x0088);
+			rtl_ocp_write(tp, 0xC0A8, 0x00A8);
+		} else {
+			/* 10/100 Mbps */
+			rtl_ocp_write(tp, 0xC0A4, 0x0038);
+			rtl_ocp_write(tp, 0xC0A8, 0x0048);
+		}
+	} else {
+		/* TX FIFO threshold */
+		rtl_ocp_write(tp, 0xE618, 0x0006);
+		rtl_ocp_write(tp, 0xE61A, 0x0010);
+
+		/* RX FIFO threshold */
+		rtl_ocp_write(tp, 0xC0A0, 0x0002);
+		rtl_ocp_write(tp, 0xC0A2, 0x0008);
+		rtl_ocp_write(tp, 0xC0A4, 0x0038);
+		rtl_ocp_write(tp, 0xC0A8, 0x0048);
+	}
+
+	RTL_R8(INTR_MASK);
+
+	rtl_hw_start_8168g_2(tp);
+
+	RTL_W8(CFG9346, CFG9346_LOCK);
+
+	RTL_W8(CHIP_CMD, CMD_TX_ENB | CMD_RX_ENB);
+
+	if (soc_device_match(rtk_soc_rtd131x)) {
+		phy_status = rtl_ocp_read(tp, 0xde40);
+		switch (tp->output_mode) {
+		case OUTPUT_EMBEDDED_PHY:
+			/* do nothing */
+			break;
+		case OUTPUT_RMII:
+			/* adjust RMII interface setting, speed */
+			tmp = rtl_ocp_read(tp, 0xea30) & ~(BIT(6) | BIT(5));
+			switch (phy_status & 0x0030) { /* link speed */
+			case 0x0000:
+				/* 10M, RGMII clock speed = 2.5MHz */
+				break;
+			case 0x0010:
+				/* 100M, RGMII clock speed = 25MHz */
+				tmp |= BIT(5);
+			}
+			/* adjust RGMII interface setting, duplex */
+			if ((phy_status & BIT(3)) == 0)
+				/* ETN spec, half duplex */
+				tmp &= ~BIT(4);
+			else	/* ETN spec, full duplex */
+				tmp |= BIT(4);
+			rtl_ocp_write(tp, 0xea30, tmp);
+			break;
+		case OUTPUT_RGMII_TO_MAC:
+		case OUTPUT_RGMII_TO_PHY:
+			/* adjust RGMII interface setting, duplex */
+			tmp = rtl_ocp_read(tp, 0xea34) & ~(BIT(4) | BIT(3));
+			switch (phy_status & 0x0030) { /* link speed */
+			case 0x0000:
+				/* 10M, RGMII clock speed = 2.5MHz */
+				break;
+			case 0x0010:
+				/* 100M, RGMII clock speed = 25MHz */
+				tmp |= BIT(3);
+				break;
+			case 0x0020:
+				/* 1000M, RGMII clock speed = 125MHz */
+				tmp |= BIT(4);
+				break;
+			}
+			/* adjust RGMII interface setting, duplex */
+			if ((phy_status & BIT(3)) == 0)
+				/* ETN spec, half duplex */
+				tmp &= ~BIT(2);
+			else	/* ETN spec, full duplex */
+				tmp |= BIT(2);
+			rtl_ocp_write(tp, 0xea34, tmp);
+			break;
+		default:
+			pr_err("invalid output mode %d\n", tp->output_mode);
+			return;
+		}
+	}
+
+	rtl_set_rx_mode(dev);
+
+	RTL_W16(MULTI_INTR, RTL_R16(MULTI_INTR) & 0xF000);
+}
+
+static int rtl8169_change_mtu(struct net_device *dev, int new_mtu)
+{
+#if defined(CONFIG_RTL_RX_NO_COPY)
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	if (!netif_running(dev)) {
+		rx_buf_sz_new = (new_mtu > ETH_DATA_LEN) ?
+			new_mtu + ETH_HLEN + 8 + 1 : RX_BUF_SIZE;
+		rx_buf_sz = rx_buf_sz_new;
+		goto out;
+	}
+
+	rx_buf_sz_new = (new_mtu > ETH_DATA_LEN) ?
+		new_mtu + ETH_HLEN + 8 + 1 : RX_BUF_SIZE;
+
+	rtl_schedule_task(tp, RTL_FLAG_TASK_RESET_PENDING);
+
+out:
+#endif /* CONFIG_RTL_RX_NO_COPY */
+
+	dev->mtu = new_mtu;
+	netdev_update_features(dev);
+
+	return 0;
+}
+
+static inline void rtl8169_make_unusable_by_asic(struct rx_desc *desc)
+{
+	desc->addr = cpu_to_le64(0x0badbadbadbadbadull);
+	desc->opts1 &= ~cpu_to_le32(DESC_OWN | RSVD_MASK);
+}
+
+#if defined(CONFIG_RTL_RX_NO_COPY)
+static void rtl8169_free_rx_databuff(struct rtl8169_private *tp,
+				     struct sk_buff **data_buff,
+				     struct rx_desc *desc)
+{
+	if (!tp->acp_enable)
+		dma_unmap_single(&tp->pdev->dev, le64_to_cpu(desc->addr),
+				 rx_buf_sz, DMA_FROM_DEVICE);
+
+	dev_kfree_skb(*data_buff);
+	*data_buff = NULL;
+	rtl8169_make_unusable_by_asic(desc);
+}
+#else
+static void rtl8169_free_rx_databuff(struct rtl8169_private *tp,
+				     void **data_buff, struct rx_desc *desc)
+{
+	if (!tp->acp_enable)
+		dma_unmap_single(&tp->pdev->dev, le64_to_cpu(desc->addr),
+				 rx_buf_sz, DMA_FROM_DEVICE);
+
+	kfree(*data_buff);
+	*data_buff = NULL;
+	rtl8169_make_unusable_by_asic(desc);
+}
+#endif /* CONFIG_RTL_RX_NO_COPY */
+
+static inline void rtl8169_mark_to_asic(struct rx_desc *desc, u32 rx_buf_sz)
+{
+	u32 eor = le32_to_cpu(desc->opts1) & RING_END;
+
+	desc->opts1 = cpu_to_le32(DESC_OWN | eor | rx_buf_sz);
+}
+
+static inline void rtl8169_map_to_asic(struct rx_desc *desc, dma_addr_t mapping,
+				       u32 rx_buf_sz)
+{
+	desc->addr = cpu_to_le64(mapping);
+	wmb(); /* make sure this RX descriptor is ready */
+	rtl8169_mark_to_asic(desc, rx_buf_sz);
+}
+
+static inline void *rtl8169_align(void *data)
+{
+	return (void *)ALIGN((long)data, 16);
+}
+
+#if defined(CONFIG_RTL_RX_NO_COPY)
+static int
+rtl8168_alloc_rx_skb(struct rtl8169_private *tp, struct sk_buff **sk_buff,
+		     struct rx_desc *desc, int rx_buf_sz)
+{
+	struct device *d = &tp->pdev->dev;
+	struct sk_buff *skb;
+	dma_addr_t mapping;
+	int ret = 0;
+
+	skb = dev_alloc_skb(rx_buf_sz + RTK_RX_ALIGN);
+	if (!skb)
+		goto err_out;
+
+	skb_reserve(skb, RTK_RX_ALIGN);
+
+	if (tp->acp_enable) {
+		mapping = virt_to_phys(skb->data);
+	} else {
+		mapping = dma_map_single(d, skb->data, rx_buf_sz,
+					 DMA_FROM_DEVICE);
+
+		if (unlikely(dma_mapping_error(d, mapping))) {
+			if (unlikely(net_ratelimit()))
+				netif_err(tp, drv, tp->dev,
+					  "Failed to map RX DMA!\n");
+			goto err_out;
+		}
+	}
+	*sk_buff = skb;
+	rtl8169_map_to_asic(desc, mapping, rx_buf_sz);
+
+out:
+	return ret;
+
+err_out:
+	if (skb)
+		dev_kfree_skb(skb);
+	ret = -ENOMEM;
+	rtl8169_make_unusable_by_asic(desc);
+	goto out;
+}
+#else
+static struct sk_buff *rtl8169_alloc_rx_data(struct rtl8169_private *tp,
+					     struct rx_desc *desc)
+{
+	void *data;
+	dma_addr_t mapping;
+	struct device *d = &tp->pdev->dev;
+	struct net_device *dev = tp->dev;
+	int node = dev->dev.parent ? dev_to_node(dev->dev.parent) : -1;
+
+	data = kmalloc_node(rx_buf_sz, GFP_KERNEL, node);
+	if (!data)
+		return NULL;
+
+	if (rtl8169_align(data) != data) {
+		kfree(data);
+		data = kmalloc_node(rx_buf_sz + 15, GFP_KERNEL, node);
+		if (!data)
+			return NULL;
+	}
+
+	if (tp->acp_enable) {
+		mapping = virt_to_phys(rtl8169_align(data));
+	} else {
+		mapping = dma_map_single(d, rtl8169_align(data), rx_buf_sz,
+					 DMA_FROM_DEVICE);
+		if (unlikely(dma_mapping_error(d, mapping))) {
+			if (net_ratelimit())
+				netif_err(tp, drv, tp->dev,
+					  "Failed to map RX DMA!\n");
+			goto err_out;
+		}
+	}
+
+	rtl8169_map_to_asic(desc, mapping, rx_buf_sz);
+	return data;
+
+err_out:
+	kfree(data);
+	return NULL;
+}
+#endif /* CONFIG_RTL_RX_NO_COPY */
+
+static void rtl8169_rx_clear(struct rtl8169_private *tp)
+{
+	unsigned int i;
+
+	for (i = 0; i < NUM_RX_DESC; i++) {
+		if (tp->rx_databuff[i]) {
+			rtl8169_free_rx_databuff(tp, tp->rx_databuff + i,
+						 tp->rx_desc_array + i);
+		}
+	}
+}
+
+static inline void rtl8169_mark_as_last_descriptor(struct rx_desc *desc)
+{
+	desc->opts1 |= cpu_to_le32(RING_END);
+}
+
+#if defined(CONFIG_RTL_RX_NO_COPY)
+static u32
+rtl8168_rx_fill(struct rtl8169_private *tp,
+		struct net_device *dev, u32 start, u32 end)
+{
+	u32 cur;
+
+	for (cur = start; end - cur > 0; cur++) {
+		int ret, i = cur % NUM_RX_DESC;
+
+		if (tp->rx_databuff[i])
+			continue;
+		ret = rtl8168_alloc_rx_skb(tp, tp->rx_databuff + i,
+					   tp->rx_desc_array + i, rx_buf_sz);
+		if (ret < 0)
+			break;
+		if (i == (NUM_RX_DESC - 1))
+			rtl8169_mark_as_last_descriptor(tp->rx_desc_array +
+							NUM_RX_DESC - 1);
+	}
+	return cur - start;
+}
+#else
+static int rtl8169_rx_fill(struct rtl8169_private *tp)
+{
+	unsigned int i;
+
+	for (i = 0; i < NUM_RX_DESC; i++) {
+		void *data;
+
+		if (tp->rx_databuff[i])
+			continue;
+
+		data = rtl8169_alloc_rx_data(tp, tp->rx_desc_array + i);
+		if (!data) {
+			rtl8169_make_unusable_by_asic(tp->rx_desc_array + i);
+			goto err_out;
+		}
+		tp->rx_databuff[i] = data;
+	}
+
+	rtl8169_mark_as_last_descriptor(tp->rx_desc_array + NUM_RX_DESC - 1);
+	return 0;
+
+err_out:
+	rtl8169_rx_clear(tp);
+	return -ENOMEM;
+}
+#endif /* CONFIG_RTL_RX_NO_COPY */
+
+static int rtl8169_init_ring(struct net_device *dev)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+	int ret;
+
+	rtl8169_init_ring_indexes(tp);
+
+	memset(tp->tx_skb, 0x0, NUM_TX_DESC * sizeof(struct ring_info));
+	memset(tp->rx_databuff, 0x0, NUM_RX_DESC * sizeof(void *));
+
+#if defined(CONFIG_RTL_RX_NO_COPY)
+	ret = rtl8168_rx_fill(tp, dev, 0, NUM_RX_DESC);
+	if (ret < NUM_RX_DESC)
+		ret = -ENOMEM;
+	else
+		ret = 0;
+#else
+	ret = rtl8169_rx_fill(tp);
+#endif /* CONFIG_RTL_RX_NO_COPY */
+
+	return ret;
+}
+
+static void rtl8169_unmap_tx_skb(struct rtl8169_private *tp, struct device *d,
+				 struct ring_info *tx_skb, struct tx_desc *desc)
+{
+	unsigned int len = tx_skb->len;
+
+	if (!tp->acp_enable)
+		dma_unmap_single(d, le64_to_cpu(desc->addr), len,
+				 DMA_TO_DEVICE);
+
+	desc->opts1 = 0x00;
+	desc->opts2 = 0x00;
+	desc->addr = 0x00;
+	tx_skb->len = 0;
+}
+
+static void rtl8169_tx_clear_range(struct rtl8169_private *tp, u32 start,
+				   unsigned int n)
+{
+	unsigned int i;
+
+	for (i = 0; i < n; i++) {
+		unsigned int entry = (start + i) % NUM_TX_DESC;
+		struct ring_info *tx_skb = tp->tx_skb + entry;
+		unsigned int len = tx_skb->len;
+
+		if (len) {
+			struct sk_buff *skb = tx_skb->skb;
+
+			rtl8169_unmap_tx_skb(tp, &tp->pdev->dev, tx_skb,
+					     tp->tx_desc_array + entry);
+			if (skb) {
+				tp->dev->stats.tx_dropped++;
+				dev_kfree_skb(skb);
+				tx_skb->skb = NULL;
+			}
+		}
+	}
+}
+
+static void rtl8169_tx_clear(struct rtl8169_private *tp)
+{
+	rtl8169_tx_clear_range(tp, tp->dirty_tx, NUM_TX_DESC);
+	tp->cur_tx = 0;
+	tp->dirty_tx = 0;
+}
+
+static void rtl_reset_work(struct rtl8169_private *tp)
+{
+	struct net_device *dev = tp->dev;
+	int i;
+
+	napi_disable(&tp->napi);
+	netif_stop_queue(dev);
+	synchronize_rcu();
+
+	rtl8169_hw_reset(tp);
+
+#if defined(CONFIG_RTL_RX_NO_COPY)
+	rtl8169_rx_clear(tp);
+	rtl8169_tx_clear(tp);
+
+	if (rx_buf_sz_new != rx_buf_sz)
+		rx_buf_sz = rx_buf_sz_new;
+
+	memset(tp->tx_desc_array, 0x0, NUM_TX_DESC * sizeof(struct tx_desc));
+	for (i = 0; i < NUM_TX_DESC; i++) {
+		if (i == (NUM_TX_DESC - 1))
+			tp->tx_desc_array[i].opts1 = cpu_to_le32(RING_END);
+	}
+	memset(tp->rx_desc_array, 0x0, NUM_RX_DESC * sizeof(struct rx_desc));
+
+	if (rtl8169_init_ring(dev) < 0) {
+		napi_enable(&tp->napi);
+		netif_wake_queue(dev);
+		netif_warn(tp, drv, dev, "No memory. Try to restart......\n");
+		msleep(1000);
+		rtl_schedule_task(tp, RTL_FLAG_TASK_RESET_PENDING);
+		return;
+	}
+#else
+	for (i = 0; i < NUM_RX_DESC; i++)
+		rtl8169_mark_to_asic(tp->rx_desc_array + i, rx_buf_sz);
+
+	rtl8169_tx_clear(tp);
+	rtl8169_init_ring_indexes(tp);
+#endif /* CONFIG_RTL_RX_NO_COPY */
+
+	napi_enable(&tp->napi);
+	rtl_hw_start(dev);
+	netif_wake_queue(dev);
+	rtl8169_check_link_status(dev, tp, tp->mmio_addr);
+}
+
+static void rtl8169_tx_timeout(struct net_device *dev, unsigned int txqueue)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	rtl_schedule_task(tp, RTL_FLAG_TASK_RESET_PENDING);
+}
+
+static int rtl8169_xmit_frags(struct rtl8169_private *tp, struct sk_buff *skb,
+			      u32 *opts)
+{
+	struct skb_shared_info *info = skb_shinfo(skb);
+	unsigned int cur_frag, entry;
+	struct tx_desc *txd = NULL;
+	struct device *d = &tp->pdev->dev;
+
+	entry = tp->cur_tx;
+	for (cur_frag = 0; cur_frag < info->nr_frags; cur_frag++) {
+		const skb_frag_t *frag = info->frags + cur_frag;
+		dma_addr_t mapping;
+		u32 status, len;
+		void *addr;
+
+		entry = (entry + 1) % NUM_TX_DESC;
+
+		txd = tp->tx_desc_array + entry;
+		len = skb_frag_size(frag);
+		addr = skb_frag_address(frag);
+		if (tp->acp_enable) {
+			mapping = virt_to_phys(addr);
+		} else {
+			mapping = dma_map_single(d, addr, len, DMA_TO_DEVICE);
+			if (unlikely(dma_mapping_error(d, mapping))) {
+				if (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 = opts[0] | len |
+			(RING_END * !((entry + 1) % NUM_TX_DESC));
+
+		txd->opts1 = cpu_to_le32(status);
+		txd->opts2 = cpu_to_le32(opts[1]);
+		txd->addr = cpu_to_le64(mapping);
+
+		tp->tx_skb[entry].len = len;
+	}
+
+	if (cur_frag) {
+		tp->tx_skb[entry].skb = skb;
+		txd->opts1 |= cpu_to_le32(LAST_FRAG);
+	}
+
+	return cur_frag;
+
+err_out:
+	rtl8169_tx_clear_range(tp, tp->cur_tx + 1, cur_frag);
+	return -EIO;
+}
+
+static inline bool rtl8169_tso_csum(struct rtl8169_private *tp,
+				    struct sk_buff *skb, u32 *opts)
+{
+	const struct rtl_tx_desc_info *info = &tx_desc_info;
+	u32 mss = skb_shinfo(skb)->gso_size;
+	int offset = info->opts_offset;
+
+	if (mss) {
+		opts[0] |= TD_LSO;
+		opts[offset] |= min(mss, TD_MSS_MAX) << info->mss_shift;
+	} else if (skb->ip_summed == CHECKSUM_PARTIAL) {
+		const struct iphdr *ip = ip_hdr(skb);
+
+		if (ip->protocol == IPPROTO_TCP)
+			opts[offset] |= info->checksum.tcp;
+		else if (ip->protocol == IPPROTO_UDP)
+			opts[offset] |= info->checksum.udp;
+		else
+			WARN_ON_ONCE(1);
+	}
+	return true;
+}
+
+static inline bool rtl_tx_slots_avail(struct rtl8169_private *tp,
+				      unsigned int nr_frags)
+{
+	unsigned int slots_avail = tp->dirty_tx + NUM_TX_DESC - tp->cur_tx;
+
+	/* A skbuff with nr_frags needs nr_frags+1 entries in the tx queue */
+	return slots_avail > nr_frags;
+}
+
+/* TX w/ own flag */
+static netdev_tx_t rtl_start_xmit(struct sk_buff *skb,
+				  struct net_device *dev)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+	unsigned int entry = tp->cur_tx % NUM_TX_DESC;
+	struct tx_desc *txd = tp->tx_desc_array + entry;
+	void __iomem *ioaddr = tp->mmio_addr;
+	struct device *d = &tp->pdev->dev;
+	dma_addr_t mapping;
+	u32 status, len;
+	u32 opts[2];
+	int frags;
+
+	if (unlikely(!rtl_tx_slots_avail(tp, skb_shinfo(skb)->nr_frags))) {
+		netif_err(tp, drv, dev,
+			  "BUG! Tx Ring full when queue awake!\n");
+		goto err_stop_0;
+	}
+	if (unlikely(le32_to_cpu(txd->opts1) & DESC_OWN))
+		goto err_stop_0;
+
+	opts[1] = cpu_to_le32(rtl8169_tx_vlan_tag(skb));
+	opts[0] = DESC_OWN;
+
+	if (!rtl8169_tso_csum(tp, skb, opts))
+		goto err_update_stats;
+
+	len = skb_headlen(skb);
+	if (tp->acp_enable) {
+		mapping = virt_to_phys(skb->data);
+	} else {
+		mapping = dma_map_single(d, skb->data, len, DMA_TO_DEVICE);
+		if (unlikely(dma_mapping_error(d, mapping))) {
+			if (net_ratelimit())
+				netif_err(tp, drv, dev,
+					  "Failed to map TX DMA!\n");
+			goto err_dma_0;
+		}
+	}
+
+	tp->tx_skb[entry].len = len;
+	txd->addr = cpu_to_le64(mapping);
+
+	frags = rtl8169_xmit_frags(tp, skb, opts);
+	if (frags < 0) {
+		goto err_dma_1;
+	} else if (frags) {
+		opts[0] |= FIRST_FRAG;
+	} else {
+		opts[0] |= FIRST_FRAG | LAST_FRAG;
+		tp->tx_skb[entry].skb = skb;
+	}
+
+	txd->opts2 = cpu_to_le32(opts[1]);
+
+	skb_tx_timestamp(skb);
+
+	wmb(); /* make sure txd->addr and txd->opts2 is ready */
+
+	/* Anti gcc 2.95.3 bugware (sic) */
+	status = opts[0] | len | (RING_END * !((entry + 1) % NUM_TX_DESC));
+	txd->opts1 = cpu_to_le32(status);
+
+	tp->cur_tx += frags + 1;
+
+	wmb(); /* make sure this TX descriptor is ready */
+
+	RTL_W8(TX_POLL, NPQ);
+
+	if (!rtl_tx_slots_avail(tp, MAX_SKB_FRAGS)) {
+		/* Avoid wrongly optimistic queue wake-up: rtl_tx thread must
+		 * not miss a ring update when it notices a stopped queue.
+		 */
+		smp_wmb();
+		netif_stop_queue(dev);
+		/* Sync with rtl_tx:
+		 * - publish queue status and cur_tx ring index (write barrier)
+		 * - refresh dirty_tx ring index (read barrier).
+		 * May the current thread have a pessimistic view of the ring
+		 * status and forget to wake up queue, a racing rtl_tx thread
+		 * can't.
+		 */
+		smp_mb();
+		if (rtl_tx_slots_avail(tp, MAX_SKB_FRAGS))
+			netif_wake_queue(dev);
+	}
+
+	return NETDEV_TX_OK;
+
+err_dma_1:
+	rtl8169_unmap_tx_skb(tp, d, tp->tx_skb + entry, txd);
+err_dma_0:
+	dev_kfree_skb(skb);
+err_update_stats:
+	dev->stats.tx_dropped++;
+	return NETDEV_TX_OK;
+
+err_stop_0:
+	netif_stop_queue(dev);
+	dev->stats.tx_dropped++;
+	return NETDEV_TX_BUSY;
+}
+
+static void rtl_tx(struct net_device *dev, struct rtl8169_private *tp)
+{
+	unsigned int dirty_tx, tx_left;
+
+	dirty_tx = tp->dirty_tx;
+	smp_rmb(); /* make sure dirty_tx is updated */
+	tx_left = tp->cur_tx - dirty_tx;
+
+	while (tx_left > 0) {
+		unsigned int entry = dirty_tx % NUM_TX_DESC;
+		struct ring_info *tx_skb = tp->tx_skb + entry;
+		u32 status;
+
+		rmb(); /* make sure this TX descriptor is ready */
+		status = le32_to_cpu(tp->tx_desc_array[entry].opts1);
+		if (status & DESC_OWN)
+			break;
+
+		rtl8169_unmap_tx_skb(tp, &tp->pdev->dev, tx_skb,
+				     tp->tx_desc_array + entry);
+		if (status & LAST_FRAG) {
+			u64_stats_update_begin(&tp->tx_stats.syncp);
+			tp->tx_stats.packets++;
+			tp->tx_stats.bytes += tx_skb->skb->len;
+			u64_stats_update_end(&tp->tx_stats.syncp);
+			dev_kfree_skb(tx_skb->skb);
+			tx_skb->skb = NULL;
+		}
+		dirty_tx++;
+		tx_left--;
+	}
+
+	if (tp->dirty_tx != dirty_tx) {
+		tp->dirty_tx = dirty_tx;
+		/* Sync with rtl_start_xmit:
+		 * - publish dirty_tx ring index (write barrier)
+		 * - refresh cur_tx ring index and queue status (read barrier)
+		 * May the current thread miss the stopped queue condition,
+		 * a racing xmit thread can only have a right view of the
+		 * ring status.
+		 */
+		smp_mb();
+		if (netif_queue_stopped(dev) &&
+		    rtl_tx_slots_avail(tp, MAX_SKB_FRAGS)) {
+			netif_wake_queue(dev);
+		}
+		/* 8168 hack: TX_POLL requests are lost when the Tx packets are
+		 * too close. Let's kick an extra TX_POLL request when a burst
+		 * of start_xmit activity is detected (if it is not detected,
+		 * it is slow enough). -- FR
+		 */
+		if (tp->cur_tx != dirty_tx) {
+			void __iomem *ioaddr = tp->mmio_addr;
+
+			RTL_W8(TX_POLL, NPQ);
+		}
+	}
+}
+
+/* TX w/o own flag;
+ * start of RTL_FEATURE_TX_NO_CLOSE
+ */
+static netdev_tx_t rtl_start_xmit_no_close(struct sk_buff *skb,
+					   struct net_device *dev)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+	unsigned int entry = tp->cur_tx % NUM_TX_DESC;
+	struct tx_desc *txd = tp->tx_desc_array + entry;
+	void __iomem *ioaddr = tp->mmio_addr;
+	struct device *d = &tp->pdev->dev;
+	dma_addr_t mapping;
+	u32 status, len;
+	u32 opts[2];
+	int frags;
+	u16 close_idx;
+	u16 tail_idx;
+
+	if (unlikely(!rtl_tx_slots_avail(tp, skb_shinfo(skb)->nr_frags))) {
+		netif_err(tp, drv, dev,
+			  "BUG! Tx Ring full when queue awake!\n");
+		goto err_stop_0;
+	}
+
+	close_idx = RTL_R16(TX_DESC_CLOSE_IDX) & TX_DESC_CNT_MASK;
+	tail_idx = tp->cur_tx & TX_DESC_CNT_MASK;
+	if ((tail_idx > close_idx && (tail_idx - close_idx == NUM_TX_DESC)) ||
+	    (tail_idx < close_idx &&
+	     (TX_DESC_CNT_SIZE - close_idx + tail_idx == NUM_TX_DESC)))
+		goto err_stop_0;
+
+	opts[1] = cpu_to_le32(rtl8169_tx_vlan_tag(skb));
+	opts[0] = DESC_OWN;
+
+	if (!rtl8169_tso_csum(tp, skb, opts))
+		goto err_update_stats;
+
+	len = skb_headlen(skb);
+	if (tp->acp_enable) {
+		mapping = virt_to_phys(skb->data);
+	} else {
+		mapping = dma_map_single(d, skb->data, len, DMA_TO_DEVICE);
+		if (unlikely(dma_mapping_error(d, mapping))) {
+			if (net_ratelimit())
+				netif_err(tp, drv, dev,
+					  "Failed to map TX DMA!\n");
+			goto err_dma_0;
+		}
+	}
+
+	tp->tx_skb[entry].len = len;
+	txd->addr = cpu_to_le64(mapping);
+
+	frags = rtl8169_xmit_frags(tp, skb, opts);
+	if (frags < 0) {
+		goto err_dma_1;
+	} else if (frags) {
+		opts[0] |= FIRST_FRAG;
+	} else {
+		opts[0] |= FIRST_FRAG | LAST_FRAG;
+		tp->tx_skb[entry].skb = skb;
+	}
+
+	txd->opts2 = cpu_to_le32(opts[1]);
+
+	skb_tx_timestamp(skb);
+
+	wmb(); /* make sure txd->addr and txd->opts2 is ready */
+
+	/* Anti gcc 2.95.3 bugware (sic) */
+	status = opts[0] | len | (RING_END * !((entry + 1) % NUM_TX_DESC));
+	txd->opts1 = cpu_to_le32(status);
+
+	tp->cur_tx += frags + 1;
+
+	RTL_W16(TX_DESC_TAIL_IDX, tp->cur_tx & TX_DESC_CNT_MASK);
+
+	wmb(); /* make sure this TX descriptor is ready */
+
+	RTL_W8(TX_POLL, NPQ);
+
+	if (!rtl_tx_slots_avail(tp, MAX_SKB_FRAGS)) {
+		/* Avoid wrongly optimistic queue wake-up:
+		 * rtl_tx_no_close thread must
+		 * not miss a ring update when it notices a stopped queue.
+		 */
+		smp_wmb();
+		netif_stop_queue(dev);
+		/* Sync with rtl_tx_no_close:
+		 * - publish queue status and cur_tx ring index (write barrier)
+		 * - refresh dirty_tx ring index (read barrier).
+		 * May the current thread have a pessimistic view of the ring
+		 * status and forget to wake up queue, a racing rtl_tx thread
+		 * can't.
+		 */
+		smp_mb();
+		if (rtl_tx_slots_avail(tp, MAX_SKB_FRAGS))
+			netif_wake_queue(dev);
+	}
+
+	return NETDEV_TX_OK;
+
+err_dma_1:
+	rtl8169_unmap_tx_skb(tp, d, tp->tx_skb + entry, txd);
+err_dma_0:
+	dev_kfree_skb(skb);
+err_update_stats:
+	dev->stats.tx_dropped++;
+	return NETDEV_TX_OK;
+
+err_stop_0:
+	netif_stop_queue(dev);
+	dev->stats.tx_dropped++;
+	return NETDEV_TX_BUSY;
+}
+
+static void rtl_tx_no_close(struct net_device *dev, struct rtl8169_private *tp)
+{
+	unsigned int dirty_tx, tx_left;
+	u16 close_idx;
+	u16 dirty_tx_idx;
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	dirty_tx = tp->dirty_tx;
+	close_idx = RTL_R16(TX_DESC_CLOSE_IDX) & TX_DESC_CNT_MASK;
+	dirty_tx_idx = dirty_tx & TX_DESC_CNT_MASK;
+	smp_rmb(); /* make sure dirty_tx and close_idx are updated */
+
+	if (dirty_tx_idx <= close_idx)
+		tx_left = close_idx - dirty_tx_idx;
+	else
+		tx_left = close_idx + TX_DESC_CNT_SIZE - dirty_tx_idx;
+
+	while (tx_left > 0) {
+		unsigned int entry = dirty_tx % NUM_TX_DESC;
+		struct ring_info *tx_skb = tp->tx_skb + entry;
+		u32 status;
+
+		rmb(); /* make sure this TX descriptor is ready */
+		status = le32_to_cpu(tp->tx_desc_array[entry].opts1);
+
+		rtl8169_unmap_tx_skb(tp, &tp->pdev->dev, tx_skb,
+				     tp->tx_desc_array + entry);
+		if (status & LAST_FRAG) {
+			u64_stats_update_begin(&tp->tx_stats.syncp);
+			tp->tx_stats.packets++;
+			tp->tx_stats.bytes += tx_skb->skb->len;
+			u64_stats_update_end(&tp->tx_stats.syncp);
+			dev_kfree_skb(tx_skb->skb);
+			tx_skb->skb = NULL;
+		}
+		dirty_tx++;
+		tx_left--;
+	}
+
+	if (tp->dirty_tx != dirty_tx) {
+		tp->dirty_tx = dirty_tx;
+		/* Sync with rtl_start_xmit_no_close:
+		 * - publish dirty_tx ring index (write barrier)
+		 * - refresh cur_tx ring index and queue status (read barrier)
+		 * May the current thread miss the stopped queue condition,
+		 * a racing xmit thread can only have a right view of the
+		 * ring status.
+		 */
+		smp_mb();
+		if (netif_queue_stopped(dev) &&
+		    rtl_tx_slots_avail(tp, MAX_SKB_FRAGS)) {
+			netif_wake_queue(dev);
+		}
+		/* 8168 hack: TX_POLL requests are lost when the Tx packets are
+		 * too close. Let's kick an extra TX_POLL request when a burst
+		 * of start_xmit activity is detected (if it is not detected,
+		 * it is slow enough). -- FR
+		 */
+		if (tp->cur_tx != dirty_tx) {
+			void __iomem *ioaddr = tp->mmio_addr;
+
+			RTL_W8(TX_POLL, NPQ);
+		}
+	}
+}
+
+/* end of RTL_FEATURE_TX_NO_CLOSE */
+
+static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb,
+				      struct net_device *dev)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	if (tp->chip->features & RTL_FEATURE_TX_NO_CLOSE)
+		return rtl_start_xmit_no_close(skb, dev);
+	else
+		return rtl_start_xmit(skb, dev);
+}
+
+static inline int rtl8169_fragmented_frame(u32 status)
+{
+	return (status & (FIRST_FRAG | LAST_FRAG)) != (FIRST_FRAG | LAST_FRAG);
+}
+
+static inline void rtl8169_rx_csum(struct sk_buff *skb, u32 opts1)
+{
+	u32 status = opts1 & RX_PROTO_MASK;
+
+	if ((status == RX_PROTO_TCP && !(opts1 & TCP_FAIL)) ||
+	    (status == RX_PROTO_UDP && !(opts1 & UDP_FAIL)))
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+	else
+		skb_checksum_none_assert(skb);
+}
+
+#if defined(CONFIG_RTL_RX_NO_COPY)
+static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp,
+		  u32 budget)
+{
+	unsigned int cur_rx, rx_left;
+	unsigned int count, delta;
+	struct device *d = &tp->pdev->dev;
+
+	cur_rx = tp->cur_rx;
+
+	rx_left = NUM_RX_DESC + tp->dirty_rx - cur_rx;
+	rx_left = min(budget, rx_left);
+
+	for (; rx_left > 0; rx_left--, cur_rx++) {
+		unsigned int entry = cur_rx % NUM_RX_DESC;
+		struct rx_desc *desc = tp->rx_desc_array + entry;
+		u32 status;
+
+		rmb(); /* make sure this RX descriptor is ready */
+		status = le32_to_cpu(desc->opts1) & tp->opts1_mask;
+
+		if (status & DESC_OWN)
+			break;
+		if (unlikely(status & RX_RES)) {
+			netif_info(tp, rx_err, dev, "Rx ERROR. status = %08x\n",
+				   status);
+			dev->stats.rx_errors++;
+			if (status & (RX_RWT | RX_RUNT))
+				dev->stats.rx_length_errors++;
+			if (status & RX_CRC)
+				dev->stats.rx_crc_errors++;
+			if (status & RX_FOVF) {
+				rtl_schedule_task(tp,
+						  RTL_FLAG_TASK_RESET_PENDING);
+				dev->stats.rx_fifo_errors++;
+			}
+			if ((status & (RX_RUNT | RX_CRC)) &&
+			    !(status & (RX_RWT | RX_FOVF)) &&
+			    (dev->features & NETIF_F_RXALL))
+				goto process_pkt;
+		} else {
+			struct sk_buff *skb;
+			dma_addr_t addr;
+			int pkt_size;
+
+process_pkt:
+
+			skb = tp->rx_databuff[entry];
+			addr = le64_to_cpu(desc->addr);
+			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(rtl8169_fragmented_frame(status))) {
+				dev->stats.rx_dropped++;
+				dev->stats.rx_length_errors++;
+				continue;
+			}
+
+			if (!tp->acp_enable)
+				dma_sync_single_for_cpu(d, addr, pkt_size,
+							DMA_FROM_DEVICE);
+			tp->rx_databuff[entry] = NULL;
+			if (!tp->acp_enable)
+				dma_unmap_single(d, addr, pkt_size,
+						 DMA_FROM_DEVICE);
+
+			rtl8169_rx_csum(skb, status);
+			skb_put(skb, pkt_size);
+			skb->protocol = eth_type_trans(skb, dev);
+
+			rtl8169_rx_vlan_tag(desc, skb);
+
+			napi_gro_receive(&tp->napi, skb);
+
+			u64_stats_update_begin(&tp->rx_stats.syncp);
+			tp->rx_stats.packets++;
+			tp->rx_stats.bytes += pkt_size;
+			u64_stats_update_end(&tp->rx_stats.syncp);
+		}
+	}
+
+	count = cur_rx - tp->cur_rx;
+	tp->cur_rx = cur_rx;
+
+	delta = rtl8168_rx_fill(tp, dev, tp->dirty_rx, tp->cur_rx);
+	/* netif_err(tp, drv, tp->dev, "delta =%x\n",delta); */
+	tp->dirty_rx += delta;
+
+	if (tp->dirty_rx + NUM_RX_DESC == tp->cur_rx) {
+		rtl_schedule_task(tp, RTL_FLAG_TASK_RESET_PENDING);
+		netif_err(tp, drv, tp->dev, "%s: Rx buffers exhausted\n",
+			  dev->name);
+	}
+
+	return count;
+}
+#else
+static struct sk_buff *rtl8169_try_rx_copy(void *data,
+					   struct rtl8169_private *tp,
+					   int pkt_size, dma_addr_t addr)
+{
+	struct sk_buff *skb;
+	struct device *d = &tp->pdev->dev;
+
+	data = rtl8169_align(data);
+	if (!tp->acp_enable)
+		dma_sync_single_for_cpu(d, addr, pkt_size, DMA_FROM_DEVICE);
+	prefetch(data);
+	skb = netdev_alloc_skb_ip_align(tp->dev, pkt_size);
+	if (skb)
+		memcpy(skb->data, data, pkt_size);
+	if (!tp->acp_enable)
+		dma_sync_single_for_device(d, addr, pkt_size, DMA_FROM_DEVICE);
+
+	return skb;
+}
+
+static int rtl_rx(struct net_device *dev, struct rtl8169_private *tp,
+		  u32 budget)
+{
+	unsigned int cur_rx, rx_left;
+	unsigned int count;
+	const unsigned int num_rx_desc = NUM_RX_DESC;
+
+	cur_rx = tp->cur_rx;
+
+	for (rx_left = min(budget, num_rx_desc); rx_left > 0;
+		rx_left--, cur_rx++) {
+		unsigned int entry = cur_rx % NUM_RX_DESC;
+		struct rx_desc *desc = tp->rx_desc_array + entry;
+		u32 status;
+
+		rmb(); /* make sure this RX descriptor is ready */
+		status = le32_to_cpu(desc->opts1) & tp->opts1_mask;
+
+		if (status & DESC_OWN)
+			break;
+		if (unlikely(status & RX_RES)) {
+			netif_info(tp, rx_err, dev, "Rx ERROR. status = %08x\n",
+				   status);
+			dev->stats.rx_errors++;
+			if (status & (RX_RWT | RX_RUNT))
+				dev->stats.rx_length_errors++;
+			if (status & RX_CRC)
+				dev->stats.rx_crc_errors++;
+			if (status & RX_FOVF) {
+				rtl_schedule_task(tp,
+						  RTL_FLAG_TASK_RESET_PENDING);
+				dev->stats.rx_fifo_errors++;
+			}
+			if ((status & (RX_RUNT | RX_CRC)) &&
+			    !(status & (RX_RWT | RX_FOVF)) &&
+			    (dev->features & NETIF_F_RXALL))
+				goto process_pkt;
+		} else {
+			struct sk_buff *skb;
+			dma_addr_t addr;
+			int pkt_size;
+
+process_pkt:
+			addr = le64_to_cpu(desc->addr);
+			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(rtl8169_fragmented_frame(status))) {
+				dev->stats.rx_dropped++;
+				dev->stats.rx_length_errors++;
+				goto release_descriptor;
+			}
+
+			skb = rtl8169_try_rx_copy(tp->rx_databuff[entry],
+						  tp, pkt_size, addr);
+			if (!skb) {
+				dev->stats.rx_dropped++;
+				goto release_descriptor;
+			}
+
+			rtl8169_rx_csum(skb, status);
+			skb_put(skb, pkt_size);
+			skb->protocol = eth_type_trans(skb, dev);
+
+			rtl8169_rx_vlan_tag(desc, skb);
+
+			napi_gro_receive(&tp->napi, skb);
+
+			u64_stats_update_begin(&tp->rx_stats.syncp);
+			tp->rx_stats.packets++;
+			tp->rx_stats.bytes += pkt_size;
+			u64_stats_update_end(&tp->rx_stats.syncp);
+		}
+release_descriptor:
+		desc->opts2 = 0;
+		wmb(); /* make sure this RX descriptor is useless */
+		rtl8169_mark_to_asic(desc, rx_buf_sz);
+	}
+
+	count = cur_rx - tp->cur_rx;
+	tp->cur_rx = cur_rx;
+
+	return count;
+}
+#endif /* CONFIG_RTL_RX_NO_COPY */
+
+static irqreturn_t phy_irq_handler(int irq, void *dev_instance)
+{
+	struct rtl8169_private *tp = dev_instance;
+	u32 isr;
+	u32 por;
+	int i;
+	bool hit = false;
+
+	pr_info("%s:%d: irq %d, tp 0x%p\n", __func__, __LINE__, irq, tp);
+	isr = readl(tp->mmio_clkaddr + ISO_UMSK_ISR);
+	pr_info("ISO_UMSK_ISR = 0x%08x\n", isr);
+	por = readl(tp->mmio_clkaddr + ISO_POR_DATAI);
+	pr_info("ISO_POR_DATAI = 0x%08x\n", por);
+
+	for (i = 0; i < tp->phy_irq_num; i++) {
+		if (irq == tp->phy_irq[i] &&
+		    (isr & (1 << tp->phy_irq_map[i]))) {
+			pr_info("%s: phy_0: get IRQ %d for GPHY POR interrupt (%d)\n",
+				tp->dev->name, irq, tp->phy_irq_map[i]);
+			writel(1 << tp->phy_irq_map[i],
+			       tp->mmio_clkaddr + ISO_UMSK_ISR);
+			hit = true;
+			break;
+		}
+	}
+
+	if (hit) {
+		if ((por & tp->phy_por_xv_mask) != tp->phy_por_xv_mask) {
+			pr_err("%s: phy_0: GPHY has power issue (0x%x)\n",
+			       tp->dev->name, por);
+			goto out;
+		}
+
+		if (!test_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags))
+			rtl_phy_reinit(tp);
+		else if (atomic_inc_return(&tp->phy_reinit_flag) == 1)
+			rtl_schedule_task(tp, RTL_FLAG_TASK_PHY_PENDING);
+		else
+			atomic_dec(&tp->phy_reinit_flag);
+	}
+
+out:
+	return IRQ_RETVAL(IRQ_HANDLED);
+}
+
+static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance)
+{
+	struct net_device *dev = dev_instance;
+	struct rtl8169_private *tp = netdev_priv(dev);
+	int handled = 0;
+	u16 status;
+
+	status = rtl_get_events(tp);
+	if (status && status != 0xffff) {
+		status &= RTL_EVENT_NAPI | tp->event_slow;
+		if (status) {
+			handled = 1;
+
+			rtl_irq_disable(tp);
+			napi_schedule(&tp->napi);
+		}
+	}
+	return IRQ_RETVAL(handled);
+}
+
+/* Workqueue context.
+ */
+static void rtl_slow_event_work(struct rtl8169_private *tp)
+{
+	struct net_device *dev = tp->dev;
+	void __iomem *ioaddr = tp->mmio_addr;
+	u16 status;
+
+	status = rtl_get_events(tp) & tp->event_slow;
+	rtl_ack_events(tp, status);
+
+	if (status & LINK_CHG) {
+		__rtl8169_check_link_status(dev, tp, tp->mmio_addr, true);
+
+		/* prevet ALDPS enter MAC Powercut Tx/Rx disable */
+		/* use MAC reset to set counter to offset 0 */
+		if (tp->chip->link_ok(ioaddr))
+			rtl_schedule_task(tp, RTL_FLAG_TASK_RESET_PENDING);
+	}
+
+	rtl_irq_enable_all(tp);
+}
+
+static void rtl_task(struct work_struct *work)
+{
+	static const struct {
+		int bitnr;
+		void (*action)(struct rtl8169_private *tp);
+	} rtl_work[] = {
+		/* XXX - keep rtl_slow_event_work() as first element. */
+		{ RTL_FLAG_TASK_SLOW_PENDING,	rtl_slow_event_work },
+		{ RTL_FLAG_TASK_RESET_PENDING,	rtl_reset_work },
+		{ RTL_FLAG_TASK_PHY_PENDING,	rtl_phy_work }
+	};
+	struct rtl8169_private *tp =
+		container_of(work, struct rtl8169_private, wk.work);
+	struct net_device *dev = tp->dev;
+	int i;
+
+	rtl_lock_work(tp);
+
+	if (!netif_running(dev) ||
+	    !test_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags))
+		goto out_unlock;
+
+	for (i = 0; i < ARRAY_SIZE(rtl_work); i++) {
+		bool pending;
+
+		pending = test_and_clear_bit(rtl_work[i].bitnr, tp->wk.flags);
+		if (pending)
+			rtl_work[i].action(tp);
+	}
+
+out_unlock:
+	rtl_unlock_work(tp);
+}
+
+static int rtl8169_poll(struct napi_struct *napi, int budget)
+{
+	struct rtl8169_private *tp =
+		container_of(napi, struct rtl8169_private, napi);
+	struct net_device *dev = tp->dev;
+	u16 enable_mask = RTL_EVENT_NAPI | tp->event_slow;
+	int work_done = 0;
+	u16 status;
+
+#if defined(CONFIG_RTL_RX_NO_COPY)
+	u32 old_dirty_rx;
+#endif /* CONFIG_RTL_RX_NO_COPY */
+
+	status = rtl_get_events(tp);
+	rtl_ack_events(tp, status & ~(tp->event_slow | RX_OVERFLOW));
+
+#if defined(CONFIG_RTL_RX_NO_COPY)
+	old_dirty_rx = tp->dirty_rx;
+#endif /* CONFIG_RTL_RX_NO_COPY */
+	if (status & RTL_EVENT_NAPI_RX)
+		work_done = rtl_rx(dev, tp, (u32)budget);
+
+#if defined(CONFIG_RTL_RX_NO_COPY)
+	if ((status & RX_OVERFLOW) && tp->dirty_rx != old_dirty_rx)
+		rtl_ack_events(tp, RX_OVERFLOW);
+#else
+	if ((status & RX_OVERFLOW) && work_done > 0)
+		rtl_ack_events(tp, RX_OVERFLOW);
+#endif /* CONFIG_RTL_RX_NO_COPY */
+
+	if (status & RTL_EVENT_NAPI_TX) {
+		if (tp->chip->features & RTL_FEATURE_TX_NO_CLOSE)
+			rtl_tx_no_close(dev, tp);
+		else
+			rtl_tx(dev, tp);
+	}
+
+	if (status & tp->event_slow) {
+		enable_mask &= ~tp->event_slow;
+
+		rtl_schedule_task(tp, RTL_FLAG_TASK_SLOW_PENDING);
+	}
+
+	if (work_done < budget) {
+		napi_complete(napi);
+
+		rtl_irq_enable(tp, enable_mask);
+	}
+
+	return work_done;
+}
+
+static void rtl8169_down(struct net_device *dev)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	napi_disable(&tp->napi);
+	netif_stop_queue(dev);
+
+	rtl8169_hw_reset(tp);
+	/* At this point device interrupts can not be enabled in any function,
+	 * as netif_running is not true (rtl8169_interrupt, rtl8169_reset_task)
+	 * and napi is disabled (rtl8169_poll).
+	 */
+
+	/* Give a racing hard_start_xmit a few cycles to complete. */
+	synchronize_rcu();
+
+	rtl8169_tx_clear(tp);
+
+	rtl8169_rx_clear(tp);
+
+	rtl_pll_power_down(tp);
+}
+
+static int rtl8169_close(struct net_device *dev)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+	struct platform_device *pdev = tp->pdev;
+
+	/* Update counters before going down */
+	rtl8169_update_counters(dev);
+
+	rtl_lock_work(tp);
+	clear_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags);
+
+	rtl8169_down(dev);
+
+	rtl_unlock_work(tp);
+
+	free_irq(dev->irq, dev);
+
+	if (tp->acp_enable) {
+		kfree(tp->rx_desc_array);
+		kfree(tp->tx_desc_array);
+	} else {
+		dma_free_coherent(&pdev->dev, R8169_RX_RING_BYTES,
+				  tp->rx_desc_array, tp->rx_phy_addr);
+		dma_free_coherent(&pdev->dev, R8169_TX_RING_BYTES,
+				  tp->tx_desc_array, tp->tx_phy_addr);
+	}
+	tp->tx_desc_array = NULL;
+	tp->rx_desc_array = NULL;
+
+	return 0;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void rtl8169_netpoll(struct net_device *dev)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	rtl8169_interrupt(tp->pdev->irq, dev);
+}
+#endif
+
+static int rtl_open(struct net_device *dev)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+	void __iomem *ioaddr = tp->mmio_addr;
+	struct platform_device *pdev = tp->pdev;
+	int retval = -ENOMEM;
+	int node = dev->dev.parent ? dev_to_node(dev->dev.parent) : -1;
+
+	/* Rx and Tx descriptors needs 256 bytes alignment.
+	 * dma_alloc_coherent provides more.
+	 */
+	if (tp->acp_enable) {
+		tp->tx_desc_array = kzalloc_node(R8169_TX_RING_BYTES,
+						 GFP_KERNEL, node);
+		tp->tx_phy_addr = virt_to_phys(tp->tx_desc_array);
+	} else {
+		tp->tx_desc_array = dma_alloc_coherent(&pdev->dev,
+						       R8169_TX_RING_BYTES,
+						       &tp->tx_phy_addr,
+						       GFP_KERNEL);
+	}
+	if (!tp->tx_desc_array)
+		goto err_pm_runtime_put;
+
+	if (tp->acp_enable) {
+		tp->rx_desc_array = kzalloc_node(R8169_RX_RING_BYTES,
+						 GFP_KERNEL, node);
+		tp->rx_phy_addr = virt_to_phys(tp->rx_desc_array);
+	} else {
+		tp->rx_desc_array = dma_alloc_coherent(&pdev->dev,
+						       R8169_RX_RING_BYTES,
+						       &tp->rx_phy_addr,
+						       GFP_KERNEL);
+	}
+	if (!tp->rx_desc_array)
+		goto err_free_tx_0;
+
+	retval = rtl8169_init_ring(dev);
+	if (retval < 0)
+		goto err_free_rx_1;
+
+	INIT_WORK(&tp->wk.work, rtl_task);
+
+	smp_mb(); /* make sure TX/RX rings are ready */
+
+	retval = request_irq(dev->irq, rtl8169_interrupt,
+			     (tp->features & RTL_FEATURE_MSI) ? 0 : IRQF_SHARED,
+			     dev->name, dev);
+	if (retval < 0)
+		goto err_free_rx_2;
+
+	rtl_lock_work(tp);
+
+	set_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags);
+
+	napi_enable(&tp->napi);
+
+	rtl8169_init_phy(dev, tp);
+
+	__rtl8169_set_features(dev, dev->features);
+
+	rtl_pll_power_up(tp);
+
+	rtl_hw_start(dev);
+
+	netif_start_queue(dev);
+
+	rtl_unlock_work(tp);
+
+	tp->saved_wolopts = 0;
+
+	rtl8169_check_link_status(dev, tp, ioaddr);
+out:
+	return retval;
+
+err_free_rx_2:
+	rtl8169_rx_clear(tp);
+err_free_rx_1:
+	if (tp->acp_enable)
+		kfree(tp->rx_desc_array);
+	else
+		dma_free_coherent(&pdev->dev, R8169_RX_RING_BYTES,
+				  tp->rx_desc_array, tp->rx_phy_addr);
+	tp->rx_desc_array = NULL;
+err_free_tx_0:
+	if (tp->acp_enable)
+		kfree(tp->tx_desc_array);
+	else
+		dma_free_coherent(&pdev->dev, R8169_TX_RING_BYTES,
+				  tp->tx_desc_array, tp->tx_phy_addr);
+	tp->tx_desc_array = NULL;
+err_pm_runtime_put:
+	goto out;
+}
+
+static void
+rtl8169_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+	unsigned int start;
+
+	do {
+		start = u64_stats_fetch_begin_irq(&tp->rx_stats.syncp);
+		stats->rx_packets = tp->rx_stats.packets;
+		stats->rx_bytes = tp->rx_stats.bytes;
+	} while (u64_stats_fetch_retry_irq(&tp->rx_stats.syncp, start));
+
+	do {
+		start = u64_stats_fetch_begin_irq(&tp->tx_stats.syncp);
+		stats->tx_packets = tp->tx_stats.packets;
+		stats->tx_bytes = tp->tx_stats.bytes;
+	} while (u64_stats_fetch_retry_irq(&tp->tx_stats.syncp, start));
+
+	stats->rx_dropped	= dev->stats.rx_dropped;
+	stats->tx_dropped	= dev->stats.tx_dropped;
+	stats->rx_length_errors	= dev->stats.rx_length_errors;
+	stats->rx_errors	= dev->stats.rx_errors;
+	stats->rx_crc_errors	= dev->stats.rx_crc_errors;
+	stats->rx_fifo_errors	= dev->stats.rx_fifo_errors;
+	stats->rx_missed_errors	= dev->stats.rx_missed_errors;
+}
+
+static void rtl8169_net_suspend(struct net_device *dev)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	if (!netif_running(dev))
+		return;
+
+	netif_device_detach(dev);
+	netif_stop_queue(dev);
+
+	rtl_lock_work(tp);
+	napi_disable(&tp->napi);
+	clear_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags);
+	rtl_unlock_work(tp);
+
+	/* disable EEE if it is enabled */
+	tp->chip->eee_set(tp, false);
+
+	rtl8169_hw_reset(tp);
+
+	rtl_pll_power_down(tp);
+}
+
+#ifdef CONFIG_PM
+
+static int rtl8169_suspend(struct device *dev)
+{
+	struct net_device *ndev = dev_get_drvdata(dev);
+	struct rtl8169_private *tp = netdev_priv(ndev);
+
+	pr_info("[RTK_ETN] Enter %s\n", __func__);
+
+	rtl8169_net_suspend(ndev);
+
+	/* turn off LED, and current solution is switch pad to GPIO input */
+	if (tp->output_mode == OUTPUT_EMBEDDED_PHY)
+		tp->chip->led_set(tp, false);
+
+	pr_info("[RTK_ETN] Exit %s\n", __func__);
+
+	return 0;
+}
+
+static void __rtl8169_resume(struct net_device *dev)
+{
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	netif_device_attach(dev);
+
+	/* turn on LED */
+	if (tp->output_mode == OUTPUT_EMBEDDED_PHY)
+		tp->chip->led_set(tp, true);
+
+	rtl_pll_power_up(tp);
+	rtl_rar_set(tp, tp->dev->dev_addr);
+
+	rtl_lock_work(tp);
+	napi_enable(&tp->napi);
+	set_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags);
+	rtl_unlock_work(tp);
+
+	rtl_schedule_task(tp, RTL_FLAG_TASK_RESET_PENDING);
+}
+
+static int rtl8169_resume(struct device *dev)
+{
+	struct net_device *ndev = dev_get_drvdata(dev);
+	struct rtl8169_private *tp = netdev_priv(ndev);
+
+	pr_info("[RTK_ETN] Enter %s\n", __func__);
+
+	if (netif_running(ndev))
+		__rtl8169_resume(ndev);
+
+	/* enable EEE according to tp->eee_enable */
+	tp->chip->eee_set(tp, true);
+
+	rtl8169_init_phy(ndev, tp);
+
+	pr_info("[RTK_ETN] Exit %s\n", __func__);
+
+	return 0;
+}
+
+static __maybe_unused int rtl8169_runtime_suspend(struct device *dev)
+{
+	struct net_device *ndev = dev_get_drvdata(dev);
+	struct rtl8169_private *tp = netdev_priv(ndev);
+
+	if (!tp->tx_desc_array)
+		return 0;
+
+	rtl_lock_work(tp);
+	tp->saved_wolopts = __rtl8169_get_wol(tp);
+	__rtl8169_set_wol(tp, WAKE_ANY);
+	rtl_unlock_work(tp);
+
+	rtl8169_net_suspend(ndev);
+
+	return 0;
+}
+
+static __maybe_unused int rtl8169_runtime_resume(struct device *dev)
+{
+	struct net_device *ndev = dev_get_drvdata(dev);
+	struct rtl8169_private *tp = netdev_priv(ndev);
+
+	if (!tp->tx_desc_array)
+		return 0;
+
+	rtl_lock_work(tp);
+	__rtl8169_set_wol(tp, tp->saved_wolopts);
+	tp->saved_wolopts = 0;
+	rtl_unlock_work(tp);
+
+	rtl8169_init_phy(ndev, tp);
+
+	__rtl8169_resume(ndev);
+
+	return 0;
+}
+
+static __maybe_unused int rtl8169_runtime_idle(struct device *dev)
+{
+	struct net_device *ndev = dev_get_drvdata(dev);
+	struct rtl8169_private *tp = netdev_priv(ndev);
+
+	return tp->tx_desc_array ? -EBUSY : 0;
+}
+
+static const struct dev_pm_ops rtl8169_pm_ops = {
+	.suspend		= rtl8169_suspend,
+	.resume			= rtl8169_resume,
+	.freeze			= rtl8169_suspend,
+	.thaw			= rtl8169_resume,
+	.poweroff		= rtl8169_suspend,
+	.restore		= rtl8169_resume,
+	.runtime_suspend	= rtl8169_runtime_suspend,
+	.runtime_resume		= rtl8169_runtime_resume,
+	.runtime_idle		= rtl8169_runtime_idle,
+};
+
+#define RTL8169_PM_OPS	(&rtl8169_pm_ops)
+
+#else /* !CONFIG_PM */
+
+#define RTL8169_PM_OPS	NULL
+
+#endif /* !CONFIG_PM */
+
+static void rtl_shutdown(struct platform_device *pdev)
+{
+	struct net_device *dev = platform_get_drvdata(pdev);
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	rtl8169_net_suspend(dev);
+
+	/* Restore original MAC address */
+	rtl_rar_set(tp, dev->perm_addr);
+
+	rtl8169_hw_reset(tp);
+
+	if (system_state == SYSTEM_POWER_OFF) {
+		if (__rtl8169_get_wol(tp) & WAKE_ANY)
+			rtl_wol_suspend_quirk(tp);
+	}
+}
+
+static int rtl_remove_one(struct platform_device *pdev)
+{
+	struct net_device *dev = platform_get_drvdata(pdev);
+	struct rtl8169_private *tp = netdev_priv(dev);
+	int i;
+
+#ifdef RTL_PROC
+	do {
+		if (!tp->dir_dev)
+			break;
+
+		remove_proc_entry("wol_enable", tp->dir_dev);
+		remove_proc_entry("phy_reinit", tp->dir_dev);
+		remove_proc_entry("eee", tp->dir_dev);
+		remove_proc_entry("driver_var", tp->dir_dev);
+		remove_proc_entry("eth_phy", tp->dir_dev);
+		remove_proc_entry("ext_regs", tp->dir_dev);
+		remove_proc_entry("registers", tp->dir_dev);
+		remove_proc_entry("tx_desc", tp->dir_dev);
+		remove_proc_entry("rx_desc", tp->dir_dev);
+		remove_proc_entry("tally", tp->dir_dev);
+		remove_proc_entry("wpd_event", tp->dir_dev);
+		remove_proc_entry("wol_packet", tp->dir_dev);
+
+		if (!rtw_proc)
+			break;
+
+		remove_proc_entry(MODULENAME, rtw_proc);
+		remove_proc_entry("eth0", init_net.proc_net);
+
+		rtw_proc = NULL;
+
+	} while (0);
+#endif
+
+	cancel_work_sync(&tp->wk.work);
+
+	netif_napi_del(&tp->napi);
+
+	unregister_netdev(dev);
+
+	/* restore original MAC address */
+	rtl_rar_set(tp, dev->perm_addr);
+
+	rtl8169_release_board(pdev, dev, tp->mmio_addr);
+	platform_set_drvdata(pdev, NULL);
+
+	for (i = 0; i < tp->phy_irq_num; i++)
+		free_irq(tp->phy_irq[i], tp);
+	return 0;
+}
+
+static const struct net_device_ops rtl_netdev_ops = {
+	.ndo_open		= rtl_open,
+	.ndo_stop		= rtl8169_close,
+	.ndo_get_stats64	= rtl8169_get_stats64,
+	.ndo_start_xmit		= rtl8169_start_xmit,
+	.ndo_tx_timeout		= rtl8169_tx_timeout,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_change_mtu		= rtl8169_change_mtu,
+	.ndo_fix_features	= rtl8169_fix_features,
+	.ndo_set_features	= rtl8169_set_features,
+	.ndo_set_mac_address	= rtl_set_mac_address,
+	.ndo_do_ioctl		= rtl8169_ioctl,
+	.ndo_set_rx_mode	= rtl_set_rx_mode,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	.ndo_poll_controller	= rtl8169_netpoll,
+#endif
+
+};
+
+DECLARE_RTL_COND(rtl_link_list_ready_cond)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	return RTL_R8(MCU) & LINK_LIST_RDY;
+}
+
+DECLARE_RTL_COND(rtl_rxtx_empty_cond)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	return (RTL_R8(MCU) & RXTX_EMPTY) == RXTX_EMPTY;
+}
+
+static void rtl_hw_init_8168g(struct rtl8169_private *tp)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+	u32 data;
+
+	tp->ocp_base = OCP_STD_PHY_BASE;
+
+	RTL_W32(MISC, RTL_R32(MISC) | RXDV_GATED_EN);
+
+	if (!rtl_udelay_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 42))
+		return;
+
+	if (!rtl_udelay_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42))
+		return;
+
+	RTL_W8(CHIP_CMD, RTL_R8(CHIP_CMD) & ~(CMD_TX_ENB | CMD_RX_ENB));
+	usleep_range(1000, 1100);
+	RTL_W8(MCU, RTL_R8(MCU) & ~NOW_IS_OOB);
+
+	data = rtl_ocp_read(tp, 0xe8de);
+	data &= ~BIT(14);
+	rtl_ocp_write(tp, 0xe8de, data);
+
+	if (!rtl_udelay_loop_wait_high(tp, &rtl_link_list_ready_cond, 100, 42))
+		return;
+
+	data = rtl_ocp_read(tp, 0xe8de);
+	data |= BIT(15);
+	rtl_ocp_write(tp, 0xe8de, data);
+
+	if (!rtl_udelay_loop_wait_high(tp, &rtl_link_list_ready_cond, 100, 42))
+		return;
+}
+
+static void rtl_hw_initialize(struct rtl8169_private *tp)
+{
+	rtl_hw_init_8168g(tp);
+}
+
+#ifdef RTL_PROC
+static int wol_read_proc(struct seq_file *m, void *v)
+{
+	struct net_device *dev = m->private;
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	pr_info("WoL setting:\n");
+	pr_info("\tBIT 0:\t WoL enable\n");
+	pr_info("\tBIT 1:\t CRC match\n");
+	pr_info("\tBIT 2:\t WPD\n");
+	pr_info("wol_enable = 0x%x\n", tp->wol_enable);
+	seq_printf(m, "%d\n", tp->wol_enable);
+	return 0;
+}
+
+static ssize_t wol_write_proc(struct file *file, const char __user *buffer,
+			      size_t count, loff_t *pos)
+{
+	struct net_device *dev = (struct net_device *)
+		((struct seq_file *)file->private_data)->private;
+	struct rtl8169_private *tp = netdev_priv(dev);
+	char tmp[32];
+	u32 val = 0;
+	u32 len = 0;
+	int ret;
+
+	len = min(count, sizeof(tmp) - 1);
+	if (buffer && !copy_from_user(tmp, buffer, len)) {
+		if (len)
+			tmp[len - 1] = '\0';
+		ret = kstrtou32(tmp, 0, &val);
+		if (ret) {
+			pr_err("invalid WoL setting [%s], ret = %d\n",
+			       tmp, ret);
+			return count;
+		}
+		tp->wol_enable = val;
+		pr_info("set wol_enable = %x\n", tp->wol_enable);
+	}
+	return count;
+}
+
+static int wol_proc_open(struct inode *inode, struct file *file)
+{
+	struct net_device *dev = proc_get_parent_data(inode);
+
+	return single_open(file, wol_read_proc, dev);
+}
+
+static const struct proc_ops wol_proc_ops = {
+	.proc_open	= wol_proc_open,
+	.proc_read	= seq_read,
+	.proc_write	= wol_write_proc,
+	.proc_lseek	= seq_lseek,
+	.proc_release	= single_release,
+};
+
+static int phy_reinit_read_proc(struct seq_file *m, void *v)
+{
+	struct net_device *dev = m->private;
+	struct rtl8169_private *tp = netdev_priv(dev);
+	u32 isr;
+	u32 por;
+
+	isr = readl(tp->mmio_clkaddr + ISO_UMSK_ISR);
+	seq_printf(m, "ISO_UMSK_ISR = 0x%08x\n", isr);
+	por = readl(tp->mmio_clkaddr + ISO_POR_DATAI);
+	seq_printf(m, "ISO_POR_DATAI = 0x%08x\n", por);
+
+	seq_puts(m, "\n\nUsage: echo 1 > phy_reinit\n");
+
+	return 0;
+}
+
+static ssize_t phy_reinit_write_proc(struct file *file,
+				     const char __user *buffer,
+				     size_t count, loff_t *pos)
+{
+	struct net_device *dev = (struct net_device *)
+		((struct seq_file *)file->private_data)->private;
+	struct rtl8169_private *tp = netdev_priv(dev);
+	char tmp[32];
+	u32 val = 0;
+	u32 len = 0;
+	int ret;
+
+	len = min(count, sizeof(tmp) - 1);
+	if (buffer && !copy_from_user(tmp, buffer, len)) {
+		if (len)
+			tmp[len - 1] = '\0';
+		ret = kstrtou32(tmp, 0, &val);
+		if (ret || val == 0) {
+			pr_err("invalid phy_reinit [%s], ret = %d\n",
+			       tmp, ret);
+			return count;
+		}
+		pr_info("phy_reinit = %x\n", val);
+
+		if (!test_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags))
+			rtl_phy_reinit(tp);
+		else if (atomic_inc_return(&tp->phy_reinit_flag) == 1)
+			rtl_schedule_task(tp, RTL_FLAG_TASK_PHY_PENDING);
+		else
+			atomic_dec(&tp->phy_reinit_flag);
+	}
+	return count;
+}
+
+static int phy_reinit_proc_open(struct inode *inode, struct file *file)
+{
+	struct net_device *dev = proc_get_parent_data(inode);
+
+	return single_open(file, phy_reinit_read_proc, dev);
+}
+
+static const struct proc_ops phy_reinit_proc_ops = {
+	.proc_open	= phy_reinit_proc_open,
+	.proc_read	= seq_read,
+	.proc_write	= phy_reinit_write_proc,
+	.proc_lseek	= seq_lseek,
+	.proc_release	= single_release,
+};
+
+static int eee_read_proc(struct seq_file *m, void *v)
+{
+	struct net_device *dev = m->private;
+	struct rtl8169_private *tp = netdev_priv(dev);
+	unsigned short e040, e080;
+
+	rtl_lock_work(tp);
+	e040 = rtl_ocp_read(tp, 0xe040);	/* EEE */
+	e080 = rtl_ocp_read(tp, 0xe080);	/* EEE+ */
+	seq_printf(m, "%s: eee = %d, OCP 0xe040 = 0x%x, OCP 0xe080 = 0x%x\n",
+		   dev->name, tp->eee_enable, e040, e080);
+	r8169_display_eee_info(dev, m, tp);
+	rtl_unlock_work(tp);
+
+	return 0;
+}
+
+static ssize_t eee_write_proc(struct file *file, const char __user *buffer,
+			      size_t count, loff_t *pos)
+{
+	char tmp[32];
+	u32 val = 0;
+	u32 len = 0;
+	int ret;
+	bool chg = false;
+	struct net_device *dev = (struct net_device *)
+		((struct seq_file *)file->private_data)->private;
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	len = min(count, sizeof(tmp) - 1);
+	if (buffer && !copy_from_user(tmp, buffer, len)) {
+		if (len)
+			tmp[len - 1] = '\0';
+		ret = kstrtou32(tmp, 0, &val);
+		if (ret) {
+			pr_err("invalid EEE setting [%s], ret = %d\n",
+			       tmp, ret);
+			return count;
+		}
+		pr_err("write %s eee = %x\n", dev->name, val);
+
+		if (val > 0 && !tp->eee_enable) {
+			tp->eee_enable = true;
+			chg = true;
+		} else if (val == 0 && tp->eee_enable) {
+			tp->eee_enable = false;
+			chg = true;
+		}
+	}
+
+	if (!chg)
+		goto done;
+
+	rtl_lock_work(tp);
+	/* power down PHY */
+	tp->chip->mdio_lock(tp);
+	rtl_writephy(tp, 0x1f, 0x0000);
+	rtl_patchphy(tp, MII_BMCR, BMCR_PDOWN);
+	tp->chip->mdio_unlock(tp);
+
+	tp->chip->eee_set(tp, tp->eee_enable);
+	mdelay(100);	/* wait PHY ready */
+	rtl8169_phy_reset(dev, tp);
+	rtl_unlock_work(tp);
+
+done:
+	return count;
+}
+
+/* seq_file wrappers for procfile show routines. */
+static int eee_proc_open(struct inode *inode, struct file *file)
+{
+	struct net_device *dev = proc_get_parent_data(inode);
+
+	return single_open(file, eee_read_proc, dev);
+}
+
+static const struct proc_ops eee_proc_ops = {
+	.proc_open	= eee_proc_open,
+	.proc_read	= seq_read,
+	.proc_write	= eee_write_proc,
+	.proc_lseek	= seq_lseek,
+	.proc_release	= single_release,
+};
+
+static void r8169soc_dump_regs(struct seq_file *m, struct rtl8169_private *tp)
+{
+	u32 addr;
+	u32 val;
+	u32 i;
+
+	tp->chip->dump_regs(m, tp);
+
+	seq_puts(m, "ETN MAC regs:\n");
+	for (i = 0; i < 256; i += 4) {
+		addr = 0x98016000 + i;
+		val = readl(tp->mmio_addr + i);
+		seq_printf(m, "[%08x] = %08x\n", addr, val);
+	}
+}
+
+static int registers_read_proc(struct seq_file *m, void *v)
+{
+	struct net_device *dev = m->private;
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	rtl_lock_work(tp);
+	r8169soc_dump_regs(m, tp);
+	rtl_unlock_work(tp);
+
+	return 0;
+}
+
+/* seq_file wrappers for procfile show routines. */
+static int registers_proc_open(struct inode *inode, struct file *file)
+{
+	struct net_device *dev = proc_get_parent_data(inode);
+
+	return single_open(file, registers_read_proc, dev);
+}
+
+static const struct proc_ops registers_proc_ops = {
+	.proc_open	= registers_proc_open,
+	.proc_read	= seq_read,
+	.proc_write	= NULL,
+	.proc_lseek	= seq_lseek,
+	.proc_release	= single_release,
+};
+
+static void r8169soc_dump_tx_desc(struct seq_file *m,
+				  struct rtl8169_private *tp)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+	u32 i;
+
+	if (!tp->tx_desc_array) {
+		seq_puts(m, "no tx_desc_array\n");
+		return;
+	}
+
+	seq_printf(m, "SW TX INDEX: %d\n", tp->cur_tx % NUM_TX_DESC);
+	seq_printf(m, "RECYCLED TX INDEX: %d\n", tp->dirty_tx % NUM_TX_DESC);
+	if (tp->chip->features & RTL_FEATURE_TX_NO_CLOSE) {
+		i = RTL_R16(TX_DESC_CLOSE_IDX) & TX_DESC_CNT_MASK;
+		seq_printf(m, "HW TX INDEX: %d\n", i % NUM_TX_DESC);
+	}
+	seq_puts(m, "TX DESC:\n");
+	for (i = 0; i < NUM_TX_DESC; i++)
+		seq_printf(m, "Desc[%04d] opts1 0x%08x, opts2 0x%08x, addr 0x%llx\n",
+			   i, tp->tx_desc_array[i].opts1,
+			   tp->tx_desc_array[i].opts2,
+			   tp->tx_desc_array[i].addr);
+}
+
+static int tx_desc_read_proc(struct seq_file *m, void *v)
+{
+	struct net_device *dev = m->private;
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	rtl_lock_work(tp);
+	r8169soc_dump_tx_desc(m, tp);
+	rtl_unlock_work(tp);
+
+	return 0;
+}
+
+/* seq_file wrappers for procfile show routines. */
+static int tx_desc_proc_open(struct inode *inode, struct file *file)
+{
+	struct net_device *dev = proc_get_parent_data(inode);
+
+	return single_open(file, tx_desc_read_proc, dev);
+}
+
+static const struct proc_ops tx_desc_proc_ops = {
+	.proc_open	= tx_desc_proc_open,
+	.proc_read	= seq_read,
+	.proc_write	= NULL,
+	.proc_lseek	= seq_lseek,
+	.proc_release	= single_release,
+};
+
+static void r8169soc_dump_rx_desc(struct seq_file *m,
+				  struct rtl8169_private *tp)
+{
+	u32 i;
+
+	if (!tp->rx_desc_array) {
+		seq_puts(m, "no rx_desc_array\n");
+		return;
+	}
+
+	seq_printf(m, "SW RX INDEX: %d\n", tp->cur_rx % NUM_RX_DESC);
+	#if defined(CONFIG_RTL_RX_NO_COPY)
+	seq_printf(m, "REFILLED RX INDEX: %d\n", tp->dirty_rx % NUM_RX_DESC);
+	#endif /* CONFIG_RTL_RX_NO_COPY */
+	seq_puts(m, "RX DESC:\n");
+	for (i = 0; i < NUM_RX_DESC; i++)
+		seq_printf(m, "Desc[%04d] opts1 0x%08x, opts2 0x%08x, addr 0x%llx\n",
+			   i, tp->rx_desc_array[i].opts1,
+			   tp->rx_desc_array[i].opts2,
+			   tp->rx_desc_array[i].addr);
+}
+
+static int rx_desc_read_proc(struct seq_file *m, void *v)
+{
+	struct net_device *dev = m->private;
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	rtl_lock_work(tp);
+	r8169soc_dump_rx_desc(m, tp);
+	rtl_unlock_work(tp);
+
+	return 0;
+}
+
+/* seq_file wrappers for procfile show routines. */
+static int rx_desc_proc_open(struct inode *inode, struct file *file)
+{
+	struct net_device *dev = proc_get_parent_data(inode);
+
+	return single_open(file, rx_desc_read_proc, dev);
+}
+
+static const struct proc_ops rx_desc_proc_ops = {
+	.proc_open	= rx_desc_proc_open,
+	.proc_read	= seq_read,
+	.proc_write	= NULL,
+	.proc_lseek	= seq_lseek,
+	.proc_release	= single_release,
+};
+
+static int driver_var_read_proc(struct seq_file *m, void *v)
+{
+	struct net_device *dev = m->private;
+	struct rtl8169_private *tp = netdev_priv(dev);
+	int i;
+
+	seq_puts(m, "\nDump Driver Variable\n");
+
+	rtl_lock_work(tp);
+	seq_puts(m, "Variable\tValue\n----------\t-----\n");
+	seq_printf(m, "MODULENAME\t%s\n", MODULENAME);
+	seq_printf(m, "mac version\t%d\n", tp->chip->mac_version);
+	seq_printf(m, "chipset_name\t%s\n", tp->chip->name);
+	seq_printf(m, "driver version\t%s\n", RTL8169_VERSION);
+	seq_printf(m, "txd version\t%d\n", tp->chip->txd_version);
+	seq_printf(m, "chip features\t0x%x\n", tp->chip->features);
+	seq_printf(m, "msg_enable\t0x%x\n", tp->msg_enable);
+	seq_printf(m, "mtu\t\t%d\n", dev->mtu);
+	seq_printf(m, "NUM_RX_DESC\t0x%x\n", NUM_RX_DESC);
+	seq_printf(m, "cur_rx\t\t0x%x\n", tp->cur_rx);
+#if defined(CONFIG_RTL_RX_NO_COPY)
+	seq_printf(m, "dirty_rx\t0x%x\n", tp->dirty_rx);
+#endif /* CONFIG_RTL_RX_NO_COPY */
+	seq_printf(m, "NUM_TX_DESC\t0x%x\n", NUM_TX_DESC);
+	seq_printf(m, "cur_tx\t\t0x%x\n", tp->cur_tx);
+	seq_printf(m, "dirty_tx\t0x%x\n", tp->dirty_tx);
+	seq_printf(m, "rx_buf_sz\t%d\n", rx_buf_sz);
+	seq_printf(m, "cp_cmd\t\t0x%x\n", tp->cp_cmd);
+	seq_printf(m, "event_slow\t0x%x\n", tp->event_slow);
+	seq_printf(m, "wol_enable\t0x%x\n", tp->wol_enable);
+	seq_printf(m, "saved_wolopts\t0x%x\n", tp->saved_wolopts);
+	seq_printf(m, "opts1_mask\t0x%x\n", tp->opts1_mask);
+	seq_printf(m, "wol_crc_cnt\t%d\n", tp->wol_crc_cnt);
+	seq_printf(m, "led_cfg\t\t0x%x\n", tp->led_cfg);
+	seq_printf(m, "cur_features\t0x%x\n", tp->features);
+	seq_printf(m, "eee_enable\t%d\n", tp->eee_enable);
+	seq_printf(m, "acp_enable\t%d\n", tp->acp_enable);
+	seq_printf(m, "ext_phy\t\t%d\n", tp->ext_phy);
+	seq_printf(m, "output_mode\t%d\n", tp->output_mode);
+	tp->chip->dump_var(m, tp);
+	seq_printf(m, "ETN IRQ\t\t%d\n", dev->irq);
+	seq_printf(m, "phy_irq_num\t%d\n", tp->phy_irq_num);
+	for (i = 0; i < tp->phy_irq_num; i++)
+		seq_printf(m, "GPHY IRQ\t%d maps to ISR bit %d\n",
+			   tp->phy_irq[i], tp->phy_irq_map[i]);
+	seq_printf(m, "perm_addr\t%pM\n", dev->perm_addr);
+	seq_printf(m, "dev_addr\t%pM\n", dev->dev_addr);
+	rtl_unlock_work(tp);
+
+	seq_putc(m, '\n');
+	return 0;
+}
+
+static int driver_var_proc_open(struct inode *inode, struct file *file)
+{
+	struct net_device *dev = proc_get_parent_data(inode);
+
+	return single_open(file, driver_var_read_proc, dev);
+}
+
+static const struct proc_ops driver_var_proc_ops = {
+	.proc_open	= driver_var_proc_open,
+	.proc_read	= seq_read,
+	.proc_write	= NULL,
+	.proc_lseek	= seq_lseek,
+	.proc_release	= single_release,
+};
+
+static int tally_read_proc(struct seq_file *m, void *v)
+{
+	struct net_device *dev = m->private;
+	struct rtl8169_private *tp = netdev_priv(dev);
+	struct rtl8169_counters *counters = &tp->counters;
+
+	rtl8169_update_counters(dev);
+
+	seq_puts(m, "\nDump Tally Counter\n");
+	seq_puts(m, "Statistics\t\tValue\n----------\t\t-----\n");
+	seq_printf(m, "tx_packets\t\t%lld\n",
+		   le64_to_cpu(counters->tx_packets));
+	seq_printf(m, "rx_packets\t\t%lld\n",
+		   le64_to_cpu(counters->rx_packets));
+	seq_printf(m, "tx_errors\t\t%lld\n", le64_to_cpu(counters->tx_errors));
+	seq_printf(m, "rx_errors\t\t%d\n", le32_to_cpu(counters->rx_errors));
+	seq_printf(m, "rx_missed\t\t%d\n", le16_to_cpu(counters->rx_missed));
+	seq_printf(m, "align_errors\t\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\t%lld\n",
+		   le64_to_cpu(counters->rx_unicast));
+	seq_printf(m, "rx_broadcast\t\t%lld\n",
+		   le64_to_cpu(counters->rx_broadcast));
+	seq_printf(m, "rx_multicast\t\t%d\n",
+		   le32_to_cpu(counters->rx_multicast));
+	seq_printf(m, "tx_aborted\t\t%d\n", le16_to_cpu(counters->tx_aborted));
+	seq_printf(m, "tx_underrun\t\t%d\n",
+		   le16_to_cpu(counters->tx_underrun));
+
+	seq_putc(m, '\n');
+	return 0;
+}
+
+static int tally_proc_open(struct inode *inode, struct file *file)
+{
+	struct net_device *dev = proc_get_parent_data(inode);
+
+	return single_open(file, tally_read_proc, dev);
+}
+
+static const struct proc_ops tally_proc_ops = {
+	.proc_open	= tally_proc_open,
+	.proc_read	= seq_read,
+	.proc_write	= NULL,
+	.proc_lseek	= seq_lseek,
+	.proc_release	= single_release,
+};
+
+static int eth_phy_read_proc(struct seq_file *m, void *v)
+{
+	struct net_device *dev = m->private;
+	int i, n, max = 16;
+	u16 word_rd;
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	seq_puts(m, "\nDump Ethernet PHY\n");
+	seq_puts(m, "\nOffset\tValue\n------\t-----\n ");
+
+	rtl_lock_work(tp);
+	seq_puts(m, "\n####################page 0##################\n ");
+	tp->chip->mdio_lock(tp);
+	r8169_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 = r8169_mdio_read(tp, n);
+			seq_printf(m, "%04x ", word_rd);
+		}
+	}
+	tp->chip->mdio_unlock(tp);
+	rtl_unlock_work(tp);
+
+	seq_putc(m, '\n');
+	return 0;
+}
+
+static int eth_phy_proc_open(struct inode *inode, struct file *file)
+{
+	struct net_device *dev = proc_get_parent_data(inode);
+
+	return single_open(file, eth_phy_read_proc, dev);
+}
+
+static const struct proc_ops eth_phy_proc_ops = {
+	.proc_open	= eth_phy_proc_open,
+	.proc_read	= seq_read,
+	.proc_write	= NULL,
+	.proc_lseek	= seq_lseek,
+	.proc_release	= single_release,
+};
+
+static int ext_regs_read_proc(struct seq_file *m, void *v)
+{
+	struct net_device *dev = m->private;
+	int i, n, max = 256;
+	u32 dword_rd;
+	struct rtl8169_private *tp = netdev_priv(dev);
+
+	seq_puts(m, "\nDump Extended Registers\n");
+	seq_puts(m, "\nOffset\tValue\n------\t-----\n ");
+
+	rtl_lock_work(tp);
+	for (n = 0; n < max;) {
+		seq_printf(m, "\n0x%02x:\t", n);
+
+		for (i = 0; i < 4 && n < max; i++, n += 4) {
+			dword_rd = rtl_eri_read(tp, n, ERIAR_EXGMAC);
+			seq_printf(m, "%08x ", dword_rd);
+		}
+	}
+	rtl_unlock_work(tp);
+
+	seq_putc(m, '\n');
+	return 0;
+}
+
+static int ext_regs_proc_open(struct inode *inode, struct file *file)
+{
+	struct net_device *dev = proc_get_parent_data(inode);
+
+	return single_open(file, ext_regs_read_proc, dev);
+}
+
+static const struct proc_ops ext_regs_proc_ops = {
+	.proc_open	= ext_regs_proc_open,
+	.proc_read	= seq_read,
+	.proc_write	= NULL,
+	.proc_lseek	= seq_lseek,
+	.proc_release	= single_release,
+};
+
+static int wpd_evt_read_proc(struct seq_file *m, void *v)
+{
+	struct net_device *dev = m->private;
+	struct rtl8169_private *tp = netdev_priv(dev);
+	u32 tmp;
+
+	rtl_lock_work(tp);
+	/* check if wol_own and wpd_en are both set */
+	tmp = rtl_ocp_read(tp, 0xC0C2);
+	if ((tmp & BIT(4)) == 0 || (tmp & BIT(0)) == 0) {
+		seq_puts(m, "\nNo WPD event\n");
+		rtl_unlock_work(tp);
+		return 0;
+	}
+
+	seq_puts(m, "\nWPD event:\n");
+	tmp = rtl_ocp_read(tp, 0xD23A) & 0x0F01;
+	seq_printf(m, "Type (0: CRC match,  1: magic pkt) = %d\n",
+		   tmp & 0x1);
+	if ((tmp & 0x1) == 0)
+		seq_printf(m, "CRC match ID = %d\n", tmp >> 8);
+	seq_printf(m, "Original packet length = %d\n",
+		   rtl_ocp_read(tp, 0xD23C));
+	seq_printf(m, "Stored packet length = %d\n",
+		   rtl_ocp_read(tp, 0xD23E));
+	rtl_unlock_work(tp);
+
+	seq_putc(m, '\n');
+	return 0;
+}
+
+static int wpd_evt_proc_open(struct inode *inode, struct file *file)
+{
+	struct net_device *dev = proc_get_parent_data(inode);
+
+	return single_open(file, wpd_evt_read_proc, dev);
+}
+
+static const struct proc_ops wpd_evt_proc_ops = {
+	.proc_open	= wpd_evt_proc_open,
+	.proc_read	= seq_read,
+	.proc_write	= NULL,
+	.proc_lseek	= seq_lseek,
+	.proc_release	= single_release,
+};
+
+static int wol_pkt_read_proc(struct seq_file *m, void *v)
+{
+	struct net_device *dev = m->private;
+	struct rtl8169_private *tp = netdev_priv(dev);
+	int i;
+	char wol_pkt[WOL_BUF_LEN];
+	u32 len;
+	u32 tmp;
+	u16 *ptr;
+
+	memset(wol_pkt, 0, WOL_BUF_LEN);
+
+	rtl_lock_work(tp);
+	/* check if wol_own and wpd_en are both set */
+	tmp = rtl_ocp_read(tp, 0xC0C2);
+	if ((tmp & BIT(4)) == 0 || (tmp & BIT(0)) == 0) {
+		rtl_unlock_work(tp);
+		return 0;
+	}
+
+	/* read 128-byte packet buffer */
+	for (i = 0; i < 128; i += 2) {
+		ptr = (u16 *)&wol_pkt[i];
+		*ptr = rtl_ocp_read(tp, 0xD240 + i);
+	}
+
+	/* get stored packet length */
+	len = rtl_ocp_read(tp, 0xD23E);
+	for (i = 0; i < len; i++) {
+		if ((i % 16) == 0)
+			seq_puts(m, "\n");
+		else if ((i % 8) == 0)
+			seq_puts(m, "  ");
+		seq_printf(m, "%02x ", wol_pkt[i]);
+	}
+	rtl_unlock_work(tp);
+
+	seq_putc(m, '\n');
+	return 0;
+}
+
+static int wol_pkt_proc_open(struct inode *inode, struct file *file)
+{
+	struct net_device *dev = proc_get_parent_data(inode);
+
+	return single_open(file, wol_pkt_read_proc, dev);
+}
+
+static const struct proc_ops wol_pkt_proc_ops = {
+	.proc_open	= wol_pkt_proc_open,
+	.proc_read	= seq_read,
+	.proc_write	= NULL,
+	.proc_lseek	= seq_lseek,
+	.proc_release	= single_release,
+};
+
+#endif
+
+static __must_check
+void *r8169soc_read_otp(struct rtl8169_private *tp, const char *name)
+{
+	struct device *dev = &tp->pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct nvmem_cell *cell;
+	unsigned char *buf;
+	size_t buf_size;
+
+	cell = of_nvmem_cell_get(np, name);
+	if (IS_ERR(cell)) {
+		dev_err(dev, "failed to get nvmem cell %s: %ld\n",
+			name, PTR_ERR(cell));
+		return ERR_CAST(cell);
+	}
+
+	buf = nvmem_cell_read(cell, &buf_size);
+	if (IS_ERR(buf))
+		dev_err(dev, "failed to read nvmem cell %s: %ld\n",
+			name, PTR_ERR(buf));
+	nvmem_cell_put(cell);
+	return buf;
+}
+
+/* empty functions */
+static void empty_mdio_lock(struct rtl8169_private *tp)
+{
+	/* no lock */
+}
+
+static void empty_mdio_unlock(struct rtl8169_private *tp)
+{
+	/* no unlock */
+}
+
+static void empty_crc_wakeup(struct rtl8169_private *tp)
+{
+	/* no CRC wakeup */
+}
+
+static void empty_reset_phy_gmac(struct rtl8169_private *tp)
+{
+	pr_info("%s is called\n", __func__);
+}
+
+static void empty_pll_clock_init(struct rtl8169_private *tp)
+{
+	pr_info("%s is called\n", __func__);
+}
+
+static void empty_acp_init(struct rtl8169_private *tp)
+{
+	pr_info("%s is called\n", __func__);
+}
+
+static void empty_mdio_init(struct rtl8169_private *tp)
+{
+	pr_info("%s is called\n", __func__);
+}
+
+static void empty_mac_mcu_patch(struct rtl8169_private *tp)
+{
+	/* no patch */
+}
+
+static void empty_hw_phy_config(struct rtl8169_private *tp)
+{
+	/* no config */
+}
+
+static void empty_eee_set(struct rtl8169_private *tp, bool enable)
+{
+	/* no EEE */
+}
+
+static void empty_led_set(struct rtl8169_private *tp, bool enable)
+{
+	/* no LED */
+}
+
+static void empty_dump_regs(struct seq_file *m, struct rtl8169_private *tp)
+{
+	/* no special registers */
+}
+
+static void empty_dump_var(struct seq_file *m, struct rtl8169_private *tp)
+{
+	/* no special variables */
+}
+
+/* RTD119X */
+static void rtd119x_reset_phy_gmac(struct rtl8169_private *tp)
+{
+	struct clk *clk_etn  = clk_get(&tp->pdev->dev, "etn");
+	struct clk *clk_etn_sys  = clk_get(&tp->pdev->dev, "etn_sys");
+	struct clk *clk_etn_250m = clk_get(&tp->pdev->dev, "etn_250m");
+	struct reset_control *rstc_etn =
+		reset_control_get_exclusive(&tp->pdev->dev, "rst_etn");
+	struct reset_control *rstc_gphy =
+		reset_control_get_exclusive(&tp->pdev->dev, "gphy");
+	struct reset_control *rstc_gmac =
+		reset_control_get_exclusive(&tp->pdev->dev, "gmac");
+
+	/* pre-init clk */
+	clk_prepare_enable(clk_etn);
+	clk_prepare_enable(clk_etn_sys);
+	clk_prepare_enable(clk_etn_250m);
+
+	/* disable clk and hold reset bits */
+	clk_disable_unprepare(clk_etn_sys);
+	clk_disable_unprepare(clk_etn_250m);
+	clk_disable_unprepare(clk_etn);
+	reset_control_assert(rstc_gphy);
+	reset_control_assert(rstc_gmac);
+	reset_control_assert(rstc_etn);
+
+	/* release resource */
+	reset_control_put(rstc_etn);
+	reset_control_put(rstc_gphy);
+	reset_control_put(rstc_gmac);
+}
+
+static void rtd119x_pll_clock_init(struct rtl8169_private *tp)
+{
+	struct clk *clk_etn  = clk_get(&tp->pdev->dev, "etn");
+	struct clk *clk_etn_sys  = clk_get(&tp->pdev->dev, "etn_sys");
+	struct clk *clk_etn_250m = clk_get(&tp->pdev->dev, "etn_250m");
+	struct reset_control *rstc_etn =
+		reset_control_get_exclusive(&tp->pdev->dev, "rst_etn");
+	struct reset_control *rstc_gphy =
+		reset_control_get_exclusive(&tp->pdev->dev, "gphy");
+	struct reset_control *rstc_gmac =
+		reset_control_get_exclusive(&tp->pdev->dev, "gmac");
+
+	/* enable clk bits and release reset bits */
+	clk_prepare_enable(clk_etn);
+	clk_prepare_enable(clk_etn_sys);
+	clk_prepare_enable(clk_etn_250m);
+	reset_control_deassert(rstc_etn);
+	reset_control_deassert(rstc_gphy);
+	reset_control_deassert(rstc_gmac);
+
+	mdelay(10);		/* wait 10ms for GMAC uC to be stable */
+
+	/* release resource */
+	reset_control_put(rstc_etn);
+	reset_control_put(rstc_gphy);
+	reset_control_put(rstc_gmac);
+}
+
+static void rtd119x_mdio_init(struct rtl8169_private *tp)
+{
+}
+
+static void rtd119x_mac_mcu_patch(struct rtl8169_private *tp)
+{
+	void __iomem *ioaddr = tp->mmio_addr;
+	const struct soc_device_attribute rtk_soc_rtd119x_a00[] = {
+		{
+			.family = "Realtek Phoenix",
+			.revision = "A00",
+		},
+		{
+		/* empty */
+		}
+	};
+
+	if (soc_device_match(rtk_soc_rtd119x_a00))
+		return;
+
+	/* Fix ALDPS */
+	RTL_W32(OCPDR, 0xFE140000);
+	RTL_W32(OCPDR, 0xFE150000);
+	RTL_W32(OCPDR, 0xFE160000);
+	RTL_W32(OCPDR, 0xFE170000);
+	RTL_W32(OCPDR, 0xFE180000);
+	RTL_W32(OCPDR, 0xFE190000);
+	RTL_W32(OCPDR, 0xFE1A0000);
+	RTL_W32(OCPDR, 0xFE1B0000);
+	mdelay(3);
+	RTL_W32(OCPDR, 0xFE130000);
+
+	RTL_W32(OCPDR, 0xFC00E008);
+	RTL_W32(OCPDR, 0xFC01E051);
+	RTL_W32(OCPDR, 0xFC02E059);
+	RTL_W32(OCPDR, 0xFC03E05B);
+	RTL_W32(OCPDR, 0xFC04E05D);
+	RTL_W32(OCPDR, 0xFC05E05F);
+	RTL_W32(OCPDR, 0xFC06E061);
+	RTL_W32(OCPDR, 0xFC07E063);
+	RTL_W32(OCPDR, 0xFC08C422);
+	RTL_W32(OCPDR, 0xFC097380);
+	RTL_W32(OCPDR, 0xFC0A49B7);
+	RTL_W32(OCPDR, 0xFC0BF003);
+	RTL_W32(OCPDR, 0xFC0C1D02);
+	RTL_W32(OCPDR, 0xFC0D8D80);
+	RTL_W32(OCPDR, 0xFC0EC51D);
+	RTL_W32(OCPDR, 0xFC0F73A0);
+	RTL_W32(OCPDR, 0xFC101300);
+	RTL_W32(OCPDR, 0xFC11F104);
+	RTL_W32(OCPDR, 0xFC1273A2);
+	RTL_W32(OCPDR, 0xFC131300);
+	RTL_W32(OCPDR, 0xFC14F010);
+	RTL_W32(OCPDR, 0xFC15C517);
+	RTL_W32(OCPDR, 0xFC1676A0);
+	RTL_W32(OCPDR, 0xFC1774A2);
+	RTL_W32(OCPDR, 0xFC180601);
+	RTL_W32(OCPDR, 0xFC193720);
+	RTL_W32(OCPDR, 0xFC1A9EA0);
+	RTL_W32(OCPDR, 0xFC1B9CA2);
+	RTL_W32(OCPDR, 0xFC1CC50F);
+	RTL_W32(OCPDR, 0xFC1D73A2);
+	RTL_W32(OCPDR, 0xFC1E4023);
+	RTL_W32(OCPDR, 0xFC1FF813);
+	RTL_W32(OCPDR, 0xFC20F304);
+	RTL_W32(OCPDR, 0xFC2173A0);
+	RTL_W32(OCPDR, 0xFC224033);
+	RTL_W32(OCPDR, 0xFC23F80F);
+	RTL_W32(OCPDR, 0xFC24C206);
+	RTL_W32(OCPDR, 0xFC257340);
+	RTL_W32(OCPDR, 0xFC2649B7);
+	RTL_W32(OCPDR, 0xFC27F013);
+	RTL_W32(OCPDR, 0xFC28C207);
+	RTL_W32(OCPDR, 0xFC29BA00);
+	RTL_W32(OCPDR, 0xFC2AC0BC);
+	RTL_W32(OCPDR, 0xFC2BD2C8);
+	RTL_W32(OCPDR, 0xFC2CD2CC);
+	RTL_W32(OCPDR, 0xFC2DC0C4);
+	RTL_W32(OCPDR, 0xFC2ED2E4);
+	RTL_W32(OCPDR, 0xFC2F100A);
+	RTL_W32(OCPDR, 0xFC30104C);
+	RTL_W32(OCPDR, 0xFC310C7E);
+	RTL_W32(OCPDR, 0xFC321D02);
+	RTL_W32(OCPDR, 0xFC33C6F7);
+	RTL_W32(OCPDR, 0xFC348DC0);
+	RTL_W32(OCPDR, 0xFC351C01);
+	RTL_W32(OCPDR, 0xFC36C5F7);
+	RTL_W32(OCPDR, 0xFC378CA1);
+	RTL_W32(OCPDR, 0xFC38C6F8);
+	RTL_W32(OCPDR, 0xFC39BE00);
+	RTL_W32(OCPDR, 0xFC3AC5F4);
+	RTL_W32(OCPDR, 0xFC3B74A0);
+	RTL_W32(OCPDR, 0xFC3C49C0);
+	RTL_W32(OCPDR, 0xFC3DF010);
+	RTL_W32(OCPDR, 0xFC3E74A2);
+	RTL_W32(OCPDR, 0xFC3F76A4);
+	RTL_W32(OCPDR, 0xFC404034);
+	RTL_W32(OCPDR, 0xFC41F804);
+	RTL_W32(OCPDR, 0xFC420601);
+	RTL_W32(OCPDR, 0xFC439EA4);
+	RTL_W32(OCPDR, 0xFC44E009);
+	RTL_W32(OCPDR, 0xFC451D02);
+	RTL_W32(OCPDR, 0xFC46C4E4);
+	RTL_W32(OCPDR, 0xFC478D80);
+	RTL_W32(OCPDR, 0xFC48C5E5);
+	RTL_W32(OCPDR, 0xFC4964A1);
+	RTL_W32(OCPDR, 0xFC4A4845);
+	RTL_W32(OCPDR, 0xFC4B8CA1);
+	RTL_W32(OCPDR, 0xFC4CE7EC);
+	RTL_W32(OCPDR, 0xFC4D1C20);
+	RTL_W32(OCPDR, 0xFC4EC5DC);
+	RTL_W32(OCPDR, 0xFC4F8CA1);
+	RTL_W32(OCPDR, 0xFC50C2E1);
+	RTL_W32(OCPDR, 0xFC51BA00);
+	RTL_W32(OCPDR, 0xFC521D02);
+	RTL_W32(OCPDR, 0xFC53C606);
+	RTL_W32(OCPDR, 0xFC548DC0);
+	RTL_W32(OCPDR, 0xFC551D20);
+	RTL_W32(OCPDR, 0xFC568DC0);
+	RTL_W32(OCPDR, 0xFC57C603);
+	RTL_W32(OCPDR, 0xFC58BE00);
+	RTL_W32(OCPDR, 0xFC59C0BC);
+	RTL_W32(OCPDR, 0xFC5A0E22);
+	RTL_W32(OCPDR, 0xFC5BC102);
+	RTL_W32(OCPDR, 0xFC5CB900);
+	RTL_W32(OCPDR, 0xFC5D02A2);
+	RTL_W32(OCPDR, 0xFC5EC602);
+	RTL_W32(OCPDR, 0xFC5FBE00);
+	RTL_W32(OCPDR, 0xFC600000);
+	RTL_W32(OCPDR, 0xFC61C602);
+	RTL_W32(OCPDR, 0xFC62BE00);
+	RTL_W32(OCPDR, 0xFC630000);
+	RTL_W32(OCPDR, 0xFC64C602);
+	RTL_W32(OCPDR, 0xFC65BE00);
+	RTL_W32(OCPDR, 0xFC660000);
+	RTL_W32(OCPDR, 0xFC67C602);
+	RTL_W32(OCPDR, 0xFC68BE00);
+	RTL_W32(OCPDR, 0xFC690000);
+	RTL_W32(OCPDR, 0xFC6AC602);
+	RTL_W32(OCPDR, 0xFC6BBE00);
+	RTL_W32(OCPDR, 0xFC6C0000);
+
+	RTL_W32(OCPDR, 0xFE138000);
+	RTL_W32(OCPDR, 0xFE140FD1);
+	RTL_W32(OCPDR, 0xFE150D21);
+	RTL_W32(OCPDR, 0xFE16029D);
+
+	/* MDC/MDIO clock speedup */
+	RTL_W32(OCPDR, 0xEf080040);
+}
+
+static void rtd119x_hw_phy_config(struct rtl8169_private *tp)
+{
+	int revision = REVISION_NONE;
+	const struct soc_device_attribute *soc;
+	const struct soc_device_attribute rtk_soc_rtd119x[] = {
+		{
+			.family = "Realtek Phoenix",
+			.revision = "A00",
+			.data = (void *)REVISION_A00,
+		},
+		{
+			.family = "Realtek Phoenix",
+			.revision = "A01",
+			.data = (void *)REVISION_A01,
+		},
+		{
+		/* empty */
+		}
+	};
+
+	soc = soc_device_match(rtk_soc_rtd119x);
+	if (soc)
+		revision = (uintptr_t)soc->data;
+
+	switch (revision) {
+	case REVISION_A00:
+		/* disable green */
+		rtl_writephy(tp, 0x1f, 0x0A43);
+		rtl_writephy(tp, 0x1b, 0x8011);
+		rtl_writephy(tp, 0x1c, 0x1737);
+
+		/* write abiq /ldvbias */
+		rtl_writephy(tp, 0x1f, 0x0BCC);
+		rtl_writephy(tp, 0x11, 0x4444);
+
+		/* R/RC auto k */
+		/* default ff08  disable auto k RC/R */
+		rtl_writephy(tp, 0x1f, 0x0A43);
+		rtl_writephy(tp, 0x1b, 0x8013);
+		rtl_writephy(tp, 0x1c, 0x0F08);
+
+		/* force tapbin = 4 */
+		rtl_writephy(tp, 0x1f, 0x0BCE);
+		rtl_writephy(tp, 0x10, 0x4444);
+
+		rtl_writephy(tp, 0x1f, 0x0BCD);
+		rtl_writephy(tp, 0x17, 0x8888);		/* tx rc */
+		rtl_writephy(tp, 0x16, 0x9999);		/* rx rc */
+
+		/* increase sd thd */
+		/* master sd  : 0x1e00 */
+		rtl_writephy(tp, 0x1f, 0x0A43);
+		rtl_writephy(tp, 0x1b, 0x8101);
+		rtl_writephy(tp, 0x1c, 0x3E00);
+
+		/* default sd  : 0x0e00 */
+		rtl_writephy(tp, 0x1f, 0x0A43);
+		rtl_writephy(tp, 0x1b, 0x80E2);
+		rtl_writephy(tp, 0x1c, 0x3E00);
+
+		/* slave sd  : 0x0e00 */
+		rtl_writephy(tp, 0x1f, 0x0A43);
+		rtl_writephy(tp, 0x1b, 0x8120);
+		rtl_writephy(tp, 0x1c, 0x3E00);
+		break;
+	case REVISION_A01:
+		/* GDAC updown */
+		rtl_writephy(tp, 0x1f, 0x0BCC);
+		rtl_writephy(tp, 0x12, 0x00BE);
+
+		/* R_RC */
+		/* fbfe [15:8] tapbin tx -3, [7:0] tapbin rx -2 */
+		rtl_writephy(tp, 0x1f, 0x0A43);
+		rtl_writephy(tp, 0x1b, 0x81DE);
+		rtl_writephy(tp, 0x1c, 0xFDFE);
+
+		rtl_writephy(tp, 0x1b, 0x81E0);
+		rtl_writephy(tp, 0x1c, 0xFDFF);		/* [15:8] rlen  -3 */
+
+		rtl_writephy(tp, 0x1b, 0x81E2);
+		/* [15:8] rlen_100 +4, -3+4=1 */
+		rtl_writephy(tp, 0x1c, 0x0400);
+		rtl_writephy(tp, 0x1b, 0x80d3);
+		/* fnet cable length constant 0aa4 */
+		rtl_writephy(tp, 0x1c, 0x04a4);
+
+		rtl_writephy(tp, 0x1b, 0x8111);
+		/* slave cable length constant fa7f */
+		rtl_writephy(tp, 0x1c, 0x0a7f);
+
+		rtl_writephy(tp, 0x1b, 0x810d);
+		/* slave const dagc 0606 */
+		rtl_writephy(tp, 0x1c, 0x5604);
+
+		rtl_writephy(tp, 0x1b, 0x80f4);
+		/* master cable length delta 3df7 */
+		rtl_writephy(tp, 0x1c, 0x5df7);
+
+		rtl_writephy(tp, 0x1b, 0x80f2);
+		/* master cable length constant fa8f */
+		rtl_writephy(tp, 0x1c, 0x0a8f);
+
+		rtl_writephy(tp, 0x1b, 0x80f6);
+		/* master delta dagc 63ca */
+		rtl_writephy(tp, 0x1c, 0x54ca);
+
+		rtl_writephy(tp, 0x1b, 0x80ec);
+		/* master aagc 146c */
+		rtl_writephy(tp, 0x1c, 0x007c);
+
+		/* disable ALDPS interrupt */
+		rtl_writephy(tp, 0x1f, 0x0a42);
+		rtl_writephy(tp, 0x12, rtl_readphy(tp, 0x12) & ~BIT(9));
+
+		/* enable ALDPS */
+		rtl_writephy(tp, 0x1f, 0x0a43);
+		rtl_writephy(tp, 0x10, rtl_readphy(tp, 0x10) | BIT(2));
+	}
+}
+
+static void rtd119x_eee_set(struct rtl8169_private *tp, bool enable)
+{
+}
+
+static void rtd119x_led_set(struct rtl8169_private *tp, bool enable)
+{
+	struct reset_control *rstc_etn =
+		reset_control_get_exclusive(&tp->pdev->dev, "rst_etn");
+	struct reset_control *rstc_gphy =
+		reset_control_get_exclusive(&tp->pdev->dev, "gphy");
+	struct reset_control *rstc_gmac =
+		reset_control_get_exclusive(&tp->pdev->dev, "gmac");
+	u32 val;
+
+	if (enable) {
+		/* hold reset bits */
+		reset_control_assert(rstc_gphy);
+		reset_control_assert(rstc_gmac);
+		reset_control_assert(rstc_etn);
+
+		/* release reset bits */
+		reset_control_deassert(rstc_etn);
+		reset_control_deassert(rstc_gphy);
+		reset_control_deassert(rstc_gmac);
+
+		msleep(300);
+
+		val = readl(tp->mmio_clkaddr + 0x310) & ~0xF0000000;
+		writel(val | 0x50000000, tp->mmio_clkaddr + 0x310);
+	} else {
+		val = readl(tp->mmio_clkaddr + 0x310) & ~0xF0000000;
+		writel(val, tp->mmio_clkaddr + 0x310);
+	}
+
+	/* release resource */
+	reset_control_put(rstc_etn);
+	reset_control_put(rstc_gphy);
+	reset_control_put(rstc_gmac);
+}
+
+static void rtd119x_dump_regs(struct seq_file *m, struct rtl8169_private *tp)
+{
+}
+
+static void rtd119x_dump_var(struct seq_file *m, struct rtl8169_private *tp)
+{
+}
+
+/******************* END of RTD119X ****************************/
+
+/* RTD129X */
+static void rtd129x_mdio_lock(struct rtl8169_private *tp)
+{
+	/* disable interrupt from PHY to MCU */
+	rtl_ocp_write(tp, 0xFC1E,
+		      rtl_ocp_read(tp, 0xFC1E) & ~(BIT(1) | BIT(11) | BIT(12)));
+}
+
+static void rtd129x_mdio_unlock(struct rtl8169_private *tp)
+{
+	u32 tmp;
+
+	/* enable interrupt from PHY to MCU */
+	tmp = rtl_ocp_read(tp, 0xFC1E);
+	if (tp->output_mode == OUTPUT_RGMII_TO_MAC)
+		tmp |= (BIT(11) | BIT(12)); /* ignore BIT(1):mac_intr*/
+	else
+		tmp |= (BIT(1) | BIT(11) | BIT(12));
+	rtl_ocp_write(tp, 0xFC1E, tmp);
+}
+
+static void rtd129x_reset_phy_gmac(struct rtl8169_private *tp)
+{
+	struct clk *clk_etn_sys  = clk_get(&tp->pdev->dev, "etn_sys");
+	struct clk *clk_etn_250m = clk_get(&tp->pdev->dev, "etn_250m");
+	struct reset_control *rstc_gphy =
+		reset_control_get_exclusive(&tp->pdev->dev, "gphy");
+	struct reset_control *rstc_gmac =
+		reset_control_get_exclusive(&tp->pdev->dev, "gmac");
+
+	/* pre-init clk bits */
+	clk_prepare_enable(clk_etn_sys);
+	clk_prepare_enable(clk_etn_250m);
+
+	/* disable clk bits and hold reset bits */
+	clk_disable_unprepare(clk_etn_sys);
+	clk_disable_unprepare(clk_etn_250m);
+	reset_control_assert(rstc_gphy);
+	reset_control_assert(rstc_gmac);
+
+	/* release resource */
+	reset_control_put(rstc_gphy);
+	reset_control_put(rstc_gmac);
+}
+
+static void rtd129x_pll_clock_init(struct rtl8169_private *tp)
+{
+	struct clk *clk_etn_sys  = clk_get(&tp->pdev->dev, "etn_sys");
+	struct clk *clk_etn_250m = clk_get(&tp->pdev->dev, "etn_250m");
+	struct reset_control *rstc_gphy =
+		reset_control_get_exclusive(&tp->pdev->dev, "gphy");
+	struct reset_control *rstc_gmac =
+		reset_control_get_exclusive(&tp->pdev->dev, "gmac");
+	u32 tmp;
+
+	/* 1. reg_0x98007088[10] = 1 */
+	/* ISO spec, reset bit of gphy */
+	reset_control_deassert(rstc_gphy);
+
+	/* 2. CPU software waiting 200uS */
+	usleep_range(200, 210);
+
+	/* 3. reg_0x98007060[1] = 0 */
+	/* ISO spec, Ethernet Boot up bypass gphy ready mode */
+	tmp = readl(tp->mmio_clkaddr + 0x60);
+	tmp &= ~BIT(1);
+	writel(tmp, tp->mmio_clkaddr + 0x60);
+
+	/* 4. reg_0x98007fc0[0] = 0 */
+	/* ISO spec, Ethernet Boot up disable dbus clock gating */
+	tmp = readl(tp->mmio_clkaddr + 0xfc0);
+	tmp &= ~BIT(0);
+	writel(tmp, tp->mmio_clkaddr + 0xfc0);
+
+	/* 5. CPU software waiting 200uS */
+	usleep_range(200, 210);
+
+	/* 6. reg_0x9800708c[12:11] = 11 */
+	/* ISO spec, clock enable bit for etn clock & etn 250MHz */
+	clk_prepare_enable(clk_etn_sys);
+	clk_prepare_enable(clk_etn_250m);
+
+	/* 7. reg_0x9800708c[12:11] = 00 */
+	/* ISO spec, clock enable bit for etn clock & etn 250MHz */
+	clk_disable_unprepare(clk_etn_sys);
+	clk_disable_unprepare(clk_etn_250m);
+
+	/* 8. reg_0x98007088[9] = 1 */
+	/* ISO spec, reset bit of gmac */
+	reset_control_deassert(rstc_gmac);
+
+	/* 9. reg_0x9800708c[12:11] = 11 */
+	/* ISO spec, clock enable bit for etn clock & etn 250MHz */
+	clk_prepare_enable(clk_etn_sys);
+	clk_prepare_enable(clk_etn_250m);
+
+	msleep(100);
+
+	/* release resource */
+	reset_control_put(rstc_gphy);
+	reset_control_put(rstc_gmac);
+}
+
+static void rtd129x_mdio_init(struct rtl8169_private *tp)
+{
+}
+
+static void rtd129x_mac_setup(struct rtl8169_private *tp)
+{
+	unsigned int tmp;
+	void __iomem *rgmii0_addr, *tmp_addr;
+
+	if (tp->output_mode == OUTPUT_EMBEDDED_PHY) {
+		tmp = rtl_ocp_read(tp, 0xea34) &
+			~(BIT(0) | BIT(1));
+		tmp |= BIT(1); /* MII */
+		rtl_ocp_write(tp, 0xea34, tmp);
+	} else { /* RGMII */
+		if (tp->output_mode == OUTPUT_RGMII_TO_PHY) {
+			/* # ETN spec, MDC freq=2.5MHz */
+			tmp = rtl_ocp_read(tp, 0xde30);
+			rtl_ocp_write(tp, 0xde30, tmp & ~(BIT(6) | BIT(7)));
+			/* # ETN spec, set external PHY addr */
+			tmp = rtl_ocp_read(tp, 0xde24) & ~(0x1F);
+			rtl_ocp_write(tp, 0xde24,
+				      tmp | (tp->ext_phy_id & 0x1F));
+		}
+
+		tmp = rtl_ocp_read(tp, 0xea34) & ~(BIT(0) | BIT(1));
+		tmp |= BIT(0); /* RGMII */
+
+		if (tp->output_mode == OUTPUT_RGMII_TO_MAC) {
+			tmp &= ~(BIT(3) | BIT(4));
+			tmp |= BIT(4); /* speed: 1G */
+			tmp |= BIT(2); /* full duplex */
+		}
+		if (tp->rgmii.rx_delay == RTD129X_RGMII_DELAY_0NS)
+			tmp &= ~BIT(6);
+		else
+			tmp |= BIT(6);
+
+		if (tp->rgmii.tx_delay == RTD129X_RGMII_DELAY_0NS)
+			tmp &= ~BIT(7);
+		else
+			tmp |= BIT(7);
+
+		rtl_ocp_write(tp, 0xea34, tmp);
+
+		/* adjust RGMII voltage */
+		rgmii0_addr = ioremap(RTD129X_RGMII0_PAD_CTRL_ADDR, 12);
+		switch (tp->rgmii.voltage) {
+		case RTD129X_VOLTAGE_1_DOT_8V:
+			writel(0, rgmii0_addr);
+			writel(0x44444444, rgmii0_addr + 4);
+			writel(0x24444444, rgmii0_addr + 8);
+			break;
+		case RTD129X_VOLTAGE_2_DOT_5V:
+			writel(0, rgmii0_addr);
+			writel(0x44444444, rgmii0_addr + 4);
+			writel(0x64444444, rgmii0_addr + 8);
+			break;
+		case RTD129X_VOLTAGE_3_DOT_3V:
+			writel(0x3F, rgmii0_addr);
+			writel(0, rgmii0_addr + 4);
+			writel(0xA4000000, rgmii0_addr + 8);
+		}
+		iounmap(rgmii0_addr);
+
+		/* switch RGMII/MDIO to GMAC */
+		tmp_addr = ioremap(RTD129X_ISO_RGMII_MDIO_TO_GMAC, 4);
+		writel((readl(tmp_addr) | BIT(1)), tmp_addr);
+		iounmap(tmp_addr);
+
+		if (tp->output_mode == OUTPUT_RGMII_TO_MAC) {
+			/* force GMAC link up */
+			rtl_ocp_write(tp, 0xde40, 0x30EC);
+			/* ignore mac_intr from PHY */
+			tmp = rtl_ocp_read(tp, 0xfc1e) & ~BIT(1);
+			rtl_ocp_write(tp, 0xfc1e, tmp);
+		}
+	}
+}
+
+static void rtd129x_mac_mcu_patch(struct rtl8169_private *tp)
+{
+	const struct soc_device_attribute rtk_soc_rtd129x_bxx[] = {
+		{
+			.family = "Realtek Kylin",
+			.revision = "B0*",
+		},
+		{
+		/* empty */
+		}
+	};
+
+	/* disable break point */
+	rtl_ocp_write(tp, 0xfc28, 0);
+	rtl_ocp_write(tp, 0xfc2a, 0);
+	rtl_ocp_write(tp, 0xfc2c, 0);
+	rtl_ocp_write(tp, 0xfc2e, 0);
+	rtl_ocp_write(tp, 0xfc30, 0);
+	rtl_ocp_write(tp, 0xfc32, 0);
+	rtl_ocp_write(tp, 0xfc34, 0);
+	rtl_ocp_write(tp, 0xfc36, 0);
+	mdelay(3);
+
+	/* disable base address */
+	rtl_ocp_write(tp, 0xfc26, 0);
+
+	/* patch code */
+	rtl_ocp_write(tp, 0xf800, 0xE008);
+	rtl_ocp_write(tp, 0xf802, 0xE012);
+	rtl_ocp_write(tp, 0xf804, 0xE044);
+	rtl_ocp_write(tp, 0xf806, 0xE046);
+	rtl_ocp_write(tp, 0xf808, 0xE048);
+	rtl_ocp_write(tp, 0xf80a, 0xE04A);
+	rtl_ocp_write(tp, 0xf80c, 0xE04C);
+	rtl_ocp_write(tp, 0xf80e, 0xE04E);
+	rtl_ocp_write(tp, 0xf810, 0x44E3);
+	rtl_ocp_write(tp, 0xf812, 0xC708);
+	rtl_ocp_write(tp, 0xf814, 0x75E0);
+	rtl_ocp_write(tp, 0xf816, 0x485D);
+	rtl_ocp_write(tp, 0xf818, 0x9DE0);
+	rtl_ocp_write(tp, 0xf81a, 0xC705);
+	rtl_ocp_write(tp, 0xf81c, 0xC502);
+	rtl_ocp_write(tp, 0xf81e, 0xBD00);
+	rtl_ocp_write(tp, 0xf820, 0x01EE);
+	rtl_ocp_write(tp, 0xf822, 0xE85A);
+	rtl_ocp_write(tp, 0xf824, 0xE000);
+	rtl_ocp_write(tp, 0xf826, 0xC72D);
+	rtl_ocp_write(tp, 0xf828, 0x76E0);
+	rtl_ocp_write(tp, 0xf82a, 0x49ED);
+	rtl_ocp_write(tp, 0xf82c, 0xF026);
+	rtl_ocp_write(tp, 0xf82e, 0xC02A);
+	rtl_ocp_write(tp, 0xf830, 0x7400);
+	rtl_ocp_write(tp, 0xf832, 0xC526);
+	rtl_ocp_write(tp, 0xf834, 0xC228);
+	rtl_ocp_write(tp, 0xf836, 0x9AA0);
+	rtl_ocp_write(tp, 0xf838, 0x73A2);
+	rtl_ocp_write(tp, 0xf83a, 0x49BE);
+	rtl_ocp_write(tp, 0xf83c, 0xF11E);
+	rtl_ocp_write(tp, 0xf83e, 0xC324);
+	rtl_ocp_write(tp, 0xf840, 0x9BA2);
+	rtl_ocp_write(tp, 0xf842, 0x73A2);
+	rtl_ocp_write(tp, 0xf844, 0x49BE);
+	rtl_ocp_write(tp, 0xf846, 0xF0FE);
+	rtl_ocp_write(tp, 0xf848, 0x73A2);
+	rtl_ocp_write(tp, 0xf84a, 0x49BE);
+	rtl_ocp_write(tp, 0xf84c, 0xF1FE);
+	rtl_ocp_write(tp, 0xf84e, 0x1A02);
+	rtl_ocp_write(tp, 0xf850, 0x49C9);
+	rtl_ocp_write(tp, 0xf852, 0xF003);
+	rtl_ocp_write(tp, 0xf854, 0x4821);
+	rtl_ocp_write(tp, 0xf856, 0xE002);
+	rtl_ocp_write(tp, 0xf858, 0x48A1);
+	rtl_ocp_write(tp, 0xf85a, 0x73A2);
+	rtl_ocp_write(tp, 0xf85c, 0x49BE);
+	rtl_ocp_write(tp, 0xf85e, 0xF10D);
+	rtl_ocp_write(tp, 0xf860, 0xC313);
+	rtl_ocp_write(tp, 0xf862, 0x9AA0);
+	rtl_ocp_write(tp, 0xf864, 0xC312);
+	rtl_ocp_write(tp, 0xf866, 0x9BA2);
+	rtl_ocp_write(tp, 0xf868, 0x73A2);
+	rtl_ocp_write(tp, 0xf86a, 0x49BE);
+	rtl_ocp_write(tp, 0xf86c, 0xF0FE);
+	rtl_ocp_write(tp, 0xf86e, 0x73A2);
+	rtl_ocp_write(tp, 0xf870, 0x49BE);
+	rtl_ocp_write(tp, 0xf872, 0xF1FE);
+	rtl_ocp_write(tp, 0xf874, 0x48ED);
+	rtl_ocp_write(tp, 0xf876, 0x9EE0);
+	rtl_ocp_write(tp, 0xf878, 0xC602);
+	rtl_ocp_write(tp, 0xf87a, 0xBE00);
+	rtl_ocp_write(tp, 0xf87c, 0x0532);
+	rtl_ocp_write(tp, 0xf87e, 0xDE00);
+	rtl_ocp_write(tp, 0xf880, 0xE85A);
+	rtl_ocp_write(tp, 0xf882, 0xE086);
+	rtl_ocp_write(tp, 0xf884, 0x0A44);
+	rtl_ocp_write(tp, 0xf886, 0x801F);
+	rtl_ocp_write(tp, 0xf888, 0x8015);
+	rtl_ocp_write(tp, 0xf88a, 0x0015);
+	rtl_ocp_write(tp, 0xf88c, 0xC602);
+	rtl_ocp_write(tp, 0xf88e, 0xBE00);
+	rtl_ocp_write(tp, 0xf890, 0x0000);
+	rtl_ocp_write(tp, 0xf892, 0xC602);
+	rtl_ocp_write(tp, 0xf894, 0xBE00);
+	rtl_ocp_write(tp, 0xf896, 0x0000);
+	rtl_ocp_write(tp, 0xf898, 0xC602);
+	rtl_ocp_write(tp, 0xf89a, 0xBE00);
+	rtl_ocp_write(tp, 0xf89c, 0x0000);
+	rtl_ocp_write(tp, 0xf89e, 0xC602);
+	rtl_ocp_write(tp, 0xf8a0, 0xBE00);
+	rtl_ocp_write(tp, 0xf8a2, 0x0000);
+	rtl_ocp_write(tp, 0xf8a4, 0xC602);
+	rtl_ocp_write(tp, 0xf8a6, 0xBE00);
+	rtl_ocp_write(tp, 0xf8a8, 0x0000);
+	rtl_ocp_write(tp, 0xf8aa, 0xC602);
+	rtl_ocp_write(tp, 0xf8ac, 0xBE00);
+	rtl_ocp_write(tp, 0xf8ae, 0x0000);
+
+	/* enable base address */
+	rtl_ocp_write(tp, 0xfc26, 0x8000);
+
+	/* enable breakpoint */
+	rtl_ocp_write(tp, 0xfc28, 0x01ED);
+	rtl_ocp_write(tp, 0xfc2a, 0x0531);
+
+	if (tp->eee_enable) {
+		/* EEE MAC mode */
+		rtl_ocp_write(tp, 0xe040,
+			      rtl_ocp_read(tp, 0xe040) | (BIT(1) | BIT(0)));
+		/* EEE+ MAC mode */
+		rtl_ocp_write(tp, 0xe080, rtl_ocp_read(tp, 0xe080) | BIT(1));
+		tp->chip->mdio_lock(tp);
+		/* EEE of 10Mbps */
+		r8169_mdio_write(tp, 31, 0x0a43);
+		r8169_mdio_write(tp, 25,
+				 r8169_mdio_read(tp, 25) | BIT(4));
+		tp->chip->mdio_unlock(tp);
+	}
+
+	if (soc_device_match(rtk_soc_rtd129x_bxx))
+		rtd129x_mac_setup(tp);
+}
+
+static void rtd129x_hw_phy_config(struct rtl8169_private *tp)
+{
+	tp->chip->mdio_lock(tp);
+	/* enable ALDPS mode */
+	r8169_mdio_write(tp, 31, 0x0a43);
+	r8169_mdio_write(tp, 24,
+			 (r8169_mdio_read(tp, 24) &
+			 ~(BIT(12) | BIT(1) | BIT(0))) | BIT(2));
+	tp->chip->mdio_unlock(tp);
+}
+
+static void rtd129x_eee_set(struct rtl8169_private *tp, bool enable)
+{
+	if (enable) {
+		tp->chip->mdio_lock(tp);
+		r8169_mmd_write(tp, 0x7, 0x3c, 0x6);
+		tp->chip->mdio_unlock(tp);
+
+		/* turn on EEE */
+		rtl_ocp_write(tp, 0xe040,
+			      rtl_ocp_read(tp, 0xe040) | BIT(1) | BIT(0));
+		/* turn on EEE+ */
+		rtl_ocp_write(tp, 0xe080, rtl_ocp_read(tp, 0xe080) | BIT(1));
+	} else {
+		tp->chip->mdio_lock(tp);
+		r8169_mmd_write(tp, 0x7, 0x3c, 0);
+		tp->chip->mdio_unlock(tp);
+
+		/* turn off EEE */
+		rtl_ocp_write(tp, 0xe040,
+			      rtl_ocp_read(tp, 0xe040) & ~(BIT(1) | BIT(0)));
+		/* turn off EEE+ */
+		rtl_ocp_write(tp, 0xe080, rtl_ocp_read(tp, 0xe080) & ~BIT(1));
+	}
+}
+
+static void rtd129x_led_set(struct rtl8169_private *tp, bool enable)
+{
+	u32 val;
+
+	if (enable) {
+		val = readl(tp->mmio_clkaddr + 0x310) & ~0x3C000000;
+		writel(val | 0x14000000, tp->mmio_clkaddr + 0x310);
+	} else {
+		writel(readl(tp->mmio_clkaddr + 0x310) & ~0x3C000000,
+		       tp->mmio_clkaddr + 0x310);
+	}
+}
+
+static void rtd129x_dump_regs(struct seq_file *m, struct rtl8169_private *tp)
+{
+	seq_printf(m, "ISO_UMSK_ISR\t[0x98007004] = %08x\n",
+		   readl(tp->mmio_clkaddr + ISO_UMSK_ISR));
+	seq_printf(m, "ISO_PWRCUT_ETN\t[0x9800705c] = %08x\n",
+		   readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN));
+	seq_printf(m, "ISO_ETN_TESTIO\t[0x98007060] = %08x\n",
+		   readl(tp->mmio_clkaddr + ISO_ETN_TESTIO));
+	seq_printf(m, "ETN_RESET_CTRL\t[0x98007088] = %08x\n",
+		   readl(tp->mmio_clkaddr + ISO_SOFT_RESET));
+	seq_printf(m, "ETN_CLK_CTRL\t[0x9800708c] = %08x\n",
+		   readl(tp->mmio_clkaddr + ISO_CLOCK_ENABLE));
+}
+
+static void rtd129x_dump_var(struct seq_file *m, struct rtl8169_private *tp)
+{
+	seq_printf(m, "rgmii_voltage\t%d\n", tp->rgmii.voltage);
+	seq_printf(m, "rgmii_tx_delay\t%d\n", tp->rgmii.tx_delay);
+	seq_printf(m, "rgmii_rx_delay\t%d\n", tp->rgmii.rx_delay);
+}
+
+/******************* END of RTD129X ****************************/
+
+/* RTD139X */
+#define MDIO_WAIT_TIMEOUT	100
+static void rtd139x_mdio_lock(struct rtl8169_private *tp)
+{
+	u32 wait_cnt = 0;
+	u32 log_de4e = 0;
+
+	/* disable EEE IMR */
+	rtl_ocp_write(tp, 0xE044,
+		      rtl_ocp_read(tp, 0xE044) &
+		      ~(BIT(3) | BIT(2) | BIT(1) | BIT(0)));
+	/* disable timer 2 */
+	rtl_ocp_write(tp, 0xE404,
+		      rtl_ocp_read(tp, 0xE404) | BIT(9));
+	/* wait MDIO channel is free */
+	log_de4e = BIT(0) & rtl_ocp_read(tp, 0xDE4E);
+	log_de4e = (log_de4e << 1) |
+		(BIT(0) & rtl_ocp_read(tp, 0xDE4E));
+	/* check if 0 for continuous 2 times */
+	while (0 != (((0x1 << 2) - 1) & log_de4e)) {
+		wait_cnt++;
+		udelay(1);
+		log_de4e = (log_de4e << 1) | (BIT(0) &
+			rtl_ocp_read(tp, 0xDE4E));
+		if (wait_cnt > MDIO_WAIT_TIMEOUT)
+			break;
+	}
+	/* enter driver mode */
+	rtl_ocp_write(tp, 0xDE42, rtl_ocp_read(tp, 0xDE42) | BIT(0));
+	if (wait_cnt > MDIO_WAIT_TIMEOUT)
+		pr_err("%s:%d: MDIO lock failed\n", __func__, __LINE__);
+}
+
+static void rtd139x_mdio_unlock(struct rtl8169_private *tp)
+{
+	/* exit driver mode */
+	rtl_ocp_write(tp, 0xDE42, rtl_ocp_read(tp, 0xDE42) & ~BIT(0));
+	/* enable timer 2 */
+	rtl_ocp_write(tp, 0xE404,
+		      rtl_ocp_read(tp, 0xE404) & ~BIT(9));
+	/* enable EEE IMR */
+	rtl_ocp_write(tp, 0xE044,
+		      rtl_ocp_read(tp, 0xE044) | BIT(3) | BIT(2) | BIT(1) |
+				   BIT(0));
+}
+
+static void rtd139x_reset_phy_gmac(struct rtl8169_private *tp)
+{
+	u32 tmp;
+	struct clk *clk_etn_sys  = clk_get(&tp->pdev->dev, "etn_sys");
+	struct clk *clk_etn_250m = clk_get(&tp->pdev->dev, "etn_250m");
+	struct reset_control *rstc_gphy =
+		reset_control_get_exclusive(&tp->pdev->dev, "gphy");
+	struct reset_control *rstc_gmac =
+		reset_control_get_exclusive(&tp->pdev->dev, "gmac");
+	struct clk *clk_en_sds = NULL;
+	struct reset_control *rstc_sds_reg = NULL;
+	struct reset_control *rstc_sds = NULL;
+	struct reset_control *rstc_pcie0_power = NULL;
+	struct reset_control *rstc_pcie0_phy = NULL;
+	struct reset_control *rstc_pcie0_sgmii_mdio = NULL;
+	struct reset_control *rstc_pcie0_phy_mdio = NULL;
+
+	clk_prepare_enable(clk_etn_sys);
+	clk_prepare_enable(clk_etn_250m);
+
+	/* reg_0x9800708c[12:11] = 00 */
+	/* ISO spec, clock enable bit for etn clock & etn 250MHz */
+	clk_disable_unprepare(clk_etn_sys);
+	clk_disable_unprepare(clk_etn_250m);
+
+	/* reg_0x98007088[10:9] = 00 */
+	/* ISO spec, rstn_gphy & rstn_gmac */
+	reset_control_assert(rstc_gphy);
+	reset_control_assert(rstc_gmac);
+
+	/* reg_0x98007060[1] = 1 */
+	/* ISO spec, bypass mode enable */
+	tmp = readl(tp->mmio_clkaddr + ISO_ETN_TESTIO);
+	tmp |= BIT(1);
+	writel(tmp, tp->mmio_clkaddr + ISO_ETN_TESTIO);
+
+	/* reg_0x9800705c = 0x00616703 */
+	/* ISO spec, default value */
+	writel(0x00616703, tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+
+	/* RESET for SGMII if needed */
+	if (tp->output_mode != OUTPUT_EMBEDDED_PHY) {
+		clk_en_sds = clk_get(&tp->pdev->dev, "sds");
+		rstc_sds_reg = reset_control_get_exclusive(&tp->pdev->dev,
+							   "sds_reg");
+		rstc_sds = reset_control_get_exclusive(&tp->pdev->dev, "sds");
+		rstc_pcie0_power =
+			reset_control_get_exclusive(&tp->pdev->dev,
+						    "pcie0_power");
+		rstc_pcie0_phy =
+			reset_control_get_exclusive(&tp->pdev->dev,
+						    "pcie0_phy");
+		rstc_pcie0_sgmii_mdio =
+			reset_control_get_exclusive(&tp->pdev->dev,
+						    "pcie0_sgmii_mdio");
+		rstc_pcie0_phy_mdio =
+			reset_control_get_exclusive(&tp->pdev->dev,
+						    "pcie0_phy_mdio");
+
+		clk_prepare_enable(clk_en_sds);
+
+		/* reg_0x9800000c[7] = 0 */
+		/* CRT spec, clk_en_sds */
+		clk_disable_unprepare(clk_en_sds);
+
+		/* reg_0x98000000[4:3] = 00 */
+		/* CRT spec, rstn_sds_reg & rstn_sds */
+		reset_control_assert(rstc_sds);
+		reset_control_assert(rstc_sds_reg);
+
+		/* reg_0x98000004[7] = 0 */
+		/* reg_0x98000004[14] = 0 */
+		/* CRT spec, rstn_pcie0_power & rstn_pcie0_phy */
+		reset_control_assert(rstc_pcie0_power);
+		reset_control_assert(rstc_pcie0_phy);
+
+		/* reg_0x98000050[13] = 0 */
+		/* reg_0x98000050[16] = 0 */
+		/* CRT spec, rstn_pcie0_sgmii_mdio & rstn_pcie0_phy_mdio */
+		reset_control_assert(rstc_pcie0_sgmii_mdio);
+		reset_control_assert(rstc_pcie0_phy_mdio);
+
+		reset_control_put(rstc_sds_reg);
+		reset_control_put(rstc_sds);
+		reset_control_put(rstc_pcie0_power);
+		reset_control_put(rstc_pcie0_phy);
+		reset_control_put(rstc_pcie0_sgmii_mdio);
+		reset_control_put(rstc_pcie0_phy_mdio);
+	}
+
+	mdelay(1);
+
+	/* release resource */
+	reset_control_put(rstc_gphy);
+	reset_control_put(rstc_gmac);
+}
+
+static void rtd139x_acp_init(struct rtl8169_private *tp)
+{
+	u32 tmp;
+
+	/* SBX spec, Select ETN access DDR path. */
+	if (tp->acp_enable) {
+		/* reg_0x9801c20c[6] = 1 */
+		/* SBX spec, Mask ETN_ALL to SB3 DBUS REQ */
+		tmp = readl(tp->mmio_sbxaddr +
+			    RTD139X_SBX_SB3_CHANNEL_REQ_MASK);
+		tmp |= BIT(6);
+		writel(tmp, tp->mmio_sbxaddr +
+		       RTD139X_SBX_SB3_CHANNEL_REQ_MASK);
+
+		pr_info("wait all SB3 access finished...");
+		tmp = 0;
+		while ((BIT(6) & readl(tp->mmio_sbxaddr +
+		       RTD139X_SBX_SB3_CHANNEL_REQ_BUSY)) != 0) {
+			mdelay(1);
+			tmp += 1;
+			if (tmp >= 100) {
+				pr_err("\n wait SB3 access failed (wait %d ms)\n",
+				       tmp);
+				break;
+			}
+		}
+		if (tmp < 100)
+			pr_info("done.\n");
+
+		/* reg_0x9801d100[29] = 0 */
+		/* SCPU wrapper spec, CLKACP division, 0 = div 2, 1 = div 3 */
+		tmp = readl(tp->mmio_sbxaddr + RTD139X_SC_WRAP_CRT_CTRL);
+		tmp &= ~(BIT(29));
+		writel(tmp, tp->mmio_sbxaddr + RTD139X_SC_WRAP_CRT_CTRL);
+
+		/* reg_0x9801d124[1:0] = 00 */
+		/* SCPU wrapper spec, ACP master active, 0 = active */
+		tmp = readl(tp->mmio_sbxaddr + RTD139X_SC_WRAP_INTERFACE_EN);
+		tmp &= ~(BIT(1) | BIT(0));
+		writel(tmp, tp->mmio_sbxaddr + RTD139X_SC_WRAP_INTERFACE_EN);
+
+		/* reg_0x9801d100[30] = 1 */
+		/* SCPU wrapper spec, dy_icg_en_acp */
+		tmp = readl(tp->mmio_sbxaddr + RTD139X_SC_WRAP_CRT_CTRL);
+		tmp |= BIT(30);
+		writel(tmp, tp->mmio_sbxaddr + RTD139X_SC_WRAP_CRT_CTRL);
+
+		/* reg_0x9801d100[21] = 1 */
+		/* SCPU wrapper spec, ACP CLK enable */
+		tmp = readl(tp->mmio_sbxaddr + RTD139X_SC_WRAP_CRT_CTRL);
+		tmp |= BIT(21);
+		writel(tmp, tp->mmio_sbxaddr + RTD139X_SC_WRAP_CRT_CTRL);
+
+		/* reg_0x9801d100[14] = 1 */
+		/* Do not apply reset to ACP port axi3 master */
+		tmp = readl(tp->mmio_sbxaddr + RTD139X_SC_WRAP_CRT_CTRL);
+		tmp |= BIT(14);
+		writel(tmp, tp->mmio_sbxaddr + RTD139X_SC_WRAP_CRT_CTRL);
+
+		/* reg_0x9801d800[3:0] = 0111 */
+		/* reg_0x9801d800[7:4] = 0111 */
+		/* reg_0x9801d800[9:8] = 00 */
+		/* reg_0x9801d800[20:16] = 01100 */
+		/* reg_0x9801d800[28:24] = 01110 */
+		/* Configure control of ACP port */
+		tmp = readl(tp->mmio_sbxaddr + RTD139X_SC_WRAP_ACP_CTRL);
+		tmp &= ~((0x1f << 24) | (0x1f << 16) | (0x1 << 9) |
+			(0xf << 4) | (0xf << 0));
+		tmp |= (0x0e << 24) | (0x0c << 16) | (0x1 << 9) | (0x7 << 4) |
+			(0x7 << 0);
+		writel(tmp, tp->mmio_sbxaddr + RTD139X_SC_WRAP_ACP_CTRL);
+
+		/* reg_0x9801d030[28] = 1 */
+		/* SCPU wrapper spec, dy_icg_en_acp */
+		tmp = readl(tp->mmio_sbxaddr + RTD139X_SC_WRAP_ACP_CRT_CTRL);
+		tmp |= BIT(28);
+		writel(tmp, tp->mmio_sbxaddr + RTD139X_SC_WRAP_ACP_CRT_CTRL);
+
+		/* reg_0x9801d030[16] = 1 */
+		/* SCPU wrapper spec, ACP CLK Enable for acp of scpu_chip_top */
+		tmp = readl(tp->mmio_sbxaddr + RTD139X_SC_WRAP_ACP_CRT_CTRL);
+		tmp |= BIT(16);
+		writel(tmp, tp->mmio_sbxaddr + RTD139X_SC_WRAP_ACP_CRT_CTRL);
+
+		/* reg_0x9801d030[0] = 1 */
+		/* Do not apply reset to ACP port axi3 master */
+		tmp = readl(tp->mmio_sbxaddr + RTD139X_SC_WRAP_ACP_CRT_CTRL);
+		tmp |= BIT(0);
+		writel(tmp, tp->mmio_sbxaddr + RTD139X_SC_WRAP_ACP_CRT_CTRL);
+
+		/* reg_0x9801c814[17] = 1 */
+		/* through ACP to SCPU_ACP */
+		tmp = readl(tp->mmio_sbxaddr + RTD139X_SBX_ACP_MISC_CTRL);
+		tmp |= BIT(17);
+		writel(tmp, tp->mmio_sbxaddr + RTD139X_SBX_ACP_MISC_CTRL);
+
+		pr_info("ARM ACP on\n.");
+	}
+}
+
+static void rtd139x_pll_clock_init(struct rtl8169_private *tp)
+{
+	u32 tmp;
+	struct clk *clk_etn_sys  = clk_get(&tp->pdev->dev, "etn_sys");
+	struct clk *clk_etn_250m = clk_get(&tp->pdev->dev, "etn_250m");
+	struct reset_control *rstc_gphy =
+		reset_control_get_exclusive(&tp->pdev->dev, "gphy");
+	struct reset_control *rstc_gmac =
+		reset_control_get_exclusive(&tp->pdev->dev, "gmac");
+
+	/* reg_0x98007004[27] = 1 */
+	/* ISO spec, ETN_PHY_INTR, clear ETN interrupt for ByPassMode */
+	writel(BIT(27), tp->mmio_clkaddr + ISO_UMSK_ISR);
+
+	/* reg_0x98007088[10] = 1 */
+	/* ISO spec, reset bit of gphy */
+	reset_control_deassert(rstc_gphy);
+
+	mdelay(1);	/* wait 1ms for PHY PLL stable */
+
+	/* In Hercules, EPHY need choose the bypass mode or Non-bypass mode */
+	/* Bypass mode : ETN MAC bypass efuse update flow.
+	 * SW need to take this sequence.
+	 */
+	/* Non-Bypass mode : ETN MAC set efuse update and efuse_rdy setting */
+	/* Default : Bypass mode (0x9800_7060[1] = 1'b1) */
+	if (!tp->bypass_enable) {
+		/* reg_0x98007060[1] = 0 */
+		/* ISO spec, bypass mode disable */
+		tmp = readl(tp->mmio_clkaddr + ISO_ETN_TESTIO);
+		tmp &= ~(BIT(1));
+		writel(tmp, tp->mmio_clkaddr + ISO_ETN_TESTIO);
+	} else {
+		/* reg_0x98007060[1] = 1 */
+		/* ISO spec, bypass mode enable */
+		/* bypass mode, SW need to handle the EPHY Status check ,
+		 * EFUSE data update and EPHY fuse_rdy setting.
+		 */
+		tmp = readl(tp->mmio_clkaddr + ISO_ETN_TESTIO);
+		tmp |= BIT(1);
+		writel(tmp, tp->mmio_clkaddr + ISO_ETN_TESTIO);
+	}
+
+	/* reg_0x98007088[9] = 1 */
+	/* ISO spec, reset bit of gmac */
+	reset_control_deassert(rstc_gmac);
+
+	/* reg_0x9800708c[12:11] = 11 */
+	/* ISO spec, clock enable bit for etn clock & etn 250MHz */
+	clk_prepare_enable(clk_etn_sys);
+	clk_prepare_enable(clk_etn_250m);
+
+	mdelay(10);	/* wait 10ms for GMAC uC to be stable */
+
+	/* release resource */
+	reset_control_put(rstc_gphy);
+	reset_control_put(rstc_gmac);
+}
+
+/* Hercules only uses 13 bits in OTP 0x9801_72C8[12:8] and 0x9801_72D8[7:0]
+ * if 0x9801_72C8[12] is 1, then
+ * 0x9801_72C8[11:8] (R-calibration) is used to set PHY
+ * 0x9801_72D8[7:0] is used to set idac_fine for PHY
+ */
+static void rtd139x_load_otp_content(struct rtl8169_private *tp)
+{
+	u32 otp;
+	u16 tmp;
+	u8 *buf;
+
+	buf = r8169soc_read_otp(tp, "para");
+	if (IS_ERR(buf))
+		goto set_idac;
+
+	otp = *buf;
+	/* OTP[4] = valid flag, OTP[3:0] = content */
+	if (0 != ((0x1 << 4) & otp)) {
+		/* frc_r_value_default = 0x8 */
+		tmp = otp ^ RTD139X_R_K_DEFAULT;
+		r8169_mdio_write(tp, 31, 0xbc0);
+		r8169_mdio_write(tp, 20,
+				 (tmp | (r8169_mdio_read(tp, 20) &
+				 ~(0x1f << 0))));
+	}
+
+	kfree(buf);
+
+set_idac:
+
+	buf = r8169soc_read_otp(tp, "idac");
+	if (IS_ERR(buf))
+		return;
+
+	otp = *buf;
+	tmp = otp ^ RTD139X_IDAC_FINE_DEFAULT;	/* IDAC_FINE_DEFAULT = 0x33 */
+	r8169_mdio_write(tp, 31, 0xbc0);
+	r8169_mdio_write(tp, 23,
+			 (tmp | (r8169_mdio_read(tp, 23) & ~(0xff << 0))));
+
+	kfree(buf);
+}
+
+static u32 rtd139x_serdes_init(struct rtl8169_private *tp)
+{
+	u32 stable_ticks;
+	u32 tmp;
+	struct clk *clk_en_sds = clk_get(&tp->pdev->dev, "sds");
+	struct reset_control *rstc_sds_reg =
+		reset_control_get_exclusive(&tp->pdev->dev, "sds_reg");
+	struct reset_control *rstc_sds =
+		reset_control_get_exclusive(&tp->pdev->dev, "sds");
+	struct reset_control *rstc_pcie0_power =
+		reset_control_get_exclusive(&tp->pdev->dev, "pcie0_power");
+	struct reset_control *rstc_pcie0_phy =
+		reset_control_get_exclusive(&tp->pdev->dev, "pcie0_phy");
+	struct reset_control *rstc_pcie0_sgmii_mdio =
+		reset_control_get_exclusive(&tp->pdev->dev, "pcie0_sgmii_mdio");
+	struct reset_control *rstc_pcie0_phy_mdio =
+		reset_control_get_exclusive(&tp->pdev->dev, "pcie0_phy_mdio");
+
+	/* reg_0x98000000[4:3] = 11 */
+	/* CRT spec, rstn_sds_reg & rstn_sds */
+	reset_control_deassert(rstc_sds);
+	reset_control_deassert(rstc_sds_reg);
+
+	/* reg_0x98000004[7] = 1 */
+	/* reg_0x98000004[14] = 1 */
+	/* CRT spec, rstn_pcie0_power & rstn_pcie0_phy */
+	reset_control_deassert(rstc_pcie0_power);
+	reset_control_deassert(rstc_pcie0_phy);
+
+	/* reg_0x98000050[13] = 1 */
+	/* reg_0x98000050[16] = 1 */
+	/* CRT spec, rstn_pcie0_sgmii_mdio & rstn_pcie0_phy_mdio */
+	reset_control_deassert(rstc_pcie0_sgmii_mdio);
+	reset_control_deassert(rstc_pcie0_phy_mdio);
+
+	/* reg_0x9800000c[7] = 1 */
+	/* CRT spec, clk_en_sds */
+	clk_prepare_enable(clk_en_sds);
+
+	/* reg_0x9800705c[6] = 1 */
+	/* ISO spec, set PCIE channel to SGMII */
+	tmp = readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+	tmp |= BIT(6);
+	writel(tmp, tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+
+	/* reg_0x9800705c[7] = 1 */
+	/* ISO spec, set internal MDIO to PCIE */
+	tmp = readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+	tmp |= BIT(7);
+	writel(tmp, tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+
+	/* ### Beginning of SGMII DPHY register tuning ### */
+	tp->chip->mdio_lock(tp);
+
+	/* reg_0x9800705c[20:16] = 00000 */
+	/* ISO spec, set internal PHY addr to SERDES_DPHY_0 */
+	tmp = readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+	tmp &= ~(BIT(20) | BIT(19) | BIT(18) | BIT(17) | BIT(16));
+	tmp |= (RTD139X_SERDES_DPHY_0 << 16);
+	writel(tmp, tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+
+	/* # DPHY spec, DPHY reg13[8:7]=00, choose 1.25GHz */
+	r8169_mdio_write(tp, 0x0d,
+			 r8169_mdio_read(tp, 0x0d) & ~(BIT(8) | BIT(7)));
+
+	/* # DPHY spec, 5GHz tuning */
+	r8169_mdio_write(tp, 0x4, 0x52f5);
+	r8169_mdio_write(tp, 0x5, 0xead7);
+	r8169_mdio_write(tp, 0x6, 0x0010);
+	r8169_mdio_write(tp, 0xa, 0xc653);
+	r8169_mdio_write(tp, 0x1, 0xa830);
+
+	/* reg_0x9800705c[20:16] = 00001 */
+	/* ISO spec, set internal PHY addr to SERDES_DPHY_1 */
+	tmp = readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+	tmp &= ~(BIT(20) | BIT(19) | BIT(18) | BIT(17) | BIT(16));
+	tmp |= (RTD139X_SERDES_DPHY_1 << 16);
+	writel(tmp, tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+
+	/* RTD139X_TX_SWING_1040MV */
+	r8169_mdio_write(tp, 0x0, 0xd4aa);
+	r8169_mdio_write(tp, 0x1, 0x88aa);
+
+	/* reg_0x9800705c[20:16] = 00001 */
+	/* ISO spec, set internal PHY addr to INT_PHY_ADDR */
+	tmp = readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+	tmp &= ~(BIT(20) | BIT(19) | BIT(18) | BIT(17) | BIT(16));
+	tmp |= (RTD139X_INT_PHY_ADDR << 16);
+	writel(tmp, tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+
+	tp->ext_phy = true;
+	tp->chip->mdio_unlock(tp);
+	mdelay(10);     /* wait for clock stable */
+	/* ext_phy == true now */
+
+	/* reg_0x981c8070[9:0] = 0000000110 */
+	/* SDS spec, set SP_CFG_SDS_DBG_SEL to get PHY_Ready */
+	tmp = readl(tp->mmio_sdsaddr + RTD139X_SDS_REG28);
+	tmp &= ~(0x3ff << 0);
+	tmp |= (0x006 << 0);
+	writel(tmp, tp->mmio_sdsaddr + RTD139X_SDS_REG28);
+
+	tmp = 0;
+	stable_ticks = 0;
+	while (stable_ticks < 10) {
+		/* # SDS spec, wait for phy ready */
+		while ((readl(tp->mmio_sdsaddr + RTD139X_SDS_REG29) & BIT(14))
+		       == 0) {
+			stable_ticks = 0;
+			if (tmp >= 100) {
+				stable_ticks = 10;
+				pr_err("SGMII PHY not ready in 100ms\n");
+				break;
+			}
+		}
+		stable_ticks++;
+		tmp++;
+		mdelay(1);
+	}
+	pr_info("SGMII PHY is ready in %d ms", tmp);
+
+	/* reg_0x9800705c[4] = 1 */
+	/* ISO spec, set data path to SGMII */
+	tmp = readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+	tmp |= BIT(4);
+	writel(tmp, tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+
+	/* reg_0x981c8008[9:8] = 00 */
+	/* # SDS spec, SP_SDS_FRC_AN, SERDES auto mode */
+	tmp = readl(tp->mmio_sdsaddr + RTD139X_SDS_REG02);
+	tmp &= ~(BIT(9) | BIT(8));
+	writel(tmp, tp->mmio_sdsaddr + RTD139X_SDS_REG02);
+
+	/* # SDS spec, wait for SERDES link up */
+	tmp = 0;
+	stable_ticks = 0;
+	while (stable_ticks < 10) {
+		while ((BIT(13) | BIT(12)) !=
+		       (readl(tp->mmio_sdsaddr + RTD139X_SDS_MISC) &
+		       (BIT(13) | BIT(12)))) {
+			stable_ticks = 0;
+			if (tmp >= 100) {
+				stable_ticks = 10;
+				pr_err("SGMII link down in 100ms\n");
+				break;
+			}
+		}
+		stable_ticks++;
+		tmp++;
+		mdelay(1);
+	}
+	pr_info("SGMII link up in %d ms", tmp);
+
+	reset_control_put(rstc_sds_reg);
+	reset_control_put(rstc_sds);
+	reset_control_put(rstc_pcie0_power);
+	reset_control_put(rstc_pcie0_phy);
+	reset_control_put(rstc_pcie0_sgmii_mdio);
+	reset_control_put(rstc_pcie0_phy_mdio);
+	return 0;
+}
+
+static void rtd139x_phy_iol_tuning(struct rtl8169_private *tp)
+{
+	int revision = REVISION_NONE;
+	const struct soc_device_attribute *soc;
+	const struct soc_device_attribute rtk_soc_rtd139x[] = {
+		{
+			.family = "Realtek Hercules",
+			.revision = "A00",
+			.data = (void *)REVISION_A00,
+		},
+		{
+			.family = "Realtek Hercules",
+			.revision = "A01",
+			.data = (void *)REVISION_A01,
+		},
+		{
+			.family = "Realtek Hercules",
+			.revision = "A02",
+			.data = (void *)REVISION_A02,
+		},
+		{
+		/* empty */
+		}
+	};
+
+	soc = soc_device_match(rtk_soc_rtd139x);
+	if (soc)
+		revision = (uintptr_t)soc->data;
+
+	switch (revision) {
+	case REVISION_A00: /* TSMC, cut A */
+	case REVISION_A01: /* TSMC, cut B */
+		/* idacfine */
+		r8169_mdio_write(tp, 31, 0xbc0);
+		r8169_mdio_write(tp, 23, 0x0088);
+
+		/* abiq */
+		/* r8169_mdio_write(tp, 31, 0xbc0); */
+		r8169_mdio_write(tp, 21, 0x0004);
+
+		/* ldvbias */
+		/* r8169_mdio_write(tp, 31, 0xbc0); */
+		r8169_mdio_write(tp, 22, 0x0777);
+
+		/* iatt */
+		r8169_mdio_write(tp, 31, 0xbd0);
+		r8169_mdio_write(tp, 16, 0x0300);
+
+		/* vcm_ref, cf_l */
+		/* r8169_mdio_write(tp, 31, 0xbd0); */
+		r8169_mdio_write(tp, 17, 0xe8ca);
+		break;
+	case REVISION_A02: /* UMC, cut C */
+		/* 100M Swing */
+		/* idac_fine_mdix, idac_fine_mdi */
+		r8169_mdio_write(tp, 31, 0xbc0);
+		r8169_mdio_write(tp, 23, 0x0044);
+
+		/* 100M Tr/Tf */
+		/* abiq_10m=0x0, abiq_100m_short=0x4, abiq_normal=0x6 */
+		/* mdio_write(tp, 31, 0xbc0); */
+		r8169_mdio_write(tp, 21, 0x0046);
+
+		/* 10M */
+		/* ldvbias_10m=0x7, ldvbias_10m_short=0x4, ldvbias_normal=0x4 */
+		/* mdio_write(tp, 31, 0xbc0); */
+		r8169_mdio_write(tp, 22, 0x0744);
+
+		/* vcmref=0x0, cf_l=0x3 */
+		r8169_mdio_write(tp, 31, 0xbd0);
+		r8169_mdio_write(tp, 17, 0x18ca);
+
+		/* iatt=0x2 */
+		/* mdio_write(tp, 31, 0xbd0); */
+		r8169_mdio_write(tp, 16, 0x0200);
+		break;
+
+	/* default: */
+	}
+}
+
+static void rtd139x_mdio_init(struct rtl8169_private *tp)
+{
+	u32 tmp;
+	struct pinctrl *p_sgmii_mdio;
+	struct pinctrl_state *ps_sgmii_mdio;
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	/* ETN_PHY_INTR, wait interrupt from PHY and it means MDIO is ready */
+	tmp = 0;
+	while ((readl(tp->mmio_clkaddr + ISO_UMSK_ISR) & BIT(27)) == 0) {
+		tmp += 1;
+		mdelay(1);
+		if (tmp >= 100) {
+			pr_err("PHY PHY_Status timeout.\n");
+			break;
+		}
+	}
+	pr_info("wait %d ms for PHY interrupt. UMSK_ISR = 0x%x\n",
+		tmp, readl(tp->mmio_clkaddr + ISO_UMSK_ISR));
+
+	tp->chip->mdio_lock(tp);
+	/* In Hercules ByPass mode,
+	 * SW need to handle the EPHY Status check ,
+	 * OTP data update and EPHY fuse_rdy setting.
+	 */
+	if (tp->bypass_enable) {
+		/* PHY will stay in state 1 mode */
+		r8169_mdio_write(tp, 31, 0x0a42);
+		tmp = 0;
+		while (0x1 != (r8169_mdio_read(tp, 16) & 0x07)) {
+			tmp += 1;
+			mdelay(1);
+			if (tmp >= 2000) {
+				pr_err("PHY status is not 0x1 in bypass mode, current = 0x%02x\n",
+				       (r8169_mdio_read(tp, 16) & 0x07));
+				break;
+			}
+		}
+
+		/* adjust FE PHY electrical characteristics */
+		rtd139x_phy_iol_tuning(tp);
+
+		/* 1. read OTP 0x9801_72C8[12:8]
+		 * 2. xor 0x08
+		 * 3. set value to PHY registers to correct R-calibration
+		 * 4. read OTP 0x9801_72D8[7:0]
+		 * 5. xor 0x33
+		 * 6. set value to PHY registers to correct AMP
+		 */
+		rtd139x_load_otp_content(tp);
+
+		/* fill fuse_rdy & rg_ext_ini_done */
+		r8169_mdio_write(tp, 31, 0x0a46);
+		r8169_mdio_write(tp, 20,
+				 (r8169_mdio_read(tp, 20) | (BIT(1) | BIT(0))));
+	} else {
+		/* adjust FE PHY electrical characteristics */
+		rtd139x_phy_iol_tuning(tp);
+	}
+
+	/* init_autoload_done = 1 */
+	tmp = rtl_ocp_read(tp, 0xe004);
+	tmp |= BIT(7);
+	rtl_ocp_write(tp, 0xe004, tmp);
+	/* ee_mode = 3 */
+	RTL_W8(CFG9346, CFG9346_UNLOCK);
+
+	/* wait LAN-ON */
+	r8169_mdio_write(tp, 31, 0x0a42);
+	tmp = 0;
+	do {
+		tmp += 1;
+		mdelay(1);
+		if (tmp >= 2000) {
+			pr_err("PHY status is not 0x3, current = 0x%02x\n",
+			       (r8169_mdio_read(tp, 16) & 0x07));
+			break;
+		}
+	} while (0x3 != (r8169_mdio_read(tp, 16) & 0x07));
+	tp->chip->mdio_unlock(tp);
+	pr_info("wait %d ms for PHY ready, current = 0x%x\n",
+		tmp, r8169_mdio_read(tp, 16));
+
+	if (tp->output_mode == OUTPUT_EMBEDDED_PHY) {
+		/* Init PHY path */
+		/* reg_0x9800705c[5] = 0 */
+		/* reg_0x9800705c[7] = 0 */
+		/* ISO spec, set internal MDIO to access PHY */
+		tmp = readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+		tmp &= ~(BIT(7) | BIT(5));
+		writel(tmp, tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+
+		/* reg_0x9800705c[4] = 0 */
+		/* ISO spec, set data path to access PHY */
+		tmp = readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+		tmp &= ~(BIT(4));
+		writel(tmp, tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+
+		/* # ETN spec, GMAC data path select MII-like(embedded GPHY),
+		 * not SGMII(external PHY)
+		 */
+		tmp = rtl_ocp_read(tp, 0xea34) & ~(BIT(0) | BIT(1));
+		tmp |= BIT(1); /* MII */
+		rtl_ocp_write(tp, 0xea34, tmp);
+	} else {
+		/* SGMII */
+		/* # ETN spec, adjust MDC freq=2.5MHz */
+		rtl_ocp_write(tp, 0xDE30,
+			      rtl_ocp_read(tp, 0xDE30) & ~(BIT(7) | BIT(6)));
+		/* # ETN spec, set external PHY addr */
+		rtl_ocp_write(tp, 0xDE24,
+			      ((rtl_ocp_read(tp, 0xDE24) & ~(0x1f << 0)) |
+					     (tp->ext_phy_id & 0x1f)));
+		/* ISO mux spec, GPIO29 is set to MDC pin */
+		/* ISO mux spec, GPIO46 is set to MDIO pin */
+		p_sgmii_mdio = devm_pinctrl_get(&tp->pdev->dev);
+		ps_sgmii_mdio = pinctrl_lookup_state(p_sgmii_mdio, "sgmii");
+		pinctrl_select_state(p_sgmii_mdio, ps_sgmii_mdio);
+
+		/* check if external PHY is available */
+		pr_info("Searching external PHY...");
+		tp->chip->mdio_lock(tp);
+		tp->ext_phy = true;
+		r8169_mdio_write(tp, 31, 0x0a43);
+		tmp = 0;
+		while (r8169_mdio_read(tp, 31) != 0xa43) {
+			pr_info(".");
+			tmp += 1;
+			mdelay(1);
+			if (tmp >= 2000) {
+				pr_err("\n External SGMII PHY not found, current = 0x%02x\n",
+				       r8169_mdio_read(tp, 31));
+				break;
+			}
+		}
+		if (tmp < 2000)
+			pr_info("found.\n");
+
+		/* lower SGMII TX swing of RTL8211FS to reduce EMI */
+		r8169_mdio_write(tp, 31, 0x0dcd);
+		/* TX swing = 470mV, default value */
+		r8169_mdio_write(tp, 16, 0x104e);
+
+		tp->ext_phy = false;
+		tp->chip->mdio_unlock(tp);
+
+		/* # ETN spec, GMAC data path select SGMII(external PHY),
+		 * not MII-like(embedded GPHY)
+		 */
+		tmp = rtl_ocp_read(tp, 0xea34) & ~(BIT(0) | BIT(1));
+		tmp |= BIT(1) | BIT(0); /* SGMII */
+		rtl_ocp_write(tp, 0xea34, tmp);
+
+		if (rtd139x_serdes_init(tp) != 0)
+			pr_err("SERDES init failed\n");
+		/* ext_phy == true now */
+
+		/* SDS spec, auto update SGMII link capability */
+		tmp = readl(tp->mmio_sdsaddr + RTD139X_SDS_LINK);
+		tmp |= BIT(2);
+		writel(tmp, tp->mmio_sdsaddr + RTD139X_SDS_LINK);
+	}
+}
+
+static void rtd139x_eee_set(struct rtl8169_private *tp, bool enable)
+{
+	if (enable) {
+		tp->chip->mdio_lock(tp);
+		if (tp->output_mode == OUTPUT_EMBEDDED_PHY) {
+			/* 100M EEE capability */
+			r8169_mmd_write(tp, 0x7, 60, BIT(1));
+			/* 10M EEE */
+			r8169_mdio_write(tp, 31, 0xa43);
+			r8169_mdio_write(tp, 25,
+					 r8169_mdio_read(tp, 25) | BIT(4) |
+					 BIT(2));
+			/* disable dynamic RX power in PHY */
+			r8169_mdio_write(tp, 31, 0xbd0);
+			r8169_mdio_write(tp, 21,
+					 (r8169_mdio_read(tp, 21) & ~BIT(8)) |
+					 BIT(9));
+		} else { /* SGMII */
+			/* 1000M & 100M EEE capability */
+			r8169_mmd_write(tp, 0x7, 60, BIT(2) | BIT(1));
+			/* 10M EEE */
+			r8169_mdio_write(tp, 31, 0xa43);
+			r8169_mdio_write(tp, 25,
+					 r8169_mdio_read(tp, 25) | BIT(4) |
+					 BIT(2));
+		}
+		tp->chip->mdio_unlock(tp);
+
+		/* EEE MAC mode */
+		rtl_ocp_write(tp, 0xe040,
+			      rtl_ocp_read(tp, 0xe040) | BIT(1) | BIT(0));
+		/* EEE+ MAC mode */
+		/* timer to wait FEPHY ready */
+		rtl_ocp_write(tp, 0xe08a, 0x00a7);
+		rtl_ocp_write(tp, 0xe080, rtl_ocp_read(tp, 0xe080) | BIT(1));
+	} else {
+		tp->chip->mdio_lock(tp);
+		if (tp->output_mode == OUTPUT_EMBEDDED_PHY) {
+			/* no EEE capability */
+			r8169_mmd_write(tp, 0x7, 60, 0);
+			/* 10M EEE */
+			r8169_mdio_write(tp, 31, 0xa43);
+			r8169_mdio_write(tp, 25,
+					 r8169_mdio_read(tp, 25) &
+					 ~(BIT(4) | BIT(2)));
+		} else { /* SGMII */
+			/* no EEE capability */
+			r8169_mmd_write(tp, 0x7, 60, 0);
+			/* 10M EEE */
+			r8169_mdio_write(tp, 31, 0xa43);
+			r8169_mdio_write(tp, 25,
+					 r8169_mdio_read(tp, 25) &
+					 ~(BIT(4) | BIT(2)));
+		}
+		tp->chip->mdio_unlock(tp);
+
+		/* reset to default value */
+		rtl_ocp_write(tp, 0xe040, rtl_ocp_read(tp, 0xe040) | BIT(13));
+		/* EEE MAC mode */
+		rtl_ocp_write(tp, 0xe040,
+			      rtl_ocp_read(tp, 0xe040) & ~(BIT(1) | BIT(0)));
+		/* EEE+ MAC mode */
+		rtl_ocp_write(tp, 0xe080, rtl_ocp_read(tp, 0xe080) & ~BIT(1));
+		rtl_ocp_write(tp, 0xe08a, 0x003f); /* default value */
+	}
+}
+
+static void rtd139x_led_set(struct rtl8169_private *tp, bool enable)
+{
+	u32 val;
+
+	if (enable) {
+		val = readl(tp->mmio_pinmuxaddr + RTD139X_ISO_TESTMUX_MUXPAD1)
+			    & ~0x000000F0;
+		writel(val | 0x50, tp->mmio_pinmuxaddr +
+		       RTD139X_ISO_TESTMUX_MUXPAD1);
+	} else {
+		writel(readl(tp->mmio_pinmuxaddr + RTD139X_ISO_TESTMUX_MUXPAD1)
+		       & ~0x000000F0,
+		       tp->mmio_pinmuxaddr + RTD139X_ISO_TESTMUX_MUXPAD1);
+	}
+}
+
+static void rtd139x_dump_regs(struct seq_file *m, struct rtl8169_private *tp)
+{
+	seq_printf(m, "ISO_UMSK_ISR\t[0x98007004] = %08x\n",
+		   readl(tp->mmio_clkaddr + ISO_UMSK_ISR));
+	seq_printf(m, "ISO_PWRCUT_ETN\t[0x9800705c] = %08x\n",
+		   readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN));
+	seq_printf(m, "ISO_ETN_TESTIO\t[0x98007060] = %08x\n",
+		   readl(tp->mmio_clkaddr + ISO_ETN_TESTIO));
+	seq_printf(m, "ETN_RESET_CTRL\t[0x98007088] = %08x\n",
+		   readl(tp->mmio_clkaddr + ISO_SOFT_RESET));
+	seq_printf(m, "ETN_CLK_CTRL\t[0x9800708c] = %08x\n",
+		   readl(tp->mmio_clkaddr + ISO_CLOCK_ENABLE));
+	if (tp->output_mode != OUTPUT_EMBEDDED_PHY) {
+		seq_printf(m, "SDS_REG02\t[0x981c8008] = %08x\n",
+			   readl(tp->mmio_sdsaddr + RTD139X_SDS_REG02));
+		seq_printf(m, "SDS_REG28\t[0x981c8070] = %08x\n",
+			   readl(tp->mmio_sdsaddr + RTD139X_SDS_REG28));
+		seq_printf(m, "SDS_REG29\t[0x981c8074] = %08x\n",
+			   readl(tp->mmio_sdsaddr + RTD139X_SDS_REG29));
+		seq_printf(m, "SDS_MISC\t\t[0x981c9804] = %08x\n",
+			   readl(tp->mmio_sdsaddr + RTD139X_SDS_MISC));
+		seq_printf(m, "SDS_LINK\t\t[0x981c9810] = %08x\n",
+			   readl(tp->mmio_sdsaddr + RTD139X_SDS_LINK));
+	}
+}
+
+static void rtd139x_dump_var(struct seq_file *m, struct rtl8169_private *tp)
+{
+	seq_printf(m, "bypass_enable\t%d\n", tp->bypass_enable);
+}
+
+/******************* END of RTD139X ****************************/
+
+/* RTD16XX */
+static void rtd16xx_mdio_lock(struct rtl8169_private *tp)
+{
+	rtd139x_mdio_lock(tp);
+}
+
+static void rtd16xx_mdio_unlock(struct rtl8169_private *tp)
+{
+	rtd139x_mdio_unlock(tp);
+}
+
+static void rtd16xx_reset_phy_gmac(struct rtl8169_private *tp)
+{
+	u32 tmp;
+	struct clk *clk_etn_sys = clk_get(&tp->pdev->dev, "etn_sys");
+	struct clk *clk_etn_250m = clk_get(&tp->pdev->dev, "etn_250m");
+	struct reset_control *rstc_gphy =
+		reset_control_get_exclusive(&tp->pdev->dev, "gphy");
+	struct reset_control *rstc_gmac =
+		reset_control_get_exclusive(&tp->pdev->dev, "gmac");
+	struct clk *clk_en_sds = NULL;
+	struct reset_control *rstc_sds_reg = NULL;
+	struct reset_control *rstc_sds = NULL;
+	struct reset_control *rstc_pcie0_power = NULL;
+	struct reset_control *rstc_pcie0_phy = NULL;
+	struct reset_control *rstc_pcie0_sgmii_mdio = NULL;
+	struct reset_control *rstc_pcie0_phy_mdio = NULL;
+
+	clk_prepare_enable(clk_etn_sys);
+	clk_prepare_enable(clk_etn_250m);
+
+	if (tp->output_mode != OUTPUT_EMBEDDED_PHY) {
+		clk_en_sds = clk_get(&tp->pdev->dev, "sds");
+		rstc_sds_reg =
+			reset_control_get_exclusive(&tp->pdev->dev, "sds_reg");
+		rstc_sds = reset_control_get_exclusive(&tp->pdev->dev, "sds");
+		rstc_pcie0_power =
+			reset_control_get_exclusive(&tp->pdev->dev, "pcie0_power");
+		rstc_pcie0_phy =
+			reset_control_get_exclusive(&tp->pdev->dev, "pcie0_phy");
+		rstc_pcie0_sgmii_mdio =
+			reset_control_get_exclusive(&tp->pdev->dev, "pcie0_sgmii_mdio");
+		rstc_pcie0_phy_mdio =
+			reset_control_get_exclusive(&tp->pdev->dev, "pcie0_phy_mdio");
+	}
+
+	/* reg_0x9800708c[12:11] = 00 */
+	/* ISO spec, clock enable bit for etn clock & etn 250MHz */
+	clk_disable_unprepare(clk_etn_sys);
+	clk_disable_unprepare(clk_etn_250m);
+
+	/* reg_0x98007088[10:9] = 00 */
+	/* ISO spec, rstn_gphy & rstn_gmac */
+	reset_control_assert(rstc_gphy);
+	reset_control_assert(rstc_gmac);
+
+	/* reg_0x98007060[1] = 1 */
+	/* ISO spec, bypass mode enable */
+	tmp = readl(tp->mmio_clkaddr + ISO_ETN_TESTIO);
+	tmp |= BIT(1);
+	writel(tmp, tp->mmio_clkaddr + ISO_ETN_TESTIO);
+
+	/* reg_0x9800705c = 0x00616703 */
+	/* ISO spec, default value */
+	writel(0x00616703, tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+
+	if (tp->output_mode != OUTPUT_EMBEDDED_PHY) {
+		/* RESET for SGMII if needed */
+		clk_prepare_enable(clk_en_sds);
+
+		/* reg_0x98000050[13:12] = 10 */
+		/* CRT spec, clk_en_sds */
+		clk_disable_unprepare(clk_en_sds);
+
+		/* reg_0x98000000[7:6] = 10 */
+		/* reg_0x98000000[9:8] = 10 */
+		/* CRT spec, rstn_sds_reg & rstn_sds */
+		reset_control_assert(rstc_sds);
+		reset_control_assert(rstc_sds_reg);
+
+		/* reg_0x98000004[25:24] = 10 CRT spec, rstn_pcie0_sgmii_mdio */
+		/* reg_0x98000004[23:22] = 10 CRT spec, rstn_pcie0_phy_mdio */
+		/* reg_0x98000004[19:18] = 10 CRT spec, rstn_pcie0_power */
+		/* reg_0x98000004[13:12] = 10 CRT spec, rstn_pcie0_phy */
+		reset_control_assert(rstc_pcie0_sgmii_mdio);
+		reset_control_assert(rstc_pcie0_phy_mdio);
+		reset_control_assert(rstc_pcie0_power);
+		reset_control_assert(rstc_pcie0_phy);
+	}
+
+	mdelay(1);
+
+	/* release resource */
+	if (tp->output_mode != OUTPUT_EMBEDDED_PHY) {
+		reset_control_put(rstc_sds_reg);
+		reset_control_put(rstc_sds);
+		reset_control_put(rstc_pcie0_power);
+		reset_control_put(rstc_pcie0_phy);
+		reset_control_put(rstc_pcie0_sgmii_mdio);
+		reset_control_put(rstc_pcie0_phy_mdio);
+	}
+	reset_control_put(rstc_gphy);
+	reset_control_put(rstc_gmac);
+}
+
+static void rtd16xx_acp_init(struct rtl8169_private *tp)
+{
+	u32 tmp;
+
+	/* SBX spec, Select ETN access DDR path. */
+	if (tp->acp_enable) {
+		/* reg_0x9801c20c[6] = 1 */
+		/* SBX spec, Mask ETN_ALL to SB3 DBUS REQ */
+		tmp = readl(tp->mmio_sbxaddr +
+			    RTD16XX_SBX_SB3_CHANNEL_REQ_MASK);
+		tmp |= BIT(6);
+		writel(tmp, tp->mmio_sbxaddr +
+		       RTD16XX_SBX_SB3_CHANNEL_REQ_MASK);
+
+		pr_info("wait all SB3 access finished...");
+		tmp = 0;
+		while (0 != (BIT(6) &
+			readl(tp->mmio_sbxaddr +
+			      RTD16XX_SBX_SB3_CHANNEL_REQ_BUSY))) {
+			mdelay(1);
+			tmp += 1;
+			if (tmp >= 100) {
+				pr_err("\n wait SB3 access failed (%d ms)\n",
+				       tmp);
+				break;
+			}
+		}
+		if (tmp < 100)
+			pr_info("done.\n");
+
+		/* reg_0x9801d100[29] = 0 */
+		/* SCPU wrapper spec, CLKACP division, 0 = div 2, 1 = div 3 */
+		tmp = readl(tp->mmio_sbxaddr + RTD16XX_SC_WRAP_CRT_CTRL);
+		tmp &= ~(BIT(29));
+		writel(tmp, tp->mmio_sbxaddr + RTD16XX_SC_WRAP_CRT_CTRL);
+
+		/* reg_0x9801d124[1:0] = 00 */
+		/* SCPU wrapper spec, ACP master active, 0 = active */
+		tmp = readl(tp->mmio_sbxaddr + RTD16XX_SC_WRAP_INTERFACE_EN);
+		tmp &= ~(BIT(1) | BIT(0));
+		writel(tmp, tp->mmio_sbxaddr + RTD16XX_SC_WRAP_INTERFACE_EN);
+
+		/* reg_0x9801d100[30] = 1 */
+		/* SCPU wrapper spec, dy_icg_en_acp */
+		tmp = readl(tp->mmio_sbxaddr + RTD16XX_SC_WRAP_CRT_CTRL);
+		tmp |= BIT(30);
+		writel(tmp, tp->mmio_sbxaddr + RTD16XX_SC_WRAP_CRT_CTRL);
+
+		/* reg_0x9801d100[21] = 1 */
+		/* SCPU wrapper spec, ACP CLK enable */
+		tmp = readl(tp->mmio_sbxaddr + RTD16XX_SC_WRAP_CRT_CTRL);
+		tmp |= BIT(21);
+		writel(tmp, tp->mmio_sbxaddr + RTD16XX_SC_WRAP_CRT_CTRL);
+
+		/* reg_0x9801d100[14] = 1 */
+		/* SCPU wrapper spec,
+		 * Do not apply reset to ACP port axi3 master
+		 */
+		tmp = readl(tp->mmio_sbxaddr + RTD16XX_SC_WRAP_CRT_CTRL);
+		tmp |= BIT(14);
+		writel(tmp, tp->mmio_sbxaddr + RTD16XX_SC_WRAP_CRT_CTRL);
+
+		/* reg_0x9801d800[3:0] = 0111 */
+		/* reg_0x9801d800[7:4] = 0111 */
+		/* reg_0x9801d800[9:8] = 00 */
+		/* reg_0x9801d800[20:16] = 01100 */
+		/* reg_0x9801d800[28:24] = 01110 */
+		/* Configure control of ACP port */
+		tmp = readl(tp->mmio_sbxaddr + RTD16XX_SC_WRAP_ACP_CTRL);
+		tmp &= ~((0x1f << 24) | (0x1f << 16) | (0x1 << 9) |
+			(0xf << 4) | (0xf << 0));
+		tmp |= (0x0e << 24) | (0x0c << 16) | (0x1 << 9) | (0x7 << 4) |
+			(0x7 << 0);
+		writel(tmp, tp->mmio_sbxaddr + RTD16XX_SC_WRAP_ACP_CTRL);
+
+		/* reg_0x9801d030[28] = 1 */
+		/* SCPU wrapper spec, dy_icg_en_acp */
+		tmp = readl(tp->mmio_sbxaddr + RTD16XX_SC_WRAP_ACP_CRT_CTRL);
+		tmp |= BIT(28);
+		writel(tmp, tp->mmio_sbxaddr + RTD16XX_SC_WRAP_ACP_CRT_CTRL);
+
+		/* reg_0x9801d030[16] = 1 */
+		/* SCPU wrapper spec, ACP CLK Enable for acp of scpu_chip_top */
+		tmp = readl(tp->mmio_sbxaddr + RTD16XX_SC_WRAP_ACP_CRT_CTRL);
+		tmp |= BIT(16);
+		writel(tmp, tp->mmio_sbxaddr + RTD16XX_SC_WRAP_ACP_CRT_CTRL);
+
+		/* reg_0x9801d030[0] = 1 */
+		/* SCPU wrapper spec,
+		 * Do not apply reset to ACP port axi3 master
+		 */
+		tmp = readl(tp->mmio_sbxaddr + RTD16XX_SC_WRAP_ACP_CRT_CTRL);
+		tmp |= BIT(0);
+		writel(tmp, tp->mmio_sbxaddr + RTD16XX_SC_WRAP_ACP_CRT_CTRL);
+
+		/* reg_0x9801c814[17] = 1 */
+		/* through ACP to SCPU_ACP */
+		tmp = readl(tp->mmio_sbxaddr + RTD16XX_SBX_ACP_MISC_CTRL);
+		tmp |= BIT(17);
+		writel(tmp, tp->mmio_sbxaddr + RTD16XX_SBX_ACP_MISC_CTRL);
+
+		/* SBX spec, Remove mask ETN_ALL to ACP DBUS REQ */
+		tmp = readl(tp->mmio_sbxaddr +
+			    RTD16XX_SBX_ACP_CHANNEL_REQ_MASK);
+		tmp &= ~BIT(1);
+		writel(tmp, tp->mmio_sbxaddr +
+		       RTD16XX_SBX_ACP_CHANNEL_REQ_MASK);
+
+		pr_info("ARM ACP on\n.");
+	} else {
+		/* SBX spec, Mask ETN_ALL to ACP DBUS REQ */
+		tmp = readl(tp->mmio_sbxaddr +
+			    RTD16XX_SBX_ACP_CHANNEL_REQ_MASK);
+		tmp |= BIT(1);
+		writel(tmp, tp->mmio_sbxaddr +
+		       RTD16XX_SBX_ACP_CHANNEL_REQ_MASK);
+
+		pr_info("wait all ACP access finished...");
+		tmp = 0;
+		while (0 != (BIT(1) &
+			readl(tp->mmio_sbxaddr +
+			      RTD16XX_SBX_ACP_CHANNEL_REQ_BUSY))) {
+			mdelay(1);
+			tmp += 1;
+			if (tmp >= 100) {
+				pr_err("\n ACP channel is still busy (wait %d ms)\n",
+				       tmp);
+				break;
+			}
+		}
+		if (tmp < 100)
+			pr_info("done.\n");
+
+		/* SCPU wrapper spec, Inactive MP4 AINACTS signal */
+		tmp = readl(tp->mmio_sbxaddr + RTD16XX_SC_WRAP_INTERFACE_EN);
+		tmp |= (BIT(1) | BIT(0));
+		writel(tmp, tp->mmio_sbxaddr + RTD16XX_SC_WRAP_INTERFACE_EN);
+
+		/* SCPU wrapper spec, nACPRESET_DVFS & CLKENACP_DVFS */
+		tmp = readl(tp->mmio_sbxaddr + RTD16XX_SC_WRAP_CRT_CTRL);
+		tmp &= ~(BIT(21) | BIT(14));
+		writel(tmp, tp->mmio_sbxaddr + RTD16XX_SC_WRAP_CRT_CTRL);
+
+		/* SCPU wrapper spec, nACPRESET & CLKENACP */
+		tmp = readl(tp->mmio_sbxaddr + RTD16XX_SC_WRAP_ACP_CRT_CTRL);
+		tmp &= ~(BIT(16) | BIT(0));
+		writel(tmp, tp->mmio_sbxaddr + RTD16XX_SC_WRAP_ACP_CRT_CTRL);
+
+		/* reg_0x9801c814[17] = 0 */
+		/* SBX spec, Switch ETN_ALL to DC_SYS path */
+		tmp = readl(tp->mmio_sbxaddr + RTD16XX_SBX_ACP_MISC_CTRL);
+		tmp &= ~BIT(17);
+		writel(tmp, tp->mmio_sbxaddr + RTD16XX_SBX_ACP_MISC_CTRL);
+
+		/* SBX spec, Remove mask ETN_ALL to SB3 DBUS REQ */
+		tmp = readl(tp->mmio_sbxaddr +
+			    RTD16XX_SBX_SB3_CHANNEL_REQ_MASK);
+		tmp &= ~BIT(6);
+		writel(tmp, tp->mmio_sbxaddr +
+		       RTD16XX_SBX_SB3_CHANNEL_REQ_MASK);
+
+		pr_info("ARM ACP off\n.");
+	}
+}
+
+static void rtd16xx_pll_clock_init(struct rtl8169_private *tp)
+{
+	u32 tmp;
+	struct clk *clk_etn_sys = clk_get(&tp->pdev->dev, "etn_sys");
+	struct clk *clk_etn_250m = clk_get(&tp->pdev->dev, "etn_250m");
+	struct reset_control *rstc_gphy =
+		reset_control_get_exclusive(&tp->pdev->dev, "gphy");
+	struct reset_control *rstc_gmac =
+		reset_control_get_exclusive(&tp->pdev->dev, "gmac");
+
+	/* reg_0x98007004[27] = 1 */
+	/* ISO spec, ETN_PHY_INTR, clear ETN interrupt for ByPassMode */
+	writel(BIT(27), tp->mmio_clkaddr + ISO_UMSK_ISR);
+
+	/* reg_0x98007088[10] = 1 */
+	/* ISO spec, reset bit of gphy */
+	reset_control_deassert(rstc_gphy);
+
+	mdelay(1);		/* wait 1ms for PHY PLL stable */
+
+	/* Thor only supports the bypass mode */
+	/* Bypass mode : ETN MAC bypass efuse update flow.
+	 * SW need to take this sequence.
+	 */
+	/* reg_0x98007060[1] = 1 */
+	/* ISO spec, bypass mode enable */
+	/* bypass mode, SW need to handle the EPHY Status check ,
+	 * EFUSE data update and EPHY fuse_rdy setting.
+	 */
+	tmp = readl(tp->mmio_clkaddr + ISO_ETN_TESTIO);
+	tmp |= BIT(1);
+	writel(tmp, tp->mmio_clkaddr + ISO_ETN_TESTIO);
+
+	/* reg_0x98007088[9] = 1 */
+	/* ISO spec, reset bit of gmac */
+	reset_control_deassert(rstc_gmac);
+
+	/* reg_0x9800708c[12:11] = 11 */
+	/* ISO spec, clock enable bit for etn clock & etn 250MHz */
+	clk_prepare_enable(clk_etn_sys);
+	clk_prepare_enable(clk_etn_250m);
+
+	mdelay(10);		/* wait 10ms for GMAC uC to be stable */
+
+	/* release resource */
+	reset_control_put(rstc_gphy);
+	reset_control_put(rstc_gmac);
+}
+
+static void rtd16xx_load_otp_content(struct rtl8169_private *tp)
+{
+	u32 otp;
+	u16 tmp;
+	u8 *buf;
+
+	/* RC-K 0x980174F8[27:24] */
+	buf = r8169soc_read_otp(tp, "rc_k");
+	if (IS_ERR(buf))
+		goto set_r_amp_cal;
+
+	otp = *buf;
+	tmp = (otp << 12) | (otp << 8) | (otp << 4) | otp;
+	tmp ^= RTD16XX_RC_K_DEFAULT;
+	r8169_mdio_write(tp, 31, 0xbcd);
+	r8169_mdio_write(tp, 22, tmp);
+	r8169_mdio_write(tp, 23, tmp);
+
+	kfree(buf);
+
+set_r_amp_cal:
+
+	buf = r8169soc_read_otp(tp, "r_amp_k");
+	if (IS_ERR(buf))
+		return;
+
+	/* R-K 0x98017500[18:15] */
+	otp = ((buf[5] & 0x80) >> 7) | ((buf[6] & 0x07) << 1);
+	tmp = (otp << 12) | (otp << 8) | (otp << 4) | otp;
+	tmp ^= RTD16XX_R_K_DEFAULT;
+	r8169_mdio_write(tp, 31, 0xbce);
+	r8169_mdio_write(tp, 16, tmp);
+	r8169_mdio_write(tp, 17, tmp);
+
+	/* Amp-K 0x980174FC[15:0] */
+	otp = buf[0] | (buf[1] << 8);
+	tmp = otp ^ RTD16XX_AMP_K_DEFAULT;
+	r8169_mdio_write(tp, 31, 0xbca);
+	r8169_mdio_write(tp, 22, tmp);
+
+	/* Bias-K 0x980174FC[31:16] */
+	otp = buf[2] | (buf[3] << 8);
+	tmp = otp ^ RTD16XX_ADC_BIAS_K_DEFAULT;
+	r8169_mdio_write(tp, 31, 0xbcf);
+	r8169_mdio_write(tp, 22, tmp);
+
+	kfree(buf);
+}
+
+static u32 rtd16xx_serdes_init(struct rtl8169_private *tp)
+{
+	u32 stable_ticks;
+	u32 tmp;
+	struct clk *clk_en_sds = clk_get(&tp->pdev->dev, "sds");
+	struct reset_control *rstc_sds_reg =
+		reset_control_get_exclusive(&tp->pdev->dev, "sds_reg");
+	struct reset_control *rstc_sds =
+		reset_control_get_exclusive(&tp->pdev->dev, "sds");
+	struct reset_control *rstc_pcie0_power =
+		reset_control_get_exclusive(&tp->pdev->dev, "pcie0_power");
+	struct reset_control *rstc_pcie0_phy =
+		reset_control_get_exclusive(&tp->pdev->dev, "pcie0_phy");
+	struct reset_control *rstc_pcie0_sgmii_mdio =
+		reset_control_get_exclusive(&tp->pdev->dev, "pcie0_sgmii_mdio");
+	struct reset_control *rstc_pcie0_phy_mdio =
+		reset_control_get_exclusive(&tp->pdev->dev, "pcie0_phy_mdio");
+
+	/* reg_0x98000050[13:12] = 11 */
+	/* CRT spec, clk_en_sds */
+	clk_prepare_enable(clk_en_sds);
+
+	/* reg_0x98000000[9:8] = 11 */
+	/* reg_0x98000000[7:6] = 11 */
+	/* CRT spec, rstn_sds_reg & rstn_sds */
+	reset_control_deassert(rstc_sds);
+	reset_control_deassert(rstc_sds_reg);
+
+	/* reg_0x98000004[25:24] = 11   CRT spec, rstn_pcie0_sgmii_mdio */
+	/* reg_0x98000004[23:22] = 11   CRT spec, rstn_pcie0_phy_mdio */
+	/* reg_0x98000004[19:18] = 11   CRT spec, rstn_pcie0_power */
+	/* reg_0x98000004[13:12] = 11   CRT spec, rstn_pcie0_phy */
+	reset_control_deassert(rstc_pcie0_power);
+	reset_control_deassert(rstc_pcie0_phy);
+	reset_control_deassert(rstc_pcie0_sgmii_mdio);
+	reset_control_deassert(rstc_pcie0_phy_mdio);
+
+	/* reg_0x9800705c[6] = 1 */
+	/* ISO spec, set PCIe channel to SGMII */
+	tmp = readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+	tmp |= BIT(6);
+	writel(tmp, tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+
+	/* ### Beginning of SGMII DPHY register tuning ### */
+	tp->chip->mdio_lock(tp);
+
+	/* reg_0x9800705c[7] = 1 */
+	/* ISO spec, set internal MDIO to PCIe */
+	tmp = readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+	tmp |= BIT(7);
+	writel(tmp, tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+
+	/* reg_0x9800705c[20:16] = 00000 */
+	/* ISO spec, set internal PHY addr to SERDES_DPHY_0 */
+	tmp = readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+	tmp &= ~(BIT(20) | BIT(19) | BIT(18) | BIT(17) | BIT(16));
+	tmp |= (RTD16XX_SERDES_DPHY_0 << 16);
+	writel(tmp, tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+
+	/* # DPHY spec, 5GHz tuning */
+	r8169_mdio_write(tp, 0x4, 0x52f5);
+	r8169_mdio_write(tp, 0x5, 0xead7);
+	r8169_mdio_write(tp, 0x6, 0x0010);
+	r8169_mdio_write(tp, 0xa, 0xc653);
+	r8169_mdio_write(tp, 0x1, 0xe030);
+	r8169_mdio_write(tp, 0xd, 0xee1c);
+
+	/* reg_0x9800705c[20:16] = 00001 */
+	/* ISO spec, set internal PHY addr to SERDES_DPHY_1 */
+	tmp = readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+	tmp &= ~(BIT(20) | BIT(19) | BIT(18) | BIT(17) | BIT(16));
+	tmp |= (RTD16XX_SERDES_DPHY_1 << 16);
+	writel(tmp, tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+
+	/* tx_swing_550mv by default */
+	switch (tp->sgmii.swing) {
+	case RTD16XX_TX_SWING_190MV:
+		r8169_mdio_write(tp, 0x0, 0xd411);
+		r8169_mdio_write(tp, 0x1, 0x2277);
+		break;
+	case RTD16XX_TX_SWING_250MV:
+		r8169_mdio_write(tp, 0x0, 0xd433);
+		r8169_mdio_write(tp, 0x1, 0x2244);
+		break;
+	case RTD16XX_TX_SWING_380MV:
+		r8169_mdio_write(tp, 0x0, 0xd433);
+		r8169_mdio_write(tp, 0x1, 0x22aa);
+		break;
+	case RTD16XX_TX_SWING_550MV:	/* recommended by RDC */
+	default:
+		r8169_mdio_write(tp, 0x0, 0xd455);
+		r8169_mdio_write(tp, 0x1, 0x2828);
+	}
+
+	/* reg_0x9800705c[20:16] = 00001 */
+	/* ISO spec, set internal PHY addr to INT_PHY_ADDR */
+	tmp = readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+	tmp &= ~(BIT(20) | BIT(19) | BIT(18) | BIT(17) | BIT(16));
+	tmp |= (RTD16XX_INT_PHY_ADDR << 16);
+	writel(tmp, tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+
+	mdelay(10);		/* wait for clock stable */
+
+	/* reg_0x9800705c[5] = 0 */
+	/* reg_0x9800705c[7] = 0 */
+	/* ISO spec, set internal MDIO to GPHY */
+	tmp = readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+	tmp &= ~(BIT(7) | BIT(5));
+	writel(tmp, tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+
+	tp->chip->mdio_unlock(tp);
+	tp->ext_phy = true;
+	/* ext_phy == true now */
+
+	tmp = 0;
+	stable_ticks = 0;
+	while (stable_ticks < 10) {
+		/* # SDS spec, wait for phy ready */
+		while ((readl(tp->mmio_sdsaddr + RTD16XX_SDS_LINK) & BIT(24))
+		       == 0) {
+			stable_ticks = 0;
+			if (tmp >= 100) {
+				stable_ticks = 10;
+				pr_err("SGMII PHY not ready in 100ms\n");
+				break;
+			}
+		}
+		stable_ticks++;
+		tmp++;
+		mdelay(1);
+	}
+	pr_info("SGMII PHY is ready in %d ms", tmp);
+
+	/* reg_0x9800705c[4] = 1 */
+	/* ISO spec, set data path to SGMII */
+	tmp = readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+	tmp |= BIT(4);
+	writel(tmp, tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+
+	/* reg_0x981c8008[9:8] = 00 */
+	/* # SDS spec, SP_SDS_FRC_AN, SERDES auto mode */
+	tmp = readl(tp->mmio_sdsaddr + RTD16XX_SDS_REG02);
+	tmp &= ~(BIT(9) | BIT(8));
+	writel(tmp, tp->mmio_sdsaddr + RTD16XX_SDS_REG02);
+
+	/* # SDS spec, wait for SERDES link up */
+	tmp = 0;
+	stable_ticks = 0;
+	while (stable_ticks < 10) {
+		while ((readl(tp->mmio_sdsaddr + RTD16XX_SDS_MISC) & BIT(12))
+		       == 0) {
+			stable_ticks = 0;
+			if (tmp >= 100) {
+				stable_ticks = 10;
+				pr_err("SGMII link down in 100ms\n");
+				break;
+			}
+		}
+		stable_ticks++;
+		tmp++;
+		mdelay(1);
+	}
+	pr_info("SGMII link up in %d ms", tmp);
+
+	reset_control_put(rstc_sds_reg);
+	reset_control_put(rstc_sds);
+	reset_control_put(rstc_pcie0_power);
+	reset_control_put(rstc_pcie0_phy);
+	reset_control_put(rstc_pcie0_sgmii_mdio);
+	reset_control_put(rstc_pcie0_phy_mdio);
+	return 0;
+}
+
+static void rtd16xx_phy_iol_tuning(struct rtl8169_private *tp)
+{
+	/* for common mode voltage */
+	r8169_mdio_write(tp, 31, 0xbc0);
+	r8169_mdio_write(tp, 17,
+			 (r8169_mdio_read(tp, 17) & ~(0xff << 4)) |
+			 (0xb4 << 4));
+
+	/* for 1000 Base-T, Transmitter Distortion */
+	r8169_mdio_write(tp, 31, 0xa43);
+	r8169_mdio_write(tp, 27, 0x8082);
+	r8169_mdio_write(tp, 28,
+			 ((r8169_mdio_read(tp, 28) & ~(0xff << 8)) |
+					   (0xae << 8)));
+
+	/* for 1000 Base-T, Master Filtered Jitter */
+	r8169_mdio_write(tp, 27, 0x807c);
+	/* adjust ldvbias_busy, abiq_busy, gdac_ib_up_tm
+	 * to accelerate slew rate
+	 */
+	r8169_mdio_write(tp, 28, 0xf001);
+	/* set CP_27to25=7 and REF_27to25_L=4 to decrease jitter */
+	r8169_mdio_write(tp, 31, 0xbc5);
+	r8169_mdio_write(tp, 16, 0xc67f);
+}
+
+static void rtd16xx_phy_sram_table(struct rtl8169_private *tp)
+{
+	/* enable echo power*2 */
+	r8169_mdio_write(tp, 31, 0xa42);
+	r8169_mdio_write(tp, 22, 0x0f10);
+
+	/* Channel estimation, 100Mbps adjustment */
+	r8169_mdio_write(tp, 31, 0xa43);
+	r8169_mdio_write(tp, 27, 0x8087);	/* gain_i slope */
+	r8169_mdio_write(tp, 28, 0x42f0);	/* 0x43 => 0x42 */
+	r8169_mdio_write(tp, 27, 0x808e);	/* clen_i_c initial value */
+	r8169_mdio_write(tp, 28, 0x14a4);	/* 0x13 => 0x14 */
+	/* adc peak adjustment */
+	/* aagc_lvl_c initial value 0x3f0 => ok */
+	r8169_mdio_write(tp, 27, 0x8088);
+	r8169_mdio_write(tp, 28, 0xf0eb);	/* delta_a slope 0x1e => 0x1d */
+	/* cb0 adjustment */
+	r8169_mdio_write(tp, 27, 0x808c);	/* cb0_i_c initial value */
+	r8169_mdio_write(tp, 28, 0xef09);	/* 0x9ef => ok */
+	r8169_mdio_write(tp, 27, 0x808f);	/* delta_b slope */
+	r8169_mdio_write(tp, 28, 0xa4c6);	/* 0xa4 => ok */
+	/* DAGC adjustment */
+	r8169_mdio_write(tp, 27, 0x808a);	/* cg_i_c initial value */
+	r8169_mdio_write(tp, 28, 0x400a);	/* 0xb40 => 0xa40 */
+	r8169_mdio_write(tp, 27, 0x8092);	/* delta_g slope */
+	r8169_mdio_write(tp, 28, 0xc21e);	/* 0x0c0 => 0x0c2 */
+
+	/* 1000Mbps master adjustment */
+	/* line adjustment */
+	r8169_mdio_write(tp, 27, 0x8099);	/* gain_i slope */
+	r8169_mdio_write(tp, 28, 0x2ae0);	/* 0x2e => 0x2a */
+	r8169_mdio_write(tp, 27, 0x80a0);	/* clen_i_c initial value */
+	r8169_mdio_write(tp, 28, 0xf28f);	/* 0xfe => 0xf2 */
+	/* adc peak adjustment */
+	/* aagc_lvl_c initial value 0x3e0 => 0x470 */
+	r8169_mdio_write(tp, 27, 0x809a);
+	r8169_mdio_write(tp, 28, 0x7084);	/* delta_a slope 0x0d => 0x10 */
+	/* cb0 adjustment */
+	r8169_mdio_write(tp, 27, 0x809e);	/* cb0_i_c initial value */
+	r8169_mdio_write(tp, 28, 0xa008);	/* 0x7a1 => 0x8a0 */
+	r8169_mdio_write(tp, 27, 0x80a1);	/* delta_b slope */
+	r8169_mdio_write(tp, 28, 0x783d);	/* 0x8f => 0x78 */
+	/* DAGC adjustment */
+	r8169_mdio_write(tp, 27, 0x809c);	/* cg_i_c initial value */
+	r8169_mdio_write(tp, 28, 0x8008);	/* 0xb00 => 0x880 */
+	r8169_mdio_write(tp, 27, 0x80a4);	/* delta_g slope */
+	r8169_mdio_write(tp, 28, 0x580c);	/* 0x063 => 0x058 */
+
+	/* 1000Mbps slave adjustment */
+	/* line adjustment */
+	r8169_mdio_write(tp, 27, 0x80ab);	/* gain_i slope */
+	r8169_mdio_write(tp, 28, 0x2b4a);	/* 0x2e => 0x2b */
+	r8169_mdio_write(tp, 27, 0x80b2);	/* clen_i_c initial value */
+	r8169_mdio_write(tp, 28, 0xf47f);	/* 0xfa => 0xf4 */
+	/* adc peak adjustment */
+	/* aagc_lvl_c initial value 0x44a => 0x488 */
+	r8169_mdio_write(tp, 27, 0x80ac);
+	r8169_mdio_write(tp, 28, 0x8884);	/* delta_a slope 0x0e => 0x10 */
+	/* cb0 adjustment */
+	r8169_mdio_write(tp, 27, 0x80b0);	/* cb0_i_c initial value */
+	r8169_mdio_write(tp, 28, 0xa107);	/* 0x6a1 => 0x7a1 */
+	r8169_mdio_write(tp, 27, 0x80b3);	/* delta_b slope */
+	r8169_mdio_write(tp, 28, 0x683d);	/* 0x7f => 0x68 */
+	/* DAGC adjustment */
+	r8169_mdio_write(tp, 27, 0x80ae);	/* cg_i_c initial value */
+	r8169_mdio_write(tp, 28, 0x2006);	/* 0x760 => 0x620 */
+	r8169_mdio_write(tp, 27, 0x80b6);	/* delta_g slope */
+	r8169_mdio_write(tp, 28, 0x850c);	/* 0x090 => 0x085 */
+}
+
+static void rtd16xx_patch_gphy_uc_code(struct rtl8169_private *tp)
+{
+	u32 tmp;
+
+	/* patch for GPHY uC firmware,
+	 * adjust 1000M EEE lpi_waketx_timer = 1.3uS
+	 */
+#define PATCH_KEY_ADDR  0x8028	/* for RL6525 */
+#define PATCH_KEY       0x5600	/* for RL6525 */
+
+	/* Patch request & wait for the asserting of patch_rdy */
+	r8169_mdio_write(tp, 31, 0xb82);
+	r8169_mdio_write(tp, 16, r8169_mdio_read(tp, 16) | (BIT(4)));
+
+	tmp = 0;
+	r8169_mdio_write(tp, 31, 0xb80);
+	while ((r8169_mdio_read(tp, 16) & BIT(6)) == 0) {
+		tmp += 1;
+		mdelay(1);
+		if (tmp >= 100) {
+			pr_err("GPHY patch_rdy timeout.\n");
+			break;
+		}
+	}
+	pr_err("wait %d ms for GPHY patch_rdy. reg = 0x%x\n",
+	       tmp, r8169_mdio_read(tp, 16));
+	pr_err("patch_rdy is asserted!!\n");
+
+	/* Set patch_key & patch_lock */
+	r8169_mdio_write(tp, 31, 0);
+	r8169_mdio_write(tp, 27, PATCH_KEY_ADDR);
+	r8169_mdio_write(tp, 28, PATCH_KEY);
+	r8169_mdio_write(tp, 27, PATCH_KEY_ADDR);
+	pr_err("check patch key = %04x\n", r8169_mdio_read(tp, 28));
+	r8169_mdio_write(tp, 27, 0xb82e);
+	r8169_mdio_write(tp, 28, 0x0001);
+
+	/* uC patch code, released by Digital Designer */
+	r8169_mdio_write(tp, 0x1f, 0x0000);
+	r8169_mdio_write(tp, 27, 0xb820);
+	r8169_mdio_write(tp, 28, 0x0290);
+
+	r8169_mdio_write(tp, 27, 0xa012);
+	r8169_mdio_write(tp, 28, 0x0000);
+
+	r8169_mdio_write(tp, 27, 0xa014);
+	r8169_mdio_write(tp, 28, 0x2c04);
+	r8169_mdio_write(tp, 28, 0x2c06);
+	r8169_mdio_write(tp, 28, 0x2c09);
+	r8169_mdio_write(tp, 28, 0x2c0c);
+	r8169_mdio_write(tp, 28, 0xd093);
+	r8169_mdio_write(tp, 28, 0x2265);
+	r8169_mdio_write(tp, 28, 0x9e20);
+	r8169_mdio_write(tp, 28, 0xd703);
+	r8169_mdio_write(tp, 28, 0x2502);
+	r8169_mdio_write(tp, 28, 0x9e40);
+	r8169_mdio_write(tp, 28, 0xd700);
+	r8169_mdio_write(tp, 28, 0x0800);
+	r8169_mdio_write(tp, 28, 0x9e80);
+	r8169_mdio_write(tp, 28, 0xd70d);
+	r8169_mdio_write(tp, 28, 0x202e);
+
+	r8169_mdio_write(tp, 27, 0xa01a);
+	r8169_mdio_write(tp, 28, 0x0000);
+
+	r8169_mdio_write(tp, 27, 0xa006);
+	r8169_mdio_write(tp, 28, 0x002d);
+
+	r8169_mdio_write(tp, 27, 0xa004);
+	r8169_mdio_write(tp, 28, 0x0507);
+
+	r8169_mdio_write(tp, 27, 0xa002);
+	r8169_mdio_write(tp, 28, 0x0501);
+
+	r8169_mdio_write(tp, 27, 0xa000);
+	r8169_mdio_write(tp, 28, 0x1264);
+
+	r8169_mdio_write(tp, 27, 0xb820);
+	r8169_mdio_write(tp, 28, 0x0210);
+
+	r8169_mdio_write(tp, 31, 0);
+
+	mdelay(10);
+
+	/* Clear patch_key & patch_lock */
+	r8169_mdio_write(tp, 27, PATCH_KEY_ADDR);
+	r8169_mdio_write(tp, 28, 0);
+	r8169_mdio_write(tp, 31, 0xb82);
+	r8169_mdio_write(tp, 23, 0);
+
+	/* Release patch request & wait for the deasserting of patch_rdy. */
+	r8169_mdio_write(tp, 31, 0xb82);
+	r8169_mdio_write(tp, 16, r8169_mdio_read(tp, 16) & ~(BIT(4)));
+
+	tmp = 0;
+	r8169_mdio_write(tp, 31, 0xb80);
+	while ((r8169_mdio_read(tp, 16) & BIT(6)) != 0) {
+		tmp += 1;
+		mdelay(1);
+		if (tmp >= 100) {
+			pr_err("GPHY patch_rdy timeout.\n");
+			break;
+		}
+	}
+	pr_err("wait %d ms for GPHY patch_rdy. reg = 0x%x\n",
+	       tmp, r8169_mdio_read(tp, 16));
+
+	pr_err("\npatch_rdy is de-asserted!!\n");
+	pr_err("GPHY uC code patched.\n");
+}
+
+static void rtd16xx_mdio_init(struct rtl8169_private *tp)
+{
+	u32 tmp;
+	struct pinctrl *p_sgmii_mdio;
+	struct pinctrl_state *ps_sgmii_mdio;
+	void __iomem *ioaddr = tp->mmio_addr;
+
+	/* ISO spec, ETN_PHY_INTR,
+	 * wait interrupt from PHY and it means MDIO is ready
+	 */
+	tmp = 0;
+	while ((readl(tp->mmio_clkaddr + ISO_UMSK_ISR) & BIT(27)) == 0) {
+		tmp += 1;
+		mdelay(1);
+		if (tmp >= 100) {
+			pr_err("PHY PHY_Status timeout.\n");
+			break;
+		}
+	}
+	pr_info("wait %d ms for PHY interrupt. UMSK_ISR = 0x%x\n",
+		tmp, readl(tp->mmio_clkaddr + ISO_UMSK_ISR));
+
+	tp->chip->mdio_lock(tp);
+	/* In ByPass mode,
+	 * SW need to handle the EPHY Status check ,
+	 * OTP data update and EPHY fuse_rdy setting.
+	 */
+	/* PHY will stay in state 1 mode */
+	r8169_mdio_write(tp, 31, 0x0a42);
+	tmp = 0;
+	while ((r8169_mdio_read(tp, 16) & 0x07) != 0x1) {
+		tmp += 1;
+		mdelay(1);
+		if (tmp >= 2000) {
+			pr_err("PHY status is not 0x1 in bypass mode, current = 0x%02x\n",
+			       r8169_mdio_read(tp, 16) & 0x07);
+			break;
+		}
+	}
+
+	/* fill fuse_rdy & rg_ext_ini_done */
+	r8169_mdio_write(tp, 31, 0x0a46);
+	r8169_mdio_write(tp, 20,
+			 r8169_mdio_read(tp, 20) | (BIT(1) | BIT(0)));
+
+	/* init_autoload_done = 1 */
+	tmp = rtl_ocp_read(tp, 0xe004);
+	tmp |= BIT(7);
+	rtl_ocp_write(tp, 0xe004, tmp);
+
+	/* ee_mode = 3 */
+	RTL_W8(CFG9346, CFG9346_UNLOCK);
+
+	/* wait LAN-ON */
+	r8169_mdio_write(tp, 31, 0x0a42);
+	tmp = 0;
+	do {
+		tmp += 1;
+		mdelay(1);
+		if (tmp >= 2000) {
+			pr_err("PHY status is not 0x3, current = 0x%02x\n",
+			       r8169_mdio_read(tp, 16) & 0x07);
+			break;
+		}
+	} while ((r8169_mdio_read(tp, 16) & 0x07) != 0x3);
+	pr_info("wait %d ms for PHY ready, current = 0x%x\n",
+		tmp, r8169_mdio_read(tp, 16));
+
+	/* adjust PHY SRAM table */
+	rtd16xx_phy_sram_table(tp);
+
+	/* GPHY patch code */
+	rtd16xx_patch_gphy_uc_code(tp);
+
+	/* adjust PHY electrical characteristics */
+	rtd16xx_phy_iol_tuning(tp);
+
+	/* load OTP contents (RC-K, R-K, Amp-K, and Bias-K)
+	 * RC-K:        0x980174F8[27:24]
+	 * R-K:         0x98017500[18:15]
+	 * Amp-K:       0x980174FC[15:0]
+	 * Bias-K:      0x980174FC[31:16]
+	 */
+	rtd16xx_load_otp_content(tp);
+
+	tp->chip->mdio_unlock(tp);
+
+	if (tp->output_mode == OUTPUT_EMBEDDED_PHY) {
+		/* Init PHY path */
+		/* reg_0x9800705c[5] = 0 */
+		/* reg_0x9800705c[7] = 0 */
+		/* ISO spec, set internal MDIO to access PHY */
+		tmp = readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+		tmp &= ~(BIT(7) | BIT(5));
+		writel(tmp, tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+
+		/* reg_0x9800705c[4] = 0 */
+		/* ISO spec, set data path to access PHY */
+		tmp = readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+		tmp &= ~(BIT(4));
+		writel(tmp, tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+
+		/* ETN spec, GMAC data path select MII-like(embedded GPHY),
+		 * not SGMII(external PHY)
+		 */
+		tmp = rtl_ocp_read(tp, 0xea34) & ~(BIT(0) | BIT(1));
+		tmp |= BIT(1);	/* MII */
+		rtl_ocp_write(tp, 0xea34, tmp);
+	} else {
+		/* SGMII */
+		/* ETN spec, adjust MDC freq=2.5MHz */
+		tmp = rtl_ocp_read(tp, 0xDE30) & ~(BIT(7) | BIT(6));
+		rtl_ocp_write(tp, 0xDE30, tmp);
+		/* ETN spec, set external PHY addr */
+		tmp = rtl_ocp_read(tp, 0xDE24) & ~(0x1f << 0);
+		rtl_ocp_write(tp, 0xDE24, tmp | (tp->ext_phy_id & 0x1f));
+		/* ISO mux spec, GPIO29 is set to MDC pin */
+		/* ISO mux spec, GPIO46 is set to MDIO pin */
+		p_sgmii_mdio = devm_pinctrl_get(&tp->pdev->dev);
+		ps_sgmii_mdio = pinctrl_lookup_state(p_sgmii_mdio, "sgmii");
+		pinctrl_select_state(p_sgmii_mdio, ps_sgmii_mdio);
+
+		/* check if external PHY is available */
+		pr_info("Searching external PHY...");
+		tp->chip->mdio_lock(tp);
+		tp->ext_phy = true;
+		r8169_mdio_write(tp, 31, 0x0a43);
+		tmp = 0;
+		while (r8169_mdio_read(tp, 31) != 0xa43) {
+			pr_info(".");
+			tmp += 1;
+			mdelay(1);
+			if (tmp >= 2000) {
+				pr_err("\n External SGMII PHY not found, current = 0x%02x\n",
+				       r8169_mdio_read(tp, 31));
+				return;
+			}
+		}
+		if (tmp < 2000)
+			pr_info("found.\n");
+
+		/* lower SGMII TX swing of RTL8211FS to reduce EMI */
+		r8169_mdio_write(tp, 31, 0x0dcd);
+		/* TX swing = 470mV, default value */
+		r8169_mdio_write(tp, 16, 0x104e);
+
+		tp->ext_phy = false;
+		tp->chip->mdio_unlock(tp);
+
+		/* ETN spec, GMAC data path select SGMII(external PHY),
+		 * not MII-like(embedded GPHY)
+		 */
+		tmp = rtl_ocp_read(tp, 0xea34) & ~(BIT(0) | BIT(1));
+		tmp |= BIT(1) | BIT(0);	/* SGMII */
+		rtl_ocp_write(tp, 0xea34, tmp);
+
+		if (rtd16xx_serdes_init(tp) != 0)
+			pr_err("SERDES init failed\n");
+		/* ext_phy == true now */
+
+		/* SDS spec, auto update SGMII link capability */
+		tmp = readl(tp->mmio_sdsaddr + RTD16XX_SDS_DEBUG);
+		tmp |= BIT(2);
+		writel(tmp, tp->mmio_sdsaddr + RTD16XX_SDS_DEBUG);
+
+		/* enable 8b/10b symbol error
+		 * even it is only valid in 1000Mbps
+		 */
+		r8169_mdio_write(tp, 31, 0x0dcf);
+		r8169_mdio_write(tp, 16,
+				 (r8169_mdio_read(tp, 16) &
+				 ~(BIT(2) | BIT(1) | BIT(0))));
+		r8169_mdio_read(tp, 17);	/* dummy read */
+	}
+}
+
+static void rtd16xx_eee_set(struct rtl8169_private *tp, bool enable)
+{
+	if (enable) {
+		tp->chip->mdio_lock(tp);
+		if (tp->output_mode == OUTPUT_EMBEDDED_PHY) {
+			/* 1000M/100M EEE capability */
+			r8169_mmd_write(tp, 0x7, 60, (BIT(2) | BIT(1)));
+			/* 10M EEE */
+			r8169_mdio_write(tp, 31, 0xa43);
+			r8169_mdio_write(tp, 25,
+					 r8169_mdio_read(tp, 25) |
+					 (BIT(4) | BIT(2)));
+			/* disable dynamic RX power in PHY */
+			r8169_mdio_write(tp, 31, 0xbd0);
+			r8169_mdio_write(tp, 21,
+					 (r8169_mdio_read(tp, 21) & ~BIT(8)) |
+					 BIT(9));
+		} else {	/* SGMII */
+			/* 1000M & 100M EEE capability */
+			r8169_mmd_write(tp, 0x7, 60, (BIT(2) | BIT(1)));
+			/* 10M EEE */
+			r8169_mdio_write(tp, 31, 0xa43);
+			r8169_mdio_write(tp, 25,
+					 r8169_mdio_read(tp, 25) |
+					 (BIT(4) | BIT(2)));
+		}
+		tp->chip->mdio_unlock(tp);
+		/* EEE MAC mode */
+		rtl_ocp_write(tp, 0xe040,
+			      rtl_ocp_read(tp, 0xe040) | (BIT(1) | BIT(0)));
+		/* EEE+ MAC mode */
+		rtl_ocp_write(tp, 0xe080, rtl_ocp_read(tp, 0xe080) | BIT(1));
+	} else {
+		tp->chip->mdio_lock(tp);
+		if (tp->output_mode == OUTPUT_EMBEDDED_PHY) {
+			/* no EEE capability */
+			r8169_mmd_write(tp, 0x7, 60, 0);
+			/* 10M EEE */
+			r8169_mdio_write(tp, 31, 0xa43);
+			r8169_mdio_write(tp, 25,
+					 r8169_mdio_read(tp, 25) &
+					 ~(BIT(4) | BIT(2)));
+		} else {	/* SGMII */
+			/* no EEE capability */
+			r8169_mmd_write(tp, 0x7, 60, 0);
+			/* 10M EEE */
+			r8169_mdio_write(tp, 31, 0xa43);
+			r8169_mdio_write(tp, 25,
+					 r8169_mdio_read(tp, 25) &
+					 ~(BIT(4) | BIT(2)));
+		}
+		tp->chip->mdio_unlock(tp);
+		/* reset to default value */
+		rtl_ocp_write(tp, 0xe040, rtl_ocp_read(tp, 0xe040) | BIT(13));
+		/* EEE MAC mode */
+		rtl_ocp_write(tp, 0xe040,
+			      rtl_ocp_read(tp, 0xe040) & ~(BIT(1) | BIT(0)));
+		/* EEE+ MAC mode */
+		rtl_ocp_write(tp, 0xe080, rtl_ocp_read(tp, 0xe080) & ~BIT(1));
+	}
+}
+
+static void rtd16xx_led_set(struct rtl8169_private *tp, bool enable)
+{
+	u32 val;
+
+	if (enable) {
+		val = readl(tp->mmio_pinmuxaddr + RTD16XX_ISO_TESTMUX_MUXPAD0)
+			& ~0x3C000000;
+		writel(val | 0x14000000, tp->mmio_pinmuxaddr +
+		       RTD16XX_ISO_TESTMUX_MUXPAD0);
+		val = readl(tp->mmio_pinmuxaddr + RTD16XX_ISO_TESTMUX_MUXPAD2)
+			& ~0x38000000;
+		writel(val | 0x18000000, tp->mmio_pinmuxaddr +
+		       RTD16XX_ISO_TESTMUX_MUXPAD2);
+	} else {
+		writel(readl(tp->mmio_pinmuxaddr + RTD16XX_ISO_TESTMUX_MUXPAD0)
+		       & ~0x3C000000,
+		       tp->mmio_pinmuxaddr + RTD16XX_ISO_TESTMUX_MUXPAD0);
+		writel(readl(tp->mmio_pinmuxaddr + RTD16XX_ISO_TESTMUX_MUXPAD2)
+		       & ~0x38000000,
+		       tp->mmio_pinmuxaddr + RTD16XX_ISO_TESTMUX_MUXPAD2);
+	}
+}
+
+static void rtd16xx_dump_regs(struct seq_file *m, struct rtl8169_private *tp)
+{
+	seq_printf(m, "ISO_UMSK_ISR\t[0x98007004] = %08x\n",
+		   readl(tp->mmio_clkaddr + ISO_UMSK_ISR));
+	seq_printf(m, "ISO_PWRCUT_ETN\t[0x9800705c] = %08x\n",
+		   readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN));
+	seq_printf(m, "ISO_ETN_TESTIO\t[0x98007060] = %08x\n",
+		   readl(tp->mmio_clkaddr + ISO_ETN_TESTIO));
+	seq_printf(m, "ETN_RESET_CTRL\t[0x98007088] = %08x\n",
+		   readl(tp->mmio_clkaddr + ISO_SOFT_RESET));
+	seq_printf(m, "ETN_CLK_CTRL\t[0x9800708c] = %08x\n",
+		   readl(tp->mmio_clkaddr + ISO_CLOCK_ENABLE));
+	if (tp->output_mode != OUTPUT_EMBEDDED_PHY) {
+		seq_printf(m, "SDS_REG02\t[0x981c8008] = %08x\n",
+			   readl(tp->mmio_sdsaddr + RTD16XX_SDS_REG02));
+		seq_printf(m, "SDS_MISC\t\t[0x981c9804] = %08x\n",
+			   readl(tp->mmio_sdsaddr + RTD16XX_SDS_MISC));
+		seq_printf(m, "SDS_LINK\t\t[0x981c980c] = %08x\n",
+			   readl(tp->mmio_sdsaddr + RTD16XX_SDS_LINK));
+		seq_printf(m, "SDS_DEBUG\t\t[0x981c9810] = %08x\n",
+			   readl(tp->mmio_sdsaddr + RTD16XX_SDS_DEBUG));
+	}
+}
+
+static void rtd16xx_dump_var(struct seq_file *m, struct rtl8169_private *tp)
+{
+	seq_printf(m, "sgmii_swing\t%d\n", tp->sgmii.swing);
+}
+
+/******************* END of RTD16XX ****************************/
+
+/* RTD13XX */
+static void rtd13xx_mdio_lock(struct rtl8169_private *tp)
+{
+	rtd139x_mdio_lock(tp);
+}
+
+static void rtd13xx_mdio_unlock(struct rtl8169_private *tp)
+{
+	rtd139x_mdio_unlock(tp);
+}
+
+static void rtd13xx_reset_phy_gmac(struct rtl8169_private *tp)
+{
+	u32 tmp;
+	struct clk *clk_etn_sys  = clk_get(&tp->pdev->dev, "etn_sys");
+	struct clk *clk_etn_250m = clk_get(&tp->pdev->dev, "etn_250m");
+	struct reset_control *rstc_gphy =
+		reset_control_get_exclusive(&tp->pdev->dev, "gphy");
+	struct reset_control *rstc_gmac =
+		reset_control_get_exclusive(&tp->pdev->dev, "gmac");
+
+	clk_prepare_enable(clk_etn_sys);
+	clk_prepare_enable(clk_etn_250m);
+
+	/* reg_0x9800708c[12:11] = 00 */
+	/* ISO spec, clock enable bit for etn clock & etn 250MHz */
+	clk_disable_unprepare(clk_etn_sys);
+	clk_disable_unprepare(clk_etn_250m);
+
+	/* reg_0x98007088[10:9] = 00 */
+	/* ISO spec, rstn_gphy & rstn_gmac */
+	reset_control_assert(rstc_gphy);
+	reset_control_assert(rstc_gmac);
+
+	/* reg_0x98007060[1] = 1 */
+	/* ISO spec, bypass mode enable */
+	tmp = readl(tp->mmio_clkaddr + ISO_ETN_TESTIO);
+	tmp |= BIT(1);
+	writel(tmp, tp->mmio_clkaddr + ISO_ETN_TESTIO);
+
+	/* reg_0x9800705c[5] = 0 */
+	/* ISO spec, default value and specify internal/external PHY ID as 1 */
+	tmp = readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+	tmp &= ~((0x1f << 21) | (0x1f << 16) | BIT(5));
+	tmp |= (0x1 << 21) | (0x1 << 16);
+	writel(tmp, tp->mmio_clkaddr + ISO_POWERCUT_ETN);
+
+	mdelay(1);
+
+	/* release resource */
+	reset_control_put(rstc_gphy);
+	reset_control_put(rstc_gmac);
+}
+
+static void rtd13xx_acp_init(struct rtl8169_private *tp)
+{
+	u32 tmp;
+
+	/* SBX spec, Select ETN access DDR path. */
+	if (tp->acp_enable) {
+		/* reg_0x9801c20c[6] = 1 */
+		/* SBX spec, Mask ETN_ALL to SB3 DBUS REQ */
+		tmp = readl(tp->mmio_sbxaddr +
+			    RTD13XX_SBX_SB3_CHANNEL_REQ_MASK);
+		tmp |= BIT(6);
+		writel(tmp, tp->mmio_sbxaddr +
+		       RTD13XX_SBX_SB3_CHANNEL_REQ_MASK);
+
+		pr_info("wait all SB3 access finished...");
+		tmp = 0;
+		while (0 != (BIT(6) &
+		       readl(tp->mmio_sbxaddr +
+			     RTD13XX_SBX_SB3_CHANNEL_REQ_BUSY))) {
+			mdelay(10);
+			tmp += 10;
+			if (tmp >= 100) {
+				pr_err("\n wait SB3 access failed (wait %d ms)\n",
+				       tmp);
+				break;
+			}
+		}
+		if (tmp < 100)
+			pr_info("done.\n");
+
+		/* reg_0x9801d100[29] = 0 */
+		/* SCPU wrapper spec, CLKACP division, 0 = div 2, 1 = div 3 */
+		tmp = readl(tp->mmio_sbxaddr + RTD13XX_SC_WRAP_CRT_CTRL);
+		tmp &= ~(BIT(29));
+		writel(tmp, tp->mmio_sbxaddr + RTD13XX_SC_WRAP_CRT_CTRL);
+
+		/* reg_0x9801d124[1:0] = 00 */
+		/* SCPU wrapper spec, ACP master active, 0 = active */
+		tmp = readl(tp->mmio_sbxaddr + RTD13XX_SC_WRAP_INTERFACE_EN);
+		tmp &= ~(BIT(1) | BIT(0));
+		writel(tmp, tp->mmio_sbxaddr + RTD13XX_SC_WRAP_INTERFACE_EN);
+
+		/* reg_0x9801d100[30] = 1 */
+		/* SCPU wrapper spec, dy_icg_en_acp */
+		tmp = readl(tp->mmio_sbxaddr + RTD13XX_SC_WRAP_CRT_CTRL);
+		tmp |= BIT(30);
+		writel(tmp, tp->mmio_sbxaddr + RTD13XX_SC_WRAP_CRT_CTRL);
+
+		/* reg_0x9801d100[21] = 1 */
+		/* SCPU wrapper spec, ACP CLK enable */
+		tmp = readl(tp->mmio_sbxaddr + RTD13XX_SC_WRAP_CRT_CTRL);
+		tmp |= BIT(21);
+		writel(tmp, tp->mmio_sbxaddr + RTD13XX_SC_WRAP_CRT_CTRL);
+
+		/* reg_0x9801d100[14] = 1 */
+		/* Do not apply reset to ACP port axi3 master */
+		tmp = readl(tp->mmio_sbxaddr + RTD13XX_SC_WRAP_CRT_CTRL);
+		tmp |= BIT(14);
+		writel(tmp, tp->mmio_sbxaddr + RTD13XX_SC_WRAP_CRT_CTRL);
+
+		/* reg_0x9801d800[3:0] = 0111 */
+		/* reg_0x9801d800[7:4] = 0111 */
+		/* reg_0x9801d800[9:8] = 00 */
+		/* reg_0x9801d800[20:16] = 01100 */
+		/* reg_0x9801d800[28:24] = 01110 */
+		/* Configure control of ACP port */
+		tmp = readl(tp->mmio_sbxaddr + RTD13XX_SC_WRAP_ACP_CTRL);
+		tmp &= ~((0x1f << 24) | (0x1f << 16) | (0x1 << 9) |
+			(0xf << 4) | (0xf << 0));
+		tmp |= (0x0e << 24) | (0x0c << 16) | (0x1 << 9) | (0x7 << 4) |
+			(0x7 << 0);
+		writel(tmp, tp->mmio_sbxaddr + RTD13XX_SC_WRAP_ACP_CTRL);
+
+		/* reg_0x9801d030[28] = 1 */
+		/* SCPU wrapper spec, dy_icg_en_acp */
+		tmp = readl(tp->mmio_sbxaddr + RTD13XX_SC_WRAP_ACP_CRT_CTRL);
+		tmp |= BIT(28);
+		writel(tmp, tp->mmio_sbxaddr + RTD13XX_SC_WRAP_ACP_CRT_CTRL);
+
+		/* reg_0x9801d030[16] = 1 */
+		/* ACP CLK Enable for acp of scpu_chip_top */
+		tmp = readl(tp->mmio_sbxaddr + RTD13XX_SC_WRAP_ACP_CRT_CTRL);
+		tmp |= BIT(16);
+		writel(tmp, tp->mmio_sbxaddr + RTD13XX_SC_WRAP_ACP_CRT_CTRL);
+
+		/* reg_0x9801d030[0] = 1 */
+		/* Do not apply reset to ACP port axi3 master */
+		tmp = readl(tp->mmio_sbxaddr + RTD13XX_SC_WRAP_ACP_CRT_CTRL);
+		tmp |= BIT(0);
+		writel(tmp, tp->mmio_sbxaddr + RTD13XX_SC_WRAP_ACP_CRT_CTRL);
+
+		/* reg_0x9801c814[17] = 1 */
+		/* through ACP to SCPU_ACP */
+		tmp = readl(tp->mmio_sbxaddr + RTD13XX_SBX_ACP_MISC_CTRL);
+		tmp |= BIT(17);
+		writel(tmp, tp->mmio_sbxaddr + RTD13XX_SBX_ACP_MISC_CTRL);
+
+		/* SBX spec, Remove mask ETN_ALL to ACP DBUS REQ */
+		tmp = readl(tp->mmio_sbxaddr +
+			    RTD13XX_SBX_ACP_CHANNEL_REQ_MASK);
+		tmp &= ~BIT(1);
+		writel(tmp, tp->mmio_sbxaddr +
+		       RTD13XX_SBX_ACP_CHANNEL_REQ_MASK);
+
+		pr_info("ARM ACP on\n.");
+	} else {
+		/* SBX spec, Mask ETN_ALL to ACP DBUS REQ */
+		tmp = readl(tp->mmio_sbxaddr +
+			    RTD13XX_SBX_ACP_CHANNEL_REQ_MASK);
+		tmp |= BIT(1);
+		writel(tmp, tp->mmio_sbxaddr +
+		       RTD13XX_SBX_ACP_CHANNEL_REQ_MASK);
+
+		pr_info("wait all ACP access finished...");
+		tmp = 0;
+		while (0 != (BIT(1) &
+		       readl(tp->mmio_sbxaddr +
+			     RTD13XX_SBX_ACP_CHANNEL_REQ_BUSY))) {
+			mdelay(1);
+			tmp += 1;
+			if (tmp >= 100) {
+				pr_err("\n ACP channel is still busy (wait %d ms)\n",
+				       tmp);
+				break;
+			}
+		}
+		if (tmp < 100)
+			pr_info("done.\n");
+
+		/* SCPU wrapper spec, Inactive MP4 AINACTS signal */
+		tmp = readl(tp->mmio_sbxaddr + RTD13XX_SC_WRAP_INTERFACE_EN);
+		tmp |= (BIT(1) | BIT(0));
+		writel(tmp, tp->mmio_sbxaddr + RTD13XX_SC_WRAP_INTERFACE_EN);
+
+		/* SCPU wrapper spec, nACPRESET_DVFS & CLKENACP_DVFS */
+		tmp = readl(tp->mmio_sbxaddr + RTD13XX_SC_WRAP_CRT_CTRL);
+		tmp &= ~(BIT(21) | BIT(14));
+		writel(tmp, tp->mmio_sbxaddr + RTD13XX_SC_WRAP_CRT_CTRL);
+
+		/* SCPU wrapper spec, nACPRESET & CLKENACP */
+		tmp = readl(tp->mmio_sbxaddr + RTD13XX_SC_WRAP_ACP_CRT_CTRL);
+		tmp &= ~(BIT(16) | BIT(0));
+		writel(tmp, tp->mmio_sbxaddr + RTD13XX_SC_WRAP_ACP_CRT_CTRL);
+
+		/* reg_0x9801c814[17] = 0 */
+		/* SBX spec, Switch ETN_ALL to DC_SYS path */
+		tmp = readl(tp->mmio_sbxaddr + RTD13XX_SBX_ACP_MISC_CTRL);
+		tmp &= ~BIT(17);
+		writel(tmp, tp->mmio_sbxaddr + RTD13XX_SBX_ACP_MISC_CTRL);
+
+		/* SBX spec, Remove mask ETN_ALL to SB3 DBUS REQ */
+		tmp = readl(tp->mmio_sbxaddr +
+			    RTD13XX_SBX_SB3_CHANNEL_REQ_MASK);
+		tmp &= ~BIT(6);
+		writel(tmp, tp->mmio_sbxaddr +
+		       RTD13XX_SBX_SB3_CHANNEL_REQ_MASK);
+
+		pr_info("ARM ACP off\n.");
+	}
+}
+
+static void rtd13xx_pll_clock_init(struct rtl8169_private *tp)
+{
+	u32 tmp;
+	struct clk *clk_etn_sys  = clk_get(&tp->pdev->dev, "etn_sys");
+	struct clk *clk_etn_250m = clk_get(&tp->pdev->dev, "etn_250m");
+	struct reset_control *rstc_gphy =
+		reset_control_get_exclusive(&tp->pdev->dev, "gphy");
+	struct reset_control *rstc_gmac =
+		reset_control_get_exclusive(&tp->pdev->dev, "gmac");
+
+	/* reg_0x98007004[27] = 1 */
+	/* ISO spec, ETN_PHY_INTR, clear ETN interrupt for ByPassMode */
+	writel(BIT(27), tp->mmio_clkaddr + ISO_UMSK_ISR);
+
+	/* reg_0x98007088[10] = 1 */
+	/* ISO spec, reset bit of fephy */
+	reset_control_deassert(rstc_gphy);
+
+	mdelay(1);	/* wait 1ms for PHY PLL stable */
+
+	/* reg_0x98007060[1] = 1 */
+	/* ISO spec, bypass mode enable */
+	tmp = readl(tp->mmio_clkaddr + ISO_ETN_TESTIO);
+	tmp |= BIT(1);
+	writel(tmp, tp->mmio_clkaddr + ISO_ETN_TESTIO);
+
+	/* reg_0x98007088[9] = 1 */
+	/* ISO spec, reset bit of gmac */
+	reset_control_deassert(rstc_gmac);
+
+	/* reg_0x9800708c[12:11] = 11 */
+	/* ISO spec, clock enable bit for etn clock & etn 250MHz */
+	clk_prepare_enable(clk_etn_sys);
+	clk_prepare_enable(clk_etn_250m);
+
+	mdelay(10);	/* wait 10ms for GMAC uC to be stable */
+
+	/* release resource */
+	reset_control_put(rstc_gphy);
+	reset_control_put(rstc_gmac);
+}
+
+static void rtd13xx_load_otp_content(struct rtl8169_private *tp)
+{
+	u32 otp;
+	u16 tmp;
+	u8 *buf;
+
+	buf = r8169soc_read_otp(tp, "para");
+	if (IS_ERR(buf))
+		goto set_idac;
+
+	/* enable 0x980174FC[4] */
+	/* R-K 0x980174FC[3:0] */
+	otp = *buf;
+	if (otp & BIT(4)) {
+		tmp = otp ^ RTD13XX_R_K_DEFAULT;
+		r8169_mdio_write(tp, 31, 0xbc0);
+		r8169_mdio_write(tp, 20,
+				 ((r8169_mdio_read(tp, 20) & ~(0x1f << 0)) |
+						   tmp));
+	}
+
+	kfree(buf);
+
+set_idac:
+
+	buf = r8169soc_read_otp(tp, "idac");
+	if (IS_ERR(buf))
+		return;
+
+	/* Amp-K 0x98017500[7:0] */
+	otp = *buf;
+	tmp = otp ^ RTD13XX_IDAC_FINE_DEFAULT;
+	r8169_mdio_write(tp, 31, 0xbc0);
+	r8169_mdio_write(tp, 23,
+			 ((r8169_mdio_read(tp, 23) & ~(0xff << 0)) | tmp));
+
+	kfree(buf);
+}
+
+static void rtd13xx_phy_iol_tuning(struct rtl8169_private *tp)
+{
+	u16 tmp;
+
+	/* rs_offset=rsw_offset=0xc */
+	tmp = 0xcc << 8;
+	r8169_mdio_write(tp, 31, 0xbc0);
+	r8169_mdio_write(tp, 20,
+			 ((r8169_mdio_read(tp, 20) & ~(0xff << 8)) | tmp));
+
+	/* 100M Tr/Tf */
+	/* reg_cf_l=0x2 */
+	tmp = 0x2 << 11;
+	r8169_mdio_write(tp, 31, 0xbd0);
+	r8169_mdio_write(tp, 17,
+			 ((r8169_mdio_read(tp, 17) & ~(0x7 << 11)) | tmp));
+
+	/* 100M Swing */
+	/* idac_fine_mdix, idac_fine_mdi */
+	tmp = 0x55 << 0;
+	r8169_mdio_write(tp, 31, 0xbc0);
+	r8169_mdio_write(tp, 23,
+			 ((r8169_mdio_read(tp, 23) & ~(0xff << 0)) | tmp));
+}
+
+static void rtd13xx_mdio_init(struct rtl8169_private *tp)
+{
+	u32 tmp;
+	struct pinctrl *p_rgmii_mdio;
+	struct pinctrl_state *ps_rgmii_mdio;
+	void __iomem *ioaddr = tp->mmio_addr;
+	const struct soc_device_attribute rtk_soc_rtd13xx_a00[] = {
+		{
+			.family = "Realtek Hank",
+			.revision = "A00",
+		},
+		{
+		/* empty */
+		}
+	};
+
+	/* ETN_PHY_INTR, wait interrupt from PHY and it means MDIO is ready */
+	tmp = 0;
+	while ((readl(tp->mmio_clkaddr + ISO_UMSK_ISR) & BIT(27)) == 0) {
+		tmp += 1;
+		mdelay(1);
+		if (tmp >= 100) {
+			pr_err("PHY PHY_Status timeout.\n");
+			break;
+		}
+	}
+	pr_info("wait %d ms for PHY interrupt. UMSK_ISR = 0x%x\n",
+		tmp, readl(tp->mmio_clkaddr + ISO_UMSK_ISR));
+
+	tp->chip->mdio_lock(tp);
+	/* In ByPass mode,
+	 * SW need to handle the EPHY Status check ,
+	 * OTP data update and EPHY fuse_rdy setting.
+	 */
+	/* PHY will stay in state 1 mode */
+	r8169_mdio_write(tp, 31, 0x0a42);
+	tmp = 0;
+	while (0x1 != (r8169_mdio_read(tp, 16) & 0x07)) {
+		tmp += 1;
+		mdelay(1);
+		if (tmp >= 2000) {
+			pr_err("PHY status is not 0x1 in bypass mode, current = 0x%02x\n",
+			       (r8169_mdio_read(tp, 16) & 0x07));
+			break;
+		}
+	}
+
+	/* fix A00 known issue */
+	if (soc_device_match(rtk_soc_rtd13xx_a00)) {
+		/* fix AFE RX power as 1 */
+		r8169_mdio_write(tp, 31, 0x0bd0);
+		r8169_mdio_write(tp, 21, 0x7201);
+	}
+
+	/* ETN spec. set MDC freq = 8.9MHZ */
+	tmp = rtl_ocp_read(tp, 0xde10) & ~(BIT(7) | BIT(6));
+	tmp |= BIT(6); /* MDC freq = 8.9MHz */
+	rtl_ocp_write(tp, 0xde10, tmp);
+
+	/* fill fuse_rdy & rg_ext_ini_done */
+	r8169_mdio_write(tp, 31, 0x0a46);
+	r8169_mdio_write(tp, 20,
+			 (r8169_mdio_read(tp, 20) | (BIT(1) | BIT(0))));
+
+	/* init_autoload_done = 1 */
+	tmp = rtl_ocp_read(tp, 0xe004);
+	tmp |= BIT(7);
+	rtl_ocp_write(tp, 0xe004, tmp);
+
+	/* ee_mode = 3 */
+	RTL_W8(CFG9346, CFG9346_UNLOCK);
+
+	/* wait LAN-ON */
+	r8169_mdio_write(tp, 31, 0x0a42);
+	tmp = 0;
+	do {
+		tmp += 1;
+		mdelay(1);
+		if (tmp >= 2000) {
+			pr_err("PHY status is not 0x3, current = 0x%02x\n",
+			       (r8169_mdio_read(tp, 16) & 0x07));
+			break;
+		}
+	} while (0x3 != (r8169_mdio_read(tp, 16) & 0x07));
+	pr_info("wait %d ms for PHY ready, current = 0x%x\n",
+		tmp, r8169_mdio_read(tp, 16));
+
+	/* adjust PHY electrical characteristics */
+	rtd13xx_phy_iol_tuning(tp);
+
+	/* load OTP contents (R-K, Amp)
+	 * R-K:		0x980174FC[4:0]
+	 * Amp:		0x98017500[7:0]
+	 */
+	rtd13xx_load_otp_content(tp);
+
+	tp->chip->mdio_unlock(tp);
+
+	switch (tp->output_mode) {
+	case OUTPUT_EMBEDDED_PHY:
+		/* ISO spec, set data path to FEPHY */
+		tmp = rtl_ocp_read(tp, 0xea30);
+		tmp &= ~(BIT(0));
+		rtl_ocp_write(tp, 0xea30, tmp);
+
+		tmp = rtl_ocp_read(tp, 0xea34);
+		tmp &= ~(BIT(1) | BIT(0));
+		tmp |= BIT(1);  /* FEPHY */
+		rtl_ocp_write(tp, 0xea34, tmp);
+
+		/* LED high active circuit */
+		/* output current: 4mA */
+		tmp = readl(tp->mmio_pinmuxaddr + RTD13XX_ISO_TESTMUX_PFUNC9);
+		tmp &= ~(0xff << 16);
+		writel(tmp, tp->mmio_pinmuxaddr + RTD13XX_ISO_TESTMUX_PFUNC9);
+		break;
+	case OUTPUT_RGMII_TO_MAC:
+		pr_err("%s:%d: FIXME! PHY_TYPE_RGMII_MAC\n", __func__, __LINE__
+		       );
+		break;
+	case OUTPUT_RMII:
+		pr_err("%s:%d: FIXME! PHY_TYPE_RMII\n", __func__, __LINE__);
+		break;
+	case OUTPUT_RGMII_TO_PHY:
+		/* ISO mux spec, GPIO15 is set to MDC pin */
+		/* ISO mux spec, GPIO14 is set to MDIO pin */
+		/* ISO mux spec, GPIO65~76 is set to TX/RX pin */
+		p_rgmii_mdio = devm_pinctrl_get(&tp->pdev->dev);
+		ps_rgmii_mdio = pinctrl_lookup_state(p_rgmii_mdio, "rgmii");
+		pinctrl_select_state(p_rgmii_mdio, ps_rgmii_mdio);
+
+		/* fixed to 1.8V, DP=100, DN=100 */
+		tmp = readl(tp->mmio_pinmuxaddr + RTD13XX_ISO_TESTMUX_PFUNC25);
+		tmp &= ~((0x7 << 3) | (0x7 << 0));
+		tmp |= (0x4 << 3) | (0x4 << 0);
+		writel(tmp, tp->mmio_pinmuxaddr + RTD13XX_ISO_TESTMUX_PFUNC25);
+
+		/* no Schmitt_Trigger */
+		switch (tp->rgmii.voltage) {
+		case 3: /* 3.3v */
+			/* MODE2=1, MODE=0, SR=1, smt=0, pudsel=0, puden=0,
+			 * E2=0
+			 */
+			tmp = readl(tp->mmio_pinmuxaddr +
+				    RTD13XX_ISO_TESTMUX_PFUNC20);
+			tmp &= ~((0xf << 24) | (0xf << 20) | (0x1f << 16) |
+				(0x1f << 12) | (0x1f << 8) | (0x1f << 4));
+			tmp |= (0x0 << 24) | (0x0 << 20) | (0x0 << 16) |
+				(0x0 << 12) | (0x0 << 8) | (0x0 << 4);
+			writel(tmp, tp->mmio_pinmuxaddr +
+			       RTD13XX_ISO_TESTMUX_PFUNC20);
+
+			tmp = readl(tp->mmio_pinmuxaddr +
+				    RTD13XX_ISO_TESTMUX_PFUNC21);
+			tmp &= ~((0x3 << 30) | (0x1f << 25) | (0x1f << 20) |
+				(0x1f << 15) | (0x1f << 10) | (0x1f << 5) |
+				(0x1f << 0));
+			tmp |= (0x2 << 30) | (0x10 << 25) | (0x10 << 20) |
+				(0x10 << 15) | (0x10 << 10) | (0x10 << 5) |
+				(0x10 << 0);
+			writel(tmp, tp->mmio_pinmuxaddr +
+			       RTD13XX_ISO_TESTMUX_PFUNC21);
+			break;
+		case 2: /* 2.5v */
+			/* MODE2=0, MODE=1, SR=0, smt=0, pudsel=0, puden=0,
+			 * E2=1
+			 */
+			tmp = readl(tp->mmio_pinmuxaddr +
+				    RTD13XX_ISO_TESTMUX_PFUNC20);
+			tmp &= ~((0xf << 24) | (0xf << 20) | (0x1f << 16) |
+				(0x1f << 12) | (0x1f << 8) | (0x1f << 4));
+			tmp |= (0x1 << 24) | (0x1 << 20) | (0x1 << 16) |
+				(0x1 << 12) | (0x1 << 8) | (0x1 << 4);
+			writel(tmp, tp->mmio_pinmuxaddr +
+			       RTD13XX_ISO_TESTMUX_PFUNC20);
+
+			tmp = readl(tp->mmio_pinmuxaddr +
+				    RTD13XX_ISO_TESTMUX_PFUNC21);
+			tmp &= ~((0x3 << 30) | (0x1f << 25) | (0x1f << 20) |
+				(0x1f << 15) | (0x1f << 10) | (0x1f << 5) |
+				(0x1f << 0));
+			tmp |= (0x1 << 30) | (0x1 << 25) | (0x1 << 20) |
+				(0x1 << 15) | (0x1 << 10) | (0x1 << 5) |
+				(0x1 << 0);
+			writel(tmp, tp->mmio_pinmuxaddr +
+			       RTD13XX_ISO_TESTMUX_PFUNC21);
+			break;
+		case 1: /* 1.8v */
+		default:
+			/* MODE2=0, MODE=0, SR=0, smt=0, pudsel=0, puden=0,
+			 * E2=1
+			 */
+			tmp = readl(tp->mmio_pinmuxaddr +
+				    RTD13XX_ISO_TESTMUX_PFUNC20);
+			tmp &= ~((0xf << 24) | (0xf << 20) | (0x1f << 16) |
+				(0x1f << 12) | (0x1f << 8) | (0x1f << 4));
+			tmp |= (0x1 << 24) | (0x1 << 20) | (0x1 << 16) |
+				(0x1 << 12) | (0x1 << 8) | (0x1 << 4);
+			writel(tmp, tp->mmio_pinmuxaddr +
+			       RTD13XX_ISO_TESTMUX_PFUNC20);
+
+			tmp = readl(tp->mmio_pinmuxaddr +
+				    RTD13XX_ISO_TESTMUX_PFUNC21);
+			tmp &= ~((0x3 << 30) | (0x1f << 25) | (0x1f << 20) |
+				(0x1f << 15) | (0x1f << 10) | (0x1f << 5) |
+				(0x1f << 0));
+			tmp |= (0x0 << 30) | (0x1 << 25) | (0x1 << 20) |
+				(0x1 << 15) | (0x1 << 10) | (0x1 << 5) |
+				(0x1 << 0);
+			writel(tmp, tp->mmio_pinmuxaddr +
+			       RTD13XX_ISO_TESTMUX_PFUNC21);
+		}
+
+		/* check if external PHY is available */
+		pr_info("Searching external PHY...");
+		tp->chip->mdio_lock(tp);
+		tp->ext_phy = true;
+		r8169_mdio_write(tp, 31, 0x0a43);
+		tmp = 0;
+		while (r8169_mdio_read(tp, 31) != 0xa43) {
+			pr_info(".");
+			tmp += 1;
+			mdelay(1);
+			if (tmp >= 2000) {
+				pr_err("\n External RGMII PHY not found, current = 0x%02x\n",
+				       r8169_mdio_read(tp, 31));
+				return;
+			}
+		}
+		if (tmp < 2000)
+			pr_info("found.\n");
+
+		/* set RGMII RX delay */
+		tmp = rtl_ocp_read(tp, 0xea34) &
+			~(BIT(14) | BIT(9) | BIT(8) | BIT(6));
+		switch (tp->rgmii.rx_delay) {
+		case 1:
+			tmp |= (0x0 << 6) | (0x1 << 8);
+			break;
+		case 2:
+			tmp |= (0x0 << 6) | (0x2 << 8);
+			break;
+		case 3:
+			tmp |= (0x0 << 6) | (0x3 << 8);
+			break;
+		case 4:
+			tmp |= (0x1 << 6) | (0x0 << 8);
+			break;
+		case 5:
+			tmp |= (0x1 << 6) | (0x1 << 8);
+			break;
+		case 6:
+			tmp |= (0x1 << 6) | (0x2 << 8);
+			break;
+		case 7:
+			tmp |= (0x1 << 6) | (0x3 << 8);
+		}
+		rtl_ocp_write(tp, 0xea34, tmp);
+
+		tmp = rtl_ocp_read(tp, 0xea36) & ~(BIT(0)); /* rg_clk_en */
+		rtl_ocp_write(tp, 0xea36, tmp);
+
+		/* external PHY RGMII timing tuning,
+		 * rg_rgmii_id_mode = 1 (default)
+		 */
+		r8169_mdio_write(tp, 31, 0x0d08);
+		tmp = r8169_mdio_read(tp, 21);
+		switch (tp->rgmii.rx_delay) {
+		case 0:
+		case 1:
+		case 2:
+		case 3:
+			tmp |= BIT(3);
+			break;
+		case 4:
+		case 5:
+		case 6:
+		case 7:
+			tmp &= ~BIT(3);
+		}
+		r8169_mdio_write(tp, 21, tmp);
+
+		/* set RGMII TX delay */
+		if (tp->rgmii.tx_delay == 0) {
+			tmp = rtl_ocp_read(tp, 0xea34) & ~(BIT(7));
+			rtl_ocp_write(tp, 0xea34, tmp);
+
+			/* external PHY RGMII timing tuning, tx_dly_mode = 1 */
+			r8169_mdio_write(tp, 31, 0x0d08);
+			r8169_mdio_write(tp, 17,
+					 (r8169_mdio_read(tp, 17) | BIT(8)));
+		} else {	/* tp->tx_delay == 1 (2ns) */
+			tmp = rtl_ocp_read(tp, 0xea34) | (BIT(7));
+			rtl_ocp_write(tp, 0xea34, tmp);
+
+			/* external PHY RGMII timing tuning, tx_dly_mode = 0 */
+			r8169_mdio_write(tp, 31, 0x0d08);
+			r8169_mdio_write(tp, 17,
+					 (r8169_mdio_read(tp, 17) & ~BIT(8)));
+		}
+
+		/* ISO spec, data path is set to RGMII */
+		tmp = rtl_ocp_read(tp, 0xea30) & ~(BIT(0));
+		rtl_ocp_write(tp, 0xea30, tmp);
+
+		tmp = rtl_ocp_read(tp, 0xea34) & ~(BIT(0) | BIT(1));
+		tmp |= BIT(0); /* RGMII */
+		rtl_ocp_write(tp, 0xea34, tmp);
+
+		tp->chip->mdio_unlock(tp);
+
+		/* ext_phy == true now */
+
+		break;
+	default:
+		pr_err("%s:%d: unsupport output mode (%d) in FPGA\n",
+		       __func__, __LINE__, tp->output_mode);
+	}
+}
+
+static void rtd13xx_eee_set(struct rtl8169_private *tp, bool enable)
+{
+	if (enable) {
+		tp->chip->mdio_lock(tp);
+		if (tp->output_mode == OUTPUT_EMBEDDED_PHY) {
+			/* enable eee auto fallback function */
+			r8169_mdio_write(tp, 31, 0x0a4b);
+			r8169_mdio_write(tp, 17,
+					 r8169_mdio_read(tp, 17) | BIT(2));
+			/* 1000M/100M EEE capability */
+			r8169_mmd_write(tp, 0x7, 60, (BIT(2) | BIT(1)));
+			/* 10M EEE */
+			r8169_mdio_write(tp, 31, 0xa43);
+			r8169_mdio_write(tp, 25,
+					 r8169_mdio_read(tp, 25) | BIT(4) |
+					 BIT(2));
+			/* keep RXC in LPI mode */
+			r8169_mmd_write(tp, 0x3, 0,
+					r8169_mmd_read(tp, 0x3, 0) & ~BIT(10));
+		} else { /* RGMII */
+			/* enable eee auto fallback function */
+			r8169_mdio_write(tp, 31, 0x0a4b);
+			r8169_mdio_write(tp, 17,
+					 r8169_mdio_read(tp, 17) | BIT(2));
+			/* 1000M & 100M EEE capability */
+			r8169_mmd_write(tp, 0x7, 60, (BIT(2) | BIT(1)));
+			/* 10M EEE */
+			r8169_mdio_write(tp, 31, 0xa43);
+			r8169_mdio_write(tp, 25,
+					 r8169_mdio_read(tp, 25) | BIT(4) |
+					 BIT(2));
+			/* stop RXC in LPI mode */
+			r8169_mmd_write(tp, 0x3, 0,
+					r8169_mmd_read(tp, 0x3, 0) | BIT(10));
+		}
+		tp->chip->mdio_unlock(tp);
+		/* EEE Tw_sys_tx timing adjustment,
+		 * make sure MAC would send data after FEPHY wake up
+		 */
+		/* default 0x001F, 100M EEE */
+		rtl_ocp_write(tp, 0xe04a, 0x002f);
+		/* default 0x0011, 1000M EEE */
+		rtl_ocp_write(tp, 0xe04c, 0x001a);
+		/* EEEP Tw_sys_tx timing adjustment,
+		 * make sure MAC would send data after FEPHY wake up
+		 */
+		 /* default 0x3f, 10M EEEP */
+		if (tp->output_mode == OUTPUT_EMBEDDED_PHY) {
+			/* FEPHY needs 160us */
+			rtl_ocp_write(tp, 0xe08a, 0x00a8);
+		} else {	/* RGMII, GPHY needs 25us */
+			rtl_ocp_write(tp, 0xe08a, 0x0020);
+		}
+		/* default 0x0005, 100M/1000M EEEP */
+		rtl_ocp_write(tp, 0xe08c, 0x0008);
+		/* EEE MAC mode */
+		rtl_ocp_write(tp, 0xe040,
+			      rtl_ocp_read(tp, 0xe040) | BIT(1) | BIT(0));
+		/* EEE+ MAC mode */
+		rtl_ocp_write(tp, 0xe080, rtl_ocp_read(tp, 0xe080) | BIT(1));
+	} else {
+		tp->chip->mdio_lock(tp);
+		if (tp->output_mode == OUTPUT_EMBEDDED_PHY) {
+			/* disable eee auto fallback function */
+			r8169_mdio_write(tp, 31, 0x0a4b);
+			r8169_mdio_write(tp, 17,
+					 r8169_mdio_read(tp, 17) & ~BIT(2));
+			/* 100M & 1000M EEE capability */
+			r8169_mmd_write(tp, 0x7, 60, 0);
+			/* 10M EEE */
+			r8169_mdio_write(tp, 31, 0xa43);
+			r8169_mdio_write(tp, 25,
+					 r8169_mdio_read(tp, 25) &
+					 ~(BIT(4) | BIT(2)));
+			/* keep RXC in LPI mode */
+			r8169_mmd_write(tp, 0x3, 0,
+					r8169_mmd_read(tp, 0x3, 0) & ~BIT(10));
+		} else { /* RGMII */
+			/* disable eee auto fallback function */
+			r8169_mdio_write(tp, 31, 0x0a4b);
+			r8169_mdio_write(tp, 17,
+					 r8169_mdio_read(tp, 17) & ~BIT(2));
+			/* 100M & 1000M EEE capability */
+			r8169_mmd_write(tp, 0x7, 60, 0);
+			/* 10M EEE */
+			r8169_mdio_write(tp, 31, 0xa43);
+			r8169_mdio_write(tp, 25,
+					 r8169_mdio_read(tp, 25) &
+					 ~(BIT(4) | BIT(2)));
+			/* keep RXC in LPI mode */
+			r8169_mmd_write(tp, 0x3, 0,
+					r8169_mmd_read(tp, 0x3, 0) & ~BIT(10));
+		}
+		tp->chip->mdio_unlock(tp);
+		/* reset to default value */
+		rtl_ocp_write(tp, 0xe040, rtl_ocp_read(tp, 0xe040) | BIT(13));
+		/* EEE MAC mode */
+		rtl_ocp_write(tp, 0xe040,
+			      rtl_ocp_read(tp, 0xe040) & ~(BIT(1) | BIT(0)));
+		/* EEE+ MAC mode */
+		rtl_ocp_write(tp, 0xe080, rtl_ocp_read(tp, 0xe080) & ~BIT(1));
+		/* EEE Tw_sys_tx timing restore */
+		/* default 0x001F, 100M EEE */
+		rtl_ocp_write(tp, 0xe04a, 0x001f);
+		/* default 0x0011, 1000M EEE */
+		rtl_ocp_write(tp, 0xe04c, 0x0011);
+		/* EEEP Tw_sys_tx timing restore */
+		/* default 0x3f, 10M EEEP */
+		rtl_ocp_write(tp, 0xe08a, 0x003f);
+		/* default 0x0005, 100M/1000M EEEP */
+		rtl_ocp_write(tp, 0xe08c, 0x0005);
+	}
+}
+
+static void rtd13xx_led_set(struct rtl8169_private *tp, bool enable)
+{
+	u32 val;
+
+	if (enable) {
+		val = readl(tp->mmio_pinmuxaddr + RTD13XX_ISO_TESTMUX_MUXPAD2)
+			& ~0x000003F0;
+		writel(val | 0x00000090, tp->mmio_pinmuxaddr +
+		       RTD13XX_ISO_TESTMUX_MUXPAD2);
+	} else {
+		writel(readl(tp->mmio_pinmuxaddr + RTD13XX_ISO_TESTMUX_MUXPAD2)
+		       & ~0x000003F0,
+		       tp->mmio_pinmuxaddr + RTD13XX_ISO_TESTMUX_MUXPAD2);
+	}
+}
+
+static void rtd13xx_dump_regs(struct seq_file *m, struct rtl8169_private *tp)
+{
+	seq_printf(m, "ISO_UMSK_ISR\t[0x98007004] = %08x\n",
+		   readl(tp->mmio_clkaddr + ISO_UMSK_ISR));
+	seq_printf(m, "ISO_PWRCUT_ETN\t[0x9800705c] = %08x\n",
+		   readl(tp->mmio_clkaddr + ISO_POWERCUT_ETN));
+	seq_printf(m, "ISO_ETN_TESTIO\t[0x98007060] = %08x\n",
+		   readl(tp->mmio_clkaddr + ISO_ETN_TESTIO));
+	seq_printf(m, "ETN_RESET_CTRL\t[0x98007088] = %08x\n",
+		   readl(tp->mmio_clkaddr + ISO_SOFT_RESET));
+	seq_printf(m, "ETN_CLK_CTRL\t[0x9800708c] = %08x\n",
+		   readl(tp->mmio_clkaddr + ISO_CLOCK_ENABLE));
+}
+
+static void rtd13xx_dump_var(struct seq_file *m, struct rtl8169_private *tp)
+{
+	seq_printf(m, "voltage \t%d\n", tp->rgmii.voltage);
+	seq_printf(m, "tx_delay\t%d\n", tp->rgmii.tx_delay);
+	seq_printf(m, "rx_delay\t%d\n", tp->rgmii.rx_delay);
+}
+
+/******************* END of RTD13XX ****************************/
+
+static struct r8169soc_chip_info rtd119x_info = {
+	.name			= "RTD119X",
+	.reset_phy_gmac		= rtd119x_reset_phy_gmac,
+	.acp_init		= NULL,
+	.pll_clock_init		= rtd119x_pll_clock_init,
+	.mdio_init		= rtd119x_mdio_init,
+	.mac_mcu_patch		= rtd119x_mac_mcu_patch,
+	.hw_phy_config		= rtd119x_hw_phy_config,
+	.eee_set		= rtd119x_eee_set,
+	.led_set		= rtd119x_led_set,
+	.dump_regs		= rtd119x_dump_regs,
+	.dump_var		= rtd119x_dump_var,
+	.mac_version		= 41,
+	.cfg_version		= RTL_CFG_1,
+	.txd_version		= RTL_TD_1,
+	.jumbo_max		= JUMBO_9K,
+	.jumbo_tx_csum		= false,
+	.led_cfg		= 0x000670CA,
+	.region			= 2,
+	.align			= 8,
+	.event_slow		= SYS_ERR | LINK_CHG | RX_OVERFLOW,
+	.features		= RTL_FEATURE_GMII | RTL_FEATURE_MSI,
+};
+
+static struct r8169soc_chip_info rtd129x_info = {
+	.name			= "RTD129X",
+	.mdio_lock		= rtd129x_mdio_lock,
+	.mdio_unlock		= rtd129x_mdio_unlock,
+	.reset_phy_gmac		= rtd129x_reset_phy_gmac,
+	.acp_init		= NULL,
+	.pll_clock_init		= rtd129x_pll_clock_init,
+	.mdio_init		= rtd129x_mdio_init,
+	.mac_mcu_patch		= rtd129x_mac_mcu_patch,
+	.hw_phy_config		= rtd129x_hw_phy_config,
+	.eee_set		= rtd129x_eee_set,
+	.led_set		= rtd129x_led_set,
+	.dump_regs		= rtd129x_dump_regs,
+	.dump_var		= rtd129x_dump_var,
+	.mac_version		= 41,
+	.cfg_version		= RTL_CFG_1,
+	.txd_version		= RTL_TD_1,
+	.jumbo_max		= JUMBO_9K,
+	.jumbo_tx_csum		= false,
+	.led_cfg		= 0x000670CA,
+	.region			= 2,
+	.align			= 8,
+	.event_slow		= SYS_ERR | LINK_CHG | RX_OVERFLOW,
+	.features		= RTL_FEATURE_GMII | RTL_FEATURE_MSI |
+				  RTL_FEATURE_EEE,
+};
+
+static struct r8169soc_chip_info rtd139x_info = {
+	.name			= "RTD139X",
+	.mdio_lock		= rtd139x_mdio_lock,
+	.mdio_unlock		= rtd139x_mdio_unlock,
+	.reset_phy_gmac		= rtd139x_reset_phy_gmac,
+	.acp_init		= rtd139x_acp_init,
+	.pll_clock_init		= rtd139x_pll_clock_init,
+	.mdio_init		= rtd139x_mdio_init,
+	.crc_wakeup		= rtl_crc_wakeup,
+	.eee_set		= rtd139x_eee_set,
+	.led_set		= rtd139x_led_set,
+	.dump_regs		= rtd139x_dump_regs,
+	.dump_var		= rtd139x_dump_var,
+	.mac_version		= 41,
+	.cfg_version		= RTL_CFG_1,
+	.txd_version		= RTL_TD_1,
+	.jumbo_max		= JUMBO_9K,
+	.jumbo_tx_csum		= false,
+	.led_cfg		= 0x000679A9,
+	.region			= 2,
+	.align			= 8,
+	.event_slow		= SYS_ERR | LINK_CHG | RX_OVERFLOW,
+	.features		= RTL_FEATURE_GMII | RTL_FEATURE_MSI |
+				  RTL_FEATURE_ACP | RTL_FEATURE_EEE,
+};
+
+static struct r8169soc_chip_info rtd16xx_info = {
+	.name			= "RTD16XX",
+	.mdio_lock		= rtd16xx_mdio_lock,
+	.mdio_unlock		= rtd16xx_mdio_unlock,
+	.reset_phy_gmac		= rtd16xx_reset_phy_gmac,
+	.acp_init		= rtd16xx_acp_init,
+	.pll_clock_init		= rtd16xx_pll_clock_init,
+	.mdio_init		= rtd16xx_mdio_init,
+	.crc_wakeup		= rtl_crc_wakeup,
+	.eee_set		= rtd16xx_eee_set,
+	.led_set		= rtd16xx_led_set,
+	.dump_regs		= rtd16xx_dump_regs,
+	.dump_var		= rtd16xx_dump_var,
+	.mac_version		= 41,
+	.cfg_version		= RTL_CFG_1,
+	.txd_version		= RTL_TD_1,
+	.jumbo_max		= JUMBO_9K,
+	.jumbo_tx_csum		= false,
+	.led_cfg		= 0x00067CA9,
+	.region			= 2,
+	.align			= 8,
+	.event_slow		= SYS_ERR | LINK_CHG | RX_OVERFLOW,
+	.features		= RTL_FEATURE_GMII | RTL_FEATURE_MSI |
+				  RTL_FEATURE_TX_NO_CLOSE |
+				  RTL_FEATURE_ADJUST_FIFO |
+				  RTL_FEATURE_ACP | RTL_FEATURE_EEE,
+};
+
+static struct r8169soc_chip_info rtd13xx_info = {
+	.name			= "RTD13XX",
+	.mdio_lock		= rtd13xx_mdio_lock,
+	.mdio_unlock		= rtd13xx_mdio_unlock,
+	.reset_phy_gmac		= rtd13xx_reset_phy_gmac,
+	.acp_init		= rtd13xx_acp_init,
+	.pll_clock_init		= rtd13xx_pll_clock_init,
+	.mdio_init		= rtd13xx_mdio_init,
+	.crc_wakeup		= rtl_crc_wakeup,
+	.eee_set		= rtd13xx_eee_set,
+	.led_set		= rtd13xx_led_set,
+	.dump_regs		= rtd13xx_dump_regs,
+	.dump_var		= rtd13xx_dump_var,
+	.mac_version		= 41,
+	.cfg_version		= RTL_CFG_1,
+	.txd_version		= RTL_TD_1,
+	.jumbo_max		= JUMBO_9K,
+	.jumbo_tx_csum		= false,
+	.led_cfg		= 0x00067CA9,
+	.region			= 2,
+	.align			= 8,
+	.event_slow		= SYS_ERR | LINK_CHG | RX_OVERFLOW,
+	.features		= RTL_FEATURE_GMII | RTL_FEATURE_MSI |
+				  RTL_FEATURE_TX_NO_CLOSE |
+				  RTL_FEATURE_ADJUST_FIFO |
+				  RTL_FEATURE_ACP | RTL_FEATURE_EEE,
+};
+
+static const struct of_device_id r8169soc_dt_ids[] = {
+	{
+		.compatible = "realtek,rtd119x-r8169soc",
+		.data = &rtd119x_info,
+	},
+	{
+		.compatible = "realtek,rtd129x-r8169soc",
+		.data = &rtd129x_info,
+	},
+	{
+		.compatible = "realtek,rtd139x-r8169soc",
+		.data = &rtd139x_info,
+	},
+	{
+		.compatible = "realtek,rtd16xx-r8169soc",
+		.data = &rtd16xx_info,
+	},
+	{
+		.compatible = "realtek,rtd13xx-r8169soc",
+		.data = &rtd13xx_info,
+	},
+	{
+	}
+};
+
+MODULE_DEVICE_TABLE(of, r8169soc_dt_ids);
+
+static void rtk_chip_info_check(struct r8169soc_chip_info *chip)
+{
+	if (!chip)
+		return;
+
+	if (!chip->mdio_write)
+		chip->mdio_write = r8169_mdio_write;
+	if (!chip->mdio_read)
+		chip->mdio_read = r8169_mdio_read;
+	if (!chip->mdio_lock)
+		chip->mdio_lock = empty_mdio_lock;
+	if (!chip->mdio_unlock)
+		chip->mdio_unlock = empty_mdio_unlock;
+	if (!chip->pll_power_down)
+		chip->pll_power_down = r8168_pll_power_down;
+	if (!chip->pll_power_up)
+		chip->pll_power_up = r8168_pll_power_up;
+	if (!chip->csi_write)
+		chip->csi_write = r8169_csi_write;
+	if (!chip->csi_read)
+		chip->csi_read = r8169_csi_read;
+	if (!chip->set_speed)
+		chip->set_speed = rtl8169_set_speed_xmii;
+	if (!chip->phy_reset_enable)
+		chip->phy_reset_enable = rtl8169_xmii_reset_enable;
+	if (!chip->phy_reset_pending)
+		chip->phy_reset_pending = rtl8169_xmii_reset_pending;
+	if (!chip->link_ok)
+		chip->link_ok = rtl8169_xmii_link_ok;
+	if (!chip->do_ioctl)
+		chip->do_ioctl = rtl_xmii_ioctl;
+	if (!chip->hw_start)
+		chip->hw_start = rtl_hw_start_8168;
+	if (!chip->reset_phy_gmac)
+		chip->reset_phy_gmac = empty_reset_phy_gmac;
+	if (!chip->acp_init)
+		chip->acp_init = empty_acp_init;
+	if (!chip->pll_clock_init)
+		chip->pll_clock_init = empty_pll_clock_init;
+	if (!chip->mdio_init)
+		chip->mdio_init = empty_mdio_init;
+	if (!chip->mac_mcu_patch)
+		chip->mac_mcu_patch = empty_mac_mcu_patch;
+	if (!chip->hw_phy_config)
+		chip->hw_phy_config = empty_hw_phy_config;
+	if (!chip->crc_wakeup)
+		chip->crc_wakeup = empty_crc_wakeup;
+	if (!chip->eee_set)
+		chip->eee_set = empty_eee_set;
+	if (!chip->led_set)
+		chip->led_set = empty_led_set;
+	if (!chip->dump_regs)
+		chip->dump_regs = empty_dump_regs;
+	if (!chip->dump_var)
+		chip->dump_var = empty_dump_var;
+
+	#if defined(CONFIG_RTL_RX_NO_COPY)
+	chip->features |= RTL_FEATURE_RX_NO_COPY;
+	#endif /* CONFIG_RTL_RX_NO_COPY */
+}
+
+static int rtl_init_one(struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+	struct r8169soc_chip_info *chip;
+	const char *s = NULL;
+	struct rtl8169_private *tp;
+	struct mii_if_info *mii;
+	struct net_device *ndev;
+	void __iomem *ioaddr;
+	void __iomem *clkaddr;
+	void __iomem *sbxaddr;
+	void __iomem *sdsaddr;
+	void __iomem *pinmuxaddr;
+	int i;
+	int rc;
+	int led_config;
+	u32 tmp;
+	int irq;
+	int retry;
+	const char *mac_addr;
+	struct property *wake_mask;
+	char tmp_str[80];
+	struct device_node *phy_0;
+	u32 phy_irq_num = 0;
+	u32 phy_irq_mask = 0;
+	u32 phy_irq[POR_NUM] = {0, 0, 0};
+	u8 phy_irq_map[POR_NUM + 1] = {0, 0, 0, 0};
+	u32 phy_por_xv_mask;
+	static char phy_irq_name[POR_NUM][IFNAMSIZ];
+#ifdef RTL_PROC
+	struct proc_dir_entry *dir_dev = NULL;
+	struct proc_dir_entry *entry = NULL;
+#endif
+	u32 bypass_enable = 0;
+	u32 acp_enable = 0;
+	u32 sgmii_swing = 0;
+	u32 voltage = 1;
+	u32 tx_delay = 0;
+	u32 rx_delay = 0;
+	u32 output_mode = 0;
+	u32 ext_phy_id = 1;
+	u32 eee_enable = 0;
+	u32 wol_enable = 0;
+
+	if (netif_msg_drv(&debug)) {
+		pr_info("%s Gigabit Ethernet driver %s loaded\n",
+			MODULENAME, RTL8169_VERSION);
+	}
+
+	match = of_match_node(r8169soc_dt_ids, pdev->dev.of_node);
+	if (!match) {
+		of_property_read_string(pdev->dev.of_node, "compatible", &s);
+		if (!s)
+			s = "Null";
+		pr_err("%s can't find the matched compatible [%s]\n",
+		       __func__, s);
+		return -EINVAL;
+	}
+
+	chip = (struct r8169soc_chip_info *)match->data;
+	if (chip) {
+		rtk_chip_info_check(chip);
+	} else {
+		pr_err("%s invalid chip data for the matched str [%s]\n",
+		       __func__, match->compatible);
+		return -EINVAL;
+	}
+	if (of_property_read_u32(pdev->dev.of_node, "output-mode", &output_mode))
+		dprintk("%s can't get output mode", __func__);
+	if (of_property_read_u32(pdev->dev.of_node, "wol-enable", &wol_enable))
+		dprintk("%s can't get wol_enable", __func__);
+	if (of_property_read_u32(pdev->dev.of_node, "eee", &eee_enable))
+		dprintk("%s can't get eee_enable", __func__);
+	if (of_property_read_u32(pdev->dev.of_node, "acp", &acp_enable))
+		dprintk("%s can't get acp", __func__);
+
+	/* optional properties */
+	if (of_property_read_u32(pdev->dev.of_node, "bypass",
+				 &bypass_enable))
+		bypass_enable = 1;
+	if (of_property_read_u32(pdev->dev.of_node, "led-cfg", &led_config))
+		led_config = 0;
+	if (of_property_read_u32(pdev->dev.of_node, "ext-phy-id", &ext_phy_id))
+		ext_phy_id = 0;
+	if (of_property_read_u32(pdev->dev.of_node, "sgmii-swing",
+				 &sgmii_swing))
+		sgmii_swing = 0;
+	if (of_property_read_u32(pdev->dev.of_node, "voltage", &voltage))
+		voltage = 1;
+	if (of_property_read_u32(pdev->dev.of_node, "tx-delay", &tx_delay))
+		tx_delay = 0;
+	if (of_property_read_u32(pdev->dev.of_node, "rx-delay", &rx_delay))
+		rx_delay = 0;
+	if (of_get_property(pdev->dev.of_node, "force-Gb-off", NULL)) {
+		pr_info("~~~ disable Gb features~~~\n");
+		chip->features &= ~RTL_FEATURE_GMII;
+	}
+
+	phy_0 = of_get_child_by_name(pdev->dev.of_node, "phy_0");
+	if (phy_0) {
+		phy_irq_num = of_irq_count(phy_0);
+		pr_err("phy_0: get %d phy_irq\n", phy_irq_num);
+		if (phy_irq_num > POR_NUM) {
+			pr_err("phy_0: %d exceed max. GPHY IRQ number\n",
+			       phy_irq_num);
+			phy_irq_num = POR_NUM;
+		}
+
+		if (of_property_read_u32(phy_0, "irq-mask", &phy_irq_mask)) {
+			pr_err("phy_0: no irq-mask is defined\n");
+			phy_irq_num = 0;
+		} else {
+			tmp = 0;
+			for (i = 0; i < 32; i++) {
+				if (phy_irq_mask & (1 << i))
+					phy_irq_map[tmp++] = i;
+
+				if (tmp > phy_irq_num)
+					break;
+			}
+			if (tmp != phy_irq_num) {
+				pr_err("phy_0: IRQ number %d and IRQ mask 0x%08x don't match\n",
+				       phy_irq_num, phy_irq_mask);
+				phy_irq_num = min(tmp, phy_irq_num);
+			}
+		}
+		for (i = 0; i < phy_irq_num; i++) {
+			phy_irq[i] = irq_of_parse_and_map(phy_0, i);
+			pr_err("phy_0: get phy_irq %d for bit %d\n",
+			       phy_irq[i], phy_irq_map[i]);
+		}
+
+		if (of_property_read_u32(phy_0, "por-xv-mask",
+					 &phy_por_xv_mask)) {
+			pr_info("phy_0: no por-xv-mask is defined\n");
+			phy_por_xv_mask = POR_XV_MASK;
+		}
+	}
+	/* end of optional properties */
+
+	irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+	if (!pdev->dev.dma_mask)
+		pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+	if (!pdev->dev.coherent_dma_mask)
+		pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
+	ioaddr = of_iomap(pdev->dev.of_node, 0);
+	clkaddr = of_iomap(pdev->dev.of_node, 1);
+	sbxaddr = of_iomap(pdev->dev.of_node, 2);
+	sdsaddr = of_iomap(pdev->dev.of_node, 3);
+	pinmuxaddr = of_iomap(pdev->dev.of_node, 4);
+
+	ndev = alloc_etherdev(sizeof(*tp));
+	if (!ndev) {
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	SET_NETDEV_DEV(ndev, &pdev->dev);
+	ndev->irq = irq;
+	ndev->netdev_ops = &rtl_netdev_ops;
+	tp = netdev_priv(ndev);
+	tp->dev = ndev;
+	tp->pdev = pdev;
+	tp->chip = chip;
+	tp->msg_enable = netif_msg_init(debug.msg_enable, R8169_MSG_DEFAULT);
+	tp->mac_version = tp->chip->mac_version;
+	tp->mmio_addr = ioaddr;
+	tp->mmio_clkaddr = clkaddr;
+	tp->led_cfg = led_config;
+	tp->output_mode = output_mode;
+	tp->ext_phy_id = ext_phy_id;
+	if ((tp->chip->features & RTL_FEATURE_EEE) && eee_enable > 0)
+		tp->eee_enable = true;
+	else
+		tp->eee_enable = false;
+	tp->phy_irq_num = phy_irq_num;
+	tp->phy_por_xv_mask = phy_por_xv_mask;
+	for (i = 0; i < tp->phy_irq_num; i++) {
+		tp->phy_irq[i] = phy_irq[i];
+		tp->phy_irq_map[i] = phy_irq_map[i];
+	}
+	atomic_set(&tp->phy_reinit_flag, 0);
+
+	tp->mmio_sbxaddr = sbxaddr;
+	tp->mmio_sdsaddr = sdsaddr;
+	tp->mmio_pinmuxaddr = pinmuxaddr;
+	tp->bypass_enable = !!(bypass_enable > 0);
+	if ((tp->chip->features & RTL_FEATURE_ACP) && acp_enable > 0)
+		tp->acp_enable = true;
+	else
+		tp->acp_enable = false;
+	tp->ext_phy = false;
+
+	switch (tp->output_mode) {
+	case OUTPUT_RGMII_TO_MAC:
+	case OUTPUT_RGMII_TO_PHY:
+		tp->rgmii.voltage = voltage;
+		tp->rgmii.tx_delay = tx_delay;
+		tp->rgmii.rx_delay = rx_delay;
+		break;
+	case OUTPUT_SGMII_TO_MAC:
+	case OUTPUT_SGMII_TO_PHY:
+		tp->sgmii.swing = sgmii_swing;
+		break;
+	case OUTPUT_RMII:
+		tp->rmii.voltage = voltage;
+		tp->rmii.tx_delay = tx_delay;
+		tp->rmii.rx_delay = rx_delay;
+		break;
+	case OUTPUT_EMBEDDED_PHY:
+	case OUTPUT_FORCE_LINK:
+	default:
+		/* do nothing */
+		break;
+	}
+
+	mii = &tp->mii;
+	mii->dev = ndev;
+	mii->mdio_read = rtl_mdio_read;
+	mii->mdio_write = rtl_mdio_write;
+	mii->phy_id_mask = 0x1f;
+	mii->reg_num_mask = 0x1f;
+	mii->supports_gmii = !!(tp->chip->features & RTL_FEATURE_GMII);
+
+	tp->chip->reset_phy_gmac(tp);
+	tp->chip->acp_init(tp);
+	tp->chip->pll_clock_init(tp);
+	tp->chip->mdio_init(tp);
+	/* after r8169soc_mdio_init(),
+	 * SGMII : tp->ext_phy == true  ==> external MDIO,
+	 * RGMII : tp->ext_phy == true  ==> external MDIO,
+	 * RMII  : tp->ext_phy == false ==> internal MDIO,
+	 * FE PHY: tp->ext_phy == false ==> internal MDIO
+	 */
+
+	tp->chip->eee_set(tp, tp->eee_enable);
+
+	/* Enable ALDPS */
+	tp->chip->mdio_lock(tp);
+	r8169_mdio_write(tp, 31, 0xa43);
+	r8169_mdio_write(tp, 24, (r8169_mdio_read(tp, 24) | BIT(2)));
+	tp->chip->mdio_unlock(tp);
+
+	tp->wol_enable = wol_enable;
+	tp->wol_crc_cnt = 0;
+	for (i = 0; i < RTL_WPD_SIZE; i++) {
+		memset(tmp_str, 0, 80);
+		sprintf(tmp_str, "wake-mask%d", i);
+		wake_mask = of_find_property(pdev->dev.of_node, tmp_str, NULL);
+		if (!wake_mask)
+			break;
+		tp->wol_mask_size[i] = wake_mask->length;
+		memcpy(&tp->wol_mask[i][0], wake_mask->value,
+		       wake_mask->length);
+
+		sprintf(tmp_str, "wake-crc%d", i);
+		if (of_property_read_u32(pdev->dev.of_node, tmp_str,
+					 &tp->wol_crc[i]))
+			break;
+		tp->wol_crc_cnt += 1;
+	}
+
+#ifdef RTL_PROC
+	do {
+		/* create /proc/net/$rtw_proc_name */
+		rtw_proc = proc_mkdir_data("eth0", 0555,
+					   init_net.proc_net, NULL);
+
+		if (!rtw_proc) {
+			pr_info("procfs:create /proc/net/eth0 failed\n");
+			break;
+		}
+
+		/* create /proc/net/$rtw_proc_name/$dev->name */
+		if (!tp->dir_dev) {
+			tp->dir_dev = proc_mkdir_data(MODULENAME,
+						      S_IFDIR | 0555,
+						      rtw_proc, ndev);
+			dir_dev = tp->dir_dev;
+
+			if (!dir_dev) {
+				pr_info("procfs:create /proc/net/eth0/r8169 failed\n");
+
+				if (rtw_proc) {
+					remove_proc_entry("eth0",
+							  init_net.proc_net);
+					rtw_proc = NULL;
+				}
+				break;
+			}
+		} else {
+			break;
+		}
+
+		entry = proc_create_data("wol_enable", S_IFREG | 0644,
+					 dir_dev, &wol_proc_ops, NULL);
+		if (!entry) {
+			pr_info("procfs:create /proc/net/eth0/r8169/wol_enable failed\n");
+			break;
+		}
+
+		entry = proc_create_data("phy_reinit", S_IFREG | 0644,
+					 dir_dev, &phy_reinit_proc_ops, NULL);
+		if (!entry) {
+			pr_info("procfs:create /proc/net/eth0/r8169/phy_reinit failed\n");
+			break;
+		}
+
+		entry = proc_create_data("eee", S_IFREG | 0644,
+					 dir_dev, &eee_proc_ops, NULL);
+		if (!entry) {
+			pr_info("procfs:create /proc/net/eth0/r8169/eee failed\n");
+			break;
+		}
+
+		entry = proc_create_data("driver_var", S_IFREG | 0444,
+					 dir_dev, &driver_var_proc_ops, NULL);
+		if (!entry) {
+			pr_info("procfs:create /proc/net/eth0/r8169/driver_var failed\n");
+			break;
+		}
+
+		entry = proc_create_data("eth_phy", S_IFREG | 0444,
+					 dir_dev, &eth_phy_proc_ops, NULL);
+		if (!entry) {
+			pr_info("procfs:create /proc/net/eth0/r8169/eth_phy failed\n");
+			break;
+		}
+
+		entry = proc_create_data("ext_regs", S_IFREG | 0444,
+					 dir_dev, &ext_regs_proc_ops, NULL);
+		if (!entry) {
+			pr_info("procfs:create /proc/net/eth0/r8169/ext_regs failed\n");
+			break;
+		}
+
+		entry = proc_create_data("registers", S_IFREG | 0444,
+					 dir_dev, &registers_proc_ops, NULL);
+		if (!entry) {
+			pr_info("procfs:create /proc/net/eth0/r8169/registers failed\n");
+			break;
+		}
+
+		entry = proc_create_data("tx_desc", S_IFREG | 0444,
+					 dir_dev, &tx_desc_proc_ops, NULL);
+		if (!entry) {
+			pr_info("procfs:create /proc/net/eth0/r8169/tx_desc failed\n");
+			break;
+		}
+
+		entry = proc_create_data("rx_desc", S_IFREG | 0444,
+					 dir_dev, &rx_desc_proc_ops, NULL);
+		if (!entry) {
+			pr_info("procfs:create /proc/net/eth0/r8169/rx_desc failed\n");
+			break;
+		}
+
+		entry = proc_create_data("tally", S_IFREG | 0444,
+					 dir_dev, &tally_proc_ops, NULL);
+		if (!entry) {
+			pr_info("procfs:create /proc/net/eth0/r8169/tally failed\n");
+			break;
+		}
+
+		entry = proc_create_data("wpd_event", S_IFREG | 0444,
+					 dir_dev, &wpd_evt_proc_ops, NULL);
+		if (!entry) {
+			pr_info("procfs:create /proc/net/eth0/r8169/wpd_event failed\n");
+			break;
+		}
+
+		entry = proc_create_data("wol_packet", S_IFREG | 0444,
+					 dir_dev, &wol_pkt_proc_ops, NULL);
+		if (!entry) {
+			pr_info("procfs:create /proc/net/eth0/r8169/wol_packet failed\n");
+			break;
+		}
+	} while (0);
+#endif
+
+	rtl_init_rxcfg(tp);
+
+	rtl_irq_disable(tp);
+
+	rtl_hw_initialize(tp);
+
+	rtl_hw_reset(tp);
+
+	rtl_ack_events(tp, 0xffff);
+
+	tp->txd_version = tp->chip->txd_version;
+
+	RTL_W8(CFG9346, CFG9346_UNLOCK);
+	RTL_W8(CONFIG1, RTL_R8(CONFIG1) | PM_ENABLE);
+	RTL_W8(CONFIG5, RTL_R8(CONFIG5) & PME_STATUS);
+
+	/* disable magic packet WOL */
+	RTL_W8(CONFIG3, RTL_R8(CONFIG3) & ~MAGIC_PKT);
+
+	if ((RTL_R8(CONFIG3) & (LINK_UP | MAGIC_PKT)) != 0)
+		tp->features |= RTL_FEATURE_WOL;
+	if ((RTL_R8(CONFIG5) & (UWF | BWF | MWF)) != 0)
+		tp->features |= RTL_FEATURE_WOL;
+	RTL_W8(CFG9346, CFG9346_LOCK);
+
+	if (tp->output_mode == OUTPUT_SGMII_TO_MAC ||
+	    tp->output_mode == OUTPUT_RGMII_TO_MAC)
+		tp->chip->link_ok = rtl8169_xmii_always_link_ok;
+
+	mutex_init(&tp->wk.mutex);
+
+	/* Get MAC address */
+	mac_addr = of_get_mac_address(pdev->dev.of_node);
+	if (mac_addr)
+		rtl_rar_set(tp, (u8 *)mac_addr);
+
+	/* workaround: avoid getting deadbeef */
+#define RETRY_MAX	10
+	for (retry = 0; retry < RETRY_MAX; retry++) {
+		for (i = 0; i < ETH_ALEN; i++)
+			ndev->dev_addr[i] = RTL_R8(MAC0 + i);
+
+		if (*(u32 *)ndev->dev_addr == 0xdeadbeef) {
+			pr_err("%s get invalid MAC address %pM, retry %d\n",
+			       tp->chip->name, ndev->dev_addr, retry);
+			tmp = RTL_R32(PHYAR);	/* read something else */
+			usleep_range(10000, 11000);
+		} else {
+			break;
+		}
+	}
+	if (retry == RETRY_MAX)
+		pr_err("%s get invalid MAC address %pM, give up!\n",
+		       tp->chip->name, ndev->dev_addr);
+
+	rtl_led_set(tp);
+
+	ndev->ethtool_ops = &rtl8169_ethtool_ops;
+	ndev->watchdog_timeo = RTL8169_TX_TIMEOUT;
+
+	netif_napi_add(ndev, &tp->napi, rtl8169_poll, R8169_NAPI_WEIGHT);
+
+	/* don't enable SG, IP_CSUM and TSO by default - it might not work
+	 * properly for all devices
+	 */
+	ndev->features |=
+		NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | NETIF_F_RXCSUM |
+		NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX;
+
+	ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
+		NETIF_F_RXCSUM | NETIF_F_HW_VLAN_CTAG_TX |
+		NETIF_F_HW_VLAN_CTAG_RX;
+	ndev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
+		NETIF_F_HIGHDMA;
+
+	ndev->hw_features |= NETIF_F_RXALL;
+	ndev->hw_features |= NETIF_F_RXFCS;
+
+	ndev->min_mtu = ETH_ZLEN;
+	ndev->max_mtu = tp->chip->jumbo_max;
+
+	tp->event_slow = tp->chip->event_slow;
+
+	tp->opts1_mask = ~(RX_BOVF | RX_FOVF);
+
+	rc = register_netdev(ndev);
+	if (rc < 0)
+		goto err_out_msi_4;
+
+	platform_set_drvdata(pdev, ndev);
+
+	netif_info(tp, probe, ndev, "%s, XID %08x IRQ %d\n",
+		   tp->chip->name,
+		   (u32)(RTL_R32(TX_CONFIG) & 0x9cf0f8ff), ndev->irq);
+	if (tp->chip->jumbo_max != JUMBO_1K) {
+		netif_info(tp, probe, ndev, "jumbo features [frames: %d bytes, tx checksumming: %s]\n",
+			   tp->chip->jumbo_max,
+			   tp->chip->jumbo_tx_csum ? "ok" : "ko");
+	}
+
+	device_set_wakeup_enable(&pdev->dev, tp->features & RTL_FEATURE_WOL);
+
+	netif_carrier_off(ndev);
+
+	if (tp->phy_irq_num) {
+		/* monitor rising edge of POR interrupts */
+		writel(0x00000333, tp->mmio_clkaddr + ISO_POR_CTRL);
+		/* set voltage threshold of POR interrupts */
+		writel(readl(tp->mmio_clkaddr + ISO_POR_VTH) | 0x00000330,
+		       tp->mmio_clkaddr + ISO_POR_VTH);
+		/* clear GPHY HV/DV/AV POR interrupts */
+		writel(0x70000000, tp->mmio_clkaddr + ISO_UMSK_ISR);
+	}
+
+	for (i = 0; i < tp->phy_irq_num; i++) {
+		memset(phy_irq_name[i], 0, IFNAMSIZ);
+		sprintf(phy_irq_name[i], "eth_phy%d", i);
+		rc = request_irq(tp->phy_irq[i], phy_irq_handler, IRQF_SHARED,
+				 phy_irq_name[i], tp);
+		if (rc < 0) {
+			pr_err("%s: unable to request %s IRQ %d, ret = 0x%x\n",
+			       ndev->name, phy_irq_name[i], tp->phy_irq[i], rc);
+		} else {
+			pr_info("%s: request %s IRQ %d successfully\n",
+				ndev->name, phy_irq_name[i], tp->phy_irq[i]);
+		}
+	}
+out:
+	return rc;
+
+err_out_msi_4:
+	netif_napi_del(&tp->napi);
+	free_netdev(ndev);
+	goto out;
+}
+
+static struct platform_driver rtl8169_soc_driver = {
+	.probe		= rtl_init_one,
+	.remove		= rtl_remove_one,
+	.shutdown	= rtl_shutdown,
+	.driver = {
+		.name		= MODULENAME,
+		.owner		= THIS_MODULE,
+		.pm		= RTL8169_PM_OPS,
+		.of_match_table = of_match_ptr(r8169soc_dt_ids),
+	},
+};
+
+module_platform_driver(rtl8169_soc_driver);
-- 
2.28.0



From afaerber at suse.de  Sun Sep 20 09:26:19 2020
From: afaerber at suse.de (=?UTF-8?q?Andreas=20F=C3=A4rber?=)
Date: Sun, 20 Sep 2020 15:26:19 +0200
Subject: [PATCH 0/2] net: wireless: rtw88: Fix oops on probe errors
Message-ID: <20200920132621.26468-1-afaerber@suse.de>

Hello,

This mini-series fixes oopses in rtw88 device probe error handling,
resulting from asynchronous firmware loading.

Since there does not appear to be a public kernel API for canceling
scheduled or ongoing firmware loads, it seems we need to wait with
teardown until rtw88's callback was invoked and signals completion.

Found on RTD1296 arm64 SoC with experimental PCI host bridge driver
(https://github.com/afaerber/linux/commits/rtd1295-next) with a 4K
physical bar window, resulting in rtw_pci_setup_resource() failing,
or with non-implemented 4K remapping resulting in rtw_pci_read32()
returning 0xffffffff values and causing rtw_mac_power_on() to fail.

Cheers,
Andreas

Andreas F?rber (2):
  rtw88: Fix probe error handling race with firmware loading
  rtw88: Fix potential probe error handling race with wow firmware
    loading

 drivers/net/wireless/realtek/rtw88/main.c | 5 +++++
 1 file changed, 5 insertions(+)

-- 
2.28.0



From afaerber at suse.de  Sun Sep 20 09:26:21 2020
From: afaerber at suse.de (=?UTF-8?q?Andreas=20F=C3=A4rber?=)
Date: Sun, 20 Sep 2020 15:26:21 +0200
Subject: [PATCH 2/2] rtw88: Fix potential probe error handling race with wow
 firmware loading
In-Reply-To: <20200920132621.26468-1-afaerber@suse.de>
References: <20200920132621.26468-1-afaerber@suse.de>
Message-ID: <20200920132621.26468-3-afaerber@suse.de>

If rtw_core_init() fails to load the wow firmware, rtw_core_deinit()
will not get called to clean up the regular firmware.

Ensure that an error loading the wow firmware does not produce an oops
for the regular firmware by waiting on its completion to be signalled
before returning. Also release the loaded firmware.

Fixes: c8e5695eae99 ("rtw88: load wowlan firmware if wowlan is supported")
Cc: Chin-Yen Lee <timlee at realtek.com>
Cc: Yan-Hsuan Chuang <yhchuang at realtek.com>
Signed-off-by: Andreas F?rber <afaerber at suse.de>
---
 drivers/net/wireless/realtek/rtw88/main.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c
index dc48ec4b0a31..cc82c80f0433 100644
--- a/drivers/net/wireless/realtek/rtw88/main.c
+++ b/drivers/net/wireless/realtek/rtw88/main.c
@@ -1472,6 +1472,9 @@ int rtw_core_init(struct rtw_dev *rtwdev)
 		ret = rtw_load_firmware(rtwdev, RTW_WOWLAN_FW);
 		if (ret) {
 			rtw_warn(rtwdev, "no wow firmware loaded\n");
+			wait_for_completion(&rtwdev->fw.completion);
+			if (rtwdev->fw.firmware)
+				release_firmware(rtwdev->fw.firmware);
 			return ret;
 		}
 	}
-- 
2.28.0



From afaerber at suse.de  Sun Sep 20 09:26:20 2020
From: afaerber at suse.de (=?UTF-8?q?Andreas=20F=C3=A4rber?=)
Date: Sun, 20 Sep 2020 15:26:20 +0200
Subject: [PATCH 1/2] rtw88: Fix probe error handling race with firmware loading
In-Reply-To: <20200920132621.26468-1-afaerber@suse.de>
References: <20200920132621.26468-1-afaerber@suse.de>
Message-ID: <20200920132621.26468-2-afaerber@suse.de>

In case of rtw8822be, a probe failure after successful rtw_core_init()
has been observed to occasionally lead to an oops from rtw_load_firmware_cb():

[    3.924268] pci 0001:01:00.0: [10ec:b822] type 00 class 0xff0000
[    3.930531] pci 0001:01:00.0: reg 0x10: [io  0x0000-0x00ff]
[    3.936360] pci 0001:01:00.0: reg 0x18: [mem 0x00000000-0x0000ffff 64bit]
[    3.944042] pci 0001:01:00.0: supports D1 D2
[    3.948438] pci 0001:01:00.0: PME# supported from D0 D1 D2 D3hot D3cold
[    3.957312] pci 0001:01:00.0: BAR 2: no space for [mem size 0x00010000 64bit]
[    3.964645] pci 0001:01:00.0: BAR 2: failed to assign [mem size 0x00010000 64bit]
[    3.972332] pci 0001:01:00.0: BAR 0: assigned [io  0x10000-0x100ff]
[    3.986240] rtw_8822be 0001:01:00.0: enabling device (0000 -> 0001)
[    3.992735] rtw_8822be 0001:01:00.0: failed to map pci memory
[    3.998638] rtw_8822be 0001:01:00.0: failed to request pci io region
[    4.005166] rtw_8822be 0001:01:00.0: failed to setup pci resources
[    4.011580] rtw_8822be: probe of 0001:01:00.0 failed with error -12
[    4.018827] cfg80211: Loading compiled-in X.509 certificates for regulatory database
[    4.029121] cfg80211: Loaded X.509 cert 'sforshee: 00b28ddf47aef9cea7'
[    4.050828] Unable to handle kernel paging request at virtual address edafeaac9607952c
[    4.058975] Mem abort info:
[    4.058980]   ESR = 0x96000004
[    4.058990]   EC = 0x25: DABT (current EL), IL = 32 bits
[    4.070353]   SET = 0, FnV = 0
[    4.073487]   EA = 0, S1PTW = 0
[    4.073501] dw-apb-uart 98007800.serial: forbid DMA for kernel console
[    4.076723] Data abort info:
[    4.086415]   ISV = 0, ISS = 0x00000004
[    4.087731] Freeing unused kernel memory: 1792K
[    4.090391]   CM = 0, WnR = 0
[    4.098091] [edafeaac9607952c] address between user and kernel address ranges
[    4.105418] Internal error: Oops: 96000004 [#1] PREEMPT SMP
[    4.111129] Modules linked in:
[    4.114275] CPU: 1 PID: 31 Comm: kworker/1:1 Not tainted 5.9.0-rc5-next-20200915+ #700
[    4.122386] Hardware name: Realtek Saola EVB (DT)
[    4.127223] Workqueue: events request_firmware_work_func
[    4.132676] pstate: 60000005 (nZCv daif -PAN -UAO BTYPE=--)
[    4.138393] pc : rtw_load_firmware_cb+0x54/0xbc
[    4.143040] lr : request_firmware_work_func+0x44/0xb4
[    4.148217] sp : ffff800010133d70
[    4.151616] x29: ffff800010133d70 x28: 0000000000000000
[    4.157069] x27: 0000000000000000 x26: 0000000000000000
[    4.162520] x25: 0000000000000000 x24: 0000000000000000
[    4.167971] x23: ffff00007ac21908 x22: ffff00007ebb2100
[    4.173424] x21: ffff00007ad35880 x20: edafeaac96079504
[    4.178877] x19: ffff00007ad35870 x18: 0000000000000000
[    4.184328] x17: 00000000000044d8 x16: 0000000000004310
[    4.189780] x15: 0000000000000800 x14: 00000000ef006305
[    4.195231] x13: ffffffff00000000 x12: ffffffffffffffff
[    4.200682] x11: 0000000000000020 x10: 0000000000000003
[    4.206135] x9 : 0000000000000000 x8 : ffff00007e73f680
[    4.211585] x7 : 0000000000000000 x6 : ffff80001119b588
[    4.217036] x5 : ffff00007e649c80 x4 : ffff00007e649c80
[    4.222487] x3 : ffff80001119b588 x2 : ffff8000108d1718
[    4.227940] x1 : ffff800011bd5000 x0 : ffff00007ac21600
[    4.233391] Call trace:
[    4.235906]  rtw_load_firmware_cb+0x54/0xbc
[    4.240198]  request_firmware_work_func+0x44/0xb4
[    4.245027]  process_one_work+0x178/0x1e4
[    4.249142]  worker_thread+0x1d0/0x268
[    4.252989]  kthread+0xe8/0xf8
[    4.256127]  ret_from_fork+0x10/0x18
[    4.259800] Code: f94013f5 a8c37bfd d65f03c0 f9000260 (f9401681)
[    4.266049] ---[ end trace f822ebae1a8545c2 ]---

To avoid this, wait on the completion callbacks in rtw_core_deinit()
before releasing firmware and continuing teardown.

Note that rtw_wait_firmware_completion() was introduced with
c8e5695eae9959fc5774c0f490f2450be8bad3de ("rtw88: load wowlan firmware
if wowlan is supported"), so backports to earlier branches may need to
inline wait_for_completion(&rtwdev->fw.completion) instead.

Fixes: e3037485c68e ("rtw88: new Realtek 802.11ac driver")
Fixes: c8e5695eae99 ("rtw88: load wowlan firmware if wowlan is supported")
Cc: Yan-Hsuan Chuang <yhchuang at realtek.com>
Signed-off-by: Andreas F?rber <afaerber at suse.de>
---
 drivers/net/wireless/realtek/rtw88/main.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c
index 9770982b2f14..dc48ec4b0a31 100644
--- a/drivers/net/wireless/realtek/rtw88/main.c
+++ b/drivers/net/wireless/realtek/rtw88/main.c
@@ -1486,6 +1486,8 @@ void rtw_core_deinit(struct rtw_dev *rtwdev)
 	struct rtw_rsvd_page *rsvd_pkt, *tmp;
 	unsigned long flags;
 
+	rtw_wait_firmware_completion(rtwdev);
+
 	if (fw->firmware)
 		release_firmware(fw->firmware);
 
-- 
2.28.0



From kvalo at codeaurora.org  Thu Sep 24 11:50:21 2020
From: kvalo at codeaurora.org (Kalle Valo)
Date: Thu, 24 Sep 2020 15:50:21 +0000 (UTC)
Subject: [PATCH 1/2] rtw88: Fix probe error handling race with firmware
 loading
In-Reply-To: <20200920132621.26468-2-afaerber@suse.de>
References: <20200920132621.26468-2-afaerber@suse.de>
Message-ID: <20200924155021.5108BC43395@smtp.codeaurora.org>

Andreas F?rber <afaerber at suse.de> wrote:

> In case of rtw8822be, a probe failure after successful rtw_core_init()
> has been observed to occasionally lead to an oops from rtw_load_firmware_cb():
> 
> [    3.924268] pci 0001:01:00.0: [10ec:b822] type 00 class 0xff0000
> [    3.930531] pci 0001:01:00.0: reg 0x10: [io  0x0000-0x00ff]
> [    3.936360] pci 0001:01:00.0: reg 0x18: [mem 0x00000000-0x0000ffff 64bit]
> [    3.944042] pci 0001:01:00.0: supports D1 D2
> [    3.948438] pci 0001:01:00.0: PME# supported from D0 D1 D2 D3hot D3cold
> [    3.957312] pci 0001:01:00.0: BAR 2: no space for [mem size 0x00010000 64bit]
> [    3.964645] pci 0001:01:00.0: BAR 2: failed to assign [mem size 0x00010000 64bit]
> [    3.972332] pci 0001:01:00.0: BAR 0: assigned [io  0x10000-0x100ff]
> [    3.986240] rtw_8822be 0001:01:00.0: enabling device (0000 -> 0001)
> [    3.992735] rtw_8822be 0001:01:00.0: failed to map pci memory
> [    3.998638] rtw_8822be 0001:01:00.0: failed to request pci io region
> [    4.005166] rtw_8822be 0001:01:00.0: failed to setup pci resources
> [    4.011580] rtw_8822be: probe of 0001:01:00.0 failed with error -12
> [    4.018827] cfg80211: Loading compiled-in X.509 certificates for regulatory database
> [    4.029121] cfg80211: Loaded X.509 cert 'sforshee: 00b28ddf47aef9cea7'
> [    4.050828] Unable to handle kernel paging request at virtual address edafeaac9607952c
> [    4.058975] Mem abort info:
> [    4.058980]   ESR = 0x96000004
> [    4.058990]   EC = 0x25: DABT (current EL), IL = 32 bits
> [    4.070353]   SET = 0, FnV = 0
> [    4.073487]   EA = 0, S1PTW = 0
> [    4.073501] dw-apb-uart 98007800.serial: forbid DMA for kernel console
> [    4.076723] Data abort info:
> [    4.086415]   ISV = 0, ISS = 0x00000004
> [    4.087731] Freeing unused kernel memory: 1792K
> [    4.090391]   CM = 0, WnR = 0
> [    4.098091] [edafeaac9607952c] address between user and kernel address ranges
> [    4.105418] Internal error: Oops: 96000004 [#1] PREEMPT SMP
> [    4.111129] Modules linked in:
> [    4.114275] CPU: 1 PID: 31 Comm: kworker/1:1 Not tainted 5.9.0-rc5-next-20200915+ #700
> [    4.122386] Hardware name: Realtek Saola EVB (DT)
> [    4.127223] Workqueue: events request_firmware_work_func
> [    4.132676] pstate: 60000005 (nZCv daif -PAN -UAO BTYPE=--)
> [    4.138393] pc : rtw_load_firmware_cb+0x54/0xbc
> [    4.143040] lr : request_firmware_work_func+0x44/0xb4
> [    4.148217] sp : ffff800010133d70
> [    4.151616] x29: ffff800010133d70 x28: 0000000000000000
> [    4.157069] x27: 0000000000000000 x26: 0000000000000000
> [    4.162520] x25: 0000000000000000 x24: 0000000000000000
> [    4.167971] x23: ffff00007ac21908 x22: ffff00007ebb2100
> [    4.173424] x21: ffff00007ad35880 x20: edafeaac96079504
> [    4.178877] x19: ffff00007ad35870 x18: 0000000000000000
> [    4.184328] x17: 00000000000044d8 x16: 0000000000004310
> [    4.189780] x15: 0000000000000800 x14: 00000000ef006305
> [    4.195231] x13: ffffffff00000000 x12: ffffffffffffffff
> [    4.200682] x11: 0000000000000020 x10: 0000000000000003
> [    4.206135] x9 : 0000000000000000 x8 : ffff00007e73f680
> [    4.211585] x7 : 0000000000000000 x6 : ffff80001119b588
> [    4.217036] x5 : ffff00007e649c80 x4 : ffff00007e649c80
> [    4.222487] x3 : ffff80001119b588 x2 : ffff8000108d1718
> [    4.227940] x1 : ffff800011bd5000 x0 : ffff00007ac21600
> [    4.233391] Call trace:
> [    4.235906]  rtw_load_firmware_cb+0x54/0xbc
> [    4.240198]  request_firmware_work_func+0x44/0xb4
> [    4.245027]  process_one_work+0x178/0x1e4
> [    4.249142]  worker_thread+0x1d0/0x268
> [    4.252989]  kthread+0xe8/0xf8
> [    4.256127]  ret_from_fork+0x10/0x18
> [    4.259800] Code: f94013f5 a8c37bfd d65f03c0 f9000260 (f9401681)
> [    4.266049] ---[ end trace f822ebae1a8545c2 ]---
> 
> To avoid this, wait on the completion callbacks in rtw_core_deinit()
> before releasing firmware and continuing teardown.
> 
> Note that rtw_wait_firmware_completion() was introduced with
> c8e5695eae9959fc5774c0f490f2450be8bad3de ("rtw88: load wowlan firmware
> if wowlan is supported"), so backports to earlier branches may need to
> inline wait_for_completion(&rtwdev->fw.completion) instead.
> 
> Fixes: e3037485c68e ("rtw88: new Realtek 802.11ac driver")
> Fixes: c8e5695eae99 ("rtw88: load wowlan firmware if wowlan is supported")
> Cc: Yan-Hsuan Chuang <yhchuang at realtek.com>
> Signed-off-by: Andreas F?rber <afaerber at suse.de>

2 patches applied to wireless-drivers-next.git, thanks.

ecda9cda3338 rtw88: Fix probe error handling race with firmware loading
ac4bac99161e rtw88: Fix potential probe error handling race with wow firmware loading

-- 
https://patchwork.kernel.org/patch/11787655/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches



