1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2022 Sumit Garg <sumit.garg@linaro.org>
4  *
5  * Based on Linux driver
6  */
7 
8 #include <dm.h>
9 #include <generic-phy.h>
10 #include <linux/bitops.h>
11 #include <asm/io.h>
12 #include <reset.h>
13 #include <clk.h>
14 #include <linux/delay.h>
15 
16 #define PHY_CTRL0			0x6C
17 #define PHY_CTRL1			0x70
18 #define PHY_CTRL2			0x74
19 #define PHY_CTRL4			0x7C
20 
21 /* PHY_CTRL bits */
22 #define REF_PHY_EN			BIT(0)
23 #define LANE0_PWR_ON			BIT(2)
24 #define SWI_PCS_CLK_SEL			BIT(4)
25 #define TST_PWR_DOWN			BIT(4)
26 #define PHY_RESET			BIT(7)
27 
28 struct ssphy_priv {
29 	void __iomem *base;
30 	struct clk_bulk clks;
31 	struct reset_ctl com_rst;
32 	struct reset_ctl phy_rst;
33 };
34 
ssphy_updatel(void __iomem * addr,u32 mask,u32 val)35 static inline void ssphy_updatel(void __iomem *addr, u32 mask, u32 val)
36 {
37 	writel((readl(addr) & ~mask) | val, addr);
38 }
39 
ssphy_do_reset(struct ssphy_priv * priv)40 static int ssphy_do_reset(struct ssphy_priv *priv)
41 {
42 	int ret;
43 
44 	ret = reset_assert(&priv->com_rst);
45 	if (ret)
46 		return ret;
47 
48 	ret = reset_assert(&priv->phy_rst);
49 	if (ret)
50 		return ret;
51 
52 	udelay(10);
53 
54 	ret = reset_deassert(&priv->com_rst);
55 	if (ret)
56 		return ret;
57 
58 	ret = reset_deassert(&priv->phy_rst);
59 	if (ret)
60 		return ret;
61 
62 	return 0;
63 }
64 
ssphy_power_on(struct phy * phy)65 static int ssphy_power_on(struct phy *phy)
66 {
67 	struct ssphy_priv *priv = dev_get_priv(phy->dev);
68 	int ret;
69 
70 	ret = ssphy_do_reset(priv);
71 	if (ret)
72 		return ret;
73 
74 	writeb(SWI_PCS_CLK_SEL, priv->base + PHY_CTRL0);
75 	ssphy_updatel(priv->base + PHY_CTRL4, LANE0_PWR_ON, LANE0_PWR_ON);
76 	ssphy_updatel(priv->base + PHY_CTRL2, REF_PHY_EN, REF_PHY_EN);
77 	ssphy_updatel(priv->base + PHY_CTRL4, TST_PWR_DOWN, 0);
78 
79 	return 0;
80 }
81 
ssphy_power_off(struct phy * phy)82 static int ssphy_power_off(struct phy *phy)
83 {
84 	struct ssphy_priv *priv = dev_get_priv(phy->dev);
85 
86 	ssphy_updatel(priv->base + PHY_CTRL4, LANE0_PWR_ON, 0);
87 	ssphy_updatel(priv->base + PHY_CTRL2, REF_PHY_EN, 0);
88 	ssphy_updatel(priv->base + PHY_CTRL4, TST_PWR_DOWN, TST_PWR_DOWN);
89 
90 	return 0;
91 }
92 
ssphy_clk_init(struct udevice * dev,struct ssphy_priv * priv)93 static int ssphy_clk_init(struct udevice *dev, struct ssphy_priv *priv)
94 {
95 	int ret;
96 
97 	ret = clk_get_bulk(dev, &priv->clks);
98 	if (ret == -ENOSYS || ret == -ENOENT)
99 		return 0;
100 	if (ret)
101 		return ret;
102 
103 	ret = clk_enable_bulk(&priv->clks);
104 	if (ret) {
105 		clk_release_bulk(&priv->clks);
106 		return ret;
107 	}
108 
109 	return 0;
110 }
111 
ssphy_probe(struct udevice * dev)112 static int ssphy_probe(struct udevice *dev)
113 {
114 	struct ssphy_priv *priv = dev_get_priv(dev);
115 	int ret;
116 
117 	priv->base = dev_read_addr_ptr(dev);
118 	if (!priv->base)
119 		return -EINVAL;
120 
121 	ret = ssphy_clk_init(dev, priv);
122 	if (ret)
123 		return ret;
124 
125 	ret = reset_get_by_name(dev, "com", &priv->com_rst);
126 	if (ret)
127 		return ret;
128 
129 	ret = reset_get_by_name(dev, "phy", &priv->phy_rst);
130 	if (ret)
131 		return ret;
132 
133 	return 0;
134 }
135 
136 static struct phy_ops ssphy_ops = {
137 	.power_on = ssphy_power_on,
138 	.power_off = ssphy_power_off,
139 };
140 
141 static const struct udevice_id ssphy_ids[] = {
142 	{ .compatible = "qcom,usb-ss-28nm-phy" },
143 	{ }
144 };
145 
146 U_BOOT_DRIVER(qcom_usb_ss) = {
147 	.name		= "qcom-usb-ss",
148 	.id		= UCLASS_PHY,
149 	.of_match	= ssphy_ids,
150 	.ops		= &ssphy_ops,
151 	.probe		= ssphy_probe,
152 	.priv_auto	= sizeof(struct ssphy_priv),
153 };
154