[PATCH 05/10] usb: phy: rtk: add the new realtek usb2 phy and usb3 phy drivers

stanley_chang stanley_chang at realtek.com
Thu Sep 17 04:38:30 EDT 2020


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




More information about the linux-realtek-soc mailing list