linux-usb.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/5] add rk3328 usb3 phy driver
@ 2019-10-28 18:22 Peter Geis
  2019-10-28 18:22 ` [PATCH 1/5] phy: rockchip: add inno-usb3 " Peter Geis
                   ` (5 more replies)
  0 siblings, 6 replies; 22+ messages in thread
From: Peter Geis @ 2019-10-28 18:22 UTC (permalink / raw)
  To: heiko; +Cc: linux-rockchip, linux-usb, katsuhiro, robin.murphy, Peter Geis

It took a lot more effort than originally anticipated, but here it is.
This is the driver from [0], updated to work with the current kernel.
I've tested it on the rk3328-roc-cc board, both usb 2.0 and usb 3.0 
devices detect on hotplug.

[0] https://github.com/FireflyTeam/kernel/commits/roc-rk3328-cc/drivers/phy/rockchip/phy-rockchip-inno-usb3.c

Peter Geis (5):
  phy: rockchip: add inno-usb3 phy driver
  dt-bindings: clean up rockchip grf binding document
  Documentation: bindings: add dt documentation for rockchip usb3 phy
  arm64: dts: rockchip: add usb3 to rk3328 devicetree
  arm64: dts: rockchip: enable usb3 on rk3328-roc-cc

 .../bindings/phy/phy-rockchip-inno-usb3.yaml  |  157 +++
 .../devicetree/bindings/soc/rockchip/grf.txt  |    8 +-
 .../devicetree/bindings/usb/rockchip,dwc3.txt |    9 +-
 .../arm64/boot/dts/rockchip/rk3328-roc-cc.dts |   21 +
 arch/arm64/boot/dts/rockchip/rk3328.dtsi      |   72 ++
 drivers/phy/rockchip/Kconfig                  |    9 +
 drivers/phy/rockchip/Makefile                 |    1 +
 drivers/phy/rockchip/phy-rockchip-inno-usb3.c | 1107 +++++++++++++++++
 8 files changed, 1378 insertions(+), 6 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb3.yaml
 create mode 100644 drivers/phy/rockchip/phy-rockchip-inno-usb3.c

-- 
2.20.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH 1/5] phy: rockchip: add inno-usb3 phy driver
  2019-10-28 18:22 [PATCH 0/5] add rk3328 usb3 phy driver Peter Geis
@ 2019-10-28 18:22 ` Peter Geis
  2019-10-29  2:11   ` Chunfeng Yun
  2019-10-30  8:15   ` Heiko Stuebner
  2019-10-28 18:22 ` [PATCH 2/5] dt-bindings: clean up rockchip grf binding document Peter Geis
                   ` (4 subsequent siblings)
  5 siblings, 2 replies; 22+ messages in thread
From: Peter Geis @ 2019-10-28 18:22 UTC (permalink / raw)
  To: heiko; +Cc: linux-rockchip, linux-usb, katsuhiro, robin.murphy, Peter Geis

Add the rockchip innosilicon usb3 phy driver, supporting devices such as the rk3328.
Pulled from:
https://github.com/FireflyTeam/kernel/blob/roc-rk3328-cc/drivers/phy/rockchip/phy-rockchip-inno-usb3.c

Signed-off-by: Peter Geis <pgwipeout@gmail.com>
---
 drivers/phy/rockchip/Kconfig                  |    9 +
 drivers/phy/rockchip/Makefile                 |    1 +
 drivers/phy/rockchip/phy-rockchip-inno-usb3.c | 1107 +++++++++++++++++
 3 files changed, 1117 insertions(+)
 create mode 100644 drivers/phy/rockchip/phy-rockchip-inno-usb3.c

diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig
index c454c90cd99e..766407939d4a 100644
--- a/drivers/phy/rockchip/Kconfig
+++ b/drivers/phy/rockchip/Kconfig
@@ -35,6 +35,15 @@ config PHY_ROCKCHIP_INNO_USB2
 	help
 	  Support for Rockchip USB2.0 PHY with Innosilicon IP block.
 
+config PHY_ROCKCHIP_INNO_USB3
+	tristate "Rockchip INNO USB 3.0 PHY Driver"
+	depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
+	depends on USB_SUPPORT
+	select GENERIC_PHY
+	select USB_PHY
+	help
+	  Support for Rockchip USB 3.0 PHY with Innosilicon IP block.
+
 config PHY_ROCKCHIP_PCIE
 	tristate "Rockchip PCIe PHY Driver"
 	depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST
diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile
index fd21cbaf40dd..d7b3d16c19ae 100644
--- a/drivers/phy/rockchip/Makefile
+++ b/drivers/phy/rockchip/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_PHY_ROCKCHIP_DP)		+= phy-rockchip-dp.o
 obj-$(CONFIG_PHY_ROCKCHIP_EMMC)		+= phy-rockchip-emmc.o
 obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI)	+= phy-rockchip-inno-hdmi.o
 obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2)	+= phy-rockchip-inno-usb2.o
+obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB3)	+= phy-rockchip-inno-usb3.o
 obj-$(CONFIG_PHY_ROCKCHIP_PCIE)		+= phy-rockchip-pcie.o
 obj-$(CONFIG_PHY_ROCKCHIP_TYPEC)	+= phy-rockchip-typec.o
 obj-$(CONFIG_PHY_ROCKCHIP_USB)		+= phy-rockchip-usb.o
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb3.c b/drivers/phy/rockchip/phy-rockchip-inno-usb3.c
new file mode 100644
index 000000000000..31fee8f3a705
--- /dev/null
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb3.c
@@ -0,0 +1,1107 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Rockchip USB 3.0 PHY with Innosilicon IP block driver
+ *
+ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_clk.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/usb/phy.h>
+#include <linux/uaccess.h>
+
+#define U3PHY_PORT_NUM	2
+#define BIT_WRITEABLE_SHIFT	16
+#define SCHEDULE_DELAY	(60 * HZ)
+
+#define U3PHY_APB_RST	BIT(0)
+#define U3PHY_POR_RST	BIT(1)
+#define U3PHY_MAC_RST	BIT(2)
+
+struct rockchip_u3phy;
+struct rockchip_u3phy_port;
+
+enum rockchip_u3phy_type {
+	U3PHY_TYPE_PIPE,
+	U3PHY_TYPE_UTMI,
+};
+
+enum rockchip_u3phy_pipe_pwr {
+	PIPE_PWR_P0	= 0,
+	PIPE_PWR_P1	= 1,
+	PIPE_PWR_P2	= 2,
+	PIPE_PWR_P3	= 3,
+	PIPE_PWR_MAX	= 4,
+};
+
+enum rockchip_u3phy_rest_req {
+	U3_POR_RSTN	= 0,
+	U2_POR_RSTN	= 1,
+	PIPE_MAC_RSTN	= 2,
+	UTMI_MAC_RSTN	= 3,
+	PIPE_APB_RSTN	= 4,
+	UTMI_APB_RSTN	= 5,
+	U3PHY_RESET_MAX	= 6,
+};
+
+enum rockchip_u3phy_utmi_state {
+	PHY_UTMI_HS_ONLINE	= 0,
+	PHY_UTMI_DISCONNECT	= 1,
+	PHY_UTMI_CONNECT	= 2,
+	PHY_UTMI_FS_LS_ONLINE	= 4,
+};
+
+/*
+ * @rvalue: reset value
+ * @dvalue: desired value
+ */
+struct u3phy_reg {
+	unsigned int	offset;
+	unsigned int	bitend;
+	unsigned int	bitstart;
+	unsigned int	rvalue;
+	unsigned int	dvalue;
+};
+
+struct rockchip_u3phy_grfcfg {
+	struct u3phy_reg	um_suspend;
+	struct u3phy_reg	ls_det_en;
+	struct u3phy_reg	ls_det_st;
+	struct u3phy_reg	um_ls;
+	struct u3phy_reg	um_hstdct;
+	struct u3phy_reg	u2_only_ctrl;
+	struct u3phy_reg	u3_disable;
+	struct u3phy_reg	pp_pwr_st;
+	struct u3phy_reg	pp_pwr_en[PIPE_PWR_MAX];
+};
+
+/**
+ * struct rockchip_u3phy_apbcfg: usb3-phy apb configuration.
+ * @u2_pre_emp: usb2-phy pre-emphasis tuning.
+ * @u2_pre_emp_sth: usb2-phy pre-emphasis strength tuning.
+ * @u2_odt_tuning: usb2-phy odt 45ohm tuning.
+ */
+struct rockchip_u3phy_apbcfg {
+	unsigned int	u2_pre_emp;
+	unsigned int	u2_pre_emp_sth;
+	unsigned int	u2_odt_tuning;
+};
+
+struct rockchip_u3phy_cfg {
+	unsigned int reg;
+	const struct rockchip_u3phy_grfcfg grfcfg;
+
+	int (*phy_pipe_power)(struct rockchip_u3phy *u3phy,
+			      struct rockchip_u3phy_port *u3phy_port,
+			      bool on);
+	int (*phy_tuning)(struct rockchip_u3phy *u3phy,
+			  struct rockchip_u3phy_port *u3phy_port,
+			  struct device_node *child_np);
+};
+
+struct rockchip_u3phy_port {
+	struct phy	*phy;
+	void __iomem	*base;
+	unsigned int	index;
+	unsigned char	type;
+	bool		suspended;
+	bool		refclk_25m_quirk;
+	struct mutex	mutex; /* mutex for updating register */
+	struct delayed_work	um_sm_work;
+};
+
+struct rockchip_u3phy {
+	struct device *dev;
+	struct regmap *u3phy_grf;
+	struct regmap *grf;
+	int um_ls_irq;
+	struct clk **clks;
+	int num_clocks;
+	struct dentry *root;
+	struct gpio_desc *vbus_drv_gpio;
+	struct reset_control *rsts[U3PHY_RESET_MAX];
+	struct rockchip_u3phy_apbcfg apbcfg;
+	const struct rockchip_u3phy_cfg *cfgs;
+	struct rockchip_u3phy_port ports[U3PHY_PORT_NUM];
+	struct usb_phy usb_phy;
+};
+
+static inline int param_write(void __iomem *base,
+			      const struct u3phy_reg *reg, bool desired)
+{
+	unsigned int val, mask;
+	unsigned int tmp = desired ? reg->dvalue : reg->rvalue;
+	int ret = 0;
+
+	mask = GENMASK(reg->bitend, reg->bitstart);
+	val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT);
+	ret = regmap_write(base, reg->offset, val);
+
+	return ret;
+}
+
+static inline bool param_exped(void __iomem *base,
+			       const struct u3phy_reg *reg,
+			       unsigned int value)
+{
+	int ret;
+	unsigned int tmp, orig;
+	unsigned int mask = GENMASK(reg->bitend, reg->bitstart);
+
+	ret = regmap_read(base, reg->offset, &orig);
+	if (ret)
+		return false;
+
+	tmp = (orig & mask) >> reg->bitstart;
+	return tmp == value;
+}
+
+static int rockchip_u3phy_usb2_only_show(struct seq_file *s, void *unused)
+{
+	struct rockchip_u3phy	*u3phy = s->private;
+
+	if (param_exped(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.u2_only_ctrl, 1))
+		dev_info(u3phy->dev, "u2\n");
+	else
+		dev_info(u3phy->dev, "u3\n");
+
+	return 0;
+}
+
+static int rockchip_u3phy_usb2_only_open(struct inode *inode,
+					 struct file *file)
+{
+	return single_open(file, rockchip_u3phy_usb2_only_show,
+			   inode->i_private);
+}
+
+static ssize_t rockchip_u3phy_usb2_only_write(struct file *file,
+					      const char __user *ubuf,
+					      size_t count, loff_t *ppos)
+{
+	struct seq_file			*s = file->private_data;
+	struct rockchip_u3phy		*u3phy = s->private;
+	struct rockchip_u3phy_port	*u3phy_port;
+	char				buf[32];
+	u8				index;
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	if (!strncmp(buf, "u3", 2) &&
+	    param_exped(u3phy->u3phy_grf,
+			&u3phy->cfgs->grfcfg.u2_only_ctrl, 1)) {
+		dev_info(u3phy->dev, "Set usb3.0 and usb2.0 mode successfully\n");
+
+		gpiod_set_value_cansleep(u3phy->vbus_drv_gpio, 0);
+
+		param_write(u3phy->grf,
+			    &u3phy->cfgs->grfcfg.u3_disable, false);
+		param_write(u3phy->u3phy_grf,
+			    &u3phy->cfgs->grfcfg.u2_only_ctrl, false);
+
+		for (index = 0; index < U3PHY_PORT_NUM; index++) {
+			u3phy_port = &u3phy->ports[index];
+			/* enable u3 rx termimation */
+			if (u3phy_port->type == U3PHY_TYPE_PIPE)
+				writel(0x30, u3phy_port->base + 0xd8);
+		}
+
+		atomic_notifier_call_chain(&u3phy->usb_phy.notifier, 0, NULL);
+
+		gpiod_set_value_cansleep(u3phy->vbus_drv_gpio, 1);
+	} else if (!strncmp(buf, "u2", 2) &&
+		   param_exped(u3phy->u3phy_grf,
+			       &u3phy->cfgs->grfcfg.u2_only_ctrl, 0)) {
+		dev_info(u3phy->dev, "Set usb2.0 only mode successfully\n");
+
+		gpiod_set_value_cansleep(u3phy->vbus_drv_gpio, 0);
+
+		param_write(u3phy->grf,
+			    &u3phy->cfgs->grfcfg.u3_disable, true);
+		param_write(u3phy->u3phy_grf,
+			    &u3phy->cfgs->grfcfg.u2_only_ctrl, true);
+
+		for (index = 0; index < U3PHY_PORT_NUM; index++) {
+			u3phy_port = &u3phy->ports[index];
+			/* disable u3 rx termimation */
+			if (u3phy_port->type == U3PHY_TYPE_PIPE)
+				writel(0x20, u3phy_port->base + 0xd8);
+		}
+
+		atomic_notifier_call_chain(&u3phy->usb_phy.notifier, 0, NULL);
+
+		gpiod_set_value_cansleep(u3phy->vbus_drv_gpio, 1);
+	} else {
+		dev_info(u3phy->dev, "Same or illegal mode\n");
+	}
+
+	return count;
+}
+
+static const struct file_operations rockchip_u3phy_usb2_only_fops = {
+	.open			= rockchip_u3phy_usb2_only_open,
+	.write			= rockchip_u3phy_usb2_only_write,
+	.read			= seq_read,
+	.llseek			= seq_lseek,
+	.release		= single_release,
+};
+
+int rockchip_u3phy_debugfs_init(struct rockchip_u3phy *u3phy)
+{
+	struct dentry		*root;
+	struct dentry		*file;
+	int			ret;
+
+	root = debugfs_create_dir(dev_name(u3phy->dev), NULL);
+	if (!root) {
+		ret = -ENOMEM;
+		goto err0;
+	}
+
+	u3phy->root = root;
+
+	file = debugfs_create_file("u3phy_mode", 0644, root,
+				   u3phy, &rockchip_u3phy_usb2_only_fops);
+	if (!file) {
+		ret = -ENOMEM;
+		goto err1;
+	}
+	return 0;
+
+err1:
+	debugfs_remove_recursive(root);
+err0:
+	return ret;
+}
+
+static const char *get_rest_name(enum rockchip_u3phy_rest_req rst)
+{
+	switch (rst) {
+	case U2_POR_RSTN:
+		return "u3phy-u2-por";
+	case U3_POR_RSTN:
+		return "u3phy-u3-por";
+	case PIPE_MAC_RSTN:
+		return "u3phy-pipe-mac";
+	case UTMI_MAC_RSTN:
+		return "u3phy-utmi-mac";
+	case UTMI_APB_RSTN:
+		return "u3phy-utmi-apb";
+	case PIPE_APB_RSTN:
+		return "u3phy-pipe-apb";
+	default:
+		return "invalid";
+	}
+}
+
+static void rockchip_u3phy_rest_deassert(struct rockchip_u3phy *u3phy,
+					 unsigned int flag)
+{
+	int rst;
+
+	if (flag & U3PHY_APB_RST) {
+		dev_dbg(u3phy->dev, "deassert APB bus interface reset\n");
+		for (rst = PIPE_APB_RSTN; rst <= UTMI_APB_RSTN; rst++) {
+			if (u3phy->rsts[rst])
+				reset_control_deassert(u3phy->rsts[rst]);
+		}
+	}
+
+	if (flag & U3PHY_POR_RST) {
+		usleep_range(12, 15);
+		dev_dbg(u3phy->dev, "deassert u2 and u3 phy power on reset\n");
+		for (rst = U3_POR_RSTN; rst <= U2_POR_RSTN; rst++) {
+			if (u3phy->rsts[rst])
+				reset_control_deassert(u3phy->rsts[rst]);
+		}
+	}
+
+	if (flag & U3PHY_MAC_RST) {
+		usleep_range(1200, 1500);
+		dev_dbg(u3phy->dev, "deassert pipe and utmi MAC reset\n");
+		for (rst = PIPE_MAC_RSTN; rst <= UTMI_MAC_RSTN; rst++)
+			if (u3phy->rsts[rst])
+				reset_control_deassert(u3phy->rsts[rst]);
+	}
+}
+
+static void rockchip_u3phy_rest_assert(struct rockchip_u3phy *u3phy)
+{
+	int rst;
+
+	dev_dbg(u3phy->dev, "assert u3phy reset\n");
+	for (rst = 0; rst < U3PHY_RESET_MAX; rst++)
+		if (u3phy->rsts[rst])
+			reset_control_assert(u3phy->rsts[rst]);
+}
+
+static int rockchip_u3phy_clk_enable(struct rockchip_u3phy *u3phy)
+{
+	int ret, clk;
+
+	for (clk = 0; clk < u3phy->num_clocks && u3phy->clks[clk]; clk++) {
+		ret = clk_prepare_enable(u3phy->clks[clk]);
+		if (ret)
+			goto err_disable_clks;
+	}
+	return 0;
+
+err_disable_clks:
+	while (--clk >= 0)
+		clk_disable_unprepare(u3phy->clks[clk]);
+	return ret;
+}
+
+static void rockchip_u3phy_clk_disable(struct rockchip_u3phy *u3phy)
+{
+	int clk;
+
+	for (clk = u3phy->num_clocks - 1; clk >= 0; clk--)
+		if (u3phy->clks[clk])
+			clk_disable_unprepare(u3phy->clks[clk]);
+}
+
+static int rockchip_u3phy_init(struct phy *phy)
+{
+	return 0;
+}
+
+static int rockchip_u3phy_exit(struct phy *phy)
+{
+	return 0;
+}
+
+static int rockchip_u3phy_power_on(struct phy *phy)
+{
+	struct rockchip_u3phy_port *u3phy_port = phy_get_drvdata(phy);
+	struct rockchip_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
+	int ret;
+
+	dev_info(&u3phy_port->phy->dev, "u3phy %s power on\n",
+		 (u3phy_port->type == U3PHY_TYPE_UTMI) ? "u2" : "u3");
+
+	if (!u3phy_port->suspended)
+		return 0;
+
+	ret = rockchip_u3phy_clk_enable(u3phy);
+	if (ret)
+		return ret;
+
+	if (u3phy_port->type == U3PHY_TYPE_UTMI) {
+		param_write(u3phy->u3phy_grf,
+			    &u3phy->cfgs->grfcfg.um_suspend, false);
+	} else {
+		/* current in p2 ? */
+		if (param_exped(u3phy->u3phy_grf,
+				&u3phy->cfgs->grfcfg.pp_pwr_st, PIPE_PWR_P2))
+			goto done;
+
+		if (u3phy->cfgs->phy_pipe_power) {
+			dev_dbg(u3phy->dev, "do pipe power up\n");
+			u3phy->cfgs->phy_pipe_power(u3phy, u3phy_port, true);
+		}
+
+		/* exit to p0 */
+		param_write(u3phy->u3phy_grf,
+			    &u3phy->cfgs->grfcfg.pp_pwr_en[PIPE_PWR_P0], true);
+		usleep_range(90, 100);
+
+		/* enter to p2 from p0 */
+		param_write(u3phy->u3phy_grf,
+			    &u3phy->cfgs->grfcfg.pp_pwr_en[PIPE_PWR_P2],
+			    false);
+		udelay(3);
+	}
+
+done:
+	u3phy_port->suspended = false;
+	return 0;
+}
+
+static int rockchip_u3phy_power_off(struct phy *phy)
+{
+	struct rockchip_u3phy_port *u3phy_port = phy_get_drvdata(phy);
+	struct rockchip_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
+
+	dev_info(&u3phy_port->phy->dev, "u3phy %s power off\n",
+		 (u3phy_port->type == U3PHY_TYPE_UTMI) ? "u2" : "u3");
+
+	if (u3phy_port->suspended)
+		return 0;
+
+	if (u3phy_port->type == U3PHY_TYPE_UTMI) {
+		param_write(u3phy->u3phy_grf,
+			    &u3phy->cfgs->grfcfg.um_suspend, true);
+	} else {
+		/* current in p3 ? */
+		if (param_exped(u3phy->u3phy_grf,
+				&u3phy->cfgs->grfcfg.pp_pwr_st, PIPE_PWR_P3))
+			goto done;
+
+		/* exit to p0 */
+		param_write(u3phy->u3phy_grf,
+			    &u3phy->cfgs->grfcfg.pp_pwr_en[PIPE_PWR_P0], true);
+		udelay(2);
+
+		/* enter to p3 from p0 */
+		param_write(u3phy->u3phy_grf,
+			    &u3phy->cfgs->grfcfg.pp_pwr_en[PIPE_PWR_P3], true);
+		udelay(6);
+
+		if (u3phy->cfgs->phy_pipe_power) {
+			dev_dbg(u3phy->dev, "do pipe power down\n");
+			u3phy->cfgs->phy_pipe_power(u3phy, u3phy_port, false);
+		}
+	}
+
+done:
+	rockchip_u3phy_clk_disable(u3phy);
+	u3phy_port->suspended = true;
+	return 0;
+}
+
+static __maybe_unused
+struct phy *rockchip_u3phy_xlate(struct device *dev,
+				 struct of_phandle_args *args)
+{
+	struct rockchip_u3phy *u3phy = dev_get_drvdata(dev);
+	struct rockchip_u3phy_port *u3phy_port = NULL;
+	struct device_node *phy_np = args->np;
+	int index;
+
+	if (args->args_count != 1) {
+		dev_err(dev, "invalid number of cells in 'phy' property\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	for (index = 0; index < U3PHY_PORT_NUM; index++) {
+		if (phy_np == u3phy->ports[index].phy->dev.of_node) {
+			u3phy_port = &u3phy->ports[index];
+			break;
+		}
+	}
+
+	if (!u3phy_port) {
+		dev_err(dev, "failed to find appropriate phy\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	return u3phy_port->phy;
+}
+
+static struct phy_ops rockchip_u3phy_ops = {
+	.init		= rockchip_u3phy_init,
+	.exit		= rockchip_u3phy_exit,
+	.power_on	= rockchip_u3phy_power_on,
+	.power_off	= rockchip_u3phy_power_off,
+	.owner		= THIS_MODULE,
+};
+
+/*
+ * The function manage host-phy port state and suspend/resume phy port
+ * to save power automatically.
+ *
+ * we rely on utmi_linestate and utmi_hostdisconnect to identify whether
+ * devices is disconnect or not. Besides, we do not need care it is FS/LS
+ * disconnected or HS disconnected, actually, we just only need get the
+ * device is disconnected at last through rearm the delayed work,
+ * to suspend the phy port in _PHY_STATE_DISCONNECT_ case.
+ */
+static void rockchip_u3phy_um_sm_work(struct work_struct *work)
+{
+	struct rockchip_u3phy_port *u3phy_port =
+		container_of(work, struct rockchip_u3phy_port, um_sm_work.work);
+	struct rockchip_u3phy *u3phy =
+		dev_get_drvdata(u3phy_port->phy->dev.parent);
+	unsigned int sh = u3phy->cfgs->grfcfg.um_hstdct.bitend -
+			u3phy->cfgs->grfcfg.um_hstdct.bitstart + 1;
+	unsigned int ul, uhd, state;
+	unsigned int ul_mask, uhd_mask;
+	int ret;
+
+	mutex_lock(&u3phy_port->mutex);
+
+	ret = regmap_read(u3phy->u3phy_grf,
+			  u3phy->cfgs->grfcfg.um_ls.offset, &ul);
+	if (ret < 0)
+		goto next_schedule;
+
+	ret = regmap_read(u3phy->u3phy_grf,
+			  u3phy->cfgs->grfcfg.um_hstdct.offset, &uhd);
+	if (ret < 0)
+		goto next_schedule;
+
+	uhd_mask = GENMASK(u3phy->cfgs->grfcfg.um_hstdct.bitend,
+			   u3phy->cfgs->grfcfg.um_hstdct.bitstart);
+	ul_mask = GENMASK(u3phy->cfgs->grfcfg.um_ls.bitend,
+			  u3phy->cfgs->grfcfg.um_ls.bitstart);
+
+	/* stitch on um_ls and um_hstdct as phy state */
+	state = ((uhd & uhd_mask) >> u3phy->cfgs->grfcfg.um_hstdct.bitstart) |
+		(((ul & ul_mask) >> u3phy->cfgs->grfcfg.um_ls.bitstart) << sh);
+
+	switch (state) {
+	case PHY_UTMI_HS_ONLINE:
+		dev_dbg(&u3phy_port->phy->dev, "HS online\n");
+		break;
+	case PHY_UTMI_FS_LS_ONLINE:
+		/*
+		 * For FS/LS device, the online state share with connect state
+		 * from um_ls and um_hstdct register, so we distinguish
+		 * them via suspended flag.
+		 *
+		 * Plus, there are two cases, one is D- Line pull-up, and D+
+		 * line pull-down, the state is 4; another is D+ line pull-up,
+		 * and D- line pull-down, the state is 2.
+		 */
+		if (!u3phy_port->suspended) {
+			/* D- line pull-up, D+ line pull-down */
+			dev_dbg(&u3phy_port->phy->dev, "FS/LS online\n");
+			break;
+		}
+		/* fall through */
+	case PHY_UTMI_CONNECT:
+		if (u3phy_port->suspended) {
+			dev_dbg(&u3phy_port->phy->dev, "Connected\n");
+			rockchip_u3phy_power_on(u3phy_port->phy);
+			u3phy_port->suspended = false;
+		} else {
+			/* D+ line pull-up, D- line pull-down */
+			dev_dbg(&u3phy_port->phy->dev, "FS/LS online\n");
+		}
+		break;
+	case PHY_UTMI_DISCONNECT:
+		if (!u3phy_port->suspended) {
+			dev_dbg(&u3phy_port->phy->dev, "Disconnected\n");
+			rockchip_u3phy_power_off(u3phy_port->phy);
+			u3phy_port->suspended = true;
+		}
+
+		/*
+		 * activate the linestate detection to get the next device
+		 * plug-in irq.
+		 */
+		param_write(u3phy->u3phy_grf,
+			    &u3phy->cfgs->grfcfg.ls_det_st, true);
+		param_write(u3phy->u3phy_grf,
+			    &u3phy->cfgs->grfcfg.ls_det_en, true);
+
+		/*
+		 * we don't need to rearm the delayed work when the phy port
+		 * is suspended.
+		 */
+		mutex_unlock(&u3phy_port->mutex);
+		return;
+	default:
+		dev_dbg(&u3phy_port->phy->dev, "unknown phy state\n");
+		break;
+	}
+
+next_schedule:
+	mutex_unlock(&u3phy_port->mutex);
+	schedule_delayed_work(&u3phy_port->um_sm_work, SCHEDULE_DELAY);
+}
+
+static irqreturn_t rockchip_u3phy_um_ls_irq(int irq, void *data)
+{
+	struct rockchip_u3phy_port *u3phy_port = data;
+	struct rockchip_u3phy *u3phy =
+		dev_get_drvdata(u3phy_port->phy->dev.parent);
+
+	if (!param_exped(u3phy->u3phy_grf,
+			 &u3phy->cfgs->grfcfg.ls_det_st,
+			 u3phy->cfgs->grfcfg.ls_det_st.dvalue))
+		return IRQ_NONE;
+
+	dev_dbg(u3phy->dev, "utmi linestate interrupt\n");
+	mutex_lock(&u3phy_port->mutex);
+
+	/* disable linestate detect irq and clear its status */
+	param_write(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.ls_det_en, false);
+	param_write(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.ls_det_st, true);
+
+	mutex_unlock(&u3phy_port->mutex);
+
+	/*
+	 * In this case for host phy, a new device is plugged in, meanwhile,
+	 * if the phy port is suspended, we need rearm the work to resume it
+	 * and mange its states; otherwise, we just return irq handled.
+	 */
+	if (u3phy_port->suspended) {
+		dev_dbg(u3phy->dev, "schedule utmi sm work\n");
+		rockchip_u3phy_um_sm_work(&u3phy_port->um_sm_work.work);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int rockchip_u3phy_parse_dt(struct rockchip_u3phy *u3phy,
+				   struct platform_device *pdev)
+
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	int ret, i, clk;
+
+	u3phy->um_ls_irq = platform_get_irq_byname(pdev, "linestate");
+	if (u3phy->um_ls_irq < 0) {
+		dev_err(dev, "get utmi linestate irq failed\n");
+		return -ENXIO;
+	}
+
+	u3phy->vbus_drv_gpio = devm_gpiod_get_optional(dev, "vbus-drv",
+						       GPIOD_OUT_HIGH);
+
+	if (!u3phy->vbus_drv_gpio) {
+		dev_warn(&pdev->dev, "vbus_drv is not assigned\n");
+	} else if (IS_ERR(u3phy->vbus_drv_gpio)) {
+		dev_err(&pdev->dev, "failed to get vbus_drv\n");
+		return PTR_ERR(u3phy->vbus_drv_gpio);
+	}
+
+	u3phy->num_clocks = of_clk_get_parent_count(np);
+	if (u3phy->num_clocks == 0)
+		dev_warn(&pdev->dev, "no clks found in dt\n");
+
+	u3phy->clks = devm_kcalloc(dev, u3phy->num_clocks,
+				  sizeof(struct clk *), GFP_KERNEL);
+
+	for (clk = 0; clk < u3phy->num_clocks; clk++) {
+		u3phy->clks[clk] = of_clk_get(np, clk);
+		if (IS_ERR(u3phy->clks[clk])) {
+			ret = PTR_ERR(u3phy->clks[clk]);
+			if (ret == -EPROBE_DEFER)
+				goto err_put_clks;
+			dev_err(&pdev->dev, "failed to get clks, %i\n",
+				ret);
+			u3phy->clks[clk] = NULL;
+			break;
+		}
+	}
+
+	for (i = 0; i < U3PHY_RESET_MAX; i++) {
+		u3phy->rsts[i] = devm_reset_control_get(dev, get_rest_name(i));
+		if (IS_ERR(u3phy->rsts[i])) {
+			dev_info(dev, "no %s reset control specified\n",
+				 get_rest_name(i));
+			u3phy->rsts[i] = NULL;
+		}
+	}
+
+	return 0;
+
+err_put_clks:
+	while (--clk >= 0)
+		clk_put(u3phy->clks[clk]);
+	return ret;
+}
+
+static int rockchip_u3phy_port_init(struct rockchip_u3phy *u3phy,
+				    struct rockchip_u3phy_port *u3phy_port,
+				    struct device_node *child_np)
+{
+	struct resource res;
+	struct phy *phy;
+	int ret;
+
+	dev_dbg(u3phy->dev, "u3phy port initialize\n");
+
+	mutex_init(&u3phy_port->mutex);
+	u3phy_port->suspended = true; /* initial status */
+
+	phy = devm_phy_create(u3phy->dev, child_np, &rockchip_u3phy_ops);
+	if (IS_ERR(phy)) {
+		dev_err(u3phy->dev, "failed to create phy\n");
+		return PTR_ERR(phy);
+	}
+
+	u3phy_port->phy = phy;
+
+	ret = of_address_to_resource(child_np, 0, &res);
+	if (ret) {
+		dev_err(u3phy->dev, "failed to get address resource(np-%s)\n",
+			child_np->name);
+		return ret;
+	}
+
+	u3phy_port->base = devm_ioremap_resource(&u3phy_port->phy->dev, &res);
+	if (IS_ERR(u3phy_port->base)) {
+		dev_err(u3phy->dev, "failed to remap phy regs\n");
+		return PTR_ERR(u3phy_port->base);
+	}
+
+	if (!of_node_cmp(child_np->name, "pipe")) {
+		u3phy_port->type = U3PHY_TYPE_PIPE;
+		u3phy_port->refclk_25m_quirk =
+			of_property_read_bool(child_np,
+					      "rockchip,refclk-25m-quirk");
+	} else {
+		u3phy_port->type = U3PHY_TYPE_UTMI;
+		INIT_DELAYED_WORK(&u3phy_port->um_sm_work,
+				  rockchip_u3phy_um_sm_work);
+
+		ret = devm_request_threaded_irq(u3phy->dev, u3phy->um_ls_irq,
+						NULL, rockchip_u3phy_um_ls_irq,
+						IRQF_ONESHOT, "rockchip_u3phy",
+						u3phy_port);
+		if (ret) {
+			dev_err(u3phy->dev, "failed to request utmi linestate irq handle\n");
+			return ret;
+		}
+	}
+
+	if (u3phy->cfgs->phy_tuning) {
+		dev_dbg(u3phy->dev, "do u3phy tuning\n");
+		ret = u3phy->cfgs->phy_tuning(u3phy, u3phy_port, child_np);
+		if (ret)
+			return ret;
+	}
+
+	phy_set_drvdata(u3phy_port->phy, u3phy_port);
+	return 0;
+}
+
+static int rockchip_u3phy_on_init(struct usb_phy *usb_phy)
+{
+	struct rockchip_u3phy *u3phy =
+		container_of(usb_phy, struct rockchip_u3phy, usb_phy);
+
+	rockchip_u3phy_rest_deassert(u3phy, U3PHY_POR_RST | U3PHY_MAC_RST);
+	return 0;
+}
+
+static void rockchip_u3phy_on_shutdown(struct usb_phy *usb_phy)
+{
+	struct rockchip_u3phy *u3phy =
+		container_of(usb_phy, struct rockchip_u3phy, usb_phy);
+	int rst;
+
+	for (rst = 0; rst < U3PHY_RESET_MAX; rst++)
+		if (u3phy->rsts[rst] && rst != UTMI_APB_RSTN &&
+		    rst != PIPE_APB_RSTN)
+			reset_control_assert(u3phy->rsts[rst]);
+	udelay(1);
+}
+
+static int rockchip_u3phy_on_disconnect(struct usb_phy *usb_phy,
+					enum usb_device_speed speed)
+{
+	struct rockchip_u3phy *u3phy =
+		container_of(usb_phy, struct rockchip_u3phy, usb_phy);
+
+	dev_info(u3phy->dev, "%s device has disconnected\n",
+		 (speed == USB_SPEED_SUPER) ? "U3" : "UW/U2/U1.1/U1");
+
+	if (speed == USB_SPEED_SUPER)
+		atomic_notifier_call_chain(&usb_phy->notifier, 0, NULL);
+
+	return 0;
+}
+
+static int rockchip_u3phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct device_node *child_np;
+	struct phy_provider *provider;
+	struct rockchip_u3phy *u3phy;
+	const struct rockchip_u3phy_cfg *phy_cfgs;
+	const struct of_device_id *match;
+	unsigned int reg[2];
+	int index, ret;
+
+	match = of_match_device(dev->driver->of_match_table, dev);
+	if (!match || !match->data) {
+		dev_err(dev, "phy-cfgs are not assigned!\n");
+		return -EINVAL;
+	}
+
+	u3phy = devm_kzalloc(dev, sizeof(*u3phy), GFP_KERNEL);
+	if (!u3phy)
+		return -ENOMEM;
+
+	u3phy->u3phy_grf =
+		syscon_regmap_lookup_by_phandle(np, "rockchip,u3phygrf");
+	if (IS_ERR(u3phy->u3phy_grf))
+		return PTR_ERR(u3phy->u3phy_grf);
+
+	u3phy->grf =
+		syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+	if (IS_ERR(u3phy->grf)) {
+		dev_err(dev, "Missing rockchip,grf property\n");
+		return PTR_ERR(u3phy->grf);
+	}
+
+	if (of_property_read_u32_array(np, "reg", reg, 2)) {
+		dev_err(dev, "the reg property is not assigned in %s node\n",
+			np->name);
+		return -EINVAL;
+	}
+
+	u3phy->dev = dev;
+	phy_cfgs = match->data;
+	platform_set_drvdata(pdev, u3phy);
+
+	/* find out a proper config which can be matched with dt. */
+	index = 0;
+	while (phy_cfgs[index].reg) {
+		if (phy_cfgs[index].reg == reg[1]) {
+			u3phy->cfgs = &phy_cfgs[index];
+			break;
+		}
+
+		++index;
+	}
+
+	if (!u3phy->cfgs) {
+		dev_err(dev, "no phy-cfgs can be matched with %s node\n",
+			np->name);
+		return -EINVAL;
+	}
+
+	ret = rockchip_u3phy_parse_dt(u3phy, pdev);
+	if (ret) {
+		dev_err(dev, "parse dt failed, ret(%d)\n", ret);
+		return ret;
+	}
+
+	ret = rockchip_u3phy_clk_enable(u3phy);
+	if (ret) {
+		dev_err(dev, "clk enable failed, ret(%d)\n", ret);
+		return ret;
+	}
+
+	rockchip_u3phy_rest_assert(u3phy);
+	rockchip_u3phy_rest_deassert(u3phy, U3PHY_APB_RST | U3PHY_POR_RST);
+
+	index = 0;
+	for_each_available_child_of_node(np, child_np) {
+		struct rockchip_u3phy_port *u3phy_port = &u3phy->ports[index];
+
+		u3phy_port->index = index;
+		ret = rockchip_u3phy_port_init(u3phy, u3phy_port, child_np);
+		if (ret) {
+			dev_err(dev, "u3phy port init failed,ret(%d)\n", ret);
+			goto put_child;
+		}
+
+		/* to prevent out of boundary */
+		if (++index >= U3PHY_PORT_NUM)
+			break;
+	}
+
+	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	if (IS_ERR_OR_NULL(provider))
+		goto put_child;
+
+	rockchip_u3phy_rest_deassert(u3phy, U3PHY_MAC_RST);
+	rockchip_u3phy_clk_disable(u3phy);
+
+	u3phy->usb_phy.dev = dev;
+	u3phy->usb_phy.init = rockchip_u3phy_on_init;
+	u3phy->usb_phy.shutdown = rockchip_u3phy_on_shutdown;
+	u3phy->usb_phy.notify_disconnect = rockchip_u3phy_on_disconnect;
+	usb_add_phy(&u3phy->usb_phy, USB_PHY_TYPE_USB3);
+	ATOMIC_INIT_NOTIFIER_HEAD(&u3phy->usb_phy.notifier);
+
+	rockchip_u3phy_debugfs_init(u3phy);
+
+	dev_info(dev, "Rockchip u3phy initialized successfully\n");
+	return 0;
+
+put_child:
+	of_node_put(child_np);
+	return ret;
+}
+
+static int rk3328_u3phy_pipe_power(struct rockchip_u3phy *u3phy,
+				   struct rockchip_u3phy_port *u3phy_port,
+				   bool on)
+{
+	unsigned int reg;
+
+	if (on) {
+		reg = readl(u3phy_port->base + 0x1a8);
+		reg &= ~BIT(4); /* ldo power up */
+		writel(reg, u3phy_port->base + 0x1a8);
+
+		reg = readl(u3phy_port->base + 0x044);
+		reg &= ~BIT(4); /* bg power on */
+		writel(reg, u3phy_port->base + 0x044);
+
+		reg = readl(u3phy_port->base + 0x150);
+		reg |= BIT(6); /* tx bias enable */
+		writel(reg, u3phy_port->base + 0x150);
+
+		reg = readl(u3phy_port->base + 0x080);
+		reg &= ~BIT(2); /* tx cm power up */
+		writel(reg, u3phy_port->base + 0x080);
+
+		reg = readl(u3phy_port->base + 0x0c0);
+		/* tx obs enable and rx cm enable */
+		reg |= (BIT(3) | BIT(4));
+		writel(reg, u3phy_port->base + 0x0c0);
+
+		udelay(1);
+	} else {
+		reg = readl(u3phy_port->base + 0x1a8);
+		reg |= BIT(4); /* ldo power down */
+		writel(reg, u3phy_port->base + 0x1a8);
+
+		reg = readl(u3phy_port->base + 0x044);
+		reg |= BIT(4); /* bg power down */
+		writel(reg, u3phy_port->base + 0x044);
+
+		reg = readl(u3phy_port->base + 0x150);
+		reg &= ~BIT(6); /* tx bias disable */
+		writel(reg, u3phy_port->base + 0x150);
+
+		reg = readl(u3phy_port->base + 0x080);
+		reg |= BIT(2); /* tx cm power down */
+		writel(reg, u3phy_port->base + 0x080);
+
+		reg = readl(u3phy_port->base + 0x0c0);
+		/* tx obs disable and rx cm disable */
+		reg &= ~(BIT(3) | BIT(4));
+		writel(reg, u3phy_port->base + 0x0c0);
+	}
+
+	return 0;
+}
+
+static int rk3328_u3phy_tuning(struct rockchip_u3phy *u3phy,
+			       struct rockchip_u3phy_port *u3phy_port,
+			       struct device_node *child_np)
+{
+	if (u3phy_port->type == U3PHY_TYPE_UTMI) {
+		/*
+		 * For rk3328 SoC, pre-emphasis and pre-emphasis strength must
+		 * be written as one fixed value as below.
+		 *
+		 * Dissimilarly, the odt 45ohm value should be flexibly tuninged
+		 * for the different boards to adjust HS eye height, so its
+		 * value can be assigned in DT in code design.
+		 */
+
+		/* {bits[2:0]=111}: always enable pre-emphasis */
+		u3phy->apbcfg.u2_pre_emp = 0x0f;
+
+		/* {bits[5:3]=000}: pre-emphasis strength as the weakest */
+		u3phy->apbcfg.u2_pre_emp_sth = 0x41;
+
+		/* {bits[4:0]=10101}: odt 45ohm tuning */
+		u3phy->apbcfg.u2_odt_tuning = 0xb5;
+		/* optional override of the odt 45ohm tuning */
+		of_property_read_u32(child_np, "rockchip,odt-val-tuning",
+				     &u3phy->apbcfg.u2_odt_tuning);
+
+		writel(u3phy->apbcfg.u2_pre_emp, u3phy_port->base + 0x030);
+		writel(u3phy->apbcfg.u2_pre_emp_sth, u3phy_port->base + 0x040);
+		writel(u3phy->apbcfg.u2_odt_tuning, u3phy_port->base + 0x11c);
+	} else if (u3phy_port->type == U3PHY_TYPE_PIPE) {
+		if (u3phy_port->refclk_25m_quirk) {
+			dev_dbg(u3phy->dev, "switch to 25m refclk\n");
+			/* ref clk switch to 25M */
+			writel(0x64, u3phy_port->base + 0x11c);
+			writel(0x64, u3phy_port->base + 0x028);
+			writel(0x01, u3phy_port->base + 0x020);
+			writel(0x21, u3phy_port->base + 0x030);
+			writel(0x06, u3phy_port->base + 0x108);
+			writel(0x00, u3phy_port->base + 0x118);
+		} else {
+			/* configure for 24M ref clk */
+			writel(0x80, u3phy_port->base + 0x10c);
+			writel(0x01, u3phy_port->base + 0x118);
+			writel(0x38, u3phy_port->base + 0x11c);
+			writel(0x83, u3phy_port->base + 0x020);
+			writel(0x02, u3phy_port->base + 0x108);
+		}
+
+		/* Enable SSC */
+		udelay(3);
+		writel(0x08, u3phy_port->base + 0x000);
+		writel(0x0c, u3phy_port->base + 0x120);
+
+		/* Tuning Rx for compliance RJTL test */
+		writel(0x70, u3phy_port->base + 0x150);
+		writel(0x12, u3phy_port->base + 0x0c8);
+		writel(0x05, u3phy_port->base + 0x148);
+		writel(0x08, u3phy_port->base + 0x068);
+		writel(0xf0, u3phy_port->base + 0x1c4);
+		writel(0xff, u3phy_port->base + 0x070);
+		writel(0x0f, u3phy_port->base + 0x06c);
+		writel(0xe0, u3phy_port->base + 0x060);
+
+		/*
+		 * Tuning Tx to increase the bias current
+		 * used in TX driver and RX EQ, it can
+		 * also increase the voltage of LFPS.
+		 */
+		writel(0x08, u3phy_port->base + 0x180);
+	} else {
+		dev_err(u3phy->dev, "invalid u3phy port type\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct rockchip_u3phy_cfg rk3328_u3phy_cfgs[] = {
+	{
+		.reg		= 0xff470000,
+		.grfcfg		= {
+			.um_suspend	= { 0x0004, 15, 0, 0x1452, 0x15d1 },
+			.u2_only_ctrl	= { 0x0020, 15, 15, 0, 1 },
+			.um_ls		= { 0x0030, 5, 4, 0, 1 },
+			.um_hstdct	= { 0x0030, 7, 7, 0, 1 },
+			.ls_det_en	= { 0x0040, 0, 0, 0, 1 },
+			.ls_det_st	= { 0x0044, 0, 0, 0, 1 },
+			.pp_pwr_st	= { 0x0034, 14, 13, 0, 0},
+			.pp_pwr_en	= { {0x0020, 14, 0, 0x0014, 0x0005},
+					    {0x0020, 14, 0, 0x0014, 0x000d},
+					    {0x0020, 14, 0, 0x0014, 0x0015},
+					    {0x0020, 14, 0, 0x0014, 0x001d} },
+			.u3_disable	= { 0x04c4, 15, 0, 0x1100, 0x101},
+		},
+		.phy_pipe_power	= rk3328_u3phy_pipe_power,
+		.phy_tuning	= rk3328_u3phy_tuning,
+	},
+	{ /* sentinel */ }
+};
+
+static const struct of_device_id rockchip_u3phy_dt_match[] = {
+	{ .compatible = "rockchip,rk3328-u3phy", .data = &rk3328_u3phy_cfgs },
+	{}
+};
+MODULE_DEVICE_TABLE(of, rockchip_u3phy_dt_match);
+
+static struct platform_driver rockchip_u3phy_driver = {
+	.probe		= rockchip_u3phy_probe,
+	.driver		= {
+		.name	= "rockchip-u3phy",
+		.of_match_table = rockchip_u3phy_dt_match,
+	},
+};
+module_platform_driver(rockchip_u3phy_driver);
+
+MODULE_AUTHOR("Frank Wang <frank.wang@rock-chips.com>");
+MODULE_AUTHOR("William Wu <william.wu@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip USB 3.0 PHY driver");
+MODULE_LICENSE("GPL v2");
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 2/5] dt-bindings: clean up rockchip grf binding document
  2019-10-28 18:22 [PATCH 0/5] add rk3328 usb3 phy driver Peter Geis
  2019-10-28 18:22 ` [PATCH 1/5] phy: rockchip: add inno-usb3 " Peter Geis
