Commit | Line | Data |
---|---|---|
97fb5e8d | 1 | // SPDX-License-Identifier: GPL-2.0-only |
81c0fc51 | 2 | /* |
54b879b7 | 3 | * Copyright (c) 2013-2016, Linux Foundation. All rights reserved. |
81c0fc51 YG |
4 | */ |
5 | ||
e1a7752c | 6 | #include <linux/acpi.h> |
3f06f780 BVA |
7 | #include <linux/clk.h> |
8 | #include <linux/delay.h> | |
be2e06c8 MS |
9 | #include <linux/devfreq.h> |
10 | #include <linux/gpio/consumer.h> | |
03ce80a1 | 11 | #include <linux/interconnect.h> |
3f06f780 | 12 | #include <linux/module.h> |
81c0fc51 | 13 | #include <linux/of.h> |
81c0fc51 | 14 | #include <linux/phy/phy.h> |
be2e06c8 | 15 | #include <linux/platform_device.h> |
12fd5f25 | 16 | #include <linux/reset-controller.h> |
be2e06c8 | 17 | #include <linux/time.h> |
4b9ad0b8 | 18 | |
56541c7c AV |
19 | #include <soc/qcom/ice.h> |
20 | ||
dd11376b | 21 | #include <ufs/ufshcd.h> |
dd11376b BVA |
22 | #include <ufs/ufshci.h> |
23 | #include <ufs/ufs_quirks.h> | |
be2e06c8 MS |
24 | #include <ufs/unipro.h> |
25 | #include "ufshcd-pltfrm.h" | |
26 | #include "ufs-qcom.h" | |
3f06f780 | 27 | |
c263b4ef AD |
28 | #define MCQ_QCFGPTR_MASK GENMASK(7, 0) |
29 | #define MCQ_QCFGPTR_UNIT 0x200 | |
30 | #define MCQ_SQATTR_OFFSET(c) \ | |
31 | ((((c) >> 16) & MCQ_QCFGPTR_MASK) * MCQ_QCFGPTR_UNIT) | |
32 | #define MCQ_QCFG_SIZE 0x40 | |
33 | ||
6e3fd44d YG |
34 | enum { |
35 | TSTBUS_UAWM, | |
36 | TSTBUS_UARM, | |
37 | TSTBUS_TXUC, | |
38 | TSTBUS_RXUC, | |
39 | TSTBUS_DFC, | |
40 | TSTBUS_TRLUT, | |
41 | TSTBUS_TMRLUT, | |
42 | TSTBUS_OCSC, | |
43 | TSTBUS_UTP_HCI, | |
44 | TSTBUS_COMBINED, | |
45 | TSTBUS_WRAPPER, | |
46 | TSTBUS_UNIPRO, | |
47 | TSTBUS_MAX, | |
48 | }; | |
81c0fc51 | 49 | |
03ce80a1 MS |
50 | #define QCOM_UFS_MAX_GEAR 4 |
51 | #define QCOM_UFS_MAX_LANE 2 | |
52 | ||
53 | enum { | |
54 | MODE_MIN, | |
55 | MODE_PWM, | |
56 | MODE_HS_RA, | |
57 | MODE_HS_RB, | |
58 | MODE_MAX, | |
59 | }; | |
60 | ||
01e74715 | 61 | static const struct __ufs_qcom_bw_table { |
03ce80a1 MS |
62 | u32 mem_bw; |
63 | u32 cfg_bw; | |
64 | } ufs_qcom_bw_table[MODE_MAX + 1][QCOM_UFS_MAX_GEAR + 1][QCOM_UFS_MAX_LANE + 1] = { | |
65 | [MODE_MIN][0][0] = { 0, 0 }, /* Bandwidth values in KB/s */ | |
66 | [MODE_PWM][UFS_PWM_G1][UFS_LANE_1] = { 922, 1000 }, | |
67 | [MODE_PWM][UFS_PWM_G2][UFS_LANE_1] = { 1844, 1000 }, | |
68 | [MODE_PWM][UFS_PWM_G3][UFS_LANE_1] = { 3688, 1000 }, | |
69 | [MODE_PWM][UFS_PWM_G4][UFS_LANE_1] = { 7376, 1000 }, | |
70 | [MODE_PWM][UFS_PWM_G1][UFS_LANE_2] = { 1844, 1000 }, | |
71 | [MODE_PWM][UFS_PWM_G2][UFS_LANE_2] = { 3688, 1000 }, | |
72 | [MODE_PWM][UFS_PWM_G3][UFS_LANE_2] = { 7376, 1000 }, | |
73 | [MODE_PWM][UFS_PWM_G4][UFS_LANE_2] = { 14752, 1000 }, | |
74 | [MODE_HS_RA][UFS_HS_G1][UFS_LANE_1] = { 127796, 1000 }, | |
75 | [MODE_HS_RA][UFS_HS_G2][UFS_LANE_1] = { 255591, 1000 }, | |
76 | [MODE_HS_RA][UFS_HS_G3][UFS_LANE_1] = { 1492582, 102400 }, | |
77 | [MODE_HS_RA][UFS_HS_G4][UFS_LANE_1] = { 2915200, 204800 }, | |
78 | [MODE_HS_RA][UFS_HS_G1][UFS_LANE_2] = { 255591, 1000 }, | |
79 | [MODE_HS_RA][UFS_HS_G2][UFS_LANE_2] = { 511181, 1000 }, | |
80 | [MODE_HS_RA][UFS_HS_G3][UFS_LANE_2] = { 1492582, 204800 }, | |
81 | [MODE_HS_RA][UFS_HS_G4][UFS_LANE_2] = { 2915200, 409600 }, | |
82 | [MODE_HS_RB][UFS_HS_G1][UFS_LANE_1] = { 149422, 1000 }, | |
83 | [MODE_HS_RB][UFS_HS_G2][UFS_LANE_1] = { 298189, 1000 }, | |
84 | [MODE_HS_RB][UFS_HS_G3][UFS_LANE_1] = { 1492582, 102400 }, | |
85 | [MODE_HS_RB][UFS_HS_G4][UFS_LANE_1] = { 2915200, 204800 }, | |
86 | [MODE_HS_RB][UFS_HS_G1][UFS_LANE_2] = { 298189, 1000 }, | |
87 | [MODE_HS_RB][UFS_HS_G2][UFS_LANE_2] = { 596378, 1000 }, | |
88 | [MODE_HS_RB][UFS_HS_G3][UFS_LANE_2] = { 1492582, 204800 }, | |
89 | [MODE_HS_RB][UFS_HS_G4][UFS_LANE_2] = { 2915200, 409600 }, | |
90 | [MODE_MAX][0][0] = { 7643136, 307200 }, | |
91 | }; | |
92 | ||
6e3fd44d | 93 | static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host); |
b4e13e1a | 94 | static int ufs_qcom_set_core_clk_ctrl(struct ufs_hba *hba, bool is_scale_up); |
f06fcc71 | 95 | |
12fd5f25 EG |
96 | static struct ufs_qcom_host *rcdev_to_ufs_host(struct reset_controller_dev *rcd) |
97 | { | |
98 | return container_of(rcd, struct ufs_qcom_host, rcdev); | |
99 | } | |
100 | ||
56541c7c AV |
101 | #ifdef CONFIG_SCSI_UFS_CRYPTO |
102 | ||
103 | static inline void ufs_qcom_ice_enable(struct ufs_qcom_host *host) | |
104 | { | |
105 | if (host->hba->caps & UFSHCD_CAP_CRYPTO) | |
106 | qcom_ice_enable(host->ice); | |
107 | } | |
108 | ||
109 | static int ufs_qcom_ice_init(struct ufs_qcom_host *host) | |
110 | { | |
111 | struct ufs_hba *hba = host->hba; | |
112 | struct device *dev = hba->dev; | |
113 | struct qcom_ice *ice; | |
114 | ||
115 | ice = of_qcom_ice_get(dev); | |
116 | if (ice == ERR_PTR(-EOPNOTSUPP)) { | |
117 | dev_warn(dev, "Disabling inline encryption support\n"); | |
118 | ice = NULL; | |
119 | } | |
120 | ||
121 | if (IS_ERR_OR_NULL(ice)) | |
122 | return PTR_ERR_OR_ZERO(ice); | |
123 | ||
124 | host->ice = ice; | |
125 | hba->caps |= UFSHCD_CAP_CRYPTO; | |
126 | ||
127 | return 0; | |
128 | } | |
129 | ||
130 | static inline int ufs_qcom_ice_resume(struct ufs_qcom_host *host) | |
131 | { | |
132 | if (host->hba->caps & UFSHCD_CAP_CRYPTO) | |
133 | return qcom_ice_resume(host->ice); | |
134 | ||
135 | return 0; | |
136 | } | |
137 | ||
138 | static inline int ufs_qcom_ice_suspend(struct ufs_qcom_host *host) | |
139 | { | |
140 | if (host->hba->caps & UFSHCD_CAP_CRYPTO) | |
141 | return qcom_ice_suspend(host->ice); | |
142 | ||
143 | return 0; | |
144 | } | |
145 | ||
146 | static int ufs_qcom_ice_program_key(struct ufs_hba *hba, | |
147 | const union ufs_crypto_cfg_entry *cfg, | |
148 | int slot) | |
149 | { | |
150 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); | |
151 | union ufs_crypto_cap_entry cap; | |
152 | bool config_enable = | |
153 | cfg->config_enable & UFS_CRYPTO_CONFIGURATION_ENABLE; | |
154 | ||
155 | /* Only AES-256-XTS has been tested so far. */ | |
156 | cap = hba->crypto_cap_array[cfg->crypto_cap_idx]; | |
157 | if (cap.algorithm_id != UFS_CRYPTO_ALG_AES_XTS || | |
158 | cap.key_size != UFS_CRYPTO_KEY_SIZE_256) | |
3bf7ab4a | 159 | return -EOPNOTSUPP; |
56541c7c AV |
160 | |
161 | if (config_enable) | |
162 | return qcom_ice_program_key(host->ice, | |
163 | QCOM_ICE_CRYPTO_ALG_AES_XTS, | |
164 | QCOM_ICE_CRYPTO_KEY_SIZE_256, | |
165 | cfg->crypto_key, | |
166 | cfg->data_unit_size, slot); | |
167 | else | |
168 | return qcom_ice_evict_key(host->ice, slot); | |
169 | } | |
170 | ||
171 | #else | |
172 | ||
173 | #define ufs_qcom_ice_program_key NULL | |
174 | ||
175 | static inline void ufs_qcom_ice_enable(struct ufs_qcom_host *host) | |
176 | { | |
177 | } | |
178 | ||
179 | static int ufs_qcom_ice_init(struct ufs_qcom_host *host) | |
180 | { | |
181 | return 0; | |
182 | } | |
183 | ||
184 | static inline int ufs_qcom_ice_resume(struct ufs_qcom_host *host) | |
185 | { | |
186 | return 0; | |
187 | } | |
188 | ||
189 | static inline int ufs_qcom_ice_suspend(struct ufs_qcom_host *host) | |
190 | { | |
191 | return 0; | |
192 | } | |
193 | #endif | |
194 | ||
81c0fc51 YG |
195 | static void ufs_qcom_disable_lane_clks(struct ufs_qcom_host *host) |
196 | { | |
197 | if (!host->is_lane_clks_enabled) | |
198 | return; | |
199 | ||
9caef856 | 200 | clk_bulk_disable_unprepare(host->num_clks, host->clks); |
81c0fc51 YG |
201 | |
202 | host->is_lane_clks_enabled = false; | |
203 | } | |
204 | ||
205 | static int ufs_qcom_enable_lane_clks(struct ufs_qcom_host *host) | |
206 | { | |
031312db | 207 | int err; |
81c0fc51 | 208 | |
9caef856 | 209 | err = clk_bulk_prepare_enable(host->num_clks, host->clks); |
81c0fc51 | 210 | if (err) |
031312db | 211 | return err; |
81c0fc51 | 212 | |
81c0fc51 | 213 | host->is_lane_clks_enabled = true; |
031312db MS |
214 | |
215 | return 0; | |
81c0fc51 YG |
216 | } |
217 | ||
218 | static int ufs_qcom_init_lane_clks(struct ufs_qcom_host *host) | |
219 | { | |
1f165c87 | 220 | int err; |
81c0fc51 YG |
221 | struct device *dev = host->hba->dev; |
222 | ||
e1a7752c LJ |
223 | if (has_acpi_companion(dev)) |
224 | return 0; | |
225 | ||
9caef856 MS |
226 | err = devm_clk_bulk_get_all(dev, &host->clks); |
227 | if (err <= 0) | |
031312db | 228 | return err; |
81c0fc51 | 229 | |
9caef856 | 230 | host->num_clks = err; |
031312db MS |
231 | |
232 | return 0; | |
81c0fc51 YG |
233 | } |
234 | ||
81c0fc51 YG |
235 | static int ufs_qcom_check_hibern8(struct ufs_hba *hba) |
236 | { | |
237 | int err; | |
1f165c87 | 238 | u32 tx_fsm_val; |
81c0fc51 YG |
239 | unsigned long timeout = jiffies + msecs_to_jiffies(HBRN8_POLL_TOUT_MS); |
240 | ||
241 | do { | |
242 | err = ufshcd_dme_get(hba, | |
f06fcc71 YG |
243 | UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, |
244 | UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), | |
245 | &tx_fsm_val); | |
81c0fc51 YG |
246 | if (err || tx_fsm_val == TX_FSM_HIBERN8) |
247 | break; | |
248 | ||
249 | /* sleep for max. 200us */ | |
250 | usleep_range(100, 200); | |
251 | } while (time_before(jiffies, timeout)); | |
252 | ||
253 | /* | |
254 | * we might have scheduled out for long during polling so | |
255 | * check the state again. | |
256 | */ | |
257 | if (time_after(jiffies, timeout)) | |
258 | err = ufshcd_dme_get(hba, | |
f06fcc71 YG |
259 | UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, |
260 | UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), | |
261 | &tx_fsm_val); | |
81c0fc51 YG |
262 | |
263 | if (err) { | |
264 | dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n", | |
265 | __func__, err); | |
266 | } else if (tx_fsm_val != TX_FSM_HIBERN8) { | |
267 | err = tx_fsm_val; | |
268 | dev_err(hba->dev, "%s: invalid TX_FSM_STATE = %d\n", | |
269 | __func__, err); | |
270 | } | |
271 | ||
272 | return err; | |
273 | } | |
274 | ||
f06fcc71 YG |
275 | static void ufs_qcom_select_unipro_mode(struct ufs_qcom_host *host) |
276 | { | |
104cd58d | 277 | ufshcd_rmwl(host->hba, QUNIPRO_SEL, QUNIPRO_SEL, REG_UFS_CFG1); |
9c02aa24 | 278 | |
c422fbd5 | 279 | if (host->hw_ver.major >= 0x05) |
9c02aa24 AV |
280 | ufshcd_rmwl(host->hba, QUNIPRO_G4_SEL, 0, REG_UFS_CFG0); |
281 | ||
f06fcc71 YG |
282 | /* make sure above configuration is applied before we return */ |
283 | mb(); | |
284 | } | |
285 | ||
bc5b6816 | 286 | /* |
870b1279 CG |
287 | * ufs_qcom_host_reset - reset host controller and PHY |
288 | */ | |
289 | static int ufs_qcom_host_reset(struct ufs_hba *hba) | |
290 | { | |
1f165c87 | 291 | int ret; |
870b1279 | 292 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
1f165c87 | 293 | bool reenable_intr; |
870b1279 | 294 | |
d42d3686 | 295 | if (!host->core_reset) |
031312db | 296 | return 0; |
870b1279 | 297 | |
4a791574 | 298 | reenable_intr = hba->is_irq_enabled; |
0ae7a027 | 299 | ufshcd_disable_irq(hba); |
4a791574 | 300 | |
870b1279 CG |
301 | ret = reset_control_assert(host->core_reset); |
302 | if (ret) { | |
303 | dev_err(hba->dev, "%s: core_reset assert failed, err = %d\n", | |
304 | __func__, ret); | |
031312db | 305 | return ret; |
870b1279 CG |
306 | } |
307 | ||
308 | /* | |
309 | * The hardware requirement for delay between assert/deassert | |
310 | * is at least 3-4 sleep clock (32.7KHz) cycles, which comes to | |
311 | * ~125us (4/32768). To be on the safe side add 200us delay. | |
312 | */ | |
313 | usleep_range(200, 210); | |
314 | ||
315 | ret = reset_control_deassert(host->core_reset); | |
d1195471 | 316 | if (ret) { |
870b1279 CG |
317 | dev_err(hba->dev, "%s: core_reset deassert failed, err = %d\n", |
318 | __func__, ret); | |
d1195471 MS |
319 | return ret; |
320 | } | |
870b1279 CG |
321 | |
322 | usleep_range(1000, 1100); | |
323 | ||
0ae7a027 MS |
324 | if (reenable_intr) |
325 | ufshcd_enable_irq(hba); | |
4a791574 | 326 | |
031312db | 327 | return 0; |
870b1279 CG |
328 | } |
329 | ||
c2709865 MS |
330 | static u32 ufs_qcom_get_hs_gear(struct ufs_hba *hba) |
331 | { | |
332 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); | |
333 | ||
104cd58d | 334 | if (host->hw_ver.major >= 0x4) |
2c407fe9 | 335 | return UFS_QCOM_MAX_GEAR(ufshcd_readl(hba, REG_UFS_PARAM0)); |
c2709865 MS |
336 | |
337 | /* Default is HS-G3 */ | |
338 | return UFS_HS_G3; | |
339 | } | |
340 | ||
81c0fc51 YG |
341 | static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) |
342 | { | |
1ce5898a | 343 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
9d8528a8 | 344 | struct ufs_host_params *host_params = &host->host_params; |
81c0fc51 | 345 | struct phy *phy = host->generic_phy; |
9d8528a8 | 346 | enum phy_mode mode; |
031312db | 347 | int ret; |
81c0fc51 | 348 | |
9d8528a8 CG |
349 | /* |
350 | * HW ver 5 can only support up to HS-G5 Rate-A due to HW limitations. | |
351 | * If the HS-G5 PHY gear is used, update host_params->hs_rate to Rate-A, | |
352 | * so that the subsequent power mode change shall stick to Rate-A. | |
353 | */ | |
354 | if (host->hw_ver.major == 0x5) { | |
355 | if (host->phy_gear == UFS_HS_G5) | |
356 | host_params->hs_rate = PA_HS_MODE_A; | |
357 | else | |
358 | host_params->hs_rate = PA_HS_MODE_B; | |
359 | } | |
360 | ||
361 | mode = host_params->hs_rate == PA_HS_MODE_B ? PHY_MODE_UFS_HS_B : PHY_MODE_UFS_HS_A; | |
362 | ||
870b1279 CG |
363 | /* Reset UFS Host Controller and PHY */ |
364 | ret = ufs_qcom_host_reset(hba); | |
365 | if (ret) | |
d1195471 | 366 | return ret; |
870b1279 | 367 | |
052553af VG |
368 | /* phy initialization - calibrate the phy */ |
369 | ret = phy_init(phy); | |
81c0fc51 | 370 | if (ret) { |
052553af | 371 | dev_err(hba->dev, "%s: phy init failed, ret = %d\n", |
4b9ad0b8 | 372 | __func__, ret); |
031312db | 373 | return ret; |
81c0fc51 YG |
374 | } |
375 | ||
a68abdad CG |
376 | ret = phy_set_mode_ext(phy, mode, host->phy_gear); |
377 | if (ret) | |
378 | goto out_disable_phy; | |
baf5ddac | 379 | |
052553af VG |
380 | /* power on phy - start serdes and phy's power and clocks */ |
381 | ret = phy_power_on(phy); | |
81c0fc51 | 382 | if (ret) { |
052553af | 383 | dev_err(hba->dev, "%s: phy power on failed, ret = %d\n", |
81c0fc51 | 384 | __func__, ret); |
052553af | 385 | goto out_disable_phy; |
81c0fc51 YG |
386 | } |
387 | ||
f06fcc71 YG |
388 | ufs_qcom_select_unipro_mode(host); |
389 | ||
052553af VG |
390 | return 0; |
391 | ||
392 | out_disable_phy: | |
052553af | 393 | phy_exit(phy); |
031312db | 394 | |
81c0fc51 YG |
395 | return ret; |
396 | } | |
397 | ||
398 | /* | |
399 | * The UTP controller has a number of internal clock gating cells (CGCs). | |
400 | * Internal hardware sub-modules within the UTP controller control the CGCs. | |
401 | * Hardware CGCs disable the clock to inactivate UTP sub-modules not involved | |
402 | * in a specific operation, UTP controller CGCs are by default disabled and | |
403 | * this function enables them (after every UFS link startup) to save some power | |
404 | * leakage. | |
405 | */ | |
406 | static void ufs_qcom_enable_hw_clk_gating(struct ufs_hba *hba) | |
407 | { | |
0e9f4375 MS |
408 | ufshcd_rmwl(hba, REG_UFS_CFG2_CGC_EN_ALL, REG_UFS_CFG2_CGC_EN_ALL, |
409 | REG_UFS_CFG2); | |
81c0fc51 YG |
410 | |
411 | /* Ensure that HW clock gating is enabled before next operations */ | |
412 | mb(); | |
413 | } | |
414 | ||
f06fcc71 YG |
415 | static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba, |
416 | enum ufs_notify_change_status status) | |
81c0fc51 | 417 | { |
1ce5898a | 418 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
1f165c87 | 419 | int err; |
81c0fc51 YG |
420 | |
421 | switch (status) { | |
422 | case PRE_CHANGE: | |
e430c0e0 MS |
423 | err = ufs_qcom_power_up_sequence(hba); |
424 | if (err) | |
425 | return err; | |
426 | ||
81c0fc51 YG |
427 | /* |
428 | * The PHY PLL output is the source of tx/rx lane symbol | |
429 | * clocks, hence, enable the lane clocks only after PHY | |
430 | * is initialized. | |
431 | */ | |
432 | err = ufs_qcom_enable_lane_clks(host); | |
433 | break; | |
434 | case POST_CHANGE: | |
435 | /* check if UFS PHY moved from DISABLED to HIBERN8 */ | |
436 | err = ufs_qcom_check_hibern8(hba); | |
437 | ufs_qcom_enable_hw_clk_gating(hba); | |
df4ec2fa | 438 | ufs_qcom_ice_enable(host); |
81c0fc51 YG |
439 | break; |
440 | default: | |
441 | dev_err(hba->dev, "%s: invalid status %d\n", __func__, status); | |
442 | err = -EINVAL; | |
443 | break; | |
444 | } | |
445 | return err; | |
446 | } | |
447 | ||
fd915c67 NR |
448 | /** |
449 | * ufs_qcom_cfg_timers - Configure ufs qcom cfg timers | |
450 | * | |
451 | * @hba: host controller instance | |
452 | * @gear: Current operating gear | |
453 | * @hs: current power mode | |
454 | * @rate: current operating rate (A or B) | |
455 | * @update_link_startup_timer: indicate if link_start ongoing | |
456 | * @is_pre_scale_up: flag to check if pre scale up condition. | |
3a17fefe | 457 | * Return: zero for success and non-zero in case of a failure. |
81c0fc51 | 458 | */ |
f06fcc71 | 459 | static int ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, |
fd915c67 NR |
460 | u32 hs, u32 rate, bool update_link_startup_timer, |
461 | bool is_pre_scale_up) | |
81c0fc51 | 462 | { |
1ce5898a | 463 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
81c0fc51 | 464 | struct ufs_clk_info *clki; |
81c0fc51 | 465 | unsigned long core_clk_rate = 0; |
1f165c87 | 466 | u32 core_clk_cycles_per_us; |
81c0fc51 | 467 | |
81c7e06a | 468 | /* |
104cd58d | 469 | * UTP controller uses SYS1CLK_1US_REG register for Interrupt |
81c7e06a | 470 | * Aggregation logic. |
fd915c67 NR |
471 | * It is mandatory to write SYS1CLK_1US_REG register on UFS host |
472 | * controller V4.0.0 onwards. | |
473 | */ | |
104cd58d | 474 | if (host->hw_ver.major < 4 && !ufshcd_is_intr_aggr_allowed(hba)) |
031312db | 475 | return 0; |
81c7e06a | 476 | |
81c0fc51 YG |
477 | if (gear == 0) { |
478 | dev_err(hba->dev, "%s: invalid gear = %d\n", __func__, gear); | |
031312db | 479 | return -EINVAL; |
81c0fc51 YG |
480 | } |
481 | ||
482 | list_for_each_entry(clki, &hba->clk_list_head, list) { | |
fd915c67 NR |
483 | if (!strcmp(clki->name, "core_clk")) { |
484 | if (is_pre_scale_up) | |
485 | core_clk_rate = clki->max_freq; | |
486 | else | |
487 | core_clk_rate = clk_get_rate(clki->clk); | |
488 | break; | |
489 | } | |
490 | ||
81c0fc51 YG |
491 | } |
492 | ||
493 | /* If frequency is smaller than 1MHz, set to 1MHz */ | |
494 | if (core_clk_rate < DEFAULT_CLK_RATE_HZ) | |
495 | core_clk_rate = DEFAULT_CLK_RATE_HZ; | |
496 | ||
497 | core_clk_cycles_per_us = core_clk_rate / USEC_PER_SEC; | |
f06fcc71 YG |
498 | if (ufshcd_readl(hba, REG_UFS_SYS1CLK_1US) != core_clk_cycles_per_us) { |
499 | ufshcd_writel(hba, core_clk_cycles_per_us, REG_UFS_SYS1CLK_1US); | |
500 | /* | |
501 | * make sure above write gets applied before we return from | |
502 | * this function. | |
503 | */ | |
504 | mb(); | |
505 | } | |
506 | ||
031312db | 507 | return 0; |
81c0fc51 YG |
508 | } |
509 | ||
f06fcc71 YG |
510 | static int ufs_qcom_link_startup_notify(struct ufs_hba *hba, |
511 | enum ufs_notify_change_status status) | |
81c0fc51 | 512 | { |
f06fcc71 | 513 | int err = 0; |
81c0fc51 YG |
514 | |
515 | switch (status) { | |
516 | case PRE_CHANGE: | |
f06fcc71 | 517 | if (ufs_qcom_cfg_timers(hba, UFS_PWM_G1, SLOWAUTO_MODE, |
fd915c67 | 518 | 0, true, false)) { |
81c0fc51 YG |
519 | dev_err(hba->dev, "%s: ufs_qcom_cfg_timers() failed\n", |
520 | __func__); | |
031312db | 521 | return -EINVAL; |
81c0fc51 | 522 | } |
f06fcc71 | 523 | |
104cd58d MS |
524 | err = ufs_qcom_set_core_clk_ctrl(hba, true); |
525 | if (err) | |
526 | dev_err(hba->dev, "cfg core clk ctrl failed\n"); | |
4b9ad0b8 YG |
527 | /* |
528 | * Some UFS devices (and may be host) have issues if LCC is | |
529 | * enabled. So we are setting PA_Local_TX_LCC_Enable to 0 | |
530 | * before link startup which will make sure that both host | |
531 | * and device TX LCC are disabled once link startup is | |
532 | * completed. | |
533 | */ | |
534 | if (ufshcd_get_local_unipro_ver(hba) != UFS_UNIPRO_VER_1_41) | |
984eaac1 | 535 | err = ufshcd_disable_host_tx_lcc(hba); |
4b9ad0b8 | 536 | |
81c0fc51 YG |
537 | break; |
538 | default: | |
539 | break; | |
540 | } | |
541 | ||
f06fcc71 | 542 | return err; |
81c0fc51 YG |
543 | } |
544 | ||
b61d0414 ZC |
545 | static void ufs_qcom_device_reset_ctrl(struct ufs_hba *hba, bool asserted) |
546 | { | |
547 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); | |
548 | ||
549 | /* reset gpio is optional */ | |
550 | if (!host->device_reset) | |
551 | return; | |
552 | ||
553 | gpiod_set_value_cansleep(host->device_reset, asserted); | |
554 | } | |
555 | ||
9561f584 PW |
556 | static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op, |
557 | enum ufs_notify_change_status status) | |
81c0fc51 | 558 | { |
1ce5898a | 559 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
81c0fc51 | 560 | struct phy *phy = host->generic_phy; |
81c0fc51 | 561 | |
9561f584 PW |
562 | if (status == PRE_CHANGE) |
563 | return 0; | |
564 | ||
81c0fc51 YG |
565 | if (ufs_qcom_is_link_off(hba)) { |
566 | /* | |
567 | * Disable the tx/rx lane symbol clocks before PHY is | |
568 | * powered down as the PLL source should be disabled | |
569 | * after downstream clocks are disabled. | |
570 | */ | |
571 | ufs_qcom_disable_lane_clks(host); | |
572 | phy_power_off(phy); | |
81c0fc51 | 573 | |
b61d0414 ZC |
574 | /* reset the connected UFS device during power down */ |
575 | ufs_qcom_device_reset_ctrl(hba, true); | |
576 | ||
3f6d1767 | 577 | } else if (!ufs_qcom_is_link_active(hba)) { |
f06fcc71 | 578 | ufs_qcom_disable_lane_clks(host); |
f06fcc71 | 579 | } |
81c0fc51 | 580 | |
56541c7c | 581 | return ufs_qcom_ice_suspend(host); |
81c0fc51 YG |
582 | } |
583 | ||
584 | static int ufs_qcom_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) | |
585 | { | |
1ce5898a | 586 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
81c0fc51 YG |
587 | struct phy *phy = host->generic_phy; |
588 | int err; | |
589 | ||
3f6d1767 EG |
590 | if (ufs_qcom_is_link_off(hba)) { |
591 | err = phy_power_on(phy); | |
592 | if (err) { | |
593 | dev_err(hba->dev, "%s: failed PHY power on: %d\n", | |
594 | __func__, err); | |
595 | return err; | |
596 | } | |
81c0fc51 | 597 | |
3f6d1767 EG |
598 | err = ufs_qcom_enable_lane_clks(host); |
599 | if (err) | |
600 | return err; | |
f06fcc71 | 601 | |
3f6d1767 EG |
602 | } else if (!ufs_qcom_is_link_active(hba)) { |
603 | err = ufs_qcom_enable_lane_clks(host); | |
604 | if (err) | |
605 | return err; | |
606 | } | |
81c0fc51 | 607 | |
bee40dc1 | 608 | return ufs_qcom_ice_resume(host); |
81c0fc51 YG |
609 | } |
610 | ||
f06fcc71 YG |
611 | static void ufs_qcom_dev_ref_clk_ctrl(struct ufs_qcom_host *host, bool enable) |
612 | { | |
613 | if (host->dev_ref_clk_ctrl_mmio && | |
614 | (enable ^ host->is_dev_ref_clk_enabled)) { | |
615 | u32 temp = readl_relaxed(host->dev_ref_clk_ctrl_mmio); | |
616 | ||
617 | if (enable) | |
618 | temp |= host->dev_ref_clk_en_mask; | |
619 | else | |
620 | temp &= ~host->dev_ref_clk_en_mask; | |
621 | ||
622 | /* | |
623 | * If we are here to disable this clock it might be immediately | |
624 | * after entering into hibern8 in which case we need to make | |
1cbadd0c | 625 | * sure that device ref_clk is active for specific time after |
f06fcc71 YG |
626 | * hibern8 enter. |
627 | */ | |
1cbadd0c CG |
628 | if (!enable) { |
629 | unsigned long gating_wait; | |
630 | ||
631 | gating_wait = host->hba->dev_info.clk_gating_wait_us; | |
632 | if (!gating_wait) { | |
633 | udelay(1); | |
634 | } else { | |
635 | /* | |
636 | * bRefClkGatingWaitTime defines the minimum | |
637 | * time for which the reference clock is | |
638 | * required by device during transition from | |
639 | * HS-MODE to LS-MODE or HIBERN8 state. Give it | |
640 | * more delay to be on the safe side. | |
641 | */ | |
642 | gating_wait += 10; | |
643 | usleep_range(gating_wait, gating_wait + 10); | |
644 | } | |
645 | } | |
f06fcc71 YG |
646 | |
647 | writel_relaxed(temp, host->dev_ref_clk_ctrl_mmio); | |
648 | ||
8eecddfc MS |
649 | /* |
650 | * Make sure the write to ref_clk reaches the destination and | |
651 | * not stored in a Write Buffer (WB). | |
652 | */ | |
653 | readl(host->dev_ref_clk_ctrl_mmio); | |
f06fcc71 YG |
654 | |
655 | /* | |
656 | * If we call hibern8 exit after this, we need to make sure that | |
657 | * device ref_clk is stable for at least 1us before the hibern8 | |
658 | * exit command. | |
659 | */ | |
660 | if (enable) | |
661 | udelay(1); | |
662 | ||
663 | host->is_dev_ref_clk_enabled = enable; | |
664 | } | |
665 | } | |
666 | ||
03ce80a1 MS |
667 | static int ufs_qcom_icc_set_bw(struct ufs_qcom_host *host, u32 mem_bw, u32 cfg_bw) |
668 | { | |
669 | struct device *dev = host->hba->dev; | |
670 | int ret; | |
671 | ||
672 | ret = icc_set_bw(host->icc_ddr, 0, mem_bw); | |
673 | if (ret < 0) { | |
674 | dev_err(dev, "failed to set bandwidth request: %d\n", ret); | |
675 | return ret; | |
676 | } | |
677 | ||
678 | ret = icc_set_bw(host->icc_cpu, 0, cfg_bw); | |
679 | if (ret < 0) { | |
680 | dev_err(dev, "failed to set bandwidth request: %d\n", ret); | |
681 | return ret; | |
682 | } | |
683 | ||
684 | return 0; | |
685 | } | |
686 | ||
687 | static struct __ufs_qcom_bw_table ufs_qcom_get_bw_table(struct ufs_qcom_host *host) | |
688 | { | |
689 | struct ufs_pa_layer_attr *p = &host->dev_req_params; | |
690 | int gear = max_t(u32, p->gear_rx, p->gear_tx); | |
691 | int lane = max_t(u32, p->lane_rx, p->lane_tx); | |
692 | ||
693 | if (ufshcd_is_hs_mode(p)) { | |
694 | if (p->hs_rate == PA_HS_MODE_B) | |
695 | return ufs_qcom_bw_table[MODE_HS_RB][gear][lane]; | |
696 | else | |
697 | return ufs_qcom_bw_table[MODE_HS_RA][gear][lane]; | |
698 | } else { | |
699 | return ufs_qcom_bw_table[MODE_PWM][gear][lane]; | |
700 | } | |
701 | } | |
702 | ||
703 | static int ufs_qcom_icc_update_bw(struct ufs_qcom_host *host) | |
704 | { | |
705 | struct __ufs_qcom_bw_table bw_table; | |
706 | ||
707 | bw_table = ufs_qcom_get_bw_table(host); | |
708 | ||
709 | return ufs_qcom_icc_set_bw(host, bw_table.mem_bw, bw_table.cfg_bw); | |
710 | } | |
711 | ||
81c0fc51 | 712 | static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, |
f06fcc71 | 713 | enum ufs_notify_change_status status, |
81c0fc51 YG |
714 | struct ufs_pa_layer_attr *dev_max_params, |
715 | struct ufs_pa_layer_attr *dev_req_params) | |
716 | { | |
1ce5898a | 717 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
55820a7f | 718 | struct ufs_host_params *host_params = &host->host_params; |
81c0fc51 | 719 | int ret = 0; |
81c0fc51 | 720 | |
031312db MS |
721 | if (!dev_req_params) { |
722 | pr_err("%s: incoming dev_req_params is NULL\n", __func__); | |
723 | return -EINVAL; | |
724 | } | |
725 | ||
81c0fc51 YG |
726 | switch (status) { |
727 | case PRE_CHANGE: | |
55820a7f | 728 | ret = ufshcd_negotiate_pwr_params(host_params, dev_max_params, dev_req_params); |
81c0fc51 | 729 | if (ret) { |
1026f7d3 | 730 | dev_err(hba->dev, "%s: failed to determine capabilities\n", |
81c0fc51 | 731 | __func__); |
031312db | 732 | return ret; |
81c0fc51 YG |
733 | } |
734 | ||
fc88ca19 | 735 | /* |
743e1f59 CG |
736 | * During UFS driver probe, always update the PHY gear to match the negotiated |
737 | * gear, so that, if quirk UFSHCD_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH is enabled, | |
738 | * the second init can program the optimal PHY settings. This allows one to start | |
739 | * the first init with either the minimum or the maximum support gear. | |
fc88ca19 | 740 | */ |
743e1f59 | 741 | if (hba->ufshcd_state == UFSHCD_STATE_RESET) |
5a738cfe | 742 | host->phy_gear = dev_req_params->gear_tx; |
baf5ddac | 743 | |
f37aabcf YG |
744 | /* enable the device ref clock before changing to HS mode */ |
745 | if (!ufshcd_is_hs_mode(&hba->pwr_info) && | |
746 | ufshcd_is_hs_mode(dev_req_params)) | |
747 | ufs_qcom_dev_ref_clk_ctrl(host, true); | |
518b32f1 CG |
748 | |
749 | if (host->hw_ver.major >= 0x4) { | |
d9fa1e73 SC |
750 | ufshcd_dme_configure_adapt(hba, |
751 | dev_req_params->gear_tx, | |
752 | PA_INITIAL_ADAPT); | |
518b32f1 | 753 | } |
81c0fc51 YG |
754 | break; |
755 | case POST_CHANGE: | |
f06fcc71 | 756 | if (ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx, |
81c0fc51 | 757 | dev_req_params->pwr_rx, |
fd915c67 | 758 | dev_req_params->hs_rate, false, false)) { |
81c0fc51 YG |
759 | dev_err(hba->dev, "%s: ufs_qcom_cfg_timers() failed\n", |
760 | __func__); | |
761 | /* | |
762 | * we return error code at the end of the routine, | |
763 | * but continue to configure UFS_PHY_TX_LANE_ENABLE | |
764 | * and bus voting as usual | |
765 | */ | |
766 | ret = -EINVAL; | |
767 | } | |
768 | ||
81c0fc51 YG |
769 | /* cache the power mode parameters to use internally */ |
770 | memcpy(&host->dev_req_params, | |
771 | dev_req_params, sizeof(*dev_req_params)); | |
f37aabcf | 772 | |
03ce80a1 MS |
773 | ufs_qcom_icc_update_bw(host); |
774 | ||
f37aabcf YG |
775 | /* disable the device ref clock if entered PWM mode */ |
776 | if (ufshcd_is_hs_mode(&hba->pwr_info) && | |
777 | !ufshcd_is_hs_mode(dev_req_params)) | |
778 | ufs_qcom_dev_ref_clk_ctrl(host, false); | |
81c0fc51 YG |
779 | break; |
780 | default: | |
781 | ret = -EINVAL; | |
782 | break; | |
783 | } | |
031312db | 784 | |
81c0fc51 YG |
785 | return ret; |
786 | } | |
787 | ||
56d4a186 SJ |
788 | static int ufs_qcom_quirk_host_pa_saveconfigtime(struct ufs_hba *hba) |
789 | { | |
790 | int err; | |
791 | u32 pa_vs_config_reg1; | |
792 | ||
793 | err = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_VS_CONFIG_REG1), | |
794 | &pa_vs_config_reg1); | |
795 | if (err) | |
031312db | 796 | return err; |
56d4a186 SJ |
797 | |
798 | /* Allow extension of MSB bits of PA_SaveConfigTime attribute */ | |
031312db | 799 | return ufshcd_dme_set(hba, UIC_ARG_MIB(PA_VS_CONFIG_REG1), |
56d4a186 | 800 | (pa_vs_config_reg1 | (1 << 12))); |
56d4a186 SJ |
801 | } |
802 | ||
803 | static int ufs_qcom_apply_dev_quirks(struct ufs_hba *hba) | |
804 | { | |
805 | int err = 0; | |
806 | ||
807 | if (hba->dev_quirks & UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME) | |
808 | err = ufs_qcom_quirk_host_pa_saveconfigtime(hba); | |
809 | ||
27ff2c60 CG |
810 | if (hba->dev_info.wmanufacturerid == UFS_VENDOR_WDC) |
811 | hba->dev_quirks |= UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE; | |
812 | ||
56d4a186 SJ |
813 | return err; |
814 | } | |
815 | ||
ae977587 YG |
816 | static u32 ufs_qcom_get_ufs_hci_version(struct ufs_hba *hba) |
817 | { | |
104cd58d | 818 | return ufshci_version(2, 0); |
ae977587 YG |
819 | } |
820 | ||
81c0fc51 YG |
821 | /** |
822 | * ufs_qcom_advertise_quirks - advertise the known QCOM UFS controller quirks | |
823 | * @hba: host controller instance | |
824 | * | |
825 | * QCOM UFS host controller might have some non standard behaviours (quirks) | |
826 | * than what is specified by UFSHCI specification. Advertise all such | |
827 | * quirks to standard UFS host controller driver so standard takes them into | |
828 | * account. | |
829 | */ | |
830 | static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) | |
831 | { | |
1ce5898a | 832 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
81c0fc51 | 833 | |
104cd58d | 834 | if (host->hw_ver.major == 0x2) |
ae977587 | 835 | hba->quirks |= UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION; |
81c0fc51 | 836 | |
baf5ddac MS |
837 | if (host->hw_ver.major > 0x3) |
838 | hba->quirks |= UFSHCD_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH; | |
cad2e03d | 839 | } |
f06fcc71 | 840 | |
0bd3cb89 CG |
841 | static void ufs_qcom_set_phy_gear(struct ufs_qcom_host *host) |
842 | { | |
843 | struct ufs_host_params *host_params = &host->host_params; | |
dc7c948d | 844 | u32 val, dev_major; |
81c7e06a | 845 | |
0bd3cb89 | 846 | host->phy_gear = host_params->hs_tx_gear; |
2f018378 | 847 | |
dc7c948d BN |
848 | if (host->hw_ver.major < 0x4) { |
849 | /* | |
850 | * For controllers whose major HW version is < 4, power up the | |
851 | * PHY using minimum supported gear (UFS_HS_G2). Switching to | |
852 | * max gear will be performed during reinit if supported. | |
853 | * For newer controllers, whose major HW version is >= 4, power | |
854 | * up the PHY using max supported gear. | |
855 | */ | |
0bd3cb89 | 856 | host->phy_gear = UFS_HS_G2; |
dc7c948d BN |
857 | } else if (host->hw_ver.major >= 0x5) { |
858 | val = ufshcd_readl(host->hba, REG_UFS_DEBUG_SPARE_CFG); | |
859 | dev_major = FIELD_GET(UFS_DEV_VER_MAJOR_MASK, val); | |
baf5ddac | 860 | |
dc7c948d BN |
861 | /* |
862 | * Since the UFS device version is populated, let's remove the | |
863 | * REINIT quirk as the negotiated gear won't change during boot. | |
864 | * So there is no need to do reinit. | |
865 | */ | |
866 | if (dev_major != 0x0) | |
867 | host->hba->quirks &= ~UFSHCD_QUIRK_REINIT_AFTER_MAX_GEAR_SWITCH; | |
868 | ||
869 | /* | |
870 | * For UFS 3.1 device and older, power up the PHY using HS-G4 | |
871 | * PHY gear to save power. | |
872 | */ | |
873 | if (dev_major > 0x0 && dev_major < 0x4) | |
874 | host->phy_gear = UFS_HS_G4; | |
875 | } | |
cad2e03d YG |
876 | } |
877 | ||
55820a7f | 878 | static void ufs_qcom_set_host_params(struct ufs_hba *hba) |
cad2e03d | 879 | { |
1ce5898a | 880 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
55820a7f CG |
881 | struct ufs_host_params *host_params = &host->host_params; |
882 | ||
883 | ufshcd_init_host_params(host_params); | |
cad2e03d | 884 | |
55820a7f CG |
885 | /* This driver only supports symmetic gear setting i.e., hs_tx_gear == hs_rx_gear */ |
886 | host_params->hs_tx_gear = host_params->hs_rx_gear = ufs_qcom_get_hs_gear(hba); | |
887 | } | |
888 | ||
cad2e03d YG |
889 | static void ufs_qcom_set_caps(struct ufs_hba *hba) |
890 | { | |
f06fcc71 | 891 | hba->caps |= UFSHCD_CAP_CLK_GATING | UFSHCD_CAP_HIBERN8_WITH_CLK_GATING; |
87bd0501 | 892 | hba->caps |= UFSHCD_CAP_CLK_SCALING | UFSHCD_CAP_WB_WITH_CLK_SCALING; |
f06fcc71 | 893 | hba->caps |= UFSHCD_CAP_AUTO_BKOPS_SUSPEND; |
04ee8a01 | 894 | hba->caps |= UFSHCD_CAP_WB_EN; |
61906fd4 | 895 | hba->caps |= UFSHCD_CAP_AGGR_POWER_COLLAPSE; |
6f21d927 | 896 | hba->caps |= UFSHCD_CAP_RPM_AUTOSUSPEND; |
81c0fc51 YG |
897 | } |
898 | ||
f06fcc71 YG |
899 | /** |
900 | * ufs_qcom_setup_clocks - enables/disable clocks | |
901 | * @hba: host controller instance | |
902 | * @on: If true, enable clocks else disable them. | |
1e879e8f | 903 | * @status: PRE_CHANGE or POST_CHANGE notify |
f06fcc71 | 904 | * |
3a17fefe | 905 | * Return: 0 on success, non-zero on failure. |
f06fcc71 | 906 | */ |
1e879e8f SJ |
907 | static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on, |
908 | enum ufs_notify_change_status status) | |
81c0fc51 | 909 | { |
1ce5898a | 910 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
81c0fc51 YG |
911 | |
912 | /* | |
913 | * In case ufs_qcom_init() is not yet done, simply ignore. | |
914 | * This ufs_qcom_setup_clocks() shall be called from | |
915 | * ufs_qcom_init() after init is done. | |
916 | */ | |
917 | if (!host) | |
918 | return 0; | |
919 | ||
8240dd97 CG |
920 | switch (status) { |
921 | case PRE_CHANGE: | |
03ce80a1 MS |
922 | if (on) { |
923 | ufs_qcom_icc_update_bw(host); | |
924 | } else { | |
8240dd97 CG |
925 | if (!ufs_qcom_is_link_active(hba)) { |
926 | /* disable device ref_clk */ | |
927 | ufs_qcom_dev_ref_clk_ctrl(host, false); | |
928 | } | |
feb3d798 | 929 | } |
8240dd97 CG |
930 | break; |
931 | case POST_CHANGE: | |
932 | if (on) { | |
933 | /* enable the device ref clock for HS mode*/ | |
934 | if (ufshcd_is_hs_mode(&hba->pwr_info)) | |
935 | ufs_qcom_dev_ref_clk_ctrl(host, true); | |
03ce80a1 MS |
936 | } else { |
937 | ufs_qcom_icc_set_bw(host, ufs_qcom_bw_table[MODE_MIN][0][0].mem_bw, | |
938 | ufs_qcom_bw_table[MODE_MIN][0][0].cfg_bw); | |
8240dd97 CG |
939 | } |
940 | break; | |
81c0fc51 YG |
941 | } |
942 | ||
c4adf171 | 943 | return 0; |
81c0fc51 YG |
944 | } |
945 | ||
12fd5f25 EG |
946 | static int |
947 | ufs_qcom_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) | |
948 | { | |
949 | struct ufs_qcom_host *host = rcdev_to_ufs_host(rcdev); | |
950 | ||
12fd5f25 EG |
951 | ufs_qcom_assert_reset(host->hba); |
952 | /* provide 1ms delay to let the reset pulse propagate. */ | |
953 | usleep_range(1000, 1100); | |
954 | return 0; | |
955 | } | |
956 | ||
957 | static int | |
958 | ufs_qcom_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) | |
959 | { | |
960 | struct ufs_qcom_host *host = rcdev_to_ufs_host(rcdev); | |
961 | ||
12fd5f25 EG |
962 | ufs_qcom_deassert_reset(host->hba); |
963 | ||
964 | /* | |
965 | * after reset deassertion, phy will need all ref clocks, | |
966 | * voltage, current to settle down before starting serdes. | |
967 | */ | |
968 | usleep_range(1000, 1100); | |
969 | return 0; | |
970 | } | |
971 | ||
972 | static const struct reset_control_ops ufs_qcom_reset_ops = { | |
973 | .assert = ufs_qcom_reset_assert, | |
974 | .deassert = ufs_qcom_reset_deassert, | |
975 | }; | |
976 | ||
03ce80a1 MS |
977 | static int ufs_qcom_icc_init(struct ufs_qcom_host *host) |
978 | { | |
979 | struct device *dev = host->hba->dev; | |
980 | int ret; | |
981 | ||
982 | host->icc_ddr = devm_of_icc_get(dev, "ufs-ddr"); | |
983 | if (IS_ERR(host->icc_ddr)) | |
984 | return dev_err_probe(dev, PTR_ERR(host->icc_ddr), | |
985 | "failed to acquire interconnect path\n"); | |
986 | ||
987 | host->icc_cpu = devm_of_icc_get(dev, "cpu-ufs"); | |
988 | if (IS_ERR(host->icc_cpu)) | |
989 | return dev_err_probe(dev, PTR_ERR(host->icc_cpu), | |
990 | "failed to acquire interconnect path\n"); | |
991 | ||
992 | /* | |
993 | * Set Maximum bandwidth vote before initializing the UFS controller and | |
994 | * device. Ideally, a minimal interconnect vote would suffice for the | |
995 | * initialization, but a max vote would allow faster initialization. | |
996 | */ | |
997 | ret = ufs_qcom_icc_set_bw(host, ufs_qcom_bw_table[MODE_MAX][0][0].mem_bw, | |
998 | ufs_qcom_bw_table[MODE_MAX][0][0].cfg_bw); | |
999 | if (ret < 0) | |
1000 | return dev_err_probe(dev, ret, "failed to set bandwidth request\n"); | |
1001 | ||
1002 | return 0; | |
1003 | } | |
1004 | ||
81c0fc51 YG |
1005 | /** |
1006 | * ufs_qcom_init - bind phy with controller | |
1007 | * @hba: host controller instance | |
1008 | * | |
1009 | * Binds PHY with controller and powers up PHY enabling clocks | |
1010 | * and regulators. | |
1011 | * | |
3a17fefe | 1012 | * Return: -EPROBE_DEFER if binding fails, returns negative error |
81c0fc51 YG |
1013 | * on phy power up failure and returns zero on success. |
1014 | */ | |
1015 | static int ufs_qcom_init(struct ufs_hba *hba) | |
1016 | { | |
1017 | int err; | |
1018 | struct device *dev = hba->dev; | |
1019 | struct ufs_qcom_host *host; | |
96f08cc5 | 1020 | struct ufs_clk_info *clki; |
81c0fc51 | 1021 | |
81c0fc51 | 1022 | host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); |
8291652e | 1023 | if (!host) |
031312db | 1024 | return -ENOMEM; |
81c0fc51 | 1025 | |
f06fcc71 | 1026 | /* Make a two way bind between the qcom host and the hba */ |
81c0fc51 | 1027 | host->hba = hba; |
1ce5898a | 1028 | ufshcd_set_variant(hba, host); |
81c0fc51 | 1029 | |
223b17ed MS |
1030 | /* Setup the optional reset control of HCI */ |
1031 | host->core_reset = devm_reset_control_get_optional(hba->dev, "rst"); | |
870b1279 | 1032 | if (IS_ERR(host->core_reset)) { |
223b17ed MS |
1033 | err = dev_err_probe(dev, PTR_ERR(host->core_reset), |
1034 | "Failed to get reset control\n"); | |
1035 | goto out_variant_clear; | |
870b1279 CG |
1036 | } |
1037 | ||
12fd5f25 EG |
1038 | /* Fire up the reset controller. Failure here is non-fatal. */ |
1039 | host->rcdev.of_node = dev->of_node; | |
1040 | host->rcdev.ops = &ufs_qcom_reset_ops; | |
1041 | host->rcdev.owner = dev->driver->owner; | |
1042 | host->rcdev.nr_resets = 1; | |
1043 | err = devm_reset_controller_register(dev, &host->rcdev); | |
031312db | 1044 | if (err) |
12fd5f25 | 1045 | dev_warn(dev, "Failed to register reset controller\n"); |
12fd5f25 | 1046 | |
c9ed9a6c MS |
1047 | if (!has_acpi_companion(dev)) { |
1048 | host->generic_phy = devm_phy_get(dev, "ufsphy"); | |
1049 | if (IS_ERR(host->generic_phy)) { | |
1050 | err = dev_err_probe(dev, PTR_ERR(host->generic_phy), "Failed to get PHY\n"); | |
e1a7752c LJ |
1051 | goto out_variant_clear; |
1052 | } | |
81c0fc51 YG |
1053 | } |
1054 | ||
03ce80a1 MS |
1055 | err = ufs_qcom_icc_init(host); |
1056 | if (err) | |
1057 | goto out_variant_clear; | |
1058 | ||
b8416b2f BA |
1059 | host->device_reset = devm_gpiod_get_optional(dev, "reset", |
1060 | GPIOD_OUT_HIGH); | |
1061 | if (IS_ERR(host->device_reset)) { | |
c7afadac MS |
1062 | err = dev_err_probe(dev, PTR_ERR(host->device_reset), |
1063 | "Failed to acquire device reset gpio\n"); | |
b8416b2f BA |
1064 | goto out_variant_clear; |
1065 | } | |
1066 | ||
bfdbe8ba YG |
1067 | ufs_qcom_get_controller_revision(hba, &host->hw_ver.major, |
1068 | &host->hw_ver.minor, &host->hw_ver.step); | |
1069 | ||
104cd58d MS |
1070 | host->dev_ref_clk_ctrl_mmio = hba->mmio_base + REG_UFS_CFG1; |
1071 | host->dev_ref_clk_en_mask = BIT(26); | |
f06fcc71 | 1072 | |
96f08cc5 CG |
1073 | list_for_each_entry(clki, &hba->clk_list_head, list) { |
1074 | if (!strcmp(clki->name, "core_clk_unipro")) | |
1075 | clki->keep_link_active = true; | |
1076 | } | |
1077 | ||
81c0fc51 YG |
1078 | err = ufs_qcom_init_lane_clks(host); |
1079 | if (err) | |
052553af | 1080 | goto out_variant_clear; |
81c0fc51 | 1081 | |
cad2e03d | 1082 | ufs_qcom_set_caps(hba); |
81c0fc51 | 1083 | ufs_qcom_advertise_quirks(hba); |
55820a7f | 1084 | ufs_qcom_set_host_params(hba); |
0bd3cb89 | 1085 | ufs_qcom_set_phy_gear(host); |
81c0fc51 | 1086 | |
df4ec2fa EB |
1087 | err = ufs_qcom_ice_init(host); |
1088 | if (err) | |
1089 | goto out_variant_clear; | |
1090 | ||
1e879e8f | 1091 | ufs_qcom_setup_clocks(hba, true, POST_CHANGE); |
81c0fc51 | 1092 | |
6e3fd44d YG |
1093 | ufs_qcom_get_default_testbus_cfg(host); |
1094 | err = ufs_qcom_testbus_config(host); | |
031312db MS |
1095 | if (err) |
1096 | /* Failure is non-fatal */ | |
6e3fd44d YG |
1097 | dev_warn(dev, "%s: failed to configure the testbus %d\n", |
1098 | __func__, err); | |
6e3fd44d | 1099 | |
031312db | 1100 | return 0; |
81c0fc51 | 1101 | |
a6854dff | 1102 | out_variant_clear: |
1ce5898a | 1103 | ufshcd_set_variant(hba, NULL); |
031312db | 1104 | |
81c0fc51 YG |
1105 | return err; |
1106 | } | |
1107 | ||
1108 | static void ufs_qcom_exit(struct ufs_hba *hba) | |
1109 | { | |
1ce5898a | 1110 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
81c0fc51 YG |
1111 | |
1112 | ufs_qcom_disable_lane_clks(host); | |
1113 | phy_power_off(host->generic_phy); | |
d7fe6b66 | 1114 | phy_exit(host->generic_phy); |
81c0fc51 YG |
1115 | } |
1116 | ||
a53dfc00 NR |
1117 | /** |
1118 | * ufs_qcom_set_clk_40ns_cycles - Configure 40ns clk cycles | |
1119 | * | |
1120 | * @hba: host controller instance | |
1121 | * @cycles_in_1us: No of cycles in 1us to be configured | |
1122 | * | |
1123 | * Returns error if dme get/set configuration for 40ns fails | |
1124 | * and returns zero on success. | |
1125 | */ | |
1126 | static int ufs_qcom_set_clk_40ns_cycles(struct ufs_hba *hba, | |
1127 | u32 cycles_in_1us) | |
1128 | { | |
1129 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); | |
1130 | u32 cycles_in_40ns; | |
1131 | u32 reg; | |
1132 | int err; | |
1133 | ||
1134 | /* | |
1135 | * UFS host controller V4.0.0 onwards needs to program | |
1136 | * PA_VS_CORE_CLK_40NS_CYCLES attribute per programmed | |
1137 | * frequency of unipro core clk of UFS host controller. | |
1138 | */ | |
1139 | if (host->hw_ver.major < 4) | |
1140 | return 0; | |
1141 | ||
1142 | /* | |
1143 | * Generic formulae for cycles_in_40ns = (freq_unipro/25) is not | |
1144 | * applicable for all frequencies. For ex: ceil(37.5 MHz/25) will | |
1145 | * be 2 and ceil(403 MHZ/25) will be 17 whereas Hardware | |
1146 | * specification expect to be 16. Hence use exact hardware spec | |
1147 | * mandated value for cycles_in_40ns instead of calculating using | |
1148 | * generic formulae. | |
1149 | */ | |
1150 | switch (cycles_in_1us) { | |
1151 | case UNIPRO_CORE_CLK_FREQ_403_MHZ: | |
1152 | cycles_in_40ns = 16; | |
1153 | break; | |
1154 | case UNIPRO_CORE_CLK_FREQ_300_MHZ: | |
1155 | cycles_in_40ns = 12; | |
1156 | break; | |
1157 | case UNIPRO_CORE_CLK_FREQ_201_5_MHZ: | |
1158 | cycles_in_40ns = 8; | |
1159 | break; | |
1160 | case UNIPRO_CORE_CLK_FREQ_150_MHZ: | |
1161 | cycles_in_40ns = 6; | |
1162 | break; | |
1163 | case UNIPRO_CORE_CLK_FREQ_100_MHZ: | |
1164 | cycles_in_40ns = 4; | |
1165 | break; | |
1166 | case UNIPRO_CORE_CLK_FREQ_75_MHZ: | |
1167 | cycles_in_40ns = 3; | |
1168 | break; | |
1169 | case UNIPRO_CORE_CLK_FREQ_37_5_MHZ: | |
1170 | cycles_in_40ns = 2; | |
1171 | break; | |
1172 | default: | |
1173 | dev_err(hba->dev, "UNIPRO clk freq %u MHz not supported\n", | |
1174 | cycles_in_1us); | |
1175 | return -EINVAL; | |
1176 | } | |
1177 | ||
1178 | err = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_VS_CORE_CLK_40NS_CYCLES), ®); | |
1179 | if (err) | |
1180 | return err; | |
1181 | ||
1182 | reg &= ~PA_VS_CORE_CLK_40NS_CYCLES_MASK; | |
1183 | reg |= cycles_in_40ns; | |
1184 | ||
1185 | return ufshcd_dme_set(hba, UIC_ARG_MIB(PA_VS_CORE_CLK_40NS_CYCLES), reg); | |
1186 | } | |
1187 | ||
b4e13e1a | 1188 | static int ufs_qcom_set_core_clk_ctrl(struct ufs_hba *hba, bool is_scale_up) |
f06fcc71 | 1189 | { |
07d2290f | 1190 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
b4e13e1a NR |
1191 | struct list_head *head = &hba->clk_list_head; |
1192 | struct ufs_clk_info *clki; | |
3b60f456 | 1193 | u32 cycles_in_1us = 0; |
f06fcc71 | 1194 | u32 core_clk_ctrl_reg; |
b4e13e1a NR |
1195 | int err; |
1196 | ||
1197 | list_for_each_entry(clki, head, list) { | |
1198 | if (!IS_ERR_OR_NULL(clki->clk) && | |
1199 | !strcmp(clki->name, "core_clk_unipro")) { | |
1200 | if (is_scale_up) | |
1201 | cycles_in_1us = ceil(clki->max_freq, (1000 * 1000)); | |
1202 | else | |
1203 | cycles_in_1us = ceil(clk_get_rate(clki->clk), (1000 * 1000)); | |
1204 | break; | |
1205 | } | |
1206 | } | |
f06fcc71 | 1207 | |
f06fcc71 YG |
1208 | err = ufshcd_dme_get(hba, |
1209 | UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL), | |
1210 | &core_clk_ctrl_reg); | |
1211 | if (err) | |
031312db | 1212 | return err; |
f06fcc71 | 1213 | |
07d2290f NR |
1214 | /* Bit mask is different for UFS host controller V4.0.0 onwards */ |
1215 | if (host->hw_ver.major >= 4) { | |
b4e13e1a | 1216 | if (!FIELD_FIT(CLK_1US_CYCLES_MASK_V4, cycles_in_1us)) |
07d2290f NR |
1217 | return -ERANGE; |
1218 | core_clk_ctrl_reg &= ~CLK_1US_CYCLES_MASK_V4; | |
b4e13e1a | 1219 | core_clk_ctrl_reg |= FIELD_PREP(CLK_1US_CYCLES_MASK_V4, cycles_in_1us); |
07d2290f | 1220 | } else { |
b4e13e1a | 1221 | if (!FIELD_FIT(CLK_1US_CYCLES_MASK, cycles_in_1us)) |
07d2290f NR |
1222 | return -ERANGE; |
1223 | core_clk_ctrl_reg &= ~CLK_1US_CYCLES_MASK; | |
b4e13e1a | 1224 | core_clk_ctrl_reg |= FIELD_PREP(CLK_1US_CYCLES_MASK, cycles_in_1us); |
07d2290f | 1225 | } |
f06fcc71 YG |
1226 | |
1227 | /* Clear CORE_CLK_DIV_EN */ | |
1228 | core_clk_ctrl_reg &= ~DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT; | |
1229 | ||
a53dfc00 | 1230 | err = ufshcd_dme_set(hba, |
f06fcc71 YG |
1231 | UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL), |
1232 | core_clk_ctrl_reg); | |
a53dfc00 NR |
1233 | if (err) |
1234 | return err; | |
1235 | ||
1236 | /* Configure unipro core clk 40ns attribute */ | |
1237 | return ufs_qcom_set_clk_40ns_cycles(hba, cycles_in_1us); | |
f06fcc71 YG |
1238 | } |
1239 | ||
1240 | static int ufs_qcom_clk_scale_up_pre_change(struct ufs_hba *hba) | |
f06fcc71 YG |
1241 | { |
1242 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); | |
fd915c67 NR |
1243 | struct ufs_pa_layer_attr *attr = &host->dev_req_params; |
1244 | int ret; | |
f06fcc71 | 1245 | |
b6f2e063 DC |
1246 | ret = ufs_qcom_cfg_timers(hba, attr->gear_rx, attr->pwr_rx, |
1247 | attr->hs_rate, false, true); | |
1248 | if (ret) { | |
1249 | dev_err(hba->dev, "%s ufs cfg timer failed\n", __func__); | |
1250 | return ret; | |
fd915c67 | 1251 | } |
b4e13e1a NR |
1252 | /* set unipro core clock attributes and clear clock divider */ |
1253 | return ufs_qcom_set_core_clk_ctrl(hba, true); | |
f06fcc71 YG |
1254 | } |
1255 | ||
3091181b NR |
1256 | static int ufs_qcom_clk_scale_up_post_change(struct ufs_hba *hba) |
1257 | { | |
1258 | return 0; | |
1259 | } | |
1260 | ||
f06fcc71 YG |
1261 | static int ufs_qcom_clk_scale_down_pre_change(struct ufs_hba *hba) |
1262 | { | |
f06fcc71 YG |
1263 | int err; |
1264 | u32 core_clk_ctrl_reg; | |
1265 | ||
f06fcc71 YG |
1266 | err = ufshcd_dme_get(hba, |
1267 | UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL), | |
1268 | &core_clk_ctrl_reg); | |
1269 | ||
1270 | /* make sure CORE_CLK_DIV_EN is cleared */ | |
1271 | if (!err && | |
1272 | (core_clk_ctrl_reg & DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT)) { | |
1273 | core_clk_ctrl_reg &= ~DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT; | |
1274 | err = ufshcd_dme_set(hba, | |
1275 | UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL), | |
1276 | core_clk_ctrl_reg); | |
1277 | } | |
1278 | ||
1279 | return err; | |
1280 | } | |
1281 | ||
1282 | static int ufs_qcom_clk_scale_down_post_change(struct ufs_hba *hba) | |
1283 | { | |
b4e13e1a NR |
1284 | /* set unipro core clock attributes and clear clock divider */ |
1285 | return ufs_qcom_set_core_clk_ctrl(hba, false); | |
f06fcc71 YG |
1286 | } |
1287 | ||
1288 | static int ufs_qcom_clk_scale_notify(struct ufs_hba *hba, | |
1289 | bool scale_up, enum ufs_notify_change_status status) | |
81c0fc51 | 1290 | { |
1ce5898a | 1291 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
1f165c87 | 1292 | int err; |
81c0fc51 | 1293 | |
21f04fb4 NR |
1294 | /* check the host controller state before sending hibern8 cmd */ |
1295 | if (!ufshcd_is_hba_active(hba)) | |
1296 | return 0; | |
1297 | ||
f06fcc71 | 1298 | if (status == PRE_CHANGE) { |
a0cea833 AD |
1299 | err = ufshcd_uic_hibern8_enter(hba); |
1300 | if (err) | |
1301 | return err; | |
f06fcc71 YG |
1302 | if (scale_up) |
1303 | err = ufs_qcom_clk_scale_up_pre_change(hba); | |
1304 | else | |
1305 | err = ufs_qcom_clk_scale_down_pre_change(hba); | |
a0cea833 | 1306 | |
9264fd61 CL |
1307 | if (err) { |
1308 | ufshcd_uic_hibern8_exit(hba); | |
1309 | return err; | |
1310 | } | |
f06fcc71 YG |
1311 | } else { |
1312 | if (scale_up) | |
1313 | err = ufs_qcom_clk_scale_up_post_change(hba); | |
1314 | else | |
1315 | err = ufs_qcom_clk_scale_down_post_change(hba); | |
1316 | ||
a0cea833 | 1317 | |
fa8d3272 | 1318 | if (err) { |
a0cea833 | 1319 | ufshcd_uic_hibern8_exit(hba); |
031312db | 1320 | return err; |
a0cea833 | 1321 | } |
f06fcc71 | 1322 | |
03ce80a1 | 1323 | ufs_qcom_icc_update_bw(host); |
a0cea833 | 1324 | ufshcd_uic_hibern8_exit(hba); |
f06fcc71 YG |
1325 | } |
1326 | ||
031312db | 1327 | return 0; |
6e3fd44d YG |
1328 | } |
1329 | ||
eba5ed35 YG |
1330 | static void ufs_qcom_enable_test_bus(struct ufs_qcom_host *host) |
1331 | { | |
e4ce23fb AH |
1332 | ufshcd_rmwl(host->hba, UFS_REG_TEST_BUS_EN, |
1333 | UFS_REG_TEST_BUS_EN, REG_UFS_CFG1); | |
1334 | ufshcd_rmwl(host->hba, TEST_BUS_EN, TEST_BUS_EN, REG_UFS_CFG1); | |
eba5ed35 YG |
1335 | } |
1336 | ||
6e3fd44d YG |
1337 | static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host) |
1338 | { | |
1339 | /* provide a legal default configuration */ | |
9c46b867 VG |
1340 | host->testbus.select_major = TSTBUS_UNIPRO; |
1341 | host->testbus.select_minor = 37; | |
6e3fd44d YG |
1342 | } |
1343 | ||
1344 | static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host) | |
1345 | { | |
1346 | if (host->testbus.select_major >= TSTBUS_MAX) { | |
1347 | dev_err(host->hba->dev, | |
1348 | "%s: UFS_CFG1[TEST_BUS_SEL} may not equal 0x%05X\n", | |
1349 | __func__, host->testbus.select_major); | |
1350 | return false; | |
1351 | } | |
1352 | ||
6e3fd44d YG |
1353 | return true; |
1354 | } | |
1355 | ||
1356 | int ufs_qcom_testbus_config(struct ufs_qcom_host *host) | |
1357 | { | |
1358 | int reg; | |
1359 | int offset; | |
1360 | u32 mask = TEST_BUS_SUB_SEL_MASK; | |
1361 | ||
1362 | if (!host) | |
1363 | return -EINVAL; | |
81c0fc51 | 1364 | |
6e3fd44d YG |
1365 | if (!ufs_qcom_testbus_cfg_is_ok(host)) |
1366 | return -EPERM; | |
1367 | ||
1368 | switch (host->testbus.select_major) { | |
1369 | case TSTBUS_UAWM: | |
1370 | reg = UFS_TEST_BUS_CTRL_0; | |
1371 | offset = 24; | |
1372 | break; | |
1373 | case TSTBUS_UARM: | |
1374 | reg = UFS_TEST_BUS_CTRL_0; | |
1375 | offset = 16; | |
1376 | break; | |
1377 | case TSTBUS_TXUC: | |
1378 | reg = UFS_TEST_BUS_CTRL_0; | |
1379 | offset = 8; | |
1380 | break; | |
1381 | case TSTBUS_RXUC: | |
1382 | reg = UFS_TEST_BUS_CTRL_0; | |
1383 | offset = 0; | |
1384 | break; | |
1385 | case TSTBUS_DFC: | |
1386 | reg = UFS_TEST_BUS_CTRL_1; | |
1387 | offset = 24; | |
1388 | break; | |
1389 | case TSTBUS_TRLUT: | |
1390 | reg = UFS_TEST_BUS_CTRL_1; | |
1391 | offset = 16; | |
1392 | break; | |
1393 | case TSTBUS_TMRLUT: | |
1394 | reg = UFS_TEST_BUS_CTRL_1; | |
1395 | offset = 8; | |
1396 | break; | |
1397 | case TSTBUS_OCSC: | |
1398 | reg = UFS_TEST_BUS_CTRL_1; | |
1399 | offset = 0; | |
1400 | break; | |
1401 | case TSTBUS_WRAPPER: | |
1402 | reg = UFS_TEST_BUS_CTRL_2; | |
1403 | offset = 16; | |
1404 | break; | |
1405 | case TSTBUS_COMBINED: | |
1406 | reg = UFS_TEST_BUS_CTRL_2; | |
1407 | offset = 8; | |
1408 | break; | |
1409 | case TSTBUS_UTP_HCI: | |
1410 | reg = UFS_TEST_BUS_CTRL_2; | |
1411 | offset = 0; | |
1412 | break; | |
1413 | case TSTBUS_UNIPRO: | |
1414 | reg = UFS_UNIPRO_CFG; | |
9c46b867 VG |
1415 | offset = 20; |
1416 | mask = 0xFFF; | |
6e3fd44d YG |
1417 | break; |
1418 | /* | |
1419 | * No need for a default case, since | |
1420 | * ufs_qcom_testbus_cfg_is_ok() checks that the configuration | |
1421 | * is legal | |
1422 | */ | |
1423 | } | |
1424 | mask <<= offset; | |
6e3fd44d YG |
1425 | ufshcd_rmwl(host->hba, TEST_BUS_SEL, |
1426 | (u32)host->testbus.select_major << 19, | |
1427 | REG_UFS_CFG1); | |
1428 | ufshcd_rmwl(host->hba, mask, | |
1429 | (u32)host->testbus.select_minor << offset, | |
1430 | reg); | |
eba5ed35 | 1431 | ufs_qcom_enable_test_bus(host); |
9c46b867 VG |
1432 | /* |
1433 | * Make sure the test bus configuration is | |
1434 | * committed before returning. | |
1435 | */ | |
1436 | mb(); | |
6e3fd44d YG |
1437 | |
1438 | return 0; | |
81c0fc51 YG |
1439 | } |
1440 | ||
6e3fd44d YG |
1441 | static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba) |
1442 | { | |
50a427a0 AH |
1443 | u32 reg; |
1444 | struct ufs_qcom_host *host; | |
1445 | ||
1446 | host = ufshcd_get_variant(hba); | |
1447 | ||
ba80917d TW |
1448 | ufshcd_dump_regs(hba, REG_UFS_SYS1CLK_1US, 16 * 4, |
1449 | "HCI Vendor Specific Registers "); | |
6e3fd44d | 1450 | |
50a427a0 AH |
1451 | reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_REG_OCSC); |
1452 | ufshcd_dump_regs(hba, reg, 44 * 4, "UFS_UFS_DBG_RD_REG_OCSC "); | |
1453 | ||
1454 | reg = ufshcd_readl(hba, REG_UFS_CFG1); | |
1455 | reg |= UTP_DBG_RAMS_EN; | |
1456 | ufshcd_writel(hba, reg, REG_UFS_CFG1); | |
1457 | ||
1458 | reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_EDTL_RAM); | |
1459 | ufshcd_dump_regs(hba, reg, 32 * 4, "UFS_UFS_DBG_RD_EDTL_RAM "); | |
1460 | ||
1461 | reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_DESC_RAM); | |
1462 | ufshcd_dump_regs(hba, reg, 128 * 4, "UFS_UFS_DBG_RD_DESC_RAM "); | |
1463 | ||
1464 | reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_PRDT_RAM); | |
1465 | ufshcd_dump_regs(hba, reg, 64 * 4, "UFS_UFS_DBG_RD_PRDT_RAM "); | |
1466 | ||
1467 | /* clear bit 17 - UTP_DBG_RAMS_EN */ | |
1468 | ufshcd_rmwl(hba, UTP_DBG_RAMS_EN, 0, REG_UFS_CFG1); | |
1469 | ||
1470 | reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_UAWM); | |
1471 | ufshcd_dump_regs(hba, reg, 4 * 4, "UFS_DBG_RD_REG_UAWM "); | |
1472 | ||
1473 | reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_UARM); | |
1474 | ufshcd_dump_regs(hba, reg, 4 * 4, "UFS_DBG_RD_REG_UARM "); | |
1475 | ||
1476 | reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_TXUC); | |
1477 | ufshcd_dump_regs(hba, reg, 48 * 4, "UFS_DBG_RD_REG_TXUC "); | |
1478 | ||
1479 | reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_RXUC); | |
1480 | ufshcd_dump_regs(hba, reg, 27 * 4, "UFS_DBG_RD_REG_RXUC "); | |
1481 | ||
1482 | reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_DFC); | |
1483 | ufshcd_dump_regs(hba, reg, 19 * 4, "UFS_DBG_RD_REG_DFC "); | |
1484 | ||
1485 | reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_TRLUT); | |
1486 | ufshcd_dump_regs(hba, reg, 34 * 4, "UFS_DBG_RD_REG_TRLUT "); | |
1487 | ||
1488 | reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_TMRLUT); | |
1489 | ufshcd_dump_regs(hba, reg, 9 * 4, "UFS_DBG_RD_REG_TMRLUT "); | |
6e3fd44d | 1490 | } |
eba5ed35 | 1491 | |
b8416b2f BA |
1492 | /** |
1493 | * ufs_qcom_device_reset() - toggle the (optional) device reset line | |
1494 | * @hba: per-adapter instance | |
1495 | * | |
1496 | * Toggles the (optional) reset line to reset the attached device. | |
1497 | */ | |
151f1b66 | 1498 | static int ufs_qcom_device_reset(struct ufs_hba *hba) |
b8416b2f BA |
1499 | { |
1500 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); | |
1501 | ||
1502 | /* reset gpio is optional */ | |
1503 | if (!host->device_reset) | |
151f1b66 | 1504 | return -EOPNOTSUPP; |
b8416b2f BA |
1505 | |
1506 | /* | |
1507 | * The UFS device shall detect reset pulses of 1us, sleep for 10us to | |
1508 | * be on the safe side. | |
1509 | */ | |
b61d0414 | 1510 | ufs_qcom_device_reset_ctrl(hba, true); |
b8416b2f BA |
1511 | usleep_range(10, 15); |
1512 | ||
b61d0414 | 1513 | ufs_qcom_device_reset_ctrl(hba, false); |
b8416b2f | 1514 | usleep_range(10, 15); |
151f1b66 AH |
1515 | |
1516 | return 0; | |
b8416b2f BA |
1517 | } |
1518 | ||
80b21006 AD |
1519 | #if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) |
1520 | static void ufs_qcom_config_scaling_param(struct ufs_hba *hba, | |
c906e832 BVA |
1521 | struct devfreq_dev_profile *p, |
1522 | struct devfreq_simple_ondemand_data *d) | |
80b21006 | 1523 | { |
80b21006 | 1524 | p->polling_ms = 60; |
0645ab15 | 1525 | p->timer = DEVFREQ_TIMER_DELAYED; |
80b21006 AD |
1526 | d->upthreshold = 70; |
1527 | d->downdifferential = 5; | |
1528 | } | |
1529 | #else | |
1530 | static void ufs_qcom_config_scaling_param(struct ufs_hba *hba, | |
c906e832 BVA |
1531 | struct devfreq_dev_profile *p, |
1532 | struct devfreq_simple_ondemand_data *data) | |
80b21006 AD |
1533 | { |
1534 | } | |
1535 | #endif | |
1536 | ||
baf5ddac MS |
1537 | static void ufs_qcom_reinit_notify(struct ufs_hba *hba) |
1538 | { | |
1539 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); | |
1540 | ||
1541 | phy_power_off(host->generic_phy); | |
1542 | } | |
1543 | ||
c263b4ef AD |
1544 | /* Resources */ |
1545 | static const struct ufshcd_res_info ufs_res_info[RES_MAX] = { | |
1546 | {.name = "ufs_mem",}, | |
1547 | {.name = "mcq",}, | |
1548 | /* Submission Queue DAO */ | |
1549 | {.name = "mcq_sqd",}, | |
1550 | /* Submission Queue Interrupt Status */ | |
1551 | {.name = "mcq_sqis",}, | |
1552 | /* Completion Queue DAO */ | |
1553 | {.name = "mcq_cqd",}, | |
1554 | /* Completion Queue Interrupt Status */ | |
1555 | {.name = "mcq_cqis",}, | |
1556 | /* MCQ vendor specific */ | |
1557 | {.name = "mcq_vs",}, | |
1558 | }; | |
1559 | ||
1560 | static int ufs_qcom_mcq_config_resource(struct ufs_hba *hba) | |
1561 | { | |
1562 | struct platform_device *pdev = to_platform_device(hba->dev); | |
1563 | struct ufshcd_res_info *res; | |
1564 | struct resource *res_mem, *res_mcq; | |
1f165c87 | 1565 | int i, ret; |
c263b4ef AD |
1566 | |
1567 | memcpy(hba->res, ufs_res_info, sizeof(ufs_res_info)); | |
1568 | ||
1569 | for (i = 0; i < RES_MAX; i++) { | |
1570 | res = &hba->res[i]; | |
1571 | res->resource = platform_get_resource_byname(pdev, | |
1572 | IORESOURCE_MEM, | |
1573 | res->name); | |
1574 | if (!res->resource) { | |
1575 | dev_info(hba->dev, "Resource %s not provided\n", res->name); | |
1576 | if (i == RES_UFS) | |
3a747c5c | 1577 | return -ENODEV; |
c263b4ef AD |
1578 | continue; |
1579 | } else if (i == RES_UFS) { | |
1580 | res_mem = res->resource; | |
1581 | res->base = hba->mmio_base; | |
1582 | continue; | |
1583 | } | |
1584 | ||
1585 | res->base = devm_ioremap_resource(hba->dev, res->resource); | |
1586 | if (IS_ERR(res->base)) { | |
1587 | dev_err(hba->dev, "Failed to map res %s, err=%d\n", | |
1588 | res->name, (int)PTR_ERR(res->base)); | |
c263b4ef | 1589 | ret = PTR_ERR(res->base); |
c8be073b | 1590 | res->base = NULL; |
c263b4ef AD |
1591 | return ret; |
1592 | } | |
1593 | } | |
1594 | ||
1595 | /* MCQ resource provided in DT */ | |
1596 | res = &hba->res[RES_MCQ]; | |
1597 | /* Bail if MCQ resource is provided */ | |
1598 | if (res->base) | |
1599 | goto out; | |
1600 | ||
1601 | /* Explicitly allocate MCQ resource from ufs_mem */ | |
1602 | res_mcq = devm_kzalloc(hba->dev, sizeof(*res_mcq), GFP_KERNEL); | |
1603 | if (!res_mcq) | |
c9507eab | 1604 | return -ENOMEM; |
c263b4ef AD |
1605 | |
1606 | res_mcq->start = res_mem->start + | |
1607 | MCQ_SQATTR_OFFSET(hba->mcq_capabilities); | |
1608 | res_mcq->end = res_mcq->start + hba->nr_hw_queues * MCQ_QCFG_SIZE - 1; | |
1609 | res_mcq->flags = res_mem->flags; | |
1610 | res_mcq->name = "mcq"; | |
1611 | ||
1612 | ret = insert_resource(&iomem_resource, res_mcq); | |
1613 | if (ret) { | |
1614 | dev_err(hba->dev, "Failed to insert MCQ resource, err=%d\n", | |
1615 | ret); | |
c9507eab | 1616 | return ret; |
c263b4ef AD |
1617 | } |
1618 | ||
1619 | res->base = devm_ioremap_resource(hba->dev, res_mcq); | |
1620 | if (IS_ERR(res->base)) { | |
1621 | dev_err(hba->dev, "MCQ registers mapping failed, err=%d\n", | |
1622 | (int)PTR_ERR(res->base)); | |
1623 | ret = PTR_ERR(res->base); | |
1624 | goto ioremap_err; | |
1625 | } | |
1626 | ||
1627 | out: | |
1628 | hba->mcq_base = res->base; | |
1629 | return 0; | |
1630 | ioremap_err: | |
1631 | res->base = NULL; | |
1632 | remove_resource(res_mcq); | |
c263b4ef AD |
1633 | return ret; |
1634 | } | |
1635 | ||
2468da61 AD |
1636 | static int ufs_qcom_op_runtime_config(struct ufs_hba *hba) |
1637 | { | |
1638 | struct ufshcd_res_info *mem_res, *sqdao_res; | |
1639 | struct ufshcd_mcq_opr_info_t *opr; | |
1640 | int i; | |
1641 | ||
1642 | mem_res = &hba->res[RES_UFS]; | |
1643 | sqdao_res = &hba->res[RES_MCQ_SQD]; | |
1644 | ||
1645 | if (!mem_res->base || !sqdao_res->base) | |
1646 | return -EINVAL; | |
1647 | ||
1648 | for (i = 0; i < OPR_MAX; i++) { | |
1649 | opr = &hba->mcq_opr[i]; | |
1650 | opr->offset = sqdao_res->resource->start - | |
1651 | mem_res->resource->start + 0x40 * i; | |
1652 | opr->stride = 0x100; | |
1653 | opr->base = sqdao_res->base + 0x40 * i; | |
1654 | } | |
1655 | ||
1656 | return 0; | |
1657 | } | |
1658 | ||
7224c806 AD |
1659 | static int ufs_qcom_get_hba_mac(struct ufs_hba *hba) |
1660 | { | |
1661 | /* Qualcomm HC supports up to 64 */ | |
1662 | return MAX_SUPP_MAC; | |
1663 | } | |
1664 | ||
f87b2c41 AD |
1665 | static int ufs_qcom_get_outstanding_cqs(struct ufs_hba *hba, |
1666 | unsigned long *ocqs) | |
1667 | { | |
1668 | struct ufshcd_res_info *mcq_vs_res = &hba->res[RES_MCQ_VS]; | |
1669 | ||
1670 | if (!mcq_vs_res->base) | |
1671 | return -EINVAL; | |
1672 | ||
1673 | *ocqs = readl(mcq_vs_res->base + UFS_MEM_CQIS_VS); | |
1674 | ||
1675 | return 0; | |
1676 | } | |
1677 | ||
519b6274 CG |
1678 | static void ufs_qcom_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg) |
1679 | { | |
1680 | struct device *dev = msi_desc_to_dev(desc); | |
1681 | struct ufs_hba *hba = dev_get_drvdata(dev); | |
1682 | ||
1683 | ufshcd_mcq_config_esi(hba, msg); | |
1684 | } | |
1685 | ||
8f2b7865 | 1686 | static irqreturn_t ufs_qcom_mcq_esi_handler(int irq, void *data) |
519b6274 | 1687 | { |
8f2b7865 ZC |
1688 | struct msi_desc *desc = data; |
1689 | struct device *dev = msi_desc_to_dev(desc); | |
1690 | struct ufs_hba *hba = dev_get_drvdata(dev); | |
8f2b7865 | 1691 | u32 id = desc->msi_index; |
519b6274 CG |
1692 | struct ufs_hw_queue *hwq = &hba->uhq[id]; |
1693 | ||
1694 | ufshcd_mcq_write_cqis(hba, 0x1, id); | |
57d6ef46 | 1695 | ufshcd_mcq_poll_cqe_lock(hba, hwq); |
519b6274 CG |
1696 | |
1697 | return IRQ_HANDLED; | |
1698 | } | |
1699 | ||
1700 | static int ufs_qcom_config_esi(struct ufs_hba *hba) | |
1701 | { | |
1702 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); | |
1703 | struct msi_desc *desc; | |
1704 | struct msi_desc *failed_desc = NULL; | |
1705 | int nr_irqs, ret; | |
1706 | ||
1707 | if (host->esi_enabled) | |
1708 | return 0; | |
519b6274 CG |
1709 | |
1710 | /* | |
1711 | * 1. We only handle CQs as of now. | |
1712 | * 2. Poll queues do not need ESI. | |
1713 | */ | |
1714 | nr_irqs = hba->nr_hw_queues - hba->nr_queues[HCTX_TYPE_POLL]; | |
1715 | ret = platform_msi_domain_alloc_irqs(hba->dev, nr_irqs, | |
1716 | ufs_qcom_write_msi_msg); | |
8f2b7865 ZC |
1717 | if (ret) { |
1718 | dev_err(hba->dev, "Failed to request Platform MSI %d\n", ret); | |
519b6274 | 1719 | goto out; |
8f2b7865 | 1720 | } |
519b6274 | 1721 | |
f52a805e | 1722 | msi_lock_descs(hba->dev); |
519b6274 | 1723 | msi_for_each_desc(desc, hba->dev, MSI_DESC_ALL) { |
519b6274 CG |
1724 | ret = devm_request_irq(hba->dev, desc->irq, |
1725 | ufs_qcom_mcq_esi_handler, | |
8f2b7865 | 1726 | IRQF_SHARED, "qcom-mcq-esi", desc); |
519b6274 CG |
1727 | if (ret) { |
1728 | dev_err(hba->dev, "%s: Fail to request IRQ for %d, err = %d\n", | |
1729 | __func__, desc->irq, ret); | |
1730 | failed_desc = desc; | |
1731 | break; | |
1732 | } | |
1733 | } | |
f52a805e | 1734 | msi_unlock_descs(hba->dev); |
519b6274 CG |
1735 | |
1736 | if (ret) { | |
1737 | /* Rewind */ | |
f52a805e | 1738 | msi_lock_descs(hba->dev); |
519b6274 CG |
1739 | msi_for_each_desc(desc, hba->dev, MSI_DESC_ALL) { |
1740 | if (desc == failed_desc) | |
1741 | break; | |
1742 | devm_free_irq(hba->dev, desc->irq, hba); | |
1743 | } | |
f52a805e | 1744 | msi_unlock_descs(hba->dev); |
519b6274 CG |
1745 | platform_msi_domain_free_irqs(hba->dev); |
1746 | } else { | |
1747 | if (host->hw_ver.major == 6 && host->hw_ver.minor == 0 && | |
0e9f4375 | 1748 | host->hw_ver.step == 0) |
26cdd694 MS |
1749 | ufshcd_rmwl(hba, ESI_VEC_MASK, |
1750 | FIELD_PREP(ESI_VEC_MASK, MAX_ESI_VEC - 1), | |
1751 | REG_UFS_CFG3); | |
519b6274 CG |
1752 | ufshcd_mcq_enable_esi(hba); |
1753 | } | |
1754 | ||
1755 | out: | |
8f2b7865 | 1756 | if (!ret) |
519b6274 | 1757 | host->esi_enabled = true; |
519b6274 CG |
1758 | |
1759 | return ret; | |
1760 | } | |
1761 | ||
bc5b6816 | 1762 | /* |
81c0fc51 YG |
1763 | * struct ufs_hba_qcom_vops - UFS QCOM specific variant operations |
1764 | * | |
1765 | * The variant operations configure the necessary controller and PHY | |
1766 | * handshake during initialization. | |
1767 | */ | |
d508e31d | 1768 | static const struct ufs_hba_variant_ops ufs_hba_qcom_vops = { |
81c0fc51 YG |
1769 | .name = "qcom", |
1770 | .init = ufs_qcom_init, | |
1771 | .exit = ufs_qcom_exit, | |
ae977587 | 1772 | .get_ufs_hci_version = ufs_qcom_get_ufs_hci_version, |
81c0fc51 YG |
1773 | .clk_scale_notify = ufs_qcom_clk_scale_notify, |
1774 | .setup_clocks = ufs_qcom_setup_clocks, | |
1775 | .hce_enable_notify = ufs_qcom_hce_enable_notify, | |
1776 | .link_startup_notify = ufs_qcom_link_startup_notify, | |
1777 | .pwr_change_notify = ufs_qcom_pwr_change_notify, | |
56d4a186 | 1778 | .apply_dev_quirks = ufs_qcom_apply_dev_quirks, |
81c0fc51 YG |
1779 | .suspend = ufs_qcom_suspend, |
1780 | .resume = ufs_qcom_resume, | |
6e3fd44d | 1781 | .dbg_register_dump = ufs_qcom_dump_dbg_regs, |
b8416b2f | 1782 | .device_reset = ufs_qcom_device_reset, |
80b21006 | 1783 | .config_scaling_param = ufs_qcom_config_scaling_param, |
df4ec2fa | 1784 | .program_key = ufs_qcom_ice_program_key, |
baf5ddac | 1785 | .reinit_notify = ufs_qcom_reinit_notify, |
c263b4ef | 1786 | .mcq_config_resource = ufs_qcom_mcq_config_resource, |
7224c806 | 1787 | .get_hba_mac = ufs_qcom_get_hba_mac, |
2468da61 | 1788 | .op_runtime_config = ufs_qcom_op_runtime_config, |
f87b2c41 | 1789 | .get_outstanding_cqs = ufs_qcom_get_outstanding_cqs, |
519b6274 | 1790 | .config_esi = ufs_qcom_config_esi, |
81c0fc51 | 1791 | }; |
fb819ee8 | 1792 | |
47555a5c YG |
1793 | /** |
1794 | * ufs_qcom_probe - probe routine of the driver | |
1795 | * @pdev: pointer to Platform device handle | |
1796 | * | |
3a17fefe | 1797 | * Return: zero for success and non-zero for failure. |
47555a5c YG |
1798 | */ |
1799 | static int ufs_qcom_probe(struct platform_device *pdev) | |
1800 | { | |
1801 | int err; | |
1802 | struct device *dev = &pdev->dev; | |
1803 | ||
1804 | /* Perform generic probe */ | |
1805 | err = ufshcd_pltfrm_init(pdev, &ufs_hba_qcom_vops); | |
1806 | if (err) | |
132b0272 | 1807 | return dev_err_probe(dev, err, "ufshcd_pltfrm_init() failed\n"); |
47555a5c | 1808 | |
132b0272 | 1809 | return 0; |
47555a5c YG |
1810 | } |
1811 | ||
1812 | /** | |
1813 | * ufs_qcom_remove - set driver_data of the device to NULL | |
1814 | * @pdev: pointer to platform device handle | |
1815 | * | |
4b9ad0b8 | 1816 | * Always returns 0 |
47555a5c | 1817 | */ |
0842b761 | 1818 | static void ufs_qcom_remove(struct platform_device *pdev) |
47555a5c YG |
1819 | { |
1820 | struct ufs_hba *hba = platform_get_drvdata(pdev); | |
1821 | ||
1822 | pm_runtime_get_sync(&(pdev)->dev); | |
1823 | ufshcd_remove(hba); | |
519b6274 | 1824 | platform_msi_domain_free_irqs(hba->dev); |
47555a5c YG |
1825 | } |
1826 | ||
dd3f5330 | 1827 | static const struct of_device_id ufs_qcom_of_match[] __maybe_unused = { |
47555a5c YG |
1828 | { .compatible = "qcom,ufshc"}, |
1829 | {}, | |
1830 | }; | |
ab3dabb3 | 1831 | MODULE_DEVICE_TABLE(of, ufs_qcom_of_match); |
47555a5c | 1832 | |
e1a7752c LJ |
1833 | #ifdef CONFIG_ACPI |
1834 | static const struct acpi_device_id ufs_qcom_acpi_match[] = { | |
1835 | { "QCOM24A5" }, | |
1836 | { }, | |
1837 | }; | |
1838 | MODULE_DEVICE_TABLE(acpi, ufs_qcom_acpi_match); | |
1839 | #endif | |
1840 | ||
47555a5c | 1841 | static const struct dev_pm_ops ufs_qcom_pm_ops = { |
f1ecbe1e | 1842 | SET_RUNTIME_PM_OPS(ufshcd_runtime_suspend, ufshcd_runtime_resume, NULL) |
b294ff3e AD |
1843 | .prepare = ufshcd_suspend_prepare, |
1844 | .complete = ufshcd_resume_complete, | |
88441a8d AH |
1845 | #ifdef CONFIG_PM_SLEEP |
1846 | .suspend = ufshcd_system_suspend, | |
1847 | .resume = ufshcd_system_resume, | |
1848 | .freeze = ufshcd_system_freeze, | |
1849 | .restore = ufshcd_system_restore, | |
1850 | .thaw = ufshcd_system_thaw, | |
1851 | #endif | |
47555a5c YG |
1852 | }; |
1853 | ||
1854 | static struct platform_driver ufs_qcom_pltform = { | |
1855 | .probe = ufs_qcom_probe, | |
0842b761 | 1856 | .remove_new = ufs_qcom_remove, |
47555a5c YG |
1857 | .driver = { |
1858 | .name = "ufshcd-qcom", | |
1859 | .pm = &ufs_qcom_pm_ops, | |
1860 | .of_match_table = of_match_ptr(ufs_qcom_of_match), | |
e1a7752c | 1861 | .acpi_match_table = ACPI_PTR(ufs_qcom_acpi_match), |
47555a5c YG |
1862 | }, |
1863 | }; | |
1864 | module_platform_driver(ufs_qcom_pltform); | |
1865 | ||
fb819ee8 | 1866 | MODULE_LICENSE("GPL v2"); |