[][MT79xx add USBIF compliance toolkit]

[Description]
Add USBIF compliance toolkit for MT79xx
usb3hqa
RG_USB20_INTR_EN
RG_UsB20_VRT_VREF_SEL
RG_USB20_TERM_VREF_SEL
RG_USB20_HSTX_SRCTRL
RG_USB20_DISCTH
RG_CHGDT_EN

[Release-log]
N/A

Change-Id: I2f7e4bdf27c2197a198ce10af3a09c362adfc04a
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/5351515
diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/unusual-declaration.h b/target/linux/mediatek/files-5.4/drivers/usb/host/unusual-declaration.h
new file mode 100644
index 0000000..f517e20
--- /dev/null
+++ b/target/linux/mediatek/files-5.4/drivers/usb/host/unusual-declaration.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * xhci-mtk-unusuallib.h -- xhci toolkit header file
+ *
+ * Copyright (C) 2021 Mediatek Inc - http://www.mediatek.com
+ *
+ * Author: Zhanyong Wang <zhanyong.wang@mediatek.com>
+ */
+
+ DEVICE_ATTR_DECLARED(RG_USB20_INTR_EN);
+ DEVICE_ATTR_DECLARED(RG_USB20_VRT_VREF_SEL);
+ DEVICE_ATTR_DECLARED(RG_USB20_TERM_VREF_SEL);
+ DEVICE_ATTR_DECLARED(RG_USB20_HSTX_SRCTRL);
+ DEVICE_ATTR_DECLARED(RG_USB20_DISCTH);
+ DEVICE_ATTR_DECLARED(RG_CHGDT_EN);
+
+ #define HQA_INFORMACTION_COLLECTS() do {\
+	ECHO_HQA(USB20_PHY_USBPHYACR0, RG_USB20_INTR_EN, 1); \
+	ECHO_HQA(USB20_PHY_USBPHYACR1, RG_USB20_VRT_VREF_SEL, 3); \
+	ECHO_HQA(USB20_PHY_USBPHYACR1, RG_USB20_TERM_VREF_SEL, 3); \
+	ECHO_HQA(USB20_PHY_USBPHYACR5, RG_USB20_HSTX_SRCTRL, 3); \
+	ECHO_HQA(USB20_PHY_USBPHYACR6, RG_USB20_DISCTH, 4); \
+	ECHO_HQA(USB20_PHY_U2PHYBC12C, RG_CHGDT_EN, 1); \
+	} while (0)
+
diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/unusual-statement.h b/target/linux/mediatek/files-5.4/drivers/usb/host/unusual-statement.h
new file mode 100644
index 0000000..b929342
--- /dev/null
+++ b/target/linux/mediatek/files-5.4/drivers/usb/host/unusual-statement.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * xhci-mtk-unusuallib.h -- xhci toolkit header file
+ *
+ * Copyright (C) 2021 Mediatek Inc - http://www.mediatek.com
+ *
+ * Author: Zhanyong Wang <zhanyong.wang@mediatek.com>
+ */
+
+UNUSUAL_DEVICE_ATTR(RG_USB20_INTR_EN),
+UNUSUAL_DEVICE_ATTR(RG_USB20_VRT_VREF_SEL),
+UNUSUAL_DEVICE_ATTR(RG_USB20_TERM_VREF_SEL),
+UNUSUAL_DEVICE_ATTR(RG_USB20_HSTX_SRCTRL),
+UNUSUAL_DEVICE_ATTR(RG_USB20_DISCTH),
+UNUSUAL_DEVICE_ATTR(RG_CHGDT_EN),
+
diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-chgdt-en.c b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-chgdt-en.c
new file mode 100644
index 0000000..13626c1
--- /dev/null
+++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-chgdt-en.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * xHCI host controller toolkit driver for chgdt-en
+ *
+ * Copyright (C) 2021  MediaTek Inc.
+ *
+ *  Author: Zhanyong Wang <zhanyong.wang@mediatek.com>
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include "xhci-mtk.h"
+#include "xhci-mtk-test.h"
+#include "xhci-mtk-unusual.h"
+
+static ssize_t RG_CHGDT_EN_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev);
+	struct usb_hcd *hcd = mtk->hcd;
+	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+	ssize_t cnt = 0;
+	void __iomem *addr;
+	u32 val;
+	u32 i;
+	int ports;
+	char str[32];
+	int index = 0;
+	u32 io, length;
+	int ret;
+
+	ports = mtk->num_u3_ports + mtk->num_u2_ports;
+	cnt += sprintf(buf + cnt, " RG_CHGDT_EN usage:\n");
+	cnt += sprintf(buf + cnt,
+		"   echo u2p index 1b0 > RG_CHGDT_EN\n");
+	if (mtk->num_u3_ports + 1 != ports)
+		cnt += sprintf(buf + cnt, "	parameter: u2p: %i ~ %i\n",
+					mtk->num_u3_ports + 1, ports);
+	else
+		cnt += sprintf(buf + cnt, "	parameter: u2p: %i\n",
+					mtk->num_u3_ports + 1);
+	if (mtk->num_u2_ports > 1)
+		cnt += sprintf(buf + cnt, "	parameter: index: 0 ~ %i\n",
+			       mtk->num_u2_ports);
+	else
+		cnt += sprintf(buf + cnt, "	parameter: index: 0\n");
+
+	cnt += sprintf(buf + cnt, " e.g.: echo 2 0 1b1 > RG_CHGDT_EN\n");
+	cnt += sprintf(buf + cnt,
+		"  port2 binding phy 0, enable 1b'1 as CHGDT_EN\n");
+
+	cnt += sprintf(buf + cnt,
+			"\n=========current HQA setting check=========\n");
+	for (i = 1; i <= ports; i++) {
+		addr = &xhci->op_regs->port_status_base +
+			NUM_PORT_REGS * ((i - 1) & 0xff);
+		val = readl(addr);
+		if (i <= mtk->num_u3_ports) {
+			cnt += sprintf(buf + cnt,
+				       "USB30 Port%i: 0x%08X\n", i, val);
+		} else {
+			cnt += sprintf(buf + cnt,
+				       "USB20 Port%i: 0x%08X\n", i, val);
+
+			ret = query_phy_addr(dev->of_node,
+						 &index, &io, &length);
+			if (ret && ret != -EACCES) {
+				if (ret == -EPERM)
+					cnt += sprintf(buf + cnt,
+					"USB20 Port%i (Phy%i: absent)\n",
+					i, index);
+				else
+					cnt += sprintf(buf + cnt,
+					"USB20 Port%i (Phy%i) failure %i\n",
+						 i, index, ret);
+				continue;
+			}
+
+			cnt += sprintf(buf + cnt,
+				"USB20 Port%i (Phy%i:%sable): 0x%08X 0x%08X\n",
+				i, index, ret ? " dis" : " en", io, length);
+
+			addr   = ioremap_nocache(io, length);
+			addr  += (length != 0x100) ? 0x300 : 0;
+
+			HQA_INFORMACTION_COLLECTS();
+
+			iounmap(addr);
+			index ++;
+		}
+	}
+
+	if (mtk->hqa_pos) {
+		cnt += sprintf(buf + cnt, "%s", mtk->hqa_buf);
+		mtk->hqa_pos = 0;
+	}
+
+	return cnt;
+}
+
+static ssize_t RG_CHGDT_EN_store(struct device *dev,
+                        struct device_attribute *attr,
+                        const char *buf, size_t n)
+{
+	u32 val;
+	u32 io;
+	u32 length;
+	int ports;
+	int words;
+	int port;
+	int index;
+	int ret;
+	char *str = NULL;
+	void __iomem *addr;
+	struct xhci_hcd_mtk *mtk  = dev_get_drvdata(dev);
+	struct device_node  *node = dev->of_node;
+
+	ports = mtk->num_u3_ports + mtk->num_u2_ports;
+	mtk->hqa_pos = 0;
+
+	memset(mtk->hqa_buf, 0, mtk->hqa_size);
+
+	str = kzalloc(n, GFP_ATOMIC);
+
+	hqa_info(mtk, "RG_CHGDT_EN(%lu): %s\n", n, buf);
+
+	words = sscanf(buf, "%i %i 1b%1[0,1]", &port, &index, str);
+	if ((words != 3) ||
+	    (port < mtk->num_u3_ports || port > ports)) {
+		hqa_info(mtk, "Check params(%i):\" %i %i %s\", Please!\n",
+			words, port, index, str);
+
+		ret = -EINVAL;
+		goto error;
+	}
+
+	hqa_info(mtk, " params: %i %i %s\n",
+		port, index, str);
+
+	ret = query_phy_addr(node, &index, &io, &length);
+	if (ret && ret != -EACCES)
+		goto error;
+
+	io += (length != 0x100) ? 0x300 : 0;
+	io += USB20_PHY_U2PHYBC12C;
+
+	addr = ioremap_nocache(io, 4);
+	val = binary_write_width1(addr, SHFT_RG_CHGDT_EN, str);
+	hqa_info(mtk, "Port%i(Phy%i)[0x%08X]: 0x%08X but 0x%08X\n",
+		port, index, io, val, readl(addr));
+
+	iounmap(addr);
+	ret = n;
+
+error:
+	kfree(str);
+	return ret;
+}
+DEVICE_ATTR_RW(RG_CHGDT_EN);
diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-discth.c b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-discth.c
new file mode 100644
index 0000000..66a024a
--- /dev/null
+++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-discth.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * xHCI host controller toolkit driver for usb20 discth
+ *
+ * Copyright (C) 2021  MediaTek Inc.
+ *
+ *  Author: Zhanyong Wang <zhanyong.wang@mediatek.com>
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include "xhci-mtk.h"
+#include "xhci-mtk-test.h"
+#include "xhci-mtk-unusual.h"
+
+static ssize_t RG_USB20_DISCTH_show(struct device *dev,
+                        struct device_attribute *attr, char *buf)
+{
+	struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev);
+	struct usb_hcd *hcd = mtk->hcd;
+	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+	ssize_t cnt = 0;
+	void __iomem *addr;
+	u32 val;
+	u32 i;
+	int ports;
+	char str[32];
+	int index = 0;
+	u32 io, length;
+	int ret;
+
+	ports = mtk->num_u3_ports + mtk->num_u2_ports;
+	cnt += sprintf(buf + cnt, " RG_USB20_DISCTH usage:\n");
+	cnt += sprintf(buf + cnt,
+		"   echo u2p index 4b0011 > RG_USB20_DISCTH\n");
+	if (mtk->num_u3_ports + 1 != ports)
+		cnt += sprintf(buf + cnt, "	parameter: u2p: %i ~ %i\n",
+					mtk->num_u3_ports + 1, ports);
+	else
+		cnt += sprintf(buf + cnt, "	parameter: u2p: %i\n",
+					mtk->num_u3_ports + 1);
+
+	if (mtk->num_u2_ports > 1)
+		cnt += sprintf(buf + cnt, "	parameter: index: 0 ~ %i\n",
+			       mtk->num_u2_ports);
+	else
+		cnt += sprintf(buf + cnt, "	parameter: index: 0\n");
+
+	cnt += sprintf(buf + cnt,
+			 " e.g.: echo 2 0 4b1010 > RG_USB20_DISCTH\n");
+	cnt += sprintf(buf + cnt,
+		"  port2 binding phy 0, tune 4b'1010 as DISCTH value\n");
+
+	cnt += sprintf(buf + cnt,
+			"\n=========current HQA setting check=========\n");
+	for (i = 1; i <= ports; i++) {
+		addr = &xhci->op_regs->port_status_base +
+			NUM_PORT_REGS * ((i - 1) & 0xff);
+		val = readl(addr);
+		if (i <= mtk->num_u3_ports) {
+			cnt += sprintf(buf + cnt,
+				       "USB30 Port%i: 0x%08X\n", i, val);
+		} else {
+			cnt += sprintf(buf + cnt,
+				       "USB20 Port%i: 0x%08X\n", i, val);
+
+			ret = query_phy_addr(dev->of_node,
+						 &index, &io, &length);
+			if (ret && ret != -EACCES) {
+				if (ret == -EPERM)
+					cnt += sprintf(buf + cnt,
+					"USB20 Port%i (Phy%i: absent)\n",
+					i, index);
+				else
+					cnt += sprintf(buf + cnt,
+					"USB20 Port%i (Phy%i) failure %i\n",
+						 i, index, ret);
+				continue;
+			}
+
+			cnt += sprintf(buf + cnt,
+				"USB20 Port%i (Phy%i:%sable): 0x%08X 0x%08X\n",
+				i, index, ret ? " dis" : " en", io, length);
+
+			addr   = ioremap_nocache(io, length);
+			addr  += (length != 0x100) ? 0x300 : 0;
+
+			HQA_INFORMACTION_COLLECTS();
+
+			iounmap(addr);
+			index ++;
+		}
+	}
+
+	if (mtk->hqa_pos) {
+		cnt += sprintf(buf + cnt, "%s", mtk->hqa_buf);
+		mtk->hqa_pos = 0;
+	}
+
+	return cnt;
+}
+
+static ssize_t RG_USB20_DISCTH_store(struct device *dev,
+                        struct device_attribute *attr,
+                        const char *buf, size_t n)
+{
+	u32 val;
+	u32 io;
+	u32 length;
+	int ports;
+	int words;
+	int port;
+	int index;
+	int ret;
+	char *str = NULL;
+	void __iomem *addr;
+	struct xhci_hcd_mtk *mtk  = dev_get_drvdata(dev);
+	struct device_node  *node = dev->of_node;
+
+	ports = mtk->num_u3_ports + mtk->num_u2_ports;
+	mtk->hqa_pos = 0;
+
+	memset(mtk->hqa_buf, 0, mtk->hqa_size);
+
+	str = kzalloc(n, GFP_ATOMIC);
+
+	hqa_info(mtk, "RG_USB20_DISCTH(%lu): %s\n", n, buf);
+
+	words = sscanf(buf, "%i %i 4b%4[0,1]", &port, &index, str);
+	if ((words != 3) ||
+	    (port < mtk->num_u3_ports || port > ports)) {
+		hqa_info(mtk, "Check params(%i):\" %i %i %s\", Please!\n",
+			words, port, index, str);
+
+		ret = -EINVAL;
+		goto error;
+	}
+
+	hqa_info(mtk, " params: %i %i %s\n",
+		port, index, str);
+
+	ret = query_phy_addr(node, &index, &io, &length);
+	if (ret && ret != -EACCES)
+		goto error;
+
+	io += (length != 0x100) ? 0x300 : 0;
+	io += USB20_PHY_USBPHYACR6;
+
+	addr = ioremap_nocache(io, 4);
+	val = binary_write_width4(addr, SHFT_RG_USB20_DISCTH, str);
+	hqa_info(mtk, "Port%i(Phy%i)[0x%08X]: 0x%08X but 0x%08X\n",
+		port, index, io, val, readl(addr));
+
+	iounmap(addr);
+	ret = n;
+
+error:
+	kfree(str);
+	return ret;
+}
+DEVICE_ATTR_RW(RG_USB20_DISCTH);
diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-hstx-srctrl.c b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-hstx-srctrl.c
new file mode 100644
index 0000000..a7791cf
--- /dev/null
+++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-hstx-srctrl.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * xHCI host controller toolkit driver for hstx-srctrl
+ *
+ * Copyright (C) 2021  MediaTek Inc.
+ *
+ *  Author: Zhanyong Wang <zhanyong.wang@mediatek.com>
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include "xhci-mtk.h"
+#include "xhci-mtk-test.h"
+#include "xhci-mtk-unusual.h"
+
+static ssize_t RG_USB20_HSTX_SRCTRL_show(struct device *dev,
+                        struct device_attribute *attr, char *buf)
+{
+	struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev);
+	struct usb_hcd *hcd = mtk->hcd;
+	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+	ssize_t cnt = 0;
+	void __iomem *addr;
+	u32 val;
+	u32 i;
+	int ports;
+	char str[32];
+	int index = 0;
+	u32 io, length;
+	int ret;
+
+	ports = mtk->num_u3_ports + mtk->num_u2_ports;
+	cnt += sprintf(buf + cnt, " RG_USB20_HSTX_SRCTRL usage:\n");
+	cnt += sprintf(buf + cnt,
+	"   echo u2p index 3b011 > RG_USB20_HSTX_SRCTRL\n");
+	if (mtk->num_u3_ports + 1 != ports)
+		cnt += sprintf(buf + cnt, "	parameter: u2p: %i ~ %i\n",
+					mtk->num_u3_ports + 1, ports);
+	else
+		cnt += sprintf(buf + cnt, "	parameter: u2p: %i\n",
+					mtk->num_u3_ports + 1);
+
+	if (mtk->num_u2_ports > 1)
+		cnt += sprintf(buf + cnt, "	parameter: index: 0 ~ %i\n",
+			       mtk->num_u2_ports);
+	else
+		cnt += sprintf(buf + cnt, "	parameter: index: 0\n");
+
+	cnt += sprintf(buf + cnt,
+			 " e.g.: echo 2 0 3b010 > RG_USB20_HSTX_SRCTRL\n");
+	cnt += sprintf(buf + cnt,
+	"  port2 binding phy 0, tune 3b'010 as HSTX_SRCTRL value\n");
+
+	cnt += sprintf(buf + cnt,
+			"\n=========current HQA setting check=========\n");
+	for (i = 1; i <= ports; i++) {
+		addr = &xhci->op_regs->port_status_base +
+			NUM_PORT_REGS * ((i - 1) & 0xff);
+		val = readl(addr);
+		if (i <= mtk->num_u3_ports) {
+			cnt += sprintf(buf + cnt,
+				       "USB30 Port%i: 0x%08X\n", i, val);
+		} else {
+			cnt += sprintf(buf + cnt,
+				       "USB20 Port%i: 0x%08X\n", i, val);
+
+			ret = query_phy_addr(dev->of_node,
+						 &index, &io, &length);
+			if (ret && ret != -EACCES) {
+				if (ret == -EPERM)
+					cnt += sprintf(buf + cnt,
+					"USB20 Port%i (Phy%i: absent)\n",
+					i, index);
+				else
+					cnt += sprintf(buf + cnt,
+					"USB20 Port%i (Phy%i) failure %i\n",
+						 i, index, ret);
+				continue;
+			}
+
+			cnt += sprintf(buf + cnt,
+				"USB20 Port%i (Phy%i:%sable): 0x%08X 0x%08X\n",
+				i, index, ret ? " dis" : " en", io, length);
+
+			addr   = ioremap_nocache(io, length);
+			addr  += (length != 0x100) ? 0x300 : 0;
+
+			HQA_INFORMACTION_COLLECTS();
+
+			iounmap(addr);
+			index ++;
+		}
+	}
+
+	if (mtk->hqa_pos) {
+		cnt += sprintf(buf + cnt, "%s", mtk->hqa_buf);
+		mtk->hqa_pos = 0;
+	}
+
+	return cnt;
+}
+
+static ssize_t RG_USB20_HSTX_SRCTRL_store(struct device *dev,
+                        struct device_attribute *attr,
+                        const char *buf, size_t n)
+{
+	u32 val;
+	u32 io;
+	u32 length;
+	int ports;
+	int words;
+	int port;
+	int index;
+	int ret;
+	char *str = NULL;
+	void __iomem *addr;
+	struct xhci_hcd_mtk *mtk  = dev_get_drvdata(dev);
+	struct device_node  *node = dev->of_node;
+
+	ports = mtk->num_u3_ports + mtk->num_u2_ports;
+	mtk->hqa_pos = 0;
+
+	memset(mtk->hqa_buf, 0, mtk->hqa_size);
+
+	str = kzalloc(n, GFP_ATOMIC);
+
+	hqa_info(mtk, "RG_USB20_HSTX_SRCTRL(%lu): %s\n", n, buf);
+
+	words = sscanf(buf, "%i %i 3b%3[0,1]", &port, &index, str);
+	if ((words != 3) ||
+	    (port < mtk->num_u3_ports || port > ports)) {
+		hqa_info(mtk, "Check params(%i):\" %i %i %s\", Please!\n",
+			words, port, index, str);
+
+		ret = -EINVAL;
+		goto error;
+	}
+
+	hqa_info(mtk, " params: %i %i %s\n",
+		port, index, str);
+
+	ret = query_phy_addr(node, &index, &io, &length);
+	if (ret && ret != -EACCES)
+		goto error;
+
+	io += (length != 0x100) ? 0x300 : 0;
+	io += USB20_PHY_USBPHYACR5;
+
+	addr = ioremap_nocache(io, 4);
+	val = binary_write_width3(addr, SHFT_RG_USB20_HSTX_SRCTRL, str);
+	hqa_info(mtk, "Port%i(Phy%i)[0x%08X]: 0x%08X but 0x%08X\n",
+		port, index, io, val, readl(addr));
+
+	iounmap(addr);
+	ret = n;
+
+error:
+	kfree(str);
+	return ret;
+}
+DEVICE_ATTR_RW(RG_USB20_HSTX_SRCTRL);
diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-intr-en.c b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-intr-en.c
new file mode 100644
index 0000000..acdaf8b
--- /dev/null
+++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-intr-en.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * xHCI host controller toolkit driver for intr-en
+ *
+ * Copyright (C) 2021  MediaTek Inc.
+ *
+ *  Author: Zhanyong Wang <zhanyong.wang@mediatek.com>
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include "xhci-mtk.h"
+#include "xhci-mtk-test.h"
+#include "xhci-mtk-unusual.h"
+
+static ssize_t RG_USB20_INTR_EN_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev);
+	struct usb_hcd *hcd = mtk->hcd;
+	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+	ssize_t cnt = 0;
+	void __iomem *addr;
+	u32 val;
+	u32 i;
+	int ports;
+	char str[32];
+	int index = 0;
+	u32 io, length;
+	int ret;
+
+	ports = mtk->num_u3_ports + mtk->num_u2_ports;
+	cnt += sprintf(buf + cnt, " RG_USB20_INTR_EN usage:\n");
+	cnt += sprintf(buf + cnt,
+                "   echo u2p index 1b0 > RG_USB20_INTR_EN\n");
+	if (mtk->num_u3_ports + 1 != ports)
+		cnt += sprintf(buf + cnt, "	parameter: u2p: %i ~ %i\n",
+					mtk->num_u3_ports + 1, ports);
+	else
+		cnt += sprintf(buf + cnt, "	parameter: u2p: %i\n",
+					mtk->num_u3_ports + 1);
+
+	if (mtk->num_u2_ports > 1)
+		cnt += sprintf(buf + cnt, "	parameter: index: 0 ~ %i\n",
+			       mtk->num_u2_ports);
+	else
+		cnt += sprintf(buf + cnt, "	parameter: index: 0\n");
+
+	cnt += sprintf(buf + cnt, " e.g.: echo 2 0 1b1 > RG_USB20_INTR_EN\n");
+	cnt += sprintf(buf + cnt,
+		"  port2 binding phy 0, enable 1b'1 as INTR_EN\n");
+
+	cnt += sprintf(buf + cnt,
+			"\n=========current HQA setting check=========\n");
+	for (i = 1; i <= ports; i++) {
+		addr = &xhci->op_regs->port_status_base +
+			NUM_PORT_REGS * ((i - 1) & 0xff);
+		val = readl(addr);
+		if (i <= mtk->num_u3_ports) {
+			cnt += sprintf(buf + cnt,
+				       "USB30 Port%i: 0x%08X\n", i, val);
+		} else {
+			cnt += sprintf(buf + cnt,
+				       "USB20 Port%i: 0x%08X\n", i, val);
+
+			ret = query_phy_addr(dev->of_node,
+						 &index, &io, &length);
+			if (ret && ret != -EACCES) {
+				if (ret == -EPERM)
+					cnt += sprintf(buf + cnt,
+					"USB20 Port%i (Phy%i: absent)\n",
+					i, index);
+				else
+					cnt += sprintf(buf + cnt,
+					"USB20 Port%i (Phy%i) failure %i\n",
+						 i, index, ret);
+				continue;
+			}
+
+			cnt += sprintf(buf + cnt,
+				"USB20 Port%i (Phy%i:%sable): 0x%08X 0x%08X\n",
+				i, index, ret ? " dis" : " en", io, length);
+
+			addr   = ioremap_nocache(io, length);
+			addr  += (length != 0x100) ? 0x300 : 0;
+
+			HQA_INFORMACTION_COLLECTS();
+
+			iounmap(addr);
+			index ++;
+		}
+	}
+
+	if (mtk->hqa_pos) {
+		cnt += sprintf(buf + cnt, "%s", mtk->hqa_buf);
+		mtk->hqa_pos = 0;
+	}
+
+	return cnt;
+}
+
+static ssize_t RG_USB20_INTR_EN_store(struct device *dev,
+                        struct device_attribute *attr,
+                        const char *buf, size_t n)
+{
+	u32 val;
+	u32 io;
+	u32 length;
+	int ports;
+	int words;
+	int port;
+	int index;
+	int ret;
+	char *str = NULL;
+	void __iomem *addr;
+	struct xhci_hcd_mtk *mtk  = dev_get_drvdata(dev);
+	struct device_node  *node = dev->of_node;
+
+	ports = mtk->num_u3_ports + mtk->num_u2_ports;
+	mtk->hqa_pos = 0;
+
+	memset(mtk->hqa_buf, 0, mtk->hqa_size);
+
+	str = kzalloc(n, GFP_ATOMIC);
+
+	hqa_info(mtk, "RG_USB20_INTR_EN(%lu): %s\n", n, buf);
+
+	words = sscanf(buf, "%i %i 1b%1[0,1]", &port, &index, str);
+	if ((words != 3) ||
+	    (port < mtk->num_u3_ports || port > ports)) {
+		hqa_info(mtk, "Check params(%i):\" %i %i %s\", Please!\n",
+			words, port, index, str);
+
+		ret = -EINVAL;
+		goto error;
+	}
+
+	hqa_info(mtk, " params: %i %i %s\n",
+		port, index, str);
+
+	ret = query_phy_addr(node, &index, &io, &length);
+	if (ret && ret != -EACCES)
+		goto error;
+
+	io += (length != 0x100) ? 0x300 : 0;
+	io += USB20_PHY_USBPHYACR0;
+
+	addr = ioremap_nocache(io, 4);
+	val = binary_write_width1(addr, SHFT_RG_USB20_INTR_EN, str);
+	hqa_info(mtk, "Port%i(Phy%i)[0x%08X]: 0x%08X but 0x%08X\n",
+		port, index, io, val, readl(addr));
+
+	iounmap(addr);
+	ret = n;
+
+error:
+	kfree(str);
+	return ret;
+}
+DEVICE_ATTR_RW(RG_USB20_INTR_EN);
diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-term-vref.c b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-term-vref.c
new file mode 100644
index 0000000..3b40ef9
--- /dev/null
+++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-term-vref.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * xHCI host controller toolkit driver for term vref
+ *
+ * Copyright (C) 2021  MediaTek Inc.
+ *
+ *  Author: Zhanyong Wang <zhanyong.wang@mediatek.com>
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include "xhci-mtk.h"
+#include "xhci-mtk-test.h"
+#include "xhci-mtk-unusual.h"
+
+static
+ssize_t RG_USB20_TERM_VREF_SEL_show(struct device *dev,
+                        struct device_attribute *attr, char *buf)
+{
+	struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev);
+	struct usb_hcd *hcd = mtk->hcd;
+	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+	ssize_t cnt = 0;
+	void __iomem *addr;
+	u32 val;
+	u32 i;
+	int ports;
+	char str[32];
+	int index = 0;
+	u32 io, length;
+	int ret;
+
+	ports = mtk->num_u3_ports + mtk->num_u2_ports;
+	cnt += sprintf(buf + cnt, " RG_USB20_TERM_VREF_SEL usage:\n");
+	cnt += sprintf(buf + cnt,
+		"   echo u2p index 3b011 > RG_USB20_TERM_VREF_SEL\n");
+	if (mtk->num_u3_ports + 1 != ports)
+		cnt += sprintf(buf + cnt, "	parameter: u2p: %i ~ %i\n",
+					mtk->num_u3_ports + 1, ports);
+	else
+		cnt += sprintf(buf + cnt, "	parameter: u2p: %i\n",
+					mtk->num_u3_ports + 1);
+
+	if (mtk->num_u2_ports > 1)
+		cnt += sprintf(buf + cnt, "	parameter: index: 0 ~ %i\n",
+			       mtk->num_u2_ports);
+	else
+		cnt += sprintf(buf + cnt, "	parameter: index: 0\n");
+
+	cnt += sprintf(buf + cnt,
+			 " e.g.: echo 2 0 3b010 > RG_USB20_TERM_VREF_SEL\n");
+	cnt += sprintf(buf + cnt,
+		"  port2 binding phy 0, tune 3b'010 as TERM_VREF value\n");
+
+	cnt += sprintf(buf + cnt,
+			"\n=========current HQA setting check=========\n");
+	for (i = 1; i <= ports; i++) {
+		addr = &xhci->op_regs->port_status_base +
+			NUM_PORT_REGS * ((i - 1) & 0xff);
+		val = readl(addr);
+		if (i <= mtk->num_u3_ports) {
+			cnt += sprintf(buf + cnt,
+				       "USB30 Port%i: 0x%08X\n", i, val);
+		} else {
+			cnt += sprintf(buf + cnt,
+				       "USB20 Port%i: 0x%08X\n", i, val);
+
+			ret = query_phy_addr(dev->of_node,
+						 &index, &io, &length);
+			if (ret && ret != -EACCES) {
+				if (ret == -EPERM)
+					cnt += sprintf(buf + cnt,
+					"USB20 Port%i (Phy%i: absent)\n",
+					i, index);
+				else
+					cnt += sprintf(buf + cnt,
+					"USB20 Port%i (Phy%i) failure %i\n",
+						 i, index, ret);
+				continue;
+			}
+
+			cnt += sprintf(buf + cnt,
+				"USB20 Port%i (Phy%i:%sable): 0x%08X 0x%08X\n",
+				i, index, ret ? " dis" : " en", io, length);
+
+			addr   = ioremap_nocache(io, length);
+			addr  += (length != 0x100) ? 0x300 : 0;
+
+			HQA_INFORMACTION_COLLECTS();
+
+			iounmap(addr);
+			index ++;
+		}
+	}
+
+	if (mtk->hqa_pos) {
+		cnt += sprintf(buf + cnt, "%s", mtk->hqa_buf);
+		mtk->hqa_pos = 0;
+	}
+
+	return cnt;
+}
+
+static
+ssize_t RG_USB20_TERM_VREF_SEL_store(struct device *dev,
+                        struct device_attribute *attr,
+                        const char *buf, size_t n)
+{
+	u32 val;
+	u32 io;
+	u32 length;
+	int ports;
+	int words;
+	int port;
+	int index;
+	int ret;
+	char *str = NULL;
+	void __iomem *addr;
+	struct xhci_hcd_mtk *mtk  = dev_get_drvdata(dev);
+	struct device_node  *node = dev->of_node;
+
+	ports = mtk->num_u3_ports + mtk->num_u2_ports;
+	mtk->hqa_pos = 0;
+
+	memset(mtk->hqa_buf, 0, mtk->hqa_size);
+
+	str = kzalloc(n, GFP_ATOMIC);
+
+	hqa_info(mtk, "RG_USB20_TERM_VREF_SEL(%lu): %s\n", n, buf);
+
+	words = sscanf(buf, "%i %i 3b%3[0,1]", &port, &index, str);
+	if ((words != 3) ||
+	    (port < mtk->num_u3_ports || port > ports)) {
+		hqa_info(mtk, "Check params(%i):\" %i %i %s\", Please!\n",
+			words, port, index, str);
+
+		ret = -EINVAL;
+		goto error;
+	}
+
+	hqa_info(mtk, " params: %i %i %s\n",
+		port, index, str);
+
+	ret = query_phy_addr(node, &index, &io, &length);
+	if (ret && ret != -EACCES)
+		goto error;
+
+	io += (length != 0x100) ? 0x300 : 0;
+	io += USB20_PHY_USBPHYACR1;
+
+	addr = ioremap_nocache(io, 4);
+	val = binary_write_width3(addr, SHFT_RG_USB20_TERM_VREF_SEL, str);
+	hqa_info(mtk, "Port%i(Phy%i)[0x%08X]: 0x%08X but 0x%08X\n",
+		port, index, io, val, readl(addr));
+
+	iounmap(addr);
+	ret = n;
+
+error:
+	kfree(str);
+	return ret;
+}
+DEVICE_ATTR_RW(RG_USB20_TERM_VREF_SEL);
+
diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-test.c b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-test.c
index fc764a6..4939e55 100644
--- a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-test.c
+++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-test.c
@@ -1,18 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
- * Copyright (c) 2015 MediaTek Inc.
- * Author:
- *  Shaocheng.Wang <shaocheng.wang@mediatek.com>
- *  Chunfeng.Yun <chunfeng.yun@mediatek.com>
+ * xHCI host controller toolkit driver
  *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