@ 2019-10-28 18:22 ` Peter Geis
  2019-10-31 14:25   ` Heiko Stuebner
  2019-10-28 18:22 ` [PATCH 3/5] Documentation: bindings: add dt documentation for rockchip usb3 phy Peter Geis
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 22+ messages in thread
From: Peter Geis @ 2019-10-28 18:22 UTC (permalink / raw)
  To: heiko; +Cc: linux-rockchip, linux-usb, katsuhiro, robin.murphy, Peter Geis

Fixup some typos and inconsistencies in the grf binding.

Signed-off-by: Peter Geis <pgwipeout@gmail.com>
---
 Documentation/devicetree/bindings/soc/rockchip/grf.txt | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/soc/rockchip/grf.txt b/Documentation/devicetree/bindings/soc/rockchip/grf.txt
index d7debec26ba4..ada5435ce2c3 100644
--- a/Documentation/devicetree/bindings/soc/rockchip/grf.txt
+++ b/Documentation/devicetree/bindings/soc/rockchip/grf.txt
@@ -27,12 +27,12 @@ Required Properties:
    - "rockchip,px30-pmugrf", "syscon": for px30
    - "rockchip,rk3368-pmugrf", "syscon": for rk3368
    - "rockchip,rk3399-pmugrf", "syscon": for rk3399
-- compatible: SGRF should be one of the following
+- compatible: SGRF should be one of the following:
    - "rockchip,rk3288-sgrf", "syscon": for rk3288
-- compatible: USB2PHYGRF should be one of the followings
+- compatible: USB2PHYGRF should be one of the following:
    - "rockchip,px30-usb2phy-grf", "syscon": for px30
    - "rockchip,rk3328-usb2phy-grf", "syscon": for rk3328
-- compatible: USBGRF should be one of the following
+- compatible: USBGRF should be one of the following:
    - "rockchip,rv1108-usbgrf", "syscon": for rv1108
 - reg: physical base address of the controller and length of memory mapped
   region.
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 3/5] Documentation: bindings: add dt documentation for rockchip usb3 phy
  2019-10-28 18:22 [PATCH 0/5] add rk3328 usb3 phy driver Peter Geis
  2019-10-28 18:22 ` [PATCH 1/5] phy: rockchip: add inno-usb3 " Peter Geis
  2019-10-28 18:22 ` [PATCH 2/5] dt-bindings: clean up rockchip grf binding document Peter Geis
@ 2019-10-28 18:22 ` Peter Geis
  2019-10-28 18:22 ` [PATCH 4/5] arm64: dts: rockchip: add usb3 to rk3328 devicetree Peter Geis
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 22+ messages in thread
From: Peter Geis @ 2019-10-28 18:22 UTC (permalink / raw)
  To: heiko; +Cc: linux-rockchip, linux-usb, katsuhiro, robin.murphy, Peter Geis

Add devicetree bindings for inno-usb3-phy.

Signed-off-by: Peter Geis <pgwipeout@gmail.com>
---
 .../bindings/phy/phy-rockchip-inno-usb3.yaml  | 157 ++++++++++++++++++
 .../devicetree/bindings/soc/rockchip/grf.txt  |   2 +
 .../devicetree/bindings/usb/rockchip,dwc3.txt |   9 +-
 3 files changed, 165 insertions(+), 3 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb3.yaml

diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb3.yaml b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb3.yaml
new file mode 100644
index 000000000000..f4f28625173a
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb3.yaml
@@ -0,0 +1,157 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/phy/phy-rockchip-inno-usb3.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: ROCKCHIP USB 3.0 PHY WITH INNO IP BLOCK
+
+maintainers:
+
+properties:
+  compatible:
+    enum:
+      - rockchip,rk3328-u3phy
+
+  reg:
+    - description: the base address of the USB 3.0 PHY
+
+  interrupts:
+    maxItems: 1
+
+  interrupt-names:
+    items:
+      - const: linestate
+        description: host/otg linestate interrupt
+
+  clocks:
+    maxItems: 2
+
+  clock-names:
+    items:
+      - const: u3phy-otg
+        description: USB 3.0 PHY UTMI
+      - const: u3phy-pipe
+        description: USB 3.0 PHY Pipe
+
+  resets:
+    maxItems: 6
+
+  reset-names:
+    items:
+      - const: u3phy-u2-por
+      description: USB 2.0 logic of USB 3.0 PHY
+      - const: u3phy-u3-por
+      description: USB 3.0 logic of USB 3.0 PHY
+      - const: u3phy-pipe-mac
+      description: USB 3.0 PHY pipe MAC
+      - const: u3phy-utmi-mac
+      description: USB 3.0 PHY utmi MAC
+      - const: u3phy-utmi-apb
+      description: USB 3.0 PHY utmi apb
+      - const: u3phy-pipe-apb
+      description: USB 3.0 PHY pipe apb
+
+  "#phy-cells":
+    const: 1
+
+  rockchip,u3phygrf:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    type: array
+    - description: phandle to the syscon managing the
+                   "USB 3.0 PHY general register files".
+
+  vbus-drv-gpios:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    type: array
+    - description: phandle for gpio vbus supply
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - interrupt-names
+  - clocks
+  - clock-names
+  - resets
+  - reset-names
+  - "#phy-cells"
+  - rockchip,u3phygrf
+
+patternProperties:
+  "^u3phy_utmi@[0-9a-f]+$":
+    type: object
+
+    properties:
+      - description: USB 2.0 utmi phy.
+
+      rockchip,odt-val-tuning:
+        type: boolean
+        - description: specify 45ohm ODT tuning value.
+
+      "phy-cells":
+        const: 0
+
+    required:
+      - reg
+      - "#phy-cells"
+
+patternProperties:
+  "^u3phy_pipe@[0-9a-f]+$":
+    type: object
+
+    properties:
+      - description: USB 3.0 pipe phy.
+
+      rockchip,refclk-25m-quirk :
+
+        - description: phy reference clock changed to 25m quirk.
+
+      "phy-cells":
+        const: 0
+
+    required:
+      - reg
+      - "#phy-cells"
+
+examples:
+
+usb3phy_grf: syscon@ff460000 {
+	compatible = "rockchip,usb3phy-grf", "syscon";
+	reg = <0x0 0xff460000 0x0 0x1000>;
+};
+
+...
+
+u3phy: usb3-phy@ff470000 {
+	compatible = "rockchip,rk3328-u3phy";
+	reg = <0x0 0xff470000 0x0 0x0>;
+	rockchip,u3phygrf = <&usb3phy_grf>;
+	interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>;
+	interrupt-names = "linestate";
+	clocks = <&cru PCLK_USB3PHY_OTG>, <&cru PCLK_USB3PHY_PIPE>;
+	clock-names = "u3phy-otg", "u3phy-pipe";
+	resets = <&cru SRST_USB3PHY_U2>,
+		 <&cru SRST_USB3PHY_U3>,
+		 <&cru SRST_USB3PHY_PIPE>,
+		 <&cru SRST_USB3OTG_UTMI>,
+		 <&cru SRST_USB3PHY_OTG_P>,
+		 <&cru SRST_USB3PHY_PIPE_P>;
+	reset-names = "u3phy-u2-por", "u3phy-u3-por",
+		      "u3phy-pipe-mac", "u3phy-utmi-mac",
+		      "u3phy-utmi-apb", "u3phy-pipe-apb";
+	vbus-drv-gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>;
+	#address-cells = <2>;
+	#size-cells = <2>;
+	ranges;
+
+	u3phy_utmi: utmi@ff470000 {
+		reg = <0x0 0xff470000 0x0 0x8000>;
+		#phy-cells = <0>;
+	};
+
+	u3phy_pipe: pipe@ff478000 {
+		reg = <0x0 0xff478000 0x0 0x8000>;
+		#phy-cells = <0>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/soc/rockchip/grf.txt b/Documentation/devicetree/bindings/soc/rockchip/grf.txt
index ada5435ce2c3..5f2f19344cc7 100644
--- a/Documentation/devicetree/bindings/soc/rockchip/grf.txt
+++ b/Documentation/devicetree/bindings/soc/rockchip/grf.txt
@@ -34,6 +34,8 @@ Required Properties:
    - "rockchip,rk3328-usb2phy-grf", "syscon": for rk3328
 - compatible: USBGRF should be one of the following:
    - "rockchip,rv1108-usbgrf", "syscon": for rv1108
+- compatible: USB3PHYGRF should be one of the following:
+   - "rockchip,u3phy-grf", "syscon"
 - reg: physical base address of the controller and length of memory mapped
   region.
 
diff --git a/Documentation/devicetree/bindings/usb/rockchip,dwc3.txt b/Documentation/devicetree/bindings/usb/rockchip,dwc3.txt
index c8c4b00ecb94..7ac8b748bac8 100644
--- a/Documentation/devicetree/bindings/usb/rockchip,dwc3.txt
+++ b/Documentation/devicetree/bindings/usb/rockchip,dwc3.txt
@@ -1,7 +1,9 @@
 Rockchip SuperSpeed DWC3 USB SoC controller
 
 Required properties:
-- compatible:	should contain "rockchip,rk3399-dwc3" for rk3399 SoC
+- compatible:	should be one of the listed compatibles:
+  - "rockchip,rk3328-dwc3" , "rockchip,rk3399-dwc3"	for rk3328 SoC
+  - "rockchip,rk3399-dwc3" 				for rk3399 SoC
 - clocks:	A list of phandle + clock-specifier pairs for the
 		clocks listed in clock-names
 - clock-names:	Should contain the following:
@@ -16,8 +18,9 @@ A child node must exist to represent the core DWC3 IP block. The name of
 the node is not important. The content of the node is defined in dwc3.txt.
 
 Phy documentation is provided in the following places:
-Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt - USB2.0 PHY
-Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt     - Type-C PHY
+Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt  - USB2.0 PHY
+Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb3.yaml - USB3.0 PHY
+Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt      - Type-C PHY
 
 Example device nodes:
 
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 4/5] arm64: dts: rockchip: add usb3 to rk3328 devicetree
  2019-10-28 18:22 [PATCH 0/5] add rk3328 usb3 phy driver Peter Geis
                   ` (2 preceding siblings ...)
  2019-10-28 18:22 ` [PATCH 3/5] Documentation: bindings: add dt documentation for rockchip usb3 phy Peter Geis
@ 2019-10-28 18:22 ` Peter Geis
  2019-10-29  1:58   ` Chunfeng Yun
  2019-10-28 18:22 ` [PATCH 5/5] arm64: dts: rockchip: enable usb3 on rk3328-roc-cc Peter Geis
  2019-10-28 18:41 ` [PATCH 0/5] add rk3328 usb3 phy driver Robin Murphy
  5 siblings, 1 reply; 22+ messages in thread
From: Peter Geis @ 2019-10-28 18:22 UTC (permalink / raw)
  To: heiko; +Cc: linux-rockchip, linux-usb, katsuhiro, robin.murphy, Peter Geis

Now that we have a proper phy driver, we can add the requisite bits to the
rk3328 device tree.
Added the u3drd and u3phy nodes.

Signed-off-by: Peter Geis <pgwipeout@gmail.com>
---
 arch/arm64/boot/dts/rockchip/rk3328.dtsi | 72 ++++++++++++++++++++++++
 1 file changed, 72 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3328.dtsi b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
index 31cc1541f1f5..072e988ad655 100644
--- a/arch/arm64/boot/dts/rockchip/rk3328.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
@@ -805,6 +805,47 @@
 		};
 	};
 
+	usb3phy_grf: syscon@ff460000 {
+		compatible = "rockchip,usb3phy-grf", "syscon";
+		reg = <0x0 0xff460000 0x0 0x1000>;
+	};
+
+	u3phy: usb3-phy@ff470000 {
+		compatible = "rockchip,rk3328-u3phy";
+		reg = <0x0 0xff470000 0x0 0x0>;
+		rockchip,u3phygrf = <&usb3phy_grf>;
+		rockchip,grf = <&grf>;
+		interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-names = "linestate";
+		clocks = <&cru PCLK_USB3PHY_OTG>, <&cru PCLK_USB3PHY_PIPE>;
+		clock-names = "u3phy-otg", "u3phy-pipe";
+		resets = <&cru SRST_USB3PHY_U2>,
+			 <&cru SRST_USB3PHY_U3>,
+			 <&cru SRST_USB3PHY_PIPE>,
+			 <&cru SRST_USB3OTG_UTMI>,
+			 <&cru SRST_USB3PHY_OTG_P>,
+			 <&cru SRST_USB3PHY_PIPE_P>;
+		reset-names = "u3phy-u2-por", "u3phy-u3-por",
+			      "u3phy-pipe-mac", "u3phy-utmi-mac",
+			      "u3phy-utmi-apb", "u3phy-pipe-apb";
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+		status = "disabled";
+
+		u3phy_utmi: utmi@ff470000 {
+			reg = <0x0 0xff470000 0x0 0x8000>;
+			#phy-cells = <0>;
+			status = "disabled";
+		};
+
+		u3phy_pipe: pipe@ff478000 {
+			reg = <0x0 0xff478000 0x0 0x8000>;
+			#phy-cells = <0>;
+			status = "disabled";
+		};
+	};
+
 	sdmmc: dwmmc@ff500000 {
 		compatible = "rockchip,rk3328-dw-mshc", "rockchip,rk3288-dw-mshc";
 		reg = <0x0 0xff500000 0x0 0x4000>;
@@ -936,6 +977,37 @@
 		status = "disabled";
 	};
 
+	usbdrd3: usb@ff600000 {
+		compatible = "rockchip,rk3328-dwc3", "rockchip,rk3399-dwc3";
+		clocks = <&cru SCLK_USB3OTG_REF>, <&cru ACLK_USB3OTG>,
+			 <&cru SCLK_USB3OTG_SUSPEND>;
+		clock-names = "ref", "bus_early",
+			      "suspend";
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+		clock-ranges;
+		status = "disabled";
+
+		usbdrd_dwc3: dwc3@ff600000 {
+			compatible = "snps,dwc3";
+			reg = <0x0 0xff600000 0x0 0x100000>;
+			interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
+			dr_mode = "otg";
+			phys = <&u3phy_utmi>, <&u3phy_pipe>;
+			phy-names = "usb2-phy", "usb3-phy";
+			phy_type = "utmi_wide";
+			snps,dis_enblslpm_quirk;
+			snps,dis-u2-freeclk-exists-quirk;
+			snps,dis_u2_susphy_quirk;
+			snps,dis_u3_susphy_quirk;
+			snps,dis-del-phy-power-chg-quirk;
+			snps,dis-tx-ipgap-linecheck-quirk;
+			snps,xhci-trb-ent-quirk;
+			status = "disabled";
+		};
+	};
+
 	gic: interrupt-controller@ff811000 {
 		compatible = "arm,gic-400";
 		#interrupt-cells = <3>;
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH 5/5] arm64: dts: rockchip: enable usb3 on rk3328-roc-cc
  2019-10-28 18:22 [PATCH 0/5] add rk3328 usb3 phy driver Peter Geis
                   ` (3 preceding siblings ...)
  2019-10-28 18:22 ` [PATCH 4/5] arm64: dts: rockchip: add usb3 to rk3328 devicetree Peter Geis
@ 2019-10-28 18:22 ` Peter Geis
  2019-10-28 18:41 ` [PATCH 0/5] add rk3328 usb3 phy driver Robin Murphy
  5 siblings, 0 replies; 22+ messages in thread
From: Peter Geis @ 2019-10-28 18:22 UTC (permalink / raw)
  To: heiko; +Cc: linux-rockchip, linux-usb, katsuhiro, robin.murphy, Peter Geis

Enable the usb3 controller and phy on the rk3328-roc-cc device tree.

Signed-off-by: Peter Geis <pgwipeout@gmail.com>
---
 .../arm64/boot/dts/rockchip/rk3328-roc-cc.dts | 21 +++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts b/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts
index bb40c163b05d..f300f3d0f02e 100644
--- a/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts
@@ -328,6 +328,18 @@
 	status = "okay";
 };
 
+&u3phy {
+	status = "okay";
+};
+
+&u3phy_utmi {
+	status = "okay";
+};
+
+&u3phy_pipe {
+	status = "okay";
+};
+
 &uart2 {
 	status = "okay";
 };
@@ -344,6 +356,15 @@
 	status = "okay";
 };
 
+&usbdrd3 {
+	status = "okay";
+};
+
+&usbdrd_dwc3 {
+	dr_mode = "host";
+	status = "okay";
+};
+
 &vop {
 	status = "okay";
 };
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* Re: [PATCH 0/5] add rk3328 usb3 phy driver
  2019-10-28 18:22 [PATCH 0/5] add rk3328 usb3 phy driver Peter Geis
                   ` (4 preceding siblings ...)
  2019-10-28 18:22 ` [PATCH 5/5] arm64: dts: rockchip: enable usb3 on rk3328-roc-cc Peter Geis
@ 2019-10-28 18:41 ` Robin Murphy
  2019-10-30  0:17   ` Robin Murphy
  5 siblings, 1 reply; 22+ messages in thread
From: Robin Murphy @ 2019-10-28 18:41 UTC (permalink / raw)
  To: Peter Geis, heiko, kishon; +Cc: linux-rockchip, linux-usb, katsuhiro

On 28/10/2019 18:22, Peter Geis wrote:
> It took a lot more effort than originally anticipated, but here it is.
> This is the driver from [0], updated to work with the current kernel.
> I've tested it on the rk3328-roc-cc board, both usb 2.0 and usb 3.0
> devices detect on hotplug.

Thanks Peter, I'll try to give this a go on my box for confirmation.

One quick comment is that it might be worth importing the version from 
Rockchip's own kernel tree, as that includes this additional patch which 
looks like a welcome improvement:

https://github.com/rockchip-linux/kernel/commit/12efa9acad65b4c3256683c1ccd769687be3ca56#diff-b6317b3425ac054be551abdcda910b68

Also, as it's a new phy driver, we should keep Kishon (+cc) in the loop 
as the subsystem maintainer.

Robin.

> [0] https://github.com/FireflyTeam/kernel/commits/roc-rk3328-cc/drivers/phy/rockchip/phy-rockchip-inno-usb3.c
> 
> Peter Geis (5):
>    phy: rockchip: add inno-usb3 phy driver
>    dt-bindings: clean up rockchip grf binding document
>    Documentation: bindings: add dt documentation for rockchip usb3 phy
>    arm64: dts: rockchip: add usb3 to rk3328 devicetree
>    arm64: dts: rockchip: enable usb3 on rk3328-roc-cc
> 
>   .../bindings/phy/phy-rockchip-inno-usb3.yaml  |  157 +++
>   .../devicetree/bindings/soc/rockchip/grf.txt  |    8 +-
>   .../devicetree/bindings/usb/rockchip,dwc3.txt |    9 +-
>   .../arm64/boot/dts/rockchip/rk3328-roc-cc.dts |   21 +
>   arch/arm64/boot/dts/rockchip/rk3328.dtsi      |   72 ++
>   drivers/phy/rockchip/Kconfig                  |    9 +
>   drivers/phy/rockchip/Makefile                 |    1 +
>   drivers/phy/rockchip/phy-rockchip-inno-usb3.c | 1107 +++++++++++++++++
>   8 files changed, 1378 insertions(+), 6 deletions(-)
>   create mode 100644 Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb3.yaml
>   create mode 100644 drivers/phy/rockchip/phy-rockchip-inno-usb3.c
> 

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 4/5] arm64: dts: rockchip: add usb3 to rk3328 devicetree
  2019-10-28 18:22 ` [PATCH 4/5] arm64: dts: rockchip: add usb3 to rk3328 devicetree Peter Geis
