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

Signed-off-by: Johan Jonker <[email protected]>
---

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

Reply via email to