+ * Copyright (C) 2021  MediaTek Inc.
  *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
+ *  Author: Zhanyong Wang <zhanyong.wang@mediatek.com>
+ *          Shaocheng.Wang <shaocheng.wang@mediatek.com>
+ *          Chunfeng.Yun <chunfeng.yun@mediatek.com>
  */
 
 #include <linux/platform_device.h>
@@ -20,9 +14,13 @@
 #include <linux/slab.h>
 #include <linux/usb.h>
 #include <linux/kobject.h>
-
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <dt-bindings/phy/phy.h>
 #include "../core/usb.h"
 #include "xhci-mtk.h"
+#include "xhci-mtk-test.h"
+#include "xhci-mtk-unusual.h"
 
 static int t_test_j(struct xhci_hcd_mtk *mtk, int argc, char **argv);
 static int t_test_k(struct xhci_hcd_mtk *mtk, int argc, char **argv);
@@ -113,31 +111,9 @@
 
 static int test_mode_exit(struct xhci_hcd_mtk *mtk)
 {
-	/* struct usb_hcd *hcd = mtk->hcd; */
-	/* struct xhci_hcd *xhci = hcd_to_xhci(hcd); */
-
-	if (mtk->test_mode == 1) {
-#if 0
-		xhci_reset(xhci);
-		/*reinitIP(&pdev->dev);*/
-
-		if (!usb_hcd_is_primary_hcd(test_hcd))
-			secondary_hcd = test_hcd;
-		else
-			secondary_hcd = xhci->shared_hcd;
-
-		retval = xhci_init(test_hcd->primary_hcd);
-		if (retval)
-			return retval;
-
-		retval = xhci_run(test_hcd->primary_hcd);
-		if (!retval)
-			retval = xhci_run(secondary_hcd);
-
-		/*enableXhciAllPortPower(xhci);*/
-#endif
+	if (mtk->test_mode == 1)
 		mtk->test_mode = 0;
-	}
+
 	return 0;
 }
 
