[][kernel][mt7988][usb]Add USB 2.0 Phy preemphasis parameter support]

[Description]
Add USB 2.0 phy preemphasis parameter support

[Release-log]
N/A

Change-Id: I0543ec4c840706bafaf0727f6409f40cff5b8056
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/6378563
diff --git a/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-preemphasic.c b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-preemphasic.c
new file mode 100644
index 0000000..5041dbc
--- /dev/null
+++ b/target/linux/mediatek/files-5.4/drivers/usb/host/xhci-mtk-preemphasic.c
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * xHCI host controller toolkit driver for pre-emphasic
+ *
+ * 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_PHY_REV_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);
+	struct device_node  *node = dev->of_node;
+	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_PHY_REV usage:\n");
+	cnt += sprintf(buf + cnt,
+                "   echo u2p index 2b00 > RG_USB20_PHY_REV\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 2b10 > RG_USB20_PHY_REV\n");
+	cnt += sprintf(buf + cnt,
+		"  port2 binding phy 0, enable 2b'10 as RG_USB20_PHY_REV\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(node,
+					 &index, &io, &length, PHY_TYPE_USB2);
+			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_PHY_REV_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_PHY_REV(%lu): %s\n", n, buf);
+
+	words = sscanf(buf, "%i %i 2b%2[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, PHY_TYPE_USB2);
+	if (ret && ret != -EACCES)
+		goto error;
+
+	io += (length != 0x100) ? 0x300 : 0;
+	io += USB20_PHY_USBPHYACR6;
+
+	addr = ioremap_nocache(io, 4);
+	val = binary_write_width2(addr, SHFT_RG_USB20_PHY_REV, 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_PHY_REV);