1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * UNISOC UFS Host Controller driver |
4 | * |
5 | * Copyright (C) 2022 Unisoc, Inc. |
6 | * Author: Zhe Wang <zhe.wang1@unisoc.com> |
7 | */ |
8 | |
9 | #include <linux/arm-smccc.h> |
10 | #include <linux/mfd/syscon.h> |
11 | #include <linux/of.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/regmap.h> |
14 | #include <linux/reset.h> |
15 | #include <linux/regulator/consumer.h> |
16 | |
17 | #include <ufs/ufshcd.h> |
18 | #include "ufshcd-pltfrm.h" |
19 | #include "ufs-sprd.h" |
20 | |
21 | static const struct of_device_id ufs_sprd_of_match[]; |
22 | |
23 | static struct ufs_sprd_priv *ufs_sprd_get_priv_data(struct ufs_hba *hba) |
24 | { |
25 | struct ufs_sprd_host *host = ufshcd_get_variant(hba); |
26 | |
27 | WARN_ON(!host->priv); |
28 | return host->priv; |
29 | } |
30 | |
31 | static void ufs_sprd_regmap_update(struct ufs_sprd_priv *priv, unsigned int index, |
32 | unsigned int reg, unsigned int bits, unsigned int val) |
33 | { |
34 | regmap_update_bits(map: priv->sysci[index].regmap, reg, mask: bits, val); |
35 | } |
36 | |
37 | static void ufs_sprd_regmap_read(struct ufs_sprd_priv *priv, unsigned int index, |
38 | unsigned int reg, unsigned int *val) |
39 | { |
40 | regmap_read(map: priv->sysci[index].regmap, reg, val); |
41 | } |
42 | |
43 | static void ufs_sprd_get_unipro_ver(struct ufs_hba *hba) |
44 | { |
45 | struct ufs_sprd_host *host = ufshcd_get_variant(hba); |
46 | |
47 | if (ufshcd_dme_get(hba, UIC_ARG_MIB(PA_LOCALVERINFO), mib_val: &host->unipro_ver)) |
48 | host->unipro_ver = 0; |
49 | } |
50 | |
51 | static void ufs_sprd_ctrl_uic_compl(struct ufs_hba *hba, bool enable) |
52 | { |
53 | u32 set = ufshcd_readl(hba, REG_INTERRUPT_ENABLE); |
54 | |
55 | if (enable == true) |
56 | set |= UIC_COMMAND_COMPL; |
57 | else |
58 | set &= ~UIC_COMMAND_COMPL; |
59 | ufshcd_writel(hba, set, REG_INTERRUPT_ENABLE); |
60 | } |
61 | |
62 | static int ufs_sprd_get_reset_ctrl(struct device *dev, struct ufs_sprd_rst *rci) |
63 | { |
64 | rci->rc = devm_reset_control_get(dev, id: rci->name); |
65 | if (IS_ERR(ptr: rci->rc)) { |
66 | dev_err(dev, "failed to get reset ctrl:%s\n" , rci->name); |
67 | return PTR_ERR(ptr: rci->rc); |
68 | } |
69 | |
70 | return 0; |
71 | } |
72 | |
73 | static int ufs_sprd_get_syscon_reg(struct device *dev, struct ufs_sprd_syscon *sysci) |
74 | { |
75 | sysci->regmap = syscon_regmap_lookup_by_phandle(np: dev->of_node, property: sysci->name); |
76 | if (IS_ERR(ptr: sysci->regmap)) { |
77 | dev_err(dev, "failed to get ufs syscon:%s\n" , sysci->name); |
78 | return PTR_ERR(ptr: sysci->regmap); |
79 | } |
80 | |
81 | return 0; |
82 | } |
83 | |
84 | static int ufs_sprd_get_vreg(struct device *dev, struct ufs_sprd_vreg *vregi) |
85 | { |
86 | vregi->vreg = devm_regulator_get(dev, id: vregi->name); |
87 | if (IS_ERR(ptr: vregi->vreg)) { |
88 | dev_err(dev, "failed to get vreg:%s\n" , vregi->name); |
89 | return PTR_ERR(ptr: vregi->vreg); |
90 | } |
91 | |
92 | return 0; |
93 | } |
94 | |
95 | static int ufs_sprd_parse_dt(struct device *dev, struct ufs_hba *hba, struct ufs_sprd_host *host) |
96 | { |
97 | u32 i; |
98 | struct ufs_sprd_priv *priv = host->priv; |
99 | int ret = 0; |
100 | |
101 | /* Parse UFS reset ctrl info */ |
102 | for (i = 0; i < SPRD_UFS_RST_MAX; i++) { |
103 | if (!priv->rci[i].name) |
104 | continue; |
105 | ret = ufs_sprd_get_reset_ctrl(dev, rci: &priv->rci[i]); |
106 | if (ret) |
107 | goto out; |
108 | } |
109 | |
110 | /* Parse UFS syscon reg info */ |
111 | for (i = 0; i < SPRD_UFS_SYSCON_MAX; i++) { |
112 | if (!priv->sysci[i].name) |
113 | continue; |
114 | ret = ufs_sprd_get_syscon_reg(dev, sysci: &priv->sysci[i]); |
115 | if (ret) |
116 | goto out; |
117 | } |
118 | |
119 | /* Parse UFS vreg info */ |
120 | for (i = 0; i < SPRD_UFS_VREG_MAX; i++) { |
121 | if (!priv->vregi[i].name) |
122 | continue; |
123 | ret = ufs_sprd_get_vreg(dev, vregi: &priv->vregi[i]); |
124 | if (ret) |
125 | goto out; |
126 | } |
127 | |
128 | out: |
129 | return ret; |
130 | } |
131 | |
132 | static int ufs_sprd_common_init(struct ufs_hba *hba) |
133 | { |
134 | struct device *dev = hba->dev; |
135 | struct ufs_sprd_host *host; |
136 | struct platform_device __maybe_unused *pdev = to_platform_device(dev); |
137 | const struct of_device_id *of_id; |
138 | int ret = 0; |
139 | |
140 | host = devm_kzalloc(dev, size: sizeof(*host), GFP_KERNEL); |
141 | if (!host) |
142 | return -ENOMEM; |
143 | |
144 | of_id = of_match_node(matches: ufs_sprd_of_match, node: pdev->dev.of_node); |
145 | if (of_id->data != NULL) |
146 | host->priv = container_of(of_id->data, struct ufs_sprd_priv, |
147 | ufs_hba_sprd_vops); |
148 | |
149 | host->hba = hba; |
150 | ufshcd_set_variant(hba, variant: host); |
151 | |
152 | hba->caps |= UFSHCD_CAP_CLK_GATING | |
153 | UFSHCD_CAP_CRYPTO | |
154 | UFSHCD_CAP_WB_EN; |
155 | hba->quirks |= UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS; |
156 | |
157 | ret = ufs_sprd_parse_dt(dev, hba, host); |
158 | |
159 | return ret; |
160 | } |
161 | |
162 | static int sprd_ufs_pwr_change_notify(struct ufs_hba *hba, |
163 | enum ufs_notify_change_status status, |
164 | struct ufs_pa_layer_attr *dev_max_params, |
165 | struct ufs_pa_layer_attr *dev_req_params) |
166 | { |
167 | struct ufs_sprd_host *host = ufshcd_get_variant(hba); |
168 | |
169 | if (status == PRE_CHANGE) { |
170 | memcpy(dev_req_params, dev_max_params, |
171 | sizeof(struct ufs_pa_layer_attr)); |
172 | if (host->unipro_ver >= UFS_UNIPRO_VER_1_8) |
173 | ufshcd_dme_configure_adapt(hba, agreed_gear: dev_req_params->gear_tx, |
174 | PA_INITIAL_ADAPT); |
175 | } |
176 | |
177 | return 0; |
178 | } |
179 | |
180 | static int ufs_sprd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op, |
181 | enum ufs_notify_change_status status) |
182 | { |
183 | unsigned long flags; |
184 | |
185 | if (status == PRE_CHANGE) { |
186 | if (ufshcd_is_auto_hibern8_supported(hba)) { |
187 | spin_lock_irqsave(hba->host->host_lock, flags); |
188 | ufshcd_writel(hba, 0, REG_AUTO_HIBERNATE_IDLE_TIMER); |
189 | spin_unlock_irqrestore(lock: hba->host->host_lock, flags); |
190 | } |
191 | } |
192 | |
193 | return 0; |
194 | } |
195 | |
196 | static void ufs_sprd_n6_host_reset(struct ufs_hba *hba) |
197 | { |
198 | struct ufs_sprd_priv *priv = ufs_sprd_get_priv_data(hba); |
199 | |
200 | dev_info(hba->dev, "ufs host reset!\n" ); |
201 | |
202 | reset_control_assert(rstc: priv->rci[SPRD_UFSHCI_SOFT_RST].rc); |
203 | usleep_range(min: 1000, max: 1100); |
204 | reset_control_deassert(rstc: priv->rci[SPRD_UFSHCI_SOFT_RST].rc); |
205 | } |
206 | |
207 | static int ufs_sprd_n6_device_reset(struct ufs_hba *hba) |
208 | { |
209 | struct ufs_sprd_priv *priv = ufs_sprd_get_priv_data(hba); |
210 | |
211 | dev_info(hba->dev, "ufs device reset!\n" ); |
212 | |
213 | reset_control_assert(rstc: priv->rci[SPRD_UFS_DEV_RST].rc); |
214 | usleep_range(min: 1000, max: 1100); |
215 | reset_control_deassert(rstc: priv->rci[SPRD_UFS_DEV_RST].rc); |
216 | |
217 | return 0; |
218 | } |
219 | |
220 | static void ufs_sprd_n6_key_acc_enable(struct ufs_hba *hba) |
221 | { |
222 | u32 val; |
223 | u32 retry = 10; |
224 | struct arm_smccc_res res; |
225 | |
226 | check_hce: |
227 | /* Key access only can be enabled under HCE enable */ |
228 | val = ufshcd_readl(hba, REG_CONTROLLER_ENABLE); |
229 | if (!(val & CONTROLLER_ENABLE)) { |
230 | ufs_sprd_n6_host_reset(hba); |
231 | val |= CONTROLLER_ENABLE; |
232 | ufshcd_writel(hba, val, REG_CONTROLLER_ENABLE); |
233 | usleep_range(min: 1000, max: 1100); |
234 | if (retry) { |
235 | retry--; |
236 | goto check_hce; |
237 | } |
238 | goto disable_crypto; |
239 | } |
240 | |
241 | arm_smccc_smc(SPRD_SIP_SVC_STORAGE_UFS_CRYPTO_ENABLE, |
242 | 0, 0, 0, 0, 0, 0, 0, &res); |
243 | if (!res.a0) |
244 | return; |
245 | |
246 | disable_crypto: |
247 | dev_err(hba->dev, "key reg access enable fail, disable crypto\n" ); |
248 | hba->caps &= ~UFSHCD_CAP_CRYPTO; |
249 | } |
250 | |
251 | static int ufs_sprd_n6_init(struct ufs_hba *hba) |
252 | { |
253 | struct ufs_sprd_priv *priv; |
254 | int ret = 0; |
255 | |
256 | ret = ufs_sprd_common_init(hba); |
257 | if (ret != 0) |
258 | return ret; |
259 | |
260 | priv = ufs_sprd_get_priv_data(hba); |
261 | |
262 | ret = regulator_enable(regulator: priv->vregi[SPRD_UFS_VDD_MPHY].vreg); |
263 | if (ret) |
264 | return -ENODEV; |
265 | |
266 | if (hba->caps & UFSHCD_CAP_CRYPTO) |
267 | ufs_sprd_n6_key_acc_enable(hba); |
268 | |
269 | return 0; |
270 | } |
271 | |
272 | static int ufs_sprd_n6_phy_init(struct ufs_hba *hba) |
273 | { |
274 | int ret = 0; |
275 | uint32_t val = 0; |
276 | uint32_t retry = 10; |
277 | uint32_t offset; |
278 | struct ufs_sprd_priv *priv = ufs_sprd_get_priv_data(hba); |
279 | |
280 | ufshcd_dme_set(hba, UIC_ARG_MIB(CBREFCLKCTRL2), mib_val: 0x90); |
281 | ufshcd_dme_set(hba, UIC_ARG_MIB(CBCRCTRL), mib_val: 0x01); |
282 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(RXSQCONTROL, |
283 | UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), mib_val: 0x01); |
284 | ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(RXSQCONTROL, |
285 | UIC_ARG_MPHY_RX_GEN_SEL_INDEX(1)), mib_val: 0x01); |
286 | ufshcd_dme_set(hba, UIC_ARG_MIB(VS_MPHYCFGUPDT), mib_val: 0x01); |
287 | ufshcd_dme_set(hba, UIC_ARG_MIB(CBRATESEL), mib_val: 0x01); |
288 | |
289 | do { |
290 | /* phy_sram_init_done */ |
291 | ufs_sprd_regmap_read(priv, index: SPRD_UFS_ANLG, reg: 0xc, val: &val); |
292 | if ((val & 0x1) == 0x1) { |
293 | for (offset = 0x40; offset < 0x42; offset++) { |
294 | /* Lane afe calibration */ |
295 | ufshcd_dme_set(hba, UIC_ARG_MIB(CBCREGADDRLSB), mib_val: 0x1c); |
296 | ufshcd_dme_set(hba, UIC_ARG_MIB(CBCREGADDRMSB), mib_val: offset); |
297 | ufshcd_dme_set(hba, UIC_ARG_MIB(CBCREGWRLSB), mib_val: 0x04); |
298 | ufshcd_dme_set(hba, UIC_ARG_MIB(CBCREGWRMSB), mib_val: 0x00); |
299 | ufshcd_dme_set(hba, UIC_ARG_MIB(CBCREGRDWRSEL), mib_val: 0x01); |
300 | ufshcd_dme_set(hba, UIC_ARG_MIB(VS_MPHYCFGUPDT), mib_val: 0x01); |
301 | } |
302 | |
303 | goto update_phy; |
304 | } |
305 | udelay(1000); |
306 | retry--; |
307 | } while (retry > 0); |
308 | |
309 | ret = -ETIMEDOUT; |
310 | goto out; |
311 | |
312 | update_phy: |
313 | /* phy_sram_ext_ld_done */ |
314 | ufs_sprd_regmap_update(priv, index: SPRD_UFS_ANLG, reg: 0xc, bits: 0x2, val: 0); |
315 | ufshcd_dme_set(hba, UIC_ARG_MIB(VS_MPHYCFGUPDT), mib_val: 0x01); |
316 | ufshcd_dme_set(hba, UIC_ARG_MIB(VS_MPHYDISABLE), mib_val: 0x0); |
317 | out: |
318 | return ret; |
319 | } |
320 | |
321 | |
322 | static int sprd_ufs_n6_hce_enable_notify(struct ufs_hba *hba, |
323 | enum ufs_notify_change_status status) |
324 | { |
325 | int err = 0; |
326 | struct ufs_sprd_priv *priv = ufs_sprd_get_priv_data(hba); |
327 | |
328 | if (status == PRE_CHANGE) { |
329 | /* phy_sram_ext_ld_done */ |
330 | ufs_sprd_regmap_update(priv, index: SPRD_UFS_ANLG, reg: 0xc, bits: 0x2, val: 0x2); |
331 | /* phy_sram_bypass */ |
332 | ufs_sprd_regmap_update(priv, index: SPRD_UFS_ANLG, reg: 0xc, bits: 0x4, val: 0x4); |
333 | |
334 | ufs_sprd_n6_host_reset(hba); |
335 | |
336 | if (hba->caps & UFSHCD_CAP_CRYPTO) |
337 | ufs_sprd_n6_key_acc_enable(hba); |
338 | } |
339 | |
340 | if (status == POST_CHANGE) { |
341 | err = ufs_sprd_n6_phy_init(hba); |
342 | if (err) { |
343 | dev_err(hba->dev, "Phy setup failed (%d)\n" , err); |
344 | goto out; |
345 | } |
346 | |
347 | ufs_sprd_get_unipro_ver(hba); |
348 | } |
349 | out: |
350 | return err; |
351 | } |
352 | |
353 | static void sprd_ufs_n6_h8_notify(struct ufs_hba *hba, |
354 | enum uic_cmd_dme cmd, |
355 | enum ufs_notify_change_status status) |
356 | { |
357 | struct ufs_sprd_priv *priv = ufs_sprd_get_priv_data(hba); |
358 | |
359 | if (status == PRE_CHANGE) { |
360 | if (cmd == UIC_CMD_DME_HIBER_ENTER) |
361 | /* |
362 | * Disable UIC COMPL INTR to prevent access to UFSHCI after |
363 | * checking HCS.UPMCRS |
364 | */ |
365 | ufs_sprd_ctrl_uic_compl(hba, enable: false); |
366 | |
367 | if (cmd == UIC_CMD_DME_HIBER_EXIT) { |
368 | ufs_sprd_regmap_update(priv, index: SPRD_UFS_AON_APB, APB_UFSDEV_REG, |
369 | APB_UFSDEV_REFCLK_EN, APB_UFSDEV_REFCLK_EN); |
370 | ufs_sprd_regmap_update(priv, index: SPRD_UFS_AON_APB, APB_USB31PLL_CTRL, |
371 | APB_USB31PLLV_REF2MPHY, APB_USB31PLLV_REF2MPHY); |
372 | } |
373 | } |
374 | |
375 | if (status == POST_CHANGE) { |
376 | if (cmd == UIC_CMD_DME_HIBER_EXIT) |
377 | ufs_sprd_ctrl_uic_compl(hba, enable: true); |
378 | |
379 | if (cmd == UIC_CMD_DME_HIBER_ENTER) { |
380 | ufs_sprd_regmap_update(priv, index: SPRD_UFS_AON_APB, APB_UFSDEV_REG, |
381 | APB_UFSDEV_REFCLK_EN, val: 0); |
382 | ufs_sprd_regmap_update(priv, index: SPRD_UFS_AON_APB, APB_USB31PLL_CTRL, |
383 | APB_USB31PLLV_REF2MPHY, val: 0); |
384 | } |
385 | } |
386 | } |
387 | |
388 | static struct ufs_sprd_priv n6_ufs = { |
389 | .rci[SPRD_UFSHCI_SOFT_RST] = { .name = "controller" , }, |
390 | .rci[SPRD_UFS_DEV_RST] = { .name = "device" , }, |
391 | |
392 | .sysci[SPRD_UFS_ANLG] = { .name = "sprd,ufs-anlg-syscon" , }, |
393 | .sysci[SPRD_UFS_AON_APB] = { .name = "sprd,aon-apb-syscon" , }, |
394 | |
395 | .vregi[SPRD_UFS_VDD_MPHY] = { .name = "vdd-mphy" , }, |
396 | |
397 | .ufs_hba_sprd_vops = { |
398 | .name = "sprd,ums9620-ufs" , |
399 | .init = ufs_sprd_n6_init, |
400 | .hce_enable_notify = sprd_ufs_n6_hce_enable_notify, |
401 | .pwr_change_notify = sprd_ufs_pwr_change_notify, |
402 | .hibern8_notify = sprd_ufs_n6_h8_notify, |
403 | .device_reset = ufs_sprd_n6_device_reset, |
404 | .suspend = ufs_sprd_suspend, |
405 | }, |
406 | }; |
407 | |
408 | static const struct of_device_id __maybe_unused ufs_sprd_of_match[] = { |
409 | { .compatible = "sprd,ums9620-ufs" , .data = &n6_ufs.ufs_hba_sprd_vops}, |
410 | {}, |
411 | }; |
412 | MODULE_DEVICE_TABLE(of, ufs_sprd_of_match); |
413 | |
414 | static int ufs_sprd_probe(struct platform_device *pdev) |
415 | { |
416 | int err; |
417 | struct device *dev = &pdev->dev; |
418 | const struct of_device_id *of_id; |
419 | |
420 | of_id = of_match_node(matches: ufs_sprd_of_match, node: dev->of_node); |
421 | err = ufshcd_pltfrm_init(pdev, vops: of_id->data); |
422 | if (err) |
423 | dev_err(dev, "ufshcd_pltfrm_init() failed %d\n" , err); |
424 | |
425 | return err; |
426 | } |
427 | |
428 | static void ufs_sprd_remove(struct platform_device *pdev) |
429 | { |
430 | struct ufs_hba *hba = platform_get_drvdata(pdev); |
431 | |
432 | pm_runtime_get_sync(dev: &(pdev)->dev); |
433 | ufshcd_remove(hba); |
434 | } |
435 | |
436 | static const struct dev_pm_ops ufs_sprd_pm_ops = { |
437 | SET_SYSTEM_SLEEP_PM_OPS(ufshcd_system_suspend, ufshcd_system_resume) |
438 | SET_RUNTIME_PM_OPS(ufshcd_runtime_suspend, ufshcd_runtime_resume, NULL) |
439 | .prepare = ufshcd_suspend_prepare, |
440 | .complete = ufshcd_resume_complete, |
441 | }; |
442 | |
443 | static struct platform_driver ufs_sprd_pltform = { |
444 | .probe = ufs_sprd_probe, |
445 | .remove_new = ufs_sprd_remove, |
446 | .driver = { |
447 | .name = "ufshcd-sprd" , |
448 | .pm = &ufs_sprd_pm_ops, |
449 | .of_match_table = of_match_ptr(ufs_sprd_of_match), |
450 | }, |
451 | }; |
452 | module_platform_driver(ufs_sprd_pltform); |
453 | |
454 | MODULE_AUTHOR("Zhe Wang <zhe.wang1@unisoc.com>" ); |
455 | MODULE_DESCRIPTION("Unisoc UFS Host Driver" ); |
456 | MODULE_LICENSE("GPL v2" ); |
457 | |