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

