On Fri, Dec 13, 2019 at 2:23 PM Stanley Chu <stanley.chu@xxxxxxxxxxxx> wrote: > > Introduce reference clock control in MediaTek Chipset in order > to disable it if it is not necessary by UFS device to save system power. > > Currently reference clock can be disabled during system suspend, runtime > suspend and clock-gating after link enters hibernate state. > > Signed-off-by: Stanley Chu <stanley.chu@xxxxxxxxxxxx> Reviewed-by: Alim Akhtar <alim.akhtar@xxxxxxxxxxx> > --- > drivers/scsi/ufs/ufs-mediatek.c | 64 ++++++++++++++++++++++++++++++--- > drivers/scsi/ufs/ufs-mediatek.h | 20 +++++++++-- > 2 files changed, 78 insertions(+), 6 deletions(-) > > diff --git a/drivers/scsi/ufs/ufs-mediatek.c b/drivers/scsi/ufs/ufs-mediatek.c > index 6a3ec11b16db..690483c78212 100644 > --- a/drivers/scsi/ufs/ufs-mediatek.c > +++ b/drivers/scsi/ufs/ufs-mediatek.c > @@ -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; > } > diff --git a/drivers/scsi/ufs/ufs-mediatek.h b/drivers/scsi/ufs/ufs-mediatek.h > index b03f601d3a9e..14f8a8357c09 100644 > --- a/drivers/scsi/ufs/ufs-mediatek.h > +++ b/drivers/scsi/ufs/ufs-mediatek.h > @@ -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 */ > -- > 2.18.0 -- Regards, Alim