[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