From aba3b0757b6cba37953bb1d7e71c460d37fd496a Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Mon, 6 Sep 2021 16:53:07 +0800 Subject: scsi: ufs: ufs-mediatek: Change dbg select by check IP version Mediatek UFS dbg select setting is changed in new IP version. Check the IP version before setting dbg select. Link: https://lore.kernel.org/r/1630918387-8333-1-git-send-email-peter.wang@mediatek.com Signed-off-by: Peter Wang Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs-mediatek.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'drivers/scsi/ufs/ufs-mediatek.c') diff --git a/drivers/scsi/ufs/ufs-mediatek.c b/drivers/scsi/ufs/ufs-mediatek.c index 80b3545dd17d..d2d7e76c5ec8 100644 --- a/drivers/scsi/ufs/ufs-mediatek.c +++ b/drivers/scsi/ufs/ufs-mediatek.c @@ -296,6 +296,21 @@ static void ufs_mtk_setup_ref_clk_wait_us(struct ufs_hba *hba, host->ref_clk_ungating_wait_us = ungating_us; } +static void ufs_mtk_dbg_sel(struct ufs_hba *hba) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + + if (((host->ip_ver >> 16) & 0xFF) >= 0x36) { + ufshcd_writel(hba, 0x820820, REG_UFS_DEBUG_SEL); + ufshcd_writel(hba, 0x0, REG_UFS_DEBUG_SEL_B0); + ufshcd_writel(hba, 0x55555555, REG_UFS_DEBUG_SEL_B1); + ufshcd_writel(hba, 0xaaaaaaaa, REG_UFS_DEBUG_SEL_B2); + ufshcd_writel(hba, 0xffffffff, REG_UFS_DEBUG_SEL_B3); + } else { + ufshcd_writel(hba, 0x20, REG_UFS_DEBUG_SEL); + } +} + static int ufs_mtk_wait_link_state(struct ufs_hba *hba, u32 state, unsigned long max_wait_ms) { @@ -305,7 +320,7 @@ static int ufs_mtk_wait_link_state(struct ufs_hba *hba, u32 state, timeout = ktime_add_ms(ktime_get(), max_wait_ms); do { time_checked = ktime_get(); - ufshcd_writel(hba, 0x20, REG_UFS_DEBUG_SEL); + ufs_mtk_dbg_sel(hba); val = ufshcd_readl(hba, REG_UFS_PROBE); val = val >> 28; @@ -689,6 +704,8 @@ static int ufs_mtk_init(struct ufs_hba *hba) ufs_mtk_mphy_power_on(hba, true); ufs_mtk_setup_clocks(hba, true, POST_CHANGE); + host->ip_ver = ufshcd_readl(hba, REG_UFS_MTK_IP_VER); + goto out; out_variant_clear: @@ -1001,7 +1018,7 @@ static void ufs_mtk_dbg_register_dump(struct ufs_hba *hba) "MPHY Ctrl "); /* Direct debugging information to REG_MTK_PROBE */ - ufshcd_writel(hba, 0x20, REG_UFS_DEBUG_SEL); + ufs_mtk_dbg_sel(hba); ufshcd_dump_regs(hba, REG_UFS_PROBE, 0x4, "Debug Probe "); } -- cgit v1.2.3 From 9561f58442e48c3c71da250aa63bd02ec6bffcb5 Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Wed, 6 Oct 2021 13:47:05 +0800 Subject: scsi: ufs: mediatek: Support vops pre suspend to disable auto-hibern8 Mediatek UFS needs auto-hibern8 disabled before suspend. Introduce a solution to do pre-suspend before SSU (sleep). Link: https://lore.kernel.org/r/20211006054705.21885-1-peter.wang@mediatek.com Reviewed-by: Bart Van Assche Reviewed-by: Bean Huo Signed-off-by: Peter Wang Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs-exynos.c | 6 +++- drivers/scsi/ufs/ufs-hisi.c | 6 +++- drivers/scsi/ufs/ufs-mediatek.c | 68 ++++++++++++++++++++++++++++++++++++++++- drivers/scsi/ufs/ufs-mediatek.h | 20 ++++++++++++ drivers/scsi/ufs/ufs-qcom.c | 6 +++- drivers/scsi/ufs/ufshcd.c | 9 ++++-- drivers/scsi/ufs/ufshcd.h | 8 +++-- 7 files changed, 114 insertions(+), 9 deletions(-) (limited to 'drivers/scsi/ufs/ufs-mediatek.c') diff --git a/drivers/scsi/ufs/ufs-exynos.c b/drivers/scsi/ufs/ufs-exynos.c index a14dd8ce56d4..b2ec9e20b14d 100644 --- a/drivers/scsi/ufs/ufs-exynos.c +++ b/drivers/scsi/ufs/ufs-exynos.c @@ -1176,10 +1176,14 @@ static void exynos_ufs_hibern8_notify(struct ufs_hba *hba, } } -static int exynos_ufs_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) +static int exynos_ufs_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op, + enum ufs_notify_change_status status) { struct exynos_ufs *ufs = ufshcd_get_variant(hba); + if (status == PRE_CHANGE) + return 0; + if (!ufshcd_is_link_active(hba)) phy_power_off(ufs->phy); diff --git a/drivers/scsi/ufs/ufs-hisi.c b/drivers/scsi/ufs/ufs-hisi.c index 6b706de8354b..8c7e8d321746 100644 --- a/drivers/scsi/ufs/ufs-hisi.c +++ b/drivers/scsi/ufs/ufs-hisi.c @@ -396,10 +396,14 @@ out: return ret; } -static int ufs_hisi_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) +static int ufs_hisi_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op, + enum ufs_notify_change_status status) { struct ufs_hisi_host *host = ufshcd_get_variant(hba); + if (status == PRE_CHANGE) + return 0; + if (pm_op == UFS_RUNTIME_PM) return 0; diff --git a/drivers/scsi/ufs/ufs-mediatek.c b/drivers/scsi/ufs/ufs-mediatek.c index d2d7e76c5ec8..d1696db70ce8 100644 --- a/drivers/scsi/ufs/ufs-mediatek.c +++ b/drivers/scsi/ufs/ufs-mediatek.c @@ -311,6 +311,46 @@ static void ufs_mtk_dbg_sel(struct ufs_hba *hba) } } +static void ufs_mtk_wait_idle_state(struct ufs_hba *hba, + unsigned long retry_ms) +{ + u64 timeout, time_checked; + u32 val, sm; + bool wait_idle; + + timeout = sched_clock() + retry_ms * 1000000UL; + + + /* wait a specific time after check base */ + udelay(10); + wait_idle = false; + + do { + time_checked = sched_clock(); + ufs_mtk_dbg_sel(hba); + val = ufshcd_readl(hba, REG_UFS_PROBE); + + sm = val & 0x1f; + + /* + * if state is in H8 enter and H8 enter confirm + * wait until return to idle state. + */ + if ((sm >= VS_HIB_ENTER) && (sm <= VS_HIB_EXIT)) { + wait_idle = true; + udelay(50); + continue; + } else if (!wait_idle) + break; + + if (wait_idle && (sm == VS_HCE_BASE)) + break; + } while (time_checked < timeout); + + if (wait_idle && sm != VS_HCE_BASE) + dev_info(hba->dev, "wait idle tmo: 0x%x\n", val); +} + static int ufs_mtk_wait_link_state(struct ufs_hba *hba, u32 state, unsigned long max_wait_ms) { @@ -949,11 +989,37 @@ static void ufs_mtk_vreg_set_lpm(struct ufs_hba *hba, bool lpm) REGULATOR_MODE_NORMAL); } -static int ufs_mtk_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) +static void ufs_mtk_auto_hibern8_disable(struct ufs_hba *hba) +{ + unsigned long flags; + int ret; + + /* disable auto-hibern8 */ + spin_lock_irqsave(hba->host->host_lock, flags); + ufshcd_writel(hba, 0, REG_AUTO_HIBERNATE_IDLE_TIMER); + spin_unlock_irqrestore(hba->host->host_lock, flags); + + /* wait host return to idle state when auto-hibern8 off */ + ufs_mtk_wait_idle_state(hba, 5); + + ret = ufs_mtk_wait_link_state(hba, VS_LINK_UP, 100); + if (ret) + dev_warn(hba->dev, "exit h8 state fail, ret=%d\n", ret); +} + +static int ufs_mtk_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op, + enum ufs_notify_change_status status) { int err; struct arm_smccc_res res; + if (status == PRE_CHANGE) { + if (!ufshcd_is_auto_hibern8_supported(hba)) + return 0; + ufs_mtk_auto_hibern8_disable(hba); + return 0; + } + if (ufshcd_is_link_hibern8(hba)) { err = ufs_mtk_link_set_lpm(hba); if (err) diff --git a/drivers/scsi/ufs/ufs-mediatek.h b/drivers/scsi/ufs/ufs-mediatek.h index 524c8e2c1e6f..c96b9b529ee2 100644 --- a/drivers/scsi/ufs/ufs-mediatek.h +++ b/drivers/scsi/ufs/ufs-mediatek.h @@ -54,6 +54,26 @@ enum { VS_LINK_CFG = 5, }; +/* + * Vendor specific host controller state + */ +enum { + VS_HCE_RESET = 0, + VS_HCE_BASE = 1, + VS_HCE_OOCPR_WAIT = 2, + VS_HCE_DME_RESET = 3, + VS_HCE_MIDDLE = 4, + VS_HCE_DME_ENABLE = 5, + VS_HCE_DEFAULTS = 6, + VS_HIB_IDLEEN = 7, + VS_HIB_ENTER = 8, + VS_HIB_ENTER_CONF = 9, + VS_HIB_MIDDLE = 10, + VS_HIB_WAITTIMER = 11, + VS_HIB_EXIT_CONF = 12, + VS_HIB_EXIT = 13, +}; + /* * SiP commands */ diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 92f5bb4965fa..0d2e950d0865 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -589,11 +589,15 @@ static void ufs_qcom_device_reset_ctrl(struct ufs_hba *hba, bool asserted) gpiod_set_value_cansleep(host->device_reset, asserted); } -static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) +static int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op, + enum ufs_notify_change_status status) { struct ufs_qcom_host *host = ufshcd_get_variant(hba); struct phy *phy = host->generic_phy; + if (status == PRE_CHANGE) + return 0; + if (ufs_qcom_is_link_off(hba)) { /* * Disable the tx/rx lane symbol clocks before PHY is diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 9283ab4ca2da..3de380a97a9b 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -8844,6 +8844,10 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) flush_work(&hba->eeh_work); + ret = ufshcd_vops_suspend(hba, pm_op, PRE_CHANGE); + if (ret) + goto enable_scaling; + if (req_dev_pwr_mode != hba->curr_dev_pwr_mode) { if (pm_op != UFS_RUNTIME_PM) /* ensure that bkops is disabled */ @@ -8871,7 +8875,7 @@ vops_suspend: * vendor specific host controller register space call them before the * host clocks are ON. */ - ret = ufshcd_vops_suspend(hba, pm_op); + ret = ufshcd_vops_suspend(hba, pm_op, POST_CHANGE); if (ret) goto set_link_active; goto out; @@ -8999,7 +9003,8 @@ static int __ufshcd_wl_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) set_old_link_state: ufshcd_link_state_transition(hba, old_link_state, 0); vendor_suspend: - ufshcd_vops_suspend(hba, pm_op); + ufshcd_vops_suspend(hba, pm_op, PRE_CHANGE); + ufshcd_vops_suspend(hba, pm_op, POST_CHANGE); out: if (ret) ufshcd_update_evt_hist(hba, UFS_EVT_WL_RES_ERR, (u32)ret); diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index a50dfdac79f6..64ce723327b9 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -344,7 +344,8 @@ struct ufs_hba_variant_ops { enum ufs_notify_change_status); int (*apply_dev_quirks)(struct ufs_hba *hba); void (*fixup_dev_quirks)(struct ufs_hba *hba); - int (*suspend)(struct ufs_hba *, enum ufs_pm_op); + int (*suspend)(struct ufs_hba *, enum ufs_pm_op, + enum ufs_notify_change_status); int (*resume)(struct ufs_hba *, enum ufs_pm_op); void (*dbg_register_dump)(struct ufs_hba *hba); int (*phy_initialization)(struct ufs_hba *); @@ -1318,10 +1319,11 @@ static inline void ufshcd_vops_fixup_dev_quirks(struct ufs_hba *hba) hba->vops->fixup_dev_quirks(hba); } -static inline int ufshcd_vops_suspend(struct ufs_hba *hba, enum ufs_pm_op op) +static inline int ufshcd_vops_suspend(struct ufs_hba *hba, enum ufs_pm_op op, + enum ufs_notify_change_status status) { if (hba->vops && hba->vops->suspend) - return hba->vops->suspend(hba, op); + return hba->vops->suspend(hba, op, status); return 0; } -- cgit v1.2.3 From fc65e933fbcce09eb6d0b3080694229f6add38bf Mon Sep 17 00:00:00 2001 From: Stanley Chu Date: Sat, 16 Oct 2021 08:58:00 +0800 Subject: scsi: ufs: ufs-mediatek: Introduce default delay for reference clock Introduce default delay time for gating or ungating reference clock instead of ambiguous magic numbers. The defined value is suitable for all current MediaTek UFS platforms. Link: https://lore.kernel.org/r/20211016005802.7729-2-stanley.chu@mediatek.com Signed-off-by: Stanley Chu Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs-mediatek.c | 13 ++++++++----- drivers/scsi/ufs/ufs-mediatek.h | 1 + 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers/scsi/ufs/ufs-mediatek.c') diff --git a/drivers/scsi/ufs/ufs-mediatek.c b/drivers/scsi/ufs/ufs-mediatek.c index d1696db70ce8..2c7d12a30493 100644 --- a/drivers/scsi/ufs/ufs-mediatek.c +++ b/drivers/scsi/ufs/ufs-mediatek.c @@ -282,7 +282,7 @@ out: } static void ufs_mtk_setup_ref_clk_wait_us(struct ufs_hba *hba, - u16 gating_us, u16 ungating_us) + u16 gating_us) { struct ufs_mtk_host *host = ufshcd_get_variant(hba); @@ -293,7 +293,7 @@ static void ufs_mtk_setup_ref_clk_wait_us(struct ufs_hba *hba, host->ref_clk_gating_wait_us = gating_us; } - host->ref_clk_ungating_wait_us = ungating_us; + host->ref_clk_ungating_wait_us = REFCLK_DEFAULT_WAIT_US; } static void ufs_mtk_dbg_sel(struct ufs_hba *hba) @@ -1102,11 +1102,14 @@ static int ufs_mtk_apply_dev_quirks(struct ufs_hba *hba) * requirements. */ if (mid == UFS_VENDOR_SAMSUNG) - ufs_mtk_setup_ref_clk_wait_us(hba, 1, 1); + ufs_mtk_setup_ref_clk_wait_us(hba, 1); else if (mid == UFS_VENDOR_SKHYNIX) - ufs_mtk_setup_ref_clk_wait_us(hba, 30, 30); + ufs_mtk_setup_ref_clk_wait_us(hba, 30); else if (mid == UFS_VENDOR_TOSHIBA) - ufs_mtk_setup_ref_clk_wait_us(hba, 100, 32); + ufs_mtk_setup_ref_clk_wait_us(hba, 100); + else + ufs_mtk_setup_ref_clk_wait_us(hba, + REFCLK_DEFAULT_WAIT_US); return 0; } diff --git a/drivers/scsi/ufs/ufs-mediatek.h b/drivers/scsi/ufs/ufs-mediatek.h index c96b9b529ee2..414dca86c09f 100644 --- a/drivers/scsi/ufs/ufs-mediatek.h +++ b/drivers/scsi/ufs/ufs-mediatek.h @@ -34,6 +34,7 @@ #define REFCLK_ACK BIT(1) #define REFCLK_REQ_TIMEOUT_US 3000 +#define REFCLK_DEFAULT_WAIT_US 32 /* * Other attributes -- cgit v1.2.3 From 1eaff502a8f132d518946bc652d53df79a9a4cd9 Mon Sep 17 00:00:00 2001 From: Stanley Chu Date: Sat, 16 Oct 2021 08:58:01 +0800 Subject: scsi: ufs: ufs-mediatek: Fix build error caused by use of sched_clock() Add proper header for using sched_clock(). Link: https://lore.kernel.org/r/20211016005802.7729-3-stanley.chu@mediatek.com Signed-off-by: Stanley Chu Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs-mediatek.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/scsi/ufs/ufs-mediatek.c') diff --git a/drivers/scsi/ufs/ufs-mediatek.c b/drivers/scsi/ufs/ufs-mediatek.c index 2c7d12a30493..bc193ba46b31 100644 --- a/drivers/scsi/ufs/ufs-mediatek.c +++ b/drivers/scsi/ufs/ufs-mediatek.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "ufshcd.h" -- cgit v1.2.3 From 25d542a8537455a3335448a3fa53cf3704883389 Mon Sep 17 00:00:00 2001 From: Peter Wang Date: Sat, 16 Oct 2021 08:58:02 +0800 Subject: scsi: ufs: ufs-mediatek: Fix wrong location for ref-clk delay Fix the location of delay for ref-clk gating and ungating in ufs_mtk_setup_ref_clk(). Link: https://lore.kernel.org/r/20211016005802.7729-4-stanley.chu@mediatek.com Reviewed-by: Stanley Chu Signed-off-by: Peter Wang Signed-off-by: Stanley Chu Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs-mediatek.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/scsi/ufs/ufs-mediatek.c') diff --git a/drivers/scsi/ufs/ufs-mediatek.c b/drivers/scsi/ufs/ufs-mediatek.c index bc193ba46b31..d0a8e1319ab3 100644 --- a/drivers/scsi/ufs/ufs-mediatek.c +++ b/drivers/scsi/ufs/ufs-mediatek.c @@ -247,9 +247,9 @@ static int ufs_mtk_setup_ref_clk(struct ufs_hba *hba, bool on) if (on) { ufs_mtk_ref_clk_notify(on, res); - ufshcd_delay_us(host->ref_clk_ungating_wait_us, 10); ufshcd_writel(hba, REFCLK_REQUEST, REG_UFS_REFCLK_CTRL); } else { + ufshcd_delay_us(host->ref_clk_gating_wait_us, 10); ufshcd_writel(hba, REFCLK_RELEASE, REG_UFS_REFCLK_CTRL); } @@ -274,10 +274,10 @@ static int ufs_mtk_setup_ref_clk(struct ufs_hba *hba, bool on) out: host->ref_clk_enabled = on; - if (!on) { - ufshcd_delay_us(host->ref_clk_gating_wait_us, 10); + if (on) + ufshcd_delay_us(host->ref_clk_ungating_wait_us, 10); + else ufs_mtk_ref_clk_notify(on, res); - } return 0; } -- cgit v1.2.3 From bb4a8dcb4e9498deccaf7b257449044968e514c7 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 18 Oct 2021 15:20:01 +0200 Subject: scsi: ufs: mediatek: Avoid sched_clock() misuse sched_clock() is not meant to be used in portable driver code, and assuming a particular clock frequency is not how this is meant to be used. It also causes a build failure because of a missing header inclusion: drivers/scsi/ufs/ufs-mediatek.c:321:12: error: implicit declaration of function 'sched_clock' [-Werror,-Wimplicit-function-declaration] timeout = sched_clock() + retry_ms * 1000000UL; A better interface to use here ktime_get_mono_fast_ns(), which works mostly like ktime_get() but is safe to use inside of a suspend callback. Link: https://lore.kernel.org/r/20211018132022.2281589-1-arnd@kernel.org Fixes: 9561f58442e4 ("scsi: ufs: mediatek: Support vops pre suspend to disable auto-hibern8") Reviewed-by: Stanley Chu Signed-off-by: Arnd Bergmann Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs-mediatek.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/scsi/ufs/ufs-mediatek.c') diff --git a/drivers/scsi/ufs/ufs-mediatek.c b/drivers/scsi/ufs/ufs-mediatek.c index d0a8e1319ab3..fc5b214347b3 100644 --- a/drivers/scsi/ufs/ufs-mediatek.c +++ b/drivers/scsi/ufs/ufs-mediatek.c @@ -319,15 +319,15 @@ static void ufs_mtk_wait_idle_state(struct ufs_hba *hba, u32 val, sm; bool wait_idle; - timeout = sched_clock() + retry_ms * 1000000UL; - + /* cannot use plain ktime_get() in suspend */ + timeout = ktime_get_mono_fast_ns() + retry_ms * 1000000UL; /* wait a specific time after check base */ udelay(10); wait_idle = false; do { - time_checked = sched_clock(); + time_checked = ktime_get_mono_fast_ns(); ufs_mtk_dbg_sel(hba); val = ufshcd_readl(hba, REG_UFS_PROBE); -- cgit v1.2.3