blob: 14c3d8394df9c1bfa79b9d2bc2f133ec8763d7b1 [file] [log] [blame]
Sumit Gargaae35732022-08-04 19:57:10 +05301// 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 = (void *)dev_read_addr(dev);
188 if ((ulong)priv->base == FDT_ADDR_T_NONE)
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};