Commit 1e879e8f authored by Subhash Jadavani's avatar Subhash Jadavani Committed by Martin K. Petersen

scsi: ufshcd: Fix possible unclocked register access

Vendor specific setup_clocks callback may require the clocks managed by
ufshcd driver to be ON. So if the vendor specific setup_clocks callback
is called while the required clocks are turned off, it could result into
unclocked register access.

To prevent possible unclock register access, this change adds one more
argument to setup_clocks callback to let it know whether it is called
pre/post the clock changes by core driver.
Signed-off-by: default avatarSubhash Jadavani <subhashj@codeaurora.org>
Reviewed-by: default avatarKiwoong Kim <kwmad.kim@samsung.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent fd37f66e
...@@ -1094,10 +1094,12 @@ static void ufs_qcom_set_caps(struct ufs_hba *hba) ...@@ -1094,10 +1094,12 @@ static void ufs_qcom_set_caps(struct ufs_hba *hba)
* ufs_qcom_setup_clocks - enables/disable clocks * ufs_qcom_setup_clocks - enables/disable clocks
* @hba: host controller instance * @hba: host controller instance
* @on: If true, enable clocks else disable them. * @on: If true, enable clocks else disable them.
* @status: PRE_CHANGE or POST_CHANGE notify
* *
* Returns 0 on success, non-zero on failure. * Returns 0 on success, non-zero on failure.
*/ */
static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on) static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on,
enum ufs_notify_change_status status)
{ {
struct ufs_qcom_host *host = ufshcd_get_variant(hba); struct ufs_qcom_host *host = ufshcd_get_variant(hba);
int err; int err;
...@@ -1111,7 +1113,7 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on) ...@@ -1111,7 +1113,7 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on)
if (!host) if (!host)
return 0; return 0;
if (on) { if (on && (status == POST_CHANGE)) {
err = ufs_qcom_phy_enable_iface_clk(host->generic_phy); err = ufs_qcom_phy_enable_iface_clk(host->generic_phy);
if (err) if (err)
goto out; goto out;
...@@ -1130,7 +1132,7 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on) ...@@ -1130,7 +1132,7 @@ static int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on)
if (vote == host->bus_vote.min_bw_vote) if (vote == host->bus_vote.min_bw_vote)
ufs_qcom_update_bus_bw_vote(host); ufs_qcom_update_bus_bw_vote(host);
} else { } else if (!on && (status == PRE_CHANGE)) {
/* M-PHY RMMI interface clocks can be turned off */ /* M-PHY RMMI interface clocks can be turned off */
ufs_qcom_phy_disable_iface_clk(host->generic_phy); ufs_qcom_phy_disable_iface_clk(host->generic_phy);
...@@ -1254,7 +1256,7 @@ static int ufs_qcom_init(struct ufs_hba *hba) ...@@ -1254,7 +1256,7 @@ static int ufs_qcom_init(struct ufs_hba *hba)
ufs_qcom_set_caps(hba); ufs_qcom_set_caps(hba);
ufs_qcom_advertise_quirks(hba); ufs_qcom_advertise_quirks(hba);
ufs_qcom_setup_clocks(hba, true); ufs_qcom_setup_clocks(hba, true, POST_CHANGE);
if (hba->dev->id < MAX_UFS_QCOM_HOSTS) if (hba->dev->id < MAX_UFS_QCOM_HOSTS)
ufs_qcom_hosts[hba->dev->id] = host; ufs_qcom_hosts[hba->dev->id] = host;
......
...@@ -5389,6 +5389,10 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on, ...@@ -5389,6 +5389,10 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
if (!head || list_empty(head)) if (!head || list_empty(head))
goto out; goto out;
ret = ufshcd_vops_setup_clocks(hba, on, PRE_CHANGE);
if (ret)
return ret;
list_for_each_entry(clki, head, list) { list_for_each_entry(clki, head, list) {
if (!IS_ERR_OR_NULL(clki->clk)) { if (!IS_ERR_OR_NULL(clki->clk)) {
if (skip_ref_clk && !strcmp(clki->name, "ref_clk")) if (skip_ref_clk && !strcmp(clki->name, "ref_clk"))
...@@ -5410,7 +5414,10 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on, ...@@ -5410,7 +5414,10 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
} }
} }
ret = ufshcd_vops_setup_clocks(hba, on); ret = ufshcd_vops_setup_clocks(hba, on, POST_CHANGE);
if (ret)
return ret;
out: out:
if (ret) { if (ret) {
list_for_each_entry(clki, head, list) { list_for_each_entry(clki, head, list) {
...@@ -5500,8 +5507,6 @@ static void ufshcd_variant_hba_exit(struct ufs_hba *hba) ...@@ -5500,8 +5507,6 @@ static void ufshcd_variant_hba_exit(struct ufs_hba *hba)
if (!hba->vops) if (!hba->vops)
return; return;
ufshcd_vops_setup_clocks(hba, false);
ufshcd_vops_setup_regulators(hba, false); ufshcd_vops_setup_regulators(hba, false);
ufshcd_vops_exit(hba); ufshcd_vops_exit(hba);
...@@ -5905,10 +5910,6 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) ...@@ -5905,10 +5910,6 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
if (ret) if (ret)
goto set_link_active; goto set_link_active;
ret = ufshcd_vops_setup_clocks(hba, false);
if (ret)
goto vops_resume;
if (!ufshcd_is_link_active(hba)) if (!ufshcd_is_link_active(hba))
ufshcd_setup_clocks(hba, false); ufshcd_setup_clocks(hba, false);
else else
...@@ -5925,8 +5926,6 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) ...@@ -5925,8 +5926,6 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
ufshcd_hba_vreg_set_lpm(hba); ufshcd_hba_vreg_set_lpm(hba);
goto out; goto out;
vops_resume:
ufshcd_vops_resume(hba, pm_op);
set_link_active: set_link_active:
ufshcd_vreg_set_hpm(hba); ufshcd_vreg_set_hpm(hba);
if (ufshcd_is_link_hibern8(hba) && !ufshcd_uic_hibern8_exit(hba)) if (ufshcd_is_link_hibern8(hba) && !ufshcd_uic_hibern8_exit(hba))
......
...@@ -273,7 +273,8 @@ struct ufs_hba_variant_ops { ...@@ -273,7 +273,8 @@ struct ufs_hba_variant_ops {
u32 (*get_ufs_hci_version)(struct ufs_hba *); u32 (*get_ufs_hci_version)(struct ufs_hba *);
int (*clk_scale_notify)(struct ufs_hba *, bool, int (*clk_scale_notify)(struct ufs_hba *, bool,
enum ufs_notify_change_status); enum ufs_notify_change_status);
int (*setup_clocks)(struct ufs_hba *, bool); int (*setup_clocks)(struct ufs_hba *, bool,
enum ufs_notify_change_status);
int (*setup_regulators)(struct ufs_hba *, bool); int (*setup_regulators)(struct ufs_hba *, bool);
int (*hce_enable_notify)(struct ufs_hba *, int (*hce_enable_notify)(struct ufs_hba *,
enum ufs_notify_change_status); enum ufs_notify_change_status);
...@@ -755,10 +756,11 @@ static inline int ufshcd_vops_clk_scale_notify(struct ufs_hba *hba, ...@@ -755,10 +756,11 @@ static inline int ufshcd_vops_clk_scale_notify(struct ufs_hba *hba,
return 0; return 0;
} }
static inline int ufshcd_vops_setup_clocks(struct ufs_hba *hba, bool on) static inline int ufshcd_vops_setup_clocks(struct ufs_hba *hba, bool on,
enum ufs_notify_change_status status)
{ {
if (hba->vops && hba->vops->setup_clocks) if (hba->vops && hba->vops->setup_clocks)
return hba->vops->setup_clocks(hba, on); return hba->vops->setup_clocks(hba, on, status);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment