[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