[PATCH v4 05/10] phy: rockchip: add phy-rockchip-usb2.c

Johan Jonker jbx6244 at gmail.com
Thu Jun 4 01:50:09 CEST 2026


Add phy-rockchip-usb2.c driver with support
for RK3066, RK3188 and RK3288 pdata.

Signed-off-by: Johan Jonker <jbx6244 at gmail.com>
---

Changed V4:
restyle
remove clk
(Jonas)
split Makefile and Kconfig sort
fixup SPL_PHY_ROCKCHIP_USB2
remove DECLARE_GLOBAL_DATA_PTR
add bind rollback
use device_get_supply_regulator
use regulator_set_enable_if_allowed
use reset_assert_bulk functions
init the port in the port probe function
protect against a blank struct
remove of_xlate
remove RESET_ROCKCHIP

Changed V2:
add DM_FLAG_PROBE_AFTER_BIND
restyle
---
 drivers/phy/rockchip/Kconfig             |  14 ++
 drivers/phy/rockchip/Makefile            |   1 +
 drivers/phy/rockchip/phy-rockchip-usb2.c | 235 +++++++++++++++++++++++
 3 files changed, 250 insertions(+)
 create mode 100644 drivers/phy/rockchip/phy-rockchip-usb2.c

diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig
index 1662aef0b8fe..8b59008a303d 100644
--- a/drivers/phy/rockchip/Kconfig
+++ b/drivers/phy/rockchip/Kconfig
@@ -56,6 +56,20 @@ config PHY_ROCKCHIP_TYPEC
 	help
 	  Support for Rockchip USB TYPEC PHY.

+config PHY_ROCKCHIP_USB2
+	bool "Rockchip USB2 PHY"
+	depends on ARCH_ROCKCHIP
+	select PHY
+	help
+	  Support for Rockchip USB 2.0 PHY.
+
+config SPL_PHY_ROCKCHIP_USB2
+	bool "Rockchip USB2 PHY in SPL"
+	depends on ARCH_ROCKCHIP && SPL
+	select SPL_PHY
+	help
+	  Support for Rockchip USB 2.0 PHY.
+
 config PHY_ROCKCHIP_USBDP
 	bool "Rockchip USBDP COMBO PHY Driver"
 	depends on ARCH_ROCKCHIP
diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile
index b5e4763572bb..725c58cf708b 100644
--- a/drivers/phy/rockchip/Makefile
+++ b/drivers/phy/rockchip/Makefile
@@ -10,4 +10,5 @@ obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBOPHY)	+= phy-rockchip-naneng-combphy.o
 obj-$(CONFIG_PHY_ROCKCHIP_PCIE)		+= phy-rockchip-pcie.o
 obj-$(CONFIG_PHY_ROCKCHIP_SNPS_PCIE3)	+= phy-rockchip-snps-pcie3.o
 obj-$(CONFIG_PHY_ROCKCHIP_TYPEC)	+= phy-rockchip-typec.o
+obj-$(CONFIG_$(PHASE_)PHY_ROCKCHIP_USB2)	+= phy-rockchip-usb2.o
 obj-$(CONFIG_PHY_ROCKCHIP_USBDP)	+= phy-rockchip-usbdp.o
