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", &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