]>
Commit | Line | Data |
---|---|---|
67b27dbe SG |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (c) 2009-2018, Linux Foundation. All rights reserved. | |
4 | * Copyright (c) 2018-2020, Linaro Limited | |
5 | */ | |
6 | ||
7 | #include <linux/clk.h> | |
8 | #include <linux/delay.h> | |
9 | #include <linux/io.h> | |
10 | #include <linux/kernel.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/of.h> | |
13 | #include <linux/of_graph.h> | |
14 | #include <linux/phy/phy.h> | |
15 | #include <linux/platform_device.h> | |
16 | #include <linux/regulator/consumer.h> | |
17 | #include <linux/reset.h> | |
18 | #include <linux/slab.h> | |
19 | ||
20 | /* PHY register and bit definitions */ | |
21 | #define PHY_CTRL_COMMON0 0x078 | |
22 | #define SIDDQ BIT(2) | |
23 | #define PHY_IRQ_CMD 0x0d0 | |
24 | #define PHY_INTR_MASK0 0x0d4 | |
25 | #define PHY_INTR_CLEAR0 0x0dc | |
26 | #define DPDM_MASK 0x1e | |
27 | #define DP_1_0 BIT(4) | |
28 | #define DP_0_1 BIT(3) | |
29 | #define DM_1_0 BIT(2) | |
30 | #define DM_0_1 BIT(1) | |
31 | ||
32 | enum hsphy_voltage { | |
33 | VOL_NONE, | |
34 | VOL_MIN, | |
35 | VOL_MAX, | |
36 | VOL_NUM, | |
37 | }; | |
38 | ||
39 | enum hsphy_vreg { | |
40 | VDD, | |
41 | VDDA_1P8, | |
42 | VDDA_3P3, | |
43 | VREG_NUM, | |
44 | }; | |
45 | ||
46 | struct hsphy_init_seq { | |
47 | int offset; | |
48 | int val; | |
49 | int delay; | |
50 | }; | |
51 | ||
52 | struct hsphy_data { | |
53 | const struct hsphy_init_seq *init_seq; | |
54 | unsigned int init_seq_num; | |
55 | }; | |
56 | ||
57 | struct hsphy_priv { | |
58 | void __iomem *base; | |
59 | struct clk_bulk_data *clks; | |
60 | int num_clks; | |
61 | struct reset_control *phy_reset; | |
62 | struct reset_control *por_reset; | |
63 | struct regulator_bulk_data vregs[VREG_NUM]; | |
64 | const struct hsphy_data *data; | |
65 | enum phy_mode mode; | |
66 | }; | |
67 | ||
68 | static int qcom_snps_hsphy_set_mode(struct phy *phy, enum phy_mode mode, | |
69 | int submode) | |
70 | { | |
71 | struct hsphy_priv *priv = phy_get_drvdata(phy); | |
72 | ||
73 | priv->mode = PHY_MODE_INVALID; | |
74 | ||
75 | if (mode > 0) | |
76 | priv->mode = mode; | |
77 | ||
78 | return 0; | |
79 | } | |
80 | ||
81 | static void qcom_snps_hsphy_enable_hv_interrupts(struct hsphy_priv *priv) | |
82 | { | |
83 | u32 val; | |
84 | ||
85 | /* Clear any existing interrupts before enabling the interrupts */ | |
86 | val = readb(priv->base + PHY_INTR_CLEAR0); | |
87 | val |= DPDM_MASK; | |
88 | writeb(val, priv->base + PHY_INTR_CLEAR0); | |
89 | ||
90 | writeb(0x0, priv->base + PHY_IRQ_CMD); | |
91 | usleep_range(200, 220); | |
92 | writeb(0x1, priv->base + PHY_IRQ_CMD); | |
93 | ||
94 | /* Make sure the interrupts are cleared */ | |
95 | usleep_range(200, 220); | |
96 | ||
97 | val = readb(priv->base + PHY_INTR_MASK0); | |
98 | switch (priv->mode) { | |
99 | case PHY_MODE_USB_HOST_HS: | |
100 | case PHY_MODE_USB_HOST_FS: | |
101 | case PHY_MODE_USB_DEVICE_HS: | |
102 | case PHY_MODE_USB_DEVICE_FS: | |
103 | val |= DP_1_0 | DM_0_1; | |
104 | break; | |
105 | case PHY_MODE_USB_HOST_LS: | |
106 | case PHY_MODE_USB_DEVICE_LS: | |
107 | val |= DP_0_1 | DM_1_0; | |
108 | break; | |
109 | default: | |
110 | /* No device connected */ | |
111 | val |= DP_0_1 | DM_0_1; | |
112 | break; | |
113 | } | |
114 | writeb(val, priv->base + PHY_INTR_MASK0); | |
115 | } | |
116 | ||
117 | static void qcom_snps_hsphy_disable_hv_interrupts(struct hsphy_priv *priv) | |
118 | { | |
119 | u32 val; | |
120 | ||
121 | val = readb(priv->base + PHY_INTR_MASK0); | |
122 | val &= ~DPDM_MASK; | |
123 | writeb(val, priv->base + PHY_INTR_MASK0); | |
124 | ||
125 | /* Clear any pending interrupts */ | |
126 | val = readb(priv->base + PHY_INTR_CLEAR0); | |
127 | val |= DPDM_MASK; | |
128 | writeb(val, priv->base + PHY_INTR_CLEAR0); | |
129 | ||
130 | writeb(0x0, priv->base + PHY_IRQ_CMD); | |
131 | usleep_range(200, 220); | |
132 | ||
133 | writeb(0x1, priv->base + PHY_IRQ_CMD); | |
134 | usleep_range(200, 220); | |
135 | } | |
136 | ||
137 | static void qcom_snps_hsphy_enter_retention(struct hsphy_priv *priv) | |
138 | { | |
139 | u32 val; | |
140 | ||
141 | val = readb(priv->base + PHY_CTRL_COMMON0); | |
142 | val |= SIDDQ; | |
143 | writeb(val, priv->base + PHY_CTRL_COMMON0); | |
144 | } | |
145 | ||
146 | static void qcom_snps_hsphy_exit_retention(struct hsphy_priv *priv) | |
147 | { | |
148 | u32 val; | |
149 | ||
150 | val = readb(priv->base + PHY_CTRL_COMMON0); | |
151 | val &= ~SIDDQ; | |
152 | writeb(val, priv->base + PHY_CTRL_COMMON0); | |
153 | } | |
154 | ||
155 | static int qcom_snps_hsphy_power_on(struct phy *phy) | |
156 | { | |
157 | struct hsphy_priv *priv = phy_get_drvdata(phy); | |
158 | int ret; | |
159 | ||
160 | ret = regulator_bulk_enable(VREG_NUM, priv->vregs); | |
161 | if (ret) | |
162 | return ret; | |
820eeb9d | 163 | |
67b27dbe SG |
164 | qcom_snps_hsphy_disable_hv_interrupts(priv); |
165 | qcom_snps_hsphy_exit_retention(priv); | |
166 | ||
167 | return 0; | |
67b27dbe SG |
168 | } |
169 | ||
170 | static int qcom_snps_hsphy_power_off(struct phy *phy) | |
171 | { | |
172 | struct hsphy_priv *priv = phy_get_drvdata(phy); | |
173 | ||
174 | qcom_snps_hsphy_enter_retention(priv); | |
175 | qcom_snps_hsphy_enable_hv_interrupts(priv); | |
67b27dbe SG |
176 | regulator_bulk_disable(VREG_NUM, priv->vregs); |
177 | ||
178 | return 0; | |
179 | } | |
180 | ||
181 | static int qcom_snps_hsphy_reset(struct hsphy_priv *priv) | |
182 | { | |
183 | int ret; | |
184 | ||
185 | ret = reset_control_assert(priv->phy_reset); | |
186 | if (ret) | |
187 | return ret; | |
188 | ||
189 | usleep_range(10, 15); | |
190 | ||
191 | ret = reset_control_deassert(priv->phy_reset); | |
192 | if (ret) | |
193 | return ret; | |
194 | ||
195 | usleep_range(80, 100); | |
196 | ||
197 | return 0; | |
198 | } | |
199 | ||
200 | static void qcom_snps_hsphy_init_sequence(struct hsphy_priv *priv) | |
201 | { | |
202 | const struct hsphy_data *data = priv->data; | |
203 | const struct hsphy_init_seq *seq; | |
204 | int i; | |
205 | ||
206 | /* Device match data is optional. */ | |
207 | if (!data) | |
208 | return; | |
209 | ||
210 | seq = data->init_seq; | |
211 | ||
212 | for (i = 0; i < data->init_seq_num; i++, seq++) { | |
213 | writeb(seq->val, priv->base + seq->offset); | |
214 | if (seq->delay) | |
215 | usleep_range(seq->delay, seq->delay + 10); | |
216 | } | |
217 | } | |
218 | ||
219 | static int qcom_snps_hsphy_por_reset(struct hsphy_priv *priv) | |
220 | { | |
221 | int ret; | |
222 | ||
223 | ret = reset_control_assert(priv->por_reset); | |
224 | if (ret) | |
225 | return ret; | |
226 | ||
227 | /* | |
228 | * The Femto PHY is POR reset in the following scenarios. | |
229 | * | |
230 | * 1. After overriding the parameter registers. | |
231 | * 2. Low power mode exit from PHY retention. | |
232 | * | |
233 | * Ensure that SIDDQ is cleared before bringing the PHY | |
234 | * out of reset. | |
235 | */ | |
236 | qcom_snps_hsphy_exit_retention(priv); | |
237 | ||
238 | /* | |
239 | * As per databook, 10 usec delay is required between | |
240 | * PHY POR assert and de-assert. | |
241 | */ | |
242 | usleep_range(10, 20); | |
243 | ret = reset_control_deassert(priv->por_reset); | |
244 | if (ret) | |
245 | return ret; | |
246 | ||
247 | /* | |
248 | * As per databook, it takes 75 usec for PHY to stabilize | |
249 | * after the reset. | |
250 | */ | |
251 | usleep_range(80, 100); | |
252 | ||
253 | return 0; | |
254 | } | |
255 | ||
256 | static int qcom_snps_hsphy_init(struct phy *phy) | |
257 | { | |
258 | struct hsphy_priv *priv = phy_get_drvdata(phy); | |
259 | int ret; | |
260 | ||
820eeb9d | 261 | ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks); |
67b27dbe SG |
262 | if (ret) |
263 | return ret; | |
264 | ||
820eeb9d BA |
265 | ret = qcom_snps_hsphy_reset(priv); |
266 | if (ret) | |
267 | goto disable_clocks; | |
268 | ||
67b27dbe SG |
269 | qcom_snps_hsphy_init_sequence(priv); |
270 | ||
271 | ret = qcom_snps_hsphy_por_reset(priv); | |
272 | if (ret) | |
820eeb9d BA |
273 | goto disable_clocks; |
274 | ||
275 | return 0; | |
276 | ||
277 | disable_clocks: | |
278 | clk_bulk_disable_unprepare(priv->num_clks, priv->clks); | |
279 | return ret; | |
280 | } | |
281 | ||
282 | static int qcom_snps_hsphy_exit(struct phy *phy) | |
283 | { | |
284 | struct hsphy_priv *priv = phy_get_drvdata(phy); | |
285 | ||
286 | clk_bulk_disable_unprepare(priv->num_clks, priv->clks); | |
67b27dbe SG |
287 | |
288 | return 0; | |
289 | } | |
290 | ||
291 | static const struct phy_ops qcom_snps_hsphy_ops = { | |
292 | .init = qcom_snps_hsphy_init, | |
820eeb9d | 293 | .exit = qcom_snps_hsphy_exit, |
67b27dbe SG |
294 | .power_on = qcom_snps_hsphy_power_on, |
295 | .power_off = qcom_snps_hsphy_power_off, | |
296 | .set_mode = qcom_snps_hsphy_set_mode, | |
297 | .owner = THIS_MODULE, | |
298 | }; | |
299 | ||
300 | static const char * const qcom_snps_hsphy_clks[] = { | |
301 | "ref", | |
302 | "ahb", | |
303 | "sleep", | |
304 | }; | |
305 | ||
306 | static int qcom_snps_hsphy_probe(struct platform_device *pdev) | |
307 | { | |
308 | struct device *dev = &pdev->dev; | |
309 | struct phy_provider *provider; | |
310 | struct hsphy_priv *priv; | |
311 | struct phy *phy; | |
312 | int ret; | |
313 | int i; | |
314 | ||
315 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | |
316 | if (!priv) | |
317 | return -ENOMEM; | |
318 | ||
319 | priv->base = devm_platform_ioremap_resource(pdev, 0); | |
320 | if (IS_ERR(priv->base)) | |
321 | return PTR_ERR(priv->base); | |
322 | ||
323 | priv->num_clks = ARRAY_SIZE(qcom_snps_hsphy_clks); | |
324 | priv->clks = devm_kcalloc(dev, priv->num_clks, sizeof(*priv->clks), | |
325 | GFP_KERNEL); | |
326 | if (!priv->clks) | |
327 | return -ENOMEM; | |
328 | ||
329 | for (i = 0; i < priv->num_clks; i++) | |
330 | priv->clks[i].id = qcom_snps_hsphy_clks[i]; | |
331 | ||
332 | ret = devm_clk_bulk_get(dev, priv->num_clks, priv->clks); | |
333 | if (ret) | |
334 | return ret; | |
335 | ||
336 | priv->phy_reset = devm_reset_control_get_exclusive(dev, "phy"); | |
337 | if (IS_ERR(priv->phy_reset)) | |
338 | return PTR_ERR(priv->phy_reset); | |
339 | ||
340 | priv->por_reset = devm_reset_control_get_exclusive(dev, "por"); | |
341 | if (IS_ERR(priv->por_reset)) | |
342 | return PTR_ERR(priv->por_reset); | |
343 | ||
344 | priv->vregs[VDD].supply = "vdd"; | |
345 | priv->vregs[VDDA_1P8].supply = "vdda1p8"; | |
346 | priv->vregs[VDDA_3P3].supply = "vdda3p3"; | |
347 | ||
348 | ret = devm_regulator_bulk_get(dev, VREG_NUM, priv->vregs); | |
349 | if (ret) | |
350 | return ret; | |
351 | ||
352 | /* Get device match data */ | |
353 | priv->data = device_get_match_data(dev); | |
354 | ||
355 | phy = devm_phy_create(dev, dev->of_node, &qcom_snps_hsphy_ops); | |
356 | if (IS_ERR(phy)) | |
357 | return PTR_ERR(phy); | |
358 | ||
359 | phy_set_drvdata(phy, priv); | |
360 | ||
361 | provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); | |
362 | if (IS_ERR(provider)) | |
363 | return PTR_ERR(provider); | |
364 | ||
365 | ret = regulator_set_load(priv->vregs[VDDA_1P8].consumer, 19000); | |
366 | if (ret < 0) | |
367 | return ret; | |
368 | ||
369 | ret = regulator_set_load(priv->vregs[VDDA_3P3].consumer, 16000); | |
370 | if (ret < 0) | |
371 | goto unset_1p8_load; | |
372 | ||
373 | return 0; | |
374 | ||
375 | unset_1p8_load: | |
376 | regulator_set_load(priv->vregs[VDDA_1P8].consumer, 0); | |
377 | ||
378 | return ret; | |
379 | } | |
380 | ||
381 | /* | |
382 | * The macro is used to define an initialization sequence. Each tuple | |
383 | * is meant to program 'value' into phy register at 'offset' with 'delay' | |
384 | * in us followed. | |
385 | */ | |
386 | #define HSPHY_INIT_CFG(o, v, d) { .offset = o, .val = v, .delay = d, } | |
387 | ||
388 | static const struct hsphy_init_seq init_seq_femtophy[] = { | |
389 | HSPHY_INIT_CFG(0xc0, 0x01, 0), | |
390 | HSPHY_INIT_CFG(0xe8, 0x0d, 0), | |
391 | HSPHY_INIT_CFG(0x74, 0x12, 0), | |
392 | HSPHY_INIT_CFG(0x98, 0x63, 0), | |
393 | HSPHY_INIT_CFG(0x9c, 0x03, 0), | |
394 | HSPHY_INIT_CFG(0xa0, 0x1d, 0), | |
395 | HSPHY_INIT_CFG(0xa4, 0x03, 0), | |
396 | HSPHY_INIT_CFG(0x8c, 0x23, 0), | |
397 | HSPHY_INIT_CFG(0x78, 0x08, 0), | |
398 | HSPHY_INIT_CFG(0x7c, 0xdc, 0), | |
399 | HSPHY_INIT_CFG(0x90, 0xe0, 20), | |
400 | HSPHY_INIT_CFG(0x74, 0x10, 0), | |
401 | HSPHY_INIT_CFG(0x90, 0x60, 0), | |
402 | }; | |
403 | ||
404 | static const struct hsphy_data hsphy_data_femtophy = { | |
405 | .init_seq = init_seq_femtophy, | |
406 | .init_seq_num = ARRAY_SIZE(init_seq_femtophy), | |
407 | }; | |
408 | ||
409 | static const struct of_device_id qcom_snps_hsphy_match[] = { | |
410 | { .compatible = "qcom,usb-hs-28nm-femtophy", .data = &hsphy_data_femtophy, }, | |
411 | { }, | |
412 | }; | |
413 | MODULE_DEVICE_TABLE(of, qcom_snps_hsphy_match); | |
414 | ||
415 | static struct platform_driver qcom_snps_hsphy_driver = { | |
416 | .probe = qcom_snps_hsphy_probe, | |
417 | .driver = { | |
418 | .name = "qcom,usb-hs-28nm-phy", | |
419 | .of_match_table = qcom_snps_hsphy_match, | |
420 | }, | |
421 | }; | |
422 | module_platform_driver(qcom_snps_hsphy_driver); | |
423 | ||
424 | MODULE_DESCRIPTION("Qualcomm 28nm Hi-Speed USB PHY driver"); | |
425 | MODULE_LICENSE("GPL v2"); |