@@ -461,9 +437,15 @@
 	return 0;
 }
 
-static ssize_t mu3h_hqa_show(struct device *dev,
+static ssize_t hqa_show(struct device *dev,
 			struct device_attribute *attr, char *buf)
 {
+	struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev);
+	struct usb_hcd *hcd = mtk->hcd;
+	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+	u32 __iomem *addr;
+	u32 val;
+	u32 ports;
 	int len = 0;
 	int bufLen = PAGE_SIZE;
 	struct hqa_test_cmd *hqa;
@@ -484,10 +466,20 @@
 				"\t%s: %s\n", hqa->name, hqa->discription);
 	}
 
+	ports = mtk->num_u3_ports + mtk->num_u2_ports;
+	for (i = mtk->num_u3_ports + 1; i <= ports; i++) {
+		addr = &xhci->op_regs->port_power_base +
+			NUM_PORT_REGS * ((i - 1) & 0xff);
+		val = readl(addr);
+		len += snprintf(buf+len, bufLen-len,
+			"USB20 Port%i PORTMSC[31,28] 4b'0000: 0x%08X\n",
+			i, val);
+	}
+
 	return len;
 }
 
-static ssize_t mu3h_hqa_store(struct device *dev,
+static ssize_t hqa_store(struct device *dev,
 	struct device_attribute *attr, const char *buf, size_t count)
 {
 	struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev);
@@ -504,320 +496,102 @@
 	return count;
 }
 
