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