scsi: ufs-qcom: implement pre and post notification for clock scaling

QUniPro controller requires additional configuration before and after
clock scaling, this change adds the support for it.

Change-Id: I0add27ff3ab54f72b8b79e1e554541c2e492a4c8
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
This commit is contained in:
Subhash Jadavani 2015-01-28 15:54:17 -08:00 committed by David Keitel
parent 1811e9b0bb
commit 413d97dd2a
2 changed files with 123 additions and 9 deletions

View file

@ -63,6 +63,8 @@ static int ufs_qcom_get_bus_vote(struct ufs_qcom_host *host,
static int ufs_qcom_set_bus_vote(struct ufs_qcom_host *host, int vote);
static int ufs_qcom_update_sec_cfg(struct ufs_hba *hba, bool restore_sec_cfg);
static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host);
static int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba,
u32 clk_cycles);
static void ufs_qcom_dump_regs(struct ufs_hba *hba, int offset, int len,
char *prefix)
@ -546,6 +548,16 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba *hba, bool status)
/* make sure RX LineCfg is enabled before link startup */
err = ufs_qcom_phy_ctrl_rx_linecfg(phy, true);
if (err)
goto out;
if (ufs_qcom_cap_qunipro(host))
/*
* set unipro core clock cycles to 150 & clear clock
* divider
*/
err = ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba,
150);
break;
case POST_CHANGE:
ufs_qcom_link_startup_post_change(hba);
@ -1386,22 +1398,118 @@ static void ufs_qcom_exit(struct ufs_hba *hba)
phy_power_off(host->generic_phy);
}
static int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba,
u32 clk_cycles)
{
int err;
u32 core_clk_ctrl_reg;
int ufs_qcom_clk_scale_notify(struct ufs_hba *hba,
bool scale_up, bool status)
if (clk_cycles > DME_VS_CORE_CLK_CTRL_MAX_CORE_CLK_1US_CYCLES_MASK)
return -EINVAL;
err = ufshcd_dme_get(hba,
UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL),
&core_clk_ctrl_reg);
if (err)
goto out;
core_clk_ctrl_reg &= ~DME_VS_CORE_CLK_CTRL_MAX_CORE_CLK_1US_CYCLES_MASK;
core_clk_ctrl_reg |= clk_cycles;
/* Clear CORE_CLK_DIV_EN */
core_clk_ctrl_reg &= ~DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT;
err = ufshcd_dme_set(hba,
UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL),
core_clk_ctrl_reg);
out:
return err;
}
static int ufs_qcom_clk_scale_up_pre_change(struct ufs_hba *hba)
{
/* nothing to do as of now */
return 0;
}
static int ufs_qcom_clk_scale_up_post_change(struct ufs_hba *hba)
{
struct ufs_qcom_host *host = hba->priv;
if (!ufs_qcom_cap_qunipro(host))
return 0;
/* set unipro core clock cycles to 150 and clear clock divider */
return ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba, 150);
}
static int ufs_qcom_clk_scale_down_pre_change(struct ufs_hba *hba)
{
struct ufs_qcom_host *host = hba->priv;
int err;
u32 core_clk_ctrl_reg;
if (!ufs_qcom_cap_qunipro(host))
return 0;
err = ufshcd_dme_get(hba,
UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL),
&core_clk_ctrl_reg);
/* make sure CORE_CLK_DIV_EN is cleared */
if (!err &&
(core_clk_ctrl_reg & DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT)) {
core_clk_ctrl_reg &= ~DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT;
err = ufshcd_dme_set(hba,
UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL),
core_clk_ctrl_reg);
}
return err;
}
static int ufs_qcom_clk_scale_down_post_change(struct ufs_hba *hba)
{
struct ufs_qcom_host *host = hba->priv;
if (!ufs_qcom_cap_qunipro(host))
return 0;
/* set unipro core clock cycles to 75 and clear clock divider */
return ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba, 75);
}
static int ufs_qcom_clk_scale_notify(struct ufs_hba *hba,
bool scale_up, bool status)
{
struct ufs_qcom_host *host = hba->priv;
struct ufs_pa_layer_attr *dev_req_params = &host->dev_req_params;
int err = 0;
if (!dev_req_params)
return 0;
if (status == PRE_CHANGE) {
if (scale_up)
err = ufs_qcom_clk_scale_up_pre_change(hba);
else
err = ufs_qcom_clk_scale_down_pre_change(hba);
} else {
if (scale_up)
err = ufs_qcom_clk_scale_up_post_change(hba);
else
err = ufs_qcom_clk_scale_down_post_change(hba);
ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx,
dev_req_params->pwr_rx,
dev_req_params->hs_rate, false);
ufs_qcom_update_bus_bw_vote(host);
if (err || !dev_req_params)
goto out;
return 0;
ufs_qcom_cfg_timers(hba,
dev_req_params->gear_rx,
dev_req_params->pwr_rx,
dev_req_params->hs_rate,
false);
ufs_qcom_update_bus_bw_vote(host);
}
out:
return err;
}
/*

View file

@ -141,6 +141,12 @@ struct ufs_qcom_phy_vreg {
(UFS_QCOM_DBG_PRINT_REGS_EN | UFS_QCOM_DBG_PRINT_ICE_REGS_EN | \
UFS_QCOM_DBG_PRINT_TEST_BUS_EN)
/* QUniPro Vendor specific attributes */
#define DME_VS_CORE_CLK_CTRL 0xD002
/* bit and mask definitions for DME_VS_CORE_CLK_CTRL attribute */
#define DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT BIT(8)
#define DME_VS_CORE_CLK_CTRL_MAX_CORE_CLK_1US_CYCLES_MASK 0xFF
static inline void
ufs_qcom_get_controller_revision(struct ufs_hba *hba,
u8 *major, u16 *minor, u16 *step)