diff options
Diffstat (limited to 'drivers/scsi/ufs')
-rw-r--r-- | drivers/scsi/ufs/Kconfig | 2 | ||||
-rw-r--r-- | drivers/scsi/ufs/ufs-qcom.c | 905 | ||||
-rw-r--r-- | drivers/scsi/ufs/ufs-qcom.h | 68 | ||||
-rw-r--r-- | drivers/scsi/ufs/ufshcd-pltfrm.c | 98 | ||||
-rw-r--r-- | drivers/scsi/ufs/ufshcd-pltfrm.h | 41 | ||||
-rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 130 | ||||
-rw-r--r-- | drivers/scsi/ufs/ufshcd.h | 149 |
7 files changed, 1048 insertions, 345 deletions
diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig index e94538362536..5f4530744e0a 100644 --- a/drivers/scsi/ufs/Kconfig +++ b/drivers/scsi/ufs/Kconfig | |||
@@ -72,7 +72,7 @@ config SCSI_UFSHCD_PLATFORM | |||
72 | If unsure, say N. | 72 | If unsure, say N. |
73 | 73 | ||
74 | config SCSI_UFS_QCOM | 74 | config SCSI_UFS_QCOM |
75 | bool "QCOM specific hooks to UFS controller platform driver" | 75 | tristate "QCOM specific hooks to UFS controller platform driver" |
76 | depends on SCSI_UFSHCD_PLATFORM && ARCH_QCOM | 76 | depends on SCSI_UFSHCD_PLATFORM && ARCH_QCOM |
77 | select PHY_QCOM_UFS | 77 | select PHY_QCOM_UFS |
78 | help | 78 | help |
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 4cdffa46d401..4f38d008bfb4 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c | |||
@@ -19,16 +19,44 @@ | |||
19 | 19 | ||
20 | #include <linux/phy/phy-qcom-ufs.h> | 20 | #include <linux/phy/phy-qcom-ufs.h> |
21 | #include "ufshcd.h" | 21 | #include "ufshcd.h" |
22 | #include "ufshcd-pltfrm.h" | ||
22 | #include "unipro.h" | 23 | #include "unipro.h" |
23 | #include "ufs-qcom.h" | 24 | #include "ufs-qcom.h" |
24 | #include "ufshci.h" | 25 | #include "ufshci.h" |
26 | #define UFS_QCOM_DEFAULT_DBG_PRINT_EN \ | ||
27 | (UFS_QCOM_DBG_PRINT_REGS_EN | UFS_QCOM_DBG_PRINT_TEST_BUS_EN) | ||
28 | |||
29 | enum { | ||
30 | TSTBUS_UAWM, | ||
31 | TSTBUS_UARM, | ||
32 | TSTBUS_TXUC, | ||
33 | TSTBUS_RXUC, | ||
34 | TSTBUS_DFC, | ||
35 | TSTBUS_TRLUT, | ||
36 | TSTBUS_TMRLUT, | ||
37 | TSTBUS_OCSC, | ||
38 | TSTBUS_UTP_HCI, | ||
39 | TSTBUS_COMBINED, | ||
40 | TSTBUS_WRAPPER, | ||
41 | TSTBUS_UNIPRO, | ||
42 | TSTBUS_MAX, | ||
43 | }; | ||
25 | 44 | ||
26 | static struct ufs_qcom_host *ufs_qcom_hosts[MAX_UFS_QCOM_HOSTS]; | 45 | static struct ufs_qcom_host *ufs_qcom_hosts[MAX_UFS_QCOM_HOSTS]; |
27 | 46 | ||
28 | static void ufs_qcom_get_speed_mode(struct ufs_pa_layer_attr *p, char *result); | ||
29 | static int ufs_qcom_get_bus_vote(struct ufs_qcom_host *host, | ||
30 | const char *speed_mode); | ||
31 | static int ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote); | 47 | static int ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote); |
48 | static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host); | ||
49 | static int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba, | ||
50 | u32 clk_cycles); | ||
51 | |||
52 | static void ufs_qcom_dump_regs(struct ufs_hba *hba, int offset, int len, | ||
53 | char *prefix) | ||
54 | { | ||
55 | print_hex_dump(KERN_ERR, prefix, | ||
56 | len > 4 ? DUMP_PREFIX_OFFSET : DUMP_PREFIX_NONE, | ||
57 | 16, 4, (void __force *)hba->mmio_base + offset, | ||
58 | len * 4, false); | ||
59 | } | ||
32 | 60 | ||
33 | static int ufs_qcom_get_connected_tx_lanes(struct ufs_hba *hba, u32 *tx_lanes) | 61 | static int ufs_qcom_get_connected_tx_lanes(struct ufs_hba *hba, u32 *tx_lanes) |
34 | { | 62 | { |
@@ -149,13 +177,14 @@ static int ufs_qcom_init_lane_clks(struct ufs_qcom_host *host) | |||
149 | 177 | ||
150 | err = ufs_qcom_host_clk_get(dev, "tx_lane1_sync_clk", | 178 | err = ufs_qcom_host_clk_get(dev, "tx_lane1_sync_clk", |
151 | &host->tx_l1_sync_clk); | 179 | &host->tx_l1_sync_clk); |
180 | |||
152 | out: | 181 | out: |
153 | return err; | 182 | return err; |
154 | } | 183 | } |
155 | 184 | ||
156 | static int ufs_qcom_link_startup_post_change(struct ufs_hba *hba) | 185 | static int ufs_qcom_link_startup_post_change(struct ufs_hba *hba) |
157 | { | 186 | { |
158 | struct ufs_qcom_host *host = hba->priv; | 187 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
159 | struct phy *phy = host->generic_phy; | 188 | struct phy *phy = host->generic_phy; |
160 | u32 tx_lanes; | 189 | u32 tx_lanes; |
161 | int err = 0; | 190 | int err = 0; |
@@ -181,7 +210,9 @@ static int ufs_qcom_check_hibern8(struct ufs_hba *hba) | |||
181 | 210 | ||
182 | do { | 211 | do { |
183 | err = ufshcd_dme_get(hba, | 212 | err = ufshcd_dme_get(hba, |
184 | UIC_ARG_MIB(MPHY_TX_FSM_STATE), &tx_fsm_val); | 213 | UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, |
214 | UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), | ||
215 | &tx_fsm_val); | ||
185 | if (err || tx_fsm_val == TX_FSM_HIBERN8) | 216 | if (err || tx_fsm_val == TX_FSM_HIBERN8) |
186 | break; | 217 | break; |
187 | 218 | ||
@@ -195,7 +226,9 @@ static int ufs_qcom_check_hibern8(struct ufs_hba *hba) | |||
195 | */ | 226 | */ |
196 | if (time_after(jiffies, timeout)) | 227 | if (time_after(jiffies, timeout)) |
197 | err = ufshcd_dme_get(hba, | 228 | err = ufshcd_dme_get(hba, |
198 | UIC_ARG_MIB(MPHY_TX_FSM_STATE), &tx_fsm_val); | 229 | UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, |
230 | UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), | ||
231 | &tx_fsm_val); | ||
199 | 232 | ||
200 | if (err) { | 233 | if (err) { |
201 | dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n", | 234 | dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n", |
@@ -209,9 +242,18 @@ static int ufs_qcom_check_hibern8(struct ufs_hba *hba) | |||
209 | return err; | 242 | return err; |
210 | } | 243 | } |
211 | 244 | ||
245 | static void ufs_qcom_select_unipro_mode(struct ufs_qcom_host *host) | ||
246 | { | ||
247 | ufshcd_rmwl(host->hba, QUNIPRO_SEL, | ||
248 | ufs_qcom_cap_qunipro(host) ? QUNIPRO_SEL : 0, | ||
249 | REG_UFS_CFG1); | ||
250 | /* make sure above configuration is applied before we return */ | ||
251 | mb(); | ||
252 | } | ||
253 | |||
212 | static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) | 254 | static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) |
213 | { | 255 | { |
214 | struct ufs_qcom_host *host = hba->priv; | 256 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
215 | struct phy *phy = host->generic_phy; | 257 | struct phy *phy = host->generic_phy; |
216 | int ret = 0; | 258 | int ret = 0; |
217 | bool is_rate_B = (UFS_QCOM_LIMIT_HS_RATE == PA_HS_MODE_B) | 259 | bool is_rate_B = (UFS_QCOM_LIMIT_HS_RATE == PA_HS_MODE_B) |
@@ -223,9 +265,11 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) | |||
223 | usleep_range(1000, 1100); | 265 | usleep_range(1000, 1100); |
224 | 266 | ||
225 | ret = ufs_qcom_phy_calibrate_phy(phy, is_rate_B); | 267 | ret = ufs_qcom_phy_calibrate_phy(phy, is_rate_B); |
268 | |||
226 | if (ret) { | 269 | if (ret) { |
227 | dev_err(hba->dev, "%s: ufs_qcom_phy_calibrate_phy() failed, ret = %d\n", | 270 | dev_err(hba->dev, |
228 | __func__, ret); | 271 | "%s: ufs_qcom_phy_calibrate_phy()failed, ret = %d\n", |
272 | __func__, ret); | ||
229 | goto out; | 273 | goto out; |
230 | } | 274 | } |
231 | 275 | ||
@@ -246,9 +290,12 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) | |||
246 | 290 | ||
247 | ret = ufs_qcom_phy_is_pcs_ready(phy); | 291 | ret = ufs_qcom_phy_is_pcs_ready(phy); |
248 | if (ret) | 292 | if (ret) |
249 | dev_err(hba->dev, "%s: is_physical_coding_sublayer_ready() failed, ret = %d\n", | 293 | dev_err(hba->dev, |
294 | "%s: is_physical_coding_sublayer_ready() failed, ret = %d\n", | ||
250 | __func__, ret); | 295 | __func__, ret); |
251 | 296 | ||
297 | ufs_qcom_select_unipro_mode(host); | ||
298 | |||
252 | out: | 299 | out: |
253 | return ret; | 300 | return ret; |
254 | } | 301 | } |
@@ -271,9 +318,10 @@ static void ufs_qcom_enable_hw_clk_gating(struct ufs_hba *hba) | |||
271 | mb(); | 318 | mb(); |
272 | } | 319 | } |
273 | 320 | ||
274 | static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba, bool status) | 321 | static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba, |
322 | enum ufs_notify_change_status status) | ||
275 | { | 323 | { |
276 | struct ufs_qcom_host *host = hba->priv; | 324 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
277 | int err = 0; | 325 | int err = 0; |
278 | 326 | ||
279 | switch (status) { | 327 | switch (status) { |
@@ -301,13 +349,13 @@ static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba, bool status) | |||
301 | } | 349 | } |
302 | 350 | ||
303 | /** | 351 | /** |
304 | * Returns non-zero for success (which rate of core_clk) and 0 | 352 | * Returns zero for success and non-zero in case of a failure |
305 | * in case of a failure | ||
306 | */ | 353 | */ |
307 | static unsigned long | 354 | static int ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, |
308 | ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate) | 355 | u32 hs, u32 rate, bool update_link_startup_timer) |
309 | { | 356 | { |
310 | struct ufs_qcom_host *host = hba->priv; | 357 | int ret = 0; |
358 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); | ||
311 | struct ufs_clk_info *clki; | 359 | struct ufs_clk_info *clki; |
312 | u32 core_clk_period_in_ns; | 360 | u32 core_clk_period_in_ns; |
313 | u32 tx_clk_cycles_per_us = 0; | 361 | u32 tx_clk_cycles_per_us = 0; |
@@ -324,11 +372,13 @@ ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate) | |||
324 | static u32 hs_fr_table_rA[][2] = { | 372 | static u32 hs_fr_table_rA[][2] = { |
325 | {UFS_HS_G1, 0x1F}, | 373 | {UFS_HS_G1, 0x1F}, |
326 | {UFS_HS_G2, 0x3e}, | 374 | {UFS_HS_G2, 0x3e}, |
375 | {UFS_HS_G3, 0x7D}, | ||
327 | }; | 376 | }; |
328 | 377 | ||
329 | static u32 hs_fr_table_rB[][2] = { | 378 | static u32 hs_fr_table_rB[][2] = { |
330 | {UFS_HS_G1, 0x24}, | 379 | {UFS_HS_G1, 0x24}, |
331 | {UFS_HS_G2, 0x49}, | 380 | {UFS_HS_G2, 0x49}, |
381 | {UFS_HS_G3, 0x92}, | ||
332 | }; | 382 | }; |
333 | 383 | ||
334 | /* | 384 | /* |
@@ -356,7 +406,17 @@ ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate) | |||
356 | core_clk_rate = DEFAULT_CLK_RATE_HZ; | 406 | core_clk_rate = DEFAULT_CLK_RATE_HZ; |
357 | 407 | ||
358 | core_clk_cycles_per_us = core_clk_rate / USEC_PER_SEC; | 408 | core_clk_cycles_per_us = core_clk_rate / USEC_PER_SEC; |
359 | ufshcd_writel(hba, core_clk_cycles_per_us, REG_UFS_SYS1CLK_1US); | 409 | if (ufshcd_readl(hba, REG_UFS_SYS1CLK_1US) != core_clk_cycles_per_us) { |
410 | ufshcd_writel(hba, core_clk_cycles_per_us, REG_UFS_SYS1CLK_1US); | ||
411 | /* | ||
412 | * make sure above write gets applied before we return from | ||
413 | * this function. | ||
414 | */ | ||
415 | mb(); | ||
416 | } | ||
417 | |||
418 | if (ufs_qcom_cap_qunipro(host)) | ||
419 | goto out; | ||
360 | 420 | ||
361 | core_clk_period_in_ns = NSEC_PER_SEC / core_clk_rate; | 421 | core_clk_period_in_ns = NSEC_PER_SEC / core_clk_rate; |
362 | core_clk_period_in_ns <<= OFFSET_CLK_NS_REG; | 422 | core_clk_period_in_ns <<= OFFSET_CLK_NS_REG; |
@@ -406,35 +466,59 @@ ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate) | |||
406 | goto out_error; | 466 | goto out_error; |
407 | } | 467 | } |
408 | 468 | ||
409 | /* this register 2 fields shall be written at once */ | 469 | if (ufshcd_readl(hba, REG_UFS_TX_SYMBOL_CLK_NS_US) != |
410 | ufshcd_writel(hba, core_clk_period_in_ns | tx_clk_cycles_per_us, | 470 | (core_clk_period_in_ns | tx_clk_cycles_per_us)) { |
411 | REG_UFS_TX_SYMBOL_CLK_NS_US); | 471 | /* this register 2 fields shall be written at once */ |
472 | ufshcd_writel(hba, core_clk_period_in_ns | tx_clk_cycles_per_us, | ||
473 | REG_UFS_TX_SYMBOL_CLK_NS_US); | ||
474 | /* | ||
475 | * make sure above write gets applied before we return from | ||
476 | * this function. | ||
477 | */ | ||
478 | mb(); | ||
479 | } | ||
480 | |||
481 | if (update_link_startup_timer) { | ||
482 | ufshcd_writel(hba, ((core_clk_rate / MSEC_PER_SEC) * 100), | ||
483 | REG_UFS_PA_LINK_STARTUP_TIMER); | ||
484 | /* | ||
485 | * make sure that this configuration is applied before | ||
486 | * we return | ||
487 | */ | ||
488 | mb(); | ||
489 | } | ||
412 | goto out; | 490 | goto out; |
413 | 491 | ||
414 | out_error: | 492 | out_error: |
415 | core_clk_rate = 0; | 493 | ret = -EINVAL; |
416 | out: | 494 | out: |
417 | return core_clk_rate; | 495 | return ret; |
418 | } | 496 | } |
419 | 497 | ||
420 | static int ufs_qcom_link_startup_notify(struct ufs_hba *hba, bool status) | 498 | static int ufs_qcom_link_startup_notify(struct ufs_hba *hba, |
499 | enum ufs_notify_change_status status) | ||
421 | { | 500 | { |
422 | unsigned long core_clk_rate = 0; | 501 | int err = 0; |
423 | u32 core_clk_cycles_per_100ms; | 502 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
424 | 503 | ||
425 | switch (status) { | 504 | switch (status) { |
426 | case PRE_CHANGE: | 505 | case PRE_CHANGE: |
427 | core_clk_rate = ufs_qcom_cfg_timers(hba, UFS_PWM_G1, | 506 | if (ufs_qcom_cfg_timers(hba, UFS_PWM_G1, SLOWAUTO_MODE, |
428 | SLOWAUTO_MODE, 0); | 507 | 0, true)) { |
429 | if (!core_clk_rate) { | ||
430 | dev_err(hba->dev, "%s: ufs_qcom_cfg_timers() failed\n", | 508 | dev_err(hba->dev, "%s: ufs_qcom_cfg_timers() failed\n", |
431 | __func__); | 509 | __func__); |
432 | return -EINVAL; | 510 | err = -EINVAL; |
511 | goto out; | ||
433 | } | 512 | } |
434 | core_clk_cycles_per_100ms = | 513 | |
435 | (core_clk_rate / MSEC_PER_SEC) * 100; | 514 | if (ufs_qcom_cap_qunipro(host)) |
436 | ufshcd_writel(hba, core_clk_cycles_per_100ms, | 515 | /* |
437 | REG_UFS_PA_LINK_STARTUP_TIMER); | 516 | * set unipro core clock cycles to 150 & clear clock |
517 | * divider | ||
518 | */ | ||
519 | err = ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba, | ||
520 | 150); | ||
521 | |||
438 | break; | 522 | break; |
439 | case POST_CHANGE: | 523 | case POST_CHANGE: |
440 | ufs_qcom_link_startup_post_change(hba); | 524 | ufs_qcom_link_startup_post_change(hba); |
@@ -443,12 +527,13 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba *hba, bool status) | |||
443 | break; | 527 | break; |
444 | } | 528 | } |
445 | 529 | ||
446 | return 0; | 530 | out: |
531 | return err; | ||
447 | } | 532 | } |
448 | 533 | ||
449 | static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) | 534 | static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) |
450 | { | 535 | { |
451 | struct ufs_qcom_host *host = hba->priv; | 536 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
452 | struct phy *phy = host->generic_phy; | 537 | struct phy *phy = host->generic_phy; |
453 | int ret = 0; | 538 | int ret = 0; |
454 | 539 | ||
@@ -470,8 +555,10 @@ static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) | |||
470 | * If UniPro link is not active, PHY ref_clk, main PHY analog power | 555 | * If UniPro link is not active, PHY ref_clk, main PHY analog power |
471 | * rail and low noise analog power rail for PLL can be switched off. | 556 | * rail and low noise analog power rail for PLL can be switched off. |
472 | */ | 557 | */ |
473 | if (!ufs_qcom_is_link_active(hba)) | 558 | if (!ufs_qcom_is_link_active(hba)) { |
559 | ufs_qcom_disable_lane_clks(host); | ||
474 | phy_power_off(phy); | 560 | phy_power_off(phy); |
561 | } | ||
475 | 562 | ||
476 | out: | 563 | out: |
477 | return ret; | 564 | return ret; |
@@ -479,7 +566,7 @@ out: | |||
479 | 566 | ||
480 | static int ufs_qcom_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) | 567 | static int ufs_qcom_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) |
481 | { | 568 | { |
482 | struct ufs_qcom_host *host = hba->priv; | 569 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
483 | struct phy *phy = host->generic_phy; | 570 | struct phy *phy = host->generic_phy; |
484 | int err; | 571 | int err; |
485 | 572 | ||
@@ -490,6 +577,10 @@ static int ufs_qcom_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) | |||
490 | goto out; | 577 | goto out; |
491 | } | 578 | } |
492 | 579 | ||
580 | err = ufs_qcom_enable_lane_clks(host); | ||
581 | if (err) | ||
582 | goto out; | ||
583 | |||
493 | hba->is_sys_suspended = false; | 584 | hba->is_sys_suspended = false; |
494 | 585 | ||
495 | out: | 586 | out: |
@@ -594,6 +685,81 @@ static int ufs_qcom_get_pwr_dev_param(struct ufs_qcom_dev_params *qcom_param, | |||
594 | return 0; | 685 | return 0; |
595 | } | 686 | } |
596 | 687 | ||
688 | #ifdef CONFIG_MSM_BUS_SCALING | ||
689 | static int ufs_qcom_get_bus_vote(struct ufs_qcom_host *host, | ||
690 | const char *speed_mode) | ||
691 | { | ||
692 | struct device *dev = host->hba->dev; | ||
693 | struct device_node *np = dev->of_node; | ||
694 | int err; | ||
695 | const char *key = "qcom,bus-vector-names"; | ||
696 | |||
697 | if (!speed_mode) { | ||
698 | err = -EINVAL; | ||
699 | goto out; | ||
700 | } | ||
701 | |||
702 | if (host->bus_vote.is_max_bw_needed && !!strcmp(speed_mode, "MIN")) | ||
703 | err = of_property_match_string(np, key, "MAX"); | ||
704 | else | ||
705 | err = of_property_match_string(np, key, speed_mode); | ||
706 | |||
707 | out: | ||
708 | if (err < 0) | ||
709 | dev_err(dev, "%s: Invalid %s mode %d\n", | ||
710 | __func__, speed_mode, err); | ||
711 | return err; | ||
712 | } | ||
713 | |||
714 | static void ufs_qcom_get_speed_mode(struct ufs_pa_layer_attr *p, char *result) | ||
715 | { | ||
716 | int gear = max_t(u32, p->gear_rx, p->gear_tx); | ||
717 | int lanes = max_t(u32, p->lane_rx, p->lane_tx); | ||
718 | int pwr; | ||
719 | |||
720 | /* default to PWM Gear 1, Lane 1 if power mode is not initialized */ | ||
721 | if (!gear) | ||
722 | gear = 1; | ||
723 | |||
724 | if (!lanes) | ||
725 | lanes = 1; | ||
726 | |||
727 | if (!p->pwr_rx && !p->pwr_tx) { | ||
728 | pwr = SLOWAUTO_MODE; | ||
729 | snprintf(result, BUS_VECTOR_NAME_LEN, "MIN"); | ||
730 | } else if (p->pwr_rx == FAST_MODE || p->pwr_rx == FASTAUTO_MODE || | ||
731 | p->pwr_tx == FAST_MODE || p->pwr_tx == FASTAUTO_MODE) { | ||
732 | pwr = FAST_MODE; | ||
733 | snprintf(result, BUS_VECTOR_NAME_LEN, "%s_R%s_G%d_L%d", "HS", | ||
734 | p->hs_rate == PA_HS_MODE_B ? "B" : "A", gear, lanes); | ||
735 | } else { | ||
736 | pwr = SLOW_MODE; | ||
737 | snprintf(result, BUS_VECTOR_NAME_LEN, "%s_G%d_L%d", | ||
738 | "PWM", gear, lanes); | ||
739 | } | ||
740 | } | ||
741 | |||
742 | static int ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote) | ||
743 | { | ||
744 | int err = 0; | ||
745 | |||
746 | if (vote != host->bus_vote.curr_vote) { | ||
747 | err = msm_bus_scale_client_update_request( | ||
748 | host->bus_vote.client_handle, vote); | ||
749 | if (err) { | ||
750 | dev_err(host->hba->dev, | ||
751 | "%s: msm_bus_scale_client_update_request() failed: bus_client_handle=0x%x, vote=%d, err=%d\n", | ||
752 | __func__, host->bus_vote.client_handle, | ||
753 | vote, err); | ||
754 | goto out; | ||
755 | } | ||
756 | |||
757 | host->bus_vote.curr_vote = vote; | ||
758 | } | ||
759 | out: | ||
760 | return err; | ||
761 | } | ||
762 | |||
597 | static int ufs_qcom_update_bus_bw_vote(struct ufs_qcom_host *host) | 763 | static int ufs_qcom_update_bus_bw_vote(struct ufs_qcom_host *host) |
598 | { | 764 | { |
599 | int vote; | 765 | int vote; |
@@ -615,13 +781,137 @@ static int ufs_qcom_update_bus_bw_vote(struct ufs_qcom_host *host) | |||
615 | return err; | 781 | return err; |
616 | } | 782 | } |
617 | 783 | ||
784 | static ssize_t | ||
785 | show_ufs_to_mem_max_bus_bw(struct device *dev, struct device_attribute *attr, | ||
786 | char *buf) | ||
787 | { | ||
788 | struct ufs_hba *hba = dev_get_drvdata(dev); | ||
789 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); | ||
790 | |||
791 | return snprintf(buf, PAGE_SIZE, "%u\n", | ||
792 | host->bus_vote.is_max_bw_needed); | ||
793 | } | ||
794 | |||
795 | static ssize_t | ||
796 | store_ufs_to_mem_max_bus_bw(struct device *dev, struct device_attribute *attr, | ||
797 | const char *buf, size_t count) | ||
798 | { | ||
799 | struct ufs_hba *hba = dev_get_drvdata(dev); | ||
800 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); | ||
801 | uint32_t value; | ||
802 | |||
803 | if (!kstrtou32(buf, 0, &value)) { | ||
804 | host->bus_vote.is_max_bw_needed = !!value; | ||
805 | ufs_qcom_update_bus_bw_vote(host); | ||
806 | } | ||
807 | |||
808 | return count; | ||
809 | } | ||
810 | |||
811 | static int ufs_qcom_bus_register(struct ufs_qcom_host *host) | ||
812 | { | ||
813 | int err; | ||
814 | struct msm_bus_scale_pdata *bus_pdata; | ||
815 | struct device *dev = host->hba->dev; | ||
816 | struct platform_device *pdev = to_platform_device(dev); | ||
817 | struct device_node *np = dev->of_node; | ||
818 | |||
819 | bus_pdata = msm_bus_cl_get_pdata(pdev); | ||
820 | if (!bus_pdata) { | ||
821 | dev_err(dev, "%s: failed to get bus vectors\n", __func__); | ||
822 | err = -ENODATA; | ||
823 | goto out; | ||
824 | } | ||
825 | |||
826 | err = of_property_count_strings(np, "qcom,bus-vector-names"); | ||
827 | if (err < 0 || err != bus_pdata->num_usecases) { | ||
828 | dev_err(dev, "%s: qcom,bus-vector-names not specified correctly %d\n", | ||
829 | __func__, err); | ||
830 | goto out; | ||
831 | } | ||
832 | |||
833 | host->bus_vote.client_handle = msm_bus_scale_register_client(bus_pdata); | ||
834 | if (!host->bus_vote.client_handle) { | ||
835 | dev_err(dev, "%s: msm_bus_scale_register_client failed\n", | ||
836 | __func__); | ||
837 | err = -EFAULT; | ||
838 | goto out; | ||
839 | } | ||
840 | |||
841 | /* cache the vote index for minimum and maximum bandwidth */ | ||
842 | host->bus_vote.min_bw_vote = ufs_qcom_get_bus_vote(host, "MIN"); | ||
843 | host->bus_vote.max_bw_vote = ufs_qcom_get_bus_vote(host, "MAX"); | ||
844 | |||
845 | host->bus_vote.max_bus_bw.show = show_ufs_to_mem_max_bus_bw; | ||
846 | host->bus_vote.max_bus_bw.store = store_ufs_to_mem_max_bus_bw; | ||
847 | sysfs_attr_init(&host->bus_vote.max_bus_bw.attr); | ||
848 | host->bus_vote.max_bus_bw.attr.name = "max_bus_bw"; | ||
849 | host->bus_vote.max_bus_bw.attr.mode = S_IRUGO | S_IWUSR; | ||
850 | err = device_create_file(dev, &host->bus_vote.max_bus_bw); | ||
851 | out: | ||
852 | return err; | ||
853 | } | ||
854 | #else /* CONFIG_MSM_BUS_SCALING */ | ||
855 | static int ufs_qcom_update_bus_bw_vote(struct ufs_qcom_host *host) | ||
856 | { | ||
857 | return 0; | ||
858 | } | ||
859 | |||
860 | static int ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote) | ||
861 | { | ||
862 | return 0; | ||
863 | } | ||
864 | |||
865 | static int ufs_qcom_bus_register(struct ufs_qcom_host *host) | ||
866 | { | ||
867 | return 0; | ||
868 | } | ||
869 | #endif /* CONFIG_MSM_BUS_SCALING */ | ||
870 | |||
871 | static void ufs_qcom_dev_ref_clk_ctrl(struct ufs_qcom_host *host, bool enable) | ||
872 | { | ||
873 | if (host->dev_ref_clk_ctrl_mmio && | ||
874 | (enable ^ host->is_dev_ref_clk_enabled)) { | ||
875 | u32 temp = readl_relaxed(host->dev_ref_clk_ctrl_mmio); | ||
876 | |||
877 | if (enable) | ||
878 | temp |= host->dev_ref_clk_en_mask; | ||
879 | else | ||
880 | temp &= ~host->dev_ref_clk_en_mask; | ||
881 | |||
882 | /* | ||
883 | * If we are here to disable this clock it might be immediately | ||
884 | * after entering into hibern8 in which case we need to make | ||
885 | * sure that device ref_clk is active at least 1us after the | ||
886 | * hibern8 enter. | ||
887 | */ | ||
888 | if (!enable) | ||
889 | udelay(1); | ||
890 | |||
891 | writel_relaxed(temp, host->dev_ref_clk_ctrl_mmio); | ||
892 | |||
893 | /* ensure that ref_clk is enabled/disabled before we return */ | ||
894 | wmb(); | ||
895 | |||
896 | /* | ||
897 | * If we call hibern8 exit after this, we need to make sure that | ||
898 | * device ref_clk is stable for at least 1us before the hibern8 | ||
899 | * exit command. | ||
900 | */ | ||
901 | if (enable) | ||
902 | udelay(1); | ||
903 | |||
904 | host->is_dev_ref_clk_enabled = enable; | ||
905 | } | ||
906 | } | ||
907 | |||
618 | static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, | 908 | static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, |
619 | bool status, | 909 | enum ufs_notify_change_status status, |
620 | struct ufs_pa_layer_attr *dev_max_params, | 910 | struct ufs_pa_layer_attr *dev_max_params, |
621 | struct ufs_pa_layer_attr *dev_req_params) | 911 | struct ufs_pa_layer_attr *dev_req_params) |
622 | { | 912 | { |
623 | u32 val; | 913 | u32 val; |
624 | struct ufs_qcom_host *host = hba->priv; | 914 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
625 | struct phy *phy = host->generic_phy; | 915 | struct phy *phy = host->generic_phy; |
626 | struct ufs_qcom_dev_params ufs_qcom_cap; | 916 | struct ufs_qcom_dev_params ufs_qcom_cap; |
627 | int ret = 0; | 917 | int ret = 0; |
@@ -649,6 +939,20 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, | |||
649 | ufs_qcom_cap.desired_working_mode = | 939 | ufs_qcom_cap.desired_working_mode = |
650 | UFS_QCOM_LIMIT_DESIRED_MODE; | 940 | UFS_QCOM_LIMIT_DESIRED_MODE; |
651 | 941 | ||
942 | if (host->hw_ver.major == 0x1) { | ||
943 | /* | ||
944 | * HS-G3 operations may not reliably work on legacy QCOM | ||
945 | * UFS host controller hardware even though capability | ||
946 | * exchange during link startup phase may end up | ||
947 | * negotiating maximum supported gear as G3. | ||
948 | * Hence downgrade the maximum supported gear to HS-G2. | ||
949 | */ | ||
950 | if (ufs_qcom_cap.hs_tx_gear > UFS_HS_G2) | ||
951 | ufs_qcom_cap.hs_tx_gear = UFS_HS_G2; | ||
952 | if (ufs_qcom_cap.hs_rx_gear > UFS_HS_G2) | ||
953 | ufs_qcom_cap.hs_rx_gear = UFS_HS_G2; | ||
954 | } | ||
955 | |||
652 | ret = ufs_qcom_get_pwr_dev_param(&ufs_qcom_cap, | 956 | ret = ufs_qcom_get_pwr_dev_param(&ufs_qcom_cap, |
653 | dev_max_params, | 957 | dev_max_params, |
654 | dev_req_params); | 958 | dev_req_params); |
@@ -660,9 +964,9 @@ static int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, | |||
660 | 964 | ||
661 | break; | 965 | break; |
662 | case POST_CHANGE: | 966 | case POST_CHANGE: |
663 | if (!ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx, | 967 | if (ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx, |
664 | dev_req_params->pwr_rx, | 968 | dev_req_params->pwr_rx, |
665 | dev_req_params->hs_rate)) { | 969 | dev_req_params->hs_rate, false)) { |
666 | dev_err(hba->dev, "%s: ufs_qcom_cfg_timers() failed\n", | 970 | dev_err(hba->dev, "%s: ufs_qcom_cfg_timers() failed\n", |
667 | __func__); | 971 | __func__); |
668 | /* | 972 | /* |
@@ -696,7 +1000,7 @@ out: | |||
696 | 1000 | ||
697 | static u32 ufs_qcom_get_ufs_hci_version(struct ufs_hba *hba) | 1001 | static u32 ufs_qcom_get_ufs_hci_version(struct ufs_hba *hba) |
698 | { | 1002 | { |
699 | struct ufs_qcom_host *host = hba->priv; | 1003 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
700 | 1004 | ||
701 | if (host->hw_ver.major == 0x1) | 1005 | if (host->hw_ver.major == 0x1) |
702 | return UFSHCI_VERSION_11; | 1006 | return UFSHCI_VERSION_11; |
@@ -715,7 +1019,7 @@ static u32 ufs_qcom_get_ufs_hci_version(struct ufs_hba *hba) | |||
715 | */ | 1019 | */ |
716 | static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) | 1020 | static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) |
717 | { | 1021 | { |
718 | struct ufs_qcom_host *host = hba->priv; | 1022 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
719 | 1023 | ||
720 | if (host->hw_ver.major == 0x01) { | 1024 | if (host->hw_ver.major == 0x01) { |
721 | hba->quirks |= UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS | 1025 | hba->quirks |= UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS |
@@ -724,10 +1028,11 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) | |||
724 | 1028 | ||
725 | if (host->hw_ver.minor == 0x0001 && host->hw_ver.step == 0x0001) | 1029 | if (host->hw_ver.minor == 0x0001 && host->hw_ver.step == 0x0001) |
726 | hba->quirks |= UFSHCD_QUIRK_BROKEN_INTR_AGGR; | 1030 | hba->quirks |= UFSHCD_QUIRK_BROKEN_INTR_AGGR; |
1031 | |||
1032 | hba->quirks |= UFSHCD_QUIRK_BROKEN_LCC; | ||
727 | } | 1033 | } |
728 | 1034 | ||
729 | if (host->hw_ver.major >= 0x2) { | 1035 | if (host->hw_ver.major >= 0x2) { |
730 | hba->quirks |= UFSHCD_QUIRK_BROKEN_LCC; | ||
731 | hba->quirks |= UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION; | 1036 | hba->quirks |= UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION; |
732 | 1037 | ||
733 | if (!ufs_qcom_cap_qunipro(host)) | 1038 | if (!ufs_qcom_cap_qunipro(host)) |
@@ -740,79 +1045,29 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) | |||
740 | 1045 | ||
741 | static void ufs_qcom_set_caps(struct ufs_hba *hba) | 1046 | static void ufs_qcom_set_caps(struct ufs_hba *hba) |
742 | { | 1047 | { |
743 | struct ufs_qcom_host *host = hba->priv; | 1048 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
744 | 1049 | ||
745 | if (host->hw_ver.major >= 0x2) | 1050 | hba->caps |= UFSHCD_CAP_CLK_GATING | UFSHCD_CAP_HIBERN8_WITH_CLK_GATING; |
746 | host->caps = UFS_QCOM_CAP_QUNIPRO; | 1051 | hba->caps |= UFSHCD_CAP_CLK_SCALING; |
747 | } | 1052 | hba->caps |= UFSHCD_CAP_AUTO_BKOPS_SUSPEND; |
748 | |||
749 | static int ufs_qcom_get_bus_vote(struct ufs_qcom_host *host, | ||
750 | const char *speed_mode) | ||
751 | { | ||
752 | struct device *dev = host->hba->dev; | ||
753 | struct device_node *np = dev->of_node; | ||
754 | int err; | ||
755 | const char *key = "qcom,bus-vector-names"; | ||
756 | |||
757 | if (!speed_mode) { | ||
758 | err = -EINVAL; | ||
759 | goto out; | ||
760 | } | ||
761 | |||
762 | if (host->bus_vote.is_max_bw_needed && !!strcmp(speed_mode, "MIN")) | ||
763 | err = of_property_match_string(np, key, "MAX"); | ||
764 | else | ||
765 | err = of_property_match_string(np, key, speed_mode); | ||
766 | |||
767 | out: | ||
768 | if (err < 0) | ||
769 | dev_err(dev, "%s: Invalid %s mode %d\n", | ||
770 | __func__, speed_mode, err); | ||
771 | return err; | ||
772 | } | ||
773 | |||
774 | static int ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote) | ||
775 | { | ||
776 | int err = 0; | ||
777 | |||
778 | if (vote != host->bus_vote.curr_vote) | ||
779 | host->bus_vote.curr_vote = vote; | ||
780 | |||
781 | return err; | ||
782 | } | ||
783 | |||
784 | static void ufs_qcom_get_speed_mode(struct ufs_pa_layer_attr *p, char *result) | ||
785 | { | ||
786 | int gear = max_t(u32, p->gear_rx, p->gear_tx); | ||
787 | int lanes = max_t(u32, p->lane_rx, p->lane_tx); | ||
788 | int pwr; | ||
789 | |||
790 | /* default to PWM Gear 1, Lane 1 if power mode is not initialized */ | ||
791 | if (!gear) | ||
792 | gear = 1; | ||
793 | |||
794 | if (!lanes) | ||
795 | lanes = 1; | ||
796 | 1053 | ||
797 | if (!p->pwr_rx && !p->pwr_tx) { | 1054 | if (host->hw_ver.major >= 0x2) { |
798 | pwr = SLOWAUTO_MODE; | 1055 | host->caps = UFS_QCOM_CAP_QUNIPRO | |
799 | snprintf(result, BUS_VECTOR_NAME_LEN, "MIN"); | 1056 | UFS_QCOM_CAP_RETAIN_SEC_CFG_AFTER_PWR_COLLAPSE; |
800 | } else if (p->pwr_rx == FAST_MODE || p->pwr_rx == FASTAUTO_MODE || | ||
801 | p->pwr_tx == FAST_MODE || p->pwr_tx == FASTAUTO_MODE) { | ||
802 | pwr = FAST_MODE; | ||
803 | snprintf(result, BUS_VECTOR_NAME_LEN, "%s_R%s_G%d_L%d", "HS", | ||
804 | p->hs_rate == PA_HS_MODE_B ? "B" : "A", gear, lanes); | ||
805 | } else { | ||
806 | pwr = SLOW_MODE; | ||
807 | snprintf(result, BUS_VECTOR_NAME_LEN, "%s_G%d_L%d", | ||
808 | "PWM", gear, lanes); | ||
809 | } | 1057 | } |
810 | } | 1058 | } |
811 | 1059 | ||
1060 | /** | ||
1061 | * ufs_qcom_setup_clocks - enables/disable clocks | ||
1062 | * @hba: host controller instance | ||
1063 | * @on: If true, enable clocks else disable them. | ||
1064 | * | ||
1065 | * Returns 0 on success, non-zero on failure. | ||
1066 | */ | ||
812 | static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on) | 1067 | static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on) |
813 | { | 1068 | { |
814 | struct ufs_qcom_host *host = hba->priv; | 1069 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
815 | int err = 0; | 1070 | int err; |
816 | int vote = 0; | 1071 | int vote = 0; |
817 | 1072 | ||
818 | /* | 1073 | /* |
@@ -835,20 +1090,18 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on) | |||
835 | ufs_qcom_phy_disable_iface_clk(host->generic_phy); | 1090 | ufs_qcom_phy_disable_iface_clk(host->generic_phy); |
836 | goto out; | 1091 | goto out; |
837 | } | 1092 | } |
838 | /* enable the device ref clock */ | ||
839 | ufs_qcom_phy_enable_dev_ref_clk(host->generic_phy); | ||
840 | vote = host->bus_vote.saved_vote; | 1093 | vote = host->bus_vote.saved_vote; |
841 | if (vote == host->bus_vote.min_bw_vote) | 1094 | if (vote == host->bus_vote.min_bw_vote) |
842 | ufs_qcom_update_bus_bw_vote(host); | 1095 | ufs_qcom_update_bus_bw_vote(host); |
1096 | |||
843 | } else { | 1097 | } else { |
1098 | |||
844 | /* M-PHY RMMI interface clocks can be turned off */ | 1099 | /* M-PHY RMMI interface clocks can be turned off */ |
845 | ufs_qcom_phy_disable_iface_clk(host->generic_phy); | 1100 | ufs_qcom_phy_disable_iface_clk(host->generic_phy); |
846 | if (!ufs_qcom_is_link_active(hba)) { | 1101 | if (!ufs_qcom_is_link_active(hba)) |
847 | /* turn off UFS local PHY ref_clk */ | ||
848 | ufs_qcom_phy_disable_ref_clk(host->generic_phy); | ||
849 | /* disable device ref_clk */ | 1102 | /* disable device ref_clk */ |
850 | ufs_qcom_phy_disable_dev_ref_clk(host->generic_phy); | 1103 | ufs_qcom_dev_ref_clk_ctrl(host, false); |
851 | } | 1104 | |
852 | vote = host->bus_vote.min_bw_vote; | 1105 | vote = host->bus_vote.min_bw_vote; |
853 | } | 1106 | } |
854 | 1107 | ||
@@ -861,68 +1114,17 @@ out: | |||
861 | return err; | 1114 | return err; |
862 | } | 1115 | } |
863 | 1116 | ||
864 | static ssize_t | ||
865 | show_ufs_to_mem_max_bus_bw(struct device *dev, struct device_attribute *attr, | ||
866 | char *buf) | ||
867 | { | ||
868 | struct ufs_hba *hba = dev_get_drvdata(dev); | ||
869 | struct ufs_qcom_host *host = hba->priv; | ||
870 | |||
871 | return snprintf(buf, PAGE_SIZE, "%u\n", | ||
872 | host->bus_vote.is_max_bw_needed); | ||
873 | } | ||
874 | |||
875 | static ssize_t | ||
876 | store_ufs_to_mem_max_bus_bw(struct device *dev, struct device_attribute *attr, | ||
877 | const char *buf, size_t count) | ||
878 | { | ||
879 | struct ufs_hba *hba = dev_get_drvdata(dev); | ||
880 | struct ufs_qcom_host *host = hba->priv; | ||
881 | uint32_t value; | ||
882 | |||
883 | if (!kstrtou32(buf, 0, &value)) { | ||
884 | host->bus_vote.is_max_bw_needed = !!value; | ||
885 | ufs_qcom_update_bus_bw_vote(host); | ||
886 | } | ||
887 | |||
888 | return count; | ||
889 | } | ||
890 | |||
891 | static int ufs_qcom_bus_register(struct ufs_qcom_host *host) | ||
892 | { | ||
893 | int err; | ||
894 | struct device *dev = host->hba->dev; | ||
895 | struct device_node *np = dev->of_node; | ||
896 | |||
897 | err = of_property_count_strings(np, "qcom,bus-vector-names"); | ||
898 | if (err < 0 ) { | ||
899 | dev_err(dev, "%s: qcom,bus-vector-names not specified correctly %d\n", | ||
900 | __func__, err); | ||
901 | goto out; | ||
902 | } | ||
903 | |||
904 | /* cache the vote index for minimum and maximum bandwidth */ | ||
905 | host->bus_vote.min_bw_vote = ufs_qcom_get_bus_vote(host, "MIN"); | ||
906 | host->bus_vote.max_bw_vote = ufs_qcom_get_bus_vote(host, "MAX"); | ||
907 | |||
908 | host->bus_vote.max_bus_bw.show = show_ufs_to_mem_max_bus_bw; | ||
909 | host->bus_vote.max_bus_bw.store = store_ufs_to_mem_max_bus_bw; | ||
910 | sysfs_attr_init(&host->bus_vote.max_bus_bw.attr); | ||
911 | host->bus_vote.max_bus_bw.attr.name = "max_bus_bw"; | ||
912 | host->bus_vote.max_bus_bw.attr.mode = S_IRUGO | S_IWUSR; | ||
913 | err = device_create_file(dev, &host->bus_vote.max_bus_bw); | ||
914 | out: | ||
915 | return err; | ||
916 | } | ||
917 | |||
918 | #define ANDROID_BOOT_DEV_MAX 30 | 1117 | #define ANDROID_BOOT_DEV_MAX 30 |
919 | static char android_boot_dev[ANDROID_BOOT_DEV_MAX]; | 1118 | static char android_boot_dev[ANDROID_BOOT_DEV_MAX]; |
920 | static int get_android_boot_dev(char *str) | 1119 | |
1120 | #ifndef MODULE | ||
1121 | static int __init get_android_boot_dev(char *str) | ||
921 | { | 1122 | { |
922 | strlcpy(android_boot_dev, str, ANDROID_BOOT_DEV_MAX); | 1123 | strlcpy(android_boot_dev, str, ANDROID_BOOT_DEV_MAX); |
923 | return 1; | 1124 | return 1; |
924 | } | 1125 | } |
925 | __setup("androidboot.bootdevice=", get_android_boot_dev); | 1126 | __setup("androidboot.bootdevice=", get_android_boot_dev); |
1127 | #endif | ||
926 | 1128 | ||
927 | /** | 1129 | /** |
928 | * ufs_qcom_init - bind phy with controller | 1130 | * ufs_qcom_init - bind phy with controller |
@@ -938,7 +1140,9 @@ static int ufs_qcom_init(struct ufs_hba *hba) | |||
938 | { | 1140 | { |
939 | int err; | 1141 | int err; |
940 | struct device *dev = hba->dev; | 1142 | struct device *dev = hba->dev; |
1143 | struct platform_device *pdev = to_platform_device(dev); | ||
941 | struct ufs_qcom_host *host; | 1144 | struct ufs_qcom_host *host; |
1145 | struct resource *res; | ||
942 | 1146 | ||
943 | if (strlen(android_boot_dev) && strcmp(android_boot_dev, dev_name(dev))) | 1147 | if (strlen(android_boot_dev) && strcmp(android_boot_dev, dev_name(dev))) |
944 | return -ENODEV; | 1148 | return -ENODEV; |
@@ -950,9 +1154,15 @@ static int ufs_qcom_init(struct ufs_hba *hba) | |||
950 | goto out; | 1154 | goto out; |
951 | } | 1155 | } |
952 | 1156 | ||
1157 | /* Make a two way bind between the qcom host and the hba */ | ||
953 | host->hba = hba; | 1158 | host->hba = hba; |
954 | hba->priv = (void *)host; | 1159 | ufshcd_set_variant(hba, host); |
955 | 1160 | ||
1161 | /* | ||
1162 | * voting/devoting device ref_clk source is time consuming hence | ||
1163 | * skip devoting it during aggressive clock gating. This clock | ||
1164 | * will still be gated off during runtime suspend. | ||
1165 | */ | ||
956 | host->generic_phy = devm_phy_get(dev, "ufsphy"); | 1166 | host->generic_phy = devm_phy_get(dev, "ufsphy"); |
957 | 1167 | ||
958 | if (IS_ERR(host->generic_phy)) { | 1168 | if (IS_ERR(host->generic_phy)) { |
@@ -968,6 +1178,30 @@ static int ufs_qcom_init(struct ufs_hba *hba) | |||
968 | ufs_qcom_get_controller_revision(hba, &host->hw_ver.major, | 1178 | ufs_qcom_get_controller_revision(hba, &host->hw_ver.major, |
969 | &host->hw_ver.minor, &host->hw_ver.step); | 1179 | &host->hw_ver.minor, &host->hw_ver.step); |
970 | 1180 | ||
1181 | /* | ||
1182 | * for newer controllers, device reference clock control bit has | ||
1183 | * moved inside UFS controller register address space itself. | ||
1184 | */ | ||
1185 | if (host->hw_ver.major >= 0x02) { | ||
1186 | host->dev_ref_clk_ctrl_mmio = hba->mmio_base + REG_UFS_CFG1; | ||
1187 | host->dev_ref_clk_en_mask = BIT(26); | ||
1188 | } else { | ||
1189 | /* "dev_ref_clk_ctrl_mem" is optional resource */ | ||
1190 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
1191 | if (res) { | ||
1192 | host->dev_ref_clk_ctrl_mmio = | ||
1193 | devm_ioremap_resource(dev, res); | ||
1194 | if (IS_ERR(host->dev_ref_clk_ctrl_mmio)) { | ||
1195 | dev_warn(dev, | ||
1196 | "%s: could not map dev_ref_clk_ctrl_mmio, err %ld\n", | ||
1197 | __func__, | ||
1198 | PTR_ERR(host->dev_ref_clk_ctrl_mmio)); | ||
1199 | host->dev_ref_clk_ctrl_mmio = NULL; | ||
1200 | } | ||
1201 | host->dev_ref_clk_en_mask = BIT(5); | ||
1202 | } | ||
1203 | } | ||
1204 | |||
971 | /* update phy revision information before calling phy_init() */ | 1205 | /* update phy revision information before calling phy_init() */ |
972 | ufs_qcom_phy_save_controller_version(host->generic_phy, | 1206 | ufs_qcom_phy_save_controller_version(host->generic_phy, |
973 | host->hw_ver.major, host->hw_ver.minor, host->hw_ver.step); | 1207 | host->hw_ver.major, host->hw_ver.minor, host->hw_ver.step); |
@@ -984,14 +1218,20 @@ static int ufs_qcom_init(struct ufs_hba *hba) | |||
984 | ufs_qcom_set_caps(hba); | 1218 | ufs_qcom_set_caps(hba); |
985 | ufs_qcom_advertise_quirks(hba); | 1219 | ufs_qcom_advertise_quirks(hba); |
986 | 1220 | ||
987 | hba->caps |= UFSHCD_CAP_CLK_GATING | UFSHCD_CAP_CLK_SCALING; | ||
988 | hba->caps |= UFSHCD_CAP_AUTO_BKOPS_SUSPEND; | ||
989 | |||
990 | ufs_qcom_setup_clocks(hba, true); | 1221 | ufs_qcom_setup_clocks(hba, true); |
991 | 1222 | ||
992 | if (hba->dev->id < MAX_UFS_QCOM_HOSTS) | 1223 | if (hba->dev->id < MAX_UFS_QCOM_HOSTS) |
993 | ufs_qcom_hosts[hba->dev->id] = host; | 1224 | ufs_qcom_hosts[hba->dev->id] = host; |
994 | 1225 | ||
1226 | host->dbg_print_en |= UFS_QCOM_DEFAULT_DBG_PRINT_EN; | ||
1227 | ufs_qcom_get_default_testbus_cfg(host); | ||
1228 | err = ufs_qcom_testbus_config(host); | ||
1229 | if (err) { | ||
1230 | dev_warn(dev, "%s: failed to configure the testbus %d\n", | ||
1231 | __func__, err); | ||
1232 | err = 0; | ||
1233 | } | ||
1234 | |||
995 | goto out; | 1235 | goto out; |
996 | 1236 | ||
997 | out_disable_phy: | 1237 | out_disable_phy: |
@@ -1000,40 +1240,266 @@ out_unregister_bus: | |||
1000 | phy_exit(host->generic_phy); | 1240 | phy_exit(host->generic_phy); |
1001 | out_host_free: | 1241 | out_host_free: |
1002 | devm_kfree(dev, host); | 1242 | devm_kfree(dev, host); |
1003 | hba->priv = NULL; | 1243 | ufshcd_set_variant(hba, NULL); |
1004 | out: | 1244 | out: |
1005 | return err; | 1245 | return err; |
1006 | } | 1246 | } |
1007 | 1247 | ||
1008 | static void ufs_qcom_exit(struct ufs_hba *hba) | 1248 | static void ufs_qcom_exit(struct ufs_hba *hba) |
1009 | { | 1249 | { |
1010 | struct ufs_qcom_host *host = hba->priv; | 1250 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
1011 | 1251 | ||
1012 | ufs_qcom_disable_lane_clks(host); | 1252 | ufs_qcom_disable_lane_clks(host); |
1013 | phy_power_off(host->generic_phy); | 1253 | phy_power_off(host->generic_phy); |
1014 | } | 1254 | } |
1015 | 1255 | ||
1016 | static | 1256 | static int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba, |
1017 | void ufs_qcom_clk_scale_notify(struct ufs_hba *hba) | 1257 | u32 clk_cycles) |
1258 | { | ||
1259 | int err; | ||
1260 | u32 core_clk_ctrl_reg; | ||
1261 | |||
1262 | if (clk_cycles > DME_VS_CORE_CLK_CTRL_MAX_CORE_CLK_1US_CYCLES_MASK) | ||
1263 | return -EINVAL; | ||
1264 | |||
1265 | err = ufshcd_dme_get(hba, | ||
1266 | UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL), | ||
1267 | &core_clk_ctrl_reg); | ||
1268 | if (err) | ||
1269 | goto out; | ||
1270 | |||
1271 | core_clk_ctrl_reg &= ~DME_VS_CORE_CLK_CTRL_MAX_CORE_CLK_1US_CYCLES_MASK; | ||
1272 | core_clk_ctrl_reg |= clk_cycles; | ||
1273 | |||
1274 | /* Clear CORE_CLK_DIV_EN */ | ||
1275 | core_clk_ctrl_reg &= ~DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT; | ||
1276 | |||
1277 | err = ufshcd_dme_set(hba, | ||
1278 | UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL), | ||
1279 | core_clk_ctrl_reg); | ||
1280 | out: | ||
1281 | return err; | ||
1282 | } | ||
1283 | |||
1284 | static int ufs_qcom_clk_scale_up_pre_change(struct ufs_hba *hba) | ||
1285 | { | ||
1286 | /* nothing to do as of now */ | ||
1287 | return 0; | ||
1288 | } | ||
1289 | |||
1290 | static int ufs_qcom_clk_scale_up_post_change(struct ufs_hba *hba) | ||
1291 | { | ||
1292 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); | ||
1293 | |||
1294 | if (!ufs_qcom_cap_qunipro(host)) | ||
1295 | return 0; | ||
1296 | |||
1297 | /* set unipro core clock cycles to 150 and clear clock divider */ | ||
1298 | return ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba, 150); | ||
1299 | } | ||
1300 | |||
1301 | static int ufs_qcom_clk_scale_down_pre_change(struct ufs_hba *hba) | ||
1302 | { | ||
1303 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); | ||
1304 | int err; | ||
1305 | u32 core_clk_ctrl_reg; | ||
1306 | |||
1307 | if (!ufs_qcom_cap_qunipro(host)) | ||
1308 | return 0; | ||
1309 | |||
1310 | err = ufshcd_dme_get(hba, | ||
1311 | UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL), | ||
1312 | &core_clk_ctrl_reg); | ||
1313 | |||
1314 | /* make sure CORE_CLK_DIV_EN is cleared */ | ||
1315 | if (!err && | ||
1316 | (core_clk_ctrl_reg & DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT)) { | ||
1317 | core_clk_ctrl_reg &= ~DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT; | ||
1318 | err = ufshcd_dme_set(hba, | ||
1319 | UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL), | ||
1320 | core_clk_ctrl_reg); | ||
1321 | } | ||
1322 | |||
1323 | return err; | ||
1324 | } | ||
1325 | |||
1326 | static int ufs_qcom_clk_scale_down_post_change(struct ufs_hba *hba) | ||
1327 | { | ||
1328 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); | ||
1329 | |||
1330 | if (!ufs_qcom_cap_qunipro(host)) | ||
1331 | return 0; | ||
1332 | |||
1333 | /* set unipro core clock cycles to 75 and clear clock divider */ | ||
1334 | return ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba, 75); | ||
1335 | } | ||
1336 | |||
1337 | static int ufs_qcom_clk_scale_notify(struct ufs_hba *hba, | ||
1338 | bool scale_up, enum ufs_notify_change_status status) | ||
1018 | { | 1339 | { |
1019 | struct ufs_qcom_host *host = hba->priv; | 1340 | struct ufs_qcom_host *host = ufshcd_get_variant(hba); |
1020 | struct ufs_pa_layer_attr *dev_req_params = &host->dev_req_params; | 1341 | struct ufs_pa_layer_attr *dev_req_params = &host->dev_req_params; |
1342 | int err = 0; | ||
1021 | 1343 | ||
1022 | if (!dev_req_params) | 1344 | if (status == PRE_CHANGE) { |
1023 | return; | 1345 | if (scale_up) |
1346 | err = ufs_qcom_clk_scale_up_pre_change(hba); | ||
1347 | else | ||
1348 | err = ufs_qcom_clk_scale_down_pre_change(hba); | ||
1349 | } else { | ||
1350 | if (scale_up) | ||
1351 | err = ufs_qcom_clk_scale_up_post_change(hba); | ||
1352 | else | ||
1353 | err = ufs_qcom_clk_scale_down_post_change(hba); | ||
1024 | 1354 | ||
1025 | ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx, | 1355 | if (err || !dev_req_params) |
1026 | dev_req_params->pwr_rx, | 1356 | goto out; |
1027 | dev_req_params->hs_rate); | 1357 | |
1358 | ufs_qcom_cfg_timers(hba, | ||
1359 | dev_req_params->gear_rx, | ||
1360 | dev_req_params->pwr_rx, | ||
1361 | dev_req_params->hs_rate, | ||
1362 | false); | ||
1363 | ufs_qcom_update_bus_bw_vote(host); | ||
1364 | } | ||
1365 | |||
1366 | out: | ||
1367 | return err; | ||
1028 | } | 1368 | } |
1029 | 1369 | ||
1370 | static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host) | ||
1371 | { | ||
1372 | /* provide a legal default configuration */ | ||
1373 | host->testbus.select_major = TSTBUS_UAWM; | ||
1374 | host->testbus.select_minor = 1; | ||
1375 | } | ||
1376 | |||
1377 | static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host) | ||
1378 | { | ||
1379 | if (host->testbus.select_major >= TSTBUS_MAX) { | ||
1380 | dev_err(host->hba->dev, | ||
1381 | "%s: UFS_CFG1[TEST_BUS_SEL} may not equal 0x%05X\n", | ||
1382 | __func__, host->testbus.select_major); | ||
1383 | return false; | ||
1384 | } | ||
1385 | |||
1386 | /* | ||
1387 | * Not performing check for each individual select_major | ||
1388 | * mappings of select_minor, since there is no harm in | ||
1389 | * configuring a non-existent select_minor | ||
1390 | */ | ||
1391 | if (host->testbus.select_minor > 0x1F) { | ||
1392 | dev_err(host->hba->dev, | ||
1393 | "%s: 0x%05X is not a legal testbus option\n", | ||
1394 | __func__, host->testbus.select_minor); | ||
1395 | return false; | ||
1396 | } | ||
1397 | |||
1398 | return true; | ||
1399 | } | ||
1400 | |||
1401 | int ufs_qcom_testbus_config(struct ufs_qcom_host *host) | ||
1402 | { | ||
1403 | int reg; | ||
1404 | int offset; | ||
1405 | u32 mask = TEST_BUS_SUB_SEL_MASK; | ||
1406 | |||
1407 | if (!host) | ||
1408 | return -EINVAL; | ||
1409 | |||
1410 | if (!ufs_qcom_testbus_cfg_is_ok(host)) | ||
1411 | return -EPERM; | ||
1412 | |||
1413 | switch (host->testbus.select_major) { | ||
1414 | case TSTBUS_UAWM: | ||
1415 | reg = UFS_TEST_BUS_CTRL_0; | ||
1416 | offset = 24; | ||
1417 | break; | ||
1418 | case TSTBUS_UARM: | ||
1419 | reg = UFS_TEST_BUS_CTRL_0; | ||
1420 | offset = 16; | ||
1421 | break; | ||
1422 | case TSTBUS_TXUC: | ||
1423 | reg = UFS_TEST_BUS_CTRL_0; | ||
1424 | offset = 8; | ||
1425 | break; | ||
1426 | case TSTBUS_RXUC: | ||
1427 | reg = UFS_TEST_BUS_CTRL_0; | ||
1428 | offset = 0; | ||
1429 | break; | ||
1430 | case TSTBUS_DFC: | ||
1431 | reg = UFS_TEST_BUS_CTRL_1; | ||
1432 | offset = 24; | ||
1433 | break; | ||
1434 | case TSTBUS_TRLUT: | ||
1435 | reg = UFS_TEST_BUS_CTRL_1; | ||
1436 | offset = 16; | ||
1437 | break; | ||
1438 | case TSTBUS_TMRLUT: | ||
1439 | reg = UFS_TEST_BUS_CTRL_1; | ||
1440 | offset = 8; | ||
1441 | break; | ||
1442 | case TSTBUS_OCSC: | ||
1443 | reg = UFS_TEST_BUS_CTRL_1; | ||
1444 | offset = 0; | ||
1445 | break; | ||
1446 | case TSTBUS_WRAPPER: | ||
1447 | reg = UFS_TEST_BUS_CTRL_2; | ||
1448 | offset = 16; | ||
1449 | break; | ||
1450 | case TSTBUS_COMBINED: | ||
1451 | reg = UFS_TEST_BUS_CTRL_2; | ||
1452 | offset = 8; | ||
1453 | break; | ||
1454 | case TSTBUS_UTP_HCI: | ||
1455 | reg = UFS_TEST_BUS_CTRL_2; | ||
1456 | offset = 0; | ||
1457 | break; | ||
1458 | case TSTBUS_UNIPRO: | ||
1459 | reg = UFS_UNIPRO_CFG; | ||
1460 | offset = 1; | ||
1461 | break; | ||
1462 | /* | ||
1463 | * No need for a default case, since | ||
1464 | * ufs_qcom_testbus_cfg_is_ok() checks that the configuration | ||
1465 | * is legal | ||
1466 | */ | ||
1467 | } | ||
1468 | mask <<= offset; | ||
1469 | |||
1470 | pm_runtime_get_sync(host->hba->dev); | ||
1471 | ufshcd_hold(host->hba, false); | ||
1472 | ufshcd_rmwl(host->hba, TEST_BUS_SEL, | ||
1473 | (u32)host->testbus.select_major << 19, | ||
1474 | REG_UFS_CFG1); | ||
1475 | ufshcd_rmwl(host->hba, mask, | ||
1476 | (u32)host->testbus.select_minor << offset, | ||
1477 | reg); | ||
1478 | ufshcd_release(host->hba); | ||
1479 | pm_runtime_put_sync(host->hba->dev); | ||
1480 | |||
1481 | return 0; | ||
1482 | } | ||
1483 | |||
1484 | static void ufs_qcom_testbus_read(struct ufs_hba *hba) | ||
1485 | { | ||
1486 | ufs_qcom_dump_regs(hba, UFS_TEST_BUS, 1, "UFS_TEST_BUS "); | ||
1487 | } | ||
1488 | |||
1489 | static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba) | ||
1490 | { | ||
1491 | ufs_qcom_dump_regs(hba, REG_UFS_SYS1CLK_1US, 16, | ||
1492 | "HCI Vendor Specific Registers "); | ||
1493 | |||
1494 | ufs_qcom_testbus_read(hba); | ||
1495 | } | ||
1030 | /** | 1496 | /** |
1031 | * struct ufs_hba_qcom_vops - UFS QCOM specific variant operations | 1497 | * struct ufs_hba_qcom_vops - UFS QCOM specific variant operations |
1032 | * | 1498 | * |
1033 | * The variant operations configure the necessary controller and PHY | 1499 | * The variant operations configure the necessary controller and PHY |
1034 | * handshake during initialization. | 1500 | * handshake during initialization. |
1035 | */ | 1501 | */ |
1036 | static const struct ufs_hba_variant_ops ufs_hba_qcom_vops = { | 1502 | static struct ufs_hba_variant_ops ufs_hba_qcom_vops = { |
1037 | .name = "qcom", | 1503 | .name = "qcom", |
1038 | .init = ufs_qcom_init, | 1504 | .init = ufs_qcom_init, |
1039 | .exit = ufs_qcom_exit, | 1505 | .exit = ufs_qcom_exit, |
@@ -1045,5 +1511,66 @@ static const struct ufs_hba_variant_ops ufs_hba_qcom_vops = { | |||
1045 | .pwr_change_notify = ufs_qcom_pwr_change_notify, | 1511 | .pwr_change_notify = ufs_qcom_pwr_change_notify, |
1046 | .suspend = ufs_qcom_suspend, | 1512 | .suspend = ufs_qcom_suspend, |
1047 | .resume = ufs_qcom_resume, | 1513 | .resume = ufs_qcom_resume, |
1514 | .dbg_register_dump = ufs_qcom_dump_dbg_regs, | ||
1515 | }; | ||
1516 | |||
1517 | /** | ||
1518 | * ufs_qcom_probe - probe routine of the driver | ||
1519 | * @pdev: pointer to Platform device handle | ||
1520 | * | ||
1521 | * Return zero for success and non-zero for failure | ||
1522 | */ | ||
1523 | static int ufs_qcom_probe(struct platform_device *pdev) | ||
1524 | { | ||
1525 | int err; | ||
1526 | struct device *dev = &pdev->dev; | ||
1527 | |||
1528 | /* Perform generic probe */ | ||
1529 | err = ufshcd_pltfrm_init(pdev, &ufs_hba_qcom_vops); | ||
1530 | if (err) | ||
1531 | dev_err(dev, "ufshcd_pltfrm_init() failed %d\n", err); | ||
1532 | |||
1533 | return err; | ||
1534 | } | ||
1535 | |||
1536 | /** | ||
1537 | * ufs_qcom_remove - set driver_data of the device to NULL | ||
1538 | * @pdev: pointer to platform device handle | ||
1539 | * | ||
1540 | * Always return 0 | ||
1541 | */ | ||
1542 | static int ufs_qcom_remove(struct platform_device *pdev) | ||
1543 | { | ||
1544 | struct ufs_hba *hba = platform_get_drvdata(pdev); | ||
1545 | |||
1546 | pm_runtime_get_sync(&(pdev)->dev); | ||
1547 | ufshcd_remove(hba); | ||
1548 | return 0; | ||
1549 | } | ||
1550 | |||
1551 | static const struct of_device_id ufs_qcom_of_match[] = { | ||
1552 | { .compatible = "qcom,ufshc"}, | ||
1553 | {}, | ||
1554 | }; | ||
1555 | |||
1556 | static const struct dev_pm_ops ufs_qcom_pm_ops = { | ||
1557 | .suspend = ufshcd_pltfrm_suspend, | ||
1558 | .resume = ufshcd_pltfrm_resume, | ||
1559 | .runtime_suspend = ufshcd_pltfrm_runtime_suspend, | ||
1560 | .runtime_resume = ufshcd_pltfrm_runtime_resume, | ||
1561 | .runtime_idle = ufshcd_pltfrm_runtime_idle, | ||
1048 | }; | 1562 | }; |
1049 | EXPORT_SYMBOL(ufs_hba_qcom_vops); | 1563 | |
1564 | static struct platform_driver ufs_qcom_pltform = { | ||
1565 | .probe = ufs_qcom_probe, | ||
1566 | .remove = ufs_qcom_remove, | ||
1567 | .shutdown = ufshcd_pltfrm_shutdown, | ||
1568 | .driver = { | ||
1569 | .name = "ufshcd-qcom", | ||
1570 | .pm = &ufs_qcom_pm_ops, | ||
1571 | .of_match_table = of_match_ptr(ufs_qcom_of_match), | ||
1572 | }, | ||
1573 | }; | ||
1574 | module_platform_driver(ufs_qcom_pltform); | ||
1575 | |||
1576 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h index db2c0a00e846..36249b35f858 100644 --- a/drivers/scsi/ufs/ufs-qcom.h +++ b/drivers/scsi/ufs/ufs-qcom.h | |||
@@ -35,8 +35,8 @@ | |||
35 | 35 | ||
36 | #define UFS_QCOM_LIMIT_NUM_LANES_RX 2 | 36 | #define UFS_QCOM_LIMIT_NUM_LANES_RX 2 |
37 | #define UFS_QCOM_LIMIT_NUM_LANES_TX 2 | 37 | #define UFS_QCOM_LIMIT_NUM_LANES_TX 2 |
38 | #define UFS_QCOM_LIMIT_HSGEAR_RX UFS_HS_G2 | 38 | #define UFS_QCOM_LIMIT_HSGEAR_RX UFS_HS_G3 |
39 | #define UFS_QCOM_LIMIT_HSGEAR_TX UFS_HS_G2 | 39 | #define UFS_QCOM_LIMIT_HSGEAR_TX UFS_HS_G3 |
40 | #define UFS_QCOM_LIMIT_PWMGEAR_RX UFS_PWM_G4 | 40 | #define UFS_QCOM_LIMIT_PWMGEAR_RX UFS_PWM_G4 |
41 | #define UFS_QCOM_LIMIT_PWMGEAR_TX UFS_PWM_G4 | 41 | #define UFS_QCOM_LIMIT_PWMGEAR_TX UFS_PWM_G4 |
42 | #define UFS_QCOM_LIMIT_RX_PWR_PWM SLOW_MODE | 42 | #define UFS_QCOM_LIMIT_RX_PWR_PWM SLOW_MODE |
@@ -58,6 +58,21 @@ enum { | |||
58 | REG_UFS_CFG2 = 0xE0, | 58 | REG_UFS_CFG2 = 0xE0, |
59 | REG_UFS_HW_VERSION = 0xE4, | 59 | REG_UFS_HW_VERSION = 0xE4, |
60 | 60 | ||
61 | UFS_TEST_BUS = 0xE8, | ||
62 | UFS_TEST_BUS_CTRL_0 = 0xEC, | ||
63 | UFS_TEST_BUS_CTRL_1 = 0xF0, | ||
64 | UFS_TEST_BUS_CTRL_2 = 0xF4, | ||
65 | UFS_UNIPRO_CFG = 0xF8, | ||
66 | |||
67 | /* | ||
68 | * QCOM UFS host controller vendor specific registers | ||
69 | * added in HW Version 3.0.0 | ||
70 | */ | ||
71 | UFS_AH8_CFG = 0xFC, | ||
72 | }; | ||
73 | |||
74 | /* QCOM UFS host controller vendor specific debug registers */ | ||
75 | enum { | ||
61 | UFS_DBG_RD_REG_UAWM = 0x100, | 76 | UFS_DBG_RD_REG_UAWM = 0x100, |
62 | UFS_DBG_RD_REG_UARM = 0x200, | 77 | UFS_DBG_RD_REG_UARM = 0x200, |
63 | UFS_DBG_RD_REG_TXUC = 0x300, | 78 | UFS_DBG_RD_REG_TXUC = 0x300, |
@@ -73,6 +88,14 @@ enum { | |||
73 | UFS_UFS_DBG_RD_EDTL_RAM = 0x1900, | 88 | UFS_UFS_DBG_RD_EDTL_RAM = 0x1900, |
74 | }; | 89 | }; |
75 | 90 | ||
91 | #define UFS_CNTLR_2_x_x_VEN_REGS_OFFSET(x) (0x000 + x) | ||
92 | #define UFS_CNTLR_3_x_x_VEN_REGS_OFFSET(x) (0x400 + x) | ||
93 | |||
94 | /* bit definitions for REG_UFS_CFG1 register */ | ||
95 | #define QUNIPRO_SEL UFS_BIT(0) | ||
96 | #define TEST_BUS_EN BIT(18) | ||
97 | #define TEST_BUS_SEL GENMASK(22, 19) | ||
98 | |||
76 | /* bit definitions for REG_UFS_CFG2 register */ | 99 | /* bit definitions for REG_UFS_CFG2 register */ |
77 | #define UAWM_HW_CGC_EN (1 << 0) | 100 | #define UAWM_HW_CGC_EN (1 << 0) |
78 | #define UARM_HW_CGC_EN (1 << 1) | 101 | #define UARM_HW_CGC_EN (1 << 1) |
@@ -83,6 +106,9 @@ enum { | |||
83 | #define TMRLUT_HW_CGC_EN (1 << 6) | 106 | #define TMRLUT_HW_CGC_EN (1 << 6) |
84 | #define OCSC_HW_CGC_EN (1 << 7) | 107 | #define OCSC_HW_CGC_EN (1 << 7) |
85 | 108 | ||
109 | /* bit definition for UFS_UFS_TEST_BUS_CTRL_n */ | ||
110 | #define TEST_BUS_SUB_SEL_MASK 0x1F /* All XXX_SEL fields are 5 bits wide */ | ||
111 | |||
86 | #define REG_UFS_CFG2_CGC_EN_ALL (UAWM_HW_CGC_EN | UARM_HW_CGC_EN |\ | 112 | #define REG_UFS_CFG2_CGC_EN_ALL (UAWM_HW_CGC_EN | UARM_HW_CGC_EN |\ |
87 | TXUC_HW_CGC_EN | RXUC_HW_CGC_EN |\ | 113 | TXUC_HW_CGC_EN | RXUC_HW_CGC_EN |\ |
88 | DFC_HW_CGC_EN | TRLUT_HW_CGC_EN |\ | 114 | DFC_HW_CGC_EN | TRLUT_HW_CGC_EN |\ |
@@ -106,6 +132,21 @@ enum ufs_qcom_phy_init_type { | |||
106 | UFS_PHY_INIT_CFG_RESTORE, | 132 | UFS_PHY_INIT_CFG_RESTORE, |
107 | }; | 133 | }; |
108 | 134 | ||
135 | /* QCOM UFS debug print bit mask */ | ||
136 | #define UFS_QCOM_DBG_PRINT_REGS_EN BIT(0) | ||
137 | #define UFS_QCOM_DBG_PRINT_ICE_REGS_EN BIT(1) | ||
138 | #define UFS_QCOM_DBG_PRINT_TEST_BUS_EN BIT(2) | ||
139 | |||
140 | #define UFS_QCOM_DBG_PRINT_ALL \ | ||
141 | (UFS_QCOM_DBG_PRINT_REGS_EN | UFS_QCOM_DBG_PRINT_ICE_REGS_EN | \ | ||
142 | UFS_QCOM_DBG_PRINT_TEST_BUS_EN) | ||
143 | |||
144 | /* QUniPro Vendor specific attributes */ | ||
145 | #define DME_VS_CORE_CLK_CTRL 0xD002 | ||
146 | /* bit and mask definitions for DME_VS_CORE_CLK_CTRL attribute */ | ||
147 | #define DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT BIT(8) | ||
148 | #define DME_VS_CORE_CLK_CTRL_MAX_CORE_CLK_1US_CYCLES_MASK 0xFF | ||
149 | |||
109 | static inline void | 150 | static inline void |
110 | ufs_qcom_get_controller_revision(struct ufs_hba *hba, | 151 | ufs_qcom_get_controller_revision(struct ufs_hba *hba, |
111 | u8 *major, u16 *minor, u16 *step) | 152 | u8 *major, u16 *minor, u16 *step) |
@@ -157,8 +198,13 @@ struct ufs_hw_version { | |||
157 | u16 minor; | 198 | u16 minor; |
158 | u8 major; | 199 | u8 major; |
159 | }; | 200 | }; |
160 | struct ufs_qcom_host { | ||
161 | 201 | ||
202 | struct ufs_qcom_testbus { | ||
203 | u8 select_major; | ||
204 | u8 select_minor; | ||
205 | }; | ||
206 | |||
207 | struct ufs_qcom_host { | ||
162 | /* | 208 | /* |
163 | * Set this capability if host controller supports the QUniPro mode | 209 | * Set this capability if host controller supports the QUniPro mode |
164 | * and if driver wants the Host controller to operate in QUniPro mode. | 210 | * and if driver wants the Host controller to operate in QUniPro mode. |
@@ -166,6 +212,12 @@ struct ufs_qcom_host { | |||
166 | * controller supports the QUniPro mode. | 212 | * controller supports the QUniPro mode. |
167 | */ | 213 | */ |
168 | #define UFS_QCOM_CAP_QUNIPRO UFS_BIT(0) | 214 | #define UFS_QCOM_CAP_QUNIPRO UFS_BIT(0) |
215 | |||
216 | /* | ||
217 | * Set this capability if host controller can retain the secure | ||
218 | * configuration even after UFS controller core power collapse. | ||
219 | */ | ||
220 | #define UFS_QCOM_CAP_RETAIN_SEC_CFG_AFTER_PWR_COLLAPSE UFS_BIT(1) | ||
169 | u32 caps; | 221 | u32 caps; |
170 | 222 | ||
171 | struct phy *generic_phy; | 223 | struct phy *generic_phy; |
@@ -178,13 +230,23 @@ struct ufs_qcom_host { | |||
178 | struct clk *tx_l1_sync_clk; | 230 | struct clk *tx_l1_sync_clk; |
179 | bool is_lane_clks_enabled; | 231 | bool is_lane_clks_enabled; |
180 | 232 | ||
233 | void __iomem *dev_ref_clk_ctrl_mmio; | ||
234 | bool is_dev_ref_clk_enabled; | ||
181 | struct ufs_hw_version hw_ver; | 235 | struct ufs_hw_version hw_ver; |
236 | |||
237 | u32 dev_ref_clk_en_mask; | ||
238 | |||
239 | /* Bitmask for enabling debug prints */ | ||
240 | u32 dbg_print_en; | ||
241 | struct ufs_qcom_testbus testbus; | ||
182 | }; | 242 | }; |
183 | 243 | ||
184 | #define ufs_qcom_is_link_off(hba) ufshcd_is_link_off(hba) | 244 | #define ufs_qcom_is_link_off(hba) ufshcd_is_link_off(hba) |
185 | #define ufs_qcom_is_link_active(hba) ufshcd_is_link_active(hba) | 245 | #define ufs_qcom_is_link_active(hba) ufshcd_is_link_active(hba) |
186 | #define ufs_qcom_is_link_hibern8(hba) ufshcd_is_link_hibern8(hba) | 246 | #define ufs_qcom_is_link_hibern8(hba) ufshcd_is_link_hibern8(hba) |
187 | 247 | ||
248 | int ufs_qcom_testbus_config(struct ufs_qcom_host *host); | ||
249 | |||
188 | static inline bool ufs_qcom_cap_qunipro(struct ufs_qcom_host *host) | 250 | static inline bool ufs_qcom_cap_qunipro(struct ufs_qcom_host *host) |
189 | { | 251 | { |
190 | if (host->caps & UFS_QCOM_CAP_QUNIPRO) | 252 | if (host->caps & UFS_QCOM_CAP_QUNIPRO) |
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c index 7db9564f507d..9714f2a8b329 100644 --- a/drivers/scsi/ufs/ufshcd-pltfrm.c +++ b/drivers/scsi/ufs/ufshcd-pltfrm.c | |||
@@ -38,20 +38,7 @@ | |||
38 | #include <linux/of.h> | 38 | #include <linux/of.h> |
39 | 39 | ||
40 | #include "ufshcd.h" | 40 | #include "ufshcd.h" |
41 | 41 | #include "ufshcd-pltfrm.h" | |
42 | static const struct of_device_id ufs_of_match[]; | ||
43 | static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev) | ||
44 | { | ||
45 | if (dev->of_node) { | ||
46 | const struct of_device_id *match; | ||
47 | |||
48 | match = of_match_node(ufs_of_match, dev->of_node); | ||
49 | if (match) | ||
50 | return (struct ufs_hba_variant_ops *)match->data; | ||
51 | } | ||
52 | |||
53 | return NULL; | ||
54 | } | ||
55 | 42 | ||
56 | static int ufshcd_parse_clock_info(struct ufs_hba *hba) | 43 | static int ufshcd_parse_clock_info(struct ufs_hba *hba) |
57 | { | 44 | { |
@@ -245,10 +232,11 @@ out: | |||
245 | * Returns 0 if successful | 232 | * Returns 0 if successful |
246 | * Returns non-zero otherwise | 233 | * Returns non-zero otherwise |
247 | */ | 234 | */ |
248 | static int ufshcd_pltfrm_suspend(struct device *dev) | 235 | int ufshcd_pltfrm_suspend(struct device *dev) |
249 | { | 236 | { |
250 | return ufshcd_system_suspend(dev_get_drvdata(dev)); | 237 | return ufshcd_system_suspend(dev_get_drvdata(dev)); |
251 | } | 238 | } |
239 | EXPORT_SYMBOL_GPL(ufshcd_pltfrm_suspend); | ||
252 | 240 | ||
253 | /** | 241 | /** |
254 | * ufshcd_pltfrm_resume - resume power management function | 242 | * ufshcd_pltfrm_resume - resume power management function |
@@ -257,43 +245,47 @@ static int ufshcd_pltfrm_suspend(struct device *dev) | |||
257 | * Returns 0 if successful | 245 | * Returns 0 if successful |
258 | * Returns non-zero otherwise | 246 | * Returns non-zero otherwise |
259 | */ | 247 | */ |
260 | static int ufshcd_pltfrm_resume(struct device *dev) | 248 | int ufshcd_pltfrm_resume(struct device *dev) |
261 | { | 249 | { |
262 | return ufshcd_system_resume(dev_get_drvdata(dev)); | 250 | return ufshcd_system_resume(dev_get_drvdata(dev)); |
263 | } | 251 | } |
252 | EXPORT_SYMBOL_GPL(ufshcd_pltfrm_resume); | ||
264 | 253 | ||
265 | static int ufshcd_pltfrm_runtime_suspend(struct device *dev) | 254 | int ufshcd_pltfrm_runtime_suspend(struct device *dev) |
266 | { | 255 | { |
267 | return ufshcd_runtime_suspend(dev_get_drvdata(dev)); | 256 | return ufshcd_runtime_suspend(dev_get_drvdata(dev)); |
268 | } | 257 | } |
269 | static int ufshcd_pltfrm_runtime_resume(struct device *dev) | 258 | EXPORT_SYMBOL_GPL(ufshcd_pltfrm_runtime_suspend); |
259 | |||
260 | int ufshcd_pltfrm_runtime_resume(struct device *dev) | ||
270 | { | 261 | { |
271 | return ufshcd_runtime_resume(dev_get_drvdata(dev)); | 262 | return ufshcd_runtime_resume(dev_get_drvdata(dev)); |
272 | } | 263 | } |
273 | static int ufshcd_pltfrm_runtime_idle(struct device *dev) | 264 | EXPORT_SYMBOL_GPL(ufshcd_pltfrm_runtime_resume); |
265 | |||
266 | int ufshcd_pltfrm_runtime_idle(struct device *dev) | ||
274 | { | 267 | { |
275 | return ufshcd_runtime_idle(dev_get_drvdata(dev)); | 268 | return ufshcd_runtime_idle(dev_get_drvdata(dev)); |
276 | } | 269 | } |
277 | #else /* !CONFIG_PM */ | 270 | EXPORT_SYMBOL_GPL(ufshcd_pltfrm_runtime_idle); |
278 | #define ufshcd_pltfrm_suspend NULL | 271 | |
279 | #define ufshcd_pltfrm_resume NULL | ||
280 | #define ufshcd_pltfrm_runtime_suspend NULL | ||
281 | #define ufshcd_pltfrm_runtime_resume NULL | ||
282 | #define ufshcd_pltfrm_runtime_idle NULL | ||
283 | #endif /* CONFIG_PM */ | 272 | #endif /* CONFIG_PM */ |
284 | 273 | ||
285 | static void ufshcd_pltfrm_shutdown(struct platform_device *pdev) | 274 | void ufshcd_pltfrm_shutdown(struct platform_device *pdev) |
286 | { | 275 | { |
287 | ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev)); | 276 | ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev)); |
288 | } | 277 | } |
278 | EXPORT_SYMBOL_GPL(ufshcd_pltfrm_shutdown); | ||
289 | 279 | ||
290 | /** | 280 | /** |
291 | * ufshcd_pltfrm_probe - probe routine of the driver | 281 | * ufshcd_pltfrm_init - probe routine of the driver |
292 | * @pdev: pointer to Platform device handle | 282 | * @pdev: pointer to Platform device handle |
283 | * @vops: pointer to variant ops | ||
293 | * | 284 | * |
294 | * Returns 0 on success, non-zero value on failure | 285 | * Returns 0 on success, non-zero value on failure |
295 | */ | 286 | */ |
296 | static int ufshcd_pltfrm_probe(struct platform_device *pdev) | 287 | int ufshcd_pltfrm_init(struct platform_device *pdev, |
288 | struct ufs_hba_variant_ops *vops) | ||
297 | { | 289 | { |
298 | struct ufs_hba *hba; | 290 | struct ufs_hba *hba; |
299 | void __iomem *mmio_base; | 291 | void __iomem *mmio_base; |
@@ -321,19 +313,19 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev) | |||
321 | goto out; | 313 | goto out; |
322 | } | 314 | } |
323 | 315 | ||
324 | hba->vops = get_variant_ops(&pdev->dev); | 316 | hba->vops = vops; |
325 | 317 | ||
326 | err = ufshcd_parse_clock_info(hba); | 318 | err = ufshcd_parse_clock_info(hba); |
327 | if (err) { | 319 | if (err) { |
328 | dev_err(&pdev->dev, "%s: clock parse failed %d\n", | 320 | dev_err(&pdev->dev, "%s: clock parse failed %d\n", |
329 | __func__, err); | 321 | __func__, err); |
330 | goto out; | 322 | goto dealloc_host; |
331 | } | 323 | } |
332 | err = ufshcd_parse_regulator_info(hba); | 324 | err = ufshcd_parse_regulator_info(hba); |
333 | if (err) { | 325 | if (err) { |
334 | dev_err(&pdev->dev, "%s: regulator init failed %d\n", | 326 | dev_err(&pdev->dev, "%s: regulator init failed %d\n", |
335 | __func__, err); | 327 | __func__, err); |
336 | goto out; | 328 | goto dealloc_host; |
337 | } | 329 | } |
338 | 330 | ||
339 | pm_runtime_set_active(&pdev->dev); | 331 | pm_runtime_set_active(&pdev->dev); |
@@ -352,50 +344,12 @@ static int ufshcd_pltfrm_probe(struct platform_device *pdev) | |||
352 | out_disable_rpm: | 344 | out_disable_rpm: |
353 | pm_runtime_disable(&pdev->dev); | 345 | pm_runtime_disable(&pdev->dev); |
354 | pm_runtime_set_suspended(&pdev->dev); | 346 | pm_runtime_set_suspended(&pdev->dev); |
347 | dealloc_host: | ||
348 | ufshcd_dealloc_host(hba); | ||
355 | out: | 349 | out: |
356 | return err; | 350 | return err; |
357 | } | 351 | } |
358 | 352 | EXPORT_SYMBOL_GPL(ufshcd_pltfrm_init); | |
359 | /** | ||
360 | * ufshcd_pltfrm_remove - remove platform driver routine | ||
361 | * @pdev: pointer to platform device handle | ||
362 | * | ||
363 | * Returns 0 on success, non-zero value on failure | ||
364 | */ | ||
365 | static int ufshcd_pltfrm_remove(struct platform_device *pdev) | ||
366 | { | ||
367 | struct ufs_hba *hba = platform_get_drvdata(pdev); | ||
368 | |||
369 | pm_runtime_get_sync(&(pdev)->dev); | ||
370 | ufshcd_remove(hba); | ||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | static const struct of_device_id ufs_of_match[] = { | ||
375 | { .compatible = "jedec,ufs-1.1"}, | ||
376 | {}, | ||
377 | }; | ||
378 | |||
379 | static const struct dev_pm_ops ufshcd_dev_pm_ops = { | ||
380 | .suspend = ufshcd_pltfrm_suspend, | ||
381 | .resume = ufshcd_pltfrm_resume, | ||
382 | .runtime_suspend = ufshcd_pltfrm_runtime_suspend, | ||
383 | .runtime_resume = ufshcd_pltfrm_runtime_resume, | ||
384 | .runtime_idle = ufshcd_pltfrm_runtime_idle, | ||
385 | }; | ||
386 | |||
387 | static struct platform_driver ufshcd_pltfrm_driver = { | ||
388 | .probe = ufshcd_pltfrm_probe, | ||
389 | .remove = ufshcd_pltfrm_remove, | ||
390 | .shutdown = ufshcd_pltfrm_shutdown, | ||
391 | .driver = { | ||
392 | .name = "ufshcd", | ||
393 | .pm = &ufshcd_dev_pm_ops, | ||
394 | .of_match_table = ufs_of_match, | ||
395 | }, | ||
396 | }; | ||
397 | |||
398 | module_platform_driver(ufshcd_pltfrm_driver); | ||
399 | 353 | ||
400 | MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>"); | 354 | MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>"); |
401 | MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>"); | 355 | MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>"); |
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.h b/drivers/scsi/ufs/ufshcd-pltfrm.h new file mode 100644 index 000000000000..df64c4180340 --- /dev/null +++ b/drivers/scsi/ufs/ufshcd-pltfrm.h | |||
@@ -0,0 +1,41 @@ | |||
1 | /* Copyright (c) 2015, The Linux Foundation. All rights reserved. | ||
2 | * | ||
3 | * This program is free software; you can redistribute it and/or modify | ||
4 | * it under the terms of the GNU General Public License version 2 and | ||
5 | * only version 2 as published by the Free Software Foundation. | ||
6 | * | ||
7 | * This program is distributed in the hope that it will be useful, | ||
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10 | * GNU General Public License for more details. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #ifndef UFSHCD_PLTFRM_H_ | ||
15 | #define UFSHCD_PLTFRM_H_ | ||
16 | |||
17 | #include "ufshcd.h" | ||
18 | |||
19 | int ufshcd_pltfrm_init(struct platform_device *pdev, | ||
20 | struct ufs_hba_variant_ops *vops); | ||
21 | void ufshcd_pltfrm_shutdown(struct platform_device *pdev); | ||
22 | |||
23 | #ifdef CONFIG_PM | ||
24 | |||
25 | int ufshcd_pltfrm_suspend(struct device *dev); | ||
26 | int ufshcd_pltfrm_resume(struct device *dev); | ||
27 | int ufshcd_pltfrm_runtime_suspend(struct device *dev); | ||
28 | int ufshcd_pltfrm_runtime_resume(struct device *dev); | ||
29 | int ufshcd_pltfrm_runtime_idle(struct device *dev); | ||
30 | |||
31 | #else /* !CONFIG_PM */ | ||
32 | |||
33 | #define ufshcd_pltfrm_suspend NULL | ||
34 | #define ufshcd_pltfrm_resume NULL | ||
35 | #define ufshcd_pltfrm_runtime_suspend NULL | ||
36 | #define ufshcd_pltfrm_runtime_resume NULL | ||
37 | #define ufshcd_pltfrm_runtime_idle NULL | ||
38 | |||
39 | #endif /* CONFIG_PM */ | ||
40 | |||
41 | #endif /* UFSHCD_PLTFRM_H_ */ | ||
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index b0ade73f8c6a..85cd2564c157 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c | |||
@@ -271,10 +271,8 @@ static inline u32 ufshcd_get_intr_mask(struct ufs_hba *hba) | |||
271 | */ | 271 | */ |
272 | static inline u32 ufshcd_get_ufs_version(struct ufs_hba *hba) | 272 | static inline u32 ufshcd_get_ufs_version(struct ufs_hba *hba) |
273 | { | 273 | { |
274 | if (hba->quirks & UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION) { | 274 | if (hba->quirks & UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION) |
275 | if (hba->vops && hba->vops->get_ufs_hci_version) | 275 | return ufshcd_vops_get_ufs_hci_version(hba); |
276 | return hba->vops->get_ufs_hci_version(hba); | ||
277 | } | ||
278 | 276 | ||
279 | return ufshcd_readl(hba, REG_UFS_VERSION); | 277 | return ufshcd_readl(hba, REG_UFS_VERSION); |
280 | } | 278 | } |
@@ -627,6 +625,7 @@ start: | |||
627 | out: | 625 | out: |
628 | return rc; | 626 | return rc; |
629 | } | 627 | } |
628 | EXPORT_SYMBOL_GPL(ufshcd_hold); | ||
630 | 629 | ||
631 | static void ufshcd_gate_work(struct work_struct *work) | 630 | static void ufshcd_gate_work(struct work_struct *work) |
632 | { | 631 | { |
@@ -714,6 +713,7 @@ void ufshcd_release(struct ufs_hba *hba) | |||
714 | __ufshcd_release(hba); | 713 | __ufshcd_release(hba); |
715 | spin_unlock_irqrestore(hba->host->host_lock, flags); | 714 | spin_unlock_irqrestore(hba->host->host_lock, flags); |
716 | } | 715 | } |
716 | EXPORT_SYMBOL_GPL(ufshcd_release); | ||
717 | 717 | ||
718 | static ssize_t ufshcd_clkgate_delay_show(struct device *dev, | 718 | static ssize_t ufshcd_clkgate_delay_show(struct device *dev, |
719 | struct device_attribute *attr, char *buf) | 719 | struct device_attribute *attr, char *buf) |
@@ -2473,9 +2473,8 @@ static int ufshcd_change_power_mode(struct ufs_hba *hba, | |||
2473 | dev_err(hba->dev, | 2473 | dev_err(hba->dev, |
2474 | "%s: power mode change failed %d\n", __func__, ret); | 2474 | "%s: power mode change failed %d\n", __func__, ret); |
2475 | } else { | 2475 | } else { |
2476 | if (hba->vops && hba->vops->pwr_change_notify) | 2476 | ufshcd_vops_pwr_change_notify(hba, POST_CHANGE, NULL, |
2477 | hba->vops->pwr_change_notify(hba, | 2477 | pwr_mode); |
2478 | POST_CHANGE, NULL, pwr_mode); | ||
2479 | 2478 | ||
2480 | memcpy(&hba->pwr_info, pwr_mode, | 2479 | memcpy(&hba->pwr_info, pwr_mode, |
2481 | sizeof(struct ufs_pa_layer_attr)); | 2480 | sizeof(struct ufs_pa_layer_attr)); |
@@ -2495,10 +2494,10 @@ static int ufshcd_config_pwr_mode(struct ufs_hba *hba, | |||
2495 | struct ufs_pa_layer_attr final_params = { 0 }; | 2494 | struct ufs_pa_layer_attr final_params = { 0 }; |
2496 | int ret; | 2495 | int ret; |
2497 | 2496 | ||
2498 | if (hba->vops && hba->vops->pwr_change_notify) | 2497 | ret = ufshcd_vops_pwr_change_notify(hba, PRE_CHANGE, |
2499 | hba->vops->pwr_change_notify(hba, | 2498 | desired_pwr_mode, &final_params); |
2500 | PRE_CHANGE, desired_pwr_mode, &final_params); | 2499 | |
2501 | else | 2500 | if (ret) |
2502 | memcpy(&final_params, desired_pwr_mode, sizeof(final_params)); | 2501 | memcpy(&final_params, desired_pwr_mode, sizeof(final_params)); |
2503 | 2502 | ||
2504 | ret = ufshcd_change_power_mode(hba, &final_params); | 2503 | ret = ufshcd_change_power_mode(hba, &final_params); |
@@ -2647,8 +2646,7 @@ static int ufshcd_hba_enable(struct ufs_hba *hba) | |||
2647 | /* UniPro link is disabled at this point */ | 2646 | /* UniPro link is disabled at this point */ |
2648 | ufshcd_set_link_off(hba); | 2647 | ufshcd_set_link_off(hba); |
2649 | 2648 | ||
2650 | if (hba->vops && hba->vops->hce_enable_notify) | 2649 | ufshcd_vops_hce_enable_notify(hba, PRE_CHANGE); |
2651 | hba->vops->hce_enable_notify(hba, PRE_CHANGE); | ||
2652 | 2650 | ||
2653 | /* start controller initialization sequence */ | 2651 | /* start controller initialization sequence */ |
2654 | ufshcd_hba_start(hba); | 2652 | ufshcd_hba_start(hba); |
@@ -2681,8 +2679,7 @@ static int ufshcd_hba_enable(struct ufs_hba *hba) | |||
2681 | /* enable UIC related interrupts */ | 2679 | /* enable UIC related interrupts */ |
2682 | ufshcd_enable_intr(hba, UFSHCD_UIC_MASK); | 2680 | ufshcd_enable_intr(hba, UFSHCD_UIC_MASK); |
2683 | 2681 | ||
2684 | if (hba->vops && hba->vops->hce_enable_notify) | 2682 | ufshcd_vops_hce_enable_notify(hba, POST_CHANGE); |
2685 | hba->vops->hce_enable_notify(hba, POST_CHANGE); | ||
2686 | 2683 | ||
2687 | return 0; | 2684 | return 0; |
2688 | } | 2685 | } |
@@ -2735,8 +2732,7 @@ static int ufshcd_link_startup(struct ufs_hba *hba) | |||
2735 | int retries = DME_LINKSTARTUP_RETRIES; | 2732 | int retries = DME_LINKSTARTUP_RETRIES; |
2736 | 2733 | ||
2737 | do { | 2734 | do { |
2738 | if (hba->vops && hba->vops->link_startup_notify) | 2735 | ufshcd_vops_link_startup_notify(hba, PRE_CHANGE); |
2739 | hba->vops->link_startup_notify(hba, PRE_CHANGE); | ||
2740 | 2736 | ||
2741 | ret = ufshcd_dme_link_startup(hba); | 2737 | ret = ufshcd_dme_link_startup(hba); |
2742 | 2738 | ||
@@ -2767,11 +2763,9 @@ static int ufshcd_link_startup(struct ufs_hba *hba) | |||
2767 | } | 2763 | } |
2768 | 2764 | ||
2769 | /* Include any host controller configuration via UIC commands */ | 2765 | /* Include any host controller configuration via UIC commands */ |
2770 | if (hba->vops && hba->vops->link_startup_notify) { | 2766 | ret = ufshcd_vops_link_startup_notify(hba, POST_CHANGE); |
2771 | ret = hba->vops->link_startup_notify(hba, POST_CHANGE); | 2767 | if (ret) |
2772 | if (ret) | 2768 | goto out; |
2773 | goto out; | ||
2774 | } | ||
2775 | 2769 | ||
2776 | ret = ufshcd_make_hba_operational(hba); | 2770 | ret = ufshcd_make_hba_operational(hba); |
2777 | out: | 2771 | out: |
@@ -4355,7 +4349,6 @@ static struct scsi_host_template ufshcd_driver_template = { | |||
4355 | .cmd_per_lun = UFSHCD_CMD_PER_LUN, | 4349 | .cmd_per_lun = UFSHCD_CMD_PER_LUN, |
4356 | .can_queue = UFSHCD_CAN_QUEUE, | 4350 | .can_queue = UFSHCD_CAN_QUEUE, |
4357 | .max_host_blocked = 1, | 4351 | .max_host_blocked = 1, |
4358 | .use_blk_tags = 1, | ||
4359 | .track_queue_depth = 1, | 4352 | .track_queue_depth = 1, |
4360 | }; | 4353 | }; |
4361 | 4354 | ||
@@ -4578,8 +4571,7 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on, | |||
4578 | } | 4571 | } |
4579 | } | 4572 | } |
4580 | 4573 | ||
4581 | if (hba->vops && hba->vops->setup_clocks) | 4574 | ret = ufshcd_vops_setup_clocks(hba, on); |
4582 | ret = hba->vops->setup_clocks(hba, on); | ||
4583 | out: | 4575 | out: |
4584 | if (ret) { | 4576 | if (ret) { |
4585 | list_for_each_entry(clki, head, list) { | 4577 | list_for_each_entry(clki, head, list) { |
@@ -4645,27 +4637,22 @@ static int ufshcd_variant_hba_init(struct ufs_hba *hba) | |||
4645 | if (!hba->vops) | 4637 | if (!hba->vops) |
4646 | goto out; | 4638 | goto out; |
4647 | 4639 | ||
4648 | if (hba->vops->init) { | 4640 | err = ufshcd_vops_init(hba); |
4649 | err = hba->vops->init(hba); | 4641 | if (err) |
4650 | if (err) | 4642 | goto out; |
4651 | goto out; | ||
4652 | } | ||
4653 | 4643 | ||
4654 | if (hba->vops->setup_regulators) { | 4644 | err = ufshcd_vops_setup_regulators(hba, true); |
4655 | err = hba->vops->setup_regulators(hba, true); | 4645 | if (err) |
4656 | if (err) | 4646 | goto out_exit; |
4657 | goto out_exit; | ||
4658 | } | ||
4659 | 4647 | ||
4660 | goto out; | 4648 | goto out; |
4661 | 4649 | ||
4662 | out_exit: | 4650 | out_exit: |
4663 | if (hba->vops->exit) | 4651 | ufshcd_vops_exit(hba); |
4664 | hba->vops->exit(hba); | ||
4665 | out: | 4652 | out: |
4666 | if (err) | 4653 | if (err) |
4667 | dev_err(hba->dev, "%s: variant %s init failed err %d\n", | 4654 | dev_err(hba->dev, "%s: variant %s init failed err %d\n", |
4668 | __func__, hba->vops ? hba->vops->name : "", err); | 4655 | __func__, ufshcd_get_var_name(hba), err); |
4669 | return err; | 4656 | return err; |
4670 | } | 4657 | } |
4671 | 4658 | ||
@@ -4674,14 +4661,11 @@ static void ufshcd_variant_hba_exit(struct ufs_hba *hba) | |||
4674 | if (!hba->vops) | 4661 | if (!hba->vops) |
4675 | return; | 4662 | return; |
4676 | 4663 | ||
4677 | if (hba->vops->setup_clocks) | 4664 | ufshcd_vops_setup_clocks(hba, false); |
4678 | hba->vops->setup_clocks(hba, false); | ||
4679 | 4665 | ||
4680 | if (hba->vops->setup_regulators) | 4666 | ufshcd_vops_setup_regulators(hba, false); |
4681 | hba->vops->setup_regulators(hba, false); | ||
4682 | 4667 | ||
4683 | if (hba->vops->exit) | 4668 | ufshcd_vops_exit(hba); |
4684 | hba->vops->exit(hba); | ||
4685 | } | 4669 | } |
4686 | 4670 | ||
4687 | static int ufshcd_hba_init(struct ufs_hba *hba) | 4671 | static int ufshcd_hba_init(struct ufs_hba *hba) |
@@ -5058,17 +5042,13 @@ disable_clks: | |||
5058 | * vendor specific host controller register space call them before the | 5042 | * vendor specific host controller register space call them before the |
5059 | * host clocks are ON. | 5043 | * host clocks are ON. |
5060 | */ | 5044 | */ |
5061 | if (hba->vops && hba->vops->suspend) { | 5045 | ret = ufshcd_vops_suspend(hba, pm_op); |
5062 | ret = hba->vops->suspend(hba, pm_op); | 5046 | if (ret) |
5063 | if (ret) | 5047 | goto set_link_active; |
5064 | goto set_link_active; | ||
5065 | } | ||
5066 | 5048 | ||
5067 | if (hba->vops && hba->vops->setup_clocks) { | 5049 | ret = ufshcd_vops_setup_clocks(hba, false); |
5068 | ret = hba->vops->setup_clocks(hba, false); | 5050 | if (ret) |
5069 | if (ret) | 5051 | goto vops_resume; |
5070 | goto vops_resume; | ||
5071 | } | ||
5072 | 5052 | ||
5073 | if (!ufshcd_is_link_active(hba)) | 5053 | if (!ufshcd_is_link_active(hba)) |
5074 | ufshcd_setup_clocks(hba, false); | 5054 | ufshcd_setup_clocks(hba, false); |
@@ -5079,7 +5059,7 @@ disable_clks: | |||
5079 | hba->clk_gating.state = CLKS_OFF; | 5059 | hba->clk_gating.state = CLKS_OFF; |
5080 | /* | 5060 | /* |
5081 | * Disable the host irq as host controller as there won't be any | 5061 | * Disable the host irq as host controller as there won't be any |
5082 | * host controller trasanction expected till resume. | 5062 | * host controller transaction expected till resume. |
5083 | */ | 5063 | */ |
5084 | ufshcd_disable_irq(hba); | 5064 | ufshcd_disable_irq(hba); |
5085 | /* Put the host controller in low power mode if possible */ | 5065 | /* Put the host controller in low power mode if possible */ |
@@ -5087,8 +5067,7 @@ disable_clks: | |||
5087 | goto out; | 5067 | goto out; |
5088 | 5068 | ||
5089 | vops_resume: | 5069 | vops_resume: |
5090 | if (hba->vops && hba->vops->resume) | 5070 | ufshcd_vops_resume(hba, pm_op); |
5091 | hba->vops->resume(hba, pm_op); | ||
5092 | set_link_active: | 5071 | set_link_active: |
5093 | ufshcd_vreg_set_hpm(hba); | 5072 | ufshcd_vreg_set_hpm(hba); |
5094 | if (ufshcd_is_link_hibern8(hba) && !ufshcd_uic_hibern8_exit(hba)) | 5073 | if (ufshcd_is_link_hibern8(hba) && !ufshcd_uic_hibern8_exit(hba)) |
@@ -5144,11 +5123,9 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) | |||
5144 | * vendor specific host controller register space call them when the | 5123 | * vendor specific host controller register space call them when the |
5145 | * host clocks are ON. | 5124 | * host clocks are ON. |
5146 | */ | 5125 | */ |
5147 | if (hba->vops && hba->vops->resume) { | 5126 | ret = ufshcd_vops_resume(hba, pm_op); |
5148 | ret = hba->vops->resume(hba, pm_op); | 5127 | if (ret) |
5149 | if (ret) | 5128 | goto disable_vreg; |
5150 | goto disable_vreg; | ||
5151 | } | ||
5152 | 5129 | ||
5153 | if (ufshcd_is_link_hibern8(hba)) { | 5130 | if (ufshcd_is_link_hibern8(hba)) { |
5154 | ret = ufshcd_uic_hibern8_exit(hba); | 5131 | ret = ufshcd_uic_hibern8_exit(hba); |
@@ -5189,8 +5166,7 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) | |||
5189 | set_old_link_state: | 5166 | set_old_link_state: |
5190 | ufshcd_link_state_transition(hba, old_link_state, 0); | 5167 | ufshcd_link_state_transition(hba, old_link_state, 0); |
5191 | vendor_suspend: | 5168 | vendor_suspend: |
5192 | if (hba->vops && hba->vops->suspend) | 5169 | ufshcd_vops_suspend(hba, pm_op); |
5193 | hba->vops->suspend(hba, pm_op); | ||
5194 | disable_vreg: | 5170 | disable_vreg: |
5195 | ufshcd_vreg_set_lpm(hba); | 5171 | ufshcd_vreg_set_lpm(hba); |
5196 | disable_irq_and_vops_clks: | 5172 | disable_irq_and_vops_clks: |
@@ -5373,6 +5349,16 @@ void ufshcd_remove(struct ufs_hba *hba) | |||
5373 | EXPORT_SYMBOL_GPL(ufshcd_remove); | 5349 | EXPORT_SYMBOL_GPL(ufshcd_remove); |
5374 | 5350 | ||
5375 | /** | 5351 | /** |
5352 | * ufshcd_dealloc_host - deallocate Host Bus Adapter (HBA) | ||
5353 | * @hba: pointer to Host Bus Adapter (HBA) | ||
5354 | */ | ||
5355 | void ufshcd_dealloc_host(struct ufs_hba *hba) | ||
5356 | { | ||
5357 | scsi_host_put(hba->host); | ||
5358 | } | ||
5359 | EXPORT_SYMBOL_GPL(ufshcd_dealloc_host); | ||
5360 | |||
5361 | /** | ||
5376 | * ufshcd_set_dma_mask - Set dma mask based on the controller | 5362 | * ufshcd_set_dma_mask - Set dma mask based on the controller |
5377 | * addressing capability | 5363 | * addressing capability |
5378 | * @hba: per adapter instance | 5364 | * @hba: per adapter instance |
@@ -5433,6 +5419,10 @@ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up) | |||
5433 | if (!head || list_empty(head)) | 5419 | if (!head || list_empty(head)) |
5434 | goto out; | 5420 | goto out; |
5435 | 5421 | ||
5422 | ret = ufshcd_vops_clk_scale_notify(hba, scale_up, PRE_CHANGE); | ||
5423 | if (ret) | ||
5424 | return ret; | ||
5425 | |||
5436 | list_for_each_entry(clki, head, list) { | 5426 | list_for_each_entry(clki, head, list) { |
5437 | if (!IS_ERR_OR_NULL(clki->clk)) { | 5427 | if (!IS_ERR_OR_NULL(clki->clk)) { |
5438 | if (scale_up && clki->max_freq) { | 5428 | if (scale_up && clki->max_freq) { |
@@ -5463,8 +5453,9 @@ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up) | |||
5463 | dev_dbg(hba->dev, "%s: clk: %s, rate: %lu\n", __func__, | 5453 | dev_dbg(hba->dev, "%s: clk: %s, rate: %lu\n", __func__, |
5464 | clki->name, clk_get_rate(clki->clk)); | 5454 | clki->name, clk_get_rate(clki->clk)); |
5465 | } | 5455 | } |
5466 | if (hba->vops->clk_scale_notify) | 5456 | |
5467 | hba->vops->clk_scale_notify(hba); | 5457 | ret = ufshcd_vops_clk_scale_notify(hba, scale_up, POST_CHANGE); |
5458 | |||
5468 | out: | 5459 | out: |
5469 | return ret; | 5460 | return ret; |
5470 | } | 5461 | } |
@@ -5619,13 +5610,6 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) | |||
5619 | hba->is_irq_enabled = true; | 5610 | hba->is_irq_enabled = true; |
5620 | } | 5611 | } |
5621 | 5612 | ||
5622 | /* Enable SCSI tag mapping */ | ||
5623 | err = scsi_init_shared_tag_map(host, host->can_queue); | ||
5624 | if (err) { | ||
5625 | dev_err(hba->dev, "init shared queue failed\n"); | ||
5626 | goto exit_gating; | ||
5627 | } | ||
5628 | |||
5629 | err = scsi_add_host(host, hba->dev); | 5613 | err = scsi_add_host(host, hba->dev); |
5630 | if (err) { | 5614 | if (err) { |
5631 | dev_err(hba->dev, "scsi_add_host failed\n"); | 5615 | dev_err(hba->dev, "scsi_add_host failed\n"); |
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index c40a0e78a6c4..2570d9477b37 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h | |||
@@ -223,8 +223,10 @@ struct ufs_clk_info { | |||
223 | bool enabled; | 223 | bool enabled; |
224 | }; | 224 | }; |
225 | 225 | ||
226 | #define PRE_CHANGE 0 | 226 | enum ufs_notify_change_status { |
227 | #define POST_CHANGE 1 | 227 | PRE_CHANGE, |
228 | POST_CHANGE, | ||
229 | }; | ||
228 | 230 | ||
229 | struct ufs_pa_layer_attr { | 231 | struct ufs_pa_layer_attr { |
230 | u32 gear_rx; | 232 | u32 gear_rx; |
@@ -259,22 +261,28 @@ struct ufs_pwr_mode_info { | |||
259 | * to be set. | 261 | * to be set. |
260 | * @suspend: called during host controller PM callback | 262 | * @suspend: called during host controller PM callback |
261 | * @resume: called during host controller PM callback | 263 | * @resume: called during host controller PM callback |
264 | * @dbg_register_dump: used to dump controller debug information | ||
262 | */ | 265 | */ |
263 | struct ufs_hba_variant_ops { | 266 | struct ufs_hba_variant_ops { |
264 | const char *name; | 267 | const char *name; |
265 | int (*init)(struct ufs_hba *); | 268 | int (*init)(struct ufs_hba *); |
266 | void (*exit)(struct ufs_hba *); | 269 | void (*exit)(struct ufs_hba *); |
267 | u32 (*get_ufs_hci_version)(struct ufs_hba *); | 270 | u32 (*get_ufs_hci_version)(struct ufs_hba *); |
268 | void (*clk_scale_notify)(struct ufs_hba *); | 271 | int (*clk_scale_notify)(struct ufs_hba *, bool, |
269 | int (*setup_clocks)(struct ufs_hba *, bool); | 272 | enum ufs_notify_change_status); |
273 | int (*setup_clocks)(struct ufs_hba *, bool); | ||
270 | int (*setup_regulators)(struct ufs_hba *, bool); | 274 | int (*setup_regulators)(struct ufs_hba *, bool); |
271 | int (*hce_enable_notify)(struct ufs_hba *, bool); | 275 | int (*hce_enable_notify)(struct ufs_hba *, |
272 | int (*link_startup_notify)(struct ufs_hba *, bool); | 276 | enum ufs_notify_change_status); |
277 | int (*link_startup_notify)(struct ufs_hba *, | ||
278 | enum ufs_notify_change_status); | ||
273 | int (*pwr_change_notify)(struct ufs_hba *, | 279 | int (*pwr_change_notify)(struct ufs_hba *, |
274 | bool, struct ufs_pa_layer_attr *, | 280 | enum ufs_notify_change_status status, |
281 | struct ufs_pa_layer_attr *, | ||
275 | struct ufs_pa_layer_attr *); | 282 | struct ufs_pa_layer_attr *); |
276 | int (*suspend)(struct ufs_hba *, enum ufs_pm_op); | 283 | int (*suspend)(struct ufs_hba *, enum ufs_pm_op); |
277 | int (*resume)(struct ufs_hba *, enum ufs_pm_op); | 284 | int (*resume)(struct ufs_hba *, enum ufs_pm_op); |
285 | void (*dbg_register_dump)(struct ufs_hba *hba); | ||
278 | }; | 286 | }; |
279 | 287 | ||
280 | /* clock gating state */ | 288 | /* clock gating state */ |
@@ -576,6 +584,7 @@ static inline void ufshcd_rmwl(struct ufs_hba *hba, u32 mask, u32 val, u32 reg) | |||
576 | } | 584 | } |
577 | 585 | ||
578 | int ufshcd_alloc_host(struct device *, struct ufs_hba **); | 586 | int ufshcd_alloc_host(struct device *, struct ufs_hba **); |
587 | void ufshcd_dealloc_host(struct ufs_hba *); | ||
579 | int ufshcd_init(struct ufs_hba * , void __iomem * , unsigned int); | 588 | int ufshcd_init(struct ufs_hba * , void __iomem * , unsigned int); |
580 | void ufshcd_remove(struct ufs_hba *); | 589 | void ufshcd_remove(struct ufs_hba *); |
581 | 590 | ||
@@ -594,6 +603,27 @@ static inline void check_upiu_size(void) | |||
594 | GENERAL_UPIU_REQUEST_SIZE + QUERY_DESC_MAX_SIZE); | 603 | GENERAL_UPIU_REQUEST_SIZE + QUERY_DESC_MAX_SIZE); |
595 | } | 604 | } |
596 | 605 | ||
606 | /** | ||
607 | * ufshcd_set_variant - set variant specific data to the hba | ||
608 | * @hba - per adapter instance | ||
609 | * @variant - pointer to variant specific data | ||
610 | */ | ||
611 | static inline void ufshcd_set_variant(struct ufs_hba *hba, void *variant) | ||
612 | { | ||
613 | BUG_ON(!hba); | ||
614 | hba->priv = variant; | ||
615 | } | ||
616 | |||
617 | /** | ||
618 | * ufshcd_get_variant - get variant specific data from the hba | ||
619 | * @hba - per adapter instance | ||
620 | */ | ||
621 | static inline void *ufshcd_get_variant(struct ufs_hba *hba) | ||
622 | { | ||
623 | BUG_ON(!hba); | ||
624 | return hba->priv; | ||
625 | } | ||
626 | |||
597 | extern int ufshcd_runtime_suspend(struct ufs_hba *hba); | 627 | extern int ufshcd_runtime_suspend(struct ufs_hba *hba); |
598 | extern int ufshcd_runtime_resume(struct ufs_hba *hba); | 628 | extern int ufshcd_runtime_resume(struct ufs_hba *hba); |
599 | extern int ufshcd_runtime_idle(struct ufs_hba *hba); | 629 | extern int ufshcd_runtime_idle(struct ufs_hba *hba); |
@@ -653,4 +683,109 @@ static inline int ufshcd_dme_peer_get(struct ufs_hba *hba, | |||
653 | 683 | ||
654 | int ufshcd_hold(struct ufs_hba *hba, bool async); | 684 | int ufshcd_hold(struct ufs_hba *hba, bool async); |
655 | void ufshcd_release(struct ufs_hba *hba); | 685 | void ufshcd_release(struct ufs_hba *hba); |
686 | |||
687 | /* Wrapper functions for safely calling variant operations */ | ||
688 | static inline const char *ufshcd_get_var_name(struct ufs_hba *hba) | ||
689 | { | ||
690 | if (hba->vops) | ||
691 | return hba->vops->name; | ||
692 | return ""; | ||
693 | } | ||
694 | |||
695 | static inline int ufshcd_vops_init(struct ufs_hba *hba) | ||
696 | { | ||
697 | if (hba->vops && hba->vops->init) | ||
698 | return hba->vops->init(hba); | ||
699 | |||
700 | return 0; | ||
701 | } | ||
702 | |||
703 | static inline void ufshcd_vops_exit(struct ufs_hba *hba) | ||
704 | { | ||
705 | if (hba->vops && hba->vops->exit) | ||
706 | return hba->vops->exit(hba); | ||
707 | } | ||
708 | |||
709 | static inline u32 ufshcd_vops_get_ufs_hci_version(struct ufs_hba *hba) | ||
710 | { | ||
711 | if (hba->vops && hba->vops->get_ufs_hci_version) | ||
712 | return hba->vops->get_ufs_hci_version(hba); | ||
713 | |||
714 | return ufshcd_readl(hba, REG_UFS_VERSION); | ||
715 | } | ||
716 | |||
717 | static inline int ufshcd_vops_clk_scale_notify(struct ufs_hba *hba, | ||
718 | bool up, enum ufs_notify_change_status status) | ||
719 | { | ||
720 | if (hba->vops && hba->vops->clk_scale_notify) | ||
721 | return hba->vops->clk_scale_notify(hba, up, status); | ||
722 | return 0; | ||
723 | } | ||
724 | |||
725 | static inline int ufshcd_vops_setup_clocks(struct ufs_hba *hba, bool on) | ||
726 | { | ||
727 | if (hba->vops && hba->vops->setup_clocks) | ||
728 | return hba->vops->setup_clocks(hba, on); | ||
729 | return 0; | ||
730 | } | ||
731 | |||
732 | static inline int ufshcd_vops_setup_regulators(struct ufs_hba *hba, bool status) | ||
733 | { | ||
734 | if (hba->vops && hba->vops->setup_regulators) | ||
735 | return hba->vops->setup_regulators(hba, status); | ||
736 | |||
737 | return 0; | ||
738 | } | ||
739 | |||
740 | static inline int ufshcd_vops_hce_enable_notify(struct ufs_hba *hba, | ||
741 | bool status) | ||
742 | { | ||
743 | if (hba->vops && hba->vops->hce_enable_notify) | ||
744 | return hba->vops->hce_enable_notify(hba, status); | ||
745 | |||
746 | return 0; | ||
747 | } | ||
748 | static inline int ufshcd_vops_link_startup_notify(struct ufs_hba *hba, | ||
749 | bool status) | ||
750 | { | ||
751 | if (hba->vops && hba->vops->link_startup_notify) | ||
752 | return hba->vops->link_startup_notify(hba, status); | ||
753 | |||
754 | return 0; | ||
755 | } | ||
756 | |||
757 | static inline int ufshcd_vops_pwr_change_notify(struct ufs_hba *hba, | ||
758 | bool status, | ||
759 | struct ufs_pa_layer_attr *dev_max_params, | ||
760 | struct ufs_pa_layer_attr *dev_req_params) | ||
761 | { | ||
762 | if (hba->vops && hba->vops->pwr_change_notify) | ||
763 | return hba->vops->pwr_change_notify(hba, status, | ||
764 | dev_max_params, dev_req_params); | ||
765 | |||
766 | return -ENOTSUPP; | ||
767 | } | ||
768 | |||
769 | static inline int ufshcd_vops_suspend(struct ufs_hba *hba, enum ufs_pm_op op) | ||
770 | { | ||
771 | if (hba->vops && hba->vops->suspend) | ||
772 | return hba->vops->suspend(hba, op); | ||
773 | |||
774 | return 0; | ||
775 | } | ||
776 | |||
777 | static inline int ufshcd_vops_resume(struct ufs_hba *hba, enum ufs_pm_op op) | ||
778 | { | ||
779 | if (hba->vops && hba->vops->resume) | ||
780 | return hba->vops->resume(hba, op); | ||
781 | |||
782 | return 0; | ||
783 | } | ||
784 | |||
785 | static inline void ufshcd_vops_dbg_register_dump(struct ufs_hba *hba) | ||
786 | { | ||
787 | if (hba->vops && hba->vops->dbg_register_dump) | ||
788 | hba->vops->dbg_register_dump(hba); | ||
789 | } | ||
790 | |||
656 | #endif /* End of Header */ | 791 | #endif /* End of Header */ |