@ 2019-10-29  1:58   ` Chunfeng Yun
  2019-10-29 15:31     ` Peter Geis
  0 siblings, 1 reply; 22+ messages in thread
From: Chunfeng Yun @ 2019-10-29  1:58 UTC (permalink / raw)
  To: Peter Geis; +Cc: heiko, linux-rockchip, linux-usb, katsuhiro, robin.murphy

On Mon, 2019-10-28 at 18:22 +0000, Peter Geis wrote:
> Now that we have a proper phy driver, we can add the requisite bits to the
> rk3328 device tree.
> Added the u3drd and u3phy nodes.
> 
> Signed-off-by: Peter Geis <pgwipeout@gmail.com>
> ---
>  arch/arm64/boot/dts/rockchip/rk3328.dtsi | 72 ++++++++++++++++++++++++
>  1 file changed, 72 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/rockchip/rk3328.dtsi b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
> index 31cc1541f1f5..072e988ad655 100644
> --- a/arch/arm64/boot/dts/rockchip/rk3328.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
> @@ -805,6 +805,47 @@
>  		};
>  	};
>  
> +	usb3phy_grf: syscon@ff460000 {
> +		compatible = "rockchip,usb3phy-grf", "syscon";
> +		reg = <0x0 0xff460000 0x0 0x1000>;
> +	};
> +
> +	u3phy: usb3-phy@ff470000 {
usb-phy?
> +		compatible = "rockchip,rk3328-u3phy";
> +		reg = <0x0 0xff470000 0x0 0x0>;
It's zero length, may be not necessary, how about use ranges with
parameter ?

> +		rockchip,u3phygrf = <&usb3phy_grf>;
> +		rockchip,grf = <&grf>;
> +		interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>;
> +		interrupt-names = "linestate";
> +		clocks = <&cru PCLK_USB3PHY_OTG>, <&cru PCLK_USB3PHY_PIPE>;
> +		clock-names = "u3phy-otg", "u3phy-pipe";
To me, no need u3phy prefix
> +		resets = <&cru SRST_USB3PHY_U2>,
> +			 <&cru SRST_USB3PHY_U3>,
> +			 <&cru SRST_USB3PHY_PIPE>,
> +			 <&cru SRST_USB3OTG_UTMI>,
> +			 <&cru SRST_USB3PHY_OTG_P>,
> +			 <&cru SRST_USB3PHY_PIPE_P>;
> +		reset-names = "u3phy-u2-por", "u3phy-u3-por",
> +			      "u3phy-pipe-mac", "u3phy-utmi-mac",
> +			      "u3phy-utmi-apb", "u3phy-pipe-apb";
ditto

> +		#address-cells = <2>;
> +		#size-cells = <2>;
> +		ranges;
> +		status = "disabled";
> +
> +		u3phy_utmi: utmi@ff470000 {
usb-phy instead of utmi?

> +			reg = <0x0 0xff470000 0x0 0x8000>;
> +			#phy-cells = <0>;
> +			status = "disabled";
> +		};
> +
> +		u3phy_pipe: pipe@ff478000 {
usb-phy
> +			reg = <0x0 0xff478000 0x0 0x8000>;
> +			#phy-cells = <0>;
> +			status = "disabled";
> +		};
> +	};
> +
>  	sdmmc: dwmmc@ff500000 {
>  		compatible = "rockchip,rk3328-dw-mshc", "rockchip,rk3288-dw-mshc";
>  		reg = <0x0 0xff500000 0x0 0x4000>;
> @@ -936,6 +977,37 @@
>  		status = "disabled";
>  	};
>  
> +	usbdrd3: usb@ff600000 {
> +		compatible = "rockchip,rk3328-dwc3", "rockchip,rk3399-dwc3";
> +		clocks = <&cru SCLK_USB3OTG_REF>, <&cru ACLK_USB3OTG>,
> +			 <&cru SCLK_USB3OTG_SUSPEND>;
> +		clock-names = "ref", "bus_early",
> +			      "suspend";
> +		#address-cells = <2>;
> +		#size-cells = <2>;
> +		ranges;
> +		clock-ranges;
> +		status = "disabled";
> +
> +		usbdrd_dwc3: dwc3@ff600000 {
> +			compatible = "snps,dwc3";
> +			reg = <0x0 0xff600000 0x0 0x100000>;
> +			interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
> +			dr_mode = "otg";
> +			phys = <&u3phy_utmi>, <&u3phy_pipe>;
> +			phy-names = "usb2-phy", "usb3-phy";
> +			phy_type = "utmi_wide";
> +			snps,dis_enblslpm_quirk;
> +			snps,dis-u2-freeclk-exists-quirk;
> +			snps,dis_u2_susphy_quirk;
> +			snps,dis_u3_susphy_quirk;
> +			snps,dis-del-phy-power-chg-quirk;
> +			snps,dis-tx-ipgap-linecheck-quirk;
> +			snps,xhci-trb-ent-quirk;
> +			status = "disabled";
> +		};
> +	};
> +
>  	gic: interrupt-controller@ff811000 {
>  		compatible = "arm,gic-400";
>  		#interrupt-cells = <3>;



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/5] phy: rockchip: add inno-usb3 phy driver
  2019-10-28 18:22 ` [PATCH 1/5] phy: rockchip: add inno-usb3 " Peter Geis
@ 2019-10-29  2:11   ` Chunfeng Yun
  2019-10-29 15:26     ` Peter Geis
  2019-10-30  8:15   ` Heiko Stuebner
  1 sibling, 1 reply; 22+ messages in thread
From: Chunfeng Yun @ 2019-10-29  2:11 UTC (permalink / raw)
  To: Peter Geis; +Cc: heiko, linux-rockchip, linux-usb, katsuhiro, robin.murphy

