* [PATCH v2 1/3] usb: phy: add usb phy notify port status API
@ 2023-05-25 2:26 Stanley Chang
2023-05-25 2:26 ` [PATCH v2 2/3] phy: realtek: usb: Add driver for the Realtek SoC USB 2.0/3.0 PHY Stanley Chang
` (2 more replies)
0 siblings, 3 replies; 21+ messages in thread
From: Stanley Chang @ 2023-05-25 2:26 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Stanley Chang, Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Alan Stern, Michael Grzeschik,
Bagas Sanjaya, Flavio Suligoi, Matthias Kaehlcke, Mathias Nyman,
Ray Chi, linux-phy, devicetree, linux-kernel, linux-usb
In Realtek SoC, the parameter of usb phy is designed to can dynamic
tuning base on port status. Therefore, add a notify callback of phy
driver when usb port status change.
Signed-off-by: Stanley Chang <stanley_chang@realtek.com>
---
v1 to v2 change:
No change
---
drivers/usb/core/hub.c | 13 +++++++++++++
include/linux/usb/phy.h | 14 ++++++++++++++
2 files changed, 27 insertions(+)
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 97a0f8faea6e..b4fbbeae1927 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -614,6 +614,19 @@ static int hub_ext_port_status(struct usb_hub *hub, int port1, int type,
ret = 0;
}
mutex_unlock(&hub->status_mutex);
+
+ if (!ret) {
+ struct usb_device *hdev = hub->hdev;
+
+ if (hdev && !hdev->parent) {
+ struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
+
+ if (hcd->usb_phy)
+ usb_phy_notify_port_status(hcd->usb_phy,
+ port1 - 1, *status, *change);
+ }
+ }
+
return ret;
}
diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h
index e4de6bc1f69b..53bf3540098f 100644
--- a/include/linux/usb/phy.h
+++ b/include/linux/usb/phy.h
@@ -144,6 +144,10 @@ struct usb_phy {
*/
int (*set_wakeup)(struct usb_phy *x, bool enabled);
+ /* notify phy port status change */
+ int (*notify_port_status)(struct usb_phy *x,
+ int port, u16 portstatus, u16 portchange);
+
/* notify phy connect status change */
int (*notify_connect)(struct usb_phy *x,
enum usb_device_speed speed);
@@ -316,6 +320,16 @@ usb_phy_set_wakeup(struct usb_phy *x, bool enabled)
return 0;
}
+static inline int
+usb_phy_notify_port_status(struct usb_phy *x, int port, u16 portstatus,
+ u16 portchange)
+{
+ if (x && x->notify_port_status)
+ return x->notify_port_status(x, port, portstatus, portchange);
+ else
+ return 0;
+}
+
static inline int
usb_phy_notify_connect(struct usb_phy *x, enum usb_device_speed speed)
{
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 2/3] phy: realtek: usb: Add driver for the Realtek SoC USB 2.0/3.0 PHY
2023-05-25 2:26 [PATCH v2 1/3] usb: phy: add usb phy notify port status API Stanley Chang
@ 2023-05-25 2:26 ` Stanley Chang
2023-05-29 14:25 ` Greg Kroah-Hartman
2023-05-25 2:26 ` [PATCH v2 3/3] dt-bindings: phy: realtek: Add the doc about " Stanley Chang
2023-05-29 14:27 ` [PATCH v2 1/3] usb: phy: add usb phy notify port status API Greg Kroah-Hartman
2 siblings, 1 reply; 21+ messages in thread
From: Stanley Chang @ 2023-05-25 2:26 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Stanley Chang, kernel test robot, Vinod Koul,
Kishon Vijay Abraham I, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Alan Stern, Bagas Sanjaya, Ray Chi,
Douglas Anderson, Mathias Nyman, Michael Grzeschik,
Flavio Suligoi, linux-phy, devicetree, linux-kernel, linux-usb
Realtek DHC (digital home center) RTD SoCs support DWC3 XHCI USB 2.0/3.0
controller. Added two drivers to drive the USB 2.0/3.0 PHY transceivers.
For USB 3.0 transceivers, a driver phy-rtk-usb3 is provided.
The driver phy-rtk-usb2 is used to support USB 2.0 transceivers.
Signed-off-by: Stanley Chang <stanley_chang@realtek.com>
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202305201608.MBqXmFKF-lkp@intel.com/
---
v1 to v2 change:
1. Move the drivers to drivers/phy/ for generic phy driver
2. Use the generic phy driver api to initialize phy
3. Use readl/writel to instead phy_read/phy_write directly.
4. Use read_poll_timeout() in function utmi_wait_register.
5. Revised some coding styles.
6. fix the compiler warning for kerenl test robot.
---
drivers/phy/Kconfig | 1 +
drivers/phy/Makefile | 1 +
drivers/phy/realtek/Kconfig | 23 +
drivers/phy/realtek/Makefile | 3 +
drivers/phy/realtek/phy-rtk-usb.h | 82 ++
drivers/phy/realtek/phy-rtk-usb2.c | 2203 ++++++++++++++++++++++++++++
drivers/phy/realtek/phy-rtk-usb3.c | 1180 +++++++++++++++
7 files changed, 3493 insertions(+)
create mode 100644 drivers/phy/realtek/Kconfig
create mode 100644 drivers/phy/realtek/Makefile
create mode 100644 drivers/phy/realtek/phy-rtk-usb.h
create mode 100644 drivers/phy/realtek/phy-rtk-usb2.c
create mode 100644 drivers/phy/realtek/phy-rtk-usb3.c
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index f46e3148d286..6d04a0029c6c 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -86,6 +86,7 @@ source "drivers/phy/motorola/Kconfig"
source "drivers/phy/mscc/Kconfig"
source "drivers/phy/qualcomm/Kconfig"
source "drivers/phy/ralink/Kconfig"
+source "drivers/phy/realtek/Kconfig"
source "drivers/phy/renesas/Kconfig"
source "drivers/phy/rockchip/Kconfig"
source "drivers/phy/samsung/Kconfig"
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 54f312c10a40..ba7c100b14fc 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -26,6 +26,7 @@ obj-y += allwinner/ \
mscc/ \
qualcomm/ \
ralink/ \
+ realtek/ \
renesas/ \
rockchip/ \
samsung/ \
diff --git a/drivers/phy/realtek/Kconfig b/drivers/phy/realtek/Kconfig
new file mode 100644
index 000000000000..28ee3d9be568
--- /dev/null
+++ b/drivers/phy/realtek/Kconfig
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Phy drivers for Realtek platforms
+#
+config PHY_RTK_RTD_USB2PHY
+ tristate "Realtek RTD USB2 PHY Transceiver Driver"
+ select GENERIC_PHY
+ select USB_PHY
+ help
+ Enable this to support Realtek SoC USB2 phy transceiver.
+ The DHC (digital home center) RTD series SoCs used the Synopsys
+ DWC3 USB IP. This driver will do the PHY initialization
+ of the parameters.
+
+config PHY_RTK_RTD_USB3PHY
+ tristate "Realtek RTD USB3 PHY Transceiver Driver"
+ select GENERIC_PHY
+ select USB_PHY
+ help
+ Enable this to support Realtek SoC USB3 phy transceiver.
+ The DHC (digital home center) RTD series SoCs used the Synopsys
+ DWC3 USB IP. This driver will do the PHY initialization
+ of the parameters.
diff --git a/drivers/phy/realtek/Makefile b/drivers/phy/realtek/Makefile
new file mode 100644
index 000000000000..ed7b47ff8a26
--- /dev/null
+++ b/drivers/phy/realtek/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PHY_RTK_RTD_USB2PHY) += phy-rtk-usb2.o
+obj-$(CONFIG_PHY_RTK_RTD_USB3PHY) += phy-rtk-usb3.o
diff --git a/drivers/phy/realtek/phy-rtk-usb.h b/drivers/phy/realtek/phy-rtk-usb.h
new file mode 100644
index 000000000000..9578243a8bd7
--- /dev/null
+++ b/drivers/phy/realtek/phy-rtk-usb.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * phy-rtk-usb.h Realtek usb phy header file
+ *
+ * copyright (c) 2023 realtek semiconductor corporation
+ *
+ */
+
+#ifndef __PHY_RTK_USB_H__
+#define __PHY_RTK_USB_H__
+
+struct rtk_usb_phy {
+ struct usb_phy phy;
+ struct device *dev;
+ struct regmap *usb_regs;
+ struct regmap *mac_regs;
+ struct regmap *usb_ctrl_regs;
+
+ int port_index;
+ int phyN;
+ void *reg_addr;
+ void *phy_data;
+
+ struct dentry *debug_dir;
+};
+
+#define PHY_IO_TIMEOUT_MSEC (50)
+#define PHY_IO_TIMEOUT_USEC (50000)
+#define PHY_IO_DELAY_US (100)
+
+static inline int utmi_wait_register(void __iomem *reg, u32 mask, u32 result)
+{
+ int ret;
+ unsigned int val;
+
+ ret = read_poll_timeout(readl, val, ((val & mask) == result),
+ PHY_IO_DELAY_US, PHY_IO_TIMEOUT_USEC, false, reg);
+ if (ret) {
+ pr_err("%s can't program USB phy\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+#define DEFAULT_CHIP_REVISION 0xA00
+#define MAX_CHIP_REVISION 0xC00
+
+static inline 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_debug("%s: Use default chip_revision %x\n", __func__,
+ DEFAULT_CHIP_REVISION);
+ return DEFAULT_CHIP_REVISION;
+}
+
+#endif /* __PHY_RTK_USB_H__ */
diff --git a/drivers/phy/realtek/phy-rtk-usb2.c b/drivers/phy/realtek/phy-rtk-usb2.c
new file mode 100644
index 000000000000..c524126ab14e
--- /dev/null
+++ b/drivers/phy/realtek/phy-rtk-usb2.c
@@ -0,0 +1,2203 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * phy-rtk-usb2.c RTK usb2.0 PHY driver
+ *
+ * Copyright (C) 2023 Realtek Semiconductor Corporation
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/regmap.h>
+#include <linux/sys_soc.h>
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+#include <linux/usb.h>
+#include <linux/usb/phy.h>
+#include <linux/usb/hcd.h>
+
+#include "phy-rtk-usb.h"
+
+/* GUSB2PHYACCn register */
+#define PHY_NEW_REG_REQ BIT(25)
+#define PHY_VSTS_BUSY BIT(23)
+#define PHY_VCTRL_SHIFT 8
+#define PHY_REG_DATA_MASK 0xff
+
+#define GET_LOW_NIBBLE(addr) (addr & 0x0f)
+#define GET_HIGH_NIBBLE(addr) ((addr & 0xf0)>>4)
+
+#define EFUS_USB_DC_CAL_RATE 2
+#define EFUS_USB_DC_CAL_MAX 7
+
+#define EFUS_USB_DC_DIS_RATE 1
+#define EFUS_USB_DC_DIS_MAX 7
+
+#define MAX_PHY_DATA_SIZE 20
+#define OFFEST_PHY_READ 0x20
+
+#define SET_PAGE_OFFSET 0xf4
+#define SET_PAGE_0 0x9b
+#define SET_PAGE_1 0xbb
+#define SET_PAGE_2 0xdb
+
+#define PAGE_START 0xe0
+#define PAGE0_0xE4 0xe4
+#define PAGE0_0xE7 0xe7
+#define PAGE1_0xE0 0xe0
+#define PAGE1_0xE2 0xe2
+
+/* mapping 0xE0 to 0 ... 0xE7 to 7, 0xF0 to 8 ,,, 0xF7 to 15 */
+#define PAGE_ADDR_MAP_ARRAY_INDEX(addr) \
+ (((addr - PAGE_START)&0x7) + \
+ (((addr - PAGE_START)&0x10)>>1))
+#define ARRAY_INDEX_MAP_PAGE_ADDR(index) \
+ (((index + PAGE_START)&0x7) + \
+ (((index&0x8)<<1) + PAGE_START))
+
+struct reg_addr {
+ void __iomem *reg_wrap_vstatus;
+ void __iomem *reg_gusb2phyacc0;
+ int vstatus_index;
+};
+
+struct phy_parameter {
+ u8 addr;
+ u8 data;
+};
+
+struct phy_data {
+ int page0_size;
+ struct phy_parameter *page0;
+ int page1_size;
+ struct phy_parameter *page1;
+ int page2_size;
+ struct phy_parameter *page2;
+
+ bool check_efuse;
+ int check_efuse_version;
+#define CHECK_EFUSE_V1 1
+#define CHECK_EFUSE_V2 2
+ int8_t efuse_usb_dc_cal;
+ int efuse_usb_dc_cal_rate;
+ int usb_dc_cal_mask;
+ int8_t efuse_usb_dc_dis;
+ int efuse_usb_dc_dis_rate;
+ int usb_dc_dis_mask;
+ bool usb_dc_dis_at_page0;
+ bool do_toggle;
+ bool do_toggle_driving;
+ int disconnect_driving_updated;
+ bool use_default_parameter;
+ bool is_double_sensitivity_mode;
+ bool ldo_force_enable;
+ bool ldo_enable;
+ s32 ldo_page0_e4_compensate;
+ s32 page0_e4_compensate;
+};
+
+static char rtk_usb_phy_read(struct reg_addr *regAddr, char addr)
+{
+ void __iomem *reg_gusb2phyacc0 = regAddr->reg_gusb2phyacc0;
+ unsigned int regVal;
+ int ret = 0;
+
+ addr -= OFFEST_PHY_READ;
+
+ /* polling until VBusy == 0 */
+ ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+ if (ret)
+ return (char)ret;
+
+ /* VCtrl = low nibble of addr, and set PHY_NEW_REG_REQ */
+ regVal = PHY_NEW_REG_REQ | (GET_LOW_NIBBLE(addr) << PHY_VCTRL_SHIFT);
+ writel(regVal, reg_gusb2phyacc0);
+ ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+ if (ret)
+ return (char)ret;
+
+ /* VCtrl = high nibble of addr, and set PHY_NEW_REG_REQ */
+ regVal = PHY_NEW_REG_REQ | (GET_HIGH_NIBBLE(addr) << PHY_VCTRL_SHIFT);
+ writel(regVal, reg_gusb2phyacc0);
+ ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+ if (ret)
+ return (char)ret;
+
+ regVal = readl(reg_gusb2phyacc0);
+
+ return (char) (regVal & PHY_REG_DATA_MASK);
+}
+
+static int rtk_usb_phy_write(struct reg_addr *regAddr, char addr, char data)
+{
+ unsigned int regVal;
+ void __iomem *reg_wrap_vstatus = regAddr->reg_wrap_vstatus;
+ 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) */
+ writel((u32)data<<shift_bits, reg_wrap_vstatus);
+
+ ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+ if (ret)
+ return ret;
+
+ /* VCtrl = low nibble of addr, set PHY_NEW_REG_REQ */
+ regVal = PHY_NEW_REG_REQ | (GET_LOW_NIBBLE(addr) << PHY_VCTRL_SHIFT);
+
+ writel(regVal, reg_gusb2phyacc0);
+ ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+ if (ret)
+ return ret;
+
+ /* VCtrl = high nibble of addr, set PHY_NEW_REG_REQ */
+ regVal = PHY_NEW_REG_REQ | (GET_HIGH_NIBBLE(addr) << PHY_VCTRL_SHIFT);
+
+ writel(regVal, reg_gusb2phyacc0);
+ ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int rtk_usb_phy_set_page(struct reg_addr *regAddr, int page)
+{
+ 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;
+}
+
+#define USB_CTRL 0x0 /* usb ctrl at 0x98007FB0 */
+#define ISO_USB_U2PHY_REG_LDO_PW (BIT(20) | BIT(21) | BIT(22) | BIT(23))
+
+static int control_phy_power(struct rtk_usb_phy *rtk_phy,
+ struct phy_data *phy_data, struct reg_addr *regAddr)
+{
+ int use_ldo = 0;
+ unsigned int val;
+
+ if (!rtk_phy->usb_ctrl_regs) {
+ dev_info(rtk_phy->dev, "%s No usb_ctrl_regs can't set USB_CTRL\n",
+ __func__);
+ return use_ldo;
+ }
+
+ if (regmap_read(rtk_phy->usb_ctrl_regs, USB_CTRL, &val)) {
+ dev_err(rtk_phy->dev, "%s Get USB_CTRL fail\n", __func__);
+ return use_ldo;
+ }
+
+ if ((val & ISO_USB_U2PHY_REG_LDO_PW) == ISO_USB_U2PHY_REG_LDO_PW) {
+ dev_info(rtk_phy->dev, "%s phy use ldo power! (USB_CTRL val=0x%x)\n",
+ __func__, val);
+ use_ldo = 1;
+ goto out;
+ }
+
+ if (phy_data->ldo_force_enable) {
+ regmap_update_bits(rtk_phy->usb_ctrl_regs, USB_CTRL,
+ (unsigned int)ISO_USB_U2PHY_REG_LDO_PW,
+ (unsigned int)ISO_USB_U2PHY_REG_LDO_PW);
+ use_ldo = 1;
+
+ dev_info(rtk_phy->dev, "%s phy %s then turn on ldo! USB_CTRL val=0x%x\n",
+ __func__,
+ phy_data->ldo_force_enable ?
+ "ldo_force_enable":"no power",
+ val);
+ }
+
+out:
+ return use_ldo;
+}
+
+static u8 __updated_page0_0xe4_parameter(struct phy_data *phy_data, u8 data)
+{
+ u8 val;
+ s32 __val;
+ s32 page0_e4_compensate = 0;
+ s32 usb_dc_cal_mask = phy_data->usb_dc_cal_mask;
+
+ if (phy_data->check_efuse_version == CHECK_EFUSE_V1) {
+ if (phy_data->ldo_enable)
+ page0_e4_compensate = phy_data->ldo_page0_e4_compensate;
+
+ __val = (s32)(data & usb_dc_cal_mask) + page0_e4_compensate
+ + phy_data->efuse_usb_dc_cal;
+ } else { /* for CHECK_EFUSE_V2 or no efuse */
+ page0_e4_compensate = phy_data->page0_e4_compensate;
+
+ if (phy_data->efuse_usb_dc_cal)
+ __val = (s32)((phy_data->efuse_usb_dc_cal & usb_dc_cal_mask)
+ + page0_e4_compensate);
+ else
+ __val = (s32)(data & usb_dc_cal_mask);
+ }
+
+ if (__val > usb_dc_cal_mask)
+ __val = usb_dc_cal_mask;
+ else if (__val < 0)
+ __val = 0;
+
+ val = (data & (~usb_dc_cal_mask)) | (__val & usb_dc_cal_mask);
+
+ return val;
+}
+
+static u8 __updated_dc_disconnect_level_page0_0xe4(struct phy_data *phy_data,
+ u8 data)
+{
+ u8 val;
+ s32 __val;
+ s32 usb_dc_dis_mask = phy_data->usb_dc_dis_mask;
+ int offset = 4;
+
+ __val = (s32)((data >> offset) & usb_dc_dis_mask)
+ + phy_data->efuse_usb_dc_dis;
+
+ if (__val > usb_dc_dis_mask)
+ __val = usb_dc_dis_mask;
+ else if (__val < 0)
+ __val = 0;
+
+ val = (data & (~(usb_dc_dis_mask << offset))) |
+ (__val & usb_dc_dis_mask) << offset;
+
+ return val;
+}
+
+/* updated disconnect level at page0 0xe4 */
+static void update_dc_disconnect_level_at_page0(struct rtk_usb_phy *rtk_phy,
+ struct reg_addr *regAddr,
+ struct phy_data *phy_data, bool isUpdate)
+{
+ struct phy_parameter *phy_page_setting;
+ int i;
+
+ /* Set page 0 */
+ phy_page_setting = phy_data->page0;
+ rtk_usb_phy_set_page(regAddr, 0);
+
+ i = PAGE_ADDR_MAP_ARRAY_INDEX(PAGE0_0xE4);
+
+ if (i < phy_data->page0_size) {
+ u8 addr = (phy_page_setting + i)->addr;
+ u8 data = (phy_page_setting + i)->data;
+ u8 __data;
+ int offset = 4;
+ s32 usb_dc_dis_mask = phy_data->usb_dc_dis_mask;
+
+ if (!addr) {
+ addr = ARRAY_INDEX_MAP_PAGE_ADDR(i);
+ data = rtk_usb_phy_read(regAddr, addr);
+
+ (phy_page_setting + i)->addr = addr;
+ (phy_page_setting + i)->data = data;
+ dev_dbg(rtk_phy->dev,
+ "Get default addr %x value %x\n",
+ (phy_page_setting + i)->addr,
+ (phy_page_setting + i)->data);
+ }
+ __data = rtk_usb_phy_read(regAddr, addr);
+
+ /* keep default dc dis and real dc cal */
+ data = (data & ((usb_dc_dis_mask << offset))) |
+ (__data & (~(usb_dc_dis_mask << offset)));
+
+ if (isUpdate)
+ data = __updated_dc_disconnect_level_page0_0xe4(phy_data, data);
+
+ if (rtk_usb_phy_write(regAddr, addr, data)) {
+ dev_err(rtk_phy->dev,
+ "[%s:%d] Error page1 addr=0x%x value=0x%x\n",
+ __func__, __LINE__,
+ addr, data);
+ return;
+ }
+
+ dev_info(rtk_phy->dev,
+ "%s to set Page0 0xE4=%x for dc disconnect level (%s)\n",
+ __func__,
+ rtk_usb_phy_read(regAddr, addr),
+ isUpdate?"Update":"restore");
+ } else {
+ dev_err(rtk_phy->dev,
+ "ERROR: %s %d index=%d addr Not PAGE0_0xE4\n",
+ __func__, __LINE__, i);
+ }
+}
+
+static u8 __updated_dc_disconnect_level_page1_0xe2(struct phy_data *phy_data,
+ u8 data)
+{
+ u8 val;
+ s32 __val;
+ s32 usb_dc_dis_mask = phy_data->usb_dc_dis_mask;
+
+ if (phy_data->check_efuse_version == CHECK_EFUSE_V1) {
+ __val = (s32)(data & usb_dc_dis_mask)
+ + phy_data->efuse_usb_dc_dis;
+ } else { /* for CHECK_EFUSE_V2 or no efuse */
+ if (phy_data->efuse_usb_dc_dis)
+ __val = (s32)(phy_data->efuse_usb_dc_dis & usb_dc_dis_mask);
+ else
+ __val = (s32)(data & usb_dc_dis_mask);
+ }
+
+ if (__val > usb_dc_dis_mask)
+ __val = usb_dc_dis_mask;
+ else if (__val < 0)
+ __val = 0;
+
+ val = (data & (~usb_dc_dis_mask)) | (__val & usb_dc_dis_mask);
+
+ return val;
+}
+
+/* updated disconnect level at page1 0xe2 */
+static void update_dc_disconnect_level_at_page1(struct rtk_usb_phy *rtk_phy,
+ struct reg_addr *regAddr,
+ struct phy_data *phy_data, bool isUpdate)
+{
+ struct phy_parameter *phy_page_setting;
+ int i;
+
+ /* Set page 1 */
+ phy_page_setting = phy_data->page1;
+ rtk_usb_phy_set_page(regAddr, 1);
+
+ i = PAGE_ADDR_MAP_ARRAY_INDEX(PAGE1_0xE2);
+
+ if (i < phy_data->page1_size) {
+ u8 addr = (phy_page_setting + i)->addr;
+ u8 data = (phy_page_setting + i)->data;
+ u8 __data;
+ s32 usb_dc_dis_mask = phy_data->usb_dc_dis_mask;
+
+ if (!addr) {
+ addr = ARRAY_INDEX_MAP_PAGE_ADDR(i);
+ data = rtk_usb_phy_read(regAddr, addr);
+
+ (phy_page_setting + i)->addr = addr;
+ (phy_page_setting + i)->data = data;
+ dev_dbg(rtk_phy->dev,
+ "Get default addr %x value %x\n",
+ (phy_page_setting + i)->addr,
+ (phy_page_setting + i)->data);
+ }
+ __data = rtk_usb_phy_read(regAddr, addr);
+
+ data = (data & usb_dc_dis_mask) | (__data & ~(usb_dc_dis_mask));
+
+ if (isUpdate)
+ data = __updated_dc_disconnect_level_page1_0xe2(phy_data, data);
+
+ if (rtk_usb_phy_write(regAddr, addr, data)) {
+ dev_err(rtk_phy->dev,
+ "[%s:%d] Error page1 addr=0x%x value=0x%x\n",
+ __func__, __LINE__,
+ addr, data);
+ return;
+ }
+
+ dev_info(rtk_phy->dev,
+ "%s to set Page1 0xE2=%x for dc disconnect level (%s)\n",
+ __func__,
+ rtk_usb_phy_read(regAddr, addr),
+ isUpdate?"Update":"restore");
+ } else {
+ dev_err(rtk_phy->dev,
+ "ERROR: %s %d index=%d addr Not PAGE1_0xE2\n",
+ __func__, __LINE__, i);
+ }
+}
+
+static void update_dc_disconnect_level(struct rtk_usb_phy *rtk_phy,
+ struct reg_addr *regAddr,
+ struct phy_data *phy_data, bool isUpdate)
+{
+ if (phy_data->usb_dc_dis_at_page0)
+ update_dc_disconnect_level_at_page0(
+ rtk_phy, regAddr, phy_data, isUpdate);
+ else
+ update_dc_disconnect_level_at_page1(
+ rtk_phy, regAddr, phy_data, isUpdate);
+}
+
+static void do_rtk_usb_phy_toggle(struct rtk_usb_phy *rtk_phy,
+ int index, bool isConnect)
+{
+ struct reg_addr *regAddr;
+ struct phy_data *phy_data;
+ struct phy_parameter *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)
+ goto out;
+
+ if (phy_data->is_double_sensitivity_mode)
+ goto do_toggle_driving;
+
+ /* Set page 0 */
+ phy_page_setting = phy_data->page0;
+ rtk_usb_phy_set_page(regAddr, 0);
+
+ i = PAGE_ADDR_MAP_ARRAY_INDEX(PAGE0_0xE7);
+
+ if (i < phy_data->page0_size) {
+ u8 addr = (phy_page_setting + i)->addr;
+ u8 data = (phy_page_setting + i)->data;
+
+ if (!addr) {
+ addr = ARRAY_INDEX_MAP_PAGE_ADDR(i);
+ data = rtk_usb_phy_read(regAddr, addr);
+
+ (phy_page_setting + i)->addr = addr;
+ (phy_page_setting + i)->data = data;
+ dev_dbg(rtk_phy->dev,
+ "Get default addr %x value %x\n",
+ (phy_page_setting + i)->addr,
+ (phy_page_setting + i)->data);
+ }
+
+ 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));
+ } else {
+ dev_err(rtk_phy->dev,
+ "ERROR: %s %d index=%d addr Not PAGE0_0xE7\n",
+ __func__, __LINE__, i);
+ }
+
+do_toggle_driving:
+
+ if (!phy_data->do_toggle_driving)
+ goto do_toggle;
+
+ /* Page 0 addr 0xE4 driving capability */
+
+ /* Set page 0 */
+ phy_page_setting = phy_data->page0;
+ rtk_usb_phy_set_page(regAddr, 0);
+
+ i = PAGE_ADDR_MAP_ARRAY_INDEX(PAGE0_0xE4);
+
+ if (i < phy_data->page0_size) {
+ u8 addr = (phy_page_setting + i)->addr;
+ u8 data = (phy_page_setting + i)->data;
+
+ if (!addr) {
+ addr = ARRAY_INDEX_MAP_PAGE_ADDR(i);
+ data = rtk_usb_phy_read(regAddr, addr);
+
+ (phy_page_setting + i)->addr = addr;
+ (phy_page_setting + i)->data = data;
+ dev_dbg(rtk_phy->dev,
+ "Get default addr %x value %x\n",
+ (phy_page_setting + i)->addr,
+ (phy_page_setting + i)->data);
+ }
+
+ if (addr == PAGE0_0xE4)
+ data = __updated_page0_0xe4_parameter(phy_data, data);
+
+ if (isConnect) {
+ rtk_usb_phy_write(regAddr, addr, data);
+ } else {
+ u8 val;
+ s32 __val;
+ s32 driving_updated =
+ phy_data->disconnect_driving_updated;
+ s32 usb_dc_cal_mask = phy_data->usb_dc_cal_mask;
+
+ __val = (s32)(data & usb_dc_cal_mask) + driving_updated;
+
+ if (__val > usb_dc_cal_mask)
+ __val = usb_dc_cal_mask;
+ else if (__val < 0)
+ __val = 0;
+
+ val = (data & (~usb_dc_cal_mask)) | (__val & usb_dc_cal_mask);
+
+ rtk_usb_phy_write(regAddr, addr, val);
+ }
+ dev_info(rtk_phy->dev,
+ "%s %sconnect to set Page0 0xE4=%x for driving\n",
+ __func__,
+ isConnect?"":"dis",
+ rtk_usb_phy_read(regAddr, addr));
+ } else {
+ dev_err(rtk_phy->dev,
+ "ERROR: %s %d index=%d addr Not PAGE0_0xE4\n",
+ __func__, __LINE__, i);
+ }
+
+do_toggle:
+ /* restore dc disconnect level before toggle */
+ update_dc_disconnect_level(rtk_phy, regAddr, phy_data, false);
+
+ /* Set page 1 */
+ phy_page_setting = phy_data->page1;
+ rtk_usb_phy_set_page(regAddr, 1);
+
+ i = PAGE_ADDR_MAP_ARRAY_INDEX(PAGE1_0xE0);
+
+ if (i < phy_data->page1_size) {
+ u8 addr = (phy_page_setting + i)->addr;
+ u8 data = (phy_page_setting + i)->data;
+
+ if (!addr) {
+ addr = ARRAY_INDEX_MAP_PAGE_ADDR(i);
+ data = rtk_usb_phy_read(regAddr, addr);
+
+ (phy_page_setting + i)->addr = addr;
+ (phy_page_setting + i)->data = data;
+ dev_dbg(rtk_phy->dev,
+ "Get default addr %x value %x\n",
+ (phy_page_setting + i)->addr,
+ (phy_page_setting + i)->data);
+ }
+
+ 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)));
+ } else {
+ dev_err(rtk_phy->dev,
+ "ERROR: %s %d index=%d addr Not PAGE1_0xE0\n",
+ __func__, __LINE__, i);
+ }
+
+ /* update dc disconnect level after toggle */
+ update_dc_disconnect_level(rtk_phy, regAddr, phy_data, true);
+
+out:
+ return;
+}
+
+static int do_rtk_usb_phy_init(struct rtk_usb_phy *rtk_phy, int index)
+{
+ struct reg_addr *regAddr;
+ struct phy_data *phy_data;
+ struct phy_parameter *phy_page_setting;
+ int i;
+
+ if (!rtk_phy)
+ return -EINVAL;
+
+ dev_dbg(rtk_phy->dev, "%s: init 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)
+ return -EINVAL;
+
+ if (control_phy_power(rtk_phy, phy_data, regAddr)) {
+ phy_data->ldo_enable = true;
+ dev_info(rtk_phy->dev, "%s USB phy use ldo power compensate phy parameter (%d)\n",
+ __func__, phy_data->ldo_page0_e4_compensate);
+ }
+
+ if (phy_data->use_default_parameter) {
+ dev_info(rtk_phy->dev, "%s phy#%d use default parameter\n",
+ __func__, index);
+ goto do_toggle;
+ }
+
+ /* 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)
+ continue;
+
+ if (addr == PAGE0_0xE4)
+ data = __updated_page0_0xe4_parameter(phy_data, data);
+
+ if (rtk_usb_phy_write(regAddr, addr, data)) {
+ dev_err(rtk_phy->dev,
+ "[%s:%d] Error page0 addr=0x%x value=0x%x\n",
+ __func__, __LINE__, addr, data);
+ return -EINVAL;
+ }
+ dev_dbg(rtk_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 (!addr)
+ continue;
+
+ if (rtk_usb_phy_write(regAddr, addr, data)) {
+ dev_err(rtk_phy->dev,
+ "[%s:%d] Error page1 addr=0x%x value=0x%x\n",
+ __func__, __LINE__,
+ addr, data);
+ return -EINVAL;
+ }
+ dev_dbg(rtk_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 (!addr)
+ continue;
+
+ if (rtk_usb_phy_write(regAddr, addr, data)) {
+ dev_err(rtk_phy->dev,
+ "[%s:%d] Error page2 addr=0x%x value=0x%x\n",
+ __func__, __LINE__, addr, data);
+ return -1;
+ }
+ dev_dbg(rtk_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_usb_phy_toggle(rtk_phy, index, false);
+
+ return 0;
+}
+
+static int rtk_usb_phy_init(struct phy *phy)
+{
+ struct rtk_usb_phy *rtk_phy = phy_get_drvdata(phy);
+ unsigned long phy_init_time = jiffies;
+ int i, ret = 0;
+
+ dev_dbg(rtk_phy->dev, "Init RTK USB 2.0 PHY\n");
+ for (i = 0; i < rtk_phy->phyN; i++)
+ ret = do_rtk_usb_phy_init(rtk_phy, i);
+
+ dev_info(rtk_phy->dev, "Initialized RTK USB 2.0 PHY (take %dms)\n",
+ jiffies_to_msecs(jiffies - phy_init_time));
+ return ret;
+}
+
+static int rtk_usb_phy_exit(struct phy *phy)
+{
+ struct rtk_usb_phy *rtk_phy = phy_get_drvdata(phy);
+
+ dev_info(rtk_phy->dev, "Exit RTK USB 2.0 PHY\n");
+
+ return 0;
+}
+
+static const struct phy_ops ops = {
+ .init = rtk_usb_phy_init,
+ .exit = rtk_usb_phy_exit,
+ .owner = THIS_MODULE,
+};
+
+static void rtk_usb_phy_toggle(struct usb_phy *usb2_phy, bool isConnect, int port)
+{
+ int index = port;
+ struct rtk_usb_phy *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_usb_phy_toggle(rtk_phy, index, isConnect);
+}
+
+static int rtk_usb_phy_notify_port_status(struct usb_phy *x, int port,
+ u16 portstatus, u16 portchange)
+{
+ bool isConnect = false;
+
+ pr_debug("%s port=%d portstatus=0x%x portchange=0x%x\n",
+ __func__, port, (int)portstatus, (int)portchange);
+ if (portstatus & USB_PORT_STAT_CONNECTION)
+ isConnect = true;
+
+ if (portchange & USB_PORT_STAT_C_CONNECTION)
+ rtk_usb_phy_toggle(x, isConnect, port);
+
+ return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+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_debug("%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 *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 phy_parameter *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 = ARRAY_INDEX_MAP_PAGE_ADDR(i);
+ u8 data = (phy_page_setting + i)->data;
+ u8 value = rtk_usb_phy_read(regAddr, addr);
+
+ if ((phy_page_setting + i)->addr)
+ seq_printf(s, "Page 0: addr=0x%x data=0x%02x ==> read value=0x%02x\n",
+ addr, data, value);
+ else
+ seq_printf(s, "Page 0: addr=0x%x data=none ==> read value=0x%02x\n",
+ addr, value);
+ }
+
+ 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 = ARRAY_INDEX_MAP_PAGE_ADDR(i);
+ u8 data = (phy_page_setting + i)->data;
+ u8 value = rtk_usb_phy_read(regAddr, addr);
+
+ if ((phy_page_setting + i)->addr)
+ seq_printf(s, "Page 1: addr=0x%x data=0x%02x ==> read value=0x%02x\n",
+ addr, data, value);
+ else
+ seq_printf(s, "Page 1: addr=0x%x data=none ==> read value=0x%02x\n",
+ addr, value);
+ }
+
+ 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 = ARRAY_INDEX_MAP_PAGE_ADDR(i);
+ u8 data = (phy_page_setting + i)->data;
+ u8 value = rtk_usb_phy_read(regAddr, addr);
+
+ if ((phy_page_setting + i)->addr)
+ seq_printf(s, "Page 2: addr=0x%x data=0x%02x ==> read value=0x%02x\n",
+ addr, data, value);
+ else
+ seq_printf(s, "Page 2: addr=0x%x data=none ==> read value=0x%02x\n",
+ addr, value);
+ }
+
+ seq_puts(s, "Property:\n");
+ seq_printf(s, "check_efuse: %s\n",
+ phy_data->check_efuse?"Enable":"Disable");
+ seq_printf(s, "check_efuse_version: %d\n",
+ phy_data->check_efuse_version);
+ seq_printf(s, "efuse_usb_dc_cal: %d\n",
+ (int)phy_data->efuse_usb_dc_cal);
+ seq_printf(s, "efuse_usb_dc_cal_rate: %d\n",
+ phy_data->efuse_usb_dc_cal_rate);
+ seq_printf(s, "usb_dc_cal_mask: 0x%x\n",
+ phy_data->usb_dc_cal_mask);
+ seq_printf(s, "efuse_usb_dc_dis: %d\n",
+ (int)phy_data->efuse_usb_dc_dis);
+ seq_printf(s, "efuse_usb_dc_dis_rate: %d\n",
+ phy_data->efuse_usb_dc_dis_rate);
+ seq_printf(s, "usb_dc_dis_mask: 0x%x\n",
+ phy_data->usb_dc_dis_mask);
+ seq_printf(s, "usb_dc_dis_at_page0: %s\n",
+ phy_data->usb_dc_dis_at_page0?"true":"false");
+ seq_printf(s, "do_toggle: %s\n",
+ phy_data->do_toggle?"Enable":"Disable");
+ seq_printf(s, "do_toggle_driving: %s\n",
+ phy_data->do_toggle_driving?"Enable":"Disable");
+ seq_printf(s, "disconnect_driving_updated: 0x%x\n",
+ phy_data->disconnect_driving_updated);
+ seq_printf(s, "use_default_parameter: %s\n",
+ phy_data->use_default_parameter?"Enable":"Disable");
+ seq_printf(s, "is_double_sensitivity_mode: %s\n",
+ phy_data->is_double_sensitivity_mode?"Enable":"Disable");
+ seq_printf(s, "ldo_force_enable: %s\n",
+ phy_data->ldo_force_enable?"Enable":"Disable");
+ seq_printf(s, "ldo_enable: %s\n",
+ phy_data->ldo_enable?"Enable":"Disable");
+ seq_printf(s, "ldo_page0_e4_compensate: %d\n",
+ phy_data->ldo_page0_e4_compensate);
+ seq_printf(s, "page0_e4_compensate: %d\n",
+ phy_data->page0_e4_compensate);
+ }
+
+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 __get_parameter_at_page(struct seq_file *s,
+ struct rtk_usb_phy *rtk_phy,
+ struct phy_parameter *phy_parameter_array,
+ const char *phy_page, const char *phy_addr)
+{
+ struct phy_parameter *phy_parameter;
+ uint32_t addr;
+ int i, ret;
+
+ ret = kstrtouint(phy_addr, 16, &addr);
+ if (ret < 0) {
+ pr_err("%s::kstrtouint() failed\n", __func__);
+ return -EINVAL;
+ }
+ i = PAGE_ADDR_MAP_ARRAY_INDEX(addr);
+ phy_parameter = (phy_parameter_array + i);
+
+ if (phy_parameter->addr)
+ seq_printf(s, "Now Parameter %s addr 0x%02x = 0x%02x\n",
+ phy_page, phy_parameter->addr, phy_parameter->data);
+ else
+ seq_printf(s, "Now Parameter %s addr 0x%02x is default\n",
+ phy_page, addr);
+
+ dev_dbg(rtk_phy->dev, "%s addr=0x%02x data=0x%02x\n",
+ __func__, phy_parameter->addr, phy_parameter->data);
+
+ return 0;
+}
+
+static int __set_parameter_at_page(
+ struct rtk_usb_phy *rtk_phy,
+ struct reg_addr *regAddr, struct phy_data *phy_data,
+ struct phy_parameter *phy_parameter_array,
+ const char *phy_page, const char *phy_addr, const char *phy_value)
+{
+ struct phy_parameter *phy_parameter;
+ uint32_t addr, value;
+ int i, ret;
+
+ ret = kstrtouint(phy_addr, 16, &addr);
+ if (ret < 0)
+ return -EINVAL;
+
+ ret = kstrtouint(phy_value, 16, &value);
+ if (ret < 0)
+ return -EINVAL;
+
+ i = PAGE_ADDR_MAP_ARRAY_INDEX(addr);
+ phy_parameter = (phy_parameter_array + i);
+
+ if (phy_parameter->addr) {
+ phy_parameter->data = value;
+ } else {
+ phy_parameter->addr = addr;
+ phy_parameter->data = value;
+ }
+
+ dev_dbg(rtk_phy->dev, "%s addr=0x%02x data=0x%02x\n",
+ __func__, phy_parameter->addr, phy_parameter->data);
+
+ if (strcmp("page0", phy_page) == 0 && (addr == PAGE0_0xE4))
+ value = __updated_page0_0xe4_parameter(phy_data, value);
+
+ if (rtk_usb_phy_write(regAddr, addr, value))
+ dev_err(rtk_phy->dev,
+ "[%s:%d] Error: addr=0x%02x value=0x%02x\n",
+ __func__, __LINE__, addr, value);
+
+ return 0;
+}
+
+static int rtk_usb2_set_parameter_show(struct seq_file *s, void *unused)
+{
+ struct rtk_usb_phy *rtk_phy = s->private;
+ const struct file *file = s->file;
+ const char *file_name = file_dentry(file)->d_iname;
+ struct dentry *p_dentry = file_dentry(file)->d_parent;
+ const char *dir_name = p_dentry->d_iname;
+ struct dentry *pp_dentry = p_dentry->d_parent;
+ const char *phy_dir_name = pp_dentry->d_iname;
+ int ret = 0;
+ int index;
+ struct phy_data *phy_data = NULL;
+
+ for (index = 0; index < rtk_phy->phyN; index++) {
+ size_t sz = 30;
+ char name[30] = {0};
+
+ snprintf(name, sz, "phy%d", index);
+ if (strncmp(name, phy_dir_name, strlen(name)) == 0) {
+ phy_data = &((struct phy_data *)rtk_phy->phy_data)[index];
+ break;
+ }
+ }
+ if (!phy_data) {
+ dev_err(rtk_phy->dev,
+ "%s: No phy_data for %s/%s/%s\n",
+ __func__, phy_dir_name, dir_name, file_name);
+ return -EINVAL;
+ }
+
+ if (strcmp("page0", dir_name) == 0)
+ ret = __get_parameter_at_page(s, rtk_phy, phy_data->page0,
+ dir_name, file_name);
+ else if (strcmp("page1", dir_name) == 0)
+ ret = __get_parameter_at_page(s, rtk_phy, phy_data->page1,
+ dir_name, file_name);
+ else if (strcmp("page2", dir_name) == 0)
+ ret = __get_parameter_at_page(s, rtk_phy, phy_data->page2,
+ dir_name, file_name);
+
+ if (ret < 0)
+ return ret;
+
+ seq_puts(s, "Set phy parameter by following command\n");
+ seq_printf(s, "echo \"value\" > %s/%s/%s\n",
+ phy_dir_name, dir_name, file_name);
+
+ 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)
+{
+ const char *file_name = file_dentry(file)->d_iname;
+ struct dentry *p_dentry = file_dentry(file)->d_parent;
+ const char *dir_name = p_dentry->d_iname;
+ struct dentry *pp_dentry = p_dentry->d_parent;
+ const char *phy_dir_name = pp_dentry->d_iname;
+ struct seq_file *s = file->private_data;
+ struct rtk_usb_phy *rtk_phy = s->private;
+ struct reg_addr *regAddr = NULL;
+ struct phy_data *phy_data = NULL;
+ int ret = 0;
+ char buffer[40] = {0};
+ int index;
+
+ if (copy_from_user(&buffer, ubuf,
+ min_t(size_t, sizeof(buffer) - 1, count)))
+ return -EFAULT;
+
+ for (index = 0; index < rtk_phy->phyN; index++) {
+ size_t sz = 30;
+ char name[30] = {0};
+
+ snprintf(name, sz, "phy%d", index);
+ if (strncmp(name, phy_dir_name, strlen(name)) == 0) {
+ regAddr = &((struct reg_addr *)rtk_phy->reg_addr)[index];
+ phy_data = &((struct phy_data *)rtk_phy->phy_data)[index];
+ break;
+ }
+ }
+ if (!regAddr) {
+ dev_err(rtk_phy->dev,
+ "%s: No regAddr for %s/%s/%s\n",
+ __func__, phy_dir_name, dir_name, file_name);
+ return -EINVAL;
+ }
+ if (!phy_data) {
+ dev_err(rtk_phy->dev,
+ "%s: No phy_data for %s/%s/%s\n",
+ __func__, phy_dir_name, dir_name, file_name);
+ return -EINVAL;
+ }
+
+ if (strcmp("page0", dir_name) == 0) {
+ rtk_usb_phy_set_page(regAddr, 0);
+ ret = __set_parameter_at_page(rtk_phy, regAddr, phy_data,
+ phy_data->page0, dir_name, file_name, buffer);
+ } else if (strcmp("page1", dir_name) == 0) {
+ rtk_usb_phy_set_page(regAddr, 1);
+ ret = __set_parameter_at_page(rtk_phy, regAddr, phy_data,
+ phy_data->page1, dir_name, file_name, buffer);
+ } else if (strcmp("page2", dir_name) == 0) {
+ rtk_usb_phy_set_page(regAddr, 2);
+ ret = __set_parameter_at_page(rtk_phy, regAddr, phy_data,
+ phy_data->page2, dir_name, file_name, buffer);
+ }
+ if (ret < 0)
+ return ret;
+
+ 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 *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 *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 int create_debug_set_parameter_files(struct rtk_usb_phy *rtk_phy,
+ struct dentry *phy_dir, const char *page, size_t addr_size)
+{
+ struct dentry *page_dir;
+ int i;
+
+ page_dir = debugfs_create_dir(page, phy_dir);
+ if (!page_dir) {
+ dev_err(rtk_phy->dev,
+ "%s Error create folder %s fail\n",
+ __func__, page);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < addr_size; i++) {
+ size_t sz = 30;
+ char name[30] = {0};
+
+ snprintf(name, sz, "%x", ARRAY_INDEX_MAP_PAGE_ADDR(i));
+
+ if (!debugfs_create_file(name, 0644,
+ page_dir, rtk_phy,
+ &rtk_usb2_set_parameter_fops))
+ dev_err(rtk_phy->dev,
+ "%s Error create file %s/%s fail",
+ page, name, __func__);
+ }
+
+ return 0;
+}
+
+static inline void create_debug_files(struct rtk_usb_phy *rtk_phy)
+{
+ struct dentry *phy_debug_root = NULL;
+ struct dentry *set_parameter_dir = NULL;
+
+ 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;
+
+ set_parameter_dir = debugfs_create_dir("set_parameter",
+ rtk_phy->debug_dir);
+ if (set_parameter_dir) {
+ int index, ret;
+
+ for (index = 0; index < rtk_phy->phyN; index++) {
+ struct dentry *phy_dir;
+ struct phy_data *phy_data;
+ size_t sz = 30;
+ char name[30] = {0};
+
+ snprintf(name, sz, "phy%d", index);
+
+ phy_data = &((struct phy_data *)rtk_phy->phy_data)[index];
+
+ phy_dir = debugfs_create_dir(name, set_parameter_dir);
+ if (!phy_dir) {
+ dev_err(rtk_phy->dev,
+ "%s Error create folder %s fail\n",
+ name, __func__);
+ goto file_error;
+ }
+
+ ret = create_debug_set_parameter_files(rtk_phy, phy_dir,
+ "page0", phy_data->page0_size);
+ if (ret < 0) {
+ dev_err(rtk_phy->dev,
+ "%s Error create files for page0 fail\n",
+ __func__);
+ goto file_error;
+ }
+
+ ret = create_debug_set_parameter_files(rtk_phy, phy_dir,
+ "page1", phy_data->page1_size);
+ if (ret < 0) {
+ dev_err(rtk_phy->dev,
+ "%s Error create files for page1 fail\n",
+ __func__);
+ goto file_error;
+ }
+
+ ret = create_debug_set_parameter_files(rtk_phy, phy_dir,
+ "page2", phy_data->page2_size);
+ if (ret < 0) {
+ dev_err(rtk_phy->dev,
+ "%s Error create files for page2 fail\n",
+ __func__);
+ 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);
+}
+
+static inline void remove_debug_files(struct rtk_usb_phy *rtk_phy)
+{
+ debugfs_remove_recursive(rtk_phy->debug_dir);
+}
+#else
+static inline void create_debug_files(struct rtk_usb_phy *rtk_phy) { }
+static inline void remove_debug_files(struct rtk_usb_phy *rtk_phy) { }
+#endif /* CONFIG_DEBUG_FS */
+
+static int __get_phy_parameter_by_efuse(struct rtk_usb_phy *rtk_phy,
+ struct phy_data *phy_data, int index)
+{
+ u8 value = 0;
+ struct nvmem_cell *cell;
+ struct soc_device_attribute rtk_soc_groot[] = {
+ { .family = "Realtek Groot",},
+ { /* empty */ }
+ };
+ struct soc_device_attribute rtk_soc_hank[] = {
+ { .family = "Realtek Hank",},
+ { /* empty */ }
+ };
+ struct soc_device_attribute rtk_soc_efuse_v1[] = {
+ { .family = "Realtek Phoenix",},
+ { .family = "Realtek Kylin",},
+ { .family = "Realtek Hercules",},
+ { .family = "Realtek Thor",},
+ { .family = "Realtek Hank",},
+ { .family = "Realtek Groot",},
+ { .family = "Realtek Stark",},
+ { .family = "Realtek Parker",},
+ { /* empty */ }
+ };
+ struct soc_device_attribute rtk_soc_dis_level_at_page0[] = {
+ { .family = "Realtek Phoenix",},
+ { .family = "Realtek Kylin",},
+ { .family = "Realtek Hercules",},
+ { .family = "Realtek Thor",},
+ { .family = "Realtek Hank",},
+ { .family = "Realtek Groot",},
+ { /* empty */ }
+ };
+
+ if (soc_device_match(rtk_soc_efuse_v1)) {
+ dev_dbg(rtk_phy->dev, "Use efuse v1 to updated phy parameter\n");
+ phy_data->check_efuse_version = CHECK_EFUSE_V1;
+ } else {
+ dev_dbg(rtk_phy->dev, "Use efuse v2 to updated phy parameter\n");
+ phy_data->check_efuse_version = CHECK_EFUSE_V2;
+ }
+
+ if (soc_device_match(rtk_soc_dis_level_at_page0)) {
+ dev_dbg(rtk_phy->dev, "Use usb_dc_dis_at_page0\\n");
+ phy_data->usb_dc_dis_at_page0 = true;
+
+ phy_data->usb_dc_cal_mask = 0xf;
+ phy_data->usb_dc_dis_mask = 0xf;
+
+ phy_data->disconnect_driving_updated = 0xf;
+ } else {
+ dev_dbg(rtk_phy->dev, "No use usb_dc_dis_at_page0\n");
+ phy_data->usb_dc_dis_at_page0 = false;
+
+ phy_data->usb_dc_cal_mask = 0x1f;
+ phy_data->usb_dc_dis_mask = 0xf;
+
+ phy_data->disconnect_driving_updated = 0x8;
+ }
+
+ phy_data->efuse_usb_dc_cal_rate = EFUS_USB_DC_CAL_RATE;
+ phy_data->efuse_usb_dc_dis_rate = EFUS_USB_DC_DIS_RATE;
+
+ if (soc_device_match(rtk_soc_hank))
+ phy_data->efuse_usb_dc_cal_rate = 1;
+
+ if (!phy_data->check_efuse)
+ goto out;
+
+ /* Read efuse for usb dc cal */
+ 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;
+
+ buf = nvmem_cell_read(cell, &buf_size);
+
+ value = buf[0] & phy_data->usb_dc_cal_mask;
+
+ dev_dbg(rtk_phy->dev,
+ "buf=0x%x buf_size=%d value=0x%x\n",
+ buf[0], (int)buf_size, value);
+
+ kfree(buf);
+ nvmem_cell_put(cell);
+ }
+
+ if (phy_data->check_efuse_version == CHECK_EFUSE_V1) {
+ int rate = phy_data->efuse_usb_dc_cal_rate;
+
+ if (value <= EFUS_USB_DC_CAL_MAX)
+ phy_data->efuse_usb_dc_cal = (int8_t)(value * rate);
+ else
+ phy_data->efuse_usb_dc_cal = -(int8_t)(
+ (EFUS_USB_DC_CAL_MAX & value) * rate);
+
+ if (soc_device_match(rtk_soc_groot)) {
+ dev_info(rtk_phy->dev, "For groot IC we need a workaround to adjust efuse_usb_dc_cal\n");
+
+ /* We don't multiple dc_cal_rate=2 for positive dc cal compensate */
+ if (value <= EFUS_USB_DC_CAL_MAX)
+ phy_data->efuse_usb_dc_cal = (int8_t)(value);
+
+ /* We set max dc cal compensate is 0x8 if otp is 0x7 */
+ if (value == 0x7)
+ phy_data->efuse_usb_dc_cal = (int8_t)(value + 1);
+ }
+ } else { /* for CHECK_EFUSE_V2 */
+ phy_data->efuse_usb_dc_cal = value & phy_data->usb_dc_cal_mask;
+ }
+
+ dev_dbg(rtk_phy->dev, "Get Efuse usb_dc_cal=%d for index=%d value=%x\n",
+ phy_data->efuse_usb_dc_cal, index, value);
+
+ /* Read efuse for usb dc disconnect level */
+ value = 0;
+ cell = nvmem_cell_get(rtk_phy->dev, "usb-dc-dis");
+ if (IS_ERR(cell)) {
+ dev_warn(rtk_phy->dev, "%s failed to get usb-dc-dis: %ld\n",
+ __func__, PTR_ERR(cell));
+ } else {
+ unsigned char *buf;
+ size_t buf_size;
+
+ buf = nvmem_cell_read(cell, &buf_size);
+
+ value = buf[0] & phy_data->usb_dc_dis_mask;
+
+ dev_dbg(rtk_phy->dev,
+ "buf=0x%x buf_size=%d value=0x%x\n",
+ buf[0], (int)buf_size, value);
+
+ kfree(buf);
+ nvmem_cell_put(cell);
+ }
+
+ if (phy_data->check_efuse_version == CHECK_EFUSE_V1) {
+ int rate = phy_data->efuse_usb_dc_dis_rate;
+
+ if (value <= EFUS_USB_DC_DIS_MAX)
+ phy_data->efuse_usb_dc_dis = (int8_t)(value * rate);
+ else
+ phy_data->efuse_usb_dc_dis = -(int8_t)(
+ (EFUS_USB_DC_DIS_MAX & value) * rate);
+ } else { /* for CHECK_EFUSE_V2 */
+ phy_data->efuse_usb_dc_dis = value & phy_data->usb_dc_dis_mask;
+ }
+
+ dev_dbg(rtk_phy->dev, "Get Efuse usb_dc_dis=%d for index=%d value=%x\n",
+ phy_data->efuse_usb_dc_dis, index, value);
+
+out:
+ return 0;
+}
+
+/* Get default phy parameter for update by efuse or ldo_page0_e4_compensate */
+static int __get_default_phy_parameter_for_updated(
+ struct rtk_usb_phy *rtk_phy, int index)
+{
+ int i;
+ struct reg_addr *regAddr;
+ struct phy_data *phy_data;
+ struct phy_parameter *phy_page_setting;
+
+ regAddr = &((struct reg_addr *)rtk_phy->reg_addr)[index];
+ phy_data = &((struct phy_data *)rtk_phy->phy_data)[index];
+
+ /* Get PAGE0_0xE4 default value */
+ if (phy_data->efuse_usb_dc_cal || phy_data->ldo_page0_e4_compensate ||
+ (phy_data->efuse_usb_dc_dis && phy_data->usb_dc_dis_at_page0)) {
+ phy_page_setting = phy_data->page0;
+ rtk_usb_phy_set_page(regAddr, 0);
+
+ i = PAGE_ADDR_MAP_ARRAY_INDEX(PAGE0_0xE4);
+ if (i < phy_data->page0_size) {
+ u8 addr = (phy_page_setting + i)->addr;
+ u8 data = (phy_page_setting + i)->data;
+
+ if (!addr) {
+ addr = ARRAY_INDEX_MAP_PAGE_ADDR(i);
+ data = rtk_usb_phy_read(regAddr, addr);
+
+ (phy_page_setting + i)->addr = addr;
+ (phy_page_setting + i)->data = data;
+ dev_dbg(rtk_phy->dev,
+ "Get default addr %x value %x\n",
+ (phy_page_setting + i)->addr,
+ (phy_page_setting + i)->data);
+ }
+ }
+ }
+
+ /* Get PAGE1_0xE2 default value */
+ if (phy_data->efuse_usb_dc_dis && !phy_data->usb_dc_dis_at_page0) {
+ phy_page_setting = phy_data->page1;
+ rtk_usb_phy_set_page(regAddr, 1);
+
+ i = PAGE_ADDR_MAP_ARRAY_INDEX(PAGE1_0xE2);
+ if (i < phy_data->page1_size) {
+ u8 addr = (phy_page_setting + i)->addr;
+ u8 data = (phy_page_setting + i)->data;
+
+ if (!addr) {
+ addr = ARRAY_INDEX_MAP_PAGE_ADDR(i);
+ data = rtk_usb_phy_read(regAddr, addr);
+
+ (phy_page_setting + i)->addr = addr;
+ (phy_page_setting + i)->data = data;
+ dev_dbg(rtk_phy->dev,
+ "Get default page1 addr %x value %x\n",
+ (phy_page_setting + i)->addr,
+ (phy_page_setting + i)->data);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int __get_phy_parameter_v1(struct device *dev, struct phy_data *phy_data,
+ struct device_node *sub_node)
+{
+ int phy_data_page0_size, phy_data_page1_size;
+ int phy_data_page2_size;
+ 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);
+
+ ret = of_property_read_u32_index(sub_node,
+ "realtek,phy-data-page0-size", 0, &phy_data_page0_size);
+ if (ret)
+ goto err;
+
+ ret = of_property_read_u32_index(sub_node,
+ "realtek,phy-data-page1-size", 0, &phy_data_page1_size);
+ if (ret)
+ goto err;
+
+ dev_dbg(dev, "%s %d phy_data_page0_size=%d, phy_data_page1_size=%d\n",
+ __func__, __LINE__,
+ 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,
+ "realtek,phy-data-page2-size", 0, &phy_data_page2_size);
+ if (ret)
+ phy_data_page2_size = 0;
+ dev_dbg(dev, "%s %d phy_data_page2_size=%d\n",
+ __func__, __LINE__,
+ 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 phy_parameter) *
+ 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 phy_parameter) *
+ 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 phy_parameter) *
+ phy_data->page2_size,
+ GFP_KERNEL);
+ if (!phy_data->page2) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ }
+
+ ret = of_property_read_u8_array(sub_node, "realtek,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[32] = {0};
+
+ snprintf(phy_data_revision, 27, "realtek,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, "realtek,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 phy_parameter *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, "realtek,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[32] = {0};
+
+ snprintf(phy_data_revision, 27, "realtek,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, "realtek,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 phy_parameter *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,
+ "realtek,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[32] = {0};
+
+ snprintf(phy_data_revision, 27, "realtek,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,
+ "realtek,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 phy_parameter *phy_data_page2 =
+ (phy_data->page2 + i);
+ phy_data_page2->addr = tmp_addr[i];
+ phy_data_page2->data = tmp_data[i];
+ }
+ }
+
+err:
+ return ret;
+}
+
+static int __get_phy_parameter_v2(struct device *dev, struct phy_data *phy_data,
+ struct device_node *sub_node)
+{
+ u32 page_size = 0;
+ u32 num_cells = 2; /*< addr value > */
+ u32 data_size;
+ int i, offset, chip_revision, revision, ret = 0;
+ char phy_data_revision[32] = {0};
+
+ chip_revision = __get_chip_revision();
+
+ /* Page 0 */
+ ret = of_property_read_u32_index(sub_node, "realtek,page0-size", 0, &page_size);
+ if (ret) {
+ dev_err(dev, "%s No page0_size\n", __func__);
+ goto parse_page1;
+ }
+
+ phy_data->page0_size = page_size;
+ phy_data->page0 = devm_kzalloc(dev,
+ sizeof(struct phy_parameter) * page_size, GFP_KERNEL);
+ if (!phy_data->page0) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ revision = chip_revision;
+ while (revision >= DEFAULT_CHIP_REVISION) {
+ snprintf(phy_data_revision, 23, "realtek,page0-data-%X", revision);
+
+ if (of_get_property(sub_node, phy_data_revision, &data_size)) {
+ dev_dbg(dev, "%s load %s parameter (data_size=%d)\n",
+ __func__, phy_data_revision, data_size);
+ break;
+ }
+ revision--;
+ if ((revision & 0xFF) > 0xF)
+ revision = (revision & 0xF00) + 0xF;
+
+ data_size = 0;
+ ret = 0;
+ }
+ data_size = data_size / (sizeof(u32) * num_cells);
+
+ for (i = 0; i < data_size; i++) {
+ struct phy_parameter *phy_data_page;
+ u32 addr, data;
+ int index;
+
+ offset = i * num_cells;
+
+ ret = of_property_read_u32_index(sub_node, phy_data_revision,
+ offset, &addr);
+ if (ret) {
+ dev_err(dev, "ERROR: To get %s i=%d addr=0x%x\n",
+ phy_data_revision, i, addr);
+ break;
+ }
+
+ ret = of_property_read_u32_index(sub_node, phy_data_revision,
+ offset + 1, &data);
+ if (ret) {
+ dev_err(dev, "ERROR: To get %s i=%d addr=0x%x\n",
+ phy_data_revision, i, data);
+ break;
+ }
+
+ index = PAGE_ADDR_MAP_ARRAY_INDEX(addr);
+ phy_data_page = (phy_data->page0 + index);
+ phy_data_page->addr = (char)addr;
+ phy_data_page->data = (char)data;
+
+ dev_dbg(dev, "%s index=%d addr=0x%x data=0x%x\n",
+ phy_data_revision, index,
+ phy_data_page->addr, phy_data_page->data);
+ }
+
+parse_page1:
+ /* Page 1 */
+ ret = of_property_read_u32_index(sub_node, "realtek,page1-size", 0, &page_size);
+ if (ret) {
+ dev_err(dev, "%s No page0_size\n", __func__);
+ goto parse_page2;
+ }
+
+ phy_data->page1_size = page_size;
+ phy_data->page1 = devm_kzalloc(dev,
+ sizeof(struct phy_parameter) * page_size, GFP_KERNEL);
+ if (!phy_data->page1) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ revision = chip_revision;
+ while (revision >= DEFAULT_CHIP_REVISION) {
+ snprintf(phy_data_revision, 23, "realtek,page1-data_%X", revision);
+
+ if (of_get_property(sub_node, phy_data_revision, &data_size)) {
+ dev_dbg(dev, "%s load %s parameter (data_size=%d)\n",
+ __func__, phy_data_revision, data_size);
+ break;
+ }
+ revision--;
+ if ((revision & 0xFF) > 0xF)
+ revision = (revision & 0xF00) + 0xF;
+
+ data_size = 0;
+ ret = 0;
+ }
+ data_size = data_size / (sizeof(u32) * num_cells);
+
+ for (i = 0; i < data_size; i++) {
+ struct phy_parameter *phy_data_page;
+ u32 addr, data;
+ int index;
+
+ offset = i * num_cells;
+
+ ret = of_property_read_u32_index(sub_node, phy_data_revision,
+ offset, &addr);
+ if (ret) {
+ dev_err(dev, "ERROR: To get %s i=%d addr=0x%x\n",
+ phy_data_revision, i, addr);
+ break;
+ }
+
+ ret = of_property_read_u32_index(sub_node, phy_data_revision,
+ offset + 1, &data);
+ if (ret) {
+ dev_err(dev, "ERROR: To get %s i=%d addr=0x%x\n",
+ phy_data_revision, i, data);
+ break;
+ }
+
+ index = PAGE_ADDR_MAP_ARRAY_INDEX(addr);
+ phy_data_page = phy_data->page1 + index;
+ phy_data_page->addr = (char)addr;
+ phy_data_page->data = (char)data;
+
+ dev_dbg(dev, "%s index=%d addr=0x%x data=0x%x\n",
+ phy_data_revision, index,
+ phy_data_page->addr, phy_data_page->data);
+ }
+
+parse_page2:
+ /* Page 2 */
+ ret = of_property_read_u32_index(sub_node, "realtek,page2-size", 0, &page_size);
+ if (ret) {
+ dev_dbg(dev, "%s No page2_size\n", __func__);
+ goto out;
+ }
+
+ phy_data->page2_size = page_size;
+ phy_data->page2 = devm_kzalloc(dev,
+ sizeof(struct phy_parameter) * page_size, GFP_KERNEL);
+ if (!phy_data->page2) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ revision = chip_revision;
+ while (revision >= DEFAULT_CHIP_REVISION) {
+ snprintf(phy_data_revision, 23, "realtek,page2-data-%X", revision);
+
+ if (of_get_property(sub_node, phy_data_revision, &data_size)) {
+ dev_dbg(dev, "%s load %s parameter (data_size=%d)\n",
+ __func__, phy_data_revision, data_size);
+ break;
+ }
+ revision--;
+ if ((revision & 0xFF) > 0xF)
+ revision = (revision & 0xF00) + 0xF;
+
+ data_size = 0;
+ ret = 0;
+ }
+ data_size = data_size / (sizeof(u32) * num_cells);
+
+ for (i = 0; i < data_size; i++) {
+ struct phy_parameter *phy_data_page;
+ u32 addr, data;
+ int index;
+
+ offset = i * num_cells;
+
+ ret = of_property_read_u32_index(sub_node, phy_data_revision,
+ offset, &addr);
+ if (ret) {
+ dev_err(dev, "ERROR: To get %s i=%d addr=0x%x\n",
+ phy_data_revision, i, addr);
+ break;
+ }
+
+ ret = of_property_read_u32_index(sub_node, phy_data_revision,
+ offset + 1, &data);
+ if (ret) {
+ dev_err(dev, "ERROR: To get %s i=%d addr=0x%x\n",
+ phy_data_revision, i, data);
+ break;
+ }
+
+ index = PAGE_ADDR_MAP_ARRAY_INDEX(addr);
+ phy_data_page = phy_data->page2 + index;
+ phy_data_page->addr = (char)addr;
+ phy_data_page->data = (char)data;
+
+ dev_dbg(dev, "%s index=%d addr=0x%x data=0x%x\n",
+ phy_data_revision, index,
+ phy_data_page->addr, phy_data_page->data);
+ }
+
+out:
+ return ret;
+}
+
+static int __get_phy_parameter(struct rtk_usb_phy *rtk_phy, int index)
+{
+ struct device *dev = rtk_phy->dev;
+ struct reg_addr *addr =
+ &((struct reg_addr *)rtk_phy->reg_addr)[index];
+ struct phy_data *phy_data =
+ &((struct phy_data *)rtk_phy->phy_data)[index];
+ char phy_name[5], phy_name_v2[10];
+ struct device_node *sub_node;
+ int ret = 0;
+
+ addr->reg_wrap_vstatus = 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_vstatus=%p\n",
+ __func__, __LINE__,
+ index, addr->reg_wrap_vstatus);
+ 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_dbg(dev, "%s %d: #%d Get phy data v1 sub_node for %s\n",
+ __func__, __LINE__, index, phy_name);
+ ret = __get_phy_parameter_v1(dev, phy_data, sub_node);
+ if (ret)
+ goto err;
+ } else {
+ snprintf(phy_name_v2, 10, "phy%d_data", index);
+ sub_node = of_get_child_by_name(dev->of_node, phy_name_v2);
+ if (sub_node) {
+ dev_dbg(dev, "%s %d: #%d Get phy data v2 sub_node for %s\n",
+ __func__, __LINE__, index, phy_name_v2);
+ ret = __get_phy_parameter_v2(dev, phy_data, sub_node);
+ if (ret)
+ goto err;
+ }
+ }
+
+ if (!sub_node)
+ goto err;
+
+ if (of_property_read_bool(sub_node, "realtek,do-toggle"))
+ phy_data->do_toggle = true;
+ else
+ phy_data->do_toggle = false;
+
+ if (of_property_read_bool(sub_node, "realtek,do-toggle-driving"))
+ phy_data->do_toggle_driving = true;
+ else
+ phy_data->do_toggle_driving = false;
+
+ if (of_property_read_bool(sub_node, "realtek,check-efuse"))
+ phy_data->check_efuse = true;
+ else
+ phy_data->check_efuse = false;
+
+ if (of_property_read_bool(sub_node, "realtek,use-default-parameter"))
+ phy_data->use_default_parameter = true;
+ else
+ phy_data->use_default_parameter = false;
+
+ if (of_property_read_bool(sub_node,
+ "realtek,is-double-sensitivity-mode"))
+ phy_data->is_double_sensitivity_mode = true;
+ else
+ phy_data->is_double_sensitivity_mode = false;
+
+ if (of_property_read_bool(sub_node,
+ "realtek,ldo-force-enable"))
+ phy_data->ldo_force_enable = true;
+ else
+ phy_data->ldo_force_enable = false;
+
+ if (of_property_read_s32(sub_node,
+ "realtek,ldo-page0-e4-compensate", &phy_data->ldo_page0_e4_compensate))
+ phy_data->ldo_page0_e4_compensate = 0;
+
+ if (of_property_read_s32(sub_node,
+ "realtek,page0-e4-compensate", &phy_data->page0_e4_compensate))
+ phy_data->page0_e4_compensate = 0;
+
+ __get_phy_parameter_by_efuse(rtk_phy, phy_data, index);
+
+ __get_default_phy_parameter_for_updated(rtk_phy, index);
+err:
+ return ret;
+}
+
+static int rtk_usb2phy_probe(struct platform_device *pdev)
+{
+ struct rtk_usb_phy *rtk_phy;
+ struct device *dev = &pdev->dev;
+ struct phy *generic_phy;
+ struct phy_provider *phy_provider;
+ int index, ret = 0;
+ int port_index, phyN;
+
+ rtk_phy = devm_kzalloc(dev, sizeof(*rtk_phy), GFP_KERNEL);
+ if (!rtk_phy)
+ return -ENOMEM;
+
+ rtk_phy->dev = &pdev->dev;
+ rtk_phy->phy.dev = rtk_phy->dev;
+ rtk_phy->phy.label = "rtk-usb2phy";
+ rtk_phy->phy.notify_port_status = rtk_usb_phy_notify_port_status;
+
+ if (!dev->of_node) {
+ dev_err(dev, "%s %d No device node\n", __func__, __LINE__);
+ goto err;
+ }
+
+ rtk_phy->usb_regs = syscon_regmap_lookup_by_phandle(dev->of_node, "realtek,usb");
+ if (IS_ERR(rtk_phy->usb_regs)) {
+ dev_info(dev, "%s: DTS no support usb regs syscon\n", __func__);
+ rtk_phy->usb_regs = NULL;
+ }
+
+ rtk_phy->mac_regs = syscon_regmap_lookup_by_phandle(dev->of_node, "realtek,mac");
+ if (IS_ERR(rtk_phy->mac_regs)) {
+ dev_info(dev, "%s: DTS no support mac regs syscon\n", __func__);
+ rtk_phy->mac_regs = NULL;
+ }
+
+ rtk_phy->usb_ctrl_regs = syscon_regmap_lookup_by_phandle(dev->of_node, "realtek,usb_ctrl");
+ if (IS_ERR(rtk_phy->usb_ctrl_regs)) {
+ dev_info(dev, "%s: DTS no support usb_ctrl regs syscon\n", __func__);
+ rtk_phy->usb_ctrl_regs = NULL;
+ }
+
+ ret = of_property_read_u32_index(dev->of_node, "realtek,port-index", 0,
+ &port_index);
+ if (ret)
+ port_index = -1;
+
+ ret = of_property_read_u32_index(dev->of_node, "realtek,phyN", 0,
+ &phyN);
+ if (ret) {
+ dev_err(dev, "%s get phyN fail (ret=%d)\n", __func__, ret);
+ goto err;
+ }
+
+ dev_dbg(dev, "%s port_index=%d phyN=%d\n", __func__, port_index, phyN);
+
+ rtk_phy->port_index = port_index;
+ rtk_phy->phyN = phyN;
+ rtk_phy->reg_addr = devm_kzalloc(dev,
+ sizeof(struct reg_addr) * phyN, GFP_KERNEL);
+ if (!rtk_phy->reg_addr)
+ return -ENOMEM;
+
+ rtk_phy->phy_data = devm_kzalloc(dev,
+ sizeof(struct phy_data) * phyN, GFP_KERNEL);
+ if (!rtk_phy->phy_data)
+ return -ENOMEM;
+
+ for (index = 0; index < phyN; index++) {
+ ret = __get_phy_parameter(rtk_phy, index);
+ if (ret) {
+ dev_err(dev, "%s: __get_phy_parameter fail ret=%d\n",
+ __func__, ret);
+ goto err;
+ }
+ }
+
+ platform_set_drvdata(pdev, rtk_phy);
+
+ generic_phy = devm_phy_create(rtk_phy->dev, NULL, &ops);
+ if (IS_ERR(generic_phy))
+ return PTR_ERR(generic_phy);
+
+ phy_set_drvdata(generic_phy, rtk_phy);
+
+ phy_provider = devm_of_phy_provider_register(rtk_phy->dev,
+ of_phy_simple_xlate);
+ if (IS_ERR(phy_provider))
+ return PTR_ERR(phy_provider);
+
+ ret = usb_add_phy_dev(&rtk_phy->phy);
+ if (ret)
+ goto err;
+
+ create_debug_files(rtk_phy);
+
+err:
+ dev_dbg(dev, "Probe RTK USB 2.0 PHY (ret=%d)\n", ret);
+
+ return ret;
+}
+
+static int rtk_usb2phy_remove(struct platform_device *pdev)
+{
+ struct rtk_usb_phy *rtk_phy = platform_get_drvdata(pdev);
+
+ remove_debug_files(rtk_phy);
+
+ usb_remove_phy(&rtk_phy->phy);
+
+ return 0;
+}
+
+static const struct of_device_id usbphy_rtk_dt_match[] = {
+ { .compatible = "realtek,usb2phy", },
+ { .compatible = "realtek,rtd-usb2phy", },
+ { .compatible = "realtek,rtd1295-usb2phy", },
+ { .compatible = "realtek,rtd1395-usb2phy", },
+ { .compatible = "realtek,rtd1619-usb2phy", },
+ { .compatible = "realtek,rtd1319-usb2phy", },
+ { .compatible = "realtek,rtd1619b-usb2phy", },
+ { .compatible = "realtek,rtd1312c-usb2phy", },
+ { .compatible = "realtek,rtd1319d-usb2phy", },
+ { .compatible = "realtek,rtd1315e-usb2phy", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, usbphy_rtk_dt_match);
+
+static struct platform_driver rtk_usb2phy_driver = {
+ .probe = rtk_usb2phy_probe,
+ .remove = rtk_usb2phy_remove,
+ .driver = {
+ .name = "rtk-usb2phy",
+ .owner = THIS_MODULE,
+ .of_match_table = usbphy_rtk_dt_match,
+ },
+};
+
+module_platform_driver(rtk_usb2phy_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform: rtk-usb2phy");
+MODULE_AUTHOR("Realtek Semiconductor Corporation");
+MODULE_DESCRIPTION("RTK usb 2.0 phy driver");
diff --git a/drivers/phy/realtek/phy-rtk-usb3.c b/drivers/phy/realtek/phy-rtk-usb3.c
new file mode 100644
index 000000000000..8bbc1ffd6977
--- /dev/null
+++ b/drivers/phy/realtek/phy-rtk-usb3.c
@@ -0,0 +1,1180 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * phy-rtk-usb3.c RTK usb3.0 phy driver
+ *
+ * copyright (c) 2023 realtek semiconductor corporation
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/regmap.h>
+#include <linux/sys_soc.h>
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/phy.h>
+
+#include "phy-rtk-usb.h"
+
+#define USB_MDIO_CTRL_PHY_BUSY BIT(7)
+#define USB_MDIO_CTRL_PHY_WRITE BIT(0)
+#define USB_MDIO_CTRL_PHY_ADDR_SHIFT 8
+#define USB_MDIO_CTRL_PHY_DATA_SHIFT 16
+
+#define PHY_ADDR_0x09 0x09
+#define PHY_ADDR_0x0B 0x0B
+#define PHY_ADDR_0x0D 0x0D
+#define PHY_ADDR_0x10 0x10
+#define PHY_ADDR_0x1F 0x1F
+#define PHY_ADDR_0x20 0x20
+#define PHY_ADDR_0x30 0x30
+
+#define REG_0x0B_RX_OFFSET_RANGE_MASK 0xC
+#define REG_0x0D_RX_DEBUG_TEST_EN BIT(6)
+#define REG_0x10_DEBUG_MODE_SETTING 0x3C0
+#define REG_0x10_DEBUG_MODE_SETTING_MASK 0x3F8
+#define REG_0x1F_RX_OFFSET_CODE_MASK 0x1E
+
+#define USB_U3_TX_LFPS_SWING_TRIM_SHIFT 4
+#define USB_U3_TX_LFPS_SWING_TRIM_MASK 0xF
+
+#define PHY_ADDR_MAP_ARRAY_INDEX(addr) (addr)
+#define ARRAY_INDEX_MAP_PHY_ADDR(index) (index)
+
+#define WRAP_CTR_reg 0x0
+#define PIPE3_POWER_DOWN_SEL BIT(10)
+
+struct reg_addr {
+ void __iomem *reg_mdio_ctl;
+};
+
+struct phy_parameter {
+ u8 addr;
+ u16 data;
+};
+
+struct phy_data {
+ int size;
+ struct phy_parameter *parameter;
+
+ bool check_efuse;
+ u8 efuse_usb_u3_tx_lfps_swing_trim;
+ bool do_toggle;
+ bool do_toggle_once;
+ bool use_default_parameter;
+ bool check_rx_front_end_offset;
+};
+
+static int rtk_usb_phy3_wait_vbusy(struct reg_addr *regAddr)
+{
+ return utmi_wait_register(regAddr->reg_mdio_ctl, USB_MDIO_CTRL_PHY_BUSY, 0);
+}
+
+static u16 rtk_usb_phy_read(struct reg_addr *regAddr, char addr)
+{
+ unsigned int regVal;
+ u32 value;
+
+ regVal = (addr << USB_MDIO_CTRL_PHY_ADDR_SHIFT);
+
+ writel(regVal, regAddr->reg_mdio_ctl);
+
+ rtk_usb_phy3_wait_vbusy(regAddr);
+
+ value = readl(regAddr->reg_mdio_ctl);
+ value = value >> USB_MDIO_CTRL_PHY_DATA_SHIFT;
+
+ return (u16)value;
+}
+
+static int rtk_usb_phy_write(struct reg_addr *regAddr, char addr, u16 data)
+{
+ unsigned int regVal;
+
+ regVal = USB_MDIO_CTRL_PHY_WRITE |
+ (addr << USB_MDIO_CTRL_PHY_ADDR_SHIFT) |
+ (data << USB_MDIO_CTRL_PHY_DATA_SHIFT);
+
+ writel(regVal, regAddr->reg_mdio_ctl);
+
+ rtk_usb_phy3_wait_vbusy(regAddr);
+
+ return 0;
+}
+
+static void do_rtk_usb3_phy_toggle(struct rtk_usb_phy *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];
+ struct phy_parameter *phy_parameter;
+ 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;
+
+ /* Disable usb3 phy suspend form usb wrapper */
+ if (rtk_phy->usb_regs)
+ regmap_update_bits(rtk_phy->usb_regs, WRAP_CTR_reg,
+ (unsigned int)PIPE3_POWER_DOWN_SEL,
+ (unsigned int)PIPE3_POWER_DOWN_SEL);
+
+ phy_parameter = phy_data->parameter;
+
+ index = PHY_ADDR_MAP_ARRAY_INDEX(PHY_ADDR_0x09);
+
+ if (index < phy_data->size) {
+ u8 addr = (phy_parameter + index)->addr;
+ u16 data = (phy_parameter + index)->data;
+
+ if (addr == 0xFF) {
+ addr = ARRAY_INDEX_MAP_PHY_ADDR(index);
+ data = rtk_usb_phy_read(regAddr, addr);
+ (phy_parameter + index)->addr = addr;
+ (phy_parameter + index)->data = data;
+ }
+ mdelay(1);
+ dev_info(rtk_phy->dev,
+ "%s ########## to toggle PHY addr 0x09 BIT(9)\n",
+ __func__);
+ rtk_usb_phy_write(regAddr, addr, data&(~BIT(9)));
+ mdelay(1);
+ rtk_usb_phy_write(regAddr, addr, data);
+ }
+ dev_info(rtk_phy->dev, "%s ########## PHY addr 0x1f = 0x%04x\n",
+ __func__, rtk_usb_phy_read(regAddr, PHY_ADDR_0x1F));
+
+ /* Enable usb3 phy suspend form usb wrapper, power control by mac */
+ if (rtk_phy->usb_regs)
+ regmap_update_bits(rtk_phy->usb_regs, WRAP_CTR_reg,
+ (unsigned int)PIPE3_POWER_DOWN_SEL,
+ (unsigned int)(~PIPE3_POWER_DOWN_SEL));
+}
+
+static int do_rtk_usb_phy_init(struct rtk_usb_phy *rtk_phy, int phy_index)
+{
+ 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;
+ struct phy_parameter *phy_parameter = phy_data->parameter;
+
+ dev_dbg(rtk_phy->dev, "%s: init phy#%d\n", __func__, phy_index);
+
+ if (phy_data->use_default_parameter) {
+ dev_info(rtk_phy->dev, "%s phy#%d use default parameter\n",
+ __func__, phy_index);
+ goto do_toggle;
+ }
+
+ for (index = 0; index < phy_data->size; index++) {
+ u8 addr = (phy_parameter + index)->addr;
+ u16 data = (phy_parameter + index)->data;
+
+ if (addr == 0xFF)
+ continue;
+
+ if (addr == PHY_ADDR_0x20) {
+ u8 efuse_val = phy_data->efuse_usb_u3_tx_lfps_swing_trim;
+ u16 val_mask = USB_U3_TX_LFPS_SWING_TRIM_MASK;
+ int val_shift = USB_U3_TX_LFPS_SWING_TRIM_SHIFT;
+
+ if (efuse_val) {
+ data &= ~(val_mask << val_shift);
+ data |= ((efuse_val & val_mask) << val_shift);
+ }
+ }
+
+ rtk_usb_phy_write(regAddr, addr, data);
+ }
+
+ for (index = 0; index < phy_data->size; index++) {
+ u8 addr = (phy_parameter + index)->addr;
+ u16 data = (phy_parameter + index)->data;
+
+ if (addr == 0xFF)
+ continue;
+
+ dev_dbg(rtk_phy->dev, "[USB3_PHY], addr = 0x%02x, data = 0x%04x ==> read value = 0x%04x\n",
+ addr, data,
+ rtk_usb_phy_read(regAddr, addr));
+ }
+
+do_toggle:
+ if (phy_data->do_toggle_once)
+ phy_data->do_toggle = true;
+
+ do_rtk_usb3_phy_toggle(rtk_phy, phy_index, false);
+
+ if (phy_data->do_toggle_once) {
+ u16 check_value = 0;
+ int count = 10;
+ u16 value_0x0D, value_0x10;
+
+ /* Enable Debug mode by set 0x0D and 0x10 */
+ value_0x0D = rtk_usb_phy_read(regAddr, PHY_ADDR_0x0D);
+ value_0x10 = rtk_usb_phy_read(regAddr, PHY_ADDR_0x10);
+
+ rtk_usb_phy_write(regAddr, PHY_ADDR_0x0D,
+ value_0x0D | REG_0x0D_RX_DEBUG_TEST_EN);
+ rtk_usb_phy_write(regAddr, PHY_ADDR_0x10,
+ (value_0x10 & ~REG_0x10_DEBUG_MODE_SETTING_MASK) |
+ REG_0x10_DEBUG_MODE_SETTING);
+
+ check_value = rtk_usb_phy_read(regAddr, PHY_ADDR_0x30);
+
+ while (!(check_value & BIT(15))) {
+ check_value = rtk_usb_phy_read(regAddr, PHY_ADDR_0x30);
+ mdelay(1);
+ if (count-- < 0)
+ break;
+ }
+
+ if (!(check_value & BIT(15)))
+ dev_info(rtk_phy->dev, "toggle fail addr=0x%02x, data=0x%04x\n",
+ PHY_ADDR_0x30, check_value);
+ else
+ dev_info(rtk_phy->dev, "toggle okay addr=0x%02x, data=0x%04x\n",
+ PHY_ADDR_0x30, check_value);
+
+ /* Disable Debug mode by set 0x0D and 0x10 to default*/
+ rtk_usb_phy_write(regAddr, PHY_ADDR_0x0D, value_0x0D);
+ rtk_usb_phy_write(regAddr, PHY_ADDR_0x10, value_0x10);
+
+ phy_data->do_toggle = false;
+ }
+
+
+ if (phy_data->check_rx_front_end_offset) {
+ u16 rx_offset_code, rx_offset_range;
+ bool do_update = false;
+
+ rx_offset_code = rtk_usb_phy_read(regAddr, PHY_ADDR_0x1F);
+ if (((rx_offset_code & REG_0x1F_RX_OFFSET_CODE_MASK) == 0x0) ||
+ ((rx_offset_code & REG_0x1F_RX_OFFSET_CODE_MASK) ==
+ REG_0x1F_RX_OFFSET_CODE_MASK))
+ do_update = true;
+
+ rx_offset_range = rtk_usb_phy_read(regAddr, PHY_ADDR_0x0B);
+ if (((rx_offset_range & REG_0x0B_RX_OFFSET_RANGE_MASK) ==
+ REG_0x0B_RX_OFFSET_RANGE_MASK) && do_update) {
+ dev_warn(rtk_phy->dev, "Don't update rx_offset_range (rx_offset_code=0x%x, rx_offset_range=0x%x)\n",
+ rx_offset_code, rx_offset_range);
+ do_update = false;
+ }
+
+ if (do_update) {
+ u16 tmp1, tmp2;
+
+ tmp1 = rx_offset_range & (~REG_0x0B_RX_OFFSET_RANGE_MASK);
+ tmp2 = rx_offset_range & REG_0x0B_RX_OFFSET_RANGE_MASK;
+ tmp2 += (1 << 2);
+ rx_offset_range = tmp1 | (tmp2 & REG_0x0B_RX_OFFSET_RANGE_MASK);
+ rtk_usb_phy_write(regAddr, PHY_ADDR_0x0B, rx_offset_range);
+ goto do_toggle;
+ }
+ }
+
+ return 0;
+}
+
+static int rtk_usb_phy_init(struct phy *phy)
+{
+ struct rtk_usb_phy *rtk_phy = phy_get_drvdata(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 -ENODEV;
+ }
+
+ dev_dbg(rtk_phy->dev, "Init RTK USB 3.0 PHY\n");
+ for (i = 0; i < rtk_phy->phyN; i++)
+ ret = do_rtk_usb_phy_init(rtk_phy, i);
+
+ dev_info(rtk_phy->dev, "Initialized RTK USB 3.0 PHY (take %dms)\n",
+ jiffies_to_msecs(jiffies - phy_init_time));
+ return ret;
+}
+
+static int rtk_usb_phy_exit(struct phy *phy)
+{
+ struct rtk_usb_phy *rtk_phy = phy_get_drvdata(phy);
+
+ dev_dbg(rtk_phy->dev, "Exit RTK USB 3.0 PHY\n");
+
+ return 0;
+}
+
+static const struct phy_ops ops = {
+ .init = rtk_usb_phy_init,
+ .exit = rtk_usb_phy_exit,
+ .owner = THIS_MODULE,
+};
+
+static void rtk_usb_phy_toggle(struct usb_phy *usb3_phy, bool isConnect, int port)
+{
+ int index = port;
+ struct rtk_usb_phy *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);
+}
+
+static int rtk_usb_phy_notify_port_status(struct usb_phy *x, int port,
+ u16 portstatus, u16 portchange)
+{
+ bool isConnect = false;
+
+ pr_debug("%s port=%d portstatus=0x%x portchange=0x%x\n",
+ __func__, port, (int)portstatus, (int)portchange);
+ if (portstatus & USB_PORT_STAT_CONNECTION)
+ isConnect = true;
+
+ if (portchange & USB_PORT_STAT_C_CONNECTION)
+ rtk_usb_phy_toggle(x, isConnect, port);
+
+ return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+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_debug("%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 *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];
+ struct phy_parameter *phy_parameter;
+
+ phy_parameter = phy_data->parameter;
+
+ seq_printf(s, "[USB3_PHY] PHY %d\n", i);
+
+ for (index = 0; index < phy_data->size; index++) {
+ u8 addr = ARRAY_INDEX_MAP_PHY_ADDR(index);
+ u16 data = (phy_parameter + index)->data;
+
+ if ((phy_parameter + index)->addr == 0xFF)
+ seq_printf(s, "[USB3_PHY], addr = 0x%02x, data = none ==> read value = 0x%04x\n",
+ addr,
+ rtk_usb_phy_read(regAddr, addr));
+ else
+ seq_printf(s, "[USB3_PHY], addr = 0x%02x, data = 0x%04x ==> read value = 0x%04x\n",
+ addr, data,
+ rtk_usb_phy_read(regAddr, addr));
+ }
+
+ seq_puts(s, "Property:\n");
+ seq_printf(s, "check_efuse: %s\n",
+ phy_data->check_efuse?"Enable":"Disable");
+ seq_printf(s, "efuse_usb_u3_tx_lfps_swing_trim: 0x%x\n",
+ (int)phy_data->efuse_usb_u3_tx_lfps_swing_trim);
+ seq_printf(s, "do_toggle: %s\n",
+ phy_data->do_toggle?"Enable":"Disable");
+ seq_printf(s, "do_toggle_once: %s\n",
+ phy_data->do_toggle_once?"Enable":"Disable");
+ seq_printf(s, "use_default_parameter: %s\n",
+ phy_data->use_default_parameter?"Enable":"Disable");
+ }
+ 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 __get_parameter_at_page(struct seq_file *s,
+ struct rtk_usb_phy *rtk_phy,
+ struct phy_parameter *phy_parameter_array,
+ const char *phy_addr)
+{
+ struct phy_parameter *phy_parameter;
+ uint32_t addr;
+ int i, ret;
+
+ ret = kstrtouint(phy_addr, 16, &addr);
+ if (ret < 0) {
+ pr_err("%s::kstrtouint() failed\n", __func__);
+ return -EINVAL;
+ }
+
+ i = PHY_ADDR_MAP_ARRAY_INDEX(addr);
+ phy_parameter = (phy_parameter_array + i);
+
+ if (phy_parameter->addr != 0xFF)
+ seq_printf(s, "Now Parameter addr 0x%02x = 0x%04x\n",
+ phy_parameter->addr, phy_parameter->data);
+ else
+ seq_printf(s, "Now Parameter addr 0x%02x is default\n",
+ addr);
+
+ dev_dbg(rtk_phy->dev, "%s addr=0x%02x data=0x%04x\n",
+ __func__, phy_parameter->addr, phy_parameter->data);
+
+ return 0;
+}
+
+static int __set_parameter_at_page(
+ struct rtk_usb_phy *rtk_phy,
+ struct reg_addr *regAddr, struct phy_data *phy_data,
+ struct phy_parameter *phy_parameter_array,
+ const char *phy_addr, const char *phy_value)
+{
+ struct phy_parameter *phy_parameter;
+ uint32_t addr, value;
+ int i, ret;
+
+ ret = kstrtouint(phy_addr, 16, &addr);
+ if (ret < 0) {
+ pr_err("%s::kstrtouint() failed\n", __func__);
+ return -EINVAL;
+ }
+ ret = kstrtouint(phy_value, 16, &value);
+ if (ret < 0) {
+ pr_err("%s::kstrtouint() failed\n", __func__);
+ return -EINVAL;
+ }
+
+ i = PHY_ADDR_MAP_ARRAY_INDEX(addr);
+ phy_parameter = (phy_parameter_array + i);
+
+ if (phy_parameter->addr != 0xFF) {
+ phy_parameter->data = value;
+ } else {
+ phy_parameter->addr = addr;
+ phy_parameter->data = value;
+ }
+
+ dev_info(rtk_phy->dev, "%s addr=0x%02x data=0x%04x\n",
+ __func__, phy_parameter->addr, phy_parameter->data);
+
+ if (addr == PHY_ADDR_0x20)
+ dev_info(rtk_phy->dev,
+ "%s PHY_ADDR_0x20 NOT use efuse u3_tx_lfps_swing_trim value\n",
+ __func__);
+
+ if (rtk_usb_phy_write(regAddr, addr, value))
+ dev_err(rtk_phy->dev,
+ "[%s:%d] Error: addr=0x%02x value=0x%04x\n",
+ __func__, __LINE__, addr, value);
+
+ return 0;
+}
+
+static int rtk_usb3_set_parameter_show(struct seq_file *s, void *unused)
+{
+ struct rtk_usb_phy *rtk_phy = s->private;
+ const struct file *file = s->file;
+ const char *file_name = file_dentry(file)->d_iname;
+ struct dentry *p_dentry = file_dentry(file)->d_parent;
+ const char *phy_dir_name = p_dentry->d_iname;
+ int ret, index;
+ struct phy_data *phy_data = NULL;
+
+ for (index = 0; index < rtk_phy->phyN; index++) {
+ size_t sz = 30;
+ char name[30] = {0};
+
+ snprintf(name, sz, "phy%d", index);
+ if (strncmp(name, phy_dir_name, strlen(name)) == 0) {
+ phy_data = &((struct phy_data *)rtk_phy->phy_data)[index];
+ break;
+ }
+ }
+ if (!phy_data) {
+ dev_err(rtk_phy->dev,
+ "%s: No phy_data for %s/%s\n",
+ __func__, phy_dir_name, file_name);
+ return -EINVAL;
+ }
+
+ ret = __get_parameter_at_page(s, rtk_phy, phy_data->parameter, file_name);
+ if (ret < 0)
+ return ret;
+
+ seq_puts(s, "Set phy parameter by following command\n");
+ seq_printf(s, "echo \"value\" > %s/%s\n",
+ phy_dir_name, file_name);
+
+ 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)
+{
+ const char *file_name = file_dentry(file)->d_iname;
+ struct dentry *p_dentry = file_dentry(file)->d_parent;
+ const char *phy_dir_name = p_dentry->d_iname;
+ struct seq_file *s = file->private_data;
+ struct rtk_usb_phy *rtk_phy = s->private;
+ struct reg_addr *regAddr = NULL;
+ struct phy_data *phy_data = NULL;
+ int ret = 0;
+ char buffer[40] = {0};
+ int index;
+
+ if (copy_from_user(&buffer, ubuf,
+ min_t(size_t, sizeof(buffer) - 1, count)))
+ return -EFAULT;
+
+ for (index = 0; index < rtk_phy->phyN; index++) {
+ size_t sz = 30;
+ char name[30] = {0};
+
+ snprintf(name, sz, "phy%d", index);
+ if (strncmp(name, phy_dir_name, strlen(name)) == 0) {
+ regAddr = &((struct reg_addr *)rtk_phy->reg_addr)[index];
+ phy_data = &((struct phy_data *)rtk_phy->phy_data)[index];
+ break;
+ }
+ }
+ if (!regAddr) {
+ dev_err(rtk_phy->dev,
+ "%s: No regAddr for %s/%s\n",
+ __func__, phy_dir_name, file_name);
+ return -EINVAL;
+ }
+ if (!phy_data) {
+ dev_err(rtk_phy->dev,
+ "%s: No phy_data for %s/%s\n",
+ __func__, phy_dir_name, file_name);
+ return -EINVAL;
+ }
+
+ ret = __set_parameter_at_page(rtk_phy, regAddr, phy_data,
+ phy_data->parameter, file_name, buffer);
+ if (ret < 0)
+ return ret;
+
+ 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 *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 *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 int create_debug_set_parameter_files(struct rtk_usb_phy *rtk_phy,
+ struct dentry *phy_dir, size_t addr_size)
+{
+ int i;
+
+ for (i = 0; i < addr_size; i++) {
+ size_t sz = 30;
+ char name[30] = {0};
+
+ snprintf(name, sz, "%02x", ARRAY_INDEX_MAP_PHY_ADDR(i));
+
+ if (!debugfs_create_file(name, 0644,
+ phy_dir, rtk_phy,
+ &rtk_usb3_set_parameter_fops))
+ dev_err(rtk_phy->dev,
+ "%s Error create file %s fail",
+ name, __func__);
+ }
+
+ return 0;
+}
+
+static inline void create_debug_files(struct rtk_usb_phy *rtk_phy)
+{
+ struct dentry *phy_debug_root = NULL;
+ struct dentry *set_parameter_dir = NULL;
+
+ 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;
+
+ set_parameter_dir = debugfs_create_dir("set_parameter",
+ rtk_phy->debug_dir);
+ if (set_parameter_dir) {
+ int index, ret;
+
+ for (index = 0; index < rtk_phy->phyN; index++) {
+ struct dentry *phy_dir;
+ struct phy_data *phy_data;
+ size_t sz = 30;
+ char name[30] = {0};
+
+ snprintf(name, sz, "phy%d", index);
+
+ phy_data = &((struct phy_data *)rtk_phy->phy_data)[index];
+
+ phy_dir = debugfs_create_dir(name, set_parameter_dir);
+ if (!phy_dir) {
+ dev_err(rtk_phy->dev,
+ "%s Error create folder %s fail\n",
+ name, __func__);
+ goto file_error;
+ }
+
+ ret = create_debug_set_parameter_files(rtk_phy, phy_dir,
+ phy_data->size);
+ if (ret < 0) {
+ dev_err(rtk_phy->dev,
+ "%s Error create files fail\n",
+ __func__);
+ 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);
+}
+
+static inline void remove_debug_files(struct rtk_usb_phy *rtk_phy)
+{
+ debugfs_remove_recursive(rtk_phy->debug_dir);
+}
+#else
+static inline void create_debug_files(struct rtk_usb_phy *rtk_phy) { }
+static inline void remove_debug_files(struct rtk_usb_phy *rtk_phy) { }
+#endif /* CONFIG_DEBUG_FS */
+
+static int __get_phy_parameter_by_efuse(struct rtk_usb_phy *rtk_phy,
+ struct phy_data *phy_data, int index)
+{
+ u8 value = 0;
+ struct nvmem_cell *cell;
+
+ if (!phy_data->check_efuse)
+ goto out;
+
+ cell = nvmem_cell_get(rtk_phy->dev, "usb_u3_tx_lfps_swing_trim");
+ if (IS_ERR(cell)) {
+ dev_warn(rtk_phy->dev,
+ "%s failed to get usb_u3_tx_lfps_swing_trim: %ld\n",
+ __func__, PTR_ERR(cell));
+ } else {
+ unsigned char *buf;
+ size_t buf_size;
+
+ buf = nvmem_cell_read(cell, &buf_size);
+
+ value = buf[0] & USB_U3_TX_LFPS_SWING_TRIM_MASK;
+
+ dev_dbg(rtk_phy->dev,
+ "phy index=%d buf=0x%x buf_size=%d value=0x%x\n",
+ index, buf[0], (int)buf_size, value);
+ kfree(buf);
+ nvmem_cell_put(cell);
+ }
+
+ if ((value > 0) && (value < 0x8))
+ phy_data->efuse_usb_u3_tx_lfps_swing_trim = 0x8;
+ else
+ phy_data->efuse_usb_u3_tx_lfps_swing_trim = (u8)value;
+
+ dev_dbg(rtk_phy->dev, "Get Efuse usb_u3_tx_lfps_swing_trim=0x%x (value=0x%x)\n",
+ phy_data->efuse_usb_u3_tx_lfps_swing_trim, value);
+
+out:
+ return 0;
+}
+
+static int __get_phy_parameter_v1(struct device *dev, struct phy_data *phy_data,
+ struct device_node *sub_node)
+{
+ struct phy_parameter *phy_parameter;
+ int revision, i, ret = 0;
+ u8 *addr;
+ u16 *data;
+
+ ret = of_property_read_u32_index(sub_node, "realtek,phy-data-size", 0,
+ &phy_data->size);
+ if (ret)
+ goto out;
+
+ phy_data->parameter = devm_kzalloc(dev,
+ sizeof(struct phy_parameter) * phy_data->size,
+ GFP_KERNEL);
+ if (!phy_data->parameter)
+ return -ENOMEM;
+
+ addr = kcalloc(phy_data->size, sizeof(u8), GFP_KERNEL);
+ if (!addr) {
+ kfree(phy_data->parameter);
+ phy_data->parameter = NULL;
+ return -ENOMEM;
+ }
+
+ data = kcalloc(phy_data->size, sizeof(u16), GFP_KERNEL);
+ if (!data) {
+ kfree(phy_data->parameter);
+ kfree(addr);
+ phy_data->parameter = NULL;
+ return -ENOMEM;
+ }
+ ret = of_property_read_u8_array(sub_node, "realtek,phy-data-addr",
+ addr, phy_data->size);
+ if (ret)
+ goto out;
+
+ revision = __get_chip_revision();
+ dev_dbg(dev, "%s: Chip revision is %x\n", __func__, revision);
+
+ while (revision >= DEFAULT_CHIP_REVISION) {
+ char phy_data_revision[32] = {0};
+
+ snprintf(phy_data_revision, 21, "realtek,phy-data-%X",
+ revision);
+ ret = of_property_read_u16_array(sub_node,
+ phy_data_revision,
+ 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,
+ "realtek,phy-data-revA",
+ data, phy_data->size);
+ if (ret)
+ goto out;
+ else
+ dev_info(dev, "%s load parameter\n", __func__);
+ }
+
+ phy_parameter = phy_data->parameter;
+
+ for (i = 0; i < phy_data->size; i++) {
+ (phy_parameter + i)->addr = *(addr + i);
+ (phy_parameter + i)->data = *(data + i);
+ dev_dbg(dev, "%s i=%d addr=0x%x data=0x%x\n",
+ __func__, i, (phy_parameter + i)->addr,
+ (phy_parameter + i)->data);
+ }
+
+out:
+ kfree(addr);
+ kfree(data);
+
+ return ret;
+}
+
+static int __get_phy_parameter_v2(struct device *dev, struct phy_data *phy_data,
+ struct device_node *sub_node)
+{
+ struct phy_parameter *phy_parameter;
+ int revision, i, ret = 0;
+ int data_size, num_cells = 2;
+ char phy_data_revision[32] = {0};
+
+ ret = of_property_read_u32_index(sub_node, "realtek,phy-data-size", 0,
+ &phy_data->size);
+ if (ret)
+ goto out;
+
+ phy_data->parameter = devm_kzalloc(dev,
+ sizeof(struct phy_parameter) * phy_data->size,
+ GFP_KERNEL);
+ if (!phy_data->parameter)
+ return -ENOMEM;
+
+ revision = __get_chip_revision();
+ dev_dbg(dev, "%s: Chip revision is %x\n", __func__, revision);
+
+ while (revision >= DEFAULT_CHIP_REVISION) {
+ snprintf(phy_data_revision, 21, "realtek,phy-data-%X",
+ revision);
+ if (of_get_property(sub_node, phy_data_revision, &data_size)) {
+ dev_dbg(dev, "%s load %s parameter (data_size=%d)\n",
+ __func__, phy_data_revision, data_size);
+ break;
+ }
+ revision--;
+ if ((revision & 0xFF) > 0xF)
+ revision = (revision & 0xF00) + 0xF;
+
+ data_size = 0;
+ ret = 0;
+ }
+
+ phy_parameter = phy_data->parameter;
+ for (i = 0; i < phy_data->size; i++)
+ (phy_parameter + i)->addr = 0xFF;
+
+ data_size = data_size / (sizeof(u32) * num_cells);
+ for (i = 0; i < data_size; i++) {
+ struct phy_parameter *parameter;
+ u32 addr, data;
+ int offset, index;
+
+ offset = i * num_cells;
+
+ ret = of_property_read_u32_index(sub_node, phy_data_revision,
+ offset, &addr);
+ if (ret) {
+ dev_err(dev, "ERROR: To get %s i=%d addr=0x%x\n",
+ phy_data_revision, i, addr);
+ break;
+ }
+
+ ret = of_property_read_u32_index(sub_node, phy_data_revision,
+ offset + 1, &data);
+ if (ret) {
+ dev_err(dev, "ERROR: To get %s i=%d addr=0x%x\n",
+ phy_data_revision, i, data);
+ break;
+ }
+
+ index = PHY_ADDR_MAP_ARRAY_INDEX(addr);
+ parameter = (phy_parameter + index);
+ parameter->addr = (u8)addr;
+ parameter->data = (u16)data;
+
+ dev_dbg(dev, "%s index=%d addr=0x%x data=0x%x\n",
+ phy_data_revision, index,
+ parameter->addr, parameter->data);
+ }
+
+out:
+ return ret;
+}
+
+static int rtk_usb3phy_probe(struct platform_device *pdev)
+{
+ struct rtk_usb_phy *rtk_phy;
+ struct device *dev = &pdev->dev;
+ struct phy *generic_phy;
+ struct phy_provider *phy_provider;
+ int i, ret, phyN;
+
+ rtk_phy = devm_kzalloc(dev, sizeof(*rtk_phy), GFP_KERNEL);
+ if (!rtk_phy)
+ return -ENOMEM;
+
+ rtk_phy->dev = &pdev->dev;
+ rtk_phy->phy.dev = rtk_phy->dev;
+ rtk_phy->phy.label = "rtk-usb3phy";
+ rtk_phy->phy.notify_port_status = rtk_usb_phy_notify_port_status;
+
+ if (!dev->of_node) {
+ dev_err(dev, "%s %d No device node\n", __func__, __LINE__);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ rtk_phy->usb_regs = syscon_regmap_lookup_by_phandle(dev->of_node, "realtek,usb");
+ if (IS_ERR(rtk_phy->usb_regs)) {
+ dev_info(dev, "%s: DTS no support usb regs syscon\n", __func__);
+ rtk_phy->usb_regs = NULL;
+ }
+
+ rtk_phy->mac_regs = syscon_regmap_lookup_by_phandle(dev->of_node, "realtek,mac");
+ if (IS_ERR(rtk_phy->mac_regs)) {
+ dev_info(dev, "%s: DTS no support mac regs syscon\n", __func__);
+ rtk_phy->mac_regs = NULL;
+ }
+
+ rtk_phy->usb_ctrl_regs = syscon_regmap_lookup_by_phandle(dev->of_node, "realtek,usb_ctrl");
+ if (IS_ERR(rtk_phy->usb_ctrl_regs)) {
+ dev_info(dev, "%s: DTS no support usb_ctrl regs syscon\n", __func__);
+ rtk_phy->usb_ctrl_regs = NULL;
+ }
+
+ ret = of_property_read_u32_index(dev->of_node, "realtek,phyN", 0,
+ &phyN);
+ if (ret)
+ goto err;
+
+ rtk_phy->phyN = phyN;
+
+ rtk_phy->reg_addr = devm_kzalloc(dev,
+ sizeof(struct reg_addr) * phyN, GFP_KERNEL);
+ if (!rtk_phy->reg_addr)
+ return -ENOMEM;
+
+ rtk_phy->phy_data = devm_kzalloc(dev,
+ sizeof(struct phy_data) * phyN, GFP_KERNEL);
+ if (!rtk_phy->phy_data)
+ return -ENOMEM;
+
+ for (i = 0; i < phyN; i++) {
+ struct reg_addr *addr =
+ &((struct reg_addr *)rtk_phy->reg_addr)[i];
+ struct phy_data *phy_data =
+ &((struct phy_data *)rtk_phy->phy_data)[i];
+
+ char phy_name[5], phy_name_v2[10];
+ struct device_node *sub_node;
+
+ 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_dbg(dev, "%s %d: #%d Get phy data v1 sub_node for %s\n",
+ __func__, __LINE__, i, phy_name);
+ ret = __get_phy_parameter_v1(dev, phy_data, sub_node);
+ if (ret)
+ goto err;
+ } else {
+ snprintf(phy_name_v2, 10, "phy%d_data", i);
+ sub_node = of_get_child_by_name(dev->of_node, phy_name_v2);
+ if (sub_node) {
+ dev_dbg(dev, "%s %d: #%d Get phy data v2 sub_node for %s\n",
+ __func__, __LINE__, i, phy_name_v2);
+ ret = __get_phy_parameter_v2(dev, phy_data, sub_node);
+ if (ret)
+ goto err;
+ }
+ }
+
+ if (!sub_node) {
+ dev_err(dev, "%s %d No device sub node for %s\n",
+ __func__, __LINE__, phy_name);
+ goto err;
+ }
+
+ if (of_property_read_bool(sub_node, "realtek,do-toggle-once"))
+ phy_data->do_toggle_once = true;
+ else
+ phy_data->do_toggle_once = false;
+
+ if (of_property_read_bool(sub_node, "realtek,do-toggle"))
+ phy_data->do_toggle = true;
+ else
+ phy_data->do_toggle = false;
+
+ if (of_property_read_bool(sub_node, "realtek,use-default-parameter"))
+ phy_data->use_default_parameter = true;
+ else
+ phy_data->use_default_parameter = false;
+
+ if (of_property_read_bool(sub_node, "realtek,check-rx-front-end-offset"))
+ phy_data->check_rx_front_end_offset = true;
+ else
+ phy_data->check_rx_front_end_offset = false;
+
+ if (of_property_read_bool(sub_node, "realtek,check-efuse"))
+ phy_data->check_efuse = true;
+ else
+ phy_data->check_efuse = false;
+
+ __get_phy_parameter_by_efuse(rtk_phy, phy_data, i);
+ }
+
+ platform_set_drvdata(pdev, rtk_phy);
+
+ generic_phy = devm_phy_create(rtk_phy->dev, NULL, &ops);
+ if (IS_ERR(generic_phy))
+ return PTR_ERR(generic_phy);
+
+ phy_set_drvdata(generic_phy, rtk_phy);
+
+ phy_provider = devm_of_phy_provider_register(rtk_phy->dev,
+ of_phy_simple_xlate);
+ if (IS_ERR(phy_provider))
+ return PTR_ERR(phy_provider);
+
+ ret = usb_add_phy_dev(&rtk_phy->phy);
+ if (ret)
+ goto err;
+
+ create_debug_files(rtk_phy);
+
+err:
+ dev_dbg(&pdev->dev, "Probe RTK USB 3.0 PHY (ret=%d)\n", ret);
+
+ return ret;
+}
+
+static int rtk_usb3phy_remove(struct platform_device *pdev)
+{
+ struct rtk_usb_phy *rtk_phy = platform_get_drvdata(pdev);
+
+ remove_debug_files(rtk_phy);
+
+ usb_remove_phy(&rtk_phy->phy);
+
+ return 0;
+}
+
+static const struct of_device_id usbphy_rtk_dt_match[] = {
+ { .compatible = "realtek,usb3phy", },
+ { .compatible = "realtek,rtd-usb3phy", },
+ { .compatible = "realtek,rtd1295-usb3phy", },
+ { .compatible = "realtek,rtd1619-usb3phy", },
+ { .compatible = "realtek,rtd1319-usb3phy", },
+ { .compatible = "realtek,rtd1619b-usb3phy", },
+ { .compatible = "realtek,rtd1319d-usb3phy", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, usbphy_rtk_dt_match);
+
+static struct platform_driver rtk_usb3phy_driver = {
+ .probe = rtk_usb3phy_probe,
+ .remove = rtk_usb3phy_remove,
+ .driver = {
+ .name = "rtk-usb3phy",
+ .owner = THIS_MODULE,
+ .of_match_table = usbphy_rtk_dt_match,
+ },
+};
+
+module_platform_driver(rtk_usb3phy_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform: rtk-usb3phy");
+MODULE_AUTHOR("Realtek Semiconductor Corporation");
+MODULE_DESCRIPTION("RTK usb 3.0 phy driver");
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 3/3] dt-bindings: phy: realtek: Add the doc about the Realtek SoC USB 2.0/3.0 PHY
2023-05-25 2:26 [PATCH v2 1/3] usb: phy: add usb phy notify port status API Stanley Chang
2023-05-25 2:26 ` [PATCH v2 2/3] phy: realtek: usb: Add driver for the Realtek SoC USB 2.0/3.0 PHY Stanley Chang
@ 2023-05-25 2:26 ` Stanley Chang
2023-05-29 18:59 ` Conor Dooley
[not found] ` <0b2143ca-ead7-c8fa-2e80-a94222af51ca@linaro.org>
2023-05-29 14:27 ` [PATCH v2 1/3] usb: phy: add usb phy notify port status API Greg Kroah-Hartman
2 siblings, 2 replies; 21+ messages in thread
From: Stanley Chang @ 2023-05-25 2:26 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Stanley Chang, Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Alan Stern, Flavio Suligoi,
Mathias Nyman, Douglas Anderson, Matthias Kaehlcke, Ray Chi,
Michael Grzeschik, linux-phy, devicetree, linux-kernel, linux-usb
Two documentations explain the property about realtek USB PHY drivers.
Realtek DHC (digital home center) RTD SoCs support DWC3 XHCI USB 2.0/3.0
controller. Added two drivers to drive the USB 2.0/3.0 PHY transceivers.
For USB 3.0 transceivers, a driver phy-rtk-usb3 is provided.
The driver phy-rtk-usb2 is used to support USB 2.0 transceivers.
Signed-off-by: Stanley Chang <stanley_chang@realtek.com>
---
v1 to v2 change:
Add phy-cells for generic phy driver
---
.../bindings/phy/realtek,usb2phy.yaml | 255 ++++++++++++++++++
.../bindings/phy/realtek,usb3phy.yaml | 201 ++++++++++++++
2 files changed, 456 insertions(+)
create mode 100644 Documentation/devicetree/bindings/phy/realtek,usb2phy.yaml
create mode 100644 Documentation/devicetree/bindings/phy/realtek,usb3phy.yaml
diff --git a/Documentation/devicetree/bindings/phy/realtek,usb2phy.yaml b/Documentation/devicetree/bindings/phy/realtek,usb2phy.yaml
new file mode 100644
index 000000000000..a2a69da0a163
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/realtek,usb2phy.yaml
@@ -0,0 +1,255 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright 2023 Realtek Semiconductor Corporation
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/realtek,usb2phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek DHC SoCs USB2 PHY
+
+maintainers:
+ - Stanley Chang <stanley_chang@realtek.com>
+
+description: |
+ Realtek USB 2.0 PHY support the digital home center (DHC) RTD series SoCs.
+
+properties:
+ compatible:
+ enum:
+ - realtek,usb2phy
+ - realtek,rtd-usb2phy
+ - realtek,rtd1295-usb2phy
+ - realtek,rtd1395-usb2phy
+ - realtek,rtd1619-usb2phy
+ - realtek,rtd1319-usb2phy
+ - realtek,rtd1619b-usb2phy
+ - realtek,rtd1312c-usb2phy
+ - realtek,rtd1319d-usb2phy
+ - realtek,rtd1315e-usb2phy
+
+ reg:
+ items:
+ - description: PHY data registers
+ - description: PHY control registers
+
+ "#phy-cells":
+ const: 0
+
+ realtek,usb:
+ description: The phandler of realtek dwc3 node
+ $ref: /schemas/types.yaml#/definitions/phandle
+
+ realtek,mac:
+ description: The phandler of dwc3 node
+ $ref: /schemas/types.yaml#/definitions/phandle
+
+ realtek,usb_ctrl:
+ description: The phandler of usb power control node
+ $ref: /schemas/types.yaml#/definitions/phandle
+
+ realtek,port-index:
+ description: The index of USB 2.0 PHY
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ realtek,phyN:
+ description: The total amount of USB 2.0 PHY
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ phy0:
+ description: The child node of PHY for the parameter v1.
+ type: object
+ properties:
+ realtek,phy-data-page0-size:
+ description: PHY data page 0 size
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ realtek,phy-data-page0-addr:
+ description: PHY data page 0 address
+ $ref: /schemas/types.yaml#/definitions/uint8-array
+
+ realtek,phy-data-page0-A00:
+ description: PHY data page 0 value
+ $ref: /schemas/types.yaml#/definitions/uint8-array
+
+ realtek,phy-data-page1-size:
+ description: PHY data page 1 size
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ realtek,phy-data-page1-addr:
+ description: PHY data page 1 address
+ $ref: /schemas/types.yaml#/definitions/uint8-array
+
+ realtek,phy-data-page1-A00:
+ description: PHY data page 1 value
+ $ref: /schemas/types.yaml#/definitions/uint8-array
+
+ realtek,phy-data-page2-size:
+ description: PHY data page 2 size
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ realtek,phy-data-page2-addr:
+ description: PHY data page 2 address
+ $ref: /schemas/types.yaml#/definitions/uint8-array
+
+ realtek,phy-data-page2-A00:
+ description: PHY data page 2 value
+ $ref: /schemas/types.yaml#/definitions/uint8-array
+
+ realtek,do-toggle:
+ description: Do PHY parameter toggle when port status change
+ type: boolean
+
+ realtek,check-efuse:
+ description: Enable to fix PHY parameter from reading otp table
+ type: boolean
+
+ realtek,use-default-parameter:
+ description: Don't set parameter and use default value
+ type: boolean
+
+ realtek,is-double-sensitivity-mode:
+ description: Enable double sensitivity mode
+ type: boolean
+
+ realtek,ldo-page0-e4-compensate:
+ description: Adjust the PHY parameter for page 0 0xE4 for ldo mode
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ realtek,page0-e4-compensate:
+ description: Adjust the PHY parameter for page 0 0xE4
+ for efuse table v2
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ phy0_data:
+ description: The child node of PHY for parameter v2.
+ type: object
+ properties:
+ realtek,page0-size:
+ description: PHY data page 0 size
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ realtek,page0-data-A00:
+ description: PHY data page 0 address and value
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+
+ realtek,page1-size:
+ description: PHY data page 1 size
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ realtek,page1-data-A00:
+ description: PHY data page 1 address and value
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+
+ realtek,page2-size:
+ description: PHY data page 2 size
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ realtek,page2-data-A00:
+ description: PHY data page 2 address and value
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+
+ realtek,do-toggle:
+ description: Do PHY parameter toggle when port status change
+ type: boolean
+
+ realtek,do-toggle-driving:
+ description: Do PHY parameter toggle for driving when port
+ status change
+ type: boolean
+
+ realtek,check-efuse:
+ description: Enable to fix PHY parameter from reading otp table
+ type: boolean
+
+ realtek,use-default-parameter:
+ description: Don't set parameter and use default value
+ type: boolean
+
+ realtek,is-double-sensitivity-mode:
+ description: Enable double sensitivity mode
+ type: boolean
+
+ realtek,ldo-force-enable:
+ description: Force enable ldo mode
+ type: boolean
+
+ realtek,ldo-page0-e4-compensate:
+ description: Adjust the PHY parameter for page0 0xE4 for ldo mode
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ realtek,page0-e4-compensate:
+ description: Adjust the PHY parameter for page0 0xE4
+ for efuse table v2
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+required:
+ - compatible
+ - reg
+ - "#phy-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ dwc3_u3drd_usb2phy: dwc3_u3drd_usb2phy@98013e14 {
+ compatible = "realtek,usb2phy";
+ reg = <0x98013e14 0x4>, <0x98058280 0x4>;
+ #phy-cells = <0>;
+ status = "okay";
+ realtek,phyN = <1>;
+
+ phy0 {
+ realtek,phy-data-page0-size = <16>;
+ realtek,phy-data-page0-addr = /bits/ 8
+ <0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7 0xF0 0xF1
+ 0xF2 0xF3 0xF4 0xF5 0xF6 0xF7>;
+ realtek,phy-data-page0-A00 = /bits/ 8
+ <0xE0 0x30 0x79 0x8D 0x6A 0x65 0x01 0x71 0xFC 0x8C
+ 0x00 0x11 0x9B 0x00 0x00 0x0A>;
+ realtek,phy-data-page0-B00 = /bits/ 8
+ <0x18 0x30 0x79 0x8D 0x6A 0x65 0x01 0x71 0xFC 0x8C
+ 0x00 0x11 0x9B 0x00 0x00 0x32>;
+ realtek,phy-data-page1-size = <8>;
+ realtek,phy-data-page1-addr = /bits/ 8
+ <0xE0 0xE1 0xE2 0xE3 0xE4 0xE5 0xE6 0xE7>;
+ realtek,phy-data-page1-A00 = /bits/ 8
+ <0x25 0xEF 0x60 0x44 0x00 0x0F 0x18 0xE3>;
+ realtek,phy-data-page2-size = <1>;
+ realtek,phy-data-page2-addr = /bits/ 8
+ <0xE0>;
+ realtek,phy-data-page2-A00 = /bits/ 8
+ <0x01>;
+ realtek,do-toggle;
+ realtek,check-efuse;
+ realtek,is-double-sensitivity-mode;
+ realtek,ldo-page0-e4-compensate = <(-2)>;
+ };
+ };
+
+ - |
+ usb_port0_usb2phy: usb_port0_usb2phy@13214 {
+ compatible = "realtek,usb2phy";
+ reg = <0x13214 0x4>, <0x28280 0x4>;
+ #phy-cells = <0>;
+ realtek,usb = <&usb_port0>;
+ realtek,mac = <&port0_dwc3>;
+ realtek,usb_ctrl = <&usb_ctrl>;
+
+ realtek,port-index = <0>;
+ realtek,phyN = <1>;
+ phy0_data {
+ realtek,page0-size = <16>;
+ realtek,page0-data-A00 = /* < addr data > */
+ <0xE0 0xA3>, <0xE4 0xB2>, <0xE5 0x4F>, <0xE6 0x42>;
+ realtek,page1-size = <8>;
+ realtek,page1-data-A00 = <0xE3 0x64>;
+ realtek,page2-size = <8>;
+ realtek,page2-data-A00 = <0xE7 0x45>;
+ realtek,do-toggle;
+ realtek.do-toggle-driving;
+ realtek,disconnect-driving-updated = <0x8>;
+ realtek,check-efuse;
+ realtek,is-double-sensitivity-mode;
+ realtek,ldo-force-enable;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/phy/realtek,usb3phy.yaml b/Documentation/devicetree/bindings/phy/realtek,usb3phy.yaml
new file mode 100644
index 000000000000..2d2543acfb5d
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/realtek,usb3phy.yaml
@@ -0,0 +1,201 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright 2023 Realtek Semiconductor Corporation
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/realtek,usb3phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek DHC SoCs USB3 PHY
+
+maintainers:
+ - Stanley Chang <stanley_chang@realtek.com>
+
+description: |
+ Realtek USB 3.0 PHY support the digital home center (DHC) RTD series SoCs.
+
+properties:
+ compatible:
+ enum:
+ - realtek,usb3phy
+ - realtek,rtd-usb3phy
+ - realtek,rtd1295-usb3phy
+ - realtek,rtd1619-usb3phy
+ - realtek,rtd1319-usb3phy
+ - realtek,rtd1619b-usb3phy
+ - realtek,rtd1319d-usb3phy
+
+ reg:
+ maxItems: 1
+
+ "#phy-cells":
+ const: 0
+
+ realtek,usb:
+ description: The phandler of realtek dwc3 node
+ $ref: /schemas/types.yaml#/definitions/phandle
+
+ realtek,mac:
+ description: The phandler of dwc3 node
+ $ref: /schemas/types.yaml#/definitions/phandle
+
+ realtek,usb_ctrl:
+ description: The phandler of usb power control node
+ $ref: /schemas/types.yaml#/definitions/phandle
+
+ realtek,port-index:
+ description: The index of USB 3.0 PHY
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ realtek,phyN:
+ description: The total amount of USB 3.0 PHY
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ phy0:
+ description: The child node of PHY for parameter v1.
+ type: object
+ properties:
+ realtek,phy-data-size:
+ description: PHY data size
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ realtek,phy-data-addr:
+ description: PHY data address
+ $ref: /schemas/types.yaml#/definitions/uint8-array
+
+ realtek,phy-data-A00:
+ description: PHY data value
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+
+ realtek,do-toggle:
+ description: Do PHY parameter toggle when port status change
+ type: boolean
+
+ realtek,do-toggle-once:
+ description: Do PHY parameter toggle only on PHY init
+ type: boolean
+
+ realtek,check-efuse:
+ description: Enable to fix PHY parameter from reading otp table
+ type: boolean
+
+ realtek,use-default-parameter:
+ description: Don't set parameter and use default value
+ type: boolean
+
+ realtek,check-rx-front-end-offset:
+ description: Enable to check rx front end offset
+ type: boolean
+
+ phy0_data:
+ description: The child node of PHY for parameter v2.
+ type: object
+ properties:
+ realtek,phy-data-size:
+ description: PHY data size
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ realtek,phy-data-A00:
+ description: PHY data address and value
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+
+ realtek,do-toggle:
+ description: Do PHY parameter toggle when port status change
+ type: boolean
+
+ realtek,do-toggle-once:
+ description: Do PHY parameter toggle only on phy init
+ type: boolean
+
+ realtek,check-efuse:
+ description: Enable to fix PHY parameter from reading otp table
+ type: boolean
+
+ realtek,use-default-parameter:
+ description: Don't set parameter and use default value
+ type: boolean
+
+ realtek,check-rx-front-end-offset:
+ description: Enable to check rx front end offset
+ type: boolean
+
+required:
+ - compatible
+ - reg
+ - "#phy-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ dwc3_u3drd_usb3phy: dwc3_u3drd_usb3phy@98013e10 {
+ compatible = "realtek,usb3phy";
+ reg = <0x98013e10 0x4>;
+ #phy-cells = <0>;
+ status = "okay";
+ realtek,port-index = <0>; /* index in u3 port */
+ realtek,phyN = <1>;
+
+ phy0 {
+ realtek,phy-data-size = <0x30>;
+ realtek,phy-data-addr = /bits/ 8
+ <0x00 0x01 0x02 0x03 0x04 0x05 0x06
+ 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D
+ 0x0E 0x0F 0x10 0x11 0x12 0x13 0x14
+ 0x15 0x16 0x17 0x18 0x19 0x1A 0x1B
+ 0x1C 0x1D 0x1E 0x1F 0x20 0x21 0x22
+ 0x23 0x24 0x25 0x26 0x27 0x28 0x29
+ 0x2A 0x2B 0x2C 0x2D 0x2E 0x2F>;
+ realtek,phy-data-A00 = /bits/ 16
+ <0x400C 0xAC86 0x6042 0x2771 0x72F5 0x2AD3 0x0003
+ 0x2E00 0x3591 0x925C 0xA608 0xA905 0xC000 0xEF1E
+ 0x2010 0x8D50 0x000C 0x4C10 0xFC00 0x0C81 0xDE01
+ 0x0000 0x0000 0x0000 0x0000 0x6000 0x0085 0x2014
+ 0xC900 0xA03F 0xC2E0 0x7E00 0x705A 0xF645 0x0013
+ 0xCB66 0x4770 0x126C 0x840A 0x01D6 0xF802 0xff04
+ 0x3040 0x8028 0xFFFF 0xFFFF 0x0000 0x8600>;
+ realtek,phy-data-B00 = /bits/ 16
+ <0x400C 0xAC86 0x6042 0x2771 0x72F5 0x2AD3 0x0003
+ 0x2E00 0x3591 0x924C 0xA608 0xB905 0xC000 0xEF1E
+ 0x2010 0x8D50 0x000C 0x4C10 0xFC00 0x0C81 0xDE01
+ 0x0000 0x0000 0x0000 0x0000 0x6000 0x0085 0x2014
+ 0xC900 0xA03F 0xC2E0 0x7E00 0x705A 0xF645 0x0013
+ 0xCB66 0x4770 0x126C 0x840A 0x01D6 0xF802 0xff04
+ 0x3040 0x8028 0xFFFF 0xFFFF 0x0000 0x8600>;
+ realtek,do-toggle;
+ };
+ };
+
+ - |
+ usb_port2_usb3phy: usb_port2_usb3phy@13e10 {
+ compatible = "realtek,usb3phy";
+ reg = <0x13e10 0x4>;
+ #phy-cells = <0>;
+ realtek,usb = <&usb_port2>;
+ realtek,mac = <&port2_dwc3>;
+ realtek,usb_ctrl = <&usb_ctrl>;
+
+ realtek,port-index = <0>; /* index in u3 port */
+ realtek,phyN = <1>;
+ phy0_data {
+ realtek,phy-data-size = <0x30>;
+ realtek,phy-data-A00 = /* <addr data> */
+ <0x01 0xAC8C>,
+ <0x06 0x0017>,
+ <0x09 0x724C>,
+ <0x0B 0xB90D>,
+ <0x0A 0xB610>,
+ <0x0D 0xEF2A>,
+ <0x0F 0x9050>,
+ <0x10 0x000C>,
+ <0x20 0x70FF>,
+ <0x21 0xCFAA>,
+ <0x22 0x0013>,
+ <0x23 0xDB66>,
+ <0x26 0x8609>,
+ <0x29 0xFF13>,
+ <0x2A 0x3070>;
+ realtek,do-toggle-once;
+ realtek,check_efuse;
+ };
+ };
+
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH v2 2/3] phy: realtek: usb: Add driver for the Realtek SoC USB 2.0/3.0 PHY
2023-05-25 2:26 ` [PATCH v2 2/3] phy: realtek: usb: Add driver for the Realtek SoC USB 2.0/3.0 PHY Stanley Chang
@ 2023-05-29 14:25 ` Greg Kroah-Hartman
2023-05-30 1:50 ` Stanley Chang[昌育德]
0 siblings, 1 reply; 21+ messages in thread
From: Greg Kroah-Hartman @ 2023-05-29 14:25 UTC (permalink / raw)
To: Stanley Chang
Cc: kernel test robot, Vinod Koul, Kishon Vijay Abraham I,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Alan Stern,
Bagas Sanjaya, Ray Chi, Douglas Anderson, Mathias Nyman,
Michael Grzeschik, Flavio Suligoi, linux-phy, devicetree,
linux-kernel, linux-usb
On Thu, May 25, 2023 at 10:26:03AM +0800, Stanley Chang wrote:
> Realtek DHC (digital home center) RTD SoCs support DWC3 XHCI USB 2.0/3.0
> controller. Added two drivers to drive the USB 2.0/3.0 PHY transceivers.
> For USB 3.0 transceivers, a driver phy-rtk-usb3 is provided.
> The driver phy-rtk-usb2 is used to support USB 2.0 transceivers.
>
> Signed-off-by: Stanley Chang <stanley_chang@realtek.com>
> Reported-by: kernel test robot <lkp@intel.com>
The kernel test robot did not report that a new driver was needed :(
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 1/3] usb: phy: add usb phy notify port status API
2023-05-25 2:26 [PATCH v2 1/3] usb: phy: add usb phy notify port status API Stanley Chang
2023-05-25 2:26 ` [PATCH v2 2/3] phy: realtek: usb: Add driver for the Realtek SoC USB 2.0/3.0 PHY Stanley Chang
2023-05-25 2:26 ` [PATCH v2 3/3] dt-bindings: phy: realtek: Add the doc about " Stanley Chang
@ 2023-05-29 14:27 ` Greg Kroah-Hartman
2023-05-30 2:19 ` Stanley Chang[昌育德]
2 siblings, 1 reply; 21+ messages in thread
From: Greg Kroah-Hartman @ 2023-05-29 14:27 UTC (permalink / raw)
To: Stanley Chang
Cc: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Alan Stern, Michael Grzeschik,
Bagas Sanjaya, Flavio Suligoi, Matthias Kaehlcke, Mathias Nyman,
Ray Chi, linux-phy, devicetree, linux-kernel, linux-usb
On Thu, May 25, 2023 at 10:26:02AM +0800, Stanley Chang wrote:
> In Realtek SoC, the parameter of usb phy is designed to can dynamic
> tuning base on port status. Therefore, add a notify callback of phy
> driver when usb port status change.
>
> Signed-off-by: Stanley Chang <stanley_chang@realtek.com>
> ---
> v1 to v2 change:
> No change
> ---
> drivers/usb/core/hub.c | 13 +++++++++++++
> include/linux/usb/phy.h | 14 ++++++++++++++
> 2 files changed, 27 insertions(+)
>
> diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
> index 97a0f8faea6e..b4fbbeae1927 100644
> --- a/drivers/usb/core/hub.c
> +++ b/drivers/usb/core/hub.c
> @@ -614,6 +614,19 @@ static int hub_ext_port_status(struct usb_hub *hub, int port1, int type,
> ret = 0;
> }
> mutex_unlock(&hub->status_mutex);
> +
> + if (!ret) {
> + struct usb_device *hdev = hub->hdev;
> +
> + if (hdev && !hdev->parent) {
> + struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
> +
> + if (hcd->usb_phy)
> + usb_phy_notify_port_status(hcd->usb_phy,
> + port1 - 1, *status, *change);
> + }
> + }
> +
> return ret;
> }
>
> diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h
> index e4de6bc1f69b..53bf3540098f 100644
> --- a/include/linux/usb/phy.h
> +++ b/include/linux/usb/phy.h
> @@ -144,6 +144,10 @@ struct usb_phy {
> */
> int (*set_wakeup)(struct usb_phy *x, bool enabled);
>
> + /* notify phy port status change */
> + int (*notify_port_status)(struct usb_phy *x,
> + int port, u16 portstatus, u16 portchange);
> +
> /* notify phy connect status change */
> int (*notify_connect)(struct usb_phy *x,
> enum usb_device_speed speed);
Why can't this be part of the same notify_connect() callback?
What makes it different somehow? Please document this much better.
> @@ -316,6 +320,16 @@ usb_phy_set_wakeup(struct usb_phy *x, bool enabled)
> return 0;
> }
>
> +static inline int
> +usb_phy_notify_port_status(struct usb_phy *x, int port, u16 portstatus,
> + u16 portchange)
> +{
> + if (x && x->notify_port_status)
How can x ever be NULL?
thanks,
greg k-h
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 3/3] dt-bindings: phy: realtek: Add the doc about the Realtek SoC USB 2.0/3.0 PHY
2023-05-25 2:26 ` [PATCH v2 3/3] dt-bindings: phy: realtek: Add the doc about " Stanley Chang
@ 2023-05-29 18:59 ` Conor Dooley
2023-05-30 3:08 ` Stanley Chang[昌育德]
[not found] ` <0b2143ca-ead7-c8fa-2e80-a94222af51ca@linaro.org>
1 sibling, 1 reply; 21+ messages in thread
From: Conor Dooley @ 2023-05-29 18:59 UTC (permalink / raw)
To: Stanley Chang
Cc: Greg Kroah-Hartman, Vinod Koul, Kishon Vijay Abraham I,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Alan Stern,
Flavio Suligoi, Mathias Nyman, Douglas Anderson,
Matthias Kaehlcke, Ray Chi, Michael Grzeschik, linux-phy,
devicetree, linux-kernel, linux-usb
[-- Attachment #1: Type: text/plain, Size: 1451 bytes --]
Hey,
On Thu, May 25, 2023 at 10:26:04AM +0800, Stanley Chang wrote:
> +properties:
> + compatible:
> + enum:
> + - realtek,usb2phy
> + - realtek,rtd-usb2phy
> + - realtek,rtd1295-usb2phy
> + - realtek,rtd1395-usb2phy
> + - realtek,rtd1619-usb2phy
> + - realtek,rtd1319-usb2phy
> + - realtek,rtd1619b-usb2phy
> + - realtek,rtd1312c-usb2phy
> + - realtek,rtd1319d-usb2phy
> + - realtek,rtd1315e-usb2phy
> +properties:
> + compatible:
> + enum:
> + - realtek,usb3phy
> + - realtek,rtd-usb3phy
> + - realtek,rtd1295-usb3phy
> + - realtek,rtd1619-usb3phy
> + - realtek,rtd1319-usb3phy
> + - realtek,rtd1619b-usb3phy
> + - realtek,rtd1319d-usb3phy
Ignoring everything else, because I really want Krzysztof or Rob to
review this rather than me, but what's going on here with the
compatibles?
What hardware do "usbNphy" and "rtd-usbNphy" represent?
You have device-specific compatibles, which is great, but you also allow
only those two generic ones. I had a _brief_ look at the driver, and it
seems like there is no decision making done based on the compatibles,
only on the properties. Is that correct?
If it is, I would understand having "realtek,usb3phy" as a fallback
compatible for "realtek,rtd1619-usb3phy", but I do not get the current
setup.
Also, I really think this should be broken down into two patches, one
for each of USB 2 & 3.
Cheers,
Conor.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* RE: [PATCH v2 2/3] phy: realtek: usb: Add driver for the Realtek SoC USB 2.0/3.0 PHY
2023-05-29 14:25 ` Greg Kroah-Hartman
@ 2023-05-30 1:50 ` Stanley Chang[昌育德]
2023-05-30 7:31 ` Greg Kroah-Hartman
0 siblings, 1 reply; 21+ messages in thread
From: Stanley Chang[昌育德] @ 2023-05-30 1:50 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: kernel test robot, Vinod Koul, Kishon Vijay Abraham I,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Alan Stern,
Bagas Sanjaya, Ray Chi, Douglas Anderson, Mathias Nyman,
Michael Grzeschik, Flavio Suligoi, linux-phy@lists.infradead.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-usb@vger.kernel.org
Hi Greg,
> On Thu, May 25, 2023 at 10:26:03AM +0800, Stanley Chang wrote:
> > Realtek DHC (digital home center) RTD SoCs support DWC3 XHCI USB
> > 2.0/3.0 controller. Added two drivers to drive the USB 2.0/3.0 PHY
> transceivers.
> > For USB 3.0 transceivers, a driver phy-rtk-usb3 is provided.
> > The driver phy-rtk-usb2 is used to support USB 2.0 transceivers.
> >
> > Signed-off-by: Stanley Chang <stanley_chang@realtek.com>
> > Reported-by: kernel test robot <lkp@intel.com>
>
> The kernel test robot did not report that a new driver was needed :(
>
This report is based on v1 patch.
https://patchwork.kernel.org/project/linux-usb/patch/20230519045825.28369-2-stanley_chang@realtek.com/
The original driver is at drivers/usb/phy/
If this report is not appropriate, I will remove it.
Thanks,
Stanley
^ permalink raw reply [flat|nested] 21+ messages in thread
* RE: [PATCH v2 1/3] usb: phy: add usb phy notify port status API
2023-05-29 14:27 ` [PATCH v2 1/3] usb: phy: add usb phy notify port status API Greg Kroah-Hartman
@ 2023-05-30 2:19 ` Stanley Chang[昌育德]
2023-05-30 7:32 ` Greg Kroah-Hartman
0 siblings, 1 reply; 21+ messages in thread
From: Stanley Chang[昌育德] @ 2023-05-30 2:19 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Alan Stern, Michael Grzeschik,
Bagas Sanjaya, Flavio Suligoi, Matthias Kaehlcke, Mathias Nyman,
Ray Chi, linux-phy@lists.infradead.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-usb@vger.kernel.org
Hi Greg,
> > --- a/include/linux/usb/phy.h
> > +++ b/include/linux/usb/phy.h
> > @@ -144,6 +144,10 @@ struct usb_phy {
> > */
> > int (*set_wakeup)(struct usb_phy *x, bool enabled);
> >
> > + /* notify phy port status change */
> > + int (*notify_port_status)(struct usb_phy *x,
> > + int port, u16 portstatus, u16 portchange);
> > +
> > /* notify phy connect status change */
> > int (*notify_connect)(struct usb_phy *x,
> > enum usb_device_speed speed);
>
> Why can't this be part of the same notify_connect() callback?
The notify connect is at device ready. But I want notify port status change before port reset.
> What makes it different somehow? Please document this much better.
In Realtek phy driver, we have designed to dynamically adjust disconnection level and calibrate phy parameters.
So we do this when the device connected bit changes and when the disconnected bit changes.
Port status change notification:
1. Check if portstatus is USB_PORT_STAT_CONNECTION and portchange is USB_PORT_STAT_C_CONNECTION.
The device is connected, the driver lowers the disconnection level and calibrates the phy parameters.
2. The device disconnects, the driver increases the disconnect level and calibrates the phy parameters.
If we adjust the disconnection level in notify_connect , the disconnect may have been triggered at this stage.
So we need to change that as early as possible.
>
> > @@ -316,6 +320,16 @@ usb_phy_set_wakeup(struct usb_phy *x, bool
> enabled)
> > return 0;
> > }
> >
> > +static inline int
> > +usb_phy_notify_port_status(struct usb_phy *x, int port, u16 portstatus,
> > + u16 portchange)
> > +{
> > + if (x && x->notify_port_status)
>
> How can x ever be NULL?
It is possible.
If the controller not use usb-phy driver. It is NULL.
Thanks,
Stanley
^ permalink raw reply [flat|nested] 21+ messages in thread
* RE: [PATCH v2 3/3] dt-bindings: phy: realtek: Add the doc about the Realtek SoC USB 2.0/3.0 PHY
2023-05-29 18:59 ` Conor Dooley
@ 2023-05-30 3:08 ` Stanley Chang[昌育德]
2023-05-30 6:56 ` Conor Dooley
[not found] ` <202305310146.34V1kevI7026106@rtits1.realtek.com.tw>
0 siblings, 2 replies; 21+ messages in thread
From: Stanley Chang[昌育德] @ 2023-05-30 3:08 UTC (permalink / raw)
To: Conor Dooley
Cc: Greg Kroah-Hartman, Vinod Koul, Kishon Vijay Abraham I,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Alan Stern,
Flavio Suligoi, Mathias Nyman, Douglas Anderson,
Matthias Kaehlcke, Ray Chi, Michael Grzeschik,
linux-phy@lists.infradead.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org
Hi Conor,
> > +properties:
> > + compatible:
> > + enum:
> > + - realtek,usb2phy
> > + - realtek,rtd-usb2phy
> > + - realtek,rtd1295-usb2phy
> > + - realtek,rtd1395-usb2phy
> > + - realtek,rtd1619-usb2phy
> > + - realtek,rtd1319-usb2phy
> > + - realtek,rtd1619b-usb2phy
> > + - realtek,rtd1312c-usb2phy
> > + - realtek,rtd1319d-usb2phy
> > + - realtek,rtd1315e-usb2phy
> > +properties:
> > + compatible:
> > + enum:
> > + - realtek,usb3phy
> > + - realtek,rtd-usb3phy
> > + - realtek,rtd1295-usb3phy
> > + - realtek,rtd1619-usb3phy
> > + - realtek,rtd1319-usb3phy
> > + - realtek,rtd1619b-usb3phy
> > + - realtek,rtd1319d-usb3phy
> Ignoring everything else, because I really want Krzysztof or Rob to
> review this rather than me, but what's going on here with the
> compatibles?
> What hardware do "usbNphy" and "rtd-usbNphy" represent?
>
> You have device-specific compatibles, which is great, but you also allow
> only those two generic ones. I had a _brief_ look at the driver, and it
> seems like there is no decision making done based on the compatibles,
> only on the properties. Is that correct?
> If it is, I would understand having "realtek,usb3phy" as a fallback
> compatible for "realtek,rtd1619-usb3phy", but I do not get the current
> setup.
This driver is compatible with all Realtek RTD SoCs without specifying different settings.
So use "realtek,usb3phy" as fallback compatible for all SoCs.
This is the compatible name we use.
Other compatible names simply indicate that the driver supports the SoCs.
The name "usbNphy" and "rtd-usbNphy" seem to be more generic for all RTD SoCs,
but they are not device-specific compatible.
Do you have a better suggestion?
>
> Also, I really think this should be broken down into two patches, one
> for each of USB 2 & 3.
Okay, I will split to two patches.
Thanks,
Stanley
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 3/3] dt-bindings: phy: realtek: Add the doc about the Realtek SoC USB 2.0/3.0 PHY
2023-05-30 3:08 ` Stanley Chang[昌育德]
@ 2023-05-30 6:56 ` Conor Dooley
[not found] ` <202305310146.34V1kevI7026106@rtits1.realtek.com.tw>
1 sibling, 0 replies; 21+ messages in thread
From: Conor Dooley @ 2023-05-30 6:56 UTC (permalink / raw)
To: Stanley Chang[昌育德]
Cc: Conor Dooley, Greg Kroah-Hartman, Vinod Koul,
Kishon Vijay Abraham I, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Alan Stern, Flavio Suligoi, Mathias Nyman,
Douglas Anderson, Matthias Kaehlcke, Ray Chi, Michael Grzeschik,
linux-phy@lists.infradead.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org
[-- Attachment #1: Type: text/plain, Size: 2488 bytes --]
On Tue, May 30, 2023 at 03:08:29AM +0000, Stanley Chang[昌育德] wrote:
> Hi Conor,
>
> > > +properties:
> > > + compatible:
> > > + enum:
> > > + - realtek,usb2phy
> > > + - realtek,rtd-usb2phy
> > > + - realtek,rtd1295-usb2phy
> > > + - realtek,rtd1395-usb2phy
> > > + - realtek,rtd1619-usb2phy
> > > + - realtek,rtd1319-usb2phy
> > > + - realtek,rtd1619b-usb2phy
> > > + - realtek,rtd1312c-usb2phy
> > > + - realtek,rtd1319d-usb2phy
> > > + - realtek,rtd1315e-usb2phy
>
> > > +properties:
> > > + compatible:
> > > + enum:
> > > + - realtek,usb3phy
> > > + - realtek,rtd-usb3phy
> > > + - realtek,rtd1295-usb3phy
> > > + - realtek,rtd1619-usb3phy
> > > + - realtek,rtd1319-usb3phy
> > > + - realtek,rtd1619b-usb3phy
> > > + - realtek,rtd1319d-usb3phy
>
> > Ignoring everything else, because I really want Krzysztof or Rob to
> > review this rather than me, but what's going on here with the
> > compatibles?
> > What hardware do "usbNphy" and "rtd-usbNphy" represent?
> >
> > You have device-specific compatibles, which is great, but you also allow
> > only those two generic ones. I had a _brief_ look at the driver, and it
> > seems like there is no decision making done based on the compatibles,
> > only on the properties. Is that correct?
> > If it is, I would understand having "realtek,usb3phy" as a fallback
> > compatible for "realtek,rtd1619-usb3phy", but I do not get the current
> > setup.
>
> This driver is compatible with all Realtek RTD SoCs without specifying different settings.
> So use "realtek,usb3phy" as fallback compatible for all SoCs.
> This is the compatible name we use.
> Other compatible names simply indicate that the driver supports the SoCs.
Then you should write the binding such that having fallback compatibles
is permitted. Try plugging
compatible = "realtek,rtd1295-usb2phy", "realtek,rtd-usb2phy", "realtek,usb2phy";
into your example below and see what happens.
> The name "usbNphy" and "rtd-usbNphy" seem to be more generic for all RTD SoCs,
> but they are not device-specific compatible.
> Do you have a better suggestion?
Write the binding so that having fallback compatibles in the DT actually
works, don't add the SoC-specific ones merely as indicators that those
SoCs are supported and don't permit "realtek,usbNphy" or
"realtek,rtd-usbNphy" in isolation ;)
Cheers,
Conor.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 2/3] phy: realtek: usb: Add driver for the Realtek SoC USB 2.0/3.0 PHY
2023-05-30 1:50 ` Stanley Chang[昌育德]
@ 2023-05-30 7:31 ` Greg Kroah-Hartman
2023-05-30 7:37 ` Stanley Chang[昌育德]
0 siblings, 1 reply; 21+ messages in thread
From: Greg Kroah-Hartman @ 2023-05-30 7:31 UTC (permalink / raw)
To: Stanley Chang[昌育德]
Cc: kernel test robot, Vinod Koul, Kishon Vijay Abraham I,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Alan Stern,
Bagas Sanjaya, Ray Chi, Douglas Anderson, Mathias Nyman,
Michael Grzeschik, Flavio Suligoi, linux-phy@lists.infradead.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-usb@vger.kernel.org
On Tue, May 30, 2023 at 01:50:51AM +0000, Stanley Chang[昌育德] wrote:
> Hi Greg,
>
> > On Thu, May 25, 2023 at 10:26:03AM +0800, Stanley Chang wrote:
> > > Realtek DHC (digital home center) RTD SoCs support DWC3 XHCI USB
> > > 2.0/3.0 controller. Added two drivers to drive the USB 2.0/3.0 PHY
> > transceivers.
> > > For USB 3.0 transceivers, a driver phy-rtk-usb3 is provided.
> > > The driver phy-rtk-usb2 is used to support USB 2.0 transceivers.
> > >
> > > Signed-off-by: Stanley Chang <stanley_chang@realtek.com>
> > > Reported-by: kernel test robot <lkp@intel.com>
> >
> > The kernel test robot did not report that a new driver was needed :(
> >
>
> This report is based on v1 patch.
> https://patchwork.kernel.org/project/linux-usb/patch/20230519045825.28369-2-stanley_chang@realtek.com/
>
> The original driver is at drivers/usb/phy/
> If this report is not appropriate, I will remove it.
Does it make sense to include it?
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 1/3] usb: phy: add usb phy notify port status API
2023-05-30 2:19 ` Stanley Chang[昌育德]
@ 2023-05-30 7:32 ` Greg Kroah-Hartman
2023-05-30 7:38 ` Stanley Chang[昌育德]
0 siblings, 1 reply; 21+ messages in thread
From: Greg Kroah-Hartman @ 2023-05-30 7:32 UTC (permalink / raw)
To: Stanley Chang[昌育德]
Cc: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Alan Stern, Michael Grzeschik,
Bagas Sanjaya, Flavio Suligoi, Matthias Kaehlcke, Mathias Nyman,
Ray Chi, linux-phy@lists.infradead.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-usb@vger.kernel.org
On Tue, May 30, 2023 at 02:19:45AM +0000, Stanley Chang[昌育德] wrote:
> Hi Greg,
>
> > > --- a/include/linux/usb/phy.h
> > > +++ b/include/linux/usb/phy.h
> > > @@ -144,6 +144,10 @@ struct usb_phy {
> > > */
> > > int (*set_wakeup)(struct usb_phy *x, bool enabled);
> > >
> > > + /* notify phy port status change */
> > > + int (*notify_port_status)(struct usb_phy *x,
> > > + int port, u16 portstatus, u16 portchange);
> > > +
> > > /* notify phy connect status change */
> > > int (*notify_connect)(struct usb_phy *x,
> > > enum usb_device_speed speed);
> >
> > Why can't this be part of the same notify_connect() callback?
>
> The notify connect is at device ready. But I want notify port status change before port reset.
>
> > What makes it different somehow? Please document this much better.
>
> In Realtek phy driver, we have designed to dynamically adjust disconnection level and calibrate phy parameters.
> So we do this when the device connected bit changes and when the disconnected bit changes.
> Port status change notification:
> 1. Check if portstatus is USB_PORT_STAT_CONNECTION and portchange is USB_PORT_STAT_C_CONNECTION.
> The device is connected, the driver lowers the disconnection level and calibrates the phy parameters.
> 2. The device disconnects, the driver increases the disconnect level and calibrates the phy parameters.
>
> If we adjust the disconnection level in notify_connect , the disconnect may have been triggered at this stage.
> So we need to change that as early as possible.
Please put this type of information in the changelog and in the comments
for the callback when you resubmit it.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 21+ messages in thread
* RE: [PATCH v2 2/3] phy: realtek: usb: Add driver for the Realtek SoC USB 2.0/3.0 PHY
2023-05-30 7:31 ` Greg Kroah-Hartman
@ 2023-05-30 7:37 ` Stanley Chang[昌育德]
0 siblings, 0 replies; 21+ messages in thread
From: Stanley Chang[昌育德] @ 2023-05-30 7:37 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: kernel test robot, Vinod Koul, Kishon Vijay Abraham I,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Alan Stern,
Bagas Sanjaya, Ray Chi, Douglas Anderson, Mathias Nyman,
Michael Grzeschik, Flavio Suligoi, linux-phy@lists.infradead.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-usb@vger.kernel.org
Hi Greg,
> > > >
> > > > Signed-off-by: Stanley Chang <stanley_chang@realtek.com>
> > > > Reported-by: kernel test robot <lkp@intel.com>
> > >
> > > The kernel test robot did not report that a new driver was needed :(
> > >
> >
> > This report is based on v1 patch.
> > https://patchwork.kernel.org/project/linux-usb/patch/20230519045825.28
> > 369-2-stanley_chang@realtek.com/
> >
> > The original driver is at drivers/usb/phy/ If this report is not
> > appropriate, I will remove it.
>
> Does it make sense to include it?
I have fixed the same errors in v2 as v1. So I added it.
I will remove it at next version.
Thanks,
Stanley
^ permalink raw reply [flat|nested] 21+ messages in thread
* RE: [PATCH v2 1/3] usb: phy: add usb phy notify port status API
2023-05-30 7:32 ` Greg Kroah-Hartman
@ 2023-05-30 7:38 ` Stanley Chang[昌育德]
0 siblings, 0 replies; 21+ messages in thread
From: Stanley Chang[昌育德] @ 2023-05-30 7:38 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Alan Stern, Michael Grzeschik,
Bagas Sanjaya, Flavio Suligoi, Matthias Kaehlcke, Mathias Nyman,
Ray Chi, linux-phy@lists.infradead.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-usb@vger.kernel.org
Hi Greg,
> > Why can't this be part of the same notify_connect() callback?
> >
> > The notify connect is at device ready. But I want notify port status change
> before port reset.
> >
> > > What makes it different somehow? Please document this much better.
> >
> > In Realtek phy driver, we have designed to dynamically adjust disconnection
> level and calibrate phy parameters.
> > So we do this when the device connected bit changes and when the
> disconnected bit changes.
> > Port status change notification:
> > 1. Check if portstatus is USB_PORT_STAT_CONNECTION and portchange is
> USB_PORT_STAT_C_CONNECTION.
> > The device is connected, the driver lowers the disconnection level and
> calibrates the phy parameters.
> > 2. The device disconnects, the driver increases the disconnect level and
> calibrates the phy parameters.
> >
> > If we adjust the disconnection level in notify_connect , the disconnect may
> have been triggered at this stage.
> > So we need to change that as early as possible.
>
> Please put this type of information in the changelog and in the comments for
> the callback when you resubmit it.
Okay, I will add this information at next version.
Thanks,
Stanley
^ permalink raw reply [flat|nested] 21+ messages in thread
* RE: [PATCH v2 3/3] dt-bindings: phy: realtek: Add the doc about the Realtek SoC USB 2.0/3.0 PHY
[not found] ` <202305310146.34V1kevI7026106@rtits1.realtek.com.tw>
@ 2023-06-01 2:24 ` Stanley Chang[昌育德]
2023-06-01 20:13 ` Conor Dooley
0 siblings, 1 reply; 21+ messages in thread
From: Stanley Chang[昌育德] @ 2023-06-01 2:24 UTC (permalink / raw)
To: Conor Dooley
Cc: Conor Dooley, Greg Kroah-Hartman, Vinod Koul,
Kishon Vijay Abraham I, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Alan Stern, Flavio Suligoi, Mathias Nyman,
Douglas Anderson, Matthias Kaehlcke, Ray Chi, Michael Grzeschik,
linux-phy@lists.infradead.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org
Hi Conor,
> > > You have device-specific compatibles, which is great, but you also allow
> > > only those two generic ones. I had a _brief_ look at the driver, and it
> > > seems like there is no decision making done based on the compatibles,
> > > only on the properties. Is that correct?
> > > If it is, I would understand having "realtek,usb3phy" as a fallback
> > > compatible for "realtek,rtd1619-usb3phy", but I do not get the current
> > > setup.
> >
> > This driver is compatible with all Realtek RTD SoCs without specifying different settings.
> > So use "realtek,usb3phy" as fallback compatible for all SoCs.
> > This is the compatible name we use.
> > Other compatible names simply indicate that the driver supports the SoCs.
>
> Then you should write the binding such that having fallback compatibles
> is permitted. Try plugging
> compatible = "realtek,rtd1295-usb2phy", "realtek,rtd-usb2phy", "realtek,usb2phy";
> into your example below and see what happens.
>
> > The name "usbNphy" and "rtd-usbNphy" seem to be more generic for all RTD SoCs,
> > but they are not device-specific compatible.
> > Do you have a better suggestion?
>
> Write the binding so that having fallback compatibles in the DT actually
> works, don't add the SoC-specific ones merely as indicators that those
> SoCs are supported and don't permit "realtek,usbNphy" or
> "realtek,rtd-usbNphy" in isolation ;)
>
As far as I understand what you mean.
I should follow other docs to define compatible.
Reference:
Documentation/devicetree/bindings/phy/mediatek,xsphy.yaml
drivers/phy/mediatek/phy-mtk-xsphy.c
For example:
compatible:
items:
- enum:
- realtek,rtd1295-usb2phy
- realtek,rtd1395-usb2phy
- realtek,rtd1619-usb2phy
- realtek,rtd1319-usb2phy
- realtek,rtd1619b-usb2phy
- realtek,rtd1312c-usb2phy
- realtek,rtd1319d-usb2phy
- realtek,rtd1315e-usb2phy
- const: realtek,usb2phy
examples:
-
dwc3_u3drd_usb2phy: dwc3_u3drd_usb2phy@98013e14 {
compatible = "realtek,rtd1319-usb2phy", "realtek,usb2phy";
And use only "Realtek, usb2phy" in the driver.
static const struct of_device_id usbphy_rtk_dt_match[] = {
{ .compatible = "realtek,usb2phy", },
{},
};
^ permalink raw reply [flat|nested] 21+ messages in thread
* RE: [PATCH v2 3/3] dt-bindings: phy: realtek: Add the doc about the Realtek SoC USB 2.0/3.0 PHY
[not found] ` <0b2143ca-ead7-c8fa-2e80-a94222af51ca@linaro.org>
@ 2023-06-01 10:49 ` Stanley Chang[昌育德]
2023-06-01 15:32 ` Krzysztof Kozlowski
0 siblings, 1 reply; 21+ messages in thread
From: Stanley Chang[昌育德] @ 2023-06-01 10:49 UTC (permalink / raw)
To: Krzysztof Kozlowski, Greg Kroah-Hartman
Cc: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Alan Stern, Flavio Suligoi,
Mathias Nyman, Douglas Anderson, Matthias Kaehlcke, Ray Chi,
Michael Grzeschik, linux-phy@lists.infradead.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-usb@vger.kernel.org
Hi Krzysztof,
> Thank you for your patch. There is something to discuss/improve.
>
> Actually a lot... The bindings are not suitable for review.
Thanks for your patience in reviewing my patches.
Most of the properties are about the phy parameters.
Is the phy parameter data suitable to be placed in DTS?
I referenced other phy drivers.
These parameters should not be defined in dts.
I would move the parameters to the driver.
> > + realtek,usb:
> > + description: The phandler of realtek dwc3 node
>
> "phandler"? Except obvious typo, drop "The phandler of" and describe what is
> it for.
realtek,usb is a phandle of syscon used to control realtek dwc3 register.
> > + $ref: /schemas/types.yaml#/definitions/phandle
>
> Anyway, it shouldn't be here. No, no.
Can I use it for phandle of syscon?
> > + realtek,port-index:
> > + description: The index of USB 2.0 PHY
> > + $ref: /schemas/types.yaml#/definitions/uint32
>
> No. No reason for this. You have reg.
This index is used for phy parameters. I will move it to phy driver.
> > +
> > + realtek,phyN:
> > + description: The total amount of USB 2.0 PHY
> > + $ref: /schemas/types.yaml#/definitions/uint32
>
> No. Compatible defines it.
This amount is used for phy parameters. I will move it to phy driver.
> > +
> > + phy0:
> > + description: The child node of PHY for the parameter v1.
>
> ??? Open other phy bindings and use them as example.
phy0 Child node is defined to assign the phy parameter.
I will remove it.
> > + type: object
> > + properties:
> > + realtek,phy-data-page0-size:
> > + description: PHY data page 0 size
> > + $ref: /schemas/types.yaml#/definitions/uint32
> > +
> > + realtek,phy-data-page0-addr:
> > + description: PHY data page 0 address
> > + $ref: /schemas/types.yaml#/definitions/uint8-array
> > +
> > + realtek,phy-data-page0-A00:
> > + description: PHY data page 0 value
> > + $ref: /schemas/types.yaml#/definitions/uint8-array
> > +
> > + realtek,phy-data-page1-size:
> > + description: PHY data page 1 size
> > + $ref: /schemas/types.yaml#/definitions/uint32
> > +
> > + realtek,phy-data-page1-addr:
> > + description: PHY data page 1 address
> > + $ref: /schemas/types.yaml#/definitions/uint8-array
> > +
> > + realtek,phy-data-page1-A00:
> > + description: PHY data page 1 value
> > + $ref: /schemas/types.yaml#/definitions/uint8-array
> > +
> > + realtek,phy-data-page2-size:
> > + description: PHY data page 2 size
> > + $ref: /schemas/types.yaml#/definitions/uint32
> > +
> > + realtek,phy-data-page2-addr:
> > + description: PHY data page 2 address
> > + $ref: /schemas/types.yaml#/definitions/uint8-array
> > +
> > + realtek,phy-data-page2-A00:
> > + description: PHY data page 2 value
> > + $ref: /schemas/types.yaml#/definitions/uint8-array
> > +
> > + realtek,do-toggle:
> > + description: Do PHY parameter toggle when port status change
> > + type: boolean
> > +
> > + realtek,check-efuse:
> > + description: Enable to fix PHY parameter from reading otp table
> > + type: boolean
> > +
> > + realtek,use-default-parameter:
> > + description: Don't set parameter and use default value
> > + type: boolean
> > +
> > + realtek,is-double-sensitivity-mode:
> > + description: Enable double sensitivity mode
> > + type: boolean
> > +
> > + realtek,ldo-page0-e4-compensate:
> > + description: Adjust the PHY parameter for page 0 0xE4 for ldo
> mode
> > + $ref: /schemas/types.yaml#/definitions/uint32
> > +
> > + realtek,page0-e4-compensate:
> > + description: Adjust the PHY parameter for page 0 0xE4
> > + for efuse table v2
> > + $ref: /schemas/types.yaml#/definitions/uint32
>
> I don't understand what's all this for. Most of these descriptions do not explain
> anything except duplicating name of property.
I'll simplify these properties and remove the one about the phy parameter.
> One huge NAK for these bindings. It looks like copy-paste from downstream
> stuff which should never be sent as is to upstream. I am sorry for being harsh,
> but amount of questions, coding and naming styles, incorrect choices is just too
> big to handle in one review.
>
I will refactor this dts based on your comments.
Thanks,
Stanley
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 3/3] dt-bindings: phy: realtek: Add the doc about the Realtek SoC USB 2.0/3.0 PHY
2023-06-01 10:49 ` Stanley Chang[昌育德]
@ 2023-06-01 15:32 ` Krzysztof Kozlowski
2023-06-02 3:20 ` Stanley Chang[昌育德]
0 siblings, 1 reply; 21+ messages in thread
From: Krzysztof Kozlowski @ 2023-06-01 15:32 UTC (permalink / raw)
To: Stanley Chang[昌育德], Greg Kroah-Hartman
Cc: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Alan Stern, Flavio Suligoi,
Mathias Nyman, Douglas Anderson, Matthias Kaehlcke, Ray Chi,
Michael Grzeschik, linux-phy@lists.infradead.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-usb@vger.kernel.org
On 01/06/2023 12:49, Stanley Chang[昌育德] wrote:
> Hi Krzysztof,
>
>> Thank you for your patch. There is something to discuss/improve.
>>
>> Actually a lot... The bindings are not suitable for review.
>
> Thanks for your patience in reviewing my patches.
>
> Most of the properties are about the phy parameters.
> Is the phy parameter data suitable to be placed in DTS?
> I referenced other phy drivers.
> These parameters should not be defined in dts.
> I would move the parameters to the driver.
If these can be in the driver, why would ever they be in DTS in the
first place?
>
>>> + realtek,usb:
>>> + description: The phandler of realtek dwc3 node
>>
>> "phandler"? Except obvious typo, drop "The phandler of" and describe what is
>> it for.
>
> realtek,usb is a phandle of syscon used to control realtek dwc3 register.
Then no, phy should not control dwc3.
>
>>> + $ref: /schemas/types.yaml#/definitions/phandle
>>
>> Anyway, it shouldn't be here. No, no.
>
> Can I use it for phandle of syscon?
PHY getting phandle to block using this PHY? Looks wrong. Why would PHY
need to poke IP block register?
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 3/3] dt-bindings: phy: realtek: Add the doc about the Realtek SoC USB 2.0/3.0 PHY
2023-06-01 2:24 ` Stanley Chang[昌育德]
@ 2023-06-01 20:13 ` Conor Dooley
0 siblings, 0 replies; 21+ messages in thread
From: Conor Dooley @ 2023-06-01 20:13 UTC (permalink / raw)
To: Stanley Chang[昌育德]
Cc: Conor Dooley, Greg Kroah-Hartman, Vinod Koul,
Kishon Vijay Abraham I, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Alan Stern, Flavio Suligoi, Mathias Nyman,
Douglas Anderson, Matthias Kaehlcke, Ray Chi, Michael Grzeschik,
linux-phy@lists.infradead.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org
[-- Attachment #1: Type: text/plain, Size: 2589 bytes --]
On Thu, Jun 01, 2023 at 02:24:28AM +0000, Stanley Chang[昌育德] wrote:
> Hi Conor,
>
> > > > You have device-specific compatibles, which is great, but you also allow
> > > > only those two generic ones. I had a _brief_ look at the driver, and it
> > > > seems like there is no decision making done based on the compatibles,
> > > > only on the properties. Is that correct?
> > > > If it is, I would understand having "realtek,usb3phy" as a fallback
> > > > compatible for "realtek,rtd1619-usb3phy", but I do not get the current
> > > > setup.
> > >
> > > This driver is compatible with all Realtek RTD SoCs without specifying different settings.
> > > So use "realtek,usb3phy" as fallback compatible for all SoCs.
> > > This is the compatible name we use.
> > > Other compatible names simply indicate that the driver supports the SoCs.
> >
> > Then you should write the binding such that having fallback compatibles
> > is permitted. Try plugging
> > compatible = "realtek,rtd1295-usb2phy", "realtek,rtd-usb2phy", "realtek,usb2phy";
> > into your example below and see what happens.
> >
> > > The name "usbNphy" and "rtd-usbNphy" seem to be more generic for all RTD SoCs,
> > > but they are not device-specific compatible.
> > > Do you have a better suggestion?
> >
> > Write the binding so that having fallback compatibles in the DT actually
> > works, don't add the SoC-specific ones merely as indicators that those
> > SoCs are supported and don't permit "realtek,usbNphy" or
> > "realtek,rtd-usbNphy" in isolation ;)
> >
>
> As far as I understand what you mean.
> I should follow other docs to define compatible.
> Reference:
> Documentation/devicetree/bindings/phy/mediatek,xsphy.yaml
> drivers/phy/mediatek/phy-mtk-xsphy.c
>
> For example:
>
> compatible:
> items:
> - enum:
> - realtek,rtd1295-usb2phy
> - realtek,rtd1395-usb2phy
> - realtek,rtd1619-usb2phy
> - realtek,rtd1319-usb2phy
> - realtek,rtd1619b-usb2phy
> - realtek,rtd1312c-usb2phy
> - realtek,rtd1319d-usb2phy
> - realtek,rtd1315e-usb2phy
> - const: realtek,usb2phy
>
> examples:
> -
> dwc3_u3drd_usb2phy: dwc3_u3drd_usb2phy@98013e14 {
> compatible = "realtek,rtd1319-usb2phy", "realtek,usb2phy";
>
> And use only "Realtek, usb2phy" in the driver.
> static const struct of_device_id usbphy_rtk_dt_match[] = {
> { .compatible = "realtek,usb2phy", },
> {},
> };
Yes, this would be a vast improvement, thanks.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 21+ messages in thread
* RE: [PATCH v2 3/3] dt-bindings: phy: realtek: Add the doc about the Realtek SoC USB 2.0/3.0 PHY
2023-06-01 15:32 ` Krzysztof Kozlowski
@ 2023-06-02 3:20 ` Stanley Chang[昌育德]
2023-06-02 7:10 ` Krzysztof Kozlowski
0 siblings, 1 reply; 21+ messages in thread
From: Stanley Chang[昌育德] @ 2023-06-02 3:20 UTC (permalink / raw)
To: Krzysztof Kozlowski, Greg Kroah-Hartman
Cc: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Alan Stern, Flavio Suligoi,
Mathias Nyman, Douglas Anderson, Matthias Kaehlcke, Ray Chi,
Michael Grzeschik, linux-phy@lists.infradead.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-usb@vger.kernel.org
Hi Krzysztof,
> >
> > Most of the properties are about the phy parameters.
> > Is the phy parameter data suitable to be placed in DTS?
> > I referenced other phy drivers.
> > These parameters should not be defined in dts.
> > I would move the parameters to the driver.
>
> If these can be in the driver, why would ever they be in DTS in the first place?
>
Our platforms have 3 xhci controllers which map to 3 different phy ports.
And the three phy ports use the same driver, but the parameters are different.
So I put the parameter settings in DTS, we have 3 usb-phy nodes representing 3 phy ports.
Also, some parameters have to be adjusted for different boards.
Therefore, it is more applicable in DTS than in driver.
> >>> + realtek,usb:
> >>> + description: The phandler of realtek dwc3 node
> >>
> >> "phandler"? Except obvious typo, drop "The phandler of" and describe
> >> what is it for.
> >
> > realtek,usb is a phandle of syscon used to control realtek dwc3 register.
>
> Then no, phy should not control dwc3.
OK I know it doesn't make sense.
We want to disable phy suspend from mac layer.
I will try other method.
> >
> >>> + $ref: /schemas/types.yaml#/definitions/phandle
> >>
> >> Anyway, it shouldn't be here. No, no.
> >
> > Can I use it for phandle of syscon?
>
> PHY getting phandle to block using this PHY? Looks wrong. Why would PHY
> need to poke IP block register?
>
OK. I know it doesn't make sense.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 3/3] dt-bindings: phy: realtek: Add the doc about the Realtek SoC USB 2.0/3.0 PHY
2023-06-02 3:20 ` Stanley Chang[昌育德]
@ 2023-06-02 7:10 ` Krzysztof Kozlowski
2023-06-02 7:33 ` Stanley Chang[昌育德]
0 siblings, 1 reply; 21+ messages in thread
From: Krzysztof Kozlowski @ 2023-06-02 7:10 UTC (permalink / raw)
To: Stanley Chang[昌育德], Greg Kroah-Hartman
Cc: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Alan Stern, Flavio Suligoi,
Mathias Nyman, Douglas Anderson, Matthias Kaehlcke, Ray Chi,
Michael Grzeschik, linux-phy@lists.infradead.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-usb@vger.kernel.org
On 02/06/2023 05:20, Stanley Chang[昌育德] wrote:
> Hi Krzysztof,
>
>>>
>>> Most of the properties are about the phy parameters.
>>> Is the phy parameter data suitable to be placed in DTS?
>>> I referenced other phy drivers.
>>> These parameters should not be defined in dts.
>>> I would move the parameters to the driver.
>>
>> If these can be in the driver, why would ever they be in DTS in the first place?
>>
> Our platforms have 3 xhci controllers which map to 3 different phy ports.
You mean on the same SoC?
> And the three phy ports use the same driver, but the parameters are different.
> So I put the parameter settings in DTS, we have 3 usb-phy nodes representing 3 phy ports.
> Also, some parameters have to be adjusted for different boards.
> Therefore, it is more applicable in DTS than in driver.
Then it looks justified in DT, so please write proper descriptions for
proper properties. Underscores are not allowed in node names. No fake
nodes. Properties should usually describe physical/hardware effect not
the register value.
qcom,usb-snps-femto-v2.yaml is nice example. Few Mediatek bindings also
would work.
'
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 21+ messages in thread
* RE: [PATCH v2 3/3] dt-bindings: phy: realtek: Add the doc about the Realtek SoC USB 2.0/3.0 PHY
2023-06-02 7:10 ` Krzysztof Kozlowski
@ 2023-06-02 7:33 ` Stanley Chang[昌育德]
0 siblings, 0 replies; 21+ messages in thread
From: Stanley Chang[昌育德] @ 2023-06-02 7:33 UTC (permalink / raw)
To: Krzysztof Kozlowski, Greg Kroah-Hartman
Cc: Vinod Koul, Kishon Vijay Abraham I, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Alan Stern, Flavio Suligoi,
Mathias Nyman, Douglas Anderson, Matthias Kaehlcke, Ray Chi,
Michael Grzeschik, linux-phy@lists.infradead.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-usb@vger.kernel.org
Hi Krzysztof,
> >> If these can be in the driver, why would ever they be in DTS in the first
> place?
> >>
> > Our platforms have 3 xhci controllers which map to 3 different phy ports.
>
> You mean on the same SoC?
Yes, one SoC has three xhci controllers.
> > And the three phy ports use the same driver, but the parameters are
> different.
> > So I put the parameter settings in DTS, we have 3 usb-phy nodes representing
> 3 phy ports.
> > Also, some parameters have to be adjusted for different boards.
> > Therefore, it is more applicable in DTS than in driver.
>
> Then it looks justified in DT, so please write proper descriptions for proper
> properties. Underscores are not allowed in node names. No fake nodes.
> Properties should usually describe physical/hardware effect not the register
> value.
I will write more detail for properties.
> qcom,usb-snps-femto-v2.yaml is nice example. Few Mediatek bindings also
> would work.
> '
Thanks,
Stanley
^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2023-06-02 7:35 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-05-25 2:26 [PATCH v2 1/3] usb: phy: add usb phy notify port status API Stanley Chang
2023-05-25 2:26 ` [PATCH v2 2/3] phy: realtek: usb: Add driver for the Realtek SoC USB 2.0/3.0 PHY Stanley Chang
2023-05-29 14:25 ` Greg Kroah-Hartman
2023-05-30 1:50 ` Stanley Chang[昌育德]
2023-05-30 7:31 ` Greg Kroah-Hartman
2023-05-30 7:37 ` Stanley Chang[昌育德]
2023-05-25 2:26 ` [PATCH v2 3/3] dt-bindings: phy: realtek: Add the doc about " Stanley Chang
2023-05-29 18:59 ` Conor Dooley
2023-05-30 3:08 ` Stanley Chang[昌育德]
2023-05-30 6:56 ` Conor Dooley
[not found] ` <202305310146.34V1kevI7026106@rtits1.realtek.com.tw>
2023-06-01 2:24 ` Stanley Chang[昌育德]
2023-06-01 20:13 ` Conor Dooley
[not found] ` <0b2143ca-ead7-c8fa-2e80-a94222af51ca@linaro.org>
2023-06-01 10:49 ` Stanley Chang[昌育德]
2023-06-01 15:32 ` Krzysztof Kozlowski
2023-06-02 3:20 ` Stanley Chang[昌育德]
2023-06-02 7:10 ` Krzysztof Kozlowski
2023-06-02 7:33 ` Stanley Chang[昌育德]
2023-05-29 14:27 ` [PATCH v2 1/3] usb: phy: add usb phy notify port status API Greg Kroah-Hartman
2023-05-30 2:19 ` Stanley Chang[昌育德]
2023-05-30 7:32 ` Greg Kroah-Hartman
2023-05-30 7:38 ` Stanley Chang[昌育德]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).