diff options
Diffstat (limited to 'drivers/scsi/ufs')
-rw-r--r-- | drivers/scsi/ufs/ufs-mediatek.c | 62 | ||||
-rw-r--r-- | drivers/scsi/ufs/ufs-mediatek.h | 22 |
2 files changed, 78 insertions, 6 deletions
diff --git a/drivers/scsi/ufs/ufs-mediatek.c b/drivers/scsi/ufs/ufs-mediatek.c index 37e2c91fc452..fc5ba21ec02a 100644 --- a/drivers/scsi/ufs/ufs-mediatek.c +++ b/drivers/scsi/ufs/ufs-mediatek.c @@ -22,6 +22,9 @@ arm_smccc_smc(MTK_SIP_UFS_CONTROL, \ cmd, val, 0, 0, 0, 0, 0, &(res)) +#define ufs_mtk_ref_clk_notify(on, res) \ + ufs_mtk_smc(UFS_MTK_SIP_REF_CLK_NOTIFICATION, on, res) + #define ufs_mtk_device_reset_ctrl(high, res) \ ufs_mtk_smc(UFS_MTK_SIP_DEVICE_RESET, high, res) @@ -90,6 +93,49 @@ static int ufs_mtk_bind_mphy(struct ufs_hba *hba) return err; } +static int ufs_mtk_setup_ref_clk(struct ufs_hba *hba, bool on) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + struct arm_smccc_res res; + unsigned long timeout; + u32 value; + + if (host->ref_clk_enabled == on) + return 0; + + if (on) { + ufs_mtk_ref_clk_notify(on, res); + ufshcd_writel(hba, REFCLK_REQUEST, REG_UFS_REFCLK_CTRL); + } else { + ufshcd_writel(hba, REFCLK_RELEASE, REG_UFS_REFCLK_CTRL); + } + + /* Wait for ack */ + timeout = jiffies + msecs_to_jiffies(REFCLK_REQ_TIMEOUT_MS); + do { + value = ufshcd_readl(hba, REG_UFS_REFCLK_CTRL); + + /* Wait until ack bit equals to req bit */ + if (((value & REFCLK_ACK) >> 1) == (value & REFCLK_REQUEST)) + goto out; + + usleep_range(100, 200); + } while (time_before(jiffies, timeout)); + + dev_err(hba->dev, "missing ack of refclk req, reg: 0x%x\n", value); + + ufs_mtk_ref_clk_notify(host->ref_clk_enabled, res); + + return -ETIMEDOUT; + +out: + host->ref_clk_enabled = on; + if (!on) + ufs_mtk_ref_clk_notify(on, res); + + return 0; +} + /** * ufs_mtk_setup_clocks - enables/disable clocks * @hba: host controller instance @@ -114,12 +160,16 @@ static int ufs_mtk_setup_clocks(struct ufs_hba *hba, bool on, switch (status) { case PRE_CHANGE: - if (!on) + if (!on) { + ufs_mtk_setup_ref_clk(hba, on); ret = phy_power_off(host->mphy); + } break; case POST_CHANGE: - if (on) + if (on) { ret = phy_power_on(host->mphy); + ufs_mtk_setup_ref_clk(hba, on); + } break; } @@ -305,8 +355,10 @@ static int ufs_mtk_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) { struct ufs_mtk_host *host = ufshcd_get_variant(hba); - if (ufshcd_is_link_hibern8(hba)) + if (ufshcd_is_link_hibern8(hba)) { phy_power_off(host->mphy); + ufs_mtk_setup_ref_clk(hba, false); + } return 0; } @@ -315,8 +367,10 @@ static int ufs_mtk_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) { struct ufs_mtk_host *host = ufshcd_get_variant(hba); - if (ufshcd_is_link_hibern8(hba)) + if (ufshcd_is_link_hibern8(hba)) { + ufs_mtk_setup_ref_clk(hba, true); phy_power_on(host->mphy); + } return 0; } diff --git a/drivers/scsi/ufs/ufs-mediatek.h b/drivers/scsi/ufs/ufs-mediatek.h index ce68ce25fdd7..31b7fead19eb 100644 --- a/drivers/scsi/ufs/ufs-mediatek.h +++ b/drivers/scsi/ufs/ufs-mediatek.h @@ -10,6 +10,22 @@ #include <linux/soc/mediatek/mtk_sip_svc.h> /* + * Vendor specific UFSHCI Registers + */ +#define REG_UFS_REFCLK_CTRL 0x144 + +/* + * Ref-clk control + * + * Values for register REG_UFS_REFCLK_CTRL + */ +#define REFCLK_RELEASE 0x0 +#define REFCLK_REQUEST BIT(0) +#define REFCLK_ACK BIT(1) + +#define REFCLK_REQ_TIMEOUT_MS 3 + +/* * Vendor specific pre-defined parameters */ #define UFS_MTK_LIMIT_NUM_LANES_RX 1 @@ -35,8 +51,9 @@ /* * SiP commands */ -#define MTK_SIP_UFS_CONTROL MTK_SIP_SMC_CMD(0x276) -#define UFS_MTK_SIP_DEVICE_RESET BIT(1) +#define MTK_SIP_UFS_CONTROL MTK_SIP_SMC_CMD(0x276) +#define UFS_MTK_SIP_DEVICE_RESET BIT(1) +#define UFS_MTK_SIP_REF_CLK_NOTIFICATION BIT(3) /* * VS_DEBUGCLOCKENABLE @@ -57,6 +74,7 @@ enum { struct ufs_mtk_host { struct ufs_hba *hba; struct phy *mphy; + bool ref_clk_enabled; }; #endif /* !_UFS_MEDIATEK_H */ |