162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Rockchip usb PHY driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 Yunzhi Li <lyz@rock-chips.com> 662306a36Sopenharmony_ci * Copyright (C) 2014 ROCKCHIP, Inc. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/clk.h> 1062306a36Sopenharmony_ci#include <linux/clk-provider.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/mutex.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/of_address.h> 1762306a36Sopenharmony_ci#include <linux/of_platform.h> 1862306a36Sopenharmony_ci#include <linux/phy/phy.h> 1962306a36Sopenharmony_ci#include <linux/platform_device.h> 2062306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 2162306a36Sopenharmony_ci#include <linux/reset.h> 2262306a36Sopenharmony_ci#include <linux/regmap.h> 2362306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 2462306a36Sopenharmony_ci#include <linux/delay.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic int enable_usb_uart; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define HIWORD_UPDATE(val, mask) \ 2962306a36Sopenharmony_ci ((val) | (mask) << 16) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define UOC_CON0 0x00 3262306a36Sopenharmony_ci#define UOC_CON0_SIDDQ BIT(13) 3362306a36Sopenharmony_ci#define UOC_CON0_DISABLE BIT(4) 3462306a36Sopenharmony_ci#define UOC_CON0_COMMON_ON_N BIT(0) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define UOC_CON2 0x08 3762306a36Sopenharmony_ci#define UOC_CON2_SOFT_CON_SEL BIT(2) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define UOC_CON3 0x0c 4062306a36Sopenharmony_ci/* bits present on rk3188 and rk3288 phys */ 4162306a36Sopenharmony_ci#define UOC_CON3_UTMI_TERMSEL_FULLSPEED BIT(5) 4262306a36Sopenharmony_ci#define UOC_CON3_UTMI_XCVRSEELCT_FSTRANSC (1 << 3) 4362306a36Sopenharmony_ci#define UOC_CON3_UTMI_XCVRSEELCT_MASK (3 << 3) 4462306a36Sopenharmony_ci#define UOC_CON3_UTMI_OPMODE_NODRIVING (1 << 1) 4562306a36Sopenharmony_ci#define UOC_CON3_UTMI_OPMODE_MASK (3 << 1) 4662306a36Sopenharmony_ci#define UOC_CON3_UTMI_SUSPENDN BIT(0) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct rockchip_usb_phys { 4962306a36Sopenharmony_ci int reg; 5062306a36Sopenharmony_ci const char *pll_name; 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistruct rockchip_usb_phy_base; 5462306a36Sopenharmony_cistruct rockchip_usb_phy_pdata { 5562306a36Sopenharmony_ci struct rockchip_usb_phys *phys; 5662306a36Sopenharmony_ci int (*init_usb_uart)(struct regmap *grf, 5762306a36Sopenharmony_ci const struct rockchip_usb_phy_pdata *pdata); 5862306a36Sopenharmony_ci int usb_uart_phy; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct rockchip_usb_phy_base { 6262306a36Sopenharmony_ci struct device *dev; 6362306a36Sopenharmony_ci struct regmap *reg_base; 6462306a36Sopenharmony_ci const struct rockchip_usb_phy_pdata *pdata; 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistruct rockchip_usb_phy { 6862306a36Sopenharmony_ci struct rockchip_usb_phy_base *base; 6962306a36Sopenharmony_ci struct device_node *np; 7062306a36Sopenharmony_ci unsigned int reg_offset; 7162306a36Sopenharmony_ci struct clk *clk; 7262306a36Sopenharmony_ci struct clk *clk480m; 7362306a36Sopenharmony_ci struct clk_hw clk480m_hw; 7462306a36Sopenharmony_ci struct phy *phy; 7562306a36Sopenharmony_ci bool uart_enabled; 7662306a36Sopenharmony_ci struct reset_control *reset; 7762306a36Sopenharmony_ci struct regulator *vbus; 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic int rockchip_usb_phy_power(struct rockchip_usb_phy *phy, 8162306a36Sopenharmony_ci bool siddq) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci u32 val = HIWORD_UPDATE(siddq ? UOC_CON0_SIDDQ : 0, UOC_CON0_SIDDQ); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return regmap_write(phy->base->reg_base, phy->reg_offset, val); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw, 8962306a36Sopenharmony_ci unsigned long parent_rate) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci return 480000000; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic void rockchip_usb_phy480m_disable(struct clk_hw *hw) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci struct rockchip_usb_phy *phy = container_of(hw, 9762306a36Sopenharmony_ci struct rockchip_usb_phy, 9862306a36Sopenharmony_ci clk480m_hw); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (phy->vbus) 10162306a36Sopenharmony_ci regulator_disable(phy->vbus); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* Power down usb phy analog blocks by set siddq 1 */ 10462306a36Sopenharmony_ci rockchip_usb_phy_power(phy, 1); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int rockchip_usb_phy480m_enable(struct clk_hw *hw) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct rockchip_usb_phy *phy = container_of(hw, 11062306a36Sopenharmony_ci struct rockchip_usb_phy, 11162306a36Sopenharmony_ci clk480m_hw); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* Power up usb phy analog blocks by set siddq 0 */ 11462306a36Sopenharmony_ci return rockchip_usb_phy_power(phy, 0); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int rockchip_usb_phy480m_is_enabled(struct clk_hw *hw) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct rockchip_usb_phy *phy = container_of(hw, 12062306a36Sopenharmony_ci struct rockchip_usb_phy, 12162306a36Sopenharmony_ci clk480m_hw); 12262306a36Sopenharmony_ci int ret; 12362306a36Sopenharmony_ci u32 val; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci ret = regmap_read(phy->base->reg_base, phy->reg_offset, &val); 12662306a36Sopenharmony_ci if (ret < 0) 12762306a36Sopenharmony_ci return ret; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return (val & UOC_CON0_SIDDQ) ? 0 : 1; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic const struct clk_ops rockchip_usb_phy480m_ops = { 13362306a36Sopenharmony_ci .enable = rockchip_usb_phy480m_enable, 13462306a36Sopenharmony_ci .disable = rockchip_usb_phy480m_disable, 13562306a36Sopenharmony_ci .is_enabled = rockchip_usb_phy480m_is_enabled, 13662306a36Sopenharmony_ci .recalc_rate = rockchip_usb_phy480m_recalc_rate, 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic int rockchip_usb_phy_power_off(struct phy *_phy) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (phy->uart_enabled) 14462306a36Sopenharmony_ci return -EBUSY; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci clk_disable_unprepare(phy->clk480m); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic int rockchip_usb_phy_power_on(struct phy *_phy) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (phy->uart_enabled) 15662306a36Sopenharmony_ci return -EBUSY; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (phy->vbus) { 15962306a36Sopenharmony_ci int ret; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci ret = regulator_enable(phy->vbus); 16262306a36Sopenharmony_ci if (ret) 16362306a36Sopenharmony_ci return ret; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return clk_prepare_enable(phy->clk480m); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int rockchip_usb_phy_reset(struct phy *_phy) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (phy->reset) { 17462306a36Sopenharmony_ci reset_control_assert(phy->reset); 17562306a36Sopenharmony_ci udelay(10); 17662306a36Sopenharmony_ci reset_control_deassert(phy->reset); 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return 0; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic const struct phy_ops ops = { 18362306a36Sopenharmony_ci .power_on = rockchip_usb_phy_power_on, 18462306a36Sopenharmony_ci .power_off = rockchip_usb_phy_power_off, 18562306a36Sopenharmony_ci .reset = rockchip_usb_phy_reset, 18662306a36Sopenharmony_ci .owner = THIS_MODULE, 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic void rockchip_usb_phy_action(void *data) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct rockchip_usb_phy *rk_phy = data; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (!rk_phy->uart_enabled) { 19462306a36Sopenharmony_ci of_clk_del_provider(rk_phy->np); 19562306a36Sopenharmony_ci clk_unregister(rk_phy->clk480m); 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (rk_phy->clk) 19962306a36Sopenharmony_ci clk_put(rk_phy->clk); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base, 20362306a36Sopenharmony_ci struct device_node *child) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct rockchip_usb_phy *rk_phy; 20662306a36Sopenharmony_ci unsigned int reg_offset; 20762306a36Sopenharmony_ci const char *clk_name; 20862306a36Sopenharmony_ci struct clk_init_data init; 20962306a36Sopenharmony_ci int err, i; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci rk_phy = devm_kzalloc(base->dev, sizeof(*rk_phy), GFP_KERNEL); 21262306a36Sopenharmony_ci if (!rk_phy) 21362306a36Sopenharmony_ci return -ENOMEM; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci rk_phy->base = base; 21662306a36Sopenharmony_ci rk_phy->np = child; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (of_property_read_u32(child, "reg", ®_offset)) { 21962306a36Sopenharmony_ci dev_err(base->dev, "missing reg property in node %pOFn\n", 22062306a36Sopenharmony_ci child); 22162306a36Sopenharmony_ci return -EINVAL; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci rk_phy->reset = of_reset_control_get(child, "phy-reset"); 22562306a36Sopenharmony_ci if (IS_ERR(rk_phy->reset)) 22662306a36Sopenharmony_ci rk_phy->reset = NULL; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci rk_phy->reg_offset = reg_offset; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci rk_phy->clk = of_clk_get_by_name(child, "phyclk"); 23162306a36Sopenharmony_ci if (IS_ERR(rk_phy->clk)) 23262306a36Sopenharmony_ci rk_phy->clk = NULL; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci i = 0; 23562306a36Sopenharmony_ci init.name = NULL; 23662306a36Sopenharmony_ci while (base->pdata->phys[i].reg) { 23762306a36Sopenharmony_ci if (base->pdata->phys[i].reg == reg_offset) { 23862306a36Sopenharmony_ci init.name = base->pdata->phys[i].pll_name; 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci i++; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (!init.name) { 24562306a36Sopenharmony_ci dev_err(base->dev, "phy data not found\n"); 24662306a36Sopenharmony_ci return -EINVAL; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (enable_usb_uart && base->pdata->usb_uart_phy == i) { 25062306a36Sopenharmony_ci dev_dbg(base->dev, "phy%d used as uart output\n", i); 25162306a36Sopenharmony_ci rk_phy->uart_enabled = true; 25262306a36Sopenharmony_ci } else { 25362306a36Sopenharmony_ci if (rk_phy->clk) { 25462306a36Sopenharmony_ci clk_name = __clk_get_name(rk_phy->clk); 25562306a36Sopenharmony_ci init.flags = 0; 25662306a36Sopenharmony_ci init.parent_names = &clk_name; 25762306a36Sopenharmony_ci init.num_parents = 1; 25862306a36Sopenharmony_ci } else { 25962306a36Sopenharmony_ci init.flags = 0; 26062306a36Sopenharmony_ci init.parent_names = NULL; 26162306a36Sopenharmony_ci init.num_parents = 0; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci init.ops = &rockchip_usb_phy480m_ops; 26562306a36Sopenharmony_ci rk_phy->clk480m_hw.init = &init; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw); 26862306a36Sopenharmony_ci if (IS_ERR(rk_phy->clk480m)) { 26962306a36Sopenharmony_ci err = PTR_ERR(rk_phy->clk480m); 27062306a36Sopenharmony_ci goto err_clk; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci err = of_clk_add_provider(child, of_clk_src_simple_get, 27462306a36Sopenharmony_ci rk_phy->clk480m); 27562306a36Sopenharmony_ci if (err < 0) 27662306a36Sopenharmony_ci goto err_clk_prov; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci err = devm_add_action_or_reset(base->dev, rockchip_usb_phy_action, 28062306a36Sopenharmony_ci rk_phy); 28162306a36Sopenharmony_ci if (err) 28262306a36Sopenharmony_ci return err; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci rk_phy->phy = devm_phy_create(base->dev, child, &ops); 28562306a36Sopenharmony_ci if (IS_ERR(rk_phy->phy)) { 28662306a36Sopenharmony_ci dev_err(base->dev, "failed to create PHY\n"); 28762306a36Sopenharmony_ci return PTR_ERR(rk_phy->phy); 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci phy_set_drvdata(rk_phy->phy, rk_phy); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci rk_phy->vbus = devm_regulator_get_optional(&rk_phy->phy->dev, "vbus"); 29262306a36Sopenharmony_ci if (IS_ERR(rk_phy->vbus)) { 29362306a36Sopenharmony_ci if (PTR_ERR(rk_phy->vbus) == -EPROBE_DEFER) 29462306a36Sopenharmony_ci return PTR_ERR(rk_phy->vbus); 29562306a36Sopenharmony_ci rk_phy->vbus = NULL; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* 29962306a36Sopenharmony_ci * When acting as uart-pipe, just keep clock on otherwise 30062306a36Sopenharmony_ci * only power up usb phy when it use, so disable it when init 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_ci if (rk_phy->uart_enabled) 30362306a36Sopenharmony_ci return clk_prepare_enable(rk_phy->clk); 30462306a36Sopenharmony_ci else 30562306a36Sopenharmony_ci return rockchip_usb_phy_power(rk_phy, 1); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cierr_clk_prov: 30862306a36Sopenharmony_ci if (!rk_phy->uart_enabled) 30962306a36Sopenharmony_ci clk_unregister(rk_phy->clk480m); 31062306a36Sopenharmony_cierr_clk: 31162306a36Sopenharmony_ci if (rk_phy->clk) 31262306a36Sopenharmony_ci clk_put(rk_phy->clk); 31362306a36Sopenharmony_ci return err; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic const struct rockchip_usb_phy_pdata rk3066a_pdata = { 31762306a36Sopenharmony_ci .phys = (struct rockchip_usb_phys[]){ 31862306a36Sopenharmony_ci { .reg = 0x17c, .pll_name = "sclk_otgphy0_480m" }, 31962306a36Sopenharmony_ci { .reg = 0x188, .pll_name = "sclk_otgphy1_480m" }, 32062306a36Sopenharmony_ci { /* sentinel */ } 32162306a36Sopenharmony_ci }, 32262306a36Sopenharmony_ci}; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic int __init rockchip_init_usb_uart_common(struct regmap *grf, 32562306a36Sopenharmony_ci const struct rockchip_usb_phy_pdata *pdata) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci int regoffs = pdata->phys[pdata->usb_uart_phy].reg; 32862306a36Sopenharmony_ci int ret; 32962306a36Sopenharmony_ci u32 val; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* 33262306a36Sopenharmony_ci * COMMON_ON and DISABLE settings are described in the TRM, 33362306a36Sopenharmony_ci * but were not present in the original code. 33462306a36Sopenharmony_ci * Also disable the analog phy components to save power. 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_ci val = HIWORD_UPDATE(UOC_CON0_COMMON_ON_N 33762306a36Sopenharmony_ci | UOC_CON0_DISABLE 33862306a36Sopenharmony_ci | UOC_CON0_SIDDQ, 33962306a36Sopenharmony_ci UOC_CON0_COMMON_ON_N 34062306a36Sopenharmony_ci | UOC_CON0_DISABLE 34162306a36Sopenharmony_ci | UOC_CON0_SIDDQ); 34262306a36Sopenharmony_ci ret = regmap_write(grf, regoffs + UOC_CON0, val); 34362306a36Sopenharmony_ci if (ret) 34462306a36Sopenharmony_ci return ret; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci val = HIWORD_UPDATE(UOC_CON2_SOFT_CON_SEL, 34762306a36Sopenharmony_ci UOC_CON2_SOFT_CON_SEL); 34862306a36Sopenharmony_ci ret = regmap_write(grf, regoffs + UOC_CON2, val); 34962306a36Sopenharmony_ci if (ret) 35062306a36Sopenharmony_ci return ret; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci val = HIWORD_UPDATE(UOC_CON3_UTMI_OPMODE_NODRIVING 35362306a36Sopenharmony_ci | UOC_CON3_UTMI_XCVRSEELCT_FSTRANSC 35462306a36Sopenharmony_ci | UOC_CON3_UTMI_TERMSEL_FULLSPEED, 35562306a36Sopenharmony_ci UOC_CON3_UTMI_SUSPENDN 35662306a36Sopenharmony_ci | UOC_CON3_UTMI_OPMODE_MASK 35762306a36Sopenharmony_ci | UOC_CON3_UTMI_XCVRSEELCT_MASK 35862306a36Sopenharmony_ci | UOC_CON3_UTMI_TERMSEL_FULLSPEED); 35962306a36Sopenharmony_ci ret = regmap_write(grf, UOC_CON3, val); 36062306a36Sopenharmony_ci if (ret) 36162306a36Sopenharmony_ci return ret; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci return 0; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci#define RK3188_UOC0_CON0 0x10c 36762306a36Sopenharmony_ci#define RK3188_UOC0_CON0_BYPASSSEL BIT(9) 36862306a36Sopenharmony_ci#define RK3188_UOC0_CON0_BYPASSDMEN BIT(8) 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci/* 37162306a36Sopenharmony_ci * Enable the bypass of uart2 data through the otg usb phy. 37262306a36Sopenharmony_ci * See description of rk3288-variant for details. 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_cistatic int __init rk3188_init_usb_uart(struct regmap *grf, 37562306a36Sopenharmony_ci const struct rockchip_usb_phy_pdata *pdata) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci u32 val; 37862306a36Sopenharmony_ci int ret; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci ret = rockchip_init_usb_uart_common(grf, pdata); 38162306a36Sopenharmony_ci if (ret) 38262306a36Sopenharmony_ci return ret; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci val = HIWORD_UPDATE(RK3188_UOC0_CON0_BYPASSSEL 38562306a36Sopenharmony_ci | RK3188_UOC0_CON0_BYPASSDMEN, 38662306a36Sopenharmony_ci RK3188_UOC0_CON0_BYPASSSEL 38762306a36Sopenharmony_ci | RK3188_UOC0_CON0_BYPASSDMEN); 38862306a36Sopenharmony_ci ret = regmap_write(grf, RK3188_UOC0_CON0, val); 38962306a36Sopenharmony_ci if (ret) 39062306a36Sopenharmony_ci return ret; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci return 0; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic const struct rockchip_usb_phy_pdata rk3188_pdata = { 39662306a36Sopenharmony_ci .phys = (struct rockchip_usb_phys[]){ 39762306a36Sopenharmony_ci { .reg = 0x10c, .pll_name = "sclk_otgphy0_480m" }, 39862306a36Sopenharmony_ci { .reg = 0x11c, .pll_name = "sclk_otgphy1_480m" }, 39962306a36Sopenharmony_ci { /* sentinel */ } 40062306a36Sopenharmony_ci }, 40162306a36Sopenharmony_ci .init_usb_uart = rk3188_init_usb_uart, 40262306a36Sopenharmony_ci .usb_uart_phy = 0, 40362306a36Sopenharmony_ci}; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci#define RK3288_UOC0_CON3 0x32c 40662306a36Sopenharmony_ci#define RK3288_UOC0_CON3_BYPASSDMEN BIT(6) 40762306a36Sopenharmony_ci#define RK3288_UOC0_CON3_BYPASSSEL BIT(7) 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci/* 41062306a36Sopenharmony_ci * Enable the bypass of uart2 data through the otg usb phy. 41162306a36Sopenharmony_ci * Original description in the TRM. 41262306a36Sopenharmony_ci * 1. Disable the OTG block by setting OTGDISABLE0 to 1’b1. 41362306a36Sopenharmony_ci * 2. Disable the pull-up resistance on the D+ line by setting 41462306a36Sopenharmony_ci * OPMODE0[1:0] to 2’b01. 41562306a36Sopenharmony_ci * 3. To ensure that the XO, Bias, and PLL blocks are powered down in Suspend 41662306a36Sopenharmony_ci * mode, set COMMONONN to 1’b1. 41762306a36Sopenharmony_ci * 4. Place the USB PHY in Suspend mode by setting SUSPENDM0 to 1’b0. 41862306a36Sopenharmony_ci * 5. Set BYPASSSEL0 to 1’b1. 41962306a36Sopenharmony_ci * 6. To transmit data, controls BYPASSDMEN0, and BYPASSDMDATA0. 42062306a36Sopenharmony_ci * To receive data, monitor FSVPLUS0. 42162306a36Sopenharmony_ci * 42262306a36Sopenharmony_ci * The actual code in the vendor kernel does some things differently. 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_cistatic int __init rk3288_init_usb_uart(struct regmap *grf, 42562306a36Sopenharmony_ci const struct rockchip_usb_phy_pdata *pdata) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci u32 val; 42862306a36Sopenharmony_ci int ret; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci ret = rockchip_init_usb_uart_common(grf, pdata); 43162306a36Sopenharmony_ci if (ret) 43262306a36Sopenharmony_ci return ret; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci val = HIWORD_UPDATE(RK3288_UOC0_CON3_BYPASSSEL 43562306a36Sopenharmony_ci | RK3288_UOC0_CON3_BYPASSDMEN, 43662306a36Sopenharmony_ci RK3288_UOC0_CON3_BYPASSSEL 43762306a36Sopenharmony_ci | RK3288_UOC0_CON3_BYPASSDMEN); 43862306a36Sopenharmony_ci ret = regmap_write(grf, RK3288_UOC0_CON3, val); 43962306a36Sopenharmony_ci if (ret) 44062306a36Sopenharmony_ci return ret; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return 0; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic const struct rockchip_usb_phy_pdata rk3288_pdata = { 44662306a36Sopenharmony_ci .phys = (struct rockchip_usb_phys[]){ 44762306a36Sopenharmony_ci { .reg = 0x320, .pll_name = "sclk_otgphy0_480m" }, 44862306a36Sopenharmony_ci { .reg = 0x334, .pll_name = "sclk_otgphy1_480m" }, 44962306a36Sopenharmony_ci { .reg = 0x348, .pll_name = "sclk_otgphy2_480m" }, 45062306a36Sopenharmony_ci { /* sentinel */ } 45162306a36Sopenharmony_ci }, 45262306a36Sopenharmony_ci .init_usb_uart = rk3288_init_usb_uart, 45362306a36Sopenharmony_ci .usb_uart_phy = 0, 45462306a36Sopenharmony_ci}; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic int rockchip_usb_phy_probe(struct platform_device *pdev) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 45962306a36Sopenharmony_ci struct rockchip_usb_phy_base *phy_base; 46062306a36Sopenharmony_ci struct phy_provider *phy_provider; 46162306a36Sopenharmony_ci const struct of_device_id *match; 46262306a36Sopenharmony_ci struct device_node *child; 46362306a36Sopenharmony_ci int err; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci phy_base = devm_kzalloc(dev, sizeof(*phy_base), GFP_KERNEL); 46662306a36Sopenharmony_ci if (!phy_base) 46762306a36Sopenharmony_ci return -ENOMEM; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci match = of_match_device(dev->driver->of_match_table, dev); 47062306a36Sopenharmony_ci if (!match || !match->data) { 47162306a36Sopenharmony_ci dev_err(dev, "missing phy data\n"); 47262306a36Sopenharmony_ci return -EINVAL; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci phy_base->pdata = match->data; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci phy_base->dev = dev; 47862306a36Sopenharmony_ci phy_base->reg_base = ERR_PTR(-ENODEV); 47962306a36Sopenharmony_ci if (dev->parent && dev->parent->of_node) 48062306a36Sopenharmony_ci phy_base->reg_base = syscon_node_to_regmap( 48162306a36Sopenharmony_ci dev->parent->of_node); 48262306a36Sopenharmony_ci if (IS_ERR(phy_base->reg_base)) 48362306a36Sopenharmony_ci phy_base->reg_base = syscon_regmap_lookup_by_phandle( 48462306a36Sopenharmony_ci dev->of_node, "rockchip,grf"); 48562306a36Sopenharmony_ci if (IS_ERR(phy_base->reg_base)) { 48662306a36Sopenharmony_ci dev_err(&pdev->dev, "Missing rockchip,grf property\n"); 48762306a36Sopenharmony_ci return PTR_ERR(phy_base->reg_base); 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci for_each_available_child_of_node(dev->of_node, child) { 49162306a36Sopenharmony_ci err = rockchip_usb_phy_init(phy_base, child); 49262306a36Sopenharmony_ci if (err) { 49362306a36Sopenharmony_ci of_node_put(child); 49462306a36Sopenharmony_ci return err; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 49962306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(phy_provider); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic const struct of_device_id rockchip_usb_phy_dt_ids[] = { 50362306a36Sopenharmony_ci { .compatible = "rockchip,rk3066a-usb-phy", .data = &rk3066a_pdata }, 50462306a36Sopenharmony_ci { .compatible = "rockchip,rk3188-usb-phy", .data = &rk3188_pdata }, 50562306a36Sopenharmony_ci { .compatible = "rockchip,rk3288-usb-phy", .data = &rk3288_pdata }, 50662306a36Sopenharmony_ci {} 50762306a36Sopenharmony_ci}; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, rockchip_usb_phy_dt_ids); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic struct platform_driver rockchip_usb_driver = { 51262306a36Sopenharmony_ci .probe = rockchip_usb_phy_probe, 51362306a36Sopenharmony_ci .driver = { 51462306a36Sopenharmony_ci .name = "rockchip-usb-phy", 51562306a36Sopenharmony_ci .of_match_table = rockchip_usb_phy_dt_ids, 51662306a36Sopenharmony_ci }, 51762306a36Sopenharmony_ci}; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cimodule_platform_driver(rockchip_usb_driver); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci#ifndef MODULE 52262306a36Sopenharmony_cistatic int __init rockchip_init_usb_uart(void) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci const struct of_device_id *match; 52562306a36Sopenharmony_ci const struct rockchip_usb_phy_pdata *data; 52662306a36Sopenharmony_ci struct device_node *np; 52762306a36Sopenharmony_ci struct regmap *grf; 52862306a36Sopenharmony_ci int ret; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (!enable_usb_uart) 53162306a36Sopenharmony_ci return 0; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci np = of_find_matching_node_and_match(NULL, rockchip_usb_phy_dt_ids, 53462306a36Sopenharmony_ci &match); 53562306a36Sopenharmony_ci if (!np) { 53662306a36Sopenharmony_ci pr_err("%s: failed to find usbphy node\n", __func__); 53762306a36Sopenharmony_ci return -ENOTSUPP; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci pr_debug("%s: using settings for %s\n", __func__, match->compatible); 54162306a36Sopenharmony_ci data = match->data; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (!data->init_usb_uart) { 54462306a36Sopenharmony_ci pr_err("%s: usb-uart not available on %s\n", 54562306a36Sopenharmony_ci __func__, match->compatible); 54662306a36Sopenharmony_ci return -ENOTSUPP; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci grf = ERR_PTR(-ENODEV); 55062306a36Sopenharmony_ci if (np->parent) 55162306a36Sopenharmony_ci grf = syscon_node_to_regmap(np->parent); 55262306a36Sopenharmony_ci if (IS_ERR(grf)) 55362306a36Sopenharmony_ci grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); 55462306a36Sopenharmony_ci if (IS_ERR(grf)) { 55562306a36Sopenharmony_ci pr_err("%s: Missing rockchip,grf property, %lu\n", 55662306a36Sopenharmony_ci __func__, PTR_ERR(grf)); 55762306a36Sopenharmony_ci return PTR_ERR(grf); 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci ret = data->init_usb_uart(grf, data); 56162306a36Sopenharmony_ci if (ret) { 56262306a36Sopenharmony_ci pr_err("%s: could not init usb_uart, %d\n", __func__, ret); 56362306a36Sopenharmony_ci enable_usb_uart = 0; 56462306a36Sopenharmony_ci return ret; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci return 0; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ciearly_initcall(rockchip_init_usb_uart); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic int __init rockchip_usb_uart(char *buf) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci enable_usb_uart = true; 57462306a36Sopenharmony_ci return 0; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ciearly_param("rockchip.usb_uart", rockchip_usb_uart); 57762306a36Sopenharmony_ci#endif 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ciMODULE_AUTHOR("Yunzhi Li <lyz@rock-chips.com>"); 58062306a36Sopenharmony_ciMODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver"); 58162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 582