]>
Commit | Line | Data |
---|---|---|
97fb5e8d | 1 | // SPDX-License-Identifier: GPL-2.0-only |
adaafaa3 YG |
2 | /* |
3 | * Copyright (c) 2013-2015, Linux Foundation. All rights reserved. | |
adaafaa3 YG |
4 | */ |
5 | ||
6 | #include "phy-qcom-ufs-i.h" | |
7 | ||
8 | #define MAX_PROP_NAME 32 | |
9 | #define VDDA_PHY_MIN_UV 1000000 | |
10 | #define VDDA_PHY_MAX_UV 1000000 | |
11 | #define VDDA_PLL_MIN_UV 1800000 | |
12 | #define VDDA_PLL_MAX_UV 1800000 | |
13 | #define VDDP_REF_CLK_MIN_UV 1200000 | |
14 | #define VDDP_REF_CLK_MAX_UV 1200000 | |
15 | ||
adaafaa3 YG |
16 | int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy, |
17 | struct ufs_qcom_phy_calibration *tbl_A, | |
18 | int tbl_size_A, | |
19 | struct ufs_qcom_phy_calibration *tbl_B, | |
20 | int tbl_size_B, bool is_rate_B) | |
21 | { | |
22 | int i; | |
23 | int ret = 0; | |
24 | ||
25 | if (!tbl_A) { | |
26 | dev_err(ufs_qcom_phy->dev, "%s: tbl_A is NULL", __func__); | |
27 | ret = EINVAL; | |
28 | goto out; | |
29 | } | |
30 | ||
31 | for (i = 0; i < tbl_size_A; i++) | |
32 | writel_relaxed(tbl_A[i].cfg_value, | |
33 | ufs_qcom_phy->mmio + tbl_A[i].reg_offset); | |
34 | ||
35 | /* | |
36 | * In case we would like to work in rate B, we need | |
37 | * to override a registers that were configured in rate A table | |
38 | * with registers of rate B table. | |
39 | * table. | |
40 | */ | |
41 | if (is_rate_B) { | |
42 | if (!tbl_B) { | |
43 | dev_err(ufs_qcom_phy->dev, "%s: tbl_B is NULL", | |
44 | __func__); | |
45 | ret = EINVAL; | |
46 | goto out; | |
47 | } | |
48 | ||
49 | for (i = 0; i < tbl_size_B; i++) | |
50 | writel_relaxed(tbl_B[i].cfg_value, | |
51 | ufs_qcom_phy->mmio + tbl_B[i].reg_offset); | |
52 | } | |
53 | ||
54 | /* flush buffered writes */ | |
55 | mb(); | |
56 | ||
57 | out: | |
58 | return ret; | |
59 | } | |
358d6c87 | 60 | EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate); |
adaafaa3 | 61 | |
adaafaa3 YG |
62 | /* |
63 | * This assumes the embedded phy structure inside generic_phy is of type | |
64 | * struct ufs_qcom_phy. In order to function properly it's crucial | |
65 | * to keep the embedded struct "struct ufs_qcom_phy common_cfg" | |
66 | * as the first inside generic_phy. | |
67 | */ | |
68 | struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy) | |
69 | { | |
70 | return (struct ufs_qcom_phy *)phy_get_drvdata(generic_phy); | |
71 | } | |
358d6c87 | 72 | EXPORT_SYMBOL_GPL(get_ufs_qcom_phy); |
adaafaa3 YG |
73 | |
74 | static | |
75 | int ufs_qcom_phy_base_init(struct platform_device *pdev, | |
76 | struct ufs_qcom_phy *phy_common) | |
77 | { | |
78 | struct device *dev = &pdev->dev; | |
79 | struct resource *res; | |
80 | int err = 0; | |
81 | ||
82 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem"); | |
adaafaa3 YG |
83 | phy_common->mmio = devm_ioremap_resource(dev, res); |
84 | if (IS_ERR((void const *)phy_common->mmio)) { | |
85 | err = PTR_ERR((void const *)phy_common->mmio); | |
86 | phy_common->mmio = NULL; | |
87 | dev_err(dev, "%s: ioremap for phy_mem resource failed %d\n", | |
88 | __func__, err); | |
52ea796b | 89 | return err; |
adaafaa3 YG |
90 | } |
91 | ||
92 | /* "dev_ref_clk_ctrl_mem" is optional resource */ | |
93 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
94 | "dev_ref_clk_ctrl_mem"); | |
adaafaa3 | 95 | phy_common->dev_ref_clk_ctrl_mmio = devm_ioremap_resource(dev, res); |
52ea796b | 96 | if (IS_ERR((void const *)phy_common->dev_ref_clk_ctrl_mmio)) |
adaafaa3 | 97 | phy_common->dev_ref_clk_ctrl_mmio = NULL; |
adaafaa3 | 98 | |
52ea796b | 99 | return 0; |
adaafaa3 YG |
100 | } |
101 | ||
15887cb8 VG |
102 | struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev, |
103 | struct ufs_qcom_phy *common_cfg, | |
104 | const struct phy_ops *ufs_qcom_phy_gen_ops, | |
105 | struct ufs_qcom_phy_specific_ops *phy_spec_ops) | |
106 | { | |
107 | int err; | |
108 | struct device *dev = &pdev->dev; | |
109 | struct phy *generic_phy = NULL; | |
110 | struct phy_provider *phy_provider; | |
111 | ||
112 | err = ufs_qcom_phy_base_init(pdev, common_cfg); | |
113 | if (err) { | |
114 | dev_err(dev, "%s: phy base init failed %d\n", __func__, err); | |
115 | goto out; | |
116 | } | |
117 | ||
118 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); | |
119 | if (IS_ERR(phy_provider)) { | |
120 | err = PTR_ERR(phy_provider); | |
121 | dev_err(dev, "%s: failed to register phy %d\n", __func__, err); | |
122 | goto out; | |
123 | } | |
124 | ||
125 | generic_phy = devm_phy_create(dev, NULL, ufs_qcom_phy_gen_ops); | |
126 | if (IS_ERR(generic_phy)) { | |
127 | err = PTR_ERR(generic_phy); | |
128 | dev_err(dev, "%s: failed to create phy %d\n", __func__, err); | |
129 | generic_phy = NULL; | |
130 | goto out; | |
131 | } | |
132 | ||
133 | common_cfg->phy_spec_ops = phy_spec_ops; | |
134 | common_cfg->dev = dev; | |
135 | ||
136 | out: | |
137 | return generic_phy; | |
138 | } | |
139 | EXPORT_SYMBOL_GPL(ufs_qcom_phy_generic_probe); | |
140 | ||
3f6d1767 | 141 | static int ufs_qcom_phy_get_reset(struct ufs_qcom_phy *phy_common) |
c9b58979 EG |
142 | { |
143 | struct reset_control *reset; | |
144 | ||
145 | if (phy_common->ufs_reset) | |
146 | return 0; | |
147 | ||
148 | reset = devm_reset_control_get_exclusive_by_index(phy_common->dev, 0); | |
149 | if (IS_ERR(reset)) | |
150 | return PTR_ERR(reset); | |
151 | ||
152 | phy_common->ufs_reset = reset; | |
153 | return 0; | |
154 | } | |
c9b58979 | 155 | |
89bd296b | 156 | static int __ufs_qcom_phy_clk_get(struct device *dev, |
adaafaa3 YG |
157 | const char *name, struct clk **clk_out, bool err_print) |
158 | { | |
159 | struct clk *clk; | |
160 | int err = 0; | |
adaafaa3 YG |
161 | |
162 | clk = devm_clk_get(dev, name); | |
163 | if (IS_ERR(clk)) { | |
164 | err = PTR_ERR(clk); | |
165 | if (err_print) | |
166 | dev_err(dev, "failed to get %s err %d", name, err); | |
167 | } else { | |
168 | *clk_out = clk; | |
169 | } | |
170 | ||
171 | return err; | |
172 | } | |
173 | ||
89bd296b | 174 | static int ufs_qcom_phy_clk_get(struct device *dev, |
adaafaa3 YG |
175 | const char *name, struct clk **clk_out) |
176 | { | |
89bd296b | 177 | return __ufs_qcom_phy_clk_get(dev, name, clk_out, true); |
adaafaa3 YG |
178 | } |
179 | ||
89bd296b | 180 | int ufs_qcom_phy_init_clks(struct ufs_qcom_phy *phy_common) |
adaafaa3 YG |
181 | { |
182 | int err; | |
183 | ||
300f9677 VG |
184 | if (of_device_is_compatible(phy_common->dev->of_node, |
185 | "qcom,msm8996-ufs-phy-qmp-14nm")) | |
186 | goto skip_txrx_clk; | |
187 | ||
89bd296b | 188 | err = ufs_qcom_phy_clk_get(phy_common->dev, "tx_iface_clk", |
adaafaa3 YG |
189 | &phy_common->tx_iface_clk); |
190 | if (err) | |
191 | goto out; | |
192 | ||
89bd296b | 193 | err = ufs_qcom_phy_clk_get(phy_common->dev, "rx_iface_clk", |
adaafaa3 YG |
194 | &phy_common->rx_iface_clk); |
195 | if (err) | |
196 | goto out; | |
197 | ||
0b10f64d | 198 | skip_txrx_clk: |
89bd296b | 199 | err = ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk_src", |
adaafaa3 YG |
200 | &phy_common->ref_clk_src); |
201 | if (err) | |
202 | goto out; | |
203 | ||
204 | /* | |
205 | * "ref_clk_parent" is optional hence don't abort init if it's not | |
206 | * found. | |
207 | */ | |
89bd296b | 208 | __ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk_parent", |
adaafaa3 YG |
209 | &phy_common->ref_clk_parent, false); |
210 | ||
89bd296b | 211 | err = ufs_qcom_phy_clk_get(phy_common->dev, "ref_clk", |
adaafaa3 YG |
212 | &phy_common->ref_clk); |
213 | ||
214 | out: | |
215 | return err; | |
216 | } | |
358d6c87 | 217 | EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_clks); |
adaafaa3 | 218 | |
3471426f BA |
219 | static int ufs_qcom_phy_init_vreg(struct device *dev, |
220 | struct ufs_qcom_phy_vreg *vreg, | |
221 | const char *name) | |
adaafaa3 YG |
222 | { |
223 | int err = 0; | |
adaafaa3 YG |
224 | |
225 | char prop_name[MAX_PROP_NAME]; | |
226 | ||
e7d5e412 | 227 | vreg->name = name; |
adaafaa3 YG |
228 | vreg->reg = devm_regulator_get(dev, name); |
229 | if (IS_ERR(vreg->reg)) { | |
230 | err = PTR_ERR(vreg->reg); | |
3471426f | 231 | dev_err(dev, "failed to get %s, %d\n", name, err); |
adaafaa3 YG |
232 | goto out; |
233 | } | |
234 | ||
235 | if (dev->of_node) { | |
236 | snprintf(prop_name, MAX_PROP_NAME, "%s-max-microamp", name); | |
237 | err = of_property_read_u32(dev->of_node, | |
238 | prop_name, &vreg->max_uA); | |
239 | if (err && err != -EINVAL) { | |
240 | dev_err(dev, "%s: failed to read %s\n", | |
241 | __func__, prop_name); | |
242 | goto out; | |
243 | } else if (err == -EINVAL || !vreg->max_uA) { | |
244 | if (regulator_count_voltages(vreg->reg) > 0) { | |
245 | dev_err(dev, "%s: %s is mandatory\n", | |
246 | __func__, prop_name); | |
247 | goto out; | |
248 | } | |
249 | err = 0; | |
250 | } | |
adaafaa3 YG |
251 | } |
252 | ||
253 | if (!strcmp(name, "vdda-pll")) { | |
254 | vreg->max_uV = VDDA_PLL_MAX_UV; | |
255 | vreg->min_uV = VDDA_PLL_MIN_UV; | |
256 | } else if (!strcmp(name, "vdda-phy")) { | |
257 | vreg->max_uV = VDDA_PHY_MAX_UV; | |
258 | vreg->min_uV = VDDA_PHY_MIN_UV; | |
259 | } else if (!strcmp(name, "vddp-ref-clk")) { | |
260 | vreg->max_uV = VDDP_REF_CLK_MAX_UV; | |
261 | vreg->min_uV = VDDP_REF_CLK_MIN_UV; | |
262 | } | |
263 | ||
264 | out: | |
adaafaa3 YG |
265 | return err; |
266 | } | |
267 | ||
15887cb8 VG |
268 | int ufs_qcom_phy_init_vregulators(struct ufs_qcom_phy *phy_common) |
269 | { | |
270 | int err; | |
271 | ||
272 | err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vdda_pll, | |
273 | "vdda-pll"); | |
274 | if (err) | |
275 | goto out; | |
276 | ||
277 | err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vdda_phy, | |
278 | "vdda-phy"); | |
279 | ||
280 | if (err) | |
281 | goto out; | |
282 | ||
3471426f BA |
283 | err = ufs_qcom_phy_init_vreg(phy_common->dev, &phy_common->vddp_ref_clk, |
284 | "vddp-ref-clk"); | |
285 | ||
15887cb8 VG |
286 | out: |
287 | return err; | |
288 | } | |
289 | EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_vregulators); | |
290 | ||
89bd296b | 291 | static int ufs_qcom_phy_cfg_vreg(struct device *dev, |
adaafaa3 YG |
292 | struct ufs_qcom_phy_vreg *vreg, bool on) |
293 | { | |
294 | int ret = 0; | |
295 | struct regulator *reg = vreg->reg; | |
296 | const char *name = vreg->name; | |
297 | int min_uV; | |
298 | int uA_load; | |
adaafaa3 | 299 | |
adaafaa3 YG |
300 | if (regulator_count_voltages(reg) > 0) { |
301 | min_uV = on ? vreg->min_uV : 0; | |
302 | ret = regulator_set_voltage(reg, min_uV, vreg->max_uV); | |
303 | if (ret) { | |
304 | dev_err(dev, "%s: %s set voltage failed, err=%d\n", | |
305 | __func__, name, ret); | |
306 | goto out; | |
307 | } | |
308 | uA_load = on ? vreg->max_uA : 0; | |
7e476c7d | 309 | ret = regulator_set_load(reg, uA_load); |
adaafaa3 YG |
310 | if (ret >= 0) { |
311 | /* | |
7e476c7d | 312 | * regulator_set_load() returns new regulator |
adaafaa3 YG |
313 | * mode upon success. |
314 | */ | |
315 | ret = 0; | |
316 | } else { | |
317 | dev_err(dev, "%s: %s set optimum mode(uA_load=%d) failed, err=%d\n", | |
318 | __func__, name, uA_load, ret); | |
319 | goto out; | |
320 | } | |
321 | } | |
322 | out: | |
323 | return ret; | |
324 | } | |
325 | ||
89bd296b | 326 | static int ufs_qcom_phy_enable_vreg(struct device *dev, |
adaafaa3 YG |
327 | struct ufs_qcom_phy_vreg *vreg) |
328 | { | |
adaafaa3 YG |
329 | int ret = 0; |
330 | ||
331 | if (!vreg || vreg->enabled) | |
332 | goto out; | |
333 | ||
89bd296b | 334 | ret = ufs_qcom_phy_cfg_vreg(dev, vreg, true); |
adaafaa3 YG |
335 | if (ret) { |
336 | dev_err(dev, "%s: ufs_qcom_phy_cfg_vreg() failed, err=%d\n", | |
337 | __func__, ret); | |
338 | goto out; | |
339 | } | |
340 | ||
341 | ret = regulator_enable(vreg->reg); | |
342 | if (ret) { | |
343 | dev_err(dev, "%s: enable failed, err=%d\n", | |
344 | __func__, ret); | |
345 | goto out; | |
346 | } | |
347 | ||
348 | vreg->enabled = true; | |
349 | out: | |
350 | return ret; | |
351 | } | |
352 | ||
feb3d798 | 353 | static int ufs_qcom_phy_enable_ref_clk(struct ufs_qcom_phy *phy) |
adaafaa3 YG |
354 | { |
355 | int ret = 0; | |
adaafaa3 YG |
356 | |
357 | if (phy->is_ref_clk_enabled) | |
358 | goto out; | |
359 | ||
360 | /* | |
361 | * reference clock is propagated in a daisy-chained manner from | |
362 | * source to phy, so ungate them at each stage. | |
363 | */ | |
364 | ret = clk_prepare_enable(phy->ref_clk_src); | |
365 | if (ret) { | |
366 | dev_err(phy->dev, "%s: ref_clk_src enable failed %d\n", | |
367 | __func__, ret); | |
368 | goto out; | |
369 | } | |
370 | ||
371 | /* | |
372 | * "ref_clk_parent" is optional clock hence make sure that clk reference | |
373 | * is available before trying to enable the clock. | |
374 | */ | |
375 | if (phy->ref_clk_parent) { | |
376 | ret = clk_prepare_enable(phy->ref_clk_parent); | |
377 | if (ret) { | |
378 | dev_err(phy->dev, "%s: ref_clk_parent enable failed %d\n", | |
379 | __func__, ret); | |
380 | goto out_disable_src; | |
381 | } | |
382 | } | |
383 | ||
384 | ret = clk_prepare_enable(phy->ref_clk); | |
385 | if (ret) { | |
386 | dev_err(phy->dev, "%s: ref_clk enable failed %d\n", | |
387 | __func__, ret); | |
388 | goto out_disable_parent; | |
389 | } | |
390 | ||
391 | phy->is_ref_clk_enabled = true; | |
392 | goto out; | |
393 | ||
394 | out_disable_parent: | |
395 | if (phy->ref_clk_parent) | |
396 | clk_disable_unprepare(phy->ref_clk_parent); | |
397 | out_disable_src: | |
398 | clk_disable_unprepare(phy->ref_clk_src); | |
399 | out: | |
400 | return ret; | |
401 | } | |
402 | ||
89bd296b | 403 | static int ufs_qcom_phy_disable_vreg(struct device *dev, |
adaafaa3 YG |
404 | struct ufs_qcom_phy_vreg *vreg) |
405 | { | |
adaafaa3 YG |
406 | int ret = 0; |
407 | ||
96c163f1 | 408 | if (!vreg || !vreg->enabled) |
adaafaa3 YG |
409 | goto out; |
410 | ||
411 | ret = regulator_disable(vreg->reg); | |
412 | ||
413 | if (!ret) { | |
414 | /* ignore errors on applying disable config */ | |
89bd296b | 415 | ufs_qcom_phy_cfg_vreg(dev, vreg, false); |
adaafaa3 YG |
416 | vreg->enabled = false; |
417 | } else { | |
418 | dev_err(dev, "%s: %s disable failed, err=%d\n", | |
419 | __func__, vreg->name, ret); | |
420 | } | |
421 | out: | |
422 | return ret; | |
423 | } | |
424 | ||
feb3d798 | 425 | static void ufs_qcom_phy_disable_ref_clk(struct ufs_qcom_phy *phy) |
adaafaa3 | 426 | { |
adaafaa3 YG |
427 | if (phy->is_ref_clk_enabled) { |
428 | clk_disable_unprepare(phy->ref_clk); | |
429 | /* | |
430 | * "ref_clk_parent" is optional clock hence make sure that clk | |
431 | * reference is available before trying to disable the clock. | |
432 | */ | |
433 | if (phy->ref_clk_parent) | |
434 | clk_disable_unprepare(phy->ref_clk_parent); | |
435 | clk_disable_unprepare(phy->ref_clk_src); | |
436 | phy->is_ref_clk_enabled = false; | |
437 | } | |
438 | } | |
439 | ||
adaafaa3 | 440 | /* Turn ON M-PHY RMMI interface clocks */ |
feb3d798 | 441 | static int ufs_qcom_phy_enable_iface_clk(struct ufs_qcom_phy *phy) |
adaafaa3 | 442 | { |
adaafaa3 YG |
443 | int ret = 0; |
444 | ||
445 | if (phy->is_iface_clk_enabled) | |
446 | goto out; | |
447 | ||
448 | ret = clk_prepare_enable(phy->tx_iface_clk); | |
449 | if (ret) { | |
450 | dev_err(phy->dev, "%s: tx_iface_clk enable failed %d\n", | |
451 | __func__, ret); | |
452 | goto out; | |
453 | } | |
454 | ret = clk_prepare_enable(phy->rx_iface_clk); | |
455 | if (ret) { | |
456 | clk_disable_unprepare(phy->tx_iface_clk); | |
457 | dev_err(phy->dev, "%s: rx_iface_clk enable failed %d. disabling also tx_iface_clk\n", | |
458 | __func__, ret); | |
459 | goto out; | |
460 | } | |
461 | phy->is_iface_clk_enabled = true; | |
462 | ||
463 | out: | |
464 | return ret; | |
465 | } | |
466 | ||
467 | /* Turn OFF M-PHY RMMI interface clocks */ | |
e0c546fa | 468 | static void ufs_qcom_phy_disable_iface_clk(struct ufs_qcom_phy *phy) |
adaafaa3 | 469 | { |
adaafaa3 YG |
470 | if (phy->is_iface_clk_enabled) { |
471 | clk_disable_unprepare(phy->tx_iface_clk); | |
472 | clk_disable_unprepare(phy->rx_iface_clk); | |
473 | phy->is_iface_clk_enabled = false; | |
474 | } | |
475 | } | |
476 | ||
052553af | 477 | static int ufs_qcom_phy_start_serdes(struct ufs_qcom_phy *ufs_qcom_phy) |
adaafaa3 | 478 | { |
adaafaa3 YG |
479 | int ret = 0; |
480 | ||
481 | if (!ufs_qcom_phy->phy_spec_ops->start_serdes) { | |
482 | dev_err(ufs_qcom_phy->dev, "%s: start_serdes() callback is not supported\n", | |
483 | __func__); | |
484 | ret = -ENOTSUPP; | |
485 | } else { | |
486 | ufs_qcom_phy->phy_spec_ops->start_serdes(ufs_qcom_phy); | |
487 | } | |
488 | ||
489 | return ret; | |
490 | } | |
491 | ||
492 | int ufs_qcom_phy_set_tx_lane_enable(struct phy *generic_phy, u32 tx_lanes) | |
493 | { | |
494 | struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); | |
495 | int ret = 0; | |
496 | ||
497 | if (!ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable) { | |
498 | dev_err(ufs_qcom_phy->dev, "%s: set_tx_lane_enable() callback is not supported\n", | |
499 | __func__); | |
500 | ret = -ENOTSUPP; | |
501 | } else { | |
502 | ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable(ufs_qcom_phy, | |
503 | tx_lanes); | |
504 | } | |
505 | ||
506 | return ret; | |
507 | } | |
65d49b3d | 508 | EXPORT_SYMBOL_GPL(ufs_qcom_phy_set_tx_lane_enable); |
adaafaa3 YG |
509 | |
510 | void ufs_qcom_phy_save_controller_version(struct phy *generic_phy, | |
511 | u8 major, u16 minor, u16 step) | |
512 | { | |
513 | struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); | |
514 | ||
515 | ufs_qcom_phy->host_ctrl_rev_major = major; | |
516 | ufs_qcom_phy->host_ctrl_rev_minor = minor; | |
517 | ufs_qcom_phy->host_ctrl_rev_step = step; | |
518 | } | |
65d49b3d | 519 | EXPORT_SYMBOL_GPL(ufs_qcom_phy_save_controller_version); |
adaafaa3 | 520 | |
052553af | 521 | static int ufs_qcom_phy_is_pcs_ready(struct ufs_qcom_phy *ufs_qcom_phy) |
adaafaa3 | 522 | { |
adaafaa3 YG |
523 | if (!ufs_qcom_phy->phy_spec_ops->is_physical_coding_sublayer_ready) { |
524 | dev_err(ufs_qcom_phy->dev, "%s: is_physical_coding_sublayer_ready() callback is not supported\n", | |
525 | __func__); | |
526 | return -ENOTSUPP; | |
527 | } | |
528 | ||
529 | return ufs_qcom_phy->phy_spec_ops-> | |
530 | is_physical_coding_sublayer_ready(ufs_qcom_phy); | |
531 | } | |
532 | ||
533 | int ufs_qcom_phy_power_on(struct phy *generic_phy) | |
534 | { | |
535 | struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); | |
536 | struct device *dev = phy_common->dev; | |
3f6d1767 | 537 | bool is_rate_B = false; |
adaafaa3 YG |
538 | int err; |
539 | ||
3f6d1767 EG |
540 | err = ufs_qcom_phy_get_reset(phy_common); |
541 | if (err) | |
542 | return err; | |
3d4640f1 | 543 | |
3f6d1767 EG |
544 | err = reset_control_assert(phy_common->ufs_reset); |
545 | if (err) | |
546 | return err; | |
052553af | 547 | |
3f6d1767 EG |
548 | if (phy_common->mode == PHY_MODE_UFS_HS_B) |
549 | is_rate_B = true; | |
052553af | 550 | |
3f6d1767 EG |
551 | err = phy_common->phy_spec_ops->calibrate(phy_common, is_rate_B); |
552 | if (err) | |
553 | return err; | |
3d4640f1 | 554 | |
c9b58979 EG |
555 | err = reset_control_deassert(phy_common->ufs_reset); |
556 | if (err) { | |
557 | dev_err(dev, "Failed to assert UFS PHY reset"); | |
558 | return err; | |
052553af VG |
559 | } |
560 | ||
3f6d1767 EG |
561 | err = ufs_qcom_phy_start_serdes(phy_common); |
562 | if (err) | |
563 | return err; | |
052553af | 564 | |
3f6d1767 EG |
565 | err = ufs_qcom_phy_is_pcs_ready(phy_common); |
566 | if (err) | |
567 | return err; | |
052553af | 568 | |
89bd296b | 569 | err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy); |
adaafaa3 YG |
570 | if (err) { |
571 | dev_err(dev, "%s enable vdda_phy failed, err=%d\n", | |
572 | __func__, err); | |
573 | goto out; | |
574 | } | |
575 | ||
576 | phy_common->phy_spec_ops->power_control(phy_common, true); | |
577 | ||
578 | /* vdda_pll also enables ref clock LDOs so enable it first */ | |
89bd296b | 579 | err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_pll); |
adaafaa3 YG |
580 | if (err) { |
581 | dev_err(dev, "%s enable vdda_pll failed, err=%d\n", | |
582 | __func__, err); | |
583 | goto out_disable_phy; | |
584 | } | |
585 | ||
feb3d798 | 586 | err = ufs_qcom_phy_enable_iface_clk(phy_common); |
adaafaa3 | 587 | if (err) { |
feb3d798 | 588 | dev_err(dev, "%s enable phy iface clock failed, err=%d\n", |
adaafaa3 YG |
589 | __func__, err); |
590 | goto out_disable_pll; | |
591 | } | |
592 | ||
feb3d798 VG |
593 | err = ufs_qcom_phy_enable_ref_clk(phy_common); |
594 | if (err) { | |
595 | dev_err(dev, "%s enable phy ref clock failed, err=%d\n", | |
596 | __func__, err); | |
597 | goto out_disable_iface_clk; | |
598 | } | |
599 | ||
adaafaa3 YG |
600 | /* enable device PHY ref_clk pad rail */ |
601 | if (phy_common->vddp_ref_clk.reg) { | |
89bd296b | 602 | err = ufs_qcom_phy_enable_vreg(dev, |
adaafaa3 YG |
603 | &phy_common->vddp_ref_clk); |
604 | if (err) { | |
605 | dev_err(dev, "%s enable vddp_ref_clk failed, err=%d\n", | |
606 | __func__, err); | |
607 | goto out_disable_ref_clk; | |
608 | } | |
609 | } | |
610 | ||
adaafaa3 YG |
611 | goto out; |
612 | ||
613 | out_disable_ref_clk: | |
feb3d798 VG |
614 | ufs_qcom_phy_disable_ref_clk(phy_common); |
615 | out_disable_iface_clk: | |
616 | ufs_qcom_phy_disable_iface_clk(phy_common); | |
adaafaa3 | 617 | out_disable_pll: |
89bd296b | 618 | ufs_qcom_phy_disable_vreg(dev, &phy_common->vdda_pll); |
adaafaa3 | 619 | out_disable_phy: |
89bd296b | 620 | ufs_qcom_phy_disable_vreg(dev, &phy_common->vdda_phy); |
adaafaa3 YG |
621 | out: |
622 | return err; | |
623 | } | |
358d6c87 | 624 | EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_on); |
adaafaa3 YG |
625 | |
626 | int ufs_qcom_phy_power_off(struct phy *generic_phy) | |
627 | { | |
628 | struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); | |
629 | ||
630 | phy_common->phy_spec_ops->power_control(phy_common, false); | |
631 | ||
632 | if (phy_common->vddp_ref_clk.reg) | |
89bd296b | 633 | ufs_qcom_phy_disable_vreg(phy_common->dev, |
adaafaa3 | 634 | &phy_common->vddp_ref_clk); |
feb3d798 VG |
635 | ufs_qcom_phy_disable_ref_clk(phy_common); |
636 | ufs_qcom_phy_disable_iface_clk(phy_common); | |
adaafaa3 | 637 | |
89bd296b VG |
638 | ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_pll); |
639 | ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_phy); | |
c9b58979 | 640 | reset_control_assert(phy_common->ufs_reset); |
adaafaa3 YG |
641 | return 0; |
642 | } | |
358d6c87 | 643 | EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_off); |
59fba086 AB |
644 | |
645 | MODULE_AUTHOR("Yaniv Gardi <ygardi@codeaurora.org>"); | |
646 | MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>"); | |
647 | MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY"); | |
648 | MODULE_LICENSE("GPL v2"); |