diff --git a/drivers/phy/rockchip/phy-rockchip-usb2.c b/drivers/phy/rockchip/phy-rockchip-usb2.c
new file mode 100644
index 000000000000..dfeeb0381cd8
--- /dev/null
+++ b/drivers/phy/rockchip/phy-rockchip-usb2.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <dm.h>
+#include <dm/device.h>
+#include <dm/device_compat.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <generic-phy.h>
+#include <linux/delay.h>
+#include <power/regulator.h>
+#include <regmap.h>
+#include <reset.h>
+
+#define BIT_WRITEABLE_SHIFT	16
+
+struct usb_phy_port_reg {
+	unsigned int offset;
+	unsigned int bitend;
+	unsigned int bitstart;
+	unsigned int disable;
+	unsigned int enable;
+};
+
+struct rockchip_usb_phy_port_cfg {
+	struct usb_phy_port_reg port_reset;
+	struct usb_phy_port_reg soft_con;
+	struct usb_phy_port_reg suspend;
+};
+
+struct rockchip_usb_phy_port_priv {
+	void __iomem *reg_base;
+	u32 reg_offset;
+	struct reset_ctl_bulk resets;
+	struct udevice *vbus_supply;
+	const struct rockchip_usb_phy_port_cfg *port_cfg;
+};
+
+static void rockchip_usb_phy_port_property_enable(struct phy *phy,
+						  const struct usb_phy_port_reg *reg,
+						  bool en)
+{
+	struct rockchip_usb_phy_port_priv *priv = dev_get_priv(phy->dev);
+	unsigned int val, mask, tmp;
+
+	if (!reg->offset && !reg->enable && !reg->disable)
+		return;
+
+	tmp = en ? reg->enable : reg->disable;
+	mask = GENMASK(reg->bitend, reg->bitstart);
+	val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT);
+
+	regmap_write(priv->reg_base, priv->reg_offset + reg->offset, val);
+}
+
+static int rockchip_usb_phy_port_power_on(struct phy *phy)
+{
+	struct rockchip_usb_phy_port_priv *priv = dev_get_priv(phy->dev);
+	const struct rockchip_usb_phy_port_cfg *port_cfg = priv->port_cfg;
+	int ret;
+
+	if (priv->vbus_supply) {
+		ret = regulator_set_enable_if_allowed(priv->vbus_supply, true);
+		if (ret)
+			return ret;
+	}
+
+	/* Exit suspend. */
+	rockchip_usb_phy_port_property_enable(phy, &port_cfg->suspend, false);
+	udelay(2000);
+
+	return 0;
+}
+
+static int rockchip_usb_phy_port_power_off(struct phy *phy)
+{
+	struct rockchip_usb_phy_port_priv *priv = dev_get_priv(phy->dev);
+	const struct rockchip_usb_phy_port_cfg *port_cfg = priv->port_cfg;
+
+	/* Enter suspend. */
+	rockchip_usb_phy_port_property_enable(phy, &port_cfg->suspend, true);
+
+	if (!priv->vbus_supply)
+		return 0;
+
+	return regulator_set_enable_if_allowed(priv->vbus_supply, false);
+}
+
+static int rockchip_usb_phy_port_reset(struct phy *phy)
+{
+	struct rockchip_usb_phy_port_priv *priv = dev_get_priv(phy->dev);
+
+	reset_assert_bulk(&priv->resets);
+	udelay(10);
+	reset_deassert_bulk(&priv->resets);
+
+	return 0;
+}
+
+static int rockchip_usb_phy_port_init(struct phy *phy)
+{
+	struct rockchip_usb_phy_port_priv *priv = dev_get_priv(phy->dev);
+	const struct rockchip_usb_phy_port_cfg *port_cfg = priv->port_cfg;
+
+
+	/* Disable software control. */
+	rockchip_usb_phy_port_property_enable(phy, &port_cfg->soft_con, false);
+
+	/* Reset OTG port. */
+	rockchip_usb_phy_port_property_enable(phy, &port_cfg->port_reset, true);
+	mdelay(1);
+	rockchip_usb_phy_port_property_enable(phy, &port_cfg->port_reset, false);
+	udelay(1);
+	return 0;
+}
+
+static int rockchip_usb_phy_port_exit(struct phy *phy)
+{
+	struct rockchip_usb_phy_port_priv *priv = dev_get_priv(phy->dev);
+	const struct rockchip_usb_phy_port_cfg *port_cfg = priv->port_cfg;
+
+	/* Enable software control. */
+	rockchip_usb_phy_port_property_enable(phy, &port_cfg->soft_con, true);
+
+	return 0;
+}
+
+static int rockchip_usb_phy_port_probe(struct udevice *dev)
+{
+	struct rockchip_usb_phy_port_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	priv->port_cfg = (const struct rockchip_usb_phy_port_cfg *)
+				dev_get_driver_data(dev_get_parent(dev));
+	if (!priv->port_cfg)
+		return -EINVAL;
+
+	priv->reg_base = dev_read_addr_ptr(dev_get_parent(dev_get_parent(dev)));
+	if (!priv->reg_base)
+		return -EINVAL;
+
+	ret = dev_read_u32(dev, "reg", &priv->reg_offset);
+	if (ret)
+		return ret;
+
+	ret = device_get_supply_regulator(dev, "vbus-supply", &priv->vbus_supply);
+	if (ret && ret != -ENOENT && ret != -ENOSYS) {
+		dev_err(dev, "device_get_supply_regulator failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = reset_get_bulk(dev, &priv->resets);
+	if (ret && ret != -ENOENT && ret != -ENOTSUPP) {
+		dev_err(dev, "reset_get_bulk failed: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rockchip_usb_phy_bind(struct udevice *dev)
+{
+	const char *name;
+	ofnode node;
+	int ret;
+
+	dev_for_each_subnode(node, dev) {
+		if (!ofnode_is_enabled(node))
+			continue;
+
+		name = ofnode_get_name(node);
+		dev_dbg(dev, "subnode %s\n", name);
+
+		ret = device_bind_driver_to_node(dev, "rockchip_usb_phy_port",
+						 name, node, NULL);
+		if (ret) {
+			dev_err(dev, "'%s' cannot bind 'rockchip_usb_phy_port'\n", name);
+			goto bind_fail;
+		}
+	}
+
+	return 0;
+
+bind_fail:
+	device_chld_unbind(dev, NULL);
+
+	return ret;
+}
+
+static struct phy_ops rockchip_usb_phy_port_ops = {
+	.init		= rockchip_usb_phy_port_init,
+	.exit		= rockchip_usb_phy_port_exit,
+	.power_on	= rockchip_usb_phy_port_power_on,
+	.power_off	= rockchip_usb_phy_port_power_off,
+	.reset		= rockchip_usb_phy_port_reset,
+};
+
+static const struct rockchip_usb_phy_port_cfg rk3066a_pdata = {
+	.port_reset	= {0x00, 12, 12, 0, 1},
+	.soft_con	= {0x08, 2, 2, 0, 1},
+	.suspend	= {0x08, 8, 3, (0x01 << 3), (0x2A << 3)},
+};
+
+static const struct rockchip_usb_phy_port_cfg rk3188_pdata = {
+	.port_reset	= {0x00, 12, 12, 0, 1},
+	.soft_con	= {0x08, 2, 2, 0, 1},
+	.suspend	= {0x0c, 5, 0, 0x01, 0x2A},
+};
+
+static const struct rockchip_usb_phy_port_cfg rk3288_pdata = {
+	.port_reset	= {0x00, 12, 12, 0, 1},
+	.soft_con	= {0x08, 2, 2, 0, 1},
+	.suspend	= {0x0c, 5, 0, 0x01, 0x2A},
+};
+
+static const struct udevice_id rockchip_usb_phy_ids[] = {
+	{ .compatible = "rockchip,rk3066a-usb-phy", .data = (ulong)&rk3066a_pdata },
+	{ .compatible = "rockchip,rk3188-usb-phy", .data = (ulong)&rk3188_pdata },
+	{ .compatible = "rockchip,rk3288-usb-phy", .data = (ulong)&rk3288_pdata },
+	{}
+};
+
+U_BOOT_DRIVER(rockchip_usb_phy_port) = {
+	.name		= "rockchip_usb_phy_port",
+	.id		= UCLASS_PHY,
+	.ops		= &rockchip_usb_phy_port_ops,
+	.probe		= rockchip_usb_phy_port_probe,
+	.priv_auto	= sizeof(struct rockchip_usb_phy_port_priv),
+};
+
+U_BOOT_DRIVER(rockchip_usb_phy) = {
+	.name		= "rockchip_usb_phy",
+	.id		= UCLASS_NOP,
+	.of_match	= rockchip_usb_phy_ids,
+	.bind		= rockchip_usb_phy_bind,
+};
--
2.39.5



More information about the U-Boot mailing list