On Mon, 2019-10-28 at 18:22 +0000, Peter Geis wrote:
> Add the rockchip innosilicon usb3 phy driver, supporting devices such as the rk3328.
> Pulled from:
> https://github.com/FireflyTeam/kernel/blob/roc-rk3328-cc/drivers/phy/rockchip/phy-rockchip-inno-usb3.c
> 
> Signed-off-by: Peter Geis <pgwipeout@gmail.com>
> ---
>  drivers/phy/rockchip/Kconfig                  |    9 +
>  drivers/phy/rockchip/Makefile                 |    1 +
>  drivers/phy/rockchip/phy-rockchip-inno-usb3.c | 1107 +++++++++++++++++
>  3 files changed, 1117 insertions(+)
>  create mode 100644 drivers/phy/rockchip/phy-rockchip-inno-usb3.c
> 
> diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig
> index c454c90cd99e..766407939d4a 100644
> --- a/drivers/phy/rockchip/Kconfig
> +++ b/drivers/phy/rockchip/Kconfig
> @@ -35,6 +35,15 @@ config PHY_ROCKCHIP_INNO_USB2
>  	help
>  	  Support for Rockchip USB2.0 PHY with Innosilicon IP block.
>  
> +config PHY_ROCKCHIP_INNO_USB3
> +	tristate "Rockchip INNO USB 3.0 PHY Driver"
> +	depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
> +	depends on USB_SUPPORT
> +	select GENERIC_PHY
> +	select USB_PHY
> +	help
> +	  Support for Rockchip USB 3.0 PHY with Innosilicon IP block.
> +
>  config PHY_ROCKCHIP_PCIE
>  	tristate "Rockchip PCIe PHY Driver"
>  	depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST
> diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile
> index fd21cbaf40dd..d7b3d16c19ae 100644
> --- a/drivers/phy/rockchip/Makefile
> +++ b/drivers/phy/rockchip/Makefile
> @@ -3,6 +3,7 @@ obj-$(CONFIG_PHY_ROCKCHIP_DP)		+= phy-rockchip-dp.o
>  obj-$(CONFIG_PHY_ROCKCHIP_EMMC)		+= phy-rockchip-emmc.o
>  obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI)	+= phy-rockchip-inno-hdmi.o
>  obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2)	+= phy-rockchip-inno-usb2.o
> +obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB3)	+= phy-rockchip-inno-usb3.o
>  obj-$(CONFIG_PHY_ROCKCHIP_PCIE)		+= phy-rockchip-pcie.o
>  obj-$(CONFIG_PHY_ROCKCHIP_TYPEC)	+= phy-rockchip-typec.o
>  obj-$(CONFIG_PHY_ROCKCHIP_USB)		+= phy-rockchip-usb.o
> diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb3.c b/drivers/phy/rockchip/phy-rockchip-inno-usb3.c
> new file mode 100644
> index 000000000000..31fee8f3a705
> --- /dev/null
> +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb3.c
> @@ -0,0 +1,1107 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Rockchip USB 3.0 PHY with Innosilicon IP block driver
> + *
> + * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/debugfs.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_clk.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <linux/usb/phy.h>
> +#include <linux/uaccess.h>
> +
> +#define U3PHY_PORT_NUM	2
> +#define BIT_WRITEABLE_SHIFT	16
> +#define SCHEDULE_DELAY	(60 * HZ)
> +
> +#define U3PHY_APB_RST	BIT(0)
> +#define U3PHY_POR_RST	BIT(1)
> +#define U3PHY_MAC_RST	BIT(2)
> +
> +struct rockchip_u3phy;
> +struct rockchip_u3phy_port;
> +
> +enum rockchip_u3phy_type {
> +	U3PHY_TYPE_PIPE,
> +	U3PHY_TYPE_UTMI,
> +};
> +
> +enum rockchip_u3phy_pipe_pwr {
> +	PIPE_PWR_P0	= 0,
> +	PIPE_PWR_P1	= 1,
> +	PIPE_PWR_P2	= 2,
> +	PIPE_PWR_P3	= 3,
> +	PIPE_PWR_MAX	= 4,
> +};
> +
> +enum rockchip_u3phy_rest_req {
> +	U3_POR_RSTN	= 0,
> +	U2_POR_RSTN	= 1,
> +	PIPE_MAC_RSTN	= 2,
> +	UTMI_MAC_RSTN	= 3,
> +	PIPE_APB_RSTN	= 4,
> +	UTMI_APB_RSTN	= 5,
> +	U3PHY_RESET_MAX	= 6,
> +};
> +
> +enum rockchip_u3phy_utmi_state {
> +	PHY_UTMI_HS_ONLINE	= 0,
> +	PHY_UTMI_DISCONNECT	= 1,
> +	PHY_UTMI_CONNECT	= 2,
> +	PHY_UTMI_FS_LS_ONLINE	= 4,
> +};
> +
> +/*
> + * @rvalue: reset value
> + * @dvalue: desired value
> + */
> +struct u3phy_reg {
> +	unsigned int	offset;
> +	unsigned int	bitend;
> +	unsigned int	bitstart;
> +	unsigned int	rvalue;
> +	unsigned int	dvalue;
> +};
> +
> +struct rockchip_u3phy_grfcfg {
> +	struct u3phy_reg	um_suspend;
> +	struct u3phy_reg	ls_det_en;
> +	struct u3phy_reg	ls_det_st;
> +	struct u3phy_reg	um_ls;
> +	struct u3phy_reg	um_hstdct;
> +	struct u3phy_reg	u2_only_ctrl;
> +	struct u3phy_reg	u3_disable;
> +	struct u3phy_reg	pp_pwr_st;
> +	struct u3phy_reg	pp_pwr_en[PIPE_PWR_MAX];
> +};
> +
> +/**
> + * struct rockchip_u3phy_apbcfg: usb3-phy apb configuration.
> + * @u2_pre_emp: usb2-phy pre-emphasis tuning.
> + * @u2_pre_emp_sth: usb2-phy pre-emphasis strength tuning.
> + * @u2_odt_tuning: usb2-phy odt 45ohm tuning.
> + */
> +struct rockchip_u3phy_apbcfg {
> +	unsigned int	u2_pre_emp;
> +	unsigned int	u2_pre_emp_sth;
> +	unsigned int	u2_odt_tuning;
> +};
> +
> +struct rockchip_u3phy_cfg {
> +	unsigned int reg;
> +	const struct rockchip_u3phy_grfcfg grfcfg;
> +
> +	int (*phy_pipe_power)(struct rockchip_u3phy *u3phy,
> +			      struct rockchip_u3phy_port *u3phy_port,
> +			      bool on);
> +	int (*phy_tuning)(struct rockchip_u3phy *u3phy,
> +			  struct rockchip_u3phy_port *u3phy_port,
> +			  struct device_node *child_np);
> +};
> +
> +struct rockchip_u3phy_port {
> +	struct phy	*phy;
> +	void __iomem	*base;
> +	unsigned int	index;
> +	unsigned char	type;
> +	bool		suspended;
> +	bool		refclk_25m_quirk;
> +	struct mutex	mutex; /* mutex for updating register */
> +	struct delayed_work	um_sm_work;
> +};
> +
> +struct rockchip_u3phy {
> +	struct device *dev;
> +	struct regmap *u3phy_grf;
> +	struct regmap *grf;
> +	int um_ls_irq;
> +	struct clk **clks;
> +	int num_clocks;
> +	struct dentry *root;
> +	struct gpio_desc *vbus_drv_gpio;
> +	struct reset_control *rsts[U3PHY_RESET_MAX];
> +	struct rockchip_u3phy_apbcfg apbcfg;
> +	const struct rockchip_u3phy_cfg *cfgs;
> +	struct rockchip_u3phy_port ports[U3PHY_PORT_NUM];
> +	struct usb_phy usb_phy;
> +};
> +
> +static inline int param_write(void __iomem *base,
> +			      const struct u3phy_reg *reg, bool desired)
> +{
> +	unsigned int val, mask;
> +	unsigned int tmp = desired ? reg->dvalue : reg->rvalue;
> +	int ret = 0;
> +
> +	mask = GENMASK(reg->bitend, reg->bitstart);
> +	val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT);
> +	ret = regmap_write(base, reg->offset, val);
> +
> +	return ret;
> +}
> +
> +static inline bool param_exped(void __iomem *base,
> +			       const struct u3phy_reg *reg,
> +			       unsigned int value)
> +{
> +	int ret;
> +	unsigned int tmp, orig;
> +	unsigned int mask = GENMASK(reg->bitend, reg->bitstart);
> +
> +	ret = regmap_read(base, reg->offset, &orig);
> +	if (ret)
> +		return false;
> +
> +	tmp = (orig & mask) >> reg->bitstart;
> +	return tmp == value;
> +}
> +
> +static int rockchip_u3phy_usb2_only_show(struct seq_file *s, void *unused)
> +{
> +	struct rockchip_u3phy	*u3phy = s->private;
> +
> +	if (param_exped(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.u2_only_ctrl, 1))
> +		dev_info(u3phy->dev, "u2\n");
> +	else
> +		dev_info(u3phy->dev, "u3\n");
> +
> +	return 0;
> +}
> +
> +static int rockchip_u3phy_usb2_only_open(struct inode *inode,
> +					 struct file *file)
> +{
> +	return single_open(file, rockchip_u3phy_usb2_only_show,
> +			   inode->i_private);
> +}
> +
> +static ssize_t rockchip_u3phy_usb2_only_write(struct file *file,
> +					      const char __user *ubuf,
> +					      size_t count, loff_t *ppos)
> +{
> +	struct seq_file			*s = file->private_data;
> +	struct rockchip_u3phy		*u3phy = s->private;
> +	struct rockchip_u3phy_port	*u3phy_port;
> +	char				buf[32];
> +	u8				index;
> +
> +	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
> +		return -EFAULT;
> +
> +	if (!strncmp(buf, "u3", 2) &&
> +	    param_exped(u3phy->u3phy_grf,
> +			&u3phy->cfgs->grfcfg.u2_only_ctrl, 1)) {
> +		dev_info(u3phy->dev, "Set usb3.0 and usb2.0 mode successfully\n");
> +
> +		gpiod_set_value_cansleep(u3phy->vbus_drv_gpio, 0);
> +
> +		param_write(u3phy->grf,
> +			    &u3phy->cfgs->grfcfg.u3_disable, false);
> +		param_write(u3phy->u3phy_grf,
> +			    &u3phy->cfgs->grfcfg.u2_only_ctrl, false);
> +
> +		for (index = 0; index < U3PHY_PORT_NUM; index++) {
> +			u3phy_port = &u3phy->ports[index];
> +			/* enable u3 rx termimation */
> +			if (u3phy_port->type == U3PHY_TYPE_PIPE)
> +				writel(0x30, u3phy_port->base + 0xd8);
> +		}
> +
> +		atomic_notifier_call_chain(&u3phy->usb_phy.notifier, 0, NULL);
> +
> +		gpiod_set_value_cansleep(u3phy->vbus_drv_gpio, 1);
> +	} else if (!strncmp(buf, "u2", 2) &&
> +		   param_exped(u3phy->u3phy_grf,
> +			       &u3phy->cfgs->grfcfg.u2_only_ctrl, 0)) {
> +		dev_info(u3phy->dev, "Set usb2.0 only mode successfully\n");
> +
> +		gpiod_set_value_cansleep(u3phy->vbus_drv_gpio, 0);
> +
> +		param_write(u3phy->grf,
> +			    &u3phy->cfgs->grfcfg.u3_disable, true);
> +		param_write(u3phy->u3phy_grf,
> +			    &u3phy->cfgs->grfcfg.u2_only_ctrl, true);
> +
> +		for (index = 0; index < U3PHY_PORT_NUM; index++) {
> +			u3phy_port = &u3phy->ports[index];
> +			/* disable u3 rx termimation */
> +			if (u3phy_port->type == U3PHY_TYPE_PIPE)
> +				writel(0x20, u3phy_port->base + 0xd8);
> +		}
> +
> +		atomic_notifier_call_chain(&u3phy->usb_phy.notifier, 0, NULL);
> +
> +		gpiod_set_value_cansleep(u3phy->vbus_drv_gpio, 1);
> +	} else {
> +		dev_info(u3phy->dev, "Same or illegal mode\n");
> +	}
> +
> +	return count;
> +}
> +
> +static const struct file_operations rockchip_u3phy_usb2_only_fops = {
> +	.open			= rockchip_u3phy_usb2_only_open,
> +	.write			= rockchip_u3phy_usb2_only_write,
> +	.read			= seq_read,
> +	.llseek			= seq_lseek,
> +	.release		= single_release,
> +};
> +
> +int rockchip_u3phy_debugfs_init(struct rockchip_u3phy *u3phy)
> +{
> +	struct dentry		*root;
> +	struct dentry		*file;
> +	int			ret;
> +
> +	root = debugfs_create_dir(dev_name(u3phy->dev), NULL);
> +	if (!root) {
> +		ret = -ENOMEM;
> +		goto err0;
> +	}
> +
> +	u3phy->root = root;
> +
> +	file = debugfs_create_file("u3phy_mode", 0644, root,
> +				   u3phy, &rockchip_u3phy_usb2_only_fops);
> +	if (!file) {
> +		ret = -ENOMEM;
> +		goto err1;
> +	}
> +	return 0;
> +
> +err1:
> +	debugfs_remove_recursive(root);
> +err0:
> +	return ret;
> +}
> +
> +static const char *get_rest_name(enum rockchip_u3phy_rest_req rst)
> +{
> +	switch (rst) {
> +	case U2_POR_RSTN:
> +		return "u3phy-u2-por";
> +	case U3_POR_RSTN:
> +		return "u3phy-u3-por";
> +	case PIPE_MAC_RSTN:
> +		return "u3phy-pipe-mac";
> +	case UTMI_MAC_RSTN:
> +		return "u3phy-utmi-mac";
> +	case UTMI_APB_RSTN:
> +		return "u3phy-utmi-apb";
> +	case PIPE_APB_RSTN:
> +		return "u3phy-pipe-apb";
> +	default:
> +		return "invalid";
> +	}
> +}
> +
> +static void rockchip_u3phy_rest_deassert(struct rockchip_u3phy *u3phy,
> +					 unsigned int flag)
> +{
> +	int rst;
> +
> +	if (flag & U3PHY_APB_RST) {
> +		dev_dbg(u3phy->dev, "deassert APB bus interface reset\n");
> +		for (rst = PIPE_APB_RSTN; rst <= UTMI_APB_RSTN; rst++) {
> +			if (u3phy->rsts[rst])
> +				reset_control_deassert(u3phy->rsts[rst]);
> +		}
> +	}
> +
> +	if (flag & U3PHY_POR_RST) {
> +		usleep_range(12, 15);
> +		dev_dbg(u3phy->dev, "deassert u2 and u3 phy power on reset\n");
> +		for (rst = U3_POR_RSTN; rst <= U2_POR_RSTN; rst++) {
> +			if (u3phy->rsts[rst])
> +				reset_control_deassert(u3phy->rsts[rst]);
> +		}
> +	}
> +
> +	if (flag & U3PHY_MAC_RST) {
> +		usleep_range(1200, 1500);
> +		dev_dbg(u3phy->dev, "deassert pipe and utmi MAC reset\n");
> +		for (rst = PIPE_MAC_RSTN; rst <= UTMI_MAC_RSTN; rst++)
> +			if (u3phy->rsts[rst])
> +				reset_control_deassert(u3phy->rsts[rst]);
> +	}
> +}
> +
> +static void rockchip_u3phy_rest_assert(struct rockchip_u3phy *u3phy)
> +{
> +	int rst;
> +
> +	dev_dbg(u3phy->dev, "assert u3phy reset\n");
> +	for (rst = 0; rst < U3PHY_RESET_MAX; rst++)
> +		if (u3phy->rsts[rst])
> +			reset_control_assert(u3phy->rsts[rst]);
> +}
> +
> +static int rockchip_u3phy_clk_enable(struct rockchip_u3phy *u3phy)
> +{
> +	int ret, clk;
> +
> +	for (clk = 0; clk < u3phy->num_clocks && u3phy->clks[clk]; clk++) {
> +		ret = clk_prepare_enable(u3phy->clks[clk]);
> +		if (ret)
> +			goto err_disable_clks;
> +	}
> +	return 0;
> +
> +err_disable_clks:
> +	while (--clk >= 0)
> +		clk_disable_unprepare(u3phy->clks[clk]);
> +	return ret;
> +}
> +
> +static void rockchip_u3phy_clk_disable(struct rockchip_u3phy *u3phy)
> +{
> +	int clk;
> +
> +	for (clk = u3phy->num_clocks - 1; clk >= 0; clk--)
> +		if (u3phy->clks[clk])
> +			clk_disable_unprepare(u3phy->clks[clk]);
> +}
> +
> +static int rockchip_u3phy_init(struct phy *phy)
> +{
> +	return 0;
> +}
> +
> +static int rockchip_u3phy_exit(struct phy *phy)
> +{
> +	return 0;
> +}
> +
> +static int rockchip_u3phy_power_on(struct phy *phy)
> +{
> +	struct rockchip_u3phy_port *u3phy_port = phy_get_drvdata(phy);
> +	struct rockchip_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
> +	int ret;
> +
> +	dev_info(&u3phy_port->phy->dev, "u3phy %s power on\n",
> +		 (u3phy_port->type == U3PHY_TYPE_UTMI) ? "u2" : "u3");
> +
> +	if (!u3phy_port->suspended)
> +		return 0;
> +
> +	ret = rockchip_u3phy_clk_enable(u3phy);
> +	if (ret)
> +		return ret;
> +
> +	if (u3phy_port->type == U3PHY_TYPE_UTMI) {
> +		param_write(u3phy->u3phy_grf,
> +			    &u3phy->cfgs->grfcfg.um_suspend, false);
> +	} else {
> +		/* current in p2 ? */
> +		if (param_exped(u3phy->u3phy_grf,
> +				&u3phy->cfgs->grfcfg.pp_pwr_st, PIPE_PWR_P2))
> +			goto done;
> +
> +		if (u3phy->cfgs->phy_pipe_power) {
> +			dev_dbg(u3phy->dev, "do pipe power up\n");
> +			u3phy->cfgs->phy_pipe_power(u3phy, u3phy_port, true);
> +		}
> +
> +		/* exit to p0 */
> +		param_write(u3phy->u3phy_grf,
> +			    &u3phy->cfgs->grfcfg.pp_pwr_en[PIPE_PWR_P0], true);
> +		usleep_range(90, 100);
> +
> +		/* enter to p2 from p0 */
> +		param_write(u3phy->u3phy_grf,
> +			    &u3phy->cfgs->grfcfg.pp_pwr_en[PIPE_PWR_P2],
> +			    false);
> +		udelay(3);
> +	}
> +
> +done:
> +	u3phy_port->suspended = false;
> +	return 0;
> +}
> +
> +static int rockchip_u3phy_power_off(struct phy *phy)
> +{
> +	struct rockchip_u3phy_port *u3phy_port = phy_get_drvdata(phy);
> +	struct rockchip_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
> +
> +	dev_info(&u3phy_port->phy->dev, "u3phy %s power off\n",
> +		 (u3phy_port->type == U3PHY_TYPE_UTMI) ? "u2" : "u3");
> +
> +	if (u3phy_port->suspended)
> +		return 0;
> +
> +	if (u3phy_port->type == U3PHY_TYPE_UTMI) {
> +		param_write(u3phy->u3phy_grf,
> +			    &u3phy->cfgs->grfcfg.um_suspend, true);
> +	} else {
> +		/* current in p3 ? */
> +		if (param_exped(u3phy->u3phy_grf,
> +				&u3phy->cfgs->grfcfg.pp_pwr_st, PIPE_PWR_P3))
> +			goto done;
> +
> +		/* exit to p0 */
> +		param_write(u3phy->u3phy_grf,
> +			    &u3phy->cfgs->grfcfg.pp_pwr_en[PIPE_PWR_P0], true);
> +		udelay(2);
> +
> +		/* enter to p3 from p0 */
> +		param_write(u3phy->u3phy_grf,
> +			    &u3phy->cfgs->grfcfg.pp_pwr_en[PIPE_PWR_P3], true);
> +		udelay(6);
> +
> +		if (u3phy->cfgs->phy_pipe_power) {
> +			dev_dbg(u3phy->dev, "do pipe power down\n");
> +			u3phy->cfgs->phy_pipe_power(u3phy, u3phy_port, false);
> +		}
> +	}
> +
> +done:
> +	rockchip_u3phy_clk_disable(u3phy);
> +	u3phy_port->suspended = true;
> +	return 0;
> +}
> +
> +static __maybe_unused
> +struct phy *rockchip_u3phy_xlate(struct device *dev,
> +				 struct of_phandle_args *args)
> +{
> +	struct rockchip_u3phy *u3phy = dev_get_drvdata(dev);
> +	struct rockchip_u3phy_port *u3phy_port = NULL;
> +	struct device_node *phy_np = args->np;
> +	int index;
> +
> +	if (args->args_count != 1) {
> +		dev_err(dev, "invalid number of cells in 'phy' property\n");
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	for (index = 0; index < U3PHY_PORT_NUM; index++) {
> +		if (phy_np == u3phy->ports[index].phy->dev.of_node) {
> +			u3phy_port = &u3phy->ports[index];
> +			break;
> +		}
> +	}
> +
> +	if (!u3phy_port) {
> +		dev_err(dev, "failed to find appropriate phy\n");
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	return u3phy_port->phy;
> +}
> +
> +static struct phy_ops rockchip_u3phy_ops = {
> +	.init		= rockchip_u3phy_init,
> +	.exit		= rockchip_u3phy_exit,
> +	.power_on	= rockchip_u3phy_power_on,
> +	.power_off	= rockchip_u3phy_power_off,
> +	.owner		= THIS_MODULE,
> +};
> +
> +/*
> + * The function manage host-phy port state and suspend/resume phy port
> + * to save power automatically.
> + *
> + * we rely on utmi_linestate and utmi_hostdisconnect to identify whether
> + * devices is disconnect or not. Besides, we do not need care it is FS/LS
> + * disconnected or HS disconnected, actually, we just only need get the
> + * device is disconnected at last through rearm the delayed work,
> + * to suspend the phy port in _PHY_STATE_DISCONNECT_ case.
> + */
> +static void rockchip_u3phy_um_sm_work(struct work_struct *work)
> +{
> +	struct rockchip_u3phy_port *u3phy_port =
> +		container_of(work, struct rockchip_u3phy_port, um_sm_work.work);
> +	struct rockchip_u3phy *u3phy =
> +		dev_get_drvdata(u3phy_port->phy->dev.parent);
> +	unsigned int sh = u3phy->cfgs->grfcfg.um_hstdct.bitend -
> +			u3phy->cfgs->grfcfg.um_hstdct.bitstart + 1;
> +	unsigned int ul, uhd, state;
> +	unsigned int ul_mask, uhd_mask;
> +	int ret;
> +
> +	mutex_lock(&u3phy_port->mutex);
> +
> +	ret = regmap_read(u3phy->u3phy_grf,
> +			  u3phy->cfgs->grfcfg.um_ls.offset, &ul);
> +	if (ret < 0)
> +		goto next_schedule;
> +
> +	ret = regmap_read(u3phy->u3phy_grf,
> +			  u3phy->cfgs->grfcfg.um_hstdct.offset, &uhd);
> +	if (ret < 0)
> +		goto next_schedule;
> +
> +	uhd_mask = GENMASK(u3phy->cfgs->grfcfg.um_hstdct.bitend,
> +			   u3phy->cfgs->grfcfg.um_hstdct.bitstart);
> +	ul_mask = GENMASK(u3phy->cfgs->grfcfg.um_ls.bitend,
> +			  u3phy->cfgs->grfcfg.um_ls.bitstart);
> +
> +	/* stitch on um_ls and um_hstdct as phy state */
> +	state = ((uhd & uhd_mask) >> u3phy->cfgs->grfcfg.um_hstdct.bitstart) |
> +		(((ul & ul_mask) >> u3phy->cfgs->grfcfg.um_ls.bitstart) << sh);
> +
> +	switch (state) {
> +	case PHY_UTMI_HS_ONLINE:
> +		dev_dbg(&u3phy_port->phy->dev, "HS online\n");
> +		break;
> +	case PHY_UTMI_FS_LS_ONLINE:
> +		/*
> +		 * For FS/LS device, the online state share with connect state
> +		 * from um_ls and um_hstdct register, so we distinguish
> +		 * them via suspended flag.
> +		 *
> +		 * Plus, there are two cases, one is D- Line pull-up, and D+
> +		 * line pull-down, the state is 4; another is D+ line pull-up,
> +		 * and D- line pull-down, the state is 2.
> +		 */
> +		if (!u3phy_port->suspended) {
> +			/* D- line pull-up, D+ line pull-down */
> +			dev_dbg(&u3phy_port->phy->dev, "FS/LS online\n");
> +			break;
> +		}
> +		/* fall through */
> +	case PHY_UTMI_CONNECT:
> +		if (u3phy_port->suspended) {
> +			dev_dbg(&u3phy_port->phy->dev, "Connected\n");
> +			rockchip_u3phy_power_on(u3phy_port->phy);
> +			u3phy_port->suspended = false;
> +		} else {
> +			/* D+ line pull-up, D- line pull-down */
> +			dev_dbg(&u3phy_port->phy->dev, "FS/LS online\n");
> +		}
> +		break;
> +	case PHY_UTMI_DISCONNECT:
> +		if (!u3phy_port->suspended) {
> +			dev_dbg(&u3phy_port->phy->dev, "Disconnected\n");
> +			rockchip_u3phy_power_off(u3phy_port->phy);
> +			u3phy_port->suspended = true;
> +		}
> +
> +		/*
> +		 * activate the linestate detection to get the next device
> +		 * plug-in irq.
> +		 */
> +		param_write(u3phy->u3phy_grf,
> +			    &u3phy->cfgs->grfcfg.ls_det_st, true);
> +		param_write(u3phy->u3phy_grf,
> +			    &u3phy->cfgs->grfcfg.ls_det_en, true);
> +
> +		/*
> +		 * we don't need to rearm the delayed work when the phy port
> +		 * is suspended.
> +		 */
> +		mutex_unlock(&u3phy_port->mutex);
> +		return;
> +	default:
> +		dev_dbg(&u3phy_port->phy->dev, "unknown phy state\n");
> +		break;
> +	}
> +
> +next_schedule:
> +	mutex_unlock(&u3phy_port->mutex);
> +	schedule_delayed_work(&u3phy_port->um_sm_work, SCHEDULE_DELAY);
> +}
> +
> +static irqreturn_t rockchip_u3phy_um_ls_irq(int irq, void *data)
> +{
> +	struct rockchip_u3phy_port *u3phy_port = data;
> +	struct rockchip_u3phy *u3phy =
> +		dev_get_drvdata(u3phy_port->phy->dev.parent);
> +
> +	if (!param_exped(u3phy->u3phy_grf,
> +			 &u3phy->cfgs->grfcfg.ls_det_st,
> +			 u3phy->cfgs->grfcfg.ls_det_st.dvalue))
> +		return IRQ_NONE;
> +
> +	dev_dbg(u3phy->dev, "utmi linestate interrupt\n");
> +	mutex_lock(&u3phy_port->mutex);
> +
> +	/* disable linestate detect irq and clear its status */
> +	param_write(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.ls_det_en, false);
> +	param_write(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.ls_det_st, true);
> +
> +	mutex_unlock(&u3phy_port->mutex);
> +
> +	/*
> +	 * In this case for host phy, a new device is plugged in, meanwhile,
> +	 * if the phy port is suspended, we need rearm the work to resume it
> +	 * and mange its states; otherwise, we just return irq handled.
> +	 */
> +	if (u3phy_port->suspended) {
> +		dev_dbg(u3phy->dev, "schedule utmi sm work\n");
> +		rockchip_u3phy_um_sm_work(&u3phy_port->um_sm_work.work);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int rockchip_u3phy_parse_dt(struct rockchip_u3phy *u3phy,
> +				   struct platform_device *pdev)
> +
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	int ret, i, clk;
> +
> +	u3phy->um_ls_irq = platform_get_irq_byname(pdev, "linestate");
> +	if (u3phy->um_ls_irq < 0) {
> +		dev_err(dev, "get utmi linestate irq failed\n");
> +		return -ENXIO;
> +	}
> +
> +	u3phy->vbus_drv_gpio = devm_gpiod_get_optional(dev, "vbus-drv",
> +						       GPIOD_OUT_HIGH);
> +
use fixed regulator?

> +	if (!u3phy->vbus_drv_gpio) {
> +		dev_warn(&pdev->dev, "vbus_drv is not assigned\n");
> +	} else if (IS_ERR(u3phy->vbus_drv_gpio)) {
> +		dev_err(&pdev->dev, "failed to get vbus_drv\n");
> +		return PTR_ERR(u3phy->vbus_drv_gpio);
> +	}
> +
> +	u3phy->num_clocks = of_clk_get_parent_count(np);
> +	if (u3phy->num_clocks == 0)
> +		dev_warn(&pdev->dev, "no clks found in dt\n");
> +
> +	u3phy->clks = devm_kcalloc(dev, u3phy->num_clocks,
> +				  sizeof(struct clk *), GFP_KERNEL);
> +
> +	for (clk = 0; clk < u3phy->num_clocks; clk++) {
> +		u3phy->clks[clk] = of_clk_get(np, clk);
> +		if (IS_ERR(u3phy->clks[clk])) {
> +			ret = PTR_ERR(u3phy->clks[clk]);
> +			if (ret == -EPROBE_DEFER)
> +				goto err_put_clks;
> +			dev_err(&pdev->dev, "failed to get clks, %i\n",
> +				ret);
> +			u3phy->clks[clk] = NULL;
> +			break;
> +		}
> +	}
> +
> +	for (i = 0; i < U3PHY_RESET_MAX; i++) {
> +		u3phy->rsts[i] = devm_reset_control_get(dev, get_rest_name(i));
> +		if (IS_ERR(u3phy->rsts[i])) {
> +			dev_info(dev, "no %s reset control specified\n",
> +				 get_rest_name(i));
> +			u3phy->rsts[i] = NULL;
> +		}
> +	}
> +
> +	return 0;
> +
> +err_put_clks:
> +	while (--clk >= 0)
> +		clk_put(u3phy->clks[clk]);
> +	return ret;
> +}
> +
> +static int rockchip_u3phy_port_init(struct rockchip_u3phy *u3phy,
> +				    struct rockchip_u3phy_port *u3phy_port,
> +				    struct device_node *child_np)
> +{
> +	struct resource res;
> +	struct phy *phy;
> +	int ret;
> +
> +	dev_dbg(u3phy->dev, "u3phy port initialize\n");
> +
> +	mutex_init(&u3phy_port->mutex);
> +	u3phy_port->suspended = true; /* initial status */
> +
> +	phy = devm_phy_create(u3phy->dev, child_np, &rockchip_u3phy_ops);
> +	if (IS_ERR(phy)) {
> +		dev_err(u3phy->dev, "failed to create phy\n");
> +		return PTR_ERR(phy);
> +	}
> +
> +	u3phy_port->phy = phy;
> +
> +	ret = of_address_to_resource(child_np, 0, &res);
> +	if (ret) {
> +		dev_err(u3phy->dev, "failed to get address resource(np-%s)\n",
> +			child_np->name);
> +		return ret;
> +	}
> +
> +	u3phy_port->base = devm_ioremap_resource(&u3phy_port->phy->dev, &res);
> +	if (IS_ERR(u3phy_port->base)) {
> +		dev_err(u3phy->dev, "failed to remap phy regs\n");
> +		return PTR_ERR(u3phy_port->base);
> +	}
> +
> +	if (!of_node_cmp(child_np->name, "pipe")) {
> +		u3phy_port->type = U3PHY_TYPE_PIPE;
> +		u3phy_port->refclk_25m_quirk =
> +			of_property_read_bool(child_np,
> +					      "rockchip,refclk-25m-quirk");
> +	} else {
> +		u3phy_port->type = U3PHY_TYPE_UTMI;
> +		INIT_DELAYED_WORK(&u3phy_port->um_sm_work,
> +				  rockchip_u3phy_um_sm_work);
> +
> +		ret = devm_request_threaded_irq(u3phy->dev, u3phy->um_ls_irq,
> +						NULL, rockchip_u3phy_um_ls_irq,
> +						IRQF_ONESHOT, "rockchip_u3phy",
> +						u3phy_port);
> +		if (ret) {
> +			dev_err(u3phy->dev, "failed to request utmi linestate irq handle\n");
> +			return ret;
> +		}
> +	}
> +
> +	if (u3phy->cfgs->phy_tuning) {
> +		dev_dbg(u3phy->dev, "do u3phy tuning\n");
> +		ret = u3phy->cfgs->phy_tuning(u3phy, u3phy_port, child_np);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	phy_set_drvdata(u3phy_port->phy, u3phy_port);
> +	return 0;
> +}
> +
> +static int rockchip_u3phy_on_init(struct usb_phy *usb_phy)
> +{
> +	struct rockchip_u3phy *u3phy =
> +		container_of(usb_phy, struct rockchip_u3phy, usb_phy);
> +
> +	rockchip_u3phy_rest_deassert(u3phy, U3PHY_POR_RST | U3PHY_MAC_RST);
> +	return 0;
> +}
> +
> +static void rockchip_u3phy_on_shutdown(struct usb_phy *usb_phy)
> +{
> +	struct rockchip_u3phy *u3phy =
> +		container_of(usb_phy, struct rockchip_u3phy, usb_phy);
> +	int rst;
> +
> +	for (rst = 0; rst < U3PHY_RESET_MAX; rst++)
> +		if (u3phy->rsts[rst] && rst != UTMI_APB_RSTN &&
> +		    rst != PIPE_APB_RSTN)
> +			reset_control_assert(u3phy->rsts[rst]);
> +	udelay(1);
> +}
> +
> +static int rockchip_u3phy_on_disconnect(struct usb_phy *usb_phy,
> +					enum usb_device_speed speed)
> +{
> +	struct rockchip_u3phy *u3phy =
> +		container_of(usb_phy, struct rockchip_u3phy, usb_phy);
> +
> +	dev_info(u3phy->dev, "%s device has disconnected\n",
> +		 (speed == USB_SPEED_SUPER) ? "U3" : "UW/U2/U1.1/U1");
> +
> +	if (speed == USB_SPEED_SUPER)
> +		atomic_notifier_call_chain(&usb_phy->notifier, 0, NULL);
> +
> +	return 0;
> +}
> +
> +static int rockchip_u3phy_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	struct device_node *child_np;
> +	struct phy_provider *provider;
> +	struct rockchip_u3phy *u3phy;
> +	const struct rockchip_u3phy_cfg *phy_cfgs;
> +	const struct of_device_id *match;
> +	unsigned int reg[2];
> +	int index, ret;
> +
> +	match = of_match_device(dev->driver->of_match_table, dev);
> +	if (!match || !match->data) {
> +		dev_err(dev, "phy-cfgs are not assigned!\n");
> +		return -EINVAL;
> +	}
> +
> +	u3phy = devm_kzalloc(dev, sizeof(*u3phy), GFP_KERNEL);
> +	if (!u3phy)
> +		return -ENOMEM;
> +
> +	u3phy->u3phy_grf =
> +		syscon_regmap_lookup_by_phandle(np, "rockchip,u3phygrf");
> +	if (IS_ERR(u3phy->u3phy_grf))
> +		return PTR_ERR(u3phy->u3phy_grf);
> +
> +	u3phy->grf =
> +		syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
> +	if (IS_ERR(u3phy->grf)) {
> +		dev_err(dev, "Missing rockchip,grf property\n");
> +		return PTR_ERR(u3phy->grf);
> +	}
> +
> +	if (of_property_read_u32_array(np, "reg", reg, 2)) {
> +		dev_err(dev, "the reg property is not assigned in %s node\n",
> +			np->name);
> +		return -EINVAL;
> +	}
> +
> +	u3phy->dev = dev;
> +	phy_cfgs = match->data;
> +	platform_set_drvdata(pdev, u3phy);
> +
> +	/* find out a proper config which can be matched with dt. */
> +	index = 0;
> +	while (phy_cfgs[index].reg) {
> +		if (phy_cfgs[index].reg == reg[1]) {
> +			u3phy->cfgs = &phy_cfgs[index];
> +			break;
> +		}
> +
> +		++index;
> +	}
> +
> +	if (!u3phy->cfgs) {
> +		dev_err(dev, "no phy-cfgs can be matched with %s node\n",
> +			np->name);
> +		return -EINVAL;
> +	}
> +
> +	ret = rockchip_u3phy_parse_dt(u3phy, pdev);
> +	if (ret) {
> +		dev_err(dev, "parse dt failed, ret(%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = rockchip_u3phy_clk_enable(u3phy);
> +	if (ret) {
> +		dev_err(dev, "clk enable failed, ret(%d)\n", ret);
> +		return ret;
> +	}
> +
> +	rockchip_u3phy_rest_assert(u3phy);
> +	rockchip_u3phy_rest_deassert(u3phy, U3PHY_APB_RST | U3PHY_POR_RST);
> +
> +	index = 0;
> +	for_each_available_child_of_node(np, child_np) {
> +		struct rockchip_u3phy_port *u3phy_port = &u3phy->ports[index];
> +
> +		u3phy_port->index = index;
> +		ret = rockchip_u3phy_port_init(u3phy, u3phy_port, child_np);
> +		if (ret) {
> +			dev_err(dev, "u3phy port init failed,ret(%d)\n", ret);
> +			goto put_child;
> +		}
> +
> +		/* to prevent out of boundary */
> +		if (++index >= U3PHY_PORT_NUM)
> +			break;
> +	}
> +
> +	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
> +	if (IS_ERR_OR_NULL(provider))
> +		goto put_child;
> +
> +	rockchip_u3phy_rest_deassert(u3phy, U3PHY_MAC_RST);
> +	rockchip_u3phy_clk_disable(u3phy);
> +
> +	u3phy->usb_phy.dev = dev;
> +	u3phy->usb_phy.init = rockchip_u3phy_on_init;
> +	u3phy->usb_phy.shutdown = rockchip_u3phy_on_shutdown;
> +	u3phy->usb_phy.notify_disconnect = rockchip_u3phy_on_disconnect;
> +	usb_add_phy(&u3phy->usb_phy, USB_PHY_TYPE_USB3);
> +	ATOMIC_INIT_NOTIFIER_HEAD(&u3phy->usb_phy.notifier);
> +
> +	rockchip_u3phy_debugfs_init(u3phy);
> +
> +	dev_info(dev, "Rockchip u3phy initialized successfully\n");
> +	return 0;
> +
> +put_child:
> +	of_node_put(child_np);
> +	return ret;
> +}
> +
> +static int rk3328_u3phy_pipe_power(struct rockchip_u3phy *u3phy,
> +				   struct rockchip_u3phy_port *u3phy_port,
> +				   bool on)
> +{
> +	unsigned int reg;
> +
> +	if (on) {
> +		reg = readl(u3phy_port->base + 0x1a8);
> +		reg &= ~BIT(4); /* ldo power up */
> +		writel(reg, u3phy_port->base + 0x1a8);
> +
> +		reg = readl(u3phy_port->base + 0x044);
> +		reg &= ~BIT(4); /* bg power on */
> +		writel(reg, u3phy_port->base + 0x044);
> +
> +		reg = readl(u3phy_port->base + 0x150);
> +		reg |= BIT(6); /* tx bias enable */
> +		writel(reg, u3phy_port->base + 0x150);
> +
> +		reg = readl(u3phy_port->base + 0x080);
> +		reg &= ~BIT(2); /* tx cm power up */
> +		writel(reg, u3phy_port->base + 0x080);
> +
> +		reg = readl(u3phy_port->base + 0x0c0);
> +		/* tx obs enable and rx cm enable */
> +		reg |= (BIT(3) | BIT(4));
> +		writel(reg, u3phy_port->base + 0x0c0);
> +
> +		udelay(1);
> +	} else {
> +		reg = readl(u3phy_port->base + 0x1a8);
> +		reg |= BIT(4); /* ldo power down */
> +		writel(reg, u3phy_port->base + 0x1a8);
> +
> +		reg = readl(u3phy_port->base + 0x044);
> +		reg |= BIT(4); /* bg power down */
> +		writel(reg, u3phy_port->base + 0x044);
> +
> +		reg = readl(u3phy_port->base + 0x150);
> +		reg &= ~BIT(6); /* tx bias disable */
> +		writel(reg, u3phy_port->base + 0x150);
> +
> +		reg = readl(u3phy_port->base + 0x080);
> +		reg |= BIT(2); /* tx cm power down */
> +		writel(reg, u3phy_port->base + 0x080);
> +
> +		reg = readl(u3phy_port->base + 0x0c0);
> +		/* tx obs disable and rx cm disable */
> +		reg &= ~(BIT(3) | BIT(4));
> +		writel(reg, u3phy_port->base + 0x0c0);
> +	}
Try to avoid magic number

> +
> +	return 0;
> +}
> +
> +static int rk3328_u3phy_tuning(struct rockchip_u3phy *u3phy,
> +			       struct rockchip_u3phy_port *u3phy_port,
> +			       struct device_node *child_np)
> +{
> +	if (u3phy_port->type == U3PHY_TYPE_UTMI) {
> +		/*
> +		 * For rk3328 SoC, pre-emphasis and pre-emphasis strength must
> +		 * be written as one fixed value as below.
> +		 *
> +		 * Dissimilarly, the odt 45ohm value should be flexibly tuninged
> +		 * for the different boards to adjust HS eye height, so its
> +		 * value can be assigned in DT in code design.
> +		 */
> +
> +		/* {bits[2:0]=111}: always enable pre-emphasis */
> +		u3phy->apbcfg.u2_pre_emp = 0x0f;
> +
> +		/* {bits[5:3]=000}: pre-emphasis strength as the weakest */
> +		u3phy->apbcfg.u2_pre_emp_sth = 0x41;
> +
> +		/* {bits[4:0]=10101}: odt 45ohm tuning */
> +		u3phy->apbcfg.u2_odt_tuning = 0xb5;
> +		/* optional override of the odt 45ohm tuning */
> +		of_property_read_u32(child_np, "rockchip,odt-val-tuning",
> +				     &u3phy->apbcfg.u2_odt_tuning);
> +
> +		writel(u3phy->apbcfg.u2_pre_emp, u3phy_port->base + 0x030);
> +		writel(u3phy->apbcfg.u2_pre_emp_sth, u3phy_port->base + 0x040);
> +		writel(u3phy->apbcfg.u2_odt_tuning, u3phy_port->base + 0x11c);
> +	} else if (u3phy_port->type == U3PHY_TYPE_PIPE) {
> +		if (u3phy_port->refclk_25m_quirk) {
> +			dev_dbg(u3phy->dev, "switch to 25m refclk\n");
> +			/* ref clk switch to 25M */
> +			writel(0x64, u3phy_port->base + 0x11c);
> +			writel(0x64, u3phy_port->base + 0x028);
> +			writel(0x01, u3phy_port->base + 0x020);
> +			writel(0x21, u3phy_port->base + 0x030);
> +			writel(0x06, u3phy_port->base + 0x108);
> +			writel(0x00, u3phy_port->base + 0x118);
> +		} else {
> +			/* configure for 24M ref clk */
> +			writel(0x80, u3phy_port->base + 0x10c);
> +			writel(0x01, u3phy_port->base + 0x118);
> +			writel(0x38, u3phy_port->base + 0x11c);
> +			writel(0x83, u3phy_port->base + 0x020);
> +			writel(0x02, u3phy_port->base + 0x108);
> +		}
> +
> +		/* Enable SSC */
> +		udelay(3);
> +		writel(0x08, u3phy_port->base + 0x000);
> +		writel(0x0c, u3phy_port->base + 0x120);
> +
> +		/* Tuning Rx for compliance RJTL test */
> +		writel(0x70, u3phy_port->base + 0x150);
> +		writel(0x12, u3phy_port->base + 0x0c8);
> +		writel(0x05, u3phy_port->base + 0x148);
> +		writel(0x08, u3phy_port->base + 0x068);
> +		writel(0xf0, u3phy_port->base + 0x1c4);
> +		writel(0xff, u3phy_port->base + 0x070);
> +		writel(0x0f, u3phy_port->base + 0x06c);
> +		writel(0xe0, u3phy_port->base + 0x060);
> +
> +		/*
> +		 * Tuning Tx to increase the bias current
> +		 * used in TX driver and RX EQ, it can
> +		 * also increase the voltage of LFPS.
> +		 */
> +		writel(0x08, u3phy_port->base + 0x180);
> +	} else {
> +		dev_err(u3phy->dev, "invalid u3phy port type\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct rockchip_u3phy_cfg rk3328_u3phy_cfgs[] = {
> +	{
> +		.reg		= 0xff470000,
> +		.grfcfg		= {
> +			.um_suspend	= { 0x0004, 15, 0, 0x1452, 0x15d1 },
> +			.u2_only_ctrl	= { 0x0020, 15, 15, 0, 1 },
> +			.um_ls		= { 0x0030, 5, 4, 0, 1 },
> +			.um_hstdct	= { 0x0030, 7, 7, 0, 1 },
> +			.ls_det_en	= { 0x0040, 0, 0, 0, 1 },
> +			.ls_det_st	= { 0x0044, 0, 0, 0, 1 },
> +			.pp_pwr_st	= { 0x0034, 14, 13, 0, 0},
> +			.pp_pwr_en	= { {0x0020, 14, 0, 0x0014, 0x0005},
> +					    {0x0020, 14, 0, 0x0014, 0x000d},
> +					    {0x0020, 14, 0, 0x0014, 0x0015},
> +					    {0x0020, 14, 0, 0x0014, 0x001d} },
> +			.u3_disable	= { 0x04c4, 15, 0, 0x1100, 0x101},
> +		},
> +		.phy_pipe_power	= rk3328_u3phy_pipe_power,
> +		.phy_tuning	= rk3328_u3phy_tuning,
> +	},
> +	{ /* sentinel */ }
> +};
> +
> +static const struct of_device_id rockchip_u3phy_dt_match[] = {
> +	{ .compatible = "rockchip,rk3328-u3phy", .data = &rk3328_u3phy_cfgs },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, rockchip_u3phy_dt_match);
> +
> +static struct platform_driver rockchip_u3phy_driver = {
> +	.probe		= rockchip_u3phy_probe,
> +	.driver		= {
> +		.name	= "rockchip-u3phy",
> +		.of_match_table = rockchip_u3phy_dt_match,
> +	},
> +};
> +module_platform_driver(rockchip_u3phy_driver);
> +
> +MODULE_AUTHOR("Frank Wang <frank.wang@rock-chips.com>");
> +MODULE_AUTHOR("William Wu <william.wu@rock-chips.com>");
> +MODULE_DESCRIPTION("Rockchip USB 3.0 PHY driver");
> +MODULE_LICENSE("GPL v2");



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/5] phy: rockchip: add inno-usb3 phy driver
  2019-10-29  2:11   ` Chunfeng Yun
@ 2019-10-29 15:26     ` Peter Geis
  2019-10-30  2:59       ` Chunfeng Yun
  0 siblings, 1 reply; 22+ messages in thread
From: Peter Geis @ 2019-10-29 15:26 UTC (permalink / raw)
  To: Chunfeng Yun
  Cc: katsuhiro, linux-rockchip, linux-usb, Robin Murphy, Heiko Stuebner

On Mon, Oct 28, 2019 at 10:12 PM Chunfeng Yun <chunfeng.yun@mediatek.com> wrote:
>
> On Mon, 2019-10-28 at 18:22 +0000, Peter Geis wrote:
> > Add the rockchip innosilicon usb3 phy driver, supporting devices such as the rk3328.
> > Pulled from:
> > https://github.com/FireflyTeam/kernel/blob/roc-rk3328-cc/drivers/phy/rockchip/phy-rockchip-inno-usb3.c
> >
> > Signed-off-by: Peter Geis <pgwipeout@gmail.com>
> > ---
> >  drivers/phy/rockchip/Kconfig                  |    9 +
> >  drivers/phy/rockchip/Makefile                 |    1 +
> >  drivers/phy/rockchip/phy-rockchip-inno-usb3.c | 1107 +++++++++++++++++
> >  3 files changed, 1117 insertions(+)
> >  create mode 100644 drivers/phy/rockchip/phy-rockchip-inno-usb3.c
> >
> > diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig
> > index c454c90cd99e..766407939d4a 100644
> > --- a/drivers/phy/rockchip/Kconfig
> > +++ b/drivers/phy/rockchip/Kconfig
> > @@ -35,6 +35,15 @@ config PHY_ROCKCHIP_INNO_USB2
> >       help
> >         Support for Rockchip USB2.0 PHY with Innosilicon IP block.
> >
> > +config PHY_ROCKCHIP_INNO_USB3
> > +     tristate "Rockchip INNO USB 3.0 PHY Driver"
> > +     depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
> > +     depends on USB_SUPPORT
> > +     select GENERIC_PHY
> > +     select USB_PHY
> > +     help
> > +       Support for Rockchip USB 3.0 PHY with Innosilicon IP block.
> > +
> >  config PHY_ROCKCHIP_PCIE
> >       tristate "Rockchip PCIe PHY Driver"
> >       depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST
> > diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile
> > index fd21cbaf40dd..d7b3d16c19ae 100644
> > --- a/drivers/phy/rockchip/Makefile
> > +++ b/drivers/phy/rockchip/Makefile
> > @@ -3,6 +3,7 @@ obj-$(CONFIG_PHY_ROCKCHIP_DP)         += phy-rockchip-dp.o
> >  obj-$(CONFIG_PHY_ROCKCHIP_EMMC)              += phy-rockchip-emmc.o
> >  obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o
> >  obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o
> > +obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB3) += phy-rockchip-inno-usb3.o
> >  obj-$(CONFIG_PHY_ROCKCHIP_PCIE)              += phy-rockchip-pcie.o
> >  obj-$(CONFIG_PHY_ROCKCHIP_TYPEC)     += phy-rockchip-typec.o
> >  obj-$(CONFIG_PHY_ROCKCHIP_USB)               += phy-rockchip-usb.o
> > diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb3.c b/drivers/phy/rockchip/phy-rockchip-inno-usb3.c
> > new file mode 100644
> > index 000000000000..31fee8f3a705
> > --- /dev/null
> > +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb3.c
> > @@ -0,0 +1,1107 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/*
> > + * Rockchip USB 3.0 PHY with Innosilicon IP block driver
> > + *
> > + * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/debugfs.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/kernel.h>
> > +#include <linux/mfd/syscon.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_clk.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regmap.h>
> > +#include <linux/reset.h>
> > +#include <linux/usb/phy.h>
> > +#include <linux/uaccess.h>
> > +
> > +#define U3PHY_PORT_NUM       2
> > +#define BIT_WRITEABLE_SHIFT  16
> > +#define SCHEDULE_DELAY       (60 * HZ)
> > +
> > +#define U3PHY_APB_RST        BIT(0)
> > +#define U3PHY_POR_RST        BIT(1)
> > +#define U3PHY_MAC_RST        BIT(2)
> > +
> > +struct rockchip_u3phy;
> > +struct rockchip_u3phy_port;
> > +
> > +enum rockchip_u3phy_type {
> > +     U3PHY_TYPE_PIPE,
> > +     U3PHY_TYPE_UTMI,
> > +};
> > +
> > +enum rockchip_u3phy_pipe_pwr {
> > +     PIPE_PWR_P0     = 0,
> > +     PIPE_PWR_P1     = 1,
> > +     PIPE_PWR_P2     = 2,
> > +     PIPE_PWR_P3     = 3,
> > +     PIPE_PWR_MAX    = 4,
> > +};
> > +
> > +enum rockchip_u3phy_rest_req {
> > +     U3_POR_RSTN     = 0,
> > +     U2_POR_RSTN     = 1,
> > +     PIPE_MAC_RSTN   = 2,
> > +     UTMI_MAC_RSTN   = 3,
> > +     PIPE_APB_RSTN   = 4,
> > +     UTMI_APB_RSTN   = 5,
> > +     U3PHY_RESET_MAX = 6,
> > +};
> > +
> > +enum rockchip_u3phy_utmi_state {
> > +     PHY_UTMI_HS_ONLINE      = 0,
> > +     PHY_UTMI_DISCONNECT     = 1,
> > +     PHY_UTMI_CONNECT        = 2,
> > +     PHY_UTMI_FS_LS_ONLINE   = 4,
> > +};
> > +
> > +/*
> > + * @rvalue: reset value
> > + * @dvalue: desired value
> > + */
> > +struct u3phy_reg {
> > +     unsigned int    offset;
> > +     unsigned int    bitend;
> > +     unsigned int    bitstart;
> > +     unsigned int    rvalue;
> > +     unsigned int    dvalue;
> > +};
> > +
> > +struct rockchip_u3phy_grfcfg {
> > +     struct u3phy_reg        um_suspend;
> > +     struct u3phy_reg        ls_det_en;
> > +     struct u3phy_reg        ls_det_st;
> > +     struct u3phy_reg        um_ls;
> > +     struct u3phy_reg        um_hstdct;
> > +     struct u3phy_reg        u2_only_ctrl;
> > +     struct u3phy_reg        u3_disable;
> > +     struct u3phy_reg        pp_pwr_st;
> > +     struct u3phy_reg        pp_pwr_en[PIPE_PWR_MAX];
> > +};
> > +
> > +/**
> > + * struct rockchip_u3phy_apbcfg: usb3-phy apb configuration.
> > + * @u2_pre_emp: usb2-phy pre-emphasis tuning.
> > + * @u2_pre_emp_sth: usb2-phy pre-emphasis strength tuning.
> > + * @u2_odt_tuning: usb2-phy odt 45ohm tuning.
> > + */
> > +struct rockchip_u3phy_apbcfg {
> > +     unsigned int    u2_pre_emp;
> > +     unsigned int    u2_pre_emp_sth;
> > +     unsigned int    u2_odt_tuning;
> > +};
> > +
> > +struct rockchip_u3phy_cfg {
> > +     unsigned int reg;
> > +     const struct rockchip_u3phy_grfcfg grfcfg;
> > +
> > +     int (*phy_pipe_power)(struct rockchip_u3phy *u3phy,
> > +                           struct rockchip_u3phy_port *u3phy_port,
> > +                           bool on);
> > +     int (*phy_tuning)(struct rockchip_u3phy *u3phy,
> > +                       struct rockchip_u3phy_port *u3phy_port,
> > +                       struct device_node *child_np);
> > +};
> > +
> > +struct rockchip_u3phy_port {
> > +     struct phy      *phy;
> > +     void __iomem    *base;
> > +     unsigned int    index;
> > +     unsigned char   type;
> > +     bool            suspended;
> > +     bool            refclk_25m_quirk;
> > +     struct mutex    mutex; /* mutex for updating register */
> > +     struct delayed_work     um_sm_work;
> > +};
> > +
> > +struct rockchip_u3phy {
> > +     struct device *dev;
> > +     struct regmap *u3phy_grf;
> > +     struct regmap *grf;
> > +     int um_ls_irq;
> > +     struct clk **clks;
> > +     int num_clocks;
> > +     struct dentry *root;
> > +     struct gpio_desc *vbus_drv_gpio;
> > +     struct reset_control *rsts[U3PHY_RESET_MAX];
> > +     struct rockchip_u3phy_apbcfg apbcfg;
> > +     const struct rockchip_u3phy_cfg *cfgs;
> > +     struct rockchip_u3phy_port ports[U3PHY_PORT_NUM];
> > +     struct usb_phy usb_phy;
> > +};
> > +
> > +static inline int param_write(void __iomem *base,
> > +                           const struct u3phy_reg *reg, bool desired)
> > +{
> > +     unsigned int val, mask;
> > +     unsigned int tmp = desired ? reg->dvalue : reg->rvalue;
> > +     int ret = 0;
> > +
> > +     mask = GENMASK(reg->bitend, reg->bitstart);
> > +     val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT);
> > +     ret = regmap_write(base, reg->offset, val);
> > +
> > +     return ret;
> > +}
> > +
> > +static inline bool param_exped(void __iomem *base,
> > +                            const struct u3phy_reg *reg,
> > +                            unsigned int value)
> > +{
> > +     int ret;
> > +     unsigned int tmp, orig;
> > +     unsigned int mask = GENMASK(reg->bitend, reg->bitstart);
> > +
> > +     ret = regmap_read(base, reg->offset, &orig);
> > +     if (ret)
> > +             return false;
> > +
> > +     tmp = (orig & mask) >> reg->bitstart;
> > +     return tmp == value;
> > +}
> > +
> > +static int rockchip_u3phy_usb2_only_show(struct seq_file *s, void *unused)
> > +{
> > +     struct rockchip_u3phy   *u3phy = s->private;
> > +
> > +     if (param_exped(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.u2_only_ctrl, 1))
> > +             dev_info(u3phy->dev, "u2\n");
> > +     else
> > +             dev_info(u3phy->dev, "u3\n");
> > +
> > +     return 0;
> > +}
> > +
> > +static int rockchip_u3phy_usb2_only_open(struct inode *inode,
> > +                                      struct file *file)
> > +{
> > +     return single_open(file, rockchip_u3phy_usb2_only_show,
> > +                        inode->i_private);
> > +}
> > +
> > +static ssize_t rockchip_u3phy_usb2_only_write(struct file *file,
> > +                                           const char __user *ubuf,
> > +                                           size_t count, loff_t *ppos)
> > +{
> > +     struct seq_file                 *s = file->private_data;
> > +     struct rockchip_u3phy           *u3phy = s->private;
> > +     struct rockchip_u3phy_port      *u3phy_port;
> > +     char                            buf[32];
> > +     u8                              index;
> > +
> > +     if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
> > +             return -EFAULT;
> > +
> > +     if (!strncmp(buf, "u3", 2) &&
> > +         param_exped(u3phy->u3phy_grf,
> > +                     &u3phy->cfgs->grfcfg.u2_only_ctrl, 1)) {
> > +             dev_info(u3phy->dev, "Set usb3.0 and usb2.0 mode successfully\n");
> > +
> > +             gpiod_set_value_cansleep(u3phy->vbus_drv_gpio, 0);
> > +
> > +             param_write(u3phy->grf,
> > +                         &u3phy->cfgs->grfcfg.u3_disable, false);
> > +             param_write(u3phy->u3phy_grf,
> > +                         &u3phy->cfgs->grfcfg.u2_only_ctrl, false);
> > +
> > +             for (index = 0; index < U3PHY_PORT_NUM; index++) {
> > +                     u3phy_port = &u3phy->ports[index];
> > +                     /* enable u3 rx termimation */
> > +                     if (u3phy_port->type == U3PHY_TYPE_PIPE)
> > +                             writel(0x30, u3phy_port->base + 0xd8);
> > +             }
> > +
> > +             atomic_notifier_call_chain(&u3phy->usb_phy.notifier, 0, NULL);
> > +
> > +             gpiod_set_value_cansleep(u3phy->vbus_drv_gpio, 1);
> > +     } else if (!strncmp(buf, "u2", 2) &&
> > +                param_exped(u3phy->u3phy_grf,
> > +                            &u3phy->cfgs->grfcfg.u2_only_ctrl, 0)) {
> > +             dev_info(u3phy->dev, "Set usb2.0 only mode successfully\n");
> > +
> > +             gpiod_set_value_cansleep(u3phy->vbus_drv_gpio, 0);
> > +
> > +             param_write(u3phy->grf,
> > +                         &u3phy->cfgs->grfcfg.u3_disable, true);
> > +             param_write(u3phy->u3phy_grf,
> > +                         &u3phy->cfgs->grfcfg.u2_only_ctrl, true);
> > +
> > +             for (index = 0; index < U3PHY_PORT_NUM; index++) {
> > +                     u3phy_port = &u3phy->ports[index];
> > +                     /* disable u3 rx termimation */
> > +                     if (u3phy_port->type == U3PHY_TYPE_PIPE)
> > +                             writel(0x20, u3phy_port->base + 0xd8);
> > +             }
> > +
> > +             atomic_notifier_call_chain(&u3phy->usb_phy.notifier, 0, NULL);
> > +
> > +             gpiod_set_value_cansleep(u3phy->vbus_drv_gpio, 1);
> > +     } else {
> > +             dev_info(u3phy->dev, "Same or illegal mode\n");
> > +     }
> > +
> > +     return count;
> > +}
> > +
> > +static const struct file_operations rockchip_u3phy_usb2_only_fops = {
> > +     .open                   = rockchip_u3phy_usb2_only_open,
> > +     .write                  = rockchip_u3phy_usb2_only_write,
> > +     .read                   = seq_read,
> > +     .llseek                 = seq_lseek,
> > +     .release                = single_release,
> > +};
> > +
> > +int rockchip_u3phy_debugfs_init(struct rockchip_u3phy *u3phy)
> > +{
> > +     struct dentry           *root;
> > +     struct dentry           *file;
> > +     int                     ret;
> > +
> > +     root = debugfs_create_dir(dev_name(u3phy->dev), NULL);
> > +     if (!root) {
> > +             ret = -ENOMEM;
> > +             goto err0;
> > +     }
> > +
> > +     u3phy->root = root;
> > +
> > +     file = debugfs_create_file("u3phy_mode", 0644, root,
> > +                                u3phy, &rockchip_u3phy_usb2_only_fops);
> > +     if (!file) {
> > +             ret = -ENOMEM;
> > +             goto err1;
> > +     }
> > +     return 0;
> > +
> > +err1:
> > +     debugfs_remove_recursive(root);
> > +err0:
> > +     return ret;
> > +}
> > +
> > +static const char *get_rest_name(enum rockchip_u3phy_rest_req rst)
> > +{
> > +     switch (rst) {
> > +     case U2_POR_RSTN:
> > +             return "u3phy-u2-por";
> > +     case U3_POR_RSTN:
> > +             return "u3phy-u3-por";
> > +     case PIPE_MAC_RSTN:
> > +             return "u3phy-pipe-mac";
> > +     case UTMI_MAC_RSTN:
> > +             return "u3phy-utmi-mac";
> > +     case UTMI_APB_RSTN:
> > +             return "u3phy-utmi-apb";
> > +     case PIPE_APB_RSTN:
> > +             return "u3phy-pipe-apb";
> > +     default:
> > +             return "invalid";
> > +     }
> > +}
> > +
> > +static void rockchip_u3phy_rest_deassert(struct rockchip_u3phy *u3phy,
> > +                                      unsigned int flag)
> > +{
> > +     int rst;
> > +
> > +     if (flag & U3PHY_APB_RST) {
> > +             dev_dbg(u3phy->dev, "deassert APB bus interface reset\n");
> > +             for (rst = PIPE_APB_RSTN; rst <= UTMI_APB_RSTN; rst++) {
> > +                     if (u3phy->rsts[rst])
> > +                             reset_control_deassert(u3phy->rsts[rst]);
> > +             }
> > +     }
> > +
> > +     if (flag & U3PHY_POR_RST) {
> > +             usleep_range(12, 15);
> > +             dev_dbg(u3phy->dev, "deassert u2 and u3 phy power on reset\n");
> > +             for (rst = U3_POR_RSTN; rst <= U2_POR_RSTN; rst++) {
> > +                     if (u3phy->rsts[rst])
> > +                             reset_control_deassert(u3phy->rsts[rst]);
> > +             }
> > +     }
> > +
> > +     if (flag & U3PHY_MAC_RST) {
> > +             usleep_range(1200, 1500);
> > +             dev_dbg(u3phy->dev, "deassert pipe and utmi MAC reset\n");
> > +             for (rst = PIPE_MAC_RSTN; rst <= UTMI_MAC_RSTN; rst++)
> > +                     if (u3phy->rsts[rst])
> > +                             reset_control_deassert(u3phy->rsts[rst]);
> > +     }
> > +}
> > +
> > +static void rockchip_u3phy_rest_assert(struct rockchip_u3phy *u3phy)
> > +{
> > +     int rst;
> > +
> > +     dev_dbg(u3phy->dev, "assert u3phy reset\n");
> > +     for (rst = 0; rst < U3PHY_RESET_MAX; rst++)
> > +             if (u3phy->rsts[rst])
> > +                     reset_control_assert(u3phy->rsts[rst]);
> > +}
> > +
> > +static int rockchip_u3phy_clk_enable(struct rockchip_u3phy *u3phy)
> > +{
> > +     int ret, clk;
> > +
> > +     for (clk = 0; clk < u3phy->num_clocks && u3phy->clks[clk]; clk++) {
> > +             ret = clk_prepare_enable(u3phy->clks[clk]);
> > +             if (ret)
> > +                     goto err_disable_clks;
> > +     }
> > +     return 0;
> > +
> > +err_disable_clks:
> > +     while (--clk >= 0)
> > +             clk_disable_unprepare(u3phy->clks[clk]);
> > +     return ret;
> > +}
> > +
> > +static void rockchip_u3phy_clk_disable(struct rockchip_u3phy *u3phy)
> > +{
> > +     int clk;
> > +
> > +     for (clk = u3phy->num_clocks - 1; clk >= 0; clk--)
> > +             if (u3phy->clks[clk])
> > +                     clk_disable_unprepare(u3phy->clks[clk]);
> > +}
> > +
> > +static int rockchip_u3phy_init(struct phy *phy)
> > +{
> > +     return 0;
> > +}
> > +
> > +static int rockchip_u3phy_exit(struct phy *phy)
> > +{
> > +     return 0;
> > +}
> > +
> > +static int rockchip_u3phy_power_on(struct phy *phy)
> > +{
> > +     struct rockchip_u3phy_port *u3phy_port = phy_get_drvdata(phy);
> > +     struct rockchip_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
> > +     int ret;
> > +
> > +     dev_info(&u3phy_port->phy->dev, "u3phy %s power on\n",
> > +              (u3phy_port->type == U3PHY_TYPE_UTMI) ? "u2" : "u3");
> > +
> > +     if (!u3phy_port->suspended)
> > +             return 0;
> > +
> > +     ret = rockchip_u3phy_clk_enable(u3phy);
> > +     if (ret)
> > +             return ret;
> > +
> > +     if (u3phy_port->type == U3PHY_TYPE_UTMI) {
> > +             param_write(u3phy->u3phy_grf,
> > +                         &u3phy->cfgs->grfcfg.um_suspend, false);
> > +     } else {
> > +             /* current in p2 ? */
> > +             if (param_exped(u3phy->u3phy_grf,
> > +                             &u3phy->cfgs->grfcfg.pp_pwr_st, PIPE_PWR_P2))
> > +                     goto done;
> > +
> > +             if (u3phy->cfgs->phy_pipe_power) {
> > +                     dev_dbg(u3phy->dev, "do pipe power up\n");
> > +                     u3phy->cfgs->phy_pipe_power(u3phy, u3phy_port, true);
> > +             }
> > +
> > +             /* exit to p0 */
> > +             param_write(u3phy->u3phy_grf,
> > +                         &u3phy->cfgs->grfcfg.pp_pwr_en[PIPE_PWR_P0], true);
> > +             usleep_range(90, 100);
> > +
> > +             /* enter to p2 from p0 */
> > +             param_write(u3phy->u3phy_grf,
> > +                         &u3phy->cfgs->grfcfg.pp_pwr_en[PIPE_PWR_P2],
> > +                         false);
> > +             udelay(3);
> > +     }
> > +
> > +done:
> > +     u3phy_port->suspended = false;
> > +     return 0;
> > +}
> > +
> > +static int rockchip_u3phy_power_off(struct phy *phy)
> > +{
> > +     struct rockchip_u3phy_port *u3phy_port = phy_get_drvdata(phy);
> > +     struct rockchip_u3phy *u3phy = dev_get_drvdata(phy->dev.parent);
> > +
> > +     dev_info(&u3phy_port->phy->dev, "u3phy %s power off\n",
> > +              (u3phy_port->type == U3PHY_TYPE_UTMI) ? "u2" : "u3");
> > +
> > +     if (u3phy_port->suspended)
> > +             return 0;
> > +
> > +     if (u3phy_port->type == U3PHY_TYPE_UTMI) {
> > +             param_write(u3phy->u3phy_grf,
> > +                         &u3phy->cfgs->grfcfg.um_suspend, true);
> > +     } else {
> > +             /* current in p3 ? */
> > +             if (param_exped(u3phy->u3phy_grf,
> > +                             &u3phy->cfgs->grfcfg.pp_pwr_st, PIPE_PWR_P3))
> > +                     goto done;
> > +
> > +             /* exit to p0 */
> > +             param_write(u3phy->u3phy_grf,
> > +                         &u3phy->cfgs->grfcfg.pp_pwr_en[PIPE_PWR_P0], true);
> > +             udelay(2);
> > +
> > +             /* enter to p3 from p0 */
> > +             param_write(u3phy->u3phy_grf,
> > +                         &u3phy->cfgs->grfcfg.pp_pwr_en[PIPE_PWR_P3], true);
> > +             udelay(6);
> > +
> > +             if (u3phy->cfgs->phy_pipe_power) {
> > +                     dev_dbg(u3phy->dev, "do pipe power down\n");
> > +                     u3phy->cfgs->phy_pipe_power(u3phy, u3phy_port, false);
> > +             }
> > +     }
> > +
> > +done:
> > +     rockchip_u3phy_clk_disable(u3phy);
> > +     u3phy_port->suspended = true;
> > +     return 0;
> > +}
> > +
> > +static __maybe_unused
> > +struct phy *rockchip_u3phy_xlate(struct device *dev,
> > +                              struct of_phandle_args *args)
> > +{
> > +     struct rockchip_u3phy *u3phy = dev_get_drvdata(dev);
> > +     struct rockchip_u3phy_port *u3phy_port = NULL;
> > +     struct device_node *phy_np = args->np;
> > +     int index;
> > +
> > +     if (args->args_count != 1) {
> > +             dev_err(dev, "invalid number of cells in 'phy' property\n");
> > +             return ERR_PTR(-EINVAL);
> > +     }
> > +
> > +     for (index = 0; index < U3PHY_PORT_NUM; index++) {
> > +             if (phy_np == u3phy->ports[index].phy->dev.of_node) {
> > +                     u3phy_port = &u3phy->ports[index];
> > +                     break;
> > +             }
> > +     }
> > +
> > +     if (!u3phy_port) {
> > +             dev_err(dev, "failed to find appropriate phy\n");
> > +             return ERR_PTR(-EINVAL);
> > +     }
> > +
> > +     return u3phy_port->phy;
> > +}
> > +
> > +static struct phy_ops rockchip_u3phy_ops = {
> > +     .init           = rockchip_u3phy_init,
> > +     .exit           = rockchip_u3phy_exit,
> > +     .power_on       = rockchip_u3phy_power_on,
> > +     .power_off      = rockchip_u3phy_power_off,
> > +     .owner          = THIS_MODULE,
> > +};
> > +
> > +/*
> > + * The function manage host-phy port state and suspend/resume phy port
> > + * to save power automatically.
> > + *
> > + * we rely on utmi_linestate and utmi_hostdisconnect to identify whether
> > + * devices is disconnect or not. Besides, we do not need care it is FS/LS
> > + * disconnected or HS disconnected, actually, we just only need get the
> > + * device is disconnected at last through rearm the delayed work,
> > + * to suspend the phy port in _PHY_STATE_DISCONNECT_ case.
> > + */
> > +static void rockchip_u3phy_um_sm_work(struct work_struct *work)
> > +{
> > +     struct rockchip_u3phy_port *u3phy_port =
> > +             container_of(work, struct rockchip_u3phy_port, um_sm_work.work);
> > +     struct rockchip_u3phy *u3phy =
> > +             dev_get_drvdata(u3phy_port->phy->dev.parent);
> > +     unsigned int sh = u3phy->cfgs->grfcfg.um_hstdct.bitend -
> > +                     u3phy->cfgs->grfcfg.um_hstdct.bitstart + 1;
> > +     unsigned int ul, uhd, state;
> > +     unsigned int ul_mask, uhd_mask;
> > +     int ret;
> > +
> > +     mutex_lock(&u3phy_port->mutex);
> > +
> > +     ret = regmap_read(u3phy->u3phy_grf,
> > +                       u3phy->cfgs->grfcfg.um_ls.offset, &ul);
> > +     if (ret < 0)
> > +             goto next_schedule;
> > +
> > +     ret = regmap_read(u3phy->u3phy_grf,
> > +                       u3phy->cfgs->grfcfg.um_hstdct.offset, &uhd);
> > +     if (ret < 0)
> > +             goto next_schedule;
> > +
> > +     uhd_mask = GENMASK(u3phy->cfgs->grfcfg.um_hstdct.bitend,
> > +                        u3phy->cfgs->grfcfg.um_hstdct.bitstart);
> > +     ul_mask = GENMASK(u3phy->cfgs->grfcfg.um_ls.bitend,
> > +                       u3phy->cfgs->grfcfg.um_ls.bitstart);
> > +
> > +     /* stitch on um_ls and um_hstdct as phy state */
> > +     state = ((uhd & uhd_mask) >> u3phy->cfgs->grfcfg.um_hstdct.bitstart) |
> > +             (((ul & ul_mask) >> u3phy->cfgs->grfcfg.um_ls.bitstart) << sh);
> > +
> > +     switch (state) {
> > +     case PHY_UTMI_HS_ONLINE:
> > +             dev_dbg(&u3phy_port->phy->dev, "HS online\n");
> > +             break;
> > +     case PHY_UTMI_FS_LS_ONLINE:
> > +             /*
> > +              * For FS/LS device, the online state share with connect state
> > +              * from um_ls and um_hstdct register, so we distinguish
> > +              * them via suspended flag.
> > +              *
> > +              * Plus, there are two cases, one is D- Line pull-up, and D+
> > +              * line pull-down, the state is 4; another is D+ line pull-up,
> > +              * and D- line pull-down, the state is 2.
> > +              */
> > +             if (!u3phy_port->suspended) {
> > +                     /* D- line pull-up, D+ line pull-down */
> > +                     dev_dbg(&u3phy_port->phy->dev, "FS/LS online\n");
> > +                     break;
> > +             }
> > +             /* fall through */
> > +     case PHY_UTMI_CONNECT:
> > +             if (u3phy_port->suspended) {
> > +                     dev_dbg(&u3phy_port->phy->dev, "Connected\n");
> > +                     rockchip_u3phy_power_on(u3phy_port->phy);
> > +                     u3phy_port->suspended = false;
> > +             } else {
> > +                     /* D+ line pull-up, D- line pull-down */
> > +                     dev_dbg(&u3phy_port->phy->dev, "FS/LS online\n");
> > +             }
> > +             break;
> > +     case PHY_UTMI_DISCONNECT:
> > +             if (!u3phy_port->suspended) {
> > +                     dev_dbg(&u3phy_port->phy->dev, "Disconnected\n");
> > +                     rockchip_u3phy_power_off(u3phy_port->phy);
> > +                     u3phy_port->suspended = true;
> > +             }
> > +
> > +             /*
> > +              * activate the linestate detection to get the next device
> > +              * plug-in irq.
> > +              */
> > +             param_write(u3phy->u3phy_grf,
> > +                         &u3phy->cfgs->grfcfg.ls_det_st, true);
> > +             param_write(u3phy->u3phy_grf,
> > +                         &u3phy->cfgs->grfcfg.ls_det_en, true);
> > +
> > +             /*
> > +              * we don't need to rearm the delayed work when the phy port
> > +              * is suspended.
> > +              */
> > +             mutex_unlock(&u3phy_port->mutex);
> > +             return;
> > +     default:
> > +             dev_dbg(&u3phy_port->phy->dev, "unknown phy state\n");
> > +             break;
> > +     }
> > +
> > +next_schedule:
> > +     mutex_unlock(&u3phy_port->mutex);
> > +     schedule_delayed_work(&u3phy_port->um_sm_work, SCHEDULE_DELAY);
> > +}
> > +
> > +static irqreturn_t rockchip_u3phy_um_ls_irq(int irq, void *data)
> > +{
> > +     struct rockchip_u3phy_port *u3phy_port = data;
> > +     struct rockchip_u3phy *u3phy =
> > +             dev_get_drvdata(u3phy_port->phy->dev.parent);
> > +
> > +     if (!param_exped(u3phy->u3phy_grf,
> > +                      &u3phy->cfgs->grfcfg.ls_det_st,
> > +                      u3phy->cfgs->grfcfg.ls_det_st.dvalue))
> > +             return IRQ_NONE;
> > +
> > +     dev_dbg(u3phy->dev, "utmi linestate interrupt\n");
> > +     mutex_lock(&u3phy_port->mutex);
> > +
> > +     /* disable linestate detect irq and clear its status */
> > +     param_write(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.ls_det_en, false);
> > +     param_write(u3phy->u3phy_grf, &u3phy->cfgs->grfcfg.ls_det_st, true);
> > +
> > +     mutex_unlock(&u3phy_port->mutex);
> > +
> > +     /*
> > +      * In this case for host phy, a new device is plugged in, meanwhile,
> > +      * if the phy port is suspended, we need rearm the work to resume it
> > +      * and mange its states; otherwise, we just return irq handled.
> > +      */
> > +     if (u3phy_port->suspended) {
> > +             dev_dbg(u3phy->dev, "schedule utmi sm work\n");
> > +             rockchip_u3phy_um_sm_work(&u3phy_port->um_sm_work.work);
> > +     }
> > +
> > +     return IRQ_HANDLED;
> > +}
> > +
> > +static int rockchip_u3phy_parse_dt(struct rockchip_u3phy *u3phy,
> > +                                struct platform_device *pdev)
> > +
> > +{
> > +     struct device *dev = &pdev->dev;
> > +     struct device_node *np = dev->of_node;
> > +     int ret, i, clk;
> > +
> > +     u3phy->um_ls_irq = platform_get_irq_byname(pdev, "linestate");
> > +     if (u3phy->um_ls_irq < 0) {
> > +             dev_err(dev, "get utmi linestate irq failed\n");
> > +             return -ENXIO;
> > +     }
> > +
> > +     u3phy->vbus_drv_gpio = devm_gpiod_get_optional(dev, "vbus-drv",
> > +                                                    GPIOD_OUT_HIGH);
> > +
> use fixed regulator?

As per Robin's comment's, I've already queued that change up for v2.

>
> > +     if (!u3phy->vbus_drv_gpio) {
> > +             dev_warn(&pdev->dev, "vbus_drv is not assigned\n");
> > +     } else if (IS_ERR(u3phy->vbus_drv_gpio)) {
> > +             dev_err(&pdev->dev, "failed to get vbus_drv\n");
> > +             return PTR_ERR(u3phy->vbus_drv_gpio);
> > +     }
> > +
> > +     u3phy->num_clocks = of_clk_get_parent_count(np);
> > +     if (u3phy->num_clocks == 0)
> > +             dev_warn(&pdev->dev, "no clks found in dt\n");
> > +
> > +     u3phy->clks = devm_kcalloc(dev, u3phy->num_clocks,
> > +                               sizeof(struct clk *), GFP_KERNEL);
> > +
> > +     for (clk = 0; clk < u3phy->num_clocks; clk++) {
> > +             u3phy->clks[clk] = of_clk_get(np, clk);
> > +             if (IS_ERR(u3phy->clks[clk])) {
> > +                     ret = PTR_ERR(u3phy->clks[clk]);
> > +                     if (ret == -EPROBE_DEFER)
> > +                             goto err_put_clks;
> > +                     dev_err(&pdev->dev, "failed to get clks, %i\n",
> > +                             ret);
> > +                     u3phy->clks[clk] = NULL;
> > +                     break;
> > +             }
> > +     }
> > +
> > +     for (i = 0; i < U3PHY_RESET_MAX; i++) {
> > +             u3phy->rsts[i] = devm_reset_control_get(dev, get_rest_name(i));
> > +             if (IS_ERR(u3phy->rsts[i])) {
> > +                     dev_info(dev, "no %s reset control specified\n",
> > +                              get_rest_name(i));
> > +                     u3phy->rsts[i] = NULL;
> > +             }
> > +     }
> > +
> > +     return 0;
> > +
> > +err_put_clks:
> > +     while (--clk >= 0)
> > +             clk_put(u3phy->clks[clk]);
> > +     return ret;
> > +}
> > +
> > +static int rockchip_u3phy_port_init(struct rockchip_u3phy *u3phy,
> > +                                 struct rockchip_u3phy_port *u3phy_port,
> > +                                 struct device_node *child_np)
> > +{
> > +     struct resource res;
> > +     struct phy *phy;
> > +     int ret;
> > +
> > +     dev_dbg(u3phy->dev, "u3phy port initialize\n");
> > +
> > +     mutex_init(&u3phy_port->mutex);
> > +     u3phy_port->suspended = true; /* initial status */
> > +
> > +     phy = devm_phy_create(u3phy->dev, child_np, &rockchip_u3phy_ops);
> > +     if (IS_ERR(phy)) {
> > +             dev_err(u3phy->dev, "failed to create phy\n");
> > +             return PTR_ERR(phy);
> > +     }
> > +
> > +     u3phy_port->phy = phy;
> > +
> > +     ret = of_address_to_resource(child_np, 0, &res);
> > +     if (ret) {
> > +             dev_err(u3phy->dev, "failed to get address resource(np-%s)\n",
> > +                     child_np->name);
> > +             return ret;
> > +     }
> > +
> > +     u3phy_port->base = devm_ioremap_resource(&u3phy_port->phy->dev, &res);
> > +     if (IS_ERR(u3phy_port->base)) {
> > +             dev_err(u3phy->dev, "failed to remap phy regs\n");
> > +             return PTR_ERR(u3phy_port->base);
> > +     }
> > +
> > +     if (!of_node_cmp(child_np->name, "pipe")) {
> > +             u3phy_port->type = U3PHY_TYPE_PIPE;
> > +             u3phy_port->refclk_25m_quirk =
> > +                     of_property_read_bool(child_np,
> > +                                           "rockchip,refclk-25m-quirk");
> > +     } else {
> > +             u3phy_port->type = U3PHY_TYPE_UTMI;
> > +             INIT_DELAYED_WORK(&u3phy_port->um_sm_work,
> > +                               rockchip_u3phy_um_sm_work);
> > +
> > +             ret = devm_request_threaded_irq(u3phy->dev, u3phy->um_ls_irq,
> > +                                             NULL, rockchip_u3phy_um_ls_irq,
> > +                                             IRQF_ONESHOT, "rockchip_u3phy",
> > +                                             u3phy_port);
> > +             if (ret) {
> > +                     dev_err(u3phy->dev, "failed to request utmi linestate irq handle\n");
> > +                     return ret;
> > +             }
> > +     }
> > +
> > +     if (u3phy->cfgs->phy_tuning) {
> > +             dev_dbg(u3phy->dev, "do u3phy tuning\n");
> > +             ret = u3phy->cfgs->phy_tuning(u3phy, u3phy_port, child_np);
> > +             if (ret)
> > +                     return ret;
> > +     }
> > +
> > +     phy_set_drvdata(u3phy_port->phy, u3phy_port);
> > +     return 0;
> > +}
> > +
> > +static int rockchip_u3phy_on_init(struct usb_phy *usb_phy)
> > +{
> > +     struct rockchip_u3phy *u3phy =
> > +             container_of(usb_phy, struct rockchip_u3phy, usb_phy);
> > +
> > +     rockchip_u3phy_rest_deassert(u3phy, U3PHY_POR_RST | U3PHY_MAC_RST);
> > +     return 0;
> > +}
> > +
> > +static void rockchip_u3phy_on_shutdown(struct usb_phy *usb_phy)
> > +{
> > +     struct rockchip_u3phy *u3phy =
> > +             container_of(usb_phy, struct rockchip_u3phy, usb_phy);
> > +     int rst;
> > +
> > +     for (rst = 0; rst < U3PHY_RESET_MAX; rst++)
> > +             if (u3phy->rsts[rst] && rst != UTMI_APB_RSTN &&
> > +                 rst != PIPE_APB_RSTN)
> > +                     reset_control_assert(u3phy->rsts[rst]);
> > +     udelay(1);
> > +}
> > +
> > +static int rockchip_u3phy_on_disconnect(struct usb_phy *usb_phy,
> > +                                     enum usb_device_speed speed)
> > +{
> > +     struct rockchip_u3phy *u3phy =
> > +             container_of(usb_phy, struct rockchip_u3phy, usb_phy);
> > +
> > +     dev_info(u3phy->dev, "%s device has disconnected\n",
> > +              (speed == USB_SPEED_SUPER) ? "U3" : "UW/U2/U1.1/U1");
> > +
> > +     if (speed == USB_SPEED_SUPER)
> > +             atomic_notifier_call_chain(&usb_phy->notifier, 0, NULL);
> > +
> > +     return 0;
> > +}
> > +
> > +static int rockchip_u3phy_probe(struct platform_device *pdev)
> > +{
> > +     struct device *dev = &pdev->dev;
> > +     struct device_node *np = dev->of_node;
> > +     struct device_node *child_np;
> > +     struct phy_provider *provider;
> > +     struct rockchip_u3phy *u3phy;
> > +     const struct rockchip_u3phy_cfg *phy_cfgs;
> > +     const struct of_device_id *match;
> > +     unsigned int reg[2];
> > +     int index, ret;
> > +
> > +     match = of_match_device(dev->driver->of_match_table, dev);
> > +     if (!match || !match->data) {
> > +             dev_err(dev, "phy-cfgs are not assigned!\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     u3phy = devm_kzalloc(dev, sizeof(*u3phy), GFP_KERNEL);
> > +     if (!u3phy)
> > +             return -ENOMEM;
> > +
> > +     u3phy->u3phy_grf =
> > +             syscon_regmap_lookup_by_phandle(np, "rockchip,u3phygrf");
> > +     if (IS_ERR(u3phy->u3phy_grf))
> > +             return PTR_ERR(u3phy->u3phy_grf);
> > +
> > +     u3phy->grf =
> > +             syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
> > +     if (IS_ERR(u3phy->grf)) {
> > +             dev_err(dev, "Missing rockchip,grf property\n");
> > +             return PTR_ERR(u3phy->grf);
> > +     }
> > +
> > +     if (of_property_read_u32_array(np, "reg", reg, 2)) {
> > +             dev_err(dev, "the reg property is not assigned in %s node\n",
> > +                     np->name);
> > +             return -EINVAL;
> > +     }
> > +
> > +     u3phy->dev = dev;
> > +     phy_cfgs = match->data;
> > +     platform_set_drvdata(pdev, u3phy);
> > +
> > +     /* find out a proper config which can be matched with dt. */
> > +     index = 0;
> > +     while (phy_cfgs[index].reg) {
> > +             if (phy_cfgs[index].reg == reg[1]) {
> > +                     u3phy->cfgs = &phy_cfgs[index];
> > +                     break;
> > +             }
> > +
> > +             ++index;
> > +     }
> > +
> > +     if (!u3phy->cfgs) {
> > +             dev_err(dev, "no phy-cfgs can be matched with %s node\n",
> > +                     np->name);
> > +             return -EINVAL;
> > +     }
> > +
> > +     ret = rockchip_u3phy_parse_dt(u3phy, pdev);
> > +     if (ret) {
> > +             dev_err(dev, "parse dt failed, ret(%d)\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     ret = rockchip_u3phy_clk_enable(u3phy);
> > +     if (ret) {
> > +             dev_err(dev, "clk enable failed, ret(%d)\n", ret);
> > +             return ret;
> > +     }
> > +
> > +     rockchip_u3phy_rest_assert(u3phy);
> > +     rockchip_u3phy_rest_deassert(u3phy, U3PHY_APB_RST | U3PHY_POR_RST);
> > +
> > +     index = 0;
> > +     for_each_available_child_of_node(np, child_np) {
> > +             struct rockchip_u3phy_port *u3phy_port = &u3phy->ports[index];
> > +
> > +             u3phy_port->index = index;
> > +             ret = rockchip_u3phy_port_init(u3phy, u3phy_port, child_np);
> > +             if (ret) {
> > +                     dev_err(dev, "u3phy port init failed,ret(%d)\n", ret);
> > +                     goto put_child;
> > +             }
> > +
> > +             /* to prevent out of boundary */
> > +             if (++index >= U3PHY_PORT_NUM)
> > +                     break;
> > +     }
> > +
> > +     provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
> > +     if (IS_ERR_OR_NULL(provider))
> > +             goto put_child;
> > +
> > +     rockchip_u3phy_rest_deassert(u3phy, U3PHY_MAC_RST);
> > +     rockchip_u3phy_clk_disable(u3phy);
> > +
> > +     u3phy->usb_phy.dev = dev;
> > +     u3phy->usb_phy.init = rockchip_u3phy_on_init;
> > +     u3phy->usb_phy.shutdown = rockchip_u3phy_on_shutdown;
> > +     u3phy->usb_phy.notify_disconnect = rockchip_u3phy_on_disconnect;
> > +     usb_add_phy(&u3phy->usb_phy, USB_PHY_TYPE_USB3);
> > +     ATOMIC_INIT_NOTIFIER_HEAD(&u3phy->usb_phy.notifier);
> > +
> > +     rockchip_u3phy_debugfs_init(u3phy);
> > +
> > +     dev_info(dev, "Rockchip u3phy initialized successfully\n");
> > +     return 0;
> > +
> > +put_child:
> > +     of_node_put(child_np);
> > +     return ret;
> > +}
> > +
> > +static int rk3328_u3phy_pipe_power(struct rockchip_u3phy *u3phy,
> > +                                struct rockchip_u3phy_port *u3phy_port,
> > +                                bool on)
> > +{
> > +     unsigned int reg;
> > +
> > +     if (on) {
> > +             reg = readl(u3phy_port->base + 0x1a8);
> > +             reg &= ~BIT(4); /* ldo power up */
> > +             writel(reg, u3phy_port->base + 0x1a8);
> > +
> > +             reg = readl(u3phy_port->base + 0x044);
> > +             reg &= ~BIT(4); /* bg power on */
> > +             writel(reg, u3phy_port->base + 0x044);
> > +
> > +             reg = readl(u3phy_port->base + 0x150);
> > +             reg |= BIT(6); /* tx bias enable */
> > +             writel(reg, u3phy_port->base + 0x150);
> > +
> > +             reg = readl(u3phy_port->base + 0x080);
> > +             reg &= ~BIT(2); /* tx cm power up */
> > +             writel(reg, u3phy_port->base + 0x080);
> > +
> > +             reg = readl(u3phy_port->base + 0x0c0);
> > +             /* tx obs enable and rx cm enable */
> > +             reg |= (BIT(3) | BIT(4));
> > +             writel(reg, u3phy_port->base + 0x0c0);
> > +
> > +             udelay(1);
> > +     } else {
> > +             reg = readl(u3phy_port->base + 0x1a8);
> > +             reg |= BIT(4); /* ldo power down */
> > +             writel(reg, u3phy_port->base + 0x1a8);
> > +
> > +             reg = readl(u3phy_port->base + 0x044);
> > +             reg |= BIT(4); /* bg power down */
> > +             writel(reg, u3phy_port->base + 0x044);
> > +
> > +             reg = readl(u3phy_port->base + 0x150);
> > +             reg &= ~BIT(6); /* tx bias disable */
> > +             writel(reg, u3phy_port->base + 0x150);
> > +
> > +             reg = readl(u3phy_port->base + 0x080);
> > +             reg |= BIT(2); /* tx cm power down */
> > +             writel(reg, u3phy_port->base + 0x080);
> > +
> > +             reg = readl(u3phy_port->base + 0x0c0);
> > +             /* tx obs disable and rx cm disable */
> > +             reg &= ~(BIT(3) | BIT(4));
> > +             writel(reg, u3phy_port->base + 0x0c0);
> > +     }
> Try to avoid magic number

Since this was not my driver, I only pulled it in and made the
necessary changes to get it working, I tried to refrain from
modifications as much as possible.
Do you want me to convert these addresses to definitions based on the
comments in the code?

>
> > +
> > +     return 0;
> > +}
> > +
> > +static int rk3328_u3phy_tuning(struct rockchip_u3phy *u3phy,
> > +                            struct rockchip_u3phy_port *u3phy_port,
> > +                            struct device_node *child_np)
> > +{
> > +     if (u3phy_port->type == U3PHY_TYPE_UTMI) {
> > +             /*
> > +              * For rk3328 SoC, pre-emphasis and pre-emphasis strength must
> > +              * be written as one fixed value as below.
> > +              *
> > +              * Dissimilarly, the odt 45ohm value should be flexibly tuninged
> > +              * for the different boards to adjust HS eye height, so its
> > +              * value can be assigned in DT in code design.
> > +              */
> > +
> > +             /* {bits[2:0]=111}: always enable pre-emphasis */
> > +             u3phy->apbcfg.u2_pre_emp = 0x0f;
> > +
> > +             /* {bits[5:3]=000}: pre-emphasis strength as the weakest */
> > +             u3phy->apbcfg.u2_pre_emp_sth = 0x41;
> > +
> > +             /* {bits[4:0]=10101}: odt 45ohm tuning */
> > +             u3phy->apbcfg.u2_odt_tuning = 0xb5;
> > +             /* optional override of the odt 45ohm tuning */
> > +             of_property_read_u32(child_np, "rockchip,odt-val-tuning",
> > +                                  &u3phy->apbcfg.u2_odt_tuning);
> > +
> > +             writel(u3phy->apbcfg.u2_pre_emp, u3phy_port->base + 0x030);
> > +             writel(u3phy->apbcfg.u2_pre_emp_sth, u3phy_port->base + 0x040);
> > +             writel(u3phy->apbcfg.u2_odt_tuning, u3phy_port->base + 0x11c);
> > +     } else if (u3phy_port->type == U3PHY_TYPE_PIPE) {
> > +             if (u3phy_port->refclk_25m_quirk) {
> > +                     dev_dbg(u3phy->dev, "switch to 25m refclk\n");
> > +                     /* ref clk switch to 25M */
> > +                     writel(0x64, u3phy_port->base + 0x11c);
> > +                     writel(0x64, u3phy_port->base + 0x028);
> > +                     writel(0x01, u3phy_port->base + 0x020);
> > +                     writel(0x21, u3phy_port->base + 0x030);
> > +                     writel(0x06, u3phy_port->base + 0x108);
> > +                     writel(0x00, u3phy_port->base + 0x118);
> > +             } else {
> > +                     /* configure for 24M ref clk */
> > +                     writel(0x80, u3phy_port->base + 0x10c);
> > +                     writel(0x01, u3phy_port->base + 0x118);
> > +                     writel(0x38, u3phy_port->base + 0x11c);
> > +                     writel(0x83, u3phy_port->base + 0x020);
> > +                     writel(0x02, u3phy_port->base + 0x108);
> > +             }
> > +
> > +             /* Enable SSC */
> > +             udelay(3);
> > +             writel(0x08, u3phy_port->base + 0x000);
> > +             writel(0x0c, u3phy_port->base + 0x120);
> > +
> > +             /* Tuning Rx for compliance RJTL test */
> > +             writel(0x70, u3phy_port->base + 0x150);
> > +             writel(0x12, u3phy_port->base + 0x0c8);
> > +             writel(0x05, u3phy_port->base + 0x148);
> > +             writel(0x08, u3phy_port->base + 0x068);
> > +             writel(0xf0, u3phy_port->base + 0x1c4);
> > +             writel(0xff, u3phy_port->base + 0x070);
> > +             writel(0x0f, u3phy_port->base + 0x06c);
> > +             writel(0xe0, u3phy_port->base + 0x060);
> > +
> > +             /*
> > +              * Tuning Tx to increase the bias current
> > +              * used in TX driver and RX EQ, it can
> > +              * also increase the voltage of LFPS.
> > +              */
> > +             writel(0x08, u3phy_port->base + 0x180);
> > +     } else {
> > +             dev_err(u3phy->dev, "invalid u3phy port type\n");
> > +             return -EINVAL;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct rockchip_u3phy_cfg rk3328_u3phy_cfgs[] = {
> > +     {
> > +             .reg            = 0xff470000,
> > +             .grfcfg         = {
> > +                     .um_suspend     = { 0x0004, 15, 0, 0x1452, 0x15d1 },
> > +                     .u2_only_ctrl   = { 0x0020, 15, 15, 0, 1 },
> > +                     .um_ls          = { 0x0030, 5, 4, 0, 1 },
> > +                     .um_hstdct      = { 0x0030, 7, 7, 0, 1 },
> > +                     .ls_det_en      = { 0x0040, 0, 0, 0, 1 },
> > +                     .ls_det_st      = { 0x0044, 0, 0, 0, 1 },
> > +                     .pp_pwr_st      = { 0x0034, 14, 13, 0, 0},
> > +                     .pp_pwr_en      = { {0x0020, 14, 0, 0x0014, 0x0005},
> > +                                         {0x0020, 14, 0, 0x0014, 0x000d},
> > +                                         {0x0020, 14, 0, 0x0014, 0x0015},
> > +                                         {0x0020, 14, 0, 0x0014, 0x001d} },
> > +                     .u3_disable     = { 0x04c4, 15, 0, 0x1100, 0x101},
> > +             },
> > +             .phy_pipe_power = rk3328_u3phy_pipe_power,
> > +             .phy_tuning     = rk3328_u3phy_tuning,
> > +     },
> > +     { /* sentinel */ }
> > +};
> > +
> > +static const struct of_device_id rockchip_u3phy_dt_match[] = {
> > +     { .compatible = "rockchip,rk3328-u3phy", .data = &rk3328_u3phy_cfgs },
> > +     {}
> > +};
> > +MODULE_DEVICE_TABLE(of, rockchip_u3phy_dt_match);
> > +
> > +static struct platform_driver rockchip_u3phy_driver = {
> > +     .probe          = rockchip_u3phy_probe,
> > +     .driver         = {
> > +             .name   = "rockchip-u3phy",
> > +             .of_match_table = rockchip_u3phy_dt_match,
> > +     },
> > +};
> > +module_platform_driver(rockchip_u3phy_driver);
> > +
> > +MODULE_AUTHOR("Frank Wang <frank.wang@rock-chips.com>");
> > +MODULE_AUTHOR("William Wu <william.wu@rock-chips.com>");
> > +MODULE_DESCRIPTION("Rockchip USB 3.0 PHY driver");
> > +MODULE_LICENSE("GPL v2");
>
>
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 4/5] arm64: dts: rockchip: add usb3 to rk3328 devicetree
  2019-10-29  1:58   ` Chunfeng Yun
@ 2019-10-29 15:31     ` Peter Geis
  2019-10-30  2:28       ` Chunfeng Yun
  0 siblings, 1 reply; 22+ messages in thread
From: Peter Geis @ 2019-10-29 15:31 UTC (permalink / raw)
  To: Chunfeng Yun
  Cc: katsuhiro, linux-rockchip, linux-usb, Robin Murphy, Heiko Stuebner

On Mon, Oct 28, 2019 at 9:59 PM Chunfeng Yun <chunfeng.yun@mediatek.com> wrote:
>
> On Mon, 2019-10-28 at 18:22 +0000, Peter Geis wrote:
> > Now that we have a proper phy driver, we can add the requisite bits to the
> > rk3328 device tree.
> > Added the u3drd and u3phy nodes.
> >
> > Signed-off-by: Peter Geis <pgwipeout@gmail.com>
> > ---
> >  arch/arm64/boot/dts/rockchip/rk3328.dtsi | 72 ++++++++++++++++++++++++
> >  1 file changed, 72 insertions(+)
> >
> > diff --git a/arch/arm64/boot/dts/rockchip/rk3328.dtsi b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
> > index 31cc1541f1f5..072e988ad655 100644
> > --- a/arch/arm64/boot/dts/rockchip/rk3328.dtsi
> > +++ b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
> > @@ -805,6 +805,47 @@
> >               };
> >       };
> >
> > +     usb3phy_grf: syscon@ff460000 {
> > +             compatible = "rockchip,usb3phy-grf", "syscon";
> > +             reg = <0x0 0xff460000 0x0 0x1000>;
> > +     };
> > +
> > +     u3phy: usb3-phy@ff470000 {
> usb-phy?

This change does not seem wise, as there are also various usb2-phys
that are controlled via other drivers, while the usb3phy also has to
support usb2 as a fallback.

> > +             compatible = "rockchip,rk3328-u3phy";
> > +             reg = <0x0 0xff470000 0x0 0x0>;
> It's zero length, may be not necessary, how about use ranges with
> parameter ?

I'll look into this.

>
> > +             rockchip,u3phygrf = <&usb3phy_grf>;
> > +             rockchip,grf = <&grf>;
> > +             interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>;
> > +             interrupt-names = "linestate";
> > +             clocks = <&cru PCLK_USB3PHY_OTG>, <&cru PCLK_USB3PHY_PIPE>;
> > +             clock-names = "u3phy-otg", "u3phy-pipe";
> To me, no need u3phy prefix

Same as above, since this could cause confusion with the usb2 phy devices.

> > +             resets = <&cru SRST_USB3PHY_U2>,
> > +                      <&cru SRST_USB3PHY_U3>,
> > +                      <&cru SRST_USB3PHY_PIPE>,
> > +                      <&cru SRST_USB3OTG_UTMI>,
> > +                      <&cru SRST_USB3PHY_OTG_P>,
> > +                      <&cru SRST_USB3PHY_PIPE_P>;
> > +             reset-names = "u3phy-u2-por", "u3phy-u3-por",
> > +                           "u3phy-pipe-mac", "u3phy-utmi-mac",
> > +                           "u3phy-utmi-apb", "u3phy-pipe-apb";
> ditto
>
> > +             #address-cells = <2>;
> > +             #size-cells = <2>;
> > +             ranges;
> > +             status = "disabled";
> > +
> > +             u3phy_utmi: utmi@ff470000 {
> usb-phy instead of utmi?

The stock driver looks for these by name, and they are both different functions.
This change would break the driver.

>
> > +                     reg = <0x0 0xff470000 0x0 0x8000>;
> > +                     #phy-cells = <0>;
> > +                     status = "disabled";
> > +             };
> > +
> > +             u3phy_pipe: pipe@ff478000 {
> usb-phy

Same as above.

> > +                     reg = <0x0 0xff478000 0x0 0x8000>;
> > +                     #phy-cells = <0>;
> > +                     status = "disabled";
> > +             };
> > +     };
> > +
> >       sdmmc: dwmmc@ff500000 {
> >               compatible = "rockchip,rk3328-dw-mshc", "rockchip,rk3288-dw-mshc";
> >               reg = <0x0 0xff500000 0x0 0x4000>;
> > @@ -936,6 +977,37 @@
> >               status = "disabled";
> >       };
> >
> > +     usbdrd3: usb@ff600000 {
> > +             compatible = "rockchip,rk3328-dwc3", "rockchip,rk3399-dwc3";
> > +             clocks = <&cru SCLK_USB3OTG_REF>, <&cru ACLK_USB3OTG>,
> > +                      <&cru SCLK_USB3OTG_SUSPEND>;
> > +             clock-names = "ref", "bus_early",
> > +                           "suspend";
> > +             #address-cells = <2>;
> > +             #size-cells = <2>;
> > +             ranges;
> > +             clock-ranges;
> > +             status = "disabled";
> > +
> > +             usbdrd_dwc3: dwc3@ff600000 {
> > +                     compatible = "snps,dwc3";
> > +                     reg = <0x0 0xff600000 0x0 0x100000>;
> > +                     interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
> > +                     dr_mode = "otg";
> > +                     phys = <&u3phy_utmi>, <&u3phy_pipe>;
> > +                     phy-names = "usb2-phy", "usb3-phy";
> > +                     phy_type = "utmi_wide";
> > +                     snps,dis_enblslpm_quirk;
> > +                     snps,dis-u2-freeclk-exists-quirk;
> > +                     snps,dis_u2_susphy_quirk;
> > +                     snps,dis_u3_susphy_quirk;
> > +                     snps,dis-del-phy-power-chg-quirk;
> > +                     snps,dis-tx-ipgap-linecheck-quirk;
> > +                     snps,xhci-trb-ent-quirk;
> > +                     status = "disabled";
> > +             };
> > +     };
> > +
> >       gic: interrupt-controller@ff811000 {
> >               compatible = "arm,gic-400";
> >               #interrupt-cells = <3>;
>
>
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 0/5] add rk3328 usb3 phy driver
  2019-10-28 18:41 ` [PATCH 0/5] add rk3328 usb3 phy driver Robin Murphy
@ 2019-10-30  0:17   ` Robin Murphy
  2019-10-30  1:15     ` Peter Geis
  0 siblings, 1 reply; 22+ messages in thread
From: Robin Murphy @ 2019-10-30  0:17 UTC (permalink / raw)
  To: Peter Geis, heiko, kishon; +Cc: katsuhiro, linux-rockchip, linux-usb

On 2019-10-28 6:41 pm, Robin Murphy wrote:
> On 28/10/2019 18:22, Peter Geis wrote:
>> It took a lot more effort than originally anticipated, but here it is.
>> This is the driver from [0], updated to work with the current kernel.
>> I've tested it on the rk3328-roc-cc board, both usb 2.0 and usb 3.0
>> devices detect on hotplug.
> 
> Thanks Peter, I'll try to give this a go on my box for confirmation.

OK, I hooked it up with a vbus-drv-gpio hacked in - USB 2.0 
unplug/replug does indeed work fine, but it takes a while to acknowledge 
an unplug of a USB 3.0 device, and throws a bunch of errors every time:

[  288.229568] usb usb4-port1: Cannot enable. Maybe the USB cable is bad?
[  290.809599] usb usb4-port1: Cannot enable. Maybe the USB cable is bad?
[  293.389594] usb usb4-port1: Cannot enable. Maybe the USB cable is bad?
[  295.969600] usb usb4-port1: Cannot enable. Maybe the USB cable is bad?
[  295.970418] usb 4-1: USB disconnect, device number 10
[  299.209631] usb usb4-port1: Cannot enable. Maybe the USB cable is bad?
[  301.789655] usb usb4-port1: Cannot enable. Maybe the USB cable is bad?
[  301.790534] usb usb4-port1: attempt power cycle

(although new devices are still detected OK eventually)

Robin.

> One quick comment is that it might be worth importing the version from 
> Rockchip's own kernel tree, as that includes this additional patch which 
> looks like a welcome improvement:
> 
> https://github.com/rockchip-linux/kernel/commit/12efa9acad65b4c3256683c1ccd769687be3ca56#diff-b6317b3425ac054be551abdcda910b68 
> 
> 
> Also, as it's a new phy driver, we should keep Kishon (+cc) in the loop 
> as the subsystem maintainer.
> 
> Robin.
> 
>> [0] 
>> https://github.com/FireflyTeam/kernel/commits/roc-rk3328-cc/drivers/phy/rockchip/phy-rockchip-inno-usb3.c 
>>
>>
>> Peter Geis (5):
>>    phy: rockchip: add inno-usb3 phy driver
>>    dt-bindings: clean up rockchip grf binding document
>>    Documentation: bindings: add dt documentation for rockchip usb3 phy
>>    arm64: dts: rockchip: add usb3 to rk3328 devicetree
>>    arm64: dts: rockchip: enable usb3 on rk3328-roc-cc
>>
>>   .../bindings/phy/phy-rockchip-inno-usb3.yaml  |  157 +++
>>   .../devicetree/bindings/soc/rockchip/grf.txt  |    8 +-
>>   .../devicetree/bindings/usb/rockchip,dwc3.txt |    9 +-
>>   .../arm64/boot/dts/rockchip/rk3328-roc-cc.dts |   21 +
>>   arch/arm64/boot/dts/rockchip/rk3328.dtsi      |   72 ++
>>   drivers/phy/rockchip/Kconfig                  |    9 +
>>   drivers/phy/rockchip/Makefile                 |    1 +
>>   drivers/phy/rockchip/phy-rockchip-inno-usb3.c | 1107 +++++++++++++++++
>>   8 files changed, 1378 insertions(+), 6 deletions(-)
>>   create mode 100644 
>> Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb3.yaml
>>   create mode 100644 drivers/phy/rockchip/phy-rockchip-inno-usb3.c
>>
> 
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 0/5] add rk3328 usb3 phy driver
  2019-10-30  0:17   ` Robin Murphy
@ 2019-10-30  1:15     ` Peter Geis
  2020-04-23 20:20       ` Peter Geis
  0 siblings, 1 reply; 22+ messages in thread
From: Peter Geis @ 2019-10-30  1:15 UTC (permalink / raw)
  To: Robin Murphy; +Cc: Heiko Stuebner, kishon, katsuhiro, linux-rockchip, linux-usb

On Tue, Oct 29, 2019, 20:18 Robin Murphy <robin.murphy@arm.com> wrote:
>
> On 2019-10-28 6:41 pm, Robin Murphy wrote:
> > On 28/10/2019 18:22, Peter Geis wrote:
> >> It took a lot more effort than originally anticipated, but here it is.
> >> This is the driver from [0], updated to work with the current kernel.
> >> I've tested it on the rk3328-roc-cc board, both usb 2.0 and usb 3.0
> >> devices detect on hotplug.
> >
> > Thanks Peter, I'll try to give this a go on my box for confirmation.
>
> OK, I hooked it up with a vbus-drv-gpio hacked in - USB 2.0
> unplug/replug does indeed work fine, but it takes a while to acknowledge
> an unplug of a USB 3.0 device, and throws a bunch of errors every time:
>
> [  288.229568] usb usb4-port1: Cannot enable. Maybe the USB cable is bad?
> [  290.809599] usb usb4-port1: Cannot enable. Maybe the USB cable is bad?
> [  293.389594] usb usb4-port1: Cannot enable. Maybe the USB cable is bad?
> [  295.969600] usb usb4-port1: Cannot enable. Maybe the USB cable is bad?
> [  295.970418] usb 4-1: USB disconnect, device number 10
> [  299.209631] usb usb4-port1: Cannot enable. Maybe the USB cable is bad?
> [  301.789655] usb usb4-port1: Cannot enable. Maybe the USB cable is bad?
> [  301.790534] usb usb4-port1: attempt power cycle
>
> (although new devices are still detected OK eventually)
>
> Robin.
(Resending because I forgot the Android Gmail app fails at plain text)

Thanks! May I ask which board and what type of USB 3 device it was?
(I tried a usb3 hub and a Samsung sad)

Also I noticed some odd behavior when I was getting it to work.
When the u3phy driver loaded but wasn't tied to the controller, it
would put everything to sleep.
In this state, the u2 host port also failed to enumerate on boot.
It's almost as if they have something common between them that isn't
being accounted for in the u2 driver.
With the u3phy driver loaded and tied to the controller, I couldn't
get a low speed device to work in the u2 host port (the aeotec zwave
stick at 12m) but a hs device works fine.
>
>
> > One quick comment is that it might be worth importing the version from
> > Rockchip's own kernel tree, as that includes this additional patch which
> > looks like a welcome improvement:
> >
> > https://github.com/rockchip-linux/kernel/commit/12efa9acad65b4c3256683c1ccd769687be3ca56#diff-b6317b3425ac054be551abdcda910b68
> >
> >
> > Also, as it's a new phy driver, we should keep Kishon (+cc) in the loop
> > as the subsystem maintainer.
> >
> > Robin.
> >
> >> [0]
> >> https://github.com/FireflyTeam/kernel/commits/roc-rk3328-cc/drivers/phy/rockchip/phy-rockchip-inno-usb3.c
> >>
> >>
> >> Peter Geis (5):
> >>    phy: rockchip: add inno-usb3 phy driver
> >>    dt-bindings: clean up rockchip grf binding document
> >>    Documentation: bindings: add dt documentation for rockchip usb3 phy
> >>    arm64: dts: rockchip: add usb3 to rk3328 devicetree
> >>    arm64: dts: rockchip: enable usb3 on rk3328-roc-cc
> >>
> >>   .../bindings/phy/phy-rockchip-inno-usb3.yaml  |  157 +++
> >>   .../devicetree/bindings/soc/rockchip/grf.txt  |    8 +-
> >>   .../devicetree/bindings/usb/rockchip,dwc3.txt |    9 +-
> >>   .../arm64/boot/dts/rockchip/rk3328-roc-cc.dts |   21 +
> >>   arch/arm64/boot/dts/rockchip/rk3328.dtsi      |   72 ++
> >>   drivers/phy/rockchip/Kconfig                  |    9 +
> >>   drivers/phy/rockchip/Makefile                 |    1 +
> >>   drivers/phy/rockchip/phy-rockchip-inno-usb3.c | 1107 +++++++++++++++++
> >>   8 files changed, 1378 insertions(+), 6 deletions(-)
> >>   create mode 100644
> >> Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb3.yaml
> >>   create mode 100644 drivers/phy/rockchip/phy-rockchip-inno-usb3.c
> >>
> >
> > _______________________________________________
> > Linux-rockchip mailing list
> > Linux-rockchip@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 4/5] arm64: dts: rockchip: add usb3 to rk3328 devicetree
  2019-10-29 15:31     ` Peter Geis
@ 2019-10-30  2:28       ` Chunfeng Yun
  2019-10-30 17:54         ` Peter Geis
  0 siblings, 1 reply; 22+ messages in thread
From: Chunfeng Yun @ 2019-10-30  2:28 UTC (permalink / raw)
  To: Peter Geis
  Cc: katsuhiro, linux-rockchip, linux-usb, Robin Murphy, Heiko Stuebner

On Tue, 2019-10-29 at 11:31 -0400, Peter Geis wrote:
> On Mon, Oct 28, 2019 at 9:59 PM Chunfeng Yun <chunfeng.yun@mediatek.com> wrote:
> >
> > On Mon, 2019-10-28 at 18:22 +0000, Peter Geis wrote:
> > > Now that we have a proper phy driver, we can add the requisite bits to the
> > > rk3328 device tree.
> > > Added the u3drd and u3phy nodes.
> > >
> > > Signed-off-by: Peter Geis <pgwipeout@gmail.com>
> > > ---
> > >  arch/arm64/boot/dts/rockchip/rk3328.dtsi | 72 ++++++++++++++++++++++++
> > >  1 file changed, 72 insertions(+)
> > >
> > > diff --git a/arch/arm64/boot/dts/rockchip/rk3328.dtsi b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
> > > index 31cc1541f1f5..072e988ad655 100644
> > > --- a/arch/arm64/boot/dts/rockchip/rk3328.dtsi
> > > +++ b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
> > > @@ -805,6 +805,47 @@
> > >               };
> > >       };
> > >
> > > +     usb3phy_grf: syscon@ff460000 {
> > > +             compatible = "rockchip,usb3phy-grf", "syscon";
> > > +             reg = <0x0 0xff460000 0x0 0x1000>;
> > > +     };
> > > +
> > > +     u3phy: usb3-phy@ff470000 {
> > usb-phy?
> 
> This change does not seem wise, as there are also various usb2-phys
> that are controlled via other drivers, while the usb3phy also has to
> support usb2 as a fallback.
We can use u3phy label, or phy-cells = <1>, to provide phys type to
distighuish u3/u2phy if need.

> 
> > > +             compatible = "rockchip,rk3328-u3phy";
> > > +             reg = <0x0 0xff470000 0x0 0x0>;
> > It's zero length, may be not necessary, how about use ranges with
> > parameter ?
> 
> I'll look into this.
> 
> >
> > > +             rockchip,u3phygrf = <&usb3phy_grf>;
> > > +             rockchip,grf = <&grf>;
> > > +             interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>;
> > > +             interrupt-names = "linestate";
> > > +             clocks = <&cru PCLK_USB3PHY_OTG>, <&cru PCLK_USB3PHY_PIPE>;
> > > +             clock-names = "u3phy-otg", "u3phy-pipe";
> > To me, no need u3phy prefix
> 
> Same as above, since this could cause confusion with the usb2 phy devices.
Other info can be used, e.g. compatible;


> 
> > > +             resets = <&cru SRST_USB3PHY_U2>,
> > > +                      <&cru SRST_USB3PHY_U3>,
> > > +                      <&cru SRST_USB3PHY_PIPE>,
> > > +                      <&cru SRST_USB3OTG_UTMI>,
> > > +                      <&cru SRST_USB3PHY_OTG_P>,
> > > +                      <&cru SRST_USB3PHY_PIPE_P>;
> > > +             reset-names = "u3phy-u2-por", "u3phy-u3-por",
> > > +                           "u3phy-pipe-mac", "u3phy-utmi-mac",
> > > +                           "u3phy-utmi-apb", "u3phy-pipe-apb";
> > ditto
> >
> > > +             #address-cells = <2>;
> > > +             #size-cells = <2>;
> > > +             ranges;
> > > +             status = "disabled";
> > > +
> > > +             u3phy_utmi: utmi@ff470000 {
> > usb-phy instead of utmi?
> 
> The stock driver looks for these by name, and they are both different functions.
> This change would break the driver.
Use phy_type? refer to bindings/usb/generic.txt

> 
> >
> > > +                     reg = <0x0 0xff470000 0x0 0x8000>;
> > > +                     #phy-cells = <0>;
> > > +                     status = "disabled";
> > > +             };
> > > +
> > > +             u3phy_pipe: pipe@ff478000 {
> > usb-phy
> 
> Same as above.
> 
> > > +                     reg = <0x0 0xff478000 0x0 0x8000>;
> > > +                     #phy-cells = <0>;
> > > +                     status = "disabled";
> > > +             };
> > > +     };
> > > +
> > >       sdmmc: dwmmc@ff500000 {
> > >               compatible = "rockchip,rk3328-dw-mshc", "rockchip,rk3288-dw-mshc";
> > >               reg = <0x0 0xff500000 0x0 0x4000>;
> > > @@ -936,6 +977,37 @@
> > >               status = "disabled";
> > >       };
> > >
> > > +     usbdrd3: usb@ff600000 {
> > > +             compatible = "rockchip,rk3328-dwc3", "rockchip,rk3399-dwc3";
> > > +             clocks = <&cru SCLK_USB3OTG_REF>, <&cru ACLK_USB3OTG>,
> > > +                      <&cru SCLK_USB3OTG_SUSPEND>;
> > > +             clock-names = "ref", "bus_early",
> > > +                           "suspend";
> > > +             #address-cells = <2>;
> > > +             #size-cells = <2>;
> > > +             ranges;
> > > +             clock-ranges;
> > > +             status = "disabled";
> > > +
> > > +             usbdrd_dwc3: dwc3@ff600000 {
> > > +                     compatible = "snps,dwc3";
> > > +                     reg = <0x0 0xff600000 0x0 0x100000>;
> > > +                     interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
> > > +                     dr_mode = "otg";
> > > +                     phys = <&u3phy_utmi>, <&u3phy_pipe>;
> > > +                     phy-names = "usb2-phy", "usb3-phy";
> > > +                     phy_type = "utmi_wide";
> > > +                     snps,dis_enblslpm_quirk;
> > > +                     snps,dis-u2-freeclk-exists-quirk;
> > > +                     snps,dis_u2_susphy_quirk;
> > > +                     snps,dis_u3_susphy_quirk;
> > > +                     snps,dis-del-phy-power-chg-quirk;
> > > +                     snps,dis-tx-ipgap-linecheck-quirk;
> > > +                     snps,xhci-trb-ent-quirk;
> > > +                     status = "disabled";
> > > +             };
> > > +     };
> > > +
> > >       gic: interrupt-controller@ff811000 {
> > >               compatible = "arm,gic-400";
> > >               #interrupt-cells = <3>;
> >
> >
> >
> > _______________________________________________
> > Linux-rockchip mailing list
> > Linux-rockchip@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-rockchip



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/5] phy: rockchip: add inno-usb3 phy driver
  2019-10-29 15:26     ` Peter Geis
@ 2019-10-30  2:59       ` Chunfeng Yun
  2019-10-30 17:50         ` Peter Geis
  0 siblings, 1 reply; 22+ messages in thread
From: Chunfeng Yun @ 2019-10-30  2:59 UTC (permalink / raw)
  To: Peter Geis
  Cc: katsuhiro, linux-rockchip, linux-usb, Robin Murphy, Heiko Stuebner

On Tue, 2019-10-29 at 11:26 -0400, Peter Geis wrote:
> On Mon, Oct 28, 2019 at 10:12 PM Chunfeng Yun <chunfeng.yun@mediatek.com> wrote:
> >
> > On Mon, 2019-10-28 at 18:22 +0000, Peter Geis wrote:
> > > Add the rockchip innosilicon usb3 phy driver, supporting devices such as the rk3328.
> > > Pulled from:
> > > https://github.com/FireflyTeam/kernel/blob/roc-rk3328-cc/drivers/phy/rockchip/phy-rockchip-inno-usb3.c
> > >
> > > Signed-off-by: Peter Geis <pgwipeout@gmail.com>
> > > ---
> > >  drivers/phy/rockchip/Kconfig                  |    9 +
> > >  drivers/phy/rockchip/Makefile                 |    1 +
> > >  drivers/phy/rockchip/phy-rockchip-inno-usb3.c | 1107 +++++++++++++++++
> > >  3 files changed, 1117 insertions(+)
> > >  create mode 100644 drivers/phy/rockchip/phy-rockchip-inno-usb3.c
> > >
...
> > > +static int rk3328_u3phy_pipe_power(struct rockchip_u3phy *u3phy,
> > > +                                struct rockchip_u3phy_port *u3phy_port,
> > > +                                bool on)
> > > +{
> > > +     unsigned int reg;
> > > +
> > > +     if (on) {
> > > +             reg = readl(u3phy_port->base + 0x1a8);
> > > +             reg &= ~BIT(4); /* ldo power up */
> > > +             writel(reg, u3phy_port->base + 0x1a8);
> > > +
> > > +             reg = readl(u3phy_port->base + 0x044);
> > > +             reg &= ~BIT(4); /* bg power on */
> > > +             writel(reg, u3phy_port->base + 0x044);
> > > +
> > > +             reg = readl(u3phy_port->base + 0x150);
> > > +             reg |= BIT(6); /* tx bias enable */
> > > +             writel(reg, u3phy_port->base + 0x150);
> > > +
> > > +             reg = readl(u3phy_port->base + 0x080);
> > > +             reg &= ~BIT(2); /* tx cm power up */
> > > +             writel(reg, u3phy_port->base + 0x080);
> > > +
> > > +             reg = readl(u3phy_port->base + 0x0c0);
> > > +             /* tx obs enable and rx cm enable */
> > > +             reg |= (BIT(3) | BIT(4));
> > > +             writel(reg, u3phy_port->base + 0x0c0);
> > > +
> > > +             udelay(1);
> > > +     } else {
> > > +             reg = readl(u3phy_port->base + 0x1a8);
> > > +             reg |= BIT(4); /* ldo power down */
> > > +             writel(reg, u3phy_port->base + 0x1a8);
> > > +
> > > +             reg = readl(u3phy_port->base + 0x044);
> > > +             reg |= BIT(4); /* bg power down */
> > > +             writel(reg, u3phy_port->base + 0x044);
> > > +
> > > +             reg = readl(u3phy_port->base + 0x150);
> > > +             reg &= ~BIT(6); /* tx bias disable */
> > > +             writel(reg, u3phy_port->base + 0x150);
> > > +
> > > +             reg = readl(u3phy_port->base + 0x080);
> > > +             reg |= BIT(2); /* tx cm power down */
> > > +             writel(reg, u3phy_port->base + 0x080);
> > > +
> > > +             reg = readl(u3phy_port->base + 0x0c0);
> > > +             /* tx obs disable and rx cm disable */
> > > +             reg &= ~(BIT(3) | BIT(4));
> > > +             writel(reg, u3phy_port->base + 0x0c0);
> > > +     }
> > Try to avoid magic number
> 
> Since this was not my driver, I only pulled it in and made the
> necessary changes to get it working, I tried to refrain from
> modifications as much as possible.
> Do you want me to convert these addresses to definitions based on the
> comments in the code?
It's better to follow register names defined in register map doc if you
have
> 
> >
> > > +
> > > +     return 0;
> > > +}
> > > +



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/5] phy: rockchip: add inno-usb3 phy driver
  2019-10-28 18:22 ` [PATCH 1/5] phy: rockchip: add inno-usb3 " Peter Geis
  2019-10-29  2:11   ` Chunfeng Yun
@ 2019-10-30  8:15   ` Heiko Stuebner
  2019-10-30 17:46     ` Peter Geis
  1 sibling, 1 reply; 22+ messages in thread
From: Heiko Stuebner @ 2019-10-30  8:15 UTC (permalink / raw)
  To: Peter Geis; +Cc: linux-rockchip, linux-usb, katsuhiro, robin.murphy

Am Montag, 28. Oktober 2019, 19:22:50 CET schrieb Peter Geis:
> Add the rockchip innosilicon usb3 phy driver, supporting devices such as the rk3328.
> Pulled from:
> https://github.com/FireflyTeam/kernel/blob/roc-rk3328-cc/drivers/phy/rockchip/phy-rockchip-inno-usb3.c
> 
> Signed-off-by: Peter Geis <pgwipeout@gmail.com>

What happens on plug - unplug - plug?

I remember what kept me from pushing the usb3 stuff for rk3328
was the usbphy breaking hotplug after the first unplug.

Did this get fixed?

Thanks
Heiko



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/5] phy: rockchip: add inno-usb3 phy driver
  2019-10-30  8:15   ` Heiko Stuebner
@ 2019-10-30 17:46     ` Peter Geis
  2019-10-31 14:02       ` Peter Geis
  0 siblings, 1 reply; 22+ messages in thread
From: Peter Geis @ 2019-10-30 17:46 UTC (permalink / raw)
  To: Heiko Stuebner; +Cc: linux-rockchip, linux-usb, katsuhiro, Robin Murphy

On Wed, Oct 30, 2019 at 4:15 AM Heiko Stuebner <heiko@sntech.de> wrote:
>
> Am Montag, 28. Oktober 2019, 19:22:50 CET schrieb Peter Geis:
> > Add the rockchip innosilicon usb3 phy driver, supporting devices such as the rk3328.
> > Pulled from:
> > https://github.com/FireflyTeam/kernel/blob/roc-rk3328-cc/drivers/phy/rockchip/phy-rockchip-inno-usb3.c
> >
> > Signed-off-by: Peter Geis <pgwipeout@gmail.com>
>
> What happens on plug - unplug - plug?
>
> I remember what kept me from pushing the usb3 stuff for rk3328
> was the usbphy breaking hotplug after the first unplug.
>
> Did this get fixed?
>
> Thanks
> Heiko
>
>
It seems to have been corrected, at least partially.
The hotplug issue may have been resolved by [0].

On my rk3328-roc-cc board, both USB2 and USB3 work correctly for
hotplugging with this driver.
I did encounter some odd behavior with the USB2HOST port, where it
stopped reading FS devices when this was loaded.
I'm wondering if we have a common clock problem or some other
undocumented shared dependancy.

Robin encountered some odd behavior when unplugging a USB3 device, the
controller didn't recognize it for a while.
I don't have that problem, but I don't know what board she's using.

[0] commit fb903392131a324a243c7731389277db1cd9f8df
clk: rockchip: fix wrong clock definitions for rk3328

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/5] phy: rockchip: add inno-usb3 phy driver
  2019-10-30  2:59       ` Chunfeng Yun
@ 2019-10-30 17:50         ` Peter Geis
  0 siblings, 0 replies; 22+ messages in thread
From: Peter Geis @ 2019-10-30 17:50 UTC (permalink / raw)
  To: Chunfeng Yun
  Cc: katsuhiro, linux-rockchip, linux-usb, Robin Murphy, Heiko Stuebner

On Tue, Oct 29, 2019 at 10:59 PM Chunfeng Yun <chunfeng.yun@mediatek.com> wrote:
>
> On Tue, 2019-10-29 at 11:26 -0400, Peter Geis wrote:
> > On Mon, Oct 28, 2019 at 10:12 PM Chunfeng Yun <chunfeng.yun@mediatek.com> wrote:
> > >
> > > On Mon, 2019-10-28 at 18:22 +0000, Peter Geis wrote:
> > > > Add the rockchip innosilicon usb3 phy driver, supporting devices such as the rk3328.
> > > > Pulled from:
> > > > https://github.com/FireflyTeam/kernel/blob/roc-rk3328-cc/drivers/phy/rockchip/phy-rockchip-inno-usb3.c
> > > >
> > > > Signed-off-by: Peter Geis <pgwipeout@gmail.com>
> > > > ---
> > > >  drivers/phy/rockchip/Kconfig                  |    9 +
> > > >  drivers/phy/rockchip/Makefile                 |    1 +
> > > >  drivers/phy/rockchip/phy-rockchip-inno-usb3.c | 1107 +++++++++++++++++
> > > >  3 files changed, 1117 insertions(+)
> > > >  create mode 100644 drivers/phy/rockchip/phy-rockchip-inno-usb3.c
> > > >
> ...
> > > > +static int rk3328_u3phy_pipe_power(struct rockchip_u3phy *u3phy,
> > > > +                                struct rockchip_u3phy_port *u3phy_port,
> > > > +                                bool on)
> > > > +{
> > > > +     unsigned int reg;
> > > > +
> > > > +     if (on) {
> > > > +             reg = readl(u3phy_port->base + 0x1a8);
> > > > +             reg &= ~BIT(4); /* ldo power up */
> > > > +             writel(reg, u3phy_port->base + 0x1a8);
> > > > +
> > > > +             reg = readl(u3phy_port->base + 0x044);
> > > > +             reg &= ~BIT(4); /* bg power on */
> > > > +             writel(reg, u3phy_port->base + 0x044);
> > > > +
> > > > +             reg = readl(u3phy_port->base + 0x150);
> > > > +             reg |= BIT(6); /* tx bias enable */
> > > > +             writel(reg, u3phy_port->base + 0x150);
> > > > +
> > > > +             reg = readl(u3phy_port->base + 0x080);
> > > > +             reg &= ~BIT(2); /* tx cm power up */
> > > > +             writel(reg, u3phy_port->base + 0x080);
> > > > +
> > > > +             reg = readl(u3phy_port->base + 0x0c0);
> > > > +             /* tx obs enable and rx cm enable */
> > > > +             reg |= (BIT(3) | BIT(4));
> > > > +             writel(reg, u3phy_port->base + 0x0c0);
> > > > +
> > > > +             udelay(1);
> > > > +     } else {
> > > > +             reg = readl(u3phy_port->base + 0x1a8);
> > > > +             reg |= BIT(4); /* ldo power down */
> > > > +             writel(reg, u3phy_port->base + 0x1a8);
> > > > +
> > > > +             reg = readl(u3phy_port->base + 0x044);
> > > > +             reg |= BIT(4); /* bg power down */
> > > > +             writel(reg, u3phy_port->base + 0x044);
> > > > +
> > > > +             reg = readl(u3phy_port->base + 0x150);
> > > > +             reg &= ~BIT(6); /* tx bias disable */
> > > > +             writel(reg, u3phy_port->base + 0x150);
> > > > +
> > > > +             reg = readl(u3phy_port->base + 0x080);
> > > > +             reg |= BIT(2); /* tx cm power down */
> > > > +             writel(reg, u3phy_port->base + 0x080);
> > > > +
> > > > +             reg = readl(u3phy_port->base + 0x0c0);
> > > > +             /* tx obs disable and rx cm disable */
> > > > +             reg &= ~(BIT(3) | BIT(4));
> > > > +             writel(reg, u3phy_port->base + 0x0c0);
> > > > +     }
> > > Try to avoid magic number
> >
> > Since this was not my driver, I only pulled it in and made the
> > necessary changes to get it working, I tried to refrain from
> > modifications as much as possible.
> > Do you want me to convert these addresses to definitions based on the
> > comments in the code?
> It's better to follow register names defined in register map doc if you
> have

Some of these registers appear to be in the rk3328 TRM.
Most, such as the tuning registers, are not documented anywhere I can find.
:'( Why rockchip, why?!
> >
> > >
> > > > +
> > > > +     return 0;
> > > > +}
> > > > +
>
>
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 4/5] arm64: dts: rockchip: add usb3 to rk3328 devicetree
  2019-10-30  2:28       ` Chunfeng Yun
@ 2019-10-30 17:54         ` Peter Geis
  0 siblings, 0 replies; 22+ messages in thread
From: Peter Geis @ 2019-10-30 17:54 UTC (permalink / raw)
  To: Chunfeng Yun
  Cc: katsuhiro, linux-rockchip, linux-usb, Robin Murphy, Heiko Stuebner

On Tue, Oct 29, 2019 at 10:28 PM Chunfeng Yun <chunfeng.yun@mediatek.com> wrote:
>
> On Tue, 2019-10-29 at 11:31 -0400, Peter Geis wrote:
> > On Mon, Oct 28, 2019 at 9:59 PM Chunfeng Yun <chunfeng.yun@mediatek.com> wrote:
> > >
> > > On Mon, 2019-10-28 at 18:22 +0000, Peter Geis wrote:
> > > > Now that we have a proper phy driver, we can add the requisite bits to the
> > > > rk3328 device tree.
> > > > Added the u3drd and u3phy nodes.
> > > >
> > > > Signed-off-by: Peter Geis <pgwipeout@gmail.com>
> > > > ---
> > > >  arch/arm64/boot/dts/rockchip/rk3328.dtsi | 72 ++++++++++++++++++++++++
> > > >  1 file changed, 72 insertions(+)
> > > >
> > > > diff --git a/arch/arm64/boot/dts/rockchip/rk3328.dtsi b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
> > > > index 31cc1541f1f5..072e988ad655 100644
> > > > --- a/arch/arm64/boot/dts/rockchip/rk3328.dtsi
> > > > +++ b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
> > > > @@ -805,6 +805,47 @@
> > > >               };
> > > >       };
> > > >
> > > > +     usb3phy_grf: syscon@ff460000 {
> > > > +             compatible = "rockchip,usb3phy-grf", "syscon";
> > > > +             reg = <0x0 0xff460000 0x0 0x1000>;
> > > > +     };
> > > > +
> > > > +     u3phy: usb3-phy@ff470000 {
> > > usb-phy?
> >
> > This change does not seem wise, as there are also various usb2-phys
> > that are controlled via other drivers, while the usb3phy also has to
> > support usb2 as a fallback.
> We can use u3phy label, or phy-cells = <1>, to provide phys type to
> distighuish u3/u2phy if need.
>
> >
> > > > +             compatible = "rockchip,rk3328-u3phy";
> > > > +             reg = <0x0 0xff470000 0x0 0x0>;
> > > It's zero length, may be not necessary, how about use ranges with
> > > parameter ?
> >
> > I'll look into this.
> >
> > >
> > > > +             rockchip,u3phygrf = <&usb3phy_grf>;
> > > > +             rockchip,grf = <&grf>;
> > > > +             interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>;
> > > > +             interrupt-names = "linestate";
> > > > +             clocks = <&cru PCLK_USB3PHY_OTG>, <&cru PCLK_USB3PHY_PIPE>;
> > > > +             clock-names = "u3phy-otg", "u3phy-pipe";
> > > To me, no need u3phy prefix
> >
> > Same as above, since this could cause confusion with the usb2 phy devices.
> Other info can be used, e.g. compatible;
>
>
> >
> > > > +             resets = <&cru SRST_USB3PHY_U2>,
> > > > +                      <&cru SRST_USB3PHY_U3>,
> > > > +                      <&cru SRST_USB3PHY_PIPE>,
> > > > +                      <&cru SRST_USB3OTG_UTMI>,
> > > > +                      <&cru SRST_USB3PHY_OTG_P>,
> > > > +                      <&cru SRST_USB3PHY_PIPE_P>;
> > > > +             reset-names = "u3phy-u2-por", "u3phy-u3-por",
> > > > +                           "u3phy-pipe-mac", "u3phy-utmi-mac",
> > > > +                           "u3phy-utmi-apb", "u3phy-pipe-apb";
> > > ditto
> > >
> > > > +             #address-cells = <2>;
> > > > +             #size-cells = <2>;
> > > > +             ranges;
> > > > +             status = "disabled";
> > > > +
> > > > +             u3phy_utmi: utmi@ff470000 {
> > > usb-phy instead of utmi?
> >
> > The stock driver looks for these by name, and they are both different functions.
> > This change would break the driver.
> Use phy_type? refer to bindings/usb/generic.txt

Roger, thank you for your feedback.
I have a bit of work to do for v2, although I'm hoping for a few more
tests to come in.
So far all I have is my rk3328-roc-cc and Robin's test.

>
> >
> > >
> > > > +                     reg = <0x0 0xff470000 0x0 0x8000>;
> > > > +                     #phy-cells = <0>;
> > > > +                     status = "disabled";
> > > > +             };
> > > > +
> > > > +             u3phy_pipe: pipe@ff478000 {
> > > usb-phy
> >
> > Same as above.
> >
> > > > +                     reg = <0x0 0xff478000 0x0 0x8000>;
> > > > +                     #phy-cells = <0>;
> > > > +                     status = "disabled";
> > > > +             };
> > > > +     };
> > > > +
> > > >       sdmmc: dwmmc@ff500000 {
> > > >               compatible = "rockchip,rk3328-dw-mshc", "rockchip,rk3288-dw-mshc";
> > > >               reg = <0x0 0xff500000 0x0 0x4000>;
> > > > @@ -936,6 +977,37 @@
> > > >               status = "disabled";
> > > >       };
> > > >
> > > > +     usbdrd3: usb@ff600000 {
> > > > +             compatible = "rockchip,rk3328-dwc3", "rockchip,rk3399-dwc3";
> > > > +             clocks = <&cru SCLK_USB3OTG_REF>, <&cru ACLK_USB3OTG>,
> > > > +                      <&cru SCLK_USB3OTG_SUSPEND>;
> > > > +             clock-names = "ref", "bus_early",
> > > > +                           "suspend";
> > > > +             #address-cells = <2>;
> > > > +             #size-cells = <2>;
> > > > +             ranges;
> > > > +             clock-ranges;
> > > > +             status = "disabled";
> > > > +
> > > > +             usbdrd_dwc3: dwc3@ff600000 {
> > > > +                     compatible = "snps,dwc3";
> > > > +                     reg = <0x0 0xff600000 0x0 0x100000>;
> > > > +                     interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
> > > > +                     dr_mode = "otg";
> > > > +                     phys = <&u3phy_utmi>, <&u3phy_pipe>;
> > > > +                     phy-names = "usb2-phy", "usb3-phy";
> > > > +                     phy_type = "utmi_wide";
> > > > +                     snps,dis_enblslpm_quirk;
> > > > +                     snps,dis-u2-freeclk-exists-quirk;
> > > > +                     snps,dis_u2_susphy_quirk;
> > > > +                     snps,dis_u3_susphy_quirk;
> > > > +                     snps,dis-del-phy-power-chg-quirk;
> > > > +                     snps,dis-tx-ipgap-linecheck-quirk;
> > > > +                     snps,xhci-trb-ent-quirk;
> > > > +                     status = "disabled";
> > > > +             };
> > > > +     };
> > > > +
> > > >       gic: interrupt-controller@ff811000 {
> > > >               compatible = "arm,gic-400";
> > > >               #interrupt-cells = <3>;
> > >
> > >
> > >
> > > _______________________________________________
> > > Linux-rockchip mailing list
> > > Linux-rockchip@lists.infradead.org
> > > http://lists.infradead.org/mailman/listinfo/linux-rockchip
>
>

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 1/5] phy: rockchip: add inno-usb3 phy driver
  2019-10-30 17:46     ` Peter Geis
@ 2019-10-31 14:02       ` Peter Geis
  0 siblings, 0 replies; 22+ messages in thread
From: Peter Geis @ 2019-10-31 14:02 UTC (permalink / raw)
  To: Heiko Stuebner; +Cc: linux-rockchip, linux-usb, katsuhiro, Robin Murphy

On Wed, Oct 30, 2019 at 1:46 PM Peter Geis <pgwipeout@gmail.com> wrote:
>
> On Wed, Oct 30, 2019 at 4:15 AM Heiko Stuebner <heiko@sntech.de> wrote:
> >
> > Am Montag, 28. Oktober 2019, 19:22:50 CET schrieb Peter Geis:
> > > Add the rockchip innosilicon usb3 phy driver, supporting devices such as the rk3328.
> > > Pulled from:
> > > https://github.com/FireflyTeam/kernel/blob/roc-rk3328-cc/drivers/phy/rockchip/phy-rockchip-inno-usb3.c
> > >
> > > Signed-off-by: Peter Geis <pgwipeout@gmail.com>
> >
> > What happens on plug - unplug - plug?
> >
> > I remember what kept me from pushing the usb3 stuff for rk3328
> > was the usbphy breaking hotplug after the first unplug.
> >
> > Did this get fixed?
> >
> > Thanks
> > Heiko
> >
> >
> It seems to have been corrected, at least partially.
> The hotplug issue may have been resolved by [0].
Other possibilities are two commits [1], [2].
One disables usb3 autosuspend, the other adds a linestate quirk.
[1] https://github.com/FireflyTeam/kernel/commit/1758c75d334c7b321a86708797541e05e9301ec5
[2] https://github.com/FireflyTeam/kernel/commit/65bfd30a169c880da359a6dfef032b670457debd
>
> On my rk3328-roc-cc board, both USB2 and USB3 work correctly for
> hotplugging with this driver.
> I did encounter some odd behavior with the USB2HOST port, where it
> stopped reading FS devices when this was loaded.
> I'm wondering if we have a common clock problem or some other
> undocumented shared dependancy.
>
> Robin encountered some odd behavior when unplugging a USB3 device, the
> controller didn't recognize it for a while.
> I don't have that problem, but I don't know what board she's using.
>
> [0] commit fb903392131a324a243c7731389277db1cd9f8df
> clk: rockchip: fix wrong clock definitions for rk3328

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 2/5] dt-bindings: clean up rockchip grf binding document
  2019-10-28 18:22 ` [PATCH 2/5] dt-bindings: clean up rockchip grf binding document Peter Geis
@ 2019-10-31 14:25   ` Heiko Stuebner
  0 siblings, 0 replies; 22+ messages in thread
From: Heiko Stuebner @ 2019-10-31 14:25 UTC (permalink / raw)
  To: Peter Geis; +Cc: linux-rockchip, linux-usb, katsuhiro, robin.murphy

Am Montag, 28. Oktober 2019, 19:22:51 CET schrieb Peter Geis:
> Fixup some typos and inconsistencies in the grf binding.
> 
> Signed-off-by: Peter Geis <pgwipeout@gmail.com>

applied for 5.5

Thanks
Heiko



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH 0/5] add rk3328 usb3 phy driver
  2019-10-30  1:15     ` Peter Geis
@ 2020-04-23 20:20       ` Peter Geis
  0 siblings, 0 replies; 22+ messages in thread
From: Peter Geis @ 2020-04-23 20:20 UTC (permalink / raw)
  To: Robin Murphy
  Cc: Heiko Stuebner, kishon, katsuhiro, open list:ARM/Rockchip SoC...,
	linux-usb

As a status update for this, I have spent quite a while working on this driver.
In the end, it appears the available documentation is insufficient.
The original driver calls a few registers that are not documented in the TRM.

What I have found is the following:
The 2.0 functionality works as documented in the original rockchip driver.
The 3.0 functionality does not.
A 3.0 device does not trigger the interrupt like a 2.0 device does.
A 3.0 device causes 0x34 bit 6 to toggle, which is usb3otg_pipe3_phystatus
I've enabled all usb3phy interrupts, and cannot get a 3.0 device to
trigger any of them.

Did the original driver ever work correctly?

^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2020-04-23 20:20 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-10-28 18:22 [PATCH 0/5] add rk3328 usb3 phy driver Peter Geis
2019-10-28 18:22 ` [PATCH 1/5] phy: rockchip: add inno-usb3 " Peter Geis
2019-10-29  2:11   ` Chunfeng Yun
2019-10-29 15:26     ` Peter Geis
2019-10-30  2:59       ` Chunfeng Yun
2019-10-30 17:50         ` Peter Geis
2019-10-30  8:15   ` Heiko Stuebner
2019-10-30 17:46     ` Peter Geis
2019-10-31 14:02       ` Peter Geis
2019-10-28 18:22 ` [PATCH 2/5] dt-bindings: clean up rockchip grf binding document Peter Geis
2019-10-31 14:25   ` Heiko Stuebner
2019-10-28 18:22 ` [PATCH 3/5] Documentation: bindings: add dt documentation for rockchip usb3 phy Peter Geis
2019-10-28 18:22 ` [PATCH 4/5] arm64: dts: rockchip: add usb3 to rk3328 devicetree Peter Geis
2019-10-29  1:58   ` Chunfeng Yun
2019-10-29 15:31     ` Peter Geis
2019-10-30  2:28       ` Chunfeng Yun
2019-10-30 17:54         ` Peter Geis
2019-10-28 18:22 ` [PATCH 5/5] arm64: dts: rockchip: enable usb3 on rk3328-roc-cc Peter Geis
2019-10-28 18:41 ` [PATCH 0/5] add rk3328 usb3 phy driver Robin Murphy
2019-10-30  0:17   ` Robin Murphy
2019-10-30  1:15     ` Peter Geis
2020-04-23 20:20       ` Peter Geis

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).