-static DEVICE_ATTR(hqa, 0664, mu3h_hqa_show, mu3h_hqa_store);
-
-#define REGS_LIMIT_XHCI 0x1000
-#define REGS_LIMIT_MU3D 0x3000
-#define REGS_LIMIT_IPPC 0x100
-#define REGS_LIMIT_PHYS 0x4000
+static DEVICE_ATTR_RW(hqa);
 
-#define REGS_XHCI_OFFSET 0x0000
-#define REGS_MU3D_OFFSET 0x1000
-#define REGS_IPPC_OFFSET 0x3e00
-#define REGS_PHY_OFFSET  0xA20000
-
-static ssize_t ssusb_reg_show(struct device *dev,
+static ssize_t usb3hqa_show(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
-	int ret = -EINVAL;
-
-	ret = sprintf(buf, "SSUSB register operation interface help info.\n"
-		"  rx - read xhci reg: offset [len]\n"
-		"  rm - read mu3d reg: offset [len]\n"
-		"  ri - read ippc reg: offset [len]\n"
-		"  rp - read phy reg: offset [len]\n"
-		"  wx - write xhci reg: offset value\n"
-		"  wm - write mu3d reg: offset value\n"
-		"  wi - write ippc reg: offset value\n"
-		"  wp - write phy reg: offset value\n"
-		"  sx - set xhci mac reg bits: offset bit_start mask value\n"
-		"  sm - set mu3d mac reg bits: offset bit_start mask value\n"
-		"  si - set ippc reg bits: offset bit_start mask value\n"
-		"  sp - set phy reg bits: offset bit_start mask value\n"
-		"  px - print xhci mac reg bits: offset bit_start mask\n"
-		"  pm - print mu3d mac reg bits: offset bit_start mask\n"
-		"  pi - print ippc reg bits: offset bit_start mask\n"
-		"  pp - print phy reg bits: offset bit_start mask\n"
-		"  NOTE: numbers should be HEX, except bit_star(DEC)\n");
-
-	return ret;
-}
-
-/* base address: return value; limit is put into @limit */
-static void __iomem *get_reg_base_limit(struct xhci_hcd_mtk *mtk,
-		const char *buf, u32 *limit)
-{
-	struct usb_hcd *hcd = mtk->hcd;
-	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-	void __iomem *base;
-	u32 len = 0;
-
-	switch (buf[1]) {
-	case 'x':
-		base = mtk->ip_base + REGS_XHCI_OFFSET;
-		len = REGS_LIMIT_XHCI;
-		xhci_info(xhci, "xhci's reg:\n");
-		break;
-	case 'm':
-		base = mtk->ip_base + REGS_MU3D_OFFSET;
-		len = REGS_LIMIT_MU3D;
-		xhci_info(xhci, "mu3d's reg:\n");
-		break;
-	case 'i':
-		base = mtk->ip_base + REGS_IPPC_OFFSET;
-		len = REGS_LIMIT_IPPC;
-		xhci_info(xhci, "ippc's reg:\n");
-		break;
-	case 'p':
-		base = mtk->ip_base + REGS_PHY_OFFSET;
-		len = REGS_LIMIT_PHYS;
-		xhci_info(xhci, "phy's reg:\n");
-		break;
-	default:
-		base = NULL;
-	}
-
-	*limit = len;
-
-	return base;
-}
-
-static void ssusb_write_reg(struct xhci_hcd_mtk *mtk, const char *buf)
-{
-	struct usb_hcd *hcd = mtk->hcd;
-	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-	void __iomem *base;
-	u32 offset = 0;
-	u32 value = 0;
-	u32 old_val = 0;
-	u32 limit = 0;
-	u32 param;
-
-	param = sscanf(buf, "%*s 0x%x 0x%x", &offset, &value);
-	xhci_info(xhci, "params-%d (offset: %#x, value: %#x)\n",
-				param, offset, value);
-
-	base = get_reg_base_limit(mtk, buf, &limit);
-	if (!base || (param != 2)) {
-		xhci_err(xhci, "params are invalid!\n");
-		return;
-	}
-
-	offset &= ~0x3;  /* 4-bytes align */
-	if (offset >= limit) {
-		xhci_err(xhci, "reg's offset overrun!\n");
-		return;
-	}
-	old_val = readl(base + offset);
-	writel(value, base + offset);
-	xhci_info(xhci, "0x%8.8x : 0x%8.8x --> 0x%8.8x\n", offset, old_val,
-		readl(base + offset));
-}
-
-static void read_single_reg(struct xhci_hcd_mtk *mtk,
-			void __iomem *base, u32 offset, u32 limit)
-{
-	struct usb_hcd *hcd = mtk->hcd;
-	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-	u32 value;
-
-	offset &= ~0x3;  /* 4-bytes align */
-	if (offset >= limit) {
-		xhci_err(xhci, "reg's offset overrun!\n");
-		return;
-	}
-	value = readl(base + offset);
-	xhci_err(xhci, "0x%8.8x : 0x%8.8x\n", offset, value);
-}
-
-static void read_multi_regs(struct xhci_hcd_mtk *mtk,
-	void __iomem *base, u32 offset, u32 len, u32 limit)
-{
-	struct usb_hcd *hcd = mtk->hcd;
-	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-	int i;
-
-	/* at least 4 ints */
-	offset &= ~0xF;
-	len = (len + 0x3) & ~0x3;
-
-	if (offset + len > limit) {
-		xhci_err(xhci, "reg's offset overrun!\n");
-		return;
-	}
-
-	len >>= 2;
-	xhci_info(xhci, "read regs [%#x, %#x)\n", offset, offset + (len << 4));
-	for (i = 0; i < len; i++) {
-		xhci_err(xhci, "0x%8.8x : 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n",
-			offset, readl(base + offset),
-			readl(base + offset + 0x4),
-			readl(base + offset + 0x8),
-			readl(base + offset + 0xc));
-		offset += 0x10;
-	}
-}
-
-static void ssusb_read_regs(struct xhci_hcd_mtk *mtk, const char *buf)
-{
-	struct usb_hcd *hcd = mtk->hcd;
-	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-	void __iomem *base;
-	u32 offset = 0;
-	u32 len = 0;
-	u32 limit = 0;
-	u32 param;
-
-	param = sscanf(buf, "%*s 0x%x 0x%x", &offset, &len);
-	xhci_info(xhci, "params-%d (offset: %#x, len: %#x)\n",
-				param, offset, len);
-
-	base = get_reg_base_limit(mtk, buf, &limit);
-	if (!base || !param) {
-		xhci_err(xhci, "params are invalid!\n");
-		return;
-	}
-
-	if (param == 1)
-		read_single_reg(mtk, base, offset, limit);
-	else
-		read_multi_regs(mtk, base, offset, len, limit);
-}
-
-static void ssusb_set_reg_bits(struct xhci_hcd_mtk *mtk, const char *buf)
-{
-	struct usb_hcd *hcd = mtk->hcd;
-	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-	void __iomem *base;
-	u32 offset = 0;
-	u32 bit_start = 0;
-	u32 mask = 0;
-	u32 value = 0;
-	u32 old_val = 0;
-	u32 new_val = 0;
-	u32 limit = 0;
-	u32 param;
-
-	param = sscanf(buf, "%*s 0x%x %d 0x%x 0x%x",
-			&offset, &bit_start, &mask, &value);
-	xhci_info(xhci, "params-%d (offset:%#x,bit_start:%d,mask:%#x,value:%#x)\n",
-		param, offset, bit_start, mask, value);
-
-	base = get_reg_base_limit(mtk, buf, &limit);
-	if (!base || (param != 4) || (bit_start > 32)) {
-		xhci_err(xhci, "params are invalid!\n");
-		return;
-	}
-
-	offset &= ~0x3;  /* 4-bytes align */
-	if (offset >= limit) {
-		xhci_err(xhci, "reg's offset overrun!\n");
-		return;
-	}
-	old_val = readl(base + offset);
-	new_val = old_val;
-	new_val &= ~(mask << bit_start);
-	new_val |= (value << bit_start);
-	writel(new_val, base + offset);
-	xhci_info(xhci, "0x%8.8x : 0x%8.8x --> 0x%8.8x\n", offset, old_val,
-		readl(base + offset));
-}
-
-
-static void ssusb_print_reg_bits(struct xhci_hcd_mtk *mtk, const char *buf)
-{
+	struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev);
 	struct usb_hcd *hcd = mtk->hcd;
 	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-	void __iomem *base;
-	u32 offset = 0;
-	u32 bit_start = 0;
-	u32 mask = 0;
-	u32 old_val = 0;
-	u32 new_val = 0;
-	u32 limit = 0;
-	u32 param;
+	ssize_t cnt = 0;
+	u32 __iomem *addr;
+	u32 val;
+	u32 i;
+	int ports;
 
-	param = sscanf(buf, "%*s 0x%x %d 0x%x", &offset, &bit_start, &mask);
-	xhci_info(xhci, "params-%d (offset: %#x, bit_start: %d, mask: %#x)\n",
-		param, offset, bit_start, mask);
+	cnt += sprintf(buf + cnt, "usb3hqa usage:\n");
+	cnt += sprintf(buf + cnt, "	echo u3port >usb3hqa\n");
 
-	base = get_reg_base_limit(mtk, buf, &limit);
-	if (!base || (param != 3) || (bit_start > 32)) {
-		xhci_err(xhci, "params are invalid!\n");
-		return;
+	ports = mtk->num_u3_ports + mtk->num_u2_ports;
+	for (i = 1; i <= ports; i++) {
+		addr = &xhci->op_regs->port_status_base +
+			NUM_PORT_REGS * ((i - 1) & 0xff);
+		val = readl(addr);
+		if (i < mtk->num_u3_ports)
+			cnt += sprintf(buf + cnt,
+				"USB30 Port%i: 0x%08X\n", i, val);
+		else 
+			cnt += sprintf(buf + cnt,
+				"USB20 Port%i: 0x%08X\n", i, val);
 	}
 
-	offset &= ~0x3;  /* 4-bytes align */
-	if (offset >= limit) {
-		xhci_err(xhci, "reg's offset overrun!\n");
-		return;
+	if (mtk->hqa_pos) {
+		cnt += sprintf(buf + cnt, "%s", mtk->hqa_buf);
+		mtk->hqa_pos = 0;
 	}
 
-	old_val = readl(base + offset);
-	new_val = old_val;
-	new_val >>= bit_start;
-	new_val &= mask;
-	xhci_info(xhci, "0x%8.8x : 0x%8.8x (0x%x)\n", offset, old_val, new_val);
+	return cnt;
 }
 
-
 static ssize_t
-ssusb_reg_store(struct device *dev, struct device_attribute *attr,
+usb3hqa_store(struct device *dev, struct device_attribute *attr,
 			const char *buf, size_t n)
 {
 	struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev);
 	struct usb_hcd *hcd = mtk->hcd;
 	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+	u32 __iomem *addr;
+	u32 val;
+	int port;
+	int words;
 
-	xhci_info(xhci, " cmd: %s\n", buf);
+	mtk->hqa_pos = 0;
+	memset(mtk->hqa_buf, 0, mtk->hqa_size);
 
-	switch (buf[0]) {
-	case 'w':
-		ssusb_write_reg(mtk, buf);
-		break;
-	case 'r':
-		ssusb_read_regs(mtk, buf);
-		break;
-	case 's':
-		ssusb_set_reg_bits(mtk, buf);
-		break;
-	case 'p':
-		ssusb_print_reg_bits(mtk, buf);
-		break;
-	default:
-		xhci_err(xhci, "No such cmd\n");
+	hqa_info(mtk, "usb3hqa: %s\n", buf);
+
+	words = sscanf(buf, "%d", &port);
+	if ((words != 1) ||
+	    (port < 1 || port > mtk->num_u3_ports)) {
+		hqa_info(mtk, "usb3hqa: param number:%i, port:%i failure\n",
+			words, port);
+		return -EINVAL;
 	}
 
+	addr = &xhci->op_regs->port_status_base +
+		NUM_PORT_REGS * ((port - 1) & 0xff);
+	val  = readl(addr);
+	val &= ~(PORT_PLS_MASK);
+	val |= (PORT_LINK_STROBE | XDEV_COMP_MODE);
+	writel(val, addr);
+	hqa_info(mtk, "usb3hqa: port%i: 0x%08X but 0x%08X\n",
+		port, val, readl(addr));
+
 	return n;
 }
-
-static DEVICE_ATTR(reg, 0664, ssusb_reg_show, ssusb_reg_store);
+static DEVICE_ATTR_RW(usb3hqa);
 
 static struct device_attribute *mu3h_hqa_attr_list[] = {
 	&dev_attr_hqa,
-	&dev_attr_reg,
+	&dev_attr_usb3hqa,
+#include "unusual-statement.h"
 };
 
-static void ssusb_remap_ip_regs(struct device *dev)
-{
-	struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev);
-	struct usb_hcd *hcd = mtk->hcd;
-	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-
-	mtk->ip_base = ioremap(hcd->rsrc_start, 0xb00000);//0xa30000
-	if (!mtk->ip_base)
-		xhci_err(xhci, "could not ioremap regs\n");
-}
-
-int mu3h_hqa_create_attr(struct device *dev)
+int hqa_create_attr(struct device *dev)
 {
 	int idx, err = 0;
 	int num = ARRAY_SIZE(mu3h_hqa_attr_list);
+	struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev);
 
-	if (dev == NULL)
+	if (dev == NULL || mtk == NULL)
 		return -EINVAL;
 
-	ssusb_remap_ip_regs(dev);
+	mtk->hqa_size = HQA_PREFIX_SIZE;
+	mtk->hqa_pos  = 0;
+	mtk->hqa_buf = kzalloc(mtk->hqa_size, GFP_KERNEL);
+	if (!mtk->hqa_buf)
+		return -ENOMEM;
 
 	for (idx = 0; idx < num; idx++) {
 		err = device_create_file(dev, mu3h_hqa_attr_list[idx]);
@@ -828,11 +602,16 @@
 	return err;
 }
 
-void mu3h_hqa_remove_attr(struct device *dev)
+void hqa_remove_attr(struct device *dev)
 {
 	int idx;
 	int num = ARRAY_SIZE(mu3h_hqa_attr_list);
+	struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev);
 
 	for (idx = 0; idx < num; idx++)
 		device_remove_file(dev, mu3h_hqa_attr_list[idx]);
+
+	kfree(mtk->hqa_buf);
+	mtk->hqa_size = 0;
+	mtk->hqa_pos  = 0;
 }
diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-test.h b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-test.h
index 41199be..83e1542 100644
--- a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-test.h
+++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-test.h
@@ -1,24 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 /*
- * Copyright (c) 2015 MediaTek Inc.
- * Author:
- *  Shaocheng.Wang <shaocheng.wang@mediatek.com>
- *  Chunfeng.Yun <chunfeng.yun@mediatek.com>
+ * xhci-mtk-unusuallib.h -- xhci toolkit header file
  *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
+ * Copyright (C) 2021 Mediatek Inc - http://www.mediatek.com
  *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
+ *  Author: Zhanyong Wang <zhanyong.wang@mediatek.com>
+ *          Shaocheng.Wang <shaocheng.wang@mediatek.com>
+ *          Chunfeng.Yun <chunfeng.yun@mediatek.com>
  */
 
 #ifndef __XHCI_MTK_TEST_H
 #define __XHCI_MTK_TEST_H
 
-int mu3h_hqa_create_attr(struct device *dev);
-void mu3h_hqa_remove_attr(struct device *dev);
-
+#ifdef CONFIG_USB_XHCI_MTK_DEBUGFS
+int hqa_create_attr(struct device *dev);
+void hqa_remove_attr(struct device *dev);
+#else
+static inline int hqa_create_attr(struct device *dev)
+{
+	return 0;
+}
+static inline void hqa_remove_attr(struct device *dev)
+{
+}
+#endif
 #endif /* __XHCI_MTK_TEST_H */
diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-unusual.c b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-unusual.c
new file mode 100644
index 0000000..14d7d0b
--- /dev/null
+++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-unusual.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * xHCI host controller toolkit driver
+ *
+ * Copyright (C) 2021  MediaTek Inc.
+ *
+ *  Author: Zhanyong Wang <zhanyong.wang@mediatek.com>
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <dt-bindings/phy/phy.h>
+#include "xhci-mtk.h"
+#include "xhci-mtk-test.h"
+#include "xhci-mtk-unusual.h"
+
+
+u32 binary_write_width1(u32 __iomem *addr, u32 shift, const char *buf)
+{
+	u32 val = 0;
+
+	if (!strncmp(buf, STRNG_0_WIDTH_1, BIT_WIDTH_1))
+		val = 0;
+	else if (!strncmp(buf, STRNG_1_WIDTH_1, BIT_WIDTH_1))
+		val = 1;
+	else
+		val = 0xFFFFFFFF;
+
+	if (val <= 1)
+		val = usb20hqa_write(addr, shift, MSK_WIDTH_1, val);
+
+	return val;
+}
+
+u32 binary_write_width3(u32 __iomem *addr, u32 shift, const char *buf)
+{
+	u32 val = 0;
+
+	if (!strncmp(buf, STRNG_0_WIDTH_3, BIT_WIDTH_3))
+		val = 0;
+	else if (!strncmp(buf, STRNG_1_WIDTH_3, BIT_WIDTH_3))
+		val = 1;
+	else if (!strncmp(buf, STRNG_2_WIDTH_3, BIT_WIDTH_3))
+		val = 2;
+	else if (!strncmp(buf, STRNG_3_WIDTH_3, BIT_WIDTH_3))
+		val = 3;
+	else if (!strncmp(buf, STRNG_4_WIDTH_3, BIT_WIDTH_3))
+		val = 4;
+	else if (!strncmp(buf, STRNG_5_WIDTH_3, BIT_WIDTH_3))
+		val = 5;
+	else if (!strncmp(buf, STRNG_6_WIDTH_3, BIT_WIDTH_3))
+		val = 6;
+	else if (!strncmp(buf, STRNG_7_WIDTH_3, BIT_WIDTH_3))
+		val = 7;
+	else
+		val = 0xFFFFFFFF;
+
+	if (val <= 7)
+		val = usb20hqa_write(addr, shift, MSK_WIDTH_3, val);
+
+	return val;
+}
+
+u32 binary_write_width4(u32 __iomem *addr, u32 shift, const char *buf)
+{
+	u32 val = 0;
+
+	if (!strncmp(buf, STRNG_0_WIDTH_4, BIT_WIDTH_4))
+		val = 0;
+	else if (!strncmp(buf, STRNG_1_WIDTH_4, BIT_WIDTH_4))
+		val = 1;
+	else if (!strncmp(buf, STRNG_2_WIDTH_4, BIT_WIDTH_4))
+		val = 2;
+	else if (!strncmp(buf, STRNG_3_WIDTH_4, BIT_WIDTH_4))
+		val = 3;
+	else if (!strncmp(buf, STRNG_4_WIDTH_4, BIT_WIDTH_4))
+		val = 4;
+	else if (!strncmp(buf, STRNG_5_WIDTH_4, BIT_WIDTH_4))
+		val = 5;
+	else if (!strncmp(buf, STRNG_6_WIDTH_4, BIT_WIDTH_4))
+		val = 6;
+	else if (!strncmp(buf, STRNG_7_WIDTH_4, BIT_WIDTH_4))
+		val = 7;
+	else if (!strncmp(buf, STRNG_8_WIDTH_4, BIT_WIDTH_4))
+		val = 8;
+	else if (!strncmp(buf, STRNG_9_WIDTH_4, BIT_WIDTH_4))
+		val = 9;
+	else if (!strncmp(buf, STRNG_A_WIDTH_4, BIT_WIDTH_4))
+		val = 10;
+	else if (!strncmp(buf, STRNG_B_WIDTH_4, BIT_WIDTH_4))
+		val = 11;
+	else if (!strncmp(buf, STRNG_C_WIDTH_4, BIT_WIDTH_4))
+		val = 12;
+	else if (!strncmp(buf, STRNG_D_WIDTH_4, BIT_WIDTH_4))
+		val = 13;
+	else if (!strncmp(buf, STRNG_E_WIDTH_4, BIT_WIDTH_4))
+		val = 14;
+	else if (!strncmp(buf, STRNG_F_WIDTH_4, BIT_WIDTH_4))
+		val = 15;
+	else
+		val = 0xFFFFFFFF;
+
+	if (val <= 15)
+		val = usb20hqa_write(addr, shift, MSK_WIDTH_4, val);
+
+	return val;
+}
+
+u32 bin2str(u32 value, u32 width, char *buffer)
+{
+	int i, temp;
+
+	temp = value;
+	buffer[width] = '\0';
+	for (i = (width - 1); i >= 0; i--) {
+		buffer[i] = '0';
+		if (value % 2)
+			buffer[i] = '1';
+
+		value /= 2;
+	}
+
+	return value;
+}
+
+int query_phy_addr(struct device_node *np, int *start, u32 *addr, u32 *length)
+{
+	int ret = -EPERM;
+	struct of_phandle_args args;
+	struct resource res;
+	int numphys = 0;
+	int index;
+
+	if (start == NULL || addr == NULL || length == NULL)
+		return -EINVAL;
+
+	numphys = of_count_phandle_with_args(np, "phys", "#phy-cells");
+	for ( index = *start; (numphys > 0) && index < numphys; index++) {
+		ret = of_parse_phandle_with_args(np, "phys", "#phy-cells",
+				index, &args);
+		if (ret < 0)
+			break;
+
+		if (args.args[0] == PHY_TYPE_USB2) {
+			ret = of_address_to_resource(args.np, 0, &res);
+			if (ret < 0) {
+				of_node_put(args.np);
+				break;
+			}
+
+			*addr   = res.start;
+			*length = (u32)resource_size(&res);
+			*start  = index;
+			if (!of_device_is_available(args.np))
+				ret = -EACCES;
+
+			of_node_put(args.np);
+			break;
+		}
+	}
+
+	ret = index < numphys ? ret : -EPERM;
+	return ret;
+}
diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-unusual.h b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-unusual.h
new file mode 100644
index 0000000..ddbf4e3
--- /dev/null
+++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-unusual.h
@@ -0,0 +1,195 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * xhci-mtk-unusuallib.h -- xhci toolkit header file
+ *
+ * Copyright (C) 2021 Mediatek Inc - http://www.mediatek.com
+ *
+ * Author: Zhanyong Wang <zhanyong.wang@mediatek.com>
+ */
+
+#ifndef __XHCI_MTK_UNUSUAL_H
+#define __XHCI_MTK_UNUSUAL_H
+
+#define HQA_PREFIX_SIZE		4*1024
+
+#define BIT_WIDTH_1		1
+#define MSK_WIDTH_1		0x1
+#define VAL_MAX_WDITH_1		0x1
+
+#define STRNG_0_WIDTH_1		"0"
+#define STRNG_1_WIDTH_1		"1"
+
+#define BIT_WIDTH_2		2
+#define MSK_WIDTH_2		0x3
+#define VAL_MAX_WDITH_2		0x3
+#define STRNG_0_WIDTH_2		"00"
+#define STRNG_1_WIDTH_2		"01"
+#define STRNG_2_WIDTH_2		"10"
+#define STRNG_3_WIDTH_2		"11"
+
+
+#define BIT_WIDTH_3		3
+#define MSK_WIDTH_3		0x7
+#define VAL_MAX_WDITH_3		0x7
+#define STRNG_0_WIDTH_3		"000"
+#define STRNG_1_WIDTH_3		"001"
+#define STRNG_2_WIDTH_3		"010"
+#define STRNG_3_WIDTH_3		"011"
+#define STRNG_4_WIDTH_3		"100"
+#define STRNG_5_WIDTH_3		"101"
+#define STRNG_6_WIDTH_3		"110"
+#define STRNG_7_WIDTH_3		"111"
+
+#define BIT_WIDTH_4		4
+#define MSK_WIDTH_4		0xf
+#define VAL_MAX_WDITH_4		0xf
+#define STRNG_0_WIDTH_4		"0000"
+#define STRNG_1_WIDTH_4		"0001"
+#define STRNG_2_WIDTH_4		"0010"
+#define STRNG_3_WIDTH_4		"0011"
+#define STRNG_4_WIDTH_4		"0100"
+#define STRNG_5_WIDTH_4		"0101"
+#define STRNG_6_WIDTH_4		"0110"
+#define STRNG_7_WIDTH_4		"0111"
+#define STRNG_8_WIDTH_4		"1000"
+#define STRNG_9_WIDTH_4		"1001"
+#define STRNG_A_WIDTH_4		"1010"
+#define STRNG_B_WIDTH_4		"1011"
+#define STRNG_C_WIDTH_4		"1100"
+#define STRNG_D_WIDTH_4		"1101"
+#define STRNG_E_WIDTH_4		"1110"
+#define STRNG_F_WIDTH_4		"1111"
+
+/* specific */
+#define NAME_RG_USB20_INTR_EN		"RG_USB20_INTR_EN"
+#define USB20_PHY_USBPHYACR0		0x00
+#define SHFT_RG_USB20_INTR_EN		5
+#define BV_RG_USB20_INTR_EN		BIT(5)
+
+#define NAME_RG_USB20_VRT_VREF_SEL	"RG_USB20_VRT_VREF_SEL"
+#define USB20_PHY_USBPHYACR1		0x04
+#define SHFT_RG_USB20_VRT_VREF_SEL	12
+#define BV_RG_USB20_VRT_VREF_SEL	GENMASK(14, 12)
+
+#define NAME_RG_USB20_TERM_VREF_SEL	"RG_USB20_TERM_VREF_SEL"
+#define SHFT_RG_USB20_TERM_VREF_SEL	8
+#define BV_RG_USB20_TERM_VREF_SEL       GENMASK(10,  8)
+
+#define NAME_RG_USB20_HSTX_SRCTRL	"RG_USB20_HSTX_SRCTRL"
+#define USB20_PHY_USBPHYACR5		0x14
+#define SHFT_RG_USB20_HSTX_SRCTRL	12
+#define BV_RG_USB20_HSTX_SRCTRL		GENMASK(14, 12)
+
+#define NAME_RG_USB20_DISCTH		"RG_USB20_DISCTH"
+#define USB20_PHY_USBPHYACR6		0x18
+#define SHFT_RG_USB20_DISCTH		4
+#define BV_RG_USB20_DISCTH		GENMASK(8, 4)
+
+#define NAME_RG_CHGDT_EN		"RG_CHGDT_EN"
+#define USB20_PHY_U2PHYBC12C		0x80
+#define SHFT_RG_CHGDT_EN		0
+#define BV_RG_CHGDT_EN			BIT(0)
+
+#define ECHO_HQA(reg, _bd, _bw)  do {\
+	val = usb20hqa_read(addr + (reg), \
+		 SHFT_##_bd, \
+		 BV_##_bd); \
+	val = bin2str(val, BIT_WIDTH_##_bw, str); \
+	cnt += sprintf(buf + cnt, "	%-22s = %ib%s\n", \
+			NAME_##_bd, _bw, str); } while(0)
+
+
+#ifdef CONFIG_USB_XHCI_MTK_DEBUGFS
+static inline u32 usb20hqa_write(u32 __iomem *addr,
+				u32 shift, u32 mask, u32 value)
+{
+	u32 val;
+
+	val  = readl(addr);
+	val &= ~((mask) << shift);
+	val |=  (((value) & (mask)) << shift);
+	writel(val, addr);
+
+	return val;
+}
+static inline u32 usb20hqa_read(u32 __iomem *addr, u32 shift, u32 mask)
+{
+	u32 val;
+
+	val   = readl(addr);
+	val  &= mask;
+	val >>=  shift;
+
+	return val;
+}
+
+u32 binary_write_width1(u32 __iomem *addr,
+				u32 shift, const char *buf);
+u32 binary_write_width3(u32 __iomem *addr,
+				u32 shift, const char *buf);
+u32 binary_write_width4(u32 __iomem *addr,
+				u32 shift, const char *buf);
+u32 bin2str(u32 value, u32 width, char *buffer);
+int query_phy_addr(struct device_node *np, int *start,
+				u32 *addr, u32 *length);
+static inline int remaining(struct xhci_hcd_mtk *mtk)
+{
+	u32 surplus = 0;
+	if (mtk && mtk->hqa_pos < mtk->hqa_size)
+		surplus = mtk->hqa_size - mtk->hqa_pos;
+
+	return surplus;
+}
+
+#define hqa_info(mtk, fmt, args...)  \
+	(mtk)->hqa_pos += snprintf((mtk)->hqa_buf + (mtk)->hqa_pos, \
+		remaining(mtk), fmt, ## args)
+
+#define DEVICE_ATTR_DECLARED(_name) \
+		extern struct device_attribute dev_attr_##_name;
+#define UNUSUAL_DEVICE_ATTR(_name)  &dev_attr_##_name
+#else
+static inline u32 usb20hqa_write(u32 __iomem *addr,
+					u32 shift, u32 mask, u32 value)
+{
+	return 0;
+}
+static inline u32 usb20hqa_read(u32 __iomem *addr, u32 shift, u32 mask)
+{
+	return 0;
+}
+static inline u32 binary_write_width1(u32 __iomem *addr,
+					u32 shift, const char *buf)
+{
+	return 0;
+};
+static inline u32 binary_write_width3(u32 __iomem *addr,
+					u32 shift, const char *buf)
+{
+	return 0;
+};
+static inline u32 binary_write_width4(u32 __iomem *addr,
+					u32 shift, const char *buf)
+{
+	return 0;
+};
+static inline u32 bin2str(u32 value, u32 width, char *buffer)
+{
+	return 0;
+};
+static inline int query_phy_addr(struct device_node *np, int *start,
+					u32 *addr, u32 *length)
+{
+	return -EPERM;
+}
+static inline int remaining(int wrote)
+{
+	return 0;
+}
+#define hqa_info(mtk, fmt, args...)
+#define DEVICE_ATTR_DECLARED(...)
+#endif
+
+#include "unusual-declaration.h"
+
+#endif /* __XHCI_MTK_UNUSUAL_H */
diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-vrt-vref.c b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-vrt-vref.c
new file mode 100644
index 0000000..ec0ef75
--- /dev/null
+++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-vrt-vref.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * xHCI host controller toolkit driver for vrt vref
+ *
+ * Copyright (C) 2021  MediaTek Inc.
+ *
+ *  Author: Zhanyong Wang <zhanyong.wang@mediatek.com>
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include "xhci-mtk.h"
+#include "xhci-mtk-test.h"
+#include "xhci-mtk-unusual.h"
+
+static ssize_t RG_USB20_VRT_VREF_SEL_show(struct device *dev,
+                        struct device_attribute *attr, char *buf)
+{
+	struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev);
+	struct usb_hcd *hcd = mtk->hcd;
+	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+	ssize_t cnt = 0;
+	void __iomem *addr;
+	u32 val;
+	u32 i;
+	int ports;
+	char str[32];
+	int index = 0;
+	u32 io, length;
+	int ret;
+
+	ports = mtk->num_u3_ports + mtk->num_u2_ports;
+	cnt += sprintf(buf + cnt, " RG_USB20_VRT_VREF_SEL usage:\n");
+	cnt += sprintf(buf + cnt,
+		"   echo u2p index 3b011 > RG_USB20_VRT_VREF_SEL\n");
+	if (mtk->num_u3_ports + 1 != ports)
+		cnt += sprintf(buf + cnt, "	parameter: u2p: %i ~ %i\n",
+					mtk->num_u3_ports + 1, ports);
+	else
+		cnt += sprintf(buf + cnt, "	parameter: u2p: %i\n",
+					mtk->num_u3_ports + 1);
+
+	if (mtk->num_u2_ports > 1)
+		cnt += sprintf(buf + cnt, "	parameter: index: 0 ~ %i\n",
+			       mtk->num_u2_ports);
+	else
+		cnt += sprintf(buf + cnt, "	parameter: index: 0\n");
+
+	cnt += sprintf(buf + cnt,
+			 " e.g.: echo 2 0 3b101 > RG_USB20_VRT_VREF_SEL\n");
+	cnt += sprintf(buf + cnt,
+		"  port2 binding phy 0, tune 3b'010 as VRT_VREF value\n");
+
+	cnt += sprintf(buf + cnt,
+			"\n=========current HQA setting check=========\n");
+	for (i = 1; i <= ports; i++) {
+		addr = &xhci->op_regs->port_status_base +
+			NUM_PORT_REGS * ((i - 1) & 0xff);
+		val = readl(addr);
+		if (i <= mtk->num_u3_ports) {
+			cnt += sprintf(buf + cnt,
+				       "USB30 Port%i: 0x%08X\n", i, val);
+		} else {
+			cnt += sprintf(buf + cnt,
+				       "USB20 Port%i: 0x%08X\n", i, val);
+
+			ret = query_phy_addr(dev->of_node,
+						 &index, &io, &length);
+			if (ret && ret != -EACCES) {
+				if (ret == -EPERM)
+					cnt += sprintf(buf + cnt,
+					"USB20 Port%i (Phy%i: absent)\n",
+					i, index);
+				else
+					cnt += sprintf(buf + cnt,
+					"USB20 Port%i (Phy%i) failure %i\n",
+						 i, index, ret);
+				continue;
+			}
+
+			cnt += sprintf(buf + cnt,
+				"USB20 Port%i (Phy%i:%sable): 0x%08X 0x%08X\n",
+				i, index, ret ? " dis" : " en", io, length);
+
+			addr   = ioremap_nocache(io, length);
+			addr  += (length != 0x100) ? 0x300 : 0;
+
+			HQA_INFORMACTION_COLLECTS();
+
+			iounmap(addr);
+			index ++;
+		}
+	}
+
+	if (mtk->hqa_pos) {
+		cnt += sprintf(buf + cnt, "%s", mtk->hqa_buf);          
+		mtk->hqa_pos = 0;
+	}
+
+	return cnt;
+}
+
+
+static ssize_t RG_USB20_VRT_VREF_SEL_store(struct device *dev,
+                        struct device_attribute *attr,
+                        const char *buf, size_t n)
+{
+	u32 val;
+	u32 io;
+	u32 length;
+	int ports;
+	int words;
+	int port;
+	int index;
+	int ret;
+	char *str = NULL;
+	void __iomem *addr;
+	struct xhci_hcd_mtk *mtk  = dev_get_drvdata(dev);
+	struct device_node  *node = dev->of_node;
+
+	ports = mtk->num_u3_ports + mtk->num_u2_ports;
+	mtk->hqa_pos = 0;
+
+	memset(mtk->hqa_buf, 0, mtk->hqa_size);
+
+	str = kzalloc(n, GFP_ATOMIC);
+
+	hqa_info(mtk, "RG_USB20_VRT_VREF_SEL(%lu): %s\n", n, buf);
+
+	words = sscanf(buf, "%i %i 3b%3[0,1]", &port, &index, str);
+	if ((words != 3) ||
+	    (port < mtk->num_u3_ports || port > ports)) {
+		hqa_info(mtk, "Check params(%i):\" %i %i %s\", Please!\n",
+			words, port, index, str);
+
+		ret = -EINVAL;
+		goto error;
+	}
+
+	hqa_info(mtk, " params: %i %i %s\n",
+		port, index, str);
+
+	ret = query_phy_addr(node, &index, &io, &length);
+	if (ret && ret != -EACCES) 
+		goto error;
+
+	io += (length != 0x100) ? 0x300 : 0;
+	io += USB20_PHY_USBPHYACR1;
+
+	addr = ioremap_nocache(io, 4);
+	val = binary_write_width3(addr, SHFT_RG_USB20_VRT_VREF_SEL, str);
+	hqa_info(mtk, "Port%i(Phy%i)[0x%08X]: 0x%08X but 0x%08X\n",
+		port, index, io, val, readl(addr));
+
+	iounmap(addr);
+	ret = n;
+      
+error:
+	kfree(str);
+	return ret;
+}
+DEVICE_ATTR_RW(RG_USB20_VRT_VREF_SEL);