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 <common.h>
9#include <dm.h>
10#include <generic-phy.h>
11#include <reset.h>
12#include <clk.h>
13#include <asm/io.h>
14#include <linux/delay.h>
15
16/* PHY register and bit definitions */
17#define PHY_CTRL_COMMON0		0x078
18#define SIDDQ				BIT(2)
19
20struct hsphy_init_seq {
21	int offset;
22	int val;
23	int delay;
24};
25
26struct hsphy_data {
27	const struct hsphy_init_seq *init_seq;
28	unsigned int init_seq_num;
29};
30
31struct hsphy_priv {
32	void __iomem *base;
33	struct clk_bulk clks;
34	struct reset_ctl phy_rst;
35	struct reset_ctl por_rst;
36	const struct hsphy_data *data;
37};
38
39static int hsphy_power_on(struct phy *phy)
40{
41	struct hsphy_priv *priv = dev_get_priv(phy->dev);
42	u32 val;
43
44	val = readb(priv->base + PHY_CTRL_COMMON0);
45	val &= ~SIDDQ;
46	writeb(val, priv->base + PHY_CTRL_COMMON0);
47
48	return 0;
49}
50
51static int hsphy_power_off(struct phy *phy)
52{
53	struct hsphy_priv *priv = dev_get_priv(phy->dev);
54	u32 val;
55
56	val = readb(priv->base + PHY_CTRL_COMMON0);
57	val |= SIDDQ;
58	writeb(val, priv->base + PHY_CTRL_COMMON0);
59
60	return 0;
61}
62
63static int hsphy_reset(struct hsphy_priv *priv)
64{
65	int ret;
66
67	ret = reset_assert(&priv->phy_rst);
68	if (ret)
69		return ret;
70
71	udelay(10);
72
73	ret = reset_deassert(&priv->phy_rst);
74	if (ret)
75		return ret;
76
77	udelay(80);
78
79	return 0;
80}
81
82static void hsphy_init_sequence(struct hsphy_priv *priv)
83{
84	const struct hsphy_data *data = priv->data;
85	const struct hsphy_init_seq *seq;
86	int i;
87
88	/* Device match data is optional. */
89	if (!data)
90		return;
91
92	seq = data->init_seq;
93
94	for (i = 0; i < data->init_seq_num; i++, seq++) {
95		writeb(seq->val, priv->base + seq->offset);
96		if (seq->delay)
97			udelay(seq->delay);
98	}
99}
100
101static int hsphy_por_reset(struct hsphy_priv *priv)
102{
103	int ret;
104	u32 val;
105
106	ret = reset_assert(&priv->por_rst);
107	if (ret)
108		return ret;
109
110	/*
111	 * The Femto PHY is POR reset in the following scenarios.
112	 *
113	 * 1. After overriding the parameter registers.
114	 * 2. Low power mode exit from PHY retention.
115	 *
116	 * Ensure that SIDDQ is cleared before bringing the PHY
117	 * out of reset.
118	 */
119	val = readb(priv->base + PHY_CTRL_COMMON0);
120	val &= ~SIDDQ;
121	writeb(val, priv->base + PHY_CTRL_COMMON0);
122
123	/*
124	 * As per databook, 10 usec delay is required between
125	 * PHY POR assert and de-assert.
126	 */
127	udelay(10);
128	ret = reset_deassert(&priv->por_rst);
129	if (ret)
130		return ret;
131
132	/*
133	 * As per databook, it takes 75 usec for PHY to stabilize
134	 * after the reset.
135	 */
136	udelay(80);
137
138	return 0;
139}
140
141static int hsphy_clk_init(struct udevice *dev, struct hsphy_priv *priv)
142{
143	int ret;
144
145	ret = clk_get_bulk(dev, &priv->clks);
146	if (ret == -ENOSYS || ret == -ENOENT)
147		return 0;
148	if (ret)
149		return ret;
150
151	ret = clk_enable_bulk(&priv->clks);
152	if (ret) {
153		clk_release_bulk(&priv->clks);
154		return ret;
155	}
156
157	return 0;
158}
159
160static int hsphy_init(struct phy *phy)
161{
162	struct hsphy_priv *priv = dev_get_priv(phy->dev);
163	int ret;
164
165	ret = hsphy_clk_init(phy->dev, priv);
166	if (ret)
167		return ret;
168
169	ret = hsphy_reset(priv);
170	if (ret)
171		return ret;
172
173	hsphy_init_sequence(priv);
174
175	hsphy_por_reset(priv);
176	if (ret)
177		return ret;
178
179	return 0;
180}
181
182static int hsphy_probe(struct udevice *dev)
183{
184	struct hsphy_priv *priv = dev_get_priv(dev);
185	int ret;
186
187	priv->base = dev_read_addr_ptr(dev);
188	if (!priv->base)
189		return -EINVAL;
190
191	ret = reset_get_by_name(dev, "phy", &priv->phy_rst);
192	if (ret)
193		return ret;
194
195	ret = reset_get_by_name(dev, "por", &priv->por_rst);
196	if (ret)
197		return ret;
198
199	priv->data = (const struct hsphy_data *)dev_get_driver_data(dev);
200
201	return 0;
202}
203
204static struct phy_ops hsphy_ops = {
205	.power_on = hsphy_power_on,
206	.power_off = hsphy_power_off,
207	.init = hsphy_init,
208};
209
210/*
211 * The macro is used to define an initialization sequence.  Each tuple
212 * is meant to program 'value' into phy register at 'offset' with 'delay'
213 * in us followed.
214 */
215#define HSPHY_INIT_CFG(o, v, d)	{ .offset = o, .val = v, .delay = d, }
216
217static const struct hsphy_init_seq init_seq_femtophy[] = {
218	HSPHY_INIT_CFG(0xc0, 0x01, 0),
219	HSPHY_INIT_CFG(0xe8, 0x0d, 0),
220	HSPHY_INIT_CFG(0x74, 0x12, 0),
221	HSPHY_INIT_CFG(0x98, 0x63, 0),
222	HSPHY_INIT_CFG(0x9c, 0x03, 0),
223	HSPHY_INIT_CFG(0xa0, 0x1d, 0),
224	HSPHY_INIT_CFG(0xa4, 0x03, 0),
225	HSPHY_INIT_CFG(0x8c, 0x23, 0),
226	HSPHY_INIT_CFG(0x78, 0x08, 0),
227	HSPHY_INIT_CFG(0x7c, 0xdc, 0),
228	HSPHY_INIT_CFG(0x90, 0xe0, 20),
229	HSPHY_INIT_CFG(0x74, 0x10, 0),
230	HSPHY_INIT_CFG(0x90, 0x60, 0),
231};
232
233static const struct hsphy_data data_femtophy = {
234	.init_seq = init_seq_femtophy,
235	.init_seq_num = ARRAY_SIZE(init_seq_femtophy),
236};
237
238static const struct udevice_id hsphy_ids[] = {
239	{ .compatible = "qcom,usb-hs-28nm-femtophy", .data = (ulong)&data_femtophy },
240	{ }
241};
242
243U_BOOT_DRIVER(qcom_usb_hs_28nm) = {
244	.name		= "qcom-usb-hs-28nm",
245	.id		= UCLASS_PHY,
246	.of_match	= hsphy_ids,
247	.ops		= &hsphy_ops,
248	.probe		= hsphy_probe,
249	.priv_auto	= sizeof(struct hsphy_priv),
250};
251