]>
Commit | Line | Data |
---|---|---|
b5ac5f03 VYA |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (C) 2024 Advanced Micro Devices, Inc. | |
4 | */ | |
5 | ||
6 | #include <clk.h> | |
7 | #include <dm.h> | |
8 | #include <ufs.h> | |
9 | #include <asm/io.h> | |
10 | #include <dm/device_compat.h> | |
11 | #include <zynqmp_firmware.h> | |
12 | #include <linux/bitops.h> | |
13 | #include <linux/delay.h> | |
14 | #include <linux/err.h> | |
15 | #include <linux/time.h> | |
16 | #include <reset.h> | |
17 | ||
18 | #include "ufs.h" | |
19 | #include "ufshcd-dwc.h" | |
20 | #include "ufshci-dwc.h" | |
21 | ||
22 | #define VERSAL2_UFS_DEVICE_ID 4 | |
23 | ||
24 | #define SRAM_CSR_INIT_DONE_MASK BIT(0) | |
25 | #define SRAM_CSR_EXT_LD_DONE_MASK BIT(1) | |
26 | #define SRAM_CSR_BYPASS_MASK BIT(2) | |
27 | ||
28 | #define MPHY_FAST_RX_AFE_CAL BIT(2) | |
29 | #define MPHY_FW_CALIB_CFG_VAL BIT(8) | |
30 | ||
31 | #define TX_RX_CFG_RDY_MASK GENMASK(3, 0) | |
32 | ||
33 | #define TIMEOUT_MICROSEC 1000000L | |
34 | ||
35 | #define IOCTL_UFS_TXRX_CFGRDY_GET 40 | |
36 | #define IOCTL_UFS_SRAM_CSR_SEL 41 | |
37 | ||
38 | #define PM_UFS_SRAM_CSR_WRITE 0 | |
39 | #define PM_UFS_SRAM_CSR_READ 1 | |
40 | ||
41 | struct ufs_versal2_priv { | |
42 | struct ufs_hba *hba; | |
43 | struct reset_ctl *rstc; | |
44 | struct reset_ctl *rstphy; | |
45 | u32 phy_mode; | |
46 | u32 host_clk; | |
47 | u32 pd_dev_id; | |
48 | u8 attcompval0; | |
49 | u8 attcompval1; | |
50 | u8 ctlecompval0; | |
51 | u8 ctlecompval1; | |
52 | }; | |
53 | ||
54 | static int ufs_versal2_phy_reg_write(struct ufs_hba *hba, u32 addr, u32 val) | |
55 | { | |
56 | static struct ufshcd_dme_attr_val phy_write_attrs[] = { | |
57 | { UIC_ARG_MIB(CBCREGADDRLSB), 0, DME_LOCAL }, | |
58 | { UIC_ARG_MIB(CBCREGADDRMSB), 0, DME_LOCAL }, | |
59 | { UIC_ARG_MIB(CBCREGWRLSB), 0, DME_LOCAL }, | |
60 | { UIC_ARG_MIB(CBCREGWRMSB), 0, DME_LOCAL }, | |
61 | { UIC_ARG_MIB(CBCREGRDWRSEL), 1, DME_LOCAL }, | |
62 | { UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL } | |
63 | }; | |
64 | ||
65 | phy_write_attrs[0].mib_val = (u8)addr; | |
66 | phy_write_attrs[1].mib_val = (u8)(addr >> 8); | |
67 | phy_write_attrs[2].mib_val = (u8)val; | |
68 | phy_write_attrs[3].mib_val = (u8)(val >> 8); | |
69 | ||
70 | return ufshcd_dwc_dme_set_attrs(hba, phy_write_attrs, ARRAY_SIZE(phy_write_attrs)); | |
71 | } | |
72 | ||
73 | static int ufs_versal2_phy_reg_read(struct ufs_hba *hba, u32 addr, u32 *val) | |
74 | { | |
75 | u32 mib_val; | |
76 | int ret; | |
77 | static struct ufshcd_dme_attr_val phy_read_attrs[] = { | |
78 | { UIC_ARG_MIB(CBCREGADDRLSB), 0, DME_LOCAL }, | |
79 | { UIC_ARG_MIB(CBCREGADDRMSB), 0, DME_LOCAL }, | |
80 | { UIC_ARG_MIB(CBCREGRDWRSEL), 0, DME_LOCAL }, | |
81 | { UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL } | |
82 | }; | |
83 | ||
84 | phy_read_attrs[0].mib_val = (u8)addr; | |
85 | phy_read_attrs[1].mib_val = (u8)(addr >> 8); | |
86 | ||
87 | ret = ufshcd_dwc_dme_set_attrs(hba, phy_read_attrs, ARRAY_SIZE(phy_read_attrs)); | |
88 | if (ret) | |
89 | return ret; | |
90 | ||
91 | ret = ufshcd_dme_get(hba, UIC_ARG_MIB(CBCREGRDLSB), &mib_val); | |
92 | if (ret) | |
93 | return ret; | |
94 | ||
95 | *val = mib_val; | |
96 | ret = ufshcd_dme_get(hba, UIC_ARG_MIB(CBCREGRDMSB), &mib_val); | |
97 | if (ret) | |
98 | return ret; | |
99 | ||
100 | *val |= (mib_val << 8); | |
101 | ||
102 | return 0; | |
103 | } | |
104 | ||
105 | int versal2_pm_ufs_get_txrx_cfgrdy(u32 node_id, u32 *value) | |
106 | { | |
107 | u32 ret_payload[PAYLOAD_ARG_CNT]; | |
108 | int ret; | |
109 | ||
110 | if (!value) | |
111 | return -EINVAL; | |
112 | ||
113 | ret = xilinx_pm_request(PM_IOCTL, node_id, IOCTL_UFS_TXRX_CFGRDY_GET, | |
114 | 0, 0, ret_payload); | |
115 | *value = ret_payload[1]; | |
116 | ||
117 | return ret; | |
118 | } | |
119 | ||
120 | int versal2_pm_ufs_sram_csr_sel(u32 node_id, u32 type, u32 *value) | |
121 | { | |
122 | u32 ret_payload[PAYLOAD_ARG_CNT]; | |
123 | int ret; | |
124 | ||
125 | if (!value) | |
126 | return -EINVAL; | |
127 | ||
128 | if (type == PM_UFS_SRAM_CSR_READ) { | |
129 | ret = xilinx_pm_request(PM_IOCTL, node_id, IOCTL_UFS_SRAM_CSR_SEL, | |
130 | type, 0, ret_payload); | |
131 | *value = ret_payload[1]; | |
132 | } else { | |
133 | ret = xilinx_pm_request(PM_IOCTL, node_id, IOCTL_UFS_SRAM_CSR_SEL, | |
134 | type, *value, 0); | |
135 | } | |
136 | ||
137 | return ret; | |
138 | } | |
139 | ||
140 | static int ufs_versal2_enable_phy(struct ufs_hba *hba) | |
141 | { | |
142 | u32 offset, reg; | |
143 | int ret; | |
144 | ||
145 | ret = ufshcd_dme_set(hba, UIC_ARG_MIB(VS_MPHYDISABLE), 0); | |
146 | if (ret) | |
147 | return ret; | |
148 | ||
149 | ret = ufshcd_dme_set(hba, UIC_ARG_MIB(VS_MPHYCFGUPDT), 1); | |
150 | if (ret) | |
151 | return ret; | |
152 | ||
153 | /* Check Tx/Rx FSM states */ | |
154 | for (offset = 0; offset < 2; offset++) { | |
155 | u32 time_left, mibsel; | |
156 | ||
157 | time_left = TIMEOUT_MICROSEC; | |
158 | mibsel = UIC_ARG_MIB_SEL(MTX_FSM_STATE, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(offset)); | |
159 | do { | |
160 | ret = ufshcd_dme_get(hba, mibsel, ®); | |
161 | if (ret) | |
162 | return ret; | |
163 | ||
164 | if (reg == TX_STATE_HIBERN8 || reg == TX_STATE_SLEEP || | |
165 | reg == TX_STATE_LSBURST) | |
166 | break; | |
167 | ||
168 | time_left--; | |
169 | mdelay(5); | |
170 | } while (time_left); | |
171 | ||
172 | if (!time_left) { | |
173 | dev_err(hba->dev, "Invalid Tx FSM state.\n"); | |
174 | return -ETIMEDOUT; | |
175 | } | |
176 | ||
177 | time_left = TIMEOUT_MICROSEC; | |
178 | mibsel = UIC_ARG_MIB_SEL(MRX_FSM_STATE, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(offset)); | |
179 | do { | |
180 | ret = ufshcd_dme_get(hba, mibsel, ®); | |
181 | if (ret) | |
182 | return ret; | |
183 | ||
184 | if (reg == RX_STATE_HIBERN8 || reg == RX_STATE_SLEEP || | |
185 | reg == RX_STATE_LSBURST) | |
186 | break; | |
187 | ||
188 | time_left--; | |
189 | mdelay(5); | |
190 | } while (time_left); | |
191 | ||
192 | if (!time_left) { | |
193 | dev_err(hba->dev, "Invalid Rx FSM state.\n"); | |
194 | return -ETIMEDOUT; | |
195 | } | |
196 | } | |
197 | ||
198 | return 0; | |
199 | } | |
200 | ||
201 | static int ufs_versal2_setup_phy(struct ufs_hba *hba) | |
202 | { | |
203 | struct ufs_versal2_priv *priv = dev_get_priv(hba->dev); | |
204 | int ret; | |
205 | u32 reg; | |
206 | ||
207 | /* Bypass RX-AFE offset calibrations (ATT/CTLE) */ | |
208 | ret = ufs_versal2_phy_reg_read(hba, FAST_FLAGS(0), ®); | |
209 | if (ret) | |
210 | return ret; | |
211 | ||
212 | reg |= MPHY_FAST_RX_AFE_CAL; | |
213 | ret = ufs_versal2_phy_reg_write(hba, FAST_FLAGS(0), reg); | |
214 | if (ret) | |
215 | return ret; | |
216 | ||
217 | ret = ufs_versal2_phy_reg_read(hba, FAST_FLAGS(1), ®); | |
218 | if (ret) | |
219 | return ret; | |
220 | ||
221 | reg |= MPHY_FAST_RX_AFE_CAL; | |
222 | ret = ufs_versal2_phy_reg_write(hba, FAST_FLAGS(1), reg); | |
223 | if (ret) | |
224 | return ret; | |
225 | ||
226 | /* Program ATT and CTLE compensation values */ | |
227 | if (priv->attcompval0) { | |
228 | ret = ufs_versal2_phy_reg_write(hba, RX_AFE_ATT_IDAC(0), priv->attcompval0); | |
229 | if (ret) | |
230 | return ret; | |
231 | } | |
232 | ||
233 | if (priv->attcompval1) { | |
234 | ret = ufs_versal2_phy_reg_write(hba, RX_AFE_ATT_IDAC(1), priv->attcompval1); | |
235 | if (ret) | |
236 | return ret; | |
237 | } | |
238 | ||
239 | if (priv->ctlecompval0) { | |
240 | ret = ufs_versal2_phy_reg_write(hba, RX_AFE_CTLE_IDAC(0), priv->ctlecompval0); | |
241 | if (ret) | |
242 | return ret; | |
243 | } | |
244 | ||
245 | if (priv->ctlecompval1) { | |
246 | ret = ufs_versal2_phy_reg_write(hba, RX_AFE_CTLE_IDAC(1), priv->ctlecompval1); | |
247 | if (ret) | |
248 | return ret; | |
249 | } | |
250 | ||
251 | ret = ufs_versal2_phy_reg_read(hba, FW_CALIB_CCFG(0), ®); | |
252 | if (ret) | |
253 | return ret; | |
254 | ||
255 | reg |= MPHY_FW_CALIB_CFG_VAL; | |
256 | ret = ufs_versal2_phy_reg_write(hba, FW_CALIB_CCFG(0), reg); | |
257 | if (ret) | |
258 | return ret; | |
259 | ||
260 | ret = ufs_versal2_phy_reg_read(hba, FW_CALIB_CCFG(1), ®); | |
261 | if (ret) | |
262 | return ret; | |
263 | ||
264 | reg |= MPHY_FW_CALIB_CFG_VAL; | |
265 | return ufs_versal2_phy_reg_write(hba, FW_CALIB_CCFG(1), reg); | |
266 | } | |
267 | ||
268 | static int ufs_versal2_phy_init(struct ufs_hba *hba) | |
269 | { | |
270 | struct ufs_versal2_priv *priv = dev_get_priv(hba->dev); | |
271 | u32 reg, time_left; | |
272 | int ret; | |
273 | static const struct ufshcd_dme_attr_val rmmi_attrs[] = { | |
274 | { UIC_ARG_MIB(CBREFCLKCTRL2), CBREFREFCLK_GATE_OVR_EN, DME_LOCAL }, | |
275 | { UIC_ARG_MIB(CBCRCTRL), 1, DME_LOCAL }, | |
276 | { UIC_ARG_MIB(CBC10DIRECTCONF2), 1, DME_LOCAL }, | |
277 | { UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL } | |
278 | }; | |
279 | ||
280 | /* Wait for Tx/Rx config_rdy */ | |
281 | time_left = TIMEOUT_MICROSEC; | |
282 | do { | |
283 | time_left--; | |
284 | ret = versal2_pm_ufs_get_txrx_cfgrdy(priv->pd_dev_id, ®); | |
285 | if (ret) | |
286 | return ret; | |
287 | ||
288 | reg &= TX_RX_CFG_RDY_MASK; | |
289 | if (!reg) | |
290 | break; | |
291 | ||
292 | mdelay(5); | |
293 | } while (time_left); | |
294 | ||
295 | if (!time_left) { | |
296 | dev_err(hba->dev, "Tx/Rx configuration signal busy.\n"); | |
297 | return -ETIMEDOUT; | |
298 | } | |
299 | ||
300 | ret = ufshcd_dwc_dme_set_attrs(hba, rmmi_attrs, ARRAY_SIZE(rmmi_attrs)); | |
301 | if (ret) | |
302 | return ret; | |
303 | ||
304 | /* DeAssert PHY reset */ | |
305 | ret = reset_deassert(priv->rstphy); | |
306 | if (ret) { | |
307 | dev_err(hba->dev, "ufsphy reset deassert failed\n"); | |
308 | return ret; | |
309 | } | |
310 | ||
311 | /* Wait for SRAM init done */ | |
312 | time_left = TIMEOUT_MICROSEC; | |
313 | do { | |
314 | time_left--; | |
315 | ret = versal2_pm_ufs_sram_csr_sel(priv->pd_dev_id, | |
316 | PM_UFS_SRAM_CSR_READ, ®); | |
317 | if (ret) | |
318 | return ret; | |
319 | ||
320 | reg &= SRAM_CSR_INIT_DONE_MASK; | |
321 | if (reg) | |
322 | break; | |
323 | ||
324 | mdelay(5); | |
325 | } while (time_left); | |
326 | ||
327 | if (!time_left) { | |
328 | dev_err(hba->dev, "SRAM initialization failed.\n"); | |
329 | return -ETIMEDOUT; | |
330 | } | |
331 | ||
332 | ret = ufs_versal2_setup_phy(hba); | |
333 | if (ret) | |
334 | return ret; | |
335 | ||
336 | return ufs_versal2_enable_phy(hba); | |
337 | } | |
338 | ||
339 | static int ufs_versal2_init(struct ufs_hba *hba) | |
340 | { | |
341 | struct ufs_versal2_priv *priv = dev_get_priv(hba->dev); | |
342 | struct clk clk; | |
343 | unsigned long core_clk_rate = 0; | |
344 | int ret = 0; | |
345 | ||
346 | priv->phy_mode = UFSHCD_DWC_PHY_MODE_ROM; | |
347 | priv->pd_dev_id = VERSAL2_UFS_DEVICE_ID; | |
348 | ||
349 | ret = clk_get_by_name(hba->dev, "core_clk", &clk); | |
350 | if (ret) { | |
351 | dev_err(hba->dev, "failed to get core_clk clock\n"); | |
352 | return ret; | |
353 | } | |
354 | ||
355 | core_clk_rate = clk_get_rate(&clk); | |
356 | if (IS_ERR_VALUE(core_clk_rate)) { | |
357 | dev_err(hba->dev, "%s: unable to find core_clk rate\n", | |
358 | __func__); | |
359 | return core_clk_rate; | |
360 | } | |
361 | priv->host_clk = core_clk_rate; | |
362 | ||
363 | priv->rstc = devm_reset_control_get(hba->dev, "ufshc-rst"); | |
364 | if (IS_ERR(priv->rstc)) { | |
365 | dev_err(hba->dev, "failed to get reset ctl: ufshc-rst\n"); | |
366 | return PTR_ERR(priv->rstc); | |
367 | } | |
368 | priv->rstphy = devm_reset_control_get(hba->dev, "ufsphy-rst"); | |
369 | if (IS_ERR(priv->rstphy)) { | |
370 | dev_err(hba->dev, "failed to get reset ctl: ufsphy-rst\n"); | |
371 | return PTR_ERR(priv->rstphy); | |
372 | } | |
373 | ||
374 | return ret; | |
375 | } | |
376 | ||
377 | static int ufs_versal2_hce_enable_notify(struct ufs_hba *hba, | |
378 | enum ufs_notify_change_status status) | |
379 | { | |
380 | struct ufs_versal2_priv *priv = dev_get_priv(hba->dev); | |
381 | u32 sram_csr; | |
382 | int ret; | |
383 | ||
384 | switch (status) { | |
385 | case PRE_CHANGE: | |
386 | /* Assert RST_UFS Reset for UFS block in PMX_IOU */ | |
387 | ret = reset_assert(priv->rstc); | |
388 | if (ret) { | |
389 | dev_err(hba->dev, "ufshc reset assert failed, err = %d\n", ret); | |
390 | return ret; | |
391 | } | |
392 | ||
393 | /* Assert PHY reset */ | |
394 | ret = reset_assert(priv->rstphy); | |
395 | if (ret) { | |
396 | dev_err(hba->dev, "ufsphy reset assert failed, err = %d\n", ret); | |
397 | return ret; | |
398 | } | |
399 | ||
400 | ret = versal2_pm_ufs_sram_csr_sel(priv->pd_dev_id, | |
401 | PM_UFS_SRAM_CSR_READ, &sram_csr); | |
402 | if (ret) | |
403 | return ret; | |
404 | ||
405 | if (!priv->phy_mode) { | |
406 | sram_csr &= ~SRAM_CSR_EXT_LD_DONE_MASK; | |
407 | sram_csr |= SRAM_CSR_BYPASS_MASK; | |
408 | } else { | |
409 | dev_err(hba->dev, "Invalid phy-mode %d.\n", priv->phy_mode); | |
410 | return -EINVAL; | |
411 | } | |
412 | ||
413 | ret = versal2_pm_ufs_sram_csr_sel(priv->pd_dev_id, | |
414 | PM_UFS_SRAM_CSR_WRITE, &sram_csr); | |
415 | if (ret) | |
416 | return ret; | |
417 | ||
418 | /* De Assert RST_UFS Reset for UFS block in PMX_IOU */ | |
419 | ret = reset_deassert(priv->rstc); | |
420 | if (ret) | |
421 | dev_err(hba->dev, "ufshc reset deassert failed, err = %d\n", ret); | |
422 | ||
423 | break; | |
424 | case POST_CHANGE: | |
425 | ret = ufs_versal2_phy_init(hba); | |
426 | if (ret) | |
427 | dev_err(hba->dev, "Phy init failed (%d)\n", ret); | |
428 | ||
429 | break; | |
430 | default: | |
431 | ret = -EINVAL; | |
432 | break; | |
433 | } | |
434 | ||
435 | return ret; | |
436 | } | |
437 | ||
438 | static int ufs_versal2_link_startup_notify(struct ufs_hba *hba, | |
439 | enum ufs_notify_change_status status) | |
440 | { | |
441 | struct ufs_versal2_priv *priv = dev_get_priv(hba->dev); | |
442 | int ret = 0; | |
443 | ||
444 | switch (status) { | |
445 | case PRE_CHANGE: | |
446 | if (priv->host_clk) { | |
447 | u32 core_clk_div = priv->host_clk / TIMEOUT_MICROSEC; | |
448 | ||
449 | ufshcd_writel(hba, core_clk_div, DWC_UFS_REG_HCLKDIV); | |
450 | } | |
451 | break; | |
452 | case POST_CHANGE: | |
453 | ret = ufshcd_dwc_link_startup_notify(hba, status); | |
454 | break; | |
455 | default: | |
456 | ret = -EINVAL; | |
457 | break; | |
458 | } | |
459 | ||
460 | return ret; | |
461 | } | |
462 | ||
463 | static struct ufs_hba_ops ufs_versal2_hba_ops = { | |
464 | .init = ufs_versal2_init, | |
465 | .link_startup_notify = ufs_versal2_link_startup_notify, | |
466 | .hce_enable_notify = ufs_versal2_hce_enable_notify, | |
467 | }; | |
468 | ||
469 | static int ufs_versal2_probe(struct udevice *dev) | |
470 | { | |
471 | int ret; | |
472 | ||
473 | /* Perform generic probe */ | |
474 | ret = ufshcd_probe(dev, &ufs_versal2_hba_ops); | |
475 | if (ret) | |
476 | dev_err(dev, "ufshcd_probe() failed %d\n", ret); | |
477 | ||
478 | return ret; | |
479 | } | |
480 | ||
481 | static int ufs_versal2_bind(struct udevice *dev) | |
482 | { | |
483 | struct udevice *scsi_dev; | |
484 | ||
485 | return ufs_scsi_bind(dev, &scsi_dev); | |
486 | } | |
487 | ||
488 | static const struct udevice_id ufs_versal2_ids[] = { | |
489 | { | |
490 | .compatible = "amd,versal2-ufs", | |
491 | }, | |
492 | {}, | |
493 | }; | |
494 | ||
495 | U_BOOT_DRIVER(ufs_versal2_pltfm) = { | |
496 | .name = "ufs-versal2-pltfm", | |
497 | .id = UCLASS_UFS, | |
498 | .of_match = ufs_versal2_ids, | |
499 | .probe = ufs_versal2_probe, | |
500 | .bind = ufs_versal2_bind, | |
501 | }; |