@@ -18,6 +18,11 @@
#include "unipro.h"
#include "ufs-mediatek.h"
+#define ufs_mtk_ref_clk_notify(on, res) \
+ arm_smccc_smc(MTK_SIP_UFS_CONTROL, \
+ UFS_MTK_SIP_REF_CLK_NOTIFICATION, \
+ on, 0, 0, 0, 0, 0, &(res))
+
static void ufs_mtk_cfg_unipro_cg(struct ufs_hba *hba, bool enable)
{
u32 tmp;
@@ -83,6 +88,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
@@ -107,12 +155,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;
}
@@ -299,8 +351,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;
}
@@ -309,8 +363,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;
}
@@ -6,7 +6,21 @@
#ifndef _UFS_MEDIATEK_H
#define _UFS_MEDIATEK_H
-#include <linux/bitops.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
@@ -34,7 +48,8 @@
/*
* SiP commands
*/
-#define UFS_MTK_SIP_DEVICE_RESET BIT(1)
+#define UFS_MTK_SIP_DEVICE_RESET BIT(1)
+#define UFS_MTK_SIP_REF_CLK_NOTIFICATION BIT(3)
/*
* VS_DEBUGCLOCKENABLE
@@ -55,6 +70,7 @@ enum {
struct ufs_mtk_host {
struct ufs_hba *hba;
struct phy *mphy;
+ bool ref_clk_enabled;
};
#endif /* !_UFS_MEDIATEK_H */